Browse Source

💩 Remove Breakpad Crash Reports

pull/2/head
Cookie Engineer 1 year ago
parent
commit
2a37cd19a0
No known key found for this signature in database GPG Key ID: 340F6A4848C5F849
  1. 2
      .github/workflows/cmake_build.yml
  2. 7
      CMakeLists.txt
  3. 4
      cmake-proxies/CMakeLists.txt
  4. 51
      crashreports/BreakpadConfigurer.cpp
  5. 45
      crashreports/BreakpadConfigurer.h
  6. 53
      crashreports/CMakeLists.txt
  7. 41
      crashreports/crashreporter/CMakeLists.txt
  8. 464
      crashreports/crashreporter/CrashReportApp.cpp
  9. 36
      crashreports/crashreporter/CrashReportApp.h
  10. 138
      crashreports/crashreporter/warning.xpm
  11. 155
      crashreports/internal/unix/CrashReportContext.cpp
  12. 43
      crashreports/internal/unix/CrashReportContext.h
  13. 165
      crashreports/internal/win32/CrashReportContext.cpp
  14. 47
      crashreports/internal/win32/CrashReportContext.h
  15. 35
      src/AudacityApp.cpp
  16. 7
      src/CMakeLists.txt
  17. 119
      src/CrashReport.cpp
  18. 36
      src/CrashReport.h
  19. 4
      src/Experimental.cmake
  20. 16
      src/menus/HelpMenus.cpp

2
.github/workflows/cmake_build.yml

@ -80,8 +80,6 @@ jobs:
- name: Configure
env:
# Error reporing
CRASH_REPORT_URL: ${{ secrets.CRASH_REPORT_URL }}
# Apple code signing
APPLE_CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }}
APPLE_NOTARIZATION_USER_NAME: ${{ secrets.APPLE_NOTARIZATION_USER_NAME }}

7
CMakeLists.txt

@ -204,10 +204,6 @@ elseif( CMAKE_SYSTEM_NAME MATCHES "Darwin" )
message( STATUS " MacOS SDK: ${CMAKE_OSX_SYSROOT}" )
message( STATUS )
if(${_OPT}has_crashreports)
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym")
endif()
endif()
# Try to get the current commit information
@ -476,9 +472,6 @@ add_subdirectory( "cmake-proxies" )
resolve_conan_dependencies()
add_subdirectory( "help" )
if(${_OPT}has_crashreports)
add_subdirectory( "crashreports" )
endif()
add_subdirectory( "images" )
add_subdirectory( "libraries" )
add_subdirectory( "locale" )

4
cmake-proxies/CMakeLists.txt

@ -18,10 +18,6 @@ add_conan_lib(
expat:shared=True
)
if(${_OPT}has_crashreports)
add_conan_lib(breakpad breakpad/0.1 REQUIRED)
endif()
set( wx_zlib "zlib" )
set( wx_png "libpng" )

51
crashreports/BreakpadConfigurer.cpp

@ -1,51 +0,0 @@
/*!********************************************************************
*
Audacity: A Digital Audio Editor
BreakpadConfigurer.cpp
Vitaly Sverchinsky
**********************************************************************/
#include "BreakpadConfigurer.h"
#if defined(WIN32)
#include "internal/win32/CrashReportContext.h"
#else
#include "internal/unix/CrashReportContext.h"
#endif
BreakpadConfigurer& BreakpadConfigurer::SetDatabasePathUTF8(const std::string& pathUTF8)
{
mDatabasePathUTF8 = pathUTF8;
return *this;
}
BreakpadConfigurer& BreakpadConfigurer::SetReportURL(const std::string& reportURL)
{
mReportURL = reportURL;
return *this;
}
BreakpadConfigurer& BreakpadConfigurer::SetParameters(const std::map<std::string, std::string>& parameters)
{
mParameters = parameters;
return *this;
}
BreakpadConfigurer& BreakpadConfigurer::SetSenderPathUTF8(const std::string& pathUTF8)
{
mSenderPathUTF8 = pathUTF8;
return *this;
}
void BreakpadConfigurer::Start()
{
static CrashReportContext context{};
bool ok = context.SetSenderPathUTF8(mSenderPathUTF8);
ok = ok && context.SetReportURL(mReportURL);
ok = ok && context.SetParameters(mParameters);
if (ok)
context.StartHandler(mDatabasePathUTF8);
}

45
crashreports/BreakpadConfigurer.h

