diff --git a/UIforETW/Settings.cpp b/UIforETW/Settings.cpp index ff01ba08..2c7b00bb 100644 --- a/UIforETW/Settings.cpp +++ b/UIforETW/Settings.cpp @@ -169,10 +169,13 @@ BOOL CSettings::OnInitDialog() toolTip_.SetMaxTipWidth(400); toolTip_.Activate(TRUE); - toolTip_.AddTool(&btHeapTracingExe_, L"Specify the file names of the exes to be heap traced, " - L"separated by semi-colons. " - L"Enter just the file parts (with the .exe extension) not a full path. For example, " - L"'chrome.exe;notepad.exe'. This is for use with the heap-tracing-to-file mode."); + toolTip_.AddTool(&btHeapTracingExe_, L"Specify which processes to heap trace when using " + L"heap-tracing-to-file mode. Three different methods can be used to specify the processes:\n" + L"1) A semi-colon separated list of process names, such as 'chrome.exe;notepad.exe'. " + L"The processes must be launched after this is set and heap-tracing-to-file is selected.\n" + L"2) A semi-colon separated list of process IDs (PIDs) - maximum of two - such as '1234;5678'.\n" + L"3) A fully specified path to an executable that will be launched by ETW when heap tracing is " + L"started."); toolTip_.AddTool(&btExtraKernelFlags_, L"Extra kernel flags, separated by '+', such as " L"\"REGISTRY+PERF_COUNTER\". See \"xperf -providers k\" for the full list. " L"Note that incorrect kernel flags will cause tracing to fail to start."); diff --git a/UIforETW/UIforETWDlg.cpp b/UIforETW/UIforETWDlg.cpp index 3493c4ab..e987e86d 100644 --- a/UIforETW/UIforETWDlg.cpp +++ b/UIforETW/UIforETWDlg.cpp @@ -963,13 +963,37 @@ void CUIforETWDlg::StopEventThreads() void CUIforETWDlg::OnBnClickedStarttracing() { RegisterProviders(); - StartEventThreads(); if (tracingMode_ == kTracingToMemory) outputPrintf(L"\nStarting tracing to in-memory circular buffers...\n"); else if (tracingMode_ == kTracingToFile) outputPrintf(L"\nStarting tracing to disk...\n"); else if (tracingMode_ == kHeapTracingToFile) - outputPrintf(L"\nStarting heap tracing to disk of %s...\n", heapTracingExes_.c_str()); + { + auto heapSettings = ParseHeapTracingSettings(heapTracingExes_); + if (heapSettings.pathName.size()) + { + outputPrintf(L""); + // Launch and heap-profile the specified process, handy for heap-profiling + // the browser process from startup. + outputPrintf(L"\nLaunching and heap tracing to disk %s...\n", heapSettings.pathName.c_str()); + } + else if (heapSettings.processIDs.size()) + { + // Heap profile the processes specified by the PIDs (maximum of two). + outputPrintf(L"\nStarting heap tracing to disk of PIDs %s...\n", heapSettings.processIDs.c_str()); + } + else + { + std::wstring processNames; + for (auto& name : heapSettings.processNames) + { + if (processNames.size() > 0) + processNames += L" and "; + processNames += name; + } + outputPrintf(L"\nStarting heap tracing to disk of the %s processes...\n", processNames.c_str()); + } + } else UIETWASSERT(0); @@ -983,15 +1007,24 @@ void CUIforETWDlg::OnBnClickedStarttracing() } std::wstring kernelProviders = L" Latency+POWER+DISPATCHER+DISK_IO_INIT+FILE_IO+FILE_IO_INIT+VIRT_ALLOC+MEMINFO"; + bool cswitch_and_profile = true; + if (tracingMode_ == kHeapTracingToFile) + { + // Latency = PROC_THREAD+LOADER+DISK_IO+HARD_FAULTS+DPC+INTERRUPT+CSWITCH+PROFILE, + // but we don't need all that for heap tracing. The minimum set is PROC_THREAD+LOADER + // and we add on VIRT_ALLOC+MEMINFO because that seems appropriate for heap tracing. + kernelProviders = L" PROC_THREAD+LOADER+VIRT_ALLOC+MEMINFO"; + cswitch_and_profile = false; + } if (!extraKernelFlags_.empty()) kernelProviders += L"+" + extraKernelFlags_; std::wstring kernelStackWalk; // Record CPU sampling call stacks, from the PROFILE provider - if (bSampledStacks_) + if (bSampledStacks_ && cswitch_and_profile) kernelStackWalk += L"+Profile"; // Record context-switch (switch in) and readying-thread (SetEvent, etc.) // call stacks from DISPATCHER provider. - if (bCswitchStacks_) + if (bCswitchStacks_ && cswitch_and_profile) kernelStackWalk += L"+CSwitch+ReadyThread"; // Record VirtualAlloc call stacks from the VIRT_ALLOC provider. Also // record VirtualFree to allow investigation of memory leaks, even though @@ -1045,7 +1078,6 @@ void CUIforETWDlg::OnBnClickedStarttracing() catch (const std::exception& e) { outputPrintf(L"Check the extra user providers; failed to translate them from the TraceLogging name to a GUID.\n%hs\n", e.what()); - StopEventThreads(); return; } } @@ -1102,7 +1134,7 @@ void CUIforETWDlg::OnBnClickedStarttracing() // CLR runtime provider // https://msdn.microsoft.com/en-us/library/ff357718(v=vs.100).aspx userProviders += L"+e13c0d23-ccbc-4e12-931b-d9cc2eee27e4:0x1CCBD:0x5"; - + // note: this seems to be an updated version of // userProviders += L"+ClrAll:0x98:5"; // which results in Invalid flags. (0x3ec) when I run it @@ -1126,7 +1158,33 @@ void CUIforETWDlg::OnBnClickedStarttracing() std::wstring heapStackWalk; if (bHeapStacks_) heapStackWalk = L" -stackwalk HeapCreate+HeapDestroy+HeapAlloc+HeapRealloc"; - const std::wstring heapArgs = L" -start UIforETWHeapSession -heap -Pids 0" + heapStackWalk + heapBuffers + heapFile; + auto heapSettings = ParseHeapTracingSettings(heapTracingExes_); + std::wstring heapArgs; + if (heapSettings.pathName.size()) + { + // Launch and heap-profile the specified process, handy for heap-profiling + // the browser process from startup. + heapArgs = L" -start UIforETWHeapSession -heap -PidNewProcess \"" + heapSettings.pathName + L"\"" + heapStackWalk + heapBuffers + heapFile; + } + else if (heapSettings.processIDs.size()) + { + // Heap profile the processes specified by the PIDs (maximum of two). + heapArgs = L" -start UIforETWHeapSession -heap -Pids " + heapSettings.processIDs + heapStackWalk + heapBuffers + heapFile; + } + else if (heapSettings.processNames.size()) + { + // Heap profile the processes specified by heapSettings.processNames that + // were launched when the registry key was set (when "Heap tracing to file" + // was the selected tracing type). + heapArgs = L" -start UIforETWHeapSession -heap -Pids 0" + heapStackWalk + heapBuffers + heapFile; + } + else + { + outputPrintf(L"Error: no heap-profiled processes settings found. Go to the Settings dialog to configure them.\n"); + return; + } + + StartEventThreads(); bPreTraceRecorded_ = false; @@ -1836,7 +1894,10 @@ void CUIforETWDlg::SetHeapTracing(bool forceOff) DWORD tracingFlags = tracingMode_ == kHeapTracingToFile ? 1 : 0; if (forceOff) tracingFlags = 0; - for (const auto& tracingName : split(heapTracingExes_, ';')) + + auto heapSettings = ParseHeapTracingSettings(heapTracingExes_); + + for (const auto& tracingName : heapSettings.processNames) { std::wstring targetKey = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options"; CreateRegistryKey(HKEY_LOCAL_MACHINE, targetKey, tracingName); diff --git a/UIforETW/Utility.cpp b/UIforETW/Utility.cpp index 22040eb3..e6dc54b8 100644 --- a/UIforETW/Utility.cpp +++ b/UIforETW/Utility.cpp @@ -974,3 +974,58 @@ void MoveControl(const CWnd* pParent, CWnd& control, int xDelta, int yDelta) control.SetWindowPos(nullptr, p.x + xDelta, p.y + yDelta, 0, 0, flags); } #endif + +// Parse the semi-colon separated heap trace settings +HeapTracedProcesses ParseHeapTracingSettings(std::wstring heapTracingExes) +{ + HeapTracedProcesses result; + for (const auto& tracingName : split(heapTracingExes, ';')) + { + if (tracingName.size()) + { + auto* p = tracingName.c_str(); + // If the first character is a digit then assume that it's a PID. + if (iswdigit(p[0])) + { + if (wcschr(p, L' ')) + { + outputPrintf(L"Error: don't use space separators between PIDs for heap tracing - use semicolons.\n"); + continue; + } + // Convert to space separated PIDs because that is what the xperf -Pids + // option expects. + if (result.processIDs.size() > 0) + result.processIDs += L' '; + result.processIDs += tracingName; + } + else if (wcschr(p, '\\')) + { + // It must be a full path name. + result.pathName = tracingName; + } + else + { + if (wcschr(p, L' ')) + { + outputPrintf(L"Error: don't use space separators between process names for heap tracing - use semicolons.\n"); + continue; + } + result.processNames.push_back(tracingName); + } + } + } + + // Since the three types of heap profiling are mutually exclusive, clear the + // ones that will not be used, to ensure consistency. + if (result.pathName.size()) + { + result.processIDs = L""; + result.processNames.clear(); + } + else if (result.processIDs.size()) + { + result.processNames.clear(); + } + + return result; +} diff --git a/UIforETW/Utility.h b/UIforETW/Utility.h index 162e475c..1ecad726 100644 --- a/UIforETW/Utility.h +++ b/UIforETW/Utility.h @@ -203,3 +203,36 @@ void CloseValidHandle(_In_ _Pre_valid_ _Post_ptr_invalid_ HANDLE handle) noexcep // Put MFC specific code here void MoveControl(const CWnd* pParent, CWnd& control, int xDelta, int yDelta); #endif + +// Heap tracing can be enabled for one or two PIDs (can be enabled when they are +// already running, one or more process names (most enable through registry +// prior to process launch), or for a single process which xperf launches. +// These are all useful in different scenarios. +// In the context of Chrome profiling: +// Profiling a single process (of any type) that is already running can be done +// by specifying the PID of that process or pair of processes. +// Profiling all Chrome processes can be done by specifying chrome.exe. If this +// specification is done after launching the browser process then this will only +// heap-profile subsequently launched processes, so mostly newly created renderer +// processes, potentially from startup. +// Profiling the *browser* process from startup can be done by specifying the +// the full patch to the executable, and xperf will then launch this process and +// start heap tracing. Note that this will run Chrome as admin, so be careful. +// These options cannot be used together. + +// The heap tracing options can be specified as a semi-colon separated list of +// process IDs (maximum of two) and process names *or* a single fully-qualified +// path name. +struct HeapTracedProcesses +{ + // Space-separated set of process IDs (as required by -Pids), or an empty string. + std::wstring processIDs; + // Vector of process names, including .exe but not any path information. + std::vector processNames; + // A single fully qualified executable which xperf will launch when tracing + // starts. + std::wstring pathName; +}; + +// Parse the semi-colon separated heap trace settings +HeapTracedProcesses ParseHeapTracingSettings(std::wstring heapTracingExes);