162306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci# arm-cs-trace-disasm.py: ARM CoreSight Trace Dump With Disassember 362306a36Sopenharmony_ci# 462306a36Sopenharmony_ci# Author: Tor Jeremiassen <tor@ti.com> 562306a36Sopenharmony_ci# Mathieu Poirier <mathieu.poirier@linaro.org> 662306a36Sopenharmony_ci# Leo Yan <leo.yan@linaro.org> 762306a36Sopenharmony_ci# Al Grant <Al.Grant@arm.com> 862306a36Sopenharmony_ci 962306a36Sopenharmony_cifrom __future__ import print_function 1062306a36Sopenharmony_ciimport os 1162306a36Sopenharmony_cifrom os import path 1262306a36Sopenharmony_ciimport re 1362306a36Sopenharmony_cifrom subprocess import * 1462306a36Sopenharmony_cifrom optparse import OptionParser, make_option 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cifrom perf_trace_context import perf_set_itrace_options, \ 1762306a36Sopenharmony_ci perf_sample_insn, perf_sample_srccode 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci# Below are some example commands for using this script. 2062306a36Sopenharmony_ci# 2162306a36Sopenharmony_ci# Output disassembly with objdump: 2262306a36Sopenharmony_ci# perf script -s scripts/python/arm-cs-trace-disasm.py \ 2362306a36Sopenharmony_ci# -- -d objdump -k path/to/vmlinux 2462306a36Sopenharmony_ci# Output disassembly with llvm-objdump: 2562306a36Sopenharmony_ci# perf script -s scripts/python/arm-cs-trace-disasm.py \ 2662306a36Sopenharmony_ci# -- -d llvm-objdump-11 -k path/to/vmlinux 2762306a36Sopenharmony_ci# Output only source line and symbols: 2862306a36Sopenharmony_ci# perf script -s scripts/python/arm-cs-trace-disasm.py 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci# Command line parsing. 3162306a36Sopenharmony_cioption_list = [ 3262306a36Sopenharmony_ci # formatting options for the bottom entry of the stack 3362306a36Sopenharmony_ci make_option("-k", "--vmlinux", dest="vmlinux_name", 3462306a36Sopenharmony_ci help="Set path to vmlinux file"), 3562306a36Sopenharmony_ci make_option("-d", "--objdump", dest="objdump_name", 3662306a36Sopenharmony_ci help="Set path to objdump executable file"), 3762306a36Sopenharmony_ci make_option("-v", "--verbose", dest="verbose", 3862306a36Sopenharmony_ci action="store_true", default=False, 3962306a36Sopenharmony_ci help="Enable debugging log") 4062306a36Sopenharmony_ci] 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciparser = OptionParser(option_list=option_list) 4362306a36Sopenharmony_ci(options, args) = parser.parse_args() 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci# Initialize global dicts and regular expression 4662306a36Sopenharmony_cidisasm_cache = dict() 4762306a36Sopenharmony_cicpu_data = dict() 4862306a36Sopenharmony_cidisasm_re = re.compile("^\s*([0-9a-fA-F]+):") 4962306a36Sopenharmony_cidisasm_func_re = re.compile("^\s*([0-9a-fA-F]+)\s.*:") 5062306a36Sopenharmony_cicache_size = 64*1024 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ciglb_source_file_name = None 5362306a36Sopenharmony_ciglb_line_number = None 5462306a36Sopenharmony_ciglb_dso = None 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cidef get_optional(perf_dict, field): 5762306a36Sopenharmony_ci if field in perf_dict: 5862306a36Sopenharmony_ci return perf_dict[field] 5962306a36Sopenharmony_ci return "[unknown]" 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cidef get_offset(perf_dict, field): 6262306a36Sopenharmony_ci if field in perf_dict: 6362306a36Sopenharmony_ci return "+%#x" % perf_dict[field] 6462306a36Sopenharmony_ci return "" 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cidef get_dso_file_path(dso_name, dso_build_id): 6762306a36Sopenharmony_ci if (dso_name == "[kernel.kallsyms]" or dso_name == "vmlinux"): 6862306a36Sopenharmony_ci if (options.vmlinux_name): 6962306a36Sopenharmony_ci return options.vmlinux_name; 7062306a36Sopenharmony_ci else: 7162306a36Sopenharmony_ci return dso_name 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (dso_name == "[vdso]") : 7462306a36Sopenharmony_ci append = "/vdso" 7562306a36Sopenharmony_ci else: 7662306a36Sopenharmony_ci append = "/elf" 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci dso_path = os.environ['PERF_BUILDID_DIR'] + "/" + dso_name + "/" + dso_build_id + append; 7962306a36Sopenharmony_ci # Replace duplicate slash chars to single slash char 8062306a36Sopenharmony_ci dso_path = dso_path.replace('//', '/', 1) 8162306a36Sopenharmony_ci return dso_path 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cidef read_disam(dso_fname, dso_start, start_addr, stop_addr): 8462306a36Sopenharmony_ci addr_range = str(start_addr) + ":" + str(stop_addr) + ":" + dso_fname 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci # Don't let the cache get too big, clear it when it hits max size 8762306a36Sopenharmony_ci if (len(disasm_cache) > cache_size): 8862306a36Sopenharmony_ci disasm_cache.clear(); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if addr_range in disasm_cache: 9162306a36Sopenharmony_ci disasm_output = disasm_cache[addr_range]; 9262306a36Sopenharmony_ci else: 9362306a36Sopenharmony_ci start_addr = start_addr - dso_start; 9462306a36Sopenharmony_ci stop_addr = stop_addr - dso_start; 9562306a36Sopenharmony_ci disasm = [ options.objdump_name, "-d", "-z", 9662306a36Sopenharmony_ci "--start-address="+format(start_addr,"#x"), 9762306a36Sopenharmony_ci "--stop-address="+format(stop_addr,"#x") ] 9862306a36Sopenharmony_ci disasm += [ dso_fname ] 9962306a36Sopenharmony_ci disasm_output = check_output(disasm).decode('utf-8').split('\n') 10062306a36Sopenharmony_ci disasm_cache[addr_range] = disasm_output 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return disasm_output 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cidef print_disam(dso_fname, dso_start, start_addr, stop_addr): 10562306a36Sopenharmony_ci for line in read_disam(dso_fname, dso_start, start_addr, stop_addr): 10662306a36Sopenharmony_ci m = disasm_func_re.search(line) 10762306a36Sopenharmony_ci if m is None: 10862306a36Sopenharmony_ci m = disasm_re.search(line) 10962306a36Sopenharmony_ci if m is None: 11062306a36Sopenharmony_ci continue 11162306a36Sopenharmony_ci print("\t" + line) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cidef print_sample(sample): 11462306a36Sopenharmony_ci print("Sample = { cpu: %04d addr: 0x%016x phys_addr: 0x%016x ip: 0x%016x " \ 11562306a36Sopenharmony_ci "pid: %d tid: %d period: %d time: %d }" % \ 11662306a36Sopenharmony_ci (sample['cpu'], sample['addr'], sample['phys_addr'], \ 11762306a36Sopenharmony_ci sample['ip'], sample['pid'], sample['tid'], \ 11862306a36Sopenharmony_ci sample['period'], sample['time'])) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cidef trace_begin(): 12162306a36Sopenharmony_ci print('ARM CoreSight Trace Data Assembler Dump') 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cidef trace_end(): 12462306a36Sopenharmony_ci print('End') 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cidef trace_unhandled(event_name, context, event_fields_dict): 12762306a36Sopenharmony_ci print(' '.join(['%s=%s'%(k,str(v))for k,v in sorted(event_fields_dict.items())])) 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cidef common_start_str(comm, sample): 13062306a36Sopenharmony_ci sec = int(sample["time"] / 1000000000) 13162306a36Sopenharmony_ci ns = sample["time"] % 1000000000 13262306a36Sopenharmony_ci cpu = sample["cpu"] 13362306a36Sopenharmony_ci pid = sample["pid"] 13462306a36Sopenharmony_ci tid = sample["tid"] 13562306a36Sopenharmony_ci return "%16s %5u/%-5u [%04u] %9u.%09u " % (comm, pid, tid, cpu, sec, ns) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci# This code is copied from intel-pt-events.py for printing source code 13862306a36Sopenharmony_ci# line and symbols. 13962306a36Sopenharmony_cidef print_srccode(comm, param_dict, sample, symbol, dso): 14062306a36Sopenharmony_ci ip = sample["ip"] 14162306a36Sopenharmony_ci if symbol == "[unknown]": 14262306a36Sopenharmony_ci start_str = common_start_str(comm, sample) + ("%x" % ip).rjust(16).ljust(40) 14362306a36Sopenharmony_ci else: 14462306a36Sopenharmony_ci offs = get_offset(param_dict, "symoff") 14562306a36Sopenharmony_ci start_str = common_start_str(comm, sample) + (symbol + offs).ljust(40) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci global glb_source_file_name 14862306a36Sopenharmony_ci global glb_line_number 14962306a36Sopenharmony_ci global glb_dso 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci source_file_name, line_number, source_line = perf_sample_srccode(perf_script_context) 15262306a36Sopenharmony_ci if source_file_name: 15362306a36Sopenharmony_ci if glb_line_number == line_number and glb_source_file_name == source_file_name: 15462306a36Sopenharmony_ci src_str = "" 15562306a36Sopenharmony_ci else: 15662306a36Sopenharmony_ci if len(source_file_name) > 40: 15762306a36Sopenharmony_ci src_file = ("..." + source_file_name[-37:]) + " " 15862306a36Sopenharmony_ci else: 15962306a36Sopenharmony_ci src_file = source_file_name.ljust(41) 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if source_line is None: 16262306a36Sopenharmony_ci src_str = src_file + str(line_number).rjust(4) + " <source not found>" 16362306a36Sopenharmony_ci else: 16462306a36Sopenharmony_ci src_str = src_file + str(line_number).rjust(4) + " " + source_line 16562306a36Sopenharmony_ci glb_dso = None 16662306a36Sopenharmony_ci elif dso == glb_dso: 16762306a36Sopenharmony_ci src_str = "" 16862306a36Sopenharmony_ci else: 16962306a36Sopenharmony_ci src_str = dso 17062306a36Sopenharmony_ci glb_dso = dso 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci glb_line_number = line_number 17362306a36Sopenharmony_ci glb_source_file_name = source_file_name 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci print(start_str, src_str) 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cidef process_event(param_dict): 17862306a36Sopenharmony_ci global cache_size 17962306a36Sopenharmony_ci global options 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci sample = param_dict["sample"] 18262306a36Sopenharmony_ci comm = param_dict["comm"] 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci name = param_dict["ev_name"] 18562306a36Sopenharmony_ci dso = get_optional(param_dict, "dso") 18662306a36Sopenharmony_ci dso_bid = get_optional(param_dict, "dso_bid") 18762306a36Sopenharmony_ci dso_start = get_optional(param_dict, "dso_map_start") 18862306a36Sopenharmony_ci dso_end = get_optional(param_dict, "dso_map_end") 18962306a36Sopenharmony_ci symbol = get_optional(param_dict, "symbol") 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (options.verbose == True): 19262306a36Sopenharmony_ci print("Event type: %s" % name) 19362306a36Sopenharmony_ci print_sample(sample) 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci # If cannot find dso so cannot dump assembler, bail out 19662306a36Sopenharmony_ci if (dso == '[unknown]'): 19762306a36Sopenharmony_ci return 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci # Validate dso start and end addresses 20062306a36Sopenharmony_ci if ((dso_start == '[unknown]') or (dso_end == '[unknown]')): 20162306a36Sopenharmony_ci print("Failed to find valid dso map for dso %s" % dso) 20262306a36Sopenharmony_ci return 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (name[0:12] == "instructions"): 20562306a36Sopenharmony_ci print_srccode(comm, param_dict, sample, symbol, dso) 20662306a36Sopenharmony_ci return 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci # Don't proceed if this event is not a branch sample, . 20962306a36Sopenharmony_ci if (name[0:8] != "branches"): 21062306a36Sopenharmony_ci return 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci cpu = sample["cpu"] 21362306a36Sopenharmony_ci ip = sample["ip"] 21462306a36Sopenharmony_ci addr = sample["addr"] 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci # Initialize CPU data if it's empty, and directly return back 21762306a36Sopenharmony_ci # if this is the first tracing event for this CPU. 21862306a36Sopenharmony_ci if (cpu_data.get(str(cpu) + 'addr') == None): 21962306a36Sopenharmony_ci cpu_data[str(cpu) + 'addr'] = addr 22062306a36Sopenharmony_ci return 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci # The format for packet is: 22362306a36Sopenharmony_ci # 22462306a36Sopenharmony_ci # +------------+------------+------------+ 22562306a36Sopenharmony_ci # sample_prev: | addr | ip | cpu | 22662306a36Sopenharmony_ci # +------------+------------+------------+ 22762306a36Sopenharmony_ci # sample_next: | addr | ip | cpu | 22862306a36Sopenharmony_ci # +------------+------------+------------+ 22962306a36Sopenharmony_ci # 23062306a36Sopenharmony_ci # We need to combine the two continuous packets to get the instruction 23162306a36Sopenharmony_ci # range for sample_prev::cpu: 23262306a36Sopenharmony_ci # 23362306a36Sopenharmony_ci # [ sample_prev::addr .. sample_next::ip ] 23462306a36Sopenharmony_ci # 23562306a36Sopenharmony_ci # For this purose, sample_prev::addr is stored into cpu_data structure 23662306a36Sopenharmony_ci # and read back for 'start_addr' when the new packet comes, and we need 23762306a36Sopenharmony_ci # to use sample_next::ip to calculate 'stop_addr', plusing extra 4 for 23862306a36Sopenharmony_ci # 'stop_addr' is for the sake of objdump so the final assembler dump can 23962306a36Sopenharmony_ci # include last instruction for sample_next::ip. 24062306a36Sopenharmony_ci start_addr = cpu_data[str(cpu) + 'addr'] 24162306a36Sopenharmony_ci stop_addr = ip + 4 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci # Record for previous sample packet 24462306a36Sopenharmony_ci cpu_data[str(cpu) + 'addr'] = addr 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci # Handle CS_ETM_TRACE_ON packet if start_addr=0 and stop_addr=4 24762306a36Sopenharmony_ci if (start_addr == 0 and stop_addr == 4): 24862306a36Sopenharmony_ci print("CPU%d: CS_ETM_TRACE_ON packet is inserted" % cpu) 24962306a36Sopenharmony_ci return 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (start_addr < int(dso_start) or start_addr > int(dso_end)): 25262306a36Sopenharmony_ci print("Start address 0x%x is out of range [ 0x%x .. 0x%x ] for dso %s" % (start_addr, int(dso_start), int(dso_end), dso)) 25362306a36Sopenharmony_ci return 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (stop_addr < int(dso_start) or stop_addr > int(dso_end)): 25662306a36Sopenharmony_ci print("Stop address 0x%x is out of range [ 0x%x .. 0x%x ] for dso %s" % (stop_addr, int(dso_start), int(dso_end), dso)) 25762306a36Sopenharmony_ci return 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (options.objdump_name != None): 26062306a36Sopenharmony_ci # It doesn't need to decrease virtual memory offset for disassembly 26162306a36Sopenharmony_ci # for kernel dso, so in this case we set vm_start to zero. 26262306a36Sopenharmony_ci if (dso == "[kernel.kallsyms]"): 26362306a36Sopenharmony_ci dso_vm_start = 0 26462306a36Sopenharmony_ci else: 26562306a36Sopenharmony_ci dso_vm_start = int(dso_start) 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci dso_fname = get_dso_file_path(dso, dso_bid) 26862306a36Sopenharmony_ci if path.exists(dso_fname): 26962306a36Sopenharmony_ci print_disam(dso_fname, dso_vm_start, start_addr, stop_addr) 27062306a36Sopenharmony_ci else: 27162306a36Sopenharmony_ci print("Failed to find dso %s for address range [ 0x%x .. 0x%x ]" % (dso, start_addr, stop_addr)) 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci print_srccode(comm, param_dict, sample, symbol, dso) 274