The goal is to be able to understand how the analysis executes, and what its incremental and final findings are, by enabling logging and reading the logs. This should include both framework and analysis-specific information. Ad-hoc printf-debugging doesn't seem sufficient for my understanding, at least. Being able to check in logging, turn it on in a production binary, and quickly find particular analysis steps within complex functions seem important. This can be enabled programmatically through DataflowAnalysisOptions, or via the flag -dataflow-log. (Works in unittests, clang-tidy, standalone tools...) Important missing pieces here: - a logger implementation that produces an interactive report (HTML file) which can be navigated via timeline/code/CFG. (I think the Logger interface is sufficient for this, but need to prototype). - display of the application-specific lattice - more useful display for the built-in environment (e.g. meaningful & consistent names for values, hiding redundant variables in the flow condition, hiding unreachable expressions) Differential Revision: https://reviews.llvm.org/D144730
109 lines
3.7 KiB
C++
109 lines
3.7 KiB
C++
//===-- Logger.cpp --------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Analysis/FlowSensitive/Logger.h"
|
|
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
|
#include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
|
|
#include "llvm/Support/WithColor.h"
|
|
|
|
namespace clang::dataflow {
|
|
|
|
Logger &Logger::null() {
|
|
struct NullLogger final : Logger {};
|
|
static auto *Instance = new NullLogger();
|
|
return *Instance;
|
|
}
|
|
|
|
namespace {
|
|
struct TextualLogger final : Logger {
|
|
llvm::raw_ostream &OS;
|
|
const CFG *CurrentCFG;
|
|
const CFGBlock *CurrentBlock;
|
|
const CFGElement *CurrentElement;
|
|
unsigned CurrentElementIndex;
|
|
bool ShowColors;
|
|
llvm::DenseMap<const CFGBlock *, unsigned> VisitCount;
|
|
TypeErasedDataflowAnalysis *CurrentAnalysis;
|
|
|
|
TextualLogger(llvm::raw_ostream &OS)
|
|
: OS(OS), ShowColors(llvm::WithColor::defaultAutoDetectFunction()(OS)) {}
|
|
|
|
virtual void beginAnalysis(const ControlFlowContext &CFG,
|
|
TypeErasedDataflowAnalysis &Analysis) override {
|
|
{
|
|
llvm::WithColor Header(OS, llvm::raw_ostream::Colors::RED, /*Bold=*/true);
|
|
OS << "=== Beginning data flow analysis ===\n";
|
|
}
|
|
if (auto *D = CFG.getDecl()) {
|
|
D->print(OS);
|
|
OS << "\n";
|
|
D->dump(OS);
|
|
}
|
|
CurrentCFG = &CFG.getCFG();
|
|
CurrentCFG->print(OS, Analysis.getASTContext().getLangOpts(), ShowColors);
|
|
CurrentAnalysis = &Analysis;
|
|
}
|
|
virtual void endAnalysis() override {
|
|
llvm::WithColor Header(OS, llvm::raw_ostream::Colors::RED, /*Bold=*/true);
|
|
unsigned Blocks = 0, Steps = 0;
|
|
for (const auto &E : VisitCount) {
|
|
++Blocks;
|
|
Steps += E.second;
|
|
}
|
|
llvm::errs() << "=== Finished analysis: " << Blocks << " blocks in "
|
|
<< Steps << " total steps ===\n";
|
|
}
|
|
virtual void enterBlock(const CFGBlock &Block) override {
|
|
unsigned Count = ++VisitCount[&Block];
|
|
{
|
|
llvm::WithColor Header(OS, llvm::raw_ostream::Colors::RED, /*Bold=*/true);
|
|
OS << "=== Entering block B" << Block.getBlockID() << " (iteration "
|
|
<< Count << ") ===\n";
|
|
}
|
|
Block.print(OS, CurrentCFG, CurrentAnalysis->getASTContext().getLangOpts(),
|
|
ShowColors);
|
|
CurrentBlock = &Block;
|
|
CurrentElement = nullptr;
|
|
CurrentElementIndex = 0;
|
|
}
|
|
virtual void enterElement(const CFGElement &Element) override {
|
|
++CurrentElementIndex;
|
|
CurrentElement = ∈
|
|
{
|
|
llvm::WithColor Subheader(OS, llvm::raw_ostream::Colors::CYAN,
|
|
/*Bold=*/true);
|
|
OS << "Processing element B" << CurrentBlock->getBlockID() << "."
|
|
<< CurrentElementIndex << ": ";
|
|
Element.dumpToStream(OS);
|
|
}
|
|
}
|
|
void recordState(TypeErasedDataflowAnalysisState &State) override {
|
|
{
|
|
llvm::WithColor Subheader(OS, llvm::raw_ostream::Colors::CYAN,
|
|
/*Bold=*/true);
|
|
OS << "Computed state for B" << CurrentBlock->getBlockID() << "."
|
|
<< CurrentElementIndex << ":\n";
|
|
}
|
|
// FIXME: currently the environment dump is verbose and unenlightening.
|
|
// FIXME: dump the user-defined lattice, too.
|
|
State.Env.dump(OS);
|
|
OS << "\n";
|
|
}
|
|
void blockConverged() override {
|
|
OS << "B" << CurrentBlock->getBlockID() << " has converged!\n";
|
|
}
|
|
virtual void logText(llvm::StringRef S) override { OS << S << "\n"; }
|
|
};
|
|
} // namespace
|
|
|
|
std::unique_ptr<Logger> Logger::textual(llvm::raw_ostream &OS) {
|
|
return std::make_unique<TextualLogger>(OS);
|
|
}
|
|
|
|
} // namespace clang::dataflow
|