blob: ae730c7c9cacec2e20862607fd47b6aff7cfe4f0 [file] [log] [blame]
weidendoa17f2a32006-03-20 10:27:30 +00001/*--------------------------------------------------------------------*/
2/*--- Callgrind ---*/
3/*--- dump.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Callgrind, a Valgrind tool for call tracing.
8
sewardj45057902006-06-05 23:27:18 +00009 Copyright (C) 2002-2006, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
weidendoa17f2a32006-03-20 10:27:30 +000010
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.
15
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.
20
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
24 02111-1307, USA.
25
26 The GNU General Public License is contained in the file COPYING.
27*/
28
29#include "config.h"
30#include "global.h"
31
32#include <pub_tool_threadstate.h>
33#include <pub_tool_libcfile.h>
34
35/*------------------------------------------------------------*/
36/*--- Support for signal handlers and multi-threading ---*/
37/*------------------------------------------------------------*/
38
39/* Dump Part Counter */
40static Int out_counter = 0;
41
42static Char* dump_file_base = 0;
43static Char* base_directory = 0;
44
45/* Command */
46static Char cmdbuf[BUF_LEN];
47
48/* Total reads/writes/misses sum over all dumps and threads.
49 * Updated during CC traversal at dump time.
50 */
51FullCost CLG_(total_cost) = 0;
52static FullCost dump_total_cost = 0;
53
54EventMapping* CLG_(dumpmap) = 0;
55
56/* Temporary output buffer for
57 * print_fn_pos, fprint_apos, fprint_fcost, fprint_jcc,
58 * fprint_fcc_ln, dump_run_info, dump_state_info
59 */
60static Char outbuf[FILENAME_LEN + FN_NAME_LEN + OBJ_NAME_LEN];
61
62Int CLG_(get_dump_counter)(void)
63{
64 return out_counter;
65}
66
67Char* CLG_(get_dump_file_base)()
68{
69 return dump_file_base;
70}
71
72/*------------------------------------------------------------*/
73/*--- Output file related stuff ---*/
74/*------------------------------------------------------------*/
75
76/* Boolean dumping array */
77static Bool* dump_array = 0;
78static Int dump_array_size = 0;
79static Bool* obj_dumped = 0;
80static Bool* file_dumped = 0;
81static Bool* fn_dumped = 0;
82static Bool* cxt_dumped = 0;
83
84static
85void reset_dump_array(void)
86{
87 int i;
88
89 CLG_ASSERT(dump_array != 0);
90
91 for(i=0;i<dump_array_size;i++)
92 dump_array[i] = False;
93}
94
95static
96void init_dump_array(void)
97{
98 dump_array_size = CLG_(stat).distinct_objs +
99 CLG_(stat).distinct_files +
100 CLG_(stat).distinct_fns +
101 CLG_(stat).context_counter;
102 CLG_ASSERT(dump_array == 0);
103 dump_array = (Bool*) CLG_MALLOC(dump_array_size * sizeof(Bool));
104 obj_dumped = dump_array;
105 file_dumped = obj_dumped + CLG_(stat).distinct_objs;
106 fn_dumped = file_dumped + CLG_(stat).distinct_files;
107 cxt_dumped = fn_dumped + CLG_(stat).distinct_fns;
108
109 reset_dump_array();
110
111 CLG_DEBUG(1, " init_dump_array: size %d\n", dump_array_size);
112}
113
114static __inline__
115void free_dump_array(void)
116{
117 CLG_ASSERT(dump_array != 0);
118 VG_(free)(dump_array);
119
120 dump_array = 0;
121 obj_dumped = 0;
122 file_dumped = 0;
123 fn_dumped = 0;
124 cxt_dumped = 0;
125}
126
127
128/* Initialize to an invalid position */
129static __inline__
130void init_fpos(FnPos* p)
131 {
132 p->file = 0;
133 p->fn = 0;
134 p->obj = 0;
135 p->cxt = 0;
136 p->rec_index = 0;
137}
138
139
140#if 0
141static __inline__
142static void my_fwrite(Int fd, Char* buf, Int len)
143{
144 VG_(write)(fd, (void*)buf, len);
145}
146#else
147
148#define FWRITE_BUFSIZE 32000
149#define FWRITE_THROUGH 10000
150static Char fwrite_buf[FWRITE_BUFSIZE];
151static Int fwrite_pos;
152static Int fwrite_fd = -1;
153
154static __inline__
155void fwrite_flush(void)
156{
157 if ((fwrite_fd>=0) && (fwrite_pos>0))
158 VG_(write)(fwrite_fd, (void*)fwrite_buf, fwrite_pos);
159 fwrite_pos = 0;
160}
161
162static void my_fwrite(Int fd, Char* buf, Int len)
163{
164 if (fwrite_fd != fd) {
165 fwrite_flush();
166 fwrite_fd = fd;
167 }
168 if (len > FWRITE_THROUGH) {
169 fwrite_flush();
170 VG_(write)(fd, (void*)buf, len);
171 return;
172 }
173 if (FWRITE_BUFSIZE - fwrite_pos <= len) fwrite_flush();
174 VG_(strncpy)(fwrite_buf + fwrite_pos, buf, len);
175 fwrite_pos += len;
176}
177#endif
178
179
180static void print_obj(Char* buf, obj_node* obj)
181{
182 int n;
183
184 if (CLG_(clo).compress_strings) {
185 CLG_ASSERT(obj_dumped != 0);
186 if (obj_dumped[obj->number])
187 n = VG_(sprintf)(buf, "(%d)\n", obj->number);
188 else {
189 n = VG_(sprintf)(buf, "(%d) %s\n",
190 obj->number, obj->name);
191 }
192 }
193 else
194 n = VG_(sprintf)(buf, "%s\n", obj->name);
195
196#if 0
197 /* add mapping parameters the first time a object is dumped
198 * format: mp=0xSTART SIZE 0xOFFSET */
199 if (!obj_dumped[obj->number]) {
200 obj_dumped[obj->number];
201 VG_(sprintf)(buf+n, "mp=%p %p %p\n",
202 pos->obj->start, pos->obj->size, pos->obj->offset);
203 }
204#else
205 obj_dumped[obj->number] = True;
206#endif
207}
208
209static void print_file(Char* buf, file_node* file)
210{
211 if (CLG_(clo).compress_strings) {
212 CLG_ASSERT(file_dumped != 0);
213 if (file_dumped[file->number])
214 VG_(sprintf)(buf, "(%d)\n", file->number);
215 else {
216 VG_(sprintf)(buf, "(%d) %s\n",
217 file->number, file->name);
218 file_dumped[file->number] = True;
219 }
220 }
221 else
222 VG_(sprintf)(buf, "%s\n", file->name);
223}
224
225/*
226 * tag can be "fn", "cfn", "jfn"
227 */
228static void print_fn(Int fd, Char* buf, Char* tag, fn_node* fn)
229{
230 int p;
231 p = VG_(sprintf)(buf, "%s=",tag);
232 if (CLG_(clo).compress_strings) {
233 CLG_ASSERT(fn_dumped != 0);
234 if (fn_dumped[fn->number])
235 p += VG_(sprintf)(buf+p, "(%d)\n", fn->number);
236 else {
237 p += VG_(sprintf)(buf+p, "(%d) %s\n",
238 fn->number, fn->name);
239 fn_dumped[fn->number] = True;
240 }
241 }
242 else
243 p += VG_(sprintf)(buf+p, "%s\n", fn->name);
244
245 my_fwrite(fd, buf, p);
246}
247
248static void print_mangled_fn(Int fd, Char* buf, Char* tag,
249 Context* cxt, int rec_index)
250{
251 int p, i;
252
253 if (CLG_(clo).compress_strings && CLG_(clo).compress_mangled) {
254
255 int n;
256 Context* last;
257
258 CLG_ASSERT(cxt_dumped != 0);
259 if (cxt_dumped[cxt->base_number+rec_index]) {
260 p = VG_(sprintf)(buf, "%s=(%d)\n",
261 tag, cxt->base_number + rec_index);
262 my_fwrite(fd, buf, p);
263 return;
264 }
265
266 last = 0;
267 /* make sure that for all context parts compressed data is written */
268 for(i=cxt->size;i>0;i--) {
269 CLG_ASSERT(cxt->fn[i-1]->pure_cxt != 0);
270 n = cxt->fn[i-1]->pure_cxt->base_number;
271 if (cxt_dumped[n]) continue;
272 p = VG_(sprintf)(buf, "%s=(%d) %s\n",
273 tag, n, cxt->fn[i-1]->name);
274 my_fwrite(fd, buf, p);
275
276 cxt_dumped[n] = True;
277 last = cxt->fn[i-1]->pure_cxt;
278 }
279 /* If the last context was the context to print, we are finished */
280 if ((last == cxt) && (rec_index == 0)) return;
281
282 p = VG_(sprintf)(buf, "%s=(%d) (%d)", tag,
283 cxt->base_number + rec_index,
284 cxt->fn[0]->pure_cxt->base_number);
285 if (rec_index >0)
286 p += VG_(sprintf)(buf+p, "'%d", rec_index +1);
287 for(i=1;i<cxt->size;i++)
288 p += VG_(sprintf)(buf+p, "'(%d)",
289 cxt->fn[i]->pure_cxt->base_number);
290 p += VG_(sprintf)(buf+p, "\n");
291 my_fwrite(fd, buf, p);
292
293 cxt_dumped[cxt->base_number+rec_index] = True;
294 return;
295 }
296
297
298 p = VG_(sprintf)(buf, "%s=", tag);
299 if (CLG_(clo).compress_strings) {
300 CLG_ASSERT(cxt_dumped != 0);
301 if (cxt_dumped[cxt->base_number+rec_index]) {
302 p += VG_(sprintf)(buf+p, "(%d)\n", cxt->base_number + rec_index);
303 my_fwrite(fd, buf, p);
304 return;
305 }
306 else {
307 p += VG_(sprintf)(buf+p, "(%d) ", cxt->base_number + rec_index);
308 cxt_dumped[cxt->base_number+rec_index] = True;
309 }
310 }
311
312 p += VG_(sprintf)(buf+p, "%s", cxt->fn[0]->name);
313 if (rec_index >0)
314 p += VG_(sprintf)(buf+p, "'%d", rec_index +1);
315 for(i=1;i<cxt->size;i++)
316 p += VG_(sprintf)(buf+p, "'%s", cxt->fn[i]->name);
317
318 p += VG_(sprintf)(buf+p, "\n");
319 my_fwrite(fd, buf, p);
320}
321
322
323
324/**
325 * Print function position of the BBCC, but only print info differing to
326 * the <last> position, update <last>
327 * Return True if something changes.
328 */
329static Bool print_fn_pos(int fd, FnPos* last, BBCC* bbcc)
330{
331 Bool res = False;
332
333 CLG_DEBUGIF(3) {
334 CLG_DEBUG(2, "+ print_fn_pos: ");
335 CLG_(print_cxt)(16, bbcc->cxt, bbcc->rec_index);
336 }
337
338 if (!CLG_(clo).mangle_names) {
339 if (last->rec_index != bbcc->rec_index) {
340 VG_(sprintf)(outbuf, "rec=%d\n\n", bbcc->rec_index);
341 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
342 last->rec_index = bbcc->rec_index;
343 last->cxt = 0; /* reprint context */
344 res = True;
345 }
346
347 if (last->cxt != bbcc->cxt) {
348 fn_node* last_from = (last->cxt && last->cxt->size>1) ?
349 last->cxt->fn[1] : 0;
350 fn_node* curr_from = (bbcc->cxt && bbcc->cxt->size>1) ?
351 bbcc->cxt->fn[1] : 0;
352 if (curr_from == 0) {
353 if (last_from != 0) {
354 /* switch back to no context */
355 VG_(sprintf)(outbuf, "frfn=(spontaneous)\n");
356 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
357 res = True;
358 }
359 }
360 else if (last_from != curr_from) {
361 print_fn(fd,outbuf,"frfn", curr_from);
362 res = True;
363 }
364 last->cxt = bbcc->cxt;
365 }
366 }
367
368 if (last->obj != bbcc->cxt->fn[0]->file->obj) {
369 VG_(sprintf)(outbuf, "ob=");
370 print_obj(outbuf+3, bbcc->cxt->fn[0]->file->obj);
371 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
372 last->obj = bbcc->cxt->fn[0]->file->obj;
373 res = True;
374 }
375
376 if (last->file != bbcc->cxt->fn[0]->file) {
377 VG_(sprintf)(outbuf, "fl=");
378 print_file(outbuf+3, bbcc->cxt->fn[0]->file);
379 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
380 last->file = bbcc->cxt->fn[0]->file;
381 res = True;
382 }
383
384 if (!CLG_(clo).mangle_names) {
385 if (last->fn != bbcc->cxt->fn[0]) {
386 print_fn(fd,outbuf, "fn", bbcc->cxt->fn[0]);
387 last->fn = bbcc->cxt->fn[0];
388 res = True;
389 }
390 }
391 else {
392 /* Print mangled name if context or rec_index changes */
393 if ((last->rec_index != bbcc->rec_index) ||
394 (last->cxt != bbcc->cxt)) {
395
396 print_mangled_fn(fd, outbuf, "fn", bbcc->cxt, bbcc->rec_index);
397 last->fn = bbcc->cxt->fn[0];
398 last->rec_index = bbcc->rec_index;
399 res = True;
400 }
401 }
402
403 last->cxt = bbcc->cxt;
404
405 CLG_DEBUG(2, "- print_fn_pos: %s\n", res ? "changed" : "");
406
407 return res;
408}
409
410/* the debug lookup cache is useful if BBCC for same BB are
411 * dumped directly in a row. This is a direct mapped cache.
412 */
413#define DEBUG_CACHE_SIZE 1777
414
415static Addr debug_cache_addr[DEBUG_CACHE_SIZE];
416static file_node* debug_cache_file[DEBUG_CACHE_SIZE];
417static int debug_cache_line[DEBUG_CACHE_SIZE];
418static Bool debug_cache_info[DEBUG_CACHE_SIZE];
419
420static __inline__
421void init_debug_cache(void)
422{
423 int i;
424 for(i=0;i<DEBUG_CACHE_SIZE;i++) {
425 debug_cache_addr[i] = 0;
426 debug_cache_file[i] = 0;
427 debug_cache_line[i] = 0;
428 debug_cache_info[i] = 0;
429 }
430}
431
432static __inline__
433Bool get_debug_pos(BBCC* bbcc, Addr addr, AddrPos* p)
434{
435 Char file[FILENAME_LEN];
436 Bool res;
437
438 int cachepos = addr % DEBUG_CACHE_SIZE;
439
440 if (debug_cache_addr[cachepos] == addr) {
441 p->line = debug_cache_line[cachepos];
442 p->file = debug_cache_file[cachepos];
443 res = debug_cache_info[cachepos];
444 }
445 else {
446 res = VG_(get_filename_linenum)(addr,
447 file, FILENAME_LEN,
448 NULL, 0, NULL, //FIXME
449 &(p->line));
450 if (!res) {
451 VG_(strcpy)(file, "???");
452 p->line = 0;
453 }
454 p->file = CLG_(get_file_node)(bbcc->bb->obj, file);
455
456 debug_cache_info[cachepos] = res;
457 debug_cache_addr[cachepos] = addr;
458 debug_cache_line[cachepos] = p->line;
459 debug_cache_file[cachepos] = p->file;
460 }
461
462 /* Address offset from bbcc start address */
463 p->addr = addr - bbcc->bb->obj->offset;
464 p->bb_addr = bbcc->bb->offset;
465
466 CLG_DEBUG(3, " get_debug_pos(%p): BB %p, fn '%s', file '%s', line %u\n",
467 addr, bb_addr(bbcc->bb), bbcc->cxt->fn[0]->name,
468 p->file->name, p->line);
469
470 return res;
471}
472
473
474/* copy file position and init cost */
475static void init_apos(AddrPos* p, Addr addr, Addr bbaddr, file_node* file)
476{
477 p->addr = addr;
478 p->bb_addr = bbaddr;
479 p->file = file;
480 p->line = 0;
481}
482
483static void copy_apos(AddrPos* dst, AddrPos* src)
484{
485 dst->addr = src->addr;
486 dst->bb_addr = src->bb_addr;
487 dst->file = src->file;
488 dst->line = src->line;
489}
490
491/* copy file position and init cost */
492static void init_fcost(AddrCost* c, Addr addr, Addr bbaddr, file_node* file)
493{
494 init_apos( &(c->p), addr, bbaddr, file);
495 /* FIXME: This is a memory leak as a AddrCost is inited multiple times */
496 c->cost = CLG_(get_eventset_cost)( CLG_(sets).full );
497 CLG_(init_cost)( CLG_(sets).full, c->cost );
498}
499
500
501/**
502 * print position change inside of a BB (last -> curr)
503 * this doesn't update last to curr!
504 */
505static void fprint_apos(Int fd, AddrPos* curr, AddrPos* last, file_node* func_file)
506{
507 CLG_ASSERT(curr->file != 0);
508 CLG_DEBUG(2, " print_apos(file '%s', line %d, bb %p, addr %p) fnFile '%s'\n",
509 curr->file->name, curr->line, curr->bb_addr, curr->addr,
510 func_file->name);
511
512 if (curr->file != last->file) {
513
514 /* if we switch back to orig file, use fe=... */
515 if (curr->file == func_file)
516 VG_(sprintf)(outbuf, "fe=");
517 else
518 VG_(sprintf)(outbuf, "fi=");
519 print_file(outbuf+3, curr->file);
520 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
521 }
522
523 if (CLG_(clo).dump_bbs) {
524 if (curr->line != last->line) {
525 VG_(sprintf)(outbuf, "ln=%d\n", curr->line);
526 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
527 }
528 }
529}
530
531
532
533/**
534 * Print a position.
535 * This prints out differences if allowed
536 *
537 * This doesn't set last to curr afterwards!
538 */
539static
540void fprint_pos(Int fd, AddrPos* curr, AddrPos* last)
541{
542 if (0) //CLG_(clo).dump_bbs)
543 VG_(sprintf)(outbuf, "%u ", curr->addr - curr->bb_addr);
544 else {
545 int p = 0;
546 if (CLG_(clo).dump_instr) {
547 int diff = curr->addr - last->addr;
548 if ( CLG_(clo).compress_pos && (last->addr >0) &&
549 (diff > -100) && (diff < 100)) {
550 if (diff >0)
551 p = VG_(sprintf)(outbuf, "+%d ", diff);
552 else if (diff==0)
553 p = VG_(sprintf)(outbuf, "* ");
554 else
555 p = VG_(sprintf)(outbuf, "%d ", diff);
556 }
557 else
558 p = VG_(sprintf)(outbuf, "%p ", curr->addr);
559 }
560
561 if (CLG_(clo).dump_bb) {
562 int diff = curr->bb_addr - last->bb_addr;
563 if ( CLG_(clo).compress_pos && (last->bb_addr >0) &&
564 (diff > -100) && (diff < 100)) {
565 if (diff >0)
566 p += VG_(sprintf)(outbuf+p, "+%d ", diff);
567 else if (diff==0)
568 p += VG_(sprintf)(outbuf+p, "* ");
569 else
570 p += VG_(sprintf)(outbuf+p, "%d ", diff);
571 }
572 else
573 p += VG_(sprintf)(outbuf+p, "%p ", curr->bb_addr);
574 }
575
576 if (CLG_(clo).dump_line) {
577 int diff = curr->line - last->line;
578 if ( CLG_(clo).compress_pos && (last->line >0) &&
579 (diff > -100) && (diff < 100)) {
580
581 if (diff >0)
582 VG_(sprintf)(outbuf+p, "+%d ", diff);
583 else if (diff==0)
584 VG_(sprintf)(outbuf+p, "* ");
585 else
586 VG_(sprintf)(outbuf+p, "%d ", diff);
587 }
588 else
589 VG_(sprintf)(outbuf+p, "%u ", curr->line);
590 }
591 }
592 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
593}
594
595
596/**
597 * Print events.
598 */
599
600static
601void fprint_cost(int fd, EventMapping* es, ULong* cost)
602{
603 int p = CLG_(sprint_mappingcost)(outbuf, es, cost);
604 VG_(sprintf)(outbuf+p, "\n");
605 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
606 return;
607}
608
609
610
611/* Write the cost of a source line; only that parts of the source
612 * position are written that changed relative to last written position.
613 * funcPos is the source position of the first line of actual function.
614 * Something is written only if cost != 0; returns True in this case.
615 */
616static void fprint_fcost(Int fd, AddrCost* c, AddrPos* last)
617{
618 CLG_DEBUGIF(3) {
619 CLG_DEBUG(2, " print_fcost(file '%s', line %d, bb %p, addr %p):\n",
620 c->p.file->name, c->p.line, c->p.bb_addr, c->p.addr);
621 CLG_(print_cost)(-5, CLG_(sets).full, c->cost);
622 }
623
624 fprint_pos(fd, &(c->p), last);
625 copy_apos( last, &(c->p) ); /* update last to current position */
626
627 fprint_cost(fd, CLG_(dumpmap), c->cost);
628
629 /* add cost to total */
630 CLG_(add_and_zero_cost)( CLG_(sets).full, dump_total_cost, c->cost );
631}
632
633
634/* Write out the calls from jcc (at pos)
635 */
636static void fprint_jcc(Int fd, jCC* jcc, AddrPos* curr, AddrPos* last, ULong ecounter)
637{
638 static AddrPos target;
639 file_node* file;
640 obj_node* obj;
641
642 CLG_DEBUGIF(2) {
643 CLG_DEBUG(2, " fprint_jcc (jkind %d)\n", jcc->jmpkind);
644 CLG_(print_jcc)(-10, jcc);
645 }
646
647 if (!get_debug_pos(jcc->to, bb_addr(jcc->to->bb), &target)) {
648 /* if we don't have debug info, don't switch to file "???" */
649 target.file = last->file;
650 }
651
652 if (jcc->from &&
653 (jcc->jmpkind == JmpCond || jcc->jmpkind == Ijk_Boring)) {
654
655 /* this is a JCC for a followed conditional or boring jump. */
656 CLG_ASSERT(CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost));
657
658 /* objects among jumps should be the same.
659 * Otherwise this jump would have been changed to a call
660 * (see setup_bbcc)
661 */
662 CLG_ASSERT(jcc->from->bb->obj == jcc->to->bb->obj);
663
664 /* only print if target position info is usefull */
665 if (!CLG_(clo).dump_instr && !CLG_(clo).dump_bb && target.line==0) {
666 jcc->call_counter = 0;
667 return;
668 }
669
670 /* Different files/functions are possible e.g. with longjmp's
671 * which change the stack, and thus context
672 */
673 if (last->file != target.file) {
674 VG_(sprintf)(outbuf, "jfi=");
675 print_file(outbuf+4, target.file);
676 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
677 }
678
679 if (jcc->from->cxt != jcc->to->cxt) {
680 if (CLG_(clo).mangle_names)
681 print_mangled_fn(fd, outbuf, "jfn",
682 jcc->to->cxt, jcc->to->rec_index);
683 else
684 print_fn(fd, outbuf, "jfn", jcc->to->cxt->fn[0]);
685 }
686
687 if (jcc->jmpkind == JmpCond) {
688 /* format: jcnd=<followed>/<executions> <target> */
689 VG_(sprintf)(outbuf, "jcnd=%llu/%llu ",
690 jcc->call_counter, ecounter);
691 }
692 else {
693 /* format: jump=<jump count> <target> */
694 VG_(sprintf)(outbuf, "jump=%llu ",
695 jcc->call_counter);
696 }
697 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
698
699 fprint_pos(fd, &target, last);
700 my_fwrite(fd, "\n", 1);
701 fprint_pos(fd, curr, last);
702 my_fwrite(fd, "\n", 1);
703
704 jcc->call_counter = 0;
705 return;
706 }
707
708 CLG_ASSERT(jcc->to !=0);
709
710 file = jcc->to->cxt->fn[0]->file;
711 obj = jcc->to->bb->obj;
712
713 /* object of called position different to object of this function?*/
714 if (jcc->from->cxt->fn[0]->file->obj != obj) {
715 VG_(sprintf)(outbuf, "cob=");
716 print_obj(outbuf+4, obj);
717 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
718 }
719
720 /* file of called position different to current file? */
721 if (last->file != file) {
722 VG_(sprintf)(outbuf, "cfi=");
723 print_file(outbuf+4, file);
724 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
725 }
726
727 if (CLG_(clo).mangle_names)
728 print_mangled_fn(fd, outbuf, "cfn", jcc->to->cxt, jcc->to->rec_index);
729 else
730 print_fn(fd, outbuf, "cfn", jcc->to->cxt->fn[0]);
731
732 if (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost)) {
733 VG_(sprintf)(outbuf, "calls=%llu ",
734 jcc->call_counter);
735 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
736
737 fprint_pos(fd, &target, last);
738 my_fwrite(fd, "\n", 1);
739 fprint_pos(fd, curr, last);
740 fprint_cost(fd, CLG_(dumpmap), jcc->cost);
741
742 CLG_(init_cost)( CLG_(sets).full, jcc->cost );
743
744 jcc->call_counter = 0;
745 }
746}
747
748
749
750/* Cost summation of functions.We use alternately ccSum[0/1], thus
751 * ssSum[currSum] for recently read lines with same line number.
752 */
753static AddrCost ccSum[2];
754static int currSum;
755
756/*
757 * Print all costs of a BBCC:
758 * - FCCs of instructions
759 * - JCCs of the unique jump of this BB
760 * returns True if something was written
761 */
762static Bool fprint_bbcc(Int fd, BBCC* bbcc, AddrPos* last)
763{
764 InstrInfo* instr_info;
765 ULong ecounter;
766 Bool something_written = False;
767 jCC* jcc;
768 AddrCost *currCost, *newCost;
769 Int jcc_count = 0, instr, i, jmp;
770 BB* bb = bbcc->bb;
771
772 CLG_ASSERT(bbcc->cxt != 0);
773 CLG_DEBUGIF(1) {
774 VG_(printf)("+ fprint_bbcc (Instr %d): ", bb->instr_count);
775 CLG_(print_bbcc)(15, bbcc, False);
776 }
777
778 CLG_ASSERT(currSum == 0 || currSum == 1);
779 currCost = &(ccSum[currSum]);
780 newCost = &(ccSum[1-currSum]);
781
782 ecounter = bbcc->ecounter_sum;
783 jmp = 0;
784 instr_info = &(bb->instr[0]);
785 for(instr=0; instr<bb->instr_count; instr++, instr_info++) {
786
787 /* get debug info of current instruction address and dump cost
788 * if CLG_(clo).dump_bbs or file/line has changed
789 */
790 if (!get_debug_pos(bbcc, bb_addr(bb) + instr_info->instr_offset,
791 &(newCost->p))) {
792 /* if we don't have debug info, don't switch to file "???" */
793 newCost->p.file = bbcc->cxt->fn[0]->file;
794 }
795
796 if (CLG_(clo).dump_bbs || CLG_(clo).dump_instr ||
797 (newCost->p.line != currCost->p.line) ||
798 (newCost->p.file != currCost->p.file)) {
799
800 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
801 something_written = True;
802
803 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
804 fprint_fcost(fd, currCost, last);
805 }
806
807 /* switch buffers */
808 currSum = 1 - currSum;
809 currCost = &(ccSum[currSum]);
810 newCost = &(ccSum[1-currSum]);
811 }
812
813 /* add line cost to current cost sum */
814 (*CLG_(cachesim).add_icost)(currCost->cost, bbcc, instr_info, ecounter);
815
816 /* print jcc's if there are: only jumps */
817 if (bb->jmp[jmp].instr == instr) {
818 jcc_count=0;
819 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from)
820 if ((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0))
821 jcc_count++;
822
823 if (jcc_count>0) {
824 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
825 /* no need to switch buffers, as position is the same */
826 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
827 fprint_fcost(fd, currCost, last);
828 }
829 get_debug_pos(bbcc, bb_addr(bb)+instr_info->instr_offset, &(currCost->p));
830 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
831 something_written = True;
832 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
833 if ((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0))
834 fprint_jcc(fd, jcc, &(currCost->p), last, ecounter);
835 }
836 }
837 }
838
839 /* update execution counter */
840 if (jmp < bb->cjmp_count)
841 if (bb->jmp[jmp].instr == instr) {
842 ecounter -= bbcc->jmp[jmp].ecounter;
843 jmp++;
844 }
845 }
846
847 /* jCCs at end? If yes, dump cumulated line info first */
848 jcc_count = 0;
849 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
850 /* yes, if JCC only counts jmp arcs or cost >0 */
851 if ( ((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
852 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
853 jcc_count++;
854 }
855
856 if ( (bbcc->skipped &&
857 !CLG_(is_zero_cost)(CLG_(sets).full, bbcc->skipped)) ||
858 (jcc_count>0) ) {
859
860 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
861 /* no need to switch buffers, as position is the same */
862 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
863 fprint_fcost(fd, currCost, last);
864 }
865
866 get_debug_pos(bbcc, bb_jmpaddr(bb), &(currCost->p));
867 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
868 something_written = True;
869
870 /* first, print skipped costs for calls */
871 if (bbcc->skipped && !CLG_(is_zero_cost)( CLG_(sets).full,
872 bbcc->skipped )) {
873 CLG_(add_and_zero_cost)( CLG_(sets).full,
874 currCost->cost, bbcc->skipped );
875#if 0
876 VG_(sprintf)(outbuf, "# Skipped\n");
877 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
878#endif
879 fprint_fcost(fd, currCost, last);
880 }
881
882 if (jcc_count > 0)
883 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
884 CLG_ASSERT(jcc->jmp == jmp);
885 if ( ((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
886 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
887
888 fprint_jcc(fd, jcc, &(currCost->p), last, ecounter);
889 }
890 }
891
892 if (CLG_(clo).dump_bbs || CLG_(clo).dump_bb) {
893 if (!CLG_(is_zero_cost)( CLG_(sets).full, currCost->cost )) {
894 something_written = True;
895
896 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
897 fprint_fcost(fd, currCost, last);
898 }
899 if (CLG_(clo).dump_bbs) my_fwrite(fd, (void*)"\n", 1);
900
901 /* when every cost was immediatly written, we must have done so,
902 * as this function is only called when there's cost in a BBCC
903 */
904 CLG_ASSERT(something_written);
905 }
906
907 bbcc->ecounter_sum = 0;
908 for(i=0; i<=bbcc->bb->cjmp_count; i++)
909 bbcc->jmp[i].ecounter = 0;
910 bbcc->ret_counter = 0;
911
912 CLG_DEBUG(1, "- fprint_bbcc: JCCs %d\n", jcc_count);
913
914 return something_written;
915}
916
917/* order by
918 * recursion,
919 * from->bb->obj, from->bb->fn
920 * obj, fn[0]->file, fn
921 * address
922 */
923static int my_cmp(BBCC** pbbcc1, BBCC** pbbcc2)
924{
925#if 0
926 return (*pbbcc1)->bb->offset - (*pbbcc2)->bb->offset;
927#else
928 BBCC *bbcc1 = *pbbcc1;
929 BBCC *bbcc2 = *pbbcc2;
930 Context* cxt1 = bbcc1->cxt;
931 Context* cxt2 = bbcc2->cxt;
932 int off = 1;
933
934 if (cxt1->fn[0]->file->obj != cxt2->fn[0]->file->obj)
935 return cxt1->fn[0]->file->obj - cxt2->fn[0]->file->obj;
936
937 if (cxt1->fn[0]->file != cxt2->fn[0]->file)
938 return cxt1->fn[0]->file - cxt2->fn[0]->file;
939
940 if (cxt1->fn[0] != cxt2->fn[0])
941 return cxt1->fn[0] - cxt2->fn[0];
942
943 if (bbcc1->rec_index != bbcc2->rec_index)
944 return bbcc1->rec_index - bbcc2->rec_index;
945
946 while((off < cxt1->size) && (off < cxt2->size)) {
947 fn_node* ffn1 = cxt1->fn[off];
948 fn_node* ffn2 = cxt2->fn[off];
949 if (ffn1->file->obj != ffn2->file->obj)
950 return ffn1->file->obj - ffn2->file->obj;
951 if (ffn1 != ffn2)
952 return ffn1 - ffn2;
953 off++;
954 }
955 if (cxt1->size > cxt2->size) return 1;
956 else if (cxt1->size < cxt2->size) return -1;
957
958 return bbcc1->bb->offset - bbcc2->bb->offset;
959#endif
960}
961
962
963
964
965
966/* modified version of:
967 *
968 * qsort -- qsort interface implemented by faster quicksort.
969 * J. L. Bentley and M. D. McIlroy, SPE 23 (1993) 1249-1265.
970 * Copyright 1993, John Wiley.
971*/
972
973static __inline__
974void swapfunc(BBCC** a, BBCC** b, int n)
975{
976 while(n>0) {
977 BBCC* t = *a; *a = *b; *b = t;
978 a++, b++;
979 n--;
980 }
981}
982
983static __inline__
984void swap(BBCC** a, BBCC** b)
985{
986 BBCC* t;
987 t = *a; *a = *b; *b = t;
988}
989
990#define min(x, y) ((x)<=(y) ? (x) : (y))
991
992static
993BBCC** med3(BBCC **a, BBCC **b, BBCC **c, int (*cmp)(BBCC**,BBCC**))
994{ return cmp(a, b) < 0 ?
995 (cmp(b, c) < 0 ? b : cmp(a, c) < 0 ? c : a)
996 : (cmp(b, c) > 0 ? b : cmp(a, c) > 0 ? c : a);
997}
998
999static BBCC** qsort_start = 0;
1000
1001static void qsort(BBCC **a, int n, int (*cmp)(BBCC**,BBCC**))
1002{
1003 BBCC **pa, **pb, **pc, **pd, **pl, **pm, **pn, **pv;
1004 int s, r;
1005 BBCC* v;
1006
1007 CLG_DEBUG(8, " qsort(%d,%d)\n", a-qsort_start, n);
1008
1009 if (n < 7) { /* Insertion sort on smallest arrays */
1010 for (pm = a+1; pm < a+n; pm++)
1011 for (pl = pm; pl > a && cmp(pl-1, pl) > 0; pl --)
1012 swap(pl, pl-1);
1013
1014 CLG_DEBUGIF(8) {
1015 for (pm = a; pm < a+n; pm++) {
1016 VG_(printf)(" %3d BB %p, ", pm - qsort_start,
1017 bb_addr((*pm)->bb));
1018 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
1019 }
1020 }
1021 return;
1022 }
1023 pm = a + n/2; /* Small arrays, middle element */
1024 if (n > 7) {
1025 pl = a;
1026 pn = a + (n-1);
1027 if (n > 40) { /* Big arrays, pseudomedian of 9 */
1028 s = n/8;
1029 pl = med3(pl, pl+s, pl+2*s, cmp);
1030 pm = med3(pm-s, pm, pm+s, cmp);
1031 pn = med3(pn-2*s, pn-s, pn, cmp);
1032 }
1033 pm = med3(pl, pm, pn, cmp); /* Mid-size, med of 3 */
1034 }
1035
1036
1037 v = *pm;
1038 pv = &v;
1039 pa = pb = a;
1040 pc = pd = a + (n-1);
1041 for (;;) {
1042 while ((pb <= pc) && ((r=cmp(pb, pv)) <= 0)) {
1043 if (r==0) {
1044 /* same as pivot, to start */
1045 swap(pa,pb); pa++;
1046 }
1047 pb ++;
1048 }
1049 while ((pb <= pc) && ((r=cmp(pc, pv)) >= 0)) {
1050 if (r==0) {
1051 /* same as pivot, to end */
1052 swap(pc,pd); pd--;
1053 }
1054 pc --;
1055 }
1056 if (pb > pc) { break; }
1057 swap(pb, pc);
1058 pb ++;
1059 pc --;
1060 }
1061 pb--;
1062 pc++;
1063
1064 /* put pivot from start into middle */
1065 if ((s = pa-a)>0) { for(r=0;r<s;r++) swap(a+r, pb+1-s+r); }
1066 /* put pivot from end into middle */
1067 if ((s = a+n-1-pd)>0) { for(r=0;r<s;r++) swap(pc+r, a+n-s+r); }
1068
1069 CLG_DEBUGIF(8) {
1070 VG_(printf)(" PV BB %p, ", bb_addr((*pv)->bb));
1071 CLG_(print_cxt)(9, (*pv)->cxt, (*pv)->rec_index);
1072
1073 s = pb-pa+1;
1074 VG_(printf)(" Lower %d - %d:\n", a-qsort_start, a+s-1-qsort_start);
1075 for (r=0;r<s;r++) {
1076 pm = a+r;
1077 VG_(printf)(" %3d BB %p, ",
1078 pm-qsort_start,bb_addr((*pm)->bb));
1079 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
1080 }
1081
1082 s = pd-pc+1;
1083 VG_(printf)(" Upper %d - %d:\n",
1084 a+n-s-qsort_start, a+n-1-qsort_start);
1085 for (r=0;r<s;r++) {
1086 pm = a+n-s+r;
1087 VG_(printf)(" %3d BB %p, ",
1088 pm-qsort_start,bb_addr((*pm)->bb));
1089 CLG_(print_cxt)(9, (*pm)->cxt, (*pm)->rec_index);
1090 }
1091 }
1092
1093 if ((s = pb+1-pa) > 1) qsort(a, s, cmp);
1094 if ((s = pd+1-pc) > 1) qsort(a+n-s, s, cmp);
1095}
1096
1097
1098/* Helpers for prepare_dump */
1099
1100static Int prepare_count;
1101static BBCC** prepare_ptr;
1102
1103
1104static void hash_addCount(BBCC* bbcc)
1105{
1106 if ((bbcc->ecounter_sum > 0) || (bbcc->ret_counter>0))
1107 prepare_count++;
1108}
1109
1110static void hash_addPtr(BBCC* bbcc)
1111{
1112 if ((bbcc->ecounter_sum == 0) &&
1113 (bbcc->ret_counter == 0)) return;
1114
1115 *prepare_ptr = bbcc;
1116 prepare_ptr++;
1117}
1118
1119
1120static void cs_addCount(thread_info* ti)
1121{
1122 Int i;
1123 BBCC* bbcc;
1124
1125 /* add BBCCs with active call in call stack of current thread.
1126 * update cost sums for active calls
1127 */
1128
1129 for(i = 0; i < CLG_(current_call_stack).sp; i++) {
1130 call_entry* e = &(CLG_(current_call_stack).entry[i]);
1131 if (e->jcc == 0) continue;
1132
1133 CLG_(add_diff_cost_lz)( CLG_(sets).full, &(e->jcc->cost),
1134 e->enter_cost, CLG_(current_state).cost);
1135 bbcc = e->jcc->from;
1136
1137 CLG_DEBUG(1, " [%2d] (tid %d), added active: %s\n",
1138 i,CLG_(current_tid),bbcc->cxt->fn[0]->name);
1139
1140 if (bbcc->ecounter_sum>0 || bbcc->ret_counter>0) {
1141 /* already counted */
1142 continue;
1143 }
1144 prepare_count++;
1145 }
1146}
1147
1148static void cs_addPtr(thread_info* ti)
1149{
1150 Int i;
1151 BBCC* bbcc;
1152
1153 /* add BBCCs with active call in call stack of current thread.
1154 * update cost sums for active calls
1155 */
1156
1157 for(i = 0; i < CLG_(current_call_stack).sp; i++) {
1158 call_entry* e = &(CLG_(current_call_stack).entry[i]);
1159 if (e->jcc == 0) continue;
1160
1161 bbcc = e->jcc->from;
1162
1163 if (bbcc->ecounter_sum>0 || bbcc->ret_counter>0) {
1164 /* already counted */
1165 continue;
1166 }
1167
1168 *prepare_ptr = bbcc;
1169 prepare_ptr++;
1170 }
1171}
1172
1173
1174/**
1175 * Put all BBCCs with costs into a sorted array.
1176 * The returned arrays ends with a null pointer.
1177 * Must be freed after dumping.
1178 */
1179static
1180BBCC** prepare_dump(void)
1181{
1182 BBCC **array;
1183
1184 prepare_count = 0;
1185
1186 /* if we do not separate among threads, this gives all */
1187 /* count number of BBCCs with >0 executions */
1188 CLG_(forall_bbccs)(hash_addCount);
1189
1190 /* even if we do not separate among threads,
1191 * call stacks are separated */
1192 if (CLG_(clo).separate_threads)
1193 cs_addCount(0);
1194 else
1195 CLG_(forall_threads)(cs_addCount);
1196
1197 CLG_DEBUG(0, "prepare_dump: %d BBCCs\n", prepare_count);
1198
1199 /* allocate bbcc array, insert BBCCs and sort */
1200 prepare_ptr = array =
1201 (BBCC**) CLG_MALLOC((prepare_count+1) * sizeof(BBCC*));
1202
1203 CLG_(forall_bbccs)(hash_addPtr);
1204
1205 if (CLG_(clo).separate_threads)
1206 cs_addPtr(0);
1207 else
1208 CLG_(forall_threads)(cs_addPtr);
1209
1210 CLG_ASSERT(array + prepare_count == prepare_ptr);
1211
1212 /* end mark */
1213 *prepare_ptr = 0;
1214
1215 CLG_DEBUG(0," BBCCs inserted\n");
1216
1217 qsort_start = array;
1218 qsort(array, prepare_count, my_cmp);
1219
1220 CLG_DEBUG(0," BBCCs sorted\n");
1221
1222 return array;
1223}
1224
1225
1226
1227
1228static void fprint_cost_ln(int fd, Char* prefix,
1229 EventMapping* em, ULong* cost)
1230{
1231 int p;
1232
1233 p = VG_(sprintf)(outbuf, "%s", prefix);
1234 p += CLG_(sprint_mappingcost)(outbuf + p, em, cost);
1235 VG_(sprintf)(outbuf + p, "\n");
1236 my_fwrite(fd, (void*)outbuf, VG_(strlen)(outbuf));
1237}
1238
1239static ULong bbs_done = 0;
1240static Char* filename = 0;
1241
1242static
1243void file_err(void)
1244{
1245 VG_(message)(Vg_UserMsg,
1246 "Error: can not open cache simulation output file `%s'",
1247 filename );
1248 VG_(exit)(1);
1249}
1250
1251/**
1252 * Create a new dump file and write header.
1253 *
1254 * Naming: <CLG_(clo).filename_base>.<pid>[.<part>][-<tid>]
1255 * <part> is skipped for final dump (trigger==0)
1256 * <tid> is skipped for thread 1 with CLG_(clo).separate_threads=no
1257 *
1258 * Returns the file descriptor, and -1 on error (no write permission)
1259 */
1260static int new_dumpfile(Char buf[BUF_LEN], int tid, Char* trigger)
1261{
1262 Bool appending = False;
1263 int i, fd;
1264 FullCost sum = 0;
1265 SysRes res;
1266
1267 CLG_ASSERT(filename != 0);
1268
1269 if (!CLG_(clo).combine_dumps) {
1270 i = VG_(sprintf)(filename, "%s.%d", dump_file_base, VG_(getpid)());
1271
1272 if (trigger)
1273 i += VG_(sprintf)(filename+i, ".%d", out_counter);
1274
1275 if (CLG_(clo).separate_threads)
1276 i += VG_(sprintf)(filename+i, "-%02d", tid);
1277
1278 res = VG_(open)(filename, VKI_O_WRONLY|VKI_O_TRUNC, 0);
1279 }
1280 else {
1281 VG_(sprintf)(filename, "%s.%d", dump_file_base, VG_(getpid)());
1282 res = VG_(open)(filename, VKI_O_WRONLY|VKI_O_APPEND, 0);
1283 if (!res.isError && out_counter>1)
1284 appending = True;
1285 }
1286
1287 if (res.isError) {
1288 res = VG_(open)(filename, VKI_O_CREAT|VKI_O_WRONLY,
1289 VKI_S_IRUSR|VKI_S_IWUSR);
1290 if (res.isError) {
1291 /* If the file can not be opened for whatever reason (conflict
1292 between multiple supervised processes?), give up now. */
1293 file_err();
1294 }
1295 }
1296 fd = (Int) res.val;
1297
1298 CLG_DEBUG(2, " new_dumpfile '%s'\n", filename);
1299
1300 if (!appending)
1301 reset_dump_array();
1302
1303
1304 if (!appending) {
1305 /* version */
1306 VG_(sprintf)(buf, "version: 1\n");
1307 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1308
1309 /* creator */
1310 VG_(sprintf)(buf, "creator: callgrind-" VERSION "\n");
1311 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1312
1313 /* "pid:" line */
1314 VG_(sprintf)(buf, "pid: %d\n", VG_(getpid)());
1315 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1316
1317 /* "cmd:" line */
1318 VG_(strcpy)(buf, "cmd: ");
1319 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1320 my_fwrite(fd, (void*)cmdbuf, VG_(strlen)(cmdbuf));
1321 }
1322
1323 VG_(sprintf)(buf, "\npart: %d\n", out_counter);
1324 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1325 if (CLG_(clo).separate_threads) {
1326 VG_(sprintf)(buf, "thread: %d\n", tid);
1327 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1328 }
1329
1330 /* "desc:" lines */
1331 if (!appending) {
1332 my_fwrite(fd, "\n", 1);
1333
1334#if 0
1335 /* Global options changing the tracing behaviour */
1336 VG_(sprintf)(buf, "\ndesc: Option: --skip-plt=%s\n",
1337 CLG_(clo).skip_plt ? "yes" : "no");
1338 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1339 VG_(sprintf)(buf, "desc: Option: --collect-jumps=%s\n",
1340 CLG_(clo).collect_jumps ? "yes" : "no");
1341 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1342 VG_(sprintf)(buf, "desc: Option: --separate-recs=%d\n",
1343 CLG_(clo).separate_recursions);
1344 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1345 VG_(sprintf)(buf, "desc: Option: --separate-callers=%d\n",
1346 CLG_(clo).separate_callers);
1347 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1348
1349 VG_(sprintf)(buf, "desc: Option: --dump-bbs=%s\n",
1350 CLG_(clo).dump_bbs ? "yes" : "no");
1351 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1352 VG_(sprintf)(buf, "desc: Option: --separate-threads=%s\n",
1353 CLG_(clo).separate_threads ? "yes" : "no");
1354 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1355#endif
1356
1357 (*CLG_(cachesim).getdesc)(buf);
1358 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1359 }
1360
1361 VG_(sprintf)(buf, "\ndesc: Timerange: Basic block %llu - %llu\n",
1362 bbs_done, CLG_(stat).bb_executions);
1363
1364 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1365 VG_(sprintf)(buf, "desc: Trigger: %s\n",
1366 trigger ? trigger : (Char*)"Program termination");
1367 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1368
1369#if 0
1370 /* Output function specific config
1371 * FIXME */
1372 for (i = 0; i < N_FNCONFIG_ENTRIES; i++) {
1373 fnc = fnc_table[i];
1374 while (fnc) {
1375 if (fnc->skip) {
1376 VG_(sprintf)(buf, "desc: Option: --fn-skip=%s\n", fnc->name);
1377 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1378 }
1379 if (fnc->dump_at_enter) {
1380 VG_(sprintf)(buf, "desc: Option: --fn-dump-at-enter=%s\n",
1381 fnc->name);
1382 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1383 }
1384 if (fnc->dump_at_leave) {
1385 VG_(sprintf)(buf, "desc: Option: --fn-dump-at-leave=%s\n",
1386 fnc->name);
1387 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1388 }
1389 if (fnc->separate_callers != CLG_(clo).separate_callers) {
1390 VG_(sprintf)(buf, "desc: Option: --separate-callers%d=%s\n",
1391 fnc->separate_callers, fnc->name);
1392 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1393 }
1394 if (fnc->separate_recursions != CLG_(clo).separate_recursions) {
1395 VG_(sprintf)(buf, "desc: Option: --separate-recs%d=%s\n",
1396 fnc->separate_recursions, fnc->name);
1397 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1398 }
1399 fnc = fnc->next;
1400 }
1401 }
1402#endif
1403
1404 /* "positions:" line */
1405 VG_(sprintf)(buf, "\npositions:%s%s%s\n",
1406 CLG_(clo).dump_instr ? " instr" : "",
1407 CLG_(clo).dump_bb ? " bb" : "",
1408 CLG_(clo).dump_line ? " line" : "");
1409 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1410
1411 /* "events:" line */
1412 i = VG_(sprintf)(buf, "events: ");
1413 CLG_(sprint_eventmapping)(buf+i, CLG_(dumpmap));
1414 my_fwrite(fd, (void*)buf, VG_(strlen)(buf));
1415 my_fwrite(fd, "\n", 1);
1416
1417 /* summary lines */
1418 sum = CLG_(get_eventset_cost)( CLG_(sets).full );
1419 CLG_(zero_cost)(CLG_(sets).full, sum);
1420 if (CLG_(clo).separate_threads) {
1421 thread_info* ti = CLG_(get_current_thread)();
1422 CLG_(add_diff_cost)(CLG_(sets).full, sum, ti->lastdump_cost,
1423 ti->states.entry[0]->cost);
1424 }
1425 else {
1426 /* This function is called once for thread 1, where
1427 * all costs are summed up when not dumping separate per thread.
1428 * But this is not true for summary: we need to add all threads.
1429 */
1430 int t;
1431 thread_info** thr = CLG_(get_threads)();
1432 for(t=1;t<VG_N_THREADS;t++) {
1433 if (!thr[t]) continue;
1434 CLG_(add_diff_cost)(CLG_(sets).full, sum,
1435 thr[t]->lastdump_cost,
1436 thr[t]->states.entry[0]->cost);
1437 }
1438 }
1439 fprint_cost_ln(fd, "summary: ", CLG_(dumpmap), sum);
1440
1441 /* all dumped cost will be added to total_fcc */
1442 CLG_(init_cost_lz)( CLG_(sets).full, &dump_total_cost );
1443
1444 my_fwrite(fd, "\n\n",2);
1445
1446 if (VG_(clo_verbosity) > 1)
1447 VG_(message)(Vg_DebugMsg, "Dump to %s", filename);
1448
1449 return fd;
1450}
1451
1452
1453static void close_dumpfile(Char buf[BUF_LEN], int fd, int tid)
1454{
1455 if (fd <0) return;
1456
1457 fprint_cost_ln(fd, "totals: ", CLG_(dumpmap),
1458 dump_total_cost);
1459 //fprint_fcc_ln(fd, "summary: ", &dump_total_fcc);
1460 CLG_(add_cost_lz)(CLG_(sets).full,
1461 &CLG_(total_cost), dump_total_cost);
1462
1463 fwrite_flush();
1464 VG_(close)(fd);
1465
1466 if (filename[0] == '.') {
1467 if (-1 == VG_(rename) (filename, filename+1)) {
1468 /* Can not rename to correct file name: give out warning */
1469 VG_(message)(Vg_DebugMsg, "Warning: Can not rename .%s to %s",
1470 filename, filename);
1471 }
1472 }
1473}
1474
1475
1476/* Helper for print_bbccs */
1477
1478static Int print_fd;
1479static Char* print_trigger;
1480static Char print_buf[BUF_LEN];
1481
1482static void print_bbccs_of_thread(thread_info* ti)
1483{
1484 BBCC **p, **array;
1485 FnPos lastFnPos;
1486 AddrPos lastAPos;
1487
1488 CLG_DEBUG(1, "+ print_bbccs(tid %d)\n", CLG_(current_tid));
1489
1490 print_fd = new_dumpfile(print_buf, CLG_(current_tid), print_trigger);
1491 if (print_fd <0) {
1492 CLG_DEBUG(1, "- print_bbccs(tid %d): No output...\n", CLG_(current_tid));
1493 return;
1494 }
1495
1496 p = array = prepare_dump();
1497 init_fpos(&lastFnPos);
1498 init_apos(&lastAPos, 0, 0, 0);
1499
1500 if (p) while(1) {
1501
1502 /* on context/function change, print old cost buffer before */
1503 if (lastFnPos.cxt && ((*p==0) ||
1504 (lastFnPos.cxt != (*p)->cxt) ||
1505 (lastFnPos.rec_index != (*p)->rec_index))) {
1506 if (!CLG_(is_zero_cost)( CLG_(sets).full, ccSum[currSum].cost )) {
1507 /* no need to switch buffers, as position is the same */
1508 fprint_apos(print_fd, &(ccSum[currSum].p), &lastAPos,
1509 lastFnPos.cxt->fn[0]->file);
1510 fprint_fcost(print_fd, &ccSum[currSum], &lastAPos);
1511 }
1512
1513 if (ccSum[currSum].p.file != lastFnPos.cxt->fn[0]->file) {
1514 /* switch back to file of function */
1515 VG_(sprintf)(print_buf, "fe=");
1516 print_file(print_buf+3, lastFnPos.cxt->fn[0]->file);
1517 my_fwrite(print_fd, (void*)print_buf, VG_(strlen)(print_buf));
1518 }
1519 my_fwrite(print_fd, "\n", 1);
1520 }
1521
1522 if (*p == 0) break;
1523
1524 if (print_fn_pos(print_fd, &lastFnPos, *p)) {
1525
1526 /* new function */
1527 init_apos(&lastAPos, 0, 0, (*p)->cxt->fn[0]->file);
1528 init_fcost(&ccSum[0], 0, 0, 0);
1529 init_fcost(&ccSum[1], 0, 0, 0);
1530 currSum = 0;
1531 }
1532
1533 if (CLG_(clo).dump_bbs) {
1534 /* FIXME: Specify Object of BB if different to object of fn */
1535 int i, pos = 0;
1536 ULong ecounter = (*p)->ecounter_sum;
1537 pos = VG_(sprintf)(print_buf, "bb=%p ", (*p)->bb->offset);
1538 for(i = 0; i<(*p)->bb->cjmp_count;i++) {
1539 pos += VG_(sprintf)(print_buf+pos, "%d %llu ",
1540 (*p)->bb->jmp[i].instr,
1541 ecounter);
1542 ecounter -= (*p)->jmp[i].ecounter;
1543 }
1544 VG_(sprintf)(print_buf+pos, "%d %llu\n",
1545 (*p)->bb->instr_count,
1546 ecounter);
1547 my_fwrite(print_fd, (void*)print_buf, VG_(strlen)(print_buf));
1548 }
1549
1550 fprint_bbcc(print_fd, *p, &lastAPos);
1551
1552 p++;
1553 }
1554
1555 close_dumpfile(print_buf, print_fd, CLG_(current_tid));
1556 if (array) VG_(free)(array);
1557
1558 /* set counters of last dump */
1559 CLG_(copy_cost)( CLG_(sets).full, ti->lastdump_cost,
1560 CLG_(current_state).cost );
1561
1562 CLG_DEBUG(1, "- print_bbccs(tid %d)\n", CLG_(current_tid));
1563}
1564
1565
1566static void print_bbccs(Char* trigger, Bool only_current_thread)
1567{
1568 init_dump_array();
1569 init_debug_cache();
1570
1571 print_fd = -1;
1572 print_trigger = trigger;
1573
1574 if (!CLG_(clo).separate_threads) {
1575 /* All BBCC/JCC costs is stored for thread 1 */
1576 Int orig_tid = CLG_(current_tid);
1577
1578 CLG_(switch_thread)(1);
1579 print_bbccs_of_thread( CLG_(get_current_thread)() );
1580 CLG_(switch_thread)(orig_tid);
1581 }
1582 else if (only_current_thread)
1583 print_bbccs_of_thread( CLG_(get_current_thread)() );
1584 else
1585 CLG_(forall_threads)(print_bbccs_of_thread);
1586
1587 free_dump_array();
1588}
1589
1590
1591void CLG_(dump_profile)(Char* trigger, Bool only_current_thread)
1592{
1593 CLG_DEBUG(2, "+ dump_profile(Trigger '%s')\n",
1594 trigger ? trigger : (Char*)"Prg.Term.");
1595
1596 if (VG_(clo_verbosity) > 1)
1597 VG_(message)(Vg_DebugMsg, "Start dumping at BB %llu (%s)...",
1598 CLG_(stat).bb_executions,
1599 trigger ? trigger : (Char*)"Prg.Term.");
1600
1601 out_counter++;
1602
1603 print_bbccs(trigger, only_current_thread);
1604
1605
1606 bbs_done = CLG_(stat).bb_executions++;
1607
1608 if (VG_(clo_verbosity) > 1)
1609 VG_(message)(Vg_DebugMsg, "Dumping done.");
1610}
1611
1612/* copy command to cmd buffer (could change) */
1613static
1614void init_cmdbuf(void)
1615{
1616 Int i,j,size = 0;
1617 HChar* argv;
1618
1619#if VG_CORE_INTERFACE_VERSION > 8
1620 if (VG_(args_the_exename))
1621 size = VG_(sprintf)(cmdbuf, " %s", VG_(args_the_exename));
1622
1623 for(i = 0; i < VG_(args_for_client).used; i++) {
1624 argv = VG_(args_for_client).strs[i];
1625 if (!argv) continue;
1626 if ((size>0) && (size < BUF_LEN)) cmdbuf[size++] = ' ';
1627 for(j=0;argv[j]!=0;j++)
1628 if (size < BUF_LEN) cmdbuf[size++] = argv[j];
1629 }
1630#else
1631 for(i = 0; i < VG_(client_argc); i++) {
1632 argv = VG_(client_argv[i]);
1633 if (!argv) continue;
1634 if ((size>0) && (size < BUF_LEN)) cmdbuf[size++] = ' ';
1635 for(j=0;argv[j]!=0;j++)
1636 if (size < BUF_LEN) cmdbuf[size++] = argv[j];
1637 }
1638#endif
1639
1640 if (size == BUF_LEN) size--;
1641 cmdbuf[size] = 0;
1642}
1643
1644void CLG_(init_files)(Char** dir, Char** file)
1645{
1646 Int size;
1647 SysRes res;
1648
1649 if (!CLG_(clo).filename_base)
1650 CLG_(clo).filename_base = DEFAULT_DUMPNAME;
1651
1652 /* get base directory for dump/command/result files */
1653 if (CLG_(clo).filename_base[0] == '/') {
1654 int lastSlash = 0, i =1;
1655 while(CLG_(clo).filename_base[i]) {
1656 for(; CLG_(clo).filename_base[i] &&
1657 CLG_(clo).filename_base[i] != '/'; i++);
1658 if (CLG_(clo).filename_base[i] != '/') break;
1659 lastSlash = i;
1660 i++;
1661 }
weidendo6bb08252006-04-21 01:02:13 +00001662 i = lastSlash;
weidendoa17f2a32006-03-20 10:27:30 +00001663 base_directory = (Char*) CLG_MALLOC(i+1);
1664 VG_(strncpy)(base_directory, CLG_(clo).filename_base, i);
1665 base_directory[i] = 0;
1666
1667 dump_file_base = CLG_(clo).filename_base;
1668 }
1669 else {
1670 size = 100;
1671 base_directory = 0;
1672
1673 /* getcwd() fails if the buffer isn't big enough -- keep doubling size
1674 until it succeeds. */
1675 while (NULL == base_directory) {
1676 base_directory = CLG_MALLOC(size);
1677 if (!VG_(getcwd)(base_directory, size)) {
1678 VG_(free)(base_directory);
1679 base_directory = 0;
1680 size *= 2;
1681 }
1682 }
1683
1684 size = VG_(strlen)(base_directory) + VG_(strlen)(CLG_(clo).filename_base) +2;
1685 dump_file_base = (Char*) CLG_MALLOC(size);
1686 CLG_ASSERT(dump_file_base != 0);
1687 VG_(sprintf)(dump_file_base, "%s/%s",
1688 base_directory, CLG_(clo).filename_base);
1689 }
1690
1691 /* allocate space big enough for final filenames */
1692 filename = (Char*) CLG_MALLOC(VG_(strlen)(dump_file_base)+32);
1693 CLG_ASSERT(filename != 0);
1694
1695 /* Make sure the output base file can be written.
1696 * This is used for the dump at program termination.
1697 * We stop with an error here if we can not create the
1698 * file: This is probably because of missing rights,
1699 * and trace parts wouldn't be allowed to be written, too.
1700 */
1701 VG_(sprintf)(filename, "%s.%d", dump_file_base, VG_(getpid)());
1702 res = VG_(open)(filename, VKI_O_WRONLY|VKI_O_TRUNC, 0);
1703 if (res.isError) {
1704 res = VG_(open)(filename, VKI_O_CREAT|VKI_O_WRONLY,
1705 VKI_S_IRUSR|VKI_S_IWUSR);
1706 if (res.isError) {
1707 file_err();
1708 }
1709 }
1710 if (!res.isError) VG_(close)( (Int)res.val );
1711
1712 *dir = base_directory;
1713 *file = filename;
1714
1715 init_cmdbuf();
1716}