[llvm] 9f41c03 - [Debugify][OriginalDIMode] Export the report into JSON file

Djordje Todorovic via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 11 01:11:42 PST 2021


Author: Djordje Todorovic
Date: 2021-03-11T01:11:13-08:00
New Revision: 9f41c03f82874075c30b97b8ff71eff70f313fa0

URL: https://github.com/llvm/llvm-project/commit/9f41c03f82874075c30b97b8ff71eff70f313fa0
DIFF: https://github.com/llvm/llvm-project/commit/9f41c03f82874075c30b97b8ff71eff70f313fa0.diff

LOG: [Debugify][OriginalDIMode] Export the report into JSON file

By using the original-di check with debugify in the combination with
the llvm/utils/llvm-original-di-preservation.py it becomes very user
friendly tool. An example of the HTML page with the issues
related to debug info can be found at [0].

[0] https://djolertrk.github.io/di-checker-html-report-example/

Differential Revision: https://reviews.llvm.org/D82546

Added: 
    llvm/test/tools/llvm-original-di-preservation/Inputs/sample.json
    llvm/test/tools/llvm-original-di-preservation/Outputs/sample.html
    llvm/test/tools/llvm-original-di-preservation/basic.test
    llvm/utils/llvm-original-di-preservation.py

Modified: 
    llvm/docs/HowToUpdateDebugInfo.rst
    llvm/include/llvm/Transforms/Utils/Debugify.h
    llvm/lib/Transforms/Utils/Debugify.cpp
    llvm/test/lit.cfg.py
    llvm/tools/opt/opt.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/HowToUpdateDebugInfo.rst b/llvm/docs/HowToUpdateDebugInfo.rst
index 1abd76e26e3b..b9bdc485b487 100644
--- a/llvm/docs/HowToUpdateDebugInfo.rst
+++ b/llvm/docs/HowToUpdateDebugInfo.rst
@@ -361,6 +361,21 @@ pre-existing debug info metadata. It could be run as follows:
   # Check the preservation of original Debug Info after each pass.
   $ opt -verify-each-debuginfo-preserve -O2 sample.ll
 
+Furthermore, there is a way to export 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
+
 Mutation testing for MIR-level transformations
 ----------------------------------------------
 

