Index: llvm/docs/HowToUpdateDebugInfo.rst =================================================================== --- llvm/docs/HowToUpdateDebugInfo.rst +++ llvm/docs/HowToUpdateDebugInfo.rst @@ -302,6 +302,21 @@ $ opt -enable-debugify=original -pass-to-test sample.ll +Another useful option (especially with the `-debugify-each=original`) to use with +the `original` mode is exporting the issues that have been found into a JSON +file as follows: + +.. code-block:: bash + + $ opt -enable-debugify=original -debugify-original-export=sample.json -pass-to-test sample.ll + +and then use the ``llvm/utils/llvm-debugify-original.py`` script to generate +an HTML page with the issues reported in a more human readable form as follows: + +.. code-block:: bash + + $ llvm-debugify-original.py sample.json sample.html + Using ``debugify`` ^^^^^^^^^^^^^^^^^^ Index: llvm/include/llvm/IR/LegacyPassManager.h =================================================================== --- llvm/include/llvm/IR/LegacyPassManager.h +++ llvm/include/llvm/IR/LegacyPassManager.h @@ -100,6 +100,7 @@ class DebugifyCustomPassManager : public legacy::PassManager { DebugifyStatsMap DIStatsMap; DebugInfoPerPassMap DIPreservationMap; + StringRef DebugifyOriginalBugsReportFilePath = ""; bool EnableDebugifyEachSynthetic = false; bool EnableDebugifyEachOriginal = false; @@ -137,7 +138,7 @@ super::add(P); super::add(createCheckDebugifyFunctionPass( false, Name, nullptr, DebugifyMode::OriginalDebugInfo, - &DIPreservationMap)); + &DIPreservationMap, getDebugifyEachOriginalFilePath())); } break; case PT_Module: @@ -152,7 +153,7 @@ super::add(P); super::add(createCheckDebugifyModulePass( false, Name, nullptr, DebugifyMode::OriginalDebugInfo, - &DIPreservationMap)); + &DIPreservationMap, getDebugifyEachOriginalFilePath())); } break; default: @@ -167,6 +168,14 @@ const DebugifyStatsMap &getDebugifyStatsMap() const { return DIStatsMap; } DebugInfoPerPassMap &getDebugInfoPerPassMap() { return DIPreservationMap; } + + void setDebugifyOriginalBugsReportFilePath(StringRef BugsReportFilePath) { + DebugifyOriginalBugsReportFilePath = BugsReportFilePath; + } + + StringRef getDebugifyEachOriginalFilePath() const { + return DebugifyOriginalBugsReportFilePath; + } }; } // End legacy namespace Index: llvm/include/llvm/Transforms/Utils/Debugify.h =================================================================== --- llvm/include/llvm/Transforms/Utils/Debugify.h +++ llvm/include/llvm/Transforms/Utils/Debugify.h @@ -76,7 +76,8 @@ bool checkDebugInfoMetadata(Module &M, iterator_range Functions, DebugInfoPerPassMap &DIPreservationMap, - StringRef Banner, StringRef NameOfWrappedPass); + StringRef Banner, StringRef NameOfWrappedPass, + StringRef DebugifyOriginalBugsReportFilePath); } // namespace llvm /// Used to check whether we track synthetic or original debug info. @@ -128,13 +129,15 @@ bool Strip = false, llvm::StringRef NameOfWrappedPass = "", DebugifyStatsMap *StatsMap = nullptr, enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, - DebugInfoPerPassMap *DIPreservationMap = nullptr); + DebugInfoPerPassMap *DIPreservationMap = nullptr, + llvm::StringRef DebugifyOriginalBugsReportFilePath = ""); llvm::FunctionPass *createCheckDebugifyFunctionPass( bool Strip = false, llvm::StringRef NameOfWrappedPass = "", DebugifyStatsMap *StatsMap = nullptr, enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, - DebugInfoPerPassMap *DIPreservationMap = nullptr); + DebugInfoPerPassMap *DIPreservationMap = nullptr, + llvm::StringRef DebugifyOriginalBugsReportFilePath = ""); struct NewPMCheckDebugifyPass : public llvm::PassInfoMixin { Index: llvm/lib/Transforms/Utils/Debugify.cpp =================================================================== --- llvm/lib/Transforms/Utils/Debugify.cpp +++ llvm/lib/Transforms/Utils/Debugify.cpp @@ -24,6 +24,7 @@ #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/JSON.h" #define DEBUG_TYPE "debugify" @@ -310,15 +311,22 @@ static bool checkFunctions(DebugFnMap &DIFunctionsBefore, DebugFnMap &DIFunctionsAfter, StringRef NameOfWrappedPass, - StringRef FileNameFromCU) { + StringRef FileNameFromCU, bool ShouldWriteIntoJSON, + llvm::json::Array &Bugs) { bool Preserved = true; for (const auto &F : DIFunctionsAfter) { if (!F.second) { auto SPIt = DIFunctionsBefore.find(F.first); if (SPIt == DIFunctionsBefore.end()) { - dbg() << "ERROR: " << NameOfWrappedPass - << " did not generate DISubprogram for " << F.first << " from " - << FileNameFromCU << '\n'; + if (ShouldWriteIntoJSON) { + Bugs.push_back(llvm::json::Object({{"metadata", "DISubprogram"}, + {"name", F.first}, + {"action", "not-generate"}})); + } else { + dbg() << "ERROR: " << NameOfWrappedPass + << " did not generate DISubprogram for " << F.first << " from " + << FileNameFromCU << '\n'; + } if (Preserved) Preserved = false; } else { @@ -326,8 +334,15 @@ // If the function had the SP attacched before the pass, consider it as // a debug info bug. if (SP) { - dbg() << "ERROR: " << NameOfWrappedPass << " dropped DISubprogram of " - << F.first << " from " << FileNameFromCU << '\n'; + if (ShouldWriteIntoJSON) { + Bugs.push_back(llvm::json::Object({{"metadata", "DISubprogram"}, + {"name", F.first}, + {"action", "drop"}})); + } else { + dbg() << "ERROR: " << NameOfWrappedPass + << " dropped DISubprogram of " << F.first << " from " + << FileNameFromCU << '\n'; + } if (Preserved) Preserved = false; } @@ -340,10 +355,10 @@ // This checks the preservation of the original debug info attached to // instructions. -static bool checkInstructions(DebugInstMap &DILocsBefore, - DebugInstMap &DILocsAfter, - StringRef NameOfWrappedPass, - StringRef FileNameFromCU) { +static bool +checkInstructions(DebugInstMap &DILocsBefore, DebugInstMap &DILocsAfter, + StringRef NameOfWrappedPass, StringRef FileNameFromCU, + bool ShouldWriteIntoJSON, llvm::json::Array &Bugs) { bool Preserved = true; for (const auto &L : DILocsAfter) { if (!L.second) { @@ -351,13 +366,22 @@ auto FnName = Instr->getFunction()->getName(); auto BB = Instr->getParent(); auto BBName = BB->hasName() ? BB->getName() : "no-name"; + auto InstName = Instruction::getOpcodeName(Instr->getOpcode()); auto InstrIt = DILocsBefore.find(Instr); if (InstrIt == DILocsBefore.end()) { - dbg() << "ERROR: " << NameOfWrappedPass - << " did not generate DILocation for " << *Instr - << " (BB: " << BBName << ", Fn: " << FnName - << ", File: " << FileNameFromCU << ")\n"; + if (ShouldWriteIntoJSON) { + Bugs.push_back(llvm::json::Object({{"metadata", "DILocation"}, + {"fn-name", FnName.str()}, + {"bb-name", BBName.str()}, + {"instr", InstName}, + {"action", "not-generate"}})); + } else { + dbg() << "ERROR: " << NameOfWrappedPass + << " did not generate DILocation for " << *Instr + << " (BB: " << BBName << ", Fn: " << FnName + << ", File: " << FileNameFromCU << ")\n"; + } if (Preserved) Preserved = false; } else { @@ -365,9 +389,17 @@ // If the instr had the !dbg attacched before the pass, consider it as // a debug info bug. if (hadLoc) { - dbg() << "ERROR: " << NameOfWrappedPass << " dropped DILocation of " - << *Instr << " (BB: " << BBName << ", Fn: " << FnName - << ", File: " << FileNameFromCU << ")\n"; + if (ShouldWriteIntoJSON) { + Bugs.push_back(llvm::json::Object({{"metadata", "DILocation"}, + {"fn-name", FnName.str()}, + {"bb-name", BBName.str()}, + {"instr", InstName}, + {"action", "drop"}})); + } else { + dbg() << "ERROR: " << NameOfWrappedPass << " dropped DILocation of " + << *Instr << " (BB: " << BBName << ", Fn: " << FnName + << ", File: " << FileNameFromCU << ")\n"; + } if (Preserved) Preserved = false; } @@ -378,11 +410,32 @@ return Preserved; } -bool llvm::checkDebugInfoMetadata(Module &M, - iterator_range Functions, - DebugInfoPerPassMap &DIPreservationMap, - StringRef Banner, - StringRef NameOfWrappedPass) { +// Write the json data into the specifed file. +static void writeJSON(StringRef DebugifyOriginalBugsReportFilePath, + StringRef FileNameFromCU, StringRef NameOfWrappedPass, + llvm::json::Array &Bugs) { + std::error_code EC; + raw_fd_ostream OS_FILE{DebugifyOriginalBugsReportFilePath, EC, + sys::fs::OF_Append | sys::fs::OF_Text}; + if (EC) { + errs() << "Could not open file: " << EC.message() << ", " + << DebugifyOriginalBugsReportFilePath << '\n'; + return; + } + + OS_FILE << "{\"file\":\"" << FileNameFromCU << "\", "; + OS_FILE << "\"pass\":\"" << NameOfWrappedPass << "\", "; + + llvm::json::Value BugsToPrint{std::move(Bugs)}; + OS_FILE << "\"bugs\": " << BugsToPrint; + + OS_FILE << "}\n"; +} + +bool llvm::checkDebugInfoMetadata( + Module &M, iterator_range Functions, + DebugInfoPerPassMap &DIPreservationMap, StringRef Banner, + StringRef NameOfWrappedPass, StringRef DebugifyOriginalBugsReportFilePath) { LLVM_DEBUG(dbgs() << Banner << " (after) " << NameOfWrappedPass << '\n'); if (!M.getNamedMetadata("llvm.dbg.cu")) { @@ -443,12 +496,21 @@ auto DILocsBefore = DIPreservationMap[NameOfWrappedPass].DILocations; auto DILocsAfter = DIPreservationAfter[NameOfWrappedPass].DILocations; - bool Result = checkFunctions(DIFunctionsBefore, DIFunctionsAfter, - NameOfWrappedPass, FileNameFromCU) && - checkInstructions(DILocsBefore, DILocsAfter, NameOfWrappedPass, - FileNameFromCU); + bool ShouldWriteIntoJSON = !DebugifyOriginalBugsReportFilePath.empty(); + llvm::json::Array Bugs; + + bool Result = + checkFunctions(DIFunctionsBefore, DIFunctionsAfter, NameOfWrappedPass, + FileNameFromCU, ShouldWriteIntoJSON, Bugs) && + checkInstructions(DILocsBefore, DILocsAfter, NameOfWrappedPass, + FileNameFromCU, ShouldWriteIntoJSON, Bugs); StringRef ResultBanner = NameOfWrappedPass != "" ? NameOfWrappedPass : Banner; + + if (ShouldWriteIntoJSON && !Bugs.empty()) + writeJSON(DebugifyOriginalBugsReportFilePath, FileNameFromCU, ResultBanner, + Bugs); + if (Result) dbg() << ResultBanner << ": PASS\n"; else @@ -666,15 +728,18 @@ "CheckModuleDebugify", Strip, StatsMap); return checkDebugInfoMetadata( M, M.functions(), *DIPreservationMap, - "CheckModuleDebugify(original): ", NameOfWrappedPass); + "CheckModuleDebugify(original): ", NameOfWrappedPass, + DebugifyOriginalBugsReportFilePath); } CheckDebugifyModulePass( bool Strip = false, StringRef NameOfWrappedPass = "", DebugifyStatsMap *StatsMap = nullptr, enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, - DebugInfoPerPassMap *DIPreservationMap = nullptr) + DebugInfoPerPassMap *DIPreservationMap = nullptr, + StringRef DebugifyOriginalBugsReportFilePath = "") : ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass), + DebugifyOriginalBugsReportFilePath(DebugifyOriginalBugsReportFilePath), StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode), Strip(Strip) {} @@ -686,6 +751,7 @@ private: StringRef NameOfWrappedPass; + StringRef DebugifyOriginalBugsReportFilePath; DebugifyStatsMap *StatsMap; DebugInfoPerPassMap *DIPreservationMap; enum DebugifyMode Mode; @@ -704,15 +770,18 @@ Strip, StatsMap); return checkDebugInfoMetadata( M, make_range(FuncIt, std::next(FuncIt)), *DIPreservationMap, - "CheckFunctionDebugify(original): ", NameOfWrappedPass); + "CheckFunctionDebugify(original): ", NameOfWrappedPass, + DebugifyOriginalBugsReportFilePath); } CheckDebugifyFunctionPass( bool Strip = false, StringRef NameOfWrappedPass = "", DebugifyStatsMap *StatsMap = nullptr, enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo, - DebugInfoPerPassMap *DIPreservationMap = nullptr) + DebugInfoPerPassMap *DIPreservationMap = nullptr, + StringRef DebugifyOriginalBugsReportFilePath = "") : FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass), + DebugifyOriginalBugsReportFilePath(DebugifyOriginalBugsReportFilePath), StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode), Strip(Strip) {} @@ -724,6 +793,7 @@ private: StringRef NameOfWrappedPass; + StringRef DebugifyOriginalBugsReportFilePath; DebugifyStatsMap *StatsMap; DebugInfoPerPassMap *DIPreservationMap; enum DebugifyMode Mode; @@ -759,22 +829,26 @@ ModulePass *createCheckDebugifyModulePass( bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap, - enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap) { + enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap, + StringRef DebugifyOriginalBugsReportFilePath) { if (Mode == DebugifyMode::SyntheticDebugInfo) return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap); assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode"); return new CheckDebugifyModulePass(false, NameOfWrappedPass, nullptr, Mode, - DIPreservationMap); + DIPreservationMap, + DebugifyOriginalBugsReportFilePath); } FunctionPass *createCheckDebugifyFunctionPass( bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap, - enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap) { + enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap, + StringRef DebugifyOriginalBugsReportFilePath) { if (Mode == DebugifyMode::SyntheticDebugInfo) return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap); assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode"); return new CheckDebugifyFunctionPass(false, NameOfWrappedPass, nullptr, Mode, - DIPreservationMap); + DIPreservationMap, + DebugifyOriginalBugsReportFilePath); } PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M, Index: llvm/test/DebugInfo/debugify-each-original-dbginfo.ll =================================================================== --- llvm/test/DebugInfo/debugify-each-original-dbginfo.ll +++ llvm/test/DebugInfo/debugify-each-original-dbginfo.ll @@ -2,9 +2,15 @@ ; RUN: opt -O2 -debugify-each=original -S -o - < %s 2>&1 | FileCheck %s +; RUN: rm -rf %t.json +; RUN: opt -O2 -debugify-each=original -debugify-original-export=%t.json -S -o - < %s +; RUN: cat %t.json | FileCheck %s -check-prefix=CHECK-JSON + ; CHECK: ERROR: Combine redundant instructions {{.*}} ; CHECK-NEXT: Combine redundant instructions: FAIL +; CHECK-JSON: {"file":"simple.c", "pass":"Combine redundant instructions", "bugs": {{.*}} + ; ModuleID = 'simple.c' source_filename = "simple.c" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" Index: llvm/tools/opt/opt.cpp =================================================================== --- llvm/tools/opt/opt.cpp +++ llvm/tools/opt/opt.cpp @@ -233,6 +233,13 @@ "Original Debug Info")), cl::init(DebugifyMode::NoDebugify)); +static cl::opt + DebugifyOriginalExportToJson("debugify-original-export", + cl::desc("Export debugify (in original mode) failures into " + "specified (JSON) file (should be abs path as we use" + " append mode to insert new JSON objects)"), + cl::value_desc("filename"), cl::init("")); + static cl::opt DebugifyExport("debugify-export", cl::desc("Export per-pass debugify (in synthetic mode) " @@ -736,10 +743,13 @@ // about to build. If the -debugify-each option is set, wrap each pass with // the (-check)-debugify passes. llvm::legacy::DebugifyCustomPassManager Passes; - if (DebugifyEach == DebugifyMode::SyntheticDebugInfo) + if (DebugifyEach == DebugifyMode::SyntheticDebugInfo) { Passes.enableDebugifyEachSynthetic(); - else if (DebugifyEach == DebugifyMode::OriginalDebugInfo) + } else if (DebugifyEach == DebugifyMode::OriginalDebugInfo) { Passes.enableDebugifyEachOriginal(); + if (!DebugifyOriginalExportToJson.empty()) + Passes.setDebugifyOriginalBugsReportFilePath(DebugifyOriginalExportToJson); + } bool AddOneTimeDebugifyPasses = EnableDebugify != DebugifyMode::NoDebugify && DebugifyEach == DebugifyMode::NoDebugify; Index: llvm/utils/llvm-debugify-original.py =================================================================== --- /dev/null +++ llvm/utils/llvm-debugify-original.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python +# +# Debugify summary for the original debug info testing. +# + +from __future__ import print_function +import argparse +import os +import sys +from json import loads +from collections import defaultdict +from collections import OrderedDict + +class DILocBug: + def __init__(self, action, bb_name, fn_name, instr): + self.action = action + self.bb_name = bb_name + self.fn_name = fn_name + self.instr = instr + +class DISPBug: + def __init__(self, action, fn_name): + self.action = action + self.fn_name = fn_name + +# Report the bugs in form of html. +def generate_html_report(di_location_bugs, di_subprogram_bugs, \ + di_location_bugs_summary, di_sp_bugs_summary, \ + html_file): + fileout = open(html_file, "w") + + html_header = "\n" + html_header += "\n" + html_header += "\n" + html_header += "\n" + html_header += "\n" + + # Create the table for Location bugs. + table_di_loc = "\n" + + # Create the table's column headers. + table_title_di_loc = "Location Bugs found by the LLVM DI Checker" + table_di_loc += "\n" + + header_di_loc = ["File", "LLVM Pass Name", "LLVM IR Instruction", \ + "Function Name", "Basic Block Name", "Action"] + table_di_loc += " \n" + + for column in header_di_loc: + table_di_loc += " \n".format(column.strip()) + table_di_loc += " \n" + + at_least_one_bug_found = False + + # Handle loction bugs. + for file, per_file_bugs in di_location_bugs.items(): + for llvm_pass, per_pass_bugs in per_file_bugs.items(): + # No location bugs for the pass. + if len(per_pass_bugs) == 0: + continue + at_least_one_bug_found = True + row = [] + table_di_loc += " \n" + # Get the bugs info. + for x in per_pass_bugs: + row.append(" \n") + row.append(file) + row.append(llvm_pass) + row.append(x.instr) + row.append(x.fn_name) + row.append(x.bb_name) + row.append(x.action) + row.append(" \n") + # Dump the bugs info into the table. + for column in row: + # The same file-pass pair can have multiple bugs. + if (column == " \n" or column == " \n"): + table_di_loc += column + continue + table_di_loc += " \n".format(column.strip()) + table_di_loc += " \n" + + if not at_least_one_bug_found: + table_di_loc += " \n" + table_di_loc += " \n" + table_di_loc += " \n" + + table_di_loc += "
" + table_di_loc += table_title_di_loc + table_di_loc += "
{0}
{0}
No bugs found
\n" + + # Create the summary table for the loc bugs. + table_di_loc_sum = "\n" + + # Create the table's column headers. + table_title_di_loc_sum = "DI Checker Summary of Location Bugs" + table_di_loc_sum += "\n" + + header_di_loc_sum = ["LLVM Pass Name", "Number of bugs"] + table_di_loc_sum += " \n" + + for column in header_di_loc_sum: + table_di_loc_sum += " \n".format(column.strip()) + table_di_loc_sum += " \n" + + # Print the summary. + row = [] + for llvm_pass, num in sorted(di_location_bugs_summary.items()): + row.append(" \n") + row.append(llvm_pass) + row.append(str(num)) + row.append(" \n") + for column in row: + if (column == " \n" or column == " \n"): + table_di_loc_sum += column + continue + table_di_loc_sum += " \n".format(column.strip()) + table_di_loc_sum += " \n" + + if not at_least_one_bug_found: + table_di_loc_sum += " \n" + table_di_loc_sum += " \n" + table_di_loc_sum += " \n" + table_di_loc_sum += "
" + table_di_loc_sum += table_title_di_loc_sum + table_di_loc_sum += "
{0}
{0}
No bugs found
\n" + + # Create the table for SP bugs. + table_di_sp = "\n" + + # Create the table's column headers. + table_title_di_sp = "SP Bugs found by the LLVM DI Checker" + table_di_sp += "\n" + + header_di_sp = ["File", "LLVM Pass Name", "Function Name", "Action"] + table_di_sp += " \n" + + for column in header_di_sp: + table_di_sp += " \n".format(column.strip()) + table_di_sp += " \n" + + at_least_one_bug_found = False + + # Handle loction bugs. + for file, per_file_bugs in di_subprogram_bugs.items(): + for llvm_pass, per_pass_bugs in per_file_bugs.items(): + # No SP bugs for the pass. + if len(per_pass_bugs) == 0: + continue + at_least_one_bug_found = True + row = [] + table_di_sp += " \n" + # Get the bugs info. + for x in per_pass_bugs: + row.append(" \n") + row.append(file) + row.append(llvm_pass) + row.append(x.fn_name) + row.append(x.action) + row.append(" \n") + # Dump the bugs info into the table. + for column in row: + # The same file-pass pair can have multiple bugs. + if (column == " \n" or column == " \n"): + table_di_sp += column + continue + table_di_sp += " \n".format(column.strip()) + table_di_sp += " \n" + + if not at_least_one_bug_found: + table_di_sp += " \n" + table_di_sp += " \n" + table_di_sp += " \n" + + table_di_sp += "
" + table_di_sp += table_title_di_sp + table_di_sp += "
{0}
{0}
No bugs found
\n" + + # Create the summary table for the sp bugs. + table_di_sp_sum = "\n" + + # Create the table's column headers. + table_title_di_sp_sum = "DI Checker Summary of SP Bugs" + table_di_sp_sum += "\n" + + header_di_sp_sum = ["LLVM Pass Name", "Number of bugs"] + table_di_sp_sum += " \n" + + for column in header_di_sp_sum: + table_di_sp_sum += " \n".format(column.strip()) + table_di_sp_sum += " \n" + + # Print the summary. + row = [] + for llvm_pass, num in sorted(di_sp_bugs_summary.items()): + row.append(" \n") + row.append(llvm_pass) + row.append(str(num)) + row.append(" \n") + for column in row: + if (column == " \n" or column == " \n"): + table_di_sp_sum += column + continue + table_di_sp_sum += " \n".format(column.strip()) + table_di_sp_sum += " \n" + + if not at_least_one_bug_found: + table_di_sp_sum += " \n" + table_di_sp_sum += " \n" + table_di_sp_sum += " \n" + table_di_sp_sum += "
" + table_di_sp_sum += table_title_di_sp_sum + table_di_sp_sum += "
{0}
{0}
No bugs found
\n" + + + # Finish the html page. + html_footer = "\n" + html_footer += "\n" + new_line = "
\n" + + fileout.writelines(html_header) + fileout.writelines(table_di_loc) + fileout.writelines(new_line) + fileout.writelines(table_di_loc_sum) + fileout.writelines(new_line) + fileout.writelines(new_line) + fileout.writelines(table_di_sp) + fileout.writelines(new_line) + fileout.writelines(table_di_sp_sum) + fileout.writelines(html_footer) + fileout.close() + + print("The " + html_file + " generated.") + +# Read the JSON file. +def get_json(file): + json_parsed = None + di_checker_data = [] + + # The file contains json object per line. + # An example of the line (formatted json): + # { + # "file": "simple.c", + # "pass": "Deduce function attributes in RPO", + # "bugs": [ + # [ + # { + # "action": "drop", + # "metadata": "DISubprogram", + # "name": "fn2" + # }, + # { + # "action": "drop", + # "metadata": "DISubprogram", + # "name": "fn1" + # } + # ] + # ] + #} + with open(file) as json_objects_file: + for json_object_line in json_objects_file: + try: + json_object = loads(json_object_line) + except: + print ("error: No valid di-checker data found.") + sys.exit(1) + di_checker_data.append(json_object) + + return di_checker_data + +# Parse the program arguments. +def parse_program_args(parser): + parser.add_argument("file_name", type=str, help="json file to process") + parser.add_argument("html_file", type=str, help="html file to output data") + + return parser.parse_args() + +def Main(): + parser = argparse.ArgumentParser() + opts = parse_program_args(parser) + + if not opts.html_file.endswith('.html'): + print ("error: The output file must be '.html'.") + sys.exit(1) + + debug_info_bugs = get_json(opts.file_name) + + # Use the defaultdict in order to make multidim dicts. + di_location_bugs = defaultdict(lambda: defaultdict(dict)) + di_subprogram_bugs = defaultdict(lambda: defaultdict(dict)) + + # Use the ordered dict to make a summary. + di_location_bugs_summary = OrderedDict() + di_sp_bugs_summary = OrderedDict() + + # Map the bugs into the file-pass pairs. + for bugs_per_pass in debug_info_bugs: + bugs_file = bugs_per_pass["file"] + bugs_pass = bugs_per_pass["pass"] + + bugs = bugs_per_pass["bugs"][0] + + di_loc_bugs = [] + di_sp_bugs = [] + for bug in bugs: + bugs_metadata = bug["metadata"] + if bugs_metadata == "DILocation": + action = bug["action"] + bb_name = bug["bb-name"] + fn_name = bug["fn-name"] + instr = bug["instr"] + di_loc_bugs.append(DILocBug(action, bb_name, fn_name, instr)) + + # Fill the summary dict. + if bugs_pass in di_location_bugs_summary: + di_location_bugs_summary[bugs_pass] += 1 + else: + di_location_bugs_summary[bugs_pass] = 1 + elif bugs_metadata == "DISubprogram": + action = bug["action"] + name = bug["name"] + di_sp_bugs.append(DISPBug(action, name)) + + # Fill the summary dict. + if bugs_pass in di_sp_bugs_summary: + di_sp_bugs_summary[bugs_pass] += 1 + else: + di_sp_bugs_summary[bugs_pass] = 1 + else: + print ("error: Only DILocation and DISubprogram are supported.") + sys.exit(1) + + di_location_bugs[bugs_file][bugs_pass] = di_loc_bugs + di_subprogram_bugs[bugs_file][bugs_pass] = di_sp_bugs + + generate_html_report(di_location_bugs, di_subprogram_bugs, \ + di_location_bugs_summary, di_sp_bugs_summary, \ + opts.html_file) + +if __name__ == "__main__": + Main() + sys.exit(0)