blob: a6b18554a31253d779b7e617159123dc008acf44 [file] [log] [blame]
xf.libdd93d52023-05-12 07:10:14 -07001/*-
2 * Copyright (c) 1983, 1992, 1993, 2011
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#include <sys/param.h>
30#include <sys/time.h>
31#include <sys/gmon.h>
32#include <sys/gmon_out.h>
33#include <sys/uio.h>
34
35#include <errno.h>
36#include <stdio.h>
37#include <fcntl.h>
38#include <unistd.h>
39#include <wchar.h>
40
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <stddef.h>
45#include <unistd.h>
46#include <libc-internal.h>
47#include <not-cancel.h>
48
49
50/* Head of basic-block list or NULL. */
51struct __bb *__bb_head attribute_hidden;
52
53struct gmonparam _gmonparam attribute_hidden = { GMON_PROF_OFF };
54
55/*
56 * See profil(2) where this is described:
57 */
58static int s_scale;
59#define SCALE_1_TO_1 0x10000L
60
61#define ERR(s) write_not_cancel (STDERR_FILENO, s, sizeof (s) - 1)
62
63void moncontrol (int mode);
64void __moncontrol (int mode);
65static void write_hist (int fd) internal_function;
66static void write_call_graph (int fd) internal_function;
67static void write_bb_counts (int fd) internal_function;
68
69/*
70 * Control profiling
71 * profiling is what mcount checks to see if
72 * all the data structures are ready.
73 */
74void
75__moncontrol (int mode)
76{
77 struct gmonparam *p = &_gmonparam;
78
79 /* Don't change the state if we ran into an error. */
80 if (p->state == GMON_PROF_ERROR)
81 return;
82
83 if (mode)
84 {
85 /* start */
86 __profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale);
87 p->state = GMON_PROF_ON;
88 }
89 else
90 {
91 /* stop */
92 __profil(NULL, 0, 0, 0);
93 p->state = GMON_PROF_OFF;
94 }
95}
96weak_alias (__moncontrol, moncontrol)
97
98
99void
100__monstartup (u_long lowpc, u_long highpc)
101{
102 int o;
103 char *cp;
104 struct gmonparam *p = &_gmonparam;
105
106 /*
107 * round lowpc and highpc to multiples of the density we're using
108 * so the rest of the scaling (here and in gprof) stays in ints.
109 */
110 p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
111 p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
112 p->textsize = p->highpc - p->lowpc;
xf.li7ccf8372024-03-07 00:08:02 -0800113 /* This looks like a typo, but it's here to align the p->froms
114 section. */
xf.libdd93d52023-05-12 07:10:14 -0700115 p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms));
116 p->hashfraction = HASHFRACTION;
117 p->log_hashfraction = -1;
118 /* The following test must be kept in sync with the corresponding
119 test in mcount.c. */
120 if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) {
121 /* if HASHFRACTION is a power of two, mcount can use shifting
122 instead of integer division. Precompute shift amount. */
123 p->log_hashfraction = ffs(p->hashfraction * sizeof(*p->froms)) - 1;
124 }
xf.li7ccf8372024-03-07 00:08:02 -0800125 /*add for CVE-2023-0687*/
126 p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms));
xf.libdd93d52023-05-12 07:10:14 -0700127 p->tolimit = p->textsize * ARCDENSITY / 100;
128 if (p->tolimit < MINARCS)
129 p->tolimit = MINARCS;
130 else if (p->tolimit > MAXARCS)
131 p->tolimit = MAXARCS;
132 p->tossize = p->tolimit * sizeof(struct tostruct);
133
134 cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1);
135 if (! cp)
136 {
137 ERR("monstartup: out of memory\n");
138 p->tos = NULL;
139 p->state = GMON_PROF_ERROR;
140 return;
141 }
142 p->tos = (struct tostruct *)cp;
143 cp += p->tossize;
144 p->kcount = (HISTCOUNTER *)cp;
145 cp += p->kcountsize;
146 p->froms = (ARCINDEX *)cp;
147
148 p->tos[0].link = 0;
149
150 o = p->highpc - p->lowpc;
151 if (p->kcountsize < (u_long) o)
152 {
153#ifndef hp300
154 s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
155#else
156 /* avoid floating point operations */
157 int quot = o / p->kcountsize;
158
159 if (quot >= 0x10000)
160 s_scale = 1;
161 else if (quot >= 0x100)
162 s_scale = 0x10000 / quot;
163 else if (o >= 0x800000)
164 s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
165 else
166 s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
167#endif
168 } else
169 s_scale = SCALE_1_TO_1;
170
171 __moncontrol(1);
172}
173weak_alias (__monstartup, monstartup)
174
175
176static void
177internal_function
178write_hist (int fd)
179{
180 u_char tag = GMON_TAG_TIME_HIST;
181
182 if (_gmonparam.kcountsize > 0)
183 {
184 struct real_gmon_hist_hdr
185 {
186 char *low_pc;
187 char *high_pc;
188 int32_t hist_size;
189 int32_t prof_rate;
190 char dimen[15];
191 char dimen_abbrev;
192 } thdr;
193 struct iovec iov[3] =
194 {
195 { &tag, sizeof (tag) },
196 { &thdr, sizeof (struct gmon_hist_hdr) },
197 { _gmonparam.kcount, _gmonparam.kcountsize }
198 };
199
200 if (sizeof (thdr) != sizeof (struct gmon_hist_hdr)
201 || (offsetof (struct real_gmon_hist_hdr, low_pc)
202 != offsetof (struct gmon_hist_hdr, low_pc))
203 || (offsetof (struct real_gmon_hist_hdr, high_pc)
204 != offsetof (struct gmon_hist_hdr, high_pc))
205 || (offsetof (struct real_gmon_hist_hdr, hist_size)
206 != offsetof (struct gmon_hist_hdr, hist_size))
207 || (offsetof (struct real_gmon_hist_hdr, prof_rate)
208 != offsetof (struct gmon_hist_hdr, prof_rate))
209 || (offsetof (struct real_gmon_hist_hdr, dimen)
210 != offsetof (struct gmon_hist_hdr, dimen))
211 || (offsetof (struct real_gmon_hist_hdr, dimen_abbrev)
212 != offsetof (struct gmon_hist_hdr, dimen_abbrev)))
213 abort ();
214
215 thdr.low_pc = (char *) _gmonparam.lowpc;
216 thdr.high_pc = (char *) _gmonparam.highpc;
217 thdr.hist_size = _gmonparam.kcountsize / sizeof (HISTCOUNTER);
218 thdr.prof_rate = __profile_frequency ();
219 strncpy (thdr.dimen, "seconds", sizeof (thdr.dimen));
220 thdr.dimen_abbrev = 's';
221
222 writev_not_cancel_no_status (fd, iov, 3);
223 }
224}
225
226
227static void
228internal_function
229write_call_graph (int fd)
230{
231#define NARCS_PER_WRITEV 32
232 u_char tag = GMON_TAG_CG_ARC;
233 struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITEV]
234 __attribute__ ((aligned (__alignof__ (char*))));
235 ARCINDEX from_index, to_index;
236 u_long from_len;
237 u_long frompc;
238 struct iovec iov[2 * NARCS_PER_WRITEV];
239 int nfilled;
240
241 for (nfilled = 0; nfilled < NARCS_PER_WRITEV; ++nfilled)
242 {
243 iov[2 * nfilled].iov_base = &tag;
244 iov[2 * nfilled].iov_len = sizeof (tag);
245
246 iov[2 * nfilled + 1].iov_base = &raw_arc[nfilled];
247 iov[2 * nfilled + 1].iov_len = sizeof (struct gmon_cg_arc_record);
248 }
249
250 nfilled = 0;
251 from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms);
252 for (from_index = 0; from_index < from_len; ++from_index)
253 {
254 if (_gmonparam.froms[from_index] == 0)
255 continue;
256
257 frompc = _gmonparam.lowpc;
258 frompc += (from_index * _gmonparam.hashfraction
259 * sizeof (*_gmonparam.froms));
260 for (to_index = _gmonparam.froms[from_index];
261 to_index != 0;
262 to_index = _gmonparam.tos[to_index].link)
263 {
264 struct arc
265 {
266 char *frompc;
267 char *selfpc;
268 int32_t count;
269 }
270 arc;
271
272 arc.frompc = (char *) frompc;
273 arc.selfpc = (char *) _gmonparam.tos[to_index].selfpc;
274 arc.count = _gmonparam.tos[to_index].count;
275 memcpy (raw_arc + nfilled, &arc, sizeof (raw_arc [0]));
276
277 if (++nfilled == NARCS_PER_WRITEV)
278 {
279 writev_not_cancel_no_status (fd, iov, 2 * nfilled);
280 nfilled = 0;
281 }
282 }
283 }
284 if (nfilled > 0)
285 writev_not_cancel_no_status (fd, iov, 2 * nfilled);
286}
287
288
289static void
290internal_function
291write_bb_counts (int fd)
292{
293 struct __bb *grp;
294 u_char tag = GMON_TAG_BB_COUNT;
295 size_t ncounts;
296 size_t i;
297
298 struct iovec bbhead[2] =
299 {
300 { &tag, sizeof (tag) },
301 { &ncounts, sizeof (ncounts) }
302 };
303 struct iovec bbbody[8];
304 size_t nfilled;
305
306 for (i = 0; i < (sizeof (bbbody) / sizeof (bbbody[0])); i += 2)
307 {
308 bbbody[i].iov_len = sizeof (grp->addresses[0]);
309 bbbody[i + 1].iov_len = sizeof (grp->counts[0]);
310 }
311
312 /* Write each group of basic-block info (all basic-blocks in a
313 compilation unit form a single group). */
314
315 for (grp = __bb_head; grp; grp = grp->next)
316 {
317 ncounts = grp->ncounts;
318 writev_not_cancel_no_status (fd, bbhead, 2);
319 for (nfilled = i = 0; i < ncounts; ++i)
320 {
321 if (nfilled > (sizeof (bbbody) / sizeof (bbbody[0])) - 2)
322 {
323 writev_not_cancel_no_status (fd, bbbody, nfilled);
324 nfilled = 0;
325 }
326
327 bbbody[nfilled++].iov_base = (char *) &grp->addresses[i];
328 bbbody[nfilled++].iov_base = &grp->counts[i];
329 }
330 if (nfilled > 0)
331 writev_not_cancel_no_status (fd, bbbody, nfilled);
332 }
333}
334
335
336static void
337write_gmon (void)
338{
339 int fd = -1;
340 char *env;
341
342#ifndef O_NOFOLLOW
343# define O_NOFOLLOW 0
344#endif
345
346 env = getenv ("GMON_OUT_PREFIX");
347 if (env != NULL && !__libc_enable_secure)
348 {
349 size_t len = strlen (env);
350 char buf[len + 20];
351 __snprintf (buf, sizeof (buf), "%s.%u", env, __getpid ());
352 fd = open_not_cancel (buf, O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW, 0666);
353 }
354
355 if (fd == -1)
356 {
357 fd = open_not_cancel ("gmon.out", O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW,
358 0666);
359 if (fd < 0)
360 {
361 char buf[300];
362 int errnum = errno;
363 __fxprintf (NULL, "_mcleanup: gmon.out: %s\n",
364 __strerror_r (errnum, buf, sizeof buf));
365 return;
366 }
367 }
368
369 /* write gmon.out header: */
370 struct real_gmon_hdr
371 {
372 char cookie[4];
373 int32_t version;
374 char spare[3 * 4];
375 } ghdr;
376 if (sizeof (ghdr) != sizeof (struct gmon_hdr)
377 || (offsetof (struct real_gmon_hdr, cookie)
378 != offsetof (struct gmon_hdr, cookie))
379 || (offsetof (struct real_gmon_hdr, version)
380 != offsetof (struct gmon_hdr, version)))
381 abort ();
382 memcpy (&ghdr.cookie[0], GMON_MAGIC, sizeof (ghdr.cookie));
383 ghdr.version = GMON_VERSION;
384 memset (ghdr.spare, '\0', sizeof (ghdr.spare));
385 write_not_cancel (fd, &ghdr, sizeof (struct gmon_hdr));
386
387 /* write PC histogram: */
388 write_hist (fd);
389
390 /* write call-graph: */
391 write_call_graph (fd);
392
393 /* write basic-block execution counts: */
394 write_bb_counts (fd);
395
396 close_not_cancel_no_status (fd);
397}
398
399
400void
401__write_profiling (void)
402{
403 int save = _gmonparam.state;
404 _gmonparam.state = GMON_PROF_OFF;
405 if (save == GMON_PROF_ON)
406 write_gmon ();
407 _gmonparam.state = save;
408}
409#ifndef SHARED
410/* This symbol isn't used anywhere in the DSO and it is not exported.
411 This would normally mean it should be removed to get the same API
412 in static libraries. But since profiling is special in static libs
413 anyway we keep it. But not when building the DSO since some
414 quality assurance tests will otherwise trigger. */
415weak_alias (__write_profiling, write_profiling)
416#endif
417
418
419void
420_mcleanup (void)
421{
422 __moncontrol (0);
423
424 if (_gmonparam.state != GMON_PROF_ERROR)
425 write_gmon ();
426
427 /* free the memory. */
428 free (_gmonparam.tos);
429}