Skip to main content
libqcperf is an open-source C library for real-time hardware performance monitoring on Qualcomm platforms. It provides a unified API that dispatches to pluggable backends, each targeting a specific hardware subsystem. Data is collected asynchronously on a background thread and delivered to your application through a registered callback at a configurable streaming rate. Source code and issue tracker: github.com/qualcomm/libqcperf

Backends

Each backend targets a specific subsystem and platform. You select which backends to compile in at build time.
BackendPlatformKey metrics
DUMMYAllSynthetic metrics across two capabilities — useful for integration testing without hardware
CPULinux ARM64Per-core and aggregate CPU load (%), frequency (MHz), effective utilization (%), DCVS frequency limit
NPULinux ARM64DSP/NPU Q6 utilization (%), Q6 clock (KHz), HVX utilization (%), HMX utilization (%)
POWERWindows ARM64Power consumption (mW) across CPU, GPU, and system components
THERMALWindows ARM64Temperature (°C) across 22 thermal zones including CPU clusters and GPU

Build

Linux ARM64 (cross-compile)

1

Install prerequisites

Download the ARM GNU Toolchain for your host and set the path:
export AARCH64_TOOLCHAIN_PATH=/path/to/arm-gnu-toolchain
2

Configure with CMake

cmake -S qcperf -B build \
    -DTARGET_ARCH=linux-aarch64 \
    -DCMAKE_BUILD_TYPE=Release \
    -DProjectVersion="0.1.0.0" \
    -DBACKENDS="CPU;NPU;DUMMY" \
    -DBUILD_SHARED=OFF
Key flags:
FlagValuesDescription
-DTARGET_ARCHlinux-aarch64Selects the cross-compilation toolchain
-DCMAKE_BUILD_TYPERelease / DebugOptimized build or debug symbols
-DProjectVersion"0.1.0.0"Version string embedded in the library
-DBACKENDS"CPU;NPU;DUMMY"Semicolon-separated list of backends to compile in; omit to enable all platform-supported backends
-DBUILD_SHAREDOFF / ONStatic .a or shared .so
3

Build

cmake --build build
Outputs land in the build directory root: libQcPerfCore.a (static) or libQcPerfCore.so (shared), and the QcPerfCoreTest executable.

CMake presets

Edit qcperf/CMakeUserPresets.json to set AARCH64_TOOLCHAIN_PATH and ProjectVersion once, then use the named presets:
cd qcperf
cmake --preset linux-aarch64-release
cmake --build --preset linux-aarch64-release
Available presets: linux-aarch64-debug, linux-aarch64-release, linux-aarch64-debug-shared, linux-aarch64-release-shared.

Windows ARM64

git submodule update --init --recursive
cmake -B build -G "Visual Studio 17 2022" -A ARM64 -DProjectVersion="0.1.0.0"
cmake --build build --config Release

API overview

All functions return a QcPerfReturnCode. Include qcperf.h and qcperf_common.h.
FunctionDescription
qcperf_init()Initialize the library. Must be called first.
qcperf_version(info)Retrieve the library version (build.major.minor.patch).
qcperf_connect_backend(id, msg_cb)Connect to a backend and optionally register a message callback.
qcperf_get_capabilities_info(id, info)Query a connected backend’s capabilities, metrics, and supported rates.
qcperf_set_data_callback(id, data_cb)Register the data callback invoked on each streaming interval.
qcperf_start(id, request)Start monitoring a capability with the given sampling and streaming rates.
qcperf_stop(id, request)Stop an active monitoring session.
qcperf_disconnect_backend(id)Disconnect from a backend and release its resources.
qcperf_deinit()Deinitialize the library and free all resources.
qcperf_get_error_info(code, info)Translate a return code into a human-readable string.

Test application

The build produces QcPerfCoreTest, a single-file C program that exercises the complete library lifecycle. It is the canonical usage example.

Running the test application

./QcPerfCoreTest <backend_id> <sampling_rate_ms> <streaming_rate_ms> <verbose_mode>
ArgumentDescription
backend_idInteger backend identifier (0 = DUMMY, 1 = CPU, 2 = NPU, 3 = POWER, 4 = THERMAL)
sampling_rate_msHardware sampling interval in ms; pass 0 to use the backend’s first supported rate
streaming_rate_msCallback delivery interval in ms; pass 0 to use the backend’s first supported rate
verbose_mode0 = print metric ID and value only; any other value = also print metric name, unit, and description
Examples:
# Dummy backend, verbose output, 100 ms sampling, 500 ms streaming
./QcPerfCoreTest 0 100 500 1

# CPU backend, basic output, backend default rates
./QcPerfCoreTest 1 0 0 0

Lifecycle

The test application walks through the full API sequence:
1

Initialize

qcperf_init();
2

Connect to backend

qcperf_connect_backend(backend_id, &message_callback);
3

Discover capabilities

qcperf_get_capabilities_info(backend_id, backend_info);
4

Register data callback

qcperf_set_data_callback(backend_id, &result_callback);
5

Start, collect, stop — for each capability

qcperf_start(backend_id, &request);
sleep(10);
qcperf_stop(backend_id, &request);
6

Disconnect and deinitialize

qcperf_disconnect_backend(backend_id);
qcperf_deinit();

Callback implementations

The message callback receives backend log messages and prints them with a severity prefix, suppressing debug-level output:
enum QcPerfReturnCode message_callback(struct QcPerfMessage *message) {
    if (message->message_level != QC_PERF_MESSAGE_LEVEL_DEBUG) {
        printf("[%s] Backend message: %s\n", level_str, message->message);
    }
    return QC_PERF_RETURN_CODE_SUCCESS;
}
The data callback is invoked on every streaming interval. In verbose mode it looks up the metric name, unit, and description from a pre-copied QcPerfBackendInfo; in basic mode it prints the metric ID and raw value:
enum QcPerfReturnCode result_callback(struct QcPerfData *data) {
    printf("[DATA] Capability ID: %d, Metrics count: %d\n",
           data->capabilityId, data->metric_response_len);

    for (uint32_t i = 0; i < data->metric_response_len; i++) {
        if (g_is_verbose_print && metric_found) {
            // verbose: name, value, unit, description
            printf("  [%llu] %s: ", timestamp, metric_name);
            print_metric_value(&data->metric_response[i].metric_value);
            printf(" %s (%s)\n", metric_unit, metric_desc);
        } else {
            // basic: metric ID and value only
            printf("  [%llu] Metric ID %d: ", timestamp,
                   data->metric_response[i].metric_id);
            print_metric_value(&data->metric_response[i].metric_value);
            printf("\n");
        }
    }
    return QC_PERF_RETURN_CODE_SUCCESS;
}
The data callback runs on the library’s internal background thread. The test application deep-copies QcPerfBackendInfo before starting any monitoring session so the callback can safely read metric metadata without holding a lock.

Resources