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