Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Common/include/Luau/TimeTrace.h
2727 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#pragma once
3
4
#include "Luau/Common.h"
5
6
#include <vector>
7
#include <memory>
8
9
#include <stdint.h>
10
#include <string.h>
11
12
LUAU_FASTFLAG(DebugLuauTimeTracing)
13
14
namespace Luau
15
{
16
namespace TimeTrace
17
{
18
double getClock();
19
uint32_t getClockMicroseconds();
20
} // namespace TimeTrace
21
} // namespace Luau
22
23
#if defined(LUAU_ENABLE_TIME_TRACE)
24
25
namespace Luau
26
{
27
namespace TimeTrace
28
{
29
struct Token
30
{
31
const char* name;
32
const char* category;
33
};
34
35
enum class EventType : uint8_t
36
{
37
Enter,
38
Leave,
39
40
ArgName,
41
ArgValue,
42
};
43
44
struct Event
45
{
46
EventType type;
47
uint16_t token;
48
49
union
50
{
51
uint32_t microsec; // 1 hour trace limit
52
uint32_t dataPos;
53
} data;
54
};
55
56
struct GlobalContext;
57
struct ThreadContext;
58
59
std::shared_ptr<GlobalContext> getGlobalContext();
60
61
uint16_t createToken(GlobalContext& context, const char* name, const char* category);
62
uint32_t createThread(GlobalContext& context, ThreadContext* threadContext);
63
void releaseThread(GlobalContext& context, ThreadContext* threadContext);
64
void flushEvents(GlobalContext& context, uint32_t threadId, const std::vector<Event>& events, const std::vector<char>& data);
65
66
struct ThreadContext
67
{
68
ThreadContext()
69
: globalContext(getGlobalContext())
70
{
71
threadId = createThread(*globalContext, this);
72
}
73
74
~ThreadContext()
75
{
76
if (!events.empty())
77
flushEvents();
78
79
releaseThread(*globalContext, this);
80
}
81
82
void flushEvents()
83
{
84
static uint16_t flushToken = createToken(*globalContext, "flushEvents", "TimeTrace");
85
86
events.push_back({EventType::Enter, flushToken, {getClockMicroseconds()}});
87
88
TimeTrace::flushEvents(*globalContext, threadId, events, data);
89
90
events.clear();
91
data.clear();
92
93
events.push_back({EventType::Leave, 0, {getClockMicroseconds()}});
94
}
95
96
void eventEnter(uint16_t token)
97
{
98
eventEnter(token, getClockMicroseconds());
99
}
100
101
void eventEnter(uint16_t token, uint32_t microsec)
102
{
103
events.push_back({EventType::Enter, token, {microsec}});
104
}
105
106
void eventLeave()
107
{
108
eventLeave(getClockMicroseconds());
109
}
110
111
void eventLeave(uint32_t microsec)
112
{
113
events.push_back({EventType::Leave, 0, {microsec}});
114
115
if (events.size() > kEventFlushLimit)
116
flushEvents();
117
}
118
119
void eventArgument(const char* name, const char* value)
120
{
121
uint32_t pos = uint32_t(data.size());
122
data.insert(data.end(), name, name + strlen(name) + 1);
123
events.push_back({EventType::ArgName, 0, {pos}});
124
125
pos = uint32_t(data.size());
126
data.insert(data.end(), value, value + strlen(value) + 1);
127
events.push_back({EventType::ArgValue, 0, {pos}});
128
}
129
130
std::shared_ptr<GlobalContext> globalContext;
131
uint32_t threadId;
132
std::vector<Event> events;
133
std::vector<char> data;
134
135
static constexpr size_t kEventFlushLimit = 8192;
136
};
137
138
using ThreadContextProvider = ThreadContext& (*)();
139
140
inline ThreadContextProvider& threadContextProvider()
141
{
142
static ThreadContextProvider handler = nullptr;
143
return handler;
144
}
145
146
ThreadContext& getThreadContext();
147
148
struct Scope
149
{
150
explicit Scope(uint16_t token)
151
: context(getThreadContext())
152
{
153
if (!FFlag::DebugLuauTimeTracing)
154
return;
155
156
context.eventEnter(token);
157
}
158
159
~Scope()
160
{
161
if (!FFlag::DebugLuauTimeTracing)
162
return;
163
164
context.eventLeave();
165
}
166
167
ThreadContext& context;
168
};
169
170
struct OptionalTailScope
171
{
172
explicit OptionalTailScope(uint16_t token, uint32_t threshold)
173
: context(getThreadContext())
174
, token(token)
175
, threshold(threshold)
176
{
177
if (!FFlag::DebugLuauTimeTracing)
178
return;
179
180
pos = uint32_t(context.events.size());
181
microsec = getClockMicroseconds();
182
}
183
184
~OptionalTailScope()
185
{
186
if (!FFlag::DebugLuauTimeTracing)
187
return;
188
189
if (pos == context.events.size())
190
{
191
uint32_t curr = getClockMicroseconds();
192
193
if (curr - microsec > threshold)
194
{
195
context.eventEnter(token, microsec);
196
context.eventLeave(curr);
197
}
198
}
199
}
200
201
ThreadContext& context;
202
uint16_t token;
203
uint32_t threshold;
204
uint32_t microsec;
205
uint32_t pos;
206
};
207
208
LUAU_NOINLINE uint16_t createScopeData(const char* name, const char* category);
209
210
} // namespace TimeTrace
211
} // namespace Luau
212
213
// Regular scope
214
#define LUAU_TIMETRACE_SCOPE(name, category) \
215
static uint16_t lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \
216
Luau::TimeTrace::Scope lttScope(lttScopeStatic)
217
218
// A scope without nested scopes that may be skipped if the time it took is less than the threshold
219
#define LUAU_TIMETRACE_OPTIONAL_TAIL_SCOPE(name, category, microsec) \
220
static uint16_t lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \
221
Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail, microsec)
222
223
// Extra key/value data can be added to regular scopes
224
#define LUAU_TIMETRACE_ARGUMENT(name, value) \
225
do \
226
{ \
227
if (FFlag::DebugLuauTimeTracing) \
228
lttScope.context.eventArgument(name, value); \
229
} while (false)
230
231
#else
232
233
#define LUAU_TIMETRACE_SCOPE(name, category)
234
#define LUAU_TIMETRACE_OPTIONAL_TAIL_SCOPE(name, category, microsec)
235
#define LUAU_TIMETRACE_ARGUMENT(name, value) \
236
do \
237
{ \
238
} while (false)
239
240
#endif
241
242