diff --git a/README.md b/README.md index 3ea9786..4161af6 100644 --- a/README.md +++ b/README.md @@ -75,8 +75,8 @@ Viewing vcperf traces in WPA requires the C++ Build Insights WPA add-in. Install | Option | Arguments and description | |------------------|---------------------------| -| `/start` | `[/nocpusampling]` `[/level1 \| /level2 \| /level3]` `` | -| | Tells *vcperf.exe* to start a trace under the given session name. There can only be one active session at a time on a given machine.

If the `/nocpusampling` option is specified, *vcperf.exe* doesn't collect CPU samples. It prevents the use of the CPU Usage (Sampled) view in Windows Performance Analyzer, but makes the collected traces smaller.

The `/level1`, `/level2`, or `/level3` option is used to specify which MSVC events to collect, in increasing level of information. Level 3 includes all events. Level 2 includes all events except template instantiation events. Level 1 includes all events except template instantiation, function, and file events. If unspecified, `/level2` is selected by default.

Once tracing is started, *vcperf.exe* returns immediately. Events are collected system-wide for all processes running on the machine. That means that you don't need to build your project from the same command prompt as the one you used to run *vcperf.exe*. For example, you can build your project from Visual Studio. | +| `/start` | `[/noadmin]` `[/nocpusampling]` `[/level1 \| /level2 \| /level3]` `` | +| | Tells *vcperf.exe* to start a trace under the given session name. When running vcperf without admin privileges, there can be more than one active session on a given machine.

If the `/noadmin` option is specified, *vcperf.exe* doesn't require admin privileges. "If the `/noadmin` option is specified, vcperf.exe doesn't require admin privileges, and the `/nocpusampling` flag is ignored."

If the `/nocpusampling` option is specified, *vcperf.exe* doesn't collect CPU samples. It prevents the use of the CPU Usage (Sampled) view in Windows Performance Analyzer, but makes the collected traces smaller.

The `/level1`, `/level2`, or `/level3` option is used to specify which MSVC events to collect, in increasing level of information. Level 3 includes all events. Level 2 includes all events except template instantiation events. Level 1 includes all events except template instantiation, function, and file events. If unspecified, `/level2` is selected by default.

Once tracing is started, *vcperf.exe* returns immediately. Events are collected system-wide for all processes running on the machine. That means that you don't need to build your project from the same command prompt as the one you used to run *vcperf.exe*. For example, you can build your project from Visual Studio. | | `/stop` | (1) `[/templates]` `` ``
(2) `[/templates]` `` `/timetrace` `` | | | Stops the trace identified by the given session name. Runs a post-processing step on the trace to generate a file specified by the `` parameter.

If the `/templates` option is specified, also analyze template instantiation events.

