| #!/usr/bin/env python |
| # |
| # Copyright (C) 2016 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| """Builds a database of symbol version introductions.""" |
| import argparse |
| import json |
| import logging |
| import os |
| |
| |
| THIS_DIR = os.path.realpath(os.path.dirname(__file__)) |
| |
| |
| ALL_ARCHITECTURES = ( |
| 'arm', |
| 'arm64', |
| 'mips', |
| 'mips64', |
| 'x86', |
| 'x86_64', |
| ) |
| |
| |
| def logger(): |
| """Returns the default logger for this module.""" |
| return logging.getLogger(__name__) |
| |
| |
| def get_platform_versions(): |
| """Returns a list of the platform versions we have data for.""" |
| versions = [] |
| platforms_dir = os.path.join(THIS_DIR, 'platforms') |
| logger().debug('Getting platform versions from %s', platforms_dir) |
| for name in os.listdir(platforms_dir): |
| if name.startswith('android-'): |
| versions.append(int(name.split('-')[1])) |
| return versions |
| |
| |
| def add_symbols(symbols, symbol_file_path, version, arch, is_var): |
| """Adds symbols from a file to the symbol dict.""" |
| with open(symbol_file_path) as symbol_file: |
| names = symbol_file.readlines() |
| |
| for name in names: |
| name = name.strip() |
| if not name: |
| continue |
| introduced_tag = 'introduced-' + arch |
| if name in symbols: |
| assert symbols[name]['is_var'] == is_var |
| if introduced_tag in symbols[name]: |
| continue |
| symbols[name][introduced_tag] = version |
| else: |
| symbols[name] = {} |
| symbols[name]['is_var'] = is_var |
| symbols[name][introduced_tag] = version |
| |
| |
| def build_symbol_db(lib_name): |
| """Returns a dict of symbols and their version information. |
| |
| Args: |
| lib_name: Name of the library to return file mapping for. |
| |
| Returns: dict of symbol information in the following format: |
| { |
| "symbol_name": { |
| "is_var": "true", |
| "introduced-arm": 9, |
| "introduced-x86": 14, |
| "introduced-mips": 16, |
| "introduced-arm64": 21, |
| "introduced-mips64": 21, |
| "introduced-x86_64": 21, |
| }, |
| ... |
| } |
| """ |
| symbols = {} |
| versions = sorted(get_platform_versions()) |
| for version in versions: |
| for arch in ALL_ARCHITECTURES: |
| symbols_dir = os.path.join( |
| THIS_DIR, 'platforms', 'android-' + str(version), |
| 'arch-' + arch, 'symbols') |
| if not os.path.exists(symbols_dir): |
| logger().debug('Skipping non-existent %s', symbols_dir) |
| continue |
| |
| logger().info('Processing android-%d arch-%s', version, arch) |
| |
| funcs_file_name = lib_name + '.so.functions.txt' |
| funcs_file = os.path.join(symbols_dir, funcs_file_name) |
| if os.path.exists(funcs_file): |
| add_symbols(symbols, funcs_file, version, arch, is_var='false') |
| |
| vars_file_name = lib_name + '.so.variables.txt' |
| vars_file = os.path.join(symbols_dir, vars_file_name) |
| if os.path.exists(vars_file): |
| add_symbols(symbols, vars_file, version, arch, is_var='true') |
| return symbols |
| |
| |
| def parse_args(): |
| """Returns parsed command line arguments.""" |
| parser = argparse.ArgumentParser() |
| |
| parser.add_argument('-v', '--verbose', action='count', default=0) |
| |
| parser.add_argument( |
| 'library_name', metavar='LIBRARY_NAME', |
| help='Name of the library to create a database for.') |
| |
| return parser.parse_args() |
| |
| |
| def main(): |
| """Program entry point.""" |
| args = parse_args() |
| os.chdir(THIS_DIR) |
| |
| verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG) |
| verbosity = args.verbose |
| if verbosity > 2: |
| verbosity = 2 |
| logging.basicConfig(level=verbose_map[verbosity]) |
| |
| symbol_db = build_symbol_db(args.library_name) |
| with open(args.library_name + '.so.json', 'w') as db_file: |
| json.dump(symbol_db, db_file, indent=4, separators=(',', ': '), |
| sort_keys=True) |
| |
| |
| if __name__ == '__main__': |
| main() |