1#!/usr/bin/env python
2#
3# Debugify summary for the original debug info testing.
4#
5
6from __future__ import print_function
7import argparse
8import os
9import sys
10from json import loads
11from collections import defaultdict
12from collections import OrderedDict
13
14class DILocBug:
15  def __init__(self, action, bb_name, fn_name, instr):
16    self.action = action
17    self.bb_name = bb_name
18    self.fn_name = fn_name
19    self.instr = instr
20
21class DISPBug:
22  def __init__(self, action, fn_name):
23    self.action = action
24    self.fn_name = fn_name
25
26class DIVarBug:
27  def __init__(self, action, name, fn_name):
28    self.action = action
29    self.name = name
30    self.fn_name = fn_name
31
32# Report the bugs in form of html.
33def generate_html_report(di_location_bugs, di_subprogram_bugs, di_var_bugs, \
34                         di_location_bugs_summary, di_sp_bugs_summary, \
35                         di_var_bugs_summary, html_file):
36  fileout = open(html_file, "w")
37
38  html_header = """ <html>
39  <head>
40  <style>
41  table, th, td {
42    border: 1px solid black;
43  }
44  table.center {
45    margin-left: auto;
46    margin-right: auto;
47  }
48  </style>
49  </head>
50  <body>
51  """
52
53  # Create the table for Location bugs.
54  table_title_di_loc = "Location Bugs found by the Debugify"
55
56  table_di_loc = """<table>
57  <caption><b>{}</b></caption>
58  <tr>
59  """.format(table_title_di_loc)
60
61  header_di_loc = ["File", "LLVM Pass Name", "LLVM IR Instruction", \
62                   "Function Name", "Basic Block Name", "Action"]
63
64  for column in header_di_loc:
65    table_di_loc += "    <th>{0}</th>\n".format(column.strip())
66  table_di_loc += "  </tr>\n"
67
68  at_least_one_bug_found = False
69
70  # Handle loction bugs.
71  for file, per_file_bugs in di_location_bugs.items():
72    for llvm_pass, per_pass_bugs in per_file_bugs.items():
73      # No location bugs for the pass.
74      if len(per_pass_bugs) == 0:
75        continue
76      at_least_one_bug_found = True
77      row = []
78      table_di_loc += "  </tr>\n"
79      # Get the bugs info.
80      for x in per_pass_bugs:
81        row.append("    <tr>\n")
82        row.append(file)
83        row.append(llvm_pass)
84        row.append(x.instr)
85        row.append(x.fn_name)
86        row.append(x.bb_name)
87        row.append(x.action)
88        row.append("    </tr>\n")
89      # Dump the bugs info into the table.
90      for column in row:
91        # The same file-pass pair can have multiple bugs.
92        if (column == "    <tr>\n" or column == "    </tr>\n"):
93          table_di_loc += column
94          continue
95        table_di_loc += "    <td>{0}</td>\n".format(column.strip())
96      table_di_loc += "  <tr>\n"
97
98  if not at_least_one_bug_found:
99    table_di_loc += """  <tr>
100        <td colspan='7'> No bugs found </td>
101      </tr>
102    """
103  table_di_loc += "</table>\n"
104
105  # Create the summary table for the loc bugs.
106  table_title_di_loc_sum = "Summary of Location Bugs"
107  table_di_loc_sum = """<table>
108  <caption><b>{}</b></caption>
109  <tr>
110  """.format(table_title_di_loc_sum)
111
112  header_di_loc_sum = ["LLVM Pass Name", "Number of bugs"]
113
114  for column in header_di_loc_sum:
115    table_di_loc_sum += "    <th>{0}</th>\n".format(column.strip())
116  table_di_loc_sum += "  </tr>\n"
117
118  # Print the summary.
119  row = []
120  for llvm_pass, num in sorted(di_location_bugs_summary.items()):
121    row.append("    <tr>\n")
122    row.append(llvm_pass)
123    row.append(str(num))
124    row.append("    </tr>\n")
125  for column in row:
126    if (column == "    <tr>\n" or column == "    </tr>\n"):
127      table_di_loc_sum += column
128      continue
129    table_di_loc_sum += "    <td>{0}</td>\n".format(column.strip())
130  table_di_loc_sum += "  <tr>\n"
131
132  if not at_least_one_bug_found:
133    table_di_loc_sum += """<tr>
134        <td colspan='2'> No bugs found </td>
135      </tr>
136    """
137  table_di_loc_sum += "</table>\n"
138
139  # Create the table for SP bugs.
140  table_title_di_sp = "SP Bugs found by the Debugify"
141  table_di_sp = """<table>
142  <caption><b>{}</b></caption>
143  <tr>
144  """.format(table_title_di_sp)
145
146  header_di_sp = ["File", "LLVM Pass Name", "Function Name", "Action"]
147
148  for column in header_di_sp:
149    table_di_sp += "    <th>{0}</th>\n".format(column.strip())
150  table_di_sp += "  </tr>\n"
151
152  at_least_one_bug_found = False
153
154  # Handle fn bugs.
155  for file, per_file_bugs in di_subprogram_bugs.items():
156    for llvm_pass, per_pass_bugs in per_file_bugs.items():
157      # No SP bugs for the pass.
158      if len(per_pass_bugs) == 0:
159        continue
160      at_least_one_bug_found = True
161      row = []
162      table_di_sp += "  </tr>\n"
163      # Get the bugs info.
164      for x in per_pass_bugs:
165        row.append("    <tr>\n")
166        row.append(file)
167        row.append(llvm_pass)
168        row.append(x.fn_name)
169        row.append(x.action)
170        row.append("    </tr>\n")
171      # Dump the bugs info into the table.
172      for column in row:
173        # The same file-pass pair can have multiple bugs.
174        if (column == "    <tr>\n" or column == "    </tr>\n"):
175          table_di_sp += column
176          continue
177        table_di_sp += "    <td>{0}</td>\n".format(column.strip())
178      table_di_sp += "  <tr>\n"
179
180  if not at_least_one_bug_found:
181    table_di_sp += """<tr>
182        <td colspan='4'> No bugs found </td>
183      </tr>
184    """
185  table_di_sp += "</table>\n"
186
187  # Create the summary table for the sp bugs.
188  table_title_di_sp_sum = "Summary of SP Bugs"
189  table_di_sp_sum = """<table>
190  <caption><b>{}</b></caption>
191  <tr>
192  """.format(table_title_di_sp_sum)
193
194  header_di_sp_sum = ["LLVM Pass Name", "Number of bugs"]
195
196  for column in header_di_sp_sum:
197    table_di_sp_sum += "    <th>{0}</th>\n".format(column.strip())
198  table_di_sp_sum += "  </tr>\n"
199
200  # Print the summary.
201  row = []
202  for llvm_pass, num in sorted(di_sp_bugs_summary.items()):
203    row.append("    <tr>\n")
204    row.append(llvm_pass)
205    row.append(str(num))
206    row.append("    </tr>\n")
207  for column in row:
208    if (column == "    <tr>\n" or column == "    </tr>\n"):
209      table_di_sp_sum += column
210      continue
211    table_di_sp_sum += "    <td>{0}</td>\n".format(column.strip())
212  table_di_sp_sum += "  <tr>\n"
213
214  if not at_least_one_bug_found:
215    table_di_sp_sum += """<tr>
216        <td colspan='2'> No bugs found </td>
217      </tr>
218    """
219  table_di_sp_sum += "</table>\n"
220
221  # Create the table for Variable bugs.
222  table_title_di_var = "Variable Location Bugs found by the Debugify"
223  table_di_var = """<table>
224  <caption><b>{}</b></caption>
225  <tr>
226  """.format(table_title_di_var)
227
228  header_di_var = ["File", "LLVM Pass Name", "Variable", "Function", "Action"]
229
230  for column in header_di_var:
231    table_di_var += "    <th>{0}</th>\n".format(column.strip())
232  table_di_var += "  </tr>\n"
233
234  at_least_one_bug_found = False
235
236  # Handle var bugs.
237  for file, per_file_bugs in di_var_bugs.items():
238    for llvm_pass, per_pass_bugs in per_file_bugs.items():
239      # No SP bugs for the pass.
240      if len(per_pass_bugs) == 0:
241        continue
242      at_least_one_bug_found = True
243      row = []
244      table_di_var += "  </tr>\n"
245      # Get the bugs info.
246      for x in per_pass_bugs:
247        row.append("    <tr>\n")
248        row.append(file)
249        row.append(llvm_pass)
250        row.append(x.name)
251        row.append(x.fn_name)
252        row.append(x.action)
253        row.append("    </tr>\n")
254      # Dump the bugs info into the table.
255      for column in row:
256        # The same file-pass pair can have multiple bugs.
257        if (column == "    <tr>\n" or column == "    </tr>\n"):
258          table_di_var += column
259          continue
260        table_di_var += "    <td>{0}</td>\n".format(column.strip())
261      table_di_var += "  <tr>\n"
262
263  if not at_least_one_bug_found:
264    table_di_var += """<tr>
265        <td colspan='4'> No bugs found </td>
266      </tr>
267    """
268  table_di_var += "</table>\n"
269
270  # Create the summary table for the sp bugs.
271  table_title_di_var_sum = "Summary of Variable Location Bugs"
272  table_di_var_sum = """<table>
273  <caption><b>{}</b></caption>
274  <tr>
275  """.format(table_title_di_var_sum)
276
277  header_di_var_sum = ["LLVM Pass Name", "Number of bugs"]
278
279  for column in header_di_var_sum:
280    table_di_var_sum += "    <th>{0}</th>\n".format(column.strip())
281  table_di_var_sum += "  </tr>\n"
282
283  # Print the summary.
284  row = []
285  for llvm_pass, num in sorted(di_var_bugs_summary.items()):
286    row.append("    <tr>\n")
287    row.append(llvm_pass)
288    row.append(str(num))
289    row.append("    </tr>\n")
290  for column in row:
291    if (column == "    <tr>\n" or column == "    </tr>\n"):
292      table_di_var_sum += column
293      continue
294    table_di_var_sum += "    <td>{0}</td>\n".format(column.strip())
295  table_di_var_sum += "  <tr>\n"
296
297  if not at_least_one_bug_found:
298    table_di_var_sum += """<tr>
299        <td colspan='2'> No bugs found </td>
300      </tr>
301    """
302  table_di_var_sum += "</table>\n"
303
304  # Finish the html page.
305  html_footer = """</body>
306  </html>"""
307
308  new_line = "<br>\n"
309
310  fileout.writelines(html_header)
311  fileout.writelines(table_di_loc)
312  fileout.writelines(new_line)
313  fileout.writelines(table_di_loc_sum)
314  fileout.writelines(new_line)
315  fileout.writelines(new_line)
316  fileout.writelines(table_di_sp)
317  fileout.writelines(new_line)
318  fileout.writelines(table_di_sp_sum)
319  fileout.writelines(new_line)
320  fileout.writelines(new_line)
321  fileout.writelines(table_di_var)
322  fileout.writelines(new_line)
323  fileout.writelines(table_di_var_sum)
324  fileout.writelines(html_footer)
325  fileout.close()
326
327  print("The " + html_file + " generated.")
328
329# Read the JSON file.
330def get_json(file):
331  json_parsed = None
332  di_checker_data = []
333
334  # The file contains json object per line.
335  # An example of the line (formatted json):
336  # {
337  #  "file": "simple.c",
338  #  "pass": "Deduce function attributes in RPO",
339  #  "bugs": [
340  #    [
341  #      {
342  #        "action": "drop",
343  #        "metadata": "DISubprogram",
344  #        "name": "fn2"
345  #      },
346  #      {
347  #        "action": "drop",
348  #        "metadata": "DISubprogram",
349  #        "name": "fn1"
350  #      }
351  #    ]
352  #  ]
353  #}
354  with open(file) as json_objects_file:
355    for json_object_line in json_objects_file:
356      try:
357        json_object = loads(json_object_line)
358      except:
359        print ("error: No valid di-checker data found.")
360        sys.exit(1)
361      di_checker_data.append(json_object)
362
363  return di_checker_data
364
365# Parse the program arguments.
366def parse_program_args(parser):
367  parser.add_argument("file_name", type=str, help="json file to process")
368  parser.add_argument("html_file", type=str, help="html file to output data")
369
370  return parser.parse_args()
371
372def Main():
373  parser = argparse.ArgumentParser()
374  opts = parse_program_args(parser)
375
376  if not opts.html_file.endswith('.html'):
377    print ("error: The output file must be '.html'.")
378    sys.exit(1)
379
380  debug_info_bugs = get_json(opts.file_name)
381
382  # Use the defaultdict in order to make multidim dicts.
383  di_location_bugs = defaultdict(lambda: defaultdict(dict))
384  di_subprogram_bugs = defaultdict(lambda: defaultdict(dict))
385  di_variable_bugs = defaultdict(lambda: defaultdict(dict))
386
387  # Use the ordered dict to make a summary.
388  di_location_bugs_summary = OrderedDict()
389  di_sp_bugs_summary = OrderedDict()
390  di_var_bugs_summary = OrderedDict()
391
392  # Map the bugs into the file-pass pairs.
393  for bugs_per_pass in debug_info_bugs:
394    bugs_file = bugs_per_pass["file"]
395    bugs_pass = bugs_per_pass["pass"]
396
397    bugs = bugs_per_pass["bugs"][0]
398
399    di_loc_bugs = []
400    di_sp_bugs = []
401    di_var_bugs = []
402
403    for bug in bugs:
404      bugs_metadata = bug["metadata"]
405      if bugs_metadata == "DILocation":
406        action = bug["action"]
407        bb_name = bug["bb-name"]
408        fn_name = bug["fn-name"]
409        instr = bug["instr"]
410        di_loc_bugs.append(DILocBug(action, bb_name, fn_name, instr))
411
412        # Fill the summary dict.
413        if bugs_pass in di_location_bugs_summary:
414          di_location_bugs_summary[bugs_pass] += 1
415        else:
416          di_location_bugs_summary[bugs_pass] = 1
417      elif bugs_metadata == "DISubprogram":
418        action = bug["action"]
419        name = bug["name"]
420        di_sp_bugs.append(DISPBug(action, name))
421
422        # Fill the summary dict.
423        if bugs_pass in di_sp_bugs_summary:
424          di_sp_bugs_summary[bugs_pass] += 1
425        else:
426          di_sp_bugs_summary[bugs_pass] = 1
427      elif bugs_metadata == "dbg-var-intrinsic":
428        action = bug["action"]
429        fn_name = bug["fn-name"]
430        name = bug["name"]
431        di_var_bugs.append(DIVarBug(action, name, fn_name))
432
433        # Fill the summary dict.
434        if bugs_pass in di_var_bugs_summary:
435          di_var_bugs_summary[bugs_pass] += 1
436        else:
437          di_var_bugs_summary[bugs_pass] = 1
438      else:
439        print ("error: Unsupported metadata.")
440        sys.exit(1)
441
442    di_location_bugs[bugs_file][bugs_pass] = di_loc_bugs
443    di_subprogram_bugs[bugs_file][bugs_pass] = di_sp_bugs
444    di_variable_bugs[bugs_file][bugs_pass] = di_var_bugs
445
446  generate_html_report(di_location_bugs, di_subprogram_bugs, di_variable_bugs, \
447                       di_location_bugs_summary, di_sp_bugs_summary, \
448                       di_var_bugs_summary, opts.html_file)
449
450if __name__ == "__main__":
451  Main()
452  sys.exit(0)
453