1 /*- 2 * Copyright (c) 2005-2007, Joseph Koshy 3 * Copyright (c) 2007 The FreeBSD Foundation 4 * Copyright (c) 2009, Fabien Thomas 5 * All rights reserved. 6 * 7 * Portions of this software were developed by A. Joseph Koshy under 8 * sponsorship from the FreeBSD Foundation and Google, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Transform a hwpmc(4) log into human readable form, and into 34 * gprof(1) compatible profiles. 35 */ 36 37 #include <sys/cdefs.h> 38 __FBSDID("$FreeBSD$"); 39 40 #include <sys/param.h> 41 #include <sys/endian.h> 42 #include <sys/gmon.h> 43 #include <sys/imgact_aout.h> 44 #include <sys/imgact_elf.h> 45 #include <sys/mman.h> 46 #include <sys/pmc.h> 47 #include <sys/queue.h> 48 #include <sys/socket.h> 49 #include <sys/stat.h> 50 #include <sys/wait.h> 51 52 #include <netinet/in.h> 53 54 #include <assert.h> 55 #include <curses.h> 56 #include <err.h> 57 #include <errno.h> 58 #include <fcntl.h> 59 #include <gelf.h> 60 #include <libgen.h> 61 #include <limits.h> 62 #include <netdb.h> 63 #include <pmc.h> 64 #include <pmclog.h> 65 #include <sysexits.h> 66 #include <stdint.h> 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <string.h> 70 #include <unistd.h> 71 72 #include "pmcstat.h" 73 #include "pmcstat_log.h" 74 #include "pmcpl_callgraph.h" 75 #include "pmcpl_gprof.h" 76 77 typedef uint64_t WIDEHISTCOUNTER; 78 79 #define WIDEHISTCOUNTER_MAX UINT64_MAX 80 #define HISTCOUNTER_MAX USHRT_MAX 81 #define WIDEHISTCOUNTER_GMONTYPE ((int) 64) 82 #define HISTCOUNTER_GMONTYPE ((int) 0) 83 static int hc_sz=0; 84 85 /* 86 * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These 87 * files are mmap()'ed in as needed. 88 */ 89 90 struct pmcstat_gmonfile { 91 LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */ 92 int pgf_overflow; /* whether a count overflowed */ 93 pmc_id_t pgf_pmcid; /* id of the associated pmc */ 94 size_t pgf_nbuckets; /* #buckets in this gmon.out */ 95 unsigned int pgf_nsamples; /* #samples in this gmon.out */ 96 pmcstat_interned_string pgf_name; /* pathname of gmon.out file */ 97 size_t pgf_ndatabytes; /* number of bytes mapped */ 98 void *pgf_gmondata; /* pointer to mmap'ed data */ 99 FILE *pgf_file; /* used when writing gmon arcs */ 100 }; 101 102 /* 103 * Prototypes 104 */ 105 106 static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf, 107 struct pmcstat_image *_image); 108 static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd, 109 struct pmcstat_image *_img, pmc_id_t _pmcid); 110 static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); 111 static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); 112 113 static struct pmcstat_gmonfile *pmcstat_image_find_gmonfile(struct 114 pmcstat_image *_i, pmc_id_t _id); 115 116 /* 117 * Create a gmon.out file and size it. 118 */ 119 120 static void 121 pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf, 122 struct pmcstat_image *image) 123 { 124 int fd; 125 size_t count; 126 struct gmonhdr gm; 127 const char *pathname; 128 char buffer[DEFAULT_BUFFER_SIZE]; 129 130 pathname = pmcstat_string_unintern(pgf->pgf_name); 131 if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT, 132 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) 133 err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname); 134 135 gm.lpc = image->pi_start; 136 gm.hpc = image->pi_end; 137 gm.ncnt = (pgf->pgf_nbuckets * hc_sz) + sizeof(struct gmonhdr); 138 gm.version = GMONVERSION; 139 gm.profrate = 0; /* use ticks */ 140 if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) 141 gm.histcounter_type = WIDEHISTCOUNTER_GMONTYPE; 142 else 143 gm.histcounter_type = HISTCOUNTER_GMONTYPE; 144 gm.spare[0] = gm.spare[1] = 0; 145 146 /* Write out the gmon header */ 147 if (write(fd, &gm, sizeof(gm)) < 0) 148 goto error; 149 150 /* Zero fill the samples[] array */ 151 (void) memset(buffer, 0, sizeof(buffer)); 152 153 count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr); 154 while (count > sizeof(buffer)) { 155 if (write(fd, &buffer, sizeof(buffer)) < 0) 156 goto error; 157 count -= sizeof(buffer); 158 } 159 160 if (write(fd, &buffer, count) < 0) 161 goto error; 162 163 (void) close(fd); 164 165 return; 166 167 error: 168 err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname); 169 } 170 171 /* 172 * Determine the full pathname of a gmon.out file for a given 173 * (image,pmcid) combination. Return the interned string. 174 */ 175 176 pmcstat_interned_string 177 pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image, 178 pmc_id_t pmcid) 179 { 180 const char *pmcname; 181 char fullpath[PATH_MAX]; 182 183 pmcname = pmcstat_pmcid_to_name(pmcid); 184 if (!pmcname) 185 err(EX_SOFTWARE, "ERROR: cannot find pmcid"); 186 187 (void) snprintf(fullpath, sizeof(fullpath), 188 "%s/%s/%s", samplesdir, pmcname, 189 pmcstat_string_unintern(image->pi_samplename)); 190 191 return (pmcstat_string_intern(fullpath)); 192 } 193 194 195 /* 196 * Mmap in a gmon.out file for processing. 197 */ 198 199 static void 200 pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf) 201 { 202 int fd; 203 const char *pathname; 204 205 pathname = pmcstat_string_unintern(pgf->pgf_name); 206 207 /* the gmon.out file must already exist */ 208 if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0) 209 err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname); 210 211 pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes, 212 PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0); 213 214 if (pgf->pgf_gmondata == MAP_FAILED) 215 err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname); 216 217 (void) close(fd); 218 } 219 220 /* 221 * Unmap a gmon.out file after sync'ing its data to disk. 222 */ 223 224 static void 225 pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf) 226 { 227 (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes, 228 MS_SYNC); 229 (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes); 230 pgf->pgf_gmondata = NULL; 231 } 232 233 static void 234 pmcstat_gmon_append_arc(struct pmcstat_image *image, pmc_id_t pmcid, 235 uintptr_t rawfrom, uintptr_t rawto, uint32_t count) 236 { 237 struct rawarc arc; /* from <sys/gmon.h> */ 238 const char *pathname; 239 struct pmcstat_gmonfile *pgf; 240 241 if ((pgf = pmcstat_image_find_gmonfile(image, pmcid)) == NULL) 242 return; 243 244 if (pgf->pgf_file == NULL) { 245 pathname = pmcstat_string_unintern(pgf->pgf_name); 246 if ((pgf->pgf_file = fopen(pathname, "a")) == NULL) 247 return; 248 } 249 250 arc.raw_frompc = rawfrom + image->pi_vaddr; 251 arc.raw_selfpc = rawto + image->pi_vaddr; 252 arc.raw_count = count; 253 254 (void) fwrite(&arc, sizeof(arc), 1, pgf->pgf_file); 255 256 } 257 258 static struct pmcstat_gmonfile * 259 pmcstat_image_find_gmonfile(struct pmcstat_image *image, pmc_id_t pmcid) 260 { 261 struct pmcstat_gmonfile *pgf; 262 LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next) 263 if (pgf->pgf_pmcid == pmcid) 264 return (pgf); 265 return (NULL); 266 } 267 268 static void 269 pmcstat_cgnode_do_gmon_arcs(struct pmcstat_cgnode *cg, pmc_id_t pmcid) 270 { 271 struct pmcstat_cgnode *cgc; 272 273 /* 274 * Look for child nodes that belong to the same image. 275 */ 276 277 LIST_FOREACH(cgc, &cg->pcg_children, pcg_sibling) { 278 if (cgc->pcg_image == cg->pcg_image) 279 pmcstat_gmon_append_arc(cg->pcg_image, pmcid, 280 cgc->pcg_func, cg->pcg_func, cgc->pcg_count); 281 if (cgc->pcg_nchildren > 0) 282 pmcstat_cgnode_do_gmon_arcs(cgc, pmcid); 283 } 284 } 285 286 static void 287 pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmc_id_t pmcid) 288 { 289 int n; 290 struct pmcstat_cgnode_hash *pch; 291 292 for (n = 0; n < PMCSTAT_NHASH; n++) 293 LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next) 294 if (pch->pch_pmcid == pmcid && 295 pch->pch_cgnode->pcg_nchildren > 1) 296 pmcstat_cgnode_do_gmon_arcs(pch->pch_cgnode, 297 pmcid); 298 } 299 300 301 static void 302 pmcstat_callgraph_do_gmon_arcs(void) 303 { 304 struct pmcstat_pmcrecord *pmcr; 305 306 LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next) 307 pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmcr->pr_pmcid); 308 } 309 310 void 311 pmcpl_gmon_initimage(struct pmcstat_image *pi) 312 { 313 const char *execpath; 314 int count, nlen; 315 char *sn, *snbuf; 316 char name[NAME_MAX]; 317 318 /* 319 * Look for a suitable name for the sample files associated 320 * with this image: if `basename(path)`+".gmon" is available, 321 * we use that, otherwise we try iterating through 322 * `basename(path)`+ "~" + NNN + ".gmon" till we get a free 323 * entry. 324 */ 325 execpath = pmcstat_string_unintern(pi->pi_execpath); 326 if ((snbuf = strdup(execpath)) == NULL) 327 err(EX_OSERR, "ERROR: Cannot copy \"%s\"", execpath); 328 if ((sn = basename(snbuf)) == NULL) 329 err(EX_OSERR, "ERROR: Cannot process \"%s\"", execpath); 330 331 nlen = strlen(sn); 332 nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon"))); 333 334 snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn); 335 336 /* try use the unabridged name first */ 337 if (pmcstat_string_lookup(name) == NULL) 338 pi->pi_samplename = pmcstat_string_intern(name); 339 else { 340 /* 341 * Otherwise use a prefix from the original name and 342 * up to 3 digits. 343 */ 344 nlen = strlen(sn); 345 nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon"))); 346 count = 0; 347 do { 348 if (++count > 999) 349 errx(EX_CANTCREAT, 350 "ERROR: cannot create a gmon file for" 351 " \"%s\"", name); 352 snprintf(name, sizeof(name), "%.*s~%3.3d.gmon", 353 nlen, sn, count); 354 if (pmcstat_string_lookup(name) == NULL) { 355 pi->pi_samplename = 356 pmcstat_string_intern(name); 357 count = 0; 358 } 359 } while (count > 0); 360 } 361 free(snbuf); 362 363 LIST_INIT(&pi->pi_gmlist); 364 } 365 366 void 367 pmcpl_gmon_shutdownimage(struct pmcstat_image *pi) 368 { 369 struct pmcstat_gmonfile *pgf, *pgftmp; 370 371 LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next, pgftmp) { 372 if (pgf->pgf_file) 373 (void) fclose(pgf->pgf_file); 374 LIST_REMOVE(pgf, pgf_next); 375 free(pgf); 376 } 377 } 378 379 void 380 pmcpl_gmon_newpmc(pmcstat_interned_string ps, struct pmcstat_pmcrecord *pr) 381 { 382 struct stat st; 383 char fullpath[PATH_MAX]; 384 385 (void) pr; 386 387 /* 388 * Create the appropriate directory to hold gmon.out files. 389 */ 390 391 (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", args.pa_samplesdir, 392 pmcstat_string_unintern(ps)); 393 394 /* If the path name exists, it should be a directory */ 395 if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) 396 return; 397 398 if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) 399 err(EX_OSERR, "ERROR: Cannot create directory \"%s\"", 400 fullpath); 401 } 402 403 /* 404 * Increment the bucket in the gmon.out file corresponding to 'pmcid' 405 * and 'pc'. 406 */ 407 408 void 409 pmcpl_gmon_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr, 410 uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu) 411 { 412 struct pmcstat_pcmap *map; 413 struct pmcstat_image *image; 414 struct pmcstat_gmonfile *pgf; 415 uintfptr_t bucket; 416 HISTCOUNTER *hc; 417 WIDEHISTCOUNTER *whc; 418 pmc_id_t pmcid; 419 420 (void) nsamples; (void) usermode; (void) cpu; 421 422 map = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, cc[0]); 423 if (map == NULL) { 424 /* Unknown offset. */ 425 pmcstat_stats.ps_samples_unknown_offset++; 426 return; 427 } 428 429 assert(cc[0] >= map->ppm_lowpc && cc[0] < map->ppm_highpc); 430 431 image = map->ppm_image; 432 pmcid = pmcr->pr_pmcid; 433 434 /* 435 * If this is the first time we are seeing a sample for 436 * this executable image, try determine its parameters. 437 */ 438 if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN) 439 pmcstat_image_determine_type(image); 440 441 assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN); 442 443 /* Ignore samples in images that we know nothing about. */ 444 if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) { 445 pmcstat_stats.ps_samples_indeterminable++; 446 return; 447 } 448 449 /* 450 * Find the gmon file corresponding to 'pmcid', creating it if 451 * needed. 452 */ 453 pgf = pmcstat_image_find_gmonfile(image, pmcid); 454 if (pgf == NULL) { 455 if (hc_sz == 0) { 456 /* Determine the correct histcounter size. */ 457 if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) 458 hc_sz = sizeof(WIDEHISTCOUNTER); 459 else 460 hc_sz = sizeof(HISTCOUNTER); 461 } 462 463 if ((pgf = calloc(1, sizeof(*pgf))) == NULL) 464 err(EX_OSERR, "ERROR:"); 465 466 pgf->pgf_gmondata = NULL; /* mark as unmapped */ 467 pgf->pgf_name = pmcstat_gmon_create_name(args.pa_samplesdir, 468 image, pmcid); 469 pgf->pgf_pmcid = pmcid; 470 assert(image->pi_end > image->pi_start); 471 pgf->pgf_nbuckets = (image->pi_end - image->pi_start) / 472 FUNCTION_ALIGNMENT; /* see <machine/profile.h> */ 473 pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + 474 pgf->pgf_nbuckets * hc_sz; 475 pgf->pgf_nsamples = 0; 476 pgf->pgf_file = NULL; 477 478 pmcstat_gmon_create_file(pgf, image); 479 480 LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next); 481 } 482 483 /* 484 * Map the gmon file in if needed. It may have been mapped 485 * out under memory pressure. 486 */ 487 if (pgf->pgf_gmondata == NULL) 488 pmcstat_gmon_map_file(pgf); 489 490 assert(pgf->pgf_gmondata != NULL); 491 492 /* 493 * 494 */ 495 496 bucket = (cc[0] - map->ppm_lowpc) / FUNCTION_ALIGNMENT; 497 498 assert(bucket < pgf->pgf_nbuckets); 499 500 if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) { 501 whc = (WIDEHISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 502 sizeof(struct gmonhdr)); 503 504 /* saturating add */ 505 if (whc[bucket] < WIDEHISTCOUNTER_MAX) 506 whc[bucket]++; 507 else /* mark that an overflow occurred */ 508 pgf->pgf_overflow = 1; 509 } else { 510 hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + 511 sizeof(struct gmonhdr)); 512 513 /* saturating add */ 514 if (hc[bucket] < HISTCOUNTER_MAX) 515 hc[bucket]++; 516 else /* mark that an overflow occurred */ 517 pgf->pgf_overflow = 1; 518 } 519 520 pgf->pgf_nsamples++; 521 } 522 523 /* 524 * Shutdown module. 525 */ 526 527 void 528 pmcpl_gmon_shutdown(FILE *mf) 529 { 530 int i; 531 struct pmcstat_gmonfile *pgf; 532 struct pmcstat_image *pi; 533 534 /* 535 * Sync back all gprof flat profile data. 536 */ 537 for (i = 0; i < PMCSTAT_NHASH; i++) { 538 LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) { 539 if (mf) 540 (void) fprintf(mf, " \"%s\" => \"%s\"", 541 pmcstat_string_unintern(pi->pi_execpath), 542 pmcstat_string_unintern( 543 pi->pi_samplename)); 544 545 /* flush gmon.out data to disk */ 546 LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) { 547 pmcstat_gmon_unmap_file(pgf); 548 if (mf) 549 (void) fprintf(mf, " %s/%d", 550 pmcstat_pmcid_to_name( 551 pgf->pgf_pmcid), 552 pgf->pgf_nsamples); 553 if (pgf->pgf_overflow && args.pa_verbosity >= 1) 554 warnx( 555 "WARNING: profile \"%s\" overflowed.", 556 pmcstat_string_unintern( 557 pgf->pgf_name)); 558 } 559 560 if (mf) 561 (void) fprintf(mf, "\n"); 562 } 563 } 564 565 /* 566 * Compute arcs and add these to the gprof files. 567 */ 568 if (args.pa_flags & FLAG_DO_GPROF && args.pa_graphdepth > 1) 569 pmcstat_callgraph_do_gmon_arcs(); 570 } 571