I’d be fuming if plugins did that to us
Would all crash handlers then be executed? or just the last one that registered?
I imagine a crashed session with >5 different report crash windows…
My experience is you only get one. I reckon you could find a way of chaining the old one though…
I know Chromium Embedded tries to steal our handler and we have to steal it back
I came across the thread for this recently. Could be helpful to some reading about this topic: https://github.com/talaviram/SymbolFriendlyPlugIn
Hello guys ! Sorry for resurrecting this old topic but what do you think of using Sentry as a crash reporter ?
It provides a Native C++ SDK with an installation tutorial.
Would it be a lot of development to make it work ?
We shortly tried using sentry some time ago, maybe something has changed since then, but these were our problems:
The default crashpad backend of sentry does not allow for preprocessing of the error events, since its out of process (since the before_send
hook can’t be called). Without filtering, you would just get spammed with errors not related to your plugin.
Using breakpad instead, we had to disable sentry for a lot of hosts in order for the plugin to work at all. Then there also was an interaction with some of the SSL Plugins, that would cause sessions to crash only when both one of these plugins and one of our plugins with sentry were loaded in the session.
Maybe there is a way to make sentry work with the crashpad backend or another backend.
Thanks for the insight, @moritzsur.
It’s disappointing to hear this… It seems neither backend (Crashpad nor Breakpad) worked as expected.
Did you end up using a different solution?
@moritzsur after using the library a bit more, it seems that the before_send
hook is indeed being called (using sentry_options_set_before_send
).
Since there have been many updates to this library, did this function exist when you tested Sentry?
Sorry for the late response! We did not end up using a different solution since we had prioritize other stuff, but we would love to do that at some point.
From taking a quick look at the sentry native docs, it seems like crashpad still has some limitations that would make it unreliable.
Maybe the warnings are just outdated though.
Also don’t underestimate filtering crash events, even with symbol filtering we still got a ton of crashes from other plugins. I guess that’s another reason why I stopped looking into it.
In the hope that it helps, I found the (kinda temporary) code for filtering:
#ifndef JUCE_MAC
#include <DbgHelp.h>
#include <Windows.h>
#pragma comment(lib, "DbgHelp.lib")
#else
#include <dlfcn.h>
#endif
static bool isFromProject (void** stacktrace, size_t stack_depth)
{
// List of identifiers that indicate the stack frame is from your library
const std::vector<std::string> projectIdentifiers = {
JucePlugin_Name
// Add more identifiers if necessary
};
#define DBG_FROM_PROJECT 0
#if DBG_FROM_PROJECT
auto tempFile = juce::File::getSpecialLocation (juce::File::SpecialLocationType::userDesktopDirectory)
.getChildFile ("crash_origin.txt");
#endif
auto containsInsensitive = [] (const std::string& str, const std::string& substr) {
return std::search (
str.begin(),
str.end(),
substr.begin(),
substr.end(),
[] (char ch1, char ch2) { return std::toupper (ch1) == std::toupper (ch2); })
!= str.end();
};
#ifndef JUCE_MAC
HANDLE process = GetCurrentProcess();
// Set symbol options
SymSetOptions (SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
if (!SymInitialize (process, NULL, FALSE))
{
DWORD error = GetLastError();
#if DBG_FROM_PROJECT
tempFile.replaceWithText ("SymInitialize failed with error code: " + std::to_string (error));
#endif
return false;
}
SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc (sizeof (SYMBOL_INFO) + 256 * sizeof (char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof (SYMBOL_INFO);
bool result = false;
for (size_t i = 0; i < stack_depth; i++)
{
if (result)
break;
DWORD64 address = (DWORD64) (stacktrace[i]);
if (SymFromAddr (process, address, 0, symbol))
{
char undecoratedName[256];
UnDecorateSymbolName (symbol->Name, undecoratedName, sizeof (undecoratedName), UNDNAME_COMPLETE);
std::string symbol_name = undecoratedName;
IMAGEHLP_MODULE64 moduleInfo = { 0 };
moduleInfo.SizeOfStruct = sizeof (IMAGEHLP_MODULE64);
std::string moduleName;
if (SymGetModuleInfo64 (process, address, &moduleInfo))
{
moduleName = moduleInfo.ModuleName;
}
#if DBG_FROM_PROJECT
tempFile.appendText ("Symbol name: " + symbol_name + ", Module name: " + moduleName + "\n");
#endif
for (const auto& identifier : projectIdentifiers)
{
if (containsInsensitive (symbol_name, identifier) || containsInsensitive (moduleName, identifier))
{
#if DBG_FROM_PROJECT
tempFile.appendText ("Crash originates from " + std::string (JucePlugin_Name) + "\n");
#endif
result = true;
break;
}
}
}
else
{
DWORD error = GetLastError();
#if DBG_FROM_PROJECT
tempFile.appendText ("SymFromAddr failed at address " + std::to_string (address) + " with error code: " + std::to_string (error) + "\n");
#endif
}
}
free (symbol);
SymCleanup (process);
#if DBG_FROM_PROJECT
if (!result)
tempFile.replaceWithText ("Crash does not originate from " + std::string (JucePlugin_Name));
#endif
return result;
#else // JUCE_MAC
Dl_info dlInfo;
for (size_t i = 0; i < stack_depth; i++)
{
if (dladdr (stacktrace[i], &dlInfo) != 0)
{
const char* symbolName = dlInfo.dli_sname;
const char* objFilename = dlInfo.dli_fname;
for (const auto& identifier : projectIdentifiers)
{
if ((symbolName && containsInsensitive (symbolName, identifier))
|| (objFilename && containsInsensitive (objFilename, identifier)))
return true;
}
}
}
#endif // JUCE_MAC
#if DBG_FROM_PROJECT
tempFile.replaceWithText ("Crash does not originate from " JucePlugin_Name);
#endif
#undef DBG_FROM_PROJECT
return false;
}
Don’t worry @moritzsur
TBH, I’m not entirely sure what’s happening . I didn’t do anything unusual, so I’m not sure if the documentation is outdated or if I’m making a mistake.
I just called this function sentry_options_set_before_send(options, strip_sensitive_data, NULL);
and used this callback sentry_value_t strip_sensitive_data(sentry_value_t event, void* hint, void* closure)
As you can see in the screenshot, the application is entering the strip_sensitive_data
function.
However, it might be a bug since the value inside the callback doesn’t seem to be valid .
I also know it’s possible to filter incoming issues directly on the Sentry dashboard. Did you use it to filter only the relevant issues?
I’m running out of ideas as to why I can’t get crash reports when using Crashpad.
I used all these External Libraries to Link
:
- sentry
- crashpad_client
- crashpad_handler_lib
- crashpad_minidump
- crashpad_snapshot
- crashpad_tools
- crashpad_util
- mini_chromium
I tried placing libsentry.dylib
and crashpad_handler
both inside and outside the application, but it still doesn’t work.
Additionally, if I catch the crash manually using juce::SystemStats::setApplicationCrashHandler(crashHandler);
, I’m not sure how to automatically obtain the dump file that Sentry generates (or creates?).