Files

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);
}