audacia/crashreports/internal/unix/CrashReportContext.cpp

156 lines
4.4 KiB
C++

/*!********************************************************************
*
Audacity: A Digital Audio Editor
CrashReportContext.cpp
Vitaly Sverchinsky
Some parts of the code are designed to operate while app is crashing,
so there may be some restrictions on heap usage. For more information
please read Breakpad documentation.
**********************************************************************/
#include "CrashReportContext.h"
#include <errno.h>
#include <map>
#include <sstream>
#if defined(__APPLE__)
#include "client/mac/handler/exception_handler.h"
#else
#include "client/linux/handler/exception_handler.h"
#endif
bool SendReport(CrashReportContext* c, const char* minidumpPath)
{
auto pid = fork();
if(pid == 0)
{
if(c->mParameters[0] != 0)
{
execl(c->mSenderPath, CRASHREPORTER_PROGRAM_NAME, "-a", c->mParameters, "-u", c->mReportURL, minidumpPath, NULL);
}
else
{
execl(c->mSenderPath, CRASHREPORTER_PROGRAM_NAME, "-u", c->mReportURL, minidumpPath, NULL);
}
fprintf(stderr, "Failed to start handler: %s\n", strerror(errno));
abort();
}
return pid != -1;
}
namespace
{
//converts parameters map to a string, so that the Crash Reporting program
//would understand them
std::string StringifyParameters(const std::map<std::string, std::string>& parameters)
{
std::stringstream stream;
std::size_t parameterIndex = 0;
std::size_t parametersCount = parameters.size();
for (auto& pair : parameters)
{
stream << pair.first.c_str() << "=\"" << pair.second.c_str() << "\"";
++parameterIndex;
if (parameterIndex < parametersCount)
stream << ",";
}
return stream.str();
}
//copy contents of src to a raw char dest buffer,
//returns false if dest is not large enough
bool StrcpyChecked(char* dest, size_t destsz, const std::string& src)
{
if(src.length() < destsz)
{
memcpy(dest, src.c_str(), src.length());
dest[src.length()] = '\0';
return true;
}
return false;
}
#if defined(__APPLE__)
static constexpr size_t MaxDumpPathLength{ 4096 };
static char DumpPath[MaxDumpPathLength];
bool DumpCallback(const char* dump_dir, const char* minidump_id, void* context, bool succeeded)
{
if(succeeded)
{
const int PathDumpLength = strlen(dump_dir) + strlen("/") + strlen(minidump_id) + strlen(".dmp");
if(PathDumpLength < MaxDumpPathLength)
{
strcpy(DumpPath, dump_dir);
strcat(DumpPath, "/");
strcat(DumpPath, minidump_id);
strcat(DumpPath, ".dmp");
auto crashReportContext = static_cast<CrashReportContext*>(context);
return SendReport(crashReportContext, DumpPath);
}
return false;
}
return succeeded;
}
#else
bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
{
if(succeeded)
{
auto crashReportContext = static_cast<CrashReportContext*>(context);
return SendReport(crashReportContext, descriptor.path());
}
return succeeded;
}
#endif
}
bool CrashReportContext::SetSenderPathUTF8(const std::string& path)
{
return StrcpyChecked(mSenderPath, MaxBufferLength, path + "/" + CRASHREPORTER_PROGRAM_NAME);
}
bool CrashReportContext::SetReportURL(const std::string& url)
{
return StrcpyChecked(mReportURL, MaxBufferLength, url);
}
bool CrashReportContext::SetParameters(const std::map<std::string, std::string>& p)
{
auto str = StringifyParameters(p);
return StrcpyChecked(mParameters, MaxBufferLength, str);
}
void CrashReportContext::StartHandler(const std::string& databasePath)
{
//intentinal leak: error hooks may be useful while application is terminating
//CrashReportContext data should be alive too...
#if(__APPLE__)
static auto handler = new google_breakpad::ExceptionHandler(
databasePath,
nullptr,
DumpCallback,
this,
true,
nullptr
);
#else
google_breakpad::MinidumpDescriptor descriptor(databasePath);
static auto handler = new google_breakpad::ExceptionHandler(
descriptor,
nullptr,
DumpCallback,
this,
true,
-1
);
#endif
}