1#!/usr/bin/env python3 2# SPDX-License-Identifier: Apache-2.0 3# ----------------------------------------------------------------------------- 4# Copyright 2021-2022 Arm Limited 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); you may not 7# use this file except in compliance with the License. You may obtain a copy 8# of the License at: 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15# License for the specific language governing permissions and limitations 16# under the License. 17# ----------------------------------------------------------------------------- 18""" 19The ``astc_dump_binary`` utility provides a wrapper around the ``objdump`` 20utility to extract disassembly of specific functions. Currently only matches 21the root name, for sake of command line sanity, so all overloads get dumped. 22 23Using __attribute__((noinline)) can be useful during profiling to stop any 24functions of interest getting inlined once they get too small ... 25""" 26 27 28import argparse 29import re 30import shutil 31import subprocess as sp 32import sys 33 34 35def run_objdump(binary, symbol): 36 """ 37 Run objdump on a single binary and extract the range for a given symbol. 38 39 Output is printed to stdout. 40 41 Args: 42 binary (str): The path of the binary file to process. 43 symbol (str): The symbol to match. 44 45 Raises: 46 CalledProcessException: The ``objdump`` subprocess failed for any reason. 47 """ 48 args = [ 49 "objdump", "-C", 50 "-M", "intel", 51 "--no-show-raw", 52 "-d", "-S", 53 binary 54 ] 55 56 result = sp.run(args, stdout=sp.PIPE, stderr=sp.PIPE, 57 check=True, universal_newlines=True) 58 59 funcPattern = re.compile(r"^[0-9a-f]{16} <(.*?)\(.*\)>:$") 60 61 funcLines = [] 62 funcActive = False 63 lines = result.stdout.splitlines() 64 65 for line in lines: 66 match = funcPattern.match(line) 67 if match: 68 funcName = match.group(1) 69 if funcName == symbol: 70 funcActive = True 71 else: 72 funcActive = False 73 74 if funcActive: 75 funcLines.append(line) 76 77 print("\n".join(funcLines)) 78 79def parse_command_line(): 80 """ 81 Parse the command line. 82 83 Returns: 84 Namespace: The parsed command line container. 85 """ 86 parser = argparse.ArgumentParser() 87 88 parser.add_argument("binary", type=argparse.FileType("r"), 89 help="The new binary to dump") 90 91 parser.add_argument("symbol", type=str, 92 help="The function name to dump") 93 94 return parser.parse_args() 95 96 97def main(): 98 """ 99 The main function. 100 101 Returns: 102 int: The process return code. 103 """ 104 args = parse_command_line() 105 106 # Preflight - check that size exists. Note that size might still fail at 107 # runtime later, e.g. if the binary is not of the correct format 108 path = shutil.which("objdump") 109 if not path: 110 print("ERROR: The 'objdump' utility is not installed on the PATH") 111 return 1 112 113 # Collect the data 114 try: 115 run_objdump(args.binary.name, args.symbol) 116 except sp.CalledProcessError as ex: 117 print("ERROR: The 'objdump' utility failed") 118 print(" %s" % ex.stderr.strip()) 119 return 1 120 121 return 0 122 123 124if __name__ == "__main__": 125 sys.exit(main()) 126