• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright (C) 2023 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16from os import sys, path
17import synth_common
18
19# com.android.systemui
20SYSUI_PID = 1000
21# com.google.android.apps.nexuslauncher
22LAUNCHER_PID = 2000
23
24THIRD_PROCESS_PID = 3000
25
26TOP_LEVEL_SLICES_PID = 4000
27
28# List of blocking calls
29blocking_call_names = [
30    'monitor contention with something else', 'SuspendThreadByThreadId 123',
31    'LoadApkAssetsFd 123', 'binder transaction', 'inflate',
32    'Lock contention on thread list lock (owner tid: 1665)',
33    "Lock contention on thread suspend count lock (owner tid: 0)",
34    "Lock contention on a monitor lock (owner tid: 0)",
35    'android.os.Handler: kotlinx.coroutines.CancellableContinuationImpl',
36    'relayoutWindow*', 'measure', 'layout', 'configChanged',
37    'Contending for pthread mutex', 'ImageDecoder#decodeBitmap',
38    'ImageDecoder#decodeDrawable', 'NotificationStackScrollLayout#onMeasure',
39    'ExpNotRow#onMeasure(MessagingStyle)', 'ExpNotRow#onMeasure(BigTextStyle)',
40    'animation', 'input', 'traversal', 'postAndWait',
41    'android.os.Handler: kotlinx.coroutines.internal.DispatchedContinuation',
42    'GC: Wait For Completion Alloc', 'Should not be in the metric'
43]
44
45top_level_names = [
46    'android.view.ViewRootImpl$ViewRootHandler: android.view.View$$Lambda4',
47    'android.os.AsyncTask$InternalHandler: #1',
48    'android.os.Handler: com.android.systemui.broadcast.ActionReceiver$1$1',
49    'com.android.keyguard.KeyguardUpdateMonitor$13: #302',
50    'android.os.Handler: com.android.systemui.qs.external.TileServiceManager$1',
51    # The following are not expected in the output
52    'receiveMessage(inputChannel=62b8bb4 NotificationShade',
53    'android.os.Handler: #0',
54]
55
56
57def add_main_thread_atrace(trace, ts, ts_end, buf, pid):
58  trace.add_atrace_begin(ts=ts, tid=pid, pid=pid, buf=buf)
59  trace.add_atrace_end(ts=ts_end, tid=pid, pid=pid)
60
61
62def add_async_trace(trace, ts, ts_end, buf, pid):
63  trace.add_atrace_async_begin(ts=ts, tid=pid, pid=pid, buf=buf)
64  trace.add_atrace_async_end(ts=ts_end, tid=pid, pid=pid, buf=buf)
65
66
67def add_binder_transaction(trace, tx_pid, rx_pid, start_ts, end_ts):
68  trace.add_binder_transaction(
69      transaction_id=tx_pid,
70      ts_start=start_ts,
71      ts_end=end_ts,
72      tid=tx_pid,
73      pid=tx_pid,
74      reply_id=rx_pid,
75      reply_ts_start=start_ts,
76      reply_ts_end=end_ts,
77      reply_tid=rx_pid,
78      reply_pid=rx_pid)
79
80
81# Adds a set of predefined blocking calls in places near the cuj boundaries to
82# verify that only the portion inside the cuj is counted in the metric.
83def add_cuj_with_blocking_calls(trace, cuj_name, pid):
84  cuj_begin = 2_000_000
85  cuj_dur = 15_000_000
86  cuj_end = cuj_begin + cuj_dur
87  blocking_call_name = "binder transaction"
88
89  add_async_trace(trace, ts=cuj_begin, ts_end=cuj_end, buf=cuj_name, pid=pid)
90
91  # all outside, before cuj, shouldn't be counted
92  add_main_thread_atrace(
93      trace,
94      ts=cuj_begin - 2_000_000,
95      ts_end=cuj_begin - 1_000_000,
96      buf=blocking_call_name,
97      pid=pid)
98
99  # mid inside, mid outside. Should account for half the time.
100  add_main_thread_atrace(
101      trace,
102      ts=cuj_begin - 1_000_000,
103      ts_end=cuj_begin + 1_000_000,
104      buf=blocking_call_name,
105      pid=pid)
106
107  # completely inside
108  add_main_thread_atrace(
109      trace,
110      ts=cuj_begin + 2_000_000,
111      ts_end=cuj_begin + 3_000_000,
112      buf=blocking_call_name,
113      pid=pid)
114
115  add_main_thread_atrace(
116      trace,
117      ts=cuj_begin + 4_000_000,
118      ts_end=cuj_begin + 7_000_000,
119      buf=blocking_call_name,
120      pid=pid)
121
122  # mid inside, mid outside
123  add_main_thread_atrace(
124      trace,
125      ts=cuj_end - 1_000_000,
126      ts_end=cuj_end + 1_000_000,
127      buf=blocking_call_name,
128      pid=pid)
129
130  # all outside, after cuj, shouldn't be counted/
131  add_main_thread_atrace(
132      trace,
133      ts=cuj_end + 2_000_000,
134      ts_end=cuj_end + 3_000_000,
135      buf=blocking_call_name,
136      pid=pid)
137
138
139def add_cuj_with_top_level_slices(trace, cuj_name, pid):
140  blocking_call_dur = 10_000_000
141  blocking_call_ts = 2_000_000
142
143  cuj_dur = len(top_level_names) * blocking_call_dur
144  add_async_trace(
145      trace,
146      ts=blocking_call_ts,
147      ts_end=blocking_call_ts + cuj_dur,
148      buf=cuj_name,
149      pid=pid)
150
151  for top_level_slice in top_level_names:
152    add_main_thread_atrace(
153        trace,
154        ts=blocking_call_ts,
155        ts_end=blocking_call_ts + blocking_call_dur,
156        buf=top_level_slice,
157        pid=pid)
158    blocking_call_ts += blocking_call_dur
159
160  # Some top level unrelated to handler
161  add_main_thread_atrace(
162      trace,
163      ts=blocking_call_ts,
164      ts_end=blocking_call_ts + blocking_call_dur,
165      buf="some top level slice that should not be in the output",
166      pid=pid)
167  # Nested inside the previous, should not be in the output as not top level.
168  add_main_thread_atrace(
169      trace,
170      ts=blocking_call_ts + 1,
171      ts_end=blocking_call_ts + blocking_call_dur - 1,
172      buf="should.not.be.in.the.output.Handler: not.in.the.output$1",
173      pid=pid)
174
175
176# Creates a cuj that contains one of each blocking call.
177def add_all_blocking_calls_in_cuj(trace, pid):
178  blocking_call_dur = 10_000_000
179  blocking_call_ts = 2_000_000
180
181  cuj_dur = len(blocking_call_names) * blocking_call_dur
182  add_async_trace(
183      trace,
184      ts=blocking_call_ts,
185      ts_end=blocking_call_ts + cuj_dur,
186      buf="L<CUJ_WITH_MANY_BLOCKING_CALLS>",
187      pid=pid)
188
189  for blocking_call in blocking_call_names:
190    add_main_thread_atrace(
191        trace,
192        ts=blocking_call_ts,
193        ts_end=blocking_call_ts + blocking_call_dur,
194        buf=blocking_call,
195        pid=pid)
196    blocking_call_ts += blocking_call_dur
197
198
199# Creates 2 overlapping cuj, and a blocking call that lasts for both of them.
200def add_overlapping_cujs_with_blocking_calls(trace, start_ts, pid):
201  add_async_trace(
202      trace,
203      ts=start_ts,
204      ts_end=start_ts + 10_000_000,
205      buf="L<OVERLAPPING_CUJ_1>",
206      pid=pid)
207  add_async_trace(
208      trace,
209      ts=start_ts + 2_000_000,
210      ts_end=start_ts + 12_000_000,
211      buf="L<OVERLAPPING_CUJ_2>",
212      pid=pid)
213
214  add_main_thread_atrace(
215      trace,
216      ts=start_ts,
217      ts_end=start_ts + 12_000_000,
218      buf=blocking_call_names[0],
219      pid=pid)
220
221
222def add_cuj_with_named_binder_transaction(pid, rx_pid):
223  cuj_begin = 40_000_000
224  cuj_end = 50_000_000
225
226  add_async_trace(
227      trace,
228      ts=cuj_begin,
229      ts_end=cuj_end,
230      buf="L<WITH_NAMED_BINDER_TRANSACTION>",
231      pid=pid)
232
233  add_binder_transaction(
234      trace, tx_pid=pid, rx_pid=rx_pid, start_ts=cuj_begin, end_ts=cuj_end)
235
236  # Slice inside the binder reply, to give a name to the binder call.
237  # The named binder slice introduced should be the length of the entire
238  # transaction even if the "name" slice only covers some of the binder reply.
239  add_main_thread_atrace(
240      trace,
241      ts=cuj_begin + 1_000_000,
242      ts_end=cuj_end - 1_000_000,
243      buf="AIDL::java::IWindowManager::hasNavigationBar::server",
244      pid=rx_pid)
245
246
247def add_process(trace, package_name, uid, pid):
248  trace.add_package_list(ts=0, name=package_name, uid=uid, version_code=1)
249  trace.add_process(pid=pid, ppid=0, cmdline=package_name, uid=uid)
250  trace.add_thread(tid=pid, tgid=pid, cmdline="MainThread", name="MainThread")
251
252
253def setup_trace():
254  trace = synth_common.create_trace()
255  trace.add_packet()
256  add_process(
257      trace, package_name="com.android.systemui", uid=10001, pid=SYSUI_PID)
258  add_process(
259      trace,
260      package_name="com.google.android.apps.nexuslauncher",
261      uid=10002,
262      pid=LAUNCHER_PID)
263  add_process(
264      trace,
265      package_name="com.google.android.third.process",
266      uid=10003,
267      pid=THIRD_PROCESS_PID)
268  add_process(
269      trace,
270      package_name="com.google.android.top.level.slices",
271      uid=10004,
272      pid=TOP_LEVEL_SLICES_PID)
273  trace.add_ftrace_packet(cpu=0)
274  add_async_trace(trace, ts=0, ts_end=5, buf="J<IGNORED>", pid=SYSUI_PID)
275  return trace
276
277
278trace = setup_trace()
279
280add_cuj_with_blocking_calls(trace, "L<TEST_SYSUI_LATENCY_EVENT>", pid=SYSUI_PID)
281add_cuj_with_blocking_calls(
282    trace, "L<TEST_LAUNCHER_LATENCY_EVENT>", pid=LAUNCHER_PID)
283add_cuj_with_top_level_slices(
284    trace, "L<CUJ_WITH_TOP_LEVEL_SLICES>", pid=TOP_LEVEL_SLICES_PID)
285
286add_all_blocking_calls_in_cuj(trace, pid=THIRD_PROCESS_PID)
287
288add_overlapping_cujs_with_blocking_calls(
289    trace, pid=SYSUI_PID, start_ts=20_000_000)
290
291add_cuj_with_named_binder_transaction(pid=SYSUI_PID, rx_pid=LAUNCHER_PID)
292
293# Note that J<*> events are not tested here.
294# See test_android_blocking_calls_on_jank_cujs.
295sys.stdout.buffer.write(trace.trace.SerializeToString())
296