• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2019 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Script to remove all indirect call targets from textual AFDO profiles.
8
9Indirect call samples can cause code to appear 'live' when it otherwise
10wouldn't be. This resurrection can happen either by the way of profile-based
11speculative devirtualization, or because of imprecision in LLVM's liveness
12calculations when performing LTO.
13
14This generally isn't a problem when an AFDO profile is applied to the binary it
15was collected on. However, because we e.g., build NaCl from the same set of
16objects as Chrome, this can become problematic, and lead to NaCl doubling in
17size (or worse). See crbug.com/1005023 and crbug.com/916130.
18"""
19
20from __future__ import division, print_function
21
22import argparse
23import re
24
25
26def _remove_indirect_call_targets(lines):
27  # Lines with indirect call targets look like:
28  #   1.1: 1234 foo:111 bar:122
29  #
30  # Where 1.1 is the line info/discriminator, 1234 is the total number of
31  # samples seen for that line/discriminator, foo:111 is "111 of the calls here
32  # went to foo," and bar:122 is "122 of the calls here went to bar."
33  call_target_re = re.compile(
34      r"""
35      ^\s+                    # Top-level lines are function records.
36      \d+(?:\.\d+)?:          # Line info/discriminator
37      \s+
38      \d+                     # Total sample count
39      \s+
40      ((?:[^\s:]+:\d+\s*)+)   # Indirect call target(s)
41      $
42  """, re.VERBOSE)
43  for line in lines:
44    line = line.rstrip()
45
46    match = call_target_re.match(line)
47    if not match:
48      yield line + '\n'
49      continue
50
51    group_start, group_end = match.span(1)
52    assert group_end == len(line)
53    yield line[:group_start].rstrip() + '\n'
54
55
56def run(input_stream, output_stream):
57  for line in _remove_indirect_call_targets(input_stream):
58    output_stream.write(line)
59
60
61def main():
62  parser = argparse.ArgumentParser(
63      description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
64  parser.add_argument(
65      '--input',
66      default='/dev/stdin',
67      help='File to read from. Defaults to stdin.')
68  parser.add_argument(
69      '--output',
70      default='/dev/stdout',
71      help='File to write to. Defaults to stdout.')
72  args = parser.parse_args()
73
74  with open(args.input) as stdin:
75    with open(args.output, 'w') as stdout:
76      run(stdin, stdout)
77
78
79if __name__ == '__main__':
80  main()
81