#!/usr/bin/python # # QProfiler is a QEMU profiler based on QMP # # Copyright (c) 2019-2022 Matias Vara # All Rights Reserved # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # from __future__ import print_function import sys, os, re from qmp import QEMUMonitorProtocol from time import sleep import subprocess def main(args): path = None filename = None # duration of the test in seconds duration = 5 # sampling frequency in seconds frequency = 0.05 while len(args): arg = args[0] if arg.startswith('--'): arg = arg[2:] if arg.find('=') == -1: value = True else: arg, value = arg.split('=', 1) if arg in ['path']: if type(value) == str: path = value elif arg in ['duration']: duration = int(value) elif arg in ['frequency']: frequency = float(value) elif arg in ['filename']: filename = value else: print('Unknown argument "%s"' % arg) return 1 args = args[1:] else: break if not path: print("Path isn't set, use --path=qmp-monitor-address") return 1 def do_command(srv, cmd, **kwds): rsp = srv.cmd(cmd, kwds) if 'error' in rsp: raise Exception(rsp['error']['desc']) return rsp['return'] srv = QEMUMonitorProtocol(path) srv.connect() arguments = {} command = 'human-monitor-command' r = int(duration // frequency) rip_hash = {} for i in range(r): arguments['command-line'] = 'info registers' rsp = do_command(srv, command, **arguments) regs = re.search(r'RIP=([\w]+)\s', rsp) rip = regs.group(1) if rip in rip_hash: rip_hash[rip] += 1 else: rip_hash[rip] = 1 sleep(frequency) srv.close() rip_hash_name = {} for i in rip_hash: with open(os.devnull, 'w') as devnull: # pass tmp = subprocess.check_output("addr2line --demangle -p -s -f -e " + filename + " " + i , shell=True, stderr=devnull).rstrip() if tmp in rip_hash_name: rip_hash_name[tmp] += rip_hash[i] else: rip_hash_name[tmp] = rip_hash[i] for i in rip_hash_name: print('{:>8} {}'.format(rip_hash_name[i], i)) if __name__ == '__main__': sys.exit(main(sys.argv[1:]))