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.
| Backend | Platform | Key metrics |
|---|
DUMMY | All | Synthetic metrics across two capabilities — useful for integration testing without hardware |
CPU | Linux ARM64 | Per-core and aggregate CPU load (%), frequency (MHz), effective utilization (%), DCVS frequency limit |
NPU | Linux ARM64 | DSP/NPU Q6 utilization (%), Q6 clock (KHz), HVX utilization (%), HMX utilization (%) |
POWER | Windows ARM64 | Power consumption (mW) across CPU, GPU, and system components |
THERMAL | Windows ARM64 | Temperature (°C) across 22 thermal zones including CPU clusters and GPU |
Build
Linux ARM64 (cross-compile)
Install prerequisites
Download the ARM GNU Toolchain for your host and set the path:export AARCH64_TOOLCHAIN_PATH=/path/to/arm-gnu-toolchain
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:| Flag | Values | Description |
|---|
-DTARGET_ARCH | linux-aarch64 | Selects the cross-compilation toolchain |
-DCMAKE_BUILD_TYPE | Release / Debug | Optimized 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_SHARED | OFF / ON | Static .a or shared .so |
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.
| Function | Description |
|---|
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>
| Argument | Description |
|---|
backend_id | Integer backend identifier (0 = DUMMY, 1 = CPU, 2 = NPU, 3 = POWER, 4 = THERMAL) |
sampling_rate_ms | Hardware sampling interval in ms; pass 0 to use the backend’s first supported rate |
streaming_rate_ms | Callback delivery interval in ms; pass 0 to use the backend’s first supported rate |
verbose_mode | 0 = 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:
Connect to backend
qcperf_connect_backend(backend_id, &message_callback);
Discover capabilities
qcperf_get_capabilities_info(backend_id, backend_info);
Register data callback
qcperf_set_data_callback(backend_id, &result_callback);
Start, collect, stop — for each capability
qcperf_start(backend_id, &request);
sleep(10);
qcperf_stop(backend_id, &request);
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