clang: lib/Analysis/FlowSensitive/HTMLLogger.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

64#include "llvm/ADT/DenseMap.h"

65#include "llvm/ADT/ScopeExit.h"

66#include "llvm/Support/Error.h"

67#include "llvm/Support/FormatVariadic.h"

68#include "llvm/Support/JSON.h"

69#include "llvm/Support/Program.h"

70#include "llvm/Support/ScopedPrinter.h"

71#include "llvm/Support/raw_ostream.h"

72

73#include "HTMLLogger.inc"

74

76namespace {

77

78

80

81using StreamFactory = std::function<std::unique_ptrllvm::raw\_ostream()>;

82

83

84class ModelDumper {

85public:

86 ModelDumper(llvm::json::OStream &JOS, const Environment &Env)

88

90 JOS.attribute("value_id", llvm::to_string(&V));

91 if (Visited.insert(&V).second)

92 return;

93

95

96 switch (V.getKind()) {

101 break;

103 JOS.attributeObject(

104 "pointee", [&] { dump(cast(V).getPointeeLoc()); });

105 break;

106 }

107

108 for (const auto& Prop : V.properties())

109 JOS.attributeObject(("p:" + Prop.first()).str(),

110 [&] { dump(*Prop.second); });

111

112

113

114 if (auto *B = llvm::dyn_cast(&V)) {

115 JOS.attribute("formula", llvm::to_string(B->formula()));

116 JOS.attribute("truth", Env.proves(B->formula()) ? "true"

117 : Env.proves(Env.arena().makeNot(B->formula()))

118 ? "false"

119 : "unknown");

120 }

121 }

122 void dump(const StorageLocation &L) {

123 JOS.attribute("location", llvm::to_string(&L));

124 if (Visited.insert(&L).second)

125 return;

126

127 JOS.attribute("type", L.getType().getAsString());

128 if (!L.getType()->isRecordType())

129 if (auto *V = Env.getValue(L))

131

132 if (auto *RLoc = dyn_cast(&L)) {

133 for (const auto &Child : RLoc->children())

134 JOS.attributeObject("f:" + Child.first->getNameAsString(), [&] {

135 if (Child.second)

136 dump(*Child.second);

137 });

138

139 for (const auto &SyntheticField : RLoc->synthetic_fields())

140 JOS.attributeObject(("sf:" + SyntheticField.first()).str(),

141 [&] { dump(*SyntheticField.second); });

142 }

143 }

144

146 llvm::json::OStream &JOS;

147 const Environment &Env;

148};

149

