Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

My Very Useful Debugging Functions


  • Please log in to reply
15 replies to this topic
Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
This debugging library- prints debugging messages (through OutputDebug and optionally HTML DialogBox)
- prepends the name of the script to any debug message
- can output the calling function's parameter list with the current values
- can output a variable, printing its name and value
- can recursively print out an object
- has very nice formatting!* each parameter in a parameter list gets its own line
* each indent level is spaced so as not to overlap with other levels
* everything in an indent level is aligned at "="
* certain parameter values can be ignored (like "__deFault__")
* parameter values can also be formatted (split lists like "first|second|third" such that each item (first, second, and third) is on its own line)[/list]REQUIREMENTS:- LowLevel library by Lexikos
- HTML DialogBox by SKAN
- There should be no problems using this library with any build.
DOWNLOADS (last updated 2010-08-13, 13:24):dout.ahk (my debugging library)
DebugViewer (A program for viewing the debug output from OutputDebug. There are alternatives available, but this viewer is the best I've seen.)
Time for an example. So for the following (in a script named t1.ahk):
dout("hello!")
dout("multiple`nlines`nwork`nfine`ntoo")

var123 := "one-two-three"
dout_v(var123)

test(1,"two","3","abc|def|ghi|jkl|")

dout_x()

dout_m("this is a more important alert")


test(first,p2,three,list,optional="cake",another="__deFault__")
{ dout_f(A_ThisFunc)
}
the debug output (in DebugView, for my setup) will be:
[5472] t1 - hello!
[5472] t1 - multiple
[5472]      lines
[5472]      work
[5472]      fine
[5472]      too
[5472] t1 - var123=one-two-three
[5472] t1 - test(first    = 1,
[5472]           p2       = "two",
[5472]           three    = "3",
[5472]           list     = "abc|
[5472]                       def|
[5472]                       ghi|
[5472]                       jkl|",
[5472]           optional = "cake")
[5472] t1 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[5472] t1 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[5472] t1 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[5472] t1 - this is a more important alert
(5472 is the process id, which DebugView adds itself. Timestamps are also present, but not shown here.)


Here's an example script to demo dout_o():
; make an object for demonstrating dout_o()
	obj := Object()

	Loop 3
	{
		c := Object()
		c.asdfqwerty := "slnh value"
		c.alpha := "a"
		c.one := 1
		
		obj[A_Index] := c
	}
	
	/*
	; you can uncomment this if you have the Array library by temp01
	obj.a := Array()
	Loop 4
		obj.a.append(A_Index)
	*/
	
	obj["extra"] := Object()
	obj["extra"].something := "this`nis`nMULTILINE!!!"
	
	obj.x 		:= Object("base", Object("__Get","func_get"))
	obj.y		:= Object("base", Object("__Set","func_set"))
	obj.z		:= Object("base",Object("__Call","func_call"))
	
	; test overriden methods (they don't interfere with dout_o())
	obj.x.test	:= "hix!"
	obj.y.test	:= obj.x.test . "!"
	obj.z.test	:= obj.z()
	
	
	
	
	; print out the object:
	dout_o(obj)
	; you can also specify the name to be displayed:
	dout_o(obj,"my object")
	MsgBox
return

;======================================================================
; these won't get called by accident when using the debug functions
; dout_o() only uses IsObject(), &, and ObjNewEnum() to interact with
; the given objects.  So feel free to customize your objects however
; you like!
;======================================================================
func_get(o,f)
{
	static already=false
	if already
		MsgBox "don't worry, this won't get called"
	else
		already := true
}
func_set(o,f)
{
	static already=false
	if already
		MsgBox "don't worry, this won't get called"
	else
		already := true
}
func_call(o,f)
{
	static already=false
	if already
		MsgBox "don't worry, this won't get called"
	else
	{
		already := true
		return "call works fine"
	}
}
After running that, the output in DebugView will be:
[5200] t2 - 
[5200]      ? = [0x1E0F178]
[5200]                      1     = [0x1E0F1C0]
[5200]                                          alpha      = a
[5200]                                          asdfqwerty = slnh value
[5200]                                          one        = 1
[5200]                      2     = [0x1E0F458]
[5200]                                          alpha      = a
[5200]                                          asdfqwerty = slnh value
[5200]                                          one        = 1
[5200]                      3     = [0x1E0F688]
[5200]                                          alpha      = a
[5200]                                          asdfqwerty = slnh value
[5200]                                          one        = 1
[5200]                      extra = [0x1A9EE80]
[5200]                                          something  = this
[5200]                                                       is
[5200]                                                       MULTILINE!!!
[5200]                      x     = [0x1A9EF58]
[5200]                                          test       = hix!
[5200]                      y     = [0x1A9F030]
[5200]                                          test       = hix!!
[5200]                      z     = [0x1A9F108]
[5200]                                          test       = call works fine
[5200] t2 - 
[5200]      my object = [0x1E0F178]
[5200]                              1     = [0x1E0F1C0]
[5200]                                                  alpha      = a
[5200]                                                  asdfqwerty = slnh value
[5200]                                                  one        = 1
[5200]                              2     = [0x1E0F458]
[5200]                                                  alpha      = a
[5200]                                                  asdfqwerty = slnh value
[5200]                                                  one        = 1
[5200]                              3     = [0x1E0F688]
[5200]                                                  alpha      = a
[5200]                                                  asdfqwerty = slnh value
[5200]                                                  one        = 1
[5200]                              extra = [0x1A9EE80]
[5200]                                                  something  = this
[5200]                                                               is
[5200]                                                               MULTILINE!!!
[5200]                              x     = [0x1A9EF58]
[5200]                                                  test       = hix!
[5200]                              y     = [0x1A9F030]
[5200]                                                  test       = hix!!
[5200]                              z     = [0x1A9F108]
[5200]                                                  test       = call works fine
The first printout was for the dout_o() call that didn't specify a name for the base object. I don't yet know how to obtain the name automatically for an object like this, but it isn't much of a problem. The hex values in brackets are the object addresses.

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
Recently made a lot of improvements. First post has been updated.

Changes:- improvement: everything is now in a single script that is compatible any build (tested with regular AHK, AHK_H, and AHK_L)
- improvement: no need to call LowLevel_init() or anything. Just put this script in your library folder and start using it!
- new function: dout_x() prints out a bunch of x's to serve as a visual marker in the debug output.
- new function: dout_m() prints out message using both MsgBox and OutputDebug. This will be very useful for users who don't yet use a debug viewer.
- new feature: hanging indent now handled for all dout functions
- new feature: dout_f() will now optionally put each parameter on a separate line, making large function calls easier to read
- new feature: dout_f() will now optionally ignore parameters with certain values (like "__deFault__", user can change this)
- new feature: dout_f() will now optionally split up parameter values based on user specified regular expression. By default, the split will occur at |'s.
- new feature: dout_v() can handle objects now too (dout_v and dout_o are now interchangeable for objects)
- various bug fixes (more reliable print formatting, a few typos, etc)


  • Guests
  • Last active:
  • Joined: --
add memory buffer specifier

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008

add memory buffer specifier

I don't understand what your asking for. Is it that you want to be able to print out chunks of memory directly? Could you describe it in more detail? Give an example of what the output would look like? Give an example script that would make use of this requested feature?

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
New version, first post has been updated.

Changes:- improvement: Added some code to ensure that DebugViewer doesn't mangle the formatting of long messages (> 4096 bytes).
- new functions: new convenient HtmDlg Box variants of existing dout functions. Example: call dout_fm rather than dout_f to use an HtmDlg box.
- bugfix: spacing for deeply nested objects has is now correctly handled. Previously, the 3rd+ indent level(s) would get spaced out way too far.


Crash&Burn
  • Members
  • 228 posts
  • Last active: Jul 16 2014 10:10 PM
  • Joined: 02 Aug 2009
Wouldn't using sprintf enable you to do easier output formatting?
Interesting stuff either way.

PS: Why not include a link to Debug Viewer in the first post?

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
Thanks for the tips. The formatting code could definitely be improved, but I might not get around to it any time soon... It seems to work well enough for this library, and for some reason I haven't had any luck getting sprintf to work in AHK (or sprintf_s for that matter).

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

- There should be no problems using this library with any build.

It should be noted that LowLevel is inherently version-dependent, so while it may work on current builds, it mightn't in future. Actually, I was interested to see how your script could support objects and still run on older builds - turns out enum[key,value] is harmlessly pre-parsed into two separate variable references, enum[key and value].

I haven't had any luck getting sprintf to work in AHK

You'd need swprintf on Unicode builds. For example,
VarSetCapacity(buf,40)
DllCall("msvcrt\s" . (A_IsUnicode ? "w":"") . "printf", str, buf
    , str, "0x%p", A_PtrSize ? ptr:uint, &foo, cdecl)
MsgBox % buf


Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008

It should be noted that LowLevel is inherently version-dependent, so while it may work on current builds, it mightn't in future.

While dout_v() and dout_f() depend on LowLevel, I have backup plans ready (something like AHK Preprocessor, or even make dout_v() and dout_f() AHKH exclusives). Whatever happens, I'll find a way to make it work... tracing function calls is just far too useful, and I'd hate to give it up.

You'd need swprintf on Unicode builds.

Figures... I check the MSDN page and still manage to mess that up! I'll play around with it later and add it to a list of potential future improvementsl

Lucid_Method
  • Members
  • 147 posts
  • Last active: Dec 06 2014 08:39 AM
  • Joined: 19 Apr 2010
Rapte_Of_Suzaku: this looks a like a really useful function you've put together here. I am building an AHK program that has grown to be rather large and I'm looking for better ways to troubleshoot. I've never done anything with debugging outside of using msgbox/tooltips to display information while I test. Does your debug method include the ability to log this data or is it just for real time output display / testing?
Posted Image
Macro Everything
Lucid_Method Index

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009

Rapte_Of_Suzaku: this looks a like a really useful function you've put together here. I am building an AHK program that has grown to be rather large and I'm looking for better ways to troubleshoot. I've never done anything with debugging outside of using msgbox/tooltips to display information while I test. Does your debug method include the ability to log this data or is it just for real time output display / testing?


Just as a short hint, you can also use the outputdebug command together with something like DebugView for logging debug messages.

Add something like this to the start of your script:
if(FileExist(A_ScriptDir "\DebugView\Dbgview.exe"))
	{
		CoordMode, Mouse, Relative 
		;Debug view
		a_scriptPID := DllCall("GetCurrentProcessId")	; get script's PID
		if(WinExist("ahk_class dbgviewClass")) ; kill it if the debug viewer is running from an older instance
		{
			winactivate, ahk_class dbgviewClass
			Winwaitactive, ahk_class dbgviewClass
			winclose, ahk_class dbgviewClass
			WinWaitNotActive ahk_class dbgviewClass
		}
		Run %A_ScriptDir%\DebugView\Dbgview.exe /f,,UseErrorLevel
		winwait, ahk_class dbgviewClass
		winactivate, ahk_class dbgviewClass
		Winwaitactive, ahk_class dbgviewClass
		sendinput, !E{down}{down}{down}{down}{down}{Enter}
		winwait, DebugView Filter
		winactivate, DebugView Filter
		Winwaitactive, DebugView Filter 
		MouseGetPos, x,y
		mouseclick, left, 125, 85,,0
		MouseMove, x,y,0
		send, [%a_scriptPID%*{Enter}
		send, !M{Down}{Enter}
		Coordmode, Mouse, Screen
	}


Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008

Does your debug method include the ability to log this data or is it just for real time output display / testing?


Sorta both. My code only spits out the debug messages, but DebugViewer has a log to file setting. I always have DebugViewer running, and have it set to keep a log of fixed size (it cycles around and starts overwritting the oldest entries once the log gets to a certain size).

Just as a short hint, you can also use the outputdebug command together with something like DebugView for logging debug messages.

At its heart, dout is just OutputDebug. The first version of dout() was just a single line prepending the script name to the passed message and then OutputDebugging it. Automatically starting DebugView might be a good feature... I'll add it to my list.

Hwulex
  • Members
  • 17 posts
  • Last active: May 10 2016 12:59 AM
  • Joined: 02 Feb 2014

It's a shame your downloads no longer work as I would be interesting in a script like this.  I am nesting several objects deep in my program and I need to debug something several layers in and it's proving frustrating.



AfterLemon
  • Moderators
  • 237 posts
  • Connoisseur of Boobs
  • Last active: Jan 04 2016 11:12 PM
  • Joined: 09 Oct 2012

Might not be exactly the same as this function (and I searched around a bit for a copy of this), but try the [Class] Console script.



Hwulex
  • Members
  • 17 posts
  • Last active: May 10 2016 12:59 AM
  • Joined: 02 Feb 2014
Yours looks awesome, I will check it out now. Thanks!

Funny, years ago I envisaged writing something very similar for web-development using JavaScript so it's interesting to see something done for AHK. I look forward to playing around.