Skip to content

Commit b0d8a8b

Browse files
committed
Refactor AnalysisPrinter to take a llvm::raw_ostream& in onFinalize() and provide a sensible default for LLVM-based analyses.
Fixes missing emit-text-report output in phasar-cli
1 parent e5e20ba commit b0d8a8b

22 files changed

+318
-155
lines changed

include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818
#include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h"
1919
#include "phasar/DataFlow/IfdsIde/InitialSeeds.h"
2020
#include "phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h"
21-
#include "phasar/DataFlow/IfdsIde/SolverResults.h"
21+
#include "phasar/Utils/DefaultAnalysisPrinterSelector.h"
2222
#include "phasar/Utils/JoinLattice.h"
23+
#include "phasar/Utils/MaybeUniquePtr.h"
2324
#include "phasar/Utils/NullAnalysisPrinter.h"
2425
#include "phasar/Utils/SemiRing.h"
2526
#include "phasar/Utils/Soundness.h"
2627

28+
#include "llvm/Support/raw_ostream.h"
29+
2730
#include <cassert>
31+
#include <memory>
2832
#include <optional>
2933
#include <set>
3034
#include <string>
@@ -96,20 +100,28 @@ class IDETabulationProblem : public FlowFunctions<AnalysisDomainTy, Container>,
96100
ZeroValue) noexcept(std::is_nothrow_move_constructible_v<d_t>)
97101
: IRDB(IRDB), EntryPoints(std::move(EntryPoints)),
98102
ZeroValue(std::move(ZeroValue)),
99-
Printer(NullAnalysisPrinter<AnalysisDomainTy>::getInstance()) {
103+
Printer(std::make_unique<typename DefaultAnalysisPrinterSelector<
104+
AnalysisDomainTy>::type>()) {
100105
assert(IRDB != nullptr);
101106
}
102107

103-
void setAnalysisPrinter(AnalysisPrinterBase<AnalysisDomainTy> *P) {
108+
IDETabulationProblem(IDETabulationProblem &&) noexcept = default;
109+
IDETabulationProblem &operator=(IDETabulationProblem &&) noexcept = default;
110+
111+
IDETabulationProblem(const IDETabulationProblem &) = delete;
112+
IDETabulationProblem &operator=(const IDETabulationProblem &) = delete;
113+
114+
~IDETabulationProblem() override = default;
115+
116+
void
117+
setAnalysisPrinter(MaybeUniquePtr<AnalysisPrinterBase<AnalysisDomainTy>> P) {
104118
if (P) {
105-
Printer = P;
119+
Printer = std::move(P);
106120
} else {
107121
Printer = NullAnalysisPrinter<AnalysisDomainTy>::getInstance();
108122
}
109123
}
110124

111-
~IDETabulationProblem() override = default;
112-
113125
/// Checks if the given data-flow fact is the special tautological lambda (or
114126
/// zero) fact.
115127
[[nodiscard]] virtual bool isZeroValue(d_t FlowFact) const noexcept {
@@ -148,7 +160,7 @@ class IDETabulationProblem : public FlowFunctions<AnalysisDomainTy, Container>,
148160
virtual void
149161
emitTextReport([[maybe_unused]] GenericSolverResults<n_t, d_t, l_t> Results,
150162
llvm::raw_ostream &OS = llvm::outs()) {
151-
OS << "No text report available!\n";
163+
Printer->onFinalize(OS);
152164
}
153165

154166
/// Generates a graphical report, e.g. in html or other markup languages, of
@@ -163,7 +175,9 @@ class IDETabulationProblem : public FlowFunctions<AnalysisDomainTy, Container>,
163175
/// the level of soundness is ignored. Otherwise, true.
164176
virtual bool setSoundness(Soundness /*S*/) { return false; }
165177

166-
const ProjectIRDBBase<db_t> *getProjectIRDB() const noexcept { return IRDB; }
178+
[[nodiscard]] const ProjectIRDBBase<db_t> *getProjectIRDB() const noexcept {
179+
return IRDB;
180+
}
167181

168182
protected:
169183
typename FlowFunctions<AnalysisDomainTy, Container>::FlowFunctionPtrType
@@ -172,6 +186,19 @@ class IDETabulationProblem : public FlowFunctions<AnalysisDomainTy, Container>,
172186
std::move(FactToGenerate), getZeroValue());
173187
}
174188

189+
template <typename L = l_t>
190+
std::enable_if_t<!std::is_same_v<std::decay_t<L>, psr::BinaryDomain>>
191+
onResult(n_t Instr, d_t DfFact, l_t LatticeElement,
192+
DataFlowAnalysisType AnalysisType) {
193+
Printer->onResult(Instr, std::move(DfFact), std::move(LatticeElement),
194+
AnalysisType);
195+
}
196+
template <typename L = l_t>
197+
std::enable_if_t<std::is_same_v<std::decay_t<L>, psr::BinaryDomain>>
198+
onResult(n_t Instr, d_t DfFact, DataFlowAnalysisType AnalysisType) {
199+
Printer->onResult(Instr, std::move(DfFact), AnalysisType);
200+
}
201+
175202
/// Seeds that just start with ZeroValue and bottomElement() at the starting
176203
/// points of each EntryPoint function.
177204
/// Takes the __ALL__ EntryPoint into account.
@@ -196,7 +223,7 @@ class IDETabulationProblem : public FlowFunctions<AnalysisDomainTy, Container>,
196223

197224
[[maybe_unused]] Soundness SF = Soundness::Soundy;
198225

199-
AnalysisPrinterBase<AnalysisDomainTy> *Printer;
226+
MaybeUniquePtr<AnalysisPrinterBase<AnalysisDomainTy>> Printer;
200227
};
201228

