bool GraphicsServerCommunication::CaptureFrameStub(gtASCIIString& frameInfoAsXML, unsigned char*& pImageBuffer, unsigned long& imageSize) { bool retVal = true; #pragma message ("TODO: FA: remove this function") static gtASCIIString serverResponseFormat = "<Root><Location></Location>"\ "<FrameNumber>%d</FrameNumber><Contents>"\ "<LinkedTrace>linkedtrace_20150915_091403.ltr</LinkedTrace>"\ "<FrameBufferImage></FrameBufferImage>"\ "<ElapsedTime>%d</ElapsedTime>"\ "<FPS>%d</FPS>"\ "<CPUFrameDuration>%f</CPUFrameDuration>"\ "<APICallCount>%d</APICallCount>"\ "<DrawCallCount>%d</DrawCallCount>"\ "</Contents></Root>"; static int frameIndex = 1345; frameIndex += rand() % 50; int elapsedTimeMS = rand() % 1000000; int fps = rand() % 250; double frameDuration = (double)rand() / 2.345; int apiCalls = rand() % 500000; int drawCalls = rand() % apiCalls; frameInfoAsXML.appendFormattedString(serverResponseFormat.asCharArray(), frameIndex, elapsedTimeMS, fps, frameDuration, apiCalls, drawCalls); GetFrameThumbnail(pImageBuffer, imageSize, frameIndex % 12); return retVal; }
//----------------------------------------------------------------------------- /// escapes special characters for formating into XML /// /// \param strValue value that needs to have special characters escaped so that /// the XML can be parsed properly /// \return generated XML string //----------------------------------------------------------------------------- gtASCIIString XMLEscape(gtASCIIString strValue) { // fix special characters strValue.replace("&", "&"); strValue.replace("<", "<"); strValue.replace(">", ">"); strValue.replace("'", "'"); strValue.replace("\"", """); return strValue; }
// --------------------------------------------------------------------------- // Name: osPortAddress::osPortAddress // Description: Constructor - Represents a remote machine port. (uses ASCII string) // Arguments: hostName - The host on which the port resides. // remotePortNumber - The port number on the local machine. // Author: AMD Developer Tools Team // Date: 13/9/2010 // --------------------------------------------------------------------------- osPortAddress::osPortAddress(const gtASCIIString& hostName, unsigned short remotePortNumber) { // Convert the host name to a unicode string: gtString hostNameUnicode; hostNameUnicode.fromASCIIString(hostName.asCharArray()); setAsRemotePortAddress(hostNameUnicode, remotePortNumber); }
// --------------------------------------------------------------------------- // Name: GraphicsServerCommunication::ConnectProcess // Description: Connect to Graphics server's specific process // Arguments: strPid - Process ID to connect to and update current PID, (optional) can be "", function will then connect to known process // Return Val: bool - Success / failure // --------------------------------------------------------------------------- bool GraphicsServerCommunication::ConnectProcess(const gtASCIIString strPid, const gtASCIIString& apiType) { gtASCIIString strWebResponse; bool retVal = false; if ((0 < strPid.length()) && (true == strPid.isIntegerNumber())) { m_strPid = strPid; } if (apiType.isEmpty() == false) { if (apiType == GP_GRAPHICS_SERVER_DX12_API_TYPE || apiType == GP_GRAPHICS_SERVER_VULKAN_API_TYPE) { m_strApiHttpCommand = "/"; m_strApiHttpCommand.append(apiType); } else { gtString msg = L"Wrong API given : "; msg.append(gtString().fromASCIIString(apiType.asCharArray())); GT_ASSERT_EX(false, msg.asCharArray()); m_strApiHttpCommand = ""; } } if (m_strPid.isEmpty() == false && m_strApiHttpCommand.isEmpty() == false) { // Connect gtASCIIString showStack = m_strApiHttpCommand; retVal = SendCommandPid(showStack.append("/ShowStack"), strWebResponse, ""); if (retVal) { gtASCIIString timeControl = m_strApiHttpCommand; retVal = SendCommandPid(timeControl.append("/PushLayer=TimeControl") , strWebResponse, ""); } if (retVal) { gtASCIIString tcSettings = m_strApiHttpCommand; retVal = SendCommandPid(tcSettings.append("/TC/Settings.xml"), strWebResponse, ""); } } return retVal; }
//-------------------------------------------------------------------------- /// Read a chunk of metadata XML to populate all members. /// \param inMetadataXML A string of XML metadata that will be parsed. /// \returns True if parsing was successful. False if an error occurred. //-------------------------------------------------------------------------- bool TraceMetadata::ReadFromXML(const gtASCIIString& inMetadataXML) { TiXmlDocument xmlDocument; xmlDocument.Parse(inMetadataXML.asCharArray()); // Create a visitor, which will populate "this" TraceMetadata instance. MetadataXMLVisitor elementVisitor(this); bool bVisistedSuccessfully = xmlDocument.Accept(&elementVisitor); return bVisistedSuccessfully; }
//-------------------------------------------------------------------------- /// 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; }
bool GraphicsServerCommunication::GetNumCapturedFrames(gtASCIIString& executable, int& numFrames) { bool retVal = true; OS_DEBUG_LOG_TRACER_WITH_RETVAL(retVal); numFrames = 3; if (executable.length() > 6) { numFrames = 4; } return retVal; }
// --------------------------------------------------------------------------- // Name: GraphicsServerCommunication::SendCommandPid // Description: locate available Graphics server // Arguments: strWebResponse(return) - web string returned. // strCommand - URL command to send to server // strPid - optional Process ID string, leave empty, "", to use existing Process ID // Return Val: bool - Success / failure // --------------------------------------------------------------------------- bool GraphicsServerCommunication::SendCommandPid(const gtASCIIString& strCommand, gtASCIIString& strWebResponse, const gtASCIIString& strPid, bool isResendAllowed) { bool retVal = false; gtASCIIString strConnectPid; gtASCIIString strQueryUrl; if ((0 < strPid.length()) && (true == strPid.isIntegerNumber())) { strConnectPid = strPid; } else if ((0 < m_strPid.length()) && (true == m_strPid.isIntegerNumber())) { strConnectPid = m_strPid; } strQueryUrl = "/"; strQueryUrl.append(strConnectPid); strQueryUrl.append(strCommand); retVal = RequestData(strQueryUrl, strWebResponse, isResendAllowed); return retVal; }
// --------------------------------------------------------------------------- // Name: GraphicsServerCommunication::ConnectServer // Description: Connect to server with specified Address or existing address // Arguments: strServer - optional address to override and update existing information // Return Val: bool - Success / failure // --------------------------------------------------------------------------- bool GraphicsServerCommunication::ConnectServer(const gtASCIIString strServer) { bool retVal = false; m_isStopSignaled = false; if (0 < strServer.length()) { m_serverURL = strServer; } // Set the server address and port m_GPSServer = osPortAddress(m_serverURL, m_Port); m_httpClient = osHTTPClient(m_GPSServer); m_httpClient.GetTCPSocket().setReadOperationTimeOut(GRAPHIC_SERVER_READ_TIMEOUT); if (true == m_httpClient.connect()) { retVal = true; } return retVal; }
//--------------------------------------------------------------------------------------------------------------- // This function checks if argument is constant. If it is it returns true, overwise false //--------------------------------------------------------------------------------------------------------------- static bool GetConstantIDFromArgument(gtASCIIString sArgument, gtASCIIString cKey, unsigned int& nSlotID) { unsigned int nKeyPosition = (unsigned int)sArgument.find(cKey); // Check if cKey is not end of other argument if (nKeyPosition == std::string::npos || (nKeyPosition != 0 && sArgument[nKeyPosition - 1] != '>' // code can be in HTML && sArgument[nKeyPosition - 1] != ' ')) { return false; } // End of if // Looking for not digital symbols in argument unsigned int nNextNotDigit = (unsigned int)sArgument.find_first_not_of("0123456789", nKeyPosition + cKey.length()); gtASCIIString sSlotID; // this variable contains string with the slot number if (nNextNotDigit == std::string::npos) // All character after cKey are digits { sSlotID = sArgument.substr(nKeyPosition + cKey.length()); } // End of if // Check if the variable is finished with digit else if ((sArgument[nNextNotDigit] != '<' && sArgument[nNextNotDigit] != ' ' && sArgument[nNextNotDigit] != ',' && sArgument[nNextNotDigit] != '.' && sArgument[nNextNotDigit] != '[' && sArgument[nNextNotDigit] != '\n') || nNextNotDigit <= nKeyPosition + 1) { return false; } // End of else if else { sSlotID = sArgument.substr(nKeyPosition + cKey.length(), nNextNotDigit - nKeyPosition - cKey.length()); } // End of else int nScanRes = sscanf_s(sSlotID.asCharArray(), "%d", &nSlotID); if (nScanRes < 1) { Log(logERROR, "%s: Failed to read integer from Str = %s\n", __FUNCTION__, sSlotID.asCharArray()); return false; } return true; }// End of GetConstantIDFromArgument
//-------------------------------------------------------------- // LaunchAppInNewProcess //-------------------------------------------------------------- PROCESS_INFORMATION ProcessTracker::LaunchAppInNewProcess(gtASCIIString strApp, gtASCIIString strDir, gtASCIIString strArgs, osModuleArchitecture binaryType) { #ifdef _LINUX PS_UNREFERENCED_PARAMETER(binaryType); #endif LogConsole(logMESSAGE, "About to launch: %s\n", strApp.asCharArray()); LogConsole(logMESSAGE, "Params: %s\n", strArgs.asCharArray()); LogConsole(logMESSAGE, "Working Directory: %s\n", strDir.asCharArray()); // Get app directory and make it default if (strDir.isEmpty()) { size_t pos = strApp.find_last_of("\\"); if (pos != std::string::npos) { strDir = strApp.substr(0, (int)pos); if (strApp[0] == '\"') { strDir += "\""; } } } PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); #ifdef _WIN32 DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED; SetLastError(0); STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); #endif // Cmd line has to include the exe name since many apps expect the executable name to be in argv[0]! // Note argv[0] on the command line needs to be surrounded with quotes if it contains spaces. // The arguments in strArgs have already been "quoted" as they are parsed. gtASCIIString strCmdLine = AddQuotesIfStringHasSpaces(strApp.asCharArray()); strCmdLine += " "; strCmdLine += strArgs; LogConsole(logMESSAGE, "strApp: %s\n", strApp.asCharArray()); LogConsole(logMESSAGE, "strCmdLine: %s\n", strCmdLine.asCharArray()); // Attempt to initialize the environment that the new process will run in. The child process should inherit "this" environment. if (!PrelaunchEnvironmentInitialization()) { // Log a warning if this failed- initializing the environment for the new process can fail if Mantle support isn't installed. // In these cases, if the user is attempting to debug a Mantle application, they will have bigger problems to deal with. // In cases where a DX/GL app is being debugged, this warning can be ignored without any side effects. Log(logWARNING, "Environment initialization failed. If using DX/GL, it is safe to ignore this warning.\n"); } BOOL succeeded = FALSE; #ifdef _WIN32 char microDLLPath[PS_MAX_PATH]; const char* strServerPath; strServerPath = SG_GET_PATH(ServerPath); if (SG_GET_BOOL(OptionDllReplacement) == true) { DllReplacement::SetDllDirectory(binaryType == OS_X86_64_ARCHITECTURE); } // if using manual dll replacement or the AppInit_DLLs registry setting, don't use any kind of dll injection if (SG_GET_BOOL(OptionManualDllReplacement) == true || SG_GET_BOOL(OptionAppInitDll)) { succeeded = CreateProcess(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(), NULL, NULL, TRUE, dwFlags, NULL, strDir.asCharArray(), &si, &pi); } else { #ifdef X64 // can only launch 64 bit applications if (binaryType != OS_X86_64_ARCHITECTURE) { sprintf_s(microDLLPath, PS_MAX_PATH, "%s" MICRODLLNAME "%s%s.dll", SG_GET_PATH(ServerPath), GDT_DEBUG_SUFFIX, GDT_BUILD_SUFFIX); succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(), NULL, NULL, TRUE, dwFlags, NULL, strDir.asCharArray(), &si, &pi, microDLLPath); } #else if (binaryType != OS_I386_ARCHITECTURE) { sprintf_s(microDLLPath, PS_MAX_PATH, "%s" MICRODLLNAME "-x64%s%s.dll", SG_GET_PATH(ServerPath), GDT_DEBUG_SUFFIX, GDT_BUILD_SUFFIX); succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(), NULL, NULL, TRUE, dwFlags, NULL, strDir.asCharArray(), &si, &pi, microDLLPath); } #endif // X64 else { succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(), NULL, NULL, TRUE, dwFlags, NULL, strDir.asCharArray(), &si, &pi, SG_GET_PATH(MicroDLLPath)); } } #else // Create the app process succeeded = CreateProcess(strApp.asCharArray(), strCmdLine.asCharArray(), strDir.asCharArray(), &pi); #endif // _WIN32 if (!succeeded) { osSystemErrorCode systemLastError = osGetLastSystemError(); gtString systemErrorString; osGetLastSystemErrorAsString(systemErrorString); Log(logERROR, "CreateProcessAndInjectDll failed; Error %d: %s\n", systemLastError, systemErrorString.asASCIICharArray()); pi.dwProcessId = 0; } #ifdef _WIN32 else { // Check to see if the Steam.exe has been hooked and if so, set the value in shared memory // If Steam.exe was used to launch the target application, then the checks for cmd.exe and fcx.exe // need to be ignored. if (strApp.length() > 0) { if (strstr(strApp.toLowerCase().asCharArray(), "steam.exe") != NULL) { SG_SET_BOOL(SteamInjected, true); } ShowLauncherReminder(strApp.toLowerCase().asCharArray()); } else { if (strstr(strCmdLine.toLowerCase().asCharArray(), "steam.exe") != NULL) { SG_SET_BOOL(SteamInjected, true); } ShowLauncherReminder(strCmdLine.toLowerCase().asCharArray()); } } #endif // _WIN32 return pi; }
//--------------------------------------------------------------------------------------------------------------- // This function returns of slots used in code // cKey is symbol which shows what kind of slots are requested: 'c' for float constants, 'b' for boolean // constants etc. Results are kept in usedSlots. //--------------------------------------------------------------------------------------------------------------- long GetConstantsFromCode(gtASCIIString code, gtASCIIString cKey, std::list<unsigned long>& usedSlots) { long hr = 0; typedef std::map< gtASCIIString, std::vector<unsigned long> > ArgumentType; typedef std::map< gtASCIIString, std::vector<unsigned long> >::iterator ArgumentIter; ArgumentType ArgumentLength; ArgumentIter ArgIter; // This map contains infoemation about arguments lenth // i.e the third argument of the m4x4 itstruction is 4x4 matrix, // which uses 4 slots, so if shader has instruction m4x4 oPos, v0, c0 it means // c0, c1, c3 and c4 are used, that is why ArgumentLength["m4x4"][2] = 4 ArgumentLength["m3x2"].resize(3); ArgumentLength["m3x2"][0] = 1; ArgumentLength["m3x2"][1] = 1; ArgumentLength["m3x2"][2] = 2; ArgumentLength["m3x3"].resize(3); ArgumentLength["m3x3"][0] = 1; ArgumentLength["m3x3"][1] = 1; ArgumentLength["m3x3"][2] = 3; ArgumentLength["m3x4"].resize(3); ArgumentLength["m3x4"][0] = 1; ArgumentLength["m3x4"][1] = 1; ArgumentLength["m3x4"][2] = 4; ArgumentLength["m4x3"].resize(3); ArgumentLength["m4x3"][0] = 1; ArgumentLength["m4x3"][1] = 1; ArgumentLength["m4x3"][2] = 3; ArgumentLength["m4x4"].resize(3); ArgumentLength["m4x4"][0] = 1; ArgumentLength["m4x4"][1] = 1; ArgumentLength["m4x4"][2] = 4; ArgumentLength["texm3x2depth"].resize(2); ArgumentLength["texm3x2depth"][0] = 3; ArgumentLength["texm3x2depth"][1] = 2; ArgumentLength["texm3x2pad"].resize(2); ArgumentLength["texm3x2pad"][0] = 3; ArgumentLength["texm3x2pad"][1] = 2; ArgumentLength["texm3x2tex"].resize(2); ArgumentLength["texm3x2tex"][0] = 3; ArgumentLength["texm3x2tex"][1] = 2; ArgumentLength["texm3x3"].resize(2); ArgumentLength["texm3x3"][0] = 3; ArgumentLength["texm3x3"][1] = 3; ArgumentLength["texm3x3pad"].resize(2); ArgumentLength["texm3x3pad"][0] = 3; ArgumentLength["texm3x3pad"][1] = 3; ArgumentLength["texm3x3spec"].resize(2); ArgumentLength["texm3x3spec"][0] = 3; ArgumentLength["texm3x3spec"][1] = 3; ArgumentLength["texm3x3tex"].resize(2); ArgumentLength["texm3x3tex"][0] = 3; ArgumentLength["texm3x3tex"][1] = 3; ArgumentLength["texm3x3vspec"].resize(2); ArgumentLength["texm3x3vspec"][0] = 3; ArgumentLength["texm3x3vspec"][1] = 3; unsigned int nStartOfToken = 0; // This loop splits code to tokens while (nStartOfToken != std::string::npos) { unsigned int nNextToken = (unsigned int)code.find('\n', nStartOfToken + 1); // comment gtASCIIString sToken = (nNextToken == std::string::npos) ? code.substr(nStartOfToken) : code.substr(nStartOfToken, nNextToken - nStartOfToken); if (sToken.length() > 1 && sToken.substr(0, 1) != "//") // Skip comments and empty strings { unsigned int nStartOfInstruction = (unsigned int)sToken.find_first_not_of(" \n", 0); // comment unsigned int nEndOfInstruction = (unsigned int)sToken.find(' ', nStartOfInstruction); if (nEndOfInstruction == std::string::npos) { nStartOfToken = nNextToken; continue; } gtASCIIString sCommand = sToken.substr(nStartOfInstruction, nEndOfInstruction - nStartOfInstruction); unsigned int nArgument = 0; unsigned int nStartOfArgument = nEndOfInstruction + 1; unsigned int nEndOfArgument; do // This separates arguments of command { nEndOfArgument = (unsigned int)sToken.find(',', nStartOfArgument); gtASCIIString sArgument = (nEndOfArgument == std::string::npos) ? sToken.substr(nStartOfArgument) : sToken.substr(nStartOfArgument, nEndOfArgument - nStartOfArgument); unsigned int nSlotID; if (GetConstantIDFromArgument(sArgument, cKey, nSlotID) == false) { nArgument++; nStartOfArgument = nEndOfArgument + 1; continue; } // Calculation of used constants. Default is 1. Next 2 lines check if the command in "special cases map" ArgIter = ArgumentLength.find(sCommand); int nArgsCount = (ArgIter == ArgumentLength.end()) ? 1 : // If coommand has not been find the lenth of argument is supposed 1 (ArgIter->second.size() > nArgument ? ArgIter->second[nArgument] : 1); // If there no information for considered argument its lenth is supposed 1 // This loop adds variables in the used constants list for (unsigned int i = nSlotID; i < nSlotID + nArgsCount; i++) { bool nNotFind = true; for (std::list<unsigned long>::const_iterator iter = usedSlots.begin(); iter != usedSlots.end(); ++iter) { if (*iter == i) { nNotFind = false; break; } } if (nNotFind) { usedSlots.push_back(i); } // End of if }// End of for nArgument++; nStartOfArgument = nEndOfArgument + 1; } while (nEndOfArgument != std::string::npos); } // End of if ( sToken.size() > 1 && sToken.substr( 0, 1 ) != "//" ) nStartOfToken = nNextToken; }// End of while ( nFound != string::npos ) return hr; }// End of GetConstantsFromCode
//----------------------------------------------------------------------------- /// Get the API parameters as a single string. Build the string from the /// individual parameters if necessary /// \return parameter string //----------------------------------------------------------------------------- const char* DX12APIEntry::GetParameterString() const { static gtASCIIString parameterString; if (mNumParameters == 0) { return mParameters.asCharArray(); } else { parameterString = ""; // get the API function parameters from the raw memory buffer int arrayCount = 0; char* buffer = mParameterBuffer; if (buffer != nullptr) { for (UINT32 loop = 0; loop < mNumParameters; loop++) { char* ptr = buffer; PARAMETER_TYPE paramType; memcpy(¶mType, ptr, sizeof(PARAMETER_TYPE)); ptr += sizeof(PARAMETER_TYPE); unsigned char length = *ptr++; if (length < BYTES_PER_PARAMETER) { // if an array token is found, add an opening brace and start the array elements countdown if (paramType == PARAMETER_ARRAY) { memcpy(&arrayCount, ptr, sizeof(unsigned int)); parameterString += "[ "; } else { char parameter[BYTES_PER_PARAMETER] = {}; GetParameterAsString(paramType, length, ptr, parameter); parameterString += parameter; // check to see if this is the last array element. If so, output a closing brace // before the comma separator (if needed) if (arrayCount > 0) { arrayCount--; if (arrayCount == 0) { parameterString += " ]"; } } // if there are more parameters to come, insert a comma delimiter if ((loop + 1) < mNumParameters) { parameterString += ", "; } } } // point to next parameter in the buffer buffer += BYTES_PER_PARAMETER; } } return parameterString.asCharArray(); } }
//-------------------------------------------------------------------------- /// 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(gtASCIIString& inFullResponseString, bool inbSaveResponseToFile) { ModernAPILayerManager* parentLayerManager = GetParentLayerManager(); if (parentLayerManager->InCapturePlayer()) { const std::string& metadataFile = parentLayerManager->GetPathToTargetMetadataFile(); if (metadataFile.length() > 0) { // Read the metadata file and store the contents in a structure. TraceMetadata traceMetadata; traceMetadata.mFrameInfo = new FrameInfo; bool bReadMetadataFileSuccessfully = ReadMetadataFile(metadataFile, &traceMetadata); if (bReadMetadataFileSuccessfully) { gtASCIIString traceContents; bool bReadTraceSuccessfully = LoadTraceFile(traceMetadata.mPathToTraceFile, traceContents); if (bReadTraceSuccessfully) { // At this point the full trace response text should be loaded into our string and ready to be sent back to the client. mCmdLinkedTrace.Send(traceContents.asCharArray()); } else { Log(logERROR, "Failed to read trace file at '%s'.", traceMetadata.mPathToTraceFile.c_str()); } } else { Log(logERROR, "Failed to read metadata file at '%s'.", metadataFile.c_str()); } // Destroy the FrameInfo instance that was created above. SAFE_DELETE(traceMetadata.mFrameInfo); } else { Log(logERROR, "Failed to locate valid path to trace metadata file."); } } else { gtASCIIString traceHeaderBlock; bool bBuiltHeaderSuccessfully = GenerateLinkedTraceHeader(traceHeaderBlock); if (bBuiltHeaderSuccessfully) { bool bKeypressTrigger = parentLayerManager->IsTraceTriggeredByKeypress(); // Collect a trace and generate the trace metadata string. Write the trace and metadata files to disk. std::string metadataXMLString; bool bWriteMetadataSuccessful = WriteTraceAndMetadataFiles(traceHeaderBlock, inFullResponseString, metadataXMLString); // If the trace wasn't triggered by a keypress, we'll need to send a response back through either of the following commands. CommandResponse& frameCaptureWithSaveResponse = (inbSaveResponseToFile == true) ? parentLayerManager->mCmdFrameCaptureWithSave : mCmdLinkedTrace; if (bWriteMetadataSuccessful) { // We only need to send the response back through a request if the client triggered collection. if (!bKeypressTrigger) { // Check if we want to cache the response to disk, or return it as-is. if (inbSaveResponseToFile) { if (bWriteMetadataSuccessful) { // Send a response back to the client indicating which trace metadata file was written to disk. frameCaptureWithSaveResponse.Send(metadataXMLString.c_str()); } else { Log(logERROR, "Failed to write trace metadata XML.\n"); frameCaptureWithSaveResponse.Send("Failed"); } } else { // Send a response containing the API and GPU trace text. frameCaptureWithSaveResponse.Send(inFullResponseString.asCharArray()); } } else { Log(logMESSAGE, "Successfully traced frame %d.\n", parentLayerManager->GetFrameCount()); } } else { Log(logERROR, "Failed to write trace metadata XML.\n"); // If a failed trace collection was triggered by a command, we need to respond with an error message. if (!bKeypressTrigger) { frameCaptureWithSaveResponse.Send("Failed"); } } } } }