Index: llvm/docs/HowToUpdateDebugInfo.rst =================================================================== --- llvm/docs/HowToUpdateDebugInfo.rst +++ llvm/docs/HowToUpdateDebugInfo.rst @@ -302,6 +302,21 @@ $ opt -verify-debuginfo-preserve -pass-to-test sample.ll +Another useful option to use with the `original-di-check` mode is exporting +the issues that have been found into a JSON file as follows: + +.. code-block:: bash + + $ opt -verify-debuginfo-preserve -verify-di-preserve-export=sample.json -pass-to-test sample.ll + +and then use the ``llvm/utils/llvm-original-di-preservation.py`` script +to generate an HTML page with the issues reported in a more human readable form +as follows: + +.. code-block:: bash + + $ llvm-original-di-preservation.py sample.json sample.html + Using ``VerifyDIPreserve`` ^^^^^^^^^^^^^^^^^^^^^^^^^^ Index: llvm/include/llvm/Transforms/Utils/VerifyDIPreserve.h =================================================================== --- llvm/include/llvm/Transforms/Utils/VerifyDIPreserve.h +++ llvm/include/llvm/Transforms/Utils/VerifyDIPreserve.h @@ -83,7 +83,8 @@ bool checkDebugInfoMetadata(Module &M, iterator_range Functions, DebugInfoPerPassMap &DIPreservationMap, - StringRef Banner, StringRef NameOfWrappedPass); + StringRef Banner, StringRef NameOfWrappedPass, + StringRef OrigDIVerifyBugsReportFilePath); } // namespace llvm /// Used to check whether we track synthetic or original debug info. @@ -138,13 +139,15 @@ bool Strip = false, llvm::StringRef NameOfWrappedPass = "", DebugifyStatsMap *StatsMap = nullptr, enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, - DebugInfoPerPassMap *DIPreservationMap = nullptr); + DebugInfoPerPassMap *DIPreservationMap = nullptr, + llvm::StringRef OrigDIVerifyBugsReportFilePath = ""); llvm::FunctionPass *createCheckDIFunctionPass( bool Strip = false, llvm::StringRef NameOfWrappedPass = "", DebugifyStatsMap *StatsMap = nullptr, enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, - DebugInfoPerPassMap *DIPreservationMap = nullptr); + DebugInfoPerPassMap *DIPreservationMap = nullptr, + llvm::StringRef OrigDIVerifyBugsReportFilePath = ""); struct NewPMCheckDebugifyPass : public llvm::PassInfoMixin { @@ -157,6 +160,7 @@ /// NOTE: We support legacy custom pass manager only. /// TODO: Add New PM support for custom pass manager. class VerifyDIPreserveCustomPassManager : public legacy::PassManager { + StringRef OrigDIVerifyBugsReportFilePath = ""; DebugifyStatsMap *DIStatsMap = nullptr; DebugInfoPerPassMap *DIPreservationMap = nullptr; enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::NoVerify; @@ -187,13 +191,15 @@ super::add(createPrepareDIFunctionPass(Mode, Name, DIPreservationMap)); super::add(P); super::add(createCheckDIFunctionPass(isDebugify(), Name, DIStatsMap, Mode, - DIPreservationMap)); + DIPreservationMap, + OrigDIVerifyBugsReportFilePath)); break; case PT_Module: super::add(createPrepareDIModulePass(Mode, Name, DIPreservationMap)); super::add(P); super::add(createCheckDIModulePass(isDebugify(), Name, DIStatsMap, Mode, - DIPreservationMap)); + DIPreservationMap, + OrigDIVerifyBugsReportFilePath)); break; default: super::add(P); @@ -213,6 +219,13 @@ Mode = M; } + void setOrigDIVerifyBugsReportFilePath(StringRef BugsReportFilePath) { + OrigDIVerifyBugsReportFilePath = BugsReportFilePath; + } + StringRef getOrigDIVerifyBugsReportFilePath() const { + return OrigDIVerifyBugsReportFilePath; + } + bool isDebugify() const { return Mode == VerifyDIPreserveMode::Debugify; } bool isOriginalDebugInfoPreserveVerify() const { return Mode == VerifyDIPreserveMode::OriginalDebugInfo; Index: llvm/lib/Transforms/Utils/VerifyDIPreserve.cpp =================================================================== --- llvm/lib/Transforms/Utils/VerifyDIPreserve.cpp +++ llvm/lib/Transforms/Utils/VerifyDIPreserve.cpp @@ -27,6 +27,7 @@ #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/JSON.h" #define DEBUG_TYPE "verify-di-preserve" @@ -308,13 +309,19 @@ static bool checkFunctions(const DebugFnMap &DIFunctionsBefore, const 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) continue; auto SPIt = DIFunctionsBefore.find(F.first); if (SPIt == DIFunctionsBefore.end()) { + 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'; @@ -325,8 +332,13 @@ continue; // If the function had the SP attached before the pass, consider it as // a debug info bug. - 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'; Preserved = false; } } @@ -340,7 +352,9 @@ const DebugInstMap &DILocsAfter, const WeakInstValueMap &InstToDelete, StringRef NameOfWrappedPass, - StringRef FileNameFromCU) { + StringRef FileNameFromCU, + bool ShouldWriteIntoJSON, + llvm::json::Array &Bugs) { bool Preserved = true; for (const auto &L : DILocsAfter) { if (L.second) @@ -356,22 +370,37 @@ 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() << "WARNING: " << 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() << "WARNING: " << NameOfWrappedPass + << " did not generate DILocation for " << *Instr + << " (BB: " << BBName << ", Fn: " << FnName + << ", File: " << FileNameFromCU << ")\n"; Preserved = false; } else { if (!InstrIt->second) continue; // If the instr had the !dbg attached before the pass, consider it as // a debug info issue. - dbg() << "WARNING: " << 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() << "WARNING: " << NameOfWrappedPass << " dropped DILocation of " + << *Instr << " (BB: " << BBName << ", Fn: " << FnName + << ", File: " << FileNameFromCU << ")\n"; Preserved = false; } } @@ -379,11 +408,34 @@ return Preserved; } +// Write the json data into the specifed file. +static void writeJSON(StringRef OrigDIVerifyBugsReportFilePath, + StringRef FileNameFromCU, StringRef NameOfWrappedPass, + llvm::json::Array &Bugs) { + std::error_code EC; + raw_fd_ostream OS_FILE{OrigDIVerifyBugsReportFilePath, EC, + sys::fs::OF_Append | sys::fs::OF_Text}; + if (EC) { + errs() << "Could not open file: " << EC.message() << ", " + << OrigDIVerifyBugsReportFilePath << '\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 NameOfWrappedPass, + StringRef OrigDIVerifyBugsReportFilePath) { LLVM_DEBUG(dbgs() << Banner << " (after) " << NameOfWrappedPass << '\n'); if (!M.getNamedMetadata("llvm.dbg.cu")) { @@ -440,14 +492,23 @@ auto DILocsBefore = DIPreservationMap[NameOfWrappedPass].DILocations; auto DILocsAfter = DIPreservationAfter[NameOfWrappedPass].DILocations; + bool ShouldWriteIntoJSON = !OrigDIVerifyBugsReportFilePath.empty(); + llvm::json::Array Bugs; auto InstToDelete = DIPreservationAfter[NameOfWrappedPass].InstToDelete; - bool Result = checkFunctions(DIFunctionsBefore, DIFunctionsAfter, - NameOfWrappedPass, FileNameFromCU) && - checkInstructions(DILocsBefore, DILocsAfter, InstToDelete, - NameOfWrappedPass, FileNameFromCU); + bool Result = + checkFunctions(DIFunctionsBefore, DIFunctionsAfter, NameOfWrappedPass, + FileNameFromCU, ShouldWriteIntoJSON, Bugs) && + checkInstructions(DILocsBefore, DILocsAfter, InstToDelete, + NameOfWrappedPass, FileNameFromCU, ShouldWriteIntoJSON, + Bugs); StringRef ResultBanner = NameOfWrappedPass != "" ? NameOfWrappedPass : Banner; + + if (ShouldWriteIntoJSON && !Bugs.empty()) + writeJSON(OrigDIVerifyBugsReportFilePath, FileNameFromCU, ResultBanner, + Bugs); + if (Result) dbg() << ResultBanner << ": PASS\n"; else @@ -671,15 +732,18 @@ StatsMap); return checkDebugInfoMetadata( M, M.functions(), *DIPreservationMap, - "CheckModuleVerifyDIPreserve(original-di-check): ", NameOfWrappedPass); + "CheckModuleVerifyDIPreserve(original-di-check): ", NameOfWrappedPass, + OrigDIVerifyBugsReportFilePath); } CheckDIModulePass( bool Strip = false, StringRef NameOfWrappedPass = "", DebugifyStatsMap *StatsMap = nullptr, enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, - DebugInfoPerPassMap *DIPreservationMap = nullptr) + DebugInfoPerPassMap *DIPreservationMap = nullptr, + StringRef OrigDIVerifyBugsReportFilePath = "") : ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass), + OrigDIVerifyBugsReportFilePath(OrigDIVerifyBugsReportFilePath), StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode), Strip(Strip) {} @@ -691,6 +755,7 @@ private: StringRef NameOfWrappedPass; + StringRef OrigDIVerifyBugsReportFilePath; DebugifyStatsMap *StatsMap; DebugInfoPerPassMap *DIPreservationMap; enum VerifyDIPreserveMode Mode; @@ -712,15 +777,17 @@ return checkDebugInfoMetadata( M, make_range(FuncIt, std::next(FuncIt)), *DIPreservationMap, "CheckFunctionVerifyDIPreserve(original-di-check): ", - NameOfWrappedPass); + NameOfWrappedPass, OrigDIVerifyBugsReportFilePath); } CheckDIFunctionPass( bool Strip = false, StringRef NameOfWrappedPass = "", DebugifyStatsMap *StatsMap = nullptr, enum VerifyDIPreserveMode Mode = VerifyDIPreserveMode::Debugify, - DebugInfoPerPassMap *DIPreservationMap = nullptr) + DebugInfoPerPassMap *DIPreservationMap = nullptr, + StringRef OrigDIVerifyBugsReportFilePath = "") : FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass), + OrigDIVerifyBugsReportFilePath(OrigDIVerifyBugsReportFilePath), StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode), Strip(Strip) {} @@ -732,6 +799,7 @@ private: StringRef NameOfWrappedPass; + StringRef OrigDIVerifyBugsReportFilePath; DebugifyStatsMap *StatsMap; DebugInfoPerPassMap *DIPreservationMap; enum VerifyDIPreserveMode Mode; @@ -770,24 +838,28 @@ ModulePass *createCheckDIModulePass(bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap, enum VerifyDIPreserveMode Mode, - DebugInfoPerPassMap *DIPreservationMap) { + DebugInfoPerPassMap *DIPreservationMap, + StringRef OrigDIVerifyBugsReportFilePath) { if (Mode == VerifyDIPreserveMode::Debugify) return new CheckDIModulePass(Strip, NameOfWrappedPass, StatsMap); assert(Mode == VerifyDIPreserveMode::OriginalDebugInfo && "Must be original-di-check mode"); return new CheckDIModulePass(false, NameOfWrappedPass, nullptr, Mode, - DIPreservationMap); + DIPreservationMap, + OrigDIVerifyBugsReportFilePath); } FunctionPass *createCheckDIFunctionPass( bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap, - enum VerifyDIPreserveMode Mode, DebugInfoPerPassMap *DIPreservationMap) { + enum VerifyDIPreserveMode Mode, DebugInfoPerPassMap *DIPreservationMap, + StringRef OrigDIVerifyBugsReportFilePath) { if (Mode == VerifyDIPreserveMode::Debugify) return new CheckDIFunctionPass(Strip, NameOfWrappedPass, StatsMap); assert(Mode == VerifyDIPreserveMode::OriginalDebugInfo && "Must be original-di-check mode"); return new CheckDIFunctionPass(false, NameOfWrappedPass, nullptr, Mode, - DIPreservationMap); + DIPreservationMap, + OrigDIVerifyBugsReportFilePath); } PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M, Index: llvm/test/DebugInfo/orig-di-preserve-verify-each-dbginfo.ll =================================================================== --- llvm/test/DebugInfo/orig-di-preserve-verify-each-dbginfo.ll +++ llvm/test/DebugInfo/orig-di-preserve-verify-each-dbginfo.ll @@ -2,9 +2,15 @@ ; RUN: opt -O2 -verify-each-debuginfo-preserve -S -o - < %s 2>&1 | FileCheck %s +; RUN: rm -rf %t.json +; RUN: opt -O2 -verify-each-debuginfo-preserve -verify-di-preserve-export=%t.json -S -o - < %s +; RUN: cat %t.json | FileCheck %s -check-prefix=CHECK-JSON + ; CHECK: WARNING: 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 @@ -223,6 +223,13 @@ cl::desc("Start each pass with collecting and end it with checking of " "debug info preservation.")); +static cl::opt + VerifyDIPreserveExport("verify-di-preserve-export", + cl::desc("Export debug info preservation 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) " @@ -820,6 +827,8 @@ } else if (VerifyEachDebugInfoPreserve) { Passes.setVerifyDIPreserveMode(VerifyDIPreserveMode::OriginalDebugInfo); Passes.setDIPreservationMap(DIPreservationMap); + if (!VerifyDIPreserveExport.empty()) + Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport); } bool AddOneTimeDebugifyPasses = Index: llvm/utils/llvm-original-di-preservation.py =================================================================== --- /dev/null +++ llvm/utils/llvm-original-di-preservation.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)