void _LogFooter(void) { char szLibPath [ PS_MAX_PATH ]; #if defined (WIN32) GetModuleFileNameA(NULL, szLibPath, PS_MAX_PATH); #elif defined (_LINUX) if (program_invocation_name[0] == '/') { // file contains full path already, so don't append path sprintf_s(szLibPath, PS_MAX_PATH, "%s", program_invocation_name); } else { char currentDir[PS_MAX_PATH]; getcwd(currentDir, PS_MAX_PATH); sprintf_s(szLibPath, PS_MAX_PATH, "%s/%s", currentDir, program_invocation_name); } #endif _Log(logRAW, "App : %s\n", szLibPath); _Log(logRAW, "PID: %i\n", osGetCurrentProcessId()); _Log(logRAW, "Time: %s\n", GetTimeStr().asCharArray()); _Log(logRAW, "--------------THE END------------------\n"); }
//-------------------------------------------------------------------------- /// 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; }
//----------------------------------------------------------------------------- /// InitCommunication /// /// This function only needs to be called by wrapper plugins. It sets up the /// inter-process communication between the wrapper (which is in the app's /// process space) and the PerfServer. //----------------------------------------------------------------------------- bool InitCommunication(const char* strShortDescription, ProcessRequest_type pProcessRequestCallback) { unsigned long pid = osGetCurrentProcessId(); #ifdef _WIN32 sprintf_s(g_strSharedMemoryName, PS_MAX_PATH, "%lu/%s", pid, strShortDescription); #else // the '/' character can't be used as a filename in Linux, so just use the plugin name as the shared memory name // (ignore the process ID) sprintf_s(g_strSharedMemoryName, PS_MAX_PATH, "%lu %s", pid, strShortDescription); #endif if (smCreate(g_strSharedMemoryName, 100, sizeof(HTTPRequestHeader)) == false) { Log(logERROR, "InitCommunication: Can't open or create SharedMemory for %s.\n", strShortDescription); return false; } if (smOpen("PLUGINS_TO_GPS") == false) { smClose(g_strSharedMemoryName); Log(logERROR, "InitCommunication: Can't open SharedMemory for PLUGINS_TO_GPS.\n"); return false; } // store a local pointer to the ProcessRequest function g_processRequest = pProcessRequestCallback; if (g_processRequest == NULL) { smClose(g_strSharedMemoryName); Log(logERROR, "InitCommunication: ProcessRequest is NULL\n"); return false; } // protect the maps from being changed by other threads using the mutex ScopeLock lock(s_mutex); g_requestMap.clear(); g_pBufferedResponse = NULL; g_uBufferedResponseSize = 0; return true; }
//-------------------------------------------------------------------------- /// 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; }
// Generate a composite log message - then finally send to handler to write and/or display void _Log(enum LogType type, const char* fmt, ...) { // check to see if logging of the level is enabled, or if s_LogConsole is specified // if not, don't process it. int logLevel = SG_GET_INT(OptionLogLevel); if (((type - logERROR) > logLevel && s_LogConsole == false)) { return; } // Define buffer to store maximum current log message. // Different destinations will receive a subset of this string. // On Win32 the entire string is passed to OutputDebugString() char fullString[PS_LOG_MAX_LENGTH] = ""; int nLen = 0; int nSize; bool truncated = false; char* pLogString; // String to print to logfile char* pConsole; // String to print to console doesn't include debug information char* pRaw; // Raw string passed in without any additional decoration if (truncated == false) { // Prepend "PerfStudio: " for windows OutputDebugString() messages nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "PerfStudio: "); if ((truncated = (nSize == -1)) == false) { nLen += nSize; } } pLogString = &fullString[nLen]; // logfile message doesn't include above WIN32 section #if (defined PS_LOG_DEBUG) && (defined _DEBUG) if (truncated == false) { // In debug builds, include the __FILE__, __LINE__ and __FUNCTION__ information nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "%s(%d) : %s(): ", s_LogFile, s_LogLine, s_LogFunction); if ((truncated = (nSize == -1)) == false) { nLen += nSize; } } #endif // Prepend accurate timestamp if (truncated == false) { gtASCIIString time = GetMicroTimeStr(); time = time.substr(12); nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "%-14s: ", time.asCharArray()); if ((truncated = (nSize == -1)) == false) { nLen += nSize; } } pConsole = &fullString[nLen]; // String to print to console starts here if (truncated == false) { // Add the message type string switch (type) { case logERROR: nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "Error: "); break; case logWARNING: nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "Warning: "); break; case logMESSAGE: nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "Message: "); break; case logTRACE: nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "Trace: "); break; case logASSERT: nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "Assert: "); break; case logDEBUG: nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "Debug: "); break; case logRAW: // Skip nSize = 0; break; default: nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "Unknown: "); break; } if ((truncated = (nSize == -1)) == false) { nLen += nSize; } } // Add the module identifier if (s_LogModule && (truncated == false)) { nSize = _snprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, "PID: %10u TID: %10u %-14s: ", osGetCurrentProcessId(), osGetCurrentThreadId(), s_LogModule); if ((truncated = (nSize == -1)) == false) { nLen += nSize; } } if (truncated == false) { if (SG_GET_INT(OptionLogLevel) >= logTRACE - logERROR) { // Add the indent for (int i = 0; (i < logIndent) && (nLen < PS_LOG_MAX_LENGTH - 1); i++, nLen++) { fullString[nLen] = ' '; } fullString[nLen] = 0; // Ensure string in buffer remains null terminated truncated = (nLen == PS_LOG_MAX_LENGTH - 1); } } pRaw = &fullString[nLen]; // Raw undecorated string starts here // Add the actual Log Message if (truncated == false) { va_list arg_ptr; va_start(arg_ptr, fmt); nSize = vsnprintf_s(&fullString[nLen], PS_LOG_MAX_LENGTH - nLen, _TRUNCATE, fmt, arg_ptr); if ((truncated = (nSize == -1)) == false) { nLen += nSize; } va_end(arg_ptr); } // Check if message has been truncated and clean up accordingly if (truncated == true) { // Truncation occurred - change end of line to reflect this char truncationString[] = " ... \n"; sprintf_s(&fullString[PS_LOG_MAX_LENGTH - sizeof(truncationString)], sizeof(truncationString), "%s", truncationString); } // Message Constructed. Print to Log, Console, and OutputDebugString as necessary if (type == logRAW) { if (s_LogConsole) { // Send string to console printf("%s", pRaw); } _logWrite(pRaw); } else { if (s_LogConsole) { // Console messages are always printed in console and in log file // regardless of logLEVEL printf("%s", pConsole); _logWrite(pLogString); OutputDebugString(fullString); } else { // not a console message - filter based on log level if ((type - logERROR) <= SG_GET_INT(OptionLogLevel)) { if (type == logTRACE) { // Trace messages also go to the console printf("%s", pConsole); } _logWrite(pLogString); OutputDebugString(fullString); } } } }
//----------------------------------------------------------------------------- /// Put up a dialog box to give the user time to attach a debugger to the /// application rather than the perf studio app. /// \param serverName the name of the server plugin loaded into the target /// application /// \param initialized has the server plugin been initialized /// \return 0 if successful, non-zero on error //----------------------------------------------------------------------------- int ServerUtils::CheckForDebuggerAttach(const char* serverName, bool initialized) { char commandString[1024]; int retVal = 0; static bool alreadyChecked = false; // only pause application once. static const char fmtString[] = "The application has been paused to allow GDB to be attached to the process.\nApplication name: %s\nOpen a terminal and cd to where the %s.so is running from\n(so that gdb can load debug symbols). Use:\n\nsudo gdb attach %d\n\nPress OK to continue"; char message[sizeof(fmtString) + 10 + PS_MAX_PATH]; // Add extra bytes for process ID if (SG_GET_BOOL(OptionBreak) && !alreadyChecked && initialized) // did we run PerfServer with --break option { alreadyChecked = true; sprintf_s(message, sizeof(message), fmtString, program_invocation_short_name, serverName, osGetCurrentProcessId()); sprintf(commandString, "xmessage \"%s\" -center -buttons OK", message); retVal = system(commandString); } return retVal; }