]> sourceware.org Git - glibc.git/blame - elf/dl-profile.c
Update.
[glibc.git] / elf / dl-profile.c
CommitLineData
3996f34b
UD
1/* Profiling of shared libraries.
2 Copyright (C) 1997 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21#include <errno.h>
22#include <fcntl.h>
23#include <inttypes.h>
24#include <link.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <sys/gmon.h>
30#include <sys/gmon_out.h>
31#include <sys/mman.h>
32#include <sys/stat.h>
33
34/* The LD_PROFILE feature has to be implemented different to the
35 normal profiling using the gmon/ functions. The problem is that an
36 arbitrary amount of processes simulataneously can be run using
37 profiling and all write the results in the same file. To provide
38 this mechanism one could implement a complicated mechanism to merge
39 the content of two profiling runs or one could extend the file
40 format to allow more than one data set. For the second solution we
41 would have the problem that the file can grow in size beyond any
42 limit and both solutions have the problem that the concurrency of
43 writing the results is a big problem.
44
45 Another much simpler method is to use mmap to map the same file in
46 all using programs and modify the data in the mmap'ed area and so
47 also automatically on the disk. Using the MAP_SHARED option of
48 mmap(2) this can be done without big problems in more than one
49 file.
50
51 This approach is very different from the normal profiling. We have
52 to use the profiling data in exactly the way they are expected to
53 be written to disk. */
54
55extern char *_strerror_internal __P ((int, char *buf, size_t));
56
57extern int __profile_frequency __P ((void));
58
59
60static struct gmonparam param;
61
62/* We define a special type to address the elements of the arc table.
63 This is basically the `gmon_cg_arc_record' format but it includes
64 the room for the tag and it uses real types. */
65struct here_cg_arc_record
66 {
67 char tag;
68 uintptr_t from_pc __attribute__ ((packed));
69 uintptr_t self_pc __attribute__ ((packed));
70 uint32_t count __attribute__ ((packed));
71 };
72
73static struct here_cg_arc_record *data;
74
75
76void
77_dl_start_profile (struct link_map *map, const char *output_dir)
78{
79 char *filename;
80 int fd;
81 struct stat st;
82 const ElfW(Phdr) *ph;
83 ElfW(Addr) mapstart = ~((ElfW(Addr)) 0);
84 ElfW(Addr) mapend = 0;
85 off_t expected_size;
86 struct gmon_hdr gmon_hdr;
87 struct gmon_hist_hdr hist_hdr;
88 struct gmon_hdr *addr;
89 char *hist;
90
91 /* Compute the size of the sections which contain program code. */
92 for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
93 if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X))
94 {
95 ElfW(Addr) start = (ph->p_vaddr & ~(_dl_pagesize - 1));
96 ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + _dl_pagesize - 1)
97 & ~(_dl_pagesize - 1));
98
99 if (start < mapstart)
100 mapstart = start;
101 if (end > mapend)
102 mapend = end;
103 }
104
105 /* Now we can compute the size of the profiling data. This is done
106 with the same formulars as in `monstartup' (see gmon.c). */
107 param.state = GMON_PROF_OFF;
108 param.lowpc = mapstart + map->l_addr;
109 param.highpc = mapend + map->l_addr;
110 param.textsize = mapend - mapstart;
111 param.kcountsize = param.textsize / HISTFRACTION;
112 param.hashfraction = HASHFRACTION;
113 param.log_hashfraction = -1;
114 if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
115 /* If HASHFRACTION is a power of two, mcount can use shifting
116 instead of integer division. Precompute shift amount. */
117 param.log_hashfraction = ffs (param.hashfraction
118 * sizeof (*param.froms)) - 1;
119 param.fromssize = param.textsize / HASHFRACTION;
120 param.tolimit = param.textsize * ARCDENSITY / 100;
121 if (param.tolimit < MINARCS)
122 param.tolimit = MINARCS;
123 if (param.tolimit > MAXARCS)
124 param.tolimit = MAXARCS;
125 param.tossize = param.tolimit * sizeof (struct tostruct);
126
127 expected_size = (sizeof (struct gmon_hdr)
128 + 1 + sizeof (struct gmon_hist_hdr)
129 + ((1 + sizeof (struct gmon_cg_arc_record))
130 * (param.fromssize / sizeof (*param.froms))));
131
132 /* Create the gmon_hdr we expect or write. */
133 memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr));
134 memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie));
135 *(int32_t *) gmon_hdr.version = GMON_VERSION;
136
137 /* Create the hist_hdr we expect or write. */
138 *(char **) hist_hdr.low_pc = (char *) mapstart;
139 *(char **) hist_hdr.high_pc = (char *) mapend;
140 *(int32_t *) hist_hdr.hist_size = param.kcountsize / sizeof (HISTCOUNTER);
141 *(int32_t *) hist_hdr.prof_rate = __profile_frequency ();
142 strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
143 hist_hdr.dimen_abbrev = 's';
144
145 /* First determine the output name. We write in the directory
146 OUTPUT_DIR and the name is composed from the shared objects
147 soname (or the file name) and the ending ".profile". */
148 filename = (char *) alloca (strlen (output_dir) + 1 + strlen (_dl_profile)
149 + sizeof ".profile");
150 __stpcpy (__stpcpy (__stpcpy (__stpcpy (filename, output_dir), "/"),
151 _dl_profile),
152 ".profile");
153
154 fd = __open (filename, O_RDWR | O_CREAT, 0666);
155 if (fd == -1)
156 /* We cannot write the profiling data so don't do anthing. */
157 return;
158
159 if (fstat (fd, &st) < 0 || !S_ISREG (st.st_mode))
160 {
161 /* Not stat'able or not a regular file => don't use it. */
162 close (fd);
163 return;
164 }
165
166 /* Test the size. If it does not match what we expect from the size
167 values in the map MAP we don't use it and warn the user. */
168 if (st.st_size == 0)
169 {
170 /* We have to create the file. */
171 char buf[_dl_pagesize];
172
173 memset (buf, '\0', _dl_pagesize);
174
175 if (__lseek (fd, expected_size & ~(_dl_pagesize - 1), SEEK_SET) == -1)
176 {
177 char buf[400];
178 int errnum;
179 cannot_create:
180 errnum = errno;
181 __close (fd);
182 fprintf (stderr, "%s: cannot create file: %s\n", filename,
183 _strerror_internal (errnum, buf, sizeof buf));
184 return;
185 }
186
187 if (TEMP_FAILURE_RETRY (__write (fd, buf, (expected_size
188 & (_dl_pagesize - 1)))) < 0)
189 goto cannot_create;
190 }
191 else if (st.st_size != expected_size)
192 {
193 __close (fd);
194 wrong_format:
195 fprintf (stderr, "%s: file is no correct profile data file for `%s'\n",
196 filename, _dl_profile);
197 return;
198 }
199
200 addr = (void *) __mmap (NULL, expected_size, PROT_READ|PROT_WRITE,
201 MAP_SHARED|MAP_FILE, fd, 0);
202 if (addr == (void *) -1)
203 {
204 char buf[400];
205 int errnum = errno;
206 __close (fd);
207 fprintf (stderr, "%s: cannot map file: %s\n", filename,
208 _strerror_internal (errnum, buf, sizeof buf));
209 return;
210 }
211
212 /* We don't need the file desriptor anymore. */
213 __close (fd);
214
215 /* Pointer to data after the header. */
216 hist = (char *) (addr + 1);
217
218 /* Compute pointer to array of the arc information. */
219 data = (struct here_cg_arc_record *) (hist + 1
220 + sizeof (struct gmon_hist_hdr));
221
222 if (st.st_size == 0)
223 {
224 /* Create the signature. */
225 size_t cnt;
226
227 memcpy (addr, &gmon_hdr, sizeof (struct gmon_hdr));
228
229 *hist = GMON_TAG_TIME_HIST;
230 memcpy (hist + 1, &hist_hdr, sizeof (struct gmon_hist_hdr));
231
232 for (cnt = 0; cnt < param.fromssize / sizeof (*param.froms); ++cnt)
233 data[cnt].tag = GMON_TAG_CG_ARC;
234 }
235 else
236 {
237 /* Test the signature in the file. */
238 if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
239 || *hist != GMON_TAG_TIME_HIST
240 || memcmp (hist + 1, &hist_hdr, sizeof (struct gmon_hist_hdr)) != 0)
241 goto wrong_format;
242 }
243
244 /* Turn on profiling. */
245 param.state = GMON_PROF_ON;
246}
247
248
249void
250_dl_mcount (ElfW(Addr) frompc, ElfW(Addr) selfpc)
251{
252 if (param.state != GMON_PROF_ON)
253 return;
254 param.state = GMON_PROF_BUSY;
255
256 /* Compute relative addresses. The shared object can be loaded at
257 any address. The value of frompc could be anything. We cannot
258 restrict it in any way, just set to a fixed value (0) in case it
259 is outside the allowed range. These calls show up as calls from
260 <external> in the gprof output. */
261 frompc -= param.lowpc;
262 if (frompc >= param.textsize)
263 frompc = 0;
264 selfpc -= param.lowpc;
265
266 param.state = GMON_PROF_ON;
267}
This page took 0.049664 seconds and 5 git commands to generate.