1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58#include <sys/param.h>
59#include <sys/systm.h>
60#include <sys/kernel.h>
61#include <sys/proc_internal.h>
62#include <sys/user.h>
63#include <machine/spl.h>
64#include <machine/machine_routines.h>
65
66#include <sys/mount_internal.h>
67#include <sys/sysproto.h>
68
69#include <mach/mach_types.h>
70#include <kern/kern_types.h>
71#include <kern/cpu_number.h>
72#include <kern/kalloc.h>
73
74extern boolean_t ml_set_interrupts_enabled(boolean_t enable);
75
76#ifdef GPROF
77#include <sys/malloc.h>
78#include <sys/gmon.h>
79#include <kern/mach_header.h>
80#include <machine/profile.h>
81
82lck_spin_t * mcount_lock;
83lck_grp_t * mcount_lock_grp;
84lck_attr_t * mcount_lock_attr;
85
86
87
88
89struct gmonparam _gmonparam = { GMON_PROF_OFF };
90
91
92
93
94
95void
96kmstartup(void)
97{
98 char *cp;
99 u_long fromssize, tossize;
100 struct segment_command *sgp;
101 struct gmonparam *p = &_gmonparam;
102
103 sgp = getsegbyname("__TEXT");
104 p->lowpc = (u_long)sgp->vmaddr;
105 p->highpc = (u_long)(sgp->vmaddr + sgp->vmsize);
106
107
108
109
110
111 p->lowpc = ROUNDDOWN(p->lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
112 p->highpc = ROUNDUP(p->highpc, HISTFRACTION * sizeof(HISTCOUNTER));
113 p->textsize = p->highpc - p->lowpc;
114 printf("Profiling kernel, textsize=%d [0x%08x..0x%08x]\n",
115 p->textsize, p->lowpc, p->highpc);
116 p->kcountsize = p->textsize / HISTFRACTION;
117 p->hashfraction = HASHFRACTION;
118 p->fromssize = p->textsize / HASHFRACTION;
119 p->tolimit = p->textsize * ARCDENSITY / 100;
120 if (p->tolimit < MINARCS)
121 p->tolimit = MINARCS;
122 else if (p->tolimit > MAXARCS)
123 p->tolimit = MAXARCS;
124 p->tossize = p->tolimit * sizeof(struct tostruct);
125
126 cp = (char *)kalloc(p->kcountsize + p->fromssize + p->tossize);
127 if (cp == 0) {
128 printf("No memory for profiling.\n");
129 return;
130 }
131 bzero(cp, p->kcountsize + p->tossize + p->fromssize);
132 p->tos = (struct tostruct *)cp;
133 cp += p->tossize;
134 p->kcount = (u_short *)cp;
135 cp += p->kcountsize;
136 p->froms = (u_short *)cp;
137
138 mcount_lock_grp = lck_grp_alloc_init("MCOUNT", LCK_GRP_ATTR_NULL);
139 mcount_lock_attr = lck_attr_alloc_init();
140
141 mcount_lock = lck_spin_alloc_init(mcount_lock_grp, mcount_lock_attr);
142
143}
144
145
146
147
148int
149sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
150 user_addr_t newp, size_t newlen)
151{
152 struct gmonparam *gp = &_gmonparam;
153 int error;
154
155
156 if (namelen != 1)
157 return (ENOTDIR);
158
159 switch (name[0]) {
160 case GPROF_STATE:
161 error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
162 if (error)
163 return (error);
164 if (gp->state == GMON_PROF_OFF)
165 stopprofclock(kernproc);
166 else
167 startprofclock(kernproc);
168 return (0);
169 case GPROF_COUNT:
170 return (sysctl_struct(oldp, oldlenp, newp, newlen,
171 gp->kcount, gp->kcountsize));
172 case GPROF_FROMS:
173 return (sysctl_struct(oldp, oldlenp, newp, newlen,
174 gp->froms, gp->fromssize));
175 case GPROF_TOS:
176 return (sysctl_struct(oldp, oldlenp, newp, newlen,
177 gp->tos, gp->tossize));
178 case GPROF_GMONPARAM:
179 return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
180 default:
181 return (ENOTSUP);
182 }
183
184}
185
186
187
188
189
190void
191mcount(
192 register u_long frompc,
193 register u_long selfpc
194)
195{
196 unsigned short *frompcindex;
197 register struct tostruct *top, *prevtop;
198 struct gmonparam *p = &_gmonparam;
199 register long toindex;
200
201
202
203
204
205 if (p->state != GMON_PROF_ON)
206 return;
207
208 lck_spin_lock(mcount_lock);
209
210
211
212
213
214
215 frompc -= p->lowpc;
216 if (frompc > p->textsize)
217 goto done;
218
219 frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
220 toindex = *frompcindex;
221 if (toindex == 0) {
222
223
224
225 toindex = ++p->tos[0].link;
226 if (toindex >= p->tolimit) {
227
228 goto overflow;
229 }
230 *frompcindex = toindex;
231 top = &p->tos[toindex];
232 top->selfpc = selfpc;
233 top->count = 1;
234 top->link = 0;
235 goto done;
236 }
237 top = &p->tos[toindex];
238 if (top->selfpc == selfpc) {
239
240
241
242 top->count++;
243 goto done;
244 }
245
246
247
248
249
250
251 for (; ; ) {
252 if (top->link == 0) {
253
254
255
256
257
258
259 toindex = ++p->tos[0].link;
260 if (toindex >= p->tolimit) {
261 goto overflow;
262 }
263 top = &p->tos[toindex];
264 top->selfpc = selfpc;
265 top->count = 1;
266 top->link = *frompcindex;
267 *frompcindex = toindex;
268 goto done;
269 }
270
271
272
273 prevtop = top;
274 top = &p->tos[top->link];
275 if (top->selfpc == selfpc) {
276
277
278
279
280
281 top->count++;
282 toindex = prevtop->link;
283 prevtop->link = top->link;
284 top->link = *frompcindex;
285 *frompcindex = toindex;
286 goto done;
287 }
288
289 }
290done:
291 lck_spin_unlock(mcount_lock);
292 return;
293
294overflow:
295 p->state = GMON_PROF_ERROR;
296 lck_spin_unlock(mcount_lock);
297 printf("mcount: tos overflow\n");
298 return;
299}
300
301#endif
302
303#define PROFILE_LOCK(x)
304#define PROFILE_UNLOCK(x)
305
306int
307profil(struct proc *p, register struct profil_args *uap, __unused register_t *retval)
308{
309 struct uprof *upp = &p->p_stats->p_prof;
310 int s;
311
312 if (uap->pcscale > (1 << 16))
313 return (EINVAL);
314 if (uap->pcscale == 0) {
315 stopprofclock(p);
316 return (0);
317 }
318
319
320 s = ml_set_interrupts_enabled(FALSE);
321
322 if (proc_is64bit(p)) {
323 struct user_uprof *user_upp = &p->p_stats->user_p_prof;
324 struct user_uprof *upc, *nupc;
325
326 PROFILE_LOCK(&user_upp->pr_lock);
327 user_upp->pr_base = uap->bufbase;
328 user_upp->pr_size = uap->bufsize;
329 user_upp->pr_off = uap->pcoffset;
330 user_upp->pr_scale = uap->pcscale;
331 upp->pr_base = NULL;
332 upp->pr_size = 0;
333 upp->pr_scale = 0;
334
335
336 for (upc = user_upp->pr_next; upc; upc = nupc) {
337 nupc = upc->pr_next;
338 kfree(upc, sizeof (*upc));
339 }
340 user_upp->pr_next = 0;
341 PROFILE_UNLOCK(&user_upp->pr_lock);
342 }
343 else {
344 struct uprof *upc, *nupc;
345
346 PROFILE_LOCK(&upp->pr_lock);
347 upp->pr_base = CAST_DOWN(caddr_t, uap->bufbase);
348 upp->pr_size = uap->bufsize;
349 upp->pr_off = uap->pcoffset;
350 upp->pr_scale = uap->pcscale;
351
352
353 for (upc = upp->pr_next; upc; upc = nupc) {
354 nupc = upc->pr_next;
355 kfree(upc, sizeof (struct uprof));
356 }
357 upp->pr_next = 0;
358 PROFILE_UNLOCK(&upp->pr_lock);
359 }
360
361 startprofclock(p);
362 ml_set_interrupts_enabled(s);
363 return(0);
364}
365
366int
367add_profil(struct proc *p, register struct add_profil_args *uap, __unused register_t *retval)
368{
369 struct uprof *upp = &p->p_stats->p_prof, *upc;
370 struct user_uprof *user_upp = NULL, *user_upc;
371 int s;
372 boolean_t is64bit = proc_is64bit(p);
373
374 if (is64bit) {
375 user_upp = &p->p_stats->user_p_prof;
376 if (user_upp->pr_scale == 0)
377 return (0);
378 }
379 else {
380 if (upp->pr_scale == 0)
381 return (0);
382 }
383
384 s = ml_set_interrupts_enabled(FALSE);
385
386 if (is64bit) {
387 user_upc = (struct user_uprof *) kalloc(sizeof (struct user_uprof));
388 user_upc->pr_base = uap->bufbase;
389 user_upc->pr_size = uap->bufsize;
390 user_upc->pr_off = uap->pcoffset;
391 user_upc->pr_scale = uap->pcscale;
392 PROFILE_LOCK(&user_upp->pr_lock);
393 user_upc->pr_next = user_upp->pr_next;
394 user_upp->pr_next = user_upc;
395 PROFILE_UNLOCK(&user_upp->pr_lock);
396 }
397 else {
398 upc = (struct uprof *) kalloc(sizeof (struct uprof));
399 upc->pr_base = CAST_DOWN(caddr_t, uap->bufbase);
400 upc->pr_size = uap->bufsize;
401 upc->pr_off = uap->pcoffset;
402 upc->pr_scale = uap->pcscale;
403 PROFILE_LOCK(&upp->pr_lock);
404 upc->pr_next = upp->pr_next;
405 upp->pr_next = upc;
406 PROFILE_UNLOCK(&upp->pr_lock);
407 }
408
409 ml_set_interrupts_enabled(s);
410 return(0);
411}
412
413
414
415
416
417
418#define PC_TO_INDEX(pc, prof) \
419 ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
420 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436void
437addupc_task(p, pc, ticks)
438 register struct proc *p;
439 user_addr_t pc;
440 u_int ticks;
441{
442 register u_int off;
443 u_short count;
444
445
446 if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
447 return;
448
449 if (proc_is64bit(p)) {
450 struct user_uprof *prof;
451 user_addr_t cell;
452
453 for (prof = &p->p_stats->user_p_prof; prof; prof = prof->pr_next) {
454 off = PC_TO_INDEX(pc, prof);
455 cell = (prof->pr_base + off);
456 if (cell >= prof->pr_base &&
457 cell < (prof->pr_size + prof->pr_base)) {
458 if (copyin(cell, (caddr_t) &count, sizeof(count)) == 0) {
459 count += ticks;
460 if(copyout((caddr_t) &count, cell, sizeof(count)) == 0)
461 return;
462 }
463 p->p_stats->user_p_prof.pr_scale = 0;
464 stopprofclock(p);
465 break;
466 }
467 }
468 }
469 else {
470 struct uprof *prof;
471 short *cell;
472
473 for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) {
474 off = PC_TO_INDEX(CAST_DOWN(uint, pc),prof);
475 cell = (short *)(prof->pr_base + off);
476 if (cell >= (short *)prof->pr_base &&
477 cell < (short*)(prof->pr_size + (int) prof->pr_base)) {
478 if (copyin(CAST_USER_ADDR_T(cell), (caddr_t) &count, sizeof(count)) == 0) {
479 count += ticks;
480 if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0)
481 return;
482 }
483 p->p_stats->p_prof.pr_scale = 0;
484 stopprofclock(p);
485 break;
486 }
487 }
488 }
489}
490