Reputation: 116
I am using MS Visual Studio 2022 and the MS Detours library to detour some functions. The detouring works fine, but I would like to collect some statistics per function, in a scaleable, performant and maintainable way.
Here's a simple functional example, hacked down for brevity, hopefully without mistakes added:
struct funcRef {
const char* funcName;
std::atomic<uint64_t> numCalls;
};
extern struct funcRef funcStats[];
#define FUNC_CALLED(name, index) \
const int funcIndex = index; \
funcStats[funcIndex].funcName = name; \
std::atomic_fetch_add(&funcStats[funcIndex].numCalls, 1);
typedef void (WINAPI *KERNSIFP)(LPSYSTEM_INFO lpSystemInfo);
static KERNSIFP TrueGetNativeSystemInfo = NULL;
void WINAPI MyGetNativeSystemInfo(LPSYSTEM_INFO lpSystemInfo)
{
FUNC_CALLED(__FUNCTION__, __COUNTER__)
return TrueGetNativeSystemInfo(lpSystemInfo);
}
const int NUM_DETOURS = __COUNTER__;
struct funcPair {
void *orig;
void *fake;
};
struct funcRef funcStats[NUM_DETOURS];
struct funcPair detourFuncs[NUM_DETOURS] = {
{(void *)&TrueGetNativeSystemInfo, MyGetNativeSystemInfo},
};
void LoadDetours()
{
int i;
DPRINTF(DLOG_INFO, "Detouring functions.\n");
kern32Lib = LoadLibraryW(L"kernel32.dll");
if (!kern32Lib)
return;
TrueGetNativeSystemInfo = (KERNSIFP) GetProcAddress(kern32Lib, "GetNativeSystemInfo");
for (i = 0; i < NUM_DETOURS; i++)
if (detourFuncs[i].orig)
DetourFunction(detourFuncs[i].orig, detourFuncs[i].fake);
}
void UnloadDetours()
{
int i;
DPRINTF(DLOG_INFO, "Restoring functions.\n");
for (i = 0; i < NUM_DETOURS; i++)
if (detourFuncs[i].orig)
RestoreFunction(detourFuncs[i].orig, detourFuncs[i].fake);
FreeLibrary(kern32Lib);
}
As can be seen, in this very minimal example, I am keeping tabs of the function name, and increment a counter each time it is called. I'm relying on the preprocessor to identify the function in the global statistics structure array based on its location in the code using __COUNTER__.
This works fine, but it's a little touchy. To add or remove a function I need to touch quite a few different lines of code, it's pretty easy to make a mistake. I would also like to modularize this as I may have hundreds of detoured functions. There are a few ways I can think of, most boil down to removing that __COUNTER__ and instead having what amounts to a "this" pointer passed in to my function (or perhaps embedded on the stack?) I'm not quite sure what the best approach is.
Is there a better way to do what I'm doing?
Upvotes: 1
Views: 72