| [444] | 1 | /*- | 
|---|
 | 2 |  * Copyright (c) 1983, 1992, 1993 | 
|---|
 | 3 |  *      The Regents of the University of California.  All rights reserved. | 
|---|
 | 4 |  * | 
|---|
 | 5 |  * Redistribution and use in source and binary forms, with or without | 
|---|
 | 6 |  * modification, are permitted provided that the following conditions | 
|---|
 | 7 |  * are met: | 
|---|
 | 8 |  * 1. Redistributions of source code must retain the above copyright | 
|---|
 | 9 |  *    notice, this list of conditions and the following disclaimer. | 
|---|
 | 10 |  * 2. Redistributions in binary form must reproduce the above copyright | 
|---|
 | 11 |  *    notice, this list of conditions and the following disclaimer in the | 
|---|
 | 12 |  *    documentation and/or other materials provided with the distribution. | 
|---|
 | 13 |  * 4. Neither the name of the University nor the names of its contributors | 
|---|
 | 14 |  *    may be used to endorse or promote products derived from this software | 
|---|
 | 15 |  *    without specific prior written permission. | 
|---|
 | 16 |  * | 
|---|
 | 17 |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
|---|
 | 18 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|---|
 | 19 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|---|
 | 20 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
|---|
 | 21 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|---|
 | 22 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|---|
 | 23 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|---|
 | 24 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|---|
 | 25 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|---|
 | 26 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|---|
 | 27 |  * SUCH DAMAGE. | 
|---|
 | 28 |  */ | 
|---|
 | 29 |  | 
|---|
 | 30 | /* This file implements a subset of the profiling support functions. | 
|---|
 | 31 |    It has been copied and adapted from mcount.c, gmon.c and gmon.h in | 
|---|
 | 32 |    the glibc sources. | 
|---|
 | 33 |    Since we do not have access to a timer interrupt in the simulator | 
|---|
 | 34 |    the histogram and basic block information is not generated.  */ | 
|---|
 | 35 |  | 
|---|
 | 36 | #include <stdio.h> | 
|---|
 | 37 | #include <stdlib.h> | 
|---|
 | 38 | #include <string.h> | 
|---|
 | 39 | #include <unistd.h> | 
|---|
 | 40 | #include <fcntl.h> | 
|---|
 | 41 |  | 
|---|
 | 42 | /* Fraction of text space to allocate for histogram counters here, 1/2.  */ | 
|---|
 | 43 | #define HISTFRACTION    2 | 
|---|
 | 44 |  | 
|---|
 | 45 | /* Fraction of text space to allocate for from hash buckets. | 
|---|
 | 46 |    The value of HASHFRACTION is based on the minimum number of bytes | 
|---|
 | 47 |    of separation between two subroutine call points in the object code. | 
|---|
 | 48 |    Given MIN_SUBR_SEPARATION bytes of separation the value of | 
|---|
 | 49 |    HASHFRACTION is calculated as: | 
|---|
 | 50 |  | 
|---|
 | 51 |         HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof (short) - 1); | 
|---|
 | 52 |  | 
|---|
 | 53 |    For example, on the VAX, the shortest two call sequence is: | 
|---|
 | 54 |  | 
|---|
 | 55 |         calls   $0,(r0) | 
|---|
 | 56 |         calls   $0,(r0) | 
|---|
 | 57 |  | 
|---|
 | 58 |    which is separated by only three bytes, thus HASHFRACTION is | 
|---|
 | 59 |    calculated as: | 
|---|
 | 60 |  | 
|---|
 | 61 |         HASHFRACTION = 3 / (2 * 2 - 1) = 1 | 
|---|
 | 62 |  | 
|---|
 | 63 |    Note that the division above rounds down, thus if MIN_SUBR_FRACTION | 
|---|
 | 64 |    is less than three, this algorithm will not work! | 
|---|
 | 65 |  | 
|---|
 | 66 |    In practice, however, call instructions are rarely at a minimal | 
|---|
 | 67 |    distance.  Hence, we will define HASHFRACTION to be 2 across all | 
|---|
 | 68 |    architectures.  This saves a reasonable amount of space for | 
|---|
 | 69 |    profiling data structures without (in practice) sacrificing | 
|---|
 | 70 |    any granularity.  */ | 
|---|
 | 71 | #define HASHFRACTION    2 | 
