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, see <http://www.gnu.org/licenses/>.
24 The GNU General Public License is contained in the file COPYING.
30 #include "pub_tool_threadstate.h"
31 #include "pub_tool_libcfile.h"
34 /* Dump Part Counter */
35 static Int out_counter
= 0;
37 static HChar
* out_file
= 0;
38 static Bool dumps_initialized
= False
;
43 /* Total reads/writes/misses sum over all dumps and threads.
44 * Updated during CC traversal at dump time.
46 FullCost
CLG_(total_cost
) = 0;
47 static FullCost dump_total_cost
= 0;
49 EventMapping
* CLG_(dumpmap
) = 0;
51 Int
CLG_(get_dump_counter
)(void)
56 /*------------------------------------------------------------*/
57 /*--- Output file related stuff ---*/
58 /*------------------------------------------------------------*/
60 /* Boolean dumping array */
61 static Bool
* dump_array
= 0;
62 static Int dump_array_size
= 0;
63 static Bool
* obj_dumped
= 0;
64 static Bool
* file_dumped
= 0;
65 static Bool
* fn_dumped
= 0;
66 static Bool
* cxt_dumped
= 0;
69 void reset_dump_array(void)
73 CLG_ASSERT(dump_array
!= 0);
75 for(i
=0;i
<dump_array_size
;i
++)
76 dump_array
[i
] = False
;
80 void init_dump_array(void)
82 dump_array_size
= CLG_(stat
).distinct_objs
+
83 CLG_(stat
).distinct_files
+
84 CLG_(stat
).distinct_fns
+
85 CLG_(stat
).context_counter
;
86 CLG_ASSERT(dump_array
== 0);
87 dump_array
= (Bool
*) CLG_MALLOC("cl.dump.ida.1",
88 dump_array_size
* sizeof(Bool
));
89 obj_dumped
= dump_array
;
90 file_dumped
= obj_dumped
+ CLG_(stat
).distinct_objs
;
91 fn_dumped
= file_dumped
+ CLG_(stat
).distinct_files
;
92 cxt_dumped
= fn_dumped
+ CLG_(stat
).distinct_fns
;
96 CLG_DEBUG(1, " init_dump_array: size %d\n", dump_array_size
);
100 void free_dump_array(void)
102 CLG_ASSERT(dump_array
!= 0);
103 VG_(free
)(dump_array
);
113 /* Initialize to an invalid position */
115 void init_fpos(FnPos
* p
)
125 static void print_obj(VgFile
*fp
, const HChar
* prefix
, obj_node
* obj
)
127 if (CLG_(clo
).compress_strings
) {
128 CLG_ASSERT(obj_dumped
!= 0);
129 if (obj_dumped
[obj
->number
])
130 VG_(fprintf
)(fp
, "%s(%u)\n", prefix
, obj
->number
);
132 VG_(fprintf
)(fp
, "%s(%u) %s\n", prefix
, obj
->number
, obj
->name
);
136 VG_(fprintf
)(fp
, "%s%s\n", prefix
, obj
->name
);
139 /* add mapping parameters the first time a object is dumped
140 * format: mp=0xSTART SIZE 0xOFFSET */
141 if (!obj_dumped
[obj
->number
]) {
142 obj_dumped
[obj
->number
];
143 VG_(fprintf
)(fp
, "mp=%p %p %p\n",
144 pos
->obj
->start
, pos
->obj
->size
, pos
->obj
->offset
);
147 obj_dumped
[obj
->number
] = True
;
151 static void print_file(VgFile
*fp
, const char *prefix
, const file_node
* file
)
153 if (CLG_(clo
).compress_strings
) {
154 CLG_ASSERT(file_dumped
!= 0);
155 if (file_dumped
[file
->number
])
156 VG_(fprintf
)(fp
, "%s(%u)\n", prefix
, file
->number
);
158 VG_(fprintf
)(fp
, "%s(%u) %s\n", prefix
, file
->number
, file
->name
);
159 file_dumped
[file
->number
] = True
;
163 VG_(fprintf
)(fp
, "%s%s\n", prefix
, file
->name
);
167 * tag can be "fn", "cfn", "jfn"
169 static void print_fn(VgFile
*fp
, const HChar
* tag
, const fn_node
* fn
)
171 VG_(fprintf
)(fp
, "%s=",tag
);
172 if (CLG_(clo
).compress_strings
) {
173 CLG_ASSERT(fn_dumped
!= 0);
174 if (fn_dumped
[fn
->number
])
175 VG_(fprintf
)(fp
, "(%u)\n", fn
->number
);
177 VG_(fprintf
)(fp
, "(%u) %s\n", fn
->number
, fn
->name
);
178 fn_dumped
[fn
->number
] = True
;
182 VG_(fprintf
)(fp
, "%s\n", fn
->name
);
185 static void print_mangled_fn(VgFile
*fp
, const HChar
* tag
,
186 Context
* cxt
, int rec_index
)
190 if (CLG_(clo
).compress_strings
&& CLG_(clo
).compress_mangled
) {
195 CLG_ASSERT(cxt_dumped
!= 0);
196 if (cxt_dumped
[cxt
->base_number
+rec_index
]) {
197 VG_(fprintf
)(fp
, "%s=(%u)\n",
198 tag
, cxt
->base_number
+ rec_index
);
203 /* make sure that for all context parts compressed data is written */
204 for(i
=cxt
->size
;i
>0;i
--) {
205 CLG_ASSERT(cxt
->fn
[i
-1]->pure_cxt
!= 0);
206 n
= cxt
->fn
[i
-1]->pure_cxt
->base_number
;
207 if (cxt_dumped
[n
]) continue;
208 VG_(fprintf
)(fp
, "%s=(%d) %s\n",
209 tag
, n
, cxt
->fn
[i
-1]->name
);
211 cxt_dumped
[n
] = True
;
212 last
= cxt
->fn
[i
-1]->pure_cxt
;
214 /* If the last context was the context to print, we are finished */
215 if ((last
== cxt
) && (rec_index
== 0)) return;
217 VG_(fprintf
)(fp
, "%s=(%u) (%u)", tag
,
218 cxt
->base_number
+ rec_index
,
219 cxt
->fn
[0]->pure_cxt
->base_number
);
221 VG_(fprintf
)(fp
, "'%d", rec_index
+1);
222 for(i
=1;i
<cxt
->size
;i
++)
223 VG_(fprintf
)(fp
, "'(%u)",
224 cxt
->fn
[i
]->pure_cxt
->base_number
);
225 VG_(fprintf
)(fp
, "\n");
227 cxt_dumped
[cxt
->base_number
+rec_index
] = True
;
232 VG_(fprintf
)(fp
, "%s=", tag
);
233 if (CLG_(clo
).compress_strings
) {
234 CLG_ASSERT(cxt_dumped
!= 0);
235 if (cxt_dumped
[cxt
->base_number
+rec_index
]) {
236 VG_(fprintf
)(fp
, "(%u)\n", cxt
->base_number
+ rec_index
);
240 VG_(fprintf
)(fp
, "(%u) ", cxt
->base_number
+ rec_index
);
241 cxt_dumped
[cxt
->base_number
+rec_index
] = True
;
245 VG_(fprintf
)(fp
, "%s", cxt
->fn
[0]->name
);
247 VG_(fprintf
)(fp
, "'%d", rec_index
+1);
248 for(i
=1;i
<cxt
->size
;i
++)
249 VG_(fprintf
)(fp
, "'%s", cxt
->fn
[i
]->name
);
251 VG_(fprintf
)(fp
, "\n");
257 * Print function position of the BBCC, but only print info differing to
258 * the <last> position, update <last>
259 * Return True if something changes.
261 static Bool
print_fn_pos(VgFile
*fp
, FnPos
* last
, BBCC
* bbcc
)
265 CLG_ASSERT(bbcc
&& bbcc
->cxt
);
268 CLG_DEBUG(2, "+ print_fn_pos: ");
269 CLG_(print_cxt
)(16, bbcc
->cxt
, bbcc
->rec_index
);
272 if (!CLG_(clo
).mangle_names
) {
273 if (last
->rec_index
!= bbcc
->rec_index
) {
274 VG_(fprintf
)(fp
, "rec=%u\n\n", bbcc
->rec_index
);
275 last
->rec_index
= bbcc
->rec_index
;
276 last
->cxt
= 0; /* reprint context */
280 if (last
->cxt
!= bbcc
->cxt
) {
281 fn_node
* last_from
= (last
->cxt
&& last
->cxt
->size
>1) ?
282 last
->cxt
->fn
[1] : 0;
283 fn_node
* curr_from
= (bbcc
->cxt
->size
>1) ?
284 bbcc
->cxt
->fn
[1] : 0;
285 if (curr_from
== 0) {
286 if (last_from
!= 0) {
287 /* switch back to no context */
288 VG_(fprintf
)(fp
, "frfn=(spontaneous)\n");
292 else if (last_from
!= curr_from
) {
293 print_fn(fp
, "frfn", curr_from
);
296 last
->cxt
= bbcc
->cxt
;
300 if (last
->obj
!= bbcc
->cxt
->fn
[0]->file
->obj
) {
301 print_obj(fp
, "ob=", bbcc
->cxt
->fn
[0]->file
->obj
);
302 last
->obj
= bbcc
->cxt
->fn
[0]->file
->obj
;
306 if (last
->file
!= bbcc
->cxt
->fn
[0]->file
) {
307 print_file(fp
, "fl=", bbcc
->cxt
->fn
[0]->file
);
308 last
->file
= bbcc
->cxt
->fn
[0]->file
;
312 if (!CLG_(clo
).mangle_names
) {
313 if (last
->fn
!= bbcc
->cxt
->fn
[0]) {
314 print_fn(fp
, "fn", bbcc
->cxt
->fn
[0]);
315 last
->fn
= bbcc
->cxt
->fn
[0];
320 /* Print mangled name if context or rec_index changes */
321 if ((last
->rec_index
!= bbcc
->rec_index
) ||
322 (last
->cxt
!= bbcc
->cxt
)) {
324 print_mangled_fn(fp
, "fn", bbcc
->cxt
, bbcc
->rec_index
);
325 last
->fn
= bbcc
->cxt
->fn
[0];
326 last
->rec_index
= bbcc
->rec_index
;
331 last
->cxt
= bbcc
->cxt
;
333 CLG_DEBUG(2, "- print_fn_pos: %s\n", res
? "changed" : "");
338 /* the debug lookup cache is useful if BBCC for same BB are
339 * dumped directly in a row. This is a direct mapped cache.
341 #define DEBUG_CACHE_SIZE 1777
343 static Addr debug_cache_addr
[DEBUG_CACHE_SIZE
];
344 static file_node
* debug_cache_file
[DEBUG_CACHE_SIZE
];
345 static int debug_cache_line
[DEBUG_CACHE_SIZE
];
346 static Bool debug_cache_info
[DEBUG_CACHE_SIZE
];
349 void init_debug_cache(void)
352 for(i
=0;i
<DEBUG_CACHE_SIZE
;i
++) {
353 debug_cache_addr
[i
] = 0;
354 debug_cache_file
[i
] = 0;
355 debug_cache_line
[i
] = 0;
356 debug_cache_info
[i
] = 0;
360 static /* __inline__ */
361 Bool
get_debug_pos(BBCC
* bbcc
, Addr addr
, AddrPos
* p
)
363 const HChar
*file
, *dir
;
364 Bool found_file_line
;
366 int cachepos
= addr
% DEBUG_CACHE_SIZE
;
368 if (debug_cache_addr
[cachepos
] == addr
) {
369 p
->line
= debug_cache_line
[cachepos
];
370 p
->file
= debug_cache_file
[cachepos
];
371 found_file_line
= debug_cache_info
[cachepos
];
374 DiEpoch ep
= VG_(current_DiEpoch
)();
375 found_file_line
= VG_(get_filename_linenum
)(ep
, addr
,
379 if (!found_file_line
) {
383 p
->file
= CLG_(get_file_node
)(bbcc
->bb
->obj
, dir
, file
);
385 debug_cache_info
[cachepos
] = found_file_line
;
386 debug_cache_addr
[cachepos
] = addr
;
387 debug_cache_line
[cachepos
] = p
->line
;
388 debug_cache_file
[cachepos
] = p
->file
;
391 /* Address offset from bbcc start address */
392 p
->addr
= addr
- bbcc
->bb
->obj
->offset
;
393 p
->bb_addr
= bbcc
->bb
->offset
;
395 CLG_DEBUG(3, " get_debug_pos(%#lx): BB %#lx, fn '%s', file '%s', line %u\n",
396 addr
, bb_addr(bbcc
->bb
), bbcc
->cxt
->fn
[0]->name
,
397 p
->file
->name
, p
->line
);
399 return found_file_line
;
403 /* copy file position and init cost */
404 static void init_apos(AddrPos
* p
, Addr addr
, Addr bbaddr
, file_node
* file
)
412 static void copy_apos(AddrPos
* dst
, AddrPos
* src
)
414 dst
->addr
= src
->addr
;
415 dst
->bb_addr
= src
->bb_addr
;
416 dst
->file
= src
->file
;
417 dst
->line
= src
->line
;
420 /* copy file position and init cost */
421 static void init_fcost(AddrCost
* c
, Addr addr
, Addr bbaddr
, file_node
* file
)
423 init_apos( &(c
->p
), addr
, bbaddr
, file
);
424 /* FIXME: This is a memory leak as a AddrCost is inited multiple times */
425 c
->cost
= CLG_(get_eventset_cost
)( CLG_(sets
).full
);
426 CLG_(init_cost
)( CLG_(sets
).full
, c
->cost
);
431 * print position change inside of a BB (last -> curr)
432 * this doesn't update last to curr!
434 static void fprint_apos(VgFile
*fp
, AddrPos
* curr
, AddrPos
* last
,
435 file_node
* func_file
)
437 CLG_ASSERT(curr
->file
!= 0);
438 CLG_DEBUG(2, " print_apos(file '%s', line %u, bb %#lx, addr %#lx) fnFile '%s'\n",
439 curr
->file
->name
, curr
->line
, curr
->bb_addr
, curr
->addr
,
442 if (curr
->file
!= last
->file
) {
444 /* if we switch back to orig file, use fe=... */
445 if (curr
->file
== func_file
)
446 print_file(fp
, "fe=", curr
->file
);
448 print_file(fp
, "fi=", curr
->file
);
451 if (CLG_(clo
).dump_bbs
) {
452 if (curr
->line
!= last
->line
) {
453 VG_(fprintf
)(fp
, "ln=%u\n", curr
->line
);
462 * This prints out differences if allowed
464 * This doesn't set last to curr afterwards!
467 void fprint_pos(VgFile
*fp
, const AddrPos
* curr
, const AddrPos
* last
)
469 if (0) //CLG_(clo).dump_bbs)
470 VG_(fprintf
)(fp
, "%lu ", curr
->addr
- curr
->bb_addr
);
472 if (CLG_(clo
).dump_instr
) {
473 int diff
= curr
->addr
- last
->addr
;
474 if ( CLG_(clo
).compress_pos
&& (last
->addr
>0) &&
475 (diff
> -100) && (diff
< 100)) {
477 VG_(fprintf
)(fp
, "+%d ", diff
);
479 VG_(fprintf
)(fp
, "* ");
481 VG_(fprintf
)(fp
, "%d ", diff
);
484 VG_(fprintf
)(fp
, "%#lx ", curr
->addr
);
487 if (CLG_(clo
).dump_bb
) {
488 int diff
= curr
->bb_addr
- last
->bb_addr
;
489 if ( CLG_(clo
).compress_pos
&& (last
->bb_addr
>0) &&
490 (diff
> -100) && (diff
< 100)) {
492 VG_(fprintf
)(fp
, "+%d ", diff
);
494 VG_(fprintf
)(fp
, "* ");
496 VG_(fprintf
)(fp
, "%d ", diff
);
499 VG_(fprintf
)(fp
, "%#lx ", curr
->bb_addr
);
502 if (CLG_(clo
).dump_line
) {
503 int diff
= curr
->line
- last
->line
;
504 if ( CLG_(clo
).compress_pos
&& (last
->line
>0) &&
505 (diff
> -100) && (diff
< 100)) {
508 VG_(fprintf
)(fp
, "+%d ", diff
);
510 VG_(fprintf
)(fp
, "* ");
512 VG_(fprintf
)(fp
, "%d ", diff
);
515 VG_(fprintf
)(fp
, "%u ", curr
->line
);
526 void fprint_cost(VgFile
*fp
, const EventMapping
* es
, const ULong
* cost
)
528 HChar
*mcost
= CLG_(mappingcost_as_string
)(es
, cost
);
529 VG_(fprintf
)(fp
, "%s\n", mcost
);
535 /* Write the cost of a source line; only that parts of the source
536 * position are written that changed relative to last written position.
537 * funcPos is the source position of the first line of actual function.
538 * Something is written only if cost != 0; returns True in this case.
540 static void fprint_fcost(VgFile
*fp
, AddrCost
* c
, AddrPos
* last
)
543 CLG_DEBUG(2, " print_fcost(file '%s', line %u, bb %#lx, addr %#lx):\n",
544 c
->p
.file
->name
, c
->p
.line
, c
->p
.bb_addr
, c
->p
.addr
);
545 CLG_(print_cost
)(-5, CLG_(sets
).full
, c
->cost
);
548 fprint_pos(fp
, &(c
->p
), last
);
549 copy_apos( last
, &(c
->p
) ); /* update last to current position */
551 fprint_cost(fp
, CLG_(dumpmap
), c
->cost
);
553 /* add cost to total */
554 CLG_(add_and_zero_cost
)( CLG_(sets
).full
, dump_total_cost
, c
->cost
);
558 /* Write out the calls from jcc (at pos)
560 static void fprint_jcc(VgFile
*fp
, jCC
* jcc
, AddrPos
* curr
, AddrPos
* last
,
563 static AddrPos target
;
568 CLG_DEBUG(2, " fprint_jcc (jkind %d)\n", (Int
)jcc
->jmpkind
);
569 CLG_(print_jcc
)(-10, jcc
);
572 CLG_ASSERT(jcc
->to
!=0);
573 CLG_ASSERT(jcc
->from
!=0);
575 if (!get_debug_pos(jcc
->to
, bb_addr(jcc
->to
->bb
), &target
)) {
576 /* if we don't have debug info, don't switch to file "???" */
577 target
.file
= last
->file
;
580 if ((jcc
->jmpkind
== jk_CondJump
) || (jcc
->jmpkind
== jk_Jump
)) {
582 /* this is a JCC for a followed conditional or boring jump. */
583 CLG_ASSERT(CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
));
585 /* objects among jumps should be the same.
586 * Otherwise this jump would have been changed to a call
589 CLG_ASSERT(jcc
->from
->bb
->obj
== jcc
->to
->bb
->obj
);
591 /* only print if target position info is useful */
592 if (!CLG_(clo
).dump_instr
&& !CLG_(clo
).dump_bb
&& target
.line
==0) {
593 jcc
->call_counter
= 0;
597 /* Different files/functions are possible e.g. with longjmp's
598 * which change the stack, and thus context
600 if (last
->file
!= target
.file
) {
601 print_file(fp
, "jfi=", target
.file
);
604 if (jcc
->from
->cxt
!= jcc
->to
->cxt
) {
605 if (CLG_(clo
).mangle_names
)
606 print_mangled_fn(fp
, "jfn",
607 jcc
->to
->cxt
, jcc
->to
->rec_index
);
609 print_fn(fp
, "jfn", jcc
->to
->cxt
->fn
[0]);
612 if (jcc
->jmpkind
== jk_CondJump
) {
613 /* format: jcnd=<followed>/<executions> <target> */
614 VG_(fprintf
)(fp
, "jcnd=%llu/%llu ",
615 jcc
->call_counter
, ecounter
);
618 /* format: jump=<jump count> <target> */
619 VG_(fprintf
)(fp
, "jump=%llu ",
623 fprint_pos(fp
, &target
, last
);
624 VG_(fprintf
)(fp
, "\n");
625 fprint_pos(fp
, curr
, last
);
626 VG_(fprintf
)(fp
, "\n");
628 jcc
->call_counter
= 0;
632 file
= jcc
->to
->cxt
->fn
[0]->file
;
633 obj
= jcc
->to
->bb
->obj
;
635 /* object of called position different to object of this function?*/
636 if (jcc
->from
->cxt
->fn
[0]->file
->obj
!= obj
) {
637 print_obj(fp
, "cob=", obj
);
640 /* file of called position different to current file? */
641 if (last
->file
!= file
) {
642 print_file(fp
, "cfi=", file
);
645 if (CLG_(clo
).mangle_names
)
646 print_mangled_fn(fp
, "cfn", jcc
->to
->cxt
, jcc
->to
->rec_index
);
648 print_fn(fp
, "cfn", jcc
->to
->cxt
->fn
[0]);
650 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)) {
651 VG_(fprintf
)(fp
, "calls=%llu ",
654 fprint_pos(fp
, &target
, last
);
655 VG_(fprintf
)(fp
, "\n");
656 fprint_pos(fp
, curr
, last
);
657 fprint_cost(fp
, CLG_(dumpmap
), jcc
->cost
);
659 CLG_(init_cost
)( CLG_(sets
).full
, jcc
->cost
);
661 jcc
->call_counter
= 0;
667 /* Cost summation of functions.We use alternately ccSum[0/1], thus
668 * ssSum[currSum] for recently read lines with same line number.
670 static AddrCost ccSum
[2];
674 * Print all costs of a BBCC:
675 * - FCCs of instructions
676 * - JCCs of the unique jump of this BB
677 * returns True if something was written
679 static Bool
fprint_bbcc(VgFile
*fp
, BBCC
* bbcc
, AddrPos
* last
)
681 InstrInfo
* instr_info
;
683 Bool something_written
= False
;
685 AddrCost
*currCost
, *newCost
;
686 Int jcc_count
= 0, instr
, i
, jmp
;
689 CLG_ASSERT(bbcc
->cxt
!= 0);
691 VG_(printf
)("+ fprint_bbcc (Instr %u): ", bb
->instr_count
);
692 CLG_(print_bbcc
)(15, bbcc
);
695 CLG_ASSERT(currSum
== 0 || currSum
== 1);
696 currCost
= &(ccSum
[currSum
]);
697 newCost
= &(ccSum
[1-currSum
]);
699 ecounter
= bbcc
->ecounter_sum
;
701 instr_info
= &(bb
->instr
[0]);
702 for(instr
=0; instr
<bb
->instr_count
; instr
++, instr_info
++) {
704 /* get debug info of current instruction address and dump cost
705 * if CLG_(clo).dump_bbs or file/line has changed
707 if (!get_debug_pos(bbcc
, bb_addr(bb
) + instr_info
->instr_offset
,
709 /* if we don't have debug info, don't switch to file "???" */
710 newCost
->p
.file
= bbcc
->cxt
->fn
[0]->file
;
713 if (CLG_(clo
).dump_bbs
|| CLG_(clo
).dump_instr
||
714 (newCost
->p
.line
!= currCost
->p
.line
) ||
715 (newCost
->p
.file
!= currCost
->p
.file
)) {
717 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
718 something_written
= True
;
720 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
721 fprint_fcost(fp
, currCost
, last
);
725 currSum
= 1 - currSum
;
726 currCost
= &(ccSum
[currSum
]);
727 newCost
= &(ccSum
[1-currSum
]);
730 /* add line cost to current cost sum */
731 (*CLG_(cachesim
).add_icost
)(currCost
->cost
, bbcc
, instr_info
, ecounter
);
733 /* print jcc's if there are: only jumps */
734 if (bb
->jmp
[jmp
].instr
== instr
) {
736 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
)
737 if (((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
738 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
742 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
743 /* no need to switch buffers, as position is the same */
744 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
745 fprint_fcost(fp
, currCost
, last
);
747 get_debug_pos(bbcc
, bb_addr(bb
)+instr_info
->instr_offset
, &(currCost
->p
));
748 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
749 something_written
= True
;
750 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
751 if (((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
752 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
753 fprint_jcc(fp
, jcc
, &(currCost
->p
), last
, ecounter
);
758 /* update execution counter */
759 if (jmp
< bb
->cjmp_count
)
760 if (bb
->jmp
[jmp
].instr
== instr
) {
761 ecounter
-= bbcc
->jmp
[jmp
].ecounter
;
766 /* jCCs at end? If yes, dump cumulated line info first */
768 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
769 /* yes, if JCC only counts jmp arcs or cost >0 */
770 if ( ((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
771 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
775 if ( (bbcc
->skipped
&&
776 !CLG_(is_zero_cost
)(CLG_(sets
).full
, bbcc
->skipped
)) ||
779 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
780 /* no need to switch buffers, as position is the same */
781 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
782 fprint_fcost(fp
, currCost
, last
);
785 get_debug_pos(bbcc
, bb_jmpaddr(bb
), &(currCost
->p
));
786 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
787 something_written
= True
;
789 /* first, print skipped costs for calls */
790 if (bbcc
->skipped
&& !CLG_(is_zero_cost
)( CLG_(sets
).full
,
792 CLG_(add_and_zero_cost
)( CLG_(sets
).full
,
793 currCost
->cost
, bbcc
->skipped
);
795 VG_(fprintf
)(fp
, "# Skipped\n");
797 fprint_fcost(fp
, currCost
, last
);
801 for(jcc
=bbcc
->jmp
[jmp
].jcc_list
; jcc
; jcc
=jcc
->next_from
) {
802 CLG_ASSERT(jcc
->jmp
== jmp
);
803 if ( ((jcc
->jmpkind
!= jk_Call
) && (jcc
->call_counter
>0)) ||
804 (!CLG_(is_zero_cost
)( CLG_(sets
).full
, jcc
->cost
)))
806 fprint_jcc(fp
, jcc
, &(currCost
->p
), last
, ecounter
);
810 if (CLG_(clo
).dump_bbs
|| CLG_(clo
).dump_bb
) {
811 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, currCost
->cost
)) {
812 something_written
= True
;
814 fprint_apos(fp
, &(currCost
->p
), last
, bbcc
->cxt
->fn
[0]->file
);
815 fprint_fcost(fp
, currCost
, last
);
817 if (CLG_(clo
).dump_bbs
) VG_(fprintf
)(fp
, "\n");
819 /* when every cost was immediately written, we must have done so,
820 * as this function is only called when there's cost in a BBCC
822 CLG_ASSERT(something_written
);
825 bbcc
->ecounter_sum
= 0;
826 for(i
=0; i
<=bbcc
->bb
->cjmp_count
; i
++)
827 bbcc
->jmp
[i
].ecounter
= 0;
828 bbcc
->ret_counter
= 0;
830 CLG_DEBUG(1, "- fprint_bbcc: JCCs %d\n", jcc_count
);
832 return something_written
;
837 * from->bb->obj, from->bb->fn
838 * obj, fn[0]->file, fn
841 static int my_cmp(BBCC
** pbbcc1
, BBCC
** pbbcc2
)
844 return (*pbbcc1
)->bb
->offset
- (*pbbcc2
)->bb
->offset
;
846 BBCC
*bbcc1
= *pbbcc1
;
847 BBCC
*bbcc2
= *pbbcc2
;
848 Context
* cxt1
= bbcc1
->cxt
;
849 Context
* cxt2
= bbcc2
->cxt
;
852 if (cxt1
->fn
[0]->file
->obj
!= cxt2
->fn
[0]->file
->obj
)
853 return cxt1
->fn
[0]->file
->obj
- cxt2
->fn
[0]->file
->obj
;
855 if (cxt1
->fn
[0]->file
!= cxt2
->fn
[0]->file
)
856 return cxt1
->fn
[0]->file
- cxt2
->fn
[0]->file
;
858 if (cxt1
->fn
[0] != cxt2
->fn
[0])
859 return cxt1
->fn
[0] - cxt2
->fn
[0];
861 if (bbcc1
->rec_index
!= bbcc2
->rec_index
)
862 return bbcc1
->rec_index
- bbcc2
->rec_index
;
864 while((off
< cxt1
->size
) && (off
< cxt2
->size
)) {
865 fn_node
* ffn1
= cxt1
->fn
[off
];
866 fn_node
* ffn2
= cxt2
->fn
[off
];
867 if (ffn1
->file
->obj
!= ffn2
->file
->obj
)
868 return ffn1
->file
->obj
- ffn2
->file
->obj
;
873 if (cxt1
->size
> cxt2
->size
) return 1;
874 else if (cxt1
->size
< cxt2
->size
) return -1;
876 return bbcc1
->bb
->offset
- bbcc2
->bb
->offset
;
884 /* modified version of:
886 * qsort -- qsort interface implemented by faster quicksort.
887 * J. L. Bentley and M. D. McIlroy, SPE 23 (1993) 1249-1265.
888 * Copyright 1993, John Wiley.
892 void swap(BBCC
** a
, BBCC
** b
)
895 t
= *a
; *a
= *b
; *b
= t
;
898 #define min(x, y) ((x)<=(y) ? (x) : (y))
901 BBCC
** med3(BBCC
**a
, BBCC
**b
, BBCC
**c
, int (*cmp
)(BBCC
**,BBCC
**))
902 { return cmp(a
, b
) < 0 ?
903 (cmp(b
, c
) < 0 ? b
: cmp(a
, c
) < 0 ? c
: a
)
904 : (cmp(b
, c
) > 0 ? b
: cmp(a
, c
) > 0 ? c
: a
);
907 static BBCC
** qsort_start
= 0;
909 static void CLG_(qsort
)(BBCC
**a
, int n
, int (*cmp
)(BBCC
**,BBCC
**))
911 BBCC
**pa
, **pb
, **pc
, **pd
, **pl
, **pm
, **pn
, **pv
;
915 CLG_DEBUG(8, " qsort(%ld,%ld)\n", a
-qsort_start
+ 0L, n
+ 0L);
917 if (n
< 7) { /* Insertion sort on smallest arrays */
918 for (pm
= a
+1; pm
< a
+n
; pm
++)
919 for (pl
= pm
; pl
> a
&& cmp(pl
-1, pl
) > 0; pl
--)
923 for (pm
= a
; pm
< a
+n
; pm
++) {
924 VG_(printf
)(" %3ld BB %#lx, ",
925 pm
- qsort_start
+ 0L,
927 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
932 pm
= a
+ n
/2; /* Small arrays, middle element */
936 if (n
> 40) { /* Big arrays, pseudomedian of 9 */
938 pl
= med3(pl
, pl
+s
, pl
+2*s
, cmp
);
939 pm
= med3(pm
-s
, pm
, pm
+s
, cmp
);
940 pn
= med3(pn
-2*s
, pn
-s
, pn
, cmp
);
942 pm
= med3(pl
, pm
, pn
, cmp
); /* Mid-size, med of 3 */
951 while ((pb
<= pc
) && ((r
=cmp(pb
, pv
)) <= 0)) {
953 /* same as pivot, to start */
958 while ((pb
<= pc
) && ((r
=cmp(pc
, pv
)) >= 0)) {
960 /* same as pivot, to end */
965 if (pb
> pc
) { break; }
973 /* put pivot from start into middle */
974 if ((s
= pa
-a
)>0) { for(r
=0;r
<s
;r
++) swap(a
+r
, pb
+1-s
+r
); }
975 /* put pivot from end into middle */
976 if ((s
= a
+n
-1-pd
)>0) { for(r
=0;r
<s
;r
++) swap(pc
+r
, a
+n
-s
+r
); }
979 VG_(printf
)(" PV BB %#lx, ", bb_addr((*pv
)->bb
));
980 CLG_(print_cxt
)(9, (*pv
)->cxt
, (*pv
)->rec_index
);
983 VG_(printf
)(" Lower %ld - %ld:\n",
985 a
+s
-1-qsort_start
+ 0L);
988 VG_(printf
)(" %3ld BB %#lx, ",
991 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
995 VG_(printf
)(" Upper %ld - %ld:\n",
996 a
+n
-s
-qsort_start
+ 0L,
997 a
+n
-1-qsort_start
+ 0L);
1000 VG_(printf
)(" %3ld BB %#lx, ",
1001 pm
-qsort_start
+ 0L,
1002 bb_addr((*pm
)->bb
));
1003 CLG_(print_cxt
)(9, (*pm
)->cxt
, (*pm
)->rec_index
);
1007 if ((s
= pb
+1-pa
) > 1) CLG_(qsort
)(a
, s
, cmp
);
1008 if ((s
= pd
+1-pc
) > 1) CLG_(qsort
)(a
+n
-s
, s
, cmp
);
1012 /* Helpers for prepare_dump */
1014 static Int prepare_count
;
1015 static BBCC
** prepare_ptr
;
1018 static void hash_addCount(BBCC
* bbcc
)
1020 if ((bbcc
->ecounter_sum
> 0) || (bbcc
->ret_counter
>0))
1024 static void hash_addPtr(BBCC
* bbcc
)
1026 if ((bbcc
->ecounter_sum
== 0) &&
1027 (bbcc
->ret_counter
== 0)) return;
1029 *prepare_ptr
= bbcc
;
1034 static void cs_addCount(thread_info
* ti
)
1039 /* add BBCCs with active call in call stack of current thread.
1040 * update cost sums for active calls
1043 for(i
= 0; i
< CLG_(current_call_stack
).sp
; i
++) {
1044 call_entry
* e
= &(CLG_(current_call_stack
).entry
[i
]);
1045 if (e
->jcc
== 0) continue;
1047 CLG_(add_diff_cost_lz
)( CLG_(sets
).full
, &(e
->jcc
->cost
),
1048 e
->enter_cost
, CLG_(current_state
).cost
);
1049 bbcc
= e
->jcc
->from
;
1051 CLG_DEBUG(1, " [%2d] (tid %u), added active: %s\n",
1052 i
,CLG_(current_tid
),bbcc
->cxt
->fn
[0]->name
);
1054 if (bbcc
->ecounter_sum
>0 || bbcc
->ret_counter
>0) {
1055 /* already counted */
1062 static void cs_addPtr(thread_info
* ti
)
1067 /* add BBCCs with active call in call stack of current thread.
1068 * update cost sums for active calls
1071 for(i
= 0; i
< CLG_(current_call_stack
).sp
; i
++) {
1072 call_entry
* e
= &(CLG_(current_call_stack
).entry
[i
]);
1073 if (e
->jcc
== 0) continue;
1075 bbcc
= e
->jcc
->from
;
1077 if (bbcc
->ecounter_sum
>0 || bbcc
->ret_counter
>0) {
1078 /* already counted */
1082 *prepare_ptr
= bbcc
;
1089 * Put all BBCCs with costs into a sorted array.
1090 * The returned arrays ends with a null pointer.
1091 * Must be freed after dumping.
1094 BBCC
** prepare_dump(void)
1100 /* if we do not separate among threads, this gives all */
1101 /* count number of BBCCs with >0 executions */
1102 CLG_(forall_bbccs
)(hash_addCount
);
1104 /* even if we do not separate among threads,
1105 * call stacks are separated */
1106 if (CLG_(clo
).separate_threads
)
1109 CLG_(forall_threads
)(cs_addCount
);
1111 CLG_DEBUG(0, "prepare_dump: %d BBCCs\n", prepare_count
);
1113 /* allocate bbcc array, insert BBCCs and sort */
1114 prepare_ptr
= array
=
1115 (BBCC
**) CLG_MALLOC("cl.dump.pd.1",
1116 (prepare_count
+1) * sizeof(BBCC
*));
1118 CLG_(forall_bbccs
)(hash_addPtr
);
1120 if (CLG_(clo
).separate_threads
)
1123 CLG_(forall_threads
)(cs_addPtr
);
1125 CLG_ASSERT(array
+ prepare_count
== prepare_ptr
);
1130 CLG_DEBUG(0," BBCCs inserted\n");
1132 qsort_start
= array
;
1133 CLG_(qsort
)(array
, prepare_count
, my_cmp
);
1135 CLG_DEBUG(0," BBCCs sorted\n");
1143 static void fprint_cost_ln(VgFile
*fp
, const HChar
* prefix
,
1144 const EventMapping
* em
, const ULong
* cost
)
1146 HChar
*mcost
= CLG_(mappingcost_as_string
)(em
, cost
);
1147 VG_(fprintf
)(fp
, "%s%s\n", prefix
, mcost
);
1151 static ULong bbs_done
= 0;
1152 static HChar
* filename
= 0;
1157 VG_(message
)(Vg_UserMsg
,
1158 "Error: can not open cache simulation output file `%s'\n",
1164 * Create a new dump file and write header.
1166 * Naming: <CLG_(clo).filename_base>.<pid>[.<part>][-<tid>]
1167 * <part> is skipped for final dump (trigger==0)
1168 * <tid> is skipped for thread 1 with CLG_(clo).separate_threads=no
1170 * Returns the file descriptor, and -1 on error (no write permission)
1172 static VgFile
*new_dumpfile(int tid
, const HChar
* trigger
)
1174 Bool appending
= False
;
1179 CLG_ASSERT(dumps_initialized
);
1180 CLG_ASSERT(filename
!= 0);
1182 if (!CLG_(clo
).combine_dumps
) {
1183 i
= VG_(sprintf
)(filename
, "%s", out_file
);
1186 i
+= VG_(sprintf
)(filename
+i
, ".%d", out_counter
);
1188 if (CLG_(clo
).separate_threads
)
1189 VG_(sprintf
)(filename
+i
, "-%02d", tid
);
1191 fp
= VG_(fopen
)(filename
, VKI_O_WRONLY
|VKI_O_TRUNC
, 0);
1194 VG_(sprintf
)(filename
, "%s", out_file
);
1195 fp
= VG_(fopen
)(filename
, VKI_O_WRONLY
|VKI_O_APPEND
, 0);
1196 if (fp
&& out_counter
>1)
1201 fp
= VG_(fopen
)(filename
, VKI_O_CREAT
|VKI_O_WRONLY
,
1202 VKI_S_IRUSR
|VKI_S_IWUSR
);
1204 /* If the file can not be opened for whatever reason (conflict
1205 between multiple supervised processes?), give up now. */
1210 CLG_DEBUG(2, " new_dumpfile '%s'\n", filename
);
1217 /* callgrind format specification, has to be on 1st line */
1218 VG_(fprintf
)(fp
, "# callgrind format\n");
1221 VG_(fprintf
)(fp
, "version: 1\n");
1224 VG_(fprintf
)(fp
, "creator: callgrind-" VERSION
"\n");
1227 VG_(fprintf
)(fp
, "pid: %d\n", VG_(getpid
)());
1230 VG_(fprintf
)(fp
, "cmd: %s", cmdbuf
);
1233 VG_(fprintf
)(fp
, "\npart: %d\n", out_counter
);
1234 if (CLG_(clo
).separate_threads
) {
1235 VG_(fprintf
)(fp
, "thread: %d\n", tid
);
1240 VG_(fprintf
)(fp
, "\n");
1243 /* Global options changing the tracing behaviour */
1244 VG_(fprintf
)(fp
, "\ndesc: Option: --skip-plt=%s\n",
1245 CLG_(clo
).skip_plt
? "yes" : "no");
1246 VG_(fprintf
)(fp
, "desc: Option: --collect-jumps=%s\n",
1247 CLG_(clo
).collect_jumps
? "yes" : "no");
1248 VG_(fprintf
)(fp
, "desc: Option: --separate-recs=%d\n",
1249 CLG_(clo
).separate_recursions
);
1250 VG_(fprintf
)(fp
, "desc: Option: --separate-callers=%d\n",
1251 CLG_(clo
).separate_callers
);
1253 VG_(fprintf
)(fp
, "desc: Option: --dump-bbs=%s\n",
1254 CLG_(clo
).dump_bbs
? "yes" : "no");
1255 VG_(fprintf
)(fp
, "desc: Option: --separate-threads=%s\n",
1256 CLG_(clo
).separate_threads
? "yes" : "no");
1259 (*CLG_(cachesim
).dump_desc
)(fp
);
1262 VG_(fprintf
)(fp
, "\ndesc: Timerange: Basic block %llu - %llu\n",
1263 bbs_done
, CLG_(stat
).bb_executions
);
1265 VG_(fprintf
)(fp
, "desc: Trigger: %s\n",
1266 trigger
? trigger
: "Program termination");
1269 /* Output function specific config
1271 for (i
= 0; i
< N_FNCONFIG_ENTRIES
; i
++) {
1275 VG_(fprintf
)(fp
, "desc: Option: --fn-skip=%s\n", fnc
->name
);
1277 if (fnc
->dump_at_enter
) {
1278 VG_(fprintf
)(fp
, "desc: Option: --fn-dump-at-enter=%s\n",
1281 if (fnc
->dump_at_leave
) {
1282 VG_(fprintf
)(fp
, "desc: Option: --fn-dump-at-leave=%s\n",
1285 if (fnc
->separate_callers
!= CLG_(clo
).separate_callers
) {
1286 VG_(fprintf
)(fp
, "desc: Option: --separate-callers%d=%s\n",
1287 fnc
->separate_callers
, fnc
->name
);
1289 if (fnc
->separate_recursions
!= CLG_(clo
).separate_recursions
) {
1290 VG_(fprintf
)(fp
, "desc: Option: --separate-recs%d=%s\n",
1291 fnc
->separate_recursions
, fnc
->name
);
1298 /* "positions:" line */
1299 VG_(fprintf
)(fp
, "\npositions:%s%s%s\n",
1300 CLG_(clo
).dump_instr
? " instr" : "",
1301 CLG_(clo
).dump_bb
? " bb" : "",
1302 CLG_(clo
).dump_line
? " line" : "");
1304 /* Some (optional) "event:" lines, giving long names to events. */
1305 switch (CLG_(clo
).collect_systime
) {
1306 case systime_no
: break;
1308 VG_(fprintf
)(fp
, "event: sysTime : sysTime (elapsed ms)\n");
1311 VG_(fprintf
)(fp
, "event: sysTime : sysTime (elapsed us)\n");
1314 VG_(fprintf
)(fp
, "event: sysTime : sysTime (elapsed ns)\n");
1315 VG_(fprintf
)(fp
, "event: sysCpuTime : sysCpuTime (system cpu ns)\n");
1322 Note: callgrind_annotate expects the "events:" line to be the last line
1323 of the PartData. In other words, this line is before the first line
1324 of the PartData body. */
1325 HChar
*evmap
= CLG_(eventmapping_as_string
)(CLG_(dumpmap
));
1326 VG_(fprintf
)(fp
, "events: %s\n", evmap
);
1330 sum
= CLG_(get_eventset_cost
)( CLG_(sets
).full
);
1331 CLG_(zero_cost
)(CLG_(sets
).full
, sum
);
1332 if (CLG_(clo
).separate_threads
) {
1333 thread_info
* ti
= CLG_(get_current_thread
)();
1334 CLG_(add_diff_cost
)(CLG_(sets
).full
, sum
, ti
->lastdump_cost
,
1335 ti
->states
.entry
[0]->cost
);
1338 /* This function is called once for thread 1, where
1339 * all costs are summed up when not dumping separate per thread.
1340 * But this is not true for summary: we need to add all threads.
1343 thread_info
** thr
= CLG_(get_threads
)();
1344 for(t
=1;t
<VG_N_THREADS
;t
++) {
1345 if (!thr
[t
]) continue;
1346 CLG_(add_diff_cost
)(CLG_(sets
).full
, sum
,
1347 thr
[t
]->lastdump_cost
,
1348 thr
[t
]->states
.entry
[0]->cost
);
1351 fprint_cost_ln(fp
, "summary: ", CLG_(dumpmap
), sum
);
1353 /* all dumped cost will be added to total_fcc */
1354 CLG_(init_cost_lz
)( CLG_(sets
).full
, &dump_total_cost
);
1356 VG_(fprintf
)(fp
, "\n\n");
1358 if (VG_(clo_verbosity
) > 1)
1359 VG_(message
)(Vg_DebugMsg
, "Dump to %s\n", filename
);
1365 static void close_dumpfile(VgFile
*fp
)
1367 if (fp
== NULL
) return;
1369 fprint_cost_ln(fp
, "totals: ", CLG_(dumpmap
),
1371 //fprint_fcc_ln(fp, "summary: ", &dump_total_fcc);
1372 CLG_(add_cost_lz
)(CLG_(sets
).full
,
1373 &CLG_(total_cost
), dump_total_cost
);
1377 if (filename
[0] == '.') {
1378 if (-1 == VG_(rename
) (filename
, filename
+1)) {
1379 /* Can not rename to correct file name: give out warning */
1380 VG_(message
)(Vg_DebugMsg
, "Warning: Can not rename .%s to %s\n",
1381 filename
, filename
);
1387 /* Helper for print_bbccs */
1389 static const HChar
* print_trigger
;
1391 static void print_bbccs_of_thread(thread_info
* ti
)
1397 CLG_DEBUG(1, "+ print_bbccs(tid %u)\n", CLG_(current_tid
));
1399 VgFile
*print_fp
= new_dumpfile(CLG_(current_tid
), print_trigger
);
1400 if (print_fp
== NULL
) {
1401 CLG_DEBUG(1, "- print_bbccs(tid %u): No output...\n", CLG_(current_tid
));
1405 p
= array
= prepare_dump();
1406 init_fpos(&lastFnPos
);
1407 init_apos(&lastAPos
, 0, 0, 0);
1411 /* on context/function change, print old cost buffer before */
1412 if (lastFnPos
.cxt
&& ((*p
==0) ||
1413 (lastFnPos
.cxt
!= (*p
)->cxt
) ||
1414 (lastFnPos
.rec_index
!= (*p
)->rec_index
))) {
1415 if (!CLG_(is_zero_cost
)( CLG_(sets
).full
, ccSum
[currSum
].cost
)) {
1416 /* no need to switch buffers, as position is the same */
1417 fprint_apos(print_fp
, &(ccSum
[currSum
].p
), &lastAPos
,
1418 lastFnPos
.cxt
->fn
[0]->file
);
1419 fprint_fcost(print_fp
, &ccSum
[currSum
], &lastAPos
);
1422 if (ccSum
[currSum
].p
.file
!= lastFnPos
.cxt
->fn
[0]->file
) {
1423 /* switch back to file of function */
1424 print_file(print_fp
, "fe=", lastFnPos
.cxt
->fn
[0]->file
);
1426 VG_(fprintf
)(print_fp
, "\n");
1431 if (print_fn_pos(print_fp
, &lastFnPos
, *p
)) {
1434 init_apos(&lastAPos
, 0, 0, (*p
)->cxt
->fn
[0]->file
);
1435 init_fcost(&ccSum
[0], 0, 0, 0);
1436 init_fcost(&ccSum
[1], 0, 0, 0);
1440 if (CLG_(clo
).dump_bbs
) {
1441 /* FIXME: Specify Object of BB if different to object of fn */
1443 ULong ecounter
= (*p
)->ecounter_sum
;
1444 VG_(fprintf
)(print_fp
, "bb=%#lx ", (UWord
)(*p
)->bb
->offset
);
1445 for(i
= 0; i
<(*p
)->bb
->cjmp_count
;i
++) {
1446 VG_(fprintf
)(print_fp
, "%u %llu ",
1447 (*p
)->bb
->jmp
[i
].instr
,
1449 ecounter
-= (*p
)->jmp
[i
].ecounter
;
1451 VG_(fprintf
)(print_fp
, "%u %llu\n",
1452 (*p
)->bb
->instr_count
,
1456 fprint_bbcc(print_fp
, *p
, &lastAPos
);
1461 close_dumpfile(print_fp
);
1464 /* set counters of last dump */
1465 CLG_(copy_cost
)( CLG_(sets
).full
, ti
->lastdump_cost
,
1466 CLG_(current_state
).cost
);
1468 CLG_DEBUG(1, "- print_bbccs(tid %u)\n", CLG_(current_tid
));
1472 static void print_bbccs(const HChar
* trigger
, Bool only_current_thread
)
1477 print_trigger
= trigger
;
1479 if (!CLG_(clo
).separate_threads
) {
1480 /* All BBCC/JCC costs is stored for thread 1 */
1481 Int orig_tid
= CLG_(current_tid
);
1483 CLG_(switch_thread
)(1);
1484 print_bbccs_of_thread( CLG_(get_current_thread
)() );
1485 CLG_(switch_thread
)(orig_tid
);
1487 else if (only_current_thread
)
1488 print_bbccs_of_thread( CLG_(get_current_thread
)() );
1490 CLG_(forall_threads
)(print_bbccs_of_thread
);
1496 void CLG_(dump_profile
)(const HChar
* trigger
, Bool only_current_thread
)
1498 CLG_DEBUG(2, "+ dump_profile(Trigger '%s')\n",
1499 trigger
? trigger
: "Prg.Term.");
1503 if (VG_(clo_verbosity
) > 1)
1504 VG_(message
)(Vg_DebugMsg
, "Start dumping at BB %llu (%s)...\n",
1505 CLG_(stat
).bb_executions
,
1506 trigger
? trigger
: "Prg.Term.");
1510 print_bbccs(trigger
, only_current_thread
);
1512 bbs_done
= CLG_(stat
).bb_executions
++;
1514 if (VG_(clo_verbosity
) > 1)
1515 VG_(message
)(Vg_DebugMsg
, "Dumping done.\n");
1518 /* Copy command to cmd buffer. We want to original command line
1519 * (can change at runtime)
1522 void init_cmdbuf(void)
1527 /* Pass #1: How many bytes do we need? */
1528 size
= 1; // leading ' '
1529 size
+= VG_(strlen
)( 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 size
+= 1; // separator ' '
1533 // escape NL in arguments to not break dump format
1534 for(j
=0; arg
[j
]; j
++)
1538 size
++; // fall through
1544 cmdbuf
= CLG_MALLOC("cl.dump.ic.1", size
+ 1); // +1 for '\0'
1546 /* Pass #2: Build up the string */
1547 size
= VG_(sprintf
)(cmdbuf
, " %s", VG_(args_the_exename
));
1549 for(i
= 0; i
< VG_(sizeXA
)( VG_(args_for_client
) ); i
++) {
1550 const HChar
*arg
= * (HChar
**) VG_(indexXA
)( VG_(args_for_client
), i
);
1551 cmdbuf
[size
++] = ' ';
1552 for(j
=0; arg
[j
]; j
++)
1555 cmdbuf
[size
++] = '\\';
1556 cmdbuf
[size
++] = 'n';
1559 cmdbuf
[size
++] = '\\';
1560 cmdbuf
[size
++] = '\\';
1563 cmdbuf
[size
++] = arg
[j
];
1567 cmdbuf
[size
] = '\0';
1571 * Set up file names for dump output: <out_file>.
1572 * <out_file> is derived from the output format string, which defaults
1573 * to "callgrind.out.%p", where %p is replaced with the PID.
1574 * For the final file name, on intermediate dumps a counter is appended,
1575 * and further, if separate dumps per thread are requested, the thread ID.
1577 * <out_file> always starts with a full absolute path.
1578 * If the output format string represents a relative path, the current
1579 * working directory at program start is used.
1581 * This function has to be called every time a profile dump is generated
1582 * to be able to react on PID changes.
1584 void CLG_(init_dumps
)()
1588 static int thisPID
= 0;
1589 int currentPID
= VG_(getpid
)();
1590 if (currentPID
== thisPID
) {
1591 /* already initialized, and no PID change */
1592 CLG_ASSERT(out_file
!= 0);
1595 thisPID
= currentPID
;
1597 if (!CLG_(clo
).out_format
)
1598 CLG_(clo
).out_format
= DEFAULT_OUTFORMAT
;
1600 /* If a file name was already set, clean up before */
1602 VG_(free
)(out_file
);
1603 VG_(free
)(filename
);
1607 // Setup output filename.
1609 VG_(expand_file_name
)("--callgrind-out-file", CLG_(clo
).out_format
);
1611 /* allocate space big enough for final filenames */
1612 filename
= (HChar
*) CLG_MALLOC("cl.dump.init_dumps.2",
1613 VG_(strlen
)(out_file
)+32);
1615 /* Make sure the output base file can be written.
1616 * This is used for the dump at program termination.
1617 * We stop with an error here if we can not create the
1618 * file: This is probably because of missing rights,
1619 * and trace parts wouldn't be allowed to be written, too.
1621 VG_(strcpy
)(filename
, out_file
);
1622 res
= VG_(open
)(filename
, VKI_O_WRONLY
|VKI_O_TRUNC
, 0);
1623 if (sr_isError(res
)) {
1624 res
= VG_(open
)(filename
, VKI_O_CREAT
|VKI_O_WRONLY
,
1625 VKI_S_IRUSR
|VKI_S_IWUSR
);
1626 if (sr_isError(res
)) {
1630 if (!sr_isError(res
)) VG_(close
)( (Int
)sr_Res(res
) );
1632 if (!dumps_initialized
)
1635 dumps_initialized
= True
;