@ -1,45 +0,0 @@
/*!********************************************************************
*
Audacity: A Digital Audio Editor
BreakpadConfigurer.h
Vitaly Sverchinsky
**********************************************************************/
#pragma once
#include <string>
#include <map>
//! This class is used to configure Breakpad's handler before start.
/*! Typically handler should be started as early as possible.
* BreakpadConfigurer may be a short living object, it is used to configure
* Breakpad handler, and run it with BreakpadConfigurer::Start() method,
* It's expected that Start() will be called once during application
* lifetime, any calls to Set* methods after handler is started will be ignored.
* The handler itself simply starts crash sender program, passing all the details
* (path crash dump, report url, parameters...) as a command-line arguments to it.
* Please read official documentation for details:
* https://chromium.googlesource.com/breakpad/breakpad
*/
class BreakpadConfigurer final
{
std::string mDatabasePathUTF8;
std::string mSenderPathUTF8;
std::string mReportURL;
std::map<std::string, std::string> mParameters;
public:
//! Sets the directory where crashreports will be stored (should have rw permission)
BreakpadConfigurer& SetDatabasePathUTF8(const std::string& pathUTF8);
//! Sets report URL to the crash reporting server (URL-Encoded, optional)
BreakpadConfigurer& SetReportURL(const std::string& reportURL);
//! Sets an additional parameters that should be sent to a crash reporting server (ASCII encoded)
BreakpadConfigurer& SetParameters(const std::map<std::string, std::string>& parameters);
//! Sets a path to a directory where crash reporter sending program is located
BreakpadConfigurer& SetSenderPathUTF8(const std::string& pathUTF8);
//! Starts the handler
void Start();
};

53
crashreports/CMakeLists.txt

