CN

Android Systrace Basics - CPU Info Interpretation

Word count: 3.4kReading time: 20 min
2019/12/21
loading

This is the twelfth article in the Systrace series, primarily providing a brief introduction to the CPU information area (Kernel) in Systrace. It covers how to view CPU-related information output by the Kernel module, including CPU frequency, scheduling, frequency locking, and core locking.

The purpose of this series is to view the overall operation of the Android system from a different perspective using Systrace, while also providing an alternative angle for learning the Framework. Perhaps you’ve read many articles about the Framework but can never remember the code, or you’re unclear about the execution flow. Maybe from Systrace’s graphical perspective, you can gain a deeper understanding.

Version note (reviewed 2026-10-13): This article retains its 2019 Systrace figures. SoC models, CPU numbering, cpuset paths, and power states depend on the device and OS; errors in the old specification table are marked nearby. Framework/tool examples were checked against AOSP android-10.0.0_r1 and android-16.0.0_r1, with kernel semantics referenced to Linux 6.6. Device inspection and affinity commands were not executed during this review.

Table of Contents

Series Article Index

  1. Introduction to Systrace
  2. Systrace Basics - Prerequisites for Systrace
  3. Systrace Basics - Why 60 fps?
  4. Android Systrace Basics - SystemServer Explained
  5. Systrace Basics - SurfaceFlinger Explained
  6. Systrace Basics - Input Explained
  7. Systrace Basics - Vsync Explained
  8. Systrace Basics - Vsync-App: Detailed Explanation of Choreographer-Based Rendering Mechanism
  9. Systrace Basics - MainThread and RenderThread Explained
  10. Systrace Basics - Binder and Lock Contention Explained
  11. Systrace Basics - Triple Buffer Explained
  12. Systrace Basics - CPU Info Explained
  13. Systrace Smoothness in Action 1: Understanding Jank Principles
  14. Systrace Smoothness in Action 2: Case Analysis - MIUI Launcher Scroll Jank Analysis
  15. Systrace Smoothness in Action 3: FAQs During Jank Analysis
  16. Systrace Responsiveness in Action 1: Understanding Responsiveness Principles
  17. Systrace Responsiveness in Action 2: Responsiveness Analysis - Using App Startup as an Example
  18. Systrace Responsiveness in Action 3: Extended Knowledge on Responsiveness
  19. Systrace Thread CPU State Analysis Tips - Runnable
  20. Systrace Thread CPU State Analysis Tips - Running
  21. Systrace Thread CPU State Analysis Tips - Sleep and Uninterruptible Sleep

CPU Area Legend

Read this with Perfetto 9: CPU information. Locate the target thread’s Runnable, Running, and sleep intervals, then correlate actual CPU placement, frequency, idle state, and competing work. CPU/thread tracks show timing; data such as sched, thread_state, and counters supports aggregation. Table details depend on the Trace Processor version.

This is an eight-logical-CPU example from an older Android device. The original text called it a Snapdragon 845, but the image alone does not establish its SoC. The three-tier annotation in a later figure is not independent device-identification evidence either.

CPU Area Legend

Four useful kinds of information are:

  1. CPU tracks reconstruct which thread occupied each logical CPU from scheduling events. Thread colors do not rank performance.
  2. Clock Frequency contains recorded events or samples. The selected 2419200, in kHz, means 2.4192 GHz, not utilization.
  3. Clock Frequency Limits describes allowed bounds, not instantaneous frequency or the identity of a boost requester.
  4. C-State/idle describes CPU idleness. The old viewer can renumber raw states, as explained below.

The top CPU usage view aggregates utilization; inspect its time window and normalization. Low overall utilization can coexist with one saturated CPU or a critical thread waiting Runnable. High frequency does not mean continuous execution, and an idle CPU’s track may retain its last recorded frequency.

When a task is slow, the first questions might be: did it run on a small core? Was the frequency too low? For something like fingerprint unlock, could it run on a big core? Use those questions to start reading the trace, then separate scheduling delay, lock/Binder/I/O waits, CPU execution, and critical-path dependencies. Small-core placement or low frequency is a clue, not proof of a scheduling error. More frequency cannot remove a blocking dependency. Frequency events are often emitted only on changes, so missing initial data is not 0 Hz; supported sysfs polling can supplement the capture. See Perfetto frequency and idle data.

