//-------------------------------------------------------------------------- /// 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; }
//-------------------------------------------------------------------------- /// 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; }