Index: tools/cygprofile/symbolize.py |
diff --git a/tools/cygprofile/symbolize.py b/tools/cygprofile/symbolize.py |
index 027da57a521ac95975d598e60687ae50877c2aba..bcc3751291da4a42d5c1747c9047b223d9ace10e 100755 |
--- a/tools/cygprofile/symbolize.py |
+++ b/tools/cygprofile/symbolize.py |
@@ -63,6 +63,10 @@ def ParseLogLines(log_file_lines): |
return call_info |
+def GetStdOutputLines(cmd): |
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
+ output = p.communicate()[0] |
+ return output.split('\n') |
def ParseLibSymbols(lib_file): |
"""Get output from running nm and greping for text symbols. |
@@ -75,9 +79,7 @@ def ParseLibSymbols(lib_file): |
in lib_file and map of addresses to all symbols at a particular address |
""" |
cmd = ['nm', '-S', '-n', lib_file] |
- nm_p = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
- output = nm_p.communicate()[0] |
- nm_lines = output.split('\n') |
+ nm_lines = GetStdOutputLines(cmd) |
nm_symbols = [] |
for nm_line in nm_lines: |
@@ -170,15 +172,66 @@ def FindFunctions(addr, unique_addrs, address_map): |
def AddrToLine(addr, lib_file): |
"""Use addr2line to determine line info of a particular address.""" |
cmd = ['addr2line', '-f', '-e', lib_file, hex(addr)] |
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
- output = (p.communicate()[0]).split('\n') |
- line = output[0] |
- index = 1 |
- while index < len(output): |
- line = line + ':' + output[index] |
- index += 1 |
- return line |
- |
+ output = GetStdOutputLines(cmd) |
+ assert(len(output) == 2) |
+ return ':'.join(output) |
+ |
+def GetObjectFileNames(obj_dir): |
+ """ Gets the list of object files in the output directory. """ |
+ obj_files = [] |
+ for (dirpath, dirnames, filenames) in os.walk(obj_dir): |
+ for file_name in filenames: |
+ if file_name.endswith('.o'): |
+ obj_files.append(os.path.join(dirpath, file_name)) |
+ return obj_files |
+ |
+class WarningCollector(object): |
+ def __init__(self, max_warnings): |
+ self._warnings = 0 |
+ self._max_warnings = max_warnings |
+ |
+ def Write(self, message): |
+ if self._warnings < self._max_warnings: |
+ sys.stderr.write(message + '\n') |
+ self._warnings += 1 |
+ |
+ def WriteEnd(self, message): |
+ if self._warnings > self._max_warnings: |
+ sys.stderr.write(str(self._warnings - self._max_warnings) + |
+ ' more warnings for: ' + message + '\n') |
+ |
+def SymbolToSection(obj_dir): |
+ """ Gets a mapping from symbol to linker section name by scanning all |
+ of the object files. """ |
+ object_files = GetObjectFileNames(obj_dir) |
+ symbol_to_section_map = {} |
+ symbol_warnings = WarningCollector(300) |
+ for obj_file in object_files: |
+ cmd = ['objdump', '-w', '-t', obj_file] |
+ symbol_lines = GetStdOutputLines(cmd) |
+ for symbol_line in symbol_lines: |
+ items = symbol_line.split() |
+ # All of the symbol lines we care about are in the form |
+ # 0000000000 g F .text.foo 000000000 [.hidden] foo |
+ # where g (global) might also be l (local) or w (weak). |
+ if len(items) > 4 and items[2] == 'F': |
+ # This symbol is a function |
+ symbol = items[len(items) - 1] |
+ if symbol.startswith('.LTHUNK'): |
+ continue |
+ section = items[3] |
+ if ((symbol in symbol_to_section_map) and |
+ (symbol_to_section_map[symbol] != section)): |
+ symbol_warnings.Write('WARNING: Symbol ' + symbol + |
+ ' in conflicting sections ' + section + |
+ ' and ' + symbol_to_section_map[symbol]) |
+ elif not section.startswith('.text.'): |
+ symbol_warnings.Write('WARNING: Symbol ' + symbol + |
+ ' in incorrect section ' + section) |
+ else: |
+ symbol_to_section_map[symbol] = section |
+ symbol_warnings.WriteEnd('bad sections') |
+ return symbol_to_section_map |
def main(): |
"""Write output for profiled run to standard out. |
@@ -201,6 +254,8 @@ def main(): |
(log_file, lib_file) = args |
output_type = options.output_type |
+ obj_dir = os.path.abspath(os.path.join(os.path.dirname(lib_file), '../obj')) |
+ |
lib_name = lib_file.split('/')[-1].strip() |
log_file_lines = map(string.rstrip, open(log_file).readlines()) |
call_info = ParseLogLines(log_file_lines) |
@@ -218,23 +273,33 @@ def main(): |
print('WARNING: Address ' + hex(addr) + ' (line= ' + |
AddrToLine(addr, lib_file) + ') already profiled.') |
+ symbol_to_section_map = SymbolToSection(obj_dir) |
+ |
+ unknown_symbol_warnings = WarningCollector(300) |
+ symbol_not_found_warnings = WarningCollector(300) |
for call in call_info: |
+ addr = call[3] |
if output_type == 'lineize': |
- symbol = AddrToLine(call[3], lib_file) |
+ symbol = AddrToLine(addr, lib_file) |
print(str(call[0]) + ' ' + str(call[1]) + '\t' + str(call[2]) + '\t' |
+ symbol) |
elif output_type == 'orderfile': |
try: |
- symbols = FindFunctions(call[3], unique_addrs, address_map) |
+ symbols = FindFunctions(addr, unique_addrs, address_map) |
for symbol in symbols: |
- print '.text.' + symbol |
+ if symbol in symbol_to_section_map: |
+ print symbol_to_section_map[symbol] |
+ else: |
+ unknown_symbol_warnings.Write( |
+ 'WARNING: No known section for symbol ' + symbol) |
print '' |
except SymbolNotFoundException as e: |
- sys.stderr.write('WARNING: Did not find function in binary. addr: ' |
- + hex(addr) + '\n') |
+ symbol_not_found_warnings.Write( |
+ 'WARNING: Did not find function in binary. addr: ' |
+ + hex(addr)) |
else: |
try: |
- symbols = FindFunctions(call[3], unique_addrs, address_map) |
+ symbols = FindFunctions(addr, unique_addrs, address_map) |
print(str(call[0]) + ' ' + str(call[1]) + '\t' + str(call[2]) + '\t' |
+ symbols[0]) |
first_symbol = True |
@@ -244,8 +309,11 @@ def main(): |
else: |
first_symbol = False |
except SymbolNotFoundException as e: |
- sys.stderr.write('WARNING: Did not find function in binary. addr: ' |
- + hex(addr) + '\n') |
+ symbol_not_found_warnings.Write( |
+ 'WARNING: Did not find function in binary. addr: ' |
+ + hex(addr)) |
+ unknown_symbol_warnings.WriteEnd('no known section for symbol') |
+ symbol_not_found_warnings.WriteEnd('did not find function') |
if __name__ == '__main__': |
main() |