Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/profiler/telemeh.c
Line
Count
Source (jump to first uncovered line)
1
#include <moar.h>
2
3
#ifdef HAVE_TELEMEH
4
5
#include <stdio.h>
6
#include <time.h>
7
#include <string.h>
8
9
#if MVM_RDTSCP
10
# ifdef _WIN32
11
#  include <intrin.h>
12
# else
13
#  if defined(__x86_64__) || defined(__i386__)
14
#   include <x86intrin.h>
15
#  else
16
unsigned int __rdtscp(unsigned int *inval) {
17
    *inval = 0;
18
    return 0;
19
}
20
#  endif
21
# endif
22
#else
23
unsigned int __rdtscp(unsigned int *inval) {
24
    *inval = 0;
25
    return 0;
26
}
27
#endif
28
29
double ticksPerSecond;
30
31
// use RDTSCP instruction to get the required pipeline flush implicitly
32
#define READ_TSC(tscValue) \
33
{ \
34
    unsigned int _tsc_aux; \
35
    tscValue = __rdtscp(&_tsc_aux); \
36
}
37
38
#ifdef __clang__
39
#if !__has_builtin(__builtin_ia32_rdtscp)
40
#undef READ_TSC
41
#define READ_TSC(tscValue) { tscValue = 0; }
42
#warning "not using rdtscp"
43
#endif
44
#endif
45
46
#ifdef _WIN32
47
#include <windows.h>
48
#define MVM_sleep(ms) do { Sleep(ms); } while (0)
49
#else
50
#include <unistd.h>
51
#define MVM_sleep(ms) do { usleep(ms * 1000); } while (0)
52
#endif
53
54
enum RecordType {
55
    Calibration,
56
    Epoch,
57
    TimeStamp,
58
    IntervalStart,
59
    IntervalEnd,
60
    IntervalAnnotation,
61
    DynamicString
62
};
63
64
struct CalibrationRecord {
65
    double ticksPerSecond;
66
};
67
68
struct EpochRecord {
69
    unsigned long long time;
70
};
71
72
struct TimeStampRecord {
73
    unsigned long long time;
74
    const char *description;
75
};
76
77
struct IntervalRecord {
78
    unsigned long long time;
79
    unsigned int intervalID;
80
    const char *description;
81
};
82
83
struct IntervalAnnotation {
84
    unsigned int intervalID;
85
    const char *description;
86
};
87
88
struct DynamicString {
89
    unsigned int intervalID;
90
    char *description;
91
};
92
93
struct TelemetryRecord {
94
    enum RecordType recordType;
95
96
    uintptr_t threadID;
97
98
    union {
99
        struct CalibrationRecord calibration;
100
        struct EpochRecord epoch;
101
        struct TimeStampRecord timeStamp;
102
        struct IntervalRecord interval;
103
        struct IntervalAnnotation annotation;
104
        struct DynamicString annotation_dynamic;
105
    } u;
106
};
107
108
#define RECORD_BUFFER_SIZE 10000
109
110
// this is a ring buffer of telemetry events
111
static struct TelemetryRecord recordBuffer[RECORD_BUFFER_SIZE];
112
static AO_t recordBufferIndex = 0;
113
static unsigned int lastSerializedIndex = 0;
114
static unsigned long long beginningEpoch = 0;
115
static unsigned int telemetry_active = 0;
116
117
struct TelemetryRecord *newRecord()
118
{
119
    AO_t newBufferIndex, recordIndex;
120
    struct TelemetryRecord *record;
121
122
    do {
123
        recordIndex = MVM_load(&recordBufferIndex);
124
        newBufferIndex = (recordBufferIndex + 1) % RECORD_BUFFER_SIZE;
125
    } while(!MVM_trycas(&recordBufferIndex, recordIndex, newBufferIndex));
126
127
    record = &recordBuffer[recordIndex];
128
    return record;
129
}
130
131
static unsigned int intervalIDCounter = 0;
132
133
MVM_PUBLIC void MVM_telemetry_timestamp(MVMThreadContext *threadID, const char *description)
134
{
135
    struct TelemetryRecord *record;
136
137
    if (!telemetry_active) { return; }
138
139
    record = newRecord();
140
141
    READ_TSC(record->u.timeStamp.time);
142
    record->recordType = TimeStamp;
143
    record->threadID = (uintptr_t)threadID;
144
    record->u.timeStamp.description = description;
145
}
146
147
MVM_PUBLIC unsigned int MVM_telemetry_interval_start(MVMThreadContext *threadID, const char *description)
148
{
149
    struct TelemetryRecord *record;
150
151
    unsigned int intervalID;
152
153
    if (!telemetry_active) { return 0; }
154
155
    record = newRecord();
156
    MVM_incr(&intervalIDCounter);
157
    intervalID = MVM_load(&intervalIDCounter);
158
    READ_TSC(record->u.interval.time);
159
160
    record->recordType = IntervalStart;
161
    record->threadID = (uintptr_t)threadID;
162
    record->u.interval.intervalID = intervalID;
163
    record->u.interval.description = description;
164
165
    return intervalID;
166
}
167
168
MVM_PUBLIC void MVM_telemetry_interval_stop(MVMThreadContext *threadID, int intervalID, const char *description)
169
{
170
    struct TelemetryRecord *record;
171
172
    if (!telemetry_active) { return; }
173
174
    record = newRecord();
175
    READ_TSC(record->u.interval.time);
176
177
    record->recordType = IntervalEnd;
178
    record->threadID = (uintptr_t)threadID;
179
    record->u.interval.intervalID = intervalID;
180
    record->u.interval.description = description;
181
}
182
183
MVM_PUBLIC void MVM_telemetry_interval_annotate(uintptr_t subject, int intervalID, const char *description) {
184
    struct TelemetryRecord *record;
185
186
    if (!telemetry_active) { return; }
187
188
    record = newRecord();
189
    record->recordType = IntervalAnnotation;
190
    record->threadID = subject;
191
    record->u.annotation.intervalID = intervalID;
192
    record->u.annotation.description = description;
193
}
194
195
MVM_PUBLIC void MVM_telemetry_interval_annotate_dynamic(uintptr_t subject, int intervalID, char *description) {
196
    struct TelemetryRecord *record = NULL;
197
    char *temp = NULL;
198
    size_t temp_size;
199
200
    if (!telemetry_active) { return; }
201
    temp_size = strlen(description) + 1;
202
    temp      = malloc(temp_size);
203
    strncpy(temp, description, temp_size);
204
205
    record = newRecord();
206
    record->recordType = DynamicString;
207
    record->threadID = subject;
208
    record->u.annotation_dynamic.intervalID = intervalID;
209
    record->u.annotation_dynamic.description = temp;
210
}
211
212
void calibrateTSC(FILE *outfile)
213
{
214
    unsigned long long startTsc, endTsc;
215
    uint64_t startTime, endTime;
216
217
    startTime = uv_hrtime();
218
    READ_TSC(startTsc)
219
220
    MVM_sleep(1000);
221
222
    endTime = uv_hrtime();
223
    READ_TSC(endTsc)
224
225
    {
226
        unsigned long long ticks = endTsc - startTsc;
227
228
        unsigned long long wallClockTime = endTime - startTime;
229
230
        ticksPerSecond = (double)ticks / (double)wallClockTime;
231
        ticksPerSecond *= 1000000000.0;
232
    }
233
}
234
235
static uv_thread_t backgroundSerializationThread;
236
static volatile int continueBackgroundSerialization = 1;
237
238
void serializeTelemetryBufferRange(FILE *outfile, unsigned int serializationStart, unsigned int serializationEnd)
239
{
240
    unsigned int i;
241
    for(i = serializationStart; i < serializationEnd; i++) {
242
        struct TelemetryRecord *record = &recordBuffer[i];
243
244
        fprintf(outfile, "%10" PRIxPTR " ", record->threadID);
245
246
        switch(record->recordType) {
247
            case Calibration:
248
                fprintf(outfile, "Calibration: %f ticks per second\n", record->u.calibration.ticksPerSecond);
249
                break;
250
            case Epoch:
251
                fprintf(outfile, "Epoch counter: %lld\n", record->u.epoch.time);
252
                break;
253
            case TimeStamp:
254
                fprintf(outfile, "%15lld -|-  \"%s\"\n", record->u.timeStamp.time - beginningEpoch, record->u.timeStamp.description);
255
                break;
256
            case IntervalStart:
257
                fprintf(outfile, "%15lld (-   \"%s\" (%d)\n", record->u.interval.time - beginningEpoch, record->u.interval.description, record->u.interval.intervalID);
258
                break;
259
            case IntervalEnd:
260
                fprintf(outfile, "%15lld  -)  \"%s\" (%d)\n", record->u.interval.time - beginningEpoch, record->u.interval.description, record->u.interval.intervalID);
261
                break;
262
            case IntervalAnnotation:
263
                fprintf(outfile,  "%15s ???  \"%s\" (%d)\n", " ", record->u.annotation.description, record->u.annotation.intervalID);
264
                break;
265
            case DynamicString:
266
                fprintf(outfile,  "%15s ???  \"%s\" (%d)\n", " ", record->u.annotation_dynamic.description, record->u.annotation_dynamic.intervalID);
267
                free(record->u.annotation_dynamic.description);
268
                break;
269
        }
270
    }
271
}
272
273
void serializeTelemetryBuffer(FILE *outfile)
274
{
275
    unsigned int serializationEnd = recordBufferIndex;
276
    unsigned int serializationStart = lastSerializedIndex;
277
278
    if(serializationEnd < serializationStart) {
279
        serializeTelemetryBufferRange(outfile, serializationStart, RECORD_BUFFER_SIZE);
280
        serializeTelemetryBufferRange(outfile, 0, serializationEnd);
281
    } else {
282
        serializeTelemetryBufferRange(outfile, serializationStart, serializationEnd);
283
    }
284
285
    lastSerializedIndex = serializationEnd;
286
}
287
288
void *backgroundSerialization(void *outfile)
289
{
290
    while(continueBackgroundSerialization) {
291
        MVM_sleep(500);
292
        serializeTelemetryBuffer((FILE *)outfile);
293
    }
294
295
    fclose((FILE *)outfile);
296
297
    return NULL;
298
}
299
300
MVM_PUBLIC void MVM_telemetry_init(FILE *outfile)
301
{
302
    struct TelemetryRecord *calibrationRecord;
303
    struct TelemetryRecord *epochRecord;
304
    int threadCreateError;
305
306
    telemetry_active = 1;
307
308
    calibrateTSC(outfile);
309
310
    calibrationRecord = newRecord();
311
    calibrationRecord->u.calibration.ticksPerSecond = ticksPerSecond;
312
    calibrationRecord->recordType = Calibration;
313
314
    epochRecord = newRecord();
315
    READ_TSC(epochRecord->u.epoch.time)
316
    epochRecord->recordType = Epoch;
317
318
    beginningEpoch = epochRecord->u.epoch.time;
319
320
    threadCreateError = uv_thread_create(&backgroundSerializationThread, (uv_thread_cb)backgroundSerialization, (void *)outfile);
321
    if (threadCreateError != 0)  {
322
        telemetry_active = 0;
323
324
        fprintf(stderr, "MoarVM: Could not initialize telemetry: %s\n", uv_strerror(threadCreateError));
325
    }
326
}
327
328
MVM_PUBLIC void MVM_telemetry_finish()
329
{
330
    continueBackgroundSerialization = 0;
331
    uv_thread_join(&backgroundSerializationThread);
332
}
333
334
#else
335
336
5.17k
MVM_PUBLIC void MVM_telemetry_timestamp(MVMThreadContext *threadID, const char *description) { }
337
338
6.08k
MVM_PUBLIC unsigned int MVM_telemetry_interval_start(MVMThreadContext *threadID, const char *description) { return 0; }
339
5.93k
MVM_PUBLIC void MVM_telemetry_interval_stop(MVMThreadContext *threadID, int intervalID, const char *description) { }
340
5.16k
MVM_PUBLIC void MVM_telemetry_interval_annotate(uintptr_t subject, int intervalID, const char *description) { }
341
3
MVM_PUBLIC void MVM_telemetry_interval_annotate_dynamic(uintptr_t subject, int intervalID, char *description) { }
342
343
0
MVM_PUBLIC void MVM_telemetry_init(FILE *outfile) { }
344
0
MVM_PUBLIC void MVM_telemetry_finish() { }
345
346
#endif