mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-18 10:32:48 +02:00
[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
This commit is contained in:
parent
82a7e62f0f
commit
1e88deac13
@ -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
|
||||
----------------------------------------------
|
||||
|
||||
|
@ -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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 {
|
||||
|
@ -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,
|
||||
|
@ -124,6 +124,12 @@ if config.have_ocamlopt:
|
||||
|
||||
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)))
|
||||
|
@ -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"}]]}
|
131
test/tools/llvm-original-di-preservation/Outputs/sample.html
Normal file
131
test/tools/llvm-original-di-preservation/Outputs/sample.html
Normal file
@ -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>
|
2
test/tools/llvm-original-di-preservation/basic.test
Normal file
2
test/tools/llvm-original-di-preservation/basic.test
Normal file
@ -0,0 +1,2 @@
|
||||
RUN: %llvm-original-di-preservation %p/Inputs/sample.json %t.html
|
||||
RUN: diff -w %p/Outputs/sample.html %t.html
|
@ -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
|
||||
|
342
utils/llvm-original-di-preservation.py
Executable file
342
utils/llvm-original-di-preservation.py
Executable file
@ -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)
|
Loading…
Reference in New Issue
Block a user