pmcpl_callgraph.c revision 212176
1138627Stakawata/*-
2138627Stakawata * Copyright (c) 2005-2007, Joseph Koshy
3147196Smarkus * Copyright (c) 2007 The FreeBSD Foundation
4138627Stakawata * All rights reserved.
5138627Stakawata *
6138627Stakawata * Portions of this software were developed by A. Joseph Koshy under
7138627Stakawata * sponsorship from the FreeBSD Foundation and Google, Inc.
8138627Stakawata *
9138627Stakawata * Redistribution and use in source and binary forms, with or without
10138627Stakawata * modification, are permitted provided that the following conditions
11138627Stakawata * are met:
12138627Stakawata * 1. Redistributions of source code must retain the above copyright
13138627Stakawata *    notice, this list of conditions and the following disclaimer.
14138627Stakawata * 2. Redistributions in binary form must reproduce the above copyright
15138627Stakawata *    notice, this list of conditions and the following disclaimer in the
16138627Stakawata *    documentation and/or other materials provided with the distribution.
17138627Stakawata *
18138627Stakawata * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19138627Stakawata * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20138627Stakawata * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21138627Stakawata * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22138627Stakawata * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23138627Stakawata * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24138627Stakawata * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25138627Stakawata * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26138627Stakawata * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27138627Stakawata * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28143002Sobrien * SUCH DAMAGE.
29143002Sobrien */
30143002Sobrien
31147196Smarkus/*
32147196Smarkus * Transform a hwpmc(4) log into human readable form, and into
33147196Smarkus * gprof(1) compatible profiles.
34147196Smarkus */
35147196Smarkus
36147196Smarkus#include <sys/cdefs.h>
37147196Smarkus__FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcpl_callgraph.c 212176 2010-09-03 13:54:02Z fabient $");
38147196Smarkus
39147196Smarkus#include <sys/param.h>
40138627Stakawata#include <sys/endian.h>
41138627Stakawata#include <sys/gmon.h>
42138627Stakawata#include <sys/imgact_aout.h>
43138627Stakawata#include <sys/imgact_elf.h>
44138627Stakawata#include <sys/mman.h>
45138627Stakawata#include <sys/pmc.h>
46138627Stakawata#include <sys/queue.h>
47138627Stakawata#include <sys/socket.h>
48138627Stakawata#include <sys/stat.h>
49147196Smarkus#include <sys/wait.h>
50138627Stakawata
51138627Stakawata#include <netinet/in.h>
52138627Stakawata
53138825Snjl#include <assert.h>
54138774Sscottl#include <curses.h>
55138774Sscottl#include <err.h>
56147196Smarkus#include <errno.h>
57147196Smarkus#include <fcntl.h>
58147196Smarkus#include <gelf.h>
59147196Smarkus#include <libgen.h>
60147196Smarkus#include <limits.h>
61147196Smarkus#include <netdb.h>
62147196Smarkus#include <pmc.h>
63147196Smarkus#include <pmclog.h>
64147196Smarkus#include <sysexits.h>
65147196Smarkus#include <stdint.h>
66147196Smarkus#include <stdio.h>
67147196Smarkus#include <stdlib.h>
68147196Smarkus#include <string.h>
69138627Stakawata#include <unistd.h>
70147196Smarkus
71147196Smarkus#include "pmcstat.h"
72147196Smarkus#include "pmcstat_log.h"
73147196Smarkus#include "pmcstat_top.h"
74147196Smarkus#include "pmcpl_callgraph.h"
75147196Smarkus
76147196Smarkus/* Get the sample value in percent related to nsamples. */
77147196Smarkus#define PMCPL_CG_COUNTP(a) \
78147196Smarkus	((a)->pcg_count * 100.0 / nsamples)
79147196Smarkus
80147196Smarkus/*
81147196Smarkus * The toplevel CG nodes (i.e., with rank == 0) are placed in a hash table.
82147196Smarkus */
83147196Smarkus
84147196Smarkusstruct pmcstat_cgnode_hash_list pmcstat_cgnode_hash[PMCSTAT_NHASH];
85147196Smarkusint pmcstat_cgnode_hash_count;
86147196Smarkus
87147196Smarkusstatic pmcstat_interned_string pmcstat_previous_filename_printed;
88147196Smarkus
89138627Stakawatastatic struct pmcstat_cgnode *
90147196Smarkuspmcstat_cgnode_allocate(struct pmcstat_image *image, uintfptr_t pc)
91147196Smarkus{
92147196Smarkus	struct pmcstat_cgnode *cg;
93147196Smarkus
94147196Smarkus	if ((cg = malloc(sizeof(*cg))) == NULL)
95147196Smarkus		err(EX_OSERR, "ERROR: Cannot allocate callgraph node");
96147196Smarkus
97147196Smarkus	cg->pcg_image = image;
98147196Smarkus	cg->pcg_func = pc;
99147196Smarkus
100147196Smarkus	cg->pcg_count = 0;
101147196Smarkus	cg->pcg_nchildren = 0;
102147196Smarkus	LIST_INIT(&cg->pcg_children);
103147196Smarkus
104147196Smarkus	return (cg);
105147196Smarkus}
106147196Smarkus
107147196Smarkus/*
108147196Smarkus * Free a node and its children.
109147196Smarkus */
110147196Smarkusstatic void
111147196Smarkuspmcstat_cgnode_free(struct pmcstat_cgnode *cg)
112147196Smarkus{
113147196Smarkus	struct pmcstat_cgnode *cgc, *cgtmp;
114147196Smarkus
115147196Smarkus	LIST_FOREACH_SAFE(cgc, &cg->pcg_children, pcg_sibling, cgtmp)
116147196Smarkus		pmcstat_cgnode_free(cgc);
117147196Smarkus	free(cg);
118147196Smarkus}
119147196Smarkus
120147196Smarkus/*
121147196Smarkus * Look for a callgraph node associated with pmc `pmcid' in the global
122147196Smarkus * hash table that corresponds to the given `pc' value in the process
123147196Smarkus * `pp'.
124147196Smarkus */
125138627Stakawatastatic struct pmcstat_cgnode *
126147196Smarkuspmcstat_cgnode_hash_lookup_pc(struct pmcstat_process *pp, pmc_id_t pmcid,
127147196Smarkus    uintfptr_t pc, int usermode)
128147196Smarkus{
129147196Smarkus	struct pmcstat_pcmap *ppm;
130147196Smarkus	struct pmcstat_symbol *sym;
131147196Smarkus	struct pmcstat_image *image;
132147196Smarkus	struct pmcstat_cgnode *cg;
133147196Smarkus	struct pmcstat_cgnode_hash *h;
134147196Smarkus	uintfptr_t loadaddress;
135147196Smarkus	unsigned int i, hash;
136147196Smarkus
137147196Smarkus	ppm = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, pc);
138147196Smarkus	if (ppm == NULL)
139147196Smarkus		return (NULL);
140147196Smarkus
141147196Smarkus	image = ppm->ppm_image;
142147196Smarkus
143147196Smarkus	loadaddress = ppm->ppm_lowpc + image->pi_vaddr - image->pi_start;
144147196Smarkus	pc -= loadaddress;	/* Convert to an offset in the image. */
145147196Smarkus
146147196Smarkus	/*
147148710Smarkus	 * Try determine the function at this offset.  If we can't
148148710Smarkus	 * find a function round leave the `pc' value alone.
149147196Smarkus	 */
150148710Smarkus	if ((sym = pmcstat_symbol_search(image, pc)) != NULL)
151148710Smarkus		pc = sym->ps_start;
152147196Smarkus	else
153147196Smarkus		pmcstat_stats.ps_samples_unknown_function++;
154147196Smarkus
155147196Smarkus	for (hash = i = 0; i < sizeof(uintfptr_t); i++)
156147196Smarkus		hash += (pc >> i) & 0xFF;
157147196Smarkus
158147196Smarkus	hash &= PMCSTAT_HASH_MASK;
159147196Smarkus
160147196Smarkus	cg = NULL;
161147196Smarkus	LIST_FOREACH(h, &pmcstat_cgnode_hash[hash], pch_next)
162147196Smarkus	{
163138627Stakawata		if (h->pch_pmcid != pmcid)
164138627Stakawata			continue;
165147196Smarkus
166147196Smarkus		cg = h->pch_cgnode;
167147196Smarkus
168147196Smarkus		assert(cg != NULL);
169147196Smarkus
170147196Smarkus		if (cg->pcg_image == image && cg->pcg_func == pc)
171147196Smarkus			return (cg);
172147196Smarkus	}
173147196Smarkus
174147196Smarkus	/*
175147196Smarkus	 * We haven't seen this (pmcid, pc) tuple yet, so allocate a
176147196Smarkus	 * new callgraph node and a new hash table entry for it.
177147196Smarkus	 */
178147196Smarkus	cg = pmcstat_cgnode_allocate(image, pc);
179147196Smarkus	if ((h = malloc(sizeof(*h))) == NULL)
180147196Smarkus		err(EX_OSERR, "ERROR: Could not allocate callgraph node");
181147196Smarkus
182147196Smarkus	h->pch_pmcid = pmcid;
183147196Smarkus	h->pch_cgnode = cg;
184147196Smarkus	LIST_INSERT_HEAD(&pmcstat_cgnode_hash[hash], h, pch_next);
185147196Smarkus
186147196Smarkus	pmcstat_cgnode_hash_count++;
187147196Smarkus
188147196Smarkus	return (cg);
189147196Smarkus}
190147196Smarkus
191147196Smarkus/*
192147196Smarkus * Compare two callgraph nodes for sorting.
193147196Smarkus */
194147196Smarkusstatic int
195147196Smarkuspmcstat_cgnode_compare(const void *a, const void *b)
196147196Smarkus{
197147196Smarkus	const struct pmcstat_cgnode *const *pcg1, *const *pcg2, *cg1, *cg2;
198147196Smarkus
199147196Smarkus	pcg1 = (const struct pmcstat_cgnode *const *) a;
200147196Smarkus	cg1 = *pcg1;
201147196Smarkus	pcg2 = (const struct pmcstat_cgnode *const *) b;
202147196Smarkus	cg2 = *pcg2;
203147196Smarkus
204147196Smarkus	/* Sort in reverse order */
205147196Smarkus	if (cg1->pcg_count < cg2->pcg_count)
206147196Smarkus		return (1);
207147196Smarkus	if (cg1->pcg_count > cg2->pcg_count)
208147196Smarkus		return (-1);
209147196Smarkus	return (0);
210147196Smarkus}
211147196Smarkus
212147196Smarkus/*
213147196Smarkus * Find (allocating if a needed) a callgraph node in the given
214147196Smarkus * parent with the same (image, pcoffset) pair.
215147196Smarkus */
216147196Smarkus
217147196Smarkusstatic struct pmcstat_cgnode *
218147196Smarkuspmcstat_cgnode_find(struct pmcstat_cgnode *parent, struct pmcstat_image *image,
219147196Smarkus    uintfptr_t pcoffset)
220147196Smarkus{
221147196Smarkus	struct pmcstat_cgnode *child;
222147196Smarkus
223147196Smarkus	LIST_FOREACH(child, &parent->pcg_children, pcg_sibling) {
224147196Smarkus		if (child->pcg_image == image &&
225147196Smarkus		    child->pcg_func == pcoffset)
226147196Smarkus			return (child);
227147196Smarkus	}
228147196Smarkus
229147196Smarkus	/*
230147196Smarkus	 * Allocate a new structure.
231147196Smarkus	 */
232147196Smarkus
233147196Smarkus	child = pmcstat_cgnode_allocate(image, pcoffset);
234147196Smarkus
235147196Smarkus	/*
236147196Smarkus	 * Link it into the parent.
237147196Smarkus	 */
238147196Smarkus	LIST_INSERT_HEAD(&parent->pcg_children, child, pcg_sibling);
239147196Smarkus	parent->pcg_nchildren++;
240147196Smarkus
241147196Smarkus	return (child);
242147196Smarkus}
243138627Stakawata
244138627Stakawata/*
245138627Stakawata * Print one callgraph node.  The output format is:
246138627Stakawata *
247148710Smarkus * indentation %(parent's samples) #nsamples function@object
248148710Smarkus */
249148710Smarkusstatic void
250147196Smarkuspmcstat_cgnode_print(struct pmcstat_cgnode *cg, int depth, uint32_t total)
251147196Smarkus{
252147196Smarkus	uint32_t n;
253147196Smarkus	const char *space;
254147196Smarkus	struct pmcstat_symbol *sym;
255147196Smarkus	struct pmcstat_cgnode **sortbuffer, **cgn, *pcg;
256147196Smarkus
257147196Smarkus	space = " ";
258147196Smarkus
259138627Stakawata	if (depth > 0)
260138627Stakawata		(void) fprintf(args.pa_graphfile, "%*s", depth, space);
261138627Stakawata
262138627Stakawata	if (cg->pcg_count == total)
263138627Stakawata		(void) fprintf(args.pa_graphfile, "100.0%% ");
264138627Stakawata	else
265138627Stakawata		(void) fprintf(args.pa_graphfile, "%05.2f%% ",
266138627Stakawata		    100.0 * cg->pcg_count / total);
267138627Stakawata
268138627Stakawata	n = fprintf(args.pa_graphfile, " [%u] ", cg->pcg_count);
269138627Stakawata
270138627Stakawata	/* #samples is a 12 character wide field. */
271138627Stakawata	if (n < 12)
272138627Stakawata		(void) fprintf(args.pa_graphfile, "%*s", 12 - n, space);
273138627Stakawata
274138627Stakawata	if (depth > 0)
275138627Stakawata		(void) fprintf(args.pa_graphfile, "%*s", depth, space);
276138627Stakawata
277138627Stakawata	sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func);
278138627Stakawata	if (sym)
279147468Scracauer		(void) fprintf(args.pa_graphfile, "%s",
280138627Stakawata		    pmcstat_string_unintern(sym->ps_name));
281147196Smarkus	else
282147196Smarkus		(void) fprintf(args.pa_graphfile, "%p",
283147196Smarkus		    (void *) (cg->pcg_image->pi_vaddr + cg->pcg_func));
284148710Smarkus
285148710Smarkus	if (pmcstat_previous_filename_printed !=
286148710Smarkus	    cg->pcg_image->pi_fullpath) {
287148710Smarkus		pmcstat_previous_filename_printed = cg->pcg_image->pi_fullpath;
288148710Smarkus		(void) fprintf(args.pa_graphfile, " @ %s\n",
289148710Smarkus		    pmcstat_string_unintern(
290148710Smarkus		    pmcstat_previous_filename_printed));
291148710Smarkus	} else
292148710Smarkus		(void) fprintf(args.pa_graphfile, "\n");
293148710Smarkus
294148710Smarkus	if (cg->pcg_nchildren == 0)
295148710Smarkus		return;
296148710Smarkus
297148710Smarkus	if ((sortbuffer = (struct pmcstat_cgnode **)
298148710Smarkus		malloc(sizeof(struct pmcstat_cgnode *) *
299148710Smarkus		    cg->pcg_nchildren)) == NULL)
300148710Smarkus		err(EX_OSERR, "ERROR: Cannot print callgraph");
301148710Smarkus	cgn = sortbuffer;
302148710Smarkus
303147196Smarkus	LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling)
304148710Smarkus	    *cgn++ = pcg;
305147196Smarkus
306148710Smarkus	assert(cgn - sortbuffer == (int) cg->pcg_nchildren);
307148710Smarkus
308147196Smarkus	qsort(sortbuffer, cg->pcg_nchildren, sizeof(struct pmcstat_cgnode *),
309147196Smarkus	    pmcstat_cgnode_compare);
310138627Stakawata
311138627Stakawata	for (cgn = sortbuffer, n = 0; n < cg->pcg_nchildren; n++, cgn++)
312138627Stakawata		pmcstat_cgnode_print(*cgn, depth+1, cg->pcg_count);
313147196Smarkus
314147196Smarkus	free(sortbuffer);
315147196Smarkus}
316147196Smarkus
317138627Stakawata/*
318147196Smarkus * Record a callchain.
319138627Stakawata */
320147196Smarkus
321138627Stakawatavoid
322138627Stakawatapmcpl_cg_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr,
323138627Stakawata    uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu)
324147196Smarkus{
325138627Stakawata	uintfptr_t pc, loadaddress;
326147196Smarkus	uint32_t n;
327147196Smarkus	struct pmcstat_image *image;
328147196Smarkus	struct pmcstat_pcmap *ppm;
329138627Stakawata	struct pmcstat_symbol *sym;
330138627Stakawata	struct pmcstat_cgnode *parent, *child;
331138627Stakawata	struct pmcstat_process *km;
332138627Stakawata	pmc_id_t pmcid;
333147196Smarkus
334147196Smarkus	(void) cpu;
335147196Smarkus
336147196Smarkus	/*
337147196Smarkus	 * Find the callgraph node recorded in the global hash table
338147196Smarkus	 * for this (pmcid, pc).
339147196Smarkus	 */
340147196Smarkus
341147196Smarkus	pc = cc[0];
342147196Smarkus	pmcid = pmcr->pr_pmcid;
343138627Stakawata	parent = pmcstat_cgnode_hash_lookup_pc(pp, pmcid, pc, usermode);
344147196Smarkus	if (parent == NULL) {
345147196Smarkus		pmcstat_stats.ps_callchain_dubious_frames++;
346147196Smarkus		pmcr->pr_dubious_frames++;
347147196Smarkus		return;
348147196Smarkus	}
349147196Smarkus
350138627Stakawata	parent->pcg_count++;
351147196Smarkus
352138627Stakawata	/*
353147196Smarkus	 * For each return address in the call chain record, subject
354147196Smarkus	 * to the maximum depth desired.
355147196Smarkus	 * - Find the image associated with the sample.  Stop if there
356138627Stakawata	 *   there is no valid image at that address.
357147196Smarkus	 * - Find the function that overlaps the return address.
358147196Smarkus	 * - If found: use the start address of the function.
359147196Smarkus	 *   If not found (say an object's symbol table is not present or
360138627Stakawata	 *   is incomplete), round down to th gprof bucket granularity.
361147196Smarkus	 * - Convert return virtual address to an offset in the image.
362147196Smarkus	 * - Look for a child with the same {offset,image} tuple,
363147196Smarkus	 *   inserting one if needed.
364147196Smarkus	 * - Increment the count of occurrences of the child.
365147196Smarkus	 */
366138627Stakawata	km = pmcstat_kernproc;
367147196Smarkus
368147196Smarkus	for (n = 1; n < (uint32_t) args.pa_graphdepth && n < nsamples; n++,
369147196Smarkus	    parent = child) {
370147196Smarkus		pc = cc[n];
371138627Stakawata
372147196Smarkus		ppm = pmcstat_process_find_map(usermode ? pp : km, pc);
373147196Smarkus		if (ppm == NULL) {
374147196Smarkus			/* Detect full frame capture (kernel + user). */
375147196Smarkus			if (!usermode) {
376147196Smarkus				ppm = pmcstat_process_find_map(pp, pc);
377138627Stakawata				if (ppm != NULL)
378147196Smarkus					km = pp;
379147196Smarkus			}
380147196Smarkus		}
381147196Smarkus		if (ppm == NULL)
382147196Smarkus			return;
383147196Smarkus
384147196Smarkus		image = ppm->ppm_image;
385147196Smarkus		loadaddress = ppm->ppm_lowpc + image->pi_vaddr -
386147196Smarkus		    image->pi_start;
387147196Smarkus		pc -= loadaddress;
388138627Stakawata
389147196Smarkus		if ((sym = pmcstat_symbol_search(image, pc)) != NULL)
390147196Smarkus			pc = sym->ps_start;
391147196Smarkus
392147196Smarkus		child = pmcstat_cgnode_find(parent, image, pc);
393147196Smarkus		child->pcg_count++;
394147196Smarkus	}
395147196Smarkus}
396147196Smarkus
397147196Smarkus/*
398147196Smarkus * Printing a callgraph for a PMC.
399147196Smarkus */
400147196Smarkusstatic void
401147196Smarkuspmcstat_callgraph_print_for_pmcid(struct pmcstat_pmcrecord *pmcr)
402147196Smarkus{
403147196Smarkus	int n, nentries;
404147196Smarkus	uint32_t nsamples;
405147196Smarkus	pmc_id_t pmcid;
406147246Smarkus	struct pmcstat_cgnode **sortbuffer, **cgn;
407147246Smarkus	struct pmcstat_cgnode_hash *pch;
408147196Smarkus
409147196Smarkus	/*
410138627Stakawata	 * We pull out all callgraph nodes in the top-level hash table
411138627Stakawata	 * with a matching PMC id.  We then sort these based on the
412138627Stakawata	 * frequency of occurrence.  Each callgraph node is then
413138627Stakawata	 * printed.
414138627Stakawata	 */
415138774Sscottl
416138774Sscottl	nsamples = 0;
417138627Stakawata	pmcid = pmcr->pr_pmcid;
418138627Stakawata	if ((sortbuffer = (struct pmcstat_cgnode **)
419147196Smarkus	    malloc(sizeof(struct pmcstat_cgnode *) *
420147196Smarkus	    pmcstat_cgnode_hash_count)) == NULL)
421147196Smarkus		err(EX_OSERR, "ERROR: Cannot sort callgraph");
422147196Smarkus	cgn = sortbuffer;
423147196Smarkus
424147196Smarkus	for (n = 0; n < PMCSTAT_NHASH; n++)
425147196Smarkus		LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next)
426147196Smarkus		    if (pch->pch_pmcid == pmcid) {
427147246Smarkus			    nsamples += pch->pch_cgnode->pcg_count;
428147246Smarkus			    *cgn++ = pch->pch_cgnode;
429147246Smarkus		    }
430147196Smarkus
431138627Stakawata	nentries = cgn - sortbuffer;
432147196Smarkus	assert(nentries <= pmcstat_cgnode_hash_count);
433138627Stakawata
434147196Smarkus	if (nentries == 0) {
435138627Stakawata		free(sortbuffer);
436147196Smarkus		return;
437147196Smarkus	}
438147196Smarkus
439147196Smarkus	qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *),
440147196Smarkus	    pmcstat_cgnode_compare);
441147196Smarkus
442147196Smarkus	(void) fprintf(args.pa_graphfile,
443147196Smarkus	    "@ %s [%u samples]\n\n",
444147196Smarkus	    pmcstat_string_unintern(pmcr->pr_pmcname),
445147196Smarkus	    nsamples);
446147196Smarkus
447147196Smarkus	for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) {
448147196Smarkus		pmcstat_previous_filename_printed = NULL;
449147196Smarkus		pmcstat_cgnode_print(*cgn, 0, nsamples);
450147196Smarkus		(void) fprintf(args.pa_graphfile, "\n");
451147196Smarkus	}
452147196Smarkus
453147196Smarkus	free(sortbuffer);
454147196Smarkus}
455147196Smarkus
456147196Smarkus/*
457147196Smarkus * Print out callgraphs.
458147196Smarkus */
459138627Stakawata
460138627Stakawatastatic void
461138627Stakawatapmcstat_callgraph_print(void)
462147196Smarkus{
463138627Stakawata	struct pmcstat_pmcrecord *pmcr;
464147196Smarkus
465147196Smarkus	LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next)
466147196Smarkus	    pmcstat_callgraph_print_for_pmcid(pmcr);
467147196Smarkus}
468147196Smarkus
469147196Smarkusstatic void
470147196Smarkuspmcstat_cgnode_topprint(struct pmcstat_cgnode *cg,
471147196Smarkus    int depth, uint32_t nsamples)
472147196Smarkus{
473147196Smarkus	int v_attrs, vs_len, ns_len, width, len, n, nchildren;
474147196Smarkus	float v;
475147196Smarkus	char ns[30], vs[10];
476147196Smarkus	struct pmcstat_symbol *sym;
477147196Smarkus	struct pmcstat_cgnode **sortbuffer, **cgn, *pcg;
478147196Smarkus
479147196Smarkus	(void) depth;
480147196Smarkus
481147196Smarkus	/* Format value. */
482147196Smarkus	v = PMCPL_CG_COUNTP(cg);
483147196Smarkus	snprintf(vs, sizeof(vs), "%.1f", v);
484147196Smarkus	v_attrs = PMCSTAT_ATTRPERCENT(v);
485147196Smarkus
486147196Smarkus	/* Format name. */
487147196Smarkus	sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func);
488147196Smarkus	if (sym != NULL) {
489147196Smarkus		snprintf(ns, sizeof(ns), "%s",
490138627Stakawata		    pmcstat_string_unintern(sym->ps_name));
491147196Smarkus	} else
492147196Smarkus		snprintf(ns, sizeof(ns), "%p",
493147196Smarkus		    (void *)cg->pcg_func);
494138627Stakawata
495147196Smarkus	PMCSTAT_ATTRON(v_attrs);
496147196Smarkus	PMCSTAT_PRINTW("%5.5s", vs);
497138627Stakawata	PMCSTAT_ATTROFF(v_attrs);
498147196Smarkus	PMCSTAT_PRINTW(" %-10.10s %-20.20s",
499147196Smarkus	    pmcstat_string_unintern(cg->pcg_image->pi_name),
500138627Stakawata	    ns);
501147196Smarkus
502147196Smarkus	nchildren = cg->pcg_nchildren;
503147196Smarkus	if (nchildren == 0) {
504147196Smarkus		PMCSTAT_PRINTW("\n");
505138627Stakawata		return;
506147196Smarkus	}
507147196Smarkus
508147196Smarkus	width = pmcstat_displaywidth - 40;
509147196Smarkus
510147196Smarkus	if ((sortbuffer = (struct pmcstat_cgnode **)
511147196Smarkus		malloc(sizeof(struct pmcstat_cgnode *) *
512147196Smarkus		    nchildren)) == NULL)
513147196Smarkus		err(EX_OSERR, "ERROR: Cannot print callgraph");
514147196Smarkus	cgn = sortbuffer;
515147196Smarkus
516147196Smarkus	LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling)
517147196Smarkus	    *cgn++ = pcg;
518147196Smarkus
519147196Smarkus	assert(cgn - sortbuffer == (int)nchildren);
520147196Smarkus
521147196Smarkus	qsort(sortbuffer, nchildren, sizeof(struct pmcstat_cgnode *),
522147196Smarkus	    pmcstat_cgnode_compare);
523147196Smarkus
524147196Smarkus	/* Count how many callers. */
525147196Smarkus	for (cgn = sortbuffer, n = 0; n < nchildren; n++, cgn++) {
526147196Smarkus		pcg = *cgn;
527147196Smarkus
528147196Smarkus		v = PMCPL_CG_COUNTP(pcg);
529147196Smarkus		if (v < pmcstat_threshold)
530147196Smarkus			break;
531147196Smarkus	}
532147196Smarkus	nchildren = n;
533147196Smarkus
534147196Smarkus	for (cgn = sortbuffer, n = 0; n < nchildren; n++, cgn++) {
535147196Smarkus		pcg = *cgn;
536147196Smarkus
537147196Smarkus		/* Format value. */
538147196Smarkus		if (nchildren > 1) {
539147196Smarkus			v = PMCPL_CG_COUNTP(pcg);
540147196Smarkus			vs_len = snprintf(vs, sizeof(vs), ":%.1f", v);
541147196Smarkus			v_attrs = PMCSTAT_ATTRPERCENT(v);
542147196Smarkus		} else
543147196Smarkus			vs_len = 0;
544147196Smarkus
545147196Smarkus		/* Format name. */
546147196Smarkus		sym = pmcstat_symbol_search(pcg->pcg_image, pcg->pcg_func);
547147196Smarkus		if (sym != NULL) {
548147196Smarkus			ns_len = snprintf(ns, sizeof(ns), "%s",
549147196Smarkus			    pmcstat_string_unintern(sym->ps_name));
550147196Smarkus		} else
551147196Smarkus			ns_len = snprintf(ns, sizeof(ns), "%p",
552147196Smarkus			    (void *)pcg->pcg_func);
553147196Smarkus
554147196Smarkus		len = ns_len + vs_len + 1;
555147246Smarkus		if (width - len < 0) {
556147196Smarkus			PMCSTAT_PRINTW(" ...");
557147196Smarkus			break;
558147196Smarkus		}
559147196Smarkus		width -= len;
560147196Smarkus
561147196Smarkus		PMCSTAT_PRINTW(" %s", ns);
562147196Smarkus		if (nchildren > 1) {
563147196Smarkus			PMCSTAT_ATTRON(v_attrs);
564147196Smarkus			PMCSTAT_PRINTW("%s", vs);
565147196Smarkus			PMCSTAT_ATTROFF(v_attrs);
566147196Smarkus		}
567147196Smarkus	}
568147196Smarkus	PMCSTAT_PRINTW("\n");
569147196Smarkus	free(sortbuffer);
570147196Smarkus}
571147196Smarkus
572147196Smarkus/*
573147196Smarkus * Top mode display.
574147196Smarkus */
575147196Smarkus
576147196Smarkusvoid
577147196Smarkuspmcpl_cg_topdisplay(void)
578147196Smarkus{
579147196Smarkus	int n, nentries;
580147196Smarkus	uint32_t nsamples;
581147196Smarkus	struct pmcstat_cgnode **sortbuffer, **cgn;
582147196Smarkus	struct pmcstat_cgnode_hash *pch;
583147196Smarkus	struct pmcstat_pmcrecord *pmcr;
584147196Smarkus
585147196Smarkus	pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter);
586138627Stakawata	if (!pmcr)
587147196Smarkus		err(EX_SOFTWARE, "ERROR: invalid pmcindex");
588147196Smarkus
589147196Smarkus	/*
590147196Smarkus	 * We pull out all callgraph nodes in the top-level hash table
591147196Smarkus	 * with a matching PMC index.  We then sort these based on the
592138627Stakawata	 * frequency of occurrence.  Each callgraph node is then
593147196Smarkus	 * printed.
594147196Smarkus	 */
595147196Smarkus
596147196Smarkus	nsamples = 0;
597138627Stakawata
598147196Smarkus	if ((sortbuffer = (struct pmcstat_cgnode **)
599147196Smarkus	    malloc(sizeof(struct pmcstat_cgnode *) *
600147196Smarkus	    pmcstat_cgnode_hash_count)) == NULL)
601138627Stakawata		err(EX_OSERR, "ERROR: Cannot sort callgraph");
602147196Smarkus	cgn = sortbuffer;
603147196Smarkus
604138627Stakawata	for (n = 0; n < PMCSTAT_NHASH; n++)
605138627Stakawata		LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next)
606138627Stakawata		    if (pmcr == NULL || pch->pch_pmcid == pmcr->pr_pmcid) {
607147196Smarkus			    nsamples += pch->pch_cgnode->pcg_count;
608138627Stakawata			    *cgn++ = pch->pch_cgnode;
609147196Smarkus		    }
610147196Smarkus
611147196Smarkus	nentries = cgn - sortbuffer;
612147196Smarkus	assert(nentries <= pmcstat_cgnode_hash_count);
613147196Smarkus
614138627Stakawata	if (nentries == 0) {
615147196Smarkus		free(sortbuffer);
616147196Smarkus		return;
617138627Stakawata	}
618147196Smarkus
619147196Smarkus	qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *),
620147196Smarkus	    pmcstat_cgnode_compare);
621147196Smarkus
622138627Stakawata	PMCSTAT_PRINTW("%5.5s %-10.10s %-20.20s %s\n",
623147196Smarkus	    "%SAMP", "IMAGE", "FUNCTION", "CALLERS");
624147196Smarkus
625147196Smarkus	nentries = min(pmcstat_displayheight - 2, nentries);
626147196Smarkus
627147196Smarkus	for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) {
628147196Smarkus		if (PMCPL_CG_COUNTP(*cgn) < pmcstat_threshold)
629138627Stakawata			break;
630147196Smarkus		pmcstat_cgnode_topprint(*cgn, 0, nsamples);
631147196Smarkus	}
632147196Smarkus
633147196Smarkus	free(sortbuffer);
634138627Stakawata}
635147196Smarkus
636147196Smarkus/*
637147196Smarkus * Handle top mode keypress.
638138627Stakawata */
639147196Smarkus
640147196Smarkusint
641147196Smarkuspmcpl_cg_topkeypress(int c, WINDOW *w)
642147196Smarkus{
643147196Smarkus
644147196Smarkus	(void) c; (void) w;
645138627Stakawata
646147196Smarkus	return 0;
647147196Smarkus}
648147196Smarkus
649147196Smarkusint
650138627Stakawatapmcpl_cg_init(void)
651147196Smarkus{
652147196Smarkus	int i;
653147196Smarkus
654147196Smarkus	pmcstat_cgnode_hash_count = 0;
655147196Smarkus	pmcstat_previous_filename_printed = NULL;
656147196Smarkus
657147196Smarkus	for (i = 0; i < PMCSTAT_NHASH; i++) {
658147196Smarkus		LIST_INIT(&pmcstat_cgnode_hash[i]);
659147196Smarkus	}
660138627Stakawata
661147196Smarkus	return (0);
662147196Smarkus}
663147196Smarkus
664138627Stakawatavoid
665147246Smarkuspmcpl_cg_shutdown(FILE *mf)
666147246Smarkus{
667147246Smarkus	int i;
668147246Smarkus	struct pmcstat_cgnode_hash *pch, *pchtmp;
669147196Smarkus
670147196Smarkus	(void) mf;
671138627Stakawata
672147196Smarkus	if (args.pa_flags & FLAG_DO_CALLGRAPHS)
673147196Smarkus		pmcstat_callgraph_print();
674147196Smarkus
675147196Smarkus	/*
676138627Stakawata	 * Free memory.
677147196Smarkus	 */
678147196Smarkus	for (i = 0; i < PMCSTAT_NHASH; i++) {
679147196Smarkus		LIST_FOREACH_SAFE(pch, &pmcstat_cgnode_hash[i], pch_next,
680147196Smarkus		    pchtmp) {
681147196Smarkus			pmcstat_cgnode_free(pch->pch_cgnode);
682147196Smarkus			LIST_REMOVE(pch, pch_next);
683147196Smarkus			free(pch);
684147196Smarkus		}
685147196Smarkus	}
686138627Stakawata}
687147196Smarkus
688147196Smarkus