]> git.ipfire.org Git - thirdparty/glibc.git/blob - gmon/gmon.c
bc0e29438a02cbe7db36e0a1d3b5495547277ec3
[thirdparty/glibc.git] / gmon / gmon.c
1 /*-
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 #define TUNABLE_NAMESPACE gmon
50 #include <elf/dl-tunables.h>
51
52 #ifdef PIC
53 # include <link.h>
54
55 static int
56 callback (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
72
73 /* Head of basic-block list or NULL. */
74 struct __bb *__bb_head attribute_hidden;
75
76 struct gmonparam _gmonparam attribute_hidden = { GMON_PROF_OFF };
77
78 /*
79 * See profil(2) where this is described:
80 */
81 static int s_scale;
82 #define SCALE_1_TO_1 0x10000L
83
84 #define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1)
85
86 void moncontrol (int mode);
87 void __moncontrol (int mode);
88 libc_hidden_proto (__moncontrol)
89 static void write_hist (int fd, u_long load_address);
90 static void write_call_graph (int fd, u_long load_address);
91 static void write_bb_counts (int fd);
92
93 /*
94 * Control profiling
95 * profiling is what mcount checks to see if
96 * all the data structures are ready.
97 */
98 void
99 __moncontrol (int mode)
100 {
101 struct gmonparam *p = &_gmonparam;
102
103 /* Treat start request as stop if error or gmon not initialized. */
104 if (mode && p->state != GMON_PROF_ERROR && p->tos != NULL)
105 {
106 /* start */
107 __profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale);
108 p->state = GMON_PROF_ON;
109 }
110 else
111 {
112 /* stop */
113 __profil(NULL, 0, 0, 0);
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;
117 }
118 }
119 libc_hidden_def (__moncontrol)
120 weak_alias (__moncontrol, moncontrol)
121
122
123 void
124 __monstartup (u_long lowpc, u_long highpc)
125 {
126 int o;
127 char *cp;
128 struct gmonparam *p = &_gmonparam;
129 long int minarcs, maxarcs;
130
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 }
139
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
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;
155 /* This looks like a typo, but it's here to align the p->froms
156 section. */
157 p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms));
158 p->hashfraction = HASHFRACTION;
159 p->log_hashfraction = -1;
160 /* The following test must be kept in sync with the corresponding
161 test in mcount.c. */
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 }
167 p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms));
168 p->tolimit = p->textsize * ARCDENSITY / 100;
169 if (p->tolimit < minarcs)
170 p->tolimit = minarcs;
171 else if (p->tolimit > maxarcs)
172 p->tolimit = maxarcs;
173 p->tossize = p->tolimit * sizeof(struct tostruct);
174
175 cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1);
176 if (! cp)
177 {
178 ERR("monstartup: out of memory\n");
179 p->tos = NULL;
180 p->state = GMON_PROF_ERROR;
181 return;
182 }
183 p->tos = (struct tostruct *)cp;
184 cp += p->tossize;
185 p->kcount = (HISTCOUNTER *)cp;
186 cp += p->kcountsize;
187 p->froms = (ARCINDEX *)cp;
188
189 p->tos[0].link = 0;
190
191 o = p->highpc - p->lowpc;
192 if (p->kcountsize < (u_long) o)
193 {
194 #ifndef hp300
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);
208 #endif
209 } else
210 s_scale = SCALE_1_TO_1;
211
212 __moncontrol(1);
213 }
214 weak_alias (__monstartup, monstartup)
215
216
217 static void
218 write_hist (int fd, u_long load_address)
219 {
220 u_char tag = GMON_TAG_TIME_HIST;
221
222 if (_gmonparam.kcountsize > 0)
223 {
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;
233 struct iovec iov[3] =
234 {
235 { &tag, sizeof (tag) },
236 { &thdr, sizeof (struct gmon_hist_hdr) },
237 { _gmonparam.kcount, _gmonparam.kcountsize }
238 };
239
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
255 thdr.low_pc = (char *) _gmonparam.lowpc - load_address;
256 thdr.high_pc = (char *) _gmonparam.highpc - load_address;
257 thdr.hist_size = _gmonparam.kcountsize / sizeof (HISTCOUNTER);
258 thdr.prof_rate = __profile_frequency ();
259 strncpy (thdr.dimen, "seconds", sizeof (thdr.dimen));
260 thdr.dimen_abbrev = 's';
261
262 __writev_nocancel_nostatus (fd, iov, 3);
263 }
264 }
265
266
267 static void
268 write_call_graph (int fd, u_long load_address)
269 {
270 #define NARCS_PER_WRITEV 32
271 u_char tag = GMON_TAG_CG_ARC;
272 struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITEV]
273 __attribute__ ((aligned (__alignof__ (char*))));
274 ARCINDEX from_index, to_index;
275 u_long from_len;
276 u_long frompc;
277 struct iovec iov[2 * NARCS_PER_WRITEV];
278 int nfilled;
279
280 for (nfilled = 0; nfilled < NARCS_PER_WRITEV; ++nfilled)
281 {
282 iov[2 * nfilled].iov_base = &tag;
283 iov[2 * nfilled].iov_len = sizeof (tag);
284
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;
290 from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms);
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
298 * sizeof (*_gmonparam.froms));
299 for (to_index = _gmonparam.froms[from_index];
300 to_index != 0;
301 to_index = _gmonparam.tos[to_index].link)
302 {
303 struct arc
304 {
305 char *frompc;
306 char *selfpc;
307 int32_t count;
308 }
309 arc;
310
311 arc.frompc = (char *) frompc - load_address;
312 arc.selfpc = ((char *) _gmonparam.tos[to_index].selfpc
313 - load_address);
314 arc.count = _gmonparam.tos[to_index].count;
315 memcpy (raw_arc + nfilled, &arc, sizeof (raw_arc [0]));
316
317 if (++nfilled == NARCS_PER_WRITEV)
318 {
319 __writev_nocancel_nostatus (fd, iov, 2 * nfilled);
320 nfilled = 0;
321 }
322 }
323 }
324 if (nfilled > 0)
325 __writev_nocancel_nostatus (fd, iov, 2 * nfilled);
326 }
327
328
329 static void
330 write_bb_counts (int fd)
331 {
332 struct __bb *grp;
333 u_char tag = GMON_TAG_BB_COUNT;
334 size_t ncounts;
335 size_t i;
336
337 struct iovec bbhead[2] =
338 {
339 { &tag, sizeof (tag) },
340 { &ncounts, sizeof (ncounts) }
341 };
342 struct iovec bbbody[8];
343 size_t nfilled;
344
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 }
350
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;
357 __writev_nocancel_nostatus (fd, bbhead, 2);
358 for (nfilled = i = 0; i < ncounts; ++i)
359 {
360 if (nfilled > (sizeof (bbbody) / sizeof (bbbody[0])) - 2)
361 {
362 __writev_nocancel_nostatus (fd, bbbody, nfilled);
363 nfilled = 0;
364 }
365
366 bbbody[nfilled++].iov_base = (char *) &grp->addresses[i];
367 bbbody[nfilled++].iov_base = &grp->counts[i];
368 }
369 if (nfilled > 0)
370 __writev_nocancel_nostatus (fd, bbbody, nfilled);
371 }
372 }
373
374
375 static void
376 write_gmon (void)
377 {
378 int fd = -1;
379 char *env;
380
381 env = getenv ("GMON_OUT_PREFIX");
382 if (env != NULL && !__libc_enable_secure)
383 {
384 size_t len = strlen (env);
385 char buf[len + 20];
386 __snprintf (buf, sizeof (buf), "%s.%u", env, __getpid ());
387 fd = __open_nocancel (buf, O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW, 0666);
388 }
389
390 if (fd == -1)
391 {
392 fd = __open_nocancel ("gmon.out", O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW,
393 0666);
394 if (fd < 0)
395 {
396 char buf[300];
397 int errnum = errno;
398 __fxprintf (NULL, "_mcleanup: gmon.out: %s\n",
399 __strerror_r (errnum, buf, sizeof buf));
400 return;
401 }
402 }
403
404 /* write gmon.out header: */
405 struct real_gmon_hdr
406 {
407 char cookie[4];
408 int32_t version;
409 char spare[3 * 4];
410 } ghdr;
411 if (sizeof (ghdr) != sizeof (struct gmon_hdr)
412 || (offsetof (struct real_gmon_hdr, cookie)
413 != offsetof (struct gmon_hdr, cookie))
414 || (offsetof (struct real_gmon_hdr, version)
415 != offsetof (struct gmon_hdr, version)))
416 abort ();
417 memcpy (&ghdr.cookie[0], GMON_MAGIC, sizeof (ghdr.cookie));
418 ghdr.version = GMON_VERSION;
419 memset (ghdr.spare, '\0', sizeof (ghdr.spare));
420 __write_nocancel (fd, &ghdr, sizeof (struct gmon_hdr));
421
422 /* Get load_address to profile PIE. */
423 u_long load_address = 0;
424 #ifdef PIC
425 __dl_iterate_phdr (callback, &load_address);
426 #endif
427
428 /* write PC histogram: */
429 write_hist (fd, load_address);
430
431 /* write call-graph: */
432 write_call_graph (fd, load_address);
433
434 /* write basic-block execution counts: */
435 write_bb_counts (fd);
436
437 __close_nocancel_nostatus (fd);
438 }
439
440
441 void
442 __write_profiling (void)
443 {
444 int save = _gmonparam.state;
445 _gmonparam.state = GMON_PROF_OFF;
446 if (save == GMON_PROF_ON)
447 write_gmon ();
448 _gmonparam.state = save;
449 }
450 #ifndef SHARED
451 /* This symbol isn't used anywhere in the DSO and it is not exported.
452 This would normally mean it should be removed to get the same API
453 in static libraries. But since profiling is special in static libs
454 anyway we keep it. But not when building the DSO since some
455 quality assurance tests will otherwise trigger. */
456 weak_alias (__write_profiling, write_profiling)
457 #endif
458
459
460 void
461 _mcleanup (void)
462 {
463 __moncontrol (0);
464
465 if (_gmonparam.state != GMON_PROF_ERROR && _gmonparam.tos != NULL)
466 write_gmon ();
467
468 /* free the memory. */
469 free (_gmonparam.tos);
470
471 /* reset buffer to initial state for safety */
472 memset(&_gmonparam, 0, sizeof _gmonparam);
473 /* somewhat confusingly, ON=0, OFF=3 */
474 _gmonparam.state = GMON_PROF_OFF;
475 }