Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CLI/src/Profiler.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 "lua.h"
3
4
#include "Luau/DenseHash.h"
5
6
#include <thread>
7
#include <atomic>
8
#include <string>
9
10
struct Profiler
11
{
12
// static state
13
lua_Callbacks* callbacks = nullptr;
14
int frequency = 1000;
15
std::thread thread;
16
17
// variables for communication between loop and trigger
18
std::atomic<bool> exit = false;
19
std::atomic<uint64_t> ticks = 0;
20
std::atomic<uint64_t> samples = 0;
21
22
// private state for trigger
23
uint64_t currentTicks = 0;
24
std::string stackScratch;
25
26
// statistics, updated by trigger
27
Luau::DenseHashMap<std::string, uint64_t> data{""};
28
uint64_t gc[16] = {};
29
} gProfiler;
30
31
static void profilerTrigger(lua_State* L, int gc)
32
{
33
uint64_t currentTicks = gProfiler.ticks.load();
34
uint64_t elapsedTicks = currentTicks - gProfiler.currentTicks;
35
36
if (elapsedTicks)
37
{
38
std::string& stack = gProfiler.stackScratch;
39
40
stack.clear();
41
42
if (gc > 0)
43
stack += "GC,GC,";
44
45
lua_Debug ar;
46
for (int level = 0; lua_getinfo(L, level, "sn", &ar); ++level)
47
{
48
if (!stack.empty())
49
stack += ';';
50
51
stack += ar.short_src;
52
stack += ',';
53
if (ar.name)
54
stack += ar.name;
55
stack += ',';
56
if (ar.linedefined > 0)
57
stack += std::to_string(ar.linedefined);
58
}
59
60
if (!stack.empty())
61
{
62
gProfiler.data[stack] += elapsedTicks;
63
}
64
65
if (gc > 0)
66
{
67
gProfiler.gc[gc] += elapsedTicks;
68
}
69
}
70
71
gProfiler.currentTicks = currentTicks;
72
gProfiler.callbacks->interrupt = nullptr;
73
}
74
75
static void profilerLoop()
76
{
77
double last = lua_clock();
78
79
while (!gProfiler.exit)
80
{
81
double now = lua_clock();
82
83
if (now - last >= 1.0 / double(gProfiler.frequency))
84
{
85
int64_t ticks = int64_t((now - last) * 1e6);
86
87
gProfiler.ticks += ticks;
88
gProfiler.samples++;
89
gProfiler.callbacks->interrupt = profilerTrigger;
90
91
last += ticks * 1e-6;
92
}
93
else
94
{
95
std::this_thread::yield();
96
}
97
}
98
}
99
100
void profilerStart(lua_State* L, int frequency)
101
{
102
gProfiler.frequency = frequency;
103
gProfiler.callbacks = lua_callbacks(L);
104
105
gProfiler.exit = false;
106
gProfiler.thread = std::thread(profilerLoop);
107
}
108
109
void profilerStop()
110
{
111
gProfiler.exit = true;
112
gProfiler.thread.join();
113
}
114
115
void profilerDump(const char* path)
116
{
117
FILE* f = fopen(path, "wb");
118
if (!f)
119
{
120
fprintf(stderr, "Error opening profile %s\n", path);
121
return;
122
}
123
124
uint64_t total = 0;
125
126
for (auto& p : gProfiler.data)
127
{
128
fprintf(f, "%lld %s\n", static_cast<long long>(p.second), p.first.c_str());
129
total += p.second;
130
}
131
132
fclose(f);
133
134
printf(
135
"Profiler dump written to %s (total runtime %.3f seconds, %lld samples, %lld stacks)\n",
136
path,
137
double(total) / 1e6,
138
static_cast<long long>(gProfiler.samples.load()),
139
static_cast<long long>(gProfiler.data.size())
140
);
141
142
uint64_t totalgc = 0;
143
for (uint64_t p : gProfiler.gc)
144
totalgc += p;
145
146
if (totalgc)
147
{
148
printf("GC: %.3f seconds (%.2f%%)", double(totalgc) / 1e6, double(totalgc) / double(total) * 100);
149
150
for (size_t i = 0; i < std::size(gProfiler.gc); ++i)
151
{
152
extern const char* luaC_statename(int state);
153
154
uint64_t p = gProfiler.gc[i];
155
156
if (p)
157
printf(", %s %.2f%%", luaC_statename(int(i)), double(p) / double(totalgc) * 100);
158
}
159
160
printf("\n");
161
}
162
}
163
164