150class HTMLLogger : public Logger {

151 struct Iteration {

156 };

157

158 StreamFactory Streams;

159 std::unique_ptrllvm::raw\_ostream OS;

160 std::string JSON;

161 llvm::raw_string_ostream JStringStream{JSON};

162 llvm::json::OStream JOS{JStringStream, 2};

163

164 const AdornedCFG *ACFG;

165

166 std::vector Iters;

167

168 llvm::DenseMap<const CFGBlock *, llvm::SmallVector<size_t>> BlockIters;

169

170 llvm::BitVector BlockConverged;

171

172 std::string ContextLogs;

173

174 unsigned ElementIndex;

175

176public:

177 explicit HTMLLogger(StreamFactory Streams) : Streams(std::move(Streams)) {}

178 void beginAnalysis(const AdornedCFG &ACFG,

179 TypeErasedDataflowAnalysis &A) override {

180 OS = Streams();

181 this->ACFG = &ACFG;

182 *OS << llvm::StringRef(HTMLLogger_html).split("").first;

183

184 BlockConverged.resize(ACFG.getCFG().getNumBlockIDs());

185

186 const auto &D = ACFG.getDecl();

187 const auto &SM = A.getASTContext().getSourceManager();

188 *OS << "";</p> <p> 189 if (const auto *ND = dyn_cast<NamedDecl>(&<a href="CheckExprLifetime%5F8cpp.html#a0a83134bd8a30628eb51b51c4cc5f709" title="null" rel="noopener noreferrer">D</a>))</p> <p> 190 *OS << ND->getNameAsString() << " at ";</p> <p> 191 *OS << <a href="Basic%5F2Cuda%5F8cpp.html#ab6882f8bbaa96cde60db40335e62eb2e" title="null" rel="noopener noreferrer">SM</a>.getFilename(<a href="CheckExprLifetime%5F8cpp.html#a0a83134bd8a30628eb51b51c4cc5f709" title="null" rel="noopener noreferrer">D</a>.getLocation()) << ":"</p> <p> 192 << <a href="Basic%5F2Cuda%5F8cpp.html#ab6882f8bbaa96cde60db40335e62eb2e" title="null" rel="noopener noreferrer">SM</a>.getSpellingLineNumber(<a href="CheckExprLifetime%5F8cpp.html#a0a83134bd8a30628eb51b51c4cc5f709" title="null" rel="noopener noreferrer">D</a>.getLocation());</p> <p> 193 *OS << "\n";

194

195 *OS << "\n";

196 *OS << "\n";

197

198 writeCode();

199 JOS.objectBegin();

200 JOS.attributeBegin("states");

201 JOS.objectBegin();

202 }

203

204

205 void endAnalysis() override {

206 JOS.objectEnd();

207 JOS.attributeEnd();

208

209 JOS.attributeArray("timeline", [&] {

210 for (const auto &E : Iters) {

211 JOS.object([&] {

212 JOS.attribute("block", blockID(E.Block->getBlockID()));

213 JOS.attribute("iter", E.Iter);

214 JOS.attribute("post_visit", E.PostVisit);

215 JOS.attribute("converged", E.Converged);

216 });

217 }

218 });

219 JOS.attributeObject("cfg", [&] {

220 for (const auto &E : BlockIters)

221 writeBlock(*E.first, E.second);

222 });

223

224 JOS.objectEnd();

225

226 writeCFG();

227

228 *OS << "\n";

231 *OS << llvm::StringRef(HTMLLogger_html).split("").second;

232 }

233

234 void enterBlock(const CFGBlock &B, bool PostVisit) override {

236 unsigned IterNum = BIter.size() + 1;

237 BIter.push_back(Iters.size());

238 Iters.push_back({&B, IterNum, PostVisit, false});

240 BlockConverged[B.getBlockID()] = false;

241 ElementIndex = 0;

242 }

243 void enterElement(const CFGElement &E) override {

244 ++ElementIndex;

245 }

246

247 static std::string blockID(unsigned Block) {

248 return llvm::formatv("B{0}", Block);

249 }

250 static std::string eltID(unsigned Block, unsigned Element) {

251 return llvm::formatv("B{0}.{1}", Block, Element);

252 }

253 static std::string iterID(unsigned Block, unsigned Iter) {

254 return llvm::formatv("B{0}:{1}", Block, Iter);

255 }

256 static std::string elementIterID(unsigned Block, unsigned Iter,

257 unsigned Element) {

258 return llvm::formatv("B{0}:{1}_B{0}.{2}", Block, Iter, Element);

259 }

260

261

262

263

264

265

266

267 void recordState(TypeErasedDataflowAnalysisState &State) override {

268 unsigned Block = Iters.back().Block->getBlockID();

269 unsigned Iter = Iters.back().Iter;

270 bool PostVisit = Iters.back().PostVisit;

271 JOS.attributeObject(elementIterID(Block, Iter, ElementIndex), [&] {

272 JOS.attribute("block", blockID(Block));

273 JOS.attribute("iter", Iter);

275 JOS.attribute("element", ElementIndex);

276

277

278 if (ElementIndex > 0) {

279 auto S =

280 Iters.back().Block->Elements[ElementIndex - 1].getAs();

281 if (const Expr *E = S ? llvm::dyn_cast(S->getStmt()) : nullptr) {

282 if (E->isPRValue()) {

283 if (!E->getType()->isRecordType())

284 if (auto *V = State.Env.getValue(*E))

285 JOS.attributeObject(

286 "value", [&] { ModelDumper(JOS, State.Env).dump(*V); });

287 } else {

288 if (auto *Loc = State.Env.getStorageLocation(*E))

289 JOS.attributeObject(

290 "value", [&] { ModelDumper(JOS, State.Env).dump(*Loc); });

291 }

292 }

293 }

294 if (!ContextLogs.empty()) {

295 JOS.attribute("logs", ContextLogs);

296 ContextLogs.clear();

297 }

298 {

299 std::string BuiltinLattice;

300 llvm::raw_string_ostream BuiltinLatticeS(BuiltinLattice);

301 State.Env.dump(BuiltinLatticeS);

302 JOS.attribute("builtinLattice", BuiltinLattice);

303 }

304 });

305 }

306 void blockConverged() override {

307 Iters.back().Converged = true;

308 BlockConverged[Iters.back().Block->getBlockID()] = true;

309 }

310

311 void logText(llvm::StringRef S) override {

312 ContextLogs.append(S.begin(), S.end());

313 ContextLogs.push_back('\n');

314 }

315

316private:

317

318

319

321 JOS.attributeObject(blockID(B.getBlockID()), [&] {

322 JOS.attributeArray("iters", [&] {

323 for (size_t IterIdx : ItersForB) {

324 const Iteration &Iter = Iters[IterIdx];

325 JOS.object([&] {

326 JOS.attribute("iter", Iter.Iter);

327 JOS.attribute("post_visit", Iter.PostVisit);

328 JOS.attribute("converged", Iter.Converged);

329 });

330 }

331 });

332 JOS.attributeArray("elements", [&] {

333 for (const auto &Elt : B.Elements) {

334 std::string Dump;

335 llvm::raw_string_ostream DumpS(Dump);

336 Elt.dumpToStream(DumpS);

337 JOS.value(Dump);

338 }

339 });

340 });

341 }