diff  --git a/llvm/include/llvm/Transforms/Utils/Debugify.h b/llvm/include/llvm/Transforms/Utils/Debugify.h
index 91bc5fb92a6f..b17f9aee50f6 100644
--- a/llvm/include/llvm/Transforms/Utils/Debugify.h
+++ b/llvm/include/llvm/Transforms/Utils/Debugify.h
@@ -84,7 +84,8 @@ bool collectDebugInfoMetadata(Module &M,
 bool checkDebugInfoMetadata(Module &M,
                             iterator_range<Module::iterator> 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.
@@ -136,13 +137,15 @@ llvm::ModulePass *createCheckDebugifyModulePass(
     bool Strip = false, llvm::StringRef NameOfWrappedPass = "",
     DebugifyStatsMap *StatsMap = nullptr,
     enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
-    DebugInfoPerPassMap *DIPreservationMap = nullptr);
+    DebugInfoPerPassMap *DIPreservationMap = nullptr,
+    llvm::StringRef OrigDIVerifyBugsReportFilePath = "");
 
 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 OrigDIVerifyBugsReportFilePath = "");
 
 struct NewPMCheckDebugifyPass
     : public llvm::PassInfoMixin<NewPMCheckDebugifyPass> {
@@ -163,6 +166,7 @@ struct DebugifyEachInstrumentation {
 /// NOTE: We support legacy custom pass manager only.
 /// TODO: Add New PM support for custom pass manager.
 class DebugifyCustomPassManager : public legacy::PassManager {
+  StringRef OrigDIVerifyBugsReportFilePath;
   DebugifyStatsMap *DIStatsMap = nullptr;
   DebugInfoPerPassMap *DIPreservationMap = nullptr;
   enum DebugifyMode Mode = DebugifyMode::NoDebugify;
@@ -173,10 +177,9 @@ class DebugifyCustomPassManager : public legacy::PassManager {
   void add(Pass *P) override {
     // Wrap each pass with (-check)-debugify passes if requested, making
     // exceptions for passes which shouldn't see -debugify instrumentation.
-    bool WrapWithDebugify =
-        Mode != DebugifyMode::NoDebugify &&
-        !P->getAsImmutablePass() && !isIRPrintingPass(P) &&
-        !isBitcodeWriterPass(P);
+    bool WrapWithDebugify = Mode != DebugifyMode::NoDebugify &&
+                            !P->getAsImmutablePass() && !isIRPrintingPass(P) &&
+                            !isBitcodeWriterPass(P);
     if (!WrapWithDebugify) {
       super::add(P);
       return;
@@ -194,13 +197,15 @@ class DebugifyCustomPassManager : public legacy::PassManager {
       super::add(createDebugifyFunctionPass(Mode, Name, DIPreservationMap));
       super::add(P);
       super::add(createCheckDebugifyFunctionPass(
-          isSyntheticDebugInfo(), Name, DIStatsMap, Mode, DIPreservationMap));
+          isSyntheticDebugInfo(), Name, DIStatsMap, Mode, DIPreservationMap,
+          OrigDIVerifyBugsReportFilePath));
       break;
     case PT_Module:
       super::add(createDebugifyModulePass(Mode, Name, DIPreservationMap));
       super::add(P);
       super::add(createCheckDebugifyModulePass(
-          isSyntheticDebugInfo(), Name, DIStatsMap, Mode, DIPreservationMap));
+          isSyntheticDebugInfo(), Name, DIStatsMap, Mode, DIPreservationMap,
+          OrigDIVerifyBugsReportFilePath));
       break;
     default:
       super::add(P);
@@ -214,6 +219,13 @@ class DebugifyCustomPassManager : public legacy::PassManager {
   void setDIPreservationMap(DebugInfoPerPassMap &PerPassMap) {
     DIPreservationMap = &PerPassMap;
   }
+  void setOrigDIVerifyBugsReportFilePath(StringRef BugsReportFilePath) {
+    OrigDIVerifyBugsReportFilePath = BugsReportFilePath;
+  }
+  StringRef getOrigDIVerifyBugsReportFilePath() const {
+    return OrigDIVerifyBugsReportFilePath;
+  }
+
   void setDebugifyMode(enum DebugifyMode M) { Mode = M; }
 
   bool isSyntheticDebugInfo() const {

diff  --git a/llvm/lib/Transforms/Utils/Debugify.cpp b/llvm/lib/Transforms/Utils/Debugify.cpp
index 7e6ce8c166e6..73a3a4063f6b 100644
--- a/llvm/lib/Transforms/Utils/Debugify.cpp
+++ b/llvm/lib/Transforms/Utils/Debugify.cpp
@@ -25,6 +25,7 @@
 #include "llvm/IR/PassInstrumentation.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/JSON.h"
 
 #define DEBUG_TYPE "debugify"
 
@@ -334,16 +335,22 @@ bool llvm::collectDebugInfoMetadata(Module &M,
 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()) {
-      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';
       Preserved = false;
     } else {
       auto SP = SPIt->second;
@@ -351,8 +358,13 @@ static bool checkFunctions(const DebugFnMap &DIFunctionsBefore,
         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;
     }
   }
@@ -366,7 +378,9 @@ static bool checkInstructions(const DebugInstMap &DILocsBefore,
                               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)
@@ -382,22 +396,37 @@ static bool checkInstructions(const DebugInstMap &DILocsBefore,
     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;
     }
   }
@@ -405,11 +434,35 @@ static bool checkInstructions(const DebugInstMap &DILocsBefore,
   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 << "\", ";
+
+  StringRef PassName = NameOfWrappedPass != "" ? NameOfWrappedPass : "no-name";
+  OS_FILE << "\"pass\":\"" << PassName << "\", ";
+
+  llvm::json::Value BugsToPrint{std::move(Bugs)};
+  OS_FILE << "\"bugs\": " << BugsToPrint;
+
+  OS_FILE << "}\n";
+}
+
 bool llvm::checkDebugInfoMetadata(Module &M,
                                   iterator_range<Module::iterator> Functions,
                                   DebugInfoPerPassMap &DIPreservationMap,
-                                  StringRef Banner,
-                                  StringRef NameOfWrappedPass) {
+                                  StringRef Banner, StringRef NameOfWrappedPass,
+                                  StringRef OrigDIVerifyBugsReportFilePath) {
   LLVM_DEBUG(dbgs() << Banner << ": (after) " << NameOfWrappedPass << '\n');
 
   if (!M.getNamedMetadata("llvm.dbg.cu")) {
@@ -428,7 +481,8 @@ bool llvm::checkDebugInfoMetadata(Module &M,
     // TODO: Collect metadata other than DISubprograms.
     // Collect the DISubprogram.
     auto *SP = F.getSubprogram();
-    DIPreservationAfter[NameOfWrappedPass].DIFunctions.insert({F.getName(), SP});
+    DIPreservationAfter[NameOfWrappedPass].DIFunctions.insert(
+        {F.getName(), SP});
     if (SP)
       LLVM_DEBUG(dbgs() << "  Collecting subprogram: " << *SP << '\n');
 
@@ -467,14 +521,22 @@ bool llvm::checkDebugInfoMetadata(Module &M,
 
   auto InstToDelete = DIPreservationAfter[NameOfWrappedPass].InstToDelete;
 
-  bool ResultForFunc = checkFunctions(DIFunctionsBefore, DIFunctionsAfter,
-                                      NameOfWrappedPass, FileNameFromCU);
-  bool ResultForInsts =
-      checkInstructions(DILocsBefore, DILocsAfter, InstToDelete,
-                        NameOfWrappedPass, FileNameFromCU);
+  bool ShouldWriteIntoJSON = !OrigDIVerifyBugsReportFilePath.empty();
+  llvm::json::Array Bugs;
+
+  bool ResultForFunc =
+      checkFunctions(DIFunctionsBefore, DIFunctionsAfter, NameOfWrappedPass,
+                     FileNameFromCU, ShouldWriteIntoJSON, Bugs);
+  bool ResultForInsts = checkInstructions(
+      DILocsBefore, DILocsAfter, InstToDelete, NameOfWrappedPass,
+      FileNameFromCU, ShouldWriteIntoJSON, Bugs);
   bool Result = ResultForFunc && ResultForInsts;
 
   StringRef ResultBanner = NameOfWrappedPass != "" ? NameOfWrappedPass : Banner;
+  if (ShouldWriteIntoJSON && !Bugs.empty())
+    writeJSON(OrigDIVerifyBugsReportFilePath, FileNameFromCU, NameOfWrappedPass,
+              Bugs);
+
   if (Result)
     dbg() << ResultBanner << ": PASS\n";
   else
@@ -680,15 +742,18 @@ struct CheckDebugifyModulePass : public ModulePass {
                                    "CheckModuleDebugify", Strip, StatsMap);
     return checkDebugInfoMetadata(
         M, M.functions(), *DIPreservationMap,
-        "CheckModuleDebugify (original debuginfo)", NameOfWrappedPass);
+        "CheckModuleDebugify (original debuginfo)", NameOfWrappedPass,
+        OrigDIVerifyBugsReportFilePath);
   }
 
   CheckDebugifyModulePass(
       bool Strip = false, StringRef NameOfWrappedPass = "",
       DebugifyStatsMap *StatsMap = nullptr,
       enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
-      DebugInfoPerPassMap *DIPreservationMap = nullptr)
+      DebugInfoPerPassMap *DIPreservationMap = nullptr,
+      StringRef OrigDIVerifyBugsReportFilePath = "")
       : ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass),
+        OrigDIVerifyBugsReportFilePath(OrigDIVerifyBugsReportFilePath),
         StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode),
         Strip(Strip) {}
 
@@ -700,6 +765,7 @@ struct CheckDebugifyModulePass : public ModulePass {
 
 private:
   StringRef NameOfWrappedPass;
+  StringRef OrigDIVerifyBugsReportFilePath;
   DebugifyStatsMap *StatsMap;
   DebugInfoPerPassMap *DIPreservationMap;
   enum DebugifyMode Mode;
@@ -718,15 +784,18 @@ struct CheckDebugifyFunctionPass : public FunctionPass {
                                    Strip, StatsMap);
     return checkDebugInfoMetadata(
         M, make_range(FuncIt, std::next(FuncIt)), *DIPreservationMap,
-        "CheckFunctionDebugify (original debuginfo)", NameOfWrappedPass);
+        "CheckFunctionDebugify (original debuginfo)", NameOfWrappedPass,
+        OrigDIVerifyBugsReportFilePath);
   }
 
   CheckDebugifyFunctionPass(
       bool Strip = false, StringRef NameOfWrappedPass = "",
       DebugifyStatsMap *StatsMap = nullptr,
       enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
-      DebugInfoPerPassMap *DIPreservationMap = nullptr)
+      DebugInfoPerPassMap *DIPreservationMap = nullptr,
+      StringRef OrigDIVerifyBugsReportFilePath = "")
       : FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass),
