diff --git a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 index 8724b27f4cbb..c3c7b4cdc818 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 +++ b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 @@ -1,817 +1,823 @@ .\" CDDL HEADER START .\" .\" The contents of this file are subject to the terms of the .\" Common Development and Distribution License (the "License"). .\" You may not use this file except in compliance with the License. .\" .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE .\" or http://www.opensolaris.org/os/licensing. .\" See the License for the specific language governing permissions .\" and limitations under the License. .\" .\" When distributing Covered Code, include this CDDL HEADER in each .\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. .\" If applicable, add the following below this CDDL HEADER, with the .\" fields enclosed by brackets "[]" replaced with your own identifying .\" information: Portions Copyright [yyyy] [name of copyright owner] .\" .\" CDDL HEADER END .\" Copyright (c) 2006, Sun Microsystems, Inc. All Rights Reserved. .\" .\" $FreeBSD$ .\" -.Dd September 7, 2021 +.Dd February 24, 2023 .Dt DTRACE 1 .Os .Sh NAME .Nm dtrace .Nd dynamic tracing compiler and tracing utility .Sh SYNOPSIS .Nm .Op Fl 32 | Fl 64 -.Op Fl aACeFGhHlqSvVwZ +.Op Fl aACdeFGhHlqSvVwZ .Op Fl b Ar bufsz .Op Fl c Ar cmd .Op Fl D Ar name Op Ns = Ns value .Op Fl I Ar path .Op Fl L Ar path .Op Fl o Ar output .Op Fl s Ar script .Op Fl U Ar name .Op Fl x Ar arg Op Ns = Ns value .Op Fl X Cm a | c | s | t .Op Fl p Ar pid .Op Fl P Ar provider Oo Oo Ar predicate Oc Ar action Oc .Op Fl m Oo Ar provider : Oc Ar module Oo Oo Ar predicate Oc Ar action Oc .Op Fl f Oo Oo Ar provider : Oc Ar module : Oc Ar function Oo Oo Ar predicate \ Oc Ar action Oc .Op Fl n Oo Oo Oo Ar provider : Oc Ar module : Oc Ar function : Oc Ar name \ Oo Oo Ar predicate Oc Ar action Oc .Op Fl i Ar probe-id Oo Oo Ar predicate Oc Ar action Oc .Sh DESCRIPTION DTrace is a comprehensive dynamic tracing framework ported from Solaris. DTrace provides a powerful infrastructure that permits administrators, developers, and service personnel to concisely answer arbitrary questions about the behavior of the operating system and user programs. .Pp The .Nm command provides a generic interface to the essential services provided by the DTrace facility, including: .Bl -bullet -offset indent .It Options that list the set of probes and providers currently published by DTrace .It Options that enable probes directly using any of the probe description specifiers (provider, module, function, name) .It Options that run the D compiler and compile one or more D program files or programs written directly on the command line .It Options that generate anonymous tracing programs .It Options that generate program stability reports .It Options that modify DTrace tracing and buffering behavior and enable additional D compiler features .El .Pp You can use .Nm to create D scripts by using it in a shebang declaration to create an interpreter file. You can also use .Nm to attempt to compile D programs and determine their properties without actually enabling traces using the .Fl e option. .Sh OPTIONS The arguments accepted by the .Fl P , .Fl m , .Fl f , .Fl n , and .Fl i options can include an optional D language .Ar predicate enclosed in slashes and an optional D language .Ar action statement list enclosed in braces. D program code specified on the command line must be appropriately quoted to avoid interpretation of meta-characters by the shell. .Pp The following options are supported: .Bl -tag -width indent .It Fl 32 | Fl 64 The D compiler produces programs using the native data model of the operating system kernel. If the .Fl 32 option is specified, .Nm forces the D compiler to compile a D program using the 32-bit data model. If the .Fl 64 option is specified, .Nm forces the D compiler to compile a D program using the 64-bit data model. These options are typically not required as .Nm selects the native data model as the default. The data model affects the sizes of integer types and other language properties. D programs compiled for either data model can be executed on both 32-bit and 64-bit kernels. The .Fl 32 and .Fl 64 options also determine the .Xr elf 5 file format (ELF32 or ELF64) produced by the .Fl G option. .It Fl a Claim anonymous tracing state and display the traced data. You can combine the .Fl a option with the .Fl e option to force .Nm to exit immediately after consuming the anonymous tracing state rather than continuing to wait for new data. .It Fl A Generate directives for anonymous tracing and write them to .Pa /boot/dtrace.dof . This option constructs a set of dtrace configuration file directives to enable the specified probes for anonymous tracing and then exits. By default, .Nm attempts to store the directives to the file .Pa /boot/dtrace.dof . This behavior can be modified using the .Fl o option to specify an alternate output file. .It Fl b Ar bufsz Set the principal trace buffer size to .Ar bufsz . The trace buffer size can include any of the size suffixes k, m, g, or t. If the buffer space cannot be allocated, .Nm dtrace attempts to reduce the buffer size or exit depending on the setting of the bufresize property. .It Fl c Ar cmd Run the specified command .Ar cmd and exit upon its completion. If more than one .Fl c option is present on the command line, .Nm dtrace exits when all commands have exited, reporting the exit status for each child process as it terminates. The process ID of the first command is made available to any D programs specified on the command line or using the .Fl s option through the .Li $target macro variable. .It Fl C Run the C preprocessor .Xr cpp 1 over D programs before compiling them. You can pass options to the C preprocessor using the .Fl D , .Fl U , .Fl I , and .Fl H options. You can select the degree of C standard conformance if you use the .Fl X option. For a description of the set of tokens defined by the D compiler when invoking the C preprocessor, see .Fl X . +.It Fl d +Dump the D script to standard output, after syntactic transformations have been +applied. +For example, if-statements in D are implemented using such transformations: a +conditional clause in a probe body is replaced at compile-time by a separate +probe predicated on the original condition. .It Fl D Ar name Op Ns = Ns value Define .Ar name when invoking .Xr cpp 1 (enabled using the .Fl C option). If you specify an additional .Ar value , the name is assigned the corresponding value. This option passes the .Fl D option to each .Xr cpp 1 invocation. .It Fl e Exit after compiling any requests and consuming anonymous tracing state .Fl ( a option) but prior to enabling any probes. You can combine this option with the .Fl a option to print anonymous tracing data and exit. You can also combine this option with D compiler options. This combination verifies that the programs compile without actually executing them and enabling the corresponding instrumentation. .It Fl f Oo Oo Ar provider : Oc Ar module : Oc Ar function Oo Oo Ar predicate \ Oc Ar action Oc Specify function name to trace or list .Fl ( l option). The corresponding argument can include any of the probe description forms .Ar provider:module:function , .Ar module:function , or .Ar function . Unspecified probe description fields are left blank and match any probes regardless of the values in those fields. If no qualifiers other than .Ar function are specified in the description, all probes with the corresponding .Ar function are matched. The .Fl f argument can be suffixed with an optional D probe clause. You can specify more than one .Fl f option on the command line at a time. .It Fl F Coalesce trace output by identifying function entry and return. Function entry probe reports are indented and their output is prefixed with .Ql -> . Function return probe reports are unindented and their output is prefixed with .Ql <- . System call entry probe reports are indented and their output is prefixed with .Ql => . System call return probe reports are unindented and their output is prefixed with .Ql <= . .It Fl G Generate an ELF file containing an embedded DTrace program. The DTrace probes specified in the program are saved inside of a relocatable ELF object which can be linked into another program. If the .Fl o option is present, the ELF file is saved using the pathname specified as the argument for this operand. If the .Fl o option is not present and the DTrace program is contained with a file whose name is .Ar filename.d , then the ELF file is saved using the name .Ar filename.o . Otherwise the ELF file is saved using the name d.out. .It Fl h Generate a header file containing macros that correspond to probes in the specified provider definitions. This option should be used to generate a header file that is included by other source files for later use with the .Fl G option. If the .Fl o option is present, the header file is saved using the pathname specified as the argument for that option. If the .Fl o option is not present and the DTrace program is contained within a file whose name is .Ar filename.d , then the header file is saved using the name .Ar filename.h . .It Fl H Print the pathnames of included files when invoking .Xr cpp 1 (enabled using the .Fl C option). This option passes the .Fl H option to each .Xr cpp 1 invocation, causing it to display the list of pathnames, one for each line, to standard error. .It Fl i Ar probe-id Op Oo Ar predicate Oc Ar action Specify probe identifier .Ar ( probe-id ) to trace or list .Ar ( l option). You can specify probe IDs using decimal integers as shown by `dtrace -l`. The .Fl i argument can be suffixed with an optional D probe clause. You can specify more than one .Fl i option at a time. .It Fl I Ar path Add the specified directory .Ar path to the search path for #include files when invoking .Xr cpp 1 (enabled using the .Fl C option). This option passes the .Fl I option to each .Xr cpp 1 invocation. The specified .Ar path is inserted into the search path ahead of the default directory list. .It Fl l List probes instead of enabling them. If the .Fl l option is specified, .Nm produces a report of the probes matching the descriptions given using the .Fl P , m , f , n , i , and .Fl s options. If none of these options are specified, this option lists all probes. .It Fl L Ar path Add the specified directory .Ar path to the search path for DTrace libraries. DTrace libraries are used to contain common definitions that can be used when writing D programs. The specified .Ar path is added after the default library search path. .It Fl m Oo Ar provider : Oc Ar module Oo Oo Ar predicate Oc Ar action Oc Specify module name to trace or list .Fl ( l option). The corresponding argument can include any of the probe description forms .Ar provider:module or .Ar module . Unspecified probe description fields are left blank and match any probes regardless of the values in those fields. If no qualifiers other than .Ar module are specified in the description, all probes with a corresponding .Ar module are matched. The .Fl m argument can be suffixed with an optional D probe clause. More than one .Fl m option can be specified on the command line at a time. .It Fl n Oo Oo Oo Ar provider : Oc Ar module : Oc Ar function : Oc Ar name \ Oo Oo Ar predicate Oc Ar action Oc Specify probe name to trace or list .Fl ( l option). The corresponding argument can include any of the probe description forms .Ar provider:module:function:name , module:function:name , function:name , or .Ar name . Unspecified probe description fields are left blank and match any probes regardless of the values in those fields. If no qualifiers other than .Ar name are specified in the description, all probes with a corresponding .Ar name are matched. The .Fl n argument can be suffixed with an optional D probe clause. More than one .Fl n option can be specified on the command line at a time. .It Fl o Ar output Specify the .Ar output file for the .Fl A , G , and .Fl l options, or for the traced data itself. If the .Fl A option is present and .Fl o is not present, the default output file is .Pa /boot/dtrace.dof . If the .Fl G option is present and the .Fl s option's argument is of the form .Ar filename.d and .Fl o is not present, the default output file is .Ar filename.o . Otherwise the default output file is .Ar d.out . .It Fl p Ar pid Grab the specified process-ID .Ar pid , cache its symbol tables, and exit upon its completion. If more than one .Fl p option is present on the command line, .Nm exits when all commands have exited, reporting the exit status for each process as it terminates. The first process-ID is made available to any D programs specified on the command line or using the .Fl s option through the .Li $target macro variable. .It Fl P Ar provider Oo Oo Ar predicate Oc Ar action Oc Specify provider name to trace or list .Fl ( l option). The remaining probe description fields module, function, and name are left blank and match any probes regardless of the values in those fields. The .Fl P argument can be suffixed with an optional D probe clause. You can specify more than one .Fl P option on the command line at a time. .It Fl q Set quiet mode. .Nm suppresses messages such as the number of probes matched by the specified options and D programs and does not print column headers, the CPU ID, the probe ID, or insert newlines into the output. Only data traced and formatted by D program statements such as .Ql dtrace() and .Ql printf() is displayed to standard output. .It Fl s Ar script Compile the specified D program source file. If the .Fl e option is present, the program is compiled but instrumentation is not enabled. If the .Fl l option is present, the program is compiled and the set of probes matched by it is listed, but instrumentation is not enabled. If none of .Fl e , l , G , or .Fl A are present, the instrumentation specified by the D program is enabled and tracing begins. .It Fl S Show D compiler intermediate code. The D compiler produces a report of the intermediate code generated for each D program to standard error. .It Fl U Ar name Undefine the specified .Ar name when invoking .Xr cpp 1 (enabled using the .Fl C option). This option passes the .Fl U option to each .Xr cpp 1 invocation. .It Fl v Set verbose mode. If the .Fl v option is specified, .Nm produces a program stability report showing the minimum interface stability and dependency level for the specified D programs. .It Fl V Report the highest D programming interface version supported by .Nm . The version information is printed to standard output and the .Nm command exits. .It Fl w Permit destructive actions in D programs specified using the .Fl s , P , m , f , n , or .Fl i options. If the .Fl w option is not specified, .Nm does not permit the compilation or enabling of a D program that contains destructive actions. .It Fl x Ar arg Op Ns = Ns value Enable or modify a DTrace runtime option or D compiler option. Boolean options are enabled by specifying their name. Options with values are set by separating the option name and value with an equals sign (=). .Pp A .Ar size argument may be suffixed with one of .Cm K , .Cm M , .Cm G or .Cm T (either upper or lower case) to indicate a multiple of Kilobytes, Megabytes, Gigabytes or Terabytes respectively. .Pp A .Ar time argument may be suffixed with one of .Cm ns , .Cm nsec , .Cm us , .Cm usec , .Cm ms , .Cm msec , .Cm s , .Cm sec , .Cm m , .Cm min , .Cm h , .Cm hour , .Cm d , .Cm day , .Cm hz . If no suffix is specified .Cm hz will be used as the unit. .Bl -tag -width indent .It Sy aggrate Ns = Ns Ar time Rate of aggregation reading. .It Sy aggsize Ns = Ns Ar size Size of the aggregation buffer. .It Sy bufpolicy Ns = Ns Cm fill Ns | Ns Cm switch Ns | Ns Cm ring Specifies the buffer policy for the principal buffer. .It Sy bufresize Ns = Ns Cm auto Ns | Ns Cm manual Buffer resizing policy. .It Sy bufsize Ns = Ns Ar size Size of the per-CPU principal buffer. Same as the .Fl b flag. .It Sy cleanrate Ns = Ns Ar time Cleaning rate. Must be specified in number-per-second with the .Dq Li hz suffix. .It Sy cpu Ns = Ns Ar scalar Specifies the CPU on which to enable tracing. .It Sy cpp Run a C preprocessor over input files. Same as the .Fl C flag. .It Sy cpppath Ns = Ns Ar path Use the specified path for the C preprocessor rather than searching for .Dq cpp in .Ev PATH . .It Sy defaultargs Allow references to unspecified macro arguments. .It Sy destructive Allow destructive actions. Same as the .Fl w flag. .It Sy dynvarsize Ns = Ns Ar size Size of the dynamic variable space. .It Sy flowindent Turn on flow indentation. Same as the .Fl F flag. .It Sy grabanon Claim anonymous state. Same as the .Fl a flag. .It Sy jstackframes Ns = Ns Ar scalar Number of default stack frames for .Fn jstack . .It Sy jstackstrsize Ns = Ns Ar scalar Default string space size for .Fn jstack . .It Sy ldpath Ns = Ns Ar path When .Fl G is specified, use the specified path for a static linker rather than searching for .Dq "ld" in .Ev PATH . .It Sy libdir Ns = Ns Ar path Add a directory to the system library path. .It Sy nspec Ns = Ns Ar scalar Number of speculations. .It Sy nolibs Do not load D system libraries. .It Sy quiet Set quiet mode. Same as the .Fl q flag. .It Sy specsize Ns = Ns Ar size Size of the speculation buffer. .It Sy strsize Ns = Ns Ar size Maximum size of strings. .It Sy stackframes Ns = Ns Ar scalar Maximum number of kernelspace stack frames to unwind when executing the .Fn stack action. .It Sy stackindent Ns = Ns Ar scalar Number of whitespace characters to use when indenting .Fn stack and .Fn ustack output. .It Sy statusrate Ns = Ns Ar time Rate of status checking. .It Sy switchrate Ns = Ns Ar time Rate of buffer switching. .It Sy syslibdir Ns = Ns Ar path Path to system libraries. Defaults to .Pa /usr/lib/dtrace . .It Sy ustackframes Ns = Ns Ar scalar Maximum number of userspace stack frames to unwind when executing the .Fn ustack action. .El .It Fl X Cm a | c | s | t Specify the degree of conformance to the ISO C standard that should be selected when invoking .Xr cpp 1 (enabled using the .Fl C option). The .Fl X option argument affects the value and presence of the __STDC__ macro depending upon the value of the argument letter. .sp The .Fl X option supports the following arguments: .Bl -tag -width indent .It a Default. ISO C plus K&R compatibility extensions, with semantic changes required by ISO C. This is the default mode if .Fl X is not specified. The predefined macro __STDC__ has a value of 0 when .Xr cpp 1 is invoked in conjunction with the .Fl Xa option. .It c Conformance. Strictly conformant ISO C, without K&R C compatibility extensions. The predefined macro __STDC__ has a value of 1 when .Xr cpp 1 is invoked in conjunction with the .Fl \&Xc option. .It s K&R C only. The macro __STDC__ is not defined when .Xr cpp 1 is invoked in conjunction with the .Fl Xs option. .It t Transition. ISO C plus K&R C compatibility extensions, without semantic changes required by ISO C. The predefined macro __STDC__ has a value of 0 when .Xr cpp 1 is invoked in conjunction with the .Fl Xt option. .El .Pp As the .Fl X option only affects how the D compiler invokes the C preprocessor, the .Fl Xa and .Fl Xt options are equivalent from the perspective of D and both are provided only to ease re-use of settings from a C build environment. .Pp Regardless of the .Fl X mode, the following additional C preprocessor definitions are always specified and valid in all modes: .Bl -bullet -offset indent .It __sun .It __unix .It __SVR4 .It __sparc (on SPARC systems only) .It __sparcv9 (on SPARC systems only when 64-bit programs are compiled) .It __i386 (on x86 systems only when 32-bit programs are compiled) .It __amd64 (on x86 systems only when 64-bit programs are compiled) .It __`uname -s`_`uname -r` (for example, .Ql FreeBSD_9.2-RELEASE . .It __SUNW_D=1 .It .No __SUNW_D_VERSION=0x Ns Ar MMmmmuuu .Pp Where .Ar MM is the major release value in hexadecimal, .Ar mmm is the minor release value in hexadecimal, and .Ar uuu is the micro release value in hexadecimal. .El .It Fl Z Permit probe descriptions that match zero probes. If the .Fl Z option is not specified, .Nm reports an error and exits if any probe descriptions specified in D program files .Fl ( s option) or on the command line .Fl ( P , m , f , n , or .Fl i options) contain descriptions that do not match any known probes. .El .Sh OPERANDS You can specify zero or more additional arguments on the .Nm command line to define a set of macro variables and so forth). The additional arguments can be used in D programs specified using the .Fl s option or on the command line. .Sh FILES .Bl -tag -width /boot/dtrace.dof -compact .It Pa /boot/dtrace.dof File for anonymous tracing directives. .El .Sh EXIT STATUS The following exit statuses are returned: .Bl -tag -width indent .It 0 Successful completion. .Pp For D program requests, an exit status of 0 indicates that programs were successfully compiled, probes were successfully enabled, or anonymous state was successfully retrieved. .Nm returns 0 even if the specified tracing requests encountered errors or drops. .It 1 An error occurred. .Pp For D program requests, an exit status of 1 indicates that program compilation failed or that the specified request could not be satisfied. .It 2 Invalid command line options or arguments were specified. .El .Sh HISTORY The .Nm utility first appeared in .Fx 7.1 . .Sh SEE ALSO .Xr cpp 1 , .Xr elf 5 , .Xr SDT 9 .Rs .%T Solaris Dynamic Tracing Guide .Re diff --git a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c index 06eff2055c17..cdc476a43b08 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c +++ b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c @@ -1,2021 +1,2026 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2012 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef illumos #include #endif #include #ifdef illumos #include #endif #ifdef __FreeBSD__ #include #include #endif typedef struct dtrace_cmd { void (*dc_func)(struct dtrace_cmd *); /* function to compile arg */ dtrace_probespec_t dc_spec; /* probe specifier context */ char *dc_arg; /* argument from main argv */ const char *dc_name; /* name for error messages */ const char *dc_desc; /* desc for error messages */ dtrace_prog_t *dc_prog; /* program compiled from arg */ char dc_ofile[PATH_MAX]; /* derived output file name */ } dtrace_cmd_t; #define DMODE_VERS 0 /* display version information and exit (-V) */ #define DMODE_EXEC 1 /* compile program for enabling (-a/e/E) */ #define DMODE_ANON 2 /* compile program for anonymous tracing (-A) */ #define DMODE_LINK 3 /* compile program for linking with ELF (-G) */ #define DMODE_LIST 4 /* compile program and list probes (-l) */ #define DMODE_HEADER 5 /* compile program for headergen (-h) */ #define E_SUCCESS 0 #define E_ERROR 1 #define E_USAGE 2 static const char DTRACE_OPTSTR[] = - "3:6:aAb:Bc:CD: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:p:P:qs:SU:vVwx:X:Z"; static char **g_argv; static int g_argc; static char **g_objv; static int g_objc; static dtrace_cmd_t *g_cmdv; static int g_cmdc; static struct ps_prochandle **g_psv; static int g_psc; static int g_pslive; static char *g_pname; static int g_quiet; static int g_flowindent; static int g_intr; static int g_impatient; static int g_newline; #ifdef __FreeBSD__ static int g_siginfo; #endif static int g_total; static int g_cflags; static int g_oflags; static int g_verbose; static int g_exec = 1; static int g_mode = DMODE_EXEC; static int g_status = E_SUCCESS; static int g_grabanon = 0; static const char *g_ofile = NULL; static FILE *g_ofp; static dtrace_hdl_t *g_dtp; #ifdef illumos static char *g_etcfile = "/etc/system"; static const char *g_etcbegin = "* vvvv Added by DTrace"; static const char *g_etcend = "* ^^^^ Added by DTrace"; static const char *g_etc[] = { "*", "* The following forceload directives were added by dtrace(1M) to allow for", "* tracing during boot. If these directives are removed, the system will", "* continue to function, but tracing will not occur during boot as desired.", "* To remove these directives (and this block comment) automatically, run", "* \"dtrace -A\" without additional arguments. See the \"Anonymous Tracing\"", "* chapter of the Solaris Dynamic Tracing Guide for details.", "*", NULL }; #endif static int usage(FILE *fp) { static const char predact[] = "[[ predicate ] action ]"; - (void) fprintf(fp, "Usage: %s [-32|-64] [-aACeFGhHlqSvVwZ] " + (void) fprintf(fp, "Usage: %s [-32|-64] [-aACdeFGhHlqSvVwZ] " "[-b bufsz] [-c cmd] [-D name[=def]]\n\t[-I path] [-L path] " "[-o output] [-p pid] [-s script] [-U name]\n\t" "[-x opt[=val]] [-X a|c|s|t]\n\n" "\t[-P provider %s]\n" "\t[-m [ provider: ] module %s]\n" "\t[-f [[ provider: ] module: ] func %s]\n" "\t[-n [[[ provider: ] module: ] func: ] name %s]\n" "\t[-i probe-id %s] [ args ... ]\n\n", g_pname, predact, predact, predact, predact, predact); (void) fprintf(fp, "\tpredicate -> '/' D-expression '/'\n"); (void) fprintf(fp, "\t action -> '{' D-statements '}'\n"); (void) fprintf(fp, "\n" "\t-32 generate 32-bit D programs and ELF files\n" "\t-64 generate 64-bit D programs and ELF files\n\n" "\t-a claim anonymous tracing state\n" "\t-A generate driver.conf(4) directives for anonymous tracing\n" "\t-b set trace buffer size\n" "\t-c run specified command and exit upon its completion\n" "\t-C run cpp(1) preprocessor on script files\n" + "\t-d dump script after syntactic transformations\n" "\t-D define symbol when invoking preprocessor\n" "\t-e exit after compiling request but prior to enabling probes\n" "\t-f enable or list probes matching the specified function name\n" "\t-F coalesce trace output by function\n" "\t-G generate an ELF file containing embedded dtrace program\n" "\t-h generate a header file with definitions for static probes\n" "\t-H print included files when invoking preprocessor\n" "\t-i enable or list probes matching the specified probe id\n" "\t-I add include directory to preprocessor search path\n" "\t-l list probes matching specified criteria\n" "\t-L add library directory to library search path\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-o set output file\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-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 print D compiler intermediate code\n" "\t-U undefine symbol when invoking preprocessor\n" "\t-v set verbose mode (report stability attributes, arguments)\n" "\t-V report DTrace API version\n" "\t-w permit destructive actions\n" "\t-x enable or modify compiler and tracing options\n" "\t-X specify ISO C conformance settings for preprocessor\n" "\t-Z permit probe descriptions that match zero probes\n"); return (E_USAGE); } static void verror(const char *fmt, va_list ap) { int error = errno; (void) fprintf(stderr, "%s: ", g_pname); (void) vfprintf(stderr, fmt, ap); if (fmt[strlen(fmt) - 1] != '\n') (void) fprintf(stderr, ": %s\n", strerror(error)); } /*PRINTFLIKE1*/ static void fatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); verror(fmt, ap); va_end(ap); /* * Close the DTrace handle to ensure that any controlled processes are * correctly restored and continued. */ if (g_dtp) dtrace_close(g_dtp); exit(E_ERROR); } /*PRINTFLIKE1*/ static void dfatal(const char *fmt, ...) { #if !defined(illumos) && defined(NEED_ERRLOC) char *p_errfile = NULL; int errline = 0; #endif va_list ap; va_start(ap, fmt); (void) fprintf(stderr, "%s: ", g_pname); if (fmt != NULL) (void) vfprintf(stderr, fmt, ap); va_end(ap); if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') { (void) fprintf(stderr, ": %s\n", dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); } else if (fmt == NULL) { (void) fprintf(stderr, "%s\n", dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); } #if !defined(illumos) && defined(NEED_ERRLOC) dt_get_errloc(g_dtp, &p_errfile, &errline); if (p_errfile != NULL) printf("File '%s', line %d\n", p_errfile, errline); #endif /* * Close the DTrace handle to ensure that any controlled processes are * correctly restored and continued. */ dtrace_close(g_dtp); exit(E_ERROR); } /*PRINTFLIKE1*/ static void error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); verror(fmt, ap); va_end(ap); } /*PRINTFLIKE1*/ static void notice(const char *fmt, ...) { va_list ap; if (g_quiet) return; /* -q or quiet pragma suppresses notice()s */ va_start(ap, fmt); verror(fmt, ap); va_end(ap); } /*PRINTFLIKE1*/ static void oprintf(const char *fmt, ...) { va_list ap; int n; if (g_ofp == NULL) return; va_start(ap, fmt); n = vfprintf(g_ofp, fmt, ap); va_end(ap); if (n < 0) { if (errno != EINTR) { fatal("failed to write to %s", g_ofile ? g_ofile : ""); } clearerr(g_ofp); } } static char ** make_argv(char *s) { const char *ws = "\f\n\r\t\v "; char **argv = malloc(sizeof (char *) * (strlen(s) / 2 + 1)); int argc = 0; char *p = s; if (argv == NULL) return (NULL); for (p = strtok(s, ws); p != NULL; p = strtok(NULL, ws)) argv[argc++] = p; if (argc == 0) argv[argc++] = s; argv[argc] = NULL; return (argv); } static void dof_prune(const char *fname) { struct stat sbuf; size_t sz, i, j, mark, len; char *buf; int msg = 0, fd; if ((fd = open(fname, O_RDONLY)) == -1) { /* * This is okay only if the file doesn't exist at all. */ if (errno != ENOENT) fatal("failed to open %s", fname); return; } if (fstat(fd, &sbuf) == -1) fatal("failed to fstat %s", fname); if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL) fatal("failed to allocate memory for %s", fname); if (read(fd, buf, sz) != sz) fatal("failed to read %s", fname); buf[sz] = '\0'; (void) close(fd); if ((fd = open(fname, O_WRONLY | O_TRUNC)) == -1) fatal("failed to open %s for writing", fname); len = strlen("dof-data-"); for (mark = 0, i = 0; i < sz; i++) { if (strncmp(&buf[i], "dof-data-", len) != 0) continue; /* * This is only a match if it's in the 0th column. */ if (i != 0 && buf[i - 1] != '\n') continue; if (msg++ == 0) { error("cleaned up old anonymous " "enabling in %s\n", fname); } /* * We have a match. First write out our data up until now. */ if (i != mark) { if (write(fd, &buf[mark], i - mark) != i - mark) fatal("failed to write to %s", fname); } /* * Now scan forward until we scan past a newline. */ for (j = i; j < sz && buf[j] != '\n'; j++) continue; /* * Reset our mark. */ if ((mark = j + 1) >= sz) break; i = j; } if (mark < sz) { if (write(fd, &buf[mark], sz - mark) != sz - mark) fatal("failed to write to %s", fname); } (void) close(fd); free(buf); } #ifdef __FreeBSD__ /* * Use nextboot(8) to tell the loader to load DTrace kernel modules during * the next boot of the system. The nextboot(8) configuration is removed during * boot, so it will not persist indefinitely. */ static void bootdof_add(void) { char * const nbargv[] = { "nextboot", "-a", "-e", "dtraceall_load=\"YES\"", "-e", "dtrace_dof_load=\"YES\"", "-e", "dtrace_dof_name=\"/boot/dtrace.dof\"", "-e", "dtrace_dof_type=\"dtrace_dof\"", NULL, }; pid_t child; int err, status; err = posix_spawnp(&child, "nextboot", NULL, NULL, nbargv, NULL); if (err != 0) { error("failed to execute nextboot: %s", strerror(err)); exit(E_ERROR); } if (waitpid(child, &status, 0) != child) fatal("waiting for nextboot"); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { error("nextboot returned with status %d", status); exit(E_ERROR); } } #else static void etcsystem_prune(void) { struct stat sbuf; size_t sz; char *buf, *start, *end; int fd; char *fname = g_etcfile, *tmpname; if ((fd = open(fname, O_RDONLY)) == -1) fatal("failed to open %s", fname); if (fstat(fd, &sbuf) == -1) fatal("failed to fstat %s", fname); if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL) fatal("failed to allocate memory for %s", fname); if (read(fd, buf, sz) != sz) fatal("failed to read %s", fname); buf[sz] = '\0'; (void) close(fd); if ((start = strstr(buf, g_etcbegin)) == NULL) goto out; if (strlen(buf) != sz) { fatal("embedded nul byte in %s; manual repair of %s " "required\n", fname, fname); } if (strstr(start + 1, g_etcbegin) != NULL) { fatal("multiple start sentinels in %s; manual repair of %s " "required\n", fname, fname); } if ((end = strstr(buf, g_etcend)) == NULL) { fatal("missing end sentinel in %s; manual repair of %s " "required\n", fname, fname); } if (start > end) { fatal("end sentinel preceeds start sentinel in %s; manual " "repair of %s required\n", fname, fname); } end += strlen(g_etcend) + 1; bcopy(end, start, strlen(end) + 1); tmpname = alloca(sz = strlen(fname) + 80); (void) snprintf(tmpname, sz, "%s.dtrace.%d", fname, getpid()); if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, sbuf.st_mode)) == -1) fatal("failed to create %s", tmpname); if (write(fd, buf, strlen(buf)) < strlen(buf)) { (void) unlink(tmpname); fatal("failed to write to %s", tmpname); } (void) close(fd); if (chown(tmpname, sbuf.st_uid, sbuf.st_gid) != 0) { (void) unlink(tmpname); fatal("failed to chown(2) %s to uid %d, gid %d", tmpname, (int)sbuf.st_uid, (int)sbuf.st_gid); } if (rename(tmpname, fname) == -1) fatal("rename of %s to %s failed", tmpname, fname); error("cleaned up forceload directives in %s\n", fname); out: free(buf); } static void etcsystem_add(void) { const char *mods[20]; int nmods, line; if ((g_ofp = fopen(g_ofile = g_etcfile, "a")) == NULL) fatal("failed to open output file '%s'", g_ofile); oprintf("%s\n", g_etcbegin); for (line = 0; g_etc[line] != NULL; line++) oprintf("%s\n", g_etc[line]); nmods = dtrace_provider_modules(g_dtp, mods, sizeof (mods) / sizeof (char *) - 1); if (nmods >= sizeof (mods) / sizeof (char *)) fatal("unexpectedly large number of modules!"); mods[nmods++] = "dtrace"; for (line = 0; line < nmods; line++) oprintf("forceload: drv/%s\n", mods[line]); oprintf("%s\n", g_etcend); if (fclose(g_ofp) == EOF) fatal("failed to close output file '%s'", g_ofile); error("added forceload directives to %s\n", g_ofile); } #endif /* !__FreeBSD__ */ static void print_probe_info(const dtrace_probeinfo_t *p) { char buf[BUFSIZ]; char *user; int i; oprintf("\n\tProbe Description Attributes\n"); oprintf("\t\tIdentifier Names: %s\n", dtrace_stability_name(p->dtp_attr.dtat_name)); oprintf("\t\tData Semantics: %s\n", dtrace_stability_name(p->dtp_attr.dtat_data)); oprintf("\t\tDependency Class: %s\n", dtrace_class_name(p->dtp_attr.dtat_class)); oprintf("\n\tArgument Attributes\n"); oprintf("\t\tIdentifier Names: %s\n", dtrace_stability_name(p->dtp_arga.dtat_name)); oprintf("\t\tData Semantics: %s\n", dtrace_stability_name(p->dtp_arga.dtat_data)); oprintf("\t\tDependency Class: %s\n", dtrace_class_name(p->dtp_arga.dtat_class)); oprintf("\n\tArgument Types\n"); for (i = 0; i < p->dtp_argc; i++) { if (p->dtp_argv[i].dtt_flags & DTT_FL_USER) user = "userland "; else user = ""; if (ctf_type_name(p->dtp_argv[i].dtt_ctfp, p->dtp_argv[i].dtt_type, buf, sizeof (buf)) == NULL) (void) strlcpy(buf, "(unknown)", sizeof (buf)); oprintf("\t\targs[%d]: %s%s\n", i, user, buf); } if (p->dtp_argc == 0) oprintf("\t\tNone\n"); oprintf("\n"); } /*ARGSUSED*/ static int info_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last) { dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; dtrace_probedesc_t *pdp = &edp->dted_probe; dtrace_probeinfo_t p; if (edp == *last) return (0); oprintf("\n%s:%s:%s:%s\n", pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); if (dtrace_probe_info(dtp, pdp, &p) == 0) print_probe_info(&p); *last = edp; return (0); } /* * Execute the specified program by enabling the corresponding instrumentation. * If -e has been specified, we get the program info but do not enable it. If * -v has been specified, we print a stability report for the program. */ static void exec_prog(const dtrace_cmd_t *dcp) { dtrace_ecbdesc_t *last = NULL; dtrace_proginfo_t dpi; if (!g_exec) { dtrace_program_info(g_dtp, dcp->dc_prog, &dpi); } else if (dtrace_program_exec(g_dtp, dcp->dc_prog, &dpi) == -1) { dfatal("failed to enable '%s'", dcp->dc_name); } else { notice("%s '%s' matched %u probe%s\n", dcp->dc_desc, dcp->dc_name, dpi.dpi_matches, dpi.dpi_matches == 1 ? "" : "s"); } if (g_verbose) { oprintf("\nStability attributes for %s %s:\n", dcp->dc_desc, dcp->dc_name); oprintf("\n\tMinimum Probe Description Attributes\n"); oprintf("\t\tIdentifier Names: %s\n", dtrace_stability_name(dpi.dpi_descattr.dtat_name)); oprintf("\t\tData Semantics: %s\n", dtrace_stability_name(dpi.dpi_descattr.dtat_data)); oprintf("\t\tDependency Class: %s\n", dtrace_class_name(dpi.dpi_descattr.dtat_class)); oprintf("\n\tMinimum Statement Attributes\n"); oprintf("\t\tIdentifier Names: %s\n", dtrace_stability_name(dpi.dpi_stmtattr.dtat_name)); oprintf("\t\tData Semantics: %s\n", dtrace_stability_name(dpi.dpi_stmtattr.dtat_data)); oprintf("\t\tDependency Class: %s\n", dtrace_class_name(dpi.dpi_stmtattr.dtat_class)); if (!g_exec) { (void) dtrace_stmt_iter(g_dtp, dcp->dc_prog, (dtrace_stmt_f *)info_stmt, &last); } else oprintf("\n"); } g_total += dpi.dpi_matches; } /* * Print out the specified DOF buffer as a set of ASCII bytes appropriate for * storing in a driver.conf(4) file associated with the dtrace driver. */ static void anon_prog(const dtrace_cmd_t *dcp, dof_hdr_t *dof, int n) { const uchar_t *p, *q; if (dof == NULL) dfatal("failed to create DOF image for '%s'", dcp->dc_name); p = (uchar_t *)dof; q = p + dof->dofh_filesz; #ifdef __FreeBSD__ /* * On FreeBSD, the DOF file is read directly during boot - just write * two hex characters per byte. */ oprintf("dof-data-%d=", n); while (p < q) oprintf("%02x", *p++); oprintf("\n"); #else oprintf("dof-data-%d=0x%x", n, *p++); while (p < q) oprintf(",0x%x", *p++); oprintf(";\n"); #endif dtrace_dof_destroy(g_dtp, dof); } /* * Link the specified D program in DOF form into an ELF file for use in either * helpers, userland provider definitions, or both. If -o was specified, that * path is used as the output file name. If -o wasn't specified and the input * program is from a script whose name is %.d, use basename(%.o) as the output * file name. Otherwise we use "d.out" as the default output file name. */ static void link_prog(dtrace_cmd_t *dcp) { char *p; if (g_cmdc == 1 && g_ofile != NULL) { (void) strlcpy(dcp->dc_ofile, g_ofile, sizeof (dcp->dc_ofile)); } else if ((p = strrchr(dcp->dc_arg, '.')) != NULL && strcmp(p, ".d") == 0) { p[0] = '\0'; /* strip .d suffix */ (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), "%s.o", basename(dcp->dc_arg)); } else if (g_cmdc > 1) { (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), "d.out.%td", dcp - g_cmdv); } else { (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), "d.out"); } if (dtrace_program_link(g_dtp, dcp->dc_prog, DTRACE_D_PROBES, dcp->dc_ofile, g_objc, g_objv) != 0) dfatal("failed to link %s %s", dcp->dc_desc, dcp->dc_name); } /*ARGSUSED*/ static int list_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) { dtrace_probeinfo_t p; oprintf("%5d %10s %17s %33s %s\n", pdp->dtpd_id, pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); if (g_verbose && dtrace_probe_info(dtp, pdp, &p) == 0) print_probe_info(&p); if (g_intr != 0) return (1); return (0); } /*ARGSUSED*/ static int list_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last) { dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; if (edp == *last) return (0); if (dtrace_probe_iter(g_dtp, &edp->dted_probe, list_probe, NULL) != 0) { error("failed to match %s:%s:%s:%s: %s\n", edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod, edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); } *last = edp; return (0); } /* * List the probes corresponding to the specified program by iterating over * each statement and then matching probes to the statement probe descriptions. */ static void list_prog(const dtrace_cmd_t *dcp) { dtrace_ecbdesc_t *last = NULL; (void) dtrace_stmt_iter(g_dtp, dcp->dc_prog, (dtrace_stmt_f *)list_stmt, &last); } static void compile_file(dtrace_cmd_t *dcp) { char *arg0; FILE *fp; if ((fp = fopen(dcp->dc_arg, "r")) == NULL) fatal("failed to open %s", dcp->dc_arg); arg0 = g_argv[0]; g_argv[0] = dcp->dc_arg; if ((dcp->dc_prog = dtrace_program_fcompile(g_dtp, fp, g_cflags, g_argc, g_argv)) == NULL) dfatal("failed to compile script %s", dcp->dc_arg); g_argv[0] = arg0; (void) fclose(fp); dcp->dc_desc = "script"; dcp->dc_name = dcp->dc_arg; } static void compile_str(dtrace_cmd_t *dcp) { char *p; if ((dcp->dc_prog = dtrace_program_strcompile(g_dtp, dcp->dc_arg, dcp->dc_spec, g_cflags | DTRACE_C_PSPEC, g_argc, g_argv)) == NULL) dfatal("invalid probe specifier %s", dcp->dc_arg); if ((p = strpbrk(dcp->dc_arg, "{/;")) != NULL) *p = '\0'; /* crop name for reporting */ dcp->dc_desc = "description"; dcp->dc_name = dcp->dc_arg; } /*ARGSUSED*/ static void prochandler(struct ps_prochandle *P, const char *msg, void *arg) { #ifdef illumos const psinfo_t *prp = Ppsinfo(P); int pid = Pstatus(P)->pr_pid; char name[SIG2STR_MAX]; #else int wstatus = proc_getwstat(P); int pid = proc_getpid(P); #endif if (msg != NULL) { notice("pid %d: %s\n", pid, msg); return; } #ifdef illumos switch (Pstate(P)) { #else switch (proc_state(P)) { #endif case PS_UNDEAD: #ifdef illumos /* * Ideally we would like to always report pr_wstat here, but it * isn't possible given current /proc semantics. If we grabbed * the process, Ppsinfo() will either fail or return a zeroed * psinfo_t depending on how far the parent is in reaping it. * When /proc provides a stable pr_wstat in the status file, * this code can be improved by examining this new pr_wstat. */ if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) { notice("pid %d terminated by %s\n", pid, proc_signame(WTERMSIG(prp->pr_wstat), name, sizeof (name))); #else if (WIFSIGNALED(wstatus)) { notice("pid %d terminated by %d\n", pid, WTERMSIG(wstatus)); #endif #ifdef illumos } else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) { notice("pid %d exited with status %d\n", pid, WEXITSTATUS(prp->pr_wstat)); #else } else if (WEXITSTATUS(wstatus) != 0) { notice("pid %d exited with status %d\n", pid, WEXITSTATUS(wstatus)); #endif } else { notice("pid %d has exited\n", pid); } g_pslive--; break; case PS_LOST: notice("pid %d exec'd a set-id or unobservable program\n", pid); g_pslive--; break; } } /*ARGSUSED*/ static int errhandler(const dtrace_errdata_t *data, void *arg) { error(data->dteda_msg); return (DTRACE_HANDLE_OK); } /*ARGSUSED*/ static int drophandler(const dtrace_dropdata_t *data, void *arg) { error(data->dtdda_msg); return (DTRACE_HANDLE_OK); } /*ARGSUSED*/ static int setopthandler(const dtrace_setoptdata_t *data, void *arg) { if (strcmp(data->dtsda_option, "quiet") == 0) g_quiet = data->dtsda_newval != DTRACEOPT_UNSET; if (strcmp(data->dtsda_option, "flowindent") == 0) g_flowindent = data->dtsda_newval != DTRACEOPT_UNSET; return (DTRACE_HANDLE_OK); } #define BUFDUMPHDR(hdr) \ (void) printf("%s: %s%s\n", g_pname, hdr, strlen(hdr) > 0 ? ":" : ""); #define BUFDUMPSTR(ptr, field) \ (void) printf("%s: %20s => ", g_pname, #field); \ if ((ptr)->field != NULL) { \ const char *c = (ptr)->field; \ (void) printf("\""); \ do { \ if (*c == '\n') { \ (void) printf("\\n"); \ continue; \ } \ \ (void) printf("%c", *c); \ } while (*c++ != '\0'); \ (void) printf("\"\n"); \ } else { \ (void) printf("\n"); \ } #define BUFDUMPASSTR(ptr, field, str) \ (void) printf("%s: %20s => %s\n", g_pname, #field, str); #define BUFDUMP(ptr, field) \ (void) printf("%s: %20s => %lld\n", g_pname, #field, \ (long long)(ptr)->field); #define BUFDUMPPTR(ptr, field) \ (void) printf("%s: %20s => %s\n", g_pname, #field, \ (ptr)->field != NULL ? "" : ""); /*ARGSUSED*/ static int bufhandler(const dtrace_bufdata_t *bufdata, void *arg) { const dtrace_aggdata_t *agg = bufdata->dtbda_aggdata; const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc; const dtrace_probedesc_t *pd; uint32_t flags = bufdata->dtbda_flags; char buf[512], *c = buf, *end = c + sizeof (buf); int i, printed; struct { const char *name; uint32_t value; } flagnames[] = { { "AGGVAL", DTRACE_BUFDATA_AGGVAL }, { "AGGKEY", DTRACE_BUFDATA_AGGKEY }, { "AGGFORMAT", DTRACE_BUFDATA_AGGFORMAT }, { "AGGLAST", DTRACE_BUFDATA_AGGLAST }, { "???", UINT32_MAX }, { NULL } }; if (bufdata->dtbda_probe != NULL) { pd = bufdata->dtbda_probe->dtpda_pdesc; } else if (agg != NULL) { pd = agg->dtada_pdesc; } else { pd = NULL; } BUFDUMPHDR(">>> Called buffer handler"); BUFDUMPHDR(""); BUFDUMPHDR(" dtrace_bufdata"); BUFDUMPSTR(bufdata, dtbda_buffered); BUFDUMPPTR(bufdata, dtbda_probe); BUFDUMPPTR(bufdata, dtbda_aggdata); BUFDUMPPTR(bufdata, dtbda_recdesc); (void) snprintf(c, end - c, "0x%x ", bufdata->dtbda_flags); c += strlen(c); for (i = 0, printed = 0; flagnames[i].name != NULL; i++) { if (!(flags & flagnames[i].value)) continue; (void) snprintf(c, end - c, "%s%s", printed++ ? " | " : "(", flagnames[i].name); c += strlen(c); flags &= ~flagnames[i].value; } if (printed) (void) snprintf(c, end - c, ")"); BUFDUMPASSTR(bufdata, dtbda_flags, buf); BUFDUMPHDR(""); if (pd != NULL) { BUFDUMPHDR(" dtrace_probedesc"); BUFDUMPSTR(pd, dtpd_provider); BUFDUMPSTR(pd, dtpd_mod); BUFDUMPSTR(pd, dtpd_func); BUFDUMPSTR(pd, dtpd_name); BUFDUMPHDR(""); } if (rec != NULL) { BUFDUMPHDR(" dtrace_recdesc"); BUFDUMP(rec, dtrd_action); BUFDUMP(rec, dtrd_size); if (agg != NULL) { uint8_t *data; int lim = rec->dtrd_size; (void) sprintf(buf, "%d (data: ", rec->dtrd_offset); c = buf + strlen(buf); if (lim > sizeof (uint64_t)) lim = sizeof (uint64_t); data = (uint8_t *)agg->dtada_data + rec->dtrd_offset; for (i = 0; i < lim; i++) { (void) snprintf(c, end - c, "%s%02x", i == 0 ? "" : " ", *data++); c += strlen(c); } (void) snprintf(c, end - c, "%s)", lim < rec->dtrd_size ? " ..." : ""); BUFDUMPASSTR(rec, dtrd_offset, buf); } else { BUFDUMP(rec, dtrd_offset); } BUFDUMPHDR(""); } if (agg != NULL) { dtrace_aggdesc_t *desc = agg->dtada_desc; BUFDUMPHDR(" dtrace_aggdesc"); BUFDUMPSTR(desc, dtagd_name); BUFDUMP(desc, dtagd_varid); BUFDUMP(desc, dtagd_id); BUFDUMP(desc, dtagd_nrecs); BUFDUMPHDR(""); } return (DTRACE_HANDLE_OK); } /*ARGSUSED*/ static int chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) { dtrace_actkind_t act; uintptr_t addr; if (rec == NULL) { /* * We have processed the final record; output the newline if * we're not in quiet mode. */ if (!g_quiet) oprintf("\n"); return (DTRACE_CONSUME_NEXT); } act = rec->dtrd_action; addr = (uintptr_t)data->dtpda_data; if (act == DTRACEACT_EXIT) { g_status = *((uint32_t *)addr); return (DTRACE_CONSUME_NEXT); } return (DTRACE_CONSUME_THIS); } /*ARGSUSED*/ static int chew(const dtrace_probedata_t *data, void *arg) { dtrace_probedesc_t *pd = data->dtpda_pdesc; processorid_t cpu = data->dtpda_cpu; static int heading; if (g_impatient) { g_newline = 0; return (DTRACE_CONSUME_ABORT); } if (heading == 0) { if (!g_flowindent) { if (!g_quiet) { oprintf("%3s %6s %32s\n", "CPU", "ID", "FUNCTION:NAME"); } } else { oprintf("%3s %-41s\n", "CPU", "FUNCTION"); } heading = 1; } if (!g_flowindent) { if (!g_quiet) { char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2]; (void) snprintf(name, sizeof (name), "%s:%s", pd->dtpd_func, pd->dtpd_name); oprintf("%3d %6d %32s ", cpu, pd->dtpd_id, name); } } else { int indent = data->dtpda_indent; char *name; size_t len; if (data->dtpda_flow == DTRACEFLOW_NONE) { len = indent + DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 5; name = alloca(len); (void) snprintf(name, len, "%*s%s%s:%s", indent, "", data->dtpda_prefix, pd->dtpd_func, pd->dtpd_name); } else { len = indent + DTRACE_FUNCNAMELEN + 5; name = alloca(len); (void) snprintf(name, len, "%*s%s%s", indent, "", data->dtpda_prefix, pd->dtpd_func); } oprintf("%3d %-41s ", cpu, name); } return (DTRACE_CONSUME_THIS); } static void go(void) { int i; struct { char *name; char *optname; dtrace_optval_t val; } bufs[] = { { "buffer size", "bufsize" }, { "aggregation size", "aggsize" }, { "speculation size", "specsize" }, { "dynamic variable size", "dynvarsize" }, { NULL } }, rates[] = { { "cleaning rate", "cleanrate" }, { "status rate", "statusrate" }, { NULL } }; for (i = 0; bufs[i].name != NULL; i++) { if (dtrace_getopt(g_dtp, bufs[i].optname, &bufs[i].val) == -1) fatal("couldn't get option %s", bufs[i].optname); } for (i = 0; rates[i].name != NULL; i++) { if (dtrace_getopt(g_dtp, rates[i].optname, &rates[i].val) == -1) fatal("couldn't get option %s", rates[i].optname); } if (dtrace_go(g_dtp) == -1) dfatal("could not enable tracing"); for (i = 0; bufs[i].name != NULL; i++) { dtrace_optval_t j = 0, mul = 10; dtrace_optval_t nsize; if (bufs[i].val == DTRACEOPT_UNSET) continue; (void) dtrace_getopt(g_dtp, bufs[i].optname, &nsize); if (nsize == DTRACEOPT_UNSET || nsize == 0) continue; if (nsize >= bufs[i].val - sizeof (uint64_t)) continue; for (; (INT64_C(1) << mul) <= nsize; j++, mul += 10) continue; if (!(nsize & ((INT64_C(1) << (mul - 10)) - 1))) { error("%s lowered to %lld%c\n", bufs[i].name, (long long)nsize >> (mul - 10), " kmgtpe"[j]); } else { error("%s lowered to %lld bytes\n", bufs[i].name, (long long)nsize); } } for (i = 0; rates[i].name != NULL; i++) { dtrace_optval_t nval; char *dir; if (rates[i].val == DTRACEOPT_UNSET) continue; (void) dtrace_getopt(g_dtp, rates[i].optname, &nval); if (nval == DTRACEOPT_UNSET || nval == 0) continue; if (rates[i].val == nval) continue; dir = nval > rates[i].val ? "reduced" : "increased"; if (nval <= NANOSEC && (NANOSEC % nval) == 0) { error("%s %s to %lld hz\n", rates[i].name, dir, (long long)NANOSEC / (long long)nval); continue; } if ((nval % NANOSEC) == 0) { error("%s %s to once every %lld seconds\n", rates[i].name, dir, (long long)nval / (long long)NANOSEC); continue; } error("%s %s to once every %lld nanoseconds\n", rates[i].name, dir, (long long)nval); } } /*ARGSUSED*/ static void intr(int signo) { if (!g_intr) g_newline = 1; if (g_intr++) g_impatient = 1; } #ifdef __FreeBSD__ static void siginfo(int signo __unused) { g_siginfo++; g_newline = 1; } #endif static void installsighands(void) { struct sigaction act, oact; (void) sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = intr; if (sigaction(SIGINT, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGINT, &act, NULL); if (sigaction(SIGTERM, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGTERM, &act, NULL); #ifdef __FreeBSD__ if (sigaction(SIGPIPE, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGPIPE, &act, NULL); if (sigaction(SIGUSR1, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGUSR1, &act, NULL); act.sa_handler = siginfo; if (sigaction(SIGINFO, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGINFO, &act, NULL); #endif } int main(int argc, char *argv[]) { dtrace_bufdesc_t buf; dtrace_status_t status[2]; dtrace_optval_t opt; dtrace_cmd_t *dcp; g_ofp = stdout; int done = 0, mode = 0; int err, i, c; char *p, **v; struct ps_prochandle *P; pid_t pid; #ifdef __FreeBSD__ /* For %'d and the like. */ (void) setlocale(LC_NUMERIC, ""); /* For %T. */ (void) setlocale(LC_TIME, ""); #endif g_pname = basename(argv[0]); if (argc == 1) return (usage(stderr)); if ((g_argv = malloc(sizeof (char *) * argc)) == NULL || (g_cmdv = malloc(sizeof (dtrace_cmd_t) * argc)) == NULL || (g_psv = malloc(sizeof (struct ps_prochandle *) * argc)) == NULL) fatal("failed to allocate memory for arguments"); g_argv[g_argc++] = argv[0]; /* propagate argv[0] to D as $0/$$0 */ argv[0] = g_pname; /* rewrite argv[0] for getopt errors */ bzero(status, sizeof (status)); bzero(&buf, sizeof (buf)); /* * Make an initial pass through argv[] processing any arguments that * affect our behavior mode (g_mode) and flags used for dtrace_open(). * We also accumulate arguments that are not affiliated with getopt * options into g_argv[], and abort if any invalid options are found. */ for (optind = 1; optind < argc; optind++) { while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { switch (c) { case '3': if (strcmp(optarg, "2") != 0) { (void) fprintf(stderr, "%s: illegal option -- 3%s\n", argv[0], optarg); return (usage(stderr)); } g_oflags &= ~DTRACE_O_LP64; g_oflags |= DTRACE_O_ILP32; break; case '6': if (strcmp(optarg, "4") != 0) { (void) fprintf(stderr, "%s: illegal option -- 6%s\n", argv[0], optarg); return (usage(stderr)); } g_oflags &= ~DTRACE_O_ILP32; g_oflags |= DTRACE_O_LP64; break; case 'a': g_grabanon++; /* also checked in pass 2 below */ break; case 'A': g_mode = DMODE_ANON; g_exec = 0; mode++; break; case 'e': g_exec = 0; done = 1; break; case 'h': g_mode = DMODE_HEADER; g_oflags |= DTRACE_O_NODEV; g_cflags |= DTRACE_C_ZDEFS; /* -h implies -Z */ g_exec = 0; mode++; break; case 'G': g_mode = DMODE_LINK; g_oflags |= DTRACE_O_NODEV; g_cflags |= DTRACE_C_ZDEFS; /* -G implies -Z */ g_exec = 0; mode++; break; case 'l': g_mode = DMODE_LIST; g_cflags |= DTRACE_C_ZDEFS; /* -l implies -Z */ mode++; break; case 'V': g_mode = DMODE_VERS; mode++; break; default: if (strchr(DTRACE_OPTSTR, c) == NULL) return (usage(stderr)); } } if (optind < argc) g_argv[g_argc++] = argv[optind]; } if (mode > 1) { (void) fprintf(stderr, "%s: only one of the [-AGhlV] options " "can be specified at a time\n", g_pname); return (E_USAGE); } if (g_mode == DMODE_VERS) return (printf("%s: %s\n", g_pname, _dtrace_version) <= 0); /* * If we're in linker mode and the data model hasn't been specified, * we try to guess the appropriate setting by examining the object * files. We ignore certain errors since we'll catch them later when * we actually process the object files. */ if (g_mode == DMODE_LINK && (g_oflags & (DTRACE_O_ILP32 | DTRACE_O_LP64)) == 0 && elf_version(EV_CURRENT) != EV_NONE) { int fd; Elf *elf; GElf_Ehdr ehdr; for (i = 1; i < g_argc; i++) { if ((fd = open64(g_argv[i], O_RDONLY)) == -1) break; if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { (void) close(fd); break; } if (elf_kind(elf) != ELF_K_ELF || gelf_getehdr(elf, &ehdr) == NULL) { (void) close(fd); (void) elf_end(elf); break; } (void) close(fd); (void) elf_end(elf); if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { if (g_oflags & DTRACE_O_ILP32) { fatal("can't mix 32-bit and 64-bit " "object files\n"); } g_oflags |= DTRACE_O_LP64; } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) { if (g_oflags & DTRACE_O_LP64) { fatal("can't mix 32-bit and 64-bit " "object files\n"); } g_oflags |= DTRACE_O_ILP32; } else { break; } } } /* * Open libdtrace. If we are not actually going to be enabling any * instrumentation attempt to reopen libdtrace using DTRACE_O_NODEV. */ while ((g_dtp = dtrace_open(DTRACE_VERSION, g_oflags, &err)) == NULL) { if (!(g_oflags & DTRACE_O_NODEV) && !g_exec && !g_grabanon) { g_oflags |= DTRACE_O_NODEV; continue; } fatal("failed to initialize dtrace: %s\n", dtrace_errmsg(NULL, err)); } #if defined(__i386__) /* XXX The 32-bit seems to need more buffer space by default -sson */ (void) dtrace_setopt(g_dtp, "bufsize", "12m"); (void) dtrace_setopt(g_dtp, "aggsize", "12m"); #else (void) dtrace_setopt(g_dtp, "bufsize", "4m"); (void) dtrace_setopt(g_dtp, "aggsize", "4m"); #endif (void) dtrace_setopt(g_dtp, "temporal", "yes"); /* * If -G is specified, enable -xlink=dynamic and -xunodefs to permit * references to undefined symbols to remain as unresolved relocations. * If -A is specified, enable -xlink=primary to permit static linking * only to kernel symbols that are defined in a primary kernel module. */ if (g_mode == DMODE_LINK) { (void) dtrace_setopt(g_dtp, "linkmode", "dynamic"); (void) dtrace_setopt(g_dtp, "unodefs", NULL); /* * Use the remaining arguments as the list of object files * when in linker mode. */ g_objc = g_argc - 1; g_objv = g_argv + 1; /* * We still use g_argv[0], the name of the executable. */ g_argc = 1; } else if (g_mode == DMODE_ANON) (void) dtrace_setopt(g_dtp, "linkmode", "primary"); /* * Now that we have libdtrace open, make a second pass through argv[] * to perform any dtrace_setopt() calls and change any compiler flags. * We also accumulate any program specifications into our g_cmdv[] at * this time; these will compiled as part of the fourth processing pass. */ for (optind = 1; optind < argc; optind++) { while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { switch (c) { case 'a': if (dtrace_setopt(g_dtp, "grabanon", 0) != 0) dfatal("failed to set -a"); break; case 'b': if (dtrace_setopt(g_dtp, "bufsize", optarg) != 0) dfatal("failed to set -b %s", optarg); break; case 'B': g_ofp = NULL; break; case 'C': g_cflags |= DTRACE_C_CPP; break; + case 'd': + g_cflags |= DTRACE_C_SUGAR; + break; + case 'D': if (dtrace_setopt(g_dtp, "define", optarg) != 0) dfatal("failed to set -D %s", optarg); break; case 'f': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_FUNC; dcp->dc_arg = optarg; break; case 'F': if (dtrace_setopt(g_dtp, "flowindent", 0) != 0) dfatal("failed to set -F"); break; case 'H': if (dtrace_setopt(g_dtp, "cpphdrs", 0) != 0) dfatal("failed to set -H"); break; case 'i': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_NAME; dcp->dc_arg = optarg; break; case 'I': if (dtrace_setopt(g_dtp, "incdir", optarg) != 0) dfatal("failed to set -I %s", optarg); break; case 'L': if (dtrace_setopt(g_dtp, "libdir", optarg) != 0) dfatal("failed to set -L %s", optarg); break; case 'm': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_MOD; dcp->dc_arg = optarg; break; case 'n': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_NAME; dcp->dc_arg = optarg; break; case 'P': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_PROVIDER; dcp->dc_arg = optarg; break; case 'q': if (dtrace_setopt(g_dtp, "quiet", 0) != 0) dfatal("failed to set -q"); break; case 'o': g_ofile = optarg; break; case 's': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_file; dcp->dc_spec = DTRACE_PROBESPEC_NONE; dcp->dc_arg = optarg; break; case 'S': g_cflags |= DTRACE_C_DIFV; break; case 'U': if (dtrace_setopt(g_dtp, "undef", optarg) != 0) dfatal("failed to set -U %s", optarg); break; case 'v': g_verbose++; break; case 'w': if (dtrace_setopt(g_dtp, "destructive", 0) != 0) dfatal("failed to set -w"); break; case 'x': if ((p = strchr(optarg, '=')) != NULL) *p++ = '\0'; if (dtrace_setopt(g_dtp, optarg, p) != 0) dfatal("failed to set -x %s", optarg); break; case 'X': if (dtrace_setopt(g_dtp, "stdc", optarg) != 0) dfatal("failed to set -X %s", optarg); break; case 'Z': g_cflags |= DTRACE_C_ZDEFS; break; default: if (strchr(DTRACE_OPTSTR, c) == NULL) return (usage(stderr)); } } } if (g_ofp == NULL && g_mode != DMODE_EXEC) { (void) fprintf(stderr, "%s: -B not valid in combination" " with [-AGl] options\n", g_pname); return (E_USAGE); } if (g_ofp == NULL && g_ofile != NULL) { (void) fprintf(stderr, "%s: -B not valid in combination" " with -o option\n", g_pname); return (E_USAGE); } /* * In our third pass we handle any command-line options related to * grabbing or creating victim processes. The behavior of these calls * may been affected by any library options set by the second pass. */ for (optind = 1; optind < argc; optind++) { while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { switch (c) { case 'c': if ((v = make_argv(optarg)) == NULL) fatal("failed to allocate memory"); P = dtrace_proc_create(g_dtp, v[0], v, NULL, NULL); if (P == NULL) dfatal(NULL); /* dtrace_errmsg() only */ g_psv[g_psc++] = P; free(v); break; case 'p': errno = 0; pid = strtol(optarg, &p, 10); if (errno != 0 || p == optarg || p[0] != '\0') fatal("invalid pid: %s\n", optarg); P = dtrace_proc_grab(g_dtp, pid, 0); if (P == NULL) dfatal(NULL); /* dtrace_errmsg() only */ g_psv[g_psc++] = P; break; } } } /* * In our fourth pass we finish g_cmdv[] by calling dc_func to convert * each string or file specification into a compiled program structure. */ for (i = 0; i < g_cmdc; i++) g_cmdv[i].dc_func(&g_cmdv[i]); if (g_mode != DMODE_LIST) { if (dtrace_handle_err(g_dtp, &errhandler, NULL) == -1) dfatal("failed to establish error handler"); if (dtrace_handle_drop(g_dtp, &drophandler, NULL) == -1) dfatal("failed to establish drop handler"); if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1) dfatal("failed to establish proc handler"); if (dtrace_handle_setopt(g_dtp, &setopthandler, NULL) == -1) dfatal("failed to establish setopt handler"); if (g_ofp == NULL && dtrace_handle_buffered(g_dtp, &bufhandler, NULL) == -1) dfatal("failed to establish buffered handler"); } (void) dtrace_getopt(g_dtp, "flowindent", &opt); g_flowindent = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "grabanon", &opt); g_grabanon = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "quiet", &opt); g_quiet = opt != DTRACEOPT_UNSET; /* * Now make a fifth and final pass over the options that have been * turned into programs and saved in g_cmdv[], performing any mode- * specific processing. If g_mode is DMODE_EXEC, we will break out * of the switch() and continue on to the data processing loop. For * other modes, we will exit dtrace once mode-specific work is done. */ switch (g_mode) { case DMODE_EXEC: if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) fatal("failed to open output file '%s'", g_ofile); for (i = 0; i < g_cmdc; i++) exec_prog(&g_cmdv[i]); if (done && !g_grabanon) { dtrace_close(g_dtp); return (g_status); } break; case DMODE_ANON: if (g_ofile == NULL) #ifdef illumos g_ofile = "/kernel/drv/dtrace.conf"; #else /* * On FreeBSD, anonymous DOF data is written to * the DTrace DOF file. */ g_ofile = "/boot/dtrace.dof"; #endif dof_prune(g_ofile); /* strip out any old DOF directives */ #ifdef illumos etcsystem_prune(); /* string out any forceload directives */ #endif if (g_cmdc == 0) { dtrace_close(g_dtp); return (g_status); } if ((g_ofp = fopen(g_ofile, "a")) == NULL) fatal("failed to open output file '%s'", g_ofile); for (i = 0; i < g_cmdc; i++) { anon_prog(&g_cmdv[i], dtrace_dof_create(g_dtp, g_cmdv[i].dc_prog, 0), i); } /* * Dump out the DOF corresponding to the error handler and the * current options as the final DOF property in the .conf file. */ anon_prog(NULL, dtrace_geterr_dof(g_dtp), i++); anon_prog(NULL, dtrace_getopt_dof(g_dtp), i++); if (fclose(g_ofp) == EOF) fatal("failed to close output file '%s'", g_ofile); /* * These messages would use notice() rather than error(), but * we don't want them suppressed when -A is run on a D program * that itself contains a #pragma D option quiet. */ error("saved anonymous enabling in %s\n", g_ofile); #ifdef __FreeBSD__ bootdof_add(); #else etcsystem_add(); error("run update_drv(1M) or reboot to enable changes\n"); #endif dtrace_close(g_dtp); return (g_status); case DMODE_LINK: if (g_cmdc == 0) { (void) fprintf(stderr, "%s: -G requires one or more " "scripts or enabling options\n", g_pname); dtrace_close(g_dtp); return (E_USAGE); } for (i = 0; i < g_cmdc; i++) link_prog(&g_cmdv[i]); if (g_cmdc > 1 && g_ofile != NULL) { char **objv = alloca(g_cmdc * sizeof (char *)); for (i = 0; i < g_cmdc; i++) objv[i] = g_cmdv[i].dc_ofile; if (dtrace_program_link(g_dtp, NULL, DTRACE_D_PROBES, g_ofile, g_cmdc, objv) != 0) dfatal(NULL); /* dtrace_errmsg() only */ } dtrace_close(g_dtp); return (g_status); case DMODE_LIST: if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) fatal("failed to open output file '%s'", g_ofile); installsighands(); oprintf("%5s %10s %17s %33s %s\n", "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME"); for (i = 0; i < g_cmdc; i++) list_prog(&g_cmdv[i]); if (g_cmdc == 0) (void) dtrace_probe_iter(g_dtp, NULL, list_probe, NULL); dtrace_close(g_dtp); return (g_status); case DMODE_HEADER: if (g_cmdc == 0) { (void) fprintf(stderr, "%s: -h requires one or more " "scripts or enabling options\n", g_pname); dtrace_close(g_dtp); return (E_USAGE); } if (g_ofile == NULL) { char *p; if (g_cmdc > 1) { (void) fprintf(stderr, "%s: -h requires an " "output file if multiple scripts are " "specified\n", g_pname); dtrace_close(g_dtp); return (E_USAGE); } if ((p = strrchr(g_cmdv[0].dc_arg, '.')) == NULL || strcmp(p, ".d") != 0) { (void) fprintf(stderr, "%s: -h requires an " "output file if no scripts are " "specified\n", g_pname); dtrace_close(g_dtp); return (E_USAGE); } p[0] = '\0'; /* strip .d suffix */ g_ofile = p = g_cmdv[0].dc_ofile; (void) snprintf(p, sizeof (g_cmdv[0].dc_ofile), "%s.h", basename(g_cmdv[0].dc_arg)); } if ((g_ofp = fopen(g_ofile, "w")) == NULL) fatal("failed to open header file '%s'", g_ofile); oprintf("/*\n * Generated by dtrace(1M).\n */\n\n"); if (dtrace_program_header(g_dtp, g_ofp, g_ofile) != 0 || fclose(g_ofp) == EOF) dfatal("failed to create header file %s", g_ofile); dtrace_close(g_dtp); return (g_status); } /* * If -a and -Z were not specified and no probes have been matched, no * probe criteria was specified on the command line and we abort. */ if (g_total == 0 && !g_grabanon && !(g_cflags & DTRACE_C_ZDEFS)) dfatal("no probes %s\n", g_cmdc ? "matched" : "specified"); /* * Start tracing. Once we dtrace_go(), reload any options that affect * our globals in case consuming anonymous state has changed them. */ go(); (void) dtrace_getopt(g_dtp, "flowindent", &opt); g_flowindent = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "grabanon", &opt); g_grabanon = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "quiet", &opt); g_quiet = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "destructive", &opt); if (opt != DTRACEOPT_UNSET) notice("allowing destructive actions\n"); installsighands(); /* * Now that tracing is active and we are ready to consume trace data, * continue any grabbed or created processes, setting them running * using the /proc control mechanism inside of libdtrace. */ for (i = 0; i < g_psc; i++) dtrace_proc_continue(g_dtp, g_psv[i]); g_pslive = g_psc; /* count for prochandler() */ do { if (!g_intr && !done) dtrace_sleep(g_dtp); #ifdef __FreeBSD__ if (g_siginfo) { (void)dtrace_aggregate_print(g_dtp, g_ofp, NULL); g_siginfo = 0; } #endif if (g_newline) { /* * Output a newline just to make the output look * slightly cleaner. Note that we do this even in * "quiet" mode... */ oprintf("\n"); g_newline = 0; } if (done || g_intr || (g_psc != 0 && g_pslive == 0)) { done = 1; if (dtrace_stop(g_dtp) == -1) dfatal("couldn't stop tracing"); } switch (dtrace_work(g_dtp, g_ofp, chew, chewrec, NULL)) { case DTRACE_WORKSTATUS_DONE: done = 1; break; case DTRACE_WORKSTATUS_OKAY: break; default: if (!g_impatient && dtrace_errno(g_dtp) != EINTR) dfatal("processing aborted"); } if (g_ofp != NULL && fflush(g_ofp) == EOF) clearerr(g_ofp); } while (!done); oprintf("\n"); if (!g_impatient) { if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 && dtrace_errno(g_dtp) != EINTR) dfatal("failed to print aggregations"); } dtrace_close(g_dtp); return (g_status); } diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c index e63771c91e08..f3fda4af834e 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c @@ -1,2616 +1,2622 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent Inc. All rights reserved. * Copyright 2015 Gary Mills */ /* * DTrace D Language Compiler * * The code in this source file implements the main engine for the D language * compiler. The driver routine for the compiler is dt_compile(), below. The * compiler operates on either stdio FILEs or in-memory strings as its input * and can produce either dtrace_prog_t structures from a D program or a single * dtrace_difo_t structure from a D expression. Multiple entry points are * provided as wrappers around dt_compile() for the various input/output pairs. * The compiler itself is implemented across the following source files: * * dt_lex.l - lex scanner * dt_grammar.y - yacc grammar * dt_parser.c - parse tree creation and semantic checking * dt_decl.c - declaration stack processing * dt_xlator.c - D translator lookup and creation * dt_ident.c - identifier and symbol table routines * dt_pragma.c - #pragma processing and D pragmas * dt_printf.c - D printf() and printa() argument checking and processing * dt_cc.c - compiler driver and dtrace_prog_t construction * dt_cg.c - DIF code generator * dt_as.c - DIF assembler * dt_dof.c - dtrace_prog_t -> DOF conversion * * Several other source files provide collections of utility routines used by * these major files. The compiler itself is implemented in multiple passes: * * (1) The input program is scanned and parsed by dt_lex.l and dt_grammar.y * and parse tree nodes are constructed using the routines in dt_parser.c. * This node construction pass is described further in dt_parser.c. * * (2) The parse tree is "cooked" by assigning each clause a context (see the * routine dt_setcontext(), below) based on its probe description and then * recursively descending the tree performing semantic checking. The cook * routines are also implemented in dt_parser.c and described there. * * (3) For actions that are DIF expression statements, the DIF code generator * and assembler are invoked to create a finished DIFO for the statement. * * (4) The dtrace_prog_t data structures for the program clauses and actions * are built, containing pointers to any DIFOs created in step (3). * * (5) The caller invokes a routine in dt_dof.c to convert the finished program * into DOF format for use in anonymous tracing or enabling in the kernel. * * In the implementation, steps 2-4 are intertwined in that they are performed * in order for each clause as part of a loop that executes over the clauses. * * The D compiler currently implements nearly no optimization. The compiler * implements integer constant folding as part of pass (1), and a set of very * simple peephole optimizations as part of pass (3). As with any C compiler, * a large number of optimizations are possible on both the intermediate data * structures and the generated DIF code. These possibilities should be * investigated in the context of whether they will have any substantive effect * on the overall DTrace probe effect before they are undertaken. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const dtrace_diftype_t dt_void_rtype = { DIF_TYPE_CTF, CTF_K_INTEGER, 0, 0, 0 }; static const dtrace_diftype_t dt_int_rtype = { DIF_TYPE_CTF, CTF_K_INTEGER, 0, 0, sizeof (uint64_t) }; static void *dt_compile(dtrace_hdl_t *, int, dtrace_probespec_t, void *, uint_t, int, char *const[], FILE *, const char *); /*ARGSUSED*/ static int dt_idreset(dt_idhash_t *dhp, dt_ident_t *idp, void *ignored) { idp->di_flags &= ~(DT_IDFLG_REF | DT_IDFLG_MOD | DT_IDFLG_DIFR | DT_IDFLG_DIFW); return (0); } /*ARGSUSED*/ static int dt_idpragma(dt_idhash_t *dhp, dt_ident_t *idp, void *ignored) { yylineno = idp->di_lineno; xyerror(D_PRAGMA_UNUSED, "unused #pragma %s\n", (char *)idp->di_iarg); return (0); } static dtrace_stmtdesc_t * dt_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp, dtrace_attribute_t descattr, dtrace_attribute_t stmtattr) { dtrace_stmtdesc_t *sdp = dtrace_stmt_create(dtp, edp); if (sdp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); assert(yypcb->pcb_stmt == NULL); yypcb->pcb_stmt = sdp; sdp->dtsd_descattr = descattr; sdp->dtsd_stmtattr = stmtattr; return (sdp); } static dtrace_actdesc_t * dt_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *new; if ((new = dtrace_stmt_action(dtp, sdp)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); return (new); } /* * Utility function to determine if a given action description is destructive. * The dtdo_destructive bit is set for us by the DIF assembler (see dt_as.c). */ static int dt_action_destructive(const dtrace_actdesc_t *ap) { return (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind) || (ap->dtad_kind == DTRACEACT_DIFEXPR && ap->dtad_difo->dtdo_destructive)); } static void dt_stmt_append(dtrace_stmtdesc_t *sdp, const dt_node_t *dnp) { dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc; dtrace_actdesc_t *ap, *tap; int commit = 0; int speculate = 0; int datarec = 0; /* * Make sure that the new statement jibes with the rest of the ECB. */ for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) { if (ap->dtad_kind == DTRACEACT_COMMIT) { if (commit) { dnerror(dnp, D_COMM_COMM, "commit( ) may " "not follow commit( )\n"); } if (datarec) { dnerror(dnp, D_COMM_DREC, "commit( ) may " "not follow data-recording action(s)\n"); } for (tap = ap; tap != NULL; tap = tap->dtad_next) { if (!DTRACEACT_ISAGG(tap->dtad_kind)) continue; dnerror(dnp, D_AGG_COMM, "aggregating actions " "may not follow commit( )\n"); } commit = 1; continue; } if (ap->dtad_kind == DTRACEACT_SPECULATE) { if (speculate) { dnerror(dnp, D_SPEC_SPEC, "speculate( ) may " "not follow speculate( )\n"); } if (commit) { dnerror(dnp, D_SPEC_COMM, "speculate( ) may " "not follow commit( )\n"); } if (datarec) { dnerror(dnp, D_SPEC_DREC, "speculate( ) may " "not follow data-recording action(s)\n"); } speculate = 1; continue; } if (DTRACEACT_ISAGG(ap->dtad_kind)) { if (speculate) { dnerror(dnp, D_AGG_SPEC, "aggregating actions " "may not follow speculate( )\n"); } datarec = 1; continue; } if (speculate) { if (dt_action_destructive(ap)) { dnerror(dnp, D_ACT_SPEC, "destructive actions " "may not follow speculate( )\n"); } if (ap->dtad_kind == DTRACEACT_EXIT) { dnerror(dnp, D_EXIT_SPEC, "exit( ) may not " "follow speculate( )\n"); } } /* * Exclude all non data-recording actions. */ if (dt_action_destructive(ap) || ap->dtad_kind == DTRACEACT_DISCARD) continue; if (ap->dtad_kind == DTRACEACT_DIFEXPR && ap->dtad_difo->dtdo_rtype.dtdt_kind == DIF_TYPE_CTF && ap->dtad_difo->dtdo_rtype.dtdt_size == 0) continue; if (commit) { dnerror(dnp, D_DREC_COMM, "data-recording actions " "may not follow commit( )\n"); } if (!speculate) datarec = 1; } if (dtrace_stmt_add(yypcb->pcb_hdl, yypcb->pcb_prog, sdp) != 0) longjmp(yypcb->pcb_jmpbuf, dtrace_errno(yypcb->pcb_hdl)); if (yypcb->pcb_stmt == sdp) yypcb->pcb_stmt = NULL; } /* * For the first element of an aggregation tuple or for printa(), we create a * simple DIF program that simply returns the immediate value that is the ID * of the aggregation itself. This could be optimized in the future by * creating a new in-kernel dtad_kind that just returns an integer. */ static void dt_action_difconst(dtrace_actdesc_t *ap, uint_t id, dtrace_actkind_t kind) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dtrace_difo_t *dp = dt_zalloc(dtp, sizeof (dtrace_difo_t)); if (dp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dp->dtdo_buf = dt_alloc(dtp, sizeof (dif_instr_t) * 2); dp->dtdo_inttab = dt_alloc(dtp, sizeof (uint64_t)); if (dp->dtdo_buf == NULL || dp->dtdo_inttab == NULL) { dt_difo_free(dtp, dp); longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } dp->dtdo_buf[0] = DIF_INSTR_SETX(0, 1); /* setx DIF_INTEGER[0], %r1 */ dp->dtdo_buf[1] = DIF_INSTR_RET(1); /* ret %r1 */ dp->dtdo_len = 2; dp->dtdo_inttab[0] = id; dp->dtdo_intlen = 1; dp->dtdo_rtype = dt_int_rtype; ap->dtad_difo = dp; ap->dtad_kind = kind; } static void dt_action_clear(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dt_ident_t *aid; dtrace_actdesc_t *ap; dt_node_t *anp; char n[DT_TYPE_NAMELEN]; int argc = 0; for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list) argc++; /* count up arguments for error messages below */ if (argc != 1) { dnerror(dnp, D_CLEAR_PROTO, "%s( ) prototype mismatch: %d args passed, 1 expected\n", dnp->dn_ident->di_name, argc); } anp = dnp->dn_args; assert(anp != NULL); if (anp->dn_kind != DT_NODE_AGG) { dnerror(dnp, D_CLEAR_AGGARG, "%s( ) argument #1 is incompatible with prototype:\n" "\tprototype: aggregation\n\t argument: %s\n", dnp->dn_ident->di_name, dt_node_type_name(anp, n, sizeof (n))); } aid = anp->dn_ident; if (aid->di_gen == dtp->dt_gen && !(aid->di_flags & DT_IDFLG_MOD)) { dnerror(dnp, D_CLEAR_AGGBAD, "undefined aggregation: @%s\n", aid->di_name); } ap = dt_stmt_action(dtp, sdp); dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_LIBACT); ap->dtad_arg = DT_ACT_CLEAR; } static void dt_action_normalize(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dt_ident_t *aid; dtrace_actdesc_t *ap; dt_node_t *anp, *normal; int denormal = (strcmp(dnp->dn_ident->di_name, "denormalize") == 0); char n[DT_TYPE_NAMELEN]; int argc = 0; for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list) argc++; /* count up arguments for error messages below */ if ((denormal && argc != 1) || (!denormal && argc != 2)) { dnerror(dnp, D_NORMALIZE_PROTO, "%s( ) prototype mismatch: %d args passed, %d expected\n", dnp->dn_ident->di_name, argc, denormal ? 1 : 2); } anp = dnp->dn_args; assert(anp != NULL); if (anp->dn_kind != DT_NODE_AGG) { dnerror(dnp, D_NORMALIZE_AGGARG, "%s( ) argument #1 is incompatible with prototype:\n" "\tprototype: aggregation\n\t argument: %s\n", dnp->dn_ident->di_name, dt_node_type_name(anp, n, sizeof (n))); } if ((normal = anp->dn_list) != NULL && !dt_node_is_scalar(normal)) { dnerror(dnp, D_NORMALIZE_SCALAR, "%s( ) argument #2 must be of scalar type\n", dnp->dn_ident->di_name); } aid = anp->dn_ident; if (aid->di_gen == dtp->dt_gen && !(aid->di_flags & DT_IDFLG_MOD)) { dnerror(dnp, D_NORMALIZE_AGGBAD, "undefined aggregation: @%s\n", aid->di_name); } ap = dt_stmt_action(dtp, sdp); dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_LIBACT); if (denormal) { ap->dtad_arg = DT_ACT_DENORMALIZE; return; } ap->dtad_arg = DT_ACT_NORMALIZE; assert(normal != NULL); ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, normal); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_LIBACT; ap->dtad_arg = DT_ACT_NORMALIZE; } static void dt_action_trunc(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dt_ident_t *aid; dtrace_actdesc_t *ap; dt_node_t *anp, *trunc; char n[DT_TYPE_NAMELEN]; int argc = 0; for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list) argc++; /* count up arguments for error messages below */ if (argc > 2 || argc < 1) { dnerror(dnp, D_TRUNC_PROTO, "%s( ) prototype mismatch: %d args passed, %s expected\n", dnp->dn_ident->di_name, argc, argc < 1 ? "at least 1" : "no more than 2"); } anp = dnp->dn_args; assert(anp != NULL); trunc = anp->dn_list; if (anp->dn_kind != DT_NODE_AGG) { dnerror(dnp, D_TRUNC_AGGARG, "%s( ) argument #1 is incompatible with prototype:\n" "\tprototype: aggregation\n\t argument: %s\n", dnp->dn_ident->di_name, dt_node_type_name(anp, n, sizeof (n))); } if (argc == 2) { assert(trunc != NULL); if (!dt_node_is_scalar(trunc)) { dnerror(dnp, D_TRUNC_SCALAR, "%s( ) argument #2 must be of scalar type\n", dnp->dn_ident->di_name); } } aid = anp->dn_ident; if (aid->di_gen == dtp->dt_gen && !(aid->di_flags & DT_IDFLG_MOD)) { dnerror(dnp, D_TRUNC_AGGBAD, "undefined aggregation: @%s\n", aid->di_name); } ap = dt_stmt_action(dtp, sdp); dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_LIBACT); ap->dtad_arg = DT_ACT_TRUNC; ap = dt_stmt_action(dtp, sdp); if (argc == 1) { dt_action_difconst(ap, 0, DTRACEACT_LIBACT); } else { assert(trunc != NULL); dt_cg(yypcb, trunc); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_LIBACT; } ap->dtad_arg = DT_ACT_TRUNC; } static void dt_action_printa(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dt_ident_t *aid, *fid; dtrace_actdesc_t *ap; const char *format; dt_node_t *anp, *proto = NULL; char n[DT_TYPE_NAMELEN]; int argc = 0, argr = 0; for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list) argc++; /* count up arguments for error messages below */ switch (dnp->dn_args->dn_kind) { case DT_NODE_STRING: format = dnp->dn_args->dn_string; anp = dnp->dn_args->dn_list; argr = 2; break; case DT_NODE_AGG: format = NULL; anp = dnp->dn_args; argr = 1; break; default: format = NULL; anp = dnp->dn_args; argr = 1; } if (argc < argr) { dnerror(dnp, D_PRINTA_PROTO, "%s( ) prototype mismatch: %d args passed, %d expected\n", dnp->dn_ident->di_name, argc, argr); } assert(anp != NULL); while (anp != NULL) { if (anp->dn_kind != DT_NODE_AGG) { dnerror(dnp, D_PRINTA_AGGARG, "%s( ) argument #%d is incompatible with " "prototype:\n\tprototype: aggregation\n" "\t argument: %s\n", dnp->dn_ident->di_name, argr, dt_node_type_name(anp, n, sizeof (n))); } aid = anp->dn_ident; fid = aid->di_iarg; if (aid->di_gen == dtp->dt_gen && !(aid->di_flags & DT_IDFLG_MOD)) { dnerror(dnp, D_PRINTA_AGGBAD, "undefined aggregation: @%s\n", aid->di_name); } /* * If we have multiple aggregations, we must be sure that * their key signatures match. */ if (proto != NULL) { dt_printa_validate(proto, anp); } else { proto = anp; } if (format != NULL) { yylineno = dnp->dn_line; sdp->dtsd_fmtdata = dt_printf_create(yypcb->pcb_hdl, format); dt_printf_validate(sdp->dtsd_fmtdata, DT_PRINTF_AGGREGATION, dnp->dn_ident, 1, fid->di_id, ((dt_idsig_t *)aid->di_data)->dis_args); format = NULL; } ap = dt_stmt_action(dtp, sdp); dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_PRINTA); anp = anp->dn_list; argr++; } } static void dt_action_printflike(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp, dtrace_actkind_t kind) { dt_node_t *anp, *arg1; dtrace_actdesc_t *ap = NULL; char n[DT_TYPE_NAMELEN], *str; assert(DTRACEACT_ISPRINTFLIKE(kind)); if (dnp->dn_args->dn_kind != DT_NODE_STRING) { dnerror(dnp, D_PRINTF_ARG_FMT, "%s( ) argument #1 is incompatible with prototype:\n" "\tprototype: string constant\n\t argument: %s\n", dnp->dn_ident->di_name, dt_node_type_name(dnp->dn_args, n, sizeof (n))); } arg1 = dnp->dn_args->dn_list; yylineno = dnp->dn_line; str = dnp->dn_args->dn_string; /* * If this is an freopen(), we use an empty string to denote that * stdout should be restored. For other printf()-like actions, an * empty format string is illegal: an empty format string would * result in malformed DOF, and the compiler thus flags an empty * format string as a compile-time error. To avoid propagating the * freopen() special case throughout the system, we simply transpose * an empty string into a sentinel string (DT_FREOPEN_RESTORE) that * denotes that stdout should be restored. */ if (kind == DTRACEACT_FREOPEN) { if (strcmp(str, DT_FREOPEN_RESTORE) == 0) { /* * Our sentinel is always an invalid argument to * freopen(), but if it's been manually specified, we * must fail now instead of when the freopen() is * actually evaluated. */ dnerror(dnp, D_FREOPEN_INVALID, "%s( ) argument #1 cannot be \"%s\"\n", dnp->dn_ident->di_name, DT_FREOPEN_RESTORE); } if (str[0] == '\0') str = DT_FREOPEN_RESTORE; } sdp->dtsd_fmtdata = dt_printf_create(dtp, str); dt_printf_validate(sdp->dtsd_fmtdata, DT_PRINTF_EXACTLEN, dnp->dn_ident, 1, DTRACEACT_AGGREGATION, arg1); if (arg1 == NULL) { dif_instr_t *dbuf; dtrace_difo_t *dp; if ((dbuf = dt_alloc(dtp, sizeof (dif_instr_t))) == NULL || (dp = dt_zalloc(dtp, sizeof (dtrace_difo_t))) == NULL) { dt_free(dtp, dbuf); longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } dbuf[0] = DIF_INSTR_RET(DIF_REG_R0); /* ret %r0 */ dp->dtdo_buf = dbuf; dp->dtdo_len = 1; dp->dtdo_rtype = dt_int_rtype; ap = dt_stmt_action(dtp, sdp); ap->dtad_difo = dp; ap->dtad_kind = kind; return; } for (anp = arg1; anp != NULL; anp = anp->dn_list) { ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, anp); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = kind; } } static void dt_action_trace(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { int ctflib; dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); boolean_t istrace = (dnp->dn_ident->di_id == DT_ACT_TRACE); const char *act = istrace ? "trace" : "print"; if (dt_node_is_void(dnp->dn_args)) { dnerror(dnp->dn_args, istrace ? D_TRACE_VOID : D_PRINT_VOID, "%s( ) may not be applied to a void expression\n", act); } if (dt_node_resolve(dnp->dn_args, DT_IDENT_XLPTR) != NULL) { dnerror(dnp->dn_args, istrace ? D_TRACE_DYN : D_PRINT_DYN, "%s( ) may not be applied to a translated pointer\n", act); } if (dnp->dn_args->dn_kind == DT_NODE_AGG) { dnerror(dnp->dn_args, istrace ? D_TRACE_AGG : D_PRINT_AGG, "%s( ) may not be applied to an aggregation%s\n", act, istrace ? "" : " -- did you mean printa()?"); } dt_cg(yypcb, dnp->dn_args); /* * The print() action behaves identically to trace(), except that it * stores the CTF type of the argument (if present) within the DOF for * the DIFEXPR action. To do this, we set the 'dtsd_strdata' to point * to the fully-qualified CTF type ID for the result of the DIF * action. We use the ID instead of the name to handles complex types * like arrays and function pointers that can't be resolved by * ctf_type_lookup(). This is later processed by dtrace_dof_create() * and turned into a reference into the string table so that we can * get the type information when we process the data after the fact. In * the case where we are referring to userland CTF data, we also need to * to identify which ctf container in question we care about and encode * that within the name. */ if (dnp->dn_ident->di_id == DT_ACT_PRINT) { dt_node_t *dret; size_t n; dt_module_t *dmp; dret = yypcb->pcb_dret; dmp = dt_module_lookup_by_ctf(dtp, dret->dn_ctfp); n = snprintf(NULL, 0, "%s`%ld", dmp->dm_name, dret->dn_type) + 1; if (dmp->dm_pid != 0) { ctflib = dt_module_getlibid(dtp, dmp, dret->dn_ctfp); assert(ctflib >= 0); n = snprintf(NULL, 0, "%s`%d`%ld", dmp->dm_name, ctflib, dret->dn_type) + 1; } else { n = snprintf(NULL, 0, "%s`%ld", dmp->dm_name, dret->dn_type) + 1; } sdp->dtsd_strdata = dt_alloc(dtp, n); if (sdp->dtsd_strdata == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); (void) snprintf(sdp->dtsd_strdata, n, "%s`%ld", dmp->dm_name, dret->dn_type); if (dmp->dm_pid != 0) { (void) snprintf(sdp->dtsd_strdata, n, "%s`%d`%ld", dmp->dm_name, ctflib, dret->dn_type); } else { (void) snprintf(sdp->dtsd_strdata, n, "%s`%ld", dmp->dm_name, dret->dn_type); } } ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_DIFEXPR; } static void dt_action_tracemem(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_node_t *addr = dnp->dn_args; dt_node_t *max = dnp->dn_args->dn_list; dt_node_t *size; char n[DT_TYPE_NAMELEN]; if (dt_node_is_integer(addr) == 0 && dt_node_is_pointer(addr) == 0) { dnerror(addr, D_TRACEMEM_ADDR, "tracemem( ) argument #1 is incompatible with " "prototype:\n\tprototype: pointer or integer\n" "\t argument: %s\n", dt_node_type_name(addr, n, sizeof (n))); } if (dt_node_is_posconst(max) == 0) { dnerror(max, D_TRACEMEM_SIZE, "tracemem( ) argument #2 must " "be a non-zero positive integral constant expression\n"); } if ((size = max->dn_list) != NULL) { if (size->dn_list != NULL) { dnerror(size, D_TRACEMEM_ARGS, "tracemem ( ) prototype " "mismatch: expected at most 3 args\n"); } if (!dt_node_is_scalar(size)) { dnerror(size, D_TRACEMEM_DYNSIZE, "tracemem ( ) " "dynamic size (argument #3) must be of " "scalar type\n"); } dt_cg(yypcb, size); ap->dtad_difo = dt_as(yypcb); ap->dtad_difo->dtdo_rtype = dt_int_rtype; ap->dtad_kind = DTRACEACT_TRACEMEM_DYNSIZE; ap = dt_stmt_action(dtp, sdp); } dt_cg(yypcb, addr); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_TRACEMEM; ap->dtad_difo->dtdo_rtype.dtdt_flags |= DIF_TF_BYREF; ap->dtad_difo->dtdo_rtype.dtdt_size = max->dn_value; } static void dt_action_stack_args(dtrace_hdl_t *dtp, dtrace_actdesc_t *ap, dt_node_t *arg0) { ap->dtad_kind = DTRACEACT_STACK; if (dtp->dt_options[DTRACEOPT_STACKFRAMES] != DTRACEOPT_UNSET) { ap->dtad_arg = dtp->dt_options[DTRACEOPT_STACKFRAMES]; } else { ap->dtad_arg = 0; } if (arg0 != NULL) { if (arg0->dn_list != NULL) { dnerror(arg0, D_STACK_PROTO, "stack( ) prototype " "mismatch: too many arguments\n"); } if (dt_node_is_posconst(arg0) == 0) { dnerror(arg0, D_STACK_SIZE, "stack( ) size must be a " "non-zero positive integral constant expression\n"); } ap->dtad_arg = arg0->dn_value; } } static void dt_action_stack(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_action_stack_args(dtp, ap, dnp->dn_args); } static void dt_action_ustack_args(dtrace_hdl_t *dtp, dtrace_actdesc_t *ap, dt_node_t *dnp) { uint32_t nframes = 0; uint32_t strsize = 0; /* default string table size */ dt_node_t *arg0 = dnp->dn_args; dt_node_t *arg1 = arg0 != NULL ? arg0->dn_list : NULL; assert(dnp->dn_ident->di_id == DT_ACT_JSTACK || dnp->dn_ident->di_id == DT_ACT_USTACK); if (dnp->dn_ident->di_id == DT_ACT_JSTACK) { if (dtp->dt_options[DTRACEOPT_JSTACKFRAMES] != DTRACEOPT_UNSET) nframes = dtp->dt_options[DTRACEOPT_JSTACKFRAMES]; if (dtp->dt_options[DTRACEOPT_JSTACKSTRSIZE] != DTRACEOPT_UNSET) strsize = dtp->dt_options[DTRACEOPT_JSTACKSTRSIZE]; ap->dtad_kind = DTRACEACT_JSTACK; } else { assert(dnp->dn_ident->di_id == DT_ACT_USTACK); if (dtp->dt_options[DTRACEOPT_USTACKFRAMES] != DTRACEOPT_UNSET) nframes = dtp->dt_options[DTRACEOPT_USTACKFRAMES]; ap->dtad_kind = DTRACEACT_USTACK; } if (arg0 != NULL) { if (!dt_node_is_posconst(arg0)) { dnerror(arg0, D_USTACK_FRAMES, "ustack( ) argument #1 " "must be a non-zero positive integer constant\n"); } nframes = (uint32_t)arg0->dn_value; } if (arg1 != NULL) { if (arg1->dn_kind != DT_NODE_INT || ((arg1->dn_flags & DT_NF_SIGNED) && (int64_t)arg1->dn_value < 0)) { dnerror(arg1, D_USTACK_STRSIZE, "ustack( ) argument #2 " "must be a positive integer constant\n"); } if (arg1->dn_list != NULL) { dnerror(arg1, D_USTACK_PROTO, "ustack( ) prototype " "mismatch: too many arguments\n"); } strsize = (uint32_t)arg1->dn_value; } ap->dtad_arg = DTRACE_USTACK_ARG(nframes, strsize); } static void dt_action_ustack(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_action_ustack_args(dtp, ap, dnp); } static void dt_action_setopt(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap; dt_node_t *arg0, *arg1; /* * The prototype guarantees that we are called with either one or * two arguments, and that any arguments that are present are strings. */ arg0 = dnp->dn_args; arg1 = arg0->dn_list; ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, arg0); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_LIBACT; ap->dtad_arg = DT_ACT_SETOPT; ap = dt_stmt_action(dtp, sdp); if (arg1 == NULL) { dt_action_difconst(ap, 0, DTRACEACT_LIBACT); } else { dt_cg(yypcb, arg1); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_LIBACT; } ap->dtad_arg = DT_ACT_SETOPT; } /*ARGSUSED*/ static void dt_action_symmod_args(dtrace_hdl_t *dtp, dtrace_actdesc_t *ap, dt_node_t *dnp, dtrace_actkind_t kind) { assert(kind == DTRACEACT_SYM || kind == DTRACEACT_MOD || kind == DTRACEACT_USYM || kind == DTRACEACT_UMOD || kind == DTRACEACT_UADDR); dt_cg(yypcb, dnp); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = kind; ap->dtad_difo->dtdo_rtype.dtdt_size = sizeof (uint64_t); } static void dt_action_symmod(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp, dtrace_actkind_t kind) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_action_symmod_args(dtp, ap, dnp->dn_args, kind); } /*ARGSUSED*/ static void dt_action_ftruncate(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); /* * Library actions need a DIFO that serves as an argument. As * ftruncate() doesn't take an argument, we generate the constant 0 * in a DIFO; this constant will be ignored when the ftruncate() is * processed. */ dt_action_difconst(ap, 0, DTRACEACT_LIBACT); ap->dtad_arg = DT_ACT_FTRUNCATE; } /*ARGSUSED*/ static void dt_action_stop(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); ap->dtad_kind = DTRACEACT_STOP; ap->dtad_arg = 0; } /*ARGSUSED*/ static void dt_action_breakpoint(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); ap->dtad_kind = DTRACEACT_BREAKPOINT; ap->dtad_arg = 0; } /*ARGSUSED*/ static void dt_action_panic(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); ap->dtad_kind = DTRACEACT_PANIC; ap->dtad_arg = 0; } static void dt_action_chill(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, dnp->dn_args); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_CHILL; } static void dt_action_raise(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, dnp->dn_args); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_RAISE; } static void dt_action_exit(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, dnp->dn_args); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_EXIT; ap->dtad_difo->dtdo_rtype.dtdt_size = sizeof (int); } static void dt_action_speculate(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, dnp->dn_args); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_SPECULATE; } static void dt_action_printm(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_node_t *size = dnp->dn_args; dt_node_t *addr = dnp->dn_args->dn_list; char n[DT_TYPE_NAMELEN]; if (dt_node_is_posconst(size) == 0) { dnerror(size, D_PRINTM_SIZE, "printm( ) argument #1 must " "be a non-zero positive integral constant expression\n"); } if (dt_node_is_pointer(addr) == 0) { dnerror(addr, D_PRINTM_ADDR, "printm( ) argument #2 is incompatible with " "prototype:\n\tprototype: pointer\n" "\t argument: %s\n", dt_node_type_name(addr, n, sizeof (n))); } dt_cg(yypcb, addr); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_PRINTM; ap->dtad_difo->dtdo_rtype.dtdt_flags |= DIF_TF_BYREF; ap->dtad_difo->dtdo_rtype.dtdt_size = size->dn_value + sizeof(uintptr_t); } static void dt_action_commit(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, dnp->dn_args); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_COMMIT; } static void dt_action_discard(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, dnp->dn_args); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_DISCARD; } static void dt_compile_fun(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { switch (dnp->dn_expr->dn_ident->di_id) { case DT_ACT_BREAKPOINT: dt_action_breakpoint(dtp, dnp->dn_expr, sdp); break; case DT_ACT_CHILL: dt_action_chill(dtp, dnp->dn_expr, sdp); break; case DT_ACT_CLEAR: dt_action_clear(dtp, dnp->dn_expr, sdp); break; case DT_ACT_COMMIT: dt_action_commit(dtp, dnp->dn_expr, sdp); break; case DT_ACT_DENORMALIZE: dt_action_normalize(dtp, dnp->dn_expr, sdp); break; case DT_ACT_DISCARD: dt_action_discard(dtp, dnp->dn_expr, sdp); break; case DT_ACT_EXIT: dt_action_exit(dtp, dnp->dn_expr, sdp); break; case DT_ACT_FREOPEN: dt_action_printflike(dtp, dnp->dn_expr, sdp, DTRACEACT_FREOPEN); break; case DT_ACT_FTRUNCATE: dt_action_ftruncate(dtp, dnp->dn_expr, sdp); break; case DT_ACT_MOD: dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_MOD); break; case DT_ACT_NORMALIZE: dt_action_normalize(dtp, dnp->dn_expr, sdp); break; case DT_ACT_PANIC: dt_action_panic(dtp, dnp->dn_expr, sdp); break; case DT_ACT_PRINT: dt_action_trace(dtp, dnp->dn_expr, sdp); break; case DT_ACT_PRINTA: dt_action_printa(dtp, dnp->dn_expr, sdp); break; case DT_ACT_PRINTF: dt_action_printflike(dtp, dnp->dn_expr, sdp, DTRACEACT_PRINTF); break; case DT_ACT_PRINTM: dt_action_printm(dtp, dnp->dn_expr, sdp); break; case DT_ACT_RAISE: dt_action_raise(dtp, dnp->dn_expr, sdp); break; case DT_ACT_SETOPT: dt_action_setopt(dtp, dnp->dn_expr, sdp); break; case DT_ACT_SPECULATE: dt_action_speculate(dtp, dnp->dn_expr, sdp); break; case DT_ACT_STACK: dt_action_stack(dtp, dnp->dn_expr, sdp); break; case DT_ACT_STOP: dt_action_stop(dtp, dnp->dn_expr, sdp); break; case DT_ACT_SYM: dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_SYM); break; case DT_ACT_SYSTEM: dt_action_printflike(dtp, dnp->dn_expr, sdp, DTRACEACT_SYSTEM); break; case DT_ACT_TRACE: dt_action_trace(dtp, dnp->dn_expr, sdp); break; case DT_ACT_TRACEMEM: dt_action_tracemem(dtp, dnp->dn_expr, sdp); break; case DT_ACT_TRUNC: dt_action_trunc(dtp, dnp->dn_expr, sdp); break; case DT_ACT_UADDR: dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_UADDR); break; case DT_ACT_UMOD: dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_UMOD); break; case DT_ACT_USYM: dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_USYM); break; case DT_ACT_USTACK: case DT_ACT_JSTACK: dt_action_ustack(dtp, dnp->dn_expr, sdp); break; default: dnerror(dnp->dn_expr, D_UNKNOWN, "tracing function %s( ) is " "not yet supported\n", dnp->dn_expr->dn_ident->di_name); } } static void dt_compile_exp(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); dt_cg(yypcb, dnp->dn_expr); ap->dtad_difo = dt_as(yypcb); ap->dtad_difo->dtdo_rtype = dt_void_rtype; ap->dtad_kind = DTRACEACT_DIFEXPR; } static void dt_compile_agg(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) { dt_ident_t *aid, *fid; dt_node_t *anp, *incr = NULL; dtrace_actdesc_t *ap; uint_t n = 1, argmax; uint64_t arg = 0; /* * If the aggregation has no aggregating function applied to it, then * this statement has no effect. Flag this as a programming error. */ if (dnp->dn_aggfun == NULL) { dnerror(dnp, D_AGG_NULL, "expression has null effect: @%s\n", dnp->dn_ident->di_name); } aid = dnp->dn_ident; fid = dnp->dn_aggfun->dn_ident; if (dnp->dn_aggfun->dn_args != NULL && dt_node_is_scalar(dnp->dn_aggfun->dn_args) == 0) { dnerror(dnp->dn_aggfun, D_AGG_SCALAR, "%s( ) argument #1 must " "be of scalar type\n", fid->di_name); } /* * The ID of the aggregation itself is implicitly recorded as the first * member of each aggregation tuple so we can distinguish them later. */ ap = dt_stmt_action(dtp, sdp); dt_action_difconst(ap, aid->di_id, DTRACEACT_DIFEXPR); for (anp = dnp->dn_aggtup; anp != NULL; anp = anp->dn_list) { ap = dt_stmt_action(dtp, sdp); n++; if (anp->dn_kind == DT_NODE_FUNC) { if (anp->dn_ident->di_id == DT_ACT_STACK) { dt_action_stack_args(dtp, ap, anp->dn_args); continue; } if (anp->dn_ident->di_id == DT_ACT_USTACK || anp->dn_ident->di_id == DT_ACT_JSTACK) { dt_action_ustack_args(dtp, ap, anp); continue; } switch (anp->dn_ident->di_id) { case DT_ACT_UADDR: dt_action_symmod_args(dtp, ap, anp->dn_args, DTRACEACT_UADDR); continue; case DT_ACT_USYM: dt_action_symmod_args(dtp, ap, anp->dn_args, DTRACEACT_USYM); continue; case DT_ACT_UMOD: dt_action_symmod_args(dtp, ap, anp->dn_args, DTRACEACT_UMOD); continue; case DT_ACT_SYM: dt_action_symmod_args(dtp, ap, anp->dn_args, DTRACEACT_SYM); continue; case DT_ACT_MOD: dt_action_symmod_args(dtp, ap, anp->dn_args, DTRACEACT_MOD); continue; default: break; } } dt_cg(yypcb, anp); ap->dtad_difo = dt_as(yypcb); ap->dtad_kind = DTRACEACT_DIFEXPR; } if (fid->di_id == DTRACEAGG_LQUANTIZE) { /* * For linear quantization, we have between two and four * arguments in addition to the expression: * * arg1 => Base value * arg2 => Limit value * arg3 => Quantization level step size (defaults to 1) * arg4 => Quantization increment value (defaults to 1) */ dt_node_t *arg1 = dnp->dn_aggfun->dn_args->dn_list; dt_node_t *arg2 = arg1->dn_list; dt_node_t *arg3 = arg2->dn_list; dt_idsig_t *isp; uint64_t nlevels, step = 1, oarg; int64_t baseval, limitval; if (arg1->dn_kind != DT_NODE_INT) { dnerror(arg1, D_LQUANT_BASETYPE, "lquantize( ) " "argument #1 must be an integer constant\n"); } baseval = (int64_t)arg1->dn_value; if (baseval < INT32_MIN || baseval > INT32_MAX) { dnerror(arg1, D_LQUANT_BASEVAL, "lquantize( ) " "argument #1 must be a 32-bit quantity\n"); } if (arg2->dn_kind != DT_NODE_INT) { dnerror(arg2, D_LQUANT_LIMTYPE, "lquantize( ) " "argument #2 must be an integer constant\n"); } limitval = (int64_t)arg2->dn_value; if (limitval < INT32_MIN || limitval > INT32_MAX) { dnerror(arg2, D_LQUANT_LIMVAL, "lquantize( ) " "argument #2 must be a 32-bit quantity\n"); } if (limitval < baseval) { dnerror(dnp, D_LQUANT_MISMATCH, "lquantize( ) base (argument #1) must be less " "than limit (argument #2)\n"); } if (arg3 != NULL) { if (!dt_node_is_posconst(arg3)) { dnerror(arg3, D_LQUANT_STEPTYPE, "lquantize( ) " "argument #3 must be a non-zero positive " "integer constant\n"); } if ((step = arg3->dn_value) > UINT16_MAX) { dnerror(arg3, D_LQUANT_STEPVAL, "lquantize( ) " "argument #3 must be a 16-bit quantity\n"); } } nlevels = (limitval - baseval) / step; if (nlevels == 0) { dnerror(dnp, D_LQUANT_STEPLARGE, "lquantize( ) step (argument #3) too large: must " "have at least one quantization level\n"); } if (nlevels > UINT16_MAX) { dnerror(dnp, D_LQUANT_STEPSMALL, "lquantize( ) step " "(argument #3) too small: number of quantization " "levels must be a 16-bit quantity\n"); } arg = (step << DTRACE_LQUANTIZE_STEPSHIFT) | (nlevels << DTRACE_LQUANTIZE_LEVELSHIFT) | ((baseval << DTRACE_LQUANTIZE_BASESHIFT) & DTRACE_LQUANTIZE_BASEMASK); assert(arg != 0); isp = (dt_idsig_t *)aid->di_data; if (isp->dis_auxinfo == 0) { /* * This is the first time we've seen an lquantize() * for this aggregation; we'll store our argument * as the auxiliary signature information. */ isp->dis_auxinfo = arg; } else if ((oarg = isp->dis_auxinfo) != arg) { /* * If we have seen this lquantize() before and the * argument doesn't match the original argument, pick * the original argument apart to concisely report the * mismatch. */ int obaseval = DTRACE_LQUANTIZE_BASE(oarg); int onlevels = DTRACE_LQUANTIZE_LEVELS(oarg); int ostep = DTRACE_LQUANTIZE_STEP(oarg); if (obaseval != baseval) { dnerror(dnp, D_LQUANT_MATCHBASE, "lquantize( ) " "base (argument #1) doesn't match previous " "declaration: expected %d, found %d\n", obaseval, (int)baseval); } if (onlevels * ostep != nlevels * step) { dnerror(dnp, D_LQUANT_MATCHLIM, "lquantize( ) " "limit (argument #2) doesn't match previous" " declaration: expected %d, found %d\n", obaseval + onlevels * ostep, (int)baseval + (int)nlevels * (int)step); } if (ostep != step) { dnerror(dnp, D_LQUANT_MATCHSTEP, "lquantize( ) " "step (argument #3) doesn't match previous " "declaration: expected %d, found %d\n", ostep, (int)step); } /* * We shouldn't be able to get here -- one of the * parameters must be mismatched if the arguments * didn't match. */ assert(0); } incr = arg3 != NULL ? arg3->dn_list : NULL; argmax = 5; } if (fid->di_id == DTRACEAGG_LLQUANTIZE) { /* * For log/linear quantizations, we have between one and five * arguments in addition to the expression: * * arg1 => Factor * arg2 => Low magnitude * arg3 => High magnitude * arg4 => Number of steps per magnitude * arg5 => Quantization increment value (defaults to 1) */ dt_node_t *llarg = dnp->dn_aggfun->dn_args->dn_list; uint64_t oarg, order, v; dt_idsig_t *isp; int i; struct { char *str; /* string identifier */ int badtype; /* error on bad type */ int badval; /* error on bad value */ int mismatch; /* error on bad match */ int shift; /* shift value */ uint16_t value; /* value itself */ } args[] = { { "factor", D_LLQUANT_FACTORTYPE, D_LLQUANT_FACTORVAL, D_LLQUANT_FACTORMATCH, DTRACE_LLQUANTIZE_FACTORSHIFT }, { "low magnitude", D_LLQUANT_LOWTYPE, D_LLQUANT_LOWVAL, D_LLQUANT_LOWMATCH, DTRACE_LLQUANTIZE_LOWSHIFT }, { "high magnitude", D_LLQUANT_HIGHTYPE, D_LLQUANT_HIGHVAL, D_LLQUANT_HIGHMATCH, DTRACE_LLQUANTIZE_HIGHSHIFT }, { "linear steps per magnitude", D_LLQUANT_NSTEPTYPE, D_LLQUANT_NSTEPVAL, D_LLQUANT_NSTEPMATCH, DTRACE_LLQUANTIZE_NSTEPSHIFT }, { NULL } }; assert(arg == 0); for (i = 0; args[i].str != NULL; i++) { if (llarg->dn_kind != DT_NODE_INT) { dnerror(llarg, args[i].badtype, "llquantize( ) " "argument #%d (%s) must be an " "integer constant\n", i + 1, args[i].str); } if ((uint64_t)llarg->dn_value > UINT16_MAX) { dnerror(llarg, args[i].badval, "llquantize( ) " "argument #%d (%s) must be an unsigned " "16-bit quantity\n", i + 1, args[i].str); } args[i].value = (uint16_t)llarg->dn_value; assert(!(arg & ((uint64_t)UINT16_MAX << args[i].shift))); arg |= ((uint64_t)args[i].value << args[i].shift); llarg = llarg->dn_list; } assert(arg != 0); if (args[0].value < 2) { dnerror(dnp, D_LLQUANT_FACTORSMALL, "llquantize( ) " "factor (argument #1) must be two or more\n"); } if (args[1].value >= args[2].value) { dnerror(dnp, D_LLQUANT_MAGRANGE, "llquantize( ) " "high magnitude (argument #3) must be greater " "than low magnitude (argument #2)\n"); } if (args[3].value < args[0].value) { dnerror(dnp, D_LLQUANT_FACTORNSTEPS, "llquantize( ) " "factor (argument #1) must be less than or " "equal to the number of linear steps per " "magnitude (argument #4)\n"); } for (v = args[0].value; v < args[3].value; v *= args[0].value) continue; if ((args[3].value % args[0].value) || (v % args[3].value)) { dnerror(dnp, D_LLQUANT_FACTOREVEN, "llquantize( ) " "factor (argument #1) must evenly divide the " "number of steps per magnitude (argument #4), " "and the number of steps per magnitude must evenly " "divide a power of the factor\n"); } for (i = 0, order = 1; i <= args[2].value + 1; i++) { if (order * args[0].value > order) { order *= args[0].value; continue; } dnerror(dnp, D_LLQUANT_MAGTOOBIG, "llquantize( ) " "factor (%d) raised to power of high magnitude " "(%d) plus 1 overflows 64-bits\n", args[0].value, args[2].value); } isp = (dt_idsig_t *)aid->di_data; if (isp->dis_auxinfo == 0) { /* * This is the first time we've seen an llquantize() * for this aggregation; we'll store our argument * as the auxiliary signature information. */ isp->dis_auxinfo = arg; } else if ((oarg = isp->dis_auxinfo) != arg) { /* * If we have seen this llquantize() before and the * argument doesn't match the original argument, pick * the original argument apart to concisely report the * mismatch. */ int expected = 0, found = 0; for (i = 0; expected == found; i++) { assert(args[i].str != NULL); expected = (oarg >> args[i].shift) & UINT16_MAX; found = (arg >> args[i].shift) & UINT16_MAX; } dnerror(dnp, args[i - 1].mismatch, "llquantize( ) " "%s (argument #%d) doesn't match previous " "declaration: expected %d, found %d\n", args[i - 1].str, i, expected, found); } incr = llarg; argmax = 6; } if (fid->di_id == DTRACEAGG_QUANTIZE) { incr = dnp->dn_aggfun->dn_args->dn_list; argmax = 2; } if (incr != NULL) { if (!dt_node_is_scalar(incr)) { dnerror(dnp, D_PROTO_ARG, "%s( ) increment value " "(argument #%d) must be of scalar type\n", fid->di_name, argmax); } if ((anp = incr->dn_list) != NULL) { int argc = argmax; for (; anp != NULL; anp = anp->dn_list) argc++; dnerror(incr, D_PROTO_LEN, "%s( ) prototype " "mismatch: %d args passed, at most %d expected", fid->di_name, argc, argmax); } ap = dt_stmt_action(dtp, sdp); n++; dt_cg(yypcb, incr); ap->dtad_difo = dt_as(yypcb); ap->dtad_difo->dtdo_rtype = dt_void_rtype; ap->dtad_kind = DTRACEACT_DIFEXPR; } assert(sdp->dtsd_aggdata == NULL); sdp->dtsd_aggdata = aid; ap = dt_stmt_action(dtp, sdp); assert(fid->di_kind == DT_IDENT_AGGFUNC); assert(DTRACEACT_ISAGG(fid->di_id)); ap->dtad_kind = fid->di_id; ap->dtad_ntuple = n; ap->dtad_arg = arg; if (dnp->dn_aggfun->dn_args != NULL) { dt_cg(yypcb, dnp->dn_aggfun->dn_args); ap->dtad_difo = dt_as(yypcb); } } static void dt_compile_one_clause(dtrace_hdl_t *dtp, dt_node_t *cnp, dt_node_t *pnp) { dtrace_ecbdesc_t *edp; dtrace_stmtdesc_t *sdp; dt_node_t *dnp; yylineno = pnp->dn_line; dt_setcontext(dtp, pnp->dn_desc); (void) dt_node_cook(cnp, DT_IDFLG_REF); if (DT_TREEDUMP_PASS(dtp, 2)) dt_node_printr(cnp, stderr, 0); if ((edp = dt_ecbdesc_create(dtp, pnp->dn_desc)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); assert(yypcb->pcb_ecbdesc == NULL); yypcb->pcb_ecbdesc = edp; if (cnp->dn_pred != NULL) { dt_cg(yypcb, cnp->dn_pred); edp->dted_pred.dtpdd_difo = dt_as(yypcb); } if (cnp->dn_acts == NULL) { dt_stmt_append(dt_stmt_create(dtp, edp, cnp->dn_ctxattr, _dtrace_defattr), cnp); } for (dnp = cnp->dn_acts; dnp != NULL; dnp = dnp->dn_list) { assert(yypcb->pcb_stmt == NULL); sdp = dt_stmt_create(dtp, edp, cnp->dn_ctxattr, cnp->dn_attr); switch (dnp->dn_kind) { case DT_NODE_DEXPR: if (dnp->dn_expr->dn_kind == DT_NODE_AGG) dt_compile_agg(dtp, dnp->dn_expr, sdp); else dt_compile_exp(dtp, dnp, sdp); break; case DT_NODE_DFUNC: dt_compile_fun(dtp, dnp, sdp); break; case DT_NODE_AGG: dt_compile_agg(dtp, dnp, sdp); break; default: dnerror(dnp, D_UNKNOWN, "internal error -- node kind " "%u is not a valid statement\n", dnp->dn_kind); } assert(yypcb->pcb_stmt == sdp); dt_stmt_append(sdp, dnp); } assert(yypcb->pcb_ecbdesc == edp); dt_ecbdesc_release(dtp, edp); dt_endcontext(dtp); yypcb->pcb_ecbdesc = NULL; } static void dt_compile_clause(dtrace_hdl_t *dtp, dt_node_t *cnp) { dt_node_t *pnp; for (pnp = cnp->dn_pdescs; pnp != NULL; pnp = pnp->dn_list) dt_compile_one_clause(dtp, cnp, pnp); } static void dt_compile_xlator(dt_node_t *dnp) { dt_xlator_t *dxp = dnp->dn_xlator; dt_node_t *mnp; for (mnp = dnp->dn_members; mnp != NULL; mnp = mnp->dn_list) { assert(dxp->dx_membdif[mnp->dn_membid] == NULL); dt_cg(yypcb, mnp); dxp->dx_membdif[mnp->dn_membid] = dt_as(yypcb); } } void dt_setcontext(dtrace_hdl_t *dtp, dtrace_probedesc_t *pdp) { const dtrace_pattr_t *pap; dt_probe_t *prp; dt_provider_t *pvp; dt_ident_t *idp; char attrstr[8]; int err; size_t prov_len; /* * Both kernel and pid based providers are allowed to have names * ending with what could be interpreted as a number. We assume it's * a pid and that we may need to dynamically create probes for * that process if: * * (1) The provider doesn't exist, or, * (2) The provider exists and has DTRACE_PRIV_PROC privilege. * * On an error, dt_pid_create_probes() will set the error message * and tag -- we just have to longjmp() out of here. */ prov_len = strlen(pdp->dtpd_provider); if ((prov_len > 0 && isdigit(pdp->dtpd_provider[prov_len - 1])) && ((pvp = dt_provider_lookup(dtp, pdp->dtpd_provider)) == NULL || pvp->pv_desc.dtvd_priv.dtpp_flags & DTRACE_PRIV_PROC) && dt_pid_create_probes(pdp, dtp, yypcb) != 0) { longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } /* * Call dt_probe_info() to get the probe arguments and attributes. If * a representative probe is found, set 'pap' to the probe provider's * attributes. Otherwise set 'pap' to default Unstable attributes. */ if ((prp = dt_probe_info(dtp, pdp, &yypcb->pcb_pinfo)) == NULL) { pap = &_dtrace_prvdesc; err = dtrace_errno(dtp); bzero(&yypcb->pcb_pinfo, sizeof (dtrace_probeinfo_t)); yypcb->pcb_pinfo.dtp_attr = pap->dtpa_provider; yypcb->pcb_pinfo.dtp_arga = pap->dtpa_args; } else { pap = &prp->pr_pvp->pv_desc.dtvd_attr; err = 0; } if (err == EDT_NOPROBE && !(yypcb->pcb_cflags & DTRACE_C_ZDEFS)) { xyerror(D_PDESC_ZERO, "probe description %s:%s:%s:%s does not " "match any probes\n", pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); } if (err != EDT_NOPROBE && err != EDT_UNSTABLE && err != 0) xyerror(D_PDESC_INVAL, "%s\n", dtrace_errmsg(dtp, err)); dt_dprintf("set context to %s:%s:%s:%s [%u] prp=%p attr=%s argc=%d\n", pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name, pdp->dtpd_id, (void *)prp, dt_attr_str(yypcb->pcb_pinfo.dtp_attr, attrstr, sizeof (attrstr)), yypcb->pcb_pinfo.dtp_argc); /* * Reset the stability attributes of D global variables that vary * based on the attributes of the provider and context itself. */ if ((idp = dt_idhash_lookup(dtp->dt_globals, "probeprov")) != NULL) idp->di_attr = pap->dtpa_provider; if ((idp = dt_idhash_lookup(dtp->dt_globals, "probemod")) != NULL) idp->di_attr = pap->dtpa_mod; if ((idp = dt_idhash_lookup(dtp->dt_globals, "probefunc")) != NULL) idp->di_attr = pap->dtpa_func; if ((idp = dt_idhash_lookup(dtp->dt_globals, "probename")) != NULL) idp->di_attr = pap->dtpa_name; if ((idp = dt_idhash_lookup(dtp->dt_globals, "args")) != NULL) idp->di_attr = pap->dtpa_args; yypcb->pcb_pdesc = pdp; yypcb->pcb_probe = prp; } /* * Reset context-dependent variables and state at the end of cooking a D probe * definition clause. This ensures that external declarations between clauses * do not reference any stale context-dependent data from the previous clause. */ void dt_endcontext(dtrace_hdl_t *dtp) { static const char *const cvars[] = { "probeprov", "probemod", "probefunc", "probename", "args", NULL }; dt_ident_t *idp; int i; for (i = 0; cvars[i] != NULL; i++) { if ((idp = dt_idhash_lookup(dtp->dt_globals, cvars[i])) != NULL) idp->di_attr = _dtrace_defattr; } yypcb->pcb_pdesc = NULL; yypcb->pcb_probe = NULL; } static int dt_reduceid(dt_idhash_t *dhp, dt_ident_t *idp, dtrace_hdl_t *dtp) { if (idp->di_vers != 0 && idp->di_vers > dtp->dt_vmax) dt_idhash_delete(dhp, idp); return (0); } /* * When dtrace_setopt() is called for "version", it calls dt_reduce() to remove * any identifiers or translators that have been previously defined as bound to * a version greater than the specified version. Therefore, in our current * version implementation, establishing a binding is a one-way transformation. * In addition, no versioning is currently provided for types as our .d library * files do not define any types and we reserve prefixes DTRACE_ and dtrace_ * for our exclusive use. If required, type versioning will require more work. */ int dt_reduce(dtrace_hdl_t *dtp, dt_version_t v) { char s[DT_VERSION_STRMAX]; dt_xlator_t *dxp, *nxp; if (v > dtp->dt_vmax) return (dt_set_errno(dtp, EDT_VERSREDUCED)); else if (v == dtp->dt_vmax) return (0); /* no reduction necessary */ dt_dprintf("reducing api version to %s\n", dt_version_num2str(v, s, sizeof (s))); dtp->dt_vmax = v; for (dxp = dt_list_next(&dtp->dt_xlators); dxp != NULL; dxp = nxp) { nxp = dt_list_next(dxp); if ((dxp->dx_souid.di_vers != 0 && dxp->dx_souid.di_vers > v) || (dxp->dx_ptrid.di_vers != 0 && dxp->dx_ptrid.di_vers > v)) dt_list_delete(&dtp->dt_xlators, dxp); } (void) dt_idhash_iter(dtp->dt_macros, (dt_idhash_f *)dt_reduceid, dtp); (void) dt_idhash_iter(dtp->dt_aggs, (dt_idhash_f *)dt_reduceid, dtp); (void) dt_idhash_iter(dtp->dt_globals, (dt_idhash_f *)dt_reduceid, dtp); (void) dt_idhash_iter(dtp->dt_tls, (dt_idhash_f *)dt_reduceid, dtp); return (0); } /* * Fork and exec the cpp(1) preprocessor to run over the specified input file, * and return a FILE handle for the cpp output. We use the /dev/fd filesystem * here to simplify the code by leveraging file descriptor inheritance. */ static FILE * dt_preproc(dtrace_hdl_t *dtp, FILE *ifp) { int argc = dtp->dt_cpp_argc; char **argv = malloc(sizeof (char *) * (argc + 5)); FILE *ofp = tmpfile(); #ifdef illumos char ipath[20], opath[20]; /* big enough for /dev/fd/ + INT_MAX + \0 */ #endif char verdef[32]; /* big enough for -D__SUNW_D_VERSION=0x%08x + \0 */ struct sigaction act, oact; sigset_t mask, omask; int wstat, estat; pid_t pid; #ifdef illumos off64_t off; #else off_t off = 0; #endif int c; if (argv == NULL || ofp == NULL) { (void) dt_set_errno(dtp, errno); goto err; } /* * If the input is a seekable file, see if it is an interpreter file. * If we see #!, seek past the first line because cpp will choke on it. * We start cpp just prior to the \n at the end of this line so that * it still sees the newline, ensuring that #line values are correct. */ if (isatty(fileno(ifp)) == 0 && (off = ftello64(ifp)) != -1) { if ((c = fgetc(ifp)) == '#' && (c = fgetc(ifp)) == '!') { for (off += 2; c != '\n'; off++) { if ((c = fgetc(ifp)) == EOF) break; } if (c == '\n') off--; /* start cpp just prior to \n */ } (void) fflush(ifp); (void) fseeko64(ifp, off, SEEK_SET); } #ifdef illumos (void) snprintf(ipath, sizeof (ipath), "/dev/fd/%d", fileno(ifp)); (void) snprintf(opath, sizeof (opath), "/dev/fd/%d", fileno(ofp)); #endif bcopy(dtp->dt_cpp_argv, argv, sizeof (char *) * argc); (void) snprintf(verdef, sizeof (verdef), "-D__SUNW_D_VERSION=0x%08x", dtp->dt_vmax); argv[argc++] = verdef; #ifdef illumos switch (dtp->dt_stdcmode) { case DT_STDC_XA: case DT_STDC_XT: argv[argc++] = "-D__STDC__=0"; break; case DT_STDC_XC: argv[argc++] = "-D__STDC__=1"; break; } argv[argc++] = ipath; argv[argc++] = opath; #else argv[argc++] = "-P"; #endif argv[argc] = NULL; /* * libdtrace must be able to be embedded in other programs that may * include application-specific signal handlers. Therefore, if we * need to fork to run cpp(1), we must avoid generating a SIGCHLD * that could confuse the containing application. To do this, * we block SIGCHLD and reset its disposition to SIG_DFL. * We restore our signal state once we are done. */ (void) sigemptyset(&mask); (void) sigaddset(&mask, SIGCHLD); (void) sigprocmask(SIG_BLOCK, &mask, &omask); bzero(&act, sizeof (act)); act.sa_handler = SIG_DFL; (void) sigaction(SIGCHLD, &act, &oact); if ((pid = fork1()) == -1) { (void) sigaction(SIGCHLD, &oact, NULL); (void) sigprocmask(SIG_SETMASK, &omask, NULL); (void) dt_set_errno(dtp, EDT_CPPFORK); goto err; } if (pid == 0) { #ifndef illumos if (isatty(fileno(ifp)) == 0) lseek(fileno(ifp), off, SEEK_SET); dup2(fileno(ifp), 0); dup2(fileno(ofp), 1); #endif (void) execvp(dtp->dt_cpp_path, argv); _exit(errno == ENOENT ? 127 : 126); } do { dt_dprintf("waiting for %s (PID %d)\n", dtp->dt_cpp_path, (int)pid); } while (waitpid(pid, &wstat, 0) == -1 && errno == EINTR); (void) sigaction(SIGCHLD, &oact, NULL); (void) sigprocmask(SIG_SETMASK, &omask, NULL); dt_dprintf("%s returned exit status 0x%x\n", dtp->dt_cpp_path, wstat); estat = WIFEXITED(wstat) ? WEXITSTATUS(wstat) : -1; if (estat != 0) { switch (estat) { case 126: (void) dt_set_errno(dtp, EDT_CPPEXEC); break; case 127: (void) dt_set_errno(dtp, EDT_CPPENT); break; default: (void) dt_set_errno(dtp, EDT_CPPERR); } goto err; } free(argv); (void) fflush(ofp); (void) fseek(ofp, 0, SEEK_SET); return (ofp); err: free(argv); (void) fclose(ofp); return (NULL); } static void dt_lib_depend_error(dtrace_hdl_t *dtp, const char *format, ...) { va_list ap; va_start(ap, format); dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap); va_end(ap); } int dt_lib_depend_add(dtrace_hdl_t *dtp, dt_list_t *dlp, const char *arg) { dt_lib_depend_t *dld; const char *end; assert(arg != NULL); if ((end = strrchr(arg, '/')) == NULL) return (dt_set_errno(dtp, EINVAL)); if ((dld = dt_zalloc(dtp, sizeof (dt_lib_depend_t))) == NULL) return (-1); if ((dld->dtld_libpath = dt_alloc(dtp, MAXPATHLEN)) == NULL) { dt_free(dtp, dld); return (-1); } (void) strlcpy(dld->dtld_libpath, arg, end - arg + 2); if ((dld->dtld_library = strdup(arg)) == NULL) { dt_free(dtp, dld->dtld_libpath); dt_free(dtp, dld); return (dt_set_errno(dtp, EDT_NOMEM)); } dt_list_append(dlp, dld); return (0); } dt_lib_depend_t * dt_lib_depend_lookup(dt_list_t *dld, const char *arg) { dt_lib_depend_t *dldn; for (dldn = dt_list_next(dld); dldn != NULL; dldn = dt_list_next(dldn)) { if (strcmp(dldn->dtld_library, arg) == 0) return (dldn); } return (NULL); } /* * Go through all the library files, and, if any library dependencies exist for * that file, add it to that node's list of dependents. The result of this * will be a graph which can then be topologically sorted to produce a * compilation order. */ static int dt_lib_build_graph(dtrace_hdl_t *dtp) { dt_lib_depend_t *dld, *dpld; for (dld = dt_list_next(&dtp->dt_lib_dep); dld != NULL; dld = dt_list_next(dld)) { char *library = dld->dtld_library; for (dpld = dt_list_next(&dld->dtld_dependencies); dpld != NULL; dpld = dt_list_next(dpld)) { dt_lib_depend_t *dlda; if ((dlda = dt_lib_depend_lookup(&dtp->dt_lib_dep, dpld->dtld_library)) == NULL) { dt_lib_depend_error(dtp, "Invalid library dependency in %s: %s\n", dld->dtld_library, dpld->dtld_library); return (dt_set_errno(dtp, EDT_COMPILER)); } if ((dt_lib_depend_add(dtp, &dlda->dtld_dependents, library)) != 0) { return (-1); /* preserve dt_errno */ } } } return (0); } static int dt_topo_sort(dtrace_hdl_t *dtp, dt_lib_depend_t *dld, int *count) { dt_lib_depend_t *dpld, *dlda, *new; dld->dtld_start = ++(*count); for (dpld = dt_list_next(&dld->dtld_dependents); dpld != NULL; dpld = dt_list_next(dpld)) { dlda = dt_lib_depend_lookup(&dtp->dt_lib_dep, dpld->dtld_library); assert(dlda != NULL); if (dlda->dtld_start == 0 && dt_topo_sort(dtp, dlda, count) == -1) return (-1); } if ((new = dt_zalloc(dtp, sizeof (dt_lib_depend_t))) == NULL) return (-1); if ((new->dtld_library = strdup(dld->dtld_library)) == NULL) { dt_free(dtp, new); return (dt_set_errno(dtp, EDT_NOMEM)); } new->dtld_start = dld->dtld_start; new->dtld_finish = dld->dtld_finish = ++(*count); dt_list_prepend(&dtp->dt_lib_dep_sorted, new); dt_dprintf("library %s sorted (%d/%d)\n", new->dtld_library, new->dtld_start, new->dtld_finish); return (0); } static int dt_lib_depend_sort(dtrace_hdl_t *dtp) { dt_lib_depend_t *dld, *dpld, *dlda; int count = 0; if (dt_lib_build_graph(dtp) == -1) return (-1); /* preserve dt_errno */ /* * Perform a topological sort of the graph that hangs off * dtp->dt_lib_dep. The result of this process will be a * dependency ordered list located at dtp->dt_lib_dep_sorted. */ for (dld = dt_list_next(&dtp->dt_lib_dep); dld != NULL; dld = dt_list_next(dld)) { if (dld->dtld_start == 0 && dt_topo_sort(dtp, dld, &count) == -1) return (-1); /* preserve dt_errno */; } /* * Check the graph for cycles. If an ancestor's finishing time is * less than any of its dependent's finishing times then a back edge * exists in the graph and this is a cycle. */ for (dld = dt_list_next(&dtp->dt_lib_dep); dld != NULL; dld = dt_list_next(dld)) { for (dpld = dt_list_next(&dld->dtld_dependents); dpld != NULL; dpld = dt_list_next(dpld)) { dlda = dt_lib_depend_lookup(&dtp->dt_lib_dep_sorted, dpld->dtld_library); assert(dlda != NULL); if (dlda->dtld_finish > dld->dtld_finish) { dt_lib_depend_error(dtp, "Cyclic dependency detected: %s => %s\n", dld->dtld_library, dpld->dtld_library); return (dt_set_errno(dtp, EDT_COMPILER)); } } } return (0); } static void dt_lib_depend_free(dtrace_hdl_t *dtp) { dt_lib_depend_t *dld, *dlda; while ((dld = dt_list_next(&dtp->dt_lib_dep)) != NULL) { while ((dlda = dt_list_next(&dld->dtld_dependencies)) != NULL) { dt_list_delete(&dld->dtld_dependencies, dlda); dt_free(dtp, dlda->dtld_library); dt_free(dtp, dlda->dtld_libpath); dt_free(dtp, dlda); } while ((dlda = dt_list_next(&dld->dtld_dependents)) != NULL) { dt_list_delete(&dld->dtld_dependents, dlda); dt_free(dtp, dlda->dtld_library); dt_free(dtp, dlda->dtld_libpath); dt_free(dtp, dlda); } dt_list_delete(&dtp->dt_lib_dep, dld); dt_free(dtp, dld->dtld_library); dt_free(dtp, dld->dtld_libpath); dt_free(dtp, dld); } while ((dld = dt_list_next(&dtp->dt_lib_dep_sorted)) != NULL) { dt_list_delete(&dtp->dt_lib_dep_sorted, dld); dt_free(dtp, dld->dtld_library); dt_free(dtp, dld); } } /* * Open all the .d library files found in the specified directory and * compile each one of them. We silently ignore any missing directories and * other files found therein. We only fail (and thereby fail dt_load_libs()) if * we fail to compile a library and the error is something other than #pragma D * depends_on. Dependency errors are silently ignored to permit a library * directory to contain libraries which may not be accessible depending on our * privileges. */ static int dt_load_libs_dir(dtrace_hdl_t *dtp, const char *path) { struct dirent *dp; const char *p, *end; DIR *dirp; char fname[PATH_MAX]; FILE *fp; void *rv; dt_lib_depend_t *dld; if ((dirp = opendir(path)) == NULL) { dt_dprintf("skipping lib dir %s: %s\n", path, strerror(errno)); return (0); } /* First, parse each file for library dependencies. */ while ((dp = readdir(dirp)) != NULL) { if ((p = strrchr(dp->d_name, '.')) == NULL || strcmp(p, ".d")) continue; /* skip any filename not ending in .d */ (void) snprintf(fname, sizeof (fname), "%s/%s", path, dp->d_name); if ((fp = fopen(fname, "r")) == NULL) { dt_dprintf("skipping library %s: %s\n", fname, strerror(errno)); continue; } /* * Skip files whose name match an already processed library */ for (dld = dt_list_next(&dtp->dt_lib_dep); dld != NULL; dld = dt_list_next(dld)) { end = strrchr(dld->dtld_library, '/'); /* dt_lib_depend_add ensures this */ assert(end != NULL); if (strcmp(end + 1, dp->d_name) == 0) break; } if (dld != NULL) { dt_dprintf("skipping library %s, already processed " "library with the same name: %s", dp->d_name, dld->dtld_library); (void) fclose(fp); continue; } dtp->dt_filetag = fname; if (dt_lib_depend_add(dtp, &dtp->dt_lib_dep, fname) != 0) { (void) fclose(fp); return (-1); /* preserve dt_errno */ } rv = dt_compile(dtp, DT_CTX_DPROG, DTRACE_PROBESPEC_NAME, NULL, DTRACE_C_EMPTY | DTRACE_C_CTL, 0, NULL, fp, NULL); if (rv != NULL && dtp->dt_errno && (dtp->dt_errno != EDT_COMPILER || dtp->dt_errtag != dt_errtag(D_PRAGMA_DEPEND))) { (void) fclose(fp); return (-1); /* preserve dt_errno */ } if (dtp->dt_errno) dt_dprintf("error parsing library %s: %s\n", fname, dtrace_errmsg(dtp, dtrace_errno(dtp))); (void) fclose(fp); dtp->dt_filetag = NULL; } (void) closedir(dirp); return (0); } /* * Perform a topological sorting of all the libraries found across the entire * dt_lib_path. Once sorted, compile each one in topological order to cache its * inlines and translators, etc. We silently ignore any missing directories and * other files found therein. We only fail (and thereby fail dt_load_libs()) if * we fail to compile a library and the error is something other than #pragma D * depends_on. Dependency errors are silently ignored to permit a library * directory to contain libraries which may not be accessible depending on our * privileges. */ static int dt_load_libs_sort(dtrace_hdl_t *dtp) { dtrace_prog_t *pgp; FILE *fp; dt_lib_depend_t *dld; /* * Finish building the graph containing the library dependencies * and perform a topological sort to generate an ordered list * for compilation. */ if (dt_lib_depend_sort(dtp) == -1) goto err; for (dld = dt_list_next(&dtp->dt_lib_dep_sorted); dld != NULL; dld = dt_list_next(dld)) { if ((fp = fopen(dld->dtld_library, "r")) == NULL) { dt_dprintf("skipping library %s: %s\n", dld->dtld_library, strerror(errno)); continue; } dtp->dt_filetag = dld->dtld_library; pgp = dtrace_program_fcompile(dtp, fp, DTRACE_C_EMPTY, 0, NULL); (void) fclose(fp); dtp->dt_filetag = NULL; if (pgp == NULL && (dtp->dt_errno != EDT_COMPILER || dtp->dt_errtag != dt_errtag(D_PRAGMA_DEPEND))) goto err; if (pgp == NULL) { dt_dprintf("skipping library %s: %s\n", dld->dtld_library, dtrace_errmsg(dtp, dtrace_errno(dtp))); } else { dld->dtld_loaded = B_TRUE; dt_program_destroy(dtp, pgp); } } dt_lib_depend_free(dtp); return (0); err: dt_lib_depend_free(dtp); return (-1); /* preserve dt_errno */ } /* * Load the contents of any appropriate DTrace .d library files. These files * contain inlines and translators that will be cached by the compiler. We * defer this activity until the first compile to permit libdtrace clients to * add their own library directories and so that we can properly report errors. */ static int dt_load_libs(dtrace_hdl_t *dtp) { dt_dirpath_t *dirp; if (dtp->dt_cflags & DTRACE_C_NOLIBS) return (0); /* libraries already processed */ dtp->dt_cflags |= DTRACE_C_NOLIBS; /* * /usr/lib/dtrace is always at the head of the list. The rest of the * list is specified in the precedence order the user requested. Process * everything other than the head first. DTRACE_C_NOLIBS has already * been spcified so dt_vopen will ensure that there is always one entry * in dt_lib_path. */ for (dirp = dt_list_next(dt_list_next(&dtp->dt_lib_path)); dirp != NULL; dirp = dt_list_next(dirp)) { if (dt_load_libs_dir(dtp, dirp->dir_path) != 0) { dtp->dt_cflags &= ~DTRACE_C_NOLIBS; return (-1); /* errno is set for us */ } } /* Handle /usr/lib/dtrace */ dirp = dt_list_next(&dtp->dt_lib_path); if (dt_load_libs_dir(dtp, dirp->dir_path) != 0) { dtp->dt_cflags &= ~DTRACE_C_NOLIBS; return (-1); /* errno is set for us */ } if (dt_load_libs_sort(dtp) < 0) return (-1); /* errno is set for us */ return (0); } static void * dt_compile(dtrace_hdl_t *dtp, int context, dtrace_probespec_t pspec, void *arg, uint_t cflags, int argc, char *const argv[], FILE *fp, const char *s) { dt_node_t *dnp; dt_decl_t *ddp; dt_pcb_t pcb; void *volatile rv; int err; if ((fp == NULL && s == NULL) || (cflags & ~DTRACE_C_MASK) != 0) { (void) dt_set_errno(dtp, EINVAL); return (NULL); } if (dt_list_next(&dtp->dt_lib_path) != NULL && dt_load_libs(dtp) != 0) return (NULL); /* errno is set for us */ if (dtp->dt_globals->dh_nelems != 0) (void) dt_idhash_iter(dtp->dt_globals, dt_idreset, NULL); if (dtp->dt_tls->dh_nelems != 0) (void) dt_idhash_iter(dtp->dt_tls, dt_idreset, NULL); if (fp && (cflags & DTRACE_C_CPP) && (fp = dt_preproc(dtp, fp)) == NULL) return (NULL); /* errno is set for us */ dt_pcb_push(dtp, &pcb); pcb.pcb_fileptr = fp; pcb.pcb_string = s; pcb.pcb_strptr = s; pcb.pcb_strlen = s ? strlen(s) : 0; pcb.pcb_sargc = argc; pcb.pcb_sargv = argv; pcb.pcb_sflagv = argc ? calloc(argc, sizeof (ushort_t)) : NULL; pcb.pcb_pspec = pspec; pcb.pcb_cflags = dtp->dt_cflags | cflags; pcb.pcb_amin = dtp->dt_amin; pcb.pcb_yystate = -1; pcb.pcb_context = context; pcb.pcb_token = context; if (context != DT_CTX_DPROG) yybegin(YYS_EXPR); else if (cflags & DTRACE_C_CTL) yybegin(YYS_CONTROL); else yybegin(YYS_CLAUSE); if ((err = setjmp(yypcb->pcb_jmpbuf)) != 0) goto out; if (yypcb->pcb_sargc != 0 && yypcb->pcb_sflagv == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); yypcb->pcb_idents = dt_idhash_create("ambiguous", NULL, 0, 0); yypcb->pcb_locals = dt_idhash_create("clause local", NULL, DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); if (yypcb->pcb_idents == NULL || yypcb->pcb_locals == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); /* * Invoke the parser to evaluate the D source code. If any errors * occur during parsing, an error function will be called and we * will longjmp back to pcb_jmpbuf to abort. If parsing succeeds, * we optionally display the parse tree if debugging is enabled. */ if (yyparse() != 0 || yypcb->pcb_root == NULL) xyerror(D_EMPTY, "empty D program translation unit\n"); yybegin(YYS_DONE); if (cflags & DTRACE_C_CTL) goto out; if (context != DT_CTX_DTYPE && DT_TREEDUMP_PASS(dtp, 1)) dt_node_printr(yypcb->pcb_root, stderr, 0); if (yypcb->pcb_pragmas != NULL) (void) dt_idhash_iter(yypcb->pcb_pragmas, dt_idpragma, NULL); if (argc > 1 && !(yypcb->pcb_cflags & DTRACE_C_ARGREF) && !(yypcb->pcb_sflagv[argc - 1] & DT_IDFLG_REF)) { xyerror(D_MACRO_UNUSED, "extraneous argument '%s' ($%d is " "not referenced)\n", yypcb->pcb_sargv[argc - 1], argc - 1); } - /* - * Perform sugar transformations (for "if" / "else") and replace the - * existing clause chain with the new one. - */ + /* Perform sugar transformations. */ if (context == DT_CTX_DPROG) { dt_node_t *dnp, *next_dnp; dt_node_t *new_list = NULL; for (dnp = yypcb->pcb_root->dn_list; dnp != NULL; dnp = next_dnp) { /* remove this node from the list */ next_dnp = dnp->dn_list; dnp->dn_list = NULL; - if (dnp->dn_kind == DT_NODE_CLAUSE) + if (dnp->dn_kind == DT_NODE_CLAUSE) { dnp = dt_compile_sugar(dtp, dnp); + if (cflags & DTRACE_C_SUGAR) { + dt_node_t *p; + + dt_printd(dnp, stdout, 0); + for (p = dnp->dn_list; p != NULL; + p = p->dn_list) + dt_printd(p, stdout, 0); + } + } /* append node to the new list */ new_list = dt_node_link(new_list, dnp); } yypcb->pcb_root->dn_list = new_list; } /* * If we have successfully created a parse tree for a D program, loop * over the clauses and actions and instantiate the corresponding * libdtrace program. If we are parsing a D expression, then we * simply run the code generator and assembler on the resulting tree. */ switch (context) { case DT_CTX_DPROG: assert(yypcb->pcb_root->dn_kind == DT_NODE_PROG); if ((dnp = yypcb->pcb_root->dn_list) == NULL && !(yypcb->pcb_cflags & DTRACE_C_EMPTY)) xyerror(D_EMPTY, "empty D program translation unit\n"); if ((yypcb->pcb_prog = dt_program_create(dtp)) == NULL) longjmp(yypcb->pcb_jmpbuf, dtrace_errno(dtp)); for (; dnp != NULL; dnp = dnp->dn_list) { switch (dnp->dn_kind) { case DT_NODE_CLAUSE: if (DT_TREEDUMP_PASS(dtp, 4)) dt_printd(dnp, stderr, 0); dt_compile_clause(dtp, dnp); break; case DT_NODE_XLATOR: if (dtp->dt_xlatemode == DT_XL_DYNAMIC) dt_compile_xlator(dnp); break; case DT_NODE_PROVIDER: (void) dt_node_cook(dnp, DT_IDFLG_REF); break; } } yypcb->pcb_prog->dp_xrefs = yypcb->pcb_asxrefs; yypcb->pcb_prog->dp_xrefslen = yypcb->pcb_asxreflen; yypcb->pcb_asxrefs = NULL; yypcb->pcb_asxreflen = 0; rv = yypcb->pcb_prog; break; case DT_CTX_DEXPR: (void) dt_node_cook(yypcb->pcb_root, DT_IDFLG_REF); dt_cg(yypcb, yypcb->pcb_root); rv = dt_as(yypcb); break; case DT_CTX_DTYPE: ddp = (dt_decl_t *)yypcb->pcb_root; /* root is really a decl */ err = dt_decl_type(ddp, arg); dt_decl_free(ddp); if (err != 0) longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); rv = NULL; break; } out: if (context != DT_CTX_DTYPE && yypcb->pcb_root != NULL && DT_TREEDUMP_PASS(dtp, 3)) dt_node_printr(yypcb->pcb_root, stderr, 0); if (dtp->dt_cdefs_fd != -1 && (ftruncate64(dtp->dt_cdefs_fd, 0) == -1 || lseek64(dtp->dt_cdefs_fd, 0, SEEK_SET) == -1 || ctf_write(dtp->dt_cdefs->dm_ctfp, dtp->dt_cdefs_fd) == CTF_ERR)) dt_dprintf("failed to update CTF cache: %s\n", strerror(errno)); if (dtp->dt_ddefs_fd != -1 && (ftruncate64(dtp->dt_ddefs_fd, 0) == -1 || lseek64(dtp->dt_ddefs_fd, 0, SEEK_SET) == -1 || ctf_write(dtp->dt_ddefs->dm_ctfp, dtp->dt_ddefs_fd) == CTF_ERR)) dt_dprintf("failed to update CTF cache: %s\n", strerror(errno)); if (yypcb->pcb_fileptr && (cflags & DTRACE_C_CPP)) (void) fclose(yypcb->pcb_fileptr); /* close dt_preproc() file */ dt_pcb_pop(dtp, err); (void) dt_set_errno(dtp, err); return (err ? NULL : rv); } dtrace_prog_t * dtrace_program_strcompile(dtrace_hdl_t *dtp, const char *s, dtrace_probespec_t spec, uint_t cflags, int argc, char *const argv[]) { return (dt_compile(dtp, DT_CTX_DPROG, spec, NULL, cflags, argc, argv, NULL, s)); } dtrace_prog_t * dtrace_program_fcompile(dtrace_hdl_t *dtp, FILE *fp, uint_t cflags, int argc, char *const argv[]) { return (dt_compile(dtp, DT_CTX_DPROG, DTRACE_PROBESPEC_NAME, NULL, cflags, argc, argv, fp, NULL)); } int dtrace_type_strcompile(dtrace_hdl_t *dtp, const char *s, dtrace_typeinfo_t *dtt) { (void) dt_compile(dtp, DT_CTX_DTYPE, DTRACE_PROBESPEC_NONE, dtt, 0, 0, NULL, NULL, s); return (dtp->dt_errno ? -1 : 0); } int dtrace_type_fcompile(dtrace_hdl_t *dtp, FILE *fp, dtrace_typeinfo_t *dtt) { (void) dt_compile(dtp, DT_CTX_DTYPE, DTRACE_PROBESPEC_NONE, dtt, 0, 0, NULL, fp, NULL); return (dtp->dt_errno ? -1 : 0); } diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h index f0bc83a7fc7b..af0213695f32 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h @@ -1,618 +1,619 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2014, 2016 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #ifndef _DTRACE_H #define _DTRACE_H #include #include #include #include #include #ifndef illumos #include #endif #ifdef __cplusplus extern "C" { #endif /* * DTrace Dynamic Tracing Software: Library Interfaces * * Note: The contents of this file are private to the implementation of the * Solaris system and DTrace subsystem and are subject to change at any time * without notice. Applications and drivers using these interfaces will fail * to run on future releases. These interfaces should not be used for any * purpose except those expressly outlined in dtrace(7D) and libdtrace(3LIB). * Please refer to the "Solaris Dynamic Tracing Guide" for more information. */ #define DTRACE_VERSION 3 /* library ABI interface version */ struct ps_prochandle; struct dt_node; typedef struct dtrace_hdl dtrace_hdl_t; typedef struct dtrace_prog dtrace_prog_t; typedef struct dtrace_vector dtrace_vector_t; typedef struct dtrace_aggdata dtrace_aggdata_t; #define DTRACE_O_NODEV 0x01 /* do not open dtrace(7D) device */ #define DTRACE_O_NOSYS 0x02 /* do not load /system/object modules */ #define DTRACE_O_LP64 0x04 /* force D compiler to be LP64 */ #define DTRACE_O_ILP32 0x08 /* force D compiler to be ILP32 */ #define DTRACE_O_MASK 0x0f /* mask of valid flags to dtrace_open */ extern dtrace_hdl_t *dtrace_open(int, int, int *); extern dtrace_hdl_t *dtrace_vopen(int, int, int *, const dtrace_vector_t *, void *); extern int dtrace_go(dtrace_hdl_t *); extern int dtrace_stop(dtrace_hdl_t *); extern void dtrace_sleep(dtrace_hdl_t *); extern void dtrace_close(dtrace_hdl_t *); extern int dtrace_errno(dtrace_hdl_t *); extern const char *dtrace_errmsg(dtrace_hdl_t *, int); extern const char *dtrace_faultstr(dtrace_hdl_t *, int); extern const char *dtrace_subrstr(dtrace_hdl_t *, int); extern int dtrace_setopt(dtrace_hdl_t *, const char *, const char *); extern int dtrace_getopt(dtrace_hdl_t *, const char *, dtrace_optval_t *); extern void dtrace_update(dtrace_hdl_t *); extern int dtrace_ctlfd(dtrace_hdl_t *); /* * DTrace Program Interface * * DTrace programs can be created by compiling ASCII text files containing * D programs or by compiling in-memory C strings that specify a D program. * Once created, callers can examine the list of program statements and * enable the probes and actions described by these statements. */ typedef struct dtrace_proginfo { dtrace_attribute_t dpi_descattr; /* minimum probedesc attributes */ dtrace_attribute_t dpi_stmtattr; /* minimum statement attributes */ uint_t dpi_aggregates; /* number of aggregates specified in program */ uint_t dpi_recgens; /* number of record generating probes in prog */ uint_t dpi_matches; /* number of probes matched by program */ uint_t dpi_speculations; /* number of speculations specified in prog */ } dtrace_proginfo_t; #define DTRACE_C_DIFV 0x0001 /* DIF verbose mode: show each compiled DIFO */ #define DTRACE_C_EMPTY 0x0002 /* Permit compilation of empty D source files */ #define DTRACE_C_ZDEFS 0x0004 /* Permit probe defs that match zero probes */ #define DTRACE_C_EATTR 0x0008 /* Error if program attributes less than min */ #define DTRACE_C_CPP 0x0010 /* Preprocess input file with cpp(1) utility */ #define DTRACE_C_KNODEF 0x0020 /* Permit unresolved kernel symbols in DIFO */ #define DTRACE_C_UNODEF 0x0040 /* Permit unresolved user symbols in DIFO */ #define DTRACE_C_PSPEC 0x0080 /* Interpret ambiguous specifiers as probes */ #define DTRACE_C_ETAGS 0x0100 /* Prefix error messages with error tags */ #define DTRACE_C_ARGREF 0x0200 /* Do not require all macro args to be used */ +#define DTRACE_C_SUGAR 0x0400 /* Dump D script post-dt_sugar */ #define DTRACE_C_DEFARG 0x0800 /* Use 0/"" as value for unspecified args */ #define DTRACE_C_NOLIBS 0x1000 /* Do not process D system libraries */ #define DTRACE_C_CTL 0x2000 /* Only process control directives */ -#define DTRACE_C_MASK 0x3bff /* mask of all valid flags to dtrace_*compile */ +#define DTRACE_C_MASK 0x3fff /* mask of all valid flags to dtrace_*compile */ extern dtrace_prog_t *dtrace_program_strcompile(dtrace_hdl_t *, const char *, dtrace_probespec_t, uint_t, int, char *const []); extern dtrace_prog_t *dtrace_program_fcompile(dtrace_hdl_t *, FILE *, uint_t, int, char *const []); extern int dtrace_program_exec(dtrace_hdl_t *, dtrace_prog_t *, dtrace_proginfo_t *); extern void dtrace_program_info(dtrace_hdl_t *, dtrace_prog_t *, dtrace_proginfo_t *); #define DTRACE_D_STRIP 0x01 /* strip non-loadable sections from program */ #define DTRACE_D_PROBES 0x02 /* include provider and probe definitions */ #define DTRACE_D_MASK 0x03 /* mask of valid flags to dtrace_dof_create */ extern int dtrace_program_link(dtrace_hdl_t *, dtrace_prog_t *, uint_t, const char *, int, char *const []); extern int dtrace_program_header(dtrace_hdl_t *, FILE *, const char *); extern void *dtrace_dof_create(dtrace_hdl_t *, dtrace_prog_t *, uint_t); extern void dtrace_dof_destroy(dtrace_hdl_t *, void *); extern void *dtrace_getopt_dof(dtrace_hdl_t *); extern void *dtrace_geterr_dof(dtrace_hdl_t *); typedef struct dtrace_stmtdesc { dtrace_ecbdesc_t *dtsd_ecbdesc; /* ECB description */ dtrace_actdesc_t *dtsd_action; /* action list */ dtrace_actdesc_t *dtsd_action_last; /* last action in action list */ void *dtsd_aggdata; /* aggregation data */ void *dtsd_fmtdata; /* type-specific output data */ void *dtsd_strdata; /* type-specific string data */ void (*dtsd_callback)(void); /* callback function for EPID */ void *dtsd_data; /* callback data pointer */ dtrace_attribute_t dtsd_descattr; /* probedesc attributes */ dtrace_attribute_t dtsd_stmtattr; /* statement attributes */ } dtrace_stmtdesc_t; typedef int dtrace_stmt_f(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *, void *); extern dtrace_stmtdesc_t *dtrace_stmt_create(dtrace_hdl_t *, dtrace_ecbdesc_t *); extern dtrace_actdesc_t *dtrace_stmt_action(dtrace_hdl_t *, dtrace_stmtdesc_t *); extern int dtrace_stmt_add(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *); extern int dtrace_stmt_iter(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmt_f *, void *); extern void dtrace_stmt_destroy(dtrace_hdl_t *, dtrace_stmtdesc_t *); /* * DTrace Data Consumption Interface */ typedef enum { DTRACEFLOW_ENTRY, DTRACEFLOW_RETURN, DTRACEFLOW_NONE } dtrace_flowkind_t; #define DTRACE_CONSUME_ERROR -1 /* error while processing */ #define DTRACE_CONSUME_THIS 0 /* consume this probe/record */ #define DTRACE_CONSUME_NEXT 1 /* advance to next probe/rec */ #define DTRACE_CONSUME_ABORT 2 /* abort consumption */ typedef struct dtrace_probedata { dtrace_hdl_t *dtpda_handle; /* handle to DTrace library */ dtrace_eprobedesc_t *dtpda_edesc; /* enabled probe description */ dtrace_probedesc_t *dtpda_pdesc; /* probe description */ processorid_t dtpda_cpu; /* CPU for data */ caddr_t dtpda_data; /* pointer to raw data */ dtrace_flowkind_t dtpda_flow; /* flow kind */ const char *dtpda_prefix; /* recommended flow prefix */ int dtpda_indent; /* recommended flow indent */ } dtrace_probedata_t; typedef int dtrace_consume_probe_f(const dtrace_probedata_t *, void *); typedef int dtrace_consume_rec_f(const dtrace_probedata_t *, const dtrace_recdesc_t *, void *); extern int dtrace_consume(dtrace_hdl_t *, FILE *, dtrace_consume_probe_f *, dtrace_consume_rec_f *, void *); #define DTRACE_STATUS_NONE 0 /* no status; not yet time */ #define DTRACE_STATUS_OKAY 1 /* status okay */ #define DTRACE_STATUS_EXITED 2 /* exit() was called; tracing stopped */ #define DTRACE_STATUS_FILLED 3 /* fill buffer filled; tracing stoped */ #define DTRACE_STATUS_STOPPED 4 /* tracing already stopped */ extern int dtrace_status(dtrace_hdl_t *); /* * DTrace Formatted Output Interfaces * * To format output associated with a given dtrace_stmtdesc, the caller can * invoke one of the following functions, passing the opaque dtsd_fmtdata and a * list of record descriptions. These functions return either -1 to indicate * an error, or a positive integer indicating the number of records consumed. * For anonymous enablings, the consumer can use the dtrd_format member of * the record description to obtain a format description. The dtfd_string * member of the format description may be passed to dtrace_print{fa}_create() * to create the opaque format data. */ extern void *dtrace_printf_create(dtrace_hdl_t *, const char *); extern void *dtrace_printa_create(dtrace_hdl_t *, const char *); extern size_t dtrace_printf_format(dtrace_hdl_t *, void *, char *, size_t); extern int dtrace_fprintf(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *, size_t); extern int dtrace_fprinta(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *, size_t); extern int dtrace_system(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *, size_t); extern int dtrace_freopen(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *, size_t); /* * Type-specific output printing * * The print() action will associate a string data record that is actually the * fully-qualified type name of the data traced by the DIFEXPR action. This is * stored in the same 'format' record from the kernel, but we know by virtue of * the fact that the action is still DIFEXPR that it is actually a reference to * plain string data. */ extern int dtrace_print(dtrace_hdl_t *, FILE *, const char *, caddr_t, size_t); /* * DTrace Work Interface */ typedef enum { DTRACE_WORKSTATUS_ERROR = -1, DTRACE_WORKSTATUS_OKAY, DTRACE_WORKSTATUS_DONE } dtrace_workstatus_t; extern dtrace_workstatus_t dtrace_work(dtrace_hdl_t *, FILE *, dtrace_consume_probe_f *, dtrace_consume_rec_f *, void *); /* * DTrace Handler Interface */ #define DTRACE_HANDLE_ABORT -1 /* abort current operation */ #define DTRACE_HANDLE_OK 0 /* handled okay; continue */ typedef struct dtrace_errdata { dtrace_hdl_t *dteda_handle; /* handle to DTrace library */ dtrace_eprobedesc_t *dteda_edesc; /* enabled probe inducing err */ dtrace_probedesc_t *dteda_pdesc; /* probe inducing error */ processorid_t dteda_cpu; /* CPU of error */ int dteda_action; /* action inducing error */ int dteda_offset; /* offset in DIFO of error */ int dteda_fault; /* specific fault */ uint64_t dteda_addr; /* address of fault, if any */ const char *dteda_msg; /* preconstructed message */ } dtrace_errdata_t; typedef int dtrace_handle_err_f(const dtrace_errdata_t *, void *); extern int dtrace_handle_err(dtrace_hdl_t *, dtrace_handle_err_f *, void *); typedef enum { DTRACEDROP_PRINCIPAL, /* drop to principal buffer */ DTRACEDROP_AGGREGATION, /* drop to aggregation buffer */ DTRACEDROP_DYNAMIC, /* dynamic drop */ DTRACEDROP_DYNRINSE, /* dyn drop due to rinsing */ DTRACEDROP_DYNDIRTY, /* dyn drop due to dirty */ DTRACEDROP_SPEC, /* speculative drop */ DTRACEDROP_SPECBUSY, /* spec drop due to busy */ DTRACEDROP_SPECUNAVAIL, /* spec drop due to unavail */ DTRACEDROP_STKSTROVERFLOW, /* stack string tab overflow */ DTRACEDROP_DBLERROR /* error in ERROR probe */ } dtrace_dropkind_t; typedef struct dtrace_dropdata { dtrace_hdl_t *dtdda_handle; /* handle to DTrace library */ processorid_t dtdda_cpu; /* CPU, if any */ dtrace_dropkind_t dtdda_kind; /* kind of drop */ uint64_t dtdda_drops; /* number of drops */ uint64_t dtdda_total; /* total drops */ const char *dtdda_msg; /* preconstructed message */ } dtrace_dropdata_t; typedef int dtrace_handle_drop_f(const dtrace_dropdata_t *, void *); extern int dtrace_handle_drop(dtrace_hdl_t *, dtrace_handle_drop_f *, void *); typedef void dtrace_handle_proc_f(struct ps_prochandle *, const char *, void *); extern int dtrace_handle_proc(dtrace_hdl_t *, dtrace_handle_proc_f *, void *); #define DTRACE_BUFDATA_AGGKEY 0x0001 /* aggregation key */ #define DTRACE_BUFDATA_AGGVAL 0x0002 /* aggregation value */ #define DTRACE_BUFDATA_AGGFORMAT 0x0004 /* aggregation format data */ #define DTRACE_BUFDATA_AGGLAST 0x0008 /* last for this key/val */ typedef struct dtrace_bufdata { dtrace_hdl_t *dtbda_handle; /* handle to DTrace library */ const char *dtbda_buffered; /* buffered output */ dtrace_probedata_t *dtbda_probe; /* probe data */ const dtrace_recdesc_t *dtbda_recdesc; /* record description */ const dtrace_aggdata_t *dtbda_aggdata; /* aggregation data, if agg. */ uint32_t dtbda_flags; /* flags; see above */ } dtrace_bufdata_t; typedef int dtrace_handle_buffered_f(const dtrace_bufdata_t *, void *); extern int dtrace_handle_buffered(dtrace_hdl_t *, dtrace_handle_buffered_f *, void *); typedef struct dtrace_setoptdata { dtrace_hdl_t *dtsda_handle; /* handle to DTrace library */ const dtrace_probedata_t *dtsda_probe; /* probe data */ const char *dtsda_option; /* option that was set */ dtrace_optval_t dtsda_oldval; /* old value */ dtrace_optval_t dtsda_newval; /* new value */ } dtrace_setoptdata_t; typedef int dtrace_handle_setopt_f(const dtrace_setoptdata_t *, void *); extern int dtrace_handle_setopt(dtrace_hdl_t *, dtrace_handle_setopt_f *, void *); /* * DTrace Aggregate Interface */ #define DTRACE_A_PERCPU 0x0001 #define DTRACE_A_KEEPDELTA 0x0002 #define DTRACE_A_ANONYMOUS 0x0004 #define DTRACE_A_TOTAL 0x0008 #define DTRACE_A_MINMAXBIN 0x0010 #define DTRACE_A_HASNEGATIVES 0x0020 #define DTRACE_A_HASPOSITIVES 0x0040 #define DTRACE_AGGZOOM_MAX 0.95 /* height of max bar */ #define DTRACE_AGGWALK_ERROR -1 /* error while processing */ #define DTRACE_AGGWALK_NEXT 0 /* proceed to next element */ #define DTRACE_AGGWALK_ABORT 1 /* abort aggregation walk */ #define DTRACE_AGGWALK_CLEAR 2 /* clear this element */ #define DTRACE_AGGWALK_NORMALIZE 3 /* normalize this element */ #define DTRACE_AGGWALK_DENORMALIZE 4 /* denormalize this element */ #define DTRACE_AGGWALK_REMOVE 5 /* remove this element */ struct dtrace_aggdata { dtrace_hdl_t *dtada_handle; /* handle to DTrace library */ dtrace_aggdesc_t *dtada_desc; /* aggregation description */ dtrace_eprobedesc_t *dtada_edesc; /* enabled probe description */ dtrace_probedesc_t *dtada_pdesc; /* probe description */ caddr_t dtada_data; /* pointer to raw data */ uint64_t dtada_normal; /* the normal -- 1 for denorm */ size_t dtada_size; /* total size of the data */ caddr_t dtada_delta; /* delta data, if available */ caddr_t *dtada_percpu; /* per CPU data, if avail */ caddr_t *dtada_percpu_delta; /* per CPU delta, if avail */ int64_t dtada_total; /* per agg total, if avail */ uint16_t dtada_minbin; /* minimum bin, if avail */ uint16_t dtada_maxbin; /* maximum bin, if avail */ uint32_t dtada_flags; /* flags */ }; typedef int dtrace_aggregate_f(const dtrace_aggdata_t *, void *); typedef int dtrace_aggregate_walk_f(dtrace_hdl_t *, dtrace_aggregate_f *, void *); typedef int dtrace_aggregate_walk_joined_f(const dtrace_aggdata_t **, const int, void *); extern void dtrace_aggregate_clear(dtrace_hdl_t *); extern int dtrace_aggregate_snap(dtrace_hdl_t *); extern int dtrace_aggregate_print(dtrace_hdl_t *, FILE *, dtrace_aggregate_walk_f *); extern int dtrace_aggregate_walk(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_joined(dtrace_hdl_t *, dtrace_aggvarid_t *, int, dtrace_aggregate_walk_joined_f *, void *); extern int dtrace_aggregate_walk_sorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_keysorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_valsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_keyvarsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_valvarsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_keyrevsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_valrevsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_keyvarrevsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_valvarrevsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); #define DTRACE_AGD_PRINTED 0x1 /* aggregation printed in program */ /* * DTrace Process Control Interface * * Library clients who wish to have libdtrace create or grab processes for * monitoring of their symbol table changes may use these interfaces to * request that libdtrace obtain control of the process using libproc. */ extern struct ps_prochandle *dtrace_proc_create(dtrace_hdl_t *, const char *, char *const *, proc_child_func *, void *); extern struct ps_prochandle *dtrace_proc_grab(dtrace_hdl_t *, pid_t, int); extern void dtrace_proc_release(dtrace_hdl_t *, struct ps_prochandle *); extern void dtrace_proc_continue(dtrace_hdl_t *, struct ps_prochandle *); /* * DTrace Object, Symbol, and Type Interfaces * * Library clients can use libdtrace to perform symbol and C type information * lookups by symbol name, symbol address, or C type name, or to lookup meta- * information cached for each of the program objects in use by DTrace. The * resulting struct contain pointers to arbitrary-length strings, including * object, symbol, and type names, that are persistent until the next call to * dtrace_update(). Once dtrace_update() is called, any cached values must * be flushed and not used subsequently by the client program. */ #define DTRACE_OBJ_EXEC ((const char *)0L) /* primary executable file */ #define DTRACE_OBJ_RTLD ((const char *)1L) /* run-time link-editor */ #define DTRACE_OBJ_CDEFS ((const char *)2L) /* C include definitions */ #define DTRACE_OBJ_DDEFS ((const char *)3L) /* D program definitions */ #define DTRACE_OBJ_EVERY ((const char *)-1L) /* all known objects */ #define DTRACE_OBJ_KMODS ((const char *)-2L) /* all kernel objects */ #define DTRACE_OBJ_UMODS ((const char *)-3L) /* all user objects */ typedef struct dtrace_objinfo { const char *dto_name; /* object file scope name */ const char *dto_file; /* object file path (if any) */ int dto_id; /* object file id (if any) */ uint_t dto_flags; /* object flags (see below) */ GElf_Addr dto_text_va; /* address of text section */ GElf_Xword dto_text_size; /* size of text section */ GElf_Addr dto_data_va; /* address of data section */ GElf_Xword dto_data_size; /* size of data section */ GElf_Addr dto_bss_va; /* address of BSS */ GElf_Xword dto_bss_size; /* size of BSS */ } dtrace_objinfo_t; #define DTRACE_OBJ_F_KERNEL 0x1 /* object is a kernel module */ #define DTRACE_OBJ_F_PRIMARY 0x2 /* object is a primary module */ typedef int dtrace_obj_f(dtrace_hdl_t *, const dtrace_objinfo_t *, void *); extern int dtrace_object_iter(dtrace_hdl_t *, dtrace_obj_f *, void *); extern int dtrace_object_info(dtrace_hdl_t *, const char *, dtrace_objinfo_t *); typedef struct dtrace_syminfo { const char *dts_object; /* object name */ const char *dts_name; /* symbol name */ ulong_t dts_id; /* symbol id */ } dtrace_syminfo_t; extern int dtrace_lookup_by_name(dtrace_hdl_t *, const char *, const char *, GElf_Sym *, dtrace_syminfo_t *); extern int dtrace_lookup_by_addr(dtrace_hdl_t *, GElf_Addr addr, GElf_Sym *, dtrace_syminfo_t *); typedef struct dtrace_typeinfo { const char *dtt_object; /* object containing type */ ctf_file_t *dtt_ctfp; /* CTF container handle */ ctf_id_t dtt_type; /* CTF type identifier */ uint_t dtt_flags; /* Misc. flags */ } dtrace_typeinfo_t; #define DTT_FL_USER 0x1 /* user type */ extern int dtrace_lookup_by_type(dtrace_hdl_t *, const char *, const char *, dtrace_typeinfo_t *); extern int dtrace_symbol_type(dtrace_hdl_t *, const GElf_Sym *, const dtrace_syminfo_t *, dtrace_typeinfo_t *); extern int dtrace_type_strcompile(dtrace_hdl_t *, const char *, dtrace_typeinfo_t *); extern int dtrace_type_fcompile(dtrace_hdl_t *, FILE *, dtrace_typeinfo_t *); extern struct dt_node *dt_compile_sugar(dtrace_hdl_t *, struct dt_node *); /* * DTrace Probe Interface * * Library clients can use these functions to iterate over the set of available * probe definitions and inquire as to their attributes. The probe iteration * interfaces report probes that are declared as well as those from dtrace(7D). */ typedef struct dtrace_probeinfo { dtrace_attribute_t dtp_attr; /* name attributes */ dtrace_attribute_t dtp_arga; /* arg attributes */ const dtrace_typeinfo_t *dtp_argv; /* arg types */ int dtp_argc; /* arg count */ } dtrace_probeinfo_t; typedef int dtrace_probe_f(dtrace_hdl_t *, const dtrace_probedesc_t *, void *); extern int dtrace_probe_iter(dtrace_hdl_t *, const dtrace_probedesc_t *pdp, dtrace_probe_f *, void *); extern int dtrace_probe_info(dtrace_hdl_t *, const dtrace_probedesc_t *, dtrace_probeinfo_t *); /* * DTrace Vector Interface * * The DTrace library normally speaks directly to dtrace(7D). However, * this communication may be vectored elsewhere. Consumers who wish to * perform a vectored open must fill in the vector, and use the dtrace_vopen() * entry point to obtain a library handle. */ struct dtrace_vector { #ifdef illumos int (*dtv_ioctl)(void *, int, void *); #else int (*dtv_ioctl)(void *, u_long, void *); #endif int (*dtv_lookup_by_addr)(void *, GElf_Addr, GElf_Sym *, dtrace_syminfo_t *); int (*dtv_status)(void *, processorid_t); long (*dtv_sysconf)(void *, int); }; /* * DTrace Utility Functions * * Library clients can use these functions to convert addresses strings, to * convert between string and integer probe descriptions and the * dtrace_probedesc_t representation, and to perform similar conversions on * stability attributes. */ extern int dtrace_addr2str(dtrace_hdl_t *, uint64_t, char *, int); extern int dtrace_uaddr2str(dtrace_hdl_t *, pid_t, uint64_t, char *, int); extern int dtrace_xstr2desc(dtrace_hdl_t *, dtrace_probespec_t, const char *, int, char *const [], dtrace_probedesc_t *); extern int dtrace_str2desc(dtrace_hdl_t *, dtrace_probespec_t, const char *, dtrace_probedesc_t *); extern int dtrace_id2desc(dtrace_hdl_t *, dtrace_id_t, dtrace_probedesc_t *); #define DTRACE_DESC2STR_MAX 1024 /* min buf size for dtrace_desc2str() */ extern char *dtrace_desc2str(const dtrace_probedesc_t *, char *, size_t); #define DTRACE_ATTR2STR_MAX 64 /* min buf size for dtrace_attr2str() */ extern char *dtrace_attr2str(dtrace_attribute_t, char *, size_t); extern int dtrace_str2attr(const char *, dtrace_attribute_t *); extern const char *dtrace_stability_name(dtrace_stability_t); extern const char *dtrace_class_name(dtrace_class_t); extern int dtrace_provider_modules(dtrace_hdl_t *, const char **, int); extern const char *const _dtrace_version; extern int _dtrace_debug; #ifdef __cplusplus } #endif #ifndef illumos #define _SC_CPUID_MAX _SC_NPROCESSORS_CONF #define _SC_NPROCESSORS_MAX _SC_NPROCESSORS_CONF #endif #endif /* _DTRACE_H */