madStackTrace Unit

Sometimes a function would like to know by whom it was called. And by whom this "who" was called again. In other words: Sometimes you want to have the full current call stack. Now there is no official API to get this information. A function is nothing but binary asm code that tells the CPU what to do. So the CPU does all the jumping and calling according to the binary asm code of the current module. Windows is not involved there, so Windows doesn't know the current call stack either.

Fortunately, every time when calling a function, the x86 CPU pushes the return address on the stack. So theoretically we only need to browse through the stack to get all the return addresses. However, the stack does not only contain valid return addresses, but also LOTS of other stuff like data pointers, random values and so on. So we have to build a filter than scans through the stack and gives us only the valid return addresses. That is easier said than done. Most of the time you'll get 1 valid return address and 10 additional invalid return addresses, which *look* like being valid. To solve this problem madStackTrace uses a disassembler and some more tricks and finally we have a good result. Well, we have no guarantee, that the call stack is 100% accurate, but in a lot of cases it actually is. In some cases one or the other invalid stack item is shown, but normally by following the call stack in your sources you can easily filter them out by yourself.

The most important case where you want to have the current call stack is when an exception has occured. That's also the main duty of "madStackTrace", it's internally used by madExcept to get the call stack for the detailed exception box madExcept wants to show. For a list of what's contained in "madStackTrace" please look at the madStackTrace Reference.

The interface of this unit is as easy as it can be: There's just one function which does all the work and returns a formatted plain text string, containing the whole callstack of the specified thread.

You can ignore most of the parameters of this function, they're for fine tuning and are internally used by madExcept. However, the first three parameters "hideUglyItems", "showRelativeAddrs" and "showRelativeLines" might be interesting for you, too. With the first one you can tell the stack tracer to hide all those stack items, for which no line number information is available. The end result most of the time is a dramatically shorter stack trace. The second and third options will add relative (to the beginning of the function) address/line information to the callstack.

  // types needed for the "StackTrace" function
  TStackItem = record
                 Addr         : pointer;   // code address
                 relAddr      : dword;     // relative address to function entry point
                 ModuleName   : string;
                 UnitName     : string;
                 Line         : integer;   // line number (0 = unknown)
                 relLine      : integer;   // relative line number to function entry point
                 FunctionName : string;    // function/method name
  TStackTrace = array of TStackItem;
  TPStackTrace = ^TStackTrace;

// traces the current stack
// example:
// 00461185 project1.exe unit1    124 FaultProc
// 0040A84B project1.exe unit1    137 Button1Click
// 00418E55 project1.exe project1  80 project1
function StackTrace (hideUglyItems     : boolean        = false;
                     showRelativeAddrs : boolean        = false;
                     showRelativeLines : boolean        = false;
                     stackTrace        : TPStackTrace   = nil;
                     currentAddr       : pointer        = nil;
                     isException       : boolean        = false;
                     extException      : boolean        = false;
                     stackBottom       : dword          = 0;
                     stackTop          : dword          = 0;
                     creator           : pointer        = nil;
                     exceptAddr        : TPPointer      = nil;
                     exceptFunc        : TPPointer      = nil;
                     exceptFuncAddr    : TPPointer      = nil;
                     progressAlert     : IProgressAlert = nil;
                     ebp               : dword          = 0;
                     dumbTrace         : boolean        = false;
                     bcbTermination    : boolean        = false;
                     preparedStack     : pointer        = nil;
                     pAbort            : TPBoolean      = nil;
                     pCrc1             : TPCardinal     = nil;
                     pCrc2             : TPCardinal     = nil;
                     pDelphiThread     : TPBoolean      = nil;
                     pMinDebugInfos    : TPDAString     = nil;
                     dontAddTopItem    : boolean        = false) : string;

// example:
0036d58c +004 crashDll.dll CrashDll   9 +0 CrashDllProc
00491d40 +000 CrashApp.exe CrashUnit 30 +0 CrashProc
00491d48 +000 CrashApp.exe CrashUnit 35 +0 TCrashForm.CrashButtonClick
004727c0 +064 CrashApp.exe Controls        TControl.Click
0046b048 +01c CrashApp.exe StdCtrls        TButton.Click
0046b13c +00c CrashApp.exe StdCtrls        TButton.CNCommand
00472628 +188 CrashApp.exe Controls        TControl.WndProc
00475533 +157 CrashApp.exe Controls        TWinControl.WndProc
0046af0c +06c CrashApp.exe StdCtrls        TButtonControl.WndProc
004723f8 +024 CrashApp.exe Controls        TControl.Perform
0047566b +023 CrashApp.exe Controls        DoControlMsg
00475cc3 +00b CrashApp.exe Controls        TWinControl.WMCommand
0048c16c +02c CrashApp.exe Forms           TCustomForm.WMCommand
00472628 +188 CrashApp.exe Controls        TControl.WndProc
00475533 +157 CrashApp.exe Controls        TWinControl.WndProc
0048a1c5 +421 CrashApp.exe Forms           TCustomForm.WndProc
004751b0 +02c CrashApp.exe Controls        TWinControl.MainWndProc
0045e564 +014 CrashApp.exe Classes         StdWndProc
77d1b8fe +044 user32.dll                   SendMessageW
77d1e900 +016 user32.dll                   CallWindowProcA
00475617 +0d7 CrashApp.exe Controls        TWinControl.DefaultHandler
00476985 +019 CrashApp.exe Controls        TWinControl.WMKeyUp
00472628 +188 CrashApp.exe Controls        TControl.WndProc
00475533 +157 CrashApp.exe Controls        TWinControl.WndProc
0046af0c +06c CrashApp.exe StdCtrls        TButtonControl.WndProc
004751b0 +02c CrashApp.exe Controls        TWinControl.MainWndProc
0045e564 +014 CrashApp.exe Classes         StdWndProc
77d196c2 +00a user32.dll                   DispatchMessageA
004904db +083 CrashApp.exe Forms           TApplication.ProcessMessage
004904fa +00a CrashApp.exe Forms           TApplication.HandleMessage
0049071a +096 CrashApp.exe Forms           TApplication.Run
0049203b +03b CrashApp.exe CrashApp  14 +3 initialization