llvm-locstats.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #!/usr/bin/env python
  2. #
  3. # This is a tool that works like debug location coverage calculator.
  4. # It parses the llvm-dwarfdump --statistics output by reporting it
  5. # in a more human readable way.
  6. #
  7. from __future__ import print_function
  8. import argparse
  9. import os
  10. import sys
  11. from json import loads
  12. from math import ceil
  13. from subprocess import Popen, PIPE
  14. def coverage_buckets():
  15. yield '0%'
  16. yield '1-9%'
  17. for start in range(10, 91, 10):
  18. yield '{0}-{1}%'.format(start, start + 9)
  19. yield '100%'
  20. def locstats_output(
  21. variables_total,
  22. variables_total_locstats,
  23. variables_with_loc,
  24. scope_bytes_covered,
  25. scope_bytes_from_first_def,
  26. variables_coverage_map
  27. ):
  28. pc_ranges_covered = int(ceil(scope_bytes_covered * 100.0)
  29. / scope_bytes_from_first_def)
  30. variables_coverage_per_map = {}
  31. for cov_bucket in coverage_buckets():
  32. variables_coverage_per_map[cov_bucket] = \
  33. int(ceil(variables_coverage_map[cov_bucket] * 100.0) \
  34. / variables_total_locstats)
  35. print (' =================================================')
  36. print (' Debug Location Statistics ')
  37. print (' =================================================')
  38. print (' cov% samples percentage(~) ')
  39. print (' -------------------------------------------------')
  40. for cov_bucket in coverage_buckets():
  41. print (' {0:6} {1:8d} {2:3d}%'. \
  42. format(cov_bucket, variables_coverage_map[cov_bucket], \
  43. variables_coverage_per_map[cov_bucket]))
  44. print (' =================================================')
  45. print (' -the number of debug variables processed: ' \
  46. + str(variables_total_locstats))
  47. print (' -PC ranges covered: ' + str(pc_ranges_covered) + '%')
  48. # Only if we are processing all the variables output the total
  49. # availability.
  50. if variables_total and variables_with_loc:
  51. total_availability = int(ceil(variables_with_loc * 100.0) \
  52. / variables_total)
  53. print (' -------------------------------------------------')
  54. print (' -total availability: ' + str(total_availability) + '%')
  55. print (' =================================================')
  56. def parse_program_args(parser):
  57. parser.add_argument('-only-variables', action='store_true',
  58. default=False,
  59. help='calculate the location statistics only for '
  60. 'local variables'
  61. )
  62. parser.add_argument('-only-formal-parameters', action='store_true',
  63. default=False,
  64. help='calculate the location statistics only for '
  65. 'formal parameters'
  66. )
  67. parser.add_argument('-ignore-debug-entry-values', action='store_true',
  68. default=False,
  69. help='ignore the location statistics on locations with '
  70. 'entry values'
  71. )
  72. parser.add_argument('file_name', type=str, help='file to process')
  73. return parser.parse_args()
  74. def Main():
  75. parser = argparse.ArgumentParser()
  76. results = parse_program_args(parser)
  77. if len(sys.argv) < 2:
  78. print ('error: Too few arguments.')
  79. parser.print_help()
  80. sys.exit(1)
  81. if results.only_variables and results.only_formal_parameters:
  82. print ('error: Please use just one only* option.')
  83. parser.print_help()
  84. sys.exit(1)
  85. # These will be different due to different options enabled.
  86. variables_total = None
  87. variables_total_locstats = None
  88. variables_with_loc = None
  89. variables_scope_bytes_covered = None
  90. variables_scope_bytes_from_first_def = None
  91. variables_scope_bytes_entry_values = None
  92. variables_coverage_map = {}
  93. binary = results.file_name
  94. # Get the directory of the LLVM tools.
  95. llvm_dwarfdump_cmd = os.path.join(os.path.dirname(__file__), \
  96. "llvm-dwarfdump")
  97. # The statistics llvm-dwarfdump option.
  98. llvm_dwarfdump_stats_opt = "--statistics"
  99. subproc = Popen([llvm_dwarfdump_cmd, llvm_dwarfdump_stats_opt, binary], \
  100. stdin=PIPE, stdout=PIPE, stderr=PIPE, \
  101. universal_newlines = True)
  102. cmd_stdout, cmd_stderr = subproc.communicate()
  103. # Get the JSON and parse it.
  104. json_parsed = None
  105. try:
  106. json_parsed = loads(cmd_stdout)
  107. except:
  108. print ('error: No valid llvm-dwarfdump statistics found.')
  109. sys.exit(1)
  110. if results.only_variables:
  111. # Read the JSON only for local variables.
  112. variables_total_locstats = \
  113. json_parsed['total vars procesed by location statistics']
  114. variables_scope_bytes_covered = \
  115. json_parsed['vars scope bytes covered']
  116. variables_scope_bytes_from_first_def = \
  117. json_parsed['vars scope bytes total']
  118. if not results.ignore_debug_entry_values:
  119. for cov_bucket in coverage_buckets():
  120. cov_category = "vars with {} of its scope covered".format(cov_bucket)
  121. variables_coverage_map[cov_bucket] = json_parsed[cov_category]
  122. else:
  123. variables_scope_bytes_entry_values = \
  124. json_parsed['vars entry value scope bytes covered']
  125. variables_scope_bytes_covered = variables_scope_bytes_covered \
  126. - variables_scope_bytes_entry_values
  127. for cov_bucket in coverage_buckets():
  128. cov_category = \
  129. "vars (excluding the debug entry values) " \
  130. "with {} of its scope covered".format(cov_bucket)
  131. variables_coverage_map[cov_bucket] = json_parsed[cov_category]
  132. elif results.only_formal_parameters:
  133. # Read the JSON only for formal parameters.
  134. variables_total_locstats = \
  135. json_parsed['total params procesed by location statistics']
  136. variables_scope_bytes_covered = \
  137. json_parsed['formal params scope bytes covered']
  138. variables_scope_bytes_from_first_def = \
  139. json_parsed['formal params scope bytes total']
  140. if not results.ignore_debug_entry_values:
  141. for cov_bucket in coverage_buckets():
  142. cov_category = "params with {} of its scope covered".format(cov_bucket)
  143. variables_coverage_map[cov_bucket] = json_parsed[cov_category]
  144. else:
  145. variables_scope_bytes_entry_values = \
  146. json_parsed['formal params entry value scope bytes covered']
  147. variables_scope_bytes_covered = variables_scope_bytes_covered \
  148. - variables_scope_bytes_entry_values
  149. for cov_bucket in coverage_buckets():
  150. cov_category = \
  151. "params (excluding the debug entry values) " \
  152. "with {} of its scope covered".format(cov_bucket)
  153. else:
  154. # Read the JSON for both local variables and formal parameters.
  155. variables_total = \
  156. json_parsed['source variables']
  157. variables_with_loc = json_parsed['variables with location']
  158. variables_total_locstats = \
  159. json_parsed['total variables procesed by location statistics']
  160. variables_scope_bytes_covered = \
  161. json_parsed['scope bytes covered']
  162. variables_scope_bytes_from_first_def = \
  163. json_parsed['scope bytes total']
  164. if not results.ignore_debug_entry_values:
  165. for cov_bucket in coverage_buckets():
  166. cov_category = "variables with {} of its scope covered". \
  167. format(cov_bucket)
  168. variables_coverage_map[cov_bucket] = json_parsed[cov_category]
  169. else:
  170. variables_scope_bytes_entry_values = \
  171. json_parsed['entry value scope bytes covered']
  172. variables_scope_bytes_covered = variables_scope_bytes_covered \
  173. - variables_scope_bytes_entry_values
  174. for cov_bucket in coverage_buckets():
  175. cov_category = "variables (excluding the debug entry values) " \
  176. "with {} of its scope covered". format(cov_bucket)
  177. variables_coverage_map[cov_bucket] = json_parsed[cov_category]
  178. # Pretty print collected info.
  179. locstats_output(
  180. variables_total,
  181. variables_total_locstats,
  182. variables_with_loc,
  183. variables_scope_bytes_covered,
  184. variables_scope_bytes_from_first_def,
  185. variables_coverage_map
  186. )
  187. if __name__ == '__main__':
  188. Main()
  189. sys.exit(0)