int __cdecl wmain()

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;
}