For basic thread-state interpretation, see Systrace Prerequisites.

Core Architecture

This article retains homogeneous, big/little, and big/medium/little as ways to discuss the old figures, not an exhaustive classification of modern SoCs. Microarchitecture, performance tier, shared cache, frequency policy, and Linux CPU numbering are distinct. Cores with the same architecture can belong to different frequency tiers.

big.LITTLE Architecture

The 0–3, 4–6, and 7 groups are annotations for a particular sample. They are not a universal numbering rule. Restricting background work to small cores is a policy choice involving cpusets, affinity, and scheduling, not a hardware rule.

Non-big.LITTLE Architecture

Homogeneous cores share a core design; they need not always run at the same frequency or power, or switch on and off together. For example, Helio A22 uses four Cortex-A53 cores. Shared frequency control and independent idle/online behavior still depend on the platform.

Snapdragon 636 was an incorrect homogeneous example. Its official brief specifies four performance and four efficiency cores: an eight-core Kryo 260 CPU, not the original text’s other description of “four small plus two large.”

big.LITTLE Architecture

Different performance/efficiency tiers let the scheduler place work according to load, allowed CPUs, and power constraints. CPU 0–3 need not be small cores or CPU 4–7 large cores. Clock speed alone is also insufficient for comparing different microarchitectures.

Snapdragon 845’s official specifications describe Kryo 385 with four performance and four efficiency cores, clocked up to 2.8 GHz. The old diagram below is retained as an illustration. Its peak rates and performance-uplift percentages are not measurements made in this review, and a Kryo product name should not be treated as an unchanged stock Cortex implementation.

Snapdragon 845

The ratio varies: Snapdragon 670, for example, uses 2+6 cores. Whether it feels like enough also comes down to how much you’re willing to spend. You get what you pay for; Qualcomm isn’t running a charity. Engineering analysis needs the particular workload and device.

The following old table is outdated and contains errors; it is not a reliable specification reference. Corrections follow the retained image:

Model Verified official information Correction to old image
Snapdragon 670 Kryo 360, 2 performance + 6 efficiency; Adreno 615 Not 4+4 or Adreno 620
Snapdragon 460 Eight-core Kryo 240; Adreno 610 Not Kryo 360 Silver or Adreno 605
Snapdragon 640 column No official specification supporting this column was verified Do not cite it as a released-product specification

Sources: 670 brief, 460 brief. Check the actual device’s policies and frequency tables for operating points.

Big-Medium-Small Core Architecture

Historical examples include:

  • Snapdragon 855’s Kryo 485 has 1 Prime + 3 performance + 4 efficiency cores. Its official technical presentation lists 2.84, 2.42, and 1.80 GHz, respectively.
  • Helio X30 has 2 Cortex-A73 + 4 Cortex-A53 + 4 Cortex-A35 cores.
  • Kirin 980 has 2 Cortex-A76 at 2.6 GHz, 2 Cortex-A76 at 1.92 GHz, and 4 Cortex-A55 at 1.8 GHz. The same A76 design can serve different tiers.

“Prime” and “medium” are descriptive tiers, not fixed core counts or Linux scheduling classes. Some numbers in the old comparison, including 855’s 2.41/1.78 GHz, differ from the official nominal figures. Without measurement conditions, they should not be used to compare peaks or infer performance.

Snapdragon 865, introduced in 2019, retained a 1+3+4 arrangement; the diagram labels its Prime core at 2.84 GHz. At the time, I wondered whether the 865 Plus would make it to 3 GHz. The 865 Plus announced in 2020 answered that question with a Prime specification of up to 3.1 GHz.

Newer designs need not retain traditional A5x efficiency cores. The 2025 Snapdragon 8 Elite Gen 5 brief lists custom Oryon cores: 2 Prime up to 4.6 GHz and 6 Performance up to 3.62 GHz. Re-establish the SoC, topology, capacity, and frequency policies when reading a new trace instead of reusing the old numbering diagram.

Core Binding (Pinning)

Pinning restricts the CPUs on which a thread may run. It can reduce migration or constrain background work, but a smaller allowed set can also increase queueing. Pinning does not reserve a CPU, prevent preemption, or guarantee faster execution.

