diff options
Diffstat (limited to 'libgloss/rx/mcount.c')
-rw-r--r-- | libgloss/rx/mcount.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/libgloss/rx/mcount.c b/libgloss/rx/mcount.c new file mode 100644 index 000000000..4a72fe1c9 --- /dev/null +++ b/libgloss/rx/mcount.c @@ -0,0 +1,422 @@ +/*- + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* This file implements a subset of the profiling support functions. + It has been copied and adapted from mcount.c, gmon.c and gmon.h in + the glibc sources. + Since we do not have access to a timer interrupt in the simulator + the histogram and basic block information is not generated. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +/* Fraction of text space to allocate for histogram counters here, 1/2. */ +#define HISTFRACTION 2 + +/* Fraction of text space to allocate for from hash buckets. + The value of HASHFRACTION is based on the minimum number of bytes + of separation between two subroutine call points in the object code. + Given MIN_SUBR_SEPARATION bytes of separation the value of + HASHFRACTION is calculated as: + + HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof (short) - 1); + + For example, on the VAX, the shortest two call sequence is: + + calls $0,(r0) + calls $0,(r0) + + which is separated by only three bytes, thus HASHFRACTION is + calculated as: + + HASHFRACTION = 3 / (2 * 2 - 1) = 1 + + Note that the division above rounds down, thus if MIN_SUBR_FRACTION + is less than three, this algorithm will not work! + + In practice, however, call instructions are rarely at a minimal + distance. Hence, we will define HASHFRACTION to be 2 across all + architectures. This saves a reasonable amount of space for + profiling data structures without (in practice) sacrificing + any granularity. */ +#define HASHFRACTION 2 + +/* Percent of text space to allocate for tostructs. + This is a heuristic; we will fail with a warning when profiling + programs with a very large number of very small functions, but + that's normally OK. + 2 is probably still a good value for normal programs. + Profiling a test case with 64000 small functions will work if + you raise this value to 3 and link statically (which bloats the + text size, thus raising the number of arcs expected by the heuristic). */ +#define ARCDENSITY 3 + +/* Always allocate at least this many tostructs. This hides the + inadequacy of the ARCDENSITY heuristic, at least for small programs. */ +#define MINARCS 50 + +/* Maximum number of arcs we want to allow. + Used to be max representable value of ARCINDEX minus 2, but now + that ARCINDEX is a long, that's too large; we don't really want + to allow a 48 gigabyte table. + The old value of 1<<16 wasn't high enough in practice for large C++ + programs; will 1<<20 be adequate for long? FIXME */ +#define MAXARCS (1 << 20) + +#define SCALE_1_TO_1 0x10000L + +#define GMON_MAGIC "gmon" /* Magic cookie. */ +#define GMON_VERSION 1 /* Version number. */ + + +/* General rounding functions. */ +#define ROUNDDOWN(x ,y) (((x) / (y)) * (y)) +#define ROUNDUP(x, y) ((((x) + (y) - 1) / (y)) * (y)) + +struct tostruct +{ + unsigned long selfpc; + unsigned long count; + unsigned long link; +}; + +/* Possible states of profiling. */ +enum profiling_state +{ + GMON_PROF_OFF, + GMON_PROF_ON, + GMON_PROF_BUSY, + GMON_PROF_ERROR +}; + +/* The profiling data structures are housed in this structure. */ +struct gmonparam +{ + enum profiling_state state; + unsigned short * kcount; + unsigned long kcountsize; + unsigned long * froms; + unsigned long fromssize; + struct tostruct * tos; + unsigned long tossize; + long tolimit; + unsigned long lowpc; + unsigned long highpc; + unsigned long textsize; + unsigned long hashfraction; + long log_hashfraction; +}; + +/* Raw header as it appears in the gmon.out file (without padding). + This header always comes first and is then followed by a series + records defined below. */ +struct gmon_hdr +{ + char cookie[4]; + char version[4]; + char spare[3 * 4]; +}; + +/* Types of records in this file. */ +typedef enum +{ + GMON_TAG_TIME_HIST = 0, + GMON_TAG_CG_ARC = 1, +} GMON_Record_Tag; + +struct gmon_cg_arc_record +{ + char tag; /* Set to GMON_TAG_CG_ARC. */ + char from_pc[sizeof (char *)]; /* Address within caller's body. */ + char self_pc[sizeof (char *)]; /* Address within callee's body. */ + char count[4]; /* Number of arc traversals. */ +}; + + +/* Forward declarations. */ +void _mcount_internal (unsigned long); +void _monstartup (unsigned long, unsigned long); +void _mcleanup (void); + +static struct gmonparam _gmonparam; + +void +_mcount_internal (unsigned long frompc) +{ + unsigned long selfpc = frompc; + unsigned long * frompcindex; + struct tostruct * top; + struct tostruct * prevtop; + struct gmonparam * p; + unsigned long toindex; + int i; + + p = & _gmonparam; + + /* Check that we are profiling and that we aren't recursively invoked. + NB/ This version is not thread-safe. */ + if (p->state != GMON_PROF_ON) + return; + p->state = GMON_PROF_BUSY; + + /* Check that frompcindex is a reasonable pc value. + For example: signal catchers get called from the stack, + not from text space. Too bad. */ + frompc -= p->lowpc; + if (frompc > p->textsize) + goto done; + + i = frompc >> p->log_hashfraction; + + frompcindex = p->froms + i; + toindex = * frompcindex; + + if (toindex == 0) + { + /* First time traversing this arc. */ + toindex = ++ p->tos[0].link; + if (toindex >= p->tolimit) + /* Halt further profiling. */ + goto overflow; + + * frompcindex = toindex; + top = p->tos + toindex; + top->selfpc = selfpc; + top->count = 1; + top->link = 0; + goto done; + } + + top = p->tos + toindex; + + if (top->selfpc == selfpc) + { + /* Arc at front of chain: usual case. */ + top->count ++; + goto done; + } + + /* Have to go looking down chain for it. + Top points to what we are looking at, + prevtop points to previous top. + We know it is not at the head of the chain. */ + for (;;) + { + if (top->link == 0) + { + /* Top is end of the chain and none of the chain + had top->selfpc == selfpc. So we allocate a + new tostruct and link it to the head of the + chain. */ + toindex = ++ p->tos[0].link; + if (toindex >= p->tolimit) + goto overflow; + + top = p->tos + toindex; + top->selfpc = selfpc; + top->count = 1; + top->link = * frompcindex; + * frompcindex = toindex; + goto done; + } + + /* Otherwise, check the next arc on the chain. */ + prevtop = top; + top = p->tos + top->link; + + if (top->selfpc == selfpc) + { + /* There it is. Increment its count + move it to the head of the chain. */ + top->count ++; + toindex = prevtop->link; + prevtop->link = top->link; + top->link = * frompcindex; + * frompcindex = toindex; + goto done; + } + } + + done: + p->state = GMON_PROF_ON; + return; + + overflow: + p->state = GMON_PROF_ERROR; + return; +} + +void +_monstartup (unsigned long lowpc, unsigned long highpc) +{ + char * cp; + struct gmonparam * p = & _gmonparam; + + /* If the calloc() function has been instrumented we must make sure + that it is not profiled until we are ready. */ + p->state = GMON_PROF_BUSY; + + /* Round lowpc and highpc to multiples of the density we're using + so the rest of the scaling (here and in gprof) stays in ints. */ + p->lowpc = ROUNDDOWN (lowpc, HISTFRACTION * sizeof (* p->kcount)); + p->highpc = ROUNDUP (highpc, HISTFRACTION * sizeof (* p->kcount)); + p->textsize = p->highpc - p->lowpc; + p->kcountsize = ROUNDUP (p->textsize / HISTFRACTION, sizeof (*p->froms)); + p->hashfraction = HASHFRACTION; + p->log_hashfraction = -1; + p->log_hashfraction = ffs (p->hashfraction * sizeof (*p->froms)) - 1; + p->fromssize = p->textsize / HASHFRACTION; + p->tolimit = p->textsize * ARCDENSITY / 100; + + if (p->tolimit < MINARCS) + p->tolimit = MINARCS; + else if (p->tolimit > MAXARCS) + p->tolimit = MAXARCS; + + p->tossize = p->tolimit * sizeof (struct tostruct); + + cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1); + if (cp == NULL) + { + write (2, "monstartup: out of memory\n", 26); + p->tos = NULL; + p->state = GMON_PROF_ERROR; + return; + } + + p->tos = (struct tostruct *) cp; + cp += p->tossize; + p->kcount = (unsigned short *) cp; + cp += p->kcountsize; + p->froms = (unsigned long *) cp; + + p->tos[0].link = 0; + p->state = GMON_PROF_ON; +} + + +static void +write_call_graph (int fd) +{ +#define NARCS_PER_WRITE 32 + + struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITE] + __attribute__ ((aligned (__alignof__ (char *)))); + unsigned long from_index; + unsigned long to_index; + unsigned long from_len; + unsigned long frompc; + int nfilled; + + for (nfilled = 0; nfilled < NARCS_PER_WRITE; ++ nfilled) + raw_arc[nfilled].tag = GMON_TAG_CG_ARC; + + nfilled = 0; + from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms); + + for (from_index = 0; from_index < from_len; ++from_index) + { + if (_gmonparam.froms[from_index] == 0) + continue; + + frompc = _gmonparam.lowpc; + frompc += (from_index * _gmonparam.hashfraction + * sizeof (*_gmonparam.froms)); + + for (to_index = _gmonparam.froms[from_index]; + to_index != 0; + to_index = _gmonparam.tos[to_index].link) + { + struct gmon_cg_arc_record * arc = raw_arc + nfilled; + + memcpy (arc->from_pc, & frompc, sizeof (arc->from_pc)); + memcpy (arc->self_pc, & _gmonparam.tos[to_index].selfpc, sizeof (arc->self_pc)); + memcpy (arc->count, & _gmonparam.tos[to_index].count, sizeof (arc->count)); + + if (++ nfilled == NARCS_PER_WRITE) + { + write (fd, raw_arc, sizeof raw_arc); + nfilled = 0; + } + } + } + + if (nfilled > 0) + write (fd, raw_arc, nfilled * sizeof (raw_arc[0])); +} + +#include <errno.h> + +static void +write_gmon (void) +{ + struct gmon_hdr ghdr __attribute__ ((aligned (__alignof__ (int)))); + int fd; + + fd = open ("gmon.out", O_CREAT|O_TRUNC|O_WRONLY, 0666); + if (fd < 0) + { + write (2, "_mcleanup: could not create gmon.out\n", 37); + return; + } + + /* Write gmon.out header: */ + memset (& ghdr, '\0', sizeof (ghdr)); + memcpy (ghdr.cookie, GMON_MAGIC, sizeof (ghdr.cookie)); + * (unsigned long *) ghdr.version = GMON_VERSION; + write (fd, & ghdr, sizeof (ghdr)); + + /* We do not have histogram or basic block information, + so we do not generate these parts of the gmon.out file. */ + + /* Write call-graph. */ + write_call_graph (fd); + + close (fd); +} + +void +_mcleanup (void) +{ + if (_gmonparam.state != GMON_PROF_ERROR) + { + _gmonparam.state = GMON_PROF_OFF; + write_gmon (); + } + + /* Free the memory. */ + if (_gmonparam.tos != NULL) + { + free (_gmonparam.tos); + _gmonparam.tos = NULL; + } +} |