# Copyright 2012 Google Inc. All Rights Reserved.
"""A script that symbolizes perf.data files."""
import optparse
import os
import shutil
from subprocess import call
from subprocess import PIPE
from subprocess import Popen
from cros_utils import misc
GSUTIL_CMD = 'gsutil cp gs://chromeos-image-archive/%s-release/%s/debug.tgz %s'
TAR_CMD = 'tar -zxvf %s -C %s'
PERF_BINARY = '/google/data/ro/projects/perf/perf'
VMLINUX_FLAG = ' --vmlinux=/usr/lib/debug/boot/vmlinux'
PERF_CMD = PERF_BINARY + ' report -i %s -n --symfs=%s' + VMLINUX_FLAG
def main():
parser = optparse.OptionParser()
parser.add_option('--in', dest='in_dir')
parser.add_option('--out', dest='out_dir')
parser.add_option('--cache', dest='cache')
(opts, _) = parser.parse_args()
if not _ValidateOpts(opts):
return 1
else:
for filename in os.listdir(opts.in_dir):
try:
_DownloadSymbols(filename, opts.cache)
_PerfReport(filename, opts.in_dir, opts.out_dir, opts.cache)
except:
print 'Exception caught. Continuing...'
return 0
def _ValidateOpts(opts):
"""Ensures all directories exist, before attempting to populate."""
if not os.path.exists(opts.in_dir):
print "Input directory doesn't exist."
return False
if not os.path.exists(opts.out_dir):
print "Output directory doesn't exist. Creating it..."
os.makedirs(opts.out_dir)
if not os.path.exists(opts.cache):
print "Cache directory doesn't exist."
return False
return True
def _ParseFilename(filename, canonical=False):
"""Returns a tuple (key, time, board, lsb_version).
If canonical is True, instead returns (database_key, board, canonical_vers)
canonical_vers includes the revision string.
"""
key, time, board, vers = filename.split('~')
if canonical:
vers = misc.GetChromeOSVersionFromLSBVersion(vers)
return (key, time, board, vers)
def _FormReleaseDir(board, version):
return '%s-release~%s' % (board, version)
def _DownloadSymbols(filename, cache):
""" Incrementally downloads appropriate symbols.
We store the downloads in cache, with each set of symbols in a TLD
named like cache/$board-release~$canonical_vers/usr/lib/debug
"""
_, _, board, vers = _ParseFilename(filename, canonical=True)
tmp_suffix = '.tmp'
tarball_subdir = _FormReleaseDir(board, vers)
tarball_dir = os.path.join(cache, tarball_subdir)
tarball_path = os.path.join(tarball_dir, 'debug.tgz')
symbol_subdir = os.path.join('usr', 'lib')
symbol_dir = os.path.join(tarball_dir, symbol_subdir)
if os.path.isdir(symbol_dir):
print 'Symbol directory %s exists, skipping download.' % symbol_dir
return
else:
# First download using gsutil.
if not os.path.isfile(tarball_path):
download_cmd = GSUTIL_CMD % (board, vers, tarball_path + tmp_suffix)
print 'Downloading symbols for %s' % filename
print download_cmd
ret = call(download_cmd.split())
if ret != 0:
print 'gsutil returned non-zero error code: %s.' % ret
# Clean up the empty directory structures.
os.remove(tarball_path + tmp_suffix)
raise IOError
shutil.move(tarball_path + tmp_suffix, tarball_path)
# Next, untar the tarball.
os.makedirs(symbol_dir + tmp_suffix)
extract_cmd = TAR_CMD % (tarball_path, symbol_dir + tmp_suffix)
print 'Extracting symbols for %s' % filename
print extract_cmd
ret = call(extract_cmd.split())
if ret != 0:
print 'tar returned non-zero code: %s.' % ret
raise IOError
shutil.move(symbol_dir + tmp_suffix, symbol_dir)
os.remove(tarball_path)
def _PerfReport(filename, in_dir, out_dir, cache):
""" Call perf report on the file, storing output to the output dir.
The output is currently stored as $out_dir/$filename
"""
_, _, board, vers = _ParseFilename(filename, canonical=True)
symbol_cache_tld = _FormReleaseDir(board, vers)
input_file = os.path.join(in_dir, filename)
symfs = os.path.join(cache, symbol_cache_tld)
report_cmd = PERF_CMD % (input_file, symfs)
print 'Reporting.'
print report_cmd
report_proc = Popen(report_cmd.split(), stdout=PIPE)
outfile = open(os.path.join(out_dir, filename), 'w')
outfile.write(report_proc.stdout.read())
outfile.close()
if __name__ == '__main__':
exit(main())