1 /*
2 Copyright (C) 2009-2014, Parrot Foundation.
3 
4 =head1 NAME
5 
6 src/runcore/profiling.c
7 
8 =head1 DESCRIPTION
9 
10 Functions controlling Parrot's profiling runcore.
11 
12 =head2 Functions
13 
14 =over 4
15 
16 =cut
17 
18 */
19 
20 #include "parrot/runcore_api.h"
21 #include "parrot/extend.h"
22 #include "parrot/runcore_profiling.h"
23 #include "parrot/oplib/core_ops.h"
24 
25 #include "profiling.str"
26 
27 #include "pmc/pmc_sub.h"
28 #include "pmc/pmc_callcontext.h"
29 #include "pmc/pmc_namespace.h"
30 
31 #define PPROF_VERSION 2
32 
33 #define code_start interp->code->base.data
34 #define code_end (interp->code->base.data + interp->code->base.size)
35 
36 /* HEADERIZER HFILE: include/parrot/runcore_profiling.h */
37 
38 /* HEADERIZER BEGIN: static */
39 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
40 
41 static void destroy_basic_output(PARROT_INTERP,
42     ARGIN(Parrot_profiling_runcore_t *runcore))
43         __attribute__nonnull__(2);
44 
45 static void destroy_profiling_core(PARROT_INTERP,
46     ARGIN(Parrot_profiling_runcore_t *runcore))
47         __attribute__nonnull__(1)
48         __attribute__nonnull__(2);
49 
50 PARROT_MALLOC
51 PARROT_CANNOT_RETURN_NULL
52 static char* get_filename_cstr(PARROT_INTERP,
53     ARGIN(PMC* ctx_pmc),
54     ARGIN(opcode_t *pc))
55         __attribute__nonnull__(1)
56         __attribute__nonnull__(2)
57         __attribute__nonnull__(3);
58 
59 static INTVAL get_line_num_from_cache(PARROT_INTERP,
60     ARGIN(Parrot_profiling_runcore_t *runcore),
61     ARGIN(PMC *ctx_pmc))
62         __attribute__nonnull__(1)
63         __attribute__nonnull__(2)
64         __attribute__nonnull__(3);
65 
66 PARROT_MALLOC
67 PARROT_CANNOT_RETURN_NULL
68 static char* get_ns_cstr(PARROT_INTERP, ARGIN(PMC* ctx_pmc))
69         __attribute__nonnull__(1)
70         __attribute__nonnull__(2);
71 
72 static void init_basic_output(PARROT_INTERP,
73     ARGIN(Parrot_profiling_runcore_t *runcore))
74         __attribute__nonnull__(1)
75         __attribute__nonnull__(2);
76 
77 static void init_null_output(PARROT_INTERP,
78     ARGIN(Parrot_profiling_runcore_t *runcore))
79         __attribute__nonnull__(1)
80         __attribute__nonnull__(2);
81 
82 PARROT_CAN_RETURN_NULL
83 static void * init_profiling_core(PARROT_INTERP,
84     ARGIN(Parrot_profiling_runcore_t *runcore),
85     ARGIN(opcode_t *pc))
86         __attribute__nonnull__(1)
87         __attribute__nonnull__(2)
88         __attribute__nonnull__(3);
89 
90 static void record_annotations(PARROT_INTERP,
91     ARGIN(Parrot_profiling_runcore_t *runcore),
92     ARGIN(PPROF_DATA *pprof_data),
93     ARGIN(opcode_t *pc))
94         __attribute__nonnull__(1)
95         __attribute__nonnull__(2)
96         __attribute__nonnull__(3)
97         __attribute__nonnull__(4);
98 
99 static void record_bogus_parent_runloop(PARROT_INTERP,
100     ARGIN(Parrot_profiling_runcore_t * runcore))
101         __attribute__nonnull__(1)
102         __attribute__nonnull__(2);
103 
104 static void record_ctx_info(PARROT_INTERP,
105     ARGIN(Parrot_profiling_runcore_t *runcore),
106     ARGIN(PPROF_DATA *pprof_data),
107     ARGIN(PMC* ctx_pmc),
108     ARGIN(opcode_t *pc))
109         __attribute__nonnull__(1)
110         __attribute__nonnull__(2)
111         __attribute__nonnull__(3)
112         __attribute__nonnull__(4)
113         __attribute__nonnull__(5);
114 
115 static void record_op(PARROT_INTERP,
116     ARGIN(Parrot_profiling_runcore_t *runcore),
117     ARGIN(PPROF_DATA *pprof_data),
118     ARGIN(const char *op_name),
119     INTVAL op_time,
120     INTVAL line_num)
121         __attribute__nonnull__(1)
122         __attribute__nonnull__(2)
123         __attribute__nonnull__(3)
124         __attribute__nonnull__(4);
125 
126 static void record_values_ascii_pprof(PARROT_INTERP,
127     ARGIN(Parrot_profiling_runcore_t * runcore),
128     ARGIN(PPROF_DATA *pprof_data),
129     ARGIN_NULLOK(Parrot_profiling_line type))
130         __attribute__nonnull__(2)
131         __attribute__nonnull__(3);
132 
133 static void record_version_and_cli(PARROT_INTERP,
134     ARGIN(Parrot_profiling_runcore_t *runcore),
135     ARGIN(PPROF_DATA* pprof_data))
136         __attribute__nonnull__(1)
137         __attribute__nonnull__(2)
138         __attribute__nonnull__(3);
139 
140 PARROT_WARN_UNUSED_RESULT
141 PARROT_CAN_RETURN_NULL
142 static opcode_t * runops_profiling_core(PARROT_INTERP,
143     ARGIN(Parrot_profiling_runcore_t *runcore),
144     ARGIN(opcode_t *pc))
145         __attribute__nonnull__(1)
146         __attribute__nonnull__(2)
147         __attribute__nonnull__(3);
148 
149 static void store_postop_time(PARROT_INTERP,
150     ARGIN(Parrot_profiling_runcore_t *runcore))
151         __attribute__nonnull__(1)
152         __attribute__nonnull__(2);
153 
154 #define ASSERT_ARGS_destroy_basic_output __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
155        PARROT_ASSERT_ARG(runcore))
156 #define ASSERT_ARGS_destroy_profiling_core __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
157        PARROT_ASSERT_ARG(interp) \
158     , PARROT_ASSERT_ARG(runcore))
159 #define ASSERT_ARGS_get_filename_cstr __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
160        PARROT_ASSERT_ARG(interp) \
161     , PARROT_ASSERT_ARG(ctx_pmc) \
162     , PARROT_ASSERT_ARG(pc))
163 #define ASSERT_ARGS_get_line_num_from_cache __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
164        PARROT_ASSERT_ARG(interp) \
165     , PARROT_ASSERT_ARG(runcore) \
166     , PARROT_ASSERT_ARG(ctx_pmc))
167 #define ASSERT_ARGS_get_ns_cstr __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
168        PARROT_ASSERT_ARG(interp) \
169     , PARROT_ASSERT_ARG(ctx_pmc))
170 #define ASSERT_ARGS_init_basic_output __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
171        PARROT_ASSERT_ARG(interp) \
172     , PARROT_ASSERT_ARG(runcore))
173 #define ASSERT_ARGS_init_null_output __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
174        PARROT_ASSERT_ARG(interp) \
175     , PARROT_ASSERT_ARG(runcore))
176 #define ASSERT_ARGS_init_profiling_core __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
177        PARROT_ASSERT_ARG(interp) \
178     , PARROT_ASSERT_ARG(runcore) \
179     , PARROT_ASSERT_ARG(pc))
180 #define ASSERT_ARGS_record_annotations __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
181        PARROT_ASSERT_ARG(interp) \
182     , PARROT_ASSERT_ARG(runcore) \
183     , PARROT_ASSERT_ARG(pprof_data) \
184     , PARROT_ASSERT_ARG(pc))
185 #define ASSERT_ARGS_record_bogus_parent_runloop __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
186        PARROT_ASSERT_ARG(interp) \
187     , PARROT_ASSERT_ARG(runcore))
188 #define ASSERT_ARGS_record_ctx_info __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
189        PARROT_ASSERT_ARG(interp) \
190     , PARROT_ASSERT_ARG(runcore) \
191     , PARROT_ASSERT_ARG(pprof_data) \
192     , PARROT_ASSERT_ARG(ctx_pmc) \
193     , PARROT_ASSERT_ARG(pc))
194 #define ASSERT_ARGS_record_op __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
195        PARROT_ASSERT_ARG(interp) \
196     , PARROT_ASSERT_ARG(runcore) \
197     , PARROT_ASSERT_ARG(pprof_data) \
198     , PARROT_ASSERT_ARG(op_name))
199 #define ASSERT_ARGS_record_values_ascii_pprof __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
200        PARROT_ASSERT_ARG(runcore) \
201     , PARROT_ASSERT_ARG(pprof_data))
202 #define ASSERT_ARGS_record_version_and_cli __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
203        PARROT_ASSERT_ARG(interp) \
204     , PARROT_ASSERT_ARG(runcore) \
205     , PARROT_ASSERT_ARG(pprof_data))
206 #define ASSERT_ARGS_runops_profiling_core __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
207        PARROT_ASSERT_ARG(interp) \
208     , PARROT_ASSERT_ARG(runcore) \
209     , PARROT_ASSERT_ARG(pc))
210 #define ASSERT_ARGS_store_postop_time __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
211        PARROT_ASSERT_ARG(interp) \
212     , PARROT_ASSERT_ARG(runcore))
213 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
214 /* HEADERIZER END: static */
215 
216 
217 /*
218 
219 =item C<void Parrot_runcore_profiling_init(PARROT_INTERP)>
220 
221 Register the profiling runcore with Parrot.
222 
223 =cut
224 
225 */
226 
227 void
Parrot_runcore_profiling_init(PARROT_INTERP)228 Parrot_runcore_profiling_init(PARROT_INTERP)
229 {
230     ASSERT_ARGS(Parrot_runcore_profiling_init)
231 
232     Parrot_profiling_runcore_t * const coredata =
233             mem_gc_allocate_zeroed_typed(interp, Parrot_profiling_runcore_t);
234 
235     coredata->name        = CONST_STRING(interp, "profiling");
236     coredata->id          = PARROT_PROFILING_CORE;
237     coredata->opinit      = PARROT_CORE_OPLIB_INIT;
238     coredata->runops      = (Parrot_runcore_runops_fn_t) init_profiling_core;
239     coredata->destroy     = NULL;
240     coredata->prepare_run = NULL;
241     coredata->flags       = 0;
242 
243     PARROT_RUNCORE_FUNC_TABLE_SET(coredata);
244 
245     Parrot_runcore_register(interp, (Parrot_runcore_t *) coredata);
246 }
247 
248 
249 /*
250 
251 =item C<static void * init_profiling_core(PARROT_INTERP,
252 Parrot_profiling_runcore_t *runcore, opcode_t *pc)>
253 
254 Perform initialization for the profiling runcore.
255 
256 =cut
257 
258 */
259 
260 PARROT_CAN_RETURN_NULL
261 static void *
init_profiling_core(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore),ARGIN (opcode_t * pc))262 init_profiling_core(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore), ARGIN(opcode_t *pc))
263 {
264     ASSERT_ARGS(init_profiling_core)
265 
266     STRING *output_str;
267 
268     /* initialize the runcore struct */
269     runcore->runops  = (Parrot_runcore_runops_fn_t)  runops_profiling_core;
270     runcore->destroy = (Parrot_runcore_destroy_fn_t) destroy_profiling_core;
271 
272     runcore->output.init     = NULL;
273     runcore->output.store    = NULL;
274     runcore->output.destroy  = NULL;
275     runcore->prev_ctx        = NULL;
276     runcore->profiling_flags = 0;
277     runcore->runloop_count   = 0;
278     runcore->time_size       = 32;
279     runcore->line_cache      = Parrot_hash_new_pointer_hash(interp);
280     runcore->time            = mem_gc_allocate_n_typed(interp, runcore->time_size,
281                                                     UHUGEINTVAL);
282 
283     /* figure out what format the output should be in */
284     output_str = Parrot_getenv(interp, CONST_STRING(interp, "PARROT_PROFILING_OUTPUT"));
285 
286     if (!STRING_IS_NULL(output_str)) {
287 
288         STRING * const profile_format_str = output_str;
289         if (STRING_equal(interp, profile_format_str, CONST_STRING(interp, "pprof"))) {
290             runcore->output.init    = init_basic_output;
291             runcore->output.store   = record_values_ascii_pprof;
292             runcore->output.destroy = destroy_basic_output;
293         }
294         else if (STRING_equal(interp, profile_format_str, CONST_STRING(interp, "none"))) {
295             runcore->output.init    = init_null_output;
296             runcore->output.store   = NULL;
297             runcore->output.destroy = NULL;
298         }
299         else {
300             Parrot_eprintf(interp, "'%Ss' is not a valid profiling output format.\n", output_str);
301             Parrot_eprintf(interp, "Valid values are pprof and none.  The default is pprof.\n");
302             Parrot_x_jump_out(interp, 1);
303         }
304     }
305     else {
306         runcore->output.init    = init_basic_output;
307         runcore->output.store   = record_values_ascii_pprof;
308         runcore->output.destroy = destroy_basic_output;
309     }
310 
311     RUNCORE_init(interp, runcore);
312 
313     Profiling_first_loop_SET(runcore);
314 
315     return runops_profiling_core(interp, runcore, pc);
316 }
317 
318 
319 /*
320 
321 =item C<static opcode_t * runops_profiling_core(PARROT_INTERP,
322 Parrot_profiling_runcore_t *runcore, opcode_t *pc)>
323 
324 Runs the Parrot operations starting at C<pc> until there are no more
325 operations with tracing, bounds checking and profiling enabled.
326 
327 Some ops, such as those which call VTABLE functions, spawn inner (a.k.a.
328 "inferior" or "nested") runloops.  To properly calculate the time used by these
329 ops, it's necessary to keep track of start and end of both the current op and
330 the current runloop.  The time spent by an op which spawns an inner runloop
331 is calculated as the time between the start of the op and the start of the
332 inner runloop (between A and B below), plus the time between the end of the
333 inner runloop and the end of the op (between C and D below).   The AB tracking
334 is handled after DO_OP(), using the exit_check flag to ensure that the value is
335 only calculated when leaving an inner runloop.  The CD tracking is handled by
336 C<store_postop_time>.
337 
338  ___________outer runloop_______________
339 /op        op with inner runloop     op \
340 ----   ---------------------------- ----
341 |  |   |     __inner runloop__    | |  |
342 ----   -    /    op    op     \   - ----
343                 ----  ----
344        ^    ^   |  |  |  |    ^   ^
345        |    |   ----  ----    |   |
346        A    B                 C   D
347 
348 =cut
349 
350 */
351 
352 PARROT_WARN_UNUSED_RESULT
353 PARROT_CAN_RETURN_NULL
354 static opcode_t *
runops_profiling_core(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore),ARGIN (opcode_t * pc))355 runops_profiling_core(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore),
356 ARGIN(opcode_t *pc))
357 {
358     ASSERT_ARGS(runops_profiling_core)
359 
360     opcode_t    *preop_pc;
361     const char  *preop_opname;
362     UHUGEINTVAL  op_time;
363     PPROF_DATA   pprof_data[PPROF_DATA_MAX + 1];
364 
365     runcore->runcore_start = Parrot_hires_get_time();
366 
367     /* if we're in a inner runloop, */
368     if (interp->current_runloop_level != 1)
369         store_postop_time(interp, runcore);
370 
371     record_version_and_cli(interp, runcore, (PPROF_DATA *)&pprof_data);
372 
373     while (pc) {
374         Parrot_Context *preop_ctx;
375         INTVAL          preop_line_num;
376         PMC            *preop_ctx_pmc;
377 
378         if (pc < code_start || pc >= code_end)
379             Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_OUT_OF_BOUNDS,
380                     "attempt to access code outside of current code segment");
381 
382         preop_ctx_pmc         = CURRENT_CONTEXT(interp);
383         preop_ctx             = PMC_data_typed(preop_ctx_pmc, Parrot_Context*);
384         preop_ctx->current_pc = pc;
385         preop_pc              = pc;
386         preop_opname          = interp->code->op_info_table[*pc]->name;
387         preop_line_num        = get_line_num_from_cache(interp, runcore, preop_ctx_pmc);
388 
389         Profiling_exit_check_CLEAR(runcore);
390 
391         runcore->op_start  = Parrot_hires_get_time();
392         DO_OP(pc, interp);
393         runcore->op_finish = Parrot_hires_get_time();
394 
395         if (Profiling_exit_check_TEST(runcore)) {
396             op_time  = runcore->op_finish - runcore->runcore_finish;
397             op_time += runcore->time[interp->current_runloop_level-1];
398             runcore->time[interp->current_runloop_level-1] = 0;
399         }
400         else
401             op_time = runcore->op_finish - runcore->op_start;
402 
403         /* Occasionally the ctx stays the same while the sub changes, e.g.
404          * with a call to a subclass' method. */
405         if ((runcore->prev_ctx != preop_ctx) || runcore->prev_sub != preop_ctx->current_sub)
406             record_ctx_info(interp, runcore, (PPROF_DATA *) &pprof_data, preop_ctx_pmc, preop_pc);
407 
408         if (Profiling_report_annotations_TEST(runcore) && interp->code->annotations)
409             record_annotations(interp, runcore, (PPROF_DATA *) &pprof_data, pc);
410 
411         record_op(interp, runcore, (PPROF_DATA *) &pprof_data,
412                   preop_opname, op_time, preop_line_num);
413     }
414 
415     /* make it easy to tell separate runloops apart */
416     if (interp->current_runloop_level == 1) {
417         RUNCORE_store(interp, runcore, pprof_data, PPROF_LINE_END_OF_RUNLOOP);
418         record_bogus_parent_runloop(interp, runcore);
419     }
420 
421     Profiling_exit_check_SET(runcore);
422     runcore->runcore_finish = Parrot_hires_get_time();
423     return pc;
424 }
425 
426 /*
427 
428 =item C<static void record_ctx_info(PARROT_INTERP, Parrot_profiling_runcore_t
429 *runcore, PPROF_DATA *pprof_data, PMC* ctx_pmc, opcode_t *pc)>
430 
431 If the active context has changed, record information about the new context.
432 
433 =cut
434 
435 */
436 
437 static void
record_ctx_info(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore),ARGIN (PPROF_DATA * pprof_data),ARGIN (PMC * ctx_pmc),ARGIN (opcode_t * pc))438 record_ctx_info(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore),
439 ARGIN(PPROF_DATA *pprof_data), ARGIN(PMC* ctx_pmc), ARGIN(opcode_t *pc))
440 {
441     ASSERT_ARGS(record_ctx_info)
442 
443     Parrot_Context * const ctx = PMC_data_typed(ctx_pmc, Parrot_Context *);
444 
445     if (ctx->current_sub) {
446         char * const filename_cstr = get_filename_cstr(interp, ctx_pmc, pc);
447         char * const ns_cstr       = get_ns_cstr(interp, ctx_pmc);
448 
449         pprof_data[PPROF_DATA_NAMESPACE] = (PPROF_DATA) ns_cstr;
450         pprof_data[PPROF_DATA_FILENAME]  = (PPROF_DATA) filename_cstr;
451 
452         if (Profiling_canonical_output_TEST(runcore)) {
453             pprof_data[PPROF_DATA_SUB_ADDR]  = (PPROF_DATA) 0x3;
454             pprof_data[PPROF_DATA_CTX_ADDR]  = (PPROF_DATA) 0x3;
455         }
456         else {
457             pprof_data[PPROF_DATA_SUB_ADDR]  = (PPROF_DATA) ctx->current_sub;
458             pprof_data[PPROF_DATA_CTX_ADDR]  = (PPROF_DATA) ctx;
459         }
460 
461         RUNCORE_store(interp, runcore, pprof_data, PPROF_LINE_CONTEXT_SWITCH);
462 
463         Parrot_str_free_cstring(ns_cstr);
464         Parrot_str_free_cstring(filename_cstr);
465     }
466 
467     runcore->prev_ctx = ctx;
468     runcore->prev_sub = ctx->current_sub;
469 
470 }
471 
472 /*
473 
474 =item C<static INTVAL get_line_num_from_cache(PARROT_INTERP,
475 Parrot_profiling_runcore_t *runcore, PMC *ctx_pmc)>
476 
477 Return the line number for the current context, either by fetching from cache
478 or by calculating.
479 
480 =cut
481 
482 */
483 
484 static INTVAL
get_line_num_from_cache(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore),ARGIN (PMC * ctx_pmc))485 get_line_num_from_cache(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore),
486 ARGIN(PMC *ctx_pmc))
487 {
488 
489     ASSERT_ARGS(get_line_num_from_cache)
490 
491     Parrot_Context * const ctx = PMC_data_typed(ctx_pmc, Parrot_Context *);
492 
493     INTVAL line_num = Parrot_hash_value_to_int(interp, runcore->line_cache,
494             Parrot_hash_get(interp, runcore->line_cache, ctx->current_pc));
495 
496     /* Parrot_sub_get_line_from_pc eats up about 20-30% of execution time
497      * *with* this cache in place. */
498     if (line_num == 0) {
499         line_num = Parrot_sub_get_line_from_pc(interp,
500                 Parrot_pcc_get_sub(interp, ctx_pmc), ctx->current_pc);
501         Parrot_hash_put(interp, runcore->line_cache, ctx->current_pc, (void *) line_num);
502     }
503     return line_num;
504 }
505 
506 /*
507 
508 =item C<static void record_annotations(PARROT_INTERP, Parrot_profiling_runcore_t
509 *runcore, PPROF_DATA *pprof_data, opcode_t *pc)>
510 
511 Record annotation data for the current pc, if any exist.
512 
513 =cut
514 
515 */
516 
517 static void
record_annotations(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore),ARGIN (PPROF_DATA * pprof_data),ARGIN (opcode_t * pc))518 record_annotations(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore),
519 ARGIN(PPROF_DATA *pprof_data), ARGIN(opcode_t *pc))
520 {
521 
522     ASSERT_ARGS(record_annotations)
523 
524     PMC * const annot = Parrot_pf_annotations_lookup(interp,
525             interp->code->annotations, pc - code_start + 1, NULL);
526 
527     if (!PMC_IS_NULL(annot)) {
528 
529         PMC * const iter = VTABLE_get_iter(interp, annot);
530         while (VTABLE_get_bool(interp, iter)) {
531 
532             STRING * const key      = VTABLE_shift_string(interp, iter);
533             STRING * const val      = VTABLE_get_string_keyed_str(interp, annot, key);
534             char   * const key_cstr = Parrot_str_to_cstring(interp, key);
535             char   * const val_cstr = Parrot_str_to_cstring(interp, val);
536 
537             pprof_data[PPROF_DATA_ANNOTATION_NAME]  = (PPROF_DATA) key_cstr;
538             pprof_data[PPROF_DATA_ANNOTATION_VALUE] = (PPROF_DATA) val_cstr;
539             RUNCORE_store(interp, runcore, pprof_data, PPROF_LINE_ANNOTATION);
540 
541             Parrot_str_free_cstring(key_cstr);
542             Parrot_str_free_cstring(val_cstr);
543         }
544     }
545 }
546 
547 /*
548 
549 =item C<static void record_op(PARROT_INTERP, Parrot_profiling_runcore_t
550 *runcore, PPROF_DATA *pprof_data, const char *op_name, INTVAL op_time, INTVAL
551 line_num)>
552 
553 Record profiing information about the most recently-executed op.
554 
555 =cut
556 
557 */
558 
559 static void
record_op(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore),ARGIN (PPROF_DATA * pprof_data),ARGIN (const char * op_name),INTVAL op_time,INTVAL line_num)560 record_op(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore),
561 ARGIN(PPROF_DATA *pprof_data), ARGIN(const char *op_name), INTVAL op_time,
562 INTVAL line_num)
563 {
564 
565     ASSERT_ARGS(record_op)
566 
567     if (Profiling_canonical_output_TEST(runcore))
568         pprof_data[PPROF_DATA_TIME] = 1;
569     else
570         pprof_data[PPROF_DATA_TIME] = op_time;
571 
572     pprof_data[PPROF_DATA_LINE]   = line_num;
573     pprof_data[PPROF_DATA_OPNAME] = (PPROF_DATA) op_name;
574     RUNCORE_store(interp, runcore, pprof_data, PPROF_LINE_OP);
575 }
576 
577 
578 
579 
580 /*
581 
582 =item C<static void store_postop_time(PARROT_INTERP, Parrot_profiling_runcore_t
583 *runcore)>
584 
585 Record the time spend between the end of an inner runloop and the end of an
586 outer op.
587 
588 =cut
589 
590 */
591 
592 static void
store_postop_time(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore))593 store_postop_time(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore))
594 {
595 
596     ASSERT_ARGS(store_postop_time)
597 
598     if (interp->current_runloop_level-1 >= (int)runcore->time_size) {
599         runcore->time_size *= 2;
600         runcore->time = mem_gc_realloc_n_typed(interp,
601                 runcore->time, runcore->time_size + 1, UHUGEINTVAL);
602     }
603 
604     runcore->time[interp->current_runloop_level-1] = runcore->runcore_start - runcore->op_start;
605 }
606 
607 /*
608 
609 =item C<static void record_version_and_cli(PARROT_INTERP,
610 Parrot_profiling_runcore_t *runcore, PPROF_DATA* pprof_data)>
611 
612 Record information about CLI arguments passed to the program being profiled and
613 the version of the output file format.
614 
615 Note that because of the way the parrot executable parses its options,
616 arguments passed directly to C<parrot> (such as C<--gc ms2> or C<--hash-seed
617 234>) will not appear here.
618 
619 =cut
620 
621 */
622 
623 static void
record_version_and_cli(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore),ARGIN (PPROF_DATA * pprof_data))624 record_version_and_cli(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore),
625                        ARGIN(PPROF_DATA* pprof_data))
626 {
627 
628     ASSERT_ARGS(record_version_and_cli)
629 
630     PMC    * const argv = VTABLE_get_pmc_keyed_int(interp, interp->iglobals, IGLOBALS_ARGV_LIST);
631 
632     /* argv isn't initialized until after :init (etc) subs are executed */
633     if (!PMC_IS_NULL(argv) && !Profiling_have_printed_cli_TEST(runcore)) {
634         STRING *space, *cli_args, *cli_exe, *cli_str;
635         char *cli_cstr;
636         PMC *exe_name;
637 
638         Profiling_have_printed_cli_SET(runcore);
639         exe_name = VTABLE_get_pmc_keyed_int(interp, interp->iglobals, IGLOBALS_EXECUTABLE);
640         space    = CONST_STRING(interp, " ");
641         cli_args = Parrot_str_join(interp, space, argv);
642         cli_exe  = VTABLE_get_string(interp, exe_name);
643         cli_str  = Parrot_sprintf_c(interp, "%Ss %Ss", cli_exe, cli_args);
644         cli_cstr = Parrot_str_to_cstring(interp, cli_str);
645 
646         /* CLI line won't reflect any options passed to the parrot binary. */
647         pprof_data[PPROF_DATA_CLI] = (PPROF_DATA) cli_cstr;
648         RUNCORE_store(interp, runcore, pprof_data, PPROF_LINE_CLI);
649 
650         Parrot_str_free_cstring(cli_cstr);
651     }
652 
653     if (Profiling_first_loop_TEST(runcore)) {
654         Profiling_first_loop_CLEAR(runcore);
655         pprof_data[PPROF_DATA_VERSION] = (PPROF_DATA) PPROF_VERSION;
656         RUNCORE_store(interp, runcore, pprof_data, PPROF_LINE_VERSION);
657         record_bogus_parent_runloop(interp, runcore);
658     }
659 
660 }
661 
662 /*
663 
664 =item C<static char* get_ns_cstr(PARROT_INTERP, PMC* ctx_pmc)>
665 
666 Return a C string containing a human-readable representation of the namespace
667 for C<ctx_pmc>.
668 
669 =cut
670 
671 */
672 
673 PARROT_MALLOC
674 PARROT_CANNOT_RETURN_NULL
675 static char*
get_ns_cstr(PARROT_INTERP,ARGIN (PMC * ctx_pmc))676 get_ns_cstr(PARROT_INTERP, ARGIN(PMC* ctx_pmc))
677 {
678 
679     ASSERT_ARGS(get_ns_cstr)
680 
681     STRING         *sub_name, *full_ns, *ns_separator, *tmp;
682     Parrot_Context * const ctx = PMC_data_typed(ctx_pmc, Parrot_Context *);
683     PMC            *ns = ctx->current_namespace;
684 
685     full_ns      = Parrot_str_new(interp, "", 0);
686     ns_separator = Parrot_str_new(interp, ";", 1);
687 
688     while (1) {
689         GETATTR_NameSpace_name(interp, ns, tmp);
690 
691         /* The root ns has the empty string as its name, so ignore it. */
692         if (Parrot_str_length(interp, tmp) == 0)
693             break;
694 
695         full_ns = Parrot_str_concat(interp, ns_separator, full_ns);
696         full_ns = Parrot_str_concat(interp, tmp, full_ns);
697 
698         GETATTR_NameSpace_parent(interp, ns, ns);
699     }
700 
701     GETATTR_Sub_name(interp, ctx->current_sub, sub_name);
702     full_ns = Parrot_str_concat(interp, full_ns, sub_name);
703     return Parrot_str_to_cstring(interp, full_ns);
704 
705 }
706 
707 
708 /*
709 
710 =item C<static char* get_filename_cstr(PARROT_INTERP, PMC* ctx_pmc, opcode_t
711 *pc)>
712 
713 Return the filename for C<ctx_pmc> as a C string.
714 
715 =cut
716 
717 */
718 
719 PARROT_MALLOC
720 PARROT_CANNOT_RETURN_NULL
721 static char*
get_filename_cstr(PARROT_INTERP,ARGIN (PMC * ctx_pmc),ARGIN (opcode_t * pc))722 get_filename_cstr(PARROT_INTERP, ARGIN(PMC* ctx_pmc), ARGIN(opcode_t *pc))
723 {
724 
725     ASSERT_ARGS(get_filename_cstr)
726 
727     STRING * const filename = Parrot_sub_get_filename_from_pc(interp,
728             Parrot_pcc_get_sub(interp, ctx_pmc), pc);
729     char * const filename_cstr = Parrot_str_to_cstring(interp, filename);
730 
731     return filename_cstr;
732 }
733 
734 
735 
736 /*
737 
738 =item C<static void record_bogus_parent_runloop(PARROT_INTERP,
739 Parrot_profiling_runcore_t * runcore)>
740 
741 Record profiling information for a bogus parent runloop.  Parrot program
742 generally have several runloops, so a little trickery is needed to profile them
743 all as a single program.  This is accomplished by having a fake sub named
744 'main' which has no cost in itself but which appears to call all top-level
745 runloops as children.
746 
747 =cut
748 
749 */
750 
751 static void
record_bogus_parent_runloop(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore))752 record_bogus_parent_runloop(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t * runcore))
753 {
754     ASSERT_ARGS(record_bogus_parent_runloop)
755 
756     PPROF_DATA pprof_data[PPROF_DATA_MAX+1];
757 
758     /* make all separate runloops appear to come from a single source */
759     pprof_data[PPROF_DATA_NAMESPACE] = (PPROF_DATA) "main";
760     pprof_data[PPROF_DATA_FILENAME]  = (PPROF_DATA) "no_file";
761     pprof_data[PPROF_DATA_SUB_ADDR]  = 1;
762     pprof_data[PPROF_DATA_CTX_ADDR]  = 1;
763     RUNCORE_store(interp, runcore, pprof_data, PPROF_LINE_CONTEXT_SWITCH);
764 
765     pprof_data[PPROF_DATA_LINE]   = runcore->runloop_count;
766     pprof_data[PPROF_DATA_TIME]   = 0;
767     pprof_data[PPROF_DATA_OPNAME] = (PPROF_DATA) "noop";
768     RUNCORE_store(interp, runcore, pprof_data, PPROF_LINE_OP);
769 
770     ++runcore->runloop_count;
771 }
772 
773 /*
774 
775 =item C<static void destroy_profiling_core(PARROT_INTERP,
776 Parrot_profiling_runcore_t *runcore)>
777 
778 Perform any finalization needed by the profiling runcore.
779 
780 =cut
781 
782 */
783 
784 static void
destroy_profiling_core(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore))785 destroy_profiling_core(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore))
786 {
787     ASSERT_ARGS(destroy_profiling_core)
788 
789     char * const filename_cstr = Parrot_str_to_cstring(interp, runcore->profile_filename);
790 
791     fprintf(stderr, "\nPROFILING RUNCORE: wrote profile to %s\n"
792         "Use tools/dev/pprof2cg.pl to generate Callgrind-compatible "
793         "output from this file.\n", filename_cstr);
794 
795     Parrot_str_free_cstring(filename_cstr);
796     Parrot_hash_destroy(interp, runcore->line_cache);
797 
798     RUNCORE_destroy(interp, runcore);
799     mem_gc_free(interp, runcore->time);
800 }
801 
802 /*
803 
804 =item C<static void record_values_ascii_pprof(PARROT_INTERP,
805 Parrot_profiling_runcore_t * runcore, PPROF_DATA *pprof_data,
806 Parrot_profiling_line type)>
807 
808 Record profiling data to a filehandle in a human-readable format.
809 
810 =cut
811 
812 */
813 
814 static void
record_values_ascii_pprof(SHIM_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore),ARGIN (PPROF_DATA * pprof_data),ARGIN_NULLOK (Parrot_profiling_line type))815 record_values_ascii_pprof(SHIM_INTERP, ARGIN(Parrot_profiling_runcore_t * runcore),
816     ARGIN(PPROF_DATA *pprof_data), ARGIN_NULLOK(Parrot_profiling_line type))
817 {
818     ASSERT_ARGS(record_values_ascii_pprof)
819 
820     switch (type) {
821         case PPROF_LINE_CONTEXT_SWITCH:
822             {
823                 const char * const pd_namespace = (const char *) pprof_data[PPROF_DATA_NAMESPACE];
824                 const char * const pd_filename  = (const char *) pprof_data[PPROF_DATA_FILENAME];
825                 const void * const sub_addr     = (const void *) pprof_data[PPROF_DATA_SUB_ADDR];
826                 const void * const ctx_addr     = (const void *) pprof_data[PPROF_DATA_CTX_ADDR];
827                 fprintf(runcore->profile_fd,
828                         "CS:{x{ns:%s}x}{x{file:%s}x}{x{sub:%p}x}{x{ctx:%p}x}\n",
829                         pd_namespace, pd_filename, sub_addr, ctx_addr);
830             }
831             break;
832 
833         case PPROF_LINE_OP:
834             {
835                 const PPROF_DATA line     = pprof_data[PPROF_DATA_LINE];
836                 const PPROF_DATA time     = pprof_data[PPROF_DATA_TIME];
837                 const char * const opname = (const char *) pprof_data[PPROF_DATA_OPNAME];
838                 fprintf(runcore->profile_fd, "OP:{x{line:%d}x}{x{time:%d}x}{x{op:%s}x}\n",
839                         (int) line, (int) time, opname);
840             }
841             break;
842 
843         case PPROF_LINE_ANNOTATION:
844             {
845                 const char * const name  = (const char *) pprof_data[PPROF_DATA_ANNOTATION_NAME];
846                 const char * const value = (const char *) pprof_data[PPROF_DATA_ANNOTATION_VALUE];
847                 fprintf(runcore->profile_fd, "AN:{x{name:%s}x}{x{value:%s}x}\n", name, value);
848             }
849             break;
850 
851         case PPROF_LINE_CLI:
852             fprintf(runcore->profile_fd, "CLI: %s\n", (const char *) pprof_data[PPROF_DATA_CLI]);
853             break;
854 
855         case PPROF_LINE_VERSION:
856             fprintf(runcore->profile_fd, "VERSION:%d\n", (int) pprof_data[PPROF_DATA_VERSION]);
857             break;
858 
859         case PPROF_LINE_END_OF_RUNLOOP:
860             fprintf(runcore->profile_fd, "END_OF_RUNLOOP:1\n");
861             break;
862 
863         default:
864             break;
865     } /* switch */
866 }
867 
868 /*
869 
870 =item C<static void init_basic_output(PARROT_INTERP, Parrot_profiling_runcore_t
871 *runcore)>
872 
873 Perform initializattion needed by the basic output methods.
874 
875 =cut
876 
877 */
878 
879 static void
init_basic_output(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore))880 init_basic_output(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore))
881 {
882     ASSERT_ARGS(init_basic_output)
883 
884     /* figure out where to write the output */
885     char   *profile_filename_cstr;
886     STRING * const env_filename_str = Parrot_getenv(interp, CONST_STRING(interp, "PARROT_PROFILING_FILENAME"));
887 
888     if (!STRING_IS_NULL(env_filename_str)) {
889         STRING  *lc_filename;
890         runcore->profile_filename = env_filename_str;
891         /* this is a little goofy, but it means that we unconditionally free
892          * profile_filename later in this function */
893         profile_filename_cstr     = Parrot_str_to_cstring(interp, runcore->profile_filename);
894         lc_filename               = Parrot_str_downcase(interp, runcore->profile_filename);
895 
896         if (STRING_equal(interp, lc_filename, CONST_STRING(interp, "stderr"))) {
897             runcore->profile_fd       = stderr;
898             runcore->profile_filename = lc_filename;
899         }
900         else if (STRING_equal(interp, lc_filename, CONST_STRING(interp, "stdout"))) {
901             runcore->profile_fd       = stdout;
902             runcore->profile_filename = lc_filename;
903         }
904         else
905             runcore->profile_fd = fopen(profile_filename_cstr, "w");
906     }
907     else {
908         runcore->profile_filename = Parrot_sprintf_c(interp, "parrot.pprof.%d", getpid());
909         profile_filename_cstr     = Parrot_str_to_cstring(interp, runcore->profile_filename);
910         runcore->profile_fd       = fopen(profile_filename_cstr, "w");
911     }
912 
913     /* put profile_filename in the gc root set so it won't get collected */
914     Parrot_str_gc_register(interp, runcore->profile_filename);
915 
916     if (!runcore->profile_fd) {
917         fprintf(stderr, "unable to open %s for writing", profile_filename_cstr);
918         Parrot_str_free_cstring(profile_filename_cstr);
919         Parrot_x_jump_out(interp, 1);
920     }
921 
922     Parrot_str_free_cstring(profile_filename_cstr);
923 
924     /* figure out if annotations are wanted */
925     if (!STRING_IS_NULL(Parrot_getenv(interp, CONST_STRING(interp, "PARROT_PROFILING_ANNOTATIONS")))) {
926         Profiling_report_annotations_SET(runcore);
927     }
928 
929     if (!STRING_IS_NULL(Parrot_getenv(interp, CONST_STRING(interp, "PARROT_PROFILING_CANONICAL_OUTPUT")))) {
930         Profiling_canonical_output_SET(runcore);
931     }
932 
933 }
934 
935 /*
936 
937 =item C<static void destroy_basic_output(PARROT_INTERP,
938 Parrot_profiling_runcore_t *runcore)>
939 
940 Perform finalization needed by the basic output methods.
941 
942 =cut
943 
944 */
945 
946 static void
destroy_basic_output(SHIM_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore))947 destroy_basic_output(SHIM_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore))
948 {
949     ASSERT_ARGS(destroy_basic_output)
950 
951     fclose(runcore->profile_fd);
952 }
953 
954 /*
955 
956 =item C<static void init_null_output(PARROT_INTERP, Parrot_profiling_runcore_t
957 *runcore)>
958 
959 Perform initialization needed by the null output methods.
960 
961 =cut
962 
963 */
964 
965 static void
init_null_output(PARROT_INTERP,ARGIN (Parrot_profiling_runcore_t * runcore))966 init_null_output(PARROT_INTERP, ARGIN(Parrot_profiling_runcore_t *runcore))
967 {
968     ASSERT_ARGS(init_null_output)
969     runcore->profile_filename = CONST_STRING(interp, "none");
970 }
971 
972 /*
973 
974 =back
975 
976 =cut
977 
978 */
979 
980 /*
981  * Local variables:
982  *   c-file-style: "parrot"
983  * End:
984  * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
985  */
986