mirror of
https://github.com/Awakenchan/LanternEngine.git
synced 2026-11-13 04:26:46 -10:00
183 lines
6.8 KiB
C++
183 lines
6.8 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
#include "bbport_write_log.h"
|
|
#include "bbport_free_check.h"
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <x86intrin.h>
|
|
#include "bbport_platform.h"
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <ucontext.h>
|
|
#endif
|
|
|
|
namespace BbWriteLog {
|
|
namespace {
|
|
struct Entry {
|
|
std::uint64_t address, size, first, tsc;
|
|
std::uint32_t source, tid;
|
|
};
|
|
constexpr std::size_t Size = 1 << 20;
|
|
std::array<Entry, Size> ring;
|
|
std::atomic<std::uint64_t> head{0};
|
|
std::array<Entry, 256> hits;
|
|
std::atomic<std::uint64_t> hits_head{0};
|
|
constexpr std::uint64_t Pattern = 0x0000005300000000ull;
|
|
|
|
void Push(std::array<Entry, Size>& r, std::atomic<std::uint64_t>& h, const Entry& e) {
|
|
r[h.fetch_add(1, std::memory_order_relaxed) % r.size()] = e;
|
|
}
|
|
}
|
|
|
|
void Record(std::uint64_t address, const void* data, std::uint64_t size, Source source);
|
|
|
|
int Mode() {
|
|
static const int mode = [] {
|
|
const char* env = std::getenv("BB_WRITE_LOG");
|
|
return env ? std::atoi(env) : 0;
|
|
}();
|
|
return mode;
|
|
}
|
|
|
|
bool Enabled() {
|
|
return Mode() == 1;
|
|
}
|
|
|
|
void NoteIntent(std::uint64_t address, const void* data, std::uint64_t size, Source source) {
|
|
if (Mode() == 2) {
|
|
Record(address, data, size, source);
|
|
}
|
|
}
|
|
|
|
void Note(std::uint64_t address, const void* data, std::uint64_t size, Source source) {
|
|
if (Enabled()) {
|
|
Record(address, data, size, source);
|
|
}
|
|
}
|
|
|
|
void Record(std::uint64_t address, const void* data, std::uint64_t size, Source source) {
|
|
static thread_local const std::uint32_t tid = BbPlatform::CurrentThreadId();
|
|
Entry e{address, size, 0, __rdtsc(), source, tid};
|
|
std::memcpy(&e.first, data, size < 8 ? size : 8);
|
|
Push(ring, head, e);
|
|
if (size > 64) {
|
|
return;
|
|
}
|
|
const auto* bytes = static_cast<const unsigned char*>(data);
|
|
for (std::uint64_t at = (8 - (address & 7)) & 7; at + 8 <= size; at += 8) {
|
|
std::uint64_t v;
|
|
std::memcpy(&v, bytes + at, 8);
|
|
if (v == Pattern) {
|
|
Entry hit = e;
|
|
hit.address = address + at;
|
|
hit.first = v;
|
|
hits[hits_head.fetch_add(1, std::memory_order_relaxed) % hits.size()] = hit;
|
|
}
|
|
}
|
|
}
|
|
void DumpRange(std::uint64_t address, std::uint64_t size) {
|
|
if (Mode() == 0) {
|
|
std::fprintf(stderr, "Write log: off (BB_WRITE_LOG=1 lists the writes into this range)\n");
|
|
return;
|
|
}
|
|
const char* sources[] = {"backing", "WriteData", "fence",
|
|
"EOP (decoded)", "WriteData (decoded)", "EOS (decoded)", "DmaData"};
|
|
const std::uint64_t now = __rdtsc(), n = head.load();
|
|
int shown = 0;
|
|
for (std::uint64_t i = n; i-- > (n > Size ? n - Size : 0) && shown < 64;) {
|
|
const Entry& e = ring[i % Size];
|
|
if (e.address < address + size && address < e.address + e.size) {
|
|
std::fprintf(stderr,
|
|
"Write log: into range %s %#llx +%llu first %#llx tid %u, %.3f s before\n",
|
|
e.source < 7 ? sources[e.source] : "?", (unsigned long long)e.address,
|
|
(unsigned long long)e.size, (unsigned long long)e.first, e.tid,
|
|
double(now - e.tsc) / 3.0e9);
|
|
++shown;
|
|
}
|
|
}
|
|
std::fprintf(stderr, "Write log: %d of %llu logged writes in %#llx +%llu\n", shown,
|
|
(unsigned long long)n, (unsigned long long)address, (unsigned long long)size);
|
|
}
|
|
}
|
|
|
|
extern "C" void bbgpu_dump_guest_writes(void* ucontext) {
|
|
using namespace BbWriteLog;
|
|
#ifdef _WIN32
|
|
const CONTEXT* c = static_cast<const EXCEPTION_POINTERS*>(ucontext)->ContextRecord;
|
|
const std::uint64_t regs[] = {c->Rax, c->Rbx, c->Rcx, c->Rdx, c->Rsi, c->Rdi, c->R14, c->R15};
|
|
#else
|
|
const auto* uc = static_cast<const ucontext_t*>(ucontext);
|
|
const auto* g = uc->uc_mcontext.gregs;
|
|
const std::uint64_t regs[] = {std::uint64_t(g[REG_RAX]), std::uint64_t(g[REG_RBX]),
|
|
std::uint64_t(g[REG_RCX]), std::uint64_t(g[REG_RDX]),
|
|
std::uint64_t(g[REG_RSI]), std::uint64_t(g[REG_RDI]),
|
|
std::uint64_t(g[REG_R14]), std::uint64_t(g[REG_R15])};
|
|
#endif
|
|
BbFreeCheck::DumpAtFault(regs[0], regs[6]);
|
|
if (Mode() == 0) {
|
|
return;
|
|
}
|
|
const char* names[] = {"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r14", "r15"};
|
|
for (int i = 0; i < 8; ++i) {
|
|
std::fprintf(stderr, "Write log: %s=%#llx\n", names[i], (unsigned long long)regs[i]);
|
|
}
|
|
const char* sources[] = {"backing", "WriteData", "fence",
|
|
"EOP (decoded)", "WriteData (decoded)", "EOS (decoded)", "DmaData"};
|
|
const std::uint64_t now = __rdtsc();
|
|
const auto print = [&](const Entry& e, const char* what) {
|
|
std::fprintf(stderr,
|
|
"Write log: %s %s %#llx +%llu first %#llx tid %u, %.3f s before the fault\n",
|
|
what, e.source < 7 ? sources[e.source] : "?", (unsigned long long)e.address,
|
|
(unsigned long long)e.size, (unsigned long long)e.first, e.tid,
|
|
double(now - e.tsc) / 3.0e9);
|
|
};
|
|
const std::uint64_t nh = hits_head.load();
|
|
for (std::uint64_t i = nh > hits.size() ? nh - hits.size() : 0; i < nh; ++i) {
|
|
print(hits[i % hits.size()], "pattern");
|
|
}
|
|
const std::uint64_t block = regs[0];
|
|
for (std::uint64_t at = block - 0x30; at < block + 0x60; at += 8) {
|
|
std::uint64_t value = 0;
|
|
std::memcpy(&value, reinterpret_cast<const void*>(at), 8);
|
|
std::fprintf(stderr, "Write log: [%#llx] = %#llx\n", (unsigned long long)at,
|
|
(unsigned long long)value);
|
|
}
|
|
{
|
|
const std::uint64_t n = head.load();
|
|
int shown = 0;
|
|
for (std::uint64_t i = n; i-- > (n > Size ? n - Size : 0) && shown < 200;) {
|
|
const Entry& e = ring[i % Size];
|
|
if (e.address + e.size > block - 0x30 && e.address < block + 0x60) {
|
|
print(e, "in block");
|
|
++shown;
|
|
}
|
|
}
|
|
shown = 0;
|
|
for (std::uint64_t i = n; i-- > (n > Size ? n - Size : 0) && shown < 40;) {
|
|
const Entry& e = ring[i % Size];
|
|
if ((e.first & 0xffffffffull) == 0x53 || (e.first >> 32) == 0x53) {
|
|
print(e, "value 0x53");
|
|
++shown;
|
|
}
|
|
}
|
|
}
|
|
const std::uint64_t n = head.load();
|
|
int shown = 0;
|
|
for (std::uint64_t i = n; i-- > (n > Size ? n - Size : 0) && shown < 64;) {
|
|
const Entry& e = ring[i % Size];
|
|
bool nearby = false;
|
|
for (const auto r : regs) {
|
|
nearby |= r + 0x1000 > e.address && r < e.address + e.size + 0x1000;
|
|
}
|
|
if (nearby) {
|
|
print(e, "near");
|
|
++shown;
|
|
}
|
|
}
|
|
std::fprintf(stderr, "Write log: %llu writes logged\n", (unsigned long long)n);
|
|
}
|