+        OrigDIVerifyBugsReportFilePath(OrigDIVerifyBugsReportFilePath),
         StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode),
         Strip(Strip) {}
 
@@ -738,6 +807,7 @@ struct CheckDebugifyFunctionPass : public FunctionPass {
 
 private:
   StringRef NameOfWrappedPass;
+  StringRef OrigDIVerifyBugsReportFilePath;
   DebugifyStatsMap *StatsMap;
   DebugInfoPerPassMap *DIPreservationMap;
   enum DebugifyMode Mode;
@@ -794,22 +864,26 @@ PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) {
 
 ModulePass *createCheckDebugifyModulePass(
     bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap,
-    enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap) {
+    enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap,
+    StringRef OrigDIVerifyBugsReportFilePath) {
   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,
+                                     OrigDIVerifyBugsReportFilePath);
 }
 
 FunctionPass *createCheckDebugifyFunctionPass(
     bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap,
-    enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap) {
+    enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap,
+    StringRef OrigDIVerifyBugsReportFilePath) {
   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,
+                                       OrigDIVerifyBugsReportFilePath);
 }
 
 PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M,

diff  --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index f862e62cc17e..2a1ccc2dcfbd 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -124,6 +124,12 @@ def get_asan_rtlib():
 
 opt_viewer_cmd = '%s %s/tools/opt-viewer/opt-viewer.py' % (sys.executable, config.llvm_src_root)
 
