1 //===- unittests/Support/TimeProfilerTest.cpp -----------------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 #include "clang/Frontend/CompilerInstance.h"
10 #include "clang/Frontend/FrontendActions.h"
11 #include "clang/Lex/PreprocessorOptions.h"
13 #include "llvm/Support/JSON.h"
14 #include "llvm/Support/TimeProfiler.h"
16 #include "gtest/gtest.h"
18 using namespace clang
;
23 // Should be called before testing.
24 void setupProfiler() {
25 timeTraceProfilerInitialize(/*TimeTraceGranularity=*/0, "test");
28 // Should be called after `compileFromString()`.
29 // Returns profiler's JSON dump.
30 std::string
teardownProfiler() {
31 SmallVector
<char, 1024> SmallVec
;
32 raw_svector_ostream
OS(SmallVec
);
33 timeTraceProfilerWrite(OS
);
34 timeTraceProfilerCleanup();
35 return OS
.str().str();
38 // Returns true if code compiles successfully.
39 // We only parse AST here. This is enough for constexpr evaluation.
40 bool compileFromString(StringRef Code
, StringRef Standard
, StringRef FileName
) {
41 CompilerInstance Compiler
;
42 Compiler
.createDiagnostics();
44 auto Invocation
= std::make_shared
<CompilerInvocation
>();
45 Invocation
->getPreprocessorOpts().addRemappedFile(
46 FileName
, MemoryBuffer::getMemBuffer(Code
).release());
47 const char *Args
[] = {Standard
.data(), FileName
.data()};
48 CompilerInvocation::CreateFromArgs(*Invocation
, Args
,
49 Compiler
.getDiagnostics());
50 Compiler
.setInvocation(std::move(Invocation
));
52 class TestFrontendAction
: public ASTFrontendAction
{
54 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&CI
,
55 StringRef InFile
) override
{
56 return std::make_unique
<ASTConsumer
>();
59 return Compiler
.ExecuteAction(Action
);
62 // Returns pretty-printed trace graph.
63 std::string
buildTraceGraph(StringRef Json
) {
65 int64_t TimestampBegin
;
70 std::vector
<EventRecord
> Events
;
72 // Parse `EventRecord`s from JSON dump.
73 Expected
<json::Value
> Root
= json::parse(Json
);
76 for (json::Value
&TraceEventValue
:
77 *Root
->getAsObject()->getArray("traceEvents")) {
78 json::Object
*TraceEventObj
= TraceEventValue
.getAsObject();
80 int64_t TimestampBegin
= TraceEventObj
->getInteger("ts").value_or(0);
81 int64_t TimestampEnd
=
82 TimestampBegin
+ TraceEventObj
->getInteger("dur").value_or(0);
83 StringRef Name
= TraceEventObj
->getString("name").value_or("");
84 StringRef Detail
= "";
85 if (json::Object
*Args
= TraceEventObj
->getObject("args"))
86 Detail
= Args
->getString("detail").value_or("");
88 // This is a "summary" event, like "Total PerformPendingInstantiations",
90 if (TimestampBegin
== 0)
94 EventRecord
{TimestampBegin
, TimestampEnd
, Name
, Detail
});
97 // There can be nested events that are very fast, for example:
98 // {"name":"EvaluateAsBooleanCondition",... ,"ts":2380,"dur":1}
99 // {"name":"EvaluateAsRValue",... ,"ts":2380,"dur":1}
100 // Therefore we should reverse the events list, so that events that have
101 // started earlier are first in the list.
102 // Then do a stable sort, we need it for the trace graph.
103 std::reverse(Events
.begin(), Events
.end());
105 Events
.begin(), Events
.end(), [](const auto &lhs
, const auto &rhs
) {
106 return std::make_pair(lhs
.TimestampBegin
, -lhs
.TimestampEnd
) <
107 std::make_pair(rhs
.TimestampBegin
, -rhs
.TimestampEnd
);
110 std::stringstream Stream
;
111 // Write a newline for better testing with multiline string literal.
114 // Keep the current event stack.
115 std::stack
<const EventRecord
*> EventStack
;
116 for (const auto &Event
: Events
) {
117 // Pop every event in the stack until meeting the parent event.
118 while (!EventStack
.empty()) {
119 bool InsideCurrentEvent
=
120 Event
.TimestampBegin
>= EventStack
.top()->TimestampBegin
&&
121 Event
.TimestampEnd
<= EventStack
.top()->TimestampEnd
;
122 if (!InsideCurrentEvent
)
127 EventStack
.push(&Event
);
129 // Write indentaion, name, detail, newline.
130 for (size_t i
= 1; i
< EventStack
.size(); ++i
) {
133 Stream
.write(Event
.Name
.data(), Event
.Name
.size());
134 if (!Event
.Detail
.empty()) {
136 Stream
.write(Event
.Detail
.data(), Event
.Detail
.size());
146 TEST(TimeProfilerTest
, ConstantEvaluationCxx20
) {
147 constexpr StringRef Code
= R
"(
148 void print(double value);
150 namespace slow_namespace {
152 consteval double slow_func() {
154 for (int i = 0; i < 100; ++i) { // 8th line
160 } // namespace slow_namespace
163 constexpr auto slow_value = slow_namespace::slow_func(); // 17th line
164 print(slow_namespace::slow_func()); // 18th line
168 int slow_arr[12 + 34 * 56 + // 22nd line
169 static_cast<int>(slow_namespace::slow_func())]; // 23rd line
171 constexpr int slow_init_list[] = {1, 1, 2, 3, 5, 8, 13, 21}; // 25th line
175 ASSERT_TRUE(compileFromString(Code
, "-std=c++20", "test.cc"));
176 std::string Json
= teardownProfiler();
177 std::string TraceGraph
= buildTraceGraph(Json
);
178 ASSERT_TRUE(TraceGraph
== R
"(
180 | ParseDeclarationOrFunctionDefinition (test.cc:2:1)
181 | ParseDeclarationOrFunctionDefinition (test.cc:6:1)
182 | | ParseFunctionDefinition (slow_func)
183 | | | EvaluateAsRValue (<test.cc:8:21>)
184 | | | EvaluateForOverflow (<test.cc:8:21, col:25>)
185 | | | EvaluateForOverflow (<test.cc:8:30, col:32>)
186 | | | EvaluateAsRValue (<test.cc:9:14>)
187 | | | EvaluateForOverflow (<test.cc:9:9, col:14>)
188 | | | isPotentialConstantExpr (slow_namespace::slow_func)
189 | | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
190 | | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
191 | | | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
192 | | | | EvaluateAsRValue (<test.cc:8:21, col:25>)
193 | ParseDeclarationOrFunctionDefinition (test.cc:16:1)
194 | | ParseFunctionDefinition (slow_test)
195 | | | EvaluateAsInitializer (slow_value)
196 | | | EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
197 | | | EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
198 | ParseDeclarationOrFunctionDefinition (test.cc:22:1)
199 | | EvaluateAsConstantExpr (<test.cc:23:31, col:57>)
200 | | EvaluateAsRValue (<test.cc:22:14, line:23:58>)
201 | ParseDeclarationOrFunctionDefinition (test.cc:25:1)
202 | | EvaluateAsInitializer (slow_init_list)
203 | PerformPendingInstantiations
206 // NOTE: If this test is failing, run this test with
207 // `llvm::errs() << TraceGraph;` and change the assert above.
210 TEST(TimeProfilerTest
, ConstantEvaluationC99
) {
211 constexpr StringRef Code
= R
"(
213 short quantval[4]; // 3rd line
218 ASSERT_TRUE(compileFromString(Code
, "-std=c99", "test.c"));
219 std::string Json
= teardownProfiler();
220 std::string TraceGraph
= buildTraceGraph(Json
);
221 ASSERT_TRUE(TraceGraph
== R
"(
223 | ParseDeclarationOrFunctionDefinition (test.c:2:1)
224 | | isIntegerConstantExpr (<test.c:3:18>)
225 | | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
226 | PerformPendingInstantiations
229 // NOTE: If this test is failing, run this test with
230 // `llvm::errs() << TraceGraph;` and change the assert above.