mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-29 23:29:41 +02:00
166 lines
5.2 KiB
C++
166 lines
5.2 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 <locale>
|
|
#include <codecvt>
|
|
#include <sstream>
|
|
#include "client/windows/handler/exception_handler.h"
|
|
|
|
namespace
|
|
{
|
|
//copy src(null-terminated) to dst,
|
|
//returns false if dest is not large enough
|
|
bool StrcpyChecked(wchar_t* dest, size_t destsz, const wchar_t* src)
|
|
{
|
|
auto len = wcslen(src);
|
|
if (len < destsz)
|
|
{
|
|
memcpy(dest, src, sizeof(wchar_t) * len);
|
|
dest[len] = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//appends src(null-terminated) to dest, destsz is the total dest buffer size, not remaining
|
|
//returns false if dest is not large enough
|
|
bool StrcatChecked(wchar_t* dest, size_t destsz, const wchar_t* src)
|
|
{
|
|
auto srclen = wcslen(src);
|
|
auto dstlen = wcslen(dest);
|
|
if (srclen + dstlen < destsz)
|
|
{
|
|
memcpy(dest + dstlen, src, sizeof(wchar_t) * srclen);
|
|
dest[srclen + dstlen] = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//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();
|
|
}
|
|
}
|
|
|
|
bool MakeCommand(CrashReportContext* c, const wchar_t* path, const wchar_t* id)
|
|
{
|
|
//utility path
|
|
auto ok = StrcpyChecked(c->mCommand, CrashReportContext::MaxCommandLength, L"\"");
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, c->mSenderPath);
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, L"\"");
|
|
|
|
//parameters: /p "..."
|
|
if (ok && c->mParameters[0] != 0)
|
|
{
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, L" /a \"");
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, c->mParameters);
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, L"\"");
|
|
}
|
|
//crash report URL: /u https://...
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxBufferLength, L" /u \"");
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxBufferLength, c->mReportURL);
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxBufferLength, L"\" ");
|
|
//minidump path: path/to/minidump.dmp
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, L" \"");
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, path);
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, L"\\");
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, id);
|
|
ok = ok && StrcatChecked(c->mCommand, CrashReportContext::MaxCommandLength, L".dmp\"");
|
|
return ok;
|
|
}
|
|
|
|
bool SendReport(CrashReportContext* c, const wchar_t* path, const wchar_t* id)
|
|
{
|
|
if (!MakeCommand(c, path, id))
|
|
return false;
|
|
|
|
STARTUPINFOW si;
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_SHOW;
|
|
|
|
PROCESS_INFORMATION pi;
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
|
|
if (CreateProcessW(NULL, c->mCommand, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
|
|
{
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool UploadReport(
|
|
const wchar_t* dump_path,
|
|
const wchar_t* minidump_id,
|
|
void* context,
|
|
EXCEPTION_POINTERS* /*exinfo*/,
|
|
MDRawAssertionInfo* /*assertion*/,
|
|
bool succeeded)
|
|
{
|
|
CrashReportContext* crashReportContext = static_cast<CrashReportContext*>(context);
|
|
if (!SendReport(crashReportContext, dump_path, minidump_id))
|
|
return false;
|
|
return succeeded;
|
|
}
|
|
|
|
bool CrashReportContext::SetSenderPathUTF8(const std::string& path)
|
|
{
|
|
auto fullpath = path + "\\" + CRASHREPORTER_PROGRAM_NAME;
|
|
return StrcpyChecked(mSenderPath, MaxBufferLength, std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes(fullpath).c_str());
|
|
}
|
|
|
|
bool CrashReportContext::SetReportURL(const std::string& url)
|
|
{
|
|
return StrcpyChecked(mReportURL, MaxBufferLength, std::wstring(url.begin(), url.end()).c_str());
|
|
}
|
|
|
|
bool CrashReportContext::SetParameters(const std::map<std::string, std::string>& p)
|
|
{
|
|
auto str = StringifyParameters(p);
|
|
return StrcpyChecked(mParameters, MaxBufferLength, std::wstring(str.begin(), str.end()).c_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...
|
|
static auto handler = new google_breakpad::ExceptionHandler(
|
|
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes(databasePath),
|
|
NULL,
|
|
UploadReport,
|
|
this,
|
|
google_breakpad::ExceptionHandler::HANDLER_ALL);
|
|
}
|
|
|