2
0

dissect.py 6.6 KB

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