Debuging Scripts : Log of executed lines in a variable

Helpful script writing tricks and HowTo's
DigiDon
Posts: 178
Joined: 19 May 2014, 04:55
Contact:

Debuging Scripts : Log of executed lines in a variable

08 Jan 2016, 11:44

Hi Everyone,

I post this here because it seems that this subject is not very well covered, at least in the documentation and from Google results.
If you have quite a complex software written in AHK it can be a mess to understand where you script has bugged/crashed.
It can then be usefull to have a variable that contains the last lines executed before the script crashes.
and if you release an ahk software to non computer savy people you might find this log very useful !
Common solutions for logging/debugging are:
-Put debugging messages yourself (like msgbox and tooltip) or manual logging to a text file
-Use a debugger like DebugView with command Outputdebug: https://www.autohotkey.com/docs/command ... tDebug.htm
-Display executed lines by the ListLines command : https://autohotkey.com/docs/commands/ListLines.htm but you need to execute it manually at a certain time and it display a box andis not savable to a variable directly

AND Yes there are some very usefull warning and such but in certain case the information is not very helpful as were the bug is located (for exemple errors in functions handled by windows directly : ex: XML).

In my case:
Having a complex software I write in AHK for almost 2 1/2 year (yes much longer than first expected but a much complexer project as well ... that will hopefully soon be released !!) I'm currently working so that when people encounter a bug they can send me the last executed lines before the crash directly by a simple button in the software.

The solution I am working on and that I will implement will look like :
-Having a function that can save ListLines to a variable
-Making a timer that will save last ~ 100 lines of this variable to a file
-Having two logs files of the current and last session so if the software has crashed last log can be sent and if the program simply failed to do something current log can be sent
-Making a simple GUI / some buttons that will say either 1) I had a serious bug that caused my software to crash ---> It will send last log via email / 2) I have a simple bug --> It will send current log via email

If you are interested by this solution, stay tuned I will try to submit it when I will be finished
If you have suggestions to make they are welcomed even though writing this scheme seems manageable to me so I don't ask for people to loose their time making it themselves

For the script to save LastLines to a variable I found a script by Lexikos (Thanks!) :) but who made everytime the script (AHK unicode x64 Win7) to crash
https://autohotkey.com/board/topic/5811 ... /?p=365156

So I saw the original post by Lexikos to save ListVars (All vriables and respective value) to a variable, which was maintained and working !
https://autohotkey.com/board/topic/2092 ... /?p=156570

Code: Select all

Loop {
    tick := A_TickCount
    ToolTip % ListGlobalVars()
}

ListGlobalVars()
{
    static hwndEdit, pSFW, pSW, bkpSFW, bkpSW
    
    if !hwndEdit
    {
        dhw := A_DetectHiddenWindows
        DetectHiddenWindows, On
        Process, Exist
        ControlGet, hwndEdit, Hwnd,, Edit1, ahk_class AutoHotkey ahk_pid %ErrorLevel%
        DetectHiddenWindows, %dhw%
        
        astr := A_IsUnicode ? "astr":"str"
        ptr := A_PtrSize=8 ? "ptr":"uint"
        hmod := DllCall("GetModuleHandle", "str", "user32.dll", ptr)
        pSFW := DllCall("GetProcAddress", ptr, hmod, astr, "SetForegroundWindow", ptr)
        pSW := DllCall("GetProcAddress", ptr, hmod, astr, "ShowWindow", ptr)
        DllCall("VirtualProtect", ptr, pSFW, ptr, 8, "uint", 0x40, "uint*", 0)
        DllCall("VirtualProtect", ptr, pSW, ptr, 8, "uint", 0x40, "uint*", 0)
        bkpSFW := NumGet(pSFW+0, 0, "int64")
        bkpSW := NumGet(pSW+0, 0, "int64")
    }

    if (A_PtrSize=8) {
        NumPut(0x0000C300000001B8, pSFW+0, 0, "int64")  ; return TRUE
        NumPut(0x0000C300000001B8, pSW+0, 0, "int64")   ; return TRUE
    } else {
        NumPut(0x0004C200000001B8, pSFW+0, 0, "int64")  ; return TRUE
        NumPut(0x0008C200000001B8, pSW+0, 0, "int64")   ; return TRUE
    }
    
    ListVars
    
    NumPut(bkpSFW, pSFW+0, 0, "int64")
    NumPut(bkpSW, pSW+0, 0, "int64")
    
    ControlGetText, text,, ahk_id %hwndEdit%

    RegExMatch(text, "sm)(?<=^Global Variables \(alphabetical\)`r`n-{50}`r`n).*", text)
    return text
}
I adapted back the changes : and here is the updated version to save LastLines to a variable:

Code: Select all

;By Lexikos
Loop {
    tick := A_TickCount
    ToolTip % ListLines()
}

ListLines()
{
    static hwndEdit, pSFW, pSW, bkpSFW, bkpSW
	ListLines Off
    
    if !hwndEdit
    {
        dhw := A_DetectHiddenWindows
        DetectHiddenWindows, On
        Process, Exist
        ControlGet, hwndEdit, Hwnd,, Edit1, ahk_class AutoHotkey ahk_pid %ErrorLevel%
        DetectHiddenWindows, %dhw%
        
        astr := A_IsUnicode ? "astr":"str"
        ptr := A_PtrSize=8 ? "ptr":"uint"
        hmod := DllCall("GetModuleHandle", "str", "user32.dll", ptr)
        pSFW := DllCall("GetProcAddress", ptr, hmod, astr, "SetForegroundWindow", ptr)
        pSW := DllCall("GetProcAddress", ptr, hmod, astr, "ShowWindow", ptr)
        DllCall("VirtualProtect", ptr, pSFW, ptr, 8, "uint", 0x40, "uint*", 0)
        DllCall("VirtualProtect", ptr, pSW, ptr, 8, "uint", 0x40, "uint*", 0)
        bkpSFW := NumGet(pSFW+0, 0, "int64")
        bkpSW := NumGet(pSW+0, 0, "int64")
    }

    if (A_PtrSize=8) {
        NumPut(0x0000C300000001B8, pSFW+0, 0, "int64")  ; return TRUE
        NumPut(0x0000C300000001B8, pSW+0, 0, "int64")   ; return TRUE
    } else {
        NumPut(0x0004C200000001B8, pSFW+0, 0, "int64")  ; return TRUE
        NumPut(0x0008C200000001B8, pSW+0, 0, "int64")   ; return TRUE
    }
    
    ListLines
    
    NumPut(bkpSFW, pSFW+0, 0, "int64")
    NumPut(bkpSW, pSW+0, 0, "int64")
    
    ControlGetText, ListLinesText,, ahk_id %hwndEdit%
    RegExMatch(ListLinesText, ".*`r`n`r`n\K[\s\S]*(?=`r`n`r`n.*$)", ListLinesText)
    StringReplace, ListLinesText, ListLinesText, `r`n, `n, All
    ListLines On
    return ListLinesText  ; This line appears in ListLines if we're called more than once.
}
Hope it helps !

I will post on advancements soon ;)
Last edited by DigiDon on 08 Jan 2016, 12:33, edited 3 times in total.
EverFastAccess : Take Notes on anything the Fast way: Attach notes, Set reminders & Speed up research in 1 gesture - AHK topic
AHK Dynamic Obfuscator L - Protect your AHK code by Obfuscation - AHK topic
QuickModules for Outlook : Sort Outlook emails very quickly to multiple folders - AHK topic
Coding takes lots of time and efforts. If I have helped you or if you enjoy one of my free projects, please consider a small donation :thumbup:
Sorry I am working hard at the moment at a new job and can't commit on delays of answers & updates
DigiDon
Posts: 178
Joined: 19 May 2014, 04:55
Contact:

Re: Debuging Scripts : Log of executed lines in a variable

08 Jan 2016, 12:07

As usual when about to make it I realize that there are some difficulties to my suggested solution :

1/ If you save log by a timer then IT WON'T BE CAPABLE OF SAVING THE LINES THAT CRASHED, because the timer will be launched a lil' bit before. We can increase frequency but as lines are executed really fast we need to make it every 100ms or even faster which will significantly reduce performance ... :(
We could save instead A_LineNumber and A_LineFile to improve performance but it will be poorly readable to my opinion..
2/ If the user makes actions before reporting the bug it will add considerably lots of useless lines to your log .. but that seems unavoidable unfortunately. At least we can add "ListLines Off/On" to turn off recording when reporting the bug itself of parts of the normal functioning of your software that should be bugless or useless to report on. So we will need to record quite a lot of lines to be sure to include the guilty ones !

So I wonder if a better solution - NOT PERFECT AT ALL OF COURSE - could be :
ADD a bouton : record my bug : that will turn ListLines ON and will save log at a very fast frequency. The user will repeat the actions that made the software to bug. Then a Turn recording off button that will turn ListLines OFF and propose to send the log via email. If the software crashed last lines will have been recorded and there will be a button : send my last bug recording that will send the log.

It seems to me a much better solution that won't hinder normal performance and that will reduce useless loging.

What's your opinion about it? :)

EDIT: After some test I came to the conclusion that ListLines in a complex software states too many lines that are not really readable for debugging and now I understand better the point of many who suggest manual logging instead.

I first tried to ListLines, Off many functions in order to axe some repetitive logs but it seems longer and not simplier as manual logging.
At least manual logging will bring much simplier logs to read with variable contents you need as well (because otherwise a combination of ListLines and ListVars would be needed!).
So anyway I will probably go ahead with that solution latter.

In the meantime I will implement a simple form for users to send me emails regarding their problems.
EDIT : The Email contact form GUI for bugs and suggestions reporting can be found here:
https://autohotkey.com/boards/viewtopic.php?f=6&t=12865
I include a simple template I am working on for those interested :

Code: Select all

; GLOBAL SETTINGS ===================================================================

#Warn
#NoEnv
#SingleInstance Force
;#NoTrayIcon

; SCRIPT ============================================================================
MailTemplate=
(
Hello, I would like to report a bug.
Here are some information :

---------------------------------------
CONCERNED AREA OF THE PROGRAM: 
Complete Here 
(ex: ...................  , OTHERS)

SHORT DESCRIPTION OF THE BUG : 
Complete Here

WHAT I WANTED : 
Complete Here

WHAT I DID : 
Complete Here

WHAT I WAS EXEPECTING : 
Complete Here

WHAT HAPPENED : 
Complete Here
---------------------------------------
)
Gui, Margin, 10, 10
Gui, Font, s12, Tahoma
Gui, Add, Text, ym+10 w700 +Center, Email Bugs / Suggestions Report
Gui, Font, s11, Tahoma
Gui, Add, Text, xm yp+40, Click the email adress to send me the bug report at : 
Gui, Add, Link, xm yp+40 gMailMeBugs, <a id="1">[email protected]</a>
Gui, Add, Text, xm yp+40, Email Content:
Gui, Font, s10, Tahoma
Gui, Add, Edit, xm y+10 w700 h500 vEditContentVar, %MailTemplate%
Gui, Add, Button, xm y+40 w80 Default gCopyMail, Copy EMail
Gui, Show, AutoSize, Mail Bug Report
ControlSend, Edit1, {PgUp}{PgUp}, A
return

CopyMail:
    Gui, Submit, NoHide
	GuiControl, Focus, Edit1
   Send, ^a
   Send, ^c
   Send {PgUp}{PgUp}
return

; EXIT ==============================================================================
MailMeBugs:
Gui, Submit, NoHide
StringReplace, FormatedEditContent, EditContentVar, `n,`%0A, All
Run, mailto:[email protected]?subject=Bug Report&body=%FormatedEditContent%
return

GuiClose:
GuiEscape:
ExitApp
EverFastAccess : Take Notes on anything the Fast way: Attach notes, Set reminders & Speed up research in 1 gesture - AHK topic
AHK Dynamic Obfuscator L - Protect your AHK code by Obfuscation - AHK topic
QuickModules for Outlook : Sort Outlook emails very quickly to multiple folders - AHK topic
Coding takes lots of time and efforts. If I have helped you or if you enjoy one of my free projects, please consider a small donation :thumbup:
Sorry I am working hard at the moment at a new job and can't commit on delays of answers & updates

Return to “Tutorials (v1)”

Who is online

Users browsing this forum: No registered users and 21 guests