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. 28267941Srpaulo * 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> 46277300Ssmh#ifdef illumos 47178476Sjb#include <alloca.h> 48178537Sjb#endif 49178476Sjb#include <libgen.h> 50277300Ssmh#ifdef illumos 51178476Sjb#include <libproc.h> 52178537Sjb#endif 53297773Smarkj#ifdef __FreeBSD__ 54297773Smarkj#include <spawn.h> 55297773Smarkj#endif 56178476Sjb 57178476Sjbtypedef struct dtrace_cmd { 58178476Sjb void (*dc_func)(struct dtrace_cmd *); /* function to compile arg */ 59178476Sjb dtrace_probespec_t dc_spec; /* probe specifier context */ 60178476Sjb char *dc_arg; /* argument from main argv */ 61178476Sjb const char *dc_name; /* name for error messages */ 62178476Sjb const char *dc_desc; /* desc for error messages */ 63178476Sjb dtrace_prog_t *dc_prog; /* program compiled from arg */ 64178476Sjb char dc_ofile[PATH_MAX]; /* derived output file name */ 65178476Sjb} dtrace_cmd_t; 66178476Sjb 67178476Sjb#define DMODE_VERS 0 /* display version information and exit (-V) */ 68178476Sjb#define DMODE_EXEC 1 /* compile program for enabling (-a/e/E) */ 69178476Sjb#define DMODE_ANON 2 /* compile program for anonymous tracing (-A) */ 70178476Sjb#define DMODE_LINK 3 /* compile program for linking with ELF (-G) */ 71178476Sjb#define DMODE_LIST 4 /* compile program and list probes (-l) */ 72178476Sjb#define DMODE_HEADER 5 /* compile program for headergen (-h) */ 73178476Sjb 74178476Sjb#define E_SUCCESS 0 75178476Sjb#define E_ERROR 1 76178476Sjb#define E_USAGE 2 77178476Sjb 78178476Sjbstatic const char DTRACE_OPTSTR[] = 79178476Sjb "3:6:aAb:Bc:CD:ef:FGhHi:I:lL:m:n:o:p:P:qs:SU:vVwx:X:Z"; 80178476Sjb 81178476Sjbstatic char **g_argv; 82178476Sjbstatic int g_argc; 83178476Sjbstatic char **g_objv; 84178476Sjbstatic int g_objc; 85178476Sjbstatic dtrace_cmd_t *g_cmdv; 86178476Sjbstatic int g_cmdc; 87178476Sjbstatic struct ps_prochandle **g_psv; 88178476Sjbstatic int g_psc; 89178476Sjbstatic int g_pslive; 90178476Sjbstatic char *g_pname; 91178476Sjbstatic int g_quiet; 92178476Sjbstatic int g_flowindent; 93178476Sjbstatic int g_intr; 94178476Sjbstatic int g_impatient; 95178476Sjbstatic int g_newline; 96306576Smarkj#ifdef __FreeBSD__ 97306576Smarkjstatic int g_siginfo; 98306576Smarkj#endif 99178476Sjbstatic int g_total; 100178476Sjbstatic int g_cflags; 101178476Sjbstatic int g_oflags; 102178476Sjbstatic int g_verbose; 103178476Sjbstatic int g_exec = 1; 104178476Sjbstatic int g_mode = DMODE_EXEC; 105178476Sjbstatic int g_status = E_SUCCESS; 106178476Sjbstatic int g_grabanon = 0; 107178476Sjbstatic const char *g_ofile = NULL; 108178537Sjbstatic FILE *g_ofp; 109178476Sjbstatic dtrace_hdl_t *g_dtp; 110277300Ssmh#ifdef illumos 111178476Sjbstatic char *g_etcfile = "/etc/system"; 112178476Sjbstatic const char *g_etcbegin = "* vvvv Added by DTrace"; 113178476Sjbstatic const char *g_etcend = "* ^^^^ Added by DTrace"; 114178476Sjb 115178476Sjbstatic const char *g_etc[] = { 116178476Sjb"*", 117178476Sjb"* The following forceload directives were added by dtrace(1M) to allow for", 118178476Sjb"* tracing during boot. If these directives are removed, the system will", 119178476Sjb"* continue to function, but tracing will not occur during boot as desired.", 120178476Sjb"* To remove these directives (and this block comment) automatically, run", 121178476Sjb"* \"dtrace -A\" without additional arguments. See the \"Anonymous Tracing\"", 122178476Sjb"* chapter of the Solaris Dynamic Tracing Guide for details.", 123178476Sjb"*", 124178476SjbNULL }; 125178537Sjb#endif 126178476Sjb 127178476Sjbstatic int 128178476Sjbusage(FILE *fp) 129178476Sjb{ 130178476Sjb static const char predact[] = "[[ predicate ] action ]"; 131178476Sjb 132178476Sjb (void) fprintf(fp, "Usage: %s [-32|-64] [-aACeFGhHlqSvVwZ] " 133178476Sjb "[-b bufsz] [-c cmd] [-D name[=def]]\n\t[-I path] [-L path] " 134178476Sjb "[-o output] [-p pid] [-s script] [-U name]\n\t" 135178476Sjb "[-x opt[=val]] [-X a|c|s|t]\n\n" 136178476Sjb "\t[-P provider %s]\n" 137178476Sjb "\t[-m [ provider: ] module %s]\n" 138178476Sjb "\t[-f [[ provider: ] module: ] func %s]\n" 139178476Sjb "\t[-n [[[ provider: ] module: ] func: ] name %s]\n" 140178476Sjb "\t[-i probe-id %s] [ args ... ]\n\n", g_pname, 141178476Sjb predact, predact, predact, predact, predact); 142178476Sjb 143178476Sjb (void) fprintf(fp, "\tpredicate -> '/' D-expression '/'\n"); 144178476Sjb (void) fprintf(fp, "\t action -> '{' D-statements '}'\n"); 145178476Sjb 146178476Sjb (void) fprintf(fp, "\n" 147178476Sjb "\t-32 generate 32-bit D programs and ELF files\n" 148178476Sjb "\t-64 generate 64-bit D programs and ELF files\n\n" 149178476Sjb "\t-a claim anonymous tracing state\n" 150178476Sjb "\t-A generate driver.conf(4) directives for anonymous tracing\n" 151178476Sjb "\t-b set trace buffer size\n" 152178476Sjb "\t-c run specified command and exit upon its completion\n" 153178476Sjb "\t-C run cpp(1) preprocessor on script files\n" 154178476Sjb "\t-D define symbol when invoking preprocessor\n" 155178476Sjb "\t-e exit after compiling request but prior to enabling probes\n" 156178476Sjb "\t-f enable or list probes matching the specified function name\n" 157178476Sjb "\t-F coalesce trace output by function\n" 158178476Sjb "\t-G generate an ELF file containing embedded dtrace program\n" 159178476Sjb "\t-h generate a header file with definitions for static probes\n" 160178476Sjb "\t-H print included files when invoking preprocessor\n" 161178476Sjb "\t-i enable or list probes matching the specified probe id\n" 162178476Sjb "\t-I add include directory to preprocessor search path\n" 163178476Sjb "\t-l list probes matching specified criteria\n" 164178476Sjb "\t-L add library directory to library search path\n" 165178476Sjb "\t-m enable or list probes matching the specified module name\n" 166178476Sjb "\t-n enable or list probes matching the specified probe name\n" 167178476Sjb "\t-o set output file\n" 168178476Sjb "\t-p grab specified process-ID and cache its symbol tables\n" 169178476Sjb "\t-P enable or list probes matching the specified provider name\n" 170178476Sjb "\t-q set quiet mode (only output explicitly traced data)\n" 171178476Sjb "\t-s enable or list probes according to the specified D script\n" 172178476Sjb "\t-S print D compiler intermediate code\n" 173178476Sjb "\t-U undefine symbol when invoking preprocessor\n" 174178476Sjb "\t-v set verbose mode (report stability attributes, arguments)\n" 175178476Sjb "\t-V report DTrace API version\n" 176178476Sjb "\t-w permit destructive actions\n" 177178476Sjb "\t-x enable or modify compiler and tracing options\n" 178178476Sjb "\t-X specify ISO C conformance settings for preprocessor\n" 179178476Sjb "\t-Z permit probe descriptions that match zero probes\n"); 180178476Sjb 181178476Sjb return (E_USAGE); 182178476Sjb} 183178476Sjb 184178476Sjbstatic void 185178476Sjbverror(const char *fmt, va_list ap) 186178476Sjb{ 187178476Sjb int error = errno; 188178476Sjb 189178476Sjb (void) fprintf(stderr, "%s: ", g_pname); 190178476Sjb (void) vfprintf(stderr, fmt, ap); 191178476Sjb 192178476Sjb if (fmt[strlen(fmt) - 1] != '\n') 193178476Sjb (void) fprintf(stderr, ": %s\n", strerror(error)); 194178476Sjb} 195178476Sjb 196178476Sjb/*PRINTFLIKE1*/ 197178476Sjbstatic void 198178476Sjbfatal(const char *fmt, ...) 199178476Sjb{ 200178476Sjb va_list ap; 201178476Sjb 202178476Sjb va_start(ap, fmt); 203178476Sjb verror(fmt, ap); 204178476Sjb va_end(ap); 205178476Sjb 206247048Sgibbs /* 207247048Sgibbs * Close the DTrace handle to ensure that any controlled processes are 208247048Sgibbs * correctly restored and continued. 209247048Sgibbs */ 210247048Sgibbs if (g_dtp) 211247048Sgibbs dtrace_close(g_dtp); 212247048Sgibbs 213178476Sjb exit(E_ERROR); 214178476Sjb} 215178476Sjb 216178476Sjb/*PRINTFLIKE1*/ 217178476Sjbstatic void 218178476Sjbdfatal(const char *fmt, ...) 219178476Sjb{ 220277300Ssmh#if !defined(illumos) && defined(NEED_ERRLOC) 221178537Sjb char *p_errfile = NULL; 222178537Sjb int errline = 0; 223178537Sjb#endif 224178476Sjb va_list ap; 225178476Sjb 226178476Sjb va_start(ap, fmt); 227178476Sjb 228178476Sjb (void) fprintf(stderr, "%s: ", g_pname); 229178476Sjb if (fmt != NULL) 230178476Sjb (void) vfprintf(stderr, fmt, ap); 231178476Sjb 232178476Sjb va_end(ap); 233178476Sjb 234178476Sjb if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') { 235178476Sjb (void) fprintf(stderr, ": %s\n", 236178476Sjb dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); 237178476Sjb } else if (fmt == NULL) { 238178476Sjb (void) fprintf(stderr, "%s\n", 239178476Sjb dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); 240178476Sjb } 241277300Ssmh#if !defined(illumos) && defined(NEED_ERRLOC) 242178537Sjb dt_get_errloc(g_dtp, &p_errfile, &errline); 243178537Sjb if (p_errfile != NULL) 244178537Sjb printf("File '%s', line %d\n", p_errfile, errline); 245178537Sjb#endif 246178476Sjb 247178476Sjb /* 248178476Sjb * Close the DTrace handle to ensure that any controlled processes are 249178476Sjb * correctly restored and continued. 250178476Sjb */ 251178476Sjb dtrace_close(g_dtp); 252178476Sjb 253178476Sjb exit(E_ERROR); 254178476Sjb} 255178476Sjb 256178476Sjb/*PRINTFLIKE1*/ 257178476Sjbstatic void 258178476Sjberror(const char *fmt, ...) 259178476Sjb{ 260178476Sjb va_list ap; 261178476Sjb 262178476Sjb va_start(ap, fmt); 263178476Sjb verror(fmt, ap); 264178476Sjb va_end(ap); 265178476Sjb} 266178476Sjb 267178476Sjb/*PRINTFLIKE1*/ 268178476Sjbstatic void 269178476Sjbnotice(const char *fmt, ...) 270178476Sjb{ 271178476Sjb va_list ap; 272178476Sjb 273178476Sjb if (g_quiet) 274178476Sjb return; /* -q or quiet pragma suppresses notice()s */ 275178476Sjb 276178476Sjb va_start(ap, fmt); 277178476Sjb verror(fmt, ap); 278178476Sjb va_end(ap); 279178476Sjb} 280178476Sjb 281178476Sjb/*PRINTFLIKE1*/ 282178476Sjbstatic void 283178476Sjboprintf(const char *fmt, ...) 284178476Sjb{ 285178476Sjb va_list ap; 286178476Sjb int n; 287178476Sjb 288178476Sjb if (g_ofp == NULL) 289178476Sjb return; 290178476Sjb 291178476Sjb va_start(ap, fmt); 292178476Sjb n = vfprintf(g_ofp, fmt, ap); 293178476Sjb va_end(ap); 294178476Sjb 295178476Sjb if (n < 0) { 296178476Sjb if (errno != EINTR) { 297178476Sjb fatal("failed to write to %s", 298178476Sjb g_ofile ? g_ofile : "<stdout>"); 299178476Sjb } 300178476Sjb clearerr(g_ofp); 301178476Sjb } 302178476Sjb} 303178476Sjb 304178476Sjbstatic char ** 305178476Sjbmake_argv(char *s) 306178476Sjb{ 307178476Sjb const char *ws = "\f\n\r\t\v "; 308178476Sjb char **argv = malloc(sizeof (char *) * (strlen(s) / 2 + 1)); 309178476Sjb int argc = 0; 310178476Sjb char *p = s; 311178476Sjb 312178476Sjb if (argv == NULL) 313178476Sjb return (NULL); 314178476Sjb 315178476Sjb for (p = strtok(s, ws); p != NULL; p = strtok(NULL, ws)) 316178476Sjb argv[argc++] = p; 317178476Sjb 318178476Sjb if (argc == 0) 319178476Sjb argv[argc++] = s; 320178476Sjb 321178476Sjb argv[argc] = NULL; 322178476Sjb return (argv); 323178476Sjb} 324178476Sjb 325178476Sjbstatic void 326178476Sjbdof_prune(const char *fname) 327178476Sjb{ 328178476Sjb struct stat sbuf; 329178476Sjb size_t sz, i, j, mark, len; 330178476Sjb char *buf; 331178476Sjb int msg = 0, fd; 332178476Sjb 333178476Sjb if ((fd = open(fname, O_RDONLY)) == -1) { 334178476Sjb /* 335178476Sjb * This is okay only if the file doesn't exist at all. 336178476Sjb */ 337178476Sjb if (errno != ENOENT) 338178476Sjb fatal("failed to open %s", fname); 339178476Sjb return; 340178476Sjb } 341178476Sjb 342178476Sjb if (fstat(fd, &sbuf) == -1) 343178476Sjb fatal("failed to fstat %s", fname); 344178476Sjb 345178476Sjb if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL) 346178476Sjb fatal("failed to allocate memory for %s", fname); 347178476Sjb 348178476Sjb if (read(fd, buf, sz) != sz) 349178476Sjb fatal("failed to read %s", fname); 350178476Sjb 351178476Sjb buf[sz] = '\0'; 352178476Sjb (void) close(fd); 353178476Sjb 354178476Sjb if ((fd = open(fname, O_WRONLY | O_TRUNC)) == -1) 355178476Sjb fatal("failed to open %s for writing", fname); 356178476Sjb 357178476Sjb len = strlen("dof-data-"); 358178476Sjb 359178476Sjb for (mark = 0, i = 0; i < sz; i++) { 360178476Sjb if (strncmp(&buf[i], "dof-data-", len) != 0) 361178476Sjb continue; 362178476Sjb 363178476Sjb /* 364178476Sjb * This is only a match if it's in the 0th column. 365178476Sjb */ 366178476Sjb if (i != 0 && buf[i - 1] != '\n') 367178476Sjb continue; 368178476Sjb 369178476Sjb if (msg++ == 0) { 370178476Sjb error("cleaned up old anonymous " 371178476Sjb "enabling in %s\n", fname); 372178476Sjb } 373178476Sjb 374178476Sjb /* 375178476Sjb * We have a match. First write out our data up until now. 376178476Sjb */ 377178476Sjb if (i != mark) { 378178476Sjb if (write(fd, &buf[mark], i - mark) != i - mark) 379178476Sjb fatal("failed to write to %s", fname); 380178476Sjb } 381178476Sjb 382178476Sjb /* 383178476Sjb * Now scan forward until we scan past a newline. 384178476Sjb */ 385178476Sjb for (j = i; j < sz && buf[j] != '\n'; j++) 386178476Sjb continue; 387178476Sjb 388178476Sjb /* 389178476Sjb * Reset our mark. 390178476Sjb */ 391178476Sjb if ((mark = j + 1) >= sz) 392178476Sjb break; 393178476Sjb 394178476Sjb i = j; 395178476Sjb } 396178476Sjb 397178476Sjb if (mark < sz) { 398178476Sjb if (write(fd, &buf[mark], sz - mark) != sz - mark) 399178476Sjb fatal("failed to write to %s", fname); 400178476Sjb } 401178476Sjb 402178476Sjb (void) close(fd); 403178476Sjb free(buf); 404178476Sjb} 405178476Sjb 406297773Smarkj#ifdef __FreeBSD__ 407297773Smarkj/* 408297773Smarkj * Use nextboot(8) to tell the loader to load DTrace kernel modules during 409297773Smarkj * the next boot of the system. The nextboot(8) configuration is removed during 410297773Smarkj * boot, so it will not persist indefinitely. 411297773Smarkj */ 412178476Sjbstatic void 413297773Smarkjbootdof_add(void) 414297773Smarkj{ 415297773Smarkj char * const nbargv[] = { 416297773Smarkj "nextboot", "-a", 417297773Smarkj "-e", "dtraceall_load=\"YES\"", 418297773Smarkj "-e", "dtrace_dof_load=\"YES\"", 419297773Smarkj "-e", "dtrace_dof_name=\"/boot/dtrace.dof\"", 420297773Smarkj "-e", "dtrace_dof_type=\"dtrace_dof\"", 421297773Smarkj NULL, 422297773Smarkj }; 423297773Smarkj pid_t child; 424297773Smarkj int err, status; 425297773Smarkj 426297773Smarkj err = posix_spawnp(&child, "nextboot", NULL, NULL, nbargv, 427297773Smarkj NULL); 428297773Smarkj if (err != 0) { 429297773Smarkj error("failed to execute nextboot: %s", strerror(err)); 430297773Smarkj exit(E_ERROR); 431297773Smarkj } 432297773Smarkj 433297773Smarkj if (waitpid(child, &status, 0) != child) 434297773Smarkj fatal("waiting for nextboot"); 435297773Smarkj if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 436297773Smarkj error("nextboot returned with status %d", status); 437297773Smarkj exit(E_ERROR); 438297773Smarkj } 439297773Smarkj} 440297773Smarkj#else 441297773Smarkjstatic void 442178476Sjbetcsystem_prune(void) 443178476Sjb{ 444178476Sjb struct stat sbuf; 445178476Sjb size_t sz; 446178476Sjb char *buf, *start, *end; 447178476Sjb int fd; 448178476Sjb char *fname = g_etcfile, *tmpname; 449178476Sjb 450178476Sjb if ((fd = open(fname, O_RDONLY)) == -1) 451178476Sjb fatal("failed to open %s", fname); 452178476Sjb 453178476Sjb if (fstat(fd, &sbuf) == -1) 454178476Sjb fatal("failed to fstat %s", fname); 455178476Sjb 456178476Sjb if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL) 457178476Sjb fatal("failed to allocate memory for %s", fname); 458178476Sjb 459178476Sjb if (read(fd, buf, sz) != sz) 460178476Sjb fatal("failed to read %s", fname); 461178476Sjb 462178476Sjb buf[sz] = '\0'; 463178476Sjb (void) close(fd); 464178476Sjb 465178476Sjb if ((start = strstr(buf, g_etcbegin)) == NULL) 466178476Sjb goto out; 467178476Sjb 468178476Sjb if (strlen(buf) != sz) { 469178476Sjb fatal("embedded nul byte in %s; manual repair of %s " 470178476Sjb "required\n", fname, fname); 471178476Sjb } 472178476Sjb 473178476Sjb if (strstr(start + 1, g_etcbegin) != NULL) { 474178476Sjb fatal("multiple start sentinels in %s; manual repair of %s " 475178476Sjb "required\n", fname, fname); 476178476Sjb } 477178476Sjb 478178476Sjb if ((end = strstr(buf, g_etcend)) == NULL) { 479178476Sjb fatal("missing end sentinel in %s; manual repair of %s " 480178476Sjb "required\n", fname, fname); 481178476Sjb } 482178476Sjb 483178476Sjb if (start > end) { 484178476Sjb fatal("end sentinel preceeds start sentinel in %s; manual " 485178476Sjb "repair of %s required\n", fname, fname); 486178476Sjb } 487178476Sjb 488178476Sjb end += strlen(g_etcend) + 1; 489178476Sjb bcopy(end, start, strlen(end) + 1); 490178476Sjb 491178476Sjb tmpname = alloca(sz = strlen(fname) + 80); 492178476Sjb (void) snprintf(tmpname, sz, "%s.dtrace.%d", fname, getpid()); 493178476Sjb 494178476Sjb if ((fd = open(tmpname, 495178476Sjb O_WRONLY | O_CREAT | O_EXCL, sbuf.st_mode)) == -1) 496178476Sjb fatal("failed to create %s", tmpname); 497178476Sjb 498178476Sjb if (write(fd, buf, strlen(buf)) < strlen(buf)) { 499178476Sjb (void) unlink(tmpname); 500178476Sjb fatal("failed to write to %s", tmpname); 501178476Sjb } 502178476Sjb 503178476Sjb (void) close(fd); 504178476Sjb 505178476Sjb if (chown(tmpname, sbuf.st_uid, sbuf.st_gid) != 0) { 506178476Sjb (void) unlink(tmpname); 507178476Sjb fatal("failed to chown(2) %s to uid %d, gid %d", tmpname, 508178476Sjb (int)sbuf.st_uid, (int)sbuf.st_gid); 509178476Sjb } 510178476Sjb 511178476Sjb if (rename(tmpname, fname) == -1) 512178476Sjb fatal("rename of %s to %s failed", tmpname, fname); 513178476Sjb 514178476Sjb error("cleaned up forceload directives in %s\n", fname); 515178476Sjbout: 516178476Sjb free(buf); 517178476Sjb} 518178476Sjb 519178476Sjbstatic void 520178476Sjbetcsystem_add(void) 521178476Sjb{ 522178476Sjb const char *mods[20]; 523178476Sjb int nmods, line; 524178476Sjb 525178476Sjb if ((g_ofp = fopen(g_ofile = g_etcfile, "a")) == NULL) 526178476Sjb fatal("failed to open output file '%s'", g_ofile); 527178476Sjb 528178476Sjb oprintf("%s\n", g_etcbegin); 529178476Sjb 530178476Sjb for (line = 0; g_etc[line] != NULL; line++) 531178476Sjb oprintf("%s\n", g_etc[line]); 532178476Sjb 533178476Sjb nmods = dtrace_provider_modules(g_dtp, mods, 534178476Sjb sizeof (mods) / sizeof (char *) - 1); 535178476Sjb 536178476Sjb if (nmods >= sizeof (mods) / sizeof (char *)) 537178476Sjb fatal("unexpectedly large number of modules!"); 538178476Sjb 539178476Sjb mods[nmods++] = "dtrace"; 540178476Sjb 541178476Sjb for (line = 0; line < nmods; line++) 542178476Sjb oprintf("forceload: drv/%s\n", mods[line]); 543178476Sjb 544178476Sjb oprintf("%s\n", g_etcend); 545178476Sjb 546178476Sjb if (fclose(g_ofp) == EOF) 547178476Sjb fatal("failed to close output file '%s'", g_ofile); 548178476Sjb 549178476Sjb error("added forceload directives to %s\n", g_ofile); 550178476Sjb} 551297773Smarkj#endif /* !__FreeBSD__ */ 552178476Sjb 553178476Sjbstatic void 554178476Sjbprint_probe_info(const dtrace_probeinfo_t *p) 555178476Sjb{ 556178476Sjb char buf[BUFSIZ]; 557267941Srpaulo char *user; 558178476Sjb int i; 559178476Sjb 560178476Sjb oprintf("\n\tProbe Description Attributes\n"); 561178476Sjb 562178476Sjb oprintf("\t\tIdentifier Names: %s\n", 563178476Sjb dtrace_stability_name(p->dtp_attr.dtat_name)); 564178476Sjb oprintf("\t\tData Semantics: %s\n", 565178476Sjb dtrace_stability_name(p->dtp_attr.dtat_data)); 566178476Sjb oprintf("\t\tDependency Class: %s\n", 567178476Sjb dtrace_class_name(p->dtp_attr.dtat_class)); 568178476Sjb 569178476Sjb oprintf("\n\tArgument Attributes\n"); 570178476Sjb 571178476Sjb oprintf("\t\tIdentifier Names: %s\n", 572178476Sjb dtrace_stability_name(p->dtp_arga.dtat_name)); 573178476Sjb oprintf("\t\tData Semantics: %s\n", 574178476Sjb dtrace_stability_name(p->dtp_arga.dtat_data)); 575178476Sjb oprintf("\t\tDependency Class: %s\n", 576178476Sjb dtrace_class_name(p->dtp_arga.dtat_class)); 577178476Sjb 578178476Sjb oprintf("\n\tArgument Types\n"); 579178476Sjb 580178476Sjb for (i = 0; i < p->dtp_argc; i++) { 581267941Srpaulo if (p->dtp_argv[i].dtt_flags & DTT_FL_USER) 582267941Srpaulo user = "userland "; 583267941Srpaulo else 584267941Srpaulo user = ""; 585178476Sjb if (ctf_type_name(p->dtp_argv[i].dtt_ctfp, 586178476Sjb p->dtp_argv[i].dtt_type, buf, sizeof (buf)) == NULL) 587178476Sjb (void) strlcpy(buf, "(unknown)", sizeof (buf)); 588267941Srpaulo oprintf("\t\targs[%d]: %s%s\n", i, user, buf); 589178476Sjb } 590178476Sjb 591178476Sjb if (p->dtp_argc == 0) 592178476Sjb oprintf("\t\tNone\n"); 593178476Sjb 594178476Sjb oprintf("\n"); 595178476Sjb} 596178476Sjb 597178476Sjb/*ARGSUSED*/ 598178476Sjbstatic int 599178476Sjbinfo_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, 600178476Sjb dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last) 601178476Sjb{ 602178476Sjb dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; 603178476Sjb dtrace_probedesc_t *pdp = &edp->dted_probe; 604178476Sjb dtrace_probeinfo_t p; 605178476Sjb 606178476Sjb if (edp == *last) 607178476Sjb return (0); 608178476Sjb 609178476Sjb oprintf("\n%s:%s:%s:%s\n", 610178476Sjb pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); 611178476Sjb 612178476Sjb if (dtrace_probe_info(dtp, pdp, &p) == 0) 613178476Sjb print_probe_info(&p); 614178476Sjb 615178476Sjb *last = edp; 616178476Sjb return (0); 617178476Sjb} 618178476Sjb 619178476Sjb/* 620178476Sjb * Execute the specified program by enabling the corresponding instrumentation. 621178476Sjb * If -e has been specified, we get the program info but do not enable it. If 622178476Sjb * -v has been specified, we print a stability report for the program. 623178476Sjb */ 624178476Sjbstatic void 625178476Sjbexec_prog(const dtrace_cmd_t *dcp) 626178476Sjb{ 627178476Sjb dtrace_ecbdesc_t *last = NULL; 628178476Sjb dtrace_proginfo_t dpi; 629178476Sjb 630178476Sjb if (!g_exec) { 631178476Sjb dtrace_program_info(g_dtp, dcp->dc_prog, &dpi); 632178476Sjb } else if (dtrace_program_exec(g_dtp, dcp->dc_prog, &dpi) == -1) { 633178476Sjb dfatal("failed to enable '%s'", dcp->dc_name); 634178476Sjb } else { 635178476Sjb notice("%s '%s' matched %u probe%s\n", 636178476Sjb dcp->dc_desc, dcp->dc_name, 637178476Sjb dpi.dpi_matches, dpi.dpi_matches == 1 ? "" : "s"); 638178476Sjb } 639178476Sjb 640178476Sjb if (g_verbose) { 641178476Sjb oprintf("\nStability attributes for %s %s:\n", 642178476Sjb dcp->dc_desc, dcp->dc_name); 643178476Sjb 644178476Sjb oprintf("\n\tMinimum Probe Description Attributes\n"); 645178476Sjb oprintf("\t\tIdentifier Names: %s\n", 646178476Sjb dtrace_stability_name(dpi.dpi_descattr.dtat_name)); 647178476Sjb oprintf("\t\tData Semantics: %s\n", 648178476Sjb dtrace_stability_name(dpi.dpi_descattr.dtat_data)); 649178476Sjb oprintf("\t\tDependency Class: %s\n", 650178476Sjb dtrace_class_name(dpi.dpi_descattr.dtat_class)); 651178476Sjb 652178476Sjb oprintf("\n\tMinimum Statement Attributes\n"); 653178476Sjb 654178476Sjb oprintf("\t\tIdentifier Names: %s\n", 655178476Sjb dtrace_stability_name(dpi.dpi_stmtattr.dtat_name)); 656178476Sjb oprintf("\t\tData Semantics: %s\n", 657178476Sjb dtrace_stability_name(dpi.dpi_stmtattr.dtat_data)); 658178476Sjb oprintf("\t\tDependency Class: %s\n", 659178476Sjb dtrace_class_name(dpi.dpi_stmtattr.dtat_class)); 660178476Sjb 661178476Sjb if (!g_exec) { 662178476Sjb (void) dtrace_stmt_iter(g_dtp, dcp->dc_prog, 663178476Sjb (dtrace_stmt_f *)info_stmt, &last); 664178476Sjb } else 665178476Sjb oprintf("\n"); 666178476Sjb } 667178476Sjb 668178476Sjb g_total += dpi.dpi_matches; 669178476Sjb} 670178476Sjb 671178476Sjb/* 672178476Sjb * Print out the specified DOF buffer as a set of ASCII bytes appropriate for 673178476Sjb * storing in a driver.conf(4) file associated with the dtrace driver. 674178476Sjb */ 675178476Sjbstatic void 676178476Sjbanon_prog(const dtrace_cmd_t *dcp, dof_hdr_t *dof, int n) 677178476Sjb{ 678178476Sjb const uchar_t *p, *q; 679178476Sjb 680178476Sjb if (dof == NULL) 681178476Sjb dfatal("failed to create DOF image for '%s'", dcp->dc_name); 682178476Sjb 683178476Sjb p = (uchar_t *)dof; 684178476Sjb q = p + dof->dofh_loadsz; 685178476Sjb 686297773Smarkj#ifdef __FreeBSD__ 687297773Smarkj /* 688297773Smarkj * On FreeBSD, the DOF file is read directly during boot - just write 689297773Smarkj * two hex characters per byte. 690297773Smarkj */ 691297773Smarkj oprintf("dof-data-%d=", n); 692178476Sjb 693178476Sjb while (p < q) 694297773Smarkj oprintf("%02x", *p++); 695178476Sjb 696297773Smarkj oprintf("\n"); 697178537Sjb#else 698297773Smarkj oprintf("dof-data-%d=0x%x", n, *p++); 699178537Sjb 700178537Sjb while (p < q) 701297773Smarkj oprintf(",0x%x", *p++); 702178537Sjb 703297773Smarkj oprintf(";\n"); 704178537Sjb#endif 705178537Sjb 706178476Sjb dtrace_dof_destroy(g_dtp, dof); 707178476Sjb} 708178476Sjb 709178476Sjb/* 710178476Sjb * Link the specified D program in DOF form into an ELF file for use in either 711178476Sjb * helpers, userland provider definitions, or both. If -o was specified, that 712178476Sjb * path is used as the output file name. If -o wasn't specified and the input 713178476Sjb * program is from a script whose name is %.d, use basename(%.o) as the output 714178476Sjb * file name. Otherwise we use "d.out" as the default output file name. 715178476Sjb */ 716178476Sjbstatic void 717178476Sjblink_prog(dtrace_cmd_t *dcp) 718178476Sjb{ 719178476Sjb char *p; 720178476Sjb 721178476Sjb if (g_cmdc == 1 && g_ofile != NULL) { 722178476Sjb (void) strlcpy(dcp->dc_ofile, g_ofile, sizeof (dcp->dc_ofile)); 723178476Sjb } else if ((p = strrchr(dcp->dc_arg, '.')) != NULL && 724178476Sjb strcmp(p, ".d") == 0) { 725178476Sjb p[0] = '\0'; /* strip .d suffix */ 726178476Sjb (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), 727178476Sjb "%s.o", basename(dcp->dc_arg)); 728228598Sdim } else if (g_cmdc > 1) { 729228598Sdim (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), 730228598Sdim "d.out.%td", dcp - g_cmdv); 731178476Sjb } else { 732178476Sjb (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), 733228598Sdim "d.out"); 734178476Sjb } 735178476Sjb 736178476Sjb if (dtrace_program_link(g_dtp, dcp->dc_prog, DTRACE_D_PROBES, 737178476Sjb dcp->dc_ofile, g_objc, g_objv) != 0) 738178476Sjb dfatal("failed to link %s %s", dcp->dc_desc, dcp->dc_name); 739178476Sjb} 740178476Sjb 741178476Sjb/*ARGSUSED*/ 742178476Sjbstatic int 743178476Sjblist_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) 744178476Sjb{ 745178476Sjb dtrace_probeinfo_t p; 746178476Sjb 747178476Sjb oprintf("%5d %10s %17s %33s %s\n", pdp->dtpd_id, 748178476Sjb pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); 749178476Sjb 750178476Sjb if (g_verbose && dtrace_probe_info(dtp, pdp, &p) == 0) 751178476Sjb print_probe_info(&p); 752178476Sjb 753272455Smarkj if (g_intr != 0) 754272455Smarkj return (1); 755272455Smarkj 756178476Sjb return (0); 757178476Sjb} 758178476Sjb 759178476Sjb/*ARGSUSED*/ 760178476Sjbstatic int 761178476Sjblist_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, 762178476Sjb dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last) 763178476Sjb{ 764178476Sjb dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; 765178476Sjb 766178476Sjb if (edp == *last) 767178476Sjb return (0); 768178476Sjb 769178476Sjb if (dtrace_probe_iter(g_dtp, &edp->dted_probe, list_probe, NULL) != 0) { 770178476Sjb error("failed to match %s:%s:%s:%s: %s\n", 771178476Sjb edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod, 772178476Sjb edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name, 773178476Sjb dtrace_errmsg(dtp, dtrace_errno(dtp))); 774178476Sjb } 775178476Sjb 776178476Sjb *last = edp; 777178476Sjb return (0); 778178476Sjb} 779178476Sjb 780178476Sjb/* 781178476Sjb * List the probes corresponding to the specified program by iterating over 782178476Sjb * each statement and then matching probes to the statement probe descriptions. 783178476Sjb */ 784178476Sjbstatic void 785178476Sjblist_prog(const dtrace_cmd_t *dcp) 786178476Sjb{ 787178476Sjb dtrace_ecbdesc_t *last = NULL; 788178476Sjb 789178476Sjb (void) dtrace_stmt_iter(g_dtp, dcp->dc_prog, 790178476Sjb (dtrace_stmt_f *)list_stmt, &last); 791178476Sjb} 792178476Sjb 793178476Sjbstatic void 794178476Sjbcompile_file(dtrace_cmd_t *dcp) 795178476Sjb{ 796178476Sjb char *arg0; 797178476Sjb FILE *fp; 798178476Sjb 799178476Sjb if ((fp = fopen(dcp->dc_arg, "r")) == NULL) 800178476Sjb fatal("failed to open %s", dcp->dc_arg); 801178476Sjb 802178476Sjb arg0 = g_argv[0]; 803178476Sjb g_argv[0] = dcp->dc_arg; 804178476Sjb 805178476Sjb if ((dcp->dc_prog = dtrace_program_fcompile(g_dtp, fp, 806178476Sjb g_cflags, g_argc, g_argv)) == NULL) 807178476Sjb dfatal("failed to compile script %s", dcp->dc_arg); 808178476Sjb 809178476Sjb g_argv[0] = arg0; 810178476Sjb (void) fclose(fp); 811178476Sjb 812178476Sjb dcp->dc_desc = "script"; 813178476Sjb dcp->dc_name = dcp->dc_arg; 814178476Sjb} 815178476Sjb 816178476Sjbstatic void 817178476Sjbcompile_str(dtrace_cmd_t *dcp) 818178476Sjb{ 819178476Sjb char *p; 820178476Sjb 821178476Sjb if ((dcp->dc_prog = dtrace_program_strcompile(g_dtp, dcp->dc_arg, 822178476Sjb dcp->dc_spec, g_cflags | DTRACE_C_PSPEC, g_argc, g_argv)) == NULL) 823178476Sjb dfatal("invalid probe specifier %s", dcp->dc_arg); 824178476Sjb 825178476Sjb if ((p = strpbrk(dcp->dc_arg, "{/;")) != NULL) 826178476Sjb *p = '\0'; /* crop name for reporting */ 827178476Sjb 828178476Sjb dcp->dc_desc = "description"; 829178476Sjb dcp->dc_name = dcp->dc_arg; 830178476Sjb} 831178476Sjb 832178476Sjb/*ARGSUSED*/ 833178476Sjbstatic void 834178476Sjbprochandler(struct ps_prochandle *P, const char *msg, void *arg) 835178476Sjb{ 836277300Ssmh#ifdef illumos 837178476Sjb const psinfo_t *prp = Ppsinfo(P); 838178476Sjb int pid = Pstatus(P)->pr_pid; 839178476Sjb char name[SIG2STR_MAX]; 840211554Srpaulo#else 841211554Srpaulo int wstatus = proc_getwstat(P); 842211554Srpaulo int pid = proc_getpid(P); 843211554Srpaulo#endif 844178476Sjb 845178476Sjb if (msg != NULL) { 846178476Sjb notice("pid %d: %s\n", pid, msg); 847178476Sjb return; 848178476Sjb } 849178476Sjb 850277300Ssmh#ifdef illumos 851178476Sjb switch (Pstate(P)) { 852211554Srpaulo#else 853211554Srpaulo switch (proc_state(P)) { 854211554Srpaulo#endif 855178476Sjb case PS_UNDEAD: 856277300Ssmh#ifdef illumos 857178476Sjb /* 858178476Sjb * Ideally we would like to always report pr_wstat here, but it 859178476Sjb * isn't possible given current /proc semantics. If we grabbed 860178476Sjb * the process, Ppsinfo() will either fail or return a zeroed 861178476Sjb * psinfo_t depending on how far the parent is in reaping it. 862178476Sjb * When /proc provides a stable pr_wstat in the status file, 863178476Sjb * this code can be improved by examining this new pr_wstat. 864178476Sjb */ 865178476Sjb if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) { 866178476Sjb notice("pid %d terminated by %s\n", pid, 867178476Sjb proc_signame(WTERMSIG(prp->pr_wstat), 868178476Sjb name, sizeof (name))); 869211554Srpaulo#else 870211554Srpaulo if (WIFSIGNALED(wstatus)) { 871211554Srpaulo notice("pid %d terminated by %d\n", pid, 872211554Srpaulo WTERMSIG(wstatus)); 873211554Srpaulo#endif 874277300Ssmh#ifdef illumos 875178476Sjb } else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) { 876178476Sjb notice("pid %d exited with status %d\n", 877178476Sjb pid, WEXITSTATUS(prp->pr_wstat)); 878211554Srpaulo#else 879211554Srpaulo } else if (WEXITSTATUS(wstatus) != 0) { 880211554Srpaulo notice("pid %d exited with status %d\n", 881211554Srpaulo pid, WEXITSTATUS(wstatus)); 882211554Srpaulo#endif 883178476Sjb } else { 884178476Sjb notice("pid %d has exited\n", pid); 885178476Sjb } 886178476Sjb g_pslive--; 887178476Sjb break; 888178476Sjb 889178476Sjb case PS_LOST: 890178476Sjb notice("pid %d exec'd a set-id or unobservable program\n", pid); 891178476Sjb g_pslive--; 892178476Sjb break; 893178476Sjb } 894178476Sjb} 895178476Sjb 896178476Sjb/*ARGSUSED*/ 897178476Sjbstatic int 898178476Sjberrhandler(const dtrace_errdata_t *data, void *arg) 899178476Sjb{ 900178476Sjb error(data->dteda_msg); 901178476Sjb return (DTRACE_HANDLE_OK); 902178476Sjb} 903178476Sjb 904178476Sjb/*ARGSUSED*/ 905178476Sjbstatic int 906178476Sjbdrophandler(const dtrace_dropdata_t *data, void *arg) 907178476Sjb{ 908178476Sjb error(data->dtdda_msg); 909178476Sjb return (DTRACE_HANDLE_OK); 910178476Sjb} 911178476Sjb 912178476Sjb/*ARGSUSED*/ 913178476Sjbstatic int 914178476Sjbsetopthandler(const dtrace_setoptdata_t *data, void *arg) 915178476Sjb{ 916178476Sjb if (strcmp(data->dtsda_option, "quiet") == 0) 917178476Sjb g_quiet = data->dtsda_newval != DTRACEOPT_UNSET; 918178476Sjb 919178476Sjb if (strcmp(data->dtsda_option, "flowindent") == 0) 920178476Sjb g_flowindent = data->dtsda_newval != DTRACEOPT_UNSET; 921178476Sjb 922178476Sjb return (DTRACE_HANDLE_OK); 923178476Sjb} 924178476Sjb 925178476Sjb#define BUFDUMPHDR(hdr) \ 926178476Sjb (void) printf("%s: %s%s\n", g_pname, hdr, strlen(hdr) > 0 ? ":" : ""); 927178476Sjb 928178476Sjb#define BUFDUMPSTR(ptr, field) \ 929178476Sjb (void) printf("%s: %20s => ", g_pname, #field); \ 930178476Sjb if ((ptr)->field != NULL) { \ 931178476Sjb const char *c = (ptr)->field; \ 932178476Sjb (void) printf("\""); \ 933178476Sjb do { \ 934178476Sjb if (*c == '\n') { \ 935178476Sjb (void) printf("\\n"); \ 936178476Sjb continue; \ 937178476Sjb } \ 938178476Sjb \ 939178476Sjb (void) printf("%c", *c); \ 940178476Sjb } while (*c++ != '\0'); \ 941178476Sjb (void) printf("\"\n"); \ 942178476Sjb } else { \ 943178476Sjb (void) printf("<NULL>\n"); \ 944178476Sjb } 945178476Sjb 946178476Sjb#define BUFDUMPASSTR(ptr, field, str) \ 947178476Sjb (void) printf("%s: %20s => %s\n", g_pname, #field, str); 948178476Sjb 949178476Sjb#define BUFDUMP(ptr, field) \ 950178476Sjb (void) printf("%s: %20s => %lld\n", g_pname, #field, \ 951178476Sjb (long long)(ptr)->field); 952178476Sjb 953178476Sjb#define BUFDUMPPTR(ptr, field) \ 954178476Sjb (void) printf("%s: %20s => %s\n", g_pname, #field, \ 955178476Sjb (ptr)->field != NULL ? "<non-NULL>" : "<NULL>"); 956178476Sjb 957178476Sjb/*ARGSUSED*/ 958178476Sjbstatic int 959178476Sjbbufhandler(const dtrace_bufdata_t *bufdata, void *arg) 960178476Sjb{ 961178476Sjb const dtrace_aggdata_t *agg = bufdata->dtbda_aggdata; 962178476Sjb const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc; 963178476Sjb const dtrace_probedesc_t *pd; 964178476Sjb uint32_t flags = bufdata->dtbda_flags; 965178476Sjb char buf[512], *c = buf, *end = c + sizeof (buf); 966178476Sjb int i, printed; 967178476Sjb 968178476Sjb struct { 969178476Sjb const char *name; 970178476Sjb uint32_t value; 971178476Sjb } flagnames[] = { 972178476Sjb { "AGGVAL", DTRACE_BUFDATA_AGGVAL }, 973178476Sjb { "AGGKEY", DTRACE_BUFDATA_AGGKEY }, 974178476Sjb { "AGGFORMAT", DTRACE_BUFDATA_AGGFORMAT }, 975178476Sjb { "AGGLAST", DTRACE_BUFDATA_AGGLAST }, 976178476Sjb { "???", UINT32_MAX }, 977178476Sjb { NULL } 978178476Sjb }; 979178476Sjb 980178476Sjb if (bufdata->dtbda_probe != NULL) { 981178476Sjb pd = bufdata->dtbda_probe->dtpda_pdesc; 982178476Sjb } else if (agg != NULL) { 983178476Sjb pd = agg->dtada_pdesc; 984178476Sjb } else { 985178476Sjb pd = NULL; 986178476Sjb } 987178476Sjb 988178476Sjb BUFDUMPHDR(">>> Called buffer handler"); 989178476Sjb BUFDUMPHDR(""); 990178476Sjb 991178476Sjb BUFDUMPHDR(" dtrace_bufdata"); 992178476Sjb BUFDUMPSTR(bufdata, dtbda_buffered); 993178476Sjb BUFDUMPPTR(bufdata, dtbda_probe); 994178476Sjb BUFDUMPPTR(bufdata, dtbda_aggdata); 995178476Sjb BUFDUMPPTR(bufdata, dtbda_recdesc); 996178476Sjb 997178476Sjb (void) snprintf(c, end - c, "0x%x ", bufdata->dtbda_flags); 998178476Sjb c += strlen(c); 999178476Sjb 1000178476Sjb for (i = 0, printed = 0; flagnames[i].name != NULL; i++) { 1001178476Sjb if (!(flags & flagnames[i].value)) 1002178476Sjb continue; 1003178476Sjb 1004178476Sjb (void) snprintf(c, end - c, 1005178476Sjb "%s%s", printed++ ? " | " : "(", flagnames[i].name); 1006178476Sjb c += strlen(c); 1007178476Sjb flags &= ~flagnames[i].value; 1008178476Sjb } 1009178476Sjb 1010178476Sjb if (printed) 1011178476Sjb (void) snprintf(c, end - c, ")"); 1012178476Sjb 1013178476Sjb BUFDUMPASSTR(bufdata, dtbda_flags, buf); 1014178476Sjb BUFDUMPHDR(""); 1015178476Sjb 1016178476Sjb if (pd != NULL) { 1017178476Sjb BUFDUMPHDR(" dtrace_probedesc"); 1018178476Sjb BUFDUMPSTR(pd, dtpd_provider); 1019178476Sjb BUFDUMPSTR(pd, dtpd_mod); 1020178476Sjb BUFDUMPSTR(pd, dtpd_func); 1021178476Sjb BUFDUMPSTR(pd, dtpd_name); 1022178476Sjb BUFDUMPHDR(""); 1023178476Sjb } 1024178476Sjb 1025178476Sjb if (rec != NULL) { 1026178476Sjb BUFDUMPHDR(" dtrace_recdesc"); 1027178476Sjb BUFDUMP(rec, dtrd_action); 1028178476Sjb BUFDUMP(rec, dtrd_size); 1029178476Sjb 1030178476Sjb if (agg != NULL) { 1031178476Sjb uint8_t *data; 1032178476Sjb int lim = rec->dtrd_size; 1033178476Sjb 1034178476Sjb (void) sprintf(buf, "%d (data: ", rec->dtrd_offset); 1035178476Sjb c = buf + strlen(buf); 1036178476Sjb 1037178476Sjb if (lim > sizeof (uint64_t)) 1038178476Sjb lim = sizeof (uint64_t); 1039178476Sjb 1040178476Sjb data = (uint8_t *)agg->dtada_data + rec->dtrd_offset; 1041178476Sjb 1042178476Sjb for (i = 0; i < lim; i++) { 1043178476Sjb (void) snprintf(c, end - c, "%s%02x", 1044178476Sjb i == 0 ? "" : " ", *data++); 1045178476Sjb c += strlen(c); 1046178476Sjb } 1047178476Sjb 1048178476Sjb (void) snprintf(c, end - c, 1049178476Sjb "%s)", lim < rec->dtrd_size ? " ..." : ""); 1050178476Sjb BUFDUMPASSTR(rec, dtrd_offset, buf); 1051178476Sjb } else { 1052178476Sjb BUFDUMP(rec, dtrd_offset); 1053178476Sjb } 1054178476Sjb 1055178476Sjb BUFDUMPHDR(""); 1056178476Sjb } 1057178476Sjb 1058178476Sjb if (agg != NULL) { 1059178476Sjb dtrace_aggdesc_t *desc = agg->dtada_desc; 1060178476Sjb 1061178476Sjb BUFDUMPHDR(" dtrace_aggdesc"); 1062178476Sjb BUFDUMPSTR(desc, dtagd_name); 1063178476Sjb BUFDUMP(desc, dtagd_varid); 1064178476Sjb BUFDUMP(desc, dtagd_id); 1065178476Sjb BUFDUMP(desc, dtagd_nrecs); 1066178476Sjb BUFDUMPHDR(""); 1067178476Sjb } 1068178476Sjb 1069178476Sjb return (DTRACE_HANDLE_OK); 1070178476Sjb} 1071178476Sjb 1072178476Sjb/*ARGSUSED*/ 1073178476Sjbstatic int 1074178476Sjbchewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) 1075178476Sjb{ 1076178476Sjb dtrace_actkind_t act; 1077178476Sjb uintptr_t addr; 1078178476Sjb 1079178476Sjb if (rec == NULL) { 1080178476Sjb /* 1081178476Sjb * We have processed the final record; output the newline if 1082178476Sjb * we're not in quiet mode. 1083178476Sjb */ 1084178476Sjb if (!g_quiet) 1085178476Sjb oprintf("\n"); 1086178476Sjb 1087178476Sjb return (DTRACE_CONSUME_NEXT); 1088178476Sjb } 1089178476Sjb 1090178476Sjb act = rec->dtrd_action; 1091178476Sjb addr = (uintptr_t)data->dtpda_data; 1092178476Sjb 1093178476Sjb if (act == DTRACEACT_EXIT) { 1094178476Sjb g_status = *((uint32_t *)addr); 1095178476Sjb return (DTRACE_CONSUME_NEXT); 1096178476Sjb } 1097178476Sjb 1098178476Sjb return (DTRACE_CONSUME_THIS); 1099178476Sjb} 1100178476Sjb 1101178476Sjb/*ARGSUSED*/ 1102178476Sjbstatic int 1103178476Sjbchew(const dtrace_probedata_t *data, void *arg) 1104178476Sjb{ 1105178476Sjb dtrace_probedesc_t *pd = data->dtpda_pdesc; 1106178476Sjb processorid_t cpu = data->dtpda_cpu; 1107178476Sjb static int heading; 1108178476Sjb 1109178476Sjb if (g_impatient) { 1110178476Sjb g_newline = 0; 1111178476Sjb return (DTRACE_CONSUME_ABORT); 1112178476Sjb } 1113178476Sjb 1114178476Sjb if (heading == 0) { 1115178476Sjb if (!g_flowindent) { 1116178476Sjb if (!g_quiet) { 1117178476Sjb oprintf("%3s %6s %32s\n", 1118178476Sjb "CPU", "ID", "FUNCTION:NAME"); 1119178476Sjb } 1120178476Sjb } else { 1121178476Sjb oprintf("%3s %-41s\n", "CPU", "FUNCTION"); 1122178476Sjb } 1123178476Sjb heading = 1; 1124178476Sjb } 1125178476Sjb 1126178476Sjb if (!g_flowindent) { 1127178476Sjb if (!g_quiet) { 1128178476Sjb char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2]; 1129178476Sjb 1130178476Sjb (void) snprintf(name, sizeof (name), "%s:%s", 1131178476Sjb pd->dtpd_func, pd->dtpd_name); 1132178476Sjb 1133178476Sjb oprintf("%3d %6d %32s ", cpu, pd->dtpd_id, name); 1134178476Sjb } 1135178476Sjb } else { 1136178476Sjb int indent = data->dtpda_indent; 1137178476Sjb char *name; 1138178476Sjb size_t len; 1139178476Sjb 1140178476Sjb if (data->dtpda_flow == DTRACEFLOW_NONE) { 1141178476Sjb len = indent + DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 5; 1142178476Sjb name = alloca(len); 1143178476Sjb (void) snprintf(name, len, "%*s%s%s:%s", indent, "", 1144178476Sjb data->dtpda_prefix, pd->dtpd_func, 1145178476Sjb pd->dtpd_name); 1146178476Sjb } else { 1147178476Sjb len = indent + DTRACE_FUNCNAMELEN + 5; 1148178476Sjb name = alloca(len); 1149178476Sjb (void) snprintf(name, len, "%*s%s%s", indent, "", 1150178476Sjb data->dtpda_prefix, pd->dtpd_func); 1151178476Sjb } 1152178476Sjb 1153178476Sjb oprintf("%3d %-41s ", cpu, name); 1154178476Sjb } 1155178476Sjb 1156178476Sjb return (DTRACE_CONSUME_THIS); 1157178476Sjb} 1158178476Sjb 1159178476Sjbstatic void 1160178476Sjbgo(void) 1161178476Sjb{ 1162178476Sjb int i; 1163178476Sjb 1164178476Sjb struct { 1165178476Sjb char *name; 1166178476Sjb char *optname; 1167178476Sjb dtrace_optval_t val; 1168178476Sjb } bufs[] = { 1169178476Sjb { "buffer size", "bufsize" }, 1170178476Sjb { "aggregation size", "aggsize" }, 1171178476Sjb { "speculation size", "specsize" }, 1172178476Sjb { "dynamic variable size", "dynvarsize" }, 1173178476Sjb { NULL } 1174178476Sjb }, rates[] = { 1175178476Sjb { "cleaning rate", "cleanrate" }, 1176178476Sjb { "status rate", "statusrate" }, 1177178476Sjb { NULL } 1178178476Sjb }; 1179178476Sjb 1180178476Sjb for (i = 0; bufs[i].name != NULL; i++) { 1181178476Sjb if (dtrace_getopt(g_dtp, bufs[i].optname, &bufs[i].val) == -1) 1182178476Sjb fatal("couldn't get option %s", bufs[i].optname); 1183178476Sjb } 1184178476Sjb 1185178476Sjb for (i = 0; rates[i].name != NULL; i++) { 1186178476Sjb if (dtrace_getopt(g_dtp, rates[i].optname, &rates[i].val) == -1) 1187178476Sjb fatal("couldn't get option %s", rates[i].optname); 1188178476Sjb } 1189178476Sjb 1190178476Sjb if (dtrace_go(g_dtp) == -1) 1191178476Sjb dfatal("could not enable tracing"); 1192178476Sjb 1193178476Sjb for (i = 0; bufs[i].name != NULL; i++) { 1194178476Sjb dtrace_optval_t j = 0, mul = 10; 1195178476Sjb dtrace_optval_t nsize; 1196178476Sjb 1197178476Sjb if (bufs[i].val == DTRACEOPT_UNSET) 1198178476Sjb continue; 1199178476Sjb 1200178476Sjb (void) dtrace_getopt(g_dtp, bufs[i].optname, &nsize); 1201178476Sjb 1202178476Sjb if (nsize == DTRACEOPT_UNSET || nsize == 0) 1203178476Sjb continue; 1204178476Sjb 1205178476Sjb if (nsize >= bufs[i].val - sizeof (uint64_t)) 1206178476Sjb continue; 1207178476Sjb 1208178476Sjb for (; (INT64_C(1) << mul) <= nsize; j++, mul += 10) 1209178476Sjb continue; 1210178476Sjb 1211178476Sjb if (!(nsize & ((INT64_C(1) << (mul - 10)) - 1))) { 1212178476Sjb error("%s lowered to %lld%c\n", bufs[i].name, 1213178476Sjb (long long)nsize >> (mul - 10), " kmgtpe"[j]); 1214178476Sjb } else { 1215178476Sjb error("%s lowered to %lld bytes\n", bufs[i].name, 1216178476Sjb (long long)nsize); 1217178476Sjb } 1218178476Sjb } 1219178476Sjb 1220178476Sjb for (i = 0; rates[i].name != NULL; i++) { 1221178476Sjb dtrace_optval_t nval; 1222178476Sjb char *dir; 1223178476Sjb 1224178476Sjb if (rates[i].val == DTRACEOPT_UNSET) 1225178476Sjb continue; 1226178476Sjb 1227178476Sjb (void) dtrace_getopt(g_dtp, rates[i].optname, &nval); 1228178476Sjb 1229178476Sjb if (nval == DTRACEOPT_UNSET || nval == 0) 1230178476Sjb continue; 1231178476Sjb 1232178476Sjb if (rates[i].val == nval) 1233178476Sjb continue; 1234178476Sjb 1235178476Sjb dir = nval > rates[i].val ? "reduced" : "increased"; 1236178476Sjb 1237178476Sjb if (nval <= NANOSEC && (NANOSEC % nval) == 0) { 1238178476Sjb error("%s %s to %lld hz\n", rates[i].name, dir, 1239178476Sjb (long long)NANOSEC / (long long)nval); 1240178476Sjb continue; 1241178476Sjb } 1242178476Sjb 1243178476Sjb if ((nval % NANOSEC) == 0) { 1244178476Sjb error("%s %s to once every %lld seconds\n", 1245178476Sjb rates[i].name, dir, 1246178476Sjb (long long)nval / (long long)NANOSEC); 1247178476Sjb continue; 1248178476Sjb } 1249178476Sjb 1250178476Sjb error("%s %s to once every %lld nanoseconds\n", 1251178476Sjb rates[i].name, dir, (long long)nval); 1252178476Sjb } 1253178476Sjb} 1254178476Sjb 1255178476Sjb/*ARGSUSED*/ 1256178476Sjbstatic void 1257178476Sjbintr(int signo) 1258178476Sjb{ 1259178476Sjb if (!g_intr) 1260178476Sjb g_newline = 1; 1261178476Sjb 1262238776Sgnn if (g_intr++) 1263178476Sjb g_impatient = 1; 1264178476Sjb} 1265178476Sjb 1266306576Smarkj#ifdef __FreeBSD__ 1267272455Smarkjstatic void 1268306576Smarkjsiginfo(int signo __unused) 1269306576Smarkj{ 1270306576Smarkj 1271306576Smarkj g_siginfo++; 1272306576Smarkj g_newline = 1; 1273306576Smarkj} 1274306576Smarkj#endif 1275306576Smarkj 1276306576Smarkjstatic void 1277272455Smarkjinstallsighands(void) 1278272455Smarkj{ 1279272455Smarkj struct sigaction act, oact; 1280272455Smarkj 1281272455Smarkj (void) sigemptyset(&act.sa_mask); 1282272455Smarkj act.sa_flags = 0; 1283272455Smarkj act.sa_handler = intr; 1284272455Smarkj 1285272455Smarkj if (sigaction(SIGINT, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) 1286272455Smarkj (void) sigaction(SIGINT, &act, NULL); 1287272455Smarkj 1288272455Smarkj if (sigaction(SIGTERM, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) 1289272455Smarkj (void) sigaction(SIGTERM, &act, NULL); 1290272455Smarkj 1291306576Smarkj#ifdef __FreeBSD__ 1292272455Smarkj if (sigaction(SIGPIPE, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) 1293272455Smarkj (void) sigaction(SIGPIPE, &act, NULL); 1294272455Smarkj 1295272455Smarkj if (sigaction(SIGUSR1, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) 1296272455Smarkj (void) sigaction(SIGUSR1, &act, NULL); 1297306576Smarkj 1298306576Smarkj act.sa_handler = siginfo; 1299306576Smarkj if (sigaction(SIGINFO, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) 1300306576Smarkj (void) sigaction(SIGINFO, &act, NULL); 1301272455Smarkj#endif 1302272455Smarkj} 1303272455Smarkj 1304178476Sjbint 1305178476Sjbmain(int argc, char *argv[]) 1306178476Sjb{ 1307178476Sjb dtrace_bufdesc_t buf; 1308178476Sjb dtrace_status_t status[2]; 1309178476Sjb dtrace_optval_t opt; 1310178476Sjb dtrace_cmd_t *dcp; 1311178476Sjb 1312178537Sjb g_ofp = stdout; 1313178476Sjb int done = 0, mode = 0; 1314178537Sjb int err, i, c; 1315178537Sjb char *p, **v; 1316178476Sjb struct ps_prochandle *P; 1317178476Sjb pid_t pid; 1318178476Sjb 1319178476Sjb g_pname = basename(argv[0]); 1320178476Sjb 1321178476Sjb if (argc == 1) 1322178476Sjb return (usage(stderr)); 1323178476Sjb 1324178476Sjb if ((g_argv = malloc(sizeof (char *) * argc)) == NULL || 1325178476Sjb (g_cmdv = malloc(sizeof (dtrace_cmd_t) * argc)) == NULL || 1326178476Sjb (g_psv = malloc(sizeof (struct ps_prochandle *) * argc)) == NULL) 1327178476Sjb fatal("failed to allocate memory for arguments"); 1328178476Sjb 1329178476Sjb g_argv[g_argc++] = argv[0]; /* propagate argv[0] to D as $0/$$0 */ 1330178476Sjb argv[0] = g_pname; /* rewrite argv[0] for getopt errors */ 1331178476Sjb 1332178476Sjb bzero(status, sizeof (status)); 1333178476Sjb bzero(&buf, sizeof (buf)); 1334178476Sjb 1335178476Sjb /* 1336178476Sjb * Make an initial pass through argv[] processing any arguments that 1337178476Sjb * affect our behavior mode (g_mode) and flags used for dtrace_open(). 1338178476Sjb * We also accumulate arguments that are not affiliated with getopt 1339178476Sjb * options into g_argv[], and abort if any invalid options are found. 1340178476Sjb */ 1341178476Sjb for (optind = 1; optind < argc; optind++) { 1342178537Sjb while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { 1343178476Sjb switch (c) { 1344178476Sjb case '3': 1345178476Sjb if (strcmp(optarg, "2") != 0) { 1346178476Sjb (void) fprintf(stderr, 1347178476Sjb "%s: illegal option -- 3%s\n", 1348178476Sjb argv[0], optarg); 1349178476Sjb return (usage(stderr)); 1350178476Sjb } 1351178476Sjb g_oflags &= ~DTRACE_O_LP64; 1352178476Sjb g_oflags |= DTRACE_O_ILP32; 1353178476Sjb break; 1354178476Sjb 1355178476Sjb case '6': 1356178476Sjb if (strcmp(optarg, "4") != 0) { 1357178476Sjb (void) fprintf(stderr, 1358178476Sjb "%s: illegal option -- 6%s\n", 1359178476Sjb argv[0], optarg); 1360178476Sjb return (usage(stderr)); 1361178476Sjb } 1362178476Sjb g_oflags &= ~DTRACE_O_ILP32; 1363178476Sjb g_oflags |= DTRACE_O_LP64; 1364178476Sjb break; 1365178476Sjb 1366178476Sjb case 'a': 1367178476Sjb g_grabanon++; /* also checked in pass 2 below */ 1368178476Sjb break; 1369178476Sjb 1370178476Sjb case 'A': 1371178476Sjb g_mode = DMODE_ANON; 1372178476Sjb g_exec = 0; 1373178476Sjb mode++; 1374178476Sjb break; 1375178476Sjb 1376178476Sjb case 'e': 1377178476Sjb g_exec = 0; 1378178476Sjb done = 1; 1379178476Sjb break; 1380178476Sjb 1381178476Sjb case 'h': 1382178476Sjb g_mode = DMODE_HEADER; 1383178476Sjb g_oflags |= DTRACE_O_NODEV; 1384178476Sjb g_cflags |= DTRACE_C_ZDEFS; /* -h implies -Z */ 1385178476Sjb g_exec = 0; 1386178476Sjb mode++; 1387178476Sjb break; 1388178476Sjb 1389178476Sjb case 'G': 1390178476Sjb g_mode = DMODE_LINK; 1391178476Sjb g_oflags |= DTRACE_O_NODEV; 1392178476Sjb g_cflags |= DTRACE_C_ZDEFS; /* -G implies -Z */ 1393178476Sjb g_exec = 0; 1394178476Sjb mode++; 1395178476Sjb break; 1396178476Sjb 1397178476Sjb case 'l': 1398178476Sjb g_mode = DMODE_LIST; 1399178476Sjb g_cflags |= DTRACE_C_ZDEFS; /* -l implies -Z */ 1400178476Sjb mode++; 1401178476Sjb break; 1402178476Sjb 1403178476Sjb case 'V': 1404178476Sjb g_mode = DMODE_VERS; 1405178476Sjb mode++; 1406178476Sjb break; 1407178476Sjb 1408178476Sjb default: 1409178476Sjb if (strchr(DTRACE_OPTSTR, c) == NULL) 1410178476Sjb return (usage(stderr)); 1411178476Sjb } 1412178476Sjb } 1413178476Sjb 1414178476Sjb if (optind < argc) 1415178476Sjb g_argv[g_argc++] = argv[optind]; 1416178476Sjb } 1417178476Sjb 1418178476Sjb if (mode > 1) { 1419178476Sjb (void) fprintf(stderr, "%s: only one of the [-AGhlV] options " 1420178476Sjb "can be specified at a time\n", g_pname); 1421178476Sjb return (E_USAGE); 1422178476Sjb } 1423178476Sjb 1424178476Sjb if (g_mode == DMODE_VERS) 1425178476Sjb return (printf("%s: %s\n", g_pname, _dtrace_version) <= 0); 1426178476Sjb 1427178476Sjb /* 1428178476Sjb * If we're in linker mode and the data model hasn't been specified, 1429178476Sjb * we try to guess the appropriate setting by examining the object 1430178476Sjb * files. We ignore certain errors since we'll catch them later when 1431178476Sjb * we actually process the object files. 1432178476Sjb */ 1433178476Sjb if (g_mode == DMODE_LINK && 1434178476Sjb (g_oflags & (DTRACE_O_ILP32 | DTRACE_O_LP64)) == 0 && 1435178476Sjb elf_version(EV_CURRENT) != EV_NONE) { 1436178476Sjb int fd; 1437178476Sjb Elf *elf; 1438178476Sjb GElf_Ehdr ehdr; 1439178476Sjb 1440178476Sjb for (i = 1; i < g_argc; i++) { 1441178476Sjb if ((fd = open64(g_argv[i], O_RDONLY)) == -1) 1442178476Sjb break; 1443178476Sjb 1444178476Sjb if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 1445178476Sjb (void) close(fd); 1446178476Sjb break; 1447178476Sjb } 1448178476Sjb 1449178476Sjb if (elf_kind(elf) != ELF_K_ELF || 1450178476Sjb gelf_getehdr(elf, &ehdr) == NULL) { 1451178476Sjb (void) close(fd); 1452178476Sjb (void) elf_end(elf); 1453178476Sjb break; 1454178476Sjb } 1455178476Sjb 1456178476Sjb (void) close(fd); 1457178476Sjb (void) elf_end(elf); 1458178476Sjb 1459178476Sjb if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { 1460178476Sjb if (g_oflags & DTRACE_O_ILP32) { 1461178476Sjb fatal("can't mix 32-bit and 64-bit " 1462178476Sjb "object files\n"); 1463178476Sjb } 1464178476Sjb g_oflags |= DTRACE_O_LP64; 1465178476Sjb } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) { 1466178476Sjb if (g_oflags & DTRACE_O_LP64) { 1467178476Sjb fatal("can't mix 32-bit and 64-bit " 1468178476Sjb "object files\n"); 1469178476Sjb } 1470178476Sjb g_oflags |= DTRACE_O_ILP32; 1471178476Sjb } else { 1472178476Sjb break; 1473178476Sjb } 1474178476Sjb } 1475178476Sjb } 1476178476Sjb 1477178476Sjb /* 1478178476Sjb * Open libdtrace. If we are not actually going to be enabling any 1479178476Sjb * instrumentation attempt to reopen libdtrace using DTRACE_O_NODEV. 1480178476Sjb */ 1481178476Sjb while ((g_dtp = dtrace_open(DTRACE_VERSION, g_oflags, &err)) == NULL) { 1482178476Sjb if (!(g_oflags & DTRACE_O_NODEV) && !g_exec && !g_grabanon) { 1483178476Sjb g_oflags |= DTRACE_O_NODEV; 1484178476Sjb continue; 1485178476Sjb } 1486178476Sjb 1487178476Sjb fatal("failed to initialize dtrace: %s\n", 1488178476Sjb dtrace_errmsg(NULL, err)); 1489178476Sjb } 1490178476Sjb 1491178537Sjb#if defined(__i386__) 1492178537Sjb /* XXX The 32-bit seems to need more buffer space by default -sson */ 1493178537Sjb (void) dtrace_setopt(g_dtp, "bufsize", "12m"); 1494178537Sjb (void) dtrace_setopt(g_dtp, "aggsize", "12m"); 1495178537Sjb#else 1496178476Sjb (void) dtrace_setopt(g_dtp, "bufsize", "4m"); 1497178476Sjb (void) dtrace_setopt(g_dtp, "aggsize", "4m"); 1498178537Sjb#endif 1499250574Smarkj (void) dtrace_setopt(g_dtp, "temporal", "yes"); 1500178476Sjb 1501178476Sjb /* 1502178476Sjb * If -G is specified, enable -xlink=dynamic and -xunodefs to permit 1503178476Sjb * references to undefined symbols to remain as unresolved relocations. 1504178476Sjb * If -A is specified, enable -xlink=primary to permit static linking 1505178476Sjb * only to kernel symbols that are defined in a primary kernel module. 1506178476Sjb */ 1507178476Sjb if (g_mode == DMODE_LINK) { 1508178476Sjb (void) dtrace_setopt(g_dtp, "linkmode", "dynamic"); 1509178476Sjb (void) dtrace_setopt(g_dtp, "unodefs", NULL); 1510178476Sjb 1511178476Sjb /* 1512178476Sjb * Use the remaining arguments as the list of object files 1513178476Sjb * when in linker mode. 1514178476Sjb */ 1515178476Sjb g_objc = g_argc - 1; 1516178476Sjb g_objv = g_argv + 1; 1517178476Sjb 1518178476Sjb /* 1519178476Sjb * We still use g_argv[0], the name of the executable. 1520178476Sjb */ 1521178476Sjb g_argc = 1; 1522178476Sjb } else if (g_mode == DMODE_ANON) 1523178476Sjb (void) dtrace_setopt(g_dtp, "linkmode", "primary"); 1524178476Sjb 1525178476Sjb /* 1526178476Sjb * Now that we have libdtrace open, make a second pass through argv[] 1527178476Sjb * to perform any dtrace_setopt() calls and change any compiler flags. 1528178476Sjb * We also accumulate any program specifications into our g_cmdv[] at 1529178476Sjb * this time; these will compiled as part of the fourth processing pass. 1530178476Sjb */ 1531178476Sjb for (optind = 1; optind < argc; optind++) { 1532178537Sjb while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { 1533178476Sjb switch (c) { 1534178476Sjb case 'a': 1535178476Sjb if (dtrace_setopt(g_dtp, "grabanon", 0) != 0) 1536178476Sjb dfatal("failed to set -a"); 1537178476Sjb break; 1538178476Sjb 1539178476Sjb case 'b': 1540178476Sjb if (dtrace_setopt(g_dtp, 1541178476Sjb "bufsize", optarg) != 0) 1542178476Sjb dfatal("failed to set -b %s", optarg); 1543178476Sjb break; 1544178476Sjb 1545178476Sjb case 'B': 1546178476Sjb g_ofp = NULL; 1547178476Sjb break; 1548178476Sjb 1549178476Sjb case 'C': 1550178476Sjb g_cflags |= DTRACE_C_CPP; 1551178476Sjb break; 1552178476Sjb 1553178476Sjb case 'D': 1554178476Sjb if (dtrace_setopt(g_dtp, "define", optarg) != 0) 1555178476Sjb dfatal("failed to set -D %s", optarg); 1556178476Sjb break; 1557178476Sjb 1558178476Sjb case 'f': 1559178476Sjb dcp = &g_cmdv[g_cmdc++]; 1560178476Sjb dcp->dc_func = compile_str; 1561178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_FUNC; 1562178476Sjb dcp->dc_arg = optarg; 1563178476Sjb break; 1564178476Sjb 1565178476Sjb case 'F': 1566178476Sjb if (dtrace_setopt(g_dtp, "flowindent", 0) != 0) 1567178476Sjb dfatal("failed to set -F"); 1568178476Sjb break; 1569178476Sjb 1570178476Sjb case 'H': 1571178476Sjb if (dtrace_setopt(g_dtp, "cpphdrs", 0) != 0) 1572178476Sjb dfatal("failed to set -H"); 1573178476Sjb break; 1574178476Sjb 1575178476Sjb case 'i': 1576178476Sjb dcp = &g_cmdv[g_cmdc++]; 1577178476Sjb dcp->dc_func = compile_str; 1578178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_NAME; 1579178476Sjb dcp->dc_arg = optarg; 1580178476Sjb break; 1581178476Sjb 1582178476Sjb case 'I': 1583178476Sjb if (dtrace_setopt(g_dtp, "incdir", optarg) != 0) 1584178476Sjb dfatal("failed to set -I %s", optarg); 1585178476Sjb break; 1586178476Sjb 1587178476Sjb case 'L': 1588178476Sjb if (dtrace_setopt(g_dtp, "libdir", optarg) != 0) 1589178476Sjb dfatal("failed to set -L %s", optarg); 1590178476Sjb break; 1591178476Sjb 1592178476Sjb case 'm': 1593178476Sjb dcp = &g_cmdv[g_cmdc++]; 1594178476Sjb dcp->dc_func = compile_str; 1595178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_MOD; 1596178476Sjb dcp->dc_arg = optarg; 1597178476Sjb break; 1598178476Sjb 1599178476Sjb case 'n': 1600178476Sjb dcp = &g_cmdv[g_cmdc++]; 1601178476Sjb dcp->dc_func = compile_str; 1602178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_NAME; 1603178476Sjb dcp->dc_arg = optarg; 1604178476Sjb break; 1605178476Sjb 1606178476Sjb case 'P': 1607178476Sjb dcp = &g_cmdv[g_cmdc++]; 1608178476Sjb dcp->dc_func = compile_str; 1609178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_PROVIDER; 1610178476Sjb dcp->dc_arg = optarg; 1611178476Sjb break; 1612178476Sjb 1613178476Sjb case 'q': 1614178476Sjb if (dtrace_setopt(g_dtp, "quiet", 0) != 0) 1615178476Sjb dfatal("failed to set -q"); 1616178476Sjb break; 1617178476Sjb 1618178476Sjb case 'o': 1619178476Sjb g_ofile = optarg; 1620178476Sjb break; 1621178476Sjb 1622178476Sjb case 's': 1623178476Sjb dcp = &g_cmdv[g_cmdc++]; 1624178476Sjb dcp->dc_func = compile_file; 1625178476Sjb dcp->dc_spec = DTRACE_PROBESPEC_NONE; 1626178476Sjb dcp->dc_arg = optarg; 1627178476Sjb break; 1628178476Sjb 1629178476Sjb case 'S': 1630178476Sjb g_cflags |= DTRACE_C_DIFV; 1631178476Sjb break; 1632178476Sjb 1633178476Sjb case 'U': 1634178476Sjb if (dtrace_setopt(g_dtp, "undef", optarg) != 0) 1635178476Sjb dfatal("failed to set -U %s", optarg); 1636178476Sjb break; 1637178476Sjb 1638178476Sjb case 'v': 1639178476Sjb g_verbose++; 1640178476Sjb break; 1641178476Sjb 1642178476Sjb case 'w': 1643178476Sjb if (dtrace_setopt(g_dtp, "destructive", 0) != 0) 1644178476Sjb dfatal("failed to set -w"); 1645178476Sjb break; 1646178476Sjb 1647178476Sjb case 'x': 1648178476Sjb if ((p = strchr(optarg, '=')) != NULL) 1649178476Sjb *p++ = '\0'; 1650178476Sjb 1651178476Sjb if (dtrace_setopt(g_dtp, optarg, p) != 0) 1652178476Sjb dfatal("failed to set -x %s", optarg); 1653178476Sjb break; 1654178476Sjb 1655178476Sjb case 'X': 1656178476Sjb if (dtrace_setopt(g_dtp, "stdc", optarg) != 0) 1657178476Sjb dfatal("failed to set -X %s", optarg); 1658178476Sjb break; 1659178476Sjb 1660178476Sjb case 'Z': 1661178476Sjb g_cflags |= DTRACE_C_ZDEFS; 1662178476Sjb break; 1663178476Sjb 1664178476Sjb default: 1665178476Sjb if (strchr(DTRACE_OPTSTR, c) == NULL) 1666178476Sjb return (usage(stderr)); 1667178476Sjb } 1668178476Sjb } 1669178476Sjb } 1670178476Sjb 1671178476Sjb if (g_ofp == NULL && g_mode != DMODE_EXEC) { 1672178476Sjb (void) fprintf(stderr, "%s: -B not valid in combination" 1673178476Sjb " with [-AGl] options\n", g_pname); 1674178476Sjb return (E_USAGE); 1675178476Sjb } 1676178476Sjb 1677178476Sjb if (g_ofp == NULL && g_ofile != NULL) { 1678178476Sjb (void) fprintf(stderr, "%s: -B not valid in combination" 1679178476Sjb " with -o option\n", g_pname); 1680178476Sjb return (E_USAGE); 1681178476Sjb } 1682178476Sjb 1683178476Sjb /* 1684178476Sjb * In our third pass we handle any command-line options related to 1685178476Sjb * grabbing or creating victim processes. The behavior of these calls 1686178476Sjb * may been affected by any library options set by the second pass. 1687178476Sjb */ 1688178476Sjb for (optind = 1; optind < argc; optind++) { 1689178537Sjb while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { 1690178476Sjb switch (c) { 1691178476Sjb case 'c': 1692178476Sjb if ((v = make_argv(optarg)) == NULL) 1693178476Sjb fatal("failed to allocate memory"); 1694178476Sjb 1695184696Srodrigc P = dtrace_proc_create(g_dtp, v[0], v, NULL, NULL); 1696178476Sjb if (P == NULL) 1697178476Sjb dfatal(NULL); /* dtrace_errmsg() only */ 1698178476Sjb 1699178476Sjb g_psv[g_psc++] = P; 1700178476Sjb free(v); 1701178476Sjb break; 1702178476Sjb 1703178476Sjb case 'p': 1704178476Sjb errno = 0; 1705178476Sjb pid = strtol(optarg, &p, 10); 1706178476Sjb 1707178476Sjb if (errno != 0 || p == optarg || p[0] != '\0') 1708178476Sjb fatal("invalid pid: %s\n", optarg); 1709178476Sjb 1710178476Sjb P = dtrace_proc_grab(g_dtp, pid, 0); 1711178476Sjb if (P == NULL) 1712178476Sjb dfatal(NULL); /* dtrace_errmsg() only */ 1713178476Sjb 1714178476Sjb g_psv[g_psc++] = P; 1715178476Sjb break; 1716178476Sjb } 1717178476Sjb } 1718178476Sjb } 1719178476Sjb 1720178476Sjb /* 1721178476Sjb * In our fourth pass we finish g_cmdv[] by calling dc_func to convert 1722178476Sjb * each string or file specification into a compiled program structure. 1723178476Sjb */ 1724178476Sjb for (i = 0; i < g_cmdc; i++) 1725178476Sjb g_cmdv[i].dc_func(&g_cmdv[i]); 1726178476Sjb 1727178476Sjb if (g_mode != DMODE_LIST) { 1728178476Sjb if (dtrace_handle_err(g_dtp, &errhandler, NULL) == -1) 1729178476Sjb dfatal("failed to establish error handler"); 1730178476Sjb 1731178476Sjb if (dtrace_handle_drop(g_dtp, &drophandler, NULL) == -1) 1732178476Sjb dfatal("failed to establish drop handler"); 1733178476Sjb 1734178476Sjb if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1) 1735178476Sjb dfatal("failed to establish proc handler"); 1736178476Sjb 1737178476Sjb if (dtrace_handle_setopt(g_dtp, &setopthandler, NULL) == -1) 1738178476Sjb dfatal("failed to establish setopt handler"); 1739178476Sjb 1740178476Sjb if (g_ofp == NULL && 1741178476Sjb dtrace_handle_buffered(g_dtp, &bufhandler, NULL) == -1) 1742178476Sjb dfatal("failed to establish buffered handler"); 1743178476Sjb } 1744178476Sjb 1745178476Sjb (void) dtrace_getopt(g_dtp, "flowindent", &opt); 1746178476Sjb g_flowindent = opt != DTRACEOPT_UNSET; 1747178476Sjb 1748178476Sjb (void) dtrace_getopt(g_dtp, "grabanon", &opt); 1749178476Sjb g_grabanon = opt != DTRACEOPT_UNSET; 1750178476Sjb 1751178476Sjb (void) dtrace_getopt(g_dtp, "quiet", &opt); 1752178476Sjb g_quiet = opt != DTRACEOPT_UNSET; 1753178476Sjb 1754178476Sjb /* 1755178476Sjb * Now make a fifth and final pass over the options that have been 1756178476Sjb * turned into programs and saved in g_cmdv[], performing any mode- 1757178476Sjb * specific processing. If g_mode is DMODE_EXEC, we will break out 1758178476Sjb * of the switch() and continue on to the data processing loop. For 1759178476Sjb * other modes, we will exit dtrace once mode-specific work is done. 1760178476Sjb */ 1761178476Sjb switch (g_mode) { 1762178476Sjb case DMODE_EXEC: 1763178476Sjb if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) 1764178476Sjb fatal("failed to open output file '%s'", g_ofile); 1765178476Sjb 1766178476Sjb for (i = 0; i < g_cmdc; i++) 1767178476Sjb exec_prog(&g_cmdv[i]); 1768178476Sjb 1769178476Sjb if (done && !g_grabanon) { 1770178476Sjb dtrace_close(g_dtp); 1771178476Sjb return (g_status); 1772178476Sjb } 1773178476Sjb break; 1774178476Sjb 1775178476Sjb case DMODE_ANON: 1776178476Sjb if (g_ofile == NULL) 1777277300Ssmh#ifdef illumos 1778178476Sjb g_ofile = "/kernel/drv/dtrace.conf"; 1779178537Sjb#else 1780178537Sjb /* 1781178537Sjb * On FreeBSD, anonymous DOF data is written to 1782297773Smarkj * the DTrace DOF file. 1783178537Sjb */ 1784178537Sjb g_ofile = "/boot/dtrace.dof"; 1785178537Sjb#endif 1786178476Sjb 1787178476Sjb dof_prune(g_ofile); /* strip out any old DOF directives */ 1788277300Ssmh#ifdef illumos 1789178476Sjb etcsystem_prune(); /* string out any forceload directives */ 1790178537Sjb#endif 1791178476Sjb 1792178476Sjb if (g_cmdc == 0) { 1793178476Sjb dtrace_close(g_dtp); 1794178476Sjb return (g_status); 1795178476Sjb } 1796178476Sjb 1797178476Sjb if ((g_ofp = fopen(g_ofile, "a")) == NULL) 1798178476Sjb fatal("failed to open output file '%s'", g_ofile); 1799178476Sjb 1800178476Sjb for (i = 0; i < g_cmdc; i++) { 1801178476Sjb anon_prog(&g_cmdv[i], 1802178476Sjb dtrace_dof_create(g_dtp, g_cmdv[i].dc_prog, 0), i); 1803178476Sjb } 1804178476Sjb 1805178476Sjb /* 1806178476Sjb * Dump out the DOF corresponding to the error handler and the 1807178476Sjb * current options as the final DOF property in the .conf file. 1808178476Sjb */ 1809178476Sjb anon_prog(NULL, dtrace_geterr_dof(g_dtp), i++); 1810178476Sjb anon_prog(NULL, dtrace_getopt_dof(g_dtp), i++); 1811178476Sjb 1812178476Sjb if (fclose(g_ofp) == EOF) 1813178476Sjb fatal("failed to close output file '%s'", g_ofile); 1814178476Sjb 1815178476Sjb /* 1816178476Sjb * These messages would use notice() rather than error(), but 1817178476Sjb * we don't want them suppressed when -A is run on a D program 1818178476Sjb * that itself contains a #pragma D option quiet. 1819178476Sjb */ 1820178476Sjb error("saved anonymous enabling in %s\n", g_ofile); 1821297773Smarkj 1822297773Smarkj#ifdef __FreeBSD__ 1823297773Smarkj bootdof_add(); 1824297773Smarkj#else 1825178476Sjb etcsystem_add(); 1826178476Sjb error("run update_drv(1M) or reboot to enable changes\n"); 1827178537Sjb#endif 1828178476Sjb 1829178476Sjb dtrace_close(g_dtp); 1830178476Sjb return (g_status); 1831178476Sjb 1832178476Sjb case DMODE_LINK: 1833178476Sjb if (g_cmdc == 0) { 1834178476Sjb (void) fprintf(stderr, "%s: -G requires one or more " 1835178476Sjb "scripts or enabling options\n", g_pname); 1836178476Sjb dtrace_close(g_dtp); 1837178476Sjb return (E_USAGE); 1838178476Sjb } 1839178476Sjb 1840178476Sjb for (i = 0; i < g_cmdc; i++) 1841178476Sjb link_prog(&g_cmdv[i]); 1842178476Sjb 1843178476Sjb if (g_cmdc > 1 && g_ofile != NULL) { 1844178476Sjb char **objv = alloca(g_cmdc * sizeof (char *)); 1845178476Sjb 1846178476Sjb for (i = 0; i < g_cmdc; i++) 1847178476Sjb objv[i] = g_cmdv[i].dc_ofile; 1848178476Sjb 1849178476Sjb if (dtrace_program_link(g_dtp, NULL, DTRACE_D_PROBES, 1850178476Sjb g_ofile, g_cmdc, objv) != 0) 1851178476Sjb dfatal(NULL); /* dtrace_errmsg() only */ 1852178476Sjb } 1853178476Sjb 1854178476Sjb dtrace_close(g_dtp); 1855178476Sjb return (g_status); 1856178476Sjb 1857178476Sjb case DMODE_LIST: 1858178476Sjb if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) 1859178476Sjb fatal("failed to open output file '%s'", g_ofile); 1860178476Sjb 1861272455Smarkj installsighands(); 1862272455Smarkj 1863178476Sjb oprintf("%5s %10s %17s %33s %s\n", 1864178476Sjb "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME"); 1865178476Sjb 1866178476Sjb for (i = 0; i < g_cmdc; i++) 1867178476Sjb list_prog(&g_cmdv[i]); 1868178476Sjb 1869178476Sjb if (g_cmdc == 0) 1870178476Sjb (void) dtrace_probe_iter(g_dtp, NULL, list_probe, NULL); 1871178476Sjb 1872178476Sjb dtrace_close(g_dtp); 1873178476Sjb return (g_status); 1874178476Sjb 1875178476Sjb case DMODE_HEADER: 1876178476Sjb if (g_cmdc == 0) { 1877178476Sjb (void) fprintf(stderr, "%s: -h requires one or more " 1878178476Sjb "scripts or enabling options\n", g_pname); 1879178476Sjb dtrace_close(g_dtp); 1880178476Sjb return (E_USAGE); 1881178476Sjb } 1882178476Sjb 1883178476Sjb if (g_ofile == NULL) { 1884178476Sjb char *p; 1885178476Sjb 1886178476Sjb if (g_cmdc > 1) { 1887178476Sjb (void) fprintf(stderr, "%s: -h requires an " 1888178476Sjb "output file if multiple scripts are " 1889178476Sjb "specified\n", g_pname); 1890178476Sjb dtrace_close(g_dtp); 1891178476Sjb return (E_USAGE); 1892178476Sjb } 1893178476Sjb 1894178476Sjb if ((p = strrchr(g_cmdv[0].dc_arg, '.')) == NULL || 1895178476Sjb strcmp(p, ".d") != 0) { 1896178476Sjb (void) fprintf(stderr, "%s: -h requires an " 1897178476Sjb "output file if no scripts are " 1898178476Sjb "specified\n", g_pname); 1899178476Sjb dtrace_close(g_dtp); 1900178476Sjb return (E_USAGE); 1901178476Sjb } 1902178476Sjb 1903178476Sjb p[0] = '\0'; /* strip .d suffix */ 1904178476Sjb g_ofile = p = g_cmdv[0].dc_ofile; 1905178476Sjb (void) snprintf(p, sizeof (g_cmdv[0].dc_ofile), 1906178476Sjb "%s.h", basename(g_cmdv[0].dc_arg)); 1907178476Sjb } 1908178476Sjb 1909178476Sjb if ((g_ofp = fopen(g_ofile, "w")) == NULL) 1910178476Sjb fatal("failed to open header file '%s'", g_ofile); 1911178476Sjb 1912178476Sjb oprintf("/*\n * Generated by dtrace(1M).\n */\n\n"); 1913178476Sjb 1914178476Sjb if (dtrace_program_header(g_dtp, g_ofp, g_ofile) != 0 || 1915178476Sjb fclose(g_ofp) == EOF) 1916178476Sjb dfatal("failed to create header file %s", g_ofile); 1917178476Sjb 1918178476Sjb dtrace_close(g_dtp); 1919178476Sjb return (g_status); 1920178476Sjb } 1921178476Sjb 1922178476Sjb /* 1923178476Sjb * If -a and -Z were not specified and no probes have been matched, no 1924178476Sjb * probe criteria was specified on the command line and we abort. 1925178476Sjb */ 1926178476Sjb if (g_total == 0 && !g_grabanon && !(g_cflags & DTRACE_C_ZDEFS)) 1927178476Sjb dfatal("no probes %s\n", g_cmdc ? "matched" : "specified"); 1928178476Sjb 1929178476Sjb /* 1930178476Sjb * Start tracing. Once we dtrace_go(), reload any options that affect 1931178476Sjb * our globals in case consuming anonymous state has changed them. 1932178476Sjb */ 1933178476Sjb go(); 1934178476Sjb 1935178476Sjb (void) dtrace_getopt(g_dtp, "flowindent", &opt); 1936178476Sjb g_flowindent = opt != DTRACEOPT_UNSET; 1937178476Sjb 1938178476Sjb (void) dtrace_getopt(g_dtp, "grabanon", &opt); 1939178476Sjb g_grabanon = opt != DTRACEOPT_UNSET; 1940178476Sjb 1941178476Sjb (void) dtrace_getopt(g_dtp, "quiet", &opt); 1942178476Sjb g_quiet = opt != DTRACEOPT_UNSET; 1943178476Sjb 1944178476Sjb (void) dtrace_getopt(g_dtp, "destructive", &opt); 1945178476Sjb if (opt != DTRACEOPT_UNSET) 1946178476Sjb notice("allowing destructive actions\n"); 1947178476Sjb 1948272455Smarkj installsighands(); 1949178476Sjb 1950178476Sjb /* 1951178476Sjb * Now that tracing is active and we are ready to consume trace data, 1952178476Sjb * continue any grabbed or created processes, setting them running 1953178476Sjb * using the /proc control mechanism inside of libdtrace. 1954178476Sjb */ 1955178476Sjb for (i = 0; i < g_psc; i++) 1956178476Sjb dtrace_proc_continue(g_dtp, g_psv[i]); 1957178476Sjb 1958178476Sjb g_pslive = g_psc; /* count for prochandler() */ 1959178476Sjb 1960178476Sjb do { 1961178476Sjb if (!g_intr && !done) 1962178476Sjb dtrace_sleep(g_dtp); 1963178476Sjb 1964306576Smarkj#ifdef __FreeBSD__ 1965306576Smarkj if (g_siginfo) { 1966306576Smarkj (void)dtrace_aggregate_print(g_dtp, g_ofp, NULL); 1967306576Smarkj g_siginfo = 0; 1968306576Smarkj } 1969306576Smarkj#endif 1970306576Smarkj 1971178476Sjb if (g_newline) { 1972178476Sjb /* 1973178476Sjb * Output a newline just to make the output look 1974178476Sjb * slightly cleaner. Note that we do this even in 1975178476Sjb * "quiet" mode... 1976178476Sjb */ 1977178476Sjb oprintf("\n"); 1978178476Sjb g_newline = 0; 1979178476Sjb } 1980178476Sjb 1981178476Sjb if (done || g_intr || (g_psc != 0 && g_pslive == 0)) { 1982178476Sjb done = 1; 1983178476Sjb if (dtrace_stop(g_dtp) == -1) 1984178476Sjb dfatal("couldn't stop tracing"); 1985178476Sjb } 1986178476Sjb 1987178476Sjb switch (dtrace_work(g_dtp, g_ofp, chew, chewrec, NULL)) { 1988178476Sjb case DTRACE_WORKSTATUS_DONE: 1989178476Sjb done = 1; 1990178476Sjb break; 1991178476Sjb case DTRACE_WORKSTATUS_OKAY: 1992178476Sjb break; 1993178476Sjb default: 1994178476Sjb if (!g_impatient && dtrace_errno(g_dtp) != EINTR) 1995178476Sjb dfatal("processing aborted"); 1996178476Sjb } 1997178476Sjb 1998178476Sjb if (g_ofp != NULL && fflush(g_ofp) == EOF) 1999178476Sjb clearerr(g_ofp); 2000178476Sjb } while (!done); 2001178476Sjb 2002178476Sjb oprintf("\n"); 2003178476Sjb 2004178476Sjb if (!g_impatient) { 2005178476Sjb if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 && 2006178476Sjb dtrace_errno(g_dtp) != EINTR) 2007178476Sjb dfatal("failed to print aggregations"); 2008178476Sjb } 2009178476Sjb 2010178476Sjb dtrace_close(g_dtp); 2011178476Sjb return (g_status); 2012178476Sjb} 2013