Debugging on a user's PC

Hi I’m currently running a beta, and some users experience problems which I just can’t reproduce - so I’m thinking about sending them debug builds and letting them run it in a debugger or in a terminal…

- Would it help to let them debug the Release build?

Usually you get errors like “Illegal instruction at 0x947F” etc. . Would there be any way for me to connect this adress to a piece of code (over debug build or release build with debug symbols)?

- Do they need to have the actual code for Debug builds to work?

I.e. will the .pdb files themselves say something like “foo.cpp, line 14” or do they need to have the codebase underneath?

- Will they even be able to start a Debug build

I remember sending someone a Debug build by accident, they couldn’t open it because they were lacking some xyzD.dll libraries.

- Any other way to approach this?

Any help much appreciated!

go to the projucer / exporters / debug / runtime library and select “use static runtime”. now all dependencies are being copied into the debug build and the user can open it

1 Like

Nice, thank you - that would settle that question!

What about letting them attach the debugger? Will they be able to see code locations?

at this point someone else has to go on because i’m still a noob :slight_smile: however if this was my problem i’d probably try to add some functionality that writes the stacktrace into a log file or something when the plugin crashes and tell the user to send me that file… idk if that’s possible. just an idea

1 Like

Ok cool, I might try the log - thanks anyway!

Using SystemStats::setApplicationCrashHandler() you can set a custom function to be called when there’s a crash. In there, you can call SystemStats::getStackBacktrace(), write that to a file, and ask the user to send over that file.

“Illegal instruction at 0x947F” etc.

If this happens on the user’s machine but not on yours, then it might indicate that your app uses CPU intrinsics (e.g. SSE4.2) that the user’s CPU doesn’t support. There are other possible reasons for this, but it’s a good thing to check.

1 Like

Thanks, this is some very usefull information!
Just to be sure: This will set crashhandler for the application (i.e. the Host), so I can use this inside a plugin (as opposed to a “standalone plugin”)?

On Windows you need to keep the exe/dll file you ship them and the pdb files created during the build process.
When the plugin/app crashes on their system you need to get a copy of the dmp minidump file it generate.

You can then use Visual Studio on your machine to look at the state of the app when the crash happened.

7 Likes

As I think this answer could be misunderstood, I wanted to clarify: The option you mentioned leads by no means to “all dependencies” being copied into the build. It only leads to the VS C++ runtime library being statically linked into your binary. If you add any other third party library to your project, you still have to take care for each of those libraries being linked correctly.

3 Likes

Sorry, but this is some high-level C++. Can somebody tell me how this functions is even used?

Documentation says

static void SystemStats::setApplicationCrashHandler (CrashHandlerFunction)

with

using SystemStats::CrashHandlerFunction = void(*)(void*)

I tried to pass it a lambda, as well as a member function (without ()), but this doesn’t work, but it has to be a function of type

void* foo(void*)

I guess?

You should be able to pass either a lambda like this:

SystemStats::setApplicationCrashHandler ([](void*) { /* your code. don't return anything */ });

A member function declared static like this:

class MyClass
{

static void myCrashHandler (void*)
{
    // your crash handler code
}
}

// And then somewhere in an appropriate place
SystemStats::setApplicationCrashHandler (MyClass::myCrashHandler);

Or a free function declared somewhere in your compile unit like

void crashHandlerFun (void*)
{
    // your crash handler code
}

// And then somewhere in an appropriate place in the same compile unit
SystemStats::setApplicationCrashHandler (crashHandlerFun);

Edit: Just looked closer at the docs and saw that the function needs to look slightly different, as a void pointer is passed in – the docs say When called, its void* argument will contain platform-specific data about the crash.. Edited the examples above so that they take the pointer as argument but do nothing to it as the documentation tells us nothing about what kind of platform-specific data this pointer points to :grimacing:

1 Like

will result in

Error	C2664	'void juce::SystemStats::setApplicationCrashHandler(juce::SystemStats::CrashHandlerFunction)': cannot convert argument 1 from 'OdinAudioProcessor::{ctor}::<lambda_0b0f2d1967343f2a9e0fe90d41876081>' to 'juce::SystemStats::CrashHandlerFunction'	Odin2_SharedCode	E:\odinvst\Source\PluginProcessor.cpp	44	

and the “free function in compile unit” results in:

Error	C2664	'void juce::SystemStats::setApplicationCrashHandler(juce::SystemStats::CrashHandlerFunction)': cannot convert argument 1 from 'void (__cdecl *)(void)' to 'juce::SystemStats::CrashHandlerFunction'	Odin2_SharedCode	E:\odinvst\Source\PluginProcessor.cpp	44	

pretty sure I used the syntax as you destribed…

Ah yes, did you notice that I edited my post after a few minutes? Overlooked the pointer argument when I first made my post, sorry for that confusion :sweat_smile:

1 Like

On Windows, the void* is of type LPEXCEPTION_POINTERS. On all other systems, you can reinterpret_cast the void* to int and it’ll be a value such as SIGABRT.

1 Like

Thanks for that information, didn’t want to dig deeper into the code base to find this out :wink: @ juce team, what about adding this piece of information to the docs? And maybe add some helper function that would convert that piece of information into some nicely readable string representation?

2 Likes

thanks for clarifying. i never used a 3rd party library yet so i didn’t know about this

Sorry to revive an old post, but has anyone got any advice on how to handle the void* argument given to custom crash handlers as described above?

It seems really odd that I have to provide a platform-specific handler for the given data - what with JUCE being a cross-platform framework and all. Is there no way to get a meaningful description of the given error data without #if JUCE_WINDOWS, #if JUCE_MAC?

If I do have to handle it differently for each platform, what’s the recommended C++ way of doing so? It seems that for mac at least, the only option is using strsignal() which is a feature of C, not C++ (AFAIK).

Any guidance would be greatly appreciated!

That is odd, but that’s because you don’t need to do that!

The examples just above your reply are correct in that a single void function with no parameters will do the trick (ie: as described by SystemStats::CrashHandlerFunction).

static void crashHandlerFunc()
{
    // Cues crazyparrot.gif in a web browser when the user decides to crash things.
}

static void superImportantInitFunction()
{
    juce::SystemStats::setApplicationCrashHandler (&crashHandlerFunc);
}

Sorry, I think maybe I didn’t explain the problem so well.

We have something like this:

void handleCrash (void* args)
{
    juce::String errorMessage;

#if JUCE_MAC
    if (const auto signalDescription = strsignal (int ((juce::pointer_sized_int) args)))
    {
        errorMessage = juce::String {signalDescription};
    }
    else
    {
        errorMessage = "Unknown error type";
    }
#elif JUCE_WINDOWS
    const auto exceptionPointers = static_cast<LPEXCEPTION_POINTERS> (args);
    errorMessage = "Exception 0x" + juce::String::toHexString (exceptionPointers->ExceptionRecord->ExceptionCode);
#endif

    DBG (errorMessage);
}

We’re having to provide two implementations to handle mac and windows since the data in args is different for each platform (an int on mac containing a signum, and a LPEXCEPTION_POINTERS object on windows). On mac, we’re also having to use strsignal() to get a description of the given signum which, as I understand it, is a feature of C, not C++, and there doesn’t seem to be an equivalent in the STL.

So I was hoping someone might have found a nicer, cross-platform solution for this?

Not sure I understand your underlying goal with the examples… You want some kind of error code?

Seeing that you’re in the territory of native development, expecting Windows to provide the same APIs as POSIX isn’t reasonable. There’s no cleaner way from that point on…