是否有可以轻松记录和重放API调用结果的库?

Bil*_*eal 8 c++ unit-testing

我正在编写各种调用相对复杂的Win32 API函数的东西.这是一个例子:

//Encapsulates calling NtQuerySystemInformation buffer management.
WindowsApi::AutoArray NtDll::NtQuerySystemInformation( 
    SystemInformationClass toGet ) const
{
    AutoArray result;
    ULONG allocationSize = 1024;
    ULONG previousSize;
    NTSTATUS errorCheck;
    do 
    {
        previousSize = allocationSize;
        result.Allocate(allocationSize);
        errorCheck = WinQuerySystemInformation(toGet, 
            result.GetAs<void>(), allocationSize, &allocationSize);
        if (allocationSize <= previousSize)
            allocationSize = previousSize * 2;
    } while (errorCheck == 0xC0000004L);
    if (errorCheck != 0)
    {
        THROW_MANUAL_WINDOWS_ERROR(WinRtlNtStatusToDosError(errorCheck));
    }
    return result;
}
//Client of the above.
ProcessSnapshot::ProcessSnapshot()
{
    using Dll::NtDll;
    NtDll ntdll;
    AutoArray systemInfoBuffer = ntdll.NtQuerySystemInformation(
        NtDll::SystemProcessInformation);
    BYTE * currentPtr = systemInfoBuffer.GetAs<BYTE>();
    //Loop through the results, creating Process objects.
    SYSTEM_PROCESSES * asSysInfo;
    do
    {
        // Loop book keeping
        asSysInfo = reinterpret_cast<SYSTEM_PROCESSES *>(currentPtr);
        currentPtr += asSysInfo->NextEntryDelta;

        //Create the process for the current iteration and fill it with data.
        std::auto_ptr<ProcImpl> currentProc(ProcFactory(
            static_cast<unsigned __int32>(asSysInfo->ProcessId), this));
        NormalProcess* nptr = dynamic_cast<NormalProcess*>(currentProc.get());
        if (nptr)
        {
            nptr->SetProcessName(asSysInfo->ProcessName);
        }
        // Populate process threads
        for(ULONG idx = 0; idx < asSysInfo->ThreadCount; ++idx)
        {
            SYSTEM_THREADS& sysThread = asSysInfo->Threads[idx];
            Thread thread(
                currentProc.get(),
                static_cast<unsigned __int32>(sysThread.ClientId.UniqueThread),
                sysThread.StartAddress);
            currentProc->AddThread(thread);
        }
        processes.push_back(currentProc);
    } while(asSysInfo->NextEntryDelta != 0);
}
Run Code Online (Sandbox Code Playgroud)

我的问题在于模拟NtDll::NtQuerySystemInformation方法 - 即,返回的数据结构很复杂(嗯,这里它实际上相对简单,但它可能很复杂),编写一个像API调用一样构建数据结构的测试可以采取编写使用API​​的代码的时间长度的5-6倍.

我想做的是调用API,并以某种方式记录它,以便我可以将该记录值返回给测试中的代码,而无需实际调用API.返回的结构不能简单地进行memcpy,因为它们通常包含内部指针(指向同一缓冲区中其他位置的指针).有问题的库需要检查这些类型的东西,并能够在重放时将指针值恢复到类似的缓冲区.(即检查每个指针大小的值,如果它可以被解释为缓冲区内的指针,将其更改为偏移量,并记住在重放时将其更改回指针 - 此处可以接受误报率)

有没有什么可以做这样的事情?

Meh*_*dad 2

手工实现真的那么难吗?

#include <stddef.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

typedef char BYTE;

//Note: Buffer is MODIFIED after this call (hence why it's not const)! Don't re-use it!
void saveBuffer(FILE* stream, BYTE* buffer, size_t bufferSize)
{
    assert(bufferSize % sizeof(void*) == 0);
    fwrite(&bufferSize, sizeof(bufferSize), 1, stream);
    for (size_t i = 0; i < bufferSize - sizeof(void*) + 1; i += sizeof(void*))
    {
        void** pAsPointer = (void**)&buffer[i];
        if (*pAsPointer >= buffer && *pAsPointer < buffer + bufferSize)
        {
            fwrite(&i, sizeof(i), 1, stream);
            *pAsPointer = pAsPointer;
        }
    }
    void* null = NULL;
    fwrite(&null, sizeof(null), 1, stream); //Null-terminator
    fwrite(buffer, 1, bufferSize, stream);
}


size_t loadBuffer(FILE* stream, BYTE* buffer, size_t bufferCapacity) //Buffer size is in stream
{
    size_t bufferSize;
    fread(&bufferSize, sizeof(bufferSize), 1, stream);
    if (bufferSize >= bufferCapacity)
    {
        memset(buffer, 0, bufferSize);
        for (;;)
        {
            size_t pointerOffset;
            fread(&pointerOffset, sizeof(pointerOffset), 1, stream);
            if (pointerOffset != 0)
            { *(size_t*)&buffer[pointerOffset] = (size_t)buffer + pointerOffset; }
            else { break; }
        }
        for (size_t i = 0; i < bufferSize; i += sizeof(void*))
        {
            if (*(void**)&buffer[i] == NULL)
            { fread(&buffer[i], sizeof(size_t), 1, stream); }
        }
    }
    return bufferSize;
}
Run Code Online (Sandbox Code Playgroud)

(抱歉,我还没有测试过它,但它应该非常接近工作了。)

唯一的麻烦是假设所有可能是指针的值实际上都是指针,但除此之外,它似乎很简单。

  • 避免重新发明轮子固然很好,但如果比实际找到轮子花费的时间更少,您可能需要考虑它!:) (4认同)