1185347Sattilio/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 4185347Sattilio * Copyright (c) 2008 Nokia Corporation 5185347Sattilio * All rights reserved. 6185347Sattilio * 7185347Sattilio * This software was developed by Attilio Rao for the IPSO project under 8185347Sattilio * contract to Nokia Corporation. 9185347Sattilio * 10185347Sattilio * Redistribution and use in source and binary forms, with or without 11185347Sattilio * modification, are permitted provided that the following conditions 12185347Sattilio * are met: 13185347Sattilio * 1. Redistributions of source code must retain the above copyright 14185347Sattilio * notice unmodified, this list of conditions, and the following 15185347Sattilio * disclaimer. 16185347Sattilio * 2. Redistributions in binary form must reproduce the above copyright 17185347Sattilio * notice, this list of conditions and the following disclaimer in the 18185347Sattilio * documentation and/or other materials provided with the distribution. 19185347Sattilio * 20185347Sattilio * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21185347Sattilio * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22185347Sattilio * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23185347Sattilio * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24185347Sattilio * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25185347Sattilio * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26185347Sattilio * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27185347Sattilio * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28185347Sattilio * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29185347Sattilio * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30185347Sattilio * 31185347Sattilio */ 32185347Sattilio 33185347Sattilio#include <sys/cdefs.h> 34185347Sattilio__FBSDID("$FreeBSD: stable/11/usr.sbin/pmcannotate/pmcannotate.c 330449 2018-03-05 07:26:05Z eadler $"); 35185347Sattilio 36185347Sattilio#include <sys/param.h> 37185347Sattilio#include <sys/queue.h> 38185347Sattilio 39185347Sattilio#include <ctype.h> 40185347Sattilio#include <stdio.h> 41185347Sattilio#include <stdlib.h> 42185347Sattilio#include <string.h> 43185347Sattilio 44185347Sattilio#include <unistd.h> 45185347Sattilio 46266173Sjmg/* NB: Make sure FNBUFF is as large as LNBUFF, otherwise it could overflow */ 47266166Sgnn#define FNBUFF 512 48266166Sgnn#define LNBUFF 512 49185347Sattilio 50185347Sattilio#define TMPPATH "/tmp/pmcannotate.XXXXXX" 51185347Sattilio 52185347Sattilio#define FATAL(ptr, x ...) do { \ 53185347Sattilio fqueue_deleteall(); \ 54185347Sattilio general_deleteall(); \ 55185347Sattilio if ((ptr) != NULL) \ 56185347Sattilio perror(ptr); \ 57185347Sattilio fprintf(stderr, ##x); \ 58185347Sattilio remove(tbfl); \ 59185347Sattilio remove(tofl); \ 60185347Sattilio exit(EXIT_FAILURE); \ 61185347Sattilio} while (0) 62185347Sattilio 63185347Sattilio#define PERCSAMP(x) ((x) * 100 / totalsamples) 64185347Sattilio 65185347Sattiliostruct entry { 66185347Sattilio TAILQ_ENTRY(entry) en_iter; 67185347Sattilio char *en_name; 68185347Sattilio uintptr_t en_pc; 69185347Sattilio uintptr_t en_ostart; 70185347Sattilio uintptr_t en_oend; 71185347Sattilio u_int en_nsamples; 72185347Sattilio}; 73185347Sattilio 74185347Sattiliostruct aggent { 75185347Sattilio TAILQ_ENTRY(aggent) ag_fiter; 76185347Sattilio long ag_offset; 77185347Sattilio uintptr_t ag_ostart; 78185347Sattilio uintptr_t ag_oend; 79185347Sattilio char *ag_name; 80185347Sattilio u_int ag_nsamples; 81185347Sattilio}; 82185347Sattilio 83185347Sattiliostatic struct aggent *agg_create(const char *name, u_int nsamples, 84185347Sattilio uintptr_t start, uintptr_t end); 85185347Sattiliostatic void agg_destroy(struct aggent *agg) __unused; 86185347Sattiliostatic void asmparse(FILE *fp); 87185347Sattiliostatic int cparse(FILE *fp); 88185347Sattiliostatic void entry_acqref(struct entry *entry); 89185347Sattiliostatic struct entry *entry_create(const char *name, uintptr_t pc, 90185347Sattilio uintptr_t start, uintptr_t end); 91185347Sattiliostatic void entry_destroy(struct entry *entry) __unused; 92185347Sattiliostatic void fqueue_compact(float th); 93185347Sattiliostatic void fqueue_deleteall(void); 94185347Sattiliostatic struct aggent *fqueue_findent_by_name(const char *name); 95185347Sattiliostatic int fqueue_getall(const char *bin, char *temp, int asmf); 96185347Sattiliostatic int fqueue_insertent(struct entry *entry); 97185347Sattiliostatic int fqueue_insertgen(void); 98185347Sattiliostatic void general_deleteall(void); 99185347Sattiliostatic struct entry *general_findent(uintptr_t pc); 100185347Sattiliostatic void general_insertent(struct entry *entry); 101185347Sattiliostatic void general_printasm(FILE *fp, struct aggent *agg); 102185347Sattiliostatic int general_printc(FILE *fp, struct aggent *agg); 103185347Sattiliostatic int printblock(FILE *fp, struct aggent *agg); 104185347Sattiliostatic void usage(const char *progname) __dead2; 105185347Sattilio 106185347Sattiliostatic TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst); 107185347Sattiliostatic TAILQ_HEAD(, aggent) fqueue = TAILQ_HEAD_INITIALIZER(fqueue); 108185347Sattilio 109185347Sattilio/* 110185347Sattilio * Use a float value in order to automatically promote operations 111185347Sattilio * to return a float value rather than use casts. 112185347Sattilio */ 113185347Sattiliostatic float totalsamples; 114185347Sattilio 115185347Sattilio/* 116185347Sattilio * Identifies a string cointaining objdump's assembly printout. 117185347Sattilio */ 118185347Sattiliostatic inline int 119185347Sattilioisasminline(const char *str) 120185347Sattilio{ 121185347Sattilio void *ptr; 122185347Sattilio int nbytes; 123185347Sattilio 124185347Sattilio if (sscanf(str, " %p%n", &ptr, &nbytes) != 1) 125185347Sattilio return (0); 126185347Sattilio if (str[nbytes] != ':' || isspace(str[nbytes + 1]) == 0) 127185347Sattilio return (0); 128185347Sattilio return (1); 129185347Sattilio} 130185347Sattilio 131185347Sattilio/* 132185347Sattilio * Identifies a string containing objdump's assembly printout 133185347Sattilio * for a new function. 134185347Sattilio */ 135185347Sattiliostatic inline int 136185347Sattilionewfunction(const char *str) 137185347Sattilio{ 138185347Sattilio char fname[FNBUFF]; 139185347Sattilio void *ptr; 140185347Sattilio int nbytes; 141185347Sattilio 142185347Sattilio if (isspace(str[0])) 143185347Sattilio return (0); 144185347Sattilio if (sscanf(str, "%p <%[^>:]>:%n", &ptr, fname, &nbytes) != 2) 145185347Sattilio return (0); 146185347Sattilio return (nbytes); 147185347Sattilio} 148185347Sattilio 149185347Sattilio/* 150185347Sattilio * Create a new first-level aggregation object for a specified 151185347Sattilio * function. 152185347Sattilio */ 153185347Sattiliostatic struct aggent * 154185347Sattilioagg_create(const char *name, u_int nsamples, uintptr_t start, uintptr_t end) 155185347Sattilio{ 156185347Sattilio struct aggent *agg; 157185347Sattilio 158185347Sattilio agg = calloc(1, sizeof(struct aggent)); 159185347Sattilio if (agg == NULL) 160185347Sattilio return (NULL); 161185347Sattilio agg->ag_name = strdup(name); 162185347Sattilio if (agg->ag_name == NULL) { 163185347Sattilio free(agg); 164185347Sattilio return (NULL); 165185347Sattilio } 166185347Sattilio agg->ag_nsamples = nsamples; 167185347Sattilio agg->ag_ostart = start; 168185347Sattilio agg->ag_oend = end; 169185347Sattilio return (agg); 170185347Sattilio} 171185347Sattilio 172185347Sattilio/* 173185347Sattilio * Destroy a first-level aggregation object for a specified 174185347Sattilio * function. 175185347Sattilio */ 176185347Sattiliostatic void 177185347Sattilioagg_destroy(struct aggent *agg) 178185347Sattilio{ 179185347Sattilio 180185347Sattilio free(agg->ag_name); 181185347Sattilio free(agg); 182185347Sattilio} 183185347Sattilio 184185347Sattilio/* 185185347Sattilio * Analyze the "objdump -d" output, locate functions and start 186185347Sattilio * printing out the assembly functions content. 187185347Sattilio * We do not use newfunction() because we actually need the 188185347Sattilio * function name in available form, but the heurstic used is 189185347Sattilio * the same. 190185347Sattilio */ 191185347Sattiliostatic void 192185347Sattilioasmparse(FILE *fp) 193185347Sattilio{ 194185347Sattilio char buffer[LNBUFF], fname[FNBUFF]; 195185347Sattilio struct aggent *agg; 196185347Sattilio void *ptr; 197185347Sattilio 198185347Sattilio while (fgets(buffer, LNBUFF, fp) != NULL) { 199185347Sattilio if (isspace(buffer[0])) 200185347Sattilio continue; 201185347Sattilio if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2) 202185347Sattilio continue; 203185347Sattilio agg = fqueue_findent_by_name(fname); 204185347Sattilio if (agg == NULL) 205185347Sattilio continue; 206185347Sattilio agg->ag_offset = ftell(fp); 207185347Sattilio } 208185347Sattilio 209185347Sattilio TAILQ_FOREACH(agg, &fqueue, ag_fiter) { 210185347Sattilio if (fseek(fp, agg->ag_offset, SEEK_SET) == -1) 211185347Sattilio return; 212185347Sattilio printf("Profile trace for function: %s() [%.2f%%]\n", 213185347Sattilio agg->ag_name, PERCSAMP(agg->ag_nsamples)); 214185347Sattilio general_printasm(fp, agg); 215185347Sattilio printf("\n"); 216185347Sattilio } 217185347Sattilio} 218185347Sattilio 219185347Sattilio/* 220185347Sattilio * Analyze the "objdump -S" output, locate functions and start 221185347Sattilio * printing out the C functions content. 222185347Sattilio * We do not use newfunction() because we actually need the 223185347Sattilio * function name in available form, but the heurstic used is 224185347Sattilio * the same. 225185347Sattilio * In order to maintain the printout sorted, on the first pass it 226185347Sattilio * simply stores the file offsets in order to fastly moved later 227185347Sattilio * (when the file is hot-cached also) when the real printout will 228185347Sattilio * happen. 229185347Sattilio */ 230185347Sattiliostatic int 231185347Sattiliocparse(FILE *fp) 232185347Sattilio{ 233185347Sattilio char buffer[LNBUFF], fname[FNBUFF]; 234185347Sattilio struct aggent *agg; 235185347Sattilio void *ptr; 236185347Sattilio 237185347Sattilio while (fgets(buffer, LNBUFF, fp) != NULL) { 238185347Sattilio if (isspace(buffer[0])) 239185347Sattilio continue; 240185347Sattilio if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2) 241185347Sattilio continue; 242185347Sattilio agg = fqueue_findent_by_name(fname); 243185347Sattilio if (agg == NULL) 244185347Sattilio continue; 245185347Sattilio agg->ag_offset = ftell(fp); 246185347Sattilio } 247185347Sattilio 248185347Sattilio TAILQ_FOREACH(agg, &fqueue, ag_fiter) { 249185347Sattilio if (fseek(fp, agg->ag_offset, SEEK_SET) == -1) 250185347Sattilio return (-1); 251185347Sattilio printf("Profile trace for function: %s() [%.2f%%]\n", 252185347Sattilio agg->ag_name, PERCSAMP(agg->ag_nsamples)); 253185347Sattilio if (general_printc(fp, agg) == -1) 254185347Sattilio return (-1); 255185347Sattilio printf("\n"); 256185347Sattilio } 257185347Sattilio return (0); 258185347Sattilio} 259185347Sattilio 260185347Sattilio/* 261185347Sattilio * Bump the number of samples for any raw entry. 262185347Sattilio */ 263185347Sattiliostatic void 264185347Sattilioentry_acqref(struct entry *entry) 265185347Sattilio{ 266185347Sattilio 267185347Sattilio entry->en_nsamples++; 268185347Sattilio} 269185347Sattilio 270185347Sattilio/* 271185347Sattilio * Create a new raw entry object for a specified function. 272185347Sattilio */ 273185347Sattiliostatic struct entry * 274185347Sattilioentry_create(const char *name, uintptr_t pc, uintptr_t start, uintptr_t end) 275185347Sattilio{ 276185347Sattilio struct entry *obj; 277185347Sattilio 278185347Sattilio obj = calloc(1, sizeof(struct entry)); 279185347Sattilio if (obj == NULL) 280185347Sattilio return (NULL); 281185347Sattilio obj->en_name = strdup(name); 282185347Sattilio if (obj->en_name == NULL) { 283185347Sattilio free(obj); 284185347Sattilio return (NULL); 285185347Sattilio } 286185347Sattilio obj->en_pc = pc; 287185347Sattilio obj->en_ostart = start; 288185347Sattilio obj->en_oend = end; 289185347Sattilio obj->en_nsamples = 1; 290185347Sattilio return (obj); 291185347Sattilio} 292185347Sattilio 293185347Sattilio/* 294185347Sattilio * Destroy a raw entry object for a specified function. 295185347Sattilio */ 296185347Sattiliostatic void 297185347Sattilioentry_destroy(struct entry *entry) 298185347Sattilio{ 299185347Sattilio 300185347Sattilio free(entry->en_name); 301185347Sattilio free(entry); 302185347Sattilio} 303185347Sattilio 304185347Sattilio/* 305185347Sattilio * Specify a lower bound in percentage and drop from the 306185347Sattilio * first-level aggregation queue all the objects with a 307185347Sattilio * smaller impact. 308185347Sattilio */ 309185347Sattiliostatic void 310185347Sattiliofqueue_compact(float th) 311185347Sattilio{ 312185347Sattilio u_int thi; 313185347Sattilio struct aggent *agg, *tmpagg; 314185347Sattilio 315185347Sattilio if (totalsamples == 0) 316185347Sattilio return; 317185347Sattilio 318185347Sattilio /* Revert the percentage calculation. */ 319185347Sattilio thi = th * totalsamples / 100; 320185347Sattilio TAILQ_FOREACH_SAFE(agg, &fqueue, ag_fiter, tmpagg) 321185347Sattilio if (agg->ag_nsamples < thi) 322185347Sattilio TAILQ_REMOVE(&fqueue, agg, ag_fiter); 323185347Sattilio} 324185347Sattilio 325185347Sattilio/* 326185347Sattilio * Flush the first-level aggregates queue. 327185347Sattilio */ 328185347Sattiliostatic void 329201227Sedfqueue_deleteall(void) 330185347Sattilio{ 331185347Sattilio struct aggent *agg; 332185347Sattilio 333185347Sattilio while (TAILQ_EMPTY(&fqueue) == 0) { 334185347Sattilio agg = TAILQ_FIRST(&fqueue); 335185347Sattilio TAILQ_REMOVE(&fqueue, agg, ag_fiter); 336185347Sattilio } 337185347Sattilio} 338185347Sattilio 339185347Sattilio/* 340185347Sattilio * Insert a raw entry into the aggregations queue. 341185347Sattilio * If the respective first-level aggregation object 342185347Sattilio * does not exist create it and maintain it sorted 343185347Sattilio * in respect of the number of samples. 344185347Sattilio */ 345185347Sattiliostatic int 346185347Sattiliofqueue_insertent(struct entry *entry) 347185347Sattilio{ 348185347Sattilio struct aggent *obj, *tmp; 349185347Sattilio int found; 350185347Sattilio 351185347Sattilio found = 0; 352185347Sattilio TAILQ_FOREACH(obj, &fqueue, ag_fiter) 353185347Sattilio if (!strcmp(obj->ag_name, entry->en_name)) { 354185347Sattilio found = 1; 355185347Sattilio obj->ag_nsamples += entry->en_nsamples; 356185347Sattilio break; 357185347Sattilio } 358185347Sattilio 359185347Sattilio /* 360213928Sbcr * If the first-level aggregation object already exists, 361185347Sattilio * just aggregate the samples and, if needed, resort 362185347Sattilio * it. 363185347Sattilio */ 364185347Sattilio if (found) { 365185347Sattilio TAILQ_REMOVE(&fqueue, obj, ag_fiter); 366185347Sattilio found = 0; 367185347Sattilio TAILQ_FOREACH(tmp, &fqueue, ag_fiter) 368185347Sattilio if (obj->ag_nsamples > tmp->ag_nsamples) { 369185347Sattilio found = 1; 370185347Sattilio break; 371185347Sattilio } 372185347Sattilio if (found) 373185347Sattilio TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter); 374185347Sattilio else 375185347Sattilio TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter); 376185347Sattilio return (0); 377185347Sattilio } 378185347Sattilio 379185347Sattilio /* 380185347Sattilio * If the first-level aggregation object does not 381185347Sattilio * exist, create it and put in the sorted queue. 382185347Sattilio * If this is the first object, we need to set the 383185347Sattilio * head of the queue. 384185347Sattilio */ 385185347Sattilio obj = agg_create(entry->en_name, entry->en_nsamples, entry->en_ostart, 386185347Sattilio entry->en_oend); 387185347Sattilio if (obj == NULL) 388185347Sattilio return (-1); 389185347Sattilio if (TAILQ_EMPTY(&fqueue) != 0) { 390185347Sattilio TAILQ_INSERT_HEAD(&fqueue, obj, ag_fiter); 391185347Sattilio return (0); 392185347Sattilio } 393185347Sattilio TAILQ_FOREACH(tmp, &fqueue, ag_fiter) 394185347Sattilio if (obj->ag_nsamples > tmp->ag_nsamples) { 395185347Sattilio found = 1; 396185347Sattilio break; 397185347Sattilio } 398185347Sattilio if (found) 399185347Sattilio TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter); 400185347Sattilio else 401185347Sattilio TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter); 402185347Sattilio return (0); 403185347Sattilio} 404185347Sattilio 405185347Sattilio/* 406185347Sattilio * Lookup a first-level aggregation object by name. 407185347Sattilio */ 408185347Sattiliostatic struct aggent * 409185347Sattiliofqueue_findent_by_name(const char *name) 410185347Sattilio{ 411185347Sattilio struct aggent *obj; 412185347Sattilio 413185347Sattilio TAILQ_FOREACH(obj, &fqueue, ag_fiter) 414185347Sattilio if (!strcmp(obj->ag_name, name)) 415185347Sattilio return (obj); 416185347Sattilio return (NULL); 417185347Sattilio} 418185347Sattilio 419185347Sattilio/* 420185347Sattilio * Return the number of object in the first-level aggregations queue. 421185347Sattilio */ 422185347Sattiliostatic int 423185347Sattiliofqueue_getall(const char *bin, char *temp, int asmf) 424185347Sattilio{ 425185347Sattilio char tmpf[MAXPATHLEN * 2 + 50]; 426185347Sattilio struct aggent *agg; 427185347Sattilio uintptr_t start, end; 428185347Sattilio 429185347Sattilio if (mkstemp(temp) == -1) 430185347Sattilio return (-1); 431185347Sattilio TAILQ_FOREACH(agg, &fqueue, ag_fiter) { 432185347Sattilio bzero(tmpf, sizeof(tmpf)); 433185347Sattilio start = agg->ag_ostart; 434185347Sattilio end = agg->ag_oend; 435185347Sattilio 436185347Sattilio /* 437185347Sattilio * Fix-up the end address in order to show it in the objdump's 438185347Sattilio * trace. 439185347Sattilio */ 440185347Sattilio end++; 441185347Sattilio if (asmf) 442185347Sattilio snprintf(tmpf, sizeof(tmpf), 443185347Sattilio "objdump --start-address=%p " 444185347Sattilio "--stop-address=%p -d %s >> %s", (void *)start, 445185347Sattilio (void *)end, bin, temp); 446185347Sattilio else 447185347Sattilio snprintf(tmpf, sizeof(tmpf), 448185347Sattilio "objdump --start-address=%p " 449185347Sattilio "--stop-address=%p -S %s >> %s", (void *)start, 450185347Sattilio (void *)end, bin, temp); 451185347Sattilio if (system(tmpf) != 0) 452185347Sattilio return (-1); 453185347Sattilio } 454185347Sattilio return (0); 455185347Sattilio} 456185347Sattilio 457185347Sattilio/* 458185347Sattilio * Insert all the raw entries present in the general queue 459185347Sattilio * into the first-level aggregations queue. 460185347Sattilio */ 461185347Sattiliostatic int 462185347Sattiliofqueue_insertgen(void) 463185347Sattilio{ 464185347Sattilio struct entry *obj; 465185347Sattilio 466185347Sattilio TAILQ_FOREACH(obj, &mainlst, en_iter) 467185347Sattilio if (fqueue_insertent(obj) == -1) 468185347Sattilio return (-1); 469185347Sattilio return (0); 470185347Sattilio} 471185347Sattilio 472185347Sattilio/* 473185347Sattilio * Flush the raw entries general queue. 474185347Sattilio */ 475185347Sattiliostatic void 476201227Sedgeneral_deleteall(void) 477185347Sattilio{ 478185347Sattilio struct entry *obj; 479185347Sattilio 480185347Sattilio while (TAILQ_EMPTY(&mainlst) == 0) { 481185347Sattilio obj = TAILQ_FIRST(&mainlst); 482185347Sattilio TAILQ_REMOVE(&mainlst, obj, en_iter); 483185347Sattilio } 484185347Sattilio} 485185347Sattilio 486185347Sattilio/* 487185347Sattilio * Lookup a raw entry by the PC. 488185347Sattilio */ 489185347Sattiliostatic struct entry * 490185347Sattiliogeneral_findent(uintptr_t pc) 491185347Sattilio{ 492185347Sattilio struct entry *obj; 493185347Sattilio 494185347Sattilio TAILQ_FOREACH(obj, &mainlst, en_iter) 495185347Sattilio if (obj->en_pc == pc) 496185347Sattilio return (obj); 497185347Sattilio return (NULL); 498185347Sattilio} 499185347Sattilio 500185347Sattilio/* 501185347Sattilio * Insert a new raw entry in the general queue. 502185347Sattilio */ 503185347Sattiliostatic void 504185347Sattiliogeneral_insertent(struct entry *entry) 505185347Sattilio{ 506185347Sattilio 507185347Sattilio TAILQ_INSERT_TAIL(&mainlst, entry, en_iter); 508185347Sattilio} 509185347Sattilio 510185347Sattilio/* 511185347Sattilio * Printout the body of an "objdump -d" assembly function. 512185347Sattilio * It does simply stops when a new function is encountered, 513185347Sattilio * bringing back the file position in order to not mess up 514185347Sattilio * subsequent analysis. 515185347Sattilio * C lines and others not recognized are simply skipped. 516185347Sattilio */ 517185347Sattiliostatic void 518185347Sattiliogeneral_printasm(FILE *fp, struct aggent *agg) 519185347Sattilio{ 520185347Sattilio char buffer[LNBUFF]; 521185347Sattilio struct entry *obj; 522185347Sattilio int nbytes; 523185347Sattilio void *ptr; 524185347Sattilio 525185347Sattilio while (fgets(buffer, LNBUFF, fp) != NULL) { 526185347Sattilio if ((nbytes = newfunction(buffer)) != 0) { 527185347Sattilio fseek(fp, nbytes * -1, SEEK_CUR); 528185347Sattilio break; 529185347Sattilio } 530185347Sattilio if (!isasminline(buffer)) 531185347Sattilio continue; 532185347Sattilio if (sscanf(buffer, " %p:", &ptr) != 1) 533185347Sattilio continue; 534185347Sattilio obj = general_findent((uintptr_t)ptr); 535185347Sattilio if (obj == NULL) 536185347Sattilio printf("\t| %s", buffer); 537185347Sattilio else 538185347Sattilio printf("%.2f%%\t| %s", 539185347Sattilio (float)obj->en_nsamples * 100 / agg->ag_nsamples, 540185347Sattilio buffer); 541185347Sattilio } 542185347Sattilio} 543185347Sattilio 544185347Sattilio/* 545185347Sattilio * Printout the body of an "objdump -S" function. 546185347Sattilio * It does simply stops when a new function is encountered, 547185347Sattilio * bringing back the file position in order to not mess up 548185347Sattilio * subsequent analysis. 549185347Sattilio * It expect from the starting to the end to find, always, valid blocks 550185347Sattilio * (see below for an explanation of the "block" concept). 551185347Sattilio */ 552185347Sattiliostatic int 553185347Sattiliogeneral_printc(FILE *fp, struct aggent *agg) 554185347Sattilio{ 555185347Sattilio char buffer[LNBUFF]; 556185347Sattilio 557185347Sattilio while (fgets(buffer, LNBUFF, fp) != NULL) { 558185347Sattilio fseek(fp, strlen(buffer) * -1, SEEK_CUR); 559185347Sattilio if (newfunction(buffer) != 0) 560185347Sattilio break; 561185347Sattilio if (printblock(fp, agg) == -1) 562185347Sattilio return (-1); 563185347Sattilio } 564185347Sattilio return (0); 565185347Sattilio} 566185347Sattilio 567185347Sattilio/* 568185347Sattilio * Printout a single block inside an "objdump -S" function. 569185347Sattilio * The block is composed of a first part in C and subsequent translation 570185347Sattilio * in assembly. 571185347Sattilio * This code also operates a second-level aggregation packing together 572185347Sattilio * samples relative to PCs into a (lower bottom) block with their 573185347Sattilio * C (higher half) counterpart. 574185347Sattilio */ 575185347Sattiliostatic int 576185347Sattilioprintblock(FILE *fp, struct aggent *agg) 577185347Sattilio{ 578185347Sattilio char buffer[LNBUFF]; 579185347Sattilio long lstart; 580185347Sattilio struct entry *obj; 581185347Sattilio u_int tnsamples; 582185347Sattilio int done, nbytes, sentinel; 583185347Sattilio void *ptr; 584185347Sattilio 585185347Sattilio /* 586185347Sattilio * We expect the first thing of the block is C code, so simply give 587185347Sattilio * up if asm line is found. 588185347Sattilio */ 589185347Sattilio lstart = ftell(fp); 590185347Sattilio sentinel = 0; 591185347Sattilio for (;;) { 592185347Sattilio if (fgets(buffer, LNBUFF, fp) == NULL) 593185347Sattilio return (0); 594185347Sattilio if (isasminline(buffer) != 0) 595185347Sattilio break; 596185347Sattilio sentinel = 1; 597185347Sattilio nbytes = newfunction(buffer); 598185347Sattilio if (nbytes != 0) { 599185347Sattilio if (fseek(fp, nbytes * -1, SEEK_CUR) == -1) 600185347Sattilio return (-1); 601185347Sattilio return (0); 602185347Sattilio } 603185347Sattilio } 604185347Sattilio 605185347Sattilio /* 606185347Sattilio * If the sentinel is not set, it means it did not match any 607185347Sattilio * "high half" for this code so simply give up. 608185347Sattilio * Operates the second-level aggregation. 609185347Sattilio */ 610185347Sattilio tnsamples = 0; 611185347Sattilio do { 612185347Sattilio if (sentinel == 0) 613185347Sattilio return (-1); 614185347Sattilio if (sscanf(buffer, " %p:", &ptr) != 1) 615185347Sattilio return (-1); 616185347Sattilio obj = general_findent((uintptr_t)ptr); 617185347Sattilio if (obj != NULL) 618185347Sattilio tnsamples += obj->en_nsamples; 619185347Sattilio } while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0); 620185347Sattilio 621185347Sattilio /* Rewind to the start of the block in order to start the printout. */ 622185347Sattilio if (fseek(fp, lstart, SEEK_SET) == -1) 623185347Sattilio return (-1); 624185347Sattilio 625185347Sattilio /* Again the high half of the block rappresenting the C part. */ 626185347Sattilio done = 0; 627185347Sattilio while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) { 628185347Sattilio if (tnsamples == 0 || done != 0) 629185347Sattilio printf("\t| %s", buffer); 630185347Sattilio else { 631185347Sattilio done = 1; 632185347Sattilio printf("%.2f%%\t| %s", 633185347Sattilio (float)tnsamples * 100 / agg->ag_nsamples, buffer); 634185347Sattilio } 635185347Sattilio } 636185347Sattilio 637185347Sattilio /* 638185347Sattilio * Again the low half of the block rappresenting the asm 639185347Sattilio * translation part. 640185347Sattilio */ 641185347Sattilio for (;;) { 642185347Sattilio if (fgets(buffer, LNBUFF, fp) == NULL) 643185347Sattilio return (0); 644185347Sattilio if (isasminline(buffer) == 0) 645185347Sattilio break; 646185347Sattilio nbytes = newfunction(buffer); 647185347Sattilio if (nbytes != 0) { 648185347Sattilio if (fseek(fp, nbytes * -1, SEEK_CUR) == -1) 649185347Sattilio return (-1); 650185347Sattilio return (0); 651185347Sattilio } 652185347Sattilio } 653185347Sattilio if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1) 654185347Sattilio return (-1); 655185347Sattilio return (0); 656185347Sattilio} 657185347Sattilio 658185347Sattilio/* 659185347Sattilio * Helper printout functions. 660185347Sattilio */ 661185347Sattiliostatic void 662185347Sattiliousage(const char *progname) 663185347Sattilio{ 664185347Sattilio 665185347Sattilio fprintf(stderr, 666185347Sattilio "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n", 667185347Sattilio progname); 668185347Sattilio exit(EXIT_SUCCESS); 669185347Sattilio} 670185347Sattilio 671185347Sattilioint 672185347Sattiliomain(int argc, char *argv[]) 673185347Sattilio{ 674185347Sattilio char buffer[LNBUFF], fname[FNBUFF], tbfl[] = TMPPATH, tofl[] = TMPPATH; 675185347Sattilio char tmpf[MAXPATHLEN * 2 + 50]; 676185347Sattilio float limit; 677185347Sattilio char *bin, *exec, *kfile, *ofile; 678185347Sattilio struct entry *obj; 679185347Sattilio FILE *gfp, *bfp; 680185347Sattilio void *ptr, *hstart, *hend; 681185347Sattilio uintptr_t tmppc, ostart, oend; 682185347Sattilio int cget, asmsrc; 683185347Sattilio 684185347Sattilio exec = argv[0]; 685185347Sattilio ofile = NULL; 686185347Sattilio bin = NULL; 687185347Sattilio kfile = NULL; 688185347Sattilio asmsrc = 0; 689185347Sattilio limit = 0.5; 690185347Sattilio while ((cget = getopt(argc, argv, "ahl:k:")) != -1) 691185347Sattilio switch(cget) { 692185347Sattilio case 'a': 693185347Sattilio asmsrc = 1; 694185347Sattilio break; 695185347Sattilio case 'k': 696185347Sattilio kfile = optarg; 697185347Sattilio break; 698185347Sattilio case 'l': 699185347Sattilio limit = (float)atof(optarg); 700185347Sattilio break; 701185347Sattilio case 'h': 702185347Sattilio case '?': 703185347Sattilio default: 704185347Sattilio usage(exec); 705185347Sattilio } 706185347Sattilio argc -= optind; 707185347Sattilio argv += optind; 708185347Sattilio if (argc != 2) 709185347Sattilio usage(exec); 710185347Sattilio ofile = argv[0]; 711185347Sattilio bin = argv[1]; 712185347Sattilio 713185347Sattilio if (access(bin, R_OK | F_OK) == -1) 714185347Sattilio FATAL(exec, "%s: Impossible to locate the binary file\n", 715185347Sattilio exec); 716185347Sattilio if (access(ofile, R_OK | F_OK) == -1) 717185347Sattilio FATAL(exec, "%s: Impossible to locate the pmcstat file\n", 718185347Sattilio exec); 719185347Sattilio if (kfile != NULL && access(kfile, R_OK | F_OK) == -1) 720185347Sattilio FATAL(exec, "%s: Impossible to locate the kernel file\n", 721185347Sattilio exec); 722185347Sattilio 723185347Sattilio bzero(tmpf, sizeof(tmpf)); 724185347Sattilio if (mkstemp(tofl) == -1) 725185347Sattilio FATAL(exec, "%s: Impossible to create the tmp file\n", 726185347Sattilio exec); 727185347Sattilio if (kfile != NULL) 728185347Sattilio snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s", 729185347Sattilio kfile, ofile, tofl); 730185347Sattilio else 731185347Sattilio snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile, 732185347Sattilio tofl); 733185347Sattilio if (system(tmpf) != 0) 734185347Sattilio FATAL(exec, "%s: Impossible to create the tmp file\n", 735185347Sattilio exec); 736185347Sattilio 737185347Sattilio gfp = fopen(tofl, "r"); 738185347Sattilio if (gfp == NULL) 739185347Sattilio FATAL(exec, "%s: Impossible to open the map file\n", 740185347Sattilio exec); 741185347Sattilio 742185347Sattilio /* 743185347Sattilio * Make the collection of raw entries from a pmcstat mapped file. 744185347Sattilio * The heuristic here wants strings in the form: 745185347Sattilio * "addr funcname startfaddr endfaddr". 746185347Sattilio */ 747185347Sattilio while (fgets(buffer, LNBUFF, gfp) != NULL) { 748185347Sattilio if (isspace(buffer[0])) 749185347Sattilio continue; 750185347Sattilio if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname, 751185347Sattilio &hstart, &hend) != 4) 752185347Sattilio FATAL(NULL, 753185347Sattilio "%s: Invalid scan of function in the map file\n", 754185347Sattilio exec); 755185347Sattilio ostart = (uintptr_t)hstart; 756185347Sattilio oend = (uintptr_t)hend; 757185347Sattilio tmppc = (uintptr_t)ptr; 758185347Sattilio totalsamples++; 759185347Sattilio obj = general_findent(tmppc); 760185347Sattilio if (obj != NULL) { 761185347Sattilio entry_acqref(obj); 762185347Sattilio continue; 763185347Sattilio } 764185347Sattilio obj = entry_create(fname, tmppc, ostart, oend); 765185347Sattilio if (obj == NULL) 766185347Sattilio FATAL(exec, 767185347Sattilio "%s: Impossible to create a new object\n", exec); 768185347Sattilio general_insertent(obj); 769185347Sattilio } 770185347Sattilio if (fclose(gfp) == EOF) 771185347Sattilio FATAL(exec, "%s: Impossible to close the filedesc\n", 772185347Sattilio exec); 773185347Sattilio if (remove(tofl) == -1) 774185347Sattilio FATAL(exec, "%s: Impossible to remove the tmpfile\n", 775185347Sattilio exec); 776185347Sattilio 777185347Sattilio /* 778185347Sattilio * Remove the loose end objects and feed the first-level aggregation 779185347Sattilio * queue. 780185347Sattilio */ 781185347Sattilio if (fqueue_insertgen() == -1) 782185347Sattilio FATAL(exec, "%s: Impossible to generate an analysis\n", 783185347Sattilio exec); 784185347Sattilio fqueue_compact(limit); 785185347Sattilio if (fqueue_getall(bin, tbfl, asmsrc) == -1) 786185347Sattilio FATAL(exec, "%s: Impossible to create the tmp file\n", 787185347Sattilio exec); 788185347Sattilio 789185347Sattilio bfp = fopen(tbfl, "r"); 790185347Sattilio if (bfp == NULL) 791185347Sattilio FATAL(exec, "%s: Impossible to open the binary file\n", 792185347Sattilio exec); 793185347Sattilio 794185347Sattilio if (asmsrc != 0) 795185347Sattilio asmparse(bfp); 796185347Sattilio else if (cparse(bfp) == -1) 797185347Sattilio FATAL(NULL, "%s: Invalid format for the C file\n", exec); 798185347Sattilio if (fclose(bfp) == EOF) 799185347Sattilio FATAL(exec, "%s: Impossible to close the filedesc\n", 800185347Sattilio exec); 801185347Sattilio if (remove(tbfl) == -1) 802185347Sattilio FATAL(exec, "%s: Impossible to remove the tmpfile\n", 803185347Sattilio exec); 804185347Sattilio return (0); 805185347Sattilio} 806