diff options
Diffstat (limited to 'debug_info_test')
-rw-r--r-- | debug_info_test/check_cus.py | 102 | ||||
-rw-r--r-- | debug_info_test/check_exist.py | 142 | ||||
-rw-r--r-- | debug_info_test/check_icf.py | 82 | ||||
-rw-r--r-- | debug_info_test/check_ngcc.py | 34 | ||||
-rwxr-xr-x | debug_info_test/debug_info_test.py | 86 | ||||
-rw-r--r-- | debug_info_test/whitelist.py | 77 |
6 files changed, 287 insertions, 236 deletions
diff --git a/debug_info_test/check_cus.py b/debug_info_test/check_cus.py index f68fe9cb..d3cd6365 100644 --- a/debug_info_test/check_cus.py +++ b/debug_info_test/check_cus.py @@ -1,7 +1,12 @@ +# -*- coding: utf-8 -*- # Copyright 2018 The Chromium OS Authors. All rights reserved. # 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 @@ -9,59 +14,62 @@ 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 - Return: - True if everything looks fine otherwise False. - """ - - failed = set() - producer = '' - comp_path = '' - - readelf = subprocess.Popen(['readelf', '--debug-dump=info', - '--dwarf-depth=1', dso_path], - stdout=subprocess.PIPE, - stderr=open(os.devnull, 'w')) - 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: + """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( + ['readelf', '--debug-dump=info', '--dwarf-depth=1', 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)) - if failed: - print('%s failed check: %s' % (dso_path, ' '.join(failed))) - return False + if failed: + print('%s failed check: %s' % (dso_path, ' '.join(failed))) + return False - return True + return True diff --git a/debug_info_test/check_exist.py b/debug_info_test/check_exist.py index 5e7cce19..dbb89127 100644 --- a/debug_info_test/check_exist.py +++ b/debug_info_test/check_exist.py @@ -1,90 +1,102 @@ +# -*- coding: utf-8 -*- # Copyright 2018 The Chromium OS Authors. All rights reserved. # 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 from whitelist import is_whitelisted + 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: - readelf: 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 whitelisted + if is_whitelisted('exist_debug_info', dso_path): + return True - # Return True if it is whitelisted - if is_whitelisted('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: - readelf: 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 whitelisted - if is_whitelisted('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: + """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 whitelisted + if is_whitelisted('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 + + 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 - Return: - True if everything looks fine otherwise False. - """ + Args: + dso_path: path to the dso. - readelf = subprocess.Popen(['readelf', '--debug-dump=info', - '--dwarf-depth=1', dso_path], - stdout=subprocess.PIPE, - stderr=open(os.devnull, 'w')) - readelf_content = list(readelf.stdout) + Returns: + True if everything looks fine otherwise False. + """ - exist_checks = [check_debug_info, check_producer] + readelf = subprocess.Popen( + ['readelf', '--debug-dump=info', '--dwarf-depth=1', dso_path], + stdout=subprocess.PIPE, + stderr=open(os.devnull, 'w'), + encoding='utf-8') + readelf_content = list(readelf.stdout) - 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 + exist_checks = [check_debug_info, check_producer] - return True + 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 diff --git a/debug_info_test/check_icf.py b/debug_info_test/check_icf.py index 4ac67dbd..a46968e7 100644 --- a/debug_info_test/check_icf.py +++ b/debug_info_test/check_icf.py @@ -1,47 +1,53 @@ +# -*- coding: utf-8 -*- # Copyright 2018 The Chromium OS Authors. All rights reserved. # 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 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 - Return: - 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')) - 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 eecbb85e..501bb988 100644 --- a/debug_info_test/check_ngcc.py +++ b/debug_info_test/check_ngcc.py @@ -1,26 +1,30 @@ +# -*- coding: utf-8 -*- # Copyright 2018 The Chromium OS Authors. All rights reserved. # 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 whitelist import is_whitelisted -def not_by_gcc(dso_path, producer, comp_path): - """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 +def not_by_gcc(dso_path, producer, comp_path): + """Check whether the compile unit is not built by gcc. - Returns: - False if compiled by gcc otherwise True - """ - if is_whitelisted('ngcc_comp_path', comp_path): - return True + 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. - if is_whitelisted('ngcc_dso_path', dso_path): - return True + Returns: + False if compiled by gcc otherwise True. + """ + if is_whitelisted('ngcc_comp_path', comp_path): + return True - if 'GNU C' in producer: - return False + if is_whitelisted('ngcc_dso_path', dso_path): return True + + 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 4839e69c..ae7e9f48 100755 --- a/debug_info_test/debug_info_test.py +++ b/debug_info_test/debug_info_test.py @@ -1,9 +1,13 @@ -#!/usr/bin/python2 - +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- # Copyright 2018 The Chromium OS Authors. All rights reserved. # 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 @@ -12,46 +16,52 @@ import check_icf import check_cus import check_exist -elf_checks = [check_exist.check_exist_all, - check_cus.check_compile_units, - check_icf.check_identical_code_folding] +elf_checks = [ + 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. + + 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] - 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) - 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 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)) + sys.exit(Main(sys.argv)) diff --git a/debug_info_test/whitelist.py b/debug_info_test/whitelist.py index 383fcc3d..b53387a8 100644 --- a/debug_info_test/whitelist.py +++ b/debug_info_test/whitelist.py @@ -1,11 +1,17 @@ +# -*- coding: utf-8 -*- # Copyright 2018 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +"""Whitelist functions.""" + +from __future__ import print_function + import os import glob import re + # Matching a string of length m in an NFA of size n is O(mn^2), but the # performance also depends largely on the implementation. It appears to be fast # enough according to the tests. @@ -13,45 +19,50 @@ import re # The performance bottleneck of this script is readelf. Unless this becomes # slower than readelf, don't waste time here. def is_whitelisted(list_name, pattern): - """chech whether the given pattern is specified in the whitelist. + """Check whether the given pattern is specified in the whitelist. + + Args: + list_name: name of the whitelist. + pattern: the target string. + + Returns: + True if matched otherwise False. + """ + return pattern and whitelists[list_name].match(pattern) - Args: - list_name: name of the whitelist - pattern: the target string - Returns: - True if matched otherwise False - """ - return pattern and whitelists[list_name].match(pattern) def prepare_whitelist(patterns): - """Join and compile the re patterns. + """Join and compile the re patterns. + + Args: + patterns: regex patterns. + + Returns: + A compiled re object. + """ + return re.compile('|'.join(patterns)) - Args: - patterns: regex patterns. - Return: - A compiled re object - """ - return re.compile('|'.join(patterns)) def load_whitelists(dirname): - """Load whitelists under dirname. - - A whitelist ends with .whitelist. - - Args: - dirname: path to the dir. - Returns: - A dictionary of 'filename' -> whitelist matcher. - """ - wlist = {} - for fn in glob.glob(os.path.join(dirname, '*.whitelist')): - key = os.path.splitext(os.path.basename(fn))[0] - with open(fn, 'r') 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_whitelist(patterns) - return wlist + """Load whitelists under dirname. + + A whitelist ends with .whitelist. + + Args: + dirname: path to the dir. + + Returns: + A dictionary of 'filename' -> whitelist matcher. + """ + wlist = {} + for fn in glob.glob(os.path.join(dirname, '*.whitelist')): + 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_whitelist(patterns) + return wlist whitelists = load_whitelists(os.path.dirname(__file__)) |