luau/CLI/Profiler.cpp
Arseny Kapoulkine f2e6a8f4a5
Sync to upstream/release/507-pre (#286)
This doesn't contain all changes for 507 yet but we might want to do the
Luau 0.507 release a bit earlier to end the year sooner.

Changes:

- Type ascription (::) now permits casts between related types in both directions, allowing to refine or loosen the type (RFC #56)
- Fix type definition for tonumber to return number? since the input string isn't guaranteed to contain a valid number
- Fix type refinements for field access via []
- Many stability fixes for type checker
- Provide extra information in error messages for type mismatches in more cases
- Improve performance of type checking for large unions when union members are string literals
- Add coverage reporting support to Repl (--coverage command line argument) and lua_getcoverage C API
- Work around code signing issues during Makefile builds on macOS
- Improve performance of truthiness checks in some cases, particularly on Apple M1, resulting in 10-25% perf gains on qsort benchmark depending on the CPU/compiler
- Fix support for little-endian systems; IBM s390x here we go!
2021-12-10 14:05:05 -08:00

156 lines
3.6 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lua.h"
#include "Luau/DenseHash.h"
#include <thread>
#include <atomic>
#include <string>
struct Profiler
{
// static state
lua_Callbacks* callbacks = nullptr;
int frequency = 1000;
std::thread thread;
// variables for communication between loop and trigger
std::atomic<bool> exit = false;
std::atomic<uint64_t> ticks = 0;
std::atomic<uint64_t> samples = 0;
// private state for trigger
uint64_t currentTicks = 0;
std::string stackScratch;
// statistics, updated by trigger
Luau::DenseHashMap<std::string, uint64_t> data{""};
uint64_t gc[16] = {};
} gProfiler;
static void profilerTrigger(lua_State* L, int gc)
{
uint64_t currentTicks = gProfiler.ticks.load();
uint64_t elapsedTicks = currentTicks - gProfiler.currentTicks;
if (elapsedTicks)
{
std::string& stack = gProfiler.stackScratch;
stack.clear();
if (gc > 0)
stack += "GC,GC,";
lua_Debug ar;
for (int level = 0; lua_getinfo(L, level, "sn", &ar); ++level)
{
if (!stack.empty())
stack += ';';
stack += ar.short_src;
stack += ',';
if (ar.name)
stack += ar.name;
stack += ',';
if (ar.linedefined > 0)
stack += std::to_string(ar.linedefined);
}
if (!stack.empty())
{
gProfiler.data[stack] += elapsedTicks;
}
if (gc > 0)
{
gProfiler.gc[gc] += elapsedTicks;
}
}
gProfiler.currentTicks = currentTicks;
gProfiler.callbacks->interrupt = nullptr;
}
static void profilerLoop()
{
double last = lua_clock();
while (!gProfiler.exit)
{
double now = lua_clock();
if (now - last >= 1.0 / double(gProfiler.frequency))
{
gProfiler.ticks += uint64_t((now - last) * 1e6);
gProfiler.samples++;
gProfiler.callbacks->interrupt = profilerTrigger;
last = now;
}
else
{
std::this_thread::yield();
}
}
}
void profilerStart(lua_State* L, int frequency)
{
gProfiler.frequency = frequency;
gProfiler.callbacks = lua_callbacks(L);
gProfiler.exit = false;
gProfiler.thread = std::thread(profilerLoop);
}
void profilerStop()
{
gProfiler.exit = true;
gProfiler.thread.join();
}
void profilerDump(const char* path)
{
FILE* f = fopen(path, "wb");
if (!f)
{
fprintf(stderr, "Error opening profile %s\n", path);
return;
}
uint64_t total = 0;
for (auto& p : gProfiler.data)
{
fprintf(f, "%lld %s\n", static_cast<long long>(p.second), p.first.c_str());
total += p.second;
}
fclose(f);
printf("Profiler dump written to %s (total runtime %.3f seconds, %lld samples, %lld stacks)\n", path, double(total) / 1e6,
static_cast<long long>(gProfiler.samples.load()), static_cast<long long>(gProfiler.data.size()));
uint64_t totalgc = 0;
for (uint64_t p : gProfiler.gc)
totalgc += p;
if (totalgc)
{
printf("GC: %.3f seconds (%.2f%%)", double(totalgc) / 1e6, double(totalgc) / double(total) * 100);
for (size_t i = 0; i < std::size(gProfiler.gc); ++i)
{
extern const char* luaC_statename(int state);
uint64_t p = gProfiler.gc[i];
if (p)
printf(", %s %.2f%%", luaC_statename(int(i)), double(p) / double(totalgc) * 100);
}
printf("\n");
}
}