sentry-native: Minidump files get placed in the wrong directory and aren't uploaded to Sentry

Description

I’m using the Native SDK with the bundled crashpad_handler, and I noticed that crashes for some users wouldn’t get uploaded to Sentry. After some digging I realized that for those users that didn’t get their crashes uploaded, the minidump (dmp) files got placed in a different directory from the one configured with sentry_options_set_database_path. The directory they get placed in is [user]/Local/AppData/CrashDumps. It only happens for some users, but for those this is consistent.

Does anyone know what could be the cause of this? Or what I could do to debug it further? I’ve looked in the documentation and source code, but I haven’t been able to figure out how I get the crashpad_handler to log stuff.

Thanks in advance!

When does the problem happen

  • During build
  • During run-time
  • When capturing a hard crash

Environment

  • OS: Microsoft Windows 10 Business, 64 bit, Version 10.0.19045 Build 19045
  • Compiler: Visual Studio 2022, 17.3.6
  • CMake version and config: 3.25.2, -DBUILD_SHARED_LIBS=OFF -DSENTRY_BUILD_RUNTIMESTATIC=ON -DSENTRY_BACKEND=crashpad

Steps To Reproduce

It’s only reproducible on some machines, but this is how I set up the crash handler in my app:

	sentry_options_t* options = sentry_options_new();
	sentry_options_set_dsn(options, "<a dsn value>");
	sentry_options_set_debug(options, true);
	sentry_options_set_environment(options, "Development");
	sentry_options_set_release(options, "0");
	sentry_options_set_handler_path(options, "<working directory>");
	sentry_options_set_database_path(options, "<working directory>/sentry-db");
	sentry_init(options);
	sentry_set_transaction("<app name>");
	sentry_set_level(SENTRY_LEVEL_WARNING);

Log output

I’ve compared the Sentry Native output between a computer where everything is working fine and one where it is not, and they are identical. I don’t know how to get output from the crashpad_handler.

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 16 (5 by maintainers)

Most upvoted comments

If you run this after sentry_init it works for me:

void lockUnhandledExceptionFilter()
{
	HMODULE kernel = GetModuleHandle("kernel32.dll");
	if (!kernel)
	{
		printf("[Warning] LockUnhandledExceptionFilter: Failed locating kernel32 module.\n");
		return;
	}

	FARPROC setUnhandledExceptionFilter = GetProcAddress(kernel, "SetUnhandledExceptionFilter");
	if (!setUnhandledExceptionFilter)
	{
		printf("[Warning] LockUnhandledExceptionFilter: Failed locating SetUnhandledExceptionFilter.\n");
		return;
	}

	#ifdef Q_OS_WIN64
	static const BYTE newBody[] = {
		0x33, 0xC0, // xor eax,eax
		0xC3 // ret
	};
	#else
	static const BYTE newBody[] = {
		0x33, 0xC0, // xor eax,eax
		0xC2, 0x04, 0x00, // ret 4
	};
	#endif
	SIZE_T bytes = 0;
	if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)setUnhandledExceptionFilter, (LPCVOID)newBody, sizeof(newBody), &bytes) == 0)
	{
		printf("[Warning] LockUnhandledExceptionFilter: Failed writing process memory.\n");
	}
}

I tried to simplify the other examples I found as much as possible but I’m no Windows programmer so take it with a grain of salt.

The best way to get to the bottom of what it is that clears the filter would be to make a proper hook, and print the callstack in there, but its somewhat involved and I’m not sure what I would do with the info anyway since I guess it theoretically could be multiple things.

To get the WER module you have to build sentry-native with CRASHPAD_WER_ENABLED

IMO hooking the UnhandledExceptionFilter completely is a really big hammer. We might be breaking apps in surprising ways that we can’t predict yet.

There are legitimate uses for temporarily using it and then restoring the original handler afterwards. We don’t want to break those usecases. Although as the examples show, restoring the original handler is often not done correctly by other tools either.

We might want to offer this either as a compile time flag or an explicit runtime API though, wdyt @supervacuus ? Then the responsibility of (not) breaking any unpredictable usecases is up to the app author.