1*39152b4fScheloha /* $OpenBSD: gmon.c,v 1.33 2022/07/26 04:07:13 cheloha Exp $ */
2df930be7Sderaadt /*-
3df930be7Sderaadt * Copyright (c) 1983, 1992, 1993
4df930be7Sderaadt * The Regents of the University of California. All rights reserved.
5df930be7Sderaadt *
6df930be7Sderaadt * Redistribution and use in source and binary forms, with or without
7df930be7Sderaadt * modification, are permitted provided that the following conditions
8df930be7Sderaadt * are met:
9df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright
10df930be7Sderaadt * notice, this list of conditions and the following disclaimer.
11df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
12df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the
13df930be7Sderaadt * documentation and/or other materials provided with the distribution.
146580fee3Smillert * 3. Neither the name of the University nor the names of its contributors
15df930be7Sderaadt * may be used to endorse or promote products derived from this software
16df930be7Sderaadt * without specific prior written permission.
17df930be7Sderaadt *
18df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28df930be7Sderaadt * SUCH DAMAGE.
29df930be7Sderaadt */
30df930be7Sderaadt
31df930be7Sderaadt #include <sys/time.h>
32df930be7Sderaadt #include <sys/gmon.h>
337f1ef45aSmillert #include <sys/mman.h>
34df930be7Sderaadt #include <sys/sysctl.h>
35df930be7Sderaadt
36df930be7Sderaadt #include <stdio.h>
3742fbf3d8Sderaadt #include <stdlib.h>
38c8f91e0dStholo #include <string.h>
39df930be7Sderaadt #include <fcntl.h>
4042fbf3d8Sderaadt #include <limits.h>
41df930be7Sderaadt #include <unistd.h>
42df930be7Sderaadt
43df930be7Sderaadt struct gmonparam _gmonparam = { GMON_PROF_OFF };
44df930be7Sderaadt
45df930be7Sderaadt static int s_scale;
46df930be7Sderaadt /* see profil(2) where this is describe (incorrectly) */
47df930be7Sderaadt #define SCALE_1_TO_1 0x10000L
48df930be7Sderaadt
493bfa8ee4Sderaadt #define ERR(s) write(STDERR_FILENO, s, sizeof(s))
50df930be7Sderaadt
51cbc0cb74Sguenther PROTO_NORMAL(moncontrol);
52dbf5b26bSguenther PROTO_DEPRECATED(monstartup);
53df930be7Sderaadt
54df930be7Sderaadt void
monstartup(u_long lowpc,u_long highpc)55ee40e6f6Sotto monstartup(u_long lowpc, u_long highpc)
56df930be7Sderaadt {
57ee40e6f6Sotto int o;
587f1ef45aSmillert void *addr;
59df930be7Sderaadt struct gmonparam *p = &_gmonparam;
60df930be7Sderaadt
61df930be7Sderaadt /*
62df930be7Sderaadt * round lowpc and highpc to multiples of the density we're using
63df930be7Sderaadt * so the rest of the scaling (here and in gprof) stays in ints.
64df930be7Sderaadt */
65df930be7Sderaadt p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
66df930be7Sderaadt p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
67df930be7Sderaadt p->textsize = p->highpc - p->lowpc;
68df930be7Sderaadt p->kcountsize = p->textsize / HISTFRACTION;
69df930be7Sderaadt p->hashfraction = HASHFRACTION;
7064d71990Sderaadt p->fromssize = p->textsize / p->hashfraction;
71df930be7Sderaadt p->tolimit = p->textsize * ARCDENSITY / 100;
72df930be7Sderaadt if (p->tolimit < MINARCS)
73df930be7Sderaadt p->tolimit = MINARCS;
74df930be7Sderaadt else if (p->tolimit > MAXARCS)
75df930be7Sderaadt p->tolimit = MAXARCS;
76df930be7Sderaadt p->tossize = p->tolimit * sizeof(struct tostruct);
77df930be7Sderaadt
7825a9e580Smmcc addr = mmap(NULL, p->kcountsize, PROT_READ|PROT_WRITE,
793240e6a8Sguenther MAP_ANON|MAP_PRIVATE, -1, 0);
807f1ef45aSmillert if (addr == MAP_FAILED)
817f1ef45aSmillert goto mapfailed;
827f1ef45aSmillert p->kcount = addr;
83df930be7Sderaadt
8425a9e580Smmcc addr = mmap(NULL, p->fromssize, PROT_READ|PROT_WRITE,
853240e6a8Sguenther MAP_ANON|MAP_PRIVATE, -1, 0);
867f1ef45aSmillert if (addr == MAP_FAILED)
877f1ef45aSmillert goto mapfailed;
887f1ef45aSmillert p->froms = addr;
897f1ef45aSmillert
9025a9e580Smmcc addr = mmap(NULL, p->tossize, PROT_READ|PROT_WRITE,
913240e6a8Sguenther MAP_ANON|MAP_PRIVATE, -1, 0);
927f1ef45aSmillert if (addr == MAP_FAILED)
937f1ef45aSmillert goto mapfailed;
947f1ef45aSmillert p->tos = addr;
95df930be7Sderaadt p->tos[0].link = 0;
96df930be7Sderaadt
97df930be7Sderaadt o = p->highpc - p->lowpc;
98df930be7Sderaadt if (p->kcountsize < o) {
99df930be7Sderaadt #ifndef notdef
100df930be7Sderaadt s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
101df930be7Sderaadt #else /* avoid floating point */
102df930be7Sderaadt int quot = o / p->kcountsize;
103df930be7Sderaadt
104df930be7Sderaadt if (quot >= 0x10000)
105df930be7Sderaadt s_scale = 1;
106df930be7Sderaadt else if (quot >= 0x100)
107df930be7Sderaadt s_scale = 0x10000 / quot;
108df930be7Sderaadt else if (o >= 0x800000)
109df930be7Sderaadt s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
110df930be7Sderaadt else
111df930be7Sderaadt s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
112df930be7Sderaadt #endif
113df930be7Sderaadt } else
114df930be7Sderaadt s_scale = SCALE_1_TO_1;
115df930be7Sderaadt
116df930be7Sderaadt moncontrol(1);
1177f1ef45aSmillert return;
1187f1ef45aSmillert
1197f1ef45aSmillert mapfailed:
1207f1ef45aSmillert if (p->kcount != NULL) {
1217f1ef45aSmillert munmap(p->kcount, p->kcountsize);
1227f1ef45aSmillert p->kcount = NULL;
1237f1ef45aSmillert }
1247f1ef45aSmillert if (p->froms != NULL) {
1257f1ef45aSmillert munmap(p->froms, p->fromssize);
1267f1ef45aSmillert p->froms = NULL;
1277f1ef45aSmillert }
1287f1ef45aSmillert if (p->tos != NULL) {
1297f1ef45aSmillert munmap(p->tos, p->tossize);
1307f1ef45aSmillert p->tos = NULL;
1317f1ef45aSmillert }
1327f1ef45aSmillert ERR("monstartup: out of memory\n");
133df930be7Sderaadt }
134dbf5b26bSguenther __strong_alias(_monstartup,monstartup);
135df930be7Sderaadt
136df930be7Sderaadt void
_mcleanup(void)13774637cb2Sderaadt _mcleanup(void)
138df930be7Sderaadt {
139df930be7Sderaadt int fd;
140df930be7Sderaadt int fromindex;
141df930be7Sderaadt int endfrom;
142df930be7Sderaadt u_long frompc;
143df930be7Sderaadt int toindex;
144df930be7Sderaadt struct rawarc rawarc;
145df930be7Sderaadt struct gmonparam *p = &_gmonparam;
146df930be7Sderaadt struct gmonhdr gmonhdr, *hdr;
147df930be7Sderaadt struct clockinfo clockinfo;
14887afc19eSderaadt const int mib[2] = { CTL_KERN, KERN_CLOCKRATE };
149df930be7Sderaadt size_t size;
15042fbf3d8Sderaadt char *profdir;
15142fbf3d8Sderaadt char *proffile;
15242fbf3d8Sderaadt char buf[PATH_MAX];
153df930be7Sderaadt #ifdef DEBUG
154df930be7Sderaadt int log, len;
1552f506d37Sderaadt char dbuf[200];
156df930be7Sderaadt #endif
157df930be7Sderaadt
158df930be7Sderaadt if (p->state == GMON_PROF_ERROR)
159df930be7Sderaadt ERR("_mcleanup: tos overflow\n");
160df930be7Sderaadt
161*39152b4fScheloha /*
162*39152b4fScheloha * There is nothing we can do if sysctl(2) fails or if
163*39152b4fScheloha * clockinfo.hz is unset.
164*39152b4fScheloha */
165df930be7Sderaadt size = sizeof(clockinfo);
166df69c215Sderaadt if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) == -1) {
167*39152b4fScheloha clockinfo.profhz = 0;
168df930be7Sderaadt } else if (clockinfo.profhz == 0) {
169*39152b4fScheloha clockinfo.profhz = clockinfo.hz; /* best guess */
170df930be7Sderaadt }
171df930be7Sderaadt
172df930be7Sderaadt moncontrol(0);
17342fbf3d8Sderaadt
1742f506d37Sderaadt if (issetugid() == 0 && (profdir = getenv("PROFDIR")) != NULL) {
1752f506d37Sderaadt char *s, *t, *limit;
17642fbf3d8Sderaadt pid_t pid;
17742fbf3d8Sderaadt long divisor;
17842fbf3d8Sderaadt
17942fbf3d8Sderaadt /* If PROFDIR contains a null value, no profiling
18042fbf3d8Sderaadt output is produced */
18142fbf3d8Sderaadt if (*profdir == '\0') {
18242fbf3d8Sderaadt return;
18342fbf3d8Sderaadt }
18442fbf3d8Sderaadt
1852f506d37Sderaadt limit = buf + sizeof buf - 1 - 10 - 1 -
1862f506d37Sderaadt strlen(__progname) - 1;
18742fbf3d8Sderaadt t = buf;
18842fbf3d8Sderaadt s = profdir;
1892f506d37Sderaadt while((*t = *s) != '\0' && t < limit) {
19042fbf3d8Sderaadt t++;
19142fbf3d8Sderaadt s++;
19242fbf3d8Sderaadt }
19342fbf3d8Sderaadt *t++ = '/';
19442fbf3d8Sderaadt
19542fbf3d8Sderaadt /*
19642fbf3d8Sderaadt * Copy and convert pid from a pid_t to a string. For
19742fbf3d8Sderaadt * best performance, divisor should be initialized to
19842fbf3d8Sderaadt * the largest power of 10 less than PID_MAX.
19942fbf3d8Sderaadt */
20042fbf3d8Sderaadt pid = getpid();
20142fbf3d8Sderaadt divisor=10000;
20242fbf3d8Sderaadt while (divisor > pid) divisor /= 10; /* skip leading zeros */
20342fbf3d8Sderaadt do {
20442fbf3d8Sderaadt *t++ = (pid/divisor) + '0';
20542fbf3d8Sderaadt pid %= divisor;
20642fbf3d8Sderaadt } while (divisor /= 10);
20742fbf3d8Sderaadt *t++ = '.';
20842fbf3d8Sderaadt
20942fbf3d8Sderaadt s = __progname;
21042fbf3d8Sderaadt while ((*t++ = *s++) != '\0')
21142fbf3d8Sderaadt ;
21242fbf3d8Sderaadt
21342fbf3d8Sderaadt proffile = buf;
21442fbf3d8Sderaadt } else {
21542fbf3d8Sderaadt proffile = "gmon.out";
21642fbf3d8Sderaadt }
21742fbf3d8Sderaadt
2183e7612c7Smillert fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0664);
219df69c215Sderaadt if (fd == -1) {
22042fbf3d8Sderaadt perror( proffile );
221df930be7Sderaadt return;
222df930be7Sderaadt }
223df930be7Sderaadt #ifdef DEBUG
224df930be7Sderaadt log = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664);
225df69c215Sderaadt if (log == -1) {
226df930be7Sderaadt perror("mcount: gmon.log");
227ee993850Sjsg close(fd);
228df930be7Sderaadt return;
229df930be7Sderaadt }
230f2c97eb2Sderaadt snprintf(dbuf, sizeof dbuf, "[mcleanup1] kcount 0x%x ssiz %d\n",
231df930be7Sderaadt p->kcount, p->kcountsize);
232f2c97eb2Sderaadt write(log, dbuf, strlen(dbuf));
233df930be7Sderaadt #endif
234df930be7Sderaadt hdr = (struct gmonhdr *)&gmonhdr;
235672e980cScloder bzero(hdr, sizeof(*hdr));
236df930be7Sderaadt hdr->lpc = p->lowpc;
237df930be7Sderaadt hdr->hpc = p->highpc;
238df930be7Sderaadt hdr->ncnt = p->kcountsize + sizeof(gmonhdr);
239df930be7Sderaadt hdr->version = GMONVERSION;
240df930be7Sderaadt hdr->profrate = clockinfo.profhz;
241df930be7Sderaadt write(fd, (char *)hdr, sizeof *hdr);
242df930be7Sderaadt write(fd, p->kcount, p->kcountsize);
243df930be7Sderaadt endfrom = p->fromssize / sizeof(*p->froms);
244df930be7Sderaadt for (fromindex = 0; fromindex < endfrom; fromindex++) {
245df930be7Sderaadt if (p->froms[fromindex] == 0)
246df930be7Sderaadt continue;
247df930be7Sderaadt
248df930be7Sderaadt frompc = p->lowpc;
249df930be7Sderaadt frompc += fromindex * p->hashfraction * sizeof(*p->froms);
250df930be7Sderaadt for (toindex = p->froms[fromindex]; toindex != 0;
251df930be7Sderaadt toindex = p->tos[toindex].link) {
252df930be7Sderaadt #ifdef DEBUG
253f2c97eb2Sderaadt (void) snprintf(dbuf, sizeof dbuf,
254df930be7Sderaadt "[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" ,
255df930be7Sderaadt frompc, p->tos[toindex].selfpc,
256df930be7Sderaadt p->tos[toindex].count);
257f2c97eb2Sderaadt write(log, dbuf, strlen(dbuf));
258df930be7Sderaadt #endif
259df930be7Sderaadt rawarc.raw_frompc = frompc;
260df930be7Sderaadt rawarc.raw_selfpc = p->tos[toindex].selfpc;
261df930be7Sderaadt rawarc.raw_count = p->tos[toindex].count;
262df930be7Sderaadt write(fd, &rawarc, sizeof rawarc);
263df930be7Sderaadt }
264df930be7Sderaadt }
265df930be7Sderaadt close(fd);
2667f1ef45aSmillert #ifdef notyet
2677f1ef45aSmillert if (p->kcount != NULL) {
2687f1ef45aSmillert munmap(p->kcount, p->kcountsize);
2697f1ef45aSmillert p->kcount = NULL;
2707f1ef45aSmillert }
2717f1ef45aSmillert if (p->froms != NULL) {
2727f1ef45aSmillert munmap(p->froms, p->fromssize);
2737f1ef45aSmillert p->froms = NULL;
2747f1ef45aSmillert }
2757f1ef45aSmillert if (p->tos != NULL) {
2767f1ef45aSmillert munmap(p->tos, p->tossize);
2777f1ef45aSmillert p->tos = NULL;
2787f1ef45aSmillert }
2797f1ef45aSmillert #endif
280df930be7Sderaadt }
281df930be7Sderaadt
282df930be7Sderaadt /*
283df930be7Sderaadt * Control profiling
284df930be7Sderaadt * profiling is what mcount checks to see if
285df930be7Sderaadt * all the data structures are ready.
286df930be7Sderaadt */
287df930be7Sderaadt void
moncontrol(int mode)288ee40e6f6Sotto moncontrol(int mode)
289df930be7Sderaadt {
290df930be7Sderaadt struct gmonparam *p = &_gmonparam;
291df930be7Sderaadt
292df930be7Sderaadt if (mode) {
293df930be7Sderaadt /* start */
29442fbf3d8Sderaadt profil((char *)p->kcount, p->kcountsize, p->lowpc,
295df930be7Sderaadt s_scale);
296df930be7Sderaadt p->state = GMON_PROF_ON;
297df930be7Sderaadt } else {
298df930be7Sderaadt /* stop */
299e72220c0Smmcc profil(NULL, 0, 0, 0);
300df930be7Sderaadt p->state = GMON_PROF_OFF;
301df930be7Sderaadt }
302df930be7Sderaadt }
303cbc0cb74Sguenther DEF_WEAK(moncontrol);
304