@ -1,53 +0,0 @@
# This module provides an interface to configure and start Breakpad handler
# in a platform independent way.
set(TARGET crashreports)
set(TARGET_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
message( STATUS "========== Configuring ${TARGET} ==========" )
add_library(${TARGET} STATIC)
set(SOURCES "")
set(INCLUDES INTERFACE ./)
set(LIBRARIES "")
set(DEFINIES "")
# also adding Crash Reporting dialog
add_subdirectory(crashreporter)
list(APPEND SOURCES
PRIVATE
BreakpadConfigurer.h
BreakpadConfigurer.cpp
)
list(APPEND LIBRARIES
PRIVATE
breakpad::client
)
list(APPEND DEFINES
PUBLIC
-DUSE_BREAKPAD
PRIVATE
-DCRASHREPORTER_PROGRAM_NAME="$<TARGET_FILE_NAME:crashreporter>"
)
if(WIN32)
list(APPEND SOURCES
PRIVATE
internal/win32/CrashReportContext.h
internal/win32/CrashReportContext.cpp
)
elseif(UNIX)
list(APPEND SOURCES
PRIVATE
internal/unix/CrashReportContext.h
internal/unix/CrashReportContext.cpp)
endif()
target_include_directories(${TARGET} ${INCLUDES})
target_sources(${TARGET} ${SOURCES})
target_link_libraries(${TARGET} ${LIBRARIES})
target_compile_definitions(${TARGET} ${DEFINES})
organize_source( "${TARGET_ROOT}" "" "${SOURCES}" )

41
crashreports/crashreporter/CMakeLists.txt

@ -1,41 +0,0 @@
#Adds a Crash Reporting dialog which may be invoked by a crashing program
set(TARGET crashreporter)
set(TARGET_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
message( STATUS "========== Configuring ${TARGET} ==========" )
set(SOURCES
PRIVATE
warning.xpm
CrashReportApp.h
CrashReportApp.cpp
)
add_executable(${TARGET})
target_sources(${TARGET} ${SOURCES})
target_link_libraries(${TARGET} breakpad::processor breakpad::sender wxwidgets::wxwidgets)
if(WIN32)
set_target_properties(${TARGET} PROPERTIES WIN32_EXECUTABLE ON)
endif()
if( CMAKE_SYSTEM_NAME MATCHES "Darwin" )
add_custom_command(
TARGET
${TARGET}
COMMAND
${CMAKE_COMMAND} -D SRC="${_EXEDIR}/crashreporter"
-D DST="${_PKGLIB}"
-D WXWIN="${_SHARED_PROXY_BASE_PATH}/$<CONFIG>"
-P ${AUDACITY_MODULE_PATH}/CopyLibs.cmake
POST_BUILD
)
elseif(UNIX)
target_compile_definitions(${TARGET} PRIVATE -DINSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${TARGET} RUNTIME)
endif()
set_target_property_all( ${TARGET} RUNTIME_OUTPUT_DIRECTORY "${_EXEDIR}" )
organize_source( "${TARGET_ROOT}" "" "${SOURCES}" )

464
crashreports/crashreporter/CrashReportApp.cpp

@ -1,464 +0,0 @@
#include "CrashReportApp.h"
#include <sstream>
#include <memory>
#include <wx/cmdline.h>
#include <wx/chartype.h>
#include <wx/artprov.h>
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/minidump_processor.h"
#include "google_breakpad/processor/process_state.h"
#include "google_breakpad/processor/minidump.h"
#include "processor/stackwalk_common.h"
#include "warning.xpm"
//Temporary solution until lib-strings is added
#define XC(msg, ctx) (wxGetTranslation(msg, wxEmptyString, ctx))
#if defined(_WIN32)
#include <locale>
#include <codecvt>
#include "client/windows/sender/crash_report_sender.h"
namespace
{
std::wstring ToPlatformString(const std::string& utf8)
{
return std::wstring_convert<std::codecvt_utf8<std::wstring::traits_type::char_type>, std::wstring::traits_type::char_type>().from_bytes(utf8);
}
bool SendMinidump(const std::string& url, const wxString& minidumpPath, const std::map<std::string, std::string>& arguments, const wxString& commentsFilePath)
{
std::map<std::wstring, std::wstring> files;
files[L"upload_file_minidump"] = minidumpPath.wc_str();
if (!commentsFilePath.empty())
{
files[wxFileName(commentsFilePath).GetFullName().wc_str()] = commentsFilePath.wc_str();
}
std::map<std::wstring, std::wstring> parameters;
for (auto& p : arguments)
{
parameters[ToPlatformString(p.first)] = ToPlatformString(p.second);
}
google_breakpad::CrashReportSender sender(L"");
auto result = sender.SendCrashReport(
ToPlatformString(url),
parameters,
files,
nullptr
);
return result == google_breakpad::RESULT_SUCCEEDED;
}
}
#else
#include "common/linux/http_upload.h"
namespace
{
bool SendMinidump(const std::string& url, const wxString& minidumpPath, const std::map<std::string, std::string>& arguments, const wxString& commentsFilePath)
{
std::map<std::string, std::string> files;
files["upload_file_minidump"] = minidumpPath.ToStdString();
if (!commentsFilePath.empty())
{
files["comments.txt"] = commentsFilePath.ToStdString();
}
std::string response, error;
bool success = google_breakpad::HTTPUpload::SendRequest(
url,
arguments,
files,
std::string(),
std::string(),
std::string(),
&response,
NULL,
&error);
return success;
}
}
#endif
IMPLEMENT_APP(CrashReportApp);
namespace
{
std::map<std::string, std::string> parseArguments(const std::string& str)
{
int TOKEN_IDENTIFIER{ 0 };
constexpr int TOKEN_EQ{ 1 };
constexpr int TOKEN_COMMA{ 2 };
constexpr int TOKEN_VALUE{ 3 };
int i = 0;
std::string key;
int state = TOKEN_COMMA;
std::map<std::string, std::string> result;
while (true)
{
if (str[i] == 0)
break;
else if (isspace(str[i]))
++i;
else if (isalpha(str[i]))
{
if (state != TOKEN_COMMA)
throw std::logic_error("malformed parameters string: unexpected identifier");
int begin = i;
while (isalnum(str[i]))
++i;
key = str.substr(begin, i - begin);
state = TOKEN_IDENTIFIER;
}
else if (str[i] == '=')
{
if (state != TOKEN_IDENTIFIER)
throw std::logic_error("malformed parameters string: unexpected '=' symbol");
++i;
state = TOKEN_EQ;
}
else if (str[i] == '\"')
{
if (state != TOKEN_EQ)
throw std::logic_error("malformed parameters string: unexpected '\"' symbol");
int begin = ++i;
while (true)
{
if (str[i] == 0)
throw std::logic_error("unterminated string literal");
else if (str[i] == '\"')
{
if (i > begin)
result[key] = str.substr(begin, i - begin);
else
result[key] = std::string();
++i;
state = TOKEN_VALUE;
break;
}
++i;
}
}
else if (str[i] == ',')
{
if (state != TOKEN_VALUE)
throw std::logic_error("malformed parameters string: unexpected ',' symbol");
state = TOKEN_COMMA;
++i;
}
else
throw std::logic_error("malformed parameters string");
}
if (state != TOKEN_VALUE)
throw std::logic_error("malformed parameters string");
return result;
}
void PrintMinidump(google_breakpad::Minidump& minidump)
{
google_breakpad::BasicSourceLineResolver resolver;
google_breakpad::MinidumpProcessor minidumpProcessor(nullptr, &resolver);
google_breakpad::MinidumpThreadList::set_max_threads(std::numeric_limits<uint32_t>::max());
google_breakpad::MinidumpMemoryList::set_max_regions(std::numeric_limits<uint32_t>::max());
google_breakpad::ProcessState processState;
if (minidumpProcessor.Process(&minidump, &processState) != google_breakpad::PROCESS_OK)
{
printf("Failed to process minidump");
}
else
{
google_breakpad::PrintProcessState(processState, true, &resolver);
}
}
wxString MakeDumpString(google_breakpad::Minidump& minidump, const wxString& temp)
{
#if _WIN32
auto stream = _wfreopen(temp.wc_str(), L"w+", stdout);
#else
auto stream = freopen(temp.utf8_str().data(), "w+", stdout);
#endif
if (stream == NULL)
throw std::runtime_error("Failed to print minidump: cannot open temp file");
PrintMinidump(minidump);
fflush(stdout);
auto length = ftell(stream);
std::vector<char> bytes(length);
fseek(stream, 0, SEEK_SET);
fread(&bytes[0], 1, length, stream);
fclose(stream);
#if _WIN32
_wremove(temp.wc_str());
#else
remove(temp.utf8_str().data());
#endif
return wxString::From8BitData(&bytes[0], bytes.size());
}
wxString MakeHeaderString(google_breakpad::Minidump& minidump)
{
if (auto exception = minidump.GetException())
{
if (auto rawException = exception->exception())
{
// i18n-hint C++ programming assertion
return wxString::Format(_("Exception code 0x%x"), rawException->exception_record.exception_code);
}
else
{
// i18n-hint C++ programming assertion
return _("Unknown exception");
}
}
else if (auto assertion = minidump.GetAssertion())
{
auto expression = assertion->expression();
if (!expression.empty())
{
return expression;
}
}
return _("Unknown error");
}
void DoShowCrashReportFrame(const wxString& header, const wxString& dump, const std::function<bool(const wxString& comment)>& onSend)
{
static constexpr int MaxUserCommentLength = 2000;
auto frame = new wxFrame(
nullptr,
wxID_ANY,
_("Problem Report for Audacity"),
wxDefaultPosition,
wxDefaultSize,
wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX)//disable frame resize
);
frame->SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
auto mainLayout = new wxBoxSizer(wxVERTICAL);
auto headerText = new wxStaticText(frame, wxID_ANY, header);
headerText->SetFont(wxFont(wxFontInfo().Bold()));
auto headerLayout = new wxBoxSizer(wxHORIZONTAL);
headerLayout->Add(new wxStaticBitmap(frame, wxID_ANY, wxIcon(warning)));
headerLayout->AddSpacer(5);
headerLayout->Add(headerText, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
auto buttonsLayout = new wxBoxSizer(wxHORIZONTAL);
wxTextCtrl* commentCtrl = nullptr;
if (onSend != nullptr)
{
commentCtrl = new wxTextCtrl(frame, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(500, 100), wxTE_MULTILINE);
commentCtrl->SetMaxLength(MaxUserCommentLength);
}
if (onSend != nullptr)
{
auto okButton = new wxButton(frame, wxID_ANY, XC("&Don't send", "crash reporter button"));
auto sendButton = new wxButton(frame, wxID_ANY, XC("&Send", "crash reporter button"));
okButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
{
frame->Close(true);
});
sendButton->Bind(wxEVT_BUTTON, [frame, commentCtrl, onSend](wxCommandEvent&)
{
if (onSend(commentCtrl->GetValue()))
{
frame->Close(true);
}
});
buttonsLayout->Add(okButton);
buttonsLayout->AddSpacer(5);
buttonsLayout->Add(sendButton);
}
else
{
auto okButton = new wxButton(frame, wxID_OK, wxT("OK"));
okButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
{
frame->Close(true);
});
buttonsLayout->Add(okButton);
}
mainLayout->Add(headerLayout, wxSizerFlags().Border(wxALL));
if (onSend != nullptr)
{
mainLayout->AddSpacer(5);
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Click \"Send\" to submit the report to Audacity. This information is collected anonymously.")), wxSizerFlags().Border(wxALL));
}
mainLayout->AddSpacer(10);
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Problem details")), wxSizerFlags().Border(wxALL));
auto dumpTextCtrl = new wxTextCtrl(frame, wxID_ANY, dump, wxDefaultPosition, wxSize(500, 300), wxTE_RICH | wxTE_READONLY | wxTE_MULTILINE | wxTE_DONTWRAP);
dumpTextCtrl->SetFont(wxFont(wxFontInfo().Family(wxFONTFAMILY_TELETYPE)));
dumpTextCtrl->ShowPosition(0);//scroll to top
mainLayout->Add(dumpTextCtrl, wxSizerFlags().Border(wxALL).Expand());
if (onSend != nullptr)
{
mainLayout->AddSpacer(10);
mainLayout->Add(new wxStaticText(frame, wxID_ANY, _("Comments")), wxSizerFlags().Border(wxALL));
mainLayout->Add(commentCtrl, wxSizerFlags().Border(wxALL).Expand());
}
mainLayout->Add(buttonsLayout, wxSizerFlags().Border(wxALL).Align(wxALIGN_RIGHT));
frame->SetSizerAndFit(mainLayout);
frame->Show(true);
}
}
bool CrashReportApp::OnInit()
{
if (!wxApp::OnInit())
return false;
if (mSilent)
{
if (!mURL.empty())
SendMinidump(mURL, mMinidumpPath, mArguments, wxEmptyString);
}
else
{
static std::unique_ptr<wxLocale> sLocale(new wxLocale(wxLANGUAGE_DEFAULT));
#if defined(__WXOSX__)
sLocale->AddCatalogLookupPathPrefix(wxT("../Resources"));
#elif defined(__WXMSW__)
sLocale->AddCatalogLookupPathPrefix(wxT("Languages"));
#elif defined(__WXGTK__)
sLocale->AddCatalogLookupPathPrefix(wxT("./locale"));
sLocale->AddCatalogLookupPathPrefix(wxString::Format(wxT("%s/share/locale"), wxT(INSTALL_PREFIX)));
#endif
sLocale->AddCatalog("audacity");
sLocale->AddCatalog("wxstd");
google_breakpad::Minidump minidump(mMinidumpPath.ToStdString(), false);
if (minidump.Read())
{
SetExitOnFrameDelete(true);
wxFileName temp(mMinidumpPath);
temp.SetExt("tmp");
try
{
ShowCrashReport(MakeHeaderString(minidump), MakeDumpString(minidump, temp.GetFullPath()));
}
catch (std::exception& e)
{
wxMessageBox(e.what());
return false;
}
return true;
}
}
return false;
}
void CrashReportApp::OnInitCmdLine(wxCmdLineParser& parser)
{
static const wxCmdLineEntryDesc cmdLineEntryDesc[] =
{
{ wxCMD_LINE_SWITCH, "h", "help", "Display help on the command line parameters", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
{ wxCMD_LINE_SWITCH, "s", "silent", "Send without displaying the confirmation dialog" },
{ wxCMD_LINE_OPTION, "u", "url", "Crash report server URL", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_OPTION, "a", "args", "A set of arguments to send", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_PARAM, NULL, NULL, "path to minidump file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
{ wxCMD_LINE_NONE }
};
parser.SetDesc(cmdLineEntryDesc);
wxApp::OnInitCmdLine(parser);
}
bool CrashReportApp::OnCmdLineParsed(wxCmdLineParser& parser)
{
wxString url;
wxString arguments;
if (parser.Found("u", &url))
{
mURL = url.ToStdString();
}
if (parser.Found("a", &arguments))
{
try
{
mArguments = parseArguments(arguments.ToStdString());
}
catch (std::exception& e)
{
wxMessageBox(e.what());
return false;
}
}
mMinidumpPath = parser.GetParam(0);
mSilent = parser.Found("s");
return wxApp::OnCmdLineParsed(parser);
}
void CrashReportApp::ShowCrashReport(const wxString& header, const wxString& text)
{
if (mURL.empty())
{
DoShowCrashReportFrame(header, text, nullptr);
}
else
{
DoShowCrashReportFrame(header, text, [this](const wxString& comments)
{
wxString commentsFilePath;
if (!comments.empty())
{
wxFileName temp(mMinidumpPath);
temp.SetName(temp.GetName() + "-comments");
temp.SetExt("txt");
commentsFilePath = temp.GetFullPath();
wxFile file;
if (file.Open(commentsFilePath, wxFile::write))
{
file.Write(comments);
file.Close();
}
}
auto result = SendMinidump(mURL, mMinidumpPath, mArguments, commentsFilePath);
if (!commentsFilePath.empty())
wxRemoveFile(commentsFilePath);
if (!result)
{
wxMessageBox(_("Failed to send crash report"));
}
return result;
});
}
}

36
crashreports/crashreporter/CrashReportApp.h

@ -1,36 +0,0 @@
/*!********************************************************************
*
Audacity: A Digital Audio Editor
CrashReportApp.h
Vitaly Sverchinsky
**********************************************************************/
#include <wx/wx.h>
#include <map>
#include <string>
//! Crash reporter GUI application
/*! Used to send crash reports to a remote server, or view them.
* Shows brief report content, and allows user to send report to developers.
* Reporting URL and other parameters are specified as a command line arguments.
*/
class CrashReportApp final : public wxApp
{
std::string mURL;
wxString mMinidumpPath;
std::map<std::string, std::string> mArguments;
bool mSilent{ false };
public:
bool OnInit() override;
void OnInitCmdLine(wxCmdLineParser& parser) override;
bool OnCmdLineParsed(wxCmdLineParser& parser) override;
private:
void ShowCrashReport(const wxString& header, const wxString& text);
};
DECLARE_APP(CrashReportApp);

138
crashreports/crashreporter/warning.xpm

@ -1,138 +0,0 @@
/* XPM */
static const char *warning[] = {
/* columns rows colors chars-per-pixel */
"24 24 108 2 ",
" c None",
". c black",
"X c #010100",
"o c #020200",
"O c #020201",
"+ c #070601",
"@ c #070701",
"# c #0E0C02",
"$ c #151204",
"% c #1C1804",
"& c #1F1A05",
"* c #261F00",
"= c #262000",
"- c #262005",
"; c #292305",
": c #2D2705",
"> c #312A06",
", c #403705",
"< c #413806",
"1 c #8F7300",
"2 c #957700",
"3 c #BA9500",
"4 c #B99600",
"5 c #E3B800",
"6 c #E5BA00",
"7 c #FEBB0B",
"8 c #FFBC08",
"9 c #FFBA0C",
"0 c #FFBF10",
"q c #FFBF11",
"w c #FFC000",
"e c #FFC100",
"r c #FFC202",
"t c #FFC400",
"y c #FFC500",
"u c #FFC700",
"i c #FFC307",
"p c #FFC800",
"a c #FFC900",
"s c #FECA00",
"d c #FFCB00",
"f c #FFCC00",
"g c #FFCD00",
"h c #FFCA04",
"j c #FFCF04",
"k c #FFC20B",
"l c #FFC00D",
"z c #FFC20D",
"x c #FFC00E",
"c c #FFCF0D",
"v c #FED004",
"b c #FFD104",
"n c #FFD00D",
"m c #FFD10D",
"M c #FFD20D",
"N c #FEC116",
"B c #FFCB14",
"V c #FFC61C",
"C c #FFCA1F",
"Z c #FFD215",
"A c #FFD11C",
"S c #FFD11D",
"D c #FFD31D",
"F c #FFD41D",
"G c #E6C327",
"H c #E7C527",
"J c #FFC621",
"K c #FFC525",
"L c #FFC624",
"P c #FFCC21",
"I c #FFCA25",
"U c #FFC927",
"Y c #FFCB26",
"T c #FFCC27",
"R c #FFCE27",
"E c #FFC22B",
"W c #FFC52A",
"Q c #FFC62C",
"! c #F2CE29",
"~ c #F3CF29",
"^ c #FFC828",
"/ c #FFCE28",
"( c #FFCC2A",
") c #FFCC2C",
"_ c #FFD423",
"` c #FFD623",
"' c #FFD226",
"] c #FFD527",
"[ c #F6D129",
"{ c #FFD129",
"} c #FFD229",
"| c #FFD22A",
" . c #FFD32A",
".. c #FDD42A",
"X. c #FCD52B",
"o. c #FFD42A",
"O. c #FFD52A",
"+. c #FFD52B",
"@. c #FFD62A",
"#. c #FFD62B",
"$. c #FFD72B",
"%. c #FFD828",
"&. c #FFD92B",
"*. c #FFDA2B",
"=. c #FFDA2C",
"-. c #FFC633",
";. c #FFC830",
":. c #FFCA31",
/* pixels */
" ",
" -.E ",
" / R ",
" U +.+.) ",
" ;.| +.+.| W ",
" U +.[ [ +.R ",
" ;.+.H X X G | J ",
" R +.! X X ! +.R ",
" L +.+.+.@ @ +.+.+.^ ",
" | +.+.=.# # *.+.+.| ",
" / +.+.+.*.$ $ *.+.+.+.U ",
" L ' ] ] ] *.% % *.] ] ] ' Q ",
" C ` ` ` ` ` - ; ` ` ` ` ` P ",
" J D D D D D D : > F D D D D D V ",
" B Z Z Z Z Z Z < , Z Z Z Z Z Z B ",
" k c n n n n n n n n n n n n n n c x ",
" q h j j j j j j j b b j j j j j j j h x ",
" i f f f f f f f 6 = * 5 f f f f f f f w ",
" 8 p p p p p p p p 3 X X 3 p p p f f p p p q ",
" w p p p p p p p p p 2 1 p p p p p p p p p w ",
"9 u u u u u u u u u u u u u u u u u u u u u u 7 ",
"N u u u u u u u u u u u u u u u u u u u u u u x ",
" ",
" "
};

155
crashreports/internal/unix/CrashReportContext.cpp

@ -1,155 +0,0 @@
/*!********************************************************************
*
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
}

43
crashreports/internal/unix/CrashReportContext.h

@ -1,43 +0,0 @@
/*!********************************************************************
*
Audacity: A Digital Audio Editor
CrashReportContext.h
Vitaly Sverchinsky
**********************************************************************/
#pragma once
#include <string>
#include <map>
//!This object is for internal usage.
/*! Simple POD type, holds user data required to start handler.
* Fields are initialized with Set* methods,
* which may return false if internal buffer isn't large enough
* to store value passed as an argument.
* After initialization call StartHandler providing path to the
* database, where minidumps will be stored.
*/
class CrashReportContext
{
static constexpr size_t MaxBufferLength{ 2048 };
char mSenderPath[MaxBufferLength]{};
char mReportURL[MaxBufferLength]{};
char mParameters[MaxBufferLength]{};
public:
bool SetSenderPathUTF8(const std::string& path);
bool SetReportURL(const std::string& url);
bool SetParameters(const std::map<std::string, std::string>& p);
void StartHandler(const std::string& databasePath);
private:
//helper function which need access to a private data, but should not be exposed to a public class interface
friend bool SendReport(CrashReportContext* ctx, const char* minidumpPath);
};

165
crashreports/internal/win32/CrashReportContext.cpp

@ -1,165 +0,0 @@
/*!********************************************************************
*
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);
}

47
crashreports/internal/win32/CrashReportContext.h

@ -1,47 +0,0 @@
/*!********************************************************************
*
Audacity: A Digital Audio Editor
CrashReportContext.h
Vitaly Sverchinsky
**********************************************************************/
#pragma once
#include <string>
#include <map>
//!This object is for internal usage.
/*! Simple POD type, holds user data required to start handler.
* Fields are initialized with Set* methods,
* which may return false if internal buffer isn't large enough
* to store value passed as an argument.
* After initialization call StartHandler providing path to the
* database, where minidumps will be stored.
*/
class CrashReportContext final
{
static constexpr size_t MaxBufferLength{ 2048 };
static constexpr size_t MaxCommandLength{ 8192 };
wchar_t mSenderPath[MaxBufferLength]{};
wchar_t mReportURL[MaxBufferLength]{};
wchar_t mParameters[MaxBufferLength]{};
//this is a buffer where the command will be built at runtime
wchar_t mCommand[MaxCommandLength]{};
public:
bool SetSenderPathUTF8(const std::string& path);
bool SetReportURL(const std::string& path);
bool SetParameters(const std::map<std::string, std::string>& p);
void StartHandler(const std::string& databasePath);
private:
//helper functions which need access to a private data, but should not be exposed to a public class interface
friend bool MakeCommand(CrashReportContext* ctx, const wchar_t* path, const wchar_t* id);
friend bool SendReport(CrashReportContext* ctx, const wchar_t* path, const wchar_t* id);
};

35
src/AudacityApp.cpp

@ -77,7 +77,6 @@ It handles initialization and termination by subclassing wxApp.
#include "AudioIO.h"
#include "Benchmark.h"
#include "Clipboard.h"
#include "CrashReport.h" // for HAS_CRASH_REPORT
#include "commands/CommandHandler.h"
#include "commands/AppCommandEvent.h"
#include "widgets/ASlider.h"
@ -123,10 +122,6 @@ It handles initialization and termination by subclassing wxApp.
#include "import/Import.h"
#if defined(USE_BREAKPAD)
#include "BreakpadConfigurer.h"
#endif
#ifdef EXPERIMENTAL_SCOREALIGN
#include "effects/ScoreAlignDialog.h"
#endif
@ -382,29 +377,6 @@ void PopulatePreferences()
gPrefs->Flush();
}
#if defined(USE_BREAKPAD)
void InitBreakpad()
{
wxFileName databasePath;
databasePath.SetPath(wxStandardPaths::Get().GetUserLocalDataDir());
databasePath.AppendDir("crashreports");
databasePath.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);
if(databasePath.DirExists())
{
BreakpadConfigurer configurer;
configurer.SetDatabasePathUTF8(databasePath.GetPath().ToUTF8().data())
.SetSenderPathUTF8(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath().ToUTF8().data())
#if defined(CRASH_REPORT_URL)
.SetReportURL(CRASH_REPORT_URL)
#endif
.SetParameters({
{ "version", wxString(AUDACITY_VERSION_STRING).ToUTF8().data() }
})
.Start();
}
}
#endif
}
static bool gInited = false;
@ -952,10 +924,6 @@ wxLanguageInfo userLangs[] =
void AudacityApp::OnFatalException()
{
#if defined(HAS_CRASH_REPORT)
CrashReport::Generate(wxDebugReport::Context_Exception);
#endif
exit(-1);
}
@ -1020,15 +988,12 @@ bool AudacityApp::OnExceptionInMainLoop()
AudacityApp::AudacityApp()
{
#if defined(USE_BREAKPAD)
InitBreakpad();
// Do not capture crashes in debug builds
#elif !defined(_DEBUG)
#if defined(HAS_CRASH_REPORT)
#if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION
wxHandleFatalExceptions();
#endif
#endif
#endif
}
AudacityApp::~AudacityApp()

