From 09b8fe5d9af0f9c5b17fef7bca13e99c8242105b Mon Sep 17 00:00:00 2001 From: alves Date: Thu, 4 Dec 2025 17:39:06 +0800 Subject: [PATCH] feature add api for function report log to server --- CMakeLists.txt | 1 + src/CMakeLists.txt | 14 +- src/Snapmaker_Orca_app_msvc.cpp | 108 +-------------- src/libslic3r/CMakeLists.txt | 9 +- src/sentry_wrapper/SentryWrapper.cpp | 194 +++++++++++++++++++++++++++ src/sentry_wrapper/SentryWrapper.hpp | 30 +++++ src/slic3r/CMakeLists.txt | 6 + src/slic3r/GUI/MainFrame.cpp | 1 + 8 files changed, 257 insertions(+), 106 deletions(-) create mode 100644 src/sentry_wrapper/SentryWrapper.cpp create mode 100644 src/sentry_wrapper/SentryWrapper.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d2d90d2521..ceb5ba3ab6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0) option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) +option(SLIC3R_SENTRY "Enable Sentry crash reporting" 1) # If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable. CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7525f6cf45..a1e137b4e5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -115,7 +115,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/msw/Snapmaker_Orca configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/msw/Snapmaker_Orca.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/Snapmaker_Orca.manifest @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY) if (WIN32) - add_library(Snapmaker_Orca SHARED Snapmaker_Orca.cpp Snapmaker_Orca.hpp dev-utils/BaseException.cpp dev-utils/BaseException.h dev-utils/StackWalker.cpp dev-utils/StackWalker.h) + add_library(Snapmaker_Orca SHARED Snapmaker_Orca.cpp Snapmaker_Orca.hpp dev-utils/BaseException.cpp dev-utils/BaseException.h dev-utils/StackWalker.cpp dev-utils/StackWalker.h sentry_wrapper/SentryWrapper.hpp sentry_wrapper/SentryWrapper.cpp) else () add_executable(Snapmaker_Orca Snapmaker_Orca.cpp Snapmaker_Orca.hpp) endif () @@ -168,6 +168,12 @@ target_link_libraries(Snapmaker_Orca libslic3r_gui) #endif () endif () +# Sentry crash reporting integration +if (SLIC3R_SENTRY) + target_compile_definitions(Snapmaker_Orca PUBLIC SLIC3R_SENTRY) + target_link_libraries(Snapmaker_Orca sentry::sentry) +endif() + # On Windows, a shim application is required to produce a console / non console version of the Slic3r application. # Also the shim may load the Mesa software OpenGL renderer if the default renderer does not support OpenGL 2.0 and higher. if (WIN32) @@ -175,7 +181,7 @@ if (WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") endif() - add_executable(Snapmaker_Orca_app_gui WIN32 Snapmaker_Orca_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/Snapmaker_Orca.rc) + add_executable(Snapmaker_Orca_app_gui WIN32 Snapmaker_Orca_app_msvc.cpp sentry_wrapper/SentryWrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/Snapmaker_Orca.rc) # Generate debug symbols even in release mode. if(MSVC) target_link_options(Snapmaker_Orca_app_gui PUBLIC "$<$:/DEBUG>") @@ -183,7 +189,11 @@ if (WIN32) target_compile_definitions(Snapmaker_Orca_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE) add_dependencies(Snapmaker_Orca_app_gui Snapmaker_Orca) set_target_properties(Snapmaker_Orca_app_gui PROPERTIES OUTPUT_NAME "snapmaker-orca") + target_include_directories(Snapmaker_Orca_app_gui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(Snapmaker_Orca_app_gui PRIVATE boost_headeronly sentry::sentry) + if (SLIC3R_SENTRY) + target_compile_definitions(Snapmaker_Orca_app_gui PRIVATE SLIC3R_SENTRY) + endif() endif () # Link the resources dir to where Slic3r GUI expects it diff --git a/src/Snapmaker_Orca_app_msvc.cpp b/src/Snapmaker_Orca_app_msvc.cpp index 9750c8782c..fe77bf37ea 100644 --- a/src/Snapmaker_Orca_app_msvc.cpp +++ b/src/Snapmaker_Orca_app_msvc.cpp @@ -7,7 +7,7 @@ #include #include #include -#include "sentry.h" +#include "sentry_wrapper/SentryWrapper.hpp" #ifdef SLIC3R_GUI extern "C" { @@ -33,14 +33,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0; #include #include -static sentry_value_t on_crash_callback(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure) -{ - (void) uctx; - (void) closure; - - // tell the backend to retain the event - return event; -} +using namespace Slic3r; #ifdef SLIC3R_GUI class OpenGLVersionCheck @@ -218,91 +211,6 @@ typedef int(__stdcall* Slic3rMainFunc)(int argc, wchar_t** argv); Slic3rMainFunc Snapmaker_Orca_main = nullptr; } -void initSentry() -{ - sentry_options_t* options = sentry_options_new(); - { -#ifdef WIN32 - std::string dsn = std::string("https://c74b617c2aedc291444d3a238d23e780@o4508125599563776.ingest.us.sentry.io/4510425163956224"); - - sentry_options_set_dsn(options, dsn.c_str()); - - wchar_t exeDir[MAX_PATH]; - ::GetModuleFileNameW(nullptr, exeDir, MAX_PATH); - std::wstring wsExeDir(exeDir); - int nPos = wsExeDir.find_last_of('\\'); - std::wstring wsDmpDir = wsExeDir.substr(0, nPos + 1); - - std::wstring handlerDir = wsDmpDir + L"crashpad_handler.exe"; - wsDmpDir += L"dump"; - - auto wstringTostring = [](std::wstring wTmpStr) -> std::string { - std::string resStr = std::string(); - int len = WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, nullptr, 0, nullptr, nullptr); - - if (len <= 0) - return std::string(); - std::string desStr(len, 0); - - WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, &desStr[0], len, nullptr, nullptr); - - resStr = desStr; - - return resStr; - }; - - std::string desDir = wstringTostring(handlerDir); - if (!desDir.empty()) - sentry_options_set_handler_path(options, desDir.c_str()); - desDir = wstringTostring(wsDmpDir); - desDir = wstringTostring(wsDmpDir); - wchar_t appDataPath[MAX_PATH] = {0}; - auto hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataPath); - char* path = new char[MAX_PATH]; - size_t pathLength; - wcstombs_s(&pathLength, path, MAX_PATH, appDataPath, MAX_PATH); - std::string filePath = path; - std::string appName = "\\" + std::string("Snapmaker_Orca\\"); - filePath = filePath + appName; - - if (!filePath.empty()) - sentry_options_set_database_path(options, filePath.c_str()); -#endif - std::string softVersion = "snapmaker_orca_2.2.0_beta2"; - // Snapmaker_VERSION - sentry_options_set_release(options, softVersion.c_str()); - -#if defined(_DEBUG) || !defined(NDEBUG) - sentry_options_set_debug(options, 1); -#else - sentry_options_set_debug(options, 0); -#endif - // release version environment(Testing/production/development/Staging) - sentry_options_set_environment(options, "develop"); - sentry_options_set_auto_session_tracking(options, false); - sentry_options_set_symbolize_stacktraces(options, true); - sentry_options_set_on_crash(options, on_crash_callback, NULL); - // Enable before_send hook for filtering sensitive data - sentry_options_set_before_send(options, NULL, NULL); - - // Ensure all events and crashes are captured (sample rate 100%) - sentry_options_set_sample_rate(options, 1.0); // Capture 100% of events - sentry_options_set_traces_sample_rate(options, 1.0); // Capture 100% of traces - - sentry_init(options); - sentry_start_session(); - - DWORD processID = GetCurrentProcessId(); - sentry_set_tag("PID", std::to_string(processID).c_str()); - - auto pcName = boost::asio::ip::host_name(); - // auto macAddress = getMacAddress(); - - sentry_set_tag("computer_name", pcName.c_str()); - // sentry_set_tag("mac_address", macAddress.c_str()); - } -} - extern "C" { #ifdef SLIC3R_WRAPPER_NOCONSOLE int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, PWSTR /* lpCmdLine */, int /* nCmdShow */) @@ -318,16 +226,10 @@ int wmain(int argc, wchar_t** argv) // the application will be killed even if "Ignore" button is pressed. _set_error_mode(_OUT_TO_MSGBOX); -#if defined(_DEBUG) || !defined(NDEBUG) - SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) ExceptionCrashHandler); -#else - // Initialize Sentry for crash reporting in Release builds initSentry(); -#endif std::vector argv_extended; argv_extended.emplace_back(argv[0]); - #ifdef SLIC3R_WRAPPER_GCODEVIEWER wchar_t gcodeviewer_param[] = L"--gcodeviewer"; argv_extended.emplace_back(gcodeviewer_param); @@ -390,7 +292,7 @@ int wmain(int argc, wchar_t** argv) HINSTANCE hInstance_Slic3r = LoadLibraryExW(path_to_slic3r, nullptr, 0); if (hInstance_Slic3r == nullptr) { printf("Snapmaker_Orca.dll was not loaded, error=%d\n", GetLastError()); - sentry_close(); + exitSentry(); return -1; } @@ -406,13 +308,13 @@ int wmain(int argc, wchar_t** argv) ); if (Snapmaker_Orca_main == nullptr) { printf("could not locate the function Snapmaker_Orca_main in Snapmaker_Orca.dll\n"); - sentry_close(); + exitSentry(); return -1; } // argc minus the trailing nullptr of the argv auto res = Snapmaker_Orca_main((int) argv_extended.size() - 1, argv_extended.data()); - sentry_close(); + exitSentry(); return res; } } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index d85c65fd51..7ddad94f1d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -511,7 +511,8 @@ endif () encoding_check(libslic3r) target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0) -target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +# Make source directory PUBLIC so other modules (like libslic3r_gui) can include headers like SentryWrapper.hpp +target_include_directories(libslic3r PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(libslic3r SYSTEM PUBLIC ${EXPAT_INCLUDE_DIRS}) # Find the OCCT and related libraries @@ -610,3 +611,9 @@ endif() if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) endif () + +# Sentry crash reporting integration +if (SLIC3R_SENTRY) + target_compile_definitions(libslic3r PUBLIC SLIC3R_SENTRY) + target_link_libraries(libslic3r PUBLIC sentry::sentry) +endif() diff --git a/src/sentry_wrapper/SentryWrapper.cpp b/src/sentry_wrapper/SentryWrapper.cpp new file mode 100644 index 0000000000..1103fbb420 --- /dev/null +++ b/src/sentry_wrapper/SentryWrapper.cpp @@ -0,0 +1,194 @@ +/** + * @file SentryWrapper.cpp + * @brief Sentry crash reporting wrapper implementation for cross-platform support. + * + * This implementation provides a unified API for Sentry integration. + * When SLIC3R_SENTRY is not defined, all functions become no-ops. + */ + +#include "SentryWrapper.hpp" + +#ifdef SLIC3R_SENTRY +#include "sentry.h" + +#ifdef _WIN32 +#include +#include +#endif + +#endif + +#include + +namespace Slic3r { + +#ifdef SLIC3R_SENTRY + +static sentry_value_t on_crash_callback(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure) +{ + (void) uctx; + (void) closure; + + // tell the backend to retain the event + return event; +} + +void initSentryEx() +{ + sentry_options_t* options = sentry_options_new(); + { +#ifdef WIN32 + std::string dsn = std::string("https://c74b617c2aedc291444d3a238d23e780@o4508125599563776.ingest.us.sentry.io/4510425163956224"); + + sentry_options_set_dsn(options, dsn.c_str()); + + wchar_t exeDir[MAX_PATH]; + ::GetModuleFileNameW(nullptr, exeDir, MAX_PATH); + std::wstring wsExeDir(exeDir); + int nPos = wsExeDir.find_last_of('\\'); + std::wstring wsDmpDir = wsExeDir.substr(0, nPos + 1); + + std::wstring handlerDir = wsDmpDir + L"crashpad_handler.exe"; + wsDmpDir += L"dump"; + + auto wstringTostring = [](std::wstring wTmpStr) -> std::string { + std::string resStr = std::string(); + int len = WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, nullptr, 0, nullptr, nullptr); + + if (len <= 0) + return std::string(); + std::string desStr(len, 0); + + WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, &desStr[0], len, nullptr, nullptr); + + resStr = desStr; + + return resStr; + }; + + std::string desDir = wstringTostring(handlerDir); + if (!desDir.empty()) + sentry_options_set_handler_path(options, desDir.c_str()); + desDir = wstringTostring(wsDmpDir); + desDir = wstringTostring(wsDmpDir); + wchar_t appDataPath[MAX_PATH] = {0}; + auto hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appDataPath); + char* path = new char[MAX_PATH]; + size_t pathLength; + wcstombs_s(&pathLength, path, MAX_PATH, appDataPath, MAX_PATH); + std::string filePath = path; + std::string appName = "\\" + std::string("Snapmaker_Orca\\"); + filePath = filePath + appName; + + if (!filePath.empty()) + sentry_options_set_database_path(options, filePath.c_str()); +#endif + std::string softVersion = "snapmaker_orca_2.2.0_beta2"; + // Snapmaker_VERSION + sentry_options_set_release(options, softVersion.c_str()); + +#if defined(_DEBUG) || !defined(NDEBUG) + sentry_options_set_debug(options, 1); +#else + sentry_options_set_debug(options, 0); +#endif + // release version environment(Testing/production/development/Staging) + sentry_options_set_environment(options, "develop"); + sentry_options_set_auto_session_tracking(options, false); + sentry_options_set_symbolize_stacktraces(options, true); + sentry_options_set_on_crash(options, on_crash_callback, NULL); + // Enable before_send hook for filtering sensitive data + sentry_options_set_before_send(options, NULL, NULL); + + // Ensure all events and crashes are captured (sample rate 100%) + sentry_options_set_sample_rate(options, 1.0); // Capture 100% of events + sentry_options_set_traces_sample_rate(options, 1.0); // Capture 100% of traces + + sentry_init(options); + sentry_start_session(); + } +} + +void exitSentryEx() +{ + sentry_close(); +} +void sentryReportLogEx(SENTRY_LOG_LEVEL logLevel, + const std::string& logContent, + const std::string& funcModule, + const std::string& logTagKey, + const std::string& logTagValue, + const std::string& logTraceId) +{ + sentry_level_t sentry_msg_level; + switch (logLevel) + { + case SENTRY_LOG_TRACE: + sentry_msg_level = SENTRY_LEVEL_TRACE; + break; + case SENTRY_LOG_DEBUG: + sentry_msg_level = SENTRY_LEVEL_DEBUG; + break; + case SENTRY_LOG_INFO: + sentry_msg_level = SENTRY_LEVEL_INFO; + break; + case SENTRY_LOG_WARNING: + sentry_msg_level = SENTRY_LEVEL_WARNING; + break; + case SENTRY_LOG_ERROR: + sentry_msg_level = SENTRY_LEVEL_ERROR; + break; + case SENTRY_LOG_FATAL: + sentry_msg_level = SENTRY_LEVEL_FATAL; + break; + default: + return; + } + + sentry_value_t event = sentry_value_new_message_event(sentry_msg_level, + funcModule.c_str(), + logContent.c_str() + ); + + if (!logTraceId.empty()) + sentry_set_trace(logTraceId.c_str(), ""); + + if (!logTagKey.empty()) + sentry_set_tag(logTagKey.c_str(), logTagValue.c_str()); + + sentry_capture_event(event); +} + + +#else // SLIC3R_SENTRY not defined - provide no-op implementations + + +#endif // SLIC3R_SENTRY + +void initSentry() +{ +#ifdef SLIC3R_SENTRY + initSentryEx(); +#endif +} + +void exitSentry() +{ +#ifdef SLIC3R_SENTRY + exitSentryEx(); +#endif +} +void sentryReportLog(SENTRY_LOG_LEVEL logLevel, + const std::string& logContent, + const std::string& funcModule, + const std::string& logTagKey, + const std::string& logTagValue, + const std::string& logTraceId) +{ +#ifdef SLIC3R_SENTRY + sentryReportLogEx(logLevel, logContent, funcModule, logTagKey, logTagValue, logTraceId); +#endif +} + +} // namespace Slic3r + diff --git a/src/sentry_wrapper/SentryWrapper.hpp b/src/sentry_wrapper/SentryWrapper.hpp new file mode 100644 index 0000000000..32f46f210d --- /dev/null +++ b/src/sentry_wrapper/SentryWrapper.hpp @@ -0,0 +1,30 @@ +#ifndef slic3r_SentryWrapper_hpp_ +#define slic3r_SentryWrapper_hpp_ + +#include + +namespace Slic3r { + + void initSentry(); + + void exitSentry(); + + typedef enum SENTRY_LOG_LEVEL { + SENTRY_LOG_TRACE = -2, + SENTRY_LOG_DEBUG = -1, + SENTRY_LOG_INFO = 0, + SENTRY_LOG_WARNING = 1, + SENTRY_LOG_ERROR = 2, + SENTRY_LOG_FATAL = 3, + }; + + void sentryReportLog(SENTRY_LOG_LEVEL logLevel, + const std::string& logContent, + const std::string& funcModule = "", + const std::string& logTagKey = "", + const std::string& logTagValue = "", + const std::string& logTraceId = ""); + } // namespace Slic3r + +#endif // slic3r_SentryWrapper_hpp_ + diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5f6584ceaa..44ae2d49a1 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -737,3 +737,9 @@ endif () # Add a definition so that we can tell we are compiling slic3r. target_compile_definitions(libslic3r_gui PRIVATE SLIC3R_CURRENTLY_COMPILING_GUI_MODULE) + +# Sentry crash reporting integration +if (SLIC3R_SENTRY) + target_compile_definitions(libslic3r_gui PUBLIC SLIC3R_SENTRY) + target_link_libraries(libslic3r_gui sentry::sentry) +endif() \ No newline at end of file diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 15b9856903..8a42c4747e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -72,6 +72,7 @@ #include #include #include +#include "sentry_wrapper/SentryWrapper.hpp" #endif // _WIN32 #include