1 /*--------------------------------------------------------------------*/
4 /*--------------------------------------------------------------------*/
7 This file is part of Callgrind, a Valgrind tool for call tracing.
9 Copyright (C) 2002-2013, 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 HChar
* out_file
= 0;
40 static Bool dumps_initialized
= False
;
43 static HChar cmdbuf
[BUF_LEN
];
45 /* Total reads/writes/misses sum over all dumps and threads.
46 * Updated during CC traversal at dump time.
48 FullCost
CLG_(total_cost
) = 0;
49 static FullCost dump_total_cost
= 0;
51 EventMapping
* CLG_(dumpmap
) = 0;
53 /* Temporary output buffer for
54 * print_fn_pos, fprint_apos, fprint_fcost, fprint_jcc,
55 * fprint_fcc_ln, dump_run_info, dump_state_info
57 static HChar outbuf
[FILENAME_LEN
+ FN_NAME_LEN
+ OBJ_NAME_LEN
+ COSTS_LEN
];
59 Int
CLG_(get_dump_counter
)(void)
64 /*------------------------------------------------------------*/
65 /*--- Output file related stuff ---*/
66 /*------------------------------------------------------------*/
68 /* Boolean dumping array */
69 static Bool
* dump_array
= 0;
70 static Int dump_array_size
= 0;
71 static Bool
* obj_dumped
= 0;
72 static Bool
* file_dumped
= 0;
73 static Bool
* fn_dumped
= 0;
74 static Bool
* cxt_dumped
= 0;
77 void reset_dump_array(void)
81 CLG_ASSERT(dump_array
!= 0);
83 for(i
=0;i
<dump_array_size
;i
++)
84 dump_array
[i
] = False
;
88 void init_dump_array(void)
90 dump_array_size
= CLG_(stat
).distinct_objs
+
91 CLG_(stat
).distinct_files
+
92 CLG_(stat
).distinct_fns
+
93 CLG_(stat
).context_counter
;
94 CLG_ASSERT(dump_array
== 0);
95 dump_array
= (Bool
*) CLG_MALLOC("cl.dump.ida.1",
96 dump_array_size
* sizeof(Bool
));
97 obj_dumped
= dump_array
;
98 file_dumped
= obj_dumped
+ CLG_(stat
).distinct_objs
;
99 fn_dumped
= file_dumped
+ CLG_(stat
).distinct_files
;
100 cxt_dumped
= fn_dumped
+ CLG_(stat
).distinct_fns
;
104 CLG_DEBUG(1, " init_dump_array: size %d\n", dump_array_size
);
108 void free_dump_array(void)
110 CLG_ASSERT(dump_array
!= 0);
111 VG_(free
)(dump_array
);
121 /* Initialize to an invalid position */
123 void init_fpos(FnPos
* p
)
135 static void my_fwrite(Int fd
, const HChar
* buf
, Int len
)
137 VG_(write
)(fd
, buf
, len
);
141 #define FWRITE_BUFSIZE 32000
142 #define FWRITE_THROUGH 10000
143 static HChar fwrite_buf
[FWRITE_BUFSIZE
];
144 static Int fwrite_pos
;
145 static Int fwrite_fd
= -1;
148 void fwrite_flush(void)
150 if ((fwrite_fd
>=0) && (fwrite_pos
>0))
151 VG_(write
)(fwrite_fd
, fwrite_buf
, fwrite_pos
);
155 static void my_fwrite(Int fd
, const HChar
* buf
, Int len
)
157 if (fwrite_fd
!= fd
) {
161 if (len
> FWRITE_THROUGH
) {
163 VG_(write
)(fd
, buf
, len
);
166 if (FWRITE_BUFSIZE
- fwrite_pos
<= len
) fwrite_flush();
167 VG_(strncpy
)(fwrite_buf
+ fwrite_pos
, buf
, len
);
173 static void print_obj(HChar
* buf
, obj_node
* obj
)
177 if (CLG_(clo
).compress_strings
) {
178 CLG_ASSERT(obj_dumped
!= 0);
179 if (obj_dumped
[obj
->number
])
180 /*n =*/ VG_(sprintf
)(buf
, "(%d)\n", obj
->number
);
182 /*n =*/ VG_(sprintf
)(buf
, "(%d) %s\n",
183 obj
->number
, obj
->name
);
187 /*n =*/ VG_(sprintf
)(buf
, "%s\n", obj
->name
);
190 /* add mapping parameters the first time a object is dumped
191 * format: mp=0xSTART SIZE 0xOFFSET */
192 if (!obj_dumped
[obj
->number
]) {
193 obj_dumped
[obj
->number
];
194 VG_(sprintf
)(buf
+n
, "mp=%p %p %p\n",
195 pos
->obj
->start
, pos
->obj
->size
, pos
->obj
->offset
);
198 obj_dumped
[obj
->number
] = True
;
202 static void print_file(HChar
* buf
, file_node
* file
)
204 if (CLG_(clo
).compress_strings
) {
205 CLG_ASSERT(file_dumped
!= 0);
206 if (file_dumped
[file
->number
])
207 VG_(sprintf
)(buf
, "(%d)\n", file
->number
);
209 VG_(sprintf
)(buf
, "(%d) %s\n",
210 file
->number
, file
->name
);
211 file_dumped
[file
->number
] = True
;
215 VG_(sprintf
)(buf
, "%s\n", file
->name
);
219 * tag can be "fn", "cfn", "jfn"
221 static void print_fn(Int fd
, HChar
* buf
, const HChar
* tag
, fn_node
* fn
)
224 p
= VG_(sprintf
)(buf
, "%s=",tag
);
225 if (CLG_(clo
).compress_strings
) {
226 CLG_ASSERT(fn_dumped
!= 0);
227 if (fn_dumped
[fn
->number
])
228 p
+= VG_(sprintf
)(buf
+p
, "(%d)\n", fn
->number
);
230 p
+= VG_(sprintf
)(buf
+p
, "(%d) %s\n",
231 fn
->number
, fn
->name
);
232 fn_dumped
[fn
->number
] = True
;
236 p
+= VG_(sprintf
)(buf
+p
, "%s\n", fn
->name
);
238 my_fwrite(fd
, buf
, p
);
241 static void print_mangled_fn(Int fd
, HChar
* buf
, const HChar
* tag
,
242 Context
* cxt
, int rec_index
)
246 if (CLG_(clo
).compress_strings
&& CLG_(clo
).compress_mangled
) {
251 CLG_ASSERT(cxt_dumped
!= 0);
252 if (cxt_dumped
[cxt
->base_number
+rec_index
]) {
253 p
= VG_(sprintf
)(buf
, "%s=(%d)\n",
254 tag
, cxt
->base_number
+ rec_index
);
255 my_fwrite(fd
, buf
, p
);
260 /* make sure that for all context parts compressed data is written */
261 for(i
=cxt
->size
;i
>0;i
--) {
262 CLG_ASSERT(cxt
->fn
[i
-1]->pure_cxt
!= 0);
263 n
= cxt
->fn
[i
-1]->pure_cxt
->base_number
;
264 if (cxt_dumped
[n
]) continue;
265 p
= VG_(sprintf
)(buf
, "%s=(%d) %s\n",
266 tag
, n
, cxt
->fn
[i
-1]->name
);
267 my_fwrite(fd
, buf
, p
);
269 cxt_dumped
[n
] = True
;
270 last
= cxt
->fn
[i
-1]->pure_cxt
;
272 /* If the last context was the context to print, we are finished */
273 if ((last
== cxt
) && (rec_index
== 0)) return;
275 p
= VG_(sprintf
)(buf
, "%s=(%d) (%d)", tag
,
276 cxt
->base_number
+ rec_index
,
277 cxt
->fn
[0]->pure_cxt
->base_number
);
279 p
+= VG_(sprintf
)(buf
+p
, "'%d", rec_index
+1);
280 for(i
=1;i
<cxt
->size
;i
++)
281 p
+= VG_(sprintf
)(buf
+p
, "'(%d)",
282 cxt
->fn
[i
]->pure_cxt
->base_number
);
283 p
+= VG_(sprintf
)(buf
+p
, "\n");
284 my_fwrite(fd
, buf
, p
);
286 cxt_dumped
[cxt
->base_number
+rec_index
] = True
;
291 p
= VG_(sprintf
)(buf
, "%s=", tag
);
292 if (CLG_(clo
).compress_strings
) {
293 CLG_ASSERT(cxt_dumped
!= 0);
294 if (cxt_dumped
[cxt
->base_number
+rec_index
]) {
295 p
+= VG_(sprintf
)(buf
+p
, "(%d)\n", cxt
->base_number
+ rec_index
);
296 my_fwrite(fd
, buf
, p
);
300 p
+= VG_(sprintf
)(buf
+p
, "(%d) ", cxt
->base_number
+ rec_index
);
301 cxt_dumped
[cxt
->base_number
+rec_index
] = True
;
305 p
+= VG_(sprintf
)(buf
+p
, "%s", cxt
->fn
[0]->name
);
307 p
+= VG_(sprintf
)(buf
+p
, "'%d", rec_index
+1);
308 for(i
=1;i
<cxt
->size
;i
++)
309 p
+= VG_(sprintf
)(buf
+p
, "'%s", cxt
->fn
[i
]->name
);
311 p
+= VG_(sprintf
)(buf
+p
, "\n");
312 my_fwrite(fd
, buf
, p
);
318 * Print function position of the BBCC, but only print info differing to
319 * the <last> position, update <last>
320 * Return True if something changes.
322 static Bool
print_fn_pos(int fd
, FnPos
* last
, BBCC
* bbcc
)
326 CLG_ASSERT(bbcc
&& bbcc
->cxt
);
329 CLG_DEBUG(2, "+ print_fn_pos: ");
330 CLG_(print_cxt
)(16, bbcc
->cxt
, bbcc
->rec_index
);
333 if (!CLG_(clo
).mangle_names
) {
334 if (last
->rec_index
!= bbcc
->rec_index
) {
335 VG_(sprintf
)(outbuf
, "rec=%d\n\n", bbcc
->rec_index
);
336 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
337 last
->rec_index
= bbcc
->rec_index
;
338 last
->cxt
= 0; /* reprint context */
342 if (last
->cxt
!= bbcc
->cxt
) {
343 fn_node
* last_from
= (last
->cxt
&& last
->cxt
->size
>1) ?
344 last
->cxt
->fn
[1] : 0;
345 fn_node
* curr_from
= (bbcc
->cxt
->size
>1) ?
346 bbcc
->cxt
->fn
[1] : 0;
347 if (curr_from
== 0) {
348 if (last_from
!= 0) {
349 /* switch back to no context */
350 VG_(sprintf
)(outbuf
, "frfn=(spontaneous)\n");
351 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
355 else if (last_from
!= curr_from
) {
356 print_fn(fd
,outbuf
,"frfn", curr_from
);
359 last
->cxt
= bbcc
->cxt
;
363 if (last
->obj
!= bbcc
->cxt
->fn
[0]->file
->obj
) {
364 VG_(sprintf
)(outbuf
, "ob=");
365 print_obj(outbuf
+3, bbcc
->cxt
->fn
[0]->file
->obj
);
366 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
367 last
->obj
= bbcc
->cxt
->fn
[0]->file
->obj
;
371 if (last
->file
!= bbcc
->cxt
->fn
[0]->file
) {
372 VG_(sprintf
)(outbuf
, "fl=");
373 print_file(outbuf
+3, bbcc
->cxt
->fn
[0]->file
);
374 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
375 last
->file
= bbcc
->cxt
->fn
[0]->file
;
379 if (!CLG_(clo
).mangle_names
) {
380 if (last
->fn
!= bbcc
->cxt
->fn
[0]) {
381 print_fn(fd
,outbuf
, "fn", bbcc
->cxt
->fn
[0]);
382 last
->fn
= bbcc
->cxt
->fn
[0];
387 /* Print mangled name if context or rec_index changes */
388 if ((last
->rec_index
!= bbcc
->rec_index
) ||
389 (last
->cxt
!= bbcc
->cxt
)) {
391 print_mangled_fn(fd
, outbuf
, "fn", bbcc
->cxt
, bbcc
->rec_index
);
392 last
->fn
= bbcc
->cxt
->fn
[0];
393 last
->rec_index
= bbcc
->rec_index
;
398 last
->cxt
= bbcc
->cxt
;
400 CLG_DEBUG(2, "- print_fn_pos: %s\n", res
? "changed" : "");
405 /* the debug lookup cache is useful if BBCC for same BB are
406 * dumped directly in a row. This is a direct mapped cache.
408 #define DEBUG_CACHE_SIZE 1777
410 static Addr debug_cache_addr
[DEBUG_CACHE_SIZE
];
411 static file_node
* debug_cache_file
[DEBUG_CACHE_SIZE
];
412 static int debug_cache_line
[DEBUG_CACHE_SIZE
];
413 static Bool debug_cache_info
[DEBUG_CACHE_SIZE
];
416 void init_debug_cache(void)
419 for(i
=0;i
<DEBUG_CACHE_SIZE
;i
++) {
420 debug_cache_addr
[i
] = 0;
421 debug_cache_file
[i
] = 0;
422 debug_cache_line
[i
] = 0;
423 debug_cache_info
[i
] = 0;
427 static /* __inline__ */
428 Bool
get_debug_pos(BBCC
* bbcc
, Addr addr
, AddrPos
* p
)
430 HChar file
[FILENAME_LEN
];
431 HChar dir
[FILENAME_LEN
];
432 Bool found_file_line
, found_dirname
;
434 int cachepos
= addr
% DEBUG_CACHE_SIZE
;
436 if (debug_cache_addr
[cachepos
] == addr
) {
437 p
->line
= debug_cache_line
[cachepos
];
438 p
->file
= debug_cache_file
[cachepos
];
439 found_file_line
= debug_cache_info
[cachepos
];
442 found_file_line
= VG_(get_filename_linenum
)(addr
,
447 if (!found_file_line
) {
448 VG_(strcpy
)(file
, "???");
453 CLG_ASSERT(VG_(strlen
)(dir
) + VG_(strlen
)(file
) + 1 < FILENAME_LEN
);
454 VG_(strcat
)(dir
, "/"); // Append '/'
455 VG_(strcat
)(dir
, file
); // Append file to dir
456 VG_(strcpy
)(file
, dir
); // Move dir+file to file
458 p
->file
= CLG_(get_file_node
)(bbcc
->bb
->obj
, file
);
460 debug_cache_info
[cachepos
] = found_file_line
;
461 debug_cache_addr
[cachepos
] = addr
;
462 debug_cache_line
[cachepos
] = p
->line
;
463 debug_cache_file
[cachepos
] = p
->file
;
466 /* Address offset from bbcc start address */
467 p
->addr
= addr
- bbcc
->bb
->obj
->offset
;
468 p
->bb_addr
= bbcc
->bb
->offset
;
470 CLG_DEBUG(3, " get_debug_pos(%#lx): BB %#lx, fn '%s', file '%s', line %u\n",
471 addr
, bb_addr(bbcc
->bb
), bbcc
->cxt
->fn
[0]->name
,
472 p
->file
->name
, p
->line
);
474 return found_file_line
;
478 /* copy file position and init cost */
479 static void init_apos(AddrPos
* p
, Addr addr
, Addr bbaddr
, file_node
* file
)
487 static void copy_apos(AddrPos
* dst
, AddrPos
* src
)
489 dst
->addr
= src
->addr
;
490 dst
->bb_addr
= src
->bb_addr
;
491 dst
->file
= src
->file
;
492 dst
->line
= src
->line
;
495 /* copy file position and init cost */
496 static void init_fcost(AddrCost
* c
, Addr addr
, Addr bbaddr
, file_node
* file
)
498 init_apos( &(c
->p
), addr
, bbaddr
, file
);
499 /* FIXME: This is a memory leak as a AddrCost is inited multiple times */
500 c
->cost
= CLG_(get_eventset_cost
)( CLG_(sets
).full
);
501 CLG_(init_cost
)( CLG_(sets
).full
, c
->cost
);
506 * print position change inside of a BB (last -> curr)
507 * this doesn't update last to curr!
509 static void fprint_apos(Int fd
, AddrPos
* curr
, AddrPos
* last
, file_node
* func_file
)
511 CLG_ASSERT(curr
->file
!= 0);
512 CLG_DEBUG(2, " print_apos(file '%s', line %d, bb %#lx, addr %#lx) fnFile '%s'\n",
513 curr
->file
->name
, curr
->line
, curr
->bb_addr
, curr
->addr
,
516 if (curr
->file
!= last
->file
) {
518 /* if we switch back to orig file, use fe=... */
519 if (curr
->file
== func_file
)
520 VG_(sprintf
)(outbuf
, "fe=");
522 VG_(sprintf
)(outbuf
, "fi=");
523 print_file(outbuf
+3, curr
->file
);
524 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
527 if (CLG_(clo
).dump_bbs
) {
528 if (curr
->line
!= last
->line
) {
529 VG_(sprintf
)(outbuf
, "ln=%d\n", curr
->line
);
530 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
539 * This prints out differences if allowed
541 * This doesn't set last to curr afterwards!
544 void fprint_pos(Int fd
, AddrPos
* curr
, AddrPos
* last
)
546 if (0) //CLG_(clo).dump_bbs)
547 VG_(sprintf
)(outbuf
, "%lu ", curr
->addr
- curr
->bb_addr
);
550 if (CLG_(clo
).dump_instr
) {
551 int diff
= curr
->addr
- last
->addr
;
552 if ( CLG_(clo
).compress_pos
&& (last
->addr
>0) &&
553 (diff
> -100) && (diff
< 100)) {
555 p
= VG_(sprintf
)(outbuf
, "+%d ", diff
);
557 p
= VG_(sprintf
)(outbuf
, "* ");
559 p
= VG_(sprintf
)(outbuf
, "%d ", diff
);
562 p
= VG_(sprintf
)(outbuf
, "%#lx ", curr
->addr
);
565 if (CLG_(clo
).dump_bb
) {
566 int diff
= curr
->bb_addr
- last
->bb_addr
;
567 if ( CLG_(clo
).compress_pos
&& (last
->bb_addr
>0) &&
568 (diff
> -100) && (diff
< 100)) {
570 p
+= VG_(sprintf
)(outbuf
+p
, "+%d ", diff
);
572 p
+= VG_(sprintf
)(outbuf
+p
, "* ");
574 p
+= VG_(sprintf
)(outbuf
+p
, "%d ", diff
);
577 p
+= VG_(sprintf
)(outbuf
+p
, "%#lx ", curr
->bb_addr
);
580 if (CLG_(clo
).dump_line
) {
581 int diff
= curr
->line
- last
->line
;
582 if ( CLG_(clo
).compress_pos
&& (last
->line
>0) &&
583 (diff
> -100) && (diff
< 100)) {
586 VG_(sprintf
)(outbuf
+p
, "+%d ", diff
);
588 VG_(sprintf
)(outbuf
+p
, "* ");
590 VG_(sprintf
)(outbuf
+p
, "%d ", diff
);
593 VG_(sprintf
)(outbuf
+p
, "%u ", curr
->line
);
596 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
605 void fprint_cost(int fd
, EventMapping
* es
, ULong
* cost
)
607 int p
= CLG_(sprint_mappingcost
)(outbuf
, es
, cost
);
608 VG_(sprintf
)(outbuf
+p
, "\n");
609 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
615 /* Write the cost of a source line; only that parts of the source
616 * position are written that changed relative to last written position.
617 * funcPos is the source position of the first line of actual function.
618 * Something is written only if cost != 0; returns True in this case.
620 static void fprint_fcost(Int fd
, AddrCost
* c
, AddrPos
* last
)
623 CLG_DEBUG(2, " print_fcost(file '%s', line %d, bb %#lx, addr %#lx):\n",
624 c
->p
.file
->name
, c
->p
.line
, c
->p
.bb_addr
, c
->p
.addr
);
625 CLG_(print_cost
)(-5, CLG_(sets
).full
, c
->cost
);
628 fprint_pos(fd
, &(c
->p
), last
);
629 copy_apos( last
, &(c
->p
) ); /* update last to current position */
631 fprint_cost(fd
, CLG_(dumpmap
), c
->cost
);
633 /* add cost to total */
634 CLG_(add_and_zero_cost
)( CLG_(sets
).full
, dump_total_cost
, c
->cost
);
638 /* Write out the calls from jcc (at pos)
640 static void fprint_jcc(Int fd
, jCC
* jcc
, AddrPos
* curr
, AddrPos
* last
, ULong ecounter
)
642 static AddrPos target
;
647 CLG_DEBUG(2, " fprint_jcc (jkind %d)\n", jcc
->jmpkind
);
648 CLG_(print_jcc
)(-10, jcc
);
651 CLG_ASSERT(jcc
->to
!=0);
652 CLG_ASSERT(jcc
->from
!=0);
654 if (!get_debug_pos(jcc
->to
, bb_addr(jcc
->to
->bb
), &target
)) {
655 /* if we don't have debug info, don't switch to file "???" */
656 target
.file
= last
->file
;
659 if ((jcc
->jmpkind
== jk_CondJump
) || (jcc
->jmpkind
== jk_Jump
)) {
661 /* this is a JCC for a followed conditional or boring jump. */
662 CLG_ASSERT(CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
));
664 /* objects among jumps should be the same.
665 * Otherwise this jump would have been changed to a call
668 CLG_ASSERT(jcc
->from
->bb
->obj
== jcc
->to
->bb
->obj
);
670 /* only print if target position info is usefull */
671 if (!CLG_(clo
).dump_instr
&& !CLG_(clo
).dump_bb
&& target
.line
==0) {
672 jcc
->call_counter
= 0;
676 /* Different files/functions are possible e.g. with longjmp's
677 * which change the stack, and thus context
679 if (last
->file
!= target
.file
) {
680 VG_(sprintf
)(outbuf
, "jfi=");
681 print_file(outbuf
+4, target
.file
);
682 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
685 if (jcc
->from
->cxt
!= jcc
->to
->cxt
) {
686 if (CLG_(clo
).mangle_names
)
687 print_mangled_fn(fd
, outbuf
, "jfn",
688 jcc
->to
->cxt
, jcc
->to
->rec_index
);
690 print_fn(fd
, outbuf
, "jfn", jcc
->to
->cxt
->fn
[0]);
693 if (jcc
->jmpkind
== jk_CondJump
) {
694 /* format: jcnd=<followed>/<executions> <target> */
695 VG_(sprintf
)(outbuf
, "jcnd=%llu/%llu ",
696 jcc
->call_counter
, ecounter
);
699 /* format: jump=<jump count> <target> */
700 VG_(sprintf
)(outbuf
, "jump=%llu ",
703 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
705 fprint_pos(fd
, &target
, last
);
706 my_fwrite(fd
, "\n", 1);
707 fprint_pos(fd
, curr
, last
);
708 my_fwrite(fd
, "\n", 1);
710 jcc
->call_counter
= 0;
714 file
= jcc
->to
->cxt
->fn
[0]->file
;
715 obj
= jcc
->to
->bb
->obj
;
717 /* object of called position different to object of this function?*/
718 if (jcc
->from
->cxt
->fn
[0]->file
->obj
!= obj
) {
719 VG_(sprintf
)(outbuf
, "cob=");
720 print_obj(outbuf
+4, obj
);
721 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
724 /* file of called position different to current file? */
725 if (last
->file
!= file
) {
726 VG_(sprintf
)(outbuf
, "cfi=");
727 print_file(outbuf
+4, file
);
728 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
731 if (CLG_(clo
).mangle_names
)
732 print_mangled_fn(fd
, outbuf
, "cfn", jcc
->to
->cxt
, jcc
->to
->rec_index
);
734 print_fn(fd
, outbuf
, "cfn", jcc
->to
->cxt
->fn
[0]);
736 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)) {
737 VG_(sprintf
)(outbuf
, "calls=%llu ",
739 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
741 fprint_pos(fd
, &target
, last
);
742 my_fwrite(fd
, "\n", 1);
743 fprint_pos(fd
, curr
, last
);
744 fprint_cost(fd
, CLG_(dumpmap
), jcc
->cost
);
746 CLG_(init_cost
)( CLG_(sets
).full
, jcc
->cost
);
748 jcc
->call_counter
= 0;
754 /* Cost summation of functions.We use alternately ccSum[0/1], thus
755 * ssSum[currSum] for recently read lines with same line number.
757 static AddrCost ccSum
[2];
761 * Print all costs of a BBCC:
762 * - FCCs of instructions
763 * - JCCs of the unique jump of this BB
764 * returns True if something was written
766 static Bool
fprint_bbcc(Int fd
, BBCC
* bbcc
, AddrPos
* last
)
768 InstrInfo
* instr_info
;
770 Bool something_written
= False
;
772 AddrCost
*currCost
, *newCost
;
773 Int jcc_count
= 0, instr
, i
, jmp
;
776 CLG_ASSERT(bbcc
->cxt
!= 0);
778 VG_(printf
)("+ fprint_bbcc (Instr %d): ", bb
->instr_count
);
779 CLG_(print_bbcc
)(15, bbcc
);
782 CLG_ASSERT(currSum
== 0 || currSum
== 1);
783 currCost
= &(ccSum
[currSum
]);
784 newCost
= &(ccSum
[1-currSum
]);
786 ecounter
= bbcc
->ecounter_sum
;
788 instr_info
= &(bb
->instr
[0]);
789 for(instr
=0; instr
<bb
->instr_count
; instr
++, instr_info
++) {
791 /* get debug info of current instruction address and dump cost
792 * if CLG_(clo).dump_bbs or file/line has changed
794 if (!get_debug_pos(bbcc
, bb_addr(bb
) + instr_info
->instr_offset
,
796 /* if we don't have debug info, don't switch to file "???" */
797 newCost
->p
.file
= bbcc
->cxt
->fn
[0]->file
;
800 if (CLG_(clo
).dump_bbs
|| CLG_(clo
).dump_instr
||
801 (newCost
->p
.line
!= currCost
->p
.line
) ||
802 (newCost
->p
.file
!= currCost
->p
.file
)) {
804 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
805 something_written
= True
;
807 fprint_apos(fd
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
808 fprint_fcost(fd
, currCost
, last
);
812 currSum
= 1 - currSum
;
813 currCost
= &(ccSum
[currSum
]);
814 newCost
= &(ccSum
[1-currSum
]);
817 /* add line cost to current cost sum */
818 (*CLG_(cachesim
).add_icost
)(currCost
->cost
, bbcc
, instr_info
, ecounter
);
820 /* print jcc's if there are: only jumps */
821 if (bb
->jmp
[jmp
].instr
== instr
) {
823 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
)
824 if (((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
825 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
829 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
830 /* no need to switch buffers, as position is the same */
831 fprint_apos(fd
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
832 fprint_fcost(fd
, currCost
, last
);
834 get_debug_pos(bbcc
, bb_addr(bb
)+instr_info
->instr_offset
, &(currCost
->p
));
835 fprint_apos(fd
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
836 something_written
= True
;
837 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
838 if (((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
839 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
840 fprint_jcc(fd
, jcc
, &(currCost
->p
), last
, ecounter
);
845 /* update execution counter */
846 if (jmp
< bb
->cjmp_count
)
847 if (bb
->jmp
[jmp
].instr
== instr
) {
848 ecounter
-= bbcc
->jmp
[jmp
].ecounter
;
853 /* jCCs at end? If yes, dump cumulated line info first */
855 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
856 /* yes, if JCC only counts jmp arcs or cost >0 */
857 if ( ((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
858 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
862 if ( (bbcc
->skipped
&&
863 !CLG_(is_zero_cost
)(CLG_(sets
).full
, bbcc
->skipped
)) ||
866 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
867 /* no need to switch buffers, as position is the same */
868 fprint_apos(fd
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
869 fprint_fcost(fd
, currCost
, last
);
872 get_debug_pos(bbcc
, bb_jmpaddr(bb
), &(currCost
->p
));
873 fprint_apos(fd
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
874 something_written
= True
;
876 /* first, print skipped costs for calls */
877 if (bbcc
->skipped
&& !CLG_(is_zero_cost
)( CLG_(sets
).full
,
879 CLG_(add_and_zero_cost
)( CLG_(sets
).full
,
880 currCost
->cost
, bbcc
->skipped
);
882 VG_(sprintf
)(outbuf
, "# Skipped\n");
883 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
885 fprint_fcost(fd
, currCost
, last
);
889 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
890 CLG_ASSERT(jcc
->jmp
== jmp
);
891 if ( ((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
892 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
894 fprint_jcc(fd
, jcc
, &(currCost
->p
), last
, ecounter
);
898 if (CLG_(clo
).dump_bbs
|| CLG_(clo
).dump_bb
) {
899 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
900 something_written
= True
;
902 fprint_apos(fd
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
903 fprint_fcost(fd
, currCost
, last
);
905 if (CLG_(clo
).dump_bbs
) my_fwrite(fd
, "\n", 1);
907 /* when every cost was immediatly written, we must have done so,
908 * as this function is only called when there's cost in a BBCC
910 CLG_ASSERT(something_written
);
913 bbcc
->ecounter_sum
= 0;
914 for(i
=0; i
<=bbcc
->bb
->cjmp_count
; i
++)
915 bbcc
->jmp
[i
].ecounter
= 0;
916 bbcc
->ret_counter
= 0;
918 CLG_DEBUG(1, "- fprint_bbcc: JCCs %d\n", jcc_count
);
920 return something_written
;
925 * from->bb->obj, from->bb->fn
926 * obj, fn[0]->file, fn
929 static int my_cmp(BBCC
** pbbcc1
, BBCC
** pbbcc2
)
932 return (*pbbcc1
)->bb
->offset
- (*pbbcc2
)->bb
->offset
;
934 BBCC
*bbcc1
= *pbbcc1
;
935 BBCC
*bbcc2
= *pbbcc2
;
936 Context
* cxt1
= bbcc1
->cxt
;
937 Context
* cxt2
= bbcc2
->cxt
;
940 if (cxt1
->fn
[0]->file
->obj
!= cxt2
->fn
[0]->file
->obj
)
941 return cxt1
->fn
[0]->file
->obj
- cxt2
->fn
[0]->file
->obj
;
943 if (cxt1
->fn
[0]->file
!= cxt2
->fn
[0]->file
)
944 return cxt1
->fn
[0]->file
- cxt2
->fn
[0]->file
;
946 if (cxt1
->fn
[0] != cxt2
->fn
[0])
947 return cxt1
->fn
[0] - cxt2
->fn
[0];
949 if (bbcc1
->rec_index
!= bbcc2
->rec_index
)
950 return bbcc1
->rec_index
- bbcc2
->rec_index
;
952 while((off
< cxt1
->size
) && (off
< cxt2
->size
)) {
953 fn_node
* ffn1
= cxt1
->fn
[off
];
954 fn_node
* ffn2
= cxt2
->fn
[off
];
955 if (ffn1
->file
->obj
!= ffn2
->file
->obj
)
956 return ffn1
->file
->obj
- ffn2
->file
->obj
;
961 if (cxt1
->size
> cxt2
->size
) return 1;
962 else if (cxt1
->size
< cxt2
->size
) return -1;
964 return bbcc1
->bb
->offset
- bbcc2
->bb
->offset
;
972 /* modified version of:
974 * qsort -- qsort interface implemented by faster quicksort.
975 * J. L. Bentley and M. D. McIlroy, SPE 23 (1993) 1249-1265.
976 * Copyright 1993, John Wiley.
980 void swap(BBCC
** a
, BBCC
** b
)
983 t
= *a
; *a
= *b
; *b
= t
;
986 #define min(x, y) ((x)<=(y) ? (x) : (y))
989 BBCC
** med3(BBCC
**a
, BBCC
**b
, BBCC
**c
, int (*cmp
)(BBCC
**,BBCC
**))
990 { return cmp(a
, b
) < 0 ?
991 (cmp(b
, c
) < 0 ? b
: cmp(a
, c
) < 0 ? c
: a
)
992 : (cmp(b
, c
) > 0 ? b
: cmp(a
, c
) > 0 ? c
: a
);
995 static BBCC
** qsort_start
= 0;
997 static void qsort(BBCC
**a
, int n
, int (*cmp
)(BBCC
**,BBCC
**))
999 BBCC
**pa
, **pb
, **pc
, **pd
, **pl
, **pm
, **pn
, **pv
;
1003 CLG_DEBUG(8, " qsort(%ld,%ld)\n", a
-qsort_start
+ 0L, n
+ 0L);
1005 if (n
< 7) { /* Insertion sort on smallest arrays */
1006 for (pm
= a
+1; pm
< a
+n
; pm
++)
1007 for (pl
= pm
; pl
> a
&& cmp(pl
-1, pl
) > 0; pl
--)
1011 for (pm
= a
; pm
< a
+n
; pm
++) {
1012 VG_(printf
)(" %3ld BB %#lx, ",
1013 pm
- qsort_start
+ 0L,
1014 bb_addr((*pm
)->bb
));
1015 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
1020 pm
= a
+ n
/2; /* Small arrays, middle element */
1024 if (n
> 40) { /* Big arrays, pseudomedian of 9 */
1026 pl
= med3(pl
, pl
+s
, pl
+2*s
, cmp
);
1027 pm
= med3(pm
-s
, pm
, pm
+s
, cmp
);
1028 pn
= med3(pn
-2*s
, pn
-s
, pn
, cmp
);
1030 pm
= med3(pl
, pm
, pn
, cmp
); /* Mid-size, med of 3 */
1037 pc
= pd
= a
+ (n
-1);
1039 while ((pb
<= pc
) && ((r
=cmp(pb
, pv
)) <= 0)) {
1041 /* same as pivot, to start */
1046 while ((pb
<= pc
) && ((r
=cmp(pc
, pv
)) >= 0)) {
1048 /* same as pivot, to end */
1053 if (pb
> pc
) { break; }
1061 /* put pivot from start into middle */
1062 if ((s
= pa
-a
)>0) { for(r
=0;r
<s
;r
++) swap(a
+r
, pb
+1-s
+r
); }
1063 /* put pivot from end into middle */
1064 if ((s
= a
+n
-1-pd
)>0) { for(r
=0;r
<s
;r
++) swap(pc
+r
, a
+n
-s
+r
); }
1067 VG_(printf
)(" PV BB %#lx, ", bb_addr((*pv
)->bb
));
1068 CLG_(print_cxt
)(9, (*pv
)->cxt
, (*pv
)->rec_index
);
1071 VG_(printf
)(" Lower %ld - %ld:\n",
1073 a
+s
-1-qsort_start
+ 0L);
1076 VG_(printf
)(" %3ld BB %#lx, ",
1077 pm
-qsort_start
+ 0L,
1078 bb_addr((*pm
)->bb
));
1079 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
1083 VG_(printf
)(" Upper %ld - %ld:\n",
1084 a
+n
-s
-qsort_start
+ 0L,
1085 a
+n
-1-qsort_start
+ 0L);
1088 VG_(printf
)(" %3ld BB %#lx, ",
1089 pm
-qsort_start
+ 0L,
1090 bb_addr((*pm
)->bb
));
1091 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
1095 if ((s
= pb
+1-pa
) > 1) qsort(a
, s
, cmp
);
1096 if ((s
= pd
+1-pc
) > 1) qsort(a
+n
-s
, s
, cmp
);
1100 /* Helpers for prepare_dump */
1102 static Int prepare_count
;
1103 static BBCC
** prepare_ptr
;
1106 static void hash_addCount(BBCC
* bbcc
)
1108 if ((bbcc
->ecounter_sum
> 0) || (bbcc
->ret_counter
>0))
1112 static void hash_addPtr(BBCC
* bbcc
)
1114 if ((bbcc
->ecounter_sum
== 0) &&
1115 (bbcc
->ret_counter
== 0)) return;
1117 *prepare_ptr
= bbcc
;
1122 static void cs_addCount(thread_info
* ti
)
1127 /* add BBCCs with active call in call stack of current thread.
1128 * update cost sums for active calls
1131 for(i
= 0; i
< CLG_(current_call_stack
).sp
; i
++) {
1132 call_entry
* e
= &(CLG_(current_call_stack
).entry
[i
]);
1133 if (e
->jcc
== 0) continue;
1135 CLG_(add_diff_cost_lz
)( CLG_(sets
).full
, &(e
->jcc
->cost
),
1136 e
->enter_cost
, CLG_(current_state
).cost
);
1137 bbcc
= e
->jcc
->from
;
1139 CLG_DEBUG(1, " [%2d] (tid %d), added active: %s\n",
1140 i
,CLG_(current_tid
),bbcc
->cxt
->fn
[0]->name
);
1142 if (bbcc
->ecounter_sum
>0 || bbcc
->ret_counter
>0) {
1143 /* already counted */
1150 static void cs_addPtr(thread_info
* ti
)
1155 /* add BBCCs with active call in call stack of current thread.
1156 * update cost sums for active calls
1159 for(i
= 0; i
< CLG_(current_call_stack
).sp
; i
++) {
1160 call_entry
* e
= &(CLG_(current_call_stack
).entry
[i
]);
1161 if (e
->jcc
== 0) continue;
1163 bbcc
= e
->jcc
->from
;
1165 if (bbcc
->ecounter_sum
>0 || bbcc
->ret_counter
>0) {
1166 /* already counted */
1170 *prepare_ptr
= bbcc
;
1177 * Put all BBCCs with costs into a sorted array.
1178 * The returned arrays ends with a null pointer.
1179 * Must be freed after dumping.
1182 BBCC
** prepare_dump(void)
1188 /* if we do not separate among threads, this gives all */
1189 /* count number of BBCCs with >0 executions */
1190 CLG_(forall_bbccs
)(hash_addCount
);
1192 /* even if we do not separate among threads,
1193 * call stacks are separated */
1194 if (CLG_(clo
).separate_threads
)
1197 CLG_(forall_threads
)(cs_addCount
);
1199 CLG_DEBUG(0, "prepare_dump: %d BBCCs\n", prepare_count
);
1201 /* allocate bbcc array, insert BBCCs and sort */
1202 prepare_ptr
= array
=
1203 (BBCC
**) CLG_MALLOC("cl.dump.pd.1",
1204 (prepare_count
+1) * sizeof(BBCC
*));
1206 CLG_(forall_bbccs
)(hash_addPtr
);
1208 if (CLG_(clo
).separate_threads
)
1211 CLG_(forall_threads
)(cs_addPtr
);
1213 CLG_ASSERT(array
+ prepare_count
== prepare_ptr
);
1218 CLG_DEBUG(0," BBCCs inserted\n");
1220 qsort_start
= array
;
1221 qsort(array
, prepare_count
, my_cmp
);
1223 CLG_DEBUG(0," BBCCs sorted\n");
1231 static void fprint_cost_ln(int fd
, const HChar
* prefix
,
1232 EventMapping
* em
, ULong
* cost
)
1236 p
= VG_(sprintf
)(outbuf
, "%s", prefix
);
1237 p
+= CLG_(sprint_mappingcost
)(outbuf
+ p
, em
, cost
);
1238 VG_(sprintf
)(outbuf
+ p
, "\n");
1239 my_fwrite(fd
, outbuf
, VG_(strlen
)(outbuf
));
1242 static ULong bbs_done
= 0;
1243 static HChar
* filename
= 0;
1248 VG_(message
)(Vg_UserMsg
,
1249 "Error: can not open cache simulation output file `%s'\n",
1255 * Create a new dump file and write header.
1257 * Naming: <CLG_(clo).filename_base>.<pid>[.<part>][-<tid>]
1258 * <part> is skipped for final dump (trigger==0)
1259 * <tid> is skipped for thread 1 with CLG_(clo).separate_threads=no
1261 * Returns the file descriptor, and -1 on error (no write permission)
1263 static int new_dumpfile(HChar buf
[BUF_LEN
], int tid
, const HChar
* trigger
)
1265 Bool appending
= False
;
1270 CLG_ASSERT(dumps_initialized
);
1271 CLG_ASSERT(filename
!= 0);
1273 if (!CLG_(clo
).combine_dumps
) {
1274 i
= VG_(sprintf
)(filename
, "%s", out_file
);
1277 i
+= VG_(sprintf
)(filename
+i
, ".%d", out_counter
);
1279 if (CLG_(clo
).separate_threads
)
1280 VG_(sprintf
)(filename
+i
, "-%02d", tid
);
1282 res
= VG_(open
)(filename
, VKI_O_WRONLY
|VKI_O_TRUNC
, 0);
1285 VG_(sprintf
)(filename
, "%s", out_file
);
1286 res
= VG_(open
)(filename
, VKI_O_WRONLY
|VKI_O_APPEND
, 0);
1287 if (!sr_isError(res
) && out_counter
>1)
1291 if (sr_isError(res
)) {
1292 res
= VG_(open
)(filename
, VKI_O_CREAT
|VKI_O_WRONLY
,
1293 VKI_S_IRUSR
|VKI_S_IWUSR
);
1294 if (sr_isError(res
)) {
1295 /* If the file can not be opened for whatever reason (conflict
1296 between multiple supervised processes?), give up now. */
1300 fd
= (Int
) sr_Res(res
);
1302 CLG_DEBUG(2, " new_dumpfile '%s'\n", filename
);
1310 VG_(sprintf
)(buf
, "version: 1\n");
1311 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1314 VG_(sprintf
)(buf
, "creator: callgrind-" VERSION
"\n");
1315 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1318 VG_(sprintf
)(buf
, "pid: %d\n", VG_(getpid
)());
1319 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1322 VG_(strcpy
)(buf
, "cmd: ");
1323 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1324 my_fwrite(fd
, cmdbuf
, VG_(strlen
)(cmdbuf
));
1327 VG_(sprintf
)(buf
, "\npart: %d\n", out_counter
);
1328 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1329 if (CLG_(clo
).separate_threads
) {
1330 VG_(sprintf
)(buf
, "thread: %d\n", tid
);
1331 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1336 my_fwrite(fd
, "\n", 1);
1339 /* Global options changing the tracing behaviour */
1340 VG_(sprintf
)(buf
, "\ndesc: Option: --skip-plt=%s\n",
1341 CLG_(clo
).skip_plt
? "yes" : "no");
1342 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1343 VG_(sprintf
)(buf
, "desc: Option: --collect-jumps=%s\n",
1344 CLG_(clo
).collect_jumps
? "yes" : "no");
1345 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1346 VG_(sprintf
)(buf
, "desc: Option: --separate-recs=%d\n",
1347 CLG_(clo
).separate_recursions
);
1348 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1349 VG_(sprintf
)(buf
, "desc: Option: --separate-callers=%d\n",
1350 CLG_(clo
).separate_callers
);
1351 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1353 VG_(sprintf
)(buf
, "desc: Option: --dump-bbs=%s\n",
1354 CLG_(clo
).dump_bbs
? "yes" : "no");
1355 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1356 VG_(sprintf
)(buf
, "desc: Option: --separate-threads=%s\n",
1357 CLG_(clo
).separate_threads
? "yes" : "no");
1358 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1361 (*CLG_(cachesim
).getdesc
)(buf
);
1362 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1365 VG_(sprintf
)(buf
, "\ndesc: Timerange: Basic block %llu - %llu\n",
1366 bbs_done
, CLG_(stat
).bb_executions
);
1368 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1369 VG_(sprintf
)(buf
, "desc: Trigger: %s\n",
1370 trigger
? trigger
: "Program termination");
1371 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1374 /* Output function specific config
1376 for (i
= 0; i
< N_FNCONFIG_ENTRIES
; i
++) {
1380 VG_(sprintf
)(buf
, "desc: Option: --fn-skip=%s\n", fnc
->name
);
1381 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1383 if (fnc
->dump_at_enter
) {
1384 VG_(sprintf
)(buf
, "desc: Option: --fn-dump-at-enter=%s\n",
1386 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1388 if (fnc
->dump_at_leave
) {
1389 VG_(sprintf
)(buf
, "desc: Option: --fn-dump-at-leave=%s\n",
1391 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1393 if (fnc
->separate_callers
!= CLG_(clo
).separate_callers
) {
1394 VG_(sprintf
)(buf
, "desc: Option: --separate-callers%d=%s\n",
1395 fnc
->separate_callers
, fnc
->name
);
1396 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1398 if (fnc
->separate_recursions
!= CLG_(clo
).separate_recursions
) {
1399 VG_(sprintf
)(buf
, "desc: Option: --separate-recs%d=%s\n",
1400 fnc
->separate_recursions
, fnc
->name
);
1401 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1408 /* "positions:" line */
1409 VG_(sprintf
)(buf
, "\npositions:%s%s%s\n",
1410 CLG_(clo
).dump_instr
? " instr" : "",
1411 CLG_(clo
).dump_bb
? " bb" : "",
1412 CLG_(clo
).dump_line
? " line" : "");
1413 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1415 /* "events:" line */
1416 i
= VG_(sprintf
)(buf
, "events: ");
1417 CLG_(sprint_eventmapping
)(buf
+i
, CLG_(dumpmap
));
1418 my_fwrite(fd
, buf
, VG_(strlen
)(buf
));
1419 my_fwrite(fd
, "\n", 1);
1422 sum
= CLG_(get_eventset_cost
)( CLG_(sets
).full
);
1423 CLG_(zero_cost
)(CLG_(sets
).full
, sum
);
1424 if (CLG_(clo
).separate_threads
) {
1425 thread_info
* ti
= CLG_(get_current_thread
)();
1426 CLG_(add_diff_cost
)(CLG_(sets
).full
, sum
, ti
->lastdump_cost
,
1427 ti
->states
.entry
[0]->cost
);
1430 /* This function is called once for thread 1, where
1431 * all costs are summed up when not dumping separate per thread.
1432 * But this is not true for summary: we need to add all threads.
1435 thread_info
** thr
= CLG_(get_threads
)();
1436 for(t
=1;t
<VG_N_THREADS
;t
++) {
1437 if (!thr
[t
]) continue;
1438 CLG_(add_diff_cost
)(CLG_(sets
).full
, sum
,
1439 thr
[t
]->lastdump_cost
,
1440 thr
[t
]->states
.entry
[0]->cost
);
1443 fprint_cost_ln(fd
, "summary: ", CLG_(dumpmap
), sum
);
1445 /* all dumped cost will be added to total_fcc */
1446 CLG_(init_cost_lz
)( CLG_(sets
).full
, &dump_total_cost
);
1448 my_fwrite(fd
, "\n\n",2);
1450 if (VG_(clo_verbosity
) > 1)
1451 VG_(message
)(Vg_DebugMsg
, "Dump to %s\n", filename
);
1457 static void close_dumpfile(int fd
)
1461 fprint_cost_ln(fd
, "totals: ", CLG_(dumpmap
),
1463 //fprint_fcc_ln(fd, "summary: ", &dump_total_fcc);
1464 CLG_(add_cost_lz
)(CLG_(sets
).full
,
1465 &CLG_(total_cost
), dump_total_cost
);
1470 if (filename
[0] == '.') {
1471 if (-1 == VG_(rename
) (filename
, filename
+1)) {
1472 /* Can not rename to correct file name: give out warning */
1473 VG_(message
)(Vg_DebugMsg
, "Warning: Can not rename .%s to %s\n",
1474 filename
, filename
);
1480 /* Helper for print_bbccs */
1482 static Int print_fd
;
1483 static const HChar
* print_trigger
;
1484 static HChar print_buf
[BUF_LEN
];
1486 static void print_bbccs_of_thread(thread_info
* ti
)
1492 CLG_DEBUG(1, "+ print_bbccs(tid %d)\n", CLG_(current_tid
));
1494 print_fd
= new_dumpfile(print_buf
, CLG_(current_tid
), print_trigger
);
1496 CLG_DEBUG(1, "- print_bbccs(tid %d): No output...\n", CLG_(current_tid
));
1500 p
= array
= prepare_dump();
1501 init_fpos(&lastFnPos
);
1502 init_apos(&lastAPos
, 0, 0, 0);
1506 /* on context/function change, print old cost buffer before */
1507 if (lastFnPos
.cxt
&& ((*p
==0) ||
1508 (lastFnPos
.cxt
!= (*p
)->cxt
) ||
1509 (lastFnPos
.rec_index
!= (*p
)->rec_index
))) {
1510 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, ccSum
[currSum
].cost
)) {
1511 /* no need to switch buffers, as position is the same */
1512 fprint_apos(print_fd
, &(ccSum
[currSum
].p
), &lastAPos
,
1513 lastFnPos
.cxt
->fn
[0]->file
);
1514 fprint_fcost(print_fd
, &ccSum
[currSum
], &lastAPos
);
1517 if (ccSum
[currSum
].p
.file
!= lastFnPos
.cxt
->fn
[0]->file
) {
1518 /* switch back to file of function */
1519 VG_(sprintf
)(print_buf
, "fe=");
1520 print_file(print_buf
+3, lastFnPos
.cxt
->fn
[0]->file
);
1521 my_fwrite(print_fd
, print_buf
, VG_(strlen
)(print_buf
));
1523 my_fwrite(print_fd
, "\n", 1);
1528 if (print_fn_pos(print_fd
, &lastFnPos
, *p
)) {
1531 init_apos(&lastAPos
, 0, 0, (*p
)->cxt
->fn
[0]->file
);
1532 init_fcost(&ccSum
[0], 0, 0, 0);
1533 init_fcost(&ccSum
[1], 0, 0, 0);
1537 if (CLG_(clo
).dump_bbs
) {
1538 /* FIXME: Specify Object of BB if different to object of fn */
1540 ULong ecounter
= (*p
)->ecounter_sum
;
1541 pos
= VG_(sprintf
)(print_buf
, "bb=%#lx ", (*p
)->bb
->offset
);
1542 for(i
= 0; i
<(*p
)->bb
->cjmp_count
;i
++) {
1543 pos
+= VG_(sprintf
)(print_buf
+pos
, "%d %llu ",
1544 (*p
)->bb
->jmp
[i
].instr
,
1546 ecounter
-= (*p
)->jmp
[i
].ecounter
;
1548 VG_(sprintf
)(print_buf
+pos
, "%d %llu\n",
1549 (*p
)->bb
->instr_count
,
1551 my_fwrite(print_fd
, print_buf
, VG_(strlen
)(print_buf
));
1554 fprint_bbcc(print_fd
, *p
, &lastAPos
);
1559 close_dumpfile(print_fd
);
1560 if (array
) VG_(free
)(array
);
1562 /* set counters of last dump */
1563 CLG_(copy_cost
)( CLG_(sets
).full
, ti
->lastdump_cost
,
1564 CLG_(current_state
).cost
);
1566 CLG_DEBUG(1, "- print_bbccs(tid %d)\n", CLG_(current_tid
));
1570 static void print_bbccs(const HChar
* trigger
, Bool only_current_thread
)
1576 print_trigger
= trigger
;
1578 if (!CLG_(clo
).separate_threads
) {
1579 /* All BBCC/JCC costs is stored for thread 1 */
1580 Int orig_tid
= CLG_(current_tid
);
1582 CLG_(switch_thread
)(1);
1583 print_bbccs_of_thread( CLG_(get_current_thread
)() );
1584 CLG_(switch_thread
)(orig_tid
);
1586 else if (only_current_thread
)
1587 print_bbccs_of_thread( CLG_(get_current_thread
)() );
1589 CLG_(forall_threads
)(print_bbccs_of_thread
);
1595 void CLG_(dump_profile
)(const HChar
* trigger
, Bool only_current_thread
)
1597 CLG_DEBUG(2, "+ dump_profile(Trigger '%s')\n",
1598 trigger
? trigger
: "Prg.Term.");
1602 if (VG_(clo_verbosity
) > 1)
1603 VG_(message
)(Vg_DebugMsg
, "Start dumping at BB %llu (%s)...\n",
1604 CLG_(stat
).bb_executions
,
1605 trigger
? trigger
: "Prg.Term.");
1609 print_bbccs(trigger
, only_current_thread
);
1611 bbs_done
= CLG_(stat
).bb_executions
++;
1613 if (VG_(clo_verbosity
) > 1)
1614 VG_(message
)(Vg_DebugMsg
, "Dumping done.\n");
1617 /* Copy command to cmd buffer. We want to original command line
1618 * (can change at runtime)
1621 void init_cmdbuf(void)
1626 CLG_ASSERT( VG_(strlen
)( VG_(args_the_exename
) ) < BUF_LEN
-1);
1627 size
= VG_(sprintf
)(cmdbuf
, " %s", VG_(args_the_exename
));
1629 for(i
= 0; i
< VG_(sizeXA
)( VG_(args_for_client
) ); i
++) {
1630 argv
= * (HChar
**) VG_(indexXA
)( VG_(args_for_client
), i
);
1631 if (!argv
) continue;
1632 if ((size
>0) && (size
< BUF_LEN
)) cmdbuf
[size
++] = ' ';
1633 for(j
=0;argv
[j
]!=0;j
++)
1634 if (size
< BUF_LEN
) cmdbuf
[size
++] = argv
[j
];
1637 if (size
>= BUF_LEN
) size
= BUF_LEN
-1;
1642 * Set up file names for dump output: <out_file>.
1643 * <out_file> is derived from the output format string, which defaults
1644 * to "callgrind.out.%p", where %p is replaced with the PID.
1645 * For the final file name, on intermediate dumps a counter is appended,
1646 * and further, if separate dumps per thread are requested, the thread ID.
1648 * <out_file> always starts with a full absolute path.
1649 * If the output format string represents a relative path, the current
1650 * working directory at program start is used.
1652 * This function has to be called every time a profile dump is generated
1653 * to be able to react on PID changes.
1655 void CLG_(init_dumps
)()
1659 static int thisPID
= 0;
1660 int currentPID
= VG_(getpid
)();
1661 if (currentPID
== thisPID
) {
1662 /* already initialized, and no PID change */
1663 CLG_ASSERT(out_file
!= 0);
1666 thisPID
= currentPID
;
1668 if (!CLG_(clo
).out_format
)
1669 CLG_(clo
).out_format
= DEFAULT_OUTFORMAT
;
1671 /* If a file name was already set, clean up before */
1673 VG_(free
)(out_file
);
1674 VG_(free
)(filename
);
1678 // Setup output filename.
1680 VG_(expand_file_name
)("--callgrind-out-file", CLG_(clo
).out_format
);
1682 /* allocate space big enough for final filenames */
1683 filename
= (HChar
*) CLG_MALLOC("cl.dump.init_dumps.2",
1684 VG_(strlen
)(out_file
)+32);
1685 CLG_ASSERT(filename
!= 0);
1687 /* Make sure the output base file can be written.
1688 * This is used for the dump at program termination.
1689 * We stop with an error here if we can not create the
1690 * file: This is probably because of missing rights,
1691 * and trace parts wouldn't be allowed to be written, too.
1693 VG_(strcpy
)(filename
, out_file
);
1694 res
= VG_(open
)(filename
, VKI_O_WRONLY
|VKI_O_TRUNC
, 0);
1695 if (sr_isError(res
)) {
1696 res
= VG_(open
)(filename
, VKI_O_CREAT
|VKI_O_WRONLY
,
1697 VKI_S_IRUSR
|VKI_S_IWUSR
);
1698 if (sr_isError(res
)) {
1702 if (!sr_isError(res
)) VG_(close
)( (Int
)sr_Res(res
) );
1704 if (!dumps_initialized
)
1707 dumps_initialized
= True
;