1

What I would like to do is write a small program that continuously live counts the number of context switches that a specific process experiences per a sufficiently small unit of time. I have observed this functionality within the software "Process Explorer", so I know it is definitely possible.

Unfortunately, I have very little idea of how to begin coding this and have so far been unable to find any helpful code snippets online. Thus, a small working example implementing a per process and per unit time live context switch count would be immensely helpful for me.

John Roberts
  • 5,885
  • 21
  • 70
  • 124

3 Answers3

1

Here's one way to do it - This will print the amount of context switches used by thread 0 of notepad (you can substitute any process and thread number you want in the CounterPathBuffer initialization) every second:

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <pdh.h>
#include <pdhmsg.h>

#pragma comment(lib, "pdh.lib")
using namespace std;
CONST ULONG SAMPLE_INTERVAL_MS    = 1000;
CONST PWSTR BROWSE_DIALOG_CAPTION = L"Select a counter to monitor.";

void wmain(void)
{
    PDH_STATUS Status;
    HQUERY Query = NULL;
    HCOUNTER Counter;
    PDH_FMT_COUNTERVALUE DisplayValue;
    DWORD CounterType;
    SYSTEMTIME SampleTime;
    PDH_BROWSE_DLG_CONFIG BrowseDlgData;
    WCHAR CounterPathBuffer[PDH_MAX_COUNTER_PATH] = L"\\\\ComputerNameGoesHere\\Thread(notepad/0)\\Context Switches/sec";


    //
    // Create a query.
    //

    Status = PdhOpenQuery(NULL, NULL, &Query);
    if (Status != ERROR_SUCCESS) 
    {
       wprintf(L"\nPdhOpenQuery failed with status 0x%x.", Status);
       goto Cleanup;
    }

    //
    // Initialize the browser dialog window settings.
    //



    ZeroMemory(&BrowseDlgData, sizeof(PDH_BROWSE_DLG_CONFIG));

    BrowseDlgData.bIncludeInstanceIndex = FALSE;
    BrowseDlgData.bSingleCounterPerAdd = TRUE;
    BrowseDlgData.bSingleCounterPerDialog = TRUE;
    BrowseDlgData.bLocalCountersOnly = FALSE;
    BrowseDlgData.bWildCardInstances = TRUE;
    BrowseDlgData.bHideDetailBox = TRUE;
    BrowseDlgData.bInitializePath = FALSE;
    BrowseDlgData.bDisableMachineSelection = FALSE;
    BrowseDlgData.bIncludeCostlyObjects = FALSE;
    BrowseDlgData.bShowObjectBrowser = FALSE;
    BrowseDlgData.hWndOwner = NULL;
    BrowseDlgData.szReturnPathBuffer = CounterPathBuffer;
    BrowseDlgData.cchReturnPathLength = PDH_MAX_COUNTER_PATH;
    BrowseDlgData.pCallBack = NULL;
    BrowseDlgData.dwCallBackArg = 0;
    BrowseDlgData.CallBackStatus = ERROR_SUCCESS;
    BrowseDlgData.dwDefaultDetailLevel = PERF_DETAIL_WIZARD;
    BrowseDlgData.szDialogBoxCaption = BROWSE_DIALOG_CAPTION;

    //
    // Add the selected counter to the query.
    //


    Status = PdhAddCounter(Query, CounterPathBuffer, 0, &Counter);
    if (Status != ERROR_SUCCESS) 
    {
        wprintf(L"\nPdhAddCounter failed with status 0x%x.", Status);
        goto Cleanup;
    }

    //
    // Most counters require two sample values to display a formatted value.
    // PDH stores the current sample value and the previously collected
    // sample value. This call retrieves the first value that will be used
    // by PdhGetFormattedCounterValue in the first iteration of the loop
    // Note that this value is lost if the counter does not require two
    // values to compute a displayable value.
    //

    Status = PdhCollectQueryData(Query);
    if (Status != ERROR_SUCCESS) 
    {
        wprintf(L"\nPdhCollectQueryData failed with 0x%x.\n", Status);
        goto Cleanup;
    }

    //
    // Print counter values until a key is pressed.
    //

    while (!_kbhit()) 
    {
        Sleep(SAMPLE_INTERVAL_MS);

        GetLocalTime(&SampleTime);

        Status = PdhCollectQueryData(Query);
        if (Status != ERROR_SUCCESS) 
        {
            wprintf(L"\nPdhCollectQueryData failed with status 0x%x.", Status);
        }

        wprintf(L"\n\"%2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d.%3.3d\"",
                SampleTime.wMonth,
                SampleTime.wDay,
                SampleTime.wYear,
                SampleTime.wHour,
                SampleTime.wMinute,
                SampleTime.wSecond,
                SampleTime.wMilliseconds);

        //
        // Compute a displayable value for the counter.
        //

        Status = PdhGetFormattedCounterValue(Counter,
                                             PDH_FMT_DOUBLE,
                                             &CounterType,
                                             &DisplayValue);
        if (Status != ERROR_SUCCESS) 
        {
            wprintf(L"\nPdhGetFormattedCounterValue failed with status 0x%x.", Status);
            goto Cleanup;
        }


        wprintf(L",\"%.20g\"", DisplayValue.doubleValue);
    }

Cleanup:

    //
    // Close the query.
    //

    if (Query) 
    {
       PdhCloseQuery(Query);
    }

    int x;
    cin >>x;
}

Most of the code is from this source: msdn.microsoft.com/en-us/library/aa371886%28v=vs.85%29.aspx. I would like to shorten the amount of time between consecutive checks on the context switches (make it less than a second). If anyone has any ideas as to how to do this, that'd be great.

John Roberts
  • 5,885
  • 21
  • 70
  • 124
  • You should attribute where you get things if you just raw copy/paste. For example, you got yours from: http://msdn.microsoft.com/en-us/library/aa371886%28v=vs.85%29.aspx – CrazyCasta Sep 29 '12 at 19:41
  • Not entirely a raw copy/paste, but you're right. I have now added the source. – John Roberts Sep 29 '12 at 19:58
  • What happens if you set `SAMPLE_INTERVAL_MS` to a smaller number, say 100? Don't you then get ten results per second? – Harry Johnston Oct 01 '12 at 04:15
  • Good man, thanks for pointing out the obvious. If you supply this as an answer, I'd be happy to give you the bounty. – John Roberts Oct 04 '12 at 00:52
0

A little searching on the Internet and I found this thing called "Performance Counters", you can either provide counter data or consume counter data with it. In your case, I believe you want to read the data from Performance Counters.

You can consume performance data using either the registry interface or the PDH interface. The PDH interface is easier to use than the registry interface and is recommended for most performance data collection tasks. The PDH interface is essentially a higher-level abstraction of the functionality that the registry interface provides.

Here's another article from Microsoft on Monitoring Context Switches.

WSBT
  • 33,033
  • 18
  • 128
  • 133
  • I have read both the sources which you have cited already. Neither of them provide the working example I requested. – John Roberts Sep 29 '12 at 04:09
0

ETW (Event Tracing for Windows) will give you much deeper information on context switches if you want to. The code likely be significantly more complicated than access to performance counters.

Initial links: Core OS Events in Windows 7 and Windows Performance Toolkit Technical Reference

Search terms: "ETW context switches", "xperf"

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179