blob: 4c40b4e961ef2a0159ff161f0bf5dc5a6f1a4882 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import logging
import os
import re
import shutil
import subprocess
import sys
import tempfile
from proc_maps import ProcMaps
def _dump_command_result(command, output_dir_path, basename, suffix, log):
handle_out, filename_out = tempfile.mkstemp(
suffix=suffix, prefix=basename + '.', dir=output_dir_path)
handle_err, filename_err = tempfile.mkstemp(
suffix=suffix + '.err', prefix=basename + '.', dir=output_dir_path)
error = False
try:
subprocess.check_call(
command, stdout=handle_out, stderr=handle_err, shell=True)
except:
error = True
finally:
os.close(handle_err)
os.close(handle_out)
if os.path.exists(filename_err):
if log.getEffectiveLevel() <= logging.DEBUG:
with open(filename_err, 'r') as f:
for line in f:
log.debug(line.rstrip())
os.remove(filename_err)
if os.path.exists(filename_out) and (
os.path.getsize(filename_out) == 0 or error):
os.remove(filename_out)
return None
if not os.path.exists(filename_out):
return None
return filename_out
def prepare_symbol_info(maps_path, output_dir_path=None, loglevel=logging.WARN):
log = logging.getLogger('prepare_symbol_info')
log.setLevel(loglevel)
handler = logging.StreamHandler()
handler.setLevel(loglevel)
formatter = logging.Formatter('%(message)s')
handler.setFormatter(formatter)
log.addHandler(handler)
if not output_dir_path:
matched = re.match('^(.*)\.maps$', os.path.basename(maps_path))
if matched:
output_dir_path = matched.group(1) + '.pre'
if not output_dir_path:
matched = re.match('^/proc/(.*)/maps$', os.path.realpath(maps_path))
if matched:
output_dir_path = matched.group(1) + '.pre'
if not output_dir_path:
output_dir_prefix = os.path.basename(maps_path) + '.pre'
# TODO(dmikurube): Find another candidate for output_dir_path.
log.info('Data for profiling will be collected in "%s".' % output_dir_path)
output_dir_path_exists = False
if os.path.exists(output_dir_path):
if os.path.isdir(output_dir_path) and not os.listdir(output_dir_path):
log.warn('Using an empty directory existing at "%s".' % output_dir_path)
else:
log.warn('A file or a directory exists at "%s".' % output_dir_path)
output_dir_path_exists = True
else:
log.info('Creating a new directory at "%s".' % output_dir_path)
os.mkdir(output_dir_path)
if output_dir_path_exists:
return 1
shutil.copyfile(maps_path, os.path.join(output_dir_path, 'maps'))
with open(maps_path, mode='r') as f:
maps = ProcMaps.load(f)
log.debug('Listing up symbols.')
files = {}
for entry in maps.iter(ProcMaps.executable):
log.debug(' %016x-%016x +%06x %s' % (
entry.begin, entry.end, entry.offset, entry.name))
nm_filename = _dump_command_result(
'nm -n --format bsd %s | c++filt' % entry.name,
output_dir_path, os.path.basename(entry.name), '.nm', log)
if not nm_filename:
continue
readelf_e_filename = _dump_command_result(
'readelf -eW %s' % entry.name,
output_dir_path, os.path.basename(entry.name), '.readelf-e', log)
if not readelf_e_filename:
continue
files[entry.name] = {}
files[entry.name]['nm'] = {
'file': os.path.basename(nm_filename),
'format': 'bsd',
'mangled': False}
files[entry.name]['readelf-e'] = {
'file': os.path.basename(readelf_e_filename)}
with open(os.path.join(output_dir_path, 'files.json'), 'w') as f:
json.dump(files, f, indent=2, sort_keys=True)
log.info('Collected symbol information at "%s".' % output_dir_path)
return 0
def main():
if not sys.platform.startswith('linux'):
sys.stderr.write('This script work only on Linux.')
return 1
if len(sys.argv) < 2:
sys.stderr.write("""Usage:
%s /path/to/maps [/path/to/output_data_dir/]
""" % sys.argv[0])
return 1
elif len(sys.argv) == 2:
sys.exit(prepare_symbol_info(sys.argv[1], loglevel=logging.INFO))
else:
sys.exit(prepare_symbol_info(sys.argv[1], sys.argv[2],
loglevel=logging.INFO))
return 0
if __name__ == '__main__':
sys.exit(main())