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