1 /*
2 * Copyright (c) 2000-2001 Red Hat, Inc. All rights reserved.
3 *
4 * This copyrighted material is made available to anyone wishing to use, modify,
5 * copy, or redistribute it subject to the terms and conditions of the BSD
6 * License. This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY expressed or implied, including the implied
8 * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. A copy
9 * of this license is available at http://www.opensource.org/licenses. Any
10 * Red Hat trademarks that are incorporated in the source code or documentation
11 * are not subject to the BSD License and may only be used or replicated with
12 * the express permission of Red Hat, Inc.
13 */
14
15 /* Structure emitted by -a */
16 struct bb
17 {
18 long zero_word;
19 const char *filename;
20 long *counts;
21 long ncounts;
22 struct bb *next;
23 const unsigned long *addresses;
24
25 /* Older GCC's did not emit these fields. */
26 long nwords;
27 const char **functions;
28 const long *line_nums;
29 const char **filenames;
30 char *flags;
31 };
32
33 /* Simple minded basic block profiling output dumper for
34 systems that don't provide tcov support. At present,
35 it requires atexit and stdio. */
36
37 #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
38 #include <stdio.h>
39 #include <time.h>
40 char *ctime (const time_t *);
41
42 /*#include "gbl-ctors.h"*/
43 #include "gcov-io.h"
44 #include <string.h>
45
46 static struct bb *bb_head;
47
48 static int num_digits (long value, int base) __attribute__ ((const));
49
50 /* Return the number of digits needed to print a value */
num_digits(long value,int base)51 /* __inline__ */ static int num_digits (long value, int base)
52 {
53 int minus = (value < 0 && base != 16);
54 unsigned long v = (minus) ? -value : value;
55 int ret = minus;
56
57 do
58 {
59 v /= base;
60 ret++;
61 }
62 while (v);
63
64 return ret;
65 }
66
67 void
__bb_exit_func(void)68 __bb_exit_func (void)
69 {
70 FILE *da_file, *file;
71 long time_value;
72 int i;
73
74 if (bb_head == 0)
75 return;
76
77 i = strlen (bb_head->filename) - 3;
78
79 if (!strcmp (bb_head->filename+i, ".da"))
80 {
81 /* Must be -fprofile-arcs not -a.
82 Dump data in a form that gcov expects. */
83
84 struct bb *ptr;
85
86 for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
87 {
88 int firstchar;
89
90 /* Make sure the output file exists -
91 but don't clobber exiting data. */
92 if ((da_file = fopen (ptr->filename, "a")) != 0)
93 fclose (da_file);
94
95 /* Need to re-open in order to be able to write from the start. */
96 da_file = fopen (ptr->filename, "r+b");
97 /* Some old systems might not allow the 'b' mode modifier.
98 Therefore, try to open without it. This can lead to a race
99 condition so that when you delete and re-create the file, the
100 file might be opened in text mode, but then, you shouldn't
101 delete the file in the first place. */
102 if (da_file == 0)
103 da_file = fopen (ptr->filename, "r+");
104 if (da_file == 0)
105 {
106 fprintf (stderr, "arc profiling: Can't open output file %s.\n",
107 ptr->filename);
108 continue;
109 }
110
111 /* After a fork, another process might try to read and/or write
112 the same file simultanously. So if we can, lock the file to
113 avoid race conditions. */
114
115 /* If the file is not empty, and the number of counts in it is the
116 same, then merge them in. */
117 firstchar = fgetc (da_file);
118 if (firstchar == EOF)
119 {
120 if (ferror (da_file))
121 {
122 fprintf (stderr, "arc profiling: Can't read output file ");
123 perror (ptr->filename);
124 }
125 }
126 else
127 {
128 long n_counts = 0;
129
130 if (ungetc (firstchar, da_file) == EOF)
131 rewind (da_file);
132 if (__read_long (&n_counts, da_file, 8) != 0)
133 {
134 fprintf (stderr, "arc profiling: Can't read output file %s.\n",
135 ptr->filename);
136 continue;
137 }
138
139 if (n_counts == ptr->ncounts)
140 {
141 int i;
142
143 for (i = 0; i < n_counts; i++)
144 {
145 long v = 0;
146
147 if (__read_long (&v, da_file, 8) != 0)
148 {
149 fprintf (stderr, "arc profiling: Can't read output file %s.\n",
150 ptr->filename);
151 break;
152 }
153 ptr->counts[i] += v;
154 }
155 }
156
157 }
158
159 rewind (da_file);
160
161 /* ??? Should first write a header to the file. Preferably, a 4 byte
162 magic number, 4 bytes containing the time the program was
163 compiled, 4 bytes containing the last modification time of the
164 source file, and 4 bytes indicating the compiler options used.
165
166 That way we can easily verify that the proper source/executable/
167 data file combination is being used from gcov. */
168
169 if (__write_long (ptr->ncounts, da_file, 8) != 0)
170 {
171
172 fprintf (stderr, "arc profiling: Error writing output file %s.\n",
173 ptr->filename);
174 }
175 else
176 {
177 int j;
178 long *count_ptr = ptr->counts;
179 int ret = 0;
180 for (j = ptr->ncounts; j > 0; j--)
181 {
182 if (__write_long (*count_ptr, da_file, 8) != 0)
183 {
184 ret=1;
185 break;
186 }
187 count_ptr++;
188 }
189 if (ret)
190 fprintf (stderr, "arc profiling: Error writing output file %s.\n",
191 ptr->filename);
192 }
193
194 if (fclose (da_file) == EOF)
195 fprintf (stderr, "arc profiling: Error closing output file %s.\n",
196 ptr->filename);
197 }
198
199 return;
200 }
201
202 /* Must be basic block profiling. Emit a human readable output file. */
203
204 file = fopen ("bb.out", "a");
205
206 if (!file)
207 perror ("bb.out");
208
209 else
210 {
211 struct bb *ptr;
212
213 /* This is somewhat type incorrect, but it avoids worrying about
214 exactly where time.h is included from. It should be ok unless
215 a void * differs from other pointer formats, or if sizeof (long)
216 is < sizeof (time_t). It would be nice if we could assume the
217 use of rationale standards here. */
218
219 time ((void *) &time_value);
220 fprintf (file, "Basic block profiling finished on %s\n", ctime ((void *) &time_value));
221
222 /* We check the length field explicitly in order to allow compatibility
223 with older GCC's which did not provide it. */
224
225 for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
226 {
227 int i;
228 int func_p = (ptr->nwords >= (long) sizeof (struct bb)
229 && ptr->nwords <= 1000
230 && ptr->functions);
231 int line_p = (func_p && ptr->line_nums);
232 int file_p = (func_p && ptr->filenames);
233 int addr_p = (ptr->addresses != 0);
234 long ncounts = ptr->ncounts;
235 long cnt_max = 0;
236 long line_max = 0;
237 long addr_max = 0;
238 int file_len = 0;
239 int func_len = 0;
240 int blk_len = num_digits (ncounts, 10);
241 int cnt_len;
242 int line_len;
243 int addr_len;
244
245 fprintf (file, "File %s, %ld basic blocks \n\n",
246 ptr->filename, ncounts);
247
248 /* Get max values for each field. */
249 for (i = 0; i < ncounts; i++)
250 {
251 const char *p;
252 int len;
253
254 if (cnt_max < ptr->counts[i])
255 cnt_max = ptr->counts[i];
256
257 if (addr_p && (unsigned long) addr_max < ptr->addresses[i])
258 addr_max = ptr->addresses[i];
259
260 if (line_p && line_max < ptr->line_nums[i])
261 line_max = ptr->line_nums[i];
262
263 if (func_p)
264 {
265 p = (ptr->functions[i]) ? (ptr->functions[i]) : "<none>";
266 len = strlen (p);
267 if (func_len < len)
268 func_len = len;
269 }
270
271 if (file_p)
272 {
273 p = (ptr->filenames[i]) ? (ptr->filenames[i]) : "<none>";
274 len = strlen (p);
275 if (file_len < len)
276 file_len = len;
277 }
278 }
279
280 addr_len = num_digits (addr_max, 16);
281 cnt_len = num_digits (cnt_max, 10);
282 line_len = num_digits (line_max, 10);
283
284 /* Now print out the basic block information. */
285 for (i = 0; i < ncounts; i++)
286 {
287 fprintf (file,
288 " Block #%*d: executed %*ld time(s)",
289 blk_len, i+1,
290 cnt_len, ptr->counts[i]);
291
292 if (addr_p)
293 fprintf (file, " address= 0x%.*lx", addr_len,
294 ptr->addresses[i]);
295
296 if (func_p)
297 fprintf (file, " function= %-*s", func_len,
298 (ptr->functions[i]) ? ptr->functions[i] : "<none>");
299
300 if (line_p)
301 fprintf (file, " line= %*ld", line_len, ptr->line_nums[i]);
302
303 if (file_p)
304 fprintf (file, " file= %s",
305 (ptr->filenames[i]) ? ptr->filenames[i] : "<none>");
306
307 fprintf (file, "\n");
308 }
309
310 fprintf (file, "\n");
311 fflush (file);
312 }
313
314 fprintf (file, "\n\n");
315 fclose (file);
316 }
317 }
318
319 void
__bb_init_func(struct bb * blocks)320 __bb_init_func (struct bb *blocks)
321 {
322 /* User is supposed to check whether the first word is non-0,
323 but just in case.... */
324
325 if (blocks->zero_word)
326 return;
327
328 /* Initialize destructor. */
329 if (!bb_head)
330 atexit (__bb_exit_func);
331
332 /* Set up linked list. */
333 blocks->zero_word = 1;
334 blocks->next = bb_head;
335 bb_head = blocks;
336 }
337
338 /* Called before fork or exec - write out profile information gathered so
339 far and reset it to zero. This avoids duplication or loss of the
340 profile information gathered so far. */
341 void
__bb_fork_func(void)342 __bb_fork_func (void)
343 {
344 struct bb *ptr;
345
346 __bb_exit_func ();
347 for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
348 {
349 long i;
350 for (i = ptr->ncounts - 1; i >= 0; i--)
351 ptr->counts[i] = 0;
352 }
353 }
354
355 #ifndef MACHINE_STATE_SAVE
356 #define MACHINE_STATE_SAVE(ID)
357 #endif
358 #ifndef MACHINE_STATE_RESTORE
359 #define MACHINE_STATE_RESTORE(ID)
360 #endif
361
362 /* Number of buckets in hashtable of basic block addresses. */
363
364 #define BB_BUCKETS 311
365
366 /* Maximum length of string in file bb.in. */
367
368 #define BBINBUFSIZE 500
369
370 struct bb_edge
371 {
372 struct bb_edge *next;
373 unsigned long src_addr;
374 unsigned long dst_addr;
375 unsigned long count;
376 };
377
378 enum bb_func_mode
379 {
380 TRACE_KEEP = 0, TRACE_ON = 1, TRACE_OFF = 2
381 };
382
383 struct bb_func
384 {
385 struct bb_func *next;
386 char *funcname;
387 char *filename;
388 enum bb_func_mode mode;
389 };
390
391 /* This is the connection to the outside world.
392 The BLOCK_PROFILER macro must set __bb.blocks
393 and __bb.blockno. */
394
395 struct {
396 unsigned long blockno;
397 struct bb *blocks;
398 } __bb;
399
400 /* Vars to store addrs of source and destination basic blocks
401 of a jump. */
402
403 static unsigned long bb_src = 0;
404 static unsigned long bb_dst = 0;
405
406 static FILE *bb_tracefile = (FILE *) 0;
407 static struct bb_edge **bb_hashbuckets = (struct bb_edge **) 0;
408 static struct bb_func *bb_func_head = (struct bb_func *) 0;
409 static unsigned long bb_callcount = 0;
410 static int bb_mode = 0;
411
412 static unsigned long *bb_stack = (unsigned long *) 0;
413 static size_t bb_stacksize = 0;
414
415 static int reported = 0;
416
417 /* Trace modes:
418 Always : Print execution frequencies of basic blocks
419 to file bb.out.
420 bb_mode & 1 != 0 : Dump trace of basic blocks to file bbtrace[.gz]
421 bb_mode & 2 != 0 : Print jump frequencies to file bb.out.
422 bb_mode & 4 != 0 : Cut call instructions from basic block flow.
423 bb_mode & 8 != 0 : Insert return instructions in basic block flow.
424 */
425
426 #ifdef HAVE_POPEN
427
428 /*#include <sys/types.h>*/
429 #include <sys/stat.h>
430 /*#include <malloc.h>*/
431
432 /* Commands executed by gopen. */
433
434 #define GOPENDECOMPRESS "gzip -cd "
435 #define GOPENCOMPRESS "gzip -c >"
436
437 /* Like fopen but pipes through gzip. mode may only be "r" or "w".
438 If it does not compile, simply replace gopen by fopen and delete
439 '.gz' from any first parameter to gopen. */
440
441 static FILE *
gopen(char * fn,char * mode)442 gopen (char *fn, char *mode)
443 {
444 int use_gzip;
445 char *p;
446
447 if (mode[1])
448 return (FILE *) 0;
449
450 if (mode[0] != 'r' && mode[0] != 'w')
451 return (FILE *) 0;
452
453 p = fn + strlen (fn)-1;
454 use_gzip = ((p[-1] == '.' && (p[0] == 'Z' || p[0] == 'z'))
455 || (p[-2] == '.' && p[-1] == 'g' && p[0] == 'z'));
456
457 if (use_gzip)
458 {
459 if (mode[0]=='r')
460 {
461 FILE *f;
462 char *s = (char *) malloc (sizeof (char) * strlen (fn)
463 + sizeof (GOPENDECOMPRESS));
464 strcpy (s, GOPENDECOMPRESS);
465 strcpy (s + (sizeof (GOPENDECOMPRESS)-1), fn);
466 f = popen (s, mode);
467 free (s);
468 return f;
469 }
470
471 else
472 {
473 FILE *f;
474 char *s = (char *) malloc (sizeof (char) * strlen (fn)
475 + sizeof (GOPENCOMPRESS));
476 strcpy (s, GOPENCOMPRESS);
477 strcpy (s + (sizeof (GOPENCOMPRESS)-1), fn);
478 if (!(f = popen (s, mode)))
479 f = fopen (s, mode);
480 free (s);
481 return f;
482 }
483 }
484
485 else
486 return fopen (fn, mode);
487 }
488
489 static int
gclose(FILE * f)490 gclose (FILE *f)
491 {
492 struct stat buf;
493
494 if (f != 0)
495 {
496 if (!fstat (fileno (f), &buf) && S_ISFIFO (buf.st_mode))
497 return pclose (f);
498
499 return fclose (f);
500 }
501 return 0;
502 }
503
504 #endif /* HAVE_POPEN */
505
506 /* Called once per program. */
507
508 static void
__bb_exit_trace_func(void)509 __bb_exit_trace_func (void)
510 {
511 FILE *file = fopen ("bb.out", "a");
512 struct bb_func *f;
513 struct bb *b;
514
515 if (!file)
516 perror ("bb.out");
517
518 if (bb_mode & 1)
519 {
520 if (!bb_tracefile)
521 perror ("bbtrace");
522 else
523 #ifdef HAVE_POPEN
524 gclose (bb_tracefile);
525 #else
526 fclose (bb_tracefile);
527 #endif /* HAVE_POPEN */
528 }
529
530 /* Check functions in `bb.in'. */
531
532 if (file)
533 {
534 long time_value;
535 const struct bb_func *p;
536 int printed_something = 0;
537 struct bb *ptr;
538 long blk;
539
540 /* This is somewhat type incorrect. */
541 time ((void *) &time_value);
542
543 for (p = bb_func_head; p != (struct bb_func *) 0; p = p->next)
544 {
545 for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
546 {
547 if (!ptr->filename || (p->filename != (char *) 0 && strcmp (p->filename, ptr->filename)))
548 continue;
549 for (blk = 0; blk < ptr->ncounts; blk++)
550 {
551 if (!strcmp (p->funcname, ptr->functions[blk]))
552 goto found;
553 }
554 }
555
556 if (!printed_something)
557 {
558 fprintf (file, "Functions in `bb.in' not executed during basic block profiling on %s\n", ctime ((void *) &time_value));
559 printed_something = 1;
560 }
561
562 fprintf (file, "\tFunction %s", p->funcname);
563 if (p->filename)
564 fprintf (file, " of file %s", p->filename);
565 fprintf (file, "\n" );
566
567 found: ;
568 }
569
570 if (printed_something)
571 fprintf (file, "\n");
572
573 }
574
575 if (bb_mode & 2)
576 {
577 if (!bb_hashbuckets)
578 {
579 if (!reported)
580 {
581 fprintf (stderr, "Profiler: out of memory\n");
582 reported = 1;
583 }
584 return;
585 }
586
587 else if (file)
588 {
589 long time_value;
590 int i;
591 unsigned long addr_max = 0;
592 unsigned long cnt_max = 0;
593 int cnt_len;
594 int addr_len;
595
596 /* This is somewhat type incorrect, but it avoids worrying about
597 exactly where time.h is included from. It should be ok unless
598 a void * differs from other pointer formats, or if sizeof (long)
599 is < sizeof (time_t). It would be nice if we could assume the
600 use of rationale standards here. */
601
602 time ((void *) &time_value);
603 fprintf (file, "Basic block jump tracing");
604
605 switch (bb_mode & 12)
606 {
607 case 0:
608 fprintf (file, " (with call)");
609 break;
610
611 case 4:
612 /* Print nothing. */
613 break;
614
615 case 8:
616 fprintf (file, " (with call & ret)");
617 break;
618
619 case 12:
620 fprintf (file, " (with ret)");
621 break;
622 }
623
624 fprintf (file, " finished on %s\n", ctime ((void *) &time_value));
625
626 for (i = 0; i < BB_BUCKETS; i++)
627 {
628 struct bb_edge *bucket = bb_hashbuckets[i];
629 for ( ; bucket; bucket = bucket->next )
630 {
631 if (addr_max < bucket->src_addr)
632 addr_max = bucket->src_addr;
633 if (addr_max < bucket->dst_addr)
634 addr_max = bucket->dst_addr;
635 if (cnt_max < bucket->count)
636 cnt_max = bucket->count;
637 }
638 }
639 addr_len = num_digits (addr_max, 16);
640 cnt_len = num_digits (cnt_max, 10);
641
642 for ( i = 0; i < BB_BUCKETS; i++)
643 {
644 struct bb_edge *bucket = bb_hashbuckets[i];
645 for ( ; bucket; bucket = bucket->next )
646 {
647 fprintf (file,
648 "Jump from block 0x%.*lx to block 0x%.*lx executed %*lu time(s)\n",
649 addr_len, bucket->src_addr,
650 addr_len, bucket->dst_addr,
651 cnt_len, bucket->count);
652 }
653 }
654
655 fprintf (file, "\n");
656
657 }
658 }
659
660 if (file)
661 fclose (file);
662
663 /* Free allocated memory. */
664
665 f = bb_func_head;
666 while (f)
667 {
668 struct bb_func *old = f;
669
670 f = f->next;
671 if (old->funcname) free (old->funcname);
672 if (old->filename) free (old->filename);
673 free (old);
674 }
675
676 if (bb_stack)
677 free (bb_stack);
678
679 if (bb_hashbuckets)
680 {
681 int i;
682
683 for (i = 0; i < BB_BUCKETS; i++)
684 {
685 struct bb_edge *old, *bucket = bb_hashbuckets[i];
686
687 while (bucket)
688 {
689 old = bucket;
690 bucket = bucket->next;
691 free (old);
692 }
693 }
694 free (bb_hashbuckets);
695 }
696
697 for (b = bb_head; b; b = b->next)
698 if (b->flags) free (b->flags);
699 }
700
701 /* Called once per program. */
702
703 static void
__bb_init_prg(void)704 __bb_init_prg (void)
705 {
706 FILE *file;
707 char buf[BBINBUFSIZE];
708 const char *p;
709 const char *pos;
710 enum bb_func_mode m;
711 int i;
712
713 /* Initialize destructor. */
714 atexit (__bb_exit_func);
715
716 if (!(file = fopen ("bb.in", "r")))
717 return;
718
719 while(fgets (buf, BBINBUFSIZE, file) != 0)
720 {
721 i = strlen (buf);
722 if (buf[i-1] == '\n')
723 buf[--i] = '\0';
724
725 p = buf;
726 if (*p == '-')
727 {
728 m = TRACE_OFF;
729 p++;
730 }
731 else
732 {
733 m = TRACE_ON;
734 }
735 if (!strcmp (p, "__bb_trace__"))
736 bb_mode |= 1;
737 else if (!strcmp (p, "__bb_jumps__"))
738 bb_mode |= 2;
739 else if (!strcmp (p, "__bb_hidecall__"))
740 bb_mode |= 4;
741 else if (!strcmp (p, "__bb_showret__"))
742 bb_mode |= 8;
743 else
744 {
745 struct bb_func *f = (struct bb_func *) malloc (sizeof (struct bb_func));
746 if (f)
747 {
748 unsigned long l;
749 f->next = bb_func_head;
750 if ((pos = strchr (p, ':')))
751 {
752 if (!(f->funcname = (char *) malloc (strlen (pos+1)+1)))
753 continue;
754 strcpy (f->funcname, pos+1);
755 l = pos-p;
756 if ((f->filename = (char *) malloc (l+1)))
757 {
758 strncpy (f->filename, p, l);
759 f->filename[l] = '\0';
760 }
761 else
762 f->filename = (char *) 0;
763 }
764 else
765 {
766 if (!(f->funcname = (char *) malloc (strlen (p)+1)))
767 continue;
768 strcpy (f->funcname, p);
769 f->filename = (char *) 0;
770 }
771 f->mode = m;
772 bb_func_head = f;
773 }
774 }
775 }
776 fclose (file);
777
778 #ifdef HAVE_POPEN
779
780 if (bb_mode & 1)
781 bb_tracefile = gopen ("bbtrace.gz", "w");
782
783 #else
784
785 if (bb_mode & 1)
786 bb_tracefile = fopen ("bbtrace", "w");
787
788 #endif /* HAVE_POPEN */
789
790 if (bb_mode & 2)
791 {
792 bb_hashbuckets = (struct bb_edge **)
793 malloc (BB_BUCKETS * sizeof (struct bb_edge *));
794 if (bb_hashbuckets)
795 /* Use a loop here rather than calling bzero to avoid having to
796 conditionalize its existance. */
797 for (i = 0; i < BB_BUCKETS; i++)
798 bb_hashbuckets[i] = 0;
799 }
800
801 if (bb_mode & 12)
802 {
803 bb_stacksize = 10;
804 bb_stack = (unsigned long *) malloc (bb_stacksize * sizeof (*bb_stack));
805 }
806
807 /* Initialize destructor. */
808 atexit (__bb_exit_trace_func);
809 }
810
811 /* Called upon entering a basic block. */
812
813 void
__bb_trace_func(void)814 __bb_trace_func (void)
815 {
816 struct bb_edge *bucket;
817
818 MACHINE_STATE_SAVE("1")
819
820 if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF)))
821 goto skip;
822
823 bb_dst = __bb.blocks->addresses[__bb.blockno];
824 __bb.blocks->counts[__bb.blockno]++;
825
826 if (bb_tracefile)
827 {
828 fwrite (&bb_dst, sizeof (unsigned long), 1, bb_tracefile);
829 }
830
831 if (bb_hashbuckets)
832 {
833 struct bb_edge **startbucket, **oldnext;
834
835 oldnext = startbucket
836 = & bb_hashbuckets[ (((int) bb_src*8) ^ (int) bb_dst) % BB_BUCKETS ];
837 bucket = *startbucket;
838
839 for (bucket = *startbucket; bucket;
840 oldnext = &(bucket->next), bucket = *oldnext)
841 {
842 if (bucket->src_addr == bb_src
843 && bucket->dst_addr == bb_dst)
844 {
845 bucket->count++;
846 *oldnext = bucket->next;
847 bucket->next = *startbucket;
848 *startbucket = bucket;
849 goto ret;
850 }
851 }
852
853 bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge));
854
855 if (!bucket)
856 {
857 if (!reported)
858 {
859 fprintf (stderr, "Profiler: out of memory\n");
860 reported = 1;
861 }
862 }
863
864 else
865 {
866 bucket->src_addr = bb_src;
867 bucket->dst_addr = bb_dst;
868 bucket->next = *startbucket;
869 *startbucket = bucket;
870 bucket->count = 1;
871 }
872 }
873
874 ret:
875 bb_src = bb_dst;
876
877 skip:
878 ;
879
880 MACHINE_STATE_RESTORE("1")
881
882 }
883
884 /* Called when returning from a function and `__bb_showret__' is set. */
885
886 static void
__bb_trace_func_ret(void)887 __bb_trace_func_ret (void)
888 {
889 struct bb_edge *bucket;
890
891 if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF)))
892 goto skip;
893
894 if (bb_hashbuckets)
895 {
896 struct bb_edge **startbucket, **oldnext;
897
898 oldnext = startbucket
899 = & bb_hashbuckets[ (((int) bb_dst * 8) ^ (int) bb_src) % BB_BUCKETS ];
900 bucket = *startbucket;
901
902 for (bucket = *startbucket; bucket;
903 oldnext = &(bucket->next), bucket = *oldnext)
904 {
905 if (bucket->src_addr == bb_dst
906 && bucket->dst_addr == bb_src)
907 {
908 bucket->count++;
909 *oldnext = bucket->next;
910 bucket->next = *startbucket;
911 *startbucket = bucket;
912 goto ret;
913 }
914 }
915
916 bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge));
917
918 if (!bucket)
919 {
920 if (!reported)
921 {
922 fprintf (stderr, "Profiler: out of memory\n");
923 reported = 1;
924 }
925 }
926
927 else
928 {
929 bucket->src_addr = bb_dst;
930 bucket->dst_addr = bb_src;
931 bucket->next = *startbucket;
932 *startbucket = bucket;
933 bucket->count = 1;
934 }
935 }
936
937 ret:
938 bb_dst = bb_src;
939
940 skip:
941 ;
942
943 }
944
945 /* Called upon entering the first function of a file. */
946
947 static void
__bb_init_file(struct bb * blocks)948 __bb_init_file (struct bb *blocks)
949 {
950
951 const struct bb_func *p;
952 long blk, ncounts = blocks->ncounts;
953 const char **functions = blocks->functions;
954
955 /* Set up linked list. */
956 blocks->zero_word = 1;
957 blocks->next = bb_head;
958 bb_head = blocks;
959
960 blocks->flags = 0;
961 if (!bb_func_head
962 || !(blocks->flags = (char *) malloc (sizeof (char) * blocks->ncounts)))
963 return;
964
965 for (blk = 0; blk < ncounts; blk++)
966 blocks->flags[blk] = 0;
967
968 for (blk = 0; blk < ncounts; blk++)
969 {
970 for (p = bb_func_head; p; p = p->next)
971 {
972 if (!strcmp (p->funcname, functions[blk])
973 && (!p->filename || !strcmp (p->filename, blocks->filename)))
974 {
975 blocks->flags[blk] |= p->mode;
976 }
977 }
978 }
979
980 }
981
982 /* Called when exiting from a function. */
983
984 void
__bb_trace_ret(void)985 __bb_trace_ret (void)
986 {
987
988 MACHINE_STATE_SAVE("2")
989
990 if (bb_callcount)
991 {
992 if ((bb_mode & 12) && bb_stacksize > bb_callcount)
993 {
994 bb_src = bb_stack[bb_callcount];
995 if (bb_mode & 8)
996 __bb_trace_func_ret ();
997 }
998
999 bb_callcount -= 1;
1000 }
1001
1002 MACHINE_STATE_RESTORE("2")
1003
1004 }
1005
1006 /* Called when entering a function. */
1007
1008 void
__bb_init_trace_func(struct bb * blocks,unsigned long blockno)1009 __bb_init_trace_func (struct bb *blocks, unsigned long blockno)
1010 {
1011 static int trace_init = 0;
1012
1013 MACHINE_STATE_SAVE("3")
1014
1015 if (!blocks->zero_word)
1016 {
1017 if (!trace_init)
1018 {
1019 trace_init = 1;
1020 __bb_init_prg ();
1021 }
1022 __bb_init_file (blocks);
1023 }
1024
1025 if (bb_callcount)
1026 {
1027
1028 bb_callcount += 1;
1029
1030 if (bb_mode & 12)
1031 {
1032 if (bb_callcount >= bb_stacksize)
1033 {
1034 size_t newsize = bb_callcount + 100;
1035
1036 bb_stack = (unsigned long *) realloc (bb_stack, newsize);
1037 if (! bb_stack)
1038 {
1039 if (!reported)
1040 {
1041 fprintf (stderr, "Profiler: out of memory\n");
1042 reported = 1;
1043 }
1044 bb_stacksize = 0;
1045 goto stack_overflow;
1046 }
1047 bb_stacksize = newsize;
1048 }
1049 bb_stack[bb_callcount] = bb_src;
1050
1051 if (bb_mode & 4)
1052 bb_src = 0;
1053
1054 }
1055
1056 stack_overflow:;
1057
1058 }
1059
1060 else if (blocks->flags && (blocks->flags[blockno] & TRACE_ON))
1061 {
1062 bb_callcount = 1;
1063 bb_src = 0;
1064
1065 if (bb_stack)
1066 bb_stack[bb_callcount] = bb_src;
1067 }
1068
1069 MACHINE_STATE_RESTORE("3")
1070 }
1071
1072