diff options
author | George Burgess IV <gbiv@google.com> | 2019-10-11 13:51:22 -0700 |
---|---|---|
committer | George Burgess <gbiv@chromium.org> | 2019-10-14 20:32:32 +0000 |
commit | f3453b21b3f63c8d65e4d7dce5b196ac2d431360 (patch) | |
tree | dfc98bde1eb81f217c23dc39f16c2fbd46d74a78 | |
parent | c08e01d98e72f2e9dffd73a7fd3d011f2a251ac9 (diff) | |
download | toolchain-utils-f3453b21b3f63c8d65e4d7dce5b196ac2d431360.tar.gz |
afdo_redaction: add a remove_indirect_calls script
This CL adds a script that removes all indirect call info from a textual
AFDO profile. This is useful for reasons mentioned in the module's
docstring.
BUG=chromium:1005023
TEST=Ran on a profile. All indirect call info was removed, but the
profiles were otherwise identical.
Change-Id: I36caf2040d011e5cabd6f2058db784c71c42c25e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1856827
Commit-Queue: George Burgess <gbiv@chromium.org>
Legacy-Commit-Queue: Commit Bot <commit-bot@chromium.org>
Reviewed-by: Tiancong Wang <tcwang@google.com>
Tested-by: George Burgess <gbiv@chromium.org>
-rwxr-xr-x | afdo_redaction/remove_indirect_calls.py | 81 | ||||
-rwxr-xr-x | afdo_redaction/remove_indirect_calls_test.py | 67 |
2 files changed, 148 insertions, 0 deletions
diff --git a/afdo_redaction/remove_indirect_calls.py b/afdo_redaction/remove_indirect_calls.py new file mode 100755 index 00000000..b879b2f0 --- /dev/null +++ b/afdo_redaction/remove_indirect_calls.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# Copyright 2019 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Script to remove all indirect call targets from textual AFDO profiles. + +Indirect call samples can cause code to appear 'live' when it otherwise +wouldn't be. This resurrection can happen either by the way of profile-based +speculative devirtualization, or because of imprecision in LLVM's liveness +calculations when performing LTO. + +This generally isn't a problem when an AFDO profile is applied to the binary it +was collected on. However, because we e.g., build NaCl from the same set of +objects as Chrome, this can become problematic, and lead to NaCl doubling in +size (or worse). See crbug.com/1005023 and crbug.com/916130. +""" + +from __future__ import division, print_function + +import argparse +import re +import sys + + +def _remove_indirect_call_targets(lines): + # Lines with indirect call targets look like: + # 1.1: 1234 foo:111 bar:122 + # + # Where 1.1 is the line info/discriminator, 1234 is the total number of + # samples seen for that line/discriminator, foo:111 is "111 of the calls here + # went to foo," and bar:122 is "122 of the calls here went to bar." + call_target_re = re.compile( + r""" + ^\s+ # Top-level lines are function records. + \d+(?:\.\d+)?: # Line info/discriminator + \s+ + \d+ # Total sample count + \s+ + ((?:[^\s:]+:\d+\s*)+) # Indirect call target(s) + $ + """, re.VERBOSE) + for line in lines: + line = line.rstrip() + + match = call_target_re.match(line) + if not match: + yield line + '\n' + continue + + group_start, group_end = match.span(1) + assert group_end == len(line) + yield line[:group_start].rstrip() + '\n' + + +def run(input_stream, output_stream): + for line in _remove_indirect_call_targets(input_stream): + output_stream.write(line) + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument( + '--input', + default='/dev/stdin', + help='File to read from. Defaults to stdin.') + parser.add_argument( + '--output', + default='/dev/stdout', + help='File to write to. Defaults to stdout.') + args = parser.parse_args() + + with open(args.input) as stdin: + with open(args.output, 'w') as stdout: + run(stdin, stdout) + + +if __name__ == '__main__': + main() diff --git a/afdo_redaction/remove_indirect_calls_test.py b/afdo_redaction/remove_indirect_calls_test.py new file mode 100755 index 00000000..1499af25 --- /dev/null +++ b/afdo_redaction/remove_indirect_calls_test.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# Copyright 2019 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Tests for remove_indirect_calls""" + +from __future__ import print_function + +import io +import unittest + +import remove_indirect_calls + + +def _run_test(input_lines): + input_buf = io.BytesIO('\n'.join(input_lines)) + output_buf = io.BytesIO() + remove_indirect_calls.run(input_buf, output_buf) + return output_buf.getvalue().splitlines() + + +class Test(unittest.TestCase): + """Tests""" + + def test_empty_profile(self): + self.assertEqual(_run_test([]), []) + + def test_removal_on_real_world_code(self): + # These are copied from an actual textual AFDO profile, but the names made + # lints unhappy due to their length, so I had to be creative. + profile_lines = """_ZLongSymbolName:52862:1766 + 14: 2483 + 8.1: _SomeInlinedSym:45413 + 11: _AndAnother:35481 + 2: 2483 + 2.1: _YetAnother:25549 + 3: 2483 + 3.1: 351 + 3.3: 2526 IndirectTarg1:675 Targ2:397 Targ3:77 + 13.2: Whee:9932 + 1.1: Whoo:9932 + 0: BleepBloop:9932 + 0: 2483 + """.strip().splitlines() + + expected_lines = """_ZLongSymbolName:52862:1766 + 14: 2483 + 8.1: _SomeInlinedSym:45413 + 11: _AndAnother:35481 + 2: 2483 + 2.1: _YetAnother:25549 + 3: 2483 + 3.1: 351 + 3.3: 2526 + 13.2: Whee:9932 + 1.1: Whoo:9932 + 0: BleepBloop:9932 + 0: 2483 + """.strip().splitlines() + + self.assertEqual(_run_test(profile_lines), expected_lines) + + +if __name__ == '__main__': + unittest.main() |