Coverage Report

Created: 2025-06-24 07:03

/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 */