mirror of
				https://github.com/ARM-software/devlib.git
				synced 2025-11-04 07:51: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