1 1.21 andvar /* $NetBSD: main.c,v 1.21 2021/12/12 22:20:53 andvar Exp $ */ 2 1.1 ad 3 1.1 ad /*- 4 1.16 ad * Copyright (c) 2006, 2007, 2009 The NetBSD Foundation, Inc. 5 1.1 ad * All rights reserved. 6 1.1 ad * 7 1.1 ad * This code is derived from software contributed to The NetBSD Foundation 8 1.1 ad * by Andrew Doran. 9 1.1 ad * 10 1.1 ad * Redistribution and use in source and binary forms, with or without 11 1.1 ad * modification, are permitted provided that the following conditions 12 1.1 ad * are met: 13 1.1 ad * 1. Redistributions of source code must retain the above copyright 14 1.1 ad * notice, this list of conditions and the following disclaimer. 15 1.1 ad * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 ad * notice, this list of conditions and the following disclaimer in the 17 1.1 ad * documentation and/or other materials provided with the distribution. 18 1.1 ad * 19 1.1 ad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 ad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 ad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 ad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 ad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 ad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 ad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 ad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 ad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 ad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 ad * POSSIBILITY OF SUCH DAMAGE. 30 1.1 ad */ 31 1.1 ad 32 1.1 ad #include <sys/cdefs.h> 33 1.1 ad #ifndef lint 34 1.21 andvar __RCSID("$NetBSD: main.c,v 1.21 2021/12/12 22:20:53 andvar Exp $"); 35 1.1 ad #endif /* not lint */ 36 1.1 ad 37 1.1 ad #include <sys/types.h> 38 1.1 ad #include <sys/param.h> 39 1.1 ad #include <sys/time.h> 40 1.1 ad #include <sys/fcntl.h> 41 1.1 ad #include <sys/ioctl.h> 42 1.1 ad #include <sys/wait.h> 43 1.1 ad #include <sys/signal.h> 44 1.1 ad #include <sys/sysctl.h> 45 1.1 ad 46 1.2 ad #include <dev/lockstat.h> 47 1.2 ad 48 1.1 ad #include <stdio.h> 49 1.1 ad #include <stdlib.h> 50 1.1 ad #include <string.h> 51 1.1 ad #include <limits.h> 52 1.1 ad #include <unistd.h> 53 1.1 ad #include <err.h> 54 1.1 ad #include <paths.h> 55 1.1 ad #include <util.h> 56 1.1 ad #include <ctype.h> 57 1.1 ad #include <errno.h> 58 1.9 ad #include <stdbool.h> 59 1.1 ad 60 1.1 ad #include "extern.h" 61 1.1 ad 62 1.1 ad #define _PATH_DEV_LOCKSTAT "/dev/lockstat" 63 1.1 ad 64 1.1 ad #define MILLI 1000.0 65 1.1 ad #define MICRO 1000000.0 66 1.1 ad #define NANO 1000000000.0 67 1.1 ad #define PICO 1000000000000.0 68 1.1 ad 69 1.1 ad TAILQ_HEAD(lock_head, lockstruct); 70 1.1 ad typedef struct lock_head locklist_t; 71 1.1 ad TAILQ_HEAD(buf_head, lsbuf); 72 1.1 ad typedef struct buf_head buflist_t; 73 1.20 ad SLIST_HEAD(bucket, lockstruct); 74 1.20 ad typedef struct bucket bucket_t; 75 1.1 ad 76 1.1 ad typedef struct lockstruct { 77 1.1 ad TAILQ_ENTRY(lockstruct) chain; 78 1.20 ad SLIST_ENTRY(lockstruct) bucket; 79 1.1 ad buflist_t bufs; 80 1.7 ad buflist_t tosort; 81 1.1 ad uintptr_t lock; 82 1.7 ad double time; 83 1.7 ad uint32_t count; 84 1.1 ad u_int flags; 85 1.5 ad u_int nbufs; 86 1.7 ad char name[NAME_SIZE]; 87 1.1 ad } lock_t; 88 1.1 ad 89 1.1 ad typedef struct name { 90 1.1 ad const char *name; 91 1.1 ad int mask; 92 1.1 ad } name_t; 93 1.1 ad 94 1.18 joerg static const name_t locknames[] = { 95 1.1 ad { "adaptive_mutex", LB_ADAPTIVE_MUTEX }, 96 1.1 ad { "spin_mutex", LB_SPIN_MUTEX }, 97 1.7 ad { "rwlock", LB_RWLOCK }, 98 1.6 ad { "kernel_lock", LB_KERNEL_LOCK }, 99 1.13 ad { "preemption", LB_NOPREEMPT }, 100 1.15 ad { "misc", LB_MISC }, 101 1.1 ad { NULL, 0 } 102 1.1 ad }; 103 1.1 ad 104 1.18 joerg static const name_t eventnames[] = { 105 1.1 ad { "spin", LB_SPIN }, 106 1.7 ad { "sleep_exclusive", LB_SLEEP1 }, 107 1.7 ad { "sleep_shared", LB_SLEEP2 }, 108 1.1 ad { NULL, 0 }, 109 1.1 ad }; 110 1.1 ad 111 1.18 joerg static const name_t alltypes[] = { 112 1.1 ad { "Adaptive mutex spin", LB_ADAPTIVE_MUTEX | LB_SPIN }, 113 1.7 ad { "Adaptive mutex sleep", LB_ADAPTIVE_MUTEX | LB_SLEEP1 }, 114 1.1 ad { "Spin mutex spin", LB_SPIN_MUTEX | LB_SPIN }, 115 1.7 ad { "RW lock sleep (writer)", LB_RWLOCK | LB_SLEEP1 }, 116 1.7 ad { "RW lock sleep (reader)", LB_RWLOCK | LB_SLEEP2 }, 117 1.12 ad { "RW lock spin", LB_RWLOCK | LB_SPIN }, 118 1.5 ad { "Kernel lock spin", LB_KERNEL_LOCK | LB_SPIN }, 119 1.13 ad { "Kernel preemption defer", LB_NOPREEMPT | LB_SPIN }, 120 1.15 ad { "Miscellaneous wait", LB_MISC | LB_SPIN }, 121 1.1 ad { NULL, 0 } 122 1.1 ad }; 123 1.1 ad 124 1.18 joerg static const name_t xtypes[] = { 125 1.16 ad { "Spin", LB_SPIN }, 126 1.16 ad { "Sleep (writer)", LB_SLEEP1 }, 127 1.16 ad { "Sleep (reader)", LB_SLEEP2 }, 128 1.16 ad { NULL, 0 } 129 1.16 ad }; 130 1.16 ad 131 1.18 joerg static locklist_t locklist; 132 1.18 joerg static locklist_t freelist; 133 1.18 joerg static locklist_t sortlist; 134 1.20 ad static bucket_t bucket[256]; 135 1.20 ad 136 1.20 ad #define HASH(a) (&bucket[((a) >> 6) & (__arraycount(bucket) - 1)]) 137 1.18 joerg 138 1.18 joerg static lsbuf_t *bufs; 139 1.18 joerg static lsdisable_t ld; 140 1.18 joerg static bool lflag; 141 1.18 joerg static bool fflag; 142 1.18 joerg static int nbufs; 143 1.18 joerg static bool cflag; 144 1.19 yamt static bool dflag; 145 1.18 joerg static bool xflag; 146 1.18 joerg static int lsfd; 147 1.18 joerg static int displayed; 148 1.18 joerg static int bin64; 149 1.18 joerg static double tscale; 150 1.18 joerg static double cscale; 151 1.18 joerg static double cpuscale[sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0])]; 152 1.18 joerg static FILE *outfp; 153 1.18 joerg 154 1.18 joerg static void findsym(findsym_t, char *, uintptr_t *, uintptr_t *, bool); 155 1.18 joerg static void spawn(int, char **); 156 1.18 joerg static void display(int, const char *name); 157 1.18 joerg __dead static void listnames(const name_t *); 158 1.18 joerg static void collapse(bool, bool); 159 1.18 joerg static int matchname(const name_t *, char *); 160 1.18 joerg static void makelists(int, int); 161 1.18 joerg static void nullsig(int); 162 1.18 joerg __dead static void usage(void); 163 1.18 joerg static int ncpu(void); 164 1.18 joerg static lock_t *morelocks(void); 165 1.1 ad 166 1.1 ad int 167 1.1 ad main(int argc, char **argv) 168 1.1 ad { 169 1.17 lukem int eventtype, locktype, ch, nlfd, fd; 170 1.17 lukem size_t i; 171 1.9 ad bool sflag, pflag, mflag, Mflag; 172 1.1 ad const char *nlistf, *outf; 173 1.1 ad char *lockname, *funcname; 174 1.1 ad const name_t *name; 175 1.1 ad lsenable_t le; 176 1.1 ad double ms; 177 1.1 ad char *p; 178 1.1 ad 179 1.1 ad nlistf = NULL; 180 1.1 ad outf = NULL; 181 1.1 ad lockname = NULL; 182 1.1 ad funcname = NULL; 183 1.1 ad eventtype = -1; 184 1.1 ad locktype = -1; 185 1.1 ad nbufs = 0; 186 1.9 ad sflag = false; 187 1.9 ad pflag = false; 188 1.9 ad mflag = false; 189 1.9 ad Mflag = false; 190 1.1 ad 191 1.19 yamt while ((ch = getopt(argc, argv, "E:F:L:MN:T:b:cdeflmo:pstx")) != -1) 192 1.1 ad switch (ch) { 193 1.1 ad case 'E': 194 1.1 ad eventtype = matchname(eventnames, optarg); 195 1.1 ad break; 196 1.1 ad case 'F': 197 1.1 ad funcname = optarg; 198 1.1 ad break; 199 1.1 ad case 'L': 200 1.1 ad lockname = optarg; 201 1.1 ad break; 202 1.1 ad case 'N': 203 1.1 ad nlistf = optarg; 204 1.1 ad break; 205 1.1 ad case 'T': 206 1.1 ad locktype = matchname(locknames, optarg); 207 1.1 ad break; 208 1.1 ad case 'b': 209 1.1 ad nbufs = (int)strtol(optarg, &p, 0); 210 1.1 ad if (!isdigit((u_int)*optarg) || *p != '\0') 211 1.1 ad usage(); 212 1.1 ad break; 213 1.1 ad case 'c': 214 1.9 ad cflag = true; 215 1.1 ad break; 216 1.19 yamt case 'd': 217 1.19 yamt dflag = true; 218 1.19 yamt break; 219 1.1 ad case 'e': 220 1.1 ad listnames(eventnames); 221 1.1 ad break; 222 1.9 ad case 'f': 223 1.9 ad fflag = true; 224 1.9 ad break; 225 1.1 ad case 'l': 226 1.9 ad lflag = true; 227 1.9 ad break; 228 1.9 ad case 'm': 229 1.9 ad mflag = true; 230 1.9 ad break; 231 1.9 ad case 'M': 232 1.9 ad Mflag = true; 233 1.1 ad break; 234 1.1 ad case 'o': 235 1.1 ad outf = optarg; 236 1.1 ad break; 237 1.1 ad case 'p': 238 1.9 ad pflag = true; 239 1.1 ad break; 240 1.1 ad case 's': 241 1.9 ad sflag = true; 242 1.1 ad break; 243 1.1 ad case 't': 244 1.1 ad listnames(locknames); 245 1.1 ad break; 246 1.16 ad case 'x': 247 1.16 ad xflag = true; 248 1.16 ad break; 249 1.1 ad default: 250 1.1 ad usage(); 251 1.1 ad } 252 1.1 ad argc -= optind; 253 1.1 ad argv += optind; 254 1.1 ad 255 1.19 yamt if (*argv == NULL && !dflag) 256 1.1 ad usage(); 257 1.1 ad 258 1.1 ad if (outf) { 259 1.5 ad fd = open(outf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 260 1.5 ad if (fd == -1) 261 1.1 ad err(EXIT_FAILURE, "opening %s", outf); 262 1.1 ad outfp = fdopen(fd, "w"); 263 1.1 ad } else 264 1.1 ad outfp = stdout; 265 1.1 ad 266 1.1 ad /* 267 1.1 ad * Find the name list for resolving symbol names, and load it into 268 1.1 ad * memory. 269 1.1 ad */ 270 1.1 ad if (nlistf == NULL) { 271 1.1 ad nlfd = open(_PATH_KSYMS, O_RDONLY); 272 1.1 ad nlistf = getbootfile(); 273 1.1 ad } else 274 1.1 ad nlfd = -1; 275 1.1 ad if (nlfd == -1) { 276 1.1 ad if ((nlfd = open(nlistf, O_RDONLY)) < 0) 277 1.1 ad err(EXIT_FAILURE, "cannot open " _PATH_KSYMS " or %s", 278 1.1 ad nlistf); 279 1.1 ad } 280 1.1 ad if (loadsym32(nlfd) != 0) { 281 1.1 ad if (loadsym64(nlfd) != 0) 282 1.1 ad errx(EXIT_FAILURE, "unable to load symbol table"); 283 1.1 ad bin64 = 1; 284 1.1 ad } 285 1.1 ad close(nlfd); 286 1.1 ad 287 1.1 ad memset(&le, 0, sizeof(le)); 288 1.1 ad le.le_nbufs = nbufs; 289 1.1 ad 290 1.1 ad /* 291 1.1 ad * Set up initial filtering. 292 1.1 ad */ 293 1.1 ad if (lockname != NULL) { 294 1.7 ad findsym(LOCK_BYNAME, lockname, &le.le_lockstart, 295 1.9 ad &le.le_lockend, true); 296 1.1 ad le.le_flags |= LE_ONE_LOCK; 297 1.1 ad } 298 1.1 ad if (!lflag) 299 1.1 ad le.le_flags |= LE_CALLSITE; 300 1.9 ad if (!fflag) 301 1.9 ad le.le_flags |= LE_LOCK; 302 1.1 ad if (funcname != NULL) { 303 1.1 ad if (lflag) 304 1.1 ad usage(); 305 1.9 ad findsym(FUNC_BYNAME, funcname, &le.le_csstart, &le.le_csend, true); 306 1.1 ad le.le_flags |= LE_ONE_CALLSITE; 307 1.1 ad } 308 1.1 ad le.le_mask = (eventtype & LB_EVENT_MASK) | (locktype & LB_LOCK_MASK); 309 1.1 ad 310 1.1 ad /* 311 1.1 ad * Start tracing. 312 1.1 ad */ 313 1.1 ad if ((lsfd = open(_PATH_DEV_LOCKSTAT, O_RDONLY)) < 0) 314 1.1 ad err(EXIT_FAILURE, "cannot open " _PATH_DEV_LOCKSTAT); 315 1.1 ad if (ioctl(lsfd, IOC_LOCKSTAT_GVERSION, &ch) < 0) 316 1.1 ad err(EXIT_FAILURE, "ioctl"); 317 1.1 ad if (ch != LS_VERSION) 318 1.7 ad errx(EXIT_FAILURE, 319 1.7 ad "incompatible lockstat interface version (%d, kernel %d)", 320 1.7 ad LS_VERSION, ch); 321 1.19 yamt if (dflag) { 322 1.19 yamt goto disable; 323 1.19 yamt } 324 1.1 ad if (ioctl(lsfd, IOC_LOCKSTAT_ENABLE, &le)) 325 1.1 ad err(EXIT_FAILURE, "cannot enable tracing"); 326 1.1 ad 327 1.1 ad /* 328 1.1 ad * Execute the traced program. 329 1.1 ad */ 330 1.1 ad spawn(argc, argv); 331 1.1 ad 332 1.19 yamt disable: 333 1.1 ad /* 334 1.1 ad * Stop tracing, and read the trace buffers from the kernel. 335 1.1 ad */ 336 1.1 ad if (ioctl(lsfd, IOC_LOCKSTAT_DISABLE, &ld) == -1) { 337 1.1 ad if (errno == EOVERFLOW) { 338 1.1 ad warnx("overflowed available kernel trace buffers"); 339 1.1 ad exit(EXIT_FAILURE); 340 1.1 ad } 341 1.1 ad err(EXIT_FAILURE, "cannot disable tracing"); 342 1.1 ad } 343 1.1 ad if ((bufs = malloc(ld.ld_size)) == NULL) 344 1.1 ad err(EXIT_FAILURE, "cannot allocate memory for user buffers"); 345 1.17 lukem if ((size_t)read(lsfd, bufs, ld.ld_size) != ld.ld_size) 346 1.1 ad err(EXIT_FAILURE, "reading from " _PATH_DEV_LOCKSTAT); 347 1.1 ad if (close(lsfd)) 348 1.1 ad err(EXIT_FAILURE, "close(" _PATH_DEV_LOCKSTAT ")"); 349 1.1 ad 350 1.1 ad /* 351 1.7 ad * Figure out how to scale the results. For internal use we convert 352 1.7 ad * all times from CPU frequency based to picoseconds, and values are 353 1.7 ad * eventually displayed in ms. 354 1.1 ad */ 355 1.1 ad for (i = 0; i < sizeof(ld.ld_freq) / sizeof(ld.ld_freq[0]); i++) 356 1.1 ad if (ld.ld_freq[i] != 0) 357 1.1 ad cpuscale[i] = PICO / ld.ld_freq[i]; 358 1.1 ad ms = ld.ld_time.tv_sec * MILLI + ld.ld_time.tv_nsec / MICRO; 359 1.1 ad if (pflag) 360 1.1 ad cscale = 1.0 / ncpu(); 361 1.1 ad else 362 1.1 ad cscale = 1.0; 363 1.1 ad cscale *= (sflag ? MILLI / ms : 1.0); 364 1.1 ad tscale = cscale / NANO; 365 1.1 ad nbufs = (int)(ld.ld_size / sizeof(lsbuf_t)); 366 1.7 ad 367 1.7 ad TAILQ_INIT(&locklist); 368 1.7 ad TAILQ_INIT(&sortlist); 369 1.7 ad TAILQ_INIT(&freelist); 370 1.1 ad 371 1.9 ad if ((mflag | Mflag) != 0) 372 1.9 ad collapse(mflag, Mflag); 373 1.9 ad 374 1.1 ad /* 375 1.1 ad * Display the results. 376 1.1 ad */ 377 1.1 ad fprintf(outfp, "Elapsed time: %.2f seconds.", ms / MILLI); 378 1.1 ad if (sflag || pflag) { 379 1.1 ad fprintf(outfp, " Displaying "); 380 1.1 ad if (pflag) 381 1.1 ad fprintf(outfp, "per-CPU "); 382 1.1 ad if (sflag) 383 1.1 ad fprintf(outfp, "per-second "); 384 1.1 ad fprintf(outfp, "averages."); 385 1.1 ad } 386 1.1 ad putc('\n', outfp); 387 1.1 ad 388 1.16 ad for (name = xflag ? xtypes : alltypes; name->name != NULL; name++) { 389 1.1 ad if (eventtype != -1 && 390 1.1 ad (name->mask & LB_EVENT_MASK) != eventtype) 391 1.1 ad continue; 392 1.1 ad if (locktype != -1 && 393 1.1 ad (name->mask & LB_LOCK_MASK) != locktype) 394 1.1 ad continue; 395 1.1 ad display(name->mask, name->name); 396 1.1 ad } 397 1.1 ad 398 1.1 ad if (displayed == 0) 399 1.1 ad fprintf(outfp, "None of the selected events were recorded.\n"); 400 1.1 ad exit(EXIT_SUCCESS); 401 1.1 ad } 402 1.1 ad 403 1.18 joerg static void 404 1.1 ad usage(void) 405 1.1 ad { 406 1.1 ad 407 1.1 ad fprintf(stderr, 408 1.1 ad "%s: usage:\n" 409 1.1 ad "%s [options] <command>\n\n" 410 1.1 ad "-b nbuf\t\tset number of event buffers to allocate\n" 411 1.1 ad "-c\t\treport percentage of total events by count, not time\n" 412 1.19 yamt "-d\t\tdisable lockstat\n" 413 1.19 yamt "-E event\tdisplay only one type of event\n" 414 1.1 ad "-e\t\tlist event types\n" 415 1.10 wiz "-F func\t\tlimit trace to one function\n" 416 1.9 ad "-f\t\ttrace only by function\n" 417 1.4 wiz "-L lock\t\tlimit trace to one lock (name, or address)\n" 418 1.1 ad "-l\t\ttrace only by lock\n" 419 1.10 wiz "-M\t\tmerge lock addresses within unique objects\n" 420 1.9 ad "-m\t\tmerge call sites within unique functions\n" 421 1.4 wiz "-N nlist\tspecify name list file\n" 422 1.1 ad "-o file\t\tsend output to named file, not stdout\n" 423 1.1 ad "-p\t\tshow average count/time per CPU, not total\n" 424 1.1 ad "-s\t\tshow average count/time per second, not total\n" 425 1.4 wiz "-T type\t\tdisplay only one type of lock\n" 426 1.16 ad "-t\t\tlist lock types\n" 427 1.16 ad "-x\t\tdon't differentiate event types\n", 428 1.1 ad getprogname(), getprogname()); 429 1.1 ad 430 1.1 ad exit(EXIT_FAILURE); 431 1.1 ad } 432 1.1 ad 433 1.18 joerg static void 434 1.1 ad nullsig(int junk) 435 1.1 ad { 436 1.1 ad 437 1.1 ad (void)junk; 438 1.1 ad } 439 1.1 ad 440 1.18 joerg static void 441 1.1 ad listnames(const name_t *name) 442 1.1 ad { 443 1.1 ad 444 1.1 ad for (; name->name != NULL; name++) 445 1.1 ad printf("%s\n", name->name); 446 1.1 ad 447 1.1 ad exit(EXIT_SUCCESS); 448 1.1 ad } 449 1.1 ad 450 1.18 joerg static int 451 1.9 ad matchname(const name_t *name, char *string) 452 1.1 ad { 453 1.9 ad int empty, mask; 454 1.9 ad char *sp; 455 1.9 ad 456 1.9 ad empty = 1; 457 1.9 ad mask = 0; 458 1.9 ad 459 1.9 ad while ((sp = strsep(&string, ",")) != NULL) { 460 1.9 ad if (*sp == '\0') 461 1.9 ad usage(); 462 1.1 ad 463 1.9 ad for (; name->name != NULL; name++) { 464 1.9 ad if (strcasecmp(name->name, sp) == 0) { 465 1.9 ad mask |= name->mask; 466 1.9 ad break; 467 1.9 ad } 468 1.9 ad } 469 1.9 ad if (name->name == NULL) 470 1.9 ad errx(EXIT_FAILURE, "unknown identifier `%s'", sp); 471 1.9 ad empty = 0; 472 1.9 ad } 473 1.9 ad 474 1.9 ad if (empty) 475 1.9 ad usage(); 476 1.1 ad 477 1.9 ad return mask; 478 1.1 ad } 479 1.1 ad 480 1.1 ad /* 481 1.1 ad * Return the number of CPUs in the running system. 482 1.1 ad */ 483 1.18 joerg static int 484 1.1 ad ncpu(void) 485 1.1 ad { 486 1.1 ad int rv, mib[2]; 487 1.1 ad size_t varlen; 488 1.1 ad 489 1.1 ad mib[0] = CTL_HW; 490 1.1 ad mib[1] = HW_NCPU; 491 1.1 ad varlen = sizeof(rv); 492 1.1 ad if (sysctl(mib, 2, &rv, &varlen, NULL, (size_t)0) < 0) 493 1.1 ad rv = 1; 494 1.1 ad 495 1.1 ad return (rv); 496 1.1 ad } 497 1.1 ad 498 1.1 ad /* 499 1.1 ad * Call into the ELF parser and look up a symbol by name or by address. 500 1.1 ad */ 501 1.18 joerg static void 502 1.9 ad findsym(findsym_t find, char *name, uintptr_t *start, uintptr_t *end, bool chg) 503 1.1 ad { 504 1.9 ad uintptr_t tend, sa, ea; 505 1.1 ad char *p; 506 1.1 ad int rv; 507 1.1 ad 508 1.9 ad if (!chg) { 509 1.9 ad sa = *start; 510 1.9 ad start = &sa; 511 1.9 ad end = &ea; 512 1.9 ad } 513 1.9 ad 514 1.1 ad if (end == NULL) 515 1.1 ad end = &tend; 516 1.1 ad 517 1.1 ad if (find == LOCK_BYNAME) { 518 1.1 ad if (isdigit((u_int)name[0])) { 519 1.1 ad *start = (uintptr_t)strtoul(name, &p, 0); 520 1.1 ad if (*p == '\0') 521 1.1 ad return; 522 1.1 ad } 523 1.1 ad } 524 1.1 ad 525 1.1 ad if (bin64) 526 1.1 ad rv = findsym64(find, name, start, end); 527 1.1 ad else 528 1.1 ad rv = findsym32(find, name, start, end); 529 1.1 ad 530 1.1 ad if (find == FUNC_BYNAME || find == LOCK_BYNAME) { 531 1.1 ad if (rv == -1) 532 1.1 ad errx(EXIT_FAILURE, "unable to find symbol `%s'", name); 533 1.1 ad return; 534 1.1 ad } 535 1.1 ad 536 1.1 ad if (rv == -1) 537 1.7 ad snprintf(name, NAME_SIZE, "%016lx", (long)*start); 538 1.1 ad } 539 1.1 ad 540 1.1 ad /* 541 1.1 ad * Fork off the child process and wait for it to complete. We trap SIGINT 542 1.1 ad * so that the caller can use Ctrl-C to stop tracing early and still get 543 1.1 ad * useful results. 544 1.1 ad */ 545 1.18 joerg static void 546 1.1 ad spawn(int argc, char **argv) 547 1.1 ad { 548 1.1 ad pid_t pid; 549 1.1 ad 550 1.1 ad switch (pid = fork()) { 551 1.1 ad case 0: 552 1.1 ad close(lsfd); 553 1.1 ad if (execvp(argv[0], argv) == -1) 554 1.1 ad err(EXIT_FAILURE, "cannot exec"); 555 1.1 ad break; 556 1.1 ad case -1: 557 1.1 ad err(EXIT_FAILURE, "cannot fork to exec"); 558 1.1 ad break; 559 1.1 ad default: 560 1.1 ad signal(SIGINT, nullsig); 561 1.1 ad wait(NULL); 562 1.1 ad signal(SIGINT, SIG_DFL); 563 1.1 ad break; 564 1.1 ad } 565 1.1 ad } 566 1.1 ad 567 1.1 ad /* 568 1.7 ad * Allocate a new block of lock_t structures. 569 1.7 ad */ 570 1.18 joerg static lock_t * 571 1.7 ad morelocks(void) 572 1.7 ad { 573 1.17 lukem const int batch = 32; 574 1.7 ad lock_t *l, *lp, *max; 575 1.7 ad 576 1.7 ad l = (lock_t *)malloc(sizeof(*l) * batch); 577 1.7 ad 578 1.7 ad for (lp = l, max = l + batch; lp < max; lp++) 579 1.7 ad TAILQ_INSERT_TAIL(&freelist, lp, chain); 580 1.7 ad 581 1.7 ad return l; 582 1.7 ad } 583 1.7 ad 584 1.7 ad /* 585 1.9 ad * Collapse addresses from unique objects. 586 1.9 ad */ 587 1.18 joerg static void 588 1.9 ad collapse(bool func, bool lock) 589 1.9 ad { 590 1.9 ad lsbuf_t *lb, *max; 591 1.9 ad 592 1.9 ad for (lb = bufs, max = bufs + nbufs; lb < max; lb++) { 593 1.9 ad if (func && lb->lb_callsite != 0) { 594 1.9 ad findsym(FUNC_BYADDR, NULL, &lb->lb_callsite, NULL, 595 1.9 ad true); 596 1.9 ad } 597 1.9 ad if (lock && lb->lb_lock != 0) { 598 1.9 ad findsym(LOCK_BYADDR, NULL, &lb->lb_lock, NULL, 599 1.9 ad true); 600 1.9 ad } 601 1.9 ad } 602 1.9 ad } 603 1.9 ad 604 1.9 ad /* 605 1.1 ad * From the kernel supplied data, construct two dimensional lists of locks 606 1.7 ad * and event buffers, indexed by lock type and sorted by event type. 607 1.1 ad */ 608 1.18 joerg static void 609 1.7 ad makelists(int mask, int event) 610 1.1 ad { 611 1.1 ad lsbuf_t *lb, *lb2, *max; 612 1.7 ad lock_t *l, *l2; 613 1.20 ad bucket_t *bp; 614 1.7 ad int type; 615 1.20 ad size_t i; 616 1.7 ad 617 1.7 ad /* 618 1.7 ad * Recycle lock_t structures from the last run. 619 1.7 ad */ 620 1.20 ad TAILQ_CONCAT(&freelist, &locklist, chain); 621 1.20 ad for (i = 0; i < __arraycount(bucket); i++) { 622 1.20 ad SLIST_INIT(&bucket[i]); 623 1.7 ad } 624 1.1 ad 625 1.7 ad type = mask & LB_LOCK_MASK; 626 1.1 ad 627 1.1 ad for (lb = bufs, max = bufs + nbufs; lb < max; lb++) { 628 1.16 ad if (!xflag && (lb->lb_flags & LB_LOCK_MASK) != type) 629 1.16 ad continue; 630 1.16 ad if (lb->lb_counts[event] == 0) 631 1.1 ad continue; 632 1.1 ad 633 1.1 ad /* 634 1.21 andvar * Look for a record describing this lock, and allocate a 635 1.1 ad * new one if needed. 636 1.1 ad */ 637 1.20 ad bp = HASH(lb->lb_lock); 638 1.20 ad SLIST_FOREACH(l, bp, bucket) { 639 1.1 ad if (l->lock == lb->lb_lock) 640 1.1 ad break; 641 1.1 ad } 642 1.1 ad if (l == NULL) { 643 1.7 ad if ((l = TAILQ_FIRST(&freelist)) == NULL) 644 1.7 ad l = morelocks(); 645 1.7 ad TAILQ_REMOVE(&freelist, l, chain); 646 1.1 ad l->flags = lb->lb_flags; 647 1.1 ad l->lock = lb->lb_lock; 648 1.5 ad l->nbufs = 0; 649 1.7 ad l->name[0] = '\0'; 650 1.7 ad l->count = 0; 651 1.7 ad l->time = 0; 652 1.7 ad TAILQ_INIT(&l->tosort); 653 1.1 ad TAILQ_INIT(&l->bufs); 654 1.7 ad TAILQ_INSERT_TAIL(&sortlist, l, chain); 655 1.20 ad SLIST_INSERT_HEAD(bp, l, bucket); 656 1.1 ad } 657 1.1 ad 658 1.1 ad /* 659 1.1 ad * Scale the time values per buffer and summarise 660 1.1 ad * times+counts per lock. 661 1.1 ad */ 662 1.7 ad lb->lb_times[event] *= cpuscale[lb->lb_cpu]; 663 1.7 ad l->count += lb->lb_counts[event]; 664 1.7 ad l->time += lb->lb_times[event]; 665 1.1 ad 666 1.1 ad /* 667 1.1 ad * Merge same lock+callsite pairs from multiple CPUs 668 1.1 ad * together. 669 1.1 ad */ 670 1.7 ad TAILQ_FOREACH(lb2, &l->tosort, lb_chain.tailq) { 671 1.1 ad if (lb->lb_callsite == lb2->lb_callsite) 672 1.1 ad break; 673 1.1 ad } 674 1.1 ad if (lb2 != NULL) { 675 1.7 ad lb2->lb_counts[event] += lb->lb_counts[event]; 676 1.7 ad lb2->lb_times[event] += lb->lb_times[event]; 677 1.5 ad } else { 678 1.7 ad TAILQ_INSERT_HEAD(&l->tosort, lb, lb_chain.tailq); 679 1.5 ad l->nbufs++; 680 1.5 ad } 681 1.1 ad } 682 1.1 ad 683 1.7 ad /* 684 1.7 ad * Now sort the lists. 685 1.7 ad */ 686 1.7 ad while ((l = TAILQ_FIRST(&sortlist)) != NULL) { 687 1.7 ad TAILQ_REMOVE(&sortlist, l, chain); 688 1.1 ad 689 1.1 ad /* 690 1.1 ad * Sort the buffers into the per-lock list. 691 1.1 ad */ 692 1.7 ad while ((lb = TAILQ_FIRST(&l->tosort)) != NULL) { 693 1.7 ad TAILQ_REMOVE(&l->tosort, lb, lb_chain.tailq); 694 1.1 ad 695 1.7 ad lb2 = TAILQ_FIRST(&l->bufs); 696 1.1 ad while (lb2 != NULL) { 697 1.1 ad if (cflag) { 698 1.1 ad if (lb->lb_counts[event] > 699 1.1 ad lb2->lb_counts[event]) 700 1.1 ad break; 701 1.1 ad } else if (lb->lb_times[event] > 702 1.1 ad lb2->lb_times[event]) 703 1.1 ad break; 704 1.1 ad lb2 = TAILQ_NEXT(lb2, lb_chain.tailq); 705 1.1 ad } 706 1.1 ad if (lb2 == NULL) 707 1.7 ad TAILQ_INSERT_TAIL(&l->bufs, lb, 708 1.7 ad lb_chain.tailq); 709 1.1 ad else 710 1.1 ad TAILQ_INSERT_BEFORE(lb2, lb, lb_chain.tailq); 711 1.1 ad } 712 1.1 ad 713 1.1 ad /* 714 1.1 ad * Sort this lock into the per-type list, based on the 715 1.1 ad * totals per lock. 716 1.1 ad */ 717 1.7 ad l2 = TAILQ_FIRST(&locklist); 718 1.1 ad while (l2 != NULL) { 719 1.1 ad if (cflag) { 720 1.7 ad if (l->count > l2->count) 721 1.1 ad break; 722 1.7 ad } else if (l->time > l2->time) 723 1.1 ad break; 724 1.1 ad l2 = TAILQ_NEXT(l2, chain); 725 1.1 ad } 726 1.1 ad if (l2 == NULL) 727 1.7 ad TAILQ_INSERT_TAIL(&locklist, l, chain); 728 1.1 ad else 729 1.1 ad TAILQ_INSERT_BEFORE(l2, l, chain); 730 1.1 ad } 731 1.1 ad } 732 1.1 ad 733 1.1 ad /* 734 1.1 ad * Display a summary table for one lock type / event type pair. 735 1.1 ad */ 736 1.18 joerg static void 737 1.1 ad display(int mask, const char *name) 738 1.1 ad { 739 1.1 ad lock_t *l; 740 1.1 ad lsbuf_t *lb; 741 1.1 ad double pcscale, metric; 742 1.7 ad char fname[NAME_SIZE]; 743 1.7 ad int event; 744 1.1 ad 745 1.7 ad event = (mask & LB_EVENT_MASK) - 1; 746 1.7 ad makelists(mask, event); 747 1.7 ad 748 1.7 ad if (TAILQ_EMPTY(&locklist)) 749 1.1 ad return; 750 1.1 ad 751 1.1 ad fprintf(outfp, "\n-- %s\n\n" 752 1.7 ad "Total%% Count Time/ms Lock Caller\n" 753 1.5 ad "------ ------- --------- ---------------------- ------------------------------\n", 754 1.1 ad name); 755 1.1 ad 756 1.1 ad /* 757 1.1 ad * Sum up all events for this type of lock + event. 758 1.1 ad */ 759 1.1 ad pcscale = 0; 760 1.7 ad TAILQ_FOREACH(l, &locklist, chain) { 761 1.1 ad if (cflag) 762 1.7 ad pcscale += l->count; 763 1.1 ad else 764 1.7 ad pcscale += l->time; 765 1.1 ad displayed++; 766 1.1 ad } 767 1.1 ad if (pcscale == 0) 768 1.1 ad pcscale = 100; 769 1.1 ad else 770 1.1 ad pcscale = (100.0 / pcscale); 771 1.1 ad 772 1.1 ad /* 773 1.1 ad * For each lock, print a summary total, followed by a breakdown by 774 1.1 ad * caller. 775 1.1 ad */ 776 1.7 ad TAILQ_FOREACH(l, &locklist, chain) { 777 1.1 ad if (cflag) 778 1.7 ad metric = l->count; 779 1.1 ad else 780 1.7 ad metric = l->time; 781 1.1 ad metric *= pcscale; 782 1.1 ad 783 1.7 ad if (l->name[0] == '\0') 784 1.9 ad findsym(LOCK_BYADDR, l->name, &l->lock, NULL, false); 785 1.1 ad 786 1.6 ad if (lflag || l->nbufs > 1) 787 1.7 ad fprintf(outfp, "%6.2f %7d %9.2f %-22s <all>\n", 788 1.7 ad metric, (int)(l->count * cscale), 789 1.7 ad l->time * tscale, l->name); 790 1.1 ad 791 1.1 ad if (lflag) 792 1.1 ad continue; 793 1.1 ad 794 1.1 ad TAILQ_FOREACH(lb, &l->bufs, lb_chain.tailq) { 795 1.1 ad if (cflag) 796 1.1 ad metric = lb->lb_counts[event]; 797 1.1 ad else 798 1.1 ad metric = lb->lb_times[event]; 799 1.1 ad metric *= pcscale; 800 1.1 ad 801 1.9 ad findsym(FUNC_BYADDR, fname, &lb->lb_callsite, NULL, 802 1.9 ad false); 803 1.7 ad fprintf(outfp, "%6.2f %7d %9.2f %-22s %s\n", 804 1.7 ad metric, (int)(lb->lb_counts[event] * cscale), 805 1.7 ad lb->lb_times[event] * tscale, l->name, fname); 806 1.1 ad } 807 1.1 ad } 808 1.1 ad } 809