| OLD | NEW |
| 1 # Copyright 2017 The Chromium Authors. All rights reserved. | 1 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Functions that rely on parsing output of "nm" tool.""" | 5 """Functions that rely on parsing output of "nm" tool.""" |
| 6 | 6 |
| 7 import atexit | 7 import atexit |
| 8 import collections | 8 import collections |
| 9 import errno | 9 import errno |
| 10 import logging | 10 import logging |
| 11 import os | 11 import os |
| 12 import subprocess | 12 import subprocess |
| 13 import sys | 13 import sys |
| 14 | 14 |
| 15 import concurrent | 15 import concurrent |
| 16 | 16 |
| 17 _active_subprocesses = None | 17 _active_subprocesses = None |
| 18 | 18 |
| 19 | 19 |
| 20 def _IsRelevantNmName(name): |
| 21 # Skip lines like: |
| 22 # 00000000 t $t |
| 23 # 00000000 r $d |
| 24 # 0000041b r .L.str.38 |
| 25 # 00000344 N |
| 26 return name and not name.startswith('.L.str.') and not ( |
| 27 len(name) == 2 and name.startswith('$')) |
| 28 |
| 29 |
| 20 def CollectAliasesByAddress(elf_path, tool_prefix): | 30 def CollectAliasesByAddress(elf_path, tool_prefix): |
| 21 """Runs nm on |elf_path| and returns a dict of address->[names]""" | 31 """Runs nm on |elf_path| and returns a dict of address->[names]""" |
| 22 names_by_address = collections.defaultdict(list) | 32 names_by_address = collections.defaultdict(list) |
| 23 | 33 |
| 24 # About 60mb of output, but piping takes ~30s, and loading it into RAM | 34 # About 60mb of output, but piping takes ~30s, and loading it into RAM |
| 25 # directly takes 3s. | 35 # directly takes 3s. |
| 26 args = [tool_prefix + 'nm', '--no-sort', '--defined-only', '--demangle', | 36 args = [tool_prefix + 'nm', '--no-sort', '--defined-only', '--demangle', |
| 27 elf_path] | 37 elf_path] |
| 28 output = subprocess.check_output(args) | 38 output = subprocess.check_output(args) |
| 29 for line in output.splitlines(): | 39 for line in output.splitlines(): |
| 30 address_str, section, name = line.split(' ', 2) | 40 space_idx = line.find(' ') |
| 41 address_str = line[:space_idx] |
| 42 section = line[space_idx + 1] |
| 43 name = line[space_idx + 3:] |
| 44 |
| 31 # To verify that rodata does not have aliases: | 45 # To verify that rodata does not have aliases: |
| 32 # nm --no-sort --defined-only libchrome.so > nm.out | 46 # nm --no-sort --defined-only libchrome.so > nm.out |
| 33 # grep -v '\$' nm.out | grep ' r ' | sort | cut -d' ' -f1 > addrs | 47 # grep -v '\$' nm.out | grep ' r ' | sort | cut -d' ' -f1 > addrs |
| 34 # wc -l < addrs; uniq < addrs | wc -l | 48 # wc -l < addrs; uniq < addrs | wc -l |
| 35 if section not in 'tT' or not name or name[0] == '$': | 49 if section not in 'tT' or not _IsRelevantNmName(name): |
| 36 continue | 50 continue |
| 37 | 51 |
| 38 address = int(address_str, 16) | 52 address = int(address_str, 16) |
| 39 if not address: | 53 if not address: |
| 40 continue | 54 continue |
| 41 # Constructors often show up twice. | 55 # Constructors often show up twice. |
| 42 name_list = names_by_address[address] | 56 name_list = names_by_address[address] |
| 43 if name not in name_list: | 57 if name not in name_list: |
| 44 name_list.append(name) | 58 name_list.append(name) |
| 45 | 59 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 64 return concurrent.ForkAndCall( | 78 return concurrent.ForkAndCall( |
| 65 _CollectAliasesByAddressAsyncHelper, (elf_path, tool_prefix), | 79 _CollectAliasesByAddressAsyncHelper, (elf_path, tool_prefix), |
| 66 decode_func=decode) | 80 decode_func=decode) |
| 67 | 81 |
| 68 | 82 |
| 69 def _ParseOneObjectFileOutput(lines): | 83 def _ParseOneObjectFileOutput(lines): |
| 70 ret = [] | 84 ret = [] |
| 71 for line in lines: | 85 for line in lines: |
| 72 if not line: | 86 if not line: |
| 73 break | 87 break |
| 74 sep = line.find(' ') # Skip over address. | 88 space_idx = line.find(' ') # Skip over address. |
| 75 sep = line.find(' ', sep + 1) # Skip over symbol type. | 89 name = line[space_idx + 3:] |
| 76 name = line[sep + 1:] | 90 if _IsRelevantNmName(name): |
| 77 # Skip lines like: | |
| 78 # 00000000 t $t | |
| 79 # 00000000 r $d | |
| 80 # 0000041b r .L.str.38 | |
| 81 if name[0] not in '$.': | |
| 82 ret.append(name) | 91 ret.append(name) |
| 83 return ret | 92 return ret |
| 84 | 93 |
| 85 | 94 |
| 86 def _BatchCollectNames(target, tool_prefix, output_directory): | 95 def _BatchCollectNames(target, tool_prefix, output_directory): |
| 87 is_archive = isinstance(target, basestring) | 96 is_archive = isinstance(target, basestring) |
| 88 # Ensure tool_prefix is absolute so that CWD does not affect it | 97 # Ensure tool_prefix is absolute so that CWD does not affect it |
| 89 if os.path.sep in tool_prefix: | 98 if os.path.sep in tool_prefix: |
| 90 # Use abspath() on the dirname to avoid it stripping a trailing /. | 99 # Use abspath() on the dirname to avoid it stripping a trailing /. |
| 91 dirname = os.path.dirname(tool_prefix) | 100 dirname = os.path.dirname(tool_prefix) |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 249 except IOError, e: | 258 except IOError, e: |
| 250 # Parent process exited. | 259 # Parent process exited. |
| 251 if e.errno == errno.EPIPE: | 260 if e.errno == errno.EPIPE: |
| 252 sys.exit(1) | 261 sys.exit(1) |
| 253 | 262 |
| 254 logging.debug('nm bulk subprocess finished.') | 263 logging.debug('nm bulk subprocess finished.') |
| 255 | 264 |
| 256 | 265 |
| 257 if __name__ == '__main__': | 266 if __name__ == '__main__': |
| 258 _SubMain(*sys.argv[1:]) | 267 _SubMain(*sys.argv[1:]) |
| OLD | NEW |