|---|
 | 72 |  | 
|---|
 | 73 | /* Percent of text space to allocate for tostructs. | 
|---|
 | 74 |    This is a heuristic; we will fail with a warning when profiling | 
|---|
 | 75 |    programs with a very large number of very small functions, but | 
|---|
 | 76 |    that's normally OK. | 
|---|
 | 77 |    2 is probably still a good value for normal programs. | 
|---|
 | 78 |    Profiling a test case with 64000 small functions will work if | 
|---|
 | 79 |    you raise this value to 3 and link statically (which bloats the | 
|---|
 | 80 |    text size, thus raising the number of arcs expected by the heuristic).  */ | 
|---|
 | 81 | #define ARCDENSITY      3 | 
|---|
 | 82 |  | 
|---|
 | 83 | /* Always allocate at least this many tostructs.  This hides the | 
|---|
 | 84 |    inadequacy of the ARCDENSITY heuristic, at least for small programs.  */ | 
|---|
 | 85 | #define MINARCS         50 | 
|---|
 | 86 |  | 
|---|
 | 87 | /* Maximum number of arcs we want to allow. | 
|---|
 | 88 |    Used to be max representable value of ARCINDEX minus 2, but now | 
|---|
 | 89 |    that ARCINDEX is a long, that's too large; we don't really want | 
|---|
 | 90 |    to allow a 48 gigabyte table. | 
|---|
 | 91 |    The old value of 1<<16 wasn't high enough in practice for large C++ | 
|---|
 | 92 |    programs; will 1<<20 be adequate for long?  FIXME  */ | 
|---|
 | 93 | #define MAXARCS         (1L << 20) | 
|---|
 | 94 |  | 
|---|
 | 95 | #define SCALE_1_TO_1    0x10000L | 
|---|
 | 96 |  | 
|---|
 | 97 | #define GMON_MAGIC      "gmon"  /* Magic cookie.  */ | 
|---|
 | 98 | #define GMON_VERSION    1       /* Version number.  */ | 
|---|
 | 99 |  | 
|---|
 | 100 |  | 
|---|
 | 101 | /* General rounding functions.  */ | 
|---|
 | 102 | #define ROUNDDOWN(x ,y) (((x) / (y)) * (y)) | 
|---|
 | 103 | #define ROUNDUP(x, y)   ((((x) + (y) - 1) / (y)) * (y)) | 
|---|
 | 104 |  | 
|---|
 | 105 | struct tostruct | 
|---|
 | 106 | { | 
|---|
 | 107 |   unsigned long selfpc; | 
|---|
 | 108 |   unsigned long count; | 
|---|
 | 109 |   unsigned long link; | 
|---|
 | 110 | }; | 
|---|
 | 111 |  | 
|---|
 | 112 | /* Possible states of profiling.  */ | 
|---|
 | 113 | enum profiling_state | 
|---|
 | 114 | { | 
|---|
 | 115 |   GMON_PROF_OFF, | 
|---|
 | 116 |   GMON_PROF_ON, | 
|---|
 | 117 |   GMON_PROF_BUSY, | 
|---|
 | 118 |   GMON_PROF_ERROR | 
|---|
 | 119 | }; | 
|---|
 | 120 |  | 
|---|
 | 121 | /* The profiling data structures are housed in this structure.  */ | 
|---|
 | 122 | struct gmonparam | 
|---|
 | 123 | { | 
|---|
 | 124 |   enum profiling_state state; | 
|---|
 | 125 |   unsigned short *     kcount; | 
|---|
 | 126 |   unsigned long        kcountsize; | 
|---|
 | 127 |   unsigned long *      froms; | 
|---|
 | 128 |   unsigned long        fromssize; | 
|---|
 | 129 |   struct tostruct *    tos; | 
|---|
 | 130 |   unsigned long        tossize; | 
|---|
 | 131 |   long                 tolimit; | 
|---|
 | 132 |   unsigned long        lowpc; | 
|---|
 | 133 |   unsigned long        highpc; | 
|---|
 | 134 |   unsigned long        textsize; | 
|---|
 | 135 |   unsigned long        hashfraction; | 
|---|
 | 136 |   long                 log_hashfraction; | 
|---|
 | 137 | }; | 
|---|
 | 138 |  | 
