mirror of
https://github.com/darlinghq/cctools-port.git
synced 2024-11-23 04:09:48 +00:00
1438 lines
38 KiB
C
1438 lines
38 KiB
C
/*
|
|
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
|
|
*
|
|
* @APPLE_LICENSE_HEADER_START@
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original Code
|
|
* as defined in and that are subject to the Apple Public Source License
|
|
* Version 2.0 (the 'License'). You may not use this file except in
|
|
* compliance with the License. Please obtain a copy of the License at
|
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
|
* file.
|
|
*
|
|
* The Original Code and all software distributed under the License are
|
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
* Please see the License for the specific language governing rights and
|
|
* limitations under the License.
|
|
*
|
|
* @APPLE_LICENSE_HEADER_END@
|
|
*/
|
|
/* $OpenBSD: gprof.c,v 1.3 1996/10/02 02:59:49 tholo Exp $ */
|
|
/* $NetBSD: gprof.c,v 1.8 1995/04/19 07:15:59 cgd Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1983, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
#include <libc.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include "stuff/errors.h"
|
|
#include "gprof.h"
|
|
|
|
#ifdef vax
|
|
#include "vax.h"
|
|
#endif
|
|
|
|
/*
|
|
* Progname for error messages.
|
|
*/
|
|
char *progname = NULL;
|
|
|
|
/*
|
|
* Ticks per second.
|
|
*/
|
|
uint32_t hz = 0;
|
|
|
|
/*
|
|
* Filename of the a.out file.
|
|
*/
|
|
char *a_outname = NULL;
|
|
|
|
/*
|
|
* Filename of the gmon.out file.
|
|
*/
|
|
char *gmonname = NULL;
|
|
|
|
__private_extern__
|
|
nltype *nl = NULL; /* the whole namelist */
|
|
nltype *npe = NULL; /* the virtual end of the namelist */
|
|
uint32_t nname = 0; /* the number of function names */
|
|
|
|
/*
|
|
* The list of file names and the ranges their pc's cover used for building
|
|
* order files with the -S option.
|
|
*/
|
|
struct file *files = NULL;
|
|
uint32_t n_files = 0;
|
|
|
|
/*
|
|
* namelist entries for cycle headers.
|
|
* the number of discovered cycles.
|
|
*/
|
|
nltype *cyclenl = NULL; /* cycle header namelist */
|
|
int ncycle = 0; /* number of cycles discovered */
|
|
|
|
/*
|
|
* The information for the pc sample sets from the gmon.out file.
|
|
*/
|
|
struct sample_set *sample_sets = NULL;
|
|
uint32_t nsample_sets = 0;
|
|
|
|
#ifdef __OPENSTEP__
|
|
/*
|
|
* The rld loaded state from the gmon.out file.
|
|
*/
|
|
struct rld_loaded_state *grld_loaded_state = NULL;
|
|
uint32_t grld_nloaded_states = 0;
|
|
#endif
|
|
|
|
/*
|
|
* The dyld images from the gmon.out file.
|
|
*/
|
|
uint32_t image_count = 0;
|
|
struct dyld_image *dyld_images = NULL;
|
|
|
|
unsigned char *textspace = NULL;/* text space of a.out in core */
|
|
double totime = 0.0; /* total time for all routines */
|
|
double printtime = 0.0; /* total of time being printed */
|
|
double actime = 0.0; /* accumulated time thus far for
|
|
putprofline */
|
|
|
|
/*
|
|
* Option flags, from a to z.
|
|
*/
|
|
enum bool aflag = FALSE; /* suppress static functions */
|
|
enum bool bflag = FALSE; /* blurbs, too */
|
|
enum bool cflag = FALSE; /* discovered call graph, too */
|
|
enum bool dflag = FALSE; /* debugging options */
|
|
enum bool eflag = FALSE; /* specific functions excluded */
|
|
enum bool Eflag = FALSE; /* functions excluded with time */
|
|
enum bool fflag = FALSE; /* specific functions requested */
|
|
enum bool Fflag = FALSE; /* functions requested with time */
|
|
enum bool sflag = FALSE; /* sum multiple gmon.out files */
|
|
enum bool Sflag = FALSE; /* produce order file for scatter loading */
|
|
enum bool xflag = FALSE; /* don't produce gmon.order file */
|
|
enum bool zflag = FALSE; /* zero time/called functions, too */
|
|
|
|
/*
|
|
* The debug value for debugging gprof.
|
|
*/
|
|
uint32_t debug = 0;
|
|
|
|
/*
|
|
* Things which get -E excluded by default.
|
|
*/
|
|
static char *defaultEs[] = {
|
|
/* from the original gprof code */
|
|
"mcount",
|
|
/* "__mcleanup", obsolete */
|
|
/* missing from gprof for the 4.3bsd implementaion */
|
|
"_monitor",
|
|
"_monstartup",
|
|
"_moncontrol",
|
|
"_profil",
|
|
/* added to the NeXT implementation for 3.0 */
|
|
"_moninit",
|
|
"_monoutput",
|
|
"_monreset",
|
|
/* added to the NeXT implementation for 3.1 */
|
|
"_monaddition",
|
|
"_moncount",
|
|
"_add_profil",
|
|
0
|
|
};
|
|
|
|
/*
|
|
* Static function declarations.
|
|
*/
|
|
static void getpfile(
|
|
char *filename);
|
|
|
|
#ifdef __OPENSTEP__
|
|
static void read_rld_state(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes);
|
|
#endif
|
|
|
|
static void read_dyld_state(
|
|
uint32_t type,
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes,
|
|
uint32_t magic);
|
|
|
|
static uint32_t new_sample_set(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes,
|
|
enum bool old_style,
|
|
uint32_t magic);
|
|
|
|
static void readarcs(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes);
|
|
|
|
static void readarcs_64(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes);
|
|
|
|
static void readarcs_orders(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes);
|
|
|
|
static void readarcs_orders_64(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes);
|
|
|
|
static void tally(
|
|
uint64_t frompc,
|
|
uint64_t selfpc,
|
|
uint32_t count,
|
|
uint32_t order);
|
|
|
|
static void dumpsum(
|
|
char *sumfile);
|
|
|
|
static void asgnsamples(
|
|
struct sample_set *s);
|
|
|
|
static uint64_t min(
|
|
uint64_t a,
|
|
uint64_t b);
|
|
|
|
static uint64_t max(
|
|
uint64_t a,
|
|
uint64_t b);
|
|
|
|
static void alignentries(
|
|
void);
|
|
|
|
/* apple_version is created by the libstuff/Makefile */
|
|
extern char apple_version[];
|
|
char *version = apple_version;
|
|
|
|
int
|
|
main(
|
|
int argc,
|
|
char **argv,
|
|
char **envp)
|
|
{
|
|
uint32_t i;
|
|
char **sp;
|
|
nltype **timesortnlp;
|
|
|
|
progname = argv[0];
|
|
--argc;
|
|
argv++;
|
|
debug = 0;
|
|
bflag = TRUE;
|
|
while(*argv != 0 && **argv == '-'){
|
|
(*argv)++;
|
|
switch(**argv){
|
|
case 'a':
|
|
aflag = TRUE;
|
|
break;
|
|
case 'b':
|
|
bflag = FALSE;
|
|
break;
|
|
case 'c':
|
|
printf("%s: -c not supported\n", progname);
|
|
/* cflag = TRUE; */
|
|
break;
|
|
case 'd':
|
|
dflag = TRUE;
|
|
(*argv)++;
|
|
debug |= atoi(*argv);
|
|
debug |= ANYDEBUG;
|
|
#ifdef DEBUG
|
|
printf("[main] debug = %u\n", debug);
|
|
#else
|
|
printf("%s: -d ignored\n", progname);
|
|
#endif
|
|
break;
|
|
case 'E':
|
|
++argv;
|
|
addlist(Elist, *argv);
|
|
Eflag = TRUE;
|
|
addlist(elist, *argv);
|
|
eflag = TRUE;
|
|
break;
|
|
case 'e':
|
|
addlist(elist, *++argv);
|
|
eflag = TRUE;
|
|
break;
|
|
case 'F':
|
|
++argv;
|
|
addlist(Flist, *argv);
|
|
Fflag = TRUE;
|
|
addlist(flist, *argv);
|
|
fflag = TRUE;
|
|
break;
|
|
case 'f':
|
|
addlist(flist, *++argv);
|
|
fflag = TRUE;
|
|
break;
|
|
case 'S':
|
|
Sflag = TRUE;
|
|
break;
|
|
case 's':
|
|
sflag = TRUE;
|
|
break;
|
|
case 'x':
|
|
xflag = TRUE;
|
|
break;
|
|
case 'z':
|
|
zflag = TRUE;
|
|
break;
|
|
}
|
|
argv++;
|
|
}
|
|
|
|
if(*argv != 0){
|
|
a_outname = *argv;
|
|
argv++;
|
|
}
|
|
else{
|
|
a_outname = A_OUTNAME;
|
|
}
|
|
|
|
if(*argv != 0){
|
|
gmonname = *argv;
|
|
argv++;
|
|
}
|
|
else{
|
|
gmonname = GMONNAME;
|
|
}
|
|
|
|
/*
|
|
* Turn off default functions.
|
|
*/
|
|
for(sp = &defaultEs[0]; *sp; sp++){
|
|
Eflag = TRUE;
|
|
addlist(Elist, *sp);
|
|
eflag = TRUE;
|
|
addlist(elist, *sp);
|
|
}
|
|
|
|
/*
|
|
* Get information about a.out file.
|
|
*/
|
|
getnfile();
|
|
if(errors != 0)
|
|
exit(1);
|
|
|
|
/*
|
|
* Get information about mon.out file(s).
|
|
*/
|
|
hz = 0;
|
|
do{
|
|
getpfile(gmonname);
|
|
if(*argv != 0){
|
|
gmonname = *argv;
|
|
}
|
|
}while(*argv++ != 0);
|
|
|
|
/*
|
|
* How many ticks per second? If we can't tell, report time in ticks.
|
|
* If this was picked up from the mon.out files use that value.
|
|
*/
|
|
#ifndef __OPENSTEP__
|
|
if(hz == 0)
|
|
#endif
|
|
hz = hertz();
|
|
if(hz == 0){
|
|
hz = 1;
|
|
fprintf(stderr, "time is in ticks, not seconds\n");
|
|
}
|
|
|
|
/*
|
|
* Dump out a gmon.sum file if requested.
|
|
*/
|
|
if(sflag){
|
|
dumpsum(GMONSUM);
|
|
exit(0);
|
|
}
|
|
|
|
/*
|
|
* Assign samples to procedures.
|
|
*/
|
|
alignentries();
|
|
for(i = 0; i < nsample_sets; i++)
|
|
asgnsamples(sample_sets + i);
|
|
|
|
/*
|
|
* Assemble the dynamic profile.
|
|
*/
|
|
timesortnlp = doarcs();
|
|
|
|
/*
|
|
* Print the dynamic profile.
|
|
*/
|
|
printgprof(timesortnlp);
|
|
|
|
/*
|
|
* Print the flat profile.
|
|
*/
|
|
printprof();
|
|
|
|
/*
|
|
* Print the index.
|
|
*/
|
|
printindex();
|
|
|
|
/*
|
|
* Dump out the order files if requested.
|
|
*/
|
|
if(Sflag)
|
|
printscatter();
|
|
|
|
return(0);
|
|
}
|
|
|
|
static
|
|
void
|
|
getpfile(
|
|
char *filename)
|
|
{
|
|
uint32_t magic, left;
|
|
gmon_data_t data;
|
|
int fd;
|
|
struct stat stat;
|
|
|
|
if((fd = open(filename, O_RDONLY)) == -1)
|
|
system_fatal("can't open: %s", filename);
|
|
if(fstat(fd, &stat) == -1)
|
|
system_fatal("can't stat: %s", filename);
|
|
/*
|
|
* See if this gmon.out file is an old format or new format by looking
|
|
* for the magic number of the new format.
|
|
*/
|
|
if(read(fd, &magic, sizeof(uint32_t)) != sizeof(uint32_t))
|
|
system_fatal("malformed gmon.out file: %s (can't read magic "
|
|
"number)", filename);
|
|
if(magic == GMON_MAGIC || magic == GMON_MAGIC_64){
|
|
#ifdef DEBUG_GMON_OUT
|
|
if(magic == GMON_MAGIC)
|
|
printf("GMON_MAGIC\n");
|
|
else
|
|
printf("GMON_MAGIC_64\n");
|
|
#endif
|
|
/*
|
|
* This is a new format gmon.out file. After the magic number comes
|
|
* any number of pairs of gmon_data structs and some typed data.
|
|
*/
|
|
left = stat.st_size - sizeof(uint32_t);
|
|
while(left >= sizeof(struct gmon_data)){
|
|
if(read(fd, &data, sizeof(struct gmon_data)) !=
|
|
sizeof(struct gmon_data))
|
|
system_fatal("malformed gmon.out file: %s (can't read "
|
|
"gmon_data struct)", filename);
|
|
left -= sizeof(struct gmon_data);
|
|
if(left < data.size)
|
|
fatal("truncated or malformed gmon.out file: %s (value in "
|
|
"size field in gmon_data struct more than left in "
|
|
"file)", filename);
|
|
#ifdef DEBUG_GMON_OUT
|
|
printf("gmon_data struct: type = %u size = %u\n", data.type,
|
|
data.size);
|
|
#endif
|
|
switch(data.type){
|
|
case GMONTYPE_SAMPLES:
|
|
#ifdef DEBUG_GMON_OUT
|
|
printf("GMONTYPE_SAMPLES\n");
|
|
#endif
|
|
new_sample_set(fd, filename, data.size, FALSE, magic);
|
|
break;
|
|
case GMONTYPE_RAWARCS:
|
|
#ifdef DEBUG_GMON_OUT
|
|
printf("GMONTYPE_RAWARCS\n");
|
|
#endif
|
|
if(magic == GMON_MAGIC)
|
|
readarcs(fd, filename, data.size);
|
|
else
|
|
readarcs_64(fd, filename, data.size);
|
|
break;
|
|
case GMONTYPE_ARCS_ORDERS:
|
|
#ifdef DEBUG_GMON_OUT
|
|
printf("GMONTYPE_ARCS_ORDERS\n");
|
|
#endif
|
|
if(magic == GMON_MAGIC)
|
|
readarcs_orders(fd, filename, data.size);
|
|
else
|
|
readarcs_orders_64(fd, filename, data.size);
|
|
break;
|
|
#ifdef __OPENSTEP__
|
|
case GMONTYPE_RLD_STATE:
|
|
#ifdef DEBUG_GMON_OUT
|
|
printf("GMONTYPE_RLD_STATE\n");
|
|
#endif
|
|
if(grld_nloaded_states == 0){
|
|
read_rld_state(fd, filename, data.size);
|
|
get_rld_state_symbols();
|
|
}
|
|
else{
|
|
warning("can't process more than one rld loaded state "
|
|
"(ignoring rld state from: %s)", filename);
|
|
lseek(fd, data.size, L_INCR);
|
|
}
|
|
break;
|
|
#endif
|
|
case GMONTYPE_DYLD_STATE:
|
|
#ifdef DEBUG_GMON_OUT
|
|
printf("GMONTYPE_DYLD_STATE\n");
|
|
#endif
|
|
goto setup_dyld_state;
|
|
case GMONTYPE_DYLD2_STATE:
|
|
#ifdef DEBUG_GMON_OUT
|
|
printf("GMONTYPE_DYLD2_STATE\n");
|
|
#endif
|
|
setup_dyld_state:
|
|
if(image_count == 0){
|
|
read_dyld_state(data.type, fd, filename, data.size,
|
|
magic);
|
|
get_dyld_state_symbols();
|
|
}
|
|
else{
|
|
warning("can't process more than one dyld state "
|
|
"(ignoring dyld state from: %s)", filename);
|
|
lseek(fd, data.size, L_INCR);
|
|
}
|
|
break;
|
|
default:
|
|
#ifdef DEBUG_GMON_OUT
|
|
printf("Unknown data.type = %u\n", data.type);
|
|
#endif
|
|
fatal("truncated or malformed gmon.out file: %s (value in "
|
|
"type field in gmon_data struct unknown)", filename);
|
|
}
|
|
left -= data.size;
|
|
}
|
|
if(left != 0)
|
|
fatal("truncated or malformed gmon.out file: %s (file end in "
|
|
"the middle of a gmon_data struct %u)", filename, left);
|
|
}
|
|
else{
|
|
/*
|
|
* This is an old format gmon.out file. It has a profile header
|
|
* then * an array of sampling hits within pc ranges, and then arcs.
|
|
*/
|
|
lseek(fd, 0, L_SET);
|
|
left = stat.st_size;
|
|
left -= new_sample_set(fd, filename, left, TRUE, GMON_MAGIC);
|
|
readarcs(fd, filename, left);
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
#ifdef __OPENSTEP__
|
|
static
|
|
void
|
|
read_rld_state(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes)
|
|
{
|
|
uint32_t i, j, size, size_read, str_size;
|
|
char *strings;
|
|
|
|
size_read = 0;
|
|
size = sizeof(uint32_t);
|
|
if(read(fd, &grld_nloaded_states, size) != size)
|
|
system_fatal("malformed gmon.out file: %s (can't read number of "
|
|
"rld states)", filename);
|
|
size_read += size;
|
|
|
|
grld_loaded_state = (struct rld_loaded_state *)
|
|
malloc(grld_nloaded_states * sizeof(struct rld_loaded_state));
|
|
if(grld_loaded_state == NULL)
|
|
fatal("no room for rld states (malloc failed)");
|
|
|
|
for(i = 0; i < grld_nloaded_states; i++){
|
|
size = sizeof(uint32_t);
|
|
if(read(fd, &(grld_loaded_state[i].header_addr), size) != size)
|
|
system_fatal("malformed gmon.out file: %s (can't read header "
|
|
"address of rld state %lu)", filename, i);
|
|
size_read += size;
|
|
|
|
size = sizeof(uint32_t);
|
|
if(read(fd, &(grld_loaded_state[i].nobject_filenames), size) != size)
|
|
system_fatal("malformed gmon.out file: %s (can't read number "
|
|
"of object file names of rld state %lu)", filename, i);
|
|
size_read += size;
|
|
|
|
grld_loaded_state[i].object_filenames = (char **)
|
|
malloc((grld_loaded_state[i].nobject_filenames + 1) *
|
|
sizeof(char *));
|
|
if(grld_loaded_state[i].object_filenames == NULL)
|
|
fatal("no room for rld state %lu object file names (malloc "
|
|
"failed)", i);
|
|
|
|
size = grld_loaded_state[i].nobject_filenames * sizeof(char *);
|
|
if(read(fd, grld_loaded_state[i].object_filenames, size) != size)
|
|
system_fatal("malformed gmon.out file: %s (can't read offsets "
|
|
"to file names of rld state %lu)", filename, i);
|
|
size_read += size;
|
|
|
|
size = sizeof(uint32_t);
|
|
if(read(fd, &str_size, size) != size)
|
|
system_fatal("malformed gmon.out file: %s (can't read string "
|
|
"size of rld state %lu)", filename, i);
|
|
size_read += size;
|
|
|
|
strings = (char *)malloc(str_size);
|
|
if(strings == NULL)
|
|
fatal("no room for rld state %lu object file names (malloc "
|
|
"failed)", i);
|
|
|
|
size = str_size;
|
|
if(read(fd, strings, size) != size)
|
|
system_fatal("malformed gmon.out file: %s (can't read strings "
|
|
"of file names of rld state %lu)", filename, i);
|
|
size_read += size;
|
|
|
|
for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++){
|
|
if((uint32_t)(grld_loaded_state[i].object_filenames[j]) >
|
|
str_size)
|
|
fatal("malformed gmon.out file: %s (bad offset to object "
|
|
"filename %lu of rld state %lu)", filename, j, i);
|
|
grld_loaded_state[i].object_filenames[j] = strings +
|
|
(uint32_t)(grld_loaded_state[i].object_filenames[j]);
|
|
}
|
|
grld_loaded_state[i].object_filenames[j] = NULL;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if(debug & RLDDEBUG){
|
|
printf("grld_nloaded_states = %lu\n", grld_nloaded_states);
|
|
printf("grld_loaded_state 0x%x\n", (unsigned int)grld_loaded_state);
|
|
for(i = 0; i < grld_nloaded_states; i++){
|
|
printf("state %lu\n\tnobject_filenames %lu\n\tobject_filenames "
|
|
"0x%x\n\theader_addr 0x%x\n", i,
|
|
grld_loaded_state[i].nobject_filenames,
|
|
(unsigned int)(grld_loaded_state[i].object_filenames),
|
|
(unsigned int)(grld_loaded_state[i].header_addr));
|
|
for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++)
|
|
printf("\t\t%s\n", grld_loaded_state[i].object_filenames[j]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static
|
|
void
|
|
read_dyld_state(
|
|
uint32_t type,
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes,
|
|
uint32_t magic)
|
|
{
|
|
uint32_t i, offset, sizeof_vmaddr, vmaddr32;
|
|
uint64_t vmaddr64;
|
|
char *buf;
|
|
|
|
buf = malloc(nbytes);
|
|
if(buf == NULL)
|
|
fatal("no room for dyld state (malloc failed)");
|
|
|
|
if(read(fd, buf, nbytes) != (int)nbytes)
|
|
system_fatal("malformed gmon.out file: %s (can't read dyld state)",
|
|
filename);
|
|
|
|
offset = 0;
|
|
if(offset + sizeof(uint32_t) > nbytes)
|
|
fatal("truncated or malformed gmon.out file: %s (image count "
|
|
"extends past the end of the dyld state)", filename);
|
|
memcpy(&image_count, buf + offset, sizeof(uint32_t));
|
|
offset += sizeof(uint32_t);
|
|
|
|
dyld_images = (struct dyld_image *)malloc(sizeof(struct dyld_image) *
|
|
image_count);
|
|
if(dyld_images == NULL)
|
|
fatal("no room for dyld images (malloc failed)");
|
|
|
|
if(magic == GMON_MAGIC)
|
|
sizeof_vmaddr = sizeof(uint32_t);
|
|
else
|
|
sizeof_vmaddr = sizeof(uint64_t);
|
|
for(i = 0; i < image_count; i++){
|
|
if(offset + sizeof_vmaddr > nbytes)
|
|
fatal("truncated or malformed gmon.out file: %s (vmaddr "
|
|
"for image %u extends past the end of the dyld state)",
|
|
filename, i);
|
|
|
|
if(magic == GMON_MAGIC){
|
|
memcpy(&vmaddr32, buf + offset, sizeof_vmaddr);
|
|
vmaddr64 = vmaddr32;
|
|
}
|
|
else{
|
|
memcpy(&vmaddr64, buf + offset, sizeof_vmaddr);
|
|
}
|
|
offset += sizeof_vmaddr;
|
|
|
|
if(type == GMONTYPE_DYLD_STATE){
|
|
dyld_images[i].vmaddr_slide = vmaddr64;
|
|
dyld_images[i].image_header = 0;
|
|
}
|
|
else{
|
|
dyld_images[i].image_header = vmaddr64;
|
|
dyld_images[i].vmaddr_slide = 0;
|
|
}
|
|
|
|
dyld_images[i].name = buf + offset;
|
|
while(buf[offset] != '\0' && offset < nbytes)
|
|
offset++;
|
|
if(buf[offset] == '\0' && offset < nbytes)
|
|
offset++;
|
|
if(offset > nbytes)
|
|
fatal("truncated or malformed gmon.out file: %s (name "
|
|
"for image %u extends past the end of the dyld state)",
|
|
filename, i);
|
|
}
|
|
#ifdef DEBUG
|
|
if(debug & DYLDDEBUG){
|
|
printf("image_count = %u\n", image_count);
|
|
for(i = 0; i < image_count; i++){
|
|
printf("dyld_image[%u].name = %s\n", i, dyld_images[i].name);
|
|
printf("dyld_image[%u].vmaddr_slide = 0x%llx\n", i,
|
|
dyld_images[i].vmaddr_slide);
|
|
printf("dyld_image[%u].image_header = 0x%llx\n", i,
|
|
dyld_images[i].image_header);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static
|
|
uint32_t
|
|
new_sample_set(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes,
|
|
enum bool old_style,
|
|
uint32_t magic)
|
|
{
|
|
#ifdef __OPENSTEP__
|
|
struct phdr header;
|
|
#else
|
|
struct gmonhdr header32;
|
|
struct gmonhdr_64 header;
|
|
#endif
|
|
uint64_t i, j, size;
|
|
uint32_t sizeof_header;
|
|
unsigned UNIT sample, *samples;
|
|
|
|
if(magic == GMON_MAGIC)
|
|
sizeof_header = sizeof(struct gmonhdr);
|
|
else
|
|
sizeof_header = sizeof(struct gmonhdr_64);
|
|
|
|
if(nbytes < sizeof_header)
|
|
fatal("gmon.out file: %s malformed (byte count less than the "
|
|
"size of the expected header)", filename);
|
|
/*
|
|
* Read the profile header and check to see if it is valid.
|
|
*/
|
|
if(magic == GMON_MAGIC)
|
|
size = read(fd, &header32, sizeof_header);
|
|
else
|
|
size = read(fd, &header, sizeof_header);
|
|
|
|
if(size != sizeof_header)
|
|
system_fatal("malformed gmon.out file: %s (can't read header)",
|
|
filename);
|
|
|
|
if(magic == GMON_MAGIC){
|
|
header.lpc = header32.lpc;
|
|
header.hpc = header32.hpc;
|
|
header.ncnt = header32.ncnt;
|
|
header.version = header32.version;
|
|
header.profrate = header32.profrate;
|
|
header.spare[0] = header32.spare[0];
|
|
header.spare[1] = header32.spare[1];
|
|
header.spare[2] = header32.spare[2];
|
|
}
|
|
|
|
/*
|
|
* The ncnt field is a byte count of the header and the number of
|
|
* bytes of the 2byte samples that follow it. Try to make sure this
|
|
* is reasonable and that what's left in the file is at least that big.
|
|
*/
|
|
if(header.ncnt < sizeof_header)
|
|
fatal("gmon.out file: %s malformed (ncnt field less than the "
|
|
"size of the expected header)", filename);
|
|
if(header.ncnt > nbytes)
|
|
fatal("gmon.out file: %s malformed (ncnt field greater than "
|
|
"the byte count for it in the file)", filename);
|
|
if(header.hpc < header.lpc)
|
|
fatal("gmon.out file: %s malformed (high pc less than low pc "
|
|
"in header)", filename);
|
|
if(((header.ncnt - sizeof_header) % sizeof(unsigned short)) != 0)
|
|
warning("gmon.out file: %s malformed (number of sample bytes "
|
|
"not a multiple of sizeof(unsigned short))", filename);
|
|
#ifndef __OPENSTEP__
|
|
if(header.version != GMONVERSION)
|
|
warning("gmon.out file: %s unknown version (0x%x)", filename,
|
|
header.version);
|
|
if(hz == 0){
|
|
if(header.profrate == 0)
|
|
warning("gmon.out file: %s profrate is zero", filename);
|
|
else
|
|
hz = header.profrate;
|
|
}
|
|
else{
|
|
if(hz != header.profrate)
|
|
warning("gmon.out file: %s profrate (%d) does not prevous "
|
|
"profrate (%u)\n", filename, header.profrate, hz);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* See if this sample range matches another range
|
|
*/
|
|
for(i = 0; i < nsample_sets; i++){
|
|
if(sample_sets[i].s_lowpc == header.lpc &&
|
|
sample_sets[i].s_highpc == header.hpc &&
|
|
sample_sets[i].sampbytes == header.ncnt - sizeof_header)
|
|
break;
|
|
}
|
|
/*
|
|
* For old_style gmon.out files with one only pc sample range it must
|
|
* match up if there are any ranges.
|
|
*/
|
|
if(old_style == TRUE && sample_sets != NULL && i != nsample_sets)
|
|
fatal("gmon.out file: %s incompatible with first gmon.out file",
|
|
filename);
|
|
|
|
/*
|
|
* If this range does not match up with a previous range then create
|
|
* a new sample set for it.
|
|
*/
|
|
if(i == nsample_sets){
|
|
sample_sets = realloc(sample_sets, (nsample_sets + 1) *
|
|
sizeof(struct sample_set));
|
|
if(sample_sets == NULL)
|
|
fatal("no room for next pc sample set (malloc failed)");
|
|
i = nsample_sets;
|
|
nsample_sets++;
|
|
memset(sample_sets + i, '\0', sizeof(struct sample_set));
|
|
|
|
sample_sets[i].s_lowpc = header.lpc;
|
|
sample_sets[i].s_highpc = header.hpc;
|
|
sample_sets[i].lowpc = header.lpc / sizeof(UNIT);
|
|
sample_sets[i].highpc = header.hpc / sizeof(UNIT);
|
|
sample_sets[i].sampbytes = header.ncnt - sizeof_header;
|
|
sample_sets[i].nsamples = sample_sets[i].sampbytes /
|
|
sizeof(unsigned UNIT);
|
|
sample_sets[i].scale = ((double)(sample_sets[i].highpc) -
|
|
(double)(sample_sets[i].lowpc)) /
|
|
(double)(sample_sets[i].nsamples);
|
|
sample_sets[i].samples = calloc(sample_sets[i].sampbytes,
|
|
sizeof(unsigned UNIT));
|
|
if(sample_sets[i].samples == NULL)
|
|
fatal("no room for %llu sample pc's\n",
|
|
sample_sets[i].sampbytes / sizeof(unsigned UNIT));
|
|
#ifndef __OPENSTEP__
|
|
sample_sets[i].version = header.version;
|
|
sample_sets[i].profrate = header.profrate;
|
|
sample_sets[i].spare[0] = header.spare[0];
|
|
sample_sets[i].spare[1] = header.spare[1];
|
|
sample_sets[i].spare[2] = header.spare[2];
|
|
#endif
|
|
}
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[new_sample_set] header.lpc 0x%llx header.hpc 0x%llx "
|
|
"header.ncnt %u\n", header.lpc, header.hpc, header.ncnt);
|
|
printf("[new_sample_set] s_lowpc 0x%llx s_highpc 0x%llx\n",
|
|
sample_sets[i].s_lowpc, sample_sets[i].s_highpc);
|
|
printf("[new_sample_set] lowpc 0x%llx highpc 0x%llx\n" ,
|
|
sample_sets[i].lowpc, sample_sets[i].highpc);
|
|
printf("[new_sample_set] sampbytes %llu nsamples %llu\n" ,
|
|
sample_sets[i].sampbytes, sample_sets[i].nsamples);
|
|
#ifndef __OPENSTEP__
|
|
printf("[new_sample_set] version 0x%x\n",
|
|
(unsigned int)(sample_sets[i].version));
|
|
printf("[new_sample_set] profrate %d\n" ,sample_sets[i].profrate);
|
|
printf("[new_sample_set] spare[0] %d\n" ,sample_sets[i].spare[0]);
|
|
printf("[new_sample_set] spare[1] %d\n" ,sample_sets[i].spare[1]);
|
|
printf("[new_sample_set] spare[2] %d\n" ,sample_sets[i].spare[2]);
|
|
#endif
|
|
}
|
|
#endif
|
|
/*
|
|
* Read and sum up the sample counts for this sample set.
|
|
*/
|
|
size = sample_sets[i].nsamples * sizeof(unsigned UNIT);
|
|
samples = malloc(size);
|
|
if(samples == NULL)
|
|
system_fatal("can't allocate buffer of size: %llu for samples from "
|
|
"gmon.out file: %s", size, filename);
|
|
if(read(fd, samples, size) != size)
|
|
system_fatal("can't read samples from gmon.out file: %s", filename);
|
|
for(j = 0; j < sample_sets[i].nsamples; j++){
|
|
sample = samples[j];
|
|
#ifdef DEBUG
|
|
if(debug & 8192){
|
|
if(sample != 0)
|
|
printf("sample[%llu] = %u\n", j, sample);
|
|
}
|
|
#endif
|
|
sample_sets[i].samples[j] += sample;
|
|
}
|
|
free(samples);
|
|
return(header.ncnt);
|
|
}
|
|
|
|
static
|
|
void
|
|
readarcs(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes)
|
|
{
|
|
uint32_t i;
|
|
struct rawarc arc;
|
|
|
|
/*
|
|
* Arcs consists of a bunch of <from,self,count> tuples.
|
|
*/
|
|
for(i = 0; i < nbytes; i += sizeof(struct rawarc)){
|
|
if(read(fd, &arc, sizeof(struct rawarc)) != sizeof(struct rawarc))
|
|
system_fatal("malformed gmon.out file: %s (can't read arcs)",
|
|
filename);
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[readarcs] frompc 0x%x selfpc 0x%x count %u\n",
|
|
arc.raw_frompc, arc.raw_selfpc, arc.raw_count);
|
|
}
|
|
#endif
|
|
/*
|
|
* Add this arc.
|
|
*/
|
|
tally(arc.raw_frompc, arc.raw_selfpc, arc.raw_count, 0);
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
readarcs_64(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes)
|
|
{
|
|
uint32_t i;
|
|
struct rawarc_64 arc;
|
|
|
|
/*
|
|
* Arcs consists of a bunch of <from,self,count> tuples.
|
|
*/
|
|
for(i = 0; i < nbytes; i += sizeof(struct rawarc_64)){
|
|
if(read(fd, &arc, sizeof(struct rawarc_64)) !=
|
|
sizeof(struct rawarc_64))
|
|
system_fatal("malformed gmon.out file: %s (can't read arcs)",
|
|
filename);
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[readarcs] frompc 0x%llx selfpc 0x%llx count %u\n",
|
|
arc.raw_frompc, arc.raw_selfpc, arc.raw_count);
|
|
}
|
|
#endif
|
|
/*
|
|
* Add this arc.
|
|
*/
|
|
tally(arc.raw_frompc, arc.raw_selfpc, arc.raw_count, 0);
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
readarcs_orders(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes)
|
|
{
|
|
uint32_t i;
|
|
struct rawarc_order arc_order;
|
|
|
|
/*
|
|
* Arcs consists of a bunch of <from,self,count> tuples.
|
|
*/
|
|
for(i = 0; i < nbytes; i += sizeof(struct rawarc_order)){
|
|
if(read(fd, &arc_order, sizeof(struct rawarc_order)) !=
|
|
sizeof(struct rawarc_order))
|
|
system_fatal("malformed gmon.out file: %s (can't read arcs)",
|
|
filename);
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[readarcs_order] frompc 0x%x selfpc 0x%x count %u "
|
|
"order %u\n", arc_order.raw_frompc, arc_order.raw_selfpc,
|
|
arc_order.raw_count, arc_order.raw_order);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Add this arc.
|
|
*/
|
|
tally(arc_order.raw_frompc, arc_order.raw_selfpc,
|
|
arc_order.raw_count, arc_order.raw_order);
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
readarcs_orders_64(
|
|
int fd,
|
|
char *filename,
|
|
uint32_t nbytes)
|
|
{
|
|
uint32_t i;
|
|
struct rawarc_order_64 arc_order;
|
|
|
|
/*
|
|
* Arcs consists of a bunch of <from,self,count> tuples.
|
|
*/
|
|
for(i = 0; i < nbytes; i += sizeof(struct rawarc_order_64)){
|
|
if(read(fd, &arc_order, sizeof(struct rawarc_order_64)) !=
|
|
sizeof(struct rawarc_order_64))
|
|
system_fatal("malformed gmon.out file: %s (can't read arcs)",
|
|
filename);
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[readarcs_order] frompc 0x%llx selfpc 0x%llx count %u "
|
|
"order %u\n" , arc_order.raw_frompc,arc_order.raw_selfpc,
|
|
arc_order.raw_count, arc_order.raw_order);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Add this arc.
|
|
*/
|
|
tally(arc_order.raw_frompc, arc_order.raw_selfpc,
|
|
arc_order.raw_count, arc_order.raw_order);
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
tally(
|
|
uint64_t frompc,
|
|
uint64_t selfpc,
|
|
uint32_t count,
|
|
uint32_t order)
|
|
{
|
|
uint32_t i;
|
|
nltype *parentp;
|
|
nltype *childp;
|
|
|
|
parentp = nllookup(frompc);
|
|
childp = nllookup(selfpc);
|
|
|
|
if(childp == NULL || parentp == NULL){
|
|
fprintf(stderr, "bad arc from 0x%llx to 0x%llx, count %u "
|
|
"(ignored).\n", frompc, selfpc, count);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* See if this arc comes from inside a shlib's text.
|
|
*/
|
|
for(i = 0; i < nshlib_text_ranges; i++){
|
|
if(frompc >= shlib_text_ranges[i].lowpc &&
|
|
frompc < shlib_text_ranges[i].highpc)
|
|
break;
|
|
}
|
|
if(parentp->name[0] != '+' && parentp->name[0] != '-' &&
|
|
nshlib_text_ranges != 0 && i != nshlib_text_ranges){
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[tally] tossing arc frompc 0x%llx selfpc 0x%llx "
|
|
"count %u order %u\n", frompc, selfpc, count, order);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
childp->ncall += count;
|
|
|
|
/*
|
|
* Set the call order to the lowest order in the arcs for a call this
|
|
* child. Orders in the arcs are start at 1 and thus orders with the
|
|
* value of zero are unordered.
|
|
*/
|
|
if(order != 0){
|
|
if(childp->order == 0)
|
|
childp->order = order;
|
|
else if(order < childp->order)
|
|
childp->order = order;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if(debug & TALLYDEBUG){
|
|
printf("[tally] arc from %s to %s traversed %u times\n",
|
|
parentp->name, childp->name, count);
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
addarc(parentp, childp, count, order);
|
|
}
|
|
|
|
/*
|
|
* Dump out the gmon.sum file.
|
|
*/
|
|
static
|
|
void
|
|
dumpsum(
|
|
char *sumfile)
|
|
{
|
|
uint32_t i, j, magic;
|
|
#ifdef __OPENSTEP__
|
|
uint32_t strsize;
|
|
struct phdr header;
|
|
#else
|
|
struct gmonhdr header;
|
|
#endif
|
|
int fd;
|
|
struct gmon_data data;
|
|
nltype *nlp;
|
|
arctype *arcp;
|
|
struct rawarc_order arc_order;
|
|
|
|
if((fd = open(sumfile, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
|
|
fatal("can't create gmon.sum file: %s", sumfile);
|
|
|
|
/*
|
|
* Write the magic number.
|
|
*/
|
|
magic = GMON_MAGIC;
|
|
if(write(fd, &magic, sizeof(uint32_t)) != sizeof(uint32_t))
|
|
fatal("can't write magic number to gmon.sum file: %s", sumfile);
|
|
|
|
#ifdef __OPENSTEP__
|
|
/*
|
|
* Write the rld_state if any.
|
|
*/
|
|
if(grld_nloaded_states != 0){
|
|
data.type = GMONTYPE_RLD_STATE;
|
|
data.size = sizeof(uint32_t) +
|
|
sizeof(uint32_t) * 3 * grld_nloaded_states;
|
|
for(i = 0; i < grld_nloaded_states; i++){
|
|
data.size += sizeof(uint32_t) *
|
|
grld_loaded_state[i].nobject_filenames;
|
|
for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++)
|
|
data.size +=
|
|
strlen(grld_loaded_state[i].object_filenames[j]) + 1;
|
|
}
|
|
|
|
if(write(fd, &data, sizeof(struct gmon_data)) !=
|
|
sizeof(struct gmon_data))
|
|
fatal("can't write gmon_data struct to gmon.sum file: %s",
|
|
sumfile);
|
|
if(write(fd, &grld_nloaded_states, sizeof(uint32_t)) !=
|
|
sizeof(uint32_t))
|
|
fatal("can't write to gmon.sum file: %s", sumfile);
|
|
for(i = 0; i < grld_nloaded_states; i++){
|
|
if(write(fd, &(grld_loaded_state[i].header_addr),
|
|
sizeof(uint32_t)) != sizeof(uint32_t))
|
|
fatal("can't write to gmon.sum file: %s", sumfile);
|
|
if(write(fd, &(grld_loaded_state[i].nobject_filenames),
|
|
sizeof(uint32_t)) != sizeof(uint32_t))
|
|
fatal("can't write to gmon.sum file: %s", sumfile);
|
|
strsize = 0;
|
|
for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++){
|
|
if(write(fd, &strsize, sizeof(uint32_t)) !=
|
|
sizeof(uint32_t))
|
|
fatal("can't write to gmon.sum file: %s", sumfile);
|
|
strsize +=
|
|
strlen(grld_loaded_state[i].object_filenames[j]) + 1;
|
|
}
|
|
if(write(fd, &strsize, sizeof(uint32_t)) !=
|
|
sizeof(uint32_t))
|
|
fatal("can't write to gmon.sum file: %s", sumfile);
|
|
for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++){
|
|
strsize =
|
|
strlen(grld_loaded_state[i].object_filenames[j]) + 1;
|
|
if(write(fd, grld_loaded_state[i].object_filenames[j],
|
|
strsize) != strsize)
|
|
fatal("can't write to gmon.sum file: %s", sumfile);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Write the samples.
|
|
*/
|
|
for(i = 0; i < nsample_sets; i++){
|
|
data.type = GMONTYPE_SAMPLES;
|
|
data.size = sample_sets[i].sampbytes + sizeof(header);
|
|
if(write(fd, &data, sizeof(struct gmon_data)) !=
|
|
sizeof(struct gmon_data))
|
|
fatal("can't write gmon_data struct to gmon.sum file: %s",
|
|
sumfile);
|
|
|
|
memset(&header, '\0', sizeof(header));
|
|
#ifdef __OPENSTEP__
|
|
header.lpc = (char *)sample_sets[i].s_lowpc;
|
|
header.hpc = (char *)sample_sets[i].s_highpc;
|
|
#else
|
|
header.lpc = sample_sets[i].s_lowpc;
|
|
header.hpc = sample_sets[i].s_highpc;
|
|
header.version = sample_sets[i].version;
|
|
header.profrate = sample_sets[i].profrate;
|
|
header.spare[0] = sample_sets[i].spare[0];
|
|
header.spare[1] = sample_sets[i].spare[1];
|
|
header.spare[2] = sample_sets[i].spare[2];
|
|
#endif
|
|
header.ncnt = sample_sets[i].sampbytes + sizeof(header);
|
|
if(write(fd, &header, sizeof(header)) != sizeof(header))
|
|
fatal("can't write header to gmon.sum file: %s", sumfile);
|
|
|
|
for(j = 0; j < sample_sets[i].nsamples; j++){
|
|
if(write(fd, &(sample_sets[i].samples[j]),
|
|
sizeof(unsigned UNIT)) != sizeof(unsigned UNIT))
|
|
system_fatal("can't write samples to gmon.sum file: %s",
|
|
sumfile);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write the arc information
|
|
*/
|
|
data.type = GMONTYPE_ARCS_ORDERS;
|
|
data.size = 0;
|
|
for(nlp = nl; nlp < npe; nlp++){
|
|
for(arcp = nlp->children; arcp; arcp = arcp->arc_childlist){
|
|
data.size += sizeof(struct rawarc_order);
|
|
}
|
|
}
|
|
if(write(fd, &data, sizeof(struct gmon_data)) !=
|
|
sizeof(struct gmon_data))
|
|
fatal("can't write gmon_data struct to gmon.sum file: %s",
|
|
sumfile);
|
|
|
|
for(nlp = nl; nlp < npe; nlp++){
|
|
for(arcp = nlp->children; arcp; arcp = arcp->arc_childlist){
|
|
arc_order.raw_frompc = arcp->arc_parentp->value;
|
|
arc_order.raw_selfpc = arcp->arc_childp->value;
|
|
arc_order.raw_count = arcp->arc_count;
|
|
arc_order.raw_order = arcp->arc_order;
|
|
if(write(fd, &arc_order, sizeof(struct rawarc_order)) !=
|
|
sizeof(struct rawarc_order))
|
|
fatal("can't write arc to gmon.sum file: %s", sumfile);
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[dumpsum] frompc 0x%x selfpc 0x%x count %u "
|
|
"order %u\n" ,
|
|
(unsigned int)arc_order.raw_frompc,
|
|
(unsigned int)arc_order.raw_selfpc,
|
|
arc_order.raw_count,
|
|
arc_order.raw_order);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
|
|
/*
|
|
* Assign samples to the procedures to which they belong.
|
|
*
|
|
* There are three cases as to where pcl and pch can be
|
|
* with respect to the routine entry addresses svalue0 and svalue1
|
|
* as shown in the following diagram. overlap computes the
|
|
* distance between the arrows, the fraction of the sample
|
|
* that is to be credited to the routine which starts at svalue0.
|
|
*
|
|
* svalue0 svalue1
|
|
* | |
|
|
* v v
|
|
*
|
|
* +-----------------------------------------------+
|
|
* | |
|
|
* | ->| |<- ->| |<- ->| |<- |
|
|
* | | | | | |
|
|
* +---------+ +---------+ +---------+
|
|
*
|
|
* ^ ^ ^ ^ ^ ^
|
|
* | | | | | |
|
|
* pcl pch pcl pch pcl pch
|
|
*
|
|
* For the vax we assert that samples will never fall in the first
|
|
* two bytes of any routine, since that is the entry mask,
|
|
* thus we give call alignentries() to adjust the entry points if
|
|
* the entry mask falls in one bucket but the code for the routine
|
|
* doesn't start until the next bucket. In conjunction with the
|
|
* alignment of routine addresses, this should allow us to have
|
|
* only one sample for every four bytes of text space and never
|
|
* have any overlap (the two end cases, above).
|
|
*/
|
|
static
|
|
void
|
|
asgnsamples(
|
|
struct sample_set *s)
|
|
{
|
|
uint32_t i, j;
|
|
unsigned UNIT ccnt;
|
|
double time;
|
|
uint32_t pcl, pch, overlap, svalue0, svalue1;
|
|
|
|
/* read samples and assign to namelist symbols */
|
|
for(i = 0, j = 1; i < s->nsamples; i++){
|
|
ccnt = s->samples[i];
|
|
if (ccnt == 0)
|
|
continue;
|
|
pcl = s->lowpc + s->scale * i;
|
|
pch = s->lowpc + s->scale * (i + 1);
|
|
time = ccnt;
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n",
|
|
(unsigned int)pcl, (unsigned int)pch, ccnt);
|
|
}
|
|
#endif
|
|
totime += time;
|
|
for(j = j - 1; j < nname; j++){
|
|
svalue0 = nl[j].svalue;
|
|
svalue1 = nl[j+1].svalue;
|
|
/*
|
|
* if high end of tick is below entry address,
|
|
* go for next tick.
|
|
*/
|
|
if(pch < svalue0)
|
|
break;
|
|
/*
|
|
* if low end of tick into next routine,
|
|
* go for next routine.
|
|
*/
|
|
if(pcl >= svalue1)
|
|
continue;
|
|
overlap = min(pch, svalue1) - max(pcl, svalue0);
|
|
if(overlap > 0){
|
|
#ifdef DEBUG
|
|
if (debug & SAMPLEDEBUG) {
|
|
printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f "
|
|
"ticks %u overlap\n",
|
|
(unsigned int)(nl[j].value/sizeof(UNIT)),
|
|
(unsigned int)svalue0, (unsigned int)svalue1,
|
|
nl[j].name, overlap * time / s->scale, overlap);
|
|
}
|
|
#endif
|
|
nl[j].time += overlap * time / s->scale;
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[asgnsamples] totime %f\n", totime);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static
|
|
uint64_t
|
|
min(
|
|
uint64_t a,
|
|
uint64_t b)
|
|
{
|
|
if(a < b)
|
|
return(a);
|
|
return(b);
|
|
}
|
|
|
|
static
|
|
uint64_t
|
|
max(
|
|
uint64_t a,
|
|
uint64_t b)
|
|
{
|
|
if(a > b)
|
|
return(a);
|
|
return(b);
|
|
}
|
|
|
|
/*
|
|
* Calculate scaled entry point addresses (to save time in asgnsamples),
|
|
* and possibly push the scaled entry points over the entry mask (vax),
|
|
* if it turns out that the entry point is in one bucket and the code
|
|
* for a routine is in the next bucket.
|
|
*/
|
|
static
|
|
void
|
|
alignentries(
|
|
void)
|
|
{
|
|
struct nl *nlp;
|
|
#ifdef vax
|
|
uint32_t i;
|
|
uint32_t bucket_of_entry;
|
|
uint32_t bucket_of_code;
|
|
#endif /* vax */
|
|
|
|
for(nlp = nl; nlp < npe; nlp++){
|
|
nlp->svalue = nlp->value / sizeof(UNIT);
|
|
#ifdef vax
|
|
for(i = 0; i < nsample_sets; i++){
|
|
if(nlp->svalue >= sample_sets[i].s_lowpc &&
|
|
nlp->svalue < sample_sets[i].s_lowpc)
|
|
break;
|
|
}
|
|
if(i == nsample_sets)
|
|
continue;
|
|
bucket_of_entry = (nlp->svalue - sample_sets[i].lowpc) /
|
|
sample_sets[i].scale;
|
|
bucket_of_code = (nlp->svalue + UNITS_TO_CODE -
|
|
sample_sets[i].lowpc) / sample_sets[i].scale;
|
|
if(bucket_of_entry < bucket_of_code) {
|
|
#ifdef DEBUG
|
|
if(debug & SAMPLEDEBUG){
|
|
printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
|
|
(unsigned int)nlp->svalue,
|
|
(unsigned int)(nlp->svalue + UNITS_TO_CODE));
|
|
}
|
|
#endif /* DEBUG */
|
|
nlp->svalue += UNITS_TO_CODE;
|
|
}
|
|
#endif /* vax */
|
|
}
|
|
}
|