Skip to main content

How to Measure CPU Usage in VxWorks with spyLib

·1554 words·8 mins
VxWorks CPU Usage SpyLib Embedded Systems Real-Time OS C Programming System Monitoring Performance Analysis
Table of Contents

How to Measure CPU Usage in VxWorks with spyLib

Monitoring CPU utilization is an important part of diagnosing performance problems in VxWorks systems. Excessive CPU consumption can indicate inefficient application logic, task scheduling problems, interrupt overload, or insufficient processing capacity.

VxWorks provides spyLib for monitoring task activity and generating CPU usage statistics. By initializing the spy subsystem and supplying a custom reporting callback, it is possible to capture the standard spy output, identify the IDLE task percentage, and calculate overall CPU utilization programmatically.

This approach is particularly useful when CPU statistics need to be processed automatically rather than simply displayed on the VxWorks shell.

🧭 Using spyLib to Measure CPU Utilization
#

The basic principle is straightforward:

  1. Initialize spyLib.
  2. Start periodic task activity monitoring with spyCommon().
  3. Redirect the generated report to a custom callback.
  4. Locate the IDLE entry in the report.
  5. Extract the idle percentage.
  6. Calculate CPU utilization as:
CPU Usage = 100% - CPU Idle Percentage

For example, if the spy report indicates:

IDLE    37%

then the estimated CPU utilization is:

CPU Usage = 100% - 37%
          = 63%

⚙️ Complete VxWorks CPU Usage Example
#

The following implementation uses spyLib to periodically collect task activity and calculates CPU utilization from the IDLE percentage.

#include "vxWorks.h"
#include "spyLib.h"
#include "stdio.h"
#include "ioLib.h"
#include "stdarg.h"
#include "taskLib.h"
#include "sysLib.h"
#include "string.h"

#define SPYTASKSMAX 100

int data_ana(const char *, ...);

/*
 * Function:
 *     CPU_utilization
 *
 * Description:
 *     Initializes spyLib, collects task activity information,
 *     processes the generated report through data_ana(), and
 *     prints the calculated CPU utilization.
 *
 * Parameters:
 *     None
 *
 * Return:
 *     0
 */
int CPU_utilization(void)
{
    /*
     * Initialize spyLib.
     *
     * SPYTASKSMAX specifies the maximum number of tasks
     * that can be monitored.
     */
    spyLibInit(SPYTASKSMAX);

    /*
     * Start common spy monitoring.
     *
     * The callback function data_ana() receives the generated
     * report information.
     *
     * The monitoring configuration periodically collects
     * task activity and generates reports.
     */
    spyCommon(5, 100, (FUNCPTR)data_ana);

    return 0;
}


/*
 * Function:
 *     data_ana
 *
 * Description:
 *     Processes the output generated by spyLib.
 *
 *     The function searches for the IDLE entry in the report,
 *     extracts its percentage value, and calculates CPU usage.
 *
 * Parameters:
 *     fmtPtn
 *         Format string supplied by the spy reporting mechanism.
 *
 *     ...
 *         Variable arguments used to construct the report line.
 *
 * Return:
 *     0
 */
int data_ana(const char *fmtPtn, ...)
{
    char rbuf[256];
    const char *IDLE = "IDLE";
    char percent[50];

    int i = 0;
    int j = 0;
    int p;

    va_list vl;

    va_start(vl, fmtPtn);

    /*
     * Format the variable arguments into rbuf.
     *
     * This reconstructs one line of spyLib output.
     */
    vsprintf(rbuf, fmtPtn, vl);

    /*
     * Check whether this line contains the IDLE task.
     */
    if (strstr(rbuf, IDLE) != NULL)
    {
        printf("%s\n", rbuf);

        /*
         * Search for the percentage value.
         *
         * Characters before '%' that are numeric are collected
         * into the percent buffer.
         */
        for (i = 0;
             (i < 256) && (rbuf[i] != '%');
             i++)
        {
            if (rbuf[i] >= '0' && rbuf[i] <= '9')
            {
                percent[j] = rbuf[i];
                j++;
            }
        }

        percent[j] = '\0';

        /*
         * Convert the extracted percentage from text to integer.
         */
        p = atoi(percent);

        /*
         * CPU utilization is the inverse of CPU idle time.
         */
        printf("CPU use percent = %i%%\n", 100 - p);
    }

    va_end(vl);

    return 0;
}

📊 How the Calculation Works
#

The key part of the implementation is the search for the IDLE entry:

if (strstr(rbuf, IDLE) != NULL)

The VxWorks spy report contains information about task activity. The IDLE task represents the amount of processor time that is not being consumed by normal workloads.

The code therefore extracts the numeric percentage preceding %:

for (i = 0;
     (i < 256) && (rbuf[i] != '%');
     i++)
{
    if (rbuf[i] >= '0' && rbuf[i] <= '9')
    {
        percent[j] = rbuf[i];
        j++;
    }
}

After converting the extracted string to an integer:

p = atoi(percent);

CPU utilization is calculated as:

100 - p

For example:

IDLE = 12%

produces:

CPU use percent = 88%

Similarly:

IDLE = 75%

produces:

CPU use percent = 25%

🔄 Understanding spyLibInit() and spyCommon()
#

The monitoring process begins with:

spyLibInit(SPYTASKSMAX);

Here, SPYTASKSMAX is defined as:

#define SPYTASKSMAX 100

This establishes the maximum number of tasks that the spy subsystem can monitor.

The next call starts periodic monitoring:

spyCommon(5, 100, (FUNCPTR)data_ana);

The callback:

(FUNCPTR)data_ana

allows the application to intercept the report generated by the spy subsystem.