|---|
 | 139 | /* Raw header as it appears in the gmon.out file (without padding). | 
|---|
 | 140 |    This header always comes first and is then followed by a series | 
|---|
 | 141 |    records defined below.  */ | 
|---|
 | 142 | struct gmon_hdr | 
|---|
 | 143 | { | 
|---|
 | 144 |   char cookie[4]; | 
|---|
 | 145 |   char version[4]; | 
|---|
 | 146 |   char spare[3 * 4]; | 
|---|
 | 147 | }; | 
|---|
 | 148 |  | 
|---|
 | 149 | /* Types of records in this file.  */ | 
|---|
 | 150 | typedef enum | 
|---|
 | 151 | { | 
|---|
 | 152 |   GMON_TAG_TIME_HIST = 0, | 
|---|
 | 153 |   GMON_TAG_CG_ARC = 1, | 
|---|
 | 154 | } GMON_Record_Tag; | 
|---|
 | 155 |  | 
|---|
 | 156 | struct gmon_cg_arc_record | 
|---|
 | 157 | { | 
|---|
 | 158 |   char tag;                             /* Set to GMON_TAG_CG_ARC.  */ | 
|---|
 | 159 |   char from_pc[sizeof (char *)];        /* Address within caller's body.  */ | 
|---|
 | 160 |   char self_pc[sizeof (char *)];        /* Address within callee's body.  */ | 
|---|
 | 161 |   char count[4];                        /* Number of arc traversals.  */ | 
|---|
 | 162 | }; | 
|---|
 | 163 |  | 
|---|
 | 164 |  | 
|---|
 | 165 | /* Forward declarations.  */ | 
|---|
 | 166 | void _mcount_internal (unsigned long); | 
|---|
 | 167 | void _monstartup (unsigned long, unsigned long); | 
|---|
 | 168 | void _mcleanup (void); | 
|---|
 | 169 |  | 
|---|
 | 170 | static struct gmonparam _gmonparam; | 
|---|
 | 171 |  | 
|---|
 | 172 | void | 
|---|
 | 173 | _mcount_internal (unsigned long frompc) | 
