@lucent-lang/lucent 0.0.4 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +97 -0
  2. package/app.plugin.js +46 -10
  3. package/dist/{bench-CR3E7e2e.js → bench-eEez4jhQ.js} +10 -6
  4. package/dist/{build-CAIrRSYA.js → build-CpUjeHFz.js} +1 -1
  5. package/dist/{build-check-QozuNJm5.js → build-check-BA1ZEp9s.js} +28 -13
  6. package/dist/{check-1SCmYlqA.js → check-BZx3FgI6.js} +1 -1
  7. package/dist/cli.js +62 -13
  8. package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
  9. package/dist/compiler.js +4 -4
  10. package/dist/{dashboard-B-NzjjaN.js → dashboard-CC-HXhcd.js} +2 -2
  11. package/dist/{dev-BYWMP7oC.js → dev-DI5yL8zx.js} +62 -23
  12. package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
  13. package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
  14. package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
  15. package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
  16. package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
  17. package/dist/pipeline-BM_VM2X_.js +700 -0
  18. package/dist/project-BKNoTEEp.js +342 -0
  19. package/dist/sdk-coverage-iiv3JqMp.js +113 -0
  20. package/dist/sdk-diff-ByJ1Gw-F.js +109 -0
  21. package/dist/sdk-lock-BQn7IyRs.js +38 -0
  22. package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-Bv2pNega.js} +2 -2
  23. package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-CPGJm_Ne.js} +2 -2
  24. package/dist/{sdk-show-CkrdlqlP.js → sdk-show-BUkLqRnA.js} +5 -4
  25. package/dist/sdk-usage-0ZFMAYg1.js +95 -0
  26. package/dist/src-ChnmWLwn.js +40851 -0
  27. package/dist/trace-Bi4hkmPx.js +139 -0
  28. package/lib/core.cjs +368 -1
  29. package/lib/sdk/android.d.ts +54 -1
  30. package/lib/sdk/compose.d.ts +94 -0
  31. package/lib/sdk/compose.schemas.json.gz +0 -0
  32. package/lib/sdk/core.d.ts +201 -0
  33. package/lib/sdk/ios.d.ts +75 -2
  34. package/lib/sdk/ui.d.ts +129 -0
  35. package/metro/transformer.cjs +11 -2
  36. package/package.json +4 -3
  37. package/runtime/cpp/lucent/abort.cpp +59 -11
  38. package/runtime/cpp/lucent/abort.h +22 -6
  39. package/runtime/cpp/lucent/array.h +12 -2
  40. package/runtime/cpp/lucent/async.h +101 -21
  41. package/runtime/cpp/lucent/bigint.cpp +898 -0
  42. package/runtime/cpp/lucent/bigint.h +302 -0
  43. package/runtime/cpp/lucent/buffer.cpp +228 -0
  44. package/runtime/cpp/lucent/buffer.h +328 -0
  45. package/runtime/cpp/lucent/builtins.cpp +4 -1
  46. package/runtime/cpp/lucent/bytes.h +21 -2
  47. package/runtime/cpp/lucent/callback.cpp +29 -0
  48. package/runtime/cpp/lucent/callback.h +230 -0
  49. package/runtime/cpp/lucent/compute.cpp +370 -0
  50. package/runtime/cpp/lucent/compute.h +455 -0
  51. package/runtime/cpp/lucent/conversions.h +111 -0
  52. package/runtime/cpp/lucent/core.h +16 -5
  53. package/runtime/cpp/lucent/equality.h +107 -28
  54. package/runtime/cpp/lucent/execution.cpp +416 -0
  55. package/runtime/cpp/lucent/execution.h +270 -0
  56. package/runtime/cpp/lucent/extension.cpp +97 -0
  57. package/runtime/cpp/lucent/extension.h +210 -0
  58. package/runtime/cpp/lucent/helpers.h +12 -5
  59. package/runtime/cpp/lucent/items.h +52 -0
  60. package/runtime/cpp/lucent/jserror.cpp +9 -0
  61. package/runtime/cpp/lucent/jserror.h +11 -0
  62. package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
  63. package/runtime/cpp/lucent/jsi/convert.h +128 -59
  64. package/runtime/cpp/lucent/jsi/host.cpp +213 -35
  65. package/runtime/cpp/lucent/jsi/host.h +116 -16
  66. package/runtime/cpp/lucent/json.h +19 -1
  67. package/runtime/cpp/lucent/lifecycle.cpp +260 -0
  68. package/runtime/cpp/lucent/lifecycle.h +124 -0
  69. package/runtime/cpp/lucent/lucent.h +7 -0
  70. package/runtime/cpp/lucent/map.h +41 -7
  71. package/runtime/cpp/lucent/native.cpp +35 -47
  72. package/runtime/cpp/lucent/native.h +116 -26
  73. package/runtime/cpp/lucent/number.cpp +38 -38
  74. package/runtime/cpp/lucent/number.h +33 -0
  75. package/runtime/cpp/lucent/operation.h +65 -0
  76. package/runtime/cpp/lucent/ops.h +14 -6
  77. package/runtime/cpp/lucent/platform/android.cpp +223 -10
  78. package/runtime/cpp/lucent/platform/android.h +227 -4
  79. package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
  80. package/runtime/cpp/lucent/platform/android_requests.h +140 -0
  81. package/runtime/cpp/lucent/platform/compose.h +256 -0
  82. package/runtime/cpp/lucent/platform/ios.h +325 -25
  83. package/runtime/cpp/lucent/platform/ios.mm +1 -14
  84. package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
  85. package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
  86. package/runtime/cpp/lucent/platform/swiftui.h +190 -0
  87. package/runtime/cpp/lucent/presentation.cpp +11 -0
  88. package/runtime/cpp/lucent/presentation.h +109 -0
  89. package/runtime/cpp/lucent/reactive.cpp +406 -0
  90. package/runtime/cpp/lucent/reactive.h +566 -0
  91. package/runtime/cpp/lucent/report.cpp +40 -0
  92. package/runtime/cpp/lucent/report.h +16 -0
  93. package/runtime/cpp/lucent/resource.cpp +83 -0
  94. package/runtime/cpp/lucent/resource.h +75 -0
  95. package/runtime/cpp/lucent/scheduler.cpp +38 -124
  96. package/runtime/cpp/lucent/scheduler.h +102 -83
  97. package/runtime/cpp/lucent/scope.cpp +308 -0
  98. package/runtime/cpp/lucent/scope.h +452 -0
  99. package/runtime/cpp/lucent/sizing.h +263 -0
  100. package/runtime/cpp/lucent/slots.h +231 -0
  101. package/runtime/cpp/lucent/trace.cpp +539 -0
  102. package/runtime/cpp/lucent/trace.h +251 -0
  103. package/runtime/cpp/lucent/transport.cpp +9 -0
  104. package/runtime/cpp/lucent/transport.h +341 -0
  105. package/runtime/cpp/lucent/view.cpp +123 -0
  106. package/runtime/cpp/lucent/view.h +157 -0
  107. package/runtime/cpp/rn/LucentComponentView.h +121 -0
  108. package/runtime/cpp/rn/LucentComponentView.mm +479 -0
  109. package/runtime/cpp/rn/LucentModule.cpp +37 -7
  110. package/runtime/cpp/rn/LucentModule.h +3 -0
  111. package/runtime/cpp/rn/LucentViewChildren.h +80 -0
  112. package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
  113. package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
  114. package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
  115. package/runtime/cpp/rn/LucentViewRequests.h +104 -0
  116. package/runtime/cpp/rn/LucentViewSizing.h +345 -0
  117. package/runtime/cpp/rn/LucentViewSlots.h +242 -0
  118. package/runtime/cpp/rn/LucentViewValues.h +125 -0
  119. package/runtime/cpp/rn/LucentViews.h +440 -0
  120. package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
  121. package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
  122. package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
  123. package/runtime/cpp/third_party/dragonbox/README.md +16 -0
  124. package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
  125. package/runtime/js/index.d.ts +11 -1
  126. package/runtime/js/index.js +84 -8
  127. package/runtime/js/views.d.ts +38 -0
  128. package/runtime/js/views.js +226 -0
  129. package/runtime/native/android/CMakeLists.txt +4 -1
  130. package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
  131. package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
  132. package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
  133. package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
  134. package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
  135. package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
  136. package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
  137. package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
  138. package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
  139. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
  140. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
  141. package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
  142. package/runtime/test/jsi/harness.cpp +9 -0
  143. package/schemas/build.schema.json +96 -1
  144. package/schemas/check.schema.json +56 -1
  145. package/schemas/lucent.schema.json +495 -9
  146. package/schemas/sdk-diff.schema.json +70 -0
  147. package/schemas/sdk-lock.schema.json +67 -0
  148. package/ts-plugin/index.js +85 -4
  149. package/dist/pipeline-CH7-2cxu.js +0 -232
  150. package/dist/project-D8tEet0k.js +0 -150
  151. package/dist/sdk-coverage-BoW_3Efd.js +0 -64
  152. package/dist/src-BD7f1_aZ.js +0 -11493