202229
} // namespace psr

include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ class IFDSTabulationProblem
5959
std::vector<std::string> EntryPoints,
6060
d_t ZeroValue)
6161
: Base(IRDB, std::move(EntryPoints), std::move(ZeroValue)) {}
62+
63+
IFDSTabulationProblem(IFDSTabulationProblem &&) noexcept = default;
64+
IFDSTabulationProblem &operator=(IFDSTabulationProblem &&) noexcept = default;
65+
66+
IFDSTabulationProblem(const IFDSTabulationProblem &) = delete;
67+
IFDSTabulationProblem &operator=(const IFDSTabulationProblem &) = delete;
68+
6269
~IFDSTabulationProblem() override = default;
6370

6471
EdgeFunction<l_t> getNormalEdgeFunction(n_t /*Curr*/, d_t /*CurrNode*/,

include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ class IDETypeStateAnalysisBase
5454
public:
5555
virtual ~IDETypeStateAnalysisBase() = default;
5656

57+
IDETypeStateAnalysisBase(IDETypeStateAnalysisBase &&) noexcept = default;
58+
IDETypeStateAnalysisBase &
59+
operator=(IDETypeStateAnalysisBase &&) noexcept = default;
60+
61+
IDETypeStateAnalysisBase(const IDETypeStateAnalysisBase &) = delete;
62+
IDETypeStateAnalysisBase &
63+
operator=(const IDETypeStateAnalysisBase &) noexcept = delete;
64+
5765
protected:
5866
IDETypeStateAnalysisBase(LLVMAliasInfoRef PT) noexcept : PT(PT) {}
5967

@@ -195,8 +203,7 @@ class IDETypeStateAnalysis
195203
}
196204

197205
[[no_unique_address]] std::conditional_t<HasJoinLatticeTraits<l_t>,
198-
EmptyType, l_t>
199-
BotElement{};
206+
EmptyType, l_t> BotElement{};
200207

201208
static EdgeFunction<l_t> join(EdgeFunctionRef<TSEdgeFunctionComposer> This,
202209
const EdgeFunction<l_t> &OtherFunction) {
@@ -348,8 +355,6 @@ class IDETypeStateAnalysis
348355
assert(PT);
349356
}
350357

351-
~IDETypeStateAnalysis() override = default;
352-
353358
// start formulating our analysis by specifying the parts required for IFDS
354359

