1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2023 The ChromiumOS Authors 3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file. 5*bb4ee6a4SAndroid Build Coastguard Worker 6*bb4ee6a4SAndroid Build Coastguard Workerimport json 7*bb4ee6a4SAndroid Build Coastguard Workerimport os 8*bb4ee6a4SAndroid Build Coastguard Workerimport pathlib 9*bb4ee6a4SAndroid Build Coastguard Workerimport re 10*bb4ee6a4SAndroid Build Coastguard Workerimport tempfile 11*bb4ee6a4SAndroid Build Coastguard Workerfrom collections import Counter 12*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import Dict, List, Tuple 13*bb4ee6a4SAndroid Build Coastguard Worker 14*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.common import CROSVM_ROOT, Triple, chdir, cmd, run_main 15*bb4ee6a4SAndroid Build Coastguard Worker 16*bb4ee6a4SAndroid Build Coastguard Worker# Capture syscall name as group 1 from strace log 17*bb4ee6a4SAndroid Build Coastguard Worker# E.g. 'access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)' will have 'access' as group 1 18*bb4ee6a4SAndroid Build Coastguard Workersyscall_re = re.compile(r"^(\w+)\(.+?= .+$", re.IGNORECASE | re.MULTILINE) 19*bb4ee6a4SAndroid Build Coastguard Worker 20*bb4ee6a4SAndroid Build Coastguard Worker# Capture seccomp_trace json as group 1 from crosvm log 21*bb4ee6a4SAndroid Build Coastguard Worker# E.g. ' DEBUG jail::helpers] seccomp_trace {"event": "minijail_create", "name": "balloon_device", "jail_addr": "0x55623bea1df0"}' 22*bb4ee6a4SAndroid Build Coastguard Worker# will have the entire json string as group 1 23*bb4ee6a4SAndroid Build Coastguard Workerseccomp_trace_log_re = re.compile(r"DEBUG.*? seccomp_trace .*?(\{.*\}).*?$", re.MULTILINE) 24*bb4ee6a4SAndroid Build Coastguard Worker 25*bb4ee6a4SAndroid Build Coastguard Worker 26*bb4ee6a4SAndroid Build Coastguard Workerdef parse_strace_file_str_to_freq_dict(strace_content: str) -> Counter[str]: 27*bb4ee6a4SAndroid Build Coastguard Worker # TODO: start from after seccomp 28*bb4ee6a4SAndroid Build Coastguard Worker # TODO: throw exception when seccomp isn't detected 29*bb4ee6a4SAndroid Build Coastguard Worker return Counter(map(lambda m: m.group(1), syscall_re.finditer(strace_content))) 30*bb4ee6a4SAndroid Build Coastguard Worker 31*bb4ee6a4SAndroid Build Coastguard Worker 32*bb4ee6a4SAndroid Build Coastguard Workerdef freq_dict_to_freq_file_str(freq_dict: Dict[str, int]) -> str: 33*bb4ee6a4SAndroid Build Coastguard Worker return "\n".join(map(lambda k: f"{k}: {str(freq_dict[k])}", sorted(freq_dict.keys()))) 34*bb4ee6a4SAndroid Build Coastguard Worker 35*bb4ee6a4SAndroid Build Coastguard Worker 36*bb4ee6a4SAndroid Build Coastguard Workerdef parse_crosvm_log(crosvm_log: str) -> List[Dict[str, str]]: 37*bb4ee6a4SAndroid Build Coastguard Worker # Load each seccomp_trace event json as dict 38*bb4ee6a4SAndroid Build Coastguard Worker seccomp_trace_events = [] 39*bb4ee6a4SAndroid Build Coastguard Worker for match in seccomp_trace_log_re.finditer(crosvm_log): 40*bb4ee6a4SAndroid Build Coastguard Worker seccomp_trace_events.append(json.loads(match.group(1))) 41*bb4ee6a4SAndroid Build Coastguard Worker return seccomp_trace_events 42*bb4ee6a4SAndroid Build Coastguard Worker 43*bb4ee6a4SAndroid Build Coastguard Worker 44*bb4ee6a4SAndroid Build Coastguard Workerdef parse_seccomp_trace_events_to_pid_table( 45*bb4ee6a4SAndroid Build Coastguard Worker seccomp_trace_events: List[Dict[str, str]] 46*bb4ee6a4SAndroid Build Coastguard Worker) -> List[Tuple[int, str]]: 47*bb4ee6a4SAndroid Build Coastguard Worker # There are 3 types of minijail events: create, clone, form 48*bb4ee6a4SAndroid Build Coastguard Worker # Create is when a jail is created and a policy file is loaded into the new jail. "name" field in this type of event will contain the policy file name used in this jail. 49*bb4ee6a4SAndroid Build Coastguard Worker # Clone is when a jail's policy is duplicated into a new jail. Cloned jail can be used to contain different processes with the same policy. 50*bb4ee6a4SAndroid Build Coastguard Worker # Fork is when a jail is enabled and a process is forked to be executed inside the jail. 51*bb4ee6a4SAndroid Build Coastguard Worker addr_to_name: Dict[str, str] = dict() 52*bb4ee6a4SAndroid Build Coastguard Worker result = [] 53*bb4ee6a4SAndroid Build Coastguard Worker for event in seccomp_trace_events: 54*bb4ee6a4SAndroid Build Coastguard Worker if event["event"] == "minijail_create": 55*bb4ee6a4SAndroid Build Coastguard Worker addr_to_name[event["jail_addr"]] = event["name"] 56*bb4ee6a4SAndroid Build Coastguard Worker elif event["event"] == "minijail_clone": 57*bb4ee6a4SAndroid Build Coastguard Worker addr_to_name[event["dst_jail_addr"]] = addr_to_name[event["src_jail_addr"]] 58*bb4ee6a4SAndroid Build Coastguard Worker elif event["event"] == "minijail_fork": 59*bb4ee6a4SAndroid Build Coastguard Worker result.append((int(event["pid"]), addr_to_name[event["jail_addr"]])) 60*bb4ee6a4SAndroid Build Coastguard Worker else: 61*bb4ee6a4SAndroid Build Coastguard Worker raise ValueError("Unrecognized event type: {}".format(event["event"])) 62*bb4ee6a4SAndroid Build Coastguard Worker return result 63*bb4ee6a4SAndroid Build Coastguard Worker 64*bb4ee6a4SAndroid Build Coastguard Worker 65*bb4ee6a4SAndroid Build Coastguard Workerbench = cmd("cargo test").with_color_flag() 66*bb4ee6a4SAndroid Build Coastguard Worker 67*bb4ee6a4SAndroid Build Coastguard Worker 68*bb4ee6a4SAndroid Build Coastguard Workerdef main( 69*bb4ee6a4SAndroid Build Coastguard Worker target_name: str, 70*bb4ee6a4SAndroid Build Coastguard Worker log_seccomp: bool = False, 71*bb4ee6a4SAndroid Build Coastguard Worker log_seccomp_output_dir: str = "", 72*bb4ee6a4SAndroid Build Coastguard Worker nocapture: bool = False, 73*bb4ee6a4SAndroid Build Coastguard Worker): 74*bb4ee6a4SAndroid Build Coastguard Worker """Run an end-to-end benchmark target. 75*bb4ee6a4SAndroid Build Coastguard Worker 76*bb4ee6a4SAndroid Build Coastguard Worker target-name -- name of target 77*bb4ee6a4SAndroid Build Coastguard Worker log-seccomp -- record minijail seccomp filter with the run 78*bb4ee6a4SAndroid Build Coastguard Worker 79*bb4ee6a4SAndroid Build Coastguard Worker """ 80*bb4ee6a4SAndroid Build Coastguard Worker 81*bb4ee6a4SAndroid Build Coastguard Worker if log_seccomp and not os.path.isdir(log_seccomp_output_dir): 82*bb4ee6a4SAndroid Build Coastguard Worker raise ValueError("invalid log_seccomp_output_dir set") 83*bb4ee6a4SAndroid Build Coastguard Worker 84*bb4ee6a4SAndroid Build Coastguard Worker if log_seccomp: 85*bb4ee6a4SAndroid Build Coastguard Worker abs_seccomp_output_dir = pathlib.Path(log_seccomp_output_dir).absolute() 86*bb4ee6a4SAndroid Build Coastguard Worker 87*bb4ee6a4SAndroid Build Coastguard Worker chdir(CROSVM_ROOT / "e2e_tests") 88*bb4ee6a4SAndroid Build Coastguard Worker 89*bb4ee6a4SAndroid Build Coastguard Worker build_env = os.environ.copy() 90*bb4ee6a4SAndroid Build Coastguard Worker build_env.update(Triple.host_default().get_cargo_env()) 91*bb4ee6a4SAndroid Build Coastguard Worker 92*bb4ee6a4SAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tempdir: 93*bb4ee6a4SAndroid Build Coastguard Worker if log_seccomp: 94*bb4ee6a4SAndroid Build Coastguard Worker strace_out_path = pathlib.Path(tempdir) / "strace_out" 95*bb4ee6a4SAndroid Build Coastguard Worker build_env.update( 96*bb4ee6a4SAndroid Build Coastguard Worker { 97*bb4ee6a4SAndroid Build Coastguard Worker "CROSVM_CARGO_TEST_LOG_LEVEL_DEBUG": "1", 98*bb4ee6a4SAndroid Build Coastguard Worker "CROSVM_CARGO_TEST_E2E_WRAPPER_CMD": "strace -ff --output={}".format( 99*bb4ee6a4SAndroid Build Coastguard Worker os.path.abspath(strace_out_path) 100*bb4ee6a4SAndroid Build Coastguard Worker ), 101*bb4ee6a4SAndroid Build Coastguard Worker "CROSVM_CARGO_TEST_LOG_FILE": os.path.abspath( 102*bb4ee6a4SAndroid Build Coastguard Worker pathlib.Path(tempdir) / "crosvm.log" 103*bb4ee6a4SAndroid Build Coastguard Worker ), 104*bb4ee6a4SAndroid Build Coastguard Worker } 105*bb4ee6a4SAndroid Build Coastguard Worker ) 106*bb4ee6a4SAndroid Build Coastguard Worker if nocapture: 107*bb4ee6a4SAndroid Build Coastguard Worker bench("--release", "--bench", target_name, "--", "--nocapture").with_envs( 108*bb4ee6a4SAndroid Build Coastguard Worker build_env 109*bb4ee6a4SAndroid Build Coastguard Worker ).fg() 110*bb4ee6a4SAndroid Build Coastguard Worker else: 111*bb4ee6a4SAndroid Build Coastguard Worker bench("--release", "--bench", target_name).with_envs(build_env).fg() 112*bb4ee6a4SAndroid Build Coastguard Worker 113*bb4ee6a4SAndroid Build Coastguard Worker if log_seccomp: 114*bb4ee6a4SAndroid Build Coastguard Worker with open(pathlib.Path(tempdir) / "crosvm.log", "r") as f: 115*bb4ee6a4SAndroid Build Coastguard Worker pid_table = parse_seccomp_trace_events_to_pid_table(parse_crosvm_log(f.read())) 116*bb4ee6a4SAndroid Build Coastguard Worker 117*bb4ee6a4SAndroid Build Coastguard Worker # Map each policy name to its frequency 118*bb4ee6a4SAndroid Build Coastguard Worker policy_freq_dict: Dict[str, Counter[str]] = {} 119*bb4ee6a4SAndroid Build Coastguard Worker for pid, policy_name in pid_table: 120*bb4ee6a4SAndroid Build Coastguard Worker strace_log = pathlib.Path( 121*bb4ee6a4SAndroid Build Coastguard Worker os.path.normpath(strace_out_path) + f".{str(pid)}" 122*bb4ee6a4SAndroid Build Coastguard Worker ).read_text() 123*bb4ee6a4SAndroid Build Coastguard Worker freq_counter = parse_strace_file_str_to_freq_dict(strace_log) 124*bb4ee6a4SAndroid Build Coastguard Worker if policy_name in policy_freq_dict: 125*bb4ee6a4SAndroid Build Coastguard Worker policy_freq_dict[policy_name] += freq_counter 126*bb4ee6a4SAndroid Build Coastguard Worker else: 127*bb4ee6a4SAndroid Build Coastguard Worker policy_freq_dict[policy_name] = freq_counter 128*bb4ee6a4SAndroid Build Coastguard Worker 129*bb4ee6a4SAndroid Build Coastguard Worker for policy_name, freq_counter in policy_freq_dict.items(): 130*bb4ee6a4SAndroid Build Coastguard Worker (abs_seccomp_output_dir / f"{policy_name}.frequency").write_text( 131*bb4ee6a4SAndroid Build Coastguard Worker freq_dict_to_freq_file_str(freq_counter) 132*bb4ee6a4SAndroid Build Coastguard Worker ) 133*bb4ee6a4SAndroid Build Coastguard Worker 134*bb4ee6a4SAndroid Build Coastguard Worker 135*bb4ee6a4SAndroid Build Coastguard Workerif __name__ == "__main__": 136*bb4ee6a4SAndroid Build Coastguard Worker run_main(main) 137