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