1 /*--------------------------------------------------------------------*/
4 /*--------------------------------------------------------------------*/
7 This file is part of Callgrind, a Valgrind tool for call tracing.
9 Copyright (C) 2002-2010, 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, write to the Free Software
23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 The GNU General Public License is contained in the file COPYING.
32 #include "pub_tool_threadstate.h"
33 #include "pub_tool_libcfile.h"
36 /* Dump Part Counter */
37 static Int out_counter = 0;
39 static Char* out_file = 0;
40 static Char* out_directory = 0;
41 static Bool dumps_initialized = False;
44 static Char cmdbuf[BUF_LEN];
46 /* Total reads/writes/misses sum over all dumps and threads.
47 * Updated during CC traversal at dump time.
49 FullCost CLG_(total_cost) = 0;
50 static FullCost dump_total_cost = 0;
52 EventMapping* CLG_(dumpmap) = 0;
54 /* Temporary output buffer for
55 * print_fn_pos, fprint_apos, fprint_fcost, fprint_jcc,
56 * fprint_fcc_ln, dump_run_info, dump_state_info
58 static Char outbuf[FILENAME_LEN + FN_NAME_LEN + OBJ_NAME_LEN + COSTS_LEN];
60 Int CLG_(get_dump_counter)(void)
65 Char* CLG_(get_out_file)()
71 Char* CLG_(get_out_directory)()
77 /*------------------------------------------------------------*/
78 /*--- Output file related stuff ---*/
79 /*------------------------------------------------------------*/
81 /* Boolean dumping array */
82 static Bool* dump_array = 0;
83 static Int dump_array_size = 0;
84 static Bool* obj_dumped = 0;
85 static Bool* file_dumped = 0;
86 static Bool* fn_dumped = 0;
87 static Bool* cxt_dumped = 0;
90 void reset_dump_array(void)
94 CLG_ASSERT(dump_array != 0);
96 for(i=0;i<dump_array_size;i++)
97 dump_array[i] = False;
101 void init_dump_array(void)
103 dump_array_size = CLG_(stat).distinct_objs +
104 CLG_(stat).distinct_files +
105 CLG_(stat).distinct_fns +
106 CLG_(stat).context_counter;
107 CLG_ASSERT(dump_array == 0);
108 dump_array = (Bool*) CLG_MALLOC("cl.dump.ida.1",
109 dump_array_size * sizeof(Bool));
110 obj_dumped = dump_array;
111 file_dumped = obj_dumped + CLG_(stat).distinct_objs;
112 fn_dumped = file_dumped + CLG_(stat).distinct_files;
113 cxt_dumped = fn_dumped + CLG_(stat).distinct_fns;
117 CLG_DEBUG(1, " init_dump_array: size %d\n", dump_array_size);
121 void free_dump_array(void)
123 CLG_ASSERT(dump_array != 0);
124 VG_(free)(dump_array);
134 /* Initialize to an invalid position */
136 void init_fpos(FnPos* p)
148 static void my_fwrite(Int fd, Char* buf, Int len)
150 VG_(write)(fd, (void*)buf, len);
154 #define FWRITE_BUFSIZE 32000
155 #define FWRITE_THROUGH 10000
156 static Char fwrite_buf[FWRITE_BUFSIZE];
157 static Int fwrite_pos;
158 static Int fwrite_fd = -1;
161 void fwrite_flush(void)
163 if ((fwrite_fd>=0) && (fwrite_pos>0))
164 VG_(write)(fwrite_fd, (void*)fwrite_buf, fwrite_pos);
168 static void my_fwrite(Int fd, Char* buf, Int len)
170 if (fwrite_fd != fd) {
174 if (len > FWRITE_THROUGH) {
176 VG_(write)(fd, (void*)buf, len);
179 if (FWRITE_BUFSIZE - fwrite_pos <= len) fwrite_flush();
180 VG_(strncpy)(fwrite_buf + fwrite_pos, buf, len);
186 static void print_obj(Char* buf, obj_node* obj)
190 if (CLG_(clo).compress_strings) {
191 CLG_ASSERT(obj_dumped != 0);
192 if (obj_dumped[obj->number])
193 /*n =*/ VG_(sprintf)(buf, "(%d)\n", obj->number);
195 /*n =*/ VG_(sprintf)(buf, "(%d) %s\n",
196 obj->number, obj->name);
200 /*n =*/ VG_(sprintf)(buf, "%s\n", obj->name);
203 /* add mapping parameters the first time a object is dumped
204 * format: mp=0xSTART SIZE 0xOFFSET */
205 if (!obj_dumped[obj->number]) {
206 obj_dumped[obj->number];
207 VG_(sprintf)(buf+n, "mp=%p %p %p\n",
208 pos->obj->start, pos->obj->size, pos->obj->offset);
211 obj_dumped[obj->number] = True;
215 static void print_file(Char* buf, file_node* file)
217 if (CLG_(clo).compress_strings) {
218 CLG_ASSERT(file_dumped != 0);
219 if (file_dumped[file->number])
220 VG_(sprintf)(buf, "(%d)\n", file->number);
222 VG_(sprintf)(buf, "(%d) %s\n",
223 file->number, file->name);
224 file_dumped[file->number] = True;
228 VG_(sprintf)(buf, "%s\n", file->name);
232 * tag can be "fn", "cfn", "jfn"
234 static void print_fn(Int fd, Char* buf, Char* tag, fn_node* fn)
237 p = VG_(sprintf)(buf, "%s=",tag);
238 if (CLG_(clo).compress_strings) {
239 CLG_ASSERT(fn_dumped != 0);
240 if (fn_dumped[fn->number])
241 p += VG_(sprintf)(buf+p, "(%d)\n", fn->number);
243 p += VG_(sprintf)(buf+p, "(%d) %s\n",
244 fn->number, fn->name);
245 fn_dumped[fn->number] = True;
249 p += VG_(sprintf)(buf+p, "%s\n", fn->name);
251 my_fwrite(fd, buf, p);
254 static void print_mangled_fn(Int fd, Char* buf, Char* tag,
255 Context* cxt, int rec_index)
259 if (CLG_(clo).compress_strings && CLG_(clo).compress_mangled) {
264 CLG_ASSERT(cxt_dumped != 0);
265 if (cxt_dumped[cxt->base_number+rec_index]) {
266 p = VG_(sprintf)(buf, "%s=(%d)\n",
267 tag, cxt->base_number + rec_index);
268 my_fwrite(fd, buf, p);
273 /* make sure that for all context parts compressed data is written */
274 for(i=cxt->size;i>0;i--) {
275 CLG_ASSERT(cxt->fn[i-1]->pure_cxt != 0);
276 n = cxt->fn[i-1]->pure_cxt->base_number;
277 if (cxt_dumped[n]) continue;
278 p = VG_(sprintf)(buf, "%s=(%d) %s\n",
279 tag, n, cxt->fn[i-1]->name);
280 my_fwrite(fd, buf, p);
282 cxt_dumped[n] = True;
283 last = cxt->fn[i-1]->pure_cxt;
285 /* If the last context was the context to print, we are finished */
286 if ((last == cxt) && (rec_index == 0)) return;
288 p = VG_(sprintf)(buf, "%s=(%d) (%d)", tag,
289 cxt->base_number + rec_index,
290 cxt->fn[0]->pure_cxt->base_number);
292 p += VG_(sprintf)(buf+p, "'%d", rec_index +1);
293 for(i=1;i<cxt->size;i++)
294 p += VG_(sprintf)(buf+p, "'(%d)",
295 cxt->fn[i]->pure_cxt->base_number);
296 p += VG_(sprintf)(buf+p, "\n");
297 my_fwrite(fd, buf, p);
299 cxt_dumped[cxt->base_number+rec_index] = True;
304 p = VG_(sprintf)(buf, "%s=", tag);
305 if (CLG_(clo).compress_strings) {
306 CLG_ASSERT(cxt_dumped != 0);
307 if (cxt_dumped[cxt->base_number+rec_index]) {
308 p += VG_(sprintf)(buf+p, "(%d)\n", cxt->base_number + rec_index);
309 my_fwrite(fd, buf, p);
313 p += VG_(sprintf)(buf+p, "(%d) ", cxt->base_number + rec_index);
314 cxt_dumped[cxt->base_number+rec_index] = True;
318 p += VG_(sprintf)(buf+p, "%s", cxt->fn[0]->name);
320 p += VG_(sprintf)(buf+p, "'%d", rec_index +1);
321 for(i=1;i<cxt->size;i++)
322 p += VG_(sprintf)(buf+p, "'%s", cxt->fn[i]->name);
324 p += VG_(sprintf)(buf+p, "\n");
325 my_fwrite(fd, buf, p);
331 * Print function position of the BBCC, but only print info differing to
332 * the <last> position, update <last>
333 * Return True if something changes.
335 static Bool print_fn_pos(int fd, FnPos* last, BBCC* bbcc)
339 CLG_ASSERT(bbcc && bbcc->cxt);
342 CLG_DEBUG(2, "+ print_fn_pos: ");
343 CLG_(print_cxt)(16, bbcc->cxt, bbcc->rec_index);
346 if (!CLG_(clo).mangle_names) {
347 if (last->rec_index != bbcc->rec_index) {
348 VG_(sprintf)(outbuf, "rec=%d\n\n", bbcc->rec_index);
349 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
350 last->rec_index = bbcc->rec_index;
351 last->cxt = 0; /* reprint context */
355 if (last->cxt != bbcc->cxt) {
356 fn_node* last_from = (last->cxt && last->cxt->size >1) ?
357 last->cxt->fn[1] : 0;
358 fn_node* curr_from = (bbcc->cxt->size >1) ?
359 bbcc->cxt->fn[1] : 0;
360 if (curr_from == 0) {
361 if (last_from != 0) {
362 /* switch back to no context */
363 VG_(sprintf)(outbuf, "frfn=(spontaneous)\n");
364 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
368 else if (last_from != curr_from) {
369 print_fn(fd,outbuf,"frfn", curr_from);
372 last->cxt = bbcc->cxt;
376 if (last->obj != bbcc->cxt->fn[0]->file->obj) {
377 VG_(sprintf)(outbuf, "ob=");
378 print_obj(outbuf+3, bbcc->cxt->fn[0]->file->obj);
379 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
380 last->obj = bbcc->cxt->fn[0]->file->obj;
384 if (last->file != bbcc->cxt->fn[0]->file) {
385 VG_(sprintf)(outbuf, "fl=");
386 print_file(outbuf+3, bbcc->cxt->fn[0]->file);
387 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
388 last->file = bbcc->cxt->fn[0]->file;
392 if (!CLG_(clo).mangle_names) {
393 if (last->fn != bbcc->cxt->fn[0]) {
394 print_fn(fd,outbuf, "fn", bbcc->cxt->fn[0]);
395 last->fn = bbcc->cxt->fn[0];
400 /* Print mangled name if context or rec_index changes */
401 if ((last->rec_index != bbcc->rec_index) ||
402 (last->cxt != bbcc->cxt)) {
404 print_mangled_fn(fd, outbuf, "fn", bbcc->cxt, bbcc->rec_index);
405 last->fn = bbcc->cxt->fn[0];
406 last->rec_index = bbcc->rec_index;
411 last->cxt = bbcc->cxt;
413 CLG_DEBUG(2, "- print_fn_pos: %s\n", res ? "changed" : "");
418 /* the debug lookup cache is useful if BBCC for same BB are
419 * dumped directly in a row. This is a direct mapped cache.
421 #define DEBUG_CACHE_SIZE 1777
423 static Addr debug_cache_addr[DEBUG_CACHE_SIZE];
424 static file_node* debug_cache_file[DEBUG_CACHE_SIZE];
425 static int debug_cache_line[DEBUG_CACHE_SIZE];
426 static Bool debug_cache_info[DEBUG_CACHE_SIZE];
429 void init_debug_cache(void)
432 for(i=0;i<DEBUG_CACHE_SIZE;i++) {
433 debug_cache_addr[i] = 0;
434 debug_cache_file[i] = 0;
435 debug_cache_line[i] = 0;
436 debug_cache_info[i] = 0;
440 static /* __inline__ */
441 Bool get_debug_pos(BBCC* bbcc, Addr addr, AddrPos* p)
443 Char file[FILENAME_LEN];
444 Char dir[FILENAME_LEN];
445 Bool found_file_line, found_dirname;
447 int cachepos = addr % DEBUG_CACHE_SIZE;
449 if (debug_cache_addr[cachepos] == addr) {
450 p->line = debug_cache_line[cachepos];
451 p->file = debug_cache_file[cachepos];
452 found_file_line = debug_cache_info[cachepos];
455 found_file_line = VG_(get_filename_linenum)(addr,
460 if (!found_file_line) {
461 VG_(strcpy)(file, "???");
466 CLG_ASSERT(VG_(strlen)(dir) + VG_(strlen)(file) + 1 < FILENAME_LEN);
467 VG_(strcat)(dir, "/"); // Append '/'
468 VG_(strcat)(dir, file); // Append file to dir
469 VG_(strcpy)(file, dir); // Move dir+file to file
471 p->file = CLG_(get_file_node)(bbcc->bb->obj, file);
473 debug_cache_info[cachepos] = found_file_line;
474 debug_cache_addr[cachepos] = addr;
475 debug_cache_line[cachepos] = p->line;
476 debug_cache_file[cachepos] = p->file;
479 /* Address offset from bbcc start address */
480 p->addr = addr - bbcc->bb->obj->offset;
481 p->bb_addr = bbcc->bb->offset;
483 CLG_DEBUG(3, " get_debug_pos(%#lx): BB %#lx, fn '%s', file '%s', line %u\n",
484 addr, bb_addr(bbcc->bb), bbcc->cxt->fn[0]->name,
485 p->file->name, p->line);
487 return found_file_line;
491 /* copy file position and init cost */
492 static void init_apos(AddrPos* p, Addr addr, Addr bbaddr, file_node* file)
500 static void copy_apos(AddrPos* dst, AddrPos* src)
502 dst->addr = src->addr;
503 dst->bb_addr = src->bb_addr;
504 dst->file = src->file;
505 dst->line = src->line;
508 /* copy file position and init cost */
509 static void init_fcost(AddrCost* c, Addr addr, Addr bbaddr, file_node* file)
511 init_apos( &(c->p), addr, bbaddr, file);
512 /* FIXME: This is a memory leak as a AddrCost is inited multiple times */
513 c->cost = CLG_(get_eventset_cost)( CLG_(sets).full );
514 CLG_(init_cost)( CLG_(sets).full, c->cost );
519 * print position change inside of a BB (last -> curr)
520 * this doesn't update last to curr!
522 static void fprint_apos(Int fd, AddrPos* curr, AddrPos* last, file_node* func_file)
524 CLG_ASSERT(curr->file != 0);
525 CLG_DEBUG(2, " print_apos(file '%s', line %d, bb %#lx, addr %#lx) fnFile '%s'\n",
526 curr->file->name, curr->line, curr->bb_addr, curr->addr,
529 if (curr->file != last->file) {
531 /* if we switch back to orig file, use fe=... */
532 if (curr->file == func_file)
533 VG_(sprintf)(outbuf, "fe=");
535 VG_(sprintf)(outbuf, "fi=");
536 print_file(outbuf+3, curr->file);
537 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
540 if (CLG_(clo).dump_bbs) {
541 if (curr->line != last->line) {
542 VG_(sprintf)(outbuf, "ln=%d\n", curr->line);
543 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
552 * This prints out differences if allowed
554 * This doesn't set last to curr afterwards!
557 void fprint_pos(Int fd, AddrPos* curr, AddrPos* last)
559 if (0) //CLG_(clo).dump_bbs)
560 VG_(sprintf)(outbuf, "%lu ", curr->addr - curr->bb_addr);
563 if (CLG_(clo).dump_instr) {
564 int diff = curr->addr - last->addr;
565 if ( CLG_(clo).compress_pos && (last->addr >0) &&
566 (diff > -100) && (diff < 100)) {
568 p = VG_(sprintf)(outbuf, "+%d ", diff);
570 p = VG_(sprintf)(outbuf, "* ");
572 p = VG_(sprintf)(outbuf, "%d ", diff);
575 p = VG_(sprintf)(outbuf, "%#lx ", curr->addr);
578 if (CLG_(clo).dump_bb) {
579 int diff = curr->bb_addr - last->bb_addr;
580 if ( CLG_(clo).compress_pos && (last->bb_addr >0) &&
581 (diff > -100) && (diff < 100)) {
583 p += VG_(sprintf)(outbuf+p, "+%d ", diff);
585 p += VG_(sprintf)(outbuf+p, "* ");
587 p += VG_(sprintf)(outbuf+p, "%d ", diff);
590 p += VG_(sprintf)(outbuf+p, "%#lx ", curr->bb_addr);
593 if (CLG_(clo).dump_line) {
594 int diff = curr->line - last->line;
595 if ( CLG_(clo).compress_pos && (last->line >0) &&
596 (diff > -100) && (diff < 100)) {
599 VG_(sprintf)(outbuf+p, "+%d ", diff);
601 VG_(sprintf)(outbuf+p, "* ");
603 VG_(sprintf)(outbuf+p, "%d ", diff);
606 VG_(sprintf)(outbuf+p, "%u ", curr->line);
609 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
618 void fprint_cost(int fd, EventMapping* es, ULong* cost)
620 int p = CLG_(sprint_mappingcost)(outbuf, es, cost);
621 VG_(sprintf)(outbuf+p, "\n");
622 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
628 /* Write the cost of a source line; only that parts of the source
629 * position are written that changed relative to last written position.
630 * funcPos is the source position of the first line of actual function.
631 * Something is written only if cost != 0; returns True in this case.
633 static void fprint_fcost(Int fd, AddrCost* c, AddrPos* last)
636 CLG_DEBUG(2, " print_fcost(file '%s', line %d, bb %#lx, addr %#lx):\n",
637 c->p.file->name, c->p.line, c->p.bb_addr, c->p.addr);
638 CLG_(print_cost)(-5, CLG_(sets).full, c->cost);
641 fprint_pos(fd, &(c->p), last);
642 copy_apos( last, &(c->p) ); /* update last to current position */
644 fprint_cost(fd, CLG_(dumpmap), c->cost);
646 /* add cost to total */
647 CLG_(add_and_zero_cost)( CLG_(sets).full, dump_total_cost, c->cost );
651 /* Write out the calls from jcc (at pos)
653 static void fprint_jcc(Int fd, jCC* jcc, AddrPos* curr, AddrPos* last, ULong ecounter)
655 static AddrPos target;
660 CLG_DEBUG(2, " fprint_jcc (jkind %d)\n", jcc->jmpkind);
661 CLG_(print_jcc)(-10, jcc);
664 CLG_ASSERT(jcc->to !=0);
665 CLG_ASSERT(jcc->from !=0);
667 if (!get_debug_pos(jcc->to, bb_addr(jcc->to->bb), &target)) {
668 /* if we don't have debug info, don't switch to file "???" */
669 target.file = last->file;
672 if ((jcc->jmpkind == JmpCond) || (jcc->jmpkind == Ijk_Boring)) {
674 /* this is a JCC for a followed conditional or boring jump. */
675 CLG_ASSERT(CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost));
677 /* objects among jumps should be the same.
678 * Otherwise this jump would have been changed to a call
681 CLG_ASSERT(jcc->from->bb->obj == jcc->to->bb->obj);
683 /* only print if target position info is usefull */
684 if (!CLG_(clo).dump_instr && !CLG_(clo).dump_bb && target.line==0) {
685 jcc->call_counter = 0;
689 /* Different files/functions are possible e.g. with longjmp's
690 * which change the stack, and thus context
692 if (last->file != target.file) {
693 VG_(sprintf)(outbuf, "jfi=");
694 print_file(outbuf+4, target.file);
695 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
698 if (jcc->from->cxt != jcc->to->cxt) {
699 if (CLG_(clo).mangle_names)
700 print_mangled_fn(fd, outbuf, "jfn",
701 jcc->to->cxt, jcc->to->rec_index);
703 print_fn(fd, outbuf, "jfn", jcc->to->cxt->fn[0]);
706 if (jcc->jmpkind == JmpCond) {
707 /* format: jcnd=<followed>/<executions> <target> */
708 VG_(sprintf)(outbuf, "jcnd=%llu/%llu ",
709 jcc->call_counter, ecounter);
712 /* format: jump=<jump count> <target> */
713 VG_(sprintf)(outbuf, "jump=%llu ",
716 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
718 fprint_pos(fd, &target, last);
719 my_fwrite(fd, "\n", 1);
720 fprint_pos(fd, curr, last);
721 my_fwrite(fd, "\n", 1);
723 jcc->call_counter = 0;
727 file = jcc->to->cxt->fn[0]->file;
728 obj = jcc->to->bb->obj;
730 /* object of called position different to object of this function?*/
731 if (jcc->from->cxt->fn[0]->file->obj != obj) {
732 VG_(sprintf)(outbuf, "cob=");
733 print_obj(outbuf+4, obj);
734 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
737 /* file of called position different to current file? */
738 if (last->file != file) {
739 VG_(sprintf)(outbuf, "cfi=");
740 print_file(outbuf+4, file);
741 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
744 if (CLG_(clo).mangle_names)
745 print_mangled_fn(fd, outbuf, "cfn", jcc->to->cxt, jcc->to->rec_index);
747 print_fn(fd, outbuf, "cfn", jcc->to->cxt->fn[0]);
749 if (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost)) {
750 VG_(sprintf)(outbuf, "calls=%llu ",
752 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
754 fprint_pos(fd, &target, last);
755 my_fwrite(fd, "\n", 1);
756 fprint_pos(fd, curr, last);
757 fprint_cost(fd, CLG_(dumpmap), jcc->cost);
759 CLG_(init_cost)( CLG_(sets).full, jcc->cost );
761 jcc->call_counter = 0;
767 /* Cost summation of functions.We use alternately ccSum[0/1], thus
768 * ssSum[currSum] for recently read lines with same line number.
770 static AddrCost ccSum[2];
774 * Print all costs of a BBCC:
775 * - FCCs of instructions
776 * - JCCs of the unique jump of this BB
777 * returns True if something was written
779 static Bool fprint_bbcc(Int fd, BBCC* bbcc, AddrPos* last)
781 InstrInfo* instr_info;
783 Bool something_written = False;
785 AddrCost *currCost, *newCost;
786 Int jcc_count = 0, instr, i, jmp;
789 CLG_ASSERT(bbcc->cxt != 0);
791 VG_(printf)("+ fprint_bbcc (Instr %d): ", bb->instr_count);
792 CLG_(print_bbcc)(15, bbcc);
795 CLG_ASSERT(currSum == 0 || currSum == 1);
796 currCost = &(ccSum[currSum]);
797 newCost = &(ccSum[1-currSum]);
799 ecounter = bbcc->ecounter_sum;
801 instr_info = &(bb->instr[0]);
802 for(instr=0; instr<bb->instr_count; instr++, instr_info++) {
804 /* get debug info of current instruction address and dump cost
805 * if CLG_(clo).dump_bbs or file/line has changed
807 if (!get_debug_pos(bbcc, bb_addr(bb) + instr_info->instr_offset,
809 /* if we don't have debug info, don't switch to file "???" */
810 newCost->p.file = bbcc->cxt->fn[0]->file;
813 if (CLG_(clo).dump_bbs || CLG_(clo).dump_instr ||
814 (newCost->p.line != currCost->p.line) ||
815 (newCost->p.file != currCost->p.file)) {
817 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
818 something_written = True;
820 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
821 fprint_fcost(fd, currCost, last);
825 currSum = 1 - currSum;
826 currCost = &(ccSum[currSum]);
827 newCost = &(ccSum[1-currSum]);
830 /* add line cost to current cost sum */
831 (*CLG_(cachesim).add_icost)(currCost->cost, bbcc, instr_info, ecounter);
833 /* print jcc's if there are: only jumps */
834 if (bb->jmp[jmp].instr == instr) {
836 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from)
837 if (((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
838 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
842 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
843 /* no need to switch buffers, as position is the same */
844 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
845 fprint_fcost(fd, currCost, last);
847 get_debug_pos(bbcc, bb_addr(bb)+instr_info->instr_offset, &(currCost->p));
848 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
849 something_written = True;
850 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
851 if (((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
852 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
853 fprint_jcc(fd, jcc, &(currCost->p), last, ecounter);
858 /* update execution counter */
859 if (jmp < bb->cjmp_count)
860 if (bb->jmp[jmp].instr == instr) {
861 ecounter -= bbcc->jmp[jmp].ecounter;
866 /* jCCs at end? If yes, dump cumulated line info first */
868 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
869 /* yes, if JCC only counts jmp arcs or cost >0 */
870 if ( ((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
871 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
875 if ( (bbcc->skipped &&
876 !CLG_(is_zero_cost)(CLG_(sets).full, bbcc->skipped)) ||
879 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
880 /* no need to switch buffers, as position is the same */
881 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
882 fprint_fcost(fd, currCost, last);
885 get_debug_pos(bbcc, bb_jmpaddr(bb), &(currCost->p));
886 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
887 something_written = True;
889 /* first, print skipped costs for calls */
890 if (bbcc->skipped && !CLG_(is_zero_cost)( CLG_(sets).full,
892 CLG_(add_and_zero_cost)( CLG_(sets).full,
893 currCost->cost, bbcc->skipped );
895 VG_(sprintf)(outbuf, "# Skipped\n");
896 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
898 fprint_fcost(fd, currCost, last);
902 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
903 CLG_ASSERT(jcc->jmp == jmp);
904 if ( ((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
905 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
907 fprint_jcc(fd, jcc, &(currCost->p), last, ecounter);
911 if (CLG_(clo).dump_bbs || CLG_(clo).dump_bb) {
912 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
913 something_written = True;
915 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
916 fprint_fcost(fd, currCost, last);
918 if (CLG_(clo).dump_bbs) my_fwrite(fd, (void*)"\n", 1);
920 /* when every cost was immediatly written, we must have done so,
921 * as this function is only called when there's cost in a BBCC
923 CLG_ASSERT(something_written);
926 bbcc->ecounter_sum = 0;
927 for(i=0; i<=bbcc->bb->cjmp_count; i++)
928 bbcc->jmp[i].ecounter = 0;
929 bbcc->ret_counter = 0;
931 CLG_DEBUG(1, "- fprint_bbcc: JCCs %d\n", jcc_count);
933 return something_written;
938 * from->bb->obj, from->bb->fn
939 * obj, fn[0]->file, fn
942 static int my_cmp(BBCC** pbbcc1, BBCC** pbbcc2)
945 return (*pbbcc1)->bb->offset - (*pbbcc2)->bb->offset;
947 BBCC *bbcc1 = *pbbcc1;
948 BBCC *bbcc2 = *pbbcc2;
949 Context* cxt1 = bbcc1->cxt;
950 Context* cxt2 = bbcc2->cxt;
953 if (cxt1->fn[0]->file->obj != cxt2->fn[0]->file->obj)
954 return cxt1->fn[0]->file->obj - cxt2->fn[0]->file->obj;
956 if (cxt1->fn[0]->file != cxt2->fn[0]->file)
957 return cxt1->fn[0]->file - cxt2->fn[0]->file;
959 if (cxt1->fn[0] != cxt2->fn[0])
960 return cxt1->fn[0] - cxt2->fn[0];
962 if (bbcc1->rec_index != bbcc2->rec_index)
963 return bbcc1->rec_index - bbcc2->rec_index;
965 while((off < cxt1->size) && (off < cxt2->size)) {
966 fn_node* ffn1 = cxt1->fn[off];
967 fn_node* ffn2 = cxt2->fn[off];
968 if (ffn1->file->obj != ffn2->file->obj)
969 return ffn1->file->obj - ffn2->file->obj;
974 if (cxt1->size > cxt2->size) return 1;
975 else if (cxt1->size < cxt2->size) return -1;
977 return bbcc1->bb->offset - bbcc2->bb->offset;
985 /* modified version of:
987 * qsort -- qsort interface implemented by faster quicksort.
988 * J. L. Bentley and M. D. McIlroy, SPE 23 (1993) 1249-1265.
989 * Copyright 1993, John Wiley.
993 void swapfunc(BBCC** a, BBCC** b, int n)
996 BBCC* t = *a; *a = *b; *b = t;
1003 void swap(BBCC** a, BBCC** b)
1006 t = *a; *a = *b; *b = t;
1009 #define min(x, y) ((x)<=(y) ? (x) : (y))
1012 BBCC** med3(BBCC **a, BBCC **b, BBCC **c, int (*cmp)(BBCC**,BBCC**))
1013 { return cmp(a, b) < 0 ?
1014 (cmp(b, c) < 0 ? b : cmp(a, c) < 0 ? c : a)
1015 : (cmp(b, c) > 0 ? b : cmp(a, c) > 0 ? c : a);
1018 static BBCC** qsort_start = 0;
1020 static void qsort(BBCC **a, int n, int (*cmp)(BBCC**,BBCC**))
1022 BBCC **pa, **pb, **pc, **pd, **pl, **pm, **pn, **pv;
1026 CLG_DEBUG(8, " qsort(%ld,%ld)\n", a-qsort_start + 0L, n + 0L);
1028 if (n < 7) { /* Insertion sort on smallest arrays */
1029 for (pm = a+1; pm < a+n; pm++)
1030 for (pl = pm; pl > a && cmp(pl-1, pl) > 0; pl --)
1034 for (pm = a; pm < a+n; pm++) {
1035 VG_(printf)(" %3ld BB %#lx, ",
1036 pm - qsort_start + 0L,
1037 bb_addr((*pm)->bb));
1038 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
1043 pm = a + n/2; /* Small arrays, middle element */
1047 if (n > 40) { /* Big arrays, pseudomedian of 9 */
1049 pl = med3(pl, pl+s, pl+2*s, cmp);
1050 pm = med3(pm-s, pm, pm+s, cmp);
1051 pn = med3(pn-2*s, pn-s, pn, cmp);
1053 pm = med3(pl, pm, pn, cmp); /* Mid-size, med of 3 */
1060 pc = pd = a + (n-1);
1062 while ((pb <= pc) && ((r=cmp(pb, pv)) <= 0)) {
1064 /* same as pivot, to start */
1069 while ((pb <= pc) && ((r=cmp(pc, pv)) >= 0)) {
1071 /* same as pivot, to end */
1076 if (pb > pc) { break; }
1084 /* put pivot from start into middle */
1085 if ((s = pa-a)>0) { for(r=0;r<s;r++) swap(a+r, pb+1-s+r); }
1086 /* put pivot from end into middle */
1087 if ((s = a+n-1-pd)>0) { for(r=0;r<s;r++) swap(pc+r, a+n-s+r); }
1090 VG_(printf)(" PV BB %#lx, ", bb_addr((*pv)->bb));
1091 CLG_(print_cxt)(9, (*pv)->cxt, (*pv)->rec_index);
1094 VG_(printf)(" Lower %ld - %ld:\n",
1096 a+s-1-qsort_start + 0L);
1099 VG_(printf)(" %3ld BB %#lx, ",
1100 pm-qsort_start + 0L,
1101 bb_addr((*pm)->bb));
1102 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
1106 VG_(printf)(" Upper %ld - %ld:\n",
1107 a+n-s-qsort_start + 0L,
1108 a+n-1-qsort_start + 0L);
1111 VG_(printf)(" %3ld BB %#lx, ",
1112 pm-qsort_start + 0L,
1113 bb_addr((*pm)->bb));
1114 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
1118 if ((s = pb+1-pa) > 1) qsort(a, s, cmp);
1119 if ((s = pd+1-pc) > 1) qsort(a+n-s, s, cmp);
1123 /* Helpers for prepare_dump */
1125 static Int prepare_count;
1126 static BBCC** prepare_ptr;
1129 static void hash_addCount(BBCC* bbcc)
1131 if ((bbcc->ecounter_sum > 0) || (bbcc->ret_counter>0))
1135 static void hash_addPtr(BBCC* bbcc)
1137 if ((bbcc->ecounter_sum == 0) &&
1138 (bbcc->ret_counter == 0)) return;
1140 *prepare_ptr = bbcc;
1145 static void cs_addCount(thread_info* ti)
1150 /* add BBCCs with active call in call stack of current thread.
1151 * update cost sums for active calls
1154 for(i = 0; i < CLG_(current_call_stack).sp; i++) {
1155 call_entry* e = &(CLG_(current_call_stack).entry[i]);
1156 if (e->jcc == 0) continue;
1158 CLG_(add_diff_cost_lz)( CLG_(sets).full, &(e->jcc->cost),
1159 e->enter_cost, CLG_(current_state).cost);
1160 bbcc = e->jcc->from;
1162 CLG_DEBUG(1, " [%2d] (tid %d), added active: %s\n",
1163 i,CLG_(current_tid),bbcc->cxt->fn[0]->name);
1165 if (bbcc->ecounter_sum>0 || bbcc->ret_counter>0) {
1166 /* already counted */
1173 static void cs_addPtr(thread_info* ti)
1178 /* add BBCCs with active call in call stack of current thread.
1179 * update cost sums for active calls
1182 for(i = 0; i < CLG_(current_call_stack).sp; i++) {
1183 call_entry* e = &(CLG_(current_call_stack).entry[i]);
1184 if (e->jcc == 0) continue;
1186 bbcc = e->jcc->from;
1188 if (bbcc->ecounter_sum>0 || bbcc->ret_counter>0) {
1189 /* already counted */
1193 *prepare_ptr = bbcc;
1200 * Put all BBCCs with costs into a sorted array.
1201 * The returned arrays ends with a null pointer.
1202 * Must be freed after dumping.
1205 BBCC** prepare_dump(void)
1211 /* if we do not separate among threads, this gives all */
1212 /* count number of BBCCs with >0 executions */
1213 CLG_(forall_bbccs)(hash_addCount);
1215 /* even if we do not separate among threads,
1216 * call stacks are separated */
1217 if (CLG_(clo).separate_threads)
1220 CLG_(forall_threads)(cs_addCount);
1222 CLG_DEBUG(0, "prepare_dump: %d BBCCs\n", prepare_count);
1224 /* allocate bbcc array, insert BBCCs and sort */
1225 prepare_ptr = array =
1226 (BBCC**) CLG_MALLOC("cl.dump.pd.1",
1227 (prepare_count+1) * sizeof(BBCC*));
1229 CLG_(forall_bbccs)(hash_addPtr);
1231 if (CLG_(clo).separate_threads)
1234 CLG_(forall_threads)(cs_addPtr);
1236 CLG_ASSERT(array + prepare_count == prepare_ptr);
1241 CLG_DEBUG(0," BBCCs inserted\n");
1243 qsort_start = array;
1244 qsort(array, prepare_count, my_cmp);
1246 CLG_DEBUG(0," BBCCs sorted\n");
1254 static void fprint_cost_ln(int fd, Char* prefix,
1255 EventMapping* em, ULong* cost)
1259 p = VG_(sprintf)(outbuf, "%s", prefix);
1260 p += CLG_(sprint_mappingcost)(outbuf + p, em, cost);
1261 VG_(sprintf)(outbuf + p, "\n");
1262 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
1265 static ULong bbs_done = 0;
1266 static Char* filename = 0;
1271 VG_(message)(Vg_UserMsg,
1272 "Error: can not open cache simulation output file `%s'\n",
1278 * Create a new dump file and write header.
1280 * Naming: <CLG_(clo).filename_base>.<pid>[.<part>][-<tid>]
1281 * <part> is skipped for final dump (trigger==0)
1282 * <tid> is skipped for thread 1 with CLG_(clo).separate_threads=no
1284 * Returns the file descriptor, and -1 on error (no write permission)
1286 static int new_dumpfile(Char buf[BUF_LEN], int tid, Char* trigger)
1288 Bool appending = False;
1293 CLG_ASSERT(dumps_initialized);
1294 CLG_ASSERT(filename != 0);
1296 if (!CLG_(clo).combine_dumps) {
1297 i = VG_(sprintf)(filename, "%s", out_file);
1300 i += VG_(sprintf)(filename+i, ".%d", out_counter);
1302 if (CLG_(clo).separate_threads)
1303 VG_(sprintf)(filename+i, "-%02d", tid);
1305 res = VG_(open)(filename, VKI_O_WRONLY|VKI_O_TRUNC, 0);
1308 VG_(sprintf)(filename, "%s", out_file);
1309 res = VG_(open)(filename, VKI_O_WRONLY|VKI_O_APPEND, 0);
1310 if (!sr_isError(res) && out_counter>1)
1314 if (sr_isError(res)) {
1315 res = VG_(open)(filename, VKI_O_CREAT|VKI_O_WRONLY,
1316 VKI_S_IRUSR|VKI_S_IWUSR);
1317 if (sr_isError(res)) {
1318 /* If the file can not be opened for whatever reason (conflict
1319 between multiple supervised processes?), give up now. */
1323 fd = (Int) sr_Res(res);
1325 CLG_DEBUG(2, " new_dumpfile '%s'\n", filename);
1333 VG_(sprintf)(buf, "version: 1\n");
1334 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1337 VG_(sprintf)(buf, "creator: callgrind-" VERSION "\n");
1338 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1341 VG_(sprintf)(buf, "pid: %d\n", VG_(getpid)());
1342 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1345 VG_(strcpy)(buf, "cmd: ");
1346 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1347 my_fwrite(fd, (void*)cmdbuf, VG_(strlen)(cmdbuf));
1350 VG_(sprintf)(buf, "\npart: %d\n", out_counter);
1351 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1352 if (CLG_(clo).separate_threads) {
1353 VG_(sprintf)(buf, "thread: %d\n", tid);
1354 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1359 my_fwrite(fd, "\n", 1);
1362 /* Global options changing the tracing behaviour */
1363 VG_(sprintf)(buf, "\ndesc: Option: --skip-plt=%s\n",
1364 CLG_(clo).skip_plt ? "yes" : "no");
1365 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1366 VG_(sprintf)(buf, "desc: Option: --collect-jumps=%s\n",
1367 CLG_(clo).collect_jumps ? "yes" : "no");
1368 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1369 VG_(sprintf)(buf, "desc: Option: --separate-recs=%d\n",
1370 CLG_(clo).separate_recursions);
1371 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1372 VG_(sprintf)(buf, "desc: Option: --separate-callers=%d\n",
1373 CLG_(clo).separate_callers);
1374 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1376 VG_(sprintf)(buf, "desc: Option: --dump-bbs=%s\n",
1377 CLG_(clo).dump_bbs ? "yes" : "no");
1378 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1379 VG_(sprintf)(buf, "desc: Option: --separate-threads=%s\n",
1380 CLG_(clo).separate_threads ? "yes" : "no");
1381 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1384 (*CLG_(cachesim).getdesc)(buf);
1385 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1388 VG_(sprintf)(buf, "\ndesc: Timerange: Basic block %llu - %llu\n",
1389 bbs_done, CLG_(stat).bb_executions);
1391 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1392 VG_(sprintf)(buf, "desc: Trigger: %s\n",
1393 trigger ? trigger : (Char*)"Program termination");
1394 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1397 /* Output function specific config
1399 for (i = 0; i < N_FNCONFIG_ENTRIES; i++) {
1403 VG_(sprintf)(buf, "desc: Option: --fn-skip=%s\n", fnc->name);
1404 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1406 if (fnc->dump_at_enter) {
1407 VG_(sprintf)(buf, "desc: Option: --fn-dump-at-enter=%s\n",
1409 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1411 if (fnc->dump_at_leave) {
1412 VG_(sprintf)(buf, "desc: Option: --fn-dump-at-leave=%s\n",
1414 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1416 if (fnc->separate_callers != CLG_(clo).separate_callers) {
1417 VG_(sprintf)(buf, "desc: Option: --separate-callers%d=%s\n",
1418 fnc->separate_callers, fnc->name);
1419 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1421 if (fnc->separate_recursions != CLG_(clo).separate_recursions) {
1422 VG_(sprintf)(buf, "desc: Option: --separate-recs%d=%s\n",
1423 fnc->separate_recursions, fnc->name);
1424 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1431 /* "positions:" line */
1432 VG_(sprintf)(buf, "\npositions:%s%s%s\n",
1433 CLG_(clo).dump_instr ? " instr" : "",
1434 CLG_(clo).dump_bb ? " bb" : "",
1435 CLG_(clo).dump_line ? " line" : "");
1436 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1438 /* "events:" line */
1439 i = VG_(sprintf)(buf, "events: ");
1440 CLG_(sprint_eventmapping)(buf+i, CLG_(dumpmap));
1441 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1442 my_fwrite(fd, "\n", 1);
1445 sum = CLG_(get_eventset_cost)( CLG_(sets).full );
1446 CLG_(zero_cost)(CLG_(sets).full, sum);
1447 if (CLG_(clo).separate_threads) {
1448 thread_info* ti = CLG_(get_current_thread)();
1449 CLG_(add_diff_cost)(CLG_(sets).full, sum, ti->lastdump_cost,
1450 ti->states.entry[0]->cost);
1453 /* This function is called once for thread 1, where
1454 * all costs are summed up when not dumping separate per thread.
1455 * But this is not true for summary: we need to add all threads.
1458 thread_info** thr = CLG_(get_threads)();
1459 for(t=1;t<VG_N_THREADS;t++) {
1460 if (!thr[t]) continue;
1461 CLG_(add_diff_cost)(CLG_(sets).full, sum,
1462 thr[t]->lastdump_cost,
1463 thr[t]->states.entry[0]->cost);
1466 fprint_cost_ln(fd, "summary: ", CLG_(dumpmap), sum);
1468 /* all dumped cost will be added to total_fcc */
1469 CLG_(init_cost_lz)( CLG_(sets).full, &dump_total_cost );
1471 my_fwrite(fd, "\n\n",2);
1473 if (VG_(clo_verbosity) > 1)
1474 VG_(message)(Vg_DebugMsg, "Dump to %s\n", filename);
1480 static void close_dumpfile(int fd)
1484 fprint_cost_ln(fd, "totals: ", CLG_(dumpmap),
1486 //fprint_fcc_ln(fd, "summary: ", &dump_total_fcc);
1487 CLG_(add_cost_lz)(CLG_(sets).full,
1488 &CLG_(total_cost), dump_total_cost);
1493 if (filename[0] == '.') {
1494 if (-1 == VG_(rename) (filename, filename+1)) {
1495 /* Can not rename to correct file name: give out warning */
1496 VG_(message)(Vg_DebugMsg, "Warning: Can not rename .%s to %s\n",
1497 filename, filename);
1503 /* Helper for print_bbccs */
1505 static Int print_fd;
1506 static Char* print_trigger;
1507 static Char print_buf[BUF_LEN];
1509 static void print_bbccs_of_thread(thread_info* ti)
1515 CLG_DEBUG(1, "+ print_bbccs(tid %d)\n", CLG_(current_tid));
1517 print_fd = new_dumpfile(print_buf, CLG_(current_tid), print_trigger);
1519 CLG_DEBUG(1, "- print_bbccs(tid %d): No output...\n", CLG_(current_tid));
1523 p = array = prepare_dump();
1524 init_fpos(&lastFnPos);
1525 init_apos(&lastAPos, 0, 0, 0);
1529 /* on context/function change, print old cost buffer before */
1530 if (lastFnPos.cxt && ((*p==0) ||
1531 (lastFnPos.cxt != (*p)->cxt) ||
1532 (lastFnPos.rec_index != (*p)->rec_index))) {
1533 if (!CLG_(is_zero_cost)( CLG_(sets).full, ccSum[currSum].cost )) {
1534 /* no need to switch buffers, as position is the same */
1535 fprint_apos(print_fd, &(ccSum[currSum].p), &lastAPos,
1536 lastFnPos.cxt->fn[0]->file);
1537 fprint_fcost(print_fd, &ccSum[currSum], &lastAPos);
1540 if (ccSum[currSum].p.file != lastFnPos.cxt->fn[0]->file) {
1541 /* switch back to file of function */
1542 VG_(sprintf)(print_buf, "fe=");
1543 print_file(print_buf+3, lastFnPos.cxt->fn[0]->file);
1544 my_fwrite(print_fd, (void*)print_buf, VG_(strlen)(print_buf));
1546 my_fwrite(print_fd, "\n", 1);
1551 if (print_fn_pos(print_fd, &lastFnPos, *p)) {
1554 init_apos(&lastAPos, 0, 0, (*p)->cxt->fn[0]->file);
1555 init_fcost(&ccSum[0], 0, 0, 0);
1556 init_fcost(&ccSum[1], 0, 0, 0);
1560 if (CLG_(clo).dump_bbs) {
1561 /* FIXME: Specify Object of BB if different to object of fn */
1563 ULong ecounter = (*p)->ecounter_sum;
1564 pos = VG_(sprintf)(print_buf, "bb=%#lx ", (*p)->bb->offset);
1565 for(i = 0; i<(*p)->bb->cjmp_count;i++) {
1566 pos += VG_(sprintf)(print_buf+pos, "%d %llu ",
1567 (*p)->bb->jmp[i].instr,
1569 ecounter -= (*p)->jmp[i].ecounter;
1571 VG_(sprintf)(print_buf+pos, "%d %llu\n",
1572 (*p)->bb->instr_count,
1574 my_fwrite(print_fd, (void*)print_buf, VG_(strlen)(print_buf));
1577 fprint_bbcc(print_fd, *p, &lastAPos);
1582 close_dumpfile(print_fd);
1583 if (array) VG_(free)(array);
1585 /* set counters of last dump */
1586 CLG_(copy_cost)( CLG_(sets).full, ti->lastdump_cost,
1587 CLG_(current_state).cost );
1589 CLG_DEBUG(1, "- print_bbccs(tid %d)\n", CLG_(current_tid));
1593 static void print_bbccs(Char* trigger, Bool only_current_thread)
1599 print_trigger = trigger;
1601 if (!CLG_(clo).separate_threads) {
1602 /* All BBCC/JCC costs is stored for thread 1 */
1603 Int orig_tid = CLG_(current_tid);
1605 CLG_(switch_thread)(1);
1606 print_bbccs_of_thread( CLG_(get_current_thread)() );
1607 CLG_(switch_thread)(orig_tid);
1609 else if (only_current_thread)
1610 print_bbccs_of_thread( CLG_(get_current_thread)() );
1612 CLG_(forall_threads)(print_bbccs_of_thread);
1618 void CLG_(dump_profile)(Char* trigger, Bool only_current_thread)
1620 CLG_DEBUG(2, "+ dump_profile(Trigger '%s')\n",
1621 trigger ? trigger : (Char*)"Prg.Term.");
1625 if (VG_(clo_verbosity) > 1)
1626 VG_(message)(Vg_DebugMsg, "Start dumping at BB %llu (%s)...\n",
1627 CLG_(stat).bb_executions,
1628 trigger ? trigger : (Char*)"Prg.Term.");
1632 print_bbccs(trigger, only_current_thread);
1634 bbs_done = CLG_(stat).bb_executions++;
1636 if (VG_(clo_verbosity) > 1)
1637 VG_(message)(Vg_DebugMsg, "Dumping done.\n");
1640 /* Copy command to cmd buffer. We want to original command line
1641 * (can change at runtime)
1644 void init_cmdbuf(void)
1649 if (VG_(args_the_exename)) {
1650 CLG_ASSERT( VG_(strlen)( VG_(args_the_exename) ) < BUF_LEN-1);
1651 size = VG_(sprintf)(cmdbuf, " %s", VG_(args_the_exename));
1654 for(i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) {
1655 argv = * (HChar**) VG_(indexXA)( VG_(args_for_client), i );
1656 if (!argv) continue;
1657 if ((size>0) && (size < BUF_LEN)) cmdbuf[size++] = ' ';
1658 for(j=0;argv[j]!=0;j++)
1659 if (size < BUF_LEN) cmdbuf[size++] = argv[j];
1662 if (size >= BUF_LEN) size = BUF_LEN-1;
1667 * Set up file names for dump output: <out_directory>, <out_file>.
1668 * <out_file> is derived from the output format string, which defaults
1669 * to "callgrind.out.%p", where %p is replaced with the PID.
1670 * For the final file name, on intermediate dumps a counter is appended,
1671 * and further, if separate dumps per thread are requested, the thread ID.
1673 * <out_file> always starts with a full absolute path.
1674 * If the output format string represents a relative path, the current
1675 * working directory at program start is used.
1677 * This function has to be called every time a profile dump is generated
1678 * to be able to react on PID changes.
1680 void CLG_(init_dumps)()
1685 static int thisPID = 0;
1686 int currentPID = VG_(getpid)();
1687 if (currentPID == thisPID) {
1688 /* already initialized, and no PID change */
1689 CLG_ASSERT(out_file != 0);
1692 thisPID = currentPID;
1694 if (!CLG_(clo).out_format)
1695 CLG_(clo).out_format = DEFAULT_OUTFORMAT;
1697 /* If a file name was already set, clean up before */
1699 VG_(free)(out_file);
1700 VG_(free)(out_directory);
1701 VG_(free)(filename);
1705 // Setup output filename.
1707 VG_(expand_file_name)("--callgrind-out-file", CLG_(clo).out_format);
1709 /* get base directory for dump/command/result files */
1710 CLG_ASSERT(out_file[0] == '/');
1713 while(out_file[i]) {
1714 if (out_file[i] == '/') lastSlash = i;
1718 out_directory = (Char*) CLG_MALLOC("cl.dump.init_dumps.1", i+1);
1719 VG_(strncpy)(out_directory, out_file, i);
1720 out_directory[i] = 0;
1722 /* allocate space big enough for final filenames */
1723 filename = (Char*) CLG_MALLOC("cl.dump.init_dumps.2",
1724 VG_(strlen)(out_file)+32);
1725 CLG_ASSERT(filename != 0);
1727 /* Make sure the output base file can be written.
1728 * This is used for the dump at program termination.
1729 * We stop with an error here if we can not create the
1730 * file: This is probably because of missing rights,
1731 * and trace parts wouldn't be allowed to be written, too.
1733 VG_(strcpy)(filename, out_file);
1734 res = VG_(open)(filename, VKI_O_WRONLY|VKI_O_TRUNC, 0);
1735 if (sr_isError(res)) {
1736 res = VG_(open)(filename, VKI_O_CREAT|VKI_O_WRONLY,
1737 VKI_S_IRUSR|VKI_S_IWUSR);
1738 if (sr_isError(res)) {
1742 if (!sr_isError(res)) VG_(close)( (Int)sr_Res(res) );
1744 if (!dumps_initialized)
1747 dumps_initialized = True;