]> git.ipfire.org Git - thirdparty/glibc.git/blame - gmon/gmon.c
hurd: Move internal functions to internal header
[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
33237fe8
AZN
49#define TUNABLE_NAMESPACE gmon
50#include <elf/dl-tunables.h>
31be941e 51
d165ca64
L
52#ifdef PIC
53# include <link.h>
54
55static int
56callback (struct dl_phdr_info *info, size_t size, void *data)
57{
58 if (info->dlpi_name[0] == '\0')
59 {
60 /* The link map for the executable is created by calling
61 _dl_new_object with "" as filename. dl_iterate_phdr
62 calls the callback function with filename from the
63 link map as dlpi_name. */
64 u_long *load_address = data;
65 *load_address = (u_long) info->dlpi_addr;
66 return 1;
67 }
68
69 return 0;
70}
71#endif
8dcc6a3f 72
100351c3
UD
73/* Head of basic-block list or NULL. */
74struct __bb *__bb_head attribute_hidden;
b20e47cb 75
0e47dbd0 76struct gmonparam _gmonparam attribute_hidden = { GMON_PROF_OFF };
11c981a9 77
b20e47cb
RM
78/*
79 * See profil(2) where this is described:
80 */
11c981a9 81static int s_scale;
11c981a9
RM
82#define SCALE_1_TO_1 0x10000L
83
c647fb88 84#define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1)
b20e47cb 85
79937577
UD
86void moncontrol (int mode);
87void __moncontrol (int mode);
fa426590 88libc_hidden_proto (__moncontrol)
d165ca64
L
89static void write_hist (int fd, u_long load_address);
90static void write_call_graph (int fd, u_long load_address);
8d2f9410 91static void write_bb_counts (int fd);
11336c16 92
b20e47cb
RM
93/*
94 * Control profiling
95 * profiling is what mcount checks to see if
96 * all the data structures are ready.
97 */
98void
9d46370c 99__moncontrol (int mode)
b20e47cb
RM
100{
101 struct gmonparam *p = &_gmonparam;
102
bde12187
SK
103 /* Treat start request as stop if error or gmon not initialized. */
104 if (mode && p->state != GMON_PROF_ERROR && p->tos != NULL)
b20e47cb
RM
105 {
106 /* start */
9a0a462c 107 __profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale);
b20e47cb
RM
108 p->state = GMON_PROF_ON;
109 }
110 else
111 {
112 /* stop */
9a0a462c 113 __profil(NULL, 0, 0, 0);
bde12187
SK
114 /* Don't change the state if we ran into an error. */
115 if (p->state != GMON_PROF_ERROR)
116 p->state = GMON_PROF_OFF;
b20e47cb
RM
117 }
118}
fa426590 119libc_hidden_def (__moncontrol)
69173865 120weak_alias (__moncontrol, moncontrol)
11c981a9 121
11c981a9
RM
122
123void
9d46370c 124__monstartup (u_long lowpc, u_long highpc)
11c981a9 125{
2e09a79a 126 int o;
b20e47cb
RM
127 char *cp;
128 struct gmonparam *p = &_gmonparam;
31be941e
SK
129 long int minarcs, maxarcs;
130
31be941e
SK
131 /* Read minarcs/maxarcs tunables. */
132 minarcs = TUNABLE_GET (minarcs, int32_t, NULL);
133 maxarcs = TUNABLE_GET (maxarcs, int32_t, NULL);
134 if (maxarcs < minarcs)
135 {
136 ERR("monstartup: maxarcs < minarcs, setting maxarcs = minarcs\n");
137 maxarcs = minarcs;
138 }
b20e47cb 139
bde12187
SK
140 /*
141 * If we are incorrectly called twice in a row (without an
142 * intervening call to _mcleanup), ignore the second call to
143 * prevent leaking memory.
144 */
145 if (p->tos != NULL)
146 return;
147
b20e47cb
RM
148 /*
149 * round lowpc and highpc to multiples of the density we're using
150 * so the rest of the scaling (here and in gprof) stays in ints.
151 */
152 p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
153 p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
154 p->textsize = p->highpc - p->lowpc;
801af9fa
ЛЮLY
155 /* This looks like a typo, but it's here to align the p->froms
156 section. */
69ac9d07 157 p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms));
b20e47cb
RM
158 p->hashfraction = HASHFRACTION;
159 p->log_hashfraction = -1;
9a0a462c
UD
160 /* The following test must be kept in sync with the corresponding
161 test in mcount.c. */
b20e47cb
RM
162 if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) {
163 /* if HASHFRACTION is a power of two, mcount can use shifting
164 instead of integer division. Precompute shift amount. */
165 p->log_hashfraction = ffs(p->hashfraction * sizeof(*p->froms)) - 1;
166 }
801af9fa 167 p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms));
b20e47cb 168 p->tolimit = p->textsize * ARCDENSITY / 100;
31be941e
SK
169 if (p->tolimit < minarcs)
170 p->tolimit = minarcs;
171 else if (p->tolimit > maxarcs)
172 p->tolimit = maxarcs;
b20e47cb 173 p->tossize = p->tolimit * sizeof(struct tostruct);
11c981a9 174
0413b54c 175 cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1);
b20e47cb
RM
176 if (! cp)
177 {
14e9dd67
UD
178 ERR("monstartup: out of memory\n");
179 p->tos = NULL;
180 p->state = GMON_PROF_ERROR;
b20e47cb
RM
181 return;
182 }
b20e47cb
RM
183 p->tos = (struct tostruct *)cp;
184 cp += p->tossize;
be5b0fbc 185 p->kcount = (HISTCOUNTER *)cp;
b20e47cb 186 cp += p->kcountsize;
be5b0fbc 187 p->froms = (ARCINDEX *)cp;
11c981a9 188
b20e47cb
RM
189 p->tos[0].link = 0;
190
191 o = p->highpc - p->lowpc;
d68171ed 192 if (p->kcountsize < (u_long) o)
b20e47cb 193 {
11c981a9 194#ifndef hp300
b20e47cb
RM
195 s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
196#else
197 /* avoid floating point operations */
198 int quot = o / p->kcountsize;
199
200 if (quot >= 0x10000)
201 s_scale = 1;
202 else if (quot >= 0x100)
203 s_scale = 0x10000 / quot;
204 else if (o >= 0x800000)
205 s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
206 else
207 s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
11c981a9 208#endif
b20e47cb
RM
209 } else
210 s_scale = SCALE_1_TO_1;
11c981a9 211
3996f34b 212 __moncontrol(1);
11c981a9 213}
1ab18a5b 214weak_alias (__monstartup, monstartup)
11c981a9 215
b20e47cb
RM
216
217static void
d165ca64 218write_hist (int fd, u_long load_address)
11c981a9 219{
5ae9d168 220 u_char tag = GMON_TAG_TIME_HIST;
11c981a9 221
b20e47cb
RM
222 if (_gmonparam.kcountsize > 0)
223 {
f521be31
UD
224 struct real_gmon_hist_hdr
225 {
226 char *low_pc;
227 char *high_pc;
228 int32_t hist_size;
229 int32_t prof_rate;
230 char dimen[15];
231 char dimen_abbrev;
232 } thdr;
5ae9d168 233 struct iovec iov[3] =
3ce1f295 234 {
5ae9d168
UD
235 { &tag, sizeof (tag) },
236 { &thdr, sizeof (struct gmon_hist_hdr) },
237 { _gmonparam.kcount, _gmonparam.kcountsize }
238 };
239
f521be31
UD
240 if (sizeof (thdr) != sizeof (struct gmon_hist_hdr)
241 || (offsetof (struct real_gmon_hist_hdr, low_pc)
242 != offsetof (struct gmon_hist_hdr, low_pc))
243 || (offsetof (struct real_gmon_hist_hdr, high_pc)
244 != offsetof (struct gmon_hist_hdr, high_pc))
245 || (offsetof (struct real_gmon_hist_hdr, hist_size)
246 != offsetof (struct gmon_hist_hdr, hist_size))
247 || (offsetof (struct real_gmon_hist_hdr, prof_rate)
248 != offsetof (struct gmon_hist_hdr, prof_rate))
249 || (offsetof (struct real_gmon_hist_hdr, dimen)
250 != offsetof (struct gmon_hist_hdr, dimen))
251 || (offsetof (struct real_gmon_hist_hdr, dimen_abbrev)
252 != offsetof (struct gmon_hist_hdr, dimen_abbrev)))
253 abort ();
254
d165ca64
L
255 thdr.low_pc = (char *) _gmonparam.lowpc - load_address;
256 thdr.high_pc = (char *) _gmonparam.highpc - load_address;
f521be31
UD
257 thdr.hist_size = _gmonparam.kcountsize / sizeof (HISTCOUNTER);
258 thdr.prof_rate = __profile_frequency ();
5ae9d168 259 strncpy (thdr.dimen, "seconds", sizeof (thdr.dimen));
b20e47cb 260 thdr.dimen_abbrev = 's';
11c981a9 261
19926de9 262 __writev_nocancel_nostatus (fd, iov, 3);
b20e47cb 263 }
11c981a9
RM
264}
265
b20e47cb
RM
266
267static void
d165ca64 268write_call_graph (int fd, u_long load_address)
11c981a9 269{
e7fd8a39 270#define NARCS_PER_WRITEV 32
5ae9d168 271 u_char tag = GMON_TAG_CG_ARC;
e7fd8a39 272 struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITEV]
5ae9d168 273 __attribute__ ((aligned (__alignof__ (char*))));
be5b0fbc 274 ARCINDEX from_index, to_index;
eb64f8cb 275 u_long from_len;
b20e47cb 276 u_long frompc;
e7fd8a39
UD
277 struct iovec iov[2 * NARCS_PER_WRITEV];
278 int nfilled;
b20e47cb 279
e7fd8a39 280 for (nfilled = 0; nfilled < NARCS_PER_WRITEV; ++nfilled)
5ae9d168 281 {
e7fd8a39
UD
282 iov[2 * nfilled].iov_base = &tag;
283 iov[2 * nfilled].iov_len = sizeof (tag);
5ae9d168 284
e7fd8a39
UD
285 iov[2 * nfilled + 1].iov_base = &raw_arc[nfilled];
286 iov[2 * nfilled + 1].iov_len = sizeof (struct gmon_cg_arc_record);
287 }
288
289 nfilled = 0;
5ae9d168 290 from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms);
b20e47cb
RM
291 for (from_index = 0; from_index < from_len; ++from_index)
292 {
293 if (_gmonparam.froms[from_index] == 0)
294 continue;
295
296 frompc = _gmonparam.lowpc;
297 frompc += (from_index * _gmonparam.hashfraction
5ae9d168 298 * sizeof (*_gmonparam.froms));
b20e47cb
RM
299 for (to_index = _gmonparam.froms[from_index];
300 to_index != 0;
301 to_index = _gmonparam.tos[to_index].link)
302 {
328c5f65
UD
303 struct arc
304 {
305 char *frompc;
306 char *selfpc;
307 int32_t count;
308 }
309 arc;
310
d165ca64
L
311 arc.frompc = (char *) frompc - load_address;
312 arc.selfpc = ((char *) _gmonparam.tos[to_index].selfpc
313 - load_address);
328c5f65
UD
314 arc.count = _gmonparam.tos[to_index].count;
315 memcpy (raw_arc + nfilled, &arc, sizeof (raw_arc [0]));
e7fd8a39
UD
316
317 if (++nfilled == NARCS_PER_WRITEV)
3e5f5557 318 {
19926de9 319 __writev_nocancel_nostatus (fd, iov, 2 * nfilled);
3e5f5557
UD
320 nfilled = 0;
321 }
11c981a9 322 }
b20e47cb 323 }
e7fd8a39 324 if (nfilled > 0)
19926de9 325 __writev_nocancel_nostatus (fd, iov, 2 * nfilled);
11c981a9
RM
326}
327
b20e47cb
RM
328
329static void
9d46370c 330write_bb_counts (int fd)
11c981a9 331{
b20e47cb 332 struct __bb *grp;
5ae9d168 333 u_char tag = GMON_TAG_BB_COUNT;
3e5f5557
UD
334 size_t ncounts;
335 size_t i;
b20e47cb 336
5ae9d168
UD
337 struct iovec bbhead[2] =
338 {
339 { &tag, sizeof (tag) },
340 { &ncounts, sizeof (ncounts) }
341 };
3e5f5557
UD
342 struct iovec bbbody[8];
343 size_t nfilled;
5ae9d168 344
3e5f5557
UD
345 for (i = 0; i < (sizeof (bbbody) / sizeof (bbbody[0])); i += 2)
346 {
347 bbbody[i].iov_len = sizeof (grp->addresses[0]);
348 bbbody[i + 1].iov_len = sizeof (grp->counts[0]);
349 }
5ae9d168 350
b20e47cb
RM
351 /* Write each group of basic-block info (all basic-blocks in a
352 compilation unit form a single group). */
353
354 for (grp = __bb_head; grp; grp = grp->next)
355 {
356 ncounts = grp->ncounts;
19926de9 357 __writev_nocancel_nostatus (fd, bbhead, 2);
e7fd8a39 358 for (nfilled = i = 0; i < ncounts; ++i)
b20e47cb 359 {
3e5f5557
UD
360 if (nfilled > (sizeof (bbbody) / sizeof (bbbody[0])) - 2)
361 {
19926de9 362 __writev_nocancel_nostatus (fd, bbbody, nfilled);
3e5f5557
UD
363 nfilled = 0;
364 }
365
366 bbbody[nfilled++].iov_base = (char *) &grp->addresses[i];
367 bbbody[nfilled++].iov_base = &grp->counts[i];
b20e47cb 368 }
e7fd8a39 369 if (nfilled > 0)
19926de9 370 __writev_nocancel_nostatus (fd, bbbody, nfilled);
b20e47cb 371 }
11c981a9
RM
372}
373
374
0413b54c
UD
375static void
376write_gmon (void)
b20e47cb 377{
14c44e2e
UD
378 int fd = -1;
379 char *env;
b20e47cb 380
14c44e2e
UD
381 env = getenv ("GMON_OUT_PREFIX");
382 if (env != NULL && !__libc_enable_secure)
b20e47cb 383 {
14c44e2e
UD
384 size_t len = strlen (env);
385 char buf[len + 20];
4c5b09ed 386 __snprintf (buf, sizeof (buf), "%s.%u", env, __getpid ());
533deafb
SB
387 fd = __open_nocancel (buf, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW
388 | O_CLOEXEC, 0666);
14c44e2e
UD
389 }
390
391 if (fd == -1)
392 {
533deafb
SB
393 fd = __open_nocancel ("gmon.out", O_CREAT | O_TRUNC | O_WRONLY
394 | O_NOFOLLOW | O_CLOEXEC, 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
bde12187 466 if (_gmonparam.state != GMON_PROF_ERROR && _gmonparam.tos != NULL)
0413b54c
UD
467 write_gmon ();
468
14e9dd67 469 /* free the memory. */
72e6cdfa 470 free (_gmonparam.tos);
bde12187
SK
471
472 /* reset buffer to initial state for safety */
473 memset(&_gmonparam, 0, sizeof _gmonparam);
474 /* somewhat confusingly, ON=0, OFF=3 */
475 _gmonparam.state = GMON_PROF_OFF;
b20e47cb 476}