Import Debian changes 20180207-1
[hcoop/debian/mlton.git] / runtime / gc / profiling.c
1 /* Copyright (C) 2011-2012 Matthew Fluet.
2 * Copyright (C) 1999-2007 Henry Cejtin, Matthew Fluet, Suresh
3 * Jagannathan, and Stephen Weeks.
4 * Copyright (C) 1997-2000 NEC Research Institute.
5 *
6 * MLton is released under a BSD-style license.
7 * See the file MLton-LICENSE for details.
8 */
9
10 GC_profileMasterIndex sourceIndexToProfileMasterIndex (GC_state s,
11 GC_sourceIndex i)
12 {
13 GC_profileMasterIndex pmi;
14 pmi = s->sourceMaps.sources[i].sourceNameIndex + s->sourceMaps.sourcesLength;
15 if (DEBUG_PROFILE)
16 fprintf (stderr, "%"PRIu32" = sourceIndexToProfileMasterIndex ("FMTSI")\n", pmi, i);
17 return pmi;
18 }
19
20 GC_sourceNameIndex profileMasterIndexToSourceNameIndex (GC_state s,
21 GC_profileMasterIndex i) {
22 assert (i >= s->sourceMaps.sourcesLength);
23 return i - s->sourceMaps.sourcesLength;
24 }
25
26 char* profileIndexSourceName (GC_state s, GC_sourceIndex i) {
27 char* res;
28
29 if (i < s->sourceMaps.sourcesLength)
30 res = getSourceName (s, i);
31 else
32 res = s->sourceMaps.sourceNames[profileMasterIndexToSourceNameIndex (s, i)];
33 return res;
34 }
35
36 GC_profileStack getProfileStackInfo (GC_state s, GC_profileMasterIndex i) {
37 assert (s->profiling.data != NULL);
38 return &(s->profiling.data->stack[i]);
39 }
40
41 static int profileDepth = 0;
42
43 static void profileIndent (void) {
44 int i;
45
46 for (i = 0; i < profileDepth; ++i)
47 fprintf (stderr, " ");
48 }
49
50 void addToStackForProfiling (GC_state s, GC_profileMasterIndex i) {
51 GC_profileData p;
52 GC_profileStack ps;
53
54 p = s->profiling.data;
55 ps = getProfileStackInfo (s, i);
56 if (DEBUG_PROFILE)
57 fprintf (stderr, "adding %s to stack lastTotal = %"PRIuMAX" lastTotalGC = %"PRIuMAX"\n",
58 getSourceName (s, i),
59 (uintmax_t)p->total,
60 (uintmax_t)p->totalGC);
61 ps->lastTotal = p->total;
62 ps->lastTotalGC = p->totalGC;
63 }
64
65 void enterSourceForProfiling (GC_state s, GC_profileMasterIndex i) {
66 GC_profileStack ps;
67
68 ps = getProfileStackInfo (s, i);
69 // assert (ps->numOccurrences >= 0);
70 ps->numOccurrences++;
71 if (1 == ps->numOccurrences) {
72 addToStackForProfiling (s, i);
73 }
74 }
75
76 void enterForProfiling (GC_state s, GC_sourceSeqIndex sourceSeqIndex) {
77 uint32_t i;
78 GC_sourceIndex sourceIndex;
79 uint32_t *sourceSeq;
80
81 if (DEBUG_PROFILE)
82 fprintf (stderr, "enterForProfiling ("FMTSSI")\n", sourceSeqIndex);
83 assert (s->profiling.stack);
84 assert (sourceSeqIndex < s->sourceMaps.sourceSeqsLength);
85 sourceSeq = s->sourceMaps.sourceSeqs[sourceSeqIndex];
86 for (i = 1; i <= sourceSeq[0]; i++) {
87 sourceIndex = sourceSeq[i];
88 if (DEBUG_ENTER_LEAVE or DEBUG_PROFILE) {
89 profileIndent ();
90 fprintf (stderr, "(entering %s\n",
91 getSourceName (s, sourceIndex));
92 profileDepth++;
93 }
94 enterSourceForProfiling (s, (GC_profileMasterIndex)sourceIndex);
95 enterSourceForProfiling (s, sourceIndexToProfileMasterIndex (s, sourceIndex));
96 }
97 }
98
99 void enterFrameForProfiling (GC_state s, GC_frameIndex i) {
100 enterForProfiling (s, s->sourceMaps.frameSources[i]);
101 }
102
103 void GC_profileEnter (GC_state s) {
104 enterForProfiling (s, getCachedStackTopFrameSourceSeqIndex (s));
105 }
106
107 void removeFromStackForProfiling (GC_state s, GC_profileMasterIndex i) {
108 GC_profileData p;
109 GC_profileStack ps;
110
111 p = s->profiling.data;
112 ps = getProfileStackInfo (s, i);
113 if (DEBUG_PROFILE)
114 fprintf (stderr, "removing %s from stack ticksInc = %"PRIuMAX" ticksGCInc = %"PRIuMAX"\n",
115 profileIndexSourceName (s, i),
116 (uintmax_t)(p->total - ps->lastTotal),
117 (uintmax_t)(p->totalGC - ps->lastTotalGC));
118 ps->ticks += p->total - ps->lastTotal;
119 ps->ticksGC += p->totalGC - ps->lastTotalGC;
120 }
121
122 void leaveSourceForProfiling (GC_state s, GC_profileMasterIndex i) {
123 GC_profileStack ps;
124
125 ps = getProfileStackInfo (s, i);
126 assert (ps->numOccurrences > 0);
127 ps->numOccurrences--;
128 if (0 == ps->numOccurrences)
129 removeFromStackForProfiling (s, i);
130 }
131
132 void leaveForProfiling (GC_state s, GC_sourceSeqIndex sourceSeqIndex) {
133 uint32_t i;
134 GC_sourceIndex sourceIndex;
135 uint32_t *sourceSeq;
136
137 if (DEBUG_PROFILE)
138 fprintf (stderr, "leaveForProfiling ("FMTSSI")\n", sourceSeqIndex);
139 assert (s->profiling.stack);
140 assert (sourceSeqIndex < s->sourceMaps.sourceSeqsLength);
141 sourceSeq = s->sourceMaps.sourceSeqs[sourceSeqIndex];
142 for (i = sourceSeq[0]; i > 0; i--) {
143 sourceIndex = sourceSeq[i];
144 if (DEBUG_ENTER_LEAVE or DEBUG_PROFILE) {
145 profileDepth--;
146 profileIndent ();
147 fprintf (stderr, "leaving %s)\n",
148 getSourceName (s, sourceIndex));
149 }
150 leaveSourceForProfiling (s, (GC_profileMasterIndex)sourceIndex);
151 leaveSourceForProfiling (s, sourceIndexToProfileMasterIndex (s, sourceIndex));
152 }
153 }
154
155 void GC_profileLeave (GC_state s) {
156 leaveForProfiling (s, getCachedStackTopFrameSourceSeqIndex (s));
157 }
158
159
160 void incForProfiling (GC_state s, size_t amount, GC_sourceSeqIndex sourceSeqIndex) {
161 uint32_t *sourceSeq;
162 GC_sourceIndex topSourceIndex;
163
164 if (DEBUG_PROFILE)
165 fprintf (stderr, "incForProfiling (%"PRIuMAX", "FMTSSI")\n",
166 (uintmax_t)amount, sourceSeqIndex);
167 assert (sourceSeqIndex < s->sourceMaps.sourceSeqsLength);
168 sourceSeq = s->sourceMaps.sourceSeqs[sourceSeqIndex];
169 topSourceIndex =
170 sourceSeq[0] > 0
171 ? sourceSeq[sourceSeq[0]]
172 : SOURCES_INDEX_UNKNOWN;
173 if (DEBUG_PROFILE) {
174 profileIndent ();
175 fprintf (stderr, "bumping %s by %"PRIuMAX"\n",
176 getSourceName (s, topSourceIndex), (uintmax_t)amount);
177 }
178 s->profiling.data->countTop[topSourceIndex] += amount;
179 s->profiling.data->countTop[sourceIndexToProfileMasterIndex (s, topSourceIndex)] += amount;
180 if (s->profiling.stack)
181 enterForProfiling (s, sourceSeqIndex);
182 if (SOURCES_INDEX_GC == topSourceIndex)
183 s->profiling.data->totalGC += amount;
184 else
185 s->profiling.data->total += amount;
186 if (s->profiling.stack)
187 leaveForProfiling (s, sourceSeqIndex);
188 }
189
190 void GC_profileInc (GC_state s, size_t amount) {
191 if (DEBUG_PROFILE)
192 fprintf (stderr, "GC_profileInc (%"PRIuMAX")\n", (uintmax_t)amount);
193 incForProfiling (s, amount,
194 s->amInGC
195 ? SOURCE_SEQ_GC
196 : getCachedStackTopFrameSourceSeqIndex (s));
197 }
198
199 void GC_profileAllocInc (GC_state s, size_t amount) {
200 if (s->profiling.isOn and (PROFILE_ALLOC == s->profiling.kind)) {
201 if (DEBUG_PROFILE)
202 fprintf (stderr, "GC_profileAllocInc (%"PRIuMAX")\n", (uintmax_t)amount);
203 GC_profileInc (s, amount);
204 }
205 }
206
207 GC_profileData profileMalloc (GC_state s) {
208 GC_profileData p;
209 uint32_t profileMasterLength;
210
211 p = (GC_profileData)(malloc_safe (sizeof(*p)));
212 p->total = 0;
213 p->totalGC = 0;
214 profileMasterLength = s->sourceMaps.sourcesLength + s->sourceMaps.sourceNamesLength;
215 p->countTop = (uintmax_t*)(calloc_safe(profileMasterLength, sizeof(*(p->countTop))));
216 if (s->profiling.stack)
217 p->stack =
218 (struct GC_profileStack *)
219 (calloc_safe(profileMasterLength, sizeof(*(p->stack))));
220 if (DEBUG_PROFILE)
221 fprintf (stderr, FMTPTR" = profileMalloc ()\n", (uintptr_t)p);
222 return p;
223 }
224
225 GC_profileData GC_profileMalloc (GC_state s) {
226 return profileMalloc (s);
227 }
228
229 void profileFree (GC_state s, GC_profileData p) {
230 if (DEBUG_PROFILE)
231 fprintf (stderr, "profileFree ("FMTPTR")\n", (uintptr_t)p);
232 free (p->countTop);
233 if (s->profiling.stack)
234 free (p->stack);
235 free (p);
236 }
237
238 void GC_profileFree (GC_state s, GC_profileData p) {
239 profileFree (s, p);
240 }
241
242 void writeProfileCount (GC_state s, FILE *f,
243 GC_profileData p, GC_profileMasterIndex i) {
244 writeUintmaxU (f, p->countTop[i]);
245 if (s->profiling.stack) {
246 GC_profileStack ps;
247
248 ps = &(p->stack[i]);
249 writeString (f, " ");
250 writeUintmaxU (f, ps->ticks);
251 writeString (f, " ");
252 writeUintmaxU (f, ps->ticksGC);
253 }
254 writeNewline (f);
255 }
256
257 void profileWrite (GC_state s, GC_profileData p, const char *fileName) {
258 FILE *f;
259 const char* kind;
260
261 if (DEBUG_PROFILE)
262 fprintf (stderr, "profileWrite("FMTPTR",%s)\n", (uintptr_t)p, fileName);
263 f = fopen_safe (fileName, "wb");
264 writeString (f, "MLton prof\n");
265 switch (s->profiling.kind) {
266 case PROFILE_ALLOC:
267 kind = "alloc\n";
268 break;
269 case PROFILE_COUNT:
270 kind = "count\n";
271 break;
272 case PROFILE_NONE:
273 die ("impossible PROFILE_NONE");
274 // break;
275 case PROFILE_TIME_FIELD:
276 kind = "time\n";
277 break;
278 case PROFILE_TIME_LABEL:
279 kind = "time\n";
280 break;
281 default:
282 kind = "";
283 assert (FALSE);
284 }
285 writeString (f, kind);
286 writeString (f, s->profiling.stack ? "stack\n" : "current\n");
287 writeUint32X (f, s->magic);
288 writeNewline (f);
289 writeUintmaxU (f, p->total);
290 writeString (f, " ");
291 writeUintmaxU (f, p->totalGC);
292 writeNewline (f);
293 writeUint32U (f, s->sourceMaps.sourcesLength);
294 writeNewline (f);
295 for (GC_sourceIndex i = 0; i < s->sourceMaps.sourcesLength; i++)
296 writeProfileCount (s, f, p,
297 (GC_profileMasterIndex)i);
298 writeUint32U (f, s->sourceMaps.sourceNamesLength);
299 writeNewline (f);
300 for (GC_sourceNameIndex i = 0; i < s->sourceMaps.sourceNamesLength; i++)
301 writeProfileCount (s, f, p,
302 (GC_profileMasterIndex)(i + s->sourceMaps.sourcesLength));
303 fclose_safe (f);
304 }
305
306 void GC_profileWrite (GC_state s, GC_profileData p, NullString8_t fileName) {
307 profileWrite (s, p, (const char*)fileName);
308 }
309
310 void setProfTimer (suseconds_t usec) {
311 struct itimerval iv;
312
313 iv.it_interval.tv_sec = 0;
314 iv.it_interval.tv_usec = usec;
315 iv.it_value.tv_sec = 0;
316 iv.it_value.tv_usec = usec;
317 unless (0 == setitimer (ITIMER_PROF, &iv, NULL))
318 die ("setProfTimer: setitimer failed");
319 }
320
321 #if not HAS_TIME_PROFILING
322
323 /* No time profiling on this platform. There is a check in
324 * mlton/main/main.fun to make sure that time profiling is never
325 * turned on.
326 */
327 __attribute__ ((noreturn))
328 void initProfilingTime (__attribute__ ((unused)) GC_state s) {
329 die ("no time profiling");
330 }
331
332 #else
333
334 static GC_state handleSigProfState;
335
336 void GC_handleSigProf (code_pointer pc) {
337 GC_frameIndex frameIndex;
338 GC_state s;
339 GC_sourceSeqIndex sourceSeqsIndex;
340
341 s = handleSigProfState;
342 if (DEBUG_PROFILE)
343 fprintf (stderr, "GC_handleSigProf ("FMTPTR")\n", (uintptr_t)pc);
344 if (s->amInGC)
345 sourceSeqsIndex = SOURCE_SEQ_GC;
346 else {
347 frameIndex = getCachedStackTopFrameIndex (s);
348 if (C_FRAME == s->frameLayouts[frameIndex].kind)
349 sourceSeqsIndex = s->sourceMaps.frameSources[frameIndex];
350 else {
351 if (PROFILE_TIME_LABEL == s->profiling.kind) {
352 uint32_t start, end, i;
353
354 /* Binary search labels to find which method contains PC */
355 start = 0;
356 end = s->sourceMaps.sourceLabelsLength;
357 while (end - start > 1) {
358 i = (start+end)/2;
359 if ((uintptr_t)s->sourceMaps.sourceLabels[i].label <= (uintptr_t)pc)
360 start = i;
361 else
362 end = i;
363 }
364 i = start;
365
366 /* The last label is dead code. Any address past it is thus unknown.
367 * The first label is before all SML code. Before it is also unknown.
368 */
369 if (i-1 == s->sourceMaps.sourceLabelsLength ||
370 (i == 0 &&
371 (uintptr_t)pc < (uintptr_t)s->sourceMaps.sourceLabels[i].label)) {
372 if (DEBUG_PROFILE)
373 fprintf (stderr, "pc out of bounds\n");
374 sourceSeqsIndex = SOURCE_SEQ_UNKNOWN;
375 } else {
376 sourceSeqsIndex = s->sourceMaps.sourceLabels[start].sourceSeqIndex;
377 }
378 } else {
379 sourceSeqsIndex = s->sourceMaps.curSourceSeqsIndex;
380 }
381 }
382 }
383 incForProfiling (s, 1, sourceSeqsIndex);
384 }
385
386 static void initProfilingTime (GC_state s) {
387 struct sigaction sa;
388
389 s->profiling.data = profileMalloc (s);
390 if (PROFILE_TIME_LABEL == s->profiling.kind) {
391 initSourceLabels (s);
392 } else {
393 s->sourceMaps.curSourceSeqsIndex = SOURCE_SEQ_UNKNOWN;
394 }
395 /*
396 * Install catcher, which handles SIGPROF and calls MLton_Profile_inc.
397 *
398 * One thing I should point out that I discovered the hard way: If
399 * the call to sigaction does NOT specify the SA_ONSTACK flag, then
400 * even if you have called sigaltstack(), it will NOT switch stacks,
401 * so you will probably die. Worse, if the call to sigaction DOES
402 * have SA_ONSTACK and you have NOT called sigaltstack(), it still
403 * switches stacks (to location 0) and you die of a SEGV. Thus the
404 * sigaction() call MUST occur after the call to sigaltstack(), and
405 * in order to have profiling cover as much as possible, you want it
406 * to occur right after the sigaltstack() call.
407 */
408 handleSigProfState = s;
409 sigemptyset (&sa.sa_mask);
410 GC_setSigProfHandler (&sa);
411 unless (sigaction (SIGPROF, &sa, NULL) == 0)
412 diee ("initProfilingTime: sigaction failed");
413 /* Start the SIGPROF timer. */
414 setProfTimer (10000);
415 }
416
417 #endif
418
419 /* atexitForProfiling is for writing out an mlmon.out file even if the C code
420 * terminates abnormally, e.g. due to running out of memory. It will
421 * only run if the usual SML profile atExit cleanup code did not
422 * manage to run.
423 */
424 static GC_state atexitForProfilingState;
425
426 void atexitForProfiling (void) {
427 GC_state s;
428
429 if (DEBUG_PROFILE)
430 fprintf (stderr, "atexitForProfiling ()\n");
431 s = atexitForProfilingState;
432 if (s->profiling.isOn) {
433 fprintf (stderr, "profiling is on\n");
434 profileWrite (s, s->profiling.data, "mlmon.out");
435 }
436 }
437
438 void initProfiling (GC_state s) {
439 if (PROFILE_NONE == s->profiling.kind)
440 s->profiling.isOn = FALSE;
441 else {
442 s->profiling.isOn = TRUE;
443 assert (s->sourceMaps.frameSourcesLength == s->frameLayoutsLength);
444 switch (s->profiling.kind) {
445 case PROFILE_ALLOC:
446 case PROFILE_COUNT:
447 s->profiling.data = profileMalloc (s);
448 break;
449 case PROFILE_NONE:
450 die ("impossible PROFILE_NONE");
451 // break;
452 case PROFILE_TIME_FIELD:
453 case PROFILE_TIME_LABEL:
454 initProfilingTime (s);
455 break;
456 default:
457 assert (FALSE);
458 }
459 atexitForProfilingState = s;
460 atexit (atexitForProfiling);
461 }
462 }
463
464 void GC_profileDone (GC_state s) {
465 GC_profileData p;
466 GC_profileMasterIndex profileMasterIndex;
467
468 if (DEBUG_PROFILE)
469 fprintf (stderr, "GC_profileDone ()\n");
470 assert (s->profiling.isOn);
471 if (PROFILE_TIME_FIELD == s->profiling.kind
472 or PROFILE_TIME_LABEL == s->profiling.kind)
473 setProfTimer (0);
474 s->profiling.isOn = FALSE;
475 p = s->profiling.data;
476 if (s->profiling.stack) {
477 uint32_t profileMasterLength =
478 s->sourceMaps.sourcesLength + s->sourceMaps.sourceNamesLength;
479 for (profileMasterIndex = 0;
480 profileMasterIndex < profileMasterLength;
481 profileMasterIndex++) {
482 if (p->stack[profileMasterIndex].numOccurrences > 0) {
483 if (DEBUG_PROFILE)
484 fprintf (stderr, "done leaving %s\n",
485 profileIndexSourceName (s, profileMasterIndex));
486 removeFromStackForProfiling (s, profileMasterIndex);
487 }
488 }
489 }
490 }
491
492
493 GC_profileData GC_getProfileCurrent (GC_state s) {
494 return s->profiling.data;
495 }
496 void GC_setProfileCurrent (GC_state s, GC_profileData p) {
497 s->profiling.data = p;
498 }