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