sentry: various fixes. Upload last log lines with the minidump

This commit is contained in:
Flyinghead 2022-12-21 11:23:15 +01:00
parent 4d77ce5a66
commit 1e0a875fa3
9 changed files with 122 additions and 19 deletions

View File

@ -23,7 +23,8 @@ option(USE_OPENGL "Use OpenGL API" ON)
option(USE_VIDEOCORE "RPI: use the legacy Broadcom GLES libraries" OFF)
option(APPLE_BREAKPAD "macOS: Build breakpad client and dump symbols" OFF)
option(ENABLE_GDB_SERVER "Build with GDB debugging support" OFF)
option(SENTRY_UPLOAD_URL "Sentry upload URL" "")
set(SENTRY_UPLOAD_URL "" CACHE STRING "Sentry upload URL")
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/shell/cmake")
@ -195,8 +196,8 @@ if(IOS)
GLES_SILENCE_DEPRECATION)
endif()
if(NOT ${SENTRY_UPLOAD_URL} STREQUAL "")
target_compile_definitions(${PROJECT_NAME} PRIVATE SENTRY_UPLOAD=${SENTRY_UPLOAD_URL})
if(NOT "${SENTRY_UPLOAD_URL}" STREQUAL "")
target_compile_definitions(${PROJECT_NAME} PRIVATE SENTRY_UPLOAD="${SENTRY_UPLOAD_URL}")
endif()
target_include_directories(${PROJECT_NAME} PRIVATE core core/deps core/deps/stb core/khronos core/deps/json)

View File

@ -298,7 +298,7 @@ std::vector<std::string> find_system_data_dirs()
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
{
if (succeeded)
registerCrash(descriptor.directory(), descriptor.path());
registerCrash(descriptor.directory().c_str(), descriptor.path());
return succeeded;
}

View File

