[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