Files
2026-08-08 13:54:26 -07:00

201 lines
6.2 KiB
C++

#include "plog.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
namespace {
std::int64_t ClockTicks() {
LARGE_INTEGER counter = {};
QueryPerformanceCounter(&counter);
return counter.QuadPart;
}
double TicksPerMillisecond() {
static double value = 0.0;
if (value == 0.0) {
LARGE_INTEGER frequency = {};
QueryPerformanceFrequency(&frequency);
value = static_cast<double>(frequency.QuadPart) / 1000.0;
}
return value;
}
float TicksToMilliseconds(const std::int64_t ticks) {
return static_cast<float>(static_cast<double>(ticks) / TicksPerMillisecond());
}
} // namespace
idPLog pLog;
idPLog::idPLog()
: treeEntries(64), logEntries(64), lastEntry(0), overHeadTicks(-1),
groupMask(0) {
Clear();
}
void idPLog::Clear() {
treeEntries.Clear();
logEntries.Clear();
logEntry_t* const root = logEntries.Alloc();
if (root != nullptr) {
root->label = "root";
root->parent = 0;
root->totalTicks = 0;
}
lastEntry = 0;
}
void idPLog::EnsureEntries() {
logEntries.Reserve(4096);
treeEntries.Reserve(4096);
}
std::int64_t idPLog::GetOverHeadTicks() {
if (overHeadTicks >= 0) return overHeadTicks;
std::int64_t best = INT64_MAX;
for (int pass = 0; pass < 4; ++pass) {
const std::int64_t start = ClockTicks();
for (int index = 0; index < 1000; ++index) {
const std::int64_t end = ClockTicks();
(void)end;
}
best = std::min(best, ClockTicks() - start);
}
overHeadTicks = static_cast<int>(best / 1000);
return overHeadTicks;
}
void idPLog::SubtractOverhead() {
const std::int64_t overhead = GetOverHeadTicks();
for (int index = 1; index < logEntries.Num(); ++index) {
const int parent = logEntries[index].parent;
if (parent >= 0 && parent < logEntries.Num()) {
logEntries[parent].totalTicks = std::max<std::int64_t>(0,
logEntries[parent].totalTicks - overhead);
}
}
}
float idPLog::BuildLogData(const float thresholdMS,
const std::int64_t adjustByTicks) {
treeEntries.Clear();
treeEntry_t* const root = treeEntries.Alloc();
if (root == nullptr) return 0.0f;
root->label = "root";
root->depth = 0;
root->numHits = 1;
root->parent = -1;
root->firstChild = -1;
root->nextChild = -1;
for (int index = 1; index < logEntries.Num(); ++index) {
const logEntry_t& source = logEntries[index];
int treeParent = 0;
if (source.parent > 0 && source.parent < index) {
const char* const parentLabel = logEntries[source.parent].label;
for (int candidate = treeEntries.Num() - 1; candidate > 0; --candidate) {
if (treeEntries[candidate].label == parentLabel
|| std::strcmp(treeEntries[candidate].label, parentLabel) == 0) {
treeParent = candidate;
break;
}
}
}
int destination = -1;
for (int candidate = 1; candidate < treeEntries.Num(); ++candidate) {
if (treeEntries[candidate].parent == treeParent
&& (treeEntries[candidate].label == source.label
|| std::strcmp(treeEntries[candidate].label, source.label) == 0)) {
destination = candidate;
break;
}
}
if (destination < 0) {
treeEntry_t* const entry = treeEntries.Alloc();
if (entry == nullptr) break;
destination = treeEntries.Num() - 1;
entry->label = source.label;
entry->depth = treeEntries[treeParent].depth + 1;
entry->parent = treeParent;
entry->firstChild = -1;
entry->nextChild = treeEntries[treeParent].firstChild;
treeEntries[treeParent].firstChild = destination;
}
treeEntry_t& entry = treeEntries[destination];
const std::int64_t adjusted = std::max<std::int64_t>(0,
source.totalTicks - adjustByTicks);
entry.totalTicks += adjusted;
++entry.numHits;
root->totalTicks += adjusted;
}
if (thresholdMS > 0.0f) {
// Keep the recovered table intact; the threshold only controls output.
}
return TicksToMilliseconds(root->totalTicks);
}
void idPLog::ShowUniqueEntries(const float thresholdMS,
const std::int64_t adjustByTicks) {
BuildLogData(thresholdMS, adjustByTicks);
for (int index = 1; index < treeEntries.Num(); ++index) {
const treeEntry_t& entry = treeEntries[index];
const float milliseconds = TicksToMilliseconds(entry.totalTicks);
if (milliseconds >= thresholdMS) {
std::printf("%-40s %6d %10.3f ms\n", entry.label,
entry.numHits, milliseconds);
}
}
}
void idPLog::ShowCallGraph(const float thresholdMS,
const std::int64_t adjustByTicks) {
BuildLogData(thresholdMS, adjustByTicks);
for (int index = 1; index < treeEntries.Num(); ++index) {
const treeEntry_t& entry = treeEntries[index];
const float milliseconds = TicksToMilliseconds(entry.totalTicks);
if (milliseconds >= thresholdMS) {
std::printf("%*s%s: %.3f ms (%d)\n", entry.depth * 2, "",
entry.label, milliseconds, entry.numHits);
}
}
}
idPLogScope::idPLogScope(idPLog& log, const std::uint64_t mask,
const char* label)
: logIndex(-1), pLog(&log) {
if (!log.IsGroupEnabled(mask)) return;
idPLog::logEntry_t* const entry = log.logEntries.Alloc();
if (entry == nullptr) return;
entry->label = label == nullptr ? "<unnamed>" : label;
entry->parent = log.lastEntry;
entry->totalTicks = ClockTicks();
logIndex = log.logEntries.Num() - 1;
log.lastEntry = logIndex;
}
idPLogScope::~idPLogScope() {
End();
}
void idPLogScope::End(std::int64_t* totalTicks) {
if (logIndex < 0 || pLog == nullptr) return;
idPLog::logEntry_t& entry = pLog->logEntries[logIndex];
entry.totalTicks = ClockTicks() - entry.totalTicks;
if (totalTicks != nullptr) *totalTicks = entry.totalTicks;
pLog->lastEntry = entry.parent;
logIndex = -1;
}