10
10
#include " clang/Frontend/FrontendActions.h"
11
11
#include " clang/Lex/PreprocessorOptions.h"
12
12
13
+ #include " llvm/ADT/StringMap.h"
13
14
#include " llvm/Support/JSON.h"
14
15
#include " llvm/Support/TimeProfiler.h"
16
+ #include " llvm/Support/VirtualFileSystem.h"
15
17
#include < stack>
16
18
17
19
#include " gtest/gtest.h"
20
+ #include < tuple>
18
21
19
22
using namespace clang ;
20
23
using namespace llvm ;
@@ -23,7 +26,8 @@ namespace {
23
26
24
27
// Should be called before testing.
25
28
void setupProfiler () {
26
- timeTraceProfilerInitialize (/* TimeTraceGranularity=*/ 0 , " test" );
29
+ timeTraceProfilerInitialize (/* TimeTraceGranularity=*/ 0 , " test" ,
30
+ /* TimeTraceVerbose=*/ true );
27
31
}
28
32
29
33
// Should be called after `compileFromString()`.
@@ -38,14 +42,24 @@ std::string teardownProfiler() {
38
42
39
43
// Returns true if code compiles successfully.
40
44
// We only parse AST here. This is enough for constexpr evaluation.
41
- bool compileFromString (StringRef Code, StringRef Standard, StringRef FileName) {
45
+ bool compileFromString (StringRef Code, StringRef Standard, StringRef File,
46
+ llvm::StringMap<std::string> Headers = {}) {
42
47
CompilerInstance Compiler;
43
48
Compiler.createDiagnostics ();
44
49
50
+ llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS (
51
+ new llvm::vfs::InMemoryFileSystem ());
52
+ FS->addFile (File, 0 , MemoryBuffer::getMemBuffer (Code));
53
+ for (const auto &Header : Headers) {
54
+ FS->addFile (Header.getKey (), 0 ,
55
+ MemoryBuffer::getMemBuffer (Header.getValue ()));
56
+ }
57
+ llvm::IntrusiveRefCntPtr<FileManager> Files (
58
+ new FileManager (FileSystemOptions (), FS));
59
+ Compiler.setFileManager (Files.get ());
60
+
45
61
auto Invocation = std::make_shared<CompilerInvocation>();
46
- Invocation->getPreprocessorOpts ().addRemappedFile (
47
- FileName, MemoryBuffer::getMemBuffer (Code).release ());
48
- const char *Args[] = {Standard.data (), FileName.data ()};
62
+ std::vector<const char *> Args = {Standard.data (), File.data ()};
49
63
CompilerInvocation::CreateFromArgs (*Invocation, Args,
50
64
Compiler.getDiagnostics ());
51
65
Compiler.setInvocation (std::move (Invocation));
@@ -60,13 +74,27 @@ bool compileFromString(StringRef Code, StringRef Standard, StringRef FileName) {
60
74
return Compiler.ExecuteAction (Action);
61
75
}
62
76
77
+ std::string GetMetadata (json::Object *Event) {
78
+ std::string Metadata;
79
+ llvm::raw_string_ostream OS (Metadata);
80
+ if (json::Object *Args = Event->getObject (" args" )) {
81
+ if (auto Detail = Args->getString (" detail" ))
82
+ OS << Detail->str ();
83
+ if (auto File = Args->getString (" file" ))
84
+ OS << " , " << File->str ();
85
+ if (auto Line = Args->getInteger (" line" ))
86
+ OS << " :" << *Line;
87
+ }
88
+ return Metadata;
89
+ }
90
+
63
91
// Returns pretty-printed trace graph.
64
92
std::string buildTraceGraph (StringRef Json) {
65
93
struct EventRecord {
66
94
int64_t TimestampBegin;
67
95
int64_t TimestampEnd;
68
- StringRef Name;
69
- StringRef Detail ;
96
+ std::string Name;
97
+ std::string Metadata ;
70
98
};
71
99
std::vector<EventRecord> Events;
72
100
@@ -81,18 +109,21 @@ std::string buildTraceGraph(StringRef Json) {
81
109
int64_t TimestampBegin = TraceEventObj->getInteger (" ts" ).value_or (0 );
82
110
int64_t TimestampEnd =
83
111
TimestampBegin + TraceEventObj->getInteger (" dur" ).value_or (0 );
84
- StringRef Name = TraceEventObj->getString (" name" ).value_or (" " );
85
- StringRef Detail = " " ;
86
- if (json::Object *Args = TraceEventObj->getObject (" args" ))
87
- Detail = Args->getString (" detail" ).value_or (" " );
112
+ std::string Name = TraceEventObj->getString (" name" ).value_or (" " ).str ();
113
+ std::string Metadata = GetMetadata (TraceEventObj);
114
+
115
+ // Source events are asynchronous events and may not perfectly nest the
116
+ // synchronous events. Skip testing them.
117
+ if (Name == " Source" )
118
+ continue ;
88
119
89
120
// This is a "summary" event, like "Total PerformPendingInstantiations",
90
121
// skip it
91
122
if (TimestampBegin == 0 )
92
123
continue ;
93
124
94
125
Events.emplace_back (
95
- EventRecord{TimestampBegin, TimestampEnd, Name, Detail });
126
+ EventRecord{TimestampBegin, TimestampEnd, Name, Metadata });
96
127
}
97
128
98
129
// There can be nested events that are very fast, for example:
@@ -132,9 +163,9 @@ std::string buildTraceGraph(StringRef Json) {
132
163
Stream << " | " ;
133
164
}
134
165
Stream.write (Event.Name .data (), Event.Name .size ());
135
- if (!Event.Detail .empty ()) {
166
+ if (!Event.Metadata .empty ()) {
136
167
Stream << " (" ;
137
- Stream.write (Event.Detail .data (), Event.Detail .size ());
168
+ Stream.write (Event.Metadata .data (), Event.Metadata .size ());
138
169
Stream << " )" ;
139
170
}
140
171
Stream << " \n " ;
@@ -145,7 +176,7 @@ std::string buildTraceGraph(StringRef Json) {
145
176
} // namespace
146
177
147
178
TEST (TimeProfilerTest, ConstantEvaluationCxx20) {
148
- constexpr StringRef Code = R"(
179
+ std::string Code = R"(
149
180
void print(double value);
150
181
151
182
namespace slow_namespace {
@@ -175,8 +206,7 @@ constexpr int slow_init_list[] = {1, 1, 2, 3, 5, 8, 13, 21}; // 25th line
175
206
setupProfiler ();
176
207
ASSERT_TRUE (compileFromString (Code, " -std=c++20" , " test.cc" ));
177
208
std::string Json = teardownProfiler ();
178
- std::string TraceGraph = buildTraceGraph (Json);
179
- ASSERT_TRUE (TraceGraph == R"(
209
+ ASSERT_EQ (R"(
180
210
Frontend
181
211
| ParseDeclarationOrFunctionDefinition (test.cc:2:1)
182
212
| ParseDeclarationOrFunctionDefinition (test.cc:6:1)
@@ -202,14 +232,54 @@ Frontend
202
232
| ParseDeclarationOrFunctionDefinition (test.cc:25:1)
203
233
| | EvaluateAsInitializer (slow_init_list)
204
234
| PerformPendingInstantiations
205
- )" );
235
+ )" ,
236
+ buildTraceGraph (Json));
237
+ }
238
+
239
+ TEST (TimeProfilerTest, TemplateInstantiations) {
240
+ std::string B_H = R"(
241
+ template <typename T>
242
+ T fooB(T t) {
243
+ return T();
244
+ }
206
245
207
- // NOTE: If this test is failing, run this test with
208
- // `llvm::errs() << TraceGraph;` and change the assert above.
246
+ #define MacroTemp(x) template <typename T> void foo##x(T) { T(); }
247
+ )" ;
248
+
249
+ std::string A_H = R"(
250
+ #include "b.h"
251
+
252
+ MacroTemp(MTA)
253
+
254
+ template <typename T>
255
+ void fooA(T t) { fooB(t); fooMTA(t); }
256
+ )" ;
257
+ std::string Code = R"(
258
+ #include "a.h"
259
+ void user() { fooA(0); }
260
+ )" ;
261
+
262
+ setupProfiler ();
263
+ ASSERT_TRUE (compileFromString (Code, " -std=c++20" , " test.cc" ,
264
+ /* Headers=*/ {{" a.h" , A_H}, {" b.h" , B_H}}));
265
+ std::string Json = teardownProfiler ();
266
+ ASSERT_EQ (R"(
267
+ Frontend
268
+ | ParseFunctionDefinition (fooB)
269
+ | ParseFunctionDefinition (fooMTA)
270
+ | ParseFunctionDefinition (fooA)
271
+ | ParseDeclarationOrFunctionDefinition (test.cc:3:5)
272
+ | | ParseFunctionDefinition (user)
273
+ | PerformPendingInstantiations
274
+ | | InstantiateFunction (fooA<int>, ./a.h:7)
275
+ | | | InstantiateFunction (fooB<int>, ./b.h:3)
276
+ | | | InstantiateFunction (fooMTA<int>, ./a.h:4)
277
+ )" ,
278
+ buildTraceGraph (Json));
209
279
}
210
280
211
281
TEST (TimeProfilerTest, ConstantEvaluationC99) {
212
- constexpr StringRef Code = R"(
282
+ std::string Code = R"(
213
283
struct {
214
284
short quantval[4]; // 3rd line
215
285
} value;
@@ -218,15 +288,12 @@ struct {
218
288
setupProfiler ();
219
289
ASSERT_TRUE (compileFromString (Code, " -std=c99" , " test.c" ));
220
290
std::string Json = teardownProfiler ();
221
- std::string TraceGraph = buildTraceGraph (Json);
222
- ASSERT_TRUE (TraceGraph == R"(
291
+ ASSERT_EQ (R"(
223
292
Frontend
224
293
| ParseDeclarationOrFunctionDefinition (test.c:2:1)
225
294
| | isIntegerConstantExpr (<test.c:3:18>)
226
295
| | EvaluateKnownConstIntCheckOverflow (<test.c:3:18>)
227
296
| PerformPendingInstantiations
228
- )" );
229
-
230
- // NOTE: If this test is failing, run this test with
231
- // `llvm::errs() << TraceGraph;` and change the assert above.
297
+ )" ,
298
+ buildTraceGraph (Json));
232
299
}
0 commit comments