+llvm_original_di_preservation_cmd = os.path.join(
+    config.llvm_src_root,'utils', 'llvm-original-di-preservation.py')
+config.substitutions.append(
+    ('%llvm-original-di-preservation', "'%s' %s" % (
+        config.python_executable, llvm_original_di_preservation_cmd)))
+
 llvm_locstats_tool = os.path.join(config.llvm_tools_dir, 'llvm-locstats')
 config.substitutions.append(
     ('%llvm-locstats', "'%s' %s" % (config.python_executable, llvm_locstats_tool)))

diff  --git a/llvm/test/tools/llvm-original-di-preservation/Inputs/sample.json b/llvm/test/tools/llvm-original-di-preservation/Inputs/sample.json
new file mode 100644
index 000000000000..ee6d2fa7988d
--- /dev/null
+++ b/llvm/test/tools/llvm-original-di-preservation/Inputs/sample.json
@@ -0,0 +1 @@
+{"file":"test.ll", "pass":"no-name", "bugs": [[{"action":"not-generate","bb-name":"no-name","fn-name":"fn","instr":"extractvalue","metadata":"DILocation"},{"action":"not-generate","bb-name":"no-name","fn-name":"fn","instr":"insertvalue","metadata":"DILocation"},{"action":"not-generate","bb-name":"no-name","fn-name":"fn","instr":"extractvalue","metadata":"DILocation"},{"action":"not-generate","bb-name":"no-name","fn-name":"fn1","instr":"insertvalue","metadata":"DILocation"},{"action":"not-generate","bb-name":"no-name","fn-name":"fn1","instr":"insertvalue","metadata":"DILocation"},{"action":"not-generate","bb-name":"no-name","fn-name":"fn","instr":"insertvalue","metadata":"DILocation"},{"action":"not-generate","bb-name":"no-name","fn-name":"fn1","instr":"extractvalue","metadata":"DILocation"},{"action":"not-generate","bb-name":"no-name","fn-name":"fn1","instr":"extractvalue","metadata":"DILocation"}]]}

