Root > Basic procedures > Configuring bug report > Configuring call stack

Configuring call stack

Previous pageReturn to chapter overviewNext page   

Call stack is a central piece of information in any bug report. Call stack is a stack data structure that stores information about the active subroutines of the application. Call stack is a sequence of addresses (mostly with textual description), which leads us to place of the problem. This kind of stack is also known as an execution stack, control stack, run-time stack, or machine stack, and is often shortened to just "the stack".

 

Two main sources of information for call stack:

1. Stack tracing method (run-time);
2. Debug information (design-time).

 

Stack tracing method collects return addresses at run-time and defines list of entries in call stack. Debug information establishes correspondence between RAW addresses and source code. It is generated at design-time. It defines human-readable form of call stack:

 

1. Executable's code -> Exception -> Stack Tracing Method -> list of RAW addresses;
2. List of RAW addresses -> Debug Information Provider + Debug Information (executable's data) -> human-readable call stack.

 

Stack tracing methods

Stack tracing method scans CPU stack and retrieves all found return addresses. The result will be a list of RAW pointers. Each pointer in the list is a return address - i.e. it points to some location in code section. Stack tracing method determines list of addresses. Different tracing methods may produce different lists.

 

To learn more about different stack tracing methods - see this article.

 

Stack tracing method can be configured here.

 

Debug information

Debug information is information about link between machine's binary code (.exe/.dll/.bpl file) and source code (.pas file). It is used to build human-readable call stacks. Debug information is necessary for EurekaLog to function. It is generated by linker and is saved into .map or .tds files. It is injected into your executable during post-processing stage. Debug information can be generated by various tools (compilers/linkers), and in different formats. EurekaLog must know debug information format in order to use debug information in that format. Code which knows some debug information format and is able to read debug information in this format is called "Debug Information Provider".

 

EurekaLog supports the following debug information providers. Debug information can also be converted from one format to another by using special tools.

 

Debug information providers can be configured here.

 

Note: debug information itself is not configured in EurekaLog's options. It must be configured via compiler/linker/converter's options. See also: configuring project for EurekaLog. Or it can be supplied by debug information converters. See also: using EurekaLog with 3rd party tools.

 

 

Other important considerations for call stacks

 

Call stacks for chained exceptions

Chained exception is an exception which occurs during processing of another exception. That "another" (original) exception is called "nested exception". For example:

 

try

  // Low-level error (a.k.a.  original, first, bottom, inner, nested)

  raise ERangeError.Create('Invalid item index'); 

except

  // High-level error (a.k.a. introduced, last, top, outer, chained)

  raise EFileLoadError.CreateFmt('Error loading file %s', [FileName]); 

end;

 

As you can see, low-level exception (nested) is the exception you're interested in. It indicates a reason for failure. This is what you typically want to be logged. Chained exception is triggered by original exception and provides more descriptive error message. So, you typically want to show it to user as error message.

 

Thus, typically you want first exception to be logged, but last exception to be shown to end user. Classic/default Delphi and C++ Builder behavior is to work only with last exception always. Default settings for EurekaLog is to log original (nested) exception, but show chained exception to user.

 

To learn more about chained/nested exceptions support in Delphi/C++ Builder and EurekaLog - see this article.

 

Chained exceptions can be configured here.

 

Important note: this feature require EurekaLog to be able to track life-time of exception objects. Therefore, it's highly recommended that you enable the following options:

Otherwise it's recommended that you keep all options on this page into "Classic" position, or EurekaLog may show information about wrong exceptions. Exceptions passed between executable modules (i.e. from/to DLLs) will probably won't be able to work properly with this feature. However, exceptions that are converted to error code/HRESULT/etc. when passed between modules boundaries will work OK.

 

Multiple call stacks in a single bug report / Bug reports for multi-threaded applications

A single bug report may contain more than one call stack. Additional call stacks may be created for leaks or background threads.

 

See this article to learn more about leak reports. See this article to learn more about multi-threaded applications.

 

Deferred and immediate call stacks

EurekaLog must collect information about exception when this exception is raised (information such as call stack, time, etc.). Collecting this information will take some time. This may become a performance issue if your application raises exceptions too often - and handles them immediately (i.e. it uses exceptions as part of its normal execution path). This would mean that even though information for exceptions is collected, but it is not used - since bug report is not created (because application handles exceptions by itself). Then enabling EurekaLog will introduce slowdown for such application.

 

We recommend to review your code and avoid raising exceptions too often (i.e. avoid using exception as part of normal execution path; use exceptions only for errors/rare conditions). A typical fixes for your code include:

Avoid raising exception when you can pre-check error-condition. For example, it's better to use TryStrToInt or StrToIntDef instead of StrToInt + try/except block;
Do not raise exception for non-errors. If you still need to do this - consider creating custom exception class for this purpose and exclude exception from EurekaLog (see next item);
You can create custom exception classes for your purposes. You can mark some exception classes as "ignored" for EurekaLog. You can do this via filters, events (specifically: OnRaise), attributes, or low-level handlers;
You can also add SetEurekaLogStateInThread(0, False) and SetEurekaLogStateInThread(0, True) around blocks of code which can raise exceptions intensively, but your code handles all these exceptions.

 

However, x86-32 platform has unique architecture: a call stack may be build later - during exception handling step. This feature can give a performance gain, because exception tracer now may build call stack later - at handling state, it's not strictly necessary to build call stack when exception is raised. Thus, there will be a huge performance boost, if most of raised exceptions are handled by your code and not reach default handler (which will create a bug report and, thus, a call stack).

 

This feature is enabled by default, but it is only applicable to Win32 platform.

 

See this article to learn more about deferred and immediate call stacks.

 

Exceptions in DLLs

The default good practice when working with exceptions and DLLs is not to let any exception escape DLL. That's because DLL and exe can be written in different programming languages, and caller may not know how to handle (and release) exception object from callee. That's why by default EurekaLog is configured to handle only exceptions inside current executable.

 

It's highly recommended to follow best practices. However, you still may want to handle DLL's exceptions in exe (or visa versa). For example, if you're 100% sure that both DLL and exe is written in the same programming language (and is compiled by the same compiler's version). Then you can instruct EurekaLog to catch exceptions from other modules. You can enable this behavior by disabling "Capture stack only for exceptions from current module" option.

 