|---|
 | 174 | { | 
|---|
 | 175 |   unsigned long      selfpc = frompc; | 
|---|
 | 176 |   unsigned long *    frompcindex; | 
|---|
 | 177 |   struct tostruct *  top; | 
|---|
 | 178 |   struct tostruct *  prevtop; | 
|---|
 | 179 |   struct gmonparam * p; | 
|---|
 | 180 |   unsigned long      toindex; | 
|---|
 | 181 |   int                i; | 
|---|
 | 182 |  | 
|---|
 | 183 |   p = & _gmonparam; | 
|---|
 | 184 |  | 
|---|
 | 185 |   /* Check that we are profiling and that we aren't recursively invoked. | 
|---|
 | 186 |      NB/ This version is not thread-safe.  */ | 
|---|
 | 187 |   if (p->state != GMON_PROF_ON) | 
|---|
 | 188 |     return; | 
|---|
 | 189 |   p->state = GMON_PROF_BUSY; | 
|---|
 | 190 |  | 
|---|
 | 191 |   /* Check that frompcindex is a reasonable pc value. | 
|---|
 | 192 |      For example: signal catchers get called from the stack, | 
|---|
 | 193 |      not from text space.  Too bad.  */ | 
|---|
 | 194 |   frompc -= p->lowpc; | 
|---|
 | 195 |   if (frompc > p->textsize) | 
|---|
 | 196 |     goto done; | 
|---|
 | 197 |  | 
|---|
 | 198 |   i = frompc >> p->log_hashfraction; | 
|---|
 | 199 |  | 
|---|
 | 200 |   frompcindex = p->froms + i; | 
|---|
 | 201 |   toindex = * frompcindex; | 
|---|
 | 202 |  | 
|---|
 | 203 |   if (toindex == 0) | 
|---|
 | 204 |     { | 
|---|
 | 205 |       /* First time traversing this arc.  */ | 
|---|
 | 206 |       toindex = ++ p->tos[0].link; | 
|---|
 | 207 |       if (toindex >= p->tolimit) | 
|---|
 | 208 |         /* Halt further profiling.  */ | 
|---|
 | 209 |         goto overflow; | 
|---|
 | 210 |  | 
|---|
 | 211 |       * frompcindex = toindex; | 
|---|
 | 212 |       top = p->tos + toindex; | 
|---|
 | 213 |       top->selfpc = selfpc; | 
|---|
 | 214 |       top->count = 1; | 
|---|
 | 215 |       top->link = 0; | 
|---|
 | 216 |       goto done; | 
|---|
 | 217 |     } | 
|---|
 | 218 |  | 
|---|
 | 219 |   top = p->tos + toindex; | 
|---|
 | 220 |  | 
|---|
 | 221 |   if (top->selfpc == selfpc) | 
|---|
 | 222 |     { | 
|---|
 | 223 |       /* Arc at front of chain: usual case.  */ | 
|---|
 | 224 |       top->count ++; | 
|---|
 | 225 |       goto done; | 
|---|
 | 226 |     } | 
|---|
 | 227 |  | 
|---|
 | 228 |   /* Have to go looking down chain for it. | 
|---|
 | 229 |      Top points to what we are looking at, | 
|---|
 | 230 |      prevtop points to previous top. | 
|---|
 | 231 |      We know it is not at the head of the chain.  */ | 
|---|
 | 232 |   for (;;) | 
|---|
 | 233 |     { | 
|---|
 | 234 |       if (top->link == 0) | 
|---|
 | 235 |         { | 
|---|
 | 236 |           /* Top is end of the chain and none of the chain | 
|---|
 | 237 |              had top->selfpc == selfpc.  So we allocate a | 
|---|
 | 238 |              new tostruct and link it to the head of the | 
|---|
 | 239 |              chain.  */ | 
|---|
 | 240 |           toindex = ++ p->tos[0].link; | 
|---|
 | 241 |           if (toindex >= p->tolimit) | 
|---|
 | 242 |             goto overflow; | 
|---|
 | 243 |  | 
|---|
 | 244 |           top = p->tos + toindex; | 
|---|
 | 245 |           top->selfpc = selfpc; | 
|---|
 | 246 |           top->count = 1; | 
|---|
 | 247 |           top->link = * frompcindex; | 
|---|
 | 248 |           * frompcindex = toindex; | 
|---|
 | 249 |           goto done; | 
|---|
 | 250 |         } | 
|---|
 | 251 |  | 
|---|
 | 252 |       /* Otherwise, check the next arc on the chain.  */ | 
|---|
 | 253 |       prevtop = top; | 
|---|
 | 254 |       top = p->tos + top->link; | 
|---|
 | 255 |  | 
|---|
 | 256 |       if (top->selfpc == selfpc) | 
|---|
 | 257 |         { | 
|---|
 | 258 |           /* There it is.  Increment its count | 
|---|
 | 259 |              move it to the head of the chain.  */ | 
|---|
 | 260 |           top->count ++; | 
|---|
 | 261 |           toindex = prevtop->link; | 
|---|
 | 262 |           prevtop->link = top->link; | 
|---|
 | 263 |           top->link = * frompcindex; | 
|---|
 | 264 |           * frompcindex = toindex; | 
|---|
 | 265 |           goto done; | 
|---|
 | 266 |         } | 
|---|
 | 267 |     } | 
|---|
 | 268 |  | 
|---|
 | 269 |  done: | 
|---|
 | 270 |   p->state = GMON_PROF_ON; | 
|---|
 | 271 |   return; | 
|---|
 | 272 |  | 
|---|
 | 273 |  overflow: | 
|---|
 | 274 |   p->state = GMON_PROF_ERROR; | 
|---|
 | 275 |   return; | 
|---|
 | 276 | } | 
|---|
 | 277 |  | 
|---|
 | 278 | void | 
|---|
 | 279 | _monstartup (unsigned long lowpc, unsigned long highpc) | 
