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