17db96d56Sopenharmony_ci#! /usr/bin/env python3 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ci# objgraph 47db96d56Sopenharmony_ci# 57db96d56Sopenharmony_ci# Read "nm -o" input of a set of libraries or modules and print various 67db96d56Sopenharmony_ci# interesting listings, such as: 77db96d56Sopenharmony_ci# 87db96d56Sopenharmony_ci# - which names are used but not defined in the set (and used where), 97db96d56Sopenharmony_ci# - which names are defined in the set (and where), 107db96d56Sopenharmony_ci# - which modules use which other modules, 117db96d56Sopenharmony_ci# - which modules are used by which other modules. 127db96d56Sopenharmony_ci# 137db96d56Sopenharmony_ci# Usage: objgraph [-cdu] [file] ... 147db96d56Sopenharmony_ci# -c: print callers per objectfile 157db96d56Sopenharmony_ci# -d: print callees per objectfile 167db96d56Sopenharmony_ci# -u: print usage of undefined symbols 177db96d56Sopenharmony_ci# If none of -cdu is specified, all are assumed. 187db96d56Sopenharmony_ci# Use "nm -o" to generate the input 197db96d56Sopenharmony_ci# e.g.: nm -o /lib/libc.a | objgraph 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_ciimport sys 237db96d56Sopenharmony_ciimport os 247db96d56Sopenharmony_ciimport getopt 257db96d56Sopenharmony_ciimport re 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ci# Types of symbols. 287db96d56Sopenharmony_ci# 297db96d56Sopenharmony_cidefinitions = 'TRGDSBAEC' 307db96d56Sopenharmony_ciexternals = 'UV' 317db96d56Sopenharmony_ciignore = 'Nntrgdsbavuc' 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_ci# Regular expression to parse "nm -o" output. 347db96d56Sopenharmony_ci# 357db96d56Sopenharmony_cimatcher = re.compile('(.*):\t?........ (.) (.*)$') 367db96d56Sopenharmony_ci 377db96d56Sopenharmony_ci# Store "item" in "dict" under "key". 387db96d56Sopenharmony_ci# The dictionary maps keys to lists of items. 397db96d56Sopenharmony_ci# If there is no list for the key yet, it is created. 407db96d56Sopenharmony_ci# 417db96d56Sopenharmony_cidef store(dict, key, item): 427db96d56Sopenharmony_ci if key in dict: 437db96d56Sopenharmony_ci dict[key].append(item) 447db96d56Sopenharmony_ci else: 457db96d56Sopenharmony_ci dict[key] = [item] 467db96d56Sopenharmony_ci 477db96d56Sopenharmony_ci# Return a flattened version of a list of strings: the concatenation 487db96d56Sopenharmony_ci# of its elements with intervening spaces. 497db96d56Sopenharmony_ci# 507db96d56Sopenharmony_cidef flat(list): 517db96d56Sopenharmony_ci s = '' 527db96d56Sopenharmony_ci for item in list: 537db96d56Sopenharmony_ci s = s + ' ' + item 547db96d56Sopenharmony_ci return s[1:] 557db96d56Sopenharmony_ci 567db96d56Sopenharmony_ci# Global variables mapping defined/undefined names to files and back. 577db96d56Sopenharmony_ci# 587db96d56Sopenharmony_cifile2undef = {} 597db96d56Sopenharmony_cidef2file = {} 607db96d56Sopenharmony_cifile2def = {} 617db96d56Sopenharmony_ciundef2file = {} 627db96d56Sopenharmony_ci 637db96d56Sopenharmony_ci# Read one input file and merge the data into the tables. 647db96d56Sopenharmony_ci# Argument is an open file. 657db96d56Sopenharmony_ci# 667db96d56Sopenharmony_cidef readinput(fp): 677db96d56Sopenharmony_ci while 1: 687db96d56Sopenharmony_ci s = fp.readline() 697db96d56Sopenharmony_ci if not s: 707db96d56Sopenharmony_ci break 717db96d56Sopenharmony_ci # If you get any output from this line, 727db96d56Sopenharmony_ci # it is probably caused by an unexpected input line: 737db96d56Sopenharmony_ci if matcher.search(s) < 0: s; continue # Shouldn't happen 747db96d56Sopenharmony_ci (ra, rb), (r1a, r1b), (r2a, r2b), (r3a, r3b) = matcher.regs[:4] 757db96d56Sopenharmony_ci fn, name, type = s[r1a:r1b], s[r3a:r3b], s[r2a:r2b] 767db96d56Sopenharmony_ci if type in definitions: 777db96d56Sopenharmony_ci store(def2file, name, fn) 787db96d56Sopenharmony_ci store(file2def, fn, name) 797db96d56Sopenharmony_ci elif type in externals: 807db96d56Sopenharmony_ci store(file2undef, fn, name) 817db96d56Sopenharmony_ci store(undef2file, name, fn) 827db96d56Sopenharmony_ci elif not type in ignore: 837db96d56Sopenharmony_ci print(fn + ':' + name + ': unknown type ' + type) 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci# Print all names that were undefined in some module and where they are 867db96d56Sopenharmony_ci# defined. 877db96d56Sopenharmony_ci# 887db96d56Sopenharmony_cidef printcallee(): 897db96d56Sopenharmony_ci flist = sorted(file2undef.keys()) 907db96d56Sopenharmony_ci for filename in flist: 917db96d56Sopenharmony_ci print(filename + ':') 927db96d56Sopenharmony_ci elist = file2undef[filename] 937db96d56Sopenharmony_ci elist.sort() 947db96d56Sopenharmony_ci for ext in elist: 957db96d56Sopenharmony_ci if len(ext) >= 8: 967db96d56Sopenharmony_ci tabs = '\t' 977db96d56Sopenharmony_ci else: 987db96d56Sopenharmony_ci tabs = '\t\t' 997db96d56Sopenharmony_ci if ext not in def2file: 1007db96d56Sopenharmony_ci print('\t' + ext + tabs + ' *undefined') 1017db96d56Sopenharmony_ci else: 1027db96d56Sopenharmony_ci print('\t' + ext + tabs + flat(def2file[ext])) 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci# Print for each module the names of the other modules that use it. 1057db96d56Sopenharmony_ci# 1067db96d56Sopenharmony_cidef printcaller(): 1077db96d56Sopenharmony_ci files = sorted(file2def.keys()) 1087db96d56Sopenharmony_ci for filename in files: 1097db96d56Sopenharmony_ci callers = [] 1107db96d56Sopenharmony_ci for label in file2def[filename]: 1117db96d56Sopenharmony_ci if label in undef2file: 1127db96d56Sopenharmony_ci callers = callers + undef2file[label] 1137db96d56Sopenharmony_ci if callers: 1147db96d56Sopenharmony_ci callers.sort() 1157db96d56Sopenharmony_ci print(filename + ':') 1167db96d56Sopenharmony_ci lastfn = '' 1177db96d56Sopenharmony_ci for fn in callers: 1187db96d56Sopenharmony_ci if fn != lastfn: 1197db96d56Sopenharmony_ci print('\t' + fn) 1207db96d56Sopenharmony_ci lastfn = fn 1217db96d56Sopenharmony_ci else: 1227db96d56Sopenharmony_ci print(filename + ': unused') 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci# Print undefined names and where they are used. 1257db96d56Sopenharmony_ci# 1267db96d56Sopenharmony_cidef printundef(): 1277db96d56Sopenharmony_ci undefs = {} 1287db96d56Sopenharmony_ci for filename in list(file2undef.keys()): 1297db96d56Sopenharmony_ci for ext in file2undef[filename]: 1307db96d56Sopenharmony_ci if ext not in def2file: 1317db96d56Sopenharmony_ci store(undefs, ext, filename) 1327db96d56Sopenharmony_ci elist = sorted(undefs.keys()) 1337db96d56Sopenharmony_ci for ext in elist: 1347db96d56Sopenharmony_ci print(ext + ':') 1357db96d56Sopenharmony_ci flist = sorted(undefs[ext]) 1367db96d56Sopenharmony_ci for filename in flist: 1377db96d56Sopenharmony_ci print('\t' + filename) 1387db96d56Sopenharmony_ci 1397db96d56Sopenharmony_ci# Print warning messages about names defined in more than one file. 1407db96d56Sopenharmony_ci# 1417db96d56Sopenharmony_cidef warndups(): 1427db96d56Sopenharmony_ci savestdout = sys.stdout 1437db96d56Sopenharmony_ci sys.stdout = sys.stderr 1447db96d56Sopenharmony_ci names = sorted(def2file.keys()) 1457db96d56Sopenharmony_ci for name in names: 1467db96d56Sopenharmony_ci if len(def2file[name]) > 1: 1477db96d56Sopenharmony_ci print('warning:', name, 'multiply defined:', end=' ') 1487db96d56Sopenharmony_ci print(flat(def2file[name])) 1497db96d56Sopenharmony_ci sys.stdout = savestdout 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ci# Main program 1527db96d56Sopenharmony_ci# 1537db96d56Sopenharmony_cidef main(): 1547db96d56Sopenharmony_ci try: 1557db96d56Sopenharmony_ci optlist, args = getopt.getopt(sys.argv[1:], 'cdu') 1567db96d56Sopenharmony_ci except getopt.error: 1577db96d56Sopenharmony_ci sys.stdout = sys.stderr 1587db96d56Sopenharmony_ci print('Usage:', os.path.basename(sys.argv[0]), end=' ') 1597db96d56Sopenharmony_ci print('[-cdu] [file] ...') 1607db96d56Sopenharmony_ci print('-c: print callers per objectfile') 1617db96d56Sopenharmony_ci print('-d: print callees per objectfile') 1627db96d56Sopenharmony_ci print('-u: print usage of undefined symbols') 1637db96d56Sopenharmony_ci print('If none of -cdu is specified, all are assumed.') 1647db96d56Sopenharmony_ci print('Use "nm -o" to generate the input') 1657db96d56Sopenharmony_ci print('e.g.: nm -o /lib/libc.a | objgraph') 1667db96d56Sopenharmony_ci return 1 1677db96d56Sopenharmony_ci optu = optc = optd = 0 1687db96d56Sopenharmony_ci for opt, void in optlist: 1697db96d56Sopenharmony_ci if opt == '-u': 1707db96d56Sopenharmony_ci optu = 1 1717db96d56Sopenharmony_ci elif opt == '-c': 1727db96d56Sopenharmony_ci optc = 1 1737db96d56Sopenharmony_ci elif opt == '-d': 1747db96d56Sopenharmony_ci optd = 1 1757db96d56Sopenharmony_ci if optu == optc == optd == 0: 1767db96d56Sopenharmony_ci optu = optc = optd = 1 1777db96d56Sopenharmony_ci if not args: 1787db96d56Sopenharmony_ci args = ['-'] 1797db96d56Sopenharmony_ci for filename in args: 1807db96d56Sopenharmony_ci if filename == '-': 1817db96d56Sopenharmony_ci readinput(sys.stdin) 1827db96d56Sopenharmony_ci else: 1837db96d56Sopenharmony_ci with open(filename) as f: 1847db96d56Sopenharmony_ci readinput(f) 1857db96d56Sopenharmony_ci # 1867db96d56Sopenharmony_ci warndups() 1877db96d56Sopenharmony_ci # 1887db96d56Sopenharmony_ci more = (optu + optc + optd > 1) 1897db96d56Sopenharmony_ci if optd: 1907db96d56Sopenharmony_ci if more: 1917db96d56Sopenharmony_ci print('---------------All callees------------------') 1927db96d56Sopenharmony_ci printcallee() 1937db96d56Sopenharmony_ci if optu: 1947db96d56Sopenharmony_ci if more: 1957db96d56Sopenharmony_ci print('---------------Undefined callees------------') 1967db96d56Sopenharmony_ci printundef() 1977db96d56Sopenharmony_ci if optc: 1987db96d56Sopenharmony_ci if more: 1997db96d56Sopenharmony_ci print('---------------All Callers------------------') 2007db96d56Sopenharmony_ci printcaller() 2017db96d56Sopenharmony_ci return 0 2027db96d56Sopenharmony_ci 2037db96d56Sopenharmony_ci# Call the main program. 2047db96d56Sopenharmony_ci# Use its return value as exit status. 2057db96d56Sopenharmony_ci# Catch interrupts to avoid stack trace. 2067db96d56Sopenharmony_ci# 2077db96d56Sopenharmony_ciif __name__ == '__main__': 2087db96d56Sopenharmony_ci try: 2097db96d56Sopenharmony_ci sys.exit(main()) 2107db96d56Sopenharmony_ci except KeyboardInterrupt: 2117db96d56Sopenharmony_ci sys.exit(1) 212