aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Burgess IV <gbiv@google.com>2019-10-11 13:51:22 -0700
committerGeorge Burgess <gbiv@chromium.org>2019-10-14 20:32:32 +0000
commitf3453b21b3f63c8d65e4d7dce5b196ac2d431360 (patch)
treedfc98bde1eb81f217c23dc39f16c2fbd46d74a78
parentc08e01d98e72f2e9dffd73a7fd3d011f2a251ac9 (diff)
downloadtoolchain-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-xafdo_redaction/remove_indirect_calls.py81
-rwxr-xr-xafdo_redaction/remove_indirect_calls_test.py67
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()