Changeset View
Changeset View
Standalone View
Standalone View
cddl/contrib/opensolaris/cmd/dtrace/dtrace.c
| Show All 20 Lines | |||||
| /* | /* | ||||
| * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | ||||
| * Use is subject to license terms. | * Use is subject to license terms. | ||||
| */ | */ | ||||
| /* | /* | ||||
| * Copyright (c) 2012 by Delphix. All rights reserved. | * Copyright (c) 2012 by Delphix. All rights reserved. | ||||
| * Copyright (c) 2013, Joyent, Inc. All rights reserved. | * Copyright (c) 2013, Joyent, Inc. All rights reserved. | ||||
| * Copyright (c) 2023, Domagoj Stolfa. All rights reserved. | |||||
| */ | */ | ||||
| #include <sys/types.h> | #include <sys/types.h> | ||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||
| #include <sys/wait.h> | #include <sys/wait.h> | ||||
| #include <dtrace.h> | #include <dtrace.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| Show All 13 Lines | |||||
| #ifdef illumos | #ifdef illumos | ||||
| #include <libproc.h> | #include <libproc.h> | ||||
| #endif | #endif | ||||
| #ifdef __FreeBSD__ | #ifdef __FreeBSD__ | ||||
| #include <locale.h> | #include <locale.h> | ||||
| #include <spawn.h> | #include <spawn.h> | ||||
| #endif | #endif | ||||
| #undef NORETURN /* needed because libxo redefines it */ | |||||
| #include <libxo/xo.h> | |||||
| typedef struct dtrace_cmd { | typedef struct dtrace_cmd { | ||||
| void (*dc_func)(struct dtrace_cmd *); /* function to compile arg */ | void (*dc_func)(struct dtrace_cmd *); /* function to compile arg */ | ||||
| dtrace_probespec_t dc_spec; /* probe specifier context */ | dtrace_probespec_t dc_spec; /* probe specifier context */ | ||||
| char *dc_arg; /* argument from main argv */ | char *dc_arg; /* argument from main argv */ | ||||
| const char *dc_name; /* name for error messages */ | const char *dc_name; /* name for error messages */ | ||||
| const char *dc_desc; /* desc for error messages */ | const char *dc_desc; /* desc for error messages */ | ||||
| dtrace_prog_t *dc_prog; /* program compiled from arg */ | dtrace_prog_t *dc_prog; /* program compiled from arg */ | ||||
| char dc_ofile[PATH_MAX]; /* derived output file name */ | char dc_ofile[PATH_MAX]; /* derived output file name */ | ||||
| } dtrace_cmd_t; | } dtrace_cmd_t; | ||||
| #define DMODE_VERS 0 /* display version information and exit (-V) */ | #define DMODE_VERS 0 /* display version information and exit (-V) */ | ||||
| #define DMODE_EXEC 1 /* compile program for enabling (-a/e/E) */ | #define DMODE_EXEC 1 /* compile program for enabling (-a/e/E) */ | ||||
| #define DMODE_ANON 2 /* compile program for anonymous tracing (-A) */ | #define DMODE_ANON 2 /* compile program for anonymous tracing (-A) */ | ||||
| #define DMODE_LINK 3 /* compile program for linking with ELF (-G) */ | #define DMODE_LINK 3 /* compile program for linking with ELF (-G) */ | ||||
| #define DMODE_LIST 4 /* compile program and list probes (-l) */ | #define DMODE_LIST 4 /* compile program and list probes (-l) */ | ||||
| #define DMODE_HEADER 5 /* compile program for headergen (-h) */ | #define DMODE_HEADER 5 /* compile program for headergen (-h) */ | ||||
| #define E_SUCCESS 0 | #define E_SUCCESS 0 | ||||
| #define E_ERROR 1 | #define E_ERROR 1 | ||||
| #define E_USAGE 2 | #define E_USAGE 2 | ||||
| static const char DTRACE_OPTSTR[] = | static const char DTRACE_OPTSTR[] = | ||||
| "3:6:aAb:Bc:CdD:ef:FGhHi:I:lL:m:n:o:p:P:qs:SU:vVwx:X:Z"; | "3:6:aAb:Bc:CdD:ef:FGhHi:I:lL:m:n:o:Op:P:qs:SU:vVwx:X:Z"; | ||||
| static char **g_argv; | static char **g_argv; | ||||
| static int g_argc; | static int g_argc; | ||||
| static char **g_objv; | static char **g_objv; | ||||
| static int g_objc; | static int g_objc; | ||||
| static dtrace_cmd_t *g_cmdv; | static dtrace_cmd_t *g_cmdv; | ||||
| static int g_cmdc; | static int g_cmdc; | ||||
| static struct ps_prochandle **g_psv; | static struct ps_prochandle **g_psv; | ||||
| ▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | (void) fprintf(fp, "\n" | ||||
| "\t-H print included files when invoking preprocessor\n" | "\t-H print included files when invoking preprocessor\n" | ||||
| "\t-i enable or list probes matching the specified probe id\n" | "\t-i enable or list probes matching the specified probe id\n" | ||||
| "\t-I add include directory to preprocessor search path\n" | "\t-I add include directory to preprocessor search path\n" | ||||
| "\t-l list probes matching specified criteria\n" | "\t-l list probes matching specified criteria\n" | ||||
| "\t-L add library directory to library search path\n" | "\t-L add library directory to library search path\n" | ||||
| "\t-m enable or list probes matching the specified module name\n" | "\t-m enable or list probes matching the specified module name\n" | ||||
| "\t-n enable or list probes matching the specified probe name\n" | "\t-n enable or list probes matching the specified probe name\n" | ||||
| "\t-o set output file\n" | "\t-o set output file\n" | ||||
| "\t-O print output upon exiting (specific to oformat)\n" | |||||
| "\t-p grab specified process-ID and cache its symbol tables\n" | "\t-p grab specified process-ID and cache its symbol tables\n" | ||||
| "\t-P enable or list probes matching the specified provider name\n" | "\t-P enable or list probes matching the specified provider name\n" | ||||
| "\t-q set quiet mode (only output explicitly traced data)\n" | "\t-q set quiet mode (only output explicitly traced data)\n" | ||||
| "\t-s enable or list probes according to the specified D script\n" | "\t-s enable or list probes according to the specified D script\n" | ||||
| "\t-S print D compiler intermediate code\n" | "\t-S print D compiler intermediate code\n" | ||||
| "\t-U undefine symbol when invoking preprocessor\n" | "\t-U undefine symbol when invoking preprocessor\n" | ||||
| "\t-v set verbose mode (report stability attributes, arguments)\n" | "\t-v set verbose mode (report stability attributes, arguments)\n" | ||||
| "\t-V report DTrace API version\n" | "\t-V report DTrace API version\n" | ||||
| ▲ Show 20 Lines • Show All 724 Lines • ▼ Show 20 Lines | errhandler(const dtrace_errdata_t *data, void *arg) | ||||
| error(data->dteda_msg); | error(data->dteda_msg); | ||||
| return (DTRACE_HANDLE_OK); | return (DTRACE_HANDLE_OK); | ||||
| } | } | ||||
| /*ARGSUSED*/ | /*ARGSUSED*/ | ||||
| static int | static int | ||||
| drophandler(const dtrace_dropdata_t *data, void *arg) | drophandler(const dtrace_dropdata_t *data, void *arg) | ||||
| { | { | ||||
| if (!dtrace_oformat(g_dtp)) { | |||||
| error(data->dtdda_msg); | error(data->dtdda_msg); | ||||
| } | |||||
| return (DTRACE_HANDLE_OK); | return (DTRACE_HANDLE_OK); | ||||
| } | } | ||||
| /*ARGSUSED*/ | /*ARGSUSED*/ | ||||
| static int | static int | ||||
| setopthandler(const dtrace_setoptdata_t *data, void *arg) | setopthandler(const dtrace_setoptdata_t *data, void *arg) | ||||
| { | { | ||||
| if (strcmp(data->dtsda_option, "quiet") == 0) | if (strcmp(data->dtsda_option, "quiet") == 0) | ||||
| ▲ Show 20 Lines • Show All 202 Lines • ▼ Show 20 Lines | if (!g_flowindent) { | ||||
| } | } | ||||
| } else { | } else { | ||||
| oprintf("%3s %-41s\n", "CPU", "FUNCTION"); | oprintf("%3s %-41s\n", "CPU", "FUNCTION"); | ||||
| } | } | ||||
| heading = 1; | heading = 1; | ||||
| } | } | ||||
| if (!g_flowindent) { | if (!g_flowindent) { | ||||
| if (!g_quiet) { | if (dtrace_oformat(g_dtp)) { | ||||
| dtrace_oformat_probe(g_dtp, data, cpu, pd); | |||||
| } else if (!g_quiet) { | |||||
| char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2]; | char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2]; | ||||
| (void) snprintf(name, sizeof (name), "%s:%s", | (void) snprintf(name, sizeof (name), "%s:%s", | ||||
| pd->dtpd_func, pd->dtpd_name); | pd->dtpd_func, pd->dtpd_name); | ||||
| oprintf("%3d %6d %32s ", cpu, pd->dtpd_id, name); | oprintf("%3d %6d %32s ", cpu, pd->dtpd_id, name); | ||||
| } | } | ||||
| } else { | } else { | ||||
| ▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| dtrace_bufdesc_t buf; | dtrace_bufdesc_t buf; | ||||
| dtrace_status_t status[2]; | dtrace_status_t status[2]; | ||||
| dtrace_optval_t opt; | dtrace_optval_t opt; | ||||
| dtrace_cmd_t *dcp; | dtrace_cmd_t *dcp; | ||||
| g_ofp = stdout; | g_ofp = stdout; | ||||
| int done = 0, mode = 0; | int done = 0, mode = 0; | ||||
| int err, i, c; | int err, i, c, new_argc, libxo_specified; | ||||
| int print_upon_exit = 0; | |||||
| char *p, **v; | char *p, **v; | ||||
| struct ps_prochandle *P; | struct ps_prochandle *P; | ||||
| pid_t pid; | pid_t pid; | ||||
| #ifdef __FreeBSD__ | #ifdef __FreeBSD__ | ||||
| /* For %'d and the like. */ | /* For %'d and the like. */ | ||||
| (void) setlocale(LC_NUMERIC, ""); | (void) setlocale(LC_NUMERIC, ""); | ||||
| /* For %T. */ | /* For %T. */ | ||||
| (void) setlocale(LC_TIME, ""); | (void) setlocale(LC_TIME, ""); | ||||
| #endif | #endif | ||||
| g_pname = basename(argv[0]); | g_pname = basename(argv[0]); | ||||
| if (argc == 1) | if (argc == 1) | ||||
| return (usage(stderr)); | return (usage(stderr)); | ||||
| if ((g_argv = malloc(sizeof (char *) * argc)) == NULL || | if ((g_argv = malloc(sizeof (char *) * argc)) == NULL || | ||||
| (g_cmdv = malloc(sizeof (dtrace_cmd_t) * argc)) == NULL || | (g_cmdv = malloc(sizeof (dtrace_cmd_t) * argc)) == NULL || | ||||
| (g_psv = malloc(sizeof (struct ps_prochandle *) * argc)) == NULL) | (g_psv = malloc(sizeof (struct ps_prochandle *) * argc)) == NULL) | ||||
| fatal("failed to allocate memory for arguments"); | fatal("failed to allocate memory for arguments"); | ||||
| new_argc = xo_parse_args(argc, argv); | |||||
| if (new_argc < 0) | |||||
| return (usage(stderr)); | |||||
| if (new_argc != argc) | |||||
| libxo_specified = 1; | |||||
| argc = new_argc; | |||||
| g_argv[g_argc++] = argv[0]; /* propagate argv[0] to D as $0/$$0 */ | g_argv[g_argc++] = argv[0]; /* propagate argv[0] to D as $0/$$0 */ | ||||
| argv[0] = g_pname; /* rewrite argv[0] for getopt errors */ | argv[0] = g_pname; /* rewrite argv[0] for getopt errors */ | ||||
| bzero(status, sizeof (status)); | bzero(status, sizeof (status)); | ||||
| bzero(&buf, sizeof (buf)); | bzero(&buf, sizeof (buf)); | ||||
| /* | /* | ||||
| * Make an initial pass through argv[] processing any arguments that | * Make an initial pass through argv[] processing any arguments that | ||||
| ▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | if (g_mode == DMODE_LINK) { | ||||
| /* | /* | ||||
| * We still use g_argv[0], the name of the executable. | * We still use g_argv[0], the name of the executable. | ||||
| */ | */ | ||||
| g_argc = 1; | g_argc = 1; | ||||
| } else if (g_mode == DMODE_ANON) | } else if (g_mode == DMODE_ANON) | ||||
| (void) dtrace_setopt(g_dtp, "linkmode", "primary"); | (void) dtrace_setopt(g_dtp, "linkmode", "primary"); | ||||
| if (libxo_specified) | |||||
| dtrace_oformat_configure(g_dtp); | |||||
| /* | /* | ||||
| * Now that we have libdtrace open, make a second pass through argv[] | * Now that we have libdtrace open, make a second pass through argv[] | ||||
| * to perform any dtrace_setopt() calls and change any compiler flags. | * to perform any dtrace_setopt() calls and change any compiler flags. | ||||
| * We also accumulate any program specifications into our g_cmdv[] at | * We also accumulate any program specifications into our g_cmdv[] at | ||||
| * this time; these will compiled as part of the fourth processing pass. | * this time; these will compiled as part of the fourth processing pass. | ||||
| */ | */ | ||||
| for (optind = 1; optind < argc; optind++) { | for (optind = 1; optind < argc; optind++) { | ||||
| while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { | while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { | ||||
| ▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { | ||||
| case 'P': | case 'P': | ||||
| dcp = &g_cmdv[g_cmdc++]; | dcp = &g_cmdv[g_cmdc++]; | ||||
| dcp->dc_func = compile_str; | dcp->dc_func = compile_str; | ||||
| dcp->dc_spec = DTRACE_PROBESPEC_PROVIDER; | dcp->dc_spec = DTRACE_PROBESPEC_PROVIDER; | ||||
| dcp->dc_arg = optarg; | dcp->dc_arg = optarg; | ||||
| break; | break; | ||||
| case 'O': | |||||
| print_upon_exit = 1; | |||||
| break; | |||||
| case 'q': | case 'q': | ||||
| if (dtrace_setopt(g_dtp, "quiet", 0) != 0) | if (dtrace_setopt(g_dtp, "quiet", 0) != 0) | ||||
| dfatal("failed to set -q"); | dfatal("failed to set -q"); | ||||
| break; | break; | ||||
| case 'o': | case 'o': | ||||
| g_ofile = optarg; | g_ofile = optarg; | ||||
| break; | break; | ||||
| ▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | #endif | ||||
| g_flowindent = opt != DTRACEOPT_UNSET; | g_flowindent = opt != DTRACEOPT_UNSET; | ||||
| (void) dtrace_getopt(g_dtp, "grabanon", &opt); | (void) dtrace_getopt(g_dtp, "grabanon", &opt); | ||||
| g_grabanon = opt != DTRACEOPT_UNSET; | g_grabanon = opt != DTRACEOPT_UNSET; | ||||
| (void) dtrace_getopt(g_dtp, "quiet", &opt); | (void) dtrace_getopt(g_dtp, "quiet", &opt); | ||||
| g_quiet = opt != DTRACEOPT_UNSET; | g_quiet = opt != DTRACEOPT_UNSET; | ||||
| if (dtrace_oformat(g_dtp)) { | |||||
| if (dtrace_setopt(g_dtp, "quiet", 0) != 0) | |||||
| dfatal("failed to set quiet (caused by oformat)"); | |||||
| } | |||||
| /* | /* | ||||
| * Now make a fifth and final pass over the options that have been | * Now make a fifth and final pass over the options that have been | ||||
| * turned into programs and saved in g_cmdv[], performing any mode- | * turned into programs and saved in g_cmdv[], performing any mode- | ||||
| * specific processing. If g_mode is DMODE_EXEC, we will break out | * specific processing. If g_mode is DMODE_EXEC, we will break out | ||||
| * of the switch() and continue on to the data processing loop. For | * of the switch() and continue on to the data processing loop. For | ||||
| * other modes, we will exit dtrace once mode-specific work is done. | * other modes, we will exit dtrace once mode-specific work is done. | ||||
| */ | */ | ||||
| switch (g_mode) { | switch (g_mode) { | ||||
| case DMODE_EXEC: | case DMODE_EXEC: | ||||
| if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) | if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) | ||||
| fatal("failed to open output file '%s'", g_ofile); | fatal("failed to open output file '%s'", g_ofile); | ||||
| if (dtrace_oformat(g_dtp)) | |||||
| dtrace_set_outfp(g_ofp); | |||||
| for (i = 0; i < g_cmdc; i++) | for (i = 0; i < g_cmdc; i++) | ||||
| exec_prog(&g_cmdv[i]); | exec_prog(&g_cmdv[i]); | ||||
| if (done && !g_grabanon) { | if (done && !g_grabanon) { | ||||
| dtrace_close(g_dtp); | dtrace_close(g_dtp); | ||||
| return (g_status); | return (g_status); | ||||
| } | } | ||||
| break; | break; | ||||
| Show All 18 Lines | #endif | ||||
| if (g_cmdc == 0) { | if (g_cmdc == 0) { | ||||
| dtrace_close(g_dtp); | dtrace_close(g_dtp); | ||||
| return (g_status); | return (g_status); | ||||
| } | } | ||||
| if ((g_ofp = fopen(g_ofile, "a")) == NULL) | if ((g_ofp = fopen(g_ofile, "a")) == NULL) | ||||
| fatal("failed to open output file '%s'", g_ofile); | fatal("failed to open output file '%s'", g_ofile); | ||||
| if (dtrace_oformat(g_dtp)) | |||||
| dtrace_set_outfp(g_ofp); | |||||
| for (i = 0; i < g_cmdc; i++) { | for (i = 0; i < g_cmdc; i++) { | ||||
| anon_prog(&g_cmdv[i], | anon_prog(&g_cmdv[i], | ||||
| dtrace_dof_create(g_dtp, g_cmdv[i].dc_prog, 0), i); | dtrace_dof_create(g_dtp, g_cmdv[i].dc_prog, 0), i); | ||||
| } | } | ||||
| /* | /* | ||||
| * Dump out the DOF corresponding to the error handler and the | * Dump out the DOF corresponding to the error handler and the | ||||
| * current options as the final DOF property in the .conf file. | * current options as the final DOF property in the .conf file. | ||||
| ▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | #endif | ||||
| * continue any grabbed or created processes, setting them running | * continue any grabbed or created processes, setting them running | ||||
| * using the /proc control mechanism inside of libdtrace. | * using the /proc control mechanism inside of libdtrace. | ||||
| */ | */ | ||||
| for (i = 0; i < g_psc; i++) | for (i = 0; i < g_psc; i++) | ||||
| dtrace_proc_continue(g_dtp, g_psv[i]); | dtrace_proc_continue(g_dtp, g_psv[i]); | ||||
| g_pslive = g_psc; /* count for prochandler() */ | g_pslive = g_psc; /* count for prochandler() */ | ||||
| dtrace_oformat_setup(g_dtp); | |||||
| do { | do { | ||||
| if (!g_intr && !done) | if (!g_intr && !done) | ||||
| dtrace_sleep(g_dtp); | dtrace_sleep(g_dtp); | ||||
| #ifdef __FreeBSD__ | #ifdef __FreeBSD__ | ||||
| if (g_siginfo) { | /* | ||||
| * XXX: Supporting SIGINFO with oformat makes little sense, as | |||||
| * it can't really produce sensible DTrace output. | |||||
markj: I think this makes sense. In retrospect it might have been a good idea for SIGINFO prints to… | |||||
Not Done Inline ActionsIs this true even with "-o"? phil: Is this true even with "-o"? | |||||
Done Inline ActionsWhen -o is specified it'll go to the file because dtrace_aggregate_print is called with g_ofp which will have been changed to the file that we redirected to. Perhaps this is a separate change that could be made unless something else depends on this behavior? domagoj.stolfa_gmail.com: When `-o` is specified it'll go to the file because `dtrace_aggregate_print` is called with… | |||||
| * | |||||
| * If needed, we could support it by having an imaginary | |||||
| * "SIGINFO" probe that we can construct in the output but leave | |||||
| * it out for now. | |||||
| */ | |||||
| if (g_siginfo && !dtrace_oformat(g_dtp)) { | |||||
| (void)dtrace_aggregate_print(g_dtp, g_ofp, NULL); | (void)dtrace_aggregate_print(g_dtp, g_ofp, NULL); | ||||
| g_siginfo = 0; | g_siginfo = 0; | ||||
| } | } | ||||
| #endif | #endif | ||||
| if (g_newline) { | if (g_newline) { | ||||
| /* | /* | ||||
| * Output a newline just to make the output look | * Output a newline just to make the output look | ||||
| Show All 20 Lines | default: | ||||
| if (!g_impatient && dtrace_errno(g_dtp) != EINTR) | if (!g_impatient && dtrace_errno(g_dtp) != EINTR) | ||||
| dfatal("processing aborted"); | dfatal("processing aborted"); | ||||
| } | } | ||||
| if (g_ofp != NULL && fflush(g_ofp) == EOF) | if (g_ofp != NULL && fflush(g_ofp) == EOF) | ||||
| clearerr(g_ofp); | clearerr(g_ofp); | ||||
| } while (!done); | } while (!done); | ||||
| if (!dtrace_oformat(g_dtp)) | |||||
| oprintf("\n"); | oprintf("\n"); | ||||
| if (!g_impatient) { | /* | ||||
| * Since there is no way to format a probe here and machine-readable | |||||
| * output makes little sense without explicitly asking for it, we print | |||||
| * nothing upon Ctrl-C if oformat is specified. If the user wishes to | |||||
| * get output upon exit, they must write an explicit dtrace:::END probe | |||||
| * to do so. | |||||
| */ | |||||
| if ((!g_impatient && !dtrace_oformat(g_dtp)) || | |||||
| (!g_impatient && print_upon_exit)) { | |||||
| if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 && | if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 && | ||||
Done Inline ActionsCan there be a new command line option to make this happen, so one-liners can work? phil: Can there be a new command line option to make this happen, so one-liners can work? | |||||
Done Inline ActionsDoes -O sound reasonable? domagoj.stolfa_gmail.com: Does `-O` sound reasonable? | |||||
Done Inline ActionsPerfect! phil: Perfect! | |||||
| dtrace_errno(g_dtp) != EINTR) | dtrace_errno(g_dtp) != EINTR) | ||||
| dfatal("failed to print aggregations"); | dfatal("failed to print aggregations"); | ||||
| } | } | ||||
| dtrace_oformat_teardown(g_dtp); | |||||
| dtrace_close(g_dtp); | dtrace_close(g_dtp); | ||||
| return (g_status); | return (g_status); | ||||
| } | } | ||||
I think this makes sense. In retrospect it might have been a good idea for SIGINFO prints to print to stderr instead of stdout. That would also interact more nicely with the case where dtrace output is going to a file, i.e., -o is specified.