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