Measuring CPU Usage

by Sergey I. Yevtushenko - Source code: cpu.zip

Foreword
This article will explain techniques which can be used to build CPU usage measurement applications.

Overview of known techniques for CPU usage measurement
Well, there are a lot of them. The first and most widely known one is using an idle priority thread. This is easy to implement method (you can look, for example, into the sources of MEMSIZE; they are found somewhere on Hobbes) but has many disadvantages. The first disadvantage is that they frequently interfere with other programs which use idle priority threads for their own purposes. Another disadvantage is based on the fact that when processor not used, it is sleeping at the HLT instruction inside the OS/2 kernel. In this state the processor consumes less power, has lower temperature and this increases system stability.

The second method is based on using the DosPerfSysCall system call. This function is documented in the "SMP Programming Addendum" for Warp Server SMP. This function uses performance counters available in Pentium and its successors. This is a good method for cases where we need to know only the total system CPU usage and it is widely used. At least WarpCenter and PU Monitor 1.10f uses them. Of course this call will not work on a 486. Also, this call is not available in all version of OS/2 Warp. But Warp 3.0 and Warp 4.0 with the latest fixpacks have them. Another advantage of this method is the ability to measure CPU usage for more than one CPU. This example code comes from the "SMP Programming Addendum", with some modifications:

This example uses the DosPerfSysCall to obtain CPU Utilization information on a uniprocessor. The third method is based on the well-known (but also undocumented) system calls DosQProcStat and DosQuerySysState. These system calls return interesting system information about threads, processes, modules and so on. Other information, also returned by these system calls is rarely used, although they can help measure CPU usage for each process (even for each thread of process) separately. We will look at structures returned by DosQProcStat (I'll return to DosQuerySysState later): As you can see there is information about how each thread uses CPU, both in processes and in system calls. But this information comes in a form which makes direct use nearly impossible: each counter is increased by the system each time the process goes through scheduler. On the other hand this is real information, which comes directly from the operating system and therefore contains the most precise information about CPU usage by each thread. So, we ought to store information about each process in our program and by periodically calling DosQProcStat, check by how many each counter has been changed by system. This is much more complicated way than the simple calculations needed by the two previous methods. To make life a lot easier I have used simple C++ Collection class which has served me for about five years now: And for storing information about each process I have used the following structure: For do manipulation with this structures (adding, removing, refreshing counters and so on) from SortedCollection was inherited new class ProcessCollection: Most of the interesting work is done in ProcessCollection::CollectData. First step is going through the structures returned by QuerySysInfo (this is just a wrapper for DosQProcStat or DosQuerySysState). We check for information about each process in the collection and, if needed, fill in ProcInfo structures from relevant fields from structures returned by QuerySysInfo. At this step each process is also marked for their presence in collection.

In the second step we remove processes not marked at first step and calculate total counters for later use.

The rest is simple: ProcessCollection::Print fills the buffer with information for specified processes. This method is used later for, of course, printing purposes, but also calculates CPU usage from information provided by ProcessCollection::CollectData. This method is mentioned here because of one problem related to VDM processes. For such processes, the system indicates that most of the time is spent inside system. This is, of course, true, because each call to int 21h (do you remember what it is?) or int 16h or any other interrupt (for example, simulation of clock timer, int 8h, or keyboard, int 9h) cause VDM to transfer execution inside a virtual device or the kernel. To avoid loss of interesting information for VDM processed, their system time is also counted as process time.

A few Words About DosQProcStat and DosQuerySysState. The first function is a 16-bit undocumented system call, available in almost all versions of OS/2, and therefore it is a reliable way to obtain system information. DosQuerySysState is relatively new system call (also undocumented, as far as I know), available from somewhere around fixpack 17 for OS/2 Warp 3.0. Often it was broken in intermediate fixpacks. Because it is a 32-bit system call it is able to use buffer for data longer than 64K, but there are many problems: from my experience, DosQuerySysState is very sensitive to length and placement of buffer and bufsz parameters (see DOSQSS.H header file). They should be smaller than the real buffer. If this is not so, in some circumstances, you can get trap 0E when many (or just frequent) VDM sessions are started and stopped. So be careful when using this function. For programs not targeted at monitoring big servers with thousands of processes and threads in PROTECTONLY=YES mode, I advice to use DosQProcStat, because it is stable.

Closing Words
The source code presented in this article uses undocumented calls to the operating system. These calls can be dangerous to your data because, in theory, they can crash your system. So use the code at your own risk.