1*7594170eSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*7594170eSAndroid Build Coastguard Worker# 3*7594170eSAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project 4*7594170eSAndroid Build Coastguard Worker# 5*7594170eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*7594170eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*7594170eSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*7594170eSAndroid Build Coastguard Worker# 9*7594170eSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*7594170eSAndroid Build Coastguard Worker# 11*7594170eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*7594170eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*7594170eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*7594170eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*7594170eSAndroid Build Coastguard Worker# limitations under the License.""" 16*7594170eSAndroid Build Coastguard Worker 17*7594170eSAndroid Build Coastguard Workerimport csv 18*7594170eSAndroid Build Coastguard Workerimport pathlib 19*7594170eSAndroid Build Coastguard Workerimport subprocess 20*7594170eSAndroid Build Coastguard Workerfrom diffs.diff import Diff, ExtractInfo 21*7594170eSAndroid Build Coastguard Worker 22*7594170eSAndroid Build Coastguard Worker 23*7594170eSAndroid Build Coastguard Workerclass BloatyDiff(Diff): 24*7594170eSAndroid Build Coastguard Worker """BloatyDiff compares the sizes of symbols present in cc objects 25*7594170eSAndroid Build Coastguard Worker 26*7594170eSAndroid Build Coastguard Worker Bloaty McBloatface (bloaty) is used to discover size differences in object 27*7594170eSAndroid Build Coastguard Worker files or cc binaries. This diff returns a list of symbols which are new or 28*7594170eSAndroid Build Coastguard Worker larger in one file than the other. 29*7594170eSAndroid Build Coastguard Worker 30*7594170eSAndroid Build Coastguard Worker The output does not distinguish between new symbols and ones that are simply 31*7594170eSAndroid Build Coastguard Worker larger, so this output is best combined with the NmSymbolDiff to see which 32*7594170eSAndroid Build Coastguard Worker symbols are new. 33*7594170eSAndroid Build Coastguard Worker 34*7594170eSAndroid Build Coastguard Worker Example bloaty output (note: compileunits may not always be available): 35*7594170eSAndroid Build Coastguard Worker $ bloaty --csv -d compileunits,symbols $BAZEL_OBJ -- $LEGACY_OBJ 36*7594170eSAndroid Build Coastguard Worker compileunits,symbols,vmsize,filesize 37*7594170eSAndroid Build Coastguard Worker external/zstd/lib/compress/zstd_fast.c,ZSTD_compressBlock_doubleFast_extDict_generic,6240,6344 38*7594170eSAndroid Build Coastguard Worker external/zstd/lib/compress/zstd_fast.c,ZSTD_compressBlock_lazy_dictMatchState,-3428,-3551 39*7594170eSAndroid Build Coastguard Worker 40*7594170eSAndroid Build Coastguard Worker The first entry is a symbol that is larger in the Bazel version of the binary, 41*7594170eSAndroid Build Coastguard Worker and the second entry is a symbol that is larger in the Soong version of the 42*7594170eSAndroid Build Coastguard Worker binary. 43*7594170eSAndroid Build Coastguard Worker """ 44*7594170eSAndroid Build Coastguard Worker def __init__(self, tool_name, data_source, has_debug_symbols=False): 45*7594170eSAndroid Build Coastguard Worker self.tool_name = tool_name 46*7594170eSAndroid Build Coastguard Worker self.data_source = data_source 47*7594170eSAndroid Build Coastguard Worker self.has_debug_symbols = has_debug_symbols 48*7594170eSAndroid Build Coastguard Worker 49*7594170eSAndroid Build Coastguard Worker def _print_diff_row(self, row, ignore_keys): 50*7594170eSAndroid Build Coastguard Worker attrs = sorted({ 51*7594170eSAndroid Build Coastguard Worker k: v 52*7594170eSAndroid Build Coastguard Worker for k, v in row.items() 53*7594170eSAndroid Build Coastguard Worker if k not in ignore_keys 54*7594170eSAndroid Build Coastguard Worker }.items()) 55*7594170eSAndroid Build Coastguard Worker return row[self.data_source] + ": { " + ", ".join(f"{a[0]}: {a[1]}" for a in attrs) + " }" 56*7594170eSAndroid Build Coastguard Worker 57*7594170eSAndroid Build Coastguard Worker def _collect_diff_compileunits(self, diffreader: csv.DictReader): 58*7594170eSAndroid Build Coastguard Worker # maps from compileunit to list of diff rows 59*7594170eSAndroid Build Coastguard Worker left_bigger = collections.defaultdict(list) 60*7594170eSAndroid Build Coastguard Worker right_bigger = collections.defaultdict(list) 61*7594170eSAndroid Build Coastguard Worker 62*7594170eSAndroid Build Coastguard Worker for row in diffreader: 63*7594170eSAndroid Build Coastguard Worker compileunit = row["compileunits"] 64*7594170eSAndroid Build Coastguard Worker if len(compileunit) > 0 and compileunit[0] == "[": 65*7594170eSAndroid Build Coastguard Worker continue 66*7594170eSAndroid Build Coastguard Worker filesize = row["filesize"] 67*7594170eSAndroid Build Coastguard Worker if int(filesize) < 0: 68*7594170eSAndroid Build Coastguard Worker left_bigger[compileunit].append(row) 69*7594170eSAndroid Build Coastguard Worker elif int(filesize) > 0: 70*7594170eSAndroid Build Coastguard Worker right_bigger[compileunit].append(row) 71*7594170eSAndroid Build Coastguard Worker 72*7594170eSAndroid Build Coastguard Worker def print_diff_dict(dict): 73*7594170eSAndroid Build Coastguard Worker lines = [] 74*7594170eSAndroid Build Coastguard Worker for compileunit, data in sorted(dict.items()): 75*7594170eSAndroid Build Coastguard Worker lines.append("\t" + compileunit + ":") 76*7594170eSAndroid Build Coastguard Worker rows = [] 77*7594170eSAndroid Build Coastguard Worker for row in data: 78*7594170eSAndroid Build Coastguard Worker if row[self.data_source] and row[self.data_source][0] == "[": 79*7594170eSAndroid Build Coastguard Worker continue 80*7594170eSAndroid Build Coastguard Worker rows.append("\t\t" + self.print_diff_row(row, ignore_keys=[self.data_source, "compileunits"])) 81*7594170eSAndroid Build Coastguard Worker lines.extend(sorted(rows)) 82*7594170eSAndroid Build Coastguard Worker return "\n".join(lines) 83*7594170eSAndroid Build Coastguard Worker 84*7594170eSAndroid Build Coastguard Worker return print_diff_dict(left_bigger), print_diff_dict(right_bigger) 85*7594170eSAndroid Build Coastguard Worker 86*7594170eSAndroid Build Coastguard Worker def _collect_diff(self, diffreader): 87*7594170eSAndroid Build Coastguard Worker left_bigger = [] 88*7594170eSAndroid Build Coastguard Worker right_bigger = [] 89*7594170eSAndroid Build Coastguard Worker 90*7594170eSAndroid Build Coastguard Worker for row in diffreader: 91*7594170eSAndroid Build Coastguard Worker filesize = row["filesize"] 92*7594170eSAndroid Build Coastguard Worker if int(filesize) > 0: 93*7594170eSAndroid Build Coastguard Worker left_bigger.append(row) 94*7594170eSAndroid Build Coastguard Worker elif int(filesize) < 0: 95*7594170eSAndroid Build Coastguard Worker right_bigger.append(row) 96*7594170eSAndroid Build Coastguard Worker 97*7594170eSAndroid Build Coastguard Worker left_errors = "\n".join(["\t" + self._print_diff_row(row, ignore_keys=[self.data_source]) for row in left_bigger]) 98*7594170eSAndroid Build Coastguard Worker right_errors = "\n".join(["\t" + self._print_diff_row(row, ignore_keys=[self.data_source]) for row in right_bigger]) 99*7594170eSAndroid Build Coastguard Worker return left_errors, right_errors 100*7594170eSAndroid Build Coastguard Worker 101*7594170eSAndroid Build Coastguard Worker def diff(self, left_path: pathlib.Path, right_path: pathlib.Path) -> list[str]: 102*7594170eSAndroid Build Coastguard Worker try: 103*7594170eSAndroid Build Coastguard Worker diff_csv = subprocess.run(["bloaty", 104*7594170eSAndroid Build Coastguard Worker "--csv", 105*7594170eSAndroid Build Coastguard Worker "-n", "0", 106*7594170eSAndroid Build Coastguard Worker "-w", 107*7594170eSAndroid Build Coastguard Worker "-d", 108*7594170eSAndroid Build Coastguard Worker self.data_source + (",compileunits" if self.has_debug_symbols else ""), 109*7594170eSAndroid Build Coastguard Worker str(left_path), 110*7594170eSAndroid Build Coastguard Worker "--", 111*7594170eSAndroid Build Coastguard Worker str(right_path)], 112*7594170eSAndroid Build Coastguard Worker check=True, capture_output=True, 113*7594170eSAndroid Build Coastguard Worker encoding="utf-8").stdout.splitlines() 114*7594170eSAndroid Build Coastguard Worker except subprocess.CalledProcessError as e: 115*7594170eSAndroid Build Coastguard Worker print("ERROR: bloaty tool returned non-zero exit status") 116*7594170eSAndroid Build Coastguard Worker if self.has_debug_symbols: 117*7594170eSAndroid Build Coastguard Worker print("ERROR: do objects contain debug symbols?") 118*7594170eSAndroid Build Coastguard Worker raise e 119*7594170eSAndroid Build Coastguard Worker 120*7594170eSAndroid Build Coastguard Worker diffreader = csv.DictReader(diff_csv) 121*7594170eSAndroid Build Coastguard Worker 122*7594170eSAndroid Build Coastguard Worker if self.has_debug_symbols: 123*7594170eSAndroid Build Coastguard Worker left_bigger, right_bigger = self._collect_diff_compileunits(diffreader) 124*7594170eSAndroid Build Coastguard Worker else: 125*7594170eSAndroid Build Coastguard Worker left_bigger, right_bigger = self._collect_diff(diffreader) 126*7594170eSAndroid Build Coastguard Worker 127*7594170eSAndroid Build Coastguard Worker errors = [] 128*7594170eSAndroid Build Coastguard Worker if left_bigger: 129*7594170eSAndroid Build Coastguard Worker errors.append(f"the following {self.data_source} are either unique or larger in\n{left_path}\n than those in\n{right_path}:\n{left_bigger}") 130*7594170eSAndroid Build Coastguard Worker if right_bigger: 131*7594170eSAndroid Build Coastguard Worker errors.append(f"the following {self.data_source} are either unique or larger in\n{right_path}\n than those in\n{left_path}:\n{right_bigger}") 132*7594170eSAndroid Build Coastguard Worker 133*7594170eSAndroid Build Coastguard Worker return errors 134