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