xref: /openbsd-src/gnu/llvm/clang/utils/perf-training/perf-helper.py (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick#===- perf-helper.py - Clang Python Bindings -----------------*- python -*--===#
2e5dd7070Spatrick#
3e5dd7070Spatrick# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick# See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick#
7e5dd7070Spatrick#===------------------------------------------------------------------------===#
8e5dd7070Spatrick
9e5dd7070Spatrickfrom __future__ import absolute_import, division, print_function
10e5dd7070Spatrick
11e5dd7070Spatrickimport sys
12e5dd7070Spatrickimport os
13e5dd7070Spatrickimport subprocess
14e5dd7070Spatrickimport argparse
15e5dd7070Spatrickimport time
16e5dd7070Spatrickimport bisect
17e5dd7070Spatrickimport shlex
18e5dd7070Spatrickimport tempfile
19e5dd7070Spatrick
20e5dd7070Spatricktest_env = { 'PATH'    : os.environ['PATH'] }
21e5dd7070Spatrick
22e5dd7070Spatrickdef findFilesWithExtension(path, extension):
23e5dd7070Spatrick  filenames = []
24e5dd7070Spatrick  for root, dirs, files in os.walk(path):
25e5dd7070Spatrick    for filename in files:
26*12c85518Srobert      if filename.endswith(f".{extension}"):
27e5dd7070Spatrick        filenames.append(os.path.join(root, filename))
28e5dd7070Spatrick  return filenames
29e5dd7070Spatrick
30e5dd7070Spatrickdef clean(args):
31e5dd7070Spatrick  if len(args) != 2:
32e5dd7070Spatrick    print('Usage: %s clean <path> <extension>\n' % __file__ +
33e5dd7070Spatrick      '\tRemoves all files with extension from <path>.')
34e5dd7070Spatrick    return 1
35e5dd7070Spatrick  for filename in findFilesWithExtension(args[0], args[1]):
36e5dd7070Spatrick    os.remove(filename)
37e5dd7070Spatrick  return 0
38e5dd7070Spatrick
39e5dd7070Spatrickdef merge(args):
40e5dd7070Spatrick  if len(args) != 3:
41*12c85518Srobert    print('Usage: %s merge <llvm-profdata> <output> <path>\n' % __file__ +
42e5dd7070Spatrick      '\tMerges all profraw files from path into output.')
43e5dd7070Spatrick    return 1
44e5dd7070Spatrick  cmd = [args[0], 'merge', '-o', args[1]]
45e5dd7070Spatrick  cmd.extend(findFilesWithExtension(args[2], "profraw"))
46e5dd7070Spatrick  subprocess.check_call(cmd)
47e5dd7070Spatrick  return 0
48e5dd7070Spatrick
49*12c85518Srobertdef merge_fdata(args):
50*12c85518Srobert  if len(args) != 3:
51*12c85518Srobert    print('Usage: %s merge-fdata <merge-fdata> <output> <path>\n' % __file__ +
52*12c85518Srobert      '\tMerges all fdata files from path into output.')
53*12c85518Srobert    return 1
54*12c85518Srobert  cmd = [args[0], '-o', args[1]]
55*12c85518Srobert  cmd.extend(findFilesWithExtension(args[2], "fdata"))
56*12c85518Srobert  subprocess.check_call(cmd)
57*12c85518Srobert  return 0
58*12c85518Srobert
59e5dd7070Spatrickdef dtrace(args):
60e5dd7070Spatrick  parser = argparse.ArgumentParser(prog='perf-helper dtrace',
61e5dd7070Spatrick    description='dtrace wrapper for order file generation')
62e5dd7070Spatrick  parser.add_argument('--buffer-size', metavar='size', type=int, required=False,
63e5dd7070Spatrick    default=1, help='dtrace buffer size in MB (default 1)')
64e5dd7070Spatrick  parser.add_argument('--use-oneshot', required=False, action='store_true',
65e5dd7070Spatrick    help='Use dtrace\'s oneshot probes')
66e5dd7070Spatrick  parser.add_argument('--use-ustack', required=False, action='store_true',
67e5dd7070Spatrick    help='Use dtrace\'s ustack to print function names')
68e5dd7070Spatrick  parser.add_argument('--cc1', required=False, action='store_true',
69e5dd7070Spatrick    help='Execute cc1 directly (don\'t profile the driver)')
70e5dd7070Spatrick  parser.add_argument('cmd', nargs='*', help='')
71e5dd7070Spatrick
72e5dd7070Spatrick  # Use python's arg parser to handle all leading option arguments, but pass
73e5dd7070Spatrick  # everything else through to dtrace
74e5dd7070Spatrick  first_cmd = next(arg for arg in args if not arg.startswith("--"))
75e5dd7070Spatrick  last_arg_idx = args.index(first_cmd)
76e5dd7070Spatrick
77e5dd7070Spatrick  opts = parser.parse_args(args[:last_arg_idx])
78e5dd7070Spatrick  cmd = args[last_arg_idx:]
79e5dd7070Spatrick
80e5dd7070Spatrick  if opts.cc1:
81e5dd7070Spatrick    cmd = get_cc1_command_for_args(cmd, test_env)
82e5dd7070Spatrick
83e5dd7070Spatrick  if opts.use_oneshot:
84e5dd7070Spatrick      target = "oneshot$target:::entry"
85e5dd7070Spatrick  else:
86e5dd7070Spatrick      target = "pid$target:::entry"
87e5dd7070Spatrick  predicate = '%s/probemod=="%s"/' % (target, os.path.basename(cmd[0]))
88e5dd7070Spatrick  log_timestamp = 'printf("dtrace-TS: %d\\n", timestamp)'
89e5dd7070Spatrick  if opts.use_ustack:
90e5dd7070Spatrick      action = 'ustack(1);'
91e5dd7070Spatrick  else:
92e5dd7070Spatrick      action = 'printf("dtrace-Symbol: %s\\n", probefunc);'
93e5dd7070Spatrick  dtrace_script = "%s { %s; %s }" % (predicate, log_timestamp, action)
94e5dd7070Spatrick
95e5dd7070Spatrick  dtrace_args = []
96e5dd7070Spatrick  if not os.geteuid() == 0:
97e5dd7070Spatrick    print(
98e5dd7070Spatrick      'Script must be run as root, or you must add the following to your sudoers:'
99e5dd7070Spatrick      + '%%admin ALL=(ALL) NOPASSWD: /usr/sbin/dtrace')
100e5dd7070Spatrick    dtrace_args.append("sudo")
101e5dd7070Spatrick
102e5dd7070Spatrick  dtrace_args.extend((
103e5dd7070Spatrick      'dtrace', '-xevaltime=exec',
104e5dd7070Spatrick      '-xbufsize=%dm' % (opts.buffer_size),
105e5dd7070Spatrick      '-q', '-n', dtrace_script,
106e5dd7070Spatrick      '-c', ' '.join(cmd)))
107e5dd7070Spatrick
108e5dd7070Spatrick  if sys.platform == "darwin":
109e5dd7070Spatrick    dtrace_args.append('-xmangled')
110e5dd7070Spatrick
111e5dd7070Spatrick  start_time = time.time()
112e5dd7070Spatrick
113e5dd7070Spatrick  with open("%d.dtrace" % os.getpid(), "w") as f:
114e5dd7070Spatrick    f.write("### Command: %s" % dtrace_args)
115e5dd7070Spatrick    subprocess.check_call(dtrace_args, stdout=f, stderr=subprocess.PIPE)
116e5dd7070Spatrick
117e5dd7070Spatrick  elapsed = time.time() - start_time
118e5dd7070Spatrick  print("... data collection took %.4fs" % elapsed)
119e5dd7070Spatrick
120e5dd7070Spatrick  return 0
121e5dd7070Spatrick
122e5dd7070Spatrickdef get_cc1_command_for_args(cmd, env):
123e5dd7070Spatrick  # Find the cc1 command used by the compiler. To do this we execute the
124e5dd7070Spatrick  # compiler with '-###' to figure out what it wants to do.
125e5dd7070Spatrick  cmd = cmd + ['-###']
126e5dd7070Spatrick  cc_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, env=env, universal_newlines=True).strip()
127e5dd7070Spatrick  cc_commands = []
128e5dd7070Spatrick  for ln in cc_output.split('\n'):
129e5dd7070Spatrick      # Filter out known garbage.
130e5dd7070Spatrick      if (ln == 'Using built-in specs.' or
131e5dd7070Spatrick          ln.startswith('Configured with:') or
132e5dd7070Spatrick          ln.startswith('Target:') or
133e5dd7070Spatrick          ln.startswith('Thread model:') or
134e5dd7070Spatrick          ln.startswith('InstalledDir:') or
135e5dd7070Spatrick          ln.startswith('LLVM Profile Note') or
136ec727ea7Spatrick          ln.startswith(' (in-process)') or
137e5dd7070Spatrick          ' version ' in ln):
138e5dd7070Spatrick          continue
139e5dd7070Spatrick      cc_commands.append(ln)
140e5dd7070Spatrick
141e5dd7070Spatrick  if len(cc_commands) != 1:
142e5dd7070Spatrick      print('Fatal error: unable to determine cc1 command: %r' % cc_output)
143e5dd7070Spatrick      exit(1)
144e5dd7070Spatrick
145e5dd7070Spatrick  cc1_cmd = shlex.split(cc_commands[0])
146e5dd7070Spatrick  if not cc1_cmd:
147e5dd7070Spatrick      print('Fatal error: unable to determine cc1 command: %r' % cc_output)
148e5dd7070Spatrick      exit(1)
149e5dd7070Spatrick
150e5dd7070Spatrick  return cc1_cmd
151e5dd7070Spatrick
152e5dd7070Spatrickdef cc1(args):
153e5dd7070Spatrick  parser = argparse.ArgumentParser(prog='perf-helper cc1',
154e5dd7070Spatrick    description='cc1 wrapper for order file generation')
155e5dd7070Spatrick  parser.add_argument('cmd', nargs='*', help='')
156e5dd7070Spatrick
157e5dd7070Spatrick  # Use python's arg parser to handle all leading option arguments, but pass
158e5dd7070Spatrick  # everything else through to dtrace
159e5dd7070Spatrick  first_cmd = next(arg for arg in args if not arg.startswith("--"))
160e5dd7070Spatrick  last_arg_idx = args.index(first_cmd)
161e5dd7070Spatrick
162e5dd7070Spatrick  opts = parser.parse_args(args[:last_arg_idx])
163e5dd7070Spatrick  cmd = args[last_arg_idx:]
164e5dd7070Spatrick
165e5dd7070Spatrick  # clear the profile file env, so that we don't generate profdata
166e5dd7070Spatrick  # when capturing the cc1 command
167e5dd7070Spatrick  cc1_env = test_env
168e5dd7070Spatrick  cc1_env["LLVM_PROFILE_FILE"] = os.devnull
169e5dd7070Spatrick  cc1_cmd = get_cc1_command_for_args(cmd, cc1_env)
170e5dd7070Spatrick
171e5dd7070Spatrick  subprocess.check_call(cc1_cmd)
172e5dd7070Spatrick  return 0
173e5dd7070Spatrick
174e5dd7070Spatrickdef parse_dtrace_symbol_file(path, all_symbols, all_symbols_set,
175e5dd7070Spatrick                             missing_symbols, opts):
176e5dd7070Spatrick  def fix_mangling(symbol):
177e5dd7070Spatrick    if sys.platform == "darwin":
178e5dd7070Spatrick      if symbol[0] != '_' and symbol != 'start':
179e5dd7070Spatrick          symbol = '_' + symbol
180e5dd7070Spatrick    return symbol
181e5dd7070Spatrick
182e5dd7070Spatrick  def get_symbols_with_prefix(symbol):
183e5dd7070Spatrick    start_index = bisect.bisect_left(all_symbols, symbol)
184e5dd7070Spatrick    for s in all_symbols[start_index:]:
185e5dd7070Spatrick      if not s.startswith(symbol):
186e5dd7070Spatrick        break
187e5dd7070Spatrick      yield s
188e5dd7070Spatrick
189e5dd7070Spatrick  # Extract the list of symbols from the given file, which is assumed to be
190e5dd7070Spatrick  # the output of a dtrace run logging either probefunc or ustack(1) and
191e5dd7070Spatrick  # nothing else. The dtrace -xdemangle option needs to be used.
192e5dd7070Spatrick  #
193e5dd7070Spatrick  # This is particular to OS X at the moment, because of the '_' handling.
194e5dd7070Spatrick  with open(path) as f:
195e5dd7070Spatrick    current_timestamp = None
196e5dd7070Spatrick    for ln in f:
197e5dd7070Spatrick      # Drop leading and trailing whitespace.
198e5dd7070Spatrick      ln = ln.strip()
199e5dd7070Spatrick      if not ln.startswith("dtrace-"):
200e5dd7070Spatrick        continue
201e5dd7070Spatrick
202e5dd7070Spatrick      # If this is a timestamp specifier, extract it.
203e5dd7070Spatrick      if ln.startswith("dtrace-TS: "):
204e5dd7070Spatrick        _,data = ln.split(': ', 1)
205e5dd7070Spatrick        if not data.isdigit():
206e5dd7070Spatrick          print("warning: unrecognized timestamp line %r, ignoring" % ln,
207e5dd7070Spatrick            file=sys.stderr)
208e5dd7070Spatrick          continue
209e5dd7070Spatrick        current_timestamp = int(data)
210e5dd7070Spatrick        continue
211e5dd7070Spatrick      elif ln.startswith("dtrace-Symbol: "):
212e5dd7070Spatrick
213e5dd7070Spatrick        _,ln = ln.split(': ', 1)
214e5dd7070Spatrick        if not ln:
215e5dd7070Spatrick          continue
216e5dd7070Spatrick
217e5dd7070Spatrick        # If there is a '`' in the line, assume it is a ustack(1) entry in
218e5dd7070Spatrick        # the form of <modulename>`<modulefunc>, where <modulefunc> is never
219e5dd7070Spatrick        # truncated (but does need the mangling patched).
220e5dd7070Spatrick        if '`' in ln:
221e5dd7070Spatrick          yield (current_timestamp, fix_mangling(ln.split('`',1)[1]))
222e5dd7070Spatrick          continue
223e5dd7070Spatrick
224e5dd7070Spatrick        # Otherwise, assume this is a probefunc printout. DTrace on OS X
225e5dd7070Spatrick        # seems to have a bug where it prints the mangled version of symbols
226e5dd7070Spatrick        # which aren't C++ mangled. We just add a '_' to anything but start
227e5dd7070Spatrick        # which doesn't already have a '_'.
228e5dd7070Spatrick        symbol = fix_mangling(ln)
229e5dd7070Spatrick
230e5dd7070Spatrick        # If we don't know all the symbols, or the symbol is one of them,
231e5dd7070Spatrick        # just return it.
232e5dd7070Spatrick        if not all_symbols_set or symbol in all_symbols_set:
233e5dd7070Spatrick          yield (current_timestamp, symbol)
234e5dd7070Spatrick          continue
235e5dd7070Spatrick
236e5dd7070Spatrick        # Otherwise, we have a symbol name which isn't present in the
237e5dd7070Spatrick        # binary. We assume it is truncated, and try to extend it.
238e5dd7070Spatrick
239e5dd7070Spatrick        # Get all the symbols with this prefix.
240e5dd7070Spatrick        possible_symbols = list(get_symbols_with_prefix(symbol))
241e5dd7070Spatrick        if not possible_symbols:
242e5dd7070Spatrick          continue
243e5dd7070Spatrick
244e5dd7070Spatrick        # If we found too many possible symbols, ignore this as a prefix.
245e5dd7070Spatrick        if len(possible_symbols) > 100:
246e5dd7070Spatrick          print( "warning: ignoring symbol %r " % symbol +
247e5dd7070Spatrick            "(no match and too many possible suffixes)", file=sys.stderr)
248e5dd7070Spatrick          continue
249e5dd7070Spatrick
250e5dd7070Spatrick        # Report that we resolved a missing symbol.
251e5dd7070Spatrick        if opts.show_missing_symbols and symbol not in missing_symbols:
252e5dd7070Spatrick          print("warning: resolved missing symbol %r" % symbol, file=sys.stderr)
253e5dd7070Spatrick          missing_symbols.add(symbol)
254e5dd7070Spatrick
255e5dd7070Spatrick        # Otherwise, treat all the possible matches as having occurred. This
256e5dd7070Spatrick        # is an over-approximation, but it should be ok in practice.
257e5dd7070Spatrick        for s in possible_symbols:
258e5dd7070Spatrick          yield (current_timestamp, s)
259e5dd7070Spatrick
260e5dd7070Spatrickdef uniq(list):
261e5dd7070Spatrick  seen = set()
262e5dd7070Spatrick  for item in list:
263e5dd7070Spatrick    if item not in seen:
264e5dd7070Spatrick      yield item
265e5dd7070Spatrick      seen.add(item)
266e5dd7070Spatrick
267e5dd7070Spatrickdef form_by_call_order(symbol_lists):
268e5dd7070Spatrick  # Simply strategy, just return symbols in order of occurrence, even across
269e5dd7070Spatrick  # multiple runs.
270e5dd7070Spatrick  return uniq(s for symbols in symbol_lists for s in symbols)
271e5dd7070Spatrick
272e5dd7070Spatrickdef form_by_call_order_fair(symbol_lists):
273e5dd7070Spatrick  # More complicated strategy that tries to respect the call order across all
274e5dd7070Spatrick  # of the test cases, instead of giving a huge preference to the first test
275e5dd7070Spatrick  # case.
276e5dd7070Spatrick
277e5dd7070Spatrick  # First, uniq all the lists.
278e5dd7070Spatrick  uniq_lists = [list(uniq(symbols)) for symbols in symbol_lists]
279e5dd7070Spatrick
280e5dd7070Spatrick  # Compute the successors for each list.
281e5dd7070Spatrick  succs = {}
282e5dd7070Spatrick  for symbols in uniq_lists:
283e5dd7070Spatrick    for a,b in zip(symbols[:-1], symbols[1:]):
284e5dd7070Spatrick      succs[a] = items = succs.get(a, [])
285e5dd7070Spatrick      if b not in items:
286e5dd7070Spatrick        items.append(b)
287e5dd7070Spatrick
288e5dd7070Spatrick  # Emit all the symbols, but make sure to always emit all successors from any
289e5dd7070Spatrick  # call list whenever we see a symbol.
290e5dd7070Spatrick  #
291e5dd7070Spatrick  # There isn't much science here, but this sometimes works better than the
292e5dd7070Spatrick  # more naive strategy. Then again, sometimes it doesn't so more research is
293e5dd7070Spatrick  # probably needed.
294e5dd7070Spatrick  return uniq(s
295e5dd7070Spatrick    for symbols in symbol_lists
296e5dd7070Spatrick    for node in symbols
297e5dd7070Spatrick    for s in ([node] + succs.get(node,[])))
298e5dd7070Spatrick
299e5dd7070Spatrickdef form_by_frequency(symbol_lists):
300e5dd7070Spatrick  # Form the order file by just putting the most commonly occurring symbols
301e5dd7070Spatrick  # first. This assumes the data files didn't use the oneshot dtrace method.
302e5dd7070Spatrick
303e5dd7070Spatrick  counts = {}
304e5dd7070Spatrick  for symbols in symbol_lists:
305e5dd7070Spatrick    for a in symbols:
306e5dd7070Spatrick      counts[a] = counts.get(a,0) + 1
307e5dd7070Spatrick
308e5dd7070Spatrick  by_count = list(counts.items())
309e5dd7070Spatrick  by_count.sort(key = lambda __n: -__n[1])
310e5dd7070Spatrick  return [s for s,n in by_count]
311e5dd7070Spatrick
312e5dd7070Spatrickdef form_by_random(symbol_lists):
313e5dd7070Spatrick  # Randomize the symbols.
314e5dd7070Spatrick  merged_symbols = uniq(s for symbols in symbol_lists
315e5dd7070Spatrick                          for s in symbols)
316e5dd7070Spatrick  random.shuffle(merged_symbols)
317e5dd7070Spatrick  return merged_symbols
318e5dd7070Spatrick
319e5dd7070Spatrickdef form_by_alphabetical(symbol_lists):
320e5dd7070Spatrick  # Alphabetize the symbols.
321e5dd7070Spatrick  merged_symbols = list(set(s for symbols in symbol_lists for s in symbols))
322e5dd7070Spatrick  merged_symbols.sort()
323e5dd7070Spatrick  return merged_symbols
324e5dd7070Spatrick
325e5dd7070Spatrickmethods = dict((name[len("form_by_"):],value)
326e5dd7070Spatrick  for name,value in locals().items() if name.startswith("form_by_"))
327e5dd7070Spatrick
328e5dd7070Spatrickdef genOrderFile(args):
329e5dd7070Spatrick  parser = argparse.ArgumentParser(
330e5dd7070Spatrick    "%prog  [options] <dtrace data file directories>]")
331e5dd7070Spatrick  parser.add_argument('input', nargs='+', help='')
332e5dd7070Spatrick  parser.add_argument("--binary", metavar="PATH", type=str, dest="binary_path",
333e5dd7070Spatrick    help="Path to the binary being ordered (for getting all symbols)",
334e5dd7070Spatrick    default=None)
335e5dd7070Spatrick  parser.add_argument("--output", dest="output_path",
336e5dd7070Spatrick    help="path to output order file to write", default=None, required=True,
337e5dd7070Spatrick    metavar="PATH")
338e5dd7070Spatrick  parser.add_argument("--show-missing-symbols", dest="show_missing_symbols",
339e5dd7070Spatrick    help="show symbols which are 'fixed up' to a valid name (requires --binary)",
340e5dd7070Spatrick    action="store_true", default=None)
341e5dd7070Spatrick  parser.add_argument("--output-unordered-symbols",
342e5dd7070Spatrick    dest="output_unordered_symbols_path",
343e5dd7070Spatrick    help="write a list of the unordered symbols to PATH (requires --binary)",
344e5dd7070Spatrick    default=None, metavar="PATH")
345e5dd7070Spatrick  parser.add_argument("--method", dest="method",
346e5dd7070Spatrick    help="order file generation method to use", choices=list(methods.keys()),
347e5dd7070Spatrick    default='call_order')
348e5dd7070Spatrick  opts = parser.parse_args(args)
349e5dd7070Spatrick
350e5dd7070Spatrick  # If the user gave us a binary, get all the symbols in the binary by
351e5dd7070Spatrick  # snarfing 'nm' output.
352e5dd7070Spatrick  if opts.binary_path is not None:
353e5dd7070Spatrick     output = subprocess.check_output(['nm', '-P', opts.binary_path], universal_newlines=True)
354e5dd7070Spatrick     lines = output.split("\n")
355e5dd7070Spatrick     all_symbols = [ln.split(' ',1)[0]
356e5dd7070Spatrick                    for ln in lines
357e5dd7070Spatrick                    if ln.strip()]
358e5dd7070Spatrick     print("found %d symbols in binary" % len(all_symbols))
359e5dd7070Spatrick     all_symbols.sort()
360e5dd7070Spatrick  else:
361e5dd7070Spatrick     all_symbols = []
362e5dd7070Spatrick  all_symbols_set = set(all_symbols)
363e5dd7070Spatrick
364e5dd7070Spatrick  # Compute the list of input files.
365e5dd7070Spatrick  input_files = []
366e5dd7070Spatrick  for dirname in opts.input:
367e5dd7070Spatrick    input_files.extend(findFilesWithExtension(dirname, "dtrace"))
368e5dd7070Spatrick
369e5dd7070Spatrick  # Load all of the input files.
370e5dd7070Spatrick  print("loading from %d data files" % len(input_files))
371e5dd7070Spatrick  missing_symbols = set()
372e5dd7070Spatrick  timestamped_symbol_lists = [
373e5dd7070Spatrick      list(parse_dtrace_symbol_file(path, all_symbols, all_symbols_set,
374e5dd7070Spatrick                                    missing_symbols, opts))
375e5dd7070Spatrick      for path in input_files]
376e5dd7070Spatrick
377e5dd7070Spatrick  # Reorder each symbol list.
378e5dd7070Spatrick  symbol_lists = []
379e5dd7070Spatrick  for timestamped_symbols_list in timestamped_symbol_lists:
380e5dd7070Spatrick    timestamped_symbols_list.sort()
381e5dd7070Spatrick    symbol_lists.append([symbol for _,symbol in timestamped_symbols_list])
382e5dd7070Spatrick
383e5dd7070Spatrick  # Execute the desire order file generation method.
384e5dd7070Spatrick  method = methods.get(opts.method)
385e5dd7070Spatrick  result = list(method(symbol_lists))
386e5dd7070Spatrick
387e5dd7070Spatrick  # Report to the user on what percentage of symbols are present in the order
388e5dd7070Spatrick  # file.
389e5dd7070Spatrick  num_ordered_symbols = len(result)
390e5dd7070Spatrick  if all_symbols:
391e5dd7070Spatrick    print("note: order file contains %d/%d symbols (%.2f%%)" % (
392e5dd7070Spatrick      num_ordered_symbols, len(all_symbols),
393e5dd7070Spatrick      100.*num_ordered_symbols/len(all_symbols)), file=sys.stderr)
394e5dd7070Spatrick
395e5dd7070Spatrick  if opts.output_unordered_symbols_path:
396e5dd7070Spatrick    ordered_symbols_set = set(result)
397e5dd7070Spatrick    with open(opts.output_unordered_symbols_path, 'w') as f:
398e5dd7070Spatrick      f.write("\n".join(s for s in all_symbols if s not in ordered_symbols_set))
399e5dd7070Spatrick
400e5dd7070Spatrick  # Write the order file.
401e5dd7070Spatrick  with open(opts.output_path, 'w') as f:
402e5dd7070Spatrick    f.write("\n".join(result))
403e5dd7070Spatrick    f.write("\n")
404e5dd7070Spatrick
405e5dd7070Spatrick  return 0
406e5dd7070Spatrick
407e5dd7070Spatrickcommands = {'clean' : clean,
408e5dd7070Spatrick  'merge' : merge,
409e5dd7070Spatrick  'dtrace' : dtrace,
410e5dd7070Spatrick  'cc1' : cc1,
411*12c85518Srobert  'gen-order-file' : genOrderFile,
412*12c85518Srobert  'merge-fdata' : merge_fdata,
413*12c85518Srobert  }
414e5dd7070Spatrick
415e5dd7070Spatrickdef main():
416e5dd7070Spatrick  f = commands[sys.argv[1]]
417e5dd7070Spatrick  sys.exit(f(sys.argv[2:]))
418e5dd7070Spatrick
419e5dd7070Spatrickif __name__ == '__main__':
420e5dd7070Spatrick  main()
421