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