2 * Copyright (c) 1999, 2003, 2004, 2007, 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #error This module cannot be compiled with profiling
28 * Copyright (c) 1983, 1992, 1993
29 * The Regents of the University of California. All rights reserved.
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Berkeley and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * 2-Mar-90 Gregg Kellogg (gk) at NeXT
62 * Changed include of kern/mach.h to kern/mach_interface.h
64 * 1-May-90 Matthew Self (mself) at NeXT
65 * Added prototypes, and added casts to remove all warnings.
66 * Made all private data static.
67 * vm_deallocate old data defore vm_allocate'ing new data.
68 * Added new functions monoutput and monreset.
70 * 18-Dec-92 Development Environment Group at NeXT
71 * Added multiple profile areas, the ability to profile shlibs and the
72 * ability to profile rld loaded code. Moved the machine dependent mcount
73 * routine out of this source file.
75 * 13-Dec-92 Development Environment Group at NeXT
76 * Added support for dynamic shared libraries. Also removed the code that
77 * had been ifdef'ed out for profiling fixed shared libraries and
81 #if defined(LIBC_SCCS) && !defined(lint)
82 static char sccsid
[] = "@(#)gmon.c 5.2 (Berkeley) 6/21/85";
86 * see profil(2) where this (SCALE_1_TO_1) is describe (incorrectly).
88 * The correct description: scale is a fixed point value with
89 * the binary point in the middle of the 32 bit value. (Bit 16 is
90 * 1, bit 15 is .5, etc.)
92 * Setting the scale to "1" (i.e. 0x10000), results in the kernel
93 * choosing the profile bucket address 1 to 1 with the pc sampled.
94 * Since buckets are shorts, if the profiling base were 0, then a pc
95 * of 0 increments bucket 0, a pc of 2 increments bucket 1, and a pc
96 * of 4 increments bucket 2.) (Actually, this seems a little bogus,
97 * 1 to 1 should map pc's to buckets -- that's probably what was
98 * intended from the man page, but historically....
100 #define SCALE_1_TO_1 0x10000L
102 #define MSG "No space for monitor buffer(s)\n"
107 #include <sys/types.h>
108 #include <sys/gmon.h>
109 #include <sys/param.h>
110 #include <sys/sysctl.h>
111 #include <mach/mach.h>
112 #include <mach-o/loader.h>
113 #include <mach-o/dyld.h>
114 #include <mach-o/getsect.h>
117 * These are defined in here and these declarations need to be moved to libc.h
118 * where the other declarations for the monitor(3) routines are declared.
122 extern void monaddition(
125 extern void moncount(
128 extern void monreset(
130 extern void monoutput(
131 const char *filename
);
133 static char profiling
= -1; /* tas (test and set) location for NeXT */
134 static char init
= 0; /* set while moninit() is being serviced */
136 static unsigned long order
= 0; /* call order */
139 /* the address range and size this mon struct refers to */
142 unsigned long textsize
;
143 /* the data structures to support the arc's and their counts */
144 unsigned short *froms
; /* froms is unsigned shorts indexing into tos */
147 /* the pc-sample buffer, it's size and scale */
149 long ssiz
; /* includes the gmonhdr_t */
152 static mon_t
*mon
= NULL
;
153 static unsigned long nmon
= 0;
155 static void monsetup(
159 static long getprofhz(
167 const struct section
*section
;
169 const struct section_64
*section
;
171 char *lowpc
, *highpc
;
177 section
= getsectbyname("__TEXT", "__text");
178 lowpc
= (char *)section
->addr
,
179 highpc
= (char *)(section
->addr
+ section
->size
);
182 if((mon
= malloc(sizeof(mon_t
))) == NULL
){
183 write(2, MSG
, sizeof(MSG
) - 1);
187 memset(mon
, '\0', sizeof(mon_t
));
190 * To continue to make monstartup() and the functions that existed
191 * before adding multiple profiling areas working correctly the new
192 * calls to get the dyld loaded code profiled are made after
193 * the first mon_t is allocated so that they will not use the
194 * first mon_t and the old calls will always use the first mon_t
197 monsetup(mon
, lowpc
, highpc
);
199 profil(mon
->sbuf
+ sizeof(gmonhdr_t
),
200 mon
->ssiz
- sizeof(gmonhdr_t
),
201 (u_long
)mon
->lowpc
, mon
->scale
);
202 for(i
= 1; i
< nmon
; i
++)
203 add_profil(mon
[i
].sbuf
+ sizeof(gmonhdr_t
),
204 mon
[i
].ssiz
- sizeof(gmonhdr_t
),
205 (u_long
)mon
[i
].lowpc
, mon
[i
].scale
);
209 #if defined(__DYNAMIC__)
211 * Call _dyld_moninit() if the dyld is present. This is done after the
212 * above calls so the dynamic libraries will be added after the
215 _dyld_moninit(monaddition
);
226 if((mon
= malloc(sizeof(mon_t
))) == NULL
){
227 write(2, MSG
, sizeof(MSG
) - 1);
231 memset(mon
, '\0', sizeof(mon_t
));
233 monsetup(mon
, lowpc
, highpc
);
237 * monaddtion() is used for adding additional pc ranges to profile. This is
238 * used for profiling dyld loaded code.
249 monstartup(lowpc
, highpc
);
252 save_profiling
= profiling
;
254 if((mon
= realloc(mon
, (nmon
+ 1) * sizeof(mon_t
))) == NULL
){
255 write(2, MSG
, sizeof(MSG
) - 1);
259 memset(m
, '\0', sizeof(mon_t
));
261 monsetup(m
, lowpc
, highpc
);
262 profiling
= save_profiling
;
279 * round lowpc and highpc to multiples of the density we're using
280 * so the rest of the scaling (here and in gprof) stays in longs.
282 lowpc
= (char *)ROUNDDOWN((uintptr_t)lowpc
,
283 HISTFRACTION
* sizeof(HISTCOUNTER
));
285 highpc
= (char *)ROUNDUP((uintptr_t)highpc
,
286 HISTFRACTION
* sizeof(HISTCOUNTER
));
290 vm_deallocate(mach_task_self(),
291 (vm_address_t
)m
->froms
,
292 (vm_size_t
)(m
->textsize
/ HASHFRACTION
));
293 m
->textsize
= highpc
- lowpc
;
294 ret
= vm_allocate(mach_task_self(),
295 (vm_address_t
*)&m
->froms
,
296 (vm_size_t
)(m
->textsize
/ HASHFRACTION
),
298 if(ret
!= KERN_SUCCESS
){
299 write(2, MSG
, sizeof(MSG
) - 1);
305 vm_deallocate(mach_task_self(),
306 (vm_address_t
)m
->sbuf
,
308 monsize
= (m
->textsize
/ HISTFRACTION
) + sizeof(gmonhdr_t
);
309 ret
= vm_allocate(mach_task_self(),
310 (vm_address_t
*)&buffer
,
313 if(ret
!= KERN_SUCCESS
){
314 write(2, MSG
, sizeof(MSG
) - 1);
320 vm_deallocate(mach_task_self(),
321 (vm_address_t
)m
->tos
,
322 (vm_size_t
)(m
->tolimit
* sizeof(tostruct_t
)));
323 m
->tolimit
= m
->textsize
* ARCDENSITY
/ 100;
324 if(m
->tolimit
< MINARCS
){
325 m
->tolimit
= MINARCS
;
327 else if(m
->tolimit
> 65534){
330 ret
= vm_allocate(mach_task_self(),
331 (vm_address_t
*)&m
->tos
,
332 (vm_size_t
)(m
->tolimit
* sizeof(tostruct_t
)),
334 if(ret
!= KERN_SUCCESS
){
335 write(2, MSG
, sizeof(MSG
) - 1);
339 m
->tos
[0].link
= 0; /* a nop since tos was vm_allocated and is zero */
342 * If this is call to monsetup() was via monstartup() (m == mon) then
343 * it is using or reusing the first pc range and then the pc sample
344 * buffer can be setup by the system call profil() via monitor() via
345 * a moncontrol(1) call.
347 * Otherwise this is call to monsetup() was via monaddition() and a
348 * new system call is needed to add an additional pc sample buffer in
351 if(m
== mon
&& !init
){
352 monitor(lowpc
, highpc
, buffer
, monsize
, m
->tolimit
);
355 /* monitor() functionality */
358 p
= (gmonhdr_t
*)m
->sbuf
;
359 memset(p
, '\0', sizeof(gmonhdr_t
));
360 p
->lpc
= (uintptr_t)m
->lowpc
;
361 p
->hpc
= (uintptr_t)m
->highpc
;
363 p
->version
= GMONVERSION
;
364 p
->profrate
= getprofhz();
366 if((monsize
- sizeof(gmonhdr_t
)) < o
)
367 /* POSSIBLE BUG, if "(float) (monsize - sizeof(gmonhdr_t))/ o)" is zero
368 * then m->scale will be set to zero and the add_profil() call will disable
370 m
->scale
= ((float) (monsize
- sizeof(gmonhdr_t
))/ o
) *
373 m
->scale
= SCALE_1_TO_1
;
375 /* moncontrol(mode == 1) functionality */
377 add_profil(m
->sbuf
+ sizeof(gmonhdr_t
),
378 m
->ssiz
- sizeof(gmonhdr_t
),
379 (long)m
->lowpc
, m
->scale
);
394 for(i
= 0; i
< nmon
; i
++){
397 memset(m
->sbuf
, '\0', m
->ssiz
);
398 p
= (gmonhdr_t
*)m
->sbuf
;
399 p
->lpc
= (uintptr_t)m
->lowpc
;
400 p
->hpc
= (uintptr_t)m
->highpc
;
402 p
->version
= GMONVERSION
;
403 p
->profrate
= getprofhz();
406 memset(m
->froms
, '\0', m
->textsize
/ HASHFRACTION
);
408 memset(m
->tos
, '\0', m
->tolimit
* sizeof(tostruct_t
));
416 const char *filename
)
419 unsigned long i
, fromindex
, endfrom
, toindex
;
421 gmon_data_t sample_data
, arc_data
, dyld_data
;
423 rawarc_order_t rawarc_order
;
425 uint32_t image_count
;
426 intptr_t image_header
;
433 fd
= creat(filename
, 0666);
435 perror("mcount: gmon.out");
442 magic
= GMON_MAGIC_64
;
444 write(fd
, &magic
, sizeof(uint32_t));
446 #if defined(__DYNAMIC__)
448 image_count
= _dyld_image_count();
451 printf("image_count = %lu\n", image_count
- 1);
452 for(i
= 1; i
< image_count
; i
++){
453 image_header
= _dyld_get_image_header(i
);
454 printf("\timage_header %p\n", image_header
);
455 image_name
= _dyld_get_image_name(i
);
456 printf("\timage_name %s\n", image_name
);
460 * Calculate the dyld_data.size.
462 dyld_data
.type
= GMONTYPE_DYLD2_STATE
;
463 dyld_data
.size
= sizeof(uint32_t) +
464 sizeof(intptr_t) * (image_count
- 1);
465 for(i
= 1; i
< image_count
; i
++){
466 image_name
= _dyld_get_image_name(i
);
467 dyld_data
.size
+= strlen(image_name
) + 1;
471 * Write the dyld_data.
473 write(fd
, &dyld_data
, sizeof(gmon_data_t
));
475 write(fd
, &image_count
, sizeof(uint32_t));
477 for(i
= 1; i
< image_count
; i
++){
478 image_header
= (intptr_t)_dyld_get_image_header(i
);
479 write(fd
, &image_header
, sizeof(intptr_t));
480 image_name
= _dyld_get_image_name(i
);
481 write(fd
, image_name
, strlen(image_name
) + 1);
486 for(i
= 0; i
< nmon
; i
++){
489 fprintf(stderr
, "[monoutput] sbuf %p ssiz %d\n", m
->sbuf
, m
->ssiz
);
491 sample_data
.type
= GMONTYPE_SAMPLES
;
492 sample_data
.size
= m
->ssiz
;
493 write(fd
, &sample_data
, sizeof(gmon_data_t
));
495 * Write the gmonhdr_t and the pc-sample buffer. Note the
496 * gmonhdr_t is in sbuf at the beginning of sbuf already
499 write(fd
, m
->sbuf
, m
->ssiz
);
502 * Now write out the raw arcs.
504 endfrom
= m
->textsize
/ (HASHFRACTION
* sizeof(*m
->froms
));
505 arc_data
.type
= GMONTYPE_ARCS_ORDERS
;
508 fprintf(stderr
, "[monoutput] raw arcs, total %lu\n", endfrom
);
510 for(fromindex
= 0; fromindex
< endfrom
; fromindex
++){
511 if(m
->froms
[fromindex
] == 0){
515 fprintf(stderr
, "[monoutput] raw arc count at index[%lu] %u\n",
516 fromindex
, m
->froms
[fromindex
]);
519 (fromindex
* HASHFRACTION
* sizeof(*m
->froms
));
520 for(toindex
= m
->froms
[fromindex
];
522 toindex
= m
->tos
[toindex
].link
){
523 arc_data
.size
+= sizeof(rawarc_order_t
);
526 write(fd
, &arc_data
, sizeof(gmon_data_t
));
528 for(fromindex
= 0; fromindex
< endfrom
; fromindex
++){
529 if(m
->froms
[fromindex
] == 0){
533 (fromindex
* HASHFRACTION
* sizeof(*m
->froms
));
534 for(toindex
= m
->froms
[fromindex
];
536 toindex
= m
->tos
[toindex
].link
){
538 fprintf(stderr
, "[monoutput] frompc %p selfpc %p "
539 "count %ld order %lu\n", frompc
,
540 m
->tos
[toindex
].selfpc
,
541 m
->tos
[toindex
].count
, m
->tos
[toindex
].order
);
543 rawarc_order
.raw_frompc
= (uintptr_t)frompc
;
544 rawarc_order
.raw_selfpc
= (uintptr_t)
545 m
->tos
[toindex
].selfpc
;
546 rawarc_order
.raw_count
= m
->tos
[toindex
].count
;
547 rawarc_order
.raw_order
= m
->tos
[toindex
].order
;
548 write(fd
, &rawarc_order
, sizeof(rawarc_order_t
));
561 int nfunc
) /* nfunc is not used; available for compatability only. */
573 monoutput("gmon.out");
578 p
= (gmonhdr_t
*)buf
;
579 memset(p
, '\0', sizeof(gmonhdr_t
));
580 p
->lpc
= (uintptr_t)lowpc
;
581 p
->hpc
= (uintptr_t)highpc
;
583 p
->version
= GMONVERSION
;
584 p
->profrate
= getprofhz();
585 bufsiz
-= sizeof(gmonhdr_t
);
590 m
->scale
= ((float) bufsiz
/ o
) * SCALE_1_TO_1
;
592 m
->scale
= SCALE_1_TO_1
;
598 * profiling is what mcount checks to see if
599 * all the data structures are ready.
612 profil(m
->sbuf
+ sizeof(gmonhdr_t
),
613 m
->ssiz
- sizeof(gmonhdr_t
),
614 (u_long
)m
->lowpc
, m
->scale
);
615 for(i
= 1; i
< nmon
; i
++)
616 add_profil(mon
[i
].sbuf
+ sizeof(gmonhdr_t
),
617 mon
[i
].ssiz
- sizeof(gmonhdr_t
),
618 (u_long
)mon
[i
].lowpc
, mon
[i
].scale
);
624 profil((char *)0, 0, 0, 0);
634 unsigned short *frompcindex
;
635 tostruct_t
*top
, *prevtop
;
636 unsigned long i
, toindex
;
643 * Check that we are profiling and that we aren't recursively invoked.
644 * This should really be a test and set instruction in changing the
645 * value of profiling.
653 fprintf(stderr
, "[moncount] frompc %p selfpc %p\n", frompc
, selfpc
);
655 frompcindex
= (unsigned short *)frompc
;
658 * check that frompcindex is a reasonable pc value.
659 * for example: signal catchers get called from the stack,
660 * not from text space. too bad.
662 for(i
= 0; i
< nmon
; i
++){
664 if((uintptr_t)frompcindex
>= (uintptr_t)m
->lowpc
&&
665 (uintptr_t)frompcindex
< (uintptr_t)m
->highpc
)
672 frompcindex
= (unsigned short *)
673 ((uintptr_t)frompcindex
- (uintptr_t)m
->lowpc
);
676 &m
->froms
[((long)frompcindex
) / (HASHFRACTION
* sizeof(*m
->froms
))];
677 toindex
= *frompcindex
;
680 * first time traversing this arc
682 toindex
= ++m
->tos
[0].link
;
683 if(toindex
>= m
->tolimit
){
686 *frompcindex
= toindex
;
687 top
= &m
->tos
[toindex
];
688 top
->selfpc
= (uintptr_t)selfpc
;
691 top
->order
= ++order
;
694 top
= &m
->tos
[toindex
];
695 if(top
->selfpc
== (uintptr_t)selfpc
){
697 * arc at front of chain; usual case.
703 * have to go looking down chain for it.
704 * top points to what we are looking at,
705 * prevtop points to previous top.
706 * we know it is not at the head of the chain.
708 for(; /* goto done */; ){
711 * top is end of the chain and none of the chain
712 * had top->selfpc == selfpc.
713 * so we allocate a new tostruct_t
714 * and link it to the head of the chain.
716 toindex
= ++m
->tos
[0].link
;
717 if(toindex
>= m
->tolimit
){
720 top
= &m
->tos
[toindex
];
721 top
->selfpc
= (uintptr_t)selfpc
;
723 top
->link
= *frompcindex
;
724 top
->order
= ++order
;
725 *frompcindex
= toindex
;
729 * otherwise, check the next arc on the chain.
732 top
= &m
->tos
[top
->link
];
733 if(top
->selfpc
== (uintptr_t)selfpc
){
736 * increment its count
737 * move it to the head of the chain.
740 toindex
= prevtop
->link
;
741 prevtop
->link
= top
->link
;
742 top
->link
= *frompcindex
;
743 *frompcindex
= toindex
;
752 profiling
++; /* halt further profiling */
753 #define TOLIMIT "mcount: tos overflow\n"
754 write(2, TOLIMIT
, sizeof(TOLIMIT
) - 1);
758 * Get the profiling rate.
766 struct clockinfo clockrate
;
769 mib
[1] = KERN_CLOCKRATE
;
770 clockrate
.profhz
= 1;
771 size
= sizeof(clockrate
);
772 if(sysctl(mib
, 2, &clockrate
, &size
, NULL
, 0) < 0)
774 return(clockrate
.profhz
);