2 * Copyright (c) 2000-2001 Red Hat, Inc. All rights reserved.
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.
15 /* Structure emitted by -a */
23 const unsigned long *addresses
;
25 /* Older GCC's did not emit these fields. */
27 const char **functions
;
28 const long *line_nums
;
29 const char **filenames
;
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. */
37 #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
40 char *ctime (const time_t *);
42 /*#include "gbl-ctors.h"*/
46 static struct bb
*bb_head
;
48 static int num_digits (long value
, int base
) __attribute__ ((const));
50 /* Return the number of digits needed to print a value */
51 /* __inline__ */ static int num_digits (long value
, int base
)
53 int minus
= (value
< 0 && base
!= 16);
54 unsigned long v
= (minus
) ? -value
: value
;
77 i
= strlen (bb_head
->filename
) - 3;
79 if (!strcmp (bb_head
->filename
+i
, ".da"))
81 /* Must be -fprofile-arcs not -a.
82 Dump data in a form that gcov expects. */
86 for (ptr
= bb_head
; ptr
!= (struct bb
*) 0; ptr
= ptr
->next
)
90 /* Make sure the output file exists -
91 but don't clobber exiting data. */
92 if ((da_file
= fopen (ptr
->filename
, "a")) != 0)
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. */
103 da_file
= fopen (ptr
->filename
, "r+");
106 fprintf (stderr
, "arc profiling: Can't open output file %s.\n",
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. */
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
)
120 if (ferror (da_file
))
122 fprintf (stderr
, "arc profiling: Can't read output file ");
123 perror (ptr
->filename
);
130 if (ungetc (firstchar
, da_file
) == EOF
)
132 if (__read_long (&n_counts
, da_file
, 8) != 0)
134 fprintf (stderr
, "arc profiling: Can't read output file %s.\n",
139 if (n_counts
== ptr
->ncounts
)
143 for (i
= 0; i
< n_counts
; i
++)
147 if (__read_long (&v
, da_file
, 8) != 0)
149 fprintf (stderr
, "arc profiling: Can't read output file %s.\n",
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.
166 That way we can easily verify that the proper source/executable/
167 data file combination is being used from gcov. */
169 if (__write_long (ptr
->ncounts
, da_file
, 8) != 0)
172 fprintf (stderr
, "arc profiling: Error writing output file %s.\n",
178 long *count_ptr
= ptr
->counts
;
180 for (j
= ptr
->ncounts
; j
> 0; j
--)
182 if (__write_long (*count_ptr
, da_file
, 8) != 0)
190 fprintf (stderr
, "arc profiling: Error writing output file %s.\n",
194 if (fclose (da_file
) == EOF
)
195 fprintf (stderr
, "arc profiling: Error closing output file %s.\n",
202 /* Must be basic block profiling. Emit a human readable output file. */
204 file
= fopen ("bb.out", "a");
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. */
219 time ((void *) &time_value
);
220 fprintf (file
, "Basic block profiling finished on %s\n", ctime ((void *) &time_value
));
222 /* We check the length field explicitly in order to allow compatibility
223 with older GCC's which did not provide it. */
225 for (ptr
= bb_head
; ptr
!= (struct bb
*) 0; ptr
= ptr
->next
)
228 int func_p
= (ptr
->nwords
>= (long) sizeof (struct bb
)
229 && ptr
->nwords
<= 1000
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
;
240 int blk_len
= num_digits (ncounts
, 10);
245 fprintf (file
, "File %s, %ld basic blocks \n\n",
246 ptr
->filename
, ncounts
);
248 /* Get max values for each field. */
249 for (i
= 0; i
< ncounts
; i
++)
254 if (cnt_max
< ptr
->counts
[i
])
255 cnt_max
= ptr
->counts
[i
];
257 if (addr_p
&& (unsigned long) addr_max
< ptr
->addresses
[i
])
258 addr_max
= ptr
->addresses
[i
];
260 if (line_p
&& line_max
< ptr
->line_nums
[i
])
261 line_max
= ptr
->line_nums
[i
];
265 p
= (ptr
->functions
[i
]) ? (ptr
->functions
[i
]) : "<none>";
273 p
= (ptr
->filenames
[i
]) ? (ptr
->filenames
[i
]) : "<none>";
280 addr_len
= num_digits (addr_max
, 16);
281 cnt_len
= num_digits (cnt_max
, 10);
282 line_len
= num_digits (line_max
, 10);
284 /* Now print out the basic block information. */
285 for (i
= 0; i
< ncounts
; i
++)
288 " Block #%*d: executed %*ld time(s)",
290 cnt_len
, ptr
->counts
[i
]);
293 fprintf (file
, " address= 0x%.*lx", addr_len
,
297 fprintf (file
, " function= %-*s", func_len
,
298 (ptr
->functions
[i
]) ? ptr
->functions
[i
] : "<none>");
301 fprintf (file
, " line= %*ld", line_len
, ptr
->line_nums
[i
]);
304 fprintf (file
, " file= %s",
305 (ptr
->filenames
[i
]) ? ptr
->filenames
[i
] : "<none>");
307 fprintf (file
, "\n");
310 fprintf (file
, "\n");
314 fprintf (file
, "\n\n");
320 __bb_init_func (struct bb
*blocks
)
322 /* User is supposed to check whether the first word is non-0,
323 but just in case.... */
325 if (blocks
->zero_word
)
328 /* Initialize destructor. */
330 atexit (__bb_exit_func
);
332 /* Set up linked list. */
333 blocks
->zero_word
= 1;
334 blocks
->next
= bb_head
;
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. */
342 __bb_fork_func (void)
347 for (ptr
= bb_head
; ptr
!= (struct bb
*) 0; ptr
= ptr
->next
)
350 for (i
= ptr
->ncounts
- 1; i
>= 0; i
--)
355 #ifndef MACHINE_STATE_SAVE
356 #define MACHINE_STATE_SAVE(ID)
358 #ifndef MACHINE_STATE_RESTORE
359 #define MACHINE_STATE_RESTORE(ID)
362 /* Number of buckets in hashtable of basic block addresses. */
364 #define BB_BUCKETS 311
366 /* Maximum length of string in file bb.in. */
368 #define BBINBUFSIZE 500
372 struct bb_edge
*next
;
373 unsigned long src_addr
;
374 unsigned long dst_addr
;
380 TRACE_KEEP
= 0, TRACE_ON
= 1, TRACE_OFF
= 2
385 struct bb_func
*next
;
388 enum bb_func_mode mode
;
391 /* This is the connection to the outside world.
392 The BLOCK_PROFILER macro must set __bb.blocks
396 unsigned long blockno
;
400 /* Vars to store addrs of source and destination basic blocks
403 static unsigned long bb_src
= 0;
404 static unsigned long bb_dst
= 0;
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;
412 static unsigned long *bb_stack
= (unsigned long *) 0;
413 static size_t bb_stacksize
= 0;
415 static int reported
= 0;
418 Always : Print execution frequencies of basic blocks
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.
428 /*#include <sys/types.h>*/
429 #include <sys/stat.h>
430 /*#include <malloc.h>*/
432 /* Commands executed by gopen. */
434 #define GOPENDECOMPRESS "gzip -cd "
435 #define GOPENCOMPRESS "gzip -c >"
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. */
442 gopen (char *fn
, char *mode
)
450 if (mode
[0] != 'r' && mode
[0] != 'w')
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'));
462 char *s
= (char *) malloc (sizeof (char) * strlen (fn
)
463 + sizeof (GOPENDECOMPRESS
));
464 strcpy (s
, GOPENDECOMPRESS
);
465 strcpy (s
+ (sizeof (GOPENDECOMPRESS
)-1), fn
);
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
)))
486 return fopen (fn
, mode
);
496 if (!fstat (fileno (f
), &buf
) && S_ISFIFO (buf
.st_mode
))
504 #endif /* HAVE_POPEN */
506 /* Called once per program. */
509 __bb_exit_trace_func (void)
511 FILE *file
= fopen ("bb.out", "a");
524 gclose (bb_tracefile
);
526 fclose (bb_tracefile
);
527 #endif /* HAVE_POPEN */
530 /* Check functions in `bb.in'. */
535 const struct bb_func
*p
;
536 int printed_something
= 0;
540 /* This is somewhat type incorrect. */
541 time ((void *) &time_value
);
543 for (p
= bb_func_head
; p
!= (struct bb_func
*) 0; p
= p
->next
)
545 for (ptr
= bb_head
; ptr
!= (struct bb
*) 0; ptr
= ptr
->next
)
547 if (!ptr
->filename
|| (p
->filename
!= (char *) 0 && strcmp (p
->filename
, ptr
->filename
)))
549 for (blk
= 0; blk
< ptr
->ncounts
; blk
++)
551 if (!strcmp (p
->funcname
, ptr
->functions
[blk
]))
556 if (!printed_something
)
558 fprintf (file
, "Functions in `bb.in' not executed during basic block profiling on %s\n", ctime ((void *) &time_value
));
559 printed_something
= 1;
562 fprintf (file
, "\tFunction %s", p
->funcname
);
564 fprintf (file
, " of file %s", p
->filename
);
565 fprintf (file
, "\n" );
570 if (printed_something
)
571 fprintf (file
, "\n");
581 fprintf (stderr
, "Profiler: out of memory\n");
591 unsigned long addr_max
= 0;
592 unsigned long cnt_max
= 0;
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. */
602 time ((void *) &time_value
);
603 fprintf (file
, "Basic block jump tracing");
605 switch (bb_mode
& 12)
608 fprintf (file
, " (with call)");
616 fprintf (file
, " (with call & ret)");
620 fprintf (file
, " (with ret)");
624 fprintf (file
, " finished on %s\n", ctime ((void *) &time_value
));
626 for (i
= 0; i
< BB_BUCKETS
; i
++)
628 struct bb_edge
*bucket
= bb_hashbuckets
[i
];
629 for ( ; bucket
; bucket
= bucket
->next
)
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
;
639 addr_len
= num_digits (addr_max
, 16);
640 cnt_len
= num_digits (cnt_max
, 10);
642 for ( i
= 0; i
< BB_BUCKETS
; i
++)
644 struct bb_edge
*bucket
= bb_hashbuckets
[i
];
645 for ( ; bucket
; bucket
= bucket
->next
)
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
);
655 fprintf (file
, "\n");
663 /* Free allocated memory. */
668 struct bb_func
*old
= f
;
671 if (old
->funcname
) free (old
->funcname
);
672 if (old
->filename
) free (old
->filename
);
683 for (i
= 0; i
< BB_BUCKETS
; i
++)
685 struct bb_edge
*old
, *bucket
= bb_hashbuckets
[i
];
690 bucket
= bucket
->next
;
694 free (bb_hashbuckets
);
697 for (b
= bb_head
; b
; b
= b
->next
)
698 if (b
->flags
) free (b
->flags
);
701 /* Called once per program. */
707 char buf
[BBINBUFSIZE
];
713 /* Initialize destructor. */
714 atexit (__bb_exit_func
);
716 if (!(file
= fopen ("bb.in", "r")))
719 while(fgets (buf
, BBINBUFSIZE
, file
) != 0)
722 if (buf
[i
-1] == '\n')
735 if (!strcmp (p
, "__bb_trace__"))
737 else if (!strcmp (p
, "__bb_jumps__"))
739 else if (!strcmp (p
, "__bb_hidecall__"))
741 else if (!strcmp (p
, "__bb_showret__"))
745 struct bb_func
*f
= (struct bb_func
*) malloc (sizeof (struct bb_func
));
749 f
->next
= bb_func_head
;
750 if ((pos
= strchr (p
, ':')))
752 if (!(f
->funcname
= (char *) malloc (strlen (pos
+1)+1)))
754 strcpy (f
->funcname
, pos
+1);
756 if ((f
->filename
= (char *) malloc (l
+1)))
758 strncpy (f
->filename
, p
, l
);
759 f
->filename
[l
] = '\0';
762 f
->filename
= (char *) 0;
766 if (!(f
->funcname
= (char *) malloc (strlen (p
)+1)))
768 strcpy (f
->funcname
, p
);
769 f
->filename
= (char *) 0;
781 bb_tracefile
= gopen ("bbtrace.gz", "w");
786 bb_tracefile
= fopen ("bbtrace", "w");
788 #endif /* HAVE_POPEN */
792 bb_hashbuckets
= (struct bb_edge
**)
793 malloc (BB_BUCKETS
* sizeof (struct bb_edge
*));
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;
804 bb_stack
= (unsigned long *) malloc (bb_stacksize
* sizeof (*bb_stack
));
807 /* Initialize destructor. */
808 atexit (__bb_exit_trace_func
);
811 /* Called upon entering a basic block. */
814 __bb_trace_func (void)
816 struct bb_edge
*bucket
;
818 MACHINE_STATE_SAVE("1")
820 if (!bb_callcount
|| (__bb
.blocks
->flags
&& (__bb
.blocks
->flags
[__bb
.blockno
] & TRACE_OFF
)))
823 bb_dst
= __bb
.blocks
->addresses
[__bb
.blockno
];
824 __bb
.blocks
->counts
[__bb
.blockno
]++;
828 fwrite (&bb_dst
, sizeof (unsigned long), 1, bb_tracefile
);
833 struct bb_edge
**startbucket
, **oldnext
;
835 oldnext
= startbucket
836 = & bb_hashbuckets
[ (((int) bb_src
*8) ^ (int) bb_dst
) % BB_BUCKETS
];
837 bucket
= *startbucket
;
839 for (bucket
= *startbucket
; bucket
;
840 oldnext
= &(bucket
->next
), bucket
= *oldnext
)
842 if (bucket
->src_addr
== bb_src
843 && bucket
->dst_addr
== bb_dst
)
846 *oldnext
= bucket
->next
;
847 bucket
->next
= *startbucket
;
848 *startbucket
= bucket
;
853 bucket
= (struct bb_edge
*) malloc (sizeof (struct bb_edge
));
859 fprintf (stderr
, "Profiler: out of memory\n");
866 bucket
->src_addr
= bb_src
;
867 bucket
->dst_addr
= bb_dst
;
868 bucket
->next
= *startbucket
;
869 *startbucket
= bucket
;
880 MACHINE_STATE_RESTORE("1")
884 /* Called when returning from a function and `__bb_showret__' is set. */
887 __bb_trace_func_ret (void)
889 struct bb_edge
*bucket
;
891 if (!bb_callcount
|| (__bb
.blocks
->flags
&& (__bb
.blocks
->flags
[__bb
.blockno
] & TRACE_OFF
)))
896 struct bb_edge
**startbucket
, **oldnext
;
898 oldnext
= startbucket
899 = & bb_hashbuckets
[ (((int) bb_dst
* 8) ^ (int) bb_src
) % BB_BUCKETS
];
900 bucket
= *startbucket
;
902 for (bucket
= *startbucket
; bucket
;
903 oldnext
= &(bucket
->next
), bucket
= *oldnext
)
905 if (bucket
->src_addr
== bb_dst
906 && bucket
->dst_addr
== bb_src
)
909 *oldnext
= bucket
->next
;
910 bucket
->next
= *startbucket
;
911 *startbucket
= bucket
;
916 bucket
= (struct bb_edge
*) malloc (sizeof (struct bb_edge
));
922 fprintf (stderr
, "Profiler: out of memory\n");
929 bucket
->src_addr
= bb_dst
;
930 bucket
->dst_addr
= bb_src
;
931 bucket
->next
= *startbucket
;
932 *startbucket
= bucket
;
945 /* Called upon entering the first function of a file. */
948 __bb_init_file (struct bb
*blocks
)
951 const struct bb_func
*p
;
952 long blk
, ncounts
= blocks
->ncounts
;
953 const char **functions
= blocks
->functions
;
955 /* Set up linked list. */
956 blocks
->zero_word
= 1;
957 blocks
->next
= bb_head
;
962 || !(blocks
->flags
= (char *) malloc (sizeof (char) * blocks
->ncounts
)))
965 for (blk
= 0; blk
< ncounts
; blk
++)
966 blocks
->flags
[blk
] = 0;
968 for (blk
= 0; blk
< ncounts
; blk
++)
970 for (p
= bb_func_head
; p
; p
= p
->next
)
972 if (!strcmp (p
->funcname
, functions
[blk
])
973 && (!p
->filename
|| !strcmp (p
->filename
, blocks
->filename
)))
975 blocks
->flags
[blk
] |= p
->mode
;
982 /* Called when exiting from a function. */
985 __bb_trace_ret (void)
988 MACHINE_STATE_SAVE("2")
992 if ((bb_mode
& 12) && bb_stacksize
> bb_callcount
)
994 bb_src
= bb_stack
[bb_callcount
];
996 __bb_trace_func_ret ();
1002 MACHINE_STATE_RESTORE("2")
1006 /* Called when entering a function. */
1009 __bb_init_trace_func (struct bb
*blocks
, unsigned long blockno
)
1011 static int trace_init
= 0;
1013 MACHINE_STATE_SAVE("3")
1015 if (!blocks
->zero_word
)
1022 __bb_init_file (blocks
);
1032 if (bb_callcount
>= bb_stacksize
)
1034 size_t newsize
= bb_callcount
+ 100;
1036 bb_stack
= (unsigned long *) realloc (bb_stack
, newsize
);
1041 fprintf (stderr
, "Profiler: out of memory\n");
1045 goto stack_overflow
;
1047 bb_stacksize
= newsize
;
1049 bb_stack
[bb_callcount
] = bb_src
;
1060 else if (blocks
->flags
&& (blocks
->flags
[blockno
] & TRACE_ON
))
1066 bb_stack
[bb_callcount
] = bb_src
;
1069 MACHINE_STATE_RESTORE("3")