diff options
Diffstat (limited to 'simpleperf_utils.py')
-rw-r--r-- | simpleperf_utils.py | 95 |
1 files changed, 58 insertions, 37 deletions
diff --git a/simpleperf_utils.py b/simpleperf_utils.py index d327f18..af4fca1 100644 --- a/simpleperf_utils.py +++ b/simpleperf_utils.py @@ -31,7 +31,7 @@ import shutil import subprocess import sys import time -from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Union +from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Union, TextIO NDK_ERROR_MESSAGE = "Please install the Android NDK (https://developer.android.com/studio/projects/install-ndk), then set NDK path with --ndk_path option." @@ -360,6 +360,8 @@ class AdbHelper(object): return 'x86_64' if '86' in output: return 'x86' + if 'riscv64' in output: + return 'riscv64' log_fatal('unsupported architecture: %s' % output.strip()) return '' @@ -867,6 +869,8 @@ class Objdump(object): """ Disassemble code for multiple addr ranges in a binary. sorted_addr_ranges should be sorted by addr_range.start. """ + if not sorted_addr_ranges: + return [] real_path, arch = dso_info objdump_path = self.objdump_paths.get(arch) if not objdump_path: @@ -876,51 +880,62 @@ class Objdump(object): self.objdump_paths[arch] = objdump_path # Run objdump. - args = [objdump_path, '-dlC', '--no-show-raw-insn', real_path] + start_addr = sorted_addr_ranges[0].start + stop_addr = max(addr_range.end for addr_range in sorted_addr_ranges) + args = [objdump_path, '-dlC', '--no-show-raw-insn', + '--start-address=0x%x' % start_addr, + '--stop-address=0x%x' % stop_addr, + real_path] if arch == 'arm' and 'llvm-objdump' in objdump_path: args += ['--print-imm-hex'] - current_id = 0 - in_range = False - result = [Disassembly() for _ in sorted_addr_ranges] try: proc = subprocess.Popen(args, stdout=subprocess.PIPE, text=True) - while True: - line = proc.stdout.readline() - if not line: - break - if current_id >= len(sorted_addr_ranges): - continue - if line[0] == ' ': - # may be an instruction, like: " 24a469c: stp x29, x30, [sp, #-0x60]!" - items = line.split(':', 1) - try: - addr = int(items[0], 16) - except (ValueError, IndexError): - addr = 0 - else: - # may be a function start point, like "00000000024a4698 <DoWork()>:" - items = line.split(maxsplit=1) - try: - addr = int(items[0], 16) - except (ValueError, IndexError): - addr = 0 - - if addr != 0: - if in_range and not sorted_addr_ranges[current_id].is_in_range(addr): - in_range = False - current_id += 1 - if current_id == len(sorted_addr_ranges): - continue - if not in_range and sorted_addr_ranges[current_id].is_in_range(addr): - in_range = True - - if in_range: - result[current_id].lines.append((line, addr)) + result = self._parse_disassembly_for_functions(proc.stdout, sorted_addr_ranges) proc.wait() except OSError: return None return result + def _parse_disassembly_for_functions(self, fh: TextIO, sorted_addr_ranges: List[AddrRange]) -> Optional[List[Disassembly]]: + current_id = 0 + in_range = False + result = [Disassembly() for _ in sorted_addr_ranges] + while True: + line = fh.readline() + if not line: + break + line = line.rstrip() # Remove '\r\n'. + addr = self._get_addr_from_disassembly_line(line) + if current_id >= len(sorted_addr_ranges): + continue + if addr: + if in_range and not sorted_addr_ranges[current_id].is_in_range(addr): + in_range = False + if not in_range: + # Skip addr ranges before the current address. + while current_id < len(sorted_addr_ranges) and sorted_addr_ranges[current_id].end <= addr: + current_id += 1 + if current_id < len(sorted_addr_ranges) and sorted_addr_ranges[current_id].is_in_range(addr): + in_range = True + if in_range: + result[current_id].lines.append((line, addr)) + return result + + def _get_addr_from_disassembly_line(self, line: str) -> int: + # line may be an instruction, like: " 24a469c: stp x29, x30, [sp, #-0x60]!" or + # "ffffffc0085d9664: paciasp". + # line may be a function start point, like "00000000024a4698 <DoWork()>:". + items = line.strip().split() + if not items: + return 0 + s = items[0] + if s.endswith(':'): + s = s[:-1] + try: + return int(s, 16) + except ValueError: + return 0 + class ReadElf(object): """ A wrapper of readelf. """ @@ -951,6 +966,8 @@ class ReadElf(object): return 'x86_64' if output.find('80386') != -1: return 'x86' + if output.find('RISC-V') != -1: + return 'riscv64' except subprocess.CalledProcessError: pass return 'unknown' @@ -1123,6 +1140,8 @@ class BaseArgumentParser(argparse.ArgumentParser): self, group: Optional[Any] = None, with_pid_shortcut: bool = True): if not group: group = self.add_argument_group('Sample filter options') + group.add_argument('--cpu', nargs='+', help="""only include samples for the selected cpus. + cpu can be a number like 1, or a range like 0-3""") group.add_argument('--exclude-pid', metavar='pid', nargs='+', type=int, help='exclude samples for selected processes') group.add_argument('--exclude-tid', metavar='tid', nargs='+', type=int, @@ -1160,6 +1179,8 @@ class BaseArgumentParser(argparse.ArgumentParser): def _build_sample_filter(self, args: argparse.Namespace) -> List[str]: """ Build sample filters, which can be passed to ReportLib.SetSampleFilter(). """ filters = [] + if args.cpu: + filters.extend(['--cpu', ','.join(args.cpu)]) if args.exclude_pid: filters.extend(['--exclude-pid', ','.join(str(pid) for pid in args.exclude_pid)]) if args.exclude_tid: |