Instead of relying exclusively on shell output, the application can therefore inspect each generated line and selectively process the information it needs.

This provides a useful foundation for integrating CPU monitoring into a larger embedded diagnostics system.

📡 Sending CPU Statistics to Another System
#

The data_ana() callback can also be extended beyond local console output.

For example, the CPU utilization result could be sent to a monitoring PC, gateway, or management server through UDP.

The overall architecture could look like:

              VxWorks Target
                    |
                    v
                spyLib
                    |
                    v
             data_ana()
                    |
          +---------+---------+
          |                   |
          v                   v
      Shell Output       CPU Calculation
                              |
                              v
                       UDP Monitoring
                              |
                              v
                     Remote Monitoring PC

This makes the same mechanism useful for remote health monitoring in deployed embedded systems.

🛡️ Improving Buffer Safety
#

The original implementation uses:

vsprintf(rbuf, fmtPtn, vl);

Although this works when the generated report is guaranteed to remain below 256 bytes, vsprintf() does not perform bounds checking.

A safer implementation should use a bounded formatting function when supported by the VxWorks toolchain:

vsnprintf(rbuf, sizeof(rbuf), fmtPtn, vl);

This prevents an unexpectedly long report line from overflowing rbuf.

Likewise, the percent array should be protected against excessive numeric characters. A safer version could limit the number of extracted digits:

if (rbuf[i] >= '0' &&
    rbuf[i] <= '9' &&
    j < sizeof(percent) - 1)
{
    percent[j++] = rbuf[i];
}

These changes are especially important when the monitoring code runs continuously in a production system.

🧩 Improving Percentage Parsing
#

The current parser assumes that all numeric characters before the first % belong to the CPU percentage.

For a tightly controlled spy output format, this can be sufficient. However, if the line contains other numeric fields before the percentage, the parser could accidentally concatenate unrelated numbers.

A more robust implementation should parse the specific column containing the percentage rather than simply collecting every digit before %.

For example, if the report format is stable, the code can tokenize the line and identify the field associated with the CPU percentage.

This avoids relying on the exact spacing or position of unrelated fields.

⏱️ CPU Usage Is an Average, Not an Instantaneous Value
#

The value reported by spyLib should be understood as an activity measurement over a sampling interval rather than an instantaneous measurement.

Consequently, CPU utilization such as:

CPU use percent = 72%

means that approximately 72% of the measured processor time was occupied during the relevant monitoring period.

This distinction matters when diagnosing short-lived CPU spikes.

For example:

Time:    0s    1s    2s    3s    4s
CPU:     20%   95%   90%   25%   30%

may produce a considerably different average from a system that continuously operates at:

CPU:     52%   51%   53%   52%   52%

Therefore, CPU monitoring should ideally be combined with task-level statistics when investigating transient performance problems.

🔍 Task-Level Analysis
#

Overall CPU utilization tells you how busy the processor is, but it does not immediately identify which task is responsible for the load.

For example:

CPU Usage: 92%

only indicates that the processor is heavily occupied.

A useful diagnostic workflow is:

CPU Usage
    |
    +-- Low
    |    |
    |    +-- Check I/O or synchronization bottlenecks
    |
    +-- High
         |
         +-- Inspect task activity
         |
         +-- Identify CPU-intensive task
         |
         +-- Check interrupt load
         |
         +-- Check scheduling behavior
         |
         +-- Optimize application code

This makes spyLib particularly useful because it can provide task activity information in addition to the overall idle percentage.

🚀 Practical Considerations for VxWorks Systems
#

When integrating CPU monitoring into a real embedded product, several points should be considered:

  • Avoid repeatedly initializing the spy subsystem if monitoring is already active.
  • Use bounded string operations wherever possible.
  • Validate all parsed numeric values before calculating CPU utilization.
  • Avoid excessive printf() or logMsg() calls in high-frequency monitoring paths.
  • Consider sending summarized statistics rather than every raw report line over the network.
  • Account for interrupt processing when interpreting CPU utilization.
  • Use task-level statistics when overall CPU usage is insufficient to identify the bottleneck.
  • Choose a monitoring interval appropriate for the application rather than assuming that a single interval is suitable for every workload.

📌 Summary
#

The essential idea behind this VxWorks CPU monitoring technique is simple:

spyLib
   |
   v
Collect task activity
   |
   v
Generate spy report
   |
   v
data_ana()
   |
   v
Find "IDLE"
   |
   v
Extract idle percentage
   |
   v
CPU Usage = 100% - Idle%

The core implementation is therefore:

spyLibInit(SPYTASKSMAX);

spyCommon(5, 100, (FUNCPTR)data_ana);

followed by processing the IDLE entry:

p = atoi(percent);

printf("CPU use percent = %i%%\n", 100 - p);

This provides a lightweight way to obtain CPU utilization directly from a VxWorks target and can be extended for shell diagnostics, automated logging, or remote monitoring through UDP.

For production use, the most important improvements are safe formatted output, bounded buffer handling, robust percentage parsing, and appropriate sampling intervals. These changes make the monitoring mechanism considerably safer and more reliable for long-running embedded systems.

Related

Building a VxWorks Telnet Client: Protocol, NVT Negotiation, and C Implementation
·1664 words·8 mins
VxWorks Telnet TCP NVT C Programming Embedded Systems Networking Real-Time OS
VxWorks: Check File System Capacity and Free Space
·1365 words·7 mins
VxWorks VxWorks File System DosFS HRFS Statfs Embedded Systems C Programming RTOS
VxWorks Programming Guide: Tasks, IPC, I/O, Semaphores
·2629 words·13 mins
VxWorks RTOS C Programming TaskLib SemLib MsgQLib Socket IPC Embedded Systems