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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
|
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
"""
Basic test of Python scripting support for perf.
Ported from tools/perf/scripts/python/check-perf-trace.py
"""
from __future__ import annotations
import argparse
import collections
import perf
unhandled: collections.defaultdict[str, int] = collections.defaultdict(int)
session = None
softirq_vecs = {
0: "HI_SOFTIRQ",
1: "TIMER_SOFTIRQ",
2: "NET_TX_SOFTIRQ",
3: "NET_RX_SOFTIRQ",
4: "BLOCK_SOFTIRQ",
5: "IRQ_POLL_SOFTIRQ",
6: "TASKLET_SOFTIRQ",
7: "SCHED_SOFTIRQ",
8: "HRTIMER_SOFTIRQ",
9: "RCU_SOFTIRQ",
}
_GFP_DMA = 1 << 0
_GFP_HIGHMEM = 1 << 1
_GFP_DMA32 = 1 << 2
_GFP_MOVABLE = 1 << 3
_GFP_RECLAIMABLE = 1 << 4
_GFP_HIGH = 1 << 5
_GFP_IO = 1 << 6
_GFP_FS = 1 << 7
_GFP_ZERO = 1 << 8
_GFP_DIRECT_RECLAIM = 1 << 10
_GFP_KSWAPD_RECLAIM = 1 << 11
_GFP_WRITE = 1 << 12
_GFP_NOWARN = 1 << 13
_GFP_RETRY_MAYFAIL = 1 << 14
_GFP_NOFAIL = 1 << 15
_GFP_NORETRY = 1 << 16
_GFP_MEMALLOC = 1 << 17
_GFP_COMP = 1 << 18
_GFP_NOMEMALLOC = 1 << 19
_GFP_HARDWALL = 1 << 20
_GFP_THISNODE = 1 << 21
_GFP_ACCOUNT = 1 << 22
_GFP_ZEROTAGS = 1 << 23
_GFP_RECLAIM = _GFP_DIRECT_RECLAIM | _GFP_KSWAPD_RECLAIM
_GFP_KERNEL = _GFP_RECLAIM | _GFP_IO | _GFP_FS
_GFP_USER = _GFP_KERNEL | _GFP_HARDWALL
_GFP_HIGHUSER = _GFP_USER | _GFP_HIGHMEM
_GFP_HIGHUSER_MOVABLE = _GFP_HIGHUSER | _GFP_MOVABLE
_GFP_TRANSHUGE_LIGHT = (
_GFP_HIGHUSER_MOVABLE | _GFP_COMP | _GFP_NOMEMALLOC | _GFP_NOWARN
) & ~_GFP_RECLAIM
_GFP_TRANSHUGE = _GFP_TRANSHUGE_LIGHT | _GFP_DIRECT_RECLAIM
GFP_FLAG_NAMES = [
(_GFP_TRANSHUGE, "GFP_TRANSHUGE"),
(_GFP_TRANSHUGE_LIGHT, "GFP_TRANSHUGE_LIGHT"),
(_GFP_HIGHUSER_MOVABLE, "GFP_HIGHUSER_MOVABLE"),
(_GFP_HIGHUSER, "GFP_HIGHUSER"),
(_GFP_USER, "GFP_USER"),
(_GFP_KERNEL | _GFP_ACCOUNT, "GFP_KERNEL_ACCOUNT"),
(_GFP_KERNEL, "GFP_KERNEL"),
(_GFP_RECLAIM | _GFP_IO, "GFP_NOFS"),
(_GFP_HIGH | _GFP_KSWAPD_RECLAIM, "GFP_ATOMIC"),
(_GFP_RECLAIM, "GFP_NOIO"),
(_GFP_KSWAPD_RECLAIM | _GFP_NOWARN, "GFP_NOWAIT"),
(_GFP_DMA, "GFP_DMA"),
(_GFP_DMA32, "GFP_DMA32"),
(_GFP_RECLAIM, "__GFP_RECLAIM"),
(_GFP_DMA, "__GFP_DMA"),
(_GFP_HIGHMEM, "__GFP_HIGHMEM"),
(_GFP_DMA32, "__GFP_DMA32"),
(_GFP_MOVABLE, "__GFP_MOVABLE"),
(_GFP_RECLAIMABLE, "__GFP_RECLAIMABLE"),
(_GFP_HIGH, "__GFP_HIGH"),
(_GFP_IO, "__GFP_IO"),
(_GFP_FS, "__GFP_FS"),
(_GFP_ZERO, "__GFP_ZERO"),
(_GFP_DIRECT_RECLAIM, "__GFP_DIRECT_RECLAIM"),
(_GFP_KSWAPD_RECLAIM, "__GFP_KSWAPD_RECLAIM"),
(_GFP_WRITE, "__GFP_WRITE"),
(_GFP_NOWARN, "__GFP_NOWARN"),
(_GFP_RETRY_MAYFAIL, "__GFP_RETRY_MAYFAIL"),
(_GFP_NOFAIL, "__GFP_NOFAIL"),
(_GFP_NORETRY, "__GFP_NORETRY"),
(_GFP_MEMALLOC, "__GFP_MEMALLOC"),
(_GFP_COMP, "__GFP_COMP"),
(_GFP_NOMEMALLOC, "__GFP_NOMEMALLOC"),
(_GFP_HARDWALL, "__GFP_HARDWALL"),
(_GFP_THISNODE, "__GFP_THISNODE"),
(_GFP_ACCOUNT, "__GFP_ACCOUNT"),
(_GFP_ZEROTAGS, "__GFP_ZEROTAGS"),
]
def trace_begin() -> None:
"""Called at the start of trace processing."""
print("in trace_begin")
def trace_end() -> None:
"""Called at the end of trace processing."""
print_unhandled()
print("in trace_end")
def symbol_str(event_name: str, field_name: str, value: int) -> str:
"""Resolves symbol values to strings."""
# Note: The standalone Python API currently lacks dynamic libtraceevent
# formatting (equivalent to _perf_trace_context.symbol_str())
if event_name == "irq__softirq_entry" and field_name == "vec":
return softirq_vecs.get(value, str(value))
return str(value)
def flag_str(event_name: str, field_name: str, value: int) -> str:
"""Resolves flag values to strings."""
# Note: The standalone Python API currently lacks dynamic libtraceevent
# formatting (equivalent to _perf_trace_context.flag_str())
if event_name == "kmem__kmalloc" and field_name == "gfp_flags":
if value == 0:
return "none"
names = []
rem = value
for mask, name in GFP_FLAG_NAMES:
if (rem & mask) == mask:
names.append(name)
rem &= ~mask
if rem:
names.append(f"0x{rem:x}")
return "|".join(names)
return str(value)
def print_header(event_name: str, sample: perf.sample_event) -> None:
"""Prints common header for events."""
secs = sample.sample_time // 1000000000
nsecs = sample.sample_time % 1000000000
comm = "[unknown]"
try:
if session:
thread = session.find_thread(sample.sample_pid, sample.sample_tid)
if thread:
comm = thread.comm() or "[unknown]"
except (TypeError, AttributeError):
pass
print(f"{event_name:<20} {sample.sample_cpu:5} {secs:05}.{nsecs:09} "
f"{sample.sample_tid:8} {comm:<20} ", end=' ')
def print_uncommon(sample: perf.sample_event) -> None:
"""Prints uncommon fields for tracepoints."""
# Fallback to 0 if field not found (e.g. on older kernels or if not tracepoint)
pc = getattr(sample, 'common_preempt_count', 0)
flags = getattr(sample, 'common_flags', 0)
lock_depth = getattr(sample, 'common_lock_depth', 0)
print(f"common_preempt_count={pc}, common_flags={flags}, "
f"common_lock_depth={lock_depth}, ", end='')
def irq__softirq_entry(sample: perf.sample_event) -> None:
"""Handles irq:softirq_entry events."""
print_header("irq__softirq_entry", sample)
print_uncommon(sample)
print(f"vec={symbol_str('irq__softirq_entry', 'vec', getattr(sample, 'vec', 0))}")
def kmem__kmalloc(sample: perf.sample_event) -> None:
"""Handles kmem:kmalloc events."""
print_header("kmem__kmalloc", sample)
print_uncommon(sample)
print(f"call_site={getattr(sample, 'call_site', 0):#x}, "
f"ptr={getattr(sample, 'ptr', 0):#x}, "
f"bytes_req={getattr(sample, 'bytes_req', 0):d}, "
f"bytes_alloc={getattr(sample, 'bytes_alloc', 0):d}, "
f"gfp_flags={flag_str('kmem__kmalloc', 'gfp_flags', getattr(sample, 'gfp_flags', 0))}")
def trace_unhandled(event_name: str) -> None:
"""Tracks unhandled events."""
unhandled[event_name] += 1
def print_unhandled() -> None:
"""Prints summary of unhandled events."""
if not unhandled:
return
print("\nunhandled events:\n")
print(f"{'event':<40} {'count':>10}")
print("---------------------------------------- -----------")
for event_name, count in unhandled.items():
print(f"{event_name:<40} {count:10}")
def process_event(sample: perf.sample_event) -> None:
"""Callback for processing events."""
event_name = str(sample.evsel)
if event_name.startswith("evsel(") and event_name.endswith(")"):
event_name = event_name[6:-1]
if event_name.startswith("irq:softirq_entry"):
irq__softirq_entry(sample)
elif event_name.startswith("kmem:kmalloc"):
kmem__kmalloc(sample)
else:
trace_unhandled(event_name)
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--input", default="perf.data", help="Input file name")
args = ap.parse_args()
trace_begin()
try:
session = perf.session(perf.data(args.input), sample=process_event)
try:
session.process_events()
except KeyboardInterrupt:
pass
trace_end()
finally:
# Break the reference cycle between the global session and the
# process_event callback so the C perf.session object is freed.
session = None
|