diff options
Diffstat (limited to 'libgloss/mep/mep-bb.c')
-rw-r--r-- | libgloss/mep/mep-bb.c | 1071 |
1 files changed, 1071 insertions, 0 deletions
diff --git a/libgloss/mep/mep-bb.c b/libgloss/mep/mep-bb.c new file mode 100644 index 000000000..191da1696 --- /dev/null +++ b/libgloss/mep/mep-bb.c @@ -0,0 +1,1071 @@ +/* + * Copyright (c) 2000-2001 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the BSD + * License. This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY expressed or implied, including the implied + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. A copy + * of this license is available at http://www.opensource.org/licenses. Any + * Red Hat trademarks that are incorporated in the source code or documentation + * are not subject to the BSD License and may only be used or replicated with + * the express permission of Red Hat, Inc. + */ + +/* Structure emitted by -a */ +struct bb +{ + long zero_word; + const char *filename; + long *counts; + long ncounts; + struct bb *next; + const unsigned long *addresses; + + /* Older GCC's did not emit these fields. */ + long nwords; + const char **functions; + const long *line_nums; + const char **filenames; + char *flags; +}; + +/* Simple minded basic block profiling output dumper for + systems that don't provide tcov support. At present, + it requires atexit and stdio. */ + +#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */ +#include <stdio.h> +#include <time.h> +char *ctime (const time_t *); + +/*#include "gbl-ctors.h"*/ +#include "gcov-io.h" +#include <string.h> + +static struct bb *bb_head; + +static int num_digits (long value, int base) __attribute__ ((const)); + +/* Return the number of digits needed to print a value */ +/* __inline__ */ static int num_digits (long value, int base) +{ + int minus = (value < 0 && base != 16); + unsigned long v = (minus) ? -value : value; + int ret = minus; + + do + { + v /= base; + ret++; + } + while (v); + + return ret; +} + +void +__bb_exit_func (void) +{ + FILE *da_file, *file; + long time_value; + int i; + + if (bb_head == 0) + return; + + i = strlen (bb_head->filename) - 3; + + if (!strcmp (bb_head->filename+i, ".da")) + { + /* Must be -fprofile-arcs not -a. + Dump data in a form that gcov expects. */ + + struct bb *ptr; + + for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) + { + int firstchar; + + /* Make sure the output file exists - + but don't clobber exiting data. */ + if ((da_file = fopen (ptr->filename, "a")) != 0) + fclose (da_file); + + /* Need to re-open in order to be able to write from the start. */ + da_file = fopen (ptr->filename, "r+b"); + /* Some old systems might not allow the 'b' mode modifier. + Therefore, try to open without it. This can lead to a race + condition so that when you delete and re-create the file, the + file might be opened in text mode, but then, you shouldn't + delete the file in the first place. */ + if (da_file == 0) + da_file = fopen (ptr->filename, "r+"); + if (da_file == 0) + { + fprintf (stderr, "arc profiling: Can't open output file %s.\n", + ptr->filename); + continue; + } + + /* After a fork, another process might try to read and/or write + the same file simultanously. So if we can, lock the file to + avoid race conditions. */ + + /* If the file is not empty, and the number of counts in it is the + same, then merge them in. */ + firstchar = fgetc (da_file); + if (firstchar == EOF) + { + if (ferror (da_file)) + { + fprintf (stderr, "arc profiling: Can't read output file "); + perror (ptr->filename); + } + } + else + { + long n_counts = 0; + + if (ungetc (firstchar, da_file) == EOF) + rewind (da_file); + if (__read_long (&n_counts, da_file, 8) != 0) + { + fprintf (stderr, "arc profiling: Can't read output file %s.\n", + ptr->filename); + continue; + } + + if (n_counts == ptr->ncounts) + { + int i; + + for (i = 0; i < n_counts; i++) + { + long v = 0; + + if (__read_long (&v, da_file, 8) != 0) + { + fprintf (stderr, "arc profiling: Can't read output file %s.\n", + ptr->filename); + break; + } + ptr->counts[i] += v; + } + } + + } + + rewind (da_file); + + /* ??? Should first write a header to the file. Preferably, a 4 byte + magic number, 4 bytes containing the time the program was + compiled, 4 bytes containing the last modification time of the + source file, and 4 bytes indicating the compiler options used. + + That way we can easily verify that the proper source/executable/ + data file combination is being used from gcov. */ + + if (__write_long (ptr->ncounts, da_file, 8) != 0) + { + + fprintf (stderr, "arc profiling: Error writing output file %s.\n", + ptr->filename); + } + else + { + int j; + long *count_ptr = ptr->counts; + int ret = 0; + for (j = ptr->ncounts; j > 0; j--) + { + if (__write_long (*count_ptr, da_file, 8) != 0) + { + ret=1; + break; + } + count_ptr++; + } + if (ret) + fprintf (stderr, "arc profiling: Error writing output file %s.\n", + ptr->filename); + } + + if (fclose (da_file) == EOF) + fprintf (stderr, "arc profiling: Error closing output file %s.\n", + ptr->filename); + } + + return; + } + + /* Must be basic block profiling. Emit a human readable output file. */ + + file = fopen ("bb.out", "a"); + + if (!file) + perror ("bb.out"); + + else + { + struct bb *ptr; + + /* This is somewhat type incorrect, but it avoids worrying about + exactly where time.h is included from. It should be ok unless + a void * differs from other pointer formats, or if sizeof (long) + is < sizeof (time_t). It would be nice if we could assume the + use of rationale standards here. */ + + time ((void *) &time_value); + fprintf (file, "Basic block profiling finished on %s\n", ctime ((void *) &time_value)); + + /* We check the length field explicitly in order to allow compatibility + with older GCC's which did not provide it. */ + + for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) + { + int i; + int func_p = (ptr->nwords >= (long) sizeof (struct bb) + && ptr->nwords <= 1000 + && ptr->functions); + int line_p = (func_p && ptr->line_nums); + int file_p = (func_p && ptr->filenames); + int addr_p = (ptr->addresses != 0); + long ncounts = ptr->ncounts; + long cnt_max = 0; + long line_max = 0; + long addr_max = 0; + int file_len = 0; + int func_len = 0; + int blk_len = num_digits (ncounts, 10); + int cnt_len; + int line_len; + int addr_len; + + fprintf (file, "File %s, %ld basic blocks \n\n", + ptr->filename, ncounts); + + /* Get max values for each field. */ + for (i = 0; i < ncounts; i++) + { + const char *p; + int len; + + if (cnt_max < ptr->counts[i]) + cnt_max = ptr->counts[i]; + + if (addr_p && (unsigned long) addr_max < ptr->addresses[i]) + addr_max = ptr->addresses[i]; + + if (line_p && line_max < ptr->line_nums[i]) + line_max = ptr->line_nums[i]; + + if (func_p) + { + p = (ptr->functions[i]) ? (ptr->functions[i]) : "<none>"; + len = strlen (p); + if (func_len < len) + func_len = len; + } + + if (file_p) + { + p = (ptr->filenames[i]) ? (ptr->filenames[i]) : "<none>"; + len = strlen (p); + if (file_len < len) + file_len = len; + } + } + + addr_len = num_digits (addr_max, 16); + cnt_len = num_digits (cnt_max, 10); + line_len = num_digits (line_max, 10); + + /* Now print out the basic block information. */ + for (i = 0; i < ncounts; i++) + { + fprintf (file, + " Block #%*d: executed %*ld time(s)", + blk_len, i+1, + cnt_len, ptr->counts[i]); + + if (addr_p) + fprintf (file, " address= 0x%.*lx", addr_len, + ptr->addresses[i]); + + if (func_p) + fprintf (file, " function= %-*s", func_len, + (ptr->functions[i]) ? ptr->functions[i] : "<none>"); + + if (line_p) + fprintf (file, " line= %*ld", line_len, ptr->line_nums[i]); + + if (file_p) + fprintf (file, " file= %s", + (ptr->filenames[i]) ? ptr->filenames[i] : "<none>"); + + fprintf (file, "\n"); + } + + fprintf (file, "\n"); + fflush (file); + } + + fprintf (file, "\n\n"); + fclose (file); + } +} + +void +__bb_init_func (struct bb *blocks) +{ + /* User is supposed to check whether the first word is non-0, + but just in case.... */ + + if (blocks->zero_word) + return; + + /* Initialize destructor. */ + if (!bb_head) + atexit (__bb_exit_func); + + /* Set up linked list. */ + blocks->zero_word = 1; + blocks->next = bb_head; + bb_head = blocks; +} + +/* Called before fork or exec - write out profile information gathered so + far and reset it to zero. This avoids duplication or loss of the + profile information gathered so far. */ +void +__bb_fork_func (void) +{ + struct bb *ptr; + + __bb_exit_func (); + for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) + { + long i; + for (i = ptr->ncounts - 1; i >= 0; i--) + ptr->counts[i] = 0; + } +} + +#ifndef MACHINE_STATE_SAVE +#define MACHINE_STATE_SAVE(ID) +#endif +#ifndef MACHINE_STATE_RESTORE +#define MACHINE_STATE_RESTORE(ID) +#endif + +/* Number of buckets in hashtable of basic block addresses. */ + +#define BB_BUCKETS 311 + +/* Maximum length of string in file bb.in. */ + +#define BBINBUFSIZE 500 + +struct bb_edge +{ + struct bb_edge *next; + unsigned long src_addr; + unsigned long dst_addr; + unsigned long count; +}; + +enum bb_func_mode +{ + TRACE_KEEP = 0, TRACE_ON = 1, TRACE_OFF = 2 +}; + +struct bb_func +{ + struct bb_func *next; + char *funcname; + char *filename; + enum bb_func_mode mode; +}; + +/* This is the connection to the outside world. + The BLOCK_PROFILER macro must set __bb.blocks + and __bb.blockno. */ + +struct { + unsigned long blockno; + struct bb *blocks; +} __bb; + +/* Vars to store addrs of source and destination basic blocks + of a jump. */ + +static unsigned long bb_src = 0; +static unsigned long bb_dst = 0; + +static FILE *bb_tracefile = (FILE *) 0; +static struct bb_edge **bb_hashbuckets = (struct bb_edge **) 0; +static struct bb_func *bb_func_head = (struct bb_func *) 0; +static unsigned long bb_callcount = 0; +static int bb_mode = 0; + +static unsigned long *bb_stack = (unsigned long *) 0; +static size_t bb_stacksize = 0; + +static int reported = 0; + +/* Trace modes: +Always : Print execution frequencies of basic blocks + to file bb.out. +bb_mode & 1 != 0 : Dump trace of basic blocks to file bbtrace[.gz] +bb_mode & 2 != 0 : Print jump frequencies to file bb.out. +bb_mode & 4 != 0 : Cut call instructions from basic block flow. +bb_mode & 8 != 0 : Insert return instructions in basic block flow. +*/ + +#ifdef HAVE_POPEN + +/*#include <sys/types.h>*/ +#include <sys/stat.h> +/*#include <malloc.h>*/ + +/* Commands executed by gopen. */ + +#define GOPENDECOMPRESS "gzip -cd " +#define GOPENCOMPRESS "gzip -c >" + +/* Like fopen but pipes through gzip. mode may only be "r" or "w". + If it does not compile, simply replace gopen by fopen and delete + '.gz' from any first parameter to gopen. */ + +static FILE * +gopen (char *fn, char *mode) +{ + int use_gzip; + char *p; + + if (mode[1]) + return (FILE *) 0; + + if (mode[0] != 'r' && mode[0] != 'w') + return (FILE *) 0; + + p = fn + strlen (fn)-1; + use_gzip = ((p[-1] == '.' && (p[0] == 'Z' || p[0] == 'z')) + || (p[-2] == '.' && p[-1] == 'g' && p[0] == 'z')); + + if (use_gzip) + { + if (mode[0]=='r') + { + FILE *f; + char *s = (char *) malloc (sizeof (char) * strlen (fn) + + sizeof (GOPENDECOMPRESS)); + strcpy (s, GOPENDECOMPRESS); + strcpy (s + (sizeof (GOPENDECOMPRESS)-1), fn); + f = popen (s, mode); + free (s); + return f; + } + + else + { + FILE *f; + char *s = (char *) malloc (sizeof (char) * strlen (fn) + + sizeof (GOPENCOMPRESS)); + strcpy (s, GOPENCOMPRESS); + strcpy (s + (sizeof (GOPENCOMPRESS)-1), fn); + if (!(f = popen (s, mode))) + f = fopen (s, mode); + free (s); + return f; + } + } + + else + return fopen (fn, mode); +} + +static int +gclose (FILE *f) +{ + struct stat buf; + + if (f != 0) + { + if (!fstat (fileno (f), &buf) && S_ISFIFO (buf.st_mode)) + return pclose (f); + + return fclose (f); + } + return 0; +} + +#endif /* HAVE_POPEN */ + +/* Called once per program. */ + +static void +__bb_exit_trace_func (void) +{ + FILE *file = fopen ("bb.out", "a"); + struct bb_func *f; + struct bb *b; + + if (!file) + perror ("bb.out"); + + if (bb_mode & 1) + { + if (!bb_tracefile) + perror ("bbtrace"); + else +#ifdef HAVE_POPEN + gclose (bb_tracefile); +#else + fclose (bb_tracefile); +#endif /* HAVE_POPEN */ + } + + /* Check functions in `bb.in'. */ + + if (file) + { + long time_value; + const struct bb_func *p; + int printed_something = 0; + struct bb *ptr; + long blk; + + /* This is somewhat type incorrect. */ + time ((void *) &time_value); + + for (p = bb_func_head; p != (struct bb_func *) 0; p = p->next) + { + for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next) + { + if (!ptr->filename || (p->filename != (char *) 0 && strcmp (p->filename, ptr->filename))) + continue; + for (blk = 0; blk < ptr->ncounts; blk++) + { + if (!strcmp (p->funcname, ptr->functions[blk])) + goto found; + } + } + + if (!printed_something) + { + fprintf (file, "Functions in `bb.in' not executed during basic block profiling on %s\n", ctime ((void *) &time_value)); + printed_something = 1; + } + + fprintf (file, "\tFunction %s", p->funcname); + if (p->filename) + fprintf (file, " of file %s", p->filename); + fprintf (file, "\n" ); + +found: ; + } + + if (printed_something) + fprintf (file, "\n"); + + } + + if (bb_mode & 2) + { + if (!bb_hashbuckets) + { + if (!reported) + { + fprintf (stderr, "Profiler: out of memory\n"); + reported = 1; + } + return; + } + + else if (file) + { + long time_value; + int i; + unsigned long addr_max = 0; + unsigned long cnt_max = 0; + int cnt_len; + int addr_len; + + /* This is somewhat type incorrect, but it avoids worrying about + exactly where time.h is included from. It should be ok unless + a void * differs from other pointer formats, or if sizeof (long) + is < sizeof (time_t). It would be nice if we could assume the + use of rationale standards here. */ + + time ((void *) &time_value); + fprintf (file, "Basic block jump tracing"); + + switch (bb_mode & 12) + { + case 0: + fprintf (file, " (with call)"); + break; + + case 4: + /* Print nothing. */ + break; + + case 8: + fprintf (file, " (with call & ret)"); + break; + + case 12: + fprintf (file, " (with ret)"); + break; + } + + fprintf (file, " finished on %s\n", ctime ((void *) &time_value)); + + for (i = 0; i < BB_BUCKETS; i++) + { + struct bb_edge *bucket = bb_hashbuckets[i]; + for ( ; bucket; bucket = bucket->next ) + { + if (addr_max < bucket->src_addr) + addr_max = bucket->src_addr; + if (addr_max < bucket->dst_addr) + addr_max = bucket->dst_addr; + if (cnt_max < bucket->count) + cnt_max = bucket->count; + } + } + addr_len = num_digits (addr_max, 16); + cnt_len = num_digits (cnt_max, 10); + + for ( i = 0; i < BB_BUCKETS; i++) + { + struct bb_edge *bucket = bb_hashbuckets[i]; + for ( ; bucket; bucket = bucket->next ) + { + fprintf (file, + "Jump from block 0x%.*lx to block 0x%.*lx executed %*lu time(s)\n", + addr_len, bucket->src_addr, + addr_len, bucket->dst_addr, + cnt_len, bucket->count); + } + } + + fprintf (file, "\n"); + + } + } + + if (file) + fclose (file); + + /* Free allocated memory. */ + + f = bb_func_head; + while (f) + { + struct bb_func *old = f; + + f = f->next; + if (old->funcname) free (old->funcname); + if (old->filename) free (old->filename); + free (old); + } + + if (bb_stack) + free (bb_stack); + + if (bb_hashbuckets) + { + int i; + + for (i = 0; i < BB_BUCKETS; i++) + { + struct bb_edge *old, *bucket = bb_hashbuckets[i]; + + while (bucket) + { + old = bucket; + bucket = bucket->next; + free (old); + } + } + free (bb_hashbuckets); + } + + for (b = bb_head; b; b = b->next) + if (b->flags) free (b->flags); +} + +/* Called once per program. */ + +static void +__bb_init_prg (void) +{ + FILE *file; + char buf[BBINBUFSIZE]; + const char *p; + const char *pos; + enum bb_func_mode m; + int i; + + /* Initialize destructor. */ + atexit (__bb_exit_func); + + if (!(file = fopen ("bb.in", "r"))) + return; + + while(fgets (buf, BBINBUFSIZE, file) != 0) + { + i = strlen (buf); + if (buf[i-1] == '\n') + buf[--i] = '\0'; + + p = buf; + if (*p == '-') + { + m = TRACE_OFF; + p++; + } + else + { + m = TRACE_ON; + } + if (!strcmp (p, "__bb_trace__")) + bb_mode |= 1; + else if (!strcmp (p, "__bb_jumps__")) + bb_mode |= 2; + else if (!strcmp (p, "__bb_hidecall__")) + bb_mode |= 4; + else if (!strcmp (p, "__bb_showret__")) + bb_mode |= 8; + else + { + struct bb_func *f = (struct bb_func *) malloc (sizeof (struct bb_func)); + if (f) + { + unsigned long l; + f->next = bb_func_head; + if ((pos = strchr (p, ':'))) + { + if (!(f->funcname = (char *) malloc (strlen (pos+1)+1))) + continue; + strcpy (f->funcname, pos+1); + l = pos-p; + if ((f->filename = (char *) malloc (l+1))) + { + strncpy (f->filename, p, l); + f->filename[l] = '\0'; + } + else + f->filename = (char *) 0; + } + else + { + if (!(f->funcname = (char *) malloc (strlen (p)+1))) + continue; + strcpy (f->funcname, p); + f->filename = (char *) 0; + } + f->mode = m; + bb_func_head = f; + } + } + } + fclose (file); + +#ifdef HAVE_POPEN + + if (bb_mode & 1) + bb_tracefile = gopen ("bbtrace.gz", "w"); + +#else + + if (bb_mode & 1) + bb_tracefile = fopen ("bbtrace", "w"); + +#endif /* HAVE_POPEN */ + + if (bb_mode & 2) + { + bb_hashbuckets = (struct bb_edge **) + malloc (BB_BUCKETS * sizeof (struct bb_edge *)); + if (bb_hashbuckets) + /* Use a loop here rather than calling bzero to avoid having to + conditionalize its existance. */ + for (i = 0; i < BB_BUCKETS; i++) + bb_hashbuckets[i] = 0; + } + + if (bb_mode & 12) + { + bb_stacksize = 10; + bb_stack = (unsigned long *) malloc (bb_stacksize * sizeof (*bb_stack)); + } + + /* Initialize destructor. */ + atexit (__bb_exit_trace_func); +} + +/* Called upon entering a basic block. */ + +void +__bb_trace_func (void) +{ + struct bb_edge *bucket; + + MACHINE_STATE_SAVE("1") + + if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF))) + goto skip; + + bb_dst = __bb.blocks->addresses[__bb.blockno]; + __bb.blocks->counts[__bb.blockno]++; + + if (bb_tracefile) + { + fwrite (&bb_dst, sizeof (unsigned long), 1, bb_tracefile); + } + + if (bb_hashbuckets) + { + struct bb_edge **startbucket, **oldnext; + + oldnext = startbucket + = & bb_hashbuckets[ (((int) bb_src*8) ^ (int) bb_dst) % BB_BUCKETS ]; + bucket = *startbucket; + + for (bucket = *startbucket; bucket; + oldnext = &(bucket->next), bucket = *oldnext) + { + if (bucket->src_addr == bb_src + && bucket->dst_addr == bb_dst) + { + bucket->count++; + *oldnext = bucket->next; + bucket->next = *startbucket; + *startbucket = bucket; + goto ret; + } + } + + bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge)); + + if (!bucket) + { + if (!reported) + { + fprintf (stderr, "Profiler: out of memory\n"); + reported = 1; + } + } + + else + { + bucket->src_addr = bb_src; + bucket->dst_addr = bb_dst; + bucket->next = *startbucket; + *startbucket = bucket; + bucket->count = 1; + } + } + +ret: + bb_src = bb_dst; + +skip: + ; + + MACHINE_STATE_RESTORE("1") + +} + +/* Called when returning from a function and `__bb_showret__' is set. */ + +static void +__bb_trace_func_ret (void) +{ + struct bb_edge *bucket; + + if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF))) + goto skip; + + if (bb_hashbuckets) + { + struct bb_edge **startbucket, **oldnext; + + oldnext = startbucket + = & bb_hashbuckets[ (((int) bb_dst * 8) ^ (int) bb_src) % BB_BUCKETS ]; + bucket = *startbucket; + + for (bucket = *startbucket; bucket; + oldnext = &(bucket->next), bucket = *oldnext) + { + if (bucket->src_addr == bb_dst + && bucket->dst_addr == bb_src) + { + bucket->count++; + *oldnext = bucket->next; + bucket->next = *startbucket; + *startbucket = bucket; + goto ret; + } + } + + bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge)); + + if (!bucket) + { + if (!reported) + { + fprintf (stderr, "Profiler: out of memory\n"); + reported = 1; + } + } + + else + { + bucket->src_addr = bb_dst; + bucket->dst_addr = bb_src; + bucket->next = *startbucket; + *startbucket = bucket; + bucket->count = 1; + } + } + +ret: + bb_dst = bb_src; + +skip: + ; + +} + +/* Called upon entering the first function of a file. */ + +static void +__bb_init_file (struct bb *blocks) +{ + + const struct bb_func *p; + long blk, ncounts = blocks->ncounts; + const char **functions = blocks->functions; + + /* Set up linked list. */ + blocks->zero_word = 1; + blocks->next = bb_head; + bb_head = blocks; + + blocks->flags = 0; + if (!bb_func_head + || !(blocks->flags = (char *) malloc (sizeof (char) * blocks->ncounts))) + return; + + for (blk = 0; blk < ncounts; blk++) + blocks->flags[blk] = 0; + + for (blk = 0; blk < ncounts; blk++) + { + for (p = bb_func_head; p; p = p->next) + { + if (!strcmp (p->funcname, functions[blk]) + && (!p->filename || !strcmp (p->filename, blocks->filename))) + { + blocks->flags[blk] |= p->mode; + } + } + } + +} + +/* Called when exiting from a function. */ + +void +__bb_trace_ret (void) +{ + + MACHINE_STATE_SAVE("2") + + if (bb_callcount) + { + if ((bb_mode & 12) && bb_stacksize > bb_callcount) + { + bb_src = bb_stack[bb_callcount]; + if (bb_mode & 8) + __bb_trace_func_ret (); + } + + bb_callcount -= 1; + } + + MACHINE_STATE_RESTORE("2") + +} + +/* Called when entering a function. */ + +void +__bb_init_trace_func (struct bb *blocks, unsigned long blockno) +{ + static int trace_init = 0; + + MACHINE_STATE_SAVE("3") + + if (!blocks->zero_word) + { + if (!trace_init) + { + trace_init = 1; + __bb_init_prg (); + } + __bb_init_file (blocks); + } + + if (bb_callcount) + { + + bb_callcount += 1; + + if (bb_mode & 12) + { + if (bb_callcount >= bb_stacksize) + { + size_t newsize = bb_callcount + 100; + + bb_stack = (unsigned long *) realloc (bb_stack, newsize); + if (! bb_stack) + { + if (!reported) + { + fprintf (stderr, "Profiler: out of memory\n"); + reported = 1; + } + bb_stacksize = 0; + goto stack_overflow; + } + bb_stacksize = newsize; + } + bb_stack[bb_callcount] = bb_src; + + if (bb_mode & 4) + bb_src = 0; + + } + +stack_overflow:; + + } + + else if (blocks->flags && (blocks->flags[blockno] & TRACE_ON)) + { + bb_callcount = 1; + bb_src = 0; + + if (bb_stack) + bb_stack[bb_callcount] = bb_src; + } + + MACHINE_STATE_RESTORE("3") +} + |