xref: /freebsd-src/usr.sbin/pmcstat/pmcpl_calltree.c (revision 4d65a7c6951cea0333f1a0c1b32c38489cdfa6c5)
10b86b1bbSFabien Thomas /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4fa18b0b2SFabien Thomas  * Copyright (c) 2012, Fabien Thomas
50b86b1bbSFabien Thomas  * All rights reserved.
60b86b1bbSFabien Thomas  *
70b86b1bbSFabien Thomas  * Redistribution and use in source and binary forms, with or without
80b86b1bbSFabien Thomas  * modification, are permitted provided that the following conditions
90b86b1bbSFabien Thomas  * are met:
100b86b1bbSFabien Thomas  * 1. Redistributions of source code must retain the above copyright
110b86b1bbSFabien Thomas  *    notice, this list of conditions and the following disclaimer.
120b86b1bbSFabien Thomas  * 2. Redistributions in binary form must reproduce the above copyright
130b86b1bbSFabien Thomas  *    notice, this list of conditions and the following disclaimer in the
140b86b1bbSFabien Thomas  *    documentation and/or other materials provided with the distribution.
150b86b1bbSFabien Thomas  *
160b86b1bbSFabien Thomas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
170b86b1bbSFabien Thomas  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180b86b1bbSFabien Thomas  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190b86b1bbSFabien Thomas  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
200b86b1bbSFabien Thomas  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
210b86b1bbSFabien Thomas  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
220b86b1bbSFabien Thomas  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
230b86b1bbSFabien Thomas  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
240b86b1bbSFabien Thomas  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250b86b1bbSFabien Thomas  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260b86b1bbSFabien Thomas  * SUCH DAMAGE.
270b86b1bbSFabien Thomas  */
280b86b1bbSFabien Thomas 
290b86b1bbSFabien Thomas /*
300b86b1bbSFabien Thomas  * Process hwpmc(4) samples as calltree.
310b86b1bbSFabien Thomas  *
320b86b1bbSFabien Thomas  * Output file format compatible with Kcachegrind (kdesdk).
330b86b1bbSFabien Thomas  * Handle top mode with a sorted tree display.
340b86b1bbSFabien Thomas  */
350b86b1bbSFabien Thomas 
360b86b1bbSFabien Thomas #include <sys/param.h>
370b86b1bbSFabien Thomas #include <sys/endian.h>
380b86b1bbSFabien Thomas #include <sys/queue.h>
390b86b1bbSFabien Thomas 
400b86b1bbSFabien Thomas #include <assert.h>
410b86b1bbSFabien Thomas #include <curses.h>
420b86b1bbSFabien Thomas #include <ctype.h>
430b86b1bbSFabien Thomas #include <err.h>
440b86b1bbSFabien Thomas #include <errno.h>
450b86b1bbSFabien Thomas #include <fcntl.h>
460b86b1bbSFabien Thomas #include <pmc.h>
470b86b1bbSFabien Thomas #include <pmclog.h>
480b86b1bbSFabien Thomas #include <stdint.h>
490b86b1bbSFabien Thomas #include <stdio.h>
500b86b1bbSFabien Thomas #include <stdlib.h>
510b86b1bbSFabien Thomas #include <string.h>
520b86b1bbSFabien Thomas #include <unistd.h>
530b86b1bbSFabien Thomas #include <sysexits.h>
540b86b1bbSFabien Thomas 
550b86b1bbSFabien Thomas #include "pmcstat.h"
560b86b1bbSFabien Thomas #include "pmcstat_log.h"
570b86b1bbSFabien Thomas #include "pmcstat_top.h"
580b86b1bbSFabien Thomas #include "pmcpl_calltree.h"
590b86b1bbSFabien Thomas 
60d27927f7SRuslan Bukin #define	min(A,B)		((A) < (B) ? (A) : (B))
61d27927f7SRuslan Bukin #define	max(A,B)		((A) > (B) ? (A) : (B))
62d27927f7SRuslan Bukin 
630b86b1bbSFabien Thomas #define	PMCPL_CT_GROWSIZE	4
640b86b1bbSFabien Thomas 
650b86b1bbSFabien Thomas static int pmcstat_skiplink = 0;
660b86b1bbSFabien Thomas 
670b86b1bbSFabien Thomas struct pmcpl_ct_node;
680b86b1bbSFabien Thomas 
690b86b1bbSFabien Thomas /* Get the sample value for PMC a. */
700b86b1bbSFabien Thomas #define	PMCPL_CT_SAMPLE(a, b) \
710b86b1bbSFabien Thomas 	((a) < (b)->npmcs ? (b)->sb[a] : 0)
720b86b1bbSFabien Thomas 
730b86b1bbSFabien Thomas /* Get the sample value in percent related to rsamples. */
740b86b1bbSFabien Thomas #define	PMCPL_CT_SAMPLEP(a, b) \
750b86b1bbSFabien Thomas 	(PMCPL_CT_SAMPLE(a, b) * 100.0 / rsamples->sb[a])
760b86b1bbSFabien Thomas 
770b86b1bbSFabien Thomas struct pmcpl_ct_sample {
780b86b1bbSFabien Thomas 	int		npmcs;		/* Max pmc index available. */
790b86b1bbSFabien Thomas 	unsigned	*sb;		/* Sample buffer for 0..npmcs. */
800b86b1bbSFabien Thomas };
810b86b1bbSFabien Thomas 
820b86b1bbSFabien Thomas struct pmcpl_ct_arc {
830b86b1bbSFabien Thomas 	struct pmcpl_ct_sample	pcta_samples;
840b86b1bbSFabien Thomas 	struct pmcpl_ct_sample	pcta_callid;
850b86b1bbSFabien Thomas 	unsigned		pcta_call;
860b86b1bbSFabien Thomas 	struct pmcpl_ct_node	*pcta_child;
870b86b1bbSFabien Thomas };
880b86b1bbSFabien Thomas 
890b86b1bbSFabien Thomas struct pmcpl_ct_instr {
900b86b1bbSFabien Thomas 	uintfptr_t		pctf_func;
910b86b1bbSFabien Thomas 	struct pmcpl_ct_sample	pctf_samples;
920b86b1bbSFabien Thomas };
930b86b1bbSFabien Thomas 
940b86b1bbSFabien Thomas /*
950b86b1bbSFabien Thomas  * Each calltree node is tracked by a pmcpl_ct_node struct.
960b86b1bbSFabien Thomas  */
970b86b1bbSFabien Thomas struct pmcpl_ct_node {
980b86b1bbSFabien Thomas 	struct pmcstat_image	*pct_image;
990b86b1bbSFabien Thomas 	uintfptr_t		pct_func;
100fa18b0b2SFabien Thomas 
101fa18b0b2SFabien Thomas 	struct pmcstat_symbol	*pct_sym;
102fa18b0b2SFabien Thomas 	pmcstat_interned_string	pct_ifl;
103fa18b0b2SFabien Thomas 	pmcstat_interned_string	pct_ifn;
104fa18b0b2SFabien Thomas 
1050b86b1bbSFabien Thomas 	struct pmcpl_ct_sample	pct_samples;
1060b86b1bbSFabien Thomas 
1070b86b1bbSFabien Thomas 	int			pct_narc;
1080b86b1bbSFabien Thomas 	int			pct_arc_c;
1090b86b1bbSFabien Thomas 	struct pmcpl_ct_arc 	*pct_arc;
1100b86b1bbSFabien Thomas 
1110b86b1bbSFabien Thomas 	/* TODO: optimize for large number of items. */
1120b86b1bbSFabien Thomas 	int			pct_ninstr;
1130b86b1bbSFabien Thomas 	int			pct_instr_c;
1140b86b1bbSFabien Thomas 	struct pmcpl_ct_instr	*pct_instr;
115fa18b0b2SFabien Thomas 
116fa18b0b2SFabien Thomas #define PMCPL_PCT_ADDR	0
117fa18b0b2SFabien Thomas #define PMCPL_PCT_NAME	1
118fa18b0b2SFabien Thomas 	char			pct_type;
119fa18b0b2SFabien Thomas #define	PMCPL_PCT_WHITE	0
120fa18b0b2SFabien Thomas #define	PMCPL_PCT_GREY	1
121fa18b0b2SFabien Thomas #define	PMCPL_PCT_BLACK	2
122fa18b0b2SFabien Thomas 	char			pct_color;
1230b86b1bbSFabien Thomas };
1240b86b1bbSFabien Thomas 
1250b86b1bbSFabien Thomas struct pmcpl_ct_node_hash {
1260b86b1bbSFabien Thomas 	struct pmcpl_ct_node  *pch_ctnode;
127fa18b0b2SFabien Thomas 	STAILQ_ENTRY(pmcpl_ct_node_hash) pch_next;
1280b86b1bbSFabien Thomas };
1290b86b1bbSFabien Thomas 
130bf70beceSEd Schouten static struct pmcpl_ct_sample pmcpl_ct_callid;
1310b86b1bbSFabien Thomas 
1320b86b1bbSFabien Thomas #define	PMCPL_CT_MAXCOL		PMC_CALLCHAIN_DEPTH_MAX
133782434abSFabien Thomas #define	PMCPL_CT_MAXLINE	1024	/* TODO: dynamic. */
134782434abSFabien Thomas 
135782434abSFabien Thomas struct pmcpl_ct_line {
136782434abSFabien Thomas 	unsigned	ln_sum;
137782434abSFabien Thomas 	unsigned	ln_index;
138782434abSFabien Thomas };
139782434abSFabien Thomas 
140bf70beceSEd Schouten static struct pmcpl_ct_line	pmcpl_ct_topmax[PMCPL_CT_MAXLINE+1];
141bf70beceSEd Schouten static struct pmcpl_ct_node
142fa18b0b2SFabien Thomas     *pmcpl_ct_topscreen[PMCPL_CT_MAXCOL+1][PMCPL_CT_MAXLINE+1];
1430b86b1bbSFabien Thomas 
1440b86b1bbSFabien Thomas /*
1450b86b1bbSFabien Thomas  * All nodes indexed by function/image name are placed in a hash table.
1460b86b1bbSFabien Thomas  */
147fa18b0b2SFabien Thomas static STAILQ_HEAD(,pmcpl_ct_node_hash) pmcpl_ct_node_hash[PMCSTAT_NHASH];
1480b86b1bbSFabien Thomas 
1490b86b1bbSFabien Thomas /*
1500b86b1bbSFabien Thomas  * Root node for the graph.
1510b86b1bbSFabien Thomas  */
1520b86b1bbSFabien Thomas static struct pmcpl_ct_node *pmcpl_ct_root;
1530b86b1bbSFabien Thomas 
1540b86b1bbSFabien Thomas /*
1550b86b1bbSFabien Thomas  * Prototypes
1560b86b1bbSFabien Thomas  */
1570b86b1bbSFabien Thomas 
1580b86b1bbSFabien Thomas /*
1590b86b1bbSFabien Thomas  * Initialize a samples.
1600b86b1bbSFabien Thomas  */
1610b86b1bbSFabien Thomas 
1620b86b1bbSFabien Thomas static void
pmcpl_ct_samples_init(struct pmcpl_ct_sample * samples)1630b86b1bbSFabien Thomas pmcpl_ct_samples_init(struct pmcpl_ct_sample *samples)
1640b86b1bbSFabien Thomas {
1650b86b1bbSFabien Thomas 
1660b86b1bbSFabien Thomas 	samples->npmcs = 0;
1670b86b1bbSFabien Thomas 	samples->sb = NULL;
1680b86b1bbSFabien Thomas }
1690b86b1bbSFabien Thomas 
1700b86b1bbSFabien Thomas /*
1710b86b1bbSFabien Thomas  * Free a samples.
1720b86b1bbSFabien Thomas  */
1730b86b1bbSFabien Thomas 
1740b86b1bbSFabien Thomas static void
pmcpl_ct_samples_free(struct pmcpl_ct_sample * samples)1750b86b1bbSFabien Thomas pmcpl_ct_samples_free(struct pmcpl_ct_sample *samples)
1760b86b1bbSFabien Thomas {
1770b86b1bbSFabien Thomas 
1780b86b1bbSFabien Thomas 	samples->npmcs = 0;
1790b86b1bbSFabien Thomas 	free(samples->sb);
1800b86b1bbSFabien Thomas 	samples->sb = NULL;
1810b86b1bbSFabien Thomas }
1820b86b1bbSFabien Thomas 
1830b86b1bbSFabien Thomas /*
1840b86b1bbSFabien Thomas  * Grow a sample block to store pmcstat_npmcs PMCs.
1850b86b1bbSFabien Thomas  */
1860b86b1bbSFabien Thomas 
1870b86b1bbSFabien Thomas static void
pmcpl_ct_samples_grow(struct pmcpl_ct_sample * samples)1880b86b1bbSFabien Thomas pmcpl_ct_samples_grow(struct pmcpl_ct_sample *samples)
1890b86b1bbSFabien Thomas {
190780154cbSPedro F. Giffuni 	unsigned int npmcs;
1910b86b1bbSFabien Thomas 
1920b86b1bbSFabien Thomas 	/* Enough storage. */
1930b86b1bbSFabien Thomas 	if (pmcstat_npmcs <= samples->npmcs)
1940b86b1bbSFabien Thomas                 return;
1950b86b1bbSFabien Thomas 
1960b86b1bbSFabien Thomas 	npmcs = samples->npmcs +
1970b86b1bbSFabien Thomas 	    max(pmcstat_npmcs - samples->npmcs, PMCPL_CT_GROWSIZE);
198780154cbSPedro F. Giffuni 	samples->sb = reallocarray(samples->sb, npmcs, sizeof(unsigned));
1990b86b1bbSFabien Thomas 	if (samples->sb == NULL)
2000b86b1bbSFabien Thomas 		errx(EX_SOFTWARE, "ERROR: out of memory");
2010b86b1bbSFabien Thomas 	bzero((char *)samples->sb + samples->npmcs * sizeof(unsigned),
2020b86b1bbSFabien Thomas 	    (npmcs - samples->npmcs) * sizeof(unsigned));
2030b86b1bbSFabien Thomas 	samples->npmcs = npmcs;
2040b86b1bbSFabien Thomas }
2050b86b1bbSFabien Thomas 
2060b86b1bbSFabien Thomas /*
2070b86b1bbSFabien Thomas  * Compute the sum of all root arcs.
2080b86b1bbSFabien Thomas  */
2090b86b1bbSFabien Thomas 
2100b86b1bbSFabien Thomas static void
pmcpl_ct_samples_root(struct pmcpl_ct_sample * samples)2110b86b1bbSFabien Thomas pmcpl_ct_samples_root(struct pmcpl_ct_sample *samples)
2120b86b1bbSFabien Thomas {
2130b86b1bbSFabien Thomas 	int i, pmcin;
2140b86b1bbSFabien Thomas 
2150b86b1bbSFabien Thomas 	pmcpl_ct_samples_init(samples);
2160b86b1bbSFabien Thomas 	pmcpl_ct_samples_grow(samples);
2170b86b1bbSFabien Thomas 
2180b86b1bbSFabien Thomas 	for (i = 0; i < pmcpl_ct_root->pct_narc; i++)
2190b86b1bbSFabien Thomas 		for (pmcin = 0; pmcin < pmcstat_npmcs; pmcin++)
2200b86b1bbSFabien Thomas 			samples->sb[pmcin] += PMCPL_CT_SAMPLE(pmcin,
2210b86b1bbSFabien Thomas 			    &pmcpl_ct_root->pct_arc[i].pcta_samples);
2220b86b1bbSFabien Thomas }
2230b86b1bbSFabien Thomas 
2240b86b1bbSFabien Thomas /*
2250b86b1bbSFabien Thomas  * Grow the arc table.
2260b86b1bbSFabien Thomas  */
2270b86b1bbSFabien Thomas 
2280b86b1bbSFabien Thomas static void
pmcpl_ct_arc_grow(int cursize,int * maxsize,struct pmcpl_ct_arc ** items)2290b86b1bbSFabien Thomas pmcpl_ct_arc_grow(int cursize, int *maxsize, struct pmcpl_ct_arc **items)
2300b86b1bbSFabien Thomas {
231780154cbSPedro F. Giffuni 	unsigned int nmaxsize;
2320b86b1bbSFabien Thomas 
2330b86b1bbSFabien Thomas 	if (cursize < *maxsize)
2340b86b1bbSFabien Thomas 		return;
2350b86b1bbSFabien Thomas 
2360b86b1bbSFabien Thomas 	nmaxsize = *maxsize + max(cursize + 1 - *maxsize, PMCPL_CT_GROWSIZE);
237780154cbSPedro F. Giffuni 	*items = reallocarray(*items, nmaxsize, sizeof(struct pmcpl_ct_arc));
2380b86b1bbSFabien Thomas 	if (*items == NULL)
2390b86b1bbSFabien Thomas 		errx(EX_SOFTWARE, "ERROR: out of memory");
2400b86b1bbSFabien Thomas 	bzero((char *)*items + *maxsize * sizeof(struct pmcpl_ct_arc),
2410b86b1bbSFabien Thomas 	    (nmaxsize - *maxsize) * sizeof(struct pmcpl_ct_arc));
2420b86b1bbSFabien Thomas 	*maxsize = nmaxsize;
2430b86b1bbSFabien Thomas }
2440b86b1bbSFabien Thomas 
2450b86b1bbSFabien Thomas /*
2460b86b1bbSFabien Thomas  * Grow the instr table.
2470b86b1bbSFabien Thomas  */
2480b86b1bbSFabien Thomas 
2490b86b1bbSFabien Thomas static void
pmcpl_ct_instr_grow(int cursize,int * maxsize,struct pmcpl_ct_instr ** items)2500b86b1bbSFabien Thomas pmcpl_ct_instr_grow(int cursize, int *maxsize, struct pmcpl_ct_instr **items)
2510b86b1bbSFabien Thomas {
252780154cbSPedro F. Giffuni 	unsigned int nmaxsize;
2530b86b1bbSFabien Thomas 
2540b86b1bbSFabien Thomas 	if (cursize < *maxsize)
2550b86b1bbSFabien Thomas 		return;
2560b86b1bbSFabien Thomas 
2570b86b1bbSFabien Thomas 	nmaxsize = *maxsize + max(cursize + 1 - *maxsize, PMCPL_CT_GROWSIZE);
258780154cbSPedro F. Giffuni 	*items = reallocarray(*items, nmaxsize, sizeof(struct pmcpl_ct_instr));
2590b86b1bbSFabien Thomas 	if (*items == NULL)
2600b86b1bbSFabien Thomas 		errx(EX_SOFTWARE, "ERROR: out of memory");
2610b86b1bbSFabien Thomas 	bzero((char *)*items + *maxsize * sizeof(struct pmcpl_ct_instr),
2620b86b1bbSFabien Thomas 	    (nmaxsize - *maxsize) * sizeof(struct pmcpl_ct_instr));
2630b86b1bbSFabien Thomas 	*maxsize = nmaxsize;
2640b86b1bbSFabien Thomas }
2650b86b1bbSFabien Thomas 
2660b86b1bbSFabien Thomas /*
2670b86b1bbSFabien Thomas  * Add a new instruction sample to given node.
2680b86b1bbSFabien Thomas  */
2690b86b1bbSFabien Thomas 
2700b86b1bbSFabien Thomas static void
pmcpl_ct_instr_add(struct pmcpl_ct_node * ct,int pmcin,uintfptr_t pc,unsigned v)271fa18b0b2SFabien Thomas pmcpl_ct_instr_add(struct pmcpl_ct_node *ct, int pmcin,
272fa18b0b2SFabien Thomas     uintfptr_t pc, unsigned v)
2730b86b1bbSFabien Thomas {
2740b86b1bbSFabien Thomas 	int i;
2750b86b1bbSFabien Thomas 	struct pmcpl_ct_instr *in;
2760b86b1bbSFabien Thomas 
2770b86b1bbSFabien Thomas 	for (i = 0; i<ct->pct_ninstr; i++) {
2780b86b1bbSFabien Thomas 		if (ct->pct_instr[i].pctf_func == pc) {
2790b86b1bbSFabien Thomas 			in = &ct->pct_instr[i];
2800b86b1bbSFabien Thomas 			pmcpl_ct_samples_grow(&in->pctf_samples);
281fa18b0b2SFabien Thomas 			in->pctf_samples.sb[pmcin] += v;
2820b86b1bbSFabien Thomas 			return;
2830b86b1bbSFabien Thomas 		}
2840b86b1bbSFabien Thomas 	}
2850b86b1bbSFabien Thomas 
2860b86b1bbSFabien Thomas 	pmcpl_ct_instr_grow(ct->pct_ninstr, &ct->pct_instr_c, &ct->pct_instr);
2870b86b1bbSFabien Thomas 	in = &ct->pct_instr[ct->pct_ninstr];
2880b86b1bbSFabien Thomas 	in->pctf_func = pc;
2890b86b1bbSFabien Thomas 	pmcpl_ct_samples_init(&in->pctf_samples);
2900b86b1bbSFabien Thomas 	pmcpl_ct_samples_grow(&in->pctf_samples);
291fa18b0b2SFabien Thomas 	in->pctf_samples.sb[pmcin] = v;
2920b86b1bbSFabien Thomas 	ct->pct_ninstr++;
2930b86b1bbSFabien Thomas }
2940b86b1bbSFabien Thomas 
2950b86b1bbSFabien Thomas /*
2960b86b1bbSFabien Thomas  * Allocate a new node.
2970b86b1bbSFabien Thomas  */
2980b86b1bbSFabien Thomas 
2990b86b1bbSFabien Thomas static struct pmcpl_ct_node *
pmcpl_ct_node_allocate(void)300fa18b0b2SFabien Thomas pmcpl_ct_node_allocate(void)
3010b86b1bbSFabien Thomas {
3020b86b1bbSFabien Thomas 	struct pmcpl_ct_node *ct;
3030b86b1bbSFabien Thomas 
3040b86b1bbSFabien Thomas 	if ((ct = malloc(sizeof(*ct))) == NULL)
3050b86b1bbSFabien Thomas 		err(EX_OSERR, "ERROR: Cannot allocate callgraph node");
3060b86b1bbSFabien Thomas 
3070b86b1bbSFabien Thomas 	pmcpl_ct_samples_init(&ct->pct_samples);
3080b86b1bbSFabien Thomas 
309fa18b0b2SFabien Thomas 	ct->pct_sym	= NULL;
310fa18b0b2SFabien Thomas 	ct->pct_image	= NULL;
311fa18b0b2SFabien Thomas 	ct->pct_func	= 0;
312fa18b0b2SFabien Thomas 
3130b86b1bbSFabien Thomas 	ct->pct_narc	= 0;
3140b86b1bbSFabien Thomas 	ct->pct_arc_c	= 0;
3150b86b1bbSFabien Thomas 	ct->pct_arc	= NULL;
3160b86b1bbSFabien Thomas 
3170b86b1bbSFabien Thomas 	ct->pct_ninstr	= 0;
3180b86b1bbSFabien Thomas 	ct->pct_instr_c	= 0;
3190b86b1bbSFabien Thomas 	ct->pct_instr	= NULL;
3200b86b1bbSFabien Thomas 
321fa18b0b2SFabien Thomas 	ct->pct_color   = PMCPL_PCT_WHITE;
322fa18b0b2SFabien Thomas 
3230b86b1bbSFabien Thomas 	return (ct);
3240b86b1bbSFabien Thomas }
3250b86b1bbSFabien Thomas 
3260b86b1bbSFabien Thomas /*
3270b86b1bbSFabien Thomas  * Free a node.
3280b86b1bbSFabien Thomas  */
3290b86b1bbSFabien Thomas 
3300b86b1bbSFabien Thomas static void
pmcpl_ct_node_free(struct pmcpl_ct_node * ct)3310b86b1bbSFabien Thomas pmcpl_ct_node_free(struct pmcpl_ct_node *ct)
3320b86b1bbSFabien Thomas {
3330b86b1bbSFabien Thomas 	int i;
3340b86b1bbSFabien Thomas 
3350b86b1bbSFabien Thomas 	for (i = 0; i < ct->pct_narc; i++) {
3360b86b1bbSFabien Thomas 		pmcpl_ct_samples_free(&ct->pct_arc[i].pcta_samples);
3370b86b1bbSFabien Thomas 		pmcpl_ct_samples_free(&ct->pct_arc[i].pcta_callid);
3380b86b1bbSFabien Thomas 	}
3390b86b1bbSFabien Thomas 
3400b86b1bbSFabien Thomas 	pmcpl_ct_samples_free(&ct->pct_samples);
3410b86b1bbSFabien Thomas 	free(ct->pct_arc);
3420b86b1bbSFabien Thomas 	free(ct->pct_instr);
3430b86b1bbSFabien Thomas 	free(ct);
3440b86b1bbSFabien Thomas }
3450b86b1bbSFabien Thomas 
3460b86b1bbSFabien Thomas /*
3470b86b1bbSFabien Thomas  * Clear the graph tag on each node.
3480b86b1bbSFabien Thomas  */
3490b86b1bbSFabien Thomas static void
pmcpl_ct_node_cleartag(void)3500b86b1bbSFabien Thomas pmcpl_ct_node_cleartag(void)
3510b86b1bbSFabien Thomas {
3520b86b1bbSFabien Thomas 	int i;
3530b86b1bbSFabien Thomas 	struct pmcpl_ct_node_hash *pch;
3540b86b1bbSFabien Thomas 
3550b86b1bbSFabien Thomas 	for (i = 0; i < PMCSTAT_NHASH; i++)
356fa18b0b2SFabien Thomas 		STAILQ_FOREACH(pch, &pmcpl_ct_node_hash[i], pch_next)
357fa18b0b2SFabien Thomas 			pch->pch_ctnode->pct_color = PMCPL_PCT_WHITE;
3580b86b1bbSFabien Thomas 
359fa18b0b2SFabien Thomas 	pmcpl_ct_root->pct_color = PMCPL_PCT_WHITE;
3600b86b1bbSFabien Thomas }
3610b86b1bbSFabien Thomas 
3620b86b1bbSFabien Thomas /*
3630b86b1bbSFabien Thomas  * Print the callchain line by line with maximum cost at top.
3640b86b1bbSFabien Thomas  */
3650b86b1bbSFabien Thomas 
3660b86b1bbSFabien Thomas static int
pmcpl_ct_node_dumptop(int pmcin,struct pmcpl_ct_node * ct,struct pmcpl_ct_sample * rsamples,int x,int * y)3670b86b1bbSFabien Thomas pmcpl_ct_node_dumptop(int pmcin, struct pmcpl_ct_node *ct,
368782434abSFabien Thomas     struct pmcpl_ct_sample *rsamples, int x, int *y)
3690b86b1bbSFabien Thomas {
370782434abSFabien Thomas 	int i, terminal;
37146514c77SFabien Thomas 	struct pmcpl_ct_arc *arc;
3720b86b1bbSFabien Thomas 
373fa18b0b2SFabien Thomas 	if (ct->pct_color == PMCPL_PCT_GREY)
3740b86b1bbSFabien Thomas 		return 0;
3750b86b1bbSFabien Thomas 
3760b86b1bbSFabien Thomas 	if (x >= PMCPL_CT_MAXCOL) {
3770b86b1bbSFabien Thomas 		pmcpl_ct_topscreen[x][*y] = NULL;
3780b86b1bbSFabien Thomas 		return 1;
3790b86b1bbSFabien Thomas 	}
3800b86b1bbSFabien Thomas 	pmcpl_ct_topscreen[x][*y] = ct;
3810b86b1bbSFabien Thomas 
3820b86b1bbSFabien Thomas 	/*
383782434abSFabien Thomas 	 * Check if this is a terminal node.
384782434abSFabien Thomas 	 * We need to check that some samples exist
385782434abSFabien Thomas 	 * for at least one arc for that PMC.
3860b86b1bbSFabien Thomas 	 */
387782434abSFabien Thomas 	terminal = 1;
38846514c77SFabien Thomas 	for (i = 0; i < ct->pct_narc; i++) {
38946514c77SFabien Thomas 		arc = &ct->pct_arc[i];
390fa18b0b2SFabien Thomas 		if (arc->pcta_child->pct_color != PMCPL_PCT_GREY &&
391fa18b0b2SFabien Thomas 		    PMCPL_CT_SAMPLE(pmcin,
39246514c77SFabien Thomas 		    &arc->pcta_samples) != 0 &&
39346514c77SFabien Thomas 		    PMCPL_CT_SAMPLEP(pmcin,
394fa18b0b2SFabien Thomas 		    &arc->pcta_samples) > pmcstat_threshold) {
395782434abSFabien Thomas 			terminal = 0;
396782434abSFabien Thomas 			break;
397782434abSFabien Thomas 		}
39846514c77SFabien Thomas 	}
399782434abSFabien Thomas 
400782434abSFabien Thomas 	if (ct->pct_narc == 0 || terminal) {
4010b86b1bbSFabien Thomas 		pmcpl_ct_topscreen[x+1][*y] = NULL;
402782434abSFabien Thomas 		if (*y >= PMCPL_CT_MAXLINE)
4030b86b1bbSFabien Thomas 			return 1;
4040b86b1bbSFabien Thomas 		*y = *y + 1;
4050b86b1bbSFabien Thomas 		for (i=0; i < x; i++)
4060b86b1bbSFabien Thomas 			pmcpl_ct_topscreen[i][*y] =
4070b86b1bbSFabien Thomas 			    pmcpl_ct_topscreen[i][*y - 1];
4080b86b1bbSFabien Thomas 		return 0;
4090b86b1bbSFabien Thomas 	}
4100b86b1bbSFabien Thomas 
411fa18b0b2SFabien Thomas 	ct->pct_color = PMCPL_PCT_GREY;
4120b86b1bbSFabien Thomas 	for (i = 0; i < ct->pct_narc; i++) {
413c86819ecSFabien Thomas 		if (PMCPL_CT_SAMPLE(pmcin,
414c86819ecSFabien Thomas 		    &ct->pct_arc[i].pcta_samples) == 0)
415c86819ecSFabien Thomas 			continue;
4160b86b1bbSFabien Thomas 		if (PMCPL_CT_SAMPLEP(pmcin,
4170b86b1bbSFabien Thomas 		    &ct->pct_arc[i].pcta_samples) > pmcstat_threshold) {
4180b86b1bbSFabien Thomas 			if (pmcpl_ct_node_dumptop(pmcin,
4190b86b1bbSFabien Thomas 			        ct->pct_arc[i].pcta_child,
420fa18b0b2SFabien Thomas 			        rsamples, x+1, y)) {
421fa18b0b2SFabien Thomas 				ct->pct_color = PMCPL_PCT_BLACK;
4220b86b1bbSFabien Thomas 				return 1;
4230b86b1bbSFabien Thomas 			}
4240b86b1bbSFabien Thomas 		}
425fa18b0b2SFabien Thomas 	}
426fa18b0b2SFabien Thomas 	ct->pct_color = PMCPL_PCT_BLACK;
4270b86b1bbSFabien Thomas 
4280b86b1bbSFabien Thomas 	return 0;
4290b86b1bbSFabien Thomas }
4300b86b1bbSFabien Thomas 
4310b86b1bbSFabien Thomas /*
432782434abSFabien Thomas  * Compare two top line by sum.
433782434abSFabien Thomas  */
434782434abSFabien Thomas static int
pmcpl_ct_line_compare(const void * a,const void * b)435782434abSFabien Thomas pmcpl_ct_line_compare(const void *a, const void *b)
436782434abSFabien Thomas {
437782434abSFabien Thomas 	const struct pmcpl_ct_line *ct1, *ct2;
438782434abSFabien Thomas 
439782434abSFabien Thomas 	ct1 = (const struct pmcpl_ct_line *) a;
440782434abSFabien Thomas 	ct2 = (const struct pmcpl_ct_line *) b;
441782434abSFabien Thomas 
442782434abSFabien Thomas 	/* Sort in reverse order */
443782434abSFabien Thomas 	if (ct1->ln_sum < ct2->ln_sum)
444782434abSFabien Thomas 		return (1);
445782434abSFabien Thomas 	if (ct1->ln_sum > ct2->ln_sum)
446782434abSFabien Thomas 		return (-1);
447782434abSFabien Thomas 	return (0);
448782434abSFabien Thomas }
449782434abSFabien Thomas 
450782434abSFabien Thomas /*
4510b86b1bbSFabien Thomas  * Format and display given PMC index.
4520b86b1bbSFabien Thomas  */
4530b86b1bbSFabien Thomas 
4540b86b1bbSFabien Thomas static void
pmcpl_ct_node_printtop(struct pmcpl_ct_sample * rsamples,int pmcin,int maxy)4550b86b1bbSFabien Thomas pmcpl_ct_node_printtop(struct pmcpl_ct_sample *rsamples, int pmcin, int maxy)
4560b86b1bbSFabien Thomas {
457782434abSFabien Thomas #undef	TS
458782434abSFabien Thomas #undef	TSI
459782434abSFabien Thomas #define	TS(x, y)	(pmcpl_ct_topscreen[x][y])
460782434abSFabien Thomas #define	TSI(x, y)	(pmcpl_ct_topscreen[x][pmcpl_ct_topmax[y].ln_index])
461782434abSFabien Thomas 
4620b86b1bbSFabien Thomas 	int v_attrs, ns_len, vs_len, is_len, width, indentwidth, x, y;
4630b86b1bbSFabien Thomas 	float v;
4640b86b1bbSFabien Thomas 	char ns[30], vs[10], is[20];
4650b86b1bbSFabien Thomas 	struct pmcpl_ct_node *ct;
4660b86b1bbSFabien Thomas 	const char *space = " ";
4670b86b1bbSFabien Thomas 
468782434abSFabien Thomas 	/*
469782434abSFabien Thomas 	 * Sort by line cost.
470782434abSFabien Thomas 	 */
471782434abSFabien Thomas 	for (y = 0; ; y++) {
472782434abSFabien Thomas 		ct = TS(1, y);
473782434abSFabien Thomas 		if (ct == NULL)
474782434abSFabien Thomas 			break;
475782434abSFabien Thomas 
476782434abSFabien Thomas 		pmcpl_ct_topmax[y].ln_sum = 0;
477782434abSFabien Thomas 		pmcpl_ct_topmax[y].ln_index = y;
478782434abSFabien Thomas 		for (x = 1; TS(x, y) != NULL; x++) {
479782434abSFabien Thomas 			pmcpl_ct_topmax[y].ln_sum +=
480782434abSFabien Thomas 			    PMCPL_CT_SAMPLE(pmcin, &TS(x, y)->pct_samples);
481782434abSFabien Thomas 		}
482782434abSFabien Thomas 	}
483782434abSFabien Thomas 	qsort(pmcpl_ct_topmax, y, sizeof(pmcpl_ct_topmax[0]),
484782434abSFabien Thomas 	    pmcpl_ct_line_compare);
485782434abSFabien Thomas 	pmcpl_ct_topmax[y].ln_index = y;
486782434abSFabien Thomas 
4870b86b1bbSFabien Thomas 	for (y = 0; y < maxy; y++) {
488782434abSFabien Thomas 		ct = TSI(1, y);
489782434abSFabien Thomas 		if (ct == NULL)
490782434abSFabien Thomas 			break;
4910b86b1bbSFabien Thomas 
492782434abSFabien Thomas 		if (y > 0)
493782434abSFabien Thomas 			PMCSTAT_PRINTW("\n");
4940b86b1bbSFabien Thomas 
495782434abSFabien Thomas 		/* Output sum. */
496782434abSFabien Thomas 		v = pmcpl_ct_topmax[y].ln_sum * 100.0 /
497782434abSFabien Thomas 		    rsamples->sb[pmcin];
498782434abSFabien Thomas 		snprintf(vs, sizeof(vs), "%.1f", v);
499782434abSFabien Thomas 		v_attrs = PMCSTAT_ATTRPERCENT(v);
500782434abSFabien Thomas 		PMCSTAT_ATTRON(v_attrs);
501782434abSFabien Thomas 		PMCSTAT_PRINTW("%5.5s ", vs);
502782434abSFabien Thomas 		PMCSTAT_ATTROFF(v_attrs);
5030b86b1bbSFabien Thomas 
504782434abSFabien Thomas 		width = indentwidth = 5 + 1;
505782434abSFabien Thomas 
506782434abSFabien Thomas 		for (x = 1; (ct = TSI(x, y)) != NULL; x++) {
507782434abSFabien Thomas 
5080b86b1bbSFabien Thomas 			vs[0] = '\0'; vs_len = 0;
5090b86b1bbSFabien Thomas 			is[0] = '\0'; is_len = 0;
5100b86b1bbSFabien Thomas 
5110b86b1bbSFabien Thomas 			/* Format value. */
5120b86b1bbSFabien Thomas 			v = PMCPL_CT_SAMPLEP(pmcin, &ct->pct_samples);
5130b86b1bbSFabien Thomas 			if (v > pmcstat_threshold)
514782434abSFabien Thomas 				vs_len  = snprintf(vs, sizeof(vs),
515782434abSFabien Thomas 				    "(%.1f%%)", v);
5160b86b1bbSFabien Thomas 			v_attrs = PMCSTAT_ATTRPERCENT(v);
5170b86b1bbSFabien Thomas 
5180b86b1bbSFabien Thomas 			if (pmcstat_skiplink && v <= pmcstat_threshold) {
519782434abSFabien Thomas 				strlcpy(ns, ".", sizeof(ns));
520782434abSFabien Thomas 				ns_len = 1;
521782434abSFabien Thomas 			} else {
522fa18b0b2SFabien Thomas 			if (ct->pct_sym != NULL) {
5230b86b1bbSFabien Thomas 				ns_len = snprintf(ns, sizeof(ns), "%s",
524fa18b0b2SFabien Thomas 				    pmcstat_string_unintern(ct->pct_sym->ps_name));
5250b86b1bbSFabien Thomas 			} else
5260b86b1bbSFabien Thomas 				ns_len = snprintf(ns, sizeof(ns), "%p",
5270b86b1bbSFabien Thomas 				    (void *)ct->pct_func);
5280b86b1bbSFabien Thomas 
5290b86b1bbSFabien Thomas 			/* Format image. */
530782434abSFabien Thomas 			if (x == 1 ||
531782434abSFabien Thomas 			    TSI(x-1, y)->pct_image != ct->pct_image)
5320b86b1bbSFabien Thomas 				is_len = snprintf(is, sizeof(is), "@%s",
5330b86b1bbSFabien Thomas 				    pmcstat_string_unintern(ct->pct_image->pi_name));
5340b86b1bbSFabien Thomas 
5350b86b1bbSFabien Thomas 			/* Check for line wrap. */
5360b86b1bbSFabien Thomas 			width += ns_len + is_len + vs_len + 1;
537782434abSFabien Thomas 			}
5380b86b1bbSFabien Thomas 			if (width >= pmcstat_displaywidth) {
53975109b48SFabien Thomas 				maxy--;
54075109b48SFabien Thomas 				if (y >= maxy)
54175109b48SFabien Thomas 					break;
5420b86b1bbSFabien Thomas 				PMCSTAT_PRINTW("\n%*s", indentwidth, space);
5430b86b1bbSFabien Thomas 				width = indentwidth + ns_len + is_len + vs_len;
5440b86b1bbSFabien Thomas 			}
5450b86b1bbSFabien Thomas 
5460b86b1bbSFabien Thomas 			PMCSTAT_ATTRON(v_attrs);
5470b86b1bbSFabien Thomas 			PMCSTAT_PRINTW("%s%s%s ", ns, is, vs);
5480b86b1bbSFabien Thomas 			PMCSTAT_ATTROFF(v_attrs);
5490b86b1bbSFabien Thomas 		}
5500b86b1bbSFabien Thomas 	}
5510b86b1bbSFabien Thomas }
5520b86b1bbSFabien Thomas 
5530b86b1bbSFabien Thomas /*
5540b86b1bbSFabien Thomas  * Output top mode snapshot.
5550b86b1bbSFabien Thomas  */
5560b86b1bbSFabien Thomas 
5570b86b1bbSFabien Thomas void
pmcpl_ct_topdisplay(void)5580b86b1bbSFabien Thomas pmcpl_ct_topdisplay(void)
5590b86b1bbSFabien Thomas {
560782434abSFabien Thomas 	int y;
561cb5a4153SFabien Thomas 	struct pmcpl_ct_sample r, *rsamples;
5620b86b1bbSFabien Thomas 
563cb5a4153SFabien Thomas 	rsamples = &r;
564cb5a4153SFabien Thomas 	pmcpl_ct_samples_root(rsamples);
5650b86b1bbSFabien Thomas 	pmcpl_ct_node_cleartag();
5660b86b1bbSFabien Thomas 
567782434abSFabien Thomas 	PMCSTAT_PRINTW("%5.5s %s\n", "%SAMP", "CALLTREE");
5680b86b1bbSFabien Thomas 
569782434abSFabien Thomas 	y = 0;
570782434abSFabien Thomas 	if (pmcpl_ct_node_dumptop(pmcstat_pmcinfilter,
571782434abSFabien Thomas 	    pmcpl_ct_root, rsamples, 0, &y))
572782434abSFabien Thomas 		PMCSTAT_PRINTW("...\n");
573782434abSFabien Thomas 	pmcpl_ct_topscreen[1][y] = NULL;
5740b86b1bbSFabien Thomas 
575782434abSFabien Thomas 	pmcpl_ct_node_printtop(rsamples,
576782434abSFabien Thomas 	    pmcstat_pmcinfilter, pmcstat_displayheight - 2);
577782434abSFabien Thomas 
578cb5a4153SFabien Thomas 	pmcpl_ct_samples_free(rsamples);
5790b86b1bbSFabien Thomas }
5800b86b1bbSFabien Thomas 
5810b86b1bbSFabien Thomas /*
5820b86b1bbSFabien Thomas  * Handle top mode keypress.
5830b86b1bbSFabien Thomas  */
5840b86b1bbSFabien Thomas 
5850b86b1bbSFabien Thomas int
pmcpl_ct_topkeypress(int c,void * arg)586d27927f7SRuslan Bukin pmcpl_ct_topkeypress(int c, void *arg)
5870b86b1bbSFabien Thomas {
588d27927f7SRuslan Bukin 	WINDOW *w;
589d27927f7SRuslan Bukin 
590d27927f7SRuslan Bukin 	w = (WINDOW *)arg;
5910b86b1bbSFabien Thomas 
5920b86b1bbSFabien Thomas 	switch (c) {
5930b86b1bbSFabien Thomas 	case 'f':
5940b86b1bbSFabien Thomas 		pmcstat_skiplink = !pmcstat_skiplink;
59537d6f8a9SDavid E. O'Brien 		wprintw(w, "skip empty link %s",
59637d6f8a9SDavid E. O'Brien 		    pmcstat_skiplink ? "on" : "off");
5970b86b1bbSFabien Thomas 		break;
5980b86b1bbSFabien Thomas 	}
5990b86b1bbSFabien Thomas 
6000b86b1bbSFabien Thomas 	return 0;
6010b86b1bbSFabien Thomas }
6020b86b1bbSFabien Thomas 
6030b86b1bbSFabien Thomas /*
6040b86b1bbSFabien Thomas  * Look for a callgraph node associated with pmc `pmcid' in the global
6050b86b1bbSFabien Thomas  * hash table that corresponds to the given `pc' value in the process map
6060b86b1bbSFabien Thomas  * `ppm'.
6070b86b1bbSFabien Thomas  */
6080b86b1bbSFabien Thomas 
609fa18b0b2SFabien Thomas static void
pmcpl_ct_node_update(struct pmcpl_ct_node * parent,struct pmcpl_ct_node * child,int pmcin,unsigned v,int cd)610fa18b0b2SFabien Thomas pmcpl_ct_node_update(struct pmcpl_ct_node *parent,
611fa18b0b2SFabien Thomas     struct pmcpl_ct_node *child, int pmcin, unsigned v, int cd)
6120b86b1bbSFabien Thomas {
6130b86b1bbSFabien Thomas 	struct pmcpl_ct_arc *arc;
6140b86b1bbSFabien Thomas 	int i;
6150b86b1bbSFabien Thomas 
6160b86b1bbSFabien Thomas 	assert(parent != NULL);
6170b86b1bbSFabien Thomas 
6180b86b1bbSFabien Thomas 	/*
6190b86b1bbSFabien Thomas 	 * Find related arc in parent node and
6200b86b1bbSFabien Thomas 	 * increment the sample count.
6210b86b1bbSFabien Thomas 	 */
6220b86b1bbSFabien Thomas 	for (i = 0; i < parent->pct_narc; i++) {
623fa18b0b2SFabien Thomas 		if (parent->pct_arc[i].pcta_child == child) {
6240b86b1bbSFabien Thomas 			arc = &parent->pct_arc[i];
6250b86b1bbSFabien Thomas 			pmcpl_ct_samples_grow(&arc->pcta_samples);
626fa18b0b2SFabien Thomas 			arc->pcta_samples.sb[pmcin] += v;
6270b86b1bbSFabien Thomas 			/* Estimate call count. */
628fa18b0b2SFabien Thomas 			if (cd) {
6290b86b1bbSFabien Thomas 			pmcpl_ct_samples_grow(&arc->pcta_callid);
6300b86b1bbSFabien Thomas 			if (pmcpl_ct_callid.sb[pmcin] -
6310b86b1bbSFabien Thomas 			    arc->pcta_callid.sb[pmcin] > 1)
6320b86b1bbSFabien Thomas 				arc->pcta_call++;
6330b86b1bbSFabien Thomas 			arc->pcta_callid.sb[pmcin] =
6340b86b1bbSFabien Thomas 			    pmcpl_ct_callid.sb[pmcin];
635fa18b0b2SFabien Thomas 			}
636fa18b0b2SFabien Thomas 			return;
6370b86b1bbSFabien Thomas 		}
6380b86b1bbSFabien Thomas 	}
6390b86b1bbSFabien Thomas 
6400b86b1bbSFabien Thomas 	/*
6410b86b1bbSFabien Thomas 	 * No arc found for us, add ourself to the parent.
6420b86b1bbSFabien Thomas 	 */
6430b86b1bbSFabien Thomas 	pmcpl_ct_arc_grow(parent->pct_narc,
6440b86b1bbSFabien Thomas 	    &parent->pct_arc_c, &parent->pct_arc);
6450b86b1bbSFabien Thomas 	arc = &parent->pct_arc[parent->pct_narc];
6460b86b1bbSFabien Thomas 	pmcpl_ct_samples_grow(&arc->pcta_samples);
647fa18b0b2SFabien Thomas 	arc->pcta_samples.sb[pmcin] = v;
6480b86b1bbSFabien Thomas 	arc->pcta_call = 1;
649fa18b0b2SFabien Thomas 	if (cd) {
6500b86b1bbSFabien Thomas 		pmcpl_ct_samples_grow(&arc->pcta_callid);
6510b86b1bbSFabien Thomas 		arc->pcta_callid.sb[pmcin] = pmcpl_ct_callid.sb[pmcin];
652fa18b0b2SFabien Thomas 	}
653fa18b0b2SFabien Thomas 	arc->pcta_child = child;
6540b86b1bbSFabien Thomas 	parent->pct_narc++;
655fa18b0b2SFabien Thomas }
656fa18b0b2SFabien Thomas 
657fa18b0b2SFabien Thomas /*
658fa18b0b2SFabien Thomas  * Lookup by image/pc.
659fa18b0b2SFabien Thomas  */
660fa18b0b2SFabien Thomas 
661fa18b0b2SFabien Thomas static struct pmcpl_ct_node *
pmcpl_ct_node_hash_lookup(struct pmcstat_image * image,uintfptr_t pc,struct pmcstat_symbol * sym,char * fl,char * fn)662fa18b0b2SFabien Thomas pmcpl_ct_node_hash_lookup(struct pmcstat_image *image, uintfptr_t pc,
663fa18b0b2SFabien Thomas     struct pmcstat_symbol *sym, char *fl, char *fn)
664fa18b0b2SFabien Thomas {
665fa18b0b2SFabien Thomas 	int i;
666fa18b0b2SFabien Thomas 	unsigned int hash;
667fa18b0b2SFabien Thomas 	struct pmcpl_ct_node *ct;
668fa18b0b2SFabien Thomas 	struct pmcpl_ct_node_hash *h;
669fa18b0b2SFabien Thomas 	pmcstat_interned_string	ifl, ifn;
670fa18b0b2SFabien Thomas 
671fa18b0b2SFabien Thomas 	if (fn != NULL) {
672fa18b0b2SFabien Thomas 		ifl = pmcstat_string_intern(fl);
673fa18b0b2SFabien Thomas 		ifn = pmcstat_string_intern(fn);
674fa18b0b2SFabien Thomas 	} else {
675fa18b0b2SFabien Thomas 		ifl = 0;
676fa18b0b2SFabien Thomas 		ifn = 0;
677fa18b0b2SFabien Thomas 	}
678fa18b0b2SFabien Thomas 
679fa18b0b2SFabien Thomas 	for (hash = i = 0; i < (int)sizeof(uintfptr_t); i++)
680fa18b0b2SFabien Thomas 		hash += (pc >> i) & 0xFF;
681fa18b0b2SFabien Thomas 
682fa18b0b2SFabien Thomas 	hash &= PMCSTAT_HASH_MASK;
683fa18b0b2SFabien Thomas 
684fa18b0b2SFabien Thomas 	STAILQ_FOREACH(h, &pmcpl_ct_node_hash[hash], pch_next) {
685fa18b0b2SFabien Thomas 		ct = h->pch_ctnode;
686fa18b0b2SFabien Thomas 
687fa18b0b2SFabien Thomas 		assert(ct != NULL);
688fa18b0b2SFabien Thomas 
689fa18b0b2SFabien Thomas 		if (ct->pct_image == image && ct->pct_func == pc) {
690fa18b0b2SFabien Thomas 			if (fn == NULL)
691fa18b0b2SFabien Thomas 				return (ct);
692fa18b0b2SFabien Thomas 			if (ct->pct_type == PMCPL_PCT_NAME &&
693fa18b0b2SFabien Thomas 			    ct->pct_ifl == ifl && ct->pct_ifn == ifn)
6940b86b1bbSFabien Thomas 				return (ct);
6950b86b1bbSFabien Thomas 		}
6960b86b1bbSFabien Thomas 	}
6970b86b1bbSFabien Thomas 
6980b86b1bbSFabien Thomas 	/*
6990b86b1bbSFabien Thomas 	 * We haven't seen this (pmcid, pc) tuple yet, so allocate a
7000b86b1bbSFabien Thomas 	 * new callgraph node and a new hash table entry for it.
7010b86b1bbSFabien Thomas 	 */
702fa18b0b2SFabien Thomas 	ct = pmcpl_ct_node_allocate();
7030b86b1bbSFabien Thomas 	if ((h = malloc(sizeof(*h))) == NULL)
7040b86b1bbSFabien Thomas 		err(EX_OSERR, "ERROR: Could not allocate callgraph node");
7050b86b1bbSFabien Thomas 
706fa18b0b2SFabien Thomas 	if (fn != NULL) {
707fa18b0b2SFabien Thomas 		ct->pct_type = PMCPL_PCT_NAME;
708fa18b0b2SFabien Thomas 		ct->pct_ifl = ifl;
709fa18b0b2SFabien Thomas 		ct->pct_ifn = ifn;
710fa18b0b2SFabien Thomas 	} else
711fa18b0b2SFabien Thomas 		ct->pct_type = PMCPL_PCT_ADDR;
712fa18b0b2SFabien Thomas 	ct->pct_image = image;
713fa18b0b2SFabien Thomas 	ct->pct_func = pc;
714fa18b0b2SFabien Thomas 	ct->pct_sym = sym;
7150b86b1bbSFabien Thomas 
716fa18b0b2SFabien Thomas 	h->pch_ctnode = ct;
717fa18b0b2SFabien Thomas 	STAILQ_INSERT_HEAD(&pmcpl_ct_node_hash[hash], h, pch_next);
7180b86b1bbSFabien Thomas 	return (ct);
7190b86b1bbSFabien Thomas }
7200b86b1bbSFabien Thomas 
7210b86b1bbSFabien Thomas /*
7220b86b1bbSFabien Thomas  * Record a callchain.
7230b86b1bbSFabien Thomas  */
7240b86b1bbSFabien Thomas 
7250b86b1bbSFabien Thomas void
pmcpl_ct_process(struct pmcstat_process * pp,struct pmcstat_pmcrecord * pmcr,uint32_t nsamples,uintfptr_t * cc,int usermode,uint32_t cpu)7260b86b1bbSFabien Thomas pmcpl_ct_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr,
7270b86b1bbSFabien Thomas     uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu)
7280b86b1bbSFabien Thomas {
729fa18b0b2SFabien Thomas 	int i, n, pmcin;
730fa18b0b2SFabien Thomas 	uintfptr_t pc, loadaddress;
731fa18b0b2SFabien Thomas 	struct pmcstat_image *image;
732fa18b0b2SFabien Thomas 	struct pmcstat_symbol *sym;
7330b86b1bbSFabien Thomas 	struct pmcstat_pcmap *ppm[PMC_CALLCHAIN_DEPTH_MAX];
7340b86b1bbSFabien Thomas 	struct pmcstat_process *km;
735fa18b0b2SFabien Thomas 	struct pmcpl_ct_node *ct;
736fa18b0b2SFabien Thomas 	struct pmcpl_ct_node *ctl[PMC_CALLCHAIN_DEPTH_MAX+1];
7370b86b1bbSFabien Thomas 
7380b86b1bbSFabien Thomas 	(void) cpu;
7390b86b1bbSFabien Thomas 
7400b86b1bbSFabien Thomas 	assert(nsamples>0 && nsamples<=PMC_CALLCHAIN_DEPTH_MAX);
7410b86b1bbSFabien Thomas 
7420b86b1bbSFabien Thomas 	/* Get the PMC index. */
7430b86b1bbSFabien Thomas 	pmcin = pmcr->pr_pmcin;
7440b86b1bbSFabien Thomas 
7450b86b1bbSFabien Thomas 	/*
7460b86b1bbSFabien Thomas 	 * Validate mapping for the callchain.
7470b86b1bbSFabien Thomas 	 * Go from bottom to first invalid entry.
7480b86b1bbSFabien Thomas 	 */
7490b86b1bbSFabien Thomas 	km = pmcstat_kernproc;
7500b86b1bbSFabien Thomas 	for (n = 0; n < (int)nsamples; n++) {
7510b86b1bbSFabien Thomas 		ppm[n] = pmcstat_process_find_map(usermode ?
7520b86b1bbSFabien Thomas 		    pp : km, cc[n]);
7530b86b1bbSFabien Thomas 		if (ppm[n] == NULL) {
7540b86b1bbSFabien Thomas 			/* Detect full frame capture (kernel + user). */
7550b86b1bbSFabien Thomas 			if (!usermode) {
7560b86b1bbSFabien Thomas 				ppm[n] = pmcstat_process_find_map(pp, cc[n]);
7570b86b1bbSFabien Thomas 				if (ppm[n] != NULL)
7580b86b1bbSFabien Thomas 					km = pp;
7590b86b1bbSFabien Thomas 			}
7600b86b1bbSFabien Thomas 		}
7610b86b1bbSFabien Thomas 		if (ppm[n] == NULL)
7620b86b1bbSFabien Thomas 			break;
7630b86b1bbSFabien Thomas 	}
7640b86b1bbSFabien Thomas 	if (n-- == 0) {
7650b86b1bbSFabien Thomas 		pmcstat_stats.ps_callchain_dubious_frames++;
766c86819ecSFabien Thomas 		pmcr->pr_dubious_frames++;
7670b86b1bbSFabien Thomas 		return;
7680b86b1bbSFabien Thomas 	}
7690b86b1bbSFabien Thomas 
7700b86b1bbSFabien Thomas 	/* Increase the call generation counter. */
7710b86b1bbSFabien Thomas 	pmcpl_ct_samples_grow(&pmcpl_ct_callid);
7720b86b1bbSFabien Thomas 	pmcpl_ct_callid.sb[pmcin]++;
7730b86b1bbSFabien Thomas 
7740b86b1bbSFabien Thomas 	/*
775fa18b0b2SFabien Thomas 	 * Build node list.
7760b86b1bbSFabien Thomas 	 */
777fa18b0b2SFabien Thomas 	ctl[0] = pmcpl_ct_root;
778fa18b0b2SFabien Thomas 	for (i = 1; n >= 0; n--) {
779fa18b0b2SFabien Thomas 		image = ppm[n]->ppm_image;
780fa18b0b2SFabien Thomas 		loadaddress = ppm[n]->ppm_lowpc +
781fa18b0b2SFabien Thomas 		    image->pi_vaddr - image->pi_start;
782fa18b0b2SFabien Thomas 		/* Convert to an offset in the image. */
783fa18b0b2SFabien Thomas 		pc = cc[n] - loadaddress;
784fa18b0b2SFabien Thomas 		/*
785fa18b0b2SFabien Thomas 		 * Try determine the function at this offset.  If we can't
786fa18b0b2SFabien Thomas 		 * find a function round leave the `pc' value alone.
787fa18b0b2SFabien Thomas 		 */
788fa18b0b2SFabien Thomas 		if ((sym = pmcstat_symbol_search(image, pc)) != NULL)
789fa18b0b2SFabien Thomas 			pc = sym->ps_start;
790fa18b0b2SFabien Thomas 		else
791fa18b0b2SFabien Thomas 			pmcstat_stats.ps_samples_unknown_function++;
792fa18b0b2SFabien Thomas 
793fa18b0b2SFabien Thomas 		ct = pmcpl_ct_node_hash_lookup(image, pc, sym, NULL, NULL);
794fa18b0b2SFabien Thomas 		if (ct == NULL) {
7950b86b1bbSFabien Thomas 			pmcstat_stats.ps_callchain_dubious_frames++;
7960b86b1bbSFabien Thomas 			continue;
7970b86b1bbSFabien Thomas 		}
798fa18b0b2SFabien Thomas 		ctl[i++] = ct;
7990b86b1bbSFabien Thomas 	}
800fa18b0b2SFabien Thomas 	/* No valid node found. */
801fa18b0b2SFabien Thomas 	if (i == 1)
802fa18b0b2SFabien Thomas 		return;
803fa18b0b2SFabien Thomas 	n = i;
804fa18b0b2SFabien Thomas 
805fa18b0b2SFabien Thomas 	ct = ctl[0];
806fa18b0b2SFabien Thomas 	for (i = 1; i < n; i++)
807fa18b0b2SFabien Thomas 		pmcpl_ct_node_update(ctl[i-1], ctl[i], pmcin, 1, 1);
8080b86b1bbSFabien Thomas 
8090b86b1bbSFabien Thomas 	/*
8100b86b1bbSFabien Thomas 	 * Increment the sample count for this PMC.
8110b86b1bbSFabien Thomas 	 */
812fa18b0b2SFabien Thomas 	pmcpl_ct_samples_grow(&ctl[n-1]->pct_samples);
813fa18b0b2SFabien Thomas 	ctl[n-1]->pct_samples.sb[pmcin]++;
8140b86b1bbSFabien Thomas 
8150b86b1bbSFabien Thomas 	/* Update per instruction sample if required. */
8160b86b1bbSFabien Thomas 	if (args.pa_ctdumpinstr)
817fa18b0b2SFabien Thomas 		pmcpl_ct_instr_add(ctl[n-1], pmcin, cc[0] -
8180b86b1bbSFabien Thomas 		    (ppm[0]->ppm_lowpc + ppm[0]->ppm_image->pi_vaddr -
819fa18b0b2SFabien Thomas 		     ppm[0]->ppm_image->pi_start), 1);
820fa18b0b2SFabien Thomas }
821fa18b0b2SFabien Thomas 
822fa18b0b2SFabien Thomas /*
823fa18b0b2SFabien Thomas  * Print node child cost.
824fa18b0b2SFabien Thomas  */
825fa18b0b2SFabien Thomas 
826fa18b0b2SFabien Thomas static void
pmcpl_ct_node_printchild(struct pmcpl_ct_node * ct,uintfptr_t paddr,int pline)827fa18b0b2SFabien Thomas pmcpl_ct_node_printchild(struct pmcpl_ct_node *ct, uintfptr_t paddr,
828fa18b0b2SFabien Thomas     int pline)
829fa18b0b2SFabien Thomas {
830fa18b0b2SFabien Thomas 	int i, j, line;
831fa18b0b2SFabien Thomas 	uintfptr_t addr;
832fa18b0b2SFabien Thomas 	struct pmcpl_ct_node *child;
833fa18b0b2SFabien Thomas 	char sourcefile[PATH_MAX];
834fa18b0b2SFabien Thomas 	char funcname[PATH_MAX];
835fa18b0b2SFabien Thomas 
836fa18b0b2SFabien Thomas 	/*
837fa18b0b2SFabien Thomas 	 * Child cost.
838d7b1cc0aSPedro F. Giffuni 	 * TODO: attach child cost to the real position in the function.
839fa18b0b2SFabien Thomas 	 * TODO: cfn=<fn> / call <ncall> addr(<fn>) / addr(call <fn>) <arccost>
840fa18b0b2SFabien Thomas 	 */
841fa18b0b2SFabien Thomas 	for (i=0 ; i<ct->pct_narc; i++) {
842fa18b0b2SFabien Thomas 		child = ct->pct_arc[i].pcta_child;
843fa18b0b2SFabien Thomas 		/* Object binary. */
844fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "cob=%s\n",
845fa18b0b2SFabien Thomas 		    pmcstat_string_unintern(child->pct_image->pi_fullpath));
846fa18b0b2SFabien Thomas 		/* Child function name. */
847fa18b0b2SFabien Thomas 		addr = child->pct_image->pi_vaddr + child->pct_func;
848fa18b0b2SFabien Thomas 		line = 0;
849fa18b0b2SFabien Thomas 		/* Child function source file. */
850fa18b0b2SFabien Thomas 		if (child->pct_type == PMCPL_PCT_NAME) {
851fa18b0b2SFabien Thomas 			fprintf(args.pa_graphfile, "cfi=%s\ncfn=%s\n",
852fa18b0b2SFabien Thomas 			    pmcstat_string_unintern(child->pct_ifl),
853fa18b0b2SFabien Thomas 			    pmcstat_string_unintern(child->pct_ifn));
854fa18b0b2SFabien Thomas 		} else if (pmcstat_image_addr2line(child->pct_image, addr,
855fa18b0b2SFabien Thomas 		    sourcefile, sizeof(sourcefile), &line,
856fa18b0b2SFabien Thomas 		    funcname, sizeof(funcname))) {
857fa18b0b2SFabien Thomas 			fprintf(args.pa_graphfile, "cfi=%s\ncfn=%s\n",
858fa18b0b2SFabien Thomas 				sourcefile, funcname);
859fa18b0b2SFabien Thomas 		} else {
860fa18b0b2SFabien Thomas 			if (child->pct_sym != NULL)
861fa18b0b2SFabien Thomas 				fprintf(args.pa_graphfile,
862fa18b0b2SFabien Thomas 				    "cfi=???\ncfn=%s\n",
863fa18b0b2SFabien Thomas 				    pmcstat_string_unintern(
864fa18b0b2SFabien Thomas 				        child->pct_sym->ps_name));
865fa18b0b2SFabien Thomas 			else
866fa18b0b2SFabien Thomas 				fprintf(args.pa_graphfile,
867fa18b0b2SFabien Thomas 				    "cfi=???\ncfn=%p\n", (void *)addr);
868fa18b0b2SFabien Thomas 		}
869fa18b0b2SFabien Thomas 
870fa18b0b2SFabien Thomas 		/* Child function address, line and call count. */
871fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "calls=%u %p %u\n",
872fa18b0b2SFabien Thomas 		    ct->pct_arc[i].pcta_call, (void *)addr, line);
873fa18b0b2SFabien Thomas 
874fa18b0b2SFabien Thomas 		/*
875fa18b0b2SFabien Thomas 		 * Call address, line, sample.
876fa18b0b2SFabien Thomas 		 * TODO: Associate call address to the right location.
877fa18b0b2SFabien Thomas 		 */
878fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "%p %u", (void *)paddr, pline);
879fa18b0b2SFabien Thomas 		for (j = 0; j<pmcstat_npmcs; j++)
880fa18b0b2SFabien Thomas 			fprintf(args.pa_graphfile, " %u",
881fa18b0b2SFabien Thomas 			    PMCPL_CT_SAMPLE(j, &ct->pct_arc[i].pcta_samples));
882fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "\n");
8830b86b1bbSFabien Thomas 	}
8840b86b1bbSFabien Thomas }
8850b86b1bbSFabien Thomas 
8860b86b1bbSFabien Thomas /*
8870b86b1bbSFabien Thomas  * Print node self cost.
8880b86b1bbSFabien Thomas  */
8890b86b1bbSFabien Thomas 
8900b86b1bbSFabien Thomas static void
pmcpl_ct_node_printself(struct pmcpl_ct_node * ct)8910b86b1bbSFabien Thomas pmcpl_ct_node_printself(struct pmcpl_ct_node *ct)
8920b86b1bbSFabien Thomas {
893fa18b0b2SFabien Thomas 	int i, j, fline, line;
894fa18b0b2SFabien Thomas 	uintfptr_t faddr, addr;
8950b86b1bbSFabien Thomas 	char sourcefile[PATH_MAX];
8960b86b1bbSFabien Thomas 	char funcname[PATH_MAX];
8970b86b1bbSFabien Thomas 
8980b86b1bbSFabien Thomas 	/*
8990b86b1bbSFabien Thomas 	 * Object binary.
9000b86b1bbSFabien Thomas 	 */
9010b86b1bbSFabien Thomas 	fprintf(args.pa_graphfile, "ob=%s\n",
902fa18b0b2SFabien Thomas 	    pmcstat_string_unintern(ct->pct_image->pi_fullpath));
9030b86b1bbSFabien Thomas 
9040b86b1bbSFabien Thomas 	/*
9050b86b1bbSFabien Thomas 	 * Function name.
9060b86b1bbSFabien Thomas 	 */
907fa18b0b2SFabien Thomas 	faddr = ct->pct_image->pi_vaddr + ct->pct_func;
908fa18b0b2SFabien Thomas 	fline = 0;
909fa18b0b2SFabien Thomas 	if (ct->pct_type == PMCPL_PCT_NAME) {
910fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "fl=%s\nfn=%s\n",
911fa18b0b2SFabien Thomas 		    pmcstat_string_unintern(ct->pct_ifl),
912fa18b0b2SFabien Thomas 		    pmcstat_string_unintern(ct->pct_ifn));
913fa18b0b2SFabien Thomas 	} else if (pmcstat_image_addr2line(ct->pct_image, faddr,
914fa18b0b2SFabien Thomas 	    sourcefile, sizeof(sourcefile), &fline,
9150b86b1bbSFabien Thomas 	    funcname, sizeof(funcname))) {
916fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "fl=%s\nfn=%s\n",
917fa18b0b2SFabien Thomas 		    sourcefile, funcname);
9180b86b1bbSFabien Thomas 	} else {
919fa18b0b2SFabien Thomas 		if (ct->pct_sym != NULL)
920fa18b0b2SFabien Thomas 			fprintf(args.pa_graphfile, "fl=???\nfn=%s\n",
921fa18b0b2SFabien Thomas 			    pmcstat_string_unintern(ct->pct_sym->ps_name));
9220b86b1bbSFabien Thomas 		else
923fa18b0b2SFabien Thomas 			fprintf(args.pa_graphfile, "fl=???\nfn=%p\n",
9240b86b1bbSFabien Thomas 			    (void *)(ct->pct_image->pi_vaddr + ct->pct_func));
9250b86b1bbSFabien Thomas 	}
9260b86b1bbSFabien Thomas 
9270b86b1bbSFabien Thomas 	/*
9280b86b1bbSFabien Thomas 	 * Self cost.
9290b86b1bbSFabien Thomas 	 */
9300b86b1bbSFabien Thomas 	if (ct->pct_ninstr > 0) {
931fa18b0b2SFabien Thomas 		/*
932fa18b0b2SFabien Thomas 		 * Per location cost.
933fa18b0b2SFabien Thomas 		 */
9340b86b1bbSFabien Thomas 		for (i = 0; i < ct->pct_ninstr; i++) {
9350b86b1bbSFabien Thomas 			addr = ct->pct_image->pi_vaddr +
9360b86b1bbSFabien Thomas 			    ct->pct_instr[i].pctf_func;
9370b86b1bbSFabien Thomas 			line = 0;
938fa18b0b2SFabien Thomas 			pmcstat_image_addr2line(ct->pct_image, addr,
9390b86b1bbSFabien Thomas 			    sourcefile, sizeof(sourcefile), &line,
940fa18b0b2SFabien Thomas 			    funcname, sizeof(funcname));
941fa18b0b2SFabien Thomas 			fprintf(args.pa_graphfile, "%p %u",
942fa18b0b2SFabien Thomas 			    (void *)addr, line);
9430b86b1bbSFabien Thomas 			for (j = 0; j<pmcstat_npmcs; j++)
9440b86b1bbSFabien Thomas 				fprintf(args.pa_graphfile, " %u",
9450b86b1bbSFabien Thomas 				    PMCPL_CT_SAMPLE(j,
9460b86b1bbSFabien Thomas 				    &ct->pct_instr[i].pctf_samples));
9470b86b1bbSFabien Thomas 			fprintf(args.pa_graphfile, "\n");
9480b86b1bbSFabien Thomas 		}
9490b86b1bbSFabien Thomas 	} else {
950fa18b0b2SFabien Thomas 		/* Global cost function cost. */
951fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "%p %u", (void *)faddr, fline);
9520b86b1bbSFabien Thomas 		for (i = 0; i<pmcstat_npmcs ; i++)
9530b86b1bbSFabien Thomas 			fprintf(args.pa_graphfile, " %u",
9540b86b1bbSFabien Thomas 			    PMCPL_CT_SAMPLE(i, &ct->pct_samples));
9550b86b1bbSFabien Thomas 		fprintf(args.pa_graphfile, "\n");
9560b86b1bbSFabien Thomas 	}
957fa18b0b2SFabien Thomas 
958fa18b0b2SFabien Thomas 	pmcpl_ct_node_printchild(ct, faddr, fline);
959fa18b0b2SFabien Thomas }
960fa18b0b2SFabien Thomas 
961fa18b0b2SFabien Thomas static void
pmcpl_ct_printnode(struct pmcpl_ct_node * ct)962fa18b0b2SFabien Thomas pmcpl_ct_printnode(struct pmcpl_ct_node *ct)
963fa18b0b2SFabien Thomas {
964fa18b0b2SFabien Thomas 	int i;
965fa18b0b2SFabien Thomas 
966fa18b0b2SFabien Thomas 	if (ct == pmcpl_ct_root) {
967fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "fn=root\n");
968fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "0x0 1");
969fa18b0b2SFabien Thomas 		for (i = 0; i<pmcstat_npmcs ; i++)
970fa18b0b2SFabien Thomas 			fprintf(args.pa_graphfile, " 0");
971fa18b0b2SFabien Thomas 		fprintf(args.pa_graphfile, "\n");
972fa18b0b2SFabien Thomas 		pmcpl_ct_node_printchild(ct, 0, 0);
973fa18b0b2SFabien Thomas 	} else
974fa18b0b2SFabien Thomas 		pmcpl_ct_node_printself(ct);
9750b86b1bbSFabien Thomas }
9760b86b1bbSFabien Thomas 
9770b86b1bbSFabien Thomas /*
978fa18b0b2SFabien Thomas  * Breadth first traversal.
9790b86b1bbSFabien Thomas  */
9800b86b1bbSFabien Thomas 
9810b86b1bbSFabien Thomas static void
pmcpl_ct_bfs(struct pmcpl_ct_node * ct)982fa18b0b2SFabien Thomas pmcpl_ct_bfs(struct pmcpl_ct_node *ct)
9830b86b1bbSFabien Thomas {
984fa18b0b2SFabien Thomas 	int i;
985fa18b0b2SFabien Thomas 	struct pmcpl_ct_node_hash *pch, *pchc;
9860b86b1bbSFabien Thomas 	struct pmcpl_ct_node *child;
987fa18b0b2SFabien Thomas 	STAILQ_HEAD(,pmcpl_ct_node_hash) q;
988fa18b0b2SFabien Thomas 
989fa18b0b2SFabien Thomas 	STAILQ_INIT(&q);
990fa18b0b2SFabien Thomas 	if ((pch = malloc(sizeof(*pch))) == NULL)
991fa18b0b2SFabien Thomas 		err(EX_OSERR, "ERROR: Cannot allocate queue");
992fa18b0b2SFabien Thomas 	pch->pch_ctnode = ct;
993fa18b0b2SFabien Thomas 	STAILQ_INSERT_TAIL(&q, pch, pch_next);
994fa18b0b2SFabien Thomas 	ct->pct_color = PMCPL_PCT_BLACK;
995fa18b0b2SFabien Thomas 
996fa18b0b2SFabien Thomas 	while (!STAILQ_EMPTY(&q)) {
997fa18b0b2SFabien Thomas 		pch = STAILQ_FIRST(&q);
998fa18b0b2SFabien Thomas 		STAILQ_REMOVE_HEAD(&q, pch_next);
999fa18b0b2SFabien Thomas 		pmcpl_ct_printnode(pch->pch_ctnode);
1000fa18b0b2SFabien Thomas 		for (i = 0; i<pch->pch_ctnode->pct_narc; i++) {
1001fa18b0b2SFabien Thomas 			child = pch->pch_ctnode->pct_arc[i].pcta_child;
1002fa18b0b2SFabien Thomas 			if (child->pct_color == PMCPL_PCT_WHITE) {
1003fa18b0b2SFabien Thomas 				child->pct_color = PMCPL_PCT_BLACK;
1004fa18b0b2SFabien Thomas 				if ((pchc = malloc(sizeof(*pchc))) == NULL)
1005fa18b0b2SFabien Thomas 					err(EX_OSERR,
1006fa18b0b2SFabien Thomas 					    "ERROR: Cannot allocate queue");
1007fa18b0b2SFabien Thomas 				pchc->pch_ctnode = child;
1008fa18b0b2SFabien Thomas 				STAILQ_INSERT_TAIL(&q, pchc, pch_next);
1009fa18b0b2SFabien Thomas 			}
1010fa18b0b2SFabien Thomas 		}
1011fa18b0b2SFabien Thomas 		free(pch);
1012fa18b0b2SFabien Thomas 	}
1013fa18b0b2SFabien Thomas }
10140b86b1bbSFabien Thomas 
10150b86b1bbSFabien Thomas /*
1016fa18b0b2SFabien Thomas  * Detect and fix inlined location.
10170b86b1bbSFabien Thomas  */
10180b86b1bbSFabien Thomas 
1019fa18b0b2SFabien Thomas static void
_pmcpl_ct_expand_inline(struct pmcpl_ct_node * ct)1020fa18b0b2SFabien Thomas _pmcpl_ct_expand_inline(struct pmcpl_ct_node *ct)
1021fa18b0b2SFabien Thomas {
1022fa18b0b2SFabien Thomas 	int i, j;
1023fa18b0b2SFabien Thomas 	unsigned fline, line, v;
1024fa18b0b2SFabien Thomas 	uintfptr_t faddr, addr, pc;
1025fa18b0b2SFabien Thomas 	char sourcefile[PATH_MAX];
1026fa18b0b2SFabien Thomas 	char ffuncname[PATH_MAX], funcname[PATH_MAX];
1027fa18b0b2SFabien Thomas 	char buffer[PATH_MAX];
1028fa18b0b2SFabien Thomas 	struct pmcpl_ct_node *child;
10290b86b1bbSFabien Thomas 
1030fa18b0b2SFabien Thomas 	/*
1031fa18b0b2SFabien Thomas 	 * Resolve parent and compare to each instr location.
1032fa18b0b2SFabien Thomas 	 */
1033fa18b0b2SFabien Thomas 	faddr = ct->pct_image->pi_vaddr + ct->pct_func;
1034fa18b0b2SFabien Thomas 	fline = 0;
1035fa18b0b2SFabien Thomas 	if (!pmcstat_image_addr2line(ct->pct_image, faddr,
1036fa18b0b2SFabien Thomas 	    sourcefile, sizeof(sourcefile), &fline,
1037fa18b0b2SFabien Thomas 	    ffuncname, sizeof(ffuncname)))
1038fa18b0b2SFabien Thomas 		return;
10390b86b1bbSFabien Thomas 
1040fa18b0b2SFabien Thomas 	for (i = 0; i < ct->pct_ninstr; i++) {
1041fa18b0b2SFabien Thomas 		addr = ct->pct_image->pi_vaddr +
1042fa18b0b2SFabien Thomas 		    ct->pct_instr[i].pctf_func;
10430b86b1bbSFabien Thomas 		line = 0;
1044fa18b0b2SFabien Thomas 		if (!pmcstat_image_addr2line(ct->pct_image, addr,
1045fa18b0b2SFabien Thomas 		    sourcefile, sizeof(sourcefile), &line,
10465c15d3c8SFabien Thomas 		    funcname, sizeof(funcname)))
1047fa18b0b2SFabien Thomas 			continue;
1048fa18b0b2SFabien Thomas 
1049fa18b0b2SFabien Thomas 		if (strcmp(funcname, ffuncname) == 0)
1050fa18b0b2SFabien Thomas 			continue;
1051fa18b0b2SFabien Thomas 
1052fa18b0b2SFabien Thomas 		/*
1053fa18b0b2SFabien Thomas 		 * - Lookup/create inline node by function name.
1054fa18b0b2SFabien Thomas 		 * - Move instr PMCs to the inline node.
1055fa18b0b2SFabien Thomas 		 * - Link nodes.
1056fa18b0b2SFabien Thomas 		 * The lookup create a specific node per image/pc.
1057fa18b0b2SFabien Thomas 		 */
1058fa18b0b2SFabien Thomas 		if (args.pa_verbosity >= 2)
1059fa18b0b2SFabien Thomas 			fprintf(args.pa_printfile,
1060fa18b0b2SFabien Thomas 			    "WARNING: inlined function at %p %s in %s\n",
1061fa18b0b2SFabien Thomas 			    (void *)addr, funcname, ffuncname);
1062fa18b0b2SFabien Thomas 
1063fa18b0b2SFabien Thomas 		snprintf(buffer, sizeof(buffer), "%s@%s",
1064fa18b0b2SFabien Thomas 			funcname, ffuncname);
1065fa18b0b2SFabien Thomas 		child = pmcpl_ct_node_hash_lookup(ct->pct_image,
1066fa18b0b2SFabien Thomas 		    ct->pct_func, ct->pct_sym, sourcefile, buffer);
1067fa18b0b2SFabien Thomas 		assert(child != NULL);
1068fa18b0b2SFabien Thomas 		pc = ct->pct_instr[i].pctf_func;
1069fa18b0b2SFabien Thomas 		for (j = 0; j<pmcstat_npmcs; j++) {
1070fa18b0b2SFabien Thomas 			v = PMCPL_CT_SAMPLE(j,
1071fa18b0b2SFabien Thomas 			    &ct->pct_instr[i].pctf_samples);
1072fa18b0b2SFabien Thomas 			if (v == 0)
1073fa18b0b2SFabien Thomas 				continue;
1074fa18b0b2SFabien Thomas 			pmcpl_ct_instr_add(child, j, pc, v);
1075fa18b0b2SFabien Thomas 			pmcpl_ct_node_update(ct, child, j, v, 0);
1076fa18b0b2SFabien Thomas 			if (j < ct->pct_samples.npmcs)
1077fa18b0b2SFabien Thomas 				ct->pct_samples.sb[j] -=
1078fa18b0b2SFabien Thomas 				    ct->pct_instr[i].pctf_samples.sb[j];
1079fa18b0b2SFabien Thomas 			ct->pct_instr[i].pctf_samples.sb[j] = 0;
10800b86b1bbSFabien Thomas 		}
10810b86b1bbSFabien Thomas 	}
10820b86b1bbSFabien Thomas }
10830b86b1bbSFabien Thomas 
1084fa18b0b2SFabien Thomas static void
pmcpl_ct_expand_inline(void)1085fa18b0b2SFabien Thomas pmcpl_ct_expand_inline(void)
1086fa18b0b2SFabien Thomas {
1087fa18b0b2SFabien Thomas 	int i;
1088fa18b0b2SFabien Thomas 	struct pmcpl_ct_node_hash *pch;
1089fa18b0b2SFabien Thomas 
1090fa18b0b2SFabien Thomas 	if (!args.pa_ctdumpinstr)
1091fa18b0b2SFabien Thomas 		return;
1092fa18b0b2SFabien Thomas 
1093fa18b0b2SFabien Thomas 	for (i = 0; i < PMCSTAT_NHASH; i++)
1094fa18b0b2SFabien Thomas 		STAILQ_FOREACH(pch, &pmcpl_ct_node_hash[i], pch_next)
1095fa18b0b2SFabien Thomas 			if (pch->pch_ctnode->pct_type == PMCPL_PCT_ADDR)
1096fa18b0b2SFabien Thomas 				_pmcpl_ct_expand_inline(pch->pch_ctnode);
1097fa18b0b2SFabien Thomas }
1098fa18b0b2SFabien Thomas 
10990b86b1bbSFabien Thomas /*
11000b86b1bbSFabien Thomas  * Clean the PMC name for Kcachegrind formula
11010b86b1bbSFabien Thomas  */
11020b86b1bbSFabien Thomas 
11030b86b1bbSFabien Thomas static void
pmcpl_ct_fixup_pmcname(char * s)11040b86b1bbSFabien Thomas pmcpl_ct_fixup_pmcname(char *s)
11050b86b1bbSFabien Thomas {
11060b86b1bbSFabien Thomas 	char *p;
11070b86b1bbSFabien Thomas 
11080b86b1bbSFabien Thomas 	for (p = s; *p; p++)
11090b86b1bbSFabien Thomas 		if (!isalnum(*p))
11100b86b1bbSFabien Thomas 			*p = '_';
11110b86b1bbSFabien Thomas }
11120b86b1bbSFabien Thomas 
11130b86b1bbSFabien Thomas /*
11140b86b1bbSFabien Thomas  * Print a calltree (KCachegrind) for all PMCs.
11150b86b1bbSFabien Thomas  */
11160b86b1bbSFabien Thomas 
11170b86b1bbSFabien Thomas static void
pmcpl_ct_print(void)11180b86b1bbSFabien Thomas pmcpl_ct_print(void)
11190b86b1bbSFabien Thomas {
1120fa18b0b2SFabien Thomas 	int i;
11210b86b1bbSFabien Thomas 	char name[40];
1122fa18b0b2SFabien Thomas 	struct pmcpl_ct_sample rsamples;
11230b86b1bbSFabien Thomas 
11240b86b1bbSFabien Thomas 	pmcpl_ct_samples_root(&rsamples);
1125fa18b0b2SFabien Thomas 	pmcpl_ct_expand_inline();
11260b86b1bbSFabien Thomas 
11270b86b1bbSFabien Thomas 	fprintf(args.pa_graphfile,
11280b86b1bbSFabien Thomas 		"version: 1\n"
11290b86b1bbSFabien Thomas 		"creator: pmcstat\n"
11300b86b1bbSFabien Thomas 		"positions: instr line\n"
11310b86b1bbSFabien Thomas 		"events:");
11320b86b1bbSFabien Thomas 	for (i=0; i<pmcstat_npmcs; i++) {
11330b86b1bbSFabien Thomas 		snprintf(name, sizeof(name), "%s_%d",
11340b86b1bbSFabien Thomas 		    pmcstat_pmcindex_to_name(i), i);
11350b86b1bbSFabien Thomas 		pmcpl_ct_fixup_pmcname(name);
11360b86b1bbSFabien Thomas 		fprintf(args.pa_graphfile, " %s", name);
11370b86b1bbSFabien Thomas 	}
11380b86b1bbSFabien Thomas 	fprintf(args.pa_graphfile, "\nsummary:");
11390b86b1bbSFabien Thomas 	for (i=0; i<pmcstat_npmcs ; i++)
11400b86b1bbSFabien Thomas 		fprintf(args.pa_graphfile, " %u",
11410b86b1bbSFabien Thomas 		    PMCPL_CT_SAMPLE(i, &rsamples));
11420b86b1bbSFabien Thomas 	fprintf(args.pa_graphfile, "\n");
1143fa18b0b2SFabien Thomas 	pmcpl_ct_bfs(pmcpl_ct_root);
11440b86b1bbSFabien Thomas 	pmcpl_ct_samples_free(&rsamples);
11450b86b1bbSFabien Thomas }
11460b86b1bbSFabien Thomas 
11470b86b1bbSFabien Thomas int
pmcpl_ct_configure(char * opt)11480b86b1bbSFabien Thomas pmcpl_ct_configure(char *opt)
11490b86b1bbSFabien Thomas {
11500b86b1bbSFabien Thomas 
11510b86b1bbSFabien Thomas 	if (strncmp(opt, "skiplink=", 9) == 0) {
11520b86b1bbSFabien Thomas 		pmcstat_skiplink = atoi(opt+9);
11530b86b1bbSFabien Thomas 	} else
11540b86b1bbSFabien Thomas 		return (0);
11550b86b1bbSFabien Thomas 
11560b86b1bbSFabien Thomas 	return (1);
11570b86b1bbSFabien Thomas }
11580b86b1bbSFabien Thomas 
11590b86b1bbSFabien Thomas int
pmcpl_ct_init(void)11600b86b1bbSFabien Thomas pmcpl_ct_init(void)
11610b86b1bbSFabien Thomas {
11620b86b1bbSFabien Thomas 	int i;
11630b86b1bbSFabien Thomas 
1164fa18b0b2SFabien Thomas 	pmcpl_ct_root = pmcpl_ct_node_allocate();
11650b86b1bbSFabien Thomas 
11660b86b1bbSFabien Thomas 	for (i = 0; i < PMCSTAT_NHASH; i++)
1167fa18b0b2SFabien Thomas 		STAILQ_INIT(&pmcpl_ct_node_hash[i]);
11680b86b1bbSFabien Thomas 
11690b86b1bbSFabien Thomas 	pmcpl_ct_samples_init(&pmcpl_ct_callid);
11700b86b1bbSFabien Thomas 
11710b86b1bbSFabien Thomas 	return (0);
11720b86b1bbSFabien Thomas }
11730b86b1bbSFabien Thomas 
11740b86b1bbSFabien Thomas void
pmcpl_ct_shutdown(FILE * mf)11750b86b1bbSFabien Thomas pmcpl_ct_shutdown(FILE *mf)
11760b86b1bbSFabien Thomas {
11770b86b1bbSFabien Thomas 	int i;
11780b86b1bbSFabien Thomas 	struct pmcpl_ct_node_hash *pch, *pchtmp;
11790b86b1bbSFabien Thomas 
11800b86b1bbSFabien Thomas 	(void) mf;
11810b86b1bbSFabien Thomas 
11820b86b1bbSFabien Thomas 	if (args.pa_flags & FLAG_DO_CALLGRAPHS)
11830b86b1bbSFabien Thomas 		pmcpl_ct_print();
11840b86b1bbSFabien Thomas 
11850b86b1bbSFabien Thomas 	/*
11860b86b1bbSFabien Thomas 	 * Free memory.
11870b86b1bbSFabien Thomas 	 */
11880b86b1bbSFabien Thomas 
11890b86b1bbSFabien Thomas 	for (i = 0; i < PMCSTAT_NHASH; i++) {
1190fa18b0b2SFabien Thomas 		STAILQ_FOREACH_SAFE(pch, &pmcpl_ct_node_hash[i], pch_next,
11910b86b1bbSFabien Thomas 		    pchtmp) {
11920b86b1bbSFabien Thomas 			pmcpl_ct_node_free(pch->pch_ctnode);
11930b86b1bbSFabien Thomas 			free(pch);
11940b86b1bbSFabien Thomas 		}
11950b86b1bbSFabien Thomas 	}
11960b86b1bbSFabien Thomas 
11970b86b1bbSFabien Thomas 	pmcpl_ct_node_free(pmcpl_ct_root);
11980b86b1bbSFabien Thomas 	pmcpl_ct_root = NULL;
11990b86b1bbSFabien Thomas 
12000b86b1bbSFabien Thomas 	pmcpl_ct_samples_free(&pmcpl_ct_callid);
12010b86b1bbSFabien Thomas }
12020b86b1bbSFabien Thomas 
1203