/// Hook the load library functions
void HookLoadLibrary()
{
    LogTrace(traceENTER, "");

    AMDT::BeginHook();

    LONG error = AMDT::HookAPICall(&(PVOID&)Real_LoadLibraryA, Mine_LoadLibraryA);
    PsAssert(error == NO_ERROR);

    error = AMDT::HookAPICall(&(PVOID&)Real_LoadLibraryExA, Mine_LoadLibraryExA);
    PsAssert(error == NO_ERROR);

    error = AMDT::HookAPICall(&(PVOID&)Real_LoadLibraryW, Mine_LoadLibraryW);
    PsAssert(error == NO_ERROR);

    error = AMDT::HookAPICall(&(PVOID&)Real_LoadLibraryExW, Mine_LoadLibraryExW);
    PsAssert(error == NO_ERROR);

    error = AMDT::HookAPICall(&(PVOID&)Real_FreeLibrary, Mine_FreeLibrary);
    PsAssert(error == NO_ERROR);

    if (AMDT::EndHook() != NO_ERROR)
    {
        Log(logERROR, "HookLoadLibrary() failed\n");
    }

    // Load the "dxgi.dll" at the beginning as we missed the dxgi loading sometimes.
    // TODO: Fix this: This function is called from DllMain(), and according to the
    // Microsoft documentation, LoadLibrary() should not be called from DllMain()
    LoadLibraryA("dxgi.dll");

    LogTrace(traceEXIT, "");
    return;
}
//-----------------------------------------------------------------------------
/// Release global shared memory region so it can be accessed by other processes.
//-----------------------------------------------------------------------------
void SharedGlobal::Unlock(void)
{
    PsAssert(this != NULL);
    PsAssert(m_Mutex != NULL);

    m_Mutex->unlock();
}
//-----------------------------------------------------------
// Stops capturing D3DPerf markers
//-----------------------------------------------------------
LONG CAPTURE_D3DPerfMarkers_Unhook()
{
    LogTrace(traceENTER, "");

    ScopeLock t(&s_mtx);

    if (dwAttached <= 0)
    {
        Log(logERROR, "Trying to Detach ID3D10Device twice!\n");
        return -1;
    }

    s_pCaptureLayer = NULL;

    LONG error;
    AMDT::BeginHook();

    if (Real_D3DPERF_BeginEvent != NULL)
    {
        error = AMDT::UnhookAPICall(&(PVOID&)Real_D3DPERF_BeginEvent, Mine_D3DPERF_BeginEvent);
        PsAssert(error == NO_ERROR);
    }

    if (Real_D3DPERF_EndEvent != NULL)
    {
        error = AMDT::UnhookAPICall(&(PVOID&)Real_D3DPERF_EndEvent, Mine_D3DPERF_EndEvent);
        PsAssert(error == NO_ERROR);
    }

    if (Real_D3DPERF_SetMarker != NULL)
    {
        error = AMDT::UnhookAPICall(&(PVOID&)Real_D3DPERF_SetMarker, Mine_D3DPERF_SetMarker);
        PsAssert(error == NO_ERROR);
    }

    if (Real_D3DPERF_SetRegion != NULL)
    {
        error = AMDT::UnhookAPICall(&(PVOID&)Real_D3DPERF_SetRegion, Mine_D3DPERF_SetRegion);
        PsAssert(error == NO_ERROR);
    }

    error = AMDT::EndHook();

    if (error != NO_ERROR)
    {
        Log(logERROR, "failed\n");
    }
    else
    {
        dwAttached--;
    }

    LogTrace(traceEXIT, "");

    return error;
}
//-----------------------------------------------------------------------------
/// Lock global shared memory region for exclusive access.
/// \return true if the shared memory could be locked; false otherwise
//-----------------------------------------------------------------------------
bool SharedGlobal::Lock(void)
{
    PsAssert(this != NULL);
    PsAssert(m_Mutex != NULL);

    if (m_Mutex->lock() == false)
    {
        Log(logERROR, "Error occurred while waiting for Mutex :%d\n", osGetLastSystemError());
        return false;
    }

    return true;
}
//--------------------------------------------------------------------------
/// Returns the values of editable commands
/// \return XML containing the values of editable commands
//--------------------------------------------------------------------------
string CommandProcessor::GetEditableCommandValues()
{
    stringstream strOut;

    // give derived classes a chance to update / add settings.
    strOut << GetDerivedSettings();

    // now add this processors editable commands (Settings).
    CommandList::const_iterator objIter;

    for (objIter = m_Commands.begin(); objIter < m_Commands.end(); ++objIter)
    {
        CommandResponse* pObj = *objIter;
        PsAssert(pObj != NULL);

        if (pObj->GetEditableContent() != NOT_EDITABLE)
        {
            strOut << "<";
            strOut << pObj->GetTagName();
            strOut << " name='";
            strOut << pObj->GetDisplayName();
            strOut << "' url='";
            strOut << GetFullPathString().asCharArray();
            strOut << "'>";
            strOut << pObj->GetEditableContentValue();
            strOut << "</";
            strOut << pObj->GetTagName();
            strOut << ">";
        }
    }

    return strOut.str();
}
Example #6
0
////////////////////////////////////////////////////////////////////////
/// Writes a message to the console
/// \param msg The message to display.
////////////////////////////////////////////////////////////////////////
void ClientRequestThread::OutputScreenMessage(const char* msg)
{
    PsAssert(s_output_criticalSection != NULL);
    s_output_criticalSection->enter();
    LogConsole(logMESSAGE, "%s\n", msg);
    s_output_criticalSection->leave();
}
Example #7
0
////////////////////////////////////////////////////////////////////////
/// Writes an error to the console. It clears the error before exiting.
/// \param errmsg The message to display.
////////////////////////////////////////////////////////////////////////
void ClientRequestThread::OutputScreenError(const char* errmsg)
{
    PsAssert(s_output_criticalSection != NULL);
    s_output_criticalSection->enter();
    LogConsole(logERROR, "%s - %i\n", errmsg, NetSocket::LastError());
    s_output_criticalSection->leave();
}
void DestroyResponse(CommunicationID& rRequestID, Response** ppResponse)
{
    PsAssert(ppResponse != NULL);

    if (*ppResponse == NULL)
    {
        return;
    }

    // protect the maps from being changed by other threads using the mutex
    ScopeLock lock(s_mutex);

    // remove from streaming map
    if ((*ppResponse)->m_bStreamingEnabled)
    {
        ResponseMap::iterator iter = g_streamingResponseMap.find(rRequestID);

        if (iter != g_streamingResponseMap.end())
        {
            g_streamingResponseMap.erase(iter);
        }
    }

    SAFE_DELETE(*ppResponse);
    rRequestID = 0;
}
//-----------------------------------------------------------------------------
/// Set a path in the global data block. All paths are assumed to be PS_MAX_PATH size.
///
/// \param offset The offset from the start of the structure to the path of interest.
///    It is calculated automatically at compile time using the offsetof functionality
///    through the SG_SET_PATH macro
/// \param path The path to set at the specified offset
/// \return true if the path could be set; false otherwise
//-----------------------------------------------------------------------------
bool SharedGlobal::SetPath(size_t offset, const char* path)
{
    PsAssert(this != NULL);
    PsAssert(path != NULL);
    PsAssert(m_MapFile != NULL);
    PsAssert(m_MapFile->Get() != NULL);

    if (Lock())
    {
        strcpy_s(&((char*)m_MapFile->Get())[offset], PS_MAX_PATH, path);

        Unlock();
        return (true);
    }

    return (false);
}
//--------------------------------------------------------------------------
/// Registers the specified CommandProcessor
/// \param pTagName The XML tag name of the processor.
/// \param pDisplayName The name to display in the client for this command
/// \param pID The ID that should target a command to the CommandProcessor
/// \param pTitlePrefix A customizable prefix that is prepended to the display name
/// \param eDisplayMode Indicates whether or not to display the command in the client
/// \param rComProc The commandProcessor that should be targeted when a
///     command with the supplied ID is received
//--------------------------------------------------------------------------
void CommandProcessor::AddProcessor(const char* pTagName,
                                    const char* pDisplayName,
                                    const char* pID,
                                    const char* pTitlePrefix,
                                    UIDisplayMode eDisplayMode,
                                    CommandProcessor& rComProc)
{
    PsAssert(pTagName != NULL);
    PsAssert(pDisplayName != NULL);
    PsAssert(pID != NULL);

    rComProc.SetTagName(pTagName);
    rComProc.SetID(pID);
    rComProc.SetDisplayName(pDisplayName);
    rComProc.SetTitlePrefix(pTitlePrefix);
    rComProc.SetUIDisplayMode(eDisplayMode);
    rComProc.SetParent(this);
    m_Processors.push_back(&rComProc);
}
//-----------------------------------------------------------------------------
/// Verify, align, and store the new profiler results.
/// \param pQueue The Queue used to collect the results to verify.
/// \param results The vector of profiler results to verify.
/// \param pTimestampPair A pair of calibration timestamps used to align CPU and GPU timestamps.
/// \param threadID The ThreadId that the results are collected with.
/// \param frameStartTime The start time of the frame as collected on the CPU.
//-----------------------------------------------------------------------------
void VktFrameProfilerLayer::VerifyAlignAndStoreResults(
    VktWrappedQueue*             pQueue,
    std::vector<ProfilerResult>& results,
    CalibrationTimestampPair*    pTimestampPair,
    UINT32                       threadID,
    GPS_TIMESTAMP                frameStartTime)
{
#if MANUAL_TIMESTAMP_CALIBRATION == 0
    UNREFERENCED_PARAMETER(pTimestampPair);
    UNREFERENCED_PARAMETER(frameStartTime);
#endif

    SampleIdToProfilerResultMap* pResultMap = FindOrCreateProfilerResultsMap(pQueue, threadID);
    PsAssert(pResultMap != nullptr);

    if (pResultMap != nullptr)
    {
        for (size_t resultIndex = 0; resultIndex < results.size(); ++resultIndex)
        {
            ProfilerResult& currentResult = results[resultIndex];

            const UINT64 sampleId = currentResult.measurementInfo.idInfo.sampleId;

            // Verify that the timestamps retrieved from the profiler appear to be valid.
            if (ValidateProfilerResult(currentResult) == true)
            {
#if MANUAL_TIMESTAMP_CALIBRATION
                // Now attempt to align the profiled GPU timestamps with the traced API calls on the CPU.
                bool bAlignedSuccessfully = AlignProfilerResultWithCPUTimeline(currentResult, pTimestampPair, frameStartTime);
#else
                bool bAlignedSuccessfully = true;
#endif

                // @TODO - determine if this is what we want to do
                // Make zero-duration case equal to 1 clock cycle
                if (currentResult.timestampResult.rawClocks.start == currentResult.timestampResult.rawClocks.end)
                {
                    currentResult.timestampResult.rawClocks.end++;
                }

                if (bAlignedSuccessfully)
                {
                    // Store the final adjusted profiler results if they're valid.
                    ProfilerResult* pNewResult = new ProfilerResult;
                    CopyProfilerResult(pNewResult, &currentResult);
                    (*pResultMap)[sampleId] = pNewResult;
                }
                else
                {
                    Log(logERROR, "Command with SampleId %d failed to align with CPU timeline.\n", sampleId);
                }
            }
        }
    }
}
//--------------------------------------------------------------------------
/// Registers the specified CommandObject
/// \param eType The type of content that this command should be responded with
/// \param pTagName The name to assign to nodes in XML in the CommandTree
/// \param pDisplayName The name to give the CommandResponse object
/// \param pURL The URL that should activate the CommandResponse object
/// \param eDisplayMode Indicates whether or not to display the command in the client
/// \param eIncludeFlag Indicates whether or not to include the command in the CommandTree
/// \param rComObj The CommandResponse object that will be activated when
///      the supplied URL is received as a command
//--------------------------------------------------------------------------
void CommandProcessor::AddCommand(ContentType       eType,
                                  const char*        pTagName,
                                  const char*        pDisplayName,
                                  const char*        pURL,
                                  UIDisplayMode     eDisplayMode,
                                  TreeInclude       eIncludeFlag,
                                  CommandResponse&   rComObj)
{
    PsAssert(pTagName != NULL);
    PsAssert(pDisplayName != NULL);
    PsAssert(pURL != NULL);

    rComObj.SetTagName(pTagName);
    rComObj.SetDisplayName(pDisplayName);
    rComObj.SetURL(pURL);
    rComObj.SetContentType(eType);
    rComObj.SetUIDisplayMode(eDisplayMode);
    rComObj.SetTreeInclude(eIncludeFlag);
    m_Commands.push_back(&rComObj);
}
//-----------------------------------------------------------------------------
/// Get a path from the global data block. All paths are assumed to be PS_MAX_PATH size.
///
/// \param offset The offset from the start of the structure to the path of interest.
/// It is calculated automatically at compile time using the offsetof functionality
/// through the SG_GET_PATH macro
///
/// \return A shadow copy of the path if maintained in the class. This allows us to directly pass back
/// a char * without concerns about locking/unlocking the shared memory region.
//-----------------------------------------------------------------------------
const char* SharedGlobal::GetPath(size_t offset)
{
    PsAssert(this != NULL);
    PsAssert(m_MapFile != NULL);
    PsAssert(m_MapFile->Get() != NULL);

    char* src = & ((char*)m_MapFile->Get())[offset];
    char* dst = & ((char*) & m_Shadow)[offset];

    size_t nSize = sizeof((&m_Shadow)[offset]);

    if (Lock())
    {
        // copy shared memory copy of string into local shadow copy
        memcpy_s(dst, nSize, src, PS_MAX_PATH);
        Unlock();
        return (dst);
    }

    return (NULL);

}
//-----------------------------------------------------------------------------
/// Provides access to the single instance of this class in the process. If the
/// class does not exist, it will initialize it and log any errors that occur.
/// \return pointer to the instance of this class.
//-----------------------------------------------------------------------------
SharedGlobal* SharedGlobal::Instance(void)
{
    static SharedGlobal* sg = new SharedGlobal;

    PsAssert(sg != NULL);

    if (!sg->m_bInitialized && !sg->Initialize())
    {
        LogConsole(logERROR, "Unable to create SharedGlobal data\n");
        delete sg;
        sg = NULL;
    }

    return (sg);
}
Example #15
0
/// Unhook the load library functions
void UnhookLoadLibrary()
{
    LogTrace(traceENTER, "");

    AMDT::BeginHook();

    LONG error = AMDT::UnhookAPICall(&(PVOID&)Real_LoadLibraryA, Mine_LoadLibraryA);
    PsAssert(error == NO_ERROR);

    error = AMDT::UnhookAPICall(&(PVOID&)Real_LoadLibraryExA, Mine_LoadLibraryExA);
    PsAssert(error == NO_ERROR);

    error = AMDT::UnhookAPICall(&(PVOID&)Real_LoadLibraryW, Mine_LoadLibraryW);
    PsAssert(error == NO_ERROR);

    error = AMDT::UnhookAPICall(&(PVOID&)Real_LoadLibraryExW, Mine_LoadLibraryExW);
    PsAssert(error == NO_ERROR);

    error = AMDT::UnhookAPICall(&(PVOID&)Real_FreeLibrary, Mine_FreeLibrary);
    PsAssert(error == NO_ERROR);

    if (AMDT::EndHook() != NO_ERROR)
    {
        Log(logERROR, "UnhookLoadLibrary() failed\n");
    }

    // Restore Real functions to original values in case they aren't restored correctly by the unhook call
    Real_LoadLibraryA = LoadLibraryA;
    Real_LoadLibraryExA = LoadLibraryExA;
    Real_LoadLibraryW = LoadLibraryW;
    Real_LoadLibraryExW = LoadLibraryExW;
    Real_FreeLibrary = FreeLibrary;

    LogTrace(traceEXIT, "");
    return;
}
//--------------------------------------------------------------------------
/// Allows the application to send an error response for this command.
/// These errors also get added to the log
/// \param pError Error message to send
//--------------------------------------------------------------------------
void CommandResponse::SendError(const char* pError, ...)
{
    PsAssert(pError != NULL);

    char errString[ COMMAND_MAX_LENGTH ];
    va_list arg_ptr;

    // we prepend "Error: to the string for the HTTP response - but not for the logfile
    // pLogString tracks the start of the formatted string to pass to the Logfile

    int nLen = sprintf_s(errString, COMMAND_MAX_LENGTH, "Error: ");

    if (nLen < 0)
    {
        Log(logERROR, "String length is less than 0\n");
        return;
    }

    char* pLogString = &errString[nLen];

    va_start(arg_ptr, pError);
    vsprintf_s(pLogString, COMMAND_MAX_LENGTH - nLen, pError, arg_ptr);
    va_end(arg_ptr);

    Log(logERROR, "%s\n", pLogString);

    // send the error back to all received requests
    for (std::list< CommunicationID >::const_iterator iRequestID = m_requestIDs.begin(); iRequestID != m_requestIDs.end(); ++iRequestID)
    {
        bool bResult = SendResponse(*iRequestID, "text/plain", errString, (unsigned int) strlen(errString), m_bStreamingEnabled);

        if (bResult == false)
        {
            Log(logERROR, "Failed to send error to request %u\n", *iRequestID);

            m_bStreamingEnabled = false;
            m_eResponseState = ERROR_SENDING_RESPONSE;
        }
    }

    if (m_bStreamingEnabled == false)
    {
        // we sent a response and the request was not for streaming data, so unset the request
        m_requestIDs.clear();
    }
}
//--------------------------------------------------------------------------
/// Processes the specified CommandObject to see if it is targeting any of
/// the added Processors
/// \param rIncomingCommand the incoming command that should be handled
/// \return true if the command could be processed; false otherwise
//--------------------------------------------------------------------------
bool CommandProcessor::ProcessProcessors(CommandObject& rIncomingCommand)
{
    ProcessorList::const_iterator it;

    for (it = m_Processors.begin() ; it < m_Processors.end(); ++it)
    {
        CommandProcessor* pProc = *it;
        PsAssert(pProc != NULL);

        if (rIncomingCommand.IsCommand(pProc->GetID()))
        {
            pProc->Process(rIncomingCommand);
            return true;
        }
    }

    return false;
}
//--------------------------------------------------------------------------
/// Returns the number of commands that are editable.
/// \return Number of editable commands (0-n).
//--------------------------------------------------------------------------
unsigned int CommandProcessor::GetEditableCount()
{
    unsigned int nCount = 0;
    CommandList::const_iterator objIter;

    for (objIter = m_Commands.begin(); objIter < m_Commands.end(); ++objIter)
    {
        CommandResponse* pObj = *objIter;
        PsAssert(pObj != NULL);

        if (pObj->GetEditableContent() != NOT_EDITABLE)
        {
            nCount++;
        }
    }

    return nCount;
}
//-----------------------------------------------------------------------------
/// ShouldResponseBeSent
///
/// indicates whether a response should be sent to the specified request based
/// on whether or not the request is streaming and is rate limited. If the
/// request is rate limited, but a response can be sent, the "last sent time"
/// will be updated if bUpdateTime is true
///
/// \param requestID id of the request that may or may not be rate limited
/// \param bUpdateTime indicates whether or not the "last sent time" will be
///    updated if the response is rate limited, but allowed to be sent
///
/// \return true if a response should NOT be sent; false otherwise
//-----------------------------------------------------------------------------
bool ShouldResponseBeSent(CommunicationID requestID, bool bUpdateTime)
{
    // protect the maps from being changed by other threads using the mutex
    ScopeLock lock(s_mutex);

    ResponseMap::iterator iterResponse = g_streamingResponseMap.find(requestID);

    if (iterResponse == g_streamingResponseMap.end())
    {
        // don't limit the send because we don't even know it is streaming
        return false;
    }

    Response* pResponse = iterResponse->second;
    PsAssert(pResponse != NULL);

    // if this is a streaming request, only send if rate allows
    if (pResponse->m_bStreamingEnabled == true)
    {
        if (pResponse->m_dwMaxStreamsPerSecond == COMM_MAX_STREAM_RATE ||
            pResponse->m_dwMaxStreamsPerSecond == 0)
        {
            return false;
        }

        DWORD dwCurrTime = g_streamTimer.GetAbsolute();

        if (dwCurrTime - pResponse->m_dwLastSent >= 1000 / pResponse->m_dwMaxStreamsPerSecond)
        {
            if (bUpdateTime)
            {
                pResponse->m_dwLastSent = dwCurrTime;
            }

            return false;
        }
        else
        {
            return true;
        }
    }

    return false;
}
//-----------------------------------------------------------------------------
/// Get the map used to associate queue with its command lists.
/// \param inQueue Input queue.
/// \returns The CommandQueueToCommandListMap used to associate a Queue with the CommandLists it utilizes.
//-----------------------------------------------------------------------------
D3D12_COMMAND_LIST_TYPE DX12FrameProfilerLayer::GetCommandListTypeFromCommandQueue(Wrapped_ID3D12CommandQueue* inQueue)
{
    // Use the metadata object to retrieve the cached CreateInfo, and then pull the type out. Start with "Direct".
    D3D12_COMMAND_LIST_TYPE commandListType = D3D12_COMMAND_LIST_TYPE_DIRECT;

    PsAssert(inQueue != nullptr);

    if (inQueue != nullptr)
    {
        DX12ObjectDatabaseProcessor* databaseProcessor = DX12ObjectDatabaseProcessor::Instance();
        DX12WrappedObjectDatabase* objectDatabase = static_cast<DX12WrappedObjectDatabase*>(databaseProcessor->GetObjectDatabase());

        IDX12InstanceBase* queueMetadata = objectDatabase->GetMetadataObject((IUnknown*)inQueue);
        Wrapped_ID3D12CommandQueueCreateInfo* queueCreateInfo = static_cast<Wrapped_ID3D12CommandQueueCreateInfo*>(queueMetadata->GetCreateInfoStruct());
        D3D12_COMMAND_QUEUE_DESC* queueDesc = queueCreateInfo->GetDescription();
        commandListType = queueDesc->Type;
    }

    return commandListType;
}
//-----------------------------------------------------------------------------
/// Set the internal flag that determines if GPU command profiling is enabled.
/// \param inbProfilingEnabled The flag used to enable or disable profiling.
//-----------------------------------------------------------------------------
void DX12FrameProfilerLayer::SetProfilingEnabled(bool inbProfilingEnabled)
{
    ModernAPIFrameProfilerLayer::SetProfilingEnabled(inbProfilingEnabled);

    for (CommandQueueToCommandListMap::iterator it = mCommandQueueTracker.begin();
         it != mCommandQueueTracker.end();
         ++it)
    {
        Wrapped_ID3D12Device* pDevice = static_cast<Wrapped_ID3D12Device*>(DX12Util::SafeGetDevice(it->first));

        if (pDevice != nullptr)
        {
            HRESULT stateSet = E_FAIL;
            stateSet = pDevice->mRealDevice->SetStablePowerState(inbProfilingEnabled);
            PsAssert(stateSet == S_OK);

            pDevice->Release();
        }
    }
}
bool IsToken(char** sIn, const char* sTok)
{
    size_t dwTokLen = strlen(sTok);
    size_t nStringLength = strlen(*sIn);

    if (_strnicmp(*sIn, sTok, dwTokLen) == 0)
    {
        // Check to see if we are going to increment the pointer beond the end of the array.
        PsAssert(dwTokLen <= nStringLength);

        if (dwTokLen > nStringLength)
        {
            Log(logERROR, "IsToken: buffer overrun. Str = %s, Tok = %s\n", *sIn, sTok);
            return false;
        }

        *sIn += dwTokLen;
        return true;
    }

    return false;
}
//-----------------------------------------------------------------------------
/// Verify, align, and store the new profiler results.
/// \param pQueue The Queue used to collect the results to verify.
/// \param results The vector of profiler results to verify.
/// \param pTimestampPair A pair of calibration timestamps used to align CPU and GPU timestamps.
/// \param threadID The ThreadId that the results are collected with.
/// \param frameStartTime The start time of the frame as collected on the CPU.
//-----------------------------------------------------------------------------
void DX12FrameProfilerLayer::VerifyAlignAndStoreResults(
    Wrapped_ID3D12CommandQueue*  pQueue,
    std::vector<ProfilerResult>& results,
    CalibrationTimestampPair*    pTimestampPair,
    UINT32                       threadID,
    GPS_TIMESTAMP                frameStartTime)
{
    SampleIdToProfilerResultMap* pResultMap = FindOrCreateProfilerResultsMap(pQueue, threadID);
    PsAssert(pResultMap != nullptr);

    if (pResultMap != nullptr)
    {
        for (size_t resultIndex = 0; resultIndex < results.size(); ++resultIndex)
        {
            ProfilerResult& currentResult = results[resultIndex];

            const UINT64 sampleId = currentResult.measurementInfo.idInfo.mSampleId;

            // Verify that the timestamps retrieved from the profiler appear to be valid.
            if (ValidateProfilerResult(currentResult) == true)
            {
                // Now attempt to align the profiled GPU timestamps with the traced API calls on the CPU.
                bool bAlignedSuccessfully = AlignProfilerResultWithCPUTimeline(currentResult, pTimestampPair, frameStartTime);

                if (bAlignedSuccessfully)
                {
                    // Store the final adjusted profiler results if they're valid.
                    ProfilerResult* pNewResult = new ProfilerResult;
                    CopyProfilerResult(pNewResult, &currentResult);
                    (*pResultMap)[sampleId] = pNewResult;
                }
                else
                {
                    Log(logERROR, "Command with SampleId %d failed to align with CPU timeline.\n", sampleId);
                }
            }
        }
    }
}
//--------------------------------------------------------------------------
/// Retrieve an existing SampleInfo instance, or create a new one, for the given ThreadId.
/// \param inThreadId The current thread requesting a SampleInfo structure.
/// \returns A SampleInfo instance to be used with the given ThreadId.
//--------------------------------------------------------------------------
ModernAPIFrameProfilerLayer::SampleInfo* ModernAPIFrameProfilerLayer::GetSampleInfoForThread(DWORD inThreadId)
{
    SampleInfo* pResult = nullptr;

    if (mSampleIdMap.find(inThreadId) != mSampleIdMap.end())
    {
        // mSampleIdMap already has a key for the incoming thread. Update the current sampleInfo.
        pResult = mSampleIdMap[inThreadId];
    }
    else
    {
        // We need to insert a new key into the mSampleIdMap. Need to lock it first.
        ScopeLock sampleLock(&mUniqueSampleIdMutex);

        pResult = new SampleInfo;

        mSampleIdMap[inThreadId] = pResult;
    }

    PsAssert(pResult != nullptr);

    return pResult;
}
//--------------------------------------------------------------------------
/// \param pName the full name of this object
//--------------------------------------------------------------------------
void CommandResponse::SetDisplayName(const char* pName)
{
    PsAssert(pName != NULL);
    m_pDisplayName = pName;
}
//--------------------------------------------------------------------------
/// Return a string with all of the logged API call data in line-delimited
/// text format. This is used within the Timeline view in the client.
/// \return A string of all of the logged API calls captured during frame render.
//--------------------------------------------------------------------------
std::string MultithreadedTraceAnalyzerLayer::GetAPITraceTXT()
{
    // A switch to determine at the last moment whether or not we should send our generated response back to the client.
    bool bWriteResponseString = false;

    // Concatenate all of the logged call lines into a single string that we can send to the client.
    std::stringstream traceString;
    std::map<DWORD, ThreadTraceData*>::iterator traceIter;

    for (traceIter = mThreadTraces.begin(); traceIter != mThreadTraces.end(); ++traceIter)
    {
        ThreadTraceData* currentTrace = traceIter->second;
        const TimingLog& currentTimer = currentTrace->mAPICallTimer;
        GPS_TIMESTAMP timeFrequency = currentTimer.GetTimeFrequency();
        const GPS_TIMESTAMP frameStartTime = mFramestartTime;
        size_t numEntries = currentTrace->mLoggedCallVector.size();

        // When using the updated trace format, include a preamble section for each traced thread.
#if defined(CODEXL_GRAPHICS)
        // Write the trace type, API, ThreadID, and count of APIs traced.
        traceString << "//==API Trace==" << std::endl;
        traceString << "//API=" << GetAPIString() << std::endl;
        traceString << "//ThreadID=" << traceIter->first << std::endl;
        traceString << "//ThreadAPICount=" << numEntries << std::endl;
#endif

        for (size_t entryIndex = 0; entryIndex < numEntries; ++entryIndex)
        {
            // Get each logged call by index.
            const CallsTiming& callTiming = currentTimer.GetTimingByIndex(entryIndex);

            double deltaStartTime, deltaEndTime;
            bool conversionResults = currentTimer.ConvertTimestampToDoubles(callTiming.m_startTime,
                                                                            callTiming.m_endTime,
                                                                            deltaStartTime,
                                                                            deltaEndTime,
                                                                            frameStartTime,
                                                                            &timeFrequency);

            // We should always be able to convert from GPS_TIMESTAMPs to doubles.
            PsAssert(conversionResults == true);
            (void)conversionResults;

            const APIEntry* callEntry = currentTrace->mLoggedCallVector[entryIndex];

            // This exists as a sanity check. If a duration stretches past this point, we can be pretty sure something is messed up.
            // This signal value is basically random, with the goal of it being large enough to catch any obvious duration errors.
            if (deltaEndTime > 8000000000.0f)
            {
                const char* functionName = callEntry->GetAPIName();
                Log(logWARNING, "The duration for APIEntry '%s' with index '%d' is suspicious. Tracing the application may have hung, producing inflated results.\n", functionName, entryIndex);
            }

            callEntry->AppendAPITraceLine(traceString, deltaStartTime, deltaEndTime);
        }

        bWriteResponseString = true;
    }

    // If for some reason we failed to write a valid response string, reply with a known failure signal so the client handles it properly.
    if (!bWriteResponseString)
    {
        traceString << "NODATA";
    }

    return traceString.str();
}
//-----------------------------------------------------------------------------
bool MakeResponse(CommunicationID requestID, Response** ppResponse)
{
    // protect the maps from being changed by other threads using the mutex
    ScopeLock lock(s_mutex);

    PsAssert(ppResponse != NULL);

    // first see if we already have this ID as a streaming response
    ResponseMap::iterator iterResponse = g_streamingResponseMap.find(requestID);

    if (iterResponse != g_streamingResponseMap.end())
    {
        *ppResponse = iterResponse->second;
        return true;
    }

    // otherwise we need to create a new response based on the original request
    // so get the request
    RequestMap::iterator iterRequest = g_requestMap.find(requestID);

    if (iterRequest == g_requestMap.end())
    {
        // the original request couldn't be found, so return failure
        return false;
    }

    // need to create a new response
    if (PsNew(*ppResponse) == false)
    {
        return false;
    }

    HTTPRequestHeader* pRequest = iterRequest->second;
    PsAssert(pRequest != NULL);

    if (pRequest->GetReceivedOverSocket() == true)
    {
        (*ppResponse)->client_socket = pRequest->GetClientSocket();
    }
    else
    {
#if defined (_WIN32)
        (*ppResponse)->client_socket = NetSocket::CreateFromDuplicate(pRequest->GetProtoInfo());
#else
        // create a new socket and connect to the streamSocket on the server
        (*ppResponse)->client_socket = NetSocket::Create();

        if ((*ppResponse)->client_socket != NULL)
        {
            osPortAddress portAddress((unsigned short)pRequest->GetPort());
            (*ppResponse)->client_socket->Connect(portAddress);
        }

#endif
    }

    if ((*ppResponse)->client_socket == NULL)
    {
        int Err = NetSocket::LastError();
        Log(logERROR, "Could not create socket: NetSocket failed with error: %ld\n", Err);
        return false;
    }

    // see if this should be added as a streaming response
    gtASCIIString strUrl(pRequest->GetUrl());
    int32 iStream = strUrl.find(STR_STREAM_TOKEN);

    if (iStream >= 0)
    {
        const char* pBuf = strUrl.asCharArray();
        const char* pRate = &pBuf[ iStream + strlen(STR_STREAM_TOKEN)];
        unsigned int uRate = 0;

        // try to get the rate from the command;
        if (sscanf_s(pRate, "%u", &uRate) < 1)
        {
            // default to max rate
            uRate = COMM_MAX_STREAM_RATE;
        }

        // set the response as streaming with the specified rate
        (*ppResponse)->m_bStreamingEnabled = true;
        (*ppResponse)->m_dwMaxStreamsPerSecond = uRate;
        g_streamingResponseMap[ requestID ] = *ppResponse;
    }
    else
    {
        // streaming requests need to be kept around so that
        // additional responses can be directed to the right place,
        // HOWEVER, non-streaming requests only get a single response
        // and we just created the response for it, so it is safe
        // to remove the request from the requestMap. This will
        // help keep communication quick as the number of incoming
        // requests grows.
        RemoveRequest(requestID);
    }

    return true;
}
//--------------------------------------------------------------------------
/// Processes the specified CommandObject to see if it should activate any
/// of the added Commands
/// \param rIncomingCommand the incoming command that should be handled
/// \return true if the command could be processed; false otherwise
//--------------------------------------------------------------------------
bool CommandProcessor::ProcessCommands(CommandObject& rIncomingCommand)
{
    CommandList::const_iterator it;

    for (it = m_Commands.begin() ; it < m_Commands.end(); ++it)
    {
        CommandResponse* pComm = *it;
        PsAssert(pComm != NULL);

        if (rIncomingCommand.IsCommand(pComm->GetURL()))
        {
            float dummy;
            pComm->m_bStreamingEnabled = rIncomingCommand.GetParam("Stream", dummy);

            if (pComm->GetParams(rIncomingCommand))
            {
                // skip over the parsed parameters
                pComm->SkipParsedParams(rIncomingCommand);

                // only set the command to be active if
                // - the command is not editable (the client has requested data) or
                // - AutoReply is false (another part of the server wants to react / respond) or
                // - this is the last command and no response has been set yet
                if (pComm->GetEditableContent() == NOT_EDITABLE ||
                    pComm->GetEditableContentAutoReply() == false ||
                    (rIncomingCommand.HasAnotherCommand() == false &&
                     rIncomingCommand.GetResponseState() == NO_RESPONSE))
                {
                    pComm->SetActiveRequest(rIncomingCommand);
                }

                if (rIncomingCommand.HasAnotherCommand() == false)
                {
                    rIncomingCommand.SetState(DELAYED_RESPONSE);

                    if (pComm->GetEditableContent() != NOT_EDITABLE &&
                        rIncomingCommand.GetResponseState() != SENT_RESPONSE)
                    {
                        if (pComm->GetEditableContentAutoReply())
                        {
                            // if this is an editable value,
                            // send back a response
                            pComm->Send("OK");
                            rIncomingCommand.SetState(SENT_RESPONSE);
                        }
                    }
                }

                return true;
            }
            else
            {
                // return variable's value
                if (pComm->GetEditableContent() != NOT_EDITABLE &&
                    rIncomingCommand.GetResponseState() != SENT_RESPONSE &&
                    pComm->GetEditableContentAutoReply()
                   )
                {
                    pComm->SetActiveRequest(rIncomingCommand);
                    rIncomingCommand.SetState(DELAYED_RESPONSE);
                    pComm->Send(pComm->GetEditableContentValue().c_str());
                    rIncomingCommand.SetState(SENT_RESPONSE);
                    return true;
                }

                return false;
            }
        }
    }

    return false;
}
//--------------------------------------------------------------------------
/// Should be called by an API-specific FrameDebugger at each drawcall
/// \param rDrawCall the API-specific DrawCall that is being executed
//--------------------------------------------------------------------------
void FrameDebugger::OnDrawCall(IDrawCall& rDrawCall)
{
    m_ulDrawCallCounter++;

    if (m_drawCallList.IsActive())
    {
        m_LastDrawCall = &rDrawCall;
        gtASCIIString xmlString = rDrawCall.GetXML();
        xmlString += XML("hash", rDrawCall.GetHash().asCharArray());
        m_drawCallList += GetDrawCallXML(m_ulDrawCallCounter, xmlString.asCharArray()) ;
    }

    // Could collect drawcall XML here if desired
    if (IsDrawCallEnabled(m_ulDrawCallCounter))
    {
        // Make sure to not render after the frame debugger's break point
        if (IsTargetDrawCall(m_ulDrawCallCounter) == false)
        {
            // execute the drawcall for the app
            rDrawCall.Execute();
        }
        else
        {
            // have the pipeline process non-hud commands before drawing anything on HUD
            OnDrawCallAtBreakPointPreHUD(rDrawCall);

            // allow the API-specific FrameDebugger to decide whether or not to do the drawcall at the breakpoint
            DoDrawCallAtBreakPoint(rDrawCall);

            // Send the current draw call back to the client.
            if (m_CurrentDrawCall.IsActive())
            {
                m_CurrentDrawCall.Send(rDrawCall.GetXML().asCharArray());
            }

            bool bRes = BeginHUD();
            PsAssert(bRes != false);

            if (bRes == false)
            {
                Log(logERROR, "BeginHUD() failed");
                return;
            }

            // show the wireframe overlay
            if (m_bWireframeOverlay)
            {
                DoWireframeOverlay(rDrawCall, m_fWireframeOverlayColor[0], m_fWireframeOverlayColor[1], m_fWireframeOverlayColor[2], m_fWireframeOverlayColor[3]);
            }

            if (m_bAutoRenderTarget)
            {
                DoAutoRenderTarget();
            }

            OnDrawCallAtBreakPoint(rDrawCall);

            EndHUD();

            // skip the rest of the draws if HUD is being configured
            if (false == m_bConfigHUD)
            {
                // When the frame debugger is enabled we process the messages from inside the FD and no longer re-render the Frame Capture. This speeds up processing of messages
                // in apps that are running very slowly
                for (int ProcessedCommands = 0;;)
                {
                    if (m_drawCallList.IsActive() == true || m_Stats.IsActive() == true)
                    {
                        break;
                    }

                    // have the pipeline process non-hud commands before drawing anything on HUD
                    OnDrawCallAtBreakPointPreHUD(rDrawCall);
                    OnDrawCallAtBreakPoint(rDrawCall);

                    gtASCIIString SuCmd = PeekPendingRequests();

                    // Check for non FD command, if this changes then we have to re-render the HUD etc. So we break out
                    if (SuCmd.length() != 0)
                    {
                        char* pCmd = (char*)SuCmd.asCharArray();
                        ProcessedCommands++;

                        if (strstr(pCmd, "FD/Pipeline") == NULL)
                        {
                            LogConsole(logMESSAGE, "Processed in FD %3u\n", ProcessedCommands);
                            break;
                        }

                        GetSinglePendingRequest();
                    }
                }
            }
        }
    }
}
//--------------------------------------------------------------------------
/// Sets the current tag name.
/// \param pTagName The tag name.
//--------------------------------------------------------------------------
void CommandResponse::SetTagName(const char* pTagName)
{
    PsAssert(pTagName != NULL);
    m_pTagName = pTagName;
}