16std::atomic<bool> g_enabled{
false};
19 return std::chrono::duration_cast<std::chrono::nanoseconds>(
20 std::chrono::steady_clock::now().time_since_epoch())
25std::mutex g_modulesMutex;
26std::vector<std::string> g_moduleNames{
""};
27std::unordered_map<std::string, uint32_t> g_moduleIds;
29uint64_t zoneKey(uint32_t module,
const char*
name) {
30 return (
static_cast<uint64_t
>(module) << 32) ^ std::hash<const char*>{}(
name);
48std::mutex g_threadsMutex;
52 static thread_local ThreadBuffer
tb;
53 if (
tb.threadId == 0) {
55 static_cast<uint32_t
>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
56 const bool isMain = std::this_thread::get_id() == std::thread::id();
57 tb.threadName = isMain ?
"main" :
"worker";
58 std::lock_guard<std::mutex> lock(g_threadsMutex);
59 threads().push_back(&
tb);
64std::vector<Profiler::ThreadBuffer*>& Profiler::threads() {
65 static std::vector<ThreadBuffer*>
t;
69Profiler::Frame& Profiler::lastFrameStorage() {
74bool& Profiler::hasFrameFlag() {
75 static bool b =
false;
80 if (!module || !module[0])
return 0;
81 std::lock_guard<std::mutex> lock(g_modulesMutex);
82 auto it = g_moduleIds.find(module);
83 if (it != g_moduleIds.end())
return it->second;
84 const uint32_t
id =
static_cast<uint32_t
>(g_moduleNames.size());
85 g_moduleNames.push_back(module);
86 g_moduleIds.emplace(module,
id);
97 if (!g_enabled.load(std::memory_order_relaxed))
return;
102 r.threadId =
tb.threadId;
103 r.parent =
tb.stack.empty() ? -1 :
tb.stack.back();
104 r.depth =
static_cast<int16_t
>(
tb.stack.size());
108 const int32_t
idx =
static_cast<int32_t
>(
tb.records.size());
109 tb.records.push_back(
r);
110 tb.stack.push_back(
idx);
114 if (!g_enabled.load(std::memory_order_relaxed))
return;
116 if (
tb.stack.empty())
return;
117 const int32_t
idx =
tb.stack.back();
119 tb.records[
static_cast<size_t>(
idx)].endNs = nowNs();
123 std::lock_guard<std::mutex> lock(g_threadsMutex);
124 for (
auto*
tb : threads()) {
128 lastFrameStorage() =
Frame{};
129 hasFrameFlag() =
false;
133 if (!g_enabled.load(std::memory_order_relaxed))
return 0.0;
135 const int64_t t0 = nowNs();
141 std::vector<ZoneRecord> records;
143 std::vector<Snap> snaps;
145 std::lock_guard<std::mutex> lock(g_threadsMutex);
146 for (
auto*
tb : threads()) {
148 s.tid =
tb->threadId;
149 s.tname =
tb->threadName;
150 for (
auto&
r :
tb->records)
151 if (
r.endNs == 0)
r.endNs = t0;
152 s.records = std::move(
tb->records);
155 snaps.push_back(std::move(
s));
162 double totalMs = 0.0;
164 int minDepth = 0x7fffffff;
169 std::unordered_map<uint64_t, Agg> aggMap;
171 for (
const auto&
s : snaps) {
172 const size_t n =
s.records.size();
173 std::vector<int64_t> total(
n, 0);
174 std::vector<int64_t> child(
n, 0);
175 for (
size_t i = 0; i <
n; ++i) {
176 int64_t
t =
s.records[i].endNs -
s.records[i].startNs;
180 for (
size_t i = 0; i <
n; ++i) {
181 const int32_t
p =
s.records[i].parent;
182 if (
p >= 0 &&
p <
static_cast<int32_t
>(
n)) child[
static_cast<size_t>(
p)] += total[i];
184 for (
size_t i = 0; i <
n; ++i) {
185 const auto&
r =
s.records[i];
186 const int64_t self = total[i] - child[i];
187 Agg&
a = aggMap[zoneKey(
r.module,
r.name)];
188 a.selfMs +=
static_cast<double>(self) / 1'000'000.0;
189 a.totalMs +=
static_cast<double>(total[i]) / 1'000'000.0;
192 a.name =
r.name ?
r.name :
"";
194 if (
r.depth <
a.minDepth)
a.minDepth =
r.depth;
200 for (
auto& kv : aggMap) {
203 sm.module =
a.module < g_moduleNames.size() ? g_moduleNames[
a.module]
204 : std::string(
"default");
211 f.samples.push_back(std::move(sm));
213 std::sort(
f.samples.begin(),
f.samples.end(),
217 lastFrameStorage() = std::move(
f);
218 hasFrameFlag() =
true;
219 return static_cast<double>(lastFrameStorage().
endNs - lastFrameStorage().
beginNs) / 1'000'000.0;
227 if (!hasFrameFlag())
return "=== Profiler: no frame captured yet ===\n";
228 std::ostringstream os;
230 const double totalMs =
231 static_cast<double>(lastFrameStorage().
endNs - lastFrameStorage().
beginNs) / 1'000'000.0;
232 std::snprintf(buf,
sizeof(buf),
"%.2f", totalMs);
233 os <<
"=== Profiler frame (" << buf <<
" ms) ===\n";
234 os <<
"-- Per-module / per-zone (self time, sorted) --\n";
235 for (
const auto&
s : lastFrameStorage().
samples) {
236 std::snprintf(buf,
sizeof(buf),
"%.3f",
s.selfMs);
237 os <<
" [" <<
s.module <<
"/" <<
s.thread <<
"] " <<
s.name
238 <<
" self=" << buf <<
" ms";
239 std::snprintf(buf,
sizeof(buf),
"%.3f",
s.totalMs);
240 os <<
" total=" << buf <<
" ms";
241 if (
s.count > 1) os <<
" x" <<
s.count;