Beispiel #1
0
static void crash_handler(int signo) {
    int logfd = STDERR_FILENO;
    for (;;) {
        // Print out the signal
        write(logfd, C4VERSION ": Caught signal ",
              sizeof(C4VERSION ": Caught signal ") - 1);
        switch (signo) {
        case SIGBUS:
            write(logfd, "SIGBUS", sizeof("SIGBUS") - 1);
            break;
        case SIGILL:
            write(logfd, "SIGILL", sizeof("SIGILL") - 1);
            break;
        case SIGSEGV:
            write(logfd, "SIGSEGV", sizeof("SIGSEGV") - 1);
            break;
        case SIGABRT:
            write(logfd, "SIGABRT", sizeof("SIGABRT") - 1);
            break;
        case SIGINT:
            write(logfd, "SIGINT", sizeof("SIGINT") - 1);
            break;
        case SIGQUIT:
            write(logfd, "SIGQUIT", sizeof("SIGQUIT") - 1);
            break;
        case SIGFPE:
            write(logfd, "SIGFPE", sizeof("SIGFPE") - 1);
            break;
        case SIGTERM:
            write(logfd, "SIGTERM", sizeof("SIGTERM") - 1);
            break;
        }
        write(logfd, "\n", sizeof("\n") - 1);
        if (logfd == STDERR_FILENO)
            logfd = GetLogFD();
        else
            break;
        if (logfd < 0) break;
    }
    // Get the backtrace
    void *stack[100];
    int count = backtrace(stack, 100);
    // Print it out
    backtrace_symbols_fd(stack, count, STDERR_FILENO);
    // Also to the log file
    if (logfd >= 0) backtrace_symbols_fd(stack, count, logfd);
    // Bye.
    _exit(C4XRV_Failure);
}
LONG WINAPI GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{
	enum
	{
		MDST_BuildId = LastReservedStream + 1
	};

	if (!FirstCrash) return EXCEPTION_EXECUTE_HANDLER;
	FirstCrash = false;

	// Open dump file
	// Work on the assumption that the config isn't corrupted
	wchar_t *filename = reinterpret_cast<wchar_t*>(DumpBuffer);
	const size_t filename_buffer_size = DumpBufferSize / sizeof(wchar_t);
	if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, ::Config.General.UserDataPath, strnlen(::Config.General.UserDataPath, sizeof(::Config.General.UserDataPath)), filename, filename_buffer_size))
	{
		// Conversion failed; the likely reason for this is a corrupted config.
		assert (GetLastError() == ERROR_NO_UNICODE_TRANSLATION);
		// Fall back to the temporary files directory to write dump.
		DWORD temp_size = GetTempPath(filename_buffer_size, filename);
		if (temp_size == 0 || temp_size > filename_buffer_size)
		{
			// Getting the temp path failed as well; dump to current working directory as a last resort.
			temp_size = GetCurrentDirectory(filename_buffer_size, filename);
			if (temp_size == 0 || temp_size > filename_buffer_size)
			{
				// We don't really have any directory where we can store the dump, so just
				// write the text log (we already have a FD for that)
				filename[0] = L'\0';
			}
		}
	}
	HANDLE file = INVALID_HANDLE_VALUE;

	if (filename[0] != L'\0')
	{
		// There is some path where we want to store our data
		const wchar_t tmpl[] = TEXT(C4ENGINENICK) L"-crash-YYYY-MM-DD-HH-MM-SS.dmp";
		size_t path_len = wcslen(filename);
		if (path_len + sizeof(tmpl) / sizeof(*tmpl) > filename_buffer_size)
		{
			// Somehow the length of the required path is too long to fit in
			// our buffer. Don't dump anything then.
			filename[0] = L'\0';
		}
		else
		{
			// Make sure the path ends in a backslash.
			if (filename[path_len - 1] != L'\\')
			{
				filename[path_len] = L'\\';
				filename[++path_len] = L'\0';
			}
			SYSTEMTIME st;
			GetSystemTime(&st);
			wsprintf(&filename[path_len], L"%s-crash-%04d-%02d-%02d-%02d-%02d-%02d.dmp",
				TEXT(C4ENGINENICK), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
		}
	}

	if (filename[0] != L'\0')
	{
		file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
		// If we can't create a *new* file to dump into, don't dump at all.
		if (file == INVALID_HANDLE_VALUE)
			filename[0] = L'\0';
	}

	// Write dump (human readable format)
	if (GetLogFD() != -1)
		SafeTextDump(pExceptionPointers, GetLogFD(), filename);

	if (file != INVALID_HANDLE_VALUE)
	{
		auto user_stream_info = MINIDUMP_USER_STREAM_INFORMATION();
		auto user_stream = MINIDUMP_USER_STREAM();
		char build_id[] = OC_BUILD_ID;
		if (OC_BUILD_ID[0] != '\0')
		{
			user_stream.Type = MDST_BuildId;
			user_stream.Buffer = build_id;
			user_stream.BufferSize = sizeof(build_id) - 1;	// don't need the terminating NUL
			user_stream_info.UserStreamCount = 1;
			user_stream_info.UserStreamArray = &user_stream;
		}

		MINIDUMP_EXCEPTION_INFORMATION ExpParam;
		ExpParam.ThreadId = GetCurrentThreadId();
		ExpParam.ExceptionPointers = pExceptionPointers;
		ExpParam.ClientPointers = true;
		MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
							file, MiniDumpNormal, &ExpParam, &user_stream_info, NULL);
		CloseHandle(file);
	}

	// Pass exception
	return EXCEPTION_EXECUTE_HANDLER;
}