Configuring CPUsets

cpusets constrain CPUs and memory nodes for groups of tasks. Older Android systems commonly have top-app, foreground, background, and system-background, but their names do not specify fixed CPU masks. foreground/boost belongs to some older implementations, not every release.

The original 0-7 / 0-3 masks were not a universal Google default. Android 10 init.rc initially copies available sets from the root group and leaves device configuration to select appropriate CPUs. This is an init-language excerpt, not shell syntax:

1
2
3
mkdir /dev/cpuset/top-app
copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
copy /dev/cpuset/mems /dev/cpuset/top-app/mems

The old cgroup-v1 path is lowercase /dev/cpuset/.../cpus. On a device exposing these nodes, read-only inspection can look like this:

1
2
3
4
5
6
7
$ adb shell cat /dev/cpuset/top-app/cpus
0-7
$ adb shell cat /dev/cpuset/top-app/tasks
1687
1689
1690
3559

These outputs are historical examples. tasks lists task/thread TIDs, not necessarily four application processes. Membership and allowed CPUs can change. Also inspect /proc/<TID>/cgroup and Cpus_allowed_list in /proc/<TID>/status, substituting a real thread ID.

cgroup v2 uses nodes such as cpuset.cpus and cpuset.cpus.effective; distinguish process membership in cgroup.procs from thread membership in cgroup.threads. Do not append v1’s tasks to arbitrary v2 paths. Android can combine v1/v2 controllers; use the device’s actual mounts and configuration. See Linux 6.6 cgroup v2.

A service can also select a group at startup. This is the complete service declaration from Android 10 lmkd.rc:

1
2
3
4
5
6
7
8
service lmkd /system/bin/lmkd
class core
user lmkd
group lmkd system readproc
capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE
critical
socket lmkd seqpacket 0660 system system
writepid /dev/cpuset/system-background/tasks

Android 12 introduced init’s task_profiles replacement for deprecated writepid usage. Android 16 lmkd.rc uses task_profiles ServiceCapacityLow; the default profile joins cpuset/system-background. Device/API/vendor configuration can override profiles, so the name alone does not identify the CPU set. See AOSP task profiles.

App scheduling groups can change with process state. These Android 10 Process.java constants belong to a hidden Framework contract, not a general app pinning API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* Default thread group -
* has meaning with setProcessGroup() only, cannot be used with setThreadGroup().
* When used with setProcessGroup(), the group of each thread in the process
* is conditionally changed based on that thread's current priority, as follows:
* threads with priority numerically less than THREAD_PRIORITY_BACKGROUND
* are moved to foreground thread group. All other threads are left unchanged.
* @hide
*/
public static final int THREAD_GROUP_DEFAULT = -1;

/**
* Background thread group - All threads in
* this group are scheduled with a reduced share of the CPU.
* Value is same as constant SP_BACKGROUND of enum SchedPolicy.
* FIXME rename to THREAD_GROUP_BACKGROUND.
* @hide
*/
public static final int THREAD_GROUP_BG_NONINTERACTIVE = 0;

/**
* Foreground thread group - All threads in
* this group are scheduled with a normal share of the CPU.
* Value is same as constant SP_FOREGROUND of enum SchedPolicy.
* Not used at this level.
* @hide
**/
private static final int THREAD_GROUP_FOREGROUND = 1;

/**
* System thread group.
* @hide
**/
public static final int THREAD_GROUP_SYSTEM = 2;

/**
* Application audio thread group.
* @hide
**/
public static final int THREAD_GROUP_AUDIO_APP = 3;

/**
* System audio thread group.
* @hide
**/
public static final int THREAD_GROUP_AUDIO_SYS = 4;

/**
* Thread group for top foreground app.
* @hide
**/
public static final int THREAD_GROUP_TOP_APP = 5;

/**
* Thread group for RT app.
* @hide
**/
public static final int THREAD_GROUP_RT_APP = 6;

/**
* Thread group for bound foreground services that should
* have additional CPU restrictions during screen off
* @hide
**/
public static final int THREAD_GROUP_RESTRICTED = 7;

They are not CPU numbers, and not every constant implies a same-named cpuset directory. THREAD_GROUP_RT_APP alone does not select SCHED_FIFO. Android 10 OomAdjuster computes scheduling groups and uses a handler to perform operations such as setProcessGroup. Follow the native implementation and device configuration too; oom_adj is not a CPU priority value.