|---|
 | 280 | { | 
|---|
 | 281 |   char * cp; | 
|---|
 | 282 |   struct gmonparam * p = & _gmonparam; | 
|---|
 | 283 |  | 
|---|
 | 284 |   /* If the calloc() function has been instrumented we must make sure | 
|---|
 | 285 |      that it is not profiled until we are ready.  */ | 
|---|
 | 286 |   p->state = GMON_PROF_BUSY; | 
|---|
 | 287 |  | 
|---|
 | 288 |   /* Round lowpc and highpc to multiples of the density we're using | 
|---|
 | 289 |      so the rest of the scaling (here and in gprof) stays in ints.  */ | 
|---|
 | 290 |   p->lowpc            = ROUNDDOWN (lowpc, HISTFRACTION * sizeof (* p->kcount)); | 
|---|
 | 291 |   p->highpc           = ROUNDUP (highpc, HISTFRACTION * sizeof (* p->kcount)); | 
|---|
 | 292 |   p->textsize         = p->highpc - p->lowpc; | 
|---|
 | 293 |   p->kcountsize       = ROUNDUP (p->textsize / HISTFRACTION, sizeof (*p->froms)); | 
|---|
 | 294 |   p->hashfraction     = HASHFRACTION; | 
|---|
 | 295 |   p->log_hashfraction = -1; | 
|---|
 | 296 |   p->log_hashfraction = ffs (p->hashfraction * sizeof (*p->froms)) - 1; | 
|---|
 | 297 |   p->fromssize        = p->textsize / HASHFRACTION; | 
|---|
 | 298 |   p->tolimit          = p->textsize * ARCDENSITY / 100; | 
|---|
 | 299 |  | 
|---|
 | 300 |   if (p->tolimit < MINARCS) | 
|---|
 | 301 |     p->tolimit = MINARCS; | 
|---|
 | 302 |   else if (p->tolimit > MAXARCS) | 
|---|
 | 303 |     p->tolimit = MAXARCS; | 
|---|
 | 304 |  | 
|---|
 | 305 |   p->tossize          = p->tolimit * sizeof (struct tostruct); | 
|---|
 | 306 |  | 
|---|
 | 307 |   cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1); | 
|---|
 | 308 |   if (cp == NULL) | 
|---|
 | 309 |     { | 
|---|
 | 310 |       write (2, "monstartup: out of memory\n", 26); | 
|---|
 | 311 |       p->tos = NULL; | 
|---|
 | 312 |       p->state = GMON_PROF_ERROR; | 
|---|
 | 313 |       return; | 
|---|
 | 314 |     } | 
|---|
 | 315 |  | 
|---|
 | 316 |   p->tos = (struct tostruct *) cp; | 
|---|
 | 317 |   cp += p->tossize; | 
|---|
 | 318 |   p->kcount = (unsigned short *) cp; | 
|---|
 | 319 |   cp += p->kcountsize; | 
|---|
 | 320 |   p->froms = (unsigned long *) cp; | 
|---|
 | 321 |  | 
|---|
 | 322 |   p->tos[0].link = 0; | 
|---|
 | 323 |   p->state = GMON_PROF_ON; | 
|---|
 | 324 | } | 
|---|
 | 325 |  | 
|---|
 | 326 |  | 
|---|
 | 327 | static void | 
|---|
 | 328 | write_call_graph (int fd) | 
|---|
 | 329 | { | 
|---|
 | 330 | #define NARCS_PER_WRITE 32 | 
|---|
 | 331 |  | 
|---|
 | 332 |   struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITE] | 
|---|
 | 333 |     __attribute__ ((aligned (__alignof__ (char *)))); | 
|---|
 | 334 |   unsigned long from_index; | 
|---|
 | 335 |   unsigned long to_index; | 
|---|
 | 336 |   unsigned long from_len; | 
|---|
 | 337 |   unsigned long frompc; | 
|---|
 | 338 |   int nfilled; | 
|---|
 | 339 |  | 
|---|
 | 340 |   for (nfilled = 0; nfilled < NARCS_PER_WRITE; ++ nfilled) | 
|---|
 | 341 |     raw_arc[nfilled].tag = GMON_TAG_CG_ARC; | 
|---|
 | 342 |  | 
|---|
 | 343 |   nfilled = 0; | 
|---|
 | 344 |   from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms); | 
|---|
 | 345 |  | 
|---|
 | 346 |   for (from_index = 0; from_index < from_len; ++from_index) | 
