From 092fa4d3c30137eca658a86d58fc8230ced56c7c Mon Sep 17 00:00:00 2001 From: Bruce Dawson Date: Sun, 20 Nov 2016 16:31:40 -0500 Subject: [PATCH] Make trace starting more robust Starting of ETW tracing will fail if there is another trace running with the same kernel logger (and there are only two), with the same name for a user logger sesion, or if the file names being used for tracing to a file are in use. This means that starting tracing will fail if it wasn't stopped for some reason (UIforETW crash perhaps) and tracing may fail if some other program happens to be using the same file or session names. These start failures are not self healing - repeated attempts to start tracing will repeatedly fail. Previously UIforETW dealt with this by printing a message suggesting that you stop tracing. This didn't always work. This change makes trace starting far more robust by unilaterally stopping all trace sessions that UIforETW might use. This change also changes the session names and file names to make them less likely to collide. If this doesn't work then changing the kernel logger using the checkbox added in the previous change may help. --- UIforETW/ChildProcess.cpp | 7 ++++--- UIforETW/ChildProcess.h | 5 ++++- UIforETW/UIforETWDlg.cpp | 15 ++++++++++++--- UIforETW/UIforETWDlg.h | 6 +++--- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/UIforETW/ChildProcess.cpp b/UIforETW/ChildProcess.cpp index 22746025..c3075bd3 100644 --- a/UIforETW/ChildProcess.cpp +++ b/UIforETW/ChildProcess.cpp @@ -22,8 +22,8 @@ limitations under the License. static const wchar_t* kPipeName = L"\\\\.\\PIPE\\UIforETWPipe"; -ChildProcess::ChildProcess(std::wstring exePath) - : exePath_(std::move(exePath)) +ChildProcess::ChildProcess(std::wstring exePath, bool printFailedExitCodes) + : exePath_(std::move(exePath)), printFailedExitCodes_(printFailedExitCodes) { // Create the pipe here so that it is guaranteed to be created before // we try starting the process. @@ -44,8 +44,9 @@ ChildProcess::~ChildProcess() { if (hProcess_) { + // Always get the exit code since this also waits for the process to exit. DWORD exitCode = GetExitCode(); - if (exitCode) + if (printFailedExitCodes_ && exitCode) outputPrintf(L"Process exit code was %08x (%lu)\n", exitCode, exitCode); CloseHandle(hProcess_); } diff --git a/UIforETW/ChildProcess.h b/UIforETW/ChildProcess.h index 4e926263..28510396 100644 --- a/UIforETW/ChildProcess.h +++ b/UIforETW/ChildProcess.h @@ -32,7 +32,7 @@ limitations under the License. class ChildProcess { public: - ChildProcess(std::wstring exePath); + ChildProcess(std::wstring exePath, bool printFailedExitCodes = true); // This waits for the child process to terminate, and prints // output with outputPrintf as it arrives. ~ChildProcess(); @@ -56,6 +56,9 @@ class ChildProcess private: // Path to the executable to be run, and its process handle. std::wstring exePath_; + + bool printFailedExitCodes_ = true; + // Process, thread, and event handles have an uninitialized state // of zero. Pipes and files have an uninitialized state of // INVALID_HANDLE_VALUE. Yay Windows! diff --git a/UIforETW/UIforETWDlg.cpp b/UIforETW/UIforETWDlg.cpp index 68e38460..a5b4ae9c 100644 --- a/UIforETW/UIforETWDlg.cpp +++ b/UIforETW/UIforETWDlg.cpp @@ -898,6 +898,15 @@ void CUIforETWDlg::OnBnClickedStarttracing() else UIETWASSERT(0); + { + // Force any existing sessions to stop. This makes starting tracing much more robust. + // Never show the commands executing, and never print the exit code. + ChildProcess child(GetXperfPath(), false); + child.Run(false, L"xperf.exe -stop UIforETWHeapSession -stop UIforETWSession -stop " + GetKernelLogger()); + // Swallow all of the output so that the normal failures will be silent. + child.GetOutput(); + } + std::wstring kernelProviders = L" Latency+POWER+DISPATCHER+DISK_IO_INIT+FILE_IO+FILE_IO_INIT+VIRT_ALLOC+MEMINFO"; if (!extraKernelFlags_.empty()) kernelProviders += L"+" + extraKernelFlags_; @@ -1030,7 +1039,7 @@ void CUIforETWDlg::OnBnClickedStarttracing() std::wstring heapStackWalk; if (bHeapStacks_) heapStackWalk = L" -stackwalk HeapCreate+HeapDestroy+HeapAlloc+HeapRealloc"; - std::wstring heapArgs = L" -start xperfHeapSession -heap -Pids 0" + heapStackWalk + heapBuffers + heapFile; + std::wstring heapArgs = L" -start UIforETWHeapSession -heap -Pids 0" + heapStackWalk + heapBuffers + heapFile; DWORD exitCode = 0; bool started = true; @@ -1158,7 +1167,7 @@ void CUIforETWDlg::StopTracingAndMaybeRecord(bool bSaveTrace) if (tracingMode_ == kHeapTracingToFile) { ETWMark("Tracing type was heap tracing to file."); - child.Run(bShowCommands_, L"xperf.exe -stop xperfHeapSession -stop UIforETWSession -stop " + GetKernelLogger()); + child.Run(bShowCommands_, L"xperf.exe -stop UIforETWHeapSession -stop UIforETWSession -stop " + GetKernelLogger()); } else { @@ -1430,7 +1439,7 @@ void CUIforETWDlg::UpdateTraceList() tempTraces.insert(tempTraces.end(), tempZips.begin(), tempZips.end()); std::sort(tempTraces.begin(), tempTraces.end()); // Function to stop the temporary traces from showing up. - auto ifInvalid = [](const std::wstring& name) { return name == L"kernel.etl" || name == L"user.etl" || name == L"heap.etl"; }; + auto ifInvalid = [](const std::wstring& name) { return name == L"UIForETWkernel.etl" || name == L"UIForETWuser.etl" || name == L"UIForETWheap.etl"; }; tempTraces.erase(std::remove_if(tempTraces.begin(), tempTraces.end(), ifInvalid), tempTraces.end()); for (auto& name : tempTraces) { diff --git a/UIforETW/UIforETWDlg.h b/UIforETW/UIforETWDlg.h index 5329c598..e71bacdf 100644 --- a/UIforETW/UIforETWDlg.h +++ b/UIforETW/UIforETWDlg.h @@ -188,9 +188,9 @@ class CUIforETWDlg : public CDialog // the same result across multiple calls! std::wstring GenerateResultFilename() const; std::wstring GetTempTraceDir() const { return tempTraceDir_; } - std::wstring GetKernelFile() const { return CUIforETWDlg::GetTempTraceDir() + L"kernel.etl"; } - std::wstring GetUserFile() const { return GetTempTraceDir() + L"user.etl"; } - std::wstring GetHeapFile() const { return GetTempTraceDir() + L"heap.etl"; } + std::wstring GetKernelFile() const { return CUIforETWDlg::GetTempTraceDir() + L"UIForETWkernel.etl"; } + std::wstring GetUserFile() const { return GetTempTraceDir() + L"UIForETWuser.etl"; } + std::wstring GetHeapFile() const { return GetTempTraceDir() + L"UIForETWheap.etl"; } // Get session name for kernel logger const std::wstring NTKernelLogger_ = L"\"NT Kernel Logger\"";