/src/moddable/xs/sources/xsProfile.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2016-2024 Moddable Tech, Inc. |
3 | | * |
4 | | * This file is part of the Moddable SDK Runtime. |
5 | | * |
6 | | * The Moddable SDK Runtime is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * The Moddable SDK Runtime is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | * This file incorporates work covered by the following copyright and |
20 | | * permission notice: |
21 | | * |
22 | | * Copyright (C) 2010-2016 Marvell International Ltd. |
23 | | * Copyright (C) 2002-2010 Kinoma, Inc. |
24 | | * |
25 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
26 | | * you may not use this file except in compliance with the License. |
27 | | * You may obtain a copy of the License at |
28 | | * |
29 | | * http://www.apache.org/licenses/LICENSE-2.0 |
30 | | * |
31 | | * Unless required by applicable law or agreed to in writing, software |
32 | | * distributed under the License is distributed on an "AS IS" BASIS, |
33 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
34 | | * See the License for the specific language governing permissions and |
35 | | * limitations under the License. |
36 | | */ |
37 | | |
38 | | |
39 | | #define _GNU_SOURCE |
40 | | #include "xsAll.h" |
41 | | #if mxMacOSX || mxLinux |
42 | | #include <dlfcn.h> |
43 | | #endif |
44 | | |
45 | | #ifdef mxProfile |
46 | | |
47 | | typedef struct sxProfiler txProfiler; |
48 | | typedef struct sxProfilerRecord txProfilerRecord; |
49 | | typedef struct sxProfilerSample txProfilerSample; |
50 | | |
51 | | struct sxProfiler { |
52 | | txU8 when; |
53 | | txU8 former; |
54 | | txU8 start; |
55 | | txU8 stop; |
56 | | txU4 interval; |
57 | | #ifdef mxMetering |
58 | | txU8 formerMeter; |
59 | | txU8 startMeter; |
60 | | #endif |
61 | | txU4 recordCount; |
62 | | txProfilerRecord** records; |
63 | | txU8 sampleCount; |
64 | | txU8 sampleIndex; |
65 | | txProfilerSample* samples; |
66 | | txProfilerRecord* host; |
67 | | txProfilerRecord* gc; |
68 | | }; |
69 | | |
70 | | struct sxProfilerRecord { |
71 | | txID recordID; |
72 | | txID constructorID; |
73 | | txID prototypeID; |
74 | | txID functionID; |
75 | | txCallback functionAddress; |
76 | | txID file; |
77 | | txInteger line; |
78 | | txInteger hitCount; |
79 | | txInteger calleeCount; |
80 | | txID* callees; |
81 | | txInteger flags; |
82 | | }; |
83 | | |
84 | | struct sxProfilerSample { |
85 | | txID recordID; |
86 | | txU4 delta; |
87 | | #ifdef mxMetering |
88 | | txU4 deltaMeter; |
89 | | #endif |
90 | | }; |
91 | | |
92 | | static txProfilerRecord* fxFrameToProfilerRecord(txMachine* the, txSlot* frame); |
93 | | static txU8 fxGetTicks(); |
94 | | static void fxInsertProfilerCallee(txMachine* the, txProfilerRecord* record, txID recordID); |
95 | | static txU8 fxMicrosecondsToTicks(txU8 microseconds); |
96 | | static void fxPrintID(txMachine* the, FILE* file, txID id); |
97 | | static void fxPrintProfiler(txMachine* the, void* stream); |
98 | | static void fxPrintString(txMachine* the, FILE* file, txString theString); |
99 | | #ifdef mxMetering |
100 | | static void fxPushProfilerSample(txMachine* the, txID recordID, txU4 delta, txU4 deltaMeter); |
101 | | #else |
102 | | static void fxPushProfilerSample(txMachine* the, txID recordID, txU4 delta); |
103 | | #endif |
104 | | static txProfilerRecord* fxNewProfilerRecord(txMachine* the, txID recordID); |
105 | | static txID fxRemoveProfilerCycle(txMachine* the, txProfiler* profiler, txID recordID); |
106 | | static txU8 fxTicksToMicroseconds(txU8 ticks); |
107 | | |
108 | | void fxCheckProfiler(txMachine* the, txSlot* frame) |
109 | 192M | { |
110 | 192M | txProfiler* profiler = the->profiler; |
111 | 192M | if (!profiler) |
112 | 192M | return; |
113 | 0 | txU8 when = profiler->when; |
114 | 0 | txU8 time = fxGetTicks(); |
115 | 0 | if (when < time) { |
116 | 0 | txProfilerRecord* callee = C_NULL; |
117 | 0 | txProfilerRecord* record = C_NULL; |
118 | 0 | if (frame) { |
119 | 0 | record = fxFrameToProfilerRecord(the, frame); |
120 | 0 | frame = frame->next; |
121 | 0 | } |
122 | 0 | else { |
123 | 0 | frame = the->frame; |
124 | 0 | if (frame) |
125 | 0 | record = profiler->gc; |
126 | 0 | } |
127 | 0 | if (record) { |
128 | 0 | callee = record; |
129 | 0 | while (frame) { |
130 | 0 | txProfilerRecord* parent = fxFrameToProfilerRecord(the, frame); |
131 | 0 | if (parent) { |
132 | 0 | fxInsertProfilerCallee(the, parent, callee->recordID); |
133 | 0 | callee = parent; |
134 | 0 | } |
135 | 0 | frame = frame->next; |
136 | 0 | } |
137 | 0 | } |
138 | 0 | if (callee) |
139 | 0 | fxInsertProfilerCallee(the, profiler->host, callee->recordID); |
140 | 0 | else |
141 | 0 | record = profiler->host; |
142 | 0 | txU4 interval = profiler->interval; |
143 | 0 | record->hitCount++; |
144 | 0 | #ifdef mxMetering |
145 | 0 | fxPushProfilerSample(the, record->recordID, (txU4)(time - profiler->former), (txU4)(the->meterIndex - profiler->formerMeter)); |
146 | 0 | profiler->formerMeter = the->meterIndex; |
147 | | #else |
148 | | fxPushProfilerSample(the, record->recordID, (txU4)(time - profiler->former)); |
149 | | #endif |
150 | 0 | profiler->former = time; |
151 | 0 | profiler->when = time + interval - (time % interval); |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | | void fxCreateProfiler(txMachine* the) |
156 | 0 | { |
157 | 0 | txProfiler* profiler = the->profiler = c_malloc(sizeof(txProfiler)); |
158 | 0 | if (profiler == C_NULL) |
159 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
160 | 0 | profiler->interval = (txU4)fxMicrosecondsToTicks(1250); |
161 | 0 | profiler->former = fxGetTicks(); |
162 | 0 | profiler->when = profiler->former + profiler->interval; |
163 | 0 | profiler->start = profiler->former; |
164 | 0 | #ifdef mxMetering |
165 | 0 | profiler->interval >>= 1; |
166 | 0 | profiler->formerMeter = the->meterIndex; |
167 | 0 | profiler->startMeter = profiler->formerMeter; |
168 | 0 | #endif |
169 | 0 | profiler->recordCount = 2; |
170 | 0 | profiler->records = (txProfilerRecord**)c_calloc(sizeof(txProfilerRecord*), profiler->recordCount); |
171 | 0 | if (profiler->records == C_NULL) |
172 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
173 | | |
174 | 0 | profiler->sampleCount = 1024; |
175 | 0 | profiler->sampleIndex = 0; |
176 | 0 | profiler->samples = (txProfilerSample*)c_malloc((size_t)(profiler->sampleCount * sizeof(txProfilerSample))); |
177 | 0 | if (profiler->samples == C_NULL) |
178 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
179 | 0 | profiler->host = fxNewProfilerRecord(the, 0); |
180 | 0 | profiler->gc = fxNewProfilerRecord(the, 1); |
181 | 0 | } |
182 | | |
183 | | void fxDeleteProfiler(txMachine* the, void* stream) |
184 | 0 | { |
185 | 0 | txProfiler* profiler = the->profiler; |
186 | 0 | profiler->stop = profiler->when; |
187 | 0 | fxPrintProfiler(the, stream); |
188 | 0 | c_free(profiler->samples); |
189 | 0 | txU4 recordIndex = 0; |
190 | 0 | while (recordIndex < profiler->recordCount) { |
191 | 0 | txProfilerRecord* record = profiler->records[recordIndex]; |
192 | 0 | if (record) { |
193 | 0 | if (record->callees) |
194 | 0 | c_free(record->callees); |
195 | 0 | c_free(record); |
196 | 0 | } |
197 | 0 | recordIndex++; |
198 | 0 | } |
199 | 0 | c_free(profiler->records); |
200 | 0 | c_free(profiler); |
201 | 0 | the->profiler = C_NULL; |
202 | 0 | } |
203 | | |
204 | | txProfilerRecord* fxFrameToProfilerRecord(txMachine* the, txSlot* frame) |
205 | 0 | { |
206 | 0 | txProfiler* profiler = the->profiler; |
207 | 0 | txSlot* function = frame + 3; |
208 | 0 | if (function->kind == XS_REFERENCE_KIND) { |
209 | 0 | function = function->value.reference; |
210 | 0 | if (mxIsFunction(function)) { |
211 | 0 | txSlot* code = mxFunctionInstanceCode(function); |
212 | 0 | txSlot* home = mxFunctionInstanceHome(function); |
213 | 0 | txID recordID = home->ID; |
214 | 0 | txProfilerRecord* record = C_NULL; |
215 | 0 | if (recordID < profiler->recordCount) |
216 | 0 | record = profiler->records[recordID]; |
217 | 0 | if (record) |
218 | 0 | return record; |
219 | 0 | record = fxNewProfilerRecord(the, recordID); |
220 | 0 | record->functionID = code->ID; |
221 | 0 | home = home->value.home.object; |
222 | 0 | if (home) { |
223 | 0 | if (mxIsFunction(home)) { |
224 | 0 | record->constructorID = mxFunctionInstanceCode(home)->ID; |
225 | 0 | } |
226 | 0 | else { |
227 | 0 | txSlot* property = home->next; |
228 | 0 | while (property && (property->flag & XS_INTERNAL_FLAG)) |
229 | 0 | property = property->next; |
230 | 0 | while (property) { |
231 | 0 | if (property->ID == mxID(_constructor)) |
232 | 0 | break; |
233 | 0 | property = property->next; |
234 | 0 | } |
235 | 0 | if (property) { |
236 | 0 | if (property->kind == XS_REFERENCE_KIND) { |
237 | 0 | property = property->value.reference; |
238 | 0 | if (mxIsFunction(property)) { |
239 | 0 | record->constructorID = mxFunctionInstanceCode(property)->ID; |
240 | 0 | record->prototypeID = mxID(_prototype); |
241 | 0 | } |
242 | 0 | } |
243 | 0 | } |
244 | 0 | else { |
245 | 0 | property = home->next; |
246 | 0 | while (property) { |
247 | 0 | if (property->ID == mxID(_Symbol_toStringTag)) |
248 | 0 | break; |
249 | 0 | property = property->next; |
250 | 0 | } |
251 | 0 | if (property) { |
252 | 0 | if ((property->kind == XS_STRING_KIND) || (property->kind == XS_STRING_X_KIND)) { |
253 | 0 | record->prototypeID = fxFindName(the, property->value.string); |
254 | 0 | } |
255 | 0 | } |
256 | 0 | } |
257 | 0 | } |
258 | 0 | } |
259 | 0 | if ((code->kind == XS_CODE_KIND) || (code->kind == XS_CODE_X_KIND)) { |
260 | 0 | txByte* p = code->value.code.address + 2; |
261 | 0 | if (*p == XS_CODE_FILE) { |
262 | 0 | txID file; |
263 | 0 | txS2 line; |
264 | 0 | p++; |
265 | 0 | mxDecodeID(p, file); |
266 | 0 | p++; |
267 | 0 | mxDecode2(p, line); |
268 | 0 | record->file = file; |
269 | 0 | record->line = line; |
270 | 0 | } |
271 | 0 | record->functionAddress = C_NULL; |
272 | 0 | } |
273 | 0 | else |
274 | 0 | record->functionAddress = code->value.callback.address; |
275 | 0 | return record; |
276 | 0 | } |
277 | 0 | } |
278 | 0 | return C_NULL; |
279 | 0 | } |
280 | | |
281 | | txU8 fxGetTicks() |
282 | 0 | { |
283 | | #if mxMacOSX |
284 | | return clock_gettime_nsec_np(CLOCK_MONOTONIC); |
285 | | #elif mxWindows |
286 | | LARGE_INTEGER counter; |
287 | | QueryPerformanceCounter(&counter); |
288 | | return counter.QuadPart; |
289 | | #else |
290 | 0 | c_timeval tv; |
291 | 0 | c_gettimeofday(&tv, NULL); |
292 | 0 | return (tv.tv_sec * 1000000ULL) + tv.tv_usec; |
293 | 0 | #endif |
294 | 0 | } |
295 | | |
296 | | void fxInsertProfilerCallee(txMachine* the, txProfilerRecord* record, txID recordID) |
297 | 0 | { |
298 | 0 | txInteger count = record->calleeCount; |
299 | 0 | if (count == 0) { |
300 | 0 | record->callees = c_malloc(sizeof(txID)); |
301 | 0 | if (record->callees == C_NULL) |
302 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
303 | 0 | record->calleeCount = 1; |
304 | 0 | record->callees[0] = recordID; |
305 | 0 | return; |
306 | 0 | } |
307 | 0 | txInteger min = 0; |
308 | 0 | txInteger max = count; |
309 | 0 | txInteger mid; |
310 | 0 | txID* callee = record->callees; |
311 | 0 | while (min < max) { |
312 | 0 | mid = (min + max) >> 1; |
313 | 0 | callee = record->callees + mid; |
314 | 0 | if (recordID < *callee) |
315 | 0 | max = mid; |
316 | 0 | else if (recordID > *callee) |
317 | 0 | min = mid + 1; |
318 | 0 | else |
319 | 0 | return; |
320 | 0 | } |
321 | 0 | if (recordID > *callee) |
322 | 0 | mid++; |
323 | 0 | record->calleeCount++; |
324 | 0 | record->callees = c_realloc(record->callees, record->calleeCount * sizeof(txID)); |
325 | 0 | if (record->callees == C_NULL) |
326 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
327 | 0 | callee = record->callees + mid; |
328 | 0 | if (mid < count) |
329 | 0 | c_memmove(callee + 1, callee, (count - mid) * sizeof(txID)); |
330 | 0 | *callee = recordID; |
331 | 0 | } |
332 | | |
333 | | txU8 fxMicrosecondsToTicks(txU8 microseconds) |
334 | 0 | { |
335 | | #if mxMacOSX |
336 | | return microseconds * 1000; |
337 | | #elif mxWindows |
338 | | LARGE_INTEGER frequency; |
339 | | QueryPerformanceFrequency(&frequency); |
340 | | return (frequency.QuadPart * microseconds) / 1000000ULL; |
341 | | #else |
342 | 0 | return microseconds; |
343 | 0 | #endif |
344 | 0 | } |
345 | | |
346 | | txProfilerRecord* fxNewProfilerRecord(txMachine* the, txID recordID) |
347 | 0 | { |
348 | 0 | txProfiler* profiler = the->profiler; |
349 | 0 | txU4 recordCount = profiler->recordCount; |
350 | 0 | txProfilerRecord* record; |
351 | 0 | if (recordID >= recordCount) { |
352 | 0 | recordCount = recordID + 1; |
353 | 0 | profiler->records = (txProfilerRecord**)c_realloc(profiler->records, recordCount * sizeof(txProfilerRecord*)); |
354 | 0 | if (profiler->records == C_NULL) |
355 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
356 | 0 | c_memset(profiler->records + profiler->recordCount, 0, (recordCount - profiler->recordCount) * sizeof(txProfilerRecord*)); |
357 | 0 | profiler->recordCount = recordCount; |
358 | 0 | } |
359 | 0 | record = c_malloc(sizeof(txProfilerRecord)); |
360 | 0 | if (record == C_NULL) |
361 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
362 | 0 | record->recordID = recordID; |
363 | 0 | record->hitCount = 0; |
364 | 0 | record->constructorID = XS_NO_ID; |
365 | 0 | record->prototypeID = XS_NO_ID; |
366 | 0 | record->functionID = XS_NO_ID; |
367 | 0 | record->file = XS_NO_ID; |
368 | 0 | record->line = 0; |
369 | 0 | record->calleeCount = 0; |
370 | 0 | record->callees = C_NULL; |
371 | 0 | record->flags = 0; |
372 | 0 | profiler->records[recordID] = record; |
373 | 0 | return record; |
374 | 0 | } |
375 | | |
376 | | void fxPrintID(txMachine* the, FILE* file, txID id) |
377 | 0 | { |
378 | 0 | if (id != XS_NO_ID) { |
379 | 0 | txBoolean adorn; |
380 | 0 | txString string = fxGetKeyString(the, id, &adorn); |
381 | 0 | if (adorn) |
382 | 0 | fprintf(file, "["); |
383 | 0 | fxPrintString(the, file, string); |
384 | 0 | if (adorn) |
385 | 0 | fprintf(file, "]"); |
386 | 0 | } |
387 | 0 | else |
388 | 0 | fprintf(file, "?"); |
389 | 0 | } |
390 | | |
391 | | void fxPrintProfiler(txMachine* the, void* stream) |
392 | 0 | { |
393 | | // https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-Profile |
394 | 0 | txProfiler* profiler = the->profiler; |
395 | 0 | FILE* file = stream; |
396 | 0 | char buffer[22]; |
397 | 0 | char name[36]; |
398 | | |
399 | 0 | fxRemoveProfilerCycle(the, profiler, 0); |
400 | | |
401 | 0 | if (!file) { |
402 | 0 | time_t timer; |
403 | 0 | struct tm* tm_info; |
404 | 0 | timer = time(NULL); |
405 | 0 | tm_info = localtime(&timer); |
406 | 0 | strftime(buffer, 22, "XS-%y-%m-%d-%H-%M-%S-", tm_info); |
407 | 0 | snprintf(name, sizeof(name), "%s%03llu.cpuprofile", buffer, (long long unsigned int)((profiler->stop / 1000) % 1000)); |
408 | 0 | file = fopen(name, "w"); |
409 | 0 | } |
410 | | |
411 | 0 | fprintf(file, "{\"nodes\":["); |
412 | 0 | txU4 recordIndex = 0; |
413 | 0 | while (recordIndex < profiler->recordCount) { |
414 | 0 | txProfilerRecord* record = profiler->records[recordIndex]; |
415 | 0 | if (record) { |
416 | 0 | if (recordIndex > 0) |
417 | 0 | fprintf(file, ","); |
418 | 0 | fprintf(file, "{\"id\":%d,\"callFrame\":{\"functionName\":\"", record->recordID); |
419 | 0 | if (recordIndex == 0) |
420 | 0 | fprintf(file, "(host)"); |
421 | 0 | else if (recordIndex == 1) |
422 | 0 | fprintf(file, "(gc)"); |
423 | 0 | else if (record->functionID != XS_NO_ID) { |
424 | 0 | if (record->constructorID != XS_NO_ID) { |
425 | 0 | fxPrintID(the, file, record->constructorID); |
426 | 0 | fprintf(file, "."); |
427 | 0 | } |
428 | 0 | if (record->prototypeID != XS_NO_ID) { |
429 | 0 | fxPrintID(the, file, record->prototypeID); |
430 | 0 | fprintf(file, "."); |
431 | 0 | } |
432 | 0 | fxPrintID(the, file, record->functionID); |
433 | 0 | } |
434 | 0 | else if (record->functionAddress) { |
435 | 0 | #if mxMacOSX || mxLinux |
436 | 0 | Dl_info info; |
437 | 0 | if (dladdr(record->functionAddress, &info) && info.dli_sname) |
438 | 0 | fprintf(file, "@%s",info.dli_sname ); |
439 | 0 | else |
440 | 0 | #endif |
441 | 0 | fprintf(file, "@anonymous-%d", record->recordID); |
442 | 0 | } |
443 | 0 | else |
444 | 0 | fprintf(file, "(anonymous-%d)", record->recordID); |
445 | |
|
446 | 0 | fprintf(file, "\",\"scriptId\":\"0\",\"url\":\""); |
447 | 0 | if (record->file != XS_NO_ID) |
448 | 0 | fxPrintID(the, file, record->file); |
449 | 0 | fprintf(file, "\",\"lineNumber\":%d,\"columnNumber\":-1},\"hitCount\":%d,\"children\":[", record->line - 1, record->hitCount); |
450 | 0 | txInteger calleeIndex = 0; |
451 | 0 | txBoolean comma = 0; |
452 | 0 | while (calleeIndex < record->calleeCount) { |
453 | 0 | txID recordID = record->callees[calleeIndex]; |
454 | 0 | if (recordID != XS_NO_ID) { |
455 | 0 | if (comma) |
456 | 0 | fprintf(file, ","); |
457 | 0 | fprintf(file, "%d", recordID); |
458 | 0 | comma = 1; |
459 | 0 | } |
460 | 0 | calleeIndex++; |
461 | 0 | } |
462 | 0 | fprintf(file, "]}"); |
463 | 0 | } |
464 | 0 | recordIndex++; |
465 | 0 | } |
466 | 0 | fprintf(file, "],\"startTime\":%llu,\"endTime\":%llu,", (long long unsigned int)fxTicksToMicroseconds(profiler->start), (long long unsigned int)fxTicksToMicroseconds(profiler->when)); |
467 | 0 | #ifdef mxMetering |
468 | 0 | fprintf(file, "\"startMeter\":%llu,\"endMeter\":%llu,", profiler->startMeter, the->meterIndex); |
469 | 0 | #endif |
470 | 0 | fprintf(file, "\"samples\":["); |
471 | 0 | { |
472 | 0 | txU8 sampleIndex = 0; |
473 | 0 | while (sampleIndex < profiler->sampleIndex) { |
474 | 0 | txProfilerSample* sample = profiler->samples + sampleIndex; |
475 | 0 | if (sampleIndex > 0) |
476 | 0 | fprintf(file, ","); |
477 | 0 | fprintf(file, "%d", sample->recordID); |
478 | 0 | sampleIndex++; |
479 | 0 | } |
480 | 0 | } |
481 | 0 | fprintf(file, "],\"timeDeltas\":["); |
482 | 0 | { |
483 | 0 | txU8 sampleIndex = 0; |
484 | 0 | while (sampleIndex < profiler->sampleIndex) { |
485 | 0 | txProfilerSample* sample = profiler->samples + sampleIndex; |
486 | 0 | if (sampleIndex > 0) |
487 | 0 | fprintf(file, ","); |
488 | 0 | fprintf(file, "%llu", (long long unsigned int)fxTicksToMicroseconds(sample->delta)); |
489 | 0 | sampleIndex++; |
490 | 0 | } |
491 | 0 | } |
492 | 0 | #ifdef mxMetering |
493 | 0 | fprintf(file, "],\"meterDeltas\":["); |
494 | 0 | { |
495 | 0 | txU8 sampleIndex = 0; |
496 | 0 | while (sampleIndex < profiler->sampleIndex) { |
497 | 0 | txProfilerSample* sample = profiler->samples + sampleIndex; |
498 | 0 | if (sampleIndex > 0) |
499 | 0 | fprintf(file, ","); |
500 | 0 | fprintf(file, "%u", sample->deltaMeter); |
501 | 0 | sampleIndex++; |
502 | 0 | } |
503 | 0 | } |
504 | 0 | #endif |
505 | 0 | fprintf(file, "]}"); |
506 | 0 | fclose(file); |
507 | 0 | } |
508 | | |
509 | | void fxPrintString(txMachine* the, FILE* file, txString theString) |
510 | 0 | { |
511 | 0 | char buffer[16]; |
512 | 0 | for (;;) { |
513 | 0 | txInteger character; |
514 | 0 | char *p; |
515 | 0 | theString = mxStringByteDecode(theString, &character); |
516 | 0 | if (character == C_EOF) |
517 | 0 | break; |
518 | 0 | p = buffer; |
519 | 0 | if ((character < 32) || ((0xD800 <= character) && (character <= 0xDFFF))) { |
520 | 0 | *p++ = '\\'; |
521 | 0 | *p++ = 'u'; |
522 | 0 | p = fxStringifyUnicodeEscape(p, character, '\\'); |
523 | 0 | } |
524 | 0 | else if (character == '"') { |
525 | 0 | *p++ = '\\'; |
526 | 0 | *p++ = '"'; |
527 | 0 | } |
528 | 0 | else if (character == '\\') { |
529 | 0 | *p++ = '\\'; |
530 | 0 | *p++ = '\\'; |
531 | 0 | } |
532 | 0 | else |
533 | 0 | p = mxStringByteEncode(p, character); |
534 | 0 | *p = 0; |
535 | 0 | fprintf(file, "%s", buffer); |
536 | 0 | } |
537 | 0 | } |
538 | | |
539 | | #ifdef mxMetering |
540 | | void fxPushProfilerSample(txMachine* the, txID recordID, txU4 delta, txU4 deltaMeter) |
541 | | #else |
542 | | void fxPushProfilerSample(txMachine* the, txID recordID, txU4 delta) |
543 | | #endif |
544 | 0 | { |
545 | 0 | txProfiler* profiler = the->profiler; |
546 | 0 | txU8 sampleCount = profiler->sampleCount; |
547 | 0 | txU8 sampleIndex = profiler->sampleIndex; |
548 | 0 | txProfilerSample* sample; |
549 | 0 | if (sampleIndex == sampleCount) { |
550 | 0 | sampleCount += 1024; |
551 | 0 | profiler->samples = (txProfilerSample*)c_realloc(profiler->samples, (size_t)(sampleCount * sizeof(txProfilerSample))); |
552 | 0 | if (profiler->samples == C_NULL) |
553 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
554 | 0 | profiler->sampleCount = sampleCount; |
555 | 0 | } |
556 | 0 | sample = profiler->samples + sampleIndex; |
557 | 0 | sample->recordID = recordID; |
558 | 0 | sample->delta = delta; |
559 | 0 | #ifdef mxMetering |
560 | 0 | sample->deltaMeter = deltaMeter; |
561 | 0 | #endif |
562 | 0 | profiler->sampleIndex = sampleIndex + 1; |
563 | 0 | } |
564 | | |
565 | | txID fxRemoveProfilerCycle(txMachine* the, txProfiler* profiler, txID recordID) |
566 | 0 | { |
567 | 0 | txProfilerRecord* record = profiler->records[recordID]; |
568 | 0 | if (record->flags & 1) |
569 | 0 | return XS_NO_ID; |
570 | 0 | if (record->flags & 2) |
571 | 0 | return recordID; |
572 | 0 | record->flags |= 3; |
573 | 0 | txInteger calleeIndex = 0; |
574 | 0 | while (calleeIndex < record->calleeCount) { |
575 | 0 | txID calleeID = record->callees[calleeIndex]; |
576 | 0 | if (calleeID) |
577 | 0 | record->callees[calleeIndex] = fxRemoveProfilerCycle(the, profiler, calleeID); |
578 | 0 | calleeIndex++; |
579 | 0 | } |
580 | 0 | record->flags &= ~1; |
581 | 0 | return recordID; |
582 | 0 | } |
583 | | |
584 | | void fxResumeProfiler(txMachine* the) |
585 | 0 | { |
586 | 0 | txProfiler* profiler = the->profiler; |
587 | 0 | if (!profiler) |
588 | 0 | return; |
589 | 0 | txU8 delta = fxGetTicks() - profiler->stop; |
590 | 0 | profiler->when += delta; |
591 | 0 | profiler->former += delta; |
592 | 0 | profiler->start += delta; |
593 | 0 | } |
594 | | |
595 | | void fxSuspendProfiler(txMachine* the) |
596 | 0 | { |
597 | 0 | txProfiler* profiler = the->profiler; |
598 | 0 | if (!profiler) |
599 | 0 | return; |
600 | 0 | profiler->stop = fxGetTicks(); |
601 | 0 | } |
602 | | |
603 | | txU8 fxTicksToMicroseconds(txU8 ticks) |
604 | 0 | { |
605 | | #if mxMacOSX |
606 | | return ticks / 1000; |
607 | | #elif mxWindows |
608 | | LARGE_INTEGER frequency; |
609 | | QueryPerformanceFrequency(&frequency); |
610 | | return (1000000ULL * ticks) / frequency.QuadPart; |
611 | | #else |
612 | 0 | return ticks; |
613 | 0 | #endif |
614 | 0 | } |
615 | | |
616 | | |
617 | | |
618 | | #endif /* mxProfile */ |