mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-16 10:20:30 +00:00
Determine where the runtime loader got mapped to when a dynamically
linked process was exec()'ed, and map samples in that address range into the appropriate buckets in the runtime loader's profile. Improve a few comments. MFC after: 3 days
This commit is contained in:
parent
dedaf3ca1f
commit
60f918ef43
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=150139
@ -78,11 +78,11 @@ __FBSDID("$FreeBSD$");
|
||||
#define max(A,B) ((A) > (B) ? (A) : (B))
|
||||
|
||||
/*
|
||||
* A simple implementation to intern strings. Each interned string is
|
||||
* assigned a unique address, so that subsequent string compares can
|
||||
* be done by a simple pointer comparision.
|
||||
* A simple implementation of interned strings. Each interned string
|
||||
* is assigned a unique address, so that subsequent string compares
|
||||
* can be done by a simple pointer comparision instead of with
|
||||
* strcmp().
|
||||
*/
|
||||
|
||||
struct pmcstat_string {
|
||||
LIST_ENTRY(pmcstat_string) ps_next; /* hash link */
|
||||
int ps_len;
|
||||
@ -143,12 +143,14 @@ struct pmcstat_image {
|
||||
LIST_ENTRY(pmcstat_image) pi_next; /* hash link */
|
||||
TAILQ_ENTRY(pmcstat_image) pi_lru; /* LRU list */
|
||||
const char *pi_internedpath; /* cookie */
|
||||
const char *pi_samplename; /* sample file name */
|
||||
const char *pi_samplename; /* sample path name */
|
||||
|
||||
enum pmcstat_image_type pi_type; /* executable type */
|
||||
uintfptr_t pi_start; /* start address (inclusive) */
|
||||
uintfptr_t pi_end; /* end address (exclusive) */
|
||||
uintfptr_t pi_entry; /* entry address */
|
||||
int pi_isdynamic; /* whether a dynamic object */
|
||||
const char *pi_dynlinkerpath; /* path in .interp section */
|
||||
|
||||
LIST_HEAD(,pmcstat_gmonfile) pi_gmlist;
|
||||
};
|
||||
@ -172,6 +174,7 @@ struct pmcstat_process {
|
||||
LIST_ENTRY(pmcstat_process) pp_next; /* hash-next */
|
||||
pid_t pp_pid; /* associated pid */
|
||||
int pp_isactive; /* whether active */
|
||||
uintfptr_t pp_entryaddr; /* entry address */
|
||||
TAILQ_HEAD(,pmcstat_pcmap) pp_map; /* address range map */
|
||||
};
|
||||
|
||||
@ -192,8 +195,7 @@ static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf);
|
||||
|
||||
static struct pmcstat_image *pmcstat_image_from_path(const char *_path);
|
||||
static enum pmcstat_image_type pmcstat_image_get_type(const char *_p);
|
||||
static void pmcstat_image_get_elf_params(struct pmcstat_image *_image,
|
||||
uintfptr_t *_minp, uintfptr_t *_maxp, int *_isdyn);
|
||||
static void pmcstat_image_get_elf_params(struct pmcstat_image *_image);
|
||||
static void pmcstat_image_increment_bucket(struct pmcstat_pcmap *_pcm,
|
||||
uintfptr_t _pc, pmc_id_t _pmcid, struct pmcstat_args *_a);
|
||||
static void pmcstat_image_link(struct pmcstat_process *_pp,
|
||||
@ -204,12 +206,12 @@ static void pmcstat_pmcid_add(pmc_id_t _pmcid, const char *_name,
|
||||
static const char *pmcstat_pmcid_to_name(pmc_id_t _pmcid);
|
||||
|
||||
static void pmcstat_process_add_elf_image(struct pmcstat_process *_pp,
|
||||
const char *_path);
|
||||
const char *_path, uintfptr_t _entryaddr);
|
||||
static void pmcstat_process_exec(struct pmcstat_process *_pp,
|
||||
const char *_path, uintfptr_t _entryaddr);
|
||||
static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid, int _allocate);
|
||||
static struct pmcstat_pcmap *pmcstat_process_find_map(
|
||||
struct pmcstat_process *_p, uintfptr_t _pc);
|
||||
static void pmcstat_process_new_image(struct pmcstat_process *_pp,
|
||||
const char *_path);
|
||||
|
||||
static int pmcstat_string_compute_hash(const char *_string);
|
||||
static const char *pmcstat_string_intern(const char *_s);
|
||||
@ -317,8 +319,7 @@ pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf)
|
||||
}
|
||||
|
||||
static void
|
||||
pmcstat_image_get_elf_params(struct pmcstat_image *image, uintfptr_t *minp,
|
||||
uintfptr_t *maxp, int *is_dynamic)
|
||||
pmcstat_image_get_elf_params(struct pmcstat_image *image)
|
||||
{
|
||||
int fd, i;
|
||||
struct stat st;
|
||||
@ -329,6 +330,8 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, uintfptr_t *minp,
|
||||
const Elf_Shdr *sh;
|
||||
const char *path;
|
||||
|
||||
assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN);
|
||||
|
||||
minva = ~(uintfptr_t) 0;
|
||||
maxva = (uintfptr_t) 0;
|
||||
path = image->pi_internedpath;
|
||||
@ -353,8 +356,8 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, uintfptr_t *minp,
|
||||
|
||||
if (h->e_type == ET_EXEC || h->e_type == ET_DYN) {
|
||||
/*
|
||||
* Some kind of shared object: find the min,max va for
|
||||
* its executable sections.
|
||||
* Some kind of executable object: find the min,max va
|
||||
* for its executable sections.
|
||||
*/
|
||||
for (i = 0; i < h->e_shnum; i++)
|
||||
if (sh[i].sh_flags & SHF_EXECINSTR) { /* code */
|
||||
@ -366,13 +369,25 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, uintfptr_t *minp,
|
||||
err(EX_DATAERR, "ERROR: Unknown file type for \"%s\"",
|
||||
image->pi_internedpath);
|
||||
|
||||
*is_dynamic = 0;
|
||||
image->pi_type = PMCSTAT_IMAGE_ELF;
|
||||
image->pi_start = minva;
|
||||
image->pi_entry = h->e_entry;
|
||||
image->pi_end = maxva;
|
||||
image->pi_isdynamic = 0;
|
||||
image->pi_dynlinkerpath = NULL;
|
||||
|
||||
|
||||
if (h->e_type == ET_EXEC) {
|
||||
ph = (const Elf_Phdr *)((uintptr_t) mapbase + h->e_phoff);
|
||||
for (i = 0; i < h->e_phnum; i++) {
|
||||
switch (ph[i].p_type) {
|
||||
case PT_DYNAMIC:
|
||||
*is_dynamic = 1;
|
||||
image->pi_isdynamic = 1;
|
||||
break;
|
||||
case PT_INTERP:
|
||||
image->pi_dynlinkerpath =
|
||||
pmcstat_string_intern((char *) mapbase +
|
||||
ph[i].p_offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -381,13 +396,12 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image, uintfptr_t *minp,
|
||||
if (munmap(mapbase, st.st_size) < 0)
|
||||
err(EX_OSERR, "ERROR: Cannot unmap \"%s\"", path);
|
||||
|
||||
*minp = minva;
|
||||
*maxp = maxva;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Locate an image descriptor given an interned path.
|
||||
* Locate an image descriptor given an interned path, adding a fresh
|
||||
* descriptor to the cache if necessary. This function also finds a
|
||||
* suitable name for this image's sample file.
|
||||
*/
|
||||
|
||||
static struct pmcstat_image *
|
||||
@ -400,7 +414,7 @@ pmcstat_image_from_path(const char *internedpath)
|
||||
|
||||
hash = pmcstat_string_compute_hash(internedpath);
|
||||
|
||||
/* look for an existing entry */
|
||||
/* Look for an existing entry. */
|
||||
LIST_FOREACH(pi, &pmcstat_image_hash[hash], pi_next)
|
||||
if (pi->pi_internedpath == internedpath) {
|
||||
/* move descriptor to the head of the lru list */
|
||||
@ -410,8 +424,8 @@ pmcstat_image_from_path(const char *internedpath)
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a new entry and place at the head of the hash and
|
||||
* LRU lists
|
||||
* Allocate a new entry and place at the head of the hash and
|
||||
* LRU lists.
|
||||
*/
|
||||
pi = malloc(sizeof(*pi));
|
||||
if (pi == NULL)
|
||||
@ -420,17 +434,23 @@ pmcstat_image_from_path(const char *internedpath)
|
||||
pi->pi_type = PMCSTAT_IMAGE_UNKNOWN;
|
||||
pi->pi_internedpath = internedpath;
|
||||
pi->pi_start = ~0;
|
||||
pi->pi_entry = ~0;
|
||||
pi->pi_end = 0;
|
||||
|
||||
/* look for a suitable name for the sample files */
|
||||
/*
|
||||
* Look for a suitable name for the sample files associated
|
||||
* with this image: if `basename(path)`+".gmon" is available,
|
||||
* we use that, otherwise we try iterating through
|
||||
* `basename(path)`+ "~" + NNN + ".gmon" till we get a free
|
||||
* entry.
|
||||
*/
|
||||
if ((sn = basename(internedpath)) == NULL)
|
||||
err(EX_OSERR, "ERROR: Cannot process \"%s\"", internedpath);
|
||||
|
||||
nlen = strlen(sn);
|
||||
nlen = min(nlen, (int) sizeof(name) - 6); /* ".gmon\0" */
|
||||
|
||||
snprintf(name, sizeof(name), "%.*s.gmon",
|
||||
nlen, sn);
|
||||
snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn);
|
||||
|
||||
if (pmcstat_string_lookup(name) == NULL)
|
||||
pi->pi_samplename = pmcstat_string_intern(name);
|
||||
@ -532,6 +552,7 @@ pmcstat_image_increment_bucket(struct pmcstat_pcmap *map, uintfptr_t pc,
|
||||
pgf->pgf_name = pmcstat_gmon_create_name(a->pa_samplesdir,
|
||||
image, pmcid);
|
||||
pgf->pgf_pmcid = pmcid;
|
||||
assert(image->pi_end > image->pi_start);
|
||||
pgf->pgf_nbuckets = (image->pi_end - image->pi_start) /
|
||||
FUNCTION_ALIGNMENT; /* see <machine/profile.h> */
|
||||
pgf->pgf_ndatabytes = sizeof(struct gmonhdr) +
|
||||
@ -659,41 +680,65 @@ pmcstat_pmcid_to_name(pmc_id_t pmcid)
|
||||
*/
|
||||
|
||||
static void
|
||||
pmcstat_process_add_elf_image(struct pmcstat_process *pp, const char *path)
|
||||
pmcstat_process_add_elf_image(struct pmcstat_process *pp, const char *path,
|
||||
uintfptr_t entryaddr)
|
||||
{
|
||||
int isdynamic;
|
||||
size_t linelen;
|
||||
FILE *rf;
|
||||
char *line;
|
||||
uintfptr_t minva, maxva;
|
||||
uintmax_t libstart;
|
||||
struct pmcstat_image *image;
|
||||
struct pmcstat_image *image, *rtldimage;
|
||||
char libpath[PATH_MAX];
|
||||
char command[PATH_MAX + sizeof(PMCSTAT_LDD_COMMAND) + 1];
|
||||
|
||||
minva = ~ (uintfptr_t) 0;
|
||||
maxva = (uintfptr_t) 0;
|
||||
isdynamic = 0;
|
||||
|
||||
/* Look up path in the cache. */
|
||||
if ((image = pmcstat_image_from_path(path)) == NULL)
|
||||
return;
|
||||
|
||||
if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) {
|
||||
if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
|
||||
pmcstat_image_get_elf_params(image);
|
||||
|
||||
pmcstat_image_get_elf_params(image, &minva, &maxva,
|
||||
&isdynamic);
|
||||
|
||||
image->pi_type = PMCSTAT_IMAGE_ELF;
|
||||
image->pi_start = minva;
|
||||
image->pi_end = maxva;
|
||||
image->pi_isdynamic = isdynamic;
|
||||
}
|
||||
|
||||
/* create a map entry for the base executable */
|
||||
pmcstat_image_link(pp, image, minva, maxva);
|
||||
/* Create a map entry for the base executable. */
|
||||
pmcstat_image_link(pp, image, image->pi_start, image->pi_end);
|
||||
|
||||
/*
|
||||
* For dynamically linked executables we need to:
|
||||
* (a) find where the dynamic linker was mapped to for this
|
||||
* process,
|
||||
* (b) find all the executable objects that the dynamic linker
|
||||
* brought in.
|
||||
*/
|
||||
if (image->pi_isdynamic) {
|
||||
|
||||
/*
|
||||
* The runtime loader gets loaded just after the maximum
|
||||
* possible heap address. Like so:
|
||||
*
|
||||
* [ TEXT DATA BSS HEAP -->*RTLD SHLIBS <--STACK]
|
||||
* ^ ^
|
||||
* 0 VM_MAXUSER_ADDRESS
|
||||
*
|
||||
* The exact address where the loader gets mapped in
|
||||
* will vary according to the size of the executable
|
||||
* and the limits on the size of the process'es data
|
||||
* segment at the time of exec(). The entry address
|
||||
* recorded at process exec time corresponds to the
|
||||
* 'start' address inside the dynamic linker. From
|
||||
* this we can figure out the address where the
|
||||
* runtime loader's file object had been mapped to.
|
||||
*/
|
||||
rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath);
|
||||
if (rtldimage == NULL)
|
||||
err(EX_OSERR, "ERROR: Cannot find image for "
|
||||
"\"%s\"", image->pi_dynlinkerpath);
|
||||
if (rtldimage->pi_type == PMCSTAT_IMAGE_UNKNOWN)
|
||||
pmcstat_image_get_elf_params(rtldimage);
|
||||
|
||||
libstart = entryaddr - rtldimage->pi_entry;
|
||||
pmcstat_image_link(pp, rtldimage, libstart,
|
||||
libstart + rtldimage->pi_end - rtldimage->pi_start);
|
||||
|
||||
/* Process all other objects loaded by this executable. */
|
||||
(void) snprintf(command, sizeof(command), "%s %s",
|
||||
PMCSTAT_LDD_COMMAND, path);
|
||||
|
||||
@ -718,16 +763,8 @@ pmcstat_process_add_elf_image(struct pmcstat_process *pp, const char *path)
|
||||
err(EX_OSERR, "ERROR: Cannot process "
|
||||
"\"%s\"", libpath);
|
||||
|
||||
if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) {
|
||||
|
||||
pmcstat_image_get_elf_params(image,
|
||||
&minva, &maxva, &isdynamic);
|
||||
|
||||
image->pi_type = PMCSTAT_IMAGE_ELF;
|
||||
image->pi_start = minva;
|
||||
image->pi_end = maxva;
|
||||
image->pi_isdynamic = isdynamic;
|
||||
}
|
||||
if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
|
||||
pmcstat_image_get_elf_params(image);
|
||||
|
||||
pmcstat_image_link(pp, image, libstart + image->pi_start,
|
||||
libstart + image->pi_end);
|
||||
@ -789,28 +826,13 @@ pmcstat_process_lookup(pid_t pid, int allocate)
|
||||
return pp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the map entry associated with process 'p' at PC value 'pc'.
|
||||
*/
|
||||
|
||||
static struct pmcstat_pcmap *
|
||||
pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc)
|
||||
{
|
||||
struct pmcstat_pcmap *ppm;
|
||||
|
||||
TAILQ_FOREACH(ppm, &p->pp_map, ppm_next)
|
||||
if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc)
|
||||
return ppm;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Associate an image and a process.
|
||||
*/
|
||||
|
||||
static void
|
||||
pmcstat_process_new_image(struct pmcstat_process *pp, const char *path)
|
||||
pmcstat_process_exec(struct pmcstat_process *pp, const char *path,
|
||||
uintfptr_t entryaddr)
|
||||
{
|
||||
enum pmcstat_image_type filetype;
|
||||
struct pmcstat_image *image;
|
||||
@ -825,19 +847,35 @@ pmcstat_process_new_image(struct pmcstat_process *pp, const char *path)
|
||||
|
||||
switch (filetype) {
|
||||
case PMCSTAT_IMAGE_ELF:
|
||||
pmcstat_process_add_elf_image(pp, path);
|
||||
pmcstat_process_add_elf_image(pp, path, entryaddr);
|
||||
break;
|
||||
|
||||
case PMCSTAT_IMAGE_AOUT:
|
||||
break;
|
||||
|
||||
default:
|
||||
err(EX_SOFTWARE, "ERROR: Unsupported executable type \"%s\"",
|
||||
path);
|
||||
err(EX_SOFTWARE, "ERROR: Unsupported executable type for "
|
||||
"\"%s\"", path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Find the map entry associated with process 'p' at PC value 'pc'.
|
||||
*/
|
||||
|
||||
static struct pmcstat_pcmap *
|
||||
pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc)
|
||||
{
|
||||
struct pmcstat_pcmap *ppm;
|
||||
|
||||
TAILQ_FOREACH(ppm, &p->pp_map, ppm_next)
|
||||
if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc)
|
||||
return ppm;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute a 'hash' value for a string.
|
||||
@ -983,7 +1021,8 @@ pmcstat_convert_log(struct pmcstat_args *a)
|
||||
ev.pl_u.pl_x.pl_pathname);
|
||||
|
||||
/* link to the new image */
|
||||
pmcstat_process_new_image(pp, image_path);
|
||||
pmcstat_process_exec(pp, image_path,
|
||||
ev.pl_u.pl_x.pl_entryaddr);
|
||||
break;
|
||||
|
||||
case PMCLOG_TYPE_PROCEXIT:
|
||||
@ -1198,10 +1237,9 @@ pmcstat_process_log(struct pmcstat_args *a)
|
||||
void
|
||||
pmcstat_initialize_logging(struct pmcstat_args *a)
|
||||
{
|
||||
int i, isdynamic;
|
||||
int i;
|
||||
const char *kernpath;
|
||||
struct pmcstat_image *img;
|
||||
uintfptr_t minva, maxva;
|
||||
|
||||
/* use a convenient format for 'ldd' output */
|
||||
if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%p %x\n",1) != 0)
|
||||
@ -1223,12 +1261,8 @@ pmcstat_initialize_logging(struct pmcstat_args *a)
|
||||
|
||||
img = pmcstat_image_from_path(kernpath);
|
||||
|
||||
pmcstat_image_get_elf_params(img, &minva, &maxva, &isdynamic);
|
||||
img->pi_type = PMCSTAT_IMAGE_ELF;
|
||||
img->pi_start = minva;
|
||||
img->pi_end = maxva;
|
||||
|
||||
pmcstat_image_link(pmcstat_kernproc, img, minva, maxva);
|
||||
pmcstat_image_get_elf_params(img);
|
||||
pmcstat_image_link(pmcstat_kernproc, img, img->pi_start, img->pi_end);
|
||||
|
||||
return;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user