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