diff  --git a/llvm/test/tools/llvm-original-di-preservation/Outputs/sample.html b/llvm/test/tools/llvm-original-di-preservation/Outputs/sample.html
new file mode 100644
index 000000000000..5c5de52999af
--- /dev/null
+++ b/llvm/test/tools/llvm-original-di-preservation/Outputs/sample.html
@@ -0,0 +1,131 @@
+ <html>
+  <head>
+  <style>
+  table, th, td {
+    border: 1px solid black;
+  }
+  table.center {
+    margin-left: auto;
+    margin-right: auto;
+  }
+  </style>
+  </head>
+  <body>
+  <table>
+  <caption><b>Location Bugs found by the Debugify</b></caption>
+  <tr>
+      <th>File</th>
+    <th>LLVM Pass Name</th>
+    <th>LLVM IR Instruction</th>
+    <th>Function Name</th>
+    <th>Basic Block Name</th>
+    <th>Action</th>
+  </tr>
+  </tr>
+    <tr>
+    <td>test.ll</td>
+    <td>no-name</td>
+    <td>extractvalue</td>
+    <td>fn</td>
+    <td>no-name</td>
+    <td>not-generate</td>
+    </tr>
+    <tr>
+    <td>test.ll</td>
+    <td>no-name</td>
+    <td>insertvalue</td>
+    <td>fn</td>
+    <td>no-name</td>
+    <td>not-generate</td>
+    </tr>
+    <tr>
+    <td>test.ll</td>
+    <td>no-name</td>
+    <td>extractvalue</td>
+    <td>fn</td>
+    <td>no-name</td>
+    <td>not-generate</td>
+    </tr>
+    <tr>
+    <td>test.ll</td>
+    <td>no-name</td>
+    <td>insertvalue</td>
+    <td>fn1</td>
+    <td>no-name</td>
+    <td>not-generate</td>
+    </tr>
+    <tr>
+    <td>test.ll</td>
+    <td>no-name</td>
+    <td>insertvalue</td>
+    <td>fn1</td>
+    <td>no-name</td>
+    <td>not-generate</td>
+    </tr>
+    <tr>
+    <td>test.ll</td>
+    <td>no-name</td>
+    <td>insertvalue</td>
+    <td>fn</td>
+    <td>no-name</td>
+    <td>not-generate</td>
+    </tr>
+    <tr>
+    <td>test.ll</td>
+    <td>no-name</td>
+    <td>extractvalue</td>
+    <td>fn1</td>
+    <td>no-name</td>
+    <td>not-generate</td>
+    </tr>
+    <tr>
+    <td>test.ll</td>
+    <td>no-name</td>
+    <td>extractvalue</td>
+    <td>fn1</td>
+    <td>no-name</td>
+    <td>not-generate</td>
+    </tr>
+  <tr>
+</table>
+<br>
+<table>
+  <caption><b>Summary of Location Bugs</b></caption>
+  <tr>
+      <th>LLVM Pass Name</th>
+    <th>Number of bugs</th>
+  </tr>
+    <tr>
+    <td>no-name</td>
+    <td>8</td>
+    </tr>
+  <tr>
+</table>
+<br>
+<br>
+<table>
+  <caption><b>SP Bugs found by the Debugify</b></caption>
+  <tr>
+      <th>File</th>
+    <th>LLVM Pass Name</th>
+    <th>Function Name</th>
+    <th>Action</th>
+  </tr>
+<tr>
+        <td colspan='4'> No bugs found </td>
+      </tr>
+    </table>
+<br>
+<table>
+  <caption><b>Summary of SP Bugs</b></caption>
+  <tr>
+      <th>LLVM Pass Name</th>
+    <th>Number of bugs</th>
+  </tr>
+  <tr>
+<tr>
+        <td colspan='2'> No bugs found </td>
+      </tr>
+    </table>
+</body>
+  </html>
\ No newline at end of file

