Run-time debugger in Blackbox Component Builder

Usage of the framework, compiler and tools
Josef Templ
Posts: 262
Joined: Tue Sep 17, 2013 6:50 am

Re: Run-time debugger in Blackbox Component Builder

Post by Josef Templ »

Syntax highlighting is an editor feature.
It would require a special editor for module source texts
as opposed to the general purpose text editor of BlackBox.
It has been done in ETH A2 Oberon, for example, and in
more traditional IDEs such as NetBeans and Eclipse.

While such an editor may be helpful indeed, I wouldn't consider
it an alternative for a runtime debugger, which could be
quite helpful in understanding existing complex programs
such as the BlackBox framework itself.

Since ominc did not include it in their original BlackBox distribution,
it is probably more a prototype of such a debugger. That's why I was
asking about known problems or limitations.

- Josef
X512
Posts: 72
Joined: Sat Feb 07, 2015 2:51 pm

Re: Run-time debugger in Blackbox Component Builder

Post by X512 »

Josef Templ wrote:Does this debugger work reliably?
Is it in a shape such that it could be included in the
center distribution?

Any known bugs or limitations?
Anything like a docu?

- Josef
I have managed to run this debugger on BlackBox build 64. Debugger is running in another process. It can show loaded modules of debugged process, show global variables of some module of debugged process and heap objects. Traps are redirected to debugger.
Step debugging are using in following way:
1. Open source code of module to be debugged
2. Put caret to line (do not select anything) and set breakpoint by menu Debug -> Breakpoint 1, 2, 3. Menu item became checked
3. When breakpoint is triggered trap will be shown and stopped position will be selected
4. Step over/into can be used: trap and selected position will be updated
Some documentation is included.
Debugger need to be adapted for Kernel UTF-8 changes. Way of transferring of modList pointer to debugger is not working on Windows 8.1. Following code in Kernel.Init are used for this, but EBX became 0 when debugger handles debug string. Probably this register is used inside of OutputDebugStringW. I changed register to EDI to make it working, but it may also stop working in future Windows versions, another way of transferring modList pointer is required.

Code: Select all

S.PUTREG(ML, S.ADR(modList));
WinApi.OutputDebugStringW("BlackBox started");
I attached my modifications here. It should be installed by unpacking to root of BlackBox 1.7 release, replacing files. Working on another versions is not guaranteed.
Attachments
RemDebug.7z
(151.07 KiB) Downloaded 378 times
X512
Posts: 72
Joined: Sat Feb 07, 2015 2:51 pm

Re: Run-time debugger in Blackbox Component Builder

Post by X512 »

Using RemDebug in theory any BlackBox made application with Kernel linked may be debugged.
Josef Templ
Posts: 262
Joined: Tue Sep 17, 2013 6:50 am

Re: Run-time debugger in Blackbox Component Builder

Post by Josef Templ »

X512 wrote: Debugger need to be adapted for Kernel UTF-8 changes. Way of transferring of modList pointer to debugger is not working on Windows 8.1. Following code in Kernel.Init are used for this, but EBX became 0 when debugger handles debug string. Probably this register is used inside of OutputDebugStringW. I changed register to EDI to make it working, but it may also stop working in future Windows versions, another way of transferring modList pointer is required.

Code: Select all

S.PUTREG(ML, S.ADR(modList));
WinApi.OutputDebugStringW("BlackBox started");
Thanks for uploading the revised RemDebug package
and for the excellent analysis.

Under Win10 I cannot reproduce the problem with EBX,
which is surprising because it should be similar to Win8.1.
Anyway, passing modList via a register is highly questionable indeed.
In addition, I thought that EBX is 'callee' saved, so it should not be changed at all
without restoring it.

A more portable approach may be to pass modList as part of
OutputDebugStringW("BlackBox started"), e.g. "BlackBox started [modList=xxxxxxxxx]"
but it requires an int-to-string conversion in Kernel.
I haven't found any other way of passing an address from the target to
the debugger so far.

- Josef
Josef Templ
Posts: 262
Joined: Tue Sep 17, 2013 6:50 am

Re: Run-time debugger in Blackbox Component Builder

Post by Josef Templ »

I just noticed that EBX is not really changed by setting it to modList
because it already had this value when the module body was started.
It is an implicit parameter passed from the main entry point to the module Kernel.
Since it is not used in code generation by the compiler, except for nested procedures
I think, it is a very natural choice for passing information to the debugger.

@X512: are you sure that it does not work under Win 8.1?

- Josef
User avatar
DGDanforth
Posts: 59
Joined: Tue Sep 17, 2013 1:16 am
Location: Palo Alto, CA, U.S.A.
Contact:

Re: Run-time debugger in Blackbox Component Builder

Post by DGDanforth »

I simply change the definition of Ctrl-K to be
"Compile And Unload" "K" "DevCompiler.CompileAndUnload" "TextCmds.FocusGuard"
X512
Posts: 72
Joined: Sat Feb 07, 2015 2:51 pm

Re: Run-time debugger in Blackbox Component Builder

Post by X512 »

