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