brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.3 KiB · 3b18aa8 Raw
359 lines · cpp
1//===- unittests/Support/TimeProfilerTest.cpp -----------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Frontend/CompilerInstance.h"10#include "clang/Frontend/FrontendActions.h"11#include "clang/Lex/PreprocessorOptions.h"12 13#include "llvm/ADT/StringMap.h"14#include "llvm/Support/JSON.h"15#include "llvm/Support/Path.h"16#include "llvm/Support/TimeProfiler.h"17#include "llvm/Support/VirtualFileSystem.h"18#include <stack>19 20#include "gtest/gtest.h"21#include <tuple>22 23using namespace clang;24using namespace llvm;25 26namespace {27 28// Should be called before testing.29void setupProfiler() {30  timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test",31                              /*TimeTraceVerbose=*/true);32}33 34// Should be called after `compileFromString()`.35// Returns profiler's JSON dump.36std::string teardownProfiler() {37  SmallVector<char, 1024> SmallVec;38  raw_svector_ostream OS(SmallVec);39  timeTraceProfilerWrite(OS);40  timeTraceProfilerCleanup();41  return OS.str().str();42}43 44// Returns true if code compiles successfully.45// We only parse AST here. This is enough for constexpr evaluation.46bool compileFromString(StringRef Code, StringRef Standard, StringRef File,47                       llvm::StringMap<std::string> Headers = {}) {48 49  auto FS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();50  FS->addFile(File, 0, MemoryBuffer::getMemBuffer(Code));51  for (const auto &Header : Headers) {52    FS->addFile(Header.getKey(), 0,53                MemoryBuffer::getMemBuffer(Header.getValue()));54  }55 56  auto Invocation = std::make_shared<CompilerInvocation>();57  std::vector<const char *> Args = {Standard.data(), File.data()};58  DiagnosticOptions InvocationDiagOpts;59  auto InvocationDiags =60      CompilerInstance::createDiagnostics(*FS, InvocationDiagOpts);61  CompilerInvocation::CreateFromArgs(*Invocation, Args, *InvocationDiags);62 63  CompilerInstance Compiler(std::move(Invocation));64  Compiler.setVirtualFileSystem(std::move(FS));65  Compiler.createDiagnostics();66  Compiler.createFileManager();67 68  class TestFrontendAction : public ASTFrontendAction {69  private:70    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,71                                                   StringRef InFile) override {72      return std::make_unique<ASTConsumer>();73    }74  } Action;75  return Compiler.ExecuteAction(Action);76}77 78std::string GetMetadata(json::Object *Event) {79  std::string M;80  llvm::raw_string_ostream OS(M);81  if (json::Object *Args = Event->getObject("args")) {82    if (auto Detail = Args->getString("detail"))83      OS << Detail;84    // Use only filename to not include os-specific path separators.85    if (auto File = Args->getString("file"))86      OS << (M.empty() ? "" : ", ") << llvm::sys::path::filename(*File);87    if (auto Line = Args->getInteger("line"))88      OS << ":" << *Line;89  }90  return M;91}92 93// Returns pretty-printed trace graph.94std::string buildTraceGraph(StringRef Json) {95  struct EventRecord {96    int64_t TimestampBegin;97    int64_t TimestampEnd;98    std::string Name;99    std::string Metadata;100  };101  std::vector<EventRecord> Events;102 103  // Parse `EventRecord`s from JSON dump.104  Expected<json::Value> Root = json::parse(Json);105  if (!Root)106    return "";107  for (json::Value &TraceEventValue :108       *Root->getAsObject()->getArray("traceEvents")) {109    json::Object *TraceEventObj = TraceEventValue.getAsObject();110 111    int64_t TimestampBegin = TraceEventObj->getInteger("ts").value_or(0);112    int64_t TimestampEnd =113        TimestampBegin + TraceEventObj->getInteger("dur").value_or(0);114    std::string Name = TraceEventObj->getString("name").value_or("").str();115    std::string Metadata = GetMetadata(TraceEventObj);116 117    // Source events are asynchronous events and may not perfectly nest the118    // synchronous events. Skip testing them.119    if (Name == "Source")120      continue;121 122    // This is a "summary" event, like "Total PerformPendingInstantiations",123    // skip it124    if (TimestampBegin == 0)125      continue;126 127    Events.emplace_back(128        EventRecord{TimestampBegin, TimestampEnd, Name, Metadata});129  }130 131  // There can be nested events that are very fast, for example:132  // {"name":"EvaluateAsBooleanCondition",... ,"ts":2380,"dur":1}133  // {"name":"EvaluateAsRValue",... ,"ts":2380,"dur":1}134  // Therefore we should reverse the events list, so that events that have135  // started earlier are first in the list.136  // Then do a stable sort, we need it for the trace graph.137  std::reverse(Events.begin(), Events.end());138  llvm::stable_sort(Events, [](const auto &lhs, const auto &rhs) {139    return std::make_pair(lhs.TimestampBegin, -lhs.TimestampEnd) <140           std::make_pair(rhs.TimestampBegin, -rhs.TimestampEnd);141  });142 143  std::stringstream Stream;144  // Write a newline for better testing with multiline string literal.145  Stream << "\n";146 147  // Keep the current event stack.148  std::stack<const EventRecord *> EventStack;149  for (const auto &Event : Events) {150    // Pop every event in the stack until meeting the parent event.151    while (!EventStack.empty()) {152      bool InsideCurrentEvent =153          Event.TimestampBegin >= EventStack.top()->TimestampBegin &&154          Event.TimestampEnd <= EventStack.top()->TimestampEnd;155 156      // Presumably due to timer rounding, PerformPendingInstantiations often157      // appear to be within the timer interval of the immediately previous158      // event group. We always know these events occur at level 1, not level 2,159      // in our tests, so pop an event in that case.160      if (InsideCurrentEvent && Event.Name == "PerformPendingInstantiations" &&161          EventStack.size() == 2) {162        InsideCurrentEvent = false;163      }164 165      if (!InsideCurrentEvent)166        EventStack.pop();167      else168        break;169    }170    EventStack.push(&Event);171 172    // Write indentaion, name, detail, newline.173    for (size_t i = 1; i < EventStack.size(); ++i) {174      Stream << "| ";175    }176    Stream.write(Event.Name.data(), Event.Name.size());177    if (!Event.Metadata.empty()) {178      Stream << " (";179      Stream.write(Event.Metadata.data(), Event.Metadata.size());180      Stream << ")";181    }182    Stream << "\n";183  }184  return Stream.str();185}186 187} // namespace188 189// FIXME: Flaky test. See https://github.com/llvm/llvm-project/pull/138613190TEST(TimeProfilerTest, DISABLED_ConstantEvaluationCxx20) {191  std::string Code = R"(192void print(double value);193 194namespace slow_namespace {195 196consteval double slow_func() {197    double d = 0.0;198    for (int i = 0; i < 100; ++i) { // 8th line199        d += i;                     // 9th line200    }201    return d;202}203 204} // namespace slow_namespace205 206void slow_test() {207    constexpr auto slow_value = slow_namespace::slow_func(); // 17th line208    print(slow_namespace::slow_func());                      // 18th line209    print(slow_value);210}211 212int slow_arr[12 + 34 * 56 +                                  // 22nd line213             static_cast<int>(slow_namespace::slow_func())]; // 23rd line214 215constexpr int slow_init_list[] = {1, 1, 2, 3, 5, 8, 13, 21}; // 25th line216    )";217 218  setupProfiler();219  ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));220  std::string Json = teardownProfiler();221  ASSERT_EQ(R"(222Frontend (test.cc)223| ParseDeclarationOrFunctionDefinition (test.cc:2:1)224| ParseDeclarationOrFunctionDefinition (test.cc:6:1)225| | ParseFunctionDefinition (slow_func)226| | | EvaluateAsRValue (<test.cc:8:21>)227| | | EvaluateForOverflow (<test.cc:8:21, col:25>)228| | | EvaluateForOverflow (<test.cc:8:30, col:32>)229| | | EvaluateAsRValue (<test.cc:9:14>)230| | | EvaluateForOverflow (<test.cc:9:9, col:14>)231| | | isPotentialConstantExpr (slow_namespace::slow_func)232| | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)233| | | | EvaluateAsRValue (<test.cc:8:21, col:25>)234| | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)235| | | | EvaluateAsRValue (<test.cc:8:21, col:25>)236| ParseDeclarationOrFunctionDefinition (test.cc:16:1)237| | ParseFunctionDefinition (slow_test)238| | | EvaluateAsInitializer (slow_value)239| | | EvaluateAsConstantExpr (<test.cc:17:33, col:59>)240| | | EvaluateAsConstantExpr (<test.cc:18:11, col:37>)241| ParseDeclarationOrFunctionDefinition (test.cc:22:1)242| | EvaluateAsConstantExpr (<test.cc:23:31, col:57>)243| | EvaluateAsRValue (<test.cc:22:14, line:23:58>)244| ParseDeclarationOrFunctionDefinition (test.cc:25:1)245| | EvaluateAsInitializer (slow_init_list)246| PerformPendingInstantiations247)",248            buildTraceGraph(Json));249}250 251TEST(TimeProfilerTest, ClassTemplateInstantiations) {252  std::string Code = R"(253    template<class T>254    struct S255    {256      void foo() {}257      void bar();258    };259 260    template struct S<double>; // explicit instantiation of S<double>261 262    void user() {263      S<int> a; // implicit instantiation of S<int>264      S<float>* b;265      b->foo(); // implicit instatiation of S<float> and S<float>::foo()266    }267  )";268 269  setupProfiler();270  ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc"));271  std::string Json = teardownProfiler();272  ASSERT_EQ(R"(273Frontend (test.cc)274| ParseClass (S)275| InstantiateClass (S<double>, test.cc:9)276| InstantiateFunction (S<double>::foo, test.cc:5)277| ParseDeclarationOrFunctionDefinition (test.cc:11:5)278| | ParseFunctionDefinition (user)279| | | InstantiateClass (S<int>, test.cc:3)280| | | InstantiateClass (S<float>, test.cc:3)281| | | DeferInstantiation (S<float>::foo)282| PerformPendingInstantiations283| | InstantiateFunction (S<float>::foo, test.cc:5)284)",285            buildTraceGraph(Json));286}287 288TEST(TimeProfilerTest, TemplateInstantiations) {289  std::string B_H = R"(290    template <typename T>291    T fooC(T t) {292      return T();293    }294 295    template <typename T>296    constexpr T fooB(T t) {297      return fooC(t);298    }299 300    #define MacroTemp(x) template <typename T> void foo##x(T) { T(); }301  )";302 303  std::string A_H = R"(304    #include "b.h"305 306    MacroTemp(MTA)307 308    template <typename T>309    void fooA(T t) { fooB(t); fooMTA(t); }310  )";311  std::string Code = R"(312    #include "a.h"313    void user() { fooA(0); }314  )";315 316  setupProfiler();317  ASSERT_TRUE(compileFromString(Code, "-std=c++20", "test.cc",318                                /*Headers=*/{{"a.h", A_H}, {"b.h", B_H}}));319  std::string Json = teardownProfiler();320  ASSERT_EQ(R"(321Frontend (test.cc)322| ParseFunctionDefinition (fooC)323| ParseFunctionDefinition (fooB)324| ParseFunctionDefinition (fooMTA)325| ParseFunctionDefinition (fooA)326| ParseDeclarationOrFunctionDefinition (test.cc:3:5)327| | ParseFunctionDefinition (user)328| | | DeferInstantiation (fooA<int>)329| PerformPendingInstantiations330| | InstantiateFunction (fooA<int>, a.h:7)331| | | InstantiateFunction (fooB<int>, b.h:8)332| | | | DeferInstantiation (fooC<int>)333| | | DeferInstantiation (fooMTA<int>)334| | | InstantiateFunction (fooC<int>, b.h:3)335| | | InstantiateFunction (fooMTA<int>, a.h:4)336)",337            buildTraceGraph(Json));338}339 340TEST(TimeProfilerTest, ConstantEvaluationC99) {341  std::string Code = R"(342struct {343  short quantval[4]; // 3rd line344} value;345    )";346 347  setupProfiler();348  ASSERT_TRUE(compileFromString(Code, "-std=c99", "test.c"));349  std::string Json = teardownProfiler();350  ASSERT_EQ(R"(351Frontend (test.c)352| ParseDeclarationOrFunctionDefinition (test.c:2:1)353| | isIntegerConstantExpr (<test.c:3:18>)354| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)355| PerformPendingInstantiations356)",357            buildTraceGraph(Json));358}359