Configuring Affinity

taskset is a command-line tool using sched_getaffinity / sched_setaffinity. Linux affinity is per thread: changing the main thread does not automatically update every existing thread. Effective placement is further restricted by cpusets and online CPUs, and permission requirements apply. See sched_setaffinity.

Usage of taskset

Android 10/16 Toybox taskset uses hexadecimal masks and supports -p and -a, not util-linux’s -c CPU-list option. These are device-shell command forms; replace TID, PID, and COMMAND. Mutation forms belong in authorized, controlled experiments:

Purpose Android Toybox form
Inspect one thread’s allowed CPUs taskset -p TID
Inspect a process’s current threads taskset -ap PID
Restrict one thread to CPU 3 taskset -p 8 TID
Start a command with CPU 3 affinity taskset 8 COMMAND

The least significant bit is CPU 0. Hexadecimal 5, binary 0101, therefore selects CPU 0 and CPU 2. CPU 3 uses hexadecimal mask 8. This is an allowed set, not the thread’s current executing CPU; use scheduling tracks for actual placement. Desktop util-linux versions supporting -c can use taskset -pc 3 PID, but that syntax does not transfer to these Android Toybox versions.

Scheduling Algorithms

The allowed set determines where a task may run; the scheduler chooses where and when it runs. Linux 6.6 EAS can use CPU capacity, load, and an energy model, while vendors may apply other policies. Seeing a thread remain on one core does not prove affinity pinning. Check affinity/cpusets and the available choices before judging placement.

Frequency Locking

Frequency scaling, task placement, and performance hints interact, but there is no universal sequence of maximizing a small core before trying medium and then large cores. Latency-sensitive work such as app launch may request a boost. Whether that raises a frequency floor, adjusts uclamp, changes placement, and for how long depends on OS, driver, and vendor policy.

Launch, installation, rotation, window animation, list flinging, and games are possible optimization scenarios, not a promise that Android maximizes every CPU/GPU/I/O/BUS resource in each case. Thermal, power-mode, and platform limits still apply. Linux 6.6 uclamp changes a performance-demand signal and can influence schedutil frequency selection; it does not promise a fixed MHz value or exclusive use of a large core.

Here is an older Qualcomm frequency-limit example:

The selected bounds are 768000–2016000 kHz: 768 MHz–2.016 GHz. Since the minimum and maximum differ, this is an allowed range rather than a single locked frequency. It also does not identify the requester, temperature, or actual execution rate.

Linux CPUFreq often manages multiple CPUs through a shared policy. related_cpus helps identify its membership. For many drivers, scaling_cur_freq reflects the most recent requested frequency rather than precise instantaneous hardware frequency. Correlate frequency events, idle, workload, and thermal evidence; see CPUFreq.

From Android 12/API 31, apps on supported devices can use PerformanceHintManager to report work-duration targets and completion data for associated threads. This is a performance-hint mechanism, not an app-controlled core or frequency lock.

CPU States

These are the old viewer’s C-State tracks. Values 0, 1, 2, and 3 belong to this sample, not a fixed four-state model for every Android CPU:

Separate raw events from viewer transformations. The Catapult power_parser already present before the 2019 article displays raw cpu_idle value 4294967295 (0xffffffff, idle exit) as 0 and adds 1 to other raw states. On that parsing path, displayed 1 means raw state 0, and displayed 3 means raw state 2.

Perfetto SQL’s cpuidle counter retains raw values: 0 means entry into idle state 0; 4294967295 means idle exit. Do not reuse the old viewer’s meaning of zero. Missing entry/exit events leave boundary state unknown.

These are indices into the platform’s cpuidle driver states. Where permissions allow, inspect name, desc, latency, and residency under /sys/devices/system/cpu/cpuN/cpuidle/stateN/ for the relevant CPU/state and units; latency and residency are normally expressed in microseconds. Deeper states often trade wakeup delay for savings, but a recorded request does not always establish the state that hardware actually reached. See Linux 6.6 CPUIdle.