7
src/CMakeLists.txt

@ -120,8 +120,6 @@ list( APPEND SOURCES
Clipboard.h
CommonCommandFlags.cpp
CommonCommandFlags.h
CrashReport.cpp
CrashReport.h
DarkThemeAsCeeCode.h
DBConnection.cpp
DBConnection.h
@ -1071,7 +1069,6 @@ list( APPEND LIBRARIES
libsoxr
portaudio-v19
sqlite
$<$<BOOL:${${_OPT}has_crashreports}>:crashreports>
$<$<BOOL:${USE_FFMPEG}>:ffmpeg>
$<$<BOOL:${USE_LIBID3TAG}>:libid3tag::libid3tag>
$<$<BOOL:${USE_LIBFLAC}>:libflac>
@ -1294,10 +1291,6 @@ else()
)
endif()
if(CRASH_REPORT_URL)
list(APPEND DEFINES PRIVATE -DCRASH_REPORT_URL="${CRASH_REPORT_URL}")
endif()
set_target_property_all( ${TARGET} RUNTIME_OUTPUT_NAME ${AUDACITY_NAME} )
organize_source( "${TARGET_ROOT}/.." "include" "${HEADERS}" )

119
src/CrashReport.cpp

@ -1,119 +0,0 @@
/**********************************************************************
Audacity: A Digital Audio Editor
CrashReport.cpp
*//*******************************************************************/
#include "CrashReport.h"
#if defined(HAS_CRASH_REPORT)
#include <atomic>
#include <thread>
#include <wx/progdlg.h>
#if defined(__WXMSW__)
#include <wx/evtloop.h>
#endif
#include "wxFileNameWrapper.h"
#include "AudacityLogger.h"
#include "AudioIOBase.h"
#include "FileNames.h"
#include "Internat.h"
#include "Languages.h"
#include "Project.h"
#include "ProjectFileIO.h"
#include "prefs/GUIPrefs.h"
#include "widgets/ErrorDialog.h"
namespace CrashReport {
void Generate(wxDebugReport::Context ctx)
{
wxDebugReportCompress rpt;
// Bug 1927: Seems there problems with wxStackWalker, so don't even include
// the stack trace or memory dump. The former is pretty much useless in Release
// builds anyway and none of use have the skill/time/desire to fiddle with the
// latter.
// rpt.AddAll(ctx);
{
// Provides a progress dialog with indeterminate mode
wxGenericProgressDialog pd(XO("Audacity Support Data").Translation(),
XO("This may take several seconds").Translation(),
300000, // range
nullptr, // parent
wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH);
std::atomic_bool done = {false};
auto thread = std::thread([&]
{
wxFileNameWrapper fn{ FileNames::DataDir(), wxT("audacity.cfg") };
rpt.AddFile(fn.GetFullPath(), _TS("Audacity Configuration"));
rpt.AddFile(FileNames::PluginRegistry(), wxT("Plugin Registry"));
rpt.AddFile(FileNames::PluginSettings(), wxT("Plugin Settings"));
if (ctx == wxDebugReport::Context_Current)
{
auto saveLang = Languages::GetLangShort();
GUIPrefs::SetLang( wxT("en") );
auto cleanup = finally( [&]{ GUIPrefs::SetLang( saveLang ); } );
auto gAudioIO = AudioIOBase::Get();
rpt.AddText(wxT("audiodev.txt"), gAudioIO->GetDeviceInfo(), wxT("Audio Device Info"));
#ifdef EXPERIMENTAL_MIDI_OUT
rpt.AddText(wxT("mididev.txt"), gAudioIO->GetMidiDeviceInfo(), wxT("MIDI Device Info"));