in ctsPerf/ctsPerf.cpp [129:366]
int __cdecl wmain(_In_ int argc, _In_reads_z_(argc) const wchar_t** argv)
{
WSADATA wsadata;
if (const auto wsError = ::WSAStartup(WINSOCK_VERSION, &wsadata); wsError != 0)
{
::wprintf(L"ctsPerf failed at WSAStartup [%d]\n", wsError);
return wsError;
}
// create a notification event to signal if the user wants to exit early
g_break = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (g_break == nullptr)
{
const auto gle = ::GetLastError();
wprintf(L"Out of resources -- cannot initialize (CreateEvent) (%u)\n", gle);
return static_cast<int>(gle);
}
if (!::SetConsoleCtrlHandler(BreakHandlerRoutine, TRUE))
{
const auto gle = ::GetLastError();
wprintf(L"Out of resources -- cannot initialize (SetConsoleCtrlHandler) (%u)\n", gle);
return static_cast<int>(gle);
}
auto trackNetworking = false;
auto trackEstats = false;
wstring trackInterfaceDescription;
wstring trackProcess;
auto processId = c_uninitializedProcessId;
DWORD timeToRunMs = 60000; // default to 60 seconds
for (DWORD argCount = argc; argCount > 1; --argCount)
{
if (ctString::ctOrdinalStartsWithCaseInsensative(argv[argCount - 1], L"-process:"))
{
trackProcess = argv[argCount - 1];
// strip off the "process:" preface to the string
const auto endOfToken = ranges::find(trackProcess, L':');
trackProcess.erase(trackProcess.begin(), endOfToken + 1);
// the performance counter does not look at the extension, so remove .exe if it's there
if (ctString::ctOrdinalEndsWithCaseInsensative(trackProcess, L".exe"))
{
trackProcess.erase(trackProcess.end() - 4, trackProcess.end());
}
if (trackProcess.empty())
{
wprintf(L"Incorrect option: %ws\n", argv[argCount - 1]);
wprintf(c_usageStatement);
return 1;
}
}
else if (ctString::ctOrdinalStartsWithCaseInsensative(argv[argCount - 1], L"-pid:"))
{
wstring pidString(argv[argCount - 1]);
// strip off the "pid:" preface to the string
const auto endOfToken = ranges::find(pidString, L':');
pidString.erase(pidString.begin(), endOfToken + 1);
// the user could have specified zero, which happens to be what is returned from wcstoul on error
if (pidString == L"0")
{
processId = 0;
}
else
{
processId = ::wcstoul(pidString.c_str(), nullptr, 10);
if (processId == 0 || processId == ULONG_MAX)
{
wprintf(L"Incorrect option: %ws\n", argv[argCount - 1]);
wprintf(c_usageStatement);
return 1;
}
}
}
else if (ctString::ctOrdinalStartsWithCaseInsensative(argv[argCount - 1], L"-estats"))
{
trackEstats = true;
}
else if (ctString::ctOrdinalStartsWithCaseInsensative(argv[argCount - 1], L"-Networking"))
{
trackNetworking = true;
}
else if (ctString::ctOrdinalStartsWithCaseInsensative(argv[argCount - 1], L"-InterfaceDescription:"))
{
trackInterfaceDescription = argv[argCount - 1];
// strip off the "-InterfaceDescription:" preface to the string
const auto endOfToken = ranges::find(trackInterfaceDescription, L':');
trackInterfaceDescription.erase(trackInterfaceDescription.begin(), endOfToken + 1);
}
else if (ctString::ctOrdinalStartsWithCaseInsensative(argv[argCount - 1], L"-MeanOnly"))
{
g_meanOnly = true;
}
else
{
const auto timeToRun = wcstoul(argv[argCount - 1], nullptr, 10);
if (timeToRun == 0 || timeToRun == ULONG_MAX)
{
wprintf(L"Incorrect option: %ws\n", argv[argCount - 1]);
wprintf(c_usageStatement);
return 1;
}
timeToRunMs = timeToRun * 1000;
if (timeToRunMs < timeToRun)
{
wprintf(L"Incorrect option: %ws\n", argv[argCount - 1]);
wprintf(c_usageStatement);
return 1;
}
}
}
const auto trackPerProcess = !trackProcess.empty() || processId != c_uninitializedProcessId;
if (timeToRunMs <= 5000)
{
wprintf(L"ERROR: Must run over 5 seconds to have enough samples for analysis\n");
wprintf(c_usageStatement);
return 1;
}
try
{
// ReSharper disable once CppTooWideScopeInitStatement
ctsPerf::ctsEstats estats;
if (trackEstats)
{
if (estats.start())
{
wprintf(L"Enabling ESTATS\n");
}
else
{
wprintf(L"ESTATS cannot be started - verify running as Administrator\n");
return 1;
}
}
wprintf(L"Instantiating WMI Performance objects (this can take a few seconds)\n");
const auto coInit = wil::CoInitializeEx();
const ctWmiService wmi(L"root\\cimv2");
g_wmi = &wmi;
auto deleteAllCounters = wil::scope_exit([&]() noexcept { DeleteAllCounters(); });
ctsPerf::ctsWriteDetails cpuwriter(g_fileName);
cpuwriter.CreateFile(g_meanOnly);
ctsPerf::ctsWriteDetails networkWriter(g_networkingFilename);
if (trackNetworking)
{
networkWriter.CreateFile(g_meanOnly);
}
ctsPerf::ctsWriteDetails processWriter(g_processFilename);
if (trackPerProcess)
{
processWriter.CreateFile(g_meanOnly);
}
wprintf(L".");
// create a perf counter objects to maintain these counters
std::vector<ctWmiPerformance> performanceVector;
performanceVector.emplace_back(InstantiateProcessorCounters());
performanceVector.emplace_back(InstantiateMemoryCounters());
if (trackNetworking)
{
performanceVector.emplace_back(InstantiateNetworkAdapterCounters(trackInterfaceDescription));
performanceVector.emplace_back(InstantiateNetworkInterfaceCounters(trackInterfaceDescription));
performanceVector.emplace_back(InstantiateIPCounters());
performanceVector.emplace_back(InstantiateTCPCounters());
performanceVector.emplace_back(InstantiateUDPCounters());
}
if (!trackProcess.empty())
{
performanceVector.emplace_back(InstantiatePerProcessByNameCounters(trackProcess));
}
else if (processId != c_uninitializedProcessId)
{
performanceVector.emplace_back(InstantiatePerProcessByPIDCounters(processId));
}
wprintf(L"\nStarting counters : will run for %lu seconds\n (hit ctrl-c to exit early) ...\n\n", timeToRunMs / 1000UL);
for (auto& perfObject : performanceVector)
{
perfObject.start_all_counters(1000);
}
::WaitForSingleObject(g_break, timeToRunMs);
wprintf(L"Stopping counters ....\n\n");
for (auto& perfObject : performanceVector)
{
perfObject.stop_all_counters();
}
ProcessProcessorCounters(cpuwriter);
ProcessMemoryCounters(cpuwriter);
if (trackNetworking)
{
ProcessNetworkAdapterCounters(networkWriter);
ProcessNetworkInterfaceCounters(networkWriter);
ProcessIPCounters(networkWriter);
ProcessTCPCounters(networkWriter);
ProcessUDPCounters(networkWriter);
}
if (trackPerProcess)
{
ProcessPerProcessCounters(trackProcess, processId, processWriter);
}
}
catch (const wil::ResultException& e)
{
wprintf(L"ctsPerf exception: %hs\n", e.what());
return 1;
}
catch (const exception& e)
{
wprintf(L"ctsPerf exception: %hs\n", e.what());
return 1;
}
CloseHandle(g_break);
return 0;
}