(1) Generates a file viewable in Windows Performance Analyzer (WPA). The output file requires a `.etl` extension.
(2) Generates a file viewable in Microsoft Edge's trace viewer ([edge://tracing](edge://tracing)). The output file requires a `.json` extension. | | `/stopnoanalyze` | `` `` | diff --git a/packages.config b/packages.config index d835a8c..ddd7d7b 100644 --- a/packages.config +++ b/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Commands.cpp b/src/Commands.cpp index 4d93c07..fb90132 100644 --- a/src/Commands.cpp +++ b/src/Commands.cpp @@ -1,366 +1,371 @@ -#include -#include "Commands.h" - -#include - -#include "VcperfBuildInsights.h" - -#include "WPA\Analyzers\ExpensiveTemplateInstantiationCache.h" -#include "WPA\Analyzers\ContextBuilder.h" -#include "WPA\Analyzers\MiscellaneousCache.h" -#include "WPA\Views\BuildExplorerView.h" -#include "WPA\Views\FunctionsView.h" -#include "WPA\Views\FilesView.h" -#include "WPA\Views\TemplateInstantiationsView.h" -#include "TimeTrace\ExecutionHierarchy.h" -#include "TimeTrace\TimeTraceGenerator.h" - -using namespace Microsoft::Cpp::BuildInsights; - -namespace vcperf -{ - -const wchar_t* ResultCodeToString(RESULT_CODE rc) -{ - switch (rc) - { - case RESULT_CODE_SUCCESS: - return L"SUCCESS"; - - case RESULT_CODE_FAILURE_ANALYSIS_ERROR: - return L"FAILURE_ANALYSIS_ERROR"; - - case RESULT_CODE_FAILURE_CANCELLED: - return L"FAILURE_CANCELLED"; - - case RESULT_CODE_FAILURE_INVALID_INPUT_LOG_FILE: - return L"FAILURE_INVALID_INPUT_LOG_FILE"; - - case RESULT_CODE_FAILURE_INVALID_OUTPUT_LOG_FILE: - return L"FAILURE_INVALID_OUTPUT_LOG_FILE"; - - case RESULT_CODE_FAILURE_MISSING_ANALYSIS_CALLBACK: - return L"FAILURE_MISSING_ANALYSIS_CALLBACK"; - - case RESULT_CODE_FAILURE_MISSING_RELOG_CALLBACK: - return L"FAILURE_MISSING_RELOG_CALLBACK"; - - case RESULT_CODE_FAILURE_OPEN_INPUT_TRACE: - return L"FAILURE_OPEN_INPUT_TRACE"; - - case RESULT_CODE_FAILURE_PROCESS_TRACE: - return L"FAILURE_PROCESS_TRACE"; - - case RESULT_CODE_FAILURE_START_RELOGGER: - return L"FAILURE_START_RELOGGER"; - - case RESULT_CODE_FAILURE_DROPPED_EVENTS: - return L"FAILURE_DROPPED_EVENTS"; - - case RESULT_CODE_FAILURE_UNSUPPORTED_OS: - return L"FAILURE_UNSUPPORTED_OS"; - - case RESULT_CODE_FAILURE_INVALID_TRACING_SESSION_NAME: - return L"FAILURE_INVALID_TRACING_SESSION_NAME"; - - case RESULT_CODE_FAILURE_INSUFFICIENT_PRIVILEGES: - return L"FAILURE_INSUFFICIENT_PRIVILEGES"; - - case RESULT_CODE_FAILURE_GENERATE_GUID: - return L"FAILURE_GENERATE_GUID"; - - case RESULT_CODE_FAILURE_OBTAINING_TEMP_DIRECTORY: - return L"FAILURE_OBTAINING_TEMP_DIRECTORY"; - - case RESULT_CODE_FAILURE_CREATE_TEMPORARY_DIRECTORY: - return L"FAILURE_CREATE_TEMPORARY_DIRECTORY"; - - case RESULT_CODE_FAILURE_START_SYSTEM_TRACE: - return L"FAILURE_START_SYSTEM_TRACE"; - - case RESULT_CODE_FAILURE_START_MSVC_TRACE: - return L"FAILURE_START_MSVC_TRACE"; - - case RESULT_CODE_FAILURE_STOP_MSVC_TRACE: - return L"FAILURE_STOP_MSVC_TRACE"; - - case RESULT_CODE_FAILURE_STOP_SYSTEM_TRACE: - return L"FAILURE_STOP_SYSTEM_TRACE"; - - case RESULT_CODE_FAILURE_SESSION_DIRECTORY_RESOLUTION: - return L"FAILURE_SESSION_DIRECTORY_RESOLUTION"; - - case RESULT_CODE_FAILURE_MSVC_TRACE_FILE_NOT_FOUND: - return L"FAILURE_MSVC_TRACE_FILE_NOT_FOUND"; - - case RESULT_CODE_FAILURE_MERGE_TRACES: - return L"FAILURE_MERGE_TRACES"; - } - - return L"FAILURE_UNKNOWN_ERROR"; -} - -void PrintTraceStatistics(const TRACING_SESSION_STATISTICS& stats) -{ - std::wcout << L"Dropped MSVC events: " << stats.MSVCEventsLost << std::endl; - std::wcout << L"Dropped MSVC buffers: " << stats.MSVCBuffersLost << std::endl; - std::wcout << L"Dropped system events: " << stats.SystemEventsLost << std::endl; - std::wcout << L"Dropped system buffers: " << stats.SystemBuffersLost << std::endl; -} - -void PrintPrivacyNotice(const std::filesystem::path& outputFile) -{ - std::error_code ec; - auto absolutePath = std::filesystem::absolute(outputFile, ec); - - std::wcout << "The trace "; - - if (!ec) - { - std::wcout << "\"" << absolutePath.c_str() << "\" "; - } - else - { - std::wcout << "\"" << outputFile.c_str() << "\" "; - } - - std::wcout << L"may contain personally identifiable information. This includes, but is not limited to, " - L"paths of files that were accessed and names of processes that were running during the collection. " - L"Please be aware of this when sharing this trace with others." << std::endl; -} - -void PrintError(RESULT_CODE failureCode) -{ - switch (failureCode) - { - case RESULT_CODE_FAILURE_INSUFFICIENT_PRIVILEGES: - std::wcout << "This operation requires administrator privileges."; - break; - - case RESULT_CODE_FAILURE_DROPPED_EVENTS: - std::wcout << "Events were dropped during the trace. Please try recollecting the trace."; - break; - - case RESULT_CODE_FAILURE_UNSUPPORTED_OS: - std::wcout << "The version of Microsoft Visual C++ Build Insights that vcperf is using " - "does not support the version of the operating system that the trace was collected on. " - "Please try updating vcperf to the latest version."; - break; - - case RESULT_CODE_FAILURE_START_SYSTEM_TRACE: - case RESULT_CODE_FAILURE_START_MSVC_TRACE: - std::wcout << "A trace that is currently being collected on your system is preventing vcperf " - "from starting a new one. This can occur if you forgot to stop a vcperf trace prior to " - "running the start command, or if processes other than vcperf have started ETW traces of " - "their own. Please try running the vcperf /stop or /stopnoanalyze commands on your previously " - "started trace. If you do not remember the session name that was used for starting a previous " - "vcperf trace, or if you don't recall starting one at all, you can use the 'tracelog -l' command " - "from an elevated command prompt to list all ongoing tracing sessions on your system. Your currently " - "ongoing vcperf trace will show up as MSVC_BUILD_INSIGHTS_SESSION_. " - "You can then issue a vcperf /stop or /stopnoanalyze command with the identified session name (the " - "part between the angle brackets). If no MSVC_BUILD_INSIGHTS_SESSION_ is found, it could mean a kernel " - "ETW trace is currently being collected. This trace will show up as 'NT Kernel Logger' in your tracelog " - "output, and will also prevent you from starting a new trace. You can stop the 'NT Kernel Logger' session " - "by running 'xperf -stop' from an elevated command prompt."; - break; - - default: - std::wcout << L"ERROR CODE: " << ResultCodeToString(failureCode); - } - - std::wcout << std::endl; -} - -RESULT_CODE StopToWPA(const std::wstring& sessionName, const std::filesystem::path& outputFile, bool analyzeTemplates, - TRACING_SESSION_STATISTICS& statistics) -{ - ExpensiveTemplateInstantiationCache etic{ analyzeTemplates }; - ContextBuilder cb; - MiscellaneousCache mc; - BuildExplorerView bev{ &cb, &mc }; - FunctionsView funcv{ &cb, &mc }; - FilesView fv{ &cb, &mc }; - TemplateInstantiationsView tiv{ &cb, &etic, &mc, analyzeTemplates }; - - auto analyzerGroup = MakeStaticAnalyzerGroup(&cb, &etic, &mc); - auto reloggerGroup = MakeStaticReloggerGroup(&etic, &mc, &cb, &bev, &funcv, &fv, &tiv); - - unsigned long long systemEventsRetentionFlags = RELOG_RETENTION_SYSTEM_EVENT_FLAGS_CPU_SAMPLES; - - int analysisPassCount = analyzeTemplates ? 2 : 1; - - return StopAndRelogTracingSession(sessionName.c_str(), outputFile.c_str(), - &statistics, analysisPassCount, systemEventsRetentionFlags, analyzerGroup, reloggerGroup); -} - -RESULT_CODE StopToTimeTrace(const std::wstring& sessionName, const std::filesystem::path& outputFile, bool analyzeTemplates, - TRACING_SESSION_STATISTICS& statistics) -{ - ExecutionHierarchy::Filter f{ analyzeTemplates, - std::chrono::milliseconds(10), - std::chrono::milliseconds(10) }; - ExecutionHierarchy eh{ f }; - TimeTraceGenerator ttg{ &eh, outputFile }; - - auto analyzerGroup = MakeStaticAnalyzerGroup(&eh, &ttg); - int analysisPassCount = 1; - - return StopAndAnalyzeTracingSession(sessionName.c_str(), analysisPassCount, &statistics, analyzerGroup); -} - -RESULT_CODE AnalyzeToWPA(const std::filesystem::path& inputFile, const std::filesystem::path& outputFile, bool analyzeTemplates) -{ - ExpensiveTemplateInstantiationCache etic{ analyzeTemplates }; - ContextBuilder cb; - MiscellaneousCache mc; - BuildExplorerView bev{ &cb, &mc }; - FunctionsView funcv{ &cb, &mc }; - FilesView fv{ &cb, &mc }; - TemplateInstantiationsView tiv{ &cb, &etic, &mc, analyzeTemplates }; - - auto analyzerGroup = MakeStaticAnalyzerGroup(&cb, &etic, &mc); - auto reloggerGroup = MakeStaticReloggerGroup(&etic, &mc, &cb, &bev, &funcv, &fv, &tiv); - - unsigned long long systemEventsRetentionFlags = RELOG_RETENTION_SYSTEM_EVENT_FLAGS_CPU_SAMPLES; - - int analysisPassCount = analyzeTemplates ? 2 : 1; - - return Relog(inputFile.c_str(), outputFile.c_str(), analysisPassCount, - systemEventsRetentionFlags, analyzerGroup, reloggerGroup); -} - -RESULT_CODE AnalyzeToTimeTrace(const std::filesystem::path& inputFile, const std::filesystem::path& outputFile, bool analyzeTemplates) -{ - ExecutionHierarchy::Filter f{ analyzeTemplates, - std::chrono::milliseconds(10), - std::chrono::milliseconds(10) }; - ExecutionHierarchy eh{ f }; - TimeTraceGenerator ttg{ &eh, outputFile }; - - auto analyzerGroup = MakeStaticAnalyzerGroup(&eh, &ttg); - int analysisPassCount = 1; - - return Analyze(inputFile.c_str(), analysisPassCount, analyzerGroup); -} - -HRESULT DoStart(const std::wstring& sessionName, bool cpuSampling, VerbosityLevel verbosityLevel) -{ - TRACING_SESSION_OPTIONS options{}; - - options.SystemEventFlags |= TRACING_SESSION_SYSTEM_EVENT_FLAGS_CONTEXT; - - switch (verbosityLevel) - { - case VerbosityLevel::VERBOSE: - options.MsvcEventFlags |= TRACING_SESSION_MSVC_EVENT_FLAGS_FRONTEND_TEMPLATE_INSTANTIATIONS; - - case VerbosityLevel::MEDIUM: - options.MsvcEventFlags |= TRACING_SESSION_MSVC_EVENT_FLAGS_FRONTEND_FILES; - options.MsvcEventFlags |= TRACING_SESSION_MSVC_EVENT_FLAGS_BACKEND_FUNCTIONS; - - case VerbosityLevel::LIGHT: - options.MsvcEventFlags |= TRACING_SESSION_MSVC_EVENT_FLAGS_BASIC; - } - - if (cpuSampling) { - options.SystemEventFlags |= TRACING_SESSION_SYSTEM_EVENT_FLAGS_CPU_SAMPLES; - } - - std::wcout << L"Starting tracing session " << sessionName << L"..." << std::endl; - - auto rc = StartTracingSession(sessionName.c_str(), options); - - if (rc != RESULT_CODE_SUCCESS) - { - std::wcout << "Failed to start trace." << std::endl; - PrintError(rc); - - return E_FAIL; - } - - std::wcout << L"Tracing session started successfully!" << std::endl; - - return S_OK; -} - - -HRESULT DoStop(const std::wstring& sessionName, const std::filesystem::path& outputFile, bool analyzeTemplates, bool generateTimeTrace) -{ - std::wcout << L"Stopping and analyzing tracing session " << sessionName << L"..." << std::endl; - - TRACING_SESSION_STATISTICS statistics{}; - RESULT_CODE rc; - if (!generateTimeTrace) { - rc = StopToWPA(sessionName, outputFile, analyzeTemplates, statistics); - } - else { - rc = StopToTimeTrace(sessionName, outputFile, analyzeTemplates, statistics); - } - - PrintTraceStatistics(statistics); - - if (rc != RESULT_CODE_SUCCESS) - { - std::wcout << "Failed to stop trace." << std::endl; - PrintError(rc); - - return E_FAIL; - } - - PrintPrivacyNotice(outputFile); - std::wcout << L"Tracing session stopped successfully!" << std::endl; - - return S_OK; -} - -HRESULT DoStopNoAnalyze(const std::wstring& sessionName, const std::filesystem::path& outputFile) -{ - TRACING_SESSION_STATISTICS statistics{}; - - std::wcout << L"Stopping tracing session " << sessionName << L"..." << std::endl; - - auto rc = StopTracingSession(sessionName.c_str(), outputFile.c_str(), &statistics); - - PrintTraceStatistics(statistics); - - if (rc != RESULT_CODE_SUCCESS) - { - std::wcout << "Failed to stop trace." << std::endl; - PrintError(rc); - - return E_FAIL; - } - - PrintPrivacyNotice(outputFile); - std::wcout << L"Tracing session stopped successfully!" << std::endl; - - return S_OK; -} - -HRESULT DoAnalyze(const std::filesystem::path& inputFile, const std::filesystem::path& outputFile, bool analyzeTemplates, bool generateTimeTrace) -{ - std::wcout << L"Analyzing..." << std::endl; - - RESULT_CODE rc; - if (!generateTimeTrace) { - rc = AnalyzeToWPA(inputFile, outputFile, analyzeTemplates); - } - else { - rc = AnalyzeToTimeTrace(inputFile, outputFile, analyzeTemplates); - } - - if (rc != RESULT_CODE_SUCCESS) - { - std::wcout << "Failed to analyze trace." << std::endl; - PrintError(rc); - - return E_FAIL; - } - - PrintPrivacyNotice(outputFile); - std::wcout << L"Analysis completed successfully!" << std::endl; - - return S_OK; -} - -} // namespace vcperf +#include +#include "Commands.h" + +#include + +#include "VcperfBuildInsights.h" + +#include "WPA\Analyzers\ExpensiveTemplateInstantiationCache.h" +#include "WPA\Analyzers\ContextBuilder.h" +#include "WPA\Analyzers\MiscellaneousCache.h" +#include "WPA\Views\BuildExplorerView.h" +#include "WPA\Views\FunctionsView.h" +#include "WPA\Views\FilesView.h" +#include "WPA\Views\TemplateInstantiationsView.h" +#include "TimeTrace\ExecutionHierarchy.h" +#include "TimeTrace\TimeTraceGenerator.h" + +using namespace Microsoft::Cpp::BuildInsights; + +namespace vcperf +{ + +const wchar_t* ResultCodeToString(RESULT_CODE rc) +{ + switch (rc) + { + case RESULT_CODE_SUCCESS: + return L"SUCCESS"; + + case RESULT_CODE_FAILURE_ANALYSIS_ERROR: + return L"FAILURE_ANALYSIS_ERROR"; + + case RESULT_CODE_FAILURE_CANCELLED: + return L"FAILURE_CANCELLED"; + + case RESULT_CODE_FAILURE_INVALID_INPUT_LOG_FILE: + return L"FAILURE_INVALID_INPUT_LOG_FILE"; + + case RESULT_CODE_FAILURE_INVALID_OUTPUT_LOG_FILE: + return L"FAILURE_INVALID_OUTPUT_LOG_FILE"; + + case RESULT_CODE_FAILURE_MISSING_ANALYSIS_CALLBACK: + return L"FAILURE_MISSING_ANALYSIS_CALLBACK"; + + case RESULT_CODE_FAILURE_MISSING_RELOG_CALLBACK: + return L"FAILURE_MISSING_RELOG_CALLBACK"; + + case RESULT_CODE_FAILURE_OPEN_INPUT_TRACE: + return L"FAILURE_OPEN_INPUT_TRACE"; + + case RESULT_CODE_FAILURE_PROCESS_TRACE: + return L"FAILURE_PROCESS_TRACE"; + + case RESULT_CODE_FAILURE_START_RELOGGER: + return L"FAILURE_START_RELOGGER"; + + case RESULT_CODE_FAILURE_DROPPED_EVENTS: + return L"FAILURE_DROPPED_EVENTS"; + + case RESULT_CODE_FAILURE_UNSUPPORTED_OS: + return L"FAILURE_UNSUPPORTED_OS"; + + case RESULT_CODE_FAILURE_INVALID_TRACING_SESSION_NAME: + return L"FAILURE_INVALID_TRACING_SESSION_NAME"; + + case RESULT_CODE_FAILURE_INSUFFICIENT_PRIVILEGES: + return L"FAILURE_INSUFFICIENT_PRIVILEGES"; + + case RESULT_CODE_FAILURE_GENERATE_GUID: + return L"FAILURE_GENERATE_GUID"; + + case RESULT_CODE_FAILURE_OBTAINING_TEMP_DIRECTORY: + return L"FAILURE_OBTAINING_TEMP_DIRECTORY"; + + case RESULT_CODE_FAILURE_CREATE_TEMPORARY_DIRECTORY: + return L"FAILURE_CREATE_TEMPORARY_DIRECTORY"; + + case RESULT_CODE_FAILURE_START_SYSTEM_TRACE: + return L"FAILURE_START_SYSTEM_TRACE"; + + case RESULT_CODE_FAILURE_START_MSVC_TRACE: + return L"FAILURE_START_MSVC_TRACE"; + + case RESULT_CODE_FAILURE_STOP_MSVC_TRACE: + return L"FAILURE_STOP_MSVC_TRACE"; + + case RESULT_CODE_FAILURE_STOP_SYSTEM_TRACE: + return L"FAILURE_STOP_SYSTEM_TRACE"; + + case RESULT_CODE_FAILURE_SESSION_DIRECTORY_RESOLUTION: + return L"FAILURE_SESSION_DIRECTORY_RESOLUTION"; + + case RESULT_CODE_FAILURE_MSVC_TRACE_FILE_NOT_FOUND: + return L"FAILURE_MSVC_TRACE_FILE_NOT_FOUND"; + + case RESULT_CODE_FAILURE_MERGE_TRACES: + return L"FAILURE_MERGE_TRACES"; + } + + return L"FAILURE_UNKNOWN_ERROR"; +} + +void PrintTraceStatistics(const TRACING_SESSION_STATISTICS& stats) +{ + std::wcout << L"Dropped MSVC events: " << stats.MSVCEventsLost << std::endl; + std::wcout << L"Dropped MSVC buffers: " << stats.MSVCBuffersLost << std::endl; + std::wcout << L"Dropped system events: " << stats.SystemEventsLost << std::endl; + std::wcout << L"Dropped system buffers: " << stats.SystemBuffersLost << std::endl; +} + +void PrintPrivacyNotice(const std::filesystem::path& outputFile) +{ + std::error_code ec; + auto absolutePath = std::filesystem::absolute(outputFile, ec); + + std::wcout << "The trace "; + + if (!ec) + { + std::wcout << "\"" << absolutePath.c_str() << "\" "; + } + else + { + std::wcout << "\"" << outputFile.c_str() << "\" "; + } + + std::wcout << L"may contain personally identifiable information. This includes, but is not limited to, " + L"paths of files that were accessed and names of processes that were running during the collection. " + L"Please be aware of this when sharing this trace with others." << std::endl; +} + +void PrintError(RESULT_CODE failureCode) +{ + switch (failureCode) + { + case RESULT_CODE_FAILURE_INSUFFICIENT_PRIVILEGES: + std::wcout << "This operation requires administrator privileges."; + break; + + case RESULT_CODE_FAILURE_DROPPED_EVENTS: + std::wcout << "Events were dropped during the trace. Please try recollecting the trace."; + break; + + case RESULT_CODE_FAILURE_UNSUPPORTED_OS: + std::wcout << "The version of Microsoft Visual C++ Build Insights that vcperf is using " + "does not support the version of the operating system that the trace was collected on. " + "Please try updating vcperf to the latest version."; + break; + case RESULT_CODE_FAILURE_NO_CONTEXT_INFO_AVAILABLE: + std::wcout << "You are using a version of the MSVC toolset that does not support the `/noadmin` option. " + "Please try updating your MSVC toolset to at least 16.11."; + break; + case RESULT_CODE_FAILURE_START_SYSTEM_TRACE: + case RESULT_CODE_FAILURE_START_MSVC_TRACE: + std::wcout << "A trace that is currently being collected on your system is preventing vcperf " + "from starting a new one. This can occur if you forgot to stop a vcperf trace prior to " + "running the start command, or if processes other than vcperf have started ETW traces of " + "their own. Please try running the vcperf /stop or /stopnoanalyze commands on your previously " + "started trace. If you do not remember the session name that was used for starting a previous " + "vcperf trace, or if you don't recall starting one at all, you can use the 'tracelog -l' command " + "from an elevated command prompt to list all ongoing tracing sessions on your system. Your currently " + "ongoing vcperf trace will show up as MSVC_BUILD_INSIGHTS_SESSION_. " + "You can then issue a vcperf /stop or /stopnoanalyze command with the identified session name (the " + "part between the angle brackets). If no MSVC_BUILD_INSIGHTS_SESSION_ is found, it could mean a kernel " + "ETW trace is currently being collected. This trace will show up as 'NT Kernel Logger' in your tracelog " + "output, and will also prevent you from starting a new trace. You can stop the 'NT Kernel Logger' session " + "by running 'xperf -stop' from an elevated command prompt."; + break; + + default: + std::wcout << L"ERROR CODE: " << ResultCodeToString(failureCode); + } + + std::wcout << std::endl; +} + +RESULT_CODE StopToWPA(const std::wstring& sessionName, const std::filesystem::path& outputFile, bool analyzeTemplates, + TRACING_SESSION_STATISTICS& statistics) +{ + ExpensiveTemplateInstantiationCache etic{ analyzeTemplates }; + ContextBuilder cb; + MiscellaneousCache mc; + BuildExplorerView bev{ &cb, &mc }; + FunctionsView funcv{ &cb, &mc }; + FilesView fv{ &cb, &mc }; + TemplateInstantiationsView tiv{ &cb, &etic, &mc, analyzeTemplates }; + + auto analyzerGroup = MakeStaticAnalyzerGroup(&cb, &etic, &mc); + auto reloggerGroup = MakeStaticReloggerGroup(&etic, &mc, &cb, &bev, &funcv, &fv, &tiv); + + unsigned long long systemEventsRetentionFlags = RELOG_RETENTION_SYSTEM_EVENT_FLAGS_CPU_SAMPLES; + + int analysisPassCount = analyzeTemplates ? 2 : 1; + + return StopAndRelogTracingSession(sessionName.c_str(), outputFile.c_str(), + &statistics, analysisPassCount, systemEventsRetentionFlags, analyzerGroup, reloggerGroup); +} + +RESULT_CODE StopToTimeTrace(const std::wstring& sessionName, const std::filesystem::path& outputFile, bool analyzeTemplates, + TRACING_SESSION_STATISTICS& statistics) +{ + ExecutionHierarchy::Filter f{ analyzeTemplates, + std::chrono::milliseconds(10), + std::chrono::milliseconds(10) }; + ExecutionHierarchy eh{ f }; + TimeTraceGenerator ttg{ &eh, outputFile }; + + auto analyzerGroup = MakeStaticAnalyzerGroup(&eh, &ttg); + int analysisPassCount = 1; + + return StopAndAnalyzeTracingSession(sessionName.c_str(), analysisPassCount, &statistics, analyzerGroup); +} + +RESULT_CODE AnalyzeToWPA(const std::filesystem::path& inputFile, const std::filesystem::path& outputFile, bool analyzeTemplates) +{ + ExpensiveTemplateInstantiationCache etic{ analyzeTemplates }; + ContextBuilder cb; + MiscellaneousCache mc; + BuildExplorerView bev{ &cb, &mc }; + FunctionsView funcv{ &cb, &mc }; + FilesView fv{ &cb, &mc }; + TemplateInstantiationsView tiv{ &cb, &etic, &mc, analyzeTemplates }; + + auto analyzerGroup = MakeStaticAnalyzerGroup(&cb, &etic, &mc); + auto reloggerGroup = MakeStaticReloggerGroup(&etic, &mc, &cb, &bev, &funcv, &fv, &tiv); + + unsigned long long systemEventsRetentionFlags = RELOG_RETENTION_SYSTEM_EVENT_FLAGS_CPU_SAMPLES; + + int analysisPassCount = analyzeTemplates ? 2 : 1; + + return Relog(inputFile.c_str(), outputFile.c_str(), analysisPassCount, + systemEventsRetentionFlags, analyzerGroup, reloggerGroup); +} + +RESULT_CODE AnalyzeToTimeTrace(const std::filesystem::path& inputFile, const std::filesystem::path& outputFile, bool analyzeTemplates) +{ + ExecutionHierarchy::Filter f{ analyzeTemplates, + std::chrono::milliseconds(10), + std::chrono::milliseconds(10) }; + ExecutionHierarchy eh{ f }; + TimeTraceGenerator ttg{ &eh, outputFile }; + + auto analyzerGroup = MakeStaticAnalyzerGroup(&eh, &ttg); + int analysisPassCount = 1; + + return Analyze(inputFile.c_str(), analysisPassCount, analyzerGroup); +} + +HRESULT DoStart(const std::wstring& sessionName, bool admin, bool cpuSampling, VerbosityLevel verbosityLevel) +{ + TRACING_SESSION_OPTIONS options{}; + + switch (verbosityLevel) + { + case VerbosityLevel::VERBOSE: + options.MsvcEventFlags |= TRACING_SESSION_MSVC_EVENT_FLAGS_FRONTEND_TEMPLATE_INSTANTIATIONS; + + case VerbosityLevel::MEDIUM: + options.MsvcEventFlags |= TRACING_SESSION_MSVC_EVENT_FLAGS_FRONTEND_FILES; + options.MsvcEventFlags |= TRACING_SESSION_MSVC_EVENT_FLAGS_BACKEND_FUNCTIONS; + + case VerbosityLevel::LIGHT: + options.MsvcEventFlags |= TRACING_SESSION_MSVC_EVENT_FLAGS_BASIC; + } + + if (cpuSampling) { + options.SystemEventFlags |= TRACING_SESSION_SYSTEM_EVENT_FLAGS_CPU_SAMPLES; + } + + if (!admin) { + options.SystemEventFlags = 0; + } + + std::wcout << L"Starting tracing session " << sessionName << L"..." << std::endl; + + auto rc = StartTracingSession(sessionName.c_str(), options); + + if (rc != RESULT_CODE_SUCCESS) + { + std::wcout << "Failed to start trace." << std::endl; + PrintError(rc); + + return E_FAIL; + } + + std::wcout << L"Tracing session started successfully!" << std::endl; + + return S_OK; +} + + +HRESULT DoStop(const std::wstring& sessionName, const std::filesystem::path& outputFile, bool analyzeTemplates, bool generateTimeTrace) +{ + std::wcout << L"Stopping and analyzing tracing session " << sessionName << L"..." << std::endl; + + TRACING_SESSION_STATISTICS statistics{}; + RESULT_CODE rc; + if (!generateTimeTrace) { + rc = StopToWPA(sessionName, outputFile, analyzeTemplates, statistics); + } + else { + rc = StopToTimeTrace(sessionName, outputFile, analyzeTemplates, statistics); + } + + PrintTraceStatistics(statistics); + + if (rc != RESULT_CODE_SUCCESS) + { + std::wcout << "Failed to stop trace." << std::endl; + PrintError(rc); + + return E_FAIL; + } + + PrintPrivacyNotice(outputFile); + std::wcout << L"Tracing session stopped successfully!" << std::endl; + + return S_OK; +} + +HRESULT DoStopNoAnalyze(const std::wstring& sessionName, const std::filesystem::path& outputFile) +{ + TRACING_SESSION_STATISTICS statistics{}; + + std::wcout << L"Stopping tracing session " << sessionName << L"..." << std::endl; + + auto rc = StopTracingSession(sessionName.c_str(), outputFile.c_str(), &statistics); + + PrintTraceStatistics(statistics); + + if (rc != RESULT_CODE_SUCCESS) + { + std::wcout << "Failed to stop trace." << std::endl; + PrintError(rc); + + return E_FAIL; + } + + PrintPrivacyNotice(outputFile); + std::wcout << L"Tracing session stopped successfully!" << std::endl; + + return S_OK; +} + +HRESULT DoAnalyze(const std::filesystem::path& inputFile, const std::filesystem::path& outputFile, bool analyzeTemplates, bool generateTimeTrace) +{ + std::wcout << L"Analyzing..." << std::endl; + + RESULT_CODE rc; + if (!generateTimeTrace) { + rc = AnalyzeToWPA(inputFile, outputFile, analyzeTemplates); + } + else { + rc = AnalyzeToTimeTrace(inputFile, outputFile, analyzeTemplates); + } + + if (rc != RESULT_CODE_SUCCESS) + { + std::wcout << "Failed to analyze trace." << std::endl; + PrintError(rc); + + return E_FAIL; + } + + PrintPrivacyNotice(outputFile); + std::wcout << L"Analysis completed successfully!" << std::endl; + + return S_OK; +} + +} // namespace vcperf diff --git a/src/Commands.h b/src/Commands.h index 6dfd032..8ba978f 100644 --- a/src/Commands.h +++ b/src/Commands.h @@ -14,7 +14,7 @@ enum class VerbosityLevel VERBOSE }; -HRESULT DoStart(const std::wstring& sessionName, bool cpuSampling, VerbosityLevel verbosityLevel); +HRESULT DoStart(const std::wstring& sessionName, bool admin, bool cpuSampling, VerbosityLevel verbosityLevel); HRESULT DoStop(const std::wstring& sessionName, const std::filesystem::path& outputFile, bool analyzeTemplates = false, bool generateTimeTrace = false); HRESULT DoStopNoAnalyze(const std::wstring& sessionName, const std::filesystem::path& outputFile); HRESULT DoAnalyze(const std::filesystem::path& inputFile, const std::filesystem::path& outputFile, bool analyzeTemplates = false, bool generateTimeTrace = false); diff --git a/src/main.cpp b/src/main.cpp index 8b59636..091e653 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ enum class StartSubCommand { NONE, + NO_ADMIN, NO_CPU_SAMPLING, LEVEL1, LEVEL2, @@ -59,6 +60,10 @@ bool ValidateFile(const std::filesystem::path& file, bool isInput, const std::ws StartSubCommand CheckStartSubCommands(const wchar_t* arg) { + if (CheckCommand(arg, L"noadmin")) { + return StartSubCommand::NO_ADMIN; + } + if (CheckCommand(arg, L"nocpusampling")) { return StartSubCommand::NO_CPU_SAMPLING; } @@ -163,7 +168,7 @@ int wmain(int argc, wchar_t* argv[]) { std::wcout << std::endl; std::wcout << L"USAGE:" << std::endl; - std::wcout << L"vcperf.exe /start [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; + std::wcout << L"vcperf.exe /start [/noadmin] [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; std::wcout << L"vcperf.exe /stop [/templates] sessionName outputFile.etl" << std::endl; std::wcout << L"vcperf.exe /stop [/templates] sessionName /timetrace outputFile.json" << std::endl; std::wcout << L"vcperf.exe /stopnoanalyze sessionName outputRawFile.etl" << std::endl; @@ -179,10 +184,11 @@ int wmain(int argc, wchar_t* argv[]) { if (argc < 3) { - std::wcout << L"vcperf.exe /start [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; + std::wcout << L"vcperf.exe /start [/noadmin] [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; return E_FAIL; } + bool noAdminSpecified = false; bool noCpuSamplingSpecified = false; VerbosityLevel verbosityLevel = VerbosityLevel::INVALID; @@ -191,12 +197,22 @@ int wmain(int argc, wchar_t* argv[]) while (subCommand != StartSubCommand::NONE) { - if (subCommand == StartSubCommand::NO_CPU_SAMPLING) + if (subCommand == StartSubCommand::NO_ADMIN) + { + if (noAdminSpecified) + { + std::wcout << L"ERROR: you can only specify /noadmin once." << std::endl; + std::wcout << L"vcperf.exe /start [/noadmin] [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; + return E_FAIL; + } + noAdminSpecified = true; + } + else if (subCommand == StartSubCommand::NO_CPU_SAMPLING) { if (noCpuSamplingSpecified) { std::wcout << L"ERROR: you can only specify /nocpusampling once." << std::endl; - std::wcout << L"vcperf.exe /start [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; + std::wcout << L"vcperf.exe /start [/noadmin] [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; return E_FAIL; } noCpuSamplingSpecified = true; @@ -204,7 +220,7 @@ int wmain(int argc, wchar_t* argv[]) else if (verbosityLevel != VerbosityLevel::INVALID) { std::wcout << L"ERROR: you can only specify one verbosity level: /level1, /level2, or /level3." << std::endl; - std::wcout << L"vcperf.exe /start [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; + std::wcout << L"vcperf.exe /start [/noadmin] [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; return E_FAIL; } else @@ -237,7 +253,7 @@ int wmain(int argc, wchar_t* argv[]) if (currentArg >= argc) { std::wcout << L"ERROR: a session name must be specified." << std::endl; - std::wcout << L"vcperf.exe /start [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; + std::wcout << L"vcperf.exe /start [/noadmin] [/nocpusampling] [/level1 | /level2 | /level3] sessionName" << std::endl; return E_FAIL; } @@ -248,7 +264,7 @@ int wmain(int argc, wchar_t* argv[]) std::wstring sessionName = argv[currentArg]; - return DoStart(sessionName, !noCpuSamplingSpecified, verbosityLevel); + return DoStart(sessionName, !noAdminSpecified, !noCpuSamplingSpecified, verbosityLevel); } else if (CheckCommand(argv[1], L"stop")) { diff --git a/vcperf.vcxproj b/vcperf.vcxproj index b97c256..4f6b339 100644 --- a/vcperf.vcxproj +++ b/vcperf.vcxproj @@ -22,26 +22,26 @@ 15.0 {E0135A9D-8AEA-40C4-8500-C793D210C271} vcperf - 10.0 + 10.0.19041.0 Application true - v142 + v143 MultiByte Application false - v142 + v143 true MultiByte Application true - v142 + v143 MultiByte @@ -55,8 +55,8 @@ - + @@ -233,7 +233,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file