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
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;
113 p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms));
114 p->hashfraction = HASHFRACTION;
115 p->log_hashfraction = -1;
116 /* The following test must be kept in sync with the corresponding
117 test in mcount.c. */
118 if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) {
119 /* if HASHFRACTION is a power of two, mcount can use shifting
120 instead of integer division. Precompute shift amount. */
121 p->log_hashfraction = ffs(p->hashfraction * sizeof(*p->froms)) - 1;
122 }
123 p->fromssize = p->textsize / HASHFRACTION;
124 p->tolimit = p->textsize * ARCDENSITY / 100;
125 if (p->tolimit < MINARCS)
126 p->tolimit = MINARCS;
127 else if (p->tolimit > MAXARCS)
128 p->tolimit = MAXARCS;
129 p->tossize = p->tolimit * sizeof(struct tostruct);
130
131 cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1);
132 if (! cp)
133 {
134 ERR("monstartup: out of memory\n");
135 p->tos = NULL;
136 p->state = GMON_PROF_ERROR;
137 return;
138 }
139 p->tos = (struct tostruct *)cp;
140 cp += p->tossize;
141 p->kcount = (HISTCOUNTER *)cp;
142 cp += p->kcountsize;
143 p->froms = (ARCINDEX *)cp;
144
145 p->tos[0].link = 0;
146
147 o = p->highpc - p->lowpc;
148 if (p->kcountsize < (u_long) o)
149 {
150#ifndef hp300
151 s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
152#else
153 /* avoid floating point operations */
154 int quot = o / p->kcountsize;
155
156 if (quot >= 0x10000)
157 s_scale = 1;
158 else if (quot >= 0x100)
159 s_scale = 0x10000 / quot;
160 else if (o >= 0x800000)
161 s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
162 else
163 s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
164#endif
165 } else
166 s_scale = SCALE_1_TO_1;
167
168 __moncontrol(1);
169}
170weak_alias (__monstartup, monstartup)
171
172
173static void
174internal_function
175write_hist (int fd)
176{
177 u_char tag = GMON_TAG_TIME_HIST;
178
179 if (_gmonparam.kcountsize > 0)
180 {
181 struct real_gmon_hist_hdr
182 {
183 char *low_pc;
184 char *high_pc;
185 int32_t hist_size;
186 int32_t prof_rate;
187 char dimen[15];
188 char dimen_abbrev;
189 } thdr;
190 struct iovec iov[3] =
191 {
192 { &tag, sizeof (tag) },
193 { &thdr, sizeof (struct gmon_hist_hdr) },
194 { _gmonparam.kcount, _gmonparam.kcountsize }
195 };
196
197 if (sizeof (thdr) != sizeof (struct gmon_hist_hdr)
198 || (offsetof (struct real_gmon_hist_hdr, low_pc)
199 != offsetof (struct gmon_hist_hdr, low_pc))
200 || (offsetof (struct real_gmon_hist_hdr, high_pc)
201 != offsetof (struct gmon_hist_hdr, high_pc))
202 || (offsetof (struct real_gmon_hist_hdr, hist_size)
203 != offsetof (struct gmon_hist_hdr, hist_size))
204 || (offsetof (struct real_gmon_hist_hdr, prof_rate)
205 != offsetof (struct gmon_hist_hdr, prof_rate))
206 || (offsetof (struct real_gmon_hist_hdr, dimen)
207 != offsetof (struct gmon_hist_hdr, dimen))
208 || (offsetof (struct real_gmon_hist_hdr, dimen_abbrev)
209 != offsetof (struct gmon_hist_hdr, dimen_abbrev)))
210 abort ();
211
212 thdr.low_pc = (char *) _gmonparam.lowpc;
213 thdr.high_pc = (char *) _gmonparam.highpc;
214 thdr.hist_size = _gmonparam.kcountsize / sizeof (HISTCOUNTER);
215 thdr.prof_rate = __profile_frequency ();
216 strncpy (thdr.dimen, "seconds", sizeof (thdr.dimen));
217 thdr.dimen_abbrev = 's';
218
219 writev_not_cancel_no_status (fd, iov, 3);
220 }
221}
222
223
224static void
225internal_function
226write_call_graph (int fd)
227{
228#define NARCS_PER_WRITEV 32
229 u_char tag = GMON_TAG_CG_ARC;
230 struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITEV]
231 __attribute__ ((aligned (__alignof__ (char*))));
232 ARCINDEX from_index, to_index;
233 u_long from_len;
234 u_long frompc;
235 struct iovec iov[2 * NARCS_PER_WRITEV];
236 int nfilled;
237
238 for (nfilled = 0; nfilled < NARCS_PER_WRITEV; ++nfilled)
239 {
240 iov[2 * nfilled].iov_base = &tag;
241 iov[2 * nfilled].iov_len = sizeof (tag);
242
243 iov[2 * nfilled + 1].iov_base = &raw_arc[nfilled];
244 iov[2 * nfilled + 1].iov_len = sizeof (struct gmon_cg_arc_record);
245 }
246
247 nfilled = 0;
248 from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms);
249 for (from_index = 0; from_index < from_len; ++from_index)
250 {
251 if (_gmonparam.froms[from_index] == 0)
252 continue;
253
254 frompc = _gmonparam.lowpc;
255 frompc += (from_index * _gmonparam.hashfraction
256 * sizeof (*_gmonparam.froms));
257 for (to_index = _gmonparam.froms[from_index];
258 to_index != 0;
259 to_index = _gmonparam.tos[to_index].link)
260 {
261 struct arc
262 {
263 char *frompc;
264 char *selfpc;
265 int32_t count;
266 }
267 arc;
268
269 arc.frompc = (char *) frompc;
270 arc.selfpc = (char *) _gmonparam.tos[to_index].selfpc;
271 arc.count = _gmonparam.tos[to_index].count;
272 memcpy (raw_arc + nfilled, &arc, sizeof (raw_arc [0]));
273
274 if (++nfilled == NARCS_PER_WRITEV)
275 {
276 writev_not_cancel_no_status (fd, iov, 2 * nfilled);
277 nfilled = 0;
278 }
279 }
280 }
281 if (nfilled > 0)
282 writev_not_cancel_no_status (fd, iov, 2 * nfilled);
283}
284
285
286static void
287internal_function
288write_bb_counts (int fd)
289{
290 struct __bb *grp;
291 u_char tag = GMON_TAG_BB_COUNT;
292 size_t ncounts;
293 size_t i;
294
295 struct iovec bbhead[2] =
296 {
297 { &tag, sizeof (tag) },
298 { &ncounts, sizeof (ncounts) }
299 };
300 struct iovec bbbody[8];
301 size_t nfilled;
302
303 for (i = 0; i < (sizeof (bbbody) / sizeof (bbbody[0])); i += 2)
304 {
305 bbbody[i].iov_len = sizeof (grp->addresses[0]);
306 bbbody[i + 1].iov_len = sizeof (grp->counts[0]);
307 }
308
309 /* Write each group of basic-block info (all basic-blocks in a
310 compilation unit form a single group). */
311
312 for (grp = __bb_head; grp; grp = grp->next)
313 {
314 ncounts = grp->ncounts;
315 writev_not_cancel_no_status (fd, bbhead, 2);
316 for (nfilled = i = 0; i < ncounts; ++i)
317 {
318 if (nfilled > (sizeof (bbbody) / sizeof (bbbody[0])) - 2)
319 {
320 writev_not_cancel_no_status (fd, bbbody, nfilled);
321 nfilled = 0;
322 }
323
324 bbbody[nfilled++].iov_base = (char *) &grp->addresses[i];
325 bbbody[nfilled++].iov_base = &grp->counts[i];
326 }
327 if (nfilled > 0)
328 writev_not_cancel_no_status (fd, bbbody, nfilled);
329 }
330}
331
332
333static void
334write_gmon (void)
335{
336 int fd = -1;
337 char *env;
338
339 env = getenv ("GMON_OUT_PREFIX");
340 if (env != NULL && !__libc_enable_secure)
341 {
342 size_t len = strlen (env);
343 char buf[len + 20];
344 __snprintf (buf, sizeof (buf), "%s.%u", env, __getpid ());
345 fd = open_not_cancel (buf, O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW, 0666);
346 }
347
348 if (fd == -1)
349 {
350 fd = open_not_cancel ("gmon.out", O_CREAT|O_TRUNC|O_WRONLY|O_NOFOLLOW,
351 0666);
352 if (fd < 0)
353 {
354 char buf[300];
355 int errnum = errno;
356 __fxprintf (NULL, "_mcleanup: gmon.out: %s\n",
357 __strerror_r (errnum, buf, sizeof buf));
358 return;
359 }
360 }
361
362 /* write gmon.out header: */
363 struct real_gmon_hdr
364 {
365 char cookie[4];
366 int32_t version;
367 char spare[3 * 4];
368 } ghdr;
369 if (sizeof (ghdr) != sizeof (struct gmon_hdr)
370 || (offsetof (struct real_gmon_hdr, cookie)
371 != offsetof (struct gmon_hdr, cookie))
372 || (offsetof (struct real_gmon_hdr, version)
373 != offsetof (struct gmon_hdr, version)))
374 abort ();
375 memcpy (&ghdr.cookie[0], GMON_MAGIC, sizeof (ghdr.cookie));
376 ghdr.version = GMON_VERSION;
377 memset (ghdr.spare, '\0', sizeof (ghdr.spare));
378 write_not_cancel (fd, &ghdr, sizeof (struct gmon_hdr));
379
380 /* write PC histogram: */
381 write_hist (fd);
382
383 /* write call-graph: */
384 write_call_graph (fd);
385
386 /* write basic-block execution counts: */
387 write_bb_counts (fd);
388
389 close_not_cancel_no_status (fd);
390}
391
392
393void
394__write_profiling (void)
395{
396 int save = _gmonparam.state;
397 _gmonparam.state = GMON_PROF_OFF;
398 if (save == GMON_PROF_ON)
399 write_gmon ();
400 _gmonparam.state = save;
401}
402#ifndef SHARED
403/* This symbol isn't used anywhere in the DSO and it is not exported.
404 This would normally mean it should be removed to get the same API
405 in static libraries. But since profiling is special in static libs
406 anyway we keep it. But not when building the DSO since some
407 quality assurance tests will otherwise trigger. */
408weak_alias (__write_profiling, write_profiling)
409#endif
410
411
412void
413_mcleanup (void)
414{
415 __moncontrol (0);
416
417 if (_gmonparam.state != GMON_PROF_ERROR)
418 write_gmon ();
419
420 /* free the memory. */
421 free (_gmonparam.tos);
422}
423