Собственный Профилировщик в C ++ портит возвращаемое значение с плавающей точкой из функции - PullRequest
0 голосов
/ 03 ноября 2019

Я написал профилировщик c ++ после этой статьи: https://www.codeproject.com/Articles/800172/A-Simple-Cplusplus-Profiler-on-x?msg=4935639#xx4935639xx. Теперь я не копировал его полностью, просто следовал общей структуре. Этот профилировщик является DLL, который используется в моем другом проекте. В этом проекте (в качестве тестового примера) у меня есть основная функция и тестовая функция:

float test(float in) { return in; }
int main() {
    float s = test(255);
    std::cout << "S: " << s << std::endl;
}

Проблема в том, что тест возвращает 0 для типов данных float и double (если я заменяю каждый 'float)'with' double), но хорошо с int, long и bool. Вот код, который использует профилировщик

// this gets called from the DllMain function in the profiler
void create(void* pAddress) {
    ::InitializeCriticalSection(&m_section);
    char moduleName[MAX_PATH];
    MEMORY_BASIC_INFORMATION mbi;

    VirtualQuery((void*)pAddress, &mbi, sizeof(mbi));
    GetModuleFileNameA((HMODULE)mbi.AllocationBase, moduleName, MAX_PATH);
    SymInitialize(GetCurrentProcess(), moduleName, TRUE);
    m_baseAddr = SymLoadModule64(GetCurrentProcess(), NULL, (PCSTR)moduleName, NULL, (DWORD)mbi.AllocationBase, (DWORD) 0);
    SymSetOptions(SymGetOptions() & ~SYMOPT_UNDNAME);
    m_active = true;
    m_profiler = this;

    LARGE_INTEGER li;
    ::QueryPerformanceFrequency(&li);
    m_tickFrequency = li.QuadPart / 1000.0;
    m_lastTime = getCurrentCycle();

    std::cout << "Started in thread " << std::this_thread::get_id() << std::endl;
}

// this finds the name of the function that was exited. Used both for entering and exiting (entering is disabled for testing)
void findFunction(void* pa, char*& funcName) {
    DWORD64 symDisplacement = 0;
    char undName[1024];
    if (m_baseAddr) {
        TCHAR buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
// if i comment out everything after this line, the returning works for floats too. Like this, it only works for ints and longs
        memset(&buffer, 0, sizeof(buffer));
        PSYMBOL_INFO symbolInfo = (PSYMBOL_INFO)buffer;
        symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
        symbolInfo->MaxNameLen = MAX_SYM_NAME;

        //BOOL result = SymFromAddr(GetCurrentProcess(), (DWORD64)pa, &symDisplacement, symbolInfo);
        BOOL result = true;
        if (!result) {
            std::cout << "Could not get address: " << GetLastError() << std::endl;
        };

        // here Im checking whether this function gets called from differents threads, because apparently UnDecorateSymbolName can act weird when called from different threads, but the thread is always the same (the one that the create function was called from too)
        std::thread::id id = std::this_thread::get_id();
        if (id != threadId) {
            std::cout << "Changed thread: " << id << " from " << threadId << std::endl;
            threadId = id;
        }


        if (result) {
            //std::cout << "Calling from thread " << std::this_thread::get_id() << std::endl;
            if (UnDecorateSymbolName(symbolInfo->Name, undName, sizeof(undName), UNDNAME_NO_MS_KEYWORDS |
                                     UNDNAME_NO_ACCESS_SPECIFIERS |
                                     UNDNAME_NO_FUNCTION_RETURNS |
                                     UNDNAME_NO_ALLOCATION_MODEL |
                                     UNDNAME_NO_ALLOCATION_LANGUAGE |
                                     UNDNAME_NO_ARGUMENTS |
                                     UNDNAME_NO_SPECIAL_SYMS |
                                     UNDNAME_NO_MEMBER_TYPE)) {
                //std::cout << "Name: " << undName << std::endl;
                if (!isStdFunction(undName)) {
                    strcpy_s(undName, symbolInfo->Name);
                    funcName = new char[strlen(undName) + 2];
                    //*funcName = std::string(undName, strlen(undName));
                    strcpy_s(funcName, strlen(undName) + 1, undName);
                }
            }
        }
    }
}

// this is called from an assembler file in _pexit. Its working so far
void exitFunction(void* pa) {
    char* callee = nullptr;
    //const char* callee = "Hallo";
    findFunction(pa, callee); 

    __int64 endtime = getCurrentCycle();
    __int64 runtime = endtime - m_lastTime; 
    ::EnterCriticalSection(&m_section);

    if (callee) {
        /* leave out for now
        std::vector<HeFunctionData*>& stack = getStack();
        HeFunctionData* data = stack[stack.size() - 1];
        data->cycles += runtime;
        stack.pop_back();
        */
    } else {
#if HE_PROFILER_TRACK_STD
        // is std function
        __int64 now = getCurrentCycle();
        __int64 runtime = now - m_lastTime;
        m_data[GetCurrentThreadId()]["std"].cycles += runtime;
        m_data[GetCurrentThreadId()]["std"].calls++;
#endif
    }

    if (HE_PROFILER_TRACK_STD || callee) {
        m_totalTime += runtime;
        m_lastTime = getCurrentCycle();
    }

    ::LeaveCriticalSection(&m_section);
    delete[] callee;
}

Теперь я отладил его до такой степени, что если я прокомментирую все после TCHAR buffer[...] в findFunction out, он также будет работать. Таким образом, memset должен как-то испортить возвращаемое значение test () в exe, если тип данных - float или double. Я использую Visual Studio 19 и DLL и EXE находятся в режиме отладки. Любые идеи, почему это происходит?

Заранее спасибо, нереально.

Редактировать: Как предложено в комментариях, я заменил memset своей собственной "версией" этого, чтобы установить массив в ноль,Теперь он работает нормально до функции UnDecorateSymbolName (если я ее опущу, возвращаемое значение будет правильным). Это говорит о том, что повреждение памяти может произойти, если функция вызывается из разных потоков, но этого никогда не происходит. Так почему же UnDecorateSymbolName ведет себя так? Есть ли альтернативы?

...