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