FreeBSD syscall: fix syscall name used in kcmp traces.
[valgrind.git] / callgrind / dump.c
blob130b73bd0bc8e6c080337e6e16879cd94e263cda
1 /*--------------------------------------------------------------------*/
2 /*--- Callgrind ---*/
3 /*--- dump.c ---*/
4 /*--------------------------------------------------------------------*/
6 /*
7 This file is part of Callgrind, a Valgrind tool for call tracing.
9 Copyright (C) 2002-2017, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, see <http://www.gnu.org/licenses/>.
24 The GNU General Public License is contained in the file COPYING.
27 #include "config.h"
28 #include "global.h"
30 #include "pub_tool_threadstate.h"
31 #include "pub_tool_libcfile.h"
34 /* Dump Part Counter */
35 static Int out_counter = 0;
37 static HChar* out_file = 0;
38 static Bool dumps_initialized = False;
40 /* Command */
41 static HChar *cmdbuf;
43 /* Total reads/writes/misses sum over all dumps and threads.
44 * Updated during CC traversal at dump time.
46 FullCost CLG_(total_cost) = 0;
47 static FullCost dump_total_cost = 0;
49 EventMapping* CLG_(dumpmap) = 0;
51 Int CLG_(get_dump_counter)(void)
53 return out_counter;
56 /*------------------------------------------------------------*/
57 /*--- Output file related stuff ---*/
58 /*------------------------------------------------------------*/
60 /* Boolean dumping array */
61 static Bool* dump_array = 0;
62 static Int dump_array_size = 0;
63 static Bool* obj_dumped = 0;
64 static Bool* file_dumped = 0;
65 static Bool* fn_dumped = 0;
66 static Bool* cxt_dumped = 0;
68 static
69 void reset_dump_array(void)
71 int i;
73 CLG_ASSERT(dump_array != 0);
75 for(i=0;i<dump_array_size;i++)
76 dump_array[i] = False;
79 static
80 void init_dump_array(void)
82 dump_array_size = CLG_(stat).distinct_objs +
83 CLG_(stat).distinct_files +
84 CLG_(stat).distinct_fns +
85 CLG_(stat).context_counter;
86 CLG_ASSERT(dump_array == 0);
87 dump_array = (Bool*) CLG_MALLOC("cl.dump.ida.1",
88 dump_array_size * sizeof(Bool));
89 obj_dumped = dump_array;
90 file_dumped = obj_dumped + CLG_(stat).distinct_objs;
91 fn_dumped = file_dumped + CLG_(stat).distinct_files;
92 cxt_dumped = fn_dumped + CLG_(stat).distinct_fns;
94 reset_dump_array();
96 CLG_DEBUG(1, " init_dump_array: size %d\n", dump_array_size);
99 static __inline__
100 void free_dump_array(void)
102 CLG_ASSERT(dump_array != 0);
103 VG_(free)(dump_array);
105 dump_array = 0;
106 obj_dumped = 0;
107 file_dumped = 0;
108 fn_dumped = 0;
109 cxt_dumped = 0;
113 /* Initialize to an invalid position */
114 static __inline__
115 void init_fpos(FnPos* p)
117 p->file = 0;
118 p->fn = 0;
119 p->obj = 0;
120 p->cxt = 0;
121 p->rec_index = 0;
125 static void print_obj(VgFile *fp, const HChar* prefix, obj_node* obj)
127 if (CLG_(clo).compress_strings) {
128 CLG_ASSERT(obj_dumped != 0);
129 if (obj_dumped[obj->number])
130 VG_(fprintf)(fp, "%s(%u)\n", prefix, obj->number);
131 else {
132 VG_(fprintf)(fp, "%s(%u) %s\n", prefix, obj->number, obj->name);
135 else
136 VG_(fprintf)(fp, "%s%s\n", prefix, obj->name);
138 #if 0
139 /* add mapping parameters the first time a object is dumped
140 * format: mp=0xSTART SIZE 0xOFFSET */
141 if (!obj_dumped[obj->number]) {
142 obj_dumped[obj->number];
143 VG_(fprintf)(fp, "mp=%p %p %p\n",
144 pos->obj->start, pos->obj->size, pos->obj->offset);
146 #else
147 obj_dumped[obj->number] = True;
148 #endif
151 static void print_file(VgFile *fp, const char *prefix, const file_node* file)
153 if (CLG_(clo).compress_strings) {
154 CLG_ASSERT(file_dumped != 0);
155 if (file_dumped[file->number])
156 VG_(fprintf)(fp, "%s(%u)\n", prefix, file->number);
157 else {
158 VG_(fprintf)(fp, "%s(%u) %s\n", prefix, file->number, file->name);
159 file_dumped[file->number] = True;
162 else
163 VG_(fprintf)(fp, "%s%s\n", prefix, file->name);
167 * tag can be "fn", "cfn", "jfn"
169 static void print_fn(VgFile *fp, const HChar* tag, const fn_node* fn)
171 VG_(fprintf)(fp, "%s=",tag);
172 if (CLG_(clo).compress_strings) {
173 CLG_ASSERT(fn_dumped != 0);
174 if (fn_dumped[fn->number])
175 VG_(fprintf)(fp, "(%u)\n", fn->number);
176 else {
177 VG_(fprintf)(fp, "(%u) %s\n", fn->number, fn->name);
178 fn_dumped[fn->number] = True;
181 else
182 VG_(fprintf)(fp, "%s\n", fn->name);
185 static void print_mangled_fn(VgFile *fp, const HChar* tag,
186 Context* cxt, int rec_index)
188 int i;
190 if (CLG_(clo).compress_strings && CLG_(clo).compress_mangled) {
192 int n;
193 Context* last;
195 CLG_ASSERT(cxt_dumped != 0);
196 if (cxt_dumped[cxt->base_number+rec_index]) {
197 VG_(fprintf)(fp, "%s=(%u)\n",
198 tag, cxt->base_number + rec_index);
199 return;
202 last = 0;
203 /* make sure that for all context parts compressed data is written */
204 for(i=cxt->size;i>0;i--) {
205 CLG_ASSERT(cxt->fn[i-1]->pure_cxt != 0);
206 n = cxt->fn[i-1]->pure_cxt->base_number;
207 if (cxt_dumped[n]) continue;
208 VG_(fprintf)(fp, "%s=(%d) %s\n",
209 tag, n, cxt->fn[i-1]->name);
211 cxt_dumped[n] = True;
212 last = cxt->fn[i-1]->pure_cxt;
214 /* If the last context was the context to print, we are finished */
215 if ((last == cxt) && (rec_index == 0)) return;
217 VG_(fprintf)(fp, "%s=(%u) (%u)", tag,
218 cxt->base_number + rec_index,
219 cxt->fn[0]->pure_cxt->base_number);
220 if (rec_index >0)
221 VG_(fprintf)(fp, "'%d", rec_index +1);
222 for(i=1;i<cxt->size;i++)
223 VG_(fprintf)(fp, "'(%u)",
224 cxt->fn[i]->pure_cxt->base_number);
225 VG_(fprintf)(fp, "\n");
227 cxt_dumped[cxt->base_number+rec_index] = True;
228 return;
232 VG_(fprintf)(fp, "%s=", tag);
233 if (CLG_(clo).compress_strings) {
234 CLG_ASSERT(cxt_dumped != 0);
235 if (cxt_dumped[cxt->base_number+rec_index]) {
236 VG_(fprintf)(fp, "(%u)\n", cxt->base_number + rec_index);
237 return;
239 else {
240 VG_(fprintf)(fp, "(%u) ", cxt->base_number + rec_index);
241 cxt_dumped[cxt->base_number+rec_index] = True;
245 VG_(fprintf)(fp, "%s", cxt->fn[0]->name);
246 if (rec_index >0)
247 VG_(fprintf)(fp, "'%d", rec_index +1);
248 for(i=1;i<cxt->size;i++)
249 VG_(fprintf)(fp, "'%s", cxt->fn[i]->name);
251 VG_(fprintf)(fp, "\n");
257 * Print function position of the BBCC, but only print info differing to
258 * the <last> position, update <last>
259 * Return True if something changes.
261 static Bool print_fn_pos(VgFile *fp, FnPos* last, BBCC* bbcc)
263 Bool res = False;
265 CLG_ASSERT(bbcc && bbcc->cxt);
267 CLG_DEBUGIF(3) {
268 CLG_DEBUG(2, "+ print_fn_pos: ");
269 CLG_(print_cxt)(16, bbcc->cxt, bbcc->rec_index);
272 if (!CLG_(clo).mangle_names) {
273 if (last->rec_index != bbcc->rec_index) {
274 VG_(fprintf)(fp, "rec=%u\n\n", bbcc->rec_index);
275 last->rec_index = bbcc->rec_index;
276 last->cxt = 0; /* reprint context */
277 res = True;
280 if (last->cxt != bbcc->cxt) {
281 fn_node* last_from = (last->cxt && last->cxt->size >1) ?
282 last->cxt->fn[1] : 0;
283 fn_node* curr_from = (bbcc->cxt->size >1) ?
284 bbcc->cxt->fn[1] : 0;
285 if (curr_from == 0) {
286 if (last_from != 0) {
287 /* switch back to no context */
288 VG_(fprintf)(fp, "frfn=(spontaneous)\n");
289 res = True;
292 else if (last_from != curr_from) {
293 print_fn(fp, "frfn", curr_from);
294 res = True;
296 last->cxt = bbcc->cxt;
300 if (last->obj != bbcc->cxt->fn[0]->file->obj) {
301 print_obj(fp, "ob=", bbcc->cxt->fn[0]->file->obj);
302 last->obj = bbcc->cxt->fn[0]->file->obj;
303 res = True;
306 if (last->file != bbcc->cxt->fn[0]->file) {
307 print_file(fp, "fl=", bbcc->cxt->fn[0]->file);
308 last->file = bbcc->cxt->fn[0]->file;
309 res = True;
312 if (!CLG_(clo).mangle_names) {
313 if (last->fn != bbcc->cxt->fn[0]) {
314 print_fn(fp, "fn", bbcc->cxt->fn[0]);
315 last->fn = bbcc->cxt->fn[0];
316 res = True;
319 else {
320 /* Print mangled name if context or rec_index changes */
321 if ((last->rec_index != bbcc->rec_index) ||
322 (last->cxt != bbcc->cxt)) {
324 print_mangled_fn(fp, "fn", bbcc->cxt, bbcc->rec_index);
325 last->fn = bbcc->cxt->fn[0];
326 last->rec_index = bbcc->rec_index;
327 res = True;
331 last->cxt = bbcc->cxt;
333 CLG_DEBUG(2, "- print_fn_pos: %s\n", res ? "changed" : "");
335 return res;
338 /* the debug lookup cache is useful if BBCC for same BB are
339 * dumped directly in a row. This is a direct mapped cache.
341 #define DEBUG_CACHE_SIZE 1777
343 static Addr debug_cache_addr[DEBUG_CACHE_SIZE];
344 static file_node* debug_cache_file[DEBUG_CACHE_SIZE];
345 static int debug_cache_line[DEBUG_CACHE_SIZE];
346 static Bool debug_cache_info[DEBUG_CACHE_SIZE];
348 static __inline__
349 void init_debug_cache(void)
351 int i;
352 for(i=0;i<DEBUG_CACHE_SIZE;i++) {
353 debug_cache_addr[i] = 0;
354 debug_cache_file[i] = 0;
355 debug_cache_line[i] = 0;
356 debug_cache_info[i] = 0;
360 static /* __inline__ */
361 Bool get_debug_pos(BBCC* bbcc, Addr addr, AddrPos* p)
363 const HChar *file, *dir;
364 Bool found_file_line;
366 int cachepos = addr % DEBUG_CACHE_SIZE;
368 if (debug_cache_addr[cachepos] == addr) {
369 p->line = debug_cache_line[cachepos];
370 p->file = debug_cache_file[cachepos];
371 found_file_line = debug_cache_info[cachepos];
373 else {
374 DiEpoch ep = VG_(current_DiEpoch)();
375 found_file_line = VG_(get_filename_linenum)(ep, addr,
376 &file,
377 &dir,
378 &(p->line));
379 if (!found_file_line) {
380 file = "???";
381 p->line = 0;
383 p->file = CLG_(get_file_node)(bbcc->bb->obj, dir, file);
385 debug_cache_info[cachepos] = found_file_line;
386 debug_cache_addr[cachepos] = addr;
387 debug_cache_line[cachepos] = p->line;
388 debug_cache_file[cachepos] = p->file;
391 /* Address offset from bbcc start address */
392 p->addr = addr - bbcc->bb->obj->offset;
393 p->bb_addr = bbcc->bb->offset;
395 CLG_DEBUG(3, " get_debug_pos(%#lx): BB %#lx, fn '%s', file '%s', line %u\n",
396 addr, bb_addr(bbcc->bb), bbcc->cxt->fn[0]->name,
397 p->file->name, p->line);
399 return found_file_line;
403 /* copy file position and init cost */
404 static void init_apos(AddrPos* p, Addr addr, Addr bbaddr, file_node* file)
406 p->addr = addr;
407 p->bb_addr = bbaddr;
408 p->file = file;
409 p->line = 0;
412 static void copy_apos(AddrPos* dst, AddrPos* src)
414 dst->addr = src->addr;
415 dst->bb_addr = src->bb_addr;
416 dst->file = src->file;
417 dst->line = src->line;
420 /* copy file position and init cost */
421 static void init_fcost(AddrCost* c, Addr addr, Addr bbaddr, file_node* file)
423 init_apos( &(c->p), addr, bbaddr, file);
424 /* FIXME: This is a memory leak as a AddrCost is inited multiple times */
425 c->cost = CLG_(get_eventset_cost)( CLG_(sets).full );
426 CLG_(init_cost)( CLG_(sets).full, c->cost );
431 * print position change inside of a BB (last -> curr)
432 * this doesn't update last to curr!
434 static void fprint_apos(VgFile *fp, AddrPos* curr, AddrPos* last,
435 file_node* func_file)
437 CLG_ASSERT(curr->file != 0);
438 CLG_DEBUG(2, " print_apos(file '%s', line %u, bb %#lx, addr %#lx) fnFile '%s'\n",
439 curr->file->name, curr->line, curr->bb_addr, curr->addr,
440 func_file->name);
442 if (curr->file != last->file) {
444 /* if we switch back to orig file, use fe=... */
445 if (curr->file == func_file)
446 print_file(fp, "fe=", curr->file);
447 else
448 print_file(fp, "fi=", curr->file);
451 if (CLG_(clo).dump_bbs) {
452 if (curr->line != last->line) {
453 VG_(fprintf)(fp, "ln=%u\n", curr->line);
461 * Print a position.
462 * This prints out differences if allowed
464 * This doesn't set last to curr afterwards!
466 static
467 void fprint_pos(VgFile *fp, const AddrPos* curr, const AddrPos* last)
469 if (0) //CLG_(clo).dump_bbs)
470 VG_(fprintf)(fp, "%lu ", curr->addr - curr->bb_addr);
471 else {
472 if (CLG_(clo).dump_instr) {
473 int diff = curr->addr - last->addr;
474 if ( CLG_(clo).compress_pos && (last->addr >0) &&
475 (diff > -100) && (diff < 100)) {
476 if (diff >0)
477 VG_(fprintf)(fp, "+%d ", diff);
478 else if (diff==0)
479 VG_(fprintf)(fp, "* ");
480 else
481 VG_(fprintf)(fp, "%d ", diff);
483 else
484 VG_(fprintf)(fp, "%#lx ", curr->addr);
487 if (CLG_(clo).dump_bb) {
488 int diff = curr->bb_addr - last->bb_addr;
489 if ( CLG_(clo).compress_pos && (last->bb_addr >0) &&
490 (diff > -100) && (diff < 100)) {
491 if (diff >0)
492 VG_(fprintf)(fp, "+%d ", diff);
493 else if (diff==0)
494 VG_(fprintf)(fp, "* ");
495 else
496 VG_(fprintf)(fp, "%d ", diff);
498 else
499 VG_(fprintf)(fp, "%#lx ", curr->bb_addr);
502 if (CLG_(clo).dump_line) {
503 int diff = curr->line - last->line;
504 if ( CLG_(clo).compress_pos && (last->line >0) &&
505 (diff > -100) && (diff < 100)) {
507 if (diff >0)
508 VG_(fprintf)(fp, "+%d ", diff);
509 else if (diff==0)
510 VG_(fprintf)(fp, "* ");
511 else
512 VG_(fprintf)(fp, "%d ", diff);
514 else
515 VG_(fprintf)(fp, "%u ", curr->line);
522 * Print events.
525 static
526 void fprint_cost(VgFile *fp, const EventMapping* es, const ULong* cost)
528 HChar *mcost = CLG_(mappingcost_as_string)(es, cost);
529 VG_(fprintf)(fp, "%s\n", mcost);
530 CLG_FREE(mcost);
535 /* Write the cost of a source line; only that parts of the source
536 * position are written that changed relative to last written position.
537 * funcPos is the source position of the first line of actual function.
538 * Something is written only if cost != 0; returns True in this case.
540 static void fprint_fcost(VgFile *fp, AddrCost* c, AddrPos* last)
542 CLG_DEBUGIF(3) {
543 CLG_DEBUG(2, " print_fcost(file '%s', line %u, bb %#lx, addr %#lx):\n",
544 c->p.file->name, c->p.line, c->p.bb_addr, c->p.addr);
545 CLG_(print_cost)(-5, CLG_(sets).full, c->cost);
548 fprint_pos(fp, &(c->p), last);
549 copy_apos( last, &(c->p) ); /* update last to current position */
551 fprint_cost(fp, CLG_(dumpmap), c->cost);
553 /* add cost to total */
554 CLG_(add_and_zero_cost)( CLG_(sets).full, dump_total_cost, c->cost );
558 /* Write out the calls from jcc (at pos)
560 static void fprint_jcc(VgFile *fp, jCC* jcc, AddrPos* curr, AddrPos* last,
561 ULong ecounter)
563 static AddrPos target;
564 file_node* file;
565 obj_node* obj;
567 CLG_DEBUGIF(2) {
568 CLG_DEBUG(2, " fprint_jcc (jkind %d)\n", (Int)jcc->jmpkind);
569 CLG_(print_jcc)(-10, jcc);
572 CLG_ASSERT(jcc->to !=0);
573 CLG_ASSERT(jcc->from !=0);
575 if (!get_debug_pos(jcc->to, bb_addr(jcc->to->bb), &target)) {
576 /* if we don't have debug info, don't switch to file "???" */
577 target.file = last->file;
580 if ((jcc->jmpkind == jk_CondJump) || (jcc->jmpkind == jk_Jump)) {
582 /* this is a JCC for a followed conditional or boring jump. */
583 CLG_ASSERT(CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost));
585 /* objects among jumps should be the same.
586 * Otherwise this jump would have been changed to a call
587 * (see setup_bbcc)
589 CLG_ASSERT(jcc->from->bb->obj == jcc->to->bb->obj);
591 /* only print if target position info is useful */
592 if (!CLG_(clo).dump_instr && !CLG_(clo).dump_bb && target.line==0) {
593 jcc->call_counter = 0;
594 return;
597 /* Different files/functions are possible e.g. with longjmp's
598 * which change the stack, and thus context
600 if (last->file != target.file) {
601 print_file(fp, "jfi=", target.file);
604 if (jcc->from->cxt != jcc->to->cxt) {
605 if (CLG_(clo).mangle_names)
606 print_mangled_fn(fp, "jfn",
607 jcc->to->cxt, jcc->to->rec_index);
608 else
609 print_fn(fp, "jfn", jcc->to->cxt->fn[0]);
612 if (jcc->jmpkind == jk_CondJump) {
613 /* format: jcnd=<followed>/<executions> <target> */
614 VG_(fprintf)(fp, "jcnd=%llu/%llu ",
615 jcc->call_counter, ecounter);
617 else {
618 /* format: jump=<jump count> <target> */
619 VG_(fprintf)(fp, "jump=%llu ",
620 jcc->call_counter);
623 fprint_pos(fp, &target, last);
624 VG_(fprintf)(fp, "\n");
625 fprint_pos(fp, curr, last);
626 VG_(fprintf)(fp, "\n");
628 jcc->call_counter = 0;
629 return;
632 file = jcc->to->cxt->fn[0]->file;
633 obj = jcc->to->bb->obj;
635 /* object of called position different to object of this function?*/
636 if (jcc->from->cxt->fn[0]->file->obj != obj) {
637 print_obj(fp, "cob=", obj);
640 /* file of called position different to current file? */
641 if (last->file != file) {
642 print_file(fp, "cfi=", file);
645 if (CLG_(clo).mangle_names)
646 print_mangled_fn(fp, "cfn", jcc->to->cxt, jcc->to->rec_index);
647 else
648 print_fn(fp, "cfn", jcc->to->cxt->fn[0]);
650 if (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost)) {
651 VG_(fprintf)(fp, "calls=%llu ",
652 jcc->call_counter);
654 fprint_pos(fp, &target, last);
655 VG_(fprintf)(fp, "\n");
656 fprint_pos(fp, curr, last);
657 fprint_cost(fp, CLG_(dumpmap), jcc->cost);
659 CLG_(init_cost)( CLG_(sets).full, jcc->cost );
661 jcc->call_counter = 0;
667 /* Cost summation of functions.We use alternately ccSum[0/1], thus
668 * ssSum[currSum] for recently read lines with same line number.
670 static AddrCost ccSum[2];
671 static int currSum;
674 * Print all costs of a BBCC:
675 * - FCCs of instructions
676 * - JCCs of the unique jump of this BB
677 * returns True if something was written
679 static Bool fprint_bbcc(VgFile *fp, BBCC* bbcc, AddrPos* last)
681 InstrInfo* instr_info;
682 ULong ecounter;
683 Bool something_written = False;
684 jCC* jcc;
685 AddrCost *currCost, *newCost;
686 Int jcc_count = 0, instr, i, jmp;
687 BB* bb = bbcc->bb;
689 CLG_ASSERT(bbcc->cxt != 0);
690 CLG_DEBUGIF(1) {
691 VG_(printf)("+ fprint_bbcc (Instr %u): ", bb->instr_count);
692 CLG_(print_bbcc)(15, bbcc);
695 CLG_ASSERT(currSum == 0 || currSum == 1);
696 currCost = &(ccSum[currSum]);
697 newCost = &(ccSum[1-currSum]);
699 ecounter = bbcc->ecounter_sum;
700 jmp = 0;
701 instr_info = &(bb->instr[0]);
702 for(instr=0; instr<bb->instr_count; instr++, instr_info++) {
704 /* get debug info of current instruction address and dump cost
705 * if CLG_(clo).dump_bbs or file/line has changed
707 if (!get_debug_pos(bbcc, bb_addr(bb) + instr_info->instr_offset,
708 &(newCost->p))) {
709 /* if we don't have debug info, don't switch to file "???" */
710 newCost->p.file = bbcc->cxt->fn[0]->file;
713 if (CLG_(clo).dump_bbs || CLG_(clo).dump_instr ||
714 (newCost->p.line != currCost->p.line) ||
715 (newCost->p.file != currCost->p.file)) {
717 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
718 something_written = True;
720 fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file);
721 fprint_fcost(fp, currCost, last);
724 /* switch buffers */
725 currSum = 1 - currSum;
726 currCost = &(ccSum[currSum]);
727 newCost = &(ccSum[1-currSum]);
730 /* add line cost to current cost sum */
731 (*CLG_(cachesim).add_icost)(currCost->cost, bbcc, instr_info, ecounter);
733 /* print jcc's if there are: only jumps */
734 if (bb->jmp[jmp].instr == instr) {
735 jcc_count=0;
736 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from)
737 if (((jcc->jmpkind != jk_Call) && (jcc->call_counter >0)) ||
738 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
739 jcc_count++;
741 if (jcc_count>0) {
742 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
743 /* no need to switch buffers, as position is the same */
744 fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file);
745 fprint_fcost(fp, currCost, last);
747 get_debug_pos(bbcc, bb_addr(bb)+instr_info->instr_offset, &(currCost->p));
748 fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file);
749 something_written = True;
750 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
751 if (((jcc->jmpkind != jk_Call) && (jcc->call_counter >0)) ||
752 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
753 fprint_jcc(fp, jcc, &(currCost->p), last, ecounter);
758 /* update execution counter */
759 if (jmp < bb->cjmp_count)
760 if (bb->jmp[jmp].instr == instr) {
761 ecounter -= bbcc->jmp[jmp].ecounter;
762 jmp++;
766 /* jCCs at end? If yes, dump cumulated line info first */
767 jcc_count = 0;
768 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
769 /* yes, if JCC only counts jmp arcs or cost >0 */
770 if ( ((jcc->jmpkind != jk_Call) && (jcc->call_counter >0)) ||
771 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
772 jcc_count++;
775 if ( (bbcc->skipped &&
776 !CLG_(is_zero_cost)(CLG_(sets).full, bbcc->skipped)) ||
777 (jcc_count>0) ) {
779 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
780 /* no need to switch buffers, as position is the same */
781 fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file);
782 fprint_fcost(fp, currCost, last);
785 get_debug_pos(bbcc, bb_jmpaddr(bb), &(currCost->p));
786 fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file);
787 something_written = True;
789 /* first, print skipped costs for calls */
790 if (bbcc->skipped && !CLG_(is_zero_cost)( CLG_(sets).full,
791 bbcc->skipped )) {
792 CLG_(add_and_zero_cost)( CLG_(sets).full,
793 currCost->cost, bbcc->skipped );
794 #if 0
795 VG_(fprintf)(fp, "# Skipped\n");
796 #endif
797 fprint_fcost(fp, currCost, last);
800 if (jcc_count > 0)
801 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
802 CLG_ASSERT(jcc->jmp == jmp);
803 if ( ((jcc->jmpkind != jk_Call) && (jcc->call_counter >0)) ||
804 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
806 fprint_jcc(fp, jcc, &(currCost->p), last, ecounter);
810 if (CLG_(clo).dump_bbs || CLG_(clo).dump_bb) {
811 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
812 something_written = True;
814 fprint_apos(fp, &(currCost->p), last, bbcc->cxt->fn[0]->file);
815 fprint_fcost(fp, currCost, last);
817 if (CLG_(clo).dump_bbs) VG_(fprintf)(fp, "\n");
819 /* when every cost was immediately written, we must have done so,
820 * as this function is only called when there's cost in a BBCC
822 CLG_ASSERT(something_written);
825 bbcc->ecounter_sum = 0;
826 for(i=0; i<=bbcc->bb->cjmp_count; i++)
827 bbcc->jmp[i].ecounter = 0;
828 bbcc->ret_counter = 0;
830 CLG_DEBUG(1, "- fprint_bbcc: JCCs %d\n", jcc_count);
832 return something_written;
835 /* order by
836 * recursion,
837 * from->bb->obj, from->bb->fn
838 * obj, fn[0]->file, fn
839 * address
841 static int my_cmp(BBCC** pbbcc1, BBCC** pbbcc2)
843 #if 0
844 return (*pbbcc1)->bb->offset - (*pbbcc2)->bb->offset;
845 #else
846 BBCC *bbcc1 = *pbbcc1;
847 BBCC *bbcc2 = *pbbcc2;
848 Context* cxt1 = bbcc1->cxt;
849 Context* cxt2 = bbcc2->cxt;
850 int off = 1;
852 if (cxt1->fn[0]->file->obj != cxt2->fn[0]->file->obj)
853 return cxt1->fn[0]->file->obj - cxt2->fn[0]->file->obj;
855 if (cxt1->fn[0]->file != cxt2->fn[0]->file)
856 return cxt1->fn[0]->file - cxt2->fn[0]->file;
858 if (cxt1->fn[0] != cxt2->fn[0])
859 return cxt1->fn[0] - cxt2->fn[0];
861 if (bbcc1->rec_index != bbcc2->rec_index)
862 return bbcc1->rec_index - bbcc2->rec_index;
864 while((off < cxt1->size) && (off < cxt2->size)) {
865 fn_node* ffn1 = cxt1->fn[off];
866 fn_node* ffn2 = cxt2->fn[off];
867 if (ffn1->file->obj != ffn2->file->obj)
868 return ffn1->file->obj - ffn2->file->obj;
869 if (ffn1 != ffn2)
870 return ffn1 - ffn2;
871 off++;
873 if (cxt1->size > cxt2->size) return 1;
874 else if (cxt1->size < cxt2->size) return -1;
876 return bbcc1->bb->offset - bbcc2->bb->offset;
877 #endif
884 /* modified version of:
886 * qsort -- qsort interface implemented by faster quicksort.
887 * J. L. Bentley and M. D. McIlroy, SPE 23 (1993) 1249-1265.
888 * Copyright 1993, John Wiley.
891 static __inline__
892 void swap(BBCC** a, BBCC** b)
894 BBCC* t;
895 t = *a; *a = *b; *b = t;
898 #if !defined(min)
899 #define min(x, y) ((x)<=(y) ? (x) : (y))
900 #endif
902 static
903 BBCC** med3(BBCC **a, BBCC **b, BBCC **c, int (*cmp)(BBCC**,BBCC**))
904 { return cmp(a, b) < 0 ?
905 (cmp(b, c) < 0 ? b : cmp(a, c) < 0 ? c : a)
906 : (cmp(b, c) > 0 ? b : cmp(a, c) > 0 ? c : a);
909 static BBCC** qsort_start = 0;
911 static void CLG_(qsort)(BBCC **a, int n, int (*cmp)(BBCC**,BBCC**))
913 BBCC **pa, **pb, **pc, **pd, **pl, **pm, **pn, **pv;
914 int s, r;
915 BBCC* v;
917 CLG_DEBUG(8, " qsort(%ld,%ld)\n", a-qsort_start + 0L, n + 0L);
919 if (n < 7) { /* Insertion sort on smallest arrays */
920 for (pm = a+1; pm < a+n; pm++)
921 for (pl = pm; pl > a && cmp(pl-1, pl) > 0; pl --)
922 swap(pl, pl-1);
924 CLG_DEBUGIF(8) {
925 for (pm = a; pm < a+n; pm++) {
926 VG_(printf)(" %3ld BB %#lx, ",
927 pm - qsort_start + 0L,
928 bb_addr((*pm)->bb));
929 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
932 return;
934 pm = a + n/2; /* Small arrays, middle element */
935 if (n > 7) {
936 pl = a;
937 pn = a + (n-1);
938 if (n > 40) { /* Big arrays, pseudomedian of 9 */
939 s = n/8;
940 pl = med3(pl, pl+s, pl+2*s, cmp);
941 pm = med3(pm-s, pm, pm+s, cmp);
942 pn = med3(pn-2*s, pn-s, pn, cmp);
944 pm = med3(pl, pm, pn, cmp); /* Mid-size, med of 3 */
948 v = *pm;
949 pv = &v;
950 pa = pb = a;
951 pc = pd = a + (n-1);
952 for (;;) {
953 while ((pb <= pc) && ((r=cmp(pb, pv)) <= 0)) {
954 if (r==0) {
955 /* same as pivot, to start */
956 swap(pa,pb); pa++;
958 pb ++;
960 while ((pb <= pc) && ((r=cmp(pc, pv)) >= 0)) {
961 if (r==0) {
962 /* same as pivot, to end */
963 swap(pc,pd); pd--;
965 pc --;
967 if (pb > pc) { break; }
968 swap(pb, pc);
969 pb ++;
970 pc --;
972 pb--;
973 pc++;
975 /* put pivot from start into middle */
976 if ((s = pa-a)>0) { for(r=0;r<s;r++) swap(a+r, pb+1-s+r); }
977 /* put pivot from end into middle */
978 if ((s = a+n-1-pd)>0) { for(r=0;r<s;r++) swap(pc+r, a+n-s+r); }
980 CLG_DEBUGIF(8) {
981 VG_(printf)(" PV BB %#lx, ", bb_addr((*pv)->bb));
982 CLG_(print_cxt)(9, (*pv)->cxt, (*pv)->rec_index);
984 s = pb-pa+1;
985 VG_(printf)(" Lower %ld - %ld:\n",
986 a-qsort_start + 0L,
987 a+s-1-qsort_start + 0L);
988 for (r=0;r<s;r++) {
989 pm = a+r;
990 VG_(printf)(" %3ld BB %#lx, ",
991 pm-qsort_start + 0L,
992 bb_addr((*pm)->bb));
993 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
996 s = pd-pc+1;
997 VG_(printf)(" Upper %ld - %ld:\n",
998 a+n-s-qsort_start + 0L,
999 a+n-1-qsort_start + 0L);
1000 for (r=0;r<s;r++) {
1001 pm = a+n-s+r;
1002 VG_(printf)(" %3ld BB %#lx, ",
1003 pm-qsort_start + 0L,
1004 bb_addr((*pm)->bb));
1005 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
1009 if ((s = pb+1-pa) > 1) CLG_(qsort)(a, s, cmp);
1010 if ((s = pd+1-pc) > 1) CLG_(qsort)(a+n-s, s, cmp);
1014 /* Helpers for prepare_dump */
1016 static Int prepare_count;
1017 static BBCC** prepare_ptr;
1020 static void hash_addCount(BBCC* bbcc)
1022 if ((bbcc->ecounter_sum > 0) || (bbcc->ret_counter>0))
1023 prepare_count++;
1026 static void hash_addPtr(BBCC* bbcc)
1028 if ((bbcc->ecounter_sum == 0) &&
1029 (bbcc->ret_counter == 0)) return;
1031 *prepare_ptr = bbcc;
1032 prepare_ptr++;
1036 static void cs_addCount(thread_info* ti)
1038 Int i;
1039 BBCC* bbcc;
1041 /* add BBCCs with active call in call stack of current thread.
1042 * update cost sums for active calls
1045 for(i = 0; i < CLG_(current_call_stack).sp; i++) {
1046 call_entry* e = &(CLG_(current_call_stack).entry[i]);
1047 if (e->jcc == 0) continue;
1049 CLG_(add_diff_cost_lz)( CLG_(sets).full, &(e->jcc->cost),
1050 e->enter_cost, CLG_(current_state).cost);
1051 bbcc = e->jcc->from;
1053 CLG_DEBUG(1, " [%2d] (tid %u), added active: %s\n",
1054 i,CLG_(current_tid),bbcc->cxt->fn[0]->name);
1056 if (bbcc->ecounter_sum>0 || bbcc->ret_counter>0) {
1057 /* already counted */
1058 continue;
1060 prepare_count++;
1064 static void cs_addPtr(thread_info* ti)
1066 Int i;
1067 BBCC* bbcc;
1069 /* add BBCCs with active call in call stack of current thread.
1070 * update cost sums for active calls
1073 for(i = 0; i < CLG_(current_call_stack).sp; i++) {
1074 call_entry* e = &(CLG_(current_call_stack).entry[i]);
1075 if (e->jcc == 0) continue;
1077 bbcc = e->jcc->from;
1079 if (bbcc->ecounter_sum>0 || bbcc->ret_counter>0) {
1080 /* already counted */
1081 continue;
1084 *prepare_ptr = bbcc;
1085 prepare_ptr++;
1091 * Put all BBCCs with costs into a sorted array.
1092 * The returned arrays ends with a null pointer.
1093 * Must be freed after dumping.
1095 static
1096 BBCC** prepare_dump(void)
1098 BBCC **array;
1100 prepare_count = 0;
1102 /* if we do not separate among threads, this gives all */
1103 /* count number of BBCCs with >0 executions */
1104 CLG_(forall_bbccs)(hash_addCount);
1106 /* even if we do not separate among threads,
1107 * call stacks are separated */
1108 if (CLG_(clo).separate_threads)
1109 cs_addCount(0);
1110 else
1111 CLG_(forall_threads)(cs_addCount);
1113 CLG_DEBUG(0, "prepare_dump: %d BBCCs\n", prepare_count);
1115 /* allocate bbcc array, insert BBCCs and sort */
1116 prepare_ptr = array =
1117 (BBCC**) CLG_MALLOC("cl.dump.pd.1",
1118 (prepare_count+1) * sizeof(BBCC*));
1120 CLG_(forall_bbccs)(hash_addPtr);
1122 if (CLG_(clo).separate_threads)
1123 cs_addPtr(0);
1124 else
1125 CLG_(forall_threads)(cs_addPtr);
1127 CLG_ASSERT(array + prepare_count == prepare_ptr);
1129 /* end mark */
1130 *prepare_ptr = 0;
1132 CLG_DEBUG(0," BBCCs inserted\n");
1134 qsort_start = array;
1135 CLG_(qsort)(array, prepare_count, my_cmp);
1137 CLG_DEBUG(0," BBCCs sorted\n");
1139 return array;
1145 static void fprint_cost_ln(VgFile *fp, const HChar* prefix,
1146 const EventMapping* em, const ULong* cost)
1148 HChar *mcost = CLG_(mappingcost_as_string)(em, cost);
1149 VG_(fprintf)(fp, "%s%s\n", prefix, mcost);
1150 CLG_FREE(mcost);
1153 static ULong bbs_done = 0;
1154 static HChar* filename = 0;
1156 static
1157 void file_err(void)
1159 VG_(message)(Vg_UserMsg,
1160 "Error: can not open cache simulation output file `%s'\n",
1161 filename );
1162 VG_(exit)(1);
1166 * Create a new dump file and write header.
1168 * Naming: <CLG_(clo).filename_base>.<pid>[.<part>][-<tid>]
1169 * <part> is skipped for final dump (trigger==0)
1170 * <tid> is skipped for thread 1 with CLG_(clo).separate_threads=no
1172 * Returns the file descriptor, and -1 on error (no write permission)
1174 static VgFile *new_dumpfile(int tid, const HChar* trigger)
1176 Bool appending = False;
1177 int i;
1178 FullCost sum = 0;
1179 VgFile *fp;
1181 CLG_ASSERT(dumps_initialized);
1182 CLG_ASSERT(filename != 0);
1184 if (!CLG_(clo).combine_dumps) {
1185 i = VG_(sprintf)(filename, "%s", out_file);
1187 if (trigger)
1188 i += VG_(sprintf)(filename+i, ".%d", out_counter);
1190 if (CLG_(clo).separate_threads)
1191 VG_(sprintf)(filename+i, "-%02d", tid);
1193 fp = VG_(fopen)(filename, VKI_O_WRONLY|VKI_O_TRUNC, 0);
1195 else {
1196 VG_(sprintf)(filename, "%s", out_file);
1197 fp = VG_(fopen)(filename, VKI_O_WRONLY|VKI_O_APPEND, 0);
1198 if (fp && out_counter>1)
1199 appending = True;
1202 if (fp == NULL) {
1203 fp = VG_(fopen)(filename, VKI_O_CREAT|VKI_O_WRONLY,
1204 VKI_S_IRUSR|VKI_S_IWUSR);
1205 if (fp == NULL) {
1206 /* If the file can not be opened for whatever reason (conflict
1207 between multiple supervised processes?), give up now. */
1208 file_err();
1212 CLG_DEBUG(2, " new_dumpfile '%s'\n", filename);
1214 if (!appending)
1215 reset_dump_array();
1218 if (!appending) {
1219 /* callgrind format specification, has to be on 1st line */
1220 VG_(fprintf)(fp, "# callgrind format\n");
1222 /* version */
1223 VG_(fprintf)(fp, "version: 1\n");
1225 /* creator */
1226 VG_(fprintf)(fp, "creator: callgrind-" VERSION "\n");
1228 /* "pid:" line */
1229 VG_(fprintf)(fp, "pid: %d\n", VG_(getpid)());
1231 /* "cmd:" line */
1232 VG_(fprintf)(fp, "cmd: %s", cmdbuf);
1235 VG_(fprintf)(fp, "\npart: %d\n", out_counter);
1236 if (CLG_(clo).separate_threads) {
1237 VG_(fprintf)(fp, "thread: %d\n", tid);
1240 /* "desc:" lines */
1241 if (!appending) {
1242 VG_(fprintf)(fp, "\n");
1244 #if 0
1245 /* Global options changing the tracing behaviour */
1246 VG_(fprintf)(fp, "\ndesc: Option: --skip-plt=%s\n",
1247 CLG_(clo).skip_plt ? "yes" : "no");
1248 VG_(fprintf)(fp, "desc: Option: --collect-jumps=%s\n",
1249 CLG_(clo).collect_jumps ? "yes" : "no");
1250 VG_(fprintf)(fp, "desc: Option: --separate-recs=%d\n",
1251 CLG_(clo).separate_recursions);
1252 VG_(fprintf)(fp, "desc: Option: --separate-callers=%d\n",
1253 CLG_(clo).separate_callers);
1255 VG_(fprintf)(fp, "desc: Option: --dump-bbs=%s\n",
1256 CLG_(clo).dump_bbs ? "yes" : "no");
1257 VG_(fprintf)(fp, "desc: Option: --separate-threads=%s\n",
1258 CLG_(clo).separate_threads ? "yes" : "no");
1259 #endif
1261 (*CLG_(cachesim).dump_desc)(fp);
1264 VG_(fprintf)(fp, "\ndesc: Timerange: Basic block %llu - %llu\n",
1265 bbs_done, CLG_(stat).bb_executions);
1267 VG_(fprintf)(fp, "desc: Trigger: %s\n",
1268 trigger ? trigger : "Program termination");
1270 #if 0
1271 /* Output function specific config
1272 * FIXME */
1273 for (i = 0; i < N_FNCONFIG_ENTRIES; i++) {
1274 fnc = fnc_table[i];
1275 while (fnc) {
1276 if (fnc->skip) {
1277 VG_(fprintf)(fp, "desc: Option: --fn-skip=%s\n", fnc->name);
1279 if (fnc->dump_at_enter) {
1280 VG_(fprintf)(fp, "desc: Option: --fn-dump-at-enter=%s\n",
1281 fnc->name);
1283 if (fnc->dump_at_leave) {
1284 VG_(fprintf)(fp, "desc: Option: --fn-dump-at-leave=%s\n",
1285 fnc->name);
1287 if (fnc->separate_callers != CLG_(clo).separate_callers) {
1288 VG_(fprintf)(fp, "desc: Option: --separate-callers%d=%s\n",
1289 fnc->separate_callers, fnc->name);
1291 if (fnc->separate_recursions != CLG_(clo).separate_recursions) {
1292 VG_(fprintf)(fp, "desc: Option: --separate-recs%d=%s\n",
1293 fnc->separate_recursions, fnc->name);
1295 fnc = fnc->next;
1298 #endif
1300 /* "positions:" line */
1301 VG_(fprintf)(fp, "\npositions:%s%s%s\n",
1302 CLG_(clo).dump_instr ? " instr" : "",
1303 CLG_(clo).dump_bb ? " bb" : "",
1304 CLG_(clo).dump_line ? " line" : "");
1306 /* Some (optional) "event:" lines, giving long names to events. */
1307 switch (CLG_(clo).collect_systime) {
1308 case systime_no: break;
1309 case systime_msec:
1310 VG_(fprintf)(fp, "event: sysTime : sysTime (elapsed ms)\n");
1311 break;
1312 case systime_usec:
1313 VG_(fprintf)(fp, "event: sysTime : sysTime (elapsed us)\n");
1314 break;
1315 case systime_nsec:
1316 VG_(fprintf)(fp, "event: sysTime : sysTime (elapsed ns)\n");
1317 VG_(fprintf)(fp, "event: sysCpuTime : sysCpuTime (system cpu ns)\n");
1318 break;
1319 default:
1320 tl_assert(0);
1323 /* "events:" line
1324 Note: callgrind_annotate expects the "events:" line to be the last line
1325 of the PartData. In other words, this line is before the first line
1326 of the PartData body. */
1327 HChar *evmap = CLG_(eventmapping_as_string)(CLG_(dumpmap));
1328 VG_(fprintf)(fp, "events: %s\n", evmap);
1329 VG_(free)(evmap);
1331 /* summary lines */
1332 sum = CLG_(get_eventset_cost)( CLG_(sets).full );
1333 CLG_(zero_cost)(CLG_(sets).full, sum);
1334 if (CLG_(clo).separate_threads) {
1335 thread_info* ti = CLG_(get_current_thread)();
1336 CLG_(add_diff_cost)(CLG_(sets).full, sum, ti->lastdump_cost,
1337 ti->states.entry[0]->cost);
1339 else {
1340 /* This function is called once for thread 1, where
1341 * all costs are summed up when not dumping separate per thread.
1342 * But this is not true for summary: we need to add all threads.
1344 int t;
1345 thread_info** thr = CLG_(get_threads)();
1346 for(t=1;t<VG_N_THREADS;t++) {
1347 if (!thr[t]) continue;
1348 CLG_(add_diff_cost)(CLG_(sets).full, sum,
1349 thr[t]->lastdump_cost,
1350 thr[t]->states.entry[0]->cost);
1353 fprint_cost_ln(fp, "summary: ", CLG_(dumpmap), sum);
1355 /* all dumped cost will be added to total_fcc */
1356 CLG_(init_cost_lz)( CLG_(sets).full, &dump_total_cost );
1358 VG_(fprintf)(fp, "\n\n");
1360 if (VG_(clo_verbosity) > 1)
1361 VG_(message)(Vg_DebugMsg, "Dump to %s\n", filename);
1363 return fp;
1367 static void close_dumpfile(VgFile *fp)
1369 if (fp == NULL) return;
1371 fprint_cost_ln(fp, "totals: ", CLG_(dumpmap),
1372 dump_total_cost);
1373 //fprint_fcc_ln(fp, "summary: ", &dump_total_fcc);
1374 CLG_(add_cost_lz)(CLG_(sets).full,
1375 &CLG_(total_cost), dump_total_cost);
1377 VG_(fclose)(fp);
1379 if (filename[0] == '.') {
1380 if (-1 == VG_(rename) (filename, filename+1)) {
1381 /* Can not rename to correct file name: give out warning */
1382 VG_(message)(Vg_DebugMsg, "Warning: Can not rename .%s to %s\n",
1383 filename, filename);
1389 /* Helper for print_bbccs */
1391 static const HChar* print_trigger;
1393 static void print_bbccs_of_thread(thread_info* ti)
1395 BBCC **p, **array;
1396 FnPos lastFnPos;
1397 AddrPos lastAPos;
1399 CLG_DEBUG(1, "+ print_bbccs(tid %u)\n", CLG_(current_tid));
1401 VgFile *print_fp = new_dumpfile(CLG_(current_tid), print_trigger);
1402 if (print_fp == NULL) {
1403 CLG_DEBUG(1, "- print_bbccs(tid %u): No output...\n", CLG_(current_tid));
1404 return;
1407 p = array = prepare_dump();
1408 init_fpos(&lastFnPos);
1409 init_apos(&lastAPos, 0, 0, 0);
1411 while(1) {
1413 /* on context/function change, print old cost buffer before */
1414 if (lastFnPos.cxt && ((*p==0) ||
1415 (lastFnPos.cxt != (*p)->cxt) ||
1416 (lastFnPos.rec_index != (*p)->rec_index))) {
1417 if (!CLG_(is_zero_cost)( CLG_(sets).full, ccSum[currSum].cost )) {
1418 /* no need to switch buffers, as position is the same */
1419 fprint_apos(print_fp, &(ccSum[currSum].p), &lastAPos,
1420 lastFnPos.cxt->fn[0]->file);
1421 fprint_fcost(print_fp, &ccSum[currSum], &lastAPos);
1424 if (ccSum[currSum].p.file != lastFnPos.cxt->fn[0]->file) {
1425 /* switch back to file of function */
1426 print_file(print_fp, "fe=", lastFnPos.cxt->fn[0]->file);
1428 VG_(fprintf)(print_fp, "\n");
1431 if (*p == 0) break;
1433 if (print_fn_pos(print_fp, &lastFnPos, *p)) {
1435 /* new function */
1436 init_apos(&lastAPos, 0, 0, (*p)->cxt->fn[0]->file);
1437 init_fcost(&ccSum[0], 0, 0, 0);
1438 init_fcost(&ccSum[1], 0, 0, 0);
1439 currSum = 0;
1442 if (CLG_(clo).dump_bbs) {
1443 /* FIXME: Specify Object of BB if different to object of fn */
1444 int i;
1445 ULong ecounter = (*p)->ecounter_sum;
1446 VG_(fprintf)(print_fp, "bb=%#lx ", (UWord)(*p)->bb->offset);
1447 for(i = 0; i<(*p)->bb->cjmp_count;i++) {
1448 VG_(fprintf)(print_fp, "%u %llu ",
1449 (*p)->bb->jmp[i].instr,
1450 ecounter);
1451 ecounter -= (*p)->jmp[i].ecounter;
1453 VG_(fprintf)(print_fp, "%u %llu\n",
1454 (*p)->bb->instr_count,
1455 ecounter);
1458 fprint_bbcc(print_fp, *p, &lastAPos);
1460 p++;
1463 close_dumpfile(print_fp);
1464 VG_(free)(array);
1466 /* set counters of last dump */
1467 CLG_(copy_cost)( CLG_(sets).full, ti->lastdump_cost,
1468 CLG_(current_state).cost );
1470 CLG_DEBUG(1, "- print_bbccs(tid %u)\n", CLG_(current_tid));
1474 static void print_bbccs(const HChar* trigger, Bool only_current_thread)
1476 init_dump_array();
1477 init_debug_cache();
1479 print_trigger = trigger;
1481 if (!CLG_(clo).separate_threads) {
1482 /* All BBCC/JCC costs is stored for thread 1 */
1483 Int orig_tid = CLG_(current_tid);
1485 CLG_(switch_thread)(1);
1486 print_bbccs_of_thread( CLG_(get_current_thread)() );
1487 CLG_(switch_thread)(orig_tid);
1489 else if (only_current_thread)
1490 print_bbccs_of_thread( CLG_(get_current_thread)() );
1491 else
1492 CLG_(forall_threads)(print_bbccs_of_thread);
1494 free_dump_array();
1498 void CLG_(dump_profile)(const HChar* trigger, Bool only_current_thread)
1500 CLG_DEBUG(2, "+ dump_profile(Trigger '%s')\n",
1501 trigger ? trigger : "Prg.Term.");
1503 CLG_(init_dumps)();
1505 if (VG_(clo_verbosity) > 1)
1506 VG_(message)(Vg_DebugMsg, "Start dumping at BB %llu (%s)...\n",
1507 CLG_(stat).bb_executions,
1508 trigger ? trigger : "Prg.Term.");
1510 out_counter++;
1512 print_bbccs(trigger, only_current_thread);
1514 bbs_done = CLG_(stat).bb_executions++;
1516 if (VG_(clo_verbosity) > 1)
1517 VG_(message)(Vg_DebugMsg, "Dumping done.\n");
1520 /* Copy command to cmd buffer. We want to original command line
1521 * (can change at runtime)
1523 static
1524 void init_cmdbuf(void)
1526 SizeT size;
1527 Int i,j;
1529 /* Pass #1: How many bytes do we need? */
1530 size = 1; // leading ' '
1531 size += VG_(strlen)( VG_(args_the_exename) );
1532 for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) {
1533 const HChar *arg = *(HChar**)VG_(indexXA)( VG_(args_for_client), i );
1534 size += 1; // separator ' '
1535 // escape NL in arguments to not break dump format
1536 for(j=0; arg[j]; j++)
1537 switch(arg[j]) {
1538 case '\n':
1539 case '\\':
1540 size++; // fall through
1541 default:
1542 size++;
1546 cmdbuf = CLG_MALLOC("cl.dump.ic.1", size + 1); // +1 for '\0'
1548 /* Pass #2: Build up the string */
1549 size = VG_(sprintf)(cmdbuf, " %s", VG_(args_the_exename));
1551 for(i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) {
1552 const HChar *arg = * (HChar**) VG_(indexXA)( VG_(args_for_client), i );
1553 cmdbuf[size++] = ' ';
1554 for(j=0; arg[j]; j++)
1555 switch(arg[j]) {
1556 case '\n':
1557 cmdbuf[size++] = '\\';
1558 cmdbuf[size++] = 'n';
1559 break;
1560 case '\\':
1561 cmdbuf[size++] = '\\';
1562 cmdbuf[size++] = '\\';
1563 break;
1564 default:
1565 cmdbuf[size++] = arg[j];
1566 break;
1569 cmdbuf[size] = '\0';
1573 * Set up file names for dump output: <out_file>.
1574 * <out_file> is derived from the output format string, which defaults
1575 * to "callgrind.out.%p", where %p is replaced with the PID.
1576 * For the final file name, on intermediate dumps a counter is appended,
1577 * and further, if separate dumps per thread are requested, the thread ID.
1579 * <out_file> always starts with a full absolute path.
1580 * If the output format string represents a relative path, the current
1581 * working directory at program start is used.
1583 * This function has to be called every time a profile dump is generated
1584 * to be able to react on PID changes.
1586 void CLG_(init_dumps)(void)
1588 SysRes res;
1590 static int thisPID = 0;
1591 int currentPID = VG_(getpid)();
1592 if (currentPID == thisPID) {
1593 /* already initialized, and no PID change */
1594 CLG_ASSERT(out_file != 0);
1595 return;
1597 thisPID = currentPID;
1599 if (!CLG_(clo).out_format)
1600 CLG_(clo).out_format = DEFAULT_OUTFORMAT;
1602 /* If a file name was already set, clean up before */
1603 if (out_file) {
1604 VG_(free)(out_file);
1605 VG_(free)(filename);
1606 out_counter = 0;
1609 // Setup output filename.
1610 out_file =
1611 VG_(expand_file_name)("--callgrind-out-file", CLG_(clo).out_format);
1613 /* allocate space big enough for final filenames */
1614 filename = (HChar*) CLG_MALLOC("cl.dump.init_dumps.2",
1615 VG_(strlen)(out_file)+32);
1617 /* Make sure the output base file can be written.
1618 * This is used for the dump at program termination.
1619 * We stop with an error here if we can not create the
1620 * file: This is probably because of missing rights,
1621 * and trace parts wouldn't be allowed to be written, too.
1623 VG_(strcpy)(filename, out_file);
1624 res = VG_(open)(filename, VKI_O_WRONLY|VKI_O_TRUNC, 0);
1625 if (sr_isError(res)) {
1626 res = VG_(open)(filename, VKI_O_CREAT|VKI_O_WRONLY,
1627 VKI_S_IRUSR|VKI_S_IWUSR);
1628 if (sr_isError(res)) {
1629 file_err();
1632 if (!sr_isError(res)) VG_(close)( (Int)sr_Res(res) );
1634 if (!dumps_initialized)
1635 init_cmdbuf();
1637 dumps_initialized = True;