@@ -0,0 +1,539 @@
1
+ #include "trace.h"
2
+
3
+ #include <algorithm>
4
+ #include <cstdio>
5
+ #include <cstdlib>
6
+ #include <fstream>
7
+ #include <map>
8
+ #include <mutex>
9
+ #include <string>
10
+
11
+ #include "execution.h"
12
+
13
+ #if defined(__APPLE__)
14
+ #include <os/log.h>
15
+ #include <os/signpost.h>
16
+ #elif defined(__ANDROID__)
17
+ #include <android/trace.h>
18
+ #include <dlfcn.h>
19
+ #include <sys/system_properties.h>
20
+ #endif
21
+
22
+ namespace lucent::trace {
23
+
24
+ namespace {
25
+
26
+ // --- the buffer -------------------------------------------------------------------
27
+
28
+ /// The latest `capacity` events, as a ring.
29
+ struct Buffer {
30
+ std::mutex m;
31
+ std::vector<Event> ring;
32
+ size_t head = 0;
33
+ size_t size = 0;
34
+ uint64_t dropped = 0;
35
+
36
+ void push(const Event& e) {
37
+ std::lock_guard<std::mutex> g(m);
38
+
39
+ if (ring.empty()) return;
40
+
41
+ if (size < ring.size()) {
42
+ ring[(head + size) % ring.size()] = e;
43
+ size++;
44
+ return;
45
+ }
46
+
47
+ ring[head] = e;
48
+ head = (head + 1) % ring.size();
49
+ dropped++;
50
+ }
51
+ };
52
+
53
+ Buffer& buffer() {
54
+ // Leaked: threads may still record during static destruction.
55
+ static auto* b = new Buffer();
56
+ return *b;
57
+ }
58
+
59
+ std::atomic<bool> platformOn{false};
60
+ std::atomic<uint64_t> nextId{1};
61
+ std::atomic<uint32_t> nextThread{1};
62
+
63
+ thread_local uint32_t threadNumber = 0;
64
+ thread_local uint64_t runningId = 0;
65
+ thread_local uint64_t pendingCorrelation = 0;
66
+
67
+ uint32_t thisThread() {
68
+ if (!threadNumber) threadNumber = nextThread.fetch_add(1, std::memory_order_relaxed);
69
+ return threadNumber;
70
+ }
71
+
72
+ uint32_t thisContext() {
73
+ ExecutionContext* context = ExecutionContext::current();
74
+ return context ? context->id() : 0;
75
+ }
76
+
77
+ void record(Event e) {
78
+ e.thread = thisThread();
79
+ e.context = thisContext();
80
+ buffer().push(e);
81
+ }
82
+
83
+ // --- the platform's tooling ---------------------------------------------------------
84
+
85
+ #if defined(__APPLE__)
86
+
87
+ os_log_t signposts() {
88
+ static os_log_t log = os_log_create("dev.lucent", "runtime");
89
+ return log;
90
+ }
91
+
92
+ // os_signpost names must be literals: one per category.
93
+ #define LUCENT_SIGNPOST(kind, id, category, name) \
94
+ switch (category) { \
95
+ case Category::Entry: os_signpost_interval_##kind(signposts(), id, "entry", "%{public}s", name); break; \
96
+ case Category::Lock: os_signpost_interval_##kind(signposts(), id, "lock", "%{public}s", name); break; \
97
+ case Category::Queue: os_signpost_interval_##kind(signposts(), id, "queue", "%{public}s", name); break; \
98
+ case Category::Run: os_signpost_interval_##kind(signposts(), id, "run", "%{public}s", name); break; \
99
+ case Category::Native: os_signpost_interval_##kind(signposts(), id, "native", "%{public}s", name); break; \
100
+ case Category::Completion: os_signpost_interval_##kind(signposts(), id, "completion", "%{public}s", name); break; \
101
+ case Category::Compute: os_signpost_interval_##kind(signposts(), id, "compute", "%{public}s", name); break; \
102
+ case Category::Copy: os_signpost_interval_##kind(signposts(), id, "copy", "%{public}s", name); break; \
103
+ case Category::Alloc: os_signpost_interval_##kind(signposts(), id, "alloc", "%{public}s", name); break; \
104
+ case Category::Build: os_signpost_interval_##kind(signposts(), id, "build", "%{public}s", name); break; \
105
+ }
106
+
107
+ void platformBegin(uint64_t cookie, Category category, const char* name) {
108
+ os_signpost_id_t id = static_cast<os_signpost_id_t>(cookie);
109
+ LUCENT_SIGNPOST(begin, id, category, name)
110
+ }
111
+
112
+ void platformEnd(uint64_t cookie, Category category, const char* name) {
113
+ os_signpost_id_t id = static_cast<os_signpost_id_t>(cookie);
114
+ LUCENT_SIGNPOST(end, id, category, name)
115
+ }
116
+
117
+ void platformInstant(Category category, const char* name, int64_t value) {
118
+ os_signpost_event_emit(signposts(), OS_SIGNPOST_ID_EXCLUSIVE, "event", "%{public}s %{public}s %lld", categoryName(category), name,
119
+ static_cast<long long>(value));
120
+ }
121
+
122
+ void platformCounter(const char* name, int64_t value) {
123
+ os_signpost_event_emit(signposts(), OS_SIGNPOST_ID_EXCLUSIVE, "counter", "%{public}s %lld", name, static_cast<long long>(value));
124
+ }
125
+
126
+ #undef LUCENT_SIGNPOST
127
+
128
+ #elif defined(__ANDROID__)
129
+
130
+ // Async sections and counters came with API 29; looked up, so older
131
+ // devices still load the library (they get synchronous sections only).
132
+ using AsyncSection = void (*)(const char*, int32_t);
133
+ using Counter = void (*)(const char*, int64_t);
134
+
135
+ struct Atrace {
136
+ AsyncSection beginAsync = nullptr;
137
+ AsyncSection endAsync = nullptr;
138
+ Counter setCounter = nullptr;
139
+
140
+ Atrace() {
141
+ void* android = dlopen("libandroid.so", RTLD_NOW | RTLD_NOLOAD);
142
+ if (!android) return;
143
+
144
+ beginAsync = reinterpret_cast<AsyncSection>(dlsym(android, "ATrace_beginAsyncSection"));
145
+ endAsync = reinterpret_cast<AsyncSection>(dlsym(android, "ATrace_endAsyncSection"));
146
+ setCounter = reinterpret_cast<Counter>(dlsym(android, "ATrace_setCounter"));
147
+ }
148
+ };
149
+
150
+ const Atrace& atrace() {
151
+ static Atrace a;
152
+ return a;
153
+ }
154
+
155
+ std::string sectionName(Category category, const char* name) { return std::string(categoryName(category)) + ":" + name; }
156
+
157
+ void platformBegin(uint64_t cookie, Category category, const char* name) {
158
+ if (!ATrace_isEnabled() || !atrace().beginAsync) return;
159
+
160
+ atrace().beginAsync(sectionName(category, name).c_str(), static_cast<int32_t>(cookie & 0x7fffffff));
161
+ }
162
+
163
+ void platformEnd(uint64_t cookie, Category category, const char* name) {
164
+ if (!ATrace_isEnabled() || !atrace().endAsync) return;
165
+
166
+ atrace().endAsync(sectionName(category, name).c_str(), static_cast<int32_t>(cookie & 0x7fffffff));
167
+ }
168
+
169
+ void platformInstant(Category category, const char* name, int64_t) {
170
+ if (!ATrace_isEnabled()) return;
171
+
172
+ ATrace_beginSection(sectionName(category, name).c_str());
173
+ ATrace_endSection();
174
+ }
175
+
176
+ void platformCounter(const char* name, int64_t value) {
177
+ if (!ATrace_isEnabled() || !atrace().setCounter) return;
178
+
179
+ atrace().setCounter(name, value);
180
+ }
181
+
182
+ #else
183
+
184
+ void platformBegin(uint64_t, Category, const char*) {}
185
+ void platformEnd(uint64_t, Category, const char*) {}
186
+ void platformInstant(Category, const char*, int64_t) {}
187
+ void platformCounter(const char*, int64_t) {}
188
+
189
+ #endif
190
+
191
+ // --- JSON -----------------------------------------------------------------------------
192
+
193
+ void appendString(std::string& out, const char* s) {
194
+ out.push_back('"');
195
+
196
+ for (const char* p = s ? s : ""; *p; p++) {
197
+ unsigned char c = static_cast<unsigned char>(*p);
198
+
199
+ if (c == '"' || c == '\\') {
200
+ out.push_back('\\');
201
+ out.push_back(static_cast<char>(c));
202
+ } else if (c < 0x20) {
203
+ char escaped[8];
204
+ std::snprintf(escaped, sizeof escaped, "\\u%04x", c);
205
+ out += escaped;
206
+ } else {
207
+ out.push_back(static_cast<char>(c));
208
+ }
209
+ }
210
+
211
+ out.push_back('"');
212
+ }
213
+
214
+ void appendMicros(std::string& out, uint64_t ns) {
215
+ char text[32];
216
+ std::snprintf(text, sizeof text, "%llu.%03llu", static_cast<unsigned long long>(ns / 1000), static_cast<unsigned long long>(ns % 1000));
217
+ out += text;
218
+ }
219
+
220
+ void appendNumber(std::string& out, long long v) { out += std::to_string(v); }
221
+
222
+ // --- the environment -------------------------------------------------------------------
223
+
224
+ std::string environmentSetting() {
225
+ #if defined(__ANDROID__)
226
+ char value[PROP_VALUE_MAX] = {};
227
+ if (__system_property_get("debug.lucent.trace", value) > 0) return value;
228
+ return "";
229
+ #else
230
+ const char* value = std::getenv("LUCENT_TRACE");
231
+ return value ? value : "";
232
+ #endif
233
+ }
234
+
235
+ std::string& exitPath() {
236
+ static auto* path = new std::string();
237
+ return *path;
238
+ }
239
+
240
+ } // namespace
241
+
242
+ const char* categoryName(Category category) {
243
+ switch (category) {
244
+ case Category::Entry: return "entry";
245
+ case Category::Lock: return "lock";
246
+ case Category::Queue: return "queue";
247
+ case Category::Run: return "run";
248
+ case Category::Native: return "native";
249
+ case Category::Completion: return "completion";
250
+ case Category::Compute: return "compute";
251
+ case Category::Copy: return "copy";
252
+ case Category::Alloc: return "alloc";
253
+ case Category::Build: return "build";
254
+ }
255
+
256
+ return "native";
257
+ }
258
+
259
+ void start(Options options) {
260
+ Buffer& b = buffer();
261
+
262
+ {
263
+ std::lock_guard<std::mutex> g(b.m);
264
+
265
+ b.ring.assign(std::max<size_t>(options.capacity, 1), Event{});
266
+ b.head = 0;
267
+ b.size = 0;
268
+ b.dropped = 0;
269
+ }
270
+
271
+ platformOn.store(options.platform, std::memory_order_relaxed);
272
+ detail::active.store(true, std::memory_order_release);
273
+ }
274
+
275
+ void stop() { detail::active.store(false, std::memory_order_release); }
276
+
277
+ void startFromEnvironment() {
278
+ static std::once_flag once;
279
+
280
+ std::call_once(once, [] {
281
+ std::string setting = environmentSetting();
282
+ if (setting.empty() || setting == "0") return;
283
+
284
+ #if defined(__ANDROID__)
285
+ // ATrace costs a check while nothing records: always mirrored.
286
+ bool platform = true;
287
+ #else
288
+ bool platform = setting == "platform";
289
+ #endif
290
+
291
+ start({.platform = platform});
292
+
293
+ // A file to write the trace to at exit.
294
+ if (setting.size() > 5 && setting.compare(setting.size() - 5, 5, ".json") == 0) {
295
+ exitPath() = setting;
296
+ std::atexit([] { writeChromeJson(exitPath()); });
297
+ }
298
+ });
299
+ }
300
+
301
+ std::vector<Event> events() {
302
+ Buffer& b = buffer();
303
+ std::lock_guard<std::mutex> g(b.m);
304
+
305
+ std::vector<Event> out;
306
+ out.reserve(b.size);
307
+
308
+ for (size_t i = 0; i < b.size; i++) out.push_back(b.ring[(b.head + i) % b.ring.size()]);
309
+
310
+ return out;
311
+ }
312
+
313
+ uint64_t dropped() {
314
+ Buffer& b = buffer();
315
+ std::lock_guard<std::mutex> g(b.m);
316
+ return b.dropped;
317
+ }
318
+
319
+ uint64_t toNs(std::chrono::steady_clock::time_point at) noexcept {
320
+ // Never 0: a Mark's start of 0 means "not traced".
321
+ auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(at.time_since_epoch()).count();
322
+ return static_cast<uint64_t>(ns) | 1;
323
+ }
324
+
325
+ uint64_t now() noexcept { return toNs(std::chrono::steady_clock::now()); }
326
+
327
+ uint64_t newId() noexcept { return nextId.fetch_add(1, std::memory_order_relaxed); }
328
+
329
+ Mark begin(Category category, const char* name) noexcept {
330
+ if (!enabled()) return {};
331
+
332
+ Mark mark{now(), 0};
333
+
334
+ if (platformOn.load(std::memory_order_relaxed)) {
335
+ mark.cookie = newId();
336
+ platformBegin(mark.cookie, category, name);
337
+ }
338
+
339
+ return mark;
340
+ }
341
+
342
+ void end(const Mark& mark, Category category, const char* name, const Detail& detail) noexcept {
343
+ if (!mark) return;
344
+
345
+ if (mark.cookie) platformEnd(mark.cookie, category, name);
346
+
347
+ if (!enabled()) return;
348
+
349
+ span(category, name, mark.startNs, now(), detail);
350
+ }
351
+
352
+ void span(Category category, const char* name, uint64_t startNs, uint64_t endNs, const Detail& detail) noexcept {
353
+ if (!enabled()) return;
354
+
355
+ Event e;
356
+ e.phase = Event::Phase::Span;
357
+ e.category = category;
358
+ e.name = name;
359
+ e.detail = detail.detail;
360
+ e.startNs = startNs;
361
+ e.durationNs = endNs > startNs ? endNs - startNs : 0;
362
+ e.id = detail.id;
363
+ e.parent = detail.parent;
364
+ e.value = detail.value;
365
+ e.count = detail.count;
366
+ e.site = detail.site;
367
+ record(e);
368
+ }
369
+
370
+ void instant(Category category, const char* name, uint64_t id, int64_t value) noexcept {
371
+ instant(category, name, {.id = id, .value = value});
372
+ }
373
+
374
+ void instant(Category category, const char* name, const Detail& detail) noexcept {
375
+ if (!enabled()) return;
376
+
377
+ Event e;
378
+ e.phase = Event::Phase::Instant;
379
+ e.category = category;
380
+ e.name = name;
381
+ e.detail = detail.detail;
382
+ e.startNs = now();
383
+ e.id = detail.id;
384
+ e.parent = detail.parent;
385
+ e.value = detail.value;
386
+ e.count = detail.count;
387
+ e.site = detail.site;
388
+ record(e);
389
+
390
+ if (platformOn.load(std::memory_order_relaxed)) platformInstant(category, name, detail.value);
391
+ }
392
+
393
+ void counter(const char* name, int64_t value) noexcept {
394
+ if (!enabled()) return;
395
+
396
+ Event e;
397
+ e.phase = Event::Phase::Counter;
398
+ e.name = name;
399
+ e.startNs = now();
400
+ e.value = value;
401
+ record(e);
402
+
403
+ if (platformOn.load(std::memory_order_relaxed)) platformCounter(name, value);
404
+ }
405
+
406
+ uint64_t currentId() noexcept { return runningId; }
407
+
408
+ Current::Current(uint64_t id) noexcept : previous_(runningId) { runningId = id; }
409
+
410
+ Current::~Current() { runningId = previous_; }
411
+
412
+ Correlate::Correlate(uint64_t id) noexcept : previous_(pendingCorrelation) { pendingCorrelation = id; }
413
+
414
+ Correlate::~Correlate() { pendingCorrelation = previous_; }
415
+
416
+ uint64_t takeCorrelation() noexcept { return std::exchange(pendingCorrelation, 0); }
417
+
418
+ // --- export ---------------------------------------------------------------------------
419
+
420
+ std::string chromeJson(const std::vector<Event>& events) {
421
+ std::string out = "{\"traceEvents\":[";
422
+ bool first = true;
423
+
424
+ auto open = [&] {
425
+ if (!first) out += ",\n";
426
+ first = false;
427
+ };
428
+
429
+ // Every event, with what it says in args.
430
+ for (const Event& e : events) {
431
+ open();
432
+
433
+ out += "{\"name\":";
434
+ appendString(out, e.name);
435
+ out += ",\"cat\":";
436
+ appendString(out, e.phase == Event::Phase::Counter ? "counter" : categoryName(e.category));
437
+ out += ",\"ph\":\"";
438
+ out.push_back(static_cast<char>(e.phase));
439
+ out += "\",\"ts\":";
440
+ appendMicros(out, e.startNs);
441
+
442
+ if (e.phase == Event::Phase::Span) {
443
+ out += ",\"dur\":";
444
+ appendMicros(out, e.durationNs);
445
+ }
446
+
447
+ if (e.phase == Event::Phase::Instant) out += ",\"s\":\"t\"";
448
+
449
+ out += ",\"pid\":1,\"tid\":";
450
+ appendNumber(out, e.thread);
451
+ out += ",\"args\":{";
452
+
453
+ if (e.phase == Event::Phase::Counter) {
454
+ appendString(out, e.name);
455
+ out += ":";
456
+ appendNumber(out, e.value);
457
+ } else {
458
+ out += "\"context\":";
459
+ appendNumber(out, e.context);
460
+
461
+ if (e.id) {
462
+ out += ",\"id\":";
463
+ appendNumber(out, static_cast<long long>(e.id));
464
+ }
465
+
466
+ if (e.parent) {
467
+ out += ",\"parent\":";
468
+ appendNumber(out, static_cast<long long>(e.parent));
469
+ }
470
+
471
+ if (e.value) {
472
+ out += ",\"value\":";
473
+ appendNumber(out, e.value);
474
+ }
475
+
476
+ if (e.count) {
477
+ out += ",\"count\":";
478
+ appendNumber(out, e.count);
479
+ }
480
+
481
+ if (e.detail) {
482
+ out += ",\"detail\":";
483
+ appendString(out, e.detail);
484
+ }
485
+
486
+ if (e.site) {
487
+ std::string where = std::string(e.site->file ? e.site->file : "") + ":" + std::to_string(e.site->line);
488
+ out += ",\"site\":";
489
+ appendString(out, where.c_str());
490
+ }
491
+ }
492
+
493
+ out += "}}";
494
+ }
495
+
496
+ // Arrows between events that share an id, in time order: a JS call to
497
+ // its job's wait, run and completion.
498
+ std::map<uint64_t, std::vector<const Event*>> flows;
499
+ for (const Event& e : events) {
500
+ if (e.id && e.phase == Event::Phase::Span) flows[e.id].push_back(&e);
501
+ }
502
+
503
+ for (auto& [id, chain] : flows) {
504
+ if (chain.size() < 2) continue;
505
+
506
+ std::stable_sort(chain.begin(), chain.end(), [](const Event* a, const Event* b) { return a->startNs < b->startNs; });
507
+
508
+ for (size_t i = 0; i + 1 < chain.size(); i++) {
509
+ for (int side = 0; side < 2; side++) {
510
+ const Event* e = chain[i + side];
511
+
512
+ open();
513
+ out += side == 0 ? "{\"ph\":\"s\"" : "{\"ph\":\"f\",\"bp\":\"e\"";
514
+ out += ",\"name\":\"correlation\",\"cat\":\"flow\",\"id\":";
515
+ appendNumber(out, static_cast<long long>(id));
516
+ out += ",\"ts\":";
517
+ appendMicros(out, e->startNs);
518
+ out += ",\"pid\":1,\"tid\":";
519
+ appendNumber(out, e->thread);
520
+ out += "}";
521
+ }
522
+ }
523
+ }
524
+
525
+ out += "],\"displayTimeUnit\":\"ms\",\"otherData\":{\"dropped\":";
526
+ appendNumber(out, static_cast<long long>(dropped()));
527
+ out += "}}\n";
528
+ return out;
529
+ }
530
+
531
+ bool writeChromeJson(const std::string& path) {
532
+ std::ofstream file(path);
533
+ if (!file) return false;
534
+
535
+ file << chromeJson(events());
536
+ return static_cast<bool>(file);
537
+ }
538
+
539
+ } // namespace lucent::trace