From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 120991 invoked by alias); 14 Jun 2017 12:26:12 -0000 Mailing-List: contact newlib-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: newlib-owner@sourceware.org Received: (qmail 120980 invoked by uid 89); 14 Jun 2017 12:26:11 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-23.9 required=5.0 tests=AWL,BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_LAZY_DOMAIN_SECURITY,RCVD_IN_DNSWL_NONE,T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy=California, holder, holders, 1992 X-HELO: smtprelay.synopsys.com Received: from us01smtprelay-2.synopsys.com (HELO smtprelay.synopsys.com) (198.182.47.9) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 14 Jun 2017 12:26:08 +0000 Received: from mailhost.synopsys.com (mailhost2.synopsys.com [10.13.184.66]) by smtprelay.synopsys.com (Postfix) with ESMTP id A2BCF24E0F1F for ; Wed, 14 Jun 2017 05:26:11 -0700 (PDT) Received: from mailhost.synopsys.com (localhost [127.0.0.1]) by mailhost.synopsys.com (Postfix) with ESMTP id 845B3963 for ; Wed, 14 Jun 2017 05:26:11 -0700 (PDT) Received: from US01WEHTC3.internal.synopsys.com (us01wehtc3.internal.synopsys.com [10.15.84.232]) by mailhost.synopsys.com (Postfix) with ESMTP id 776CE962 for ; Wed, 14 Jun 2017 05:26:11 -0700 (PDT) Received: from DE02WEHTCB.internal.synopsys.com (10.225.19.94) by US01WEHTC3.internal.synopsys.com (10.15.84.232) with Microsoft SMTP Server (TLS) id 14.3.266.1; Wed, 14 Jun 2017 05:26:11 -0700 Received: from DE02WEMBXA.internal.synopsys.com ([fe80::a014:7216:77d:d55c]) by DE02WEHTCB.internal.synopsys.com ([::1]) with mapi id 14.03.0266.001; Wed, 14 Jun 2017 14:26:08 +0200 From: Claudiu Zissulescu To: "newlib@sourceware.org" CC: Anton Kolesov , Francois Bedard Subject: RE: [PATCH 1/2] [ARC] Add profile support. [PING] Date: Wed, 14 Jun 2017 12:26:00 -0000 Message-ID: <098ECE41A0A6114BB2A07F1EC238DE896662D6E3@de02wembxa.internal.synopsys.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-SW-Source: 2017/txt/msg00420.txt.bz2 Ping > -----Original Message----- > From: Claudiu Zissulescu > Sent: Tuesday, May 23, 2017 3:06 PM > To: newlib@sourceware.org > Cc: Claudiu Zissulescu ; Anton Kolesov > ; Francois Bedard > Subject: [PATCH 1/2] [ARC] Add profile support. >=20 > Add profile support for ARC processors. >=20 > libgloss/ > 2016-07-28 Claudiu Zissulescu >=20 > * arc/crt0.S: Add calls to profiler support routines. > * Makefile.in (CRT0): Add gcrt0. > (NSIM_OBJS): Add mcount. > (CRT0_INSTALL): Install gcrt0, and crt0. > * arc/gcrt0.S: New file. > * arc/mcount.c: Likewise. > --- > libgloss/arc/Makefile.in | 20 ++- > libgloss/arc/crt0.S | 14 ++ > libgloss/arc/gcrt0.S | 79 +++++++++ > libgloss/arc/mcount.c | 422 > +++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 527 insertions(+), 8 deletions(-) > create mode 100644 libgloss/arc/gcrt0.S > create mode 100644 libgloss/arc/mcount.c >=20 > diff --git a/libgloss/arc/Makefile.in b/libgloss/arc/Makefile.in > index 073d650..7f444dd 100644 > --- a/libgloss/arc/Makefile.in > +++ b/libgloss/arc/Makefile.in > @@ -47,14 +47,15 @@ OBJCOPY =3D `if [ -f ${objroot}/../binutils/objcopy ]= ; \ > then echo ${objroot}/../binutils/objcopy ; \ > else t=3D'$(program_transform_name)'; echo objcopy | sed -e $$t ; fi` >=20 > -CRT0 =3D crt0.o > +CRT0 =3D gcrt0.o crt0.o > CRT0_INSTALL =3D install-crt0 >=20 > NSIM_BSP =3D libnsim.a > NSIM_OBJS =3D \ > - libcfunc.o \ > - nsim-syscalls.o \ > - sbrk.o > + libcfunc.o \ > + nsim-syscalls.o \ > + sbrk.o \ > + mcount.o > NSIM_INSTALL =3D install-nsim > NSIM_SCRIPTS =3D nsim.specs >=20 > @@ -75,9 +76,9 @@ $(NSIM_BSP): $(NSIM_OBJS) > libcfunc.o: libcfunc.c > nsim-syscalls.o: nsim-syscalls.c > sbrk.o: sbrk.c > - > -# > -$(CRT0): crt0.S > +mcount.o: mcount.c > +gcrt0.o: gcrt0.S crt0.S > +crt0.o: crt0.S >=20 > clean mostlyclean: > rm -f *.o *.a > @@ -95,7 +96,10 @@ install: $(CRT0_INSTALL) $(NSIM_INSTALL) > $(NANO_INSTALL) >=20 > $(CRT0_INSTALL): > $(mkinstalldirs) $(DESTDIR)${tooldir}/lib${MULTISUBDIR} > - ${INSTALL_DATA} ${CRT0} > $(DESTDIR)${tooldir}/lib${MULTISUBDIR}/$(CRT0) > + for c in $(CRT0); do \ > + b=3D`basename $$c`; \ > + ${INSTALL_DATA} $$c $(DESTDIR)${tooldir}/lib${MULTISUBDIR}/$$b > ;\ > + done >=20 > $(NSIM_INSTALL): > $(mkinstalldirs) $(DESTDIR)${tooldir}/lib${MULTISUBDIR} > diff --git a/libgloss/arc/crt0.S b/libgloss/arc/crt0.S > index 205b126..f8d7148 100644 > --- a/libgloss/arc/crt0.S > +++ b/libgloss/arc/crt0.S > @@ -194,6 +194,13 @@ __start: > #else > bl @_init > #endif /* __ARCEM__ || __ARCHS__ */ > + > +#ifdef PROFILE_SUPPORT /* Defined in gcrt0.S. */ > + mov r0,@__start > + mov r1,@_etext > + jl @_monstartup > +#endif /* PROFILE_SUPPORT */ > + > mov_s r0, r13 > mov_s r1, r14 > ; branch to main > @@ -204,6 +211,13 @@ __start: > bl.d @main > mov fp, 0 ; initialize frame pointer > #endif /* __ARCEM__ || __ARCHS__ */ > + > +#ifdef PROFILE_SUPPORT > + mov r13, r0 ; Save return code > + jl @_mcleanup > + mov r0, r13 > +#endif /* PROFILE_SUPPORT */ > + > ; r0 contains exit code > j @exit >=20 > diff --git a/libgloss/arc/gcrt0.S b/libgloss/arc/gcrt0.S > new file mode 100644 > index 0000000..0ce6b63 > --- /dev/null > +++ b/libgloss/arc/gcrt0.S > @@ -0,0 +1,79 @@ > +/* > + Copyright (c) 2016, Synopsys, Inc. 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 not= ice, > + 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 documenta= tion > + and/or other materials provided with the distribution. > + > + 3) Neither the name of the Synopsys, Inc., 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. > +*/ > +#define PROFILE_SUPPORT 1 > + > +#include "crt0.S" > + > + .global __mcount > + .type __mcount, @function > + .align 4 > +__mcount: > + ;; When a function is compiled for profiling, gcc will insert > + ;; a call to __mcount without checking for resources. Hence, > + ;; we need save all of the argument registers and temp registers, > + ;; extract the address of , and call the > _mcount_internal > + ;; to do the real work. Finally, restore all the argument registers > + ;; before returning. > + push_s blink > + push_s r0 > + push_s r1 > + push_s r2 > + push_s r3 > +#ifndef __ARC_RF16__ > + push r4 > + push r5 > + push r6 > + push r7 > + push r8 > + push r9 > +#endif > + push r10 > + push r11 > + push r12 > + mov r0,blink > + jl @_mcount_internal > + pop r12 > + pop r11 > + pop r10 > +#ifndef __ARC_RF16__ > + pop r9 > + pop r8 > + pop r7 > + pop r6 > + pop r5 > + pop r4 > +#endif > + pop_s r3 > + pop_s r2 > + pop_s r1 > + pop_s r0 > + pop_s blink > + j_s [blink] > + .size __mcount, . - __mcount > diff --git a/libgloss/arc/mcount.c b/libgloss/arc/mcount.c > new file mode 100644 > index 0000000..6bfa3d8 > --- /dev/null > +++ b/libgloss/arc/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 distributio= n. > + * 4. Neither the name of the University nor the names of its contributo= rs > + * 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 > +#include > +#include > +#include > +#include > + > +/* 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 =3D 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 =3D 3 / (2 * 2 - 1) =3D 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 (1L << 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 =3D 0, > + GMON_TAG_CG_ARC =3D 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 =3D frompc; > + unsigned long * frompcindex; > + struct tostruct * top; > + struct tostruct * prevtop; > + struct gmonparam * p; > + unsigned long toindex; > + int i; > + > + p =3D & _gmonparam; > + > + /* Check that we are profiling and that we aren't recursively invoked. > + NB/ This version is not thread-safe. */ > + if (p->state !=3D GMON_PROF_ON) > + return; > + p->state =3D 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 -=3D p->lowpc; > + if (frompc > p->textsize) > + goto done; > + > + i =3D frompc >> p->log_hashfraction; > + > + frompcindex =3D p->froms + i; > + toindex =3D * frompcindex; > + > + if (toindex =3D=3D 0) > + { > + /* First time traversing this arc. */ > + toindex =3D ++ p->tos[0].link; > + if (toindex >=3D p->tolimit) > + /* Halt further profiling. */ > + goto overflow; > + > + * frompcindex =3D toindex; > + top =3D p->tos + toindex; > + top->selfpc =3D selfpc; > + top->count =3D 1; > + top->link =3D 0; > + goto done; > + } > + > + top =3D p->tos + toindex; > + > + if (top->selfpc =3D=3D 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 =3D=3D 0) > + { > + /* Top is end of the chain and none of the chain > + had top->selfpc =3D=3D selfpc. So we allocate a > + new tostruct and link it to the head of the > + chain. */ > + toindex =3D ++ p->tos[0].link; > + if (toindex >=3D p->tolimit) > + goto overflow; > + > + top =3D p->tos + toindex; > + top->selfpc =3D selfpc; > + top->count =3D 1; > + top->link =3D * frompcindex; > + * frompcindex =3D toindex; > + goto done; > + } > + > + /* Otherwise, check the next arc on the chain. */ > + prevtop =3D top; > + top =3D p->tos + top->link; > + > + if (top->selfpc =3D=3D selfpc) > + { > + /* There it is. Increment its count > + move it to the head of the chain. */ > + top->count ++; > + toindex =3D prevtop->link; > + prevtop->link =3D top->link; > + top->link =3D * frompcindex; > + * frompcindex =3D toindex; > + goto done; > + } > + } > + > + done: > + p->state =3D GMON_PROF_ON; > + return; > + > + overflow: > + p->state =3D GMON_PROF_ERROR; > + return; > +} > + > +void > +_monstartup (unsigned long lowpc, unsigned long highpc) > +{ > + char * cp; > + struct gmonparam * p =3D & _gmonparam; > + > + /* If the calloc() function has been instrumented we must make sure > + that it is not profiled until we are ready. */ > + p->state =3D 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 =3D ROUNDDOWN (lowpc, HISTFRACTION * sizeof (* p- > >kcount)); > + p->highpc =3D ROUNDUP (highpc, HISTFRACTION * sizeof (* p- > >kcount)); > + p->textsize =3D p->highpc - p->lowpc; > + p->kcountsize =3D ROUNDUP (p->textsize / HISTFRACTION, sizeof (*= p- > >froms)); > + p->hashfraction =3D HASHFRACTION; > + p->log_hashfraction =3D -1; > + p->log_hashfraction =3D ffs (p->hashfraction * sizeof (*p->froms)) - 1; > + p->fromssize =3D p->textsize / HASHFRACTION; > + p->tolimit =3D p->textsize * ARCDENSITY / 100; > + > + if (p->tolimit < MINARCS) > + p->tolimit =3D MINARCS; > + else if (p->tolimit > MAXARCS) > + p->tolimit =3D MAXARCS; > + > + p->tossize =3D p->tolimit * sizeof (struct tostruct); > + > + cp =3D calloc (p->kcountsize + p->fromssize + p->tossize, 1); > + if (cp =3D=3D NULL) > + { > + write (2, "monstartup: out of memory\n", 26); > + p->tos =3D NULL; > + p->state =3D GMON_PROF_ERROR; > + return; > + } > + > + p->tos =3D (struct tostruct *) cp; > + cp +=3D p->tossize; > + p->kcount =3D (unsigned short *) cp; > + cp +=3D p->kcountsize; > + p->froms =3D (unsigned long *) cp; > + > + p->tos[0].link =3D 0; > + p->state =3D 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 =3D 0; nfilled < NARCS_PER_WRITE; ++ nfilled) > + raw_arc[nfilled].tag =3D GMON_TAG_CG_ARC; > + > + nfilled =3D 0; > + from_len =3D _gmonparam.fromssize / sizeof (*_gmonparam.froms); > + > + for (from_index =3D 0; from_index < from_len; ++from_index) > + { > + if (_gmonparam.froms[from_index] =3D=3D 0) > + continue; > + > + frompc =3D _gmonparam.lowpc; > + frompc +=3D (from_index * _gmonparam.hashfraction > + * sizeof (*_gmonparam.froms)); > + > + for (to_index =3D _gmonparam.froms[from_index]; > + to_index !=3D 0; > + to_index =3D _gmonparam.tos[to_index].link) > + { > + struct gmon_cg_arc_record * arc =3D 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 =3D=3D NARCS_PER_WRITE) > + { > + write (fd, raw_arc, sizeof raw_arc); > + nfilled =3D 0; > + } > + } > + } > + > + if (nfilled > 0) > + write (fd, raw_arc, nfilled * sizeof (raw_arc[0])); > +} > + > +#include > + > +static void > +write_gmon (void) > +{ > + struct gmon_hdr ghdr __attribute__ ((aligned (__alignof__ (int)))); > + int fd; > + > + fd =3D 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 =3D 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 !=3D GMON_PROF_ERROR) > + { > + _gmonparam.state =3D GMON_PROF_OFF; > + write_gmon (); > + } > + > + /* Free the memory. */ > + if (_gmonparam.tos !=3D NULL) > + { > + free (_gmonparam.tos); > + _gmonparam.tos =3D NULL; > + } > +} > -- > 1.9.1