Deleted Added
sdiff udiff text old ( 153710 ) new ( 157144 )
full compact
1/*-
2 * Copyright (c) 2005, Joseph Koshy
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright

--- 8 unchanged lines hidden (view full) ---

19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcstat_log.c 153710 2005-12-25 05:11:29Z jkoshy $");
29
30/*
31 * Transform a hwpmc(4) log into human readable form and into gprof(1)
32 * compatible profiles.
33 *
34 * Each executable object encountered in the log gets one 'gmon.out'
35 * profile per PMC. We currently track:
36 * - program executables
37 * - shared libraries loaded by the runtime loader
38 * - the runtime loader itself
39 * - the kernel.
40 * We do not track shared objects mapped in by dlopen() yet (this
41 * needs additional support from hwpmc(4)).
42 *
43 * 'gmon.out' profiles generated for a given sampling PMC are
44 * aggregates of all the samples for that particular executable
45 * object.
46 */
47
48#include <sys/param.h>
49#include <sys/endian.h>
50#include <sys/gmon.h>
51#include <sys/imgact_aout.h>
52#include <sys/imgact_elf.h>
53#include <sys/mman.h>
54#include <sys/pmc.h>
55#include <sys/queue.h>

--- 17 unchanged lines hidden (view full) ---

73#include <unistd.h>
74
75#include "pmcstat.h"
76
77#define min(A,B) ((A) < (B) ? (A) : (B))
78#define max(A,B) ((A) > (B) ? (A) : (B))
79
80/*
81 * A simple implementation of interned strings. Each interned string
82 * is assigned a unique address, so that subsequent string compares
83 * can be done by a simple pointer comparision instead of with
84 * strcmp().
85 */
86struct pmcstat_string {
87 LIST_ENTRY(pmcstat_string) ps_next; /* hash link */
88 int ps_len;
89 int ps_hash;
90 const char *ps_string;
91};
92
93static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH];
94
95/*
96 * 'pmcstat_pmcrecord' is a mapping from PMC ids to human-readable
97 * names.
98 */
99
100struct pmcstat_pmcrecord {
101 LIST_ENTRY(pmcstat_pmcrecord) pr_next;
102 pmc_id_t pr_pmcid;
103 const char *pr_pmcname;
104};
105
106static LIST_HEAD(,pmcstat_pmcrecord) pmcstat_pmcs =
107 LIST_HEAD_INITIALIZER(&pmcstat_pmcs);
108
109
110/*
111 * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These
112 * files are mmap()'ed in as needed.
113 */
114
115struct pmcstat_gmonfile {
116 LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */
117 pmc_id_t pgf_pmcid; /* id of the associated pmc */
118 size_t pgf_nbuckets; /* #buckets in this gmon.out */
119 const char *pgf_name; /* pathname of gmon.out file */
120 size_t pgf_ndatabytes; /* number of bytes mapped */
121 void *pgf_gmondata; /* pointer to mmap'ed data */
122};
123
124static TAILQ_HEAD(,pmcstat_gmonfile) pmcstat_gmonfiles =
125 TAILQ_HEAD_INITIALIZER(pmcstat_gmonfiles);
126
127/*
128 * A 'pmcstat_image' structure describes an executable program on
129 * disk. 'pi_internedpath' is a cookie representing the pathname of
130 * the executable. 'pi_start' and 'pi_end' are the least and greatest
131 * virtual addresses for the text segments in the executable.
132 * 'pi_gmonlist' contains a linked list of gmon.out files associated
133 * with this image.
134 */
135
136enum pmcstat_image_type {
137 PMCSTAT_IMAGE_UNKNOWN = 0,
138 PMCSTAT_IMAGE_ELF,
139 PMCSTAT_IMAGE_AOUT
140};
141
142struct pmcstat_image {
143 LIST_ENTRY(pmcstat_image) pi_next; /* hash link */
144 TAILQ_ENTRY(pmcstat_image) pi_lru; /* LRU list */
145 const char *pi_internedpath; /* cookie */
146 const char *pi_samplename; /* sample path name */
147
148 enum pmcstat_image_type pi_type; /* executable type */
149 uintfptr_t pi_start; /* start address (inclusive) */
150 uintfptr_t pi_end; /* end address (exclusive) */
151 uintfptr_t pi_entry; /* entry address */
152 int pi_isdynamic; /* whether a dynamic object */
153 const char *pi_dynlinkerpath; /* path in .interp section */
154
155 LIST_HEAD(,pmcstat_gmonfile) pi_gmlist;
156};
157
158static LIST_HEAD(,pmcstat_image) pmcstat_image_hash[PMCSTAT_NHASH];
159static TAILQ_HEAD(,pmcstat_image) pmcstat_image_lru =
160 TAILQ_HEAD_INITIALIZER(pmcstat_image_lru);
161
162struct pmcstat_pcmap {
163 TAILQ_ENTRY(pmcstat_pcmap) ppm_next;
164 uintfptr_t ppm_lowpc;
165 uintfptr_t ppm_highpc;
166 struct pmcstat_image *ppm_image;
167};
168
169/*
170 * A 'pmcstat_process' structure tracks processes.
171 */
172
173struct pmcstat_process {
174 LIST_ENTRY(pmcstat_process) pp_next; /* hash-next */
175 pid_t pp_pid; /* associated pid */
176 int pp_isactive; /* whether active */
177 uintfptr_t pp_entryaddr; /* entry address */
178 TAILQ_HEAD(,pmcstat_pcmap) pp_map; /* address range map */
179};
180
181static LIST_HEAD(,pmcstat_process) pmcstat_process_hash[PMCSTAT_NHASH];
182
183static struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */
184
185/*
186 * Prototypes
187 */
188
189static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf,
190 struct pmcstat_image *_image);
191static const char *pmcstat_gmon_create_name(const char *_sd,
192 struct pmcstat_image *_img, pmc_id_t _pmcid);
193static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf);
194static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf);
195
196static struct pmcstat_image *pmcstat_image_from_path(const char *_path);
197static enum pmcstat_image_type pmcstat_image_get_type(const char *_p);
198static void pmcstat_image_get_elf_params(struct pmcstat_image *_image);
199static void pmcstat_image_increment_bucket(struct pmcstat_pcmap *_pcm,
200 uintfptr_t _pc, pmc_id_t _pmcid, struct pmcstat_args *_a);
201static void pmcstat_image_link(struct pmcstat_process *_pp,
202 struct pmcstat_image *_i, uintfptr_t _lpc, uintfptr_t _hpc);
203
204static void pmcstat_pmcid_add(pmc_id_t _pmcid, const char *_name,
205 struct pmcstat_args *_a);
206static const char *pmcstat_pmcid_to_name(pmc_id_t _pmcid);
207
208static void pmcstat_process_add_elf_image(struct pmcstat_process *_pp,
209 const char *_path, uintfptr_t _entryaddr);
210static void pmcstat_process_exec(struct pmcstat_process *_pp,
211 const char *_path, uintfptr_t _entryaddr);
212static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid, int _allocate);
213static struct pmcstat_pcmap *pmcstat_process_find_map(
214 struct pmcstat_process *_p, uintfptr_t _pc);
215
216static int pmcstat_string_compute_hash(const char *_string);
217static const char *pmcstat_string_intern(const char *_s);
218static struct pmcstat_string *pmcstat_string_lookup(const char *_s);
219
220
221/*
222 * Create a gmon.out file and size it.
223 */
224
225static void
226pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf,
227 struct pmcstat_image *image)
228{
229 int fd;
230 size_t count;
231 struct gmonhdr gm;
232 char buffer[DEFAULT_BUFFER_SIZE];
233
234 if ((fd = open(pgf->pgf_name, O_RDWR|O_NOFOLLOW|O_CREAT,
235 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
236 err(EX_OSERR, "ERROR: Cannot open \"%s\"", pgf->pgf_name);
237
238 gm.lpc = image->pi_start;
239 gm.hpc = image->pi_end;
240 gm.ncnt = (pgf->pgf_nbuckets * sizeof(HISTCOUNTER)) +
241 sizeof(struct gmonhdr);
242 gm.version = GMONVERSION;
243 gm.profrate = 0; /* use ticks */
244 gm.histcounter_type = 0; /* compatibility with moncontrol() */

--- 11 unchanged lines hidden (view full) ---

256 if (write(fd, &buffer, sizeof(buffer)) < 0)
257 goto error;
258 count -= sizeof(buffer);
259 }
260
261 if (write(fd, &buffer, count) < 0)
262 goto error;
263
264 (void) close(fd);
265
266 return;
267
268 error:
269 err(EX_OSERR, "ERROR: Cannot write \"%s\"", pgf->pgf_name);
270}
271
272const char *
273pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image,
274 pmc_id_t pmcid)
275{
276 const char *pmcname;
277 char fullpath[PATH_MAX];
278
279 pmcname = pmcstat_pmcid_to_name(pmcid);
280
281 (void) snprintf(fullpath, sizeof(fullpath),
282 "%s/%s/%s", samplesdir, pmcname, image->pi_samplename);
283
284 return pmcstat_string_intern(fullpath);
285}
286
287
288static void
289pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf)
290{
291 int fd;
292
293 /* the gmon.out file must already exist */
294 if ((fd = open(pgf->pgf_name, O_RDWR | O_NOFOLLOW, 0)) < 0)
295 err(EX_OSERR, "ERROR: cannot open \"%s\"",
296 pgf->pgf_name);
297
298 pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes,
299 PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0);
300
301 if (pgf->pgf_gmondata == MAP_FAILED)
302 /* XXX unmap a few files and try again? */
303 err(EX_OSERR, "ERROR: cannot map \"%s\"", pgf->pgf_name);
304
305 (void) close(fd);
306}
307
308/*
309 * Unmap the data mapped from a gmon.out file.
310 */
311
312static void
313pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf)
314{
315 (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes,
316 MS_SYNC);
317 (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes);
318 pgf->pgf_gmondata = NULL;
319}
320
321static void
322pmcstat_image_get_elf_params(struct pmcstat_image *image)
323{
324 int fd, i;
325 struct stat st;
326 void *mapbase;
327 uintfptr_t minva, maxva;
328 const Elf_Ehdr *h;
329 const Elf_Phdr *ph;
330 const Elf_Shdr *sh;
331#if defined(__amd64__)
332 const Elf32_Ehdr *h32;
333 const Elf32_Phdr *ph32;
334 const Elf32_Shdr *sh32;
335#endif
336 const char *path;
337
338 assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN);
339
340 minva = ~(uintfptr_t) 0;
341 maxva = (uintfptr_t) 0;
342 path = image->pi_internedpath;
343
344 if ((fd = open(path, O_RDONLY, 0)) < 0)
345 err(EX_OSERR, "ERROR: Cannot open \"%s\"", path);
346
347 if (fstat(fd, &st) < 0)
348 err(EX_OSERR, "ERROR: Cannot stat \"%s\"", path);
349
350 if ((mapbase = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) ==
351 MAP_FAILED)
352 err(EX_OSERR, "ERROR: Cannot mmap \"%s\"", path);
353
354 (void) close(fd);
355
356 h = (const Elf_Ehdr *) mapbase;
357 if (!IS_ELF(*h))
358 err(EX_SOFTWARE, "ERROR: \"%s\" not an ELF file", path);
359
360 /* we only handle executable objects */
361 if (h->e_type != ET_EXEC && h->e_type != ET_DYN)
362 err(EX_DATAERR, "ERROR: Unknown file type for \"%s\"",
363 image->pi_internedpath);
364
365#define GET_VA(H, SH, MINVA, MAXVA) do { \
366 for (i = 0; i < (H)->e_shnum; i++) \
367 if ((SH)[i].sh_flags & SHF_EXECINSTR) { \
368 (MINVA) = min((MINVA),(SH)[i].sh_addr); \
369 (MAXVA) = max((MAXVA),(SH)[i].sh_addr + \
370 (SH)[i].sh_size); \
371 } \
372 } while (0)
373
374
375#define GET_PHDR_INFO(H, PH, IMAGE) do { \
376 for (i = 0; i < (H)->e_phnum; i++) { \
377 switch ((PH)[i].p_type) { \
378 case PT_DYNAMIC: \
379 image->pi_isdynamic = 1; \
380 break; \
381 case PT_INTERP: \
382 image->pi_dynlinkerpath = \
383 pmcstat_string_intern( \
384 (char *) mapbase + \
385 (PH)[i].p_offset); \
386 break; \
387 } \
388 } \
389 } while (0)
390
391 image->pi_type = PMCSTAT_IMAGE_ELF;
392 image->pi_isdynamic = 0;
393 image->pi_dynlinkerpath = NULL;
394
395 switch (h->e_machine) {
396 case EM_386:
397 case EM_486:
398#if defined(__amd64__)
399 /* a 32 bit executable */
400 h32 = (const Elf32_Ehdr *) h;
401 sh32 = (const Elf32_Shdr *)((uintptr_t) mapbase + h32->e_shoff);
402
403 GET_VA(h32, sh32, minva, maxva);
404
405 image->pi_entry = h32->e_entry;
406
407 if (h32->e_type == ET_EXEC) {
408 ph32 = (const Elf32_Phdr *)((uintptr_t) mapbase +
409 h32->e_phoff);
410 GET_PHDR_INFO(h32, ph32, image);
411 }
412 break;
413#endif
414 default:
415 sh = (const Elf_Shdr *)((uintptr_t) mapbase + h->e_shoff);
416
417 GET_VA(h, sh, minva, maxva);
418
419 image->pi_entry = h->e_entry;
420
421 if (h->e_type == ET_EXEC) {
422 ph = (const Elf_Phdr *)((uintptr_t) mapbase +
423 h->e_phoff);
424 GET_PHDR_INFO(h, ph, image);
425 }
426 break;
427 }
428
429#undef GET_PHDR_INFO
430#undef GET_VA
431
432 image->pi_start = minva;
433 image->pi_end = maxva;
434
435 if (munmap(mapbase, st.st_size) < 0)
436 err(EX_OSERR, "ERROR: Cannot unmap \"%s\"", path);
437
438}
439
440/*
441 * Locate an image descriptor given an interned path, adding a fresh
442 * descriptor to the cache if necessary. This function also finds a
443 * suitable name for this image's sample file.
444 */
445
446static struct pmcstat_image *
447pmcstat_image_from_path(const char *internedpath)
448{
449 int count, hash, nlen;
450 struct pmcstat_image *pi;
451 char *sn;
452 char name[NAME_MAX];
453
454 hash = pmcstat_string_compute_hash(internedpath);
455
456 /* Look for an existing entry. */
457 LIST_FOREACH(pi, &pmcstat_image_hash[hash], pi_next)
458 if (pi->pi_internedpath == internedpath) {
459 /* move descriptor to the head of the lru list */
460 TAILQ_REMOVE(&pmcstat_image_lru, pi, pi_lru);
461 TAILQ_INSERT_HEAD(&pmcstat_image_lru, pi, pi_lru);
462 return pi;
463 }
464
465 /*
466 * Allocate a new entry and place at the head of the hash and
467 * LRU lists.
468 */
469 pi = malloc(sizeof(*pi));
470 if (pi == NULL)
471 return NULL;
472
473 pi->pi_type = PMCSTAT_IMAGE_UNKNOWN;
474 pi->pi_internedpath = internedpath;
475 pi->pi_start = ~0;
476 pi->pi_entry = ~0;
477 pi->pi_end = 0;
478
479 /*
480 * Look for a suitable name for the sample files associated
481 * with this image: if `basename(path)`+".gmon" is available,
482 * we use that, otherwise we try iterating through
483 * `basename(path)`+ "~" + NNN + ".gmon" till we get a free
484 * entry.
485 */
486 if ((sn = basename(internedpath)) == NULL)
487 err(EX_OSERR, "ERROR: Cannot process \"%s\"", internedpath);
488
489 nlen = strlen(sn);
490 nlen = min(nlen, (int) sizeof(name) - 6); /* ".gmon\0" */
491
492 snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn);
493
494 if (pmcstat_string_lookup(name) == NULL)
495 pi->pi_samplename = pmcstat_string_intern(name);
496 else {
497 nlen = strlen(sn);
498 nlen = min(nlen, (int) sizeof(name)-10); /* "~ddd.gmon\0" */
499 count = 0;
500 do {
501 count++;
502 snprintf(name, sizeof(name), "%.*s~%3.3d",
503 nlen, sn, count);
504 if (pmcstat_string_lookup(name) == NULL) {
505 pi->pi_samplename = pmcstat_string_intern(name);
506 count = 0;
507 }
508 } while (count > 0);
509 }
510
511 LIST_INIT(&pi->pi_gmlist);
512
513 LIST_INSERT_HEAD(&pmcstat_image_hash[hash], pi, pi_next);
514 TAILQ_INSERT_HEAD(&pmcstat_image_lru, pi, pi_lru);
515
516 return pi;
517}
518
519/*
520 * Given an open file, determine its file type.
521 */
522
523static enum pmcstat_image_type
524pmcstat_image_get_type(const char *path)
525{
526 int fd;
527 Elf_Ehdr eh;
528 struct exec ex;
529 ssize_t nbytes;
530 char buffer[DEFAULT_BUFFER_SIZE];
531
532 if ((fd = open(path, O_RDONLY)) < 0)
533 err(EX_OSERR, "ERROR: Cannot open \"%s\"", path);
534
535 nbytes = max(sizeof(eh), sizeof(ex));
536 if ((nbytes = pread(fd, buffer, nbytes, 0)) < 0)
537 err(EX_OSERR, "ERROR: Cannot read \"%s\"", path);
538
539 (void) close(fd);
540
541 /* check if its an ELF file */
542 if ((unsigned) nbytes >= sizeof(Elf_Ehdr)) {
543 bcopy(buffer, &eh, sizeof(eh));
544 if (IS_ELF(eh))
545 return PMCSTAT_IMAGE_ELF;
546 }
547
548 /* Look for an A.OUT header */
549 if ((unsigned) nbytes >= sizeof(struct exec)) {
550 bcopy(buffer, &ex, sizeof(ex));
551 if (!N_BADMAG(ex))
552 return PMCSTAT_IMAGE_AOUT;
553 }
554
555 return PMCSTAT_IMAGE_UNKNOWN;
556}
557
558/*
559 * Increment the bucket in the gmon.out file corresponding to 'pmcid'
560 * and 'pc'.
561 */
562
563static void
564pmcstat_image_increment_bucket(struct pmcstat_pcmap *map, uintfptr_t pc,
565 pmc_id_t pmcid, struct pmcstat_args *a)
566{
567 struct pmcstat_image *image;
568 struct pmcstat_gmonfile *pgf;
569 uintfptr_t bucket;
570 HISTCOUNTER *hc;
571
572 assert(pc >= map->ppm_lowpc && pc < map->ppm_highpc);
573
574 /*
575 * Find the gmon file corresponding to 'pmcid', creating it if
576 * needed.
577 */
578
579 image = map->ppm_image;
580
581 LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next)
582 if (pgf->pgf_pmcid == pmcid)
583 break;
584
585 /* If we don't have a gmon.out file for this PMCid, create one */
586 if (pgf == NULL) {
587 if ((pgf = calloc(1, sizeof(*pgf))) == NULL)
588 err(EX_OSERR, "ERROR:");

--- 15 unchanged lines hidden (view full) ---

604
605 /*
606 * Map the gmon file in if needed. It may have been mapped
607 * out under memory pressure.
608 */
609 if (pgf->pgf_gmondata == NULL)
610 pmcstat_gmon_map_file(pgf);
611
612 bucket = (pc - map->ppm_lowpc) / FUNCTION_ALIGNMENT;
613
614 assert(bucket < pgf->pgf_nbuckets);
615
616 hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata +
617 sizeof(struct gmonhdr));
618
619 /* saturating add */
620 if (hc[bucket] < 0xFFFF)
621 hc[bucket]++;
622
623}
624
625/*
626 * Record the fact that PC values from 'lowpc' to 'highpc' come from
627 * image 'image'.
628 */
629
630static void
631pmcstat_image_link(struct pmcstat_process *pp, struct pmcstat_image *image,
632 uintfptr_t lowpc, uintfptr_t highpc)
633{
634 struct pmcstat_pcmap *pcm, *pcmnew;
635
636 if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL)
637 err(EX_OSERR, "ERROR: ");
638
639 pcmnew->ppm_lowpc = lowpc;
640 pcmnew->ppm_highpc = highpc;
641 pcmnew->ppm_image = image;
642
643 TAILQ_FOREACH(pcm, &pp->pp_map, ppm_next)
644 if (pcm->ppm_lowpc < lowpc)
645 break;
646
647 if (pcm == NULL)
648 TAILQ_INSERT_TAIL(&pp->pp_map, pcmnew, ppm_next);
649 else
650 TAILQ_INSERT_BEFORE(pcm, pcmnew, ppm_next);
651}
652
653/*
654 * Add a {pmcid,name} mapping.
655 */
656
657static void
658pmcstat_pmcid_add(pmc_id_t pmcid, const char *name, struct pmcstat_args *a)
659{
660 struct pmcstat_pmcrecord *pr;
661 struct stat st;
662 char fullpath[PATH_MAX];
663
664 LIST_FOREACH(pr, &pmcstat_pmcs, pr_next)
665 if (pr->pr_pmcid == pmcid) {
666 pr->pr_pmcname = name;
667 return;
668 }
669
670 if ((pr = malloc(sizeof(*pr))) == NULL)
671 err(EX_OSERR, "ERROR: Cannot allocate pmc record");
672
673 pr->pr_pmcid = pmcid;
674 pr->pr_pmcname = name;
675 LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next);
676
677 (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", a->pa_samplesdir,
678 name);
679
680 /* If the path name exists, it should be a directory */
681 if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode))
682 return;
683
684 if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0)
685 err(EX_OSERR, "ERROR: Cannot create directory \"%s\"",
686 fullpath);
687}
688
689/*
690 * Given a pmcid in use, find its human-readable name, or a
691 */
692
693static const char *
694pmcstat_pmcid_to_name(pmc_id_t pmcid)
695{
696 struct pmcstat_pmcrecord *pr;
697 char fullpath[PATH_MAX];
698
699 LIST_FOREACH(pr, &pmcstat_pmcs, pr_next)
700 if (pr->pr_pmcid == pmcid)
701 return pr->pr_pmcname;
702
703 /* create a default name and add this entry */
704 if ((pr = malloc(sizeof(*pr))) == NULL)
705 err(EX_OSERR, "ERROR: ");
706 pr->pr_pmcid = pmcid;
707
708 (void) snprintf(fullpath, sizeof(fullpath), "%X", (unsigned int) pmcid);
709 pr->pr_pmcname = pmcstat_string_intern(fullpath);
710
711 LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next);
712
713 return pr->pr_pmcname;
714}
715
716/*
717 * Associate an ELF image with a process. Argument 'path' names the
718 * executable while 'fd' is an already open descriptor to it.
719 */
720
721static void
722pmcstat_process_add_elf_image(struct pmcstat_process *pp, const char *path,
723 uintfptr_t entryaddr)
724{
725 size_t linelen;
726 FILE *rf;
727 char *line;
728 uintmax_t libstart;
729 struct pmcstat_image *image, *rtldimage;
730 char libname[PATH_MAX], libpath[PATH_MAX];
731 char command[PATH_MAX + sizeof(PMCSTAT_LDD_COMMAND) + 1];
732
733 /* Look up path in the cache. */
734 if ((image = pmcstat_image_from_path(path)) == NULL)
735 return;
736
737 if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
738 pmcstat_image_get_elf_params(image);
739
740 /* Create a map entry for the base executable. */
741 pmcstat_image_link(pp, image, image->pi_start, image->pi_end);
742
743 /*
744 * For dynamically linked executables we need to:
745 * (a) find where the dynamic linker was mapped to for this
746 * process,
747 * (b) find all the executable objects that the dynamic linker
748 * brought in.
749 */
750 if (image->pi_isdynamic) {
751
752 /*
753 * The runtime loader gets loaded just after the maximum
754 * possible heap address. Like so:
755 *
756 * [ TEXT DATA BSS HEAP -->*RTLD SHLIBS <--STACK]
757 * ^ ^
758 * 0 VM_MAXUSER_ADDRESS
759 *
760 * The exact address where the loader gets mapped in
761 * will vary according to the size of the executable
762 * and the limits on the size of the process'es data
763 * segment at the time of exec(). The entry address
764 * recorded at process exec time corresponds to the
765 * 'start' address inside the dynamic linker. From
766 * this we can figure out the address where the
767 * runtime loader's file object had been mapped to.
768 */
769 rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath);
770 if (rtldimage == NULL)
771 err(EX_OSERR, "ERROR: Cannot find image for "
772 "\"%s\"", image->pi_dynlinkerpath);
773 if (rtldimage->pi_type == PMCSTAT_IMAGE_UNKNOWN)
774 pmcstat_image_get_elf_params(rtldimage);
775
776 libstart = entryaddr - rtldimage->pi_entry;
777 pmcstat_image_link(pp, rtldimage, libstart,
778 libstart + rtldimage->pi_end - rtldimage->pi_start);
779
780 /* Process all other objects loaded by this executable. */
781 (void) snprintf(command, sizeof(command), "%s %s",
782 PMCSTAT_LDD_COMMAND, path);
783
784 if ((rf = popen(command, "r")) == NULL)
785 err(EX_OSERR, "ERROR: Cannot create pipe");
786
787 (void) fgetln(rf, &linelen);
788
789 while (!feof(rf) && !ferror(rf)) {
790
791 if ((line = fgetln(rf, &linelen)) == NULL)
792 continue;
793 line[linelen-1] = '\0';
794
795 libstart = 0;
796 libpath[0] = libname[0] = '\0';
797 if (sscanf(line, "%s \"%[^\"]\" %jx",
798 libname, libpath, &libstart) != 3)
799 continue;
800
801 if (libstart == 0) {
802 warnx("WARNING: object \"%s\" was not found "
803 "for program \"%s\".", libname, path);
804 continue;
805 }
806
807 image = pmcstat_image_from_path(
808 pmcstat_string_intern(libpath));
809 if (image == NULL)
810 err(EX_OSERR, "ERROR: Cannot process "
811 "\"%s\"", libpath);
812
813 if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
814 pmcstat_image_get_elf_params(image);
815
816 pmcstat_image_link(pp, image, libstart + image->pi_start,
817 libstart + image->pi_end);
818 }
819
820 (void) pclose(rf);
821
822 }
823}
824
825/*
826 * Find the process descriptor corresponding to a PID. If 'allocate'
827 * is zero, we return a NULL if a pid descriptor could not be found or
828 * a process descriptor process. If 'allocate' is non-zero, then we
829 * will attempt to allocate a fresh process descriptor. Zombie

--- 8 unchanged lines hidden (view full) ---

838 struct pmcstat_pcmap *ppm, *ppmtmp;
839 struct pmcstat_process *pp, *pptmp;
840
841 hash = (uint32_t) pid & PMCSTAT_HASH_MASK; /* simplicity wins */
842
843 LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[hash], pp_next, pptmp)
844 if (pp->pp_pid == pid) {
845 /* Found a descriptor, check and process zombies */
846 if (allocate && !pp->pp_isactive) {
847 /* remove maps */
848 TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next,
849 ppmtmp) {
850 TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next);
851 free(ppm);
852 }
853 /* remove process entry */
854 LIST_REMOVE(pp, pp_next);
855 free(pp);
856 break;
857 }
858 return pp;
859 }
860
861 if (!allocate)
862 return NULL;
863
864 if ((pp = malloc(sizeof(*pp))) == NULL)
865 err(EX_OSERR, "ERROR: Cannot allocate pid descriptor");
866
867 pp->pp_pid = pid;
868 pp->pp_isactive = 1;
869
870 TAILQ_INIT(&pp->pp_map);
871
872 LIST_INSERT_HEAD(&pmcstat_process_hash[hash], pp, pp_next);
873 return pp;
874}
875
876/*
877 * Associate an image and a process.
878 */
879
880static void
881pmcstat_process_exec(struct pmcstat_process *pp, const char *path,
882 uintfptr_t entryaddr)
883{
884 enum pmcstat_image_type filetype;
885 struct pmcstat_image *image;
886
887 if ((image = pmcstat_image_from_path(path)) == NULL)
888 return;
889
890 if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
891 filetype = pmcstat_image_get_type(path);
892 else
893 filetype = image->pi_type;
894
895 switch (filetype) {
896 case PMCSTAT_IMAGE_ELF:
897 pmcstat_process_add_elf_image(pp, path, entryaddr);
898 break;
899
900 case PMCSTAT_IMAGE_AOUT:
901 break;
902
903 default:
904 err(EX_SOFTWARE, "ERROR: Unsupported executable type for "
905 "\"%s\"", path);
906 }
907}
908
909
910/*
911 * Find the map entry associated with process 'p' at PC value 'pc'.
912 */
913
914static struct pmcstat_pcmap *
915pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc)
916{
917 struct pmcstat_pcmap *ppm;
918
919 TAILQ_FOREACH(ppm, &p->pp_map, ppm_next)
920 if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc)
921 return ppm;
922
923 return NULL;
924}
925
926
927/*
928 * Compute a 'hash' value for a string.
929 */
930
931static int
932pmcstat_string_compute_hash(const char *s)
933{
934 int hash;
935
936 for (hash = 0; *s; s++)
937 hash ^= *s;
938
939 return hash & PMCSTAT_HASH_MASK;
940}
941
942/*
943 * Intern a copy of string 's', and return a pointer to it.
944 */
945
946static const char *
947pmcstat_string_intern(const char *s)
948{
949 struct pmcstat_string *ps;
950 int hash, len;
951
952 hash = pmcstat_string_compute_hash(s);
953 len = strlen(s);
954
955 if ((ps = pmcstat_string_lookup(s)) != NULL)
956 return ps->ps_string;
957
958 if ((ps = malloc(sizeof(*ps))) == NULL)
959 err(EX_OSERR, "ERROR: Could not intern string");
960 ps->ps_len = len;
961 ps->ps_hash = hash;
962 ps->ps_string = strdup(s);
963 LIST_INSERT_HEAD(&pmcstat_string_hash[hash], ps, ps_next);
964 return ps->ps_string;
965}
966
967static struct pmcstat_string *
968pmcstat_string_lookup(const char *s)
969{
970 struct pmcstat_string *ps;
971 int hash, len;
972
973 hash = pmcstat_string_compute_hash(s);
974 len = strlen(s);
975
976 LIST_FOREACH(ps, &pmcstat_string_hash[hash], ps_next)
977 if (ps->ps_len == len && ps->ps_hash == hash &&
978 strcmp(ps->ps_string, s) == 0)
979 return ps;
980 return NULL;
981}
982
983/*
984 * Public Interfaces.
985 */
986
987/*
988 * Close a logfile, after first flushing all in-module queued data.
989 */
990
991int
992pmcstat_close_log(struct pmcstat_args *a)
993{
994 if (pmc_flush_logfile() < 0 ||
995 pmc_configure_logfile(-1) < 0)
996 err(EX_OSERR, "ERROR: logging failed");
997 a->pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE);
998 return a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING :
999 PMCSTAT_FINISHED;
1000}
1001
1002
1003int
1004pmcstat_convert_log(struct pmcstat_args *a)
1005{
1006 uintfptr_t pc;
1007 struct pmcstat_process *pp, *ppnew;
1008 struct pmcstat_pcmap *ppm, *ppmtmp;
1009 struct pmclog_ev ev;
1010 const char *image_path;
1011
1012 while (pmclog_read(a->pa_logparser, &ev) == 0) {
1013 assert(ev.pl_state == PMCLOG_OK);
1014
1015 switch (ev.pl_type) {
1016 case PMCLOG_TYPE_MAPPINGCHANGE:
1017 /*
1018 * Introduce an address range mapping for a
1019 * process.
1020 */
1021 break;
1022
1023 case PMCLOG_TYPE_PCSAMPLE:
1024
1025 /*
1026 * We bring in the gmon file for the image
1027 * currently associated with the PMC & pid
1028 * pair and increment the appropriate entry
1029 * bin inside this.
1030 */
1031 pc = ev.pl_u.pl_s.pl_pc;
1032 pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid, 1);
1033 if ((ppm = pmcstat_process_find_map(pp, pc)) == NULL &&
1034 (ppm = pmcstat_process_find_map(pmcstat_kernproc,
1035 pc)) == NULL)
1036 break; /* unknown process,offset pair */
1037
1038 pmcstat_image_increment_bucket(ppm, pc,
1039 ev.pl_u.pl_s.pl_pmcid, a);
1040
1041 break;
1042
1043 case PMCLOG_TYPE_PMCALLOCATE:
1044 /*

--- 5 unchanged lines hidden (view full) ---

1050 break;
1051
1052 case PMCLOG_TYPE_PROCEXEC:
1053
1054 /*
1055 * Change the executable image associated with
1056 * a process.
1057 */
1058 pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid, 1);
1059
1060 /* delete the current process map */
1061 TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) {
1062 TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next);
1063 free(ppm);
1064 }
1065
1066 /* locate the descriptor for the new 'base' image */
1067 image_path = pmcstat_string_intern(
1068 ev.pl_u.pl_x.pl_pathname);
1069
1070 /* link to the new image */
1071 pmcstat_process_exec(pp, image_path,
1072 ev.pl_u.pl_x.pl_entryaddr);
1073 break;
1074
1075 case PMCLOG_TYPE_PROCEXIT:
1076
1077 /*
1078 * Due to the way the log is generated, the
1079 * last few samples corresponding to a process
1080 * may appear in the log after the process
1081 * exit event is recorded. Thus we keep the
1082 * process' descriptor and associated data
1083 * structures around, but mark the process as
1084 * having exited.
1085 */
1086 pp = pmcstat_process_lookup(ev.pl_u.pl_e.pl_pid, 0);
1087 if (pp == NULL)
1088 break;
1089 pp->pp_isactive = 0; /* make a zombie */
1090 break;
1091
1092 case PMCLOG_TYPE_SYSEXIT:
1093 pp = pmcstat_process_lookup(ev.pl_u.pl_se.pl_pid, 0);
1094 if (pp == NULL)
1095 break;
1096 pp->pp_isactive = 0; /* make a zombie */
1097 break;
1098
1099 case PMCLOG_TYPE_PROCFORK:
1100
1101 /*
1102 * If we had been tracking 'oldpid', then clone
1103 * its pid descriptor.
1104 */
1105 pp = pmcstat_process_lookup(ev.pl_u.pl_f.pl_oldpid, 0);
1106 if (pp == NULL)
1107 break;
1108
1109 ppnew =
1110 pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid, 1);
1111
1112 /* copy the old process' address maps */
1113 TAILQ_FOREACH(ppm, &pp->pp_map, ppm_next)
1114 pmcstat_image_link(ppnew, ppm->ppm_image,
1115 ppm->ppm_lowpc, ppm->ppm_highpc);
1116 break;
1117
1118 default: /* other types of entries are not relevant */
1119 break;
1120 }
1121 }
1122
1123 if (ev.pl_state == PMCLOG_EOF)
1124 return PMCSTAT_FINISHED;
1125 else if (ev.pl_state == PMCLOG_REQUIRE_DATA)
1126 return PMCSTAT_RUNNING;
1127
1128 err(EX_DATAERR, "ERROR: event parsing failed (record %jd, "
1129 "offset 0x%jx)", (uintmax_t) ev.pl_count + 1, ev.pl_offset);
1130}
1131
1132
1133/*
1134 * Open a log file, for reading or writing.
1135 *
1136 * The function returns the fd of a successfully opened log or -1 in
1137 * case of failure.
1138 */
1139
1140int
1141pmcstat_open(const char *path, int mode)
1142{
1143 int fd;
1144
1145 /*
1146 * If 'path' is "-" then open one of stdin or stdout depending
1147 * on the value of 'mode'. Otherwise, treat 'path' as a file
1148 * name and open that.
1149 */
1150 if (path[0] == '-' && path[1] == '\0')
1151 fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1;
1152 else
1153 fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ?
1154 O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC),
1155 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
1156
1157 return fd;
1158}
1159
1160/*
1161 * Print log entries as text.
1162 */
1163
1164int
1165pmcstat_print_log(struct pmcstat_args *a)
1166{
1167 struct pmclog_ev ev;
1168
1169 while (pmclog_read(a->pa_logparser, &ev) == 0) {
1170 assert(ev.pl_state == PMCLOG_OK);
1171 switch (ev.pl_type) {
1172 case PMCLOG_TYPE_CLOSELOG:
1173 PMCSTAT_PRINT_ENTRY(a,"closelog",);
1174 break;
1175 case PMCLOG_TYPE_DROPNOTIFY:
1176 PMCSTAT_PRINT_ENTRY(a,"drop",);
1177 break;
1178 case PMCLOG_TYPE_INITIALIZE:
1179 PMCSTAT_PRINT_ENTRY(a,"initlog","0x%x \"%s\"",
1180 ev.pl_u.pl_i.pl_version,
1181 pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch));
1182 break;
1183 case PMCLOG_TYPE_MAPPINGCHANGE:
1184 PMCSTAT_PRINT_ENTRY(a,"mapping","%s %d %p %p \"%s\"",
1185 ev.pl_u.pl_m.pl_type == PMCLOG_MAPPING_INSERT ?
1186 "insert" : "delete",
1187 ev.pl_u.pl_m.pl_pid,
1188 (void *) ev.pl_u.pl_m.pl_start,
1189 (void *) ev.pl_u.pl_m.pl_end,
1190 ev.pl_u.pl_m.pl_pathname);
1191 break;
1192 case PMCLOG_TYPE_PCSAMPLE:
1193 PMCSTAT_PRINT_ENTRY(a,"sample","0x%x %d %p %c",
1194 ev.pl_u.pl_s.pl_pmcid,
1195 ev.pl_u.pl_s.pl_pid,
1196 (void *) ev.pl_u.pl_s.pl_pc,
1197 ev.pl_u.pl_s.pl_usermode ? 'u' : 's');
1198 break;
1199 case PMCLOG_TYPE_PMCALLOCATE:

--- 47 unchanged lines hidden (view full) ---

1247 break;
1248 default:
1249 fprintf(a->pa_printfile, "unknown %d",
1250 ev.pl_type);
1251 }
1252 }
1253
1254 if (ev.pl_state == PMCLOG_EOF)
1255 return PMCSTAT_FINISHED;
1256 else if (ev.pl_state == PMCLOG_REQUIRE_DATA)
1257 return PMCSTAT_RUNNING;
1258
1259 err(EX_DATAERR, "ERROR: event parsing failed "
1260 "(record %jd, offset 0x%jx)",
1261 (uintmax_t) ev.pl_count + 1, ev.pl_offset);
1262 /*NOTREACHED*/
1263}
1264
1265/*
1266 * Process a log file in offline analysis mode.
1267 */
1268
1269int
1270pmcstat_process_log(struct pmcstat_args *a)
1271{
1272
1273 /*
1274 * If gprof style profiles haven't been asked for, just print the
1275 * log to the current output file.
1276 */
1277 if (a->pa_flags & FLAG_DO_PRINT)
1278 return pmcstat_print_log(a);
1279 else
1280 /* convert the log to gprof compatible profiles */
1281 return pmcstat_convert_log(a);
1282}
1283
1284void
1285pmcstat_initialize_logging(struct pmcstat_args *a)
1286{
1287 int i;
1288 const char *kernpath;
1289 struct pmcstat_image *img;
1290
1291 /* use a convenient format for 'ldd' output */
1292 if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%o \"%p\" %x\n",1) != 0)
1293 goto error;
1294
1295 /* Initialize hash tables */
1296 for (i = 0; i < PMCSTAT_NHASH; i++) {
1297 LIST_INIT(&pmcstat_image_hash[i]);
1298 LIST_INIT(&pmcstat_process_hash[i]);
1299 LIST_INIT(&pmcstat_string_hash[i]);
1300 }
1301
1302 /* create a fake 'process' entry for the kernel with pid == -1 */
1303 if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1, 1)) == NULL)
1304 goto error;
1305
1306 if ((kernpath = pmcstat_string_intern(a->pa_kernel)) == NULL)
1307 goto error;
1308
1309 img = pmcstat_image_from_path(kernpath);
1310
1311 pmcstat_image_get_elf_params(img);
1312 pmcstat_image_link(pmcstat_kernproc, img, img->pi_start, img->pi_end);
1313
1314 return;
1315
1316 error:
1317 err(EX_OSERR, "ERROR: Cannot initialize logging");
1318}
1319
1320void
1321pmcstat_shutdown_logging(void)
1322{
1323 int i;
1324 struct pmcstat_gmonfile *pgf, *pgftmp;
1325 struct pmcstat_image *pi, *pitmp;
1326 struct pmcstat_process *pp, *pptmp;
1327 struct pmcstat_string *ps, *pstmp;
1328
1329 for (i = 0; i < PMCSTAT_NHASH; i++) {
1330 LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, pitmp) {
1331 /* flush gmon.out data to disk */
1332 LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next,
1333 pgftmp) {
1334 pmcstat_gmon_unmap_file(pgf);
1335 LIST_REMOVE(pgf, pgf_next);
1336 free(pgf);
1337 }
1338
1339 LIST_REMOVE(pi, pi_next);
1340 free(pi);
1341 }
1342 LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next,
1343 pptmp) {
1344 LIST_REMOVE(pp, pp_next);
1345 free(pp);
1346 }
1347 LIST_FOREACH_SAFE(ps, &pmcstat_string_hash[i], ps_next,
1348 pstmp) {
1349 LIST_REMOVE(ps, ps_next);
1350 free(ps);
1351 }
1352 }
1353}