Repository navigation
Debugging Tool Perf
← Debugging Tools and Commands
Linux perf is the default profiler for xemu on Linux and Steam Deck. It can sample one thread's call stacks, separate scheduler wait/delay/runtime, and collect hardware counters without adding xemu instrumentation.
| Property | Details |
|---|---|
| Supported OS | Linux; event availability depends on the kernel, CPU, and permissions. |
| Install | Distribution package normally named perf or matching linux-tools. SteamOS 3.8.16 already includes /usr/bin/perf. |
| Verify |
perf --version, perf list, and a short controlled capture. |
| Primary outputs |
perf.data, sched.data, text/JSON counter summaries. |
| Question | Command family |
|---|---|
| Which code used CPU time? |
perf record and perf report
|
| Was a thread running, blocked, or runnable but unscheduled? |
perf sched record and perf sched timehist
|
| Is executing code instruction-heavy, cache-bound, or branch-heavy? | perf stat |
| Are threads bouncing shared cache lines? |
perf c2c after simpler evidence points to false sharing |
Do not interpret an on-CPU profile as an explanation for off-CPU time. Use the scheduler capture alongside the sampling capture.
Thread IDs are local to one run. Record the process tree and thread list immediately before capture:
pgrep -n -x xemu
ps -L -p 12345 \
-o pid,tid,psr,pcpu,stat,wchan:32,comm
top -H -p 12345Replace 12345 with the PID printed by pgrep. In later commands, replace 12346 with the selected thread's TID.
Save the selected PID, TID, thread name, source commit, executable SHA-256, matching symbols, workload boundary, and capture start/stop times. A thread name is useful context but is not immutable identity.
For an optimized binary with matching DWARF:
perf record \
-F 499 \
-g \
--call-graph dwarf \
-t 12346 \
-o perf-thread.data \
-- sleep 10
perf report -i perf-thread.dataUse a lower sampling rate when the Deck or workload is sensitive to profiling overhead. If the build retains frame pointers, --call-graph fp is cheaper and often more reliable:
-fno-omit-frame-pointer
Do not assume frame-pointer unwinding is valid merely because perf accepts the option. Verify stack depth and unresolved-frame rate in the result. --call-graph dwarf is the fallback for an optimized build without dependable frame pointers.
Hotspot reads perf.data and provides a flame graph, caller/callee views, and timelines. It is an optional viewer for the same capture, not another source of evidence. Keep perf report available for scripted and remote checks.
A scheduler trace answers a different question:
interval = task runtime + blocked time + runnable delay
Capture the process only for a smaller file:
sudo perf sched record \
-p 12345 \
-o sched.data \
-- sleep 10If the investigation must retain external wakers, capture system-wide for a short bounded interval and filter during analysis:
sudo perf sched record \
-a \
-o sched.data \
-- sleep 10
sudo perf sched timehist \
-i sched.data \
-t 12346 \
--state \
--wakeupstimehist reports wait time, scheduling delay, and run time. Interpret them separately:
- run time: the thread was executing;
- wait time: the thread was not runnable;
- scheduling delay: the thread was runnable but did not have a CPU;
- wakeup: useful attribution evidence, but still inspect the blocked and waking stacks before assigning ownership.
On the checked Steam Deck, unprivileged perf sched record could not read sched:sched_switch; use sudo for scheduler tracing. Ordinary user-space perf record and perf stat worked without privilege for a process owned by the same user.
When the target thread is genuinely on-CPU, collect a bounded counter set:
perf stat \
-t 12346 \
-e cycles:u,instructions:u,branches:u,branch-misses:u,cache-references:u,cache-misses:u \
-- sleep 10The :u suffix restricts the example to user space and is useful under restrictive perf_event_paranoid settings. Confirm the event names with perf list; unsupported or multiplexed counters must remain visible in the report.
Interpretation is comparative, not diagnostic by one number:
| Observation | Possible direction | Does not prove |
|---|---|---|
| High instruction count | Too much actual work | Which work can be removed |
| Low IPC with cache misses | Memory locality or cache pressure | A specific data structure is wrong |
| High branch-miss share | Dispatch or unpredictable branches | That branch elimination improves guest progress |
| High runnable delay | Host CPU contention | That xemu itself consumed the missing time |
Normalize counters to the same guest-progress interval when comparing builds.
Use cache-to-cache analysis only after ordinary samples and counters suggest shared-cache-line contention. It normally needs elevated access and a system-wide capture:
sudo perf c2c record -o c2c.data -- sleep 10
sudo perf c2c report -i c2c.dataFalse-sharing investigation requires the exact binary layout and symbols. Do not infer a production struct change from an address alone.
The 2026-09-21 Deck check found:
SteamOS 3.8.16
kernel 6.16.12-valve24.5-1-neptune-616-gb2f7cfe85e45
perf 6.15-3
kernel.perf_event_paranoid = 2
kernel.kptr_restrict = 0
perf record with DWARF and a user-space perf stat counter set both completed against a short controlled process. Scheduler tracepoints required elevation. Treat this as an appliance snapshot, not a guarantee for later SteamOS versions; run the preflight in the Steam Deck and Linux Thread Diagnosis guide.
- Sampling frequency changes overhead and sample confidence.
- A high sample share is not removable frame time.
- A thread blocked for 20 ms did not consume 20 ms of CPU.
-
perf sched -pcan omit an external waker; use a short-acapture when wakeup attribution matters. - Counter multiplexing and unsupported PMU events can invalidate naive comparisons.
- Symbolize only with the executable/debug artifact from the captured build.
- Profiling results are diagnostic; acceptance still uses an uninstrumented optimized build.
-
perf --version, kernel version, CPU model,perf_event_paranoid, andkptr_restrict; - source/executable/symbol hashes and build flags;
- PID/TID/name mapping and exact capture command;
- raw
perf.dataorsched.dataplus exported summaries; - lost samples, permission warnings, unresolved stacks, and multiplexed counters;
- the exact guest-progress interval and cache/configuration state.
DSP tooling guide and navigation updated November 2, 2026 for experimental PR #384, source 8f51d237. Capture/replay usage and limits.
The earlier wiki-wide review was published November 1, 2026. Emulator source: b7140848. Reviewed wiki content: 1d2b671. Current status · Release availability · Publication process.
- Home
- Current status
- Stable versus main
- Implemented mechanisms
- Controls and defaults
- Measured results and limits
- Complete change register
- System
- CPU and vCPU
- Memory and DMA
- Graphics / NV2A
- APU and audio
- Timing and timers
- Platform and I/O
- Hardware observations
- Fidelity boundaries
- Methodology
- PGR2 routes
- Debugging lag / hot paths
- Tools and commands
- DSP capture, replay and fuzzing
- HLE readiness and public XISO proof
- Concurrent DSP shadow and OpenAL proof
- DSP HLE operation inventory
- DSP HLE fix examples and testing
- Development
- Upstream shortlist
- Wiki maintenance
- Research / retired work
- Optimization catalog
- Hot-path shortcuts
- Researched experiments
- Unproven proposal directory
- PR lessons
- September26 snapshot
Repository · PRs · Issues