1*01afa757SAhmed Karaman#!/usr/bin/env python3 2*01afa757SAhmed Karaman 3*01afa757SAhmed Karaman# Print the percentage of instructions spent in each phase of QEMU 4*01afa757SAhmed Karaman# execution. 5*01afa757SAhmed Karaman# 6*01afa757SAhmed Karaman# Syntax: 7*01afa757SAhmed Karaman# dissect.py [-h] -- <qemu executable> [<qemu executable options>] \ 8*01afa757SAhmed Karaman# <target executable> [<target executable options>] 9*01afa757SAhmed Karaman# 10*01afa757SAhmed Karaman# [-h] - Print the script arguments help message. 11*01afa757SAhmed Karaman# 12*01afa757SAhmed Karaman# Example of usage: 13*01afa757SAhmed Karaman# dissect.py -- qemu-arm coulomb_double-arm 14*01afa757SAhmed Karaman# 15*01afa757SAhmed Karaman# This file is a part of the project "TCG Continuous Benchmarking". 16*01afa757SAhmed Karaman# 17*01afa757SAhmed Karaman# Copyright (C) 2020 Ahmed Karaman <ahmedkhaledkaraman@gmail.com> 18*01afa757SAhmed Karaman# Copyright (C) 2020 Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> 19*01afa757SAhmed Karaman# 20*01afa757SAhmed Karaman# This program is free software: you can redistribute it and/or modify 21*01afa757SAhmed Karaman# it under the terms of the GNU General Public License as published by 22*01afa757SAhmed Karaman# the Free Software Foundation, either version 2 of the License, or 23*01afa757SAhmed Karaman# (at your option) any later version. 24*01afa757SAhmed Karaman# 25*01afa757SAhmed Karaman# This program is distributed in the hope that it will be useful, 26*01afa757SAhmed Karaman# but WITHOUT ANY WARRANTY; without even the implied warranty of 27*01afa757SAhmed Karaman# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28*01afa757SAhmed Karaman# GNU General Public License for more details. 29*01afa757SAhmed Karaman# 30*01afa757SAhmed Karaman# You should have received a copy of the GNU General Public License 31*01afa757SAhmed Karaman# along with this program. If not, see <https://www.gnu.org/licenses/>. 32*01afa757SAhmed Karaman 33*01afa757SAhmed Karamanimport argparse 34*01afa757SAhmed Karamanimport os 35*01afa757SAhmed Karamanimport subprocess 36*01afa757SAhmed Karamanimport sys 37*01afa757SAhmed Karamanimport tempfile 38*01afa757SAhmed Karaman 39*01afa757SAhmed Karaman 40*01afa757SAhmed Karamandef get_JIT_line(callgrind_data): 41*01afa757SAhmed Karaman """ 42*01afa757SAhmed Karaman Search for the first instance of the JIT call in 43*01afa757SAhmed Karaman the callgrind_annotate output when ran using --tree=caller 44*01afa757SAhmed Karaman This is equivalent to the self number of instructions of JIT. 45*01afa757SAhmed Karaman 46*01afa757SAhmed Karaman Parameters: 47*01afa757SAhmed Karaman callgrind_data (list): callgrind_annotate output 48*01afa757SAhmed Karaman 49*01afa757SAhmed Karaman Returns: 50*01afa757SAhmed Karaman (int): Line number 51*01afa757SAhmed Karaman """ 52*01afa757SAhmed Karaman line = -1 53*01afa757SAhmed Karaman for i in range(len(callgrind_data)): 54*01afa757SAhmed Karaman if callgrind_data[i].strip('\n') and \ 55*01afa757SAhmed Karaman callgrind_data[i].split()[-1] == "[???]": 56*01afa757SAhmed Karaman line = i 57*01afa757SAhmed Karaman break 58*01afa757SAhmed Karaman if line == -1: 59*01afa757SAhmed Karaman sys.exit("Couldn't locate the JIT call ... Exiting.") 60*01afa757SAhmed Karaman return line 61*01afa757SAhmed Karaman 62*01afa757SAhmed Karaman 63*01afa757SAhmed Karamandef main(): 64*01afa757SAhmed Karaman # Parse the command line arguments 65*01afa757SAhmed Karaman parser = argparse.ArgumentParser( 66*01afa757SAhmed Karaman usage='dissect.py [-h] -- ' 67*01afa757SAhmed Karaman '<qemu executable> [<qemu executable options>] ' 68*01afa757SAhmed Karaman '<target executable> [<target executable options>]') 69*01afa757SAhmed Karaman 70*01afa757SAhmed Karaman parser.add_argument('command', type=str, nargs='+', help=argparse.SUPPRESS) 71*01afa757SAhmed Karaman 72*01afa757SAhmed Karaman args = parser.parse_args() 73*01afa757SAhmed Karaman 74*01afa757SAhmed Karaman # Extract the needed variables from the args 75*01afa757SAhmed Karaman command = args.command 76*01afa757SAhmed Karaman 77*01afa757SAhmed Karaman # Insure that valgrind is installed 78*01afa757SAhmed Karaman check_valgrind = subprocess.run( 79*01afa757SAhmed Karaman ["which", "valgrind"], stdout=subprocess.DEVNULL) 80*01afa757SAhmed Karaman if check_valgrind.returncode: 81*01afa757SAhmed Karaman sys.exit("Please install valgrind before running the script.") 82*01afa757SAhmed Karaman 83*01afa757SAhmed Karaman # Save all intermediate files in a temporary directory 84*01afa757SAhmed Karaman with tempfile.TemporaryDirectory() as tmpdirname: 85*01afa757SAhmed Karaman # callgrind output file path 86*01afa757SAhmed Karaman data_path = os.path.join(tmpdirname, "callgrind.data") 87*01afa757SAhmed Karaman # callgrind_annotate output file path 88*01afa757SAhmed Karaman annotate_out_path = os.path.join(tmpdirname, "callgrind_annotate.out") 89*01afa757SAhmed Karaman 90*01afa757SAhmed Karaman # Run callgrind 91*01afa757SAhmed Karaman callgrind = subprocess.run((["valgrind", 92*01afa757SAhmed Karaman "--tool=callgrind", 93*01afa757SAhmed Karaman "--callgrind-out-file=" + data_path] 94*01afa757SAhmed Karaman + command), 95*01afa757SAhmed Karaman stdout=subprocess.DEVNULL, 96*01afa757SAhmed Karaman stderr=subprocess.PIPE) 97*01afa757SAhmed Karaman if callgrind.returncode: 98*01afa757SAhmed Karaman sys.exit(callgrind.stderr.decode("utf-8")) 99*01afa757SAhmed Karaman 100*01afa757SAhmed Karaman # Save callgrind_annotate output 101*01afa757SAhmed Karaman with open(annotate_out_path, "w") as output: 102*01afa757SAhmed Karaman callgrind_annotate = subprocess.run( 103*01afa757SAhmed Karaman ["callgrind_annotate", data_path, "--tree=caller"], 104*01afa757SAhmed Karaman stdout=output, 105*01afa757SAhmed Karaman stderr=subprocess.PIPE) 106*01afa757SAhmed Karaman if callgrind_annotate.returncode: 107*01afa757SAhmed Karaman sys.exit(callgrind_annotate.stderr.decode("utf-8")) 108*01afa757SAhmed Karaman 109*01afa757SAhmed Karaman # Read the callgrind_annotate output to callgrind_data[] 110*01afa757SAhmed Karaman callgrind_data = [] 111*01afa757SAhmed Karaman with open(annotate_out_path, 'r') as data: 112*01afa757SAhmed Karaman callgrind_data = data.readlines() 113*01afa757SAhmed Karaman 114*01afa757SAhmed Karaman # Line number with the total number of instructions 115*01afa757SAhmed Karaman total_instructions_line_number = 20 116*01afa757SAhmed Karaman # Get the total number of instructions 117*01afa757SAhmed Karaman total_instructions_line_data = \ 118*01afa757SAhmed Karaman callgrind_data[total_instructions_line_number] 119*01afa757SAhmed Karaman total_instructions = total_instructions_line_data.split()[0] 120*01afa757SAhmed Karaman total_instructions = int(total_instructions.replace(',', '')) 121*01afa757SAhmed Karaman 122*01afa757SAhmed Karaman # Line number with the JIT self number of instructions 123*01afa757SAhmed Karaman JIT_self_instructions_line_number = get_JIT_line(callgrind_data) 124*01afa757SAhmed Karaman # Get the JIT self number of instructions 125*01afa757SAhmed Karaman JIT_self_instructions_line_data = \ 126*01afa757SAhmed Karaman callgrind_data[JIT_self_instructions_line_number] 127*01afa757SAhmed Karaman JIT_self_instructions = JIT_self_instructions_line_data.split()[0] 128*01afa757SAhmed Karaman JIT_self_instructions = int(JIT_self_instructions.replace(',', '')) 129*01afa757SAhmed Karaman 130*01afa757SAhmed Karaman # Line number with the JIT self + inclusive number of instructions 131*01afa757SAhmed Karaman # It's the line above the first JIT call when running with --tree=caller 132*01afa757SAhmed Karaman JIT_total_instructions_line_number = JIT_self_instructions_line_number-1 133*01afa757SAhmed Karaman # Get the JIT self + inclusive number of instructions 134*01afa757SAhmed Karaman JIT_total_instructions_line_data = \ 135*01afa757SAhmed Karaman callgrind_data[JIT_total_instructions_line_number] 136*01afa757SAhmed Karaman JIT_total_instructions = JIT_total_instructions_line_data.split()[0] 137*01afa757SAhmed Karaman JIT_total_instructions = int(JIT_total_instructions.replace(',', '')) 138*01afa757SAhmed Karaman 139*01afa757SAhmed Karaman # Calculate number of instructions in helpers and code generation 140*01afa757SAhmed Karaman helpers_instructions = JIT_total_instructions-JIT_self_instructions 141*01afa757SAhmed Karaman code_generation_instructions = total_instructions-JIT_total_instructions 142*01afa757SAhmed Karaman 143*01afa757SAhmed Karaman # Print results (Insert commas in large numbers) 144*01afa757SAhmed Karaman # Print total number of instructions 145*01afa757SAhmed Karaman print('{:<20}{:>20}\n'. 146*01afa757SAhmed Karaman format("Total Instructions:", 147*01afa757SAhmed Karaman format(total_instructions, ','))) 148*01afa757SAhmed Karaman # Print code generation instructions and percentage 149*01afa757SAhmed Karaman print('{:<20}{:>20}\t{:>6.3f}%'. 150*01afa757SAhmed Karaman format("Code Generation:", 151*01afa757SAhmed Karaman format(code_generation_instructions, ","), 152*01afa757SAhmed Karaman (code_generation_instructions / total_instructions) * 100)) 153*01afa757SAhmed Karaman # Print JIT instructions and percentage 154*01afa757SAhmed Karaman print('{:<20}{:>20}\t{:>6.3f}%'. 155*01afa757SAhmed Karaman format("JIT Execution:", 156*01afa757SAhmed Karaman format(JIT_self_instructions, ","), 157*01afa757SAhmed Karaman (JIT_self_instructions / total_instructions) * 100)) 158*01afa757SAhmed Karaman # Print helpers instructions and percentage 159*01afa757SAhmed Karaman print('{:<20}{:>20}\t{:>6.3f}%'. 160*01afa757SAhmed Karaman format("Helpers:", 161*01afa757SAhmed Karaman format(helpers_instructions, ","), 162*01afa757SAhmed Karaman (helpers_instructions/total_instructions)*100)) 163*01afa757SAhmed Karaman 164*01afa757SAhmed Karaman 165*01afa757SAhmed Karamanif __name__ == "__main__": 166*01afa757SAhmed Karaman main() 167