Skip to content

Commit ff22f03

Browse files
committed
collector: Add PerfettoCollector
Add a Collector for accessing Google's Perfetto tracing infrastructure. The Collector takes a path to an on-device config file, starts tracing in the background using the perfetto binary and then stops by killing the tracing process. Signed-off-by: Kajetan Puchalski <[email protected]>
1 parent b018d02 commit ff22f03

File tree

3 files changed

+112
-4
lines changed

3 files changed

+112
-4
lines changed

devlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from devlib.derived.fps import DerivedGfxInfoStats, DerivedSurfaceFlingerStats
4747

4848
from devlib.collector.ftrace import FtraceCollector
49+
from devlib.collector.perfetto import PerfettoCollector
4950
from devlib.collector.perf import PerfCollector
5051
from devlib.collector.serial_trace import SerialTraceCollector
5152
from devlib.collector.dmesg import DmesgCollector

devlib/collector/perfetto.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Copyright 2023 ARM Limited
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
16+
import os
17+
import subprocess
18+
from shlex import quote
19+
20+
from devlib.host import PACKAGE_BIN_DIRECTORY
21+
from devlib.collector import (CollectorBase, CollectorOutput,
22+
CollectorOutputEntry)
23+
from devlib.exception import TargetStableError
24+
25+
OUTPUT_PERFETTO_TRACE = 'trace.perfetto-trace'
26+
27+
28+
class PerfettoCollector(CollectorBase):
29+
"""
30+
Perfetto is a production-grade open-source stack for performance instrumentation
31+
and trace analysis developed by Google. It offers services and libraries for
32+
recording system-level and app-level traces, native + java heap profiling,
33+
a library for analyzing traces using SQL and a web-based UI to visualize and
34+
explore multi-GB traces.
35+
36+
This collector takes a path to a perfetto config file saved on disk and passes
37+
it directly to the tool.
38+
39+
On Android platfroms Perfetto is included in the framework starting with Android 9.
40+
On Android 8 and below, follow the Linux instructions below to build and include
41+
the standalone tracebox binary.
42+
43+
On Linux platforms, either traced (Perfetto tracing daemon) needs to be running
44+
in the background or the tracebox binary needs to be built from source and placed
45+
in the Package Bin directory. The build instructions can be found here:
46+
47+
https://perfetto.dev/docs/contributing/build-instructions
48+
49+
After building the 'tracebox' binary should be copied to devlib/bin/<arch>/.
50+
51+
For more information consult the official documentation:
52+
https://perfetto.dev/docs/
53+
"""
54+
55+
def __init__(self, target, config=None):
56+
super().__init__(target)
57+
self.target_output_file = target.path.join(self.target.working_directory, OUTPUT_PERFETTO_TRACE)
58+
self.bg_cmd = None
59+
self.config = config
60+
61+
os_version = target.os_version['release']
62+
self.target_binary = 'perfetto'
63+
if target.os == 'android' and os_version > 8:
64+
# Android 9 and 10 require traced to be enabled manually
65+
if os_version == '9' or os_version == '10':
66+
target.execute('setprop persist.traced.enable 1')
67+
elif target.os in ['linux', 'android'] and not target.is_running('traced'):
68+
self.target_binary = 'tracebox'
69+
if not self.target.get_installed(self.target_binary):
70+
host_executable = os.path.join(PACKAGE_BIN_DIRECTORY,
71+
self.target.abi, self.target_binary)
72+
self.target.install(host_executable)
73+
74+
def start(self):
75+
cmd = "cat {} | {} --txt -c - -o {}".format(
76+
quote(self.config), quote(self.target_binary), quote(self.target_output_file)
77+
)
78+
# start tracing
79+
if self.bg_cmd is None:
80+
self.bg_cmd = self.target.background(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
81+
else:
82+
raise TargetStableError('Perfetto collector is not re-entrant')
83+
84+
def stop(self):
85+
# stop tracing
86+
self.bg_cmd.cancel()
87+
self.bg_cmd = None
88+
89+
def set_output(self, output_path):
90+
if os.path.isdir(output_path):
91+
output_path = os.path.join(output_path, os.path.basename(self.target_output_file))
92+
self.output_path = output_path
93+
94+
def get_data(self):
95+
if self.output_path is None:
96+
raise RuntimeError("Output path was not set.")
97+
if not self.target.file_exists(self.target_output_file):
98+
raise RuntimeError("Output file not found on the device")
99+
self.target.pull(self.target_output_file, self.output_path)
100+
output = CollectorOutput()
101+
if not os.path.isfile(self.output_path):
102+
self.logger.warning('Perfetto trace not pulled from device.')
103+
else:
104+
output.append(CollectorOutputEntry(self.output_path, 'file'))
105+
return output
106+

devlib/target.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,11 @@ def shutils(self):
280280
self._setup_shutils()
281281
return self._shutils
282282

283-
def is_running(self, process):
284-
cmd = f'''{self.busybox} ps -A -o comm,stat | {self.busybox} awk '$1 == "{process}" && $2 != "Z"' '''
285-
result = self.execute(cmd, as_root=False)
286-
return bool(result)
283+
def is_running(self, comm):
284+
cmd_ps = f'''{self.busybox} ps -A -o stat,comm'''
285+
cmd_awk = f'''{self.busybox} awk 'BEGIN{{found=0}} {{state=$1; $1=""; if ($state != "Z" && $0 == " {comm}") {{found=1}}}} END {{print found}}' '''
286+
result = self.execute(f"{cmd_ps} | {cmd_awk}", as_root=False)
287+
return bool(int(result))
287288

288289
@tls_property
289290
def _conn(self):

0 commit comments

Comments
 (0)