]> git.ipfire.org Git - thirdparty/glibc.git/blame - gmon/gmon.c
gmon: improve mcount overflow handling [BZ# 27576]
[thirdparty/glibc.git] / gmon / gmon.c
CommitLineData
11c981a9 1/*-
3ce1f295 2 * Copyright (c) 1983, 1992, 1993, 2011
11c981a9
RM
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.
11c981a9
RM
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 */
11c981a9
RM
29#include <sys/param.h>
30#include <sys/time.h>
31#include <sys/gmon.h>
b20e47cb 32#include <sys/gmon_out.h>
5ae9d168 33#include <sys/uio.h>
11c981a9 34
3996f34b 35#include <errno.h>
11c981a9
RM
36#include <stdio.h>
37#include <fcntl.h>
b20e47cb 38#include <unistd.h>
3ce1f295 39#include <wchar.h>
b20e47cb
RM
40
41#include <stdio.h>
11c981a9
RM
42#include <stdlib.h>
43#include <string.h>
f521be31 44#include <stddef.h>
11c981a9 45#include <unistd.h>
66539a73 46#include <libc-internal.h>
fbb37d25 47#include <not-cancel.h>
d68171ed 48
31be941e
SK
49#if HAVE_TUNABLES
50# define TUNABLE_NAMESPACE gmon
51# include <elf/dl-tunables.h>
52#endif
53
d165ca64
L
54#ifdef PIC
55# include <link.h>
56
57static int
58callback (struct dl_phdr_info *info, size_t size, void *data)
59{
60 if (info->dlpi_name[0] == '\0')
61 {
62 /* The link map for the executable is created by calling
63 _dl_new_object with "" as filename. dl_iterate_phdr
64 calls the callback function with filename from the
65 link map as dlpi_name. */
66 u_long *load_address = data;
67 *load_address = (u_long) info->dlpi_addr;
68 return 1;
69 }
70
71 return 0;
72}
73#endif
8dcc6a3f 74
100351c3
UD
75/* Head of basic-block list or NULL. */
76struct __bb *__bb_head attribute_hidden;
b20e47cb 77
0e47dbd0 78struct gmonparam _gmonparam attribute_hidden = { GMON_PROF_OFF };
11c981a9 79
b20e47cb
RM
80/*
81 * See profil(2) where this is described:
82 */
11c981a9 83static int s_scale;
11c981a9
RM
84#define SCALE_1_TO_1 0x10000L
85
c647fb88 86#define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1)
b20e47cb 87
79937577
UD
88void moncontrol (int mode);
89void __moncontrol (int mode);
fa426590 90libc_hidden_proto (__moncontrol)
d165ca64
L
91static void write_hist (int fd, u_long load_address);
92static void write_call_graph (int fd, u_long load_address);
8d2f9410 93static void write_bb_counts (int fd);
11336c16 94
b20e47cb
RM
95/*
96 * Control profiling
97 * profiling is what mcount checks to see if
98 * all the data structures are ready.
99 */
100void
9d46370c 101__moncontrol (int mode)
b20e47cb
RM
102{
103 struct gmonparam *p = &_gmonparam;
104
14e9dd67
UD
105 /* Don't change the state if we ran into an error. */
106 if (p->state == GMON_PROF_ERROR)
107 return;
108
b20e47cb
RM
109 if (mode)
110 {
111 /* start */
9a0a462c 112 __profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale);
b20e47cb
RM
113 p->state = GMON_PROF_ON;
114 }
115 else
116 {
117 /* stop */
9a0a462c 118 __profil(NULL, 0, 0, 0);
b20e47cb
RM
119 p->state = GMON_PROF_OFF;
120 }
121}
fa426590 122libc_hidden_def (__moncontrol)
69173865 123weak_alias (__moncontrol, moncontrol)
11c981a9 124
11c981a9
RM
125
126void
9d46370c 127__monstartup (u_long lowpc, u_long highpc)
11c981a9 128{
2e09a79a 129 int o;
b20e47cb
RM
130 char *cp;
131 struct gmonparam *p = &_gmonparam;
31be941e
SK
132 long int minarcs, maxarcs;
133
134#if HAVE_TUNABLES
135 /* Read minarcs/maxarcs tunables. */
136 minarcs = TUNABLE_GET (minarcs, int32_t, NULL);
137 maxarcs = TUNABLE_GET (maxarcs, int32_t, NULL);
138 if (maxarcs < minarcs)
139 {
140 ERR("monstartup: maxarcs < minarcs, setting maxarcs = minarcs\n");
141 maxarcs = minarcs;
142 }
143#else
144 /* No tunables, we use hardcoded defaults */
145 minarcs = MINARCS;
146 maxarcs = MAXARCS;
147#endif
b20e47cb
RM
148
149 /*
150 * round lowpc and highpc to multiples of the density we're using
151 * so the rest of the scaling (here and in gprof) stays in ints.
152 */
153 p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
154 p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
155 p->textsize = p->highpc - p->lowpc;
801af9fa
ЛЮLY
156 /* This looks like a typo, but it's here to align the p->froms
157 section. */
69ac9d07 158 p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms));
b20e47cb
RM
159 p->hashfraction = HASHFRACTION;
160 p->log_hashfraction = -1;
9a0a462c
UD
161 /* The following test must be kept in sync with the corresponding
162 test in mcount.c. */
b20e47cb
RM
163 if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) {
164 /* if HASHFRACTION is a power of two, mcount can use shifting
165 instead of integer division. Precompute shift amount. */
166 p->log_hashfraction = ffs(p->hashfraction * sizeof(*p->froms)) - 1;
167 }
801af9fa 168 p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms));
b20e47cb 169 p->tolimit = p->textsize * ARCDENSITY / 100;
31be941e
SK
170 if (p->tolimit < minarcs)
171 p->tolimit = minarcs;
172 else if (p->tolimit > maxarcs)
173 p->tolimit = maxarcs;
b20e47cb 174 p->tossize = p->tolimit * sizeof(struct tostruct);
11c981a9 175
0413b54c 176 cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1);
b20e47cb
RM
177 if (! cp)
178 {
14e9dd67
UD
179 ERR("monstartup: out of memory\n");
180 p->tos = NULL;
181 p->state = GMON_PROF_ERROR;
b20e47cb
RM
182 return;
183 }
b20e47cb
RM
184 p->tos = (struct tostruct *)cp;
185 cp += p->tossize;
be5b0fbc 186 p->kcount = (HISTCOUNTER *)cp;
b20e47cb 187 cp += p->kcountsize;
be5b0fbc 188 p->froms = (ARCINDEX *)cp;
11c981a9 189
b20e47cb
RM
190 p->tos[0].link = 0;
191
192 o = p->highpc - p->lowpc;
d68171ed 193 if (p->kcountsize < (u_long) o)
b20e47cb 194 {
11c981a9 195#ifndef hp300
b20e47cb
RM
196 s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
197#else
198 /* avoid floating point operations */
199 int quot = o / p->kcountsize;
200
201 if (quot >= 0x10000)
202 s_scale = 1;
203 else if (quot >= 0x100)
204 s_scale = 0x10000 / quot;
205 else if (o >= 0x800000)
206 s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
207 else
208 s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
11c981a9 209#endif
b20e47cb
RM
210 } else
211 s_scale = SCALE_1_TO_1;
11c981a9 212
3996f34b 213 __moncontrol(1);
11c981a9 214}
1ab18a5b 215weak_alias (__monstartup, monstartup)
11c981a9 216
b20e47cb
RM
217
218static void
d165ca64 219write_hist (int fd, u_long load_address)
11c981a9 220{
5ae9d168 221 u_char tag = GMON_TAG_TIME_HIST;
11c981a9 222
b20e47cb
RM
223 if (_gmonparam.kcountsize > 0)
224 {
f521be31
UD
225 struct real_gmon_hist_hdr
226 {
227 char *low_pc;
228 char *high_pc;
229 int32_t hist_size;
230 int32_t prof_rate;
231 char dimen[15];
232 char dimen_abbrev;
233 } thdr;
5ae9d168 234 struct iovec iov[3] =
3ce1f295 235 {
5ae9d168
UD
236 { &tag, sizeof (tag) },
237 { &thdr, sizeof (struct gmon_hist_hdr) },
238 { _gmonparam.kcount, _gmonparam.kcountsize }
239 };
240
f521be31
UD
241 if (sizeof (thdr) != sizeof (struct gmon_hist_hdr)
242 || (offsetof (struct real_gmon_hist_hdr, low_pc)
243 != offsetof (struct gmon_hist_hdr, low_pc))
244 || (offsetof (struct real_gmon_hist_hdr, high_pc)
245 != offsetof (struct gmon_hist_hdr, high_pc))
246 || (offsetof (struct real_gmon_hist_hdr, hist_size)
247 != offsetof (struct gmon_hist_hdr, hist_size))
248 || (offsetof (struct real_gmon_hist_hdr, prof_rate)
249 != offsetof (struct gmon_hist_hdr, prof_rate))
250 || (offsetof (struct real_gmon_hist_hdr, dimen)
251 != offsetof (struct gmon_hist_hdr, dimen))
252 || (offsetof (struct real_gmon_hist_hdr, dimen_abbrev)
253 != offsetof (struct gmon_hist_hdr, dimen_abbrev)))
254 abort ();
255
d165ca64
L
256 thdr.low_pc = (char *) _gmonparam.lowpc - load_address;
257 thdr.high_pc = (char *) _gmonparam.highpc - load_address;
f521be31
UD
258 thdr.hist_size = _gmonparam.kcountsize / sizeof (HISTCOUNTER);
259 thdr.prof_rate = __profile_frequency ();
5ae9d168 260 strncpy (thdr.dimen, "seconds", sizeof (thdr.dimen));
b20e47cb 261 thdr.dimen_abbrev = 's';
11c981a9 262
19926de9 263 __writev_nocancel_nostatus (fd, iov, 3);
b20e47cb 264 }
11c981a9
RM
265}
266
b20e47cb
RM
267
268static void
d165ca64 269write_call_graph (int fd, u_long load_address)
11c981a9 270{
e7fd8a39 271#define NARCS_PER_WRITEV 32
5ae9d168 272 u_char tag = GMON_TAG_CG_ARC;
e7fd8a39 273 struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITEV]
5ae9d168 274 __attribute__ ((aligned (__alignof__ (char*))));
be5b0fbc 275 ARCINDEX from_index, to_index;
eb64f8cb 276 u_long from_len;
b20e47cb 277 u_long frompc;
e7fd8a39
UD
278 struct iovec iov[2 * NARCS_PER_WRITEV];
279 int nfilled;
b20e47cb 280
e7fd8a39 281 for (nfilled = 0; nfilled < NARCS_PER_WRITEV; ++nfilled)
5ae9d168 282 {
e7fd8a39
UD
283 iov[2 * nfilled].iov_base = &tag;
284 iov[2 * nfilled].iov_len = sizeof (tag);
5ae9d168 285
e7fd8a39
UD
286 iov[2 * nfilled + 1].iov_base = &raw_arc[nfilled];
287 iov[2 * nfilled + 1].iov_len = sizeof (struct gmon_cg_arc_record);
288 }
289
290 nfilled = 0;
5ae9d168 291 from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms);
b20e47cb
RM
292 for (from_index = 0; from_index < from_len; ++from_index)
293 {
294 if (_gmonparam.froms[from_index] == 0)
295 continue;
296
297 frompc = _gmonparam.lowpc;
298 frompc += (from_index * _gmonparam.hashfraction
5ae9d168 299 * sizeof (*_gmonparam.froms));
b20e47cb
RM
300 for (to_index = _gmonparam.froms[from_index];
301 to_index != 0;
302 to_index = _gmonparam.tos[to_index].link)
303 {
328c5f65
UD
304 struct arc
305 {
306 char *frompc;
307 char *selfpc;
308 int32_t count;
309 }
310 arc;
311
d165ca64
L
312 arc.frompc = (char *) frompc - load_address;
313 arc.selfpc = ((char *) _gmonparam.tos[to_index].selfpc
314 - load_address);
328c5f65
UD
315 arc.count = _gmonparam.tos[to_index].count;
316 memcpy (raw_arc + nfilled, &arc, sizeof (raw_arc [0]));
e7fd8a39
UD
317
318 if (++nfilled == NARCS_PER_WRITEV)
3e5f5557 319 {
19926de9 320 __writev_nocancel_nostatus (fd, iov, 2 * nfilled);
3e5f5557
UD
321 nfilled = 0;
322 }
11c981a9 323 }
b20e47cb 324 }
e7fd8a39 325 if (nfilled > 0)
19926de9 326 __writev_nocancel_nostatus (fd, iov, 2 * nfilled);
11c981a9
RM
327}
328
b20e47cb
RM
329
330static void
9d46370c 331write_bb_counts (int fd)
11c981a9 332{
b20e47cb 333 struct __bb *grp;
5ae9d168 334 u_char tag = GMON_TAG_BB_COUNT;
3e5f5557
UD
335 size_t ncounts;
336 size_t i;
b20e47cb 337
5ae9d168
UD
338 struct iovec bbhead[2] =
339 {
340 { &tag, sizeof (tag) },
341 { &ncounts, sizeof (ncounts) }
342 };
3e5f5557
UD
343 struct iovec bbbody[8];
344 size_t nfilled;
5ae9d168 345
3e5f5557
UD
346 for (i = 0; i < (sizeof (bbbody) / sizeof (bbbody[0])); i += 2)
347 {
348 bbbody[i].iov_len = sizeof (grp->addresses[0]);
349 bbbody[i + 1].iov_len = sizeof (grp->counts[0]);
350 }
5ae9d168 351
b20e47cb
RM
352 /* Write each group of basic-block info (all basic-blocks in a
353 compilation unit form a single group). */
354
355 for (grp = __bb_head; grp; grp = grp->next)
356 {
357 ncounts = grp->ncounts;
19926de9 358 __writev_nocancel_nostatus (fd, bbhead, 2);
e7fd8a39 359 for (nfilled = i = 0; i < ncounts; ++i)
b20e47cb 360 {
3e5f5557
UD
361 if (nfilled > (sizeof (bbbody) / sizeof (bbbody[0])) - 2)
362 {
19926de9 363 __writev_nocancel_nostatus (fd, bbbody, nfilled);
3e5f5557
UD
364 nfilled = 0;
365 }
366
367 bbbody[nfilled++].iov_base = (char *) &grp->addresses[i];
368 bbbody[nfilled++].iov_base = &grp->counts[i];
b20e47cb 369 }
e7fd8a39 370 if (nfilled > 0)
19926de9 371 __writev_nocancel_nostatus (fd, bbbody, nfilled);
b20e47cb 372 }
11c981a9
RM
373}
374
375
0413b54c
UD
376static void
377write_gmon (void)
b20e47cb 378{
14c44e2e
UD
379 int fd = -1;
380 char *env;
b20e47cb 381
14c44e2e
UD
382 env = getenv ("GMON_OUT_PREFIX");
383 if (env != NULL && !__libc_enable_secure)
b20e47cb 384 {
14c44e2e
UD
385 size_t len = strlen (env);
386 char buf[len + 20];
4c5b09ed 387 __snprintf (buf, sizeof (buf), "%s.%u", env, __getpid ());
c2284574 388 fd = __open_nocancel (buf, O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW, 0666);
14c44e2e
UD
389 }
390
391 if (fd == -1)
392 {
c2284574 393 fd = __open_nocancel ("gmon.out", O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW,
fbb37d25 394 0666);
14c44e2e
UD
395 if (fd < 0)
396 {
397 char buf[300];
398 int errnum = errno;
df6f8969 399 __fxprintf (NULL, "_mcleanup: gmon.out: %s\n",
df6f8969 400 __strerror_r (errnum, buf, sizeof buf));
14c44e2e
UD
401 return;
402 }
b20e47cb
RM
403 }
404
405 /* write gmon.out header: */
f521be31
UD
406 struct real_gmon_hdr
407 {
408 char cookie[4];
409 int32_t version;
410 char spare[3 * 4];
411 } ghdr;
412 if (sizeof (ghdr) != sizeof (struct gmon_hdr)
413 || (offsetof (struct real_gmon_hdr, cookie)
414 != offsetof (struct gmon_hdr, cookie))
415 || (offsetof (struct real_gmon_hdr, version)
416 != offsetof (struct gmon_hdr, version)))
417 abort ();
5ae9d168 418 memcpy (&ghdr.cookie[0], GMON_MAGIC, sizeof (ghdr.cookie));
f521be31
UD
419 ghdr.version = GMON_VERSION;
420 memset (ghdr.spare, '\0', sizeof (ghdr.spare));
c647fb88 421 __write_nocancel (fd, &ghdr, sizeof (struct gmon_hdr));
b20e47cb 422
d165ca64
L
423 /* Get load_address to profile PIE. */
424 u_long load_address = 0;
425#ifdef PIC
426 __dl_iterate_phdr (callback, &load_address);
427#endif
428
b20e47cb 429 /* write PC histogram: */
d165ca64 430 write_hist (fd, load_address);
b20e47cb
RM
431
432 /* write call-graph: */
d165ca64 433 write_call_graph (fd, load_address);
b20e47cb
RM
434
435 /* write basic-block execution counts: */
5ae9d168 436 write_bb_counts (fd);
b20e47cb 437
c181840c 438 __close_nocancel_nostatus (fd);
0413b54c
UD
439}
440
441
442void
443__write_profiling (void)
444{
445 int save = _gmonparam.state;
446 _gmonparam.state = GMON_PROF_OFF;
447 if (save == GMON_PROF_ON)
448 write_gmon ();
449 _gmonparam.state = save;
450}
f5bf21a7
UD
451#ifndef SHARED
452/* This symbol isn't used anywhere in the DSO and it is not exported.
453 This would normally mean it should be removed to get the same API
454 in static libraries. But since profiling is special in static libs
455 anyway we keep it. But not when building the DSO since some
456 quality assurance tests will otherwise trigger. */
0413b54c 457weak_alias (__write_profiling, write_profiling)
f5bf21a7 458#endif
0413b54c
UD
459
460
461void
462_mcleanup (void)
463{
14e9dd67 464 __moncontrol (0);
0413b54c 465
14e9dd67 466 if (_gmonparam.state != GMON_PROF_ERROR)
0413b54c
UD
467 write_gmon ();
468
14e9dd67 469 /* free the memory. */
72e6cdfa 470 free (_gmonparam.tos);
b20e47cb 471}