viztracer 1.1.0__cp314-cp314-win_amd64.whl

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.

Potentially problematic release.


This version of viztracer might be problematic. Click here for more details.

Files changed (109) hide show
  1. viztracer/__init__.py +19 -0
  2. viztracer/__main__.py +8 -0
  3. viztracer/attach.py +67 -0
  4. viztracer/attach_process/LICENSE +203 -0
  5. viztracer/attach_process/__init__.py +0 -0
  6. viztracer/attach_process/add_code_to_python_process.py +582 -0
  7. viztracer/attach_process/attach_x86.dll +0 -0
  8. viztracer/attach_process/inject_dll_amd64.exe +0 -0
  9. viztracer/attach_process/linux_and_mac/lldb_prepare.py +54 -0
  10. viztracer/attach_process/run_code_on_dllmain_amd64.dll +0 -0
  11. viztracer/attach_process/run_code_on_dllmain_x86.dll +0 -0
  12. viztracer/cellmagic.py +70 -0
  13. viztracer/code_monkey.py +353 -0
  14. viztracer/decorator.py +164 -0
  15. viztracer/event_base.py +81 -0
  16. viztracer/functree.py +135 -0
  17. viztracer/html/flamegraph.html +34 -0
  18. viztracer/html/trace_viewer_embedder.html +203 -0
  19. viztracer/html/trace_viewer_full.html +10207 -0
  20. viztracer/main.py +699 -0
  21. viztracer/modules/eventnode.c +172 -0
  22. viztracer/modules/eventnode.h +73 -0
  23. viztracer/modules/pythoncapi_compat.h +1726 -0
  24. viztracer/modules/quicktime.c +177 -0
  25. viztracer/modules/quicktime.h +104 -0
  26. viztracer/modules/snaptrace.c +2205 -0
  27. viztracer/modules/snaptrace.h +134 -0
  28. viztracer/modules/snaptrace_member.c +483 -0
  29. viztracer/modules/util.c +45 -0
  30. viztracer/modules/util.h +22 -0
  31. viztracer/modules/vcompressor/vc_dump.c +1131 -0
  32. viztracer/modules/vcompressor/vc_dump.h +49 -0
  33. viztracer/modules/vcompressor/vcompressor.c +396 -0
  34. viztracer/modules/vcompressor/vcompressor.h +15 -0
  35. viztracer/patch.py +307 -0
  36. viztracer/report_builder.py +311 -0
  37. viztracer/snaptrace.cp314-win_amd64.pyd +0 -0
  38. viztracer/snaptrace.pyi +77 -0
  39. viztracer/util.py +196 -0
  40. viztracer/vcompressor.cp314-win_amd64.pyd +0 -0
  41. viztracer/vcompressor.pyi +10 -0
  42. viztracer/viewer.py +528 -0
  43. viztracer/vizcounter.py +20 -0
  44. viztracer/vizevent.py +31 -0
  45. viztracer/vizlogging.py +20 -0
  46. viztracer/vizobject.py +28 -0
  47. viztracer/vizplugin.py +143 -0
  48. viztracer/viztracer.py +472 -0
  49. viztracer/web_dist/LICENSE +189 -0
  50. viztracer/web_dist/index.html +127 -0
  51. viztracer/web_dist/service_worker.js +279 -0
  52. viztracer/web_dist/trace_processor +300 -0
  53. viztracer/web_dist/v52.0-6b9586def/assets/MaterialSymbolsOutlined.woff2 +0 -0
  54. viztracer/web_dist/v52.0-6b9586def/assets/Roboto-100.woff2 +0 -0
  55. viztracer/web_dist/v52.0-6b9586def/assets/Roboto-300.woff2 +0 -0
  56. viztracer/web_dist/v52.0-6b9586def/assets/Roboto-400.woff2 +0 -0
  57. viztracer/web_dist/v52.0-6b9586def/assets/Roboto-500.woff2 +0 -0
  58. viztracer/web_dist/v52.0-6b9586def/assets/RobotoCondensed-Light.woff2 +0 -0
  59. viztracer/web_dist/v52.0-6b9586def/assets/RobotoCondensed-Regular.woff2 +0 -0
  60. viztracer/web_dist/v52.0-6b9586def/assets/RobotoMono-Regular.woff2 +0 -0
  61. viztracer/web_dist/v52.0-6b9586def/assets/brand.png +0 -0
  62. viztracer/web_dist/v52.0-6b9586def/assets/catapult_trace_viewer.html +3946 -0
  63. viztracer/web_dist/v52.0-6b9586def/assets/catapult_trace_viewer.js +7539 -0
  64. viztracer/web_dist/v52.0-6b9586def/assets/favicon.png +0 -0
  65. viztracer/web_dist/v52.0-6b9586def/assets/logo-128.png +0 -0
  66. viztracer/web_dist/v52.0-6b9586def/assets/logo-3d.png +0 -0
  67. viztracer/web_dist/v52.0-6b9586def/assets/rec_atrace.png +0 -0
  68. viztracer/web_dist/v52.0-6b9586def/assets/rec_battery_counters.png +0 -0
  69. viztracer/web_dist/v52.0-6b9586def/assets/rec_board_voltage.png +0 -0
  70. viztracer/web_dist/v52.0-6b9586def/assets/rec_cpu_coarse.png +0 -0
  71. viztracer/web_dist/v52.0-6b9586def/assets/rec_cpu_fine.png +0 -0
  72. viztracer/web_dist/v52.0-6b9586def/assets/rec_cpu_freq.png +0 -0
  73. viztracer/web_dist/v52.0-6b9586def/assets/rec_cpu_voltage.png +0 -0
  74. viztracer/web_dist/v52.0-6b9586def/assets/rec_frame_timeline.png +0 -0
  75. viztracer/web_dist/v52.0-6b9586def/assets/rec_ftrace.png +0 -0
  76. viztracer/web_dist/v52.0-6b9586def/assets/rec_gpu_mem_total.png +0 -0
  77. viztracer/web_dist/v52.0-6b9586def/assets/rec_java_heap_dump.png +0 -0
  78. viztracer/web_dist/v52.0-6b9586def/assets/rec_lmk.png +0 -0
  79. viztracer/web_dist/v52.0-6b9586def/assets/rec_logcat.png +0 -0
  80. viztracer/web_dist/v52.0-6b9586def/assets/rec_long_trace.png +0 -0
  81. viztracer/web_dist/v52.0-6b9586def/assets/rec_mem_hifreq.png +0 -0
  82. viztracer/web_dist/v52.0-6b9586def/assets/rec_meminfo.png +0 -0
  83. viztracer/web_dist/v52.0-6b9586def/assets/rec_native_heap_profiler.png +0 -0
  84. viztracer/web_dist/v52.0-6b9586def/assets/rec_one_shot.png +0 -0
  85. viztracer/web_dist/v52.0-6b9586def/assets/rec_profiling.png +0 -0
  86. viztracer/web_dist/v52.0-6b9586def/assets/rec_ps_stats.png +0 -0
  87. viztracer/web_dist/v52.0-6b9586def/assets/rec_ring_buf.png +0 -0
  88. viztracer/web_dist/v52.0-6b9586def/assets/rec_syscalls.png +0 -0
  89. viztracer/web_dist/v52.0-6b9586def/assets/rec_vmstat.png +0 -0
  90. viztracer/web_dist/v52.0-6b9586def/assets/scheduling_latency.png +0 -0
  91. viztracer/web_dist/v52.0-6b9586def/assets/vscode-icon.png +0 -0
  92. viztracer/web_dist/v52.0-6b9586def/engine_bundle.js +3 -0
  93. viztracer/web_dist/v52.0-6b9586def/frontend_bundle.js +5495 -0
  94. viztracer/web_dist/v52.0-6b9586def/index.html +127 -0
  95. viztracer/web_dist/v52.0-6b9586def/manifest.json +52 -0
  96. viztracer/web_dist/v52.0-6b9586def/perfetto.css +5737 -0
  97. viztracer/web_dist/v52.0-6b9586def/stdlib_docs.json +1 -0
  98. viztracer/web_dist/v52.0-6b9586def/trace_config_utils.wasm +0 -0
  99. viztracer/web_dist/v52.0-6b9586def/trace_processor.wasm +0 -0
  100. viztracer/web_dist/v52.0-6b9586def/trace_processor_memory64.wasm +0 -0
  101. viztracer/web_dist/v52.0-6b9586def/traceconv.wasm +0 -0
  102. viztracer/web_dist/v52.0-6b9586def/traceconv_bundle.js +2 -0
  103. viztracer-1.1.0.dist-info/METADATA +316 -0
  104. viztracer-1.1.0.dist-info/RECORD +109 -0
  105. viztracer-1.1.0.dist-info/WHEEL +5 -0
  106. viztracer-1.1.0.dist-info/entry_points.txt +3 -0
  107. viztracer-1.1.0.dist-info/licenses/LICENSE +222 -0
  108. viztracer-1.1.0.dist-info/licenses/NOTICE.txt +27 -0
  109. viztracer-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2205 @@