355360
FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override {
@@ -513,40 +518,27 @@ class IDETypeStateAnalysis
513518

514519
void emitTextReport(GenericSolverResults<n_t, d_t, l_t> SR,
515520
llvm::raw_ostream &OS = llvm::outs()) override {
516-
LLVMBasedCFG CFG;
521+
517522
for (const auto &F : this->IRDB->getAllFunctions()) {
518523
for (const auto &BB : *F) {
519524
for (const auto &I : BB) {
520525
auto Results = SR.resultsAt(&I, true);
521526

522-
if (CFG.isExitInst(&I)) {
523-
for (auto Res : Results) {
524-
if (const auto *Alloca =
525-
llvm::dyn_cast<llvm::AllocaInst>(Res.first)) {
526-
if (Res.second == TSD->error()) {
527-
// ERROR STATE DETECTED
528-
this->Printer->onResult(&I, Res.first, TSD->error(),
529-
TSD->analysisType());
530-
}
531-
}
532-
}
533-
} else {
534-
for (auto Res : Results) {
535-
if (const auto *Alloca =
536-
llvm::dyn_cast<llvm::AllocaInst>(Res.first)) {
537-
if (Res.second == TSD->error()) {
538-
// ERROR STATE DETECTED
539-
this->Printer->onResult(&I, Res.first, TSD->error(),
540-
TSD->analysisType());
541-
}
527+
for (auto Res : Results) {
528+
if (const auto *Alloca =
529+
llvm::dyn_cast<llvm::AllocaInst>(Res.first)) {
530+
if (Res.second == TSD->error()) {
531+
// ERROR STATE DETECTED
532+
this->onResult(&I, Res.first, TSD->error(),
533+
TSD->analysisType());
542534
}
543535
}
544536
}
545537
}
546538
}
547539
}
548540

549-
this->Printer->onFinalize();
541+
this->Printer->onFinalize(OS);
550542
}
551543

552544
[[nodiscard]] bool

include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ class IFDSConstAnalysis
4949
IFDSConstAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT,
5050
std::vector<std::string> EntryPoints = {"main"});
5151

52-
~IFDSConstAnalysis() override = default;
53-
5452
/**
5553
* If the current instruction is a store instruction, the memory locations's
5654
* state of initialization is checked. If the memory location was already

include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ class IFDSTaintAnalysis
5858
std::vector<std::string> EntryPoints = {"main"},
5959
bool TaintMainArgs = true);
6060

61-
~IFDSTaintAnalysis() override = default;
62-
6361
FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override;
6462

6563
FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override;

include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ class IFDSUninitializedVariables
4040
IFDSUninitializedVariables(const LLVMProjectIRDB *IRDB,
4141
std::vector<std::string> EntryPoints = {"main"});
4242

43-
~IFDSUninitializedVariables() override = default;
44-
4543
FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override;
4644

4745
FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override;

include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
#define PHASAR_PHASARLLVM_DOMAIN_LLVMANALYSISDOMAIN_H
1212

1313
#include "phasar/Domain/AnalysisDomain.h"
14+
#include "phasar/PhasarLLVM/Utils/LLVMAnalysisPrinter.h"
15+
#include "phasar/Utils/DefaultAnalysisPrinterSelector.h"
16+
#include "phasar/Utils/TypeTraits.h"
1417

1518
namespace llvm {
1619
class Value;
@@ -43,6 +46,13 @@ struct LLVMAnalysisDomainDefault : public AnalysisDomain {
4346
using LLVMIFDSAnalysisDomainDefault =
4447
WithBinaryValueDomain<LLVMAnalysisDomainDefault>;
4548

49+
extern template class DefaultLLVMAnalysisPrinter<LLVMIFDSAnalysisDomainDefault>;
50+
51+
template <>
52+
struct DefaultAnalysisPrinterSelector<LLVMIFDSAnalysisDomainDefault>
53+
: type_identity<DefaultLLVMAnalysisPrinter<LLVMIFDSAnalysisDomainDefault>> {
54+
};
55+
4656
} // namespace psr
4757

4858
#endif // PHASAR_PHASARLLVM_DOMAIN_LLVMANALYSISDOMAIN_H
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#ifndef PHASAR_PHASARLLVM_UTILS_LLVMANALYSISPRINTER_H
2+
#define PHASAR_PHASARLLVM_UTILS_LLVMANALYSISPRINTER_H
3+
4+
#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h"
5+
#include "phasar/Utils/ByRef.h"
6+
#include "phasar/Utils/DefaultAnalysisPrinter.h"
7+
8+
#include "llvm/ADT/ArrayRef.h"
9+
#include "llvm/ADT/SmallVector.h"
10+
#include "llvm/IR/Function.h"
11+
12+
#include <type_traits>
13+
14+
namespace psr {
15+
16+
template <typename AnalysisDomainTy>
17+
class DefaultLLVMAnalysisPrinter
18+
: public AnalysisPrinterBase<AnalysisDomainTy> {
19+
using n_t = typename AnalysisDomainTy::n_t;
20+
using d_t = typename AnalysisDomainTy::d_t;
21+
using l_t = typename AnalysisDomainTy::l_t;
22+
23+
public:
24+
DefaultLLVMAnalysisPrinter() noexcept = default;
25+
26+
private:
27+
template <typename D = d_t>
28+
static std::optional<DebugLocation> getDbgLoc(n_t Instr,
29+
ByConstRef<d_t> DfFact) {
30+
auto Ret = getDebugLocation(Instr);
31+
if constexpr (std::is_convertible_v<ByConstRef<D>, const llvm::Value *>) {
32+
if (!Ret) {
33+
Ret = getDebugLocation(static_cast<const llvm::Value *>(DfFact));
34+
}
35+
}
36+
return Ret;
37+
}
38+
template <typename D = d_t>
39+
void printVariables(llvm::raw_ostream &OS,
40+
llvm::ArrayRef<Warning<AnalysisDomainTy>> Results) {
41+
if constexpr (std::is_convertible_v<const D &, const llvm::Value *>) {
42+
bool HasVariable = false;
43+
for (const auto &Warn : Results) {
44+
if (auto VarName = getVarNameFromIR(Warn.Fact); !VarName.empty()) {
45+
if (!HasVariable) {
46+
HasVariable = true;
47+
OS << " Variable: " << VarName;
48+
} else {
49+
OS << ", " << VarName;
50+
}
51+
}
52+
}
53+
if (HasVariable) {
54+
OS << '\n';
55+
}
56+
}
57+
}
58+
59+
void doOnResult(n_t Instr, d_t DfFact, l_t Lattice,
60+
DataFlowAnalysisType AnalysisType) override {
61+
if (auto DbgLoc = getDbgLoc(Instr, DfFact)) {
62+
DbgResultsEntries[*DbgLoc].emplace_back(Instr, std::move(DfFact),
63+
std::move(Lattice), AnalysisType);
64+
} else {
65+
NonDbgResultsEntries.emplace_back(Instr, std::move(DfFact),
66+
std::move(Lattice), AnalysisType);
67+
}
68+
}
69+
70+
void doOnFinalize(llvm::raw_ostream &OS) override {
71+
size_t Ctr = 0;
72+
for (const auto &[DbgLoc, Results] : DbgResultsEntries) {
73+
OS << '#' << ++Ctr << ": ";
74+
OS << "At " << getFilePathFromIR(DbgLoc.File) << ':' << DbgLoc.Line << ':'
75+
<< DbgLoc.Column << ":\n";
76+
OS << " In Function: " << Results.front().Instr->getFunction()->getName()
77+
<< '\n';
78+
if (auto SrcCode = getSrcCodeFromIR(DbgLoc); !SrcCode.empty()) {
79+
OS << " Source code: " << SrcCode << '\n';
80+
}
81+
printVariables(OS, Results);
82+
OS << "\n Corresponding IR Statements:\n";
83+
for (const auto &Warn : Results) {
84+
OS << " At IR statement: " << NToString(Warn.Instr) << '\n';
85+
OS << " Fact: " << DToString(Warn.Fact) << '\n';
86+
87+
if constexpr (!std::is_same_v<l_t, BinaryDomain>) {
88+
OS << " Value: " << LToString(Warn.LatticeElement) << '\n';
89+
}
90+
OS << '\n';
91+
}
92+
}
93+
94+
// Fallback in case we don't have debug information:
95+
for (const auto &Iter : NonDbgResultsEntries) {
96+
OS << "At IR statement: " << NToString(Iter.Instr) << '\n';
97+
OS << " In Function: " << Iter.Instr->getFunction()->getName() << '\n';
98+
OS << " Fact: " << DToString(Iter.Fact) << '\n';
99+
100+
if constexpr (!std::is_same_v<l_t, BinaryDomain>) {
101+
OS << " Value: " << LToString(Iter.LatticeElement) << '\n';
102+
}
103+
OS << '\n';
104+
}
105+
NonDbgResultsEntries.clear();
106+
}
107+
108+
std::map<DebugLocation, llvm::SmallVector<Warning<AnalysisDomainTy>, 1>>
109+
DbgResultsEntries;
110+
std::vector<Warning<AnalysisDomainTy>> NonDbgResultsEntries{};
111+
};
112+
113+
} // namespace psr
114+
115+
#endif // PHASAR_PHASARLLVM_UTILS_LLVMANALYSISPRINTER_H

include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include <optional>
2525
#include <string>
26+
#include <tuple>
2627

2728
// Forward declaration of types for which we only use its pointer or ref type
2829
namespace llvm {
@@ -47,6 +48,15 @@ struct DebugLocation {
4748
unsigned Line{};
4849
unsigned Column{};
4950
const llvm::DIFile *File{};
51+
52+
friend constexpr bool operator<(DebugLocation L, DebugLocation R) noexcept {
53+
return std::tie(L.File, L.Line, L.Column) <
54+
std::tie(R.File, R.Line, R.Column);
55+
}
56+
friend constexpr bool operator==(DebugLocation L, DebugLocation R) noexcept {
57+
return std::tie(L.File, L.Line, L.Column) ==
58+
std::tie(R.File, R.Line, R.Column);
59+
}
5060
};
5161

5262
[[nodiscard]] llvm::DILocalVariable *getDILocalVariable(const llvm::Value *V);

0 commit comments

Comments
 (0)