mirror of
https://github.com/MrLowPr/overdrive.git
synced 2026-10-19 00:01:19 +02:00
120 lines
4.7 KiB
C++
120 lines
4.7 KiB
C++
// Copyright (C) 2026 half144 and PS5 React contributors
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Additional attribution term: see LICENSE-ATTRIBUTION.
|
|
#include "crash_log.hpp"
|
|
#include <atomic>
|
|
#include <csignal>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <fcntl.h>
|
|
#include <initializer_list>
|
|
#include <sys/stat.h>
|
|
#include <sys/ucontext.h>
|
|
#include <unistd.h>
|
|
|
|
extern "C" int __real_sceKernelDebugOutText(int channel, const char* text);
|
|
extern "C" {
|
|
struct ModuleSegment { void* address; std::uint32_t size; std::int32_t prot; };
|
|
struct ModuleInfo { std::size_t size; char name[256]; ModuleSegment segments[4]; std::uint32_t segment_count; std::uint8_t fingerprint[20]; };
|
|
int sceKernelGetModuleList(int* handles, std::size_t count, std::size_t* actual);
|
|
int sceKernelGetModuleInfo(int handle, ModuleInfo* info);
|
|
}
|
|
|
|
namespace {
|
|
std::atomic<int> log_fd{-1};
|
|
// A session that logs in a loop stops here rather than filling the drive.
|
|
constexpr long kMaxBytes = 16L * 1024 * 1024;
|
|
std::atomic<long> written{0};
|
|
|
|
// Lines from before start(), such as how filesystem access chose the folder app.log lives in, wait
|
|
// here until start() opens it.
|
|
char early[16 * 1024];
|
|
std::atomic<std::size_t> early_size{0};
|
|
std::atomic<bool> started{false};
|
|
|
|
void append(const char* text, std::size_t size) {
|
|
if (!started.load(std::memory_order_acquire)) {
|
|
std::size_t at = early_size.load();
|
|
do {
|
|
if (at + size > sizeof early) return;
|
|
} while (!early_size.compare_exchange_weak(at, at + size));
|
|
std::memcpy(early + at, text, size);
|
|
return;
|
|
}
|
|
const int fd = log_fd.load(std::memory_order_relaxed);
|
|
if (fd < 0 || written.fetch_add(static_cast<long>(size)) > kMaxBytes) return;
|
|
(void)!::write(fd, text, size);
|
|
}
|
|
|
|
// Async-signal-safe: no stdio, no allocation.
|
|
char* hex(char* out, std::uint64_t value) {
|
|
*out++ = '0'; *out++ = 'x';
|
|
for (int shift = 60; shift >= 0; shift -= 4) *out++ = "0123456789abcdef"[(value >> shift) & 0xf];
|
|
return out;
|
|
}
|
|
|
|
void fatal(int signal, siginfo_t* info, void* context) {
|
|
char line[160];
|
|
char* at = line;
|
|
const char prefix[] = "[PS5-REACT] fatal signal ";
|
|
std::memcpy(at, prefix, sizeof prefix - 1); at += sizeof prefix - 1;
|
|
if (signal >= 10) *at++ = static_cast<char>('0' + signal / 10);
|
|
*at++ = static_cast<char>('0' + signal % 10);
|
|
const char address[] = " address "; std::memcpy(at, address, sizeof address - 1); at += sizeof address - 1;
|
|
at = hex(at, reinterpret_cast<std::uintptr_t>(info ? info->si_addr : nullptr));
|
|
const auto* uc = static_cast<const ucontext_t*>(context);
|
|
const char rip[] = " rip "; std::memcpy(at, rip, sizeof rip - 1); at += sizeof rip - 1;
|
|
at = hex(at, uc ? static_cast<std::uint64_t>(uc->uc_mcontext.mc_rip) : 0);
|
|
const char rsp[] = " rsp "; std::memcpy(at, rsp, sizeof rsp - 1); at += sizeof rsp - 1;
|
|
at = hex(at, uc ? static_cast<std::uint64_t>(uc->uc_mcontext.mc_rsp) : 0);
|
|
*at++ = '\n';
|
|
append(line, static_cast<std::size_t>(at - line));
|
|
__real_sceKernelDebugOutText(0, line);
|
|
::signal(signal, SIG_DFL);
|
|
::raise(signal);
|
|
}
|
|
}
|
|
|
|
// Linked with --wrap=sceKernelDebugOutText: every klog line of the title, from any library.
|
|
extern "C" int __wrap_sceKernelDebugOutText(int channel, const char* text) {
|
|
if (text) append(text, std::strlen(text));
|
|
return __real_sceKernelDebugOutText(channel, text);
|
|
}
|
|
|
|
namespace {
|
|
// Where every loaded module sits, so a fault's rip maps to a module and an offset in it.
|
|
void log_modules() {
|
|
int handles[256];
|
|
std::size_t count = 0;
|
|
if (sceKernelGetModuleList(handles, 256, &count) != 0) return;
|
|
for (std::size_t i = 0; i < count && i < 256; ++i) {
|
|
ModuleInfo info {};
|
|
info.size = sizeof info;
|
|
if (sceKernelGetModuleInfo(handles[i], &info) != 0) continue;
|
|
char line[400];
|
|
const int n = std::snprintf(line, sizeof line, "[PS5-REACT] module %s text=%p+0x%x\n", info.name,
|
|
info.segment_count ? info.segments[0].address : nullptr, info.segment_count ? info.segments[0].size : 0u);
|
|
if (n > 0) { append(line, static_cast<std::size_t>(n)); __real_sceKernelDebugOutText(0, line); }
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace crash_log {
|
|
void start(const char* directory) {
|
|
char current[320], previous[320];
|
|
std::snprintf(current, sizeof current, "%s/app.log", directory);
|
|
std::snprintf(previous, sizeof previous, "%s/app.prev.log", directory);
|
|
::rename(current, previous);
|
|
log_fd = ::open(current, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0644);
|
|
started.store(true, std::memory_order_release);
|
|
if (log_fd >= 0) append(early, early_size.load());
|
|
struct sigaction action {};
|
|
action.sa_sigaction = fatal;
|
|
action.sa_flags = SA_SIGINFO | SA_RESETHAND;
|
|
sigemptyset(&action.sa_mask);
|
|
for (int signal : {SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGABRT, SIGTRAP}) sigaction(signal, &action, nullptr);
|
|
log_modules();
|
|
}
|
|
}
|