diff  --git a/llvm/test/tools/llvm-original-di-preservation/basic.test b/llvm/test/tools/llvm-original-di-preservation/basic.test
new file mode 100644
index 000000000000..dc87cd6d6da5
--- /dev/null
+++ b/llvm/test/tools/llvm-original-di-preservation/basic.test
@@ -0,0 +1,2 @@
+RUN: %llvm-original-di-preservation %p/Inputs/sample.json %t.html
+RUN: 
diff  -w %p/Outputs/sample.html %t.html

diff  --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp
index f690d5f1a789..0a7c68e9c4c6 100644
--- a/llvm/tools/opt/opt.cpp
+++ b/llvm/tools/opt/opt.cpp
@@ -229,6 +229,13 @@ static cl::opt<bool> VerifyEachDebugInfoPreserve(
     cl::desc("Start each pass with collecting and end it with checking of "
              "debug info preservation."));
 
+static cl::opt<std::string>
+    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<bool>
 PrintBreakpoints("print-breakpoints-for-testing",
                  cl::desc("Print select breakpoints location for testing"));
@@ -837,6 +844,8 @@ int main(int argc, char **argv) {
   } else if (VerifyEachDebugInfoPreserve) {
     Passes.setDebugifyMode(DebugifyMode::OriginalDebugInfo);
     Passes.setDIPreservationMap(DIPreservationMap);
+    if (!VerifyDIPreserveExport.empty())
+      Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport);
   }
 
   bool AddOneTimeDebugifyPasses =
@@ -1006,10 +1015,13 @@ int main(int argc, char **argv) {
   if (AddOneTimeDebugifyPasses) {
     if (EnableDebugify)
       Passes.add(createCheckDebugifyModulePass(false));
-    else if (VerifyDebugInfoPreserve)
+    else if (VerifyDebugInfoPreserve) {
+      if (!VerifyDIPreserveExport.empty())
+        Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport);
       Passes.add(createCheckDebugifyModulePass(
           false, "", nullptr, DebugifyMode::OriginalDebugInfo,
-          &(Passes.getDebugInfoPerPassMap())));
+          &(Passes.getDebugInfoPerPassMap()), VerifyDIPreserveExport));
+    }
   }
 
   // In run twice mode, we want to make sure the output is bit-by-bit

