blob: 7573b76f681a60a0e5a3ccac3fc61d1a0203409a [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (C) 2023 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from os import sys, path
import synth_common
# com.android.systemui
SYSUI_PID = 1000
# com.google.android.apps.nexuslauncher
LAUNCHER_PID = 2000
THIRD_PROCESS_PID = 3000
# List of blocking calls
blocking_call_names = [
'monitor contention with something else', 'SuspendThreadByThreadId 123',
'LoadApkAssetsFd 123', 'binder transaction',
'inflate', 'Lock contention on thread list lock (owner tid: 1665)',
'CancellableContinuationImpl#123', 'relayoutWindow*', 'measure', 'layout',
'configChanged', 'Contending for pthread mutex',
'ImageDecoder#decodeBitmap', 'ImageDecoder#decodeDrawable',
'Should not be in the metric'
]
def add_main_thread_atrace(trace, ts, ts_end, buf, pid):
trace.add_atrace_begin(ts=ts, tid=pid, pid=pid, buf=buf)
trace.add_atrace_end(ts=ts_end, tid=pid, pid=pid)
def add_async_trace(trace, ts, ts_end, buf, pid):
trace.add_atrace_async_begin(ts=ts, tid=pid, pid=pid, buf=buf)
trace.add_atrace_async_end(ts=ts_end, tid=pid, pid=pid, buf=buf)
# Adds a set of predefined blocking calls in places near the cuj boundaries to
# verify that only the portion inside the cuj is counted in the metric.
def add_cuj_with_blocking_calls(trace, cuj_name, pid):
cuj_begin = 2_000_000
cuj_dur = 15_000_000
cuj_end = cuj_begin + cuj_dur
blocking_call_name = "binder transaction"
add_async_trace(trace, ts=cuj_begin, ts_end=cuj_end, buf=cuj_name, pid=pid)
# all outside, before cuj, shouldn't be counted
add_main_thread_atrace(
trace,
ts=cuj_begin - 2_000_000,
ts_end=cuj_begin - 1_000_000,
buf=blocking_call_name,
pid=pid)
# mid inside, mid outside. Should account for half the time.
add_main_thread_atrace(
trace,
ts=cuj_begin - 1_000_000,
ts_end=cuj_begin + 1_000_000,
buf=blocking_call_name,
pid=pid)
# completely inside
add_main_thread_atrace(
trace,
ts=cuj_begin + 2_000_000,
ts_end=cuj_begin + 3_000_000,
buf=blocking_call_name,
pid=pid)
add_main_thread_atrace(
trace,
ts=cuj_begin + 4_000_000,
ts_end=cuj_begin + 7_000_000,
buf=blocking_call_name,
pid=pid)
# mid inside, mid outside
add_main_thread_atrace(
trace,
ts=cuj_end - 1_000_000,
ts_end=cuj_end + 1_000_000,
buf=blocking_call_name,
pid=pid)
# all outside, after cuj, shouldn't be counted/
add_main_thread_atrace(
trace,
ts=cuj_end + 2_000_000,
ts_end=cuj_end + 3_000_000,
buf=blocking_call_name,
pid=pid)
# Creates a cuj that contains one of each blocking call.
def add_all_blocking_calls_in_cuj(trace, pid):
blocking_call_dur = 10_000_000
blocking_call_ts = 2_000_000
cuj_dur = len(blocking_call_names) * blocking_call_dur
add_async_trace(
trace,
ts=blocking_call_ts,
ts_end=blocking_call_ts + cuj_dur,
buf="L<CUJ_WITH_MANY_BLOCKING_CALLS>",
pid=pid)
for blocking_call in blocking_call_names:
add_main_thread_atrace(
trace,
ts=blocking_call_ts,
ts_end=blocking_call_ts + blocking_call_dur,
buf=blocking_call,
pid=pid)
blocking_call_ts += blocking_call_dur
# Creates 2 overlapping cuj, and a blocking call that lasts for both of them.
def add_overlapping_cujs_with_blocking_calls(trace, start_ts, pid):
add_async_trace(
trace,
ts=start_ts,
ts_end=start_ts + 10_000_000,
buf="L<OVERLAPPING_CUJ_1>",
pid=pid)
add_async_trace(
trace,
ts=start_ts + 2_000_000,
ts_end=start_ts + 12_000_000,
buf="L<OVERLAPPING_CUJ_2>",
pid=pid)
add_main_thread_atrace(
trace,
ts=start_ts,
ts_end=start_ts + 12_000_000,
buf=blocking_call_names[0],
pid=pid)
def add_process(trace, package_name, uid, pid):
trace.add_package_list(ts=0, name=package_name, uid=uid, version_code=1)
trace.add_process(
pid=pid, ppid=0, cmdline=package_name, uid=uid)
trace.add_thread(tid=pid, tgid=pid, cmdline="MainThread", name="MainThread")
def setup_trace():
trace = synth_common.create_trace()
trace.add_packet()
add_process(trace, package_name="com.android.systemui", uid=10001,
pid=SYSUI_PID)
add_process(trace, package_name="com.google.android.apps.nexuslauncher",
uid=10002, pid=LAUNCHER_PID)
add_process(trace, package_name="com.google.android.third.process",
uid=10003, pid=THIRD_PROCESS_PID)
trace.add_ftrace_packet(cpu=0)
add_async_trace(trace, ts=0, ts_end=5, buf="J<IGNORED>", pid=SYSUI_PID)
return trace
trace = setup_trace()
add_cuj_with_blocking_calls(trace, "L<TEST_SYSUI_LATENCY_EVENT>", pid=SYSUI_PID)
add_cuj_with_blocking_calls(trace, "L<TEST_LAUNCHER_LATENCY_EVENT>",
pid=LAUNCHER_PID)
add_all_blocking_calls_in_cuj(trace, pid=THIRD_PROCESS_PID)
add_overlapping_cujs_with_blocking_calls(trace, pid=SYSUI_PID,
start_ts=20_000_000)
# Note that J<*> events are not tested here.
# See test_android_blocking_calls_on_jank_cujs.
sys.stdout.buffer.write(trace.trace.SerializeToString())