summaryrefslogtreecommitdiff
path: root/simpleperf_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'simpleperf_utils.py')
-rw-r--r--simpleperf_utils.py95
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: