viztracer 1.1.0__cp314-cp314-win32.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.
- viztracer/__init__.py +19 -0
- viztracer/__main__.py +8 -0
- viztracer/attach.py +67 -0
- viztracer/attach_process/LICENSE +203 -0
- viztracer/attach_process/__init__.py +0 -0
- viztracer/attach_process/add_code_to_python_process.py +582 -0
- viztracer/attach_process/attach_x86.dll +0 -0
- viztracer/attach_process/inject_dll_amd64.exe +0 -0
- viztracer/attach_process/linux_and_mac/lldb_prepare.py +54 -0
- viztracer/attach_process/run_code_on_dllmain_amd64.dll +0 -0
- viztracer/attach_process/run_code_on_dllmain_x86.dll +0 -0
- viztracer/cellmagic.py +70 -0
- viztracer/code_monkey.py +353 -0
- viztracer/decorator.py +164 -0
- viztracer/event_base.py +81 -0
- viztracer/functree.py +135 -0
- viztracer/html/flamegraph.html +34 -0
- viztracer/html/trace_viewer_embedder.html +203 -0
- viztracer/html/trace_viewer_full.html +10207 -0
- viztracer/main.py +699 -0
- viztracer/modules/eventnode.c +172 -0
- viztracer/modules/eventnode.h +73 -0
- viztracer/modules/pythoncapi_compat.h +1726 -0
- viztracer/modules/quicktime.c +177 -0
- viztracer/modules/quicktime.h +104 -0
- viztracer/modules/snaptrace.c +2205 -0
- viztracer/modules/snaptrace.h +134 -0
- viztracer/modules/snaptrace_member.c +483 -0
- viztracer/modules/util.c +45 -0
- viztracer/modules/util.h +22 -0
- viztracer/modules/vcompressor/vc_dump.c +1131 -0
- viztracer/modules/vcompressor/vc_dump.h +49 -0
- viztracer/modules/vcompressor/vcompressor.c +396 -0
- viztracer/modules/vcompressor/vcompressor.h +15 -0
- viztracer/patch.py +307 -0
- viztracer/report_builder.py +311 -0
- viztracer/snaptrace.cp314-win32.pyd +0 -0
- viztracer/snaptrace.pyi +77 -0
- viztracer/util.py +196 -0
- viztracer/vcompressor.cp314-win32.pyd +0 -0
- viztracer/vcompressor.pyi +10 -0
- viztracer/viewer.py +528 -0
- viztracer/vizcounter.py +20 -0
- viztracer/vizevent.py +31 -0
- viztracer/vizlogging.py +20 -0
- viztracer/vizobject.py +28 -0
- viztracer/vizplugin.py +143 -0
- viztracer/viztracer.py +472 -0
- viztracer/web_dist/LICENSE +189 -0
- viztracer/web_dist/index.html +127 -0
- viztracer/web_dist/service_worker.js +279 -0
- viztracer/web_dist/trace_processor +300 -0
- viztracer/web_dist/v52.0-6b9586def/assets/MaterialSymbolsOutlined.woff2 +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/Roboto-100.woff2 +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/Roboto-300.woff2 +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/Roboto-400.woff2 +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/Roboto-500.woff2 +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/RobotoCondensed-Light.woff2 +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/RobotoCondensed-Regular.woff2 +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/RobotoMono-Regular.woff2 +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/brand.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/catapult_trace_viewer.html +3946 -0
- viztracer/web_dist/v52.0-6b9586def/assets/catapult_trace_viewer.js +7539 -0
- viztracer/web_dist/v52.0-6b9586def/assets/favicon.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/logo-128.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/logo-3d.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_atrace.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_battery_counters.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_board_voltage.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_cpu_coarse.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_cpu_fine.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_cpu_freq.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_cpu_voltage.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_frame_timeline.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_ftrace.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_gpu_mem_total.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_java_heap_dump.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_lmk.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_logcat.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_long_trace.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_mem_hifreq.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_meminfo.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_native_heap_profiler.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_one_shot.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_profiling.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_ps_stats.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_ring_buf.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_syscalls.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/rec_vmstat.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/scheduling_latency.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/assets/vscode-icon.png +0 -0
- viztracer/web_dist/v52.0-6b9586def/engine_bundle.js +3 -0
- viztracer/web_dist/v52.0-6b9586def/frontend_bundle.js +5495 -0
- viztracer/web_dist/v52.0-6b9586def/index.html +127 -0
- viztracer/web_dist/v52.0-6b9586def/manifest.json +52 -0
- viztracer/web_dist/v52.0-6b9586def/perfetto.css +5737 -0
- viztracer/web_dist/v52.0-6b9586def/stdlib_docs.json +1 -0
- viztracer/web_dist/v52.0-6b9586def/trace_config_utils.wasm +0 -0
- viztracer/web_dist/v52.0-6b9586def/trace_processor.wasm +0 -0
- viztracer/web_dist/v52.0-6b9586def/trace_processor_memory64.wasm +0 -0
- viztracer/web_dist/v52.0-6b9586def/traceconv.wasm +0 -0
- viztracer/web_dist/v52.0-6b9586def/traceconv_bundle.js +2 -0
- viztracer-1.1.0.dist-info/METADATA +316 -0
- viztracer-1.1.0.dist-info/RECORD +109 -0
- viztracer-1.1.0.dist-info/WHEEL +5 -0
- viztracer-1.1.0.dist-info/entry_points.txt +3 -0
- viztracer-1.1.0.dist-info/licenses/LICENSE +222 -0
- viztracer-1.1.0.dist-info/licenses/NOTICE.txt +27 -0
- viztracer-1.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1131 @@
|
|
|
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
|
+
#include <Python.h>
|
|
5
|
+
#include "vcompressor.h"
|
|
6
|
+
#include "vc_dump.h"
|
|
7
|
+
|
|
8
|
+
#define STRING_BUFFER_SIZE 512
|
|
9
|
+
|
|
10
|
+
// Not sure if we need to compress the whole file using zlib.
|
|
11
|
+
// Use this flag to control if we need to compress json data.
|
|
12
|
+
// This is for test for now
|
|
13
|
+
#define NEED_COMPRESS_IN_FILE 1
|
|
14
|
+
|
|
15
|
+
#define READ_DATA(ptr, type, fptr) \
|
|
16
|
+
{ \
|
|
17
|
+
size_t s = fread(ptr, sizeof(type), 1, fptr); \
|
|
18
|
+
if (s != 1) { \
|
|
19
|
+
PyErr_SetString(PyExc_ValueError, "file is corrupted"); \
|
|
20
|
+
goto clean_exit; \
|
|
21
|
+
} \
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#define PyDict_SetItemStringULL(dct, key, val) \
|
|
25
|
+
{ \
|
|
26
|
+
PyObject* o = PyLong_FromUnsignedLongLong(val); \
|
|
27
|
+
PyDict_SetItemString(dct, key, o); \
|
|
28
|
+
Py_DECREF(o); \
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#define PyDict_SetItemStringDouble(dct, key, val) \
|
|
32
|
+
{ \
|
|
33
|
+
PyObject* o = PyFloat_FromDouble(val); \
|
|
34
|
+
PyDict_SetItemString(dct, key, o); \
|
|
35
|
+
Py_DECREF(o); \
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// The input line has to be null-terminated
|
|
39
|
+
// This function will write the line and append a null terminator after it
|
|
40
|
+
#define fwritestr(line, fptr) \
|
|
41
|
+
{ \
|
|
42
|
+
fwrite(line, sizeof(char), strlen(line), fptr); \
|
|
43
|
+
fputc('\0', fptr); \
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#define PEEK_DATA(ptr, type, fptr) \
|
|
47
|
+
{ \
|
|
48
|
+
size_t s = fread(ptr, sizeof(type), 1, fptr); \
|
|
49
|
+
if (s != 1) { \
|
|
50
|
+
PyErr_SetString(PyExc_ValueError, "file is corrupted"); \
|
|
51
|
+
goto clean_exit; \
|
|
52
|
+
} else { \
|
|
53
|
+
fseek(fptr, -sizeof(type), SEEK_CUR); \
|
|
54
|
+
} \
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* The write_encoded_int and read_encoded_int functions are used to compress
|
|
59
|
+
* and decompress timestamp. For a trace file, most of the data are ts and dur.
|
|
60
|
+
* So we sort the timestamp and only record diff of two between two contiguous
|
|
61
|
+
* timestamp.
|
|
62
|
+
*
|
|
63
|
+
* We use a 2-bit flag to indicate how many bytes can uint64_t data be fit in.
|
|
64
|
+
* For Windows, Mac and most of the Linux, the byte order is little-endian,
|
|
65
|
+
* which means if we want to record 0X12345678. The data in file would be:
|
|
66
|
+
* 78 56 34 12
|
|
67
|
+
* To make it simple, we just move the data left 2 bits and put the flag at
|
|
68
|
+
* the lowest position of the timestamp.
|
|
69
|
+
*
|
|
70
|
+
* And the parsed timestamp is always less than 0x3FFFFFFFFFFFFFFF
|
|
71
|
+
*/
|
|
72
|
+
static inline void
|
|
73
|
+
write_encoded_int(uint64_t num, FILE* fptr)
|
|
74
|
+
{
|
|
75
|
+
if (num == (num & 0x3F)) {
|
|
76
|
+
uint8_t encoded_num = (num << 2) | TS_6_BIT;
|
|
77
|
+
fwrite(&encoded_num, sizeof(uint8_t), 1, fptr);
|
|
78
|
+
} else if (num == (num & 0x3FFF)) {
|
|
79
|
+
uint16_t encoded_num = (num << 2) | TS_14_BIT;
|
|
80
|
+
fwrite(&encoded_num, sizeof(uint16_t), 1, fptr);
|
|
81
|
+
} else if (num == (num & 0x3FFFFFFF)) {
|
|
82
|
+
uint32_t encoded_num = (num << 2) | TS_30_BIT;
|
|
83
|
+
fwrite(&encoded_num, sizeof(uint32_t), 1, fptr);
|
|
84
|
+
} else {
|
|
85
|
+
uint64_t encoded_num = (num << 2) | TS_62_BIT;
|
|
86
|
+
fwrite(&encoded_num, sizeof(uint64_t), 1, fptr);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static inline int
|
|
91
|
+
read_encoded_int(uint64_t *num, FILE* fptr)
|
|
92
|
+
{
|
|
93
|
+
uint8_t flag;
|
|
94
|
+
uint8_t encoded_num_8_bit = 0;
|
|
95
|
+
uint16_t encoded_num_16_bit = 0;
|
|
96
|
+
uint32_t encoded_num_32_bit = 0;
|
|
97
|
+
uint64_t encoded_num_64_bit = 0;
|
|
98
|
+
PEEK_DATA(&flag, uint8_t, fptr)
|
|
99
|
+
switch (flag & 0x03)
|
|
100
|
+
{
|
|
101
|
+
case TS_6_BIT:
|
|
102
|
+
READ_DATA(&encoded_num_8_bit, uint8_t, fptr)
|
|
103
|
+
(*num) = encoded_num_8_bit >> 2;
|
|
104
|
+
break;
|
|
105
|
+
case TS_14_BIT:
|
|
106
|
+
READ_DATA(&encoded_num_16_bit, uint16_t, fptr)
|
|
107
|
+
(*num) = encoded_num_16_bit >> 2;
|
|
108
|
+
break;
|
|
109
|
+
case TS_30_BIT:
|
|
110
|
+
READ_DATA(&encoded_num_32_bit, uint32_t, fptr)
|
|
111
|
+
(*num) = encoded_num_32_bit >> 2;
|
|
112
|
+
break;
|
|
113
|
+
case TS_62_BIT:
|
|
114
|
+
READ_DATA(&encoded_num_64_bit, uint64_t, fptr)
|
|
115
|
+
(*num) = encoded_num_64_bit >> 2;
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
clean_exit:
|
|
121
|
+
if (PyErr_Occurred()) {
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
int
|
|
129
|
+
freadstrn(char* buffer, int n, FILE* fptr)
|
|
130
|
+
{
|
|
131
|
+
int c;
|
|
132
|
+
int idx = 0;
|
|
133
|
+
while (1) {
|
|
134
|
+
c = fgetc(fptr);
|
|
135
|
+
if (c == EOF || c == '\0') {
|
|
136
|
+
buffer[idx++] = '\0';
|
|
137
|
+
break;
|
|
138
|
+
} else {
|
|
139
|
+
buffer[idx++] = c;
|
|
140
|
+
}
|
|
141
|
+
if (idx == n) {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return idx;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
char*
|
|
149
|
+
freadstr(FILE* fptr)
|
|
150
|
+
{
|
|
151
|
+
char* str = NULL;
|
|
152
|
+
int c = 0;
|
|
153
|
+
size_t len = 256;
|
|
154
|
+
size_t idx = 0;
|
|
155
|
+
str = malloc(len * sizeof(char));
|
|
156
|
+
if (str == NULL) {
|
|
157
|
+
return NULL;
|
|
158
|
+
}
|
|
159
|
+
while ((c = fgetc(fptr)) != EOF && c != '\0') {
|
|
160
|
+
str[idx++] = c;
|
|
161
|
+
if (idx == len) {
|
|
162
|
+
len *= 2;
|
|
163
|
+
str = realloc(str, len * sizeof(char));
|
|
164
|
+
if (str == NULL) {
|
|
165
|
+
return NULL;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
str[idx++] = '\0';
|
|
170
|
+
return str;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
int
|
|
174
|
+
dump_metadata(FILE* fptr)
|
|
175
|
+
{
|
|
176
|
+
uint64_t version = VCOMPRESSOR_VERSION;
|
|
177
|
+
if (!fptr) {
|
|
178
|
+
return -1;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
fwrite(&version, 1, sizeof(uint64_t), fptr);
|
|
182
|
+
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
PyObject*
|
|
188
|
+
json_dumps_to_bytes(PyObject* json_data)
|
|
189
|
+
{
|
|
190
|
+
PyObject* json_ret = NULL;
|
|
191
|
+
PyObject* bytes_data = NULL;
|
|
192
|
+
PyObject* dumps_func = NULL;
|
|
193
|
+
|
|
194
|
+
if (!json_data) {
|
|
195
|
+
PyErr_SetString(PyExc_ValueError, "json_data can not be NULL");
|
|
196
|
+
goto clean_exit;
|
|
197
|
+
}
|
|
198
|
+
dumps_func = PyObject_GetAttrString(json_module, "dumps");
|
|
199
|
+
if (!dumps_func) {
|
|
200
|
+
goto clean_exit;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// json dumps json_data
|
|
204
|
+
json_ret = PyObject_CallOneArg(dumps_func, json_data);
|
|
205
|
+
if (!json_ret) {
|
|
206
|
+
goto clean_exit;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// convert string to bytes
|
|
210
|
+
bytes_data = PyObject_CallMethod(json_ret, "encode", NULL);
|
|
211
|
+
Py_DECREF(json_ret);
|
|
212
|
+
if (!bytes_data) {
|
|
213
|
+
goto clean_exit;
|
|
214
|
+
}
|
|
215
|
+
if (!PyBytes_Check(bytes_data)) {
|
|
216
|
+
// need to release bytes_data here, bytes_data will not release after clean_exit
|
|
217
|
+
Py_DECREF(bytes_data);
|
|
218
|
+
PyErr_SetString(PyExc_ValueError, "Failed to convert string to bytes");
|
|
219
|
+
goto clean_exit;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
clean_exit:
|
|
223
|
+
Py_XDECREF(dumps_func);
|
|
224
|
+
|
|
225
|
+
if (PyErr_Occurred()) {
|
|
226
|
+
return NULL;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return bytes_data;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
PyObject*
|
|
234
|
+
json_loads_from_bytes(PyObject* bytes_data)
|
|
235
|
+
{
|
|
236
|
+
PyObject* loads_func = NULL;
|
|
237
|
+
PyObject* string_data = NULL;
|
|
238
|
+
PyObject* json_data = NULL;
|
|
239
|
+
|
|
240
|
+
if (!PyBytes_Check(bytes_data)) {
|
|
241
|
+
Py_DECREF(bytes_data);
|
|
242
|
+
PyErr_SetString(PyExc_ValueError, "expect a bytes object to decode");
|
|
243
|
+
goto clean_exit;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
loads_func = PyObject_GetAttrString(json_module, "loads");
|
|
247
|
+
if (!loads_func) {
|
|
248
|
+
goto clean_exit;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// decode depressed bytes to string
|
|
252
|
+
string_data = PyObject_CallMethod(bytes_data, "decode", NULL);
|
|
253
|
+
if (!string_data) {
|
|
254
|
+
goto clean_exit;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// convert string to json
|
|
258
|
+
json_data = PyObject_CallOneArg(loads_func, string_data);
|
|
259
|
+
if (!json_data) {
|
|
260
|
+
goto clean_exit;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
clean_exit:
|
|
264
|
+
Py_XDECREF(loads_func);
|
|
265
|
+
|
|
266
|
+
if (PyErr_Occurred()) {
|
|
267
|
+
return NULL;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return json_data;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
PyObject*
|
|
275
|
+
compress_bytes(PyObject* bytes_data)
|
|
276
|
+
{
|
|
277
|
+
PyObject* compress_func = NULL;
|
|
278
|
+
PyObject* compressed_data = NULL;
|
|
279
|
+
|
|
280
|
+
if (!PyBytes_Check(bytes_data)) {
|
|
281
|
+
Py_DECREF(bytes_data);
|
|
282
|
+
PyErr_SetString(PyExc_ValueError, "expect a bytes object to compress");
|
|
283
|
+
goto clean_exit;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
compress_func = PyObject_GetAttrString(zlib_module, "compress");
|
|
287
|
+
if (!compress_func) {
|
|
288
|
+
goto clean_exit;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
compressed_data = PyObject_CallOneArg(compress_func, bytes_data);
|
|
292
|
+
if (!compressed_data) {
|
|
293
|
+
goto clean_exit;
|
|
294
|
+
}
|
|
295
|
+
if (!PyBytes_Check(compressed_data)) {
|
|
296
|
+
Py_DECREF(compressed_data);
|
|
297
|
+
PyErr_SetString(PyExc_ValueError, "zlib.compress() returns a none bytes object");
|
|
298
|
+
goto clean_exit;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
clean_exit:
|
|
302
|
+
Py_XDECREF(compress_func);
|
|
303
|
+
|
|
304
|
+
if (PyErr_Occurred()) {
|
|
305
|
+
return NULL;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return compressed_data;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
PyObject*
|
|
313
|
+
decompress_bytes(PyObject* bytes_data)
|
|
314
|
+
{
|
|
315
|
+
// decompress data
|
|
316
|
+
PyObject* decompressed_data = NULL;
|
|
317
|
+
PyObject* decompress_func = NULL;
|
|
318
|
+
|
|
319
|
+
if (!PyBytes_Check(bytes_data)) {
|
|
320
|
+
Py_DECREF(bytes_data);
|
|
321
|
+
PyErr_SetString(PyExc_ValueError, "expect a bytes object to decompress");
|
|
322
|
+
goto clean_exit;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
decompress_func = PyObject_GetAttrString(zlib_module, "decompress");
|
|
326
|
+
if (!decompress_func) {
|
|
327
|
+
goto clean_exit;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
decompressed_data = PyObject_CallOneArg(decompress_func, bytes_data);
|
|
331
|
+
if (!decompressed_data) {
|
|
332
|
+
goto clean_exit;
|
|
333
|
+
}
|
|
334
|
+
if (!PyBytes_Check(decompressed_data)) {
|
|
335
|
+
Py_DECREF(decompressed_data);
|
|
336
|
+
PyErr_SetString(PyExc_ValueError, "zlib.decompress() returns a none bytes object");
|
|
337
|
+
goto clean_exit;
|
|
338
|
+
}
|
|
339
|
+
clean_exit:
|
|
340
|
+
Py_XDECREF(decompress_func);
|
|
341
|
+
|
|
342
|
+
if (PyErr_Occurred()) {
|
|
343
|
+
return NULL;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return decompressed_data;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
int
|
|
351
|
+
json_dumps_and_compress_to_file(PyObject* json_data, FILE* fptr)
|
|
352
|
+
{
|
|
353
|
+
char* buffer;
|
|
354
|
+
uint64_t uncompressed_size = 0;
|
|
355
|
+
uint64_t compressed_size = 0;
|
|
356
|
+
PyObject* bytes_data = NULL;
|
|
357
|
+
PyObject* compressed_data = NULL;
|
|
358
|
+
|
|
359
|
+
bytes_data = json_dumps_to_bytes(json_data);
|
|
360
|
+
if (!bytes_data) {
|
|
361
|
+
goto clean_exit;
|
|
362
|
+
}
|
|
363
|
+
uncompressed_size = PyBytes_Size(bytes_data);
|
|
364
|
+
if (NEED_COMPRESS_IN_FILE) {
|
|
365
|
+
compressed_data = compress_bytes(bytes_data);
|
|
366
|
+
if (!compressed_data) {
|
|
367
|
+
goto clean_exit;
|
|
368
|
+
}
|
|
369
|
+
compressed_size = PyBytes_Size(compressed_data);
|
|
370
|
+
buffer = PyBytes_AsString(compressed_data);
|
|
371
|
+
fwrite(&uncompressed_size, sizeof(uint64_t), 1, fptr);
|
|
372
|
+
fwrite(&compressed_size, sizeof(uint64_t), 1, fptr);
|
|
373
|
+
fwrite(buffer, sizeof(char), compressed_size, fptr);
|
|
374
|
+
Py_DECREF(compressed_data);
|
|
375
|
+
} else {
|
|
376
|
+
buffer = PyBytes_AsString(bytes_data);
|
|
377
|
+
fwrite(&uncompressed_size, sizeof(uint64_t), 1, fptr);
|
|
378
|
+
fwrite(buffer, sizeof(char), uncompressed_size, fptr);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
clean_exit:
|
|
382
|
+
Py_DECREF(bytes_data);
|
|
383
|
+
if (PyErr_Occurred()) {
|
|
384
|
+
return 1;
|
|
385
|
+
}
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
PyObject*
|
|
391
|
+
json_loads_and_decompress_from_file(FILE* fptr)
|
|
392
|
+
{
|
|
393
|
+
char* buffer = NULL;
|
|
394
|
+
uint64_t compressed_size = 0;
|
|
395
|
+
uint64_t uncompressed_size = 0;
|
|
396
|
+
uint64_t read_length = 0;
|
|
397
|
+
PyObject* json_data = NULL;
|
|
398
|
+
PyObject* compressed_data = NULL;
|
|
399
|
+
PyObject* bytes_data = NULL;
|
|
400
|
+
|
|
401
|
+
if (NEED_COMPRESS_IN_FILE) {
|
|
402
|
+
READ_DATA(&uncompressed_size, uint64_t, fptr)
|
|
403
|
+
READ_DATA(&compressed_size, uint64_t, fptr)
|
|
404
|
+
buffer = (char *)malloc(sizeof(char)*compressed_size);
|
|
405
|
+
if (!buffer) {
|
|
406
|
+
PyErr_Format(PyExc_RuntimeError, "Failed to malloc memory size %lld", compressed_size);
|
|
407
|
+
goto clean_exit;
|
|
408
|
+
}
|
|
409
|
+
read_length = fread(buffer, sizeof(char), compressed_size, fptr);
|
|
410
|
+
if (read_length != compressed_size) {
|
|
411
|
+
PyErr_Format(PyExc_ValueError, "file is corrupted");
|
|
412
|
+
free(buffer);
|
|
413
|
+
goto clean_exit;
|
|
414
|
+
}
|
|
415
|
+
compressed_data = PyBytes_FromStringAndSize(buffer, compressed_size);
|
|
416
|
+
free(buffer);
|
|
417
|
+
if (!compressed_data) {
|
|
418
|
+
goto clean_exit;
|
|
419
|
+
}
|
|
420
|
+
bytes_data = decompress_bytes(compressed_data);
|
|
421
|
+
Py_DECREF(compressed_data);
|
|
422
|
+
} else {
|
|
423
|
+
READ_DATA(&uncompressed_size, uint64_t, fptr)
|
|
424
|
+
buffer = (char *)malloc(sizeof(char)*uncompressed_size);
|
|
425
|
+
if (!buffer) {
|
|
426
|
+
PyErr_Format(PyExc_RuntimeError, "Failed to malloc memory size %lld", uncompressed_size);
|
|
427
|
+
goto clean_exit;
|
|
428
|
+
}
|
|
429
|
+
read_length = fread(buffer, sizeof(char), uncompressed_size, fptr);
|
|
430
|
+
if (read_length != uncompressed_size) {
|
|
431
|
+
PyErr_Format(PyExc_ValueError, "file is corrupted");
|
|
432
|
+
free(buffer);
|
|
433
|
+
goto clean_exit;
|
|
434
|
+
}
|
|
435
|
+
bytes_data = PyBytes_FromStringAndSize(buffer, uncompressed_size);
|
|
436
|
+
free(buffer);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (!bytes_data) {
|
|
440
|
+
goto clean_exit;
|
|
441
|
+
}
|
|
442
|
+
json_data = json_loads_from_bytes(bytes_data);
|
|
443
|
+
Py_DECREF(bytes_data);
|
|
444
|
+
if (!json_data) {
|
|
445
|
+
goto clean_exit;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
clean_exit:
|
|
449
|
+
|
|
450
|
+
if (PyErr_Occurred()) {
|
|
451
|
+
return NULL;
|
|
452
|
+
}
|
|
453
|
+
return json_data;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
int
|
|
458
|
+
dump_parsed_trace_events(PyObject* trace_events, FILE* fptr)
|
|
459
|
+
{
|
|
460
|
+
// Dump process and thread names
|
|
461
|
+
PyObject* process_names = PyDict_GetItemString(trace_events, "process_names");
|
|
462
|
+
PyObject* thread_names = PyDict_GetItemString(trace_events, "thread_names");
|
|
463
|
+
PyObject* fee_events = PyDict_GetItemString(trace_events, "fee_events");
|
|
464
|
+
PyObject* counter_events = PyDict_GetItemString(trace_events, "counter_events");
|
|
465
|
+
PyObject* other_events = PyDict_GetItemString(trace_events, "other_events");
|
|
466
|
+
Py_ssize_t ppos = 0;
|
|
467
|
+
PyObject* key = NULL;
|
|
468
|
+
PyObject* value = NULL;
|
|
469
|
+
|
|
470
|
+
// Iterate through process names
|
|
471
|
+
ppos = 0;
|
|
472
|
+
while (PyDict_Next(process_names, &ppos, &key, &value)) {
|
|
473
|
+
uint64_t pid = PyLong_AsLong(PyTuple_GetItem(key, 0));
|
|
474
|
+
uint64_t tid = PyLong_AsLong(PyTuple_GetItem(key, 1));
|
|
475
|
+
const char* name = PyUnicode_AsUTF8(value);
|
|
476
|
+
fputc(VC_HEADER_PROCESS_NAME, fptr);
|
|
477
|
+
fwrite(&pid, sizeof(uint64_t), 1, fptr);
|
|
478
|
+
fwrite(&tid, sizeof(uint64_t), 1, fptr);
|
|
479
|
+
fwritestr(name, fptr);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Iterate through thread names
|
|
483
|
+
ppos = 0;
|
|
484
|
+
while (PyDict_Next(thread_names, &ppos, &key, &value)) {
|
|
485
|
+
uint64_t pid = PyLong_AsLong(PyTuple_GetItem(key, 0));
|
|
486
|
+
uint64_t tid = PyLong_AsLong(PyTuple_GetItem(key, 1));
|
|
487
|
+
const char* name = PyUnicode_AsUTF8(value);
|
|
488
|
+
fputc(VC_HEADER_THREAD_NAME, fptr);
|
|
489
|
+
fwrite(&pid, sizeof(uint64_t), 1, fptr);
|
|
490
|
+
fwrite(&tid, sizeof(uint64_t), 1, fptr);
|
|
491
|
+
fwritestr(name, fptr);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Iterate through fee events
|
|
495
|
+
ppos = 0;
|
|
496
|
+
while (PyDict_Next(fee_events, &ppos, &key, &value)) {
|
|
497
|
+
if (write_fee_events(key, value, fptr) != 0){
|
|
498
|
+
goto clean_exit;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Iterate through counter events
|
|
503
|
+
ppos = 0;
|
|
504
|
+
while (PyDict_Next(counter_events, &ppos, &key, &value)) {
|
|
505
|
+
uint64_t pid = PyLong_AsLong(PyTuple_GetItem(key, 0));
|
|
506
|
+
uint64_t tid = PyLong_AsLong(PyTuple_GetItem(key, 1));
|
|
507
|
+
const char* name = PyUnicode_AsUTF8(PyTuple_GetItem(key, 2));
|
|
508
|
+
fputc(VC_HEADER_COUNTER_EVENTS, fptr);
|
|
509
|
+
fwrite(&pid, sizeof(uint64_t), 1, fptr);
|
|
510
|
+
fwrite(&tid, sizeof(uint64_t), 1, fptr);
|
|
511
|
+
fwritestr(name, fptr);
|
|
512
|
+
if (diff_and_write_counter_args(value, fptr) != 0) {
|
|
513
|
+
goto clean_exit;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// just write other events
|
|
518
|
+
fputc(VC_HEADER_OTHER_EVENTS, fptr);
|
|
519
|
+
if (json_dumps_and_compress_to_file(other_events, fptr) != 0) {
|
|
520
|
+
goto clean_exit;
|
|
521
|
+
}
|
|
522
|
+
clean_exit:
|
|
523
|
+
if (PyErr_Occurred()) {
|
|
524
|
+
return 1;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return 0;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
int
|
|
532
|
+
write_fee_events(PyObject* fee_key, PyObject* fee_value, FILE* fptr) {
|
|
533
|
+
PyObject* args_list = NULL;
|
|
534
|
+
uint64_t pid = PyLong_AsLong(PyTuple_GetItem(fee_key, 0));
|
|
535
|
+
uint64_t tid = PyLong_AsLong(PyTuple_GetItem(fee_key, 1));
|
|
536
|
+
uint64_t ts_size = PyList_GET_SIZE(fee_value);
|
|
537
|
+
uint64_t args_offset = 0;
|
|
538
|
+
int64_t last_ts = 0;
|
|
539
|
+
const char* name = PyUnicode_AsUTF8(PyTuple_GetItem(fee_key, 2));
|
|
540
|
+
int place_holder = 0;
|
|
541
|
+
fputc(VC_HEADER_FEE, fptr);
|
|
542
|
+
fwrite(&pid, sizeof(uint64_t), 1, fptr);
|
|
543
|
+
fwrite(&tid, sizeof(uint64_t), 1, fptr);
|
|
544
|
+
fwritestr(name, fptr);
|
|
545
|
+
fwrite(&ts_size, sizeof(uint64_t), 1, fptr);
|
|
546
|
+
if (!PyObject_CallMethod(fee_value, "sort", NULL)) {
|
|
547
|
+
goto clean_exit;
|
|
548
|
+
}
|
|
549
|
+
// write place holder for args offset
|
|
550
|
+
place_holder = ftell(fptr);
|
|
551
|
+
fwrite(&args_offset, sizeof(uint64_t), 1, fptr);
|
|
552
|
+
if (PyTuple_GetItem(fee_key, 3) == Py_True) {
|
|
553
|
+
args_list = PyList_New(0);
|
|
554
|
+
}
|
|
555
|
+
for (Py_ssize_t idx = 0; idx < (Py_ssize_t)ts_size; idx++) {
|
|
556
|
+
PyObject * event_ts_tuple = PyList_GET_ITEM(fee_value, idx);
|
|
557
|
+
double ts = PyFloat_AsDouble(PyTuple_GET_ITEM(event_ts_tuple, 0));
|
|
558
|
+
double dur = PyFloat_AsDouble(PyTuple_GET_ITEM(event_ts_tuple, 1));
|
|
559
|
+
int64_t ts64 = ts * 100;
|
|
560
|
+
uint64_t dur64 = dur * 100;
|
|
561
|
+
uint64_t delta_ts = ts64 - last_ts;
|
|
562
|
+
last_ts = ts64;
|
|
563
|
+
if (idx == 0) {
|
|
564
|
+
// write the first timestamp as int64_t, for timestamp on windows may be negative
|
|
565
|
+
fwrite(&ts64, sizeof(int64_t), 1, fptr);
|
|
566
|
+
} else {
|
|
567
|
+
write_encoded_int(delta_ts, fptr);
|
|
568
|
+
}
|
|
569
|
+
write_encoded_int(dur64, fptr);
|
|
570
|
+
if (args_list) {
|
|
571
|
+
PyList_Append(args_list, PyTuple_GET_ITEM(event_ts_tuple, 2));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (args_list) {
|
|
576
|
+
args_offset = ftell(fptr);
|
|
577
|
+
fseek(fptr, place_holder, SEEK_SET);
|
|
578
|
+
fwrite(&args_offset, sizeof(args_offset), 1, fptr);
|
|
579
|
+
fseek(fptr, args_offset, SEEK_SET);
|
|
580
|
+
if (json_dumps_and_compress_to_file(args_list, fptr) != 0) {
|
|
581
|
+
goto clean_exit;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
clean_exit:
|
|
586
|
+
Py_XDECREF(args_list);
|
|
587
|
+
|
|
588
|
+
if (PyErr_Occurred()) {
|
|
589
|
+
return 1;
|
|
590
|
+
}
|
|
591
|
+
return 0;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
PyObject*
|
|
596
|
+
load_fee_events(FILE* fptr) {
|
|
597
|
+
uint64_t pid = 0;
|
|
598
|
+
uint64_t tid = 0;
|
|
599
|
+
uint64_t count = 0;
|
|
600
|
+
uint64_t dur = 0;
|
|
601
|
+
uint64_t delta_ts = 0;
|
|
602
|
+
uint64_t args_offset = 0;
|
|
603
|
+
uint64_t place_holder_ts = 0;
|
|
604
|
+
uint64_t place_holder_end = 0;
|
|
605
|
+
int64_t last_ts = 0;
|
|
606
|
+
char buffer[STRING_BUFFER_SIZE] = {0};
|
|
607
|
+
PyObject* fee_events_list = PyList_New(0);
|
|
608
|
+
PyObject* event = NULL;
|
|
609
|
+
PyObject* name = NULL;
|
|
610
|
+
PyObject* args_list = NULL;
|
|
611
|
+
PyObject* unicode_X = PyUnicode_FromString("X");
|
|
612
|
+
PyObject* unicode_FEE = PyUnicode_FromString("FEE");
|
|
613
|
+
|
|
614
|
+
READ_DATA(&pid, uint64_t, fptr);
|
|
615
|
+
READ_DATA(&tid, uint64_t, fptr);
|
|
616
|
+
freadstrn(buffer, STRING_BUFFER_SIZE - 1, fptr);
|
|
617
|
+
READ_DATA(&count, uint64_t, fptr);
|
|
618
|
+
name = PyUnicode_FromString(buffer);
|
|
619
|
+
READ_DATA(&args_offset, uint64_t, fptr);
|
|
620
|
+
if (args_offset != 0) {
|
|
621
|
+
// read fee args
|
|
622
|
+
place_holder_ts = ftell(fptr);
|
|
623
|
+
if (fseek(fptr, args_offset, SEEK_SET) != 0) {
|
|
624
|
+
PyErr_SetString(PyExc_ValueError, "seek to args offset failed!");
|
|
625
|
+
goto clean_exit;
|
|
626
|
+
}
|
|
627
|
+
args_list = json_loads_and_decompress_from_file(fptr);
|
|
628
|
+
if (!args_list) {
|
|
629
|
+
goto clean_exit;
|
|
630
|
+
}
|
|
631
|
+
// There's error checking in PyList_Size
|
|
632
|
+
if (PyList_Size(args_list) != (Py_ssize_t)count) {
|
|
633
|
+
PyErr_SetString(PyExc_ValueError, "args length is not equal to count!");
|
|
634
|
+
goto clean_exit;
|
|
635
|
+
}
|
|
636
|
+
place_holder_end = ftell(fptr);
|
|
637
|
+
fseek(fptr, place_holder_ts, SEEK_SET);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
for (uint64_t i = 0; i < count; i++) {
|
|
641
|
+
if (i == 0) {
|
|
642
|
+
READ_DATA(&last_ts, int64_t, fptr);
|
|
643
|
+
} else {
|
|
644
|
+
if (read_encoded_int(&delta_ts, fptr) != 0) {
|
|
645
|
+
goto clean_exit;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (read_encoded_int(&dur, fptr) != 0) {
|
|
649
|
+
goto clean_exit;
|
|
650
|
+
}
|
|
651
|
+
event = PyDict_New();
|
|
652
|
+
PyDict_SetItemString(event, "ph", unicode_X);
|
|
653
|
+
PyDict_SetItemString(event, "name", name);
|
|
654
|
+
PyDict_SetItemString(event, "cat", unicode_FEE);
|
|
655
|
+
PyDict_SetItemStringULL(event, "pid", pid);
|
|
656
|
+
PyDict_SetItemStringULL(event, "tid", tid);
|
|
657
|
+
last_ts = delta_ts + last_ts;
|
|
658
|
+
PyDict_SetItemStringDouble(event, "ts", (double)last_ts / 100);
|
|
659
|
+
PyDict_SetItemStringDouble(event, "dur", (double)dur / 100);
|
|
660
|
+
if (args_list) {
|
|
661
|
+
PyDict_SetItemString(event, "args", PyList_GET_ITEM(args_list, i));
|
|
662
|
+
}
|
|
663
|
+
PyList_Append(fee_events_list, event);
|
|
664
|
+
Py_DECREF(event);
|
|
665
|
+
}
|
|
666
|
+
if (args_list) {
|
|
667
|
+
fseek(fptr, place_holder_end, SEEK_SET);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
clean_exit:
|
|
671
|
+
Py_XDECREF(name);
|
|
672
|
+
Py_XDECREF(args_list);
|
|
673
|
+
Py_DECREF(unicode_X);
|
|
674
|
+
Py_DECREF(unicode_FEE);
|
|
675
|
+
|
|
676
|
+
if (PyErr_Occurred()) {
|
|
677
|
+
return NULL;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return fee_events_list;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
int
|
|
685
|
+
diff_and_write_counter_args(PyObject* counter_args, FILE* fptr) {
|
|
686
|
+
/* there may be several args in a counter, log them all may take more spaces
|
|
687
|
+
* so this step is to do diffing between two contiguous timestamp
|
|
688
|
+
* and finally we only log those changed args
|
|
689
|
+
* Here's a counter_args example that we parsed
|
|
690
|
+
* {
|
|
691
|
+
* 1.1: {"a": 20, "b": 10}
|
|
692
|
+
* 2.2: {"a": 30, "b": 10}
|
|
693
|
+
* }
|
|
694
|
+
* In this case, "b" value is not changed, so we only need to log
|
|
695
|
+
* {
|
|
696
|
+
* 1.1: {"a": 20, "b": 10}
|
|
697
|
+
* 2.2: {"a": 30}
|
|
698
|
+
* }
|
|
699
|
+
*/
|
|
700
|
+
PyObject* cached_args_dict = PyDict_New();
|
|
701
|
+
PyObject* diffed_args = PyDict_New();
|
|
702
|
+
PyObject* ts_keys = PyDict_Keys(counter_args);
|
|
703
|
+
PyObject* ts = NULL;
|
|
704
|
+
PyObject* cur_diffed_arg = NULL;
|
|
705
|
+
PyObject* cur_counter_arg = NULL;
|
|
706
|
+
PyObject* arg_key_list = NULL;
|
|
707
|
+
PyObject* counter_arg_key = NULL;
|
|
708
|
+
PyObject* counter_arg_value = NULL;
|
|
709
|
+
PyObject* cached_arg_value = NULL;
|
|
710
|
+
PyObject* overflowed_num_string = NULL;
|
|
711
|
+
Py_ssize_t ppos = 0;
|
|
712
|
+
uint64_t ts_key_count = 0;
|
|
713
|
+
uint64_t arg_nums = 0;
|
|
714
|
+
// sort the args by timestamp so we can diff
|
|
715
|
+
if (!ts_keys || !PyList_Check(ts_keys)) {
|
|
716
|
+
PyErr_SetString(PyExc_ValueError, "failed to get timestamp list");
|
|
717
|
+
goto clean_exit;
|
|
718
|
+
}
|
|
719
|
+
ts_key_count = PyList_GET_SIZE(ts_keys);
|
|
720
|
+
if (PyList_Sort(ts_keys) == -1) {
|
|
721
|
+
goto clean_exit;
|
|
722
|
+
}
|
|
723
|
+
/* Do diffing between two timestamps and store the result.
|
|
724
|
+
* In this for loop, we iterate all the counter_args by the sort of timestamp.
|
|
725
|
+
* And we mainly have three variables used to do the diffing:
|
|
726
|
+
* cur_counter_arg: the full args of this timestamp
|
|
727
|
+
* cached_args_dict: the full args of last timestamp
|
|
728
|
+
* cur_diffed_arg: diffed result between cached_args_dict and cur_counter_arg
|
|
729
|
+
*/
|
|
730
|
+
for (uint64_t i = 0; i < ts_key_count; i++) {
|
|
731
|
+
ts = PyList_GET_ITEM(ts_keys, i);
|
|
732
|
+
cur_counter_arg = PyDict_GetItem(counter_args, ts);
|
|
733
|
+
cur_diffed_arg = PyDict_New();
|
|
734
|
+
ppos = 0;
|
|
735
|
+
/* This while statement is to find the different arg between cur_counter_arg and cached_args_dict.
|
|
736
|
+
* It will iterate the arg value of cur_counter_arg, and compare with the value in cached_args_dict.
|
|
737
|
+
* If the value is same, just ignore this value
|
|
738
|
+
* If the value is different, then store it in cur_diffed_arg and update it in cached_args_dict.
|
|
739
|
+
*/
|
|
740
|
+
while (PyDict_Next(cur_counter_arg, &ppos, &counter_arg_key, &counter_arg_value)) {
|
|
741
|
+
cached_arg_value = PyDict_GetItem(cached_args_dict, counter_arg_key);
|
|
742
|
+
if (!cached_arg_value) {
|
|
743
|
+
PyDict_SetItem(cached_args_dict, counter_arg_key, counter_arg_value);
|
|
744
|
+
PyDict_SetItem(cur_diffed_arg, counter_arg_key, counter_arg_value);
|
|
745
|
+
} else {
|
|
746
|
+
int compare_result = PyObject_RichCompareBool(cached_arg_value, counter_arg_value, Py_EQ);
|
|
747
|
+
if (compare_result == -1) {
|
|
748
|
+
// compare error
|
|
749
|
+
goto clean_exit;
|
|
750
|
+
} else if (compare_result == 0) {
|
|
751
|
+
// if value is not equal in last timestamp, store the newest value
|
|
752
|
+
PyDict_SetItem(cached_args_dict, counter_arg_key, counter_arg_value);
|
|
753
|
+
PyDict_SetItem(cur_diffed_arg, counter_arg_key, counter_arg_value);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
ppos = 0;
|
|
758
|
+
/* This while statement is to find the arg that is in cached_args_dict but not in cur_counter_arg.
|
|
759
|
+
* Considering the situation that an arg is deleted at some timestamp, for example:
|
|
760
|
+
* {
|
|
761
|
+
* 1.1: {"a": 20, "b": 10}
|
|
762
|
+
* 2.2: {"a": 20}
|
|
763
|
+
* }
|
|
764
|
+
* In this case, we need to mark b value as UNKNOWN at timestamp 2.2
|
|
765
|
+
* This step is to iterate arg in cached_args_dict and determine if the arg is in cur_counter_arg
|
|
766
|
+
* Normally, the value of counter_arg is always numeric
|
|
767
|
+
* So we use Py_None to mark the value as UNKNOWN
|
|
768
|
+
*/
|
|
769
|
+
while (PyDict_Next(cached_args_dict, &ppos, &counter_arg_key, &cached_arg_value)) {
|
|
770
|
+
counter_arg_value = PyDict_GetItem(cur_counter_arg, counter_arg_key);
|
|
771
|
+
if (!counter_arg_value && cached_arg_value != Py_None) {
|
|
772
|
+
PyDict_SetItem(cached_args_dict, counter_arg_key, Py_None);
|
|
773
|
+
PyDict_SetItem(cur_diffed_arg, counter_arg_key, Py_None);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
PyDict_SetItem(diffed_args, ts, cur_diffed_arg);
|
|
777
|
+
Py_DECREF(cur_diffed_arg);
|
|
778
|
+
}
|
|
779
|
+
// write all the arg keys
|
|
780
|
+
// write the number of timestamp of this counter
|
|
781
|
+
arg_nums = PyDict_Size(cached_args_dict);
|
|
782
|
+
fwrite(&arg_nums, sizeof(uint64_t), 1, fptr);
|
|
783
|
+
arg_key_list = PyDict_Keys(cached_args_dict);
|
|
784
|
+
if (!arg_key_list) {
|
|
785
|
+
PyErr_SetString(PyExc_ValueError, "failed to get arg name list");
|
|
786
|
+
goto clean_exit;
|
|
787
|
+
}
|
|
788
|
+
// write all the name for all arguments appeared
|
|
789
|
+
for (uint64_t i = 0; i < arg_nums; i++) {
|
|
790
|
+
const char * key_name = NULL;
|
|
791
|
+
counter_arg_key = PyList_GetItem(arg_key_list, i);
|
|
792
|
+
key_name = PyUnicode_AsUTF8(counter_arg_key);
|
|
793
|
+
fwritestr(key_name, fptr);
|
|
794
|
+
}
|
|
795
|
+
// write [timestamp - values] * ts_key_count
|
|
796
|
+
fwrite(&ts_key_count, sizeof(uint64_t), 1, fptr);
|
|
797
|
+
for (uint64_t i = 0; i < ts_key_count; i++) {
|
|
798
|
+
double ts_double = 0;
|
|
799
|
+
int64_t ts_64 = 0;
|
|
800
|
+
ts = PyList_GET_ITEM(ts_keys, i);
|
|
801
|
+
cur_diffed_arg = PyDict_GetItem(diffed_args, ts);
|
|
802
|
+
ts_double = PyFloat_AsDouble(ts);
|
|
803
|
+
ts_64 = ts_double * 1000;
|
|
804
|
+
fwrite(&ts_64, sizeof(int64_t), 1, fptr);
|
|
805
|
+
for (uint64_t j = 0; j < arg_nums; j++) {
|
|
806
|
+
counter_arg_value = PyDict_GetItem(cur_diffed_arg, PyList_GET_ITEM(arg_key_list, j));
|
|
807
|
+
if (!counter_arg_value) {
|
|
808
|
+
fputc(VC_HEADER_COUNTER_ARG_SAME, fptr);
|
|
809
|
+
} else if (PyLong_CheckExact(counter_arg_value)) {
|
|
810
|
+
// if PyLongObject is overflowed, just store the string
|
|
811
|
+
int overflow = 0;
|
|
812
|
+
int64_t counter_value_int64 = PyLong_AsLongLongAndOverflow(counter_arg_value, &overflow);
|
|
813
|
+
if (overflow == 0) {
|
|
814
|
+
fputc(VC_HEADER_COUNTER_ARG_LONG, fptr);
|
|
815
|
+
fwrite(&counter_value_int64, sizeof(int64_t), 1, fptr);
|
|
816
|
+
} else {
|
|
817
|
+
const char * num_string = NULL;
|
|
818
|
+
overflowed_num_string = PyObject_Repr(counter_arg_value);
|
|
819
|
+
num_string = PyUnicode_AsUTF8(overflowed_num_string);
|
|
820
|
+
fputc(VC_HEADER_COUNTER_ARG_LONG_STRING, fptr);
|
|
821
|
+
fwritestr(num_string, fptr);
|
|
822
|
+
Py_DECREF(overflowed_num_string);
|
|
823
|
+
}
|
|
824
|
+
} else if (PyFloat_CheckExact(counter_arg_value)) {
|
|
825
|
+
double counter_value_double = PyFloat_AsDouble(counter_arg_value);
|
|
826
|
+
fputc(VC_HEADER_COUNTER_ARG_FLOAT, fptr);
|
|
827
|
+
fwrite(&counter_value_double, sizeof(double), 1, fptr);
|
|
828
|
+
} else if (counter_arg_value == Py_None) {
|
|
829
|
+
fputc(VC_HEADER_COUNTER_ARG_UNKNOWN, fptr);
|
|
830
|
+
} else {
|
|
831
|
+
PyErr_SetString(PyExc_ValueError, "Counter can only take numeric values");
|
|
832
|
+
goto clean_exit;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
clean_exit:
|
|
838
|
+
Py_XDECREF(arg_key_list);
|
|
839
|
+
Py_XDECREF(ts_keys);
|
|
840
|
+
Py_DECREF(cached_args_dict);
|
|
841
|
+
Py_DECREF(diffed_args);
|
|
842
|
+
|
|
843
|
+
if (PyErr_Occurred()) {
|
|
844
|
+
return 1;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
return 0;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
PyObject*
|
|
851
|
+
load_counter_event(FILE* fptr)
|
|
852
|
+
{
|
|
853
|
+
PyObject* counter_events_list = PyList_New(0);
|
|
854
|
+
PyObject* arg_key_list = PyList_New(0);
|
|
855
|
+
PyObject* cached_args = PyDict_New();
|
|
856
|
+
PyObject* counter_ph = PyUnicode_FromString("C");
|
|
857
|
+
PyObject* counter_event = NULL;
|
|
858
|
+
PyObject* current_arg = NULL;
|
|
859
|
+
PyObject* counter_arg_key = NULL;
|
|
860
|
+
PyObject* counter_arg_value = NULL;
|
|
861
|
+
PyObject* counter_name = NULL;
|
|
862
|
+
PyObject* counter_pid = NULL;
|
|
863
|
+
PyObject* counter_tid = NULL;
|
|
864
|
+
uint64_t pid = 0;
|
|
865
|
+
uint64_t tid = 0;
|
|
866
|
+
uint64_t arg_key_count = 0;
|
|
867
|
+
uint64_t counter_event_count = 0;
|
|
868
|
+
int64_t ts_64 = 0;
|
|
869
|
+
uint8_t header = 0;
|
|
870
|
+
int64_t value_longlong = 0;
|
|
871
|
+
double value_double = 0;
|
|
872
|
+
char buffer[STRING_BUFFER_SIZE] = {0};
|
|
873
|
+
char* string_value = NULL;
|
|
874
|
+
char* name = NULL;
|
|
875
|
+
|
|
876
|
+
// read pid, tid, name and keys
|
|
877
|
+
READ_DATA(&pid, uint64_t, fptr);
|
|
878
|
+
READ_DATA(&tid, uint64_t, fptr);
|
|
879
|
+
name = freadstr(fptr);
|
|
880
|
+
READ_DATA(&arg_key_count, uint64_t, fptr);
|
|
881
|
+
counter_name = PyUnicode_FromString(name);
|
|
882
|
+
free(name);
|
|
883
|
+
counter_pid = PyLong_FromUnsignedLongLong(pid);
|
|
884
|
+
counter_tid = PyLong_FromUnsignedLongLong(tid);
|
|
885
|
+
for (uint64_t i = 0; i < arg_key_count; i++) {
|
|
886
|
+
freadstrn(buffer, STRING_BUFFER_SIZE - 1, fptr);
|
|
887
|
+
counter_arg_key = PyUnicode_FromString(buffer);
|
|
888
|
+
PyList_Append(arg_key_list, counter_arg_key);
|
|
889
|
+
Py_DECREF(counter_arg_key);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// read counter events
|
|
893
|
+
// cached_args stores the newest value
|
|
894
|
+
// current_arg stores the counter arg of current timestamp
|
|
895
|
+
READ_DATA(&counter_event_count, uint64_t, fptr);
|
|
896
|
+
for (uint64_t i = 0; i < counter_event_count; i++) {
|
|
897
|
+
current_arg = PyDict_New();
|
|
898
|
+
READ_DATA(&ts_64, int64_t, fptr);
|
|
899
|
+
for (uint64_t j = 0; j < arg_key_count; j++) {
|
|
900
|
+
READ_DATA(&header, uint8_t, fptr);
|
|
901
|
+
counter_arg_key = PyList_GetItem(arg_key_list, j);
|
|
902
|
+
// counter arg not change means current value is same with it in last arg
|
|
903
|
+
// so we need to read it from cached_args
|
|
904
|
+
// other state means current value is different from it in last arg
|
|
905
|
+
// so we need to read it from file and save it in cached_args
|
|
906
|
+
switch (header)
|
|
907
|
+
{
|
|
908
|
+
case VC_HEADER_COUNTER_ARG_UNKNOWN:
|
|
909
|
+
PyDict_SetItem(cached_args, counter_arg_key, Py_None);
|
|
910
|
+
break;
|
|
911
|
+
case VC_HEADER_COUNTER_ARG_SAME:
|
|
912
|
+
counter_arg_value = PyDict_GetItem(cached_args, counter_arg_key);
|
|
913
|
+
if (counter_arg_value && counter_arg_value != Py_None) {
|
|
914
|
+
PyDict_SetItem(current_arg, counter_arg_key, counter_arg_value);
|
|
915
|
+
}
|
|
916
|
+
break;
|
|
917
|
+
case VC_HEADER_COUNTER_ARG_LONG:
|
|
918
|
+
READ_DATA(&value_longlong, int64_t, fptr);
|
|
919
|
+
counter_arg_value = PyLong_FromLongLong(value_longlong);
|
|
920
|
+
PyDict_SetItem(current_arg, counter_arg_key, counter_arg_value);
|
|
921
|
+
PyDict_SetItem(cached_args, counter_arg_key, counter_arg_value);
|
|
922
|
+
Py_DECREF(counter_arg_value);
|
|
923
|
+
break;
|
|
924
|
+
case VC_HEADER_COUNTER_ARG_FLOAT:
|
|
925
|
+
READ_DATA(&value_double, double, fptr);
|
|
926
|
+
counter_arg_value = PyFloat_FromDouble(value_double);
|
|
927
|
+
PyDict_SetItem(current_arg, counter_arg_key, counter_arg_value);
|
|
928
|
+
PyDict_SetItem(cached_args, counter_arg_key, counter_arg_value);
|
|
929
|
+
Py_DECREF(counter_arg_value);
|
|
930
|
+
break;
|
|
931
|
+
case VC_HEADER_COUNTER_ARG_LONG_STRING:
|
|
932
|
+
string_value = freadstr(fptr);
|
|
933
|
+
counter_arg_value = PyLong_FromString(string_value, NULL, 0);
|
|
934
|
+
free(string_value);
|
|
935
|
+
PyDict_SetItem(current_arg, counter_arg_key, counter_arg_value);
|
|
936
|
+
PyDict_SetItem(cached_args, counter_arg_key, counter_arg_value);
|
|
937
|
+
Py_DECREF(counter_arg_value);
|
|
938
|
+
break;
|
|
939
|
+
default:
|
|
940
|
+
PyErr_SetString(PyExc_ValueError, "counter arg header error!");
|
|
941
|
+
goto clean_exit;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
counter_event = PyDict_New();
|
|
945
|
+
PyList_Append(counter_events_list, counter_event);
|
|
946
|
+
Py_DECREF(counter_event);
|
|
947
|
+
PyDict_SetItemString(counter_event, "name", counter_name);
|
|
948
|
+
PyDict_SetItemString(counter_event, "pid", counter_pid);
|
|
949
|
+
PyDict_SetItemString(counter_event, "tid", counter_tid);
|
|
950
|
+
PyDict_SetItemString(counter_event, "ph", counter_ph);
|
|
951
|
+
PyDict_SetItemString(counter_event, "args", current_arg);
|
|
952
|
+
Py_DECREF(current_arg);
|
|
953
|
+
PyDict_SetItemStringDouble(counter_event, "ts", (double)ts_64 / 1000);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
clean_exit:
|
|
957
|
+
if (counter_name) {
|
|
958
|
+
Py_DECREF(counter_name);
|
|
959
|
+
}
|
|
960
|
+
if (counter_pid) {
|
|
961
|
+
Py_DECREF(counter_pid);
|
|
962
|
+
}
|
|
963
|
+
if (counter_tid) {
|
|
964
|
+
Py_DECREF(counter_tid);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
Py_DECREF(counter_ph);
|
|
968
|
+
Py_DECREF(arg_key_list);
|
|
969
|
+
Py_DECREF(cached_args);
|
|
970
|
+
|
|
971
|
+
if (PyErr_Occurred()) {
|
|
972
|
+
Py_DECREF(counter_events_list);
|
|
973
|
+
return NULL;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
return counter_events_list;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
PyObject*
|
|
980
|
+
load_events_from_file(FILE* fptr)
|
|
981
|
+
{
|
|
982
|
+
uint64_t version = 0;
|
|
983
|
+
uint8_t header = 0;
|
|
984
|
+
PyObject* parsed_events = PyDict_New();
|
|
985
|
+
PyObject* trace_events = PyList_New(0);
|
|
986
|
+
PyObject* file_info = NULL;
|
|
987
|
+
PyObject* name = NULL;
|
|
988
|
+
PyObject* event = NULL;
|
|
989
|
+
PyObject* counter_events = NULL;
|
|
990
|
+
PyObject* fee_events = NULL;
|
|
991
|
+
PyObject* other_events = NULL;
|
|
992
|
+
uint64_t pid = 0;
|
|
993
|
+
uint64_t tid = 0;
|
|
994
|
+
PyObject* args = NULL;
|
|
995
|
+
PyObject* unicode_X = PyUnicode_FromString("X");
|
|
996
|
+
PyObject* unicode_M = PyUnicode_FromString("M");
|
|
997
|
+
PyObject* unicode_FEE = PyUnicode_FromString("FEE");
|
|
998
|
+
PyObject* unicode_process_name = PyUnicode_FromString("process_name");
|
|
999
|
+
PyObject* unicode_thread_name = PyUnicode_FromString("thread_name");
|
|
1000
|
+
|
|
1001
|
+
char buffer[STRING_BUFFER_SIZE] = {0};
|
|
1002
|
+
|
|
1003
|
+
READ_DATA(&version, uint64_t, fptr);
|
|
1004
|
+
if (version != VCOMPRESSOR_VERSION) {
|
|
1005
|
+
Py_DECREF(trace_events);
|
|
1006
|
+
PyErr_SetString(PyExc_ValueError, "VCompressor does not support this version of file");
|
|
1007
|
+
goto clean_exit;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
PyDict_SetItemString(parsed_events, "traceEvents", trace_events);
|
|
1011
|
+
Py_DECREF(trace_events);
|
|
1012
|
+
|
|
1013
|
+
while (fread(&header, sizeof(uint8_t), 1, fptr)) {
|
|
1014
|
+
switch (header) {
|
|
1015
|
+
case VC_HEADER_PROCESS_NAME:
|
|
1016
|
+
event = PyDict_New();
|
|
1017
|
+
READ_DATA(&pid, uint64_t, fptr);
|
|
1018
|
+
READ_DATA(&tid, uint64_t, fptr);
|
|
1019
|
+
freadstrn(buffer, STRING_BUFFER_SIZE - 1, fptr);
|
|
1020
|
+
name = PyUnicode_FromString(buffer);
|
|
1021
|
+
event = PyDict_New();
|
|
1022
|
+
args = PyDict_New();
|
|
1023
|
+
PyDict_SetItemString(event, "ph", unicode_M);
|
|
1024
|
+
PyDict_SetItemString(event, "name", unicode_process_name);
|
|
1025
|
+
PyDict_SetItemStringULL(event, "pid", pid);
|
|
1026
|
+
PyDict_SetItemStringULL(event, "tid", tid);
|
|
1027
|
+
PyDict_SetItemString(event, "args", args);
|
|
1028
|
+
PyDict_SetItemString(args, "name", name);
|
|
1029
|
+
PyList_Append(trace_events, event);
|
|
1030
|
+
Py_DECREF(name);
|
|
1031
|
+
Py_DECREF(event);
|
|
1032
|
+
Py_DECREF(args);
|
|
1033
|
+
break;
|
|
1034
|
+
case VC_HEADER_THREAD_NAME:
|
|
1035
|
+
event = PyDict_New();
|
|
1036
|
+
READ_DATA(&pid, uint64_t, fptr);
|
|
1037
|
+
READ_DATA(&tid, uint64_t, fptr);
|
|
1038
|
+
freadstrn(buffer, STRING_BUFFER_SIZE - 1, fptr);
|
|
1039
|
+
name = PyUnicode_FromString(buffer);
|
|
1040
|
+
event = PyDict_New();
|
|
1041
|
+
args = PyDict_New();
|
|
1042
|
+
PyDict_SetItemString(event, "ph", unicode_M);
|
|
1043
|
+
PyDict_SetItemString(event, "name", unicode_thread_name);
|
|
1044
|
+
PyDict_SetItemStringULL(event, "pid", pid);
|
|
1045
|
+
PyDict_SetItemStringULL(event, "tid", tid);
|
|
1046
|
+
PyDict_SetItemString(event, "args", args);
|
|
1047
|
+
PyDict_SetItemString(args, "name", name);
|
|
1048
|
+
PyList_Append(trace_events, event);
|
|
1049
|
+
Py_DECREF(name);
|
|
1050
|
+
Py_DECREF(event);
|
|
1051
|
+
Py_DECREF(args);
|
|
1052
|
+
break;
|
|
1053
|
+
case VC_HEADER_FEE:
|
|
1054
|
+
fee_events = load_fee_events(fptr);
|
|
1055
|
+
if (!fee_events) {
|
|
1056
|
+
goto clean_exit;
|
|
1057
|
+
}
|
|
1058
|
+
PyObject_CallMethod(trace_events, "extend", "O", fee_events);
|
|
1059
|
+
Py_DECREF(fee_events);
|
|
1060
|
+
if (PyErr_Occurred()) {
|
|
1061
|
+
goto clean_exit;
|
|
1062
|
+
}
|
|
1063
|
+
break;
|
|
1064
|
+
case VC_HEADER_FILE_INFO:
|
|
1065
|
+
file_info = load_file_info(fptr);
|
|
1066
|
+
if (!file_info) {
|
|
1067
|
+
goto clean_exit;
|
|
1068
|
+
}
|
|
1069
|
+
PyDict_SetItemString(parsed_events, "file_info", file_info);
|
|
1070
|
+
Py_DECREF(file_info);
|
|
1071
|
+
break;
|
|
1072
|
+
case VC_HEADER_COUNTER_EVENTS:
|
|
1073
|
+
counter_events = load_counter_event(fptr);
|
|
1074
|
+
if (!counter_events) {
|
|
1075
|
+
goto clean_exit;
|
|
1076
|
+
}
|
|
1077
|
+
PyObject_CallMethod(trace_events, "extend", "O", counter_events);
|
|
1078
|
+
Py_DECREF(counter_events);
|
|
1079
|
+
if (PyErr_Occurred()) {
|
|
1080
|
+
goto clean_exit;
|
|
1081
|
+
}
|
|
1082
|
+
break;
|
|
1083
|
+
case VC_HEADER_OTHER_EVENTS:
|
|
1084
|
+
other_events = json_loads_and_decompress_from_file(fptr);
|
|
1085
|
+
if (!other_events) {
|
|
1086
|
+
goto clean_exit;
|
|
1087
|
+
}
|
|
1088
|
+
PyObject_CallMethod(trace_events, "extend", "O", other_events);
|
|
1089
|
+
Py_DECREF(other_events);
|
|
1090
|
+
if (PyErr_Occurred()) {
|
|
1091
|
+
goto clean_exit;
|
|
1092
|
+
}
|
|
1093
|
+
break;
|
|
1094
|
+
default:
|
|
1095
|
+
printf("wrong header %d\n", header);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
clean_exit:
|
|
1100
|
+
Py_DECREF(unicode_X);
|
|
1101
|
+
Py_DECREF(unicode_M);
|
|
1102
|
+
Py_DECREF(unicode_FEE);
|
|
1103
|
+
Py_DECREF(unicode_process_name);
|
|
1104
|
+
Py_DECREF(unicode_thread_name);
|
|
1105
|
+
|
|
1106
|
+
if (PyErr_Occurred()) {
|
|
1107
|
+
Py_DECREF(parsed_events);
|
|
1108
|
+
return NULL;
|
|
1109
|
+
}
|
|
1110
|
+
return parsed_events;
|
|
1111
|
+
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
int
|
|
1116
|
+
dump_file_info(PyObject* file_info, FILE* fptr)
|
|
1117
|
+
{
|
|
1118
|
+
// write data
|
|
1119
|
+
fputc(VC_HEADER_FILE_INFO, fptr);
|
|
1120
|
+
if (json_dumps_and_compress_to_file(file_info, fptr) == 0){
|
|
1121
|
+
return 0;
|
|
1122
|
+
} else {
|
|
1123
|
+
return 1;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
PyObject*
|
|
1128
|
+
load_file_info(FILE* fptr)
|
|
1129
|
+
{
|
|
1130
|
+
return json_loads_and_decompress_from_file(fptr);
|
|
1131
|
+
}
|