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 
52 static int
callback(struct dl_phdr_info * info,size_t size,void * data)53 callback (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. */
71 struct __bb *__bb_head attribute_hidden;
72 
73 struct gmonparam _gmonparam attribute_hidden = { GMON_PROF_OFF };
74 
75 /*
76  * See profil(2) where this is described:
77  */
78 static int	s_scale;
79 #define		SCALE_1_TO_1	0x10000L
80 
81 #define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1)
82 
83 void moncontrol (int mode);
84 void __moncontrol (int mode);
85 libc_hidden_proto (__moncontrol)
86 static void write_hist (int fd, u_long load_address);
87 static void write_call_graph (int fd, u_long load_address);
88 static 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  */
95 void
__moncontrol(int mode)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 }
117 libc_hidden_def (__moncontrol)
weak_alias(__moncontrol,moncontrol)118 weak_alias (__moncontrol, moncontrol)
119 
120 
121 void
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 }
weak_alias(__monstartup,monstartup)192 weak_alias (__monstartup, monstartup)
193 
194 
195 static void
196 write_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 
245 static void
write_call_graph(int fd,u_long load_address)246 write_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 
307 static void
write_bb_counts(int fd)308 write_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 
353 static void
write_gmon(void)354 write_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 
419 void
__write_profiling(void)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.  */
weak_alias(__write_profiling,write_profiling)434 weak_alias (__write_profiling, write_profiling)
435 #endif
436 
437 
438 void
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