@ -0,0 +1,62 @@
/*
Copyright 2022 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "LogManager.h"
#include <deque>
#include <mutex>
#include <vector>
class InMemoryListener : public LogListener
{
public:
InMemoryListener() {
instance = this;
}
~InMemoryListener() override {
instance = nullptr;
}
void Log(LogTypes::LOG_LEVELS, const char* msg) override
{
std::lock_guard<std::mutex> lock(mutex);
lines.emplace_back(msg);
if (lines.size() > MaxLines)
lines.pop_front();
}
std::vector<std::string> getLog()
{
std::lock_guard<std::mutex> lock(mutex);
std::vector<std::string> v(lines.begin(), lines.end());
return v;
}
static InMemoryListener *getInstance() {
return instance;
}
private:
std::mutex mutex;
std::deque<std::string> lines;
static constexpr int MaxLines = 10;
static InMemoryListener *instance;
};

View File

@ -14,6 +14,7 @@
#include <fstream>
#include "ConsoleListener.h"
#include "InMemoryListener.h"
#include "Log.h"
#include "StringUtil.h"
#include "cfg/cfg.h"
@ -150,6 +151,8 @@ LogManager::LogManager()
}
EnableListener(LogListener::CONSOLE_LISTENER, cfgLoadBool("log", "LogToConsole", true));
// EnableListener(LogListener::LOG_WINDOW_LISTENER, Config::Get(LOGGER_WRITE_TO_WINDOW));
RegisterListener(LogListener::IN_MEMORY_LISTENER, new InMemoryListener());
EnableListener(LogListener::IN_MEMORY_LISTENER, true);
for (LogContainer& container : m_log)
{
@ -164,6 +167,7 @@ LogManager::~LogManager()
// The log window listener pointer is owned by the GUI code.
delete m_listeners[LogListener::CONSOLE_LISTENER];
delete m_listeners[LogListener::FILE_LISTENER];
delete m_listeners[LogListener::IN_MEMORY_LISTENER];
}
// Return the current time formatted as Minutes:Seconds:Milliseconds
@ -265,3 +269,6 @@ void LogManager::Shutdown()
delete s_log_manager;
s_log_manager = nullptr;
}
// Another singleton
InMemoryListener *InMemoryListener::instance;

View File

@ -22,6 +22,7 @@ public:
FILE_LISTENER = 0,
CONSOLE_LISTENER,
LOG_WINDOW_LISTENER,
IN_MEMORY_LISTENER,
NUMBER_OF_LISTENERS // Must be last
};

View File

@ -153,17 +153,41 @@ std::string getBiosFontPath()
#include "rend/boxart/http_client.h"
#include "version.h"
#include "log/InMemoryListener.h"
#define FLYCAST_CRASH_LIST "flycast-crashes.txt"
void registerCrash(const std::string& directory, const char *path)
void registerCrash(const char *directory, const char *path)
{
FILE *f = nowide::fopen((directory + "/" FLYCAST_CRASH_LIST).c_str(), "at");
char list[256];
// Register .dmp in crash list
snprintf(list, sizeof(list), "%s/%s", directory, FLYCAST_CRASH_LIST);
FILE *f = nowide::fopen(list, "at");
if (f != nullptr)
{
fprintf(f, "%s", path);
fprintf(f, "%s\n", path);
fclose(f);
}
// Save last log lines
InMemoryListener *listener = InMemoryListener::getInstance();
if (listener != nullptr)
{
strncpy(list, path, sizeof(list) - 1);
list[sizeof(list) - 1] = '\0';
char *p = strrchr(list, '.');
if (p != nullptr && (p - list) < (int)sizeof(list) - 4)
{
strcpy(p + 1, "log");
FILE *f = nowide::fopen(list, "wt");
if (f != nullptr)
{
std::vector<std::string> log = listener->getLog();
for (const auto& line : log)
fprintf(f, "%s", line.c_str());
fclose(f);
}
}
}
}
void uploadCrashes(const std::string& directory)
@ -181,26 +205,34 @@ void uploadCrashes(const std::string& directory)
*p = '\0';
if (file_exists(line))
{
std::string dmpfile(line);
std::string logfile = get_file_basename(dmpfile) + ".log";
#ifdef SENTRY_UPLOAD
#define STRINGIZE(x) #x
if (config::UploadCrashLogs)
{
NOTICE_LOG(COMMON, "Uploading minidump %s", line);
std::vector<http::PostField> fields;
fields.emplace_back("upload_file_minidump", std::string(line), "application/octet-stream");
fields.emplace_back("upload_file_minidump", dmpfile, "application/octet-stream");
fields.emplace_back("flycast_version", std::string(GIT_VERSION));
// TODO log, config, gpu/driver
int rc = http::post(STRINGIZE(SENTRY_UPLOAD), fields);
if (rc >= 200 && rc < 300)
nowide::remove(line);
if (file_exists(logfile))
fields.emplace_back("flycast_log", logfile, "text/plain");
// TODO config, gpu/driver, ...
int rc = http::post(SENTRY_UPLOAD, fields);
if (rc >= 200 && rc < 300) {
nowide::remove(dmpfile.c_str());
nowide::remove(logfile.c_str());
}
else
{
NOTICE_LOG(COMMON, "Upload failed: HTTP error %d", rc);
uploadFailure = true;
}
}
else
#undef STRINGIZE
#endif
{
nowide::remove(line);
nowide::remove(dmpfile.c_str());
nowide::remove(logfile.c_str());
}
}
}
@ -212,7 +244,7 @@ void uploadCrashes(const std::string& directory)
#else
void registerCrash(const std::string& directory, const char *path) {}
void registerCrash(const char *directory, const char *path) {}
void uploadCrashes(const std::string& directory) {}
#endif

View File

@ -135,5 +135,5 @@ static inline void freeAligned(void *p)
#endif
}
void registerCrash(const std::string& directory, const char *path);
void registerCrash(const char *directory, const char *path);
void uploadCrashes(const std::string& directory);

View File

@ -739,7 +739,7 @@ static bool dumpCallback(const wchar_t* dump_path,
if (path.convert(minidump_id))
{
std::string fullPath = directory + '\\' + std::string(path.c_str()) + ".dmp";
registerCrash(directory, fullPath.c_str());
registerCrash(directory.c_str(), fullPath.c_str());
}
}
}

View File

@ -148,7 +148,7 @@ static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
if (succeeded)
{
__android_log_print(ANDROID_LOG_ERROR, "Flycast", "Minidump saved to '%s'\n", descriptor.path());
registerCrash(descriptor.directory(), descriptor.path());
registerCrash(descriptor.directory().c_str(), descriptor.path());
}
return succeeded;
}