/// Check the wrappers depending on whether PerfStudio is using DLL replacement or not. /// If using DLL replacement, PerfStudio's versions of the API dll's (D3D12.dll, dxgi.dll etc) /// will have been loaded by the OS. The UpdateHooks() method needs to be called on these /// replaced Dll's to do some initialization that can't be done from DllMain, such as loading /// in the system dll of the same name and getting the addresses of the real function /// pointers. /// If not using dll replacement, CheckWrapperOnLoadLibrary() is called, which loads the /// appropriate PerfStudio server plugin, based on which system dll's have been loaded (ie /// if D3D12.dll has been loaded, then load DX12Server.dll and call its UpdateHooks method static void CheckWrappers() { if (SG_GET_BOOL(OptionDllReplacement) == false) { CheckWrapperOnLoadLibrary(); } else { UpdateHooksOnLoadLibrary(); } }
// Returns reference to file that is currently being used. // const char* GetLogFilename() { if (SG_GET_BOOL(OptionNoLogfile)) { return (NULL); } else { return (SG_GET_PATH(LogfilePath)); } }
//-------------------------------------------------------------------------- /// Constructor //-------------------------------------------------------------------------- FrameDebugger::FrameDebugger() { m_dwBreakPoint = 0; m_bAutoRenderTarget = true; m_ulDrawCallCounter = 0; m_bForceClear = true; m_ClearColor[0] = 0.0f; // red m_ClearColor[1] = 0.4f; // green m_ClearColor[2] = 0.8f; // blue m_ClearColor[3] = 1.0f; // alpha m_iWireframeColor = SG_GET_INT(OptionWireFrameColor); m_bWireframeOverlay = SG_GET_BOOL(OptionWireFrameOverlay); // 1 = pink/purple, 2 = green, 3 = blue if (2 == m_iWireframeColor) { // green m_fWireframeOverlayColor[0] = 0.0f; m_fWireframeOverlayColor[1] = 1.0f; m_fWireframeOverlayColor[2] = 0.0f; } else if (3 == m_iWireframeColor) { // blue m_fWireframeOverlayColor[0] = 0.0f; m_fWireframeOverlayColor[1] = 0.0f; m_fWireframeOverlayColor[2] = 1.0f; } else { // 1 or other // pink / purple m_fWireframeOverlayColor[0] = 1.0f; m_fWireframeOverlayColor[1] = 0.0f; m_fWireframeOverlayColor[2] = 1.0f; } m_fWireframeOverlayColor[3] = 1.0f; AddCommand(CONTENT_XML, "breakpoint", "BreakPoint", CMD_BREAKPOINT, NO_DISPLAY, INCLUDE, m_dwBreakPoint); AddCommand(CONTENT_XML, "autorendertarget", "Auto Render Target", CMD_AUTORENDERTARGET, NO_DISPLAY, INCLUDE, m_bAutoRenderTarget); AddCommand(CONTENT_XML, "forceclear", "Force Clear", CMD_FORCECLEAR, NO_DISPLAY, INCLUDE, m_bForceClear); AddCommand(CONTENT_XML, "wireframeoverlay", "WireFrame Overlay", CMD_WIREFRAMEOVERLAY, NO_DISPLAY, INCLUDE, m_bWireframeOverlay); AddCommand(CONTENT_XML, "stats", "Stats", CMD_STATISTICS, NO_DISPLAY, INCLUDE, m_Stats); AddCommand(CONTENT_XML, "currentdrawcall", "Current Draw Call", CMD_CURRENTDRAWCALL, NO_DISPLAY, INCLUDE, m_CurrentDrawCall); AddCommand(CONTENT_XML, "DrawCallList", "DrawCallList", "DrawCallList.xml", NO_DISPLAY, INCLUDE, m_drawCallList); AddCommand(CONTENT_XML, "ConfigHUD", "Config HUD", CMD_CONFIGHUD, NO_DISPLAY, INCLUDE, m_bConfigHUD); SetLayerName("FrameDebugger"); }
void ProcessTracker::UpdateListOfInjectedProcesses() { this->m_injectedProcessList.clear(); ProcessInfoList localProcessInfoArray; #ifdef _WIN32 // If using DLL replacement, get any process running that has a replaced // dll loaded into it // just D3D12.dll & OpenGL32.dll at the moment (D3D11 / GLES can be added later if needed) if (SG_GET_BOOL(OptionDllReplacement) == true) { get_process_list("D3D12.dll", localProcessInfoArray); get_process_list("OpenGL32.dll", localProcessInfoArray); } else { get_process_list(MICRODLLNAME GDT_DEBUG_SUFFIX GDT_BUILD_SUFFIX ".dll", localProcessInfoArray); get_process_list(MICRODLLNAME "-x64" GDT_DEBUG_SUFFIX GDT_BUILD_SUFFIX ".dll", localProcessInfoArray); } #else get_process_list("GLServer" GDT_PROJECT_SUFFIX ".so", localProcessInfoArray); get_process_list("GLESServer" GDT_PROJECT_SUFFIX ".so", localProcessInfoArray); #endif for (ProcessInfoList::const_iterator iter = localProcessInfoArray.begin(); iter != localProcessInfoArray.end(); ++iter) { // find if we've already listed this process (because it has more than 1 plugin attached) bool bAlreadyHaveProcess = false; for (ProcessInfoList::iterator aProc = this->m_injectedProcessList.begin(); aProc != this->m_injectedProcessList.end(); ++aProc) { if (aProc->th32ProcessID == iter->th32ProcessID) { bAlreadyHaveProcess = true; } } // only add new processes if (bAlreadyHaveProcess == false) { this->m_injectedProcessList.push_back(*iter); } } }
/// Pause application to allow debugger to attach /// This functionality only happens once - on the first call to LoadLibrary. static void CheckForDebuggerAttach(void) { static bool alreadyChecked = false; // only pause application once. static const char fmtString[] = "The application has been paused to allow the Visual Studio debugger to be attached to the process.\n\nProcess ID = %d \n\nProcess Name: %s\n\nPress OK to continue"; if (SG_GET_BOOL(OptionBreak) && !alreadyChecked) // did we run PerfServer with --break option { char message[sizeof(fmtString) + 4 + MAX_PATH]; // Add 4 extra bytes for process ID - allows for up to 6 digits of process ID in total char modulename[MAX_PATH]; GetModuleFileNameA(NULL, modulename, MAX_PATH); alreadyChecked = true; sprintf_s(message, sizeof(message), fmtString, GetCurrentProcessId(), modulename); MessageBoxStop(message); } }
//----------------------------------------------------------------------------- /// 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; }
//-------------------------------------------------------------- /// Is micro dll allowed to be injected into this process? /// check the current executable with the filelist specified. If /// it is in the list, or the list contains 'all', then injection /// is allowed. /// \param modulename name of the executable being started /// \returns true if injection is allowed, false otherwise static bool InjectionAllowed(const char* modulename) { // if not using AppInit_DLLs registry key, always allow injection if (SG_GET_BOOL(OptionAppInitDll) == false) { return true; } // get the filename from the path. If there's an error here, don't inject char path[PS_MAX_PATH]; char* filename; DWORD res = GetFullPathName(modulename, PS_MAX_PATH, path, &filename); if (res == 0) { return false; } gtASCIIString fileString = SG_GET_PATH(AppInitDllFileList); // if 'all' specified, always inject if (fileString.compareNoCase("all") == 0) { return true; } // split the list up and see if the current exe is in the list std::list<gtASCIIString> fileList; fileString.Split(",", false, fileList); for (std::list<gtASCIIString>::const_iterator it = fileList.begin(); it != fileList.end(); ++it) { if ((*it).compareNoCase(filename) == 0) { // file is in list, inject return true; } } return false; }
//-------------------------------------------------------------- // 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; }
//-------------------------------------------------------------- /// Generates XML that describes the injected processes and which /// plugins were injected. /// \return string containing the XML //-------------------------------------------------------------- gtASCIIString ProcessTracker::GetProcessesXML() { std::unordered_map< DWORD, gtASCIIString > procXMLMap; this->UpdateListOfInjectedProcesses(); ProcessInfoList injectedProcesses = this->GetListOfInjectedProcesses(); WrapperMap wrappers = GetWrapperMap(); // the strPlatform is named this way to match options in the client. #ifdef X64 gtASCIIString strPlatform = "Win64"; #else gtASCIIString strPlatform = "Win32"; #endif #ifndef CODEXL_GRAPHICS if (injectedProcesses.empty() == true) { LogConsole(logERROR, "There are no processes running which have been injected with the GPU PerfStudio server plugin\n"); LogConsole(logERROR, "Please ensure that the server has the same bitness as the target application\n"); LogConsole(logERROR, "For example, use the 64-bit GPU PerfStudio server with a 64-bit application\n"); } #endif for (ProcessInfoList::iterator procIter = injectedProcesses.begin(); procIter != injectedProcesses.end(); ++procIter) { DWORD pid = procIter->th32ProcessID; // only add the process info if it wasn't already found. if (procXMLMap.find(pid) == procXMLMap.end()) { // insert new process and the process info gtASCIIString tmpString = XML("Name", XMLEscape(procIter->szExeFile).asCharArray()); tmpString += XML("PID", pid); tmpString += XML("Path", XMLEscape(procIter->szPath).asCharArray()); tmpString += XML("Platform", strPlatform.asCharArray()); procXMLMap[ pid ] = tmpString; // if this process is the one that was launched, then we know what the args and working directory were. // we could probably get the actual args and working dir from MicroDLL if it is a different app though, which // would be the case if this app uses a launcher app. if (m_injectedAppName.compare(procIter->szPath) == 0) { tmpString = XML("Args", XMLEscape(m_injectedAppArgs.c_str()).asCharArray()); tmpString += XML("WDir", XMLEscape(m_injectedAppDir.c_str()).asCharArray()); procXMLMap[ pid ] += tmpString.asCharArray(); } } // add an API node for each of the wrappers that are injected into the app for (WrapperMap::const_iterator wrapperIter = wrappers.begin(); wrapperIter != wrappers.end(); ++wrapperIter) { // List of plugin extensions to check for. On Windows, test for 32 and 64 bit plugins. // On linux, just check for the plugin corresponding to the server bitness static const char* pluginExtensions[] = { #ifdef WIN32 GDT_DEBUG_SUFFIX GDT_BUILD_SUFFIX "." DLL_EXTENSION, "-x64" GDT_DEBUG_SUFFIX GDT_BUILD_SUFFIX "." DLL_EXTENSION #else GDT_PROJECT_SUFFIX "." DLL_EXTENSION #endif }; int numPlugins = sizeof(pluginExtensions) / sizeof(pluginExtensions[0]); for (int loop = 0; loop < numPlugins; loop++) { // check to see if this wrapper is in the application gtASCIIString strPluginName = wrapperIter->second.strPluginName; if (SG_GET_BOOL(OptionDllReplacement) == false) { strPluginName += pluginExtensions[loop]; } if (IsLibraryLoadedInProcess(pid, strPluginName.asCharArray(), NULL)) { bool attached = false; if (g_activeWrappersMap.find(FormatText("%lu/%s", pid, wrapperIter->first.c_str()).asCharArray()) != g_activeWrappersMap.end()) { // the pid/plugin string was listed in the active wrappers map, so the plugin must be active. attached = true; } procXMLMap[pid] += XMLAttrib("API", FormatText("attached='%s'", attached ? "TRUE" : "FALSE").asCharArray(), wrapperIter->second.strPluginShortDesc.asCharArray()); } } } } // concatenate the process XML and additional info gtASCIIString xml; for (std::unordered_map< DWORD, gtASCIIString >::iterator iterP = procXMLMap.begin(); iterP != procXMLMap.end(); iterP++) { xml += XML("Process", (iterP->second).asCharArray()); } gtASCIIString out = XMLHeader(); out += XML("ProcessList", xml.asCharArray()); return out; }
bool ProcessTracker::HandleRequest(HTTPRequestHeader* pRequestHeader, CommunicationID requestID, NetSocket* pClientSocket, bool renderLoopStalled) { char* ptr = pRequestHeader->GetUrl(); char* sCmd = &ptr[1]; #ifdef CODEXL_GRAPHICS #ifdef USE_GRAPHICS_SERVER_STATUS_RETURN_CODES // Handle process not running condition if (pRequestHeader->CheckProcessStillRunning() == false) { Log(logMESSAGE, "Rejecting the command above due to process no longer running: %s\n", pRequestHeader->GetUrl()); // Need to return the correct error data for process not running HandleServerStatusResponse(GRAPHICS_SERVER_STATE_PROCESS_NOT_RUNNING, pRequestHeader, pClientSocket); // This request never gets passed to the graphics server. return true; } // Check if renering has stalled. if (renderLoopStalled == true) { Log(logMESSAGE, "Rejecting the command above due to render stall: %s\n", pRequestHeader->GetUrl()); // Need to return the correct error data for process not running HandleServerStatusResponse(GRAPHICS_SERVER_STATE_STALLED, pRequestHeader, pClientSocket); // This request never gets passed to the graphics server. return true; } #else UNREFERENCED_PARAMETER(renderLoopStalled); #endif #else PS_UNREFERENCED_PARAMETER(renderLoopStalled); #endif #ifdef _WIN32 // if using AppInit_Dll, clear the registry as soon as a process.xml request is sent. // TODO: move this to where a connection has definately been made if (SG_GET_BOOL(OptionAppInitDll) == true) { if (registryCleared == false) { RestoreAppInit(); registryCleared = true; } } #endif if (IsToken(&sCmd, "inject?")) { DoInjectCommand(requestID, &sCmd, pClientSocket); } else { // do commands that depend on PID for (WrapperMap::iterator wrapperIter = g_activeWrappersMap.begin(); wrapperIter != g_activeWrappersMap.end(); ++wrapperIter) { // parse out the process ID unsigned long pid = 0; sscanf_s(wrapperIter->first.c_str(), "%lu/", &pid); // make PID string for easier parsing of the command gtASCIIString strPID; strPID.appendFormattedString("%lu", pid); gtASCIIString strPidSlashPlugin = wrapperIter->first.c_str(); // the key in the activeWrappersMap is formatted as pid/plugin (ie: "435/DX11") // and this conveniently matches the format of the commands coming into the server, so parse for matching commands gtASCIIString tmpString = strPidSlashPlugin; tmpString += "/"; if (IsToken(&sCmd, tmpString.asCharArray())) { // we know this command is targetting the current plugin pRequestHeader->SetUrl(sCmd); // pass the request to the plugin #ifdef _WIN32 const char* memoryName = strPidSlashPlugin.asCharArray(); #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) char memoryName[PS_MAX_PATH]; char pluginShortDesc[ PS_MAX_PATH ]; sscanf_s(strPidSlashPlugin.asCharArray(), "%lu/%s", &pid, pluginShortDesc, sizeof(pluginShortDesc)); sprintf_s(memoryName, PS_MAX_PATH, "%lu %s", pid, pluginShortDesc); #endif if (PassRequestToPlugin(memoryName, pRequestHeader, pid, pClientSocket)) { return true; } else { Log(logERROR, "Request '%s' is not targeted to an active plugin.\n", GetRequestText(requestID)); SendHTMLResponse(requestID, "<html>Error: Targeted plugin is not currently active.</html>", pClientSocket); } } else { tmpString = strPID; tmpString += "/Kill"; if (IsToken(&sCmd, tmpString.asCharArray())) { if (KillProcess(pid)) { SendTextResponse(requestID, "Ok", pClientSocket); // if the app was launched from the command line or drag and drop // then killing the app should cause the server to shutdown also if (g_bAppSpecifiedAtCmdLine) { g_shutdownEvent.Signal(); CloseStreamThread(); CloseStreamSockets(); SendTextResponse(requestID, "OK", pClientSocket); return true; } } else { Log(logERROR, "Failed to Kill the process\n"); SendTextResponse(requestID, "Error: Failed to Kill the process.", pClientSocket); } } } } } return false; }
///////////////////////////////////////////////////////////////////////////////////////////////////////// /// Checks to see if the app has stopped rendering (or ever rendered at all). /// The result is stored in g_bRenderLoopStalled and it is used when incoming messages are processed. ///////////////////////////////////////////////////////////////////////////////////////////////////////// void RenderStallThread::CheckForRenderStall() { static double lastPresentTime = 0.0f; int delay = 0; int outerDelay = 0; NamedEvent shutdownEvent; bool opened = shutdownEvent.Open("GPS_SHUTDOWN_SERVER"); while (opened && false == shutdownEvent.IsSignaled()) { if (GetServerShutdownState() == true) { return; } // Get the last value that the server wrote into the slot. It may never have written to the slot. double checkTime = SG_GET_DOUBLE(LastPresentTime); bool bForceStall = SG_GET_BOOL(ForceRenderStallState); // Until the server actually renders lastPresentTime will always be 0 if (lastPresentTime == 0.0f) { lastPresentTime = checkTime; } // Get the diff between renders double diffTime = checkTime - lastPresentTime; // record the last time so that our diffs are correct next time around lastPresentTime = checkTime; if (bForceStall == true) { SetServerStalledState(true); Log(logMESSAGE, "Keypress capture is blocking client commands.\n"); } else { // If there is no diff then there was no rendering so we have stalled. if (diffTime == 0.0f) { // The server has stopped rendering or has never rendered if (delay > GRAPHICS_SERVER_STATUS_STALL_THRESHOLD_TIME) { SetServerStalledState(true); #ifdef CODEXL_GRAPHICS #ifdef USE_GRAPHICS_SERVER_STATUS_RETURN_CODES int inFlight = RequestsInFlightDatabase::Instance()->InFlightCount(); Log(logMESSAGE, "RenderStallThread::CheckForRenderStall(): App has not rendered for %ld (ms), inFlightCount = %d\n", outerDelay, inFlight); if (inFlight > 0) { // Send stalled status back to the messages in flight Log(logMESSAGE, "RenderStallThread::CheckForRenderStall(): Check messages in flight to see if process is still running\n"); RequestsInFlightDatabase::Instance()->CheckProcessesAreRunning(); } #endif #endif delay = 0; } delay += GRAPHICS_SERVER_STATUS_STALL_LOOP_SLEEP_TIME; outerDelay += GRAPHICS_SERVER_STATUS_STALL_LOOP_SLEEP_TIME; } else { SetServerStalledState(false); delay = 0; outerDelay = 0; } } // Wait a reasonable time. 10ms is too short as it is below a games 16ms typical update and will lead to diffs of zero (a stall condition). osSleep(GRAPHICS_SERVER_STATUS_STALL_LOOP_SLEEP_TIME); } shutdownEvent.Close(); }
//-------------------------------------------------------------- /// DllMain /// \param hModule Module /// \param ul_reason_for_call Reason for call /// \param lpReserved Reserved /// \return True or false //-------------------------------------------------------------- BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { PS_UNREFERENCED_PARAMETER(hModule); PS_UNREFERENCED_PARAMETER(lpReserved); if (AMDT::InitHookDLL(ul_reason_for_call) == false) { return TRUE; } BOOL retVal = TRUE; char modulename[MAX_PATH]; GetModuleFileNameA(NULL, modulename, MAX_PATH); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { if (OSWrappers::IsProcessRunning(sWebServerName, true) && InjectionAllowed(modulename)) { Log(logMESSAGE, "Attaching to %s\n", modulename); Log(logMESSAGE, "DllMain DLL_PROCESS_ATTACH module %s\n", modulename); if (SG_GET_BOOL(OptionDllReplacement) == true) { UpdateHooksOnLoadLibrary(); } #ifdef _WIN32 { // Initialize and register the unhandled exception handler: bool rc1 = UnhandledExceptionHandler::init(); if (rc1) { Log(logMESSAGE, "Registered unhandled exception handler\n"); } else { Log(logERROR, "Failed to register unhandled exception handler\n"); } } #endif // @Note: Do we need to do this? Overwriting it doesn't seem to break anything? // get current directory so we can specify as a location for DLLs to be loaded from char curDir[PS_MAX_PATH]; GetCurrentDirectory(PS_MAX_PATH, curDir); // SetDllDirectory requires XP SP1 or later. SetDllDirectory(curDir); sprintf_s(g_MicroDLLPath, PS_MAX_PATH, "%s", SG_GET_PATH(MicroDLLPath)); CollectWrapperInfo(); if (SG_GET_BOOL(OptionNoProcessTrack) == false) { HookCreateProcess(); Log(logMESSAGE, "Process Tracking is ON\n"); } else { Log(logMESSAGE, "Process Tracking is OFF\n"); } // At the moment, dxgi.dll is implicitly loaded from HookLoadLibrary // Force the Dll folder to point to our replaced dll's so the replace version // of dxgi.dll is loaded if (SG_GET_BOOL(OptionDllReplacement) == true) { // Get architecture of parent application (32 or 64 bit) osModuleArchitecture binaryType; OSWrappers::GetBinaryType(modulename, &binaryType); DllReplacement::SetDllDirectory(binaryType == OS_X86_64_ARCHITECTURE); } if (SG_GET_BOOL(OptionManualDllReplacement) == false) { HookLoadLibrary(); } } else { // set return value to FALSE. This will indicate a load error so the loader will // next unload this dll retVal = FALSE; } break; } case DLL_THREAD_ATTACH: { Log(logMESSAGE, "DllMain DLL_THREAD_ATTACH to %i %s\n", GetCurrentProcessId(), modulename); } break; case DLL_THREAD_DETACH: { Log(logMESSAGE, "DllMain DLL_THREAD_DETACH to %i %s\n", GetCurrentProcessId(), modulename); } break; case DLL_PROCESS_DETACH: { if (OSWrappers::IsProcessRunning(sWebServerName, true)) { Log(logMESSAGE, "DllMain DLL_PROCESS_DETACH from module %s\n", modulename); if (SG_GET_BOOL(OptionNoProcessTrack) == false) { UnhookCreateProcess(); } UnhookLoadLibrary(); } } break; default: { Log(logMESSAGE, "DllMain Unhandled switch case module %s\n", modulename); } break; } return retVal; }