mirror of
				https://github.com/ARM-software/devlib.git
				synced 2025-11-03 23:41:21 +00:00 
			
		
		
		
	trace: Add a Systrace TraceCollector
This commit is contained in:
		
				
					committed by
					
						
						Marc Bonnici
					
				
			
			
				
	
			
			
			
						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)
 | 
			
		||||
		Reference in New Issue
	
	Block a user