[clang] b56b15e - [dataflow] HTMLLogger - show the value of the current expr
Sam McCall via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 26 08:15:31 PDT 2023
Author: Sam McCall
Date: 2023-04-26T17:15:23+02:00
New Revision: b56b15ed719b4b6a5fe1a0a8b5ece54467ab2fee
URL: https://github.com/llvm/llvm-project/commit/b56b15ed719b4b6a5fe1a0a8b5ece54467ab2fee
DIFF: https://github.com/llvm/llvm-project/commit/b56b15ed719b4b6a5fe1a0a8b5ece54467ab2fee.diff
LOG: [dataflow] HTMLLogger - show the value of the current expr
Differential Revision: https://reviews.llvm.org/D148949
Added:
Modified:
clang/include/clang/Analysis/FlowSensitive/Value.h
clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
clang/lib/Analysis/FlowSensitive/HTMLLogger.css
clang/lib/Analysis/FlowSensitive/HTMLLogger.html
clang/lib/Analysis/FlowSensitive/HTMLLogger.js
clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/FlowSensitive/Value.h b/clang/include/clang/Analysis/FlowSensitive/Value.h
index 32d10a3489483..861a9963e6689 100644
--- a/clang/include/clang/Analysis/FlowSensitive/Value.h
+++ b/clang/include/clang/Analysis/FlowSensitive/Value.h
@@ -73,6 +73,11 @@ class Value {
Properties.insert_or_assign(Name, &Val);
}
+ llvm::iterator_range<llvm::StringMap<Value *>::const_iterator>
+ properties() const {
+ return {Properties.begin(), Properties.end()};
+ }
+
private:
Kind ValKind;
llvm::StringMap<Value *> Properties;
@@ -307,6 +312,12 @@ class StructValue final : public Value {
/// Assigns `Val` as the child value for `D`.
void setChild(const ValueDecl &D, Value &Val) { Children[&D] = &Val; }
+ llvm::iterator_range<
+ llvm::DenseMap<const ValueDecl *, Value *>::const_iterator>
+ children() const {
+ return {Children.begin(), Children.end()};
+ }
+
private:
llvm::DenseMap<const ValueDecl *, Value *> Children;
};
diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
index b229194bc8f44..14293a3043f98 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
@@ -67,6 +67,7 @@
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Program.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
// Defines assets: HTMLLogger_{html_js,css}
#include "HTMLLogger.inc"
@@ -79,6 +80,96 @@ llvm::Expected<std::string> renderSVG(llvm::StringRef DotGraph);
using StreamFactory = std::function<std::unique_ptr<llvm::raw_ostream>()>;
+// Recursively dumps Values/StorageLocations as JSON
+class ModelDumper {
+public:
+ ModelDumper(llvm::json::OStream &JOS, const Environment &Env)
+ : JOS(JOS), Env(Env) {}
+
+ void dump(Value &V) {
+ JOS.attribute("value_id", llvm::to_string(&V));
+ if (!Visited.insert(&V).second)
+ return;
+
+ JOS.attribute("kind", debugString(V.getKind()));
+
+ switch (V.getKind()) {
+ case Value::Kind::Integer:
+ case Value::Kind::TopBool:
+ case Value::Kind::AtomicBool:
+ break;
+ case Value::Kind::Reference:
+ JOS.attributeObject(
+ "referent", [&] { dump(cast<ReferenceValue>(V).getReferentLoc()); });
+ break;
+ case Value::Kind::Pointer:
+ JOS.attributeObject(
+ "pointee", [&] { dump(cast<PointerValue>(V).getPointeeLoc()); });
+ break;
+ case Value::Kind::Struct:
+ for (const auto &Child : cast<StructValue>(V).children())
+ JOS.attributeObject("f:" + Child.first->getNameAsString(),
+ [&] { dump(*Child.second); });
+ break;
+ case Value::Kind::Disjunction: {
+ auto &VV = cast<DisjunctionValue>(V);
+ JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); });
+ JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); });
+ break;
+ }
+ case Value::Kind::Conjunction: {
+ auto &VV = cast<ConjunctionValue>(V);
+ JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); });
+ JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); });
+ break;
+ }
+ case Value::Kind::Negation: {
+ auto &VV = cast<NegationValue>(V);
+ JOS.attributeObject("not", [&] { dump(VV.getSubVal()); });
+ break;
+ }
+ case Value::Kind::Implication: {
+ auto &VV = cast<ImplicationValue>(V);
+ JOS.attributeObject("if", [&] { dump(VV.getLeftSubValue()); });
+ JOS.attributeObject("then", [&] { dump(VV.getRightSubValue()); });
+ break;
+ }
+ case Value::Kind::Biconditional: {
+ auto &VV = cast<BiconditionalValue>(V);
+ JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); });
+ JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); });
+ break;
+ }
+ }
+
+ for (const auto& Prop : V.properties())
+ JOS.attributeObject(("p:" + Prop.first()).str(),
+ [&] { dump(*Prop.second); });
+
+ // Running the SAT solver is expensive, but knowing which booleans are
+ // guaranteed true/false here is valuable and hard to determine by hand.
+ if (auto *B = llvm::dyn_cast<BoolValue>(&V)) {
+ JOS.attribute("truth", Env.flowConditionImplies(*B) ? "true"
+ : Env.flowConditionImplies(Env.makeNot(*B))
+ ? "false"
+ : "unknown");
+ }
+ }
+ void dump(const StorageLocation &L) {
+ JOS.attribute("location", llvm::to_string(&L));
+ if (!Visited.insert(&L).second)
+ return;
+
+ JOS.attribute("type", L.getType().getAsString());
+ if (auto *V = Env.getValue(L))
+ dump(*V);
+ }
+
+ llvm::DenseSet<const void*> Visited;
+ llvm::json::OStream &JOS;
+ const Environment &Env;
+};
+
class HTMLLogger : public Logger {
StreamFactory Streams;
std::unique_ptr<llvm::raw_ostream> OS;
@@ -184,6 +275,16 @@ class HTMLLogger : public Logger {
JOS->attribute("block", blockID(Block));
JOS->attribute("iter", Iter);
JOS->attribute("element", ElementIndex);
+
+ // If this state immediately follows an Expr, show its built-in model.
+ if (ElementIndex > 0) {
+ auto S =
+ Iters.back().first->Elements[ElementIndex - 1].getAs<CFGStmt>();
+ if (const Expr *E = S ? llvm::dyn_cast<Expr>(S->getStmt()) : nullptr)
+ if (auto *Loc = State.Env.getStorageLocation(*E, SkipPast::None))
+ JOS->attributeObject(
+ "value", [&] { ModelDumper(*JOS, State.Env).dump(*Loc); });
+ }
if (!ContextLogs.empty()) {
JOS->attribute("logs", ContextLogs);
ContextLogs.clear();
diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.css b/clang/lib/Analysis/FlowSensitive/HTMLLogger.css
index 4877c1264c83b..c8212df1f94b8 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.css
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.css
@@ -116,3 +116,27 @@ code.line:has(.bb-select):before {
position: relative;
margin-left: 0.5em;
}
+
+.value {
+ border: 1px solid #888;
+ font-size: x-small;
+ flex-grow: 1;
+}
+.value summary {
+ background-color: #ace;
+ display: flex;
+ justify-content: space-between;
+}
+.value .address {
+ font-size: xx-small;
+ font-family: monospace;
+ color: #888;
+}
+.value .property {
+ display: flex;
+ margin-top: 0.5em;
+}
+.value .property .key {
+ font-weight: bold;
+ min-width: 5em;
+}
diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html
index 99d4debd05be3..a60259a99cce0 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html
@@ -10,6 +10,30 @@
<head>
<?INJECT?>
+
+<template id="value-template">
+ <details class="value" open>
+ <summary>
+ <span>{{v.kind}}
+ <template data-if="v.value_id"><span class="address">#{{v.value_id}}</span></template>
+ </span>
+ <template data-if="v.location">
+ <span>{{v.type}} <span class="address">@{{v.location}}</span></span>
+ </template>
+ </summary>
+ <template
+ data-for="kv in Object.entries(v)"
+ data-if="['kind', 'value_id', 'type', 'location'].indexOf(kv[0]) < 0">
+ <div class="property"><span class="key">{{kv[0]}}</span>
+ <template data-if="typeof(kv[1]) != 'object'">{{kv[1]}}</template>
+ <template data-if="typeof(kv[1]) == 'object'" data-let="v = kv[1]">
+ <template data-use="value-template"></template>
+ </template>
+ </div>
+ </template>
+ </details>
+</template>
+
</head>
<body>
@@ -56,6 +80,10 @@
<template data-if="state.element == 0">{{state.block}} (iteration {{state.iter}}) initial state</template>
<template data-if="state.element != 0">Element {{selection.elt}} (iteration {{state.iter}})</template>
</header>
+<template data-if="state.value" data-let="v = state.value">
+ <h2>Value</h2>
+ <template data-use="value-template"></template>
+</template>
<template data-if="state.logs">
<h2>Logs</h2>
<pre>{{state.logs}}</pre>
diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.js b/clang/lib/Analysis/FlowSensitive/HTMLLogger.js
index 5958418bc1e93..6e04bc00f6631 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.js
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.js
@@ -73,6 +73,9 @@ function inflate(tmpl, data, parent, next) {
}
return parent.insertBefore(clone, next);
}
+ // data-use="xyz": use <template id="xyz"> instead. (Allows recursion.)
+ if ('use' in tmpl.dataset)
+ return inflate(document.getElementById(tmpl.dataset.use), data, parent, next);
// <template> tag handling. Base case: recursively inflate.
function handle(data) {
for (c of tmpl.content.childNodes)
diff --git a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
index 0b8579a45b2df..7ba19b86dd102 100644
--- a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
@@ -176,6 +176,7 @@ TEST(LoggerTest, HTML) {
<< "has analysis point state";
EXPECT_THAT(Logs[0], HasSubstr("transferBranch(0)")) << "has analysis logs";
EXPECT_THAT(Logs[0], HasSubstr("LocToVal")) << "has built-in lattice dump";
+ EXPECT_THAT(Logs[0], HasSubstr("\"type\": \"int\"")) << "has value dump";
}
} // namespace
More information about the cfe-commits
mailing list