112fdd6c0SLeo Yan# SPDX-License-Identifier: GPL-2.0 212fdd6c0SLeo Yan# arm-cs-trace-disasm.py: ARM CoreSight Trace Dump With Disassember 312fdd6c0SLeo Yan# 412fdd6c0SLeo Yan# Author: Tor Jeremiassen <tor@ti.com> 512fdd6c0SLeo Yan# Mathieu Poirier <mathieu.poirier@linaro.org> 612fdd6c0SLeo Yan# Leo Yan <leo.yan@linaro.org> 712fdd6c0SLeo Yan# Al Grant <Al.Grant@arm.com> 812fdd6c0SLeo Yan 912fdd6c0SLeo Yanfrom __future__ import print_function 1012fdd6c0SLeo Yanimport os 1112fdd6c0SLeo Yanfrom os import path 1212fdd6c0SLeo Yanimport re 1312fdd6c0SLeo Yanfrom subprocess import * 1412fdd6c0SLeo Yanfrom optparse import OptionParser, make_option 1512fdd6c0SLeo Yan 1612fdd6c0SLeo Yanfrom perf_trace_context import perf_set_itrace_options, \ 1712fdd6c0SLeo Yan perf_sample_insn, perf_sample_srccode 1812fdd6c0SLeo Yan 1912fdd6c0SLeo Yan# Below are some example commands for using this script. 2012fdd6c0SLeo Yan# 2112fdd6c0SLeo Yan# Output disassembly with objdump: 2212fdd6c0SLeo Yan# perf script -s scripts/python/arm-cs-trace-disasm.py \ 2312fdd6c0SLeo Yan# -- -d objdump -k path/to/vmlinux 2412fdd6c0SLeo Yan# Output disassembly with llvm-objdump: 2512fdd6c0SLeo Yan# perf script -s scripts/python/arm-cs-trace-disasm.py \ 2612fdd6c0SLeo Yan# -- -d llvm-objdump-11 -k path/to/vmlinux 2712fdd6c0SLeo Yan# Output only source line and symbols: 2812fdd6c0SLeo Yan# perf script -s scripts/python/arm-cs-trace-disasm.py 2912fdd6c0SLeo Yan 3012fdd6c0SLeo Yan# Command line parsing. 3112fdd6c0SLeo Yanoption_list = [ 3212fdd6c0SLeo Yan # formatting options for the bottom entry of the stack 3312fdd6c0SLeo Yan make_option("-k", "--vmlinux", dest="vmlinux_name", 3412fdd6c0SLeo Yan help="Set path to vmlinux file"), 3512fdd6c0SLeo Yan make_option("-d", "--objdump", dest="objdump_name", 3612fdd6c0SLeo Yan help="Set path to objdump executable file"), 3712fdd6c0SLeo Yan make_option("-v", "--verbose", dest="verbose", 3812fdd6c0SLeo Yan action="store_true", default=False, 3912fdd6c0SLeo Yan help="Enable debugging log") 4012fdd6c0SLeo Yan] 4112fdd6c0SLeo Yan 4212fdd6c0SLeo Yanparser = OptionParser(option_list=option_list) 4312fdd6c0SLeo Yan(options, args) = parser.parse_args() 4412fdd6c0SLeo Yan 4512fdd6c0SLeo Yan# Initialize global dicts and regular expression 4612fdd6c0SLeo Yandisasm_cache = dict() 4712fdd6c0SLeo Yancpu_data = dict() 4812fdd6c0SLeo Yandisasm_re = re.compile("^\s*([0-9a-fA-F]+):") 4912fdd6c0SLeo Yandisasm_func_re = re.compile("^\s*([0-9a-fA-F]+)\s.*:") 5012fdd6c0SLeo Yancache_size = 64*1024 5112fdd6c0SLeo Yan 5212fdd6c0SLeo Yanglb_source_file_name = None 5312fdd6c0SLeo Yanglb_line_number = None 5412fdd6c0SLeo Yanglb_dso = None 5512fdd6c0SLeo Yan 5612fdd6c0SLeo Yandef get_optional(perf_dict, field): 5712fdd6c0SLeo Yan if field in perf_dict: 5812fdd6c0SLeo Yan return perf_dict[field] 5912fdd6c0SLeo Yan return "[unknown]" 6012fdd6c0SLeo Yan 6112fdd6c0SLeo Yandef get_offset(perf_dict, field): 6212fdd6c0SLeo Yan if field in perf_dict: 63*b2265219SLeo Yan return "+%#x" % perf_dict[field] 6412fdd6c0SLeo Yan return "" 6512fdd6c0SLeo Yan 6612fdd6c0SLeo Yandef get_dso_file_path(dso_name, dso_build_id): 6712fdd6c0SLeo Yan if (dso_name == "[kernel.kallsyms]" or dso_name == "vmlinux"): 6812fdd6c0SLeo Yan if (options.vmlinux_name): 6912fdd6c0SLeo Yan return options.vmlinux_name; 7012fdd6c0SLeo Yan else: 7112fdd6c0SLeo Yan return dso_name 7212fdd6c0SLeo Yan 7312fdd6c0SLeo Yan if (dso_name == "[vdso]") : 7412fdd6c0SLeo Yan append = "/vdso" 7512fdd6c0SLeo Yan else: 7612fdd6c0SLeo Yan append = "/elf" 7712fdd6c0SLeo Yan 78*b2265219SLeo Yan dso_path = os.environ['PERF_BUILDID_DIR'] + "/" + dso_name + "/" + dso_build_id + append; 7912fdd6c0SLeo Yan # Replace duplicate slash chars to single slash char 8012fdd6c0SLeo Yan dso_path = dso_path.replace('//', '/', 1) 8112fdd6c0SLeo Yan return dso_path 8212fdd6c0SLeo Yan 8312fdd6c0SLeo Yandef read_disam(dso_fname, dso_start, start_addr, stop_addr): 8412fdd6c0SLeo Yan addr_range = str(start_addr) + ":" + str(stop_addr) + ":" + dso_fname 8512fdd6c0SLeo Yan 8612fdd6c0SLeo Yan # Don't let the cache get too big, clear it when it hits max size 8712fdd6c0SLeo Yan if (len(disasm_cache) > cache_size): 8812fdd6c0SLeo Yan disasm_cache.clear(); 8912fdd6c0SLeo Yan 9012fdd6c0SLeo Yan if addr_range in disasm_cache: 9112fdd6c0SLeo Yan disasm_output = disasm_cache[addr_range]; 9212fdd6c0SLeo Yan else: 9312fdd6c0SLeo Yan start_addr = start_addr - dso_start; 9412fdd6c0SLeo Yan stop_addr = stop_addr - dso_start; 9512fdd6c0SLeo Yan disasm = [ options.objdump_name, "-d", "-z", 96*b2265219SLeo Yan "--start-address="+format(start_addr,"#x"), 97*b2265219SLeo Yan "--stop-address="+format(stop_addr,"#x") ] 9812fdd6c0SLeo Yan disasm += [ dso_fname ] 9912fdd6c0SLeo Yan disasm_output = check_output(disasm).decode('utf-8').split('\n') 10012fdd6c0SLeo Yan disasm_cache[addr_range] = disasm_output 10112fdd6c0SLeo Yan 10212fdd6c0SLeo Yan return disasm_output 10312fdd6c0SLeo Yan 10412fdd6c0SLeo Yandef print_disam(dso_fname, dso_start, start_addr, stop_addr): 10512fdd6c0SLeo Yan for line in read_disam(dso_fname, dso_start, start_addr, stop_addr): 10612fdd6c0SLeo Yan m = disasm_func_re.search(line) 10712fdd6c0SLeo Yan if m is None: 10812fdd6c0SLeo Yan m = disasm_re.search(line) 10912fdd6c0SLeo Yan if m is None: 11012fdd6c0SLeo Yan continue 111*b2265219SLeo Yan print("\t" + line) 11212fdd6c0SLeo Yan 11312fdd6c0SLeo Yandef print_sample(sample): 114*b2265219SLeo Yan print("Sample = { cpu: %04d addr: 0x%016x phys_addr: 0x%016x ip: 0x%016x " \ 115*b2265219SLeo Yan "pid: %d tid: %d period: %d time: %d }" % \ 116*b2265219SLeo Yan (sample['cpu'], sample['addr'], sample['phys_addr'], \ 117*b2265219SLeo Yan sample['ip'], sample['pid'], sample['tid'], \ 118*b2265219SLeo Yan sample['period'], sample['time'])) 11912fdd6c0SLeo Yan 12012fdd6c0SLeo Yandef trace_begin(): 12112fdd6c0SLeo Yan print('ARM CoreSight Trace Data Assembler Dump') 12212fdd6c0SLeo Yan 12312fdd6c0SLeo Yandef trace_end(): 12412fdd6c0SLeo Yan print('End') 12512fdd6c0SLeo Yan 12612fdd6c0SLeo Yandef trace_unhandled(event_name, context, event_fields_dict): 12712fdd6c0SLeo Yan print(' '.join(['%s=%s'%(k,str(v))for k,v in sorted(event_fields_dict.items())])) 12812fdd6c0SLeo Yan 12912fdd6c0SLeo Yandef common_start_str(comm, sample): 13012fdd6c0SLeo Yan sec = int(sample["time"] / 1000000000) 13112fdd6c0SLeo Yan ns = sample["time"] % 1000000000 13212fdd6c0SLeo Yan cpu = sample["cpu"] 13312fdd6c0SLeo Yan pid = sample["pid"] 13412fdd6c0SLeo Yan tid = sample["tid"] 135*b2265219SLeo Yan return "%16s %5u/%-5u [%04u] %9u.%09u " % (comm, pid, tid, cpu, sec, ns) 13612fdd6c0SLeo Yan 13712fdd6c0SLeo Yan# This code is copied from intel-pt-events.py for printing source code 13812fdd6c0SLeo Yan# line and symbols. 13912fdd6c0SLeo Yandef print_srccode(comm, param_dict, sample, symbol, dso): 14012fdd6c0SLeo Yan ip = sample["ip"] 14112fdd6c0SLeo Yan if symbol == "[unknown]": 14212fdd6c0SLeo Yan start_str = common_start_str(comm, sample) + ("%x" % ip).rjust(16).ljust(40) 14312fdd6c0SLeo Yan else: 14412fdd6c0SLeo Yan offs = get_offset(param_dict, "symoff") 14512fdd6c0SLeo Yan start_str = common_start_str(comm, sample) + (symbol + offs).ljust(40) 14612fdd6c0SLeo Yan 14712fdd6c0SLeo Yan global glb_source_file_name 14812fdd6c0SLeo Yan global glb_line_number 14912fdd6c0SLeo Yan global glb_dso 15012fdd6c0SLeo Yan 15112fdd6c0SLeo Yan source_file_name, line_number, source_line = perf_sample_srccode(perf_script_context) 15212fdd6c0SLeo Yan if source_file_name: 15312fdd6c0SLeo Yan if glb_line_number == line_number and glb_source_file_name == source_file_name: 15412fdd6c0SLeo Yan src_str = "" 15512fdd6c0SLeo Yan else: 15612fdd6c0SLeo Yan if len(source_file_name) > 40: 15712fdd6c0SLeo Yan src_file = ("..." + source_file_name[-37:]) + " " 15812fdd6c0SLeo Yan else: 15912fdd6c0SLeo Yan src_file = source_file_name.ljust(41) 16012fdd6c0SLeo Yan 16112fdd6c0SLeo Yan if source_line is None: 16212fdd6c0SLeo Yan src_str = src_file + str(line_number).rjust(4) + " <source not found>" 16312fdd6c0SLeo Yan else: 16412fdd6c0SLeo Yan src_str = src_file + str(line_number).rjust(4) + " " + source_line 16512fdd6c0SLeo Yan glb_dso = None 16612fdd6c0SLeo Yan elif dso == glb_dso: 16712fdd6c0SLeo Yan src_str = "" 16812fdd6c0SLeo Yan else: 16912fdd6c0SLeo Yan src_str = dso 17012fdd6c0SLeo Yan glb_dso = dso 17112fdd6c0SLeo Yan 17212fdd6c0SLeo Yan glb_line_number = line_number 17312fdd6c0SLeo Yan glb_source_file_name = source_file_name 17412fdd6c0SLeo Yan 175*b2265219SLeo Yan print(start_str, src_str) 17612fdd6c0SLeo Yan 17712fdd6c0SLeo Yandef process_event(param_dict): 17812fdd6c0SLeo Yan global cache_size 17912fdd6c0SLeo Yan global options 18012fdd6c0SLeo Yan 18112fdd6c0SLeo Yan sample = param_dict["sample"] 18212fdd6c0SLeo Yan comm = param_dict["comm"] 18312fdd6c0SLeo Yan 18412fdd6c0SLeo Yan name = param_dict["ev_name"] 18512fdd6c0SLeo Yan dso = get_optional(param_dict, "dso") 18612fdd6c0SLeo Yan dso_bid = get_optional(param_dict, "dso_bid") 18712fdd6c0SLeo Yan dso_start = get_optional(param_dict, "dso_map_start") 18812fdd6c0SLeo Yan dso_end = get_optional(param_dict, "dso_map_end") 18912fdd6c0SLeo Yan symbol = get_optional(param_dict, "symbol") 19012fdd6c0SLeo Yan 19112fdd6c0SLeo Yan if (options.verbose == True): 192*b2265219SLeo Yan print("Event type: %s" % name) 19312fdd6c0SLeo Yan print_sample(sample) 19412fdd6c0SLeo Yan 19512fdd6c0SLeo Yan # If cannot find dso so cannot dump assembler, bail out 19612fdd6c0SLeo Yan if (dso == '[unknown]'): 19712fdd6c0SLeo Yan return 19812fdd6c0SLeo Yan 19912fdd6c0SLeo Yan # Validate dso start and end addresses 20012fdd6c0SLeo Yan if ((dso_start == '[unknown]') or (dso_end == '[unknown]')): 201*b2265219SLeo Yan print("Failed to find valid dso map for dso %s" % dso) 20212fdd6c0SLeo Yan return 20312fdd6c0SLeo Yan 20412fdd6c0SLeo Yan if (name[0:12] == "instructions"): 20512fdd6c0SLeo Yan print_srccode(comm, param_dict, sample, symbol, dso) 20612fdd6c0SLeo Yan return 20712fdd6c0SLeo Yan 20812fdd6c0SLeo Yan # Don't proceed if this event is not a branch sample, . 20912fdd6c0SLeo Yan if (name[0:8] != "branches"): 21012fdd6c0SLeo Yan return 21112fdd6c0SLeo Yan 21212fdd6c0SLeo Yan cpu = sample["cpu"] 21312fdd6c0SLeo Yan ip = sample["ip"] 21412fdd6c0SLeo Yan addr = sample["addr"] 21512fdd6c0SLeo Yan 21612fdd6c0SLeo Yan # Initialize CPU data if it's empty, and directly return back 21712fdd6c0SLeo Yan # if this is the first tracing event for this CPU. 21812fdd6c0SLeo Yan if (cpu_data.get(str(cpu) + 'addr') == None): 21912fdd6c0SLeo Yan cpu_data[str(cpu) + 'addr'] = addr 22012fdd6c0SLeo Yan return 22112fdd6c0SLeo Yan 22212fdd6c0SLeo Yan # The format for packet is: 22312fdd6c0SLeo Yan # 22412fdd6c0SLeo Yan # +------------+------------+------------+ 22512fdd6c0SLeo Yan # sample_prev: | addr | ip | cpu | 22612fdd6c0SLeo Yan # +------------+------------+------------+ 22712fdd6c0SLeo Yan # sample_next: | addr | ip | cpu | 22812fdd6c0SLeo Yan # +------------+------------+------------+ 22912fdd6c0SLeo Yan # 23012fdd6c0SLeo Yan # We need to combine the two continuous packets to get the instruction 23112fdd6c0SLeo Yan # range for sample_prev::cpu: 23212fdd6c0SLeo Yan # 23312fdd6c0SLeo Yan # [ sample_prev::addr .. sample_next::ip ] 23412fdd6c0SLeo Yan # 23512fdd6c0SLeo Yan # For this purose, sample_prev::addr is stored into cpu_data structure 23612fdd6c0SLeo Yan # and read back for 'start_addr' when the new packet comes, and we need 23712fdd6c0SLeo Yan # to use sample_next::ip to calculate 'stop_addr', plusing extra 4 for 23812fdd6c0SLeo Yan # 'stop_addr' is for the sake of objdump so the final assembler dump can 23912fdd6c0SLeo Yan # include last instruction for sample_next::ip. 24012fdd6c0SLeo Yan start_addr = cpu_data[str(cpu) + 'addr'] 24112fdd6c0SLeo Yan stop_addr = ip + 4 24212fdd6c0SLeo Yan 24312fdd6c0SLeo Yan # Record for previous sample packet 24412fdd6c0SLeo Yan cpu_data[str(cpu) + 'addr'] = addr 24512fdd6c0SLeo Yan 24612fdd6c0SLeo Yan # Handle CS_ETM_TRACE_ON packet if start_addr=0 and stop_addr=4 24712fdd6c0SLeo Yan if (start_addr == 0 and stop_addr == 4): 248*b2265219SLeo Yan print("CPU%d: CS_ETM_TRACE_ON packet is inserted" % cpu) 24912fdd6c0SLeo Yan return 25012fdd6c0SLeo Yan 25112fdd6c0SLeo Yan if (start_addr < int(dso_start) or start_addr > int(dso_end)): 252*b2265219SLeo Yan 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)) 25312fdd6c0SLeo Yan return 25412fdd6c0SLeo Yan 25512fdd6c0SLeo Yan if (stop_addr < int(dso_start) or stop_addr > int(dso_end)): 256*b2265219SLeo Yan 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)) 25712fdd6c0SLeo Yan return 25812fdd6c0SLeo Yan 25912fdd6c0SLeo Yan if (options.objdump_name != None): 26012fdd6c0SLeo Yan # It doesn't need to decrease virtual memory offset for disassembly 26112fdd6c0SLeo Yan # for kernel dso, so in this case we set vm_start to zero. 26212fdd6c0SLeo Yan if (dso == "[kernel.kallsyms]"): 26312fdd6c0SLeo Yan dso_vm_start = 0 26412fdd6c0SLeo Yan else: 26512fdd6c0SLeo Yan dso_vm_start = int(dso_start) 26612fdd6c0SLeo Yan 26712fdd6c0SLeo Yan dso_fname = get_dso_file_path(dso, dso_bid) 26812fdd6c0SLeo Yan if path.exists(dso_fname): 26912fdd6c0SLeo Yan print_disam(dso_fname, dso_vm_start, start_addr, stop_addr) 27012fdd6c0SLeo Yan else: 271*b2265219SLeo Yan print("Failed to find dso %s for address range [ 0x%x .. 0x%x ]" % (dso, start_addr, stop_addr)) 27212fdd6c0SLeo Yan 27312fdd6c0SLeo Yan print_srccode(comm, param_dict, sample, symbol, dso) 274