//--------------------------------------------------------------------------
/// Generate a trace info block that can be appended to the top of the trace. Included application and system information.
/// \param outHeaderString A string containing the generated block of header text.
/// \returns True if the header was generated successfully. False if it failed.
//--------------------------------------------------------------------------
bool MultithreadedTraceAnalyzerLayer::GenerateLinkedTraceHeader(gtASCIIString& outHeaderString)
{
    bool bHeaderGenerated = false;

    // The response should include a header when connected to CodeXL Graphics.
    outHeaderString.appendFormattedString("//CodeXL Frame Trace\n");

    osModuleArchitecture moduleArchitecture;
    osRuntimePlatform currentPlatform;
    gtString executablePath;
    gtString commandLine;
    gtString workingDirectory;

    if (osGetProcessLaunchInfo(osGetCurrentProcessId(), moduleArchitecture, currentPlatform, executablePath, commandLine, workingDirectory) == true)
    {
        outHeaderString.appendFormattedString("//ProcessExe=%s\n", executablePath.asASCIICharArray());

        // Build a timestamp.
        osTime currentTime;
        currentTime.setFromCurrentTime();
        tm timeStruct;
        currentTime.timeAsTmStruct(timeStruct, osTime::LOCAL);

        // Need to add 1900, since tm contains "years since 1900".
        int year = timeStruct.tm_year + 1900;

        // Need to add 1, since tm contains "months since January".
        int month = timeStruct.tm_mon + 1;

        int day = timeStruct.tm_mday;
        int hour = timeStruct.tm_hour;
        int minute = timeStruct.tm_min;
        int second = timeStruct.tm_sec;

        gtASCIIString timestampBuilder;
        timestampBuilder.appendFormattedString("%d/%d/%d %d:%d:%d", month, day, year, hour, minute, second);
        outHeaderString.appendFormattedString("//TraceDateTime=%s\n", timestampBuilder.asCharArray());

        outHeaderString.appendFormattedString("//TraceFileVersion=%d\n", 1);
        outHeaderString.appendFormattedString("//ApplicationArgs=%s\n", commandLine.asASCIICharArray());
        outHeaderString.appendFormattedString("//WorkingDirectory=%s\n", workingDirectory.asASCIICharArray());

        // Build a system information header string.
        std::string systemInfo;
        OSWrappers::WriteSystemInfoString(systemInfo);
        outHeaderString.appendFormattedString("\n%s\n", systemInfo.c_str());

        bHeaderGenerated = true;
    }
    else
    {
        Log(logERROR, "Failed to retrieve process info when building response header.\n");
    }

    return bHeaderGenerated;
}
//--------------------------------------------------------------------------
/// Handle what happens when a Linked Trace is requested. We can either:
/// 1. Return the trace response as normal.
/// 2. Cache the response to disk, and generate a "trace metadata" file used to retrieve the trace later.
/// \param inFullResponseString The response string built by tracing the application.
/// \param inbSaveResponseToFile A switch used to determine which response method to use.
//--------------------------------------------------------------------------
void MultithreadedTraceAnalyzerLayer::HandleLinkedTraceResponse(std::stringstream& inFullResponseString, bool inbSaveResponseToFile)
{
    // If we're building for use with CodeXL, insert extra metadata into the response before returning.
#if defined(CODEXL_GRAPHICS)
    // The response should include a header when connected to CodeXL Graphics.
    gtASCIIString headerBuilder;
    headerBuilder.appendFormattedString("//AMD CodeXL Frame Trace\n");

    osModuleArchitecture moduleArchitecture;
    osRuntimePlatform currentPlatform;
    gtString executablePath;
    gtString commandLine;
    gtString workingDirectory;

    if (osGetProcessLaunchInfo(osGetCurrentProcessId(), moduleArchitecture, currentPlatform, executablePath, commandLine, workingDirectory) == true)
    {
        headerBuilder.appendFormattedString("//ProcessExe=%s\n", executablePath.asASCIICharArray());

        // Build a timestamp.
        osTime currentTime;
        currentTime.setFromCurrentTime();
        tm timeStruct;
        currentTime.timeAsTmStruct(timeStruct, osTime::LOCAL);

        // Need to add 1900, since tm contains "years since 1900".
        int year = timeStruct.tm_year + 1900;

        // Need to add 1, since tm contains "months since January".
        int month = timeStruct.tm_mon + 1;

        int day = timeStruct.tm_mday;
        int hour = timeStruct.tm_hour;
        int minute = timeStruct.tm_min;
        int second = timeStruct.tm_sec;

        gtASCIIString timestampBuilder;
        timestampBuilder.appendFormattedString("%d/%d/%d %d:%d:%d", month, day, year, hour, minute, second);
        headerBuilder.appendFormattedString("//TraceDateTime=%s\n", timestampBuilder.asCharArray());

        headerBuilder.appendFormattedString("//TraceFileVersion=%d\n", 1);
        headerBuilder.appendFormattedString("//ApplicationArgs=%s\n", commandLine.asASCIICharArray());
        headerBuilder.appendFormattedString("//WorkingDirectory=%s\n", workingDirectory.asASCIICharArray());
    }
    else
    {
        Log(logERROR, "Failed to retrieve process info when building response header.\n");
    }

    // Build a system information header string.
    std::string systemInfo;
    OSWrappers::WriteSystemInfoString(systemInfo);
    headerBuilder.appendFormattedString("\n%s\n", systemInfo.c_str());

    // Store the response temporarily.
    std::string headerString = headerBuilder.asCharArray();
    std::string responseString = inFullResponseString.str();

    // Clear the response string stream.
    inFullResponseString.str(std::string());
    inFullResponseString.clear();

    // Write the header and response chunks into the final response.
    inFullResponseString << headerString;
    inFullResponseString << responseString;
#endif

    // Check if we want to cache the response to disk, or return it as-is.
    if (inbSaveResponseToFile)
    {
        std::string metadataXMLString;
        bool bWriteMetadataSuccessful = WriteTraceAndMetadataFiles(inFullResponseString, metadataXMLString);
        if (bWriteMetadataSuccessful)
        {
            // Send a response back to the client indicating which trace metadata file was written to disk.
            mCmdLinkedTraceWithSave.Send(metadataXMLString.c_str());
        }
        else
        {
            Log(logERROR, "Failed to write trace metadata XML.\n");
            mCmdLinkedTraceWithSave.Send("Failed");
        }
    }
    else
    {
        // Return the normal trace response string.
        // Send a response containing the API and GPU trace text.
        mCmdLinkedTrace.Send(std::string(inFullResponseString.str()).c_str());
    }
}
//--------------------------------------------------------------------------
/// Write a trace's metadata file and return the contenst through the out-param.
/// \param inFullResponseString The full response string for a collected linked trace request.
/// \param outMetadataXML The XML metadata string to return to the client.
/// \returns True if writing the metadata file was successful.
//--------------------------------------------------------------------------
bool MultithreadedTraceAnalyzerLayer::WriteTraceAndMetadataFiles(const std::stringstream& inFullResponseString, std::string& outMetadataXML)
{
    bool bWrittenSuccessfully = false;

    // Empty out the incoming path to the metadata file. We'll know the exact path later.
    outMetadataXML.assign("");

    osModuleArchitecture moduleArchitecture;
    osRuntimePlatform currentPlatform;
    gtString executablePath;
    gtString commandLine;
    gtString workingDirectory;

    // Retrieve the name of the instrumented application. Construct a metadata filename which references it.
    if (osGetProcessLaunchInfo(osGetCurrentProcessId(), moduleArchitecture, currentPlatform, executablePath, commandLine, workingDirectory) == true)
    {
        osFilePath executableFilepath;
        executableFilepath.setFullPathFromString(executablePath);

        gtString appName;
        if (executableFilepath.getFileName(appName) == true)
        {
            osTime currentTime;
            currentTime.setFromCurrentTime();
            tm timeStruct;
            currentTime.timeAsTmStruct(timeStruct, osTime::LOCAL);

            // Need to add 1900, since tm contains "years since 1900".
            int year = timeStruct.tm_year + 1900;

            // Need to add 1, since tm contains "months since January".
            int month = timeStruct.tm_mon + 1;
            int day = timeStruct.tm_mday;

            int hour = timeStruct.tm_hour;
            int minute = timeStruct.tm_min;
            int second = timeStruct.tm_sec;

            gtASCIIString metadataFilename;

            // ExecutableFilename-YEAR-MM-DD-HOUR-MINUTE-SECOND
            metadataFilename.appendFormattedString("description-%s-%d-%d-%d-%d-%d-%d.xml",
                appName.asASCIICharArray(),
                year, month, day,
                hour, minute, second);

            // Build a path to the GPS folder within the Temp directory.
            osFilePath systemTempDirectory;
            systemTempDirectory.setPath(osFilePath::OS_TEMP_DIRECTORY);


            gtString toolDirectory;
#ifdef CODEXL_GRAPHICS
            toolDirectory.fromASCIIString("CodeXL");
#else
            toolDirectory.fromASCIIString(GetPerfStudioDirName());
#endif
            // @TODO: Construct a path from the temp + tool directory.
            systemTempDirectory.appendSubDirectory(toolDirectory);

            gtList<osFilePath> toolDirectoryPaths;
            osDirectory d;
            d.setDirectoryPath(systemTempDirectory);

            // @TODO: Find a better way to create the "Session" directory. We shouldn't need to search through existing session directories.
            int maxSessionIndex = 0;
            bool bGotSubDirectories = d.getSubDirectoriesPaths(osDirectory::SORT_BY_NAME_DESCENDING, toolDirectoryPaths);
            if (bGotSubDirectories)
            {
                // Step through each directory, and look for "Session" folders.
                gtList<osFilePath>::iterator subdirectoryIter;
                for (subdirectoryIter = toolDirectoryPaths.begin(); subdirectoryIter != toolDirectoryPaths.end(); ++subdirectoryIter)
                {
                    gtString subdirString;
                    osFilePath subdir = *subdirectoryIter;
                    subdir.getFileName(subdirString);

                    if (subdir.isDirectory() && subdirString.startsWith(L"Session"))
                    {
                        // Remove the "Session" part of the string. We're only interested in the number at the end.
                        subdirString.replace(L"Session", L"", true);

                        const char* sessionIndexAsString = subdirString.asASCIICharArray();
                        int thisSessionId = atoi(sessionIndexAsString);

                        if (thisSessionId > maxSessionIndex)
                        {
                            maxSessionIndex = thisSessionId;
                        }
                    }

                }
            }


            gtASCIIString pathToDataDirectory = systemTempDirectory.asString().asASCIICharArray();

            // Metadata files will be saved to the temp directory with the following filename scheme:
            // "%TEMP%/ToolDirectory/Session[Index]/ApplicationBinaryName/Frame[Index]/description.xml"

            int frameIndex = GetInterceptor()->GetParentLayerManager()->GetFrameCount();

            // Generate a "Session" folder with a number at the end. Compute the correct number by looking at the
            // Session folders that already exist
            gtASCIIString sessionString;
            sessionString.appendFormattedString("Session%d", maxSessionIndex + 1);

            pathToDataDirectory.appendFormattedString("\\%s\\%s\\Frame%d\\", sessionString.asCharArray(), appName.asASCIICharArray(), frameIndex);

            // Create the data directory if it doesn't already exist.
            gtString fullPathToDataDirectoryAsGTString;
            fullPathToDataDirectoryAsGTString.fromASCIIString(pathToDataDirectory.asCharArray());

            osDirectory dir;
            dir.setDirectoryFullPathFromString(fullPathToDataDirectoryAsGTString);
            if (!dir.exists())
            {
                bool bDirectoryCreated = dir.create();
                if (!bDirectoryCreated)
                {
                    Log(logERROR, "Failed to create data directory for traced frame: '%s'.\n", fullPathToDataDirectoryAsGTString.asASCIICharArray());
                }
            }

            gtASCIIString pathToMetadataFile = pathToDataDirectory;
            pathToMetadataFile.appendFormattedString("%s", metadataFilename.asCharArray());

            gtString fullMetadataFilepathAsGTString;
            fullMetadataFilepathAsGTString.fromASCIIString(pathToMetadataFile.asCharArray());

            osFile metadataFile(fullMetadataFilepathAsGTString);
            bool bMetadataFileOpened = metadataFile.open(osChannel::OS_ASCII_TEXT_CHANNEL, osFile::OS_OPEN_TO_WRITE);

            // If we've successfully opened the metadata file, we'll also attempt to write the trace file.
            if (bMetadataFileOpened)
            {
                osFilePath traceFileDirectory;
                traceFileDirectory.setPath(osFilePath::OS_TEMP_DIRECTORY);

                traceFileDirectory.appendSubDirectory(toolDirectory);

                // Construct a filename for the cached trace response.
                gtASCIIString fullTraceFilename;
                fullTraceFilename.appendFormattedString("LinkedTrace-%s-%d-%d-%d-%d-%d-%d.ltr", appName.asASCIICharArray(), year, month, day, hour, minute, second);

                gtASCIIString fullTraceFilePath = pathToDataDirectory;
                fullTraceFilePath.appendFormattedString("%s", fullTraceFilename.asCharArray());

                gtString fullTraceResponseFilepathGTString;
                fullTraceResponseFilepathGTString.fromASCIIString(fullTraceFilePath.asCharArray());

                // Write the contents of the trace response file.
                osFile traceResponseFile(fullTraceResponseFilepathGTString);
                bool bTraceResponseFileOpened = traceResponseFile.open(osChannel::OS_ASCII_TEXT_CHANNEL, osFile::OS_OPEN_TO_WRITE);
                if (bTraceResponseFileOpened)
                {
                    // Dump the response into an ASCII string.
                    std::string responseAsString = inFullResponseString.str();

                    // Now put the ASCII string into a gtString so we can write it to the open file.
                    gtString gtStringResponse;
                    std::wstring wideString;
                    wideString.assign(responseAsString.begin(), responseAsString.end());
                    gtStringResponse.appendFormattedString(L"%s", wideString.c_str());
                    traceResponseFile.writeString(gtStringResponse);
                    traceResponseFile.close();
                }
                else
                {
                    Log(logERROR, "Failed to write trace response to file: '%s'\n", fullTraceResponseFilepathGTString.asASCIICharArray());
                }

                // Write the filename for the associated trace response that was just collected.
                std::string traceFilepathAsString;
                traceFilepathAsString.assign(fullTraceResponseFilepathGTString.asASCIICharArray());

                TraceMetadata metadata;

                // Insert the location of the metadata file being written out.
                metadata.mMetadataFilepath = pathToMetadataFile.asCharArray();

                // Insert the path to the cached trace file.
                metadata.mPathToTraceFile = fullTraceFilePath.asCharArray();

                // @TODO: When we have a framebuffer image system working, assign the path-to-image here.
                metadata.mPathToFrameBufferImage = "UNKNOWNPATH";

                ModernAPILayerManager* layerManager = GetInterceptor()->GetParentLayerManager();

                FrameInfo frameInfo;

                layerManager->GetFrameInfo(frameInfo);

                // Populate the metadata structure with the values stored in the LayerManager.
                metadata.mFrameInfo = frameInfo;
                metadata.mFrameIndex = frameIndex;
                metadata.mArchitecture = moduleArchitecture;

                // Write the metadata xml into the output file.
                gtASCIIString metadataXMLString;
                metadata.WriteToXML(metadataXMLString);
                gtString metadataXMLAsGTString;
                metadataXMLAsGTString.fromASCIIString(metadataXMLString.asCharArray());

                // Write the metadata XML into the file, and close.
                metadataFile.writeString(metadataXMLAsGTString);
                metadataFile.close();

                // The client will receive the full metadata XML string to parse.
                outMetadataXML.assign(metadataXMLString.asCharArray());

                bWrittenSuccessfully = true;
            }
            else
            {
                Log(logERROR, "Failed to open trace metadata file for writing: '%s'\n", metadataFilename);
            }
        }
        else
        {
            Log(logERROR, "Failed to retrieve the instrumented process's application filename.\n");
        }
    }
    else
    {
        Log(logERROR, "Failed to retrieve process launch info for target application.\n");
    }

    return bWrittenSuccessfully;
}
Beispiel #4
0
//--------------------------------------------------------------------------
/// The layer must create its resources here, it may hook some functions if really needed
/// \param type the creation object type.
/// \param pPtr Pointer to the object that was just created.
/// \return True if success, False if fail.
//--------------------------------------------------------------------------
bool ModernAPILayerManager::OnCreate(CREATION_TYPE type, void* pPtr)
{
    bool bReturn = true;

    for (UINT32 i = 0; i < m_AvailableLayers.size(); i++)
    {
        if (m_AvailableLayers[i]->OnCreate(type, pPtr) == false)
        {
            Log(logERROR, "Layer with index '%u' failed in OnCreate call.\n", i);
            bReturn = false;
        }
    }

    static bool bInitialized = false;

    if (!bInitialized)
    {
        osModuleArchitecture moduleArchitecture;
        osRuntimePlatform currentPlatform;
        gtString executablePathString;
        gtString commandLine;
        gtString workingDirectory;

        if (osGetProcessLaunchInfo(osGetCurrentProcessId(), moduleArchitecture, currentPlatform, executablePathString, commandLine, workingDirectory) == true)
        {
            osFilePath executablePath;
            executablePath.setFullPathFromString(executablePathString);

            gtString appName;

            if (executablePath.getFileName(appName) == true)
            {
                gtString capturePlayerExecutableName;
#ifdef CODEXL_GRAPHICS
                capturePlayerExecutableName.fromASCIIString("CXLGraphicsServerPlayer");
#else // CODEXL_GRAPHICS
                capturePlayerExecutableName.fromASCIIString("CapturePlayer");
#endif // CODEXL_GRAPHICS

                if (appName.startsWith(capturePlayerExecutableName))
                {
                    mbInCapturePlayer = true;
#ifdef _LINUX
                    // strip off the executable name and its path from the command line if it exists, and remove leading and trailing whitespaces
                    int startPos = commandLine.find(capturePlayerExecutableName);
                    startPos += capturePlayerExecutableName.length();
                    int endPos = commandLine.length();

                    commandLine.truncate(startPos, endPos);
                    commandLine.trim();
#endif

                    ParseCommandLine(commandLine.asASCIICharArray());
                }
            }
            else
            {
                Log(logWARNING, "Failed to parse target application filepath.\n");
            }
        }
        else
        {
            Log(logERROR, "Failed to retrieve process launch information.\n");
        }

        bInitialized = true;
    }

    return bReturn;
}