1 /*--------------------------------------------------------------------*/
4 /*--------------------------------------------------------------------*/
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, 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
;
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 Int
CLG_(get_dump_counter
)(void)
58 /*------------------------------------------------------------*/
59 /*--- Output file related stuff ---*/
60 /*------------------------------------------------------------*/
62 /* Boolean dumping array */
63 static Bool
* dump_array
= 0;
64 static Int dump_array_size
= 0;
65 static Bool
* obj_dumped
= 0;
66 static Bool
* file_dumped
= 0;
67 static Bool
* fn_dumped
= 0;
68 static Bool
* cxt_dumped
= 0;
71 void reset_dump_array(void)
75 CLG_ASSERT(dump_array
!= 0);
77 for(i
=0;i
<dump_array_size
;i
++)
78 dump_array
[i
] = False
;
82 void init_dump_array(void)
84 dump_array_size
= CLG_(stat
).distinct_objs
+
85 CLG_(stat
).distinct_files
+
86 CLG_(stat
).distinct_fns
+
87 CLG_(stat
).context_counter
;
88 CLG_ASSERT(dump_array
== 0);
89 dump_array
= (Bool
*) CLG_MALLOC("cl.dump.ida.1",
90 dump_array_size
* sizeof(Bool
));
91 obj_dumped
= dump_array
;
92 file_dumped
= obj_dumped
+ CLG_(stat
).distinct_objs
;
93 fn_dumped
= file_dumped
+ CLG_(stat
).distinct_files
;
94 cxt_dumped
= fn_dumped
+ CLG_(stat
).distinct_fns
;
98 CLG_DEBUG(1, " init_dump_array: size %d\n", dump_array_size
);
102 void free_dump_array(void)
104 CLG_ASSERT(dump_array
!= 0);
105 VG_(free
)(dump_array
);
115 /* Initialize to an invalid position */
117 void init_fpos(FnPos
* p
)
127 static void print_obj(VgFile
*fp
, const HChar
* prefix
, obj_node
* obj
)
129 if (CLG_(clo
).compress_strings
) {
130 CLG_ASSERT(obj_dumped
!= 0);
131 if (obj_dumped
[obj
->number
])
132 VG_(fprintf
)(fp
, "%s(%u)\n", prefix
, obj
->number
);
134 VG_(fprintf
)(fp
, "%s(%u) %s\n", prefix
, obj
->number
, obj
->name
);
138 VG_(fprintf
)(fp
, "%s%s\n", prefix
, obj
->name
);
141 /* add mapping parameters the first time a object is dumped
142 * format: mp=0xSTART SIZE 0xOFFSET */
143 if (!obj_dumped
[obj
->number
]) {
144 obj_dumped
[obj
->number
];
145 VG_(fprintf
)(fp
, "mp=%p %p %p\n",
146 pos
->obj
->start
, pos
->obj
->size
, pos
->obj
->offset
);
149 obj_dumped
[obj
->number
] = True
;
153 static void print_file(VgFile
*fp
, const char *prefix
, const file_node
* file
)
155 if (CLG_(clo
).compress_strings
) {
156 CLG_ASSERT(file_dumped
!= 0);
157 if (file_dumped
[file
->number
])
158 VG_(fprintf
)(fp
, "%s(%u)\n", prefix
, file
->number
);
160 VG_(fprintf
)(fp
, "%s(%u) %s\n", prefix
, file
->number
, file
->name
);
161 file_dumped
[file
->number
] = True
;
165 VG_(fprintf
)(fp
, "%s%s\n", prefix
, file
->name
);
169 * tag can be "fn", "cfn", "jfn"
171 static void print_fn(VgFile
*fp
, const HChar
* tag
, const fn_node
* fn
)
173 VG_(fprintf
)(fp
, "%s=",tag
);
174 if (CLG_(clo
).compress_strings
) {
175 CLG_ASSERT(fn_dumped
!= 0);
176 if (fn_dumped
[fn
->number
])
177 VG_(fprintf
)(fp
, "(%u)\n", fn
->number
);
179 VG_(fprintf
)(fp
, "(%u) %s\n", fn
->number
, fn
->name
);
180 fn_dumped
[fn
->number
] = True
;
184 VG_(fprintf
)(fp
, "%s\n", fn
->name
);
187 static void print_mangled_fn(VgFile
*fp
, const HChar
* tag
,
188 Context
* cxt
, int rec_index
)
192 if (CLG_(clo
).compress_strings
&& CLG_(clo
).compress_mangled
) {
197 CLG_ASSERT(cxt_dumped
!= 0);
198 if (cxt_dumped
[cxt
->base_number
+rec_index
]) {
199 VG_(fprintf
)(fp
, "%s=(%u)\n",
200 tag
, cxt
->base_number
+ rec_index
);
205 /* make sure that for all context parts compressed data is written */
206 for(i
=cxt
->size
;i
>0;i
--) {
207 CLG_ASSERT(cxt
->fn
[i
-1]->pure_cxt
!= 0);
208 n
= cxt
->fn
[i
-1]->pure_cxt
->base_number
;
209 if (cxt_dumped
[n
]) continue;
210 VG_(fprintf
)(fp
, "%s=(%d) %s\n",
211 tag
, n
, cxt
->fn
[i
-1]->name
);
213 cxt_dumped
[n
] = True
;
214 last
= cxt
->fn
[i
-1]->pure_cxt
;
216 /* If the last context was the context to print, we are finished */
217 if ((last
== cxt
) && (rec_index
== 0)) return;
219 VG_(fprintf
)(fp
, "%s=(%u) (%u)", tag
,
220 cxt
->base_number
+ rec_index
,
221 cxt
->fn
[0]->pure_cxt
->base_number
);
223 VG_(fprintf
)(fp
, "'%d", rec_index
+1);
224 for(i
=1;i
<cxt
->size
;i
++)
225 VG_(fprintf
)(fp
, "'(%u)",
226 cxt
->fn
[i
]->pure_cxt
->base_number
);
227 VG_(fprintf
)(fp
, "\n");
229 cxt_dumped
[cxt
->base_number
+rec_index
] = True
;
234 VG_(fprintf
)(fp
, "%s=", tag
);
235 if (CLG_(clo
).compress_strings
) {
236 CLG_ASSERT(cxt_dumped
!= 0);
237 if (cxt_dumped
[cxt
->base_number
+rec_index
]) {
238 VG_(fprintf
)(fp
, "(%u)\n", cxt
->base_number
+ rec_index
);
242 VG_(fprintf
)(fp
, "(%u) ", cxt
->base_number
+ rec_index
);
243 cxt_dumped
[cxt
->base_number
+rec_index
] = True
;
247 VG_(fprintf
)(fp
, "%s", cxt
->fn
[0]->name
);
249 VG_(fprintf
)(fp
, "'%d", rec_index
+1);
250 for(i
=1;i
<cxt
->size
;i
++)
251 VG_(fprintf
)(fp
, "'%s", cxt
->fn
[i
]->name
);
253 VG_(fprintf
)(fp
, "\n");
259 * Print function position of the BBCC, but only print info differing to
260 * the <last> position, update <last>
261 * Return True if something changes.
263 static Bool
print_fn_pos(VgFile
*fp
, FnPos
* last
, BBCC
* bbcc
)
267 CLG_ASSERT(bbcc
&& bbcc
->cxt
);
270 CLG_DEBUG(2, "+ print_fn_pos: ");
271 CLG_(print_cxt
)(16, bbcc
->cxt
, bbcc
->rec_index
);
274 if (!CLG_(clo
).mangle_names
) {
275 if (last
->rec_index
!= bbcc
->rec_index
) {
276 VG_(fprintf
)(fp
, "rec=%u\n\n", bbcc
->rec_index
);
277 last
->rec_index
= bbcc
->rec_index
;
278 last
->cxt
= 0; /* reprint context */
282 if (last
->cxt
!= bbcc
->cxt
) {
283 fn_node
* last_from
= (last
->cxt
&& last
->cxt
->size
>1) ?
284 last
->cxt
->fn
[1] : 0;
285 fn_node
* curr_from
= (bbcc
->cxt
->size
>1) ?
286 bbcc
->cxt
->fn
[1] : 0;
287 if (curr_from
== 0) {
288 if (last_from
!= 0) {
289 /* switch back to no context */
290 VG_(fprintf
)(fp
, "frfn=(spontaneous)\n");
294 else if (last_from
!= curr_from
) {
295 print_fn(fp
, "frfn", curr_from
);
298 last
->cxt
= bbcc
->cxt
;
302 if (last
->obj
!= bbcc
->cxt
->fn
[0]->file
->obj
) {
303 print_obj(fp
, "ob=", bbcc
->cxt
->fn
[0]->file
->obj
);
304 last
->obj
= bbcc
->cxt
->fn
[0]->file
->obj
;
308 if (last
->file
!= bbcc
->cxt
->fn
[0]->file
) {
309 print_file(fp
, "fl=", bbcc
->cxt
->fn
[0]->file
);
310 last
->file
= bbcc
->cxt
->fn
[0]->file
;
314 if (!CLG_(clo
).mangle_names
) {
315 if (last
->fn
!= bbcc
->cxt
->fn
[0]) {
316 print_fn(fp
, "fn", bbcc
->cxt
->fn
[0]);
317 last
->fn
= bbcc
->cxt
->fn
[0];
322 /* Print mangled name if context or rec_index changes */
323 if ((last
->rec_index
!= bbcc
->rec_index
) ||
324 (last
->cxt
!= bbcc
->cxt
)) {
326 print_mangled_fn(fp
, "fn", bbcc
->cxt
, bbcc
->rec_index
);
327 last
->fn
= bbcc
->cxt
->fn
[0];
328 last
->rec_index
= bbcc
->rec_index
;
333 last
->cxt
= bbcc
->cxt
;
335 CLG_DEBUG(2, "- print_fn_pos: %s\n", res
? "changed" : "");
340 /* the debug lookup cache is useful if BBCC for same BB are
341 * dumped directly in a row. This is a direct mapped cache.
343 #define DEBUG_CACHE_SIZE 1777
345 static Addr debug_cache_addr
[DEBUG_CACHE_SIZE
];
346 static file_node
* debug_cache_file
[DEBUG_CACHE_SIZE
];
347 static int debug_cache_line
[DEBUG_CACHE_SIZE
];
348 static Bool debug_cache_info
[DEBUG_CACHE_SIZE
];
351 void init_debug_cache(void)
354 for(i
=0;i
<DEBUG_CACHE_SIZE
;i
++) {
355 debug_cache_addr
[i
] = 0;
356 debug_cache_file
[i
] = 0;
357 debug_cache_line
[i
] = 0;
358 debug_cache_info
[i
] = 0;
362 static /* __inline__ */
363 Bool
get_debug_pos(BBCC
* bbcc
, Addr addr
, AddrPos
* p
)
365 const HChar
*file
, *dir
;
366 Bool found_file_line
;
368 int cachepos
= addr
% DEBUG_CACHE_SIZE
;
370 if (debug_cache_addr
[cachepos
] == addr
) {
371 p
->line
= debug_cache_line
[cachepos
];
372 p
->file
= debug_cache_file
[cachepos
];
373 found_file_line
= debug_cache_info
[cachepos
];
376 found_file_line
= VG_(get_filename_linenum
)(addr
,
380 if (!found_file_line
) {
384 p
->file
= CLG_(get_file_node
)(bbcc
->bb
->obj
, dir
, file
);
386 debug_cache_info
[cachepos
] = found_file_line
;
387 debug_cache_addr
[cachepos
] = addr
;
388 debug_cache_line
[cachepos
] = p
->line
;
389 debug_cache_file
[cachepos
] = p
->file
;
392 /* Address offset from bbcc start address */
393 p
->addr
= addr
- bbcc
->bb
->obj
->offset
;
394 p
->bb_addr
= bbcc
->bb
->offset
;
396 CLG_DEBUG(3, " get_debug_pos(%#lx): BB %#lx, fn '%s', file '%s', line %u\n",
397 addr
, bb_addr(bbcc
->bb
), bbcc
->cxt
->fn
[0]->name
,
398 p
->file
->name
, p
->line
);
400 return found_file_line
;
404 /* copy file position and init cost */
405 static void init_apos(AddrPos
* p
, Addr addr
, Addr bbaddr
, file_node
* file
)
413 static void copy_apos(AddrPos
* dst
, AddrPos
* src
)
415 dst
->addr
= src
->addr
;
416 dst
->bb_addr
= src
->bb_addr
;
417 dst
->file
= src
->file
;
418 dst
->line
= src
->line
;
421 /* copy file position and init cost */
422 static void init_fcost(AddrCost
* c
, Addr addr
, Addr bbaddr
, file_node
* file
)
424 init_apos( &(c
->p
), addr
, bbaddr
, file
);
425 /* FIXME: This is a memory leak as a AddrCost is inited multiple times */
426 c
->cost
= CLG_(get_eventset_cost
)( CLG_(sets
).full
);
427 CLG_(init_cost
)( CLG_(sets
).full
, c
->cost
);
432 * print position change inside of a BB (last -> curr)
433 * this doesn't update last to curr!
435 static void fprint_apos(VgFile
*fp
, AddrPos
* curr
, AddrPos
* last
,
436 file_node
* func_file
)
438 CLG_ASSERT(curr
->file
!= 0);
439 CLG_DEBUG(2, " print_apos(file '%s', line %u, bb %#lx, addr %#lx) fnFile '%s'\n",
440 curr
->file
->name
, curr
->line
, curr
->bb_addr
, curr
->addr
,
443 if (curr
->file
!= last
->file
) {
445 /* if we switch back to orig file, use fe=... */
446 if (curr
->file
== func_file
)
447 print_file(fp
, "fe=", curr
->file
);
449 print_file(fp
, "fi=", curr
->file
);
452 if (CLG_(clo
).dump_bbs
) {
453 if (curr
->line
!= last
->line
) {
454 VG_(fprintf
)(fp
, "ln=%u\n", curr
->line
);
463 * This prints out differences if allowed
465 * This doesn't set last to curr afterwards!
468 void fprint_pos(VgFile
*fp
, const AddrPos
* curr
, const AddrPos
* last
)
470 if (0) //CLG_(clo).dump_bbs)
471 VG_(fprintf
)(fp
, "%lu ", curr
->addr
- curr
->bb_addr
);
473 if (CLG_(clo
).dump_instr
) {
474 int diff
= curr
->addr
- last
->addr
;
475 if ( CLG_(clo
).compress_pos
&& (last
->addr
>0) &&
476 (diff
> -100) && (diff
< 100)) {
478 VG_(fprintf
)(fp
, "+%d ", diff
);
480 VG_(fprintf
)(fp
, "* ");
482 VG_(fprintf
)(fp
, "%d ", diff
);
485 VG_(fprintf
)(fp
, "%#lx ", curr
->addr
);
488 if (CLG_(clo
).dump_bb
) {
489 int diff
= curr
->bb_addr
- last
->bb_addr
;
490 if ( CLG_(clo
).compress_pos
&& (last
->bb_addr
>0) &&
491 (diff
> -100) && (diff
< 100)) {
493 VG_(fprintf
)(fp
, "+%d ", diff
);
495 VG_(fprintf
)(fp
, "* ");
497 VG_(fprintf
)(fp
, "%d ", diff
);
500 VG_(fprintf
)(fp
, "%#lx ", curr
->bb_addr
);
503 if (CLG_(clo
).dump_line
) {
504 int diff
= curr
->line
- last
->line
;
505 if ( CLG_(clo
).compress_pos
&& (last
->line
>0) &&
506 (diff
> -100) && (diff
< 100)) {
509 VG_(fprintf
)(fp
, "+%d ", diff
);
511 VG_(fprintf
)(fp
, "* ");
513 VG_(fprintf
)(fp
, "%d ", diff
);
516 VG_(fprintf
)(fp
, "%u ", curr
->line
);
527 void fprint_cost(VgFile
*fp
, const EventMapping
* es
, const ULong
* cost
)
529 HChar
*mcost
= CLG_(mappingcost_as_string
)(es
, cost
);
530 VG_(fprintf
)(fp
, "%s\n", mcost
);
536 /* Write the cost of a source line; only that parts of the source
537 * position are written that changed relative to last written position.
538 * funcPos is the source position of the first line of actual function.
539 * Something is written only if cost != 0; returns True in this case.
541 static void fprint_fcost(VgFile
*fp
, AddrCost
* c
, AddrPos
* last
)
544 CLG_DEBUG(2, " print_fcost(file '%s', line %u, bb %#lx, addr %#lx):\n",
545 c
->p
.file
->name
, c
->p
.line
, c
->p
.bb_addr
, c
->p
.addr
);
546 CLG_(print_cost
)(-5, CLG_(sets
).full
, c
->cost
);
549 fprint_pos(fp
, &(c
->p
), last
);
550 copy_apos( last
, &(c
->p
) ); /* update last to current position */
552 fprint_cost(fp
, CLG_(dumpmap
), c
->cost
);
554 /* add cost to total */
555 CLG_(add_and_zero_cost
)( CLG_(sets
).full
, dump_total_cost
, c
->cost
);
559 /* Write out the calls from jcc (at pos)
561 static void fprint_jcc(VgFile
*fp
, jCC
* jcc
, AddrPos
* curr
, AddrPos
* last
,
564 static AddrPos target
;
569 CLG_DEBUG(2, " fprint_jcc (jkind %d)\n", (Int
)jcc
->jmpkind
);
570 CLG_(print_jcc
)(-10, jcc
);
573 CLG_ASSERT(jcc
->to
!=0);
574 CLG_ASSERT(jcc
->from
!=0);
576 if (!get_debug_pos(jcc
->to
, bb_addr(jcc
->to
->bb
), &target
)) {
577 /* if we don't have debug info, don't switch to file "???" */
578 target
.file
= last
->file
;
581 if ((jcc
->jmpkind
== jk_CondJump
) || (jcc
->jmpkind
== jk_Jump
)) {
583 /* this is a JCC for a followed conditional or boring jump. */
584 CLG_ASSERT(CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
));
586 /* objects among jumps should be the same.
587 * Otherwise this jump would have been changed to a call
590 CLG_ASSERT(jcc
->from
->bb
->obj
== jcc
->to
->bb
->obj
);
592 /* only print if target position info is useful */
593 if (!CLG_(clo
).dump_instr
&& !CLG_(clo
).dump_bb
&& target
.line
==0) {
594 jcc
->call_counter
= 0;
598 /* Different files/functions are possible e.g. with longjmp's
599 * which change the stack, and thus context
601 if (last
->file
!= target
.file
) {
602 print_file(fp
, "jfi=", target
.file
);
605 if (jcc
->from
->cxt
!= jcc
->to
->cxt
) {
606 if (CLG_(clo
).mangle_names
)
607 print_mangled_fn(fp
, "jfn",
608 jcc
->to
->cxt
, jcc
->to
->rec_index
);
610 print_fn(fp
, "jfn", jcc
->to
->cxt
->fn
[0]);
613 if (jcc
->jmpkind
== jk_CondJump
) {
614 /* format: jcnd=<followed>/<executions> <target> */
615 VG_(fprintf
)(fp
, "jcnd=%llu/%llu ",
616 jcc
->call_counter
, ecounter
);
619 /* format: jump=<jump count> <target> */
620 VG_(fprintf
)(fp
, "jump=%llu ",
624 fprint_pos(fp
, &target
, last
);
625 VG_(fprintf
)(fp
, "\n");
626 fprint_pos(fp
, curr
, last
);
627 VG_(fprintf
)(fp
, "\n");
629 jcc
->call_counter
= 0;
633 file
= jcc
->to
->cxt
->fn
[0]->file
;
634 obj
= jcc
->to
->bb
->obj
;
636 /* object of called position different to object of this function?*/
637 if (jcc
->from
->cxt
->fn
[0]->file
->obj
!= obj
) {
638 print_obj(fp
, "cob=", obj
);
641 /* file of called position different to current file? */
642 if (last
->file
!= file
) {
643 print_file(fp
, "cfi=", file
);
646 if (CLG_(clo
).mangle_names
)
647 print_mangled_fn(fp
, "cfn", jcc
->to
->cxt
, jcc
->to
->rec_index
);
649 print_fn(fp
, "cfn", jcc
->to
->cxt
->fn
[0]);
651 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)) {
652 VG_(fprintf
)(fp
, "calls=%llu ",
655 fprint_pos(fp
, &target
, last
);
656 VG_(fprintf
)(fp
, "\n");
657 fprint_pos(fp
, curr
, last
);
658 fprint_cost(fp
, CLG_(dumpmap
), jcc
->cost
);
660 CLG_(init_cost
)( CLG_(sets
).full
, jcc
->cost
);
662 jcc
->call_counter
= 0;
668 /* Cost summation of functions.We use alternately ccSum[0/1], thus
669 * ssSum[currSum] for recently read lines with same line number.
671 static AddrCost ccSum
[2];
675 * Print all costs of a BBCC:
676 * - FCCs of instructions
677 * - JCCs of the unique jump of this BB
678 * returns True if something was written
680 static Bool
fprint_bbcc(VgFile
*fp
, BBCC
* bbcc
, AddrPos
* last
)
682 InstrInfo
* instr_info
;
684 Bool something_written
= False
;
686 AddrCost
*currCost
, *newCost
;
687 Int jcc_count
= 0, instr
, i
, jmp
;
690 CLG_ASSERT(bbcc
->cxt
!= 0);
692 VG_(printf
)("+ fprint_bbcc (Instr %u): ", bb
->instr_count
);
693 CLG_(print_bbcc
)(15, bbcc
);
696 CLG_ASSERT(currSum
== 0 || currSum
== 1);
697 currCost
= &(ccSum
[currSum
]);
698 newCost
= &(ccSum
[1-currSum
]);
700 ecounter
= bbcc
->ecounter_sum
;
702 instr_info
= &(bb
->instr
[0]);
703 for(instr
=0; instr
<bb
->instr_count
; instr
++, instr_info
++) {
705 /* get debug info of current instruction address and dump cost
706 * if CLG_(clo).dump_bbs or file/line has changed
708 if (!get_debug_pos(bbcc
, bb_addr(bb
) + instr_info
->instr_offset
,
710 /* if we don't have debug info, don't switch to file "???" */
711 newCost
->p
.file
= bbcc
->cxt
->fn
[0]->file
;
714 if (CLG_(clo
).dump_bbs
|| CLG_(clo
).dump_instr
||
715 (newCost
->p
.line
!= currCost
->p
.line
) ||
716 (newCost
->p
.file
!= currCost
->p
.file
)) {
718 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
719 something_written
= True
;
721 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
722 fprint_fcost(fp
, currCost
, last
);
726 currSum
= 1 - currSum
;
727 currCost
= &(ccSum
[currSum
]);
728 newCost
= &(ccSum
[1-currSum
]);
731 /* add line cost to current cost sum */
732 (*CLG_(cachesim
).add_icost
)(currCost
->cost
, bbcc
, instr_info
, ecounter
);
734 /* print jcc's if there are: only jumps */
735 if (bb
->jmp
[jmp
].instr
== instr
) {
737 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
)
738 if (((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
739 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
743 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
744 /* no need to switch buffers, as position is the same */
745 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
746 fprint_fcost(fp
, currCost
, last
);
748 get_debug_pos(bbcc
, bb_addr(bb
)+instr_info
->instr_offset
, &(currCost
->p
));
749 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
750 something_written
= True
;
751 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
752 if (((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
753 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
754 fprint_jcc(fp
, jcc
, &(currCost
->p
), last
, ecounter
);
759 /* update execution counter */
760 if (jmp
< bb
->cjmp_count
)
761 if (bb
->jmp
[jmp
].instr
== instr
) {
762 ecounter
-= bbcc
->jmp
[jmp
].ecounter
;
767 /* jCCs at end? If yes, dump cumulated line info first */
769 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
770 /* yes, if JCC only counts jmp arcs or cost >0 */
771 if ( ((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
772 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
776 if ( (bbcc
->skipped
&&
777 !CLG_(is_zero_cost
)(CLG_(sets
).full
, bbcc
->skipped
)) ||
780 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
781 /* no need to switch buffers, as position is the same */
782 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
783 fprint_fcost(fp
, currCost
, last
);
786 get_debug_pos(bbcc
, bb_jmpaddr(bb
), &(currCost
->p
));
787 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
788 something_written
= True
;
790 /* first, print skipped costs for calls */
791 if (bbcc
->skipped
&& !CLG_(is_zero_cost
)( CLG_(sets
).full
,
793 CLG_(add_and_zero_cost
)( CLG_(sets
).full
,
794 currCost
->cost
, bbcc
->skipped
);
796 VG_(fprintf
)(fp
, "# Skipped\n");
798 fprint_fcost(fp
, currCost
, last
);
802 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
803 CLG_ASSERT(jcc
->jmp
== jmp
);
804 if ( ((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
805 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
807 fprint_jcc(fp
, jcc
, &(currCost
->p
), last
, ecounter
);
811 if (CLG_(clo
).dump_bbs
|| CLG_(clo
).dump_bb
) {
812 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
813 something_written
= True
;
815 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
816 fprint_fcost(fp
, currCost
, last
);
818 if (CLG_(clo
).dump_bbs
) VG_(fprintf
)(fp
, "\n");
820 /* when every cost was immediately written, we must have done so,
821 * as this function is only called when there's cost in a BBCC
823 CLG_ASSERT(something_written
);
826 bbcc
->ecounter_sum
= 0;
827 for(i
=0; i
<=bbcc
->bb
->cjmp_count
; i
++)
828 bbcc
->jmp
[i
].ecounter
= 0;
829 bbcc
->ret_counter
= 0;
831 CLG_DEBUG(1, "- fprint_bbcc: JCCs %d\n", jcc_count
);
833 return something_written
;
838 * from->bb->obj, from->bb->fn
839 * obj, fn[0]->file, fn
842 static int my_cmp(BBCC
** pbbcc1
, BBCC
** pbbcc2
)
845 return (*pbbcc1
)->bb
->offset
- (*pbbcc2
)->bb
->offset
;
847 BBCC
*bbcc1
= *pbbcc1
;
848 BBCC
*bbcc2
= *pbbcc2
;
849 Context
* cxt1
= bbcc1
->cxt
;
850 Context
* cxt2
= bbcc2
->cxt
;
853 if (cxt1
->fn
[0]->file
->obj
!= cxt2
->fn
[0]->file
->obj
)
854 return cxt1
->fn
[0]->file
->obj
- cxt2
->fn
[0]->file
->obj
;
856 if (cxt1
->fn
[0]->file
!= cxt2
->fn
[0]->file
)
857 return cxt1
->fn
[0]->file
- cxt2
->fn
[0]->file
;
859 if (cxt1
->fn
[0] != cxt2
->fn
[0])
860 return cxt1
->fn
[0] - cxt2
->fn
[0];
862 if (bbcc1
->rec_index
!= bbcc2
->rec_index
)
863 return bbcc1
->rec_index
- bbcc2
->rec_index
;
865 while((off
< cxt1
->size
) && (off
< cxt2
->size
)) {
866 fn_node
* ffn1
= cxt1
->fn
[off
];
867 fn_node
* ffn2
= cxt2
->fn
[off
];
868 if (ffn1
->file
->obj
!= ffn2
->file
->obj
)
869 return ffn1
->file
->obj
- ffn2
->file
->obj
;
874 if (cxt1
->size
> cxt2
->size
) return 1;
875 else if (cxt1
->size
< cxt2
->size
) return -1;
877 return bbcc1
->bb
->offset
- bbcc2
->bb
->offset
;
885 /* modified version of:
887 * qsort -- qsort interface implemented by faster quicksort.
888 * J. L. Bentley and M. D. McIlroy, SPE 23 (1993) 1249-1265.
889 * Copyright 1993, John Wiley.
893 void swap(BBCC
** a
, BBCC
** b
)
896 t
= *a
; *a
= *b
; *b
= t
;
899 #define min(x, y) ((x)<=(y) ? (x) : (y))
902 BBCC
** med3(BBCC
**a
, BBCC
**b
, BBCC
**c
, int (*cmp
)(BBCC
**,BBCC
**))
903 { return cmp(a
, b
) < 0 ?
904 (cmp(b
, c
) < 0 ? b
: cmp(a
, c
) < 0 ? c
: a
)
905 : (cmp(b
, c
) > 0 ? b
: cmp(a
, c
) > 0 ? c
: a
);
908 static BBCC
** qsort_start
= 0;
910 static void CLG_(qsort
)(BBCC
**a
, int n
, int (*cmp
)(BBCC
**,BBCC
**))
912 BBCC
**pa
, **pb
, **pc
, **pd
, **pl
, **pm
, **pn
, **pv
;
916 CLG_DEBUG(8, " qsort(%ld,%ld)\n", a
-qsort_start
+ 0L, n
+ 0L);
918 if (n
< 7) { /* Insertion sort on smallest arrays */
919 for (pm
= a
+1; pm
< a
+n
; pm
++)
920 for (pl
= pm
; pl
> a
&& cmp(pl
-1, pl
) > 0; pl
--)
924 for (pm
= a
; pm
< a
+n
; pm
++) {
925 VG_(printf
)(" %3ld BB %#lx, ",
926 pm
- qsort_start
+ 0L,
928 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
933 pm
= a
+ n
/2; /* Small arrays, middle element */
937 if (n
> 40) { /* Big arrays, pseudomedian of 9 */
939 pl
= med3(pl
, pl
+s
, pl
+2*s
, cmp
);
940 pm
= med3(pm
-s
, pm
, pm
+s
, cmp
);
941 pn
= med3(pn
-2*s
, pn
-s
, pn
, cmp
);
943 pm
= med3(pl
, pm
, pn
, cmp
); /* Mid-size, med of 3 */
952 while ((pb
<= pc
) && ((r
=cmp(pb
, pv
)) <= 0)) {
954 /* same as pivot, to start */
959 while ((pb
<= pc
) && ((r
=cmp(pc
, pv
)) >= 0)) {
961 /* same as pivot, to end */
966 if (pb
> pc
) { break; }
974 /* put pivot from start into middle */
975 if ((s
= pa
-a
)>0) { for(r
=0;r
<s
;r
++) swap(a
+r
, pb
+1-s
+r
); }
976 /* put pivot from end into middle */
977 if ((s
= a
+n
-1-pd
)>0) { for(r
=0;r
<s
;r
++) swap(pc
+r
, a
+n
-s
+r
); }
980 VG_(printf
)(" PV BB %#lx, ", bb_addr((*pv
)->bb
));
981 CLG_(print_cxt
)(9, (*pv
)->cxt
, (*pv
)->rec_index
);
984 VG_(printf
)(" Lower %ld - %ld:\n",
986 a
+s
-1-qsort_start
+ 0L);
989 VG_(printf
)(" %3ld BB %#lx, ",
992 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
996 VG_(printf
)(" Upper %ld - %ld:\n",
997 a
+n
-s
-qsort_start
+ 0L,
998 a
+n
-1-qsort_start
+ 0L);
1001 VG_(printf
)(" %3ld BB %#lx, ",
1002 pm
-qsort_start
+ 0L,
1003 bb_addr((*pm
)->bb
));
1004 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
1008 if ((s
= pb
+1-pa
) > 1) CLG_(qsort
)(a
, s
, cmp
);
1009 if ((s
= pd
+1-pc
) > 1) CLG_(qsort
)(a
+n
-s
, s
, cmp
);
1013 /* Helpers for prepare_dump */
1015 static Int prepare_count
;
1016 static BBCC
** prepare_ptr
;
1019 static void hash_addCount(BBCC
* bbcc
)
1021 if ((bbcc
->ecounter_sum
> 0) || (bbcc
->ret_counter
>0))
1025 static void hash_addPtr(BBCC
* bbcc
)
1027 if ((bbcc
->ecounter_sum
== 0) &&
1028 (bbcc
->ret_counter
== 0)) return;
1030 *prepare_ptr
= bbcc
;
1035 static void cs_addCount(thread_info
* ti
)
1040 /* add BBCCs with active call in call stack of current thread.
1041 * update cost sums for active calls
1044 for(i
= 0; i
< CLG_(current_call_stack
).sp
; i
++) {
1045 call_entry
* e
= &(CLG_(current_call_stack
).entry
[i
]);
1046 if (e
->jcc
== 0) continue;
1048 CLG_(add_diff_cost_lz
)( CLG_(sets
).full
, &(e
->jcc
->cost
),
1049 e
->enter_cost
, CLG_(current_state
).cost
);
1050 bbcc
= e
->jcc
->from
;
1052 CLG_DEBUG(1, " [%2d] (tid %u), added active: %s\n",
1053 i
,CLG_(current_tid
),bbcc
->cxt
->fn
[0]->name
);
1055 if (bbcc
->ecounter_sum
>0 || bbcc
->ret_counter
>0) {
1056 /* already counted */
1063 static void cs_addPtr(thread_info
* ti
)
1068 /* add BBCCs with active call in call stack of current thread.
1069 * update cost sums for active calls
1072 for(i
= 0; i
< CLG_(current_call_stack
).sp
; i
++) {
1073 call_entry
* e
= &(CLG_(current_call_stack
).entry
[i
]);
1074 if (e
->jcc
== 0) continue;
1076 bbcc
= e
->jcc
->from
;
1078 if (bbcc
->ecounter_sum
>0 || bbcc
->ret_counter
>0) {
1079 /* already counted */
1083 *prepare_ptr
= bbcc
;
1090 * Put all BBCCs with costs into a sorted array.
1091 * The returned arrays ends with a null pointer.
1092 * Must be freed after dumping.
1095 BBCC
** prepare_dump(void)
1101 /* if we do not separate among threads, this gives all */
1102 /* count number of BBCCs with >0 executions */
1103 CLG_(forall_bbccs
)(hash_addCount
);
1105 /* even if we do not separate among threads,
1106 * call stacks are separated */
1107 if (CLG_(clo
).separate_threads
)
1110 CLG_(forall_threads
)(cs_addCount
);
1112 CLG_DEBUG(0, "prepare_dump: %d BBCCs\n", prepare_count
);
1114 /* allocate bbcc array, insert BBCCs and sort */
1115 prepare_ptr
= array
=
1116 (BBCC
**) CLG_MALLOC("cl.dump.pd.1",
1117 (prepare_count
+1) * sizeof(BBCC
*));
1119 CLG_(forall_bbccs
)(hash_addPtr
);
1121 if (CLG_(clo
).separate_threads
)
1124 CLG_(forall_threads
)(cs_addPtr
);
1126 CLG_ASSERT(array
+ prepare_count
== prepare_ptr
);
1131 CLG_DEBUG(0," BBCCs inserted\n");
1133 qsort_start
= array
;
1134 CLG_(qsort
)(array
, prepare_count
, my_cmp
);
1136 CLG_DEBUG(0," BBCCs sorted\n");
1144 static void fprint_cost_ln(VgFile
*fp
, const HChar
* prefix
,
1145 const EventMapping
* em
, const ULong
* cost
)
1147 HChar
*mcost
= CLG_(mappingcost_as_string
)(em
, cost
);
1148 VG_(fprintf
)(fp
, "%s%s\n", prefix
, mcost
);
1152 static ULong bbs_done
= 0;
1153 static HChar
* filename
= 0;
1158 VG_(message
)(Vg_UserMsg
,
1159 "Error: can not open cache simulation output file `%s'\n",
1165 * Create a new dump file and write header.
1167 * Naming: <CLG_(clo).filename_base>.<pid>[.<part>][-<tid>]
1168 * <part> is skipped for final dump (trigger==0)
1169 * <tid> is skipped for thread 1 with CLG_(clo).separate_threads=no
1171 * Returns the file descriptor, and -1 on error (no write permission)
1173 static VgFile
*new_dumpfile(int tid
, const HChar
* trigger
)
1175 Bool appending
= False
;
1180 CLG_ASSERT(dumps_initialized
);
1181 CLG_ASSERT(filename
!= 0);
1183 if (!CLG_(clo
).combine_dumps
) {
1184 i
= VG_(sprintf
)(filename
, "%s", out_file
);
1187 i
+= VG_(sprintf
)(filename
+i
, ".%d", out_counter
);
1189 if (CLG_(clo
).separate_threads
)
1190 VG_(sprintf
)(filename
+i
, "-%02d", tid
);
1192 fp
= VG_(fopen
)(filename
, VKI_O_WRONLY
|VKI_O_TRUNC
, 0);
1195 VG_(sprintf
)(filename
, "%s", out_file
);
1196 fp
= VG_(fopen
)(filename
, VKI_O_WRONLY
|VKI_O_APPEND
, 0);
1197 if (fp
&& out_counter
>1)
1202 fp
= VG_(fopen
)(filename
, VKI_O_CREAT
|VKI_O_WRONLY
,
1203 VKI_S_IRUSR
|VKI_S_IWUSR
);
1205 /* If the file can not be opened for whatever reason (conflict
1206 between multiple supervised processes?), give up now. */
1211 CLG_DEBUG(2, " new_dumpfile '%s'\n", filename
);
1218 /* callgrind format specification, has to be on 1st line */
1219 VG_(fprintf
)(fp
, "# callgrind format\n");
1222 VG_(fprintf
)(fp
, "version: 1\n");
1225 VG_(fprintf
)(fp
, "creator: callgrind-" VERSION
"\n");
1228 VG_(fprintf
)(fp
, "pid: %d\n", VG_(getpid
)());
1231 VG_(fprintf
)(fp
, "cmd: %s", cmdbuf
);
1234 VG_(fprintf
)(fp
, "\npart: %d\n", out_counter
);
1235 if (CLG_(clo
).separate_threads
) {
1236 VG_(fprintf
)(fp
, "thread: %d\n", tid
);
1241 VG_(fprintf
)(fp
, "\n");
1244 /* Global options changing the tracing behaviour */
1245 VG_(fprintf
)(fp
, "\ndesc: Option: --skip-plt=%s\n",
1246 CLG_(clo
).skip_plt
? "yes" : "no");
1247 VG_(fprintf
)(fp
, "desc: Option: --collect-jumps=%s\n",
1248 CLG_(clo
).collect_jumps
? "yes" : "no");
1249 VG_(fprintf
)(fp
, "desc: Option: --separate-recs=%d\n",
1250 CLG_(clo
).separate_recursions
);
1251 VG_(fprintf
)(fp
, "desc: Option: --separate-callers=%d\n",
1252 CLG_(clo
).separate_callers
);
1254 VG_(fprintf
)(fp
, "desc: Option: --dump-bbs=%s\n",
1255 CLG_(clo
).dump_bbs
? "yes" : "no");
1256 VG_(fprintf
)(fp
, "desc: Option: --separate-threads=%s\n",
1257 CLG_(clo
).separate_threads
? "yes" : "no");
1260 (*CLG_(cachesim
).dump_desc
)(fp
);
1263 VG_(fprintf
)(fp
, "\ndesc: Timerange: Basic block %llu - %llu\n",
1264 bbs_done
, CLG_(stat
).bb_executions
);
1266 VG_(fprintf
)(fp
, "desc: Trigger: %s\n",
1267 trigger
? trigger
: "Program termination");
1270 /* Output function specific config
1272 for (i
= 0; i
< N_FNCONFIG_ENTRIES
; i
++) {
1276 VG_(fprintf
)(fp
, "desc: Option: --fn-skip=%s\n", fnc
->name
);
1278 if (fnc
->dump_at_enter
) {
1279 VG_(fprintf
)(fp
, "desc: Option: --fn-dump-at-enter=%s\n",
1282 if (fnc
->dump_at_leave
) {
1283 VG_(fprintf
)(fp
, "desc: Option: --fn-dump-at-leave=%s\n",
1286 if (fnc
->separate_callers
!= CLG_(clo
).separate_callers
) {
1287 VG_(fprintf
)(fp
, "desc: Option: --separate-callers%d=%s\n",
1288 fnc
->separate_callers
, fnc
->name
);
1290 if (fnc
->separate_recursions
!= CLG_(clo
).separate_recursions
) {
1291 VG_(fprintf
)(fp
, "desc: Option: --separate-recs%d=%s\n",
1292 fnc
->separate_recursions
, fnc
->name
);
1299 /* "positions:" line */
1300 VG_(fprintf
)(fp
, "\npositions:%s%s%s\n",
1301 CLG_(clo
).dump_instr
? " instr" : "",
1302 CLG_(clo
).dump_bb
? " bb" : "",
1303 CLG_(clo
).dump_line
? " line" : "");
1305 /* "events:" line */
1306 HChar
*evmap
= CLG_(eventmapping_as_string
)(CLG_(dumpmap
));
1307 VG_(fprintf
)(fp
, "events: %s\n", evmap
);
1311 sum
= CLG_(get_eventset_cost
)( CLG_(sets
).full
);
1312 CLG_(zero_cost
)(CLG_(sets
).full
, sum
);
1313 if (CLG_(clo
).separate_threads
) {
1314 thread_info
* ti
= CLG_(get_current_thread
)();
1315 CLG_(add_diff_cost
)(CLG_(sets
).full
, sum
, ti
->lastdump_cost
,
1316 ti
->states
.entry
[0]->cost
);
1319 /* This function is called once for thread 1, where
1320 * all costs are summed up when not dumping separate per thread.
1321 * But this is not true for summary: we need to add all threads.
1324 thread_info
** thr
= CLG_(get_threads
)();
1325 for(t
=1;t
<VG_N_THREADS
;t
++) {
1326 if (!thr
[t
]) continue;
1327 CLG_(add_diff_cost
)(CLG_(sets
).full
, sum
,
1328 thr
[t
]->lastdump_cost
,
1329 thr
[t
]->states
.entry
[0]->cost
);
1332 fprint_cost_ln(fp
, "summary: ", CLG_(dumpmap
), sum
);
1334 /* all dumped cost will be added to total_fcc */
1335 CLG_(init_cost_lz
)( CLG_(sets
).full
, &dump_total_cost
);
1337 VG_(fprintf
)(fp
, "\n\n");
1339 if (VG_(clo_verbosity
) > 1)
1340 VG_(message
)(Vg_DebugMsg
, "Dump to %s\n", filename
);
1346 static void close_dumpfile(VgFile
*fp
)
1348 if (fp
== NULL
) return;
1350 fprint_cost_ln(fp
, "totals: ", CLG_(dumpmap
),
1352 //fprint_fcc_ln(fp, "summary: ", &dump_total_fcc);
1353 CLG_(add_cost_lz
)(CLG_(sets
).full
,
1354 &CLG_(total_cost
), dump_total_cost
);
1358 if (filename
[0] == '.') {
1359 if (-1 == VG_(rename
) (filename
, filename
+1)) {
1360 /* Can not rename to correct file name: give out warning */
1361 VG_(message
)(Vg_DebugMsg
, "Warning: Can not rename .%s to %s\n",
1362 filename
, filename
);
1368 /* Helper for print_bbccs */
1370 static const HChar
* print_trigger
;
1372 static void print_bbccs_of_thread(thread_info
* ti
)
1378 CLG_DEBUG(1, "+ print_bbccs(tid %u)\n", CLG_(current_tid
));
1380 VgFile
*print_fp
= new_dumpfile(CLG_(current_tid
), print_trigger
);
1381 if (print_fp
== NULL
) {
1382 CLG_DEBUG(1, "- print_bbccs(tid %u): No output...\n", CLG_(current_tid
));
1386 p
= array
= prepare_dump();
1387 init_fpos(&lastFnPos
);
1388 init_apos(&lastAPos
, 0, 0, 0);
1392 /* on context/function change, print old cost buffer before */
1393 if (lastFnPos
.cxt
&& ((*p
==0) ||
1394 (lastFnPos
.cxt
!= (*p
)->cxt
) ||
1395 (lastFnPos
.rec_index
!= (*p
)->rec_index
))) {
1396 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, ccSum
[currSum
].cost
)) {
1397 /* no need to switch buffers, as position is the same */
1398 fprint_apos(print_fp
, &(ccSum
[currSum
].p
), &lastAPos
,
1399 lastFnPos
.cxt
->fn
[0]->file
);
1400 fprint_fcost(print_fp
, &ccSum
[currSum
], &lastAPos
);
1403 if (ccSum
[currSum
].p
.file
!= lastFnPos
.cxt
->fn
[0]->file
) {
1404 /* switch back to file of function */
1405 print_file(print_fp
, "fe=", lastFnPos
.cxt
->fn
[0]->file
);
1407 VG_(fprintf
)(print_fp
, "\n");
1412 if (print_fn_pos(print_fp
, &lastFnPos
, *p
)) {
1415 init_apos(&lastAPos
, 0, 0, (*p
)->cxt
->fn
[0]->file
);
1416 init_fcost(&ccSum
[0], 0, 0, 0);
1417 init_fcost(&ccSum
[1], 0, 0, 0);
1421 if (CLG_(clo
).dump_bbs
) {
1422 /* FIXME: Specify Object of BB if different to object of fn */
1424 ULong ecounter
= (*p
)->ecounter_sum
;
1425 VG_(fprintf
)(print_fp
, "bb=%#lx ", (UWord
)(*p
)->bb
->offset
);
1426 for(i
= 0; i
<(*p
)->bb
->cjmp_count
;i
++) {
1427 VG_(fprintf
)(print_fp
, "%u %llu ",
1428 (*p
)->bb
->jmp
[i
].instr
,
1430 ecounter
-= (*p
)->jmp
[i
].ecounter
;
1432 VG_(fprintf
)(print_fp
, "%u %llu\n",
1433 (*p
)->bb
->instr_count
,
1437 fprint_bbcc(print_fp
, *p
, &lastAPos
);
1442 close_dumpfile(print_fp
);
1445 /* set counters of last dump */
1446 CLG_(copy_cost
)( CLG_(sets
).full
, ti
->lastdump_cost
,
1447 CLG_(current_state
).cost
);
1449 CLG_DEBUG(1, "- print_bbccs(tid %u)\n", CLG_(current_tid
));
1453 static void print_bbccs(const HChar
* trigger
, Bool only_current_thread
)
1458 print_trigger
= trigger
;
1460 if (!CLG_(clo
).separate_threads
) {
1461 /* All BBCC/JCC costs is stored for thread 1 */
1462 Int orig_tid
= CLG_(current_tid
);
1464 CLG_(switch_thread
)(1);
1465 print_bbccs_of_thread( CLG_(get_current_thread
)() );
1466 CLG_(switch_thread
)(orig_tid
);
1468 else if (only_current_thread
)
1469 print_bbccs_of_thread( CLG_(get_current_thread
)() );
1471 CLG_(forall_threads
)(print_bbccs_of_thread
);
1477 void CLG_(dump_profile
)(const HChar
* trigger
, Bool only_current_thread
)
1479 CLG_DEBUG(2, "+ dump_profile(Trigger '%s')\n",
1480 trigger
? trigger
: "Prg.Term.");
1484 if (VG_(clo_verbosity
) > 1)
1485 VG_(message
)(Vg_DebugMsg
, "Start dumping at BB %llu (%s)...\n",
1486 CLG_(stat
).bb_executions
,
1487 trigger
? trigger
: "Prg.Term.");
1491 print_bbccs(trigger
, only_current_thread
);
1493 bbs_done
= CLG_(stat
).bb_executions
++;
1495 if (VG_(clo_verbosity
) > 1)
1496 VG_(message
)(Vg_DebugMsg
, "Dumping done.\n");
1499 /* Copy command to cmd buffer. We want to original command line
1500 * (can change at runtime)
1503 void init_cmdbuf(void)
1508 /* Pass #1: How many bytes do we need? */
1509 size
= 1; // leading ' '
1510 size
+= VG_(strlen
)( VG_(args_the_exename
) );
1511 for (i
= 0; i
< VG_(sizeXA
)( VG_(args_for_client
) ); i
++) {
1512 const HChar
*arg
= *(HChar
**)VG_(indexXA
)( VG_(args_for_client
), i
);
1513 size
+= 1; // separator ' '
1514 // escape NL in arguments to not break dump format
1515 for(j
=0; arg
[j
]; j
++)
1519 size
++; // fall through
1525 cmdbuf
= CLG_MALLOC("cl.dump.ic.1", size
+ 1); // +1 for '\0'
1527 /* Pass #2: Build up the string */
1528 size
= VG_(sprintf
)(cmdbuf
, " %s", VG_(args_the_exename
));
1530 for(i
= 0; i
< VG_(sizeXA
)( VG_(args_for_client
) ); i
++) {
1531 const HChar
*arg
= * (HChar
**) VG_(indexXA
)( VG_(args_for_client
), i
);
1532 cmdbuf
[size
++] = ' ';
1533 for(j
=0; arg
[j
]; j
++)
1536 cmdbuf
[size
++] = '\\';
1537 cmdbuf
[size
++] = 'n';
1540 cmdbuf
[size
++] = '\\';
1541 cmdbuf
[size
++] = '\\';
1544 cmdbuf
[size
++] = arg
[j
];
1548 cmdbuf
[size
] = '\0';
1552 * Set up file names for dump output: <out_file>.
1553 * <out_file> is derived from the output format string, which defaults
1554 * to "callgrind.out.%p", where %p is replaced with the PID.
1555 * For the final file name, on intermediate dumps a counter is appended,
1556 * and further, if separate dumps per thread are requested, the thread ID.
1558 * <out_file> always starts with a full absolute path.
1559 * If the output format string represents a relative path, the current
1560 * working directory at program start is used.
1562 * This function has to be called every time a profile dump is generated
1563 * to be able to react on PID changes.
1565 void CLG_(init_dumps
)()
1569 static int thisPID
= 0;
1570 int currentPID
= VG_(getpid
)();
1571 if (currentPID
== thisPID
) {
1572 /* already initialized, and no PID change */
1573 CLG_ASSERT(out_file
!= 0);
1576 thisPID
= currentPID
;
1578 if (!CLG_(clo
).out_format
)
1579 CLG_(clo
).out_format
= DEFAULT_OUTFORMAT
;
1581 /* If a file name was already set, clean up before */
1583 VG_(free
)(out_file
);
1584 VG_(free
)(filename
);
1588 // Setup output filename.
1590 VG_(expand_file_name
)("--callgrind-out-file", CLG_(clo
).out_format
);
1592 /* allocate space big enough for final filenames */
1593 filename
= (HChar
*) CLG_MALLOC("cl.dump.init_dumps.2",
1594 VG_(strlen
)(out_file
)+32);
1596 /* Make sure the output base file can be written.
1597 * This is used for the dump at program termination.
1598 * We stop with an error here if we can not create the
1599 * file: This is probably because of missing rights,
1600 * and trace parts wouldn't be allowed to be written, too.
1602 VG_(strcpy
)(filename
, out_file
);
1603 res
= VG_(open
)(filename
, VKI_O_WRONLY
|VKI_O_TRUNC
, 0);
1604 if (sr_isError(res
)) {
1605 res
= VG_(open
)(filename
, VKI_O_CREAT
|VKI_O_WRONLY
,
1606 VKI_S_IRUSR
|VKI_S_IWUSR
);
1607 if (sr_isError(res
)) {
1611 if (!sr_isError(res
)) VG_(close
)( (Int
)sr_Res(res
) );
1613 if (!dumps_initialized
)
1616 dumps_initialized
= True
;