dtrace.c revision 250574
1178476Sjb/* 2178476Sjb * CDDL HEADER START 3178476Sjb * 4178476Sjb * The contents of this file are subject to the terms of the 5178476Sjb * Common Development and Distribution License (the "License"). 6178476Sjb * You may not use this file except in compliance with the License. 7178476Sjb * 8178476Sjb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9178476Sjb * or http://www.opensolaris.org/os/licensing. 10178476Sjb * See the License for the specific language governing permissions 11178476Sjb * and limitations under the License. 12178476Sjb * 13178476Sjb * When distributing Covered Code, include this CDDL HEADER in each 14178476Sjb * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15178476Sjb * If applicable, add the following below this CDDL HEADER, with the 16178476Sjb * fields enclosed by brackets "[]" replaced with your own identifying 17178476Sjb * information: Portions Copyright [yyyy] [name of copyright owner] 18178476Sjb * 19178476Sjb * CDDL HEADER END 20178476Sjb */ 21178476Sjb 22178476Sjb/* 23178476Sjb * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24178476Sjb * Use is subject to license terms. 25178476Sjb */ 26250574Smarkj/* 27250574Smarkj * Copyright (c) 2012 by Delphix. All rights reserved. 28250574Smarkj */ 29178476Sjb 30178476Sjb#include <sys/types.h> 31178476Sjb#include <sys/stat.h> 32178476Sjb#include <sys/wait.h> 33178476Sjb 34178476Sjb#include <dtrace.h> 35178476Sjb#include <stdlib.h> 36178476Sjb#include <stdarg.h> 37178476Sjb#include <stdio.h> 38178537Sjb#include <string.h> 39178476Sjb#include <strings.h> 40178476Sjb#include <unistd.h> 41178476Sjb#include <limits.h> 42178476Sjb#include <fcntl.h> 43178476Sjb#include <errno.h> 44178476Sjb#include <signal.h> 45178537Sjb#if defined(sun) 46178476Sjb#include <alloca.h> 47178537Sjb#endif 48178476Sjb#include <libgen.h> 49178537Sjb#if defined(sun) 50178476Sjb#include <libproc.h> 51178537Sjb#endif 52178476Sjb 53178476Sjbtypedef struct dtrace_cmd { 54178476Sjb void (*dc_func)(struct dtrace_cmd *); /* function to compile arg */ 55178476Sjb dtrace_probespec_t dc_spec; /* probe specifier context */ 56178476Sjb char *dc_arg; /* argument from main argv */ 57178476Sjb const char *dc_name; /* name for error messages */ 58178476Sjb const char *dc_desc; /* desc for error messages */ 59178476Sjb dtrace_prog_t *dc_prog; /* program compiled from arg */ 60178476Sjb char dc_ofile[PATH_MAX]; /* derived output file name */ 61178476Sjb} dtrace_cmd_t; 62178476Sjb 63178476Sjb#define DMODE_VERS 0 /* display version information and exit (-V) */ 64178476Sjb#define DMODE_EXEC 1 /* compile program for enabling (-a/e/E) */ 65178476Sjb#define DMODE_ANON 2 /* compile program for anonymous tracing (-A) */ 66178476Sjb#define DMODE_LINK 3 /* compile program for linking with ELF (-G) */ 67178476Sjb#define DMODE_LIST 4 /* compile program and list probes (-l) */ 68178476Sjb#define DMODE_HEADER 5 /* compile program for headergen (-h) */ 69178476Sjb 70178476Sjb#define E_SUCCESS 0 71178476Sjb#define E_ERROR 1 72178476Sjb#define E_USAGE 2 73178476Sjb 74178476Sjbstatic const char DTRACE_OPTSTR[] = 75178476Sjb "3:6:aAb:Bc:CD:ef:FGhHi:I:lL:m:n:o:p:P:qs:SU:vVwx:X:Z"; 76178476Sjb 77178476Sjbstatic char **g_argv; 78178476Sjbstatic int g_argc; 79178476Sjbstatic char **g_objv; 80178476Sjbstatic int g_objc; 81178476Sjbstatic dtrace_cmd_t *g_cmdv; 82178476Sjbstatic int g_cmdc; 83178476Sjbstatic struct ps_prochandle **g_psv; 84178476Sjbstatic int g_psc; 85178476Sjbstatic int g_pslive; 86178476Sjbstatic char *g_pname; 87178476Sjbstatic int g_quiet; 88178476Sjbstatic int g_flowindent; 89178476Sjbstatic int g_intr; 90178476Sjbstatic int g_impatient; 91178476Sjbstatic int g_newline; 92178476Sjbstatic int g_total; 93178476Sjbstatic int g_cflags; 94178476Sjbstatic int g_oflags; 95178476Sjbstatic int g_verbose; 96178476Sjbstatic int g_exec = 1; 97178476Sjbstatic int g_mode = DMODE_EXEC; 98178476Sjbstatic int g_status = E_SUCCESS; 99178476Sjbstatic int g_grabanon = 0; 100178476Sjbstatic const char *g_ofile = NULL; 101178537Sjbstatic FILE *g_ofp; 102178476Sjbstatic dtrace_hdl_t *g_dtp; 103178537Sjb#if defined(sun) 104178476Sjbstatic char *g_etcfile = "/etc/system"; 105178476Sjbstatic const char *g_etcbegin = "* vvvv Added by DTrace"; 106178476Sjbstatic const char *g_etcend = "* ^^^^ Added by DTrace"; 107178476Sjb 108178476Sjbstatic const char *g_etc[] = { 109178476Sjb"*", 110178476Sjb"* The following forceload directives were added by dtrace(1M) to allow for", 111178476Sjb"* tracing during boot. If these directives are removed, the system will", 112178476Sjb"* continue to function, but tracing will not occur during boot as desired.", 113178476Sjb"* To remove these directives (and this block comment) automatically, run", 114178476Sjb"* \"dtrace -A\" without additional arguments. See the \"Anonymous Tracing\"", 115178476Sjb"* chapter of the Solaris Dynamic Tracing Guide for details.", 116178476Sjb"*", 117178476SjbNULL }; 118178537Sjb#endif 119178476Sjb 120178476Sjbstatic int 121178476Sjbusage(FILE *fp) 122178476Sjb{ 123178476Sjb static const char predact[] = "[[ predicate ] action ]"; 124178476Sjb 125178476Sjb (void) fprintf(fp, "Usage: %s [-32|-64] [-aACeFGhHlqSvVwZ] " 126178476Sjb "[-b bufsz] [-c cmd] [-D name[=def]]\n\t[-I path] [-L path] " 127178476Sjb "[-o output] [-p pid] [-s script] [-U name]\n\t" 128178476Sjb "[-x opt[=val]] [-X a|c|s|t]\n\n" 129178476Sjb "\t[-P provider %s]\n" 130178476Sjb "\t[-m [ provider: ] module %s]\n" 131178476Sjb "\t[-f [[ provider: ] module: ] func %s]\n" 132178476Sjb "\t[-n [[[ provider: ] module: ] func: ] name %s]\n" 133178476Sjb "\t[-i probe-id %s] [ args ... ]\n\n", g_pname, 134178476Sjb predact, predact, predact, predact, predact); 135178476Sjb 136178476Sjb (void) fprintf(fp, "\tpredicate -> '/' D-expression '/'\n"); 137178476Sjb (void) fprintf(fp, "\t action -> '{' D-statements '}'\n"); 138178476Sjb 139178476Sjb (void) fprintf(fp, "\n" 140178476Sjb "\t-32 generate 32-bit D programs and ELF files\n" 141178476Sjb "\t-64 generate 64-bit D programs and ELF files\n\n" 142178476Sjb "\t-a claim anonymous tracing state\n" 143178476Sjb "\t-A generate driver.conf(4) directives for anonymous tracing\n" 144178476Sjb "\t-b set trace buffer size\n" 145178476Sjb "\t-c run specified command and exit upon its completion\n" 146178476Sjb "\t-C run cpp(1) preprocessor on script files\n" 147178476Sjb "\t-D define symbol when invoking preprocessor\n" 148178476Sjb "\t-e exit after compiling request but prior to enabling probes\n" 149178476Sjb "\t-f enable or list probes matching the specified function name\n" 150178476Sjb "\t-F coalesce trace output by function\n" 151178476Sjb "\t-G generate an ELF file containing embedded dtrace program\n" 152178476Sjb "\t-h generate a header file with definitions for static probes\n" 153178476Sjb "\t-H print included files when invoking preprocessor\n" 154178476Sjb "\t-i enable or list probes matching the specified probe id\n" 155178476Sjb "\t-I add include directory to preprocessor search path\n" 156178476Sjb "\t-l list probes matching specified criteria\n" 157178476Sjb "\t-L add library directory to library search path\n" 158178476Sjb "\t-m enable or list probes matching the specified module name\n" 159178476Sjb "\t-n enable or list probes matching the specified probe name\n" 160178476Sjb "\t-o set output file\n" 161178476Sjb "\t-p grab specified process-ID and cache its symbol tables\n" 162178476Sjb "\t-P enable or list probes matching the specified provider name\n" 163178476Sjb "\t-q set quiet mode (only output explicitly traced data)\n" 164178476Sjb "\t-s enable or list probes according to the specified D script\n" 165178476Sjb "\t-S print D compiler intermediate code\n" 166178476Sjb "\t-U undefine symbol when invoking preprocessor\n" 167178476Sjb "\t-v set verbose mode (report stability attributes, arguments)\n" 168178476Sjb "\t-V report DTrace API version\n" 169178476Sjb "\t-w permit destructive actions\n" 170178476Sjb "\t-x enable or modify compiler and tracing options\n" 171178476Sjb "\t-X specify ISO C conformance settings for preprocessor\n" 172178476Sjb "\t-Z permit probe descriptions that match zero probes\n"); 173178476Sjb 174178476Sjb return (E_USAGE); 175178476Sjb} 176178476Sjb 177178476Sjbstatic void 178178476Sjbverror(const char *fmt, va_list ap) 179178476Sjb{ 180178476Sjb int error = errno; 181178476Sjb 182178476Sjb (void) fprintf(stderr, "%s: ", g_pname); 183178476Sjb (void) vfprintf(stderr, fmt, ap); 184178476Sjb 185178476Sjb if (fmt[strlen(fmt) - 1] != '\n') 186178476Sjb (void) fprintf(stderr, ": %s\n", strerror(error)); 187178476Sjb} 188178476Sjb 189178476Sjb/*PRINTFLIKE1*/ 190178476Sjbstatic void 191178476Sjbfatal(const char *fmt, ...) 192178476Sjb{ 193178476Sjb va_list ap; 194178476Sjb 195178476Sjb va_start(ap, fmt); 196178476Sjb verror(fmt, ap); 197178476Sjb va_end(ap); 198178476Sjb 199247048Sgibbs /* 200247048Sgibbs * Close the DTrace handle to ensure that any controlled processes are 201247048Sgibbs * correctly restored and continued. 202247048Sgibbs */ 203247048Sgibbs if (g_dtp) 204247048Sgibbs dtrace_close(g_dtp); 205247048Sgibbs 206178476Sjb exit(E_ERROR); 207178476Sjb} 208178476Sjb 209178476Sjb/*PRINTFLIKE1*/ 210178476Sjbstatic void 211178476Sjbdfatal(const char *fmt, ...) 212178476Sjb{ 213178537Sjb#if !defined(sun) && defined(NEED_ERRLOC) 214178537Sjb char *p_errfile = NULL; 215178537Sjb int errline = 0; 216178537Sjb#endif 217178476Sjb va_list ap; 218178476Sjb 219178476Sjb va_start(ap, fmt); 220178476Sjb 221178476Sjb (void) fprintf(stderr, "%s: ", g_pname); 222178476Sjb if (fmt != NULL) 223178476Sjb (void) vfprintf(stderr, fmt, ap); 224178476Sjb 225178476Sjb va_end(ap); 226178476Sjb 227178476Sjb if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') { 228178476Sjb (void) fprintf(stderr, ": %s\n", 229178476Sjb dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); 230178476Sjb } else if (fmt == NULL) { 231178476Sjb (void) fprintf(stderr, "%s\n", 232178476Sjb dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); 233178476Sjb } 234178537Sjb#if !defined(sun) && defined(NEED_ERRLOC) 235178537Sjb dt_get_errloc(g_dtp, &p_errfile, &errline); 236178537Sjb if (p_errfile != NULL) 237178537Sjb printf("File '%s', line %d\n", p_errfile, errline); 238178537Sjb#endif 239178476Sjb 240178476Sjb /* 241178476Sjb * Close the DTrace handle to ensure that any controlled processes are 242178476Sjb * correctly restored and continued. 243178476Sjb */ 244178476Sjb dtrace_close(g_dtp); 245178476Sjb 246178476Sjb exit(E_ERROR); 247178476Sjb} 248178476Sjb 249178476Sjb/*PRINTFLIKE1*/ 250178476Sjbstatic void 251178476Sjberror(const char *fmt, ...) 252178476Sjb{ 253178476Sjb va_list ap; 254178476Sjb 255178476Sjb va_start(ap, fmt); 256178476Sjb verror(fmt, ap); 257178476Sjb va_end(ap); 258178476Sjb} 259178476Sjb 260178476Sjb/*PRINTFLIKE1*/ 261178476Sjbstatic void 262178476Sjbnotice(const char *fmt, ...) 263178476Sjb{ 264178476Sjb va_list ap; 265178476Sjb 266178476Sjb if (g_quiet) 267178476Sjb return; /* -q or quiet pragma suppresses notice()s */ 268178476Sjb 269178476Sjb va_start(ap, fmt); 270178476Sjb verror(fmt, ap); 271178476Sjb va_end(ap); 272178476Sjb} 273178476Sjb 274178476Sjb/*PRINTFLIKE1*/ 275178476Sjbstatic void 276178476Sjboprintf(const char *fmt, ...) 277178476Sjb{ 278178476Sjb va_list ap; 279178476Sjb int n; 280178476Sjb 281178476Sjb if (g_ofp == NULL) 282178476Sjb return; 283178476Sjb 284178476Sjb va_start(ap, fmt); 285178476Sjb n = vfprintf(g_ofp, fmt, ap); 286178476Sjb va_end(ap); 287178476Sjb 288178476Sjb if (n < 0) { 289178476Sjb if (errno != EINTR) { 290178476Sjb fatal("failed to write to %s", 291178476Sjb g_ofile ? g_ofile : "<stdout>"); 292178476Sjb } 293178476Sjb clearerr(g_ofp); 294178476Sjb } 295178476Sjb} 296178476Sjb 297178476Sjbstatic char ** 298178476Sjbmake_argv(char *s) 299178476Sjb{ 300178476Sjb const char *ws = "\f\n\r\t\v "; 301178476Sjb char **argv = malloc(sizeof (char *) * (strlen(s) / 2 + 1)); 302178476Sjb int argc = 0; 303178476Sjb char *p = s; 304178476Sjb 305178476Sjb if (argv == NULL) 306178476Sjb return (NULL); 307178476Sjb 308178476Sjb for (p = strtok(s, ws); p != NULL; p = strtok(NULL, ws)) 309178476Sjb argv[argc++] = p; 310178476Sjb 311178476Sjb if (argc == 0) 312178476Sjb argv[argc++] = s; 313178476Sjb 314178476Sjb argv[argc] = NULL; 315178476Sjb return (argv); 316178476Sjb} 317178476Sjb 318178476Sjbstatic void 319178476Sjbdof_prune(const char *fname) 320178476Sjb{ 321178476Sjb struct stat sbuf; 322178476Sjb size_t sz, i, j, mark, len; 323178476Sjb char *buf; 324178476Sjb int msg = 0, fd; 325178476Sjb 326178476Sjb if ((fd = open(fname, O_RDONLY)) == -1) { 327178476Sjb /* 328178476Sjb * This is okay only if the file doesn't exist at all. 329178476Sjb */ 330178476Sjb if (errno != ENOENT) 331178476Sjb fatal("failed to open %s", fname); 332178476Sjb return; 333178476Sjb } 334178476Sjb 335178476Sjb if (fstat(fd, &sbuf) == -1) 336178476Sjb fatal("failed to fstat %s", fname); 337178476Sjb 338178476Sjb if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL) 339178476Sjb fatal("failed to allocate memory for %s", fname); 340178476Sjb 341178476Sjb if (read(fd, buf, sz) != sz) 342178476Sjb fatal("failed to read %s", fname); 343178476Sjb 344178476Sjb buf[sz] = '\0'; 345178476Sjb (void) close(fd); 346178476Sjb 347178476Sjb if ((fd = open(fname, O_WRONLY | O_TRUNC)) == -1) 348178476Sjb fatal("failed to open %s for writing", fname); 349178476Sjb 350178476Sjb len = strlen("dof-data-"); 351178476Sjb 352178476Sjb for (mark = 0, i = 0; i < sz; i++) { 353178476Sjb if (strncmp(&buf[i], "dof-data-", len) != 0) 354178476Sjb continue; 355178476Sjb 356178476Sjb /* 357178476Sjb * This is only a match if it's in the 0th column. 358178476Sjb */ 359178476Sjb if (i != 0 && buf[i - 1] != '\n') 360178476Sjb continue; 361178476Sjb 362178476Sjb if (msg++ == 0) { 363178476Sjb error("cleaned up old anonymous " 364178476Sjb "enabling in %s\n", fname); 365178476Sjb } 366178476Sjb 367178476Sjb /* 368178476Sjb * We have a match. First write out our data up until now. 369178476Sjb */ 370178476Sjb if (i != mark) { 371178476Sjb if (write(fd, &buf[mark], i - mark) != i - mark) 372178476Sjb fatal("failed to write to %s", fname); 373178476Sjb } 374178476Sjb 375178476Sjb /* 376178476Sjb * Now scan forward until we scan past a newline. 377178476Sjb */ 378178476Sjb for (j = i; j < sz && buf[j] != '\n'; j++) 379178476Sjb continue; 380178476Sjb 381178476Sjb /* 382178476Sjb * Reset our mark. 383178476Sjb */ 384178476Sjb if ((mark = j + 1) >= sz) 385178476Sjb break; 386178476Sjb 387178476Sjb i = j; 388178476Sjb } 389178476Sjb 390178476Sjb if (mark < sz) { 391178476Sjb if (write(fd, &buf[mark], sz - mark) != sz - mark) 392178476Sjb fatal("failed to write to %s", fname); 393178476Sjb } 394178476Sjb 395178476Sjb (void) close(fd); 396178476Sjb free(buf); 397178476Sjb} 398178476Sjb 399178537Sjb#if defined(sun) 400178476Sjbstatic void 401178476Sjbetcsystem_prune(void) 402178476Sjb{ 403178476Sjb struct stat sbuf; 404178476Sjb size_t sz; 405178476Sjb char *buf, *start, *end; 406178476Sjb int fd; 407178476Sjb char *fname = g_etcfile, *tmpname; 408178476Sjb 409178476Sjb if ((fd = open(fname, O_RDONLY)) == -1) 410178476Sjb fatal("failed to open %s", fname); 411178476Sjb 412178476Sjb if (fstat(fd, &sbuf) == -1) 413178476Sjb fatal("failed to fstat %s", fname); 414178476Sjb 415178476Sjb if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL) 416178476Sjb fatal("failed to allocate memory for %s", fname); 417178476Sjb 418178476Sjb if (read(fd, buf, sz) != sz) 419178476Sjb fatal("failed to read %s", fname); 420178476Sjb 421178476Sjb buf[sz] = '\0'; 422178476Sjb (void) close(fd); 423178476Sjb 424178476Sjb if ((start = strstr(buf, g_etcbegin)) == NULL) 425178476Sjb goto out; 426178476Sjb 427178476Sjb if (strlen(buf) != sz) { 428178476Sjb fatal("embedded nul byte in %s; manual repair of %s " 429178476Sjb "required\n", fname, fname); 430178476Sjb } 431178476Sjb 432178476Sjb if (strstr(start + 1, g_etcbegin) != NULL) { 433178476Sjb fatal("multiple start sentinels in %s; manual repair of %s " 434178476Sjb "required\n", fname, fname); 435178476Sjb } 436178476Sjb 437178476Sjb if ((end = strstr(buf, g_etcend)) == NULL) { 438178476Sjb fatal("missing end sentinel in %s; manual repair of %s " 439178476Sjb "required\n", fname, fname); 440178476Sjb } 441178476Sjb 442178476Sjb if (start > end) { 443178476Sjb fatal("end sentinel preceeds start sentinel in %s; manual " 444178476Sjb "repair of %s required\n", fname, fname); 445178476Sjb } 446178476Sjb 447178476Sjb end += strlen(g_etcend) + 1; 448178476Sjb bcopy(end, start, strlen(end) + 1); 449178476Sjb 450178476Sjb tmpname = alloca(sz = strlen(fname) + 80); 451178476Sjb (void) snprintf(tmpname, sz, "%s.dtrace.%d", fname, getpid()); 452178476Sjb 453178476Sjb if ((fd = open(tmpname, 454178476Sjb O_WRONLY | O_CREAT | O_EXCL, sbuf.st_mode)) == -1) 455178476Sjb fatal("failed to create %s", tmpname); 456178476Sjb 457178476Sjb if (write(fd, buf, strlen(buf)) < strlen(buf)) { 458178476Sjb (void) unlink(tmpname); 459178476Sjb fatal("failed to write to %s", tmpname); 460178476Sjb } 461178476Sjb 462178476Sjb (void) close(fd); 463178476Sjb 464178476Sjb if (chown(tmpname, sbuf.st_uid, sbuf.st_gid) != 0) { 465178476Sjb (void) unlink(tmpname); 466178476Sjb fatal("failed to chown(2) %s to uid %d, gid %d", tmpname, 467178476Sjb (int)sbuf.st_uid, (int)sbuf.st_gid); 468178476Sjb } 469178476Sjb 470178476Sjb if (rename(tmpname, fname) == -1) 471178476Sjb fatal("rename of %s to %s failed", tmpname, fname); 472178476Sjb 473178476Sjb error("cleaned up forceload directives in %s\n", fname); 474178476Sjbout: 475178476Sjb free(buf); 476178476Sjb} 477178476Sjb 478178476Sjbstatic void 479178476Sjbetcsystem_add(void) 480178476Sjb{ 481178476Sjb const char *mods[20]; 482178476Sjb int nmods, line; 483178476Sjb 484178476Sjb if ((g_ofp = fopen(g_ofile = g_etcfile, "a")) == NULL) 485178476Sjb fatal("failed to open output file '%s'", g_ofile); 486178476Sjb 487178476Sjb oprintf("%s\n", g_etcbegin); 488178476Sjb 489178476Sjb for (line = 0; g_etc[line] != NULL; line++) 490178476Sjb oprintf("%s\n", g_etc[line]); 491178476Sjb 492178476Sjb nmods = dtrace_provider_modules(g_dtp, mods, 493178476Sjb sizeof (mods) / sizeof (char *) - 1); 494178476Sjb 495178476Sjb if (nmods >= sizeof (mods) / sizeof (char *)) 496178476Sjb fatal("unexpectedly large number of modules!"); 497178476Sjb 498178476Sjb mods[nmods++] = "dtrace"; 499178476Sjb 500178476Sjb for (line = 0; line < nmods; line++) 501178476Sjb oprintf("forceload: drv/%s\n", mods[line]); 502178476Sjb 503178476Sjb oprintf("%s\n", g_etcend); 504178476Sjb 505178476Sjb if (fclose(g_ofp) == EOF) 506178476Sjb fatal("failed to close output file '%s'", g_ofile); 507178476Sjb 508178476Sjb error("added forceload directives to %s\n", g_ofile); 509178476Sjb} 510178537Sjb#endif 511178476Sjb 512178476Sjbstatic void 513178476Sjbprint_probe_info(const dtrace_probeinfo_t *p) 514178476Sjb{ 515178476Sjb char buf[BUFSIZ]; 516178476Sjb int i; 517178476Sjb 518178476Sjb oprintf("\n\tProbe Description Attributes\n"); 519178476Sjb 520178476Sjb oprintf("\t\tIdentifier Names: %s\n", 521178476Sjb dtrace_stability_name(p->dtp_attr.dtat_name)); 522178476Sjb oprintf("\t\tData Semantics: %s\n", 523178476Sjb dtrace_stability_name(p->dtp_attr.dtat_data)); 524178476Sjb oprintf("\t\tDependency Class: %s\n", 525178476Sjb dtrace_class_name(p->dtp_attr.dtat_class)); 526178476Sjb 527178476Sjb oprintf("\n\tArgument Attributes\n"); 528178476Sjb 529178476Sjb oprintf("\t\tIdentifier Names: %s\n", 530178476Sjb dtrace_stability_name(p->dtp_arga.dtat_name)); 531178476Sjb oprintf("\t\tData Semantics: %s\n", 532178476Sjb dtrace_stability_name(p->dtp_arga.dtat_data)); 533178476Sjb oprintf("\t\tDependency Class: %s\n", 534178476Sjb dtrace_class_name(p->dtp_arga.dtat_class)); 535178476Sjb 536178476Sjb oprintf("\n\tArgument Types\n"); 537178476Sjb 538178476Sjb for (i = 0; i < p->dtp_argc; i++) { 539178476Sjb if (ctf_type_name(p->dtp_argv[i].dtt_ctfp, 540178476Sjb p->dtp_argv[i].dtt_type, buf, sizeof (buf)) == NULL) 541178476Sjb (void) strlcpy(buf, "(unknown)", sizeof (buf)); 542178476Sjb oprintf("\t\targs[%d]: %s\n", i, buf); 543178476Sjb } 544178476Sjb 545178476Sjb if (p->dtp_argc == 0) 546178476Sjb oprintf("\t\tNone\n"); 547178476Sjb 548178476Sjb oprintf("\n"); 549178476Sjb} 550178476Sjb 551178476Sjb/*ARGSUSED*/ 552178476Sjbstatic int 553178476Sjbinfo_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, 554178476Sjb dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last) 555178476Sjb{ 556178476Sjb dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; 557178476Sjb dtrace_probedesc_t *pdp = &edp->dted_probe; 558178476Sjb dtrace_probeinfo_t p; 559178476Sjb 560178476Sjb if (edp == *last) 561178476Sjb return (0); 562178476Sjb 563178476Sjb oprintf("\n%s:%s:%s:%s\n", 564178476Sjb pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); 565178476Sjb 566178476Sjb if (dtrace_probe_info(dtp, pdp, &p) == 0) 567178476Sjb print_probe_info(&p); 568178476Sjb 569178476Sjb *last = edp; 570178476Sjb return (0); 571178476Sjb} 572178476Sjb 573178476Sjb/* 574178476Sjb * Execute the specified program by enabling the corresponding instrumentation. 575178476Sjb * If -e has been specified, we get the program info but do not enable it. If 576178476Sjb * -v has been specified, we print a stability report for the program. 577178476Sjb */ 578178476Sjbstatic void 579178476Sjbexec_prog(const dtrace_cmd_t *dcp) 580178476Sjb{ 581178476Sjb dtrace_ecbdesc_t *last = NULL; 582178476Sjb dtrace_proginfo_t dpi; 583178476Sjb 584178476Sjb if (!g_exec) { 585178476Sjb dtrace_program_info(g_dtp, dcp->dc_prog, &dpi); 586178476Sjb } else if (dtrace_program_exec(g_dtp, dcp->dc_prog, &dpi) == -1) { 587178476Sjb dfatal("failed to enable '%s'", dcp->dc_name); 588178476Sjb } else { 589178476Sjb notice("%s '%s' matched %u probe%s\n", 590178476Sjb dcp->dc_desc, dcp->dc_name, 591178476Sjb dpi.dpi_matches, dpi.dpi_matches == 1 ? "" : "s"); 592178476Sjb } 593178476Sjb 594178476Sjb if (g_verbose) { 595178476Sjb oprintf("\nStability attributes for %s %s:\n", 596178476Sjb dcp->dc_desc, dcp->dc_name); 597178476Sjb 598178476Sjb oprintf("\n\tMinimum Probe Description Attributes\n"); 599178476Sjb oprintf("\t\tIdentifier Names: %s\n", 600178476Sjb dtrace_stability_name(dpi.dpi_descattr.dtat_name)); 601178476Sjb oprintf("\t\tData Semantics: %s\n", 602178476Sjb dtrace_stability_name(dpi.dpi_descattr.dtat_data)); 603178476Sjb oprintf("\t\tDependency Class: %s\n", 604178476Sjb dtrace_class_name(dpi.dpi_descattr.dtat_class)); 605178476Sjb 606178476Sjb oprintf("\n\tMinimum Statement Attributes\n"); 607178476Sjb 608178476Sjb oprintf("\t\tIdentifier Names: %s\n", 609178476Sjb dtrace_stability_name(dpi.dpi_stmtattr.dtat_name)); 610178476Sjb oprintf("\t\tData Semantics: %s\n", 611178476Sjb dtrace_stability_name(dpi.dpi_stmtattr.dtat_data)); 612178476Sjb oprintf("\t\tDependency Class: %s\n", 613178476Sjb dtrace_class_name(dpi.dpi_stmtattr.dtat_class)); 614178476Sjb 615178476Sjb if (!g_exec) { 616178476Sjb (void) dtrace_stmt_iter(g_dtp, dcp->dc_prog, 617178476Sjb (dtrace_stmt_f *)info_stmt, &last); 618178476Sjb } else 619178476Sjb oprintf("\n"); 620178476Sjb } 621178476Sjb 622178476Sjb g_total += dpi.dpi_matches; 623178476Sjb} 624178476Sjb 625178476Sjb/* 626178476Sjb * Print out the specified DOF buffer as a set of ASCII bytes appropriate for 627178476Sjb * storing in a driver.conf(4) file associated with the dtrace driver. 628178476Sjb */ 629178476Sjbstatic void 630178476Sjbanon_prog(const dtrace_cmd_t *dcp, dof_hdr_t *dof, int n) 631178476Sjb{ 632178476Sjb const uchar_t *p, *q; 633178476Sjb 634178476Sjb if (dof == NULL) 635178476Sjb dfatal("failed to create DOF image for '%s'", dcp->dc_name); 636178476Sjb 637178476Sjb p = (uchar_t *)dof; 638178476Sjb q = p + dof->dofh_loadsz; 639178476Sjb 640178537Sjb#if defined(sun) 641178476Sjb oprintf("dof-data-%d=0x%x", n, *p++); 642178476Sjb 643178476Sjb while (p < q) 644178476Sjb oprintf(",0x%x", *p++); 645178476Sjb 646178476Sjb oprintf(";\n"); 647178537Sjb#else 648178537Sjb /* 649178537Sjb * On FreeBSD, the DOF data is handled as a kernel environment (kenv) 650178537Sjb * string. We use two hex characters per DOF byte. 651178537Sjb */ 652178537Sjb oprintf("dof-data-%d=%02x", n, *p++); 653178537Sjb 654178537Sjb while (p < q) 655178537Sjb oprintf("%02x", *p++); 656178537Sjb 657178537Sjb oprintf("\n"); 658178537Sjb#endif 659178537Sjb 660178476Sjb dtrace_dof_destroy(g_dtp, dof); 661178476Sjb} 662178476Sjb 663178476Sjb/* 664178476Sjb * Link the specified D program in DOF form into an ELF file for use in either 665178476Sjb * helpers, userland provider definitions, or both. If -o was specified, that 666178476Sjb * path is used as the output file name. If -o wasn't specified and the input 667178476Sjb * program is from a script whose name is %.d, use basename(%.o) as the output 668178476Sjb * file name. Otherwise we use "d.out" as the default output file name. 669178476Sjb */ 670178476Sjbstatic void 671178476Sjblink_prog(dtrace_cmd_t *dcp) 672178476Sjb{ 673178476Sjb char *p; 674178476Sjb 675178476Sjb if (g_cmdc == 1 && g_ofile != NULL) { 676178476Sjb (void) strlcpy(dcp->dc_ofile, g_ofile, sizeof (dcp->dc_ofile)); 677178476Sjb } else if ((p = strrchr(dcp->dc_arg, '.')) != NULL && 678178476Sjb strcmp(p, ".d") == 0) { 679178476Sjb p[0] = '\0'; /* strip .d suffix */ 680178476Sjb (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), 681178476Sjb "%s.o", basename(dcp->dc_arg)); 682228598Sdim } else if (g_cmdc > 1) { 683228598Sdim (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), 684228598Sdim "d.out.%td", dcp - g_cmdv); 685178476Sjb } else { 686178476Sjb (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), 687228598Sdim "d.out"); 688178476Sjb } 689178476Sjb 690178476Sjb if (dtrace_program_link(g_dtp, dcp->dc_prog, DTRACE_D_PROBES, 691178476Sjb dcp->dc_ofile, g_objc, g_objv) != 0) 692178476Sjb dfatal("failed to link %s %s", dcp->dc_desc, dcp->dc_name); 693178476Sjb} 694178476Sjb 695178476Sjb/*ARGSUSED*/ 696178476Sjbstatic int 697178476Sjblist_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) 698178476Sjb{ 699178476Sjb dtrace_probeinfo_t p; 700178476Sjb 701178476Sjb oprintf("%5d %10s %17s %33s %s\n", pdp->dtpd_id, 702178476Sjb pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); 703178476Sjb 704178476Sjb if (g_verbose && dtrace_probe_info(dtp, pdp, &p) == 0) 705178476Sjb print_probe_info(&p); 706178476Sjb 707178476Sjb return (0); 708178476Sjb} 709178476Sjb 710178476Sjb/*ARGSUSED*/ 711178476Sjbstatic int 712178476Sjblist_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, 713178476Sjb dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last) 714178476Sjb{ 715178476Sjb dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; 716178476Sjb 717178476Sjb if (edp == *last) 718178476Sjb return (0); 719178476Sjb 720178476Sjb if (dtrace_probe_iter(g_dtp, &edp->dted_probe, list_probe, NULL) != 0) { 721178476Sjb error("failed to match %s:%s:%s:%s: %s\n", 722178476Sjb edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod, 723178476Sjb edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name, 724178476Sjb dtrace_errmsg(dtp, dtrace_errno(dtp))); 725178476Sjb } 726178476Sjb 727178476Sjb *last = edp; 728178476Sjb return (0); 729178476Sjb} 730178476Sjb 731178476Sjb/* 732178476Sjb * List the probes corresponding to the specified program by iterating over 733178476Sjb * each statement and then matching probes to the statement probe descriptions. 734178476Sjb */ 735178476Sjbstatic void 736178476Sjblist_prog(const dtrace_cmd_t *dcp) 737178476Sjb{ 738178476Sjb dtrace_ecbdesc_t *last = NULL; 739178476Sjb 740178476Sjb (void) dtrace_stmt_iter(g_dtp, dcp->dc_prog, 741178476Sjb (dtrace_stmt_f *)list_stmt, &last); 742178476Sjb} 743178476Sjb 744178476Sjbstatic void 745178476Sjbcompile_file(dtrace_cmd_t *dcp) 746178476Sjb{ 747178476Sjb char *arg0; 748178476Sjb FILE *fp; 749178476Sjb 750178476Sjb if ((fp = fopen(dcp->dc_arg, "r")) == NULL) 751178476Sjb fatal("failed to open %s", dcp->dc_arg); 752178476Sjb 753178476Sjb arg0 = g_argv[0]; 754178476Sjb g_argv[0] = dcp->dc_arg; 755178476Sjb 756178476Sjb if ((dcp->dc_prog = dtrace_program_fcompile(g_dtp, fp, 757178476Sjb g_cflags, g_argc, g_argv)) == NULL) 758178476Sjb dfatal("failed to compile script %s", dcp->dc_arg); 759178476Sjb 760178476Sjb g_argv[0] = arg0; 761178476Sjb (void) fclose(fp); 762178476Sjb 763178476Sjb dcp->dc_desc = "script"; 764178476Sjb dcp->dc_name = dcp->dc_arg; 765178476Sjb} 766178476Sjb 767178476Sjbstatic void 768178476Sjbcompile_str(dtrace_cmd_t *dcp) 769178476Sjb{ 770178476Sjb char *p; 771178476Sjb 772178476Sjb if ((dcp->dc_prog = dtrace_program_strcompile(g_dtp, dcp->dc_arg, 773178476Sjb dcp->dc_spec, g_cflags | DTRACE_C_PSPEC, g_argc, g_argv)) == NULL) 774178476Sjb dfatal("invalid probe specifier %s", dcp->dc_arg); 775178476Sjb 776178476Sjb if ((p = strpbrk(dcp->dc_arg, "{/;")) != NULL) 777178476Sjb *p = '\0'; /* crop name for reporting */ 778178476Sjb 779178476Sjb dcp->dc_desc = "description"; 780178476Sjb dcp->dc_name = dcp->dc_arg; 781178476Sjb} 782178476Sjb 783178476Sjb/*ARGSUSED*/ 784178476Sjbstatic void 785178476Sjbprochandler(struct ps_prochandle *P, const char *msg, void *arg) 786178476Sjb{ 787211554Srpaulo#if defined(sun) 788178476Sjb const psinfo_t *prp = Ppsinfo(P); 789178476Sjb int pid = Pstatus(P)->pr_pid; 790178476Sjb char name[SIG2STR_MAX]; 791211554Srpaulo#else 792211554Srpaulo int wstatus = proc_getwstat(P); 793211554Srpaulo int pid = proc_getpid(P); 794211554Srpaulo#endif 795178476Sjb 796178476Sjb if (msg != NULL) { 797178476Sjb notice("pid %d: %s\n", pid, msg); 798178476Sjb return; 799178476Sjb } 800178476Sjb 801211554Srpaulo#if defined(sun) 802178476Sjb switch (Pstate(P)) { 803211554Srpaulo#else 804211554Srpaulo switch (proc_state(P)) { 805211554Srpaulo#endif 806178476Sjb case PS_UNDEAD: 807211554Srpaulo#if defined(sun) 808178476Sjb /* 809178476Sjb * Ideally we would like to always report pr_wstat here, but it 810178476Sjb * isn't possible given current /proc semantics. If we grabbed 811178476Sjb * the process, Ppsinfo() will either fail or return a zeroed 812178476Sjb * psinfo_t depending on how far the parent is in reaping it. 813178476Sjb * When /proc provides a stable pr_wstat in the status file, 814178476Sjb * this code can be improved by examining this new pr_wstat. 815178476Sjb */ 816178476Sjb if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) { 817178476Sjb notice("pid %d terminated by %s\n", pid, 818178476Sjb proc_signame(WTERMSIG(prp->pr_wstat), 819178476Sjb name, sizeof (name))); 820211554Srpaulo#else 821211554Srpaulo if (WIFSIGNALED(wstatus)) { 822211554Srpaulo notice("pid %d terminated by %d\n", pid, 823211554Srpaulo WTERMSIG(wstatus)); 824211554Srpaulo#endif 825211554Srpaulo#if defined(sun) 826178476Sjb } else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) { 827178476Sjb notice("pid %d exited with status %d\n", 828178476Sjb pid, WEXITSTATUS(prp->pr_wstat)); 829211554Srpaulo#else 830211554Srpaulo } else if (WEXITSTATUS(wstatus) != 0) { 831211554Srpaulo notice("pid %d exited with status %d\n", 832211554Srpaulo pid, WEXITSTATUS(wstatus)); 833211554Srpaulo#endif 834178476Sjb } else { 835178476Sjb notice("pid %d has exited\n", pid); 836178476Sjb } 837178476Sjb g_pslive--; 838178476Sjb break; 839178476Sjb 840178476Sjb case PS_LOST: 841178476Sjb notice("pid %d exec'd a set-id or unobservable program\n", pid); 842178476Sjb g_pslive--; 843178476Sjb break; 844178476Sjb } 845178476Sjb} 846178476Sjb 847178476Sjb/*ARGSUSED*/ 848178476Sjbstatic int 849178476Sjberrhandler(const dtrace_errdata_t *data, void *arg) 850178476Sjb{ 851178476Sjb error(data->dteda_msg); 852178476Sjb return (DTRACE_HANDLE_OK); 853178476Sjb} 854178476Sjb 855178476Sjb/*ARGSUSED*/ 856178476Sjbstatic int 857178476Sjbdrophandler(const dtrace_dropdata_t *data, void *arg) 858178476Sjb{ 859178476Sjb error(data->dtdda_msg); 860178476Sjb return (DTRACE_HANDLE_OK); 861178476Sjb} 862178476Sjb 863178476Sjb/*ARGSUSED*/ 864178476Sjbstatic int 865178476Sjbsetopthandler(const dtrace_setoptdata_t *data, void *arg) 866178476Sjb{ 867178476Sjb if (strcmp(data->dtsda_option, "quiet") == 0) 868178476Sjb g_quiet = data->dtsda_newval != DTRACEOPT_UNSET; 869178476Sjb 870178476Sjb if (strcmp(data->dtsda_option, "flowindent") == 0) 871178476Sjb g_flowindent = data->dtsda_newval != DTRACEOPT_UNSET; 872178476Sjb 873178476Sjb return (DTRACE_HANDLE_OK); 874178476Sjb} 875178476Sjb 876178476Sjb#define BUFDUMPHDR(hdr) \ 877178476Sjb (void) printf("%s: %s%s\n", g_pname, hdr, strlen(hdr) > 0 ? ":" : ""); 878178476Sjb 879178476Sjb#define BUFDUMPSTR(ptr, field) \ 880178476Sjb (void) printf("%s: %20s => ", g_pname, #field); \ 881178476Sjb if ((ptr)->field != NULL) { \ 882178476Sjb const char *c = (ptr)->field; \ 883178476Sjb (void) printf("\""); \ 884178476Sjb do { \ 885178476Sjb if (*c == '\n') { \ 886178476Sjb (void) printf("\\n"); \ 887178476Sjb continue; \ 888178476Sjb } \ 889178476Sjb \ 890178476Sjb (void) printf("%c", *c); \ 891178476Sjb } while (*c++ != '\0'); \ 892178476Sjb (void) printf("\"\n"); \ 893178476Sjb } else { \ 894178476Sjb (void) printf("<NULL>\n"); \ 895178476Sjb } 896178476Sjb 897178476Sjb#define BUFDUMPASSTR(ptr, field, str) \ 898178476Sjb (void) printf("%s: %20s => %s\n", g_pname, #field, str); 899178476Sjb 900178476Sjb#define BUFDUMP(ptr, field) \ 901178476Sjb (void) printf("%s: %20s => %lld\n", g_pname, #field, \ 902178476Sjb (long long)(ptr)->field); 903178476Sjb 904178476Sjb#define BUFDUMPPTR(ptr, field) \ 905178476Sjb (void) printf("%s: %20s => %s\n", g_pname, #field, \ 906178476Sjb (ptr)->field != NULL ? "<non-NULL>" : "<NULL>"); 907178476Sjb 908178476Sjb/*ARGSUSED*/ 909178476Sjbstatic int 910178476Sjbbufhandler(const dtrace_bufdata_t *bufdata, void *arg) 911178476Sjb{ 912178476Sjb const dtrace_aggdata_t *agg = bufdata->dtbda_aggdata; 913178476Sjb const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc; 914178476Sjb const dtrace_probedesc_t *pd; 915178476Sjb uint32_t flags = bufdata->dtbda_flags; 916178476Sjb char buf[512], *c = buf, *end = c + sizeof (buf); 917178476Sjb int i, printed; 918178476Sjb 919178476Sjb struct { 920178476Sjb const char *name; 921178476Sjb uint32_t value; 922178476Sjb } flagnames[] = { 923178476Sjb { "AGGVAL", DTRACE_BUFDATA_AGGVAL }, 924178476Sjb { "AGGKEY", DTRACE_BUFDATA_AGGKEY }, 925178476Sjb { "AGGFORMAT", DTRACE_BUFDATA_AGGFORMAT }, 926178476Sjb { "AGGLAST", DTRACE_BUFDATA_AGGLAST }, 927178476Sjb { "???", UINT32_MAX }, 928178476Sjb { NULL } 929178476Sjb }; 930178476Sjb 931178476Sjb if (bufdata->dtbda_probe != NULL) { 932178476Sjb pd = bufdata->dtbda_probe->dtpda_pdesc; 933178476Sjb } else if (agg != NULL) { 934178476Sjb pd = agg->dtada_pdesc; 935178476Sjb } else { 936178476Sjb pd = NULL; 937178476Sjb } 938178476Sjb 939178476Sjb BUFDUMPHDR(">>> Called buffer handler"); 940178476Sjb BUFDUMPHDR(""); 941178476Sjb 942178476Sjb BUFDUMPHDR(" dtrace_bufdata"); 943178476Sjb BUFDUMPSTR(bufdata, dtbda_buffered); 944178476Sjb BUFDUMPPTR(bufdata, dtbda_probe); 945178476Sjb BUFDUMPPTR(bufdata, dtbda_aggdata); 946178476Sjb BUFDUMPPTR(bufdata, dtbda_recdesc); 947178476Sjb 948178476Sjb (void) snprintf(c, end - c, "0x%x ", bufdata->dtbda_flags); 949178476Sjb c += strlen(c); 950178476Sjb 951178476Sjb for (i = 0, printed = 0; flagnames[i].name != NULL; i++) { 952178476Sjb if (!(flags & flagnames[i].value)) 953178476Sjb continue; 954178476Sjb 955178476Sjb (void) snprintf(c, end - c, 956178476Sjb "%s%s", printed++ ? " | " : "(", flagnames[i].name); 957178476Sjb c += strlen(c); 958178476Sjb flags &= ~flagnames[i].value; 959178476Sjb } 960178476Sjb 961178476Sjb if (printed) 962178476Sjb (void) snprintf(c, end - c, ")"); 963178476Sjb 964178476Sjb BUFDUMPASSTR(bufdata, dtbda_flags, buf); 965178476Sjb BUFDUMPHDR(""); 966178476Sjb 967178476Sjb if (pd != NULL) { 968178476Sjb BUFDUMPHDR(" dtrace_probedesc"); 969178476Sjb BUFDUMPSTR(pd, dtpd_provider); 970178476Sjb BUFDUMPSTR(pd, dtpd_mod); 971178476Sjb BUFDUMPSTR(pd, dtpd_func); 972178476Sjb BUFDUMPSTR(pd, dtpd_name); 973178476Sjb BUFDUMPHDR(""); 974178476Sjb } 975178476Sjb 976178476Sjb if (rec != NULL) { 977178476Sjb BUFDUMPHDR(" dtrace_recdesc"); 978178476Sjb BUFDUMP(rec, dtrd_action); 979178476Sjb BUFDUMP(rec, dtrd_size); 980178476Sjb 981178476Sjb if (agg != NULL) { 982178476Sjb uint8_t *data; 983178476Sjb int lim = rec->dtrd_size; 984178476Sjb 985178476Sjb (void) sprintf(buf, "%d (data: ", rec->dtrd_offset); 986178476Sjb c = buf + strlen(buf); 987178476Sjb 988178476Sjb if (lim > sizeof (uint64_t)) 989178476Sjb lim = sizeof (uint64_t); 990178476Sjb 991178476Sjb data = (uint8_t *)agg->dtada_data + rec->dtrd_offset; 992178476Sjb 993178476Sjb for (i = 0; i < lim; i++) { 994178476Sjb (void) snprintf(c, end - c, "%s%02x", 995178476Sjb i == 0 ? "" : " ", *data++); 996178476Sjb c += strlen(c); 997178476Sjb } 998178476Sjb 999178476Sjb (void) snprintf(c, end - c, 1000178476Sjb "%s)", lim < rec->dtrd_size ? " ..." : ""); 1001178476Sjb BUFDUMPASSTR(rec, dtrd_offset, buf); 1002178476Sjb } else { 1003178476Sjb BUFDUMP(rec, dtrd_offset); 1004178476Sjb } 1005178476Sjb 1006178476Sjb BUFDUMPHDR(""); 1007178476Sjb } 1008178476Sjb 1009178476Sjb if (agg != NULL) { 1010178476Sjb dtrace_aggdesc_t *desc = agg->dtada_desc; 1011178476Sjb 1012178476Sjb BUFDUMPHDR(" dtrace_aggdesc"); 1013178476Sjb BUFDUMPSTR(desc, dtagd_name); 1014178476Sjb BUFDUMP(desc, dtagd_varid); 1015178476Sjb BUFDUMP(desc, dtagd_id); 1016178476Sjb BUFDUMP(desc, dtagd_nrecs); 1017178476Sjb BUFDUMPHDR(""); 1018178476Sjb } 1019178476Sjb 1020178476Sjb return (DTRACE_HANDLE_OK); 1021178476Sjb} 1022178476Sjb 1023178476Sjb/*ARGSUSED*/ 1024178476Sjbstatic int 1025178476Sjbchewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) 1026178476Sjb{ 1027178476Sjb dtrace_actkind_t act; 1028178476Sjb uintptr_t addr; 1029178476Sjb 1030178476Sjb if (rec == NULL) { 1031178476Sjb /* 1032178476Sjb * We have processed the final record; output the newline if 1033178476Sjb * we're not in quiet mode. 1034178476Sjb */ 1035178476Sjb if (!g_quiet) 1036178476Sjb oprintf("\n"); 1037178476Sjb 1038178476Sjb return (DTRACE_CONSUME_NEXT); 1039178476Sjb } 1040178476Sjb 1041178476Sjb act = rec->dtrd_action; 1042178476Sjb addr = (uintptr_t)data->dtpda_data; 1043178476Sjb 1044178476Sjb if (act == DTRACEACT_EXIT) { 1045178476Sjb g_status = *((uint32_t *)addr); 1046178476Sjb return (DTRACE_CONSUME_NEXT); 1047178476Sjb } 1048178476Sjb 1049178476Sjb return (DTRACE_CONSUME_THIS); 1050178476Sjb} 1051178476Sjb 1052178476Sjb/*ARGSUSED*/ 1053178476Sjbstatic int 1054178476Sjbchew(const dtrace_probedata_t *data, void *arg) 1055178476Sjb{ 1056178476Sjb dtrace_probedesc_t *pd = data->dtpda_pdesc; 1057178476Sjb processorid_t cpu = data->dtpda_cpu; 1058178476Sjb static int heading; 1059178476Sjb 1060178476Sjb if (g_impatient) { 1061178476Sjb g_newline = 0; 1062178476Sjb return (DTRACE_CONSUME_ABORT); 1063178476Sjb } 1064178476Sjb 1065178476Sjb if (heading == 0) { 1066178476Sjb if (!g_flowindent) { 1067178476Sjb if (!g_quiet) { 1068178476Sjb oprintf("%3s %6s %32s\n", 1069178476Sjb "CPU", "ID", "FUNCTION:NAME"); 1070178476Sjb } 1071178476Sjb } else { 1072178476Sjb oprintf("%3s %-41s\n", "CPU", "FUNCTION"); 1073178476Sjb } 1074178476Sjb heading = 1; 1075178476Sjb } 1076178476Sjb 1077178476Sjb if (!g_flowindent) { 1078178476Sjb if (!g_quiet) { 1079178476Sjb char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2]; 1080178476Sjb 1081178476Sjb (void) snprintf(name, sizeof (name), "%s:%s", 1082178476Sjb pd->dtpd_func, pd->dtpd_name); 1083178476Sjb 1084178476Sjb oprintf("%3d %6d %32s ", cpu, pd->dtpd_id, name); 1085178476Sjb } 1086178476Sjb } else { 1087178476Sjb int indent = data->dtpda_indent; 1088178476Sjb char *name; 1089178476Sjb size_t len; 1090178476Sjb 1091178476Sjb if (data->dtpda_flow == DTRACEFLOW_NONE) { 1092178476Sjb len = indent + DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 5; 1093178476Sjb name = alloca(len); 1094178476Sjb (void) snprintf(name, len, "%*s%s%s:%s", indent, "", 1095178476Sjb data->dtpda_prefix, pd->dtpd_func, 1096178476Sjb pd->dtpd_name); 1097178476Sjb } else { 1098178476Sjb len = indent + DTRACE_FUNCNAMELEN + 5; 1099178476Sjb name = alloca(len); 1100178476Sjb (void) snprintf(name, len, "%*s%s%s", indent, "", 1101178476Sjb data->dtpda_prefix, pd->dtpd_func); 1102178476Sjb } 1103178476Sjb 1104178476Sjb oprintf("%3d %-41s ", cpu, name); 1105178476Sjb } 1106178476Sjb 1107178476Sjb return (DTRACE_CONSUME_THIS); 1108178476Sjb} 1109178476Sjb 1110178476Sjbstatic void 1111178476Sjbgo(void) 1112178476Sjb{ 1113178476Sjb int i; 1114178476Sjb 1115178476Sjb struct { 1116178476Sjb char *name; 1117178476Sjb char *optname; 1118178476Sjb dtrace_optval_t val; 1119178476Sjb } bufs[] = { 1120178476Sjb { "buffer size", "bufsize" }, 1121178476Sjb { "aggregation size", "aggsize" }, 1122178476Sjb { "speculation size", "specsize" }, 1123178476Sjb { "dynamic variable size", "dynvarsize" }, 1124178476Sjb { NULL } 1125178476Sjb }, rates[] = { 1126178476Sjb { "cleaning rate", "cleanrate" }, 1127178476Sjb { "status rate", "statusrate" }, 1128178476Sjb { NULL } 1129178476Sjb }; 1130178476Sjb 1131178476Sjb for (i = 0; bufs[i].name != NULL; i++) { 1132178476Sjb if (dtrace_getopt(g_dtp, bufs[i].optname, &bufs[i].val) == -1) 1133178476Sjb fatal("couldn't get option %s", bufs[i].optname); 1134178476Sjb } 1135178476Sjb 1136178476Sjb for (i = 0; rates[i].name != NULL; i++) { 1137178476Sjb if (dtrace_getopt(g_dtp, rates[i].optname, &rates[i].val) == -1) 1138178476Sjb fatal("couldn't get option %s", rates[i].optname); 1139178476Sjb } 1140178476Sjb 1141178476Sjb if (dtrace_go(g_dtp) == -1) 1142178476Sjb dfatal("could not enable tracing"); 1143178476Sjb 1144178476Sjb for (i = 0; bufs[i].name != NULL; i++) { 1145178476Sjb dtrace_optval_t j = 0, mul = 10; 1146178476Sjb dtrace_optval_t nsize; 1147178476Sjb 1148178476Sjb if (bufs[i].val == DTRACEOPT_UNSET) 1149178476Sjb continue; 1150178476Sjb 1151178476Sjb (void) dtrace_getopt(g_dtp, bufs[i].optname, &nsize); 1152178476Sjb 1153178476Sjb if (nsize == DTRACEOPT_UNSET || nsize == 0) 1154178476Sjb continue; 1155178476Sjb 1156178476Sjb if (nsize >= bufs[i].val - sizeof (uint64_t)) 1157178476Sjb continue; 1158178476Sjb 1159178476Sjb for (; (INT64_C(1) << mul) <= nsize; j++, mul += 10) 1160178476Sjb continue; 1161178476Sjb 1162178476Sjb if (!(nsize & ((INT64_C(1) << (mul - 10)) - 1))) { 1163178476Sjb error("%s lowered to %lld%c\n", bufs[i].name, 1164178476Sjb (long long)nsize >> (mul - 10), " kmgtpe"[j]); 1165178476Sjb } else { 1166178476Sjb error("%s lowered to %lld bytes\n", bufs[i].name, 1167178476Sjb (long long)nsize); 1168178476Sjb } 1169178476Sjb } 1170178476Sjb 1171178476Sjb for (i = 0; rates[i].name != NULL; i++) { 1172178476Sjb dtrace_optval_t nval; 1173178476Sjb char *dir; 1174178476Sjb 1175178476Sjb if (rates[i].val == DTRACEOPT_UNSET) 1176178476Sjb continue; 1177178476Sjb 1178178476Sjb (void) dtrace_getopt(g_dtp, rates[i].optname, &nval); 1179178476Sjb 1180178476Sjb if (nval == DTRACEOPT_UNSET || nval == 0) 1181178476Sjb continue; 1182178476Sjb 1183178476Sjb if (rates[i].val == nval) 1184178476Sjb continue; 1185178476Sjb 1186178476Sjb dir = nval > rates[i].val ? "reduced" : "increased"; 1187178476Sjb 1188178476Sjb if (nval <= NANOSEC && (NANOSEC % nval) == 0) { 1189178476Sjb error("%s %s to %lld hz\n", rates[i].name, dir, 1190178476Sjb (long long)NANOSEC / (long long)nval); 1191178476Sjb continue; 1192178476Sjb } 1193178476Sjb 1194178476Sjb if ((nval % NANOSEC) == 0) { 1195178476Sjb error("%s %s to once every %lld seconds\n", 1196178476Sjb rates[i].name, dir, 1197178476Sjb (long long)nval / (long long)NANOSEC); 1198178476Sjb continue; 1199178476Sjb } 1200178476Sjb 1201178476Sjb error("%s %s to once every %lld nanoseconds\n", 1202178476Sjb rates[i].name, dir, (long long)nval); 1203178476Sjb } 1204178476Sjb} 1205178476Sjb 1206178476Sjb/*ARGSUSED*/ 1207178476Sjbstatic void 1208178476Sjbintr(int signo) 1209178476Sjb{ 1210178476Sjb if (!g_intr) 1211178476Sjb g_newline = 1; 1212178476Sjb 1213238776Sgnn if (g_intr++) 1214178476Sjb g_impatient = 1; 1215178476Sjb} 1216178476Sjb 1217178476Sjbint 1218178476Sjbmain(int argc, char *argv[]) 1219178476Sjb{ 1220178476Sjb dtrace_bufdesc_t buf; 1221178476Sjb struct sigaction act, oact; 1222178476Sjb dtrace_status_t status[2]; 1223178476Sjb dtrace_optval_t opt; 1224178476Sjb dtrace_cmd_t *dcp; 1225178476Sjb 1226178537Sjb g_ofp = stdout; 1227178476Sjb int done = 0, mode = 0; 1228178537Sjb int err, i, c; 1229178537Sjb char *p, **v; 1230178476Sjb struct ps_prochandle *P; 1231178476Sjb pid_t pid; 1232178476Sjb 1233178476Sjb g_pname = basename(argv[0]); 1234178476Sjb 1235178476Sjb if (argc == 1) 1236178476Sjb return (usage(stderr)); 1237178476Sjb 1238178476Sjb if ((g_argv = malloc(sizeof (char *) * argc)) == NULL || 1239178476Sjb (g_cmdv = malloc(sizeof (dtrace_cmd_t) * argc)) == NULL || 1240178476Sjb (g_psv = malloc(sizeof (struct ps_prochandle *) * argc)) == NULL) 1241178476Sjb fatal("failed to allocate memory for arguments"); 1242178476Sjb 1243178476Sjb g_argv[g_argc++] = argv[0]; /* propagate argv[0] to D as $0/$$0 */ 1244178476Sjb argv[0] = g_pname; /* rewrite argv[0] for getopt errors */ 1245178476Sjb 1246178476Sjb bzero(status, sizeof (status)); 1247178476Sjb bzero(&buf, sizeof (buf)); 1248178476Sjb 1249178476Sjb /* 1250178476Sjb * Make an initial pass through argv[] processing any arguments that 1251178476Sjb * affect our behavior mode (g_mode) and flags used for dtrace_open(). 1252178476Sjb * We also accumulate arguments that are not affiliated with getopt 1253178476Sjb * options into g_argv[], and abort if any invalid options are found. 1254178476Sjb */ 1255178476Sjb for (optind = 1; optind < argc; optind++) { 1256178537Sjb while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { 1257178476Sjb switch (c) { 1258178476Sjb case '3': 1259178476Sjb if (strcmp(optarg, "2") != 0) { 1260178476Sjb (void) fprintf(stderr, 1261178476Sjb "%s: illegal option -- 3%s\n", 1262178476Sjb argv[0], optarg); 1263178476Sjb return (usage(stderr)); 1264178476Sjb } 1265178476Sjb g_oflags &= ~DTRACE_O_LP64; 1266178476Sjb g_oflags |= DTRACE_O_ILP32; 1267178476Sjb break; 1268178476Sjb 1269178476Sjb case '6': 1270178476Sjb if (strcmp(optarg, "4") != 0) { 1271178476Sjb (void) fprintf(stderr, 1272178476Sjb "%s: illegal option -- 6%s\n", 1273178476Sjb argv[0], optarg); 1274178476Sjb return (usage(stderr)); 1275178476Sjb } 1276178476Sjb g_oflags &= ~DTRACE_O_ILP32; 1277178476Sjb g_oflags |= DTRACE_O_LP64; 1278178476Sjb break; 1279178476Sjb 1280178476Sjb case 'a': 1281178476Sjb g_grabanon++; /* also checked in pass 2 below */ 1282178476Sjb break; 1283178476Sjb 1284178476Sjb case 'A': 1285178476Sjb g_mode = DMODE_ANON; 1286178476Sjb g_exec = 0; 1287178476Sjb mode++; 1288178476Sjb break; 1289178476Sjb 1290178476Sjb case 'e': 1291178476Sjb g_exec = 0; 1292178476Sjb done = 1; 1293178476Sjb break; 1294178476Sjb 1295178476Sjb case 'h': 1296178476Sjb g_mode = DMODE_HEADER; 1297178476Sjb g_oflags |= DTRACE_O_NODEV; 1298178476Sjb g_cflags |= DTRACE_C_ZDEFS; /* -h implies -Z */ 1299178476Sjb g_exec = 0; 1300178476Sjb mode++; 1301178476Sjb break; 1302178476Sjb 1303178476Sjb case 'G': 1304178476Sjb g_mode = DMODE_LINK; 1305178476Sjb g_oflags |= DTRACE_O_NODEV; 1306178476Sjb g_cflags |= DTRACE_C_ZDEFS; /* -G implies -Z */ 1307178476Sjb g_exec = 0; 1308178476Sjb mode++; 1309178476Sjb break; 1310178476Sjb 1311178476Sjb case 'l': 1312178476Sjb g_mode = DMODE_LIST; 1313178476Sjb g_cflags |= DTRACE_C_ZDEFS; /* -l implies -Z */ 1314178476Sjb mode++; 1315178476Sjb break; 1316178476Sjb 1317178476Sjb case 'V': 1318178476Sjb g_mode = DMODE_VERS; 1319178476Sjb mode++; 1320178476Sjb break; 1321178476Sjb 1322178476Sjb default: 1323178476Sjb if (strchr(DTRACE_OPTSTR, c) == NULL) 1324178476Sjb return (usage(stderr)); 1325178476Sjb } 1326178476Sjb } 1327178476Sjb 1328178476Sjb if (optind < argc) 1329178476Sjb g_argv[g_argc++] = argv[optind]; 1330178476Sjb } 1331178476Sjb 1332178476Sjb if (mode > 1) { 1333178476Sjb (void) fprintf(stderr, "%s: only one of the [-AGhlV] options " 1334178476Sjb "can be specified at a time\n", g_pname); 1335178476Sjb return (E_USAGE); 1336178476Sjb } 1337178476Sjb 1338178476Sjb if (g_mode == DMODE_VERS) 1339178476Sjb return (printf("%s: %s\n", g_pname, _dtrace_version) <= 0); 1340178476Sjb 1341178476Sjb /* 1342178476Sjb * If we're in linker mode and the data model hasn't been specified, 1343178476Sjb * we try to guess the appropriate setting by examining the object 1344178476Sjb * files. We ignore certain errors since we'll catch them later when 1345178476Sjb * we actually process the object files. 1346178476Sjb */ 1347178476Sjb if (g_mode == DMODE_LINK && 1348178476Sjb (g_oflags & (DTRACE_O_ILP32 | DTRACE_O_LP64)) == 0 && 1349178476Sjb elf_version(EV_CURRENT) != EV_NONE) { 1350178476Sjb int fd; 1351178476Sjb Elf *elf; 1352178476Sjb GElf_Ehdr ehdr; 1353178476Sjb 1354178476Sjb for (i = 1; i < g_argc; i++) { 1355178476Sjb if ((fd = open64(g_argv[i], O_RDONLY)) == -1) 1356178476Sjb break; 1357178476Sjb 1358178476Sjb if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 1359178476Sjb (void) close(fd); 1360178476Sjb break; 1361178476Sjb } 1362178476Sjb 1363178476Sjb if (elf_kind(elf) != ELF_K_ELF || 1364178476Sjb gelf_getehdr(elf, &ehdr) == NULL) { 1365178476Sjb (void) close(fd); 1366178476Sjb (void) elf_end(elf); 1367178476Sjb break; 1368178476Sjb } 1369178476Sjb 1370178476Sjb (void) close(fd); 1371178476Sjb (void) elf_end(elf); 1372178476Sjb 1373178476Sjb if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { 1374178476Sjb if (g_oflags & DTRACE_O_ILP32) { 1375178476Sjb fatal("can't mix 32-bit and 64-bit " 1376178476Sjb "object files\n"); 1377178476Sjb } 1378178476Sjb g_oflags |= DTRACE_O_LP64; 1379178476Sjb } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) { 1380178476Sjb if (g_oflags & DTRACE_O_LP64) { 1381178476Sjb fatal("can't mix 32-bit and 64-bit " 1382178476Sjb "object files\n"); 1383178476Sjb } 1384178476Sjb g_oflags |= DTRACE_O_ILP32; 1385178476Sjb } else { 1386178476Sjb break; 1387178476Sjb } 1388178476Sjb } 1389178476Sjb } 1390178476Sjb 1391178476Sjb /* 1392178476Sjb * Open libdtrace. If we are not actually going to be enabling any 1393178476Sjb * instrumentation attempt to reopen libdtrace using DTRACE_O_NODEV. 1394178476Sjb */ 1395178476Sjb while ((g_dtp = dtrace_open(DTRACE_VERSION, g_oflags, &err)) == NULL) { 1396178476Sjb if (!(g_oflags & DTRACE_O_NODEV) && !g_exec && !g_grabanon) { 1397178476Sjb g_oflags |= DTRACE_O_NODEV; 1398178476Sjb continue; 1399178476Sjb } 1400178476Sjb 1401178476Sjb fatal("failed to initialize dtrace: %s\n", 1402178476Sjb dtrace_errmsg(NULL, err)); 1403178476Sjb } 1404178476Sjb 1405178537Sjb#if defined(__i386__) 1406178537Sjb /* XXX The 32-bit seems to need more buffer space by default -sson */ 1407178537Sjb (void) dtrace_setopt(g_dtp, "bufsize", "12m"); 1408178537Sjb (void) dtrace_setopt(g_dtp, "aggsize", "12m"); 1409178537Sjb#else 1410178476Sjb (void) dtrace_setopt(g_dtp, "bufsize", "4m"); 1411178476Sjb (void) dtrace_setopt(g_dtp, "aggsize", "4m"); 1412178537Sjb#endif 1413250574Smarkj (void) dtrace_setopt(g_dtp, "temporal", "yes"); 1414178476Sjb 1415178476Sjb /* 1416178476Sjb * If -G is specified, enable -xlink=dynamic and -xunodefs to permit 1417178476Sjb * references to undefined symbols to remain as unresolved relocations. 1418178476Sjb * If -A is specified, enable -xlink=primary to permit static linking 1419178476Sjb * only to kernel symbols that are defined in a primary kernel module. 1420178476Sjb */ 1421178476Sjb if (g_mode == DMODE_LINK) { 1422178476Sjb (void) dtrace_setopt(g_dtp, "linkmode", "dynamic"); 1423178476Sjb (void) dtrace_setopt(g_dtp, "unodefs", NULL); 1424178476Sjb 1425178476Sjb /* 1426178476Sjb * Use the remaining arguments as the list of object files 1427178476Sjb * when in linker mode. 1428178476Sjb */ 1429178476Sjb g_objc = g_argc - 1; 1430178476Sjb g_objv = g_argv + 1; 1431178476Sjb 1432178476Sjb /* 1433178476Sjb * We still use g_argv[0], the name of the executable. 1434178476Sjb */ 1435178476Sjb g_argc = 1; 1436178476Sjb } else if (g_mode == DMODE_ANON) 1437178476Sjb (void) dtrace_setopt(g_dtp, "linkmode", "primary"); 1438178476Sjb 1439178476Sjb /* 1440178476Sjb * Now that we have libdtrace open, make a second pass through argv[] 1441178476Sjb * to perform any dtrace_setopt() calls and change any compiler flags. 1442178476Sjb * We also accumulate any program specifications into our g_cmdv[] at 1443178476Sjb * this time; these will compiled as part of the fourth processing pass. 1444178476Sjb */ 1445178476Sjb for (optind = 1; optind < argc; optind++) { 1446178537Sjb while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { 1447178476Sjb switch (c) { 1448178476Sjb case 'a': 1449178476Sjb if (dtrace_setopt(g_dtp, "grabanon", 0) != 0) 1450178476Sjb dfatal("failed to set -a"); 1451178476Sjb break; 1452178476Sjb 1453178476Sjb case 'b': 1454178476Sjb if (dtrace_setopt(g_dtp, 1455178476Sjb "bufsize", optarg) != 0) 1456178476Sjb dfatal("failed to set -b %s", optarg); 1457178476Sjb break; 1458178476Sjb 1459178476Sjb case 'B': 1460178476Sjb g_ofp = NULL; 1461178476Sjb break; 1462178476Sjb 1463178476Sjb case 'C': 1464178476Sjb g_cflags |= DTRACE_C_CPP; 1465178476Sjb break; 1466178476Sjb 1467178476Sjb case 'D': 1468178476Sjb if (dtrace_setopt(g_dtp, "define", optarg) != 0) 1469178476Sjb dfatal("failed to set -D %s", optarg); 1470178476Sjb break; 1471178476Sjb 1472178476Sjb case 'f': 1473178476Sjb dcp = &g_cmdv[g_cmdc++]; 1474178476Sjb dcp->dc_func = compile_str; 1475178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_FUNC; 1476178476Sjb dcp->dc_arg = optarg; 1477178476Sjb break; 1478178476Sjb 1479178476Sjb case 'F': 1480178476Sjb if (dtrace_setopt(g_dtp, "flowindent", 0) != 0) 1481178476Sjb dfatal("failed to set -F"); 1482178476Sjb break; 1483178476Sjb 1484178476Sjb case 'H': 1485178476Sjb if (dtrace_setopt(g_dtp, "cpphdrs", 0) != 0) 1486178476Sjb dfatal("failed to set -H"); 1487178476Sjb break; 1488178476Sjb 1489178476Sjb case 'i': 1490178476Sjb dcp = &g_cmdv[g_cmdc++]; 1491178476Sjb dcp->dc_func = compile_str; 1492178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_NAME; 1493178476Sjb dcp->dc_arg = optarg; 1494178476Sjb break; 1495178476Sjb 1496178476Sjb case 'I': 1497178476Sjb if (dtrace_setopt(g_dtp, "incdir", optarg) != 0) 1498178476Sjb dfatal("failed to set -I %s", optarg); 1499178476Sjb break; 1500178476Sjb 1501178476Sjb case 'L': 1502178476Sjb if (dtrace_setopt(g_dtp, "libdir", optarg) != 0) 1503178476Sjb dfatal("failed to set -L %s", optarg); 1504178476Sjb break; 1505178476Sjb 1506178476Sjb case 'm': 1507178476Sjb dcp = &g_cmdv[g_cmdc++]; 1508178476Sjb dcp->dc_func = compile_str; 1509178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_MOD; 1510178476Sjb dcp->dc_arg = optarg; 1511178476Sjb break; 1512178476Sjb 1513178476Sjb case 'n': 1514178476Sjb dcp = &g_cmdv[g_cmdc++]; 1515178476Sjb dcp->dc_func = compile_str; 1516178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_NAME; 1517178476Sjb dcp->dc_arg = optarg; 1518178476Sjb break; 1519178476Sjb 1520178476Sjb case 'P': 1521178476Sjb dcp = &g_cmdv[g_cmdc++]; 1522178476Sjb dcp->dc_func = compile_str; 1523178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_PROVIDER; 1524178476Sjb dcp->dc_arg = optarg; 1525178476Sjb break; 1526178476Sjb 1527178476Sjb case 'q': 1528178476Sjb if (dtrace_setopt(g_dtp, "quiet", 0) != 0) 1529178476Sjb dfatal("failed to set -q"); 1530178476Sjb break; 1531178476Sjb 1532178476Sjb case 'o': 1533178476Sjb g_ofile = optarg; 1534178476Sjb break; 1535178476Sjb 1536178476Sjb case 's': 1537178476Sjb dcp = &g_cmdv[g_cmdc++]; 1538178476Sjb dcp->dc_func = compile_file; 1539178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_NONE; 1540178476Sjb dcp->dc_arg = optarg; 1541178476Sjb break; 1542178476Sjb 1543178476Sjb case 'S': 1544178476Sjb g_cflags |= DTRACE_C_DIFV; 1545178476Sjb break; 1546178476Sjb 1547178476Sjb case 'U': 1548178476Sjb if (dtrace_setopt(g_dtp, "undef", optarg) != 0) 1549178476Sjb dfatal("failed to set -U %s", optarg); 1550178476Sjb break; 1551178476Sjb 1552178476Sjb case 'v': 1553178476Sjb g_verbose++; 1554178476Sjb break; 1555178476Sjb 1556178476Sjb case 'w': 1557178476Sjb if (dtrace_setopt(g_dtp, "destructive", 0) != 0) 1558178476Sjb dfatal("failed to set -w"); 1559178476Sjb break; 1560178476Sjb 1561178476Sjb case 'x': 1562178476Sjb if ((p = strchr(optarg, '=')) != NULL) 1563178476Sjb *p++ = '\0'; 1564178476Sjb 1565178476Sjb if (dtrace_setopt(g_dtp, optarg, p) != 0) 1566178476Sjb dfatal("failed to set -x %s", optarg); 1567178476Sjb break; 1568178476Sjb 1569178476Sjb case 'X': 1570178476Sjb if (dtrace_setopt(g_dtp, "stdc", optarg) != 0) 1571178476Sjb dfatal("failed to set -X %s", optarg); 1572178476Sjb break; 1573178476Sjb 1574178476Sjb case 'Z': 1575178476Sjb g_cflags |= DTRACE_C_ZDEFS; 1576178476Sjb break; 1577178476Sjb 1578178476Sjb default: 1579178476Sjb if (strchr(DTRACE_OPTSTR, c) == NULL) 1580178476Sjb return (usage(stderr)); 1581178476Sjb } 1582178476Sjb } 1583178476Sjb } 1584178476Sjb 1585178476Sjb if (g_ofp == NULL && g_mode != DMODE_EXEC) { 1586178476Sjb (void) fprintf(stderr, "%s: -B not valid in combination" 1587178476Sjb " with [-AGl] options\n", g_pname); 1588178476Sjb return (E_USAGE); 1589178476Sjb } 1590178476Sjb 1591178476Sjb if (g_ofp == NULL && g_ofile != NULL) { 1592178476Sjb (void) fprintf(stderr, "%s: -B not valid in combination" 1593178476Sjb " with -o option\n", g_pname); 1594178476Sjb return (E_USAGE); 1595178476Sjb } 1596178476Sjb 1597178476Sjb /* 1598178476Sjb * In our third pass we handle any command-line options related to 1599178476Sjb * grabbing or creating victim processes. The behavior of these calls 1600178476Sjb * may been affected by any library options set by the second pass. 1601178476Sjb */ 1602178476Sjb for (optind = 1; optind < argc; optind++) { 1603178537Sjb while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { 1604178476Sjb switch (c) { 1605178476Sjb case 'c': 1606178476Sjb if ((v = make_argv(optarg)) == NULL) 1607178476Sjb fatal("failed to allocate memory"); 1608178476Sjb 1609184696Srodrigc P = dtrace_proc_create(g_dtp, v[0], v, NULL, NULL); 1610178476Sjb if (P == NULL) 1611178476Sjb dfatal(NULL); /* dtrace_errmsg() only */ 1612178476Sjb 1613178476Sjb g_psv[g_psc++] = P; 1614178476Sjb free(v); 1615178476Sjb break; 1616178476Sjb 1617178476Sjb case 'p': 1618178476Sjb errno = 0; 1619178476Sjb pid = strtol(optarg, &p, 10); 1620178476Sjb 1621178476Sjb if (errno != 0 || p == optarg || p[0] != '\0') 1622178476Sjb fatal("invalid pid: %s\n", optarg); 1623178476Sjb 1624178476Sjb P = dtrace_proc_grab(g_dtp, pid, 0); 1625178476Sjb if (P == NULL) 1626178476Sjb dfatal(NULL); /* dtrace_errmsg() only */ 1627178476Sjb 1628178476Sjb g_psv[g_psc++] = P; 1629178476Sjb break; 1630178476Sjb } 1631178476Sjb } 1632178476Sjb } 1633178476Sjb 1634178476Sjb /* 1635178476Sjb * In our fourth pass we finish g_cmdv[] by calling dc_func to convert 1636178476Sjb * each string or file specification into a compiled program structure. 1637178476Sjb */ 1638178476Sjb for (i = 0; i < g_cmdc; i++) 1639178476Sjb g_cmdv[i].dc_func(&g_cmdv[i]); 1640178476Sjb 1641178476Sjb if (g_mode != DMODE_LIST) { 1642178476Sjb if (dtrace_handle_err(g_dtp, &errhandler, NULL) == -1) 1643178476Sjb dfatal("failed to establish error handler"); 1644178476Sjb 1645178476Sjb if (dtrace_handle_drop(g_dtp, &drophandler, NULL) == -1) 1646178476Sjb dfatal("failed to establish drop handler"); 1647178476Sjb 1648178476Sjb if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1) 1649178476Sjb dfatal("failed to establish proc handler"); 1650178476Sjb 1651178476Sjb if (dtrace_handle_setopt(g_dtp, &setopthandler, NULL) == -1) 1652178476Sjb dfatal("failed to establish setopt handler"); 1653178476Sjb 1654178476Sjb if (g_ofp == NULL && 1655178476Sjb dtrace_handle_buffered(g_dtp, &bufhandler, NULL) == -1) 1656178476Sjb dfatal("failed to establish buffered handler"); 1657178476Sjb } 1658178476Sjb 1659178476Sjb (void) dtrace_getopt(g_dtp, "flowindent", &opt); 1660178476Sjb g_flowindent = opt != DTRACEOPT_UNSET; 1661178476Sjb 1662178476Sjb (void) dtrace_getopt(g_dtp, "grabanon", &opt); 1663178476Sjb g_grabanon = opt != DTRACEOPT_UNSET; 1664178476Sjb 1665178476Sjb (void) dtrace_getopt(g_dtp, "quiet", &opt); 1666178476Sjb g_quiet = opt != DTRACEOPT_UNSET; 1667178476Sjb 1668178476Sjb /* 1669178476Sjb * Now make a fifth and final pass over the options that have been 1670178476Sjb * turned into programs and saved in g_cmdv[], performing any mode- 1671178476Sjb * specific processing. If g_mode is DMODE_EXEC, we will break out 1672178476Sjb * of the switch() and continue on to the data processing loop. For 1673178476Sjb * other modes, we will exit dtrace once mode-specific work is done. 1674178476Sjb */ 1675178476Sjb switch (g_mode) { 1676178476Sjb case DMODE_EXEC: 1677178476Sjb if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) 1678178476Sjb fatal("failed to open output file '%s'", g_ofile); 1679178476Sjb 1680178476Sjb for (i = 0; i < g_cmdc; i++) 1681178476Sjb exec_prog(&g_cmdv[i]); 1682178476Sjb 1683178476Sjb if (done && !g_grabanon) { 1684178476Sjb dtrace_close(g_dtp); 1685178476Sjb return (g_status); 1686178476Sjb } 1687178476Sjb break; 1688178476Sjb 1689178476Sjb case DMODE_ANON: 1690178476Sjb if (g_ofile == NULL) 1691178537Sjb#if defined(sun) 1692178476Sjb g_ofile = "/kernel/drv/dtrace.conf"; 1693178537Sjb#else 1694178537Sjb /* 1695178537Sjb * On FreeBSD, anonymous DOF data is written to 1696178537Sjb * the DTrace DOF file that the boot loader will 1697178537Sjb * read if booting with the DTrace option. 1698178537Sjb */ 1699178537Sjb g_ofile = "/boot/dtrace.dof"; 1700178537Sjb#endif 1701178476Sjb 1702178476Sjb dof_prune(g_ofile); /* strip out any old DOF directives */ 1703178537Sjb#if defined(sun) 1704178476Sjb etcsystem_prune(); /* string out any forceload directives */ 1705178537Sjb#endif 1706178476Sjb 1707178476Sjb if (g_cmdc == 0) { 1708178476Sjb dtrace_close(g_dtp); 1709178476Sjb return (g_status); 1710178476Sjb } 1711178476Sjb 1712178476Sjb if ((g_ofp = fopen(g_ofile, "a")) == NULL) 1713178476Sjb fatal("failed to open output file '%s'", g_ofile); 1714178476Sjb 1715178476Sjb for (i = 0; i < g_cmdc; i++) { 1716178476Sjb anon_prog(&g_cmdv[i], 1717178476Sjb dtrace_dof_create(g_dtp, g_cmdv[i].dc_prog, 0), i); 1718178476Sjb } 1719178476Sjb 1720178476Sjb /* 1721178476Sjb * Dump out the DOF corresponding to the error handler and the 1722178476Sjb * current options as the final DOF property in the .conf file. 1723178476Sjb */ 1724178476Sjb anon_prog(NULL, dtrace_geterr_dof(g_dtp), i++); 1725178476Sjb anon_prog(NULL, dtrace_getopt_dof(g_dtp), i++); 1726178476Sjb 1727178476Sjb if (fclose(g_ofp) == EOF) 1728178476Sjb fatal("failed to close output file '%s'", g_ofile); 1729178476Sjb 1730178476Sjb /* 1731178476Sjb * These messages would use notice() rather than error(), but 1732178476Sjb * we don't want them suppressed when -A is run on a D program 1733178476Sjb * that itself contains a #pragma D option quiet. 1734178476Sjb */ 1735178476Sjb error("saved anonymous enabling in %s\n", g_ofile); 1736178537Sjb#if defined(sun) 1737178476Sjb etcsystem_add(); 1738178476Sjb error("run update_drv(1M) or reboot to enable changes\n"); 1739178537Sjb#endif 1740178476Sjb 1741178476Sjb dtrace_close(g_dtp); 1742178476Sjb return (g_status); 1743178476Sjb 1744178476Sjb case DMODE_LINK: 1745178476Sjb if (g_cmdc == 0) { 1746178476Sjb (void) fprintf(stderr, "%s: -G requires one or more " 1747178476Sjb "scripts or enabling options\n", g_pname); 1748178476Sjb dtrace_close(g_dtp); 1749178476Sjb return (E_USAGE); 1750178476Sjb } 1751178476Sjb 1752178476Sjb for (i = 0; i < g_cmdc; i++) 1753178476Sjb link_prog(&g_cmdv[i]); 1754178476Sjb 1755178476Sjb if (g_cmdc > 1 && g_ofile != NULL) { 1756178476Sjb char **objv = alloca(g_cmdc * sizeof (char *)); 1757178476Sjb 1758178476Sjb for (i = 0; i < g_cmdc; i++) 1759178476Sjb objv[i] = g_cmdv[i].dc_ofile; 1760178476Sjb 1761178476Sjb if (dtrace_program_link(g_dtp, NULL, DTRACE_D_PROBES, 1762178476Sjb g_ofile, g_cmdc, objv) != 0) 1763178476Sjb dfatal(NULL); /* dtrace_errmsg() only */ 1764178476Sjb } 1765178476Sjb 1766178476Sjb dtrace_close(g_dtp); 1767178476Sjb return (g_status); 1768178476Sjb 1769178476Sjb case DMODE_LIST: 1770178476Sjb if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) 1771178476Sjb fatal("failed to open output file '%s'", g_ofile); 1772178476Sjb 1773178476Sjb oprintf("%5s %10s %17s %33s %s\n", 1774178476Sjb "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME"); 1775178476Sjb 1776178476Sjb for (i = 0; i < g_cmdc; i++) 1777178476Sjb list_prog(&g_cmdv[i]); 1778178476Sjb 1779178476Sjb if (g_cmdc == 0) 1780178476Sjb (void) dtrace_probe_iter(g_dtp, NULL, list_probe, NULL); 1781178476Sjb 1782178476Sjb dtrace_close(g_dtp); 1783178476Sjb return (g_status); 1784178476Sjb 1785178476Sjb case DMODE_HEADER: 1786178476Sjb if (g_cmdc == 0) { 1787178476Sjb (void) fprintf(stderr, "%s: -h requires one or more " 1788178476Sjb "scripts or enabling options\n", g_pname); 1789178476Sjb dtrace_close(g_dtp); 1790178476Sjb return (E_USAGE); 1791178476Sjb } 1792178476Sjb 1793178476Sjb if (g_ofile == NULL) { 1794178476Sjb char *p; 1795178476Sjb 1796178476Sjb if (g_cmdc > 1) { 1797178476Sjb (void) fprintf(stderr, "%s: -h requires an " 1798178476Sjb "output file if multiple scripts are " 1799178476Sjb "specified\n", g_pname); 1800178476Sjb dtrace_close(g_dtp); 1801178476Sjb return (E_USAGE); 1802178476Sjb } 1803178476Sjb 1804178476Sjb if ((p = strrchr(g_cmdv[0].dc_arg, '.')) == NULL || 1805178476Sjb strcmp(p, ".d") != 0) { 1806178476Sjb (void) fprintf(stderr, "%s: -h requires an " 1807178476Sjb "output file if no scripts are " 1808178476Sjb "specified\n", g_pname); 1809178476Sjb dtrace_close(g_dtp); 1810178476Sjb return (E_USAGE); 1811178476Sjb } 1812178476Sjb 1813178476Sjb p[0] = '\0'; /* strip .d suffix */ 1814178476Sjb g_ofile = p = g_cmdv[0].dc_ofile; 1815178476Sjb (void) snprintf(p, sizeof (g_cmdv[0].dc_ofile), 1816178476Sjb "%s.h", basename(g_cmdv[0].dc_arg)); 1817178476Sjb } 1818178476Sjb 1819178476Sjb if ((g_ofp = fopen(g_ofile, "w")) == NULL) 1820178476Sjb fatal("failed to open header file '%s'", g_ofile); 1821178476Sjb 1822178476Sjb oprintf("/*\n * Generated by dtrace(1M).\n */\n\n"); 1823178476Sjb 1824178476Sjb if (dtrace_program_header(g_dtp, g_ofp, g_ofile) != 0 || 1825178476Sjb fclose(g_ofp) == EOF) 1826178476Sjb dfatal("failed to create header file %s", g_ofile); 1827178476Sjb 1828178476Sjb dtrace_close(g_dtp); 1829178476Sjb return (g_status); 1830178476Sjb } 1831178476Sjb 1832178476Sjb /* 1833178476Sjb * If -a and -Z were not specified and no probes have been matched, no 1834178476Sjb * probe criteria was specified on the command line and we abort. 1835178476Sjb */ 1836178476Sjb if (g_total == 0 && !g_grabanon && !(g_cflags & DTRACE_C_ZDEFS)) 1837178476Sjb dfatal("no probes %s\n", g_cmdc ? "matched" : "specified"); 1838178476Sjb 1839178476Sjb /* 1840178476Sjb * Start tracing. Once we dtrace_go(), reload any options that affect 1841178476Sjb * our globals in case consuming anonymous state has changed them. 1842178476Sjb */ 1843178476Sjb go(); 1844178476Sjb 1845178476Sjb (void) dtrace_getopt(g_dtp, "flowindent", &opt); 1846178476Sjb g_flowindent = opt != DTRACEOPT_UNSET; 1847178476Sjb 1848178476Sjb (void) dtrace_getopt(g_dtp, "grabanon", &opt); 1849178476Sjb g_grabanon = opt != DTRACEOPT_UNSET; 1850178476Sjb 1851178476Sjb (void) dtrace_getopt(g_dtp, "quiet", &opt); 1852178476Sjb g_quiet = opt != DTRACEOPT_UNSET; 1853178476Sjb 1854178476Sjb (void) dtrace_getopt(g_dtp, "destructive", &opt); 1855178476Sjb if (opt != DTRACEOPT_UNSET) 1856178476Sjb notice("allowing destructive actions\n"); 1857178476Sjb 1858178476Sjb (void) sigemptyset(&act.sa_mask); 1859178476Sjb act.sa_flags = 0; 1860178476Sjb act.sa_handler = intr; 1861178476Sjb 1862178476Sjb if (sigaction(SIGINT, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) 1863178476Sjb (void) sigaction(SIGINT, &act, NULL); 1864178476Sjb 1865178476Sjb if (sigaction(SIGTERM, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) 1866178476Sjb (void) sigaction(SIGTERM, &act, NULL); 1867178476Sjb 1868178537Sjb#if !defined(sun) 1869178537Sjb if (sigaction(SIGUSR1, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) 1870178537Sjb (void) sigaction(SIGUSR1, &act, NULL); 1871178537Sjb#endif 1872178537Sjb 1873178476Sjb /* 1874178476Sjb * Now that tracing is active and we are ready to consume trace data, 1875178476Sjb * continue any grabbed or created processes, setting them running 1876178476Sjb * using the /proc control mechanism inside of libdtrace. 1877178476Sjb */ 1878178476Sjb for (i = 0; i < g_psc; i++) 1879178476Sjb dtrace_proc_continue(g_dtp, g_psv[i]); 1880178476Sjb 1881178476Sjb g_pslive = g_psc; /* count for prochandler() */ 1882178476Sjb 1883178476Sjb do { 1884178476Sjb if (!g_intr && !done) 1885178476Sjb dtrace_sleep(g_dtp); 1886178476Sjb 1887178476Sjb if (g_newline) { 1888178476Sjb /* 1889178476Sjb * Output a newline just to make the output look 1890178476Sjb * slightly cleaner. Note that we do this even in 1891178476Sjb * "quiet" mode... 1892178476Sjb */ 1893178476Sjb oprintf("\n"); 1894178476Sjb g_newline = 0; 1895178476Sjb } 1896178476Sjb 1897178476Sjb if (done || g_intr || (g_psc != 0 && g_pslive == 0)) { 1898178476Sjb done = 1; 1899178476Sjb if (dtrace_stop(g_dtp) == -1) 1900178476Sjb dfatal("couldn't stop tracing"); 1901178476Sjb } 1902178476Sjb 1903178476Sjb switch (dtrace_work(g_dtp, g_ofp, chew, chewrec, NULL)) { 1904178476Sjb case DTRACE_WORKSTATUS_DONE: 1905178476Sjb done = 1; 1906178476Sjb break; 1907178476Sjb case DTRACE_WORKSTATUS_OKAY: 1908178476Sjb break; 1909178476Sjb default: 1910178476Sjb if (!g_impatient && dtrace_errno(g_dtp) != EINTR) 1911178476Sjb dfatal("processing aborted"); 1912178476Sjb } 1913178476Sjb 1914178476Sjb if (g_ofp != NULL && fflush(g_ofp) == EOF) 1915178476Sjb clearerr(g_ofp); 1916178476Sjb } while (!done); 1917178476Sjb 1918178476Sjb oprintf("\n"); 1919178476Sjb 1920178476Sjb if (!g_impatient) { 1921178476Sjb if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 && 1922178476Sjb dtrace_errno(g_dtp) != EINTR) 1923178476Sjb dfatal("failed to print aggregations"); 1924178476Sjb } 1925178476Sjb 1926178476Sjb dtrace_close(g_dtp); 1927178476Sjb return (g_status); 1928178476Sjb} 1929