Note: you should probably disable chained exceptions support for DLLs that let exceptions escape DLL and be handled by the caller (see above). This feature requires ability to track life time of exceptions objects. This is not possible for general case (e.g. host and DLL are compiled by different compilers and there is no assist from RTL for tracking exception objects). This feature may work in some specific configurations.

 

See also FAQ below to learn more about building call stacks for DLLs.

 

 

How to...

...show RTL and VCL units in call stack?

Set "Detalization level" option to "Show any (including RAW addresses)" or "Show items with procedure name (DLLs)".

 

...show RTL and VCL units with line numbers in call stack?

Enable "Use Debug DCUs" option and build the project (make/compile is not enough).

 

...hide RTL and VCL units in call stack?

Disable "Use Debug DCUs" option, set "Detalization level" option to "Show items with unit name (BPLs)" or "Show only items with full info (line number available)", and build the project (make/compile is not enough).

 

...show RTL and VCL units in call stack for packaged applications?

Enable "JEDI (JclDebug)" option. Distribute *.jdbg files (along with *.bpl files) with your application.

 

Notes:

*.jdbg files can be found in \bin folder of your IDE installation;
*.jdbg files are not available for very old Delphi/C++ Builder versions.

 

...show JCL/JVCL units in call stack for packaged applications?

Enable "JEDI (JclDebug)" option. Enable "Packages / Create MAP files / Create JEDI Debug Informations" and "Packages / Create MAP files / Insert JEDI Debug Informations into the libraries" options during installation of JCL/JVCL.

 

...capture exceptions from DLLs?

Disable "Capture stack only for exceptions from current module" option. Enable "DLL export table" option or provide debug information for DLL by compiling DLL project with EurekaLog ("Lightweight DLL" profile).

 

Normally this practice is not recommended. Recommended practice is to handle exceptions within the same module and pass error condition (flag, error code, HRESULT, etc.) to the caller. See also: using EurekaLog with DLLs.

 

...how to work with call stacks in COM application?

See this article.

 

...show system DLL functions in call stack?

Enable "DLL export table" option.

 

...show system DLL functions (including internal functions) in call stack?

See this article.

 

...hide system DLL functions in call stack?

Disable "DLL export table" option, disable "Microsoft DBG/PDB" option, and set "Detalization level" option to any value except "Show any (including RAW addresses)" and "".

 

...show custom DLL/package functions in call stack?

Enable "DLL export table" option.

 

Note: this question is independent from "How to capture exceptions from DLLs" question above.

 

See also this article.

 

...show custom DLL/package functions with line numbers in call stack?

Compile your DLL or package project with EurekaLog (use "Lightweight DLL" profile for DLL and "Package" profile for packages).

 

Note: "Standalone DLL" profile can also be used for DLL, but it is designed for different usage case (such as COM, plugins or any other usage of EurekaLog-enabled DLL inside non-EurekaLog-enabled host).

 

...show custom DLL functions with line numbers in call stack (DLL is compiled by non-Embarcadero compiler)?

a). [Microsoft Visual Studio only] Enable "Microsoft DBG/PDB" option, set "Debug Information Format" option in your Visual Studio DLL project to "Program Database" (/Zi option for ompiler) or "Program Database for Edit And Continue" (/ZI option for compiler), enable "Generate debug info" option in your Visual Studio DLL project (/DEBUG option for linker). Distribute generated .pdb file with your .dll file. Note: it is not possible to create an .exe or .dll that contains debug information. Debug information is always placed in a .pdb file.

 

or:

 

b). Enable "Microsoft Dbg/PDB" option, set "Debug Information Format" option in your Visual Studio DLL project to "Program Database" (/Zi option for ompiler) or "Program Database for Edit And Continue" (/ZI option for compiler), enable "Generate debug info" option in your Visual Studio DLL project (/DEBUG option for linker). Post-process your DLL file with EurekaLog command-line compiler. Use NUL as project file name for --el_alter_exe switch and don't forget to add --el_source=PDB switch. You also have to create .eof file for your DLL and specify path to it via --el_config option. Note: most options in .eof file will be ignored. Only design-time options will have effect (such as password encryption for debug information, etc.).

 

See this article for more details.

 

...minimize information in debug information/call stack (for shareware applications)?

Enable "Store units names only" option or "Store all names externally" option.

 

See this article for more information.

 

...hide certain routines from call stack?

Wrap such routines in {$D-} { routine code here } {$D+}.

 

See this article for more information.

 

 

See also:




Send feedback... Build date: 2023-09-11
Last edited: 2023-07-18
PRIVACY STATEMENT
The documentation team uses the feedback submitted to improve the EurekaLog documentation. We do not use your e-mail address for any other purpose. We will remove your e-mail address from our system after the issue you are reporting has been resolved. While we are working to resolve this issue, we may send you an e-mail message to request more information about your feedback. After the issues have been addressed, we may send you an email message to let you know that your feedback has been addressed.


Permanent link to this article: https://www.eurekalog.com/help/eurekalog/configuring_call_stack.php