diff  --git a/llvm/utils/llvm-original-di-preservation.py b/llvm/utils/llvm-original-di-preservation.py
new file mode 100755
index 000000000000..dd3161a60dbc
--- /dev/null
+++ b/llvm/utils/llvm-original-di-preservation.py
@@ -0,0 +1,342 @@
+#!/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 = """ <html>
+  <head>
+  <style>
+  table, th, td {
+    border: 1px solid black;
+  }
+  table.center {
+    margin-left: auto;
+    margin-right: auto;
+  }
+  </style>
+  </head>
+  <body>
+  """
+
+  # Create the table for Location bugs.
+  table_title_di_loc = "Location Bugs found by the Debugify"
+
+  table_di_loc = """<table>
+  <caption><b>{}</b></caption>
+  <tr>
+  """.format(table_title_di_loc)
+
+  header_di_loc = ["File", "LLVM Pass Name", "LLVM IR Instruction", \
+                   "Function Name", "Basic Block Name", "Action"]
+
+  for column in header_di_loc:
+    table_di_loc += "    <th>{0}</th>\n".format(column.strip())
+  table_di_loc += "  </tr>\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 += "  </tr>\n"
+      # Get the bugs info.
+      for x in per_pass_bugs:
+        row.append("    <tr>\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("    </tr>\n")
+      # Dump the bugs info into the table.
+      for column in row:
+        # The same file-pass pair can have multiple bugs.
+        if (column == "    <tr>\n" or column == "    </tr>\n"):
+          table_di_loc += column
+          continue
+        table_di_loc += "    <td>{0}</td>\n".format(column.strip())
+      table_di_loc += "  <tr>\n"
+
+  if not at_least_one_bug_found:
+    table_di_loc += """  <tr>
+        <td colspan='7'> No bugs found </td>
+      </tr>
+    """
+  table_di_loc += "</table>\n"
+
+  # Create the summary table for the loc bugs.
+  table_title_di_loc_sum = "Summary of Location Bugs"
+  table_di_loc_sum = """<table>
+  <caption><b>{}</b></caption>
+  <tr>
+  """.format(table_title_di_loc_sum)
+
+  header_di_loc_sum = ["LLVM Pass Name", "Number of bugs"]
+
+  for column in header_di_loc_sum:
+    table_di_loc_sum += "    <th>{0}</th>\n".format(column.strip())
+  table_di_loc_sum += "  </tr>\n"
+
+  # Print the summary.
+  row = []
+  for llvm_pass, num in sorted(di_location_bugs_summary.items()):
+    row.append("    <tr>\n")
+    row.append(llvm_pass)
+    row.append(str(num))
+    row.append("    </tr>\n")
+  for column in row:
+    if (column == "    <tr>\n" or column == "    </tr>\n"):
+      table_di_loc_sum += column
+      continue
+    table_di_loc_sum += "    <td>{0}</td>\n".format(column.strip())
+  table_di_loc_sum += "  <tr>\n"
+
+  if not at_least_one_bug_found:
+    table_di_loc_sum += """<tr>
+        <td colspan='2'> No bugs found </td>
+      </tr>
+    """
+  table_di_loc_sum += "</table>\n"
+
+  # Create the table for SP bugs.
+  table_title_di_sp = "SP Bugs found by the Debugify"
+  table_di_sp = """<table>
+  <caption><b>{}</b></caption>
+  <tr>
+  """.format(table_title_di_sp)
+
+  header_di_sp = ["File", "LLVM Pass Name", "Function Name", "Action"]
+
+  for column in header_di_sp:
+    table_di_sp += "    <th>{0}</th>\n".format(column.strip())
+  table_di_sp += "  </tr>\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 += "  </tr>\n"
+      # Get the bugs info.
+      for x in per_pass_bugs:
+        row.append("    <tr>\n")
+        row.append(file)
+        row.append(llvm_pass)
+        row.append(x.fn_name)
+        row.append(x.action)
+        row.append("    </tr>\n")
+      # Dump the bugs info into the table.
+      for column in row:
+        # The same file-pass pair can have multiple bugs.
+        if (column == "    <tr>\n" or column == "    </tr>\n"):
+          table_di_sp += column
+          continue
+        table_di_sp += "    <td>{0}</td>\n".format(column.strip())
+      table_di_sp += "  <tr>\n"
+
+  if not at_least_one_bug_found:
+    table_di_sp += """<tr>
+        <td colspan='4'> No bugs found </td>
+      </tr>
+    """
+  table_di_sp += "</table>\n"
+
+  # Create the summary table for the sp bugs.
+  table_title_di_sp_sum = "Summary of SP Bugs"
+  table_di_sp_sum = """<table>
+  <caption><b>{}</b></caption>
+  <tr>
+  """.format(table_title_di_sp_sum)
+
+  header_di_sp_sum = ["LLVM Pass Name", "Number of bugs"]
+
+  for column in header_di_sp_sum:
+    table_di_sp_sum += "    <th>{0}</th>\n".format(column.strip())
+  table_di_sp_sum += "  </tr>\n"
+
+  # Print the summary.
+  row = []
+  for llvm_pass, num in sorted(di_sp_bugs_summary.items()):
+    row.append("    <tr>\n")
+    row.append(llvm_pass)
+    row.append(str(num))
+    row.append("    </tr>\n")
+  for column in row:
+    if (column == "    <tr>\n" or column == "    </tr>\n"):
+      table_di_sp_sum += column
+      continue
+    table_di_sp_sum += "    <td>{0}</td>\n".format(column.strip())
+  table_di_sp_sum += "  <tr>\n"
+
+  if not at_least_one_bug_found:
+    table_di_sp_sum += """<tr>
+        <td colspan='2'> No bugs found </td>
+      </tr>
+    """
+  table_di_sp_sum += "</table>\n"
+
+  # Finish the html page.
+  html_footer = """</body>
+  </html>"""
+
+  new_line = "<br>\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)


        


More information about the llvm-commits mailing list