1203790Sfabient/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 4233611Sfabient * Copyright (c) 2012, Fabien Thomas 5203790Sfabient * All rights reserved. 6203790Sfabient * 7203790Sfabient * Redistribution and use in source and binary forms, with or without 8203790Sfabient * modification, are permitted provided that the following conditions 9203790Sfabient * are met: 10203790Sfabient * 1. Redistributions of source code must retain the above copyright 11203790Sfabient * notice, this list of conditions and the following disclaimer. 12203790Sfabient * 2. Redistributions in binary form must reproduce the above copyright 13203790Sfabient * notice, this list of conditions and the following disclaimer in the 14203790Sfabient * documentation and/or other materials provided with the distribution. 15203790Sfabient * 16203790Sfabient * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17203790Sfabient * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18203790Sfabient * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19203790Sfabient * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20203790Sfabient * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21203790Sfabient * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22203790Sfabient * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23203790Sfabient * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24203790Sfabient * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25203790Sfabient * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26203790Sfabient * SUCH DAMAGE. 27203790Sfabient */ 28203790Sfabient 29203790Sfabient/* 30203790Sfabient * Process hwpmc(4) samples as calltree. 31203790Sfabient * 32203790Sfabient * Output file format compatible with Kcachegrind (kdesdk). 33203790Sfabient * Handle top mode with a sorted tree display. 34203790Sfabient */ 35203790Sfabient 36203790Sfabient#include <sys/cdefs.h> 37203790Sfabient__FBSDID("$FreeBSD: stable/11/usr.sbin/pmcstat/pmcpl_calltree.c 330449 2018-03-05 07:26:05Z eadler $"); 38203790Sfabient 39203790Sfabient#include <sys/param.h> 40203790Sfabient#include <sys/endian.h> 41203790Sfabient#include <sys/queue.h> 42203790Sfabient 43203790Sfabient#include <assert.h> 44203790Sfabient#include <curses.h> 45203790Sfabient#include <ctype.h> 46203790Sfabient#include <err.h> 47203790Sfabient#include <errno.h> 48203790Sfabient#include <fcntl.h> 49203790Sfabient#include <pmc.h> 50203790Sfabient#include <pmclog.h> 51203790Sfabient#include <stdint.h> 52203790Sfabient#include <stdio.h> 53203790Sfabient#include <stdlib.h> 54203790Sfabient#include <string.h> 55203790Sfabient#include <unistd.h> 56203790Sfabient#include <sysexits.h> 57203790Sfabient 58203790Sfabient#include "pmcstat.h" 59203790Sfabient#include "pmcstat_log.h" 60203790Sfabient#include "pmcstat_top.h" 61203790Sfabient#include "pmcpl_calltree.h" 62203790Sfabient 63233611Sfabient#define PMCPL_CT_GROWSIZE 4 64203790Sfabient 65203790Sfabientstatic int pmcstat_skiplink = 0; 66203790Sfabient 67203790Sfabientstruct pmcpl_ct_node; 68203790Sfabient 69203790Sfabient/* Get the sample value for PMC a. */ 70233611Sfabient#define PMCPL_CT_SAMPLE(a, b) \ 71203790Sfabient ((a) < (b)->npmcs ? (b)->sb[a] : 0) 72203790Sfabient 73203790Sfabient/* Get the sample value in percent related to rsamples. */ 74233611Sfabient#define PMCPL_CT_SAMPLEP(a, b) \ 75203790Sfabient (PMCPL_CT_SAMPLE(a, b) * 100.0 / rsamples->sb[a]) 76203790Sfabient 77203790Sfabientstruct pmcpl_ct_sample { 78203790Sfabient int npmcs; /* Max pmc index available. */ 79203790Sfabient unsigned *sb; /* Sample buffer for 0..npmcs. */ 80203790Sfabient}; 81203790Sfabient 82203790Sfabientstruct pmcpl_ct_arc { 83203790Sfabient struct pmcpl_ct_sample pcta_samples; 84203790Sfabient struct pmcpl_ct_sample pcta_callid; 85203790Sfabient unsigned pcta_call; 86203790Sfabient struct pmcpl_ct_node *pcta_child; 87203790Sfabient}; 88203790Sfabient 89203790Sfabientstruct pmcpl_ct_instr { 90203790Sfabient uintfptr_t pctf_func; 91203790Sfabient struct pmcpl_ct_sample pctf_samples; 92203790Sfabient}; 93203790Sfabient 94203790Sfabient/* 95203790Sfabient * Each calltree node is tracked by a pmcpl_ct_node struct. 96203790Sfabient */ 97203790Sfabientstruct pmcpl_ct_node { 98203790Sfabient struct pmcstat_image *pct_image; 99203790Sfabient uintfptr_t pct_func; 100233611Sfabient 101233611Sfabient struct pmcstat_symbol *pct_sym; 102233611Sfabient pmcstat_interned_string pct_ifl; 103233611Sfabient pmcstat_interned_string pct_ifn; 104233611Sfabient 105203790Sfabient struct pmcpl_ct_sample pct_samples; 106203790Sfabient 107203790Sfabient int pct_narc; 108203790Sfabient int pct_arc_c; 109203790Sfabient struct pmcpl_ct_arc *pct_arc; 110203790Sfabient 111203790Sfabient /* TODO: optimize for large number of items. */ 112203790Sfabient int pct_ninstr; 113203790Sfabient int pct_instr_c; 114203790Sfabient struct pmcpl_ct_instr *pct_instr; 115233611Sfabient 116233611Sfabient#define PMCPL_PCT_ADDR 0 117233611Sfabient#define PMCPL_PCT_NAME 1 118233611Sfabient char pct_type; 119233611Sfabient#define PMCPL_PCT_WHITE 0 120233611Sfabient#define PMCPL_PCT_GREY 1 121233611Sfabient#define PMCPL_PCT_BLACK 2 122233611Sfabient char pct_color; 123203790Sfabient}; 124203790Sfabient 125203790Sfabientstruct pmcpl_ct_node_hash { 126203790Sfabient struct pmcpl_ct_node *pch_ctnode; 127233611Sfabient STAILQ_ENTRY(pmcpl_ct_node_hash) pch_next; 128203790Sfabient}; 129203790Sfabient 130241737Sedstatic struct pmcpl_ct_sample pmcpl_ct_callid; 131203790Sfabient 132233611Sfabient#define PMCPL_CT_MAXCOL PMC_CALLCHAIN_DEPTH_MAX 133233611Sfabient#define PMCPL_CT_MAXLINE 1024 /* TODO: dynamic. */ 134203790Sfabient 135207755Sfabientstruct pmcpl_ct_line { 136207755Sfabient unsigned ln_sum; 137207755Sfabient unsigned ln_index; 138207755Sfabient}; 139207755Sfabient 140241737Sedstatic struct pmcpl_ct_line pmcpl_ct_topmax[PMCPL_CT_MAXLINE+1]; 141241737Sedstatic struct pmcpl_ct_node 142233611Sfabient *pmcpl_ct_topscreen[PMCPL_CT_MAXCOL+1][PMCPL_CT_MAXLINE+1]; 143207755Sfabient 144203790Sfabient/* 145203790Sfabient * All nodes indexed by function/image name are placed in a hash table. 146203790Sfabient */ 147233611Sfabientstatic STAILQ_HEAD(,pmcpl_ct_node_hash) pmcpl_ct_node_hash[PMCSTAT_NHASH]; 148203790Sfabient 149203790Sfabient/* 150203790Sfabient * Root node for the graph. 151203790Sfabient */ 152203790Sfabientstatic struct pmcpl_ct_node *pmcpl_ct_root; 153203790Sfabient 154203790Sfabient/* 155203790Sfabient * Prototypes 156203790Sfabient */ 157203790Sfabient 158203790Sfabient/* 159203790Sfabient * Initialize a samples. 160203790Sfabient */ 161203790Sfabient 162203790Sfabientstatic void 163203790Sfabientpmcpl_ct_samples_init(struct pmcpl_ct_sample *samples) 164203790Sfabient{ 165203790Sfabient 166203790Sfabient samples->npmcs = 0; 167203790Sfabient samples->sb = NULL; 168203790Sfabient} 169203790Sfabient 170203790Sfabient/* 171203790Sfabient * Free a samples. 172203790Sfabient */ 173203790Sfabient 174203790Sfabientstatic void 175203790Sfabientpmcpl_ct_samples_free(struct pmcpl_ct_sample *samples) 176203790Sfabient{ 177203790Sfabient 178203790Sfabient samples->npmcs = 0; 179203790Sfabient free(samples->sb); 180203790Sfabient samples->sb = NULL; 181203790Sfabient} 182203790Sfabient 183203790Sfabient/* 184203790Sfabient * Grow a sample block to store pmcstat_npmcs PMCs. 185203790Sfabient */ 186203790Sfabient 187203790Sfabientstatic void 188203790Sfabientpmcpl_ct_samples_grow(struct pmcpl_ct_sample *samples) 189203790Sfabient{ 190317861Spfg unsigned int npmcs; 191203790Sfabient 192203790Sfabient /* Enough storage. */ 193203790Sfabient if (pmcstat_npmcs <= samples->npmcs) 194203790Sfabient return; 195203790Sfabient 196203790Sfabient npmcs = samples->npmcs + 197203790Sfabient max(pmcstat_npmcs - samples->npmcs, PMCPL_CT_GROWSIZE); 198317861Spfg samples->sb = reallocarray(samples->sb, npmcs, sizeof(unsigned)); 199203790Sfabient if (samples->sb == NULL) 200203790Sfabient errx(EX_SOFTWARE, "ERROR: out of memory"); 201203790Sfabient bzero((char *)samples->sb + samples->npmcs * sizeof(unsigned), 202203790Sfabient (npmcs - samples->npmcs) * sizeof(unsigned)); 203203790Sfabient samples->npmcs = npmcs; 204203790Sfabient} 205203790Sfabient 206203790Sfabient/* 207203790Sfabient * Compute the sum of all root arcs. 208203790Sfabient */ 209203790Sfabient 210203790Sfabientstatic void 211203790Sfabientpmcpl_ct_samples_root(struct pmcpl_ct_sample *samples) 212203790Sfabient{ 213203790Sfabient int i, pmcin; 214203790Sfabient 215203790Sfabient pmcpl_ct_samples_init(samples); 216203790Sfabient pmcpl_ct_samples_grow(samples); 217203790Sfabient 218203790Sfabient for (i = 0; i < pmcpl_ct_root->pct_narc; i++) 219203790Sfabient for (pmcin = 0; pmcin < pmcstat_npmcs; pmcin++) 220203790Sfabient samples->sb[pmcin] += PMCPL_CT_SAMPLE(pmcin, 221203790Sfabient &pmcpl_ct_root->pct_arc[i].pcta_samples); 222203790Sfabient} 223203790Sfabient 224203790Sfabient/* 225203790Sfabient * Grow the arc table. 226203790Sfabient */ 227203790Sfabient 228203790Sfabientstatic void 229203790Sfabientpmcpl_ct_arc_grow(int cursize, int *maxsize, struct pmcpl_ct_arc **items) 230203790Sfabient{ 231317861Spfg unsigned int nmaxsize; 232203790Sfabient 233203790Sfabient if (cursize < *maxsize) 234203790Sfabient return; 235203790Sfabient 236203790Sfabient nmaxsize = *maxsize + max(cursize + 1 - *maxsize, PMCPL_CT_GROWSIZE); 237317861Spfg *items = reallocarray(*items, nmaxsize, sizeof(struct pmcpl_ct_arc)); 238203790Sfabient if (*items == NULL) 239203790Sfabient errx(EX_SOFTWARE, "ERROR: out of memory"); 240203790Sfabient bzero((char *)*items + *maxsize * sizeof(struct pmcpl_ct_arc), 241203790Sfabient (nmaxsize - *maxsize) * sizeof(struct pmcpl_ct_arc)); 242203790Sfabient *maxsize = nmaxsize; 243203790Sfabient} 244203790Sfabient 245203790Sfabient/* 246203790Sfabient * Grow the instr table. 247203790Sfabient */ 248203790Sfabient 249203790Sfabientstatic void 250203790Sfabientpmcpl_ct_instr_grow(int cursize, int *maxsize, struct pmcpl_ct_instr **items) 251203790Sfabient{ 252317861Spfg unsigned int nmaxsize; 253203790Sfabient 254203790Sfabient if (cursize < *maxsize) 255203790Sfabient return; 256203790Sfabient 257203790Sfabient nmaxsize = *maxsize + max(cursize + 1 - *maxsize, PMCPL_CT_GROWSIZE); 258317861Spfg *items = reallocarray(*items, nmaxsize, sizeof(struct pmcpl_ct_instr)); 259203790Sfabient if (*items == NULL) 260203790Sfabient errx(EX_SOFTWARE, "ERROR: out of memory"); 261203790Sfabient bzero((char *)*items + *maxsize * sizeof(struct pmcpl_ct_instr), 262203790Sfabient (nmaxsize - *maxsize) * sizeof(struct pmcpl_ct_instr)); 263203790Sfabient *maxsize = nmaxsize; 264203790Sfabient} 265203790Sfabient 266203790Sfabient/* 267203790Sfabient * Add a new instruction sample to given node. 268203790Sfabient */ 269203790Sfabient 270203790Sfabientstatic void 271233611Sfabientpmcpl_ct_instr_add(struct pmcpl_ct_node *ct, int pmcin, 272233611Sfabient uintfptr_t pc, unsigned v) 273203790Sfabient{ 274203790Sfabient int i; 275203790Sfabient struct pmcpl_ct_instr *in; 276203790Sfabient 277203790Sfabient for (i = 0; i<ct->pct_ninstr; i++) { 278203790Sfabient if (ct->pct_instr[i].pctf_func == pc) { 279203790Sfabient in = &ct->pct_instr[i]; 280203790Sfabient pmcpl_ct_samples_grow(&in->pctf_samples); 281233611Sfabient in->pctf_samples.sb[pmcin] += v; 282203790Sfabient return; 283203790Sfabient } 284203790Sfabient } 285203790Sfabient 286203790Sfabient pmcpl_ct_instr_grow(ct->pct_ninstr, &ct->pct_instr_c, &ct->pct_instr); 287203790Sfabient in = &ct->pct_instr[ct->pct_ninstr]; 288203790Sfabient in->pctf_func = pc; 289203790Sfabient pmcpl_ct_samples_init(&in->pctf_samples); 290203790Sfabient pmcpl_ct_samples_grow(&in->pctf_samples); 291233611Sfabient in->pctf_samples.sb[pmcin] = v; 292203790Sfabient ct->pct_ninstr++; 293203790Sfabient} 294203790Sfabient 295203790Sfabient/* 296203790Sfabient * Allocate a new node. 297203790Sfabient */ 298203790Sfabient 299203790Sfabientstatic struct pmcpl_ct_node * 300233611Sfabientpmcpl_ct_node_allocate(void) 301203790Sfabient{ 302203790Sfabient struct pmcpl_ct_node *ct; 303203790Sfabient 304203790Sfabient if ((ct = malloc(sizeof(*ct))) == NULL) 305203790Sfabient err(EX_OSERR, "ERROR: Cannot allocate callgraph node"); 306203790Sfabient 307203790Sfabient pmcpl_ct_samples_init(&ct->pct_samples); 308203790Sfabient 309233611Sfabient ct->pct_sym = NULL; 310233611Sfabient ct->pct_image = NULL; 311233611Sfabient ct->pct_func = 0; 312233611Sfabient 313203790Sfabient ct->pct_narc = 0; 314203790Sfabient ct->pct_arc_c = 0; 315203790Sfabient ct->pct_arc = NULL; 316203790Sfabient 317203790Sfabient ct->pct_ninstr = 0; 318203790Sfabient ct->pct_instr_c = 0; 319203790Sfabient ct->pct_instr = NULL; 320203790Sfabient 321233611Sfabient ct->pct_color = PMCPL_PCT_WHITE; 322233611Sfabient 323203790Sfabient return (ct); 324203790Sfabient} 325203790Sfabient 326203790Sfabient/* 327203790Sfabient * Free a node. 328203790Sfabient */ 329203790Sfabient 330203790Sfabientstatic void 331203790Sfabientpmcpl_ct_node_free(struct pmcpl_ct_node *ct) 332203790Sfabient{ 333203790Sfabient int i; 334203790Sfabient 335203790Sfabient for (i = 0; i < ct->pct_narc; i++) { 336203790Sfabient pmcpl_ct_samples_free(&ct->pct_arc[i].pcta_samples); 337203790Sfabient pmcpl_ct_samples_free(&ct->pct_arc[i].pcta_callid); 338203790Sfabient } 339203790Sfabient 340203790Sfabient pmcpl_ct_samples_free(&ct->pct_samples); 341203790Sfabient free(ct->pct_arc); 342203790Sfabient free(ct->pct_instr); 343203790Sfabient free(ct); 344203790Sfabient} 345203790Sfabient 346203790Sfabient/* 347203790Sfabient * Clear the graph tag on each node. 348203790Sfabient */ 349203790Sfabientstatic void 350203790Sfabientpmcpl_ct_node_cleartag(void) 351203790Sfabient{ 352203790Sfabient int i; 353203790Sfabient struct pmcpl_ct_node_hash *pch; 354203790Sfabient 355203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) 356233611Sfabient STAILQ_FOREACH(pch, &pmcpl_ct_node_hash[i], pch_next) 357233611Sfabient pch->pch_ctnode->pct_color = PMCPL_PCT_WHITE; 358203790Sfabient 359233611Sfabient pmcpl_ct_root->pct_color = PMCPL_PCT_WHITE; 360203790Sfabient} 361203790Sfabient 362203790Sfabient/* 363203790Sfabient * Print the callchain line by line with maximum cost at top. 364203790Sfabient */ 365203790Sfabient 366203790Sfabientstatic int 367203790Sfabientpmcpl_ct_node_dumptop(int pmcin, struct pmcpl_ct_node *ct, 368207755Sfabient struct pmcpl_ct_sample *rsamples, int x, int *y) 369203790Sfabient{ 370207755Sfabient int i, terminal; 371210766Sfabient struct pmcpl_ct_arc *arc; 372203790Sfabient 373233611Sfabient if (ct->pct_color == PMCPL_PCT_GREY) 374203790Sfabient return 0; 375203790Sfabient 376203790Sfabient if (x >= PMCPL_CT_MAXCOL) { 377203790Sfabient pmcpl_ct_topscreen[x][*y] = NULL; 378203790Sfabient return 1; 379203790Sfabient } 380203790Sfabient pmcpl_ct_topscreen[x][*y] = ct; 381203790Sfabient 382203790Sfabient /* 383207755Sfabient * Check if this is a terminal node. 384207755Sfabient * We need to check that some samples exist 385207755Sfabient * for at least one arc for that PMC. 386203790Sfabient */ 387207755Sfabient terminal = 1; 388210766Sfabient for (i = 0; i < ct->pct_narc; i++) { 389210766Sfabient arc = &ct->pct_arc[i]; 390233611Sfabient if (arc->pcta_child->pct_color != PMCPL_PCT_GREY && 391233611Sfabient PMCPL_CT_SAMPLE(pmcin, 392210766Sfabient &arc->pcta_samples) != 0 && 393210766Sfabient PMCPL_CT_SAMPLEP(pmcin, 394233611Sfabient &arc->pcta_samples) > pmcstat_threshold) { 395207755Sfabient terminal = 0; 396207755Sfabient break; 397207755Sfabient } 398210766Sfabient } 399207755Sfabient 400207755Sfabient if (ct->pct_narc == 0 || terminal) { 401203790Sfabient pmcpl_ct_topscreen[x+1][*y] = NULL; 402207755Sfabient if (*y >= PMCPL_CT_MAXLINE) 403203790Sfabient return 1; 404203790Sfabient *y = *y + 1; 405203790Sfabient for (i=0; i < x; i++) 406203790Sfabient pmcpl_ct_topscreen[i][*y] = 407203790Sfabient pmcpl_ct_topscreen[i][*y - 1]; 408203790Sfabient return 0; 409203790Sfabient } 410203790Sfabient 411233611Sfabient ct->pct_color = PMCPL_PCT_GREY; 412203790Sfabient for (i = 0; i < ct->pct_narc; i++) { 413206090Sfabient if (PMCPL_CT_SAMPLE(pmcin, 414206090Sfabient &ct->pct_arc[i].pcta_samples) == 0) 415206090Sfabient continue; 416203790Sfabient if (PMCPL_CT_SAMPLEP(pmcin, 417203790Sfabient &ct->pct_arc[i].pcta_samples) > pmcstat_threshold) { 418203790Sfabient if (pmcpl_ct_node_dumptop(pmcin, 419203790Sfabient ct->pct_arc[i].pcta_child, 420233611Sfabient rsamples, x+1, y)) { 421233611Sfabient ct->pct_color = PMCPL_PCT_BLACK; 422203790Sfabient return 1; 423233611Sfabient } 424203790Sfabient } 425203790Sfabient } 426233611Sfabient ct->pct_color = PMCPL_PCT_BLACK; 427203790Sfabient 428203790Sfabient return 0; 429203790Sfabient} 430203790Sfabient 431203790Sfabient/* 432207755Sfabient * Compare two top line by sum. 433207755Sfabient */ 434207755Sfabientstatic int 435207755Sfabientpmcpl_ct_line_compare(const void *a, const void *b) 436207755Sfabient{ 437207755Sfabient const struct pmcpl_ct_line *ct1, *ct2; 438207755Sfabient 439207755Sfabient ct1 = (const struct pmcpl_ct_line *) a; 440207755Sfabient ct2 = (const struct pmcpl_ct_line *) b; 441207755Sfabient 442207755Sfabient /* Sort in reverse order */ 443207755Sfabient if (ct1->ln_sum < ct2->ln_sum) 444207755Sfabient return (1); 445207755Sfabient if (ct1->ln_sum > ct2->ln_sum) 446207755Sfabient return (-1); 447207755Sfabient return (0); 448207755Sfabient} 449207755Sfabient 450207755Sfabient/* 451203790Sfabient * Format and display given PMC index. 452203790Sfabient */ 453203790Sfabient 454203790Sfabientstatic void 455203790Sfabientpmcpl_ct_node_printtop(struct pmcpl_ct_sample *rsamples, int pmcin, int maxy) 456203790Sfabient{ 457207755Sfabient#undef TS 458207755Sfabient#undef TSI 459207755Sfabient#define TS(x, y) (pmcpl_ct_topscreen[x][y]) 460207755Sfabient#define TSI(x, y) (pmcpl_ct_topscreen[x][pmcpl_ct_topmax[y].ln_index]) 461207755Sfabient 462203790Sfabient int v_attrs, ns_len, vs_len, is_len, width, indentwidth, x, y; 463203790Sfabient float v; 464203790Sfabient char ns[30], vs[10], is[20]; 465203790Sfabient struct pmcpl_ct_node *ct; 466203790Sfabient const char *space = " "; 467203790Sfabient 468207755Sfabient /* 469207755Sfabient * Sort by line cost. 470207755Sfabient */ 471207755Sfabient for (y = 0; ; y++) { 472207755Sfabient ct = TS(1, y); 473207755Sfabient if (ct == NULL) 474207755Sfabient break; 475207755Sfabient 476207755Sfabient pmcpl_ct_topmax[y].ln_sum = 0; 477207755Sfabient pmcpl_ct_topmax[y].ln_index = y; 478207755Sfabient for (x = 1; TS(x, y) != NULL; x++) { 479207755Sfabient pmcpl_ct_topmax[y].ln_sum += 480207755Sfabient PMCPL_CT_SAMPLE(pmcin, &TS(x, y)->pct_samples); 481207755Sfabient } 482207755Sfabient } 483207755Sfabient qsort(pmcpl_ct_topmax, y, sizeof(pmcpl_ct_topmax[0]), 484207755Sfabient pmcpl_ct_line_compare); 485207755Sfabient pmcpl_ct_topmax[y].ln_index = y; 486207755Sfabient 487203790Sfabient for (y = 0; y < maxy; y++) { 488207755Sfabient ct = TSI(1, y); 489207755Sfabient if (ct == NULL) 490207755Sfabient break; 491203790Sfabient 492207755Sfabient if (y > 0) 493207755Sfabient PMCSTAT_PRINTW("\n"); 494203790Sfabient 495207755Sfabient /* Output sum. */ 496207755Sfabient v = pmcpl_ct_topmax[y].ln_sum * 100.0 / 497207755Sfabient rsamples->sb[pmcin]; 498207755Sfabient snprintf(vs, sizeof(vs), "%.1f", v); 499207755Sfabient v_attrs = PMCSTAT_ATTRPERCENT(v); 500207755Sfabient PMCSTAT_ATTRON(v_attrs); 501207755Sfabient PMCSTAT_PRINTW("%5.5s ", vs); 502207755Sfabient PMCSTAT_ATTROFF(v_attrs); 503203790Sfabient 504207755Sfabient width = indentwidth = 5 + 1; 505207755Sfabient 506207755Sfabient for (x = 1; (ct = TSI(x, y)) != NULL; x++) { 507207755Sfabient 508203790Sfabient vs[0] = '\0'; vs_len = 0; 509203790Sfabient is[0] = '\0'; is_len = 0; 510203790Sfabient 511203790Sfabient /* Format value. */ 512203790Sfabient v = PMCPL_CT_SAMPLEP(pmcin, &ct->pct_samples); 513203790Sfabient if (v > pmcstat_threshold) 514207755Sfabient vs_len = snprintf(vs, sizeof(vs), 515207755Sfabient "(%.1f%%)", v); 516203790Sfabient v_attrs = PMCSTAT_ATTRPERCENT(v); 517203790Sfabient 518203790Sfabient if (pmcstat_skiplink && v <= pmcstat_threshold) { 519207755Sfabient strlcpy(ns, ".", sizeof(ns)); 520207755Sfabient ns_len = 1; 521207755Sfabient } else { 522233611Sfabient if (ct->pct_sym != NULL) { 523203790Sfabient ns_len = snprintf(ns, sizeof(ns), "%s", 524233611Sfabient pmcstat_string_unintern(ct->pct_sym->ps_name)); 525203790Sfabient } else 526203790Sfabient ns_len = snprintf(ns, sizeof(ns), "%p", 527203790Sfabient (void *)ct->pct_func); 528203790Sfabient 529203790Sfabient /* Format image. */ 530207755Sfabient if (x == 1 || 531207755Sfabient TSI(x-1, y)->pct_image != ct->pct_image) 532203790Sfabient is_len = snprintf(is, sizeof(is), "@%s", 533203790Sfabient pmcstat_string_unintern(ct->pct_image->pi_name)); 534203790Sfabient 535203790Sfabient /* Check for line wrap. */ 536203790Sfabient width += ns_len + is_len + vs_len + 1; 537207755Sfabient } 538203790Sfabient if (width >= pmcstat_displaywidth) { 539205693Sfabient maxy--; 540205693Sfabient if (y >= maxy) 541205693Sfabient break; 542203790Sfabient PMCSTAT_PRINTW("\n%*s", indentwidth, space); 543203790Sfabient width = indentwidth + ns_len + is_len + vs_len; 544203790Sfabient } 545203790Sfabient 546203790Sfabient PMCSTAT_ATTRON(v_attrs); 547203790Sfabient PMCSTAT_PRINTW("%s%s%s ", ns, is, vs); 548203790Sfabient PMCSTAT_ATTROFF(v_attrs); 549203790Sfabient } 550203790Sfabient } 551203790Sfabient} 552203790Sfabient 553203790Sfabient/* 554203790Sfabient * Output top mode snapshot. 555203790Sfabient */ 556203790Sfabient 557203790Sfabientvoid 558203790Sfabientpmcpl_ct_topdisplay(void) 559203790Sfabient{ 560207755Sfabient int y; 561206994Sfabient struct pmcpl_ct_sample r, *rsamples; 562203790Sfabient 563206994Sfabient rsamples = &r; 564206994Sfabient pmcpl_ct_samples_root(rsamples); 565207755Sfabient pmcpl_ct_node_cleartag(); 566203790Sfabient 567207755Sfabient PMCSTAT_PRINTW("%5.5s %s\n", "%SAMP", "CALLTREE"); 568203790Sfabient 569207755Sfabient y = 0; 570207755Sfabient if (pmcpl_ct_node_dumptop(pmcstat_pmcinfilter, 571207755Sfabient pmcpl_ct_root, rsamples, 0, &y)) 572207755Sfabient PMCSTAT_PRINTW("...\n"); 573207755Sfabient pmcpl_ct_topscreen[1][y] = NULL; 574203790Sfabient 575207755Sfabient pmcpl_ct_node_printtop(rsamples, 576207755Sfabient pmcstat_pmcinfilter, pmcstat_displayheight - 2); 577203790Sfabient 578206994Sfabient pmcpl_ct_samples_free(rsamples); 579203790Sfabient} 580203790Sfabient 581203790Sfabient/* 582203790Sfabient * Handle top mode keypress. 583203790Sfabient */ 584203790Sfabient 585203790Sfabientint 586203790Sfabientpmcpl_ct_topkeypress(int c, WINDOW *w) 587203790Sfabient{ 588203790Sfabient 589203790Sfabient switch (c) { 590203790Sfabient case 'f': 591203790Sfabient pmcstat_skiplink = !pmcstat_skiplink; 592227524Sobrien wprintw(w, "skip empty link %s", 593227524Sobrien pmcstat_skiplink ? "on" : "off"); 594203790Sfabient break; 595203790Sfabient } 596203790Sfabient 597203790Sfabient return 0; 598203790Sfabient} 599203790Sfabient 600203790Sfabient/* 601203790Sfabient * Look for a callgraph node associated with pmc `pmcid' in the global 602203790Sfabient * hash table that corresponds to the given `pc' value in the process map 603203790Sfabient * `ppm'. 604203790Sfabient */ 605203790Sfabient 606233611Sfabientstatic void 607233611Sfabientpmcpl_ct_node_update(struct pmcpl_ct_node *parent, 608233611Sfabient struct pmcpl_ct_node *child, int pmcin, unsigned v, int cd) 609203790Sfabient{ 610203790Sfabient struct pmcpl_ct_arc *arc; 611203790Sfabient int i; 612203790Sfabient 613203790Sfabient assert(parent != NULL); 614203790Sfabient 615233611Sfabient /* 616233611Sfabient * Find related arc in parent node and 617233611Sfabient * increment the sample count. 618233611Sfabient */ 619233611Sfabient for (i = 0; i < parent->pct_narc; i++) { 620233611Sfabient if (parent->pct_arc[i].pcta_child == child) { 621233611Sfabient arc = &parent->pct_arc[i]; 622233611Sfabient pmcpl_ct_samples_grow(&arc->pcta_samples); 623233611Sfabient arc->pcta_samples.sb[pmcin] += v; 624233611Sfabient /* Estimate call count. */ 625233611Sfabient if (cd) { 626233611Sfabient pmcpl_ct_samples_grow(&arc->pcta_callid); 627233611Sfabient if (pmcpl_ct_callid.sb[pmcin] - 628233611Sfabient arc->pcta_callid.sb[pmcin] > 1) 629233611Sfabient arc->pcta_call++; 630233611Sfabient arc->pcta_callid.sb[pmcin] = 631233611Sfabient pmcpl_ct_callid.sb[pmcin]; 632233611Sfabient } 633233611Sfabient return; 634233611Sfabient } 635233611Sfabient } 636203790Sfabient 637203790Sfabient /* 638233611Sfabient * No arc found for us, add ourself to the parent. 639203790Sfabient */ 640233611Sfabient pmcpl_ct_arc_grow(parent->pct_narc, 641233611Sfabient &parent->pct_arc_c, &parent->pct_arc); 642233611Sfabient arc = &parent->pct_arc[parent->pct_narc]; 643233611Sfabient pmcpl_ct_samples_grow(&arc->pcta_samples); 644233611Sfabient arc->pcta_samples.sb[pmcin] = v; 645233611Sfabient arc->pcta_call = 1; 646233611Sfabient if (cd) { 647233611Sfabient pmcpl_ct_samples_grow(&arc->pcta_callid); 648233611Sfabient arc->pcta_callid.sb[pmcin] = pmcpl_ct_callid.sb[pmcin]; 649233611Sfabient } 650233611Sfabient arc->pcta_child = child; 651233611Sfabient parent->pct_narc++; 652233611Sfabient} 653203790Sfabient 654233611Sfabient/* 655233611Sfabient * Lookup by image/pc. 656233611Sfabient */ 657233611Sfabient 658233611Sfabientstatic struct pmcpl_ct_node * 659233611Sfabientpmcpl_ct_node_hash_lookup(struct pmcstat_image *image, uintfptr_t pc, 660233611Sfabient struct pmcstat_symbol *sym, char *fl, char *fn) 661233611Sfabient{ 662233611Sfabient int i; 663233611Sfabient unsigned int hash; 664233611Sfabient struct pmcpl_ct_node *ct; 665233611Sfabient struct pmcpl_ct_node_hash *h; 666233611Sfabient pmcstat_interned_string ifl, ifn; 667233611Sfabient 668233611Sfabient if (fn != NULL) { 669233611Sfabient ifl = pmcstat_string_intern(fl); 670233611Sfabient ifn = pmcstat_string_intern(fn); 671233611Sfabient } else { 672233611Sfabient ifl = 0; 673233611Sfabient ifn = 0; 674233611Sfabient } 675233611Sfabient 676203790Sfabient for (hash = i = 0; i < (int)sizeof(uintfptr_t); i++) 677203790Sfabient hash += (pc >> i) & 0xFF; 678203790Sfabient 679203790Sfabient hash &= PMCSTAT_HASH_MASK; 680203790Sfabient 681233611Sfabient STAILQ_FOREACH(h, &pmcpl_ct_node_hash[hash], pch_next) { 682203790Sfabient ct = h->pch_ctnode; 683203790Sfabient 684203790Sfabient assert(ct != NULL); 685203790Sfabient 686203790Sfabient if (ct->pct_image == image && ct->pct_func == pc) { 687233611Sfabient if (fn == NULL) 688233611Sfabient return (ct); 689233611Sfabient if (ct->pct_type == PMCPL_PCT_NAME && 690233611Sfabient ct->pct_ifl == ifl && ct->pct_ifn == ifn) 691233611Sfabient return (ct); 692203790Sfabient } 693203790Sfabient } 694203790Sfabient 695203790Sfabient /* 696203790Sfabient * We haven't seen this (pmcid, pc) tuple yet, so allocate a 697203790Sfabient * new callgraph node and a new hash table entry for it. 698203790Sfabient */ 699233611Sfabient ct = pmcpl_ct_node_allocate(); 700203790Sfabient if ((h = malloc(sizeof(*h))) == NULL) 701203790Sfabient err(EX_OSERR, "ERROR: Could not allocate callgraph node"); 702203790Sfabient 703233611Sfabient if (fn != NULL) { 704233611Sfabient ct->pct_type = PMCPL_PCT_NAME; 705233611Sfabient ct->pct_ifl = ifl; 706233611Sfabient ct->pct_ifn = ifn; 707233611Sfabient } else 708233611Sfabient ct->pct_type = PMCPL_PCT_ADDR; 709233611Sfabient ct->pct_image = image; 710233611Sfabient ct->pct_func = pc; 711233611Sfabient ct->pct_sym = sym; 712233611Sfabient 713203790Sfabient h->pch_ctnode = ct; 714233611Sfabient STAILQ_INSERT_HEAD(&pmcpl_ct_node_hash[hash], h, pch_next); 715203790Sfabient return (ct); 716203790Sfabient} 717203790Sfabient 718203790Sfabient/* 719203790Sfabient * Record a callchain. 720203790Sfabient */ 721203790Sfabient 722203790Sfabientvoid 723203790Sfabientpmcpl_ct_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 724203790Sfabient uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 725203790Sfabient{ 726233611Sfabient int i, n, pmcin; 727233611Sfabient uintfptr_t pc, loadaddress; 728233611Sfabient struct pmcstat_image *image; 729233611Sfabient struct pmcstat_symbol *sym; 730203790Sfabient struct pmcstat_pcmap *ppm[PMC_CALLCHAIN_DEPTH_MAX]; 731203790Sfabient struct pmcstat_process *km; 732233611Sfabient struct pmcpl_ct_node *ct; 733233611Sfabient struct pmcpl_ct_node *ctl[PMC_CALLCHAIN_DEPTH_MAX+1]; 734203790Sfabient 735203790Sfabient (void) cpu; 736203790Sfabient 737203790Sfabient assert(nsamples>0 && nsamples<=PMC_CALLCHAIN_DEPTH_MAX); 738203790Sfabient 739203790Sfabient /* Get the PMC index. */ 740203790Sfabient pmcin = pmcr->pr_pmcin; 741203790Sfabient 742203790Sfabient /* 743203790Sfabient * Validate mapping for the callchain. 744203790Sfabient * Go from bottom to first invalid entry. 745203790Sfabient */ 746203790Sfabient km = pmcstat_kernproc; 747203790Sfabient for (n = 0; n < (int)nsamples; n++) { 748203790Sfabient ppm[n] = pmcstat_process_find_map(usermode ? 749203790Sfabient pp : km, cc[n]); 750203790Sfabient if (ppm[n] == NULL) { 751203790Sfabient /* Detect full frame capture (kernel + user). */ 752203790Sfabient if (!usermode) { 753203790Sfabient ppm[n] = pmcstat_process_find_map(pp, cc[n]); 754203790Sfabient if (ppm[n] != NULL) 755203790Sfabient km = pp; 756203790Sfabient } 757203790Sfabient } 758203790Sfabient if (ppm[n] == NULL) 759203790Sfabient break; 760203790Sfabient } 761203790Sfabient if (n-- == 0) { 762203790Sfabient pmcstat_stats.ps_callchain_dubious_frames++; 763206090Sfabient pmcr->pr_dubious_frames++; 764203790Sfabient return; 765203790Sfabient } 766203790Sfabient 767203790Sfabient /* Increase the call generation counter. */ 768203790Sfabient pmcpl_ct_samples_grow(&pmcpl_ct_callid); 769203790Sfabient pmcpl_ct_callid.sb[pmcin]++; 770203790Sfabient 771203790Sfabient /* 772233611Sfabient * Build node list. 773203790Sfabient */ 774233611Sfabient ctl[0] = pmcpl_ct_root; 775233611Sfabient for (i = 1; n >= 0; n--) { 776233611Sfabient image = ppm[n]->ppm_image; 777233611Sfabient loadaddress = ppm[n]->ppm_lowpc + 778233611Sfabient image->pi_vaddr - image->pi_start; 779233611Sfabient /* Convert to an offset in the image. */ 780233611Sfabient pc = cc[n] - loadaddress; 781233611Sfabient /* 782233611Sfabient * Try determine the function at this offset. If we can't 783233611Sfabient * find a function round leave the `pc' value alone. 784233611Sfabient */ 785233611Sfabient if ((sym = pmcstat_symbol_search(image, pc)) != NULL) 786233611Sfabient pc = sym->ps_start; 787233611Sfabient else 788233611Sfabient pmcstat_stats.ps_samples_unknown_function++; 789233611Sfabient 790233611Sfabient ct = pmcpl_ct_node_hash_lookup(image, pc, sym, NULL, NULL); 791233611Sfabient if (ct == NULL) { 792203790Sfabient pmcstat_stats.ps_callchain_dubious_frames++; 793203790Sfabient continue; 794203790Sfabient } 795233611Sfabient ctl[i++] = ct; 796203790Sfabient } 797233611Sfabient /* No valid node found. */ 798233611Sfabient if (i == 1) 799233611Sfabient return; 800233611Sfabient n = i; 801203790Sfabient 802233611Sfabient ct = ctl[0]; 803233611Sfabient for (i = 1; i < n; i++) 804233611Sfabient pmcpl_ct_node_update(ctl[i-1], ctl[i], pmcin, 1, 1); 805233611Sfabient 806203790Sfabient /* 807203790Sfabient * Increment the sample count for this PMC. 808203790Sfabient */ 809233611Sfabient pmcpl_ct_samples_grow(&ctl[n-1]->pct_samples); 810233611Sfabient ctl[n-1]->pct_samples.sb[pmcin]++; 811203790Sfabient 812233611Sfabient /* Update per instruction sample if required. */ 813233611Sfabient if (args.pa_ctdumpinstr) 814233611Sfabient pmcpl_ct_instr_add(ctl[n-1], pmcin, cc[0] - 815233611Sfabient (ppm[0]->ppm_lowpc + ppm[0]->ppm_image->pi_vaddr - 816233611Sfabient ppm[0]->ppm_image->pi_start), 1); 817233611Sfabient} 818233611Sfabient 819233611Sfabient/* 820233611Sfabient * Print node child cost. 821233611Sfabient */ 822233611Sfabient 823233611Sfabientstatic void 824233611Sfabientpmcpl_ct_node_printchild(struct pmcpl_ct_node *ct, uintfptr_t paddr, 825233611Sfabient int pline) 826233611Sfabient{ 827233611Sfabient int i, j, line; 828233611Sfabient uintfptr_t addr; 829233611Sfabient struct pmcpl_ct_node *child; 830233611Sfabient char sourcefile[PATH_MAX]; 831233611Sfabient char funcname[PATH_MAX]; 832233611Sfabient 833233611Sfabient /* 834233611Sfabient * Child cost. 835298885Spfg * TODO: attach child cost to the real position in the function. 836233611Sfabient * TODO: cfn=<fn> / call <ncall> addr(<fn>) / addr(call <fn>) <arccost> 837233611Sfabient */ 838233611Sfabient for (i=0 ; i<ct->pct_narc; i++) { 839233611Sfabient child = ct->pct_arc[i].pcta_child; 840233611Sfabient /* Object binary. */ 841233611Sfabient fprintf(args.pa_graphfile, "cob=%s\n", 842233611Sfabient pmcstat_string_unintern(child->pct_image->pi_fullpath)); 843233611Sfabient /* Child function name. */ 844233611Sfabient addr = child->pct_image->pi_vaddr + child->pct_func; 845233611Sfabient line = 0; 846233611Sfabient /* Child function source file. */ 847233611Sfabient if (child->pct_type == PMCPL_PCT_NAME) { 848233611Sfabient fprintf(args.pa_graphfile, "cfi=%s\ncfn=%s\n", 849233611Sfabient pmcstat_string_unintern(child->pct_ifl), 850233611Sfabient pmcstat_string_unintern(child->pct_ifn)); 851233611Sfabient } else if (pmcstat_image_addr2line(child->pct_image, addr, 852233611Sfabient sourcefile, sizeof(sourcefile), &line, 853233611Sfabient funcname, sizeof(funcname))) { 854233611Sfabient fprintf(args.pa_graphfile, "cfi=%s\ncfn=%s\n", 855233611Sfabient sourcefile, funcname); 856233611Sfabient } else { 857233611Sfabient if (child->pct_sym != NULL) 858233611Sfabient fprintf(args.pa_graphfile, 859233611Sfabient "cfi=???\ncfn=%s\n", 860233611Sfabient pmcstat_string_unintern( 861233611Sfabient child->pct_sym->ps_name)); 862233611Sfabient else 863233611Sfabient fprintf(args.pa_graphfile, 864233611Sfabient "cfi=???\ncfn=%p\n", (void *)addr); 865233611Sfabient } 866233611Sfabient 867233611Sfabient /* Child function address, line and call count. */ 868233611Sfabient fprintf(args.pa_graphfile, "calls=%u %p %u\n", 869233611Sfabient ct->pct_arc[i].pcta_call, (void *)addr, line); 870233611Sfabient 871233611Sfabient /* 872233611Sfabient * Call address, line, sample. 873233611Sfabient * TODO: Associate call address to the right location. 874233611Sfabient */ 875233611Sfabient fprintf(args.pa_graphfile, "%p %u", (void *)paddr, pline); 876233611Sfabient for (j = 0; j<pmcstat_npmcs; j++) 877233611Sfabient fprintf(args.pa_graphfile, " %u", 878233611Sfabient PMCPL_CT_SAMPLE(j, &ct->pct_arc[i].pcta_samples)); 879233611Sfabient fprintf(args.pa_graphfile, "\n"); 880203790Sfabient } 881203790Sfabient} 882203790Sfabient 883203790Sfabient/* 884203790Sfabient * Print node self cost. 885203790Sfabient */ 886203790Sfabient 887203790Sfabientstatic void 888203790Sfabientpmcpl_ct_node_printself(struct pmcpl_ct_node *ct) 889203790Sfabient{ 890233611Sfabient int i, j, fline, line; 891233611Sfabient uintfptr_t faddr, addr; 892203790Sfabient char sourcefile[PATH_MAX]; 893203790Sfabient char funcname[PATH_MAX]; 894203790Sfabient 895203790Sfabient /* 896203790Sfabient * Object binary. 897203790Sfabient */ 898233611Sfabient fprintf(args.pa_graphfile, "ob=%s\n", 899233611Sfabient pmcstat_string_unintern(ct->pct_image->pi_fullpath)); 900203790Sfabient 901203790Sfabient /* 902203790Sfabient * Function name. 903203790Sfabient */ 904233611Sfabient faddr = ct->pct_image->pi_vaddr + ct->pct_func; 905233611Sfabient fline = 0; 906233611Sfabient if (ct->pct_type == PMCPL_PCT_NAME) { 907233611Sfabient fprintf(args.pa_graphfile, "fl=%s\nfn=%s\n", 908233611Sfabient pmcstat_string_unintern(ct->pct_ifl), 909233611Sfabient pmcstat_string_unintern(ct->pct_ifn)); 910233611Sfabient } else if (pmcstat_image_addr2line(ct->pct_image, faddr, 911233611Sfabient sourcefile, sizeof(sourcefile), &fline, 912203790Sfabient funcname, sizeof(funcname))) { 913233611Sfabient fprintf(args.pa_graphfile, "fl=%s\nfn=%s\n", 914233611Sfabient sourcefile, funcname); 915203790Sfabient } else { 916233611Sfabient if (ct->pct_sym != NULL) 917233611Sfabient fprintf(args.pa_graphfile, "fl=???\nfn=%s\n", 918233611Sfabient pmcstat_string_unintern(ct->pct_sym->ps_name)); 919203790Sfabient else 920233611Sfabient fprintf(args.pa_graphfile, "fl=???\nfn=%p\n", 921203790Sfabient (void *)(ct->pct_image->pi_vaddr + ct->pct_func)); 922203790Sfabient } 923203790Sfabient 924203790Sfabient /* 925203790Sfabient * Self cost. 926203790Sfabient */ 927203790Sfabient if (ct->pct_ninstr > 0) { 928233611Sfabient /* 929233611Sfabient * Per location cost. 930233611Sfabient */ 931203790Sfabient for (i = 0; i < ct->pct_ninstr; i++) { 932203790Sfabient addr = ct->pct_image->pi_vaddr + 933203790Sfabient ct->pct_instr[i].pctf_func; 934203790Sfabient line = 0; 935233611Sfabient pmcstat_image_addr2line(ct->pct_image, addr, 936203790Sfabient sourcefile, sizeof(sourcefile), &line, 937233611Sfabient funcname, sizeof(funcname)); 938233611Sfabient fprintf(args.pa_graphfile, "%p %u", 939233611Sfabient (void *)addr, line); 940203790Sfabient for (j = 0; j<pmcstat_npmcs; j++) 941203790Sfabient fprintf(args.pa_graphfile, " %u", 942203790Sfabient PMCPL_CT_SAMPLE(j, 943203790Sfabient &ct->pct_instr[i].pctf_samples)); 944203790Sfabient fprintf(args.pa_graphfile, "\n"); 945203790Sfabient } 946203790Sfabient } else { 947233611Sfabient /* Global cost function cost. */ 948233611Sfabient fprintf(args.pa_graphfile, "%p %u", (void *)faddr, fline); 949203790Sfabient for (i = 0; i<pmcstat_npmcs ; i++) 950203790Sfabient fprintf(args.pa_graphfile, " %u", 951203790Sfabient PMCPL_CT_SAMPLE(i, &ct->pct_samples)); 952203790Sfabient fprintf(args.pa_graphfile, "\n"); 953203790Sfabient } 954233611Sfabient 955233611Sfabient pmcpl_ct_node_printchild(ct, faddr, fline); 956203790Sfabient} 957203790Sfabient 958233611Sfabientstatic void 959233611Sfabientpmcpl_ct_printnode(struct pmcpl_ct_node *ct) 960233611Sfabient{ 961233611Sfabient int i; 962233611Sfabient 963233611Sfabient if (ct == pmcpl_ct_root) { 964233611Sfabient fprintf(args.pa_graphfile, "fn=root\n"); 965233611Sfabient fprintf(args.pa_graphfile, "0x0 1"); 966233611Sfabient for (i = 0; i<pmcstat_npmcs ; i++) 967233611Sfabient fprintf(args.pa_graphfile, " 0"); 968233611Sfabient fprintf(args.pa_graphfile, "\n"); 969233611Sfabient pmcpl_ct_node_printchild(ct, 0, 0); 970233611Sfabient } else 971233611Sfabient pmcpl_ct_node_printself(ct); 972233611Sfabient} 973233611Sfabient 974203790Sfabient/* 975233611Sfabient * Breadth first traversal. 976203790Sfabient */ 977203790Sfabient 978203790Sfabientstatic void 979233611Sfabientpmcpl_ct_bfs(struct pmcpl_ct_node *ct) 980203790Sfabient{ 981233611Sfabient int i; 982233611Sfabient struct pmcpl_ct_node_hash *pch, *pchc; 983203790Sfabient struct pmcpl_ct_node *child; 984233611Sfabient STAILQ_HEAD(,pmcpl_ct_node_hash) q; 985233611Sfabient 986233611Sfabient STAILQ_INIT(&q); 987233611Sfabient if ((pch = malloc(sizeof(*pch))) == NULL) 988233611Sfabient err(EX_OSERR, "ERROR: Cannot allocate queue"); 989233611Sfabient pch->pch_ctnode = ct; 990233611Sfabient STAILQ_INSERT_TAIL(&q, pch, pch_next); 991233611Sfabient ct->pct_color = PMCPL_PCT_BLACK; 992233611Sfabient 993233611Sfabient while (!STAILQ_EMPTY(&q)) { 994233611Sfabient pch = STAILQ_FIRST(&q); 995233611Sfabient STAILQ_REMOVE_HEAD(&q, pch_next); 996233611Sfabient pmcpl_ct_printnode(pch->pch_ctnode); 997233611Sfabient for (i = 0; i<pch->pch_ctnode->pct_narc; i++) { 998233611Sfabient child = pch->pch_ctnode->pct_arc[i].pcta_child; 999233611Sfabient if (child->pct_color == PMCPL_PCT_WHITE) { 1000233611Sfabient child->pct_color = PMCPL_PCT_BLACK; 1001233611Sfabient if ((pchc = malloc(sizeof(*pchc))) == NULL) 1002233611Sfabient err(EX_OSERR, 1003233611Sfabient "ERROR: Cannot allocate queue"); 1004233611Sfabient pchc->pch_ctnode = child; 1005233611Sfabient STAILQ_INSERT_TAIL(&q, pchc, pch_next); 1006233611Sfabient } 1007233611Sfabient } 1008233611Sfabient free(pch); 1009233611Sfabient } 1010233611Sfabient} 1011233611Sfabient 1012233611Sfabient/* 1013233611Sfabient * Detect and fix inlined location. 1014233611Sfabient */ 1015233611Sfabient 1016233611Sfabientstatic void 1017233611Sfabient_pmcpl_ct_expand_inline(struct pmcpl_ct_node *ct) 1018233611Sfabient{ 1019233611Sfabient int i, j; 1020233611Sfabient unsigned fline, line, v; 1021233611Sfabient uintfptr_t faddr, addr, pc; 1022203790Sfabient char sourcefile[PATH_MAX]; 1023233611Sfabient char ffuncname[PATH_MAX], funcname[PATH_MAX]; 1024233611Sfabient char buffer[PATH_MAX]; 1025233611Sfabient struct pmcpl_ct_node *child; 1026203790Sfabient 1027203790Sfabient /* 1028233611Sfabient * Resolve parent and compare to each instr location. 1029203790Sfabient */ 1030233611Sfabient faddr = ct->pct_image->pi_vaddr + ct->pct_func; 1031233611Sfabient fline = 0; 1032233611Sfabient if (!pmcstat_image_addr2line(ct->pct_image, faddr, 1033233611Sfabient sourcefile, sizeof(sourcefile), &fline, 1034233611Sfabient ffuncname, sizeof(ffuncname))) 1035233611Sfabient return; 1036203790Sfabient 1037233611Sfabient for (i = 0; i < ct->pct_ninstr; i++) { 1038233611Sfabient addr = ct->pct_image->pi_vaddr + 1039233611Sfabient ct->pct_instr[i].pctf_func; 1040233611Sfabient line = 0; 1041233611Sfabient if (!pmcstat_image_addr2line(ct->pct_image, addr, 1042203790Sfabient sourcefile, sizeof(sourcefile), &line, 1043233611Sfabient funcname, sizeof(funcname))) 1044233611Sfabient continue; 1045203790Sfabient 1046233611Sfabient if (strcmp(funcname, ffuncname) == 0) 1047233611Sfabient continue; 1048203790Sfabient 1049233611Sfabient /* 1050233611Sfabient * - Lookup/create inline node by function name. 1051233611Sfabient * - Move instr PMCs to the inline node. 1052233611Sfabient * - Link nodes. 1053233611Sfabient * The lookup create a specific node per image/pc. 1054233611Sfabient */ 1055233611Sfabient if (args.pa_verbosity >= 2) 1056233611Sfabient fprintf(args.pa_printfile, 1057233611Sfabient "WARNING: inlined function at %p %s in %s\n", 1058233611Sfabient (void *)addr, funcname, ffuncname); 1059233611Sfabient 1060233611Sfabient snprintf(buffer, sizeof(buffer), "%s@%s", 1061233611Sfabient funcname, ffuncname); 1062233611Sfabient child = pmcpl_ct_node_hash_lookup(ct->pct_image, 1063233611Sfabient ct->pct_func, ct->pct_sym, sourcefile, buffer); 1064233611Sfabient assert(child != NULL); 1065233611Sfabient pc = ct->pct_instr[i].pctf_func; 1066233611Sfabient for (j = 0; j<pmcstat_npmcs; j++) { 1067233611Sfabient v = PMCPL_CT_SAMPLE(j, 1068233611Sfabient &ct->pct_instr[i].pctf_samples); 1069233611Sfabient if (v == 0) 1070233611Sfabient continue; 1071233611Sfabient pmcpl_ct_instr_add(child, j, pc, v); 1072233611Sfabient pmcpl_ct_node_update(ct, child, j, v, 0); 1073233611Sfabient if (j < ct->pct_samples.npmcs) 1074233611Sfabient ct->pct_samples.sb[j] -= 1075233611Sfabient ct->pct_instr[i].pctf_samples.sb[j]; 1076233611Sfabient ct->pct_instr[i].pctf_samples.sb[j] = 0; 1077203790Sfabient } 1078203790Sfabient } 1079203790Sfabient} 1080203790Sfabient 1081233611Sfabientstatic void 1082233611Sfabientpmcpl_ct_expand_inline(void) 1083233611Sfabient{ 1084233611Sfabient int i; 1085233611Sfabient struct pmcpl_ct_node_hash *pch; 1086233611Sfabient 1087233611Sfabient if (!args.pa_ctdumpinstr) 1088233611Sfabient return; 1089233611Sfabient 1090233611Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) 1091233611Sfabient STAILQ_FOREACH(pch, &pmcpl_ct_node_hash[i], pch_next) 1092233611Sfabient if (pch->pch_ctnode->pct_type == PMCPL_PCT_ADDR) 1093233611Sfabient _pmcpl_ct_expand_inline(pch->pch_ctnode); 1094233611Sfabient} 1095233611Sfabient 1096203790Sfabient/* 1097203790Sfabient * Clean the PMC name for Kcachegrind formula 1098203790Sfabient */ 1099203790Sfabient 1100203790Sfabientstatic void 1101203790Sfabientpmcpl_ct_fixup_pmcname(char *s) 1102203790Sfabient{ 1103203790Sfabient char *p; 1104203790Sfabient 1105203790Sfabient for (p = s; *p; p++) 1106203790Sfabient if (!isalnum(*p)) 1107203790Sfabient *p = '_'; 1108203790Sfabient} 1109203790Sfabient 1110203790Sfabient/* 1111203790Sfabient * Print a calltree (KCachegrind) for all PMCs. 1112203790Sfabient */ 1113203790Sfabient 1114203790Sfabientstatic void 1115203790Sfabientpmcpl_ct_print(void) 1116203790Sfabient{ 1117233611Sfabient int i; 1118233611Sfabient char name[40]; 1119203790Sfabient struct pmcpl_ct_sample rsamples; 1120203790Sfabient 1121203790Sfabient pmcpl_ct_samples_root(&rsamples); 1122233611Sfabient pmcpl_ct_expand_inline(); 1123203790Sfabient 1124203790Sfabient fprintf(args.pa_graphfile, 1125203790Sfabient "version: 1\n" 1126203790Sfabient "creator: pmcstat\n" 1127203790Sfabient "positions: instr line\n" 1128203790Sfabient "events:"); 1129203790Sfabient for (i=0; i<pmcstat_npmcs; i++) { 1130203790Sfabient snprintf(name, sizeof(name), "%s_%d", 1131203790Sfabient pmcstat_pmcindex_to_name(i), i); 1132203790Sfabient pmcpl_ct_fixup_pmcname(name); 1133203790Sfabient fprintf(args.pa_graphfile, " %s", name); 1134203790Sfabient } 1135203790Sfabient fprintf(args.pa_graphfile, "\nsummary:"); 1136203790Sfabient for (i=0; i<pmcstat_npmcs ; i++) 1137203790Sfabient fprintf(args.pa_graphfile, " %u", 1138203790Sfabient PMCPL_CT_SAMPLE(i, &rsamples)); 1139203790Sfabient fprintf(args.pa_graphfile, "\n"); 1140233611Sfabient pmcpl_ct_bfs(pmcpl_ct_root); 1141203790Sfabient pmcpl_ct_samples_free(&rsamples); 1142203790Sfabient} 1143203790Sfabient 1144203790Sfabientint 1145203790Sfabientpmcpl_ct_configure(char *opt) 1146203790Sfabient{ 1147203790Sfabient 1148203790Sfabient if (strncmp(opt, "skiplink=", 9) == 0) { 1149203790Sfabient pmcstat_skiplink = atoi(opt+9); 1150203790Sfabient } else 1151203790Sfabient return (0); 1152203790Sfabient 1153203790Sfabient return (1); 1154203790Sfabient} 1155203790Sfabient 1156203790Sfabientint 1157203790Sfabientpmcpl_ct_init(void) 1158203790Sfabient{ 1159203790Sfabient int i; 1160203790Sfabient 1161233611Sfabient pmcpl_ct_root = pmcpl_ct_node_allocate(); 1162203790Sfabient 1163203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) 1164233611Sfabient STAILQ_INIT(&pmcpl_ct_node_hash[i]); 1165203790Sfabient 1166203790Sfabient pmcpl_ct_samples_init(&pmcpl_ct_callid); 1167203790Sfabient 1168203790Sfabient return (0); 1169203790Sfabient} 1170203790Sfabient 1171203790Sfabientvoid 1172203790Sfabientpmcpl_ct_shutdown(FILE *mf) 1173203790Sfabient{ 1174203790Sfabient int i; 1175203790Sfabient struct pmcpl_ct_node_hash *pch, *pchtmp; 1176203790Sfabient 1177203790Sfabient (void) mf; 1178203790Sfabient 1179203790Sfabient if (args.pa_flags & FLAG_DO_CALLGRAPHS) 1180203790Sfabient pmcpl_ct_print(); 1181203790Sfabient 1182203790Sfabient /* 1183203790Sfabient * Free memory. 1184203790Sfabient */ 1185203790Sfabient 1186203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) { 1187233611Sfabient STAILQ_FOREACH_SAFE(pch, &pmcpl_ct_node_hash[i], pch_next, 1188203790Sfabient pchtmp) { 1189203790Sfabient pmcpl_ct_node_free(pch->pch_ctnode); 1190203790Sfabient free(pch); 1191203790Sfabient } 1192203790Sfabient } 1193203790Sfabient 1194203790Sfabient pmcpl_ct_node_free(pmcpl_ct_root); 1195203790Sfabient pmcpl_ct_root = NULL; 1196203790Sfabient 1197203790Sfabient pmcpl_ct_samples_free(&pmcpl_ct_callid); 1198203790Sfabient} 1199203790Sfabient 1200