Trace GPS resolver during map queries

This commit is contained in:
2026-06-20 21:56:36 -05:00
parent 407c44b1ad
commit 1d77b8483f
3 changed files with 171 additions and 43 deletions
+94 -25
View File
@@ -160,6 +160,7 @@ constexpr uintptr_t kGpsQuerySubmitRva = 0x070A42C;
constexpr uintptr_t kGpsQueryDispatchRva = 0x070A570;
constexpr uintptr_t kGpsQueryResultFetchRva = 0x07094B8;
constexpr uintptr_t kGpsQueryStatusRva = 0x0AA5704;
constexpr uintptr_t kGpsMapRouteSubmitReturnRva = 0x08D20D4;
constexpr uintptr_t kGpsDispatchAdvanceRva = 0x08D17D8;
constexpr uintptr_t kGpsAsyncWorkerTickRva = 0x0126B28;
constexpr uintptr_t kGpsRouteJobStartRva = 0x0818928;
@@ -177,6 +178,8 @@ constexpr uintptr_t kGpsFilteredCostProviderVtableRva = 0x2AE60F8;
constexpr uintptr_t kGpsAuxMultiplierRva = 0x040BB00;
constexpr uintptr_t kGpsNodeMultiplierRva = 0x040BB40;
constexpr uintptr_t kGpsCostMultiplierTableRva = 0x3154D28;
constexpr uint64_t kGpsResolveTraceWindowMs = 5000;
constexpr uint32_t kGpsResolveSelectedRetLogLimit = 4096;
constexpr std::array<float, 7> kGpsPatchedCostMultipliers = {
12.0f, 7.0f, 2.5f, 0.0f, 3.0f, 2.0f, 1.25f,
};
@@ -271,6 +274,8 @@ uint32_t gGpsAuxMultiplierLogCount = 0;
uint32_t gGpsNodeMultiplierLogCount = 0;
uint32_t gGpsCostTablePatchLogCount = 0;
uint32_t gGpsProviderClassPatchLogCount = 0;
std::array<uint32_t, 4> gGpsResolveHandleRetLogCounts{};
uint64_t gGpsResolveTraceWindowUntilMs = 0;
std::array<uint32_t, 64> gGpsEdgeCostClassLogCounts{};
std::mutex gGpsCostLogMutex;
std::mutex gGpsAsyncLogMutex;
@@ -500,6 +505,18 @@ std::filesystem::path GetLogPath()
return std::filesystem::path(L"red4ext") / L"plugins" / L"EdgeWeightGPS" / L"EdgeWeightGPS.log";
}
uint64_t GetElapsedMs()
{
if (!gLogStartTick || !gLogFrequency)
{
return 0;
}
LARGE_INTEGER counter{};
QueryPerformanceCounter(&counter);
return static_cast<uint64_t>((counter.QuadPart - gLogStartTick) * 1000 / gLogFrequency);
}
void Log(std::string_view aMessage)
{
std::lock_guard lock(gLogMutex);
@@ -515,13 +532,7 @@ void Log(std::string_view aMessage)
return;
}
uint64_t elapsedMs = 0;
if (gLogStartTick && gLogFrequency)
{
LARGE_INTEGER counter{};
QueryPerformanceCounter(&counter);
elapsedMs = static_cast<uint64_t>((counter.QuadPart - gLogStartTick) * 1000 / gLogFrequency);
}
const auto elapsedMs = GetElapsedMs();
out << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S") << "." << std::setw(3) << std::setfill('0')
<< elapsedMs % 1000 << std::setfill(' ') << " +" << elapsedMs << "ms " << aMessage << "\n";
@@ -1072,20 +1083,38 @@ void AppendGpsPackedHandleSummary(std::ostringstream& aLine, const char* aPrefix
bool IsSelectedGpsResolveReturnRva(uintptr_t aReturnRva)
{
constexpr std::array<uintptr_t, 7> kSelectedReturnRvas = {
0x44D26C,
0x44D304,
0x44D99F,
0x44D9CD,
0x44DB50,
0x44DB88,
0x44DD92,
constexpr std::array<uintptr_t, 2> kSelectedReturnRvas = {
0x44CB9E,
0x44DD97,
};
return std::find(kSelectedReturnRvas.begin(), kSelectedReturnRvas.end(), aReturnRva) !=
kSelectedReturnRvas.end();
}
size_t GetGpsResolveReturnBucket(uintptr_t aReturnRva)
{
switch (aReturnRva)
{
case 0x44CB9E:
return 0;
case 0x44DD97:
return 1;
default:
return gGpsResolveHandleRetLogCounts.size();
}
}
void ExtendGpsResolveTraceWindow(uint64_t aNowMs)
{
std::lock_guard lock(gGpsCostLogMutex);
const auto untilMs = aNowMs + kGpsResolveTraceWindowMs;
if (untilMs > gGpsResolveTraceWindowUntilMs)
{
gGpsResolveTraceWindowUntilMs = untilMs;
}
}
void AppendGpsCostProviderSummary(std::ostringstream& aLine, const char* aPrefix, void* aProvider)
{
const auto prefix = std::string(aPrefix);
@@ -3411,6 +3440,16 @@ int32_t DetourGpsQuerySubmit(void* aManager, void* aQuery)
const auto callCount = gGpsQuerySubmitLogCount;
const auto shouldLog = callCount < kMaxCalls;
uint32_t queryKind = 0;
const auto hasQueryKind = aQuery && TryReadMemory(reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(aQuery) + 0xCC),
queryKind);
const auto shouldTraceResolver =
returnRva == kGpsMapRouteSubmitReturnRva && hasQueryKind && queryKind == 5;
if (shouldTraceResolver)
{
ExtendGpsResolveTraceWindow(GetElapsedMs());
}
if (shouldLog)
{
std::ostringstream line;
@@ -3423,6 +3462,10 @@ int32_t DetourGpsQuerySubmit(void* aManager, void* aQuery)
AppendUint32Field(line, "query_f0c", aQuery, 0x0C);
AppendUint32Field(line, "query_fcc", aQuery, 0xCC);
AppendVector4Field(line, "query_pos58", aQuery, 0x58);
if (shouldTraceResolver)
{
line << " resolveTraceWindowMs=" << kGpsResolveTraceWindowMs;
}
AppendReturnRva(line, returnAddress);
LogRed4ext(line.str());
++gGpsQuerySubmitLogCount;
@@ -3952,36 +3995,62 @@ int32_t DetourGpsResolveHandle(void* aGraph, uint64_t aHandle, void* aOutSegment
const auto hasSegment = aOutSegment && TryReadMemory(aOutSegment, segment);
const auto hasPoint = aOutPoint && TryReadMemory(aOutPoint, point);
const auto pointIndex = aHandle & 0xFFFFF;
uint32_t callCount = 0;
uint32_t returnCallCount = 0;
bool shouldLog = false;
{
std::lock_guard lock(gGpsCostLogMutex);
callCount = gGpsResolveHandleLogCount++;
const auto returnBucket = GetGpsResolveReturnBucket(returnRva);
if (returnBucket < gGpsResolveHandleRetLogCounts.size())
{
returnCallCount = gGpsResolveHandleRetLogCounts[returnBucket]++;
}
const auto inTraceWindow = GetElapsedMs() <= gGpsResolveTraceWindowUntilMs;
shouldLog = result >= 0 && hasSegment && hasPoint &&
(IsSelectedGpsResolveReturnRva(returnRva) || callCount < 64) && callCount < 512;
((IsSelectedGpsResolveReturnRva(returnRva) && inTraceWindow &&
returnCallCount < kGpsResolveSelectedRetLogLimit) ||
callCount < 32);
}
if (shouldLog)
{
auto* segmentMeta = ReadPointerField(segment, 0x08);
auto* pointArray = ReadPointerField(segment, 0x10);
auto* coordArray = ReadPointerField(segment, 0x18);
auto* aux20 = ReadPointerField(segment, 0x20);
auto* aux28 = ReadPointerField(segment, 0x28);
auto* aux30 = ReadPointerField(segment, 0x30);
auto* aux38 = ReadPointerField(segment, 0x38);
const auto auxPointOffset = pointIndex * 12;
auto* aux28Point = aux28 ? reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(aux28) + auxPointOffset) : nullptr;
std::ostringstream line;
line << "hook GPSResolveHandle 0x44e1a8 call=" << callCount << " value=0x" << std::hex
<< static_cast<uint32_t>(result) << " handle=0x" << aHandle << std::dec;
line << "hook GPSResolveHandle 0x44e1a8 call=" << callCount << " retCall=" << returnCallCount
<< " value=0x" << std::hex << static_cast<uint32_t>(result) << " handle=0x" << aHandle << std::dec;
AppendGpsPackedHandleSummary(line, "handle", aHandle);
AppendPointerWithRva(line, "graph", aGraph);
AppendPointerWithRva(line, "outSegment", aOutSegment);
AppendPointerWithRva(line, "outPoint", aOutPoint);
AppendPointerWithRva(line, "segment", segment);
AppendPointerWithRva(line, "point", point);
AppendPointerField(line, "segment_p08", segment, 0x08);
AppendPointerField(line, "segment_p10", segment, 0x10);
AppendPointerField(line, "segment_p18", segment, 0x18);
AppendPointerField(line, "segment_p20", segment, 0x20);
AppendPointerField(line, "segment_p28", segment, 0x28);
AppendPointerField(line, "segment_p30", segment, 0x30);
AppendPointerField(line, "segment_p38", segment, 0x38);
AppendPointerWithRva(line, "segment_p08", segmentMeta);
AppendPointerWithRva(line, "segment_p10", pointArray);
AppendPointerWithRva(line, "segment_p18", coordArray);
AppendPointerWithRva(line, "segment_p20", aux20);
AppendPointerWithRva(line, "segment_p28", aux28);
AppendPointerWithRva(line, "segment_p30", aux30);
AppendPointerWithRva(line, "segment_p38", aux38);
AppendGpsPointSummary(line, "pointSummary", point);
AppendBytesPointer(line, "segment_raw68", segment, 0x68);
AppendBytesPointer(line, "point_raw14", point, 0x14);
AppendBytesPointer(line, "segment_p08_raw30", segmentMeta, 0x30);
AppendBytesPointer(line, "segment_p20_raw40", aux20, 0x40);
AppendBytesPointer(line, "segment_p28_point_raw18", aux28Point, 0x18);
AppendBytesPointer(line, "segment_p30_raw40", aux30, 0x40);
AppendBytesPointer(line, "segment_p38_raw40", aux38, 0x40);
if (gActiveGpsRouteProducerCall != UINT32_MAX)
{
line << " routeProducerCall=" << gActiveGpsRouteProducerCall;