Idle and offline/hotplug are different mechanisms. Modern kernels still support CPU hotplug; availability and policy depend on the device. CPU idle is also distinct from whole-device suspend. The original cross-platform HLT description, fixed 10 ns/100 ns wakeup claims, and “70% savings” do not explain these ARM tracks and have been removed from this interpretation.

Detailed Information in Systrace

Old Systrace HTML embeds trace data that can be inspected as text; compatibility of the old interactive viewer with current Chrome needs separate verification. Perfetto protobuf data requires appropriate decoding and is not the same plain-text format.

Here is the original scheduling record:

1
appEventThread-8193  [001] d..2 1638545.400415: sched_switch: prev_comm=appEventThread prev_pid=8193 prev_prio=97 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120
Field Meaning in this record
appEventThread-8193 Current task name and Linux TID; 8193 need not be the process TGID
[001] The event occurred on logical CPU 1, not CPU 0
d..2 Four character fields, not four bits: IRQs disabled, no resched flag shown, ordinary context, preempt depth 2
1638545.400415 Trace timestamp in seconds, in the selected trace-clock/format domain; not scheduling latency or calendar time
prev_* / next_* Outgoing and incoming tasks; this switches to CPU 1’s idle task swapper/1, TID 0
prev_state=S The outgoing thread enters interruptible sleep; this is not evidence that it remained Runnable and was preempted
prev_prio=97 / next_prio=120 Kernel scheduling-priority values, not direct nice values or enough information to distinguish every policy

d..2 describes context at event time, not an entire app interval spent with interrupts or preemption disabled. Other kernel versions can add flag fields. See the Linux ftrace format.

Other original samples and their interpretation:

  1. sched_waking: comm=kworker/u16:4 pid=17373 prio=120 target_cpu=003: waking TID 17373 toward CPU 3; it is not already Running and can migrate later.
  2. sched_blocked_reason: pid=17373 iowait=0 caller=rpmh_write_batch+0x638/0x7d0: this extension reports a blocking location and iowait flag. Zero does not exclude all I/O/hardware waits, and symbol offsets are not source lines. Not every kernel emits this event.
  3. cpu_idle: state=0 cpu_id=3: CPU 3 enters raw idle state 0, not an active C0 state.
  4. softirq_raise: vec=6 [action=TASKLET]: raises the softirq; inspect entry/exit for execution rather than treating the raise as completion.
  5. cpu_frequency_limits: min=1555200 max=1785600 cpu_id=0
  6. cpu_frequency_limits: min=710400 max=2419200 cpu_id=4
  7. cpu_frequency_limits: min=825600 max=2841600 cpu_id=7

The last three events report frequency limits in kHz. cpu_id can identify a policy’s representative CPU, so the limit need not affect only one core. These records do not replace actual frequency samples. The visual trace, raw events, and matching-version source should explain the same behavior.

About Me && Blog

Below is my personal intro and related links. I look forward to exchanging ideas with fellow professionals. “When three walk together, one can always be my teacher!”

  1. Blogger Intro: Includes personal WeChat and WeChat group links.
  2. Blog Content Navigation: A guide for my blog content.
  3. Curated Excellent Blog Articles - Android Performance Optimization Must-Knows: Welcome to recommend projects/articles.
  4. Android Performance Optimization Knowledge Planet: The paid community stopped operating in 2026 and no longer accepts new members; this link preserves its history.

One walks faster alone, but a group walks further together.

Scan WeChat QR Code

References

  1. Linux sched_setaffinity: thread affinity
  2. AOSP cgroups and task profiles
  3. Linux 6.6 CPUFreq
  4. Linux 6.6 CPUIdle
  5. Perfetto CPU frequency and idle

Spring Bamboo Shoots

I took a photo and thought it was quite nice, sharing it with everyone.

CATALOG
  1. 1. Table of Contents
  • Series Article Index
  • CPU Area Legend
  • Core Architecture
    1. 1. Non-big.LITTLE Architecture
    2. 2. big.LITTLE Architecture
    3. 3. Big-Medium-Small Core Architecture
  • Core Binding (Pinning)
    1. 1. Configuring CPUsets
    2. 2. Configuring Affinity
      1. 2.1. Usage of taskset
    3. 3. Scheduling Algorithms
  • Frequency Locking
  • CPU States
  • Detailed Information in Systrace
  • About Me && Blog
  • References
  • Spring Bamboo Shoots