2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 #error This module cannot be compiled with profiling
30 * Copyright (c) 1983, 1992, 1993
31 * The Regents of the University of California. All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the University of
44 * California, Berkeley and its contributors.
45 * 4. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * 2-Mar-90 Gregg Kellogg (gk) at NeXT
64 * Changed include of kern/mach.h to kern/mach_interface.h
66 * 1-May-90 Matthew Self (mself) at NeXT
67 * Added prototypes, and added casts to remove all warnings.
68 * Made all private data static.
69 * vm_deallocate old data defore vm_allocate'ing new data.
70 * Added new functions monoutput and monreset.
72 * 18-Dec-92 Development Environment Group at NeXT
73 * Added multiple profile areas, the ability to profile shlibs and the
74 * ability to profile rld loaded code. Moved the machine dependent mcount
75 * routine out of this source file.
77 * 13-Dec-92 Development Environment Group at NeXT
78 * Added support for dynamic shared libraries. Also removed the code that
79 * had been ifdef'ed out for profiling fixed shared libraries and
83 #if defined(LIBC_SCCS) && !defined(lint)
84 static char sccsid
[] = "@(#)gmon.c 5.2 (Berkeley) 6/21/85";
88 * see profil(2) where this (SCALE_1_TO_1) is describe (incorrectly).
90 * The correct description: scale is a fixed point value with
91 * the binary point in the middle of the 32 bit value. (Bit 16 is
92 * 1, bit 15 is .5, etc.)
94 * Setting the scale to "1" (i.e. 0x10000), results in the kernel
95 * choosing the profile bucket address 1 to 1 with the pc sampled.
96 * Since buckets are shorts, if the profiling base were 0, then a pc
97 * of 0 increments bucket 0, a pc of 2 increments bucket 1, and a pc
98 * of 4 increments bucket 2.) (Actually, this seems a little bogus,
99 * 1 to 1 should map pc's to buckets -- that's probably what was
100 * intended from the man page, but historically....
102 #define SCALE_1_TO_1 0x10000L
104 #define MSG "No space for monitor buffer(s)\n"
108 extern const struct section
*getsectbyname(
110 const char *sectname
);
112 #include <sys/types.h>
113 #include <sys/gmon.h>
114 #include <sys/param.h>
115 #include <sys/sysctl.h>
116 #include <mach/mach.h>
117 #include <mach-o/loader.h>
118 #include <mach-o/dyld.h>
121 * These are defined in here and these declarations need to be moved to libc.h
122 * where the other declarations for the monitor(3) routines are declared.
126 extern void monaddition(
129 extern void moncount(
132 extern void monreset(
134 extern void monoutput(
135 const char *filename
);
136 extern int add_profil(char *, int, int, int);
138 static char profiling
= -1; /* tas (test and set) location for NeXT */
139 static char init
= 0; /* set while moninit() is being serviced */
141 static unsigned long order
= 0; /* call order */
144 /* the address range and size this mon struct refers to */
147 unsigned long textsize
;
148 /* the data structures to support the arc's and their counts */
149 unsigned short *froms
; /* froms is unsigned shorts indexing into tos */
150 struct tostruct
*tos
;
152 /* the pc-sample buffer, it's size and scale */
154 int ssiz
; /* includes the gmonhdr struct */
157 static struct mon_t
*mon
= NULL
;
158 static unsigned long nmon
= 0;
160 static void monsetup(
164 static int getprofhz(
171 const struct section
*section
;
172 char *lowpc
, *highpc
;
178 section
= getsectbyname ("__TEXT", "__text");
179 lowpc
= (char *)section
->addr
,
180 highpc
= (char *)(section
->addr
+ section
->size
);
183 if((mon
= malloc(sizeof(struct mon_t
))) == NULL
){
184 write(2, MSG
, sizeof(MSG
) - 1);
188 memset(mon
, '\0', sizeof(struct mon_t
));
191 * To continue to make monstartup() and the functions that existed
192 * before adding multiple profiling areas working correctly the new
193 * calls to get the dyld loaded code profiled are made after
194 * the first mon_t struct is allocated so that they will not use the
195 * first mon_t and the old calls will always use the first mon_t struct
198 monsetup(mon
, lowpc
, highpc
);
200 profil(mon
->sbuf
+ sizeof(struct gmonhdr
),
201 mon
->ssiz
- sizeof(struct gmonhdr
),
202 (int)mon
->lowpc
, mon
->scale
);
203 for(i
= 1; i
< nmon
; i
++)
204 add_profil(mon
[i
].sbuf
+ sizeof(struct gmonhdr
),
205 mon
[i
].ssiz
- sizeof(struct gmonhdr
),
206 (int)mon
[i
].lowpc
, mon
[i
].scale
);
210 #if defined(__DYNAMIC__)
212 * Call _dyld_moninit() if the dyld is present. This is done after the
213 * above calls so the dynamic libraries will be added after the
217 _dyld_moninit(monaddition
);
228 if((mon
= malloc(sizeof(struct mon_t
))) == NULL
){
229 write(2, MSG
, sizeof(MSG
) - 1);
233 memset(mon
, '\0', sizeof(struct mon_t
));
235 monsetup(mon
, lowpc
, highpc
);
239 * monaddtion() is used for adding additional pc ranges to profile. This is
240 * used for profiling dyld loaded code.
251 monstartup(lowpc
, highpc
);
254 save_profiling
= profiling
;
256 if((mon
= realloc(mon
, (nmon
+ 1) * sizeof(struct mon_t
))) == NULL
){
257 write(2, MSG
, sizeof(MSG
) - 1);
261 memset(m
, '\0', sizeof(struct mon_t
));
263 monsetup(m
, lowpc
, highpc
);
264 profiling
= save_profiling
;
281 * round lowpc and highpc to multiples of the density we're using
282 * so the rest of the scaling (here and in gprof) stays in ints.
284 lowpc
= (char *)ROUNDDOWN((unsigned)lowpc
,
285 HISTFRACTION
* sizeof(HISTCOUNTER
));
287 highpc
= (char *)ROUNDUP((unsigned)highpc
,
288 HISTFRACTION
* sizeof(HISTCOUNTER
));
292 vm_deallocate(mach_task_self(),
293 (vm_address_t
)m
->froms
,
294 (vm_size_t
)(m
->textsize
/ HASHFRACTION
));
295 m
->textsize
= highpc
- lowpc
;
296 ret
= vm_allocate(mach_task_self(),
297 (vm_address_t
*)&m
->froms
,
298 (vm_size_t
)(m
->textsize
/ HASHFRACTION
),
300 if(ret
!= KERN_SUCCESS
){
301 write(2, MSG
, sizeof(MSG
) - 1);
307 vm_deallocate(mach_task_self(),
308 (vm_address_t
)m
->sbuf
,
310 monsize
= (m
->textsize
/ HISTFRACTION
) + sizeof(struct gmonhdr
);
311 ret
= vm_allocate(mach_task_self(),
312 (vm_address_t
*)&buffer
,
315 if(ret
!= KERN_SUCCESS
){
316 write(2, MSG
, sizeof(MSG
) - 1);
322 vm_deallocate(mach_task_self(),
323 (vm_address_t
)m
->tos
,
324 (vm_size_t
)(m
->tolimit
* sizeof(struct tostruct
)));
325 m
->tolimit
= m
->textsize
* ARCDENSITY
/ 100;
326 if(m
->tolimit
< MINARCS
){
327 m
->tolimit
= MINARCS
;
329 else if(m
->tolimit
> 65534){
332 ret
= vm_allocate(mach_task_self(),
333 (vm_address_t
*)&m
->tos
,
334 (vm_size_t
)(m
->tolimit
* sizeof(struct tostruct
)),
336 if(ret
!= KERN_SUCCESS
){
337 write(2, MSG
, sizeof(MSG
) - 1);
341 m
->tos
[0].link
= 0; /* a nop since tos was vm_allocated and is zero */
344 * If this is call to monsetup() was via monstartup() (m == mon) then
345 * it is using or reusing the first pc range and then the pc sample
346 * buffer can be setup by the system call profil() via monitor() via
347 * a moncontrol(1) call.
349 * Otherwise this is call to monsetup() was via monaddition() and a
350 * new system call is needed to add an additional pc sample buffer in
353 if(m
== mon
&& !init
){
354 monitor(lowpc
, highpc
, buffer
, monsize
, m
->tolimit
);
357 /* monitor() functionality */
360 p
= (struct gmonhdr
*)m
->sbuf
;
361 memset(p
, '\0', sizeof(struct gmonhdr
));
362 p
->lpc
= (unsigned long)m
->lowpc
;
363 p
->hpc
= (unsigned long)m
->highpc
;
365 p
->version
= GMONVERSION
;
366 p
->profrate
= getprofhz();
368 if((monsize
- sizeof(struct gmonhdr
)) < o
)
369 m
->scale
= ((float) (monsize
- sizeof(struct gmonhdr
))/ o
) *
372 m
->scale
= SCALE_1_TO_1
;
374 /* moncontrol(mode == 1) functionality */
376 add_profil(m
->sbuf
+ sizeof(struct gmonhdr
),
377 m
->ssiz
- sizeof(struct gmonhdr
),
378 (int)m
->lowpc
, m
->scale
);
393 for(i
= 0; i
< nmon
; i
++){
396 memset(m
->sbuf
, '\0', m
->ssiz
);
397 p
= (struct gmonhdr
*)m
->sbuf
;
398 p
->lpc
= (unsigned long)m
->lowpc
;
399 p
->hpc
= (unsigned long)m
->highpc
;
403 memset(m
->froms
, '\0', m
->textsize
/ HASHFRACTION
);
405 memset(m
->tos
, '\0', m
->tolimit
* sizeof (struct tostruct
));
413 const char *filename
)
416 unsigned long magic
, i
, fromindex
, endfrom
, toindex
;
417 struct gmon_data sample_data
, arc_data
, dyld_data
;
419 struct rawarc_order rawarc_order
;
421 unsigned long image_count
, vmaddr_slide
;
428 fd
= creat(filename
, 0666);
430 perror("mcount: gmon.out");
435 write(fd
, &magic
, sizeof(unsigned long));
437 #if defined(__DYNAMIC__)
439 image_count
= _dyld_image_count();
442 printf("image_count = %lu\n", image_count
- 1);
443 for(i
= 1; i
< image_count
; i
++){
444 vmaddr_slide
= _dyld_get_image_vmaddr_slide(i
);
445 printf("\tvmaddr_slide 0x%x\n", (unsigned int)vmaddr_slide
);
446 image_name
= _dyld_get_image_name(i
);
447 printf("\timage_name %s\n", image_name
);
451 * Calculate the dyld_data.size.
453 dyld_data
.type
= GMONTYPE_DYLD_STATE
;
454 dyld_data
.size
= sizeof(unsigned long) +
455 sizeof(unsigned long) * (image_count
- 1);
456 for(i
= 1; i
< image_count
; i
++){
457 image_name
= _dyld_get_image_name(i
);
458 dyld_data
.size
+= strlen(image_name
) + 1;
462 * Write the dyld_data.
464 write(fd
, &dyld_data
, sizeof(struct gmon_data
));
466 write(fd
, &image_count
, sizeof(unsigned long));
468 for(i
= 1; i
< image_count
; i
++){
469 vmaddr_slide
= _dyld_get_image_vmaddr_slide(i
);
470 write(fd
, &vmaddr_slide
, sizeof(unsigned long));
471 image_name
= _dyld_get_image_name(i
);
472 write(fd
, image_name
, strlen(image_name
) + 1);
477 for(i
= 0; i
< nmon
; i
++){
480 fprintf(stderr
, "[monoutput] sbuf 0x%x ssiz %d\n",
483 sample_data
.type
= GMONTYPE_SAMPLES
;
484 sample_data
.size
= m
->ssiz
;
485 write(fd
, &sample_data
, sizeof(struct gmon_data
));
487 * Write the gmonhdr struct and the pc-sample buffer. Note the
488 * gmonhdr struct is in sbuf at the beginning of sbuf already
491 write(fd
, m
->sbuf
, m
->ssiz
);
494 * Now write out the raw arcs.
496 endfrom
= m
->textsize
/ (HASHFRACTION
* sizeof(*m
->froms
));
497 arc_data
.type
= GMONTYPE_ARCS_ORDERS
;
499 for(fromindex
= 0; fromindex
< endfrom
; fromindex
++){
500 if(m
->froms
[fromindex
] == 0){
504 (fromindex
* HASHFRACTION
* sizeof(*m
->froms
));
505 for(toindex
= m
->froms
[fromindex
];
507 toindex
= m
->tos
[toindex
].link
){
508 arc_data
.size
+= sizeof(struct rawarc_order
);
511 write(fd
, &arc_data
, sizeof(struct gmon_data
));
513 for(fromindex
= 0; fromindex
< endfrom
; fromindex
++){
514 if(m
->froms
[fromindex
] == 0){
518 (fromindex
* HASHFRACTION
* sizeof(*m
->froms
));
519 for(toindex
= m
->froms
[fromindex
];
521 toindex
= m
->tos
[toindex
].link
){
523 fprintf(stderr
, "[monoutput] frompc 0x%x selfpc 0x%x "
524 "count %ld order %lu\n", (unsigned int)frompc
,
525 (unsigned int)m
->tos
[toindex
].selfpc
,
526 m
->tos
[toindex
].count
, m
->tos
[toindex
].order
);
528 rawarc_order
.raw_frompc
= (unsigned long)frompc
;
529 rawarc_order
.raw_selfpc
= (unsigned long)
530 m
->tos
[toindex
].selfpc
;
531 rawarc_order
.raw_count
= m
->tos
[toindex
].count
;
532 rawarc_order
.raw_order
= m
->tos
[toindex
].order
;
533 write(fd
, &rawarc_order
, sizeof(struct rawarc_order
));
546 int nfunc
) /* nfunc is not used; available for compatability only. */
558 monoutput("gmon.out");
563 p
= (struct gmonhdr
*)buf
;
564 memset(p
, '\0', sizeof(struct gmonhdr
));
565 p
->lpc
= (unsigned long)lowpc
;
566 p
->hpc
= (unsigned long)highpc
;
568 p
->version
= GMONVERSION
;
569 p
->profrate
= getprofhz();
570 bufsiz
-= sizeof(struct gmonhdr
);
575 m
->scale
= ((float) bufsiz
/ o
) * SCALE_1_TO_1
;
577 m
->scale
= SCALE_1_TO_1
;
583 * profiling is what mcount checks to see if
584 * all the data structures are ready.
597 profil(m
->sbuf
+ sizeof(struct gmonhdr
),
598 m
->ssiz
- sizeof(struct gmonhdr
),
599 (int)m
->lowpc
, m
->scale
);
600 for(i
= 1; i
< nmon
; i
++)
601 add_profil(mon
[i
].sbuf
+ sizeof(struct gmonhdr
),
602 mon
[i
].ssiz
- sizeof(struct gmonhdr
),
603 (int)mon
[i
].lowpc
, mon
[i
].scale
);
609 profil((char *)0, 0, 0, 0);
619 unsigned short *frompcindex
;
620 struct tostruct
*top
, *prevtop
;
621 unsigned long i
, toindex
;
628 * Check that we are profiling and that we aren't recursively invoked.
629 * This should really be a test and set instruction in changing the
630 * value of profiling.
638 fprintf(stderr
, "[moncount] frompc 0x%x selfpc 0x%x\n",
639 (unsigned int)frompc
, (unsigned int)selfpc
);
641 frompcindex
= (unsigned short *)frompc
;
644 * check that frompcindex is a reasonable pc value.
645 * for example: signal catchers get called from the stack,
646 * not from text space. too bad.
648 for(i
= 0; i
< nmon
; i
++){
650 if((unsigned long)frompcindex
>= (unsigned long)m
->lowpc
&&
651 (unsigned long)frompcindex
< (unsigned long)m
->highpc
)
658 frompcindex
= (unsigned short *)
659 ((unsigned long)frompcindex
- (unsigned long)m
->lowpc
);
662 &m
->froms
[((long)frompcindex
) / (HASHFRACTION
* sizeof(*m
->froms
))];
663 toindex
= *frompcindex
;
666 * first time traversing this arc
668 toindex
= ++m
->tos
[0].link
;
669 if(toindex
>= m
->tolimit
){
672 *frompcindex
= toindex
;
673 top
= &m
->tos
[toindex
];
674 top
->selfpc
= (unsigned long)selfpc
;
677 top
->order
= ++order
;
680 top
= &m
->tos
[toindex
];
681 if(top
->selfpc
== (unsigned long)selfpc
){
683 * arc at front of chain; usual case.
689 * have to go looking down chain for it.
690 * top points to what we are looking at,
691 * prevtop points to previous top.
692 * we know it is not at the head of the chain.
694 for(; /* goto done */; ){
697 * top is end of the chain and none of the chain
698 * had top->selfpc == selfpc.
699 * so we allocate a new tostruct
700 * and link it to the head of the chain.
702 toindex
= ++m
->tos
[0].link
;
703 if(toindex
>= m
->tolimit
){
706 top
= &m
->tos
[toindex
];
707 top
->selfpc
= (unsigned long)selfpc
;
709 top
->link
= *frompcindex
;
710 top
->order
= ++order
;
711 *frompcindex
= toindex
;
715 * otherwise, check the next arc on the chain.
718 top
= &m
->tos
[top
->link
];
719 if(top
->selfpc
== (unsigned long)selfpc
){
722 * increment its count
723 * move it to the head of the chain.
726 toindex
= prevtop
->link
;
727 prevtop
->link
= top
->link
;
728 top
->link
= *frompcindex
;
729 *frompcindex
= toindex
;
738 profiling
++; /* halt further profiling */
739 #define TOLIMIT "mcount: tos overflow\n"
740 write(2, TOLIMIT
, sizeof(TOLIMIT
) - 1);
744 * Get the profiling rate.
752 struct clockinfo clockrate
;
755 mib
[1] = KERN_CLOCKRATE
;
756 clockrate
.profhz
= 1;
757 size
= sizeof(clockrate
);
758 if(sysctl(mib
, 2, &clockrate
, &size
, NULL
, 0) < 0)
760 return(clockrate
.profhz
);