mirror of
https://github.com/AxioDL/logvisor.git
synced 2025-12-08 21:17:44 +00:00
Compare commits
9 Commits
libbacktra
...
33e7322595
| Author | SHA1 | Date | |
|---|---|---|---|
| 33e7322595 | |||
| bb113e03f8 | |||
| 240491ccad | |||
| fd343eb234 | |||
| aac06c3465 | |||
| 0e3fe3e766 | |||
| 17aa25cfa9 | |||
|
|
d4af1a1088 | ||
| 8913e55e34 |
8
.gitmodules
vendored
8
.gitmodules
vendored
@@ -1,6 +1,8 @@
|
||||
[submodule "fmt"]
|
||||
path = fmt
|
||||
url = https://github.com/fmtlib/fmt
|
||||
[submodule "libbacktrace"]
|
||||
path = libbacktrace
|
||||
url = https://github.com/status-im/libbacktrace.git
|
||||
branch = master
|
||||
[submodule "sentry"]
|
||||
path = sentry
|
||||
url = https://github.com/getsentry/sentry-native.git
|
||||
branch = master
|
||||
|
||||
@@ -15,38 +15,44 @@ add_library(logvisor
|
||||
lib/logvisor.cpp
|
||||
include/logvisor/logvisor.hpp)
|
||||
|
||||
if(APPLE OR UNIX)
|
||||
include(ExternalProject)
|
||||
find_package(Git REQUIRED)
|
||||
find_program(MAKE_EXE NAMES gmake make)
|
||||
ExternalProject_Add(
|
||||
project_libbacktrace
|
||||
PREFIX ${PROJECT_BINARY_DIR}/libbacktrace
|
||||
GIT_REPOSITORY https://github.com/status-im/libbacktrace.git
|
||||
GIT_TAG 2e878a38dd7144c84b665019b1085ea38ebe56d1
|
||||
CONFIGURE_COMMAND ${PROJECT_BINARY_DIR}/libbacktrace/src/project_libbacktrace/configure --enable-host-shared --prefix=${PROJECT_BINARY_DIR}/libbacktrace/install --enable-shared=no --enable-static=yes
|
||||
BUILD_COMMAND ${MAKE_EXE}
|
||||
INSTALL_COMMAND ${MAKE_EXE} install
|
||||
BUILD_IN_SOURCE 1
|
||||
)
|
||||
add_library(libbacktrace STATIC IMPORTED GLOBAL)
|
||||
set_target_properties(libbacktrace PROPERTIES IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/libbacktrace/install/lib/libbacktrace.a")
|
||||
set_target_properties(libbacktrace PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}/libbacktrace/install/include")
|
||||
# Hack for INTERFACE_INCLUDE_DIRECTORIES propagation
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/15052
|
||||
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/libbacktrace/install/include")
|
||||
add_dependencies(libbacktrace project_libbacktrace)
|
||||
set(BACKTRACE_LIBS libbacktrace)
|
||||
if ("${SENTRY_DSN}" STREQUAL "")
|
||||
message(STATUS "SENTRY_DSN not set, not enabling Sentry")
|
||||
target_compile_definitions(logvisor PUBLIC SENTRY_ENABLED=0)
|
||||
set(SENTRY_LIB "")
|
||||
set(BREAKPAD_CLIENT "")
|
||||
else ()
|
||||
set(BACKTRACE_LIBS "")
|
||||
message(STATUS "Enabling Sentry integration")
|
||||
add_subdirectory(sentry)
|
||||
target_compile_definitions(logvisor PUBLIC SENTRY_ENABLED=1)
|
||||
target_compile_definitions(logvisor PRIVATE SENTRY_DSN="${SENTRY_DSN}")
|
||||
set(SENTRY_LIB sentry)
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(BREAKPAD_CLIENT breakpad_client)
|
||||
target_compile_options(breakpad_client PRIVATE -Wno-implicit-fallthrough -Wno-array-bounds)
|
||||
target_compile_options(sentry PRIVATE "-Wno-implicit-fallthrough")
|
||||
set_property(TARGET breakpad_client PROPERTY CXX_STANDARD 17)
|
||||
set_property(TARGET sentry PROPERTY CXX_STANDARD 17)
|
||||
else ()
|
||||
set(BREAKPAD_CLIENT "")
|
||||
endif ()
|
||||
if (MSVC)
|
||||
target_compile_options(crashpad_client PRIVATE "/W0")
|
||||
target_compile_options(crashpad_util PRIVATE "/W0")
|
||||
target_compile_options(crashpad_snapshot PRIVATE "/W0")
|
||||
target_compile_options(mini_chromium PRIVATE "/W0")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
target_compile_options(crashpad_zlib PRIVATE "-mpclmul")
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
target_link_libraries(logvisor PUBLIC fmt)
|
||||
target_link_libraries(logvisor PUBLIC fmt ${SENTRY_LIB})
|
||||
if(NX)
|
||||
target_link_libraries(logvisor PUBLIC debug nxd optimized nx)
|
||||
else()
|
||||
target_link_libraries(logvisor PRIVATE ${CMAKE_DL_LIBS} ${BACKTRACE_LIBS})
|
||||
target_link_libraries(logvisor PUBLIC ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
target_include_directories(logvisor PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||
|
||||
install(DIRECTORY include/logvisor DESTINATION include)
|
||||
@@ -58,7 +64,7 @@ set(config_install_dir "lib/cmake/logvisor")
|
||||
|
||||
# Associate target with export
|
||||
install(
|
||||
TARGETS logvisor fmt
|
||||
TARGETS logvisor fmt ${SENTRY_LIB} ${BREAKPAD_CLIENT}
|
||||
EXPORT logvisorTargets
|
||||
ARCHIVE DESTINATION "lib"
|
||||
INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target.
|
||||
|
||||
2
fmt
2
fmt
Submodule fmt updated: 9bdd1596ce...95da484727
@@ -17,6 +17,7 @@
|
||||
#endif
|
||||
|
||||
extern "C" void logvisorBp();
|
||||
#define log_typeid(type) std::hash<std::string>()(#type)
|
||||
|
||||
namespace logvisor {
|
||||
|
||||
@@ -43,6 +44,10 @@ enum Level {
|
||||
* @brief Backend interface for receiving app-wide log events
|
||||
*/
|
||||
struct ILogger {
|
||||
private:
|
||||
uint64_t m_typeHash;
|
||||
public:
|
||||
ILogger(uint64_t typeHash) : m_typeHash(typeHash) {}
|
||||
virtual ~ILogger() = default;
|
||||
virtual void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) = 0;
|
||||
virtual void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) = 0;
|
||||
@@ -50,6 +55,8 @@ struct ILogger {
|
||||
fmt::string_view format, fmt::format_args args) = 0;
|
||||
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
|
||||
fmt::wstring_view format, fmt::wformat_args args) = 0;
|
||||
|
||||
[[nodiscard]] uint64_t getTypeId() const { return m_typeHash; }
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -146,6 +153,16 @@ void RegisterFileLogger(const char* filepath);
|
||||
*/
|
||||
void RegisterStandardExceptions();
|
||||
|
||||
#if SENTRY_ENABLED
|
||||
/**
|
||||
* @brief Register Sentry crash reporting & logging.
|
||||
* @param appName The application name
|
||||
* @param appVersion The application version
|
||||
* @param cacheDir Directory for Sentry cache files
|
||||
*/
|
||||
void RegisterSentry(const char* appName, const char* appVersion, const char* cacheDir);
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
/**
|
||||
* @brief Spawn an application-owned cmd.exe window for displaying console output
|
||||
@@ -219,7 +236,7 @@ public:
|
||||
return;
|
||||
_vreport(severity, fmt::to_string_view<Char>(format),
|
||||
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
||||
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||
fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
@@ -243,7 +260,7 @@ public:
|
||||
return;
|
||||
_vreportSource(severity, file, linenum, fmt::to_string_view<Char>(format),
|
||||
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
||||
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||
fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
@@ -302,5 +319,5 @@ void quicklog(const S& format, Args&&... args) {
|
||||
logvisor::MainLoggers[0]->report(
|
||||
"quick", logvisor::Info, fmt::to_string_view<Char>(format),
|
||||
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
||||
fmt::internal::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||
fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
140
lib/logvisor.cpp
140
lib/logvisor.cpp
@@ -34,6 +34,10 @@
|
||||
#include <csignal>
|
||||
#include "logvisor/logvisor.hpp"
|
||||
|
||||
#if SENTRY_ENABLED
|
||||
#include <sentry.h>
|
||||
#endif
|
||||
|
||||
/* ANSI sequences */
|
||||
#define RED "\x1b[1;31m"
|
||||
#define YELLOW "\x1b[1;33m"
|
||||
@@ -178,26 +182,57 @@ void KillProcessTree() {
|
||||
|
||||
void KillProcessTree() {}
|
||||
|
||||
#include <backtrace.h>
|
||||
|
||||
void backtrace_error(void* data, const char* msg, int errnum) {
|
||||
std::fprintf(stderr, "Error retrieving stack trace: %d %s\n", errnum, msg);
|
||||
}
|
||||
|
||||
int backtrace_callback(void* data, uintptr_t pc, const char* filename, int lineno, const char* function) {
|
||||
int status = -255;
|
||||
char* demangledName = abi::__cxa_demangle(function, nullptr, nullptr, &status);
|
||||
std::fprintf(stderr, "0x%lX %s\n\t%s:%d\n", pc, status == 0 ? demangledName : function, filename, lineno);
|
||||
if (demangledName != nullptr) {
|
||||
std::free(demangledName);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <execinfo.h>
|
||||
[[noreturn]] void logvisorAbort() {
|
||||
backtrace_state* state = backtrace_create_state(nullptr, 0, backtrace_error, nullptr);
|
||||
if (state != nullptr) {
|
||||
backtrace_full(state, 0, backtrace_callback, backtrace_error, nullptr);
|
||||
void* array[128];
|
||||
size_t size = backtrace(array, 128);
|
||||
|
||||
constexpr size_t exeBufSize = 1024 + 1;
|
||||
char exeNameBuffer[exeBufSize] = {};
|
||||
|
||||
#if __linux__
|
||||
readlink("/proc/self/exe", exeNameBuffer, exeBufSize);
|
||||
#endif
|
||||
|
||||
#if __APPLE__
|
||||
std::string cmdLineStr = fmt::format(FMT_STRING("atos -p {}"), getpid());
|
||||
#else
|
||||
std::string cmdLineStr = fmt::format(FMT_STRING("2>/dev/null addr2line -C -f -e \"{}\""), exeNameBuffer);
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
#if __linux__
|
||||
Dl_info dlip;
|
||||
if (dladdr(array[i], &dlip))
|
||||
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
|
||||
else
|
||||
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)array[i]);
|
||||
#else
|
||||
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)array[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
FILE* fp = popen(cmdLineStr.c_str(), "r");
|
||||
if (fp) {
|
||||
char readBuf[256];
|
||||
size_t readSz;
|
||||
while ((readSz = fread(readBuf, 1, 256, fp)))
|
||||
std::fwrite(readBuf, 1, readSz, stderr);
|
||||
pclose(fp);
|
||||
} else {
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
std::fputs("- ", stderr);
|
||||
Dl_info dlip;
|
||||
if (dladdr(array[i], &dlip)) {
|
||||
int status;
|
||||
char* demangledName = abi::__cxa_demangle(dlip.dli_sname, nullptr, nullptr, &status);
|
||||
std::fprintf(stderr, "%p(%s+%p)\n", dlip.dli_saddr, demangledName ? demangledName : dlip.dli_sname,
|
||||
(void*)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
|
||||
std::free(demangledName);
|
||||
} else {
|
||||
std::fprintf(stderr, "%p\n", array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::fflush(stderr);
|
||||
@@ -505,7 +540,7 @@ static const char* Term = nullptr;
|
||||
#endif
|
||||
bool XtermColor = false;
|
||||
struct ConsoleLogger : public ILogger {
|
||||
ConsoleLogger() {
|
||||
ConsoleLogger() : ILogger(log_typeid(ConsoleLogger)) {
|
||||
#if _WIN32
|
||||
#if !WINDOWS_STORE
|
||||
const char* conemuANSI = getenv("ConEmuANSI");
|
||||
@@ -524,6 +559,7 @@ struct ConsoleLogger : public ILogger {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
~ConsoleLogger() override = default;
|
||||
|
||||
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
||||
/* Clear current line out */
|
||||
@@ -705,10 +741,43 @@ void RegisterStandardExceptions() {
|
||||
signal(SIGFPE, AbortHandler);
|
||||
}
|
||||
|
||||
#if SENTRY_ENABLED
|
||||
void RegisterSentry(const char* appName, const char* appVersion, const char* cacheDir) {
|
||||
sentry_options_t *options = sentry_options_new();
|
||||
sentry_options_set_database_path(options, cacheDir);
|
||||
sentry_options_set_auto_session_tracking(options, true);
|
||||
sentry_options_set_symbolize_stacktraces(options, true);
|
||||
sentry_options_set_dsn(options, SENTRY_DSN);
|
||||
|
||||
#ifdef NDEBUG
|
||||
sentry_options_set_environment(options, "release");
|
||||
#else
|
||||
sentry_options_set_environment(options, "debug");
|
||||
#endif
|
||||
|
||||
std::string release = fmt::format(FMT_STRING("{}@{}"), appName, appVersion);
|
||||
sentry_options_set_release(options, release.c_str());
|
||||
|
||||
sentry_init(options);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct FileLogger : public ILogger {
|
||||
FILE* fp;
|
||||
FILE* fp = nullptr;
|
||||
FileLogger(uint64_t typeHash) : ILogger(typeHash) {}
|
||||
virtual void openFile() = 0;
|
||||
virtual void closeFile() { std::fclose(fp); }
|
||||
void openFileIfNeeded() {
|
||||
if (!fp) {
|
||||
openFile();
|
||||
}
|
||||
}
|
||||
virtual void closeFile() {
|
||||
if (fp) {
|
||||
std::fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
}
|
||||
virtual ~FileLogger() { closeFile(); }
|
||||
|
||||
void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
||||
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
||||
@@ -753,44 +822,41 @@ struct FileLogger : public ILogger {
|
||||
}
|
||||
|
||||
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
||||
openFile();
|
||||
openFileIfNeeded();
|
||||
_reportHead(modName, nullptr, severity);
|
||||
fmt::vprint(fp, format, args);
|
||||
std::fputc('\n', fp);
|
||||
closeFile();
|
||||
}
|
||||
|
||||
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
|
||||
openFile();
|
||||
openFileIfNeeded();
|
||||
_reportHead(modName, nullptr, severity);
|
||||
fmt::vprint(fp, format, args);
|
||||
std::fputc('\n', fp);
|
||||
closeFile();
|
||||
}
|
||||
|
||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
||||
fmt::format_args args) override {
|
||||
openFile();
|
||||
openFileIfNeeded();
|
||||
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
||||
fmt::vprint(fp, format, args);
|
||||
std::fputc('\n', fp);
|
||||
closeFile();
|
||||
}
|
||||
|
||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
|
||||
fmt::wformat_args args) override {
|
||||
openFile();
|
||||
openFileIfNeeded();
|
||||
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
||||
fmt::vprint(fp, format, args);
|
||||
std::fputc('\n', fp);
|
||||
closeFile();
|
||||
}
|
||||
};
|
||||
|
||||
struct FileLogger8 : public FileLogger {
|
||||
const char* m_filepath;
|
||||
FileLogger8(const char* filepath) : m_filepath(filepath) {}
|
||||
FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {}
|
||||
void openFile() override { fp = std::fopen(m_filepath, "a"); }
|
||||
~FileLogger8() override = default;
|
||||
};
|
||||
|
||||
void RegisterFileLogger(const char* filepath) {
|
||||
@@ -802,19 +868,21 @@ void RegisterFileLogger(const char* filepath) {
|
||||
|
||||
struct FileLogger16 : public FileLogger {
|
||||
const wchar_t* m_filepath;
|
||||
FileLogger16(const wchar_t* filepath) : m_filepath(filepath) {}
|
||||
FileLogger16(const wchar_t* filepath) : FileLogger(log_typeid(FileLogger16)), m_filepath(filepath) {}
|
||||
void openFile() override { fp = _wfopen(m_filepath, L"a"); }
|
||||
~FileLogger16() override = default;
|
||||
};
|
||||
|
||||
void RegisterFileLogger(const wchar_t* filepath) {
|
||||
/* Determine if file logger already added */
|
||||
for (auto& logger : MainLoggers) {
|
||||
FileLogger16* filelogger = dynamic_cast<FileLogger16*>(logger.get());
|
||||
if (filelogger) {
|
||||
if (!wcscmp(filepath, filelogger->m_filepath))
|
||||
for (const auto& logger : MainLoggers) {
|
||||
if (logger->getTypeId() == log_typeid(FileLogger16)) {
|
||||
const auto* fl = static_cast<const FileLogger16*>(logger.get());
|
||||
if (!wcscmp(filepath, fl->m_filepath)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise construct new file logger */
|
||||
MainLoggers.emplace_back(new FileLogger16(filepath));
|
||||
|
||||
1
sentry
Submodule
1
sentry
Submodule
Submodule sentry added at 6d71367b17
Reference in New Issue
Block a user