Foobar2000:Development:Debugging

From Hydrogenaudio Knowledgebase


Running debug builds of a component

To conveniently debug your foobar2000 component DLL:

  • Configure Visual Studio to create your DLL directly in its intended destination in your foobar2000 profile (or installation) directory:

Project Properties, General, Output Directory, set to the directory where your component DLL resides, such as %appdata%\foobar2000\user-components\foo_sample

  • Set debugged program to foobar2000.exe of your foobar2000 installation:

Project Properties, Debugging, Command


Note that foobar2000 will detect debug build of your component and complain if trying to use it with no debugger attached. This is by design, to keep people from accidentally releasing debug builds of their components to the general public.

Crash reporting

Upon crash, foobar2000 writes a crash log file and a minidump, prompting user to submit them to our online database of crash reports.

If you wish to obtain access to logged crash reports identified as related to your component, register a forum account and contact Peter with info about your component.

Debug symbols

Always archive PDB debug symbols of components that you release. This makes minidumps of your crashes much more useful, in many cases allowing the debugger to highlight the crashing code line automatically.

Debugging utilities in foobar2000 code

Various tools exist in foobar2000 to allow you to produce more useful crash reports.

pfc::crash() / uBugCheck()

When encountering an error condition in your code that should not be possible, you should crash as soon as possible to generate a bug report containing information that can be used to identify the cause.

These functions, pfc::crash() and uBugCheck(), reliably crash and generate a crash report containing stack trace pointing at the current context of your code.

These functions are the same, uBugCheck() is implemented by shared.dll, pfc::crash() is redirected to call uBugCheck().

fb2k::crashWithMessage()

This function crashes (similar to pfc::crash()) and makes sure that the passed message is readable from the crash report.

Dynamic assert

FB2K_DYNAMIC_ASSERT(cond) calls uBugCheck() if (cond) evaluates to false.

Call path tracking

In addition to stack traces provided by crash reports, TRACK_CALL() utility can be used to include context information in crash logs.

Example:

{
  TRACK_CALL(foo);
  foo();
  TRACK_CODE("asdf", asdf());
}

Will put "foo" in crash log call path if foo() crashes, or "foo=>asdf" in asdf() crashes, as the last part is still within TRACK_CALL(foo) scope.

Note that, for historical reasons, TRACK_CALL() expects a string without quotation marks, TRACK_CALL_TEXT() expects a string. TRACK_CALL(foo) is equivalent to TRACK_CALL_TEXT("foo").

This technique was essential before crash reports included minidumps. Now it's mainly used for crash report tracker to group similar crashes together or identify new reports of already-known bugs.

Include recent events in crash reports

Recent foobar2000 console events are included in crash reports.

If you wish to log other recent events in crash reports, without printing them to console, use shared.dll uAddDebugEvent() to output your diagnostic lines. They will not be visible to anyone unless foobar2000 crashes shortly after.

Instacrash scope

Sometimes you want to reliably turn an exception into a crash, yet the code calling your code handles exceptions for you doing something else. Additionally, some Windows APIs behave erratically if your callbacks throw C++ exceptions.

Use fb2k_instacrash_scope() macro to reliably crash & generate crash report if some code throws an exception, without ever possibly passing an exception to the calling context.

Example:

void foo() { /* code that possibly throws exceptions goes here */ }
void bar() noexcept {
  fb2k_instacrash_scope(foo);
}

This will reliably crash should foo() throw an exception.