Skip to content

Commit

Permalink
Fix up engine profiling
Browse files Browse the repository at this point in the history
  • Loading branch information
dedmen committed Jan 23, 2019
1 parent 7b42006 commit 0295ef9
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 75 deletions.
2 changes: 2 additions & 0 deletions src/AdapterBrofiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ uint32_t getRandColor() {

AdapterBrofiler::AdapterBrofiler() {
type = AdapterType::Brofiler;

static Brofiler::ThreadScope mainThreadScope("Frame");
}


Expand Down
21 changes: 21 additions & 0 deletions src/AdapterTracy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ScopeInfoTracy final: public ScopeInfo {
class ScopeTempStorageTracy final : public ScopeTempStorage {
public:
std::unique_ptr<tracy::ScopedZone> zone;
std::thread::id origin;
};

AdapterTracy::AdapterTracy() {
Expand Down Expand Up @@ -50,15 +51,21 @@ std::shared_ptr<ScopeInfo> AdapterTracy::createScope(intercept::types::r_string
std::shared_ptr<ScopeTempStorage> AdapterTracy::enterScope(std::shared_ptr<ScopeInfo> scope) {
auto info = std::dynamic_pointer_cast<ScopeInfoTracy>(scope);
if (!info) return nullptr; //#TODO debugbreak? log error?
ensureReady();

auto ret = std::make_shared<ScopeTempStorageTracy>();
//ret->origin = std::this_thread::get_id();
ret->zone = std::make_unique<tracy::ScopedZone>(&info->info, true);
return ret;
}
void AdapterTracy::leaveScope(std::shared_ptr<ScopeTempStorage> tempStorage) {
auto tmpStorage = std::dynamic_pointer_cast<ScopeTempStorageTracy>(tempStorage);
if (!tmpStorage) return; //#TODO debugbreak? log error?


//if (tmpStorage->origin != std::this_thread::get_id())
// __debugbreak();

tmpStorage->zone.reset(); //zone destructor ends zone
}

Expand All @@ -77,3 +84,17 @@ std::shared_ptr<ScopeInfo> AdapterTracy::createScopeStatic(const char* name, con
info->info = tracy::SourceLocationData{nullptr, name, filename,fileline, 0};
return info;
}

bool AdapterTracy::isConnected() {
return tracy::s_profiler.IsConnected();
}

void AdapterTracy::ensureReady() {
if (tracy::s_token.ptr) return;

tracy::rpmalloc_thread_initialize();
tracy::s_token_detail = tracy::moodycamel::ProducerToken(tracy::s_queue);
tracy::s_token = tracy::ProducerWrapper{tracy::s_queue.get_explicit_producer(tracy::s_token_detail) };


}
4 changes: 2 additions & 2 deletions src/AdapterTracy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ class AdapterTracy final : public ProfilerAdapter
void setCounter(intercept::types::r_string name, float val) override;

std::shared_ptr<ScopeInfo> createScopeStatic(const char* name, const char* filename, uint32_t fileline);

bool isConnected();

private:

void ensureReady();
using scopeCacheKey = std::tuple<intercept::types::r_string, intercept::types::r_string,uint32_t>;

struct ScopeCacheFastEqual {
Expand Down
123 changes: 91 additions & 32 deletions src/EngineProfiling.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
#include "EngineProfiling.h"
#include "ProfilerAdapter.hpp"
#include "AdapterTracy.hpp"
#include <client.hpp>
#include <shared_mutex>

extern std::shared_ptr<ProfilerAdapter> GProfilerAdapter;
std::unordered_map<PCounter*, std::shared_ptr<ScopeTempStorage>> openScopes;


struct CounterHasher {
public:
size_t operator()(const std::pair<PCounter*, int>& key) const {
//intercept pairhash
size_t _hash = std::hash<PCounter*>()(key.first);
_hash ^= std::hash<uint64_t>()(key.second) + 0x9e3779b9 + (_hash << 6) + (_hash >> 2);
return _hash;
}
};

thread_local std::unique_ptr<std::unordered_map<std::pair<PCounter*, int>, std::shared_ptr<ScopeTempStorage>, CounterHasher>> openScopes;
thread_local bool openScopesInit;
std::unordered_map<PCounter*, std::shared_ptr<ScopeInfo>> scopeCache;
std::shared_mutex scopeCacheMtx;

extern "C" {
uintptr_t profEndJmpback;
Expand All @@ -13,42 +29,65 @@ extern "C" {

void shouldTime();
void doEnd();
void scopeCompleted();
}

bool PCounter::shouldTime() {
if (slot < 0) return false;
//if (!boss) return false;
//if (!boss->da) return false;
//if (!boss->stuffzi) return false;
//if (!boss->stuffzi[slot].enabled) return false;

if (GProfilerAdapter->getType() == AdapterType::Tracy) {
auto tracyProf = std::dynamic_pointer_cast<AdapterTracy>(GProfilerAdapter);

auto found = scopeCache.find(this);
if (found == scopeCache.end()) {
auto res = scopeCache.insert({this, tracyProf->createScopeStatic(name, cat, 0)});
found = res.first;
}

//make sure the add is not inside the scope
openScopes[this] = nullptr;
auto ins = openScopes.find(this);

ins->second = tracyProf->enterScope(found->second);
if (GProfilerAdapter->getType() != AdapterType::Tracy) return false;

auto tracyProf = std::dynamic_pointer_cast<AdapterTracy>(GProfilerAdapter);
if (!tracyProf->isConnected()) return false;


std::shared_lock lock(scopeCacheMtx);
auto found = scopeCache.find(this);
if (found == scopeCache.end()) {
lock.unlock();
std::unique_lock lockInternal(scopeCacheMtx);
auto res = scopeCache.insert({ this, tracyProf->createScopeStatic(name, cat, 0) });
lockInternal.unlock();//#TODO this is unsafe
lock.lock();
found = res.first;
}

if (!openScopes)
openScopes = std::make_unique<std::unordered_map<std::pair<PCounter*, int>, std::shared_ptr<ScopeTempStorage>, CounterHasher>>();

//make sure the add is not inside the scope
auto p = std::make_pair(this, slot);
auto ins = openScopes->insert_or_assign(p,nullptr);
auto tmp = tracyProf->enterScope(found->second);
//if (tmp)
ins.first->second = tmp;
//else
// openScopes.erase(p);




if (slot < 0) return false;
if (!boss) return false;
if (!boss->da) return false;
if (!boss->stuffzi) return false;
if (!boss->stuffzi[slot].enabled) return false;
return true;
}

void ScopeProf::doEnd() {
auto found = openScopes.find(counter);
if (found == openScopes.end()) return;
if (!openScopes || openScopes->empty() || !counter) return;
auto found = openScopes->find({ counter, counter->slot });
if (found == openScopes->end()) return;
GProfilerAdapter->leaveScope(found->second);
openScopes->erase(found);
}

void ArmaProf::scopeCompleted(int64_t start, int64_t end, intercept::types::r_string* stuff, PCounter* counter) {

if (!openScopes || openScopes->empty() || !counter) return;
auto found = openScopes->find({ counter, counter->slot });
if (found == openScopes->end()) return;
GProfilerAdapter->leaveScope(found->second);
openScopes.erase(found);
openScopes->erase(found);

}


Expand All @@ -57,28 +96,48 @@ HookManager::Pattern pat_doEnd{
"\x40\x53\x48\x83\xEC\x30\x80\x79\x11\x00\x48\x8B\xD9\x75\x09\x80\x3D\x00\x00\x00\x00\x00\x74\x38\x80\x3D\x00\x00\x00\x00\x00\x74\x0B\x0F\x31\x48\xC1\xE2\x20\x48\x0B\xC2\xEB\x05\xE8\x00\x00\x00\x00\x48\x8B\x13\x4C\x8B\xC0\x48\x8B\x43\x08\x4C\x8D\x4B\x18\x48\x8D\x0D\x00\x00\x00\x00\x48\x89\x44\x24\x00\xE8\x00\x00\x00\x00\x48\x8B\x53\x18\x48\x85\xD2\x74\x1A\xF0\xFF\x0A\x75\x0D\x48\x8B\x0D\x00\x00\x00\x00\x48\x8B\x01\xFF\x50\x18\x48\xC7\x43\x00\x00\x00\x00\x00\x48\x83\xC4\x30\x5B\xC3"
};

HookManager::Pattern pat_scopeCompleted{
"xxxx?xxxx?xxxxxxxxxxxx????xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx?xxxxxxxx????xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx????xxx????xxxxxx????xxxx?xx????xxx",
"\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x57\x41\x54\x41\x57\x48\x83\xEC\x20\x48\x8B\x81\x00\x00\x00\x00\x49\x8B\xF8\x4D\x8B\xE1\x48\x3B\xD0\x48\x8B\xD9\x48\x0F\x4C\xD0\x48\xC1\xF8\x04\x48\xC1\xFF\x04\x48\xC1\xFA\x04\x44\x8B\xFA\x2B\xFA\x44\x2B\xF8\x48\x8B\x44\x24\x00\x48\x63\x68\x18\x85\xED\x0F\x88\x00\x00\x00\x00\x8B\x41\x68\x66\x0F\x6E\xC7\x8B\xC8\xD1\xF9\x66\x0F\x6E\xD0\x8B\x43\x6C\x0F\x5B\xC0\x2B\xC1\x66\x0F\x6E\xC8\x0F\x5B\xD2\x42\x8D\x04\x3F\xF3\x0F\x59\xD0\x66\x0F\x6E\xC0\x0F\x5B\xC9\x0F\x5B\xC0\xF3\x0F\x59\xC8\x0F\x2F\xD1\x73\x48\x80\x3B\x00\x0F\x84\x00\x00\x00\x00\x4C\x69\xC5\x00\x00\x00\x00\x4C\x03\x43\x20\x0F\x84\x00\x00\x00\x00\x41\x0F\x0D\x48\x00\x41\xB9\x00\x00\x00\x00\x0F\x1F\x00"
};

HookManager::Pattern pat_shouldTime{
"xxxxxxxxxxxxxxxxxxxxxxxxxx????xxxxxxxxxxxxxxxxxxxx????xxxxxxx",
"\x48\x63\x41\x18\x85\xC0\x78\x32\x4C\x8B\x01\x33\xD2\x4D\x85\xC0\x74\x12\x41\x38\x10\x74\x0D\x48\x69\xC8\x00\x00\x00\x00\x49\x03\x48\x20\xEB\x03\x48\x8B\xCA\x48\x85\xC9\x74\x0A\x38\x51\x4A\x74\x05\xBA\x00\x00\x00\x00\x0F\xB6\xC2\xC3\x32\xC0\xC3"
};


EngineProfiling::EngineProfiling()
{
EngineProfiling::EngineProfiling() {
//order is important
//hooks.placeHook(hookTypes::doEnd, pat_doEnd, reinterpret_cast<uintptr_t>(doEnd), profEndJmpback, 1, true);
hooks.placeHook(hookTypes::scopeCompleted, pat_scopeCompleted, reinterpret_cast<uintptr_t>(scopeCompleted), profEndJmpback, 0);
hooks.placeHook(hookTypes::shouldTime, pat_shouldTime, reinterpret_cast<uintptr_t>(shouldTime), shouldTimeJmpback, 0);

hooks.placeHook(hookTypes::shouldTime, pat_shouldTime, reinterpret_cast<uintptr_t>(shouldTime), shouldTimeJmpback, 0);
hooks.placeHook(hookTypes::doEnd, pat_doEnd, reinterpret_cast<uintptr_t>(doEnd), profEndJmpback, 0);
auto found = hooks.findPattern(pat_doEnd, 0xD);


}

auto stuffByte = found + 0x2 + 2;
uint32_t offs = *((uint32_t*)stuffByte);
uint64_t addr = stuffByte + 4+1 + offs;
uint64_t base = addr - 0x121;

EngineProfiling::~EngineProfiling()
{
armaP = reinterpret_cast<ArmaProf*>(base);
armaP->blip.clear();
armaP->forceCapture = true;
armaP->capture = true;

//disable captureSlowFrame because it can set forceCapture to false
static auto stuff = intercept::client::host::register_sqf_command("diag_captureSlowFrame", "", [](uintptr_t, game_value_parameter) -> game_value
{
return {};
}, game_data_type::NOTHING, game_data_type::ARRAY);
}


EngineProfiling::~EngineProfiling() {}


extern "C" {


Expand Down
12 changes: 8 additions & 4 deletions src/EngineProfiling.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ class ArmaProf {
friend class PCounter;
public:

void scopeCompleted(int64_t start, int64_t end, intercept::types::r_string* stuff, void* stuff2);
__declspec(noinline) void scopeCompleted(int64_t start, int64_t end, intercept::types::r_string* stuff, PCounter* stuff2);


private:
public:
//This is engine stuff.
bool da,db,dc;
int dd,df;
Expand Down Expand Up @@ -78,7 +78,11 @@ class ArmaProf {
int blios;
intercept::types::r_string blip;
float blop;
bool asd,adasd;

float dummy, dummy2, dummy3;//no idea what dis is.. Stuff above is probably wrong somewhere

bool forceCapture;
bool capture;
int stuffiz;

int64_t framestart;
Expand All @@ -99,6 +103,6 @@ class EngineProfiling {
EngineProfiling();
~EngineProfiling();


ArmaProf* armaP;
HookManager hooks;
};
59 changes: 45 additions & 14 deletions src/HookManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ HookManager::HookManager() {
engineSize = static_cast<uintptr_t>(modInfo.SizeOfImage);
}

bool HookManager::placeHook(hookTypes type, const Pattern& pat, uintptr_t jmpTo, uintptr_t & jmpBackRef, uint8_t jmpBackOffset) {
bool HookManager::placeHook(hookTypes type, const Pattern& pat, uintptr_t jmpTo, uintptr_t & jmpBackRef, uint8_t jmpBackOffset, bool taintRax) {

auto found = findPattern(pat);
if (found == 0) {
Expand All @@ -21,7 +21,7 @@ bool HookManager::placeHook(hookTypes type, const Pattern& pat, uintptr_t jmpTo,
//#endif
return false;
}
jmpBackRef = placeHookTotalOffs(found, jmpTo) + jmpBackOffset;
jmpBackRef = placeHookTotalOffs(found, jmpTo, taintRax) + jmpBackOffset;
return true;
}

Expand All @@ -42,7 +42,7 @@ uintptr_t HookManager::placeHook(uintptr_t offset, uintptr_t jmpTo, uint8_t jmpB
return placeHookTotalOffs(totalOffset, jmpTo) + jmpBackOffset;
}

uintptr_t HookManager::placeHookTotalOffs(uintptr_t totalOffset, uintptr_t jmpTo) {
uintptr_t HookManager::placeHookTotalOffs(uintptr_t totalOffset, uintptr_t jmpTo, bool taintRax) {
DWORD dwVirtualProtectBackup;


Expand All @@ -53,19 +53,50 @@ uintptr_t HookManager::placeHookTotalOffs(uintptr_t totalOffset, uintptr_t jmpTo
64bit
FF 25 64bit relative
*/
#ifdef X64
#ifdef _WIN64
//auto distance = std::max(totalOffset, jmpTo) - std::min(totalOffset, jmpTo);
// if distance < 2GB (2147483648) we could use the 32bit relative jmp
VirtualProtect(reinterpret_cast<LPVOID>(totalOffset), 14u, 0x40u, &dwVirtualProtectBackup);
auto jmpInstr = reinterpret_cast<unsigned char*>(totalOffset);
auto addrOffs = reinterpret_cast<uint32_t*>(totalOffset + 1);
*jmpInstr = 0x68; //push DWORD
*addrOffs = static_cast<uint32_t>(jmpTo) /*- totalOffset - 6*/;//offset
*reinterpret_cast<uint32_t*>(totalOffset + 5) = 0x042444C7; //MOV [RSP+4],
*reinterpret_cast<uint32_t*>(totalOffset + 9) = static_cast<uint64_t>(jmpTo) >> 32;//DWORD
*reinterpret_cast<unsigned char*>(totalOffset + 13) = 0xc3;//ret
VirtualProtect(reinterpret_cast<LPVOID>(totalOffset), 14u, dwVirtualProtectBackup, &dwVirtualProtectBackup);
return totalOffset + 14;


if (taintRax) {
//This is shorter, but messes up RAX

/*
push rax; //to restore it inside out target
moveabs rax, address
push rax
ret
*/

VirtualProtect(reinterpret_cast<LPVOID>(totalOffset), 12u, 0x40u, &dwVirtualProtectBackup);
auto memberRax = reinterpret_cast<unsigned char*>(totalOffset);
auto jmpInstr1 = reinterpret_cast<unsigned char*>(totalOffset+1);
auto jmpInstr2 = reinterpret_cast<unsigned char*>(totalOffset+2);
auto addrOffs = reinterpret_cast<uint64_t*>(totalOffset + 3);
*memberRax = 0x50; //push rax
*jmpInstr1 = 0x48; //moveabs
*jmpInstr2 = 0xb8; //into rax
*addrOffs = static_cast<uint64_t>(jmpTo) /*- totalOffset - 6*/;//offset

auto pushInstr = reinterpret_cast<unsigned char*>(totalOffset + 11);
*pushInstr = 0x50; //push rax
*reinterpret_cast<unsigned char*>(totalOffset + 12) = 0xc3;//ret
VirtualProtect(reinterpret_cast<LPVOID>(totalOffset), 12u, dwVirtualProtectBackup, &dwVirtualProtectBackup);
return totalOffset + 12;
} else {
VirtualProtect(reinterpret_cast<LPVOID>(totalOffset), 14u, 0x40u, &dwVirtualProtectBackup);
auto jmpInstr = reinterpret_cast<unsigned char*>(totalOffset);
auto addrOffs = reinterpret_cast<uint32_t*>(totalOffset + 1);
*jmpInstr = 0x68; //push DWORD
*addrOffs = static_cast<uint32_t>(jmpTo) /*- totalOffset - 6*/;//offset
*reinterpret_cast<uint32_t*>(totalOffset + 5) = 0x042444C7; //MOV [RSP+4],
*reinterpret_cast<uint32_t*>(totalOffset + 9) = static_cast<uint64_t>(jmpTo) >> 32;//DWORD
*reinterpret_cast<unsigned char*>(totalOffset + 13) = 0xc3;//ret
VirtualProtect(reinterpret_cast<LPVOID>(totalOffset), 14u, dwVirtualProtectBackup, &dwVirtualProtectBackup);
return totalOffset + 14;
}


#else
VirtualProtect(reinterpret_cast<LPVOID>(totalOffset), 5u, 0x40u, &dwVirtualProtectBackup);
auto jmpInstr = reinterpret_cast<unsigned char *>(totalOffset);
Expand Down
5 changes: 3 additions & 2 deletions src/HookManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
enum class hookTypes {
shouldTime, //FrameEnd/FrameStart
doEnd,
scopeCompleted,
End
};

Expand All @@ -30,10 +31,10 @@ class HookManager {//Implementation in dllmain


HookManager();
bool placeHook(hookTypes, const Pattern& pat, uintptr_t jmpTo, uintptr_t& jmpBackRef, uint8_t jmpBackOffset = 0);
bool placeHook(hookTypes, const Pattern& pat, uintptr_t jmpTo, uintptr_t& jmpBackRef, uint8_t jmpBackOffset = 0, bool taintRax = false);
bool placeHook(hookTypes, const Pattern& pat, uintptr_t jmpTo);
uintptr_t placeHook(uintptr_t offset, uintptr_t jmpTo, uint8_t jmpBackOffset = 0);
uintptr_t placeHookTotalOffs(uintptr_t offset, uintptr_t jmpTo);
uintptr_t placeHookTotalOffs(uintptr_t offset, uintptr_t jmpTo, bool taintRax = false);
bool MatchPattern(uintptr_t addr, const char* pattern, const char* mask);
uintptr_t findPattern(const char* pattern, const char* mask, uintptr_t offset = 0);
uintptr_t findPattern(const Pattern& pat, uintptr_t offset = 0);
Expand Down
Loading

0 comments on commit 0295ef9

Please sign in to comment.