Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Common/src/TimeTrace.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "Luau/TimeTrace.h"
3
4
#include "Luau/StringUtils.h"
5
6
#include <algorithm>
7
#include <mutex>
8
#include <string>
9
10
#include <stdlib.h>
11
12
#ifdef _WIN32
13
#ifndef WIN32_LEAN_AND_MEAN
14
#define WIN32_LEAN_AND_MEAN
15
#endif
16
#ifndef NOMINMAX
17
#define NOMINMAX
18
#endif
19
#include <windows.h>
20
#endif
21
22
#ifdef __APPLE__
23
#include <mach/mach.h>
24
#include <mach/mach_time.h>
25
#endif
26
27
#include <time.h>
28
29
LUAU_FASTFLAGVARIABLE(DebugLuauTimeTracing)
30
namespace Luau
31
{
32
namespace TimeTrace
33
{
34
static double getClockPeriod()
35
{
36
#if defined(_WIN32)
37
LARGE_INTEGER result = {};
38
QueryPerformanceFrequency(&result);
39
return 1.0 / double(result.QuadPart);
40
#elif defined(__APPLE__)
41
mach_timebase_info_data_t result = {};
42
mach_timebase_info(&result);
43
return double(result.numer) / double(result.denom) * 1e-9;
44
#elif defined(__linux__) || defined(__FreeBSD__)
45
return 1e-9;
46
#else
47
return 1.0 / double(CLOCKS_PER_SEC);
48
#endif
49
}
50
51
static double getClockTimestamp()
52
{
53
#if defined(_WIN32)
54
LARGE_INTEGER result = {};
55
QueryPerformanceCounter(&result);
56
return double(result.QuadPart);
57
#elif defined(__APPLE__)
58
return double(mach_absolute_time());
59
#elif defined(__linux__) || defined(__FreeBSD__)
60
timespec now;
61
clock_gettime(CLOCK_MONOTONIC, &now);
62
return now.tv_sec * 1e9 + now.tv_nsec;
63
#else
64
return double(clock());
65
#endif
66
}
67
68
double getClock()
69
{
70
static double period = getClockPeriod();
71
static double start = getClockTimestamp();
72
73
return (getClockTimestamp() - start) * period;
74
}
75
76
uint32_t getClockMicroseconds()
77
{
78
static double period = getClockPeriod() * 1e6;
79
static double start = getClockTimestamp();
80
81
return uint32_t((getClockTimestamp() - start) * period);
82
}
83
} // namespace TimeTrace
84
} // namespace Luau
85
86
#if defined(LUAU_ENABLE_TIME_TRACE)
87
88
namespace Luau
89
{
90
namespace TimeTrace
91
{
92
struct GlobalContext
93
{
94
~GlobalContext()
95
{
96
if (traceFile)
97
fclose(traceFile);
98
}
99
100
std::mutex mutex;
101
std::vector<ThreadContext*> threads;
102
uint32_t nextThreadId = 0;
103
std::vector<Token> tokens;
104
FILE* traceFile = nullptr;
105
106
private:
107
friend std::shared_ptr<GlobalContext> getGlobalContext();
108
GlobalContext() = default;
109
};
110
111
std::shared_ptr<GlobalContext> getGlobalContext()
112
{
113
static std::shared_ptr<GlobalContext> context = std::shared_ptr<GlobalContext>{new GlobalContext};
114
return context;
115
}
116
117
uint16_t createToken(GlobalContext& context, const char* name, const char* category)
118
{
119
std::scoped_lock lock(context.mutex);
120
121
LUAU_ASSERT(context.tokens.size() < 64 * 1024);
122
123
context.tokens.push_back({name, category});
124
return uint16_t(context.tokens.size() - 1);
125
}
126
127
uint32_t createThread(GlobalContext& context, ThreadContext* threadContext)
128
{
129
std::scoped_lock lock(context.mutex);
130
131
context.threads.push_back(threadContext);
132
133
return ++context.nextThreadId;
134
}
135
136
void releaseThread(GlobalContext& context, ThreadContext* threadContext)
137
{
138
std::scoped_lock lock(context.mutex);
139
140
if (auto it = std::find(context.threads.begin(), context.threads.end(), threadContext); it != context.threads.end())
141
context.threads.erase(it);
142
}
143
144
void flushEvents(GlobalContext& context, uint32_t threadId, const std::vector<Event>& events, const std::vector<char>& data)
145
{
146
std::scoped_lock lock(context.mutex);
147
148
if (!context.traceFile)
149
{
150
context.traceFile = fopen("trace.json", "w");
151
152
if (!context.traceFile)
153
return;
154
155
fprintf(context.traceFile, "[\n");
156
}
157
158
std::string temp;
159
const unsigned tempReserve = 64 * 1024;
160
temp.reserve(tempReserve);
161
162
const char* rawData = data.data();
163
164
// Formatting state
165
bool unfinishedEnter = false;
166
bool unfinishedArgs = false;
167
168
for (const Event& ev : events)
169
{
170
switch (ev.type)
171
{
172
case EventType::Enter:
173
{
174
if (unfinishedArgs)
175
{
176
formatAppend(temp, "}");
177
unfinishedArgs = false;
178
}
179
180
if (unfinishedEnter)
181
{
182
formatAppend(temp, "},\n");
183
unfinishedEnter = false;
184
}
185
186
Token& token = context.tokens[ev.token];
187
188
formatAppend(
189
temp,
190
R"({"name": "%s", "cat": "%s", "ph": "B", "ts": %u, "pid": 0, "tid": %u)",
191
token.name,
192
token.category,
193
ev.data.microsec,
194
threadId
195
);
196
unfinishedEnter = true;
197
}
198
break;
199
case EventType::Leave:
200
if (unfinishedArgs)
201
{
202
formatAppend(temp, "}");
203
unfinishedArgs = false;
204
}
205
if (unfinishedEnter)
206
{
207
formatAppend(temp, "},\n");
208
unfinishedEnter = false;
209
}
210
211
formatAppend(
212
temp,
213
R"({"ph": "E", "ts": %u, "pid": 0, "tid": %u},)"
214
"\n",
215
ev.data.microsec,
216
threadId
217
);
218
break;
219
case EventType::ArgName:
220
LUAU_ASSERT(unfinishedEnter);
221
222
if (!unfinishedArgs)
223
{
224
formatAppend(temp, R"(, "args": { "%s": )", rawData + ev.data.dataPos);
225
unfinishedArgs = true;
226
}
227
else
228
{
229
formatAppend(temp, R"(, "%s": )", rawData + ev.data.dataPos);
230
}
231
break;
232
case EventType::ArgValue:
233
LUAU_ASSERT(unfinishedArgs);
234
formatAppend(temp, R"("%s")", rawData + ev.data.dataPos);
235
break;
236
}
237
238
// Don't want to hit the string capacity and reallocate
239
if (temp.size() > tempReserve - 1024)
240
{
241
fwrite(temp.data(), 1, temp.size(), context.traceFile);
242
temp.clear();
243
}
244
}
245
246
if (unfinishedArgs)
247
{
248
formatAppend(temp, "}");
249
unfinishedArgs = false;
250
}
251
if (unfinishedEnter)
252
{
253
formatAppend(temp, "},\n");
254
unfinishedEnter = false;
255
}
256
257
fwrite(temp.data(), 1, temp.size(), context.traceFile);
258
fflush(context.traceFile);
259
}
260
261
ThreadContext& getThreadContext()
262
{
263
// Check custom provider that which might implement a custom TLS
264
if (auto provider = threadContextProvider())
265
return provider();
266
267
thread_local ThreadContext context;
268
return context;
269
}
270
271
uint16_t createScopeData(const char* name, const char* category)
272
{
273
return createToken(*Luau::TimeTrace::getGlobalContext(), name, category);
274
}
275
} // namespace TimeTrace
276
} // namespace Luau
277
278
#endif
279
280