diff options
Diffstat (limited to 'debug_info_test')
-rw-r--r-- | debug_info_test/allowlist.py | 71 | ||||
-rw-r--r-- | debug_info_test/check_cus.py | 107 | ||||
-rw-r--r-- | debug_info_test/check_exist.py | 140 | ||||
-rw-r--r-- | debug_info_test/check_icf.py | 81 | ||||
-rw-r--r-- | debug_info_test/check_ngcc.py | 29 | ||||
-rwxr-xr-x | debug_info_test/debug_info_test.py | 87 |
6 files changed, 260 insertions, 255 deletions
diff --git a/debug_info_test/allowlist.py b/debug_info_test/allowlist.py index 9cf42af0..70bb776a 100644 --- a/debug_info_test/allowlist.py +++ b/debug_info_test/allowlist.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright 2018 The Chromium OS Authors. All rights reserved. +# Copyright 2018 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Allowlist functions.""" -from __future__ import print_function import glob import os @@ -19,50 +18,50 @@ import re # The performance bottleneck of this script is readelf. Unless this becomes # slower than readelf, don't waste time here. def is_allowlisted(list_name, pattern): - """Check whether the given pattern is specified in the allowlist. + """Check whether the given pattern is specified in the allowlist. - Args: - list_name: name of the allowlist. - pattern: the target string. + Args: + list_name: name of the allowlist. + pattern: the target string. - Returns: - True if matched otherwise False. - """ - return pattern and allowlists[list_name].match(pattern) + Returns: + True if matched otherwise False. + """ + return pattern and allowlists[list_name].match(pattern) def prepare_allowlist(patterns): - """Join and compile the re patterns. + """Join and compile the re patterns. - Args: - patterns: regex patterns. + Args: + patterns: regex patterns. - Returns: - A compiled re object. - """ - return re.compile('|'.join(patterns)) + Returns: + A compiled re object. + """ + return re.compile("|".join(patterns)) def load_allowlists(dirname): - """Load allowlists under dirname. - - An allowlist ends with .allowlist. - - Args: - dirname: path to the dir. - - Returns: - A dictionary of 'filename' -> allowlist matcher. - """ - wlist = {} - for fn in glob.glob(os.path.join(dirname, '*.allowlist')): - key = os.path.splitext(os.path.basename(fn))[0] - with open(fn, 'r', encoding='utf-8') as f: - patterns = f.read().splitlines() - patterns = [l for l in patterns if l != ''] - patterns = [l for l in patterns if l[0] != '#'] - wlist[key] = prepare_allowlist(patterns) - return wlist + """Load allowlists under dirname. + + An allowlist ends with .allowlist. + + Args: + dirname: path to the dir. + + Returns: + A dictionary of 'filename' -> allowlist matcher. + """ + wlist = {} + for fn in glob.glob(os.path.join(dirname, "*.allowlist")): + key = os.path.splitext(os.path.basename(fn))[0] + with open(fn, "r", encoding="utf-8") as f: + patterns = f.read().splitlines() + patterns = [l for l in patterns if l != ""] + patterns = [l for l in patterns if l[0] != "#"] + wlist[key] = prepare_allowlist(patterns) + return wlist allowlists = load_allowlists(os.path.dirname(__file__)) diff --git a/debug_info_test/check_cus.py b/debug_info_test/check_cus.py index 41123259..dbf22d08 100644 --- a/debug_info_test/check_cus.py +++ b/debug_info_test/check_cus.py @@ -1,75 +1,78 @@ # -*- coding: utf-8 -*- -# Copyright 2018 The Chromium OS Authors. All rights reserved. +# Copyright 2018 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """check compile units.""" -from __future__ import print_function import os import subprocess import check_ngcc + cu_checks = [check_ngcc.not_by_gcc] def check_compile_unit(dso_path, producer, comp_path): - """check all compiler flags used to build the compile unit. + """check all compiler flags used to build the compile unit. - Args: - dso_path: path to the elf/dso. - producer: DW_AT_producer contains the compiler command line. - comp_path: DW_AT_comp_dir + DW_AT_name. + Args: + dso_path: path to the elf/dso. + producer: DW_AT_producer contains the compiler command line. + comp_path: DW_AT_comp_dir + DW_AT_name. - Returns: - A set of failed tests. - """ - failed = set() - for c in cu_checks: - if not c(dso_path, producer, comp_path): - failed.add(c.__module__) + Returns: + A set of failed tests. + """ + failed = set() + for c in cu_checks: + if not c(dso_path, producer, comp_path): + failed.add(c.__module__) - return failed + return failed def check_compile_units(dso_path): - """check all compile units in the given dso. - - Args: - dso_path: path to the dso. - - Returns: - True if everything looks fine otherwise False. - """ - - failed = set() - producer = '' - comp_path = '' - - readelf = subprocess.Popen( - ['llvm-dwarfdump', '--recurse-depth=0', dso_path], - stdout=subprocess.PIPE, - stderr=open(os.devnull, 'w'), - encoding='utf-8') - for l in readelf.stdout: - if 'DW_TAG_compile_unit' in l: - if producer: + """check all compile units in the given dso. + + Args: + dso_path: path to the dso. + + Returns: + True if everything looks fine otherwise False. + """ + + failed = set() + producer = "" + comp_path = "" + + readelf = subprocess.Popen( + ["llvm-dwarfdump", "--recurse-depth=0", dso_path], + stdout=subprocess.PIPE, + stderr=open(os.devnull, "w"), + encoding="utf-8", + ) + for l in readelf.stdout: + if "DW_TAG_compile_unit" in l: + if producer: + failed = failed.union( + check_compile_unit(dso_path, producer, comp_path) + ) + producer = "" + comp_path = "" + elif "DW_AT_producer" in l: + producer = l + elif "DW_AT_name" in l: + comp_path = os.path.join(comp_path, l.split(":")[-1].strip()) + elif "DW_AT_comp_dir" in l: + comp_path = os.path.join(l.split(":")[-1].strip(), comp_path) + if producer: failed = failed.union(check_compile_unit(dso_path, producer, comp_path)) - producer = '' - comp_path = '' - elif 'DW_AT_producer' in l: - producer = l - elif 'DW_AT_name' in l: - comp_path = os.path.join(comp_path, l.split(':')[-1].strip()) - elif 'DW_AT_comp_dir' in l: - comp_path = os.path.join(l.split(':')[-1].strip(), comp_path) - if producer: - failed = failed.union(check_compile_unit(dso_path, producer, comp_path)) - - if failed: - print('%s failed check: %s' % (dso_path, ' '.join(failed))) - return False - - return True + + if failed: + print("%s failed check: %s" % (dso_path, " ".join(failed))) + return False + + return True diff --git a/debug_info_test/check_exist.py b/debug_info_test/check_exist.py index 898dae45..863c591f 100644 --- a/debug_info_test/check_exist.py +++ b/debug_info_test/check_exist.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright 2018 The Chromium OS Authors. All rights reserved. +# Copyright 2018 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """check whether intended components exists in the given dso.""" -from __future__ import print_function import os import subprocess @@ -14,89 +13,90 @@ from allowlist import is_allowlisted def check_debug_info(dso_path, readelf_content): - """Check whether debug info section exists in the elf file. + """Check whether debug info section exists in the elf file. - Args: - dso_path: path to the dso. - readelf_content: debug info dumped by command readelf. + Args: + dso_path: path to the dso. + readelf_content: debug info dumped by command readelf. - Returns: - True if debug info section exists, otherwise False. - """ + Returns: + True if debug info section exists, otherwise False. + """ - # Return True if it is allowlisted - if is_allowlisted('exist_debug_info', dso_path): - return True + # Return True if it is allowlisted + if is_allowlisted("exist_debug_info", dso_path): + return True - for l in readelf_content: - if 'debug_info' in l: - return True - return False + for l in readelf_content: + if "debug_info" in l: + return True + return False def check_producer(dso_path, readelf_content): - """Check whether DW_AT_producer exists in each compile unit. - - Args: - dso_path: path to the dso. - readelf_content: debug info dumped by command readelf. - - Returns: - True if DW_AT_producer exists in each compile unit, otherwise False. - Notice: If no compile unit in DSO, also return True. - """ - - # Return True if it is allowlisted - if is_allowlisted('exist_producer', dso_path): - return True - - # Indicate if there is a producer under each cu - cur_producer = False - - first_cu = True - producer_exist = True - - for l in readelf_content: - if 'DW_TAG_compile_unit' in l: - if not first_cu and not cur_producer: + """Check whether DW_AT_producer exists in each compile unit. + + Args: + dso_path: path to the dso. + readelf_content: debug info dumped by command readelf. + + Returns: + True if DW_AT_producer exists in each compile unit, otherwise False. + Notice: If no compile unit in DSO, also return True. + """ + + # Return True if it is allowlisted + if is_allowlisted("exist_producer", dso_path): + return True + + # Indicate if there is a producer under each cu + cur_producer = False + + first_cu = True + producer_exist = True + + for l in readelf_content: + if "DW_TAG_compile_unit" in l: + if not first_cu and not cur_producer: + producer_exist = False + break + first_cu = False + cur_producer = False + elif "DW_AT_producer" in l: + cur_producer = True + + # Check whether last producer of compile unit exists in the elf, + # also return True if no cu in the DSO. + if not first_cu and not cur_producer: producer_exist = False - break - first_cu = False - cur_producer = False - elif 'DW_AT_producer' in l: - cur_producer = True - # Check whether last producer of compile unit exists in the elf, - # also return True if no cu in the DSO. - if not first_cu and not cur_producer: - producer_exist = False - - return producer_exist + return producer_exist def check_exist_all(dso_path): - """check whether intended components exists in the given dso. + """check whether intended components exists in the given dso. - Args: - dso_path: path to the dso. + Args: + dso_path: path to the dso. - Returns: - True if everything looks fine otherwise False. - """ + Returns: + True if everything looks fine otherwise False. + """ - readelf = subprocess.Popen( - ['llvm-dwarfdump', '--recurse-depth=0', dso_path], - stdout=subprocess.PIPE, - stderr=open(os.devnull, 'w'), - encoding='utf-8') - readelf_content = list(readelf.stdout) + readelf = subprocess.Popen( + ["llvm-dwarfdump", "--recurse-depth=0", dso_path], + stdout=subprocess.PIPE, + stderr=open(os.devnull, "w"), + encoding="utf-8", + ) + readelf_content = list(readelf.stdout) - exist_checks = [check_debug_info, check_producer] + exist_checks = [check_debug_info, check_producer] - for e in exist_checks: - if not e(dso_path, readelf_content): - check_failed = e.__module__ + ': ' + e.__name__ - print('%s failed check: %s' % (dso_path, check_failed)) - return False + for e in exist_checks: + if not e(dso_path, readelf_content): + check_failed = e.__module__ + ": " + e.__name__ + print("%s failed check: %s" % (dso_path, check_failed)) + return False - return True + return True diff --git a/debug_info_test/check_icf.py b/debug_info_test/check_icf.py index a46968e7..a717d81e 100644 --- a/debug_info_test/check_icf.py +++ b/debug_info_test/check_icf.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright 2018 The Chromium OS Authors. All rights reserved. +# Copyright 2018 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """check whether chrome was built with identical code folding.""" -from __future__ import print_function import os import re @@ -13,41 +12,43 @@ import subprocess def check_identical_code_folding(dso_path): - """check whether chrome was built with identical code folding. - - Args: - dso_path: path to the dso. - - Returns: - False if the dso is chrome and it was not built with icf, - True otherwise. - """ - - if not dso_path.endswith('/chrome.debug'): - return True - - # Run 'nm' on the chrome binary and read the output. - nm = subprocess.Popen(['nm', dso_path], - stdout=subprocess.PIPE, - stderr=open(os.devnull, 'w'), - encoding='utf-8') - nm_output, _ = nm.communicate() - - # Search for addresses of text symbols. - text_addresses = re.findall('^[0-9a-f]+[ ]+[tT] ', nm_output, re.MULTILINE) - - # Calculate number of text symbols in chrome binary. - num_text_addresses = len(text_addresses) - - # Calculate number of unique text symbols in chrome binary. - num_unique_text_addresses = len(set(text_addresses)) - - # Check that the number of duplicate symbols is at least 10,000. - # - https://crbug.com/813272#c18 - if num_text_addresses - num_unique_text_addresses >= 10000: - return True - - print('%s was not built with ICF' % dso_path) - print(' num_text_addresses = %d' % num_text_addresses) - print(' num_unique_text_addresses = %d' % num_unique_text_addresses) - return False + """check whether chrome was built with identical code folding. + + Args: + dso_path: path to the dso. + + Returns: + False if the dso is chrome and it was not built with icf, + True otherwise. + """ + + if not dso_path.endswith("/chrome.debug"): + return True + + # Run 'nm' on the chrome binary and read the output. + nm = subprocess.Popen( + ["nm", dso_path], + stdout=subprocess.PIPE, + stderr=open(os.devnull, "w"), + encoding="utf-8", + ) + nm_output, _ = nm.communicate() + + # Search for addresses of text symbols. + text_addresses = re.findall("^[0-9a-f]+[ ]+[tT] ", nm_output, re.MULTILINE) + + # Calculate number of text symbols in chrome binary. + num_text_addresses = len(text_addresses) + + # Calculate number of unique text symbols in chrome binary. + num_unique_text_addresses = len(set(text_addresses)) + + # Check that the number of duplicate symbols is at least 10,000. + # - https://crbug.com/813272#c18 + if num_text_addresses - num_unique_text_addresses >= 10000: + return True + + print("%s was not built with ICF" % dso_path) + print(" num_text_addresses = %d" % num_text_addresses) + print(" num_unique_text_addresses = %d" % num_unique_text_addresses) + return False diff --git a/debug_info_test/check_ngcc.py b/debug_info_test/check_ngcc.py index c86c220a..bbb58741 100644 --- a/debug_info_test/check_ngcc.py +++ b/debug_info_test/check_ngcc.py @@ -1,30 +1,29 @@ # -*- coding: utf-8 -*- -# Copyright 2018 The Chromium OS Authors. All rights reserved. +# Copyright 2018 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Check whether the compile unit is not built by gcc.""" -from __future__ import print_function from allowlist import is_allowlisted def not_by_gcc(dso_path, producer, comp_path): - """Check whether the compile unit is not built by gcc. + """Check whether the compile unit is not built by gcc. - Args: - dso_path: path to the elf/dso. - producer: DW_AT_producer contains the compiler command line. - comp_path: DW_AT_comp_dir + DW_AT_name. + Args: + dso_path: path to the elf/dso. + producer: DW_AT_producer contains the compiler command line. + comp_path: DW_AT_comp_dir + DW_AT_name. - Returns: - False if compiled by gcc otherwise True. - """ - if is_allowlisted('ngcc_comp_path', comp_path): - return True + Returns: + False if compiled by gcc otherwise True. + """ + if is_allowlisted("ngcc_comp_path", comp_path): + return True - if is_allowlisted('ngcc_dso_path', dso_path): - return True + if is_allowlisted("ngcc_dso_path", dso_path): + return True - return 'GNU C' not in producer + return "GNU C" not in producer diff --git a/debug_info_test/debug_info_test.py b/debug_info_test/debug_info_test.py index ae7e9f48..c324bf4c 100755 --- a/debug_info_test/debug_info_test.py +++ b/debug_info_test/debug_info_test.py @@ -1,67 +1,70 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2018 The Chromium OS Authors. All rights reserved. +# Copyright 2018 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Test for debug info.""" -from __future__ import print_function import os import subprocess import sys -import check_icf import check_cus import check_exist +import check_icf + elf_checks = [ - check_exist.check_exist_all, check_cus.check_compile_units, - check_icf.check_identical_code_folding + check_exist.check_exist_all, + check_cus.check_compile_units, + check_icf.check_identical_code_folding, ] def scanelf(root): - """Find ELFs in root. + """Find ELFs in root. - Args: - root: root dir to start with the search. + Args: + root: root dir to start with the search. - Returns: - Filenames of ELFs in root. - """ - p = subprocess.Popen(['scanelf', '-y', '-B', '-F', '%F', '-R', root], - stdout=subprocess.PIPE, - encoding='utf-8') - return [l.strip() for l in p.stdout] + Returns: + Filenames of ELFs in root. + """ + p = subprocess.Popen( + ["scanelf", "-y", "-B", "-F", "%F", "-R", root], + stdout=subprocess.PIPE, + encoding="utf-8", + ) + return [l.strip() for l in p.stdout] def Main(argv): - if len(argv) < 2: - print('usage: %s [file|dir]') - return 1 - - files = [] - cand = argv[1] - if os.path.isfile(cand): - files = [cand] - elif os.path.isdir(cand): - files = scanelf(cand) - else: - print('usage: %s [file|dir]') - return 1 - - failed = False - for f in files: - for c in elf_checks: - if not c(f): - failed = True - - if failed: - return 1 - return 0 - - -if __name__ == '__main__': - sys.exit(Main(sys.argv)) + if len(argv) < 2: + print("usage: %s [file|dir]") + return 1 + + files = [] + cand = argv[1] + if os.path.isfile(cand): + files = [cand] + elif os.path.isdir(cand): + files = scanelf(cand) + else: + print("usage: %s [file|dir]") + return 1 + + failed = False + for f in files: + for c in elf_checks: + if not c(f): + failed = True + + if failed: + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(Main(sys.argv)) |