pmcpl_callgraph.c revision 212176
1138627Stakawata/*- 2138627Stakawata * Copyright (c) 2005-2007, Joseph Koshy 3147196Smarkus * Copyright (c) 2007 The FreeBSD Foundation 4138627Stakawata * All rights reserved. 5138627Stakawata * 6138627Stakawata * Portions of this software were developed by A. Joseph Koshy under 7138627Stakawata * sponsorship from the FreeBSD Foundation and Google, Inc. 8138627Stakawata * 9138627Stakawata * Redistribution and use in source and binary forms, with or without 10138627Stakawata * modification, are permitted provided that the following conditions 11138627Stakawata * are met: 12138627Stakawata * 1. Redistributions of source code must retain the above copyright 13138627Stakawata * notice, this list of conditions and the following disclaimer. 14138627Stakawata * 2. Redistributions in binary form must reproduce the above copyright 15138627Stakawata * notice, this list of conditions and the following disclaimer in the 16138627Stakawata * documentation and/or other materials provided with the distribution. 17138627Stakawata * 18138627Stakawata * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19138627Stakawata * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20138627Stakawata * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21138627Stakawata * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22138627Stakawata * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23138627Stakawata * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24138627Stakawata * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25138627Stakawata * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26138627Stakawata * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27138627Stakawata * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28143002Sobrien * SUCH DAMAGE. 29143002Sobrien */ 30143002Sobrien 31147196Smarkus/* 32147196Smarkus * Transform a hwpmc(4) log into human readable form, and into 33147196Smarkus * gprof(1) compatible profiles. 34147196Smarkus */ 35147196Smarkus 36147196Smarkus#include <sys/cdefs.h> 37147196Smarkus__FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcpl_callgraph.c 212176 2010-09-03 13:54:02Z fabient $"); 38147196Smarkus 39147196Smarkus#include <sys/param.h> 40138627Stakawata#include <sys/endian.h> 41138627Stakawata#include <sys/gmon.h> 42138627Stakawata#include <sys/imgact_aout.h> 43138627Stakawata#include <sys/imgact_elf.h> 44138627Stakawata#include <sys/mman.h> 45138627Stakawata#include <sys/pmc.h> 46138627Stakawata#include <sys/queue.h> 47138627Stakawata#include <sys/socket.h> 48138627Stakawata#include <sys/stat.h> 49147196Smarkus#include <sys/wait.h> 50138627Stakawata 51138627Stakawata#include <netinet/in.h> 52138627Stakawata 53138825Snjl#include <assert.h> 54138774Sscottl#include <curses.h> 55138774Sscottl#include <err.h> 56147196Smarkus#include <errno.h> 57147196Smarkus#include <fcntl.h> 58147196Smarkus#include <gelf.h> 59147196Smarkus#include <libgen.h> 60147196Smarkus#include <limits.h> 61147196Smarkus#include <netdb.h> 62147196Smarkus#include <pmc.h> 63147196Smarkus#include <pmclog.h> 64147196Smarkus#include <sysexits.h> 65147196Smarkus#include <stdint.h> 66147196Smarkus#include <stdio.h> 67147196Smarkus#include <stdlib.h> 68147196Smarkus#include <string.h> 69138627Stakawata#include <unistd.h> 70147196Smarkus 71147196Smarkus#include "pmcstat.h" 72147196Smarkus#include "pmcstat_log.h" 73147196Smarkus#include "pmcstat_top.h" 74147196Smarkus#include "pmcpl_callgraph.h" 75147196Smarkus 76147196Smarkus/* Get the sample value in percent related to nsamples. */ 77147196Smarkus#define PMCPL_CG_COUNTP(a) \ 78147196Smarkus ((a)->pcg_count * 100.0 / nsamples) 79147196Smarkus 80147196Smarkus/* 81147196Smarkus * The toplevel CG nodes (i.e., with rank == 0) are placed in a hash table. 82147196Smarkus */ 83147196Smarkus 84147196Smarkusstruct pmcstat_cgnode_hash_list pmcstat_cgnode_hash[PMCSTAT_NHASH]; 85147196Smarkusint pmcstat_cgnode_hash_count; 86147196Smarkus 87147196Smarkusstatic pmcstat_interned_string pmcstat_previous_filename_printed; 88147196Smarkus 89138627Stakawatastatic struct pmcstat_cgnode * 90147196Smarkuspmcstat_cgnode_allocate(struct pmcstat_image *image, uintfptr_t pc) 91147196Smarkus{ 92147196Smarkus struct pmcstat_cgnode *cg; 93147196Smarkus 94147196Smarkus if ((cg = malloc(sizeof(*cg))) == NULL) 95147196Smarkus err(EX_OSERR, "ERROR: Cannot allocate callgraph node"); 96147196Smarkus 97147196Smarkus cg->pcg_image = image; 98147196Smarkus cg->pcg_func = pc; 99147196Smarkus 100147196Smarkus cg->pcg_count = 0; 101147196Smarkus cg->pcg_nchildren = 0; 102147196Smarkus LIST_INIT(&cg->pcg_children); 103147196Smarkus 104147196Smarkus return (cg); 105147196Smarkus} 106147196Smarkus 107147196Smarkus/* 108147196Smarkus * Free a node and its children. 109147196Smarkus */ 110147196Smarkusstatic void 111147196Smarkuspmcstat_cgnode_free(struct pmcstat_cgnode *cg) 112147196Smarkus{ 113147196Smarkus struct pmcstat_cgnode *cgc, *cgtmp; 114147196Smarkus 115147196Smarkus LIST_FOREACH_SAFE(cgc, &cg->pcg_children, pcg_sibling, cgtmp) 116147196Smarkus pmcstat_cgnode_free(cgc); 117147196Smarkus free(cg); 118147196Smarkus} 119147196Smarkus 120147196Smarkus/* 121147196Smarkus * Look for a callgraph node associated with pmc `pmcid' in the global 122147196Smarkus * hash table that corresponds to the given `pc' value in the process 123147196Smarkus * `pp'. 124147196Smarkus */ 125138627Stakawatastatic struct pmcstat_cgnode * 126147196Smarkuspmcstat_cgnode_hash_lookup_pc(struct pmcstat_process *pp, pmc_id_t pmcid, 127147196Smarkus uintfptr_t pc, int usermode) 128147196Smarkus{ 129147196Smarkus struct pmcstat_pcmap *ppm; 130147196Smarkus struct pmcstat_symbol *sym; 131147196Smarkus struct pmcstat_image *image; 132147196Smarkus struct pmcstat_cgnode *cg; 133147196Smarkus struct pmcstat_cgnode_hash *h; 134147196Smarkus uintfptr_t loadaddress; 135147196Smarkus unsigned int i, hash; 136147196Smarkus 137147196Smarkus ppm = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, pc); 138147196Smarkus if (ppm == NULL) 139147196Smarkus return (NULL); 140147196Smarkus 141147196Smarkus image = ppm->ppm_image; 142147196Smarkus 143147196Smarkus loadaddress = ppm->ppm_lowpc + image->pi_vaddr - image->pi_start; 144147196Smarkus pc -= loadaddress; /* Convert to an offset in the image. */ 145147196Smarkus 146147196Smarkus /* 147148710Smarkus * Try determine the function at this offset. If we can't 148148710Smarkus * find a function round leave the `pc' value alone. 149147196Smarkus */ 150148710Smarkus if ((sym = pmcstat_symbol_search(image, pc)) != NULL) 151148710Smarkus pc = sym->ps_start; 152147196Smarkus else 153147196Smarkus pmcstat_stats.ps_samples_unknown_function++; 154147196Smarkus 155147196Smarkus for (hash = i = 0; i < sizeof(uintfptr_t); i++) 156147196Smarkus hash += (pc >> i) & 0xFF; 157147196Smarkus 158147196Smarkus hash &= PMCSTAT_HASH_MASK; 159147196Smarkus 160147196Smarkus cg = NULL; 161147196Smarkus LIST_FOREACH(h, &pmcstat_cgnode_hash[hash], pch_next) 162147196Smarkus { 163138627Stakawata if (h->pch_pmcid != pmcid) 164138627Stakawata continue; 165147196Smarkus 166147196Smarkus cg = h->pch_cgnode; 167147196Smarkus 168147196Smarkus assert(cg != NULL); 169147196Smarkus 170147196Smarkus if (cg->pcg_image == image && cg->pcg_func == pc) 171147196Smarkus return (cg); 172147196Smarkus } 173147196Smarkus 174147196Smarkus /* 175147196Smarkus * We haven't seen this (pmcid, pc) tuple yet, so allocate a 176147196Smarkus * new callgraph node and a new hash table entry for it. 177147196Smarkus */ 178147196Smarkus cg = pmcstat_cgnode_allocate(image, pc); 179147196Smarkus if ((h = malloc(sizeof(*h))) == NULL) 180147196Smarkus err(EX_OSERR, "ERROR: Could not allocate callgraph node"); 181147196Smarkus 182147196Smarkus h->pch_pmcid = pmcid; 183147196Smarkus h->pch_cgnode = cg; 184147196Smarkus LIST_INSERT_HEAD(&pmcstat_cgnode_hash[hash], h, pch_next); 185147196Smarkus 186147196Smarkus pmcstat_cgnode_hash_count++; 187147196Smarkus 188147196Smarkus return (cg); 189147196Smarkus} 190147196Smarkus 191147196Smarkus/* 192147196Smarkus * Compare two callgraph nodes for sorting. 193147196Smarkus */ 194147196Smarkusstatic int 195147196Smarkuspmcstat_cgnode_compare(const void *a, const void *b) 196147196Smarkus{ 197147196Smarkus const struct pmcstat_cgnode *const *pcg1, *const *pcg2, *cg1, *cg2; 198147196Smarkus 199147196Smarkus pcg1 = (const struct pmcstat_cgnode *const *) a; 200147196Smarkus cg1 = *pcg1; 201147196Smarkus pcg2 = (const struct pmcstat_cgnode *const *) b; 202147196Smarkus cg2 = *pcg2; 203147196Smarkus 204147196Smarkus /* Sort in reverse order */ 205147196Smarkus if (cg1->pcg_count < cg2->pcg_count) 206147196Smarkus return (1); 207147196Smarkus if (cg1->pcg_count > cg2->pcg_count) 208147196Smarkus return (-1); 209147196Smarkus return (0); 210147196Smarkus} 211147196Smarkus 212147196Smarkus/* 213147196Smarkus * Find (allocating if a needed) a callgraph node in the given 214147196Smarkus * parent with the same (image, pcoffset) pair. 215147196Smarkus */ 216147196Smarkus 217147196Smarkusstatic struct pmcstat_cgnode * 218147196Smarkuspmcstat_cgnode_find(struct pmcstat_cgnode *parent, struct pmcstat_image *image, 219147196Smarkus uintfptr_t pcoffset) 220147196Smarkus{ 221147196Smarkus struct pmcstat_cgnode *child; 222147196Smarkus 223147196Smarkus LIST_FOREACH(child, &parent->pcg_children, pcg_sibling) { 224147196Smarkus if (child->pcg_image == image && 225147196Smarkus child->pcg_func == pcoffset) 226147196Smarkus return (child); 227147196Smarkus } 228147196Smarkus 229147196Smarkus /* 230147196Smarkus * Allocate a new structure. 231147196Smarkus */ 232147196Smarkus 233147196Smarkus child = pmcstat_cgnode_allocate(image, pcoffset); 234147196Smarkus 235147196Smarkus /* 236147196Smarkus * Link it into the parent. 237147196Smarkus */ 238147196Smarkus LIST_INSERT_HEAD(&parent->pcg_children, child, pcg_sibling); 239147196Smarkus parent->pcg_nchildren++; 240147196Smarkus 241147196Smarkus return (child); 242147196Smarkus} 243138627Stakawata 244138627Stakawata/* 245138627Stakawata * Print one callgraph node. The output format is: 246138627Stakawata * 247148710Smarkus * indentation %(parent's samples) #nsamples function@object 248148710Smarkus */ 249148710Smarkusstatic void 250147196Smarkuspmcstat_cgnode_print(struct pmcstat_cgnode *cg, int depth, uint32_t total) 251147196Smarkus{ 252147196Smarkus uint32_t n; 253147196Smarkus const char *space; 254147196Smarkus struct pmcstat_symbol *sym; 255147196Smarkus struct pmcstat_cgnode **sortbuffer, **cgn, *pcg; 256147196Smarkus 257147196Smarkus space = " "; 258147196Smarkus 259138627Stakawata if (depth > 0) 260138627Stakawata (void) fprintf(args.pa_graphfile, "%*s", depth, space); 261138627Stakawata 262138627Stakawata if (cg->pcg_count == total) 263138627Stakawata (void) fprintf(args.pa_graphfile, "100.0%% "); 264138627Stakawata else 265138627Stakawata (void) fprintf(args.pa_graphfile, "%05.2f%% ", 266138627Stakawata 100.0 * cg->pcg_count / total); 267138627Stakawata 268138627Stakawata n = fprintf(args.pa_graphfile, " [%u] ", cg->pcg_count); 269138627Stakawata 270138627Stakawata /* #samples is a 12 character wide field. */ 271138627Stakawata if (n < 12) 272138627Stakawata (void) fprintf(args.pa_graphfile, "%*s", 12 - n, space); 273138627Stakawata 274138627Stakawata if (depth > 0) 275138627Stakawata (void) fprintf(args.pa_graphfile, "%*s", depth, space); 276138627Stakawata 277138627Stakawata sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); 278138627Stakawata if (sym) 279147468Scracauer (void) fprintf(args.pa_graphfile, "%s", 280138627Stakawata pmcstat_string_unintern(sym->ps_name)); 281147196Smarkus else 282147196Smarkus (void) fprintf(args.pa_graphfile, "%p", 283147196Smarkus (void *) (cg->pcg_image->pi_vaddr + cg->pcg_func)); 284148710Smarkus 285148710Smarkus if (pmcstat_previous_filename_printed != 286148710Smarkus cg->pcg_image->pi_fullpath) { 287148710Smarkus pmcstat_previous_filename_printed = cg->pcg_image->pi_fullpath; 288148710Smarkus (void) fprintf(args.pa_graphfile, " @ %s\n", 289148710Smarkus pmcstat_string_unintern( 290148710Smarkus pmcstat_previous_filename_printed)); 291148710Smarkus } else 292148710Smarkus (void) fprintf(args.pa_graphfile, "\n"); 293148710Smarkus 294148710Smarkus if (cg->pcg_nchildren == 0) 295148710Smarkus return; 296148710Smarkus 297148710Smarkus if ((sortbuffer = (struct pmcstat_cgnode **) 298148710Smarkus malloc(sizeof(struct pmcstat_cgnode *) * 299148710Smarkus cg->pcg_nchildren)) == NULL) 300148710Smarkus err(EX_OSERR, "ERROR: Cannot print callgraph"); 301148710Smarkus cgn = sortbuffer; 302148710Smarkus 303147196Smarkus LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling) 304148710Smarkus *cgn++ = pcg; 305147196Smarkus 306148710Smarkus assert(cgn - sortbuffer == (int) cg->pcg_nchildren); 307148710Smarkus 308147196Smarkus qsort(sortbuffer, cg->pcg_nchildren, sizeof(struct pmcstat_cgnode *), 309147196Smarkus pmcstat_cgnode_compare); 310138627Stakawata 311138627Stakawata for (cgn = sortbuffer, n = 0; n < cg->pcg_nchildren; n++, cgn++) 312138627Stakawata pmcstat_cgnode_print(*cgn, depth+1, cg->pcg_count); 313147196Smarkus 314147196Smarkus free(sortbuffer); 315147196Smarkus} 316147196Smarkus 317138627Stakawata/* 318147196Smarkus * Record a callchain. 319138627Stakawata */ 320147196Smarkus 321138627Stakawatavoid 322138627Stakawatapmcpl_cg_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 323138627Stakawata uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 324147196Smarkus{ 325138627Stakawata uintfptr_t pc, loadaddress; 326147196Smarkus uint32_t n; 327147196Smarkus struct pmcstat_image *image; 328147196Smarkus struct pmcstat_pcmap *ppm; 329138627Stakawata struct pmcstat_symbol *sym; 330138627Stakawata struct pmcstat_cgnode *parent, *child; 331138627Stakawata struct pmcstat_process *km; 332138627Stakawata pmc_id_t pmcid; 333147196Smarkus 334147196Smarkus (void) cpu; 335147196Smarkus 336147196Smarkus /* 337147196Smarkus * Find the callgraph node recorded in the global hash table 338147196Smarkus * for this (pmcid, pc). 339147196Smarkus */ 340147196Smarkus 341147196Smarkus pc = cc[0]; 342147196Smarkus pmcid = pmcr->pr_pmcid; 343138627Stakawata parent = pmcstat_cgnode_hash_lookup_pc(pp, pmcid, pc, usermode); 344147196Smarkus if (parent == NULL) { 345147196Smarkus pmcstat_stats.ps_callchain_dubious_frames++; 346147196Smarkus pmcr->pr_dubious_frames++; 347147196Smarkus return; 348147196Smarkus } 349147196Smarkus 350138627Stakawata parent->pcg_count++; 351147196Smarkus 352138627Stakawata /* 353147196Smarkus * For each return address in the call chain record, subject 354147196Smarkus * to the maximum depth desired. 355147196Smarkus * - Find the image associated with the sample. Stop if there 356138627Stakawata * there is no valid image at that address. 357147196Smarkus * - Find the function that overlaps the return address. 358147196Smarkus * - If found: use the start address of the function. 359147196Smarkus * If not found (say an object's symbol table is not present or 360138627Stakawata * is incomplete), round down to th gprof bucket granularity. 361147196Smarkus * - Convert return virtual address to an offset in the image. 362147196Smarkus * - Look for a child with the same {offset,image} tuple, 363147196Smarkus * inserting one if needed. 364147196Smarkus * - Increment the count of occurrences of the child. 365147196Smarkus */ 366138627Stakawata km = pmcstat_kernproc; 367147196Smarkus 368147196Smarkus for (n = 1; n < (uint32_t) args.pa_graphdepth && n < nsamples; n++, 369147196Smarkus parent = child) { 370147196Smarkus pc = cc[n]; 371138627Stakawata 372147196Smarkus ppm = pmcstat_process_find_map(usermode ? pp : km, pc); 373147196Smarkus if (ppm == NULL) { 374147196Smarkus /* Detect full frame capture (kernel + user). */ 375147196Smarkus if (!usermode) { 376147196Smarkus ppm = pmcstat_process_find_map(pp, pc); 377138627Stakawata if (ppm != NULL) 378147196Smarkus km = pp; 379147196Smarkus } 380147196Smarkus } 381147196Smarkus if (ppm == NULL) 382147196Smarkus return; 383147196Smarkus 384147196Smarkus image = ppm->ppm_image; 385147196Smarkus loadaddress = ppm->ppm_lowpc + image->pi_vaddr - 386147196Smarkus image->pi_start; 387147196Smarkus pc -= loadaddress; 388138627Stakawata 389147196Smarkus if ((sym = pmcstat_symbol_search(image, pc)) != NULL) 390147196Smarkus pc = sym->ps_start; 391147196Smarkus 392147196Smarkus child = pmcstat_cgnode_find(parent, image, pc); 393147196Smarkus child->pcg_count++; 394147196Smarkus } 395147196Smarkus} 396147196Smarkus 397147196Smarkus/* 398147196Smarkus * Printing a callgraph for a PMC. 399147196Smarkus */ 400147196Smarkusstatic void 401147196Smarkuspmcstat_callgraph_print_for_pmcid(struct pmcstat_pmcrecord *pmcr) 402147196Smarkus{ 403147196Smarkus int n, nentries; 404147196Smarkus uint32_t nsamples; 405147196Smarkus pmc_id_t pmcid; 406147246Smarkus struct pmcstat_cgnode **sortbuffer, **cgn; 407147246Smarkus struct pmcstat_cgnode_hash *pch; 408147196Smarkus 409147196Smarkus /* 410138627Stakawata * We pull out all callgraph nodes in the top-level hash table 411138627Stakawata * with a matching PMC id. We then sort these based on the 412138627Stakawata * frequency of occurrence. Each callgraph node is then 413138627Stakawata * printed. 414138627Stakawata */ 415138774Sscottl 416138774Sscottl nsamples = 0; 417138627Stakawata pmcid = pmcr->pr_pmcid; 418138627Stakawata if ((sortbuffer = (struct pmcstat_cgnode **) 419147196Smarkus malloc(sizeof(struct pmcstat_cgnode *) * 420147196Smarkus pmcstat_cgnode_hash_count)) == NULL) 421147196Smarkus err(EX_OSERR, "ERROR: Cannot sort callgraph"); 422147196Smarkus cgn = sortbuffer; 423147196Smarkus 424147196Smarkus for (n = 0; n < PMCSTAT_NHASH; n++) 425147196Smarkus LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) 426147196Smarkus if (pch->pch_pmcid == pmcid) { 427147246Smarkus nsamples += pch->pch_cgnode->pcg_count; 428147246Smarkus *cgn++ = pch->pch_cgnode; 429147246Smarkus } 430147196Smarkus 431138627Stakawata nentries = cgn - sortbuffer; 432147196Smarkus assert(nentries <= pmcstat_cgnode_hash_count); 433138627Stakawata 434147196Smarkus if (nentries == 0) { 435138627Stakawata free(sortbuffer); 436147196Smarkus return; 437147196Smarkus } 438147196Smarkus 439147196Smarkus qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), 440147196Smarkus pmcstat_cgnode_compare); 441147196Smarkus 442147196Smarkus (void) fprintf(args.pa_graphfile, 443147196Smarkus "@ %s [%u samples]\n\n", 444147196Smarkus pmcstat_string_unintern(pmcr->pr_pmcname), 445147196Smarkus nsamples); 446147196Smarkus 447147196Smarkus for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) { 448147196Smarkus pmcstat_previous_filename_printed = NULL; 449147196Smarkus pmcstat_cgnode_print(*cgn, 0, nsamples); 450147196Smarkus (void) fprintf(args.pa_graphfile, "\n"); 451147196Smarkus } 452147196Smarkus 453147196Smarkus free(sortbuffer); 454147196Smarkus} 455147196Smarkus 456147196Smarkus/* 457147196Smarkus * Print out callgraphs. 458147196Smarkus */ 459138627Stakawata 460138627Stakawatastatic void 461138627Stakawatapmcstat_callgraph_print(void) 462147196Smarkus{ 463138627Stakawata struct pmcstat_pmcrecord *pmcr; 464147196Smarkus 465147196Smarkus LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) 466147196Smarkus pmcstat_callgraph_print_for_pmcid(pmcr); 467147196Smarkus} 468147196Smarkus 469147196Smarkusstatic void 470147196Smarkuspmcstat_cgnode_topprint(struct pmcstat_cgnode *cg, 471147196Smarkus int depth, uint32_t nsamples) 472147196Smarkus{ 473147196Smarkus int v_attrs, vs_len, ns_len, width, len, n, nchildren; 474147196Smarkus float v; 475147196Smarkus char ns[30], vs[10]; 476147196Smarkus struct pmcstat_symbol *sym; 477147196Smarkus struct pmcstat_cgnode **sortbuffer, **cgn, *pcg; 478147196Smarkus 479147196Smarkus (void) depth; 480147196Smarkus 481147196Smarkus /* Format value. */ 482147196Smarkus v = PMCPL_CG_COUNTP(cg); 483147196Smarkus snprintf(vs, sizeof(vs), "%.1f", v); 484147196Smarkus v_attrs = PMCSTAT_ATTRPERCENT(v); 485147196Smarkus 486147196Smarkus /* Format name. */ 487147196Smarkus sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); 488147196Smarkus if (sym != NULL) { 489147196Smarkus snprintf(ns, sizeof(ns), "%s", 490138627Stakawata pmcstat_string_unintern(sym->ps_name)); 491147196Smarkus } else 492147196Smarkus snprintf(ns, sizeof(ns), "%p", 493147196Smarkus (void *)cg->pcg_func); 494138627Stakawata 495147196Smarkus PMCSTAT_ATTRON(v_attrs); 496147196Smarkus PMCSTAT_PRINTW("%5.5s", vs); 497138627Stakawata PMCSTAT_ATTROFF(v_attrs); 498147196Smarkus PMCSTAT_PRINTW(" %-10.10s %-20.20s", 499147196Smarkus pmcstat_string_unintern(cg->pcg_image->pi_name), 500138627Stakawata ns); 501147196Smarkus 502147196Smarkus nchildren = cg->pcg_nchildren; 503147196Smarkus if (nchildren == 0) { 504147196Smarkus PMCSTAT_PRINTW("\n"); 505138627Stakawata return; 506147196Smarkus } 507147196Smarkus 508147196Smarkus width = pmcstat_displaywidth - 40; 509147196Smarkus 510147196Smarkus if ((sortbuffer = (struct pmcstat_cgnode **) 511147196Smarkus malloc(sizeof(struct pmcstat_cgnode *) * 512147196Smarkus nchildren)) == NULL) 513147196Smarkus err(EX_OSERR, "ERROR: Cannot print callgraph"); 514147196Smarkus cgn = sortbuffer; 515147196Smarkus 516147196Smarkus LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling) 517147196Smarkus *cgn++ = pcg; 518147196Smarkus 519147196Smarkus assert(cgn - sortbuffer == (int)nchildren); 520147196Smarkus 521147196Smarkus qsort(sortbuffer, nchildren, sizeof(struct pmcstat_cgnode *), 522147196Smarkus pmcstat_cgnode_compare); 523147196Smarkus 524147196Smarkus /* Count how many callers. */ 525147196Smarkus for (cgn = sortbuffer, n = 0; n < nchildren; n++, cgn++) { 526147196Smarkus pcg = *cgn; 527147196Smarkus 528147196Smarkus v = PMCPL_CG_COUNTP(pcg); 529147196Smarkus if (v < pmcstat_threshold) 530147196Smarkus break; 531147196Smarkus } 532147196Smarkus nchildren = n; 533147196Smarkus 534147196Smarkus for (cgn = sortbuffer, n = 0; n < nchildren; n++, cgn++) { 535147196Smarkus pcg = *cgn; 536147196Smarkus 537147196Smarkus /* Format value. */ 538147196Smarkus if (nchildren > 1) { 539147196Smarkus v = PMCPL_CG_COUNTP(pcg); 540147196Smarkus vs_len = snprintf(vs, sizeof(vs), ":%.1f", v); 541147196Smarkus v_attrs = PMCSTAT_ATTRPERCENT(v); 542147196Smarkus } else 543147196Smarkus vs_len = 0; 544147196Smarkus 545147196Smarkus /* Format name. */ 546147196Smarkus sym = pmcstat_symbol_search(pcg->pcg_image, pcg->pcg_func); 547147196Smarkus if (sym != NULL) { 548147196Smarkus ns_len = snprintf(ns, sizeof(ns), "%s", 549147196Smarkus pmcstat_string_unintern(sym->ps_name)); 550147196Smarkus } else 551147196Smarkus ns_len = snprintf(ns, sizeof(ns), "%p", 552147196Smarkus (void *)pcg->pcg_func); 553147196Smarkus 554147196Smarkus len = ns_len + vs_len + 1; 555147246Smarkus if (width - len < 0) { 556147196Smarkus PMCSTAT_PRINTW(" ..."); 557147196Smarkus break; 558147196Smarkus } 559147196Smarkus width -= len; 560147196Smarkus 561147196Smarkus PMCSTAT_PRINTW(" %s", ns); 562147196Smarkus if (nchildren > 1) { 563147196Smarkus PMCSTAT_ATTRON(v_attrs); 564147196Smarkus PMCSTAT_PRINTW("%s", vs); 565147196Smarkus PMCSTAT_ATTROFF(v_attrs); 566147196Smarkus } 567147196Smarkus } 568147196Smarkus PMCSTAT_PRINTW("\n"); 569147196Smarkus free(sortbuffer); 570147196Smarkus} 571147196Smarkus 572147196Smarkus/* 573147196Smarkus * Top mode display. 574147196Smarkus */ 575147196Smarkus 576147196Smarkusvoid 577147196Smarkuspmcpl_cg_topdisplay(void) 578147196Smarkus{ 579147196Smarkus int n, nentries; 580147196Smarkus uint32_t nsamples; 581147196Smarkus struct pmcstat_cgnode **sortbuffer, **cgn; 582147196Smarkus struct pmcstat_cgnode_hash *pch; 583147196Smarkus struct pmcstat_pmcrecord *pmcr; 584147196Smarkus 585147196Smarkus pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter); 586138627Stakawata if (!pmcr) 587147196Smarkus err(EX_SOFTWARE, "ERROR: invalid pmcindex"); 588147196Smarkus 589147196Smarkus /* 590147196Smarkus * We pull out all callgraph nodes in the top-level hash table 591147196Smarkus * with a matching PMC index. We then sort these based on the 592138627Stakawata * frequency of occurrence. Each callgraph node is then 593147196Smarkus * printed. 594147196Smarkus */ 595147196Smarkus 596147196Smarkus nsamples = 0; 597138627Stakawata 598147196Smarkus if ((sortbuffer = (struct pmcstat_cgnode **) 599147196Smarkus malloc(sizeof(struct pmcstat_cgnode *) * 600147196Smarkus pmcstat_cgnode_hash_count)) == NULL) 601138627Stakawata err(EX_OSERR, "ERROR: Cannot sort callgraph"); 602147196Smarkus cgn = sortbuffer; 603147196Smarkus 604138627Stakawata for (n = 0; n < PMCSTAT_NHASH; n++) 605138627Stakawata LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) 606138627Stakawata if (pmcr == NULL || pch->pch_pmcid == pmcr->pr_pmcid) { 607147196Smarkus nsamples += pch->pch_cgnode->pcg_count; 608138627Stakawata *cgn++ = pch->pch_cgnode; 609147196Smarkus } 610147196Smarkus 611147196Smarkus nentries = cgn - sortbuffer; 612147196Smarkus assert(nentries <= pmcstat_cgnode_hash_count); 613147196Smarkus 614138627Stakawata if (nentries == 0) { 615147196Smarkus free(sortbuffer); 616147196Smarkus return; 617138627Stakawata } 618147196Smarkus 619147196Smarkus qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), 620147196Smarkus pmcstat_cgnode_compare); 621147196Smarkus 622138627Stakawata PMCSTAT_PRINTW("%5.5s %-10.10s %-20.20s %s\n", 623147196Smarkus "%SAMP", "IMAGE", "FUNCTION", "CALLERS"); 624147196Smarkus 625147196Smarkus nentries = min(pmcstat_displayheight - 2, nentries); 626147196Smarkus 627147196Smarkus for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) { 628147196Smarkus if (PMCPL_CG_COUNTP(*cgn) < pmcstat_threshold) 629138627Stakawata break; 630147196Smarkus pmcstat_cgnode_topprint(*cgn, 0, nsamples); 631147196Smarkus } 632147196Smarkus 633147196Smarkus free(sortbuffer); 634138627Stakawata} 635147196Smarkus 636147196Smarkus/* 637147196Smarkus * Handle top mode keypress. 638138627Stakawata */ 639147196Smarkus 640147196Smarkusint 641147196Smarkuspmcpl_cg_topkeypress(int c, WINDOW *w) 642147196Smarkus{ 643147196Smarkus 644147196Smarkus (void) c; (void) w; 645138627Stakawata 646147196Smarkus return 0; 647147196Smarkus} 648147196Smarkus 649147196Smarkusint 650138627Stakawatapmcpl_cg_init(void) 651147196Smarkus{ 652147196Smarkus int i; 653147196Smarkus 654147196Smarkus pmcstat_cgnode_hash_count = 0; 655147196Smarkus pmcstat_previous_filename_printed = NULL; 656147196Smarkus 657147196Smarkus for (i = 0; i < PMCSTAT_NHASH; i++) { 658147196Smarkus LIST_INIT(&pmcstat_cgnode_hash[i]); 659147196Smarkus } 660138627Stakawata 661147196Smarkus return (0); 662147196Smarkus} 663147196Smarkus 664138627Stakawatavoid 665147246Smarkuspmcpl_cg_shutdown(FILE *mf) 666147246Smarkus{ 667147246Smarkus int i; 668147246Smarkus struct pmcstat_cgnode_hash *pch, *pchtmp; 669147196Smarkus 670147196Smarkus (void) mf; 671138627Stakawata 672147196Smarkus if (args.pa_flags & FLAG_DO_CALLGRAPHS) 673147196Smarkus pmcstat_callgraph_print(); 674147196Smarkus 675147196Smarkus /* 676138627Stakawata * Free memory. 677147196Smarkus */ 678147196Smarkus for (i = 0; i < PMCSTAT_NHASH; i++) { 679147196Smarkus LIST_FOREACH_SAFE(pch, &pmcstat_cgnode_hash[i], pch_next, 680147196Smarkus pchtmp) { 681147196Smarkus pmcstat_cgnode_free(pch->pch_cgnode); 682147196Smarkus LIST_REMOVE(pch, pch_next); 683147196Smarkus free(pch); 684147196Smarkus } 685147196Smarkus } 686138627Stakawata} 687147196Smarkus 688147196Smarkus