mirror of
https://github.com/ARM-software/devlib.git
synced 2025-01-31 02:00:45 +00:00
trace: Add a Systrace TraceCollector
This commit is contained in:
parent
96ffa64ad8
commit
d81b72a91b
158
devlib/trace/systrace.py
Normal file
158
devlib/trace/systrace.py
Normal file
@ -0,0 +1,158 @@
|
||||
# Copyright 2018 Arm Limited
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from shutil import copyfile
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from devlib.exception import TargetError, HostError
|
||||
from devlib.trace import TraceCollector
|
||||
from devlib.utils.android import platform_tools
|
||||
from devlib.utils.misc import memoized
|
||||
|
||||
|
||||
DEFAULT_CATEGORIES = [
|
||||
'gfx',
|
||||
'view',
|
||||
'sched',
|
||||
'freq',
|
||||
'idle'
|
||||
]
|
||||
|
||||
class SystraceCollector(TraceCollector):
|
||||
"""
|
||||
A trace collector based on Systrace
|
||||
|
||||
For more details, see https://developer.android.com/studio/command-line/systrace
|
||||
|
||||
:param target: Devlib target
|
||||
:type target: AndroidTarget
|
||||
|
||||
:param outdir: Working directory to use on the host
|
||||
:type outdir: str
|
||||
|
||||
:param categories: Systrace categories to trace. See `available_categories`
|
||||
:type categories: list(str)
|
||||
|
||||
:param buffer_size: Buffer size in kb
|
||||
:type buffer_size: int
|
||||
|
||||
:param strict: Raise an exception if any of the requested categories
|
||||
are not available
|
||||
:type strict: bool
|
||||
"""
|
||||
|
||||
@property
|
||||
@memoized
|
||||
def available_categories(self):
|
||||
lines = subprocess.check_output([self.systrace_binary, '-l']).splitlines()
|
||||
|
||||
categories = []
|
||||
for line in lines:
|
||||
categories.append(line.split()[0])
|
||||
|
||||
return categories
|
||||
|
||||
def __init__(self, target,
|
||||
categories=None,
|
||||
buffer_size=None,
|
||||
strict=False):
|
||||
|
||||
super(SystraceCollector, self).__init__(target)
|
||||
|
||||
self.categories = categories or DEFAULT_CATEGORIES
|
||||
self.buffer_size = buffer_size
|
||||
|
||||
self._systrace_process = None
|
||||
self._tmpfile = None
|
||||
|
||||
# Try to find a systrace binary
|
||||
self.systrace_binary = None
|
||||
|
||||
systrace_binary_path = os.path.join(platform_tools, 'systrace', 'systrace.py')
|
||||
if not os.path.isfile(systrace_binary_path):
|
||||
raise HostError('Could not find any systrace binary under {}'.format(platform_tools))
|
||||
|
||||
self.systrace_binary = systrace_binary_path
|
||||
|
||||
# Filter the requested categories
|
||||
for category in self.categories:
|
||||
if category not in self.available_categories:
|
||||
message = 'Category [{}] not available for tracing'.format(category)
|
||||
if strict:
|
||||
raise TargetError(message)
|
||||
self.logger.warning(message)
|
||||
|
||||
self.categories = list(set(self.categories) & set(self.available_categories))
|
||||
if not self.categories:
|
||||
raise TargetError('None of the requested categories are available')
|
||||
|
||||
def __del__(self):
|
||||
self.reset()
|
||||
|
||||
def _build_cmd(self):
|
||||
self._tmpfile = NamedTemporaryFile()
|
||||
|
||||
self.systrace_cmd = '{} -o {} -e {}'.format(
|
||||
self.systrace_binary,
|
||||
self._tmpfile.name,
|
||||
self.target.adb_name
|
||||
)
|
||||
|
||||
if self.buffer_size:
|
||||
self.systrace_cmd += ' -b {}'.format(self.buffer_size)
|
||||
|
||||
self.systrace_cmd += ' {}'.format(' '.join(self.categories))
|
||||
|
||||
def reset(self):
|
||||
if self._systrace_process:
|
||||
self.stop()
|
||||
|
||||
if self._tmpfile:
|
||||
self._tmpfile.close()
|
||||
self._tmpfile = None
|
||||
|
||||
def start(self):
|
||||
if self._systrace_process:
|
||||
raise RuntimeError("Tracing is already underway, call stop() first")
|
||||
|
||||
self.reset()
|
||||
|
||||
self._build_cmd()
|
||||
|
||||
self._systrace_process = subprocess.Popen(
|
||||
self.systrace_cmd,
|
||||
stdin=subprocess.PIPE,
|
||||
shell=True
|
||||
)
|
||||
|
||||
def stop(self):
|
||||
if not self._systrace_process:
|
||||
raise RuntimeError("No tracing to stop, call start() first")
|
||||
|
||||
# Systrace expects <enter> to stop
|
||||
self._systrace_process.communicate('\n')
|
||||
self._systrace_process = None
|
||||
|
||||
def get_trace(self, outfile):
|
||||
if self._systrace_process:
|
||||
raise RuntimeError("Tracing is underway, call stop() first")
|
||||
|
||||
if not self._tmpfile:
|
||||
raise RuntimeError("No tracing data available")
|
||||
|
||||
copyfile(self._tmpfile.name, outfile)
|
Loading…
x
Reference in New Issue
Block a user