|---|
 | 347 |     { | 
|---|
 | 348 |       if (_gmonparam.froms[from_index] == 0) | 
|---|
 | 349 |         continue; | 
|---|
 | 350 |  | 
|---|
 | 351 |       frompc = _gmonparam.lowpc; | 
|---|
 | 352 |       frompc += (from_index * _gmonparam.hashfraction | 
|---|
 | 353 |                  * sizeof (*_gmonparam.froms)); | 
|---|
 | 354 |  | 
|---|
 | 355 |       for (to_index = _gmonparam.froms[from_index]; | 
|---|
 | 356 |            to_index != 0; | 
|---|
 | 357 |            to_index = _gmonparam.tos[to_index].link) | 
|---|
 | 358 |         { | 
|---|
 | 359 |           struct gmon_cg_arc_record * arc = raw_arc + nfilled; | 
|---|
 | 360 |  | 
|---|
 | 361 |           memcpy (arc->from_pc, & frompc, sizeof (arc->from_pc)); | 
|---|
 | 362 |           memcpy (arc->self_pc, & _gmonparam.tos[to_index].selfpc, sizeof (arc->self_pc)); | 
|---|
 | 363 |           memcpy (arc->count,   & _gmonparam.tos[to_index].count, sizeof (arc->count)); | 
|---|
 | 364 |  | 
|---|
 | 365 |           if (++ nfilled == NARCS_PER_WRITE) | 
|---|
 | 366 |             { | 
|---|
 | 367 |               write (fd, raw_arc, sizeof raw_arc); | 
|---|
 | 368 |               nfilled = 0; | 
|---|
 | 369 |             } | 
|---|
 | 370 |         } | 
|---|
 | 371 |     } | 
|---|
 | 372 |  | 
|---|
 | 373 |   if (nfilled > 0) | 
|---|
 | 374 |     write (fd, raw_arc, nfilled * sizeof (raw_arc[0])); | 
|---|
 | 375 | } | 
|---|
 | 376 |  | 
|---|
 | 377 | #include <errno.h> | 
|---|
 | 378 |  | 
|---|
 | 379 | static void | 
|---|
 | 380 | write_gmon (void) | 
|---|
 | 381 | { | 
|---|
 | 382 |   struct gmon_hdr ghdr __attribute__ ((aligned (__alignof__ (int)))); | 
|---|
 | 383 |   int fd; | 
|---|
 | 384 |  | 
|---|
 | 385 |   fd = open ("gmon.out", O_CREAT|O_TRUNC|O_WRONLY, 0666); | 
|---|
 | 386 |   if (fd < 0) | 
|---|
 | 387 |     { | 
|---|
 | 388 |       write (2, "_mcleanup: could not create gmon.out\n", 37); | 
|---|
 | 389 |       return; | 
|---|
 | 390 |     } | 
|---|
 | 391 |  | 
|---|
 | 392 |   /* Write gmon.out header: */ | 
|---|
 | 393 |   memset (& ghdr, '\0', sizeof (ghdr)); | 
|---|
 | 394 |   memcpy (ghdr.cookie, GMON_MAGIC, sizeof (ghdr.cookie)); | 
|---|
 | 395 |   * (unsigned long *) ghdr.version = GMON_VERSION; | 
|---|
 | 396 |   write (fd, & ghdr, sizeof (ghdr)); | 
|---|
 | 397 |  | 
|---|
 | 398 |   /* We do not have histogram or basic block information, | 
|---|
 | 399 |      so we do not generate these parts of the gmon.out file.  */ | 
|---|
 | 400 |  | 
|---|
 | 401 |   /* Write call-graph.  */ | 
|---|
 | 402 |   write_call_graph (fd); | 
|---|
 | 403 |  | 
|---|
 | 404 |   close (fd); | 
|---|
 | 405 | } | 
|---|
 | 406 |  | 
|---|
 | 407 | void | 
|---|
 | 408 | _mcleanup (void) | 
|---|
 | 409 | { | 
|---|
 | 410 |   if (_gmonparam.state != GMON_PROF_ERROR) | 
|---|
 | 411 |     { | 
|---|
 | 412 |       _gmonparam.state = GMON_PROF_OFF; | 
|---|
 | 413 |       write_gmon (); | 
|---|
 | 414 |     } | 
|---|
 | 415 |  | 
|---|
 | 416 |   /* Free the memory.  */ | 
|---|
 | 417 |   if (_gmonparam.tos != NULL) | 
|---|
 | 418 |     { | 
|---|
 | 419 |       free (_gmonparam.tos); | 
|---|
 | 420 |       _gmonparam.tos = NULL; | 
|---|
 | 421 |     } | 
|---|
 | 422 | } | 
|---|