342

343

344

345

346

347 void writeCode() {

348 const auto &AST = ACFG->getDecl().getASTContext();

350

351

352

353

354

356 CharSourceRange::getTokenRange(ACFG->getDecl().getSourceRange()),

357 AST.getSourceManager(), AST.getLangOpts());

358 if (Range.isInvalid())

359 return;

361 Range, AST.getSourceManager(), AST.getLangOpts(), &Invalid);

362 if (Invalid)

363 return;

364

365

366 struct TokenInfo {

367 enum : unsigned { Missing = static_cast<unsigned>(-1) };

368

369

370

371 unsigned BB = Missing;

372 unsigned BBPriority = 0;

373

374 unsigned Elt = Missing;

375 unsigned EltPriority = 0;

376

377 SmallVector Elts;

378

379

380

381

382

383

384

385 void assign(unsigned BB, unsigned Elt, unsigned RangeLen) {

386

387 if (this->BB != Missing && BB != this->BB && BBPriority <= RangeLen)

388 return;

389 if (BB != this->BB) {

390 this->BB = BB;

391 Elts.clear();

392 BBPriority = RangeLen;

393 }

394 BBPriority = std::min(BBPriority, RangeLen);

395 Elts.push_back(Elt);

396 if (this->Elt == Missing || EltPriority > RangeLen)

397 this->Elt = Elt;

398 }

399 bool operator==(const TokenInfo &Other) const {

400 return std::tie(BB, Elt, Elts) ==

402 }

403

404 void write(llvm::raw_ostream &OS) const {

405 OS << "class='c";

406 if (BB != Missing)

407 OS << " " << blockID(BB);

408 for (unsigned Elt : Elts)

409 OS << " " << eltID(BB, Elt);

410 OS << "'";

411

412 if (Elt != Missing)

413 OS << " data-elt='" << eltID(BB, Elt) << "'";

414 if (BB != Missing)

415 OS << " data-bb='" << blockID(BB) << "'";

416 }

417 };

418

419

420

421 std::vector State(Code.size());

422 for (const auto *Block : ACFG->getCFG()) {

423 unsigned EltIndex = 0;

424 for (const auto& Elt : *Block) {

425 ++EltIndex;

426 if (const auto S = Elt.getAs()) {

428 CharSourceRange::getTokenRange(S->getStmt()->getSourceRange()),

429 AST.getSourceManager(), AST.getLangOpts());

430 if (EltRange.isInvalid())

431 continue;

432 if (EltRange.getBegin() < Range.getBegin() ||

433 EltRange.getEnd() >= Range.getEnd() ||

434 EltRange.getEnd() < Range.getBegin() ||

435 EltRange.getEnd() >= Range.getEnd())

436 continue;

437 unsigned Off = EltRange.getBegin().getRawEncoding() -

438 Range.getBegin().getRawEncoding();

439 unsigned Len = EltRange.getEnd().getRawEncoding() -

440 EltRange.getBegin().getRawEncoding();

441 for (unsigned I = 0; I < Len; ++I)

442 State[Off + I].assign(Block->getBlockID(), EltIndex, Len);

443 }

444 }

445 }

446

447

448 unsigned Line =

449 AST.getSourceManager().getSpellingLineNumber(Range.getBegin());

450 *OS << "\n";

451 *OS << "";

452 llvm::printHTMLEscaped(

453 llvm::sys::path::filename(

454 AST.getSourceManager().getFilename(Range.getBegin())),

455 *OS);

456 *OS << "";

457 *OS << "";

458 for (unsigned I = 0; I < Code.size(); ++I) {

459

460

461 bool NeedOpen = I == 0 || !(State[I] == State[I-1]);

462 bool NeedClose = I + 1 == Code.size() || !(State[I] == State[I + 1]);

463 if (NeedOpen) {

464 *OS << "

465 State[I].write(*OS);

466 *OS << ">";

467 }

468 if (Code[I] == '\n')

469 *OS << "\n";

470 else

471 llvm::printHTMLEscaped(Code.substr(I, 1), *OS);

472 if (NeedClose) *OS << "

";

473 }

474 *OS << "\n";

475 *OS << "";

476 }

477

478

479

480

