Skip to content

Commit 9b15807

Browse files
mrkajetanpmarcbonnici
authored andcommitted
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 86fcc11 commit 9b15807

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
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: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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, HostError
24+
25+
OUTPUT_PERFETTO_TRACE = 'devlib-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+
It is also possible to force using the prebuilt tracebox binary on platforms which
48+
already have traced running using the force_tracebox collector parameter.
49+
50+
https://perfetto.dev/docs/contributing/build-instructions
51+
52+
After building the 'tracebox' binary should be copied to devlib/bin/<arch>/.
53+
54+
For more information consult the official documentation:
55+
https://perfetto.dev/docs/
56+
"""
57+
58+
def __init__(self, target, config=None, force_tracebox=False):
59+
super().__init__(target)
60+
self.bg_cmd = None
61+
self.config = config
62+
self.target_binary = 'perfetto'
63+
target_output_path = self.target.working_directory
64+
65+
install_tracebox = force_tracebox or (target.os in ['linux', 'android'] and not target.is_running('traced'))
66+
67+
# Install Perfetto through tracebox
68+
if install_tracebox:
69+
self.target_binary = 'tracebox'
70+
if not self.target.get_installed(self.target_binary):
71+
host_executable = os.path.join(PACKAGE_BIN_DIRECTORY,
72+
self.target.abi, self.target_binary)
73+
if not os.path.exists(host_executable):
74+
raise HostError("{} not found on the host".format(self.target_binary))
75+
self.target.install(host_executable)
76+
# Use Android's built-in Perfetto
77+
elif target.os == 'android':
78+
os_version = target.os_version['release']
79+
if int(os_version) >= 9:
80+
# Android requires built-in Perfetto to write to this directory
81+
target_output_path = '/data/misc/perfetto-traces'
82+
# Android 9 and 10 require traced to be enabled manually
83+
if int(os_version) <= 10:
84+
target.execute('setprop persist.traced.enable 1')
85+
86+
self.target_output_file = target.path.join(target_output_path, OUTPUT_PERFETTO_TRACE)
87+
88+
def start(self):
89+
cmd = "cat {} | {} --txt -c - -o {}".format(
90+
quote(self.config), quote(self.target_binary), quote(self.target_output_file)
91+
)
92+
# start tracing
93+
if self.bg_cmd is None:
94+
self.bg_cmd = self.target.background(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
95+
else:
96+
raise TargetStableError('Perfetto collector is not re-entrant')
97+
98+
def stop(self):
99+
# stop tracing
100+
self.bg_cmd.cancel()
101+
self.bg_cmd = None
102+
103+
def set_output(self, output_path):
104+
if os.path.isdir(output_path):
105+
output_path = os.path.join(output_path, os.path.basename(self.target_output_file))
106+
self.output_path = output_path
107+
108+
def get_data(self):
109+
if self.output_path is None:
110+
raise RuntimeError("Output path was not set.")
111+
if not self.target.file_exists(self.target_output_file):
112+
raise RuntimeError("Output file not found on the device")
113+
self.target.pull(self.target_output_file, self.output_path)
114+
output = CollectorOutput()
115+
if not os.path.isfile(self.output_path):
116+
self.logger.warning('Perfetto trace not pulled from device.')
117+
else:
118+
output.append(CollectorOutputEntry(self.output_path, 'file'))
119+
return output
120+

0 commit comments

Comments
 (0)