Crashreporting
This commit is contained in:
parent
5c05f6b421
commit
e8b186a9b4
|
@ -69,6 +69,7 @@ jobs:
|
||||||
SENTRY_DSN_KEY: ${{ secrets.SENTRY_DSN_KEY }}
|
SENTRY_DSN_KEY: ${{ secrets.SENTRY_DSN_KEY }}
|
||||||
SENTRY_HOST: ${{ secrets.SENTRY_HOST }}
|
SENTRY_HOST: ${{ secrets.SENTRY_HOST }}
|
||||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||||
|
CRASH_REPORT_URL: ${{ secrets.CRASH_REPORT_URL }}
|
||||||
run: |
|
run: |
|
||||||
exec bash "scripts/ci/configure.sh"
|
exec bash "scripts/ci/configure.sh"
|
||||||
|
|
||||||
|
@ -76,6 +77,20 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
exec bash "scripts/ci/build.sh"
|
exec bash "scripts/ci/build.sh"
|
||||||
|
|
||||||
|
- name: Upload debug symbols
|
||||||
|
if: startsWith(github.ref, 'refs/tags/release-')
|
||||||
|
env:
|
||||||
|
SENTRY_DSN_KEY: ${{ secrets.SENTRY_DSN_KEY }}
|
||||||
|
SENTRY_HOST: ${{ secrets.SENTRY_HOST }}
|
||||||
|
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||||
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
|
SENTRY_ORG_SLUG: ${{ secrets.SENTRY_ORG_SLUG }}
|
||||||
|
SENTRY_PROJECT_SLUG: ${{ secrets.SENTRY_PROJECT_SLUG }}
|
||||||
|
#this is required to run sentry's get-cli script on platforms where 'sudo' is not available
|
||||||
|
INSTALL_DIR: ${{ github.workspace }}
|
||||||
|
run: |
|
||||||
|
exec bash "scripts/ci/upload_debug_symbols.sh"
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
exec bash "scripts/ci/install.sh"
|
exec bash "scripts/ci/install.sh"
|
||||||
|
|
|
@ -171,6 +171,7 @@ include( TestBigEndian )
|
||||||
set_from_env(SENTRY_DSN_KEY)
|
set_from_env(SENTRY_DSN_KEY)
|
||||||
set_from_env(SENTRY_HOST)
|
set_from_env(SENTRY_HOST)
|
||||||
set_from_env(SENTRY_PROJECT)
|
set_from_env(SENTRY_PROJECT)
|
||||||
|
set_from_env(CRASH_REPORT_URL)
|
||||||
|
|
||||||
cmake_dependent_option(
|
cmake_dependent_option(
|
||||||
${_OPT}has_sentry_reporting
|
${_OPT}has_sentry_reporting
|
||||||
|
@ -180,6 +181,14 @@ cmake_dependent_option(
|
||||||
Off
|
Off
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cmake_dependent_option(
|
||||||
|
${_OPT}has_crashreports
|
||||||
|
"Enables crash reporting for Audacity"
|
||||||
|
On
|
||||||
|
"${_OPT}has_networking;DEFINED CRASH_REPORT_URL"
|
||||||
|
Off
|
||||||
|
)
|
||||||
|
|
||||||
# Determine 32-bit or 64-bit target
|
# Determine 32-bit or 64-bit target
|
||||||
if( CMAKE_C_COMPILER_ID MATCHES "MSVC" AND CMAKE_VS_PLATFORM_NAME MATCHES "Win64|x64" )
|
if( CMAKE_C_COMPILER_ID MATCHES "MSVC" AND CMAKE_VS_PLATFORM_NAME MATCHES "Win64|x64" )
|
||||||
set( IS_64BIT ON )
|
set( IS_64BIT ON )
|
||||||
|
@ -217,6 +226,11 @@ elseif( CMAKE_SYSTEM_NAME MATCHES "Darwin" )
|
||||||
endif()
|
endif()
|
||||||
message( STATUS " MacOS SDK: ${CMAKE_OSX_SYSROOT}" )
|
message( STATUS " MacOS SDK: ${CMAKE_OSX_SYSROOT}" )
|
||||||
message( STATUS )
|
message( STATUS )
|
||||||
|
|
||||||
|
if(${_OPT}has_crashreports)
|
||||||
|
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym")
|
||||||
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Try to get the current commit information
|
# Try to get the current commit information
|
||||||
|
@ -485,6 +499,9 @@ add_subdirectory( "cmake-proxies" )
|
||||||
resolve_conan_dependencies()
|
resolve_conan_dependencies()
|
||||||
|
|
||||||
add_subdirectory( "help" )
|
add_subdirectory( "help" )
|
||||||
|
if(${_OPT}has_crashreports)
|
||||||
|
add_subdirectory( "crashreports" )
|
||||||
|
endif()
|
||||||
add_subdirectory( "images" )
|
add_subdirectory( "images" )
|
||||||
add_subdirectory( "libraries" )
|
add_subdirectory( "libraries" )
|
||||||
add_subdirectory( "locale" )
|
add_subdirectory( "locale" )
|
||||||
|
|
|
@ -18,6 +18,10 @@ add_conan_lib(
|
||||||
expat:shared=True
|
expat:shared=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(${_OPT}has_crashreports)
|
||||||
|
add_conan_lib(breakpad breakpad/0.1 REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
set( wx_zlib "zlib" )
|
set( wx_zlib "zlib" )
|
||||||
|
|
||||||
set( wx_png "libpng" )
|
set( wx_png "libpng" )
|
||||||
|
|
|
@ -262,7 +262,7 @@ function( audacity_append_common_compiler_options var use_pch )
|
||||||
|
|
||||||
# Define/undefine _DEBUG
|
# Define/undefine _DEBUG
|
||||||
# Yes, -U to /U too as needed for Windows:
|
# Yes, -U to /U too as needed for Windows:
|
||||||
$<IF:$<CONFIG:Release>,-U_DEBUG,-D_DEBUG=1>
|
$<IF:$<CONFIG:Debug>,-D_DEBUG=1,-U_DEBUG>
|
||||||
)
|
)
|
||||||
# Definitions controlled by the AUDACITY_BUILD_LEVEL switch
|
# Definitions controlled by the AUDACITY_BUILD_LEVEL switch
|
||||||
if( AUDACITY_BUILD_LEVEL EQUAL 0 )
|
if( AUDACITY_BUILD_LEVEL EQUAL 0 )
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*!********************************************************************
|
||||||
|
*
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*!********************************************************************
|
||||||
|
*
|
||||||
|
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();
|
||||||
|
};
|
|
@ -0,0 +1,53 @@
|
||||||
|
# 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}" )
|
|
@ -0,0 +1,41 @@
|
||||||
|
#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}" )
|
|
@ -0,0 +1,464 @@
|
||||||
|
#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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*!********************************************************************
|
||||||
|
*
|
||||||
|
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);
|
|
@ -0,0 +1,138 @@
|
||||||
|
/* 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 ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
};
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*!********************************************************************
|
||||||
|
*
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*!********************************************************************
|
||||||
|
*
|
||||||
|
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);
|
||||||
|
};
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*!********************************************************************
|
||||||
|
*
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*!********************************************************************
|
||||||
|
*
|
||||||
|
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);
|
||||||
|
};
|
|
@ -96,21 +96,6 @@ Some example strings are also given first, to document the syntax.
|
||||||
// //////////////////////////////////////////////// End examples
|
// //////////////////////////////////////////////// End examples
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Crash & error report window
|
|
||||||
|
|
||||||
XO("Problem Report for Audacity"),
|
|
||||||
XO("Problem details"),
|
|
||||||
// i18n-hint C++ programming exception
|
|
||||||
XO("Exception code 0x%x"),
|
|
||||||
// i18n-hint C++ programming exception
|
|
||||||
XO("Unknown exception"),
|
|
||||||
// i18n-hint C++ programming assertion
|
|
||||||
XO("Unknown assertion"),
|
|
||||||
XO("Unknown error"),
|
|
||||||
XO("Failed to send crash report"),
|
|
||||||
|
|
||||||
|
|
||||||
// Update version dialog
|
// Update version dialog
|
||||||
XC("Update Audacity", "update dialog"),
|
XC("Update Audacity", "update dialog"),
|
||||||
XC("&Skip", "update dialog"),
|
XC("&Skip", "update dialog"),
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Run this script with locale as the current directory
|
# Run this script with locale as the current directory
|
||||||
set -o errexit
|
set -o errexit
|
||||||
echo ";; Recreating audacity.pot using .h, .cpp and .mm files"
|
echo ";; Recreating audacity.pot using .h, .cpp and .mm files"
|
||||||
for path in ../modules/mod-* ../libraries/lib-* ../include ../src ; do
|
for path in ../modules/mod-* ../libraries/lib-* ../include ../src ../crashreports ; do
|
||||||
find $path -name \*.h -o -name \*.cpp -o -name \*.mm
|
find $path -name \*.h -o -name \*.cpp -o -name \*.mm
|
||||||
done | LANG=c sort | \
|
done | LANG=c sort | \
|
||||||
sed -E 's/\.\.\///g' |\
|
sed -E 's/\.\.\///g' |\
|
||||||
|
|
|
@ -20,3 +20,21 @@ fi
|
||||||
|
|
||||||
# Build Audacity
|
# Build Audacity
|
||||||
cmake --build build -j "${cpus}" --config "${AUDACITY_BUILD_TYPE}"
|
cmake --build build -j "${cpus}" --config "${AUDACITY_BUILD_TYPE}"
|
||||||
|
|
||||||
|
BIN_OUTPUT_DIR=build/bin/${AUDACITY_BUILD_TYPE}
|
||||||
|
SYMBOLS_OUTPUT_DIR=debug
|
||||||
|
|
||||||
|
mkdir ${SYMBOLS_OUTPUT_DIR}
|
||||||
|
|
||||||
|
if [[ "${OSTYPE}" == msys* ]]; then # Windows
|
||||||
|
# copy PDBs to debug folder...
|
||||||
|
find ${BIN_OUTPUT_DIR} -name '*.pdb' | xargs -I % cp % ${SYMBOLS_OUTPUT_DIR}
|
||||||
|
# and remove debug symbol files from the file tree before archieving
|
||||||
|
find ${BIN_OUTPUT_DIR} -name '*.iobj' -o -name '*.ipdb' -o -name '*.pdb' -o -name '*.ilk' | xargs rm -f
|
||||||
|
elif [[ "${OSTYPE}" == darwin* ]]; then # macOS
|
||||||
|
find ${BIN_OUTPUT_DIR} -name '*.dSYM' | xargs -J % mv % ${SYMBOLS_OUTPUT_DIR}
|
||||||
|
else # Linux & others
|
||||||
|
chmod +x scripts/ci/linux/split_debug_symbols.sh
|
||||||
|
find ${BIN_OUTPUT_DIR} -type f -executable -o -name '*.so' | xargs -n 1 scripts/ci/linux/split_debug_symbols.sh
|
||||||
|
find ${BIN_OUTPUT_DIR} -name '*.debug' | xargs -I % mv % ${SYMBOLS_OUTPUT_DIR}
|
||||||
|
fi
|
||||||
|
|
|
@ -17,6 +17,10 @@ cmake_args=(
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ "${AUDACITY_CMAKE_GENERATOR}" == "Visual Studio"* ]]; then
|
if [[ "${AUDACITY_CMAKE_GENERATOR}" == "Visual Studio"* ]]; then
|
||||||
|
cmake_args+=(
|
||||||
|
# skip unneeded configurations
|
||||||
|
-D CMAKE_CONFIGURATION_TYPES="${AUDACITY_BUILD_TYPE}"
|
||||||
|
)
|
||||||
case "${AUDACITY_ARCH_LABEL}" in
|
case "${AUDACITY_ARCH_LABEL}" in
|
||||||
32bit) cmake_args+=( -A Win32 ) ;;
|
32bit) cmake_args+=( -A Win32 ) ;;
|
||||||
64bit) cmake_args+=( -A x64 ) ;;
|
64bit) cmake_args+=( -A x64 ) ;;
|
||||||
|
@ -24,6 +28,8 @@ if [[ "${AUDACITY_CMAKE_GENERATOR}" == "Visual Studio"* ]]; then
|
||||||
esac
|
esac
|
||||||
elif [[ "${AUDACITY_CMAKE_GENERATOR}" == Xcode* ]]; then
|
elif [[ "${AUDACITY_CMAKE_GENERATOR}" == Xcode* ]]; then
|
||||||
cmake_args+=(
|
cmake_args+=(
|
||||||
|
# skip unneeded configurations
|
||||||
|
-D CMAKE_CONFIGURATION_TYPES="${AUDACITY_BUILD_TYPE}"
|
||||||
-T buildsystem=1
|
-T buildsystem=1
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -21,5 +21,5 @@ gh_export CONAN_USER_HOME_SHORT="${repository_root}/conan-home/short"
|
||||||
gh_export GIT_HASH="$(git show -s --format='%H')"
|
gh_export GIT_HASH="$(git show -s --format='%H')"
|
||||||
gh_export GIT_HASH_SHORT="$(git show -s --format='%h')"
|
gh_export GIT_HASH_SHORT="$(git show -s --format='%h')"
|
||||||
|
|
||||||
gh_export AUDACITY_BUILD_TYPE="Release"
|
gh_export AUDACITY_BUILD_TYPE="RelWithDebInfo"
|
||||||
gh_export AUDACITY_INSTALL_PREFIX="${repository_root}/build/install"
|
gh_export AUDACITY_INSTALL_PREFIX="${repository_root}/build/install"
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -x
|
||||||
|
objcopy --only-keep-debug --compress-debug-section=zlib "${1}" "${1}.debug"
|
||||||
|
if [ -f "${1}.debug" ]; then
|
||||||
|
objcopy --strip-debug --strip-unneeded "${1}"
|
||||||
|
objcopy --add-gnu-debuglink="${1}.debug" "${1}"
|
||||||
|
fi
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
((${BASH_VERSION%%.*} >= 4)) || { echo >&2 "$0: Error: Please upgrade Bash."; exit 1; }
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
# download sentry-cli
|
||||||
|
# TODO: currently this script downloads binaries and install them
|
||||||
|
# each time job is started, workarounds?
|
||||||
|
curl -sL https://sentry.io/get-cli/ | bash
|
||||||
|
|
||||||
|
#Where debug symbols will go
|
||||||
|
UPLOAD_URL=https://${SENTRY_HOST}/api/${SENTRY_PROJECT}/minidump/?sentry_key=${SENTRY_DSN_KEY}
|
||||||
|
|
||||||
|
SYMBOLS=$(find debug | xargs)
|
||||||
|
|
||||||
|
${INSTALL_DIR}/sentry-cli --auth-token ${SENTRY_AUTH_TOKEN} --url ${UPLOAD_URL} \
|
||||||
|
--org ${SENTRY_ORG_SLUG}} \
|
||||||
|
--project ${SENTRY_PROJECT_SLUG} ${SYMBOLS}
|
|
@ -127,6 +127,10 @@ It handles initialization and termination by subclassing wxApp.
|
||||||
|
|
||||||
#include "import/Import.h"
|
#include "import/Import.h"
|
||||||
|
|
||||||
|
#if defined(USE_BREAKPAD)
|
||||||
|
#include "BreakpadConfigurer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_SCOREALIGN
|
#ifdef EXPERIMENTAL_SCOREALIGN
|
||||||
#include "effects/ScoreAlignDialog.h"
|
#include "effects/ScoreAlignDialog.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -382,6 +386,29 @@ void PopulatePreferences()
|
||||||
gPrefs->Flush();
|
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;
|
static bool gInited = false;
|
||||||
|
@ -996,8 +1023,10 @@ bool AudacityApp::OnExceptionInMainLoop()
|
||||||
|
|
||||||
AudacityApp::AudacityApp()
|
AudacityApp::AudacityApp()
|
||||||
{
|
{
|
||||||
|
#if defined(USE_BREAKPAD)
|
||||||
|
InitBreakpad();
|
||||||
// Do not capture crashes in debug builds
|
// Do not capture crashes in debug builds
|
||||||
#if !defined(_DEBUG)
|
#elif !defined(_DEBUG)
|
||||||
#if defined(HAS_CRASH_REPORT)
|
#if defined(HAS_CRASH_REPORT)
|
||||||
#if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION
|
#if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION
|
||||||
wxHandleFatalExceptions();
|
wxHandleFatalExceptions();
|
||||||
|
|
|
@ -1073,6 +1073,7 @@ list( APPEND LIBRARIES
|
||||||
libsoxr
|
libsoxr
|
||||||
portaudio-v19
|
portaudio-v19
|
||||||
sqlite
|
sqlite
|
||||||
|
$<$<BOOL:${${_OPT}has_crashreports}>:crashreports>
|
||||||
$<$<BOOL:${USE_FFMPEG}>:ffmpeg>
|
$<$<BOOL:${USE_FFMPEG}>:ffmpeg>
|
||||||
$<$<BOOL:${USE_LIBID3TAG}>:libid3tag::libid3tag>
|
$<$<BOOL:${USE_LIBID3TAG}>:libid3tag::libid3tag>
|
||||||
$<$<BOOL:${USE_LIBFLAC}>:libflac>
|
$<$<BOOL:${USE_LIBFLAC}>:libflac>
|
||||||
|
@ -1292,6 +1293,10 @@ else()
|
||||||
)
|
)
|
||||||
endif()
|
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} )
|
set_target_property_all( ${TARGET} RUNTIME_OUTPUT_NAME ${AUDACITY_NAME} )
|
||||||
|
|
||||||
organize_source( "${TARGET_ROOT}/.." "include" "${HEADERS}" )
|
organize_source( "${TARGET_ROOT}/.." "include" "${HEADERS}" )
|
||||||
|
|
Loading…
Reference in New Issue