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