481 void writeCFG() {

482 *OS << "\n";

483 if (auto SVG = renderSVG(buildCFGDot(ACFG->getCFG())))

484 *OS << *SVG;

485 else

486 *OS << "Can't draw CFG: " << toString(SVG.takeError());

487 *OS << "\n";

488 }

489

490

491 std::string buildCFGDot(const clang::CFG &CFG) {

492 std::string Graph;

493 llvm::raw_string_ostream GraphS(Graph);

494

495 GraphS << R"(digraph {

496 tooltip=" "

497 node[class=bb, shape=square, fontname="sans-serif", tooltip=" "]

498 edge[tooltip = " "]

499)";

500 for (unsigned I = 0; I < CFG.getNumBlockIDs(); ++I) {

501 std::string Name = blockID(I);

502

503 const char *ConvergenceMarker = (const char *)u8"\\n\u2192\u007c";

504 if (BlockConverged[I])

505 Name += ConvergenceMarker;

506 GraphS << " " << blockID(I) << " [id=" << blockID(I) << " label=\""

507 << Name << "\"]\n";

508 }

509 for (const auto *Block : CFG) {

510 for (const auto &Succ : Block->succs()) {

511 if (Succ.getReachableBlock())

512 GraphS << " " << blockID(Block->getBlockID()) << " -> "

513 << blockID(Succ.getReachableBlock()->getBlockID()) << "\n";

514 }

515 }

516 GraphS << "}\n";

517 return Graph;

518 }

519};

520

521

523 std::string DotPath;

524 if (const auto *FromEnv = ::getenv("GRAPHVIZ_DOT"))

525 DotPath = FromEnv;

526 else {

527 auto FromPath = llvm::sys::findProgramByName("dot");

528 if (!FromPath)

529 return llvm::createStringError(FromPath.getError(),

530 "'dot' not found on PATH");

531 DotPath = FromPath.get();

532 }

533

534

535

537 int InputFD;

538 if (auto EC = llvm::sys::fs::createTemporaryFile("analysis", ".dot", InputFD,

539 Input))

540 return llvm::createStringError(EC, "failed to create `dot` temp input");

541 llvm::raw_fd_ostream(InputFD, true) << DotGraph;

542 auto DeleteInput =

543 llvm::make_scope_exit([&] { llvm::sys::fs::remove(Input); });

544 if (auto EC = llvm::sys::fs::createTemporaryFile("analysis", ".svg", Output))

545 return llvm::createStringError(EC, "failed to create `dot` temp output");

546 auto DeleteOutput =

547 llvm::make_scope_exit([&] { llvm::sys::fs::remove(Output); });

548

549 std::vector<std::optionalllvm::StringRef> Redirects = {

550 Input, Output,

551 std::nullopt};

552 std::string ErrMsg;

553 int Code = llvm::sys::ExecuteAndWait(

554 DotPath, {"dot", "-Tsvg"}, std::nullopt, Redirects,

555 0, 0, &ErrMsg);

556 if (!ErrMsg.empty())

557 return llvm::createStringError(llvm::inconvertibleErrorCode(),

558 "'dot' failed: " + ErrMsg);

559 if (Code != 0)

560 return llvm::createStringError(llvm::inconvertibleErrorCode(),

561 "'dot' failed (" + llvm::Twine(Code) + ")");

562

563 auto Buf = llvm::MemoryBuffer::getFile(Output);

564 if (!Buf)

565 return llvm::createStringError(Buf.getError(), "Can't read `dot` output");

566

567

568 llvm::StringRef Result = Buf.get()->getBuffer();

569 auto Pos = Result.find("<svg");

570 if (Pos == llvm::StringRef::npos)

571 return llvm::createStringError(llvm::inconvertibleErrorCode(),

572 "Can't find tag in `dot` output");

573 return Result.substr(Pos).str();

574}

575

576}

577

578std::unique_ptr

579Logger::html(std::function<std::unique_ptrllvm::raw\_ostream()> Streams) {

580 return std::make_unique(std::move(Streams));

581}

582

583}

static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)

llvm::json::OStream & JOS

llvm::DenseSet< const void * > Visited

static std::string toString(const clang::SanitizerSet &Sanitizers)

Produce a string containing comma-separated names of sanitizers in Sanitizers set.

Defines the SourceManager interface.

Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.

unsigned getNumBlockIDs() const

Returns the total number of BlockIDs allocated (which start at 0).

static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)

Returns a string for the source that the range encompasses.

static CharSourceRange makeFileCharRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)

Accepts a range and returns a character range with file locations.

Dataflow Directional Tag Classes.

llvm::StringRef debugString(Value::Kind Kind)

Returns a string representation of a value kind.

if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))

bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)

@ Other

Other implicit parameter.