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.
6 * MLton is released under a BSD-style license.
7 * See the file MLton-LICENSE for details.
10 GC_profileMasterIndex
sourceIndexToProfileMasterIndex (GC_state s
,
13 GC_profileMasterIndex pmi
;
14 pmi
= s
->sourceMaps
.sources
[i
].sourceNameIndex
+ s
->sourceMaps
.sourcesLength
;
16 fprintf (stderr
, "%"PRIu32
" = sourceIndexToProfileMasterIndex ("FMTSI
")\n", pmi
, i
);
20 GC_sourceNameIndex
profileMasterIndexToSourceNameIndex (GC_state s
,
21 GC_profileMasterIndex i
) {
22 assert (i
>= s
->sourceMaps
.sourcesLength
);
23 return i
- s
->sourceMaps
.sourcesLength
;
26 char* profileIndexSourceName (GC_state s
, GC_sourceIndex i
) {
29 if (i
< s
->sourceMaps
.sourcesLength
)
30 res
= getSourceName (s
, i
);
32 res
= s
->sourceMaps
.sourceNames
[profileMasterIndexToSourceNameIndex (s
, i
)];
36 GC_profileStack
getProfileStackInfo (GC_state s
, GC_profileMasterIndex i
) {
37 assert (s
->profiling
.data
!= NULL
);
38 return &(s
->profiling
.data
->stack
[i
]);
41 static int profileDepth
= 0;
43 static void profileIndent (void) {
46 for (i
= 0; i
< profileDepth
; ++i
)
47 fprintf (stderr
, " ");
50 void addToStackForProfiling (GC_state s
, GC_profileMasterIndex i
) {
54 p
= s
->profiling
.data
;
55 ps
= getProfileStackInfo (s
, i
);
57 fprintf (stderr
, "adding %s to stack lastTotal = %"PRIuMAX
" lastTotalGC = %"PRIuMAX
"\n",
60 (uintmax_t)p
->totalGC
);
61 ps
->lastTotal
= p
->total
;
62 ps
->lastTotalGC
= p
->totalGC
;
65 void enterSourceForProfiling (GC_state s
, GC_profileMasterIndex i
) {
68 ps
= getProfileStackInfo (s
, i
);
69 // assert (ps->numOccurrences >= 0);
71 if (1 == ps
->numOccurrences
) {
72 addToStackForProfiling (s
, i
);
76 void enterForProfiling (GC_state s
, GC_sourceSeqIndex sourceSeqIndex
) {
78 GC_sourceIndex sourceIndex
;
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
) {
90 fprintf (stderr
, "(entering %s\n",
91 getSourceName (s
, sourceIndex
));
94 enterSourceForProfiling (s
, (GC_profileMasterIndex
)sourceIndex
);
95 enterSourceForProfiling (s
, sourceIndexToProfileMasterIndex (s
, sourceIndex
));
99 void enterFrameForProfiling (GC_state s
, GC_frameIndex i
) {
100 enterForProfiling (s
, s
->sourceMaps
.frameSources
[i
]);
103 void GC_profileEnter (GC_state s
) {
104 enterForProfiling (s
, getCachedStackTopFrameSourceSeqIndex (s
));
107 void removeFromStackForProfiling (GC_state s
, GC_profileMasterIndex i
) {
111 p
= s
->profiling
.data
;
112 ps
= getProfileStackInfo (s
, i
);
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
;
122 void leaveSourceForProfiling (GC_state s
, GC_profileMasterIndex i
) {
125 ps
= getProfileStackInfo (s
, i
);
126 assert (ps
->numOccurrences
> 0);
127 ps
->numOccurrences
--;
128 if (0 == ps
->numOccurrences
)
129 removeFromStackForProfiling (s
, i
);
132 void leaveForProfiling (GC_state s
, GC_sourceSeqIndex sourceSeqIndex
) {
134 GC_sourceIndex sourceIndex
;
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
) {
147 fprintf (stderr
, "leaving %s)\n",
148 getSourceName (s
, sourceIndex
));
150 leaveSourceForProfiling (s
, (GC_profileMasterIndex
)sourceIndex
);
151 leaveSourceForProfiling (s
, sourceIndexToProfileMasterIndex (s
, sourceIndex
));
155 void GC_profileLeave (GC_state s
) {
156 leaveForProfiling (s
, getCachedStackTopFrameSourceSeqIndex (s
));
160 void incForProfiling (GC_state s
, size_t amount
, GC_sourceSeqIndex sourceSeqIndex
) {
162 GC_sourceIndex topSourceIndex
;
165 fprintf (stderr
, "incForProfiling (%"PRIuMAX
", "FMTSSI
")\n",
166 (uintmax_t)amount
, sourceSeqIndex
);
167 assert (sourceSeqIndex
< s
->sourceMaps
.sourceSeqsLength
);
168 sourceSeq
= s
->sourceMaps
.sourceSeqs
[sourceSeqIndex
];
171 ? sourceSeq
[sourceSeq
[0]]
172 : SOURCES_INDEX_UNKNOWN
;
175 fprintf (stderr
, "bumping %s by %"PRIuMAX
"\n",
176 getSourceName (s
, topSourceIndex
), (uintmax_t)amount
);
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
;
185 s
->profiling
.data
->total
+= amount
;
186 if (s
->profiling
.stack
)
187 leaveForProfiling (s
, sourceSeqIndex
);
190 void GC_profileInc (GC_state s
, size_t amount
) {
192 fprintf (stderr
, "GC_profileInc (%"PRIuMAX
")\n", (uintmax_t)amount
);
193 incForProfiling (s
, amount
,
196 : getCachedStackTopFrameSourceSeqIndex (s
));
199 void GC_profileAllocInc (GC_state s
, size_t amount
) {
200 if (s
->profiling
.isOn
and (PROFILE_ALLOC
== s
->profiling
.kind
)) {
202 fprintf (stderr
, "GC_profileAllocInc (%"PRIuMAX
")\n", (uintmax_t)amount
);
203 GC_profileInc (s
, amount
);
207 GC_profileData
profileMalloc (GC_state s
) {
209 uint32_t profileMasterLength
;
211 p
= (GC_profileData
)(malloc_safe (sizeof(*p
)));
214 profileMasterLength
= s
->sourceMaps
.sourcesLength
+ s
->sourceMaps
.sourceNamesLength
;
215 p
->countTop
= (uintmax_t*)(calloc_safe(profileMasterLength
, sizeof(*(p
->countTop
))));
216 if (s
->profiling
.stack
)
218 (struct GC_profileStack
*)
219 (calloc_safe(profileMasterLength
, sizeof(*(p
->stack
))));
221 fprintf (stderr
, FMTPTR
" = profileMalloc ()\n", (uintptr_t)p
);
225 GC_profileData
GC_profileMalloc (GC_state s
) {
226 return profileMalloc (s
);
229 void profileFree (GC_state s
, GC_profileData p
) {
231 fprintf (stderr
, "profileFree ("FMTPTR
")\n", (uintptr_t)p
);
233 if (s
->profiling
.stack
)
238 void GC_profileFree (GC_state s
, GC_profileData p
) {
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
) {
249 writeString (f
, " ");
250 writeUintmaxU (f
, ps
->ticks
);
251 writeString (f
, " ");
252 writeUintmaxU (f
, ps
->ticksGC
);
257 void profileWrite (GC_state s
, GC_profileData p
, const char *fileName
) {
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
) {
273 die ("impossible PROFILE_NONE");
275 case PROFILE_TIME_FIELD
:
278 case PROFILE_TIME_LABEL
:
285 writeString (f
, kind
);
286 writeString (f
, s
->profiling
.stack
? "stack\n" : "current\n");
287 writeUint32X (f
, s
->magic
);
289 writeUintmaxU (f
, p
->total
);
290 writeString (f
, " ");
291 writeUintmaxU (f
, p
->totalGC
);
293 writeUint32U (f
, s
->sourceMaps
.sourcesLength
);
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
);
300 for (GC_sourceNameIndex i
= 0; i
< s
->sourceMaps
.sourceNamesLength
; i
++)
301 writeProfileCount (s
, f
, p
,
302 (GC_profileMasterIndex
)(i
+ s
->sourceMaps
.sourcesLength
));
306 void GC_profileWrite (GC_state s
, GC_profileData p
, NullString8_t fileName
) {
307 profileWrite (s
, p
, (const char*)fileName
);
310 void setProfTimer (suseconds_t usec
) {
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");
321 #if not HAS_TIME_PROFILING
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
327 __attribute__ ((noreturn
))
328 void initProfilingTime (__attribute__ ((unused
)) GC_state s
) {
329 die ("no time profiling");
334 static GC_state handleSigProfState
;
336 void GC_handleSigProf (code_pointer pc
) {
337 GC_frameIndex frameIndex
;
339 GC_sourceSeqIndex sourceSeqsIndex
;
341 s
= handleSigProfState
;
343 fprintf (stderr
, "GC_handleSigProf ("FMTPTR
")\n", (uintptr_t)pc
);
345 sourceSeqsIndex
= SOURCE_SEQ_GC
;
347 frameIndex
= getCachedStackTopFrameIndex (s
);
348 if (C_FRAME
== s
->frameLayouts
[frameIndex
].kind
)
349 sourceSeqsIndex
= s
->sourceMaps
.frameSources
[frameIndex
];
351 if (PROFILE_TIME_LABEL
== s
->profiling
.kind
) {
352 uint32_t start
, end
, i
;
354 /* Binary search labels to find which method contains PC */
356 end
= s
->sourceMaps
.sourceLabelsLength
;
357 while (end
- start
> 1) {
359 if ((uintptr_t)s
->sourceMaps
.sourceLabels
[i
].label
<= (uintptr_t)pc
)
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.
369 if (i
-1 == s
->sourceMaps
.sourceLabelsLength
||
371 (uintptr_t)pc
< (uintptr_t)s
->sourceMaps
.sourceLabels
[i
].label
)) {
373 fprintf (stderr
, "pc out of bounds\n");
374 sourceSeqsIndex
= SOURCE_SEQ_UNKNOWN
;
376 sourceSeqsIndex
= s
->sourceMaps
.sourceLabels
[start
].sourceSeqIndex
;
379 sourceSeqsIndex
= s
->sourceMaps
.curSourceSeqsIndex
;
383 incForProfiling (s
, 1, sourceSeqsIndex
);
386 static void initProfilingTime (GC_state s
) {
389 s
->profiling
.data
= profileMalloc (s
);
390 if (PROFILE_TIME_LABEL
== s
->profiling
.kind
) {
391 initSourceLabels (s
);
393 s
->sourceMaps
.curSourceSeqsIndex
= SOURCE_SEQ_UNKNOWN
;
396 * Install catcher, which handles SIGPROF and calls MLton_Profile_inc.
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.
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);
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
424 static GC_state atexitForProfilingState
;
426 void atexitForProfiling (void) {
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");
438 void initProfiling (GC_state s
) {
439 if (PROFILE_NONE
== s
->profiling
.kind
)
440 s
->profiling
.isOn
= FALSE
;
442 s
->profiling
.isOn
= TRUE
;
443 assert (s
->sourceMaps
.frameSourcesLength
== s
->frameLayoutsLength
);
444 switch (s
->profiling
.kind
) {
447 s
->profiling
.data
= profileMalloc (s
);
450 die ("impossible PROFILE_NONE");
452 case PROFILE_TIME_FIELD
:
453 case PROFILE_TIME_LABEL
:
454 initProfilingTime (s
);
459 atexitForProfilingState
= s
;
460 atexit (atexitForProfiling
);
464 void GC_profileDone (GC_state s
) {
466 GC_profileMasterIndex profileMasterIndex
;
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
)
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) {
484 fprintf (stderr
, "done leaving %s\n",
485 profileIndexSourceName (s
, profileMasterIndex
));
486 removeFromStackForProfiling (s
, profileMasterIndex
);
493 GC_profileData
GC_getProfileCurrent (GC_state s
) {
494 return s
->profiling
.data
;
496 void GC_setProfileCurrent (GC_state s
, GC_profileData p
) {
497 s
->profiling
.data
= p
;