Path: blob/main/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c
39488 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*/2021/*22* Copyright 2006 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/25/*26* Copyright (c) 2012 by Delphix. All rights reserved.27* Copyright (c) 2013, Joyent, Inc. All rights reserved.28* Copyright (c) 2023, Domagoj Stolfa. All rights reserved.29*/3031#include <sys/types.h>32#include <sys/stat.h>33#include <sys/wait.h>3435#include <dtrace.h>36#include <stdlib.h>37#include <stdarg.h>38#include <stdio.h>39#include <string.h>40#include <strings.h>41#include <unistd.h>42#include <limits.h>43#include <fcntl.h>44#include <errno.h>45#include <signal.h>46#ifdef illumos47#include <alloca.h>48#endif49#include <libgen.h>50#ifdef illumos51#include <libproc.h>52#endif53#ifdef __FreeBSD__54#include <locale.h>55#include <spawn.h>56#endif5758#undef NORETURN /* needed because libxo redefines it */59#include <libxo/xo.h>6061typedef struct dtrace_cmd {62void (*dc_func)(struct dtrace_cmd *); /* function to compile arg */63dtrace_probespec_t dc_spec; /* probe specifier context */64char *dc_arg; /* argument from main argv */65const char *dc_name; /* name for error messages */66const char *dc_desc; /* desc for error messages */67dtrace_prog_t *dc_prog; /* program compiled from arg */68char dc_ofile[PATH_MAX]; /* derived output file name */69} dtrace_cmd_t;7071#define DMODE_VERS 0 /* display version information and exit (-V) */72#define DMODE_EXEC 1 /* compile program for enabling (-a/e/E) */73#define DMODE_ANON 2 /* compile program for anonymous tracing (-A) */74#define DMODE_LINK 3 /* compile program for linking with ELF (-G) */75#define DMODE_LIST 4 /* compile program and list probes (-l) */76#define DMODE_HEADER 5 /* compile program for headergen (-h) */7778#define E_SUCCESS 079#define E_ERROR 180#define E_USAGE 28182static const char DTRACE_OPTSTR[] =83"3:6:aAb:Bc:CdD:ef:FGhHi:I:lL:m:n:o:Op:P:qs:SU:vVwx:X:Z";8485static char **g_argv;86static int g_argc;87static char **g_objv;88static int g_objc;89static dtrace_cmd_t *g_cmdv;90static int g_cmdc;91static struct ps_prochandle **g_psv;92static int g_psc;93static int g_pslive;94static char *g_pname;95static int g_quiet;96static int g_flowindent;97static int g_intr;98static int g_impatient;99static int g_newline;100#ifdef __FreeBSD__101static int g_siginfo;102#endif103static int g_total;104static int g_cflags;105static int g_oflags;106static int g_verbose;107static int g_exec = 1;108static int g_mode = DMODE_EXEC;109static int g_status = E_SUCCESS;110static int g_grabanon = 0;111static const char *g_ofile = NULL;112static FILE *g_ofp;113static dtrace_hdl_t *g_dtp;114#ifdef illumos115static char *g_etcfile = "/etc/system";116static const char *g_etcbegin = "* vvvv Added by DTrace";117static const char *g_etcend = "* ^^^^ Added by DTrace";118119static const char *g_etc[] = {120"*",121"* The following forceload directives were added by dtrace(1M) to allow for",122"* tracing during boot. If these directives are removed, the system will",123"* continue to function, but tracing will not occur during boot as desired.",124"* To remove these directives (and this block comment) automatically, run",125"* \"dtrace -A\" without additional arguments. See the \"Anonymous Tracing\"",126"* chapter of the Solaris Dynamic Tracing Guide for details.",127"*",128NULL };129#endif130131static int132usage(FILE *fp)133{134static const char predact[] = "[[ predicate ] action ]";135136(void) fprintf(fp, "Usage: %s [-32|-64] [-aACdeFGhHlqSvVwZ] "137"[-b bufsz] [-c cmd] [-D name[=def]]\n\t[-I path] [-L path] "138"[-o output] [-p pid] [-s script] [-U name]\n\t"139"[-x opt[=val]] [-X a|c|s|t]\n\n"140"\t[-P provider %s]\n"141"\t[-m [ provider: ] module %s]\n"142"\t[-f [[ provider: ] module: ] func %s]\n"143"\t[-n [[[ provider: ] module: ] func: ] name %s]\n"144"\t[-i probe-id %s] [ args ... ]\n\n", g_pname,145predact, predact, predact, predact, predact);146147(void) fprintf(fp, "\tpredicate -> '/' D-expression '/'\n");148(void) fprintf(fp, "\t action -> '{' D-statements '}'\n");149150(void) fprintf(fp, "\n"151"\t-32 generate 32-bit D programs and ELF files\n"152"\t-64 generate 64-bit D programs and ELF files\n\n"153"\t-a claim anonymous tracing state\n"154"\t-A generate driver.conf(4) directives for anonymous tracing\n"155"\t-b set trace buffer size\n"156"\t-c run specified command and exit upon its completion\n"157"\t-C run cpp(1) preprocessor on script files\n"158"\t-d dump script after syntactic transformations\n"159"\t-D define symbol when invoking preprocessor\n"160"\t-e exit after compiling request but prior to enabling probes\n"161"\t-f enable or list probes matching the specified function name\n"162"\t-F coalesce trace output by function\n"163"\t-G generate an ELF file containing embedded dtrace program\n"164"\t-h generate a header file with definitions for static probes\n"165"\t-H print included files when invoking preprocessor\n"166"\t-i enable or list probes matching the specified probe id\n"167"\t-I add include directory to preprocessor search path\n"168"\t-l list probes matching specified criteria\n"169"\t-L add library directory to library search path\n"170"\t-m enable or list probes matching the specified module name\n"171"\t-n enable or list probes matching the specified probe name\n"172"\t-o set output file\n"173"\t-O print output upon exiting (specific to oformat)\n"174"\t-p grab specified process-ID and cache its symbol tables\n"175"\t-P enable or list probes matching the specified provider name\n"176"\t-q set quiet mode (only output explicitly traced data)\n"177"\t-s enable or list probes according to the specified D script\n"178"\t-S print D compiler intermediate code\n"179"\t-U undefine symbol when invoking preprocessor\n"180"\t-v set verbose mode (report stability attributes, arguments)\n"181"\t-V report DTrace API version\n"182"\t-w permit destructive actions\n"183"\t-x enable or modify compiler and tracing options\n"184"\t-X specify ISO C conformance settings for preprocessor\n"185"\t-Z permit probe descriptions that match zero probes\n");186187return (E_USAGE);188}189190static void191verror(const char *fmt, va_list ap)192{193int error = errno;194195(void) fprintf(stderr, "%s: ", g_pname);196(void) vfprintf(stderr, fmt, ap);197198if (fmt[strlen(fmt) - 1] != '\n')199(void) fprintf(stderr, ": %s\n", strerror(error));200}201202/*PRINTFLIKE1*/203static void204fatal(const char *fmt, ...)205{206va_list ap;207208va_start(ap, fmt);209verror(fmt, ap);210va_end(ap);211212/*213* Close the DTrace handle to ensure that any controlled processes are214* correctly restored and continued.215*/216if (g_dtp)217dtrace_close(g_dtp);218219exit(E_ERROR);220}221222/*PRINTFLIKE1*/223static void224dfatal(const char *fmt, ...)225{226#if !defined(illumos) && defined(NEED_ERRLOC)227char *p_errfile = NULL;228int errline = 0;229#endif230va_list ap;231232va_start(ap, fmt);233234(void) fprintf(stderr, "%s: ", g_pname);235if (fmt != NULL)236(void) vfprintf(stderr, fmt, ap);237238va_end(ap);239240if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') {241(void) fprintf(stderr, ": %s\n",242dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));243} else if (fmt == NULL) {244(void) fprintf(stderr, "%s\n",245dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));246}247#if !defined(illumos) && defined(NEED_ERRLOC)248dt_get_errloc(g_dtp, &p_errfile, &errline);249if (p_errfile != NULL)250printf("File '%s', line %d\n", p_errfile, errline);251#endif252253/*254* Close the DTrace handle to ensure that any controlled processes are255* correctly restored and continued.256*/257dtrace_close(g_dtp);258259exit(E_ERROR);260}261262/*PRINTFLIKE1*/263static void264error(const char *fmt, ...)265{266va_list ap;267268va_start(ap, fmt);269verror(fmt, ap);270va_end(ap);271}272273/*PRINTFLIKE1*/274static void275notice(const char *fmt, ...)276{277va_list ap;278279if (g_quiet)280return; /* -q or quiet pragma suppresses notice()s */281282va_start(ap, fmt);283verror(fmt, ap);284va_end(ap);285}286287/*PRINTFLIKE1*/288static void289oprintf(const char *fmt, ...)290{291va_list ap;292int n;293294if (g_ofp == NULL)295return;296297va_start(ap, fmt);298n = vfprintf(g_ofp, fmt, ap);299va_end(ap);300301if (n < 0) {302if (errno != EINTR) {303fatal("failed to write to %s",304g_ofile ? g_ofile : "<stdout>");305}306clearerr(g_ofp);307}308}309310static char **311make_argv(char *s)312{313const char *ws = "\f\n\r\t\v ";314char **argv = malloc(sizeof (char *) * (strlen(s) / 2 + 1));315int argc = 0;316char *p = s;317318if (argv == NULL)319return (NULL);320321for (p = strtok(s, ws); p != NULL; p = strtok(NULL, ws))322argv[argc++] = p;323324if (argc == 0)325argv[argc++] = s;326327argv[argc] = NULL;328return (argv);329}330331static void332dof_prune(const char *fname)333{334struct stat sbuf;335size_t sz, i, j, mark, len;336char *buf;337int msg = 0, fd;338339if ((fd = open(fname, O_RDONLY)) == -1) {340/*341* This is okay only if the file doesn't exist at all.342*/343if (errno != ENOENT)344fatal("failed to open %s", fname);345return;346}347348if (fstat(fd, &sbuf) == -1)349fatal("failed to fstat %s", fname);350351if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL)352fatal("failed to allocate memory for %s", fname);353354if (read(fd, buf, sz) != sz)355fatal("failed to read %s", fname);356357buf[sz] = '\0';358(void) close(fd);359360if ((fd = open(fname, O_WRONLY | O_TRUNC)) == -1)361fatal("failed to open %s for writing", fname);362363len = strlen("dof-data-");364365for (mark = 0, i = 0; i < sz; i++) {366if (strncmp(&buf[i], "dof-data-", len) != 0)367continue;368369/*370* This is only a match if it's in the 0th column.371*/372if (i != 0 && buf[i - 1] != '\n')373continue;374375if (msg++ == 0) {376error("cleaned up old anonymous "377"enabling in %s\n", fname);378}379380/*381* We have a match. First write out our data up until now.382*/383if (i != mark) {384if (write(fd, &buf[mark], i - mark) != i - mark)385fatal("failed to write to %s", fname);386}387388/*389* Now scan forward until we scan past a newline.390*/391for (j = i; j < sz && buf[j] != '\n'; j++)392continue;393394/*395* Reset our mark.396*/397if ((mark = j + 1) >= sz)398break;399400i = j;401}402403if (mark < sz) {404if (write(fd, &buf[mark], sz - mark) != sz - mark)405fatal("failed to write to %s", fname);406}407408(void) close(fd);409free(buf);410}411412#ifdef __FreeBSD__413/*414* Use nextboot(8) to tell the loader to load DTrace kernel modules during415* the next boot of the system. The nextboot(8) configuration is removed during416* boot, so it will not persist indefinitely.417*/418static void419bootdof_add(void)420{421char * const nbargv[] = {422"nextboot", "-a",423"-e", "dtraceall_load=\"YES\"",424"-e", "dtrace_dof_load=\"YES\"",425"-e", "dtrace_dof_name=\"/boot/dtrace.dof\"",426"-e", "dtrace_dof_type=\"dtrace_dof\"",427NULL,428};429pid_t child;430int err, status;431432err = posix_spawnp(&child, "nextboot", NULL, NULL, nbargv,433NULL);434if (err != 0) {435error("failed to execute nextboot: %s", strerror(err));436exit(E_ERROR);437}438439if (waitpid(child, &status, 0) != child)440fatal("waiting for nextboot");441if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {442error("nextboot returned with status %d", status);443exit(E_ERROR);444}445}446#else447static void448etcsystem_prune(void)449{450struct stat sbuf;451size_t sz;452char *buf, *start, *end;453int fd;454char *fname = g_etcfile, *tmpname;455456if ((fd = open(fname, O_RDONLY)) == -1)457fatal("failed to open %s", fname);458459if (fstat(fd, &sbuf) == -1)460fatal("failed to fstat %s", fname);461462if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL)463fatal("failed to allocate memory for %s", fname);464465if (read(fd, buf, sz) != sz)466fatal("failed to read %s", fname);467468buf[sz] = '\0';469(void) close(fd);470471if ((start = strstr(buf, g_etcbegin)) == NULL)472goto out;473474if (strlen(buf) != sz) {475fatal("embedded nul byte in %s; manual repair of %s "476"required\n", fname, fname);477}478479if (strstr(start + 1, g_etcbegin) != NULL) {480fatal("multiple start sentinels in %s; manual repair of %s "481"required\n", fname, fname);482}483484if ((end = strstr(buf, g_etcend)) == NULL) {485fatal("missing end sentinel in %s; manual repair of %s "486"required\n", fname, fname);487}488489if (start > end) {490fatal("end sentinel preceeds start sentinel in %s; manual "491"repair of %s required\n", fname, fname);492}493494end += strlen(g_etcend) + 1;495bcopy(end, start, strlen(end) + 1);496497tmpname = alloca(sz = strlen(fname) + 80);498(void) snprintf(tmpname, sz, "%s.dtrace.%d", fname, getpid());499500if ((fd = open(tmpname,501O_WRONLY | O_CREAT | O_EXCL, sbuf.st_mode)) == -1)502fatal("failed to create %s", tmpname);503504if (write(fd, buf, strlen(buf)) < strlen(buf)) {505(void) unlink(tmpname);506fatal("failed to write to %s", tmpname);507}508509(void) close(fd);510511if (chown(tmpname, sbuf.st_uid, sbuf.st_gid) != 0) {512(void) unlink(tmpname);513fatal("failed to chown(2) %s to uid %d, gid %d", tmpname,514(int)sbuf.st_uid, (int)sbuf.st_gid);515}516517if (rename(tmpname, fname) == -1)518fatal("rename of %s to %s failed", tmpname, fname);519520error("cleaned up forceload directives in %s\n", fname);521out:522free(buf);523}524525static void526etcsystem_add(void)527{528const char *mods[20];529int nmods, line;530531if ((g_ofp = fopen(g_ofile = g_etcfile, "a")) == NULL)532fatal("failed to open output file '%s'", g_ofile);533534oprintf("%s\n", g_etcbegin);535536for (line = 0; g_etc[line] != NULL; line++)537oprintf("%s\n", g_etc[line]);538539nmods = dtrace_provider_modules(g_dtp, mods,540sizeof (mods) / sizeof (char *) - 1);541542if (nmods >= sizeof (mods) / sizeof (char *))543fatal("unexpectedly large number of modules!");544545mods[nmods++] = "dtrace";546547for (line = 0; line < nmods; line++)548oprintf("forceload: drv/%s\n", mods[line]);549550oprintf("%s\n", g_etcend);551552if (fclose(g_ofp) == EOF)553fatal("failed to close output file '%s'", g_ofile);554555error("added forceload directives to %s\n", g_ofile);556}557#endif /* !__FreeBSD__ */558559static void560print_probe_info(const dtrace_probeinfo_t *p)561{562char buf[BUFSIZ];563char *user;564int i;565566oprintf("\n\tProbe Description Attributes\n");567568oprintf("\t\tIdentifier Names: %s\n",569dtrace_stability_name(p->dtp_attr.dtat_name));570oprintf("\t\tData Semantics: %s\n",571dtrace_stability_name(p->dtp_attr.dtat_data));572oprintf("\t\tDependency Class: %s\n",573dtrace_class_name(p->dtp_attr.dtat_class));574575oprintf("\n\tArgument Attributes\n");576577oprintf("\t\tIdentifier Names: %s\n",578dtrace_stability_name(p->dtp_arga.dtat_name));579oprintf("\t\tData Semantics: %s\n",580dtrace_stability_name(p->dtp_arga.dtat_data));581oprintf("\t\tDependency Class: %s\n",582dtrace_class_name(p->dtp_arga.dtat_class));583584oprintf("\n\tArgument Types\n");585586for (i = 0; i < p->dtp_argc; i++) {587if (p->dtp_argv[i].dtt_flags & DTT_FL_USER)588user = "userland ";589else590user = "";591if (ctf_type_name(p->dtp_argv[i].dtt_ctfp,592p->dtp_argv[i].dtt_type, buf, sizeof (buf)) == NULL)593(void) strlcpy(buf, "(unknown)", sizeof (buf));594oprintf("\t\targs[%d]: %s%s\n", i, user, buf);595}596597if (p->dtp_argc == 0)598oprintf("\t\tNone\n");599600oprintf("\n");601}602603/*ARGSUSED*/604static int605info_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,606dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last)607{608dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc;609dtrace_probedesc_t *pdp = &edp->dted_probe;610dtrace_probeinfo_t p;611612if (edp == *last)613return (0);614615oprintf("\n%s:%s:%s:%s\n",616pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name);617618if (dtrace_probe_info(dtp, pdp, &p) == 0)619print_probe_info(&p);620621*last = edp;622return (0);623}624625/*626* Execute the specified program by enabling the corresponding instrumentation.627* If -e has been specified, we get the program info but do not enable it. If628* -v has been specified, we print a stability report for the program.629*/630static void631exec_prog(const dtrace_cmd_t *dcp)632{633dtrace_ecbdesc_t *last = NULL;634dtrace_proginfo_t dpi;635636if (!g_exec) {637dtrace_program_info(g_dtp, dcp->dc_prog, &dpi);638} else if (dtrace_program_exec(g_dtp, dcp->dc_prog, &dpi) == -1) {639dfatal("failed to enable '%s'", dcp->dc_name);640} else {641notice("%s '%s' matched %u probe%s\n",642dcp->dc_desc, dcp->dc_name,643dpi.dpi_matches, dpi.dpi_matches == 1 ? "" : "s");644}645646if (g_verbose) {647oprintf("\nStability attributes for %s %s:\n",648dcp->dc_desc, dcp->dc_name);649650oprintf("\n\tMinimum Probe Description Attributes\n");651oprintf("\t\tIdentifier Names: %s\n",652dtrace_stability_name(dpi.dpi_descattr.dtat_name));653oprintf("\t\tData Semantics: %s\n",654dtrace_stability_name(dpi.dpi_descattr.dtat_data));655oprintf("\t\tDependency Class: %s\n",656dtrace_class_name(dpi.dpi_descattr.dtat_class));657658oprintf("\n\tMinimum Statement Attributes\n");659660oprintf("\t\tIdentifier Names: %s\n",661dtrace_stability_name(dpi.dpi_stmtattr.dtat_name));662oprintf("\t\tData Semantics: %s\n",663dtrace_stability_name(dpi.dpi_stmtattr.dtat_data));664oprintf("\t\tDependency Class: %s\n",665dtrace_class_name(dpi.dpi_stmtattr.dtat_class));666667if (!g_exec) {668(void) dtrace_stmt_iter(g_dtp, dcp->dc_prog,669(dtrace_stmt_f *)info_stmt, &last);670} else671oprintf("\n");672}673674g_total += dpi.dpi_matches;675}676677/*678* Print out the specified DOF buffer as a set of ASCII bytes appropriate for679* storing in a driver.conf(4) file associated with the dtrace driver.680*/681static void682anon_prog(const dtrace_cmd_t *dcp, dof_hdr_t *dof, int n)683{684const uchar_t *p, *q;685686if (dof == NULL)687dfatal("failed to create DOF image for '%s'", dcp->dc_name);688689p = (uchar_t *)dof;690q = p + dof->dofh_filesz;691692#ifdef __FreeBSD__693/*694* On FreeBSD, the DOF file is read directly during boot - just write695* two hex characters per byte.696*/697oprintf("dof-data-%d=", n);698699while (p < q)700oprintf("%02x", *p++);701702oprintf("\n");703#else704oprintf("dof-data-%d=0x%x", n, *p++);705706while (p < q)707oprintf(",0x%x", *p++);708709oprintf(";\n");710#endif711712dtrace_dof_destroy(g_dtp, dof);713}714715/*716* Link the specified D program in DOF form into an ELF file for use in either717* helpers, userland provider definitions, or both. If -o was specified, that718* path is used as the output file name. If -o wasn't specified and the input719* program is from a script whose name is %.d, use basename(%.o) as the output720* file name. Otherwise we use "d.out" as the default output file name.721*/722static void723link_prog(dtrace_cmd_t *dcp)724{725char *p;726727if (g_cmdc == 1 && g_ofile != NULL) {728(void) strlcpy(dcp->dc_ofile, g_ofile, sizeof (dcp->dc_ofile));729} else if ((p = strrchr(dcp->dc_arg, '.')) != NULL &&730strcmp(p, ".d") == 0) {731p[0] = '\0'; /* strip .d suffix */732(void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile),733"%s.o", basename(dcp->dc_arg));734} else if (g_cmdc > 1) {735(void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile),736"d.out.%td", dcp - g_cmdv);737} else {738(void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile),739"d.out");740}741742if (dtrace_program_link(g_dtp, dcp->dc_prog, DTRACE_D_PROBES,743dcp->dc_ofile, g_objc, g_objv) != 0)744dfatal("failed to link %s %s", dcp->dc_desc, dcp->dc_name);745}746747/*ARGSUSED*/748static int749list_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg)750{751dtrace_probeinfo_t p;752753oprintf("%5d %10s %17s %33s %s\n", pdp->dtpd_id,754pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name);755756if (g_verbose && dtrace_probe_info(dtp, pdp, &p) == 0)757print_probe_info(&p);758759if (g_intr != 0)760return (1);761762return (0);763}764765/*ARGSUSED*/766static int767list_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,768dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last)769{770dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc;771772if (edp == *last)773return (0);774775if (dtrace_probe_iter(g_dtp, &edp->dted_probe, list_probe, NULL) != 0) {776error("failed to match %s:%s:%s:%s: %s\n",777edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod,778edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name,779dtrace_errmsg(dtp, dtrace_errno(dtp)));780}781782*last = edp;783return (0);784}785786/*787* List the probes corresponding to the specified program by iterating over788* each statement and then matching probes to the statement probe descriptions.789*/790static void791list_prog(const dtrace_cmd_t *dcp)792{793dtrace_ecbdesc_t *last = NULL;794795(void) dtrace_stmt_iter(g_dtp, dcp->dc_prog,796(dtrace_stmt_f *)list_stmt, &last);797}798799static void800compile_file(dtrace_cmd_t *dcp)801{802char *arg0;803FILE *fp;804805if ((fp = fopen(dcp->dc_arg, "r")) == NULL)806fatal("failed to open %s", dcp->dc_arg);807808arg0 = g_argv[0];809g_argv[0] = dcp->dc_arg;810811if ((dcp->dc_prog = dtrace_program_fcompile(g_dtp, fp,812g_cflags, g_argc, g_argv)) == NULL)813dfatal("failed to compile script %s", dcp->dc_arg);814815g_argv[0] = arg0;816(void) fclose(fp);817818dcp->dc_desc = "script";819dcp->dc_name = dcp->dc_arg;820}821822static void823compile_str(dtrace_cmd_t *dcp)824{825char *p;826827if ((dcp->dc_prog = dtrace_program_strcompile(g_dtp, dcp->dc_arg,828dcp->dc_spec, g_cflags | DTRACE_C_PSPEC, g_argc, g_argv)) == NULL)829dfatal("invalid probe specifier %s", dcp->dc_arg);830831if ((p = strpbrk(dcp->dc_arg, "{/;")) != NULL)832*p = '\0'; /* crop name for reporting */833834dcp->dc_desc = "description";835dcp->dc_name = dcp->dc_arg;836}837838/*ARGSUSED*/839static void840prochandler(struct ps_prochandle *P, const char *msg, void *arg)841{842#ifdef illumos843const psinfo_t *prp = Ppsinfo(P);844int pid = Pstatus(P)->pr_pid;845char name[SIG2STR_MAX];846#else847int wstatus = proc_getwstat(P);848int pid = proc_getpid(P);849#endif850851if (msg != NULL) {852notice("pid %d: %s\n", pid, msg);853return;854}855856#ifdef illumos857switch (Pstate(P)) {858#else859switch (proc_state(P)) {860#endif861case PS_UNDEAD:862#ifdef illumos863/*864* Ideally we would like to always report pr_wstat here, but it865* isn't possible given current /proc semantics. If we grabbed866* the process, Ppsinfo() will either fail or return a zeroed867* psinfo_t depending on how far the parent is in reaping it.868* When /proc provides a stable pr_wstat in the status file,869* this code can be improved by examining this new pr_wstat.870*/871if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) {872notice("pid %d terminated by %s\n", pid,873proc_signame(WTERMSIG(prp->pr_wstat),874name, sizeof (name)));875#else876if (WIFSIGNALED(wstatus)) {877notice("pid %d terminated by %d\n", pid,878WTERMSIG(wstatus));879#endif880#ifdef illumos881} else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) {882notice("pid %d exited with status %d\n",883pid, WEXITSTATUS(prp->pr_wstat));884#else885} else if (WEXITSTATUS(wstatus) != 0) {886notice("pid %d exited with status %d\n",887pid, WEXITSTATUS(wstatus));888#endif889} else {890notice("pid %d has exited\n", pid);891}892g_pslive--;893break;894895case PS_LOST:896notice("pid %d exec'd a set-id or unobservable program\n", pid);897g_pslive--;898break;899}900}901902/*ARGSUSED*/903static int904errhandler(const dtrace_errdata_t *data, void *arg)905{906error(data->dteda_msg);907return (DTRACE_HANDLE_OK);908}909910/*ARGSUSED*/911static int912drophandler(const dtrace_dropdata_t *data, void *arg)913{914if (!dtrace_oformat(g_dtp)) {915error(data->dtdda_msg);916}917918return (DTRACE_HANDLE_OK);919}920921/*ARGSUSED*/922static int923setopthandler(const dtrace_setoptdata_t *data, void *arg)924{925if (strcmp(data->dtsda_option, "quiet") == 0)926g_quiet = data->dtsda_newval != DTRACEOPT_UNSET;927928if (strcmp(data->dtsda_option, "flowindent") == 0)929g_flowindent = data->dtsda_newval != DTRACEOPT_UNSET;930931return (DTRACE_HANDLE_OK);932}933934#define BUFDUMPHDR(hdr) \935(void) printf("%s: %s%s\n", g_pname, hdr, strlen(hdr) > 0 ? ":" : "");936937#define BUFDUMPSTR(ptr, field) \938(void) printf("%s: %20s => ", g_pname, #field); \939if ((ptr)->field != NULL) { \940const char *c = (ptr)->field; \941(void) printf("\""); \942do { \943if (*c == '\n') { \944(void) printf("\\n"); \945continue; \946} \947\948(void) printf("%c", *c); \949} while (*c++ != '\0'); \950(void) printf("\"\n"); \951} else { \952(void) printf("<NULL>\n"); \953}954955#define BUFDUMPASSTR(ptr, field, str) \956(void) printf("%s: %20s => %s\n", g_pname, #field, str);957958#define BUFDUMP(ptr, field) \959(void) printf("%s: %20s => %lld\n", g_pname, #field, \960(long long)(ptr)->field);961962#define BUFDUMPPTR(ptr, field) \963(void) printf("%s: %20s => %s\n", g_pname, #field, \964(ptr)->field != NULL ? "<non-NULL>" : "<NULL>");965966/*ARGSUSED*/967static int968bufhandler(const dtrace_bufdata_t *bufdata, void *arg)969{970const dtrace_aggdata_t *agg = bufdata->dtbda_aggdata;971const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc;972const dtrace_probedesc_t *pd;973uint32_t flags = bufdata->dtbda_flags;974char buf[512], *c = buf, *end = c + sizeof (buf);975int i, printed;976977struct {978const char *name;979uint32_t value;980} flagnames[] = {981{ "AGGVAL", DTRACE_BUFDATA_AGGVAL },982{ "AGGKEY", DTRACE_BUFDATA_AGGKEY },983{ "AGGFORMAT", DTRACE_BUFDATA_AGGFORMAT },984{ "AGGLAST", DTRACE_BUFDATA_AGGLAST },985{ "???", UINT32_MAX },986{ NULL }987};988989if (bufdata->dtbda_probe != NULL) {990pd = bufdata->dtbda_probe->dtpda_pdesc;991} else if (agg != NULL) {992pd = agg->dtada_pdesc;993} else {994pd = NULL;995}996997BUFDUMPHDR(">>> Called buffer handler");998BUFDUMPHDR("");9991000BUFDUMPHDR(" dtrace_bufdata");1001BUFDUMPSTR(bufdata, dtbda_buffered);1002BUFDUMPPTR(bufdata, dtbda_probe);1003BUFDUMPPTR(bufdata, dtbda_aggdata);1004BUFDUMPPTR(bufdata, dtbda_recdesc);10051006(void) snprintf(c, end - c, "0x%x ", bufdata->dtbda_flags);1007c += strlen(c);10081009for (i = 0, printed = 0; flagnames[i].name != NULL; i++) {1010if (!(flags & flagnames[i].value))1011continue;10121013(void) snprintf(c, end - c,1014"%s%s", printed++ ? " | " : "(", flagnames[i].name);1015c += strlen(c);1016flags &= ~flagnames[i].value;1017}10181019if (printed)1020(void) snprintf(c, end - c, ")");10211022BUFDUMPASSTR(bufdata, dtbda_flags, buf);1023BUFDUMPHDR("");10241025if (pd != NULL) {1026BUFDUMPHDR(" dtrace_probedesc");1027BUFDUMPSTR(pd, dtpd_provider);1028BUFDUMPSTR(pd, dtpd_mod);1029BUFDUMPSTR(pd, dtpd_func);1030BUFDUMPSTR(pd, dtpd_name);1031BUFDUMPHDR("");1032}10331034if (rec != NULL) {1035BUFDUMPHDR(" dtrace_recdesc");1036BUFDUMP(rec, dtrd_action);1037BUFDUMP(rec, dtrd_size);10381039if (agg != NULL) {1040uint8_t *data;1041int lim = rec->dtrd_size;10421043(void) sprintf(buf, "%d (data: ", rec->dtrd_offset);1044c = buf + strlen(buf);10451046if (lim > sizeof (uint64_t))1047lim = sizeof (uint64_t);10481049data = (uint8_t *)agg->dtada_data + rec->dtrd_offset;10501051for (i = 0; i < lim; i++) {1052(void) snprintf(c, end - c, "%s%02x",1053i == 0 ? "" : " ", *data++);1054c += strlen(c);1055}10561057(void) snprintf(c, end - c,1058"%s)", lim < rec->dtrd_size ? " ..." : "");1059BUFDUMPASSTR(rec, dtrd_offset, buf);1060} else {1061BUFDUMP(rec, dtrd_offset);1062}10631064BUFDUMPHDR("");1065}10661067if (agg != NULL) {1068dtrace_aggdesc_t *desc = agg->dtada_desc;10691070BUFDUMPHDR(" dtrace_aggdesc");1071BUFDUMPSTR(desc, dtagd_name);1072BUFDUMP(desc, dtagd_varid);1073BUFDUMP(desc, dtagd_id);1074BUFDUMP(desc, dtagd_nrecs);1075BUFDUMPHDR("");1076}10771078return (DTRACE_HANDLE_OK);1079}10801081/*ARGSUSED*/1082static int1083chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg)1084{1085dtrace_actkind_t act;1086uintptr_t addr;10871088if (rec == NULL) {1089/*1090* We have processed the final record; output the newline if1091* we're not in quiet mode.1092*/1093if (!g_quiet)1094oprintf("\n");10951096return (DTRACE_CONSUME_NEXT);1097}10981099act = rec->dtrd_action;1100addr = (uintptr_t)data->dtpda_data;11011102if (act == DTRACEACT_EXIT) {1103g_status = *((uint32_t *)addr);1104return (DTRACE_CONSUME_NEXT);1105}11061107return (DTRACE_CONSUME_THIS);1108}11091110/*ARGSUSED*/1111static int1112chew(const dtrace_probedata_t *data, void *arg)1113{1114dtrace_probedesc_t *pd = data->dtpda_pdesc;1115processorid_t cpu = data->dtpda_cpu;1116static int heading;11171118if (g_impatient) {1119g_newline = 0;1120return (DTRACE_CONSUME_ABORT);1121}11221123if (heading == 0) {1124if (!g_flowindent) {1125if (!g_quiet) {1126oprintf("%3s %6s %32s\n",1127"CPU", "ID", "FUNCTION:NAME");1128}1129} else {1130oprintf("%3s %-41s\n", "CPU", "FUNCTION");1131}1132heading = 1;1133}11341135if (!g_flowindent) {1136if (dtrace_oformat(g_dtp)) {1137dtrace_oformat_probe(g_dtp, data, cpu, pd);1138} else if (!g_quiet) {1139char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2];11401141(void) snprintf(name, sizeof (name), "%s:%s",1142pd->dtpd_func, pd->dtpd_name);11431144oprintf("%3d %6d %32s ", cpu, pd->dtpd_id, name);1145}1146} else {1147int indent = data->dtpda_indent;1148char *name;1149size_t len;11501151if (data->dtpda_flow == DTRACEFLOW_NONE) {1152len = indent + DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 5;1153name = alloca(len);1154(void) snprintf(name, len, "%*s%s%s:%s", indent, "",1155data->dtpda_prefix, pd->dtpd_func,1156pd->dtpd_name);1157} else {1158len = indent + DTRACE_FUNCNAMELEN + 5;1159name = alloca(len);1160(void) snprintf(name, len, "%*s%s%s", indent, "",1161data->dtpda_prefix, pd->dtpd_func);1162}11631164oprintf("%3d %-41s ", cpu, name);1165}11661167return (DTRACE_CONSUME_THIS);1168}11691170static void1171go(void)1172{1173int i;11741175struct {1176char *name;1177char *optname;1178dtrace_optval_t val;1179} bufs[] = {1180{ "buffer size", "bufsize" },1181{ "aggregation size", "aggsize" },1182{ "speculation size", "specsize" },1183{ "dynamic variable size", "dynvarsize" },1184{ NULL }1185}, rates[] = {1186{ "cleaning rate", "cleanrate" },1187{ "status rate", "statusrate" },1188{ NULL }1189};11901191for (i = 0; bufs[i].name != NULL; i++) {1192if (dtrace_getopt(g_dtp, bufs[i].optname, &bufs[i].val) == -1)1193fatal("couldn't get option %s", bufs[i].optname);1194}11951196for (i = 0; rates[i].name != NULL; i++) {1197if (dtrace_getopt(g_dtp, rates[i].optname, &rates[i].val) == -1)1198fatal("couldn't get option %s", rates[i].optname);1199}12001201if (dtrace_go(g_dtp) == -1)1202dfatal("could not enable tracing");12031204for (i = 0; bufs[i].name != NULL; i++) {1205dtrace_optval_t j = 0, mul = 10;1206dtrace_optval_t nsize;12071208if (bufs[i].val == DTRACEOPT_UNSET)1209continue;12101211(void) dtrace_getopt(g_dtp, bufs[i].optname, &nsize);12121213if (nsize == DTRACEOPT_UNSET || nsize == 0)1214continue;12151216if (nsize >= bufs[i].val - sizeof (uint64_t))1217continue;12181219for (; (INT64_C(1) << mul) <= nsize; j++, mul += 10)1220continue;12211222if (!(nsize & ((INT64_C(1) << (mul - 10)) - 1))) {1223error("%s lowered to %lld%c\n", bufs[i].name,1224(long long)nsize >> (mul - 10), " kmgtpe"[j]);1225} else {1226error("%s lowered to %lld bytes\n", bufs[i].name,1227(long long)nsize);1228}1229}12301231for (i = 0; rates[i].name != NULL; i++) {1232dtrace_optval_t nval;1233char *dir;12341235if (rates[i].val == DTRACEOPT_UNSET)1236continue;12371238(void) dtrace_getopt(g_dtp, rates[i].optname, &nval);12391240if (nval == DTRACEOPT_UNSET || nval == 0)1241continue;12421243if (rates[i].val == nval)1244continue;12451246dir = nval > rates[i].val ? "reduced" : "increased";12471248if (nval <= NANOSEC && (NANOSEC % nval) == 0) {1249error("%s %s to %lld hz\n", rates[i].name, dir,1250(long long)NANOSEC / (long long)nval);1251continue;1252}12531254if ((nval % NANOSEC) == 0) {1255error("%s %s to once every %lld seconds\n",1256rates[i].name, dir,1257(long long)nval / (long long)NANOSEC);1258continue;1259}12601261error("%s %s to once every %lld nanoseconds\n",1262rates[i].name, dir, (long long)nval);1263}1264}12651266/*ARGSUSED*/1267static void1268intr(int signo)1269{1270if (!g_intr)1271g_newline = 1;12721273if (g_intr++)1274g_impatient = 1;1275}12761277#ifdef __FreeBSD__1278static void1279siginfo(int signo __unused)1280{12811282g_siginfo++;1283g_newline = 1;1284}1285#endif12861287static void1288installsighands(void)1289{1290struct sigaction act, oact;12911292(void) sigemptyset(&act.sa_mask);1293act.sa_flags = 0;1294act.sa_handler = intr;12951296if (sigaction(SIGINT, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN)1297(void) sigaction(SIGINT, &act, NULL);12981299if (sigaction(SIGTERM, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN)1300(void) sigaction(SIGTERM, &act, NULL);13011302#ifdef __FreeBSD__1303if (sigaction(SIGPIPE, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN)1304(void) sigaction(SIGPIPE, &act, NULL);13051306if (sigaction(SIGUSR1, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN)1307(void) sigaction(SIGUSR1, &act, NULL);13081309act.sa_handler = siginfo;1310if (sigaction(SIGINFO, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN)1311(void) sigaction(SIGINFO, &act, NULL);1312#endif1313}13141315int1316main(int argc, char *argv[])1317{1318dtrace_bufdesc_t buf;1319dtrace_status_t status[2];1320dtrace_optval_t opt;1321dtrace_cmd_t *dcp;13221323g_ofp = stdout;1324int done = 0, mode = 0;1325int err, i, c, new_argc, libxo_specified;1326int print_upon_exit = 0;1327char *p, **v;1328struct ps_prochandle *P;1329pid_t pid;13301331#ifdef __FreeBSD__1332/* For %'d and the like. */1333(void) setlocale(LC_NUMERIC, "");13341335/* For %T. */1336(void) setlocale(LC_TIME, "");1337#endif13381339g_pname = basename(argv[0]);13401341if (argc == 1)1342return (usage(stderr));13431344if ((g_argv = malloc(sizeof (char *) * argc)) == NULL ||1345(g_cmdv = malloc(sizeof (dtrace_cmd_t) * argc)) == NULL ||1346(g_psv = malloc(sizeof (struct ps_prochandle *) * argc)) == NULL)1347fatal("failed to allocate memory for arguments");13481349new_argc = xo_parse_args(argc, argv);1350if (new_argc < 0)1351return (usage(stderr));13521353if (new_argc != argc)1354libxo_specified = 1;13551356argc = new_argc;13571358g_argv[g_argc++] = argv[0]; /* propagate argv[0] to D as $0/$$0 */1359argv[0] = g_pname; /* rewrite argv[0] for getopt errors */13601361bzero(status, sizeof (status));1362bzero(&buf, sizeof (buf));13631364/*1365* Make an initial pass through argv[] processing any arguments that1366* affect our behavior mode (g_mode) and flags used for dtrace_open().1367* We also accumulate arguments that are not affiliated with getopt1368* options into g_argv[], and abort if any invalid options are found.1369*/1370for (optind = 1; optind < argc; optind++) {1371while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) {1372switch (c) {1373case '3':1374if (strcmp(optarg, "2") != 0) {1375(void) fprintf(stderr,1376"%s: illegal option -- 3%s\n",1377argv[0], optarg);1378return (usage(stderr));1379}1380g_oflags &= ~DTRACE_O_MODEL_MASK;1381g_oflags |= DTRACE_O_ILP32;1382break;13831384case '6':1385if (strcmp(optarg, "4") != 0) {1386(void) fprintf(stderr,1387"%s: illegal option -- 6%s\n",1388argv[0], optarg);1389return (usage(stderr));1390}1391g_oflags &= ~DTRACE_O_MODEL_MASK;1392g_oflags |= DTRACE_O_LP64;1393break;13941395case 'a':1396g_grabanon++; /* also checked in pass 2 below */1397break;13981399case 'A':1400g_mode = DMODE_ANON;1401g_exec = 0;1402mode++;1403break;14041405case 'e':1406g_exec = 0;1407done = 1;1408break;14091410case 'h':1411g_mode = DMODE_HEADER;1412g_oflags |= DTRACE_O_NODEV;1413g_cflags |= DTRACE_C_ZDEFS; /* -h implies -Z */1414g_exec = 0;1415mode++;1416break;14171418case 'G':1419g_mode = DMODE_LINK;1420g_oflags |= DTRACE_O_NODEV;1421g_cflags |= DTRACE_C_ZDEFS; /* -G implies -Z */1422g_exec = 0;1423mode++;1424break;14251426case 'l':1427g_mode = DMODE_LIST;1428g_cflags |= DTRACE_C_ZDEFS; /* -l implies -Z */1429mode++;1430break;14311432case 'V':1433g_mode = DMODE_VERS;1434mode++;1435break;14361437default:1438if (strchr(DTRACE_OPTSTR, c) == NULL)1439return (usage(stderr));1440}1441}14421443if (optind < argc)1444g_argv[g_argc++] = argv[optind];1445}14461447if (mode > 1) {1448(void) fprintf(stderr, "%s: only one of the [-AGhlV] options "1449"can be specified at a time\n", g_pname);1450return (E_USAGE);1451}14521453if (g_mode == DMODE_VERS)1454return (printf("%s: %s\n", g_pname, _dtrace_version) <= 0);14551456/*1457* If we're in linker mode and the data model hasn't been specified,1458* we try to guess the appropriate setting by examining the object1459* files. We ignore certain errors since we'll catch them later when1460* we actually process the object files.1461*/1462if (g_mode == DMODE_LINK && (g_oflags & DTRACE_O_MODEL_MASK) == 0 &&1463elf_version(EV_CURRENT) != EV_NONE) {1464int fd;1465Elf *elf;1466GElf_Ehdr ehdr;14671468for (i = 1; i < g_argc; i++) {1469if ((fd = open64(g_argv[i], O_RDONLY)) == -1)1470break;14711472if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {1473(void) close(fd);1474break;1475}14761477if (elf_kind(elf) != ELF_K_ELF ||1478gelf_getehdr(elf, &ehdr) == NULL) {1479(void) close(fd);1480(void) elf_end(elf);1481break;1482}14831484(void) close(fd);1485(void) elf_end(elf);14861487if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {1488if (g_oflags & DTRACE_O_ILP32) {1489fatal("can't mix 32-bit and 64-bit "1490"object files\n");1491}1492g_oflags |= DTRACE_O_LP64;1493} else if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {1494if (g_oflags & DTRACE_O_LP64) {1495fatal("can't mix 32-bit and 64-bit "1496"object files\n");1497}1498g_oflags |= DTRACE_O_ILP32;1499} else {1500break;1501}1502}1503}15041505/*1506* Open libdtrace. If we are not actually going to be enabling any1507* instrumentation attempt to reopen libdtrace using DTRACE_O_NODEV.1508*/1509while ((g_dtp = dtrace_open(DTRACE_VERSION, g_oflags, &err)) == NULL) {1510if (!(g_oflags & DTRACE_O_NODEV) && !g_exec && !g_grabanon) {1511g_oflags |= DTRACE_O_NODEV;1512continue;1513}15141515fatal("failed to initialize dtrace: %s\n",1516dtrace_errmsg(NULL, err));1517}15181519#if defined(__i386__)1520/* XXX The 32-bit seems to need more buffer space by default -sson */1521(void) dtrace_setopt(g_dtp, "bufsize", "12m");1522(void) dtrace_setopt(g_dtp, "aggsize", "12m");1523#else1524(void) dtrace_setopt(g_dtp, "bufsize", "4m");1525(void) dtrace_setopt(g_dtp, "aggsize", "4m");1526#endif1527(void) dtrace_setopt(g_dtp, "temporal", "yes");15281529/*1530* If -G is specified, enable -xlink=dynamic and -xunodefs to permit1531* references to undefined symbols to remain as unresolved relocations.1532* If -A is specified, enable -xlink=primary to permit static linking1533* only to kernel symbols that are defined in a primary kernel module.1534*/1535if (g_mode == DMODE_LINK) {1536(void) dtrace_setopt(g_dtp, "linkmode", "dynamic");1537(void) dtrace_setopt(g_dtp, "unodefs", NULL);15381539/*1540* Use the remaining arguments as the list of object files1541* when in linker mode.1542*/1543g_objc = g_argc - 1;1544g_objv = g_argv + 1;15451546/*1547* We still use g_argv[0], the name of the executable.1548*/1549g_argc = 1;1550} else if (g_mode == DMODE_ANON)1551(void) dtrace_setopt(g_dtp, "linkmode", "primary");155215531554if (libxo_specified)1555dtrace_oformat_configure(g_dtp);15561557/*1558* Now that we have libdtrace open, make a second pass through argv[]1559* to perform any dtrace_setopt() calls and change any compiler flags.1560* We also accumulate any program specifications into our g_cmdv[] at1561* this time; these will compiled as part of the fourth processing pass.1562*/1563for (optind = 1; optind < argc; optind++) {1564while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) {1565switch (c) {1566case 'a':1567if (dtrace_setopt(g_dtp, "grabanon", 0) != 0)1568dfatal("failed to set -a");1569break;15701571case 'b':1572if (dtrace_setopt(g_dtp,1573"bufsize", optarg) != 0)1574dfatal("failed to set -b %s", optarg);1575break;15761577case 'B':1578g_ofp = NULL;1579break;15801581case 'C':1582g_cflags |= DTRACE_C_CPP;1583break;15841585case 'd':1586g_cflags |= DTRACE_C_SUGAR;1587break;15881589case 'D':1590if (dtrace_setopt(g_dtp, "define", optarg) != 0)1591dfatal("failed to set -D %s", optarg);1592break;15931594case 'f':1595dcp = &g_cmdv[g_cmdc++];1596dcp->dc_func = compile_str;1597dcp->dc_spec = DTRACE_PROBESPEC_FUNC;1598dcp->dc_arg = optarg;1599break;16001601case 'F':1602if (dtrace_setopt(g_dtp, "flowindent", 0) != 0)1603dfatal("failed to set -F");1604break;16051606case 'H':1607if (dtrace_setopt(g_dtp, "cpphdrs", 0) != 0)1608dfatal("failed to set -H");1609break;16101611case 'i':1612dcp = &g_cmdv[g_cmdc++];1613dcp->dc_func = compile_str;1614dcp->dc_spec = DTRACE_PROBESPEC_NAME;1615dcp->dc_arg = optarg;1616break;16171618case 'I':1619if (dtrace_setopt(g_dtp, "incdir", optarg) != 0)1620dfatal("failed to set -I %s", optarg);1621break;16221623case 'L':1624if (dtrace_setopt(g_dtp, "libdir", optarg) != 0)1625dfatal("failed to set -L %s", optarg);1626break;16271628case 'm':1629dcp = &g_cmdv[g_cmdc++];1630dcp->dc_func = compile_str;1631dcp->dc_spec = DTRACE_PROBESPEC_MOD;1632dcp->dc_arg = optarg;1633break;16341635case 'n':1636dcp = &g_cmdv[g_cmdc++];1637dcp->dc_func = compile_str;1638dcp->dc_spec = DTRACE_PROBESPEC_NAME;1639dcp->dc_arg = optarg;1640break;16411642case 'P':1643dcp = &g_cmdv[g_cmdc++];1644dcp->dc_func = compile_str;1645dcp->dc_spec = DTRACE_PROBESPEC_PROVIDER;1646dcp->dc_arg = optarg;1647break;16481649case 'O':1650print_upon_exit = 1;1651break;16521653case 'q':1654if (dtrace_setopt(g_dtp, "quiet", 0) != 0)1655dfatal("failed to set -q");1656break;16571658case 'o':1659g_ofile = optarg;1660break;16611662case 's':1663dcp = &g_cmdv[g_cmdc++];1664dcp->dc_func = compile_file;1665dcp->dc_spec = DTRACE_PROBESPEC_NONE;1666dcp->dc_arg = optarg;1667break;16681669case 'S':1670g_cflags |= DTRACE_C_DIFV;1671break;16721673case 'U':1674if (dtrace_setopt(g_dtp, "undef", optarg) != 0)1675dfatal("failed to set -U %s", optarg);1676break;16771678case 'v':1679g_verbose++;1680break;16811682case 'w':1683if (dtrace_setopt(g_dtp, "destructive", 0) != 0)1684dfatal("failed to set -w");1685break;16861687case 'x':1688if ((p = strchr(optarg, '=')) != NULL)1689*p++ = '\0';16901691if (dtrace_setopt(g_dtp, optarg, p) != 0)1692dfatal("failed to set -x %s", optarg);1693break;16941695case 'X':1696if (dtrace_setopt(g_dtp, "stdc", optarg) != 0)1697dfatal("failed to set -X %s", optarg);1698break;16991700case 'Z':1701g_cflags |= DTRACE_C_ZDEFS;1702break;17031704default:1705if (strchr(DTRACE_OPTSTR, c) == NULL)1706return (usage(stderr));1707}1708}1709}17101711if (g_ofp == NULL && g_mode != DMODE_EXEC) {1712(void) fprintf(stderr, "%s: -B not valid in combination"1713" with [-AGl] options\n", g_pname);1714return (E_USAGE);1715}17161717if (g_ofp == NULL && g_ofile != NULL) {1718(void) fprintf(stderr, "%s: -B not valid in combination"1719" with -o option\n", g_pname);1720return (E_USAGE);1721}17221723/*1724* In our third pass we handle any command-line options related to1725* grabbing or creating victim processes. The behavior of these calls1726* may been affected by any library options set by the second pass.1727*/1728for (optind = 1; optind < argc; optind++) {1729while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) {1730switch (c) {1731case 'c':1732if ((v = make_argv(optarg)) == NULL)1733fatal("failed to allocate memory");17341735P = dtrace_proc_create(g_dtp, v[0], v, NULL, NULL);1736if (P == NULL)1737dfatal(NULL); /* dtrace_errmsg() only */17381739g_psv[g_psc++] = P;1740free(v);1741break;17421743case 'p':1744errno = 0;1745pid = strtol(optarg, &p, 10);17461747if (errno != 0 || p == optarg || p[0] != '\0')1748fatal("invalid pid: %s\n", optarg);17491750P = dtrace_proc_grab(g_dtp, pid, 0);1751if (P == NULL)1752dfatal(NULL); /* dtrace_errmsg() only */17531754g_psv[g_psc++] = P;1755break;1756}1757}1758}17591760/*1761* In our fourth pass we finish g_cmdv[] by calling dc_func to convert1762* each string or file specification into a compiled program structure.1763*/1764for (i = 0; i < g_cmdc; i++)1765g_cmdv[i].dc_func(&g_cmdv[i]);17661767if (g_mode != DMODE_LIST) {1768if (dtrace_handle_err(g_dtp, &errhandler, NULL) == -1)1769dfatal("failed to establish error handler");17701771if (dtrace_handle_drop(g_dtp, &drophandler, NULL) == -1)1772dfatal("failed to establish drop handler");17731774if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1)1775dfatal("failed to establish proc handler");17761777if (dtrace_handle_setopt(g_dtp, &setopthandler, NULL) == -1)1778dfatal("failed to establish setopt handler");17791780if (g_ofp == NULL &&1781dtrace_handle_buffered(g_dtp, &bufhandler, NULL) == -1)1782dfatal("failed to establish buffered handler");1783}17841785(void) dtrace_getopt(g_dtp, "flowindent", &opt);1786g_flowindent = opt != DTRACEOPT_UNSET;17871788(void) dtrace_getopt(g_dtp, "grabanon", &opt);1789g_grabanon = opt != DTRACEOPT_UNSET;17901791(void) dtrace_getopt(g_dtp, "quiet", &opt);1792g_quiet = opt != DTRACEOPT_UNSET;17931794if (dtrace_oformat(g_dtp)) {1795if (dtrace_setopt(g_dtp, "quiet", 0) != 0)1796dfatal("failed to set quiet (caused by oformat)");1797}17981799/*1800* Now make a fifth and final pass over the options that have been1801* turned into programs and saved in g_cmdv[], performing any mode-1802* specific processing. If g_mode is DMODE_EXEC, we will break out1803* of the switch() and continue on to the data processing loop. For1804* other modes, we will exit dtrace once mode-specific work is done.1805*/1806switch (g_mode) {1807case DMODE_EXEC:1808if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL)1809fatal("failed to open output file '%s'", g_ofile);18101811if (dtrace_oformat(g_dtp))1812dtrace_set_outfp(g_ofp);18131814for (i = 0; i < g_cmdc; i++)1815exec_prog(&g_cmdv[i]);18161817if (done && !g_grabanon) {1818dtrace_close(g_dtp);1819return (g_status);1820}1821break;18221823case DMODE_ANON:1824if (g_ofile == NULL)1825#ifdef illumos1826g_ofile = "/kernel/drv/dtrace.conf";1827#else1828/*1829* On FreeBSD, anonymous DOF data is written to1830* the DTrace DOF file.1831*/1832g_ofile = "/boot/dtrace.dof";1833#endif18341835dof_prune(g_ofile); /* strip out any old DOF directives */1836#ifdef illumos1837etcsystem_prune(); /* string out any forceload directives */1838#endif18391840if (g_cmdc == 0) {1841dtrace_close(g_dtp);1842return (g_status);1843}18441845if ((g_ofp = fopen(g_ofile, "a")) == NULL)1846fatal("failed to open output file '%s'", g_ofile);18471848if (dtrace_oformat(g_dtp))1849dtrace_set_outfp(g_ofp);18501851for (i = 0; i < g_cmdc; i++) {1852anon_prog(&g_cmdv[i],1853dtrace_dof_create(g_dtp, g_cmdv[i].dc_prog, 0), i);1854}18551856/*1857* Dump out the DOF corresponding to the error handler and the1858* current options as the final DOF property in the .conf file.1859*/1860anon_prog(NULL, dtrace_geterr_dof(g_dtp), i++);1861anon_prog(NULL, dtrace_getopt_dof(g_dtp), i++);18621863if (fclose(g_ofp) == EOF)1864fatal("failed to close output file '%s'", g_ofile);18651866/*1867* These messages would use notice() rather than error(), but1868* we don't want them suppressed when -A is run on a D program1869* that itself contains a #pragma D option quiet.1870*/1871error("saved anonymous enabling in %s\n", g_ofile);18721873#ifdef __FreeBSD__1874bootdof_add();1875#else1876etcsystem_add();1877error("run update_drv(1M) or reboot to enable changes\n");1878#endif18791880dtrace_close(g_dtp);1881return (g_status);18821883case DMODE_LINK:1884if (g_cmdc == 0) {1885(void) fprintf(stderr, "%s: -G requires one or more "1886"scripts or enabling options\n", g_pname);1887dtrace_close(g_dtp);1888return (E_USAGE);1889}18901891for (i = 0; i < g_cmdc; i++)1892link_prog(&g_cmdv[i]);18931894if (g_cmdc > 1 && g_ofile != NULL) {1895char **objv = alloca(g_cmdc * sizeof (char *));18961897for (i = 0; i < g_cmdc; i++)1898objv[i] = g_cmdv[i].dc_ofile;18991900if (dtrace_program_link(g_dtp, NULL, DTRACE_D_PROBES,1901g_ofile, g_cmdc, objv) != 0)1902dfatal(NULL); /* dtrace_errmsg() only */1903}19041905dtrace_close(g_dtp);1906return (g_status);19071908case DMODE_LIST:1909if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL)1910fatal("failed to open output file '%s'", g_ofile);19111912installsighands();19131914oprintf("%5s %10s %17s %33s %s\n",1915"ID", "PROVIDER", "MODULE", "FUNCTION", "NAME");19161917for (i = 0; i < g_cmdc; i++)1918list_prog(&g_cmdv[i]);19191920if (g_cmdc == 0)1921(void) dtrace_probe_iter(g_dtp, NULL, list_probe, NULL);19221923dtrace_close(g_dtp);1924return (g_status);19251926case DMODE_HEADER:1927if (g_cmdc == 0) {1928(void) fprintf(stderr, "%s: -h requires one or more "1929"scripts or enabling options\n", g_pname);1930dtrace_close(g_dtp);1931return (E_USAGE);1932}19331934if (g_ofile == NULL) {1935char *p;19361937if (g_cmdc > 1) {1938(void) fprintf(stderr, "%s: -h requires an "1939"output file if multiple scripts are "1940"specified\n", g_pname);1941dtrace_close(g_dtp);1942return (E_USAGE);1943}19441945if ((p = strrchr(g_cmdv[0].dc_arg, '.')) == NULL ||1946strcmp(p, ".d") != 0) {1947(void) fprintf(stderr, "%s: -h requires an "1948"output file if no scripts are "1949"specified\n", g_pname);1950dtrace_close(g_dtp);1951return (E_USAGE);1952}19531954p[0] = '\0'; /* strip .d suffix */1955g_ofile = p = g_cmdv[0].dc_ofile;1956(void) snprintf(p, sizeof (g_cmdv[0].dc_ofile),1957"%s.h", basename(g_cmdv[0].dc_arg));1958}19591960if ((g_ofp = fopen(g_ofile, "w")) == NULL)1961fatal("failed to open header file '%s'", g_ofile);19621963oprintf("/*\n * Generated by dtrace(1M).\n */\n\n");19641965if (dtrace_program_header(g_dtp, g_ofp, g_ofile) != 0 ||1966fclose(g_ofp) == EOF)1967dfatal("failed to create header file %s", g_ofile);19681969dtrace_close(g_dtp);1970return (g_status);1971}19721973/*1974* If -a and -Z were not specified and no probes have been matched, no1975* probe criteria was specified on the command line and we abort.1976*/1977if (g_total == 0 && !g_grabanon && !(g_cflags & DTRACE_C_ZDEFS))1978dfatal("no probes %s\n", g_cmdc ? "matched" : "specified");19791980/*1981* Start tracing. Once we dtrace_go(), reload any options that affect1982* our globals in case consuming anonymous state has changed them.1983*/1984go();19851986(void) dtrace_getopt(g_dtp, "flowindent", &opt);1987g_flowindent = opt != DTRACEOPT_UNSET;19881989(void) dtrace_getopt(g_dtp, "grabanon", &opt);1990g_grabanon = opt != DTRACEOPT_UNSET;19911992(void) dtrace_getopt(g_dtp, "quiet", &opt);1993g_quiet = opt != DTRACEOPT_UNSET;19941995(void) dtrace_getopt(g_dtp, "destructive", &opt);1996if (opt != DTRACEOPT_UNSET)1997notice("allowing destructive actions\n");19981999installsighands();20002001/*2002* Now that tracing is active and we are ready to consume trace data,2003* continue any grabbed or created processes, setting them running2004* using the /proc control mechanism inside of libdtrace.2005*/2006for (i = 0; i < g_psc; i++)2007dtrace_proc_continue(g_dtp, g_psv[i]);20082009g_pslive = g_psc; /* count for prochandler() */20102011dtrace_oformat_setup(g_dtp);2012do {2013if (!g_intr && !done)2014dtrace_sleep(g_dtp);20152016#ifdef __FreeBSD__2017/*2018* XXX: Supporting SIGINFO with oformat makes little sense, as2019* it can't really produce sensible DTrace output.2020*2021* If needed, we could support it by having an imaginary2022* "SIGINFO" probe that we can construct in the output but leave2023* it out for now.2024*/2025if (g_siginfo && !dtrace_oformat(g_dtp)) {2026(void)dtrace_aggregate_print(g_dtp, g_ofp, NULL);2027g_siginfo = 0;2028}2029#endif20302031if (g_newline) {2032/*2033* Output a newline just to make the output look2034* slightly cleaner. Note that we do this even in2035* "quiet" mode...2036*/2037oprintf("\n");2038g_newline = 0;2039}20402041if (done || g_intr || (g_psc != 0 && g_pslive == 0)) {2042done = 1;2043if (dtrace_stop(g_dtp) == -1)2044dfatal("couldn't stop tracing");2045}20462047switch (dtrace_work(g_dtp, g_ofp, chew, chewrec, NULL)) {2048case DTRACE_WORKSTATUS_DONE:2049done = 1;2050break;2051case DTRACE_WORKSTATUS_OKAY:2052break;2053default:2054if (!g_impatient && dtrace_errno(g_dtp) != EINTR)2055dfatal("processing aborted");2056}20572058if (g_ofp != NULL && fflush(g_ofp) == EOF)2059clearerr(g_ofp);2060} while (!done);20612062if (!dtrace_oformat(g_dtp))2063oprintf("\n");20642065/*2066* Since there is no way to format a probe here and machine-readable2067* output makes little sense without explicitly asking for it, we print2068* nothing upon Ctrl-C if oformat is specified. If the user wishes to2069* get output upon exit, they must write an explicit dtrace:::END probe2070* to do so.2071*/2072if ((!g_impatient && !dtrace_oformat(g_dtp)) ||2073(!g_impatient && print_upon_exit)) {2074if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 &&2075dtrace_errno(g_dtp) != EINTR)2076dfatal("failed to print aggregations");2077}20782079dtrace_oformat_teardown(g_dtp);2080dtrace_close(g_dtp);2081return (g_status);2082}208320842085