symbolizer.py revision a8af9a7a2462b00e72deff99327bdb452a715277
1# Copyright 2012 Google Inc. All Rights Reserved.
2"""A script that symbolizes perf.data files."""
3import optparse
4import os
5import shutil
6from subprocess import call
7from subprocess import PIPE
8from subprocess import Popen
9from cros_utils import misc
10
11GSUTIL_CMD = 'gsutil cp gs://chromeos-image-archive/%s-release/%s/debug.tgz %s'
12TAR_CMD = 'tar -zxvf %s -C %s'
13PERF_BINARY = '/google/data/ro/projects/perf/perf'
14VMLINUX_FLAG = ' --vmlinux=/usr/lib/debug/boot/vmlinux'
15PERF_CMD = PERF_BINARY + ' report -i %s -n --symfs=%s' + VMLINUX_FLAG
16
17
18def main():
19  parser = optparse.OptionParser()
20  parser.add_option('--in', dest='in_dir')
21  parser.add_option('--out', dest='out_dir')
22  parser.add_option('--cache', dest='cache')
23  (opts, _) = parser.parse_args()
24  if not _ValidateOpts(opts):
25    return 1
26  else:
27    for filename in os.listdir(opts.in_dir):
28      try:
29        _DownloadSymbols(filename, opts.cache)
30        _PerfReport(filename, opts.in_dir, opts.out_dir, opts.cache)
31      except:
32        print 'Exception caught. Continuing...'
33  return 0
34
35
36def _ValidateOpts(opts):
37  """Ensures all directories exist, before attempting to populate."""
38  if not os.path.exists(opts.in_dir):
39    print "Input directory doesn't exist."
40    return False
41  if not os.path.exists(opts.out_dir):
42    print "Output directory doesn't exist. Creating it..."
43    os.makedirs(opts.out_dir)
44  if not os.path.exists(opts.cache):
45    print "Cache directory doesn't exist."
46    return False
47  return True
48
49
50def _ParseFilename(filename, canonical=False):
51  """Returns a tuple (key, time, board, lsb_version).
52     If canonical is True, instead returns (database_key, board, canonical_vers)
53     canonical_vers includes the revision string.
54  """
55  key, time, board, vers = filename.split('~')
56  if canonical:
57    vers = misc.GetChromeOSVersionFromLSBVersion(vers)
58  return (key, time, board, vers)
59
60
61def _FormReleaseDir(board, version):
62  return '%s-release~%s' % (board, version)
63
64
65def _DownloadSymbols(filename, cache):
66  """ Incrementally downloads appropriate symbols.
67      We store the downloads in cache, with each set of symbols in a TLD
68      named like cache/$board-release~$canonical_vers/usr/lib/debug
69  """
70  _, _, board, vers = _ParseFilename(filename, canonical=True)
71  tmp_suffix = '.tmp'
72
73  tarball_subdir = _FormReleaseDir(board, vers)
74  tarball_dir = os.path.join(cache, tarball_subdir)
75  tarball_path = os.path.join(tarball_dir, 'debug.tgz')
76
77  symbol_subdir = os.path.join('usr', 'lib')
78  symbol_dir = os.path.join(tarball_dir, symbol_subdir)
79
80  if os.path.isdir(symbol_dir):
81    print 'Symbol directory %s exists, skipping download.' % symbol_dir
82    return
83  else:
84    # First download using gsutil.
85    if not os.path.isfile(tarball_path):
86      download_cmd = GSUTIL_CMD % (board, vers, tarball_path + tmp_suffix)
87      print 'Downloading symbols for %s' % filename
88      print download_cmd
89      ret = call(download_cmd.split())
90      if ret != 0:
91        print 'gsutil returned non-zero error code: %s.' % ret
92        # Clean up the empty directory structures.
93        os.remove(tarball_path + tmp_suffix)
94        raise IOError
95
96      shutil.move(tarball_path + tmp_suffix, tarball_path)
97
98    # Next, untar the tarball.
99    os.makedirs(symbol_dir + tmp_suffix)
100    extract_cmd = TAR_CMD % (tarball_path, symbol_dir + tmp_suffix)
101    print 'Extracting symbols for %s' % filename
102    print extract_cmd
103    ret = call(extract_cmd.split())
104    if ret != 0:
105      print 'tar returned non-zero code: %s.' % ret
106      raise IOError
107    shutil.move(symbol_dir + tmp_suffix, symbol_dir)
108    os.remove(tarball_path)
109
110
111def _PerfReport(filename, in_dir, out_dir, cache):
112  """ Call perf report on the file, storing output to the output dir.
113      The output is currently stored as $out_dir/$filename
114  """
115  _, _, board, vers = _ParseFilename(filename, canonical=True)
116  symbol_cache_tld = _FormReleaseDir(board, vers)
117  input_file = os.path.join(in_dir, filename)
118  symfs = os.path.join(cache, symbol_cache_tld)
119  report_cmd = PERF_CMD % (input_file, symfs)
120  print 'Reporting.'
121  print report_cmd
122  report_proc = Popen(report_cmd.split(), stdout=PIPE)
123  outfile = open(os.path.join(out_dir, filename), 'w')
124  outfile.write(report_proc.stdout.read())
125  outfile.close()
126
127
128if __name__ == '__main__':
129  exit(main())
130