divide_and_merge_profiles.py revision f81680c018729fd4499e1e200d04b48c4b90127c
1#!/usr/bin/python2.6
2#
3# Copyright 2011 Google Inc. All Rights Reserved.
4
5"""Script to divide and merge profiles."""
6
7import copy
8import optparse
9import os
10import pickle
11import re
12import sys
13import tempfile
14
15import build_chrome_browser
16import lock_machine
17import run_tests
18from utils import command_executer
19from utils import logger
20
21
22class ProfileMerger:
23  def __init__(self, inputs, output, chunk_size, merge_program, multipliers):
24    self._inputs = inputs
25    self._output = output
26    self._chunk_size = chunk_size
27    self._merge_program = merge_program
28    self._multipliers = multipliers
29    self._ce = command_executer.GetCommandExecuter()
30    self._l = logger.GetLogger()
31
32  def _GetFilesSetForInputDir(self, input_dir):
33    output_file = tempfile.mktemp()
34    command = "find %s -name '*.gcda' -o -name '*.imports' > %s" % (input_dir, output_file)
35    self._ce.RunCommand(command)
36    files = open(output_file, "r").read()
37    files_set = set([])
38    for f in files.splitlines():
39      stripped_file = f.replace(input_dir, "", 1)
40      stripped_file = stripped_file.lstrip("/")
41      files_set.add(stripped_file)
42    return files_set
43
44  def _PopulateFilesSet(self):
45    self._files_set = set([])
46    for i in self._inputs:
47      current_files_set = self._GetFilesSetForInputDir(i)
48      self._files_set.update(current_files_set)
49
50  def _GetSubset(self):
51    ret = []
52    for i in range(self._chunk_size):
53      if not self._files_set:
54        break
55      ret.append(self._files_set.pop())
56    return ret
57
58  def _CopyFilesTree(self, input_dir, files, output_dir):
59    for f in files:
60      src_file = os.path.join(input_dir, f)
61      dst_file = os.path.join(output_dir, f)
62      if not os.path.isdir(os.path.dirname(dst_file)):
63        command = "mkdir -p %s" % os.path.dirname(dst_file)
64        self._ce.RunCommand(command)
65      command = "cp %s %s" % (src_file, dst_file)
66      self._ce.RunCommand(command)
67
68  def _DoChunkMerge(self, current_files):
69    temp_dirs = []
70    for i in self._inputs:
71      temp_dir = tempfile.mkdtemp()
72      temp_dirs.append(temp_dir)
73      self._CopyFilesTree(i, current_files, temp_dir)
74    # Now do the merge.
75    command = ("%s --inputs=%s --output=%s" %
76               (self._merge_program,
77                ",".join(temp_dirs),
78                self._output))
79    if self._multipliers:
80      command = ("%s --multipliers=%s" %
81                 (command, self._multipliers))
82    ret = self._ce.RunCommand(command)
83    assert ret == 0, "%s command failed!" % command
84    for temp_dir in temp_dirs:
85      command = "rm -rf %s" % temp_dir
86      self._ce.RunCommand(command)
87
88  def DoMerge(self):
89    self._PopulateFilesSet()
90    while True:
91      current_files = self._GetSubset()
92      if not current_files:
93        break
94      self._DoChunkMerge(current_files)
95
96
97def Main(argv):
98  """The main function."""
99  # Common initializations
100###  command_executer.InitCommandExecuter(True)
101  command_executer.InitCommandExecuter()
102  l = logger.GetLogger()
103  ce = command_executer.GetCommandExecuter()
104  parser = optparse.OptionParser()
105  parser.add_option("--inputs",
106                    dest="inputs",
107                    help="Comma-separated input profile directories to merge.")
108  parser.add_option("--output",
109                    dest="output",
110                    help="Output profile directory.")
111  parser.add_option("--chunk_size",
112                    dest="chunk_size",
113                    default="50",
114                    help="Chunk size to divide up the profiles into.")
115  parser.add_option("--merge_program",
116                    dest="merge_program",
117                    default="/home/xur/bin/profile_merge_v15.par",
118                    help="Merge program to use to do the actual merge.")
119  parser.add_option("--multipliers",
120                    dest="multipliers",
121                    help="multipliers to use when merging. (optional)")
122
123  options, _ = parser.parse_args(argv)
124
125  if not all([options.inputs,
126              options.output,]):
127    l.LogError("Must supply --inputs and --output")
128    return 1
129
130  try:
131    pm = ProfileMerger(options.inputs.split(","), options.output,
132                       int(options.chunk_size), options.merge_program,
133                       options.multipliers)
134    pm.DoMerge()
135    retval = 0
136  except:
137    retval = 1
138  finally:
139    print "My work is done..."
140  return retval
141
142
143if __name__ == "__main__":
144  retval = Main(sys.argv)
145  sys.exit(retval)
146