Josef Templ wrote:I just noticed that EBX is not really changed by setting it to modList
because it already had this value when the module body was started.
It is an implicit parameter passed from the main entry point to the module Kernel.
In theory EBX register may be allocated by compiler for expression evaluation. Also value passed to debugger is not modList, but pointer to modList. Global variables modList, root and baseStack are used by debugger as single structure and pointer to modList is pointer to that structure.
Josef Templ wrote:@X512: are you sure that it does not work under Win 8.1?
Yes. I made a simple test to inspect what happens with registers during call of OutputDebugString:
Kernel.Init

Code: Select all

S.PUTREG(0, 12345678H);
S.PUTREG(1, 12345678H);
S.PUTREG(2, 12345678H);
S.PUTREG(3, 12345678H);
S.PUTREG(6, 12345678H);
S.PUTREG(7, 12345678H);
WinApi.OutputDebugStringW("BlackBox started");
DevRemDebug.HandleEvent

Code: Select all

IF string = "BlackBox started" THEN
	context.ContextFlags := WinApi.CONTEXT_INTEGER;
	res := WinApi.GetThreadContext(threadHandle, context); ASSERT(res # 0);
	Log.String("EAX = "); Log.IntForm(context.Eax, 16, 9, "0", FALSE); Log.String("H"); Log.Ln;
	Log.String("ECX = "); Log.IntForm(context.Ecx, 16, 9, "0", FALSE); Log.String("H"); Log.Ln;
	Log.String("EDX = "); Log.IntForm(context.Edx, 16, 9, "0", FALSE); Log.String("H"); Log.Ln;
	Log.String("EBX = "); Log.IntForm(context.Ebx, 16, 9, "0", FALSE); Log.String("H"); Log.Ln;
	Log.String("ESI = "); Log.IntForm(context.Esi, 16, 9, "0", FALSE); Log.String("H"); Log.Ln;
	Log.String("EDI = "); Log.IntForm(context.Edi, 16, 9, "0", FALSE); Log.String("H"); Log.Ln;
	kernelAdr := 0;
END;
Output:

Code: Select all

EAX = 00023FC60H
ECX = 000000002H
EDX = 000000000H
EBX = 000000000H
ESI = 0005C3259H
EDI = 012345678H
Only EDI is unchanged. There is nothing strange in this behavior, because preserved registers may be changed inside procedure. Only guaranteed thing is preserved registers must be changed to their original values at exit of procedure. Calling of debugger callback is technically done inside OutputDebugString procedure.
Tested system is Windows 8.1, 32 bit.
Josef Templ
Posts: 262
Joined: Tue Sep 17, 2013 6:50 am

Re: Run-time debugger in Blackbox Component Builder

Post by Josef Templ »

Since EDI is not guaranteed to be unchanged either
the only portable approach would be to
pass kernelAdr as part of the debug string, right?

WinApi.OutputDebugStringW("BlackBox started [kernelAdr=1234567]";

No big problem, but requires some string handling.

- Josef
User avatar
Ivan Denisov
Posts: 362
Joined: Tue Sep 17, 2013 12:21 am
Location: Krasnoyarsk, Russia

Re: Run-time debugger in Blackbox Component Builder

Post by Ivan Denisov »

Is there version of DevRemDebug which works with Windows 10 correctly?
User avatar
Ivan Denisov
Posts: 362
Joined: Tue Sep 17, 2013 12:21 am
Location: Krasnoyarsk, Russia

Re: Run-time debugger in Blackbox Component Builder

Post by Ivan Denisov »

I checked suggested method and it works.

I modified the kernel.

Code: Select all

		S.PUTREG(ML, S.ADR(modList));
		IntToString(S.ADR(modList), trans);
		str := "BlackBox started [-----------------]";
		FOR i:= 0 TO LEN(trans$)-1 DO
			str[18 + i] := trans[i]
		END;
		WinApi.OutputDebugStringW(str);
And in DevRemDebug

Code: Select all

			| WinApi.OUTPUT_DEBUG_STRING_EVENT:
				IF (event.u.DebugString.fUnicode = 0) & (event.dwProcessId = procId) THEN
					GetName(SYSTEM.VAL(INTEGER, event.u.DebugString.lpDebugStringData), string);
					Log.String(string); Log.Ln; (* DIA *)
					string[16] := 0X;
					IF string$ = "BlackBox started" THEN
						Log.String(string); Log.Ln; (* DIA *)
						context.ContextFlags := WinApi.CONTEXT_INTEGER;
						res := WinApi.GetThreadContext(threadHandle, context); ASSERT(res # 0);
						IF string[17] = "[" THEN
							i := 18;
							WHILE (string[i] # "-") & (string[i] # "]") DO
								kernelAddressString[i - 18] := string[i];
								INC(i);
							END;
							kernelAddressString[i - 18] := 0X;
							Log.String(kernelAddressString); Log.Ln; (* DIA *)
							Strings.StringToInt(kernelAddressString, kernelAdr, i);
						ELSE
							kernelAdr := context.(*Ebx*)Edi;
						END;
						Log.Int(kernelAdr); Log.Ln; (* DIA *)
					END;
				END
Post Reply