#!/usr/bin/python
#
# QProfiler is a QEMU profiler based on QMP
#
# Copyright (c) 2019-2022 Matias Vara <matiasevara@gmail.com>
# 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 <http://www.gnu.org/licenses/>.
#
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:]))