pmcpl_calltree.c revision 233611
1203790Sfabient/*- 2233611Sfabient * Copyright (c) 2012, Fabien Thomas 3203790Sfabient * All rights reserved. 4203790Sfabient * 5203790Sfabient * Redistribution and use in source and binary forms, with or without 6203790Sfabient * modification, are permitted provided that the following conditions 7203790Sfabient * are met: 8203790Sfabient * 1. Redistributions of source code must retain the above copyright 9203790Sfabient * notice, this list of conditions and the following disclaimer. 10203790Sfabient * 2. Redistributions in binary form must reproduce the above copyright 11203790Sfabient * notice, this list of conditions and the following disclaimer in the 12203790Sfabient * documentation and/or other materials provided with the distribution. 13203790Sfabient * 14203790Sfabient * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15203790Sfabient * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16203790Sfabient * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17203790Sfabient * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18203790Sfabient * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19203790Sfabient * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20203790Sfabient * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21203790Sfabient * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22203790Sfabient * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23203790Sfabient * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24203790Sfabient * SUCH DAMAGE. 25203790Sfabient */ 26203790Sfabient 27203790Sfabient/* 28203790Sfabient * Process hwpmc(4) samples as calltree. 29203790Sfabient * 30203790Sfabient * Output file format compatible with Kcachegrind (kdesdk). 31203790Sfabient * Handle top mode with a sorted tree display. 32203790Sfabient */ 33203790Sfabient 34203790Sfabient#include <sys/cdefs.h> 35203790Sfabient__FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcpl_calltree.c 233611 2012-03-28 16:23:40Z fabient $"); 36203790Sfabient 37203790Sfabient#include <sys/param.h> 38203790Sfabient#include <sys/endian.h> 39203790Sfabient#include <sys/queue.h> 40203790Sfabient 41203790Sfabient#include <assert.h> 42203790Sfabient#include <curses.h> 43203790Sfabient#include <ctype.h> 44203790Sfabient#include <err.h> 45203790Sfabient#include <errno.h> 46203790Sfabient#include <fcntl.h> 47203790Sfabient#include <pmc.h> 48203790Sfabient#include <pmclog.h> 49203790Sfabient#include <stdint.h> 50203790Sfabient#include <stdio.h> 51203790Sfabient#include <stdlib.h> 52203790Sfabient#include <string.h> 53203790Sfabient#include <unistd.h> 54203790Sfabient#include <sysexits.h> 55203790Sfabient 56203790Sfabient#include "pmcstat.h" 57203790Sfabient#include "pmcstat_log.h" 58203790Sfabient#include "pmcstat_top.h" 59203790Sfabient#include "pmcpl_calltree.h" 60203790Sfabient 61233611Sfabient#define PMCPL_CT_GROWSIZE 4 62203790Sfabient 63203790Sfabientstatic int pmcstat_skiplink = 0; 64203790Sfabient 65203790Sfabientstruct pmcpl_ct_node; 66203790Sfabient 67203790Sfabient/* Get the sample value for PMC a. */ 68233611Sfabient#define PMCPL_CT_SAMPLE(a, b) \ 69203790Sfabient ((a) < (b)->npmcs ? (b)->sb[a] : 0) 70203790Sfabient 71203790Sfabient/* Get the sample value in percent related to rsamples. */ 72233611Sfabient#define PMCPL_CT_SAMPLEP(a, b) \ 73203790Sfabient (PMCPL_CT_SAMPLE(a, b) * 100.0 / rsamples->sb[a]) 74203790Sfabient 75203790Sfabientstruct pmcpl_ct_sample { 76203790Sfabient int npmcs; /* Max pmc index available. */ 77203790Sfabient unsigned *sb; /* Sample buffer for 0..npmcs. */ 78203790Sfabient}; 79203790Sfabient 80203790Sfabientstruct pmcpl_ct_arc { 81203790Sfabient struct pmcpl_ct_sample pcta_samples; 82203790Sfabient struct pmcpl_ct_sample pcta_callid; 83203790Sfabient unsigned pcta_call; 84203790Sfabient struct pmcpl_ct_node *pcta_child; 85203790Sfabient}; 86203790Sfabient 87203790Sfabientstruct pmcpl_ct_instr { 88203790Sfabient uintfptr_t pctf_func; 89203790Sfabient struct pmcpl_ct_sample pctf_samples; 90203790Sfabient}; 91203790Sfabient 92203790Sfabient/* 93203790Sfabient * Each calltree node is tracked by a pmcpl_ct_node struct. 94203790Sfabient */ 95203790Sfabientstruct pmcpl_ct_node { 96203790Sfabient struct pmcstat_image *pct_image; 97203790Sfabient uintfptr_t pct_func; 98233611Sfabient 99233611Sfabient struct pmcstat_symbol *pct_sym; 100233611Sfabient pmcstat_interned_string pct_ifl; 101233611Sfabient pmcstat_interned_string pct_ifn; 102233611Sfabient 103203790Sfabient struct pmcpl_ct_sample pct_samples; 104203790Sfabient 105203790Sfabient int pct_narc; 106203790Sfabient int pct_arc_c; 107203790Sfabient struct pmcpl_ct_arc *pct_arc; 108203790Sfabient 109203790Sfabient /* TODO: optimize for large number of items. */ 110203790Sfabient int pct_ninstr; 111203790Sfabient int pct_instr_c; 112203790Sfabient struct pmcpl_ct_instr *pct_instr; 113233611Sfabient 114233611Sfabient#define PMCPL_PCT_ADDR 0 115233611Sfabient#define PMCPL_PCT_NAME 1 116233611Sfabient char pct_type; 117233611Sfabient#define PMCPL_PCT_WHITE 0 118233611Sfabient#define PMCPL_PCT_GREY 1 119233611Sfabient#define PMCPL_PCT_BLACK 2 120233611Sfabient char pct_color; 121203790Sfabient}; 122203790Sfabient 123203790Sfabientstruct pmcpl_ct_node_hash { 124203790Sfabient struct pmcpl_ct_node *pch_ctnode; 125233611Sfabient STAILQ_ENTRY(pmcpl_ct_node_hash) pch_next; 126203790Sfabient}; 127203790Sfabient 128203790Sfabientstruct pmcpl_ct_sample pmcpl_ct_callid; 129203790Sfabient 130233611Sfabient#define PMCPL_CT_MAXCOL PMC_CALLCHAIN_DEPTH_MAX 131233611Sfabient#define PMCPL_CT_MAXLINE 1024 /* TODO: dynamic. */ 132203790Sfabient 133207755Sfabientstruct pmcpl_ct_line { 134207755Sfabient unsigned ln_sum; 135207755Sfabient unsigned ln_index; 136207755Sfabient}; 137207755Sfabient 138207755Sfabientstruct pmcpl_ct_line pmcpl_ct_topmax[PMCPL_CT_MAXLINE+1]; 139233611Sfabientstruct pmcpl_ct_node 140233611Sfabient *pmcpl_ct_topscreen[PMCPL_CT_MAXCOL+1][PMCPL_CT_MAXLINE+1]; 141207755Sfabient 142203790Sfabient/* 143203790Sfabient * All nodes indexed by function/image name are placed in a hash table. 144203790Sfabient */ 145233611Sfabientstatic STAILQ_HEAD(,pmcpl_ct_node_hash) pmcpl_ct_node_hash[PMCSTAT_NHASH]; 146203790Sfabient 147203790Sfabient/* 148203790Sfabient * Root node for the graph. 149203790Sfabient */ 150203790Sfabientstatic struct pmcpl_ct_node *pmcpl_ct_root; 151203790Sfabient 152203790Sfabient/* 153203790Sfabient * Prototypes 154203790Sfabient */ 155203790Sfabient 156203790Sfabient/* 157203790Sfabient * Initialize a samples. 158203790Sfabient */ 159203790Sfabient 160203790Sfabientstatic void 161203790Sfabientpmcpl_ct_samples_init(struct pmcpl_ct_sample *samples) 162203790Sfabient{ 163203790Sfabient 164203790Sfabient samples->npmcs = 0; 165203790Sfabient samples->sb = NULL; 166203790Sfabient} 167203790Sfabient 168203790Sfabient/* 169203790Sfabient * Free a samples. 170203790Sfabient */ 171203790Sfabient 172203790Sfabientstatic void 173203790Sfabientpmcpl_ct_samples_free(struct pmcpl_ct_sample *samples) 174203790Sfabient{ 175203790Sfabient 176203790Sfabient samples->npmcs = 0; 177203790Sfabient free(samples->sb); 178203790Sfabient samples->sb = NULL; 179203790Sfabient} 180203790Sfabient 181203790Sfabient/* 182203790Sfabient * Grow a sample block to store pmcstat_npmcs PMCs. 183203790Sfabient */ 184203790Sfabient 185203790Sfabientstatic void 186203790Sfabientpmcpl_ct_samples_grow(struct pmcpl_ct_sample *samples) 187203790Sfabient{ 188203790Sfabient int npmcs; 189203790Sfabient 190203790Sfabient /* Enough storage. */ 191203790Sfabient if (pmcstat_npmcs <= samples->npmcs) 192203790Sfabient return; 193203790Sfabient 194203790Sfabient npmcs = samples->npmcs + 195203790Sfabient max(pmcstat_npmcs - samples->npmcs, PMCPL_CT_GROWSIZE); 196203790Sfabient samples->sb = realloc(samples->sb, npmcs * sizeof(unsigned)); 197203790Sfabient if (samples->sb == NULL) 198203790Sfabient errx(EX_SOFTWARE, "ERROR: out of memory"); 199203790Sfabient bzero((char *)samples->sb + samples->npmcs * sizeof(unsigned), 200203790Sfabient (npmcs - samples->npmcs) * sizeof(unsigned)); 201203790Sfabient samples->npmcs = npmcs; 202203790Sfabient} 203203790Sfabient 204203790Sfabient/* 205203790Sfabient * Compute the sum of all root arcs. 206203790Sfabient */ 207203790Sfabient 208203790Sfabientstatic void 209203790Sfabientpmcpl_ct_samples_root(struct pmcpl_ct_sample *samples) 210203790Sfabient{ 211203790Sfabient int i, pmcin; 212203790Sfabient 213203790Sfabient pmcpl_ct_samples_init(samples); 214203790Sfabient pmcpl_ct_samples_grow(samples); 215203790Sfabient 216203790Sfabient for (i = 0; i < pmcpl_ct_root->pct_narc; i++) 217203790Sfabient for (pmcin = 0; pmcin < pmcstat_npmcs; pmcin++) 218203790Sfabient samples->sb[pmcin] += PMCPL_CT_SAMPLE(pmcin, 219203790Sfabient &pmcpl_ct_root->pct_arc[i].pcta_samples); 220203790Sfabient} 221203790Sfabient 222203790Sfabient/* 223203790Sfabient * Grow the arc table. 224203790Sfabient */ 225203790Sfabient 226203790Sfabientstatic void 227203790Sfabientpmcpl_ct_arc_grow(int cursize, int *maxsize, struct pmcpl_ct_arc **items) 228203790Sfabient{ 229203790Sfabient int nmaxsize; 230203790Sfabient 231203790Sfabient if (cursize < *maxsize) 232203790Sfabient return; 233203790Sfabient 234203790Sfabient nmaxsize = *maxsize + max(cursize + 1 - *maxsize, PMCPL_CT_GROWSIZE); 235203790Sfabient *items = realloc(*items, nmaxsize * sizeof(struct pmcpl_ct_arc)); 236203790Sfabient if (*items == NULL) 237203790Sfabient errx(EX_SOFTWARE, "ERROR: out of memory"); 238203790Sfabient bzero((char *)*items + *maxsize * sizeof(struct pmcpl_ct_arc), 239203790Sfabient (nmaxsize - *maxsize) * sizeof(struct pmcpl_ct_arc)); 240203790Sfabient *maxsize = nmaxsize; 241203790Sfabient} 242203790Sfabient 243203790Sfabient/* 244203790Sfabient * Grow the instr table. 245203790Sfabient */ 246203790Sfabient 247203790Sfabientstatic void 248203790Sfabientpmcpl_ct_instr_grow(int cursize, int *maxsize, struct pmcpl_ct_instr **items) 249203790Sfabient{ 250203790Sfabient int nmaxsize; 251203790Sfabient 252203790Sfabient if (cursize < *maxsize) 253203790Sfabient return; 254203790Sfabient 255203790Sfabient nmaxsize = *maxsize + max(cursize + 1 - *maxsize, PMCPL_CT_GROWSIZE); 256203790Sfabient *items = realloc(*items, nmaxsize * sizeof(struct pmcpl_ct_instr)); 257203790Sfabient if (*items == NULL) 258203790Sfabient errx(EX_SOFTWARE, "ERROR: out of memory"); 259203790Sfabient bzero((char *)*items + *maxsize * sizeof(struct pmcpl_ct_instr), 260203790Sfabient (nmaxsize - *maxsize) * sizeof(struct pmcpl_ct_instr)); 261203790Sfabient *maxsize = nmaxsize; 262203790Sfabient} 263203790Sfabient 264203790Sfabient/* 265203790Sfabient * Add a new instruction sample to given node. 266203790Sfabient */ 267203790Sfabient 268203790Sfabientstatic void 269233611Sfabientpmcpl_ct_instr_add(struct pmcpl_ct_node *ct, int pmcin, 270233611Sfabient uintfptr_t pc, unsigned v) 271203790Sfabient{ 272203790Sfabient int i; 273203790Sfabient struct pmcpl_ct_instr *in; 274203790Sfabient 275203790Sfabient for (i = 0; i<ct->pct_ninstr; i++) { 276203790Sfabient if (ct->pct_instr[i].pctf_func == pc) { 277203790Sfabient in = &ct->pct_instr[i]; 278203790Sfabient pmcpl_ct_samples_grow(&in->pctf_samples); 279233611Sfabient in->pctf_samples.sb[pmcin] += v; 280203790Sfabient return; 281203790Sfabient } 282203790Sfabient } 283203790Sfabient 284203790Sfabient pmcpl_ct_instr_grow(ct->pct_ninstr, &ct->pct_instr_c, &ct->pct_instr); 285203790Sfabient in = &ct->pct_instr[ct->pct_ninstr]; 286203790Sfabient in->pctf_func = pc; 287203790Sfabient pmcpl_ct_samples_init(&in->pctf_samples); 288203790Sfabient pmcpl_ct_samples_grow(&in->pctf_samples); 289233611Sfabient in->pctf_samples.sb[pmcin] = v; 290203790Sfabient ct->pct_ninstr++; 291203790Sfabient} 292203790Sfabient 293203790Sfabient/* 294203790Sfabient * Allocate a new node. 295203790Sfabient */ 296203790Sfabient 297203790Sfabientstatic struct pmcpl_ct_node * 298233611Sfabientpmcpl_ct_node_allocate(void) 299203790Sfabient{ 300203790Sfabient struct pmcpl_ct_node *ct; 301203790Sfabient 302203790Sfabient if ((ct = malloc(sizeof(*ct))) == NULL) 303203790Sfabient err(EX_OSERR, "ERROR: Cannot allocate callgraph node"); 304203790Sfabient 305203790Sfabient pmcpl_ct_samples_init(&ct->pct_samples); 306203790Sfabient 307233611Sfabient ct->pct_sym = NULL; 308233611Sfabient ct->pct_image = NULL; 309233611Sfabient ct->pct_func = 0; 310233611Sfabient 311203790Sfabient ct->pct_narc = 0; 312203790Sfabient ct->pct_arc_c = 0; 313203790Sfabient ct->pct_arc = NULL; 314203790Sfabient 315203790Sfabient ct->pct_ninstr = 0; 316203790Sfabient ct->pct_instr_c = 0; 317203790Sfabient ct->pct_instr = NULL; 318203790Sfabient 319233611Sfabient ct->pct_color = PMCPL_PCT_WHITE; 320233611Sfabient 321203790Sfabient return (ct); 322203790Sfabient} 323203790Sfabient 324203790Sfabient/* 325203790Sfabient * Free a node. 326203790Sfabient */ 327203790Sfabient 328203790Sfabientstatic void 329203790Sfabientpmcpl_ct_node_free(struct pmcpl_ct_node *ct) 330203790Sfabient{ 331203790Sfabient int i; 332203790Sfabient 333203790Sfabient for (i = 0; i < ct->pct_narc; i++) { 334203790Sfabient pmcpl_ct_samples_free(&ct->pct_arc[i].pcta_samples); 335203790Sfabient pmcpl_ct_samples_free(&ct->pct_arc[i].pcta_callid); 336203790Sfabient } 337203790Sfabient 338203790Sfabient pmcpl_ct_samples_free(&ct->pct_samples); 339203790Sfabient free(ct->pct_arc); 340203790Sfabient free(ct->pct_instr); 341203790Sfabient free(ct); 342203790Sfabient} 343203790Sfabient 344203790Sfabient/* 345203790Sfabient * Clear the graph tag on each node. 346203790Sfabient */ 347203790Sfabientstatic void 348203790Sfabientpmcpl_ct_node_cleartag(void) 349203790Sfabient{ 350203790Sfabient int i; 351203790Sfabient struct pmcpl_ct_node_hash *pch; 352203790Sfabient 353203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) 354233611Sfabient STAILQ_FOREACH(pch, &pmcpl_ct_node_hash[i], pch_next) 355233611Sfabient pch->pch_ctnode->pct_color = PMCPL_PCT_WHITE; 356203790Sfabient 357233611Sfabient pmcpl_ct_root->pct_color = PMCPL_PCT_WHITE; 358203790Sfabient} 359203790Sfabient 360203790Sfabient/* 361203790Sfabient * Print the callchain line by line with maximum cost at top. 362203790Sfabient */ 363203790Sfabient 364203790Sfabientstatic int 365203790Sfabientpmcpl_ct_node_dumptop(int pmcin, struct pmcpl_ct_node *ct, 366207755Sfabient struct pmcpl_ct_sample *rsamples, int x, int *y) 367203790Sfabient{ 368207755Sfabient int i, terminal; 369210766Sfabient struct pmcpl_ct_arc *arc; 370203790Sfabient 371233611Sfabient if (ct->pct_color == PMCPL_PCT_GREY) 372203790Sfabient return 0; 373203790Sfabient 374203790Sfabient if (x >= PMCPL_CT_MAXCOL) { 375203790Sfabient pmcpl_ct_topscreen[x][*y] = NULL; 376203790Sfabient return 1; 377203790Sfabient } 378203790Sfabient pmcpl_ct_topscreen[x][*y] = ct; 379203790Sfabient 380203790Sfabient /* 381207755Sfabient * Check if this is a terminal node. 382207755Sfabient * We need to check that some samples exist 383207755Sfabient * for at least one arc for that PMC. 384203790Sfabient */ 385207755Sfabient terminal = 1; 386210766Sfabient for (i = 0; i < ct->pct_narc; i++) { 387210766Sfabient arc = &ct->pct_arc[i]; 388233611Sfabient if (arc->pcta_child->pct_color != PMCPL_PCT_GREY && 389233611Sfabient PMCPL_CT_SAMPLE(pmcin, 390210766Sfabient &arc->pcta_samples) != 0 && 391210766Sfabient PMCPL_CT_SAMPLEP(pmcin, 392233611Sfabient &arc->pcta_samples) > pmcstat_threshold) { 393207755Sfabient terminal = 0; 394207755Sfabient break; 395207755Sfabient } 396210766Sfabient } 397207755Sfabient 398207755Sfabient if (ct->pct_narc == 0 || terminal) { 399203790Sfabient pmcpl_ct_topscreen[x+1][*y] = NULL; 400207755Sfabient if (*y >= PMCPL_CT_MAXLINE) 401203790Sfabient return 1; 402203790Sfabient *y = *y + 1; 403203790Sfabient for (i=0; i < x; i++) 404203790Sfabient pmcpl_ct_topscreen[i][*y] = 405203790Sfabient pmcpl_ct_topscreen[i][*y - 1]; 406203790Sfabient return 0; 407203790Sfabient } 408203790Sfabient 409233611Sfabient ct->pct_color = PMCPL_PCT_GREY; 410203790Sfabient for (i = 0; i < ct->pct_narc; i++) { 411206090Sfabient if (PMCPL_CT_SAMPLE(pmcin, 412206090Sfabient &ct->pct_arc[i].pcta_samples) == 0) 413206090Sfabient continue; 414203790Sfabient if (PMCPL_CT_SAMPLEP(pmcin, 415203790Sfabient &ct->pct_arc[i].pcta_samples) > pmcstat_threshold) { 416203790Sfabient if (pmcpl_ct_node_dumptop(pmcin, 417203790Sfabient ct->pct_arc[i].pcta_child, 418233611Sfabient rsamples, x+1, y)) { 419233611Sfabient ct->pct_color = PMCPL_PCT_BLACK; 420203790Sfabient return 1; 421233611Sfabient } 422203790Sfabient } 423203790Sfabient } 424233611Sfabient ct->pct_color = PMCPL_PCT_BLACK; 425203790Sfabient 426203790Sfabient return 0; 427203790Sfabient} 428203790Sfabient 429203790Sfabient/* 430207755Sfabient * Compare two top line by sum. 431207755Sfabient */ 432207755Sfabientstatic int 433207755Sfabientpmcpl_ct_line_compare(const void *a, const void *b) 434207755Sfabient{ 435207755Sfabient const struct pmcpl_ct_line *ct1, *ct2; 436207755Sfabient 437207755Sfabient ct1 = (const struct pmcpl_ct_line *) a; 438207755Sfabient ct2 = (const struct pmcpl_ct_line *) b; 439207755Sfabient 440207755Sfabient /* Sort in reverse order */ 441207755Sfabient if (ct1->ln_sum < ct2->ln_sum) 442207755Sfabient return (1); 443207755Sfabient if (ct1->ln_sum > ct2->ln_sum) 444207755Sfabient return (-1); 445207755Sfabient return (0); 446207755Sfabient} 447207755Sfabient 448207755Sfabient/* 449203790Sfabient * Format and display given PMC index. 450203790Sfabient */ 451203790Sfabient 452203790Sfabientstatic void 453203790Sfabientpmcpl_ct_node_printtop(struct pmcpl_ct_sample *rsamples, int pmcin, int maxy) 454203790Sfabient{ 455207755Sfabient#undef TS 456207755Sfabient#undef TSI 457207755Sfabient#define TS(x, y) (pmcpl_ct_topscreen[x][y]) 458207755Sfabient#define TSI(x, y) (pmcpl_ct_topscreen[x][pmcpl_ct_topmax[y].ln_index]) 459207755Sfabient 460203790Sfabient int v_attrs, ns_len, vs_len, is_len, width, indentwidth, x, y; 461203790Sfabient float v; 462203790Sfabient char ns[30], vs[10], is[20]; 463203790Sfabient struct pmcpl_ct_node *ct; 464203790Sfabient const char *space = " "; 465203790Sfabient 466207755Sfabient /* 467207755Sfabient * Sort by line cost. 468207755Sfabient */ 469207755Sfabient for (y = 0; ; y++) { 470207755Sfabient ct = TS(1, y); 471207755Sfabient if (ct == NULL) 472207755Sfabient break; 473207755Sfabient 474207755Sfabient pmcpl_ct_topmax[y].ln_sum = 0; 475207755Sfabient pmcpl_ct_topmax[y].ln_index = y; 476207755Sfabient for (x = 1; TS(x, y) != NULL; x++) { 477207755Sfabient pmcpl_ct_topmax[y].ln_sum += 478207755Sfabient PMCPL_CT_SAMPLE(pmcin, &TS(x, y)->pct_samples); 479207755Sfabient } 480207755Sfabient } 481207755Sfabient qsort(pmcpl_ct_topmax, y, sizeof(pmcpl_ct_topmax[0]), 482207755Sfabient pmcpl_ct_line_compare); 483207755Sfabient pmcpl_ct_topmax[y].ln_index = y; 484207755Sfabient 485203790Sfabient for (y = 0; y < maxy; y++) { 486207755Sfabient ct = TSI(1, y); 487207755Sfabient if (ct == NULL) 488207755Sfabient break; 489203790Sfabient 490207755Sfabient if (y > 0) 491207755Sfabient PMCSTAT_PRINTW("\n"); 492203790Sfabient 493207755Sfabient /* Output sum. */ 494207755Sfabient v = pmcpl_ct_topmax[y].ln_sum * 100.0 / 495207755Sfabient rsamples->sb[pmcin]; 496207755Sfabient snprintf(vs, sizeof(vs), "%.1f", v); 497207755Sfabient v_attrs = PMCSTAT_ATTRPERCENT(v); 498207755Sfabient PMCSTAT_ATTRON(v_attrs); 499207755Sfabient PMCSTAT_PRINTW("%5.5s ", vs); 500207755Sfabient PMCSTAT_ATTROFF(v_attrs); 501203790Sfabient 502207755Sfabient width = indentwidth = 5 + 1; 503207755Sfabient 504207755Sfabient for (x = 1; (ct = TSI(x, y)) != NULL; x++) { 505207755Sfabient 506203790Sfabient vs[0] = '\0'; vs_len = 0; 507203790Sfabient is[0] = '\0'; is_len = 0; 508203790Sfabient 509203790Sfabient /* Format value. */ 510203790Sfabient v = PMCPL_CT_SAMPLEP(pmcin, &ct->pct_samples); 511203790Sfabient if (v > pmcstat_threshold) 512207755Sfabient vs_len = snprintf(vs, sizeof(vs), 513207755Sfabient "(%.1f%%)", v); 514203790Sfabient v_attrs = PMCSTAT_ATTRPERCENT(v); 515203790Sfabient 516203790Sfabient if (pmcstat_skiplink && v <= pmcstat_threshold) { 517207755Sfabient strlcpy(ns, ".", sizeof(ns)); 518207755Sfabient ns_len = 1; 519207755Sfabient } else { 520233611Sfabient if (ct->pct_sym != NULL) { 521203790Sfabient ns_len = snprintf(ns, sizeof(ns), "%s", 522233611Sfabient pmcstat_string_unintern(ct->pct_sym->ps_name)); 523203790Sfabient } else 524203790Sfabient ns_len = snprintf(ns, sizeof(ns), "%p", 525203790Sfabient (void *)ct->pct_func); 526203790Sfabient 527203790Sfabient /* Format image. */ 528207755Sfabient if (x == 1 || 529207755Sfabient TSI(x-1, y)->pct_image != ct->pct_image) 530203790Sfabient is_len = snprintf(is, sizeof(is), "@%s", 531203790Sfabient pmcstat_string_unintern(ct->pct_image->pi_name)); 532203790Sfabient 533203790Sfabient /* Check for line wrap. */ 534203790Sfabient width += ns_len + is_len + vs_len + 1; 535207755Sfabient } 536203790Sfabient if (width >= pmcstat_displaywidth) { 537205693Sfabient maxy--; 538205693Sfabient if (y >= maxy) 539205693Sfabient break; 540203790Sfabient PMCSTAT_PRINTW("\n%*s", indentwidth, space); 541203790Sfabient width = indentwidth + ns_len + is_len + vs_len; 542203790Sfabient } 543203790Sfabient 544203790Sfabient PMCSTAT_ATTRON(v_attrs); 545203790Sfabient PMCSTAT_PRINTW("%s%s%s ", ns, is, vs); 546203790Sfabient PMCSTAT_ATTROFF(v_attrs); 547203790Sfabient } 548203790Sfabient } 549203790Sfabient} 550203790Sfabient 551203790Sfabient/* 552203790Sfabient * Output top mode snapshot. 553203790Sfabient */ 554203790Sfabient 555203790Sfabientvoid 556203790Sfabientpmcpl_ct_topdisplay(void) 557203790Sfabient{ 558207755Sfabient int y; 559206994Sfabient struct pmcpl_ct_sample r, *rsamples; 560203790Sfabient 561206994Sfabient rsamples = &r; 562206994Sfabient pmcpl_ct_samples_root(rsamples); 563207755Sfabient pmcpl_ct_node_cleartag(); 564203790Sfabient 565207755Sfabient PMCSTAT_PRINTW("%5.5s %s\n", "%SAMP", "CALLTREE"); 566203790Sfabient 567207755Sfabient y = 0; 568207755Sfabient if (pmcpl_ct_node_dumptop(pmcstat_pmcinfilter, 569207755Sfabient pmcpl_ct_root, rsamples, 0, &y)) 570207755Sfabient PMCSTAT_PRINTW("...\n"); 571207755Sfabient pmcpl_ct_topscreen[1][y] = NULL; 572203790Sfabient 573207755Sfabient pmcpl_ct_node_printtop(rsamples, 574207755Sfabient pmcstat_pmcinfilter, pmcstat_displayheight - 2); 575203790Sfabient 576206994Sfabient pmcpl_ct_samples_free(rsamples); 577203790Sfabient} 578203790Sfabient 579203790Sfabient/* 580203790Sfabient * Handle top mode keypress. 581203790Sfabient */ 582203790Sfabient 583203790Sfabientint 584203790Sfabientpmcpl_ct_topkeypress(int c, WINDOW *w) 585203790Sfabient{ 586203790Sfabient 587203790Sfabient switch (c) { 588203790Sfabient case 'f': 589203790Sfabient pmcstat_skiplink = !pmcstat_skiplink; 590227524Sobrien wprintw(w, "skip empty link %s", 591227524Sobrien pmcstat_skiplink ? "on" : "off"); 592203790Sfabient break; 593203790Sfabient } 594203790Sfabient 595203790Sfabient return 0; 596203790Sfabient} 597203790Sfabient 598203790Sfabient/* 599203790Sfabient * Look for a callgraph node associated with pmc `pmcid' in the global 600203790Sfabient * hash table that corresponds to the given `pc' value in the process map 601203790Sfabient * `ppm'. 602203790Sfabient */ 603203790Sfabient 604233611Sfabientstatic void 605233611Sfabientpmcpl_ct_node_update(struct pmcpl_ct_node *parent, 606233611Sfabient struct pmcpl_ct_node *child, int pmcin, unsigned v, int cd) 607203790Sfabient{ 608203790Sfabient struct pmcpl_ct_arc *arc; 609203790Sfabient int i; 610203790Sfabient 611203790Sfabient assert(parent != NULL); 612203790Sfabient 613233611Sfabient /* 614233611Sfabient * Find related arc in parent node and 615233611Sfabient * increment the sample count. 616233611Sfabient */ 617233611Sfabient for (i = 0; i < parent->pct_narc; i++) { 618233611Sfabient if (parent->pct_arc[i].pcta_child == child) { 619233611Sfabient arc = &parent->pct_arc[i]; 620233611Sfabient pmcpl_ct_samples_grow(&arc->pcta_samples); 621233611Sfabient arc->pcta_samples.sb[pmcin] += v; 622233611Sfabient /* Estimate call count. */ 623233611Sfabient if (cd) { 624233611Sfabient pmcpl_ct_samples_grow(&arc->pcta_callid); 625233611Sfabient if (pmcpl_ct_callid.sb[pmcin] - 626233611Sfabient arc->pcta_callid.sb[pmcin] > 1) 627233611Sfabient arc->pcta_call++; 628233611Sfabient arc->pcta_callid.sb[pmcin] = 629233611Sfabient pmcpl_ct_callid.sb[pmcin]; 630233611Sfabient } 631233611Sfabient return; 632233611Sfabient } 633233611Sfabient } 634203790Sfabient 635203790Sfabient /* 636233611Sfabient * No arc found for us, add ourself to the parent. 637203790Sfabient */ 638233611Sfabient pmcpl_ct_arc_grow(parent->pct_narc, 639233611Sfabient &parent->pct_arc_c, &parent->pct_arc); 640233611Sfabient arc = &parent->pct_arc[parent->pct_narc]; 641233611Sfabient pmcpl_ct_samples_grow(&arc->pcta_samples); 642233611Sfabient arc->pcta_samples.sb[pmcin] = v; 643233611Sfabient arc->pcta_call = 1; 644233611Sfabient if (cd) { 645233611Sfabient pmcpl_ct_samples_grow(&arc->pcta_callid); 646233611Sfabient arc->pcta_callid.sb[pmcin] = pmcpl_ct_callid.sb[pmcin]; 647233611Sfabient } 648233611Sfabient arc->pcta_child = child; 649233611Sfabient parent->pct_narc++; 650233611Sfabient} 651203790Sfabient 652233611Sfabient/* 653233611Sfabient * Lookup by image/pc. 654233611Sfabient */ 655233611Sfabient 656233611Sfabientstatic struct pmcpl_ct_node * 657233611Sfabientpmcpl_ct_node_hash_lookup(struct pmcstat_image *image, uintfptr_t pc, 658233611Sfabient struct pmcstat_symbol *sym, char *fl, char *fn) 659233611Sfabient{ 660233611Sfabient int i; 661233611Sfabient unsigned int hash; 662233611Sfabient struct pmcpl_ct_node *ct; 663233611Sfabient struct pmcpl_ct_node_hash *h; 664233611Sfabient pmcstat_interned_string ifl, ifn; 665233611Sfabient 666233611Sfabient if (fn != NULL) { 667233611Sfabient ifl = pmcstat_string_intern(fl); 668233611Sfabient ifn = pmcstat_string_intern(fn); 669233611Sfabient } else { 670233611Sfabient ifl = 0; 671233611Sfabient ifn = 0; 672233611Sfabient } 673233611Sfabient 674203790Sfabient for (hash = i = 0; i < (int)sizeof(uintfptr_t); i++) 675203790Sfabient hash += (pc >> i) & 0xFF; 676203790Sfabient 677203790Sfabient hash &= PMCSTAT_HASH_MASK; 678203790Sfabient 679233611Sfabient STAILQ_FOREACH(h, &pmcpl_ct_node_hash[hash], pch_next) { 680203790Sfabient ct = h->pch_ctnode; 681203790Sfabient 682203790Sfabient assert(ct != NULL); 683203790Sfabient 684203790Sfabient if (ct->pct_image == image && ct->pct_func == pc) { 685233611Sfabient if (fn == NULL) 686233611Sfabient return (ct); 687233611Sfabient if (ct->pct_type == PMCPL_PCT_NAME && 688233611Sfabient ct->pct_ifl == ifl && ct->pct_ifn == ifn) 689233611Sfabient return (ct); 690203790Sfabient } 691203790Sfabient } 692203790Sfabient 693203790Sfabient /* 694203790Sfabient * We haven't seen this (pmcid, pc) tuple yet, so allocate a 695203790Sfabient * new callgraph node and a new hash table entry for it. 696203790Sfabient */ 697233611Sfabient ct = pmcpl_ct_node_allocate(); 698203790Sfabient if ((h = malloc(sizeof(*h))) == NULL) 699203790Sfabient err(EX_OSERR, "ERROR: Could not allocate callgraph node"); 700203790Sfabient 701233611Sfabient if (fn != NULL) { 702233611Sfabient ct->pct_type = PMCPL_PCT_NAME; 703233611Sfabient ct->pct_ifl = ifl; 704233611Sfabient ct->pct_ifn = ifn; 705233611Sfabient } else 706233611Sfabient ct->pct_type = PMCPL_PCT_ADDR; 707233611Sfabient ct->pct_image = image; 708233611Sfabient ct->pct_func = pc; 709233611Sfabient ct->pct_sym = sym; 710233611Sfabient 711203790Sfabient h->pch_ctnode = ct; 712233611Sfabient STAILQ_INSERT_HEAD(&pmcpl_ct_node_hash[hash], h, pch_next); 713203790Sfabient return (ct); 714203790Sfabient} 715203790Sfabient 716203790Sfabient/* 717203790Sfabient * Record a callchain. 718203790Sfabient */ 719203790Sfabient 720203790Sfabientvoid 721203790Sfabientpmcpl_ct_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 722203790Sfabient uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 723203790Sfabient{ 724233611Sfabient int i, n, pmcin; 725233611Sfabient uintfptr_t pc, loadaddress; 726233611Sfabient struct pmcstat_image *image; 727233611Sfabient struct pmcstat_symbol *sym; 728203790Sfabient struct pmcstat_pcmap *ppm[PMC_CALLCHAIN_DEPTH_MAX]; 729203790Sfabient struct pmcstat_process *km; 730233611Sfabient struct pmcpl_ct_node *ct; 731233611Sfabient struct pmcpl_ct_node *ctl[PMC_CALLCHAIN_DEPTH_MAX+1]; 732203790Sfabient 733203790Sfabient (void) cpu; 734203790Sfabient 735203790Sfabient assert(nsamples>0 && nsamples<=PMC_CALLCHAIN_DEPTH_MAX); 736203790Sfabient 737203790Sfabient /* Get the PMC index. */ 738203790Sfabient pmcin = pmcr->pr_pmcin; 739203790Sfabient 740203790Sfabient /* 741203790Sfabient * Validate mapping for the callchain. 742203790Sfabient * Go from bottom to first invalid entry. 743203790Sfabient */ 744203790Sfabient km = pmcstat_kernproc; 745203790Sfabient for (n = 0; n < (int)nsamples; n++) { 746203790Sfabient ppm[n] = pmcstat_process_find_map(usermode ? 747203790Sfabient pp : km, cc[n]); 748203790Sfabient if (ppm[n] == NULL) { 749203790Sfabient /* Detect full frame capture (kernel + user). */ 750203790Sfabient if (!usermode) { 751203790Sfabient ppm[n] = pmcstat_process_find_map(pp, cc[n]); 752203790Sfabient if (ppm[n] != NULL) 753203790Sfabient km = pp; 754203790Sfabient } 755203790Sfabient } 756203790Sfabient if (ppm[n] == NULL) 757203790Sfabient break; 758203790Sfabient } 759203790Sfabient if (n-- == 0) { 760203790Sfabient pmcstat_stats.ps_callchain_dubious_frames++; 761206090Sfabient pmcr->pr_dubious_frames++; 762203790Sfabient return; 763203790Sfabient } 764203790Sfabient 765203790Sfabient /* Increase the call generation counter. */ 766203790Sfabient pmcpl_ct_samples_grow(&pmcpl_ct_callid); 767203790Sfabient pmcpl_ct_callid.sb[pmcin]++; 768203790Sfabient 769203790Sfabient /* 770233611Sfabient * Build node list. 771203790Sfabient */ 772233611Sfabient ctl[0] = pmcpl_ct_root; 773233611Sfabient for (i = 1; n >= 0; n--) { 774233611Sfabient image = ppm[n]->ppm_image; 775233611Sfabient loadaddress = ppm[n]->ppm_lowpc + 776233611Sfabient image->pi_vaddr - image->pi_start; 777233611Sfabient /* Convert to an offset in the image. */ 778233611Sfabient pc = cc[n] - loadaddress; 779233611Sfabient /* 780233611Sfabient * Try determine the function at this offset. If we can't 781233611Sfabient * find a function round leave the `pc' value alone. 782233611Sfabient */ 783233611Sfabient if ((sym = pmcstat_symbol_search(image, pc)) != NULL) 784233611Sfabient pc = sym->ps_start; 785233611Sfabient else 786233611Sfabient pmcstat_stats.ps_samples_unknown_function++; 787233611Sfabient 788233611Sfabient ct = pmcpl_ct_node_hash_lookup(image, pc, sym, NULL, NULL); 789233611Sfabient if (ct == NULL) { 790203790Sfabient pmcstat_stats.ps_callchain_dubious_frames++; 791203790Sfabient continue; 792203790Sfabient } 793233611Sfabient ctl[i++] = ct; 794203790Sfabient } 795233611Sfabient /* No valid node found. */ 796233611Sfabient if (i == 1) 797233611Sfabient return; 798233611Sfabient n = i; 799203790Sfabient 800233611Sfabient ct = ctl[0]; 801233611Sfabient for (i = 1; i < n; i++) 802233611Sfabient pmcpl_ct_node_update(ctl[i-1], ctl[i], pmcin, 1, 1); 803233611Sfabient 804203790Sfabient /* 805203790Sfabient * Increment the sample count for this PMC. 806203790Sfabient */ 807233611Sfabient pmcpl_ct_samples_grow(&ctl[n-1]->pct_samples); 808233611Sfabient ctl[n-1]->pct_samples.sb[pmcin]++; 809203790Sfabient 810233611Sfabient /* Update per instruction sample if required. */ 811233611Sfabient if (args.pa_ctdumpinstr) 812233611Sfabient pmcpl_ct_instr_add(ctl[n-1], pmcin, cc[0] - 813233611Sfabient (ppm[0]->ppm_lowpc + ppm[0]->ppm_image->pi_vaddr - 814233611Sfabient ppm[0]->ppm_image->pi_start), 1); 815233611Sfabient} 816233611Sfabient 817233611Sfabient/* 818233611Sfabient * Print node child cost. 819233611Sfabient */ 820233611Sfabient 821233611Sfabientstatic void 822233611Sfabientpmcpl_ct_node_printchild(struct pmcpl_ct_node *ct, uintfptr_t paddr, 823233611Sfabient int pline) 824233611Sfabient{ 825233611Sfabient int i, j, line; 826233611Sfabient uintfptr_t addr; 827233611Sfabient struct pmcpl_ct_node *child; 828233611Sfabient char sourcefile[PATH_MAX]; 829233611Sfabient char funcname[PATH_MAX]; 830233611Sfabient 831233611Sfabient /* 832233611Sfabient * Child cost. 833233611Sfabient * TODO: attach child cost to the real position in the funtion. 834233611Sfabient * TODO: cfn=<fn> / call <ncall> addr(<fn>) / addr(call <fn>) <arccost> 835233611Sfabient */ 836233611Sfabient for (i=0 ; i<ct->pct_narc; i++) { 837233611Sfabient child = ct->pct_arc[i].pcta_child; 838233611Sfabient /* Object binary. */ 839233611Sfabient fprintf(args.pa_graphfile, "cob=%s\n", 840233611Sfabient pmcstat_string_unintern(child->pct_image->pi_fullpath)); 841233611Sfabient /* Child function name. */ 842233611Sfabient addr = child->pct_image->pi_vaddr + child->pct_func; 843233611Sfabient line = 0; 844233611Sfabient /* Child function source file. */ 845233611Sfabient if (child->pct_type == PMCPL_PCT_NAME) { 846233611Sfabient fprintf(args.pa_graphfile, "cfi=%s\ncfn=%s\n", 847233611Sfabient pmcstat_string_unintern(child->pct_ifl), 848233611Sfabient pmcstat_string_unintern(child->pct_ifn)); 849233611Sfabient } else if (pmcstat_image_addr2line(child->pct_image, addr, 850233611Sfabient sourcefile, sizeof(sourcefile), &line, 851233611Sfabient funcname, sizeof(funcname))) { 852233611Sfabient fprintf(args.pa_graphfile, "cfi=%s\ncfn=%s\n", 853233611Sfabient sourcefile, funcname); 854233611Sfabient } else { 855233611Sfabient if (child->pct_sym != NULL) 856233611Sfabient fprintf(args.pa_graphfile, 857233611Sfabient "cfi=???\ncfn=%s\n", 858233611Sfabient pmcstat_string_unintern( 859233611Sfabient child->pct_sym->ps_name)); 860233611Sfabient else 861233611Sfabient fprintf(args.pa_graphfile, 862233611Sfabient "cfi=???\ncfn=%p\n", (void *)addr); 863233611Sfabient } 864233611Sfabient 865233611Sfabient /* Child function address, line and call count. */ 866233611Sfabient fprintf(args.pa_graphfile, "calls=%u %p %u\n", 867233611Sfabient ct->pct_arc[i].pcta_call, (void *)addr, line); 868233611Sfabient 869233611Sfabient /* 870233611Sfabient * Call address, line, sample. 871233611Sfabient * TODO: Associate call address to the right location. 872233611Sfabient */ 873233611Sfabient fprintf(args.pa_graphfile, "%p %u", (void *)paddr, pline); 874233611Sfabient for (j = 0; j<pmcstat_npmcs; j++) 875233611Sfabient fprintf(args.pa_graphfile, " %u", 876233611Sfabient PMCPL_CT_SAMPLE(j, &ct->pct_arc[i].pcta_samples)); 877233611Sfabient fprintf(args.pa_graphfile, "\n"); 878203790Sfabient } 879203790Sfabient} 880203790Sfabient 881203790Sfabient/* 882203790Sfabient * Print node self cost. 883203790Sfabient */ 884203790Sfabient 885203790Sfabientstatic void 886203790Sfabientpmcpl_ct_node_printself(struct pmcpl_ct_node *ct) 887203790Sfabient{ 888233611Sfabient int i, j, fline, line; 889233611Sfabient uintfptr_t faddr, addr; 890203790Sfabient char sourcefile[PATH_MAX]; 891203790Sfabient char funcname[PATH_MAX]; 892203790Sfabient 893203790Sfabient /* 894203790Sfabient * Object binary. 895203790Sfabient */ 896233611Sfabient fprintf(args.pa_graphfile, "ob=%s\n", 897233611Sfabient pmcstat_string_unintern(ct->pct_image->pi_fullpath)); 898203790Sfabient 899203790Sfabient /* 900203790Sfabient * Function name. 901203790Sfabient */ 902233611Sfabient faddr = ct->pct_image->pi_vaddr + ct->pct_func; 903233611Sfabient fline = 0; 904233611Sfabient if (ct->pct_type == PMCPL_PCT_NAME) { 905233611Sfabient fprintf(args.pa_graphfile, "fl=%s\nfn=%s\n", 906233611Sfabient pmcstat_string_unintern(ct->pct_ifl), 907233611Sfabient pmcstat_string_unintern(ct->pct_ifn)); 908233611Sfabient } else if (pmcstat_image_addr2line(ct->pct_image, faddr, 909233611Sfabient sourcefile, sizeof(sourcefile), &fline, 910203790Sfabient funcname, sizeof(funcname))) { 911233611Sfabient fprintf(args.pa_graphfile, "fl=%s\nfn=%s\n", 912233611Sfabient sourcefile, funcname); 913203790Sfabient } else { 914233611Sfabient if (ct->pct_sym != NULL) 915233611Sfabient fprintf(args.pa_graphfile, "fl=???\nfn=%s\n", 916233611Sfabient pmcstat_string_unintern(ct->pct_sym->ps_name)); 917203790Sfabient else 918233611Sfabient fprintf(args.pa_graphfile, "fl=???\nfn=%p\n", 919203790Sfabient (void *)(ct->pct_image->pi_vaddr + ct->pct_func)); 920203790Sfabient } 921203790Sfabient 922203790Sfabient /* 923203790Sfabient * Self cost. 924203790Sfabient */ 925203790Sfabient if (ct->pct_ninstr > 0) { 926233611Sfabient /* 927233611Sfabient * Per location cost. 928233611Sfabient */ 929203790Sfabient for (i = 0; i < ct->pct_ninstr; i++) { 930203790Sfabient addr = ct->pct_image->pi_vaddr + 931203790Sfabient ct->pct_instr[i].pctf_func; 932203790Sfabient line = 0; 933233611Sfabient pmcstat_image_addr2line(ct->pct_image, addr, 934203790Sfabient sourcefile, sizeof(sourcefile), &line, 935233611Sfabient funcname, sizeof(funcname)); 936233611Sfabient fprintf(args.pa_graphfile, "%p %u", 937233611Sfabient (void *)addr, line); 938203790Sfabient for (j = 0; j<pmcstat_npmcs; j++) 939203790Sfabient fprintf(args.pa_graphfile, " %u", 940203790Sfabient PMCPL_CT_SAMPLE(j, 941203790Sfabient &ct->pct_instr[i].pctf_samples)); 942203790Sfabient fprintf(args.pa_graphfile, "\n"); 943203790Sfabient } 944203790Sfabient } else { 945233611Sfabient /* Global cost function cost. */ 946233611Sfabient fprintf(args.pa_graphfile, "%p %u", (void *)faddr, fline); 947203790Sfabient for (i = 0; i<pmcstat_npmcs ; i++) 948203790Sfabient fprintf(args.pa_graphfile, " %u", 949203790Sfabient PMCPL_CT_SAMPLE(i, &ct->pct_samples)); 950203790Sfabient fprintf(args.pa_graphfile, "\n"); 951203790Sfabient } 952233611Sfabient 953233611Sfabient pmcpl_ct_node_printchild(ct, faddr, fline); 954203790Sfabient} 955203790Sfabient 956233611Sfabientstatic void 957233611Sfabientpmcpl_ct_printnode(struct pmcpl_ct_node *ct) 958233611Sfabient{ 959233611Sfabient int i; 960233611Sfabient 961233611Sfabient if (ct == pmcpl_ct_root) { 962233611Sfabient fprintf(args.pa_graphfile, "fn=root\n"); 963233611Sfabient fprintf(args.pa_graphfile, "0x0 1"); 964233611Sfabient for (i = 0; i<pmcstat_npmcs ; i++) 965233611Sfabient fprintf(args.pa_graphfile, " 0"); 966233611Sfabient fprintf(args.pa_graphfile, "\n"); 967233611Sfabient pmcpl_ct_node_printchild(ct, 0, 0); 968233611Sfabient } else 969233611Sfabient pmcpl_ct_node_printself(ct); 970233611Sfabient} 971233611Sfabient 972203790Sfabient/* 973233611Sfabient * Breadth first traversal. 974203790Sfabient */ 975203790Sfabient 976203790Sfabientstatic void 977233611Sfabientpmcpl_ct_bfs(struct pmcpl_ct_node *ct) 978203790Sfabient{ 979233611Sfabient int i; 980233611Sfabient struct pmcpl_ct_node_hash *pch, *pchc; 981203790Sfabient struct pmcpl_ct_node *child; 982233611Sfabient STAILQ_HEAD(,pmcpl_ct_node_hash) q; 983233611Sfabient 984233611Sfabient STAILQ_INIT(&q); 985233611Sfabient if ((pch = malloc(sizeof(*pch))) == NULL) 986233611Sfabient err(EX_OSERR, "ERROR: Cannot allocate queue"); 987233611Sfabient pch->pch_ctnode = ct; 988233611Sfabient STAILQ_INSERT_TAIL(&q, pch, pch_next); 989233611Sfabient ct->pct_color = PMCPL_PCT_BLACK; 990233611Sfabient 991233611Sfabient while (!STAILQ_EMPTY(&q)) { 992233611Sfabient pch = STAILQ_FIRST(&q); 993233611Sfabient STAILQ_REMOVE_HEAD(&q, pch_next); 994233611Sfabient pmcpl_ct_printnode(pch->pch_ctnode); 995233611Sfabient for (i = 0; i<pch->pch_ctnode->pct_narc; i++) { 996233611Sfabient child = pch->pch_ctnode->pct_arc[i].pcta_child; 997233611Sfabient if (child->pct_color == PMCPL_PCT_WHITE) { 998233611Sfabient child->pct_color = PMCPL_PCT_BLACK; 999233611Sfabient if ((pchc = malloc(sizeof(*pchc))) == NULL) 1000233611Sfabient err(EX_OSERR, 1001233611Sfabient "ERROR: Cannot allocate queue"); 1002233611Sfabient pchc->pch_ctnode = child; 1003233611Sfabient STAILQ_INSERT_TAIL(&q, pchc, pch_next); 1004233611Sfabient } 1005233611Sfabient } 1006233611Sfabient free(pch); 1007233611Sfabient } 1008233611Sfabient} 1009233611Sfabient 1010233611Sfabient/* 1011233611Sfabient * Detect and fix inlined location. 1012233611Sfabient */ 1013233611Sfabient 1014233611Sfabientstatic void 1015233611Sfabient_pmcpl_ct_expand_inline(struct pmcpl_ct_node *ct) 1016233611Sfabient{ 1017233611Sfabient int i, j; 1018233611Sfabient unsigned fline, line, v; 1019233611Sfabient uintfptr_t faddr, addr, pc; 1020203790Sfabient char sourcefile[PATH_MAX]; 1021233611Sfabient char ffuncname[PATH_MAX], funcname[PATH_MAX]; 1022233611Sfabient char buffer[PATH_MAX]; 1023233611Sfabient struct pmcpl_ct_node *child; 1024203790Sfabient 1025203790Sfabient /* 1026233611Sfabient * Resolve parent and compare to each instr location. 1027203790Sfabient */ 1028233611Sfabient faddr = ct->pct_image->pi_vaddr + ct->pct_func; 1029233611Sfabient fline = 0; 1030233611Sfabient if (!pmcstat_image_addr2line(ct->pct_image, faddr, 1031233611Sfabient sourcefile, sizeof(sourcefile), &fline, 1032233611Sfabient ffuncname, sizeof(ffuncname))) 1033233611Sfabient return; 1034203790Sfabient 1035233611Sfabient for (i = 0; i < ct->pct_ninstr; i++) { 1036233611Sfabient addr = ct->pct_image->pi_vaddr + 1037233611Sfabient ct->pct_instr[i].pctf_func; 1038233611Sfabient line = 0; 1039233611Sfabient if (!pmcstat_image_addr2line(ct->pct_image, addr, 1040203790Sfabient sourcefile, sizeof(sourcefile), &line, 1041233611Sfabient funcname, sizeof(funcname))) 1042233611Sfabient continue; 1043203790Sfabient 1044233611Sfabient if (strcmp(funcname, ffuncname) == 0) 1045233611Sfabient continue; 1046203790Sfabient 1047233611Sfabient /* 1048233611Sfabient * - Lookup/create inline node by function name. 1049233611Sfabient * - Move instr PMCs to the inline node. 1050233611Sfabient * - Link nodes. 1051233611Sfabient * The lookup create a specific node per image/pc. 1052233611Sfabient */ 1053233611Sfabient if (args.pa_verbosity >= 2) 1054233611Sfabient fprintf(args.pa_printfile, 1055233611Sfabient "WARNING: inlined function at %p %s in %s\n", 1056233611Sfabient (void *)addr, funcname, ffuncname); 1057233611Sfabient 1058233611Sfabient snprintf(buffer, sizeof(buffer), "%s@%s", 1059233611Sfabient funcname, ffuncname); 1060233611Sfabient child = pmcpl_ct_node_hash_lookup(ct->pct_image, 1061233611Sfabient ct->pct_func, ct->pct_sym, sourcefile, buffer); 1062233611Sfabient assert(child != NULL); 1063233611Sfabient pc = ct->pct_instr[i].pctf_func; 1064233611Sfabient for (j = 0; j<pmcstat_npmcs; j++) { 1065233611Sfabient v = PMCPL_CT_SAMPLE(j, 1066233611Sfabient &ct->pct_instr[i].pctf_samples); 1067233611Sfabient if (v == 0) 1068233611Sfabient continue; 1069233611Sfabient pmcpl_ct_instr_add(child, j, pc, v); 1070233611Sfabient pmcpl_ct_node_update(ct, child, j, v, 0); 1071233611Sfabient if (j < ct->pct_samples.npmcs) 1072233611Sfabient ct->pct_samples.sb[j] -= 1073233611Sfabient ct->pct_instr[i].pctf_samples.sb[j]; 1074233611Sfabient ct->pct_instr[i].pctf_samples.sb[j] = 0; 1075203790Sfabient } 1076203790Sfabient } 1077203790Sfabient} 1078203790Sfabient 1079233611Sfabientstatic void 1080233611Sfabientpmcpl_ct_expand_inline(void) 1081233611Sfabient{ 1082233611Sfabient int i; 1083233611Sfabient struct pmcpl_ct_node_hash *pch; 1084233611Sfabient 1085233611Sfabient if (!args.pa_ctdumpinstr) 1086233611Sfabient return; 1087233611Sfabient 1088233611Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) 1089233611Sfabient STAILQ_FOREACH(pch, &pmcpl_ct_node_hash[i], pch_next) 1090233611Sfabient if (pch->pch_ctnode->pct_type == PMCPL_PCT_ADDR) 1091233611Sfabient _pmcpl_ct_expand_inline(pch->pch_ctnode); 1092233611Sfabient} 1093233611Sfabient 1094203790Sfabient/* 1095203790Sfabient * Clean the PMC name for Kcachegrind formula 1096203790Sfabient */ 1097203790Sfabient 1098203790Sfabientstatic void 1099203790Sfabientpmcpl_ct_fixup_pmcname(char *s) 1100203790Sfabient{ 1101203790Sfabient char *p; 1102203790Sfabient 1103203790Sfabient for (p = s; *p; p++) 1104203790Sfabient if (!isalnum(*p)) 1105203790Sfabient *p = '_'; 1106203790Sfabient} 1107203790Sfabient 1108203790Sfabient/* 1109203790Sfabient * Print a calltree (KCachegrind) for all PMCs. 1110203790Sfabient */ 1111203790Sfabient 1112203790Sfabientstatic void 1113203790Sfabientpmcpl_ct_print(void) 1114203790Sfabient{ 1115233611Sfabient int i; 1116233611Sfabient char name[40]; 1117203790Sfabient struct pmcpl_ct_sample rsamples; 1118203790Sfabient 1119203790Sfabient pmcpl_ct_samples_root(&rsamples); 1120233611Sfabient pmcpl_ct_expand_inline(); 1121203790Sfabient 1122203790Sfabient fprintf(args.pa_graphfile, 1123203790Sfabient "version: 1\n" 1124203790Sfabient "creator: pmcstat\n" 1125203790Sfabient "positions: instr line\n" 1126203790Sfabient "events:"); 1127203790Sfabient for (i=0; i<pmcstat_npmcs; i++) { 1128203790Sfabient snprintf(name, sizeof(name), "%s_%d", 1129203790Sfabient pmcstat_pmcindex_to_name(i), i); 1130203790Sfabient pmcpl_ct_fixup_pmcname(name); 1131203790Sfabient fprintf(args.pa_graphfile, " %s", name); 1132203790Sfabient } 1133203790Sfabient fprintf(args.pa_graphfile, "\nsummary:"); 1134203790Sfabient for (i=0; i<pmcstat_npmcs ; i++) 1135203790Sfabient fprintf(args.pa_graphfile, " %u", 1136203790Sfabient PMCPL_CT_SAMPLE(i, &rsamples)); 1137203790Sfabient fprintf(args.pa_graphfile, "\n"); 1138233611Sfabient pmcpl_ct_bfs(pmcpl_ct_root); 1139203790Sfabient pmcpl_ct_samples_free(&rsamples); 1140203790Sfabient} 1141203790Sfabient 1142203790Sfabientint 1143203790Sfabientpmcpl_ct_configure(char *opt) 1144203790Sfabient{ 1145203790Sfabient 1146203790Sfabient if (strncmp(opt, "skiplink=", 9) == 0) { 1147203790Sfabient pmcstat_skiplink = atoi(opt+9); 1148203790Sfabient } else 1149203790Sfabient return (0); 1150203790Sfabient 1151203790Sfabient return (1); 1152203790Sfabient} 1153203790Sfabient 1154203790Sfabientint 1155203790Sfabientpmcpl_ct_init(void) 1156203790Sfabient{ 1157203790Sfabient int i; 1158203790Sfabient 1159233611Sfabient pmcpl_ct_root = pmcpl_ct_node_allocate(); 1160203790Sfabient 1161203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) 1162233611Sfabient STAILQ_INIT(&pmcpl_ct_node_hash[i]); 1163203790Sfabient 1164203790Sfabient pmcpl_ct_samples_init(&pmcpl_ct_callid); 1165203790Sfabient 1166203790Sfabient return (0); 1167203790Sfabient} 1168203790Sfabient 1169203790Sfabientvoid 1170203790Sfabientpmcpl_ct_shutdown(FILE *mf) 1171203790Sfabient{ 1172203790Sfabient int i; 1173203790Sfabient struct pmcpl_ct_node_hash *pch, *pchtmp; 1174203790Sfabient 1175203790Sfabient (void) mf; 1176203790Sfabient 1177203790Sfabient if (args.pa_flags & FLAG_DO_CALLGRAPHS) 1178203790Sfabient pmcpl_ct_print(); 1179203790Sfabient 1180203790Sfabient /* 1181203790Sfabient * Free memory. 1182203790Sfabient */ 1183203790Sfabient 1184203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) { 1185233611Sfabient STAILQ_FOREACH_SAFE(pch, &pmcpl_ct_node_hash[i], pch_next, 1186203790Sfabient pchtmp) { 1187203790Sfabient pmcpl_ct_node_free(pch->pch_ctnode); 1188203790Sfabient free(pch); 1189203790Sfabient } 1190203790Sfabient } 1191203790Sfabient 1192203790Sfabient pmcpl_ct_node_free(pmcpl_ct_root); 1193203790Sfabient pmcpl_ct_root = NULL; 1194203790Sfabient 1195203790Sfabient pmcpl_ct_samples_free(&pmcpl_ct_callid); 1196203790Sfabient} 1197203790Sfabient 1198