1203790Sfabient/*- 2203790Sfabient * Copyright (c) 2005-2007, Joseph Koshy 3203790Sfabient * Copyright (c) 2007 The FreeBSD Foundation 4203790Sfabient * All rights reserved. 5203790Sfabient * 6203790Sfabient * Portions of this software were developed by A. Joseph Koshy under 7203790Sfabient * sponsorship from the FreeBSD Foundation and Google, Inc. 8203790Sfabient * 9203790Sfabient * Redistribution and use in source and binary forms, with or without 10203790Sfabient * modification, are permitted provided that the following conditions 11203790Sfabient * are met: 12203790Sfabient * 1. Redistributions of source code must retain the above copyright 13203790Sfabient * notice, this list of conditions and the following disclaimer. 14203790Sfabient * 2. Redistributions in binary form must reproduce the above copyright 15203790Sfabient * notice, this list of conditions and the following disclaimer in the 16203790Sfabient * documentation and/or other materials provided with the distribution. 17203790Sfabient * 18203790Sfabient * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19203790Sfabient * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20203790Sfabient * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21203790Sfabient * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22203790Sfabient * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23203790Sfabient * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24203790Sfabient * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25203790Sfabient * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26203790Sfabient * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27203790Sfabient * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28203790Sfabient * SUCH DAMAGE. 29203790Sfabient */ 30203790Sfabient 31203790Sfabient/* 32203790Sfabient * Transform a hwpmc(4) log into human readable form, and into 33203790Sfabient * gprof(1) compatible profiles. 34203790Sfabient */ 35203790Sfabient 36203790Sfabient#include <sys/cdefs.h> 37203790Sfabient__FBSDID("$FreeBSD$"); 38203790Sfabient 39203790Sfabient#include <sys/param.h> 40203790Sfabient#include <sys/endian.h> 41203790Sfabient#include <sys/gmon.h> 42203790Sfabient#include <sys/imgact_aout.h> 43203790Sfabient#include <sys/imgact_elf.h> 44203790Sfabient#include <sys/mman.h> 45203790Sfabient#include <sys/pmc.h> 46203790Sfabient#include <sys/queue.h> 47203790Sfabient#include <sys/socket.h> 48203790Sfabient#include <sys/stat.h> 49203790Sfabient#include <sys/wait.h> 50203790Sfabient 51203790Sfabient#include <netinet/in.h> 52203790Sfabient 53203790Sfabient#include <assert.h> 54203790Sfabient#include <curses.h> 55203790Sfabient#include <err.h> 56203790Sfabient#include <errno.h> 57203790Sfabient#include <fcntl.h> 58203790Sfabient#include <gelf.h> 59203790Sfabient#include <libgen.h> 60203790Sfabient#include <limits.h> 61203790Sfabient#include <netdb.h> 62203790Sfabient#include <pmc.h> 63203790Sfabient#include <pmclog.h> 64203790Sfabient#include <sysexits.h> 65203790Sfabient#include <stdint.h> 66203790Sfabient#include <stdio.h> 67203790Sfabient#include <stdlib.h> 68203790Sfabient#include <string.h> 69203790Sfabient#include <unistd.h> 70203790Sfabient 71203790Sfabient#include "pmcstat.h" 72203790Sfabient#include "pmcstat_log.h" 73203790Sfabient#include "pmcstat_top.h" 74203790Sfabient#include "pmcpl_callgraph.h" 75203790Sfabient 76203790Sfabient/* Get the sample value in percent related to nsamples. */ 77203790Sfabient#define PMCPL_CG_COUNTP(a) \ 78203790Sfabient ((a)->pcg_count * 100.0 / nsamples) 79203790Sfabient 80203790Sfabient/* 81203790Sfabient * The toplevel CG nodes (i.e., with rank == 0) are placed in a hash table. 82203790Sfabient */ 83203790Sfabient 84203790Sfabientstruct pmcstat_cgnode_hash_list pmcstat_cgnode_hash[PMCSTAT_NHASH]; 85203790Sfabientint pmcstat_cgnode_hash_count; 86203790Sfabient 87203790Sfabientstatic pmcstat_interned_string pmcstat_previous_filename_printed; 88203790Sfabient 89203790Sfabientstatic struct pmcstat_cgnode * 90203790Sfabientpmcstat_cgnode_allocate(struct pmcstat_image *image, uintfptr_t pc) 91203790Sfabient{ 92203790Sfabient struct pmcstat_cgnode *cg; 93203790Sfabient 94203790Sfabient if ((cg = malloc(sizeof(*cg))) == NULL) 95203790Sfabient err(EX_OSERR, "ERROR: Cannot allocate callgraph node"); 96203790Sfabient 97203790Sfabient cg->pcg_image = image; 98203790Sfabient cg->pcg_func = pc; 99203790Sfabient 100203790Sfabient cg->pcg_count = 0; 101203790Sfabient cg->pcg_nchildren = 0; 102203790Sfabient LIST_INIT(&cg->pcg_children); 103203790Sfabient 104203790Sfabient return (cg); 105203790Sfabient} 106203790Sfabient 107203790Sfabient/* 108203790Sfabient * Free a node and its children. 109203790Sfabient */ 110203790Sfabientstatic void 111203790Sfabientpmcstat_cgnode_free(struct pmcstat_cgnode *cg) 112203790Sfabient{ 113203790Sfabient struct pmcstat_cgnode *cgc, *cgtmp; 114203790Sfabient 115203790Sfabient LIST_FOREACH_SAFE(cgc, &cg->pcg_children, pcg_sibling, cgtmp) 116203790Sfabient pmcstat_cgnode_free(cgc); 117203790Sfabient free(cg); 118203790Sfabient} 119203790Sfabient 120203790Sfabient/* 121203790Sfabient * Look for a callgraph node associated with pmc `pmcid' in the global 122203790Sfabient * hash table that corresponds to the given `pc' value in the process 123203790Sfabient * `pp'. 124203790Sfabient */ 125203790Sfabientstatic struct pmcstat_cgnode * 126203790Sfabientpmcstat_cgnode_hash_lookup_pc(struct pmcstat_process *pp, pmc_id_t pmcid, 127203790Sfabient uintfptr_t pc, int usermode) 128203790Sfabient{ 129203790Sfabient struct pmcstat_pcmap *ppm; 130203790Sfabient struct pmcstat_symbol *sym; 131203790Sfabient struct pmcstat_image *image; 132203790Sfabient struct pmcstat_cgnode *cg; 133203790Sfabient struct pmcstat_cgnode_hash *h; 134203790Sfabient uintfptr_t loadaddress; 135203790Sfabient unsigned int i, hash; 136203790Sfabient 137203790Sfabient ppm = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, pc); 138203790Sfabient if (ppm == NULL) 139203790Sfabient return (NULL); 140203790Sfabient 141203790Sfabient image = ppm->ppm_image; 142203790Sfabient 143203790Sfabient loadaddress = ppm->ppm_lowpc + image->pi_vaddr - image->pi_start; 144203790Sfabient pc -= loadaddress; /* Convert to an offset in the image. */ 145203790Sfabient 146203790Sfabient /* 147203790Sfabient * Try determine the function at this offset. If we can't 148203790Sfabient * find a function round leave the `pc' value alone. 149203790Sfabient */ 150203790Sfabient if ((sym = pmcstat_symbol_search(image, pc)) != NULL) 151203790Sfabient pc = sym->ps_start; 152212176Sfabient else 153212176Sfabient pmcstat_stats.ps_samples_unknown_function++; 154203790Sfabient 155203790Sfabient for (hash = i = 0; i < sizeof(uintfptr_t); i++) 156203790Sfabient hash += (pc >> i) & 0xFF; 157203790Sfabient 158203790Sfabient hash &= PMCSTAT_HASH_MASK; 159203790Sfabient 160203790Sfabient cg = NULL; 161203790Sfabient LIST_FOREACH(h, &pmcstat_cgnode_hash[hash], pch_next) 162203790Sfabient { 163203790Sfabient if (h->pch_pmcid != pmcid) 164203790Sfabient continue; 165203790Sfabient 166203790Sfabient cg = h->pch_cgnode; 167203790Sfabient 168203790Sfabient assert(cg != NULL); 169203790Sfabient 170203790Sfabient if (cg->pcg_image == image && cg->pcg_func == pc) 171203790Sfabient return (cg); 172203790Sfabient } 173203790Sfabient 174203790Sfabient /* 175203790Sfabient * We haven't seen this (pmcid, pc) tuple yet, so allocate a 176203790Sfabient * new callgraph node and a new hash table entry for it. 177203790Sfabient */ 178203790Sfabient cg = pmcstat_cgnode_allocate(image, pc); 179203790Sfabient if ((h = malloc(sizeof(*h))) == NULL) 180203790Sfabient err(EX_OSERR, "ERROR: Could not allocate callgraph node"); 181203790Sfabient 182203790Sfabient h->pch_pmcid = pmcid; 183203790Sfabient h->pch_cgnode = cg; 184203790Sfabient LIST_INSERT_HEAD(&pmcstat_cgnode_hash[hash], h, pch_next); 185203790Sfabient 186203790Sfabient pmcstat_cgnode_hash_count++; 187203790Sfabient 188203790Sfabient return (cg); 189203790Sfabient} 190203790Sfabient 191203790Sfabient/* 192203790Sfabient * Compare two callgraph nodes for sorting. 193203790Sfabient */ 194203790Sfabientstatic int 195203790Sfabientpmcstat_cgnode_compare(const void *a, const void *b) 196203790Sfabient{ 197203790Sfabient const struct pmcstat_cgnode *const *pcg1, *const *pcg2, *cg1, *cg2; 198203790Sfabient 199203790Sfabient pcg1 = (const struct pmcstat_cgnode *const *) a; 200203790Sfabient cg1 = *pcg1; 201203790Sfabient pcg2 = (const struct pmcstat_cgnode *const *) b; 202203790Sfabient cg2 = *pcg2; 203203790Sfabient 204203790Sfabient /* Sort in reverse order */ 205203790Sfabient if (cg1->pcg_count < cg2->pcg_count) 206203790Sfabient return (1); 207203790Sfabient if (cg1->pcg_count > cg2->pcg_count) 208203790Sfabient return (-1); 209203790Sfabient return (0); 210203790Sfabient} 211203790Sfabient 212203790Sfabient/* 213203790Sfabient * Find (allocating if a needed) a callgraph node in the given 214203790Sfabient * parent with the same (image, pcoffset) pair. 215203790Sfabient */ 216203790Sfabient 217203790Sfabientstatic struct pmcstat_cgnode * 218203790Sfabientpmcstat_cgnode_find(struct pmcstat_cgnode *parent, struct pmcstat_image *image, 219203790Sfabient uintfptr_t pcoffset) 220203790Sfabient{ 221203790Sfabient struct pmcstat_cgnode *child; 222203790Sfabient 223203790Sfabient LIST_FOREACH(child, &parent->pcg_children, pcg_sibling) { 224203790Sfabient if (child->pcg_image == image && 225203790Sfabient child->pcg_func == pcoffset) 226203790Sfabient return (child); 227203790Sfabient } 228203790Sfabient 229203790Sfabient /* 230203790Sfabient * Allocate a new structure. 231203790Sfabient */ 232203790Sfabient 233203790Sfabient child = pmcstat_cgnode_allocate(image, pcoffset); 234203790Sfabient 235203790Sfabient /* 236203790Sfabient * Link it into the parent. 237203790Sfabient */ 238203790Sfabient LIST_INSERT_HEAD(&parent->pcg_children, child, pcg_sibling); 239203790Sfabient parent->pcg_nchildren++; 240203790Sfabient 241203790Sfabient return (child); 242203790Sfabient} 243203790Sfabient 244203790Sfabient/* 245203790Sfabient * Print one callgraph node. The output format is: 246203790Sfabient * 247203790Sfabient * indentation %(parent's samples) #nsamples function@object 248203790Sfabient */ 249203790Sfabientstatic void 250203790Sfabientpmcstat_cgnode_print(struct pmcstat_cgnode *cg, int depth, uint32_t total) 251203790Sfabient{ 252203790Sfabient uint32_t n; 253203790Sfabient const char *space; 254203790Sfabient struct pmcstat_symbol *sym; 255203790Sfabient struct pmcstat_cgnode **sortbuffer, **cgn, *pcg; 256203790Sfabient 257203790Sfabient space = " "; 258203790Sfabient 259203790Sfabient if (depth > 0) 260203790Sfabient (void) fprintf(args.pa_graphfile, "%*s", depth, space); 261203790Sfabient 262203790Sfabient if (cg->pcg_count == total) 263203790Sfabient (void) fprintf(args.pa_graphfile, "100.0%% "); 264203790Sfabient else 265203790Sfabient (void) fprintf(args.pa_graphfile, "%05.2f%% ", 266203790Sfabient 100.0 * cg->pcg_count / total); 267203790Sfabient 268203790Sfabient n = fprintf(args.pa_graphfile, " [%u] ", cg->pcg_count); 269203790Sfabient 270203790Sfabient /* #samples is a 12 character wide field. */ 271203790Sfabient if (n < 12) 272203790Sfabient (void) fprintf(args.pa_graphfile, "%*s", 12 - n, space); 273203790Sfabient 274203790Sfabient if (depth > 0) 275203790Sfabient (void) fprintf(args.pa_graphfile, "%*s", depth, space); 276203790Sfabient 277203790Sfabient sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); 278203790Sfabient if (sym) 279203790Sfabient (void) fprintf(args.pa_graphfile, "%s", 280203790Sfabient pmcstat_string_unintern(sym->ps_name)); 281203790Sfabient else 282203790Sfabient (void) fprintf(args.pa_graphfile, "%p", 283203790Sfabient (void *) (cg->pcg_image->pi_vaddr + cg->pcg_func)); 284203790Sfabient 285203790Sfabient if (pmcstat_previous_filename_printed != 286203790Sfabient cg->pcg_image->pi_fullpath) { 287203790Sfabient pmcstat_previous_filename_printed = cg->pcg_image->pi_fullpath; 288203790Sfabient (void) fprintf(args.pa_graphfile, " @ %s\n", 289203790Sfabient pmcstat_string_unintern( 290203790Sfabient pmcstat_previous_filename_printed)); 291203790Sfabient } else 292203790Sfabient (void) fprintf(args.pa_graphfile, "\n"); 293203790Sfabient 294203790Sfabient if (cg->pcg_nchildren == 0) 295203790Sfabient return; 296203790Sfabient 297203790Sfabient if ((sortbuffer = (struct pmcstat_cgnode **) 298203790Sfabient malloc(sizeof(struct pmcstat_cgnode *) * 299203790Sfabient cg->pcg_nchildren)) == NULL) 300203790Sfabient err(EX_OSERR, "ERROR: Cannot print callgraph"); 301203790Sfabient cgn = sortbuffer; 302203790Sfabient 303203790Sfabient LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling) 304203790Sfabient *cgn++ = pcg; 305203790Sfabient 306203790Sfabient assert(cgn - sortbuffer == (int) cg->pcg_nchildren); 307203790Sfabient 308203790Sfabient qsort(sortbuffer, cg->pcg_nchildren, sizeof(struct pmcstat_cgnode *), 309203790Sfabient pmcstat_cgnode_compare); 310203790Sfabient 311203790Sfabient for (cgn = sortbuffer, n = 0; n < cg->pcg_nchildren; n++, cgn++) 312203790Sfabient pmcstat_cgnode_print(*cgn, depth+1, cg->pcg_count); 313203790Sfabient 314203790Sfabient free(sortbuffer); 315203790Sfabient} 316203790Sfabient 317203790Sfabient/* 318203790Sfabient * Record a callchain. 319203790Sfabient */ 320203790Sfabient 321203790Sfabientvoid 322203790Sfabientpmcpl_cg_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 323203790Sfabient uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 324203790Sfabient{ 325203790Sfabient uintfptr_t pc, loadaddress; 326203790Sfabient uint32_t n; 327203790Sfabient struct pmcstat_image *image; 328203790Sfabient struct pmcstat_pcmap *ppm; 329203790Sfabient struct pmcstat_symbol *sym; 330203790Sfabient struct pmcstat_cgnode *parent, *child; 331203790Sfabient struct pmcstat_process *km; 332203790Sfabient pmc_id_t pmcid; 333203790Sfabient 334203790Sfabient (void) cpu; 335203790Sfabient 336203790Sfabient /* 337203790Sfabient * Find the callgraph node recorded in the global hash table 338203790Sfabient * for this (pmcid, pc). 339203790Sfabient */ 340203790Sfabient 341203790Sfabient pc = cc[0]; 342203790Sfabient pmcid = pmcr->pr_pmcid; 343203790Sfabient parent = pmcstat_cgnode_hash_lookup_pc(pp, pmcid, pc, usermode); 344203790Sfabient if (parent == NULL) { 345203790Sfabient pmcstat_stats.ps_callchain_dubious_frames++; 346206090Sfabient pmcr->pr_dubious_frames++; 347203790Sfabient return; 348203790Sfabient } 349203790Sfabient 350203790Sfabient parent->pcg_count++; 351203790Sfabient 352203790Sfabient /* 353203790Sfabient * For each return address in the call chain record, subject 354203790Sfabient * to the maximum depth desired. 355203790Sfabient * - Find the image associated with the sample. Stop if there 356203790Sfabient * there is no valid image at that address. 357203790Sfabient * - Find the function that overlaps the return address. 358203790Sfabient * - If found: use the start address of the function. 359203790Sfabient * If not found (say an object's symbol table is not present or 360203790Sfabient * is incomplete), round down to th gprof bucket granularity. 361203790Sfabient * - Convert return virtual address to an offset in the image. 362203790Sfabient * - Look for a child with the same {offset,image} tuple, 363203790Sfabient * inserting one if needed. 364203790Sfabient * - Increment the count of occurrences of the child. 365203790Sfabient */ 366203790Sfabient km = pmcstat_kernproc; 367203790Sfabient 368203790Sfabient for (n = 1; n < (uint32_t) args.pa_graphdepth && n < nsamples; n++, 369203790Sfabient parent = child) { 370203790Sfabient pc = cc[n]; 371203790Sfabient 372203790Sfabient ppm = pmcstat_process_find_map(usermode ? pp : km, pc); 373203790Sfabient if (ppm == NULL) { 374203790Sfabient /* Detect full frame capture (kernel + user). */ 375203790Sfabient if (!usermode) { 376203790Sfabient ppm = pmcstat_process_find_map(pp, pc); 377203790Sfabient if (ppm != NULL) 378203790Sfabient km = pp; 379203790Sfabient } 380203790Sfabient } 381203790Sfabient if (ppm == NULL) 382203790Sfabient return; 383203790Sfabient 384203790Sfabient image = ppm->ppm_image; 385203790Sfabient loadaddress = ppm->ppm_lowpc + image->pi_vaddr - 386203790Sfabient image->pi_start; 387203790Sfabient pc -= loadaddress; 388203790Sfabient 389203790Sfabient if ((sym = pmcstat_symbol_search(image, pc)) != NULL) 390203790Sfabient pc = sym->ps_start; 391203790Sfabient 392203790Sfabient child = pmcstat_cgnode_find(parent, image, pc); 393203790Sfabient child->pcg_count++; 394203790Sfabient } 395203790Sfabient} 396203790Sfabient 397203790Sfabient/* 398203790Sfabient * Printing a callgraph for a PMC. 399203790Sfabient */ 400203790Sfabientstatic void 401203790Sfabientpmcstat_callgraph_print_for_pmcid(struct pmcstat_pmcrecord *pmcr) 402203790Sfabient{ 403203790Sfabient int n, nentries; 404203790Sfabient uint32_t nsamples; 405203790Sfabient pmc_id_t pmcid; 406203790Sfabient struct pmcstat_cgnode **sortbuffer, **cgn; 407203790Sfabient struct pmcstat_cgnode_hash *pch; 408203790Sfabient 409203790Sfabient /* 410203790Sfabient * We pull out all callgraph nodes in the top-level hash table 411203790Sfabient * with a matching PMC id. We then sort these based on the 412203790Sfabient * frequency of occurrence. Each callgraph node is then 413203790Sfabient * printed. 414203790Sfabient */ 415203790Sfabient 416203790Sfabient nsamples = 0; 417203790Sfabient pmcid = pmcr->pr_pmcid; 418203790Sfabient if ((sortbuffer = (struct pmcstat_cgnode **) 419203790Sfabient malloc(sizeof(struct pmcstat_cgnode *) * 420203790Sfabient pmcstat_cgnode_hash_count)) == NULL) 421203790Sfabient err(EX_OSERR, "ERROR: Cannot sort callgraph"); 422203790Sfabient cgn = sortbuffer; 423203790Sfabient 424203790Sfabient for (n = 0; n < PMCSTAT_NHASH; n++) 425203790Sfabient LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) 426203790Sfabient if (pch->pch_pmcid == pmcid) { 427203790Sfabient nsamples += pch->pch_cgnode->pcg_count; 428203790Sfabient *cgn++ = pch->pch_cgnode; 429203790Sfabient } 430203790Sfabient 431203790Sfabient nentries = cgn - sortbuffer; 432203790Sfabient assert(nentries <= pmcstat_cgnode_hash_count); 433203790Sfabient 434203790Sfabient if (nentries == 0) { 435203790Sfabient free(sortbuffer); 436203790Sfabient return; 437203790Sfabient } 438203790Sfabient 439203790Sfabient qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), 440203790Sfabient pmcstat_cgnode_compare); 441203790Sfabient 442203790Sfabient (void) fprintf(args.pa_graphfile, 443203790Sfabient "@ %s [%u samples]\n\n", 444203790Sfabient pmcstat_string_unintern(pmcr->pr_pmcname), 445203790Sfabient nsamples); 446203790Sfabient 447203790Sfabient for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) { 448203790Sfabient pmcstat_previous_filename_printed = NULL; 449203790Sfabient pmcstat_cgnode_print(*cgn, 0, nsamples); 450203790Sfabient (void) fprintf(args.pa_graphfile, "\n"); 451203790Sfabient } 452203790Sfabient 453203790Sfabient free(sortbuffer); 454203790Sfabient} 455203790Sfabient 456203790Sfabient/* 457203790Sfabient * Print out callgraphs. 458203790Sfabient */ 459203790Sfabient 460203790Sfabientstatic void 461203790Sfabientpmcstat_callgraph_print(void) 462203790Sfabient{ 463203790Sfabient struct pmcstat_pmcrecord *pmcr; 464203790Sfabient 465203790Sfabient LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) 466203790Sfabient pmcstat_callgraph_print_for_pmcid(pmcr); 467203790Sfabient} 468203790Sfabient 469203790Sfabientstatic void 470203790Sfabientpmcstat_cgnode_topprint(struct pmcstat_cgnode *cg, 471203790Sfabient int depth, uint32_t nsamples) 472203790Sfabient{ 473203790Sfabient int v_attrs, vs_len, ns_len, width, len, n, nchildren; 474203790Sfabient float v; 475203790Sfabient char ns[30], vs[10]; 476203790Sfabient struct pmcstat_symbol *sym; 477203790Sfabient struct pmcstat_cgnode **sortbuffer, **cgn, *pcg; 478203790Sfabient 479203790Sfabient (void) depth; 480203790Sfabient 481203790Sfabient /* Format value. */ 482203790Sfabient v = PMCPL_CG_COUNTP(cg); 483203790Sfabient snprintf(vs, sizeof(vs), "%.1f", v); 484203790Sfabient v_attrs = PMCSTAT_ATTRPERCENT(v); 485203790Sfabient 486203790Sfabient /* Format name. */ 487203790Sfabient sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); 488203790Sfabient if (sym != NULL) { 489203790Sfabient snprintf(ns, sizeof(ns), "%s", 490203790Sfabient pmcstat_string_unintern(sym->ps_name)); 491203790Sfabient } else 492203790Sfabient snprintf(ns, sizeof(ns), "%p", 493203790Sfabient (void *)cg->pcg_func); 494203790Sfabient 495203790Sfabient PMCSTAT_ATTRON(v_attrs); 496203790Sfabient PMCSTAT_PRINTW("%5.5s", vs); 497203790Sfabient PMCSTAT_ATTROFF(v_attrs); 498203790Sfabient PMCSTAT_PRINTW(" %-10.10s %-20.20s", 499203790Sfabient pmcstat_string_unintern(cg->pcg_image->pi_name), 500203790Sfabient ns); 501203790Sfabient 502203790Sfabient nchildren = cg->pcg_nchildren; 503203790Sfabient if (nchildren == 0) { 504203790Sfabient PMCSTAT_PRINTW("\n"); 505203790Sfabient return; 506203790Sfabient } 507203790Sfabient 508203790Sfabient width = pmcstat_displaywidth - 40; 509203790Sfabient 510203790Sfabient if ((sortbuffer = (struct pmcstat_cgnode **) 511203790Sfabient malloc(sizeof(struct pmcstat_cgnode *) * 512203790Sfabient nchildren)) == NULL) 513203790Sfabient err(EX_OSERR, "ERROR: Cannot print callgraph"); 514203790Sfabient cgn = sortbuffer; 515203790Sfabient 516203790Sfabient LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling) 517203790Sfabient *cgn++ = pcg; 518203790Sfabient 519203790Sfabient assert(cgn - sortbuffer == (int)nchildren); 520203790Sfabient 521203790Sfabient qsort(sortbuffer, nchildren, sizeof(struct pmcstat_cgnode *), 522203790Sfabient pmcstat_cgnode_compare); 523203790Sfabient 524203790Sfabient /* Count how many callers. */ 525203790Sfabient for (cgn = sortbuffer, n = 0; n < nchildren; n++, cgn++) { 526203790Sfabient pcg = *cgn; 527203790Sfabient 528203790Sfabient v = PMCPL_CG_COUNTP(pcg); 529203790Sfabient if (v < pmcstat_threshold) 530203790Sfabient break; 531203790Sfabient } 532203790Sfabient nchildren = n; 533203790Sfabient 534203790Sfabient for (cgn = sortbuffer, n = 0; n < nchildren; n++, cgn++) { 535203790Sfabient pcg = *cgn; 536203790Sfabient 537203790Sfabient /* Format value. */ 538203790Sfabient if (nchildren > 1) { 539203790Sfabient v = PMCPL_CG_COUNTP(pcg); 540203790Sfabient vs_len = snprintf(vs, sizeof(vs), ":%.1f", v); 541203790Sfabient v_attrs = PMCSTAT_ATTRPERCENT(v); 542203790Sfabient } else 543203790Sfabient vs_len = 0; 544203790Sfabient 545203790Sfabient /* Format name. */ 546203790Sfabient sym = pmcstat_symbol_search(pcg->pcg_image, pcg->pcg_func); 547203790Sfabient if (sym != NULL) { 548203790Sfabient ns_len = snprintf(ns, sizeof(ns), "%s", 549203790Sfabient pmcstat_string_unintern(sym->ps_name)); 550203790Sfabient } else 551203790Sfabient ns_len = snprintf(ns, sizeof(ns), "%p", 552203790Sfabient (void *)pcg->pcg_func); 553203790Sfabient 554203790Sfabient len = ns_len + vs_len + 1; 555203790Sfabient if (width - len < 0) { 556204783Sfabient PMCSTAT_PRINTW(" ..."); 557203790Sfabient break; 558203790Sfabient } 559203790Sfabient width -= len; 560203790Sfabient 561203790Sfabient PMCSTAT_PRINTW(" %s", ns); 562203790Sfabient if (nchildren > 1) { 563203790Sfabient PMCSTAT_ATTRON(v_attrs); 564203790Sfabient PMCSTAT_PRINTW("%s", vs); 565203790Sfabient PMCSTAT_ATTROFF(v_attrs); 566203790Sfabient } 567203790Sfabient } 568203790Sfabient PMCSTAT_PRINTW("\n"); 569203790Sfabient free(sortbuffer); 570203790Sfabient} 571203790Sfabient 572203790Sfabient/* 573203790Sfabient * Top mode display. 574203790Sfabient */ 575203790Sfabient 576203790Sfabientvoid 577203790Sfabientpmcpl_cg_topdisplay(void) 578203790Sfabient{ 579203790Sfabient int n, nentries; 580203790Sfabient uint32_t nsamples; 581203790Sfabient struct pmcstat_cgnode **sortbuffer, **cgn; 582203790Sfabient struct pmcstat_cgnode_hash *pch; 583203790Sfabient struct pmcstat_pmcrecord *pmcr; 584203790Sfabient 585203790Sfabient pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter); 586206635Sfabient if (!pmcr) 587206635Sfabient err(EX_SOFTWARE, "ERROR: invalid pmcindex"); 588203790Sfabient 589203790Sfabient /* 590203790Sfabient * We pull out all callgraph nodes in the top-level hash table 591203790Sfabient * with a matching PMC index. We then sort these based on the 592203790Sfabient * frequency of occurrence. Each callgraph node is then 593203790Sfabient * printed. 594203790Sfabient */ 595203790Sfabient 596203790Sfabient nsamples = 0; 597203790Sfabient 598203790Sfabient if ((sortbuffer = (struct pmcstat_cgnode **) 599203790Sfabient malloc(sizeof(struct pmcstat_cgnode *) * 600203790Sfabient pmcstat_cgnode_hash_count)) == NULL) 601203790Sfabient err(EX_OSERR, "ERROR: Cannot sort callgraph"); 602203790Sfabient cgn = sortbuffer; 603203790Sfabient 604203790Sfabient for (n = 0; n < PMCSTAT_NHASH; n++) 605203790Sfabient LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) 606203790Sfabient if (pmcr == NULL || pch->pch_pmcid == pmcr->pr_pmcid) { 607203790Sfabient nsamples += pch->pch_cgnode->pcg_count; 608203790Sfabient *cgn++ = pch->pch_cgnode; 609203790Sfabient } 610203790Sfabient 611203790Sfabient nentries = cgn - sortbuffer; 612203790Sfabient assert(nentries <= pmcstat_cgnode_hash_count); 613203790Sfabient 614203790Sfabient if (nentries == 0) { 615203790Sfabient free(sortbuffer); 616203790Sfabient return; 617203790Sfabient } 618203790Sfabient 619203790Sfabient qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), 620203790Sfabient pmcstat_cgnode_compare); 621203790Sfabient 622203790Sfabient PMCSTAT_PRINTW("%5.5s %-10.10s %-20.20s %s\n", 623203790Sfabient "%SAMP", "IMAGE", "FUNCTION", "CALLERS"); 624203790Sfabient 625203790Sfabient nentries = min(pmcstat_displayheight - 2, nentries); 626203790Sfabient 627203790Sfabient for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) { 628203790Sfabient if (PMCPL_CG_COUNTP(*cgn) < pmcstat_threshold) 629203790Sfabient break; 630203790Sfabient pmcstat_cgnode_topprint(*cgn, 0, nsamples); 631203790Sfabient } 632203790Sfabient 633203790Sfabient free(sortbuffer); 634203790Sfabient} 635203790Sfabient 636203790Sfabient/* 637203790Sfabient * Handle top mode keypress. 638203790Sfabient */ 639203790Sfabient 640203790Sfabientint 641203790Sfabientpmcpl_cg_topkeypress(int c, WINDOW *w) 642203790Sfabient{ 643203790Sfabient 644203790Sfabient (void) c; (void) w; 645203790Sfabient 646203790Sfabient return 0; 647203790Sfabient} 648203790Sfabient 649203790Sfabientint 650203790Sfabientpmcpl_cg_init(void) 651203790Sfabient{ 652203790Sfabient int i; 653203790Sfabient 654203790Sfabient pmcstat_cgnode_hash_count = 0; 655203790Sfabient pmcstat_previous_filename_printed = NULL; 656203790Sfabient 657203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) { 658203790Sfabient LIST_INIT(&pmcstat_cgnode_hash[i]); 659203790Sfabient } 660203790Sfabient 661203790Sfabient return (0); 662203790Sfabient} 663203790Sfabient 664203790Sfabientvoid 665203790Sfabientpmcpl_cg_shutdown(FILE *mf) 666203790Sfabient{ 667203790Sfabient int i; 668203790Sfabient struct pmcstat_cgnode_hash *pch, *pchtmp; 669203790Sfabient 670203790Sfabient (void) mf; 671203790Sfabient 672203790Sfabient if (args.pa_flags & FLAG_DO_CALLGRAPHS) 673203790Sfabient pmcstat_callgraph_print(); 674203790Sfabient 675203790Sfabient /* 676203790Sfabient * Free memory. 677203790Sfabient */ 678203790Sfabient for (i = 0; i < PMCSTAT_NHASH; i++) { 679203790Sfabient LIST_FOREACH_SAFE(pch, &pmcstat_cgnode_hash[i], pch_next, 680203790Sfabient pchtmp) { 681203790Sfabient pmcstat_cgnode_free(pch->pch_cgnode); 682203790Sfabient LIST_REMOVE(pch, pch_next); 683203790Sfabient free(pch); 684203790Sfabient } 685203790Sfabient } 686203790Sfabient} 687203790Sfabient 688