firecracker
165 строк · 5.2 Кб
1# Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2# SPDX-License-Identifier: Apache-2.0
3
4"""
5Script used to check if bindgen-generated code creates structs that differ from previously created
6onces.
7
8The script uses `pahole` (man 1 pahole) to gather debug information from two firecracker binaries
9(script's arguments). It parses pahole output and gathers struct information in a dictionary of the
10form:
11
12```
13{
14"struct_name": {"size": size_in_bytes, "alignment": alignment_in_bytes},
15...
16}
17```
18
19It also, filters structure names using the "bindings" filter for keeping only bindgen related
20structs.
21
22*NOTE*: this assumes that all bindgen-related structs live under a crate or module name with
23"bindings" in it. At the moment, this is true.
24
25It then iterates through the structs of the firecracker binary built from the older version and
26checks if there are mismatches with the struct info from the second binary (newer version)
27
28### Usage
29
301. Create the two binaries
31
32```
33# First create the binary with existing bindings
34$ git checkout main
35$ ./tools/devtool build
36$ cp ./build/cargo_target/x86_64-unknown-linux-musl/debug/firecracker firecracker_old
37
38# Second create the binary with new bindings
39$ git checkout new_bindings
40$ ./tools/devtool build
41$ cp ./build/cargo_target/x86_64-unknown-linux-musl/debug/firecracker firecracker_new
42
43# Run the script
44$ python3 ./tools/test_bindings.py firecracker_old firecracker_new
45```
46"""
47
48import argparse49import logging50import re51import subprocess52import sys53
54logging.basicConfig(level=logging.DEBUG)55log = logging.getLogger(__name__)56
57
58def parse_pahole(pahole_output):59"""Gather bindings related structs from pahole output60
61Parse pahole output and gather struct information filtering for the 'bindings' keyword.
62The information gathered is the struct size and its alignment.
63
64@param fname: File including pahole output
65@return: A dictionary where keys are struct names and values struct size and alignment
66"""
67ret = {}68
69# regular expression matches the name of the struct, its size and alignment70structs = re.findall(71rb"struct (.*?)\{.*?/\* size: (\d+).*?\*/.*?\n\} "72rb"__attribute__\(\(__aligned__\((\d+)\)\)\)\;",73pahole_output,74flags=re.DOTALL,75)76
77for struct in structs:78struct_name = str(struct[0])79size = int(struct[1])80alignment = int(struct[2])81
82if "bindings" in struct_name:83ret[struct_name] = {"size": size, "alignment": alignment}84
85return ret86
87
88def pahole(binary: str) -> str:89"""Runs pahole on a binary and returns its output as a str90
91If pahole fails this will raise a `CalledProcessError`
92
93@param binary: binary to run pahole on
94@return: On success, it will return the stdout of the pahole process
95"""
96result = subprocess.run(97["pahole", binary], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True98)99return result.stdout100
101
102def check_pahole_mismatches(old: str, new: str) -> bool:103"""Checks for pahole mismatches in pahole information between two binaries104
105@param old: old Firecracker binary
106@param new: new Firecracker binary
107@return: false if no mismatches found, true otherwise
108"""
109pahole_structs_1 = parse_pahole(pahole(old))110pahole_structs_2 = parse_pahole(pahole(new))111
112# We go through all the structs existing in the old firecracker binary and check for mismatches113# in the new one.114for name, prop_1 in pahole_structs_1.items():115# Note that the reverse, i.e. a name existing in the new binary but not in the old binary,116# is not a problem. That would mean we are making use of some new struct from117# bindgen-generated code. That does not break ABI compatibility.118if name not in pahole_structs_2:119log.warning("struct '%s' does not exist in new binary", name)120continue121
122prop_2 = pahole_structs_2[name]123# Size mismatches are hard errors124if prop_1["size"] != prop_2["size"]:125log.error("size of '%s' does not match in two binaries", name)126log.error("old: %s", prop_1["size"])127log.error("new: %s", prop_2["size"])128return True129
130# Alignment mismatches just cause warnings131if prop_1["alignment"] != prop_2["alignment"]:132log.warning("alignment of '%s' does not match in two binaries", name)133log.warning("old: %s", prop_1["alignment"])134log.warning("new: %s", prop_2["alignment"])135else:136log.info("struct '%s' matches", name)137
138return False139
140
141if __name__ == "__main__":142parser = argparse.ArgumentParser(143description="Check bindings ABI compatibility for Firecracker"144)145parser.add_argument(146"firecracker_old",147type=str,148metavar="old-firecracker-binary",149help="Firecracker binary with old bindings",150)151parser.add_argument(152"firecracker_new",153type=str,154metavar="new-firecracker-binary",155help="Firecracker binary with new bindings",156)157args = parser.parse_args()158
159if check_pahole_mismatches(args.firecracker_old, args.firecracker_new):160log.error("Structure layout mismatch")161sys.exit(1)162else:163log.info("Structure layout matches")164
165sys.exit(0)166