1
+ // Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2
+ // For details: https://github.com/gaogaotiantian/viztracer/blob/master/NOTICE.txt
3
+
4
+ #define PY_SSIZE_T_CLEAN
5
+ #include <Python.h>
6
+ #include <stdlib.h>
7
+ #include <frameobject.h>
8
+ #if _WIN32
9
+ #include <windows.h>
10
+ #elif defined(__APPLE__)
11
+ #include <pthread.h>
12
+ #elif defined(__FreeBSD__)
13
+ #include <pthread_np.h>
14
+ #else
15
+ #include <pthread.h>
16
+ #include <sys/syscall.h>
17
+ #endif
18
+
19
+ #include "pythoncapi_compat.h"
20
+ #include "snaptrace.h"
21
+ #include "quicktime.h"
22
+ #include "util.h"
23
+ #include "eventnode.h"
24
+
25
+
26
+ TracerObject* curr_tracer = NULL;
27
+ PyObject* threading_module = NULL;
28
+ PyObject* multiprocessing_module = NULL;
29
+ PyObject* json_module = NULL;
30
+ PyObject* asyncio_module = NULL;
31
+ PyObject* asyncio_tasks_module = NULL;
32
+ PyObject* trio_module = NULL;
33
+ PyObject* trio_lowlevel_module = NULL;
34
+ PyObject* sys_module = NULL;
35
+ PyObject* sys_monitoring_missing = NULL;
36
+
37
+ PyObject* curr_task_getters[2] = {0};
38
+
39
+ // =============================================================================
40
+ // Utility function
41
+ // =============================================================================
42
+
43
+ int64_t prev_ts = 0;
44
+
45
+ static inline int64_t
46
+ get_ts()
47
+ {
48
+ #if defined(QUICKTIME_RDTSC)
49
+ return get_system_ts();
50
+ #else
51
+ int64_t curr_ts = get_system_ts();
52
+ if (curr_ts <= prev_ts) {
53
+ // We use artificial timestamp to avoid timestamp conflict.
54
+ // 20 ns should be a safe granularity because that's normally
55
+ // how long clock_gettime() takes.
56
+ // It's possible to have three same timestamp in a row so we
57
+ // need to check if curr_ts <= prev_ts instead of ==
58
+ #if _WIN32 || defined(__APPLE__)
59
+ curr_ts = prev_ts + 1;
60
+ #else
61
+ curr_ts = prev_ts + 20;
62
+ #endif
63
+ }
64
+ prev_ts = curr_ts;
65
+ return curr_ts;
66
+ #endif
67
+ }
68
+
69
+ static inline struct EventNode*
70
+ get_next_node(TracerObject* self)
71
+ {
72
+ struct EventNode* node = NULL;
73
+
74
+ SNAPTRACE_THREAD_PROTECT_START(self);
75
+ node = self->buffer + self->buffer_tail_idx;
76
+ // This is actually faster than modulo
77
+ self->buffer_tail_idx = self->buffer_tail_idx + 1;
78
+ if (self->buffer_tail_idx >= self->buffer_size) {
79
+ self->buffer_tail_idx = 0;
80
+ }
81
+ if (self->buffer_tail_idx == self->buffer_head_idx) {
82
+ self->buffer_head_idx = self->buffer_head_idx + 1;
83
+ if (self->buffer_head_idx >= self->buffer_size) {
84
+ self->buffer_head_idx = 0;
85
+ }
86
+ clear_node(self->buffer + self->buffer_tail_idx);
87
+ } else {
88
+ self->total_entries += 1;
89
+ }
90
+ SNAPTRACE_THREAD_PROTECT_END(self);
91
+
92
+ return node;
93
+ }
94
+
95
+ static void
96
+ log_func_args(struct FunctionNode* node, PyFrameObject* frame, PyObject* log_func_repr)
97
+ {
98
+ PyObject* func_arg_dict = PyDict_New();
99
+ PyCodeObject* code = PyFrame_GetCode(frame);
100
+ PyObject* names = PyCode_GetVarnames(code);
101
+
102
+ #if PY_VERSION_HEX >= 0x030D0000
103
+ PyObject* locals = PyEval_GetFrameLocals();
104
+ #else
105
+ PyObject* locals = PyEval_GetLocals();
106
+ #endif
107
+
108
+ int idx = 0;
109
+ if (!node->args) {
110
+ node->args = PyDict_New();
111
+ }
112
+
113
+ int name_length = code->co_argcount + code->co_kwonlyargcount;
114
+ if (code->co_flags & CO_VARARGS) {
115
+ name_length ++;
116
+ }
117
+
118
+ if (code->co_flags & CO_VARKEYWORDS) {
119
+ name_length ++;
120
+ }
121
+
122
+ while (idx < name_length) {
123
+ // Borrowed
124
+ PyObject* name = PyTuple_GET_ITEM(names, idx);
125
+ PyObject* repr = NULL;
126
+ // New
127
+ if (log_func_repr) {
128
+ repr = PyObject_CallOneArg(log_func_repr, PyDict_GetItem(locals, name));
129
+ } else {
130
+ repr = PyObject_Repr(PyDict_GetItem(locals, name));
131
+ }
132
+ if (!repr) {
133
+ repr = PyUnicode_FromString("Not Displayable");
134
+ PyErr_Clear();
135
+ }
136
+ PyDict_SetItem(func_arg_dict, name, repr);
137
+ Py_DECREF(repr);
138
+ idx++;
139
+ }
140
+
141
+ #if PY_VERSION_HEX >= 0x030D0000
142
+ Py_DECREF(locals);
143
+ #endif
144
+
145
+ PyDict_SetItemString(node->args, "func_args", func_arg_dict);
146
+ Py_DECREF(func_arg_dict);
147
+
148
+ Py_XDECREF(code);
149
+ Py_XDECREF(names);
150
+ }
151
+
152
+ static void
153
+ verbose_printf(TracerObject* self, int v, const char* fmt, ...)
154
+ {
155
+ va_list args;
156
+ if (self->verbose >= v) {
157
+ va_start(args, fmt);
158
+ vprintf(fmt, args);
159
+ va_end(args);
160
+ fflush(stdout);
161
+ }
162
+ }
163
+
164
+ void
165
+ clear_stack(struct FunctionNode** stack_top) {
166
+ Py_CLEAR((*stack_top)->args);
167
+ Py_CLEAR((*stack_top)->func);
168
+ while ((*stack_top)->prev) {
169
+ (*stack_top) = (*stack_top) -> prev;
170
+ Py_CLEAR((*stack_top)->args);
171
+ Py_CLEAR((*stack_top)->func);
172
+ }
173
+ }
174
+
175
+ // =============================================================================
176
+ // Thread info related functions
177
+ // =============================================================================
178
+
179
+ static struct ThreadInfo*
180
+ snaptrace_createthreadinfo(TracerObject* self) {
181
+ struct ThreadInfo* info = PyMem_Calloc(1, sizeof(struct ThreadInfo));
182
+ info->stack_top = (struct FunctionNode*) PyMem_Calloc(1, sizeof(struct FunctionNode));
183
+
184
+ #if _WIN32
185
+ info->tid = GetCurrentThreadId();
186
+ #elif defined(__APPLE__)
187
+ __uint64_t tid = 0;
188
+ if (pthread_threadid_np(NULL, &tid)) {
189
+ info->tid = (unsigned long)pthread_self();
190
+ } else {
191
+ info->tid = tid;
192
+ }
193
+ #elif defined(__FreeBSD__)
194
+ info->tid = pthread_getthreadid_np();
195
+ #else
196
+ info->tid = syscall(SYS_gettid);
197
+ #endif
198
+
199
+ #if _WIN32
200
+ TlsSetValue(self->dwTlsIndex, info);
201
+ #else
202
+ pthread_setspecific(self->thread_key, info);
203
+ #endif
204
+
205
+ PyGILState_STATE state = PyGILState_Ensure();
206
+ SNAPTRACE_THREAD_PROTECT_START(self);
207
+
208
+ PyObject* current_thread = PyObject_CallMethod(threading_module, "current_thread", "");
209
+ if (!current_thread) {
210
+ PyErr_SetString(PyExc_RuntimeError, "Failed to get current thread");
211
+ goto cleanup;
212
+ }
213
+ PyObject* thread_name = PyObject_GetAttrString(current_thread, "name");
214
+ if (!thread_name) {
215
+ // It's okay not having a name
216
+ PyErr_Clear();
217
+ thread_name = PyUnicode_FromString("Unknown");
218
+ }
219
+
220
+ Py_DECREF(current_thread);
221
+
222
+ // Check for existing node for the same tid first
223
+ struct MetadataNode* node = self->metadata_head;
224
+ int found_node = 0;
225
+
226
+ while (node) {
227
+ if (node->tid == info->tid) {
228
+ Py_DECREF(node->name);
229
+ node->name = thread_name;
230
+ node->thread_info = info;
231
+ info->metadata_node = node;
232
+ found_node = 1;
233
+ break;
234
+ }
235
+ node = node->next;
236
+ }
237
+
238
+ if (!found_node) {
239
+ node = (struct MetadataNode*) PyMem_Calloc(1, sizeof(struct MetadataNode));
240
+ if (!node) {
241
+ PyErr_SetString(PyExc_RuntimeError, "Out of memory!");
242
+ info = NULL;
243
+ goto cleanup;
244
+ }
245
+ node->name = thread_name;
246
+ node->tid = info->tid;
247
+ node->thread_info = info;
248
+ info->metadata_node = node;
249
+ node->next = self->metadata_head;
250
+ self->metadata_head = node;
251
+ }
252
+
253
+ info->curr_task = NULL;
254
+ info->curr_task_frame = NULL;
255
+
256
+ cleanup:
257
+
258
+ SNAPTRACE_THREAD_PROTECT_END(self);
259
+ PyGILState_Release(state);
260
+
261
+ return info;
262
+ }
263
+
264
+ static struct ThreadInfo*
265
+ get_thread_info(TracerObject* self)
266
+ {
267
+ // self is non-NULL value
268
+ struct ThreadInfo* info = NULL;
269
+ #if _WIN32
270
+ info = TlsGetValue(self->dwTlsIndex);
271
+ #else
272
+ info = pthread_getspecific(self->thread_key);
273
+ #endif
274
+ if (!info) {
275
+ info = snaptrace_createthreadinfo(self);
276
+ }
277
+ return info;
278
+ }
279
+
280
+ static void
281
+ snaptrace_threaddestructor(void* key) {
282
+ struct ThreadInfo* info = key;
283
+ struct FunctionNode* tmp = NULL;
284
+ if (info) {
285
+ PyGILState_STATE state = PyGILState_Ensure();
286
+ info->paused = 0;
287
+ info->curr_stack_depth = 0;
288
+ info->ignore_stack_depth = 0;
289
+ info->tid = 0;
290
+ if (info->stack_top) {
291
+ while (info->stack_top->prev) {
292
+ info->stack_top = info->stack_top->prev;
293
+ }
294
+ while (info->stack_top) {
295
+ tmp = info->stack_top;
296
+ Py_CLEAR(tmp->args);
297
+ Py_CLEAR(tmp->func);
298
+ info->stack_top = info->stack_top->next;
299
+ PyMem_FREE(tmp);
300
+ }
301
+ }
302
+ info->stack_top = NULL;
303
+ Py_CLEAR(info->curr_task);
304
+ Py_CLEAR(info->curr_task_frame);
305
+ info->metadata_node->thread_info = NULL;
306
+ PyMem_FREE(info);
307
+ PyGILState_Release(state);
308
+ }
309
+ }
310
+
311
+ // =============================================================================
312
+ // Tracing function, triggered when FEE
313
+ // =============================================================================
314
+
315
+ // This function is called before we actually start tracing.
316
+ // * Prepare the thread info and create one if not exist
317
+ // * Check if we should trace based on all the flags
318
+ // * -1: Error
319
+ // * 0: Not trace
320
+ // * 1: Trace
321
+ int
322
+ prepare_before_trace(TracerObject* self, int is_call, struct ThreadInfo** info_out) {
323
+
324
+ if (!self->collecting) {
325
+ return 0;
326
+ }
327
+
328
+ struct ThreadInfo* info = get_thread_info(self);
329
+
330
+ if (!info) {
331
+ self->collecting = 0;
332
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to create thread info. This should not happen.");
333
+ return -1;
334
+ }
335
+
336
+ *info_out = info;
337
+
338
+ if (info->paused) {
339
+ return 0;
340
+ }
341
+
342
+ if (info->ignore_stack_depth > 0) {
343
+ return 0;
344
+ }
345
+
346
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_MAX_STACK_DEPTH)) {
347
+ if (is_call) {
348
+ if (info->curr_stack_depth >= self->max_stack_depth) {
349
+ return 0;
350
+ }
351
+ } else {
352
+ if (info->curr_stack_depth > 0) {
353
+ if (info->curr_stack_depth > self->max_stack_depth) {
354
+ return 0;
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ return 1;
361
+ }
362
+
363
+ int
364
+ tracer_pycall_callback(TracerObject* self, PyCodeObject* code)
365
+ {
366
+ struct ThreadInfo* info = NULL;
367
+
368
+ if (prepare_before_trace(self, 1, &info) <= 0) {
369
+ // For now we think -1 and 0 should both return because we should not
370
+ // have the -1 case.
371
+ goto cleanup_ignore;
372
+ }
373
+
374
+ PyObject* co_filename = code->co_filename;
375
+
376
+ if (!CHECK_FLAG(self->check_flags, SNAPTRACE_TRACE_SELF)) {
377
+ if (self->lib_file_path && co_filename && PyUnicode_Check(co_filename) &&
378
+ startswith(PyUnicode_AsUTF8(co_filename), self->lib_file_path)) {
379
+ goto cleanup_ignore;
380
+ }
381
+ }
382
+
383
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_INCLUDE_FILES | SNAPTRACE_EXCLUDE_FILES)) {
384
+ if (info->ignore_stack_depth == 0) {
385
+ PyObject* files = NULL;
386
+ int record = 0;
387
+ int is_include = CHECK_FLAG(self->check_flags, SNAPTRACE_INCLUDE_FILES);
388
+ if (is_include) {
389
+ files = self->include_files;
390
+ record = 0;
391
+ } else {
392
+ files = self->exclude_files;
393
+ record = 1;
394
+ }
395
+ Py_ssize_t length = PyList_GET_SIZE(files);
396
+ for (int i = 0; i < length; i++) {
397
+ PyObject* f = PyList_GET_ITEM(files, i);
398
+ if (startswith(PyUnicode_AsUTF8(co_filename), PyUnicode_AsUTF8(f))) {
399
+ record = 1 - record;
400
+ break;
401
+ }
402
+ }
403
+ if (record == 0) {
404
+ goto cleanup_ignore;
405
+ }
406
+ } else {
407
+ goto cleanup_ignore;
408
+ }
409
+ }
410
+
411
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_IGNORE_FROZEN)) {
412
+ if (startswith(PyUnicode_AsUTF8(co_filename), "<frozen")) {
413
+ goto cleanup_ignore;
414
+ }
415
+ }
416
+
417
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC) &&
418
+ info->curr_task == NULL &&
419
+ (code->co_flags & CO_COROUTINE) != 0) {
420
+ PyObject* curr_task = Py_None;
421
+ info->paused = 1;
422
+ for (size_t i = 0; i < sizeof(curr_task_getters)/sizeof(curr_task_getters[0]); i++) {
423
+ if (curr_task_getters[i] != NULL) {
424
+ curr_task = PyObject_CallNoArgs(curr_task_getters[i]);
425
+ if (!curr_task) {
426
+ PyErr_Clear(); // RuntimeError, probably
427
+ curr_task = Py_None;
428
+ } else if (curr_task != Py_None) {
429
+ break; // got a valid task
430
+ }
431
+ }
432
+ }
433
+ info->paused = 0;
434
+ info->curr_task = Py_NewRef(curr_task);
435
+ info->curr_task_frame = (PyFrameObject*)Py_NewRef(PyEval_GetFrame());
436
+ }
437
+
438
+ // If it's a call, we need a new node, and we need to update the stack
439
+ if (!info->stack_top->next) {
440
+ info->stack_top->next = (struct FunctionNode*) PyMem_Calloc(1, sizeof(struct FunctionNode));
441
+ info->stack_top->next->prev = info->stack_top;
442
+ }
443
+ info->stack_top = info->stack_top->next;
444
+ info->stack_top->ts = get_ts();
445
+ info->stack_top->func = Py_NewRef(code);
446
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_FUNCTION_ARGS)) {
447
+ log_func_args(info->stack_top, PyEval_GetFrame(), self->log_func_repr);
448
+ }
449
+
450
+ info->curr_stack_depth += 1;
451
+
452
+ return 0;
453
+
454
+ cleanup_ignore:
455
+
456
+ if (info) {
457
+ info->ignore_stack_depth += 1;
458
+ info->curr_stack_depth += 1;
459
+ }
460
+
461
+ return 0;
462
+ }
463
+
464
+ int
465
+ tracer_ccall_callback(TracerObject* self, PyCodeObject* code, PyObject* arg)
466
+ {
467
+ struct ThreadInfo* info = NULL;
468
+
469
+ if (prepare_before_trace(self, 1, &info) <= 0) {
470
+ // For now we think -1 and 0 should both return because we should not
471
+ // have the -1 case.
472
+ goto cleanup_ignore;
473
+ }
474
+
475
+ PyCFunctionObject* cfunc = (PyCFunctionObject*) arg;
476
+
477
+ if (cfunc->m_self == (PyObject*)self) {
478
+ goto cleanup_ignore;
479
+ }
480
+
481
+ // If it's a call, we need a new node, and we need to update the stack
482
+ if (!info->stack_top->next) {
483
+ info->stack_top->next = (struct FunctionNode*) PyMem_Calloc(1, sizeof(struct FunctionNode));
484
+ info->stack_top->next->prev = info->stack_top;
485
+ }
486
+ info->stack_top = info->stack_top->next;
487
+ info->stack_top->ts = get_ts();
488
+ info->stack_top->func = Py_NewRef(arg);
489
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_FUNCTION_ARGS)) {
490
+ log_func_args(info->stack_top, PyEval_GetFrame(), self->log_func_repr);
491
+ }
492
+
493
+ info->curr_stack_depth += 1;
494
+
495
+ return 0;
496
+
497
+ cleanup_ignore:
498
+
499
+ if (info) {
500
+ info->ignore_stack_depth += 1;
501
+ info->curr_stack_depth += 1;
502
+ }
503
+
504
+ return 0;
505
+ }
506
+
507
+ int
508
+ tracer_pyreturn_callback(TracerObject* self, PyCodeObject* code, PyObject* arg)
509
+ {
510
+ struct ThreadInfo* info = NULL;
511
+
512
+ if (prepare_before_trace(self, 0, &info) <= 0) {
513
+ // For now we think -1 and 0 should both return because we should not
514
+ // have the -1 case.
515
+ goto cleanup_ignore;
516
+ }
517
+
518
+ struct FunctionNode* stack_top = info->stack_top;
519
+ if (stack_top->prev) {
520
+ // if stack_top has prev, it's not the fake node so it's at least root
521
+ int64_t dur = get_ts() - info->stack_top->ts;
522
+ int log_this_entry = self->min_duration == 0 || dur_ts_to_ns(dur) >= self->min_duration;
523
+
524
+ if (log_this_entry) {
525
+ PyCodeObject* call_code = (PyCodeObject*) stack_top->func;
526
+
527
+ if (!PyCode_Check(call_code) || call_code != code) {
528
+ self->collecting = 0;
529
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Unexpected type. Might be an event mismatch.");
530
+ return -1;
531
+ }
532
+
533
+ struct EventNode* node = get_next_node(self);
534
+
535
+ node->ntype = FEE_NODE;
536
+ node->ts = info->stack_top->ts;
537
+ node->data.fee.dur = dur;
538
+ node->tid = info->tid;
539
+ node->data.fee.type = PyTrace_RETURN;
540
+ node->data.fee.code = (PyCodeObject*)Py_NewRef(code);
541
+ // steal the reference when return
542
+ node->data.fee.args = Py_XNewRef(stack_top->args);
543
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_RETURN_VALUE)) {
544
+ PyObject* repr = NULL;
545
+ if (self->log_func_repr) {
546
+ repr = PyObject_CallOneArg(self->log_func_repr, arg);
547
+ } else {
548
+ repr = PyObject_Repr(arg);
549
+ }
550
+ if (!repr) {
551
+ repr = PyUnicode_FromString("Not Displayable");
552
+ PyErr_Clear();
553
+ }
554
+ node->data.fee.retval = repr;
555
+ }
556
+
557
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC)) {
558
+ node->data.fee.asyncio_task = Py_XNewRef(info->curr_task);
559
+ }
560
+ }
561
+ // Finish return whether to log the data
562
+ info->stack_top = info->stack_top->prev;
563
+
564
+ Py_CLEAR(stack_top->args);
565
+ Py_CLEAR(stack_top->func);
566
+
567
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC) &&
568
+ info->curr_task &&
569
+ PyEval_GetFrame() == info->curr_task_frame) {
570
+ Py_CLEAR(info->curr_task);
571
+ Py_CLEAR(info->curr_task_frame);
572
+ }
573
+ }
574
+
575
+ if (info->curr_stack_depth > 0) {
576
+ info->curr_stack_depth -= 1;
577
+ }
578
+
579
+ return 0;
580
+
581
+ cleanup_ignore:
582
+
583
+ if (info) {
584
+ if (info->curr_stack_depth > 0) {
585
+ info->curr_stack_depth -= 1;
586
+ }
587
+
588
+ if (info->ignore_stack_depth > 0) {
589
+ info->ignore_stack_depth -= 1;
590
+ }
591
+ }
592
+
593
+ return 0;
594
+ }
595
+
596
+ int
597
+ tracer_creturn_callback(TracerObject* self, PyCodeObject* code, PyObject* arg)
598
+ {
599
+ struct ThreadInfo* info = NULL;
600
+
601
+ if (prepare_before_trace(self, 0, &info) <= 0) {
602
+ // For now we think -1 and 0 should both return because we should not
603
+ // have the -1 case.
604
+ goto cleanup_ignore;
605
+ }
606
+
607
+ struct FunctionNode* stack_top = info->stack_top;
608
+ if (stack_top->prev) {
609
+ // if stack_top has prev, it's not the fake node so it's at least root
610
+ int64_t dur = get_ts() - info->stack_top->ts;
611
+ int log_this_entry = self->min_duration == 0 || dur_ts_to_ns(dur) >= self->min_duration;
612
+
613
+ if (log_this_entry) {
614
+ PyCFunctionObject* cfunc = (PyCFunctionObject*) stack_top->func;
615
+
616
+ if (!PyCFunction_Check(cfunc)) {
617
+ self->collecting = 0;
618
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Unexpected type. Might be an event mismatch.");
619
+ return -1;
620
+ }
621
+
622
+ struct EventNode* node = get_next_node(self);
623
+
624
+ node->ntype = FEE_NODE;
625
+ node->ts = info->stack_top->ts;
626
+ node->data.fee.dur = dur;
627
+ node->tid = info->tid;
628
+ node->data.fee.type = PyTrace_C_RETURN;
629
+ node->data.fee.ml_name = cfunc->m_ml->ml_name;
630
+ if (cfunc->m_module) {
631
+ // The function belongs to a module
632
+ node->data.fee.m_module = Py_NewRef(cfunc->m_module);
633
+ } else {
634
+ // The function is a class method
635
+ node->data.fee.m_module = NULL;
636
+ if (cfunc->m_self) {
637
+ // It's not a static method, has __self__
638
+ node->data.fee.tp_name = cfunc->m_self->ob_type->tp_name;
639
+ } else {
640
+ // It's a static method, does not have __self__
641
+ node->data.fee.tp_name = NULL;
642
+ }
643
+ }
644
+
645
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC)) {
646
+ node->data.fee.asyncio_task = Py_XNewRef(info->curr_task);
647
+ }
648
+ }
649
+ // Finish return whether to log the data
650
+ info->stack_top = info->stack_top->prev;
651
+
652
+ Py_CLEAR(stack_top->args);
653
+ Py_CLEAR(stack_top->func);
654
+ }
655
+
656
+
657
+ if (info->curr_stack_depth > 0) {
658
+ info->curr_stack_depth -= 1;
659
+ }
660
+
661
+ return 0;
662
+
663
+ cleanup_ignore:
664
+
665
+ if (info) {
666
+ if (info->curr_stack_depth > 0) {
667
+ info->curr_stack_depth -= 1;
668
+ }
669
+
670
+ if (info->ignore_stack_depth > 0) {
671
+ info->ignore_stack_depth -= 1;
672
+ }
673
+ }
674
+
675
+ return 0;
676
+ }
677
+
678
+ // sys.setprofile mechanism
679
+
680
+ int
681
+ tracer_tracefunc(PyObject* obj, PyFrameObject* frame, int what, PyObject* arg)
682
+ {
683
+ TracerObject* self = (TracerObject*) obj;
684
+ int ret = 0;
685
+
686
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_IGNORE_C_FUNCTION) &&
687
+ (what == PyTrace_C_CALL || what == PyTrace_C_RETURN || what == PyTrace_C_EXCEPTION)) {
688
+ return 0;
689
+ }
690
+
691
+ PyCodeObject* code = PyFrame_GetCode(frame);
692
+
693
+ switch (what) {
694
+ case PyTrace_CALL:
695
+ ret = tracer_pycall_callback(self, code);
696
+ break;
697
+ case PyTrace_C_CALL:
698
+ ret = tracer_ccall_callback(self, code, arg);
699
+ break;
700
+ case PyTrace_RETURN:
701
+ ret = tracer_pyreturn_callback(self, code, arg);
702
+ break;
703
+ case PyTrace_C_RETURN:
704
+ case PyTrace_C_EXCEPTION:
705
+ ret = tracer_creturn_callback(self, code, arg);
706
+ break;
707
+ default:
708
+ return 0;
709
+ }
710
+
711
+ Py_DECREF(code);
712
+
713
+ return ret;
714
+ }
715
+
716
+ static PyObject*
717
+ tracer_threadtracefunc(PyObject* obj, PyObject* args)
718
+ {
719
+ PyFrameObject* frame = NULL;
720
+ char* event = NULL;
721
+ PyObject* trace_args = NULL;
722
+ int what = 0;
723
+ if (!PyArg_ParseTuple(args, "OsO", &frame, &event, &trace_args)) {
724
+ printf("Error when parsing arguments!\n");
725
+ exit(1);
726
+ }
727
+ PyEval_SetProfile(tracer_tracefunc, obj);
728
+ if (!strcmp(event, "call")) {
729
+ what = PyTrace_CALL;
730
+ } else if (!strcmp(event, "c_call")) {
731
+ what = PyTrace_C_CALL;
732
+ } else if (!strcmp(event, "return")) {
733
+ what = PyTrace_RETURN;
734
+ } else if (!strcmp(event, "c_return")) {
735
+ what = PyTrace_C_RETURN;
736
+ } else if (!strcmp(event, "c_exception")) {
737
+ what = PyTrace_C_EXCEPTION;
738
+ } else {
739
+ printf("Unexpected event type: %s\n", event);
740
+ }
741
+ tracer_tracefunc(obj, frame, what, trace_args);
742
+ Py_RETURN_NONE;
743
+ }
744
+
745
+ // sys.monitoring mechanism
746
+
747
+ PyObject* get_cfunc_from_callable(PyObject* callable, PyObject* self_arg)
748
+ {
749
+ // return a new reference
750
+ if (PyCFunction_Check(callable)) {
751
+ Py_INCREF(callable);
752
+ return (PyObject*)((PyCFunctionObject*)callable);
753
+ }
754
+ if (Py_TYPE(callable) == &PyMethodDescr_Type) {
755
+ /* For backwards compatibility need to
756
+ * convert to builtin method */
757
+
758
+ /* If no arg, skip */
759
+ if (self_arg == sys_monitoring_missing) {
760
+ return NULL;
761
+ }
762
+ PyObject* meth = Py_TYPE(callable)->tp_descr_get(
763
+ callable, self_arg, (PyObject*)Py_TYPE(self_arg));
764
+ if (meth == NULL) {
765
+ return NULL;
766
+ }
767
+ if (PyCFunction_Check(meth)) {
768
+ return (PyObject*)((PyCFunctionObject*)meth);
769
+ }
770
+ } else if (Py_TYPE(callable) == &PyMethod_Type) {
771
+ PyObject* func = PyMethod_GET_FUNCTION(callable);
772
+ if (func && PyCFunction_Check(func)) {
773
+ Py_INCREF(func);
774
+ return func;
775
+ }
776
+ }
777
+ return NULL;
778
+ }
779
+
780
+ PyObject*
781
+ _pystart_callback(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
782
+ {
783
+ PyCodeObject* code = (PyCodeObject*)args[0];
784
+ int ret = tracer_pycall_callback((TracerObject*)self, code);
785
+ if (ret != 0) {
786
+ return NULL;
787
+ }
788
+ Py_RETURN_NONE;
789
+ }
790
+
791
+ PyObject*
792
+ _pyreturn_callback(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
793
+ {
794
+ PyCodeObject* code = (PyCodeObject*)args[0];
795
+ PyObject* arg = args[2];
796
+ int ret = tracer_pyreturn_callback((TracerObject*)self, code, arg);
797
+ if (ret != 0) {
798
+ return NULL;
799
+ }
800
+ Py_RETURN_NONE;
801
+ }
802
+
803
+ PyObject*
804
+ _ccall_callback(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
805
+ {
806
+ PyCodeObject* code = (PyCodeObject*)args[0];
807
+ PyObject* cfunc = get_cfunc_from_callable(args[2], args[3]);
808
+ if (!cfunc) {
809
+ Py_RETURN_NONE;
810
+ }
811
+ int ret = tracer_ccall_callback((TracerObject*)self, code, cfunc);
812
+ Py_DECREF(cfunc);
813
+ if (ret != 0) {
814
+ return NULL;
815
+ }
816
+ Py_RETURN_NONE;
817
+ }
818
+
819
+ PyObject*
820
+ _creturn_callback(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
821
+ {
822
+ PyCodeObject* code = (PyCodeObject*)args[0];
823
+ PyObject* cfunc = get_cfunc_from_callable(args[2], args[3]);
824
+ if (!cfunc) {
825
+ Py_RETURN_NONE;
826
+ }
827
+ int ret = tracer_creturn_callback((TracerObject*)self, code, cfunc);
828
+ Py_DECREF(cfunc);
829
+ if (ret != 0) {
830
+ return NULL;
831
+ }
832
+ Py_RETURN_NONE;
833
+ }
834
+
835
+ static struct {
836
+ unsigned int event;
837
+ PyMethodDef callback_method;
838
+ } callback_table[] = {
839
+ {PY_MONITORING_EVENT_PY_START,
840
+ {"_pystart_callback", (PyCFunction)_pystart_callback, METH_FASTCALL, NULL}},
841
+ {PY_MONITORING_EVENT_PY_RESUME,
842
+ {"_pystart_callback", (PyCFunction)_pystart_callback, METH_FASTCALL, NULL}},
843
+ {PY_MONITORING_EVENT_PY_THROW,
844
+ {"_pystart_callback", (PyCFunction)_pystart_callback, METH_FASTCALL, NULL}},
845
+ {PY_MONITORING_EVENT_PY_RETURN,
846
+ {"_pyreturn_callback", (PyCFunction)_pyreturn_callback, METH_FASTCALL, NULL}},
847
+ {PY_MONITORING_EVENT_PY_YIELD,
848
+ {"_pyreturn_callback", (PyCFunction)_pyreturn_callback, METH_FASTCALL, NULL}},
849
+ {PY_MONITORING_EVENT_PY_UNWIND,
850
+ {"_pyreturn_callback", (PyCFunction)_pyreturn_callback, METH_FASTCALL, NULL}},
851
+ {PY_MONITORING_EVENT_CALL,
852
+ {"_ccall_callback", (PyCFunction)_ccall_callback, METH_FASTCALL, NULL}},
853
+ {PY_MONITORING_EVENT_C_RETURN,
854
+ {"_creturn_callback", (PyCFunction)_creturn_callback, METH_FASTCALL, NULL}},
855
+ {PY_MONITORING_EVENT_C_RAISE,
856
+ {"_creturn_callback", (PyCFunction)_creturn_callback, METH_FASTCALL, NULL}},
857
+ {0,
858
+ {NULL, NULL, 0, NULL}}
859
+ };
860
+
861
+ int
862
+ enable_monitoring(TracerObject* self)
863
+ {
864
+ unsigned int all_events = 0;
865
+ PyObject* monitoring = PyObject_GetAttrString(sys_module, "monitoring");
866
+ if (!monitoring) {
867
+ PyErr_SetString(PyExc_RuntimeError, "Failed to access sys.monitoring");
868
+ goto cleanup;
869
+ }
870
+
871
+ PyObject* ret = PyObject_CallMethod(monitoring, "use_tool_id",
872
+ "is", SNAPTRACE_TOOL_ID, "viztracer");
873
+ if (!ret) {
874
+ PyErr_Clear();
875
+ PyObject_CallMethod(monitoring, "free_tool_id", "i", SNAPTRACE_TOOL_ID);
876
+ ret = PyObject_CallMethod(monitoring, "use_tool_id",
877
+ "is", SNAPTRACE_TOOL_ID, "viztracer");
878
+ if (!ret) {
879
+ goto cleanup;
880
+ }
881
+ }
882
+ Py_DECREF(ret);
883
+
884
+ for (int i = 0; callback_table[i].callback_method.ml_meth != 0; i++) {
885
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_IGNORE_C_FUNCTION) &&
886
+ (callback_table[i].event == PY_MONITORING_EVENT_CALL ||
887
+ callback_table[i].event == PY_MONITORING_EVENT_C_RETURN ||
888
+ callback_table[i].event == PY_MONITORING_EVENT_C_RAISE)) {
889
+ continue;
890
+ }
891
+ unsigned int event = (1 << callback_table[i].event);
892
+ PyObject* callback = PyCFunction_New(&callback_table[i].callback_method, (PyObject*)self);
893
+
894
+ PyObject* regsiter_result = PyObject_CallMethod(monitoring, "register_callback",
895
+ "iiO", SNAPTRACE_TOOL_ID, event, callback);
896
+ Py_DECREF(callback);
897
+
898
+ if (!regsiter_result) {
899
+ goto cleanup;
900
+ }
901
+ Py_DECREF(regsiter_result);
902
+ all_events |= event;
903
+ }
904
+
905
+ PyObject* event_result = PyObject_CallMethod(monitoring, "set_events",
906
+ "ii", SNAPTRACE_TOOL_ID, all_events);
907
+ if (!event_result) {
908
+ goto cleanup;
909
+ }
910
+ Py_DECREF(event_result);
911
+
912
+ cleanup:
913
+
914
+ Py_XDECREF(monitoring);
915
+
916
+ if (PyErr_Occurred()) {
917
+ return -1;
918
+ }
919
+
920
+ return 0;
921
+ }
922
+
923
+ int disable_monitoring(TracerObject* self)
924
+ {
925
+ PyObject* monitoring = PyObject_GetAttrString(sys_module, "monitoring");
926
+ if (!monitoring) {
927
+ PyErr_SetString(PyExc_RuntimeError, "Failed to access sys.monitoring");
928
+ goto cleanup;
929
+ }
930
+
931
+ PyObject* curr_tool = PyObject_CallMethod(monitoring, "get_tool", "i", SNAPTRACE_TOOL_ID);
932
+
933
+ if (!curr_tool) {
934
+ goto cleanup;
935
+ }
936
+
937
+ if (curr_tool == Py_None) {
938
+ // No current tool, nothing to do
939
+ Py_DECREF(curr_tool);
940
+ goto cleanup;
941
+ }
942
+
943
+ PyObject* event_result = PyObject_CallMethod(monitoring, "set_events",
944
+ "ii", SNAPTRACE_TOOL_ID, 0);
945
+ if (!event_result) {
946
+ goto cleanup;
947
+ }
948
+ Py_DECREF(event_result);
949
+
950
+ PyObject* ret = PyObject_CallMethod(monitoring, "free_tool_id",
951
+ "i", SNAPTRACE_TOOL_ID);
952
+ if (!ret) {
953
+ goto cleanup;
954
+ }
955
+ Py_DECREF(ret);
956
+
957
+ cleanup:
958
+
959
+ Py_XDECREF(monitoring);
960
+
961
+ if (PyErr_Occurred()) {
962
+ return -1;
963
+ }
964
+
965
+ return 0;
966
+ }
967
+
968
+ // =============================================================================
969
+ // snaptrace.Tracer methods
970
+ // =============================================================================
971
+
972
+ static void
973
+ tracer__flush_unfinished(TracerObject* self, int flush_as_finish)
974
+ {
975
+ SNAPTRACE_THREAD_PROTECT_START(self);
976
+
977
+ struct MetadataNode* meta_node = self->metadata_head;
978
+ while(meta_node) {
979
+ struct ThreadInfo* info = meta_node->thread_info;
980
+
981
+ if (info == NULL) {
982
+ meta_node = meta_node->next;
983
+ continue;
984
+ }
985
+
986
+ struct FunctionNode* func_node = info->stack_top;
987
+
988
+ while (func_node->prev && info->curr_stack_depth > 0) {
989
+ // Fake a FEE node to get the name
990
+ struct EventNode* fee_node = get_next_node(self);
991
+
992
+ fee_node->ntype = FEE_NODE;
993
+ fee_node->ts = func_node->ts;
994
+ fee_node->tid = meta_node->tid;
995
+
996
+ if (flush_as_finish) {
997
+ fee_node->data.fee.dur = get_ts() - func_node->ts;
998
+ } else {
999
+ fee_node->data.fee.dur = 0;
1000
+ }
1001
+
1002
+ if (PyCode_Check(func_node->func)) {
1003
+ PyCodeObject* code = (PyCodeObject*) func_node->func;
1004
+ if (flush_as_finish) {
1005
+ fee_node->data.fee.type = PyTrace_RETURN;
1006
+ } else {
1007
+ fee_node->data.fee.type = PyTrace_CALL;
1008
+ }
1009
+ fee_node->data.fee.code = (PyCodeObject*)Py_NewRef(code);
1010
+ } else if (PyCFunction_Check(func_node->func)) {
1011
+ PyCFunctionObject* cfunc = (PyCFunctionObject*) func_node->func;
1012
+ if (flush_as_finish) {
1013
+ fee_node->data.fee.type = PyTrace_C_RETURN;
1014
+ } else {
1015
+ fee_node->data.fee.type = PyTrace_C_CALL;
1016
+ }
1017
+ fee_node->data.fee.ml_name = cfunc->m_ml->ml_name;
1018
+ if (cfunc->m_module) {
1019
+ // The function belongs to a module
1020
+ fee_node->data.fee.m_module = Py_NewRef(cfunc->m_module);
1021
+ } else {
1022
+ // The function is a class method
1023
+ fee_node->data.fee.m_module = NULL;
1024
+ if (cfunc->m_self) {
1025
+ // It's not a static method, has __self__
1026
+ fee_node->data.fee.tp_name = cfunc->m_self->ob_type->tp_name;
1027
+ } else {
1028
+ // It's a static method, does not have __self__
1029
+ fee_node->data.fee.tp_name = NULL;
1030
+ }
1031
+ }
1032
+ }
1033
+
1034
+ // Clean up the node
1035
+ Py_CLEAR(func_node->args);
1036
+ Py_CLEAR(func_node->func);
1037
+
1038
+ func_node = func_node->prev;
1039
+ info->curr_stack_depth -= 1;
1040
+ }
1041
+ info->stack_top = func_node;
1042
+ meta_node = meta_node->next;
1043
+ }
1044
+
1045
+ SNAPTRACE_THREAD_PROTECT_END(self);
1046
+ }
1047
+
1048
+ static PyObject*
1049
+ tracer_start(TracerObject* self, PyObject* Py_UNUSED(unused))
1050
+ {
1051
+ if (curr_tracer) {
1052
+ printf("Warning! Overwrite tracer! You should not have two VizTracer recording at the same time!\n");
1053
+ } else {
1054
+ curr_tracer = self;
1055
+ }
1056
+
1057
+ self->collecting = 1;
1058
+ #if PY_VERSION_HEX >= 0x030C0000
1059
+ if (enable_monitoring(self) != 0) {
1060
+ return NULL;
1061
+ };
1062
+ #else
1063
+ PyEval_SetProfile(tracer_tracefunc, (PyObject*) self);
1064
+ #endif
1065
+
1066
+ Py_RETURN_NONE;
1067
+ }
1068
+
1069
+ static PyObject*
1070
+ tracer_stop(TracerObject* self, PyObject* stop_option)
1071
+ {
1072
+ if (self) {
1073
+ struct ThreadInfo* info = get_thread_info(self);
1074
+ if (!info) {
1075
+ self->collecting = 0;
1076
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1077
+ return NULL;
1078
+ }
1079
+ self->collecting = 0;
1080
+
1081
+ if (PyUnicode_CheckExact(stop_option) &&
1082
+ strcmp(PyUnicode_AsUTF8(stop_option), "flush_as_finish") == 0) {
1083
+ tracer__flush_unfinished(self, 1);
1084
+ } else {
1085
+ tracer__flush_unfinished(self, 0);
1086
+ }
1087
+ info->curr_stack_depth = 0;
1088
+ info->ignore_stack_depth = 0;
1089
+ info->paused = 0;
1090
+ }
1091
+
1092
+ curr_tracer = NULL;
1093
+ #if PY_VERSION_HEX >= 0x030C0000
1094
+ if (disable_monitoring(self) != 0) {
1095
+ return NULL;
1096
+ }
1097
+ #else
1098
+ PyEval_SetProfile(NULL, NULL);
1099
+ #endif
1100
+
1101
+ Py_RETURN_NONE;
1102
+ }
1103
+
1104
+ static PyObject*
1105
+ tracer_pause(TracerObject* self, PyObject* Py_UNUSED(unused))
1106
+ {
1107
+ if (self->collecting) {
1108
+ struct ThreadInfo* info = get_thread_info((TracerObject*)self);
1109
+ if (!info) {
1110
+ self->collecting = 0;
1111
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1112
+ return NULL;
1113
+ }
1114
+
1115
+ if (!info->paused) {
1116
+ // When we enter this function, tracer.pause has been called.
1117
+ // We need to reduce the ignore_stack_depth to simulate the
1118
+ // returns from these two functions
1119
+ info->ignore_stack_depth -= 1;
1120
+ info->paused = 1;
1121
+ #if PY_VERSION_HEX >= 0x030C0000
1122
+ if (disable_monitoring(self) != 0) {
1123
+ return NULL;
1124
+ }
1125
+ #else
1126
+ PyEval_SetProfile(NULL, NULL);
1127
+ #endif
1128
+ }
1129
+ }
1130
+
1131
+ Py_RETURN_NONE;
1132
+ }
1133
+
1134
+ static PyObject*
1135
+ tracer_resume(TracerObject* self, PyObject* Py_UNUSED(unused))
1136
+ {
1137
+ if (self->collecting) {
1138
+ struct ThreadInfo* info = get_thread_info(self);
1139
+ if (!info) {
1140
+ self->collecting = 0;
1141
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1142
+ return NULL;
1143
+ }
1144
+
1145
+ if (info->paused) {
1146
+ info->paused = 0;
1147
+ #if PY_VERSION_HEX >= 0x030C0000
1148
+ if (enable_monitoring(self) != 0) {
1149
+ return NULL;
1150
+ }
1151
+ #else
1152
+ PyEval_SetProfile(tracer_tracefunc, (PyObject*)self);
1153
+ #endif
1154
+ }
1155
+ }
1156
+
1157
+ Py_RETURN_NONE;
1158
+ }
1159
+
1160
+ static PyObject*
1161
+ tracer_load(TracerObject* self, PyObject* Py_UNUSED(unused))
1162
+ {
1163
+ PyObject* lst = PyList_New(0);
1164
+
1165
+ SNAPTRACE_THREAD_PROTECT_START(self);
1166
+ struct EventNode* curr = self->buffer + self->buffer_head_idx;
1167
+ PyObject* pid = NULL;
1168
+ PyObject* cat_fee = PyUnicode_FromString("FEE");
1169
+ PyObject* cat_instant = PyUnicode_FromString("INSTANT");
1170
+ PyObject* ph_B = PyUnicode_FromString("B");
1171
+ PyObject* ph_i = PyUnicode_FromString("i");
1172
+ PyObject* ph_X = PyUnicode_FromString("X");
1173
+ PyObject* ph_C = PyUnicode_FromString("C");
1174
+ PyObject* ph_M = PyUnicode_FromString("M");
1175
+ unsigned long counter = 0;
1176
+ unsigned long prev_counter = 0;
1177
+ struct MetadataNode* metadata_node = NULL;
1178
+ PyObject* task_dict = NULL;
1179
+ PyObject* func_name_dict = PyDict_New();
1180
+
1181
+ if (self->fix_pid > 0) {
1182
+ pid = PyLong_FromLong(self->fix_pid);
1183
+ } else {
1184
+ #if _WIN32
1185
+ pid = PyLong_FromLong(GetCurrentProcessId());
1186
+ #else
1187
+ pid = PyLong_FromLong(getpid());
1188
+ #endif
1189
+ }
1190
+
1191
+ // == Load the metadata first ==
1192
+ // Process Name
1193
+ {
1194
+ PyObject* dict = PyDict_New();
1195
+ PyObject* args = PyDict_New();
1196
+ PyObject* process_name_string = PyUnicode_FromString("process_name");
1197
+ PyObject* process_name = NULL;
1198
+
1199
+ if (self->process_name) {
1200
+ process_name = Py_NewRef(self->process_name);
1201
+ } else {
1202
+ PyObject* current_process_method = PyObject_GetAttrString(multiprocessing_module, "current_process");
1203
+ if (!current_process_method) {
1204
+ perror("Failed to access multiprocessing.current_process()");
1205
+ exit(-1);
1206
+ }
1207
+ PyObject* current_process = PyObject_CallNoArgs(current_process_method);
1208
+ if (!current_process_method) {
1209
+ perror("Failed to access multiprocessing.current_process()");
1210
+ exit(-1);
1211
+ }
1212
+ process_name = PyObject_GetAttrString(current_process, "name");
1213
+ Py_DECREF(current_process_method);
1214
+ Py_DECREF(current_process);
1215
+ }
1216
+
1217
+ PyDict_SetItemString(dict, "ph", ph_M);
1218
+ PyDict_SetItemString(dict, "pid", pid);
1219
+ PyDict_SetItemString(dict, "tid", pid);
1220
+ PyDict_SetItemString(dict, "name", process_name_string);
1221
+ Py_DECREF(process_name_string);
1222
+ PyDict_SetItemString(args, "name", process_name);
1223
+ PyDict_SetItemString(dict, "args", args);
1224
+ Py_DECREF(args);
1225
+ Py_DECREF(process_name);
1226
+ PyList_Append(lst, dict);
1227
+ }
1228
+
1229
+
1230
+ // Thread Name
1231
+ metadata_node = self->metadata_head;
1232
+ while (metadata_node) {
1233
+ PyObject* dict = PyDict_New();
1234
+ PyObject* args = PyDict_New();
1235
+ PyObject* tid = PyLong_FromLong(metadata_node->tid);
1236
+ PyObject* thread_name_string = PyUnicode_FromString("thread_name");
1237
+
1238
+ PyDict_SetItemString(dict, "ph", ph_M);
1239
+ PyDict_SetItemString(dict, "pid", pid);
1240
+ PyDict_SetItemString(dict, "tid", tid);
1241
+ Py_DECREF(tid);
1242
+ PyDict_SetItemString(dict, "name", thread_name_string);
1243
+ Py_DECREF(thread_name_string);
1244
+ PyDict_SetItemString(args, "name", metadata_node->name);
1245
+ PyDict_SetItemString(dict, "args", args);
1246
+ Py_DECREF(args);
1247
+ metadata_node = metadata_node->next;
1248
+ PyList_Append(lst, dict);
1249
+ }
1250
+
1251
+ // Task Name if using LOG_ASYNC
1252
+ // We need to make up some thread id for the task
1253
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC)) {
1254
+ task_dict = PyDict_New();
1255
+ }
1256
+
1257
+ while (curr != self->buffer + self->buffer_tail_idx) {
1258
+ struct EventNode* node = curr;
1259
+ PyObject* dict = PyDict_New();
1260
+ PyObject* name = NULL;
1261
+ PyObject* tid = PyLong_FromLong(node->tid);
1262
+ PyObject* ts = PyFloat_FromDouble(system_ts_to_us(node->ts));
1263
+
1264
+ PyDict_SetItemString(dict, "pid", pid);
1265
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC)) {
1266
+ if (curr->data.fee.asyncio_task == NULL) {
1267
+ PyDict_SetItemString(dict, "tid", tid);
1268
+ } else {
1269
+ PyObject* task_id = PyLong_FromUnsignedLongLong(((uintptr_t)curr->data.fee.asyncio_task) & 0xffffff);
1270
+ PyDict_SetItemString(dict, "tid", task_id);
1271
+ if (!PyDict_Contains(task_dict, task_id)) {
1272
+ PyObject* task_name = NULL;
1273
+ if (PyObject_HasAttrString(curr->data.fee.asyncio_task, "get_name")) {
1274
+ PyObject* task_name_method = PyObject_GetAttrString(curr->data.fee.asyncio_task, "get_name");
1275
+ task_name = PyObject_CallNoArgs(task_name_method);
1276
+ Py_DECREF(task_name_method);
1277
+ } else if (PyObject_HasAttrString(curr->data.fee.asyncio_task, "name")) {
1278
+ task_name = PyObject_GetAttrString(curr->data.fee.asyncio_task, "name");
1279
+ } else {
1280
+ task_name = PyUnicode_FromString("Task");
1281
+ }
1282
+ PyDict_SetItem(task_dict, task_id, task_name);
1283
+ Py_DECREF(task_name);
1284
+ }
1285
+ Py_DECREF(task_id);
1286
+ }
1287
+ } else {
1288
+ PyDict_SetItemString(dict, "tid", tid);
1289
+ }
1290
+ Py_DECREF(tid);
1291
+ PyDict_SetItemString(dict, "ts", ts);
1292
+ Py_DECREF(ts);
1293
+
1294
+ switch (node->ntype) {
1295
+ case FEE_NODE:
1296
+ name = get_name_from_fee_node(node, func_name_dict);
1297
+
1298
+ if (node->data.fee.type == PyTrace_CALL || node->data.fee.type == PyTrace_C_CALL) {
1299
+ PyDict_SetItemString(dict, "ph", ph_B);
1300
+ } else {
1301
+ PyDict_SetItemString(dict, "ph", ph_X);
1302
+ PyObject* dur = PyFloat_FromDouble(dur_ts_to_us(node->data.fee.dur));
1303
+ PyDict_SetItemString(dict, "dur", dur);
1304
+ Py_DECREF(dur);
1305
+ }
1306
+ PyDict_SetItemString(dict, "name", name);
1307
+ Py_DECREF(name);
1308
+
1309
+ PyObject* arg_dict = Py_XNewRef(node->data.fee.args);
1310
+ if (node->data.fee.retval) {
1311
+ if (!arg_dict) {
1312
+ arg_dict = PyDict_New();
1313
+ }
1314
+ PyDict_SetItemString(arg_dict, "return_value", node->data.fee.retval);
1315
+ }
1316
+ if (arg_dict) {
1317
+ PyDict_SetItemString(dict, "args", arg_dict);
1318
+ Py_DECREF(arg_dict);
1319
+ }
1320
+
1321
+ PyDict_SetItemString(dict, "cat", cat_fee);
1322
+ break;
1323
+ case INSTANT_NODE:
1324
+ PyDict_SetItemString(dict, "ph", ph_i);
1325
+ PyDict_SetItemString(dict, "cat", cat_instant);
1326
+ PyDict_SetItemString(dict, "name", node->data.instant.name);
1327
+ PyDict_SetItemString(dict, "args", node->data.instant.args);
1328
+ PyDict_SetItemString(dict, "s", node->data.instant.scope);
1329
+ break;
1330
+ case COUNTER_NODE:
1331
+ PyDict_SetItemString(dict, "ph", ph_C);
1332
+ PyDict_SetItemString(dict, "name", node->data.counter.name);
1333
+ PyDict_SetItemString(dict, "args", node->data.counter.args);
1334
+ break;
1335
+ case OBJECT_NODE:
1336
+ PyDict_SetItemString(dict, "ph", node->data.object.ph);
1337
+ PyDict_SetItemString(dict, "id", node->data.object.id);
1338
+ PyDict_SetItemString(dict, "name", node->data.object.name);
1339
+ if (!(node->data.object.args == Py_None)) {
1340
+ PyDict_SetItemString(dict, "args", node->data.object.args);
1341
+ }
1342
+ break;
1343
+ case RAW_NODE:
1344
+ // We still need to tid from node and we need the pid
1345
+ tid = PyLong_FromLong(node->tid);
1346
+
1347
+ Py_DECREF(dict);
1348
+ dict = node->data.raw;
1349
+
1350
+ PyDict_SetItemString(dict, "pid", pid);
1351
+ PyDict_SetItemString(dict, "tid", tid);
1352
+ Py_DECREF(tid);
1353
+
1354
+ Py_INCREF(dict);
1355
+ break;
1356
+ default:
1357
+ printf("Unknown Node Type!\n");
1358
+ exit(1);
1359
+ }
1360
+ clear_node(node);
1361
+ PyList_Append(lst, dict);
1362
+ Py_DECREF(dict);
1363
+ curr = curr + 1;
1364
+ if (curr == self->buffer + self->buffer_size) {
1365
+ curr = self->buffer;
1366
+ }
1367
+
1368
+ counter += 1;
1369
+ if (counter - prev_counter > 10000 && (counter - prev_counter) / ((1 + self->total_entries)/100) > 0) {
1370
+ verbose_printf(self, 1, "Loading data, %lu / %lu\r", counter, self->total_entries);
1371
+ prev_counter = counter;
1372
+ }
1373
+ }
1374
+
1375
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC)) {
1376
+ Py_ssize_t pos = 0;
1377
+ PyObject* key = NULL;
1378
+ PyObject* value = NULL;
1379
+ while (PyDict_Next(task_dict, &pos, &key, &value)) {
1380
+ PyObject* dict = PyDict_New();
1381
+ PyObject* args = PyDict_New();
1382
+ PyObject* tid = key;
1383
+ PyObject* thread_name_string = PyUnicode_FromString("thread_name");
1384
+
1385
+ PyDict_SetItemString(dict, "ph", ph_M);
1386
+ PyDict_SetItemString(dict, "pid", pid);
1387
+ PyDict_SetItemString(dict, "tid", tid);
1388
+ PyDict_SetItemString(dict, "name", thread_name_string);
1389
+ Py_DECREF(thread_name_string);
1390
+ PyDict_SetItemString(args, "name", value);
1391
+ PyDict_SetItemString(dict, "args", args);
1392
+ Py_DECREF(args);
1393
+ PyList_Append(lst, dict);
1394
+ }
1395
+ }
1396
+
1397
+ verbose_printf(self, 1, "Loading finish \n");
1398
+ Py_DECREF(pid);
1399
+ Py_DECREF(cat_fee);
1400
+ Py_DECREF(cat_instant);
1401
+ Py_DECREF(ph_B);
1402
+ Py_DECREF(ph_i);
1403
+ Py_DECREF(ph_X);
1404
+ Py_DECREF(ph_C);
1405
+ Py_DECREF(ph_M);
1406
+ Py_DECREF(func_name_dict);
1407
+ self->buffer_tail_idx = self->buffer_head_idx;
1408
+ SNAPTRACE_THREAD_PROTECT_END(self);
1409
+ return lst;
1410
+ }
1411
+
1412
+ static PyObject*
1413
+ tracer_dump(TracerObject* self, PyObject* args, PyObject* kw)
1414
+ {
1415
+ const char* filename = NULL;
1416
+ int sanitize_function_name = 0;
1417
+ static char* kwlist[] = {"filename", "sanitize_function_name", NULL};
1418
+ FILE* fptr = NULL;
1419
+
1420
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s|p", kwlist,
1421
+ &filename, &sanitize_function_name)) {
1422
+ return NULL;
1423
+ }
1424
+ fptr = fopen(filename, "w");
1425
+ if (!fptr) {
1426
+ PyErr_Format(PyExc_ValueError, "Can't open file %s to write", filename);
1427
+ return NULL;
1428
+ }
1429
+
1430
+ fprintf(fptr, "{\"traceEvents\":[");
1431
+
1432
+ SNAPTRACE_THREAD_PROTECT_START(self);
1433
+ struct EventNode* curr = self->buffer + self->buffer_head_idx;
1434
+ unsigned long pid = 0;
1435
+ uint8_t overflowed = ((self->buffer_tail_idx + 1) % self->buffer_size) == self->buffer_head_idx;
1436
+ struct MetadataNode* metadata_node = NULL;
1437
+ PyObject* task_dict = NULL;
1438
+
1439
+ if (self->fix_pid > 0) {
1440
+ pid = self->fix_pid;
1441
+ } else {
1442
+ #if _WIN32
1443
+ pid = GetCurrentProcessId();
1444
+ #else
1445
+ pid = getpid();
1446
+ #endif
1447
+ }
1448
+
1449
+ // == Load the metadata first ==
1450
+ // Process Name
1451
+ {
1452
+ PyObject* process_name = NULL;
1453
+ if (self->process_name) {
1454
+ process_name = Py_NewRef(self->process_name);
1455
+ } else {
1456
+ PyObject* current_process_method = PyObject_GetAttrString(multiprocessing_module, "current_process");
1457
+ if (!current_process_method) {
1458
+ perror("Failed to access multiprocessing.current_process()");
1459
+ exit(-1);
1460
+ }
1461
+ PyObject* current_process = PyObject_CallNoArgs(current_process_method);
1462
+ if (!current_process_method) {
1463
+ perror("Failed to access multiprocessing.current_process()");
1464
+ exit(-1);
1465
+ }
1466
+ process_name = PyObject_GetAttrString(current_process, "name");
1467
+ Py_DECREF(current_process_method);
1468
+ Py_DECREF(current_process);
1469
+ }
1470
+
1471
+ fprintf(fptr, "{\"ph\":\"M\",\"pid\":%lu,\"tid\":%lu,\"name\":\"process_name\",\"args\":{\"name\":\"",
1472
+ pid, pid);
1473
+ fprint_escape(fptr, PyUnicode_AsUTF8(process_name));
1474
+ fprintf(fptr, "\"}},");
1475
+ Py_DECREF(process_name);
1476
+ }
1477
+
1478
+ // Thread Name
1479
+ metadata_node = self->metadata_head;
1480
+ while (metadata_node) {
1481
+ fprintf(fptr, "{\"ph\":\"M\",\"pid\":%lu,\"tid\":%lu,\"name\":\"thread_name\",\"args\":{\"name\":\"",
1482
+ pid, metadata_node->tid);
1483
+ fprint_escape(fptr, PyUnicode_AsUTF8(metadata_node->name));
1484
+ fprintf(fptr, "\"}},");
1485
+ metadata_node = metadata_node->next;
1486
+ }
1487
+
1488
+ // Task Name if using LOG_ASYNC
1489
+ // We need to make up some thread id for the task
1490
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC)) {
1491
+ task_dict = PyDict_New();
1492
+ }
1493
+
1494
+ while (curr != self->buffer + self->buffer_tail_idx) {
1495
+ struct EventNode* node = curr;
1496
+ long long ts_long = system_ts_to_ns(node->ts);
1497
+ unsigned long tid = node->tid;
1498
+
1499
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC)) {
1500
+ if (curr->data.fee.asyncio_task != NULL) {
1501
+ tid = (unsigned long)(((uintptr_t)curr->data.fee.asyncio_task) & 0xffffff);
1502
+ PyObject* task_id = PyLong_FromLong(tid);
1503
+ if (!PyDict_Contains(task_dict, task_id)) {
1504
+ PyObject* task_name = NULL;
1505
+ if (PyObject_HasAttrString(curr->data.fee.asyncio_task, "get_name")) {
1506
+ PyObject* task_name_method = PyObject_GetAttrString(curr->data.fee.asyncio_task, "get_name");
1507
+ task_name = PyObject_CallNoArgs(task_name_method);
1508
+ Py_DECREF(task_name_method);
1509
+ } else if (PyObject_HasAttrString(curr->data.fee.asyncio_task, "name")) {
1510
+ task_name = PyObject_GetAttrString(curr->data.fee.asyncio_task, "name");
1511
+ } else {
1512
+ task_name = PyUnicode_FromString("Task");
1513
+ }
1514
+ PyDict_SetItem(task_dict, task_id, task_name);
1515
+ Py_DECREF(task_name);
1516
+ }
1517
+ Py_DECREF(task_id);
1518
+ }
1519
+ }
1520
+ if (node->ntype != RAW_NODE) {
1521
+ // printf("%f") is about 10x slower than print("%d")
1522
+ fprintf(fptr, "{\"pid\":%lu,\"tid\":%lu,\"ts\":%lld.%03lld,", pid, tid, ts_long / 1000, ts_long % 1000);
1523
+ }
1524
+
1525
+ switch (node->ntype) {
1526
+ case FEE_NODE:
1527
+ ;
1528
+ long long dur_long = dur_ts_to_ns(node->data.fee.dur);
1529
+ char ph = 'X';
1530
+ if (node->data.fee.type == PyTrace_CALL || node->data.fee.type == PyTrace_C_CALL) {
1531
+ ph = 'B';
1532
+ }
1533
+ fprintf(fptr, "\"ph\":\"%c\",\"cat\":\"fee\",\"dur\":%lld.%03lld,\"name\":\"", ph, dur_long / 1000, dur_long % 1000);
1534
+ fprintfeename(fptr, node, sanitize_function_name);
1535
+ fputc('\"', fptr);
1536
+
1537
+ PyObject* arg_dict = NULL;
1538
+ if (node->data.fee.args) {
1539
+ arg_dict = node->data.fee.args;
1540
+ Py_INCREF(arg_dict);
1541
+ }
1542
+ if (node->data.fee.retval) {
1543
+ if (!arg_dict) {
1544
+ arg_dict = PyDict_New();
1545
+ }
1546
+ PyDict_SetItemString(arg_dict, "return_value", node->data.fee.retval);
1547
+ }
1548
+ if (arg_dict) {
1549
+ fprintf(fptr, ",\"args\":");
1550
+ fprintjson(fptr, arg_dict);
1551
+ Py_DECREF(arg_dict);
1552
+ }
1553
+ break;
1554
+ case INSTANT_NODE:
1555
+ fprintf(fptr, "\"ph\":\"i\",\"cat\":\"instant\",\"name\":\"");
1556
+ fprint_escape(fptr, PyUnicode_AsUTF8(node->data.instant.name));
1557
+ if (node->data.instant.args == Py_None) {
1558
+ fprintf(fptr, "\",\"s\":\"%s\"", PyUnicode_AsUTF8(node->data.instant.scope));
1559
+ } else {
1560
+ fprintf(fptr, "\",\"s\":\"%s\",\"args\":", PyUnicode_AsUTF8(node->data.instant.scope));
1561
+ fprintjson(fptr, node->data.instant.args);
1562
+ }
1563
+ break;
1564
+ case COUNTER_NODE:
1565
+ fprintf(fptr, "\"ph\":\"C\",\"name\":\"");
1566
+ fprint_escape(fptr, PyUnicode_AsUTF8(node->data.counter.name));
1567
+ fprintf(fptr, "\",\"args\":");
1568
+ fprintjson(fptr, node->data.counter.args);
1569
+ break;
1570
+ case OBJECT_NODE:
1571
+ fprintf(fptr, "\"ph\":\"%s\",\"id\":\"%s\",\"name\":\"",
1572
+ PyUnicode_AsUTF8(node->data.object.ph), PyUnicode_AsUTF8(node->data.object.id));
1573
+ fprint_escape(fptr, PyUnicode_AsUTF8(node->data.object.name));
1574
+ fputc('\"', fptr);
1575
+ if (!(node->data.object.args == Py_None)) {
1576
+ fprintf(fptr, ",\"args\":");
1577
+ fprintjson(fptr, node->data.object.args);
1578
+ }
1579
+ break;
1580
+ case RAW_NODE:
1581
+ // We still need to tid from node and we need the pid
1582
+ ;
1583
+ PyObject* py_pid = PyLong_FromLong(pid);
1584
+ PyObject* py_tid = PyLong_FromLong(node->tid);
1585
+ PyObject* dict = node->data.raw;
1586
+
1587
+ PyDict_SetItemString(dict, "pid", py_pid);
1588
+ PyDict_SetItemString(dict, "tid", py_tid);
1589
+ fprintjson(fptr, dict);
1590
+ fputc(',', fptr);
1591
+ Py_DECREF(py_pid);
1592
+ Py_DECREF(py_tid);
1593
+ break;
1594
+ default:
1595
+ printf("Unknown Node Type!\n");
1596
+ exit(1);
1597
+ }
1598
+ if (node->ntype != RAW_NODE) {
1599
+ fputs("},", fptr);
1600
+ }
1601
+ clear_node(node);
1602
+ curr = curr + 1;
1603
+ if (curr == self->buffer + self->buffer_size) {
1604
+ curr = self->buffer;
1605
+ }
1606
+ }
1607
+
1608
+ if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_ASYNC)) {
1609
+ Py_ssize_t pos = 0;
1610
+ PyObject* key = NULL;
1611
+ PyObject* value = NULL;
1612
+ while (PyDict_Next(task_dict, &pos, &key, &value)) {
1613
+ PyObject* tid_repr = PyObject_Repr(key);
1614
+ fprintf(fptr, "{\"ph\":\"M\",\"pid\":%lu,\"tid\":%s,\"name\":\"thread_name\",\"args\":{\"name\":\"%s\"}},",
1615
+ pid, PyUnicode_AsUTF8(tid_repr), PyUnicode_AsUTF8(value));
1616
+ Py_DECREF(tid_repr);
1617
+ }
1618
+ Py_DECREF(task_dict);
1619
+ }
1620
+
1621
+ self->buffer_tail_idx = self->buffer_head_idx;
1622
+ fseek(fptr, -1, SEEK_CUR);
1623
+ fprintf(fptr, "], \"viztracer_metadata\": {\"overflow\":%s", overflowed? "true": "false");
1624
+
1625
+ if (self->sync_marker > 0)
1626
+ {
1627
+ long long ts_sync_marker = system_ts_to_ns(self->sync_marker);
1628
+ fprintf(fptr, ",\"sync_marker\":%lld.%03lld", ts_sync_marker / 1000, ts_sync_marker % 1000);
1629
+ }
1630
+
1631
+ fprintf(fptr, "}}");
1632
+ fclose(fptr);
1633
+ SNAPTRACE_THREAD_PROTECT_END(self);
1634
+ Py_RETURN_NONE;
1635
+ }
1636
+
1637
+ static PyObject*
1638
+ tracer_clear(TracerObject* self, PyObject* Py_UNUSED(unused))
1639
+ {
1640
+ struct EventNode* curr = self->buffer + self->buffer_head_idx;
1641
+ while (curr != self->buffer + self->buffer_tail_idx) {
1642
+ struct EventNode* node = curr;
1643
+ clear_node(node);
1644
+ curr = curr + 1;
1645
+ if (curr == self->buffer + self->buffer_size) {
1646
+ curr = self->buffer;
1647
+ }
1648
+ }
1649
+ self->buffer_tail_idx = self->buffer_head_idx;
1650
+
1651
+ Py_RETURN_NONE;
1652
+ }
1653
+
1654
+ static PyObject*
1655
+ tracer_setpid(TracerObject* self, PyObject* args)
1656
+ {
1657
+ long input_pid = -1;
1658
+ if (!PyArg_ParseTuple(args, "|l", &input_pid)) {
1659
+ printf("Parsing error on setpid\n");
1660
+ }
1661
+
1662
+ if (input_pid >= 0) {
1663
+ self->fix_pid = input_pid;
1664
+ } else {
1665
+ #if _WIN32
1666
+ self->fix_pid = GetCurrentProcessId();
1667
+ #else
1668
+ self->fix_pid = getpid();
1669
+ #endif
1670
+ }
1671
+
1672
+ Py_RETURN_NONE;
1673
+ }
1674
+
1675
+ static PyObject*
1676
+ tracer_getts(TracerObject* self, PyObject* Py_UNUSED(unused))
1677
+ {
1678
+ int64_t ts = get_ts();
1679
+ double us = system_ts_to_us(ts);
1680
+
1681
+ return PyFloat_FromDouble(us);
1682
+ }
1683
+
1684
+ static PyObject*
1685
+ tracer_getbasetime(TracerObject* self, PyObject* Py_UNUSED(unused))
1686
+ {
1687
+ return PyLong_FromLongLong(get_base_time_ns());
1688
+ }
1689
+
1690
+ static PyObject*
1691
+ tracer_resetstack(TracerObject* self, PyObject* Py_UNUSED(unused))
1692
+ {
1693
+ struct ThreadInfo* info = get_thread_info(self);
1694
+ if (!info) {
1695
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1696
+ return NULL;
1697
+ }
1698
+
1699
+ info->curr_stack_depth = 0;
1700
+ info->ignore_stack_depth = 0;
1701
+
1702
+ struct FunctionNode* stack_top = info->stack_top;
1703
+ clear_stack(&stack_top);
1704
+ info->stack_top = stack_top;
1705
+
1706
+ Py_RETURN_NONE;
1707
+ }
1708
+
1709
+ static PyObject*
1710
+ tracer_addinstant(TracerObject* self, PyObject* args, PyObject* kw)
1711
+ {
1712
+ PyObject* name = NULL;
1713
+ PyObject* instant_args = NULL;
1714
+ PyObject* scope = NULL;
1715
+ struct EventNode* node = NULL;
1716
+ static char* kwlist[] = {"name", "args", "scope", NULL};
1717
+ const char* allowed_scope[] = {"g", "p", "t"};
1718
+
1719
+ if (!self->collecting) {
1720
+ Py_RETURN_NONE;
1721
+ }
1722
+
1723
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "O|OO", kwlist,
1724
+ &name, &instant_args, &scope)) {
1725
+ return NULL;
1726
+ }
1727
+
1728
+ struct ThreadInfo* info = get_thread_info(self);
1729
+ if (!info) {
1730
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1731
+ return NULL;
1732
+ }
1733
+
1734
+ if (!instant_args) {
1735
+ instant_args = Py_None;
1736
+ }
1737
+
1738
+ if (!scope) {
1739
+ scope = PyUnicode_FromString("g");
1740
+ } else {
1741
+ if (!PyUnicode_CheckExact(scope)) {
1742
+ PyErr_SetString(PyExc_TypeError, "Scope must be a string");
1743
+ return NULL;
1744
+ }
1745
+ for (int i = 0; i < 3; i++) {
1746
+ if (strcmp(PyUnicode_AsUTF8(scope), allowed_scope[i]) == 0) {
1747
+ break;
1748
+ }
1749
+ if (i == 2) {
1750
+ PyErr_SetString(PyExc_ValueError, "Scope must be one of 'g', 'p', 't'");
1751
+ return NULL;
1752
+ }
1753
+ }
1754
+ Py_INCREF(scope);
1755
+ }
1756
+
1757
+ node = get_next_node(self);
1758
+ node->ntype = INSTANT_NODE;
1759
+ node->tid = info->tid;
1760
+ node->ts = get_ts();
1761
+ node->data.instant.name = Py_NewRef(name);
1762
+ node->data.instant.args = Py_NewRef(instant_args);
1763
+ node->data.instant.scope = scope;
1764
+
1765
+ Py_RETURN_NONE;
1766
+ }
1767
+
1768
+ static PyObject*
1769
+ tracer_addfunctionarg(TracerObject* self, PyObject* args, PyObject* kw)
1770
+ {
1771
+ PyObject* key = NULL;
1772
+ PyObject* value = NULL;
1773
+ static char* kwlist[] = {"key", "value", NULL};
1774
+
1775
+ if (!self->collecting) {
1776
+ Py_RETURN_NONE;
1777
+ }
1778
+
1779
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "OO", kwlist, &key, &value)) {
1780
+ return NULL;
1781
+ }
1782
+
1783
+ struct ThreadInfo* info = get_thread_info(self);
1784
+ if (!info) {
1785
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1786
+ return NULL;
1787
+ }
1788
+
1789
+ struct FunctionNode* fnode = info->stack_top;
1790
+ if (!fnode->args) {
1791
+ fnode->args = PyDict_New();
1792
+ }
1793
+
1794
+ PyDict_SetItem(fnode->args, key, value);
1795
+
1796
+ Py_RETURN_NONE;
1797
+ }
1798
+
1799
+ static PyObject*
1800
+ tracer_addcounter(TracerObject* self, PyObject* args, PyObject* kw)
1801
+ {
1802
+ PyObject* name = NULL;
1803
+ PyObject* counter_args = NULL;
1804
+ static char* kwlist[] = {"name", "args", NULL};
1805
+ struct EventNode* node = NULL;
1806
+
1807
+ if (!self->collecting) {
1808
+ Py_RETURN_NONE;
1809
+ }
1810
+
1811
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "OO", kwlist,
1812
+ &name, &counter_args)) {
1813
+ return NULL;
1814
+ }
1815
+
1816
+ struct ThreadInfo* info = get_thread_info(self);
1817
+
1818
+ if (!info) {
1819
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1820
+ return NULL;
1821
+ }
1822
+
1823
+ node = get_next_node(self);
1824
+ node->ntype = COUNTER_NODE;
1825
+ node->tid = info->tid;
1826
+ node->ts = get_ts();
1827
+ node->data.counter.name = Py_NewRef(name);
1828
+ node->data.counter.args = Py_NewRef(counter_args);
1829
+
1830
+ Py_RETURN_NONE;
1831
+ }
1832
+
1833
+ static PyObject*
1834
+ tracer_addobject(TracerObject* self, PyObject* args, PyObject* kw)
1835
+ {
1836
+ PyObject* ph = NULL;
1837
+ PyObject* id = NULL;
1838
+ PyObject* name = NULL;
1839
+ PyObject* object_args = NULL;
1840
+ static char* kwlist[] = {"ph", "obj_id", "name", "args", NULL};
1841
+ struct EventNode* node = NULL;
1842
+
1843
+ if (!self->collecting) {
1844
+ Py_RETURN_NONE;
1845
+ }
1846
+
1847
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "OOO|O", kwlist,
1848
+ &ph, &id, &name, &object_args)) {
1849
+ return NULL;
1850
+ }
1851
+
1852
+ struct ThreadInfo* info = get_thread_info(self);
1853
+
1854
+ if (!info) {
1855
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1856
+ return NULL;
1857
+ }
1858
+
1859
+ if (!object_args) {
1860
+ object_args = Py_None;
1861
+ }
1862
+
1863
+ node = get_next_node(self);
1864
+ node->ntype = OBJECT_NODE;
1865
+ node->tid = info->tid;
1866
+ node->ts = get_ts();
1867
+ node->data.object.ph = Py_NewRef(ph);
1868
+ node->data.object.id = Py_NewRef(id);
1869
+ node->data.object.name = Py_NewRef(name);
1870
+ node->data.object.args = Py_NewRef(object_args);
1871
+
1872
+ Py_RETURN_NONE;
1873
+ }
1874
+
1875
+ static PyObject*
1876
+ tracer_addraw(TracerObject* self, PyObject* args, PyObject* kw)
1877
+ {
1878
+ PyObject* raw = NULL;
1879
+ static char* kwlist[] = {"raw", NULL};
1880
+ struct EventNode* node = NULL;
1881
+
1882
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "O", kwlist, &raw)) {
1883
+ return NULL;
1884
+ }
1885
+
1886
+ struct ThreadInfo* info = get_thread_info(self);
1887
+
1888
+ if (!info) {
1889
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1890
+ return NULL;
1891
+ }
1892
+
1893
+ node = get_next_node(self);
1894
+ node->tid = info->tid;
1895
+ node->ntype = RAW_NODE;
1896
+ node->data.raw = Py_NewRef(raw);
1897
+
1898
+ Py_RETURN_NONE;
1899
+ }
1900
+
1901
+ static PyObject*
1902
+ tracer_setignorestackcounter(TracerObject* self, PyObject* value)
1903
+ {
1904
+ int current_value = 0;
1905
+
1906
+ if (!PyLong_Check(value)) {
1907
+ PyErr_SetString(PyExc_TypeError, "value must be an integer");
1908
+ return NULL;
1909
+ }
1910
+
1911
+ struct ThreadInfo* info = get_thread_info(self);
1912
+
1913
+ if (!info) {
1914
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1915
+ return NULL;
1916
+ }
1917
+
1918
+ current_value = info->ignore_stack_depth;
1919
+ // +1 to compensate for this call so when it returns, the value is correct
1920
+ info->ignore_stack_depth = PyLong_AsLong(value) + 1;
1921
+
1922
+ // -1 is the actual ignore stack depth before this call
1923
+ return Py_BuildValue("i", current_value - 1);
1924
+ }
1925
+
1926
+ static PyObject*
1927
+ tracer_getfunctionarg(TracerObject* self, PyObject* Py_UNUSED(unused))
1928
+ {
1929
+ struct ThreadInfo* info = get_thread_info(self);
1930
+
1931
+ if (!info) {
1932
+ PyErr_SetString(PyExc_RuntimeError, "VizTracer: Failed to get thread info. This should not happen.");
1933
+ return NULL;
1934
+ }
1935
+
1936
+ struct FunctionNode* fnode = info->stack_top;
1937
+ if (!fnode->args) {
1938
+ Py_RETURN_NONE;
1939
+ }
1940
+
1941
+ return Py_NewRef(fnode->args);
1942
+ }
1943
+
1944
+ static PyObject*
1945
+ tracer_set_sync_marker(TracerObject* self, PyObject* Py_UNUSED(unused))
1946
+ {
1947
+ if (self->sync_marker != 0)
1948
+ {
1949
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "Synchronization marker already set");
1950
+ }
1951
+ self->sync_marker = get_ts();
1952
+ Py_RETURN_NONE;
1953
+ }
1954
+
1955
+ static PyObject*
1956
+ tracer_get_sync_marker(TracerObject* self, PyObject* Py_UNUSED(unused))
1957
+ {
1958
+ if (self->sync_marker == 0)
1959
+ {
1960
+ Py_RETURN_NONE;
1961
+ }
1962
+
1963
+ double ts_sync_marker = system_ts_to_us(self->sync_marker);
1964
+ return PyFloat_FromDouble(ts_sync_marker);
1965
+ }
1966
+
1967
+ static PyMethodDef Tracer_methods[] = {
1968
+ {"threadtracefunc", (PyCFunction)tracer_threadtracefunc, METH_VARARGS, "trace function"},
1969
+ {"start", (PyCFunction)tracer_start, METH_NOARGS, "start profiling"},
1970
+ {"stop", (PyCFunction)tracer_stop, METH_O, "stop profiling"},
1971
+ {"load", (PyCFunction)tracer_load, METH_NOARGS, "load buffer"},
1972
+ {"dump", (PyCFunction)tracer_dump, METH_VARARGS|METH_KEYWORDS, "dump buffer to file"},
1973
+ {"clear", (PyCFunction)tracer_clear, METH_NOARGS, "clear buffer"},
1974
+ {"setpid", (PyCFunction)tracer_setpid, METH_VARARGS, "set fixed pid"},
1975
+ {"add_instant", (PyCFunction)tracer_addinstant, METH_VARARGS|METH_KEYWORDS, "add instant event"},
1976
+ {"add_counter", (PyCFunction)tracer_addcounter, METH_VARARGS|METH_KEYWORDS, "add counter event"},
1977
+ {"add_object", (PyCFunction)tracer_addobject, METH_VARARGS|METH_KEYWORDS, "add object event"},
1978
+ {"add_raw", (PyCFunction)tracer_addraw, METH_VARARGS|METH_KEYWORDS, "add raw event"},
1979
+ {"add_func_args", (PyCFunction)tracer_addfunctionarg, METH_VARARGS|METH_KEYWORDS, "add function arg"},
1980
+ {"get_func_args", (PyCFunction)tracer_getfunctionarg, METH_NOARGS, "get current function arg"},
1981
+ {"getts", (PyCFunction)tracer_getts, METH_NOARGS, "get timestamp"},
1982
+ {"get_base_time", (PyCFunction)tracer_getbasetime, METH_NOARGS, "get base time in nanoseconds"},
1983
+ {"reset_stack", (PyCFunction)tracer_resetstack, METH_NOARGS, "reset stack"},
1984
+ {"pause", (PyCFunction)tracer_pause, METH_NOARGS, "pause profiling"},
1985
+ {"resume", (PyCFunction)tracer_resume, METH_NOARGS, "resume profiling"},
1986
+ {"setignorestackcounter", (PyCFunction)tracer_setignorestackcounter, METH_O, "reset ignore stack depth"},
1987
+ {"set_sync_marker", (PyCFunction)tracer_set_sync_marker, METH_NOARGS, "set current timestamp to synchronization marker"},
1988
+ {"get_sync_marker", (PyCFunction)tracer_get_sync_marker, METH_NOARGS, "get synchronization marker or None if not set"},
1989
+ {NULL, NULL, 0, NULL}
1990
+ };
1991
+
1992
+ // ===========================================================================
1993
+ // snaptrace.Tracer internals
1994
+ // ===========================================================================
1995
+
1996
+ static PyObject*
1997
+ Tracer_New(PyTypeObject* type, PyObject* args, PyObject* kwargs)
1998
+ {
1999
+ TracerObject* self = (TracerObject*) type->tp_alloc(type, 0);
2000
+ if (self) {
2001
+ self->collecting = 0;
2002
+ self->fix_pid = 0;
2003
+ self->total_entries = 0;
2004
+ self->check_flags = 0;
2005
+ self->verbose = 0;
2006
+ self->lib_file_path = NULL;
2007
+ self->max_stack_depth = 0;
2008
+ self->include_files = NULL;
2009
+ self->exclude_files = NULL;
2010
+ self->min_duration = 0;
2011
+ self->buffer = NULL;
2012
+ self->buffer_head_idx = 0;
2013
+ self->buffer_tail_idx = 0;
2014
+ self->sync_marker = 0;
2015
+ self->metadata_head = NULL;
2016
+ }
2017
+
2018
+ return (PyObject*) self;
2019
+ }
2020
+
2021
+ static int
2022
+ Tracer_Init(TracerObject* self, PyObject* args, PyObject* kwargs)
2023
+ {
2024
+ if (!PyArg_ParseTuple(args, "l", &self->buffer_size)) {
2025
+ PyErr_SetString(PyExc_TypeError, "You need to specify buffer size when initializing Tracer");
2026
+ return -1;
2027
+ }
2028
+
2029
+ // We need an extra slot for circular buffer
2030
+ self->buffer_size += 1;
2031
+ self->buffer = (struct EventNode*) PyMem_Calloc(self->buffer_size, sizeof(struct EventNode));
2032
+ if (!self->buffer) {
2033
+ PyErr_NoMemory();
2034
+ return -1;
2035
+ }
2036
+
2037
+ #if _WIN32
2038
+ if ((self->dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) {
2039
+ printf("Error on TLS!\n");
2040
+ exit(-1);
2041
+ }
2042
+ #else
2043
+ if (pthread_key_create(&self->thread_key, snaptrace_threaddestructor)) {
2044
+ perror("Failed to create Tss_Key");
2045
+ exit(-1);
2046
+ }
2047
+ #endif
2048
+
2049
+ #if PY_VERSION_HEX >= 0x030C0000
2050
+ #else
2051
+ // Python: threading.setprofile(tracefunc)
2052
+ {
2053
+ PyObject* handler = PyCFunction_New(&Tracer_methods[0], (PyObject*)self);
2054
+
2055
+ if (PyObject_CallMethod(threading_module, "setprofile", "N", handler) == NULL) {
2056
+ perror("Failed to call threading.setprofile() properly");
2057
+ exit(-1);
2058
+ }
2059
+ }
2060
+ #endif
2061
+
2062
+ #if PY_VERSION_HEX >= 0x030C0000
2063
+ #else
2064
+ PyEval_SetProfile(tracer_tracefunc, (PyObject*)self);
2065
+ #endif
2066
+
2067
+ return 0;
2068
+ }
2069
+
2070
+ static void
2071
+ Tracer_dealloc(TracerObject* self)
2072
+ {
2073
+ tracer_clear(self, NULL);
2074
+ if (self->lib_file_path) {
2075
+ PyMem_FREE(self->lib_file_path);
2076
+ }
2077
+ Py_XDECREF(self->include_files);
2078
+ Py_XDECREF(self->exclude_files);
2079
+ PyMem_FREE(self->buffer);
2080
+
2081
+ struct MetadataNode* node = self->metadata_head;
2082
+ struct MetadataNode* prev = NULL;
2083
+ while (node) {
2084
+ prev = node;
2085
+ Py_CLEAR(node->name);
2086
+ node = node->next;
2087
+ PyMem_FREE(prev);
2088
+ }
2089
+
2090
+ #if PY_VERSION_HEX >= 0x030C0000
2091
+ #else
2092
+ // threading.setprofile(None)
2093
+ // It's possible that during deallocation phase threading module has released setprofile
2094
+ // and we should be okay with that.
2095
+ PyObject* result = PyObject_CallMethod(threading_module, "setprofile", "O", Py_None);
2096
+ if (result != NULL) {
2097
+ Py_DECREF(result);
2098
+ }
2099
+ #endif
2100
+
2101
+ Py_TYPE(self)->tp_free((PyObject*) self);
2102
+ }
2103
+
2104
+ // ================================================================
2105
+ // snaptrace.Tracer Definition
2106
+ // ================================================================
2107
+
2108
+ // We define getsetters in another file
2109
+ extern PyGetSetDef Tracer_getsetters[];
2110
+
2111
+ static PyTypeObject TracerType = {
2112
+ PyVarObject_HEAD_INIT(NULL, 0)
2113
+ .tp_name = "snaptrace.Tracer",
2114
+ .tp_doc = "Tracer",
2115
+ .tp_basicsize = sizeof(TracerObject),
2116
+ .tp_itemsize = 0,
2117
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
2118
+ .tp_new = Tracer_New,
2119
+ .tp_init = (initproc) Tracer_Init,
2120
+ .tp_dealloc = (destructor) Tracer_dealloc,
2121
+ .tp_methods = Tracer_methods,
2122
+ .tp_getset = Tracer_getsetters,
2123
+ };
2124
+
2125
+ // ================================================================
2126
+ // snaptrace Module Functions
2127
+ // ================================================================
2128
+
2129
+ void
2130
+ snaptrace_free(void* Py_UNUSED(unused)) {
2131
+ quicktime_free();
2132
+ Py_CLEAR(threading_module);
2133
+ Py_CLEAR(multiprocessing_module);
2134
+ Py_CLEAR(asyncio_module);
2135
+ Py_CLEAR(asyncio_tasks_module);
2136
+ Py_CLEAR(curr_task_getters[0]);
2137
+ Py_CLEAR(trio_lowlevel_module);
2138
+ Py_CLEAR(curr_task_getters[1]);
2139
+ Py_CLEAR(json_module);
2140
+ Py_CLEAR(sys_module);
2141
+ }
2142
+
2143
+ // ================================================================
2144
+ // snaptrace Module Definition
2145
+ // ================================================================
2146
+
2147
+ static struct PyModuleDef snaptracemodule = {
2148
+ .m_base = PyModuleDef_HEAD_INIT,
2149
+ .m_name = "viztracer.snaptrace",
2150
+ .m_size = -1,
2151
+ .m_free = snaptrace_free,
2152
+ };
2153
+
2154
+ // ================================================================
2155
+ // Python Interface
2156
+ // ================================================================
2157
+
2158
+ PyMODINIT_FUNC
2159
+ PyInit_snaptrace(void)
2160
+ {
2161
+ // Tracer Module
2162
+ PyObject* m = NULL;
2163
+
2164
+ if (PyType_Ready(&TracerType) < 0) {
2165
+ return NULL;
2166
+ }
2167
+
2168
+ m = PyModule_Create(&snaptracemodule);
2169
+
2170
+ if (!m) {
2171
+ return NULL;
2172
+ }
2173
+
2174
+ #ifdef Py_GIL_DISABLED
2175
+ PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
2176
+ #endif
2177
+
2178
+ Py_INCREF(&TracerType);
2179
+ if (PyModule_AddObject(m, "Tracer", (PyObject*) &TracerType) < 0) {
2180
+ Py_DECREF(&TracerType);
2181
+ Py_DECREF(m);
2182
+ return NULL;
2183
+ }
2184
+
2185
+ threading_module = PyImport_ImportModule("threading");
2186
+ multiprocessing_module = PyImport_ImportModule("multiprocessing");
2187
+ if ((trio_module = PyImport_ImportModule("trio"))) {
2188
+ trio_lowlevel_module = PyImport_AddModule("trio.lowlevel");
2189
+ curr_task_getters[1] = PyObject_GetAttrString(trio_lowlevel_module, "current_task");
2190
+ } else {
2191
+ PyErr_Clear();
2192
+ }
2193
+ json_module = PyImport_ImportModule("json");
2194
+
2195
+ #if PY_VERSION_HEX >= 0x030C0000
2196
+ sys_module = PyImport_ImportModule("sys");
2197
+ PyObject* monitoring = PyObject_GetAttrString(sys_module, "monitoring");
2198
+ sys_monitoring_missing = PyObject_GetAttrString(monitoring, "MISSING");
2199
+ Py_DECREF(monitoring);
2200
+ #endif
2201
+
2202
+ quicktime_init();
2203
+
2204
+ return m;
2205
+ }