Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c (revision 268578) @@ -1,1928 +1,1934 @@ /* * 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 #if defined(sun) #include #endif #include #if defined(sun) #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"; 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; 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; #if defined(sun) 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] " "[-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 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(sun) && 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(sun) && 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); } #if defined(sun) 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 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\n", i, 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_loadsz; #if defined(sun) oprintf("dof-data-%d=0x%x", n, *p++); while (p < q) oprintf(",0x%x", *p++); oprintf(";\n"); #else /* * On FreeBSD, the DOF data is handled as a kernel environment (kenv) * string. We use two hex characters per DOF byte. */ oprintf("dof-data-%d=%02x", n, *p++); while (p < q) oprintf("%02x", *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); 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) { #if defined(sun) 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; } #if defined(sun) switch (Pstate(P)) { #else switch (proc_state(P)) { #endif case PS_UNDEAD: #if defined(sun) /* * 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 #if defined(sun) } 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; } int main(int argc, char *argv[]) { dtrace_bufdesc_t buf; struct sigaction act, oact; 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; 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': 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) #if defined(sun) g_ofile = "/kernel/drv/dtrace.conf"; #else /* * On FreeBSD, anonymous DOF data is written to * the DTrace DOF file that the boot loader will * read if booting with the DTrace option. */ g_ofile = "/boot/dtrace.dof"; #endif dof_prune(g_ofile); /* strip out any old DOF directives */ #if defined(sun) 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); #if defined(sun) 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); 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"); (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); #if !defined(sun) if (sigaction(SIGUSR1, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGUSR1, &act, NULL); #endif /* * 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); 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); } Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggencoding.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggencoding.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggencoding.d (revision 268578) @@ -0,0 +1,32 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#pragma D option quiet +#pragma D option encoding=utf8 +#pragma D option aggzoom + +tick-1ms +/i++ < 320/ +{ + @ = lquantize(i, 0, 640, 1, i); + @ = lquantize(641 - i, 0, 640, 1, i); +} + +tick-1ms +/i == 320/ +{ + printa(@); + exit(0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggencoding.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggencoding.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggencoding.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggencoding.d.out (revision 268578) @@ -0,0 +1,646 @@ + + + value ------------- Distribution ------------- count + 0 | 0 + 1 | 1 + 2 |▏ 2 + 3 |▎ 3 + 4 |▍ 4 + 5 |▌ 5 + 6 |▋ 6 + 7 |▊ 7 + 8 |▉ 8 + 9 |█ 9 + 10 |█▏ 10 + 11 |█▎ 11 + 12 |█▍ 12 + 13 |█▌ 13 + 14 |█▋ 14 + 15 |█▊ 15 + 16 |█▉ 16 + 17 |██ 17 + 18 |██▏ 18 + 19 |██▎ 19 + 20 |██▍ 20 + 21 |██▍ 21 + 22 |██▌ 22 + 23 |██▋ 23 + 24 |██▊ 24 + 25 |██▉ 25 + 26 |███ 26 + 27 |███▏ 27 + 28 |███▎ 28 + 29 |███▍ 29 + 30 |███▌ 30 + 31 |███▋ 31 + 32 |███▊ 32 + 33 |███▉ 33 + 34 |████ 34 + 35 |████▏ 35 + 36 |████▎ 36 + 37 |████▍ 37 + 38 |████▌ 38 + 39 |████▋ 39 + 40 |████▊ 40 + 41 |████▊ 41 + 42 |████▉ 42 + 43 |█████ 43 + 44 |█████▏ 44 + 45 |█████▎ 45 + 46 |█████▍ 46 + 47 |█████▌ 47 + 48 |█████▋ 48 + 49 |█████▊ 49 + 50 |█████▉ 50 + 51 |██████ 51 + 52 |██████▏ 52 + 53 |██████▎ 53 + 54 |██████▍ 54 + 55 |██████▌ 55 + 56 |██████▋ 56 + 57 |██████▊ 57 + 58 |██████▉ 58 + 59 |███████ 59 + 60 |███████▏ 60 + 61 |███████▏ 61 + 62 |███████▎ 62 + 63 |███████▍ 63 + 64 |███████▌ 64 + 65 |███████▋ 65 + 66 |███████▊ 66 + 67 |███████▉ 67 + 68 |████████ 68 + 69 |████████▏ 69 + 70 |████████▎ 70 + 71 |████████▍ 71 + 72 |████████▌ 72 + 73 |████████▋ 73 + 74 |████████▊ 74 + 75 |████████▉ 75 + 76 |█████████ 76 + 77 |█████████▏ 77 + 78 |█████████▎ 78 + 79 |█████████▍ 79 + 80 |█████████▌ 80 + 81 |█████████▌ 81 + 82 |█████████▋ 82 + 83 |█████████▊ 83 + 84 |█████████▉ 84 + 85 |██████████ 85 + 86 |██████████▏ 86 + 87 |██████████▎ 87 + 88 |██████████▍ 88 + 89 |██████████▌ 89 + 90 |██████████▋ 90 + 91 |██████████▊ 91 + 92 |██████████▉ 92 + 93 |███████████ 93 + 94 |███████████▏ 94 + 95 |███████████▎ 95 + 96 |███████████▍ 96 + 97 |███████████▌ 97 + 98 |███████████▋ 98 + 99 |███████████▊ 99 + 100 |███████████▉ 100 + 101 |███████████▉ 101 + 102 |████████████ 102 + 103 |████████████▏ 103 + 104 |████████████▎ 104 + 105 |████████████▍ 105 + 106 |████████████▌ 106 + 107 |████████████▋ 107 + 108 |████████████▊ 108 + 109 |████████████▉ 109 + 110 |█████████████ 110 + 111 |█████████████▏ 111 + 112 |█████████████▎ 112 + 113 |█████████████▍ 113 + 114 |█████████████▌ 114 + 115 |█████████████▋ 115 + 116 |█████████████▊ 116 + 117 |█████████████▉ 117 + 118 |██████████████ 118 + 119 |██████████████▏ 119 + 120 |██████████████▎ 120 + 121 |██████████████▎ 121 + 122 |██████████████▍ 122 + 123 |██████████████▌ 123 + 124 |██████████████▋ 124 + 125 |██████████████▊ 125 + 126 |██████████████▉ 126 + 127 |███████████████ 127 + 128 |███████████████▏ 128 + 129 |███████████████▎ 129 + 130 |███████████████▍ 130 + 131 |███████████████▌ 131 + 132 |███████████████▋ 132 + 133 |███████████████▊ 133 + 134 |███████████████▉ 134 + 135 |████████████████ 135 + 136 |████████████████▏ 136 + 137 |████████████████▎ 137 + 138 |████████████████▍ 138 + 139 |████████████████▌ 139 + 140 |████████████████▋ 140 + 141 |████████████████▋ 141 + 142 |████████████████▊ 142 + 143 |████████████████▉ 143 + 144 |█████████████████ 144 + 145 |█████████████████▏ 145 + 146 |█████████████████▎ 146 + 147 |█████████████████▍ 147 + 148 |█████████████████▌ 148 + 149 |█████████████████▋ 149 + 150 |█████████████████▊ 150 + 151 |█████████████████▉ 151 + 152 |██████████████████ 152 + 153 |██████████████████▏ 153 + 154 |██████████████████▎ 154 + 155 |██████████████████▍ 155 + 156 |██████████████████▌ 156 + 157 |██████████████████▋ 157 + 158 |██████████████████▊ 158 + 159 |██████████████████▉ 159 + 160 |███████████████████ 160 + 161 |███████████████████ 161 + 162 |███████████████████▏ 162 + 163 |███████████████████▎ 163 + 164 |███████████████████▍ 164 + 165 |███████████████████▌ 165 + 166 |███████████████████▋ 166 + 167 |███████████████████▊ 167 + 168 |███████████████████▉ 168 + 169 |████████████████████ 169 + 170 |████████████████████▏ 170 + 171 |████████████████████▎ 171 + 172 |████████████████████▍ 172 + 173 |████████████████████▌ 173 + 174 |████████████████████▋ 174 + 175 |████████████████████▊ 175 + 176 |████████████████████▉ 176 + 177 |█████████████████████ 177 + 178 |█████████████████████▏ 178 + 179 |█████████████████████▎ 179 + 180 |█████████████████████▍ 180 + 181 |█████████████████████▍ 181 + 182 |█████████████████████▌ 182 + 183 |█████████████████████▋ 183 + 184 |█████████████████████▊ 184 + 185 |█████████████████████▉ 185 + 186 |██████████████████████ 186 + 187 |██████████████████████▏ 187 + 188 |██████████████████████▎ 188 + 189 |██████████████████████▍ 189 + 190 |██████████████████████▌ 190 + 191 |██████████████████████▋ 191 + 192 |██████████████████████▊ 192 + 193 |██████████████████████▉ 193 + 194 |███████████████████████ 194 + 195 |███████████████████████▏ 195 + 196 |███████████████████████▎ 196 + 197 |███████████████████████▍ 197 + 198 |███████████████████████▌ 198 + 199 |███████████████████████▋ 199 + 200 |███████████████████████▊ 200 + 201 |███████████████████████▊ 201 + 202 |███████████████████████▉ 202 + 203 |████████████████████████ 203 + 204 |████████████████████████▏ 204 + 205 |████████████████████████▎ 205 + 206 |████████████████████████▍ 206 + 207 |████████████████████████▌ 207 + 208 |████████████████████████▋ 208 + 209 |████████████████████████▊ 209 + 210 |████████████████████████▉ 210 + 211 |█████████████████████████ 211 + 212 |█████████████████████████▏ 212 + 213 |█████████████████████████▎ 213 + 214 |█████████████████████████▍ 214 + 215 |█████████████████████████▌ 215 + 216 |█████████████████████████▋ 216 + 217 |█████████████████████████▊ 217 + 218 |█████████████████████████▉ 218 + 219 |██████████████████████████ 219 + 220 |██████████████████████████▏ 220 + 221 |██████████████████████████▏ 221 + 222 |██████████████████████████▎ 222 + 223 |██████████████████████████▍ 223 + 224 |██████████████████████████▌ 224 + 225 |██████████████████████████▋ 225 + 226 |██████████████████████████▊ 226 + 227 |██████████████████████████▉ 227 + 228 |███████████████████████████ 228 + 229 |███████████████████████████▏ 229 + 230 |███████████████████████████▎ 230 + 231 |███████████████████████████▍ 231 + 232 |███████████████████████████▌ 232 + 233 |███████████████████████████▋ 233 + 234 |███████████████████████████▊ 234 + 235 |███████████████████████████▉ 235 + 236 |████████████████████████████ 236 + 237 |████████████████████████████▏ 237 + 238 |████████████████████████████▎ 238 + 239 |████████████████████████████▍ 239 + 240 |████████████████████████████▌ 240 + 241 |████████████████████████████▌ 241 + 242 |████████████████████████████▋ 242 + 243 |████████████████████████████▊ 243 + 244 |████████████████████████████▉ 244 + 245 |█████████████████████████████ 245 + 246 |█████████████████████████████▏ 246 + 247 |█████████████████████████████▎ 247 + 248 |█████████████████████████████▍ 248 + 249 |█████████████████████████████▌ 249 + 250 |█████████████████████████████▋ 250 + 251 |█████████████████████████████▊ 251 + 252 |█████████████████████████████▉ 252 + 253 |██████████████████████████████ 253 + 254 |██████████████████████████████▏ 254 + 255 |██████████████████████████████▎ 255 + 256 |██████████████████████████████▍ 256 + 257 |██████████████████████████████▌ 257 + 258 |██████████████████████████████▋ 258 + 259 |██████████████████████████████▊ 259 + 260 |██████████████████████████████▉ 260 + 261 |██████████████████████████████▉ 261 + 262 |███████████████████████████████ 262 + 263 |███████████████████████████████▏ 263 + 264 |███████████████████████████████▎ 264 + 265 |███████████████████████████████▍ 265 + 266 |███████████████████████████████▌ 266 + 267 |███████████████████████████████▋ 267 + 268 |███████████████████████████████▊ 268 + 269 |███████████████████████████████▉ 269 + 270 |████████████████████████████████ 270 + 271 |████████████████████████████████▏ 271 + 272 |████████████████████████████████▎ 272 + 273 |████████████████████████████████▍ 273 + 274 |████████████████████████████████▌ 274 + 275 |████████████████████████████████▋ 275 + 276 |████████████████████████████████▊ 276 + 277 |████████████████████████████████▉ 277 + 278 |█████████████████████████████████ 278 + 279 |█████████████████████████████████▏ 279 + 280 |█████████████████████████████████▎ 280 + 281 |█████████████████████████████████▎ 281 + 282 |█████████████████████████████████▍ 282 + 283 |█████████████████████████████████▌ 283 + 284 |█████████████████████████████████▋ 284 + 285 |█████████████████████████████████▊ 285 + 286 |█████████████████████████████████▉ 286 + 287 |██████████████████████████████████ 287 + 288 |██████████████████████████████████▏ 288 + 289 |██████████████████████████████████▎ 289 + 290 |██████████████████████████████████▍ 290 + 291 |██████████████████████████████████▌ 291 + 292 |██████████████████████████████████▋ 292 + 293 |██████████████████████████████████▊ 293 + 294 |██████████████████████████████████▉ 294 + 295 |███████████████████████████████████ 295 + 296 |███████████████████████████████████▏ 296 + 297 |███████████████████████████████████▎ 297 + 298 |███████████████████████████████████▍ 298 + 299 |███████████████████████████████████▌ 299 + 300 |███████████████████████████████████▋ 300 + 301 |███████████████████████████████████▋ 301 + 302 |███████████████████████████████████▊ 302 + 303 |███████████████████████████████████▉ 303 + 304 |████████████████████████████████████ 304 + 305 |████████████████████████████████████▏ 305 + 306 |████████████████████████████████████▎ 306 + 307 |████████████████████████████████████▍ 307 + 308 |████████████████████████████████████▌ 308 + 309 |████████████████████████████████████▋ 309 + 310 |████████████████████████████████████▊ 310 + 311 |████████████████████████████████████▉ 311 + 312 |█████████████████████████████████████ 312 + 313 |█████████████████████████████████████▏ 313 + 314 |█████████████████████████████████████▎ 314 + 315 |█████████████████████████████████████▍ 315 + 316 |█████████████████████████████████████▌ 316 + 317 |█████████████████████████████████████▋ 317 + 318 |█████████████████████████████████████▊ 318 + 319 |█████████████████████████████████████▉ 319 + 320 |██████████████████████████████████████ 320 + 321 |██████████████████████████████████████ 320 + 322 |█████████████████████████████████████▉ 319 + 323 |█████████████████████████████████████▊ 318 + 324 |█████████████████████████████████████▋ 317 + 325 |█████████████████████████████████████▌ 316 + 326 |█████████████████████████████████████▍ 315 + 327 |█████████████████████████████████████▎ 314 + 328 |█████████████████████████████████████▏ 313 + 329 |█████████████████████████████████████ 312 + 330 |████████████████████████████████████▉ 311 + 331 |████████████████████████████████████▊ 310 + 332 |████████████████████████████████████▋ 309 + 333 |████████████████████████████████████▌ 308 + 334 |████████████████████████████████████▍ 307 + 335 |████████████████████████████████████▎ 306 + 336 |████████████████████████████████████▏ 305 + 337 |████████████████████████████████████ 304 + 338 |███████████████████████████████████▉ 303 + 339 |███████████████████████████████████▊ 302 + 340 |███████████████████████████████████▋ 301 + 341 |███████████████████████████████████▋ 300 + 342 |███████████████████████████████████▌ 299 + 343 |███████████████████████████████████▍ 298 + 344 |███████████████████████████████████▎ 297 + 345 |███████████████████████████████████▏ 296 + 346 |███████████████████████████████████ 295 + 347 |██████████████████████████████████▉ 294 + 348 |██████████████████████████████████▊ 293 + 349 |██████████████████████████████████▋ 292 + 350 |██████████████████████████████████▌ 291 + 351 |██████████████████████████████████▍ 290 + 352 |██████████████████████████████████▎ 289 + 353 |██████████████████████████████████▏ 288 + 354 |██████████████████████████████████ 287 + 355 |█████████████████████████████████▉ 286 + 356 |█████████████████████████████████▊ 285 + 357 |█████████████████████████████████▋ 284 + 358 |█████████████████████████████████▌ 283 + 359 |█████████████████████████████████▍ 282 + 360 |█████████████████████████████████▎ 281 + 361 |█████████████████████████████████▎ 280 + 362 |█████████████████████████████████▏ 279 + 363 |█████████████████████████████████ 278 + 364 |████████████████████████████████▉ 277 + 365 |████████████████████████████████▊ 276 + 366 |████████████████████████████████▋ 275 + 367 |████████████████████████████████▌ 274 + 368 |████████████████████████████████▍ 273 + 369 |████████████████████████████████▎ 272 + 370 |████████████████████████████████▏ 271 + 371 |████████████████████████████████ 270 + 372 |███████████████████████████████▉ 269 + 373 |███████████████████████████████▊ 268 + 374 |███████████████████████████████▋ 267 + 375 |███████████████████████████████▌ 266 + 376 |███████████████████████████████▍ 265 + 377 |███████████████████████████████▎ 264 + 378 |███████████████████████████████▏ 263 + 379 |███████████████████████████████ 262 + 380 |██████████████████████████████▉ 261 + 381 |██████████████████████████████▉ 260 + 382 |██████████████████████████████▊ 259 + 383 |██████████████████████████████▋ 258 + 384 |██████████████████████████████▌ 257 + 385 |██████████████████████████████▍ 256 + 386 |██████████████████████████████▎ 255 + 387 |██████████████████████████████▏ 254 + 388 |██████████████████████████████ 253 + 389 |█████████████████████████████▉ 252 + 390 |█████████████████████████████▊ 251 + 391 |█████████████████████████████▋ 250 + 392 |█████████████████████████████▌ 249 + 393 |█████████████████████████████▍ 248 + 394 |█████████████████████████████▎ 247 + 395 |█████████████████████████████▏ 246 + 396 |█████████████████████████████ 245 + 397 |████████████████████████████▉ 244 + 398 |████████████████████████████▊ 243 + 399 |████████████████████████████▋ 242 + 400 |████████████████████████████▌ 241 + 401 |████████████████████████████▌ 240 + 402 |████████████████████████████▍ 239 + 403 |████████████████████████████▎ 238 + 404 |████████████████████████████▏ 237 + 405 |████████████████████████████ 236 + 406 |███████████████████████████▉ 235 + 407 |███████████████████████████▊ 234 + 408 |███████████████████████████▋ 233 + 409 |███████████████████████████▌ 232 + 410 |███████████████████████████▍ 231 + 411 |███████████████████████████▎ 230 + 412 |███████████████████████████▏ 229 + 413 |███████████████████████████ 228 + 414 |██████████████████████████▉ 227 + 415 |██████████████████████████▊ 226 + 416 |██████████████████████████▋ 225 + 417 |██████████████████████████▌ 224 + 418 |██████████████████████████▍ 223 + 419 |██████████████████████████▎ 222 + 420 |██████████████████████████▏ 221 + 421 |██████████████████████████▏ 220 + 422 |██████████████████████████ 219 + 423 |█████████████████████████▉ 218 + 424 |█████████████████████████▊ 217 + 425 |█████████████████████████▋ 216 + 426 |█████████████████████████▌ 215 + 427 |█████████████████████████▍ 214 + 428 |█████████████████████████▎ 213 + 429 |█████████████████████████▏ 212 + 430 |█████████████████████████ 211 + 431 |████████████████████████▉ 210 + 432 |████████████████████████▊ 209 + 433 |████████████████████████▋ 208 + 434 |████████████████████████▌ 207 + 435 |████████████████████████▍ 206 + 436 |████████████████████████▎ 205 + 437 |████████████████████████▏ 204 + 438 |████████████████████████ 203 + 439 |███████████████████████▉ 202 + 440 |███████████████████████▊ 201 + 441 |███████████████████████▊ 200 + 442 |███████████████████████▋ 199 + 443 |███████████████████████▌ 198 + 444 |███████████████████████▍ 197 + 445 |███████████████████████▎ 196 + 446 |███████████████████████▏ 195 + 447 |███████████████████████ 194 + 448 |██████████████████████▉ 193 + 449 |██████████████████████▊ 192 + 450 |██████████████████████▋ 191 + 451 |██████████████████████▌ 190 + 452 |██████████████████████▍ 189 + 453 |██████████████████████▎ 188 + 454 |██████████████████████▏ 187 + 455 |██████████████████████ 186 + 456 |█████████████████████▉ 185 + 457 |█████████████████████▊ 184 + 458 |█████████████████████▋ 183 + 459 |█████████████████████▌ 182 + 460 |█████████████████████▍ 181 + 461 |█████████████████████▍ 180 + 462 |█████████████████████▎ 179 + 463 |█████████████████████▏ 178 + 464 |█████████████████████ 177 + 465 |████████████████████▉ 176 + 466 |████████████████████▊ 175 + 467 |████████████████████▋ 174 + 468 |████████████████████▌ 173 + 469 |████████████████████▍ 172 + 470 |████████████████████▎ 171 + 471 |████████████████████▏ 170 + 472 |████████████████████ 169 + 473 |███████████████████▉ 168 + 474 |███████████████████▊ 167 + 475 |███████████████████▋ 166 + 476 |███████████████████▌ 165 + 477 |███████████████████▍ 164 + 478 |███████████████████▎ 163 + 479 |███████████████████▏ 162 + 480 |███████████████████ 161 + 481 |███████████████████ 160 + 482 |██████████████████▉ 159 + 483 |██████████████████▊ 158 + 484 |██████████████████▋ 157 + 485 |██████████████████▌ 156 + 486 |██████████████████▍ 155 + 487 |██████████████████▎ 154 + 488 |██████████████████▏ 153 + 489 |██████████████████ 152 + 490 |█████████████████▉ 151 + 491 |█████████████████▊ 150 + 492 |█████████████████▋ 149 + 493 |█████████████████▌ 148 + 494 |█████████████████▍ 147 + 495 |█████████████████▎ 146 + 496 |█████████████████▏ 145 + 497 |█████████████████ 144 + 498 |████████████████▉ 143 + 499 |████████████████▊ 142 + 500 |████████████████▋ 141 + 501 |████████████████▋ 140 + 502 |████████████████▌ 139 + 503 |████████████████▍ 138 + 504 |████████████████▎ 137 + 505 |████████████████▏ 136 + 506 |████████████████ 135 + 507 |███████████████▉ 134 + 508 |███████████████▊ 133 + 509 |███████████████▋ 132 + 510 |███████████████▌ 131 + 511 |███████████████▍ 130 + 512 |███████████████▎ 129 + 513 |███████████████▏ 128 + 514 |███████████████ 127 + 515 |██████████████▉ 126 + 516 |██████████████▊ 125 + 517 |██████████████▋ 124 + 518 |██████████████▌ 123 + 519 |██████████████▍ 122 + 520 |██████████████▎ 121 + 521 |██████████████▎ 120 + 522 |██████████████▏ 119 + 523 |██████████████ 118 + 524 |█████████████▉ 117 + 525 |█████████████▊ 116 + 526 |█████████████▋ 115 + 527 |█████████████▌ 114 + 528 |█████████████▍ 113 + 529 |█████████████▎ 112 + 530 |█████████████▏ 111 + 531 |█████████████ 110 + 532 |████████████▉ 109 + 533 |████████████▊ 108 + 534 |████████████▋ 107 + 535 |████████████▌ 106 + 536 |████████████▍ 105 + 537 |████████████▎ 104 + 538 |████████████▏ 103 + 539 |████████████ 102 + 540 |███████████▉ 101 + 541 |███████████▉ 100 + 542 |███████████▊ 99 + 543 |███████████▋ 98 + 544 |███████████▌ 97 + 545 |███████████▍ 96 + 546 |███████████▎ 95 + 547 |███████████▏ 94 + 548 |███████████ 93 + 549 |██████████▉ 92 + 550 |██████████▊ 91 + 551 |██████████▋ 90 + 552 |██████████▌ 89 + 553 |██████████▍ 88 + 554 |██████████▎ 87 + 555 |██████████▏ 86 + 556 |██████████ 85 + 557 |█████████▉ 84 + 558 |█████████▊ 83 + 559 |█████████▋ 82 + 560 |█████████▌ 81 + 561 |█████████▌ 80 + 562 |█████████▍ 79 + 563 |█████████▎ 78 + 564 |█████████▏ 77 + 565 |█████████ 76 + 566 |████████▉ 75 + 567 |████████▊ 74 + 568 |████████▋ 73 + 569 |████████▌ 72 + 570 |████████▍ 71 + 571 |████████▎ 70 + 572 |████████▏ 69 + 573 |████████ 68 + 574 |███████▉ 67 + 575 |███████▊ 66 + 576 |███████▋ 65 + 577 |███████▌ 64 + 578 |███████▍ 63 + 579 |███████▎ 62 + 580 |███████▏ 61 + 581 |███████▏ 60 + 582 |███████ 59 + 583 |██████▉ 58 + 584 |██████▊ 57 + 585 |██████▋ 56 + 586 |██████▌ 55 + 587 |██████▍ 54 + 588 |██████▎ 53 + 589 |██████▏ 52 + 590 |██████ 51 + 591 |█████▉ 50 + 592 |█████▊ 49 + 593 |█████▋ 48 + 594 |█████▌ 47 + 595 |█████▍ 46 + 596 |█████▎ 45 + 597 |█████▏ 44 + 598 |█████ 43 + 599 |████▉ 42 + 600 |████▊ 41 + 601 |████▊ 40 + 602 |████▋ 39 + 603 |████▌ 38 + 604 |████▍ 37 + 605 |████▎ 36 + 606 |████▏ 35 + 607 |████ 34 + 608 |███▉ 33 + 609 |███▊ 32 + 610 |███▋ 31 + 611 |███▌ 30 + 612 |███▍ 29 + 613 |███▎ 28 + 614 |███▏ 27 + 615 |███ 26 + 616 |██▉ 25 + 617 |██▊ 24 + 618 |██▋ 23 + 619 |██▌ 22 + 620 |██▍ 21 + 621 |██▍ 20 + 622 |██▎ 19 + 623 |██▏ 18 + 624 |██ 17 + 625 |█▉ 16 + 626 |█▊ 15 + 627 |█▋ 14 + 628 |█▌ 13 + 629 |█▍ 12 + 630 |█▎ 11 + 631 |█▏ 10 + 632 |█ 9 + 633 |▉ 8 + 634 |▊ 7 + 635 |▋ 6 + 636 |▌ 5 + 637 |▍ 4 + 638 |▎ 3 + 639 |▏ 2 + >= 640 | 1 + + Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggencoding.d.out ___________________________________________________________________ Added: fbsd:notbinary ## -0,0 +1 ## +yes \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.agghist.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.agghist.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.agghist.d (revision 268578) @@ -0,0 +1,46 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#pragma D option agghist +#pragma D option quiet + +BEGIN +{ + @["demerit"] = sum(-10); + @["wtf"] = sum(10); + @["bot"] = sum(20); + + @bagnoogle["SOAP/XML"] = sum(1); + @bagnoogle["XACML store"] = sum(5); + @bagnoogle["SAML token"] = sum(6); + + @stalloogle["breakfast"] = sum(-5); + @stalloogle["non-diet pepsi"] = sum(-20); + @stalloogle["parrot"] = sum(-100); + + printa(@); + printa(@bagnoogle); + printa(@stalloogle); + + printf("\nzoomed:"); + + setopt("aggzoom"); + printa(@); + printa(@bagnoogle); + printa(@stalloogle); + + exit(0); +} + Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.agghist.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.agghist.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.agghist.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.agghist.d.out (revision 268578) @@ -0,0 +1,38 @@ + + + key ------------- Distribution ------------- count + demerit @@@@@| -10 + wtf |@@@@@ 10 + bot |@@@@@@@@@@ 20 + + + key ------------- Distribution ------------- count + SOAP/XML |@@@ 1 + XACML store |@@@@@@@@@@@@@@@@@ 5 + SAML token |@@@@@@@@@@@@@@@@@@@@ 6 + + + key ------------- Distribution ------------- count + parrot @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| -100 + non-diet pepsi @@@@@@| -20 + breakfast @@| -5 + +zoomed: + + key ------------- Distribution ------------- count + demerit @@@@@@@@@@| -10 + wtf |@@@@@@@@@@ 10 + bot |@@@@@@@@@@@@@@@@@@@ 20 + + + key ------------- Distribution ------------- count + SOAP/XML |@@@@@@@ 1 + XACML store |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 5 + SAML token |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 6 + + + key ------------- Distribution ------------- count + parrot @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| -100 + non-diet pepsi @@@@@@@@| -20 + breakfast @@| -5 + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpack.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpack.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpack.d (revision 268578) @@ -0,0 +1,53 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#pragma D option aggpack +#pragma D option encoding=ascii +#pragma D option quiet + +BEGIN +{ + @x = quantize(1 << 32); + @y[1] = quantize(1); + @z["mumble"] = quantize(1); + @xx["foo", (char)1, (short)2, (long)3] = quantize(1); + + @neg = lquantize(-10, -10, 20, 1, -1); + @neg = lquantize(-5, -10, 20, 1, 1); + @neg = lquantize(0, -10, 20, 1, 1); + + i = 0; +} + +tick-1ms +{ + @a[i] = quantize(0, i); + @a[i] = quantize(1, 100 - i); + i++; +} + +tick-1ms +/i > 100/ +{ + exit(0); +} + +END +{ + setopt("aggzoom", "true"); + printa(@neg); + setopt("aggzoom", "false"); + printa(@neg); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpack.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpack.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpack.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpack.d.out (revision 268578) @@ -0,0 +1,124 @@ + + + min .--------------------------------. max | count + < -10 : V X X : >= 20 | 1 + + + min .--------------------------------. max | count + < -10 : v x x : >= 20 | 1 + + + min .---. max | count + 2147483648 : X : 8589934592 | 1 + + key min .---. max | count + 1 0 : X : 2 | 1 + + key min .---. max | count + mumble 0 : X : 2 | 1 + + key min .---. max | count + foo 1 2 3 0 : X : 2 | 1 + + key min .---. max | count + 100 0 :X : 2 | 100 + 99 0 :X_ : 2 | 100 + 98 0 :X_ : 2 | 100 + 97 0 :X_ : 2 | 100 + 96 0 :X_ : 2 | 100 + 95 0 :X_ : 2 | 100 + 94 0 :X_ : 2 | 100 + 93 0 :X_ : 2 | 100 + 92 0 :X_ : 2 | 100 + 91 0 :X_ : 2 | 100 + 90 0 :X_ : 2 | 100 + 89 0 :X_ : 2 | 100 + 88 0 :X_ : 2 | 100 + 87 0 :X_ : 2 | 100 + 86 0 :X_ : 2 | 100 + 85 0 :X_ : 2 | 100 + 84 0 :X_ : 2 | 100 + 83 0 :X_ : 2 | 100 + 82 0 :X_ : 2 | 100 + 81 0 :X_ : 2 | 100 + 80 0 :X_ : 2 | 100 + 79 0 :X_ : 2 | 100 + 78 0 :xx : 2 | 100 + 77 0 :xx : 2 | 100 + 76 0 :xx : 2 | 100 + 75 0 :xx : 2 | 100 + 74 0 :xx : 2 | 100 + 73 0 :xx : 2 | 100 + 72 0 :xx : 2 | 100 + 71 0 :xx : 2 | 100 + 70 0 :xx : 2 | 100 + 69 0 :xx : 2 | 100 + 68 0 :xx : 2 | 100 + 67 0 :xx : 2 | 100 + 66 0 :xx : 2 | 100 + 65 0 :xx : 2 | 100 + 64 0 :xx : 2 | 100 + 63 0 :xx : 2 | 100 + 62 0 :xx : 2 | 100 + 61 0 :xx : 2 | 100 + 60 0 :xx : 2 | 100 + 59 0 :xx : 2 | 100 + 58 0 :xx : 2 | 100 + 57 0 :xx : 2 | 100 + 56 0 :xx : 2 | 100 + 55 0 :xx : 2 | 100 + 54 0 :xx : 2 | 100 + 53 0 :xx : 2 | 100 + 52 0 :xx : 2 | 100 + 51 0 :xx : 2 | 100 + 50 0 :xx : 2 | 100 + 49 0 :xx : 2 | 100 + 48 0 :xx : 2 | 100 + 47 0 :xx : 2 | 100 + 46 0 :xx : 2 | 100 + 45 0 :xx : 2 | 100 + 44 0 :xx : 2 | 100 + 43 0 :xx : 2 | 100 + 42 0 :xx : 2 | 100 + 41 0 :xx : 2 | 100 + 40 0 :xx : 2 | 100 + 39 0 :xx : 2 | 100 + 38 0 :xx : 2 | 100 + 37 0 :xx : 2 | 100 + 36 0 :xx : 2 | 100 + 35 0 :xx : 2 | 100 + 34 0 :xx : 2 | 100 + 33 0 :xx : 2 | 100 + 32 0 :xx : 2 | 100 + 31 0 :xx : 2 | 100 + 30 0 :xx : 2 | 100 + 29 0 :xx : 2 | 100 + 28 0 :xx : 2 | 100 + 27 0 :xx : 2 | 100 + 26 0 :xx : 2 | 100 + 25 0 :xx : 2 | 100 + 24 0 :xx : 2 | 100 + 23 0 :xx : 2 | 100 + 22 0 :xx : 2 | 100 + 21 0 :_X : 2 | 100 + 20 0 :_X : 2 | 100 + 19 0 :_X : 2 | 100 + 18 0 :_X : 2 | 100 + 17 0 :_X : 2 | 100 + 16 0 :_X : 2 | 100 + 15 0 :_X : 2 | 100 + 14 0 :_X : 2 | 100 + 13 0 :_X : 2 | 100 + 12 0 :_X : 2 | 100 + 11 0 :_X : 2 | 100 + 10 0 :_X : 2 | 100 + 9 0 :_X : 2 | 100 + 8 0 :_X : 2 | 100 + 7 0 :_X : 2 | 100 + 6 0 :_X : 2 | 100 + 5 0 :_X : 2 | 100 + 4 0 :_X : 2 | 100 + 3 0 :_X : 2 | 100 + 2 0 :_X : 2 | 100 + 1 0 :_X : 2 | 100 + 0 0 : X : 2 | 100 Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackbanner.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackbanner.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackbanner.ksh (revision 268578) @@ -0,0 +1,75 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +let width=8 + +function outputchar +{ + banner $3 | /bin/nawk -v line=$1 -v pos=$2 -v width=$width '{ \ + for (i = 1; i <= length($0); i++) { \ + if (substr($0, i, 1) == " ") \ + continue; \ + printf("\t@letter%d[%d] = lquantize(%d, 0, 40, 1);\n", \ + line, NR, i + (pos * width)); + } \ + }' +} + +function outputstr +{ + let pos=0; + let line=0 + + printf "#pragma D option aggpack\n#pragma D option aggsortkey\n" + + printf "BEGIN\n{\n" + for c in `echo "$1" | /bin/nawk '{ \ + for (i = 1; i <= length($0); i++) { \ + c = substr($0, i, 1); \ + printf("%s\n", c == " " ? "space" : \ + c == "\n" ? "newline" : c); \ + } \ + }'`; do + if [[ "$c" == "space" ]]; then + let line=line+1 + let pos=0 + continue + fi + + outputchar $line $pos $c + let pos=pos+1 + done + + let i=0 + + while [[ $i -le $line ]]; do + printf "\tprinta(@letter%d);\n" $i + let i=i+1 + done + printf "\texit(0);\n}\n" +} + +dtrace -qs /dev/stdin -x encoding=utf8 <= 40 | 6 + 3 < 0 : ▂ ▂ ▂ ▂ ▂ ▂ : >= 40 | 6 + 4 < 0 : ▂ ▂ ▂▂▂▂▂▂ ▂ : >= 40 | 9 + 5 < 0 : ▂ ▂▂ ▂ ▂ ▂ ▂ : >= 40 | 7 + 6 < 0 : ▂▂ ▂▂ ▂ ▂ ▂ : >= 40 | 7 + 7 < 0 : ▂ ▂ ▂ ▂ ▂ : >= 40 | 5 + + + key min .------------------------------------------. max | count + 2 < 0 : ▂ ▂ ▂ ▂ ▂▂▂▂ ▂▂▂▂▂ : >= 40 | 13 + 3 < 0 : ▂▂ ▂▂ ▂ ▂ ▂ ▂ : >= 40 | 8 + 4 < 0 : ▂ ▂▂ ▂ ▂ ▂ ▂▂▂▂ ▂ : >= 40 | 11 + 5 < 0 : ▂ ▂ ▂ ▂ ▂ ▂ : >= 40 | 6 + 6 < 0 : ▂ ▂ ▂ ▂ ▂ ▂ ▂ : >= 40 | 7 + 7 < 0 : ▂ ▂ ▂▂▂▂ ▂▂▂▂ ▂ : >= 40 | 11 + + + key min .------------------------------------------. max | count + 2 < 0 : █ : >= 40 | 1 + 3 < 0 : █ : >= 40 | 1 + 4 < 0 : █ : >= 40 | 1 + 5 < 0 : █ : >= 40 | 1 + 6 < 0 : █ : >= 40 | 1 + 7 < 0 : █ : >= 40 | 1 + + + key min .------------------------------------------. max | count + 2 < 0 : ▂▂▂▂▂ ▂▂▂▂ : >= 40 | 9 + 3 < 0 : ▃ ▃ ▃ ▃ : >= 40 | 4 + 4 < 0 : ▃ ▃ ▃ ▃ : >= 40 | 4 + 5 < 0 : ▃ ▃ ▃ ▃ : >= 40 | 4 + 6 < 0 : ▃ ▃ ▃ ▃ : >= 40 | 4 + 7 < 0 : ▂▂▂▂▂ ▂▂▂▂ : >= 40 | 9 + + + key min .------------------------------------------. max | count + 2 < 0 : ▂▂▂▂▂ ▂ ▂ ▂ ▂▂▂▂ : >= 40 | 12 + 3 < 0 : ▂ ▂ ▂ ▂ ▂ : >= 40 | 5 + 4 < 0 : ▂ ▂▂▂▂▂▂ ▂ ▂▂▂▂ : >= 40 | 12 + 5 < 0 : ▂ ▂ ▂ ▂ ▂ : >= 40 | 5 + 6 < 0 : ▂ ▂ ▂ ▂ ▂ ▂ : >= 40 | 6 + 7 < 0 : ▂ ▂ ▂ ▂ ▂▂▂▂ : >= 40 | 8 + + + + key min .------------------------------------------. max | count + 2 < 0 : X : >= 40 | 1 + 3 < 0 : X : >= 40 | 1 + 4 < 0 : X : >= 40 | 1 + 5 < 0 : X : >= 40 | 1 + 6 < 0 : X : >= 40 | 1 + 7 < 0 : X : >= 40 | 1 + + + key min .------------------------------------------. max | count + 2 < 0 : XX X X : >= 40 | 4 + 3 < 0 : X X XX XX : >= 40 | 6 + 4 < 0 : X X X XX X : >= 40 | 6 + 5 < 0 : XXXXXX X X : >= 40 | 8 + 6 < 0 : X X X X : >= 40 | 4 + 7 < 0 : X X X X : >= 40 | 4 + + + key min .------------------------------------------. max | count + 2 < 0 : X X XXXX XXXXX : >= 40 | 11 + 3 < 0 : XX X X X X : >= 40 | 6 + 4 < 0 : X X X X X X : >= 40 | 6 + 5 < 0 : X X X X X X : >= 40 | 6 + 6 < 0 : X XX X X X : >= 40 | 6 + 7 < 0 : X X XXXX X : >= 40 | 7 + + + key min .------------------------------------------. max | count + 2 < 0 : X X XXXXXX X X : >= 40 | 10 + 3 < 0 : X X X X X : >= 40 | 5 + 4 < 0 : X X XXXXX X X : >= 40 | 9 + 5 < 0 : X XX X X X X : >= 40 | 7 + 6 < 0 : XX XX X X X : >= 40 | 7 + 7 < 0 : X X XXXXXX XXXXXX XXXXXX : >= 40 | 20 + + + + key min .------------------------------------------. max | count + 2 < 0 : ████ ██████ █ █ █████ : >= 40 | 17 + 3 < 0 : █ █ ██ █ █ █ : >= 40 | 7 + 4 < 0 : ████ █████ █ █ █ █ █ : >= 40 | 14 + 5 < 0 : █ █ █ █ █ █ █ : >= 40 | 7 + 6 < 0 : █ █ █ █ ██ █ █ : >= 40 | 8 + 7 < 0 : ████ ██████ █ █ █████ : >= 40 | 17 + + + key min .------------------------------------------. max | count + 2 < 0 : █ █ ██████ █ █████ : >= 40 | 14 + 3 < 0 : █ █ █ █ █ █ : >= 40 | 6 + 4 < 0 : ██████ █████ █ █ █ : >= 40 | 14 + 5 < 0 : █ █ █ █ █████ : >= 40 | 9 + 6 < 0 : █ █ █ █ █ : >= 40 | 5 + 7 < 0 : █ █ ██████ ██████ █ : >= 40 | 15 + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackzoom.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackzoom.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackzoom.d (revision 268578) @@ -0,0 +1,43 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#pragma D option encoding=ascii +#pragma D option quiet + +tick-1ms +/i++ < 30/ +{ + @[1] = lquantize(i, 0, 40, 1, 1000); + @[2] = lquantize(i, 0, 40, 1, 1000); + @[3] = lquantize(i, 0, 40, 1, 1000); +} + +tick-1ms +/i == 40/ +{ + @[1] = lquantize(0, 0, 40, 1, 1); + @[1] = lquantize(i, 0, 40, 1, 2000); + @[2] = lquantize(0, 0, 40, 1, 1); + @[2] = lquantize(i, 0, 40, 1, 2000); + @[3] = lquantize(0, 0, 40, 1, 1); + @[3] = lquantize(i, 0, 40, 1, 2000); + + printa(@); + setopt("aggpack"); + printa(@); + setopt("aggzoom"); + printa(@); + exit(0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackzoom.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackzoom.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackzoom.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggpackzoom.d.out (revision 268578) @@ -0,0 +1,149 @@ + + 1 + value ------------- Distribution ------------- count + < 0 | 0 + 0 | 1 + 1 |@ 1000 + 2 |@ 1000 + 3 |@ 1000 + 4 |@ 1000 + 5 |@ 1000 + 6 |@ 1000 + 7 |@ 1000 + 8 |@ 1000 + 9 |@ 1000 + 10 |@ 1000 + 11 |@ 1000 + 12 |@ 1000 + 13 |@ 1000 + 14 |@ 1000 + 15 |@ 1000 + 16 |@ 1000 + 17 |@ 1000 + 18 |@ 1000 + 19 |@ 1000 + 20 |@ 1000 + 21 |@ 1000 + 22 |@ 1000 + 23 |@ 1000 + 24 |@ 1000 + 25 |@ 1000 + 26 |@ 1000 + 27 |@ 1000 + 28 |@ 1000 + 29 |@ 1000 + 30 |@ 1000 + 31 | 0 + 32 | 0 + 33 | 0 + 34 | 0 + 35 | 0 + 36 | 0 + 37 | 0 + 38 | 0 + 39 | 0 + >= 40 |@@ 2000 + + 2 + value ------------- Distribution ------------- count + < 0 | 0 + 0 | 1 + 1 |@ 1000 + 2 |@ 1000 + 3 |@ 1000 + 4 |@ 1000 + 5 |@ 1000 + 6 |@ 1000 + 7 |@ 1000 + 8 |@ 1000 + 9 |@ 1000 + 10 |@ 1000 + 11 |@ 1000 + 12 |@ 1000 + 13 |@ 1000 + 14 |@ 1000 + 15 |@ 1000 + 16 |@ 1000 + 17 |@ 1000 + 18 |@ 1000 + 19 |@ 1000 + 20 |@ 1000 + 21 |@ 1000 + 22 |@ 1000 + 23 |@ 1000 + 24 |@ 1000 + 25 |@ 1000 + 26 |@ 1000 + 27 |@ 1000 + 28 |@ 1000 + 29 |@ 1000 + 30 |@ 1000 + 31 | 0 + 32 | 0 + 33 | 0 + 34 | 0 + 35 | 0 + 36 | 0 + 37 | 0 + 38 | 0 + 39 | 0 + >= 40 |@@ 2000 + + 3 + value ------------- Distribution ------------- count + < 0 | 0 + 0 | 1 + 1 |@ 1000 + 2 |@ 1000 + 3 |@ 1000 + 4 |@ 1000 + 5 |@ 1000 + 6 |@ 1000 + 7 |@ 1000 + 8 |@ 1000 + 9 |@ 1000 + 10 |@ 1000 + 11 |@ 1000 + 12 |@ 1000 + 13 |@ 1000 + 14 |@ 1000 + 15 |@ 1000 + 16 |@ 1000 + 17 |@ 1000 + 18 |@ 1000 + 19 |@ 1000 + 20 |@ 1000 + 21 |@ 1000 + 22 |@ 1000 + 23 |@ 1000 + 24 |@ 1000 + 25 |@ 1000 + 26 |@ 1000 + 27 |@ 1000 + 28 |@ 1000 + 29 |@ 1000 + 30 |@ 1000 + 31 | 0 + 32 | 0 + 33 | 0 + 34 | 0 + 35 | 0 + 36 | 0 + 37 | 0 + 38 | 0 + 39 | 0 + >= 40 |@@ 2000 + + + + key min .------------------------------------------. max | count + 1 < 0 : _______________________________ _: >= 40 | 32001 + 2 < 0 : _______________________________ _: >= 40 | 32001 + 3 < 0 : _______________________________ _: >= 40 | 32001 + + + key min .------------------------------------------. max | count + 1 < 0 : _xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx X: >= 40 | 32001 + 2 < 0 : _xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx X: >= 40 | 32001 + 3 < 0 : _xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx X: >= 40 | 32001 + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggzoom.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggzoom.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggzoom.d (revision 268578) @@ -0,0 +1,35 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#pragma D option encoding=ascii +#pragma D option quiet + +tick-1ms +/i++ < 90/ +{ + @ = lquantize(i, 0, 100, 1, 1000); +} + +tick-1ms +/i == 100/ +{ + @ = lquantize(i++, 0, 100, 1, 2000); + @ = lquantize(i++, 0, 100, 1, 3000); + + printa(@); + setopt("aggzoom"); + printa(@); + exit(0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggzoom.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggzoom.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggzoom.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.aggzoom.d.out (revision 268578) @@ -0,0 +1,211 @@ + + + value ------------- Distribution ------------- count + 0 | 0 + 1 | 1000 + 2 | 1000 + 3 | 1000 + 4 | 1000 + 5 | 1000 + 6 | 1000 + 7 | 1000 + 8 | 1000 + 9 | 1000 + 10 | 1000 + 11 | 1000 + 12 | 1000 + 13 | 1000 + 14 | 1000 + 15 | 1000 + 16 | 1000 + 17 | 1000 + 18 | 1000 + 19 | 1000 + 20 | 1000 + 21 | 1000 + 22 | 1000 + 23 | 1000 + 24 | 1000 + 25 | 1000 + 26 | 1000 + 27 | 1000 + 28 | 1000 + 29 | 1000 + 30 | 1000 + 31 | 1000 + 32 | 1000 + 33 | 1000 + 34 | 1000 + 35 | 1000 + 36 | 1000 + 37 | 1000 + 38 | 1000 + 39 | 1000 + 40 | 1000 + 41 | 1000 + 42 | 1000 + 43 | 1000 + 44 | 1000 + 45 | 1000 + 46 | 1000 + 47 | 1000 + 48 | 1000 + 49 | 1000 + 50 | 1000 + 51 | 1000 + 52 | 1000 + 53 | 1000 + 54 | 1000 + 55 | 1000 + 56 | 1000 + 57 | 1000 + 58 | 1000 + 59 | 1000 + 60 | 1000 + 61 | 1000 + 62 | 1000 + 63 | 1000 + 64 | 1000 + 65 | 1000 + 66 | 1000 + 67 | 1000 + 68 | 1000 + 69 | 1000 + 70 | 1000 + 71 | 1000 + 72 | 1000 + 73 | 1000 + 74 | 1000 + 75 | 1000 + 76 | 1000 + 77 | 1000 + 78 | 1000 + 79 | 1000 + 80 | 1000 + 81 | 1000 + 82 | 1000 + 83 | 1000 + 84 | 1000 + 85 | 1000 + 86 | 1000 + 87 | 1000 + 88 | 1000 + 89 | 1000 + 90 | 1000 + 91 | 0 + 92 | 0 + 93 | 0 + 94 | 0 + 95 | 0 + 96 | 0 + 97 | 0 + 98 | 0 + 99 | 0 + >= 100 |@@ 5000 + + + + value ------------- Distribution ------------- count + 0 | 0 + 1 |@@@@@@@@ 1000 + 2 |@@@@@@@@ 1000 + 3 |@@@@@@@@ 1000 + 4 |@@@@@@@@ 1000 + 5 |@@@@@@@@ 1000 + 6 |@@@@@@@@ 1000 + 7 |@@@@@@@@ 1000 + 8 |@@@@@@@@ 1000 + 9 |@@@@@@@@ 1000 + 10 |@@@@@@@@ 1000 + 11 |@@@@@@@@ 1000 + 12 |@@@@@@@@ 1000 + 13 |@@@@@@@@ 1000 + 14 |@@@@@@@@ 1000 + 15 |@@@@@@@@ 1000 + 16 |@@@@@@@@ 1000 + 17 |@@@@@@@@ 1000 + 18 |@@@@@@@@ 1000 + 19 |@@@@@@@@ 1000 + 20 |@@@@@@@@ 1000 + 21 |@@@@@@@@ 1000 + 22 |@@@@@@@@ 1000 + 23 |@@@@@@@@ 1000 + 24 |@@@@@@@@ 1000 + 25 |@@@@@@@@ 1000 + 26 |@@@@@@@@ 1000 + 27 |@@@@@@@@ 1000 + 28 |@@@@@@@@ 1000 + 29 |@@@@@@@@ 1000 + 30 |@@@@@@@@ 1000 + 31 |@@@@@@@@ 1000 + 32 |@@@@@@@@ 1000 + 33 |@@@@@@@@ 1000 + 34 |@@@@@@@@ 1000 + 35 |@@@@@@@@ 1000 + 36 |@@@@@@@@ 1000 + 37 |@@@@@@@@ 1000 + 38 |@@@@@@@@ 1000 + 39 |@@@@@@@@ 1000 + 40 |@@@@@@@@ 1000 + 41 |@@@@@@@@ 1000 + 42 |@@@@@@@@ 1000 + 43 |@@@@@@@@ 1000 + 44 |@@@@@@@@ 1000 + 45 |@@@@@@@@ 1000 + 46 |@@@@@@@@ 1000 + 47 |@@@@@@@@ 1000 + 48 |@@@@@@@@ 1000 + 49 |@@@@@@@@ 1000 + 50 |@@@@@@@@ 1000 + 51 |@@@@@@@@ 1000 + 52 |@@@@@@@@ 1000 + 53 |@@@@@@@@ 1000 + 54 |@@@@@@@@ 1000 + 55 |@@@@@@@@ 1000 + 56 |@@@@@@@@ 1000 + 57 |@@@@@@@@ 1000 + 58 |@@@@@@@@ 1000 + 59 |@@@@@@@@ 1000 + 60 |@@@@@@@@ 1000 + 61 |@@@@@@@@ 1000 + 62 |@@@@@@@@ 1000 + 63 |@@@@@@@@ 1000 + 64 |@@@@@@@@ 1000 + 65 |@@@@@@@@ 1000 + 66 |@@@@@@@@ 1000 + 67 |@@@@@@@@ 1000 + 68 |@@@@@@@@ 1000 + 69 |@@@@@@@@ 1000 + 70 |@@@@@@@@ 1000 + 71 |@@@@@@@@ 1000 + 72 |@@@@@@@@ 1000 + 73 |@@@@@@@@ 1000 + 74 |@@@@@@@@ 1000 + 75 |@@@@@@@@ 1000 + 76 |@@@@@@@@ 1000 + 77 |@@@@@@@@ 1000 + 78 |@@@@@@@@ 1000 + 79 |@@@@@@@@ 1000 + 80 |@@@@@@@@ 1000 + 81 |@@@@@@@@ 1000 + 82 |@@@@@@@@ 1000 + 83 |@@@@@@@@ 1000 + 84 |@@@@@@@@ 1000 + 85 |@@@@@@@@ 1000 + 86 |@@@@@@@@ 1000 + 87 |@@@@@@@@ 1000 + 88 |@@@@@@@@ 1000 + 89 |@@@@@@@@ 1000 + 90 |@@@@@@@@ 1000 + 91 | 0 + 92 | 0 + 93 | 0 + 94 | 0 + 95 | 0 + 96 | 0 + 97 | 0 + 98 | 0 + 99 | 0 + >= 100 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 5000 + + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.subr.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.subr.d (revision 268577) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/aggs/tst.subr.d (revision 268578) @@ -1,111 +1,115 @@ /* * 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) 2012, Joyent, Inc. All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" #define INTFUNC(x) \ BEGIN \ /*DSTYLED*/ \ { \ subr++; \ @[(long)x] = sum(1); \ /*DSTYLED*/ \ } #define STRFUNC(x) \ BEGIN \ /*DSTYLED*/ \ { \ subr++; \ @str[x] = sum(1); \ /*DSTYLED*/ \ } #define VOIDFUNC(x) \ BEGIN \ /*DSTYLED*/ \ { \ subr++; \ /*DSTYLED*/ \ } INTFUNC(rand()) INTFUNC(mutex_owned(&`cpu_lock)) INTFUNC(mutex_owner(&`cpu_lock)) INTFUNC(mutex_type_adaptive(&`cpu_lock)) INTFUNC(mutex_type_spin(&`cpu_lock)) INTFUNC(rw_read_held(&`vfssw_lock)) INTFUNC(rw_write_held(&`vfssw_lock)) INTFUNC(rw_iswriter(&`vfssw_lock)) INTFUNC(copyin(NULL, 1)) STRFUNC(copyinstr(NULL, 1)) INTFUNC(speculation()) INTFUNC(progenyof($pid)) INTFUNC(strlen("fooey")) VOIDFUNC(copyout) VOIDFUNC(copyoutstr) INTFUNC(alloca(10)) VOIDFUNC(bcopy) VOIDFUNC(copyinto) INTFUNC(msgdsize(NULL)) INTFUNC(msgsize(NULL)) INTFUNC(getmajor(0)) INTFUNC(getminor(0)) STRFUNC(ddi_pathname(NULL, 0)) STRFUNC(strjoin("foo", "bar")) STRFUNC(lltostr(12373)) STRFUNC(basename("/var/crash/systemtap")) STRFUNC(dirname("/var/crash/systemtap")) STRFUNC(cleanpath("/var/crash/systemtap")) STRFUNC(strchr("The SystemTap, The.", 't')) STRFUNC(strrchr("The SystemTap, The.", 't')) STRFUNC(strstr("The SystemTap, The.", "The")) STRFUNC(strtok("The SystemTap, The.", "T")) STRFUNC(substr("The SystemTap, The.", 0)) INTFUNC(index("The SystemTap, The.", "The")) INTFUNC(rindex("The SystemTap, The.", "The")) INTFUNC(htons(0x1234)) INTFUNC(htonl(0x12345678)) INTFUNC(htonll(0x1234567890abcdefL)) INTFUNC(ntohs(0x1234)) INTFUNC(ntohl(0x12345678)) INTFUNC(ntohll(0x1234567890abcdefL)) STRFUNC(inet_ntoa((ipaddr_t *)alloca(sizeof (ipaddr_t)))) STRFUNC(inet_ntoa6((in6_addr_t *)alloca(sizeof (in6_addr_t)))) STRFUNC(inet_ntop(AF_INET, (void *)alloca(sizeof (ipaddr_t)))) +INTFUNC(getf(0)) +INTFUNC(strtoll("0x12EE5D5", 16)) +STRFUNC(json("{\"systemtap\": false}", "systemtap")) BEGIN /subr == DIF_SUBR_MAX + 1/ { exit(0); } BEGIN { printf("found %d subroutines, expected %d\n", subr, DIF_SUBR_MAX + 1); exit(1); } Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.general.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.general.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.general.d (revision 268578) @@ -0,0 +1,179 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2012, Joyent, Inc. All rights reserved. + */ + +/* + * General functional tests of JSON parser for json(). + */ + +#pragma D option quiet +#pragma D option strsize=1k + +#define TST(name) \ + printf("\ntst |%s|\n", name) +#define IN2(vala, valb) \ + in = strjoin(vala, valb); \ + printf("in |%s|\n", in) +#define IN(val) \ + in = val; \ + printf("in |%s|\n", in) +#define SEL(ss) \ + out = json(in, ss); \ + printf("sel |%s|\nout |%s|\n", ss, \ + out != NULL ? out : "") + +BEGIN +{ + TST("empty array"); + IN("[]"); + SEL("0"); + + TST("one-element array: integer"); + IN("[1]"); + SEL("0"); + SEL("1"); + SEL("100"); + SEL("-1"); + + TST("one-element array: hex integer (not in spec, not supported)"); + IN("[0x1000]"); + SEL("0"); + + TST("one-element array: float"); + IN("[1.5001]"); + SEL("0"); + + TST("one-element array: float + exponent"); + IN("[16.3e10]"); + SEL("0"); + + TST("one-element array: integer + whitespace"); + IN("[ \t 5\t]"); + SEL("0"); + + TST("one-element array: integer + exponent + whitespace"); + IN("[ \t \t 16E10 \t ]"); + SEL("0"); + + TST("one-element array: string"); + IN("[\"alpha\"]"); + SEL("0"); + + TST("alternative first-element indexing"); + IN("[1,5,10,15,20]"); + SEL("[0]"); + SEL("[3]"); + SEL("[4]"); + SEL("[5]"); + + TST("one-element array: object"); + IN("[ { \"first\": true, \"second\": false }]"); + SEL("0.first"); + SEL("0.second"); + SEL("0.third"); + + TST("many-element array: integers"); + IN("[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]"); + SEL("10"); /* F(10) = 55 */ + SEL("14"); /* F(14) = 377 */ + SEL("19"); + + TST("many-element array: multiple types"); + IN2("[\"string\",32,true,{\"a\":9,\"b\":false},100.3e10,false,200.5,", + "{\"key\":\"val\"},null]"); + SEL("0"); + SEL("0.notobject"); + SEL("1"); + SEL("2"); + SEL("3"); + SEL("3.a"); + SEL("3.b"); + SEL("3.c"); + SEL("4"); + SEL("5"); + SEL("6"); + SEL("7"); + SEL("7.key"); + SEL("7.key.notobject"); + SEL("7.nonexist"); + SEL("8"); + SEL("9"); + + TST("many-element array: multiple types + whitespace"); + IN2("\n[\t\"string\" ,\t32 , true\t,\t {\"a\": 9,\t\"b\": false},\t\t", + "100.3e10, false, 200.5,{\"key\" \t:\n \"val\"},\t\t null ]\t\t"); + SEL("0"); + SEL("0.notobject"); + SEL("1"); + SEL("2"); + SEL("3"); + SEL("3.a"); + SEL("3.b"); + SEL("3.c"); + SEL("4"); + SEL("5"); + SEL("6"); + SEL("7"); + SEL("7.key"); + SEL("7.key.notobject"); + SEL("7.nonexist"); + SEL("8"); + SEL("9"); + + TST("two-element array: various string escape codes"); + IN2("[\"abcd \\\" \\\\ \\/ \\b \\f \\n \\r \\t \\u0000 \\uf00F \", ", + "\"final\"]"); + SEL("0"); + SEL("1"); + + TST("three-element array: broken escape code"); + IN("[\"fine here\", \"dodgey \\u00AZ\", \"wont get here\"]"); + SEL("0"); + SEL("1"); + SEL("2"); + + TST("nested objects"); + IN2("{ \"top\": { \"mid\" : { \"legs\": \"feet\" }, \"number\": 9, ", + "\"array\":[0,1,{\"a\":true,\"bb\":[1,2,false,{\"x\":\"yz\"}]}]}}"); + SEL("top"); + SEL("fargo"); + SEL("top.mid"); + SEL("top.centre"); + SEL("top.mid.legs"); + SEL("top.mid.number"); + SEL("top.mid.array"); + SEL("top.number"); + SEL("top.array"); + SEL("top.array[0]"); + SEL("top.array[1]"); + SEL("top.array[2]"); + SEL("top.array[2].a"); + SEL("top.array[2].b"); + SEL("top.array[2].bb"); + SEL("top.array[2].bb[0]"); + SEL("top.array[2].bb[1]"); + SEL("top.array[2].bb[2]"); + SEL("top.array[2].bb[3]"); + SEL("top.array[2].bb[3].x"); + SEL("top.array[2].bb[3].x.nofurther"); + SEL("top.array[2].bb[4]"); + SEL("top.array[3]"); + + exit(0); +} + +ERROR +{ + exit(1); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.general.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.general.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.general.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.general.d.out (revision 268578) @@ -0,0 +1,218 @@ + +tst |empty array| +in |[]| +sel |0| +out || + +tst |one-element array: integer| +in |[1]| +sel |0| +out |1| +sel |1| +out || +sel |100| +out || +sel |-1| +out || + +tst |one-element array: hex integer (not in spec, not supported)| +in |[0x1000]| +sel |0| +out || + +tst |one-element array: float| +in |[1.5001]| +sel |0| +out |1.5001| + +tst |one-element array: float + exponent| +in |[16.3e10]| +sel |0| +out |16.3e10| + +tst |one-element array: integer + whitespace| +in |[ 5 ]| +sel |0| +out |5| + +tst |one-element array: integer + exponent + whitespace| +in |[ 16E10 ]| +sel |0| +out |16E10| + +tst |one-element array: string| +in |["alpha"]| +sel |0| +out |alpha| + +tst |alternative first-element indexing| +in |[1,5,10,15,20]| +sel |[0]| +out |1| +sel |[3]| +out |15| +sel |[4]| +out |20| +sel |[5]| +out || + +tst |one-element array: object| +in |[ { "first": true, "second": false }]| +sel |0.first| +out |true| +sel |0.second| +out |false| +sel |0.third| +out || + +tst |many-element array: integers| +in |[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]| +sel |10| +out |55| +sel |14| +out |377| +sel |19| +out || + +tst |many-element array: multiple types| +in |["string",32,true,{"a":9,"b":false},100.3e10,false,200.5,{"key":"val"},null]| +sel |0| +out |string| +sel |0.notobject| +out || +sel |1| +out |32| +sel |2| +out |true| +sel |3| +out |{"a":9,"b":false}| +sel |3.a| +out |9| +sel |3.b| +out |false| +sel |3.c| +out || +sel |4| +out |100.3e10| +sel |5| +out |false| +sel |6| +out |200.5| +sel |7| +out |{"key":"val"}| +sel |7.key| +out |val| +sel |7.key.notobject| +out || +sel |7.nonexist| +out || +sel |8| +out |null| +sel |9| +out || + +tst |many-element array: multiple types + whitespace| +in | +[ "string" , 32 , true , {"a": 9, "b": false}, 100.3e10, false, 200.5,{"key" : + "val"}, null ] | +sel |0| +out |string| +sel |0.notobject| +out || +sel |1| +out |32| +sel |2| +out |true| +sel |3| +out |{"a": 9, "b": false}| +sel |3.a| +out |9| +sel |3.b| +out |false| +sel |3.c| +out || +sel |4| +out |100.3e10| +sel |5| +out |false| +sel |6| +out |200.5| +sel |7| +out |{"key" : + "val"}| +sel |7.key| +out |val| +sel |7.key.notobject| +out || +sel |7.nonexist| +out || +sel |8| +out |null| +sel |9| +out || + +tst |two-element array: various string escape codes| +in |["abcd \" \\ \/ \b \f \n \r \t \u0000 \uf00F ", "final"]| +sel |0| +out |abcd \" \\ \/ \b \f \n \r \t \u0000 \uf00F | +sel |1| +out |final| + +tst |three-element array: broken escape code| +in |["fine here", "dodgey \u00AZ", "wont get here"]| +sel |0| +out |fine here| +sel |1| +out || +sel |2| +out || + +tst |nested objects| +in |{ "top": { "mid" : { "legs": "feet" }, "number": 9, "array":[0,1,{"a":true,"bb":[1,2,false,{"x":"yz"}]}]}}| +sel |top| +out |{ "mid" : { "legs": "feet" }, "number": 9, "array":[0,1,{"a":true,"bb":[1,2,false,{"x":"yz"}]}]}| +sel |fargo| +out || +sel |top.mid| +out |{ "legs": "feet" }| +sel |top.centre| +out || +sel |top.mid.legs| +out |feet| +sel |top.mid.number| +out || +sel |top.mid.array| +out || +sel |top.number| +out |9| +sel |top.array| +out |[0,1,{"a":true,"bb":[1,2,false,{"x":"yz"}]}]| +sel |top.array[0]| +out |0| +sel |top.array[1]| +out |1| +sel |top.array[2]| +out |{"a":true,"bb":[1,2,false,{"x":"yz"}]}| +sel |top.array[2].a| +out |true| +sel |top.array[2].b| +out || +sel |top.array[2].bb| +out |[1,2,false,{"x":"yz"}]| +sel |top.array[2].bb[0]| +out |1| +sel |top.array[2].bb[1]| +out |2| +sel |top.array[2].bb[2]| +out |false| +sel |top.array[2].bb[3]| +out |{"x":"yz"}| +sel |top.array[2].bb[3].x| +out |yz| +sel |top.array[2].bb[3].x.nofurther| +out || +sel |top.array[2].bb[4]| +out || +sel |top.array[3]| +out || + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.strsize.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.strsize.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.strsize.d (revision 268578) @@ -0,0 +1,51 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2012, Joyent, Inc. All rights reserved. + */ + +/* + * ASSERTION: + * json() run time must be bounded above by strsize. This test makes strsize + * small and deliberately overflows it to prove we bail and return NULL in + * the event that we run off the end of the string. + * + */ + +#pragma D option quiet +#pragma D option strsize=18 + +BEGIN +{ + in = "{\"a\": 1024}"; /* length == 19 */ + out = json(in, "a"); + printf("|%s|\n%s\n\n", in, out != NULL ? out : ""); + + in = "{\"a\": 1024}"; /* length == 11 */ + out = json(in, "a"); + printf("|%s|\n%s\n\n", in, out != NULL ? out : ""); + + in = "{\"a\":false,\"b\":true}"; /* length == 20 */ + out = json(in, "b"); + printf("|%s|\n%s\n\n", in, out != NULL ? out : ""); + + in = "{\"a\":false,\"b\":20}"; /* length == 18 */ + out = json(in, "b"); + printf("|%s|\n%s\n\n", in, out != NULL ? out : ""); + + exit(0); +} + +ERROR +{ + exit(1); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.strsize.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.strsize.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.strsize.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.strsize.d.out (revision 268578) @@ -0,0 +1,13 @@ +|{"a": 1024| + + +|{"a": 1024}| +1024 + +|{"a":false,"b":tru| + + +|{"a":false,"b":20}| +20 + + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.c =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.c (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.c (revision 268578) @@ -0,0 +1,61 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2012 (c), Joyent, Inc. All rights reserved. + */ + +#include +#include "usdt.h" + +#define FMT "{" \ + " \"sizes\": [ \"first\", 2, %f ]," \ + " \"index\": %d," \ + " \"facts\": {" \ + " \"odd\": \"%s\"," \ + " \"even\": \"%s\"" \ + " }," \ + " \"action\": \"%s\"" \ + "}\n" + +int +waiting(volatile int *a) +{ + return (*a); +} + +int +main(int argc, char **argv) +{ + volatile int a = 0; + int idx; + double size = 250.5; + + while (waiting(&a) == 0) + continue; + + for (idx = 0; idx < 10; idx++) { + char *odd, *even, *json, *action; + + size *= 1.78; + odd = idx % 2 == 1 ? "true" : "false"; + even = idx % 2 == 0 ? "true" : "false"; + action = idx == 7 ? "ignore" : "print"; + + asprintf(&json, FMT, size, idx, odd, even, action); + BUNYAN_FAKE_LOG_DEBUG(json); + free(json); + } + + BUNYAN_FAKE_LOG_DEBUG("{\"finished\": true}"); + + return (0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.d (revision 268578) @@ -0,0 +1,65 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ + +#pragma D option strsize=4k +#pragma D option quiet +#pragma D option destructive + +/* + * This test reads a JSON string from a USDT probe, roughly simulating the + * primary motivating use case for the json() subroutine: filtering + * JSON-formatted log messages from a logging subsystem like node-bunyan. + */ + +pid$1:a.out:waiting:entry +{ + this->value = (int *)alloca(sizeof (int)); + *this->value = 1; + copyout(this->value, arg0, sizeof (int)); +} + +bunyan*$1:::log-* +{ + this->j = copyinstr(arg0); +} + +bunyan*$1:::log-* +/json(this->j, "finished") == NULL && json(this->j, "action") != "ignore"/ +{ + this->index = strtoll(json(this->j, "index")); + this->size = json(this->j, "sizes[2]"); + this->odd = json(this->j, "facts.odd"); + this->even = json(this->j, "facts.even"); + printf("[%d] sz %s odd %s even %s\n", this->index, this->size, + this->odd, this->even); +} + +bunyan*$1:::log-* +/json(this->j, "finished") != NULL/ +{ + printf("FINISHED!\n"); + exit(0); +} + +tick-10s +{ + printf("ERROR: Timed out before finish message!\n"); + exit(1); +} + +ERROR +{ + exit(1); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/tst.usdt.d.out (revision 268578) @@ -0,0 +1,11 @@ +[0] sz 445.890000 odd false even true +[1] sz 793.684200 odd true even false +[2] sz 1412.757876 odd false even true +[3] sz 2514.709019 odd true even false +[4] sz 4476.182054 odd false even true +[5] sz 7967.604057 odd true even false +[6] sz 14182.335221 odd false even true +[8] sz 44935.310914 odd false even true +[9] sz 79984.853427 odd true even false +FINISHED! + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/usdt.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/usdt.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/usdt.d (revision 268578) @@ -0,0 +1,27 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2012, Joyent, Inc. All rights reserved. + */ + +/* + * Sets up a fake node-bunyan-like USDT provider for use from C. + */ + +provider bunyan_fake { + probe log__trace(char *msg); + probe log__debug(char *msg); + probe log__info(char *msg); + probe log__warn(char *msg); + probe log__error(char *msg); + probe log__fatal(char *msg); +}; Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/json/usdt.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.fds.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.fds.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.fds.ksh (revision 268578) @@ -0,0 +1,91 @@ +# +# 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) 2012, Joyent, Inc. All rights reserved. +# + +tmpin=/tmp/tst.fds.$$.d +tmpout1=/tmp/tst.fds.$$.out1 +tmpout2=/tmp/tst.fds.$$.out2 + +cat > $tmpin < $tmpout2 +mv $tmpout2 $tmpout1 + +# +# And now with only dtrace_proc and dtrace_user -- the output should be +# identical. +# +ppriv -s A=basic,dtrace_proc,dtrace_user $$ + +/usr/sbin/dtrace -q -Cs /dev/stdin < $tmpin > $tmpout2 + +echo ">>> $tmpout1" +cat $tmpout1 + +echo ">>> $tmpout2" +cat $tmpout2 + +rval=0 + +if ! cmp $tmpout1 $tmpout2 ; then + rval=1 +fi + +rm $tmpout1 $tmpout2 $tmpin +exit $rval Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh (revision 268577) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.func_access.ksh (revision 268578) @@ -1,82 +1,87 @@ # # 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, Joyent, Inc. All rights reserved. # -#ident "%Z%%M% %I% %E% SMI" ppriv -s A=basic,dtrace_proc,dtrace_user $$ /usr/sbin/dtrace -q -s /dev/stdin <<"EOF" BEGIN { errorcount = 0; - expected_errorcount = 23; + expected_errorcount = 27; } BEGIN { trace(mutex_owned(&`pidlock)); } BEGIN { trace(mutex_owner(&`pidlock)); } BEGIN { trace(mutex_type_adaptive(&`pidlock)); } BEGIN { trace(mutex_type_spin(&`pidlock)); } BEGIN { trace(rw_read_held(&`ksyms_lock)); } BEGIN { trace(rw_write_held(&`ksyms_lock)); } BEGIN { trace(rw_iswriter(&`ksyms_lock)); } BEGIN { x = alloca(10); bcopy(`initname, x, 10); trace(stringof(x)); } /* We have no reliable way to test msgsize */ BEGIN { trace(strlen(`initname)); } BEGIN { trace(strchr(`initname, 0x69)); } BEGIN { trace(strrchr(`initname, 0x69)); } BEGIN { trace(strstr("/sbin/init/foo", `initname)); } BEGIN { trace(strstr(`initname, "in")); } BEGIN { trace(strtok(`initname, "/")); } BEGIN { trace(strtok(NULL, "/")); } BEGIN { trace(strtok("foo/bar", `initname)); } BEGIN { trace(strtok(NULL, `initname)); } +BEGIN { trace(strtoll(`initname)); } +BEGIN { trace(strtoll(`initname, 10)); } BEGIN { trace(substr(`initname, 2, 3)); } BEGIN { trace(ddi_pathname(`top_devinfo, 1)); } BEGIN { trace(strjoin(`initname, "foo")); } BEGIN { trace(strjoin("foo", `initname)); } BEGIN { trace(dirname(`initname)); } BEGIN { trace(cleanpath(`initname)); } + +BEGIN { j = "{\"/sbin/init\":\"uh oh\"}"; trace(json(j, `initname)); } +BEGIN { trace(json(`initname, "x")); } ERROR { errorcount++; } BEGIN /errorcount == expected_errorcount/ { trace("test passed"); exit(0); } BEGIN /errorcount != expected_errorcount/ { printf("fail: expected %d. saw %d.", expected_errorcount, errorcount); exit(1); } EOF exit $? Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.getf.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.getf.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.getf.ksh (revision 268578) @@ -0,0 +1,98 @@ +# +# 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) 2012, Joyent, Inc. All rights reserved. +# + +ppriv -s A=basic,dtrace_proc,dtrace_user $$ + +/usr/sbin/dtrace -q -Cs /dev/stdin <fp = getf(0); errmsg = "can't read field"; \ + printf("field: "); trace(this->fp->field); printf("\n"); } + +#define CANTREAD(field) \ + BEGIN { errmsg = ""; this->fp = getf(0); trace(this->fp->field); \ + printf("\nable to successfully read field!"); exit(1); } + +CANREAD(f_flag) +CANREAD(f_flag2) +CANREAD(f_vnode) +CANREAD(f_offset) +CANREAD(f_cred) +CANREAD(f_audit_data) +CANREAD(f_count) + +/* + * We can potentially read parts of our cred, but we can't dereference + * through cr_zone. + */ +CANTREAD(f_cred->cr_zone->zone_id) + +CANREAD(f_vnode->v_path) +CANREAD(f_vnode->v_op) +CANREAD(f_vnode->v_op->vnop_name) + +CANTREAD(f_vnode->v_flag) +CANTREAD(f_vnode->v_count) +CANTREAD(f_vnode->v_pages) +CANTREAD(f_vnode->v_type) +CANTREAD(f_vnode->v_vfsmountedhere) +CANTREAD(f_vnode->v_op->vop_open) + +BEGIN +{ + errmsg = ""; + this->fp = getf(0); + this->fp2 = getf(1); + + trace(this->fp->f_vnode); + printf("\nable to successfully read this->fp!"); + exit(1); +} + +BEGIN +{ + errmsg = ""; + this->fp = getf(0); +} + +BEGIN +{ + trace(this->fp->f_vnode); + printf("\nable to successfully read this->fp from prior clause!"); +} + +BEGIN +{ + exit(0); +} + +ERROR +/errmsg != ""/ +{ + printf("fatal error: %s", errmsg); + exit(1); +} + +EOF Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.procpriv.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.procpriv.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.procpriv.ksh (revision 268578) @@ -0,0 +1,138 @@ +# +# 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) 2012, Joyent, Inc. All rights reserved. +# + +ppriv -s A=basic,dtrace_proc,dtrace_user $$ + +# +# When we have dtrace_proc (but lack dtrace_kernel), we expect to be able to +# read certain curpsinfo/curlwpsinfo/curcpu fields even though they require +# reading in-kernel state. However, there are other fields in these translated +# structures that we know we shouldn't be able to read, as they require reading +# in-kernel state that we cannot read with only dtrace_proc. Finally, there +# are a few fields that we may or may not be able to read depending on the +# specifics of context. This test therefore asserts that we can read what we +# think we should be able to, that we can't read what we think we shouldn't be +# able to, and (for purposes of completeness) that we are indifferent about +# what we cannot assert one way or the other. +# +/usr/sbin/dtrace -q -Cs /dev/stdin <field); printf("\n"); } + +#define CANTREAD(what, field) \ + BEGIN { errmsg = ""; trace(what->field); \ + printf("\nable to successfully read field from what!"); exit(1); } + +#define MIGHTREAD(what, field) \ + BEGIN { errmsg = ""; printf("field: "); trace(what->field); printf("\n"); } + +#define CANREADVAR(vname) \ + BEGIN { errmsg = "can't read vname"; printf("vname: "); \ + trace(vname); printf("\n"); } + +#define CANTREADVAR(vname) \ + BEGIN { errmsg = ""; trace(vname); \ + printf("\nable to successfully read vname!"); exit(1); } + +#define MIGHTREADVAR(vname) \ + BEGIN { errmsg = ""; printf("vname: "); trace(vname); printf("\n"); } + +CANREAD(curpsinfo, pr_pid) +CANREAD(curpsinfo, pr_nlwp) +CANREAD(curpsinfo, pr_ppid) +CANREAD(curpsinfo, pr_uid) +CANREAD(curpsinfo, pr_euid) +CANREAD(curpsinfo, pr_gid) +CANREAD(curpsinfo, pr_egid) +CANREAD(curpsinfo, pr_addr) +CANREAD(curpsinfo, pr_start) +CANREAD(curpsinfo, pr_fname) +CANREAD(curpsinfo, pr_psargs) +CANREAD(curpsinfo, pr_argc) +CANREAD(curpsinfo, pr_argv) +CANREAD(curpsinfo, pr_envp) +CANREAD(curpsinfo, pr_dmodel) + +/* + * If our p_pgidp points to the same pid structure as our p_pidp, we will + * be able to read pr_pgid -- but we won't if not. + */ +MIGHTREAD(curpsinfo, pr_pgid) + +CANTREAD(curpsinfo, pr_sid) +CANTREAD(curpsinfo, pr_ttydev) +CANTREAD(curpsinfo, pr_projid) +CANTREAD(curpsinfo, pr_zoneid) +CANTREAD(curpsinfo, pr_contract) + +CANREAD(curlwpsinfo, pr_flag) +CANREAD(curlwpsinfo, pr_lwpid) +CANREAD(curlwpsinfo, pr_addr) +CANREAD(curlwpsinfo, pr_wchan) +CANREAD(curlwpsinfo, pr_stype) +CANREAD(curlwpsinfo, pr_state) +CANREAD(curlwpsinfo, pr_sname) +CANREAD(curlwpsinfo, pr_syscall) +CANREAD(curlwpsinfo, pr_pri) +CANREAD(curlwpsinfo, pr_onpro) +CANREAD(curlwpsinfo, pr_bindpro) +CANREAD(curlwpsinfo, pr_bindpset) + +CANTREAD(curlwpsinfo, pr_clname) +CANTREAD(curlwpsinfo, pr_lgrp) + +CANREAD(curcpu, cpu_id) + +CANTREAD(curcpu, cpu_pset) +CANTREAD(curcpu, cpu_chip) +CANTREAD(curcpu, cpu_lgrp) +CANTREAD(curcpu, cpu_info) + +/* + * We cannot assert one thing or another about the variable "root": for those + * with only dtrace_proc, it will be readable in the global but not readable in + * the non-global. + */ +MIGHTREADVAR(root) + +CANREADVAR(cpu) +CANTREADVAR(pset) +CANTREADVAR(cwd) +CANTREADVAR(chip) +CANTREADVAR(lgrp) + +BEGIN +{ + exit(0); +} + +ERROR +/errmsg != ""/ +{ + printf("fatal error: %s", errmsg); + exit(1); +} Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.providers.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.providers.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/privs/tst.providers.ksh (revision 268578) @@ -0,0 +1,126 @@ +# +# 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) 2012, Joyent, Inc. All rights reserved. +# + +# +# First, make sure that we can successfully enable the io provider +# +if ! dtrace -P io -n BEGIN'{exit(0)}' > /dev/null 2>&1 ; then + echo failed to enable io provider with full privs + exit 1 +fi + +ppriv -s A=basic,dtrace_proc,dtrace_user $$ + +# +# Now make sure that we cannot enable the io provider with reduced privs +# +if ! dtrace -x errtags -P io -n BEGIN'{exit(1)}' 2>&1 | \ + grep D_PDESC_ZERO > /dev/null 2>&1 ; then + echo successfully enabled the io provider with reduced privs + exit 1 +fi + +# +# Keeping our reduced privs, we want to assure that we can see every provider +# that we think we should be able to see -- and that we can see curpsinfo +# state but can't otherwise see arguments. +# +/usr/sbin/dtrace -wq -Cs /dev/stdin <ignore = stringof(curpsinfo->pr_psargs); \ + errstr = ""; \ +} \ + \ +END \ +/err == 0 && !seen["provider"]/ \ +{ \ + printf("no probes from provider\n"); \ + exit(++err); \ +} \ + \ +END \ +/err == 0/ \ +{ \ + printf("saw %d probes from provider\n", seen["provider"]); \ +} + +CANENABLE(proc) +CANENABLE(sched) +CANENABLE(vminfo) +CANENABLE(sysinfo) + +BEGIN +{ + /* + * We'll kick off a system of a do-nothing command -- which should be + * enough to kick proc, sched, vminfo and sysinfo probes. + */ + system("echo > /dev/null"); +} + +ERROR +/err == 0 && errstr != ""/ +{ + printf("fatal error: couldn't read curpsinfo->pr_psargs in "); + printf("%s-provided probe\n", errstr); + exit(++err); +} + +proc:::exit +/progenyof(\$pid)/ +{ + exit(0); +} + +tick-10ms +/i++ > 500/ +{ + printf("exit probe did not seem to fire\n"); + exit(++err); +} +EOF Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/err.BaseTooLarge.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/err.BaseTooLarge.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/err.BaseTooLarge.d (revision 268578) @@ -0,0 +1,35 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ + +/* + * ASSERTION: + * The largest base we will accept is Base 36 -- i.e. using all of 0-9 and + * A-Z as numerals. + * + * SECTION: Actions and Subroutines/strtoll() + */ + +#pragma D option quiet + +BEGIN +{ + printf("%d\n", strtoll("0", 37)); + exit(0); +} + +ERROR +{ + exit(1); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/err.BaseTooLarge.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/err.BaseTooSmall.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/err.BaseTooSmall.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/err.BaseTooSmall.d (revision 268578) @@ -0,0 +1,34 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ + +/* + * ASSERTION: + * The smallest base we will accept is Base 2. + * + * SECTION: Actions and Subroutines/strtoll() + */ + +#pragma D option quiet + +BEGIN +{ + printf("%d\n", strtoll("0", 1)); + exit(0); +} + +ERROR +{ + exit(1); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/err.BaseTooSmall.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d (revision 268578) @@ -0,0 +1,66 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ + +/* + * ASSERTION: + * Test the strtoll() subroutine. + * + * SECTION: Actions and Subroutines/strtoll() + */ + +#pragma D option quiet + +BEGIN +{ + + /* minimum base (2) and maximum base (36): */ + printf("%d\n", strtoll("0", 2)); + printf("%d\n", strtoll("1", 36)); + + /* simple tests: */ + printf("%d\n", strtoll("0x20", 16)); + printf("%d\n", strtoll("-32", 10)); + printf("%d\n", strtoll("010", 8)); + printf("%d\n", strtoll("101010", 2)); + + /* INT64_MIN and INT64_MAX: */ + printf("%d\n", strtoll("9223372036854775807")); + printf("%d\n", strtoll("-9223372036854775808")); + printf("%d\n", strtoll("0777777777777777777777", 8)); + printf("%d\n", strtoll("-01000000000000000000000", 8)); + + /* wrapping: */ + printf("%d\n", strtoll("1000000000000000000000", 8)); + printf("%d\n", strtoll("-1000000000000000000001", 8)); + + /* hex without prefix: */ + printf("%d\n", strtoll("baddcafe", 16)); + + /* stopping at first out-of-base character: */ + printf("%d\n", strtoll("12j", 10)); + printf("%d\n", strtoll("102", 2)); + + /* base 36: */ + printf("%d\n", strtoll("-0DTrace4EverZ", 36)); + + /* base 10 is assumed: */ + printf("%d\n", strtoll("1985")); + printf("%d\n", strtoll("-2012")); + + /* empty string: */ + printf("%d\n", strtoll("")); + + exit(0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/strtoll/tst.strtoll.d.out (revision 268578) @@ -0,0 +1,20 @@ +0 +1 +32 +-32 +8 +42 +9223372036854775807 +-9223372036854775808 +9223372036854775807 +-9223372036854775808 +-9223372036854775808 +9223372036854775807 +3135097598 +12 +2 +-1819882045752187535 +1985 +-2012 +0 + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid.d (revision 268578) @@ -0,0 +1,21 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#pragma D option quiet + +BEGIN +{ + trace((pidfoo`int)0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid2.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid2.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid2.d (revision 268578) @@ -0,0 +1,21 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#pragma D option quiet + +BEGIN +{ + trace((pid8foo`int)0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid2.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid3.d =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid3.d (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid3.d (revision 268578) @@ -0,0 +1,21 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#pragma D option quiet + +BEGIN +{ + trace((pid0`int)0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidpid3.d ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidtype.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidtype.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidtype.ksh (revision 268578) @@ -0,0 +1,34 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# While it's hard to be completely certain that a type of the name we want +# doesn't exist, we're going to try to pick a name which is rather unique. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +t="season_8_mountain_of_madness_t" +pid=$$ + +rc=`$dtrace -n "BEGIN{ trace(pid$pid`$t)0); }"` + +exit $rc Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidtype2.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidtype2.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.invalidtype2.ksh (revision 268578) @@ -0,0 +1,35 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# While it's hard to be completely certain that a type of the name we want +# doesn't exist, we're going to try to pick a name which is rather +# unique. This time we're also going to use the pid$target alias. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +t="season_8_mountain_of_madness_t" +pid=$$ + +rc=`$dtrace -n "BEGIN{ trace(pid`$t)0); }"` -p $pid + +exit $rc Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.user64mode.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.user64mode.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/err.user64mode.ksh (revision 268578) @@ -0,0 +1,90 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# This test is purposefully using a 64-bit DTrace and thus 64-bit types +# when compared with a 32-bit process. This test uses the userland +# keyword and so the implicit copyin should access illegal memory and +# thus exit. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +t="zelda_info_t" +exe="tst.chasestrings.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +$dtrace -64 -qs /dev/stdin <t = (userland info_t *)arg0; + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + stringof(this->t->zi_gamename), this->t->zi_ndungeons, + stringof(this->t->zi_villain), this->t->zi_haszelda); + next = 1; +} + +pid$pid::has_dungeons:entry +/next == 1/ +{ + this->t = (userland info_t *)arg0; + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + stringof(this->t->zi_gamename), this->t->zi_ndungeons, + stringof(this->t->zi_villain), this->t->zi_haszelda); + next = 2; +} + +pid$pid::has_villain:entry +/next == 2/ +{ + this->t = (userland info_t *)arg0; + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + stringof(this->t->zi_gamename), this->t->zi_ndungeons, + stringof(this->t->zi_villain), this->t->zi_haszelda); + exit(0); +} + +ERROR +{ + exit(1); +} +EOF +rc=$? + +kill -9 $pid + +exit $rc Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.aouttype.c =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.aouttype.c (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.aouttype.c (revision 268578) @@ -0,0 +1,46 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2013 (c) Joyent, Inc. All rights reserved. + */ + +/* + * This test tries to make sure that we have CTF data for a type that only this + * binary would reasonably have. In this case, the + * season_7_lisa_the_vegetarian_t. + */ +#include + +typedef struct season_7_lisa_the_vegetarian { + int fr_salad; +} season_7_lisa_the_vegetarian_t; + +int +sleeper(season_7_lisa_the_vegetarian_t *lp) +{ + for (;;) { + sleep(lp->fr_salad); + } + /*NOTREACHED*/ + return (0); +} + +int +main(void) +{ + season_7_lisa_the_vegetarian_t l; + l.fr_salad = 100; + + sleeper(&l); + + return (0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.aouttype.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.aouttype.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.aouttype.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.aouttype.ksh (revision 268578) @@ -0,0 +1,44 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# Lookup a type that is inside a.out. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +t="season_7_lisa_the_vegetrian_t *" +exe="tst.aouttype.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +rc=`$dtrace -n "BEGIN{ trace((pid$pid\`$t)0); exit(0); }"` + +kill -9 $pid + +exit $rc Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.c =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.c (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.c (revision 268578) @@ -0,0 +1,79 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2013 (c) Joyent, Inc. All rights reserved. + */ + +/* + * This test takes data from the current binary which is basically running in a + * loop between two functions and our goal is to have two unique types that they + * contain which we can print. + */ + +#include + +typedef struct zelda_info { + char *zi_gamename; + int zi_ndungeons; + char *zi_villain; + int zi_haszelda; +} zelda_info_t; + +static int +has_princess(zelda_info_t *z) +{ + return (z->zi_haszelda); +} + +static int +has_dungeons(zelda_info_t *z) +{ + return (z->zi_ndungeons != 0); +} + +static const char * +has_villain(zelda_info_t *z) +{ + return (z->zi_villain); +} + +int +main(void) +{ + zelda_info_t oot; + zelda_info_t la; + zelda_info_t lttp; + + oot.zi_gamename = "Ocarina of Time"; + oot.zi_ndungeons = 10; + oot.zi_villain = "Ganondorf"; + oot.zi_haszelda = 1; + + la.zi_gamename = "Link's Awakening"; + la.zi_ndungeons = 9; + la.zi_villain = "Nightmare"; + la.zi_haszelda = 0; + + lttp.zi_gamename = "A Link to the Past"; + lttp.zi_ndungeons = 12; + lttp.zi_villain = "Ganon"; + lttp.zi_haszelda = 1; + + for (;;) { + (void) has_princess(&oot); + (void) has_dungeons(&la); + (void) has_villain(<tp); + sleep(1); + } + + return (0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.ksh (revision 268578) @@ -0,0 +1,76 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# This test is checking that we can read members and that pointers inside +# members point to valid data that is intelligible, eg. strings. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +t="zelda_info_t" +exe="tst.chasestrings.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +$dtrace -qs /dev/stdin <t = (pid$pid\`$t *)(copyin(arg0, sizeof (pid$pid\`$t))); + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + copyinstr((uintptr_t)this->t->zi_gamename), this->t->zi_ndungeons, + copyinstr((uintptr_t)this->t->zi_villain), this->t->zi_haszelda); + next = 1; +} + +pid$pid::has_dungeons:entry +/next == 1/ +{ + this->t = (pid$pid\`$t *)(copyin(arg0, sizeof (pid$pid\`$t))); + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + copyinstr((uintptr_t)this->t->zi_gamename), this->t->zi_ndungeons, + copyinstr((uintptr_t)this->t->zi_villain), this->t->zi_haszelda); + next = 2; +} + +pid$pid::has_villain:entry +/next == 2/ +{ + this->t = (pid$pid\`$t *)(copyin(arg0, sizeof (pid$pid\`$t))); + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + copyinstr((uintptr_t)this->t->zi_gamename), this->t->zi_ndungeons, + copyinstr((uintptr_t)this->t->zi_villain), this->t->zi_haszelda); + exit(0); +} +EOF +rc=$? + +kill -9 $pid + +exit $rc Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.ksh.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.ksh.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.chasestrings.ksh.out (revision 268578) @@ -0,0 +1,4 @@ +game: Ocarina of Time, dungeon: 10, villain: Ganondorf, zelda: 1 +game: Link's Awakening, dungeon: 9, villain: Nightmare, zelda: 0 +game: A Link to the Past, dungeon: 12, villain: Ganon, zelda: 1 + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.libtype.c =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.libtype.c (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.libtype.c (revision 268578) @@ -0,0 +1,29 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2013 (c) Joyent, Inc. All rights reserved. + */ + +/* + * We're linked against libc which has types, though we do not. + */ +#include + +int +main(void) +{ + for (;;) { + sleep(1000); + } + /*NOTREACHED*/ + return (0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.libtype.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.libtype.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.libtype.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.libtype.ksh (revision 268578) @@ -0,0 +1,45 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# Here we want to make sure that the program in question does not have ctf data +# in its a.out; however, we can get types out of a linked libc. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +t="int" +exe="tst.libtype.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -eq 0 ]]; then + echo "CTF exists in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +rc=`$dtrace -n "BEGIN{ trace((pid$pid\`$t)0); exit(0); }"` + +kill -9 $pid + +exit $rc Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.linkmap.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.linkmap.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.linkmap.ksh (revision 268578) @@ -0,0 +1,44 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# We should be able to see both strstr from libc and from ld on an +# alternate linkmap. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +$dtrace -q -p $$ -s /dev/stdin <' + exit 2 +fi + +dtrace=$1 +t="final_fantasy_info_t" +exe="tst.printtype.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +$dtrace -qs /dev/stdin <' + exit 2 +fi + +dtrace=$1 +t="final_fantasy_info_t" +exe="tst.printtype.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +$dtrace -p $pid -qs /dev/stdin < + +typedef struct final_fantasy_info { + int ff_gameid; + int ff_partysize; + int ff_hassummons; +} final_fantasy_info_t; + +static int +ff_getgameid(final_fantasy_info_t *f) +{ + return (0); +} + +static int +ff_getpartysize(final_fantasy_info_t *f) +{ + return (0); +} + +static int +ff_getsummons(final_fantasy_info_t *f) +{ + return (0); +} + +int +main(void) +{ + final_fantasy_info_t ffiii, ffx, ffi; + + ffi.ff_gameid = 1; + ffi.ff_partysize = 4; + ffi.ff_hassummons = 0; + + ffiii.ff_gameid = 6; + ffiii.ff_partysize = 4; + ffiii.ff_hassummons = 1; + + ffx.ff_gameid = 10; + ffx.ff_partysize = 3; + ffx.ff_hassummons = 1; + + for (;;) { + ff_getgameid(&ffi); + ff_getpartysize(&ffx); + ff_getsummons(&ffiii); + sleep(1); + } + /*NOTREACHED*/ + return (0); +} Property changes on: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.printtype.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.printtype.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.printtype.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.printtype.ksh (revision 268578) @@ -0,0 +1,69 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# Use print() on userland CTF types and verify we get the data we expect. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +t="final_fantasy_info_t" +exe="tst.printtype.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +$dtrace -qs /dev/stdin <' + exit 2 +fi + +dtrace=$1 +t="final_fantasy_info_t" +exe="tst.printtype.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +$dtrace -p $pid -qs /dev/stdin <' + exit 2 +fi + +dtrace=$1 +t="zelda_info_t" +exe="tst.chasestrings.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +$dtrace -32 -qs /dev/stdin <t = (userland info_t *)arg0; + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + stringof(this->t->zi_gamename), this->t->zi_ndungeons, + stringof(this->t->zi_villain), this->t->zi_haszelda); + next = 1; +} + +pid$pid::has_dungeons:entry +/next == 1/ +{ + this->t = (userland info_t *)arg0; + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + stringof(this->t->zi_gamename), this->t->zi_ndungeons, + stringof(this->t->zi_villain), this->t->zi_haszelda); + next = 2; +} + +pid$pid::has_villain:entry +/next == 2/ +{ + this->t = (userland info_t *)arg0; + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + stringof(this->t->zi_gamename), this->t->zi_ndungeons, + stringof(this->t->zi_villain), this->t->zi_haszelda); + exit(0); +} +EOF +rc=$? + +kill -9 $pid + +exit $rc Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userlandkey.ksh.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userlandkey.ksh.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userlandkey.ksh.out (revision 268578) @@ -0,0 +1,4 @@ +game: Ocarina of Time, dungeon: 10, villain: Ganondorf, zelda: 1 +game: Link's Awakening, dungeon: 9, villain: Nightmare, zelda: 0 +game: A Link to the Past, dungeon: 12, villain: Ganon, zelda: 1 + Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userstrings.ksh =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userstrings.ksh (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userstrings.ksh (revision 268578) @@ -0,0 +1,72 @@ +#! /usr/bin/ksh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +# +# This test is checking that we can read members and that pointers inside +# members point to valid data that is intelligible, eg. strings. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +exe="tst.chasestrings.exe" + +elfdump "./$exe" | grep -q '.SUNW_ctf' +if [[ $? -ne 0 ]]; then + echo "CTF does not exist in $exe, that's a bug" >&2 + exit 1 +fi + +./$exe & +pid=$! + +$dtrace -qs /dev/stdin <zi_gamename), args[0]->zi_ndungeons, + stringof(args[0]->zi_villain), args[0]->zi_haszelda); + next = 1; +} + +pid$pid::has_dungeons:entry +/next == 1/ +{ + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + stringof(args[0]->zi_gamename), args[0]->zi_ndungeons, + stringof(args[0]->zi_villain), args[0]->zi_haszelda); + next = 2; +} + +pid$pid::has_villain:entry +/next == 2/ +{ + printf("game: %s, dungeon: %d, villain: %s, zelda: %d\n", + stringof(args[0]->zi_gamename), args[0]->zi_ndungeons, + stringof(args[0]->zi_villain), args[0]->zi_haszelda); + exit(0); +} +EOF +rc=$? + +kill -9 $pid + +exit $rc Index: stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userstrings.ksh.out =================================================================== --- stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userstrings.ksh.out (nonexistent) +++ stable/10/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/uctf/tst.userstrings.ksh.out (revision 268578) @@ -0,0 +1,4 @@ +game: Ocarina of Time, dungeon: 10, villain: Ganondorf, zelda: 1 +game: Link's Awakening, dungeon: 9, villain: Nightmare, zelda: 0 +game: A Link to the Past, dungeon: 12, villain: Ganon, zelda: 1 + Index: stable/10/cddl/contrib/opensolaris/common/ctf/ctf_open.c =================================================================== --- stable/10/cddl/contrib/opensolaris/common/ctf/ctf_open.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/common/ctf/ctf_open.c (revision 268578) @@ -1,959 +1,1045 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #include #include #include static const ctf_dmodel_t _libctf_models[] = { { "ILP32", CTF_MODEL_ILP32, 4, 1, 2, 4, 4 }, { "LP64", CTF_MODEL_LP64, 8, 1, 2, 4, 8 }, { NULL, 0, 0, 0, 0, 0, 0 } }; const char _CTF_SECTION[] = ".SUNW_ctf"; const char _CTF_NULLSTR[] = ""; int _libctf_version = CTF_VERSION; /* library client version */ int _libctf_debug = 0; /* debugging messages enabled */ static ushort_t get_kind_v1(ushort_t info) { return (CTF_INFO_KIND_V1(info)); } static ushort_t get_kind_v2(ushort_t info) { return (CTF_INFO_KIND(info)); } static ushort_t get_root_v1(ushort_t info) { return (CTF_INFO_ISROOT_V1(info)); } static ushort_t get_root_v2(ushort_t info) { return (CTF_INFO_ISROOT(info)); } static ushort_t get_vlen_v1(ushort_t info) { return (CTF_INFO_VLEN_V1(info)); } static ushort_t get_vlen_v2(ushort_t info) { return (CTF_INFO_VLEN(info)); } static const ctf_fileops_t ctf_fileops[] = { { NULL, NULL }, { get_kind_v1, get_root_v1, get_vlen_v1 }, { get_kind_v2, get_root_v2, get_vlen_v2 }, }; /* * Convert a 32-bit ELF symbol into GElf (Elf64) and return a pointer to it. */ static Elf64_Sym * sym_to_gelf(const Elf32_Sym *src, Elf64_Sym *dst) { dst->st_name = src->st_name; dst->st_value = src->st_value; dst->st_size = src->st_size; dst->st_info = src->st_info; dst->st_other = src->st_other; dst->st_shndx = src->st_shndx; return (dst); } /* * Initialize the symtab translation table by filling each entry with the * offset of the CTF type or function data corresponding to each STT_FUNC or * STT_OBJECT entry in the symbol table. */ static int init_symtab(ctf_file_t *fp, const ctf_header_t *hp, const ctf_sect_t *sp, const ctf_sect_t *strp) { const uchar_t *symp = sp->cts_data; uint_t *xp = fp->ctf_sxlate; uint_t *xend = xp + fp->ctf_nsyms; uint_t objtoff = hp->cth_objtoff; uint_t funcoff = hp->cth_funcoff; ushort_t info, vlen; Elf64_Sym sym, *gsp; const char *name; /* * The CTF data object and function type sections are ordered to match * the relative order of the respective symbol types in the symtab. * If no type information is available for a symbol table entry, a * pad is inserted in the CTF section. As a further optimization, * anonymous or undefined symbols are omitted from the CTF data. */ for (; xp < xend; xp++, symp += sp->cts_entsize) { if (sp->cts_entsize == sizeof (Elf32_Sym)) gsp = sym_to_gelf((Elf32_Sym *)(uintptr_t)symp, &sym); else gsp = (Elf64_Sym *)(uintptr_t)symp; if (gsp->st_name < strp->cts_size) name = (const char *)strp->cts_data + gsp->st_name; else name = _CTF_NULLSTR; if (gsp->st_name == 0 || gsp->st_shndx == SHN_UNDEF || strcmp(name, "_START_") == 0 || strcmp(name, "_END_") == 0) { *xp = -1u; continue; } switch (ELF64_ST_TYPE(gsp->st_info)) { case STT_OBJECT: if (objtoff >= hp->cth_funcoff || (gsp->st_shndx == SHN_ABS && gsp->st_value == 0)) { *xp = -1u; break; } *xp = objtoff; objtoff += sizeof (ushort_t); break; case STT_FUNC: if (funcoff >= hp->cth_typeoff) { *xp = -1u; break; } *xp = funcoff; info = *(ushort_t *)((uintptr_t)fp->ctf_buf + funcoff); vlen = LCTF_INFO_VLEN(fp, info); /* * If we encounter a zero pad at the end, just skip it. * Otherwise skip over the function and its return type * (+2) and the argument list (vlen). */ if (LCTF_INFO_KIND(fp, info) == CTF_K_UNKNOWN && vlen == 0) funcoff += sizeof (ushort_t); /* skip pad */ else funcoff += sizeof (ushort_t) * (vlen + 2); break; default: *xp = -1u; break; } } ctf_dprintf("loaded %lu symtab entries\n", fp->ctf_nsyms); return (0); } /* * Initialize the type ID translation table with the byte offset of each type, * and initialize the hash tables of each named type. */ static int init_types(ctf_file_t *fp, const ctf_header_t *cth) { /* LINTED - pointer alignment */ const ctf_type_t *tbuf = (ctf_type_t *)(fp->ctf_buf + cth->cth_typeoff); /* LINTED - pointer alignment */ const ctf_type_t *tend = (ctf_type_t *)(fp->ctf_buf + cth->cth_stroff); ulong_t pop[CTF_K_MAX + 1] = { 0 }; const ctf_type_t *tp; ctf_hash_t *hp; ushort_t id, dst; uint_t *xp; /* * We initially determine whether the container is a child or a parent * based on the value of cth_parname. To support containers that pre- * date cth_parname, we also scan the types themselves for references * to values in the range reserved for child types in our first pass. */ int child = cth->cth_parname != 0; int nlstructs = 0, nlunions = 0; int err; /* * We make two passes through the entire type section. In this first * pass, we count the number of each type and the total number of types. */ for (tp = tbuf; tp < tend; fp->ctf_typemax++) { ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info); ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info); ssize_t size, increment; size_t vbytes; uint_t n; (void) ctf_get_ctt_size(fp, tp, &size, &increment); switch (kind) { case CTF_K_INTEGER: case CTF_K_FLOAT: vbytes = sizeof (uint_t); break; case CTF_K_ARRAY: vbytes = sizeof (ctf_array_t); break; case CTF_K_FUNCTION: vbytes = sizeof (ushort_t) * (vlen + (vlen & 1)); break; case CTF_K_STRUCT: case CTF_K_UNION: if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) { ctf_member_t *mp = (ctf_member_t *) ((uintptr_t)tp + increment); vbytes = sizeof (ctf_member_t) * vlen; for (n = vlen; n != 0; n--, mp++) child |= CTF_TYPE_ISCHILD(mp->ctm_type); } else { ctf_lmember_t *lmp = (ctf_lmember_t *) ((uintptr_t)tp + increment); vbytes = sizeof (ctf_lmember_t) * vlen; for (n = vlen; n != 0; n--, lmp++) child |= CTF_TYPE_ISCHILD(lmp->ctlm_type); } break; case CTF_K_ENUM: vbytes = sizeof (ctf_enum_t) * vlen; break; case CTF_K_FORWARD: /* * For forward declarations, ctt_type is the CTF_K_* * kind for the tag, so bump that population count too. * If ctt_type is unknown, treat the tag as a struct. */ if (tp->ctt_type == CTF_K_UNKNOWN || tp->ctt_type >= CTF_K_MAX) pop[CTF_K_STRUCT]++; else pop[tp->ctt_type]++; /*FALLTHRU*/ case CTF_K_UNKNOWN: vbytes = 0; break; case CTF_K_POINTER: case CTF_K_TYPEDEF: case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: child |= CTF_TYPE_ISCHILD(tp->ctt_type); vbytes = 0; break; default: ctf_dprintf("detected invalid CTF kind -- %u\n", kind); return (ECTF_CORRUPT); } tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); pop[kind]++; } /* * If we detected a reference to a child type ID, then we know this * container is a child and may have a parent's types imported later. */ if (child) { ctf_dprintf("CTF container %p is a child\n", (void *)fp); fp->ctf_flags |= LCTF_CHILD; } else ctf_dprintf("CTF container %p is a parent\n", (void *)fp); /* * Now that we've counted up the number of each type, we can allocate * the hash tables, type translation table, and pointer table. */ if ((err = ctf_hash_create(&fp->ctf_structs, pop[CTF_K_STRUCT])) != 0) return (err); if ((err = ctf_hash_create(&fp->ctf_unions, pop[CTF_K_UNION])) != 0) return (err); if ((err = ctf_hash_create(&fp->ctf_enums, pop[CTF_K_ENUM])) != 0) return (err); if ((err = ctf_hash_create(&fp->ctf_names, pop[CTF_K_INTEGER] + pop[CTF_K_FLOAT] + pop[CTF_K_FUNCTION] + pop[CTF_K_TYPEDEF] + pop[CTF_K_POINTER] + pop[CTF_K_VOLATILE] + pop[CTF_K_CONST] + pop[CTF_K_RESTRICT])) != 0) return (err); fp->ctf_txlate = ctf_alloc(sizeof (uint_t) * (fp->ctf_typemax + 1)); fp->ctf_ptrtab = ctf_alloc(sizeof (ushort_t) * (fp->ctf_typemax + 1)); if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL) return (EAGAIN); /* memory allocation failed */ xp = fp->ctf_txlate; *xp++ = 0; /* type id 0 is used as a sentinel value */ bzero(fp->ctf_txlate, sizeof (uint_t) * (fp->ctf_typemax + 1)); bzero(fp->ctf_ptrtab, sizeof (ushort_t) * (fp->ctf_typemax + 1)); /* * In the second pass through the types, we fill in each entry of the * type and pointer tables and add names to the appropriate hashes. */ for (id = 1, tp = tbuf; tp < tend; xp++, id++) { ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info); ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info); ssize_t size, increment; const char *name; size_t vbytes; ctf_helem_t *hep; ctf_encoding_t cte; (void) ctf_get_ctt_size(fp, tp, &size, &increment); name = ctf_strptr(fp, tp->ctt_name); switch (kind) { case CTF_K_INTEGER: case CTF_K_FLOAT: /* * Only insert a new integer base type definition if * this type name has not been defined yet. We re-use * the names with different encodings for bit-fields. */ if ((hep = ctf_hash_lookup(&fp->ctf_names, fp, name, strlen(name))) == NULL) { err = ctf_hash_insert(&fp->ctf_names, fp, CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); if (err != 0 && err != ECTF_STRTAB) return (err); } else if (ctf_type_encoding(fp, hep->h_type, &cte) == 0 && cte.cte_bits == 0) { /* * Work-around SOS8 stabs bug: replace existing * intrinsic w/ same name if it was zero bits. */ hep->h_type = CTF_INDEX_TO_TYPE(id, child); } vbytes = sizeof (uint_t); break; case CTF_K_ARRAY: vbytes = sizeof (ctf_array_t); break; case CTF_K_FUNCTION: err = ctf_hash_insert(&fp->ctf_names, fp, CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); if (err != 0 && err != ECTF_STRTAB) return (err); vbytes = sizeof (ushort_t) * (vlen + (vlen & 1)); break; case CTF_K_STRUCT: err = ctf_hash_define(&fp->ctf_structs, fp, CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); if (err != 0 && err != ECTF_STRTAB) return (err); if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) vbytes = sizeof (ctf_member_t) * vlen; else { vbytes = sizeof (ctf_lmember_t) * vlen; nlstructs++; } break; case CTF_K_UNION: err = ctf_hash_define(&fp->ctf_unions, fp, CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); if (err != 0 && err != ECTF_STRTAB) return (err); if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) vbytes = sizeof (ctf_member_t) * vlen; else { vbytes = sizeof (ctf_lmember_t) * vlen; nlunions++; } break; case CTF_K_ENUM: err = ctf_hash_define(&fp->ctf_enums, fp, CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); if (err != 0 && err != ECTF_STRTAB) return (err); vbytes = sizeof (ctf_enum_t) * vlen; break; case CTF_K_TYPEDEF: err = ctf_hash_insert(&fp->ctf_names, fp, CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); if (err != 0 && err != ECTF_STRTAB) return (err); vbytes = 0; break; case CTF_K_FORWARD: /* * Only insert forward tags into the given hash if the * type or tag name is not already present. */ switch (tp->ctt_type) { case CTF_K_STRUCT: hp = &fp->ctf_structs; break; case CTF_K_UNION: hp = &fp->ctf_unions; break; case CTF_K_ENUM: hp = &fp->ctf_enums; break; default: hp = &fp->ctf_structs; } if (ctf_hash_lookup(hp, fp, name, strlen(name)) == NULL) { err = ctf_hash_insert(hp, fp, CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); if (err != 0 && err != ECTF_STRTAB) return (err); } vbytes = 0; break; case CTF_K_POINTER: /* * If the type referenced by the pointer is in this CTF * container, then store the index of the pointer type * in fp->ctf_ptrtab[ index of referenced type ]. */ if (CTF_TYPE_ISCHILD(tp->ctt_type) == child && CTF_TYPE_TO_INDEX(tp->ctt_type) <= fp->ctf_typemax) fp->ctf_ptrtab[ CTF_TYPE_TO_INDEX(tp->ctt_type)] = id; /*FALLTHRU*/ case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: err = ctf_hash_insert(&fp->ctf_names, fp, CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); if (err != 0 && err != ECTF_STRTAB) return (err); /*FALLTHRU*/ default: vbytes = 0; break; } *xp = (uint_t)((uintptr_t)tp - (uintptr_t)fp->ctf_buf); tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); } ctf_dprintf("%lu total types processed\n", fp->ctf_typemax); ctf_dprintf("%u enum names hashed\n", ctf_hash_size(&fp->ctf_enums)); ctf_dprintf("%u struct names hashed (%d long)\n", ctf_hash_size(&fp->ctf_structs), nlstructs); ctf_dprintf("%u union names hashed (%d long)\n", ctf_hash_size(&fp->ctf_unions), nlunions); ctf_dprintf("%u base type names hashed\n", ctf_hash_size(&fp->ctf_names)); /* * Make an additional pass through the pointer table to find pointers * that point to anonymous typedef nodes. If we find one, modify the * pointer table so that the pointer is also known to point to the * node that is referenced by the anonymous typedef node. */ for (id = 1; id <= fp->ctf_typemax; id++) { if ((dst = fp->ctf_ptrtab[id]) != 0) { tp = LCTF_INDEX_TO_TYPEPTR(fp, id); if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_TYPEDEF && strcmp(ctf_strptr(fp, tp->ctt_name), "") == 0 && CTF_TYPE_ISCHILD(tp->ctt_type) == child && CTF_TYPE_TO_INDEX(tp->ctt_type) <= fp->ctf_typemax) fp->ctf_ptrtab[ CTF_TYPE_TO_INDEX(tp->ctt_type)] = dst; } } return (0); } /* * Decode the specified CTF buffer and optional symbol table and create a new * CTF container representing the symbolic debugging information. This code * can be used directly by the debugger, or it can be used as the engine for * ctf_fdopen() or ctf_open(), below. */ ctf_file_t * ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, const ctf_sect_t *strsect, int *errp) { const ctf_preamble_t *pp; ctf_header_t hp; ctf_file_t *fp; void *buf, *base; size_t size, hdrsz; int err; if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL))) return (ctf_set_open_errno(errp, EINVAL)); if (symsect != NULL && symsect->cts_entsize != sizeof (Elf32_Sym) && symsect->cts_entsize != sizeof (Elf64_Sym)) return (ctf_set_open_errno(errp, ECTF_SYMTAB)); if (symsect != NULL && symsect->cts_data == NULL) return (ctf_set_open_errno(errp, ECTF_SYMBAD)); if (strsect != NULL && strsect->cts_data == NULL) return (ctf_set_open_errno(errp, ECTF_STRBAD)); if (ctfsect->cts_size < sizeof (ctf_preamble_t)) return (ctf_set_open_errno(errp, ECTF_NOCTFBUF)); pp = (const ctf_preamble_t *)ctfsect->cts_data; ctf_dprintf("ctf_bufopen: magic=0x%x version=%u\n", pp->ctp_magic, pp->ctp_version); /* * Validate each part of the CTF header (either V1 or V2). * First, we validate the preamble (common to all versions). At that * point, we know specific header version, and can validate the * version-specific parts including section offsets and alignments. */ if (pp->ctp_magic != CTF_MAGIC) return (ctf_set_open_errno(errp, ECTF_NOCTFBUF)); if (pp->ctp_version == CTF_VERSION_2) { if (ctfsect->cts_size < sizeof (ctf_header_t)) return (ctf_set_open_errno(errp, ECTF_NOCTFBUF)); bcopy(ctfsect->cts_data, &hp, sizeof (hp)); hdrsz = sizeof (ctf_header_t); } else if (pp->ctp_version == CTF_VERSION_1) { const ctf_header_v1_t *h1p = (const ctf_header_v1_t *)ctfsect->cts_data; if (ctfsect->cts_size < sizeof (ctf_header_v1_t)) return (ctf_set_open_errno(errp, ECTF_NOCTFBUF)); bzero(&hp, sizeof (hp)); hp.cth_preamble = h1p->cth_preamble; hp.cth_objtoff = h1p->cth_objtoff; hp.cth_funcoff = h1p->cth_funcoff; hp.cth_typeoff = h1p->cth_typeoff; hp.cth_stroff = h1p->cth_stroff; hp.cth_strlen = h1p->cth_strlen; hdrsz = sizeof (ctf_header_v1_t); } else return (ctf_set_open_errno(errp, ECTF_CTFVERS)); size = hp.cth_stroff + hp.cth_strlen; ctf_dprintf("ctf_bufopen: uncompressed size=%lu\n", (ulong_t)size); if (hp.cth_lbloff > size || hp.cth_objtoff > size || hp.cth_funcoff > size || hp.cth_typeoff > size || hp.cth_stroff > size) return (ctf_set_open_errno(errp, ECTF_CORRUPT)); if (hp.cth_lbloff > hp.cth_objtoff || hp.cth_objtoff > hp.cth_funcoff || hp.cth_funcoff > hp.cth_typeoff || hp.cth_typeoff > hp.cth_stroff) return (ctf_set_open_errno(errp, ECTF_CORRUPT)); if ((hp.cth_lbloff & 3) || (hp.cth_objtoff & 1) || (hp.cth_funcoff & 1) || (hp.cth_typeoff & 3)) return (ctf_set_open_errno(errp, ECTF_CORRUPT)); /* * Once everything is determined to be valid, attempt to decompress * the CTF data buffer if it is compressed. Otherwise we just put * the data section's buffer pointer into ctf_buf, below. */ if (hp.cth_flags & CTF_F_COMPRESS) { size_t srclen, dstlen; const void *src; int rc = Z_OK; if (ctf_zopen(errp) == NULL) return (NULL); /* errp is set for us */ if ((base = ctf_data_alloc(size + hdrsz)) == MAP_FAILED) return (ctf_set_open_errno(errp, ECTF_ZALLOC)); bcopy(ctfsect->cts_data, base, hdrsz); ((ctf_preamble_t *)base)->ctp_flags &= ~CTF_F_COMPRESS; buf = (uchar_t *)base + hdrsz; src = (uchar_t *)ctfsect->cts_data + hdrsz; srclen = ctfsect->cts_size - hdrsz; dstlen = size; if ((rc = z_uncompress(buf, &dstlen, src, srclen)) != Z_OK) { ctf_dprintf("zlib inflate err: %s\n", z_strerror(rc)); ctf_data_free(base, size + hdrsz); return (ctf_set_open_errno(errp, ECTF_DECOMPRESS)); } if (dstlen != size) { ctf_dprintf("zlib inflate short -- got %lu of %lu " "bytes\n", (ulong_t)dstlen, (ulong_t)size); ctf_data_free(base, size + hdrsz); return (ctf_set_open_errno(errp, ECTF_CORRUPT)); } ctf_data_protect(base, size + hdrsz); } else { base = (void *)ctfsect->cts_data; buf = (uchar_t *)base + hdrsz; } /* * Once we have uncompressed and validated the CTF data buffer, we can * proceed with allocating a ctf_file_t and initializing it. */ if ((fp = ctf_alloc(sizeof (ctf_file_t))) == NULL) return (ctf_set_open_errno(errp, EAGAIN)); bzero(fp, sizeof (ctf_file_t)); fp->ctf_version = hp.cth_version; fp->ctf_fileops = &ctf_fileops[hp.cth_version]; bcopy(ctfsect, &fp->ctf_data, sizeof (ctf_sect_t)); if (symsect != NULL) { bcopy(symsect, &fp->ctf_symtab, sizeof (ctf_sect_t)); bcopy(strsect, &fp->ctf_strtab, sizeof (ctf_sect_t)); } if (fp->ctf_data.cts_name != NULL) fp->ctf_data.cts_name = ctf_strdup(fp->ctf_data.cts_name); if (fp->ctf_symtab.cts_name != NULL) fp->ctf_symtab.cts_name = ctf_strdup(fp->ctf_symtab.cts_name); if (fp->ctf_strtab.cts_name != NULL) fp->ctf_strtab.cts_name = ctf_strdup(fp->ctf_strtab.cts_name); if (fp->ctf_data.cts_name == NULL) fp->ctf_data.cts_name = _CTF_NULLSTR; if (fp->ctf_symtab.cts_name == NULL) fp->ctf_symtab.cts_name = _CTF_NULLSTR; if (fp->ctf_strtab.cts_name == NULL) fp->ctf_strtab.cts_name = _CTF_NULLSTR; fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *)buf + hp.cth_stroff; fp->ctf_str[CTF_STRTAB_0].cts_len = hp.cth_strlen; if (strsect != NULL) { fp->ctf_str[CTF_STRTAB_1].cts_strs = strsect->cts_data; fp->ctf_str[CTF_STRTAB_1].cts_len = strsect->cts_size; } fp->ctf_base = base; fp->ctf_buf = buf; fp->ctf_size = size + hdrsz; /* * If we have a parent container name and label, store the relocated * string pointers in the CTF container for easy access later. */ if (hp.cth_parlabel != 0) fp->ctf_parlabel = ctf_strptr(fp, hp.cth_parlabel); if (hp.cth_parname != 0) fp->ctf_parname = ctf_strptr(fp, hp.cth_parname); ctf_dprintf("ctf_bufopen: parent name %s (label %s)\n", fp->ctf_parname ? fp->ctf_parname : "", fp->ctf_parlabel ? fp->ctf_parlabel : ""); /* * If we have a symbol table section, allocate and initialize * the symtab translation table, pointed to by ctf_sxlate. */ if (symsect != NULL) { fp->ctf_nsyms = symsect->cts_size / symsect->cts_entsize; fp->ctf_sxlate = ctf_alloc(fp->ctf_nsyms * sizeof (uint_t)); if (fp->ctf_sxlate == NULL) { (void) ctf_set_open_errno(errp, EAGAIN); goto bad; } if ((err = init_symtab(fp, &hp, symsect, strsect)) != 0) { (void) ctf_set_open_errno(errp, err); goto bad; } } if ((err = init_types(fp, &hp)) != 0) { (void) ctf_set_open_errno(errp, err); goto bad; } /* * Initialize the ctf_lookup_by_name top-level dictionary. We keep an * array of type name prefixes and the corresponding ctf_hash to use. * NOTE: This code must be kept in sync with the code in ctf_update(). */ fp->ctf_lookups[0].ctl_prefix = "struct"; fp->ctf_lookups[0].ctl_len = strlen(fp->ctf_lookups[0].ctl_prefix); fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs; fp->ctf_lookups[1].ctl_prefix = "union"; fp->ctf_lookups[1].ctl_len = strlen(fp->ctf_lookups[1].ctl_prefix); fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions; fp->ctf_lookups[2].ctl_prefix = "enum"; fp->ctf_lookups[2].ctl_len = strlen(fp->ctf_lookups[2].ctl_prefix); fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums; fp->ctf_lookups[3].ctl_prefix = _CTF_NULLSTR; fp->ctf_lookups[3].ctl_len = strlen(fp->ctf_lookups[3].ctl_prefix); fp->ctf_lookups[3].ctl_hash = &fp->ctf_names; fp->ctf_lookups[4].ctl_prefix = NULL; fp->ctf_lookups[4].ctl_len = 0; fp->ctf_lookups[4].ctl_hash = NULL; if (symsect != NULL) { if (symsect->cts_entsize == sizeof (Elf64_Sym)) (void) ctf_setmodel(fp, CTF_MODEL_LP64); else (void) ctf_setmodel(fp, CTF_MODEL_ILP32); } else (void) ctf_setmodel(fp, CTF_MODEL_NATIVE); fp->ctf_refcnt = 1; return (fp); bad: ctf_close(fp); + return (NULL); +} + +/* + * Dupliate a ctf_file_t and its underlying section information into a new + * container. This works by copying the three ctf_sect_t's of the original + * container if they exist and passing those into ctf_bufopen. To copy those, we + * mmap anonymous memory with ctf_data_alloc and bcopy the data across. It's not + * the cheapest thing, but it's what we've got. + */ +ctf_file_t * +ctf_dup(ctf_file_t *ofp) +{ + ctf_file_t *fp; + ctf_sect_t ctfsect, symsect, strsect; + ctf_sect_t *ctp, *symp, *strp; + void *cbuf, *symbuf, *strbuf; + int err; + + cbuf = symbuf = strbuf = NULL; + /* + * The ctfsect isn't allowed to not exist, but the symbol and string + * section might not. We only need to copy the data of the section, not + * the name, as ctf_bufopen will take care of that. + */ + bcopy(&ofp->ctf_data, &ctfsect, sizeof (ctf_sect_t)); + cbuf = ctf_data_alloc(ctfsect.cts_size); + if (cbuf == NULL) { + (void) ctf_set_errno(ofp, ECTF_MMAP); + return (NULL); + } + + bcopy(ctfsect.cts_data, cbuf, ctfsect.cts_size); + ctf_data_protect(cbuf, ctfsect.cts_size); + ctfsect.cts_data = cbuf; + ctfsect.cts_offset = 0; + ctp = &ctfsect; + + if (ofp->ctf_symtab.cts_data != NULL) { + bcopy(&ofp->ctf_symtab, &symsect, sizeof (ctf_sect_t)); + symbuf = ctf_data_alloc(symsect.cts_size); + if (symbuf == NULL) { + (void) ctf_set_errno(ofp, ECTF_MMAP); + goto err; + } + bcopy(symsect.cts_data, symbuf, symsect.cts_size); + ctf_data_protect(symbuf, symsect.cts_size); + symsect.cts_data = symbuf; + symsect.cts_offset = 0; + symp = &symsect; + } else { + symp = NULL; + } + + if (ofp->ctf_strtab.cts_data != NULL) { + bcopy(&ofp->ctf_strtab, &strsect, sizeof (ctf_sect_t)); + strbuf = ctf_data_alloc(strsect.cts_size); + if (strbuf == NULL) { + (void) ctf_set_errno(ofp, ECTF_MMAP); + goto err; + } + bcopy(strsect.cts_data, strbuf, strsect.cts_size); + ctf_data_protect(strbuf, strsect.cts_size); + strsect.cts_data = strbuf; + strsect.cts_offset = 0; + strp = &strsect; + } else { + strp = NULL; + } + + fp = ctf_bufopen(ctp, symp, strp, &err); + if (fp == NULL) { + (void) ctf_set_errno(ofp, err); + goto err; + } + + fp->ctf_flags |= LCTF_MMAP; + + return (fp); + +err: + ctf_data_free(cbuf, ctfsect.cts_size); + if (symbuf != NULL) + ctf_data_free(symbuf, symsect.cts_size); + if (strbuf != NULL) + ctf_data_free(strbuf, strsect.cts_size); return (NULL); } /* * Close the specified CTF container and free associated data structures. Note * that ctf_close() is a reference counted operation: if the specified file is * the parent of other active containers, its reference count will be greater * than one and it will be freed later when no active children exist. */ void ctf_close(ctf_file_t *fp) { ctf_dtdef_t *dtd, *ntd; if (fp == NULL) return; /* allow ctf_close(NULL) to simplify caller code */ ctf_dprintf("ctf_close(%p) refcnt=%u\n", (void *)fp, fp->ctf_refcnt); if (fp->ctf_refcnt > 1) { fp->ctf_refcnt--; return; } if (fp->ctf_parent != NULL) ctf_close(fp->ctf_parent); /* * Note, to work properly with reference counting on the dynamic * section, we must delete the list in reverse. */ for (dtd = ctf_list_prev(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { ntd = ctf_list_prev(dtd); ctf_dtd_delete(fp, dtd); } ctf_free(fp->ctf_dthash, fp->ctf_dthashlen * sizeof (ctf_dtdef_t *)); if (fp->ctf_flags & LCTF_MMAP) { if (fp->ctf_data.cts_data != NULL) ctf_sect_munmap(&fp->ctf_data); if (fp->ctf_symtab.cts_data != NULL) ctf_sect_munmap(&fp->ctf_symtab); if (fp->ctf_strtab.cts_data != NULL) ctf_sect_munmap(&fp->ctf_strtab); } if (fp->ctf_data.cts_name != _CTF_NULLSTR && fp->ctf_data.cts_name != NULL) { ctf_free((char *)fp->ctf_data.cts_name, strlen(fp->ctf_data.cts_name) + 1); } if (fp->ctf_symtab.cts_name != _CTF_NULLSTR && fp->ctf_symtab.cts_name != NULL) { ctf_free((char *)fp->ctf_symtab.cts_name, strlen(fp->ctf_symtab.cts_name) + 1); } if (fp->ctf_strtab.cts_name != _CTF_NULLSTR && fp->ctf_strtab.cts_name != NULL) { ctf_free((char *)fp->ctf_strtab.cts_name, strlen(fp->ctf_strtab.cts_name) + 1); } if (fp->ctf_base != fp->ctf_data.cts_data && fp->ctf_base != NULL) ctf_data_free((void *)fp->ctf_base, fp->ctf_size); if (fp->ctf_sxlate != NULL) ctf_free(fp->ctf_sxlate, sizeof (uint_t) * fp->ctf_nsyms); if (fp->ctf_txlate != NULL) { ctf_free(fp->ctf_txlate, sizeof (uint_t) * (fp->ctf_typemax + 1)); } if (fp->ctf_ptrtab != NULL) { ctf_free(fp->ctf_ptrtab, sizeof (ushort_t) * (fp->ctf_typemax + 1)); } ctf_hash_destroy(&fp->ctf_structs); ctf_hash_destroy(&fp->ctf_unions); ctf_hash_destroy(&fp->ctf_enums); ctf_hash_destroy(&fp->ctf_names); ctf_free(fp, sizeof (ctf_file_t)); } /* * Return the CTF handle for the parent CTF container, if one exists. * Otherwise return NULL to indicate this container has no imported parent. */ ctf_file_t * ctf_parent_file(ctf_file_t *fp) { return (fp->ctf_parent); } /* * Return the name of the parent CTF container, if one exists. Otherwise * return NULL to indicate this container is a root container. */ const char * ctf_parent_name(ctf_file_t *fp) { return (fp->ctf_parname); } /* * Import the types from the specified parent container by storing a pointer * to it in ctf_parent and incrementing its reference count. Only one parent * is allowed: if a parent already exists, it is replaced by the new parent. */ int ctf_import(ctf_file_t *fp, ctf_file_t *pfp) { if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0)) return (ctf_set_errno(fp, EINVAL)); if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel) return (ctf_set_errno(fp, ECTF_DMODEL)); if (fp->ctf_parent != NULL) ctf_close(fp->ctf_parent); if (pfp != NULL) { fp->ctf_flags |= LCTF_CHILD; pfp->ctf_refcnt++; } fp->ctf_parent = pfp; return (0); } /* * Set the data model constant for the CTF container. */ int ctf_setmodel(ctf_file_t *fp, int model) { const ctf_dmodel_t *dp; for (dp = _libctf_models; dp->ctd_name != NULL; dp++) { if (dp->ctd_code == model) { fp->ctf_dmodel = dp; return (0); } } return (ctf_set_errno(fp, EINVAL)); } /* * Return the data model constant for the CTF container. */ int ctf_getmodel(ctf_file_t *fp) { return (fp->ctf_dmodel->ctd_code); } void ctf_setspecific(ctf_file_t *fp, void *data) { fp->ctf_specific = data; } void * ctf_getspecific(ctf_file_t *fp) { return (fp->ctf_specific); } Index: stable/10/cddl/contrib/opensolaris/common/ctf/ctf_types.c =================================================================== --- stable/10/cddl/contrib/opensolaris/common/ctf/ctf_types.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/common/ctf/ctf_types.c (revision 268578) @@ -1,845 +1,870 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include ssize_t ctf_get_ctt_size(const ctf_file_t *fp, const ctf_type_t *tp, ssize_t *sizep, ssize_t *incrementp) { ssize_t size, increment; if (fp->ctf_version > CTF_VERSION_1 && tp->ctt_size == CTF_LSIZE_SENT) { size = CTF_TYPE_LSIZE(tp); increment = sizeof (ctf_type_t); } else { size = tp->ctt_size; increment = sizeof (ctf_stype_t); } if (sizep) *sizep = size; if (incrementp) *incrementp = increment; return (size); } /* * Iterate over the members of a STRUCT or UNION. We pass the name, member * type, and offset of each member to the specified callback function. */ int ctf_member_iter(ctf_file_t *fp, ctf_id_t type, ctf_member_f *func, void *arg) { ctf_file_t *ofp = fp; const ctf_type_t *tp; ssize_t size, increment; uint_t kind, n; int rc; if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ (void) ctf_get_ctt_size(fp, tp, &size, &increment); kind = LCTF_INFO_KIND(fp, tp->ctt_info); if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) return (ctf_set_errno(ofp, ECTF_NOTSOU)); if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) { const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) { const char *name = ctf_strptr(fp, mp->ctm_name); if ((rc = func(name, mp->ctm_type, mp->ctm_offset, arg)) != 0) return (rc); } } else { const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) { const char *name = ctf_strptr(fp, lmp->ctlm_name); if ((rc = func(name, lmp->ctlm_type, (ulong_t)CTF_LMEM_OFFSET(lmp), arg)) != 0) return (rc); } } return (0); } /* * Iterate over the members of an ENUM. We pass the string name and associated * integer value of each enum element to the specified callback function. */ int ctf_enum_iter(ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg) { ctf_file_t *ofp = fp; const ctf_type_t *tp; const ctf_enum_t *ep; ssize_t increment; uint_t n; int rc; if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) return (ctf_set_errno(ofp, ECTF_NOTENUM)); (void) ctf_get_ctt_size(fp, tp, NULL, &increment); ep = (const ctf_enum_t *)((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) { const char *name = ctf_strptr(fp, ep->cte_name); if ((rc = func(name, ep->cte_value, arg)) != 0) return (rc); } return (0); } /* * Iterate over every root (user-visible) type in the given CTF container. * We pass the type ID of each type to the specified callback function. */ int ctf_type_iter(ctf_file_t *fp, ctf_type_f *func, void *arg) { ctf_id_t id, max = fp->ctf_typemax; int rc, child = (fp->ctf_flags & LCTF_CHILD); for (id = 1; id <= max; id++) { const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR(fp, id); if (CTF_INFO_ISROOT(tp->ctt_info) && (rc = func(CTF_INDEX_TO_TYPE(id, child), arg)) != 0) return (rc); } return (0); } /* * Follow a given type through the graph for TYPEDEF, VOLATILE, CONST, and * RESTRICT nodes until we reach a "base" type node. This is useful when * we want to follow a type ID to a node that has members or a size. To guard * against infinite loops, we implement simplified cycle detection and check * each link against itself, the previous node, and the topmost node. */ ctf_id_t ctf_type_resolve(ctf_file_t *fp, ctf_id_t type) { ctf_id_t prev = type, otype = type; ctf_file_t *ofp = fp; const ctf_type_t *tp; while ((tp = ctf_lookup_by_id(&fp, type)) != NULL) { switch (LCTF_INFO_KIND(fp, tp->ctt_info)) { case CTF_K_TYPEDEF: case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: if (tp->ctt_type == type || tp->ctt_type == otype || tp->ctt_type == prev) { ctf_dprintf("type %ld cycle detected\n", otype); return (ctf_set_errno(ofp, ECTF_CORRUPT)); } prev = type; type = tp->ctt_type; break; default: return (type); } } return (CTF_ERR); /* errno is set for us */ } /* * Lookup the given type ID and print a string name for it into buf. Return * the actual number of bytes (not including \0) needed to format the name. */ -ssize_t -ctf_type_lname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len) +static ssize_t +ctf_type_qlname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len, + const char *qname) { ctf_decl_t cd; ctf_decl_node_t *cdp; ctf_decl_prec_t prec, lp, rp; int ptr, arr; uint_t k; if (fp == NULL && type == CTF_ERR) return (-1); /* simplify caller code by permitting CTF_ERR */ ctf_decl_init(&cd, buf, len); ctf_decl_push(&cd, fp, type); if (cd.cd_err != 0) { ctf_decl_fini(&cd); return (ctf_set_errno(fp, cd.cd_err)); } /* * If the type graph's order conflicts with lexical precedence order * for pointers or arrays, then we need to surround the declarations at * the corresponding lexical precedence with parentheses. This can * result in either a parenthesized pointer (*) as in int (*)() or * int (*)[], or in a parenthesized pointer and array as in int (*[])(). */ ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER; arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY; rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1; lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1; k = CTF_K_POINTER; /* avoid leading whitespace (see below) */ for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) { for (cdp = ctf_list_next(&cd.cd_nodes[prec]); cdp != NULL; cdp = ctf_list_next(cdp)) { ctf_file_t *rfp = fp; const ctf_type_t *tp = ctf_lookup_by_id(&rfp, cdp->cd_type); const char *name = ctf_strptr(rfp, tp->ctt_name); if (k != CTF_K_POINTER && k != CTF_K_ARRAY) ctf_decl_sprintf(&cd, " "); if (lp == prec) { ctf_decl_sprintf(&cd, "("); lp = -1; } switch (cdp->cd_kind) { case CTF_K_INTEGER: case CTF_K_FLOAT: case CTF_K_TYPEDEF: + if (qname != NULL) + ctf_decl_sprintf(&cd, "%s`", qname); ctf_decl_sprintf(&cd, "%s", name); break; case CTF_K_POINTER: ctf_decl_sprintf(&cd, "*"); break; case CTF_K_ARRAY: ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n); break; case CTF_K_FUNCTION: ctf_decl_sprintf(&cd, "()"); break; case CTF_K_STRUCT: case CTF_K_FORWARD: - ctf_decl_sprintf(&cd, "struct %s", name); + ctf_decl_sprintf(&cd, "struct "); + if (qname != NULL) + ctf_decl_sprintf(&cd, "%s`", qname); + ctf_decl_sprintf(&cd, "%s", name); break; case CTF_K_UNION: - ctf_decl_sprintf(&cd, "union %s", name); + ctf_decl_sprintf(&cd, "union "); + if (qname != NULL) + ctf_decl_sprintf(&cd, "%s`", qname); + ctf_decl_sprintf(&cd, "%s", name); break; case CTF_K_ENUM: - ctf_decl_sprintf(&cd, "enum %s", name); + ctf_decl_sprintf(&cd, "enum "); + if (qname != NULL) + ctf_decl_sprintf(&cd, "%s`", qname); + ctf_decl_sprintf(&cd, "%s", name); break; case CTF_K_VOLATILE: ctf_decl_sprintf(&cd, "volatile"); break; case CTF_K_CONST: ctf_decl_sprintf(&cd, "const"); break; case CTF_K_RESTRICT: ctf_decl_sprintf(&cd, "restrict"); break; } k = cdp->cd_kind; } if (rp == prec) ctf_decl_sprintf(&cd, ")"); } if (cd.cd_len >= len) (void) ctf_set_errno(fp, ECTF_NAMELEN); ctf_decl_fini(&cd); return (cd.cd_len); } +ssize_t +ctf_type_lname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len) +{ + return (ctf_type_qlname(fp, type, buf, len, NULL)); +} + /* * Lookup the given type ID and print a string name for it into buf. If buf * is too small, return NULL: the ECTF_NAMELEN error is set on 'fp' for us. */ char * ctf_type_name(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len) { - ssize_t rv = ctf_type_lname(fp, type, buf, len); + ssize_t rv = ctf_type_qlname(fp, type, buf, len, NULL); return (rv >= 0 && rv < len ? buf : NULL); } + +char * +ctf_type_qname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len, + const char *qname) +{ + ssize_t rv = ctf_type_qlname(fp, type, buf, len, qname); + return (rv >= 0 && rv < len ? buf : NULL); +} + /* * Resolve the type down to a base type node, and then return the size * of the type storage in bytes. */ ssize_t ctf_type_size(ctf_file_t *fp, ctf_id_t type) { const ctf_type_t *tp; ssize_t size; ctf_arinfo_t ar; if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (-1); /* errno is set for us */ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (-1); /* errno is set for us */ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) { case CTF_K_POINTER: return (fp->ctf_dmodel->ctd_pointer); case CTF_K_FUNCTION: return (0); /* function size is only known by symtab */ case CTF_K_ENUM: return (fp->ctf_dmodel->ctd_int); case CTF_K_ARRAY: /* * Array size is not directly returned by stabs data. Instead, * it defines the element type and requires the user to perform * the multiplication. If ctf_get_ctt_size() returns zero, the * current version of ctfconvert does not compute member sizes * and we compute the size here on its behalf. */ if ((size = ctf_get_ctt_size(fp, tp, NULL, NULL)) > 0) return (size); if (ctf_array_info(fp, type, &ar) == CTF_ERR || (size = ctf_type_size(fp, ar.ctr_contents)) == CTF_ERR) return (-1); /* errno is set for us */ return (size * ar.ctr_nelems); default: return (ctf_get_ctt_size(fp, tp, NULL, NULL)); } } /* * Resolve the type down to a base type node, and then return the alignment * needed for the type storage in bytes. */ ssize_t ctf_type_align(ctf_file_t *fp, ctf_id_t type) { const ctf_type_t *tp; ctf_arinfo_t r; if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (-1); /* errno is set for us */ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (-1); /* errno is set for us */ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) { case CTF_K_POINTER: case CTF_K_FUNCTION: return (fp->ctf_dmodel->ctd_pointer); case CTF_K_ARRAY: if (ctf_array_info(fp, type, &r) == CTF_ERR) return (-1); /* errno is set for us */ return (ctf_type_align(fp, r.ctr_contents)); case CTF_K_STRUCT: case CTF_K_UNION: { uint_t n = LCTF_INFO_VLEN(fp, tp->ctt_info); ssize_t size, increment; size_t align = 0; const void *vmp; (void) ctf_get_ctt_size(fp, tp, &size, &increment); vmp = (uchar_t *)tp + increment; if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_STRUCT) n = MIN(n, 1); /* only use first member for structs */ if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) { const ctf_member_t *mp = vmp; for (; n != 0; n--, mp++) { ssize_t am = ctf_type_align(fp, mp->ctm_type); align = MAX(align, am); } } else { const ctf_lmember_t *lmp = vmp; for (; n != 0; n--, lmp++) { ssize_t am = ctf_type_align(fp, lmp->ctlm_type); align = MAX(align, am); } } return (align); } case CTF_K_ENUM: return (fp->ctf_dmodel->ctd_int); default: return (ctf_get_ctt_size(fp, tp, NULL, NULL)); } } /* * Return the kind (CTF_K_* constant) for the specified type ID. */ int ctf_type_kind(ctf_file_t *fp, ctf_id_t type) { const ctf_type_t *tp; if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ return (LCTF_INFO_KIND(fp, tp->ctt_info)); } /* * If the type is one that directly references another type (such as POINTER), * then return the ID of the type to which it refers. */ ctf_id_t ctf_type_reference(ctf_file_t *fp, ctf_id_t type) { ctf_file_t *ofp = fp; const ctf_type_t *tp; if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) { case CTF_K_POINTER: case CTF_K_TYPEDEF: case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: return (tp->ctt_type); default: return (ctf_set_errno(ofp, ECTF_NOTREF)); } } /* * Find a pointer to type by looking in fp->ctf_ptrtab. If we can't find a * pointer to the given type, see if we can compute a pointer to the type * resulting from resolving the type down to its base type and use that * instead. This helps with cases where the CTF data includes "struct foo *" * but not "foo_t *" and the user accesses "foo_t *" in the debugger. */ ctf_id_t ctf_type_pointer(ctf_file_t *fp, ctf_id_t type) { ctf_file_t *ofp = fp; ctf_id_t ntype; if (ctf_lookup_by_id(&fp, type) == NULL) return (CTF_ERR); /* errno is set for us */ if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0) return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD))); if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (ctf_set_errno(ofp, ECTF_NOTYPE)); if (ctf_lookup_by_id(&fp, type) == NULL) return (ctf_set_errno(ofp, ECTF_NOTYPE)); if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0) return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD))); return (ctf_set_errno(ofp, ECTF_NOTYPE)); } /* * Return the encoding for the specified INTEGER or FLOAT. */ int ctf_type_encoding(ctf_file_t *fp, ctf_id_t type, ctf_encoding_t *ep) { ctf_file_t *ofp = fp; const ctf_type_t *tp; ssize_t increment; uint_t data; if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ (void) ctf_get_ctt_size(fp, tp, NULL, &increment); switch (LCTF_INFO_KIND(fp, tp->ctt_info)) { case CTF_K_INTEGER: data = *(const uint_t *)((uintptr_t)tp + increment); ep->cte_format = CTF_INT_ENCODING(data); ep->cte_offset = CTF_INT_OFFSET(data); ep->cte_bits = CTF_INT_BITS(data); break; case CTF_K_FLOAT: data = *(const uint_t *)((uintptr_t)tp + increment); ep->cte_format = CTF_FP_ENCODING(data); ep->cte_offset = CTF_FP_OFFSET(data); ep->cte_bits = CTF_FP_BITS(data); break; default: return (ctf_set_errno(ofp, ECTF_NOTINTFP)); } return (0); } int ctf_type_cmp(ctf_file_t *lfp, ctf_id_t ltype, ctf_file_t *rfp, ctf_id_t rtype) { int rval; if (ltype < rtype) rval = -1; else if (ltype > rtype) rval = 1; else rval = 0; if (lfp == rfp) return (rval); if (CTF_TYPE_ISPARENT(ltype) && lfp->ctf_parent != NULL) lfp = lfp->ctf_parent; if (CTF_TYPE_ISPARENT(rtype) && rfp->ctf_parent != NULL) rfp = rfp->ctf_parent; if (lfp < rfp) return (-1); if (lfp > rfp) return (1); return (rval); } /* * Return a boolean value indicating if two types are compatible integers or * floating-pointer values. This function returns true if the two types are * the same, or if they have the same ASCII name and encoding properties. * This function could be extended to test for compatibility for other kinds. */ int ctf_type_compat(ctf_file_t *lfp, ctf_id_t ltype, ctf_file_t *rfp, ctf_id_t rtype) { const ctf_type_t *ltp, *rtp; ctf_encoding_t le, re; ctf_arinfo_t la, ra; uint_t lkind, rkind; if (ctf_type_cmp(lfp, ltype, rfp, rtype) == 0) return (1); ltype = ctf_type_resolve(lfp, ltype); lkind = ctf_type_kind(lfp, ltype); rtype = ctf_type_resolve(rfp, rtype); rkind = ctf_type_kind(rfp, rtype); if (lkind != rkind || (ltp = ctf_lookup_by_id(&lfp, ltype)) == NULL || (rtp = ctf_lookup_by_id(&rfp, rtype)) == NULL || strcmp(ctf_strptr(lfp, ltp->ctt_name), ctf_strptr(rfp, rtp->ctt_name)) != 0) return (0); switch (lkind) { case CTF_K_INTEGER: case CTF_K_FLOAT: return (ctf_type_encoding(lfp, ltype, &le) == 0 && ctf_type_encoding(rfp, rtype, &re) == 0 && bcmp(&le, &re, sizeof (ctf_encoding_t)) == 0); case CTF_K_POINTER: return (ctf_type_compat(lfp, ctf_type_reference(lfp, ltype), rfp, ctf_type_reference(rfp, rtype))); case CTF_K_ARRAY: return (ctf_array_info(lfp, ltype, &la) == 0 && ctf_array_info(rfp, rtype, &ra) == 0 && la.ctr_nelems == ra.ctr_nelems && ctf_type_compat( lfp, la.ctr_contents, rfp, ra.ctr_contents) && ctf_type_compat(lfp, la.ctr_index, rfp, ra.ctr_index)); case CTF_K_STRUCT: case CTF_K_UNION: return (ctf_type_size(lfp, ltype) == ctf_type_size(rfp, rtype)); case CTF_K_ENUM: case CTF_K_FORWARD: return (1); /* no other checks required for these type kinds */ default: return (0); /* should not get here since we did a resolve */ } } /* * Return the type and offset for a given member of a STRUCT or UNION. */ int ctf_member_info(ctf_file_t *fp, ctf_id_t type, const char *name, ctf_membinfo_t *mip) { ctf_file_t *ofp = fp; const ctf_type_t *tp; ssize_t size, increment; uint_t kind, n; if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ (void) ctf_get_ctt_size(fp, tp, &size, &increment); kind = LCTF_INFO_KIND(fp, tp->ctt_info); if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) return (ctf_set_errno(ofp, ECTF_NOTSOU)); if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) { const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) { if (strcmp(ctf_strptr(fp, mp->ctm_name), name) == 0) { mip->ctm_type = mp->ctm_type; mip->ctm_offset = mp->ctm_offset; return (0); } } } else { const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) { if (strcmp(ctf_strptr(fp, lmp->ctlm_name), name) == 0) { mip->ctm_type = lmp->ctlm_type; mip->ctm_offset = (ulong_t)CTF_LMEM_OFFSET(lmp); return (0); } } } return (ctf_set_errno(ofp, ECTF_NOMEMBNAM)); } /* * Return the array type, index, and size information for the specified ARRAY. */ int ctf_array_info(ctf_file_t *fp, ctf_id_t type, ctf_arinfo_t *arp) { ctf_file_t *ofp = fp; const ctf_type_t *tp; const ctf_array_t *ap; ssize_t increment; if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ARRAY) return (ctf_set_errno(ofp, ECTF_NOTARRAY)); (void) ctf_get_ctt_size(fp, tp, NULL, &increment); ap = (const ctf_array_t *)((uintptr_t)tp + increment); arp->ctr_contents = ap->cta_contents; arp->ctr_index = ap->cta_index; arp->ctr_nelems = ap->cta_nelems; return (0); } /* * Convert the specified value to the corresponding enum member name, if a * matching name can be found. Otherwise NULL is returned. */ const char * ctf_enum_name(ctf_file_t *fp, ctf_id_t type, int value) { ctf_file_t *ofp = fp; const ctf_type_t *tp; const ctf_enum_t *ep; ssize_t increment; uint_t n; if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (NULL); /* errno is set for us */ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (NULL); /* errno is set for us */ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) { (void) ctf_set_errno(ofp, ECTF_NOTENUM); return (NULL); } (void) ctf_get_ctt_size(fp, tp, NULL, &increment); ep = (const ctf_enum_t *)((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) { if (ep->cte_value == value) return (ctf_strptr(fp, ep->cte_name)); } (void) ctf_set_errno(ofp, ECTF_NOENUMNAM); return (NULL); } /* * Convert the specified enum tag name to the corresponding value, if a * matching name can be found. Otherwise CTF_ERR is returned. */ int ctf_enum_value(ctf_file_t *fp, ctf_id_t type, const char *name, int *valp) { ctf_file_t *ofp = fp; const ctf_type_t *tp; const ctf_enum_t *ep; ssize_t size, increment; uint_t n; if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) { (void) ctf_set_errno(ofp, ECTF_NOTENUM); return (CTF_ERR); } (void) ctf_get_ctt_size(fp, tp, &size, &increment); ep = (const ctf_enum_t *)((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) { if (strcmp(ctf_strptr(fp, ep->cte_name), name) == 0) { if (valp != NULL) *valp = ep->cte_value; return (0); } } (void) ctf_set_errno(ofp, ECTF_NOENUMNAM); return (CTF_ERR); } /* * Recursively visit the members of any type. This function is used as the * engine for ctf_type_visit, below. We resolve the input type, recursively * invoke ourself for each type member if the type is a struct or union, and * then invoke the callback function on the current type. If any callback * returns non-zero, we abort and percolate the error code back up to the top. */ static int ctf_type_rvisit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg, const char *name, ulong_t offset, int depth) { ctf_id_t otype = type; const ctf_type_t *tp; ssize_t size, increment; uint_t kind, n; int rc; if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) return (CTF_ERR); /* errno is set for us */ if ((rc = func(name, otype, offset, depth, arg)) != 0) return (rc); kind = LCTF_INFO_KIND(fp, tp->ctt_info); if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) return (0); (void) ctf_get_ctt_size(fp, tp, &size, &increment); if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) { const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) { if ((rc = ctf_type_rvisit(fp, mp->ctm_type, func, arg, ctf_strptr(fp, mp->ctm_name), offset + mp->ctm_offset, depth + 1)) != 0) return (rc); } } else { const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t)tp + increment); for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) { if ((rc = ctf_type_rvisit(fp, lmp->ctlm_type, func, arg, ctf_strptr(fp, lmp->ctlm_name), offset + (ulong_t)CTF_LMEM_OFFSET(lmp), depth + 1)) != 0) return (rc); } } return (0); } /* * Recursively visit the members of any type. We pass the name, member * type, and offset of each member to the specified callback function. */ int ctf_type_visit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg) { return (ctf_type_rvisit(fp, type, func, arg, "", 0, 0)); } Index: stable/10/cddl/contrib/opensolaris/common/util/strtolctype.h =================================================================== --- stable/10/cddl/contrib/opensolaris/common/util/strtolctype.h (nonexistent) +++ stable/10/cddl/contrib/opensolaris/common/util/strtolctype.h (revision 268578) @@ -0,0 +1,79 @@ +/* + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1988 AT&T */ +/* All Rights Reserved */ + +#ifndef _COMMON_UTIL_CTYPE_H +#define _COMMON_UTIL_CTYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This header file contains a collection of macros that the strtou?ll? + * functions in common/util use to test characters. What we need is a kernel + * version of ctype.h. + * + * NOTE: These macros are used within several DTrace probe context functions. + * They must not be altered to make function calls or perform actions not + * safe in probe context. + */ + +#if defined(sun) && (defined(_KERNEL) || defined(_BOOT)) + +#define isalnum(ch) (isalpha(ch) || isdigit(ch)) +#define isalpha(ch) (isupper(ch) || islower(ch)) +#define isdigit(ch) ((ch) >= '0' && (ch) <= '9') +#define islower(ch) ((ch) >= 'a' && (ch) <= 'z') +#define isspace(ch) (((ch) == ' ') || ((ch) == '\r') || ((ch) == '\n') || \ + ((ch) == '\t') || ((ch) == '\f')) +#define isupper(ch) ((ch) >= 'A' && (ch) <= 'Z') +#define isxdigit(ch) (isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \ + ((ch) >= 'A' && (ch) <= 'F')) + +#endif /* _KERNEL || _BOOT */ + +#define DIGIT(x) \ + (isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A') + +#define MBASE ('z' - 'a' + 1 + 10) + +/* + * The following macro is a version of isalnum() that limits alphabetic + * characters to the ranges a-z and A-Z; locale dependent characters will not + * return 1. The members of a-z and A-Z are assumed to be in ascending order + * and contiguous. + */ +#define lisalnum(x) \ + (isdigit(x) || ((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z')) + +#ifdef __cplusplus +} +#endif + +#endif /* _COMMON_UTIL_CTYPE_H */ Property changes on: stable/10/cddl/contrib/opensolaris/common/util/strtolctype.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c (revision 268578) @@ -1,1948 +1,2198 @@ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* - * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include #if defined(sun) #include #else #include #include #endif #include #define DTRACE_AHASHSIZE 32779 /* big 'ol prime */ /* * Because qsort(3C) does not allow an argument to be passed to a comparison * function, the variables that affect comparison must regrettably be global; * they are protected by a global static lock, dt_qsort_lock. */ static pthread_mutex_t dt_qsort_lock = PTHREAD_MUTEX_INITIALIZER; static int dt_revsort; static int dt_keysort; static int dt_keypos; #define DT_LESSTHAN (dt_revsort == 0 ? -1 : 1) #define DT_GREATERTHAN (dt_revsort == 0 ? 1 : -1) static void dt_aggregate_count(int64_t *existing, int64_t *new, size_t size) { uint_t i; for (i = 0; i < size / sizeof (int64_t); i++) existing[i] = existing[i] + new[i]; } static int dt_aggregate_countcmp(int64_t *lhs, int64_t *rhs) { int64_t lvar = *lhs; int64_t rvar = *rhs; if (lvar < rvar) return (DT_LESSTHAN); if (lvar > rvar) return (DT_GREATERTHAN); return (0); } /*ARGSUSED*/ static void dt_aggregate_min(int64_t *existing, int64_t *new, size_t size) { if (*new < *existing) *existing = *new; } /*ARGSUSED*/ static void dt_aggregate_max(int64_t *existing, int64_t *new, size_t size) { if (*new > *existing) *existing = *new; } static int dt_aggregate_averagecmp(int64_t *lhs, int64_t *rhs) { int64_t lavg = lhs[0] ? (lhs[1] / lhs[0]) : 0; int64_t ravg = rhs[0] ? (rhs[1] / rhs[0]) : 0; if (lavg < ravg) return (DT_LESSTHAN); if (lavg > ravg) return (DT_GREATERTHAN); return (0); } static int dt_aggregate_stddevcmp(int64_t *lhs, int64_t *rhs) { uint64_t lsd = dt_stddev((uint64_t *)lhs, 1); uint64_t rsd = dt_stddev((uint64_t *)rhs, 1); if (lsd < rsd) return (DT_LESSTHAN); if (lsd > rsd) return (DT_GREATERTHAN); return (0); } /*ARGSUSED*/ static void dt_aggregate_lquantize(int64_t *existing, int64_t *new, size_t size) { int64_t arg = *existing++; uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg); int i; for (i = 0; i <= levels + 1; i++) existing[i] = existing[i] + new[i + 1]; } static long double dt_aggregate_lquantizedsum(int64_t *lquanta) { int64_t arg = *lquanta++; int32_t base = DTRACE_LQUANTIZE_BASE(arg); uint16_t step = DTRACE_LQUANTIZE_STEP(arg); uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg), i; long double total = (long double)lquanta[0] * (long double)(base - 1); for (i = 0; i < levels; base += step, i++) total += (long double)lquanta[i + 1] * (long double)base; return (total + (long double)lquanta[levels + 1] * (long double)(base + 1)); } static int64_t dt_aggregate_lquantizedzero(int64_t *lquanta) { int64_t arg = *lquanta++; int32_t base = DTRACE_LQUANTIZE_BASE(arg); uint16_t step = DTRACE_LQUANTIZE_STEP(arg); uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg), i; if (base - 1 == 0) return (lquanta[0]); for (i = 0; i < levels; base += step, i++) { if (base != 0) continue; return (lquanta[i + 1]); } if (base + 1 == 0) return (lquanta[levels + 1]); return (0); } static int dt_aggregate_lquantizedcmp(int64_t *lhs, int64_t *rhs) { long double lsum = dt_aggregate_lquantizedsum(lhs); long double rsum = dt_aggregate_lquantizedsum(rhs); int64_t lzero, rzero; if (lsum < rsum) return (DT_LESSTHAN); if (lsum > rsum) return (DT_GREATERTHAN); /* * If they're both equal, then we will compare based on the weights at * zero. If the weights at zero are equal (or if zero is not within * the range of the linear quantization), then this will be judged a * tie and will be resolved based on the key comparison. */ lzero = dt_aggregate_lquantizedzero(lhs); rzero = dt_aggregate_lquantizedzero(rhs); if (lzero < rzero) return (DT_LESSTHAN); if (lzero > rzero) return (DT_GREATERTHAN); return (0); } static void dt_aggregate_llquantize(int64_t *existing, int64_t *new, size_t size) { int i; for (i = 1; i < size / sizeof (int64_t); i++) existing[i] = existing[i] + new[i]; } static long double dt_aggregate_llquantizedsum(int64_t *llquanta) { int64_t arg = *llquanta++; uint16_t factor = DTRACE_LLQUANTIZE_FACTOR(arg); uint16_t low = DTRACE_LLQUANTIZE_LOW(arg); uint16_t high = DTRACE_LLQUANTIZE_HIGH(arg); uint16_t nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); int bin = 0, order; int64_t value = 1, next, step; long double total; assert(nsteps >= factor); assert(nsteps % factor == 0); for (order = 0; order < low; order++) value *= factor; total = (long double)llquanta[bin++] * (long double)(value - 1); next = value * factor; step = next > nsteps ? next / nsteps : 1; while (order <= high) { assert(value < next); total += (long double)llquanta[bin++] * (long double)(value); if ((value += step) != next) continue; next = value * factor; step = next > nsteps ? next / nsteps : 1; order++; } return (total + (long double)llquanta[bin] * (long double)value); } static int dt_aggregate_llquantizedcmp(int64_t *lhs, int64_t *rhs) { long double lsum = dt_aggregate_llquantizedsum(lhs); long double rsum = dt_aggregate_llquantizedsum(rhs); int64_t lzero, rzero; if (lsum < rsum) return (DT_LESSTHAN); if (lsum > rsum) return (DT_GREATERTHAN); /* * If they're both equal, then we will compare based on the weights at * zero. If the weights at zero are equal, then this will be judged a * tie and will be resolved based on the key comparison. */ lzero = lhs[1]; rzero = rhs[1]; if (lzero < rzero) return (DT_LESSTHAN); if (lzero > rzero) return (DT_GREATERTHAN); return (0); } static int dt_aggregate_quantizedcmp(int64_t *lhs, int64_t *rhs) { int nbuckets = DTRACE_QUANTIZE_NBUCKETS; long double ltotal = 0, rtotal = 0; int64_t lzero, rzero; uint_t i; for (i = 0; i < nbuckets; i++) { int64_t bucketval = DTRACE_QUANTIZE_BUCKETVAL(i); if (bucketval == 0) { lzero = lhs[i]; rzero = rhs[i]; } ltotal += (long double)bucketval * (long double)lhs[i]; rtotal += (long double)bucketval * (long double)rhs[i]; } if (ltotal < rtotal) return (DT_LESSTHAN); if (ltotal > rtotal) return (DT_GREATERTHAN); /* * If they're both equal, then we will compare based on the weights at * zero. If the weights at zero are equal, then this will be judged a * tie and will be resolved based on the key comparison. */ if (lzero < rzero) return (DT_LESSTHAN); if (lzero > rzero) return (DT_GREATERTHAN); return (0); } static void dt_aggregate_usym(dtrace_hdl_t *dtp, uint64_t *data) { uint64_t pid = data[0]; uint64_t *pc = &data[1]; struct ps_prochandle *P; GElf_Sym sym; if (dtp->dt_vector != NULL) return; if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) == NULL) return; dt_proc_lock(dtp, P); if (Plookup_by_addr(P, *pc, NULL, 0, &sym) == 0) *pc = sym.st_value; dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } static void dt_aggregate_umod(dtrace_hdl_t *dtp, uint64_t *data) { uint64_t pid = data[0]; uint64_t *pc = &data[1]; struct ps_prochandle *P; const prmap_t *map; if (dtp->dt_vector != NULL) return; if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) == NULL) return; dt_proc_lock(dtp, P); if ((map = Paddr_to_map(P, *pc)) != NULL) *pc = map->pr_vaddr; dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } static void dt_aggregate_sym(dtrace_hdl_t *dtp, uint64_t *data) { GElf_Sym sym; uint64_t *pc = data; if (dtrace_lookup_by_addr(dtp, *pc, &sym, NULL) == 0) *pc = sym.st_value; } static void dt_aggregate_mod(dtrace_hdl_t *dtp, uint64_t *data) { uint64_t *pc = data; dt_module_t *dmp; if (dtp->dt_vector != NULL) { /* * We don't have a way of just getting the module for a * vectored open, and it doesn't seem to be worth defining * one. This means that use of mod() won't get true * aggregation in the postmortem case (some modules may * appear more than once in aggregation output). It seems * unlikely that anyone will ever notice or care... */ return; } for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; dmp = dt_list_next(dmp)) { if (*pc - dmp->dm_text_va < dmp->dm_text_size) { *pc = dmp->dm_text_va; return; } } } static dtrace_aggvarid_t dt_aggregate_aggvarid(dt_ahashent_t *ent) { dtrace_aggdesc_t *agg = ent->dtahe_data.dtada_desc; caddr_t data = ent->dtahe_data.dtada_data; dtrace_recdesc_t *rec = agg->dtagd_rec; /* * First, we'll check the variable ID in the aggdesc. If it's valid, * we'll return it. If not, we'll use the compiler-generated ID * present as the first record. */ if (agg->dtagd_varid != DTRACE_AGGVARIDNONE) return (agg->dtagd_varid); agg->dtagd_varid = *((dtrace_aggvarid_t *)(uintptr_t)(data + rec->dtrd_offset)); return (agg->dtagd_varid); } static int dt_aggregate_snap_cpu(dtrace_hdl_t *dtp, processorid_t cpu) { dtrace_epid_t id; uint64_t hashval; size_t offs, roffs, size, ndx; int i, j, rval; caddr_t addr, data; dtrace_recdesc_t *rec; dt_aggregate_t *agp = &dtp->dt_aggregate; dtrace_aggdesc_t *agg; dt_ahash_t *hash = &agp->dtat_hash; dt_ahashent_t *h; dtrace_bufdesc_t b = agp->dtat_buf, *buf = &b; dtrace_aggdata_t *aggdata; int flags = agp->dtat_flags; buf->dtbd_cpu = cpu; #if defined(sun) if (dt_ioctl(dtp, DTRACEIOC_AGGSNAP, buf) == -1) { #else if (dt_ioctl(dtp, DTRACEIOC_AGGSNAP, &buf) == -1) { #endif if (errno == ENOENT) { /* * If that failed with ENOENT, it may be because the * CPU was unconfigured. This is okay; we'll just * do nothing but return success. */ return (0); } return (dt_set_errno(dtp, errno)); } if (buf->dtbd_drops != 0) { if (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_AGGREGATION, buf->dtbd_drops) == -1) return (-1); } if (buf->dtbd_size == 0) return (0); if (hash->dtah_hash == NULL) { size_t size; hash->dtah_size = DTRACE_AHASHSIZE; size = hash->dtah_size * sizeof (dt_ahashent_t *); if ((hash->dtah_hash = malloc(size)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); bzero(hash->dtah_hash, size); } for (offs = 0; offs < buf->dtbd_size; ) { /* * We're guaranteed to have an ID. */ id = *((dtrace_epid_t *)((uintptr_t)buf->dtbd_data + (uintptr_t)offs)); if (id == DTRACE_AGGIDNONE) { /* * This is filler to assure proper alignment of the * next record; we simply ignore it. */ offs += sizeof (id); continue; } if ((rval = dt_aggid_lookup(dtp, id, &agg)) != 0) return (rval); addr = buf->dtbd_data + offs; size = agg->dtagd_size; hashval = 0; for (j = 0; j < agg->dtagd_nrecs - 1; j++) { rec = &agg->dtagd_rec[j]; roffs = rec->dtrd_offset; switch (rec->dtrd_action) { case DTRACEACT_USYM: dt_aggregate_usym(dtp, /* LINTED - alignment */ (uint64_t *)&addr[roffs]); break; case DTRACEACT_UMOD: dt_aggregate_umod(dtp, /* LINTED - alignment */ (uint64_t *)&addr[roffs]); break; case DTRACEACT_SYM: /* LINTED - alignment */ dt_aggregate_sym(dtp, (uint64_t *)&addr[roffs]); break; case DTRACEACT_MOD: /* LINTED - alignment */ dt_aggregate_mod(dtp, (uint64_t *)&addr[roffs]); break; default: break; } for (i = 0; i < rec->dtrd_size; i++) hashval += addr[roffs + i]; } ndx = hashval % hash->dtah_size; for (h = hash->dtah_hash[ndx]; h != NULL; h = h->dtahe_next) { if (h->dtahe_hashval != hashval) continue; if (h->dtahe_size != size) continue; aggdata = &h->dtahe_data; data = aggdata->dtada_data; for (j = 0; j < agg->dtagd_nrecs - 1; j++) { rec = &agg->dtagd_rec[j]; roffs = rec->dtrd_offset; for (i = 0; i < rec->dtrd_size; i++) if (addr[roffs + i] != data[roffs + i]) goto hashnext; } /* * We found it. Now we need to apply the aggregating * action on the data here. */ rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; roffs = rec->dtrd_offset; /* LINTED - alignment */ h->dtahe_aggregate((int64_t *)&data[roffs], /* LINTED - alignment */ (int64_t *)&addr[roffs], rec->dtrd_size); /* * If we're keeping per CPU data, apply the aggregating * action there as well. */ if (aggdata->dtada_percpu != NULL) { data = aggdata->dtada_percpu[cpu]; /* LINTED - alignment */ h->dtahe_aggregate((int64_t *)data, /* LINTED - alignment */ (int64_t *)&addr[roffs], rec->dtrd_size); } goto bufnext; hashnext: continue; } /* * If we're here, we couldn't find an entry for this record. */ if ((h = malloc(sizeof (dt_ahashent_t))) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); bzero(h, sizeof (dt_ahashent_t)); aggdata = &h->dtahe_data; if ((aggdata->dtada_data = malloc(size)) == NULL) { free(h); return (dt_set_errno(dtp, EDT_NOMEM)); } bcopy(addr, aggdata->dtada_data, size); aggdata->dtada_size = size; aggdata->dtada_desc = agg; aggdata->dtada_handle = dtp; (void) dt_epid_lookup(dtp, agg->dtagd_epid, &aggdata->dtada_edesc, &aggdata->dtada_pdesc); aggdata->dtada_normal = 1; h->dtahe_hashval = hashval; h->dtahe_size = size; (void) dt_aggregate_aggvarid(h); rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; if (flags & DTRACE_A_PERCPU) { int max_cpus = agp->dtat_maxcpu; caddr_t *percpu = malloc(max_cpus * sizeof (caddr_t)); if (percpu == NULL) { free(aggdata->dtada_data); free(h); return (dt_set_errno(dtp, EDT_NOMEM)); } for (j = 0; j < max_cpus; j++) { percpu[j] = malloc(rec->dtrd_size); if (percpu[j] == NULL) { while (--j >= 0) free(percpu[j]); free(aggdata->dtada_data); free(h); return (dt_set_errno(dtp, EDT_NOMEM)); } if (j == cpu) { bcopy(&addr[rec->dtrd_offset], percpu[j], rec->dtrd_size); } else { bzero(percpu[j], rec->dtrd_size); } } aggdata->dtada_percpu = percpu; } switch (rec->dtrd_action) { case DTRACEAGG_MIN: h->dtahe_aggregate = dt_aggregate_min; break; case DTRACEAGG_MAX: h->dtahe_aggregate = dt_aggregate_max; break; case DTRACEAGG_LQUANTIZE: h->dtahe_aggregate = dt_aggregate_lquantize; break; case DTRACEAGG_LLQUANTIZE: h->dtahe_aggregate = dt_aggregate_llquantize; break; case DTRACEAGG_COUNT: case DTRACEAGG_SUM: case DTRACEAGG_AVG: case DTRACEAGG_STDDEV: case DTRACEAGG_QUANTIZE: h->dtahe_aggregate = dt_aggregate_count; break; default: return (dt_set_errno(dtp, EDT_BADAGG)); } if (hash->dtah_hash[ndx] != NULL) hash->dtah_hash[ndx]->dtahe_prev = h; h->dtahe_next = hash->dtah_hash[ndx]; hash->dtah_hash[ndx] = h; if (hash->dtah_all != NULL) hash->dtah_all->dtahe_prevall = h; h->dtahe_nextall = hash->dtah_all; hash->dtah_all = h; bufnext: offs += agg->dtagd_size; } return (0); } int dtrace_aggregate_snap(dtrace_hdl_t *dtp) { int i, rval; dt_aggregate_t *agp = &dtp->dt_aggregate; hrtime_t now = gethrtime(); dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_AGGRATE]; if (dtp->dt_lastagg != 0) { if (now - dtp->dt_lastagg < interval) return (0); dtp->dt_lastagg += interval; } else { dtp->dt_lastagg = now; } if (!dtp->dt_active) return (dt_set_errno(dtp, EINVAL)); if (agp->dtat_buf.dtbd_size == 0) return (0); for (i = 0; i < agp->dtat_ncpus; i++) { if ((rval = dt_aggregate_snap_cpu(dtp, agp->dtat_cpus[i]))) return (rval); } return (0); } static int dt_aggregate_hashcmp(const void *lhs, const void *rhs) { dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; if (lagg->dtagd_nrecs < ragg->dtagd_nrecs) return (DT_LESSTHAN); if (lagg->dtagd_nrecs > ragg->dtagd_nrecs) return (DT_GREATERTHAN); return (0); } static int dt_aggregate_varcmp(const void *lhs, const void *rhs) { dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); dtrace_aggvarid_t lid, rid; lid = dt_aggregate_aggvarid(lh); rid = dt_aggregate_aggvarid(rh); if (lid < rid) return (DT_LESSTHAN); if (lid > rid) return (DT_GREATERTHAN); return (0); } static int dt_aggregate_keycmp(const void *lhs, const void *rhs) { dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; dtrace_recdesc_t *lrec, *rrec; char *ldata, *rdata; int rval, i, j, keypos, nrecs; if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0) return (rval); nrecs = lagg->dtagd_nrecs - 1; assert(nrecs == ragg->dtagd_nrecs - 1); keypos = dt_keypos + 1 >= nrecs ? 0 : dt_keypos; for (i = 1; i < nrecs; i++) { uint64_t lval, rval; int ndx = i + keypos; if (ndx >= nrecs) ndx = ndx - nrecs + 1; lrec = &lagg->dtagd_rec[ndx]; rrec = &ragg->dtagd_rec[ndx]; ldata = lh->dtahe_data.dtada_data + lrec->dtrd_offset; rdata = rh->dtahe_data.dtada_data + rrec->dtrd_offset; if (lrec->dtrd_size < rrec->dtrd_size) return (DT_LESSTHAN); if (lrec->dtrd_size > rrec->dtrd_size) return (DT_GREATERTHAN); switch (lrec->dtrd_size) { case sizeof (uint64_t): /* LINTED - alignment */ lval = *((uint64_t *)ldata); /* LINTED - alignment */ rval = *((uint64_t *)rdata); break; case sizeof (uint32_t): /* LINTED - alignment */ lval = *((uint32_t *)ldata); /* LINTED - alignment */ rval = *((uint32_t *)rdata); break; case sizeof (uint16_t): /* LINTED - alignment */ lval = *((uint16_t *)ldata); /* LINTED - alignment */ rval = *((uint16_t *)rdata); break; case sizeof (uint8_t): lval = *((uint8_t *)ldata); rval = *((uint8_t *)rdata); break; default: switch (lrec->dtrd_action) { case DTRACEACT_UMOD: case DTRACEACT_UADDR: case DTRACEACT_USYM: for (j = 0; j < 2; j++) { /* LINTED - alignment */ lval = ((uint64_t *)ldata)[j]; /* LINTED - alignment */ rval = ((uint64_t *)rdata)[j]; if (lval < rval) return (DT_LESSTHAN); if (lval > rval) return (DT_GREATERTHAN); } break; default: for (j = 0; j < lrec->dtrd_size; j++) { lval = ((uint8_t *)ldata)[j]; rval = ((uint8_t *)rdata)[j]; if (lval < rval) return (DT_LESSTHAN); if (lval > rval) return (DT_GREATERTHAN); } } continue; } if (lval < rval) return (DT_LESSTHAN); if (lval > rval) return (DT_GREATERTHAN); } return (0); } static int dt_aggregate_valcmp(const void *lhs, const void *rhs) { dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; caddr_t ldata = lh->dtahe_data.dtada_data; caddr_t rdata = rh->dtahe_data.dtada_data; dtrace_recdesc_t *lrec, *rrec; int64_t *laddr, *raddr; int rval; assert(lagg->dtagd_nrecs == ragg->dtagd_nrecs); lrec = &lagg->dtagd_rec[lagg->dtagd_nrecs - 1]; rrec = &ragg->dtagd_rec[ragg->dtagd_nrecs - 1]; assert(lrec->dtrd_action == rrec->dtrd_action); laddr = (int64_t *)(uintptr_t)(ldata + lrec->dtrd_offset); raddr = (int64_t *)(uintptr_t)(rdata + rrec->dtrd_offset); switch (lrec->dtrd_action) { case DTRACEAGG_AVG: rval = dt_aggregate_averagecmp(laddr, raddr); break; case DTRACEAGG_STDDEV: rval = dt_aggregate_stddevcmp(laddr, raddr); break; case DTRACEAGG_QUANTIZE: rval = dt_aggregate_quantizedcmp(laddr, raddr); break; case DTRACEAGG_LQUANTIZE: rval = dt_aggregate_lquantizedcmp(laddr, raddr); break; case DTRACEAGG_LLQUANTIZE: rval = dt_aggregate_llquantizedcmp(laddr, raddr); break; case DTRACEAGG_COUNT: case DTRACEAGG_SUM: case DTRACEAGG_MIN: case DTRACEAGG_MAX: rval = dt_aggregate_countcmp(laddr, raddr); break; default: assert(0); } return (rval); } static int dt_aggregate_valkeycmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_valcmp(lhs, rhs)) != 0) return (rval); /* * If we're here, the values for the two aggregation elements are * equal. We already know that the key layout is the same for the two * elements; we must now compare the keys themselves as a tie-breaker. */ return (dt_aggregate_keycmp(lhs, rhs)); } static int dt_aggregate_keyvarcmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_keycmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_varcmp(lhs, rhs)); } static int dt_aggregate_varkeycmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_keycmp(lhs, rhs)); } static int dt_aggregate_valvarcmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_valkeycmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_varcmp(lhs, rhs)); } static int dt_aggregate_varvalcmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_valkeycmp(lhs, rhs)); } static int dt_aggregate_keyvarrevcmp(const void *lhs, const void *rhs) { return (dt_aggregate_keyvarcmp(rhs, lhs)); } static int dt_aggregate_varkeyrevcmp(const void *lhs, const void *rhs) { return (dt_aggregate_varkeycmp(rhs, lhs)); } static int dt_aggregate_valvarrevcmp(const void *lhs, const void *rhs) { return (dt_aggregate_valvarcmp(rhs, lhs)); } static int dt_aggregate_varvalrevcmp(const void *lhs, const void *rhs) { return (dt_aggregate_varvalcmp(rhs, lhs)); } static int dt_aggregate_bundlecmp(const void *lhs, const void *rhs) { dt_ahashent_t **lh = *((dt_ahashent_t ***)lhs); dt_ahashent_t **rh = *((dt_ahashent_t ***)rhs); int i, rval; if (dt_keysort) { /* * If we're sorting on keys, we need to scan until we find the * last entry -- that's the representative key. (The order of * the bundle is values followed by key to accommodate the * default behavior of sorting by value.) If the keys are * equal, we'll fall into the value comparison loop, below. */ for (i = 0; lh[i + 1] != NULL; i++) continue; assert(i != 0); assert(rh[i + 1] == NULL); if ((rval = dt_aggregate_keycmp(&lh[i], &rh[i])) != 0) return (rval); } for (i = 0; ; i++) { if (lh[i + 1] == NULL) { /* * All of the values are equal; if we're sorting on * keys, then we're only here because the keys were * found to be equal and these records are therefore * equal. If we're not sorting on keys, we'll use the * key comparison from the representative key as the * tie-breaker. */ if (dt_keysort) return (0); assert(i != 0); assert(rh[i + 1] == NULL); return (dt_aggregate_keycmp(&lh[i], &rh[i])); } else { if ((rval = dt_aggregate_valcmp(&lh[i], &rh[i])) != 0) return (rval); } } } int dt_aggregate_go(dtrace_hdl_t *dtp) { dt_aggregate_t *agp = &dtp->dt_aggregate; dtrace_optval_t size, cpu; dtrace_bufdesc_t *buf = &agp->dtat_buf; int rval, i; assert(agp->dtat_maxcpu == 0); assert(agp->dtat_ncpu == 0); assert(agp->dtat_cpus == NULL); agp->dtat_maxcpu = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; agp->dtat_ncpu = dt_sysconf(dtp, _SC_NPROCESSORS_MAX); agp->dtat_cpus = malloc(agp->dtat_ncpu * sizeof (processorid_t)); if (agp->dtat_cpus == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); /* * Use the aggregation buffer size as reloaded from the kernel. */ size = dtp->dt_options[DTRACEOPT_AGGSIZE]; rval = dtrace_getopt(dtp, "aggsize", &size); assert(rval == 0); if (size == 0 || size == DTRACEOPT_UNSET) return (0); buf = &agp->dtat_buf; buf->dtbd_size = size; if ((buf->dtbd_data = malloc(buf->dtbd_size)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); /* * Now query for the CPUs enabled. */ rval = dtrace_getopt(dtp, "cpu", &cpu); assert(rval == 0 && cpu != DTRACEOPT_UNSET); if (cpu != DTRACE_CPUALL) { assert(cpu < agp->dtat_ncpu); agp->dtat_cpus[agp->dtat_ncpus++] = (processorid_t)cpu; return (0); } agp->dtat_ncpus = 0; for (i = 0; i < agp->dtat_maxcpu; i++) { if (dt_status(dtp, i) == -1) continue; agp->dtat_cpus[agp->dtat_ncpus++] = i; } return (0); } static int dt_aggwalk_rval(dtrace_hdl_t *dtp, dt_ahashent_t *h, int rval) { dt_aggregate_t *agp = &dtp->dt_aggregate; dtrace_aggdata_t *data; dtrace_aggdesc_t *aggdesc; dtrace_recdesc_t *rec; int i; switch (rval) { case DTRACE_AGGWALK_NEXT: break; case DTRACE_AGGWALK_CLEAR: { uint32_t size, offs = 0; aggdesc = h->dtahe_data.dtada_desc; rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; size = rec->dtrd_size; data = &h->dtahe_data; if (rec->dtrd_action == DTRACEAGG_LQUANTIZE) { offs = sizeof (uint64_t); size -= sizeof (uint64_t); } bzero(&data->dtada_data[rec->dtrd_offset] + offs, size); if (data->dtada_percpu == NULL) break; for (i = 0; i < dtp->dt_aggregate.dtat_maxcpu; i++) bzero(data->dtada_percpu[i] + offs, size); break; } case DTRACE_AGGWALK_ERROR: /* * We assume that errno is already set in this case. */ return (dt_set_errno(dtp, errno)); case DTRACE_AGGWALK_ABORT: return (dt_set_errno(dtp, EDT_DIRABORT)); case DTRACE_AGGWALK_DENORMALIZE: h->dtahe_data.dtada_normal = 1; return (0); case DTRACE_AGGWALK_NORMALIZE: if (h->dtahe_data.dtada_normal == 0) { h->dtahe_data.dtada_normal = 1; return (dt_set_errno(dtp, EDT_BADRVAL)); } return (0); case DTRACE_AGGWALK_REMOVE: { dtrace_aggdata_t *aggdata = &h->dtahe_data; int max_cpus = agp->dtat_maxcpu; /* * First, remove this hash entry from its hash chain. */ if (h->dtahe_prev != NULL) { h->dtahe_prev->dtahe_next = h->dtahe_next; } else { dt_ahash_t *hash = &agp->dtat_hash; size_t ndx = h->dtahe_hashval % hash->dtah_size; assert(hash->dtah_hash[ndx] == h); hash->dtah_hash[ndx] = h->dtahe_next; } if (h->dtahe_next != NULL) h->dtahe_next->dtahe_prev = h->dtahe_prev; /* * Now remove it from the list of all hash entries. */ if (h->dtahe_prevall != NULL) { h->dtahe_prevall->dtahe_nextall = h->dtahe_nextall; } else { dt_ahash_t *hash = &agp->dtat_hash; assert(hash->dtah_all == h); hash->dtah_all = h->dtahe_nextall; } if (h->dtahe_nextall != NULL) h->dtahe_nextall->dtahe_prevall = h->dtahe_prevall; /* * We're unlinked. We can safely destroy the data. */ if (aggdata->dtada_percpu != NULL) { for (i = 0; i < max_cpus; i++) free(aggdata->dtada_percpu[i]); free(aggdata->dtada_percpu); } free(aggdata->dtada_data); free(h); return (0); } default: return (dt_set_errno(dtp, EDT_BADRVAL)); } return (0); } void dt_aggregate_qsort(dtrace_hdl_t *dtp, void *base, size_t nel, size_t width, int (*compar)(const void *, const void *)) { int rev = dt_revsort, key = dt_keysort, keypos = dt_keypos; dtrace_optval_t keyposopt = dtp->dt_options[DTRACEOPT_AGGSORTKEYPOS]; dt_revsort = (dtp->dt_options[DTRACEOPT_AGGSORTREV] != DTRACEOPT_UNSET); dt_keysort = (dtp->dt_options[DTRACEOPT_AGGSORTKEY] != DTRACEOPT_UNSET); if (keyposopt != DTRACEOPT_UNSET && keyposopt <= INT_MAX) { dt_keypos = (int)keyposopt; } else { dt_keypos = 0; } if (compar == NULL) { if (!dt_keysort) { compar = dt_aggregate_varvalcmp; } else { compar = dt_aggregate_varkeycmp; } } qsort(base, nel, width, compar); dt_revsort = rev; dt_keysort = key; dt_keypos = keypos; } int dtrace_aggregate_walk(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { dt_ahashent_t *h, *next; dt_ahash_t *hash = &dtp->dt_aggregate.dtat_hash; for (h = hash->dtah_all; h != NULL; h = next) { /* * dt_aggwalk_rval() can potentially remove the current hash * entry; we need to load the next hash entry before calling * into it. */ next = h->dtahe_nextall; if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) return (-1); } return (0); } static int +dt_aggregate_total(dtrace_hdl_t *dtp, boolean_t clear) +{ + dt_ahashent_t *h; + dtrace_aggdata_t **total; + dtrace_aggid_t max = DTRACE_AGGVARIDNONE, id; + dt_aggregate_t *agp = &dtp->dt_aggregate; + dt_ahash_t *hash = &agp->dtat_hash; + uint32_t tflags; + + tflags = DTRACE_A_TOTAL | DTRACE_A_HASNEGATIVES | DTRACE_A_HASPOSITIVES; + + /* + * If we need to deliver per-aggregation totals, we're going to take + * three passes over the aggregate: one to clear everything out and + * determine our maximum aggregation ID, one to actually total + * everything up, and a final pass to assign the totals to the + * individual elements. + */ + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + dtrace_aggdata_t *aggdata = &h->dtahe_data; + + if ((id = dt_aggregate_aggvarid(h)) > max) + max = id; + + aggdata->dtada_total = 0; + aggdata->dtada_flags &= ~tflags; + } + + if (clear || max == DTRACE_AGGVARIDNONE) + return (0); + + total = dt_zalloc(dtp, (max + 1) * sizeof (dtrace_aggdata_t *)); + + if (total == NULL) + return (-1); + + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + dtrace_aggdata_t *aggdata = &h->dtahe_data; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + dtrace_recdesc_t *rec; + caddr_t data; + int64_t val, *addr; + + rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; + data = aggdata->dtada_data; + addr = (int64_t *)(uintptr_t)(data + rec->dtrd_offset); + + switch (rec->dtrd_action) { + case DTRACEAGG_STDDEV: + val = dt_stddev((uint64_t *)addr, 1); + break; + + case DTRACEAGG_SUM: + case DTRACEAGG_COUNT: + val = *addr; + break; + + case DTRACEAGG_AVG: + val = addr[0] ? (addr[1] / addr[0]) : 0; + break; + + default: + continue; + } + + if (total[agg->dtagd_varid] == NULL) { + total[agg->dtagd_varid] = aggdata; + aggdata->dtada_flags |= DTRACE_A_TOTAL; + } else { + aggdata = total[agg->dtagd_varid]; + } + + if (val > 0) + aggdata->dtada_flags |= DTRACE_A_HASPOSITIVES; + + if (val < 0) { + aggdata->dtada_flags |= DTRACE_A_HASNEGATIVES; + val = -val; + } + + if (dtp->dt_options[DTRACEOPT_AGGZOOM] != DTRACEOPT_UNSET) { + val = (int64_t)((long double)val * + (1 / DTRACE_AGGZOOM_MAX)); + + if (val > aggdata->dtada_total) + aggdata->dtada_total = val; + } else { + aggdata->dtada_total += val; + } + } + + /* + * And now one final pass to set everyone's total. + */ + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + dtrace_aggdata_t *aggdata = &h->dtahe_data, *t; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + + if ((t = total[agg->dtagd_varid]) == NULL || aggdata == t) + continue; + + aggdata->dtada_total = t->dtada_total; + aggdata->dtada_flags |= (t->dtada_flags & tflags); + } + + dt_free(dtp, total); + + return (0); +} + +static int +dt_aggregate_minmaxbin(dtrace_hdl_t *dtp, boolean_t clear) +{ + dt_ahashent_t *h; + dtrace_aggdata_t **minmax; + dtrace_aggid_t max = DTRACE_AGGVARIDNONE, id; + dt_aggregate_t *agp = &dtp->dt_aggregate; + dt_ahash_t *hash = &agp->dtat_hash; + + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + dtrace_aggdata_t *aggdata = &h->dtahe_data; + + if ((id = dt_aggregate_aggvarid(h)) > max) + max = id; + + aggdata->dtada_minbin = 0; + aggdata->dtada_maxbin = 0; + aggdata->dtada_flags &= ~DTRACE_A_MINMAXBIN; + } + + if (clear || max == DTRACE_AGGVARIDNONE) + return (0); + + minmax = dt_zalloc(dtp, (max + 1) * sizeof (dtrace_aggdata_t *)); + + if (minmax == NULL) + return (-1); + + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + dtrace_aggdata_t *aggdata = &h->dtahe_data; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + dtrace_recdesc_t *rec; + caddr_t data; + int64_t *addr; + int minbin = -1, maxbin = -1, i; + int start = 0, size; + + rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; + size = rec->dtrd_size / sizeof (int64_t); + data = aggdata->dtada_data; + addr = (int64_t *)(uintptr_t)(data + rec->dtrd_offset); + + switch (rec->dtrd_action) { + case DTRACEAGG_LQUANTIZE: + /* + * For lquantize(), we always display the entire range + * of the aggregation when aggpack is set. + */ + start = 1; + minbin = start; + maxbin = size - 1 - start; + break; + + case DTRACEAGG_QUANTIZE: + for (i = start; i < size; i++) { + if (!addr[i]) + continue; + + if (minbin == -1) + minbin = i - start; + + maxbin = i - start; + } + + if (minbin == -1) { + /* + * If we have no data (e.g., due to a clear() + * or negative increments), we'll use the + * zero bucket as both our min and max. + */ + minbin = maxbin = DTRACE_QUANTIZE_ZEROBUCKET; + } + + break; + + default: + continue; + } + + if (minmax[agg->dtagd_varid] == NULL) { + minmax[agg->dtagd_varid] = aggdata; + aggdata->dtada_flags |= DTRACE_A_MINMAXBIN; + aggdata->dtada_minbin = minbin; + aggdata->dtada_maxbin = maxbin; + continue; + } + + if (minbin < minmax[agg->dtagd_varid]->dtada_minbin) + minmax[agg->dtagd_varid]->dtada_minbin = minbin; + + if (maxbin > minmax[agg->dtagd_varid]->dtada_maxbin) + minmax[agg->dtagd_varid]->dtada_maxbin = maxbin; + } + + /* + * And now one final pass to set everyone's minbin and maxbin. + */ + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + dtrace_aggdata_t *aggdata = &h->dtahe_data, *mm; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + + if ((mm = minmax[agg->dtagd_varid]) == NULL || aggdata == mm) + continue; + + aggdata->dtada_minbin = mm->dtada_minbin; + aggdata->dtada_maxbin = mm->dtada_maxbin; + aggdata->dtada_flags |= DTRACE_A_MINMAXBIN; + } + + dt_free(dtp, minmax); + + return (0); +} + +static int dt_aggregate_walk_sorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg, int (*sfunc)(const void *, const void *)) { dt_aggregate_t *agp = &dtp->dt_aggregate; dt_ahashent_t *h, **sorted; dt_ahash_t *hash = &agp->dtat_hash; size_t i, nentries = 0; + int rval = -1; + agp->dtat_flags &= ~(DTRACE_A_TOTAL | DTRACE_A_MINMAXBIN); + + if (dtp->dt_options[DTRACEOPT_AGGHIST] != DTRACEOPT_UNSET) { + agp->dtat_flags |= DTRACE_A_TOTAL; + + if (dt_aggregate_total(dtp, B_FALSE) != 0) + return (-1); + } + + if (dtp->dt_options[DTRACEOPT_AGGPACK] != DTRACEOPT_UNSET) { + agp->dtat_flags |= DTRACE_A_MINMAXBIN; + + if (dt_aggregate_minmaxbin(dtp, B_FALSE) != 0) + return (-1); + } + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) nentries++; sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *)); if (sorted == NULL) - return (-1); + goto out; for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) sorted[i++] = h; (void) pthread_mutex_lock(&dt_qsort_lock); if (sfunc == NULL) { dt_aggregate_qsort(dtp, sorted, nentries, sizeof (dt_ahashent_t *), NULL); } else { /* * If we've been explicitly passed a sorting function, * we'll use that -- ignoring the values of the "aggsortrev", * "aggsortkey" and "aggsortkeypos" options. */ qsort(sorted, nentries, sizeof (dt_ahashent_t *), sfunc); } (void) pthread_mutex_unlock(&dt_qsort_lock); for (i = 0; i < nentries; i++) { h = sorted[i]; - if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) { - dt_free(dtp, sorted); - return (-1); - } + if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) + goto out; } + rval = 0; +out: + if (agp->dtat_flags & DTRACE_A_TOTAL) + (void) dt_aggregate_total(dtp, B_TRUE); + + if (agp->dtat_flags & DTRACE_A_MINMAXBIN) + (void) dt_aggregate_minmaxbin(dtp, B_TRUE); + dt_free(dtp, sorted); - return (0); + return (rval); } int dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, NULL)); } int dtrace_aggregate_walk_keysorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_varkeycmp)); } int dtrace_aggregate_walk_valsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_varvalcmp)); } int dtrace_aggregate_walk_keyvarsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_keyvarcmp)); } int dtrace_aggregate_walk_valvarsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_valvarcmp)); } int dtrace_aggregate_walk_keyrevsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_varkeyrevcmp)); } int dtrace_aggregate_walk_valrevsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_varvalrevcmp)); } int dtrace_aggregate_walk_keyvarrevsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_keyvarrevcmp)); } int dtrace_aggregate_walk_valvarrevsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_valvarrevcmp)); } int dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars, int naggvars, dtrace_aggregate_walk_joined_f *func, void *arg) { dt_aggregate_t *agp = &dtp->dt_aggregate; dt_ahashent_t *h, **sorted = NULL, ***bundle, **nbundle; const dtrace_aggdata_t **data; dt_ahashent_t *zaggdata = NULL; dt_ahash_t *hash = &agp->dtat_hash; size_t nentries = 0, nbundles = 0, start, zsize = 0, bundlesize; dtrace_aggvarid_t max = 0, aggvar; int rval = -1, *map, *remap = NULL; int i, j; dtrace_optval_t sortpos = dtp->dt_options[DTRACEOPT_AGGSORTPOS]; /* * If the sorting position is greater than the number of aggregation * variable IDs, we silently set it to 0. */ if (sortpos == DTRACEOPT_UNSET || sortpos >= naggvars) sortpos = 0; /* * First we need to translate the specified aggregation variable IDs * into a linear map that will allow us to translate an aggregation * variable ID into its position in the specified aggvars. */ for (i = 0; i < naggvars; i++) { if (aggvars[i] == DTRACE_AGGVARIDNONE || aggvars[i] < 0) return (dt_set_errno(dtp, EDT_BADAGGVAR)); if (aggvars[i] > max) max = aggvars[i]; } if ((map = dt_zalloc(dtp, (max + 1) * sizeof (int))) == NULL) return (-1); zaggdata = dt_zalloc(dtp, naggvars * sizeof (dt_ahashent_t)); if (zaggdata == NULL) goto out; for (i = 0; i < naggvars; i++) { int ndx = i + sortpos; if (ndx >= naggvars) ndx -= naggvars; aggvar = aggvars[ndx]; assert(aggvar <= max); if (map[aggvar]) { /* * We have an aggregation variable that is present * more than once in the array of aggregation * variables. While it's unclear why one might want * to do this, it's legal. To support this construct, * we will allocate a remap that will indicate the * position from which this aggregation variable * should be pulled. (That is, where the remap will * map from one position to another.) */ if (remap == NULL) { remap = dt_zalloc(dtp, naggvars * sizeof (int)); if (remap == NULL) goto out; } /* * Given that the variable is already present, assert * that following through the mapping and adjusting * for the sort position yields the same aggregation * variable ID. */ assert(aggvars[(map[aggvar] - 1 + sortpos) % naggvars] == aggvars[ndx]); remap[i] = map[aggvar]; continue; } map[aggvar] = i + 1; } /* * We need to take two passes over the data to size our allocation, so * we'll use the first pass to also fill in the zero-filled data to be * used to properly format a zero-valued aggregation. */ for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { dtrace_aggvarid_t id; int ndx; if ((id = dt_aggregate_aggvarid(h)) > max || !(ndx = map[id])) continue; if (zaggdata[ndx - 1].dtahe_size == 0) { zaggdata[ndx - 1].dtahe_size = h->dtahe_size; zaggdata[ndx - 1].dtahe_data = h->dtahe_data; } nentries++; } if (nentries == 0) { /* * We couldn't find any entries; there is nothing else to do. */ rval = 0; goto out; } /* * Before we sort the data, we're going to look for any holes in our * zero-filled data. This will occur if an aggregation variable that * we are being asked to print has not yet been assigned the result of * any aggregating action for _any_ tuple. The issue becomes that we * would like a zero value to be printed for all columns for this * aggregation, but without any record description, we don't know the * aggregating action that corresponds to the aggregation variable. To * try to find a match, we're simply going to lookup aggregation IDs * (which are guaranteed to be contiguous and to start from 1), looking * for the specified aggregation variable ID. If we find a match, * we'll use that. If we iterate over all aggregation IDs and don't * find a match, then we must be an anonymous enabling. (Anonymous * enablings can't currently derive either aggregation variable IDs or * aggregation variable names given only an aggregation ID.) In this * obscure case (anonymous enabling, multiple aggregation printa() with * some aggregations not represented for any tuple), our defined * behavior is that the zero will be printed in the format of the first * aggregation variable that contains any non-zero value. */ for (i = 0; i < naggvars; i++) { if (zaggdata[i].dtahe_size == 0) { dtrace_aggvarid_t aggvar; aggvar = aggvars[(i - sortpos + naggvars) % naggvars]; assert(zaggdata[i].dtahe_data.dtada_data == NULL); for (j = DTRACE_AGGIDNONE + 1; ; j++) { dtrace_aggdesc_t *agg; dtrace_aggdata_t *aggdata; if (dt_aggid_lookup(dtp, j, &agg) != 0) break; if (agg->dtagd_varid != aggvar) continue; /* * We have our description -- now we need to * cons up the zaggdata entry for it. */ aggdata = &zaggdata[i].dtahe_data; aggdata->dtada_size = agg->dtagd_size; aggdata->dtada_desc = agg; aggdata->dtada_handle = dtp; (void) dt_epid_lookup(dtp, agg->dtagd_epid, &aggdata->dtada_edesc, &aggdata->dtada_pdesc); aggdata->dtada_normal = 1; zaggdata[i].dtahe_hashval = 0; zaggdata[i].dtahe_size = agg->dtagd_size; break; } if (zaggdata[i].dtahe_size == 0) { caddr_t data; /* * We couldn't find this aggregation, meaning * that we have never seen it before for any * tuple _and_ this is an anonymous enabling. * That is, we're in the obscure case outlined * above. In this case, our defined behavior * is to format the data in the format of the * first non-zero aggregation -- of which, of * course, we know there to be at least one * (or nentries would have been zero). */ for (j = 0; j < naggvars; j++) { if (zaggdata[j].dtahe_size != 0) break; } assert(j < naggvars); zaggdata[i] = zaggdata[j]; data = zaggdata[i].dtahe_data.dtada_data; assert(data != NULL); } } } /* * Now we need to allocate our zero-filled data for use for * aggregations that don't have a value corresponding to a given key. */ for (i = 0; i < naggvars; i++) { dtrace_aggdata_t *aggdata = &zaggdata[i].dtahe_data; dtrace_aggdesc_t *aggdesc = aggdata->dtada_desc; dtrace_recdesc_t *rec; uint64_t larg; caddr_t zdata; zsize = zaggdata[i].dtahe_size; assert(zsize != 0); if ((zdata = dt_zalloc(dtp, zsize)) == NULL) { /* * If we failed to allocated some zero-filled data, we * need to zero out the remaining dtada_data pointers * to prevent the wrong data from being freed below. */ for (j = i; j < naggvars; j++) zaggdata[j].dtahe_data.dtada_data = NULL; goto out; } aggvar = aggvars[(i - sortpos + naggvars) % naggvars]; /* * First, the easy bit. To maintain compatibility with * consumers that pull the compiler-generated ID out of the * data, we put that ID at the top of the zero-filled data. */ rec = &aggdesc->dtagd_rec[0]; /* LINTED - alignment */ *((dtrace_aggvarid_t *)(zdata + rec->dtrd_offset)) = aggvar; rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; /* * Now for the more complicated part. If (and only if) this * is an lquantize() aggregating action, zero-filled data is * not equivalent to an empty record: we must also get the * parameters for the lquantize(). */ if (rec->dtrd_action == DTRACEAGG_LQUANTIZE) { if (aggdata->dtada_data != NULL) { /* * The easier case here is if we actually have * some prototype data -- in which case we * manually dig it out of the aggregation * record. */ /* LINTED - alignment */ larg = *((uint64_t *)(aggdata->dtada_data + rec->dtrd_offset)); } else { /* * We don't have any prototype data. As a * result, we know that we _do_ have the * compiler-generated information. (If this * were an anonymous enabling, all of our * zero-filled data would have prototype data * -- either directly or indirectly.) So as * gross as it is, we'll grovel around in the * compiler-generated information to find the * lquantize() parameters. */ dtrace_stmtdesc_t *sdp; dt_ident_t *aid; dt_idsig_t *isp; sdp = (dtrace_stmtdesc_t *)(uintptr_t) aggdesc->dtagd_rec[0].dtrd_uarg; aid = sdp->dtsd_aggdata; isp = (dt_idsig_t *)aid->di_data; assert(isp->dis_auxinfo != 0); larg = isp->dis_auxinfo; } /* LINTED - alignment */ *((uint64_t *)(zdata + rec->dtrd_offset)) = larg; } aggdata->dtada_data = zdata; } /* * Now that we've dealt with setting up our zero-filled data, we can * allocate our sorted array, and take another pass over the data to * fill it. */ sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *)); if (sorted == NULL) goto out; for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) { dtrace_aggvarid_t id; if ((id = dt_aggregate_aggvarid(h)) > max || !map[id]) continue; sorted[i++] = h; } assert(i == nentries); /* * We've loaded our array; now we need to sort by value to allow us * to create bundles of like value. We're going to acquire the * dt_qsort_lock here, and hold it across all of our subsequent * comparison and sorting. */ (void) pthread_mutex_lock(&dt_qsort_lock); qsort(sorted, nentries, sizeof (dt_ahashent_t *), dt_aggregate_keyvarcmp); /* * Now we need to go through and create bundles. Because the number * of bundles is bounded by the size of the sorted array, we're going * to reuse the underlying storage. And note that "bundle" is an * array of pointers to arrays of pointers to dt_ahashent_t -- making * its type (regrettably) "dt_ahashent_t ***". (Regrettable because * '*' -- like '_' and 'X' -- should never appear in triplicate in * an ideal world.) */ bundle = (dt_ahashent_t ***)sorted; for (i = 1, start = 0; i <= nentries; i++) { if (i < nentries && dt_aggregate_keycmp(&sorted[i], &sorted[i - 1]) == 0) continue; /* * We have a bundle boundary. Everything from start to * (i - 1) belongs in one bundle. */ assert(i - start <= naggvars); bundlesize = (naggvars + 2) * sizeof (dt_ahashent_t *); if ((nbundle = dt_zalloc(dtp, bundlesize)) == NULL) { (void) pthread_mutex_unlock(&dt_qsort_lock); goto out; } for (j = start; j < i; j++) { dtrace_aggvarid_t id = dt_aggregate_aggvarid(sorted[j]); assert(id <= max); assert(map[id] != 0); assert(map[id] - 1 < naggvars); assert(nbundle[map[id] - 1] == NULL); nbundle[map[id] - 1] = sorted[j]; if (nbundle[naggvars] == NULL) nbundle[naggvars] = sorted[j]; } for (j = 0; j < naggvars; j++) { if (nbundle[j] != NULL) continue; /* * Before we assume that this aggregation variable * isn't present (and fall back to using the * zero-filled data allocated earlier), check the * remap. If we have a remapping, we'll drop it in * here. Note that we might be remapping an * aggregation variable that isn't present for this * key; in this case, the aggregation data that we * copy will point to the zeroed data. */ if (remap != NULL && remap[j]) { assert(remap[j] - 1 < j); assert(nbundle[remap[j] - 1] != NULL); nbundle[j] = nbundle[remap[j] - 1]; } else { nbundle[j] = &zaggdata[j]; } } bundle[nbundles++] = nbundle; start = i; } /* * Now we need to re-sort based on the first value. */ dt_aggregate_qsort(dtp, bundle, nbundles, sizeof (dt_ahashent_t **), dt_aggregate_bundlecmp); (void) pthread_mutex_unlock(&dt_qsort_lock); /* * We're done! Now we just need to go back over the sorted bundles, * calling the function. */ data = alloca((naggvars + 1) * sizeof (dtrace_aggdata_t *)); for (i = 0; i < nbundles; i++) { for (j = 0; j < naggvars; j++) data[j + 1] = NULL; for (j = 0; j < naggvars; j++) { int ndx = j - sortpos; if (ndx < 0) ndx += naggvars; assert(bundle[i][ndx] != NULL); data[j + 1] = &bundle[i][ndx]->dtahe_data; } for (j = 0; j < naggvars; j++) assert(data[j + 1] != NULL); /* * The representative key is the last element in the bundle. * Assert that we have one, and then set it to be the first * element of data. */ assert(bundle[i][j] != NULL); data[0] = &bundle[i][j]->dtahe_data; if ((rval = func(data, naggvars + 1, arg)) == -1) goto out; } rval = 0; out: for (i = 0; i < nbundles; i++) dt_free(dtp, bundle[i]); if (zaggdata != NULL) { for (i = 0; i < naggvars; i++) dt_free(dtp, zaggdata[i].dtahe_data.dtada_data); } dt_free(dtp, zaggdata); dt_free(dtp, sorted); dt_free(dtp, remap); dt_free(dtp, map); return (rval); } int dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp, dtrace_aggregate_walk_f *func) { dt_print_aggdata_t pd; + + bzero(&pd, sizeof (pd)); pd.dtpa_dtp = dtp; pd.dtpa_fp = fp; pd.dtpa_allunprint = 1; if (func == NULL) func = dtrace_aggregate_walk_sorted; if ((*func)(dtp, dt_print_agg, &pd) == -1) return (dt_set_errno(dtp, dtp->dt_errno)); return (0); } void dtrace_aggregate_clear(dtrace_hdl_t *dtp) { dt_aggregate_t *agp = &dtp->dt_aggregate; dt_ahash_t *hash = &agp->dtat_hash; dt_ahashent_t *h; dtrace_aggdata_t *data; dtrace_aggdesc_t *aggdesc; dtrace_recdesc_t *rec; int i, max_cpus = agp->dtat_maxcpu; for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { aggdesc = h->dtahe_data.dtada_desc; rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; data = &h->dtahe_data; bzero(&data->dtada_data[rec->dtrd_offset], rec->dtrd_size); if (data->dtada_percpu == NULL) continue; for (i = 0; i < max_cpus; i++) bzero(data->dtada_percpu[i], rec->dtrd_size); } } void dt_aggregate_destroy(dtrace_hdl_t *dtp) { dt_aggregate_t *agp = &dtp->dt_aggregate; dt_ahash_t *hash = &agp->dtat_hash; dt_ahashent_t *h, *next; dtrace_aggdata_t *aggdata; int i, max_cpus = agp->dtat_maxcpu; if (hash->dtah_hash == NULL) { assert(hash->dtah_all == NULL); } else { free(hash->dtah_hash); for (h = hash->dtah_all; h != NULL; h = next) { next = h->dtahe_nextall; aggdata = &h->dtahe_data; if (aggdata->dtada_percpu != NULL) { for (i = 0; i < max_cpus; i++) free(aggdata->dtada_percpu[i]); free(aggdata->dtada_percpu); } free(aggdata->dtada_data); free(h); } hash->dtah_hash = NULL; hash->dtah_all = NULL; hash->dtah_size = 0; } free(agp->dtat_buf.dtbd_data); free(agp->dtat_cpus); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_as.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_as.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_as.c (revision 268578) @@ -1,501 +1,503 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include #include #include #include #include void dt_irlist_create(dt_irlist_t *dlp) { bzero(dlp, sizeof (dt_irlist_t)); dlp->dl_label = 1; } void dt_irlist_destroy(dt_irlist_t *dlp) { dt_irnode_t *dip, *nip; for (dip = dlp->dl_list; dip != NULL; dip = nip) { nip = dip->di_next; free(dip); } } void dt_irlist_append(dt_irlist_t *dlp, dt_irnode_t *dip) { if (dlp->dl_last != NULL) dlp->dl_last->di_next = dip; else dlp->dl_list = dip; dlp->dl_last = dip; if (dip->di_label == DT_LBL_NONE || dip->di_instr != DIF_INSTR_NOP) dlp->dl_len++; /* don't count forward refs in instr count */ } uint_t dt_irlist_label(dt_irlist_t *dlp) { return (dlp->dl_label++); } /*ARGSUSED*/ static int dt_countvar(dt_idhash_t *dhp, dt_ident_t *idp, void *data) { size_t *np = data; if (idp->di_flags & (DT_IDFLG_DIFR | DT_IDFLG_DIFW)) (*np)++; /* include variable in vartab */ return (0); } /*ARGSUSED*/ static int dt_copyvar(dt_idhash_t *dhp, dt_ident_t *idp, void *data) { dt_pcb_t *pcb = data; dtrace_difv_t *dvp; ssize_t stroff; dt_node_t dn; if (!(idp->di_flags & (DT_IDFLG_DIFR | DT_IDFLG_DIFW))) return (0); /* omit variable from vartab */ dvp = &pcb->pcb_difo->dtdo_vartab[pcb->pcb_asvidx++]; stroff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name); if (stroff == -1L) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); if (stroff > DIF_STROFF_MAX) longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG); dvp->dtdv_name = (uint_t)stroff; dvp->dtdv_id = idp->di_id; dvp->dtdv_flags = 0; dvp->dtdv_kind = (idp->di_kind == DT_IDENT_ARRAY) ? DIFV_KIND_ARRAY : DIFV_KIND_SCALAR; if (idp->di_flags & DT_IDFLG_LOCAL) dvp->dtdv_scope = DIFV_SCOPE_LOCAL; else if (idp->di_flags & DT_IDFLG_TLS) dvp->dtdv_scope = DIFV_SCOPE_THREAD; else dvp->dtdv_scope = DIFV_SCOPE_GLOBAL; if (idp->di_flags & DT_IDFLG_DIFR) dvp->dtdv_flags |= DIFV_F_REF; if (idp->di_flags & DT_IDFLG_DIFW) dvp->dtdv_flags |= DIFV_F_MOD; bzero(&dn, sizeof (dn)); - dt_node_type_assign(&dn, idp->di_ctfp, idp->di_type); + dt_node_type_assign(&dn, idp->di_ctfp, idp->di_type, B_FALSE); dt_node_diftype(pcb->pcb_hdl, &dn, &dvp->dtdv_type); idp->di_flags &= ~(DT_IDFLG_DIFR | DT_IDFLG_DIFW); return (0); } static ssize_t dt_copystr(const char *s, size_t n, size_t off, dt_pcb_t *pcb) { bcopy(s, pcb->pcb_difo->dtdo_strtab + off, n); return (n); } /* * Rewrite the xlate/xlarg instruction at dtdo_buf[i] so that the instruction's * xltab index reflects the offset 'xi' of the assigned dtdo_xlmtab[] location. * We track the cumulative references to translators and members in the pcb's * pcb_asxrefs[] array, a two-dimensional array of bitmaps indexed by the * global translator id and then by the corresponding translator member id. */ static void dt_as_xlate(dt_pcb_t *pcb, dtrace_difo_t *dp, uint_t i, uint_t xi, dt_node_t *dnp) { dtrace_hdl_t *dtp = pcb->pcb_hdl; dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator; assert(i < dp->dtdo_len); assert(xi < dp->dtdo_xlmlen); assert(dnp->dn_kind == DT_NODE_MEMBER); assert(dnp->dn_membexpr->dn_kind == DT_NODE_XLATOR); assert(dxp->dx_id < dtp->dt_xlatorid); assert(dnp->dn_membid < dxp->dx_nmembers); if (pcb->pcb_asxrefs == NULL) { pcb->pcb_asxreflen = dtp->dt_xlatorid; pcb->pcb_asxrefs = dt_zalloc(dtp, sizeof (ulong_t *) * pcb->pcb_asxreflen); if (pcb->pcb_asxrefs == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); } if (pcb->pcb_asxrefs[dxp->dx_id] == NULL) { pcb->pcb_asxrefs[dxp->dx_id] = dt_zalloc(dtp, BT_SIZEOFMAP(dxp->dx_nmembers)); if (pcb->pcb_asxrefs[dxp->dx_id] == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); } dp->dtdo_buf[i] = DIF_INSTR_XLATE( DIF_INSTR_OP(dp->dtdo_buf[i]), xi, DIF_INSTR_RD(dp->dtdo_buf[i])); BT_SET(pcb->pcb_asxrefs[dxp->dx_id], dnp->dn_membid); dp->dtdo_xlmtab[xi] = dnp; } static void dt_as_undef(const dt_ident_t *idp, uint_t offset) { const char *kind, *mark = (idp->di_flags & DT_IDFLG_USER) ? "``" : "`"; const dtrace_syminfo_t *dts = idp->di_data; if (idp->di_flags & DT_IDFLG_USER) kind = "user"; else if (idp->di_flags & DT_IDFLG_PRIM) kind = "primary kernel"; else kind = "loadable kernel"; yylineno = idp->di_lineno; xyerror(D_ASRELO, "relocation remains against %s symbol %s%s%s (offset " "0x%x)\n", kind, dts->dts_object, mark, dts->dts_name, offset); } dtrace_difo_t * dt_as(dt_pcb_t *pcb) { dtrace_hdl_t *dtp = pcb->pcb_hdl; dt_irlist_t *dlp = &pcb->pcb_ir; uint_t *labels = NULL; dt_irnode_t *dip; dtrace_difo_t *dp; dt_ident_t *idp; size_t n = 0; uint_t i; uint_t kmask, kbits, umask, ubits; uint_t krel = 0, urel = 0, xlrefs = 0; /* * Select bitmasks based upon the desired symbol linking policy. We * test (di_extern->di_flags & xmask) == xbits to determine if the * symbol should have a relocation entry generated in the loop below. * * DT_LINK_KERNEL = kernel symbols static, user symbols dynamic * DT_LINK_PRIMARY = primary kernel symbols static, others dynamic * DT_LINK_DYNAMIC = all symbols dynamic * DT_LINK_STATIC = all symbols static * * By 'static' we mean that we use the symbol's value at compile-time * in the final DIF. By 'dynamic' we mean that we create a relocation * table entry for the symbol's value so it can be relocated later. */ switch (dtp->dt_linkmode) { case DT_LINK_KERNEL: kmask = 0; kbits = -1u; umask = DT_IDFLG_USER; ubits = DT_IDFLG_USER; break; case DT_LINK_PRIMARY: kmask = DT_IDFLG_USER | DT_IDFLG_PRIM; kbits = 0; umask = DT_IDFLG_USER; ubits = DT_IDFLG_USER; break; case DT_LINK_DYNAMIC: kmask = DT_IDFLG_USER; kbits = 0; umask = DT_IDFLG_USER; ubits = DT_IDFLG_USER; break; case DT_LINK_STATIC: kmask = umask = 0; kbits = ubits = -1u; break; default: xyerror(D_UNKNOWN, "internal error -- invalid link mode %u\n", dtp->dt_linkmode); } assert(pcb->pcb_difo == NULL); pcb->pcb_difo = dt_zalloc(dtp, sizeof (dtrace_difo_t)); if ((dp = pcb->pcb_difo) == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); dp->dtdo_buf = dt_alloc(dtp, sizeof (dif_instr_t) * dlp->dl_len); if (dp->dtdo_buf == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); if ((labels = dt_alloc(dtp, sizeof (uint_t) * dlp->dl_label)) == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); /* * Make an initial pass through the instruction list, filling in the * instruction buffer with valid instructions and skipping labeled nops. * While doing this, we also fill in our labels[] translation table * and we count up the number of relocation table entries we will need. */ for (i = 0, dip = dlp->dl_list; dip != NULL; dip = dip->di_next) { if (dip->di_label != DT_LBL_NONE) labels[dip->di_label] = i; if (dip->di_label == DT_LBL_NONE || dip->di_instr != DIF_INSTR_NOP) dp->dtdo_buf[i++] = dip->di_instr; if (dip->di_extern == NULL) continue; /* no external references needed */ switch (DIF_INSTR_OP(dip->di_instr)) { case DIF_OP_SETX: idp = dip->di_extern; if ((idp->di_flags & kmask) == kbits) krel++; else if ((idp->di_flags & umask) == ubits) urel++; break; case DIF_OP_XLATE: case DIF_OP_XLARG: xlrefs++; break; default: xyerror(D_UNKNOWN, "unexpected assembler relocation " "for opcode 0x%x\n", DIF_INSTR_OP(dip->di_instr)); } } assert(i == dlp->dl_len); dp->dtdo_len = dlp->dl_len; /* * Make a second pass through the instructions, relocating each branch * label to the index of the final instruction in the buffer and noting * any other instruction-specific DIFO flags such as dtdo_destructive. */ for (i = 0; i < dp->dtdo_len; i++) { dif_instr_t instr = dp->dtdo_buf[i]; uint_t op = DIF_INSTR_OP(instr); if (op == DIF_OP_CALL) { if (DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUT || DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUTSTR) dp->dtdo_destructive = 1; continue; } if (op >= DIF_OP_BA && op <= DIF_OP_BLEU) { assert(DIF_INSTR_LABEL(instr) < dlp->dl_label); dp->dtdo_buf[i] = DIF_INSTR_BRANCH(op, labels[DIF_INSTR_LABEL(instr)]); } } dt_free(dtp, labels); pcb->pcb_asvidx = 0; /* * Allocate memory for the appropriate number of variable records and * then fill in each variable record. As we populate the variable * table we insert the corresponding variable names into the strtab. */ (void) dt_idhash_iter(dtp->dt_tls, dt_countvar, &n); (void) dt_idhash_iter(dtp->dt_globals, dt_countvar, &n); (void) dt_idhash_iter(pcb->pcb_locals, dt_countvar, &n); if (n != 0) { dp->dtdo_vartab = dt_alloc(dtp, n * sizeof (dtrace_difv_t)); dp->dtdo_varlen = (uint32_t)n; if (dp->dtdo_vartab == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); (void) dt_idhash_iter(dtp->dt_tls, dt_copyvar, pcb); (void) dt_idhash_iter(dtp->dt_globals, dt_copyvar, pcb); (void) dt_idhash_iter(pcb->pcb_locals, dt_copyvar, pcb); } /* * Allocate memory for the appropriate number of relocation table * entries based upon our kernel and user counts from the first pass. */ if (krel != 0) { dp->dtdo_kreltab = dt_alloc(dtp, krel * sizeof (dof_relodesc_t)); dp->dtdo_krelen = krel; if (dp->dtdo_kreltab == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); } if (urel != 0) { dp->dtdo_ureltab = dt_alloc(dtp, urel * sizeof (dof_relodesc_t)); dp->dtdo_urelen = urel; if (dp->dtdo_ureltab == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); } if (xlrefs != 0) { dp->dtdo_xlmtab = dt_zalloc(dtp, sizeof (dt_node_t *) * xlrefs); dp->dtdo_xlmlen = xlrefs; if (dp->dtdo_xlmtab == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); } /* * If any relocations are needed, make another pass through the * instruction list and fill in the relocation table entries. */ if (krel + urel + xlrefs != 0) { uint_t knodef = pcb->pcb_cflags & DTRACE_C_KNODEF; uint_t unodef = pcb->pcb_cflags & DTRACE_C_UNODEF; dof_relodesc_t *krp = dp->dtdo_kreltab; dof_relodesc_t *urp = dp->dtdo_ureltab; dt_node_t **xlp = dp->dtdo_xlmtab; i = 0; /* dtdo_buf[] index */ for (dip = dlp->dl_list; dip != NULL; dip = dip->di_next) { dof_relodesc_t *rp; ssize_t soff; uint_t nodef; if (dip->di_label != DT_LBL_NONE && dip->di_instr == DIF_INSTR_NOP) continue; /* skip label declarations */ i++; /* advance dtdo_buf[] index */ if (DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLATE || DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLARG) { assert(dp->dtdo_buf[i - 1] == dip->di_instr); dt_as_xlate(pcb, dp, i - 1, (uint_t) (xlp++ - dp->dtdo_xlmtab), dip->di_extern); continue; } if ((idp = dip->di_extern) == NULL) continue; /* no relocation entry needed */ if ((idp->di_flags & kmask) == kbits) { nodef = knodef; rp = krp++; } else if ((idp->di_flags & umask) == ubits) { nodef = unodef; rp = urp++; } else continue; if (!nodef) dt_as_undef(idp, i); assert(DIF_INSTR_OP(dip->di_instr) == DIF_OP_SETX); soff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name); if (soff == -1L) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); if (soff > DIF_STROFF_MAX) longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG); rp->dofr_name = (dof_stridx_t)soff; rp->dofr_type = DOF_RELO_SETX; rp->dofr_offset = DIF_INSTR_INTEGER(dip->di_instr) * sizeof (uint64_t); rp->dofr_data = 0; } assert(krp == dp->dtdo_kreltab + dp->dtdo_krelen); assert(urp == dp->dtdo_ureltab + dp->dtdo_urelen); assert(xlp == dp->dtdo_xlmtab + dp->dtdo_xlmlen); assert(i == dp->dtdo_len); } /* * Allocate memory for the compiled string table and then copy the * chunks from the string table into the final string buffer. */ if ((n = dt_strtab_size(pcb->pcb_strtab)) != 0) { if ((dp->dtdo_strtab = dt_alloc(dtp, n)) == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); (void) dt_strtab_write(pcb->pcb_strtab, (dt_strtab_write_f *)dt_copystr, pcb); dp->dtdo_strlen = (uint32_t)n; } /* * Allocate memory for the compiled integer table and then copy the * integer constants from the table into the final integer buffer. */ if ((n = dt_inttab_size(pcb->pcb_inttab)) != 0) { if ((dp->dtdo_inttab = dt_alloc(dtp, n * sizeof (uint64_t))) == NULL) longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); dt_inttab_write(pcb->pcb_inttab, dp->dtdo_inttab); dp->dtdo_intlen = (uint32_t)n; } /* * Fill in the DIFO return type from the type associated with the * node saved in pcb_dret, and then clear pcb_difo and pcb_dret * now that the assembler has completed successfully. */ dt_node_diftype(dtp, pcb->pcb_dret, &dp->dtdo_rtype); pcb->pcb_difo = NULL; pcb->pcb_dret = NULL; if (pcb->pcb_cflags & DTRACE_C_DIFV) dt_dis(dp, stderr); return (dp); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_cc.c (revision 268578) @@ -1,2605 +1,2626 @@ /* * 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, Joyent Inc. All rights reserved. + * Copyright (c) 2013, Joyent Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ /* * 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. + * 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_printt(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_PRINTT_SIZE, "printt( ) argument #1 must " "be a non-zero positive integral constant expression\n"); } if (addr == NULL || addr->dn_kind != DT_NODE_FUNC || addr->dn_ident != dt_idhash_lookup(dtp->dt_globals, "typeref")) { dnerror(addr, D_PRINTT_ADDR, "printt( ) argument #2 is incompatible with " "prototype:\n\tprototype: typeref()\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_PRINTT; ap->dtad_difo->dtdo_rtype.dtdt_flags |= DIF_TF_BYREF; /* * Allow additional buffer space for the data size, type size, * type string length and a stab in the dark (32 bytes) for the * type string. The type string is part of the typeref() that * this action references. */ ap->dtad_difo->dtdo_rtype.dtdt_size = size->dn_value + 3 * sizeof(uintptr_t) + 32; } 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_PRINTT: dt_action_printt(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; 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) 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; /* * 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. */ if (isdigit(pdp->dtpd_provider[strlen(pdp->dtpd_provider) - 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(); #if defined(sun) 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; #if defined(sun) 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); } #if defined(sun) (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; #if defined(sun) 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) { #if !defined(sun) 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); continue; } dtp->dt_filetag = fname; if (dt_lib_depend_add(dtp, &dtp->dt_lib_dep, fname) != 0) 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))) 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 *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); } /* * 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: 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); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c (revision 268578) @@ -1,2994 +1,3383 @@ /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* - * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include #if defined(sun) #include #endif #include #include #if !defined(sun) #include #endif #define DT_MASK_LO 0x00000000FFFFFFFFULL /* * We declare this here because (1) we need it and (2) we want to avoid a * dependency on libm in libdtrace. */ static long double dt_fabsl(long double x) { if (x < 0) return (-x); return (x); } +static int +dt_ndigits(long long val) +{ + int rval = 1; + long long cmp = 10; + + if (val < 0) { + val = val == INT64_MIN ? INT64_MAX : -val; + rval++; + } + + while (val > cmp && cmp > 0) { + rval++; + cmp *= 10; + } + + return (rval < 4 ? 4 : rval); +} + /* * 128-bit arithmetic functions needed to support the stddev() aggregating * action. */ static int dt_gt_128(uint64_t *a, uint64_t *b) { return (a[1] > b[1] || (a[1] == b[1] && a[0] > b[0])); } static int dt_ge_128(uint64_t *a, uint64_t *b) { return (a[1] > b[1] || (a[1] == b[1] && a[0] >= b[0])); } static int dt_le_128(uint64_t *a, uint64_t *b) { return (a[1] < b[1] || (a[1] == b[1] && a[0] <= b[0])); } /* * Shift the 128-bit value in a by b. If b is positive, shift left. * If b is negative, shift right. */ static void dt_shift_128(uint64_t *a, int b) { uint64_t mask; if (b == 0) return; if (b < 0) { b = -b; if (b >= 64) { a[0] = a[1] >> (b - 64); a[1] = 0; } else { a[0] >>= b; mask = 1LL << (64 - b); mask -= 1; a[0] |= ((a[1] & mask) << (64 - b)); a[1] >>= b; } } else { if (b >= 64) { a[1] = a[0] << (b - 64); a[0] = 0; } else { a[1] <<= b; mask = a[0] >> (64 - b); a[1] |= mask; a[0] <<= b; } } } static int dt_nbits_128(uint64_t *a) { int nbits = 0; uint64_t tmp[2]; uint64_t zero[2] = { 0, 0 }; tmp[0] = a[0]; tmp[1] = a[1]; dt_shift_128(tmp, -1); while (dt_gt_128(tmp, zero)) { dt_shift_128(tmp, -1); nbits++; } return (nbits); } static void dt_subtract_128(uint64_t *minuend, uint64_t *subtrahend, uint64_t *difference) { uint64_t result[2]; result[0] = minuend[0] - subtrahend[0]; result[1] = minuend[1] - subtrahend[1] - (minuend[0] < subtrahend[0] ? 1 : 0); difference[0] = result[0]; difference[1] = result[1]; } static void dt_add_128(uint64_t *addend1, uint64_t *addend2, uint64_t *sum) { uint64_t result[2]; result[0] = addend1[0] + addend2[0]; result[1] = addend1[1] + addend2[1] + (result[0] < addend1[0] || result[0] < addend2[0] ? 1 : 0); sum[0] = result[0]; sum[1] = result[1]; } /* * The basic idea is to break the 2 64-bit values into 4 32-bit values, * use native multiplication on those, and then re-combine into the * resulting 128-bit value. * * (hi1 << 32 + lo1) * (hi2 << 32 + lo2) = * hi1 * hi2 << 64 + * hi1 * lo2 << 32 + * hi2 * lo1 << 32 + * lo1 * lo2 */ static void dt_multiply_128(uint64_t factor1, uint64_t factor2, uint64_t *product) { uint64_t hi1, hi2, lo1, lo2; uint64_t tmp[2]; hi1 = factor1 >> 32; hi2 = factor2 >> 32; lo1 = factor1 & DT_MASK_LO; lo2 = factor2 & DT_MASK_LO; product[0] = lo1 * lo2; product[1] = hi1 * hi2; tmp[0] = hi1 * lo2; tmp[1] = 0; dt_shift_128(tmp, 32); dt_add_128(product, tmp, product); tmp[0] = hi2 * lo1; tmp[1] = 0; dt_shift_128(tmp, 32); dt_add_128(product, tmp, product); } /* * This is long-hand division. * * We initialize subtrahend by shifting divisor left as far as possible. We * loop, comparing subtrahend to dividend: if subtrahend is smaller, we * subtract and set the appropriate bit in the result. We then shift * subtrahend right by one bit for the next comparison. */ static void dt_divide_128(uint64_t *dividend, uint64_t divisor, uint64_t *quotient) { uint64_t result[2] = { 0, 0 }; uint64_t remainder[2]; uint64_t subtrahend[2]; uint64_t divisor_128[2]; uint64_t mask[2] = { 1, 0 }; int log = 0; assert(divisor != 0); divisor_128[0] = divisor; divisor_128[1] = 0; remainder[0] = dividend[0]; remainder[1] = dividend[1]; subtrahend[0] = divisor; subtrahend[1] = 0; while (divisor > 0) { log++; divisor >>= 1; } dt_shift_128(subtrahend, 128 - log); dt_shift_128(mask, 128 - log); while (dt_ge_128(remainder, divisor_128)) { if (dt_ge_128(remainder, subtrahend)) { dt_subtract_128(remainder, subtrahend, remainder); result[0] |= mask[0]; result[1] |= mask[1]; } dt_shift_128(subtrahend, -1); dt_shift_128(mask, -1); } quotient[0] = result[0]; quotient[1] = result[1]; } /* * This is the long-hand method of calculating a square root. * The algorithm is as follows: * * 1. Group the digits by 2 from the right. * 2. Over the leftmost group, find the largest single-digit number * whose square is less than that group. * 3. Subtract the result of the previous step (2 or 4, depending) and * bring down the next two-digit group. * 4. For the result R we have so far, find the largest single-digit number * x such that 2 * R * 10 * x + x^2 is less than the result from step 3. * (Note that this is doubling R and performing a decimal left-shift by 1 * and searching for the appropriate decimal to fill the one's place.) * The value x is the next digit in the square root. * Repeat steps 3 and 4 until the desired precision is reached. (We're * dealing with integers, so the above is sufficient.) * * In decimal, the square root of 582,734 would be calculated as so: * * __7__6__3 * | 58 27 34 * -49 (7^2 == 49 => 7 is the first digit in the square root) * -- * 9 27 (Subtract and bring down the next group.) * 146 8 76 (2 * 7 * 10 * 6 + 6^2 == 876 => 6 is the next digit in * ----- the square root) * 51 34 (Subtract and bring down the next group.) * 1523 45 69 (2 * 76 * 10 * 3 + 3^2 == 4569 => 3 is the next digit in * ----- the square root) * 5 65 (remainder) * * The above algorithm applies similarly in binary, but note that the * only possible non-zero value for x in step 4 is 1, so step 4 becomes a * simple decision: is 2 * R * 2 * 1 + 1^2 (aka R << 2 + 1) less than the * preceding difference? * * In binary, the square root of 11011011 would be calculated as so: * * __1__1__1__0 * | 11 01 10 11 * 01 (0 << 2 + 1 == 1 < 11 => this bit is 1) * -- * 10 01 10 11 * 101 1 01 (1 << 2 + 1 == 101 < 1001 => next bit is 1) * ----- * 1 00 10 11 * 1101 11 01 (11 << 2 + 1 == 1101 < 10010 => next bit is 1) * ------- * 1 01 11 * 11101 1 11 01 (111 << 2 + 1 == 11101 > 10111 => last bit is 0) * */ static uint64_t dt_sqrt_128(uint64_t *square) { uint64_t result[2] = { 0, 0 }; uint64_t diff[2] = { 0, 0 }; uint64_t one[2] = { 1, 0 }; uint64_t next_pair[2]; uint64_t next_try[2]; uint64_t bit_pairs, pair_shift; int i; bit_pairs = dt_nbits_128(square) / 2; pair_shift = bit_pairs * 2; for (i = 0; i <= bit_pairs; i++) { /* * Bring down the next pair of bits. */ next_pair[0] = square[0]; next_pair[1] = square[1]; dt_shift_128(next_pair, -pair_shift); next_pair[0] &= 0x3; next_pair[1] = 0; dt_shift_128(diff, 2); dt_add_128(diff, next_pair, diff); /* * next_try = R << 2 + 1 */ next_try[0] = result[0]; next_try[1] = result[1]; dt_shift_128(next_try, 2); dt_add_128(next_try, one, next_try); if (dt_le_128(next_try, diff)) { dt_subtract_128(diff, next_try, diff); dt_shift_128(result, 1); dt_add_128(result, one, result); } else { dt_shift_128(result, 1); } pair_shift -= 2; } assert(result[1] == 0); return (result[0]); } uint64_t dt_stddev(uint64_t *data, uint64_t normal) { uint64_t avg_of_squares[2]; uint64_t square_of_avg[2]; int64_t norm_avg; uint64_t diff[2]; /* * The standard approximation for standard deviation is * sqrt(average(x**2) - average(x)**2), i.e. the square root * of the average of the squares minus the square of the average. */ dt_divide_128(data + 2, normal, avg_of_squares); dt_divide_128(avg_of_squares, data[0], avg_of_squares); norm_avg = (int64_t)data[1] / (int64_t)normal / (int64_t)data[0]; if (norm_avg < 0) norm_avg = -norm_avg; dt_multiply_128((uint64_t)norm_avg, (uint64_t)norm_avg, square_of_avg); dt_subtract_128(avg_of_squares, square_of_avg, diff); return (dt_sqrt_128(diff)); } static int dt_flowindent(dtrace_hdl_t *dtp, dtrace_probedata_t *data, dtrace_epid_t last, dtrace_bufdesc_t *buf, size_t offs) { dtrace_probedesc_t *pd = data->dtpda_pdesc, *npd; dtrace_eprobedesc_t *epd = data->dtpda_edesc, *nepd; char *p = pd->dtpd_provider, *n = pd->dtpd_name, *sub; dtrace_flowkind_t flow = DTRACEFLOW_NONE; const char *str = NULL; static const char *e_str[2] = { " -> ", " => " }; static const char *r_str[2] = { " <- ", " <= " }; static const char *ent = "entry", *ret = "return"; static int entlen = 0, retlen = 0; dtrace_epid_t next, id = epd->dtepd_epid; int rval; if (entlen == 0) { assert(retlen == 0); entlen = strlen(ent); retlen = strlen(ret); } /* * If the name of the probe is "entry" or ends with "-entry", we * treat it as an entry; if it is "return" or ends with "-return", * we treat it as a return. (This allows application-provided probes * like "method-entry" or "function-entry" to participate in flow * indentation -- without accidentally misinterpreting popular probe * names like "carpentry", "gentry" or "Coventry".) */ if ((sub = strstr(n, ent)) != NULL && sub[entlen] == '\0' && (sub == n || sub[-1] == '-')) { flow = DTRACEFLOW_ENTRY; str = e_str[strcmp(p, "syscall") == 0]; } else if ((sub = strstr(n, ret)) != NULL && sub[retlen] == '\0' && (sub == n || sub[-1] == '-')) { flow = DTRACEFLOW_RETURN; str = r_str[strcmp(p, "syscall") == 0]; } /* * If we're going to indent this, we need to check the ID of our last * call. If we're looking at the same probe ID but a different EPID, * we _don't_ want to indent. (Yes, there are some minor holes in * this scheme -- it's a heuristic.) */ if (flow == DTRACEFLOW_ENTRY) { if ((last != DTRACE_EPIDNONE && id != last && pd->dtpd_id == dtp->dt_pdesc[last]->dtpd_id)) flow = DTRACEFLOW_NONE; } /* * If we're going to unindent this, it's more difficult to see if * we don't actually want to unindent it -- we need to look at the * _next_ EPID. */ if (flow == DTRACEFLOW_RETURN) { offs += epd->dtepd_size; do { if (offs >= buf->dtbd_size) goto out; next = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); if (next == DTRACE_EPIDNONE) offs += sizeof (id); } while (next == DTRACE_EPIDNONE); if ((rval = dt_epid_lookup(dtp, next, &nepd, &npd)) != 0) return (rval); if (next != id && npd->dtpd_id == pd->dtpd_id) flow = DTRACEFLOW_NONE; } out: if (flow == DTRACEFLOW_ENTRY || flow == DTRACEFLOW_RETURN) { data->dtpda_prefix = str; } else { data->dtpda_prefix = "| "; } if (flow == DTRACEFLOW_RETURN && data->dtpda_indent > 0) data->dtpda_indent -= 2; data->dtpda_flow = flow; return (0); } static int dt_nullprobe() { return (DTRACE_CONSUME_THIS); } static int dt_nullrec() { return (DTRACE_CONSUME_NEXT); } -int +static void +dt_quantize_total(dtrace_hdl_t *dtp, int64_t datum, long double *total) +{ + long double val = dt_fabsl((long double)datum); + + if (dtp->dt_options[DTRACEOPT_AGGZOOM] == DTRACEOPT_UNSET) { + *total += val; + return; + } + + /* + * If we're zooming in on an aggregation, we want the height of the + * highest value to be approximately 95% of total bar height -- so we + * adjust up by the reciprocal of DTRACE_AGGZOOM_MAX when comparing to + * our highest value. + */ + val *= 1 / DTRACE_AGGZOOM_MAX; + + if (*total < val) + *total = val; +} + +static int +dt_print_quanthdr(dtrace_hdl_t *dtp, FILE *fp, int width) +{ + return (dt_printf(dtp, fp, "\n%*s %41s %-9s\n", + width ? width : 16, width ? "key" : "value", + "------------- Distribution -------------", "count")); +} + +static int +dt_print_quanthdr_packed(dtrace_hdl_t *dtp, FILE *fp, int width, + const dtrace_aggdata_t *aggdata, dtrace_actkind_t action) +{ + int min = aggdata->dtada_minbin, max = aggdata->dtada_maxbin; + int minwidth, maxwidth, i; + + assert(action == DTRACEAGG_QUANTIZE || action == DTRACEAGG_LQUANTIZE); + + if (action == DTRACEAGG_QUANTIZE) { + if (min != 0 && min != DTRACE_QUANTIZE_ZEROBUCKET) + min--; + + if (max < DTRACE_QUANTIZE_NBUCKETS - 1) + max++; + + minwidth = dt_ndigits(DTRACE_QUANTIZE_BUCKETVAL(min)); + maxwidth = dt_ndigits(DTRACE_QUANTIZE_BUCKETVAL(max)); + } else { + maxwidth = 8; + minwidth = maxwidth - 1; + max++; + } + + if (dt_printf(dtp, fp, "\n%*s %*s .", + width, width > 0 ? "key" : "", minwidth, "min") < 0) + return (-1); + + for (i = min; i <= max; i++) { + if (dt_printf(dtp, fp, "-") < 0) + return (-1); + } + + return (dt_printf(dtp, fp, ". %*s | count\n", -maxwidth, "max")); +} + +/* + * We use a subset of the Unicode Block Elements (U+2588 through U+258F, + * inclusive) to represent aggregations via UTF-8 -- which are expressed via + * 3-byte UTF-8 sequences. + */ +#define DTRACE_AGGUTF8_FULL 0x2588 +#define DTRACE_AGGUTF8_BASE 0x258f +#define DTRACE_AGGUTF8_LEVELS 8 + +#define DTRACE_AGGUTF8_BYTE0(val) (0xe0 | ((val) >> 12)) +#define DTRACE_AGGUTF8_BYTE1(val) (0x80 | (((val) >> 6) & 0x3f)) +#define DTRACE_AGGUTF8_BYTE2(val) (0x80 | ((val) & 0x3f)) + +static int +dt_print_quantline_utf8(dtrace_hdl_t *dtp, FILE *fp, int64_t val, + uint64_t normal, long double total) +{ + uint_t len = 40, i, whole, partial; + long double f = (dt_fabsl((long double)val) * len) / total; + const char *spaces = " "; + + whole = (uint_t)f; + partial = (uint_t)((f - (long double)(uint_t)f) * + (long double)DTRACE_AGGUTF8_LEVELS); + + if (dt_printf(dtp, fp, "|") < 0) + return (-1); + + for (i = 0; i < whole; i++) { + if (dt_printf(dtp, fp, "%c%c%c", + DTRACE_AGGUTF8_BYTE0(DTRACE_AGGUTF8_FULL), + DTRACE_AGGUTF8_BYTE1(DTRACE_AGGUTF8_FULL), + DTRACE_AGGUTF8_BYTE2(DTRACE_AGGUTF8_FULL)) < 0) + return (-1); + } + + if (partial != 0) { + partial = DTRACE_AGGUTF8_BASE - (partial - 1); + + if (dt_printf(dtp, fp, "%c%c%c", + DTRACE_AGGUTF8_BYTE0(partial), + DTRACE_AGGUTF8_BYTE1(partial), + DTRACE_AGGUTF8_BYTE2(partial)) < 0) + return (-1); + + i++; + } + + return (dt_printf(dtp, fp, "%s %-9lld\n", spaces + i, + (long long)val / normal)); +} + +static int dt_print_quantline(dtrace_hdl_t *dtp, FILE *fp, int64_t val, uint64_t normal, long double total, char positives, char negatives) { long double f; uint_t depth, len = 40; const char *ats = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"; const char *spaces = " "; assert(strlen(ats) == len && strlen(spaces) == len); assert(!(total == 0 && (positives || negatives))); assert(!(val < 0 && !negatives)); assert(!(val > 0 && !positives)); assert(!(val != 0 && total == 0)); if (!negatives) { if (positives) { + if (dtp->dt_encoding == DT_ENCODING_UTF8) { + return (dt_print_quantline_utf8(dtp, fp, val, + normal, total)); + } + f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); } else { depth = 0; } return (dt_printf(dtp, fp, "|%s%s %-9lld\n", ats + len - depth, spaces + depth, (long long)val / normal)); } if (!positives) { f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); return (dt_printf(dtp, fp, "%s%s| %-9lld\n", spaces + depth, ats + len - depth, (long long)val / normal)); } /* * If we're here, we have both positive and negative bucket values. * To express this graphically, we're going to generate both positive * and negative bars separated by a centerline. These bars are half * the size of normal quantize()/lquantize() bars, so we divide the * length in half before calculating the bar length. */ len /= 2; ats = &ats[len]; spaces = &spaces[len]; f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); if (val <= 0) { return (dt_printf(dtp, fp, "%s%s|%*s %-9lld\n", spaces + depth, ats + len - depth, len, "", (long long)val / normal)); } else { return (dt_printf(dtp, fp, "%20s|%s%s %-9lld\n", "", ats + len - depth, spaces + depth, (long long)val / normal)); } } +/* + * As with UTF-8 printing of aggregations, we use a subset of the Unicode + * Block Elements (U+2581 through U+2588, inclusive) to represent our packed + * aggregation. + */ +#define DTRACE_AGGPACK_BASE 0x2581 +#define DTRACE_AGGPACK_LEVELS 8 + +static int +dt_print_packed(dtrace_hdl_t *dtp, FILE *fp, + long double datum, long double total) +{ + static boolean_t utf8_checked = B_FALSE; + static boolean_t utf8; + char *ascii = "__xxxxXX"; + char *neg = "vvvvVV"; + unsigned int len; + long double val; + + if (!utf8_checked) { + char *term; + + /* + * We want to determine if we can reasonably emit UTF-8 for our + * packed aggregation. To do this, we will check for terminals + * that are known to be primitive to emit UTF-8 on these. + */ + utf8_checked = B_TRUE; + + if (dtp->dt_encoding == DT_ENCODING_ASCII) { + utf8 = B_FALSE; + } else if (dtp->dt_encoding == DT_ENCODING_UTF8) { + utf8 = B_TRUE; + } else if ((term = getenv("TERM")) != NULL && + (strcmp(term, "sun") == 0 || + strcmp(term, "sun-color") == 0) || + strcmp(term, "dumb") == 0) { + utf8 = B_FALSE; + } else { + utf8 = B_TRUE; + } + } + + if (datum == 0) + return (dt_printf(dtp, fp, " ")); + + if (datum < 0) { + len = strlen(neg); + val = dt_fabsl(datum * (len - 1)) / total; + return (dt_printf(dtp, fp, "%c", neg[(uint_t)(val + 0.5)])); + } + + if (utf8) { + int block = DTRACE_AGGPACK_BASE + (unsigned int)(((datum * + (DTRACE_AGGPACK_LEVELS - 1)) / total) + 0.5); + + return (dt_printf(dtp, fp, "%c%c%c", + DTRACE_AGGUTF8_BYTE0(block), + DTRACE_AGGUTF8_BYTE1(block), + DTRACE_AGGUTF8_BYTE2(block))); + } + + len = strlen(ascii); + val = (datum * (len - 1)) / total; + return (dt_printf(dtp, fp, "%c", ascii[(uint_t)(val + 0.5)])); +} + int dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, uint64_t normal) { const int64_t *data = addr; int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; long double total = 0; char positives = 0, negatives = 0; if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); while (first_bin < DTRACE_QUANTIZE_NBUCKETS - 1 && data[first_bin] == 0) first_bin++; if (first_bin == DTRACE_QUANTIZE_NBUCKETS - 1) { /* - * There isn't any data. This is possible if (and only if) - * negative increment values have been used. In this case, - * we'll print the buckets around 0. + * There isn't any data. This is possible if the aggregation + * has been clear()'d or if negative increment values have been + * used. Regardless, we'll print the buckets around 0. */ first_bin = DTRACE_QUANTIZE_ZEROBUCKET - 1; last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 1; } else { if (first_bin > 0) first_bin--; while (last_bin > 0 && data[last_bin] == 0) last_bin--; if (last_bin < DTRACE_QUANTIZE_NBUCKETS - 1) last_bin++; } for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); - total += dt_fabsl((long double)data[i]); + dt_quantize_total(dtp, data[i], &total); } - if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", - "------------- Distribution -------------", "count") < 0) + if (dt_print_quanthdr(dtp, fp, 0) < 0) return (-1); for (i = first_bin; i <= last_bin; i++) { if (dt_printf(dtp, fp, "%16lld ", (long long)DTRACE_QUANTIZE_BUCKETVAL(i)) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[i], normal, total, positives, negatives) < 0) return (-1); } return (0); } int +dt_print_quantize_packed(dtrace_hdl_t *dtp, FILE *fp, const void *addr, + size_t size, const dtrace_aggdata_t *aggdata) +{ + const int64_t *data = addr; + long double total = 0, count = 0; + int min = aggdata->dtada_minbin, max = aggdata->dtada_maxbin, i; + int64_t minval, maxval; + + if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) + return (dt_set_errno(dtp, EDT_DMISMATCH)); + + if (min != 0 && min != DTRACE_QUANTIZE_ZEROBUCKET) + min--; + + if (max < DTRACE_QUANTIZE_NBUCKETS - 1) + max++; + + minval = DTRACE_QUANTIZE_BUCKETVAL(min); + maxval = DTRACE_QUANTIZE_BUCKETVAL(max); + + if (dt_printf(dtp, fp, " %*lld :", dt_ndigits(minval), + (long long)minval) < 0) + return (-1); + + for (i = min; i <= max; i++) { + dt_quantize_total(dtp, data[i], &total); + count += data[i]; + } + + for (i = min; i <= max; i++) { + if (dt_print_packed(dtp, fp, data[i], total) < 0) + return (-1); + } + + if (dt_printf(dtp, fp, ": %*lld | %lld\n", + -dt_ndigits(maxval), (long long)maxval, (long long)count) < 0) + return (-1); + + return (0); +} + +int dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, uint64_t normal) { const int64_t *data = addr; int i, first_bin, last_bin, base; uint64_t arg; long double total = 0; uint16_t step, levels; char positives = 0, negatives = 0; if (size < sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); arg = *data++; size -= sizeof (uint64_t); base = DTRACE_LQUANTIZE_BASE(arg); step = DTRACE_LQUANTIZE_STEP(arg); levels = DTRACE_LQUANTIZE_LEVELS(arg); first_bin = 0; last_bin = levels + 1; if (size != sizeof (uint64_t) * (levels + 2)) return (dt_set_errno(dtp, EDT_DMISMATCH)); while (first_bin <= levels + 1 && data[first_bin] == 0) first_bin++; if (first_bin > levels + 1) { first_bin = 0; last_bin = 2; } else { if (first_bin > 0) first_bin--; while (last_bin > 0 && data[last_bin] == 0) last_bin--; if (last_bin < levels + 1) last_bin++; } for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); - total += dt_fabsl((long double)data[i]); + dt_quantize_total(dtp, data[i], &total); } if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", "------------- Distribution -------------", "count") < 0) return (-1); for (i = first_bin; i <= last_bin; i++) { char c[32]; int err; if (i == 0) { - (void) snprintf(c, sizeof (c), "< %d", - base / (uint32_t)normal); + (void) snprintf(c, sizeof (c), "< %d", base); err = dt_printf(dtp, fp, "%16s ", c); } else if (i == levels + 1) { (void) snprintf(c, sizeof (c), ">= %d", base + (levels * step)); err = dt_printf(dtp, fp, "%16s ", c); } else { err = dt_printf(dtp, fp, "%16d ", base + (i - 1) * step); } if (err < 0 || dt_print_quantline(dtp, fp, data[i], normal, total, positives, negatives) < 0) return (-1); } return (0); } +/*ARGSUSED*/ int +dt_print_lquantize_packed(dtrace_hdl_t *dtp, FILE *fp, const void *addr, + size_t size, const dtrace_aggdata_t *aggdata) +{ + const int64_t *data = addr; + long double total = 0, count = 0; + int min, max, base, err; + uint64_t arg; + uint16_t step, levels; + char c[32]; + unsigned int i; + + if (size < sizeof (uint64_t)) + return (dt_set_errno(dtp, EDT_DMISMATCH)); + + arg = *data++; + size -= sizeof (uint64_t); + + base = DTRACE_LQUANTIZE_BASE(arg); + step = DTRACE_LQUANTIZE_STEP(arg); + levels = DTRACE_LQUANTIZE_LEVELS(arg); + + if (size != sizeof (uint64_t) * (levels + 2)) + return (dt_set_errno(dtp, EDT_DMISMATCH)); + + min = 0; + max = levels + 1; + + if (min == 0) { + (void) snprintf(c, sizeof (c), "< %d", base); + err = dt_printf(dtp, fp, "%8s :", c); + } else { + err = dt_printf(dtp, fp, "%8d :", base + (min - 1) * step); + } + + if (err < 0) + return (-1); + + for (i = min; i <= max; i++) { + dt_quantize_total(dtp, data[i], &total); + count += data[i]; + } + + for (i = min; i <= max; i++) { + if (dt_print_packed(dtp, fp, data[i], total) < 0) + return (-1); + } + + (void) snprintf(c, sizeof (c), ">= %d", base + (levels * step)); + return (dt_printf(dtp, fp, ": %-8s | %lld\n", c, (long long)count)); +} + +int dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, uint64_t normal) { int i, first_bin, last_bin, bin = 1, order, levels; uint16_t factor, low, high, nsteps; const int64_t *data = addr; int64_t value = 1, next, step; char positives = 0, negatives = 0; long double total = 0; uint64_t arg; char c[32]; if (size < sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); arg = *data++; size -= sizeof (uint64_t); factor = DTRACE_LLQUANTIZE_FACTOR(arg); low = DTRACE_LLQUANTIZE_LOW(arg); high = DTRACE_LLQUANTIZE_HIGH(arg); nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); /* * We don't expect to be handed invalid llquantize() parameters here, * but sanity check them (to a degree) nonetheless. */ if (size > INT32_MAX || factor < 2 || low >= high || nsteps == 0 || factor > nsteps) return (dt_set_errno(dtp, EDT_DMISMATCH)); levels = (int)size / sizeof (uint64_t); first_bin = 0; last_bin = levels - 1; while (first_bin < levels && data[first_bin] == 0) first_bin++; if (first_bin == levels) { first_bin = 0; last_bin = 1; } else { if (first_bin > 0) first_bin--; while (last_bin > 0 && data[last_bin] == 0) last_bin--; if (last_bin < levels - 1) last_bin++; } for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); - total += dt_fabsl((long double)data[i]); + dt_quantize_total(dtp, data[i], &total); } if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", "------------- Distribution -------------", "count") < 0) return (-1); for (order = 0; order < low; order++) value *= factor; next = value * factor; step = next > nsteps ? next / nsteps : 1; if (first_bin == 0) { (void) snprintf(c, sizeof (c), "< %lld", (long long)value); if (dt_printf(dtp, fp, "%16s ", c) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[0], normal, total, positives, negatives) < 0) return (-1); } while (order <= high) { if (bin >= first_bin && bin <= last_bin) { if (dt_printf(dtp, fp, "%16lld ", (long long)value) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[bin], normal, total, positives, negatives) < 0) return (-1); } assert(value < next); bin++; if ((value += step) != next) continue; next = value * factor; step = next > nsteps ? next / nsteps : 1; order++; } if (last_bin < bin) return (0); assert(last_bin == bin); (void) snprintf(c, sizeof (c), ">= %lld", (long long)value); if (dt_printf(dtp, fp, "%16s ", c) < 0) return (-1); return (dt_print_quantline(dtp, fp, data[bin], normal, total, positives, negatives)); } /*ARGSUSED*/ static int dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t size, uint64_t normal) { /* LINTED - alignment */ int64_t *data = (int64_t *)addr; return (dt_printf(dtp, fp, " %16lld", data[0] ? (long long)(data[1] / (int64_t)normal / data[0]) : 0)); } /*ARGSUSED*/ static int dt_print_stddev(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t size, uint64_t normal) { /* LINTED - alignment */ uint64_t *data = (uint64_t *)addr; return (dt_printf(dtp, fp, " %16llu", data[0] ? (unsigned long long) dt_stddev(data, normal) : 0)); } /*ARGSUSED*/ -int +static int dt_print_bytes(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t nbytes, int width, int quiet, int forceraw) { /* * If the byte stream is a series of printable characters, followed by * a terminating byte, we print it out as a string. Otherwise, we * assume that it's something else and just print the bytes. */ int i, j, margin = 5; char *c = (char *)addr; if (nbytes == 0) return (0); if (forceraw) goto raw; if (dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET) goto raw; for (i = 0; i < nbytes; i++) { /* * We define a "printable character" to be one for which * isprint(3C) returns non-zero, isspace(3C) returns non-zero, * or a character which is either backspace or the bell. * Backspace and the bell are regrettably special because * they fail the first two tests -- and yet they are entirely * printable. These are the only two control characters that * have meaning for the terminal and for which isprint(3C) and * isspace(3C) return 0. */ if (isprint(c[i]) || isspace(c[i]) || c[i] == '\b' || c[i] == '\a') continue; if (c[i] == '\0' && i > 0) { /* * This looks like it might be a string. Before we * assume that it is indeed a string, check the * remainder of the byte range; if it contains * additional non-nul characters, we'll assume that * it's a binary stream that just happens to look like * a string, and we'll print out the individual bytes. */ for (j = i + 1; j < nbytes; j++) { if (c[j] != '\0') break; } if (j != nbytes) break; - if (quiet) + if (quiet) { return (dt_printf(dtp, fp, "%s", c)); - else - return (dt_printf(dtp, fp, " %-*s", width, c)); + } else { + return (dt_printf(dtp, fp, " %s%*s", + width < 0 ? " " : "", width, c)); + } } break; } if (i == nbytes) { /* * The byte range is all printable characters, but there is * no trailing nul byte. We'll assume that it's a string and * print it as such. */ char *s = alloca(nbytes + 1); bcopy(c, s, nbytes); s[nbytes] = '\0'; return (dt_printf(dtp, fp, " %-*s", width, s)); } raw: if (dt_printf(dtp, fp, "\n%*s ", margin, "") < 0) return (-1); for (i = 0; i < 16; i++) if (dt_printf(dtp, fp, " %c", "0123456789abcdef"[i]) < 0) return (-1); if (dt_printf(dtp, fp, " 0123456789abcdef\n") < 0) return (-1); for (i = 0; i < nbytes; i += 16) { if (dt_printf(dtp, fp, "%*s%5x:", margin, "", i) < 0) return (-1); for (j = i; j < i + 16 && j < nbytes; j++) { if (dt_printf(dtp, fp, " %02x", (uchar_t)c[j]) < 0) return (-1); } while (j++ % 16) { if (dt_printf(dtp, fp, " ") < 0) return (-1); } if (dt_printf(dtp, fp, " ") < 0) return (-1); for (j = i; j < i + 16 && j < nbytes; j++) { if (dt_printf(dtp, fp, "%c", c[j] < ' ' || c[j] > '~' ? '.' : c[j]) < 0) return (-1); } if (dt_printf(dtp, fp, "\n") < 0) return (-1); } return (0); } int dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr, int depth, int size) { dtrace_syminfo_t dts; GElf_Sym sym; int i, indent; char c[PATH_MAX * 2]; uint64_t pc; if (dt_printf(dtp, fp, "\n") < 0) return (-1); if (format == NULL) format = "%s"; if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; else indent = _dtrace_stkindent; for (i = 0; i < depth; i++) { switch (size) { case sizeof (uint32_t): /* LINTED - alignment */ pc = *((uint32_t *)addr); break; case sizeof (uint64_t): /* LINTED - alignment */ pc = *((uint64_t *)addr); break; default: return (dt_set_errno(dtp, EDT_BADSTACKPC)); } if (pc == 0) break; addr += size; if (dt_printf(dtp, fp, "%*s", indent, "") < 0) return (-1); if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { if (pc > sym.st_value) { (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", dts.dts_object, dts.dts_name, (u_longlong_t)(pc - sym.st_value)); } else { (void) snprintf(c, sizeof (c), "%s`%s", dts.dts_object, dts.dts_name); } } else { /* * We'll repeat the lookup, but this time we'll specify * a NULL GElf_Sym -- indicating that we're only * interested in the containing module. */ if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { (void) snprintf(c, sizeof (c), "%s`0x%llx", dts.dts_object, (u_longlong_t)pc); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } } if (dt_printf(dtp, fp, format, c) < 0) return (-1); if (dt_printf(dtp, fp, "\n") < 0) return (-1); } return (0); } int dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr, uint64_t arg) { /* LINTED - alignment */ uint64_t *pc = (uint64_t *)addr; uint32_t depth = DTRACE_USTACK_NFRAMES(arg); uint32_t strsize = DTRACE_USTACK_STRSIZE(arg); const char *strbase = addr + (depth + 1) * sizeof (uint64_t); const char *str = strsize ? strbase : NULL; int err = 0; char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; struct ps_prochandle *P; GElf_Sym sym; int i, indent; pid_t pid; if (depth == 0) return (0); pid = (pid_t)*pc++; if (dt_printf(dtp, fp, "\n") < 0) return (-1); if (format == NULL) format = "%s"; if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; else indent = _dtrace_stkindent; /* * Ultimately, we need to add an entry point in the library vector for * determining from . For now, if * this is a vector open, we just print the raw address or string. */ if (dtp->dt_vector == NULL) P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); else P = NULL; if (P != NULL) dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ for (i = 0; i < depth && pc[i] != 0; i++) { const prmap_t *map; if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) break; if (P != NULL && Plookup_by_addr(P, pc[i], name, sizeof (name), &sym) == 0) { (void) Pobjname(P, pc[i], objname, sizeof (objname)); if (pc[i] > sym.st_value) { (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", dt_basename(objname), name, (u_longlong_t)(pc[i] - sym.st_value)); } else { (void) snprintf(c, sizeof (c), "%s`%s", dt_basename(objname), name); } } else if (str != NULL && str[0] != '\0' && str[0] != '@' && (P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL || (map->pr_mflags & MA_WRITE)))) { /* * If the current string pointer in the string table * does not point to an empty string _and_ the program * counter falls in a writable region, we'll use the * string from the string table instead of the raw * address. This last condition is necessary because * some (broken) ustack helpers will return a string * even for a program counter that they can't * identify. If we have a string for a program * counter that falls in a segment that isn't * writable, we assume that we have fallen into this * case and we refuse to use the string. */ (void) snprintf(c, sizeof (c), "%s", str); } else { if (P != NULL && Pobjname(P, pc[i], objname, sizeof (objname)) != 0) { (void) snprintf(c, sizeof (c), "%s`0x%llx", dt_basename(objname), (u_longlong_t)pc[i]); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc[i]); } } if ((err = dt_printf(dtp, fp, format, c)) < 0) break; if ((err = dt_printf(dtp, fp, "\n")) < 0) break; if (str != NULL && str[0] == '@') { /* * If the first character of the string is an "at" sign, * then the string is inferred to be an annotation -- * and it is printed out beneath the frame and offset * with brackets. */ if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) break; (void) snprintf(c, sizeof (c), " [ %s ]", &str[1]); if ((err = dt_printf(dtp, fp, format, c)) < 0) break; if ((err = dt_printf(dtp, fp, "\n")) < 0) break; } if (str != NULL) { str += strlen(str) + 1; if (str - strbase >= strsize) str = NULL; } } if (P != NULL) { dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } return (err); } static int dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act) { /* LINTED - alignment */ uint64_t pid = ((uint64_t *)addr)[0]; /* LINTED - alignment */ uint64_t pc = ((uint64_t *)addr)[1]; const char *format = " %-50s"; char *s; int n, len = 256; if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) { struct ps_prochandle *P; if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) { GElf_Sym sym; dt_proc_lock(dtp, P); if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0) pc = sym.st_value; dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } } do { n = len; s = alloca(n); } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n); return (dt_printf(dtp, fp, format, s)); } int dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) { /* LINTED - alignment */ uint64_t pid = ((uint64_t *)addr)[0]; /* LINTED - alignment */ uint64_t pc = ((uint64_t *)addr)[1]; int err = 0; char objname[PATH_MAX], c[PATH_MAX * 2]; struct ps_prochandle *P; if (format == NULL) format = " %-50s"; /* * See the comment in dt_print_ustack() for the rationale for * printing raw addresses in the vectored case. */ if (dtp->dt_vector == NULL) P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); else P = NULL; if (P != NULL) dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != 0) { (void) snprintf(c, sizeof (c), "%s", dt_basename(objname)); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } err = dt_printf(dtp, fp, format, c); if (P != NULL) { dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } return (err); } int dt_print_memory(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) { int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); size_t nbytes = *((uintptr_t *) addr); return (dt_print_bytes(dtp, fp, addr + sizeof(uintptr_t), nbytes, 50, quiet, 1)); } typedef struct dt_type_cbdata { dtrace_hdl_t *dtp; dtrace_typeinfo_t dtt; caddr_t addr; caddr_t addrend; const char *name; int f_type; int indent; int type_width; int name_width; FILE *fp; } dt_type_cbdata_t; static int dt_print_type_data(dt_type_cbdata_t *, ctf_id_t); static int dt_print_type_member(const char *name, ctf_id_t type, ulong_t off, void *arg) { dt_type_cbdata_t cbdata; dt_type_cbdata_t *cbdatap = arg; ssize_t ssz; if ((ssz = ctf_type_size(cbdatap->dtt.dtt_ctfp, type)) <= 0) return (0); off /= 8; cbdata = *cbdatap; cbdata.name = name; cbdata.addr += off; cbdata.addrend = cbdata.addr + ssz; return (dt_print_type_data(&cbdata, type)); } static int dt_print_type_width(const char *name, ctf_id_t type, ulong_t off, void *arg) { char buf[DT_TYPE_NAMELEN]; char *p; dt_type_cbdata_t *cbdatap = arg; size_t sz = strlen(name); ctf_type_name(cbdatap->dtt.dtt_ctfp, type, buf, sizeof (buf)); if ((p = strchr(buf, '[')) != NULL) p[-1] = '\0'; else p = ""; sz += strlen(p); if (sz > cbdatap->name_width) cbdatap->name_width = sz; sz = strlen(buf); if (sz > cbdatap->type_width) cbdatap->type_width = sz; return (0); } static int dt_print_type_data(dt_type_cbdata_t *cbdatap, ctf_id_t type) { caddr_t addr = cbdatap->addr; caddr_t addrend = cbdatap->addrend; char buf[DT_TYPE_NAMELEN]; char *p; int cnt = 0; uint_t kind = ctf_type_kind(cbdatap->dtt.dtt_ctfp, type); ssize_t ssz = ctf_type_size(cbdatap->dtt.dtt_ctfp, type); ctf_type_name(cbdatap->dtt.dtt_ctfp, type, buf, sizeof (buf)); if ((p = strchr(buf, '[')) != NULL) p[-1] = '\0'; else p = ""; if (cbdatap->f_type) { int type_width = roundup(cbdatap->type_width + 1, 4); int name_width = roundup(cbdatap->name_width + 1, 4); name_width -= strlen(cbdatap->name); dt_printf(cbdatap->dtp, cbdatap->fp, "%*s%-*s%s%-*s = ",cbdatap->indent * 4,"",type_width,buf,cbdatap->name,name_width,p); } while (addr < addrend) { dt_type_cbdata_t cbdata; ctf_arinfo_t arinfo; ctf_encoding_t cte; uintptr_t *up; void *vp = addr; cbdata = *cbdatap; cbdata.name = ""; cbdata.addr = addr; cbdata.addrend = addr + ssz; cbdata.f_type = 0; cbdata.indent++; cbdata.type_width = 0; cbdata.name_width = 0; if (cnt > 0) dt_printf(cbdatap->dtp, cbdatap->fp, "%*s", cbdatap->indent * 4,""); switch (kind) { case CTF_K_INTEGER: if (ctf_type_encoding(cbdatap->dtt.dtt_ctfp, type, &cte) != 0) return (-1); if ((cte.cte_format & CTF_INT_SIGNED) != 0) switch (cte.cte_bits) { case 8: if (isprint(*((char *) vp))) dt_printf(cbdatap->dtp, cbdatap->fp, "'%c', ", *((char *) vp)); dt_printf(cbdatap->dtp, cbdatap->fp, "%d (0x%x);\n", *((char *) vp), *((char *) vp)); break; case 16: dt_printf(cbdatap->dtp, cbdatap->fp, "%hd (0x%hx);\n", *((short *) vp), *((u_short *) vp)); break; case 32: dt_printf(cbdatap->dtp, cbdatap->fp, "%d (0x%x);\n", *((int *) vp), *((u_int *) vp)); break; case 64: dt_printf(cbdatap->dtp, cbdatap->fp, "%jd (0x%jx);\n", *((long long *) vp), *((unsigned long long *) vp)); break; default: dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_INTEGER: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); break; } else switch (cte.cte_bits) { case 8: dt_printf(cbdatap->dtp, cbdatap->fp, "%u (0x%x);\n", *((uint8_t *) vp) & 0xff, *((uint8_t *) vp) & 0xff); break; case 16: dt_printf(cbdatap->dtp, cbdatap->fp, "%hu (0x%hx);\n", *((u_short *) vp), *((u_short *) vp)); break; case 32: dt_printf(cbdatap->dtp, cbdatap->fp, "%u (0x%x);\n", *((u_int *) vp), *((u_int *) vp)); break; case 64: dt_printf(cbdatap->dtp, cbdatap->fp, "%ju (0x%jx);\n", *((unsigned long long *) vp), *((unsigned long long *) vp)); break; default: dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_INTEGER: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); break; } break; case CTF_K_FLOAT: dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_FLOAT: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); break; case CTF_K_POINTER: dt_printf(cbdatap->dtp, cbdatap->fp, "%p;\n", *((void **) addr)); break; case CTF_K_ARRAY: if (ctf_array_info(cbdatap->dtt.dtt_ctfp, type, &arinfo) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "{\n%*s",cbdata.indent * 4,""); dt_print_type_data(&cbdata, arinfo.ctr_contents); dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); break; case CTF_K_FUNCTION: dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_FUNCTION:\n"); break; case CTF_K_STRUCT: cbdata.f_type = 1; if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, dt_print_type_width, &cbdata) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "{\n"); if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, dt_print_type_member, &cbdata) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); break; case CTF_K_UNION: cbdata.f_type = 1; if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, dt_print_type_width, &cbdata) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "{\n"); if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, dt_print_type_member, &cbdata) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); break; case CTF_K_ENUM: dt_printf(cbdatap->dtp, cbdatap->fp, "%s;\n", ctf_enum_name(cbdatap->dtt.dtt_ctfp, type, *((int *) vp))); break; case CTF_K_TYPEDEF: dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); break; case CTF_K_VOLATILE: if (cbdatap->f_type) dt_printf(cbdatap->dtp, cbdatap->fp, "volatile "); dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); break; case CTF_K_CONST: if (cbdatap->f_type) dt_printf(cbdatap->dtp, cbdatap->fp, "const "); dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); break; case CTF_K_RESTRICT: if (cbdatap->f_type) dt_printf(cbdatap->dtp, cbdatap->fp, "restrict "); dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); break; default: break; } addr += ssz; cnt++; } return (0); } static int dt_print_type(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) { caddr_t addrend; char *p; dtrace_typeinfo_t dtt; dt_type_cbdata_t cbdata; int num = 0; int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); ssize_t ssz; if (!quiet) dt_printf(dtp, fp, "\n"); /* Get the total number of bytes of data buffered. */ size_t nbytes = *((uintptr_t *) addr); addr += sizeof(uintptr_t); /* * Get the size of the type so that we can check that it matches * the CTF data we look up and so that we can figure out how many * type elements are buffered. */ size_t typs = *((uintptr_t *) addr); addr += sizeof(uintptr_t); /* * Point to the type string in the buffer. Get it's string * length and round it up to become the offset to the start * of the buffered type data which we would like to be aligned * for easy access. */ char *strp = (char *) addr; int offset = roundup(strlen(strp) + 1, sizeof(uintptr_t)); /* * The type string might have a format such as 'int [20]'. * Check if there is an array dimension present. */ if ((p = strchr(strp, '[')) != NULL) { /* Strip off the array dimension. */ *p++ = '\0'; for (; *p != '\0' && *p != ']'; p++) num = num * 10 + *p - '0'; } else /* No array dimension, so default. */ num = 1; /* Lookup the CTF type from the type string. */ if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY, strp, &dtt) < 0) return (-1); /* Offset the buffer address to the start of the data... */ addr += offset; ssz = ctf_type_size(dtt.dtt_ctfp, dtt.dtt_type); if (typs != ssz) { printf("Expected type size from buffer (%lu) to match type size looked up now (%ld)\n", (u_long) typs, (long) ssz); return (-1); } cbdata.dtp = dtp; cbdata.dtt = dtt; cbdata.name = ""; cbdata.addr = addr; cbdata.addrend = addr + nbytes; cbdata.indent = 1; cbdata.f_type = 1; cbdata.type_width = 0; cbdata.name_width = 0; cbdata.fp = fp; return (dt_print_type_data(&cbdata, dtt.dtt_type)); } static int dt_print_sym(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) { /* LINTED - alignment */ uint64_t pc = *((uint64_t *)addr); dtrace_syminfo_t dts; GElf_Sym sym; char c[PATH_MAX * 2]; if (format == NULL) format = " %-50s"; if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { (void) snprintf(c, sizeof (c), "%s`%s", dts.dts_object, dts.dts_name); } else { /* * We'll repeat the lookup, but this time we'll specify a * NULL GElf_Sym -- indicating that we're only interested in * the containing module. */ if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { (void) snprintf(c, sizeof (c), "%s`0x%llx", dts.dts_object, (u_longlong_t)pc); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } } if (dt_printf(dtp, fp, format, c) < 0) return (-1); return (0); } int dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) { /* LINTED - alignment */ uint64_t pc = *((uint64_t *)addr); dtrace_syminfo_t dts; char c[PATH_MAX * 2]; if (format == NULL) format = " %-50s"; if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { (void) snprintf(c, sizeof (c), "%s", dts.dts_object); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } if (dt_printf(dtp, fp, format, c) < 0) return (-1); return (0); } typedef struct dt_normal { dtrace_aggvarid_t dtnd_id; uint64_t dtnd_normal; } dt_normal_t; static int dt_normalize_agg(const dtrace_aggdata_t *aggdata, void *arg) { dt_normal_t *normal = arg; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t id = normal->dtnd_id; if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); ((dtrace_aggdata_t *)aggdata)->dtada_normal = normal->dtnd_normal; return (DTRACE_AGGWALK_NORMALIZE); } static int dt_normalize(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) { dt_normal_t normal; caddr_t addr; /* * We (should) have two records: the aggregation ID followed by the * normalization value. */ addr = base + rec->dtrd_offset; if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) return (dt_set_errno(dtp, EDT_BADNORMAL)); /* LINTED - alignment */ normal.dtnd_id = *((dtrace_aggvarid_t *)addr); rec++; if (rec->dtrd_action != DTRACEACT_LIBACT) return (dt_set_errno(dtp, EDT_BADNORMAL)); if (rec->dtrd_arg != DT_ACT_NORMALIZE) return (dt_set_errno(dtp, EDT_BADNORMAL)); addr = base + rec->dtrd_offset; switch (rec->dtrd_size) { case sizeof (uint64_t): /* LINTED - alignment */ normal.dtnd_normal = *((uint64_t *)addr); break; case sizeof (uint32_t): /* LINTED - alignment */ normal.dtnd_normal = *((uint32_t *)addr); break; case sizeof (uint16_t): /* LINTED - alignment */ normal.dtnd_normal = *((uint16_t *)addr); break; case sizeof (uint8_t): normal.dtnd_normal = *((uint8_t *)addr); break; default: return (dt_set_errno(dtp, EDT_BADNORMAL)); } (void) dtrace_aggregate_walk(dtp, dt_normalize_agg, &normal); return (0); } static int dt_denormalize_agg(const dtrace_aggdata_t *aggdata, void *arg) { dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); return (DTRACE_AGGWALK_DENORMALIZE); } static int dt_clear_agg(const dtrace_aggdata_t *aggdata, void *arg) { dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); return (DTRACE_AGGWALK_CLEAR); } typedef struct dt_trunc { dtrace_aggvarid_t dttd_id; uint64_t dttd_remaining; } dt_trunc_t; static int dt_trunc_agg(const dtrace_aggdata_t *aggdata, void *arg) { dt_trunc_t *trunc = arg; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t id = trunc->dttd_id; if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); if (trunc->dttd_remaining == 0) return (DTRACE_AGGWALK_REMOVE); trunc->dttd_remaining--; return (DTRACE_AGGWALK_NEXT); } static int dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) { dt_trunc_t trunc; caddr_t addr; int64_t remaining; int (*func)(dtrace_hdl_t *, dtrace_aggregate_f *, void *); /* * We (should) have two records: the aggregation ID followed by the * number of aggregation entries after which the aggregation is to be * truncated. */ addr = base + rec->dtrd_offset; if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) return (dt_set_errno(dtp, EDT_BADTRUNC)); /* LINTED - alignment */ trunc.dttd_id = *((dtrace_aggvarid_t *)addr); rec++; if (rec->dtrd_action != DTRACEACT_LIBACT) return (dt_set_errno(dtp, EDT_BADTRUNC)); if (rec->dtrd_arg != DT_ACT_TRUNC) return (dt_set_errno(dtp, EDT_BADTRUNC)); addr = base + rec->dtrd_offset; switch (rec->dtrd_size) { case sizeof (uint64_t): /* LINTED - alignment */ remaining = *((int64_t *)addr); break; case sizeof (uint32_t): /* LINTED - alignment */ remaining = *((int32_t *)addr); break; case sizeof (uint16_t): /* LINTED - alignment */ remaining = *((int16_t *)addr); break; case sizeof (uint8_t): remaining = *((int8_t *)addr); break; default: return (dt_set_errno(dtp, EDT_BADNORMAL)); } if (remaining < 0) { func = dtrace_aggregate_walk_valsorted; remaining = -remaining; } else { func = dtrace_aggregate_walk_valrevsorted; } assert(remaining >= 0); trunc.dttd_remaining = remaining; (void) func(dtp, dt_trunc_agg, &trunc); return (0); } static int dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, - caddr_t addr, size_t size, uint64_t normal) + caddr_t addr, size_t size, const dtrace_aggdata_t *aggdata, + uint64_t normal, dt_print_aggdata_t *pd) { - int err; + int err, width; dtrace_actkind_t act = rec->dtrd_action; + boolean_t packed = pd->dtpa_agghist || pd->dtpa_aggpack; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + static struct { + size_t size; + int width; + int packedwidth; + } *fmt, fmttab[] = { + { sizeof (uint8_t), 3, 3 }, + { sizeof (uint16_t), 5, 5 }, + { sizeof (uint32_t), 8, 8 }, + { sizeof (uint64_t), 16, 16 }, + { 0, -50, 16 } + }; + + if (packed && pd->dtpa_agghisthdr != agg->dtagd_varid) { + dtrace_recdesc_t *r; + + width = 0; + + /* + * To print our quantization header for either an agghist or + * aggpack aggregation, we need to iterate through all of our + * of our records to determine their width. + */ + for (r = rec; !DTRACEACT_ISAGG(r->dtrd_action); r++) { + for (fmt = fmttab; fmt->size && + fmt->size != r->dtrd_size; fmt++) + continue; + + width += fmt->packedwidth + 1; + } + + if (pd->dtpa_agghist) { + if (dt_print_quanthdr(dtp, fp, width) < 0) + return (-1); + } else { + if (dt_print_quanthdr_packed(dtp, fp, + width, aggdata, r->dtrd_action) < 0) + return (-1); + } + + pd->dtpa_agghisthdr = agg->dtagd_varid; + } + + if (pd->dtpa_agghist && DTRACEACT_ISAGG(act)) { + char positives = aggdata->dtada_flags & DTRACE_A_HASPOSITIVES; + char negatives = aggdata->dtada_flags & DTRACE_A_HASNEGATIVES; + int64_t val; + + assert(act == DTRACEAGG_SUM || act == DTRACEAGG_COUNT); + val = (long long)*((uint64_t *)addr); + + if (dt_printf(dtp, fp, " ") < 0) + return (-1); + + return (dt_print_quantline(dtp, fp, val, normal, + aggdata->dtada_total, positives, negatives)); + } + + if (pd->dtpa_aggpack && DTRACEACT_ISAGG(act)) { + switch (act) { + case DTRACEAGG_QUANTIZE: + return (dt_print_quantize_packed(dtp, + fp, addr, size, aggdata)); + case DTRACEAGG_LQUANTIZE: + return (dt_print_lquantize_packed(dtp, + fp, addr, size, aggdata)); + default: + break; + } + } + switch (act) { case DTRACEACT_STACK: return (dt_print_stack(dtp, fp, NULL, addr, rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg)); case DTRACEACT_USTACK: case DTRACEACT_JSTACK: return (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg)); case DTRACEACT_USYM: case DTRACEACT_UADDR: return (dt_print_usym(dtp, fp, addr, act)); case DTRACEACT_UMOD: return (dt_print_umod(dtp, fp, NULL, addr)); case DTRACEACT_SYM: return (dt_print_sym(dtp, fp, NULL, addr)); case DTRACEACT_MOD: return (dt_print_mod(dtp, fp, NULL, addr)); case DTRACEAGG_QUANTIZE: return (dt_print_quantize(dtp, fp, addr, size, normal)); case DTRACEAGG_LQUANTIZE: return (dt_print_lquantize(dtp, fp, addr, size, normal)); case DTRACEAGG_LLQUANTIZE: return (dt_print_llquantize(dtp, fp, addr, size, normal)); case DTRACEAGG_AVG: return (dt_print_average(dtp, fp, addr, size, normal)); case DTRACEAGG_STDDEV: return (dt_print_stddev(dtp, fp, addr, size, normal)); default: break; } + for (fmt = fmttab; fmt->size && fmt->size != size; fmt++) + continue; + + width = packed ? fmt->packedwidth : fmt->width; + switch (size) { case sizeof (uint64_t): - err = dt_printf(dtp, fp, " %16lld", + err = dt_printf(dtp, fp, " %*lld", width, /* LINTED - alignment */ (long long)*((uint64_t *)addr) / normal); break; case sizeof (uint32_t): /* LINTED - alignment */ - err = dt_printf(dtp, fp, " %8d", *((uint32_t *)addr) / + err = dt_printf(dtp, fp, " %*d", width, *((uint32_t *)addr) / (uint32_t)normal); break; case sizeof (uint16_t): /* LINTED - alignment */ - err = dt_printf(dtp, fp, " %5d", *((uint16_t *)addr) / + err = dt_printf(dtp, fp, " %*d", width, *((uint16_t *)addr) / (uint32_t)normal); break; case sizeof (uint8_t): - err = dt_printf(dtp, fp, " %3d", *((uint8_t *)addr) / + err = dt_printf(dtp, fp, " %*d", width, *((uint8_t *)addr) / (uint32_t)normal); break; default: - err = dt_print_bytes(dtp, fp, addr, size, 50, 0, 0); + err = dt_print_bytes(dtp, fp, addr, size, width, 0, 0); break; } return (err); } int dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) { int i, aggact = 0; dt_print_aggdata_t *pd = arg; const dtrace_aggdata_t *aggdata = aggsdata[0]; dtrace_aggdesc_t *agg = aggdata->dtada_desc; FILE *fp = pd->dtpa_fp; dtrace_hdl_t *dtp = pd->dtpa_dtp; dtrace_recdesc_t *rec; dtrace_actkind_t act; caddr_t addr; size_t size; + pd->dtpa_agghist = (aggdata->dtada_flags & DTRACE_A_TOTAL); + pd->dtpa_aggpack = (aggdata->dtada_flags & DTRACE_A_MINMAXBIN); + /* * Iterate over each record description in the key, printing the traced * data, skipping the first datum (the tuple member created by the * compiler). */ for (i = 1; i < agg->dtagd_nrecs; i++) { rec = &agg->dtagd_rec[i]; act = rec->dtrd_action; addr = aggdata->dtada_data + rec->dtrd_offset; size = rec->dtrd_size; if (DTRACEACT_ISAGG(act)) { aggact = i; break; } - if (dt_print_datum(dtp, fp, rec, addr, size, 1) < 0) + if (dt_print_datum(dtp, fp, rec, addr, + size, aggdata, 1, pd) < 0) return (-1); if (dt_buffered_flush(dtp, NULL, rec, aggdata, DTRACE_BUFDATA_AGGKEY) < 0) return (-1); } assert(aggact != 0); for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) { uint64_t normal; aggdata = aggsdata[i]; agg = aggdata->dtada_desc; rec = &agg->dtagd_rec[aggact]; act = rec->dtrd_action; addr = aggdata->dtada_data + rec->dtrd_offset; size = rec->dtrd_size; assert(DTRACEACT_ISAGG(act)); normal = aggdata->dtada_normal; - if (dt_print_datum(dtp, fp, rec, addr, size, normal) < 0) + if (dt_print_datum(dtp, fp, rec, addr, + size, aggdata, normal, pd) < 0) return (-1); if (dt_buffered_flush(dtp, NULL, rec, aggdata, DTRACE_BUFDATA_AGGVAL) < 0) return (-1); if (!pd->dtpa_allunprint) agg->dtagd_flags |= DTRACE_AGD_PRINTED; } - if (dt_printf(dtp, fp, "\n") < 0) - return (-1); + if (!pd->dtpa_agghist && !pd->dtpa_aggpack) { + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + } if (dt_buffered_flush(dtp, NULL, NULL, aggdata, DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) return (-1); return (0); } int dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) { dt_print_aggdata_t *pd = arg; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t aggvarid = pd->dtpa_id; if (pd->dtpa_allunprint) { if (agg->dtagd_flags & DTRACE_AGD_PRINTED) return (0); } else { /* * If we're not printing all unprinted aggregations, then the * aggregation variable ID denotes a specific aggregation * variable that we should print -- skip any other aggregations * that we encounter. */ if (agg->dtagd_nrecs == 0) return (0); if (aggvarid != agg->dtagd_varid) return (0); } return (dt_print_aggs(&aggdata, 1, arg)); } int dt_setopt(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, const char *option, const char *value) { int len, rval; char *msg; const char *errstr; dtrace_setoptdata_t optdata; bzero(&optdata, sizeof (optdata)); (void) dtrace_getopt(dtp, option, &optdata.dtsda_oldval); if (dtrace_setopt(dtp, option, value) == 0) { (void) dtrace_getopt(dtp, option, &optdata.dtsda_newval); optdata.dtsda_probe = data; optdata.dtsda_option = option; optdata.dtsda_handle = dtp; if ((rval = dt_handle_setopt(dtp, &optdata)) != 0) return (rval); return (0); } errstr = dtrace_errmsg(dtp, dtrace_errno(dtp)); len = strlen(option) + strlen(value) + strlen(errstr) + 80; msg = alloca(len); (void) snprintf(msg, len, "couldn't set option \"%s\" to \"%s\": %s\n", option, value, errstr); if ((rval = dt_handle_liberr(dtp, data, msg)) == 0) return (0); return (rval); } static int dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, dtrace_bufdesc_t *buf, boolean_t just_one, dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) { dtrace_epid_t id; size_t offs; int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); int rval, i, n; uint64_t tracememsize = 0; dtrace_probedata_t data; uint64_t drops; bzero(&data, sizeof (data)); data.dtpda_handle = dtp; data.dtpda_cpu = cpu; data.dtpda_flow = dtp->dt_flow; data.dtpda_indent = dtp->dt_indent; data.dtpda_prefix = dtp->dt_prefix; for (offs = buf->dtbd_oldest; offs < buf->dtbd_size; ) { dtrace_eprobedesc_t *epd; /* * We're guaranteed to have an ID. */ id = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); if (id == DTRACE_EPIDNONE) { /* * This is filler to assure proper alignment of the * next record; we simply ignore it. */ offs += sizeof (id); continue; } if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc, &data.dtpda_pdesc)) != 0) return (rval); epd = data.dtpda_edesc; data.dtpda_data = buf->dtbd_data + offs; if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) { rval = dt_handle(dtp, &data); if (rval == DTRACE_CONSUME_NEXT) goto nextepid; if (rval == DTRACE_CONSUME_ERROR) return (-1); } if (flow) (void) dt_flowindent(dtp, &data, dtp->dt_last_epid, buf, offs); rval = (*efunc)(&data, arg); if (flow) { if (data.dtpda_flow == DTRACEFLOW_ENTRY) data.dtpda_indent += 2; } if (rval == DTRACE_CONSUME_NEXT) goto nextepid; if (rval == DTRACE_CONSUME_ABORT) return (dt_set_errno(dtp, EDT_DIRABORT)); if (rval != DTRACE_CONSUME_THIS) return (dt_set_errno(dtp, EDT_BADRVAL)); for (i = 0; i < epd->dtepd_nrecs; i++) { caddr_t addr; dtrace_recdesc_t *rec = &epd->dtepd_rec[i]; dtrace_actkind_t act = rec->dtrd_action; data.dtpda_data = buf->dtbd_data + offs + rec->dtrd_offset; addr = data.dtpda_data; if (act == DTRACEACT_LIBACT) { uint64_t arg = rec->dtrd_arg; dtrace_aggvarid_t id; switch (arg) { case DT_ACT_CLEAR: /* LINTED - alignment */ id = *((dtrace_aggvarid_t *)addr); (void) dtrace_aggregate_walk(dtp, dt_clear_agg, &id); continue; case DT_ACT_DENORMALIZE: /* LINTED - alignment */ id = *((dtrace_aggvarid_t *)addr); (void) dtrace_aggregate_walk(dtp, dt_denormalize_agg, &id); continue; case DT_ACT_FTRUNCATE: if (fp == NULL) continue; (void) fflush(fp); (void) ftruncate(fileno(fp), 0); (void) fseeko(fp, 0, SEEK_SET); continue; case DT_ACT_NORMALIZE: if (i == epd->dtepd_nrecs - 1) return (dt_set_errno(dtp, EDT_BADNORMAL)); if (dt_normalize(dtp, buf->dtbd_data + offs, rec) != 0) return (-1); i++; continue; case DT_ACT_SETOPT: { uint64_t *opts = dtp->dt_options; dtrace_recdesc_t *valrec; uint32_t valsize; caddr_t val; int rv; if (i == epd->dtepd_nrecs - 1) { return (dt_set_errno(dtp, EDT_BADSETOPT)); } valrec = &epd->dtepd_rec[++i]; valsize = valrec->dtrd_size; if (valrec->dtrd_action != act || valrec->dtrd_arg != arg) { return (dt_set_errno(dtp, EDT_BADSETOPT)); } if (valsize > sizeof (uint64_t)) { val = buf->dtbd_data + offs + valrec->dtrd_offset; } else { val = "1"; } rv = dt_setopt(dtp, &data, addr, val); if (rv != 0) return (-1); flow = (opts[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); quiet = (opts[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); continue; } case DT_ACT_TRUNC: if (i == epd->dtepd_nrecs - 1) return (dt_set_errno(dtp, EDT_BADTRUNC)); if (dt_trunc(dtp, buf->dtbd_data + offs, rec) != 0) return (-1); i++; continue; default: continue; } } if (act == DTRACEACT_TRACEMEM_DYNSIZE && rec->dtrd_size == sizeof (uint64_t)) { /* LINTED - alignment */ tracememsize = *((unsigned long long *)addr); continue; } rval = (*rfunc)(&data, rec, arg); if (rval == DTRACE_CONSUME_NEXT) continue; if (rval == DTRACE_CONSUME_ABORT) return (dt_set_errno(dtp, EDT_DIRABORT)); if (rval != DTRACE_CONSUME_THIS) return (dt_set_errno(dtp, EDT_BADRVAL)); if (act == DTRACEACT_STACK) { int depth = rec->dtrd_arg; if (dt_print_stack(dtp, fp, NULL, addr, depth, rec->dtrd_size / depth) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_USTACK || act == DTRACEACT_JSTACK) { if (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_SYM) { if (dt_print_sym(dtp, fp, NULL, addr) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_MOD) { if (dt_print_mod(dtp, fp, NULL, addr) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { if (dt_print_usym(dtp, fp, addr, act) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_UMOD) { if (dt_print_umod(dtp, fp, NULL, addr) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_PRINTM) { if (dt_print_memory(dtp, fp, addr) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_PRINTT) { if (dt_print_type(dtp, fp, addr) < 0) return (-1); goto nextrec; } if (DTRACEACT_ISPRINTFLIKE(act)) { void *fmtdata; int (*func)(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *buf, size_t); if ((fmtdata = dt_format_lookup(dtp, rec->dtrd_format)) == NULL) goto nofmt; switch (act) { case DTRACEACT_PRINTF: func = dtrace_fprintf; break; case DTRACEACT_PRINTA: func = dtrace_fprinta; break; case DTRACEACT_SYSTEM: func = dtrace_system; break; case DTRACEACT_FREOPEN: func = dtrace_freopen; break; } n = (*func)(dtp, fp, fmtdata, &data, rec, epd->dtepd_nrecs - i, (uchar_t *)buf->dtbd_data + offs, buf->dtbd_size - offs); if (n < 0) return (-1); /* errno is set for us */ if (n > 0) i += n - 1; goto nextrec; } /* * If this is a DIF expression, and the record has a * format set, this indicates we have a CTF type name * associated with the data and we should try to print * it out by type. */ if (act == DTRACEACT_DIFEXPR) { const char *strdata = dt_strdata_lookup(dtp, rec->dtrd_format); if (strdata != NULL) { n = dtrace_print(dtp, fp, strdata, addr, rec->dtrd_size); /* * dtrace_print() will return -1 on * error, or return the number of bytes * consumed. It will return 0 if the * type couldn't be determined, and we * should fall through to the normal * trace method. */ if (n < 0) return (-1); if (n > 0) goto nextrec; } } nofmt: if (act == DTRACEACT_PRINTA) { dt_print_aggdata_t pd; dtrace_aggvarid_t *aggvars; int j, naggvars = 0; size_t size = ((epd->dtepd_nrecs - i) * sizeof (dtrace_aggvarid_t)); if ((aggvars = dt_alloc(dtp, size)) == NULL) return (-1); /* * This might be a printa() with multiple * aggregation variables. We need to scan * forward through the records until we find * a record from a different statement. */ for (j = i; j < epd->dtepd_nrecs; j++) { dtrace_recdesc_t *nrec; caddr_t naddr; nrec = &epd->dtepd_rec[j]; if (nrec->dtrd_uarg != rec->dtrd_uarg) break; if (nrec->dtrd_action != act) { return (dt_set_errno(dtp, EDT_BADAGG)); } naddr = buf->dtbd_data + offs + nrec->dtrd_offset; aggvars[naggvars++] = /* LINTED - alignment */ *((dtrace_aggvarid_t *)naddr); } i = j - 1; bzero(&pd, sizeof (pd)); pd.dtpa_dtp = dtp; pd.dtpa_fp = fp; assert(naggvars >= 1); if (naggvars == 1) { pd.dtpa_id = aggvars[0]; dt_free(dtp, aggvars); if (dt_printf(dtp, fp, "\n") < 0 || dtrace_aggregate_walk_sorted(dtp, dt_print_agg, &pd) < 0) return (-1); goto nextrec; } if (dt_printf(dtp, fp, "\n") < 0 || dtrace_aggregate_walk_joined(dtp, aggvars, naggvars, dt_print_aggs, &pd) < 0) { dt_free(dtp, aggvars); return (-1); } dt_free(dtp, aggvars); goto nextrec; } if (act == DTRACEACT_TRACEMEM) { if (tracememsize == 0 || tracememsize > rec->dtrd_size) { tracememsize = rec->dtrd_size; } n = dt_print_bytes(dtp, fp, addr, - tracememsize, 33, quiet, 1); + tracememsize, -33, quiet, 1); tracememsize = 0; if (n < 0) return (-1); goto nextrec; } switch (rec->dtrd_size) { case sizeof (uint64_t): n = dt_printf(dtp, fp, quiet ? "%lld" : " %16lld", /* LINTED - alignment */ *((unsigned long long *)addr)); break; case sizeof (uint32_t): n = dt_printf(dtp, fp, quiet ? "%d" : " %8d", /* LINTED - alignment */ *((uint32_t *)addr)); break; case sizeof (uint16_t): n = dt_printf(dtp, fp, quiet ? "%d" : " %5d", /* LINTED - alignment */ *((uint16_t *)addr)); break; case sizeof (uint8_t): n = dt_printf(dtp, fp, quiet ? "%d" : " %3d", *((uint8_t *)addr)); break; default: n = dt_print_bytes(dtp, fp, addr, - rec->dtrd_size, 33, quiet, 0); + rec->dtrd_size, -33, quiet, 0); break; } if (n < 0) return (-1); /* errno is set for us */ nextrec: if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) return (-1); /* errno is set for us */ } /* * Call the record callback with a NULL record to indicate * that we're done processing this EPID. */ rval = (*rfunc)(&data, NULL, arg); nextepid: offs += epd->dtepd_size; dtp->dt_last_epid = id; if (just_one) { buf->dtbd_oldest = offs; break; } } dtp->dt_flow = data.dtpda_flow; dtp->dt_indent = data.dtpda_indent; dtp->dt_prefix = data.dtpda_prefix; if ((drops = buf->dtbd_drops) == 0) return (0); /* * Explicitly zero the drops to prevent us from processing them again. */ buf->dtbd_drops = 0; return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops)); } /* * Reduce memory usage by shrinking the buffer if it's no more than half full. * Note, we need to preserve the alignment of the data at dtbd_oldest, which is * only 4-byte aligned. */ static void dt_realloc_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf, int cursize) { uint64_t used = buf->dtbd_size - buf->dtbd_oldest; if (used < cursize / 2) { int misalign = buf->dtbd_oldest & (sizeof (uint64_t) - 1); char *newdata = dt_alloc(dtp, used + misalign); if (newdata == NULL) return; bzero(newdata, misalign); bcopy(buf->dtbd_data + buf->dtbd_oldest, newdata + misalign, used); dt_free(dtp, buf->dtbd_data); buf->dtbd_oldest = misalign; buf->dtbd_size = used + misalign; buf->dtbd_data = newdata; } } /* * If the ring buffer has wrapped, the data is not in order. Rearrange it * so that it is. Note, we need to preserve the alignment of the data at * dtbd_oldest, which is only 4-byte aligned. */ static int dt_unring_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf) { int misalign; char *newdata, *ndp; if (buf->dtbd_oldest == 0) return (0); misalign = buf->dtbd_oldest & (sizeof (uint64_t) - 1); newdata = ndp = dt_alloc(dtp, buf->dtbd_size + misalign); if (newdata == NULL) return (-1); assert(0 == (buf->dtbd_size & (sizeof (uint64_t) - 1))); bzero(ndp, misalign); ndp += misalign; bcopy(buf->dtbd_data + buf->dtbd_oldest, ndp, buf->dtbd_size - buf->dtbd_oldest); ndp += buf->dtbd_size - buf->dtbd_oldest; bcopy(buf->dtbd_data, ndp, buf->dtbd_oldest); dt_free(dtp, buf->dtbd_data); buf->dtbd_oldest = 0; buf->dtbd_data = newdata; buf->dtbd_size += misalign; return (0); } static void dt_put_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf) { dt_free(dtp, buf->dtbd_data); dt_free(dtp, buf); } /* * Returns 0 on success, in which case *cbp will be filled in if we retrieved * data, or NULL if there is no data for this CPU. * Returns -1 on failure and sets dt_errno. */ static int dt_get_buf(dtrace_hdl_t *dtp, int cpu, dtrace_bufdesc_t **bufp) { dtrace_optval_t size; dtrace_bufdesc_t *buf = dt_zalloc(dtp, sizeof (*buf)); int error; if (buf == NULL) return (-1); (void) dtrace_getopt(dtp, "bufsize", &size); buf->dtbd_data = dt_alloc(dtp, size); if (buf->dtbd_data == NULL) { dt_free(dtp, buf); return (-1); } buf->dtbd_size = size; buf->dtbd_cpu = cpu; #if defined(sun) if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { #else if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &buf) == -1) { #endif dt_put_buf(dtp, buf); /* * If we failed with ENOENT, it may be because the * CPU was unconfigured -- this is okay. Any other * error, however, is unexpected. */ if (errno == ENOENT) { *bufp = NULL; return (0); } return (dt_set_errno(dtp, errno)); } error = dt_unring_buf(dtp, buf); if (error != 0) { dt_put_buf(dtp, buf); return (error); } dt_realloc_buf(dtp, buf, size); *bufp = buf; return (0); } typedef struct dt_begin { dtrace_consume_probe_f *dtbgn_probefunc; dtrace_consume_rec_f *dtbgn_recfunc; void *dtbgn_arg; dtrace_handle_err_f *dtbgn_errhdlr; void *dtbgn_errarg; int dtbgn_beginonly; } dt_begin_t; static int dt_consume_begin_probe(const dtrace_probedata_t *data, void *arg) { dt_begin_t *begin = arg; dtrace_probedesc_t *pd = data->dtpda_pdesc; int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); if (begin->dtbgn_beginonly) { if (!(r1 && r2)) return (DTRACE_CONSUME_NEXT); } else { if (r1 && r2) return (DTRACE_CONSUME_NEXT); } /* * We have a record that we're interested in. Now call the underlying * probe function... */ return (begin->dtbgn_probefunc(data, begin->dtbgn_arg)); } static int dt_consume_begin_record(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) { dt_begin_t *begin = arg; return (begin->dtbgn_recfunc(data, rec, begin->dtbgn_arg)); } static int dt_consume_begin_error(const dtrace_errdata_t *data, void *arg) { dt_begin_t *begin = (dt_begin_t *)arg; dtrace_probedesc_t *pd = data->dteda_pdesc; int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); if (begin->dtbgn_beginonly) { if (!(r1 && r2)) return (DTRACE_HANDLE_OK); } else { if (r1 && r2) return (DTRACE_HANDLE_OK); } return (begin->dtbgn_errhdlr(data, begin->dtbgn_errarg)); } static int dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) { /* * There's this idea that the BEGIN probe should be processed before * everything else, and that the END probe should be processed after * anything else. In the common case, this is pretty easy to deal * with. However, a situation may arise where the BEGIN enabling and * END enabling are on the same CPU, and some enabling in the middle * occurred on a different CPU. To deal with this (blech!) we need to * consume the BEGIN buffer up until the end of the BEGIN probe, and * then set it aside. We will then process every other CPU, and then * we'll return to the BEGIN CPU and process the rest of the data * (which will inevitably include the END probe, if any). Making this * even more complicated (!) is the library's ERROR enabling. Because * this enabling is processed before we even get into the consume call * back, any ERROR firing would result in the library's ERROR enabling * being processed twice -- once in our first pass (for BEGIN probes), * and again in our second pass (for everything but BEGIN probes). To * deal with this, we interpose on the ERROR handler to assure that we * only process ERROR enablings induced by BEGIN enablings in the * first pass, and that we only process ERROR enablings _not_ induced * by BEGIN enablings in the second pass. */ dt_begin_t begin; processorid_t cpu = dtp->dt_beganon; int rval, i; static int max_ncpus; dtrace_bufdesc_t *buf; dtp->dt_beganon = -1; if (dt_get_buf(dtp, cpu, &buf) != 0) return (-1); if (buf == NULL) return (0); if (!dtp->dt_stopped || buf->dtbd_cpu != dtp->dt_endedon) { /* * This is the simple case. We're either not stopped, or if * we are, we actually processed any END probes on another * CPU. We can simply consume this buffer and return. */ rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); return (rval); } begin.dtbgn_probefunc = pf; begin.dtbgn_recfunc = rf; begin.dtbgn_arg = arg; begin.dtbgn_beginonly = 1; /* * We need to interpose on the ERROR handler to be sure that we * only process ERRORs induced by BEGIN. */ begin.dtbgn_errhdlr = dtp->dt_errhdlr; begin.dtbgn_errarg = dtp->dt_errarg; dtp->dt_errhdlr = dt_consume_begin_error; dtp->dt_errarg = &begin; rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, dt_consume_begin_probe, dt_consume_begin_record, &begin); dtp->dt_errhdlr = begin.dtbgn_errhdlr; dtp->dt_errarg = begin.dtbgn_errarg; if (rval != 0) { dt_put_buf(dtp, buf); return (rval); } if (max_ncpus == 0) max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *nbuf; if (i == cpu) continue; if (dt_get_buf(dtp, i, &nbuf) != 0) { dt_put_buf(dtp, buf); return (-1); } if (nbuf == NULL) continue; rval = dt_consume_cpu(dtp, fp, i, nbuf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, nbuf); if (rval != 0) { dt_put_buf(dtp, buf); return (rval); } } /* * Okay -- we're done with the other buffers. Now we want to * reconsume the first buffer -- but this time we're looking for * everything _but_ BEGIN. And of course, in order to only consume * those ERRORs _not_ associated with BEGIN, we need to reinstall our * ERROR interposition function... */ begin.dtbgn_beginonly = 0; assert(begin.dtbgn_errhdlr == dtp->dt_errhdlr); assert(begin.dtbgn_errarg == dtp->dt_errarg); dtp->dt_errhdlr = dt_consume_begin_error; dtp->dt_errarg = &begin; rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, dt_consume_begin_probe, dt_consume_begin_record, &begin); dtp->dt_errhdlr = begin.dtbgn_errhdlr; dtp->dt_errarg = begin.dtbgn_errarg; return (rval); } /* ARGSUSED */ static uint64_t dt_buf_oldest(void *elem, void *arg) { dtrace_bufdesc_t *buf = elem; size_t offs = buf->dtbd_oldest; while (offs < buf->dtbd_size) { dtrace_rechdr_t *dtrh = /* LINTED - alignment */ (dtrace_rechdr_t *)(buf->dtbd_data + offs); if (dtrh->dtrh_epid == DTRACE_EPIDNONE) { offs += sizeof (dtrace_epid_t); } else { return (DTRACE_RECORD_LOAD_TIMESTAMP(dtrh)); } } /* There are no records left; use the time the buffer was retrieved. */ return (buf->dtbd_timestamp); } int dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) { dtrace_optval_t size; static int max_ncpus; int i, rval; dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_SWITCHRATE]; hrtime_t now = gethrtime(); if (dtp->dt_lastswitch != 0) { if (now - dtp->dt_lastswitch < interval) return (0); dtp->dt_lastswitch += interval; } else { dtp->dt_lastswitch = now; } if (!dtp->dt_active) return (dt_set_errno(dtp, EINVAL)); if (max_ncpus == 0) max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; if (pf == NULL) pf = (dtrace_consume_probe_f *)dt_nullprobe; if (rf == NULL) rf = (dtrace_consume_rec_f *)dt_nullrec; if (dtp->dt_options[DTRACEOPT_TEMPORAL] == DTRACEOPT_UNSET) { /* * The output will not be in the order it was traced. Rather, * we will consume all of the data from each CPU's buffer in * turn. We apply special handling for the records from BEGIN * and END probes so that they are consumed first and last, * respectively. * * If we have just begun, we want to first process the CPU that * executed the BEGIN probe (if any). */ if (dtp->dt_active && dtp->dt_beganon != -1 && (rval = dt_consume_begin(dtp, fp, pf, rf, arg)) != 0) return (rval); for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *buf; /* * If we have stopped, we want to process the CPU on * which the END probe was processed only _after_ we * have processed everything else. */ if (dtp->dt_stopped && (i == dtp->dt_endedon)) continue; if (dt_get_buf(dtp, i, &buf) != 0) return (-1); if (buf == NULL) continue; dtp->dt_flow = 0; dtp->dt_indent = 0; dtp->dt_prefix = NULL; rval = dt_consume_cpu(dtp, fp, i, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); if (rval != 0) return (rval); } if (dtp->dt_stopped) { dtrace_bufdesc_t *buf; if (dt_get_buf(dtp, dtp->dt_endedon, &buf) != 0) return (-1); if (buf == NULL) return (0); rval = dt_consume_cpu(dtp, fp, dtp->dt_endedon, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); return (rval); } } else { /* * The output will be in the order it was traced (or for * speculations, when it was committed). We retrieve a buffer * from each CPU and put it into a priority queue, which sorts * based on the first entry in the buffer. This is sufficient * because entries within a buffer are already sorted. * * We then consume records one at a time, always consuming the * oldest record, as determined by the priority queue. When * we reach the end of the time covered by these buffers, * we need to stop and retrieve more records on the next pass. * The kernel tells us the time covered by each buffer, in * dtbd_timestamp. The first buffer's timestamp tells us the * time covered by all buffers, as subsequently retrieved * buffers will cover to a more recent time. */ uint64_t *drops = alloca(max_ncpus * sizeof (uint64_t)); uint64_t first_timestamp = 0; uint_t cookie = 0; dtrace_bufdesc_t *buf; bzero(drops, max_ncpus * sizeof (uint64_t)); if (dtp->dt_bufq == NULL) { dtp->dt_bufq = dt_pq_init(dtp, max_ncpus * 2, dt_buf_oldest, NULL); if (dtp->dt_bufq == NULL) /* ENOMEM */ return (-1); } /* Retrieve data from each CPU. */ (void) dtrace_getopt(dtp, "bufsize", &size); for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *buf; if (dt_get_buf(dtp, i, &buf) != 0) return (-1); if (buf != NULL) { if (first_timestamp == 0) first_timestamp = buf->dtbd_timestamp; assert(buf->dtbd_timestamp >= first_timestamp); dt_pq_insert(dtp->dt_bufq, buf); drops[i] = buf->dtbd_drops; buf->dtbd_drops = 0; } } /* Consume records. */ for (;;) { dtrace_bufdesc_t *buf = dt_pq_pop(dtp->dt_bufq); uint64_t timestamp; if (buf == NULL) break; timestamp = dt_buf_oldest(buf, dtp); assert(timestamp >= dtp->dt_last_timestamp); dtp->dt_last_timestamp = timestamp; if (timestamp == buf->dtbd_timestamp) { /* * We've reached the end of the time covered * by this buffer. If this is the oldest * buffer, we must do another pass * to retrieve more data. */ dt_put_buf(dtp, buf); if (timestamp == first_timestamp && !dtp->dt_stopped) break; continue; } if ((rval = dt_consume_cpu(dtp, fp, buf->dtbd_cpu, buf, B_TRUE, pf, rf, arg)) != 0) return (rval); dt_pq_insert(dtp->dt_bufq, buf); } /* Consume drops. */ for (i = 0; i < max_ncpus; i++) { if (drops[i] != 0) { int error = dt_handle_cpudrop(dtp, i, DTRACEDROP_PRINCIPAL, drops[i]); if (error != 0) return (error); } } /* * Reduce memory usage by re-allocating smaller buffers * for the "remnants". */ while (buf = dt_pq_walk(dtp->dt_bufq, &cookie)) dt_realloc_buf(dtp, buf, buf->dtbd_size); } return (0); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_decl.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_decl.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_decl.c (revision 268578) @@ -1,1123 +1,1129 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include static dt_decl_t * dt_decl_check(dt_decl_t *ddp) { if (ddp->dd_kind == CTF_K_UNKNOWN) return (ddp); /* nothing to check if the type is not yet set */ if (ddp->dd_name != NULL && strcmp(ddp->dd_name, "char") == 0 && (ddp->dd_attr & (DT_DA_SHORT | DT_DA_LONG | DT_DA_LONGLONG))) { xyerror(D_DECL_CHARATTR, "invalid type declaration: short and " "long may not be used with char type\n"); } if (ddp->dd_name != NULL && strcmp(ddp->dd_name, "void") == 0 && (ddp->dd_attr & (DT_DA_SHORT | DT_DA_LONG | DT_DA_LONGLONG | (DT_DA_SIGNED | DT_DA_UNSIGNED)))) { xyerror(D_DECL_VOIDATTR, "invalid type declaration: attributes " "may not be used with void type\n"); } if (ddp->dd_kind != CTF_K_INTEGER && (ddp->dd_attr & (DT_DA_SIGNED | DT_DA_UNSIGNED))) { xyerror(D_DECL_SIGNINT, "invalid type declaration: signed and " "unsigned may only be used with integer type\n"); } if (ddp->dd_kind != CTF_K_INTEGER && ddp->dd_kind != CTF_K_FLOAT && (ddp->dd_attr & (DT_DA_LONG | DT_DA_LONGLONG))) { xyerror(D_DECL_LONGINT, "invalid type declaration: long and " "long long may only be used with integer or " "floating-point type\n"); } return (ddp); } dt_decl_t * dt_decl_alloc(ushort_t kind, char *name) { dt_decl_t *ddp = malloc(sizeof (dt_decl_t)); if (ddp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); ddp->dd_kind = kind; ddp->dd_attr = 0; ddp->dd_ctfp = NULL; ddp->dd_type = CTF_ERR; ddp->dd_name = name; ddp->dd_node = NULL; ddp->dd_next = NULL; return (ddp); } void dt_decl_free(dt_decl_t *ddp) { dt_decl_t *ndp; for (; ddp != NULL; ddp = ndp) { ndp = ddp->dd_next; free(ddp->dd_name); dt_node_list_free(&ddp->dd_node); free(ddp); } } void dt_decl_reset(void) { dt_scope_t *dsp = &yypcb->pcb_dstack; dt_decl_t *ddp = dsp->ds_decl; while (ddp->dd_next != NULL) { dsp->ds_decl = ddp->dd_next; ddp->dd_next = NULL; dt_decl_free(ddp); ddp = dsp->ds_decl; } } dt_decl_t * dt_decl_push(dt_decl_t *ddp) { dt_scope_t *dsp = &yypcb->pcb_dstack; dt_decl_t *top = dsp->ds_decl; if (top != NULL && top->dd_kind == CTF_K_UNKNOWN && top->dd_name == NULL) { top->dd_kind = CTF_K_INTEGER; (void) dt_decl_check(top); } assert(ddp->dd_next == NULL); ddp->dd_next = top; dsp->ds_decl = ddp; return (ddp); } dt_decl_t * dt_decl_pop(void) { dt_scope_t *dsp = &yypcb->pcb_dstack; dt_decl_t *ddp = dt_decl_top(); dsp->ds_decl = NULL; free(dsp->ds_ident); dsp->ds_ident = NULL; dsp->ds_ctfp = NULL; dsp->ds_type = CTF_ERR; dsp->ds_class = DT_DC_DEFAULT; dsp->ds_enumval = -1; return (ddp); } dt_decl_t * dt_decl_pop_param(char **idp) { dt_scope_t *dsp = &yypcb->pcb_dstack; if (dsp->ds_class != DT_DC_DEFAULT && dsp->ds_class != DT_DC_REGISTER) { xyerror(D_DECL_PARMCLASS, "inappropriate storage class " "for function or associative array parameter\n"); } if (idp != NULL && dt_decl_top() != NULL) { *idp = dsp->ds_ident; dsp->ds_ident = NULL; } return (dt_decl_pop()); } dt_decl_t * dt_decl_top(void) { dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; if (ddp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NODECL); if (ddp->dd_kind == CTF_K_UNKNOWN && ddp->dd_name == NULL) { ddp->dd_kind = CTF_K_INTEGER; (void) dt_decl_check(ddp); } return (ddp); } dt_decl_t * dt_decl_ident(char *name) { dt_scope_t *dsp = &yypcb->pcb_dstack; dt_decl_t *ddp = dsp->ds_decl; if (dsp->ds_ident != NULL) { free(name); xyerror(D_DECL_IDENT, "old-style declaration or " "incorrect type specified\n"); } dsp->ds_ident = name; if (ddp == NULL) ddp = dt_decl_push(dt_decl_alloc(CTF_K_UNKNOWN, NULL)); return (ddp); } void dt_decl_class(dt_dclass_t class) { dt_scope_t *dsp = &yypcb->pcb_dstack; if (dsp->ds_class != DT_DC_DEFAULT) { xyerror(D_DECL_CLASS, "only one storage class allowed " "in a declaration\n"); } dsp->ds_class = class; } /* * Set the kind and name of the current declaration. If none is allocated, * make a new decl and push it on to the top of our stack. If the name or kind * is already set for the current decl, then we need to fail this declaration. * This can occur because too many types were given (e.g. "int int"), etc. */ dt_decl_t * dt_decl_spec(ushort_t kind, char *name) { dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; if (ddp == NULL) return (dt_decl_push(dt_decl_alloc(kind, name))); /* * If we already have a type name specified and we see another type * name, this is an error if the declaration is a typedef. If the * declaration is not a typedef, then the user may be trying to declare * a variable whose name has been returned by lex as a TNAME token: * call dt_decl_ident() as if the grammar's IDENT rule was matched. */ if (ddp->dd_name != NULL && kind == CTF_K_TYPEDEF) { if (yypcb->pcb_dstack.ds_class != DT_DC_TYPEDEF) return (dt_decl_ident(name)); xyerror(D_DECL_IDRED, "identifier redeclared: %s\n", name); } if (ddp->dd_name != NULL || ddp->dd_kind != CTF_K_UNKNOWN) xyerror(D_DECL_COMBO, "invalid type combination\n"); ddp->dd_kind = kind; ddp->dd_name = name; return (dt_decl_check(ddp)); } dt_decl_t * dt_decl_attr(ushort_t attr) { dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; if (ddp == NULL) { ddp = dt_decl_push(dt_decl_alloc(CTF_K_UNKNOWN, NULL)); ddp->dd_attr = attr; return (ddp); } if (attr == DT_DA_LONG && (ddp->dd_attr & DT_DA_LONG)) { ddp->dd_attr &= ~DT_DA_LONG; attr = DT_DA_LONGLONG; } ddp->dd_attr |= attr; return (dt_decl_check(ddp)); } /* * Examine the list of formal parameters 'flist' and determine if the formal * name fnp->dn_string is defined in this list (B_TRUE) or not (B_FALSE). * If 'fnp' is in 'flist', do not search beyond 'fnp' itself in 'flist'. */ static int dt_decl_protoform(dt_node_t *fnp, dt_node_t *flist) { dt_node_t *dnp; for (dnp = flist; dnp != fnp && dnp != NULL; dnp = dnp->dn_list) { if (dnp->dn_string != NULL && strcmp(dnp->dn_string, fnp->dn_string) == 0) return (B_TRUE); } return (B_FALSE); } /* * Common code for parsing array, function, and probe definition prototypes. * The prototype node list is specified as 'plist'. The formal prototype * against which to compare the prototype is specified as 'flist'. If plist * and flist are the same, we require that named parameters are unique. If * plist and flist are different, we require that named parameters in plist * match a name that is present in flist. */ int dt_decl_prototype(dt_node_t *plist, dt_node_t *flist, const char *kind, uint_t flags) { char n[DT_TYPE_NAMELEN]; int is_void, v = 0, i = 1; int form = plist != flist; dt_node_t *dnp; for (dnp = plist; dnp != NULL; dnp = dnp->dn_list, i++) { if (dnp->dn_type == CTF_ERR && !(flags & DT_DP_VARARGS)) { dnerror(dnp, D_DECL_PROTO_VARARGS, "%s prototype may " "not use a variable-length argument list\n", kind); } if (dt_node_is_dynamic(dnp) && !(flags & DT_DP_DYNAMIC)) { dnerror(dnp, D_DECL_PROTO_TYPE, "%s prototype may not " "use parameter of type %s: %s, parameter #%d\n", kind, dt_node_type_name(dnp, n, sizeof (n)), dnp->dn_string ? dnp->dn_string : "(anonymous)", i); } is_void = dt_node_is_void(dnp); v += is_void; if (is_void && !(flags & DT_DP_VOID)) { dnerror(dnp, D_DECL_PROTO_TYPE, "%s prototype may not " "use parameter of type %s: %s, parameter #%d\n", kind, dt_node_type_name(dnp, n, sizeof (n)), dnp->dn_string ? dnp->dn_string : "(anonymous)", i); } if (is_void && dnp->dn_string != NULL) { dnerror(dnp, D_DECL_PROTO_NAME, "void parameter may " "not have a name: %s\n", dnp->dn_string); } if (dnp->dn_string != NULL && dt_decl_protoform(dnp, flist) != form) { dnerror(dnp, D_DECL_PROTO_FORM, "parameter is " "%s declared in %s prototype: %s, parameter #%d\n", form ? "not" : "already", kind, dnp->dn_string, i); } if (dnp->dn_string == NULL && !is_void && !(flags & DT_DP_ANON)) { dnerror(dnp, D_DECL_PROTO_NAME, "parameter declaration " "requires a name: parameter #%d\n", i); } } if (v != 0 && plist->dn_list != NULL) xyerror(D_DECL_PROTO_VOID, "void must be sole parameter\n"); return (v ? 0 : i - 1); /* return zero if sole parameter is 'void' */ } dt_decl_t * dt_decl_array(dt_node_t *dnp) { dt_decl_t *ddp = dt_decl_push(dt_decl_alloc(CTF_K_ARRAY, NULL)); dt_scope_t *dsp = &yypcb->pcb_dstack; dt_decl_t *ndp = ddp; /* * After pushing the array on to the decl stack, scan ahead for multi- * dimensional array declarations and push the current decl to the * bottom to match the resulting CTF type tree and data layout. Refer * to the comments in dt_decl_type() and ISO C 6.5.2.1 for more info. */ while (ndp->dd_next != NULL && ndp->dd_next->dd_kind == CTF_K_ARRAY) ndp = ndp->dd_next; /* skip to bottom-most array declaration */ if (ndp != ddp) { if (dnp != NULL && dnp->dn_kind == DT_NODE_TYPE) { xyerror(D_DECL_DYNOBJ, "cannot declare array of associative arrays\n"); } dsp->ds_decl = ddp->dd_next; ddp->dd_next = ndp->dd_next; ndp->dd_next = ddp; } if (ddp->dd_next->dd_name != NULL && strcmp(ddp->dd_next->dd_name, "void") == 0) xyerror(D_DECL_VOIDOBJ, "cannot declare array of void\n"); if (dnp != NULL && dnp->dn_kind != DT_NODE_TYPE) { dnp = ddp->dd_node = dt_node_cook(dnp, DT_IDFLG_REF); if (dt_node_is_posconst(dnp) == 0) { xyerror(D_DECL_ARRSUB, "positive integral constant " "expression or tuple signature expected as " "array declaration subscript\n"); } if (dnp->dn_value > UINT_MAX) xyerror(D_DECL_ARRBIG, "array dimension too big\n"); } else if (dnp != NULL) { ddp->dd_node = dnp; (void) dt_decl_prototype(dnp, dnp, "array", DT_DP_ANON); } return (ddp); } /* * When a function is declared, we need to fudge the decl stack a bit if the * declaration uses the function pointer (*)() syntax. In this case, the * dt_decl_func() call occurs *after* the dt_decl_ptr() call, even though the * resulting type is "pointer to function". To make the pointer land on top, * we check to see if 'pdp' is non-NULL and a pointer. If it is, we search * backward for a decl tagged with DT_DA_PAREN, and if one is found, the func * decl is inserted behind this node in the decl list instead of at the top. * In all cases, the func decl's dd_next pointer is set to the decl chain * for the function's return type and the function parameter list is discarded. */ dt_decl_t * dt_decl_func(dt_decl_t *pdp, dt_node_t *dnp) { dt_decl_t *ddp = dt_decl_alloc(CTF_K_FUNCTION, NULL); ddp->dd_node = dnp; (void) dt_decl_prototype(dnp, dnp, "function", DT_DP_VARARGS | DT_DP_VOID | DT_DP_ANON); if (pdp == NULL || pdp->dd_kind != CTF_K_POINTER) return (dt_decl_push(ddp)); while (pdp->dd_next != NULL && !(pdp->dd_next->dd_attr & DT_DA_PAREN)) pdp = pdp->dd_next; if (pdp->dd_next == NULL) return (dt_decl_push(ddp)); ddp->dd_next = pdp->dd_next; pdp->dd_next = ddp; return (pdp); } dt_decl_t * dt_decl_ptr(void) { return (dt_decl_push(dt_decl_alloc(CTF_K_POINTER, NULL))); } dt_decl_t * dt_decl_sou(uint_t kind, char *name) { dt_decl_t *ddp = dt_decl_spec(kind, name); char n[DT_TYPE_NAMELEN]; ctf_file_t *ctfp; ctf_id_t type; uint_t flag; if (yypcb->pcb_idepth != 0) ctfp = yypcb->pcb_hdl->dt_cdefs->dm_ctfp; else ctfp = yypcb->pcb_hdl->dt_ddefs->dm_ctfp; if (yypcb->pcb_dstack.ds_next != NULL) flag = CTF_ADD_NONROOT; else flag = CTF_ADD_ROOT; (void) snprintf(n, sizeof (n), "%s %s", kind == CTF_K_STRUCT ? "struct" : "union", name == NULL ? "(anon)" : name); if (name != NULL && (type = ctf_lookup_by_name(ctfp, n)) != CTF_ERR && ctf_type_kind(ctfp, type) != CTF_K_FORWARD) xyerror(D_DECL_TYPERED, "type redeclared: %s\n", n); if (kind == CTF_K_STRUCT) type = ctf_add_struct(ctfp, flag, name); else type = ctf_add_union(ctfp, flag, name); if (type == CTF_ERR || ctf_update(ctfp) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to define %s: %s\n", n, ctf_errmsg(ctf_errno(ctfp))); } ddp->dd_ctfp = ctfp; ddp->dd_type = type; dt_scope_push(ctfp, type); return (ddp); } void dt_decl_member(dt_node_t *dnp) { dt_scope_t *dsp = yypcb->pcb_dstack.ds_next; dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; char *ident = yypcb->pcb_dstack.ds_ident; const char *idname = ident ? ident : "(anon)"; char n[DT_TYPE_NAMELEN]; dtrace_typeinfo_t dtt; ctf_encoding_t cte; ctf_id_t base; uint_t kind; ssize_t size; if (dsp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOSCOPE); if (ddp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NODECL); if (dnp == NULL && ident == NULL) xyerror(D_DECL_MNAME, "member declaration requires a name\n"); if (ddp->dd_kind == CTF_K_UNKNOWN && ddp->dd_name == NULL) { ddp->dd_kind = CTF_K_INTEGER; (void) dt_decl_check(ddp); } if (dt_decl_type(ddp, &dtt) != 0) longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); if (ident != NULL && strchr(ident, '`') != NULL) { xyerror(D_DECL_SCOPE, "D scoping operator may not be used " "in a member name (%s)\n", ident); } if (dtt.dtt_ctfp == DT_DYN_CTFP(yypcb->pcb_hdl) && dtt.dtt_type == DT_DYN_TYPE(yypcb->pcb_hdl)) { xyerror(D_DECL_DYNOBJ, "cannot have dynamic member: %s\n", ident); } base = ctf_type_resolve(dtt.dtt_ctfp, dtt.dtt_type); kind = ctf_type_kind(dtt.dtt_ctfp, base); size = ctf_type_size(dtt.dtt_ctfp, base); if (kind == CTF_K_FORWARD || ((kind == CTF_K_STRUCT || kind == CTF_K_UNION) && size == 0)) { xyerror(D_DECL_INCOMPLETE, "incomplete struct/union/enum %s: " "%s\n", dt_type_name(dtt.dtt_ctfp, dtt.dtt_type, n, sizeof (n)), ident); } if (size == 0) xyerror(D_DECL_VOIDOBJ, "cannot have void member: %s\n", ident); /* * If a bit-field qualifier was part of the member declaration, create * a new integer type of the same name and attributes as the base type * and size equal to the specified number of bits. We reset 'dtt' to * refer to this new bit-field type and continue on to add the member. */ if (dnp != NULL) { dnp = dt_node_cook(dnp, DT_IDFLG_REF); /* * A bit-field member with no declarator is permitted to have * size zero and indicates that no more fields are to be packed * into the current storage unit. We ignore these directives * as the underlying ctf code currently does so for all fields. */ if (ident == NULL && dnp->dn_kind == DT_NODE_INT && dnp->dn_value == 0) { dt_node_free(dnp); goto done; } if (dt_node_is_posconst(dnp) == 0) { xyerror(D_DECL_BFCONST, "positive integral constant " "expression expected as bit-field size\n"); } if (ctf_type_kind(dtt.dtt_ctfp, base) != CTF_K_INTEGER || ctf_type_encoding(dtt.dtt_ctfp, base, &cte) == CTF_ERR || IS_VOID(cte)) { xyerror(D_DECL_BFTYPE, "invalid type for " "bit-field: %s\n", idname); } if (dnp->dn_value > cte.cte_bits) { xyerror(D_DECL_BFSIZE, "bit-field too big " "for type: %s\n", idname); } cte.cte_offset = 0; cte.cte_bits = (uint_t)dnp->dn_value; dtt.dtt_type = ctf_add_integer(dsp->ds_ctfp, CTF_ADD_NONROOT, ctf_type_name(dtt.dtt_ctfp, dtt.dtt_type, n, sizeof (n)), &cte); if (dtt.dtt_type == CTF_ERR || ctf_update(dsp->ds_ctfp) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to create type for " "member '%s': %s\n", idname, ctf_errmsg(ctf_errno(dsp->ds_ctfp))); } dtt.dtt_ctfp = dsp->ds_ctfp; dt_node_free(dnp); } /* * If the member type is not defined in the same CTF container as the * one associated with the current scope (i.e. the container for the * struct or union itself) or its parent, copy the member type into * this container and reset dtt to refer to the copied type. */ if (dtt.dtt_ctfp != dsp->ds_ctfp && dtt.dtt_ctfp != ctf_parent_file(dsp->ds_ctfp)) { dtt.dtt_type = ctf_add_type(dsp->ds_ctfp, dtt.dtt_ctfp, dtt.dtt_type); dtt.dtt_ctfp = dsp->ds_ctfp; if (dtt.dtt_type == CTF_ERR || ctf_update(dtt.dtt_ctfp) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to copy type of '%s': %s\n", idname, ctf_errmsg(ctf_errno(dtt.dtt_ctfp))); } } if (ctf_add_member(dsp->ds_ctfp, dsp->ds_type, ident, dtt.dtt_type) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to define member '%s': %s\n", idname, ctf_errmsg(ctf_errno(dsp->ds_ctfp))); } done: free(ident); yypcb->pcb_dstack.ds_ident = NULL; dt_decl_reset(); } /*ARGSUSED*/ static int dt_decl_hasmembers(const char *name, int value, void *private) { return (1); /* abort search and return true if a member exists */ } dt_decl_t * dt_decl_enum(char *name) { dt_decl_t *ddp = dt_decl_spec(CTF_K_ENUM, name); char n[DT_TYPE_NAMELEN]; ctf_file_t *ctfp; ctf_id_t type; uint_t flag; if (yypcb->pcb_idepth != 0) ctfp = yypcb->pcb_hdl->dt_cdefs->dm_ctfp; else ctfp = yypcb->pcb_hdl->dt_ddefs->dm_ctfp; if (yypcb->pcb_dstack.ds_next != NULL) flag = CTF_ADD_NONROOT; else flag = CTF_ADD_ROOT; (void) snprintf(n, sizeof (n), "enum %s", name ? name : "(anon)"); if (name != NULL && (type = ctf_lookup_by_name(ctfp, n)) != CTF_ERR) { if (ctf_enum_iter(ctfp, type, dt_decl_hasmembers, NULL)) xyerror(D_DECL_TYPERED, "type redeclared: %s\n", n); } else if ((type = ctf_add_enum(ctfp, flag, name)) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to define %s: %s\n", n, ctf_errmsg(ctf_errno(ctfp))); } ddp->dd_ctfp = ctfp; ddp->dd_type = type; dt_scope_push(ctfp, type); return (ddp); } void dt_decl_enumerator(char *s, dt_node_t *dnp) { dt_scope_t *dsp = yypcb->pcb_dstack.ds_next; dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_idnode_t *inp; dt_ident_t *idp; char *name; int value; name = alloca(strlen(s) + 1); (void) strcpy(name, s); free(s); if (dsp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOSCOPE); assert(dsp->ds_decl->dd_kind == CTF_K_ENUM); value = dsp->ds_enumval + 1; /* default is previous value plus one */ if (strchr(name, '`') != NULL) { xyerror(D_DECL_SCOPE, "D scoping operator may not be used in " "an enumerator name (%s)\n", name); } /* * If the enumerator is being assigned a value, cook and check the node * and then free it after we get the value. We also permit references * to identifiers which are previously defined enumerators in the type. */ if (dnp != NULL) { if (dnp->dn_kind != DT_NODE_IDENT || ctf_enum_value( dsp->ds_ctfp, dsp->ds_type, dnp->dn_string, &value) != 0) { dnp = dt_node_cook(dnp, DT_IDFLG_REF); if (dnp->dn_kind != DT_NODE_INT) { xyerror(D_DECL_ENCONST, "enumerator '%s' must " "be assigned to an integral constant " "expression\n", name); } if ((intmax_t)dnp->dn_value > INT_MAX || (intmax_t)dnp->dn_value < INT_MIN) { xyerror(D_DECL_ENOFLOW, "enumerator '%s' value " "overflows INT_MAX (%d)\n", name, INT_MAX); } value = (int)dnp->dn_value; } dt_node_free(dnp); } if (ctf_add_enumerator(dsp->ds_ctfp, dsp->ds_type, name, value) == CTF_ERR || ctf_update(dsp->ds_ctfp) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to define enumerator '%s': %s\n", name, ctf_errmsg(ctf_errno(dsp->ds_ctfp))); } dsp->ds_enumval = value; /* save most recent value */ /* * If the enumerator name matches an identifier in the global scope, * flag this as an error. We only do this for "D" enumerators to * prevent "C" header file enumerators from conflicting with the ever- * growing list of D built-in global variables and inlines. If a "C" * enumerator conflicts with a global identifier, we add the enumerator * but do not insert a corresponding inline (i.e. the D variable wins). */ if (dt_idstack_lookup(&yypcb->pcb_globals, name) != NULL) { if (dsp->ds_ctfp == dtp->dt_ddefs->dm_ctfp) { xyerror(D_DECL_IDRED, "identifier redeclared: %s\n", name); } else return; } dt_dprintf("add global enumerator %s = %d\n", name, value); idp = dt_idhash_insert(dtp->dt_globals, name, DT_IDENT_ENUM, DT_IDFLG_INLINE | DT_IDFLG_REF, 0, _dtrace_defattr, 0, &dt_idops_inline, NULL, dtp->dt_gen); if (idp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); yyintprefix = 0; yyintsuffix[0] = '\0'; yyintdecimal = 0; dnp = dt_node_int(value); - dt_node_type_assign(dnp, dsp->ds_ctfp, dsp->ds_type); + dt_node_type_assign(dnp, dsp->ds_ctfp, dsp->ds_type, B_FALSE); if ((inp = malloc(sizeof (dt_idnode_t))) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); /* * Remove the INT node from the node allocation list and store it in * din_list and din_root so it persists with and is freed by the ident. */ assert(yypcb->pcb_list == dnp); yypcb->pcb_list = dnp->dn_link; dnp->dn_link = NULL; bzero(inp, sizeof (dt_idnode_t)); inp->din_list = dnp; inp->din_root = dnp; idp->di_iarg = inp; idp->di_ctfp = dsp->ds_ctfp; idp->di_type = dsp->ds_type; } /* * Look up the type corresponding to the specified decl stack. The scoping of * the underlying type names is handled by dt_type_lookup(). We build up the * name from the specified string and prefixes and then lookup the type. If * we fail, an errmsg is saved and the caller must abort with EDT_COMPILER. */ int dt_decl_type(dt_decl_t *ddp, dtrace_typeinfo_t *tip) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_module_t *dmp; ctf_arinfo_t r; ctf_id_t type; char n[DT_TYPE_NAMELEN]; uint_t flag; char *name; int rv; + tip->dtt_flags = 0; + /* * Based on our current #include depth and decl stack depth, determine * which dynamic CTF module and scope to use when adding any new types. */ dmp = yypcb->pcb_idepth ? dtp->dt_cdefs : dtp->dt_ddefs; flag = yypcb->pcb_dstack.ds_next ? CTF_ADD_NONROOT : CTF_ADD_ROOT; + + if (ddp->dd_attr & DT_DA_USER) + tip->dtt_flags = DTT_FL_USER; /* * If we have already cached a CTF type for this decl, then we just * return the type information for the cached type. */ if (ddp->dd_ctfp != NULL && (dmp = dt_module_lookup_by_ctf(dtp, ddp->dd_ctfp)) != NULL) { tip->dtt_object = dmp->dm_name; tip->dtt_ctfp = ddp->dd_ctfp; tip->dtt_type = ddp->dd_type; return (0); } /* * Currently CTF treats all function pointers identically. We cache a * representative ID of kind CTF_K_FUNCTION and just return that type. * If we want to support full function declarations, dd_next refers to * the declaration of the function return type, and the parameter list * should be parsed and hung off a new pointer inside of this decl. */ if (ddp->dd_kind == CTF_K_FUNCTION) { tip->dtt_object = dtp->dt_ddefs->dm_name; tip->dtt_ctfp = DT_FUNC_CTFP(dtp); tip->dtt_type = DT_FUNC_TYPE(dtp); return (0); } /* * If the decl is a pointer, resolve the rest of the stack by calling * dt_decl_type() recursively and then compute a pointer to the result. * Similar to the code above, we return a cached id for function ptrs. */ if (ddp->dd_kind == CTF_K_POINTER) { if (ddp->dd_next->dd_kind == CTF_K_FUNCTION) { tip->dtt_object = dtp->dt_ddefs->dm_name; tip->dtt_ctfp = DT_FPTR_CTFP(dtp); tip->dtt_type = DT_FPTR_TYPE(dtp); return (0); } if ((rv = dt_decl_type(ddp->dd_next, tip)) == 0 && (rv = dt_type_pointer(tip)) != 0) { xywarn(D_UNKNOWN, "cannot find type: %s*: %s\n", dt_type_name(tip->dtt_ctfp, tip->dtt_type, n, sizeof (n)), ctf_errmsg(dtp->dt_ctferr)); } return (rv); } /* * If the decl is an array, we must find the base type and then call * dt_decl_type() recursively and then build an array of the result. * The C and D multi-dimensional array syntax requires that consecutive * array declarations be processed from right-to-left (i.e. top-down * from the perspective of the declaration stack). For example, an * array declaration such as int x[3][5] is stored on the stack as: * * (bottom) NULL <- ( INT "int" ) <- ( ARR [3] ) <- ( ARR [5] ) (top) * * but means that x is declared to be an array of 3 objects each of * which is an array of 5 integers, or in CTF representation: * * type T1:( content=int, nelems=5 ) type T2:( content=T1, nelems=3 ) * * For more details, refer to K&R[5.7] and ISO C 6.5.2.1. Rather than * overcomplicate the implementation of dt_decl_type(), we push array * declarations down into the stack in dt_decl_array(), above, so that * by the time dt_decl_type() is called, the decl stack looks like: * * (bottom) NULL <- ( INT "int" ) <- ( ARR [5] ) <- ( ARR [3] ) (top) * * which permits a straightforward recursive descent of the decl stack * to build the corresponding CTF type tree in the appropriate order. */ if (ddp->dd_kind == CTF_K_ARRAY) { /* * If the array decl has a parameter list associated with it, * this is an associative array declaration: return . */ if (ddp->dd_node != NULL && ddp->dd_node->dn_kind == DT_NODE_TYPE) { tip->dtt_object = dtp->dt_ddefs->dm_name; tip->dtt_ctfp = DT_DYN_CTFP(dtp); tip->dtt_type = DT_DYN_TYPE(dtp); return (0); } if ((rv = dt_decl_type(ddp->dd_next, tip)) != 0) return (rv); /* * If the array base type is not defined in the target * container or its parent, copy the type to the target * container and reset dtt_ctfp and dtt_type to the copy. */ if (tip->dtt_ctfp != dmp->dm_ctfp && tip->dtt_ctfp != ctf_parent_file(dmp->dm_ctfp)) { tip->dtt_type = ctf_add_type(dmp->dm_ctfp, tip->dtt_ctfp, tip->dtt_type); tip->dtt_ctfp = dmp->dm_ctfp; if (tip->dtt_type == CTF_ERR || ctf_update(tip->dtt_ctfp) == CTF_ERR) { xywarn(D_UNKNOWN, "failed to copy type: %s\n", ctf_errmsg(ctf_errno(tip->dtt_ctfp))); return (-1); } } /* * The array index type is irrelevant in C and D: just set it * to "long" for all array types that we create on-the-fly. */ r.ctr_contents = tip->dtt_type; r.ctr_index = ctf_lookup_by_name(tip->dtt_ctfp, "long"); r.ctr_nelems = ddp->dd_node ? (uint_t)ddp->dd_node->dn_value : 0; tip->dtt_object = dmp->dm_name; tip->dtt_ctfp = dmp->dm_ctfp; tip->dtt_type = ctf_add_array(dmp->dm_ctfp, CTF_ADD_ROOT, &r); if (tip->dtt_type == CTF_ERR || ctf_update(tip->dtt_ctfp) == CTF_ERR) { xywarn(D_UNKNOWN, "failed to create array type: %s\n", ctf_errmsg(ctf_errno(tip->dtt_ctfp))); return (-1); } return (0); } /* * Allocate space for the type name and enough space for the maximum * additional text ("unsigned long long \0" requires 20 more bytes). */ name = alloca(ddp->dd_name ? strlen(ddp->dd_name) + 20 : 20); name[0] = '\0'; switch (ddp->dd_kind) { case CTF_K_INTEGER: case CTF_K_FLOAT: if (ddp->dd_attr & DT_DA_SIGNED) (void) strcat(name, "signed "); if (ddp->dd_attr & DT_DA_UNSIGNED) (void) strcat(name, "unsigned "); if (ddp->dd_attr & DT_DA_SHORT) (void) strcat(name, "short "); if (ddp->dd_attr & DT_DA_LONG) (void) strcat(name, "long "); if (ddp->dd_attr & DT_DA_LONGLONG) (void) strcat(name, "long long "); if (ddp->dd_attr == 0 && ddp->dd_name == NULL) (void) strcat(name, "int"); break; case CTF_K_STRUCT: (void) strcpy(name, "struct "); break; case CTF_K_UNION: (void) strcpy(name, "union "); break; case CTF_K_ENUM: (void) strcpy(name, "enum "); break; case CTF_K_TYPEDEF: break; default: xywarn(D_UNKNOWN, "internal error -- " "bad decl kind %u\n", ddp->dd_kind); return (-1); } /* * Add dd_name unless a short, long, or long long is explicitly * suffixed by int. We use the C/CTF canonical names for integers. */ if (ddp->dd_name != NULL && (ddp->dd_kind != CTF_K_INTEGER || (ddp->dd_attr & (DT_DA_SHORT | DT_DA_LONG | DT_DA_LONGLONG)) == 0)) (void) strcat(name, ddp->dd_name); /* * Lookup the type. If we find it, we're done. Otherwise create a * forward tag for the type if it is a struct, union, or enum. If * we can't find it and we can't create a tag, return failure. */ if ((rv = dt_type_lookup(name, tip)) == 0) return (rv); switch (ddp->dd_kind) { case CTF_K_STRUCT: case CTF_K_UNION: case CTF_K_ENUM: type = ctf_add_forward(dmp->dm_ctfp, flag, ddp->dd_name, ddp->dd_kind); break; default: xywarn(D_UNKNOWN, "failed to resolve type %s: %s\n", name, dtrace_errmsg(dtp, dtrace_errno(dtp))); return (rv); } if (type == CTF_ERR || ctf_update(dmp->dm_ctfp) == CTF_ERR) { xywarn(D_UNKNOWN, "failed to add forward tag for %s: %s\n", name, ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (-1); } ddp->dd_ctfp = dmp->dm_ctfp; ddp->dd_type = type; tip->dtt_object = dmp->dm_name; tip->dtt_ctfp = dmp->dm_ctfp; tip->dtt_type = type; return (0); } void dt_scope_create(dt_scope_t *dsp) { dsp->ds_decl = NULL; dsp->ds_next = NULL; dsp->ds_ident = NULL; dsp->ds_ctfp = NULL; dsp->ds_type = CTF_ERR; dsp->ds_class = DT_DC_DEFAULT; dsp->ds_enumval = -1; } void dt_scope_destroy(dt_scope_t *dsp) { dt_scope_t *nsp; for (; dsp != NULL; dsp = nsp) { dt_decl_free(dsp->ds_decl); free(dsp->ds_ident); nsp = dsp->ds_next; if (dsp != &yypcb->pcb_dstack) free(dsp); } } void dt_scope_push(ctf_file_t *ctfp, ctf_id_t type) { dt_scope_t *rsp = &yypcb->pcb_dstack; dt_scope_t *dsp = malloc(sizeof (dt_scope_t)); if (dsp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dsp->ds_decl = rsp->ds_decl; dsp->ds_next = rsp->ds_next; dsp->ds_ident = rsp->ds_ident; dsp->ds_ctfp = ctfp; dsp->ds_type = type; dsp->ds_class = rsp->ds_class; dsp->ds_enumval = rsp->ds_enumval; dt_scope_create(rsp); rsp->ds_next = dsp; } dt_decl_t * dt_scope_pop(void) { dt_scope_t *rsp = &yypcb->pcb_dstack; dt_scope_t *dsp = rsp->ds_next; if (dsp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOSCOPE); if (dsp->ds_ctfp != NULL && ctf_update(dsp->ds_ctfp) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to update type definitions: %s\n", ctf_errmsg(ctf_errno(dsp->ds_ctfp))); } dt_decl_free(rsp->ds_decl); free(rsp->ds_ident); rsp->ds_decl = dsp->ds_decl; rsp->ds_next = dsp->ds_next; rsp->ds_ident = dsp->ds_ident; rsp->ds_ctfp = dsp->ds_ctfp; rsp->ds_type = dsp->ds_type; rsp->ds_class = dsp->ds_class; rsp->ds_enumval = dsp->ds_enumval; free(dsp); return (rsp->ds_decl); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_decl.h =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_decl.h (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_decl.h (revision 268578) @@ -1,126 +1,129 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ #ifndef _DT_DECL_H #define _DT_DECL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include #include #ifdef __cplusplus extern "C" { #endif struct dt_node; /* forward declaration of dt_node_t */ typedef struct dt_decl { ushort_t dd_kind; /* declaration kind (CTF_K_* kind) */ ushort_t dd_attr; /* attributes (DT_DA_* flags) */ ctf_file_t *dd_ctfp; /* CTF container for decl's type */ ctf_id_t dd_type; /* CTF identifier for decl's type */ char *dd_name; /* string name of this decl (or NULL) */ struct dt_node *dd_node; /* node for array size or parm list */ struct dt_decl *dd_next; /* next declaration in list */ } dt_decl_t; #define DT_DA_SIGNED 0x0001 /* signed integer value */ #define DT_DA_UNSIGNED 0x0002 /* unsigned integer value */ #define DT_DA_SHORT 0x0004 /* short integer value */ #define DT_DA_LONG 0x0008 /* long integer or double */ #define DT_DA_LONGLONG 0x0010 /* long long integer value */ #define DT_DA_CONST 0x0020 /* qualify type as const */ #define DT_DA_RESTRICT 0x0040 /* qualify type as restrict */ #define DT_DA_VOLATILE 0x0080 /* qualify type as volatile */ #define DT_DA_PAREN 0x0100 /* parenthesis tag */ +#define DT_DA_USER 0x0200 /* user-land type specifier */ typedef enum dt_dclass { DT_DC_DEFAULT, /* no storage class specified */ DT_DC_AUTO, /* automatic storage */ DT_DC_REGISTER, /* register storage */ DT_DC_STATIC, /* static storage */ DT_DC_EXTERN, /* extern storage */ DT_DC_TYPEDEF, /* type definition */ DT_DC_SELF, /* thread-local storage */ DT_DC_THIS /* clause-local storage */ } dt_dclass_t; typedef struct dt_scope { dt_decl_t *ds_decl; /* pointer to top of decl stack */ struct dt_scope *ds_next; /* pointer to next scope */ char *ds_ident; /* identifier for this scope (if any) */ ctf_file_t *ds_ctfp; /* CTF container for this scope */ ctf_id_t ds_type; /* CTF id of enclosing type */ dt_dclass_t ds_class; /* declaration class for this scope */ int ds_enumval; /* most recent enumerator value */ } dt_scope_t; extern dt_decl_t *dt_decl_alloc(ushort_t, char *); extern void dt_decl_free(dt_decl_t *); extern void dt_decl_reset(void); extern dt_decl_t *dt_decl_push(dt_decl_t *); extern dt_decl_t *dt_decl_pop(void); extern dt_decl_t *dt_decl_pop_param(char **); extern dt_decl_t *dt_decl_top(void); extern dt_decl_t *dt_decl_ident(char *); extern void dt_decl_class(dt_dclass_t); #define DT_DP_VARARGS 0x1 /* permit varargs in prototype */ #define DT_DP_DYNAMIC 0x2 /* permit dynamic type in prototype */ #define DT_DP_VOID 0x4 /* permit void type in prototype */ #define DT_DP_ANON 0x8 /* permit anonymous parameters */ extern int dt_decl_prototype(struct dt_node *, struct dt_node *, const char *, uint_t); extern dt_decl_t *dt_decl_spec(ushort_t, char *); extern dt_decl_t *dt_decl_attr(ushort_t); extern dt_decl_t *dt_decl_array(struct dt_node *); extern dt_decl_t *dt_decl_func(dt_decl_t *, struct dt_node *); extern dt_decl_t *dt_decl_ptr(void); extern dt_decl_t *dt_decl_sou(uint_t, char *); extern void dt_decl_member(struct dt_node *); extern dt_decl_t *dt_decl_enum(char *); extern void dt_decl_enumerator(char *, struct dt_node *); extern int dt_decl_type(dt_decl_t *, dtrace_typeinfo_t *); extern void dt_scope_create(dt_scope_t *); extern void dt_scope_destroy(dt_scope_t *); extern void dt_scope_push(ctf_file_t *, ctf_id_t); extern dt_decl_t *dt_scope_pop(void); #ifdef __cplusplus } #endif #endif /* _DT_DECL_H */ Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_dis.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_dis.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_dis.c (revision 268578) @@ -1,524 +1,526 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. */ #include #include #include #include /*ARGSUSED*/ static void dt_dis_log(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%-4s %%r%u, %%r%u, %%r%u", name, DIF_INSTR_R1(in), DIF_INSTR_R2(in), DIF_INSTR_RD(in)); } /*ARGSUSED*/ static void dt_dis_branch(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%-4s %u", name, DIF_INSTR_LABEL(in)); } /*ARGSUSED*/ static void dt_dis_load(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%-4s [%%r%u], %%r%u", name, DIF_INSTR_R1(in), DIF_INSTR_RD(in)); } /*ARGSUSED*/ static void dt_dis_store(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%-4s %%r%u, [%%r%u]", name, DIF_INSTR_R1(in), DIF_INSTR_RD(in)); } /*ARGSUSED*/ static void dt_dis_str(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%s", name); } /*ARGSUSED*/ static void dt_dis_r1rd(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%-4s %%r%u, %%r%u", name, DIF_INSTR_R1(in), DIF_INSTR_RD(in)); } /*ARGSUSED*/ static void dt_dis_cmp(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%-4s %%r%u, %%r%u", name, DIF_INSTR_R1(in), DIF_INSTR_R2(in)); } /*ARGSUSED*/ static void dt_dis_tst(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%-4s %%r%u", name, DIF_INSTR_R1(in)); } static const char * dt_dis_varname(const dtrace_difo_t *dp, uint_t id, uint_t scope) { const dtrace_difv_t *dvp = dp->dtdo_vartab; uint_t i; for (i = 0; i < dp->dtdo_varlen; i++, dvp++) { if (dvp->dtdv_id == id && dvp->dtdv_scope == scope) { if (dvp->dtdv_name < dp->dtdo_strlen) return (dp->dtdo_strtab + dvp->dtdv_name); break; } } return (NULL); } static uint_t dt_dis_scope(const char *name) { switch (name[2]) { case 'l': return (DIFV_SCOPE_LOCAL); case 't': return (DIFV_SCOPE_THREAD); case 'g': return (DIFV_SCOPE_GLOBAL); default: return (-1u); } } static void dt_dis_lda(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { uint_t var = DIF_INSTR_R1(in); const char *vname; (void) fprintf(fp, "%-4s DT_VAR(%u), %%r%u, %%r%u", name, var, DIF_INSTR_R2(in), DIF_INSTR_RD(in)); if ((vname = dt_dis_varname(dp, var, dt_dis_scope(name))) != NULL) (void) fprintf(fp, "\t\t! DT_VAR(%u) = \"%s\"", var, vname); } static void dt_dis_ldv(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { uint_t var = DIF_INSTR_VAR(in); const char *vname; (void) fprintf(fp, "%-4s DT_VAR(%u), %%r%u", name, var, DIF_INSTR_RD(in)); if ((vname = dt_dis_varname(dp, var, dt_dis_scope(name))) != NULL) (void) fprintf(fp, "\t\t! DT_VAR(%u) = \"%s\"", var, vname); } static void dt_dis_stv(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { uint_t var = DIF_INSTR_VAR(in); const char *vname; (void) fprintf(fp, "%-4s %%r%u, DT_VAR(%u)", name, DIF_INSTR_RS(in), var); if ((vname = dt_dis_varname(dp, var, dt_dis_scope(name))) != NULL) (void) fprintf(fp, "\t\t! DT_VAR(%u) = \"%s\"", var, vname); } static void dt_dis_setx(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { uint_t intptr = DIF_INSTR_INTEGER(in); (void) fprintf(fp, "%-4s DT_INTEGER[%u], %%r%u", name, intptr, DIF_INSTR_RD(in)); if (intptr < dp->dtdo_intlen) { (void) fprintf(fp, "\t\t! 0x%llx", (u_longlong_t)dp->dtdo_inttab[intptr]); } } static void dt_dis_sets(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { uint_t strptr = DIF_INSTR_STRING(in); (void) fprintf(fp, "%-4s DT_STRING[%u], %%r%u", name, strptr, DIF_INSTR_RD(in)); if (strptr < dp->dtdo_strlen) (void) fprintf(fp, "\t\t! \"%s\"", dp->dtdo_strtab + strptr); } /*ARGSUSED*/ static void dt_dis_ret(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { (void) fprintf(fp, "%-4s %%r%u", name, DIF_INSTR_RD(in)); } /*ARGSUSED*/ static void dt_dis_call(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { uint_t subr = DIF_INSTR_SUBR(in); (void) fprintf(fp, "%-4s DIF_SUBR(%u), %%r%u\t\t! %s", name, subr, DIF_INSTR_RD(in), dtrace_subrstr(NULL, subr)); } /*ARGSUSED*/ static void dt_dis_pushts(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { static const char *const tnames[] = { "D type", "string" }; uint_t type = DIF_INSTR_TYPE(in); const char *pad; if (DIF_INSTR_OP(in) == DIF_OP_PUSHTV) { (void) fprintf(fp, "%-4s DT_TYPE(%u), %%r%u", name, type, DIF_INSTR_RS(in)); pad = "\t\t"; } else { (void) fprintf(fp, "%-4s DT_TYPE(%u), %%r%u, %%r%u", name, type, DIF_INSTR_R2(in), DIF_INSTR_RS(in)); pad = "\t"; } if (type < sizeof (tnames) / sizeof (tnames[0])) { (void) fprintf(fp, "%s! DT_TYPE(%u) = %s", pad, type, tnames[type]); } } static void dt_dis_xlate(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) { uint_t xlr = DIF_INSTR_XLREF(in); (void) fprintf(fp, "%-4s DT_XLREF[%u], %%r%u", name, xlr, DIF_INSTR_RD(in)); if (xlr < dp->dtdo_xlmlen) { (void) fprintf(fp, "\t\t! DT_XLREF[%u] = %u.%s", xlr, (uint_t)dp->dtdo_xlmtab[xlr]->dn_membexpr->dn_xlator->dx_id, dp->dtdo_xlmtab[xlr]->dn_membname); } } static char * dt_dis_typestr(const dtrace_diftype_t *t, char *buf, size_t len) { char kind[16], ckind[16]; switch (t->dtdt_kind) { case DIF_TYPE_CTF: (void) strcpy(kind, "D type"); break; case DIF_TYPE_STRING: (void) strcpy(kind, "string"); break; default: (void) snprintf(kind, sizeof (kind), "0x%x", t->dtdt_kind); } switch (t->dtdt_ckind) { case CTF_K_UNKNOWN: (void) strcpy(ckind, "unknown"); break; case CTF_K_INTEGER: (void) strcpy(ckind, "integer"); break; case CTF_K_FLOAT: (void) strcpy(ckind, "float"); break; case CTF_K_POINTER: (void) strcpy(ckind, "pointer"); break; case CTF_K_ARRAY: (void) strcpy(ckind, "array"); break; case CTF_K_FUNCTION: (void) strcpy(ckind, "function"); break; case CTF_K_STRUCT: (void) strcpy(ckind, "struct"); break; case CTF_K_UNION: (void) strcpy(ckind, "union"); break; case CTF_K_ENUM: (void) strcpy(ckind, "enum"); break; case CTF_K_FORWARD: (void) strcpy(ckind, "forward"); break; case CTF_K_TYPEDEF: (void) strcpy(ckind, "typedef"); break; case CTF_K_VOLATILE: (void) strcpy(ckind, "volatile"); break; case CTF_K_CONST: (void) strcpy(ckind, "const"); break; case CTF_K_RESTRICT: (void) strcpy(ckind, "restrict"); break; default: (void) snprintf(ckind, sizeof (ckind), "0x%x", t->dtdt_ckind); } - if (t->dtdt_flags & DIF_TF_BYREF) { - (void) snprintf(buf, len, "%s (%s) by ref (size %lu)", - kind, ckind, (ulong_t)t->dtdt_size); + if (t->dtdt_flags & (DIF_TF_BYREF | DIF_TF_BYUREF)) { + (void) snprintf(buf, len, "%s (%s) by %sref (size %lu)", + kind, ckind, (t->dtdt_flags & DIF_TF_BYUREF) ? "user " : "", + (ulong_t)t->dtdt_size); } else { (void) snprintf(buf, len, "%s (%s) (size %lu)", kind, ckind, (ulong_t)t->dtdt_size); } return (buf); } static void dt_dis_rtab(const char *rtag, const dtrace_difo_t *dp, FILE *fp, const dof_relodesc_t *rp, uint32_t len) { (void) fprintf(fp, "\n%-4s %-8s %-8s %s\n", rtag, "OFFSET", "DATA", "NAME"); for (; len != 0; len--, rp++) { (void) fprintf(fp, "%-4u %-8llu %-8llu %s\n", rp->dofr_type, (u_longlong_t)rp->dofr_offset, (u_longlong_t)rp->dofr_data, &dp->dtdo_strtab[rp->dofr_name]); } } void dt_dis(const dtrace_difo_t *dp, FILE *fp) { static const struct opent { const char *op_name; void (*op_func)(const dtrace_difo_t *, const char *, dif_instr_t, FILE *); } optab[] = { { "(illegal opcode)", dt_dis_str }, { "or", dt_dis_log }, /* DIF_OP_OR */ { "xor", dt_dis_log }, /* DIF_OP_XOR */ { "and", dt_dis_log }, /* DIF_OP_AND */ { "sll", dt_dis_log }, /* DIF_OP_SLL */ { "srl", dt_dis_log }, /* DIF_OP_SRL */ { "sub", dt_dis_log }, /* DIF_OP_SUB */ { "add", dt_dis_log }, /* DIF_OP_ADD */ { "mul", dt_dis_log }, /* DIF_OP_MUL */ { "sdiv", dt_dis_log }, /* DIF_OP_SDIV */ { "udiv", dt_dis_log }, /* DIF_OP_UDIV */ { "srem", dt_dis_log }, /* DIF_OP_SREM */ { "urem", dt_dis_log }, /* DIF_OP_UREM */ { "not", dt_dis_r1rd }, /* DIF_OP_NOT */ { "mov", dt_dis_r1rd }, /* DIF_OP_MOV */ { "cmp", dt_dis_cmp }, /* DIF_OP_CMP */ { "tst", dt_dis_tst }, /* DIF_OP_TST */ { "ba", dt_dis_branch }, /* DIF_OP_BA */ { "be", dt_dis_branch }, /* DIF_OP_BE */ { "bne", dt_dis_branch }, /* DIF_OP_BNE */ { "bg", dt_dis_branch }, /* DIF_OP_BG */ { "bgu", dt_dis_branch }, /* DIF_OP_BGU */ { "bge", dt_dis_branch }, /* DIF_OP_BGE */ { "bgeu", dt_dis_branch }, /* DIF_OP_BGEU */ { "bl", dt_dis_branch }, /* DIF_OP_BL */ { "blu", dt_dis_branch }, /* DIF_OP_BLU */ { "ble", dt_dis_branch }, /* DIF_OP_BLE */ { "bleu", dt_dis_branch }, /* DIF_OP_BLEU */ { "ldsb", dt_dis_load }, /* DIF_OP_LDSB */ { "ldsh", dt_dis_load }, /* DIF_OP_LDSH */ { "ldsw", dt_dis_load }, /* DIF_OP_LDSW */ { "ldub", dt_dis_load }, /* DIF_OP_LDUB */ { "lduh", dt_dis_load }, /* DIF_OP_LDUH */ { "lduw", dt_dis_load }, /* DIF_OP_LDUW */ { "ldx", dt_dis_load }, /* DIF_OP_LDX */ { "ret", dt_dis_ret }, /* DIF_OP_RET */ { "nop", dt_dis_str }, /* DIF_OP_NOP */ { "setx", dt_dis_setx }, /* DIF_OP_SETX */ { "sets", dt_dis_sets }, /* DIF_OP_SETS */ { "scmp", dt_dis_cmp }, /* DIF_OP_SCMP */ { "ldga", dt_dis_lda }, /* DIF_OP_LDGA */ { "ldgs", dt_dis_ldv }, /* DIF_OP_LDGS */ { "stgs", dt_dis_stv }, /* DIF_OP_STGS */ { "ldta", dt_dis_lda }, /* DIF_OP_LDTA */ { "ldts", dt_dis_ldv }, /* DIF_OP_LDTS */ { "stts", dt_dis_stv }, /* DIF_OP_STTS */ { "sra", dt_dis_log }, /* DIF_OP_SRA */ { "call", dt_dis_call }, /* DIF_OP_CALL */ { "pushtr", dt_dis_pushts }, /* DIF_OP_PUSHTR */ { "pushtv", dt_dis_pushts }, /* DIF_OP_PUSHTV */ { "popts", dt_dis_str }, /* DIF_OP_POPTS */ { "flushts", dt_dis_str }, /* DIF_OP_FLUSHTS */ { "ldgaa", dt_dis_ldv }, /* DIF_OP_LDGAA */ { "ldtaa", dt_dis_ldv }, /* DIF_OP_LDTAA */ { "stgaa", dt_dis_stv }, /* DIF_OP_STGAA */ { "sttaa", dt_dis_stv }, /* DIF_OP_STTAA */ { "ldls", dt_dis_ldv }, /* DIF_OP_LDLS */ { "stls", dt_dis_stv }, /* DIF_OP_STLS */ { "allocs", dt_dis_r1rd }, /* DIF_OP_ALLOCS */ { "copys", dt_dis_log }, /* DIF_OP_COPYS */ { "stb", dt_dis_store }, /* DIF_OP_STB */ { "sth", dt_dis_store }, /* DIF_OP_STH */ { "stw", dt_dis_store }, /* DIF_OP_STW */ { "stx", dt_dis_store }, /* DIF_OP_STX */ { "uldsb", dt_dis_load }, /* DIF_OP_ULDSB */ { "uldsh", dt_dis_load }, /* DIF_OP_ULDSH */ { "uldsw", dt_dis_load }, /* DIF_OP_ULDSW */ { "uldub", dt_dis_load }, /* DIF_OP_ULDUB */ { "ulduh", dt_dis_load }, /* DIF_OP_ULDUH */ { "ulduw", dt_dis_load }, /* DIF_OP_ULDUW */ { "uldx", dt_dis_load }, /* DIF_OP_ULDX */ { "rldsb", dt_dis_load }, /* DIF_OP_RLDSB */ { "rldsh", dt_dis_load }, /* DIF_OP_RLDSH */ { "rldsw", dt_dis_load }, /* DIF_OP_RLDSW */ { "rldub", dt_dis_load }, /* DIF_OP_RLDUB */ { "rlduh", dt_dis_load }, /* DIF_OP_RLDUH */ { "rlduw", dt_dis_load }, /* DIF_OP_RLDUW */ { "rldx", dt_dis_load }, /* DIF_OP_RLDX */ { "xlate", dt_dis_xlate }, /* DIF_OP_XLATE */ { "xlarg", dt_dis_xlate }, /* DIF_OP_XLARG */ }; const struct opent *op; ulong_t i = 0; char type[DT_TYPE_NAMELEN]; (void) fprintf(fp, "\nDIFO 0x%p returns %s\n", (void *)dp, dt_dis_typestr(&dp->dtdo_rtype, type, sizeof (type))); (void) fprintf(fp, "%-3s %-8s %s\n", "OFF", "OPCODE", "INSTRUCTION"); for (i = 0; i < dp->dtdo_len; i++) { dif_instr_t instr = dp->dtdo_buf[i]; dif_instr_t opcode = DIF_INSTR_OP(instr); if (opcode >= sizeof (optab) / sizeof (optab[0])) opcode = 0; /* force invalid opcode message */ op = &optab[opcode]; (void) fprintf(fp, "%02lu: %08x ", i, instr); op->op_func(dp, op->op_name, instr, fp); (void) fprintf(fp, "\n"); } if (dp->dtdo_varlen != 0) { (void) fprintf(fp, "\n%-16s %-4s %-3s %-3s %-4s %s\n", "NAME", "ID", "KND", "SCP", "FLAG", "TYPE"); } for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; char kind[4], scope[4], flags[16] = { 0 }; switch (v->dtdv_kind) { case DIFV_KIND_ARRAY: (void) strcpy(kind, "arr"); break; case DIFV_KIND_SCALAR: (void) strcpy(kind, "scl"); break; default: (void) snprintf(kind, sizeof (kind), "%u", v->dtdv_kind); } switch (v->dtdv_scope) { case DIFV_SCOPE_GLOBAL: (void) strcpy(scope, "glb"); break; case DIFV_SCOPE_THREAD: (void) strcpy(scope, "tls"); break; case DIFV_SCOPE_LOCAL: (void) strcpy(scope, "loc"); break; default: (void) snprintf(scope, sizeof (scope), "%u", v->dtdv_scope); } if (v->dtdv_flags & ~(DIFV_F_REF | DIFV_F_MOD)) { (void) snprintf(flags, sizeof (flags), "/0x%x", v->dtdv_flags & ~(DIFV_F_REF | DIFV_F_MOD)); } if (v->dtdv_flags & DIFV_F_REF) (void) strcat(flags, "/r"); if (v->dtdv_flags & DIFV_F_MOD) (void) strcat(flags, "/w"); (void) fprintf(fp, "%-16s %-4x %-3s %-3s %-4s %s\n", &dp->dtdo_strtab[v->dtdv_name], v->dtdv_id, kind, scope, flags + 1, dt_dis_typestr(&v->dtdv_type, type, sizeof (type))); } if (dp->dtdo_xlmlen != 0) { (void) fprintf(fp, "\n%-4s %-3s %-12s %s\n", "XLID", "ARG", "MEMBER", "TYPE"); } for (i = 0; i < dp->dtdo_xlmlen; i++) { dt_node_t *dnp = dp->dtdo_xlmtab[i]; dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator; (void) fprintf(fp, "%-4u %-3d %-12s %s\n", (uint_t)dxp->dx_id, dxp->dx_arg, dnp->dn_membname, dt_node_type_name(dnp, type, sizeof (type))); } if (dp->dtdo_krelen != 0) dt_dis_rtab("KREL", dp, fp, dp->dtdo_kreltab, dp->dtdo_krelen); if (dp->dtdo_urelen != 0) dt_dis_rtab("UREL", dp, fp, dp->dtdo_ureltab, dp->dtdo_urelen); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_error.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_error.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_error.c (revision 268578) @@ -1,240 +1,241 @@ /* * 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 2009 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 static const struct { int err; const char *msg; } _dt_errlist[] = { { EDT_VERSION, "Client requested version newer than library" }, { EDT_VERSINVAL, "Version is not properly formatted or is too large" }, { EDT_VERSUNDEF, "Requested version is not supported by compiler" }, { EDT_VERSREDUCED, "Requested version conflicts with earlier setting" }, { EDT_CTF, "Unexpected libctf error" }, { EDT_COMPILER, "Error in D program compilation" }, { EDT_NOTUPREG, "Insufficient tuple registers to generate code" }, { EDT_NOMEM, "Memory allocation failure" }, { EDT_INT2BIG, "Integer constant table limit exceeded" }, { EDT_STR2BIG, "String constant table limit exceeded" }, { EDT_NOMOD, "Unknown module name" }, { EDT_NOPROV, "Unknown provider name" }, { EDT_NOPROBE, "No probe matches description" }, { EDT_NOSYM, "Unknown symbol name" }, { EDT_NOSYMADDR, "No symbol corresponds to address" }, { EDT_NOTYPE, "Unknown type name" }, { EDT_NOVAR, "Unknown variable name" }, { EDT_NOAGG, "Unknown aggregation name" }, { EDT_BADSCOPE, "Improper use of scoping operator in type name" }, { EDT_BADSPEC, "Overspecified probe description" }, { EDT_BADSPCV, "Undefined macro variable in probe description" }, { EDT_BADID, "Unknown probe identifier" }, { EDT_NOTLOADED, "Module is no longer loaded" }, { EDT_NOCTF, "Module does not contain any CTF data" }, { EDT_DATAMODEL, "Module and program data models do not match" }, { EDT_DIFVERS, "Library uses newer DIF version than kernel" }, { EDT_BADAGG, "Unknown aggregating action" }, { EDT_FIO, "Error occurred while reading from input stream" }, { EDT_DIFINVAL, "DIF program content is invalid" }, { EDT_DIFSIZE, "DIF program exceeds maximum program size" }, { EDT_DIFFAULT, "DIF program contains invalid pointer" }, { EDT_BADPROBE, "Invalid probe specification" }, { EDT_BADPGLOB, "Probe description has too many globbing characters" }, { EDT_NOSCOPE, "Declaration scope stack underflow" }, { EDT_NODECL, "Declaration stack underflow" }, { EDT_DMISMATCH, "Data record list does not match statement" }, { EDT_DOFFSET, "Data record offset exceeds buffer boundary" }, { EDT_DALIGN, "Data record has inappropriate alignment" }, { EDT_BADOPTNAME, "Invalid option name" }, { EDT_BADOPTVAL, "Invalid value for specified option" }, { EDT_BADOPTCTX, "Option cannot be used from within a D program" }, { EDT_CPPFORK, "Failed to fork preprocessor" }, { EDT_CPPEXEC, "Failed to exec preprocessor" }, { EDT_CPPENT, "Preprocessor not found" }, { EDT_CPPERR, "Preprocessor failed to process input program" }, { EDT_SYMOFLOW, "Symbol table identifier space exhausted" }, { EDT_ACTIVE, "Operation illegal when tracing is active" }, { EDT_DESTRUCTIVE, "Destructive actions not allowed" }, { EDT_NOANON, "No anonymous tracing state" }, { EDT_ISANON, "Can't claim anonymous state and enable probes" }, { EDT_ENDTOOBIG, "END enablings exceed size of principal buffer" }, { EDT_NOCONV, "Failed to load type for printf conversion" }, { EDT_BADCONV, "Incomplete printf conversion" }, { EDT_BADERROR, "Invalid library ERROR action" }, { EDT_ERRABORT, "Abort due to error" }, { EDT_DROPABORT, "Abort due to drop" }, { EDT_DIRABORT, "Abort explicitly directed" }, { EDT_BADRVAL, "Invalid return value from callback" }, { EDT_BADNORMAL, "Invalid normalization" }, { EDT_BUFTOOSMALL, "Enabling exceeds size of buffer" }, { EDT_BADTRUNC, "Invalid truncation" }, { EDT_BUSY, "DTrace cannot be used when kernel debugger is active" }, { EDT_ACCESS, "DTrace requires additional privileges" }, { EDT_NOENT, "DTrace device not available on system" }, { EDT_BRICKED, "Abort due to systemic unresponsiveness" }, { EDT_HARDWIRE, "Failed to load language definitions" }, { EDT_ELFVERSION, "libelf is out-of-date with respect to libdtrace" }, { EDT_NOBUFFERED, "Attempt to buffer output without handler" }, { EDT_UNSTABLE, "Description matched an unstable set of probes" }, { EDT_BADSETOPT, "Invalid setopt() library action" }, { EDT_BADSTACKPC, "Invalid stack program counter size" }, { EDT_BADAGGVAR, "Invalid aggregation variable identifier" }, { EDT_OVERSION, "Client requested deprecated version of library" }, { EDT_ENABLING_ERR, "Failed to enable probe" }, - { EDT_NOPROBES, "No probe sites found for declared provider" } + { EDT_NOPROBES, "No probe sites found for declared provider" }, + { EDT_CANTLOAD, "Failed to load module" }, }; static const int _dt_nerr = sizeof (_dt_errlist) / sizeof (_dt_errlist[0]); const char * dtrace_errmsg(dtrace_hdl_t *dtp, int error) { const char *str; int i; if (error == EDT_COMPILER && dtp != NULL && dtp->dt_errmsg[0] != '\0') str = dtp->dt_errmsg; else if (error == EDT_CTF && dtp != NULL && dtp->dt_ctferr != 0) str = ctf_errmsg(dtp->dt_ctferr); else if (error >= EDT_BASE && (error - EDT_BASE) < _dt_nerr) { for (i = 0; i < _dt_nerr; i++) { if (_dt_errlist[i].err == error) return (_dt_errlist[i].msg); } str = NULL; } else str = strerror(error); return (str ? str : "Unknown error"); } int dtrace_errno(dtrace_hdl_t *dtp) { return (dtp->dt_errno); } #if defined(sun) int dt_set_errno(dtrace_hdl_t *dtp, int err) { dtp->dt_errno = err; return (-1); } #else int _dt_set_errno(dtrace_hdl_t *dtp, int err, const char *errfile, int errline) { dtp->dt_errno = err; dtp->dt_errfile = errfile; dtp->dt_errline = errline; return (-1); } void dt_get_errloc(dtrace_hdl_t *dtp, const char **p_errfile, int *p_errline) { *p_errfile = dtp->dt_errfile; *p_errline = dtp->dt_errline; } #endif void dt_set_errmsg(dtrace_hdl_t *dtp, const char *errtag, const char *region, const char *filename, int lineno, const char *format, va_list ap) { size_t len, n; char *p, *s; s = dtp->dt_errmsg; n = sizeof (dtp->dt_errmsg); if (errtag != NULL && (yypcb->pcb_cflags & DTRACE_C_ETAGS)) (void) snprintf(s, n, "[%s] ", errtag); else s[0] = '\0'; len = strlen(dtp->dt_errmsg); s = dtp->dt_errmsg + len; n = sizeof (dtp->dt_errmsg) - len; if (filename == NULL) filename = dtp->dt_filetag; if (filename != NULL) (void) snprintf(s, n, "\"%s\", line %d: ", filename, lineno); else if (lineno != 0) (void) snprintf(s, n, "line %d: ", lineno); else if (region != NULL) (void) snprintf(s, n, "in %s: ", region); len = strlen(dtp->dt_errmsg); s = dtp->dt_errmsg + len; n = sizeof (dtp->dt_errmsg) - len; (void) vsnprintf(s, n, format, ap); if ((p = strrchr(dtp->dt_errmsg, '\n')) != NULL) *p = '\0'; /* remove trailing \n from message buffer */ dtp->dt_errtag = errtag; } /*ARGSUSED*/ const char * dtrace_faultstr(dtrace_hdl_t *dtp, int fault) { int i; static const struct { int code; const char *str; } faults[] = { { DTRACEFLT_BADADDR, "invalid address" }, { DTRACEFLT_BADALIGN, "invalid alignment" }, { DTRACEFLT_ILLOP, "illegal operation" }, { DTRACEFLT_DIVZERO, "divide-by-zero" }, { DTRACEFLT_NOSCRATCH, "out of scratch space" }, { DTRACEFLT_KPRIV, "invalid kernel access" }, { DTRACEFLT_UPRIV, "invalid user access" }, { DTRACEFLT_TUPOFLOW, "tuple stack overflow" }, { DTRACEFLT_BADSTACK, "bad stack" }, { DTRACEFLT_LIBRARY, "library-level fault" }, { 0, NULL } }; for (i = 0; faults[i].str != NULL; i++) { if (faults[i].code == fault) return (faults[i].str); } return ("unknown fault"); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_grammar.y =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_grammar.y (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_grammar.y (revision 268578) @@ -1,834 +1,838 @@ %{ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include #define OP1(op, c) dt_node_op1(op, c) #define OP2(op, l, r) dt_node_op2(op, l, r) #define OP3(x, y, z) dt_node_op3(x, y, z) #define LINK(l, r) dt_node_link(l, r) #define DUP(s) strdup(s) %} %union { dt_node_t *l_node; dt_decl_t *l_decl; char *l_str; uintmax_t l_int; int l_tok; } %token DT_TOK_COMMA DT_TOK_ELLIPSIS %token DT_TOK_ASGN DT_TOK_ADD_EQ DT_TOK_SUB_EQ DT_TOK_MUL_EQ %token DT_TOK_DIV_EQ DT_TOK_MOD_EQ DT_TOK_AND_EQ DT_TOK_XOR_EQ DT_TOK_OR_EQ %token DT_TOK_LSH_EQ DT_TOK_RSH_EQ DT_TOK_QUESTION DT_TOK_COLON %token DT_TOK_LOR DT_TOK_LXOR DT_TOK_LAND %token DT_TOK_BOR DT_TOK_XOR DT_TOK_BAND DT_TOK_EQU DT_TOK_NEQ %token DT_TOK_LT DT_TOK_LE DT_TOK_GT DT_TOK_GE DT_TOK_LSH DT_TOK_RSH %token DT_TOK_ADD DT_TOK_SUB DT_TOK_MUL DT_TOK_DIV DT_TOK_MOD %token DT_TOK_LNEG DT_TOK_BNEG DT_TOK_ADDADD DT_TOK_SUBSUB %token DT_TOK_PREINC DT_TOK_POSTINC DT_TOK_PREDEC DT_TOK_POSTDEC %token DT_TOK_IPOS DT_TOK_INEG DT_TOK_DEREF DT_TOK_ADDROF %token DT_TOK_OFFSETOF DT_TOK_SIZEOF DT_TOK_STRINGOF DT_TOK_XLATE %token DT_TOK_LPAR DT_TOK_RPAR DT_TOK_LBRAC DT_TOK_RBRAC DT_TOK_PTR DT_TOK_DOT %token DT_TOK_STRING %token DT_TOK_IDENT %token DT_TOK_PSPEC %token DT_TOK_AGG %token DT_TOK_TNAME %token DT_TOK_INT %token DT_KEY_AUTO %token DT_KEY_BREAK %token DT_KEY_CASE %token DT_KEY_CHAR %token DT_KEY_CONST %token DT_KEY_CONTINUE %token DT_KEY_COUNTER %token DT_KEY_DEFAULT %token DT_KEY_DO %token DT_KEY_DOUBLE %token DT_KEY_ELSE %token DT_KEY_ENUM %token DT_KEY_EXTERN %token DT_KEY_FLOAT %token DT_KEY_FOR %token DT_KEY_GOTO %token DT_KEY_IF %token DT_KEY_IMPORT %token DT_KEY_INLINE %token DT_KEY_INT %token DT_KEY_LONG %token DT_KEY_PROBE %token DT_KEY_PROVIDER %token DT_KEY_REGISTER %token DT_KEY_RESTRICT %token DT_KEY_RETURN %token DT_KEY_SELF %token DT_KEY_SHORT %token DT_KEY_SIGNED %token DT_KEY_STATIC %token DT_KEY_STRING %token DT_KEY_STRUCT %token DT_KEY_SWITCH %token DT_KEY_THIS %token DT_KEY_TYPEDEF %token DT_KEY_UNION %token DT_KEY_UNSIGNED +%token DT_KEY_USERLAND %token DT_KEY_VOID %token DT_KEY_VOLATILE %token DT_KEY_WHILE %token DT_KEY_XLATOR %token DT_TOK_EPRED %token DT_CTX_DEXPR %token DT_CTX_DPROG %token DT_CTX_DTYPE %token DT_TOK_EOF 0 %left DT_TOK_COMMA %right DT_TOK_ASGN DT_TOK_ADD_EQ DT_TOK_SUB_EQ DT_TOK_MUL_EQ DT_TOK_DIV_EQ DT_TOK_MOD_EQ DT_TOK_AND_EQ DT_TOK_XOR_EQ DT_TOK_OR_EQ DT_TOK_LSH_EQ DT_TOK_RSH_EQ %left DT_TOK_QUESTION DT_TOK_COLON %left DT_TOK_LOR %left DT_TOK_LXOR %left DT_TOK_LAND %left DT_TOK_BOR %left DT_TOK_XOR %left DT_TOK_BAND %left DT_TOK_EQU DT_TOK_NEQ %left DT_TOK_LT DT_TOK_LE DT_TOK_GT DT_TOK_GE %left DT_TOK_LSH DT_TOK_RSH %left DT_TOK_ADD DT_TOK_SUB %left DT_TOK_MUL DT_TOK_DIV DT_TOK_MOD %right DT_TOK_LNEG DT_TOK_BNEG DT_TOK_ADDADD DT_TOK_SUBSUB DT_TOK_IPOS DT_TOK_INEG %right DT_TOK_DEREF DT_TOK_ADDROF DT_TOK_SIZEOF DT_TOK_STRINGOF DT_TOK_XLATE %left DT_TOK_LPAR DT_TOK_RPAR DT_TOK_LBRAC DT_TOK_RBRAC DT_TOK_PTR DT_TOK_DOT %type d_expression %type d_program %type d_type %type translation_unit %type external_declaration %type inline_definition %type translator_definition %type translator_member_list %type translator_member %type provider_definition %type provider_probe_list %type provider_probe %type probe_definition %type probe_specifiers %type probe_specifier_list %type probe_specifier %type statement_list %type statement %type declaration %type init_declarator_list %type init_declarator %type type_specifier %type type_qualifier %type struct_or_union_specifier %type specifier_qualifier_list %type enum_specifier %type declarator %type direct_declarator %type pointer %type type_qualifier_list %type type_name %type abstract_declarator %type direct_abstract_declarator %type parameter_type_list %type parameter_list %type parameter_declaration %type array %type array_parameters %type function %type function_parameters %type expression %type assignment_expression %type conditional_expression %type constant_expression %type logical_or_expression %type logical_xor_expression %type logical_and_expression %type inclusive_or_expression %type exclusive_or_expression %type and_expression %type equality_expression %type relational_expression %type shift_expression %type additive_expression %type multiplicative_expression %type cast_expression %type unary_expression %type postfix_expression %type primary_expression %type argument_expression_list %type assignment_operator %type unary_operator %type struct_or_union %% dtrace_program: d_expression DT_TOK_EOF { return (dt_node_root($1)); } | d_program DT_TOK_EOF { return (dt_node_root($1)); } | d_type DT_TOK_EOF { return (dt_node_root($1)); } ; d_expression: DT_CTX_DEXPR { $$ = NULL; } | DT_CTX_DEXPR expression { $$ = $2; } ; d_program: DT_CTX_DPROG { $$ = dt_node_program(NULL); } | DT_CTX_DPROG translation_unit { $$ = dt_node_program($2); } ; d_type: DT_CTX_DTYPE { $$ = NULL; } | DT_CTX_DTYPE type_name { $$ = (dt_node_t *)$2; } ; translation_unit: external_declaration | translation_unit external_declaration { $$ = LINK($1, $2); } ; external_declaration: inline_definition | translator_definition | provider_definition | probe_definition | declaration ; inline_definition: DT_KEY_INLINE declaration_specifiers declarator { dt_scope_push(NULL, CTF_ERR); } DT_TOK_ASGN assignment_expression ';' { /* * We push a new declaration scope before shifting the * assignment_expression in order to preserve ds_class * and ds_ident for use in dt_node_inline(). Once the * entire inline_definition rule is matched, pop the * scope and construct the inline using the saved decl. */ dt_scope_pop(); $$ = dt_node_inline($6); } ; translator_definition: DT_KEY_XLATOR type_name DT_TOK_LT type_name DT_TOK_IDENT DT_TOK_GT '{' translator_member_list '}' ';' { $$ = dt_node_xlator($2, $4, $5, $8); } | DT_KEY_XLATOR type_name DT_TOK_LT type_name DT_TOK_IDENT DT_TOK_GT '{' '}' ';' { $$ = dt_node_xlator($2, $4, $5, NULL); } ; translator_member_list: translator_member | translator_member_list translator_member { $$ = LINK($1,$2); } ; translator_member: DT_TOK_IDENT DT_TOK_ASGN assignment_expression ';' { $$ = dt_node_member(NULL, $1, $3); } ; provider_definition: DT_KEY_PROVIDER DT_TOK_IDENT '{' provider_probe_list '}' ';' { $$ = dt_node_provider($2, $4); } | DT_KEY_PROVIDER DT_TOK_IDENT '{' '}' ';' { $$ = dt_node_provider($2, NULL); } ; provider_probe_list: provider_probe | provider_probe_list provider_probe { $$ = LINK($1, $2); } ; provider_probe: DT_KEY_PROBE DT_TOK_IDENT function DT_TOK_COLON function ';' { $$ = dt_node_probe($2, 2, $3, $5); } | DT_KEY_PROBE DT_TOK_IDENT function ';' { $$ = dt_node_probe($2, 1, $3, NULL); } ; probe_definition: probe_specifiers { /* * If the input stream is a file, do not permit a probe * specification without / / or { } after * it. This can only occur if the next token is EOF or * an ambiguous predicate was slurped up as a comment. * We cannot perform this check if input() is a string * because dtrace(1M) [-fmnP] also use the compiler and * things like dtrace -n BEGIN have to be accepted. */ if (yypcb->pcb_fileptr != NULL) { dnerror($1, D_SYNTAX, "expected predicate and/" "or actions following probe description\n"); } $$ = dt_node_clause($1, NULL, NULL); } | probe_specifiers '{' statement_list '}' { $$ = dt_node_clause($1, NULL, $3); } | probe_specifiers DT_TOK_DIV expression DT_TOK_EPRED { dnerror($3, D_SYNTAX, "expected actions { } following " "probe description and predicate\n"); } | probe_specifiers DT_TOK_DIV expression DT_TOK_EPRED '{' statement_list '}' { $$ = dt_node_clause($1, $3, $6); } ; probe_specifiers: probe_specifier_list { yybegin(YYS_EXPR); $$ = $1; } ; probe_specifier_list: probe_specifier | probe_specifier_list DT_TOK_COMMA probe_specifier { $$ = LINK($1, $3); } ; probe_specifier: DT_TOK_PSPEC { $$ = dt_node_pdesc_by_name($1); } | DT_TOK_INT { $$ = dt_node_pdesc_by_id($1); } ; statement_list: statement { $$ = $1; } | statement_list ';' statement { $$ = LINK($1, $3); } ; statement: /* empty */ { $$ = NULL; } | expression { $$ = dt_node_statement($1); } ; argument_expression_list: assignment_expression | argument_expression_list DT_TOK_COMMA assignment_expression { $$ = LINK($1, $3); } ; primary_expression: DT_TOK_IDENT { $$ = dt_node_ident($1); } | DT_TOK_AGG { $$ = dt_node_ident($1); } | DT_TOK_INT { $$ = dt_node_int($1); } | DT_TOK_STRING { $$ = dt_node_string($1); } | DT_KEY_SELF { $$ = dt_node_ident(DUP("self")); } | DT_KEY_THIS { $$ = dt_node_ident(DUP("this")); } | DT_TOK_LPAR expression DT_TOK_RPAR { $$ = $2; } ; postfix_expression: primary_expression | postfix_expression DT_TOK_LBRAC argument_expression_list DT_TOK_RBRAC { $$ = OP2(DT_TOK_LBRAC, $1, $3); } | postfix_expression DT_TOK_LPAR DT_TOK_RPAR { $$ = dt_node_func($1, NULL); } | postfix_expression DT_TOK_LPAR argument_expression_list DT_TOK_RPAR { $$ = dt_node_func($1, $3); } | postfix_expression DT_TOK_DOT DT_TOK_IDENT { $$ = OP2(DT_TOK_DOT, $1, dt_node_ident($3)); } | postfix_expression DT_TOK_DOT DT_TOK_TNAME { $$ = OP2(DT_TOK_DOT, $1, dt_node_ident($3)); } | postfix_expression DT_TOK_PTR DT_TOK_IDENT { $$ = OP2(DT_TOK_PTR, $1, dt_node_ident($3)); } | postfix_expression DT_TOK_PTR DT_TOK_TNAME { $$ = OP2(DT_TOK_PTR, $1, dt_node_ident($3)); } | postfix_expression DT_TOK_ADDADD { $$ = OP1(DT_TOK_POSTINC, $1); } | postfix_expression DT_TOK_SUBSUB { $$ = OP1(DT_TOK_POSTDEC, $1); } | DT_TOK_OFFSETOF DT_TOK_LPAR type_name DT_TOK_COMMA DT_TOK_IDENT DT_TOK_RPAR { $$ = dt_node_offsetof($3, $5); } | DT_TOK_OFFSETOF DT_TOK_LPAR type_name DT_TOK_COMMA DT_TOK_TNAME DT_TOK_RPAR { $$ = dt_node_offsetof($3, $5); } | DT_TOK_XLATE DT_TOK_LT type_name DT_TOK_GT DT_TOK_LPAR expression DT_TOK_RPAR { $$ = OP2(DT_TOK_XLATE, dt_node_type($3), $6); } ; unary_expression: postfix_expression | DT_TOK_ADDADD unary_expression { $$ = OP1(DT_TOK_PREINC, $2); } | DT_TOK_SUBSUB unary_expression { $$ = OP1(DT_TOK_PREDEC, $2); } | unary_operator cast_expression { $$ = OP1($1, $2); } | DT_TOK_SIZEOF unary_expression { $$ = OP1(DT_TOK_SIZEOF, $2); } | DT_TOK_SIZEOF DT_TOK_LPAR type_name DT_TOK_RPAR { $$ = OP1(DT_TOK_SIZEOF, dt_node_type($3)); } | DT_TOK_STRINGOF unary_expression { $$ = OP1(DT_TOK_STRINGOF, $2); } ; unary_operator: DT_TOK_BAND { $$ = DT_TOK_ADDROF; } | DT_TOK_MUL { $$ = DT_TOK_DEREF; } | DT_TOK_ADD { $$ = DT_TOK_IPOS; } | DT_TOK_SUB { $$ = DT_TOK_INEG; } | DT_TOK_BNEG { $$ = DT_TOK_BNEG; } | DT_TOK_LNEG { $$ = DT_TOK_LNEG; } ; cast_expression: unary_expression | DT_TOK_LPAR type_name DT_TOK_RPAR cast_expression { $$ = OP2(DT_TOK_LPAR, dt_node_type($2), $4); } ; multiplicative_expression: cast_expression | multiplicative_expression DT_TOK_MUL cast_expression { $$ = OP2(DT_TOK_MUL, $1, $3); } | multiplicative_expression DT_TOK_DIV cast_expression { $$ = OP2(DT_TOK_DIV, $1, $3); } | multiplicative_expression DT_TOK_MOD cast_expression { $$ = OP2(DT_TOK_MOD, $1, $3); } ; additive_expression: multiplicative_expression | additive_expression DT_TOK_ADD multiplicative_expression { $$ = OP2(DT_TOK_ADD, $1, $3); } | additive_expression DT_TOK_SUB multiplicative_expression { $$ = OP2(DT_TOK_SUB, $1, $3); } ; shift_expression: additive_expression | shift_expression DT_TOK_LSH additive_expression { $$ = OP2(DT_TOK_LSH, $1, $3); } | shift_expression DT_TOK_RSH additive_expression { $$ = OP2(DT_TOK_RSH, $1, $3); } ; relational_expression: shift_expression | relational_expression DT_TOK_LT shift_expression { $$ = OP2(DT_TOK_LT, $1, $3); } | relational_expression DT_TOK_GT shift_expression { $$ = OP2(DT_TOK_GT, $1, $3); } | relational_expression DT_TOK_LE shift_expression { $$ = OP2(DT_TOK_LE, $1, $3); } | relational_expression DT_TOK_GE shift_expression { $$ = OP2(DT_TOK_GE, $1, $3); } ; equality_expression: relational_expression | equality_expression DT_TOK_EQU relational_expression { $$ = OP2(DT_TOK_EQU, $1, $3); } | equality_expression DT_TOK_NEQ relational_expression { $$ = OP2(DT_TOK_NEQ, $1, $3); } ; and_expression: equality_expression | and_expression DT_TOK_BAND equality_expression { $$ = OP2(DT_TOK_BAND, $1, $3); } ; exclusive_or_expression: and_expression | exclusive_or_expression DT_TOK_XOR and_expression { $$ = OP2(DT_TOK_XOR, $1, $3); } ; inclusive_or_expression: exclusive_or_expression | inclusive_or_expression DT_TOK_BOR exclusive_or_expression { $$ = OP2(DT_TOK_BOR, $1, $3); } ; logical_and_expression: inclusive_or_expression | logical_and_expression DT_TOK_LAND inclusive_or_expression { $$ = OP2(DT_TOK_LAND, $1, $3); } ; logical_xor_expression: logical_and_expression | logical_xor_expression DT_TOK_LXOR logical_and_expression { $$ = OP2(DT_TOK_LXOR, $1, $3); } ; logical_or_expression: logical_xor_expression | logical_or_expression DT_TOK_LOR logical_xor_expression { $$ = OP2(DT_TOK_LOR, $1, $3); } ; constant_expression: conditional_expression ; conditional_expression: logical_or_expression | logical_or_expression DT_TOK_QUESTION expression DT_TOK_COLON conditional_expression { $$ = OP3($1, $3, $5); } ; assignment_expression: conditional_expression | unary_expression assignment_operator assignment_expression { $$ = OP2($2, $1, $3); } ; assignment_operator: DT_TOK_ASGN { $$ = DT_TOK_ASGN; } | DT_TOK_MUL_EQ { $$ = DT_TOK_MUL_EQ; } | DT_TOK_DIV_EQ { $$ = DT_TOK_DIV_EQ; } | DT_TOK_MOD_EQ { $$ = DT_TOK_MOD_EQ; } | DT_TOK_ADD_EQ { $$ = DT_TOK_ADD_EQ; } | DT_TOK_SUB_EQ { $$ = DT_TOK_SUB_EQ; } | DT_TOK_LSH_EQ { $$ = DT_TOK_LSH_EQ; } | DT_TOK_RSH_EQ { $$ = DT_TOK_RSH_EQ; } | DT_TOK_AND_EQ { $$ = DT_TOK_AND_EQ; } | DT_TOK_XOR_EQ { $$ = DT_TOK_XOR_EQ; } | DT_TOK_OR_EQ { $$ = DT_TOK_OR_EQ; } ; expression: assignment_expression | expression DT_TOK_COMMA assignment_expression { $$ = OP2(DT_TOK_COMMA, $1, $3); } ; declaration: declaration_specifiers ';' { $$ = dt_node_decl(); dt_decl_free(dt_decl_pop()); yybegin(YYS_CLAUSE); } | declaration_specifiers init_declarator_list ';' { $$ = $2; dt_decl_free(dt_decl_pop()); yybegin(YYS_CLAUSE); } ; declaration_specifiers: d_storage_class_specifier | d_storage_class_specifier declaration_specifiers | type_specifier | type_specifier declaration_specifiers | type_qualifier | type_qualifier declaration_specifiers ; parameter_declaration_specifiers: storage_class_specifier | storage_class_specifier declaration_specifiers | type_specifier | type_specifier declaration_specifiers | type_qualifier | type_qualifier declaration_specifiers ; storage_class_specifier: DT_KEY_AUTO { dt_decl_class(DT_DC_AUTO); } | DT_KEY_REGISTER { dt_decl_class(DT_DC_REGISTER); } | DT_KEY_STATIC { dt_decl_class(DT_DC_STATIC); } | DT_KEY_EXTERN { dt_decl_class(DT_DC_EXTERN); } | DT_KEY_TYPEDEF { dt_decl_class(DT_DC_TYPEDEF); } ; d_storage_class_specifier: storage_class_specifier | DT_KEY_SELF { dt_decl_class(DT_DC_SELF); } | DT_KEY_THIS { dt_decl_class(DT_DC_THIS); } ; type_specifier: DT_KEY_VOID { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("void")); } | DT_KEY_CHAR { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("char")); } | DT_KEY_SHORT { $$ = dt_decl_attr(DT_DA_SHORT); } | DT_KEY_INT { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("int")); } | DT_KEY_LONG { $$ = dt_decl_attr(DT_DA_LONG); } | DT_KEY_FLOAT { $$ = dt_decl_spec(CTF_K_FLOAT, DUP("float")); } | DT_KEY_DOUBLE { $$ = dt_decl_spec(CTF_K_FLOAT, DUP("double")); } | DT_KEY_SIGNED { $$ = dt_decl_attr(DT_DA_SIGNED); } | DT_KEY_UNSIGNED { $$ = dt_decl_attr(DT_DA_UNSIGNED); } + | DT_KEY_USERLAND { $$ = dt_decl_attr(DT_DA_USER); } | DT_KEY_STRING { $$ = dt_decl_spec(CTF_K_TYPEDEF, DUP("string")); } | DT_TOK_TNAME { $$ = dt_decl_spec(CTF_K_TYPEDEF, $1); } | struct_or_union_specifier | enum_specifier ; type_qualifier: DT_KEY_CONST { $$ = dt_decl_attr(DT_DA_CONST); } | DT_KEY_RESTRICT { $$ = dt_decl_attr(DT_DA_RESTRICT); } | DT_KEY_VOLATILE { $$ = dt_decl_attr(DT_DA_VOLATILE); } ; struct_or_union_specifier: struct_or_union_definition struct_declaration_list '}' { $$ = dt_scope_pop(); } | struct_or_union DT_TOK_IDENT { $$ = dt_decl_spec($1, $2); } | struct_or_union DT_TOK_TNAME { $$ = dt_decl_spec($1, $2); } ; struct_or_union_definition: struct_or_union '{' { dt_decl_sou($1, NULL); } | struct_or_union DT_TOK_IDENT '{' { dt_decl_sou($1, $2); } | struct_or_union DT_TOK_TNAME '{' { dt_decl_sou($1, $2); } ; struct_or_union: DT_KEY_STRUCT { $$ = CTF_K_STRUCT; } | DT_KEY_UNION { $$ = CTF_K_UNION; } ; struct_declaration_list: struct_declaration | struct_declaration_list struct_declaration ; init_declarator_list: init_declarator | init_declarator_list DT_TOK_COMMA init_declarator { $$ = LINK($1, $3); } ; init_declarator: declarator { $$ = dt_node_decl(); dt_decl_reset(); } ; struct_declaration: specifier_qualifier_list struct_declarator_list ';' { dt_decl_free(dt_decl_pop()); } ; specifier_qualifier_list: type_specifier | type_specifier specifier_qualifier_list { $$ = $2; } | type_qualifier | type_qualifier specifier_qualifier_list { $$ = $2; } ; struct_declarator_list: struct_declarator | struct_declarator_list DT_TOK_COMMA struct_declarator ; struct_declarator: declarator { dt_decl_member(NULL); } | DT_TOK_COLON constant_expression { dt_decl_member($2); } | declarator DT_TOK_COLON constant_expression { dt_decl_member($3); } ; enum_specifier: enum_definition enumerator_list '}' { $$ = dt_scope_pop(); } | DT_KEY_ENUM DT_TOK_IDENT { $$ = dt_decl_spec(CTF_K_ENUM, $2); } | DT_KEY_ENUM DT_TOK_TNAME { $$ = dt_decl_spec(CTF_K_ENUM, $2); } ; enum_definition: DT_KEY_ENUM '{' { dt_decl_enum(NULL); } | DT_KEY_ENUM DT_TOK_IDENT '{' { dt_decl_enum($2); } | DT_KEY_ENUM DT_TOK_TNAME '{' { dt_decl_enum($2); } ; enumerator_list: enumerator | enumerator_list DT_TOK_COMMA enumerator ; enumerator: DT_TOK_IDENT { dt_decl_enumerator($1, NULL); } | DT_TOK_IDENT DT_TOK_ASGN expression { dt_decl_enumerator($1, $3); } ; declarator: direct_declarator | pointer direct_declarator ; direct_declarator: DT_TOK_IDENT { $$ = dt_decl_ident($1); } | lparen declarator DT_TOK_RPAR { $$ = $2; } | direct_declarator array { dt_decl_array($2); } | direct_declarator function { dt_decl_func($1, $2); } ; lparen: DT_TOK_LPAR { dt_decl_top()->dd_attr |= DT_DA_PAREN; } ; pointer: DT_TOK_MUL { $$ = dt_decl_ptr(); } | DT_TOK_MUL type_qualifier_list { $$ = dt_decl_ptr(); } | DT_TOK_MUL pointer { $$ = dt_decl_ptr(); } | DT_TOK_MUL type_qualifier_list pointer { $$ = dt_decl_ptr(); } ; type_qualifier_list: type_qualifier | type_qualifier_list type_qualifier { $$ = $2; } ; parameter_type_list: parameter_list | DT_TOK_ELLIPSIS { $$ = dt_node_vatype(); } | parameter_list DT_TOK_COMMA DT_TOK_ELLIPSIS { $$ = LINK($1, dt_node_vatype()); } ; parameter_list: parameter_declaration | parameter_list DT_TOK_COMMA parameter_declaration { $$ = LINK($1, $3); } ; parameter_declaration: parameter_declaration_specifiers { $$ = dt_node_type(NULL); } | parameter_declaration_specifiers declarator { $$ = dt_node_type(NULL); } | parameter_declaration_specifiers abstract_declarator { $$ = dt_node_type(NULL); } ; type_name: specifier_qualifier_list { $$ = dt_decl_pop(); } | specifier_qualifier_list abstract_declarator { $$ = dt_decl_pop(); } ; abstract_declarator: pointer | direct_abstract_declarator | pointer direct_abstract_declarator ; direct_abstract_declarator: lparen abstract_declarator DT_TOK_RPAR { $$ = $2; } | direct_abstract_declarator array { dt_decl_array($2); } | array { dt_decl_array($1); $$ = NULL; } | direct_abstract_declarator function { dt_decl_func($1, $2); } | function { dt_decl_func(NULL, $1); } ; array: DT_TOK_LBRAC { dt_scope_push(NULL, CTF_ERR); } array_parameters DT_TOK_RBRAC { dt_scope_pop(); $$ = $3; } ; array_parameters: /* empty */ { $$ = NULL; } | constant_expression { $$ = $1; } | parameter_type_list { $$ = $1; } ; function: DT_TOK_LPAR { dt_scope_push(NULL, CTF_ERR); } function_parameters DT_TOK_RPAR { dt_scope_pop(); $$ = $3; } ; function_parameters: /* empty */ { $$ = NULL; } | parameter_type_list { $$ = $1; } ; %% Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_ident.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_ident.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_ident.c (revision 268578) @@ -1,1047 +1,1052 @@ /* * 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) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" #if defined(sun) #include #endif #include #include #if defined(sun) #include #endif #include #include #include #if defined(sun) #include #endif #include #include #include #include #include #include /* * Common code for cooking an identifier that uses a typed signature list (we * use this for associative arrays and functions). If the argument list is * of the same length and types, then return the return type. Otherwise * print an appropriate compiler error message and abort the compile. */ static void dt_idcook_sign(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args, const char *prefix, const char *suffix) { dt_idsig_t *isp = idp->di_data; int i, compat, mismatch, arglimit, iskey; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; iskey = idp->di_kind == DT_IDENT_ARRAY || idp->di_kind == DT_IDENT_AGG; if (isp->dis_varargs >= 0) { mismatch = argc < isp->dis_varargs; arglimit = isp->dis_varargs; } else if (isp->dis_optargs >= 0) { mismatch = (argc < isp->dis_optargs || argc > isp->dis_argc); arglimit = argc; } else { mismatch = argc != isp->dis_argc; arglimit = isp->dis_argc; } if (mismatch) { xyerror(D_PROTO_LEN, "%s%s%s prototype mismatch: %d %s%s" "passed, %s%d expected\n", prefix, idp->di_name, suffix, argc, iskey ? "key" : "arg", argc == 1 ? " " : "s ", isp->dis_optargs >= 0 ? "at least " : "", isp->dis_optargs >= 0 ? isp->dis_optargs : arglimit); } for (i = 0; i < arglimit; i++, args = args->dn_list) { if (isp->dis_args[i].dn_ctfp != NULL) compat = dt_node_is_argcompat(&isp->dis_args[i], args); else compat = 1; /* "@" matches any type */ if (!compat) { xyerror(D_PROTO_ARG, "%s%s%s %s #%d is incompatible with " "prototype:\n\tprototype: %s\n\t%9s: %s\n", prefix, idp->di_name, suffix, iskey ? "key" : "argument", i + 1, dt_node_type_name(&isp->dis_args[i], n1, sizeof (n1)), iskey ? "key" : "argument", dt_node_type_name(args, n2, sizeof (n2))); } } - dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type, B_FALSE); } /* * Cook an associative array identifier. If this is the first time we are * cooking this array, create its signature based on the argument list. * Otherwise validate the argument list against the existing signature. */ static void dt_idcook_assc(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) { if (idp->di_data == NULL) { dt_idsig_t *isp = idp->di_data = malloc(sizeof (dt_idsig_t)); char n[DT_TYPE_NAMELEN]; int i; if (isp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); isp->dis_varargs = -1; isp->dis_optargs = -1; isp->dis_argc = argc; isp->dis_args = NULL; isp->dis_auxinfo = 0; if (argc != 0 && (isp->dis_args = calloc(argc, sizeof (dt_node_t))) == NULL) { idp->di_data = NULL; free(isp); longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } /* * If this identifier has not been explicitly declared earlier, * set the identifier's base type to be our special type . * If this ident is an aggregation, it will remain as is. If * this ident is an associative array, it will be reassigned * based on the result type of the first assignment statement. */ if (!(idp->di_flags & DT_IDFLG_DECL)) { idp->di_ctfp = DT_DYN_CTFP(yypcb->pcb_hdl); idp->di_type = DT_DYN_TYPE(yypcb->pcb_hdl); } for (i = 0; i < argc; i++, args = args->dn_list) { if (dt_node_is_dynamic(args) || dt_node_is_void(args)) { xyerror(D_KEY_TYPE, "%s expression may not be " "used as %s index: key #%d\n", dt_node_type_name(args, n, sizeof (n)), dt_idkind_name(idp->di_kind), i + 1); } dt_node_type_propagate(args, &isp->dis_args[i]); isp->dis_args[i].dn_list = &isp->dis_args[i + 1]; } if (argc != 0) isp->dis_args[argc - 1].dn_list = NULL; - dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type, B_FALSE); } else { dt_idcook_sign(dnp, idp, argc, args, idp->di_kind == DT_IDENT_AGG ? "@" : "", "[ ]"); } } /* * Cook a function call. If this is the first time we are cooking this * identifier, create its type signature based on predefined prototype stored * in di_iarg. We then validate the argument list against this signature. */ static void dt_idcook_func(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) { if (idp->di_data == NULL) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dtrace_typeinfo_t dtt; dt_idsig_t *isp; char *s, *p1, *p2; int i = 0; assert(idp->di_iarg != NULL); s = alloca(strlen(idp->di_iarg) + 1); (void) strcpy(s, idp->di_iarg); if ((p2 = strrchr(s, ')')) != NULL) *p2 = '\0'; /* mark end of parameter list string */ if ((p1 = strchr(s, '(')) != NULL) *p1++ = '\0'; /* mark end of return type string */ if (p1 == NULL || p2 == NULL) { xyerror(D_UNKNOWN, "internal error: malformed entry " "for built-in function %s\n", idp->di_name); } for (p2 = p1; *p2 != '\0'; p2++) { if (!isspace(*p2)) { i++; break; } } for (p2 = strchr(p2, ','); p2++ != NULL; i++) p2 = strchr(p2, ','); /* * We first allocate a new ident signature structure with the * appropriate number of argument entries, and then look up * the return type and store its CTF data in di_ctfp/type. */ if ((isp = idp->di_data = malloc(sizeof (dt_idsig_t))) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); isp->dis_varargs = -1; isp->dis_optargs = -1; isp->dis_argc = i; isp->dis_args = NULL; isp->dis_auxinfo = 0; if (i != 0 && (isp->dis_args = calloc(i, sizeof (dt_node_t))) == NULL) { idp->di_data = NULL; free(isp); longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } if (dt_type_lookup(s, &dtt) == -1) { xyerror(D_UNKNOWN, "failed to resolve type of %s (%s):" " %s\n", idp->di_name, s, dtrace_errmsg(dtp, dtrace_errno(dtp))); } if (idp->di_kind == DT_IDENT_AGGFUNC) { idp->di_ctfp = DT_DYN_CTFP(dtp); idp->di_type = DT_DYN_TYPE(dtp); } else { idp->di_ctfp = dtt.dtt_ctfp; idp->di_type = dtt.dtt_type; } /* * For each comma-delimited parameter in the prototype string, * we look up the corresponding type and store its CTF data in * the corresponding location in dis_args[]. We also recognize * the special type string "@" to indicate that the specified * parameter may be a D expression of *any* type (represented * as a dis_args[] element with ctfp = NULL, type == CTF_ERR). * If a varargs "..." is present, we record the argument index * in dis_varargs for the benefit of dt_idcook_sign(), above. * If the type of an argument is enclosed in square brackets * (e.g. "[int]"), the argument is considered optional: the * argument may be absent, but if it is present, it must be of * the specified type. Note that varargs may not optional, * optional arguments may not follow varargs, and non-optional * arguments may not follow optional arguments. */ for (i = 0; i < isp->dis_argc; i++, p1 = p2) { while (isspace(*p1)) p1++; /* skip leading whitespace */ if ((p2 = strchr(p1, ',')) == NULL) p2 = p1 + strlen(p1); else *p2++ = '\0'; if (strcmp(p1, "@") == 0 || strcmp(p1, "...") == 0) { isp->dis_args[i].dn_ctfp = NULL; isp->dis_args[i].dn_type = CTF_ERR; if (*p1 == '.') isp->dis_varargs = i; continue; } if (*p1 == '[' && p1[strlen(p1) - 1] == ']') { if (isp->dis_varargs != -1) { xyerror(D_UNKNOWN, "optional arg#%d " "may not follow variable arg#%d\n", i + 1, isp->dis_varargs + 1); } if (isp->dis_optargs == -1) isp->dis_optargs = i; p1[strlen(p1) - 1] = '\0'; p1++; } else if (isp->dis_optargs != -1) { xyerror(D_UNKNOWN, "required arg#%d may not " "follow optional arg#%d\n", i + 1, isp->dis_optargs + 1); } if (dt_type_lookup(p1, &dtt) == -1) { xyerror(D_UNKNOWN, "failed to resolve type of " "%s arg#%d (%s): %s\n", idp->di_name, i + 1, p1, dtrace_errmsg(dtp, dtrace_errno(dtp))); } dt_node_type_assign(&isp->dis_args[i], - dtt.dtt_ctfp, dtt.dtt_type); + dtt.dtt_ctfp, dtt.dtt_type, B_FALSE); } } dt_idcook_sign(dnp, idp, argc, args, "", "( )"); } /* * Cook a reference to the dynamically typed args[] array. We verify that the * reference is using a single integer constant, and then construct a new ident * representing the appropriate type or translation specifically for this node. */ static void dt_idcook_args(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *ap) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_probe_t *prp = yypcb->pcb_probe; dt_node_t tag, *nnp, *xnp; dt_xlator_t *dxp; dt_ident_t *xidp; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; if (argc != 1) { xyerror(D_PROTO_LEN, "%s[ ] prototype mismatch: %d arg%s" "passed, 1 expected\n", idp->di_name, argc, argc == 1 ? " " : "s "); } if (ap->dn_kind != DT_NODE_INT) { xyerror(D_PROTO_ARG, "%s[ ] argument #1 is incompatible with " "prototype:\n\tprototype: %s\n\t argument: %s\n", idp->di_name, "integer constant", dt_type_name(ap->dn_ctfp, ap->dn_type, n1, sizeof (n1))); } if (yypcb->pcb_pdesc == NULL) { xyerror(D_ARGS_NONE, "%s[ ] may not be referenced outside " "of a probe clause\n", idp->di_name); } if (prp == NULL) { xyerror(D_ARGS_MULTI, "%s[ ] may not be referenced because probe description %s " "matches an unstable set of probes\n", idp->di_name, dtrace_desc2str(yypcb->pcb_pdesc, n1, sizeof (n1))); } if (ap->dn_value >= prp->pr_argc) { xyerror(D_ARGS_IDX, "index %lld is out of range for %s %s[ ]\n", (longlong_t)ap->dn_value, dtrace_desc2str(yypcb->pcb_pdesc, n1, sizeof (n1)), idp->di_name); } /* * Look up the native and translated argument types for the probe. * If no translation is needed, these will be the same underlying node. * If translation is needed, look up the appropriate translator. Once * we have the appropriate node, create a new dt_ident_t for this node, * assign it the appropriate attributes, and set the type of 'dnp'. */ xnp = prp->pr_xargv[ap->dn_value]; nnp = prp->pr_nargv[prp->pr_mapping[ap->dn_value]]; if (xnp->dn_type == CTF_ERR) { xyerror(D_ARGS_TYPE, "failed to resolve translated type for " "%s[%lld]\n", idp->di_name, (longlong_t)ap->dn_value); } if (nnp->dn_type == CTF_ERR) { xyerror(D_ARGS_TYPE, "failed to resolve native type for " "%s[%lld]\n", idp->di_name, (longlong_t)ap->dn_value); } if (dtp->dt_xlatemode == DT_XL_STATIC && ( nnp == xnp || dt_node_is_argcompat(nnp, xnp))) { dnp->dn_ident = dt_ident_create(idp->di_name, idp->di_kind, idp->di_flags | DT_IDFLG_ORPHAN, idp->di_id, idp->di_attr, idp->di_vers, idp->di_ops, idp->di_iarg, idp->di_gen); if (dnp->dn_ident == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dt_node_type_assign(dnp, prp->pr_argv[ap->dn_value].dtt_ctfp, - prp->pr_argv[ap->dn_value].dtt_type); + prp->pr_argv[ap->dn_value].dtt_type, + prp->pr_argv[ap->dn_value].dtt_flags & DTT_FL_USER ? + B_TRUE : B_FALSE); } else if ((dxp = dt_xlator_lookup(dtp, nnp, xnp, DT_XLATE_FUZZY)) != NULL || ( dxp = dt_xlator_lookup(dtp, dt_probe_tag(prp, ap->dn_value, &tag), xnp, DT_XLATE_EXACT | DT_XLATE_EXTERN)) != NULL) { xidp = dt_xlator_ident(dxp, xnp->dn_ctfp, xnp->dn_type); dnp->dn_ident = dt_ident_create(idp->di_name, xidp->di_kind, xidp->di_flags | DT_IDFLG_ORPHAN, idp->di_id, idp->di_attr, idp->di_vers, idp->di_ops, idp->di_iarg, idp->di_gen); if (dnp->dn_ident == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); if (dt_xlator_dynamic(dxp)) dxp->dx_arg = (int)ap->dn_value; /* * Propagate relevant members from the translator's internal * dt_ident_t. This code must be kept in sync with the state * that is initialized for idents in dt_xlator_create(). */ dnp->dn_ident->di_data = xidp->di_data; dnp->dn_ident->di_ctfp = xidp->di_ctfp; dnp->dn_ident->di_type = xidp->di_type; - dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp)); + dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp), + B_FALSE); } else { xyerror(D_ARGS_XLATOR, "translator for %s[%lld] from %s to %s " "is not defined\n", idp->di_name, (longlong_t)ap->dn_value, dt_node_type_name(nnp, n1, sizeof (n1)), dt_node_type_name(xnp, n2, sizeof (n2))); } assert(dnp->dn_ident->di_flags & DT_IDFLG_ORPHAN); assert(dnp->dn_ident->di_id == idp->di_id); } static void dt_idcook_regs(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *ap) { dtrace_typeinfo_t dtt; dtrace_hdl_t *dtp = yypcb->pcb_hdl; char n[DT_TYPE_NAMELEN]; if (argc != 1) { xyerror(D_PROTO_LEN, "%s[ ] prototype mismatch: %d arg%s" "passed, 1 expected\n", idp->di_name, argc, argc == 1 ? " " : "s "); } if (ap->dn_kind != DT_NODE_INT) { xyerror(D_PROTO_ARG, "%s[ ] argument #1 is incompatible with " "prototype:\n\tprototype: %s\n\t argument: %s\n", idp->di_name, "integer constant", dt_type_name(ap->dn_ctfp, ap->dn_type, n, sizeof (n))); } if ((ap->dn_flags & DT_NF_SIGNED) && (int64_t)ap->dn_value < 0) { xyerror(D_REGS_IDX, "index %lld is out of range for array %s\n", (longlong_t)ap->dn_value, idp->di_name); } if (dt_type_lookup("uint64_t", &dtt) == -1) { xyerror(D_UNKNOWN, "failed to resolve type of %s: %s\n", idp->di_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); } idp->di_ctfp = dtt.dtt_ctfp; idp->di_type = dtt.dtt_type; - dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type, B_FALSE); } /*ARGSUSED*/ static void dt_idcook_type(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) { if (idp->di_type == CTF_ERR) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dtrace_typeinfo_t dtt; if (dt_type_lookup(idp->di_iarg, &dtt) == -1) { xyerror(D_UNKNOWN, "failed to resolve type %s for identifier %s: %s\n", (const char *)idp->di_iarg, idp->di_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); } idp->di_ctfp = dtt.dtt_ctfp; idp->di_type = dtt.dtt_type; } - dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type, B_FALSE); } /*ARGSUSED*/ static void dt_idcook_thaw(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) { if (idp->di_ctfp != NULL && idp->di_type != CTF_ERR) - dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type, B_FALSE); } static void dt_idcook_inline(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) { if (idp->di_kind == DT_IDENT_ARRAY) dt_idcook_assc(dnp, idp, argc, args); else dt_idcook_thaw(dnp, idp, argc, args); } static void dt_iddtor_sign(dt_ident_t *idp) { if (idp->di_data != NULL) free(((dt_idsig_t *)idp->di_data)->dis_args); free(idp->di_data); } static void dt_iddtor_free(dt_ident_t *idp) { free(idp->di_data); } static void dt_iddtor_inline(dt_ident_t *idp) { dt_idnode_t *inp = idp->di_iarg; if (inp != NULL) { dt_node_link_free(&inp->din_list); if (inp->din_hash != NULL) dt_idhash_destroy(inp->din_hash); free(inp->din_argv); free(inp); } if (idp->di_kind == DT_IDENT_ARRAY) dt_iddtor_sign(idp); else dt_iddtor_free(idp); } /*ARGSUSED*/ static void dt_iddtor_none(dt_ident_t *idp) { /* do nothing */ } static void dt_iddtor_probe(dt_ident_t *idp) { if (idp->di_data != NULL) dt_probe_destroy(idp->di_data); } static size_t dt_idsize_type(dt_ident_t *idp) { return (ctf_type_size(idp->di_ctfp, idp->di_type)); } /*ARGSUSED*/ static size_t dt_idsize_none(dt_ident_t *idp) { return (0); } const dt_idops_t dt_idops_assc = { dt_idcook_assc, dt_iddtor_sign, dt_idsize_none, }; const dt_idops_t dt_idops_func = { dt_idcook_func, dt_iddtor_sign, dt_idsize_none, }; const dt_idops_t dt_idops_args = { dt_idcook_args, dt_iddtor_none, dt_idsize_none, }; const dt_idops_t dt_idops_regs = { dt_idcook_regs, dt_iddtor_free, dt_idsize_none, }; const dt_idops_t dt_idops_type = { dt_idcook_type, dt_iddtor_free, dt_idsize_type, }; const dt_idops_t dt_idops_thaw = { dt_idcook_thaw, dt_iddtor_free, dt_idsize_type, }; const dt_idops_t dt_idops_inline = { dt_idcook_inline, dt_iddtor_inline, dt_idsize_type, }; const dt_idops_t dt_idops_probe = { dt_idcook_thaw, dt_iddtor_probe, dt_idsize_none, }; static void dt_idhash_populate(dt_idhash_t *dhp) { const dt_ident_t *idp = dhp->dh_tmpl; dhp->dh_tmpl = NULL; /* clear dh_tmpl first to avoid recursion */ dt_dprintf("populating %s idhash from %p\n", dhp->dh_name, (void *)idp); for (; idp->di_name != NULL; idp++) { if (dt_idhash_insert(dhp, idp->di_name, idp->di_kind, idp->di_flags, idp->di_id, idp->di_attr, idp->di_vers, idp->di_ops ? idp->di_ops : &dt_idops_thaw, idp->di_iarg, 0) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } } dt_idhash_t * dt_idhash_create(const char *name, const dt_ident_t *tmpl, uint_t min, uint_t max) { dt_idhash_t *dhp; size_t size; assert(min <= max); size = sizeof (dt_idhash_t) + sizeof (dt_ident_t *) * (_dtrace_strbuckets - 1); if ((dhp = malloc(size)) == NULL) return (NULL); bzero(dhp, size); dhp->dh_name = name; dhp->dh_tmpl = tmpl; dhp->dh_nextid = min; dhp->dh_minid = min; dhp->dh_maxid = max; dhp->dh_hashsz = _dtrace_strbuckets; return (dhp); } /* * Destroy an entire identifier hash. This must be done using two passes with * an inlined version of dt_ident_destroy() to avoid referencing freed memory. * In the first pass di_dtor() is called for all identifiers; then the second * pass frees the actual dt_ident_t's. These must be done separately because * a di_dtor() may operate on data structures which contain references to other * identifiers inside of this hash itself (e.g. a global inline definition * which contains a parse tree that refers to another global variable). */ void dt_idhash_destroy(dt_idhash_t *dhp) { dt_ident_t *idp, *next; ulong_t i; for (i = 0; i < dhp->dh_hashsz; i++) { for (idp = dhp->dh_hash[i]; idp != NULL; idp = next) { next = idp->di_next; idp->di_ops->di_dtor(idp); } } for (i = 0; i < dhp->dh_hashsz; i++) { for (idp = dhp->dh_hash[i]; idp != NULL; idp = next) { next = idp->di_next; free(idp->di_name); free(idp); } } free(dhp); } void dt_idhash_update(dt_idhash_t *dhp) { uint_t nextid = dhp->dh_minid; dt_ident_t *idp; ulong_t i; for (i = 0; i < dhp->dh_hashsz; i++) { for (idp = dhp->dh_hash[i]; idp != NULL; idp = idp->di_next) { /* * Right now we're hard coding which types need to be * reset, but ideally this would be done dynamically. */ if (idp->di_kind == DT_IDENT_ARRAY || idp->di_kind == DT_IDENT_SCALAR || idp->di_kind == DT_IDENT_AGG) nextid = MAX(nextid, idp->di_id + 1); } } dhp->dh_nextid = nextid; } dt_ident_t * dt_idhash_lookup(dt_idhash_t *dhp, const char *name) { size_t len; ulong_t h = dt_strtab_hash(name, &len) % dhp->dh_hashsz; dt_ident_t *idp; if (dhp->dh_tmpl != NULL) dt_idhash_populate(dhp); /* fill hash w/ initial population */ for (idp = dhp->dh_hash[h]; idp != NULL; idp = idp->di_next) { if (strcmp(idp->di_name, name) == 0) return (idp); } return (NULL); } int dt_idhash_nextid(dt_idhash_t *dhp, uint_t *p) { if (dhp->dh_nextid >= dhp->dh_maxid) return (-1); /* no more id's are free to allocate */ *p = dhp->dh_nextid++; return (0); } ulong_t dt_idhash_size(const dt_idhash_t *dhp) { return (dhp->dh_nelems); } const char * dt_idhash_name(const dt_idhash_t *dhp) { return (dhp->dh_name); } dt_ident_t * dt_idhash_insert(dt_idhash_t *dhp, const char *name, ushort_t kind, ushort_t flags, uint_t id, dtrace_attribute_t attr, uint_t vers, const dt_idops_t *ops, void *iarg, ulong_t gen) { dt_ident_t *idp; ulong_t h; if (dhp->dh_tmpl != NULL) dt_idhash_populate(dhp); /* fill hash w/ initial population */ idp = dt_ident_create(name, kind, flags, id, attr, vers, ops, iarg, gen); if (idp == NULL) return (NULL); h = dt_strtab_hash(name, NULL) % dhp->dh_hashsz; idp->di_next = dhp->dh_hash[h]; dhp->dh_hash[h] = idp; dhp->dh_nelems++; if (dhp->dh_defer != NULL) dhp->dh_defer(dhp, idp); return (idp); } void dt_idhash_xinsert(dt_idhash_t *dhp, dt_ident_t *idp) { ulong_t h; if (dhp->dh_tmpl != NULL) dt_idhash_populate(dhp); /* fill hash w/ initial population */ h = dt_strtab_hash(idp->di_name, NULL) % dhp->dh_hashsz; idp->di_next = dhp->dh_hash[h]; idp->di_flags &= ~DT_IDFLG_ORPHAN; dhp->dh_hash[h] = idp; dhp->dh_nelems++; if (dhp->dh_defer != NULL) dhp->dh_defer(dhp, idp); } void dt_idhash_delete(dt_idhash_t *dhp, dt_ident_t *key) { size_t len; ulong_t h = dt_strtab_hash(key->di_name, &len) % dhp->dh_hashsz; dt_ident_t **pp = &dhp->dh_hash[h]; dt_ident_t *idp; for (idp = dhp->dh_hash[h]; idp != NULL; idp = idp->di_next) { if (idp == key) break; else pp = &idp->di_next; } assert(idp == key); *pp = idp->di_next; assert(dhp->dh_nelems != 0); dhp->dh_nelems--; if (!(idp->di_flags & DT_IDFLG_ORPHAN)) dt_ident_destroy(idp); } static int dt_idhash_comp(const void *lp, const void *rp) { const dt_ident_t *lhs = *((const dt_ident_t **)lp); const dt_ident_t *rhs = *((const dt_ident_t **)rp); if (lhs->di_id != rhs->di_id) return ((int)(lhs->di_id - rhs->di_id)); else return (strcmp(lhs->di_name, rhs->di_name)); } int dt_idhash_iter(dt_idhash_t *dhp, dt_idhash_f *func, void *data) { dt_ident_t **ids; dt_ident_t *idp; ulong_t i, j, n; int rv; if (dhp->dh_tmpl != NULL) dt_idhash_populate(dhp); /* fill hash w/ initial population */ n = dhp->dh_nelems; ids = alloca(sizeof (dt_ident_t *) * n); for (i = 0, j = 0; i < dhp->dh_hashsz; i++) { for (idp = dhp->dh_hash[i]; idp != NULL; idp = idp->di_next) ids[j++] = idp; } qsort(ids, dhp->dh_nelems, sizeof (dt_ident_t *), dt_idhash_comp); for (i = 0; i < n; i++) { if ((rv = func(dhp, ids[i], data)) != 0) return (rv); } return (0); } dt_ident_t * dt_idstack_lookup(dt_idstack_t *sp, const char *name) { dt_idhash_t *dhp; dt_ident_t *idp; for (dhp = dt_list_prev(&sp->dids_list); dhp != NULL; dhp = dt_list_prev(dhp)) { if ((idp = dt_idhash_lookup(dhp, name)) != NULL) return (idp); } return (NULL); } void dt_idstack_push(dt_idstack_t *sp, dt_idhash_t *dhp) { dt_list_append(&sp->dids_list, dhp); } void dt_idstack_pop(dt_idstack_t *sp, dt_idhash_t *dhp) { assert(dt_list_prev(&sp->dids_list) == dhp); dt_list_delete(&sp->dids_list, dhp); } dt_ident_t * dt_ident_create(const char *name, ushort_t kind, ushort_t flags, uint_t id, dtrace_attribute_t attr, uint_t vers, const dt_idops_t *ops, void *iarg, ulong_t gen) { dt_ident_t *idp; char *s = NULL; if ((name != NULL && (s = strdup(name)) == NULL) || (idp = malloc(sizeof (dt_ident_t))) == NULL) { free(s); return (NULL); } idp->di_name = s; idp->di_kind = kind; idp->di_flags = flags; idp->di_id = id; idp->di_attr = attr; idp->di_vers = vers; idp->di_ops = ops; idp->di_iarg = iarg; idp->di_data = NULL; idp->di_ctfp = NULL; idp->di_type = CTF_ERR; idp->di_next = NULL; idp->di_gen = gen; idp->di_lineno = yylineno; return (idp); } /* * Destroy an individual identifier. This code must be kept in sync with the * dt_idhash_destroy() function below, which separates out the call to di_dtor. */ void dt_ident_destroy(dt_ident_t *idp) { idp->di_ops->di_dtor(idp); free(idp->di_name); free(idp); } void dt_ident_morph(dt_ident_t *idp, ushort_t kind, const dt_idops_t *ops, void *iarg) { idp->di_ops->di_dtor(idp); idp->di_kind = kind; idp->di_ops = ops; idp->di_iarg = iarg; idp->di_data = NULL; } dtrace_attribute_t dt_ident_cook(dt_node_t *dnp, dt_ident_t *idp, dt_node_t **pargp) { dtrace_attribute_t attr; dt_node_t *args, *argp; int argc = 0; attr = dt_node_list_cook(pargp, DT_IDFLG_REF); args = pargp ? *pargp : NULL; for (argp = args; argp != NULL; argp = argp->dn_list) argc++; idp->di_ops->di_cook(dnp, idp, argc, args); if (idp->di_flags & DT_IDFLG_USER) dnp->dn_flags |= DT_NF_USERLAND; return (dt_attr_min(attr, idp->di_attr)); } void dt_ident_type_assign(dt_ident_t *idp, ctf_file_t *fp, ctf_id_t type) { idp->di_ctfp = fp; idp->di_type = type; } dt_ident_t * dt_ident_resolve(dt_ident_t *idp) { while (idp->di_flags & DT_IDFLG_INLINE) { const dt_node_t *dnp = ((dt_idnode_t *)idp->di_iarg)->din_root; if (dnp == NULL) break; /* can't resolve any further yet */ switch (dnp->dn_kind) { case DT_NODE_VAR: case DT_NODE_SYM: case DT_NODE_FUNC: case DT_NODE_AGG: case DT_NODE_INLINE: case DT_NODE_PROBE: idp = dnp->dn_ident; continue; } if (dt_node_is_dynamic(dnp)) idp = dnp->dn_ident; else break; } return (idp); } size_t dt_ident_size(dt_ident_t *idp) { idp = dt_ident_resolve(idp); return (idp->di_ops->di_size(idp)); } int dt_ident_unref(const dt_ident_t *idp) { return (idp->di_gen == yypcb->pcb_hdl->dt_gen && (idp->di_flags & (DT_IDFLG_REF|DT_IDFLG_MOD|DT_IDFLG_DECL)) == 0); } const char * dt_idkind_name(uint_t kind) { switch (kind) { case DT_IDENT_ARRAY: return ("associative array"); case DT_IDENT_SCALAR: return ("scalar"); case DT_IDENT_PTR: return ("pointer"); case DT_IDENT_FUNC: return ("function"); case DT_IDENT_AGG: return ("aggregation"); case DT_IDENT_AGGFUNC: return ("aggregating function"); case DT_IDENT_ACTFUNC: return ("tracing function"); case DT_IDENT_XLSOU: return ("translated data"); case DT_IDENT_XLPTR: return ("pointer to translated data"); case DT_IDENT_SYMBOL: return ("external symbol reference"); case DT_IDENT_ENUM: return ("enumerator"); case DT_IDENT_PRAGAT: return ("#pragma attributes"); case DT_IDENT_PRAGBN: return ("#pragma binding"); case DT_IDENT_PROBE: return ("probe definition"); default: return (""); } } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h (revision 268578) @@ -1,713 +1,730 @@ /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #ifndef _DT_IMPL_H #define _DT_IMPL_H #include #include #if !defined(sun) #include #include #include #include #include #endif #include #include #include #include #if defined(sun) #include #endif #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include struct dt_module; /* see below */ struct dt_pfdict; /* see */ struct dt_arg; /* see below */ struct dt_provider; /* see */ struct dt_xlator; /* see */ typedef struct dt_intrinsic { const char *din_name; /* string name of the intrinsic type */ ctf_encoding_t din_data; /* integer or floating-point CTF encoding */ uint_t din_kind; /* CTF type kind to instantiate */ } dt_intrinsic_t; typedef struct dt_typedef { const char *dty_src; /* string name of typedef source type */ const char *dty_dst; /* string name of typedef destination type */ } dt_typedef_t; typedef struct dt_intdesc { const char *did_name; /* string name of the integer type */ ctf_file_t *did_ctfp; /* CTF container for this type reference */ ctf_id_t did_type; /* CTF type reference for this type */ uintmax_t did_limit; /* maximum positive value held by type */ } dt_intdesc_t; typedef struct dt_modops { uint_t (*do_syminit)(struct dt_module *); void (*do_symsort)(struct dt_module *); GElf_Sym *(*do_symname)(struct dt_module *, const char *, GElf_Sym *, uint_t *); GElf_Sym *(*do_symaddr)(struct dt_module *, GElf_Addr, GElf_Sym *, uint_t *); } dt_modops_t; typedef struct dt_arg { int da_ndx; /* index of this argument */ int da_mapping; /* mapping of argument indices to arguments */ ctf_id_t da_type; /* type of argument */ ctf_file_t *da_ctfp; /* CTF container for type */ dt_ident_t *da_xlator; /* translator, if any */ struct dt_arg *da_next; /* next argument */ } dt_arg_t; typedef struct dt_sym { uint_t ds_symid; /* id of corresponding symbol */ uint_t ds_next; /* index of next element in hash chain */ } dt_sym_t; typedef struct dt_module { dt_list_t dm_list; /* list forward/back pointers */ char dm_name[DTRACE_MODNAMELEN]; /* string name of module */ char dm_file[MAXPATHLEN]; /* file path of module (if any) */ struct dt_module *dm_next; /* pointer to next module in hash chain */ const dt_modops_t *dm_ops; /* pointer to data model's ops vector */ Elf *dm_elf; /* libelf handle for module object */ objfs_info_t dm_info; /* object filesystem private info */ ctf_sect_t dm_symtab; /* symbol table for module */ ctf_sect_t dm_strtab; /* string table for module */ ctf_sect_t dm_ctdata; /* CTF data for module */ ctf_file_t *dm_ctfp; /* CTF container handle */ uint_t *dm_symbuckets; /* symbol table hash buckets (chain indices) */ dt_sym_t *dm_symchains; /* symbol table hash chains buffer */ void *dm_asmap; /* symbol pointers sorted by value */ uint_t dm_symfree; /* index of next free hash element */ uint_t dm_nsymbuckets; /* number of elements in bucket array */ uint_t dm_nsymelems; /* number of elements in hash table */ uint_t dm_asrsv; /* actual reserved size of dm_asmap */ uint_t dm_aslen; /* number of entries in dm_asmap */ uint_t dm_flags; /* module flags (see below) */ int dm_modid; /* modinfo(1M) module identifier */ GElf_Addr dm_text_va; /* virtual address of text section */ GElf_Xword dm_text_size; /* size in bytes of text section */ GElf_Addr dm_data_va; /* virtual address of data section */ GElf_Xword dm_data_size; /* size in bytes of data section */ GElf_Addr dm_bss_va; /* virtual address of BSS */ GElf_Xword dm_bss_size; /* size in bytes of BSS */ dt_idhash_t *dm_extern; /* external symbol definitions */ #if !defined(sun) caddr_t dm_reloc_offset; /* Symbol relocation offset. */ uintptr_t *dm_sec_offsets; #endif + pid_t dm_pid; /* pid for this module */ + uint_t dm_nctflibs; /* number of ctf children libraries */ + ctf_file_t **dm_libctfp; /* process library ctf pointers */ + char **dm_libctfn; /* names of process ctf containers */ } dt_module_t; #define DT_DM_LOADED 0x1 /* module symbol and type data is loaded */ #define DT_DM_KERNEL 0x2 /* module is associated with a kernel object */ #define DT_DM_PRIMARY 0x4 /* module is a krtld primary kernel object */ typedef struct dt_provmod { char *dp_name; /* name of provider module */ struct dt_provmod *dp_next; /* next module */ } dt_provmod_t; typedef struct dt_ahashent { struct dt_ahashent *dtahe_prev; /* prev on hash chain */ struct dt_ahashent *dtahe_next; /* next on hash chain */ struct dt_ahashent *dtahe_prevall; /* prev on list of all */ struct dt_ahashent *dtahe_nextall; /* next on list of all */ uint64_t dtahe_hashval; /* hash value */ size_t dtahe_size; /* size of data */ dtrace_aggdata_t dtahe_data; /* data */ void (*dtahe_aggregate)(int64_t *, int64_t *, size_t); /* function */ } dt_ahashent_t; typedef struct dt_ahash { dt_ahashent_t **dtah_hash; /* hash table */ dt_ahashent_t *dtah_all; /* list of all elements */ size_t dtah_size; /* size of hash table */ } dt_ahash_t; typedef struct dt_aggregate { dtrace_bufdesc_t dtat_buf; /* buf aggregation snapshot */ int dtat_flags; /* aggregate flags */ processorid_t dtat_ncpus; /* number of CPUs in aggregate */ processorid_t *dtat_cpus; /* CPUs in aggregate */ processorid_t dtat_ncpu; /* size of dtat_cpus array */ processorid_t dtat_maxcpu; /* maximum number of CPUs */ dt_ahash_t dtat_hash; /* aggregate hash table */ } dt_aggregate_t; typedef struct dt_print_aggdata { dtrace_hdl_t *dtpa_dtp; /* pointer to libdtrace handle */ dtrace_aggvarid_t dtpa_id; /* aggregation variable of interest */ FILE *dtpa_fp; /* file pointer */ int dtpa_allunprint; /* print only unprinted aggregations */ + int dtpa_agghist; /* print aggregation as histogram */ + int dtpa_agghisthdr; /* aggregation histogram hdr printed */ + int dtpa_aggpack; /* pack quantized aggregations */ } dt_print_aggdata_t; typedef struct dt_dirpath { dt_list_t dir_list; /* linked-list forward/back pointers */ char *dir_path; /* directory pathname */ } dt_dirpath_t; typedef struct dt_lib_depend { dt_list_t dtld_deplist; /* linked-list forward/back pointers */ char *dtld_library; /* library name */ char *dtld_libpath; /* library pathname */ uint_t dtld_finish; /* completion time in tsort for lib */ uint_t dtld_start; /* starting time in tsort for lib */ uint_t dtld_loaded; /* boolean: is this library loaded */ dt_list_t dtld_dependencies; /* linked-list of lib dependencies */ dt_list_t dtld_dependents; /* linked-list of lib dependents */ } dt_lib_depend_t; typedef uint32_t dt_version_t; /* encoded version (see below) */ struct dtrace_hdl { const dtrace_vector_t *dt_vector; /* library vector, if vectored open */ void *dt_varg; /* vector argument, if vectored open */ dtrace_conf_t dt_conf; /* DTrace driver configuration profile */ char dt_errmsg[BUFSIZ]; /* buffer for formatted syntax error msgs */ const char *dt_errtag; /* tag used with last call to dt_set_errmsg() */ dt_pcb_t *dt_pcb; /* pointer to current parsing control block */ ulong_t dt_gen; /* compiler generation number */ dt_list_t dt_programs; /* linked list of dtrace_prog_t's */ dt_list_t dt_xlators; /* linked list of dt_xlator_t's */ struct dt_xlator **dt_xlatormap; /* dt_xlator_t's indexed by dx_id */ id_t dt_xlatorid; /* next dt_xlator_t id to assign */ dt_ident_t *dt_externs; /* linked list of external symbol identifiers */ dt_idhash_t *dt_macros; /* hash table of macro variable identifiers */ dt_idhash_t *dt_aggs; /* hash table of aggregation identifiers */ dt_idhash_t *dt_globals; /* hash table of global identifiers */ dt_idhash_t *dt_tls; /* hash table of thread-local identifiers */ dt_list_t dt_modlist; /* linked list of dt_module_t's */ dt_module_t **dt_mods; /* hash table of dt_module_t's */ uint_t dt_modbuckets; /* number of module hash buckets */ uint_t dt_nmods; /* number of modules in hash and list */ dt_provmod_t *dt_provmod; /* linked list of provider modules */ dt_module_t *dt_exec; /* pointer to executable module */ dt_module_t *dt_rtld; /* pointer to run-time linker module */ dt_module_t *dt_cdefs; /* pointer to C dynamic type module */ dt_module_t *dt_ddefs; /* pointer to D dynamic type module */ dt_list_t dt_provlist; /* linked list of dt_provider_t's */ struct dt_provider **dt_provs; /* hash table of dt_provider_t's */ uint_t dt_provbuckets; /* number of provider hash buckets */ uint_t dt_nprovs; /* number of providers in hash and list */ dt_proc_hash_t *dt_procs; /* hash table of grabbed process handles */ char **dt_proc_env; /* additional environment variables */ dt_intdesc_t dt_ints[6]; /* cached integer type descriptions */ ctf_id_t dt_type_func; /* cached CTF identifier for function type */ ctf_id_t dt_type_fptr; /* cached CTF identifier for function pointer */ ctf_id_t dt_type_str; /* cached CTF identifier for string type */ ctf_id_t dt_type_dyn; /* cached CTF identifier for type */ ctf_id_t dt_type_stack; /* cached CTF identifier for stack type */ ctf_id_t dt_type_symaddr; /* cached CTF identifier for _symaddr type */ ctf_id_t dt_type_usymaddr; /* cached CTF ident. for _usymaddr type */ size_t dt_maxprobe; /* max enabled probe ID */ dtrace_eprobedesc_t **dt_edesc; /* enabled probe descriptions */ dtrace_probedesc_t **dt_pdesc; /* probe descriptions for enabled prbs */ size_t dt_maxagg; /* max aggregation ID */ dtrace_aggdesc_t **dt_aggdesc; /* aggregation descriptions */ int dt_maxformat; /* max format ID */ void **dt_formats; /* pointer to format array */ int dt_maxstrdata; /* max strdata ID */ char **dt_strdata; /* pointer to strdata array */ dt_aggregate_t dt_aggregate; /* aggregate */ dt_pq_t *dt_bufq; /* CPU-specific data queue */ struct dt_pfdict *dt_pfdict; /* dictionary of printf conversions */ dt_version_t dt_vmax; /* optional ceiling on program API binding */ dtrace_attribute_t dt_amin; /* optional floor on program attributes */ char *dt_cpp_path; /* pathname of cpp(1) to invoke if needed */ char **dt_cpp_argv; /* argument vector for exec'ing cpp(1) */ int dt_cpp_argc; /* count of initialized cpp(1) arguments */ int dt_cpp_args; /* size of dt_cpp_argv[] array */ char *dt_ld_path; /* pathname of ld(1) to invoke if needed */ dt_list_t dt_lib_path; /* linked-list forming library search path */ uint_t dt_lazyload; /* boolean: set via -xlazyload */ uint_t dt_droptags; /* boolean: set via -xdroptags */ uint_t dt_active; /* boolean: set once tracing is active */ uint_t dt_stopped; /* boolean: set once tracing is stopped */ processorid_t dt_beganon; /* CPU that executed BEGIN probe (if any) */ processorid_t dt_endedon; /* CPU that executed END probe (if any) */ uint_t dt_oflags; /* dtrace open-time options (see dtrace.h) */ uint_t dt_cflags; /* dtrace compile-time options (see dtrace.h) */ uint_t dt_dflags; /* dtrace link-time options (see dtrace.h) */ uint_t dt_prcmode; /* dtrace process create mode (see dt_proc.h) */ uint_t dt_linkmode; /* dtrace symbol linking mode (see below) */ uint_t dt_linktype; /* dtrace link output file type (see below) */ uint_t dt_xlatemode; /* dtrace translator linking mode (see below) */ uint_t dt_stdcmode; /* dtrace stdc compatibility mode (see below) */ + uint_t dt_encoding; /* dtrace output encoding (see below) */ uint_t dt_treedump; /* dtrace tree debug bitmap (see below) */ uint64_t dt_options[DTRACEOPT_MAX]; /* dtrace run-time options */ int dt_version; /* library version requested by client */ int dt_ctferr; /* error resulting from last CTF failure */ int dt_errno; /* error resulting from last failed operation */ #if !defined(sun) const char *dt_errfile; int dt_errline; #endif int dt_fd; /* file descriptor for dtrace pseudo-device */ int dt_ftfd; /* file descriptor for fasttrap pseudo-device */ int dt_fterr; /* saved errno from failed open of dt_ftfd */ int dt_cdefs_fd; /* file descriptor for C CTF debugging cache */ int dt_ddefs_fd; /* file descriptor for D CTF debugging cache */ #if defined(sun) int dt_stdout_fd; /* file descriptor for saved stdout */ #else FILE *dt_freopen_fp; /* file pointer for freopened stdout */ #endif dtrace_handle_err_f *dt_errhdlr; /* error handler, if any */ void *dt_errarg; /* error handler argument */ dtrace_prog_t *dt_errprog; /* error handler program, if any */ dtrace_handle_drop_f *dt_drophdlr; /* drop handler, if any */ void *dt_droparg; /* drop handler argument */ dtrace_handle_proc_f *dt_prochdlr; /* proc handler, if any */ void *dt_procarg; /* proc handler argument */ dtrace_handle_setopt_f *dt_setopthdlr; /* setopt handler, if any */ void *dt_setoptarg; /* setopt handler argument */ dtrace_status_t dt_status[2]; /* status cache */ int dt_statusgen; /* current status generation */ hrtime_t dt_laststatus; /* last status */ hrtime_t dt_lastswitch; /* last switch of buffer data */ hrtime_t dt_lastagg; /* last snapshot of aggregation data */ char *dt_sprintf_buf; /* buffer for dtrace_sprintf() */ int dt_sprintf_buflen; /* length of dtrace_sprintf() buffer */ const char *dt_filetag; /* default filetag for dt_set_errmsg() */ char *dt_buffered_buf; /* buffer for buffered output */ size_t dt_buffered_offs; /* current offset into buffered buffer */ size_t dt_buffered_size; /* size of buffered buffer */ dtrace_handle_buffered_f *dt_bufhdlr; /* buffered handler, if any */ void *dt_bufarg; /* buffered handler argument */ dt_dof_t dt_dof; /* DOF generation buffers (see dt_dof.c) */ struct utsname dt_uts; /* uname(2) information for system */ dt_list_t dt_lib_dep; /* scratch linked-list of lib dependencies */ dt_list_t dt_lib_dep_sorted; /* dependency sorted library list */ dtrace_flowkind_t dt_flow; /* flow kind */ const char *dt_prefix; /* recommended flow prefix */ int dt_indent; /* recommended flow indent */ dtrace_epid_t dt_last_epid; /* most recently consumed EPID */ uint64_t dt_last_timestamp; /* most recently consumed timestamp */ }; /* * Values for the user arg of the ECB. */ #define DT_ECB_DEFAULT 0 #define DT_ECB_ERROR 1 /* * Values for the dt_linkmode property, which is used by the assembler when * processing external symbol references. User can set using -xlink=. */ #define DT_LINK_KERNEL 0 /* kernel syms static, user syms dynamic */ #define DT_LINK_PRIMARY 1 /* primary kernel syms static, others dynamic */ #define DT_LINK_DYNAMIC 2 /* all symbols dynamic */ #define DT_LINK_STATIC 3 /* all symbols static */ /* * Values for the dt_linktype property, which is used by dtrace_program_link() * to determine the type of output file that is desired by the client. */ #define DT_LTYP_ELF 0 /* produce ELF containing DOF */ #define DT_LTYP_DOF 1 /* produce stand-alone DOF */ /* * Values for the dt_xlatemode property, which is used to determine whether * references to dynamic translators are permitted. Set using -xlate=. */ #define DT_XL_STATIC 0 /* require xlators to be statically defined */ #define DT_XL_DYNAMIC 1 /* produce references to dynamic translators */ /* * Values for the dt_stdcmode property, which is used by the compiler when * running cpp to determine the presence and setting of the __STDC__ macro. */ #define DT_STDC_XA 0 /* ISO C + K&R C compat w/o ISO: __STDC__=0 */ #define DT_STDC_XC 1 /* Strict ISO C: __STDC__=1 */ #define DT_STDC_XS 2 /* K&R C: __STDC__ not defined */ #define DT_STDC_XT 3 /* ISO C + K&R C compat with ISO: __STDC__=0 */ /* + * Values for the dt_encoding property, which is used to force a particular + * character encoding (overriding default behavior and/or automatic detection). + */ +#define DT_ENCODING_UNSET 0 +#define DT_ENCODING_ASCII 1 +#define DT_ENCODING_UTF8 2 + +/* * Macro to test whether a given pass bit is set in the dt_treedump bit-vector. * If the bit for pass 'p' is set, the D compiler displays the parse tree for * the program by printing it to stderr at the end of compiler pass 'p'. */ #define DT_TREEDUMP_PASS(dtp, p) ((dtp)->dt_treedump & (1 << ((p) - 1))) /* * Macros for accessing the cached CTF container and type ID for the common * types "int", "string", and , which we need to use frequently in the D * compiler. The DT_INT_* macro relies upon "int" being at index 0 in the * _dtrace_ints_* tables in dt_open.c; the others are also set up there. */ #define DT_INT_CTFP(dtp) ((dtp)->dt_ints[0].did_ctfp) #define DT_INT_TYPE(dtp) ((dtp)->dt_ints[0].did_type) #define DT_FUNC_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_FUNC_TYPE(dtp) ((dtp)->dt_type_func) #define DT_FPTR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_FPTR_TYPE(dtp) ((dtp)->dt_type_fptr) #define DT_STR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_STR_TYPE(dtp) ((dtp)->dt_type_str) #define DT_DYN_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_DYN_TYPE(dtp) ((dtp)->dt_type_dyn) #define DT_STACK_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_STACK_TYPE(dtp) ((dtp)->dt_type_stack) #define DT_SYMADDR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_SYMADDR_TYPE(dtp) ((dtp)->dt_type_symaddr) #define DT_USYMADDR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_USYMADDR_TYPE(dtp) ((dtp)->dt_type_usymaddr) /* * Actions and subroutines are both DT_NODE_FUNC nodes; to avoid confusing * an action for a subroutine (or vice versa), we assure that the DT_ACT_* * constants and the DIF_SUBR_* constants occupy non-overlapping ranges by * starting the DT_ACT_* constants at DIF_SUBR_MAX + 1. */ #define DT_ACT_BASE DIF_SUBR_MAX + 1 #define DT_ACT(n) (DT_ACT_BASE + (n)) #define DT_ACT_PRINTF DT_ACT(0) /* printf() action */ #define DT_ACT_TRACE DT_ACT(1) /* trace() action */ #define DT_ACT_TRACEMEM DT_ACT(2) /* tracemem() action */ #define DT_ACT_STACK DT_ACT(3) /* stack() action */ #define DT_ACT_STOP DT_ACT(4) /* stop() action */ #define DT_ACT_BREAKPOINT DT_ACT(5) /* breakpoint() action */ #define DT_ACT_PANIC DT_ACT(6) /* panic() action */ #define DT_ACT_SPECULATE DT_ACT(7) /* speculate() action */ #define DT_ACT_COMMIT DT_ACT(8) /* commit() action */ #define DT_ACT_DISCARD DT_ACT(9) /* discard() action */ #define DT_ACT_CHILL DT_ACT(10) /* chill() action */ #define DT_ACT_EXIT DT_ACT(11) /* exit() action */ #define DT_ACT_USTACK DT_ACT(12) /* ustack() action */ #define DT_ACT_PRINTA DT_ACT(13) /* printa() action */ #define DT_ACT_RAISE DT_ACT(14) /* raise() action */ #define DT_ACT_CLEAR DT_ACT(15) /* clear() action */ #define DT_ACT_NORMALIZE DT_ACT(16) /* normalize() action */ #define DT_ACT_DENORMALIZE DT_ACT(17) /* denormalize() action */ #define DT_ACT_TRUNC DT_ACT(18) /* trunc() action */ #define DT_ACT_SYSTEM DT_ACT(19) /* system() action */ #define DT_ACT_JSTACK DT_ACT(20) /* jstack() action */ #define DT_ACT_FTRUNCATE DT_ACT(21) /* ftruncate() action */ #define DT_ACT_FREOPEN DT_ACT(22) /* freopen() action */ #define DT_ACT_SYM DT_ACT(23) /* sym()/func() actions */ #define DT_ACT_MOD DT_ACT(24) /* mod() action */ #define DT_ACT_USYM DT_ACT(25) /* usym()/ufunc() actions */ #define DT_ACT_UMOD DT_ACT(26) /* umod() action */ #define DT_ACT_UADDR DT_ACT(27) /* uaddr() action */ #define DT_ACT_SETOPT DT_ACT(28) /* setopt() action */ #define DT_ACT_PRINT DT_ACT(29) /* print() action */ #define DT_ACT_PRINTM DT_ACT(30) /* printm() action */ #define DT_ACT_PRINTT DT_ACT(31) /* printt() action */ /* * Sentinel to tell freopen() to restore the saved stdout. This must not * be ever valid for opening for write access via freopen(3C), which of * course, "." never is. */ #define DT_FREOPEN_RESTORE "." #define EDT_BASE 1000 /* base value for libdtrace errnos */ enum { EDT_VERSION = EDT_BASE, /* client is requesting unsupported version */ EDT_VERSINVAL, /* version string is invalid or overflows */ EDT_VERSUNDEF, /* requested API version is not defined */ EDT_VERSREDUCED, /* requested API version has been reduced */ EDT_CTF, /* libctf called failed (dt_ctferr has more) */ EDT_COMPILER, /* error in D program compilation */ EDT_NOTUPREG, /* tuple register allocation failure */ EDT_NOMEM, /* memory allocation failure */ EDT_INT2BIG, /* integer limit exceeded */ EDT_STR2BIG, /* string limit exceeded */ EDT_NOMOD, /* unknown module name */ EDT_NOPROV, /* unknown provider name */ EDT_NOPROBE, /* unknown probe name */ EDT_NOSYM, /* unknown symbol name */ EDT_NOSYMADDR, /* no symbol corresponds to address */ EDT_NOTYPE, /* unknown type name */ EDT_NOVAR, /* unknown variable name */ EDT_NOAGG, /* unknown aggregation name */ EDT_BADSCOPE, /* improper use of type name scoping operator */ EDT_BADSPEC, /* overspecified probe description */ EDT_BADSPCV, /* bad macro variable in probe description */ EDT_BADID, /* invalid probe identifier */ EDT_NOTLOADED, /* module is not currently loaded */ EDT_NOCTF, /* module does not contain any CTF data */ EDT_DATAMODEL, /* module and program data models don't match */ EDT_DIFVERS, /* library has newer DIF version than driver */ EDT_BADAGG, /* unrecognized aggregating action */ EDT_FIO, /* file i/o error */ EDT_DIFINVAL, /* invalid DIF program */ EDT_DIFSIZE, /* invalid DIF size */ EDT_DIFFAULT, /* failed to copyin DIF program */ EDT_BADPROBE, /* bad probe description */ EDT_BADPGLOB, /* bad probe description globbing pattern */ EDT_NOSCOPE, /* declaration scope stack underflow */ EDT_NODECL, /* declaration stack underflow */ EDT_DMISMATCH, /* record list does not match statement */ EDT_DOFFSET, /* record data offset error */ EDT_DALIGN, /* record data alignment error */ EDT_BADOPTNAME, /* invalid dtrace_setopt option name */ EDT_BADOPTVAL, /* invalid dtrace_setopt option value */ EDT_BADOPTCTX, /* invalid dtrace_setopt option context */ EDT_CPPFORK, /* failed to fork preprocessor */ EDT_CPPEXEC, /* failed to exec preprocessor */ EDT_CPPENT, /* preprocessor not found */ EDT_CPPERR, /* unknown preprocessor error */ EDT_SYMOFLOW, /* external symbol table overflow */ EDT_ACTIVE, /* operation illegal when tracing is active */ EDT_DESTRUCTIVE, /* destructive actions not allowed */ EDT_NOANON, /* no anonymous tracing state */ EDT_ISANON, /* can't claim anon state and enable probes */ EDT_ENDTOOBIG, /* END enablings exceed size of prncpl buffer */ EDT_NOCONV, /* failed to load type for printf conversion */ EDT_BADCONV, /* incomplete printf conversion */ EDT_BADERROR, /* invalid library ERROR action */ EDT_ERRABORT, /* abort due to error */ EDT_DROPABORT, /* abort due to drop */ EDT_DIRABORT, /* abort explicitly directed */ EDT_BADRVAL, /* invalid return value from callback */ EDT_BADNORMAL, /* invalid normalization */ EDT_BUFTOOSMALL, /* enabling exceeds size of buffer */ EDT_BADTRUNC, /* invalid truncation */ EDT_BUSY, /* device busy (active kernel debugger) */ EDT_ACCESS, /* insufficient privileges to use DTrace */ EDT_NOENT, /* dtrace device not available */ EDT_BRICKED, /* abort due to systemic unresponsiveness */ EDT_HARDWIRE, /* failed to load hard-wired definitions */ EDT_ELFVERSION, /* libelf is out-of-date w.r.t libdtrace */ EDT_NOBUFFERED, /* attempt to buffer output without handler */ EDT_UNSTABLE, /* description matched unstable set of probes */ EDT_BADSETOPT, /* invalid setopt library action */ EDT_BADSTACKPC, /* invalid stack program counter size */ EDT_BADAGGVAR, /* invalid aggregation variable identifier */ EDT_OVERSION, /* client is requesting deprecated version */ EDT_ENABLING_ERR, /* failed to enable probe */ - EDT_NOPROBES /* no probes sites for declared provider */ + EDT_NOPROBES, /* no probes sites for declared provider */ + EDT_CANTLOAD /* failed to load a module */ }; /* * Interfaces for parsing and comparing DTrace attribute tuples, which describe * stability and architectural binding information. The dtrace_attribute_t * structure and associated constant definitions are found in . */ extern dtrace_attribute_t dt_attr_min(dtrace_attribute_t, dtrace_attribute_t); extern dtrace_attribute_t dt_attr_max(dtrace_attribute_t, dtrace_attribute_t); extern char *dt_attr_str(dtrace_attribute_t, char *, size_t); extern int dt_attr_cmp(dtrace_attribute_t, dtrace_attribute_t); /* * Interfaces for parsing and handling DTrace version strings. Version binding * is a feature of the D compiler that is handled completely independently of * the DTrace kernel infrastructure, so the definitions are here in libdtrace. * Version strings are compiled into an encoded uint32_t which can be compared * using C comparison operators. Version definitions are found in dt_open.c. */ #define DT_VERSION_STRMAX 16 /* enough for "255.4095.4095\0" */ #define DT_VERSION_MAJMAX 0xFF /* maximum major version number */ #define DT_VERSION_MINMAX 0xFFF /* maximum minor version number */ #define DT_VERSION_MICMAX 0xFFF /* maximum micro version number */ #define DT_VERSION_NUMBER(M, m, u) \ ((((M) & 0xFF) << 24) | (((m) & 0xFFF) << 12) | ((u) & 0xFFF)) #define DT_VERSION_MAJOR(v) (((v) & 0xFF000000) >> 24) #define DT_VERSION_MINOR(v) (((v) & 0x00FFF000) >> 12) #define DT_VERSION_MICRO(v) ((v) & 0x00000FFF) extern char *dt_version_num2str(dt_version_t, char *, size_t); extern int dt_version_str2num(const char *, dt_version_t *); extern int dt_version_defined(dt_version_t); /* * Miscellaneous internal libdtrace interfaces. The definitions below are for * libdtrace routines that do not yet merit their own separate header file. */ extern char *dt_cpp_add_arg(dtrace_hdl_t *, const char *); extern char *dt_cpp_pop_arg(dtrace_hdl_t *); #if defined(sun) extern int dt_set_errno(dtrace_hdl_t *, int); #else int _dt_set_errno(dtrace_hdl_t *, int, const char *, int); void dt_get_errloc(dtrace_hdl_t *, const char **, int *); #define dt_set_errno(_a,_b) _dt_set_errno(_a,_b,__FILE__,__LINE__) #endif extern void dt_set_errmsg(dtrace_hdl_t *, const char *, const char *, const char *, int, const char *, va_list); #if defined(sun) extern int dt_ioctl(dtrace_hdl_t *, int, void *); #else extern int dt_ioctl(dtrace_hdl_t *, u_long, void *); #endif extern int dt_status(dtrace_hdl_t *, processorid_t); extern long dt_sysconf(dtrace_hdl_t *, int); extern ssize_t dt_write(dtrace_hdl_t *, int, const void *, size_t); extern int dt_printf(dtrace_hdl_t *, FILE *, const char *, ...); extern void *dt_zalloc(dtrace_hdl_t *, size_t); extern void *dt_alloc(dtrace_hdl_t *, size_t); extern void dt_free(dtrace_hdl_t *, void *); extern void dt_difo_free(dtrace_hdl_t *, dtrace_difo_t *); extern int dt_gmatch(const char *, const char *); extern char *dt_basename(char *); extern ulong_t dt_popc(ulong_t); extern ulong_t dt_popcb(const ulong_t *, ulong_t); extern int dt_buffered_enable(dtrace_hdl_t *); extern int dt_buffered_flush(dtrace_hdl_t *, dtrace_probedata_t *, const dtrace_recdesc_t *, const dtrace_aggdata_t *, uint32_t flags); extern void dt_buffered_disable(dtrace_hdl_t *); extern void dt_buffered_destroy(dtrace_hdl_t *); extern uint64_t dt_stddev(uint64_t *, uint64_t); extern int dt_rw_read_held(pthread_rwlock_t *); extern int dt_rw_write_held(pthread_rwlock_t *); extern int dt_mutex_held(pthread_mutex_t *); extern int dt_options_load(dtrace_hdl_t *); #define DT_RW_READ_HELD(x) dt_rw_read_held(x) #define DT_RW_WRITE_HELD(x) dt_rw_write_held(x) #define DT_RW_LOCK_HELD(x) (DT_RW_READ_HELD(x) || DT_RW_WRITE_HELD(x)) #define DT_MUTEX_HELD(x) dt_mutex_held(x) extern void dt_dprintf(const char *, ...); extern void dt_setcontext(dtrace_hdl_t *, dtrace_probedesc_t *); extern void dt_endcontext(dtrace_hdl_t *); extern void dt_pragma(dt_node_t *); extern int dt_reduce(dtrace_hdl_t *, dt_version_t); extern void dt_cg(dt_pcb_t *, dt_node_t *); extern dtrace_difo_t *dt_as(dt_pcb_t *); extern void dt_dis(const dtrace_difo_t *, FILE *); extern int dt_aggregate_go(dtrace_hdl_t *); extern int dt_aggregate_init(dtrace_hdl_t *); extern void dt_aggregate_destroy(dtrace_hdl_t *); extern int dt_epid_lookup(dtrace_hdl_t *, dtrace_epid_t, dtrace_eprobedesc_t **, dtrace_probedesc_t **); extern void dt_epid_destroy(dtrace_hdl_t *); extern int dt_aggid_lookup(dtrace_hdl_t *, dtrace_aggid_t, dtrace_aggdesc_t **); extern void dt_aggid_destroy(dtrace_hdl_t *); extern void *dt_format_lookup(dtrace_hdl_t *, int); extern void dt_format_destroy(dtrace_hdl_t *); extern const char *dt_strdata_lookup(dtrace_hdl_t *, int); extern void dt_strdata_destroy(dtrace_hdl_t *); extern int dt_print_quantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_lquantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_llquantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_agg(const dtrace_aggdata_t *, void *); extern int dt_handle(dtrace_hdl_t *, dtrace_probedata_t *); extern int dt_handle_liberr(dtrace_hdl_t *, const dtrace_probedata_t *, const char *); extern int dt_handle_cpudrop(dtrace_hdl_t *, processorid_t, dtrace_dropkind_t, uint64_t); extern int dt_handle_status(dtrace_hdl_t *, dtrace_status_t *, dtrace_status_t *); extern int dt_handle_setopt(dtrace_hdl_t *, dtrace_setoptdata_t *); extern int dt_lib_depend_add(dtrace_hdl_t *, dt_list_t *, const char *); extern dt_lib_depend_t *dt_lib_depend_lookup(dt_list_t *, const char *); extern dt_pcb_t *yypcb; /* pointer to current parser control block */ extern char yyintprefix; /* int token prefix for macros (+/-) */ extern char yyintsuffix[4]; /* int token suffix ([uUlL]*) */ extern int yyintdecimal; /* int token is decimal (1) or octal/hex (0) */ extern char yytext[]; /* lex input buffer */ extern int yylineno; /* lex line number */ extern int yydebug; /* lex debugging */ extern dt_node_t *yypragma; /* lex token list for control lines */ extern const dtrace_attribute_t _dtrace_maxattr; /* maximum attributes */ extern const dtrace_attribute_t _dtrace_defattr; /* default attributes */ extern const dtrace_attribute_t _dtrace_symattr; /* symbol ref attributes */ extern const dtrace_attribute_t _dtrace_typattr; /* type ref attributes */ extern const dtrace_attribute_t _dtrace_prvattr; /* provider attributes */ extern const dtrace_pattr_t _dtrace_prvdesc; /* provider attribute bundle */ extern const dt_version_t _dtrace_versions[]; /* array of valid versions */ extern const char *const _dtrace_version; /* current version string */ extern int _dtrace_strbuckets; /* number of hash buckets for strings */ extern int _dtrace_intbuckets; /* number of hash buckets for ints */ extern uint_t _dtrace_stkindent; /* default indent for stack/ustack */ extern uint_t _dtrace_pidbuckets; /* number of hash buckets for pids */ extern uint_t _dtrace_pidlrulim; /* number of proc handles to cache */ extern int _dtrace_debug; /* debugging messages enabled */ extern size_t _dtrace_bufsize; /* default dt_buf_create() size */ extern int _dtrace_argmax; /* default maximum probe arguments */ extern const char *_dtrace_libdir; /* default library directory */ extern const char *_dtrace_moddir; /* default kernel module directory */ #ifdef __cplusplus } #endif #endif /* _DT_IMPL_H */ Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_lex.l =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_lex.l (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_lex.l (revision 268578) @@ -1,872 +1,883 @@ %{ /* * 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) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #include #include #include #include #include #include #include #include #include #include /* * We need to undefine lex's input and unput macros so that references to these * call the functions provided at the end of this source file. */ #if defined(sun) #undef input #undef unput #else /* * Define YY_INPUT for flex since input() can't be re-defined. */ #define YY_INPUT(buf,result,max_size) \ if (yypcb->pcb_fileptr != NULL) { \ if (((result = fread(buf, 1, max_size, yypcb->pcb_fileptr)) == 0) \ && ferror(yypcb->pcb_fileptr)) \ longjmp(yypcb->pcb_jmpbuf, EDT_FIO); \ } else { \ int n; \ for (n = 0; n < max_size && \ yypcb->pcb_strptr < yypcb->pcb_string + yypcb->pcb_strlen; n++) \ buf[n] = *yypcb->pcb_strptr++; \ result = n; \ } /* * Do not EOF let tokens to be put back. This does not work with flex. * On the other hand, leaving current buffer in same state it was when * last EOF was received guarantees that input() will keep returning EOF * for all subsequent invocations, which is the effect desired. */ #undef unput #define unput(c) \ do { \ int _c = c; \ if (_c != EOF) \ yyunput(_c, yytext_ptr); \ } while(0) #endif static int id_or_type(const char *); #if defined(sun) static int input(void); static void unput(int); #endif /* * We first define a set of labeled states for use in the D lexer and then a * set of regular expressions to simplify things below. The lexer states are: * * S0 - D program clause and expression lexing * S1 - D comments (i.e. skip everything until end of comment) * S2 - D program outer scope (probe specifiers and declarations) * S3 - D control line parsing (i.e. after ^# is seen but before \n) * S4 - D control line scan (locate control directives only and invoke S3) */ %} %e 1500 /* maximum nodes */ -%p 3700 /* maximum positions */ +%p 4900 /* maximum positions */ %n 600 /* maximum states */ +%a 3000 /* maximum transitions */ %s S0 S1 S2 S3 S4 RGX_AGG "@"[a-zA-Z_][0-9a-zA-Z_]* RGX_PSPEC [-$:a-zA-Z_.?*\\\[\]!][-$:0-9a-zA-Z_.`?*\\\[\]!]* +RGX_ALTIDENT [a-zA-Z_][0-9a-zA-Z_]* +RGX_LMID LM[0-9a-fA-F]+` +RGX_MOD_IDENT [a-zA-Z_`][0-9a-z.A-Z_`]*` RGX_IDENT [a-zA-Z_`][0-9a-zA-Z_`]* RGX_INT ([0-9]+|0[xX][0-9A-Fa-f]+)[uU]?[lL]?[lL]? RGX_FP ([0-9]+("."?)[0-9]*|"."[0-9]+)((e|E)("+"|-)?[0-9]+)?[fFlL]? RGX_WS [\f\n\r\t\v ] RGX_STR ([^"\\\n]|\\[^"\n]|\\\")* RGX_CHR ([^'\\\n]|\\[^'\n]|\\')* RGX_INTERP ^[\f\t\v ]*#!.* RGX_CTL ^[\f\t\v ]*# %% %{ /* * We insert a special prologue into yylex() itself: if the pcb contains a * context token, we return that prior to running the normal lexer. This * allows libdtrace to force yacc into one of our three parsing contexts: D * expression (DT_CTX_DEXPR), D program (DT_CTX_DPROG) or D type (DT_CTX_DTYPE). * Once the token is returned, we clear it so this only happens once. */ if (yypcb->pcb_token != 0) { int tok = yypcb->pcb_token; yypcb->pcb_token = 0; return (tok); } %} auto return (DT_KEY_AUTO); break return (DT_KEY_BREAK); case return (DT_KEY_CASE); char return (DT_KEY_CHAR); const return (DT_KEY_CONST); continue return (DT_KEY_CONTINUE); counter return (DT_KEY_COUNTER); default return (DT_KEY_DEFAULT); do return (DT_KEY_DO); double return (DT_KEY_DOUBLE); else return (DT_KEY_ELSE); enum return (DT_KEY_ENUM); extern return (DT_KEY_EXTERN); float return (DT_KEY_FLOAT); for return (DT_KEY_FOR); goto return (DT_KEY_GOTO); if return (DT_KEY_IF); import return (DT_KEY_IMPORT); inline return (DT_KEY_INLINE); int return (DT_KEY_INT); long return (DT_KEY_LONG); offsetof return (DT_TOK_OFFSETOF); probe return (DT_KEY_PROBE); provider return (DT_KEY_PROVIDER); register return (DT_KEY_REGISTER); restrict return (DT_KEY_RESTRICT); return return (DT_KEY_RETURN); self return (DT_KEY_SELF); short return (DT_KEY_SHORT); signed return (DT_KEY_SIGNED); sizeof return (DT_TOK_SIZEOF); static return (DT_KEY_STATIC); string return (DT_KEY_STRING); stringof return (DT_TOK_STRINGOF); struct return (DT_KEY_STRUCT); switch return (DT_KEY_SWITCH); this return (DT_KEY_THIS); translator return (DT_KEY_XLATOR); typedef return (DT_KEY_TYPEDEF); union return (DT_KEY_UNION); unsigned return (DT_KEY_UNSIGNED); +userland return (DT_KEY_USERLAND); void return (DT_KEY_VOID); volatile return (DT_KEY_VOLATILE); while return (DT_KEY_WHILE); xlate return (DT_TOK_XLATE); auto { yybegin(YYS_EXPR); return (DT_KEY_AUTO); } char { yybegin(YYS_EXPR); return (DT_KEY_CHAR); } const { yybegin(YYS_EXPR); return (DT_KEY_CONST); } counter { yybegin(YYS_DEFINE); return (DT_KEY_COUNTER); } double { yybegin(YYS_EXPR); return (DT_KEY_DOUBLE); } enum { yybegin(YYS_EXPR); return (DT_KEY_ENUM); } extern { yybegin(YYS_EXPR); return (DT_KEY_EXTERN); } float { yybegin(YYS_EXPR); return (DT_KEY_FLOAT); } import { yybegin(YYS_EXPR); return (DT_KEY_IMPORT); } inline { yybegin(YYS_DEFINE); return (DT_KEY_INLINE); } int { yybegin(YYS_EXPR); return (DT_KEY_INT); } long { yybegin(YYS_EXPR); return (DT_KEY_LONG); } provider { yybegin(YYS_DEFINE); return (DT_KEY_PROVIDER); } register { yybegin(YYS_EXPR); return (DT_KEY_REGISTER); } restrict { yybegin(YYS_EXPR); return (DT_KEY_RESTRICT); } self { yybegin(YYS_EXPR); return (DT_KEY_SELF); } short { yybegin(YYS_EXPR); return (DT_KEY_SHORT); } signed { yybegin(YYS_EXPR); return (DT_KEY_SIGNED); } static { yybegin(YYS_EXPR); return (DT_KEY_STATIC); } string { yybegin(YYS_EXPR); return (DT_KEY_STRING); } struct { yybegin(YYS_EXPR); return (DT_KEY_STRUCT); } this { yybegin(YYS_EXPR); return (DT_KEY_THIS); } translator { yybegin(YYS_DEFINE); return (DT_KEY_XLATOR); } typedef { yybegin(YYS_EXPR); return (DT_KEY_TYPEDEF); } union { yybegin(YYS_EXPR); return (DT_KEY_UNION); } unsigned { yybegin(YYS_EXPR); return (DT_KEY_UNSIGNED); } void { yybegin(YYS_EXPR); return (DT_KEY_VOID); } volatile { yybegin(YYS_EXPR); return (DT_KEY_VOLATILE); } "$$"[0-9]+ { int i = atoi(yytext + 2); char *v = ""; /* * A macro argument reference substitutes the text of * an argument in place of the current token. When we * see $$ we fetch the saved string from pcb_sargv * (or use the default argument if the option has been * set and the argument hasn't been specified) and * return a token corresponding to this string. */ if (i < 0 || (i >= yypcb->pcb_sargc && !(yypcb->pcb_cflags & DTRACE_C_DEFARG))) { xyerror(D_MACRO_UNDEF, "macro argument %s is " "not defined\n", yytext); } if (i < yypcb->pcb_sargc) { v = yypcb->pcb_sargv[i]; /* get val from pcb */ yypcb->pcb_sflagv[i] |= DT_IDFLG_REF; } if ((yylval.l_str = strdup(v)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); (void) stresc2chr(yylval.l_str); return (DT_TOK_STRING); } "$"[0-9]+ { int i = atoi(yytext + 1); char *p, *v = "0"; /* * A macro argument reference substitutes the text of * one identifier or integer pattern for another. When * we see $ we fetch the saved string from pcb_sargv * (or use the default argument if the option has been * set and the argument hasn't been specified) and * return a token corresponding to this string. */ if (i < 0 || (i >= yypcb->pcb_sargc && !(yypcb->pcb_cflags & DTRACE_C_DEFARG))) { xyerror(D_MACRO_UNDEF, "macro argument %s is " "not defined\n", yytext); } if (i < yypcb->pcb_sargc) { v = yypcb->pcb_sargv[i]; /* get val from pcb */ yypcb->pcb_sflagv[i] |= DT_IDFLG_REF; } /* * If the macro text is not a valid integer or ident, * then we treat it as a string. The string may be * optionally enclosed in quotes, which we strip. */ if (strbadidnum(v)) { size_t len = strlen(v); if (len != 1 && *v == '"' && v[len - 1] == '"') yylval.l_str = strndup(v + 1, len - 2); else yylval.l_str = strndup(v, len); if (yylval.l_str == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); (void) stresc2chr(yylval.l_str); return (DT_TOK_STRING); } /* * If the macro text is not a string an begins with a * digit or a +/- sign, process it as an integer token. */ if (isdigit(v[0]) || v[0] == '-' || v[0] == '+') { if (isdigit(v[0])) yyintprefix = 0; else yyintprefix = *v++; errno = 0; yylval.l_int = strtoull(v, &p, 0); (void) strncpy(yyintsuffix, p, sizeof (yyintsuffix)); yyintdecimal = *v != '0'; if (errno == ERANGE) { xyerror(D_MACRO_OFLOW, "macro argument" " %s constant %s results in integer" " overflow\n", yytext, v); } return (DT_TOK_INT); } return (id_or_type(v)); } "$$"{RGX_IDENT} { dt_ident_t *idp = dt_idhash_lookup( yypcb->pcb_hdl->dt_macros, yytext + 2); char s[16]; /* enough for UINT_MAX + \0 */ if (idp == NULL) { xyerror(D_MACRO_UNDEF, "macro variable %s " "is not defined\n", yytext); } /* * For the moment, all current macro variables are of * type id_t (refer to dtrace_update() for details). */ (void) snprintf(s, sizeof (s), "%u", idp->di_id); if ((yylval.l_str = strdup(s)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); return (DT_TOK_STRING); } "$"{RGX_IDENT} { dt_ident_t *idp = dt_idhash_lookup( yypcb->pcb_hdl->dt_macros, yytext + 1); if (idp == NULL) { xyerror(D_MACRO_UNDEF, "macro variable %s " "is not defined\n", yytext); } /* * For the moment, all current macro variables are of * type id_t (refer to dtrace_update() for details). */ yylval.l_int = (intmax_t)(int)idp->di_id; yyintprefix = 0; yyintsuffix[0] = '\0'; yyintdecimal = 1; return (DT_TOK_INT); } -{RGX_IDENT} { +{RGX_IDENT} | +{RGX_MOD_IDENT}{RGX_IDENT} | +{RGX_MOD_IDENT} { return (id_or_type(yytext)); } {RGX_AGG} { if ((yylval.l_str = strdup(yytext)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); return (DT_TOK_AGG); } "@" { if ((yylval.l_str = strdup("@_")) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); return (DT_TOK_AGG); } {RGX_INT} | {RGX_INT} | {RGX_INT} { char *p; errno = 0; yylval.l_int = strtoull(yytext, &p, 0); yyintprefix = 0; (void) strncpy(yyintsuffix, p, sizeof (yyintsuffix)); yyintdecimal = yytext[0] != '0'; if (errno == ERANGE) { xyerror(D_INT_OFLOW, "constant %s results in " "integer overflow\n", yytext); } if (*p != '\0' && strchr("uUlL", *p) == NULL) { xyerror(D_INT_DIGIT, "constant %s contains " "invalid digit %c\n", yytext, *p); } if ((YYSTATE) != S3) return (DT_TOK_INT); yypragma = dt_node_link(yypragma, dt_node_int(yylval.l_int)); } {RGX_FP} yyerror("floating-point constants are not permitted\n"); \"{RGX_STR}$ | \"{RGX_STR}$ xyerror(D_STR_NL, "newline encountered in string literal"); \"{RGX_STR}\" | \"{RGX_STR}\" { /* * Quoted string -- convert C escape sequences and * return the string as a token. */ yylval.l_str = strndup(yytext + 1, yyleng - 2); if (yylval.l_str == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); (void) stresc2chr(yylval.l_str); if ((YYSTATE) != S3) return (DT_TOK_STRING); yypragma = dt_node_link(yypragma, dt_node_string(yylval.l_str)); } '{RGX_CHR}$ xyerror(D_CHR_NL, "newline encountered in character constant"); '{RGX_CHR}' { char *s, *p, *q; size_t nbytes; /* * Character constant -- convert C escape sequences and * return the character as an integer immediate value. */ if (yyleng == 2) xyerror(D_CHR_NULL, "empty character constant"); s = yytext + 1; yytext[yyleng - 1] = '\0'; nbytes = stresc2chr(s); yylval.l_int = 0; yyintprefix = 0; yyintsuffix[0] = '\0'; yyintdecimal = 1; if (nbytes > sizeof (yylval.l_int)) { xyerror(D_CHR_OFLOW, "character constant is " "too long"); } #if BYTE_ORDER == _LITTLE_ENDIAN p = ((char *)&yylval.l_int) + nbytes - 1; for (q = s; nbytes != 0; nbytes--) *p-- = *q++; #else bcopy(s, ((char *)&yylval.l_int) + sizeof (yylval.l_int) - nbytes, nbytes); #endif return (DT_TOK_INT); } "/*" | "/*" { yypcb->pcb_cstate = (YYSTATE); BEGIN(S1); } {RGX_INTERP} | {RGX_INTERP} ; /* discard any #! lines */ {RGX_CTL} | {RGX_CTL} | {RGX_CTL} { assert(yypragma == NULL); yypcb->pcb_cstate = (YYSTATE); BEGIN(S3); } . ; /* discard */ "\n" ; /* discard */ "/" { int c, tok; /* * The use of "/" as the predicate delimiter and as the * integer division symbol requires special lookahead * to avoid a shift/reduce conflict in the D grammar. * We look ahead to the next non-whitespace character. * If we encounter EOF, ";", "{", or "/", then this "/" * closes the predicate and we return DT_TOK_EPRED. * If we encounter anything else, it's DT_TOK_DIV. */ while ((c = input()) != 0) { if (strchr("\f\n\r\t\v ", c) == NULL) break; } if (c == 0 || c == ';' || c == '{' || c == '/') { if (yypcb->pcb_parens != 0) { yyerror("closing ) expected in " "predicate before /\n"); } if (yypcb->pcb_brackets != 0) { yyerror("closing ] expected in " "predicate before /\n"); } tok = DT_TOK_EPRED; } else tok = DT_TOK_DIV; unput(c); return (tok); } "(" { yypcb->pcb_parens++; return (DT_TOK_LPAR); } ")" { if (--yypcb->pcb_parens < 0) yyerror("extra ) in input stream\n"); return (DT_TOK_RPAR); } "[" { yypcb->pcb_brackets++; return (DT_TOK_LBRAC); } "]" { if (--yypcb->pcb_brackets < 0) yyerror("extra ] in input stream\n"); return (DT_TOK_RBRAC); } "{" | "{" { yypcb->pcb_braces++; return ('{'); } "}" { if (--yypcb->pcb_braces < 0) yyerror("extra } in input stream\n"); return ('}'); } "|" return (DT_TOK_BOR); "^" return (DT_TOK_XOR); "&" return (DT_TOK_BAND); "&&" return (DT_TOK_LAND); "^^" return (DT_TOK_LXOR); "||" return (DT_TOK_LOR); "==" return (DT_TOK_EQU); "!=" return (DT_TOK_NEQ); "<" return (DT_TOK_LT); "<=" return (DT_TOK_LE); ">" return (DT_TOK_GT); ">=" return (DT_TOK_GE); "<<" return (DT_TOK_LSH); ">>" return (DT_TOK_RSH); "+" return (DT_TOK_ADD); "-" return (DT_TOK_SUB); "*" return (DT_TOK_MUL); "%" return (DT_TOK_MOD); "~" return (DT_TOK_BNEG); "!" return (DT_TOK_LNEG); "?" return (DT_TOK_QUESTION); ":" return (DT_TOK_COLON); "." return (DT_TOK_DOT); "->" return (DT_TOK_PTR); "=" return (DT_TOK_ASGN); "+=" return (DT_TOK_ADD_EQ); "-=" return (DT_TOK_SUB_EQ); "*=" return (DT_TOK_MUL_EQ); "/=" return (DT_TOK_DIV_EQ); "%=" return (DT_TOK_MOD_EQ); "&=" return (DT_TOK_AND_EQ); "^=" return (DT_TOK_XOR_EQ); "|=" return (DT_TOK_OR_EQ); "<<=" return (DT_TOK_LSH_EQ); ">>=" return (DT_TOK_RSH_EQ); "++" return (DT_TOK_ADDADD); "--" return (DT_TOK_SUBSUB); "..." return (DT_TOK_ELLIPSIS); "," return (DT_TOK_COMMA); ";" return (';'); {RGX_WS} ; /* discard */ "\\"\n ; /* discard */ . yyerror("syntax error near \"%c\"\n", yytext[0]); "/*" yyerror("/* encountered inside a comment\n"); "*/" BEGIN(yypcb->pcb_cstate); .|\n ; /* discard */ {RGX_PSPEC} { /* * S2 has an ambiguity because RGX_PSPEC includes '*' * as a glob character and '*' also can be DT_TOK_STAR. * Since lex always matches the longest token, this * rule can be matched by an input string like "int*", * which could begin a global variable declaration such * as "int*x;" or could begin a RGX_PSPEC with globbing * such as "int* { trace(timestamp); }". If C_PSPEC is * not set, we must resolve the ambiguity in favor of * the type and perform lexer pushback if the fragment * before '*' or entire fragment matches a type name. * If C_PSPEC is set, we always return a PSPEC token. * If C_PSPEC is off, the user can avoid ambiguity by * including a ':' delimiter in the specifier, which * they should be doing anyway to specify the provider. */ if (!(yypcb->pcb_cflags & DTRACE_C_PSPEC) && strchr(yytext, ':') == NULL) { char *p = strchr(yytext, '*'); char *q = yytext + yyleng - 1; if (p != NULL && p > yytext) *p = '\0'; /* prune yytext */ if (dt_type_lookup(yytext, NULL) == 0) { yylval.l_str = strdup(yytext); if (yylval.l_str == NULL) { longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } if (p != NULL && p > yytext) { for (*p = '*'; q >= p; q--) unput(*q); } yybegin(YYS_EXPR); return (DT_TOK_TNAME); } if (p != NULL && p > yytext) *p = '*'; /* restore yytext */ } if ((yylval.l_str = strdup(yytext)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); return (DT_TOK_PSPEC); } "/" return (DT_TOK_DIV); "," return (DT_TOK_COMMA); {RGX_WS} ; /* discard */ . yyerror("syntax error near \"%c\"\n", yytext[0]); \n { dt_pragma(yypragma); yypragma = NULL; BEGIN(yypcb->pcb_cstate); } [\f\t\v ]+ ; /* discard */ [^\f\n\t\v "]+ { dt_node_t *dnp; if ((yylval.l_str = strdup(yytext)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); /* * We want to call dt_node_ident() here, but we can't * because it will expand inlined identifiers, which we * don't want to do from #pragma context in order to * support pragmas that apply to the ident itself. We * call dt_node_string() and then reset dn_op instead. */ dnp = dt_node_string(yylval.l_str); dnp->dn_kind = DT_NODE_IDENT; dnp->dn_op = DT_TOK_IDENT; yypragma = dt_node_link(yypragma, dnp); } . yyerror("syntax error near \"%c\"\n", yytext[0]); %% /* * yybegin provides a wrapper for use from C code around the lex BEGIN() macro. * We use two main states for lexing because probe descriptions use a syntax * that is incompatible with the normal D tokens (e.g. names can contain "-"). * yybegin also handles the job of switching between two lists of dt_nodes * as we allocate persistent definitions, like inlines, and transient nodes * that will be freed once we are done parsing the current program file. */ void yybegin(yystate_t state) { #ifdef YYDEBUG yydebug = _dtrace_debug; #endif if (yypcb->pcb_yystate == state) return; /* nothing to do if we're in the state already */ if (yypcb->pcb_yystate == YYS_DEFINE) { yypcb->pcb_list = yypcb->pcb_hold; yypcb->pcb_hold = NULL; } switch (state) { case YYS_CLAUSE: BEGIN(S2); break; case YYS_DEFINE: assert(yypcb->pcb_hold == NULL); yypcb->pcb_hold = yypcb->pcb_list; yypcb->pcb_list = NULL; /*FALLTHRU*/ case YYS_EXPR: BEGIN(S0); break; case YYS_DONE: break; case YYS_CONTROL: BEGIN(S4); break; default: xyerror(D_UNKNOWN, "internal error -- bad yystate %d\n", state); } yypcb->pcb_yystate = state; } void yyinit(dt_pcb_t *pcb) { yypcb = pcb; yylineno = 1; yypragma = NULL; #if defined(sun) yysptr = yysbuf; #endif } /* * Given a lexeme 's' (typically yytext), set yylval and return an appropriate * token to the parser indicating either an identifier or a typedef name. * User-defined global variables always take precedence over types, but we do * use some heuristics because D programs can look at an ever-changing set of * kernel types and also can implicitly instantiate variables by assignment, * unlike in C. The code here is ordered carefully as lookups are not cheap. */ static int id_or_type(const char *s) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; int c0, c1, ttok = DT_TOK_TNAME; dt_ident_t *idp; if ((s = yylval.l_str = strdup(s)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); /* * If the lexeme is a global variable or likely identifier or *not* a * type_name, then it is an identifier token. */ if (dt_idstack_lookup(&yypcb->pcb_globals, s) != NULL || dt_idhash_lookup(yypcb->pcb_idents, s) != NULL || dt_type_lookup(s, NULL) != 0) return (DT_TOK_IDENT); /* * If we're in the midst of parsing a declaration and a type_specifier * has already been shifted, then return DT_TOK_IDENT instead of TNAME. * This semantic is necessary to permit valid ISO C code such as: * * typedef int foo; * struct s { foo foo; }; * * without causing shift/reduce conflicts in the direct_declarator part * of the grammar. The result is that we must check for conflicting * redeclarations of the same identifier as part of dt_node_decl(). */ if (ddp != NULL && ddp->dd_name != NULL) return (DT_TOK_IDENT); /* * If the lexeme is a type name and we are not in a program clause, * then always interpret it as a type and return DT_TOK_TNAME. */ if ((YYSTATE) != S0) return (DT_TOK_TNAME); /* * If the lexeme matches a type name but is in a program clause, then * it could be a type or it could be an undefined variable. Peek at * the next token to decide. If we see ++, --, [, or =, we know there * might be an assignment that is trying to create a global variable, * so we optimistically return DT_TOK_IDENT. There is no harm in being * wrong: a type_name followed by ++, --, [, or = is a syntax error. */ while ((c0 = input()) != 0) { if (strchr("\f\n\r\t\v ", c0) == NULL) break; } switch (c0) { case '+': case '-': if ((c1 = input()) == c0) ttok = DT_TOK_IDENT; unput(c1); break; case '=': if ((c1 = input()) != c0) ttok = DT_TOK_IDENT; unput(c1); break; case '[': ttok = DT_TOK_IDENT; break; } if (ttok == DT_TOK_IDENT) { idp = dt_idhash_insert(yypcb->pcb_idents, s, DT_IDENT_SCALAR, 0, 0, _dtrace_defattr, 0, &dt_idops_thaw, NULL, dtp->dt_gen); if (idp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } unput(c0); return (ttok); } #if defined(sun) static int input(void) { int c; if (yysptr > yysbuf) c = *--yysptr; else if (yypcb->pcb_fileptr != NULL) c = fgetc(yypcb->pcb_fileptr); else if (yypcb->pcb_strptr < yypcb->pcb_string + yypcb->pcb_strlen) c = *(unsigned char *)(yypcb->pcb_strptr++); else c = EOF; if (c == '\n') yylineno++; if (c != EOF) return (c); if ((YYSTATE) == S1) yyerror("end-of-file encountered before matching */\n"); if ((YYSTATE) == S3) yyerror("end-of-file encountered before end of control line\n"); if (yypcb->pcb_fileptr != NULL && ferror(yypcb->pcb_fileptr)) longjmp(yypcb->pcb_jmpbuf, EDT_FIO); return (0); /* EOF */ } static void unput(int c) { if (c == '\n') yylineno--; *yysptr++ = c; yytchar = c; } #endif Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.c (revision 268578) @@ -1,1467 +1,1735 @@ /* * 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) 2013, Joyent, Inc. All rights reserved. + */ #include #if defined(sun) #include #include #include #include #include #include #else #include #include #include #endif #include #if defined(sun) #include #endif #include #include #include #include #include #include #include #if !defined(sun) #include +#include #endif #include #include #include static const char *dt_module_strtab; /* active strtab for qsort callbacks */ static void dt_module_symhash_insert(dt_module_t *dmp, const char *name, uint_t id) { dt_sym_t *dsp = &dmp->dm_symchains[dmp->dm_symfree]; uint_t h; assert(dmp->dm_symfree < dmp->dm_nsymelems + 1); dsp->ds_symid = id; h = dt_strtab_hash(name, NULL) % dmp->dm_nsymbuckets; dsp->ds_next = dmp->dm_symbuckets[h]; dmp->dm_symbuckets[h] = dmp->dm_symfree++; } static uint_t dt_module_syminit32(dt_module_t *dmp) { #if STT_NUM != (STT_TLS + 1) #error "STT_NUM has grown. update dt_module_syminit32()" #endif Elf32_Sym *sym = dmp->dm_symtab.cts_data; const char *base = dmp->dm_strtab.cts_data; size_t ss_size = dmp->dm_strtab.cts_size; uint_t i, n = dmp->dm_nsymelems; uint_t asrsv = 0; #if defined(__FreeBSD__) GElf_Ehdr ehdr; int is_elf_obj; gelf_getehdr(dmp->dm_elf, &ehdr); is_elf_obj = (ehdr.e_type == ET_REL); #endif for (i = 0; i < n; i++, sym++) { const char *name = base + sym->st_name; uchar_t type = ELF32_ST_TYPE(sym->st_info); if (type >= STT_NUM || type == STT_SECTION) continue; /* skip sections and unknown types */ if (sym->st_name == 0 || sym->st_name >= ss_size) continue; /* skip null or invalid names */ if (sym->st_value != 0 && (ELF32_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size)) { asrsv++; /* reserve space in the address map */ #if defined(__FreeBSD__) sym->st_value += (Elf_Addr) dmp->dm_reloc_offset; if (is_elf_obj && sym->st_shndx != SHN_UNDEF && sym->st_shndx < ehdr.e_shnum) sym->st_value += dmp->dm_sec_offsets[sym->st_shndx]; #endif } dt_module_symhash_insert(dmp, name, i); } return (asrsv); } static uint_t dt_module_syminit64(dt_module_t *dmp) { #if STT_NUM != (STT_TLS + 1) #error "STT_NUM has grown. update dt_module_syminit64()" #endif Elf64_Sym *sym = dmp->dm_symtab.cts_data; const char *base = dmp->dm_strtab.cts_data; size_t ss_size = dmp->dm_strtab.cts_size; uint_t i, n = dmp->dm_nsymelems; uint_t asrsv = 0; #if defined(__FreeBSD__) GElf_Ehdr ehdr; int is_elf_obj; gelf_getehdr(dmp->dm_elf, &ehdr); is_elf_obj = (ehdr.e_type == ET_REL); #endif for (i = 0; i < n; i++, sym++) { const char *name = base + sym->st_name; uchar_t type = ELF64_ST_TYPE(sym->st_info); if (type >= STT_NUM || type == STT_SECTION) continue; /* skip sections and unknown types */ if (sym->st_name == 0 || sym->st_name >= ss_size) continue; /* skip null or invalid names */ if (sym->st_value != 0 && (ELF64_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size)) { asrsv++; /* reserve space in the address map */ #if defined(__FreeBSD__) sym->st_value += (Elf_Addr) dmp->dm_reloc_offset; if (is_elf_obj && sym->st_shndx != SHN_UNDEF && sym->st_shndx < ehdr.e_shnum) sym->st_value += dmp->dm_sec_offsets[sym->st_shndx]; #endif } dt_module_symhash_insert(dmp, name, i); } return (asrsv); } /* * Sort comparison function for 32-bit symbol address-to-name lookups. We sort * symbols by value. If values are equal, we prefer the symbol that is * non-zero sized, typed, not weak, or lexically first, in that order. */ static int dt_module_symcomp32(const void *lp, const void *rp) { Elf32_Sym *lhs = *((Elf32_Sym **)lp); Elf32_Sym *rhs = *((Elf32_Sym **)rp); if (lhs->st_value != rhs->st_value) return (lhs->st_value > rhs->st_value ? 1 : -1); if ((lhs->st_size == 0) != (rhs->st_size == 0)) return (lhs->st_size == 0 ? 1 : -1); if ((ELF32_ST_TYPE(lhs->st_info) == STT_NOTYPE) != (ELF32_ST_TYPE(rhs->st_info) == STT_NOTYPE)) return (ELF32_ST_TYPE(lhs->st_info) == STT_NOTYPE ? 1 : -1); if ((ELF32_ST_BIND(lhs->st_info) == STB_WEAK) != (ELF32_ST_BIND(rhs->st_info) == STB_WEAK)) return (ELF32_ST_BIND(lhs->st_info) == STB_WEAK ? 1 : -1); return (strcmp(dt_module_strtab + lhs->st_name, dt_module_strtab + rhs->st_name)); } /* * Sort comparison function for 64-bit symbol address-to-name lookups. We sort * symbols by value. If values are equal, we prefer the symbol that is * non-zero sized, typed, not weak, or lexically first, in that order. */ static int dt_module_symcomp64(const void *lp, const void *rp) { Elf64_Sym *lhs = *((Elf64_Sym **)lp); Elf64_Sym *rhs = *((Elf64_Sym **)rp); if (lhs->st_value != rhs->st_value) return (lhs->st_value > rhs->st_value ? 1 : -1); if ((lhs->st_size == 0) != (rhs->st_size == 0)) return (lhs->st_size == 0 ? 1 : -1); if ((ELF64_ST_TYPE(lhs->st_info) == STT_NOTYPE) != (ELF64_ST_TYPE(rhs->st_info) == STT_NOTYPE)) return (ELF64_ST_TYPE(lhs->st_info) == STT_NOTYPE ? 1 : -1); if ((ELF64_ST_BIND(lhs->st_info) == STB_WEAK) != (ELF64_ST_BIND(rhs->st_info) == STB_WEAK)) return (ELF64_ST_BIND(lhs->st_info) == STB_WEAK ? 1 : -1); return (strcmp(dt_module_strtab + lhs->st_name, dt_module_strtab + rhs->st_name)); } static void dt_module_symsort32(dt_module_t *dmp) { Elf32_Sym *symtab = (Elf32_Sym *)dmp->dm_symtab.cts_data; Elf32_Sym **sympp = (Elf32_Sym **)dmp->dm_asmap; const dt_sym_t *dsp = dmp->dm_symchains + 1; uint_t i, n = dmp->dm_symfree; for (i = 1; i < n; i++, dsp++) { Elf32_Sym *sym = symtab + dsp->ds_symid; if (sym->st_value != 0 && (ELF32_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size)) *sympp++ = sym; } dmp->dm_aslen = (uint_t)(sympp - (Elf32_Sym **)dmp->dm_asmap); assert(dmp->dm_aslen <= dmp->dm_asrsv); dt_module_strtab = dmp->dm_strtab.cts_data; qsort(dmp->dm_asmap, dmp->dm_aslen, sizeof (Elf32_Sym *), dt_module_symcomp32); dt_module_strtab = NULL; } static void dt_module_symsort64(dt_module_t *dmp) { Elf64_Sym *symtab = (Elf64_Sym *)dmp->dm_symtab.cts_data; Elf64_Sym **sympp = (Elf64_Sym **)dmp->dm_asmap; const dt_sym_t *dsp = dmp->dm_symchains + 1; uint_t i, n = dmp->dm_symfree; for (i = 1; i < n; i++, dsp++) { Elf64_Sym *sym = symtab + dsp->ds_symid; if (sym->st_value != 0 && (ELF64_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size)) *sympp++ = sym; } dmp->dm_aslen = (uint_t)(sympp - (Elf64_Sym **)dmp->dm_asmap); assert(dmp->dm_aslen <= dmp->dm_asrsv); dt_module_strtab = dmp->dm_strtab.cts_data; qsort(dmp->dm_asmap, dmp->dm_aslen, sizeof (Elf64_Sym *), dt_module_symcomp64); dt_module_strtab = NULL; } static GElf_Sym * dt_module_symgelf32(const Elf32_Sym *src, GElf_Sym *dst) { if (dst != NULL) { dst->st_name = src->st_name; dst->st_info = src->st_info; dst->st_other = src->st_other; dst->st_shndx = src->st_shndx; dst->st_value = src->st_value; dst->st_size = src->st_size; } return (dst); } static GElf_Sym * dt_module_symgelf64(const Elf64_Sym *src, GElf_Sym *dst) { if (dst != NULL) bcopy(src, dst, sizeof (GElf_Sym)); return (dst); } static GElf_Sym * dt_module_symname32(dt_module_t *dmp, const char *name, GElf_Sym *symp, uint_t *idp) { const Elf32_Sym *symtab = dmp->dm_symtab.cts_data; const char *strtab = dmp->dm_strtab.cts_data; const Elf32_Sym *sym; const dt_sym_t *dsp; uint_t i, h; if (dmp->dm_nsymelems == 0) return (NULL); h = dt_strtab_hash(name, NULL) % dmp->dm_nsymbuckets; for (i = dmp->dm_symbuckets[h]; i != 0; i = dsp->ds_next) { dsp = &dmp->dm_symchains[i]; sym = symtab + dsp->ds_symid; if (strcmp(name, strtab + sym->st_name) == 0) { if (idp != NULL) *idp = dsp->ds_symid; return (dt_module_symgelf32(sym, symp)); } } return (NULL); } static GElf_Sym * dt_module_symname64(dt_module_t *dmp, const char *name, GElf_Sym *symp, uint_t *idp) { const Elf64_Sym *symtab = dmp->dm_symtab.cts_data; const char *strtab = dmp->dm_strtab.cts_data; const Elf64_Sym *sym; const dt_sym_t *dsp; uint_t i, h; if (dmp->dm_nsymelems == 0) return (NULL); h = dt_strtab_hash(name, NULL) % dmp->dm_nsymbuckets; for (i = dmp->dm_symbuckets[h]; i != 0; i = dsp->ds_next) { dsp = &dmp->dm_symchains[i]; sym = symtab + dsp->ds_symid; if (strcmp(name, strtab + sym->st_name) == 0) { if (idp != NULL) *idp = dsp->ds_symid; return (dt_module_symgelf64(sym, symp)); } } return (NULL); } static GElf_Sym * dt_module_symaddr32(dt_module_t *dmp, GElf_Addr addr, GElf_Sym *symp, uint_t *idp) { const Elf32_Sym **asmap = (const Elf32_Sym **)dmp->dm_asmap; const Elf32_Sym *symtab = dmp->dm_symtab.cts_data; const Elf32_Sym *sym; uint_t i, mid, lo = 0, hi = dmp->dm_aslen - 1; Elf32_Addr v; if (dmp->dm_aslen == 0) return (NULL); while (hi - lo > 1) { mid = (lo + hi) / 2; if (addr >= asmap[mid]->st_value) lo = mid; else hi = mid; } i = addr < asmap[hi]->st_value ? lo : hi; sym = asmap[i]; v = sym->st_value; /* * If the previous entry has the same value, improve our choice. The * order of equal-valued symbols is determined by the comparison func. */ while (i-- != 0 && asmap[i]->st_value == v) sym = asmap[i]; if (addr - sym->st_value < MAX(sym->st_size, 1)) { if (idp != NULL) *idp = (uint_t)(sym - symtab); return (dt_module_symgelf32(sym, symp)); } return (NULL); } static GElf_Sym * dt_module_symaddr64(dt_module_t *dmp, GElf_Addr addr, GElf_Sym *symp, uint_t *idp) { const Elf64_Sym **asmap = (const Elf64_Sym **)dmp->dm_asmap; const Elf64_Sym *symtab = dmp->dm_symtab.cts_data; const Elf64_Sym *sym; uint_t i, mid, lo = 0, hi = dmp->dm_aslen - 1; Elf64_Addr v; if (dmp->dm_aslen == 0) return (NULL); while (hi - lo > 1) { mid = (lo + hi) / 2; if (addr >= asmap[mid]->st_value) lo = mid; else hi = mid; } i = addr < asmap[hi]->st_value ? lo : hi; sym = asmap[i]; v = sym->st_value; /* * If the previous entry has the same value, improve our choice. The * order of equal-valued symbols is determined by the comparison func. */ while (i-- != 0 && asmap[i]->st_value == v) sym = asmap[i]; if (addr - sym->st_value < MAX(sym->st_size, 1)) { if (idp != NULL) *idp = (uint_t)(sym - symtab); return (dt_module_symgelf64(sym, symp)); } return (NULL); } static const dt_modops_t dt_modops_32 = { dt_module_syminit32, dt_module_symsort32, dt_module_symname32, dt_module_symaddr32 }; static const dt_modops_t dt_modops_64 = { dt_module_syminit64, dt_module_symsort64, dt_module_symname64, dt_module_symaddr64 }; dt_module_t * dt_module_create(dtrace_hdl_t *dtp, const char *name) { + long pid; + char *eptr; + dt_ident_t *idp; uint_t h = dt_strtab_hash(name, NULL) % dtp->dt_modbuckets; dt_module_t *dmp; for (dmp = dtp->dt_mods[h]; dmp != NULL; dmp = dmp->dm_next) { if (strcmp(dmp->dm_name, name) == 0) return (dmp); } if ((dmp = malloc(sizeof (dt_module_t))) == NULL) return (NULL); /* caller must handle allocation failure */ bzero(dmp, sizeof (dt_module_t)); (void) strlcpy(dmp->dm_name, name, sizeof (dmp->dm_name)); dt_list_append(&dtp->dt_modlist, dmp); dmp->dm_next = dtp->dt_mods[h]; dtp->dt_mods[h] = dmp; dtp->dt_nmods++; if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) dmp->dm_ops = &dt_modops_64; else dmp->dm_ops = &dt_modops_32; + /* + * Modules for userland processes are special. They always refer to a + * specific process and have a copy of their CTF data from a specific + * instant in time. Any dt_module_t that begins with 'pid' is a module + * for a specific process, much like how any probe description that + * begins with 'pid' is special. pid123 refers to process 123. A module + * that is just 'pid' refers specifically to pid$target. This is + * generally done as D does not currently allow for macros to be + * evaluated when working with types. + */ + if (strncmp(dmp->dm_name, "pid", 3) == 0) { + errno = 0; + if (dmp->dm_name[3] == '\0') { + idp = dt_idhash_lookup(dtp->dt_macros, "target"); + if (idp != NULL && idp->di_id != 0) + dmp->dm_pid = idp->di_id; + } else { + pid = strtol(dmp->dm_name + 3, &eptr, 10); + if (errno == 0 && *eptr == '\0') + dmp->dm_pid = (pid_t)pid; + else + dt_dprintf("encountered malformed pid " + "module: %s\n", dmp->dm_name); + } + } + return (dmp); } dt_module_t * dt_module_lookup_by_name(dtrace_hdl_t *dtp, const char *name) { uint_t h = dt_strtab_hash(name, NULL) % dtp->dt_modbuckets; dt_module_t *dmp; for (dmp = dtp->dt_mods[h]; dmp != NULL; dmp = dmp->dm_next) { if (strcmp(dmp->dm_name, name) == 0) return (dmp); } return (NULL); } /*ARGSUSED*/ dt_module_t * dt_module_lookup_by_ctf(dtrace_hdl_t *dtp, ctf_file_t *ctfp) { return (ctfp ? ctf_getspecific(ctfp) : NULL); } static int dt_module_load_sect(dtrace_hdl_t *dtp, dt_module_t *dmp, ctf_sect_t *ctsp) { const char *s; size_t shstrs; GElf_Shdr sh; Elf_Data *dp; Elf_Scn *sp; if (elf_getshdrstrndx(dmp->dm_elf, &shstrs) == -1) return (dt_set_errno(dtp, EDT_NOTLOADED)); for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) continue; /* skip any malformed sections */ if (sh.sh_type == ctsp->cts_type && sh.sh_entsize == ctsp->cts_entsize && strcmp(s, ctsp->cts_name) == 0) break; /* section matches specification */ } /* * If the section isn't found, return success but leave cts_data set * to NULL and cts_size set to zero for our caller. */ if (sp == NULL || (dp = elf_getdata(sp, NULL)) == NULL) return (0); #if defined(sun) ctsp->cts_data = dp->d_buf; #else if ((ctsp->cts_data = malloc(dp->d_size)) == NULL) return (0); memcpy(ctsp->cts_data, dp->d_buf, dp->d_size); #endif ctsp->cts_size = dp->d_size; dt_dprintf("loaded %s [%s] (%lu bytes)\n", dmp->dm_name, ctsp->cts_name, (ulong_t)ctsp->cts_size); return (0); } +typedef struct dt_module_cb_arg { + struct ps_prochandle *dpa_proc; + dtrace_hdl_t *dpa_dtp; + dt_module_t *dpa_dmp; + uint_t dpa_count; +} dt_module_cb_arg_t; + +/* ARGSUSED */ +static int +dt_module_load_proc_count(void *arg, const prmap_t *prmap, const char *obj) +{ + ctf_file_t *fp; + dt_module_cb_arg_t *dcp = arg; + + /* Try to grab a ctf container if it exists */ + fp = Pname_to_ctf(dcp->dpa_proc, obj); + if (fp != NULL) + dcp->dpa_count++; + return (0); +} + +/* ARGSUSED */ +static int +dt_module_load_proc_build(void *arg, const prmap_t *prmap, const char *obj) +{ + ctf_file_t *fp; + char buf[MAXPATHLEN], *p; + dt_module_cb_arg_t *dcp = arg; + int count = dcp->dpa_count; + Lmid_t lmid; + + fp = Pname_to_ctf(dcp->dpa_proc, obj); + if (fp == NULL) + return (0); + fp = ctf_dup(fp); + if (fp == NULL) + return (0); + dcp->dpa_dmp->dm_libctfp[count] = fp; + /* + * While it'd be nice to simply use objname here, because of our prior + * actions we'll always get a resolved object name to its on disk file. + * Like the pid provider, we need to tell a bit of a lie here. The type + * that the user thinks of is in terms of the libraries they requested, + * eg. libc.so.1, they don't care about the fact that it's + * libc_hwcap.so.1. + */ + (void) Pobjname(dcp->dpa_proc, prmap->pr_vaddr, buf, sizeof (buf)); + if ((p = strrchr(buf, '/')) == NULL) + p = buf; + else + p++; + + /* + * If for some reason we can't find a link map id for this module, which + * would be really quite weird. We instead just say the link map id is + * zero. + */ + if (Plmid(dcp->dpa_proc, prmap->pr_vaddr, &lmid) != 0) + lmid = 0; + + if (lmid == 0) + dcp->dpa_dmp->dm_libctfn[count] = strdup(p); + else + (void) asprintf(&dcp->dpa_dmp->dm_libctfn[count], + "LM%x`%s", lmid, p); + if (dcp->dpa_dmp->dm_libctfn[count] == NULL) + return (1); + ctf_setspecific(fp, dcp->dpa_dmp); + dcp->dpa_count++; + return (0); +} + +/* + * We've been asked to load data that belongs to another process. As such we're + * going to pgrab it at this instant, load everything that we might ever care + * about, and then drive on. The reason for this is that the process that we're + * interested in might be changing. As long as we have grabbed it, then this + * can't be a problem for us. + * + * For now, we're actually going to punt on most things and just try to get CTF + * data, nothing else. Basically this is only useful as a source of type + * information, we can't go and do the stacktrace lookups, etc. + */ +static int +dt_module_load_proc(dtrace_hdl_t *dtp, dt_module_t *dmp) +{ + struct ps_prochandle *p; + dt_module_cb_arg_t arg; + + /* + * Note that on success we do not release this hold. We must hold this + * for our life time. + */ + p = dt_proc_grab(dtp, dmp->dm_pid, 0, PGRAB_RDONLY | PGRAB_FORCE); + if (p == NULL) { + dt_dprintf("failed to grab pid: %d\n", (int)dmp->dm_pid); + return (dt_set_errno(dtp, EDT_CANTLOAD)); + } + dt_proc_lock(dtp, p); + + arg.dpa_proc = p; + arg.dpa_dtp = dtp; + arg.dpa_dmp = dmp; + arg.dpa_count = 0; + if (Pobject_iter_resolved(p, dt_module_load_proc_count, &arg) != 0) { + dt_dprintf("failed to iterate objects\n"); + dt_proc_release(dtp, p); + return (dt_set_errno(dtp, EDT_CANTLOAD)); + } + + if (arg.dpa_count == 0) { + dt_dprintf("no ctf data present\n"); + dt_proc_unlock(dtp, p); + dt_proc_release(dtp, p); + return (dt_set_errno(dtp, EDT_CANTLOAD)); + } + + dmp->dm_libctfp = malloc(sizeof (ctf_file_t *) * arg.dpa_count); + if (dmp->dm_libctfp == NULL) { + dt_proc_unlock(dtp, p); + dt_proc_release(dtp, p); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + bzero(dmp->dm_libctfp, sizeof (ctf_file_t *) * arg.dpa_count); + + dmp->dm_libctfn = malloc(sizeof (char *) * arg.dpa_count); + if (dmp->dm_libctfn == NULL) { + free(dmp->dm_libctfp); + dt_proc_unlock(dtp, p); + dt_proc_release(dtp, p); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + bzero(dmp->dm_libctfn, sizeof (char *) * arg.dpa_count); + + dmp->dm_nctflibs = arg.dpa_count; + + arg.dpa_count = 0; + if (Pobject_iter_resolved(p, dt_module_load_proc_build, &arg) != 0) { + dt_proc_unlock(dtp, p); + dt_module_unload(dtp, dmp); + dt_proc_release(dtp, p); + return (dt_set_errno(dtp, EDT_CANTLOAD)); + } + assert(arg.dpa_count == dmp->dm_nctflibs); + dt_dprintf("loaded %d ctf modules for pid %d\n", arg.dpa_count, + (int)dmp->dm_pid); + + dt_proc_unlock(dtp, p); + dt_proc_release(dtp, p); + dmp->dm_flags |= DT_DM_LOADED; + + return (0); +} + int dt_module_load(dtrace_hdl_t *dtp, dt_module_t *dmp) { if (dmp->dm_flags & DT_DM_LOADED) return (0); /* module is already loaded */ + if (dmp->dm_pid != 0) + return (dt_module_load_proc(dtp, dmp)); + dmp->dm_ctdata.cts_name = ".SUNW_ctf"; dmp->dm_ctdata.cts_type = SHT_PROGBITS; dmp->dm_ctdata.cts_flags = 0; dmp->dm_ctdata.cts_data = NULL; dmp->dm_ctdata.cts_size = 0; dmp->dm_ctdata.cts_entsize = 0; dmp->dm_ctdata.cts_offset = 0; dmp->dm_symtab.cts_name = ".symtab"; dmp->dm_symtab.cts_type = SHT_SYMTAB; dmp->dm_symtab.cts_flags = 0; dmp->dm_symtab.cts_data = NULL; dmp->dm_symtab.cts_size = 0; dmp->dm_symtab.cts_entsize = dmp->dm_ops == &dt_modops_64 ? sizeof (Elf64_Sym) : sizeof (Elf32_Sym); dmp->dm_symtab.cts_offset = 0; dmp->dm_strtab.cts_name = ".strtab"; dmp->dm_strtab.cts_type = SHT_STRTAB; dmp->dm_strtab.cts_flags = 0; dmp->dm_strtab.cts_data = NULL; dmp->dm_strtab.cts_size = 0; dmp->dm_strtab.cts_entsize = 0; dmp->dm_strtab.cts_offset = 0; /* * Attempt to load the module's CTF section, symbol table section, and * string table section. Note that modules may not contain CTF data: * this will result in a successful load_sect but data of size zero. * We will then fail if dt_module_getctf() is called, as shown below. */ if (dt_module_load_sect(dtp, dmp, &dmp->dm_ctdata) == -1 || dt_module_load_sect(dtp, dmp, &dmp->dm_symtab) == -1 || dt_module_load_sect(dtp, dmp, &dmp->dm_strtab) == -1) { dt_module_unload(dtp, dmp); return (-1); /* dt_errno is set for us */ } /* * Allocate the hash chains and hash buckets for symbol name lookup. * This is relatively simple since the symbol table is of fixed size * and is known in advance. We allocate one extra element since we * use element indices instead of pointers and zero is our sentinel. */ dmp->dm_nsymelems = dmp->dm_symtab.cts_size / dmp->dm_symtab.cts_entsize; dmp->dm_nsymbuckets = _dtrace_strbuckets; dmp->dm_symfree = 1; /* first free element is index 1 */ dmp->dm_symbuckets = malloc(sizeof (uint_t) * dmp->dm_nsymbuckets); dmp->dm_symchains = malloc(sizeof (dt_sym_t) * dmp->dm_nsymelems + 1); if (dmp->dm_symbuckets == NULL || dmp->dm_symchains == NULL) { dt_module_unload(dtp, dmp); return (dt_set_errno(dtp, EDT_NOMEM)); } bzero(dmp->dm_symbuckets, sizeof (uint_t) * dmp->dm_nsymbuckets); bzero(dmp->dm_symchains, sizeof (dt_sym_t) * dmp->dm_nsymelems + 1); /* * Iterate over the symbol table data buffer and insert each symbol * name into the name hash if the name and type are valid. Then * allocate the address map, fill it in, and sort it. */ dmp->dm_asrsv = dmp->dm_ops->do_syminit(dmp); dt_dprintf("hashed %s [%s] (%u symbols)\n", dmp->dm_name, dmp->dm_symtab.cts_name, dmp->dm_symfree - 1); if ((dmp->dm_asmap = malloc(sizeof (void *) * dmp->dm_asrsv)) == NULL) { dt_module_unload(dtp, dmp); return (dt_set_errno(dtp, EDT_NOMEM)); } dmp->dm_ops->do_symsort(dmp); dt_dprintf("sorted %s [%s] (%u symbols)\n", dmp->dm_name, dmp->dm_symtab.cts_name, dmp->dm_aslen); dmp->dm_flags |= DT_DM_LOADED; return (0); } +int +dt_module_hasctf(dtrace_hdl_t *dtp, dt_module_t *dmp) +{ + if (dmp->dm_pid != 0 && dmp->dm_nctflibs > 0) + return (1); + return (dt_module_getctf(dtp, dmp) != NULL); +} + ctf_file_t * dt_module_getctf(dtrace_hdl_t *dtp, dt_module_t *dmp) { const char *parent; dt_module_t *pmp; ctf_file_t *pfp; int model; if (dmp->dm_ctfp != NULL || dt_module_load(dtp, dmp) != 0) return (dmp->dm_ctfp); if (dmp->dm_ops == &dt_modops_64) model = CTF_MODEL_LP64; else model = CTF_MODEL_ILP32; /* * If the data model of the module does not match our program data * model, then do not permit CTF from this module to be opened and * returned to the compiler. If we support mixed data models in the * future for combined kernel/user tracing, this can be removed. */ if (dtp->dt_conf.dtc_ctfmodel != model) { (void) dt_set_errno(dtp, EDT_DATAMODEL); return (NULL); } if (dmp->dm_ctdata.cts_size == 0) { (void) dt_set_errno(dtp, EDT_NOCTF); return (NULL); } dmp->dm_ctfp = ctf_bufopen(&dmp->dm_ctdata, &dmp->dm_symtab, &dmp->dm_strtab, &dtp->dt_ctferr); if (dmp->dm_ctfp == NULL) { (void) dt_set_errno(dtp, EDT_CTF); return (NULL); } (void) ctf_setmodel(dmp->dm_ctfp, model); ctf_setspecific(dmp->dm_ctfp, dmp); if ((parent = ctf_parent_name(dmp->dm_ctfp)) != NULL) { if ((pmp = dt_module_create(dtp, parent)) == NULL || (pfp = dt_module_getctf(dtp, pmp)) == NULL) { if (pmp == NULL) (void) dt_set_errno(dtp, EDT_NOMEM); goto err; } if (ctf_import(dmp->dm_ctfp, pfp) == CTF_ERR) { dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp); (void) dt_set_errno(dtp, EDT_CTF); goto err; } } dt_dprintf("loaded CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); return (dmp->dm_ctfp); err: ctf_close(dmp->dm_ctfp); dmp->dm_ctfp = NULL; return (NULL); } /*ARGSUSED*/ void dt_module_unload(dtrace_hdl_t *dtp, dt_module_t *dmp) { + int i; + ctf_close(dmp->dm_ctfp); dmp->dm_ctfp = NULL; #if !defined(sun) if (dmp->dm_ctdata.cts_data != NULL) { free(dmp->dm_ctdata.cts_data); } if (dmp->dm_symtab.cts_data != NULL) { free(dmp->dm_symtab.cts_data); } if (dmp->dm_strtab.cts_data != NULL) { free(dmp->dm_strtab.cts_data); } #endif + if (dmp->dm_libctfp != NULL) { + for (i = 0; i < dmp->dm_nctflibs; i++) { + ctf_close(dmp->dm_libctfp[i]); + free(dmp->dm_libctfn[i]); + } + free(dmp->dm_libctfp); + free(dmp->dm_libctfn); + dmp->dm_libctfp = NULL; + dmp->dm_nctflibs = 0; + } + bzero(&dmp->dm_ctdata, sizeof (ctf_sect_t)); bzero(&dmp->dm_symtab, sizeof (ctf_sect_t)); bzero(&dmp->dm_strtab, sizeof (ctf_sect_t)); if (dmp->dm_symbuckets != NULL) { free(dmp->dm_symbuckets); dmp->dm_symbuckets = NULL; } if (dmp->dm_symchains != NULL) { free(dmp->dm_symchains); dmp->dm_symchains = NULL; } if (dmp->dm_asmap != NULL) { free(dmp->dm_asmap); dmp->dm_asmap = NULL; } #if defined(__FreeBSD__) if (dmp->dm_sec_offsets != NULL) { free(dmp->dm_sec_offsets); dmp->dm_sec_offsets = NULL; } #endif dmp->dm_symfree = 0; dmp->dm_nsymbuckets = 0; dmp->dm_nsymelems = 0; dmp->dm_asrsv = 0; dmp->dm_aslen = 0; dmp->dm_text_va = 0; dmp->dm_text_size = 0; dmp->dm_data_va = 0; dmp->dm_data_size = 0; dmp->dm_bss_va = 0; dmp->dm_bss_size = 0; if (dmp->dm_extern != NULL) { dt_idhash_destroy(dmp->dm_extern); dmp->dm_extern = NULL; } (void) elf_end(dmp->dm_elf); dmp->dm_elf = NULL; + dmp->dm_pid = 0; + dmp->dm_flags &= ~DT_DM_LOADED; } void dt_module_destroy(dtrace_hdl_t *dtp, dt_module_t *dmp) { uint_t h = dt_strtab_hash(dmp->dm_name, NULL) % dtp->dt_modbuckets; dt_module_t **dmpp = &dtp->dt_mods[h]; dt_list_delete(&dtp->dt_modlist, dmp); assert(dtp->dt_nmods != 0); dtp->dt_nmods--; /* * Now remove this module from its hash chain. We expect to always * find the module on its hash chain, so in this loop we assert that * we don't run off the end of the list. */ while (*dmpp != dmp) { dmpp = &((*dmpp)->dm_next); assert(*dmpp != NULL); } *dmpp = dmp->dm_next; dt_module_unload(dtp, dmp); free(dmp); } /* * Insert a new external symbol reference into the specified module. The new * symbol will be marked as undefined and is assigned a symbol index beyond * any existing cached symbols from this module. We use the ident's di_data * field to store a pointer to a copy of the dtrace_syminfo_t for this symbol. */ dt_ident_t * dt_module_extern(dtrace_hdl_t *dtp, dt_module_t *dmp, const char *name, const dtrace_typeinfo_t *tip) { dtrace_syminfo_t *sip; dt_ident_t *idp; uint_t id; if (dmp->dm_extern == NULL && (dmp->dm_extern = dt_idhash_create( "extern", NULL, dmp->dm_nsymelems, UINT_MAX)) == NULL) { (void) dt_set_errno(dtp, EDT_NOMEM); return (NULL); } if (dt_idhash_nextid(dmp->dm_extern, &id) == -1) { (void) dt_set_errno(dtp, EDT_SYMOFLOW); return (NULL); } if ((sip = malloc(sizeof (dtrace_syminfo_t))) == NULL) { (void) dt_set_errno(dtp, EDT_NOMEM); return (NULL); } idp = dt_idhash_insert(dmp->dm_extern, name, DT_IDENT_SYMBOL, 0, id, _dtrace_symattr, 0, &dt_idops_thaw, NULL, dtp->dt_gen); if (idp == NULL) { (void) dt_set_errno(dtp, EDT_NOMEM); free(sip); return (NULL); } sip->dts_object = dmp->dm_name; sip->dts_name = idp->di_name; sip->dts_id = idp->di_id; idp->di_data = sip; idp->di_ctfp = tip->dtt_ctfp; idp->di_type = tip->dtt_type; return (idp); } const char * dt_module_modelname(dt_module_t *dmp) { if (dmp->dm_ops == &dt_modops_64) return ("64-bit"); else return ("32-bit"); } +/* ARGSUSED */ +int +dt_module_getlibid(dtrace_hdl_t *dtp, dt_module_t *dmp, const ctf_file_t *fp) +{ + int i; + + for (i = 0; i < dmp->dm_nctflibs; i++) { + if (dmp->dm_libctfp[i] == fp) + return (i); + } + + return (-1); +} + +/* ARGSUSED */ +ctf_file_t * +dt_module_getctflib(dtrace_hdl_t *dtp, dt_module_t *dmp, const char *name) +{ + int i; + + for (i = 0; i < dmp->dm_nctflibs; i++) { + if (strcmp(dmp->dm_libctfn[i], name) == 0) + return (dmp->dm_libctfp[i]); + } + + return (NULL); +} + /* * Update our module cache by adding an entry for the specified module 'name'. * We create the dt_module_t and populate it using /system/object//. * * On FreeBSD, the module name is passed as the full module file name, * including the path. */ static void #if defined(sun) dt_module_update(dtrace_hdl_t *dtp, const char *name) #else dt_module_update(dtrace_hdl_t *dtp, struct kld_file_stat *k_stat) #endif { char fname[MAXPATHLEN]; struct stat64 st; int fd, err, bits; dt_module_t *dmp; const char *s; size_t shstrs; GElf_Shdr sh; Elf_Data *dp; Elf_Scn *sp; #if defined(sun) (void) snprintf(fname, sizeof (fname), "%s/%s/object", OBJFS_ROOT, name); #else GElf_Ehdr ehdr; GElf_Phdr ph; char name[MAXPATHLEN]; uintptr_t mapbase, alignmask; int i = 0; int is_elf_obj; (void) strlcpy(name, k_stat->name, sizeof(name)); (void) strlcpy(fname, k_stat->pathname, sizeof(fname)); #endif if ((fd = open(fname, O_RDONLY)) == -1 || fstat64(fd, &st) == -1 || (dmp = dt_module_create(dtp, name)) == NULL) { dt_dprintf("failed to open %s: %s\n", fname, strerror(errno)); (void) close(fd); return; } /* * Since the module can unload out from under us (and /system/object * will return ENOENT), tell libelf to cook the entire file now and * then close the underlying file descriptor immediately. If this * succeeds, we know that we can continue safely using dmp->dm_elf. */ dmp->dm_elf = elf_begin(fd, ELF_C_READ, NULL); err = elf_cntl(dmp->dm_elf, ELF_C_FDREAD); (void) close(fd); if (dmp->dm_elf == NULL || err == -1 || elf_getshdrstrndx(dmp->dm_elf, &shstrs) == -1) { dt_dprintf("failed to load %s: %s\n", fname, elf_errmsg(elf_errno())); dt_module_destroy(dtp, dmp); return; } switch (gelf_getclass(dmp->dm_elf)) { case ELFCLASS32: dmp->dm_ops = &dt_modops_32; bits = 32; break; case ELFCLASS64: dmp->dm_ops = &dt_modops_64; bits = 64; break; default: dt_dprintf("failed to load %s: unknown ELF class\n", fname); dt_module_destroy(dtp, dmp); return; } #if defined(__FreeBSD__) mapbase = (uintptr_t)k_stat->address; gelf_getehdr(dmp->dm_elf, &ehdr); is_elf_obj = (ehdr.e_type == ET_REL); if (is_elf_obj) { dmp->dm_sec_offsets = malloc(ehdr.e_shnum * sizeof(*dmp->dm_sec_offsets)); if (dmp->dm_sec_offsets == NULL) { dt_dprintf("failed to allocate memory\n"); dt_module_destroy(dtp, dmp); return; } } #endif /* * Iterate over the section headers locating various sections of * interest and use their attributes to flesh out the dt_module_t. */ for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) continue; /* skip any malformed sections */ #if defined(__FreeBSD__) if (sh.sh_size == 0) continue; if (is_elf_obj && (sh.sh_type == SHT_PROGBITS || sh.sh_type == SHT_NOBITS)) { alignmask = sh.sh_addralign - 1; mapbase += alignmask; mapbase &= ~alignmask; sh.sh_addr = mapbase; dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr; mapbase += sh.sh_size; } #endif if (strcmp(s, ".text") == 0) { dmp->dm_text_size = sh.sh_size; dmp->dm_text_va = sh.sh_addr; } else if (strcmp(s, ".data") == 0) { dmp->dm_data_size = sh.sh_size; dmp->dm_data_va = sh.sh_addr; } else if (strcmp(s, ".bss") == 0) { dmp->dm_bss_size = sh.sh_size; dmp->dm_bss_va = sh.sh_addr; } else if (strcmp(s, ".info") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { bcopy(dp->d_buf, &dmp->dm_info, MIN(sh.sh_size, sizeof (dmp->dm_info))); } else if (strcmp(s, ".filename") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { (void) strlcpy(dmp->dm_file, dp->d_buf, sizeof (dmp->dm_file)); } } dmp->dm_flags |= DT_DM_KERNEL; #if defined(sun) dmp->dm_modid = (int)OBJFS_MODID(st.st_ino); #else /* * Include .rodata and special sections into .text. * This depends on default section layout produced by GNU ld * for ELF objects and libraries: * [Text][R/O data][R/W data][Dynamic][BSS][Non loadable] */ dmp->dm_text_size = dmp->dm_data_va - dmp->dm_text_va; #if defined(__i386__) /* * Find the first load section and figure out the relocation * offset for the symbols. The kernel module will not need * relocation, but the kernel linker modules will. */ for (i = 0; gelf_getphdr(dmp->dm_elf, i, &ph) != NULL; i++) { if (ph.p_type == PT_LOAD) { dmp->dm_reloc_offset = k_stat->address - ph.p_vaddr; break; } } #endif #endif if (dmp->dm_info.objfs_info_primary) dmp->dm_flags |= DT_DM_PRIMARY; dt_dprintf("opened %d-bit module %s (%s) [%d]\n", bits, dmp->dm_name, dmp->dm_file, dmp->dm_modid); } /* * Unload all the loaded modules and then refresh the module cache with the * latest list of loaded modules and their address ranges. */ void dtrace_update(dtrace_hdl_t *dtp) { dt_module_t *dmp; DIR *dirp; #if defined(__FreeBSD__) int fileid; #endif for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; dmp = dt_list_next(dmp)) dt_module_unload(dtp, dmp); #if defined(sun) /* * Open /system/object and attempt to create a libdtrace module for * each kernel module that is loaded on the current system. */ if (!(dtp->dt_oflags & DTRACE_O_NOSYS) && (dirp = opendir(OBJFS_ROOT)) != NULL) { struct dirent *dp; while ((dp = readdir(dirp)) != NULL) { if (dp->d_name[0] != '.') dt_module_update(dtp, dp->d_name); } (void) closedir(dirp); } #elif defined(__FreeBSD__) /* * Use FreeBSD's kernel loader interface to discover what kernel * modules are loaded and create a libdtrace module for each one. */ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { struct kld_file_stat k_stat; k_stat.version = sizeof(k_stat); if (kldstat(fileid, &k_stat) == 0) dt_module_update(dtp, &k_stat); } #endif /* * Look up all the macro identifiers and set di_id to the latest value. * This code collaborates with dt_lex.l on the use of di_id. We will * need to implement something fancier if we need to support non-ints. */ dt_idhash_lookup(dtp->dt_macros, "egid")->di_id = getegid(); dt_idhash_lookup(dtp->dt_macros, "euid")->di_id = geteuid(); dt_idhash_lookup(dtp->dt_macros, "gid")->di_id = getgid(); dt_idhash_lookup(dtp->dt_macros, "pid")->di_id = getpid(); dt_idhash_lookup(dtp->dt_macros, "pgid")->di_id = getpgid(0); dt_idhash_lookup(dtp->dt_macros, "ppid")->di_id = getppid(); #if defined(sun) dt_idhash_lookup(dtp->dt_macros, "projid")->di_id = getprojid(); #endif dt_idhash_lookup(dtp->dt_macros, "sid")->di_id = getsid(0); #if defined(sun) dt_idhash_lookup(dtp->dt_macros, "taskid")->di_id = gettaskid(); #endif dt_idhash_lookup(dtp->dt_macros, "uid")->di_id = getuid(); /* * Cache the pointers to the modules representing the base executable * and the run-time linker in the dtrace client handle. Note that on * x86 krtld is folded into unix, so if we don't find it, use unix * instead. */ dtp->dt_exec = dt_module_lookup_by_name(dtp, "genunix"); dtp->dt_rtld = dt_module_lookup_by_name(dtp, "krtld"); if (dtp->dt_rtld == NULL) dtp->dt_rtld = dt_module_lookup_by_name(dtp, "unix"); /* * If this is the first time we are initializing the module list, * remove the module for genunix from the module list and then move it * to the front of the module list. We do this so that type and symbol * queries encounter genunix and thereby optimize for the common case * in dtrace_lookup_by_name() and dtrace_lookup_by_type(), below. */ if (dtp->dt_exec != NULL && dtp->dt_cdefs == NULL && dtp->dt_ddefs == NULL) { dt_list_delete(&dtp->dt_modlist, dtp->dt_exec); dt_list_prepend(&dtp->dt_modlist, dtp->dt_exec); } } static dt_module_t * dt_module_from_object(dtrace_hdl_t *dtp, const char *object) { int err = EDT_NOMOD; dt_module_t *dmp; switch ((uintptr_t)object) { case (uintptr_t)DTRACE_OBJ_EXEC: dmp = dtp->dt_exec; break; case (uintptr_t)DTRACE_OBJ_RTLD: dmp = dtp->dt_rtld; break; case (uintptr_t)DTRACE_OBJ_CDEFS: dmp = dtp->dt_cdefs; break; case (uintptr_t)DTRACE_OBJ_DDEFS: dmp = dtp->dt_ddefs; break; default: dmp = dt_module_create(dtp, object); err = EDT_NOMEM; } if (dmp == NULL) (void) dt_set_errno(dtp, err); return (dmp); } /* * Exported interface to look up a symbol by name. We return the GElf_Sym and * complete symbol information for the matching symbol. */ int dtrace_lookup_by_name(dtrace_hdl_t *dtp, const char *object, const char *name, GElf_Sym *symp, dtrace_syminfo_t *sip) { dt_module_t *dmp; dt_ident_t *idp; uint_t n, id; GElf_Sym sym; uint_t mask = 0; /* mask of dt_module flags to match */ uint_t bits = 0; /* flag bits that must be present */ if (object != DTRACE_OBJ_EVERY && object != DTRACE_OBJ_KMODS && object != DTRACE_OBJ_UMODS) { if ((dmp = dt_module_from_object(dtp, object)) == NULL) return (-1); /* dt_errno is set for us */ if (dt_module_load(dtp, dmp) == -1) return (-1); /* dt_errno is set for us */ n = 1; } else { if (object == DTRACE_OBJ_KMODS) mask = bits = DT_DM_KERNEL; else if (object == DTRACE_OBJ_UMODS) mask = DT_DM_KERNEL; dmp = dt_list_next(&dtp->dt_modlist); n = dtp->dt_nmods; } if (symp == NULL) symp = &sym; for (; n > 0; n--, dmp = dt_list_next(dmp)) { if ((dmp->dm_flags & mask) != bits) continue; /* failed to match required attributes */ if (dt_module_load(dtp, dmp) == -1) continue; /* failed to load symbol table */ if (dmp->dm_ops->do_symname(dmp, name, symp, &id) != NULL) { if (sip != NULL) { sip->dts_object = dmp->dm_name; sip->dts_name = (const char *) dmp->dm_strtab.cts_data + symp->st_name; sip->dts_id = id; } return (0); } if (dmp->dm_extern != NULL && (idp = dt_idhash_lookup(dmp->dm_extern, name)) != NULL) { if (symp != &sym) { symp->st_name = (uintptr_t)idp->di_name; symp->st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); symp->st_other = 0; symp->st_shndx = SHN_UNDEF; symp->st_value = 0; symp->st_size = ctf_type_size(idp->di_ctfp, idp->di_type); } if (sip != NULL) { sip->dts_object = dmp->dm_name; sip->dts_name = idp->di_name; sip->dts_id = idp->di_id; } return (0); } } return (dt_set_errno(dtp, EDT_NOSYM)); } /* * Exported interface to look up a symbol by address. We return the GElf_Sym * and complete symbol information for the matching symbol. */ int dtrace_lookup_by_addr(dtrace_hdl_t *dtp, GElf_Addr addr, GElf_Sym *symp, dtrace_syminfo_t *sip) { dt_module_t *dmp; uint_t id; const dtrace_vector_t *v = dtp->dt_vector; if (v != NULL) return (v->dtv_lookup_by_addr(dtp->dt_varg, addr, symp, sip)); for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; dmp = dt_list_next(dmp)) { if (addr - dmp->dm_text_va < dmp->dm_text_size || addr - dmp->dm_data_va < dmp->dm_data_size || addr - dmp->dm_bss_va < dmp->dm_bss_size) break; } if (dmp == NULL) return (dt_set_errno(dtp, EDT_NOSYMADDR)); if (dt_module_load(dtp, dmp) == -1) return (-1); /* dt_errno is set for us */ if (symp != NULL) { if (dmp->dm_ops->do_symaddr(dmp, addr, symp, &id) == NULL) return (dt_set_errno(dtp, EDT_NOSYMADDR)); } if (sip != NULL) { sip->dts_object = dmp->dm_name; if (symp != NULL) { sip->dts_name = (const char *) dmp->dm_strtab.cts_data + symp->st_name; sip->dts_id = id; } else { sip->dts_name = NULL; sip->dts_id = 0; } } return (0); } int dtrace_lookup_by_type(dtrace_hdl_t *dtp, const char *object, const char *name, dtrace_typeinfo_t *tip) { dtrace_typeinfo_t ti; dt_module_t *dmp; int found = 0; ctf_id_t id; - uint_t n; + uint_t n, i; int justone; + ctf_file_t *fp; + char *buf, *p, *q; uint_t mask = 0; /* mask of dt_module flags to match */ uint_t bits = 0; /* flag bits that must be present */ if (object != DTRACE_OBJ_EVERY && object != DTRACE_OBJ_KMODS && object != DTRACE_OBJ_UMODS) { if ((dmp = dt_module_from_object(dtp, object)) == NULL) return (-1); /* dt_errno is set for us */ if (dt_module_load(dtp, dmp) == -1) return (-1); /* dt_errno is set for us */ n = 1; justone = 1; - } else { if (object == DTRACE_OBJ_KMODS) mask = bits = DT_DM_KERNEL; else if (object == DTRACE_OBJ_UMODS) mask = DT_DM_KERNEL; dmp = dt_list_next(&dtp->dt_modlist); n = dtp->dt_nmods; justone = 0; } if (tip == NULL) tip = &ti; for (; n > 0; n--, dmp = dt_list_next(dmp)) { if ((dmp->dm_flags & mask) != bits) continue; /* failed to match required attributes */ /* * If we can't load the CTF container, continue on to the next * module. If our search was scoped to only one module then * return immediately leaving dt_errno unmodified. */ - if (dt_module_getctf(dtp, dmp) == NULL) { + if (dt_module_hasctf(dtp, dmp) == 0) { if (justone) return (-1); continue; } /* * Look up the type in the module's CTF container. If our * match is a forward declaration tag, save this choice in * 'tip' and keep going in the hope that we will locate the * underlying structure definition. Otherwise just return. */ - if ((id = ctf_lookup_by_name(dmp->dm_ctfp, name)) != CTF_ERR) { + if (dmp->dm_pid == 0) { + id = ctf_lookup_by_name(dmp->dm_ctfp, name); + fp = dmp->dm_ctfp; + } else { + if ((p = strchr(name, '`')) != NULL) { + buf = strdup(name); + if (buf == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + p = strchr(buf, '`'); + if ((q = strchr(p + 1, '`')) != NULL) + p = q; + *p = '\0'; + fp = dt_module_getctflib(dtp, dmp, buf); + if (fp == NULL || (id = ctf_lookup_by_name(fp, + p + 1)) == CTF_ERR) + id = CTF_ERR; + free(buf); + } else { + for (i = 0; i < dmp->dm_nctflibs; i++) { + fp = dmp->dm_libctfp[i]; + id = ctf_lookup_by_name(fp, name); + if (id != CTF_ERR) + break; + } + } + } + if (id != CTF_ERR) { tip->dtt_object = dmp->dm_name; - tip->dtt_ctfp = dmp->dm_ctfp; + tip->dtt_ctfp = fp; tip->dtt_type = id; - - if (ctf_type_kind(dmp->dm_ctfp, ctf_type_resolve( - dmp->dm_ctfp, id)) != CTF_K_FORWARD) + if (ctf_type_kind(fp, ctf_type_resolve(fp, id)) != + CTF_K_FORWARD) return (0); found++; } } if (found == 0) return (dt_set_errno(dtp, EDT_NOTYPE)); return (0); } int dtrace_symbol_type(dtrace_hdl_t *dtp, const GElf_Sym *symp, const dtrace_syminfo_t *sip, dtrace_typeinfo_t *tip) { dt_module_t *dmp; tip->dtt_object = NULL; tip->dtt_ctfp = NULL; tip->dtt_type = CTF_ERR; + tip->dtt_flags = 0; if ((dmp = dt_module_lookup_by_name(dtp, sip->dts_object)) == NULL) return (dt_set_errno(dtp, EDT_NOMOD)); if (symp->st_shndx == SHN_UNDEF && dmp->dm_extern != NULL) { dt_ident_t *idp = dt_idhash_lookup(dmp->dm_extern, sip->dts_name); if (idp == NULL) return (dt_set_errno(dtp, EDT_NOSYM)); tip->dtt_ctfp = idp->di_ctfp; tip->dtt_type = idp->di_type; } else if (GELF_ST_TYPE(symp->st_info) != STT_FUNC) { if (dt_module_getctf(dtp, dmp) == NULL) return (-1); /* errno is set for us */ tip->dtt_ctfp = dmp->dm_ctfp; tip->dtt_type = ctf_lookup_by_symbol(dmp->dm_ctfp, sip->dts_id); if (tip->dtt_type == CTF_ERR) { dtp->dt_ctferr = ctf_errno(tip->dtt_ctfp); return (dt_set_errno(dtp, EDT_CTF)); } } else { tip->dtt_ctfp = DT_FPTR_CTFP(dtp); tip->dtt_type = DT_FPTR_TYPE(dtp); } tip->dtt_object = dmp->dm_name; return (0); } static dtrace_objinfo_t * dt_module_info(const dt_module_t *dmp, dtrace_objinfo_t *dto) { dto->dto_name = dmp->dm_name; dto->dto_file = dmp->dm_file; dto->dto_id = dmp->dm_modid; dto->dto_flags = 0; if (dmp->dm_flags & DT_DM_KERNEL) dto->dto_flags |= DTRACE_OBJ_F_KERNEL; if (dmp->dm_flags & DT_DM_PRIMARY) dto->dto_flags |= DTRACE_OBJ_F_PRIMARY; dto->dto_text_va = dmp->dm_text_va; dto->dto_text_size = dmp->dm_text_size; dto->dto_data_va = dmp->dm_data_va; dto->dto_data_size = dmp->dm_data_size; dto->dto_bss_va = dmp->dm_bss_va; dto->dto_bss_size = dmp->dm_bss_size; return (dto); } int dtrace_object_iter(dtrace_hdl_t *dtp, dtrace_obj_f *func, void *data) { const dt_module_t *dmp = dt_list_next(&dtp->dt_modlist); dtrace_objinfo_t dto; int rv; for (; dmp != NULL; dmp = dt_list_next(dmp)) { if ((rv = (*func)(dtp, dt_module_info(dmp, &dto), data)) != 0) return (rv); } return (0); } int dtrace_object_info(dtrace_hdl_t *dtp, const char *object, dtrace_objinfo_t *dto) { dt_module_t *dmp; if (object == DTRACE_OBJ_EVERY || object == DTRACE_OBJ_KMODS || object == DTRACE_OBJ_UMODS || dto == NULL) return (dt_set_errno(dtp, EINVAL)); if ((dmp = dt_module_from_object(dtp, object)) == NULL) return (-1); /* dt_errno is set for us */ if (dt_module_load(dtp, dmp) == -1) return (-1); /* dt_errno is set for us */ (void) dt_module_info(dmp, dto); return (0); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.h =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.h (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.h (revision 268578) @@ -1,56 +1,62 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #ifndef _DT_MODULE_H #define _DT_MODULE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include #ifdef __cplusplus extern "C" { #endif extern dt_module_t *dt_module_create(dtrace_hdl_t *, const char *); extern int dt_module_load(dtrace_hdl_t *, dt_module_t *); extern void dt_module_unload(dtrace_hdl_t *, dt_module_t *); extern void dt_module_destroy(dtrace_hdl_t *, dt_module_t *); extern dt_module_t *dt_module_lookup_by_name(dtrace_hdl_t *, const char *); extern dt_module_t *dt_module_lookup_by_ctf(dtrace_hdl_t *, ctf_file_t *); +extern int dt_module_hasctf(dtrace_hdl_t *, dt_module_t *); extern ctf_file_t *dt_module_getctf(dtrace_hdl_t *, dt_module_t *); extern dt_ident_t *dt_module_extern(dtrace_hdl_t *, dt_module_t *, const char *, const dtrace_typeinfo_t *); extern const char *dt_module_modelname(dt_module_t *); +extern int dt_module_getlibid(dtrace_hdl_t *, dt_module_t *, + const ctf_file_t *); +extern ctf_file_t *dt_module_getctflib(dtrace_hdl_t *, dt_module_t *, + const char *); #ifdef __cplusplus } #endif #endif /* _DT_MODULE_H */ Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c (revision 268578) @@ -1,1685 +1,1700 @@ /* * 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, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #if defined(sun) #include #include #endif #include #include #include #if defined(sun) #include #endif #include #include #include #include #include #include #include #define _POSIX_PTHREAD_SEMANTICS #include #undef _POSIX_PTHREAD_SEMANTICS #include #include #include #include #include #include #if !defined(sun) #include #include #endif #if defined(__i386__) #include #endif /* * Stability and versioning definitions. These #defines are used in the tables * of identifiers below to fill in the attribute and version fields associated * with each identifier. The DT_ATTR_* macros are a convenience to permit more * concise declarations of common attributes such as Stable/Stable/Common. The * DT_VERS_* macros declare the encoded integer values of all versions used so * far. DT_VERS_LATEST must correspond to the latest version value among all * versions exported by the D compiler. DT_VERS_STRING must be an ASCII string * that contains DT_VERS_LATEST within it along with any suffixes (e.g. Beta). * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, * and then add the new version to the _dtrace_versions[] array declared below. * Refer to the Solaris Dynamic Tracing Guide Stability and Versioning chapters * respectively for an explanation of these DTrace features and their values. * * NOTE: Although the DTrace versioning scheme supports the labeling and * introduction of incompatible changes (e.g. dropping an interface in a * major release), the libdtrace code does not currently support this. * All versions are assumed to strictly inherit from one another. If * we ever need to provide divergent interfaces, this will need work. */ #define DT_ATTR_STABCMN { DTRACE_STABILITY_STABLE, \ DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON } #define DT_ATTR_EVOLCMN { DTRACE_STABILITY_EVOLVING, \ DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON \ } /* * The version number should be increased for every customer visible release * of DTrace. The major number should be incremented when a fundamental * change has been made that would affect all consumers, and would reflect * sweeping changes to DTrace or the D language. The minor number should be * incremented when a change is introduced that could break scripts that had * previously worked; for example, adding a new built-in variable could break * a script which was already using that identifier. The micro number should * be changed when introducing functionality changes or major bug fixes that * do not affect backward compatibility -- this is merely to make capabilities * easily determined from the version number. Minor bugs do not require any * modification to the version number. */ #define DT_VERS_1_0 DT_VERSION_NUMBER(1, 0, 0) #define DT_VERS_1_1 DT_VERSION_NUMBER(1, 1, 0) #define DT_VERS_1_2 DT_VERSION_NUMBER(1, 2, 0) #define DT_VERS_1_2_1 DT_VERSION_NUMBER(1, 2, 1) #define DT_VERS_1_2_2 DT_VERSION_NUMBER(1, 2, 2) #define DT_VERS_1_3 DT_VERSION_NUMBER(1, 3, 0) #define DT_VERS_1_4 DT_VERSION_NUMBER(1, 4, 0) #define DT_VERS_1_4_1 DT_VERSION_NUMBER(1, 4, 1) #define DT_VERS_1_5 DT_VERSION_NUMBER(1, 5, 0) #define DT_VERS_1_6 DT_VERSION_NUMBER(1, 6, 0) #define DT_VERS_1_6_1 DT_VERSION_NUMBER(1, 6, 1) #define DT_VERS_1_6_2 DT_VERSION_NUMBER(1, 6, 2) #define DT_VERS_1_6_3 DT_VERSION_NUMBER(1, 6, 3) #define DT_VERS_1_7 DT_VERSION_NUMBER(1, 7, 0) #define DT_VERS_1_7_1 DT_VERSION_NUMBER(1, 7, 1) #define DT_VERS_1_8 DT_VERSION_NUMBER(1, 8, 0) #define DT_VERS_1_8_1 DT_VERSION_NUMBER(1, 8, 1) #define DT_VERS_1_9 DT_VERSION_NUMBER(1, 9, 0) #define DT_VERS_1_9_1 DT_VERSION_NUMBER(1, 9, 1) -#define DT_VERS_LATEST DT_VERS_1_9_1 -#define DT_VERS_STRING "Sun D 1.9.1" +#define DT_VERS_1_10 DT_VERSION_NUMBER(1, 10, 0) +#define DT_VERS_1_11 DT_VERSION_NUMBER(1, 11, 0) +#define DT_VERS_1_12 DT_VERSION_NUMBER(1, 12, 0) +#define DT_VERS_1_12_1 DT_VERSION_NUMBER(1, 12, 1) +#define DT_VERS_LATEST DT_VERS_1_12_1 +#define DT_VERS_STRING "Sun D 1.12.1" const dt_version_t _dtrace_versions[] = { DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */ DT_VERS_1_1, /* D API 1.1.0 Solaris Express 6/05 */ DT_VERS_1_2, /* D API 1.2.0 Solaris 10 Update 1 */ DT_VERS_1_2_1, /* D API 1.2.1 Solaris Express 4/06 */ DT_VERS_1_2_2, /* D API 1.2.2 Solaris Express 6/06 */ DT_VERS_1_3, /* D API 1.3 Solaris Express 10/06 */ DT_VERS_1_4, /* D API 1.4 Solaris Express 2/07 */ DT_VERS_1_4_1, /* D API 1.4.1 Solaris Express 4/07 */ DT_VERS_1_5, /* D API 1.5 Solaris Express 7/07 */ DT_VERS_1_6, /* D API 1.6 */ DT_VERS_1_6_1, /* D API 1.6.1 */ DT_VERS_1_6_2, /* D API 1.6.2 */ DT_VERS_1_6_3, /* D API 1.6.3 */ DT_VERS_1_7, /* D API 1.7 */ DT_VERS_1_7_1, /* D API 1.7.1 */ DT_VERS_1_8, /* D API 1.8 */ DT_VERS_1_8_1, /* D API 1.8.1 */ DT_VERS_1_9, /* D API 1.9 */ DT_VERS_1_9_1, /* D API 1.9.1 */ + DT_VERS_1_10, /* D API 1.10 */ + DT_VERS_1_11, /* D API 1.11 */ + DT_VERS_1_12, /* D API 1.12 */ + DT_VERS_1_12_1, /* D API 1.12.1 */ 0 }; /* * Global variables that are formatted on FreeBSD based on the kernel file name. */ #if !defined(sun) static char curthread_str[MAXPATHLEN]; static char intmtx_str[MAXPATHLEN]; static char threadmtx_str[MAXPATHLEN]; static char rwlock_str[MAXPATHLEN]; static char sxlock_str[MAXPATHLEN]; #endif /* * Table of global identifiers. This is used to populate the global identifier * hash when a new dtrace client open occurs. For more info see dt_ident.h. * The global identifiers that represent functions use the dt_idops_func ops * and specify the private data pointer as a prototype string which is parsed * when the identifier is first encountered. These prototypes look like ANSI * C function prototypes except that the special symbol "@" can be used as a * wildcard to represent a single parameter of any type (i.e. any dt_node_t). * The standard "..." notation can also be used to represent varargs. An empty * parameter list is taken to mean void (that is, no arguments are permitted). * A parameter enclosed in square brackets (e.g. "[int]") denotes an optional * argument. */ static const dt_ident_t _dtrace_globals[] = { { "alloca", DT_IDENT_FUNC, 0, DIF_SUBR_ALLOCA, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void *(size_t)" }, { "arg0", DT_IDENT_SCALAR, 0, DIF_VAR_ARG0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg1", DT_IDENT_SCALAR, 0, DIF_VAR_ARG1, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg2", DT_IDENT_SCALAR, 0, DIF_VAR_ARG2, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg3", DT_IDENT_SCALAR, 0, DIF_VAR_ARG3, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg4", DT_IDENT_SCALAR, 0, DIF_VAR_ARG4, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg5", DT_IDENT_SCALAR, 0, DIF_VAR_ARG5, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg6", DT_IDENT_SCALAR, 0, DIF_VAR_ARG6, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg7", DT_IDENT_SCALAR, 0, DIF_VAR_ARG7, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg8", DT_IDENT_SCALAR, 0, DIF_VAR_ARG8, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg9", DT_IDENT_SCALAR, 0, DIF_VAR_ARG9, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "args", DT_IDENT_ARRAY, 0, DIF_VAR_ARGS, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_args, NULL }, { "avg", DT_IDENT_AGGFUNC, 0, DTRACEAGG_AVG, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "basename", DT_IDENT_FUNC, 0, DIF_SUBR_BASENAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "bcopy", DT_IDENT_FUNC, 0, DIF_SUBR_BCOPY, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(void *, void *, size_t)" }, { "breakpoint", DT_IDENT_ACTFUNC, 0, DT_ACT_BREAKPOINT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "caller", DT_IDENT_SCALAR, 0, DIF_VAR_CALLER, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uintptr_t" }, { "chill", DT_IDENT_ACTFUNC, 0, DT_ACT_CHILL, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "cleanpath", DT_IDENT_FUNC, 0, DIF_SUBR_CLEANPATH, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "clear", DT_IDENT_ACTFUNC, 0, DT_ACT_CLEAR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "commit", DT_IDENT_ACTFUNC, 0, DT_ACT_COMMIT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "copyin", DT_IDENT_FUNC, 0, DIF_SUBR_COPYIN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void *(uintptr_t, size_t)" }, { "copyinstr", DT_IDENT_FUNC, 0, DIF_SUBR_COPYINSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(uintptr_t, [size_t])" }, { "copyinto", DT_IDENT_FUNC, 0, DIF_SUBR_COPYINTO, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(uintptr_t, size_t, void *)" }, { "copyout", DT_IDENT_FUNC, 0, DIF_SUBR_COPYOUT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(void *, uintptr_t, size_t)" }, { "copyoutstr", DT_IDENT_FUNC, 0, DIF_SUBR_COPYOUTSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(char *, uintptr_t, size_t)" }, { "count", DT_IDENT_AGGFUNC, 0, DTRACEAGG_COUNT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "curthread", DT_IDENT_SCALAR, 0, DIF_VAR_CURTHREAD, { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_COMMON }, DT_VERS_1_0, #if defined(sun) &dt_idops_type, "genunix`kthread_t *" }, #else &dt_idops_type, curthread_str }, #endif { "ddi_pathname", DT_IDENT_FUNC, 0, DIF_SUBR_DDI_PATHNAME, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "string(void *, int64_t)" }, { "denormalize", DT_IDENT_ACTFUNC, 0, DT_ACT_DENORMALIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "dirname", DT_IDENT_FUNC, 0, DIF_SUBR_DIRNAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "discard", DT_IDENT_ACTFUNC, 0, DT_ACT_DISCARD, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "epid", DT_IDENT_SCALAR, 0, DIF_VAR_EPID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, { "errno", DT_IDENT_SCALAR, 0, DIF_VAR_ERRNO, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int" }, { "execargs", DT_IDENT_SCALAR, 0, DIF_VAR_EXECARGS, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "execname", DT_IDENT_SCALAR, 0, DIF_VAR_EXECNAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "exit", DT_IDENT_ACTFUNC, 0, DT_ACT_EXIT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "freopen", DT_IDENT_ACTFUNC, 0, DT_ACT_FREOPEN, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "void(@, ...)" }, { "ftruncate", DT_IDENT_ACTFUNC, 0, DT_ACT_FTRUNCATE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "func", DT_IDENT_ACTFUNC, 0, DT_ACT_SYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_symaddr(uintptr_t)" }, { "getmajor", DT_IDENT_FUNC, 0, DIF_SUBR_GETMAJOR, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "genunix`major_t(genunix`dev_t)" }, { "getminor", DT_IDENT_FUNC, 0, DIF_SUBR_GETMINOR, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "genunix`minor_t(genunix`dev_t)" }, { "htonl", DT_IDENT_FUNC, 0, DIF_SUBR_HTONL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint32_t(uint32_t)" }, { "htonll", DT_IDENT_FUNC, 0, DIF_SUBR_HTONLL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint64_t(uint64_t)" }, { "htons", DT_IDENT_FUNC, 0, DIF_SUBR_HTONS, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint16_t(uint16_t)" }, +{ "getf", DT_IDENT_FUNC, 0, DIF_SUBR_GETF, DT_ATTR_STABCMN, DT_VERS_1_10, + &dt_idops_func, "file_t *(int)" }, { "gid", DT_IDENT_SCALAR, 0, DIF_VAR_GID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "gid_t" }, { "id", DT_IDENT_SCALAR, 0, DIF_VAR_ID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, { "index", DT_IDENT_FUNC, 0, DIF_SUBR_INDEX, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "int(const char *, const char *, [int])" }, { "inet_ntoa", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA, DT_ATTR_STABCMN, #if defined(sun) DT_VERS_1_5, &dt_idops_func, "string(ipaddr_t *)" }, #else DT_VERS_1_5, &dt_idops_func, "string(in_addr_t *)" }, #endif { "inet_ntoa6", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA6, DT_ATTR_STABCMN, #if defined(sun) DT_VERS_1_5, &dt_idops_func, "string(in6_addr_t *)" }, #else DT_VERS_1_5, &dt_idops_func, "string(struct in6_addr *)" }, #endif { "inet_ntop", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOP, DT_ATTR_STABCMN, DT_VERS_1_5, &dt_idops_func, "string(int, void *)" }, { "ipl", DT_IDENT_SCALAR, 0, DIF_VAR_IPL, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, +{ "json", DT_IDENT_FUNC, 0, DIF_SUBR_JSON, DT_ATTR_STABCMN, DT_VERS_1_11, + &dt_idops_func, "string(const char *, const char *)" }, { "jstack", DT_IDENT_ACTFUNC, 0, DT_ACT_JSTACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "lltostr", DT_IDENT_FUNC, 0, DIF_SUBR_LLTOSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(int64_t, [int])" }, { "llquantize", DT_IDENT_AGGFUNC, 0, DTRACEAGG_LLQUANTIZE, DT_ATTR_STABCMN, DT_VERS_1_7, &dt_idops_func, "void(@, int32_t, int32_t, int32_t, int32_t, ...)" }, { "lquantize", DT_IDENT_AGGFUNC, 0, DTRACEAGG_LQUANTIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, int32_t, int32_t, ...)" }, { "max", DT_IDENT_AGGFUNC, 0, DTRACEAGG_MAX, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "memref", DT_IDENT_FUNC, 0, DIF_SUBR_MEMREF, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "uintptr_t *(void *, size_t)" }, { "min", DT_IDENT_AGGFUNC, 0, DTRACEAGG_MIN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "mod", DT_IDENT_ACTFUNC, 0, DT_ACT_MOD, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_symaddr(uintptr_t)" }, { "msgdsize", DT_IDENT_FUNC, 0, DIF_SUBR_MSGDSIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "size_t(mblk_t *)" }, { "msgsize", DT_IDENT_FUNC, 0, DIF_SUBR_MSGSIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "size_t(mblk_t *)" }, #if defined(sun) { "mutex_owned", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNED, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`kmutex_t *)" }, { "mutex_owner", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "genunix`kthread_t *(genunix`kmutex_t *)" }, { "mutex_type_adaptive", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_ADAPTIVE, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`kmutex_t *)" }, { "mutex_type_spin", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_SPIN, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`kmutex_t *)" }, #else { "mutex_owned", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNED, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, { "mutex_owner", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, threadmtx_str }, { "mutex_type_adaptive", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_ADAPTIVE, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, { "mutex_type_spin", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_SPIN, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, #endif { "ntohl", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint32_t(uint32_t)" }, { "ntohll", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHLL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint64_t(uint64_t)" }, { "ntohs", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHS, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint16_t(uint16_t)" }, { "normalize", DT_IDENT_ACTFUNC, 0, DT_ACT_NORMALIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "panic", DT_IDENT_ACTFUNC, 0, DT_ACT_PANIC, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "pid", DT_IDENT_SCALAR, 0, DIF_VAR_PID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "pid_t" }, { "ppid", DT_IDENT_SCALAR, 0, DIF_VAR_PPID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "pid_t" }, { "print", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINT, DT_ATTR_STABCMN, DT_VERS_1_9, &dt_idops_func, "void(@)" }, { "printa", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTA, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "printf", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTF, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "printm", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(size_t, uintptr_t *)" }, { "printt", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(size_t, uintptr_t *)" }, { "probefunc", DT_IDENT_SCALAR, 0, DIF_VAR_PROBEFUNC, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "probemod", DT_IDENT_SCALAR, 0, DIF_VAR_PROBEMOD, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "probename", DT_IDENT_SCALAR, 0, DIF_VAR_PROBENAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "probeprov", DT_IDENT_SCALAR, 0, DIF_VAR_PROBEPROV, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "progenyof", DT_IDENT_FUNC, 0, DIF_SUBR_PROGENYOF, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "int(pid_t)" }, { "quantize", DT_IDENT_AGGFUNC, 0, DTRACEAGG_QUANTIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "raise", DT_IDENT_ACTFUNC, 0, DT_ACT_RAISE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "rand", DT_IDENT_FUNC, 0, DIF_SUBR_RAND, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "int()" }, { "rindex", DT_IDENT_FUNC, 0, DIF_SUBR_RINDEX, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "int(const char *, const char *, [int])" }, #if defined(sun) { "rw_iswriter", DT_IDENT_FUNC, 0, DIF_SUBR_RW_ISWRITER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, { "rw_read_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_READ_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, { "rw_write_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_WRITE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, #else { "rw_iswriter", DT_IDENT_FUNC, 0, DIF_SUBR_RW_ISWRITER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, { "rw_read_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_READ_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, { "rw_write_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_WRITE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, #endif { "self", DT_IDENT_PTR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "void" }, { "setopt", DT_IDENT_ACTFUNC, 0, DT_ACT_SETOPT, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "void(const char *, [const char *])" }, { "speculate", DT_IDENT_ACTFUNC, 0, DT_ACT_SPECULATE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "speculation", DT_IDENT_FUNC, 0, DIF_SUBR_SPECULATION, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "int()" }, { "stack", DT_IDENT_ACTFUNC, 0, DT_ACT_STACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "stackdepth", DT_IDENT_SCALAR, 0, DIF_VAR_STACKDEPTH, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint32_t" }, { "stddev", DT_IDENT_AGGFUNC, 0, DTRACEAGG_STDDEV, DT_ATTR_STABCMN, DT_VERS_1_6, &dt_idops_func, "void(@)" }, { "stop", DT_IDENT_ACTFUNC, 0, DT_ACT_STOP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "strchr", DT_IDENT_FUNC, 0, DIF_SUBR_STRCHR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, char)" }, { "strlen", DT_IDENT_FUNC, 0, DIF_SUBR_STRLEN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "size_t(const char *)" }, { "strjoin", DT_IDENT_FUNC, 0, DIF_SUBR_STRJOIN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *, const char *)" }, { "strrchr", DT_IDENT_FUNC, 0, DIF_SUBR_STRRCHR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, char)" }, { "strstr", DT_IDENT_FUNC, 0, DIF_SUBR_STRSTR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, const char *)" }, { "strtok", DT_IDENT_FUNC, 0, DIF_SUBR_STRTOK, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, const char *)" }, +{ "strtoll", DT_IDENT_FUNC, 0, DIF_SUBR_STRTOLL, DT_ATTR_STABCMN, DT_VERS_1_11, + &dt_idops_func, "int64_t(const char *, [int])" }, { "substr", DT_IDENT_FUNC, 0, DIF_SUBR_SUBSTR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, int, [int])" }, { "sum", DT_IDENT_AGGFUNC, 0, DTRACEAGG_SUM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, #if !defined(sun) { "sx_isexclusive", DT_IDENT_FUNC, 0, DIF_SUBR_SX_ISEXCLUSIVE, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, { "sx_shared_held", DT_IDENT_FUNC, 0, DIF_SUBR_SX_SHARED_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, { "sx_exclusive_held", DT_IDENT_FUNC, 0, DIF_SUBR_SX_EXCLUSIVE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, #endif { "sym", DT_IDENT_ACTFUNC, 0, DT_ACT_SYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_symaddr(uintptr_t)" }, { "system", DT_IDENT_ACTFUNC, 0, DT_ACT_SYSTEM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "this", DT_IDENT_PTR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "void" }, { "tid", DT_IDENT_SCALAR, 0, DIF_VAR_TID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "id_t" }, { "timestamp", DT_IDENT_SCALAR, 0, DIF_VAR_TIMESTAMP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint64_t" }, { "tolower", DT_IDENT_FUNC, 0, DIF_SUBR_TOLOWER, DT_ATTR_STABCMN, DT_VERS_1_8, &dt_idops_func, "string(const char *)" }, { "toupper", DT_IDENT_FUNC, 0, DIF_SUBR_TOUPPER, DT_ATTR_STABCMN, DT_VERS_1_8, &dt_idops_func, "string(const char *)" }, { "trace", DT_IDENT_ACTFUNC, 0, DT_ACT_TRACE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "tracemem", DT_IDENT_ACTFUNC, 0, DT_ACT_TRACEMEM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, size_t, ...)" }, { "trunc", DT_IDENT_ACTFUNC, 0, DT_ACT_TRUNC, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "typeref", DT_IDENT_FUNC, 0, DIF_SUBR_TYPEREF, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "uintptr_t *(void *, size_t, string, size_t)" }, #if defined(sun) { "uaddr", DT_IDENT_ACTFUNC, 0, DT_ACT_UADDR, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, #endif { "ucaller", DT_IDENT_SCALAR, 0, DIF_VAR_UCALLER, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_type, "uint64_t" }, #if defined(sun) { "ufunc", DT_IDENT_ACTFUNC, 0, DT_ACT_USYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, #endif { "uid", DT_IDENT_SCALAR, 0, DIF_VAR_UID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uid_t" }, #if defined(sun) { "umod", DT_IDENT_ACTFUNC, 0, DT_ACT_UMOD, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, #endif { "uregs", DT_IDENT_ARRAY, 0, DIF_VAR_UREGS, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_regs, NULL }, { "ustack", DT_IDENT_ACTFUNC, 0, DT_ACT_USTACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "ustackdepth", DT_IDENT_SCALAR, 0, DIF_VAR_USTACKDEPTH, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_type, "uint32_t" }, #if defined(sun) { "usym", DT_IDENT_ACTFUNC, 0, DT_ACT_USYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, #endif { "vtimestamp", DT_IDENT_SCALAR, 0, DIF_VAR_VTIMESTAMP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint64_t" }, { "walltimestamp", DT_IDENT_SCALAR, 0, DIF_VAR_WALLTIMESTAMP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, #if defined(sun) { "zonename", DT_IDENT_SCALAR, 0, DIF_VAR_ZONENAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, #endif #if !defined(sun) { "cpu", DT_IDENT_SCALAR, 0, DIF_VAR_CPU, DT_ATTR_STABCMN, DT_VERS_1_6_3, &dt_idops_type, "int" }, #endif { NULL, 0, 0, 0, { 0, 0, 0 }, 0, NULL, NULL } }; /* * Tables of ILP32 intrinsic integer and floating-point type templates to use * to populate the dynamic "C" CTF type container. */ static const dt_intrinsic_t _dtrace_intrinsics_32[] = { { "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER }, { "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "unsigned", { 0, 0, 32 }, CTF_K_INTEGER }, { "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER }, { "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER }, { "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER }, { "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT }, { "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT }, { "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT }, { "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT }, { "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT }, { "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT }, { "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT }, { "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT }, { "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT }, { NULL, { 0, 0, 0 }, 0 } }; /* * Tables of LP64 intrinsic integer and floating-point type templates to use * to populate the dynamic "C" CTF type container. */ static const dt_intrinsic_t _dtrace_intrinsics_64[] = { { "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER }, { "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "unsigned", { 0, 0, 32 }, CTF_K_INTEGER }, { "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER }, { "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long", { 0, 0, 64 }, CTF_K_INTEGER }, { "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER }, { "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER }, { "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT }, { "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT }, { "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT }, { "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT }, { "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT }, { "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT }, { "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT }, { "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT }, { "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT }, { NULL, { 0, 0, 0 }, 0 } }; /* * Tables of ILP32 typedefs to use to populate the dynamic "D" CTF container. * These aliases ensure that D definitions can use typical names. */ static const dt_typedef_t _dtrace_typedefs_32[] = { { "char", "int8_t" }, { "short", "int16_t" }, { "int", "int32_t" }, { "long long", "int64_t" }, { "int", "intptr_t" }, { "int", "ssize_t" }, { "unsigned char", "uint8_t" }, { "unsigned short", "uint16_t" }, { "unsigned", "uint32_t" }, { "unsigned long long", "uint64_t" }, { "unsigned char", "uchar_t" }, { "unsigned short", "ushort_t" }, { "unsigned", "uint_t" }, { "unsigned long", "ulong_t" }, { "unsigned long long", "u_longlong_t" }, { "int", "ptrdiff_t" }, { "unsigned", "uintptr_t" }, { "unsigned", "size_t" }, { "long", "id_t" }, { "long", "pid_t" }, { NULL, NULL } }; /* * Tables of LP64 typedefs to use to populate the dynamic "D" CTF container. * These aliases ensure that D definitions can use typical names. */ static const dt_typedef_t _dtrace_typedefs_64[] = { { "char", "int8_t" }, { "short", "int16_t" }, { "int", "int32_t" }, { "long", "int64_t" }, { "long", "intptr_t" }, { "long", "ssize_t" }, { "unsigned char", "uint8_t" }, { "unsigned short", "uint16_t" }, { "unsigned", "uint32_t" }, { "unsigned long", "uint64_t" }, { "unsigned char", "uchar_t" }, { "unsigned short", "ushort_t" }, { "unsigned", "uint_t" }, { "unsigned long", "ulong_t" }, { "unsigned long long", "u_longlong_t" }, { "long", "ptrdiff_t" }, { "unsigned long", "uintptr_t" }, { "unsigned long", "size_t" }, { "int", "id_t" }, { "int", "pid_t" }, { NULL, NULL } }; /* * Tables of ILP32 integer type templates used to populate the dtp->dt_ints[] * cache when a new dtrace client open occurs. Values are set by dtrace_open(). */ static const dt_intdesc_t _dtrace_ints_32[] = { { "int", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned int", NULL, CTF_ERR, 0xffffffffULL }, { "long", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned long", NULL, CTF_ERR, 0xffffffffULL }, { "long long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long long", NULL, CTF_ERR, 0xffffffffffffffffULL } }; /* * Tables of LP64 integer type templates used to populate the dtp->dt_ints[] * cache when a new dtrace client open occurs. Values are set by dtrace_open(). */ static const dt_intdesc_t _dtrace_ints_64[] = { { "int", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned int", NULL, CTF_ERR, 0xffffffffULL }, { "long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long", NULL, CTF_ERR, 0xffffffffffffffffULL }, { "long long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long long", NULL, CTF_ERR, 0xffffffffffffffffULL } }; /* * Table of macro variable templates used to populate the macro identifier hash * when a new dtrace client open occurs. Values are set by dtrace_update(). */ static const dt_ident_t _dtrace_macros[] = { { "egid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "euid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "gid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "pid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "pgid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "ppid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "projid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "sid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "taskid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "target", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "uid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { NULL, 0, 0, 0, { 0, 0, 0 }, 0 } }; /* * Hard-wired definition string to be compiled and cached every time a new * DTrace library handle is initialized. This string should only be used to * contain definitions that should be present regardless of DTRACE_O_NOLIBS. */ static const char _dtrace_hardwire[] = "\ inline long NULL = 0; \n\ #pragma D binding \"1.0\" NULL\n\ "; /* * Default DTrace configuration to use when opening libdtrace DTRACE_O_NODEV. * If DTRACE_O_NODEV is not set, we load the configuration from the kernel. * The use of CTF_MODEL_NATIVE is more subtle than it might appear: we are * relying on the fact that when running dtrace(1M), isaexec will invoke the * binary with the same bitness as the kernel, which is what we want by default * when generating our DIF. The user can override the choice using oflags. */ static const dtrace_conf_t _dtrace_conf = { DIF_VERSION, /* dtc_difversion */ DIF_DIR_NREGS, /* dtc_difintregs */ DIF_DTR_NREGS, /* dtc_diftupregs */ CTF_MODEL_NATIVE /* dtc_ctfmodel */ }; const dtrace_attribute_t _dtrace_maxattr = { DTRACE_STABILITY_MAX, DTRACE_STABILITY_MAX, DTRACE_CLASS_MAX }; const dtrace_attribute_t _dtrace_defattr = { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }; const dtrace_attribute_t _dtrace_symattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_attribute_t _dtrace_typattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_attribute_t _dtrace_prvattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_pattr_t _dtrace_prvdesc = { { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, }; #if defined(sun) const char *_dtrace_defcpp = "/usr/ccs/lib/cpp"; /* default cpp(1) to invoke */ const char *_dtrace_defld = "/usr/ccs/bin/ld"; /* default ld(1) to invoke */ #else const char *_dtrace_defcpp = "cpp"; /* default cpp(1) to invoke */ const char *_dtrace_defld = "ld"; /* default ld(1) to invoke */ #endif const char *_dtrace_libdir = "/usr/lib/dtrace"; /* default library directory */ #if defined(sun) const char *_dtrace_provdir = "/dev/dtrace/provider"; /* provider directory */ #else const char *_dtrace_provdir = "/dev/dtrace"; /* provider directory */ #endif int _dtrace_strbuckets = 211; /* default number of hash buckets (prime) */ int _dtrace_intbuckets = 256; /* default number of integer buckets (Pof2) */ uint_t _dtrace_strsize = 256; /* default size of string intrinsic type */ uint_t _dtrace_stkindent = 14; /* default whitespace indent for stack/ustack */ uint_t _dtrace_pidbuckets = 64; /* default number of pid hash buckets */ uint_t _dtrace_pidlrulim = 8; /* default number of pid handles to cache */ size_t _dtrace_bufsize = 512; /* default dt_buf_create() size */ int _dtrace_argmax = 32; /* default maximum number of probe arguments */ int _dtrace_debug = 0; /* debug messages enabled (off) */ const char *const _dtrace_version = DT_VERS_STRING; /* API version string */ int _dtrace_rdvers = RD_VERSION; /* rtld_db feature version */ typedef struct dt_fdlist { int *df_fds; /* array of provider driver file descriptors */ uint_t df_ents; /* number of valid elements in df_fds[] */ uint_t df_size; /* size of df_fds[] */ } dt_fdlist_t; #if defined(sun) #pragma init(_dtrace_init) #else void _dtrace_init(void) __attribute__ ((constructor)); #endif void _dtrace_init(void) { _dtrace_debug = getenv("DTRACE_DEBUG") != NULL; for (; _dtrace_rdvers > 0; _dtrace_rdvers--) { if (rd_init(_dtrace_rdvers) == RD_OK) break; } #if defined(__i386__) /* make long doubles 64 bits -sson */ (void) fpsetprec(FP_PE); #endif } static dtrace_hdl_t * set_open_errno(dtrace_hdl_t *dtp, int *errp, int err) { if (dtp != NULL) dtrace_close(dtp); if (errp != NULL) *errp = err; return (NULL); } static void dt_provmod_open(dt_provmod_t **provmod, dt_fdlist_t *dfp) { dt_provmod_t *prov; char path[PATH_MAX]; int fd; #if defined(sun) struct dirent *dp, *ep; DIR *dirp; if ((dirp = opendir(_dtrace_provdir)) == NULL) return; /* failed to open directory; just skip it */ ep = alloca(sizeof (struct dirent) + PATH_MAX + 1); bzero(ep, sizeof (struct dirent) + PATH_MAX + 1); while (readdir_r(dirp, ep, &dp) == 0 && dp != NULL) { if (dp->d_name[0] == '.') continue; /* skip "." and ".." */ if (dfp->df_ents == dfp->df_size) { uint_t size = dfp->df_size ? dfp->df_size * 2 : 16; int *fds = realloc(dfp->df_fds, size * sizeof (int)); if (fds == NULL) break; /* skip the rest of this directory */ dfp->df_fds = fds; dfp->df_size = size; } (void) snprintf(path, sizeof (path), "%s/%s", _dtrace_provdir, dp->d_name); if ((fd = open(path, O_RDONLY)) == -1) continue; /* failed to open driver; just skip it */ if (((prov = malloc(sizeof (dt_provmod_t))) == NULL) || (prov->dp_name = malloc(strlen(dp->d_name) + 1)) == NULL) { free(prov); (void) close(fd); break; } (void) strcpy(prov->dp_name, dp->d_name); prov->dp_next = *provmod; *provmod = prov; dt_dprintf("opened provider %s\n", dp->d_name); dfp->df_fds[dfp->df_ents++] = fd; } (void) closedir(dirp); #else char *p; char *p1; char *p_providers = NULL; int error; size_t len = 0; /* * Loop to allocate/reallocate memory for the string of provider * names and retry: */ while(1) { /* * The first time around, get the string length. The next time, * hopefully we've allocated enough memory. */ error = sysctlbyname("debug.dtrace.providers",p_providers,&len,NULL,0); if (len == 0) /* No providers? That's strange. Where's dtrace? */ break; else if (error == 0 && p_providers == NULL) { /* * Allocate the initial memory which should be enough * unless another provider loads before we have * time to go back and get the string. */ if ((p_providers = malloc(len)) == NULL) /* How do we report errors here? */ return; } else if (error == -1 && errno == ENOMEM) { /* * The current buffer isn't large enough, so * reallocate it. We normally won't need to do this * because providers aren't being loaded all the time. */ if ((p = realloc(p_providers,len)) == NULL) /* How do we report errors here? */ return; p_providers = p; } else break; } /* Check if we got a string of provider names: */ if (error == 0 && len > 0 && p_providers != NULL) { p = p_providers; /* * Parse the string containing the space separated * provider names. */ while ((p1 = strsep(&p," ")) != NULL) { if (dfp->df_ents == dfp->df_size) { uint_t size = dfp->df_size ? dfp->df_size * 2 : 16; int *fds = realloc(dfp->df_fds, size * sizeof (int)); if (fds == NULL) break; dfp->df_fds = fds; dfp->df_size = size; } (void) snprintf(path, sizeof (path), "/dev/dtrace/%s", p1); if ((fd = open(path, O_RDONLY)) == -1) continue; /* failed to open driver; just skip it */ if (((prov = malloc(sizeof (dt_provmod_t))) == NULL) || (prov->dp_name = malloc(strlen(p1) + 1)) == NULL) { free(prov); (void) close(fd); break; } (void) strcpy(prov->dp_name, p1); prov->dp_next = *provmod; *provmod = prov; dt_dprintf("opened provider %s\n", p1); dfp->df_fds[dfp->df_ents++] = fd; } } if (p_providers != NULL) free(p_providers); #endif } static void dt_provmod_destroy(dt_provmod_t **provmod) { dt_provmod_t *next, *current; for (current = *provmod; current != NULL; current = next) { next = current->dp_next; free(current->dp_name); free(current); } *provmod = NULL; } #if defined(sun) static const char * dt_get_sysinfo(int cmd, char *buf, size_t len) { ssize_t rv = sysinfo(cmd, buf, len); char *p = buf; if (rv < 0 || rv > len) (void) snprintf(buf, len, "%s", "Unknown"); while ((p = strchr(p, '.')) != NULL) *p++ = '_'; return (buf); } #endif static dtrace_hdl_t * dt_vopen(int version, int flags, int *errp, const dtrace_vector_t *vector, void *arg) { dtrace_hdl_t *dtp = NULL; int dtfd = -1, ftfd = -1, fterr = 0; dtrace_prog_t *pgp; dt_module_t *dmp; dt_provmod_t *provmod = NULL; int i, err; struct rlimit rl; const dt_intrinsic_t *dinp; const dt_typedef_t *dtyp; const dt_ident_t *idp; dtrace_typeinfo_t dtt; ctf_funcinfo_t ctc; ctf_arinfo_t ctr; dt_fdlist_t df = { NULL, 0, 0 }; char isadef[32], utsdef[32]; char s1[64], s2[64]; if (version <= 0) return (set_open_errno(dtp, errp, EINVAL)); if (version > DTRACE_VERSION) return (set_open_errno(dtp, errp, EDT_VERSION)); if (version < DTRACE_VERSION) { /* * Currently, increasing the library version number is used to * denote a binary incompatible change. That is, a consumer * of the library cannot run on a version of the library with * a higher DTRACE_VERSION number than the consumer compiled * against. Once the library API has been committed to, * backwards binary compatibility will be required; at that * time, this check should change to return EDT_OVERSION only * if the specified version number is less than the version * number at the time of interface commitment. */ return (set_open_errno(dtp, errp, EDT_OVERSION)); } if (flags & ~DTRACE_O_MASK) return (set_open_errno(dtp, errp, EINVAL)); if ((flags & DTRACE_O_LP64) && (flags & DTRACE_O_ILP32)) return (set_open_errno(dtp, errp, EINVAL)); if (vector == NULL && arg != NULL) return (set_open_errno(dtp, errp, EINVAL)); if (elf_version(EV_CURRENT) == EV_NONE) return (set_open_errno(dtp, errp, EDT_ELFVERSION)); if (vector != NULL || (flags & DTRACE_O_NODEV)) goto alloc; /* do not attempt to open dtrace device */ /* * Before we get going, crank our limit on file descriptors up to the * hard limit. This is to allow for the fact that libproc keeps file * descriptors to objects open for the lifetime of the proc handle; * without raising our hard limit, we would have an acceptably small * bound on the number of processes that we could concurrently * instrument with the pid provider. */ if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { rl.rlim_cur = rl.rlim_max; (void) setrlimit(RLIMIT_NOFILE, &rl); } /* * Get the device path of each of the providers. We hold them open * in the df.df_fds list until we open the DTrace driver itself, * allowing us to see all of the probes provided on this system. Once * we have the DTrace driver open, we can safely close all the providers * now that they have registered with the framework. */ dt_provmod_open(&provmod, &df); dtfd = open("/dev/dtrace/dtrace", O_RDWR); err = errno; /* save errno from opening dtfd */ #if defined(__FreeBSD__) /* * Automatically load the 'dtraceall' module if we couldn't open the * char device. */ if (err == ENOENT && modfind("dtraceall") < 0) { kldload("dtraceall"); /* ignore the error */ dtfd = open("/dev/dtrace/dtrace", O_RDWR); err = errno; } #endif #if defined(sun) ftfd = open("/dev/dtrace/provider/fasttrap", O_RDWR); #else ftfd = open("/dev/dtrace/fasttrap", O_RDWR); #endif fterr = ftfd == -1 ? errno : 0; /* save errno from open ftfd */ while (df.df_ents-- != 0) (void) close(df.df_fds[df.df_ents]); free(df.df_fds); /* * If we failed to open the dtrace device, fail dtrace_open(). * We convert some kernel errnos to custom libdtrace errnos to * improve the resulting message from the usual strerror(). */ if (dtfd == -1) { dt_provmod_destroy(&provmod); switch (err) { case ENOENT: err = EDT_NOENT; break; case EBUSY: err = EDT_BUSY; break; case EACCES: err = EDT_ACCESS; break; } return (set_open_errno(dtp, errp, err)); } (void) fcntl(dtfd, F_SETFD, FD_CLOEXEC); (void) fcntl(ftfd, F_SETFD, FD_CLOEXEC); alloc: if ((dtp = malloc(sizeof (dtrace_hdl_t))) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); bzero(dtp, sizeof (dtrace_hdl_t)); dtp->dt_oflags = flags; #if defined(sun) dtp->dt_prcmode = DT_PROC_STOP_PREINIT; #else dtp->dt_prcmode = DT_PROC_STOP_MAIN; #endif dtp->dt_linkmode = DT_LINK_KERNEL; dtp->dt_linktype = DT_LTYP_ELF; dtp->dt_xlatemode = DT_XL_STATIC; dtp->dt_stdcmode = DT_STDC_XA; + dtp->dt_encoding = DT_ENCODING_UNSET; dtp->dt_version = version; dtp->dt_fd = dtfd; dtp->dt_ftfd = ftfd; dtp->dt_fterr = fterr; dtp->dt_cdefs_fd = -1; dtp->dt_ddefs_fd = -1; #if defined(sun) dtp->dt_stdout_fd = -1; #else dtp->dt_freopen_fp = NULL; #endif dtp->dt_modbuckets = _dtrace_strbuckets; dtp->dt_mods = calloc(dtp->dt_modbuckets, sizeof (dt_module_t *)); dtp->dt_provbuckets = _dtrace_strbuckets; dtp->dt_provs = calloc(dtp->dt_provbuckets, sizeof (dt_provider_t *)); dt_proc_hash_create(dtp); dtp->dt_vmax = DT_VERS_LATEST; dtp->dt_cpp_path = strdup(_dtrace_defcpp); dtp->dt_cpp_argv = malloc(sizeof (char *)); dtp->dt_cpp_argc = 1; dtp->dt_cpp_args = 1; dtp->dt_ld_path = strdup(_dtrace_defld); dtp->dt_provmod = provmod; dtp->dt_vector = vector; dtp->dt_varg = arg; dt_dof_init(dtp); (void) uname(&dtp->dt_uts); if (dtp->dt_mods == NULL || dtp->dt_provs == NULL || dtp->dt_procs == NULL || dtp->dt_ld_path == NULL || dtp->dt_cpp_path == NULL || dtp->dt_cpp_argv == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); for (i = 0; i < DTRACEOPT_MAX; i++) dtp->dt_options[i] = DTRACEOPT_UNSET; dtp->dt_cpp_argv[0] = (char *)strbasename(dtp->dt_cpp_path); #if defined(sun) (void) snprintf(isadef, sizeof (isadef), "-D__SUNW_D_%u", (uint_t)(sizeof (void *) * NBBY)); (void) snprintf(utsdef, sizeof (utsdef), "-D__%s_%s", dt_get_sysinfo(SI_SYSNAME, s1, sizeof (s1)), dt_get_sysinfo(SI_RELEASE, s2, sizeof (s2))); if (dt_cpp_add_arg(dtp, "-D__sun") == NULL || dt_cpp_add_arg(dtp, "-D__unix") == NULL || dt_cpp_add_arg(dtp, "-D__SVR4") == NULL || dt_cpp_add_arg(dtp, "-D__SUNW_D=1") == NULL || dt_cpp_add_arg(dtp, isadef) == NULL || dt_cpp_add_arg(dtp, utsdef) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); #endif if (flags & DTRACE_O_NODEV) bcopy(&_dtrace_conf, &dtp->dt_conf, sizeof (_dtrace_conf)); else if (dt_ioctl(dtp, DTRACEIOC_CONF, &dtp->dt_conf) != 0) return (set_open_errno(dtp, errp, errno)); if (flags & DTRACE_O_LP64) dtp->dt_conf.dtc_ctfmodel = CTF_MODEL_LP64; else if (flags & DTRACE_O_ILP32) dtp->dt_conf.dtc_ctfmodel = CTF_MODEL_ILP32; #ifdef __sparc /* * On SPARC systems, __sparc is always defined for * and __sparcv9 is defined if we are doing a 64-bit compile. */ if (dt_cpp_add_arg(dtp, "-D__sparc") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64 && dt_cpp_add_arg(dtp, "-D__sparcv9") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); #endif #if defined(sun) #ifdef __x86 /* * On x86 systems, __i386 is defined for for 32-bit * compiles and __amd64 is defined for 64-bit compiles. Unlike SPARC, * they are defined exclusive of one another (see PSARC 2004/619). */ if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) { if (dt_cpp_add_arg(dtp, "-D__amd64") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } else { if (dt_cpp_add_arg(dtp, "-D__i386") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } #endif #else #if defined(__amd64__) || defined(__i386__) if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) { if (dt_cpp_add_arg(dtp, "-m64") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } else { if (dt_cpp_add_arg(dtp, "-m32") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } #endif #endif if (dtp->dt_conf.dtc_difversion < DIF_VERSION) return (set_open_errno(dtp, errp, EDT_DIFVERS)); if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_ILP32) bcopy(_dtrace_ints_32, dtp->dt_ints, sizeof (_dtrace_ints_32)); else bcopy(_dtrace_ints_64, dtp->dt_ints, sizeof (_dtrace_ints_64)); /* * On FreeBSD the kernel module name can't be hard-coded. The * 'kern.bootfile' sysctl value tells us exactly which file is being * used as the kernel. */ #if !defined(sun) { char bootfile[MAXPATHLEN]; char *p; int i; size_t len = sizeof(bootfile); /* This call shouldn't fail, but use a default just in case. */ if (sysctlbyname("kern.bootfile", bootfile, &len, NULL, 0) != 0) strlcpy(bootfile, "kernel", sizeof(bootfile)); if ((p = strrchr(bootfile, '/')) != NULL) p++; else p = bootfile; /* * Format the global variables based on the kernel module name. */ snprintf(curthread_str, sizeof(curthread_str), "%s`struct thread *",p); snprintf(intmtx_str, sizeof(intmtx_str), "int(%s`struct mtx *)",p); snprintf(threadmtx_str, sizeof(threadmtx_str), "struct thread *(%s`struct mtx *)",p); snprintf(rwlock_str, sizeof(rwlock_str), "int(%s`struct rwlock *)",p); snprintf(sxlock_str, sizeof(sxlock_str), "int(%s`struct sxlock *)",p); } #endif dtp->dt_macros = dt_idhash_create("macro", NULL, 0, UINT_MAX); dtp->dt_aggs = dt_idhash_create("aggregation", NULL, DTRACE_AGGVARIDNONE + 1, UINT_MAX); dtp->dt_globals = dt_idhash_create("global", _dtrace_globals, DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); dtp->dt_tls = dt_idhash_create("thread local", NULL, DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); if (dtp->dt_macros == NULL || dtp->dt_aggs == NULL || dtp->dt_globals == NULL || dtp->dt_tls == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); /* * Populate the dt_macros identifier hash table by hand: we can't use * the dt_idhash_populate() mechanism because we're not yet compiling * and dtrace_update() needs to immediately reference these idents. */ for (idp = _dtrace_macros; idp->di_name != NULL; idp++) { if (dt_idhash_insert(dtp->dt_macros, idp->di_name, idp->di_kind, idp->di_flags, idp->di_id, idp->di_attr, idp->di_vers, idp->di_ops ? idp->di_ops : &dt_idops_thaw, idp->di_iarg, 0) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } /* * Update the module list using /system/object and load the values for * the macro variable definitions according to the current process. */ dtrace_update(dtp); /* * Select the intrinsics and typedefs we want based on the data model. * The intrinsics are under "C". The typedefs are added under "D". */ if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_ILP32) { dinp = _dtrace_intrinsics_32; dtyp = _dtrace_typedefs_32; } else { dinp = _dtrace_intrinsics_64; dtyp = _dtrace_typedefs_64; } /* * Create a dynamic CTF container under the "C" scope for intrinsic * types and types defined in ANSI-C header files that are included. */ if ((dmp = dtp->dt_cdefs = dt_module_create(dtp, "C")) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if ((dmp->dm_ctfp = ctf_create(&dtp->dt_ctferr)) == NULL) return (set_open_errno(dtp, errp, EDT_CTF)); dt_dprintf("created CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); (void) ctf_setmodel(dmp->dm_ctfp, dtp->dt_conf.dtc_ctfmodel); ctf_setspecific(dmp->dm_ctfp, dmp); dmp->dm_flags = DT_DM_LOADED; /* fake up loaded bit */ dmp->dm_modid = -1; /* no module ID */ /* * Fill the dynamic "C" CTF container with all of the intrinsic * integer and floating-point types appropriate for this data model. */ for (; dinp->din_name != NULL; dinp++) { if (dinp->din_kind == CTF_K_INTEGER) { err = ctf_add_integer(dmp->dm_ctfp, CTF_ADD_ROOT, dinp->din_name, &dinp->din_data); } else { err = ctf_add_float(dmp->dm_ctfp, CTF_ADD_ROOT, dinp->din_name, &dinp->din_data); } if (err == CTF_ERR) { dt_dprintf("failed to add %s to C container: %s\n", dinp->din_name, ctf_errmsg( ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } } if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed to update C container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Add intrinsic pointer types that are needed to initialize printf * format dictionary types (see table in dt_printf.c). */ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "void")); (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "char")); (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "int")); if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed to update C container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Create a dynamic CTF container under the "D" scope for types that * are defined by the D program itself or on-the-fly by the D compiler. * The "D" CTF container is a child of the "C" CTF container. */ if ((dmp = dtp->dt_ddefs = dt_module_create(dtp, "D")) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if ((dmp->dm_ctfp = ctf_create(&dtp->dt_ctferr)) == NULL) return (set_open_errno(dtp, errp, EDT_CTF)); dt_dprintf("created CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); (void) ctf_setmodel(dmp->dm_ctfp, dtp->dt_conf.dtc_ctfmodel); ctf_setspecific(dmp->dm_ctfp, dmp); dmp->dm_flags = DT_DM_LOADED; /* fake up loaded bit */ dmp->dm_modid = -1; /* no module ID */ if (ctf_import(dmp->dm_ctfp, dtp->dt_cdefs->dm_ctfp) == CTF_ERR) { dt_dprintf("failed to import D parent container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Fill the dynamic "D" CTF container with all of the built-in typedefs * that we need to use for our D variable and function definitions. * This ensures that basic inttypes.h names are always available to us. */ for (; dtyp->dty_src != NULL; dtyp++) { if (ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, dtyp->dty_dst, ctf_lookup_by_name(dmp->dm_ctfp, dtyp->dty_src)) == CTF_ERR) { dt_dprintf("failed to add typedef %s %s to D " "container: %s", dtyp->dty_src, dtyp->dty_dst, ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } } /* * Insert a CTF ID corresponding to a pointer to a type of kind * CTF_K_FUNCTION we can use in the compiler for function pointers. * CTF treats all function pointers as "int (*)()" so we only need one. */ ctc.ctc_return = ctf_lookup_by_name(dmp->dm_ctfp, "int"); ctc.ctc_argc = 0; ctc.ctc_flags = 0; dtp->dt_type_func = ctf_add_function(dmp->dm_ctfp, CTF_ADD_ROOT, &ctc, NULL); dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, dtp->dt_type_func); /* * We also insert CTF definitions for the special D intrinsic types * string and into the D container. The string type is added * as a typedef of char[n]. The type is an alias for void. * We compare types to these special CTF ids throughout the compiler. */ ctr.ctr_contents = ctf_lookup_by_name(dmp->dm_ctfp, "char"); ctr.ctr_index = ctf_lookup_by_name(dmp->dm_ctfp, "long"); ctr.ctr_nelems = _dtrace_strsize; dtp->dt_type_str = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "string", ctf_add_array(dmp->dm_ctfp, CTF_ADD_ROOT, &ctr)); dtp->dt_type_dyn = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_stack = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "stack", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_symaddr = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "_symaddr", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_usymaddr = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "_usymaddr", ctf_lookup_by_name(dmp->dm_ctfp, "void")); if (dtp->dt_type_func == CTF_ERR || dtp->dt_type_fptr == CTF_ERR || dtp->dt_type_str == CTF_ERR || dtp->dt_type_dyn == CTF_ERR || dtp->dt_type_stack == CTF_ERR || dtp->dt_type_symaddr == CTF_ERR || dtp->dt_type_usymaddr == CTF_ERR) { dt_dprintf("failed to add intrinsic to D container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed update D container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Initialize the integer description table used to convert integer * constants to the appropriate types. Refer to the comments above * dt_node_int() for a complete description of how this table is used. */ for (i = 0; i < sizeof (dtp->dt_ints) / sizeof (dtp->dt_ints[0]); i++) { if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY, dtp->dt_ints[i].did_name, &dtt) != 0) { dt_dprintf("failed to lookup integer type %s: %s\n", dtp->dt_ints[i].did_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); return (set_open_errno(dtp, errp, dtp->dt_errno)); } dtp->dt_ints[i].did_ctfp = dtt.dtt_ctfp; dtp->dt_ints[i].did_type = dtt.dtt_type; } /* * Now that we've created the "C" and "D" containers, move them to the * start of the module list so that these types and symbols are found * first (for stability) when iterating through the module list. */ dt_list_delete(&dtp->dt_modlist, dtp->dt_ddefs); dt_list_prepend(&dtp->dt_modlist, dtp->dt_ddefs); dt_list_delete(&dtp->dt_modlist, dtp->dt_cdefs); dt_list_prepend(&dtp->dt_modlist, dtp->dt_cdefs); if (dt_pfdict_create(dtp) == -1) return (set_open_errno(dtp, errp, dtp->dt_errno)); /* * If we are opening libdtrace DTRACE_O_NODEV enable C_ZDEFS by default * because without /dev/dtrace open, we will not be able to load the * names and attributes of any providers or probes from the kernel. */ if (flags & DTRACE_O_NODEV) dtp->dt_cflags |= DTRACE_C_ZDEFS; /* * Load hard-wired inlines into the definition cache by calling the * compiler on the raw definition string defined above. */ if ((pgp = dtrace_program_strcompile(dtp, _dtrace_hardwire, DTRACE_PROBESPEC_NONE, DTRACE_C_EMPTY, 0, NULL)) == NULL) { dt_dprintf("failed to load hard-wired definitions: %s\n", dtrace_errmsg(dtp, dtrace_errno(dtp))); return (set_open_errno(dtp, errp, EDT_HARDWIRE)); } dt_program_destroy(dtp, pgp); /* * Set up the default DTrace library path. Once set, the next call to * dt_compile() will compile all the libraries. We intentionally defer * library processing to improve overhead for clients that don't ever * compile, and to provide better error reporting (because the full * reporting of compiler errors requires dtrace_open() to succeed). */ if (dtrace_setopt(dtp, "libdir", _dtrace_libdir) != 0) return (set_open_errno(dtp, errp, dtp->dt_errno)); return (dtp); } dtrace_hdl_t * dtrace_open(int version, int flags, int *errp) { return (dt_vopen(version, flags, errp, NULL, NULL)); } dtrace_hdl_t * dtrace_vopen(int version, int flags, int *errp, const dtrace_vector_t *vector, void *arg) { return (dt_vopen(version, flags, errp, vector, arg)); } void dtrace_close(dtrace_hdl_t *dtp) { dt_ident_t *idp, *ndp; dt_module_t *dmp; dt_provider_t *pvp; dtrace_prog_t *pgp; dt_xlator_t *dxp; dt_dirpath_t *dirp; int i; if (dtp->dt_procs != NULL) dt_proc_hash_destroy(dtp); while ((pgp = dt_list_next(&dtp->dt_programs)) != NULL) dt_program_destroy(dtp, pgp); while ((dxp = dt_list_next(&dtp->dt_xlators)) != NULL) dt_xlator_destroy(dtp, dxp); dt_free(dtp, dtp->dt_xlatormap); for (idp = dtp->dt_externs; idp != NULL; idp = ndp) { ndp = idp->di_next; dt_ident_destroy(idp); } if (dtp->dt_macros != NULL) dt_idhash_destroy(dtp->dt_macros); if (dtp->dt_aggs != NULL) dt_idhash_destroy(dtp->dt_aggs); if (dtp->dt_globals != NULL) dt_idhash_destroy(dtp->dt_globals); if (dtp->dt_tls != NULL) dt_idhash_destroy(dtp->dt_tls); while ((dmp = dt_list_next(&dtp->dt_modlist)) != NULL) dt_module_destroy(dtp, dmp); while ((pvp = dt_list_next(&dtp->dt_provlist)) != NULL) dt_provider_destroy(dtp, pvp); if (dtp->dt_fd != -1) (void) close(dtp->dt_fd); if (dtp->dt_ftfd != -1) (void) close(dtp->dt_ftfd); if (dtp->dt_cdefs_fd != -1) (void) close(dtp->dt_cdefs_fd); if (dtp->dt_ddefs_fd != -1) (void) close(dtp->dt_ddefs_fd); #if defined(sun) if (dtp->dt_stdout_fd != -1) (void) close(dtp->dt_stdout_fd); #else if (dtp->dt_freopen_fp != NULL) (void) fclose(dtp->dt_freopen_fp); #endif dt_epid_destroy(dtp); dt_aggid_destroy(dtp); dt_format_destroy(dtp); dt_strdata_destroy(dtp); dt_buffered_destroy(dtp); dt_aggregate_destroy(dtp); dt_pfdict_destroy(dtp); dt_provmod_destroy(&dtp->dt_provmod); dt_dof_fini(dtp); for (i = 1; i < dtp->dt_cpp_argc; i++) free(dtp->dt_cpp_argv[i]); while ((dirp = dt_list_next(&dtp->dt_lib_path)) != NULL) { dt_list_delete(&dtp->dt_lib_path, dirp); free(dirp->dir_path); free(dirp); } free(dtp->dt_cpp_argv); free(dtp->dt_cpp_path); free(dtp->dt_ld_path); free(dtp->dt_mods); free(dtp->dt_provs); free(dtp); } int dtrace_provider_modules(dtrace_hdl_t *dtp, const char **mods, int nmods) { dt_provmod_t *prov; int i = 0; for (prov = dtp->dt_provmod; prov != NULL; prov = prov->dp_next, i++) { if (i < nmods) mods[i] = prov->dp_name; } return (i); } int dtrace_ctlfd(dtrace_hdl_t *dtp) { return (dtp->dt_fd); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c (revision 268578) @@ -1,1065 +1,1087 @@ /* * 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) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include #include #if defined(sun) #include #endif #include #include #include #include static int dt_opt_agg(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dt_aggregate_t *agp = &dtp->dt_aggregate; if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); agp->dtat_flags |= option; return (0); } /*ARGSUSED*/ static int dt_opt_amin(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char str[DTRACE_ATTR2STR_MAX]; dtrace_attribute_t attr; if (arg == NULL || dtrace_str2attr(arg, &attr) == -1) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dt_dprintf("set compiler attribute minimum to %s\n", dtrace_attr2str(attr, str, sizeof (str))); if (dtp->dt_pcb != NULL) { dtp->dt_pcb->pcb_cflags |= DTRACE_C_EATTR; dtp->dt_pcb->pcb_amin = attr; } else { dtp->dt_cflags |= DTRACE_C_EATTR; dtp->dt_amin = attr; } return (0); } static void dt_coredump(void) { const char msg[] = "libdtrace DEBUG: [ forcing coredump ]\n"; struct sigaction act; struct rlimit lim; (void) write(STDERR_FILENO, msg, sizeof (msg) - 1); act.sa_handler = SIG_DFL; act.sa_flags = 0; (void) sigemptyset(&act.sa_mask); (void) sigaction(SIGABRT, &act, NULL); lim.rlim_cur = RLIM_INFINITY; lim.rlim_max = RLIM_INFINITY; (void) setrlimit(RLIMIT_CORE, &lim); abort(); } /*ARGSUSED*/ static int dt_opt_core(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { static int enabled = 0; if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (enabled++ || atexit(dt_coredump) == 0) return (0); return (dt_set_errno(dtp, errno)); } /*ARGSUSED*/ static int dt_opt_cpp_hdrs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if (dt_cpp_add_arg(dtp, "-H") == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); return (0); } /*ARGSUSED*/ static int dt_opt_cpp_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *cpp; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if ((cpp = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); dtp->dt_cpp_argv[0] = (char *)strbasename(cpp); free(dtp->dt_cpp_path); dtp->dt_cpp_path = cpp; return (0); } static int dt_opt_cpp_opts(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *buf; size_t len; const char *opt = (const char *)option; if (opt == NULL || arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); len = strlen(opt) + strlen(arg) + 1; buf = alloca(len); (void) strcpy(buf, opt); (void) strcat(buf, arg); if (dt_cpp_add_arg(dtp, buf) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); return (0); } /*ARGSUSED*/ static int dt_opt_ctypes(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int fd; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if ((fd = open64(arg, O_CREAT | O_WRONLY, 0666)) == -1) return (dt_set_errno(dtp, errno)); (void) close(dtp->dt_cdefs_fd); dtp->dt_cdefs_fd = fd; return (0); } /*ARGSUSED*/ static int dt_opt_droptags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtp->dt_droptags = 1; return (0); } /*ARGSUSED*/ static int dt_opt_dtypes(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int fd; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if ((fd = open64(arg, O_CREAT | O_WRONLY, 0666)) == -1) return (dt_set_errno(dtp, errno)); (void) close(dtp->dt_ddefs_fd); dtp->dt_ddefs_fd = fd; return (0); } /*ARGSUSED*/ static int dt_opt_debug(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); _dtrace_debug = 1; return (0); } /*ARGSUSED*/ static int dt_opt_iregs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int n; if (arg == NULL || (n = atoi(arg)) <= 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_conf.dtc_difintregs = n; return (0); } /*ARGSUSED*/ static int dt_opt_lazyload(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtp->dt_lazyload = 1; return (0); } /*ARGSUSED*/ static int dt_opt_ld_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *ld; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if ((ld = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); free(dtp->dt_ld_path); dtp->dt_ld_path = ld; return (0); } /*ARGSUSED*/ static int dt_opt_libdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dt_dirpath_t *dp; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if ((dp = malloc(sizeof (dt_dirpath_t))) == NULL || (dp->dir_path = strdup(arg)) == NULL) { free(dp); return (dt_set_errno(dtp, EDT_NOMEM)); } dt_list_append(&dtp->dt_lib_path, dp); return (0); } /*ARGSUSED*/ static int dt_opt_linkmode(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcmp(arg, "kernel") == 0) dtp->dt_linkmode = DT_LINK_KERNEL; else if (strcmp(arg, "primary") == 0) dtp->dt_linkmode = DT_LINK_PRIMARY; else if (strcmp(arg, "dynamic") == 0) dtp->dt_linkmode = DT_LINK_DYNAMIC; else if (strcmp(arg, "static") == 0) dtp->dt_linkmode = DT_LINK_STATIC; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_linktype(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcasecmp(arg, "elf") == 0) dtp->dt_linktype = DT_LTYP_ELF; else if (strcasecmp(arg, "dof") == 0) dtp->dt_linktype = DT_LTYP_DOF; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int +dt_opt_encoding(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) +{ + if (arg == NULL) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + + if (strcmp(arg, "ascii") == 0) + dtp->dt_encoding = DT_ENCODING_ASCII; + else if (strcmp(arg, "utf8") == 0) + dtp->dt_encoding = DT_ENCODING_UTF8; + else + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + + return (0); +} + +/*ARGSUSED*/ +static int dt_opt_evaltime(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcmp(arg, "exec") == 0) dtp->dt_prcmode = DT_PROC_STOP_CREATE; else if (strcmp(arg, "preinit") == 0) dtp->dt_prcmode = DT_PROC_STOP_PREINIT; else if (strcmp(arg, "postinit") == 0) dtp->dt_prcmode = DT_PROC_STOP_POSTINIT; else if (strcmp(arg, "main") == 0) dtp->dt_prcmode = DT_PROC_STOP_MAIN; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_pgmax(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int n; if (arg == NULL || (n = atoi(arg)) < 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_procs->dph_lrulim = n; return (0); } static int dt_opt_setenv(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char **p; char *var; int i; /* * We can't effectively set environment variables from #pragma lines * since the processes have already been spawned. */ if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (!option && strchr(arg, '=') != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); for (i = 1, p = dtp->dt_proc_env; *p != NULL; i++, p++) continue; for (p = dtp->dt_proc_env; *p != NULL; p++) { var = strchr(*p, '='); if (var == NULL) var = *p + strlen(*p); if (strncmp(*p, arg, var - *p) == 0) { dt_free(dtp, *p); *p = dtp->dt_proc_env[i - 1]; dtp->dt_proc_env[i - 1] = NULL; i--; } } if (option) { if ((var = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); if ((p = dt_alloc(dtp, sizeof (char *) * (i + 1))) == NULL) { dt_free(dtp, var); return (dt_set_errno(dtp, EDT_NOMEM)); } bcopy(dtp->dt_proc_env, p, sizeof (char *) * i); dt_free(dtp, dtp->dt_proc_env); dtp->dt_proc_env = p; dtp->dt_proc_env[i - 1] = var; dtp->dt_proc_env[i] = NULL; } return (0); } /*ARGSUSED*/ static int dt_opt_stdc(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if (strcmp(arg, "a") == 0) dtp->dt_stdcmode = DT_STDC_XA; else if (strcmp(arg, "c") == 0) dtp->dt_stdcmode = DT_STDC_XC; else if (strcmp(arg, "s") == 0) dtp->dt_stdcmode = DT_STDC_XS; else if (strcmp(arg, "t") == 0) dtp->dt_stdcmode = DT_STDC_XT; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_syslibdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dt_dirpath_t *dp = dt_list_next(&dtp->dt_lib_path); char *path; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if ((path = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); free(dp->dir_path); dp->dir_path = path; return (0); } /*ARGSUSED*/ static int dt_opt_tree(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int m; if (arg == NULL || (m = atoi(arg)) <= 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_treedump = m; return (0); } /*ARGSUSED*/ static int dt_opt_tregs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int n; if (arg == NULL || (n = atoi(arg)) <= 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_conf.dtc_diftupregs = n; return (0); } /*ARGSUSED*/ static int dt_opt_xlate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcmp(arg, "dynamic") == 0) dtp->dt_xlatemode = DT_XL_DYNAMIC; else if (strcmp(arg, "static") == 0) dtp->dt_xlatemode = DT_XL_STATIC; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_cflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) dtp->dt_pcb->pcb_cflags |= option; else dtp->dt_cflags |= option; return (0); } static int dt_opt_dflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_dflags |= option; return (0); } static int dt_opt_invcflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) dtp->dt_pcb->pcb_cflags &= ~option; else dtp->dt_cflags &= ~option; return (0); } /*ARGSUSED*/ static int dt_opt_version(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dt_version_t v; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dt_version_str2num(arg, &v) == -1) return (dt_set_errno(dtp, EDT_VERSINVAL)); if (!dt_version_defined(v)) return (dt_set_errno(dtp, EDT_VERSUNDEF)); return (dt_reduce(dtp, v)); } static int dt_opt_runtime(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *end; dtrace_optval_t val = 0; int i; const struct { char *positive; char *negative; } couples[] = { { "yes", "no" }, { "enable", "disable" }, { "enabled", "disabled" }, { "true", "false" }, { "on", "off" }, { "set", "unset" }, { NULL } }; if (arg != NULL) { if (arg[0] == '\0') { val = DTRACEOPT_UNSET; goto out; } for (i = 0; couples[i].positive != NULL; i++) { if (strcasecmp(couples[i].positive, arg) == 0) { val = 1; goto out; } if (strcasecmp(couples[i].negative, arg) == 0) { val = DTRACEOPT_UNSET; goto out; } } errno = 0; val = strtoull(arg, &end, 0); if (*end != '\0' || errno != 0 || val < 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); } out: dtp->dt_options[option] = val; return (0); } static int dt_optval_parse(const char *arg, dtrace_optval_t *rval) { dtrace_optval_t mul = 1; size_t len; char *end; len = strlen(arg); errno = 0; switch (arg[len - 1]) { case 't': case 'T': mul *= 1024; /*FALLTHRU*/ case 'g': case 'G': mul *= 1024; /*FALLTHRU*/ case 'm': case 'M': mul *= 1024; /*FALLTHRU*/ case 'k': case 'K': mul *= 1024; /*FALLTHRU*/ default: break; } errno = 0; *rval = strtoull(arg, &end, 0) * mul; if ((mul > 1 && end != &arg[len - 1]) || (mul == 1 && *end != '\0') || *rval < 0 || errno != 0) return (-1); return (0); } static int dt_opt_size(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtrace_optval_t val = 0; if (arg != NULL && dt_optval_parse(arg, &val) != 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_options[option] = val; return (0); } static int dt_opt_rate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *end; int i; dtrace_optval_t mul = 1, val = 0; const struct { char *name; hrtime_t mul; } suffix[] = { { "ns", NANOSEC / NANOSEC }, { "nsec", NANOSEC / NANOSEC }, { "us", NANOSEC / MICROSEC }, { "usec", NANOSEC / MICROSEC }, { "ms", NANOSEC / MILLISEC }, { "msec", NANOSEC / MILLISEC }, { "s", NANOSEC / SEC }, { "sec", NANOSEC / SEC }, { "m", NANOSEC * (hrtime_t)60 }, { "min", NANOSEC * (hrtime_t)60 }, { "h", NANOSEC * (hrtime_t)60 * (hrtime_t)60 }, { "hour", NANOSEC * (hrtime_t)60 * (hrtime_t)60 }, { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) }, { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) }, { "hz", 0 }, { NULL } }; if (arg != NULL) { errno = 0; val = strtoull(arg, &end, 0); for (i = 0; suffix[i].name != NULL; i++) { if (strcasecmp(suffix[i].name, end) == 0) { mul = suffix[i].mul; break; } } if (suffix[i].name == NULL && *end != '\0' || val < 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (mul == 0) { /* * The rate has been specified in frequency-per-second. */ if (val != 0) val = NANOSEC / val; } else { val *= mul; } } dtp->dt_options[option] = val; return (0); } /* * When setting the strsize option, set the option in the dt_options array * using dt_opt_size() as usual, and then update the definition of the CTF * type for the D intrinsic "string" to be an array of the corresponding size. * If any errors occur, reset dt_options[option] to its previous value. */ static int dt_opt_strsize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtrace_optval_t val = dtp->dt_options[option]; ctf_file_t *fp = DT_STR_CTFP(dtp); ctf_id_t type = ctf_type_resolve(fp, DT_STR_TYPE(dtp)); ctf_arinfo_t r; if (dt_opt_size(dtp, arg, option) != 0) return (-1); /* dt_errno is set for us */ if (dtp->dt_options[option] > UINT_MAX) { dtp->dt_options[option] = val; return (dt_set_errno(dtp, EOVERFLOW)); } if (ctf_array_info(fp, type, &r) == CTF_ERR) { dtp->dt_options[option] = val; dtp->dt_ctferr = ctf_errno(fp); return (dt_set_errno(dtp, EDT_CTF)); } r.ctr_nelems = (uint_t)dtp->dt_options[option]; if (ctf_set_array(fp, type, &r) == CTF_ERR || ctf_update(fp) == CTF_ERR) { dtp->dt_options[option] = val; dtp->dt_ctferr = ctf_errno(fp); return (dt_set_errno(dtp, EDT_CTF)); } return (0); } static const struct { const char *dtbp_name; int dtbp_policy; } _dtrace_bufpolicies[] = { { "ring", DTRACEOPT_BUFPOLICY_RING }, { "fill", DTRACEOPT_BUFPOLICY_FILL }, { "switch", DTRACEOPT_BUFPOLICY_SWITCH }, { NULL, 0 } }; /*ARGSUSED*/ static int dt_opt_bufpolicy(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtrace_optval_t policy = DTRACEOPT_UNSET; int i; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); for (i = 0; _dtrace_bufpolicies[i].dtbp_name != NULL; i++) { if (strcmp(_dtrace_bufpolicies[i].dtbp_name, arg) == 0) { policy = _dtrace_bufpolicies[i].dtbp_policy; break; } } if (policy == DTRACEOPT_UNSET) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_options[DTRACEOPT_BUFPOLICY] = policy; return (0); } static const struct { const char *dtbr_name; int dtbr_policy; } _dtrace_bufresize[] = { { "auto", DTRACEOPT_BUFRESIZE_AUTO }, { "manual", DTRACEOPT_BUFRESIZE_MANUAL }, { NULL, 0 } }; /*ARGSUSED*/ static int dt_opt_bufresize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtrace_optval_t policy = DTRACEOPT_UNSET; int i; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); for (i = 0; _dtrace_bufresize[i].dtbr_name != NULL; i++) { if (strcmp(_dtrace_bufresize[i].dtbr_name, arg) == 0) { policy = _dtrace_bufresize[i].dtbr_policy; break; } } if (policy == DTRACEOPT_UNSET) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_options[DTRACEOPT_BUFRESIZE] = policy; return (0); } int dt_options_load(dtrace_hdl_t *dtp) { dof_hdr_t hdr, *dof; dof_sec_t *sec; size_t offs; int i; /* * To load the option values, we need to ask the kernel to provide its * DOF, which we'll sift through to look for OPTDESC sections. */ bzero(&hdr, sizeof (dof_hdr_t)); hdr.dofh_loadsz = sizeof (dof_hdr_t); #if defined(sun) if (dt_ioctl(dtp, DTRACEIOC_DOFGET, &hdr) == -1) #else dof = &hdr; if (dt_ioctl(dtp, DTRACEIOC_DOFGET, &dof) == -1) #endif return (dt_set_errno(dtp, errno)); if (hdr.dofh_loadsz < sizeof (dof_hdr_t)) return (dt_set_errno(dtp, EINVAL)); dof = alloca(hdr.dofh_loadsz); bzero(dof, sizeof (dof_hdr_t)); dof->dofh_loadsz = hdr.dofh_loadsz; for (i = 0; i < DTRACEOPT_MAX; i++) dtp->dt_options[i] = DTRACEOPT_UNSET; #if defined(sun) if (dt_ioctl(dtp, DTRACEIOC_DOFGET, dof) == -1) #else if (dt_ioctl(dtp, DTRACEIOC_DOFGET, &dof) == -1) #endif return (dt_set_errno(dtp, errno)); for (i = 0; i < dof->dofh_secnum; i++) { sec = (dof_sec_t *)(uintptr_t)((uintptr_t)dof + dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_OPTDESC) continue; break; } for (offs = 0; offs < sec->dofs_size; offs += sec->dofs_entsize) { dof_optdesc_t *opt = (dof_optdesc_t *)(uintptr_t) ((uintptr_t)dof + sec->dofs_offset + offs); if (opt->dofo_strtab != DOF_SECIDX_NONE) continue; if (opt->dofo_option >= DTRACEOPT_MAX) continue; dtp->dt_options[opt->dofo_option] = opt->dofo_value; } return (0); } typedef struct dt_option { const char *o_name; int (*o_func)(dtrace_hdl_t *, const char *, uintptr_t); uintptr_t o_option; } dt_option_t; /* * Compile-time options. */ static const dt_option_t _dtrace_ctoptions[] = { { "aggpercpu", dt_opt_agg, DTRACE_A_PERCPU }, { "amin", dt_opt_amin }, { "argref", dt_opt_cflags, DTRACE_C_ARGREF }, { "core", dt_opt_core }, { "cpp", dt_opt_cflags, DTRACE_C_CPP }, { "cpphdrs", dt_opt_cpp_hdrs }, { "cpppath", dt_opt_cpp_path }, { "ctypes", dt_opt_ctypes }, { "defaultargs", dt_opt_cflags, DTRACE_C_DEFARG }, { "dtypes", dt_opt_dtypes }, { "debug", dt_opt_debug }, { "define", dt_opt_cpp_opts, (uintptr_t)"-D" }, { "droptags", dt_opt_droptags }, { "empty", dt_opt_cflags, DTRACE_C_EMPTY }, + { "encoding", dt_opt_encoding }, { "errtags", dt_opt_cflags, DTRACE_C_ETAGS }, { "evaltime", dt_opt_evaltime }, { "incdir", dt_opt_cpp_opts, (uintptr_t)"-I" }, { "iregs", dt_opt_iregs }, { "kdefs", dt_opt_invcflags, DTRACE_C_KNODEF }, { "knodefs", dt_opt_cflags, DTRACE_C_KNODEF }, { "late", dt_opt_xlate }, { "lazyload", dt_opt_lazyload }, { "ldpath", dt_opt_ld_path }, { "libdir", dt_opt_libdir }, { "linkmode", dt_opt_linkmode }, { "linktype", dt_opt_linktype }, { "nolibs", dt_opt_cflags, DTRACE_C_NOLIBS }, { "pgmax", dt_opt_pgmax }, { "pspec", dt_opt_cflags, DTRACE_C_PSPEC }, { "setenv", dt_opt_setenv, 1 }, { "stdc", dt_opt_stdc }, { "strip", dt_opt_dflags, DTRACE_D_STRIP }, { "syslibdir", dt_opt_syslibdir }, { "tree", dt_opt_tree }, { "tregs", dt_opt_tregs }, { "udefs", dt_opt_invcflags, DTRACE_C_UNODEF }, { "undef", dt_opt_cpp_opts, (uintptr_t)"-U" }, { "unodefs", dt_opt_cflags, DTRACE_C_UNODEF }, { "unsetenv", dt_opt_setenv, 0 }, { "verbose", dt_opt_cflags, DTRACE_C_DIFV }, { "version", dt_opt_version }, { "zdefs", dt_opt_cflags, DTRACE_C_ZDEFS }, { NULL, NULL, 0 } }; /* * Run-time options. */ static const dt_option_t _dtrace_rtoptions[] = { { "aggsize", dt_opt_size, DTRACEOPT_AGGSIZE }, { "bufsize", dt_opt_size, DTRACEOPT_BUFSIZE }, { "bufpolicy", dt_opt_bufpolicy, DTRACEOPT_BUFPOLICY }, { "bufresize", dt_opt_bufresize, DTRACEOPT_BUFRESIZE }, { "cleanrate", dt_opt_rate, DTRACEOPT_CLEANRATE }, { "cpu", dt_opt_runtime, DTRACEOPT_CPU }, { "destructive", dt_opt_runtime, DTRACEOPT_DESTRUCTIVE }, { "dynvarsize", dt_opt_size, DTRACEOPT_DYNVARSIZE }, { "grabanon", dt_opt_runtime, DTRACEOPT_GRABANON }, { "jstackframes", dt_opt_runtime, DTRACEOPT_JSTACKFRAMES }, { "jstackstrsize", dt_opt_size, DTRACEOPT_JSTACKSTRSIZE }, { "nspec", dt_opt_runtime, DTRACEOPT_NSPEC }, { "specsize", dt_opt_size, DTRACEOPT_SPECSIZE }, { "stackframes", dt_opt_runtime, DTRACEOPT_STACKFRAMES }, { "statusrate", dt_opt_rate, DTRACEOPT_STATUSRATE }, { "strsize", dt_opt_strsize, DTRACEOPT_STRSIZE }, { "ustackframes", dt_opt_runtime, DTRACEOPT_USTACKFRAMES }, { "temporal", dt_opt_runtime, DTRACEOPT_TEMPORAL }, { NULL, NULL, 0 } }; /* * Dynamic run-time options. */ static const dt_option_t _dtrace_drtoptions[] = { + { "agghist", dt_opt_runtime, DTRACEOPT_AGGHIST }, + { "aggpack", dt_opt_runtime, DTRACEOPT_AGGPACK }, { "aggrate", dt_opt_rate, DTRACEOPT_AGGRATE }, { "aggsortkey", dt_opt_runtime, DTRACEOPT_AGGSORTKEY }, { "aggsortkeypos", dt_opt_runtime, DTRACEOPT_AGGSORTKEYPOS }, { "aggsortpos", dt_opt_runtime, DTRACEOPT_AGGSORTPOS }, { "aggsortrev", dt_opt_runtime, DTRACEOPT_AGGSORTREV }, + { "aggzoom", dt_opt_runtime, DTRACEOPT_AGGZOOM }, { "flowindent", dt_opt_runtime, DTRACEOPT_FLOWINDENT }, { "quiet", dt_opt_runtime, DTRACEOPT_QUIET }, { "rawbytes", dt_opt_runtime, DTRACEOPT_RAWBYTES }, { "stackindent", dt_opt_runtime, DTRACEOPT_STACKINDENT }, { "switchrate", dt_opt_rate, DTRACEOPT_SWITCHRATE }, { NULL, NULL, 0 } }; int dtrace_getopt(dtrace_hdl_t *dtp, const char *opt, dtrace_optval_t *val) { const dt_option_t *op; if (opt == NULL) return (dt_set_errno(dtp, EINVAL)); /* * We only need to search the run-time options -- it's not legal * to get the values of compile-time options. */ for (op = _dtrace_rtoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) { *val = dtp->dt_options[op->o_option]; return (0); } } for (op = _dtrace_drtoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) { *val = dtp->dt_options[op->o_option]; return (0); } } return (dt_set_errno(dtp, EDT_BADOPTNAME)); } int dtrace_setopt(dtrace_hdl_t *dtp, const char *opt, const char *val) { const dt_option_t *op; if (opt == NULL) return (dt_set_errno(dtp, EINVAL)); for (op = _dtrace_ctoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) return (op->o_func(dtp, val, op->o_option)); } for (op = _dtrace_drtoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) return (op->o_func(dtp, val, op->o_option)); } for (op = _dtrace_rtoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) { /* * Only dynamic run-time options may be set while * tracing is active. */ if (dtp->dt_active) return (dt_set_errno(dtp, EDT_ACTIVE)); return (op->o_func(dtp, val, op->o_option)); } } return (dt_set_errno(dtp, EDT_BADOPTNAME)); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_parser.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_parser.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_parser.c (revision 268578) @@ -1,4932 +1,4985 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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. - * Copyright (c) 2011, Joyent Inc. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013, Joyent Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * DTrace D Language Parser * * The D Parser is a lex/yacc parser consisting of the lexer dt_lex.l, the * parsing grammar dt_grammar.y, and this file, dt_parser.c, which handles * the construction of the parse tree nodes and their syntactic validation. * The parse tree is constructed of dt_node_t structures (see ) * that are built in two passes: (1) the "create" pass, where the parse tree * nodes are allocated by calls from the grammar to dt_node_*() subroutines, * and (2) the "cook" pass, where nodes are coalesced, assigned D types, and * validated according to the syntactic rules of the language. * * All node allocations are performed using dt_node_alloc(). All node frees * during the parsing phase are performed by dt_node_free(), which frees node- * internal state but does not actually free the nodes. All final node frees * are done as part of the end of dt_compile() or as part of destroying * persistent identifiers or translators which have embedded nodes. * * The dt_node_* routines that implement pass (1) may allocate new nodes. The * dt_cook_* routines that implement pass (2) may *not* allocate new nodes. * They may free existing nodes using dt_node_free(), but they may not actually * deallocate any dt_node_t's. Currently dt_cook_op2() is an exception to this * rule: see the comments therein for how this issue is resolved. * * The dt_cook_* routines are responsible for (at minimum) setting the final * node type (dn_ctfp/dn_type) and attributes (dn_attr). If dn_ctfp/dn_type * are set manually (i.e. not by one of the type assignment functions), then * the DT_NF_COOKED flag must be set manually on the node. * * The cooking pass can be applied to the same parse tree more than once (used * in the case of a comma-separated list of probe descriptions). As such, the * cook routines must not perform any parse tree transformations which would * be invalid if the tree were subsequently cooked using a different context. * * The dn_ctfp and dn_type fields form the type of the node. This tuple can * take on the following set of values, which form our type invariants: * * 1. dn_ctfp = NULL, dn_type = CTF_ERR * * In this state, the node has unknown type and is not yet cooked. The * DT_NF_COOKED flag is not yet set on the node. * * 2. dn_ctfp = DT_DYN_CTFP(dtp), dn_type = DT_DYN_TYPE(dtp) * * In this state, the node is a dynamic D type. This means that generic * operations are not valid on this node and only code that knows how to * examine the inner details of the node can operate on it. A node * must have dn_ident set to point to an identifier describing the object * and its type. The DT_NF_REF flag is set for all nodes of type . * At present, the D compiler uses the type for: * * - associative arrays that do not yet have a value type defined * - translated data (i.e. the result of the xlate operator) * - aggregations * * 3. dn_ctfp = DT_STR_CTFP(dtp), dn_type = DT_STR_TYPE(dtp) * * In this state, the node is of type D string. The string type is really * a char[0] typedef, but requires special handling throughout the compiler. * * 4. dn_ctfp != NULL, dn_type = any other type ID * * In this state, the node is of some known D/CTF type. The normal libctf * APIs can be used to learn more about the type name or structure. When * the type is assigned, the DT_NF_SIGNED, DT_NF_REF, and DT_NF_BITFIELD * flags cache the corresponding attributes of the underlying CTF type. */ #include #include #include #include #include #include #if defined(sun) #include #endif #include #include #include #include #include #include #include #include #include #include #include dt_pcb_t *yypcb; /* current control block for parser */ dt_node_t *yypragma; /* lex token list for control lines */ char yyintprefix; /* int token macro prefix (+/-) */ char yyintsuffix[4]; /* int token suffix string [uU][lL] */ int yyintdecimal; /* int token format flag (1=decimal, 0=octal/hex) */ static const char * opstr(int op) { switch (op) { case DT_TOK_COMMA: return (","); case DT_TOK_ELLIPSIS: return ("..."); case DT_TOK_ASGN: return ("="); case DT_TOK_ADD_EQ: return ("+="); case DT_TOK_SUB_EQ: return ("-="); case DT_TOK_MUL_EQ: return ("*="); case DT_TOK_DIV_EQ: return ("/="); case DT_TOK_MOD_EQ: return ("%="); case DT_TOK_AND_EQ: return ("&="); case DT_TOK_XOR_EQ: return ("^="); case DT_TOK_OR_EQ: return ("|="); case DT_TOK_LSH_EQ: return ("<<="); case DT_TOK_RSH_EQ: return (">>="); case DT_TOK_QUESTION: return ("?"); case DT_TOK_COLON: return (":"); case DT_TOK_LOR: return ("||"); case DT_TOK_LXOR: return ("^^"); case DT_TOK_LAND: return ("&&"); case DT_TOK_BOR: return ("|"); case DT_TOK_XOR: return ("^"); case DT_TOK_BAND: return ("&"); case DT_TOK_EQU: return ("=="); case DT_TOK_NEQ: return ("!="); case DT_TOK_LT: return ("<"); case DT_TOK_LE: return ("<="); case DT_TOK_GT: return (">"); case DT_TOK_GE: return (">="); case DT_TOK_LSH: return ("<<"); case DT_TOK_RSH: return (">>"); case DT_TOK_ADD: return ("+"); case DT_TOK_SUB: return ("-"); case DT_TOK_MUL: return ("*"); case DT_TOK_DIV: return ("/"); case DT_TOK_MOD: return ("%"); case DT_TOK_LNEG: return ("!"); case DT_TOK_BNEG: return ("~"); case DT_TOK_ADDADD: return ("++"); case DT_TOK_PREINC: return ("++"); case DT_TOK_POSTINC: return ("++"); case DT_TOK_SUBSUB: return ("--"); case DT_TOK_PREDEC: return ("--"); case DT_TOK_POSTDEC: return ("--"); case DT_TOK_IPOS: return ("+"); case DT_TOK_INEG: return ("-"); case DT_TOK_DEREF: return ("*"); case DT_TOK_ADDROF: return ("&"); case DT_TOK_OFFSETOF: return ("offsetof"); case DT_TOK_SIZEOF: return ("sizeof"); case DT_TOK_STRINGOF: return ("stringof"); case DT_TOK_XLATE: return ("xlate"); case DT_TOK_LPAR: return ("("); case DT_TOK_RPAR: return (")"); case DT_TOK_LBRAC: return ("["); case DT_TOK_RBRAC: return ("]"); case DT_TOK_PTR: return ("->"); case DT_TOK_DOT: return ("."); case DT_TOK_STRING: return (""); case DT_TOK_IDENT: return (""); case DT_TOK_TNAME: return (""); case DT_TOK_INT: return (""); default: return (""); } } int dt_type_lookup(const char *s, dtrace_typeinfo_t *tip) { static const char delimiters[] = " \t\n\r\v\f*`"; dtrace_hdl_t *dtp = yypcb->pcb_hdl; - const char *p, *q, *end, *obj; + const char *p, *q, *r, *end, *obj; for (p = s, end = s + strlen(s); *p != '\0'; p = q) { while (isspace(*p)) p++; /* skip leading whitespace prior to token */ if (p == end || (q = strpbrk(p + 1, delimiters)) == NULL) break; /* empty string or single token remaining */ if (*q == '`') { char *object = alloca((size_t)(q - p) + 1); char *type = alloca((size_t)(end - s) + 1); /* * Copy from the start of the token (p) to the location * backquote (q) to extract the nul-terminated object. */ bcopy(p, object, (size_t)(q - p)); object[(size_t)(q - p)] = '\0'; /* * Copy the original string up to the start of this * token (p) into type, and then concatenate everything * after q. This is the type name without the object. */ bcopy(s, type, (size_t)(p - s)); bcopy(q + 1, type + (size_t)(p - s), strlen(q + 1) + 1); - if (strchr(q + 1, '`') != NULL) - return (dt_set_errno(dtp, EDT_BADSCOPE)); + /* + * There may be at most three delimeters. The second + * delimeter is usually used to distinguish the type + * within a given module, however, there could be a link + * map id on the scene in which case that delimeter + * would be the third. We determine presence of the lmid + * if it rouglhly meets the from LM[0-9] + */ + if ((r = strchr(q + 1, '`')) != NULL && + ((r = strchr(r + 1, '`')) != NULL)) { + if (strchr(r + 1, '`') != NULL) + return (dt_set_errno(dtp, + EDT_BADSCOPE)); + if (q[1] != 'L' || q[2] != 'M') + return (dt_set_errno(dtp, + EDT_BADSCOPE)); + } return (dtrace_lookup_by_type(dtp, object, type, tip)); } } if (yypcb->pcb_idepth != 0) obj = DTRACE_OBJ_CDEFS; else obj = DTRACE_OBJ_EVERY; return (dtrace_lookup_by_type(dtp, obj, s, tip)); } /* * When we parse type expressions or parse an expression with unary "&", we * need to find a type that is a pointer to a previously known type. * Unfortunately CTF is limited to a per-container view, so ctf_type_pointer() * alone does not suffice for our needs. We provide a more intelligent wrapper * for the compiler that attempts to compute a pointer to either the given type * or its base (that is, we try both "foo_t *" and "struct foo *"), and also * to potentially construct the required type on-the-fly. */ int dt_type_pointer(dtrace_typeinfo_t *tip) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; ctf_file_t *ctfp = tip->dtt_ctfp; ctf_id_t type = tip->dtt_type; ctf_id_t base = ctf_type_resolve(ctfp, type); + uint_t bflags = tip->dtt_flags; dt_module_t *dmp; ctf_id_t ptr; if ((ptr = ctf_type_pointer(ctfp, type)) != CTF_ERR || (ptr = ctf_type_pointer(ctfp, base)) != CTF_ERR) { tip->dtt_type = ptr; return (0); } if (yypcb->pcb_idepth != 0) dmp = dtp->dt_cdefs; else dmp = dtp->dt_ddefs; if (ctfp != dmp->dm_ctfp && ctfp != ctf_parent_file(dmp->dm_ctfp) && (type = ctf_add_type(dmp->dm_ctfp, ctfp, type)) == CTF_ERR) { dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp); return (dt_set_errno(dtp, EDT_CTF)); } ptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, type); if (ptr == CTF_ERR || ctf_update(dmp->dm_ctfp) == CTF_ERR) { dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp); return (dt_set_errno(dtp, EDT_CTF)); } tip->dtt_object = dmp->dm_name; tip->dtt_ctfp = dmp->dm_ctfp; tip->dtt_type = ptr; + tip->dtt_flags = bflags; return (0); } const char * dt_type_name(ctf_file_t *ctfp, ctf_id_t type, char *buf, size_t len) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; if (ctfp == DT_FPTR_CTFP(dtp) && type == DT_FPTR_TYPE(dtp)) (void) snprintf(buf, len, "function pointer"); else if (ctfp == DT_FUNC_CTFP(dtp) && type == DT_FUNC_TYPE(dtp)) (void) snprintf(buf, len, "function"); else if (ctfp == DT_DYN_CTFP(dtp) && type == DT_DYN_TYPE(dtp)) (void) snprintf(buf, len, "dynamic variable"); else if (ctfp == NULL) (void) snprintf(buf, len, ""); else if (ctf_type_name(ctfp, type, buf, len) == NULL) (void) snprintf(buf, len, "unknown"); return (buf); } /* * Perform the "usual arithmetic conversions" to determine which of the two * input operand types should be promoted and used as a result type. The * rules for this are described in ISOC[6.3.1.8] and K&R[A6.5]. */ static void dt_type_promote(dt_node_t *lp, dt_node_t *rp, ctf_file_t **ofp, ctf_id_t *otype) { ctf_file_t *lfp = lp->dn_ctfp; ctf_id_t ltype = lp->dn_type; ctf_file_t *rfp = rp->dn_ctfp; ctf_id_t rtype = rp->dn_type; ctf_id_t lbase = ctf_type_resolve(lfp, ltype); uint_t lkind = ctf_type_kind(lfp, lbase); ctf_id_t rbase = ctf_type_resolve(rfp, rtype); uint_t rkind = ctf_type_kind(rfp, rbase); dtrace_hdl_t *dtp = yypcb->pcb_hdl; ctf_encoding_t le, re; uint_t lrank, rrank; assert(lkind == CTF_K_INTEGER || lkind == CTF_K_ENUM); assert(rkind == CTF_K_INTEGER || rkind == CTF_K_ENUM); if (lkind == CTF_K_ENUM) { lfp = DT_INT_CTFP(dtp); ltype = lbase = DT_INT_TYPE(dtp); } if (rkind == CTF_K_ENUM) { rfp = DT_INT_CTFP(dtp); rtype = rbase = DT_INT_TYPE(dtp); } if (ctf_type_encoding(lfp, lbase, &le) == CTF_ERR) { yypcb->pcb_hdl->dt_ctferr = ctf_errno(lfp); longjmp(yypcb->pcb_jmpbuf, EDT_CTF); } if (ctf_type_encoding(rfp, rbase, &re) == CTF_ERR) { yypcb->pcb_hdl->dt_ctferr = ctf_errno(rfp); longjmp(yypcb->pcb_jmpbuf, EDT_CTF); } /* * Compute an integer rank based on the size and unsigned status. * If rank is identical, pick the "larger" of the equivalent types * which we define as having a larger base ctf_id_t. If rank is * different, pick the type with the greater rank. */ lrank = le.cte_bits + ((le.cte_format & CTF_INT_SIGNED) == 0); rrank = re.cte_bits + ((re.cte_format & CTF_INT_SIGNED) == 0); if (lrank == rrank) { if (lbase - rbase < 0) goto return_rtype; else goto return_ltype; } else if (lrank > rrank) { goto return_ltype; } else goto return_rtype; return_ltype: *ofp = lfp; *otype = ltype; return; return_rtype: *ofp = rfp; *otype = rtype; } void dt_node_promote(dt_node_t *lp, dt_node_t *rp, dt_node_t *dnp) { dt_type_promote(lp, rp, &dnp->dn_ctfp, &dnp->dn_type); - dt_node_type_assign(dnp, dnp->dn_ctfp, dnp->dn_type); + dt_node_type_assign(dnp, dnp->dn_ctfp, dnp->dn_type, B_FALSE); dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr)); } const char * dt_node_name(const dt_node_t *dnp, char *buf, size_t len) { char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; const char *prefix = "", *suffix = ""; const dtrace_syminfo_t *dts; char *s; switch (dnp->dn_kind) { case DT_NODE_INT: (void) snprintf(buf, len, "integer constant 0x%llx", (u_longlong_t)dnp->dn_value); break; case DT_NODE_STRING: s = strchr2esc(dnp->dn_string, strlen(dnp->dn_string)); (void) snprintf(buf, len, "string constant \"%s\"", s != NULL ? s : dnp->dn_string); free(s); break; case DT_NODE_IDENT: (void) snprintf(buf, len, "identifier %s", dnp->dn_string); break; case DT_NODE_VAR: case DT_NODE_FUNC: case DT_NODE_AGG: case DT_NODE_INLINE: switch (dnp->dn_ident->di_kind) { case DT_IDENT_FUNC: case DT_IDENT_AGGFUNC: case DT_IDENT_ACTFUNC: suffix = "( )"; break; case DT_IDENT_AGG: prefix = "@"; break; } (void) snprintf(buf, len, "%s %s%s%s", dt_idkind_name(dnp->dn_ident->di_kind), prefix, dnp->dn_ident->di_name, suffix); break; case DT_NODE_SYM: dts = dnp->dn_ident->di_data; (void) snprintf(buf, len, "symbol %s`%s", dts->dts_object, dts->dts_name); break; case DT_NODE_TYPE: (void) snprintf(buf, len, "type %s", dt_node_type_name(dnp, n1, sizeof (n1))); break; case DT_NODE_OP1: case DT_NODE_OP2: case DT_NODE_OP3: (void) snprintf(buf, len, "operator %s", opstr(dnp->dn_op)); break; case DT_NODE_DEXPR: case DT_NODE_DFUNC: if (dnp->dn_expr) return (dt_node_name(dnp->dn_expr, buf, len)); (void) snprintf(buf, len, "%s", "statement"); break; case DT_NODE_PDESC: if (dnp->dn_desc->dtpd_id == 0) { (void) snprintf(buf, len, "probe description %s:%s:%s:%s", dnp->dn_desc->dtpd_provider, dnp->dn_desc->dtpd_mod, dnp->dn_desc->dtpd_func, dnp->dn_desc->dtpd_name); } else { (void) snprintf(buf, len, "probe description %u", dnp->dn_desc->dtpd_id); } break; case DT_NODE_CLAUSE: (void) snprintf(buf, len, "%s", "clause"); break; case DT_NODE_MEMBER: (void) snprintf(buf, len, "member %s", dnp->dn_membname); break; case DT_NODE_XLATOR: (void) snprintf(buf, len, "translator <%s> (%s)", dt_type_name(dnp->dn_xlator->dx_dst_ctfp, dnp->dn_xlator->dx_dst_type, n1, sizeof (n1)), dt_type_name(dnp->dn_xlator->dx_src_ctfp, dnp->dn_xlator->dx_src_type, n2, sizeof (n2))); break; case DT_NODE_PROG: (void) snprintf(buf, len, "%s", "program"); break; default: (void) snprintf(buf, len, "node <%u>", dnp->dn_kind); break; } return (buf); } /* * dt_node_xalloc() can be used to create new parse nodes from any libdtrace * caller. The caller is responsible for assigning dn_link appropriately. */ dt_node_t * dt_node_xalloc(dtrace_hdl_t *dtp, int kind) { dt_node_t *dnp = dt_alloc(dtp, sizeof (dt_node_t)); if (dnp == NULL) return (NULL); dnp->dn_ctfp = NULL; dnp->dn_type = CTF_ERR; dnp->dn_kind = (uchar_t)kind; dnp->dn_flags = 0; dnp->dn_op = 0; dnp->dn_line = -1; dnp->dn_reg = -1; dnp->dn_attr = _dtrace_defattr; dnp->dn_list = NULL; dnp->dn_link = NULL; bzero(&dnp->dn_u, sizeof (dnp->dn_u)); return (dnp); } /* * dt_node_alloc() is used to create new parse nodes from the parser. It * assigns the node location based on the current lexer line number and places * the new node on the default allocation list. If allocation fails, we * automatically longjmp the caller back to the enclosing compilation call. */ static dt_node_t * dt_node_alloc(int kind) { dt_node_t *dnp = dt_node_xalloc(yypcb->pcb_hdl, kind); if (dnp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dnp->dn_line = yylineno; dnp->dn_link = yypcb->pcb_list; yypcb->pcb_list = dnp; return (dnp); } void dt_node_free(dt_node_t *dnp) { uchar_t kind = dnp->dn_kind; dnp->dn_kind = DT_NODE_FREE; switch (kind) { case DT_NODE_STRING: case DT_NODE_IDENT: case DT_NODE_TYPE: free(dnp->dn_string); dnp->dn_string = NULL; break; case DT_NODE_VAR: case DT_NODE_FUNC: case DT_NODE_PROBE: if (dnp->dn_ident != NULL) { if (dnp->dn_ident->di_flags & DT_IDFLG_ORPHAN) dt_ident_destroy(dnp->dn_ident); dnp->dn_ident = NULL; } dt_node_list_free(&dnp->dn_args); break; case DT_NODE_OP1: if (dnp->dn_child != NULL) { dt_node_free(dnp->dn_child); dnp->dn_child = NULL; } break; case DT_NODE_OP3: if (dnp->dn_expr != NULL) { dt_node_free(dnp->dn_expr); dnp->dn_expr = NULL; } /*FALLTHRU*/ case DT_NODE_OP2: if (dnp->dn_left != NULL) { dt_node_free(dnp->dn_left); dnp->dn_left = NULL; } if (dnp->dn_right != NULL) { dt_node_free(dnp->dn_right); dnp->dn_right = NULL; } break; case DT_NODE_DEXPR: case DT_NODE_DFUNC: if (dnp->dn_expr != NULL) { dt_node_free(dnp->dn_expr); dnp->dn_expr = NULL; } break; case DT_NODE_AGG: if (dnp->dn_aggfun != NULL) { dt_node_free(dnp->dn_aggfun); dnp->dn_aggfun = NULL; } dt_node_list_free(&dnp->dn_aggtup); break; case DT_NODE_PDESC: free(dnp->dn_spec); dnp->dn_spec = NULL; free(dnp->dn_desc); dnp->dn_desc = NULL; break; case DT_NODE_CLAUSE: if (dnp->dn_pred != NULL) dt_node_free(dnp->dn_pred); if (dnp->dn_locals != NULL) dt_idhash_destroy(dnp->dn_locals); dt_node_list_free(&dnp->dn_pdescs); dt_node_list_free(&dnp->dn_acts); break; case DT_NODE_MEMBER: free(dnp->dn_membname); dnp->dn_membname = NULL; if (dnp->dn_membexpr != NULL) { dt_node_free(dnp->dn_membexpr); dnp->dn_membexpr = NULL; } break; case DT_NODE_PROVIDER: dt_node_list_free(&dnp->dn_probes); free(dnp->dn_provname); dnp->dn_provname = NULL; break; case DT_NODE_PROG: dt_node_list_free(&dnp->dn_list); break; } } void dt_node_attr_assign(dt_node_t *dnp, dtrace_attribute_t attr) { if ((yypcb->pcb_cflags & DTRACE_C_EATTR) && (dt_attr_cmp(attr, yypcb->pcb_amin) < 0)) { char a[DTRACE_ATTR2STR_MAX]; char s[BUFSIZ]; dnerror(dnp, D_ATTR_MIN, "attributes for %s (%s) are less than " "predefined minimum\n", dt_node_name(dnp, s, sizeof (s)), dtrace_attr2str(attr, a, sizeof (a))); } dnp->dn_attr = attr; } void -dt_node_type_assign(dt_node_t *dnp, ctf_file_t *fp, ctf_id_t type) +dt_node_type_assign(dt_node_t *dnp, ctf_file_t *fp, ctf_id_t type, + boolean_t user) { ctf_id_t base = ctf_type_resolve(fp, type); uint_t kind = ctf_type_kind(fp, base); ctf_encoding_t e; dnp->dn_flags &= ~(DT_NF_SIGNED | DT_NF_REF | DT_NF_BITFIELD | DT_NF_USERLAND); if (kind == CTF_K_INTEGER && ctf_type_encoding(fp, base, &e) == 0) { size_t size = e.cte_bits / NBBY; if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1))) dnp->dn_flags |= DT_NF_BITFIELD; if (e.cte_format & CTF_INT_SIGNED) dnp->dn_flags |= DT_NF_SIGNED; } if (kind == CTF_K_FLOAT && ctf_type_encoding(fp, base, &e) == 0) { if (e.cte_bits / NBBY > sizeof (uint64_t)) dnp->dn_flags |= DT_NF_REF; } if (kind == CTF_K_STRUCT || kind == CTF_K_UNION || kind == CTF_K_FORWARD || kind == CTF_K_ARRAY || kind == CTF_K_FUNCTION) dnp->dn_flags |= DT_NF_REF; else if (yypcb != NULL && fp == DT_DYN_CTFP(yypcb->pcb_hdl) && type == DT_DYN_TYPE(yypcb->pcb_hdl)) dnp->dn_flags |= DT_NF_REF; + if (user) + dnp->dn_flags |= DT_NF_USERLAND; + dnp->dn_flags |= DT_NF_COOKED; dnp->dn_ctfp = fp; dnp->dn_type = type; } void dt_node_type_propagate(const dt_node_t *src, dt_node_t *dst) { assert(src->dn_flags & DT_NF_COOKED); dst->dn_flags = src->dn_flags & ~DT_NF_LVALUE; dst->dn_ctfp = src->dn_ctfp; dst->dn_type = src->dn_type; } const char * dt_node_type_name(const dt_node_t *dnp, char *buf, size_t len) { if (dt_node_is_dynamic(dnp) && dnp->dn_ident != NULL) { (void) snprintf(buf, len, "%s", dt_idkind_name(dt_ident_resolve(dnp->dn_ident)->di_kind)); return (buf); } if (dnp->dn_flags & DT_NF_USERLAND) { size_t n = snprintf(buf, len, "userland "); len = len > n ? len - n : 0; (void) dt_type_name(dnp->dn_ctfp, dnp->dn_type, buf + n, len); return (buf); } return (dt_type_name(dnp->dn_ctfp, dnp->dn_type, buf, len)); } size_t dt_node_type_size(const dt_node_t *dnp) { ctf_id_t base; + dtrace_hdl_t *dtp = yypcb->pcb_hdl; if (dnp->dn_kind == DT_NODE_STRING) return (strlen(dnp->dn_string) + 1); if (dt_node_is_dynamic(dnp) && dnp->dn_ident != NULL) return (dt_ident_size(dnp->dn_ident)); base = ctf_type_resolve(dnp->dn_ctfp, dnp->dn_type); if (ctf_type_kind(dnp->dn_ctfp, base) == CTF_K_FORWARD) return (0); + /* + * Here we have a 32-bit user pointer that is being used with a 64-bit + * kernel. When we're using it and its tagged as a userland reference -- + * then we need to keep it as a 32-bit pointer. However, if we are + * referring to it as a kernel address, eg. being used after a copyin() + * then we need to make sure that we actually return the kernel's size + * of a pointer, 8 bytes. + */ + if (ctf_type_kind(dnp->dn_ctfp, base) == CTF_K_POINTER && + ctf_getmodel(dnp->dn_ctfp) == CTF_MODEL_ILP32 && + !(dnp->dn_flags & DT_NF_USERLAND) && + dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) + return (8); + return (ctf_type_size(dnp->dn_ctfp, dnp->dn_type)); } /* * Determine if the specified parse tree node references an identifier of the * specified kind, and if so return a pointer to it; otherwise return NULL. * This function resolves the identifier itself, following through any inlines. */ dt_ident_t * dt_node_resolve(const dt_node_t *dnp, uint_t idkind) { dt_ident_t *idp; switch (dnp->dn_kind) { case DT_NODE_VAR: case DT_NODE_SYM: case DT_NODE_FUNC: case DT_NODE_AGG: case DT_NODE_INLINE: case DT_NODE_PROBE: idp = dt_ident_resolve(dnp->dn_ident); return (idp->di_kind == idkind ? idp : NULL); } if (dt_node_is_dynamic(dnp)) { idp = dt_ident_resolve(dnp->dn_ident); return (idp->di_kind == idkind ? idp : NULL); } return (NULL); } size_t dt_node_sizeof(const dt_node_t *dnp) { dtrace_syminfo_t *sip; GElf_Sym sym; dtrace_hdl_t *dtp = yypcb->pcb_hdl; /* * The size of the node as used for the sizeof() operator depends on * the kind of the node. If the node is a SYM, the size is obtained * from the symbol table; if it is not a SYM, the size is determined * from the node's type. This is slightly different from C's sizeof() * operator in that (for example) when applied to a function, sizeof() * will evaluate to the length of the function rather than the size of * the function type. */ if (dnp->dn_kind != DT_NODE_SYM) return (dt_node_type_size(dnp)); sip = dnp->dn_ident->di_data; if (dtrace_lookup_by_name(dtp, sip->dts_object, sip->dts_name, &sym, NULL) == -1) return (0); return (sym.st_size); } int dt_node_is_integer(const dt_node_t *dnp) { ctf_file_t *fp = dnp->dn_ctfp; ctf_encoding_t e; ctf_id_t type; uint_t kind; assert(dnp->dn_flags & DT_NF_COOKED); type = ctf_type_resolve(fp, dnp->dn_type); kind = ctf_type_kind(fp, type); if (kind == CTF_K_INTEGER && ctf_type_encoding(fp, type, &e) == 0 && IS_VOID(e)) return (0); /* void integer */ return (kind == CTF_K_INTEGER || kind == CTF_K_ENUM); } int dt_node_is_float(const dt_node_t *dnp) { ctf_file_t *fp = dnp->dn_ctfp; ctf_encoding_t e; ctf_id_t type; uint_t kind; assert(dnp->dn_flags & DT_NF_COOKED); type = ctf_type_resolve(fp, dnp->dn_type); kind = ctf_type_kind(fp, type); return (kind == CTF_K_FLOAT && ctf_type_encoding(dnp->dn_ctfp, type, &e) == 0 && ( e.cte_format == CTF_FP_SINGLE || e.cte_format == CTF_FP_DOUBLE || e.cte_format == CTF_FP_LDOUBLE)); } int dt_node_is_scalar(const dt_node_t *dnp) { ctf_file_t *fp = dnp->dn_ctfp; ctf_encoding_t e; ctf_id_t type; uint_t kind; assert(dnp->dn_flags & DT_NF_COOKED); type = ctf_type_resolve(fp, dnp->dn_type); kind = ctf_type_kind(fp, type); if (kind == CTF_K_INTEGER && ctf_type_encoding(fp, type, &e) == 0 && IS_VOID(e)) return (0); /* void cannot be used as a scalar */ return (kind == CTF_K_INTEGER || kind == CTF_K_ENUM || kind == CTF_K_POINTER); } int dt_node_is_arith(const dt_node_t *dnp) { ctf_file_t *fp = dnp->dn_ctfp; ctf_encoding_t e; ctf_id_t type; uint_t kind; assert(dnp->dn_flags & DT_NF_COOKED); type = ctf_type_resolve(fp, dnp->dn_type); kind = ctf_type_kind(fp, type); if (kind == CTF_K_INTEGER) return (ctf_type_encoding(fp, type, &e) == 0 && !IS_VOID(e)); else return (kind == CTF_K_ENUM); } int dt_node_is_vfptr(const dt_node_t *dnp) { ctf_file_t *fp = dnp->dn_ctfp; ctf_encoding_t e; ctf_id_t type; uint_t kind; assert(dnp->dn_flags & DT_NF_COOKED); type = ctf_type_resolve(fp, dnp->dn_type); if (ctf_type_kind(fp, type) != CTF_K_POINTER) return (0); /* type is not a pointer */ type = ctf_type_resolve(fp, ctf_type_reference(fp, type)); kind = ctf_type_kind(fp, type); return (kind == CTF_K_FUNCTION || (kind == CTF_K_INTEGER && ctf_type_encoding(fp, type, &e) == 0 && IS_VOID(e))); } int dt_node_is_dynamic(const dt_node_t *dnp) { if (dnp->dn_kind == DT_NODE_VAR && (dnp->dn_ident->di_flags & DT_IDFLG_INLINE)) { const dt_idnode_t *inp = dnp->dn_ident->di_iarg; return (inp->din_root ? dt_node_is_dynamic(inp->din_root) : 0); } return (dnp->dn_ctfp == DT_DYN_CTFP(yypcb->pcb_hdl) && dnp->dn_type == DT_DYN_TYPE(yypcb->pcb_hdl)); } int dt_node_is_string(const dt_node_t *dnp) { return (dnp->dn_ctfp == DT_STR_CTFP(yypcb->pcb_hdl) && dnp->dn_type == DT_STR_TYPE(yypcb->pcb_hdl)); } int dt_node_is_stack(const dt_node_t *dnp) { return (dnp->dn_ctfp == DT_STACK_CTFP(yypcb->pcb_hdl) && dnp->dn_type == DT_STACK_TYPE(yypcb->pcb_hdl)); } int dt_node_is_symaddr(const dt_node_t *dnp) { return (dnp->dn_ctfp == DT_SYMADDR_CTFP(yypcb->pcb_hdl) && dnp->dn_type == DT_SYMADDR_TYPE(yypcb->pcb_hdl)); } int dt_node_is_usymaddr(const dt_node_t *dnp) { return (dnp->dn_ctfp == DT_USYMADDR_CTFP(yypcb->pcb_hdl) && dnp->dn_type == DT_USYMADDR_TYPE(yypcb->pcb_hdl)); } int dt_node_is_strcompat(const dt_node_t *dnp) { ctf_file_t *fp = dnp->dn_ctfp; ctf_encoding_t e; ctf_arinfo_t r; ctf_id_t base; uint_t kind; assert(dnp->dn_flags & DT_NF_COOKED); base = ctf_type_resolve(fp, dnp->dn_type); kind = ctf_type_kind(fp, base); if (kind == CTF_K_POINTER && (base = ctf_type_reference(fp, base)) != CTF_ERR && (base = ctf_type_resolve(fp, base)) != CTF_ERR && ctf_type_encoding(fp, base, &e) == 0 && IS_CHAR(e)) return (1); /* promote char pointer to string */ if (kind == CTF_K_ARRAY && ctf_array_info(fp, base, &r) == 0 && (base = ctf_type_resolve(fp, r.ctr_contents)) != CTF_ERR && ctf_type_encoding(fp, base, &e) == 0 && IS_CHAR(e)) return (1); /* promote char array to string */ return (0); } int dt_node_is_pointer(const dt_node_t *dnp) { ctf_file_t *fp = dnp->dn_ctfp; uint_t kind; assert(dnp->dn_flags & DT_NF_COOKED); if (dt_node_is_string(dnp)) return (0); /* string are pass-by-ref but act like structs */ kind = ctf_type_kind(fp, ctf_type_resolve(fp, dnp->dn_type)); return (kind == CTF_K_POINTER || kind == CTF_K_ARRAY); } int dt_node_is_void(const dt_node_t *dnp) { ctf_file_t *fp = dnp->dn_ctfp; ctf_encoding_t e; ctf_id_t type; if (dt_node_is_dynamic(dnp)) return (0); /* is an alias for void but not the same */ if (dt_node_is_stack(dnp)) return (0); if (dt_node_is_symaddr(dnp) || dt_node_is_usymaddr(dnp)) return (0); type = ctf_type_resolve(fp, dnp->dn_type); return (ctf_type_kind(fp, type) == CTF_K_INTEGER && ctf_type_encoding(fp, type, &e) == 0 && IS_VOID(e)); } int dt_node_is_ptrcompat(const dt_node_t *lp, const dt_node_t *rp, ctf_file_t **fpp, ctf_id_t *tp) { ctf_file_t *lfp = lp->dn_ctfp; ctf_file_t *rfp = rp->dn_ctfp; ctf_id_t lbase = CTF_ERR, rbase = CTF_ERR; ctf_id_t lref = CTF_ERR, rref = CTF_ERR; int lp_is_void, rp_is_void, lp_is_int, rp_is_int, compat; uint_t lkind, rkind; ctf_encoding_t e; ctf_arinfo_t r; assert(lp->dn_flags & DT_NF_COOKED); assert(rp->dn_flags & DT_NF_COOKED); if (dt_node_is_dynamic(lp) || dt_node_is_dynamic(rp)) return (0); /* fail if either node is a dynamic variable */ lp_is_int = dt_node_is_integer(lp); rp_is_int = dt_node_is_integer(rp); if (lp_is_int && rp_is_int) return (0); /* fail if both nodes are integers */ if (lp_is_int && (lp->dn_kind != DT_NODE_INT || lp->dn_value != 0)) return (0); /* fail if lp is an integer that isn't 0 constant */ if (rp_is_int && (rp->dn_kind != DT_NODE_INT || rp->dn_value != 0)) return (0); /* fail if rp is an integer that isn't 0 constant */ if ((lp_is_int == 0 && rp_is_int == 0) && ( (lp->dn_flags & DT_NF_USERLAND) ^ (rp->dn_flags & DT_NF_USERLAND))) return (0); /* fail if only one pointer is a userland address */ /* * Resolve the left-hand and right-hand types to their base type, and * then resolve the referenced type as well (assuming the base type * is CTF_K_POINTER or CTF_K_ARRAY). Otherwise [lr]ref = CTF_ERR. */ if (!lp_is_int) { lbase = ctf_type_resolve(lfp, lp->dn_type); lkind = ctf_type_kind(lfp, lbase); if (lkind == CTF_K_POINTER) { lref = ctf_type_resolve(lfp, ctf_type_reference(lfp, lbase)); } else if (lkind == CTF_K_ARRAY && ctf_array_info(lfp, lbase, &r) == 0) { lref = ctf_type_resolve(lfp, r.ctr_contents); } } if (!rp_is_int) { rbase = ctf_type_resolve(rfp, rp->dn_type); rkind = ctf_type_kind(rfp, rbase); if (rkind == CTF_K_POINTER) { rref = ctf_type_resolve(rfp, ctf_type_reference(rfp, rbase)); } else if (rkind == CTF_K_ARRAY && ctf_array_info(rfp, rbase, &r) == 0) { rref = ctf_type_resolve(rfp, r.ctr_contents); } } /* * We know that one or the other type may still be a zero-valued * integer constant. To simplify the code below, set the integer * type variables equal to the non-integer types and proceed. */ if (lp_is_int) { lbase = rbase; lkind = rkind; lref = rref; lfp = rfp; } else if (rp_is_int) { rbase = lbase; rkind = lkind; rref = lref; rfp = lfp; } lp_is_void = ctf_type_encoding(lfp, lref, &e) == 0 && IS_VOID(e); rp_is_void = ctf_type_encoding(rfp, rref, &e) == 0 && IS_VOID(e); /* * The types are compatible if both are pointers to the same type, or * if either pointer is a void pointer. If they are compatible, set * tp to point to the more specific pointer type and return it. */ compat = (lkind == CTF_K_POINTER || lkind == CTF_K_ARRAY) && (rkind == CTF_K_POINTER || rkind == CTF_K_ARRAY) && (lp_is_void || rp_is_void || ctf_type_compat(lfp, lref, rfp, rref)); if (compat) { if (fpp != NULL) *fpp = rp_is_void ? lfp : rfp; if (tp != NULL) *tp = rp_is_void ? lbase : rbase; } return (compat); } /* * The rules for checking argument types against parameter types are described * in the ANSI-C spec (see K&R[A7.3.2] and K&R[A7.17]). We use the same rule * set to determine whether associative array arguments match the prototype. */ int dt_node_is_argcompat(const dt_node_t *lp, const dt_node_t *rp) { ctf_file_t *lfp = lp->dn_ctfp; ctf_file_t *rfp = rp->dn_ctfp; assert(lp->dn_flags & DT_NF_COOKED); assert(rp->dn_flags & DT_NF_COOKED); if (dt_node_is_integer(lp) && dt_node_is_integer(rp)) return (1); /* integer types are compatible */ if (dt_node_is_strcompat(lp) && dt_node_is_strcompat(rp)) return (1); /* string types are compatible */ if (dt_node_is_stack(lp) && dt_node_is_stack(rp)) return (1); /* stack types are compatible */ if (dt_node_is_symaddr(lp) && dt_node_is_symaddr(rp)) return (1); /* symaddr types are compatible */ if (dt_node_is_usymaddr(lp) && dt_node_is_usymaddr(rp)) return (1); /* usymaddr types are compatible */ switch (ctf_type_kind(lfp, ctf_type_resolve(lfp, lp->dn_type))) { case CTF_K_FUNCTION: case CTF_K_STRUCT: case CTF_K_UNION: return (ctf_type_compat(lfp, lp->dn_type, rfp, rp->dn_type)); default: return (dt_node_is_ptrcompat(lp, rp, NULL, NULL)); } } /* * We provide dt_node_is_posconst() as a convenience routine for callers who * wish to verify that an argument is a positive non-zero integer constant. */ int dt_node_is_posconst(const dt_node_t *dnp) { return (dnp->dn_kind == DT_NODE_INT && dnp->dn_value != 0 && ( (dnp->dn_flags & DT_NF_SIGNED) == 0 || (int64_t)dnp->dn_value > 0)); } int dt_node_is_actfunc(const dt_node_t *dnp) { return (dnp->dn_kind == DT_NODE_FUNC && dnp->dn_ident->di_kind == DT_IDENT_ACTFUNC); } /* * The original rules for integer constant typing are described in K&R[A2.5.1]. * However, since we support long long, we instead use the rules from ISO C99 * clause 6.4.4.1 since that is where long longs are formally described. The * rules require us to know whether the constant was specified in decimal or * in octal or hex, which we do by looking at our lexer's 'yyintdecimal' flag. * The type of an integer constant is the first of the corresponding list in * which its value can be represented: * * unsuffixed decimal: int, long, long long * unsuffixed oct/hex: int, unsigned int, long, unsigned long, * long long, unsigned long long * suffix [uU]: unsigned int, unsigned long, unsigned long long * suffix [lL] decimal: long, long long * suffix [lL] oct/hex: long, unsigned long, long long, unsigned long long * suffix [uU][Ll]: unsigned long, unsigned long long * suffix ll/LL decimal: long long * suffix ll/LL oct/hex: long long, unsigned long long * suffix [uU][ll/LL]: unsigned long long * * Given that our lexer has already validated the suffixes by regexp matching, * there is an obvious way to concisely encode these rules: construct an array * of the types in the order int, unsigned int, long, unsigned long, long long, * unsigned long long. Compute an integer array starting index based on the * suffix (e.g. none = 0, u = 1, ull = 5), and compute an increment based on * the specifier (dec/oct/hex) and suffix (u). Then iterate from the starting * index to the end, advancing using the increment, and searching until we * find a limit that matches or we run out of choices (overflow). To make it * even faster, we precompute the table of type information in dtrace_open(). */ dt_node_t * dt_node_int(uintmax_t value) { dt_node_t *dnp = dt_node_alloc(DT_NODE_INT); dtrace_hdl_t *dtp = yypcb->pcb_hdl; int n = (yyintdecimal | (yyintsuffix[0] == 'u')) + 1; int i = 0; const char *p; char c; dnp->dn_op = DT_TOK_INT; dnp->dn_value = value; for (p = yyintsuffix; (c = *p) != '\0'; p++) { if (c == 'U' || c == 'u') i += 1; else if (c == 'L' || c == 'l') i += 2; } for (; i < sizeof (dtp->dt_ints) / sizeof (dtp->dt_ints[0]); i += n) { if (value <= dtp->dt_ints[i].did_limit) { dt_node_type_assign(dnp, dtp->dt_ints[i].did_ctfp, - dtp->dt_ints[i].did_type); + dtp->dt_ints[i].did_type, B_FALSE); /* * If a prefix character is present in macro text, add * in the corresponding operator node (see dt_lex.l). */ switch (yyintprefix) { case '+': return (dt_node_op1(DT_TOK_IPOS, dnp)); case '-': return (dt_node_op1(DT_TOK_INEG, dnp)); default: return (dnp); } } } xyerror(D_INT_OFLOW, "integer constant 0x%llx cannot be represented " "in any built-in integral type\n", (u_longlong_t)value); /*NOTREACHED*/ return (NULL); /* keep gcc happy */ } dt_node_t * dt_node_string(char *string) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_node_t *dnp; if (string == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dnp = dt_node_alloc(DT_NODE_STRING); dnp->dn_op = DT_TOK_STRING; dnp->dn_string = string; - dt_node_type_assign(dnp, DT_STR_CTFP(dtp), DT_STR_TYPE(dtp)); + dt_node_type_assign(dnp, DT_STR_CTFP(dtp), DT_STR_TYPE(dtp), B_FALSE); return (dnp); } dt_node_t * dt_node_ident(char *name) { dt_ident_t *idp; dt_node_t *dnp; if (name == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); /* * If the identifier is an inlined integer constant, then create an INT * node that is a clone of the inline parse tree node and return that * immediately, allowing this inline to be used in parsing contexts * that require constant expressions (e.g. scalar array sizes). */ if ((idp = dt_idstack_lookup(&yypcb->pcb_globals, name)) != NULL && (idp->di_flags & DT_IDFLG_INLINE)) { dt_idnode_t *inp = idp->di_iarg; if (inp->din_root != NULL && inp->din_root->dn_kind == DT_NODE_INT) { free(name); dnp = dt_node_alloc(DT_NODE_INT); dnp->dn_op = DT_TOK_INT; dnp->dn_value = inp->din_root->dn_value; dt_node_type_propagate(inp->din_root, dnp); return (dnp); } } dnp = dt_node_alloc(DT_NODE_IDENT); dnp->dn_op = name[0] == '@' ? DT_TOK_AGG : DT_TOK_IDENT; dnp->dn_string = name; return (dnp); } /* * Create an empty node of type corresponding to the given declaration. * Explicit references to user types (C or D) are assigned the default * stability; references to other types are _dtrace_typattr (Private). */ dt_node_t * dt_node_type(dt_decl_t *ddp) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dtrace_typeinfo_t dtt; dt_node_t *dnp; char *name = NULL; int err; /* * If 'ddp' is NULL, we get a decl by popping the decl stack. This * form of dt_node_type() is used by parameter rules in dt_grammar.y. */ if (ddp == NULL) ddp = dt_decl_pop_param(&name); err = dt_decl_type(ddp, &dtt); dt_decl_free(ddp); if (err != 0) { free(name); longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } dnp = dt_node_alloc(DT_NODE_TYPE); dnp->dn_op = DT_TOK_IDENT; dnp->dn_string = name; - dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type); + dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type, dtt.dtt_flags); + if (dtt.dtt_ctfp == dtp->dt_cdefs->dm_ctfp || dtt.dtt_ctfp == dtp->dt_ddefs->dm_ctfp) dt_node_attr_assign(dnp, _dtrace_defattr); else dt_node_attr_assign(dnp, _dtrace_typattr); return (dnp); } /* * Create a type node corresponding to a varargs (...) parameter by just * assigning it type CTF_ERR. The decl processing code will handle this. */ dt_node_t * dt_node_vatype(void) { dt_node_t *dnp = dt_node_alloc(DT_NODE_TYPE); dnp->dn_op = DT_TOK_IDENT; dnp->dn_ctfp = yypcb->pcb_hdl->dt_cdefs->dm_ctfp; dnp->dn_type = CTF_ERR; dnp->dn_attr = _dtrace_defattr; return (dnp); } /* * Instantiate a decl using the contents of the current declaration stack. As * we do not currently permit decls to be initialized, this function currently * returns NULL and no parse node is created. When this function is called, * the topmost scope's ds_ident pointer will be set to NULL (indicating no * init_declarator rule was matched) or will point to the identifier to use. */ dt_node_t * dt_node_decl(void) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_scope_t *dsp = &yypcb->pcb_dstack; dt_dclass_t class = dsp->ds_class; dt_decl_t *ddp = dt_decl_top(); dt_module_t *dmp; dtrace_typeinfo_t dtt; ctf_id_t type; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; if (dt_decl_type(ddp, &dtt) != 0) longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); /* * If we have no declaration identifier, then this is either a spurious * declaration of an intrinsic type (e.g. "extern int;") or declaration * or redeclaration of a struct, union, or enum type or tag. */ if (dsp->ds_ident == NULL) { if (ddp->dd_kind != CTF_K_STRUCT && ddp->dd_kind != CTF_K_UNION && ddp->dd_kind != CTF_K_ENUM) xyerror(D_DECL_USELESS, "useless declaration\n"); dt_dprintf("type %s added as id %ld\n", dt_type_name( ddp->dd_ctfp, ddp->dd_type, n1, sizeof (n1)), ddp->dd_type); return (NULL); } if (strchr(dsp->ds_ident, '`') != NULL) { xyerror(D_DECL_SCOPE, "D scoping operator may not be used in " "a declaration name (%s)\n", dsp->ds_ident); } /* * If we are nested inside of a C include file, add the declaration to * the C definition module; otherwise use the D definition module. */ if (yypcb->pcb_idepth != 0) dmp = dtp->dt_cdefs; else dmp = dtp->dt_ddefs; /* * If we see a global or static declaration of a function prototype, * treat this as equivalent to a D extern declaration. */ if (ctf_type_kind(dtt.dtt_ctfp, dtt.dtt_type) == CTF_K_FUNCTION && (class == DT_DC_DEFAULT || class == DT_DC_STATIC)) class = DT_DC_EXTERN; switch (class) { case DT_DC_AUTO: case DT_DC_REGISTER: case DT_DC_STATIC: xyerror(D_DECL_BADCLASS, "specified storage class not " "appropriate in D\n"); /*NOTREACHED*/ case DT_DC_EXTERN: { dtrace_typeinfo_t ott; dtrace_syminfo_t dts; GElf_Sym sym; int exists = dtrace_lookup_by_name(dtp, dmp->dm_name, dsp->ds_ident, &sym, &dts) == 0; if (exists && (dtrace_symbol_type(dtp, &sym, &dts, &ott) != 0 || ctf_type_cmp(dtt.dtt_ctfp, dtt.dtt_type, ott.dtt_ctfp, ott.dtt_type) != 0)) { xyerror(D_DECL_IDRED, "identifier redeclared: %s`%s\n" "\t current: %s\n\tprevious: %s\n", dmp->dm_name, dsp->ds_ident, dt_type_name(dtt.dtt_ctfp, dtt.dtt_type, n1, sizeof (n1)), dt_type_name(ott.dtt_ctfp, ott.dtt_type, n2, sizeof (n2))); } else if (!exists && dt_module_extern(dtp, dmp, dsp->ds_ident, &dtt) == NULL) { xyerror(D_UNKNOWN, "failed to extern %s: %s\n", dsp->ds_ident, dtrace_errmsg(dtp, dtrace_errno(dtp))); } else { dt_dprintf("extern %s`%s type=<%s>\n", dmp->dm_name, dsp->ds_ident, dt_type_name(dtt.dtt_ctfp, dtt.dtt_type, n1, sizeof (n1))); } break; } case DT_DC_TYPEDEF: if (dt_idstack_lookup(&yypcb->pcb_globals, dsp->ds_ident)) { xyerror(D_DECL_IDRED, "global variable identifier " "redeclared: %s\n", dsp->ds_ident); } if (ctf_lookup_by_name(dmp->dm_ctfp, dsp->ds_ident) != CTF_ERR) { xyerror(D_DECL_IDRED, "typedef redeclared: %s\n", dsp->ds_ident); } /* * If the source type for the typedef is not defined in the * target container or its parent, copy the type to the target * container and reset dtt_ctfp and dtt_type to the copy. */ if (dtt.dtt_ctfp != dmp->dm_ctfp && dtt.dtt_ctfp != ctf_parent_file(dmp->dm_ctfp)) { dtt.dtt_type = ctf_add_type(dmp->dm_ctfp, dtt.dtt_ctfp, dtt.dtt_type); dtt.dtt_ctfp = dmp->dm_ctfp; if (dtt.dtt_type == CTF_ERR || ctf_update(dtt.dtt_ctfp) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to copy typedef %s " "source type: %s\n", dsp->ds_ident, ctf_errmsg(ctf_errno(dtt.dtt_ctfp))); } } type = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, dsp->ds_ident, dtt.dtt_type); if (type == CTF_ERR || ctf_update(dmp->dm_ctfp) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to typedef %s: %s\n", dsp->ds_ident, ctf_errmsg(ctf_errno(dmp->dm_ctfp))); } dt_dprintf("typedef %s added as id %ld\n", dsp->ds_ident, type); break; default: { ctf_encoding_t cte; dt_idhash_t *dhp; dt_ident_t *idp; dt_node_t idn; int assc, idkind; uint_t id, kind; ushort_t idflags; switch (class) { case DT_DC_THIS: dhp = yypcb->pcb_locals; idflags = DT_IDFLG_LOCAL; idp = dt_idhash_lookup(dhp, dsp->ds_ident); break; case DT_DC_SELF: dhp = dtp->dt_tls; idflags = DT_IDFLG_TLS; idp = dt_idhash_lookup(dhp, dsp->ds_ident); break; default: dhp = dtp->dt_globals; idflags = 0; idp = dt_idstack_lookup( &yypcb->pcb_globals, dsp->ds_ident); break; } if (ddp->dd_kind == CTF_K_ARRAY && ddp->dd_node == NULL) { xyerror(D_DECL_ARRNULL, "array declaration requires array dimension or " "tuple signature: %s\n", dsp->ds_ident); } if (idp != NULL && idp->di_gen == 0) { xyerror(D_DECL_IDRED, "built-in identifier " "redeclared: %s\n", idp->di_name); } if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_CDEFS, dsp->ds_ident, NULL) == 0 || dtrace_lookup_by_type(dtp, DTRACE_OBJ_DDEFS, dsp->ds_ident, NULL) == 0) { xyerror(D_DECL_IDRED, "typedef identifier " "redeclared: %s\n", dsp->ds_ident); } /* * Cache some attributes of the decl to make the rest of this * code simpler: if the decl is an array which is subscripted * by a type rather than an integer, then it's an associative * array (assc). We then expect to match either DT_IDENT_ARRAY * for associative arrays or DT_IDENT_SCALAR for anything else. */ assc = ddp->dd_kind == CTF_K_ARRAY && ddp->dd_node->dn_kind == DT_NODE_TYPE; idkind = assc ? DT_IDENT_ARRAY : DT_IDENT_SCALAR; /* * Create a fake dt_node_t on the stack so we can determine the * type of any matching identifier by assigning to this node. * If the pre-existing ident has its di_type set, propagate * the type by hand so as not to trigger a prototype check for * arrays (yet); otherwise we use dt_ident_cook() on the ident * to ensure it is fully initialized before looking at it. */ bzero(&idn, sizeof (dt_node_t)); if (idp != NULL && idp->di_type != CTF_ERR) - dt_node_type_assign(&idn, idp->di_ctfp, idp->di_type); + dt_node_type_assign(&idn, idp->di_ctfp, idp->di_type, + B_FALSE); else if (idp != NULL) (void) dt_ident_cook(&idn, idp, NULL); if (assc) { if (class == DT_DC_THIS) { xyerror(D_DECL_LOCASSC, "associative arrays " "may not be declared as local variables:" " %s\n", dsp->ds_ident); } if (dt_decl_type(ddp->dd_next, &dtt) != 0) longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } if (idp != NULL && (idp->di_kind != idkind || ctf_type_cmp(dtt.dtt_ctfp, dtt.dtt_type, idn.dn_ctfp, idn.dn_type) != 0)) { xyerror(D_DECL_IDRED, "identifier redeclared: %s\n" "\t current: %s %s\n\tprevious: %s %s\n", dsp->ds_ident, dt_idkind_name(idkind), dt_type_name(dtt.dtt_ctfp, dtt.dtt_type, n1, sizeof (n1)), dt_idkind_name(idp->di_kind), dt_node_type_name(&idn, n2, sizeof (n2))); } else if (idp != NULL && assc) { const dt_idsig_t *isp = idp->di_data; dt_node_t *dnp = ddp->dd_node; int argc = 0; for (; dnp != NULL; dnp = dnp->dn_list, argc++) { const dt_node_t *pnp = &isp->dis_args[argc]; if (argc >= isp->dis_argc) continue; /* tuple length mismatch */ if (ctf_type_cmp(dnp->dn_ctfp, dnp->dn_type, pnp->dn_ctfp, pnp->dn_type) == 0) continue; xyerror(D_DECL_IDRED, "identifier redeclared: %s\n" "\t current: %s, key #%d of type %s\n" "\tprevious: %s, key #%d of type %s\n", dsp->ds_ident, dt_idkind_name(idkind), argc + 1, dt_node_type_name(dnp, n1, sizeof (n1)), dt_idkind_name(idp->di_kind), argc + 1, dt_node_type_name(pnp, n2, sizeof (n2))); } if (isp->dis_argc != argc) { xyerror(D_DECL_IDRED, "identifier redeclared: %s\n" "\t current: %s of %s, tuple length %d\n" "\tprevious: %s of %s, tuple length %d\n", dsp->ds_ident, dt_idkind_name(idkind), dt_type_name(dtt.dtt_ctfp, dtt.dtt_type, n1, sizeof (n1)), argc, dt_idkind_name(idp->di_kind), dt_node_type_name(&idn, n2, sizeof (n2)), isp->dis_argc); } } else if (idp == NULL) { type = ctf_type_resolve(dtt.dtt_ctfp, dtt.dtt_type); kind = ctf_type_kind(dtt.dtt_ctfp, type); switch (kind) { case CTF_K_INTEGER: if (ctf_type_encoding(dtt.dtt_ctfp, type, &cte) == 0 && IS_VOID(cte)) { xyerror(D_DECL_VOIDOBJ, "cannot have " "void object: %s\n", dsp->ds_ident); } break; case CTF_K_STRUCT: case CTF_K_UNION: if (ctf_type_size(dtt.dtt_ctfp, type) != 0) break; /* proceed to declaring */ /*FALLTHRU*/ case CTF_K_FORWARD: xyerror(D_DECL_INCOMPLETE, "incomplete struct/union/enum %s: %s\n", dt_type_name(dtt.dtt_ctfp, dtt.dtt_type, n1, sizeof (n1)), dsp->ds_ident); /*NOTREACHED*/ } if (dt_idhash_nextid(dhp, &id) == -1) { xyerror(D_ID_OFLOW, "cannot create %s: limit " "on number of %s variables exceeded\n", dsp->ds_ident, dt_idhash_name(dhp)); } dt_dprintf("declare %s %s variable %s, id=%u\n", dt_idhash_name(dhp), dt_idkind_name(idkind), dsp->ds_ident, id); idp = dt_idhash_insert(dhp, dsp->ds_ident, idkind, idflags | DT_IDFLG_WRITE | DT_IDFLG_DECL, id, _dtrace_defattr, 0, assc ? &dt_idops_assc : &dt_idops_thaw, NULL, dtp->dt_gen); if (idp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dt_ident_type_assign(idp, dtt.dtt_ctfp, dtt.dtt_type); /* * If we are declaring an associative array, use our * fake parse node to cook the new assoc identifier. * This will force the ident code to instantiate the * array type signature corresponding to the list of * types pointed to by ddp->dd_node. We also reset * the identifier's attributes based upon the result. */ if (assc) { idp->di_attr = dt_ident_cook(&idn, idp, &ddp->dd_node); } } } } /* end of switch */ free(dsp->ds_ident); dsp->ds_ident = NULL; return (NULL); } dt_node_t * dt_node_func(dt_node_t *dnp, dt_node_t *args) { dt_ident_t *idp; if (dnp->dn_kind != DT_NODE_IDENT) { xyerror(D_FUNC_IDENT, "function designator is not of function type\n"); } idp = dt_idstack_lookup(&yypcb->pcb_globals, dnp->dn_string); if (idp == NULL) { xyerror(D_FUNC_UNDEF, "undefined function name: %s\n", dnp->dn_string); } if (idp->di_kind != DT_IDENT_FUNC && idp->di_kind != DT_IDENT_AGGFUNC && idp->di_kind != DT_IDENT_ACTFUNC) { xyerror(D_FUNC_IDKIND, "%s '%s' may not be referenced as a " "function\n", dt_idkind_name(idp->di_kind), idp->di_name); } free(dnp->dn_string); dnp->dn_string = NULL; dnp->dn_kind = DT_NODE_FUNC; dnp->dn_flags &= ~DT_NF_COOKED; dnp->dn_ident = idp; dnp->dn_args = args; dnp->dn_list = NULL; return (dnp); } /* * The offsetof() function is special because it takes a type name as an * argument. It does not actually construct its own node; after looking up the * structure or union offset, we just return an integer node with the offset. */ dt_node_t * dt_node_offsetof(dt_decl_t *ddp, char *s) { dtrace_typeinfo_t dtt; dt_node_t dn; char *name; int err; ctf_membinfo_t ctm; ctf_id_t type; uint_t kind; name = alloca(strlen(s) + 1); (void) strcpy(name, s); free(s); err = dt_decl_type(ddp, &dtt); dt_decl_free(ddp); if (err != 0) longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); type = ctf_type_resolve(dtt.dtt_ctfp, dtt.dtt_type); kind = ctf_type_kind(dtt.dtt_ctfp, type); if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) { xyerror(D_OFFSETOF_TYPE, "offsetof operand must be a struct or union type\n"); } if (ctf_member_info(dtt.dtt_ctfp, type, name, &ctm) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to determine offset of %s: %s\n", name, ctf_errmsg(ctf_errno(dtt.dtt_ctfp))); } bzero(&dn, sizeof (dn)); - dt_node_type_assign(&dn, dtt.dtt_ctfp, ctm.ctm_type); + dt_node_type_assign(&dn, dtt.dtt_ctfp, ctm.ctm_type, B_FALSE); if (dn.dn_flags & DT_NF_BITFIELD) { xyerror(D_OFFSETOF_BITFIELD, "cannot take offset of a bit-field: %s\n", name); } return (dt_node_int(ctm.ctm_offset / NBBY)); } dt_node_t * dt_node_op1(int op, dt_node_t *cp) { dt_node_t *dnp; if (cp->dn_kind == DT_NODE_INT) { switch (op) { case DT_TOK_INEG: /* * If we're negating an unsigned integer, zero out any * extra top bits to truncate the value to the size of * the effective type determined by dt_node_int(). */ cp->dn_value = -cp->dn_value; if (!(cp->dn_flags & DT_NF_SIGNED)) { cp->dn_value &= ~0ULL >> (64 - dt_node_type_size(cp) * NBBY); } /*FALLTHRU*/ case DT_TOK_IPOS: return (cp); case DT_TOK_BNEG: cp->dn_value = ~cp->dn_value; return (cp); case DT_TOK_LNEG: cp->dn_value = !cp->dn_value; return (cp); } } /* * If sizeof is applied to a type_name or string constant, we can * transform 'cp' into an integer constant in the node construction * pass so that it can then be used for arithmetic in this pass. */ if (op == DT_TOK_SIZEOF && (cp->dn_kind == DT_NODE_STRING || cp->dn_kind == DT_NODE_TYPE)) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; size_t size = dt_node_type_size(cp); if (size == 0) { xyerror(D_SIZEOF_TYPE, "cannot apply sizeof to an " "operand of unknown size\n"); } dt_node_type_assign(cp, dtp->dt_ddefs->dm_ctfp, - ctf_lookup_by_name(dtp->dt_ddefs->dm_ctfp, "size_t")); + ctf_lookup_by_name(dtp->dt_ddefs->dm_ctfp, "size_t"), + B_FALSE); cp->dn_kind = DT_NODE_INT; cp->dn_op = DT_TOK_INT; cp->dn_value = size; return (cp); } dnp = dt_node_alloc(DT_NODE_OP1); assert(op <= USHRT_MAX); dnp->dn_op = (ushort_t)op; dnp->dn_child = cp; return (dnp); } /* * If an integer constant is being cast to another integer type, we can * perform the cast as part of integer constant folding in this pass. We must * take action when the integer is being cast to a smaller type or if it is * changing signed-ness. If so, we first shift rp's bits bits high (losing * excess bits if narrowing) and then shift them down with either a logical * shift (unsigned) or arithmetic shift (signed). */ static void dt_cast(dt_node_t *lp, dt_node_t *rp) { size_t srcsize = dt_node_type_size(rp); size_t dstsize = dt_node_type_size(lp); if (dstsize < srcsize) { int n = (sizeof (uint64_t) - dstsize) * NBBY; rp->dn_value <<= n; rp->dn_value >>= n; } else if (dstsize > srcsize) { int n = (sizeof (uint64_t) - srcsize) * NBBY; int s = (dstsize - srcsize) * NBBY; rp->dn_value <<= n; if (rp->dn_flags & DT_NF_SIGNED) { rp->dn_value = (intmax_t)rp->dn_value >> s; rp->dn_value >>= n - s; } else { rp->dn_value >>= n; } } } dt_node_t * dt_node_op2(int op, dt_node_t *lp, dt_node_t *rp) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_node_t *dnp; /* * First we check for operations that are illegal -- namely those that * might result in integer division by zero, and abort if one is found. */ if (rp->dn_kind == DT_NODE_INT && rp->dn_value == 0 && (op == DT_TOK_MOD || op == DT_TOK_DIV || op == DT_TOK_MOD_EQ || op == DT_TOK_DIV_EQ)) xyerror(D_DIV_ZERO, "expression contains division by zero\n"); /* * If both children are immediate values, we can just perform inline * calculation and return a new immediate node with the result. */ if (lp->dn_kind == DT_NODE_INT && rp->dn_kind == DT_NODE_INT) { uintmax_t l = lp->dn_value; uintmax_t r = rp->dn_value; dnp = dt_node_int(0); /* allocate new integer node for result */ switch (op) { case DT_TOK_LOR: dnp->dn_value = l || r; dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_LXOR: dnp->dn_value = (l != 0) ^ (r != 0); dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_LAND: dnp->dn_value = l && r; dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_BOR: dnp->dn_value = l | r; dt_node_promote(lp, rp, dnp); break; case DT_TOK_XOR: dnp->dn_value = l ^ r; dt_node_promote(lp, rp, dnp); break; case DT_TOK_BAND: dnp->dn_value = l & r; dt_node_promote(lp, rp, dnp); break; case DT_TOK_EQU: dnp->dn_value = l == r; dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_NEQ: dnp->dn_value = l != r; dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_LT: dt_node_promote(lp, rp, dnp); if (dnp->dn_flags & DT_NF_SIGNED) dnp->dn_value = (intmax_t)l < (intmax_t)r; else dnp->dn_value = l < r; dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_LE: dt_node_promote(lp, rp, dnp); if (dnp->dn_flags & DT_NF_SIGNED) dnp->dn_value = (intmax_t)l <= (intmax_t)r; else dnp->dn_value = l <= r; dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_GT: dt_node_promote(lp, rp, dnp); if (dnp->dn_flags & DT_NF_SIGNED) dnp->dn_value = (intmax_t)l > (intmax_t)r; else dnp->dn_value = l > r; dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_GE: dt_node_promote(lp, rp, dnp); if (dnp->dn_flags & DT_NF_SIGNED) dnp->dn_value = (intmax_t)l >= (intmax_t)r; else dnp->dn_value = l >= r; dt_node_type_assign(dnp, - DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), B_FALSE); break; case DT_TOK_LSH: dnp->dn_value = l << r; dt_node_type_propagate(lp, dnp); dt_node_attr_assign(rp, dt_attr_min(lp->dn_attr, rp->dn_attr)); break; case DT_TOK_RSH: dnp->dn_value = l >> r; dt_node_type_propagate(lp, dnp); dt_node_attr_assign(rp, dt_attr_min(lp->dn_attr, rp->dn_attr)); break; case DT_TOK_ADD: dnp->dn_value = l + r; dt_node_promote(lp, rp, dnp); break; case DT_TOK_SUB: dnp->dn_value = l - r; dt_node_promote(lp, rp, dnp); break; case DT_TOK_MUL: dnp->dn_value = l * r; dt_node_promote(lp, rp, dnp); break; case DT_TOK_DIV: dt_node_promote(lp, rp, dnp); if (dnp->dn_flags & DT_NF_SIGNED) dnp->dn_value = (intmax_t)l / (intmax_t)r; else dnp->dn_value = l / r; break; case DT_TOK_MOD: dt_node_promote(lp, rp, dnp); if (dnp->dn_flags & DT_NF_SIGNED) dnp->dn_value = (intmax_t)l % (intmax_t)r; else dnp->dn_value = l % r; break; default: dt_node_free(dnp); dnp = NULL; } if (dnp != NULL) { dt_node_free(lp); dt_node_free(rp); return (dnp); } } if (op == DT_TOK_LPAR && rp->dn_kind == DT_NODE_INT && dt_node_is_integer(lp)) { dt_cast(lp, rp); dt_node_type_propagate(lp, rp); dt_node_attr_assign(rp, dt_attr_min(lp->dn_attr, rp->dn_attr)); dt_node_free(lp); return (rp); } /* * If no immediate optimizations are available, create an new OP2 node * and glue the left and right children into place and return. */ dnp = dt_node_alloc(DT_NODE_OP2); assert(op <= USHRT_MAX); dnp->dn_op = (ushort_t)op; dnp->dn_left = lp; dnp->dn_right = rp; return (dnp); } dt_node_t * dt_node_op3(dt_node_t *expr, dt_node_t *lp, dt_node_t *rp) { dt_node_t *dnp; if (expr->dn_kind == DT_NODE_INT) return (expr->dn_value != 0 ? lp : rp); dnp = dt_node_alloc(DT_NODE_OP3); dnp->dn_op = DT_TOK_QUESTION; dnp->dn_expr = expr; dnp->dn_left = lp; dnp->dn_right = rp; return (dnp); } dt_node_t * dt_node_statement(dt_node_t *expr) { dt_node_t *dnp; if (expr->dn_kind == DT_NODE_AGG) return (expr); if (expr->dn_kind == DT_NODE_FUNC && expr->dn_ident->di_kind == DT_IDENT_ACTFUNC) dnp = dt_node_alloc(DT_NODE_DFUNC); else dnp = dt_node_alloc(DT_NODE_DEXPR); dnp->dn_expr = expr; return (dnp); } dt_node_t * dt_node_pdesc_by_name(char *spec) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_node_t *dnp; if (spec == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dnp = dt_node_alloc(DT_NODE_PDESC); dnp->dn_spec = spec; dnp->dn_desc = malloc(sizeof (dtrace_probedesc_t)); if (dnp->dn_desc == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); if (dtrace_xstr2desc(dtp, yypcb->pcb_pspec, dnp->dn_spec, yypcb->pcb_sargc, yypcb->pcb_sargv, dnp->dn_desc) != 0) { xyerror(D_PDESC_INVAL, "invalid probe description \"%s\": %s\n", dnp->dn_spec, dtrace_errmsg(dtp, dtrace_errno(dtp))); } free(dnp->dn_spec); dnp->dn_spec = NULL; return (dnp); } dt_node_t * dt_node_pdesc_by_id(uintmax_t id) { static const char *const names[] = { "providers", "modules", "functions" }; dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_node_t *dnp = dt_node_alloc(DT_NODE_PDESC); if ((dnp->dn_desc = malloc(sizeof (dtrace_probedesc_t))) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); if (id > UINT_MAX) { xyerror(D_PDESC_INVAL, "identifier %llu exceeds maximum " "probe id\n", (u_longlong_t)id); } if (yypcb->pcb_pspec != DTRACE_PROBESPEC_NAME) { xyerror(D_PDESC_INVAL, "probe identifier %llu not permitted " "when specifying %s\n", (u_longlong_t)id, names[yypcb->pcb_pspec]); } if (dtrace_id2desc(dtp, (dtrace_id_t)id, dnp->dn_desc) != 0) { xyerror(D_PDESC_INVAL, "invalid probe identifier %llu: %s\n", (u_longlong_t)id, dtrace_errmsg(dtp, dtrace_errno(dtp))); } return (dnp); } dt_node_t * dt_node_clause(dt_node_t *pdescs, dt_node_t *pred, dt_node_t *acts) { dt_node_t *dnp = dt_node_alloc(DT_NODE_CLAUSE); dnp->dn_pdescs = pdescs; dnp->dn_pred = pred; dnp->dn_acts = acts; yybegin(YYS_CLAUSE); return (dnp); } dt_node_t * dt_node_inline(dt_node_t *expr) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_scope_t *dsp = &yypcb->pcb_dstack; dt_decl_t *ddp = dt_decl_top(); char n[DT_TYPE_NAMELEN]; dtrace_typeinfo_t dtt; dt_ident_t *idp, *rdp; dt_idnode_t *inp; dt_node_t *dnp; if (dt_decl_type(ddp, &dtt) != 0) longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); if (dsp->ds_class != DT_DC_DEFAULT) { xyerror(D_DECL_BADCLASS, "specified storage class not " "appropriate for inline declaration\n"); } if (dsp->ds_ident == NULL) xyerror(D_DECL_USELESS, "inline declaration requires a name\n"); if ((idp = dt_idstack_lookup( &yypcb->pcb_globals, dsp->ds_ident)) != NULL) { xyerror(D_DECL_IDRED, "identifier redefined: %s\n\t current: " "inline definition\n\tprevious: %s %s\n", idp->di_name, dt_idkind_name(idp->di_kind), (idp->di_flags & DT_IDFLG_INLINE) ? "inline" : ""); } /* * If we are declaring an inlined array, verify that we have a tuple * signature, and then recompute 'dtt' as the array's value type. */ if (ddp->dd_kind == CTF_K_ARRAY) { if (ddp->dd_node == NULL) { xyerror(D_DECL_ARRNULL, "inline declaration requires " "array tuple signature: %s\n", dsp->ds_ident); } if (ddp->dd_node->dn_kind != DT_NODE_TYPE) { xyerror(D_DECL_ARRNULL, "inline declaration cannot be " "of scalar array type: %s\n", dsp->ds_ident); } if (dt_decl_type(ddp->dd_next, &dtt) != 0) longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } /* * If the inline identifier is not defined, then create it with the * orphan flag set. We do not insert the identifier into dt_globals * until we have successfully cooked the right-hand expression, below. */ dnp = dt_node_alloc(DT_NODE_INLINE); - dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type); + dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type, B_FALSE); dt_node_attr_assign(dnp, _dtrace_defattr); if (dt_node_is_void(dnp)) { xyerror(D_DECL_VOIDOBJ, "cannot declare void inline: %s\n", dsp->ds_ident); } if (ctf_type_kind(dnp->dn_ctfp, ctf_type_resolve( dnp->dn_ctfp, dnp->dn_type)) == CTF_K_FORWARD) { xyerror(D_DECL_INCOMPLETE, "incomplete struct/union/enum %s: %s\n", dt_node_type_name(dnp, n, sizeof (n)), dsp->ds_ident); } if ((inp = malloc(sizeof (dt_idnode_t))) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); bzero(inp, sizeof (dt_idnode_t)); idp = dnp->dn_ident = dt_ident_create(dsp->ds_ident, ddp->dd_kind == CTF_K_ARRAY ? DT_IDENT_ARRAY : DT_IDENT_SCALAR, DT_IDFLG_INLINE | DT_IDFLG_REF | DT_IDFLG_DECL | DT_IDFLG_ORPHAN, 0, _dtrace_defattr, 0, &dt_idops_inline, inp, dtp->dt_gen); if (idp == NULL) { free(inp); longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } /* * If we're inlining an associative array, create a private identifier * hash containing the named parameters and store it in inp->din_hash. * We then push this hash on to the top of the pcb_globals stack. */ if (ddp->dd_kind == CTF_K_ARRAY) { dt_idnode_t *pinp; dt_ident_t *pidp; dt_node_t *pnp; uint_t i = 0; for (pnp = ddp->dd_node; pnp != NULL; pnp = pnp->dn_list) i++; /* count up parameters for din_argv[] */ inp->din_hash = dt_idhash_create("inline args", NULL, 0, 0); inp->din_argv = calloc(i, sizeof (dt_ident_t *)); if (inp->din_hash == NULL || inp->din_argv == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); /* * Create an identifier for each parameter as a scalar inline, * and store it in din_hash and in position in din_argv[]. The * parameter identifiers also use dt_idops_inline, but we leave * the dt_idnode_t argument 'pinp' zeroed. This will be filled * in by the code generation pass with references to the args. */ for (i = 0, pnp = ddp->dd_node; pnp != NULL; pnp = pnp->dn_list, i++) { if (pnp->dn_string == NULL) continue; /* ignore anonymous parameters */ if ((pinp = malloc(sizeof (dt_idnode_t))) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); pidp = dt_idhash_insert(inp->din_hash, pnp->dn_string, DT_IDENT_SCALAR, DT_IDFLG_DECL | DT_IDFLG_INLINE, 0, _dtrace_defattr, 0, &dt_idops_inline, pinp, dtp->dt_gen); if (pidp == NULL) { free(pinp); longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); } inp->din_argv[i] = pidp; bzero(pinp, sizeof (dt_idnode_t)); dt_ident_type_assign(pidp, pnp->dn_ctfp, pnp->dn_type); } dt_idstack_push(&yypcb->pcb_globals, inp->din_hash); } /* * Unlike most constructors, we need to explicitly cook the right-hand * side of the inline definition immediately to prevent recursion. If * the right-hand side uses the inline itself, the cook will fail. */ expr = dt_node_cook(expr, DT_IDFLG_REF); if (ddp->dd_kind == CTF_K_ARRAY) dt_idstack_pop(&yypcb->pcb_globals, inp->din_hash); /* * Set the type, attributes, and flags for the inline. If the right- * hand expression has an identifier, propagate its flags. Then cook * the identifier to fully initialize it: if we're declaring an inline * associative array this will construct a type signature from 'ddp'. */ if (dt_node_is_dynamic(expr)) rdp = dt_ident_resolve(expr->dn_ident); else if (expr->dn_kind == DT_NODE_VAR || expr->dn_kind == DT_NODE_SYM) rdp = expr->dn_ident; else rdp = NULL; if (rdp != NULL) { idp->di_flags |= (rdp->di_flags & (DT_IDFLG_WRITE | DT_IDFLG_USER | DT_IDFLG_PRIM)); } idp->di_attr = dt_attr_min(_dtrace_defattr, expr->dn_attr); dt_ident_type_assign(idp, dtt.dtt_ctfp, dtt.dtt_type); (void) dt_ident_cook(dnp, idp, &ddp->dd_node); /* * Store the parse tree nodes for 'expr' inside of idp->di_data ('inp') * so that they will be preserved with this identifier. Then pop the * inline declaration from the declaration stack and restore the lexer. */ inp->din_list = yypcb->pcb_list; inp->din_root = expr; dt_decl_free(dt_decl_pop()); yybegin(YYS_CLAUSE); /* * Finally, insert the inline identifier into dt_globals to make it * visible, and then cook 'dnp' to check its type against 'expr'. */ dt_idhash_xinsert(dtp->dt_globals, idp); return (dt_node_cook(dnp, DT_IDFLG_REF)); } dt_node_t * dt_node_member(dt_decl_t *ddp, char *name, dt_node_t *expr) { dtrace_typeinfo_t dtt; dt_node_t *dnp; int err; if (ddp != NULL) { err = dt_decl_type(ddp, &dtt); dt_decl_free(ddp); if (err != 0) longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } dnp = dt_node_alloc(DT_NODE_MEMBER); dnp->dn_membname = name; dnp->dn_membexpr = expr; if (ddp != NULL) - dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type); + dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type, + dtt.dtt_flags); return (dnp); } dt_node_t * dt_node_xlator(dt_decl_t *ddp, dt_decl_t *sdp, char *name, dt_node_t *members) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dtrace_typeinfo_t src, dst; dt_node_t sn, dn; dt_xlator_t *dxp; dt_node_t *dnp; int edst, esrc; uint_t kind; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; edst = dt_decl_type(ddp, &dst); dt_decl_free(ddp); esrc = dt_decl_type(sdp, &src); dt_decl_free(sdp); if (edst != 0 || esrc != 0) { free(name); longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } bzero(&sn, sizeof (sn)); - dt_node_type_assign(&sn, src.dtt_ctfp, src.dtt_type); + dt_node_type_assign(&sn, src.dtt_ctfp, src.dtt_type, B_FALSE); bzero(&dn, sizeof (dn)); - dt_node_type_assign(&dn, dst.dtt_ctfp, dst.dtt_type); + dt_node_type_assign(&dn, dst.dtt_ctfp, dst.dtt_type, B_FALSE); if (dt_xlator_lookup(dtp, &sn, &dn, DT_XLATE_EXACT) != NULL) { xyerror(D_XLATE_REDECL, "translator from %s to %s has already been declared\n", dt_node_type_name(&sn, n1, sizeof (n1)), dt_node_type_name(&dn, n2, sizeof (n2))); } kind = ctf_type_kind(dst.dtt_ctfp, ctf_type_resolve(dst.dtt_ctfp, dst.dtt_type)); if (kind == CTF_K_FORWARD) { xyerror(D_XLATE_SOU, "incomplete struct/union/enum %s\n", dt_type_name(dst.dtt_ctfp, dst.dtt_type, n1, sizeof (n1))); } if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) { xyerror(D_XLATE_SOU, "translator output type must be a struct or union\n"); } dxp = dt_xlator_create(dtp, &src, &dst, name, members, yypcb->pcb_list); yybegin(YYS_CLAUSE); free(name); if (dxp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dnp = dt_node_alloc(DT_NODE_XLATOR); dnp->dn_xlator = dxp; dnp->dn_members = members; return (dt_node_cook(dnp, DT_IDFLG_REF)); } dt_node_t * dt_node_probe(char *s, int protoc, dt_node_t *nargs, dt_node_t *xargs) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; int nargc, xargc; dt_node_t *dnp; size_t len = strlen(s) + 3; /* +3 for :: and \0 */ char *name = alloca(len); (void) snprintf(name, len, "::%s", s); (void) strhyphenate(name); free(s); if (strchr(name, '`') != NULL) { xyerror(D_PROV_BADNAME, "probe name may not " "contain scoping operator: %s\n", name); } if (strlen(name) - 2 >= DTRACE_NAMELEN) { xyerror(D_PROV_BADNAME, "probe name may not exceed %d " "characters: %s\n", DTRACE_NAMELEN - 1, name); } dnp = dt_node_alloc(DT_NODE_PROBE); dnp->dn_ident = dt_ident_create(name, DT_IDENT_PROBE, DT_IDFLG_ORPHAN, DTRACE_IDNONE, _dtrace_defattr, 0, &dt_idops_probe, NULL, dtp->dt_gen); nargc = dt_decl_prototype(nargs, nargs, "probe input", DT_DP_VOID | DT_DP_ANON); xargc = dt_decl_prototype(xargs, nargs, "probe output", DT_DP_VOID); if (nargc > UINT8_MAX) { xyerror(D_PROV_PRARGLEN, "probe %s input prototype exceeds %u " "parameters: %d params used\n", name, UINT8_MAX, nargc); } if (xargc > UINT8_MAX) { xyerror(D_PROV_PRARGLEN, "probe %s output prototype exceeds %u " "parameters: %d params used\n", name, UINT8_MAX, xargc); } if (dnp->dn_ident == NULL || dt_probe_create(dtp, dnp->dn_ident, protoc, nargs, nargc, xargs, xargc) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); return (dnp); } dt_node_t * dt_node_provider(char *name, dt_node_t *probes) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_node_t *dnp = dt_node_alloc(DT_NODE_PROVIDER); dt_node_t *lnp; size_t len; dnp->dn_provname = name; dnp->dn_probes = probes; if (strchr(name, '`') != NULL) { dnerror(dnp, D_PROV_BADNAME, "provider name may not " "contain scoping operator: %s\n", name); } if ((len = strlen(name)) >= DTRACE_PROVNAMELEN) { dnerror(dnp, D_PROV_BADNAME, "provider name may not exceed %d " "characters: %s\n", DTRACE_PROVNAMELEN - 1, name); } if (isdigit(name[len - 1])) { dnerror(dnp, D_PROV_BADNAME, "provider name may not " "end with a digit: %s\n", name); } /* * Check to see if the provider is already defined or visible through * dtrace(7D). If so, set dn_provred to treat it as a re-declaration. * If not, create a new provider and set its interface-only flag. This * flag may be cleared later by calls made to dt_probe_declare(). */ if ((dnp->dn_provider = dt_provider_lookup(dtp, name)) != NULL) dnp->dn_provred = B_TRUE; else if ((dnp->dn_provider = dt_provider_create(dtp, name)) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); else dnp->dn_provider->pv_flags |= DT_PROVIDER_INTF; /* * Store all parse nodes created since we consumed the DT_KEY_PROVIDER * token with the provider and then restore our lexing state to CLAUSE. * Note that if dnp->dn_provred is true, we may end up storing dups of * a provider's interface and implementation: we eat this space because * the implementation will likely need to redeclare probe members, and * therefore may result in those member nodes becoming persistent. */ for (lnp = yypcb->pcb_list; lnp->dn_link != NULL; lnp = lnp->dn_link) continue; /* skip to end of allocation list */ lnp->dn_link = dnp->dn_provider->pv_nodes; dnp->dn_provider->pv_nodes = yypcb->pcb_list; yybegin(YYS_CLAUSE); return (dnp); } dt_node_t * dt_node_program(dt_node_t *lnp) { dt_node_t *dnp = dt_node_alloc(DT_NODE_PROG); dnp->dn_list = lnp; return (dnp); } /* * This function provides the underlying implementation of cooking an * identifier given its node, a hash of dynamic identifiers, an identifier * kind, and a boolean flag indicating whether we are allowed to instantiate * a new identifier if the string is not found. This function is either * called from dt_cook_ident(), below, or directly by the various cooking * routines that are allowed to instantiate identifiers (e.g. op2 TOK_ASGN). */ static void dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; const char *sname = dt_idhash_name(dhp); int uref = 0; dtrace_attribute_t attr = _dtrace_defattr; dt_ident_t *idp; dtrace_syminfo_t dts; GElf_Sym sym; const char *scope, *mark; uchar_t dnkind; char *name; /* * Look for scoping marks in the identifier. If one is found, set our * scope to either DTRACE_OBJ_KMODS or UMODS or to the first part of * the string that specifies the scope using an explicit module name. * If two marks in a row are found, set 'uref' (user symbol reference). * Otherwise we set scope to DTRACE_OBJ_EXEC, indicating that normal * scope is desired and we should search the specified idhash. */ if ((name = strrchr(dnp->dn_string, '`')) != NULL) { if (name > dnp->dn_string && name[-1] == '`') { uref++; name[-1] = '\0'; } if (name == dnp->dn_string + uref) scope = uref ? DTRACE_OBJ_UMODS : DTRACE_OBJ_KMODS; else scope = dnp->dn_string; *name++ = '\0'; /* leave name pointing after scoping mark */ dnkind = DT_NODE_VAR; } else if (idkind == DT_IDENT_AGG) { scope = DTRACE_OBJ_EXEC; name = dnp->dn_string + 1; dnkind = DT_NODE_AGG; } else { scope = DTRACE_OBJ_EXEC; name = dnp->dn_string; dnkind = DT_NODE_VAR; } /* * If create is set to false, and we fail our idhash lookup, preset * the errno code to EDT_NOVAR for our final error message below. * If we end up calling dtrace_lookup_by_name(), it will reset the * errno appropriately and that error will be reported instead. */ (void) dt_set_errno(dtp, EDT_NOVAR); mark = uref ? "``" : "`"; if (scope == DTRACE_OBJ_EXEC && ( (dhp != dtp->dt_globals && (idp = dt_idhash_lookup(dhp, name)) != NULL) || (dhp == dtp->dt_globals && (idp = dt_idstack_lookup(&yypcb->pcb_globals, name)) != NULL))) { /* * Check that we are referencing the ident in the manner that * matches its type if this is a global lookup. In the TLS or * local case, we don't know how the ident will be used until * the time operator -> is seen; more parsing is needed. */ if (idp->di_kind != idkind && dhp == dtp->dt_globals) { xyerror(D_IDENT_BADREF, "%s '%s' may not be referenced " "as %s\n", dt_idkind_name(idp->di_kind), idp->di_name, dt_idkind_name(idkind)); } /* * Arrays and aggregations are not cooked individually. They * have dynamic types and must be referenced using operator []. * This is handled explicitly by the code for DT_TOK_LBRAC. */ if (idp->di_kind != DT_IDENT_ARRAY && idp->di_kind != DT_IDENT_AGG) attr = dt_ident_cook(dnp, idp, NULL); else { dt_node_type_assign(dnp, - DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp)); + DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp), B_FALSE); attr = idp->di_attr; } free(dnp->dn_string); dnp->dn_string = NULL; dnp->dn_kind = dnkind; dnp->dn_ident = idp; dnp->dn_flags |= DT_NF_LVALUE; if (idp->di_flags & DT_IDFLG_WRITE) dnp->dn_flags |= DT_NF_WRITABLE; dt_node_attr_assign(dnp, attr); } else if (dhp == dtp->dt_globals && scope != DTRACE_OBJ_EXEC && dtrace_lookup_by_name(dtp, scope, name, &sym, &dts) == 0) { dt_module_t *mp = dt_module_lookup_by_name(dtp, dts.dts_object); int umod = (mp->dm_flags & DT_DM_KERNEL) == 0; static const char *const kunames[] = { "kernel", "user" }; dtrace_typeinfo_t dtt; dtrace_syminfo_t *sip; if (uref ^ umod) { xyerror(D_SYM_BADREF, "%s module '%s' symbol '%s' may " "not be referenced as a %s symbol\n", kunames[umod], dts.dts_object, dts.dts_name, kunames[uref]); } if (dtrace_symbol_type(dtp, &sym, &dts, &dtt) != 0) { /* * For now, we special-case EDT_DATAMODEL to clarify * that mixed data models are not currently supported. */ if (dtp->dt_errno == EDT_DATAMODEL) { xyerror(D_SYM_MODEL, "cannot use %s symbol " "%s%s%s in a %s D program\n", dt_module_modelname(mp), dts.dts_object, mark, dts.dts_name, dt_module_modelname(dtp->dt_ddefs)); } xyerror(D_SYM_NOTYPES, "no symbolic type information is available for " "%s%s%s: %s\n", dts.dts_object, mark, dts.dts_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); } idp = dt_ident_create(name, DT_IDENT_SYMBOL, 0, 0, _dtrace_symattr, 0, &dt_idops_thaw, NULL, dtp->dt_gen); if (idp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); if (mp->dm_flags & DT_DM_PRIMARY) idp->di_flags |= DT_IDFLG_PRIM; idp->di_next = dtp->dt_externs; dtp->dt_externs = idp; if ((sip = malloc(sizeof (dtrace_syminfo_t))) == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); bcopy(&dts, sip, sizeof (dtrace_syminfo_t)); idp->di_data = sip; idp->di_ctfp = dtt.dtt_ctfp; idp->di_type = dtt.dtt_type; free(dnp->dn_string); dnp->dn_string = NULL; dnp->dn_kind = DT_NODE_SYM; dnp->dn_ident = idp; dnp->dn_flags |= DT_NF_LVALUE; - dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type); + dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type, + dtt.dtt_flags); dt_node_attr_assign(dnp, _dtrace_symattr); if (uref) { idp->di_flags |= DT_IDFLG_USER; dnp->dn_flags |= DT_NF_USERLAND; } } else if (scope == DTRACE_OBJ_EXEC && create == B_TRUE) { uint_t flags = DT_IDFLG_WRITE; uint_t id; if (dt_idhash_nextid(dhp, &id) == -1) { xyerror(D_ID_OFLOW, "cannot create %s: limit on number " "of %s variables exceeded\n", name, sname); } if (dhp == yypcb->pcb_locals) flags |= DT_IDFLG_LOCAL; else if (dhp == dtp->dt_tls) flags |= DT_IDFLG_TLS; dt_dprintf("create %s %s variable %s, id=%u\n", sname, dt_idkind_name(idkind), name, id); if (idkind == DT_IDENT_ARRAY || idkind == DT_IDENT_AGG) { idp = dt_idhash_insert(dhp, name, idkind, flags, id, _dtrace_defattr, 0, &dt_idops_assc, NULL, dtp->dt_gen); } else { idp = dt_idhash_insert(dhp, name, idkind, flags, id, _dtrace_defattr, 0, &dt_idops_thaw, NULL, dtp->dt_gen); } if (idp == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); /* * Arrays and aggregations are not cooked individually. They * have dynamic types and must be referenced using operator []. * This is handled explicitly by the code for DT_TOK_LBRAC. */ if (idp->di_kind != DT_IDENT_ARRAY && idp->di_kind != DT_IDENT_AGG) attr = dt_ident_cook(dnp, idp, NULL); else { dt_node_type_assign(dnp, - DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp)); + DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp), B_FALSE); attr = idp->di_attr; } free(dnp->dn_string); dnp->dn_string = NULL; dnp->dn_kind = dnkind; dnp->dn_ident = idp; dnp->dn_flags |= DT_NF_LVALUE | DT_NF_WRITABLE; dt_node_attr_assign(dnp, attr); } else if (scope != DTRACE_OBJ_EXEC) { xyerror(D_IDENT_UNDEF, "failed to resolve %s%s%s: %s\n", dnp->dn_string, mark, name, dtrace_errmsg(dtp, dtrace_errno(dtp))); } else { xyerror(D_IDENT_UNDEF, "failed to resolve %s: %s\n", dnp->dn_string, dtrace_errmsg(dtp, dtrace_errno(dtp))); } } static dt_node_t * dt_cook_ident(dt_node_t *dnp, uint_t idflags) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; if (dnp->dn_op == DT_TOK_AGG) dt_xcook_ident(dnp, dtp->dt_aggs, DT_IDENT_AGG, B_FALSE); else dt_xcook_ident(dnp, dtp->dt_globals, DT_IDENT_SCALAR, B_FALSE); return (dt_node_cook(dnp, idflags)); } /* * Since operators [ and -> can instantiate new variables before we know * whether the reference is for a read or a write, we need to check read * references to determine if the identifier is currently dt_ident_unref(). * If so, we report that this first access was to an undefined variable. */ static dt_node_t * dt_cook_var(dt_node_t *dnp, uint_t idflags) { dt_ident_t *idp = dnp->dn_ident; if ((idflags & DT_IDFLG_REF) && dt_ident_unref(idp)) { dnerror(dnp, D_VAR_UNDEF, "%s%s has not yet been declared or assigned\n", (idp->di_flags & DT_IDFLG_LOCAL) ? "this->" : (idp->di_flags & DT_IDFLG_TLS) ? "self->" : "", idp->di_name); } dt_node_attr_assign(dnp, dt_ident_cook(dnp, idp, &dnp->dn_args)); return (dnp); } /*ARGSUSED*/ static dt_node_t * dt_cook_func(dt_node_t *dnp, uint_t idflags) { dt_node_attr_assign(dnp, dt_ident_cook(dnp, dnp->dn_ident, &dnp->dn_args)); return (dnp); } static dt_node_t * dt_cook_op1(dt_node_t *dnp, uint_t idflags) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_node_t *cp = dnp->dn_child; char n[DT_TYPE_NAMELEN]; dtrace_typeinfo_t dtt; dt_ident_t *idp; ctf_encoding_t e; ctf_arinfo_t r; ctf_id_t type, base; uint_t kind; if (dnp->dn_op == DT_TOK_PREINC || dnp->dn_op == DT_TOK_POSTINC || dnp->dn_op == DT_TOK_PREDEC || dnp->dn_op == DT_TOK_POSTDEC) idflags = DT_IDFLG_REF | DT_IDFLG_MOD; else idflags = DT_IDFLG_REF; /* * We allow the unary ++ and -- operators to instantiate new scalar * variables if applied to an identifier; otherwise just cook as usual. */ if (cp->dn_kind == DT_NODE_IDENT && (idflags & DT_IDFLG_MOD)) dt_xcook_ident(cp, dtp->dt_globals, DT_IDENT_SCALAR, B_TRUE); cp = dnp->dn_child = dt_node_cook(cp, 0); /* don't set idflags yet */ if (cp->dn_kind == DT_NODE_VAR && dt_ident_unref(cp->dn_ident)) { if (dt_type_lookup("int64_t", &dtt) != 0) xyerror(D_TYPE_ERR, "failed to lookup int64_t\n"); dt_ident_type_assign(cp->dn_ident, dtt.dtt_ctfp, dtt.dtt_type); - dt_node_type_assign(cp, dtt.dtt_ctfp, dtt.dtt_type); + dt_node_type_assign(cp, dtt.dtt_ctfp, dtt.dtt_type, + dtt.dtt_flags); } if (cp->dn_kind == DT_NODE_VAR) cp->dn_ident->di_flags |= idflags; switch (dnp->dn_op) { case DT_TOK_DEREF: /* * If the deref operator is applied to a translated pointer, * we set our output type to the output of the translation. */ if ((idp = dt_node_resolve(cp, DT_IDENT_XLPTR)) != NULL) { dt_xlator_t *dxp = idp->di_data; dnp->dn_ident = &dxp->dx_souid; dt_node_type_assign(dnp, - dnp->dn_ident->di_ctfp, dnp->dn_ident->di_type); + dnp->dn_ident->di_ctfp, dnp->dn_ident->di_type, + cp->dn_flags & DT_NF_USERLAND); break; } type = ctf_type_resolve(cp->dn_ctfp, cp->dn_type); kind = ctf_type_kind(cp->dn_ctfp, type); if (kind == CTF_K_ARRAY) { if (ctf_array_info(cp->dn_ctfp, type, &r) != 0) { dtp->dt_ctferr = ctf_errno(cp->dn_ctfp); longjmp(yypcb->pcb_jmpbuf, EDT_CTF); } else type = r.ctr_contents; } else if (kind == CTF_K_POINTER) { type = ctf_type_reference(cp->dn_ctfp, type); } else { xyerror(D_DEREF_NONPTR, "cannot dereference non-pointer type\n"); } - dt_node_type_assign(dnp, cp->dn_ctfp, type); + dt_node_type_assign(dnp, cp->dn_ctfp, type, + cp->dn_flags & DT_NF_USERLAND); base = ctf_type_resolve(cp->dn_ctfp, type); kind = ctf_type_kind(cp->dn_ctfp, base); if (kind == CTF_K_INTEGER && ctf_type_encoding(cp->dn_ctfp, base, &e) == 0 && IS_VOID(e)) { xyerror(D_DEREF_VOID, "cannot dereference pointer to void\n"); } if (kind == CTF_K_FUNCTION) { xyerror(D_DEREF_FUNC, "cannot dereference pointer to function\n"); } if (kind != CTF_K_ARRAY || dt_node_is_string(dnp)) dnp->dn_flags |= DT_NF_LVALUE; /* see K&R[A7.4.3] */ /* * If we propagated the l-value bit and the child operand was * a writable D variable or a binary operation of the form * a + b where a is writable, then propagate the writable bit. * This is necessary to permit assignments to scalar arrays, * which are converted to expressions of the form *(a + i). */ if ((cp->dn_flags & DT_NF_WRITABLE) || (cp->dn_kind == DT_NODE_OP2 && cp->dn_op == DT_TOK_ADD && (cp->dn_left->dn_flags & DT_NF_WRITABLE))) dnp->dn_flags |= DT_NF_WRITABLE; if ((cp->dn_flags & DT_NF_USERLAND) && (kind == CTF_K_POINTER || (dnp->dn_flags & DT_NF_REF))) dnp->dn_flags |= DT_NF_USERLAND; break; case DT_TOK_IPOS: case DT_TOK_INEG: if (!dt_node_is_arith(cp)) { xyerror(D_OP_ARITH, "operator %s requires an operand " "of arithmetic type\n", opstr(dnp->dn_op)); } dt_node_type_propagate(cp, dnp); /* see K&R[A7.4.4-6] */ break; case DT_TOK_BNEG: if (!dt_node_is_integer(cp)) { xyerror(D_OP_INT, "operator %s requires an operand of " "integral type\n", opstr(dnp->dn_op)); } dt_node_type_propagate(cp, dnp); /* see K&R[A7.4.4-6] */ break; case DT_TOK_LNEG: if (!dt_node_is_scalar(cp)) { xyerror(D_OP_SCALAR, "operator %s requires an operand " "of scalar type\n", opstr(dnp->dn_op)); } - dt_node_type_assign(dnp, DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + dt_node_type_assign(dnp, DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), + B_FALSE); break; case DT_TOK_ADDROF: if (cp->dn_kind == DT_NODE_VAR || cp->dn_kind == DT_NODE_AGG) { xyerror(D_ADDROF_VAR, "cannot take address of dynamic variable\n"); } if (dt_node_is_dynamic(cp)) { xyerror(D_ADDROF_VAR, "cannot take address of dynamic object\n"); } if (!(cp->dn_flags & DT_NF_LVALUE)) { xyerror(D_ADDROF_LVAL, /* see K&R[A7.4.2] */ "unacceptable operand for unary & operator\n"); } if (cp->dn_flags & DT_NF_BITFIELD) { xyerror(D_ADDROF_BITFIELD, "cannot take address of bit-field\n"); } dtt.dtt_object = NULL; dtt.dtt_ctfp = cp->dn_ctfp; dtt.dtt_type = cp->dn_type; if (dt_type_pointer(&dtt) == -1) { xyerror(D_TYPE_ERR, "cannot find type for \"&\": %s*\n", dt_node_type_name(cp, n, sizeof (n))); } - dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type); - - if (cp->dn_flags & DT_NF_USERLAND) - dnp->dn_flags |= DT_NF_USERLAND; + dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type, + cp->dn_flags & DT_NF_USERLAND); break; case DT_TOK_SIZEOF: if (cp->dn_flags & DT_NF_BITFIELD) { xyerror(D_SIZEOF_BITFIELD, "cannot apply sizeof to a bit-field\n"); } if (dt_node_sizeof(cp) == 0) { xyerror(D_SIZEOF_TYPE, "cannot apply sizeof to an " "operand of unknown size\n"); } dt_node_type_assign(dnp, dtp->dt_ddefs->dm_ctfp, - ctf_lookup_by_name(dtp->dt_ddefs->dm_ctfp, "size_t")); + ctf_lookup_by_name(dtp->dt_ddefs->dm_ctfp, "size_t"), + B_FALSE); break; case DT_TOK_STRINGOF: if (!dt_node_is_scalar(cp) && !dt_node_is_pointer(cp) && !dt_node_is_strcompat(cp)) { xyerror(D_STRINGOF_TYPE, "cannot apply stringof to a value of type %s\n", dt_node_type_name(cp, n, sizeof (n))); } - dt_node_type_assign(dnp, DT_STR_CTFP(dtp), DT_STR_TYPE(dtp)); + dt_node_type_assign(dnp, DT_STR_CTFP(dtp), DT_STR_TYPE(dtp), + cp->dn_flags & DT_NF_USERLAND); break; case DT_TOK_PREINC: case DT_TOK_POSTINC: case DT_TOK_PREDEC: case DT_TOK_POSTDEC: if (dt_node_is_scalar(cp) == 0) { xyerror(D_OP_SCALAR, "operator %s requires operand of " "scalar type\n", opstr(dnp->dn_op)); } if (dt_node_is_vfptr(cp)) { xyerror(D_OP_VFPTR, "operator %s requires an operand " "of known size\n", opstr(dnp->dn_op)); } if (!(cp->dn_flags & DT_NF_LVALUE)) { xyerror(D_OP_LVAL, "operator %s requires modifiable " "lvalue as an operand\n", opstr(dnp->dn_op)); } if (!(cp->dn_flags & DT_NF_WRITABLE)) { xyerror(D_OP_WRITE, "operator %s can only be applied " "to a writable variable\n", opstr(dnp->dn_op)); } dt_node_type_propagate(cp, dnp); /* see K&R[A7.4.1] */ break; default: xyerror(D_UNKNOWN, "invalid unary op %s\n", opstr(dnp->dn_op)); } dt_node_attr_assign(dnp, cp->dn_attr); return (dnp); } static void dt_assign_common(dt_node_t *dnp) { dt_node_t *lp = dnp->dn_left; dt_node_t *rp = dnp->dn_right; int op = dnp->dn_op; if (rp->dn_kind == DT_NODE_INT) dt_cast(lp, rp); if (!(lp->dn_flags & DT_NF_LVALUE)) { xyerror(D_OP_LVAL, "operator %s requires modifiable " "lvalue as an operand\n", opstr(op)); /* see K&R[A7.17] */ } if (!(lp->dn_flags & DT_NF_WRITABLE)) { xyerror(D_OP_WRITE, "operator %s can only be applied " "to a writable variable\n", opstr(op)); } dt_node_type_propagate(lp, dnp); /* see K&R[A7.17] */ dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr)); } static dt_node_t * dt_cook_op2(dt_node_t *dnp, uint_t idflags) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_node_t *lp = dnp->dn_left; dt_node_t *rp = dnp->dn_right; int op = dnp->dn_op; ctf_membinfo_t m; ctf_file_t *ctfp; ctf_id_t type; int kind, val, uref; dt_ident_t *idp; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; /* * The expression E1[E2] is identical by definition to *((E1)+(E2)) so * we convert "[" to "+" and glue on "*" at the end (see K&R[A7.3.1]) * unless the left-hand side is an untyped D scalar, associative array, * or aggregation. In these cases, we proceed to case DT_TOK_LBRAC and * handle associative array and aggregation references there. */ if (op == DT_TOK_LBRAC) { if (lp->dn_kind == DT_NODE_IDENT) { dt_idhash_t *dhp; uint_t idkind; if (lp->dn_op == DT_TOK_AGG) { dhp = dtp->dt_aggs; idp = dt_idhash_lookup(dhp, lp->dn_string + 1); idkind = DT_IDENT_AGG; } else { dhp = dtp->dt_globals; idp = dt_idstack_lookup( &yypcb->pcb_globals, lp->dn_string); idkind = DT_IDENT_ARRAY; } if (idp == NULL || dt_ident_unref(idp)) dt_xcook_ident(lp, dhp, idkind, B_TRUE); else dt_xcook_ident(lp, dhp, idp->di_kind, B_FALSE); } else lp = dnp->dn_left = dt_node_cook(lp, 0); /* * Switch op to '+' for *(E1 + E2) array mode in these cases: * (a) lp is a DT_IDENT_ARRAY variable that has already been * referenced using [] notation (dn_args != NULL). * (b) lp is a non-ARRAY variable that has already been given * a type by assignment or declaration (!dt_ident_unref()) * (c) lp is neither a variable nor an aggregation */ if (lp->dn_kind == DT_NODE_VAR) { if (lp->dn_ident->di_kind == DT_IDENT_ARRAY) { if (lp->dn_args != NULL) op = DT_TOK_ADD; } else if (!dt_ident_unref(lp->dn_ident)) op = DT_TOK_ADD; } else if (lp->dn_kind != DT_NODE_AGG) op = DT_TOK_ADD; } switch (op) { case DT_TOK_BAND: case DT_TOK_XOR: case DT_TOK_BOR: lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); if (!dt_node_is_integer(lp) || !dt_node_is_integer(rp)) { xyerror(D_OP_INT, "operator %s requires operands of " "integral type\n", opstr(op)); } dt_node_promote(lp, rp, dnp); /* see K&R[A7.11-13] */ break; case DT_TOK_LSH: case DT_TOK_RSH: lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); if (!dt_node_is_integer(lp) || !dt_node_is_integer(rp)) { xyerror(D_OP_INT, "operator %s requires operands of " "integral type\n", opstr(op)); } dt_node_type_propagate(lp, dnp); /* see K&R[A7.8] */ dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr)); break; case DT_TOK_MOD: lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); if (!dt_node_is_integer(lp) || !dt_node_is_integer(rp)) { xyerror(D_OP_INT, "operator %s requires operands of " "integral type\n", opstr(op)); } dt_node_promote(lp, rp, dnp); /* see K&R[A7.6] */ break; case DT_TOK_MUL: case DT_TOK_DIV: lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); if (!dt_node_is_arith(lp) || !dt_node_is_arith(rp)) { xyerror(D_OP_ARITH, "operator %s requires operands of " "arithmetic type\n", opstr(op)); } dt_node_promote(lp, rp, dnp); /* see K&R[A7.6] */ break; case DT_TOK_LAND: case DT_TOK_LXOR: case DT_TOK_LOR: lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); if (!dt_node_is_scalar(lp) || !dt_node_is_scalar(rp)) { xyerror(D_OP_SCALAR, "operator %s requires operands " "of scalar type\n", opstr(op)); } - dt_node_type_assign(dnp, DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + dt_node_type_assign(dnp, DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), + B_FALSE); dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr)); break; case DT_TOK_LT: case DT_TOK_LE: case DT_TOK_GT: case DT_TOK_GE: case DT_TOK_EQU: case DT_TOK_NEQ: /* * The D comparison operators provide the ability to transform * a right-hand identifier into a corresponding enum tag value * if the left-hand side is an enum type. To do this, we cook * the left-hand side, and then see if the right-hand side is * an unscoped identifier defined in the enum. If so, we * convert into an integer constant node with the tag's value. */ lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); kind = ctf_type_kind(lp->dn_ctfp, ctf_type_resolve(lp->dn_ctfp, lp->dn_type)); if (kind == CTF_K_ENUM && rp->dn_kind == DT_NODE_IDENT && strchr(rp->dn_string, '`') == NULL && ctf_enum_value( lp->dn_ctfp, lp->dn_type, rp->dn_string, &val) == 0) { if ((idp = dt_idstack_lookup(&yypcb->pcb_globals, rp->dn_string)) != NULL) { xyerror(D_IDENT_AMBIG, "ambiguous use of operator %s: %s is " "both a %s enum tag and a global %s\n", opstr(op), rp->dn_string, dt_node_type_name(lp, n1, sizeof (n1)), dt_idkind_name(idp->di_kind)); } free(rp->dn_string); rp->dn_string = NULL; rp->dn_kind = DT_NODE_INT; rp->dn_flags |= DT_NF_COOKED; rp->dn_op = DT_TOK_INT; rp->dn_value = (intmax_t)val; - dt_node_type_assign(rp, lp->dn_ctfp, lp->dn_type); + dt_node_type_assign(rp, lp->dn_ctfp, lp->dn_type, + B_FALSE); dt_node_attr_assign(rp, _dtrace_symattr); } rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); /* * The rules for type checking for the relational operators are * described in the ANSI-C spec (see K&R[A7.9-10]). We perform * the various tests in order from least to most expensive. We * also allow derived strings to be compared as a first-class * type (resulting in a strcmp(3C)-style comparison), and we * slightly relax the A7.9 rules to permit void pointer * comparisons as in A7.10. Our users won't be confused by * this since they understand pointers are just numbers, and * relaxing this constraint simplifies the implementation. */ if (ctf_type_compat(lp->dn_ctfp, lp->dn_type, rp->dn_ctfp, rp->dn_type)) /*EMPTY*/; else if (dt_node_is_integer(lp) && dt_node_is_integer(rp)) /*EMPTY*/; else if (dt_node_is_strcompat(lp) && dt_node_is_strcompat(rp) && (dt_node_is_string(lp) || dt_node_is_string(rp))) /*EMPTY*/; else if (dt_node_is_ptrcompat(lp, rp, NULL, NULL) == 0) { xyerror(D_OP_INCOMPAT, "operands have " "incompatible types: \"%s\" %s \"%s\"\n", dt_node_type_name(lp, n1, sizeof (n1)), opstr(op), dt_node_type_name(rp, n2, sizeof (n2))); } - dt_node_type_assign(dnp, DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + dt_node_type_assign(dnp, DT_INT_CTFP(dtp), DT_INT_TYPE(dtp), + B_FALSE); dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr)); break; case DT_TOK_ADD: case DT_TOK_SUB: { /* * The rules for type checking for the additive operators are * described in the ANSI-C spec (see K&R[A7.7]). Pointers and * integers may be manipulated according to specific rules. In * these cases D permits strings to be treated as pointers. */ int lp_is_ptr, lp_is_int, rp_is_ptr, rp_is_int; lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); lp_is_ptr = dt_node_is_string(lp) || (dt_node_is_pointer(lp) && !dt_node_is_vfptr(lp)); lp_is_int = dt_node_is_integer(lp); rp_is_ptr = dt_node_is_string(rp) || (dt_node_is_pointer(rp) && !dt_node_is_vfptr(rp)); rp_is_int = dt_node_is_integer(rp); if (lp_is_int && rp_is_int) { dt_type_promote(lp, rp, &ctfp, &type); uref = 0; } else if (lp_is_ptr && rp_is_int) { ctfp = lp->dn_ctfp; type = lp->dn_type; uref = lp->dn_flags & DT_NF_USERLAND; } else if (lp_is_int && rp_is_ptr && op == DT_TOK_ADD) { ctfp = rp->dn_ctfp; type = rp->dn_type; uref = rp->dn_flags & DT_NF_USERLAND; } else if (lp_is_ptr && rp_is_ptr && op == DT_TOK_SUB && dt_node_is_ptrcompat(lp, rp, NULL, NULL)) { ctfp = dtp->dt_ddefs->dm_ctfp; type = ctf_lookup_by_name(ctfp, "ptrdiff_t"); uref = 0; } else { xyerror(D_OP_INCOMPAT, "operands have incompatible " "types: \"%s\" %s \"%s\"\n", dt_node_type_name(lp, n1, sizeof (n1)), opstr(op), dt_node_type_name(rp, n2, sizeof (n2))); } - dt_node_type_assign(dnp, ctfp, type); + dt_node_type_assign(dnp, ctfp, type, B_FALSE); dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr)); if (uref) dnp->dn_flags |= DT_NF_USERLAND; break; } case DT_TOK_OR_EQ: case DT_TOK_XOR_EQ: case DT_TOK_AND_EQ: case DT_TOK_LSH_EQ: case DT_TOK_RSH_EQ: case DT_TOK_MOD_EQ: if (lp->dn_kind == DT_NODE_IDENT) { dt_xcook_ident(lp, dtp->dt_globals, DT_IDENT_SCALAR, B_TRUE); } lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF | DT_IDFLG_MOD); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF | DT_IDFLG_MOD); if (!dt_node_is_integer(lp) || !dt_node_is_integer(rp)) { xyerror(D_OP_INT, "operator %s requires operands of " "integral type\n", opstr(op)); } goto asgn_common; case DT_TOK_MUL_EQ: case DT_TOK_DIV_EQ: if (lp->dn_kind == DT_NODE_IDENT) { dt_xcook_ident(lp, dtp->dt_globals, DT_IDENT_SCALAR, B_TRUE); } lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF | DT_IDFLG_MOD); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF | DT_IDFLG_MOD); if (!dt_node_is_arith(lp) || !dt_node_is_arith(rp)) { xyerror(D_OP_ARITH, "operator %s requires operands of " "arithmetic type\n", opstr(op)); } goto asgn_common; case DT_TOK_ASGN: /* * If the left-hand side is an identifier, attempt to resolve * it as either an aggregation or scalar variable. We pass * B_TRUE to dt_xcook_ident to indicate that a new variable can * be created if no matching variable exists in the namespace. */ if (lp->dn_kind == DT_NODE_IDENT) { if (lp->dn_op == DT_TOK_AGG) { dt_xcook_ident(lp, dtp->dt_aggs, DT_IDENT_AGG, B_TRUE); } else { dt_xcook_ident(lp, dtp->dt_globals, DT_IDENT_SCALAR, B_TRUE); } } lp = dnp->dn_left = dt_node_cook(lp, 0); /* don't set mod yet */ rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); /* * If the left-hand side is an aggregation, verify that we are * assigning it the result of an aggregating function. Once * we've done so, hide the func node in the aggregation and * return the aggregation itself up to the parse tree parent. * This transformation is legal since the assigned function * cannot change identity across disjoint cooking passes and * the argument list subtree is retained for later cooking. */ if (lp->dn_kind == DT_NODE_AGG) { const char *aname = lp->dn_ident->di_name; dt_ident_t *oid = lp->dn_ident->di_iarg; if (rp->dn_kind != DT_NODE_FUNC || rp->dn_ident->di_kind != DT_IDENT_AGGFUNC) { xyerror(D_AGG_FUNC, "@%s must be assigned the result of " "an aggregating function\n", aname); } if (oid != NULL && oid != rp->dn_ident) { xyerror(D_AGG_REDEF, "aggregation redefined: @%s\n\t " "current: @%s = %s( )\n\tprevious: @%s = " "%s( ) : line %d\n", aname, aname, rp->dn_ident->di_name, aname, oid->di_name, lp->dn_ident->di_lineno); } else if (oid == NULL) lp->dn_ident->di_iarg = rp->dn_ident; /* * Do not allow multiple aggregation assignments in a * single statement, e.g. (@a = count()) = count(); * We produce a message as if the result of aggregating * function does not propagate DT_NF_LVALUE. */ if (lp->dn_aggfun != NULL) { xyerror(D_OP_LVAL, "operator = requires " "modifiable lvalue as an operand\n"); } lp->dn_aggfun = rp; lp = dt_node_cook(lp, DT_IDFLG_MOD); dnp->dn_left = dnp->dn_right = NULL; dt_node_free(dnp); return (lp); } /* * If the right-hand side is a dynamic variable that is the * output of a translator, our result is the translated type. */ if ((idp = dt_node_resolve(rp, DT_IDENT_XLSOU)) != NULL) { ctfp = idp->di_ctfp; type = idp->di_type; uref = idp->di_flags & DT_IDFLG_USER; } else { ctfp = rp->dn_ctfp; type = rp->dn_type; uref = rp->dn_flags & DT_NF_USERLAND; } /* * If the left-hand side of an assignment statement is a virgin * variable created by this compilation pass, reset the type of * this variable to the type of the right-hand side. */ if (lp->dn_kind == DT_NODE_VAR && dt_ident_unref(lp->dn_ident)) { - dt_node_type_assign(lp, ctfp, type); + dt_node_type_assign(lp, ctfp, type, B_FALSE); dt_ident_type_assign(lp->dn_ident, ctfp, type); if (uref) { lp->dn_flags |= DT_NF_USERLAND; lp->dn_ident->di_flags |= DT_IDFLG_USER; } } if (lp->dn_kind == DT_NODE_VAR) lp->dn_ident->di_flags |= DT_IDFLG_MOD; /* * The rules for type checking for the assignment operators are * described in the ANSI-C spec (see K&R[A7.17]). We share * most of this code with the argument list checking code. */ if (!dt_node_is_string(lp)) { kind = ctf_type_kind(lp->dn_ctfp, ctf_type_resolve(lp->dn_ctfp, lp->dn_type)); if (kind == CTF_K_ARRAY || kind == CTF_K_FUNCTION) { xyerror(D_OP_ARRFUN, "operator %s may not be " "applied to operand of type \"%s\"\n", opstr(op), dt_node_type_name(lp, n1, sizeof (n1))); } } if (idp != NULL && idp->di_kind == DT_IDENT_XLSOU && ctf_type_compat(lp->dn_ctfp, lp->dn_type, ctfp, type)) goto asgn_common; if (dt_node_is_argcompat(lp, rp)) goto asgn_common; xyerror(D_OP_INCOMPAT, "operands have incompatible types: \"%s\" %s \"%s\"\n", dt_node_type_name(lp, n1, sizeof (n1)), opstr(op), dt_node_type_name(rp, n2, sizeof (n2))); /*NOTREACHED*/ case DT_TOK_ADD_EQ: case DT_TOK_SUB_EQ: if (lp->dn_kind == DT_NODE_IDENT) { dt_xcook_ident(lp, dtp->dt_globals, DT_IDENT_SCALAR, B_TRUE); } lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF | DT_IDFLG_MOD); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF | DT_IDFLG_MOD); if (dt_node_is_string(lp) || dt_node_is_string(rp)) { xyerror(D_OP_INCOMPAT, "operands have " "incompatible types: \"%s\" %s \"%s\"\n", dt_node_type_name(lp, n1, sizeof (n1)), opstr(op), dt_node_type_name(rp, n2, sizeof (n2))); } /* * The rules for type checking for the assignment operators are * described in the ANSI-C spec (see K&R[A7.17]). To these * rules we add that only writable D nodes can be modified. */ if (dt_node_is_integer(lp) == 0 || dt_node_is_integer(rp) == 0) { if (!dt_node_is_pointer(lp) || dt_node_is_vfptr(lp)) { xyerror(D_OP_VFPTR, "operator %s requires left-hand scalar " "operand of known size\n", opstr(op)); } else if (dt_node_is_integer(rp) == 0 && dt_node_is_ptrcompat(lp, rp, NULL, NULL) == 0) { xyerror(D_OP_INCOMPAT, "operands have " "incompatible types: \"%s\" %s \"%s\"\n", dt_node_type_name(lp, n1, sizeof (n1)), opstr(op), dt_node_type_name(rp, n2, sizeof (n2))); } } asgn_common: dt_assign_common(dnp); break; case DT_TOK_PTR: /* * If the left-hand side of operator -> is the name "self", * then we permit a TLS variable to be created or referenced. */ if (lp->dn_kind == DT_NODE_IDENT && strcmp(lp->dn_string, "self") == 0) { if (rp->dn_kind != DT_NODE_VAR) { dt_xcook_ident(rp, dtp->dt_tls, DT_IDENT_SCALAR, B_TRUE); } if (idflags != 0) rp = dt_node_cook(rp, idflags); dnp->dn_right = dnp->dn_left; /* avoid freeing rp */ dt_node_free(dnp); return (rp); } /* * If the left-hand side of operator -> is the name "this", * then we permit a local variable to be created or referenced. */ if (lp->dn_kind == DT_NODE_IDENT && strcmp(lp->dn_string, "this") == 0) { if (rp->dn_kind != DT_NODE_VAR) { dt_xcook_ident(rp, yypcb->pcb_locals, DT_IDENT_SCALAR, B_TRUE); } if (idflags != 0) rp = dt_node_cook(rp, idflags); dnp->dn_right = dnp->dn_left; /* avoid freeing rp */ dt_node_free(dnp); return (rp); } /*FALLTHRU*/ case DT_TOK_DOT: lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); if (rp->dn_kind != DT_NODE_IDENT) { xyerror(D_OP_IDENT, "operator %s must be followed by " "an identifier\n", opstr(op)); } if ((idp = dt_node_resolve(lp, DT_IDENT_XLSOU)) != NULL || (idp = dt_node_resolve(lp, DT_IDENT_XLPTR)) != NULL) { /* * If the left-hand side is a translated struct or ptr, * the type of the left is the translation output type. */ dt_xlator_t *dxp = idp->di_data; if (dt_xlator_member(dxp, rp->dn_string) == NULL) { xyerror(D_XLATE_NOCONV, "translator does not define conversion " "for member: %s\n", rp->dn_string); } ctfp = idp->di_ctfp; type = ctf_type_resolve(ctfp, idp->di_type); uref = idp->di_flags & DT_IDFLG_USER; } else { ctfp = lp->dn_ctfp; type = ctf_type_resolve(ctfp, lp->dn_type); uref = lp->dn_flags & DT_NF_USERLAND; } kind = ctf_type_kind(ctfp, type); if (op == DT_TOK_PTR) { if (kind != CTF_K_POINTER) { xyerror(D_OP_PTR, "operator %s must be " "applied to a pointer\n", opstr(op)); } type = ctf_type_reference(ctfp, type); type = ctf_type_resolve(ctfp, type); kind = ctf_type_kind(ctfp, type); } /* * If we follow a reference to a forward declaration tag, * search the entire type space for the actual definition. */ while (kind == CTF_K_FORWARD) { char *tag = ctf_type_name(ctfp, type, n1, sizeof (n1)); dtrace_typeinfo_t dtt; if (tag != NULL && dt_type_lookup(tag, &dtt) == 0 && (dtt.dtt_ctfp != ctfp || dtt.dtt_type != type)) { ctfp = dtt.dtt_ctfp; type = ctf_type_resolve(ctfp, dtt.dtt_type); kind = ctf_type_kind(ctfp, type); } else { xyerror(D_OP_INCOMPLETE, "operator %s cannot be applied to a " "forward declaration: no %s definition " "is available\n", opstr(op), tag); } } if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) { if (op == DT_TOK_PTR) { xyerror(D_OP_SOU, "operator -> cannot be " "applied to pointer to type \"%s\"; must " "be applied to a struct or union pointer\n", ctf_type_name(ctfp, type, n1, sizeof (n1))); } else { xyerror(D_OP_SOU, "operator %s cannot be " "applied to type \"%s\"; must be applied " "to a struct or union\n", opstr(op), ctf_type_name(ctfp, type, n1, sizeof (n1))); } } if (ctf_member_info(ctfp, type, rp->dn_string, &m) == CTF_ERR) { xyerror(D_TYPE_MEMBER, "%s is not a member of %s\n", rp->dn_string, ctf_type_name(ctfp, type, n1, sizeof (n1))); } type = ctf_type_resolve(ctfp, m.ctm_type); kind = ctf_type_kind(ctfp, type); - dt_node_type_assign(dnp, ctfp, m.ctm_type); + dt_node_type_assign(dnp, ctfp, m.ctm_type, B_FALSE); dt_node_attr_assign(dnp, lp->dn_attr); if (op == DT_TOK_PTR && (kind != CTF_K_ARRAY || dt_node_is_string(dnp))) dnp->dn_flags |= DT_NF_LVALUE; /* see K&R[A7.3.3] */ if (op == DT_TOK_DOT && (lp->dn_flags & DT_NF_LVALUE) && (kind != CTF_K_ARRAY || dt_node_is_string(dnp))) dnp->dn_flags |= DT_NF_LVALUE; /* see K&R[A7.3.3] */ if (lp->dn_flags & DT_NF_WRITABLE) dnp->dn_flags |= DT_NF_WRITABLE; if (uref && (kind == CTF_K_POINTER || (dnp->dn_flags & DT_NF_REF))) dnp->dn_flags |= DT_NF_USERLAND; break; case DT_TOK_LBRAC: { /* * If op is DT_TOK_LBRAC, we know from the special-case code at * the top that lp is either a D variable or an aggregation. */ dt_node_t *lnp; /* * If the left-hand side is an aggregation, just set dn_aggtup * to the right-hand side and return the cooked aggregation. * This transformation is legal since we are just collapsing * nodes to simplify later processing, and the entire aggtup * parse subtree is retained for subsequent cooking passes. */ if (lp->dn_kind == DT_NODE_AGG) { if (lp->dn_aggtup != NULL) { xyerror(D_AGG_MDIM, "improper attempt to " "reference @%s as a multi-dimensional " "array\n", lp->dn_ident->di_name); } lp->dn_aggtup = rp; lp = dt_node_cook(lp, 0); dnp->dn_left = dnp->dn_right = NULL; dt_node_free(dnp); return (lp); } assert(lp->dn_kind == DT_NODE_VAR); idp = lp->dn_ident; /* * If the left-hand side is a non-global scalar that hasn't yet * been referenced or modified, it was just created by self-> * or this-> and we can convert it from scalar to assoc array. */ if (idp->di_kind == DT_IDENT_SCALAR && dt_ident_unref(idp) && (idp->di_flags & (DT_IDFLG_LOCAL | DT_IDFLG_TLS)) != 0) { if (idp->di_flags & DT_IDFLG_LOCAL) { xyerror(D_ARR_LOCAL, "local variables may not be used as " "associative arrays: %s\n", idp->di_name); } dt_dprintf("morph variable %s (id %u) from scalar to " "array\n", idp->di_name, idp->di_id); dt_ident_morph(idp, DT_IDENT_ARRAY, &dt_idops_assc, NULL); } if (idp->di_kind != DT_IDENT_ARRAY) { xyerror(D_IDENT_BADREF, "%s '%s' may not be referenced " "as %s\n", dt_idkind_name(idp->di_kind), idp->di_name, dt_idkind_name(DT_IDENT_ARRAY)); } /* * Now that we've confirmed our left-hand side is a DT_NODE_VAR * of idkind DT_IDENT_ARRAY, we need to splice the [ node from * the parse tree and leave a cooked DT_NODE_VAR in its place * where dn_args for the VAR node is the right-hand 'rp' tree, * as shown in the parse tree diagram below: * * / / * [ OP2 "[" ]=dnp [ VAR ]=dnp * / \ => | * / \ +- dn_args -> [ ??? ]=rp * [ VAR ]=lp [ ??? ]=rp * * Since the final dt_node_cook(dnp) can fail using longjmp we * must perform the transformations as a group first by over- * writing 'dnp' to become the VAR node, so that the parse tree * is guaranteed to be in a consistent state if the cook fails. */ assert(lp->dn_kind == DT_NODE_VAR); assert(lp->dn_args == NULL); lnp = dnp->dn_link; bcopy(lp, dnp, sizeof (dt_node_t)); dnp->dn_link = lnp; dnp->dn_args = rp; dnp->dn_list = NULL; dt_node_free(lp); return (dt_node_cook(dnp, idflags)); } case DT_TOK_XLATE: { dt_xlator_t *dxp; assert(lp->dn_kind == DT_NODE_TYPE); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); dxp = dt_xlator_lookup(dtp, rp, lp, DT_XLATE_FUZZY); if (dxp == NULL) { xyerror(D_XLATE_NONE, "cannot translate from \"%s\" to \"%s\"\n", dt_node_type_name(rp, n1, sizeof (n1)), dt_node_type_name(lp, n2, sizeof (n2))); } dnp->dn_ident = dt_xlator_ident(dxp, lp->dn_ctfp, lp->dn_type); - dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp)); + dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp), + B_FALSE); dt_node_attr_assign(dnp, dt_attr_min(rp->dn_attr, dnp->dn_ident->di_attr)); break; } case DT_TOK_LPAR: { ctf_id_t ltype, rtype; uint_t lkind, rkind; assert(lp->dn_kind == DT_NODE_TYPE); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); ltype = ctf_type_resolve(lp->dn_ctfp, lp->dn_type); lkind = ctf_type_kind(lp->dn_ctfp, ltype); rtype = ctf_type_resolve(rp->dn_ctfp, rp->dn_type); rkind = ctf_type_kind(rp->dn_ctfp, rtype); /* * The rules for casting are loosely explained in K&R[A7.5] * and K&R[A6]. Basically, we can cast to the same type or * same base type, between any kind of scalar values, from * arrays to pointers, and we can cast anything to void. * To these rules D adds casts from scalars to strings. */ if (ctf_type_compat(lp->dn_ctfp, lp->dn_type, rp->dn_ctfp, rp->dn_type)) /*EMPTY*/; else if (dt_node_is_scalar(lp) && (dt_node_is_scalar(rp) || rkind == CTF_K_FUNCTION)) /*EMPTY*/; else if (dt_node_is_void(lp)) /*EMPTY*/; else if (lkind == CTF_K_POINTER && dt_node_is_pointer(rp)) /*EMPTY*/; else if (dt_node_is_string(lp) && (dt_node_is_scalar(rp) || dt_node_is_pointer(rp) || dt_node_is_strcompat(rp))) /*EMPTY*/; else { xyerror(D_CAST_INVAL, "invalid cast expression: \"%s\" to \"%s\"\n", dt_node_type_name(rp, n1, sizeof (n1)), dt_node_type_name(lp, n2, sizeof (n2))); } dt_node_type_propagate(lp, dnp); /* see K&R[A7.5] */ dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr)); /* * If it's a pointer then should be able to (attempt to) * assign to it. */ if (lkind == CTF_K_POINTER) dnp->dn_flags |= DT_NF_WRITABLE; break; } case DT_TOK_COMMA: lp = dnp->dn_left = dt_node_cook(lp, DT_IDFLG_REF); rp = dnp->dn_right = dt_node_cook(rp, DT_IDFLG_REF); if (dt_node_is_dynamic(lp) || dt_node_is_dynamic(rp)) { xyerror(D_OP_DYN, "operator %s operands " "cannot be of dynamic type\n", opstr(op)); } if (dt_node_is_actfunc(lp) || dt_node_is_actfunc(rp)) { xyerror(D_OP_ACT, "operator %s operands " "cannot be actions\n", opstr(op)); } dt_node_type_propagate(rp, dnp); /* see K&R[A7.18] */ dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr)); break; default: xyerror(D_UNKNOWN, "invalid binary op %s\n", opstr(op)); } /* * Complete the conversion of E1[E2] to *((E1)+(E2)) that we started * at the top of our switch() above (see K&R[A7.3.1]). Since E2 is * parsed as an argument_expression_list by dt_grammar.y, we can * end up with a comma-separated list inside of a non-associative * array reference. We check for this and report an appropriate error. */ if (dnp->dn_op == DT_TOK_LBRAC && op == DT_TOK_ADD) { dt_node_t *pnp; if (rp->dn_list != NULL) { xyerror(D_ARR_BADREF, "cannot access %s as an associative array\n", dt_node_name(lp, n1, sizeof (n1))); } dnp->dn_op = DT_TOK_ADD; pnp = dt_node_op1(DT_TOK_DEREF, dnp); /* * Cook callbacks are not typically permitted to allocate nodes. * When we do, we must insert them in the middle of an existing * allocation list rather than having them appended to the pcb * list because the sub-expression may be part of a definition. */ assert(yypcb->pcb_list == pnp); yypcb->pcb_list = pnp->dn_link; pnp->dn_link = dnp->dn_link; dnp->dn_link = pnp; return (dt_node_cook(pnp, DT_IDFLG_REF)); } return (dnp); } /*ARGSUSED*/ static dt_node_t * dt_cook_op3(dt_node_t *dnp, uint_t idflags) { dt_node_t *lp, *rp; ctf_file_t *ctfp; ctf_id_t type; dnp->dn_expr = dt_node_cook(dnp->dn_expr, DT_IDFLG_REF); lp = dnp->dn_left = dt_node_cook(dnp->dn_left, DT_IDFLG_REF); rp = dnp->dn_right = dt_node_cook(dnp->dn_right, DT_IDFLG_REF); if (!dt_node_is_scalar(dnp->dn_expr)) { xyerror(D_OP_SCALAR, "operator ?: expression must be of scalar type\n"); } if (dt_node_is_dynamic(lp) || dt_node_is_dynamic(rp)) { xyerror(D_OP_DYN, "operator ?: operands cannot be of dynamic type\n"); } /* * The rules for type checking for the ternary operator are complex and * are described in the ANSI-C spec (see K&R[A7.16]). We implement * the various tests in order from least to most expensive. */ if (ctf_type_compat(lp->dn_ctfp, lp->dn_type, rp->dn_ctfp, rp->dn_type)) { ctfp = lp->dn_ctfp; type = lp->dn_type; } else if (dt_node_is_integer(lp) && dt_node_is_integer(rp)) { dt_type_promote(lp, rp, &ctfp, &type); } else if (dt_node_is_strcompat(lp) && dt_node_is_strcompat(rp) && (dt_node_is_string(lp) || dt_node_is_string(rp))) { ctfp = DT_STR_CTFP(yypcb->pcb_hdl); type = DT_STR_TYPE(yypcb->pcb_hdl); } else if (dt_node_is_ptrcompat(lp, rp, &ctfp, &type) == 0) { xyerror(D_OP_INCOMPAT, "operator ?: operands must have compatible types\n"); } if (dt_node_is_actfunc(lp) || dt_node_is_actfunc(rp)) { xyerror(D_OP_ACT, "action cannot be " "used in a conditional context\n"); } - dt_node_type_assign(dnp, ctfp, type); + dt_node_type_assign(dnp, ctfp, type, B_FALSE); dt_node_attr_assign(dnp, dt_attr_min(dnp->dn_expr->dn_attr, dt_attr_min(lp->dn_attr, rp->dn_attr))); return (dnp); } static dt_node_t * dt_cook_statement(dt_node_t *dnp, uint_t idflags) { dnp->dn_expr = dt_node_cook(dnp->dn_expr, idflags); dt_node_attr_assign(dnp, dnp->dn_expr->dn_attr); return (dnp); } /* * If dn_aggfun is set, this node is a collapsed aggregation assignment (see * the special case code for DT_TOK_ASGN in dt_cook_op2() above), in which * case we cook both the tuple and the function call. If dn_aggfun is NULL, * this node is just a reference to the aggregation's type and attributes. */ /*ARGSUSED*/ static dt_node_t * dt_cook_aggregation(dt_node_t *dnp, uint_t idflags) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; if (dnp->dn_aggfun != NULL) { dnp->dn_aggfun = dt_node_cook(dnp->dn_aggfun, DT_IDFLG_REF); dt_node_attr_assign(dnp, dt_ident_cook(dnp, dnp->dn_ident, &dnp->dn_aggtup)); } else { - dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp)); + dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp), + B_FALSE); dt_node_attr_assign(dnp, dnp->dn_ident->di_attr); } return (dnp); } /* * Since D permits new variable identifiers to be instantiated in any program * expression, we may need to cook a clause's predicate either before or after * the action list depending on the program code in question. Consider: * * probe-description-list probe-description-list * /x++/ /x == 0/ * { { * trace(x); trace(x++); * } } * * In the left-hand example, the predicate uses operator ++ to instantiate 'x' * as a variable of type int64_t. The predicate must be cooked first because * otherwise the statement trace(x) refers to an unknown identifier. In the * right-hand example, the action list uses ++ to instantiate 'x'; the action * list must be cooked first because otherwise the predicate x == 0 refers to * an unknown identifier. In order to simplify programming, we support both. * * When cooking a clause, we cook the action statements before the predicate by * default, since it seems more common to create or modify identifiers in the * action list. If cooking fails due to an unknown identifier, we attempt to * cook the predicate (i.e. do it first) and then go back and cook the actions. * If this, too, fails (or if we get an error other than D_IDENT_UNDEF) we give * up and report failure back to the user. There are five possible paths: * * cook actions = OK, cook predicate = OK -> OK * cook actions = OK, cook predicate = ERR -> ERR * cook actions = ERR, cook predicate = ERR -> ERR * cook actions = ERR, cook predicate = OK, cook actions = OK -> OK * cook actions = ERR, cook predicate = OK, cook actions = ERR -> ERR * * The programmer can still defeat our scheme by creating circular definition * dependencies between predicates and actions, as in this example clause: * * probe-description-list * /x++ && y == 0/ * { * trace(x + y++); * } * * but it doesn't seem worth the complexity to handle such rare cases. The * user can simply use the D variable declaration syntax to work around them. */ static dt_node_t * dt_cook_clause(dt_node_t *dnp, uint_t idflags) { volatile int err, tries; jmp_buf ojb; /* * Before assigning dn_ctxattr, temporarily assign the probe attribute * to 'dnp' itself to force an attribute check and minimum violation. */ dt_node_attr_assign(dnp, yypcb->pcb_pinfo.dtp_attr); dnp->dn_ctxattr = yypcb->pcb_pinfo.dtp_attr; bcopy(yypcb->pcb_jmpbuf, ojb, sizeof (jmp_buf)); tries = 0; if (dnp->dn_pred != NULL && (err = setjmp(yypcb->pcb_jmpbuf)) != 0) { bcopy(ojb, yypcb->pcb_jmpbuf, sizeof (jmp_buf)); if (tries++ != 0 || err != EDT_COMPILER || ( yypcb->pcb_hdl->dt_errtag != dt_errtag(D_IDENT_UNDEF) && yypcb->pcb_hdl->dt_errtag != dt_errtag(D_VAR_UNDEF))) longjmp(yypcb->pcb_jmpbuf, err); } if (tries == 0) { yylabel("action list"); dt_node_attr_assign(dnp, dt_node_list_cook(&dnp->dn_acts, idflags)); bcopy(ojb, yypcb->pcb_jmpbuf, sizeof (jmp_buf)); yylabel(NULL); } if (dnp->dn_pred != NULL) { yylabel("predicate"); dnp->dn_pred = dt_node_cook(dnp->dn_pred, idflags); dt_node_attr_assign(dnp, dt_attr_min(dnp->dn_attr, dnp->dn_pred->dn_attr)); if (!dt_node_is_scalar(dnp->dn_pred)) { xyerror(D_PRED_SCALAR, "predicate result must be of scalar type\n"); } yylabel(NULL); } if (tries != 0) { yylabel("action list"); dt_node_attr_assign(dnp, dt_node_list_cook(&dnp->dn_acts, idflags)); yylabel(NULL); } return (dnp); } /*ARGSUSED*/ static dt_node_t * dt_cook_inline(dt_node_t *dnp, uint_t idflags) { dt_idnode_t *inp = dnp->dn_ident->di_iarg; dt_ident_t *rdp; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; assert(dnp->dn_ident->di_flags & DT_IDFLG_INLINE); assert(inp->din_root->dn_flags & DT_NF_COOKED); /* * If we are inlining a translation, verify that the inline declaration * type exactly matches the type that is returned by the translation. * Otherwise just use dt_node_is_argcompat() to check the types. */ if ((rdp = dt_node_resolve(inp->din_root, DT_IDENT_XLSOU)) != NULL || (rdp = dt_node_resolve(inp->din_root, DT_IDENT_XLPTR)) != NULL) { ctf_file_t *lctfp = dnp->dn_ctfp; ctf_id_t ltype = ctf_type_resolve(lctfp, dnp->dn_type); dt_xlator_t *dxp = rdp->di_data; ctf_file_t *rctfp = dxp->dx_dst_ctfp; ctf_id_t rtype = dxp->dx_dst_base; if (ctf_type_kind(lctfp, ltype) == CTF_K_POINTER) { ltype = ctf_type_reference(lctfp, ltype); ltype = ctf_type_resolve(lctfp, ltype); } if (ctf_type_compat(lctfp, ltype, rctfp, rtype) == 0) { dnerror(dnp, D_OP_INCOMPAT, "inline %s definition uses incompatible types: " "\"%s\" = \"%s\"\n", dnp->dn_ident->di_name, dt_type_name(lctfp, ltype, n1, sizeof (n1)), dt_type_name(rctfp, rtype, n2, sizeof (n2))); } } else if (dt_node_is_argcompat(dnp, inp->din_root) == 0) { dnerror(dnp, D_OP_INCOMPAT, "inline %s definition uses incompatible types: " "\"%s\" = \"%s\"\n", dnp->dn_ident->di_name, dt_node_type_name(dnp, n1, sizeof (n1)), dt_node_type_name(inp->din_root, n2, sizeof (n2))); } return (dnp); } static dt_node_t * dt_cook_member(dt_node_t *dnp, uint_t idflags) { dnp->dn_membexpr = dt_node_cook(dnp->dn_membexpr, idflags); dt_node_attr_assign(dnp, dnp->dn_membexpr->dn_attr); return (dnp); } /*ARGSUSED*/ static dt_node_t * dt_cook_xlator(dt_node_t *dnp, uint_t idflags) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_xlator_t *dxp = dnp->dn_xlator; dt_node_t *mnp; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; dtrace_attribute_t attr = _dtrace_maxattr; ctf_membinfo_t ctm; /* * Before cooking each translator member, we push a reference to the * hash containing translator-local identifiers on to pcb_globals to * temporarily interpose these identifiers in front of other globals. */ dt_idstack_push(&yypcb->pcb_globals, dxp->dx_locals); for (mnp = dnp->dn_members; mnp != NULL; mnp = mnp->dn_list) { if (ctf_member_info(dxp->dx_dst_ctfp, dxp->dx_dst_type, mnp->dn_membname, &ctm) == CTF_ERR) { xyerror(D_XLATE_MEMB, "translator member %s is not a member of %s\n", mnp->dn_membname, ctf_type_name(dxp->dx_dst_ctfp, dxp->dx_dst_type, n1, sizeof (n1))); } (void) dt_node_cook(mnp, DT_IDFLG_REF); - dt_node_type_assign(mnp, dxp->dx_dst_ctfp, ctm.ctm_type); + dt_node_type_assign(mnp, dxp->dx_dst_ctfp, ctm.ctm_type, + B_FALSE); attr = dt_attr_min(attr, mnp->dn_attr); if (dt_node_is_argcompat(mnp, mnp->dn_membexpr) == 0) { xyerror(D_XLATE_INCOMPAT, "translator member %s definition uses " "incompatible types: \"%s\" = \"%s\"\n", mnp->dn_membname, dt_node_type_name(mnp, n1, sizeof (n1)), dt_node_type_name(mnp->dn_membexpr, n2, sizeof (n2))); } } dt_idstack_pop(&yypcb->pcb_globals, dxp->dx_locals); dxp->dx_souid.di_attr = attr; dxp->dx_ptrid.di_attr = attr; - dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp)); + dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp), B_FALSE); dt_node_attr_assign(dnp, _dtrace_defattr); return (dnp); } static void dt_node_provider_cmp_argv(dt_provider_t *pvp, dt_node_t *pnp, const char *kind, uint_t old_argc, dt_node_t *old_argv, uint_t new_argc, dt_node_t *new_argv) { dt_probe_t *prp = pnp->dn_ident->di_data; uint_t i; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; if (old_argc != new_argc) { dnerror(pnp, D_PROV_INCOMPAT, "probe %s:%s %s prototype mismatch:\n" "\t current: %u arg%s\n\tprevious: %u arg%s\n", pvp->pv_desc.dtvd_name, prp->pr_ident->di_name, kind, new_argc, new_argc != 1 ? "s" : "", old_argc, old_argc != 1 ? "s" : ""); } for (i = 0; i < old_argc; i++, old_argv = old_argv->dn_list, new_argv = new_argv->dn_list) { if (ctf_type_cmp(old_argv->dn_ctfp, old_argv->dn_type, new_argv->dn_ctfp, new_argv->dn_type) == 0) continue; dnerror(pnp, D_PROV_INCOMPAT, "probe %s:%s %s prototype argument #%u mismatch:\n" "\t current: %s\n\tprevious: %s\n", pvp->pv_desc.dtvd_name, prp->pr_ident->di_name, kind, i + 1, dt_node_type_name(new_argv, n1, sizeof (n1)), dt_node_type_name(old_argv, n2, sizeof (n2))); } } /* * Compare a new probe declaration with an existing probe definition (either * from a previous declaration or cached from the kernel). If the existing * definition and declaration both have an input and output parameter list, * compare both lists. Otherwise compare only the output parameter lists. */ static void dt_node_provider_cmp(dt_provider_t *pvp, dt_node_t *pnp, dt_probe_t *old, dt_probe_t *new) { dt_node_provider_cmp_argv(pvp, pnp, "output", old->pr_xargc, old->pr_xargs, new->pr_xargc, new->pr_xargs); if (old->pr_nargs != old->pr_xargs && new->pr_nargs != new->pr_xargs) { dt_node_provider_cmp_argv(pvp, pnp, "input", old->pr_nargc, old->pr_nargs, new->pr_nargc, new->pr_nargs); } if (old->pr_nargs == old->pr_xargs && new->pr_nargs != new->pr_xargs) { if (pvp->pv_flags & DT_PROVIDER_IMPL) { dnerror(pnp, D_PROV_INCOMPAT, "provider interface mismatch: %s\n" "\t current: probe %s:%s has an output prototype\n" "\tprevious: probe %s:%s has no output prototype\n", pvp->pv_desc.dtvd_name, pvp->pv_desc.dtvd_name, new->pr_ident->di_name, pvp->pv_desc.dtvd_name, old->pr_ident->di_name); } if (old->pr_ident->di_gen == yypcb->pcb_hdl->dt_gen) old->pr_ident->di_flags |= DT_IDFLG_ORPHAN; dt_idhash_delete(pvp->pv_probes, old->pr_ident); dt_probe_declare(pvp, new); } } static void dt_cook_probe(dt_node_t *dnp, dt_provider_t *pvp) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; dt_probe_t *prp = dnp->dn_ident->di_data; dt_xlator_t *dxp; uint_t i; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; if (prp->pr_nargs == prp->pr_xargs) return; for (i = 0; i < prp->pr_xargc; i++) { dt_node_t *xnp = prp->pr_xargv[i]; dt_node_t *nnp = prp->pr_nargv[prp->pr_mapping[i]]; if ((dxp = dt_xlator_lookup(dtp, nnp, xnp, DT_XLATE_FUZZY)) != NULL) { if (dt_provider_xref(dtp, pvp, dxp->dx_id) != 0) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); continue; } if (dt_node_is_argcompat(nnp, xnp)) continue; /* no translator defined and none required */ dnerror(dnp, D_PROV_PRXLATOR, "translator for %s:%s output " "argument #%u from %s to %s is not defined\n", pvp->pv_desc.dtvd_name, dnp->dn_ident->di_name, i + 1, dt_node_type_name(nnp, n1, sizeof (n1)), dt_node_type_name(xnp, n2, sizeof (n2))); } } /*ARGSUSED*/ static dt_node_t * dt_cook_provider(dt_node_t *dnp, uint_t idflags) { dt_provider_t *pvp = dnp->dn_provider; dt_node_t *pnp; /* * If we're declaring a provider for the first time and it is unknown * to dtrace(7D), insert the probe definitions into the provider's hash. * If we're redeclaring a known provider, verify the interface matches. */ for (pnp = dnp->dn_probes; pnp != NULL; pnp = pnp->dn_list) { const char *probename = pnp->dn_ident->di_name; dt_probe_t *prp = dt_probe_lookup(pvp, probename); assert(pnp->dn_kind == DT_NODE_PROBE); if (prp != NULL && dnp->dn_provred) { dt_node_provider_cmp(pvp, pnp, prp, pnp->dn_ident->di_data); } else if (prp == NULL && dnp->dn_provred) { dnerror(pnp, D_PROV_INCOMPAT, "provider interface mismatch: %s\n" "\t current: probe %s:%s defined\n" "\tprevious: probe %s:%s not defined\n", dnp->dn_provname, dnp->dn_provname, probename, dnp->dn_provname, probename); } else if (prp != NULL) { dnerror(pnp, D_PROV_PRDUP, "probe redeclared: %s:%s\n", dnp->dn_provname, probename); } else dt_probe_declare(pvp, pnp->dn_ident->di_data); dt_cook_probe(pnp, pvp); } return (dnp); } /*ARGSUSED*/ static dt_node_t * dt_cook_none(dt_node_t *dnp, uint_t idflags) { return (dnp); } static dt_node_t *(*dt_cook_funcs[])(dt_node_t *, uint_t) = { dt_cook_none, /* DT_NODE_FREE */ dt_cook_none, /* DT_NODE_INT */ dt_cook_none, /* DT_NODE_STRING */ dt_cook_ident, /* DT_NODE_IDENT */ dt_cook_var, /* DT_NODE_VAR */ dt_cook_none, /* DT_NODE_SYM */ dt_cook_none, /* DT_NODE_TYPE */ dt_cook_func, /* DT_NODE_FUNC */ dt_cook_op1, /* DT_NODE_OP1 */ dt_cook_op2, /* DT_NODE_OP2 */ dt_cook_op3, /* DT_NODE_OP3 */ dt_cook_statement, /* DT_NODE_DEXPR */ dt_cook_statement, /* DT_NODE_DFUNC */ dt_cook_aggregation, /* DT_NODE_AGG */ dt_cook_none, /* DT_NODE_PDESC */ dt_cook_clause, /* DT_NODE_CLAUSE */ dt_cook_inline, /* DT_NODE_INLINE */ dt_cook_member, /* DT_NODE_MEMBER */ dt_cook_xlator, /* DT_NODE_XLATOR */ dt_cook_none, /* DT_NODE_PROBE */ dt_cook_provider, /* DT_NODE_PROVIDER */ dt_cook_none /* DT_NODE_PROG */ }; /* * Recursively cook the parse tree starting at the specified node. The idflags * parameter is used to indicate the type of reference (r/w) and is applied to * the resulting identifier if it is a D variable or D aggregation. */ dt_node_t * dt_node_cook(dt_node_t *dnp, uint_t idflags) { int oldlineno = yylineno; yylineno = dnp->dn_line; dnp = dt_cook_funcs[dnp->dn_kind](dnp, idflags); dnp->dn_flags |= DT_NF_COOKED; if (dnp->dn_kind == DT_NODE_VAR || dnp->dn_kind == DT_NODE_AGG) dnp->dn_ident->di_flags |= idflags; yylineno = oldlineno; return (dnp); } dtrace_attribute_t dt_node_list_cook(dt_node_t **pnp, uint_t idflags) { dtrace_attribute_t attr = _dtrace_defattr; dt_node_t *dnp, *nnp; for (dnp = (pnp != NULL ? *pnp : NULL); dnp != NULL; dnp = nnp) { nnp = dnp->dn_list; dnp = *pnp = dt_node_cook(dnp, idflags); attr = dt_attr_min(attr, dnp->dn_attr); dnp->dn_list = nnp; pnp = &dnp->dn_list; } return (attr); } void dt_node_list_free(dt_node_t **pnp) { dt_node_t *dnp, *nnp; for (dnp = (pnp != NULL ? *pnp : NULL); dnp != NULL; dnp = nnp) { nnp = dnp->dn_list; dt_node_free(dnp); } if (pnp != NULL) *pnp = NULL; } void dt_node_link_free(dt_node_t **pnp) { dt_node_t *dnp, *nnp; for (dnp = (pnp != NULL ? *pnp : NULL); dnp != NULL; dnp = nnp) { nnp = dnp->dn_link; dt_node_free(dnp); } for (dnp = (pnp != NULL ? *pnp : NULL); dnp != NULL; dnp = nnp) { nnp = dnp->dn_link; free(dnp); } if (pnp != NULL) *pnp = NULL; } dt_node_t * dt_node_link(dt_node_t *lp, dt_node_t *rp) { dt_node_t *dnp; if (lp == NULL) return (rp); else if (rp == NULL) return (lp); for (dnp = lp; dnp->dn_list != NULL; dnp = dnp->dn_list) continue; dnp->dn_list = rp; return (lp); } /* * Compute the DOF dtrace_diftype_t representation of a node's type. This is * called from a variety of places in the library so it cannot assume yypcb * is valid: any references to handle-specific data must be made through 'dtp'. */ void dt_node_diftype(dtrace_hdl_t *dtp, const dt_node_t *dnp, dtrace_diftype_t *tp) { if (dnp->dn_ctfp == DT_STR_CTFP(dtp) && dnp->dn_type == DT_STR_TYPE(dtp)) { tp->dtdt_kind = DIF_TYPE_STRING; tp->dtdt_ckind = CTF_K_UNKNOWN; } else { tp->dtdt_kind = DIF_TYPE_CTF; tp->dtdt_ckind = ctf_type_kind(dnp->dn_ctfp, ctf_type_resolve(dnp->dn_ctfp, dnp->dn_type)); } - tp->dtdt_flags = (dnp->dn_flags & DT_NF_REF) ? DIF_TF_BYREF : 0; + tp->dtdt_flags = (dnp->dn_flags & DT_NF_REF) ? + (dnp->dn_flags & DT_NF_USERLAND) ? DIF_TF_BYUREF : + DIF_TF_BYREF : 0; tp->dtdt_pad = 0; tp->dtdt_size = ctf_type_size(dnp->dn_ctfp, dnp->dn_type); } void dt_node_printr(dt_node_t *dnp, FILE *fp, int depth) { char n[DT_TYPE_NAMELEN], buf[BUFSIZ], a[8]; const dtrace_syminfo_t *dts; const dt_idnode_t *inp; dt_node_t *arg; (void) fprintf(fp, "%*s", depth * 2, ""); (void) dt_attr_str(dnp->dn_attr, a, sizeof (a)); if (dnp->dn_ctfp != NULL && dnp->dn_type != CTF_ERR && ctf_type_name(dnp->dn_ctfp, dnp->dn_type, n, sizeof (n)) != NULL) { (void) snprintf(buf, BUFSIZ, "type=<%s> attr=%s flags=", n, a); } else { (void) snprintf(buf, BUFSIZ, "type=<%ld> attr=%s flags=", dnp->dn_type, a); } if (dnp->dn_flags != 0) { n[0] = '\0'; if (dnp->dn_flags & DT_NF_SIGNED) (void) strcat(n, ",SIGN"); if (dnp->dn_flags & DT_NF_COOKED) (void) strcat(n, ",COOK"); if (dnp->dn_flags & DT_NF_REF) (void) strcat(n, ",REF"); if (dnp->dn_flags & DT_NF_LVALUE) (void) strcat(n, ",LVAL"); if (dnp->dn_flags & DT_NF_WRITABLE) (void) strcat(n, ",WRITE"); if (dnp->dn_flags & DT_NF_BITFIELD) (void) strcat(n, ",BITF"); if (dnp->dn_flags & DT_NF_USERLAND) (void) strcat(n, ",USER"); (void) strcat(buf, n + 1); } else (void) strcat(buf, "0"); switch (dnp->dn_kind) { case DT_NODE_FREE: (void) fprintf(fp, "FREE \n", (void *)dnp); break; case DT_NODE_INT: (void) fprintf(fp, "INT 0x%llx (%s)\n", (u_longlong_t)dnp->dn_value, buf); break; case DT_NODE_STRING: (void) fprintf(fp, "STRING \"%s\" (%s)\n", dnp->dn_string, buf); break; case DT_NODE_IDENT: (void) fprintf(fp, "IDENT %s (%s)\n", dnp->dn_string, buf); break; case DT_NODE_VAR: (void) fprintf(fp, "VARIABLE %s%s (%s)\n", (dnp->dn_ident->di_flags & DT_IDFLG_LOCAL) ? "this->" : (dnp->dn_ident->di_flags & DT_IDFLG_TLS) ? "self->" : "", dnp->dn_ident->di_name, buf); if (dnp->dn_args != NULL) (void) fprintf(fp, "%*s[\n", depth * 2, ""); for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list) { dt_node_printr(arg, fp, depth + 1); if (arg->dn_list != NULL) (void) fprintf(fp, "%*s,\n", depth * 2, ""); } if (dnp->dn_args != NULL) (void) fprintf(fp, "%*s]\n", depth * 2, ""); break; case DT_NODE_SYM: dts = dnp->dn_ident->di_data; (void) fprintf(fp, "SYMBOL %s`%s (%s)\n", dts->dts_object, dts->dts_name, buf); break; case DT_NODE_TYPE: if (dnp->dn_string != NULL) { (void) fprintf(fp, "TYPE (%s) %s\n", buf, dnp->dn_string); } else (void) fprintf(fp, "TYPE (%s)\n", buf); break; case DT_NODE_FUNC: (void) fprintf(fp, "FUNC %s (%s)\n", dnp->dn_ident->di_name, buf); for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list) { dt_node_printr(arg, fp, depth + 1); if (arg->dn_list != NULL) (void) fprintf(fp, "%*s,\n", depth * 2, ""); } break; case DT_NODE_OP1: (void) fprintf(fp, "OP1 %s (%s)\n", opstr(dnp->dn_op), buf); dt_node_printr(dnp->dn_child, fp, depth + 1); break; case DT_NODE_OP2: (void) fprintf(fp, "OP2 %s (%s)\n", opstr(dnp->dn_op), buf); dt_node_printr(dnp->dn_left, fp, depth + 1); dt_node_printr(dnp->dn_right, fp, depth + 1); break; case DT_NODE_OP3: (void) fprintf(fp, "OP3 (%s)\n", buf); dt_node_printr(dnp->dn_expr, fp, depth + 1); (void) fprintf(fp, "%*s?\n", depth * 2, ""); dt_node_printr(dnp->dn_left, fp, depth + 1); (void) fprintf(fp, "%*s:\n", depth * 2, ""); dt_node_printr(dnp->dn_right, fp, depth + 1); break; case DT_NODE_DEXPR: case DT_NODE_DFUNC: (void) fprintf(fp, "D EXPRESSION attr=%s\n", a); dt_node_printr(dnp->dn_expr, fp, depth + 1); break; case DT_NODE_AGG: (void) fprintf(fp, "AGGREGATE @%s attr=%s [\n", dnp->dn_ident->di_name, a); for (arg = dnp->dn_aggtup; arg != NULL; arg = arg->dn_list) { dt_node_printr(arg, fp, depth + 1); if (arg->dn_list != NULL) (void) fprintf(fp, "%*s,\n", depth * 2, ""); } if (dnp->dn_aggfun) { (void) fprintf(fp, "%*s] = ", depth * 2, ""); dt_node_printr(dnp->dn_aggfun, fp, depth + 1); } else (void) fprintf(fp, "%*s]\n", depth * 2, ""); if (dnp->dn_aggfun) (void) fprintf(fp, "%*s)\n", depth * 2, ""); break; case DT_NODE_PDESC: (void) fprintf(fp, "PDESC %s:%s:%s:%s [%u]\n", dnp->dn_desc->dtpd_provider, dnp->dn_desc->dtpd_mod, dnp->dn_desc->dtpd_func, dnp->dn_desc->dtpd_name, dnp->dn_desc->dtpd_id); break; case DT_NODE_CLAUSE: (void) fprintf(fp, "CLAUSE attr=%s\n", a); for (arg = dnp->dn_pdescs; arg != NULL; arg = arg->dn_list) dt_node_printr(arg, fp, depth + 1); (void) fprintf(fp, "%*sCTXATTR %s\n", depth * 2, "", dt_attr_str(dnp->dn_ctxattr, a, sizeof (a))); if (dnp->dn_pred != NULL) { (void) fprintf(fp, "%*sPREDICATE /\n", depth * 2, ""); dt_node_printr(dnp->dn_pred, fp, depth + 1); (void) fprintf(fp, "%*s/\n", depth * 2, ""); } for (arg = dnp->dn_acts; arg != NULL; arg = arg->dn_list) dt_node_printr(arg, fp, depth + 1); break; case DT_NODE_INLINE: inp = dnp->dn_ident->di_iarg; (void) fprintf(fp, "INLINE %s (%s)\n", dnp->dn_ident->di_name, buf); dt_node_printr(inp->din_root, fp, depth + 1); break; case DT_NODE_MEMBER: (void) fprintf(fp, "MEMBER %s (%s)\n", dnp->dn_membname, buf); if (dnp->dn_membexpr) dt_node_printr(dnp->dn_membexpr, fp, depth + 1); break; case DT_NODE_XLATOR: (void) fprintf(fp, "XLATOR (%s)", buf); if (ctf_type_name(dnp->dn_xlator->dx_src_ctfp, dnp->dn_xlator->dx_src_type, n, sizeof (n)) != NULL) (void) fprintf(fp, " from <%s>", n); if (ctf_type_name(dnp->dn_xlator->dx_dst_ctfp, dnp->dn_xlator->dx_dst_type, n, sizeof (n)) != NULL) (void) fprintf(fp, " to <%s>", n); (void) fprintf(fp, "\n"); for (arg = dnp->dn_members; arg != NULL; arg = arg->dn_list) dt_node_printr(arg, fp, depth + 1); break; case DT_NODE_PROBE: (void) fprintf(fp, "PROBE %s\n", dnp->dn_ident->di_name); break; case DT_NODE_PROVIDER: (void) fprintf(fp, "PROVIDER %s (%s)\n", dnp->dn_provname, dnp->dn_provred ? "redecl" : "decl"); for (arg = dnp->dn_probes; arg != NULL; arg = arg->dn_list) dt_node_printr(arg, fp, depth + 1); break; case DT_NODE_PROG: (void) fprintf(fp, "PROGRAM attr=%s\n", a); for (arg = dnp->dn_list; arg != NULL; arg = arg->dn_list) dt_node_printr(arg, fp, depth + 1); break; default: (void) fprintf(fp, "\n", (void *)dnp, dnp->dn_kind); } } int dt_node_root(dt_node_t *dnp) { yypcb->pcb_root = dnp; return (0); } /*PRINTFLIKE3*/ void dnerror(const dt_node_t *dnp, dt_errtag_t tag, const char *format, ...) { int oldlineno = yylineno; va_list ap; yylineno = dnp->dn_line; va_start(ap, format); xyvwarn(tag, format, ap); va_end(ap); yylineno = oldlineno; longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } /*PRINTFLIKE3*/ void dnwarn(const dt_node_t *dnp, dt_errtag_t tag, const char *format, ...) { int oldlineno = yylineno; va_list ap; yylineno = dnp->dn_line; va_start(ap, format); xyvwarn(tag, format, ap); va_end(ap); yylineno = oldlineno; } /*PRINTFLIKE2*/ void xyerror(dt_errtag_t tag, const char *format, ...) { va_list ap; va_start(ap, format); xyvwarn(tag, format, ap); va_end(ap); longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } /*PRINTFLIKE2*/ void xywarn(dt_errtag_t tag, const char *format, ...) { va_list ap; va_start(ap, format); xyvwarn(tag, format, ap); va_end(ap); } void xyvwarn(dt_errtag_t tag, const char *format, va_list ap) { if (yypcb == NULL) return; /* compiler is not currently active: act as a no-op */ dt_set_errmsg(yypcb->pcb_hdl, dt_errtag(tag), yypcb->pcb_region, yypcb->pcb_filetag, yypcb->pcb_fileptr ? yylineno : 0, format, ap); } /*PRINTFLIKE1*/ void yyerror(const char *format, ...) { va_list ap; va_start(ap, format); yyvwarn(format, ap); va_end(ap); longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); } /*PRINTFLIKE1*/ void yywarn(const char *format, ...) { va_list ap; va_start(ap, format); yyvwarn(format, ap); va_end(ap); } void yyvwarn(const char *format, va_list ap) { if (yypcb == NULL) return; /* compiler is not currently active: act as a no-op */ dt_set_errmsg(yypcb->pcb_hdl, dt_errtag(D_SYNTAX), yypcb->pcb_region, yypcb->pcb_filetag, yypcb->pcb_fileptr ? yylineno : 0, format, ap); if (strchr(format, '\n') == NULL) { dtrace_hdl_t *dtp = yypcb->pcb_hdl; size_t len = strlen(dtp->dt_errmsg); char *p, *s = dtp->dt_errmsg + len; size_t n = sizeof (dtp->dt_errmsg) - len; if (yytext[0] == '\0') (void) snprintf(s, n, " near end of input"); else if (yytext[0] == '\n') (void) snprintf(s, n, " near end of line"); else { if ((p = strchr(yytext, '\n')) != NULL) *p = '\0'; /* crop at newline */ (void) snprintf(s, n, " near \"%s\"", yytext); } } } void yylabel(const char *label) { dt_dprintf("set label to <%s>\n", label ? label : "NULL"); yypcb->pcb_region = label; } int yywrap(void) { return (1); /* indicate that lex should return a zero token for EOF */ } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_parser.h =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_parser.h (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_parser.h (revision 268578) @@ -1,285 +1,287 @@ /* * 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) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ #ifndef _DT_PARSER_H #define _DT_PARSER_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include typedef struct dt_node { ctf_file_t *dn_ctfp; /* CTF type container for node's type */ ctf_id_t dn_type; /* CTF type reference for node's type */ uchar_t dn_kind; /* node kind (DT_NODE_*, defined below) */ uchar_t dn_flags; /* node flags (DT_NF_*, defined below) */ ushort_t dn_op; /* operator (DT_TOK_*, defined by lex) */ int dn_line; /* line number for error messages */ int dn_reg; /* register allocated by cg */ dtrace_attribute_t dn_attr; /* node stability attributes */ /* * D compiler nodes, as is the usual style, contain a union of the * different sub-elements required by the various kinds of nodes. * These sub-elements are accessed using the macros defined below. */ union { struct { uintmax_t _value; /* integer value */ char *_string; /* string value */ } _const; struct { dt_ident_t *_ident; /* identifier reference */ struct dt_node *_links[3]; /* child node pointers */ } _nodes; struct { struct dt_node *_descs; /* list of descriptions */ struct dt_node *_pred; /* predicate expression */ struct dt_node *_acts; /* action statement list */ dt_idhash_t *_locals; /* local variable hash */ dtrace_attribute_t _attr; /* context attributes */ } _clause; struct { char *_spec; /* specifier string (if any) */ dtrace_probedesc_t *_desc; /* final probe description */ } _pdesc; struct { char *_name; /* string name of member */ struct dt_node *_expr; /* expression node pointer */ dt_xlator_t *_xlator; /* translator reference */ uint_t _id; /* member identifier */ } _member; struct { dt_xlator_t *_xlator; /* translator reference */ struct dt_node *_xmemb; /* individual xlator member */ struct dt_node *_membs; /* list of member nodes */ } _xlator; struct { char *_name; /* string name of provider */ struct dt_provider *_pvp; /* provider references */ struct dt_node *_probes; /* list of probe nodes */ int _redecl; /* provider redeclared */ } _provider; } dn_u; struct dt_node *dn_list; /* parse tree list link */ struct dt_node *dn_link; /* allocation list link */ } dt_node_t; #define dn_value dn_u._const._value /* DT_NODE_INT */ #define dn_string dn_u._const._string /* STRING, IDENT, TYPE */ #define dn_ident dn_u._nodes._ident /* VAR,SYM,FUN,AGG,INL,PROBE */ #define dn_args dn_u._nodes._links[0] /* DT_NODE_VAR, FUNC */ #define dn_child dn_u._nodes._links[0] /* DT_NODE_OP1 */ #define dn_left dn_u._nodes._links[0] /* DT_NODE_OP2, OP3 */ #define dn_right dn_u._nodes._links[1] /* DT_NODE_OP2, OP3 */ #define dn_expr dn_u._nodes._links[2] /* DT_NODE_OP3, DEXPR */ #define dn_aggfun dn_u._nodes._links[0] /* DT_NODE_AGG */ #define dn_aggtup dn_u._nodes._links[1] /* DT_NODE_AGG */ #define dn_pdescs dn_u._clause._descs /* DT_NODE_CLAUSE */ #define dn_pred dn_u._clause._pred /* DT_NODE_CLAUSE */ #define dn_acts dn_u._clause._acts /* DT_NODE_CLAUSE */ #define dn_locals dn_u._clause._locals /* DT_NODE_CLAUSE */ #define dn_ctxattr dn_u._clause._attr /* DT_NODE_CLAUSE */ #define dn_spec dn_u._pdesc._spec /* DT_NODE_PDESC */ #define dn_desc dn_u._pdesc._desc /* DT_NODE_PDESC */ #define dn_membname dn_u._member._name /* DT_NODE_MEMBER */ #define dn_membexpr dn_u._member._expr /* DT_NODE_MEMBER */ #define dn_membxlator dn_u._member._xlator /* DT_NODE_MEMBER */ #define dn_membid dn_u._member._id /* DT_NODE_MEMBER */ #define dn_xlator dn_u._xlator._xlator /* DT_NODE_XLATOR */ #define dn_xmember dn_u._xlator._xmemb /* DT_NODE_XLATOR */ #define dn_members dn_u._xlator._membs /* DT_NODE_XLATOR */ #define dn_provname dn_u._provider._name /* DT_NODE_PROVIDER */ #define dn_provider dn_u._provider._pvp /* DT_NODE_PROVIDER */ #define dn_provred dn_u._provider._redecl /* DT_NODE_PROVIDER */ #define dn_probes dn_u._provider._probes /* DT_NODE_PROVIDER */ #define DT_NODE_FREE 0 /* unused node (waiting to be freed) */ #define DT_NODE_INT 1 /* integer value */ #define DT_NODE_STRING 2 /* string value */ #define DT_NODE_IDENT 3 /* identifier */ #define DT_NODE_VAR 4 /* variable reference */ #define DT_NODE_SYM 5 /* symbol reference */ #define DT_NODE_TYPE 6 /* type reference or formal parameter */ #define DT_NODE_FUNC 7 /* function call */ #define DT_NODE_OP1 8 /* unary operator */ #define DT_NODE_OP2 9 /* binary operator */ #define DT_NODE_OP3 10 /* ternary operator */ #define DT_NODE_DEXPR 11 /* D expression action */ #define DT_NODE_DFUNC 12 /* D function action */ #define DT_NODE_AGG 13 /* aggregation */ #define DT_NODE_PDESC 14 /* probe description */ #define DT_NODE_CLAUSE 15 /* clause definition */ #define DT_NODE_INLINE 16 /* inline definition */ #define DT_NODE_MEMBER 17 /* member definition */ #define DT_NODE_XLATOR 18 /* translator definition */ #define DT_NODE_PROBE 19 /* probe definition */ #define DT_NODE_PROVIDER 20 /* provider definition */ #define DT_NODE_PROG 21 /* program translation unit */ #define DT_NF_SIGNED 0x01 /* data is a signed quantity (else unsigned) */ #define DT_NF_COOKED 0x02 /* data is a known type (else still cooking) */ #define DT_NF_REF 0x04 /* pass by reference (array, struct, union) */ #define DT_NF_LVALUE 0x08 /* node is an l-value according to ANSI-C */ #define DT_NF_WRITABLE 0x10 /* node is writable (can be modified) */ #define DT_NF_BITFIELD 0x20 /* node is an integer bitfield */ #define DT_NF_USERLAND 0x40 /* data is a userland address */ #define DT_TYPE_NAMELEN 128 /* reasonable size for ctf_type_name() */ extern int dt_node_is_integer(const dt_node_t *); extern int dt_node_is_float(const dt_node_t *); extern int dt_node_is_scalar(const dt_node_t *); extern int dt_node_is_arith(const dt_node_t *); extern int dt_node_is_vfptr(const dt_node_t *); extern int dt_node_is_dynamic(const dt_node_t *); extern int dt_node_is_stack(const dt_node_t *); extern int dt_node_is_symaddr(const dt_node_t *); extern int dt_node_is_usymaddr(const dt_node_t *); extern int dt_node_is_string(const dt_node_t *); extern int dt_node_is_strcompat(const dt_node_t *); extern int dt_node_is_pointer(const dt_node_t *); extern int dt_node_is_void(const dt_node_t *); extern int dt_node_is_ptrcompat(const dt_node_t *, const dt_node_t *, ctf_file_t **, ctf_id_t *); extern int dt_node_is_argcompat(const dt_node_t *, const dt_node_t *); extern int dt_node_is_posconst(const dt_node_t *); extern int dt_node_is_actfunc(const dt_node_t *); extern dt_node_t *dt_node_int(uintmax_t); extern dt_node_t *dt_node_string(char *); extern dt_node_t *dt_node_ident(char *); extern dt_node_t *dt_node_type(dt_decl_t *); extern dt_node_t *dt_node_vatype(void); extern dt_node_t *dt_node_decl(void); extern dt_node_t *dt_node_func(dt_node_t *, dt_node_t *); extern dt_node_t *dt_node_offsetof(dt_decl_t *, char *); extern dt_node_t *dt_node_op1(int, dt_node_t *); extern dt_node_t *dt_node_op2(int, dt_node_t *, dt_node_t *); extern dt_node_t *dt_node_op3(dt_node_t *, dt_node_t *, dt_node_t *); extern dt_node_t *dt_node_statement(dt_node_t *); extern dt_node_t *dt_node_pdesc_by_name(char *); extern dt_node_t *dt_node_pdesc_by_id(uintmax_t); extern dt_node_t *dt_node_clause(dt_node_t *, dt_node_t *, dt_node_t *); extern dt_node_t *dt_node_inline(dt_node_t *); extern dt_node_t *dt_node_member(dt_decl_t *, char *, dt_node_t *); extern dt_node_t *dt_node_xlator(dt_decl_t *, dt_decl_t *, char *, dt_node_t *); extern dt_node_t *dt_node_probe(char *, int, dt_node_t *, dt_node_t *); extern dt_node_t *dt_node_provider(char *, dt_node_t *); extern dt_node_t *dt_node_program(dt_node_t *); extern dt_node_t *dt_node_link(dt_node_t *, dt_node_t *); extern dt_node_t *dt_node_cook(dt_node_t *, uint_t); extern dt_node_t *dt_node_xalloc(dtrace_hdl_t *, int); extern void dt_node_free(dt_node_t *); extern dtrace_attribute_t dt_node_list_cook(dt_node_t **, uint_t); extern void dt_node_list_free(dt_node_t **); extern void dt_node_link_free(dt_node_t **); extern void dt_node_attr_assign(dt_node_t *, dtrace_attribute_t); -extern void dt_node_type_assign(dt_node_t *, ctf_file_t *, ctf_id_t); +extern void dt_node_type_assign(dt_node_t *, ctf_file_t *, ctf_id_t, boolean_t); extern void dt_node_type_propagate(const dt_node_t *, dt_node_t *); extern const char *dt_node_type_name(const dt_node_t *, char *, size_t); extern size_t dt_node_type_size(const dt_node_t *); extern dt_ident_t *dt_node_resolve(const dt_node_t *, uint_t); extern size_t dt_node_sizeof(const dt_node_t *); extern void dt_node_promote(dt_node_t *, dt_node_t *, dt_node_t *); extern void dt_node_diftype(dtrace_hdl_t *, const dt_node_t *, dtrace_diftype_t *); extern void dt_node_printr(dt_node_t *, FILE *, int); extern const char *dt_node_name(const dt_node_t *, char *, size_t); extern int dt_node_root(dt_node_t *); struct dtrace_typeinfo; /* see */ struct dt_pcb; /* see */ #define IS_CHAR(e) \ (((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \ (CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY) #define IS_VOID(e) \ ((e).cte_offset == 0 && (e).cte_bits == 0) extern int dt_type_lookup(const char *, struct dtrace_typeinfo *); extern int dt_type_pointer(struct dtrace_typeinfo *); extern const char *dt_type_name(ctf_file_t *, ctf_id_t, char *, size_t); typedef enum { YYS_CLAUSE, /* lex/yacc state for finding program clauses */ YYS_DEFINE, /* lex/yacc state for parsing persistent definitions */ YYS_EXPR, /* lex/yacc state for parsing D expressions */ YYS_DONE, /* lex/yacc state for indicating parse tree is done */ YYS_CONTROL /* lex/yacc state for parsing control lines */ } yystate_t; extern void dnerror(const dt_node_t *, dt_errtag_t, const char *, ...); extern void dnwarn(const dt_node_t *, dt_errtag_t, const char *, ...); extern void xyerror(dt_errtag_t, const char *, ...); extern void xywarn(dt_errtag_t, const char *, ...); extern void xyvwarn(dt_errtag_t, const char *, va_list); extern void yyerror(const char *, ...); extern void yywarn(const char *, ...); extern void yyvwarn(const char *, va_list); extern void yylabel(const char *); extern void yybegin(yystate_t); extern void yyinit(struct dt_pcb *); extern int yyparse(void); extern int yyinput(void); #ifdef __cplusplus } #endif #endif /* _DT_PARSER_H */ Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pid.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pid.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pid.c (revision 268578) @@ -1,829 +1,1001 @@ /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #include #include #include #include #include #include #if defined(sun) #include #endif #include #include +#include #include #include #include #include #if !defined(sun) #include #endif +#include typedef struct dt_pid_probe { dtrace_hdl_t *dpp_dtp; dt_pcb_t *dpp_pcb; dt_proc_t *dpp_dpr; struct ps_prochandle *dpp_pr; const char *dpp_mod; char *dpp_func; const char *dpp_name; const char *dpp_obj; uintptr_t dpp_pc; size_t dpp_size; Lmid_t dpp_lmid; uint_t dpp_nmatches; uint64_t dpp_stret[4]; GElf_Sym dpp_last; uint_t dpp_last_taken; } dt_pid_probe_t; /* * Compose the lmid and object name into the canonical representation. We * omit the lmid for the default link map for convenience. */ static void dt_pid_objname(char *buf, size_t len, Lmid_t lmid, const char *obj) { #if defined(sun) if (lmid == LM_ID_BASE) (void) strncpy(buf, obj, len); else (void) snprintf(buf, len, "LM%lx`%s", lmid, obj); #else (void) strncpy(buf, obj, len); #endif } static int dt_pid_error(dtrace_hdl_t *dtp, dt_pcb_t *pcb, dt_proc_t *dpr, fasttrap_probe_spec_t *ftp, dt_errtag_t tag, const char *fmt, ...) { va_list ap; int len; if (ftp != NULL) dt_free(dtp, ftp); va_start(ap, fmt); if (pcb == NULL) { assert(dpr != NULL); len = vsnprintf(dpr->dpr_errmsg, sizeof (dpr->dpr_errmsg), fmt, ap); assert(len >= 2); if (dpr->dpr_errmsg[len - 2] == '\n') dpr->dpr_errmsg[len - 2] = '\0'; } else { dt_set_errmsg(dtp, dt_errtag(tag), pcb->pcb_region, pcb->pcb_filetag, pcb->pcb_fileptr ? yylineno : 0, fmt, ap); } va_end(ap); return (1); } static int dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func) { dtrace_hdl_t *dtp = pp->dpp_dtp; dt_pcb_t *pcb = pp->dpp_pcb; dt_proc_t *dpr = pp->dpp_dpr; fasttrap_probe_spec_t *ftp; uint64_t off; char *end; uint_t nmatches = 0; ulong_t sz; int glob, err; int isdash = strcmp("-", func) == 0; pid_t pid; #if defined(sun) pid = Pstatus(pp->dpp_pr)->pr_pid; #else pid = proc_getpid(pp->dpp_pr); #endif dt_dprintf("creating probe pid%d:%s:%s:%s\n", (int)pid, pp->dpp_obj, func, pp->dpp_name); sz = sizeof (fasttrap_probe_spec_t) + (isdash ? 4 : (symp->st_size - 1) * sizeof (ftp->ftps_offs[0])); if ((ftp = dt_alloc(dtp, sz)) == NULL) { dt_dprintf("proc_per_sym: dt_alloc(%lu) failed\n", sz); return (1); /* errno is set for us */ } ftp->ftps_pid = pid; (void) strncpy(ftp->ftps_func, func, sizeof (ftp->ftps_func)); dt_pid_objname(ftp->ftps_mod, sizeof (ftp->ftps_mod), pp->dpp_lmid, pp->dpp_obj); if (!isdash && gmatch("return", pp->dpp_name)) { if (dt_pid_create_return_probe(pp->dpp_pr, dtp, ftp, symp, pp->dpp_stret) < 0) { return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_CREATEFAIL, "failed to create return probe " "for '%s': %s", func, dtrace_errmsg(dtp, dtrace_errno(dtp)))); } nmatches++; } if (!isdash && gmatch("entry", pp->dpp_name)) { if (dt_pid_create_entry_probe(pp->dpp_pr, dtp, ftp, symp) < 0) { return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_CREATEFAIL, "failed to create entry probe " "for '%s': %s", func, dtrace_errmsg(dtp, dtrace_errno(dtp)))); } nmatches++; } glob = strisglob(pp->dpp_name); if (!glob && nmatches == 0) { off = strtoull(pp->dpp_name, &end, 16); if (*end != '\0') { return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_NAME, "'%s' is an invalid probe name", pp->dpp_name)); } if (off >= symp->st_size) { return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_OFF, "offset 0x%llx outside of function '%s'", (u_longlong_t)off, func)); } err = dt_pid_create_offset_probe(pp->dpp_pr, pp->dpp_dtp, ftp, symp, off); if (err == DT_PROC_ERR) { return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_CREATEFAIL, "failed to create probe at " "'%s+0x%llx': %s", func, (u_longlong_t)off, dtrace_errmsg(dtp, dtrace_errno(dtp)))); } if (err == DT_PROC_ALIGN) { return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_ALIGN, "offset 0x%llx is not aligned on an instruction", (u_longlong_t)off)); } nmatches++; } else if (glob && !isdash) { if (dt_pid_create_glob_offset_probes(pp->dpp_pr, pp->dpp_dtp, ftp, symp, pp->dpp_name) < 0) { return (dt_pid_error(dtp, pcb, dpr, ftp, D_PROC_CREATEFAIL, "failed to create offset probes in '%s': %s", func, dtrace_errmsg(dtp, dtrace_errno(dtp)))); } nmatches++; } pp->dpp_nmatches += nmatches; dt_free(dtp, ftp); return (0); } static int dt_pid_sym_filt(void *arg, const GElf_Sym *symp, const char *func) { dt_pid_probe_t *pp = arg; if (symp->st_shndx == SHN_UNDEF) return (0); if (symp->st_size == 0) { dt_dprintf("st_size of %s is zero\n", func); return (0); } if (pp->dpp_last_taken == 0 || symp->st_value != pp->dpp_last.st_value || symp->st_size != pp->dpp_last.st_size) { /* * Due to 4524008, _init and _fini may have a bloated st_size. * While this bug has been fixed for a while, old binaries * may exist that still exhibit this problem. As a result, we * don't match _init and _fini though we allow users to * specify them explicitly. */ if (strcmp(func, "_init") == 0 || strcmp(func, "_fini") == 0) return (0); if ((pp->dpp_last_taken = gmatch(func, pp->dpp_func)) != 0) { pp->dpp_last = *symp; return (dt_pid_per_sym(pp, symp, func)); } } return (0); } static int dt_pid_per_mod(void *arg, const prmap_t *pmp, const char *obj) { dt_pid_probe_t *pp = arg; dtrace_hdl_t *dtp = pp->dpp_dtp; dt_pcb_t *pcb = pp->dpp_pcb; dt_proc_t *dpr = pp->dpp_dpr; GElf_Sym sym; if (obj == NULL) return (0); #if defined(sun) (void) Plmid(pp->dpp_pr, pmp->pr_vaddr, &pp->dpp_lmid); #endif if ((pp->dpp_obj = strrchr(obj, '/')) == NULL) pp->dpp_obj = obj; else pp->dpp_obj++; #if defined(sun) if (Pxlookup_by_name(pp->dpp_pr, pp->dpp_lmid, obj, ".stret1", &sym, NULL) == 0) pp->dpp_stret[0] = sym.st_value; else pp->dpp_stret[0] = 0; if (Pxlookup_by_name(pp->dpp_pr, pp->dpp_lmid, obj, ".stret2", &sym, NULL) == 0) pp->dpp_stret[1] = sym.st_value; else pp->dpp_stret[1] = 0; if (Pxlookup_by_name(pp->dpp_pr, pp->dpp_lmid, obj, ".stret4", &sym, NULL) == 0) pp->dpp_stret[2] = sym.st_value; else pp->dpp_stret[2] = 0; if (Pxlookup_by_name(pp->dpp_pr, pp->dpp_lmid, obj, ".stret8", &sym, NULL) == 0) pp->dpp_stret[3] = sym.st_value; else pp->dpp_stret[3] = 0; #else pp->dpp_stret[0] = 0; pp->dpp_stret[1] = 0; pp->dpp_stret[2] = 0; pp->dpp_stret[3] = 0; #endif dt_dprintf("%s stret %llx %llx %llx %llx\n", obj, (u_longlong_t)pp->dpp_stret[0], (u_longlong_t)pp->dpp_stret[1], (u_longlong_t)pp->dpp_stret[2], (u_longlong_t)pp->dpp_stret[3]); /* * If pp->dpp_func contains any globbing meta-characters, we need * to iterate over the symbol table and compare each function name * against the pattern. */ if (!strisglob(pp->dpp_func)) { /* * If we fail to lookup the symbol, try interpreting the * function as the special "-" function that indicates that the * probe name should be interpreted as a absolute virtual * address. If that fails and we were matching a specific * function in a specific module, report the error, otherwise * just fail silently in the hopes that some other object will * contain the desired symbol. */ if (Pxlookup_by_name(pp->dpp_pr, pp->dpp_lmid, obj, pp->dpp_func, &sym, NULL) != 0) { if (strcmp("-", pp->dpp_func) == 0) { sym.st_name = 0; sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_FUNC); sym.st_other = 0; sym.st_value = 0; #if defined(sun) sym.st_size = Pstatus(pp->dpp_pr)->pr_dmodel == PR_MODEL_ILP32 ? -1U : -1ULL; #else sym.st_size = ~((Elf64_Xword) 0); #endif } else if (!strisglob(pp->dpp_mod)) { return (dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_FUNC, "failed to lookup '%s' in module '%s'", pp->dpp_func, pp->dpp_mod)); } else { return (0); } } /* * Only match defined functions of non-zero size. */ if (GELF_ST_TYPE(sym.st_info) != STT_FUNC || sym.st_shndx == SHN_UNDEF || sym.st_size == 0) return (0); /* * We don't instrument PLTs -- they're dynamically rewritten, * and, so, inherently dicey to instrument. */ #ifdef DOODAD if (Ppltdest(pp->dpp_pr, sym.st_value) != NULL) return (0); #endif (void) Plookup_by_addr(pp->dpp_pr, sym.st_value, pp->dpp_func, DTRACE_FUNCNAMELEN, &sym); return (dt_pid_per_sym(pp, &sym, pp->dpp_func)); } else { uint_t nmatches = pp->dpp_nmatches; if (Psymbol_iter_by_addr(pp->dpp_pr, obj, PR_SYMTAB, BIND_ANY | TYPE_FUNC, dt_pid_sym_filt, pp) == 1) return (1); if (nmatches == pp->dpp_nmatches) { /* * If we didn't match anything in the PR_SYMTAB, try * the PR_DYNSYM. */ if (Psymbol_iter_by_addr(pp->dpp_pr, obj, PR_DYNSYM, BIND_ANY | TYPE_FUNC, dt_pid_sym_filt, pp) == 1) return (1); } } return (0); } static int dt_pid_mod_filt(void *arg, const prmap_t *pmp, const char *obj) { char name[DTRACE_MODNAMELEN]; dt_pid_probe_t *pp = arg; if (gmatch(obj, pp->dpp_mod)) return (dt_pid_per_mod(pp, pmp, obj)); #if defined(sun) (void) Plmid(pp->dpp_pr, pmp->pr_vaddr, &pp->dpp_lmid); #else pp->dpp_lmid = 0; #endif if ((pp->dpp_obj = strrchr(obj, '/')) == NULL) pp->dpp_obj = obj; else pp->dpp_obj++; if (gmatch(pp->dpp_obj, pp->dpp_mod)) return (dt_pid_per_mod(pp, pmp, obj)); #if defined(sun) (void) Plmid(pp->dpp_pr, pmp->pr_vaddr, &pp->dpp_lmid); #endif dt_pid_objname(name, sizeof (name), pp->dpp_lmid, pp->dpp_obj); if (gmatch(name, pp->dpp_mod)) return (dt_pid_per_mod(pp, pmp, obj)); return (0); } static const prmap_t * dt_pid_fix_mod(dtrace_probedesc_t *pdp, struct ps_prochandle *P) { char m[MAXPATHLEN]; #if defined(sun) Lmid_t lmid = PR_LMID_EVERY; #else Lmid_t lmid = 0; #endif const char *obj; const prmap_t *pmp; #if defined(sun) /* * Pick apart the link map from the library name. */ if (strchr(pdp->dtpd_mod, '`') != NULL) { char *end; if (strncmp(pdp->dtpd_mod, "LM", 2) != 0 || !isdigit(pdp->dtpd_mod[2])) return (NULL); lmid = strtoul(&pdp->dtpd_mod[2], &end, 16); obj = end + 1; if (*end != '`' || strchr(obj, '`') != NULL) return (NULL); } else { obj = pdp->dtpd_mod; } #else obj = pdp->dtpd_mod; #endif if ((pmp = Plmid_to_map(P, lmid, obj)) == NULL) return (NULL); #if defined(sun) (void) Pobjname(P, pmp->pr_vaddr, m, sizeof (m)); if ((obj = strrchr(m, '/')) == NULL) obj = &m[0]; else obj++; (void) Plmid(P, pmp->pr_vaddr, &lmid); #endif dt_pid_objname(pdp->dtpd_mod, sizeof (pdp->dtpd_mod), lmid, obj); return (pmp); } static int dt_pid_create_pid_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, dt_pcb_t *pcb, dt_proc_t *dpr) { dt_pid_probe_t pp; int ret = 0; pp.dpp_dtp = dtp; pp.dpp_dpr = dpr; pp.dpp_pr = dpr->dpr_proc; pp.dpp_pcb = pcb; #ifdef DOODAD /* * We can only trace dynamically-linked executables (since we've * hidden some magic in ld.so.1 as well as libc.so.1). */ if (Pname_to_map(pp.dpp_pr, PR_OBJ_LDSO) == NULL) { return (dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_DYN, "process %s is not a dynamically-linked executable", &pdp->dtpd_provider[3])); } #endif pp.dpp_mod = pdp->dtpd_mod[0] != '\0' ? pdp->dtpd_mod : "*"; pp.dpp_func = pdp->dtpd_func[0] != '\0' ? pdp->dtpd_func : "*"; pp.dpp_name = pdp->dtpd_name[0] != '\0' ? pdp->dtpd_name : "*"; pp.dpp_last_taken = 0; if (strcmp(pp.dpp_func, "-") == 0) { const prmap_t *aout, *pmp; if (pdp->dtpd_mod[0] == '\0') { pp.dpp_mod = pdp->dtpd_mod; (void) strcpy(pdp->dtpd_mod, "a.out"); } else if (strisglob(pp.dpp_mod) || (aout = Pname_to_map(pp.dpp_pr, "a.out")) == NULL || (pmp = Pname_to_map(pp.dpp_pr, pp.dpp_mod)) == NULL || aout->pr_vaddr != pmp->pr_vaddr) { return (dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_LIB, "only the a.out module is valid with the " "'-' function")); } if (strisglob(pp.dpp_name)) { return (dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_NAME, "only individual addresses may be specified " "with the '-' function")); } } /* * If pp.dpp_mod contains any globbing meta-characters, we need * to iterate over each module and compare its name against the * pattern. An empty module name is treated as '*'. */ if (strisglob(pp.dpp_mod)) { ret = Pobject_iter(pp.dpp_pr, dt_pid_mod_filt, &pp); } else { const prmap_t *pmp; char *obj; /* * If we can't find a matching module, don't sweat it -- either * we'll fail the enabling because the probes don't exist or * we'll wait for that module to come along. */ if ((pmp = dt_pid_fix_mod(pdp, pp.dpp_pr)) != NULL) { if ((obj = strchr(pdp->dtpd_mod, '`')) == NULL) obj = pdp->dtpd_mod; else obj++; ret = dt_pid_per_mod(&pp, pmp, obj); } } return (ret); } static int dt_pid_usdt_mapping(void *data, const prmap_t *pmp, const char *oname) { struct ps_prochandle *P = data; GElf_Sym sym; #if defined(sun) prsyminfo_t sip; #endif dof_helper_t dh; GElf_Half e_type; const char *mname; const char *syms[] = { "___SUNW_dof", "__SUNW_dof" }; int i, fd = -1; /* * The symbol ___SUNW_dof is for lazy-loaded DOF sections, and * __SUNW_dof is for actively-loaded DOF sections. We try to force * in both types of DOF section since the process may not yet have * run the code to instantiate these providers. */ for (i = 0; i < 2; i++) { if (Pxlookup_by_name(P, PR_LMID_EVERY, oname, syms[i], &sym, &sip) != 0) { continue; } if ((mname = strrchr(oname, '/')) == NULL) mname = oname; else mname++; dt_dprintf("lookup of %s succeeded for %s\n", syms[i], mname); if (Pread(P, &e_type, sizeof (e_type), pmp->pr_vaddr + offsetof(Elf64_Ehdr, e_type)) != sizeof (e_type)) { dt_dprintf("read of ELF header failed"); continue; } dh.dofhp_dof = sym.st_value; dh.dofhp_addr = (e_type == ET_EXEC) ? 0 : pmp->pr_vaddr; dt_pid_objname(dh.dofhp_mod, sizeof (dh.dofhp_mod), #if defined(sun) sip.prs_lmid, mname); #else 0, mname); #endif #if defined(sun) if (fd == -1 && (fd = pr_open(P, "/dev/dtrace/helper", O_RDWR, 0)) < 0) { dt_dprintf("pr_open of helper device failed: %s\n", strerror(errno)); return (-1); /* errno is set for us */ } if (pr_ioctl(P, fd, DTRACEHIOC_ADDDOF, &dh, sizeof (dh)) < 0) dt_dprintf("DOF was rejected for %s\n", dh.dofhp_mod); #endif } #if defined(sun) if (fd != -1) (void) pr_close(P, fd); #endif return (0); } static int dt_pid_create_usdt_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, dt_pcb_t *pcb, dt_proc_t *dpr) { struct ps_prochandle *P = dpr->dpr_proc; int ret = 0; assert(DT_MUTEX_HELD(&dpr->dpr_lock)); #if defined(sun) (void) Pupdate_maps(P); if (Pobject_iter(P, dt_pid_usdt_mapping, P) != 0) { ret = -1; (void) dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_USDT, "failed to instantiate probes for pid %d: %s", #if defined(sun) (int)Pstatus(P)->pr_pid, strerror(errno)); #else (int)proc_getpid(P), strerror(errno)); #endif } #else ret = 0; #endif /* * Put the module name in its canonical form. */ (void) dt_pid_fix_mod(pdp, P); return (ret); } static pid_t dt_pid_get_pid(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, dt_pcb_t *pcb, dt_proc_t *dpr) { pid_t pid; char *c, *last = NULL, *end; for (c = &pdp->dtpd_provider[0]; *c != '\0'; c++) { if (!isdigit(*c)) last = c; } if (last == NULL || (*(++last) == '\0')) { (void) dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_BADPROV, "'%s' is not a valid provider", pdp->dtpd_provider); return (-1); } errno = 0; pid = strtol(last, &end, 10); if (errno != 0 || end == last || end[0] != '\0' || pid <= 0) { (void) dt_pid_error(dtp, pcb, dpr, NULL, D_PROC_BADPID, "'%s' does not contain a valid pid", pdp->dtpd_provider); return (-1); } return (pid); } int dt_pid_create_probes(dtrace_probedesc_t *pdp, dtrace_hdl_t *dtp, dt_pcb_t *pcb) { char provname[DTRACE_PROVNAMELEN]; struct ps_prochandle *P; dt_proc_t *dpr; pid_t pid; int err = 0; assert(pcb != NULL); if ((pid = dt_pid_get_pid(pdp, dtp, pcb, NULL)) == -1) return (-1); if (dtp->dt_ftfd == -1) { if (dtp->dt_fterr == ENOENT) { (void) dt_pid_error(dtp, pcb, NULL, NULL, D_PROC_NODEV, "pid provider is not installed on this system"); } else { (void) dt_pid_error(dtp, pcb, NULL, NULL, D_PROC_NODEV, "pid provider is not available: %s", strerror(dtp->dt_fterr)); } return (-1); } (void) snprintf(provname, sizeof (provname), "pid%d", (int)pid); if (gmatch(provname, pdp->dtpd_provider) != 0) { if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) == NULL) { (void) dt_pid_error(dtp, pcb, NULL, NULL, D_PROC_GRAB, "failed to grab process %d", (int)pid); return (-1); } dpr = dt_proc_lookup(dtp, P, 0); assert(dpr != NULL); (void) pthread_mutex_lock(&dpr->dpr_lock); if ((err = dt_pid_create_pid_probes(pdp, dtp, pcb, dpr)) == 0) { /* * Alert other retained enablings which may match * against the newly created probes. */ (void) dt_ioctl(dtp, DTRACEIOC_ENABLE, NULL); } (void) pthread_mutex_unlock(&dpr->dpr_lock); dt_proc_release(dtp, P); } /* * If it's not strictly a pid provider, we might match a USDT provider. */ if (strcmp(provname, pdp->dtpd_provider) != 0) { if ((P = dt_proc_grab(dtp, pid, 0, 1)) == NULL) { (void) dt_pid_error(dtp, pcb, NULL, NULL, D_PROC_GRAB, "failed to grab process %d", (int)pid); return (-1); } dpr = dt_proc_lookup(dtp, P, 0); assert(dpr != NULL); (void) pthread_mutex_lock(&dpr->dpr_lock); if (!dpr->dpr_usdt) { err = dt_pid_create_usdt_probes(pdp, dtp, pcb, dpr); dpr->dpr_usdt = B_TRUE; } (void) pthread_mutex_unlock(&dpr->dpr_lock); dt_proc_release(dtp, P); } return (err ? -1 : 0); } int dt_pid_create_probes_module(dtrace_hdl_t *dtp, dt_proc_t *dpr) { dtrace_enable_io_t args; dtrace_prog_t *pgp; dt_stmt_t *stp; dtrace_probedesc_t *pdp, pd; pid_t pid; int ret = 0, found = B_FALSE; char provname[DTRACE_PROVNAMELEN]; (void) snprintf(provname, sizeof (provname), "pid%d", (int)dpr->dpr_pid); for (pgp = dt_list_next(&dtp->dt_programs); pgp != NULL; pgp = dt_list_next(pgp)) { for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = dt_list_next(stp)) { pdp = &stp->ds_desc->dtsd_ecbdesc->dted_probe; pid = dt_pid_get_pid(pdp, dtp, NULL, dpr); if (pid != dpr->dpr_pid) continue; found = B_TRUE; pd = *pdp; if (gmatch(provname, pdp->dtpd_provider) != 0 && dt_pid_create_pid_probes(&pd, dtp, NULL, dpr) != 0) ret = 1; /* * If it's not strictly a pid provider, we might match * a USDT provider. */ if (strcmp(provname, pdp->dtpd_provider) != 0 && dt_pid_create_usdt_probes(&pd, dtp, NULL, dpr) != 0) ret = 1; } } if (found) { /* * Give DTrace a shot to the ribs to get it to check * out the newly created probes. */ args.dof = NULL; args.n_matched = 0; (void) dt_ioctl(dtp, DTRACEIOC_ENABLE, &args); } return (ret); +} + +/* + * libdtrace has a backroom deal with us to ask us for type information on + * behalf of pid provider probes when fasttrap doesn't return any type + * information. Instead we'll look up the module and see if there is type + * information available. However, if there is no type information available due + * to a lack of CTF data, then we want to make sure that DTrace still carries on + * in face of that. As such we don't have a meaningful exit code about failure. + * We emit information about why we failed to the dtrace debug log so someone + * can figure it out by asking nicely for DTRACE_DEBUG. + */ +void +dt_pid_get_types(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, + dtrace_argdesc_t *adp, int *nargs) +{ + dt_module_t *dmp; + ctf_file_t *fp; + ctf_funcinfo_t f; + ctf_id_t argv[32]; + GElf_Sym sym; +#if defined(sun) + prsyminfo_t si; +#else + void *si; +#endif + struct ps_prochandle *p; + int i, args; + char buf[DTRACE_ARGTYPELEN]; + const char *mptr; + char *eptr; + int ret = 0; + int argc = sizeof (argv) / sizeof (ctf_id_t); + Lmid_t lmid; + + /* Set up a potential outcome */ + args = *nargs; + *nargs = 0; + + /* + * If we don't have an entry or return probe then we can just stop right + * now as we don't have arguments for offset probes. + */ + if (strcmp(pdp->dtpd_name, "entry") != 0 && + strcmp(pdp->dtpd_name, "return") != 0) + return; + + dmp = dt_module_create(dtp, pdp->dtpd_provider); + if (dmp == NULL) { + dt_dprintf("failed to find module for %s\n", + pdp->dtpd_provider); + return; + } + if (dt_module_load(dtp, dmp) != 0) { + dt_dprintf("failed to load module for %s\n", + pdp->dtpd_provider); + return; + } + + /* + * We may be working with a module that doesn't have ctf. If that's the + * case then we just return now and move on with life. + */ + fp = dt_module_getctflib(dtp, dmp, pdp->dtpd_mod); + if (fp == NULL) { + dt_dprintf("no ctf container for %s\n", + pdp->dtpd_mod); + return; + } + p = dt_proc_grab(dtp, dmp->dm_pid, 0, PGRAB_RDONLY | PGRAB_FORCE); + if (p == NULL) { + dt_dprintf("failed to grab pid\n"); + return; + } + dt_proc_lock(dtp, p); + + /* + * Check to see if the D module has a link map ID and separate that out + * for properly interrogating libproc. + */ + if ((mptr = strchr(pdp->dtpd_mod, '`')) != NULL) { + if (strlen(pdp->dtpd_mod) < 3) { + dt_dprintf("found weird modname with linkmap, " + "aborting: %s\n", pdp->dtpd_mod); + goto out; + } + if (pdp->dtpd_mod[0] != 'L' || pdp->dtpd_mod[1] != 'M') { + dt_dprintf("missing leading 'LM', " + "aborting: %s\n", pdp->dtpd_mod); + goto out; + } + errno = 0; + lmid = strtol(pdp->dtpd_mod + 2, &eptr, 16); + if (errno == ERANGE || eptr != mptr) { + dt_dprintf("failed to parse out lmid, aborting: %s\n", + pdp->dtpd_mod); + goto out; + } + mptr++; + } else { + mptr = pdp->dtpd_mod; + lmid = 0; + } + + if (Pxlookup_by_name(p, lmid, mptr, pdp->dtpd_func, + &sym, &si) != 0) { + dt_dprintf("failed to find function %s in %s`%s\n", + pdp->dtpd_func, pdp->dtpd_provider, pdp->dtpd_mod); + goto out; + } +#if defined(sun) + if (ctf_func_info(fp, si.prs_id, &f) == CTF_ERR) { + dt_dprintf("failed to get ctf information for %s in %s`%s\n", + pdp->dtpd_func, pdp->dtpd_provider, pdp->dtpd_mod); + goto out; + } +#endif + + (void) snprintf(buf, sizeof (buf), "%s`%s", pdp->dtpd_provider, + pdp->dtpd_mod); + + if (strcmp(pdp->dtpd_name, "return") == 0) { + if (args < 2) + goto out; + + bzero(adp, sizeof (dtrace_argdesc_t)); + adp->dtargd_ndx = 0; + adp->dtargd_id = pdp->dtpd_id; + adp->dtargd_mapping = adp->dtargd_ndx; + /* + * We explicitly leave out the library here, we only care that + * it is some int. We are assuming that there is no ctf + * container in here that is lying about what an int is. + */ + (void) snprintf(adp->dtargd_native, DTRACE_ARGTYPELEN, + "user %s`%s", pdp->dtpd_provider, "int"); + adp++; + bzero(adp, sizeof (dtrace_argdesc_t)); + adp->dtargd_ndx = 1; + adp->dtargd_id = pdp->dtpd_id; + adp->dtargd_mapping = adp->dtargd_ndx; + ret = snprintf(adp->dtargd_native, DTRACE_ARGTYPELEN, + "userland "); + (void) ctf_type_qname(fp, f.ctc_return, adp->dtargd_native + + ret, DTRACE_ARGTYPELEN - ret, buf); + *nargs = 2; +#if defined(sun) + } else { + if (ctf_func_args(fp, si.prs_id, argc, argv) == CTF_ERR) + goto out; + + *nargs = MIN(args, f.ctc_argc); + for (i = 0; i < *nargs; i++, adp++) { + bzero(adp, sizeof (dtrace_argdesc_t)); + adp->dtargd_ndx = i; + adp->dtargd_id = pdp->dtpd_id; + adp->dtargd_mapping = adp->dtargd_ndx; + ret = snprintf(adp->dtargd_native, DTRACE_ARGTYPELEN, + "userland "); + (void) ctf_type_qname(fp, argv[i], adp->dtargd_native + + ret, DTRACE_ARGTYPELEN - ret, buf); + } +#endif + } +out: + dt_proc_unlock(dtp, p); + dt_proc_release(dtp, p); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pid.h =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pid.h (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_pid.h (revision 268578) @@ -1,64 +1,68 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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) 2013, Joyent, Inc. All rights reserved. + */ #ifndef _DT_PID_H #define _DT_PID_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include #ifdef __cplusplus extern "C" { #endif #define DT_PROC_ERR (-1) #define DT_PROC_ALIGN (-2) extern int dt_pid_create_probes(dtrace_probedesc_t *, dtrace_hdl_t *, dt_pcb_t *pcb); extern int dt_pid_create_probes_module(dtrace_hdl_t *, dt_proc_t *); extern int dt_pid_create_entry_probe(struct ps_prochandle *, dtrace_hdl_t *, fasttrap_probe_spec_t *, const GElf_Sym *); extern int dt_pid_create_return_probe(struct ps_prochandle *, dtrace_hdl_t *, fasttrap_probe_spec_t *, const GElf_Sym *, uint64_t *); extern int dt_pid_create_offset_probe(struct ps_prochandle *, dtrace_hdl_t *, fasttrap_probe_spec_t *, const GElf_Sym *, ulong_t); extern int dt_pid_create_glob_offset_probes(struct ps_prochandle *, dtrace_hdl_t *, fasttrap_probe_spec_t *, const GElf_Sym *, const char *); + +extern void dt_pid_get_types(dtrace_hdl_t *, const dtrace_probedesc_t *, + dtrace_argdesc_t *, int *); #ifdef __cplusplus } #endif #endif /* _DT_PID_H */ Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c (revision 268578) @@ -1,691 +1,706 @@ /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2011 by Delphix. All rights reserved. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* * DTrace print() action * * This file contains the post-processing logic for the print() action. The * print action behaves identically to trace() in that it generates a * DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type * string stored in the DOF string table (similar to printf formats). We * take the result of the trace action and post-process it in the fashion of * MDB's ::print dcmd. * * This implementation differs from MDB's in the following ways: * * - We do not expose any options or flags. The behavior of print() is * equivalent to "::print -tn". * * - MDB will display "holes" in structures (unused padding between * members). * * - When printing arrays of structures, MDB will leave a trailing ',' * after the last element. * * - MDB will print time_t types as date and time. * * - MDB will detect when an enum is actually the OR of several flags, * and print it out with the constituent flags separated. * * - For large arrays, MDB will print the first few members and then * print a "..." continuation line. * * - MDB will break and wrap arrays at 80 columns. * * - MDB prints out floats and doubles by hand, as it must run in kmdb * context. We're able to leverage the printf() format strings, * but the result is a slightly different format. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* determines whether the given integer CTF encoding is a character */ #define CTF_IS_CHAR(e) \ (((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \ (CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY) /* determines whether the given CTF kind is a struct or union */ #define CTF_IS_STRUCTLIKE(k) \ ((k) == CTF_K_STRUCT || (k) == CTF_K_UNION) /* * Print structure passed down recursively through printing algorithm. */ typedef struct dt_printarg { dtrace_hdl_t *pa_dtp; /* libdtrace handle */ caddr_t pa_addr; /* base address of trace data */ ctf_file_t *pa_ctfp; /* CTF container */ int pa_depth; /* member depth */ int pa_nest; /* nested array depth */ FILE *pa_file; /* output file */ } dt_printarg_t; static int dt_print_member(const char *, ctf_id_t, ulong_t, int, void *); /* * Safe version of ctf_type_name() that will fall back to just "" if it * can't resolve the type. */ static void dt_print_type_name(ctf_file_t *ctfp, ctf_id_t id, char *buf, size_t buflen) { if (ctf_type_name(ctfp, id, buf, buflen) == NULL) (void) snprintf(buf, buflen, "<%ld>", id); } /* * Print any necessary trailing braces for structures or unions. We don't get * invoked when a struct or union ends, so we infer the need to print braces * based on the depth the last time we printed something and the new depth. */ static void dt_print_trailing_braces(dt_printarg_t *pap, int depth) { int d; for (d = pap->pa_depth; d > depth; d--) { (void) fprintf(pap->pa_file, "%*s}%s", (d + pap->pa_nest - 1) * 4, "", d == depth + 1 ? "" : "\n"); } } /* * Print the appropriate amount of indentation given the current depth and * array nesting. */ static void dt_print_indent(dt_printarg_t *pap) { (void) fprintf(pap->pa_file, "%*s", (pap->pa_depth + pap->pa_nest) * 4, ""); } /* * Print a bitfield. It's worth noting that the D compiler support for * bitfields is currently broken; printing "D`user_desc_t" (pulled in by the * various D provider files) will produce incorrect results compared to * "genunix`user_desc_t". */ static void print_bitfield(dt_printarg_t *pap, ulong_t off, ctf_encoding_t *ep) { FILE *fp = pap->pa_file; caddr_t addr = pap->pa_addr + off / NBBY; uint64_t mask = (1ULL << ep->cte_bits) - 1; uint64_t value = 0; size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY; uint8_t *buf = (uint8_t *)&value; uint8_t shift; /* * On big-endian machines, we need to adjust the buf pointer to refer * to the lowest 'size' bytes in 'value', and we need to shift based on * the offset from the end of the data, not the offset of the start. */ #ifdef _BIG_ENDIAN buf += sizeof (value) - size; off += ep->cte_bits; #endif bcopy(addr, buf, size); shift = off % NBBY; /* * Offsets are counted from opposite ends on little- and * big-endian machines. */ #ifdef _BIG_ENDIAN shift = NBBY - shift; #endif /* * If the bits we want do not begin on a byte boundary, shift the data * right so that the value is in the lowest 'cte_bits' of 'value'. */ if (off % NBBY != 0) value >>= shift; value &= mask; (void) fprintf(fp, "%#llx", (u_longlong_t)value); } /* * Dump the contents of memory as a fixed-size integer in hex. */ static void dt_print_hex(FILE *fp, caddr_t addr, size_t size) { switch (size) { case sizeof (uint8_t): (void) fprintf(fp, "%#x", *(uint8_t *)addr); break; case sizeof (uint16_t): /* LINTED - alignment */ (void) fprintf(fp, "%#x", *(uint16_t *)addr); break; case sizeof (uint32_t): /* LINTED - alignment */ (void) fprintf(fp, "%#x", *(uint32_t *)addr); break; case sizeof (uint64_t): (void) fprintf(fp, "%#llx", /* LINTED - alignment */ (unsigned long long)*(uint64_t *)addr); break; default: (void) fprintf(fp, "", (uint_t)size); } } /* * Print an integer type. Before dumping the contents via dt_print_hex(), we * first check the encoding to see if it's part of a bitfield or a character. */ static void dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; ctf_encoding_t e; size_t size; caddr_t addr = pap->pa_addr + off / NBBY; if (ctf_type_encoding(ctfp, base, &e) == CTF_ERR) { (void) fprintf(fp, ""); return; } /* * This comes from MDB - it's not clear under what circumstances this * would be found. */ if (e.cte_format & CTF_INT_VARARGS) { (void) fprintf(fp, "..."); return; } /* * We print this as a bitfield if the bit encoding indicates it's not * an even power of two byte size, or is larger than 8 bytes. */ size = e.cte_bits / NBBY; if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) { print_bitfield(pap, off, &e); return; } /* * If this is a character, print it out as such. */ if (CTF_IS_CHAR(e)) { char c = *(char *)addr; if (isprint(c)) (void) fprintf(fp, "'%c'", c); else if (c == 0) (void) fprintf(fp, "'\\0'"); else (void) fprintf(fp, "'\\%03o'", c); return; } dt_print_hex(fp, addr, size); } /* * Print a floating point (float, double, long double) value. */ /* ARGSUSED */ static void dt_print_float(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; ctf_encoding_t e; caddr_t addr = pap->pa_addr + off / NBBY; if (ctf_type_encoding(ctfp, base, &e) == 0) { if (e.cte_format == CTF_FP_SINGLE && e.cte_bits == sizeof (float) * NBBY) { /* LINTED - alignment */ (void) fprintf(fp, "%+.7e", *((float *)addr)); } else if (e.cte_format == CTF_FP_DOUBLE && e.cte_bits == sizeof (double) * NBBY) { /* LINTED - alignment */ (void) fprintf(fp, "%+.7e", *((double *)addr)); } else if (e.cte_format == CTF_FP_LDOUBLE && e.cte_bits == sizeof (long double) * NBBY) { /* LINTED - alignment */ (void) fprintf(fp, "%+.16LE", *((long double *)addr)); } else { (void) fprintf(fp, ""); } } } /* * A pointer is generally printed as a fixed-size integer. If we have a * function pointer, we try to look up its name. */ static void dt_print_ptr(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; caddr_t addr = pap->pa_addr + off / NBBY; size_t size = ctf_type_size(ctfp, base); ctf_id_t bid = ctf_type_reference(ctfp, base); uint64_t pc; dtrace_syminfo_t dts; GElf_Sym sym; if (bid == CTF_ERR || ctf_type_kind(ctfp, bid) != CTF_K_FUNCTION) { dt_print_hex(fp, addr, size); } else { /* LINTED - alignment */ pc = *((uint64_t *)addr); if (dtrace_lookup_by_addr(pap->pa_dtp, pc, &sym, &dts) != 0) { dt_print_hex(fp, addr, size); } else { (void) fprintf(fp, "%s`%s", dts.dts_object, dts.dts_name); } } } /* * Print out an array. This is somewhat complex, as we must manually visit * each member, and recursively invoke ctf_type_visit() for each member. If * the members are non-structs, then we print them out directly: * * [ 0x14, 0x2e, 0 ] * * If they are structs, then we print out the necessary leading and trailing * braces, to end up with: * * [ * type { * ... * }, * type { * ... * } * ] * * We also use a heuristic to detect whether the array looks like a character * array. If the encoding indicates it's a character, and we have all * printable characters followed by a null byte, then we display it as a * string: * * [ "string" ] */ static void dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; caddr_t addr = pap->pa_addr + off / NBBY; ctf_arinfo_t car; ssize_t eltsize; ctf_encoding_t e; int i; boolean_t isstring; int kind; ctf_id_t rtype; if (ctf_array_info(ctfp, base, &car) == CTF_ERR) { (void) fprintf(fp, "0x%p", (void *)addr); return; } if ((eltsize = ctf_type_size(ctfp, car.ctr_contents)) < 0 || (rtype = ctf_type_resolve(ctfp, car.ctr_contents)) == CTF_ERR || (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR) { (void) fprintf(fp, "", car.ctr_contents); return; } /* see if this looks like a string */ isstring = B_FALSE; if (kind == CTF_K_INTEGER && ctf_type_encoding(ctfp, rtype, &e) != CTF_ERR && CTF_IS_CHAR(e)) { char c; for (i = 0; i < car.ctr_nelems; i++) { c = *((char *)addr + eltsize * i); if (!isprint(c) || c == '\0') break; } if (i != car.ctr_nelems && c == '\0') isstring = B_TRUE; } /* * As a slight aesthetic optimization, if we are a top-level type, then * don't bother printing out the brackets. This lets print("foo") look * like: * * string "foo" * * As D will internally represent this as a char[256] array. */ if (!isstring || pap->pa_depth != 0) (void) fprintf(fp, "[ "); if (isstring) (void) fprintf(fp, "\""); for (i = 0; i < car.ctr_nelems; i++) { if (isstring) { char c = *((char *)addr + eltsize * i); if (c == '\0') break; (void) fprintf(fp, "%c", c); } else { /* * Recursively invoke ctf_type_visit() on each member. * We setup a new printarg struct with 'pa_nest' set to * indicate that we are within a nested array. */ dt_printarg_t pa = *pap; pa.pa_nest += pap->pa_depth + 1; pa.pa_depth = 0; pa.pa_addr = addr + eltsize * i; (void) ctf_type_visit(ctfp, car.ctr_contents, dt_print_member, &pa); dt_print_trailing_braces(&pa, 0); if (i != car.ctr_nelems - 1) (void) fprintf(fp, ", "); else if (CTF_IS_STRUCTLIKE(kind)) (void) fprintf(fp, "\n"); } } if (isstring) (void) fprintf(fp, "\""); if (!isstring || pap->pa_depth != 0) { if (CTF_IS_STRUCTLIKE(kind)) dt_print_indent(pap); else (void) fprintf(fp, " "); (void) fprintf(fp, "]"); } } /* * This isued by both structs and unions to print the leading brace. */ /* ARGSUSED */ static void dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap) { (void) fprintf(pap->pa_file, "{"); } /* * For enums, we try to print the enum name, and fall back to the value if it * can't be determined. We do not do any fancy flag processing like mdb. */ /* ARGSUSED */ static void dt_print_enum(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; const char *ename; ssize_t size; caddr_t addr = pap->pa_addr + off / NBBY; int value = 0; /* * The C standard says that an enum will be at most the sizeof (int). * But if all the values are less than that, the compiler can use a * smaller size. Thanks standards. */ size = ctf_type_size(ctfp, base); switch (size) { case sizeof (uint8_t): value = *(uint8_t *)addr; break; case sizeof (uint16_t): value = *(uint16_t *)addr; break; case sizeof (int32_t): value = *(int32_t *)addr; break; default: (void) fprintf(fp, "", (uint_t)size); return; } if ((ename = ctf_enum_name(ctfp, base, value)) != NULL) (void) fprintf(fp, "%s", ename); else (void) fprintf(fp, "%d", value); } /* * Forward declaration. There's not much to do here without the complete * type information, so just print out this fact and drive on. */ /* ARGSUSED */ static void dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { (void) fprintf(pap->pa_file, ""); } typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *); static dt_printarg_f *const dt_printfuncs[] = { dt_print_int, /* CTF_K_INTEGER */ dt_print_float, /* CTF_K_FLOAT */ dt_print_ptr, /* CTF_K_POINTER */ dt_print_array, /* CTF_K_ARRAY */ dt_print_ptr, /* CTF_K_FUNCTION */ dt_print_structlike, /* CTF_K_STRUCT */ dt_print_structlike, /* CTF_K_UNION */ dt_print_enum, /* CTF_K_ENUM */ dt_print_tag /* CTF_K_FORWARD */ }; /* * Print one member of a structure. This callback is invoked from * ctf_type_visit() recursively. */ static int dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth, void *data) { char type[DT_TYPE_NAMELEN]; int kind; dt_printarg_t *pap = data; FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; boolean_t arraymember; boolean_t brief; ctf_encoding_t e; ctf_id_t rtype; dt_print_trailing_braces(pap, depth); /* * dt_print_trailing_braces() doesn't include the trailing newline; add * it here if necessary. */ if (depth < pap->pa_depth) (void) fprintf(fp, "\n"); pap->pa_depth = depth; if ((rtype = ctf_type_resolve(ctfp, id)) == CTF_ERR || (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR || kind < CTF_K_INTEGER || kind > CTF_K_FORWARD) { dt_print_indent(pap); (void) fprintf(fp, "%s = ", name, id); return (0); } dt_print_type_name(ctfp, id, type, sizeof (type)); arraymember = (pap->pa_nest != 0 && depth == 0); brief = (arraymember && !CTF_IS_STRUCTLIKE(kind)); if (!brief) { /* * If this is a direct array member and a struct (otherwise * brief would be true), then print a trailing newline, as the * array printing code doesn't include it because it might be a * simple type. */ if (arraymember) (void) fprintf(fp, "\n"); dt_print_indent(pap); /* always print the type */ (void) fprintf(fp, "%s", type); if (name[0] != '\0') { /* * For aesthetics, we don't include a space between the * type name and member name if the type is a pointer. * This will give us "void *foo =" instead of "void * * foo =". Unions also have the odd behavior that the * type name is returned as "union ", with a trailing * space, so we also avoid printing a space if the type * name already ends with a space. */ if (type[strlen(type) - 1] != '*' && type[strlen(type) -1] != ' ') { (void) fprintf(fp, " "); } (void) fprintf(fp, "%s", name); /* * If this looks like a bitfield, or is an integer not * aligned on a byte boundary, print the number of * bits after the name. */ if (kind == CTF_K_INTEGER && ctf_type_encoding(ctfp, id, &e) == 0) { ulong_t bits = e.cte_bits; ulong_t size = bits / NBBY; if (bits % NBBY != 0 || off % NBBY != 0 || size > 8 || size != ctf_type_size(ctfp, id)) { (void) fprintf(fp, " :%lu", bits); } } (void) fprintf(fp, " ="); } (void) fprintf(fp, " "); } dt_printfuncs[kind - 1](rtype, off, pap); /* direct simple array members are not separated by newlines */ if (!brief) (void) fprintf(fp, "\n"); return (0); } /* * Main print function invoked by dt_consume_cpu(). */ int dtrace_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename, caddr_t addr, size_t len) { const char *s; char *object; dt_printarg_t pa; ctf_id_t id; dt_module_t *dmp; + ctf_file_t *ctfp; + int libid; /* * Split the fully-qualified type ID (module`id). This should * always be the format, but if for some reason we don't find the * expected value, return 0 to fall back to the generic trace() - * behavior. + * behavior. In the case of userland CTF modules this will actually be + * of the format (module`lib`id). This is due to the fact that those + * modules have multiple CTF containers which `lib` identifies. */ for (s = typename; *s != '\0' && *s != '`'; s++) ; if (*s != '`') return (0); object = alloca(s - typename + 1); bcopy(typename, object, s - typename); object[s - typename] = '\0'; + dmp = dt_module_lookup_by_name(dtp, object); + if (dmp == NULL) + return (0); + + if (dmp->dm_pid != 0) { + libid = atoi(s + 1); + s = strchr(s + 1, '`'); + if (s == NULL || libid > dmp->dm_nctflibs) + return (0); + ctfp = dmp->dm_libctfp[libid]; + } else { + ctfp = dt_module_getctf(dtp, dmp); + } + id = atoi(s + 1); /* * Try to get the CTF kind for this id. If something has gone horribly * wrong and we can't resolve the ID, bail out and let trace() do the * work. */ - dmp = dt_module_lookup_by_name(dtp, object); - if (dmp == NULL || ctf_type_kind(dt_module_getctf(dtp, dmp), - id) == CTF_ERR) { + if (ctfp == NULL || ctf_type_kind(ctfp, id) == CTF_ERR) return (0); - } /* setup the print structure and kick off the main print routine */ pa.pa_dtp = dtp; pa.pa_addr = addr; - pa.pa_ctfp = dt_module_getctf(dtp, dmp); + pa.pa_ctfp = ctfp; pa.pa_nest = 0; pa.pa_depth = 0; pa.pa_file = fp; (void) ctf_type_visit(pa.pa_ctfp, id, dt_print_member, &pa); dt_print_trailing_braces(&pa, 0); return (len); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c (revision 268578) @@ -1,2065 +1,2065 @@ /* * 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, Joyent, Inc. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #if defined(sun) #include #else #define ABS(a) ((a) < 0 ? -(a) : (a)) #endif #include #include #include #if defined(sun) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include /*ARGSUSED*/ static int pfcheck_addr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { return (dt_node_is_pointer(dnp) || dt_node_is_integer(dnp)); } /*ARGSUSED*/ static int pfcheck_kaddr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { return (dt_node_is_pointer(dnp) || dt_node_is_integer(dnp) || dt_node_is_symaddr(dnp)); } /*ARGSUSED*/ static int pfcheck_uaddr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { dtrace_hdl_t *dtp = pfv->pfv_dtp; dt_ident_t *idp = dt_idhash_lookup(dtp->dt_macros, "target"); if (dt_node_is_usymaddr(dnp)) return (1); if (idp == NULL || idp->di_id == 0) return (0); return (dt_node_is_pointer(dnp) || dt_node_is_integer(dnp)); } /*ARGSUSED*/ static int pfcheck_stack(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { return (dt_node_is_stack(dnp)); } /*ARGSUSED*/ static int pfcheck_time(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { return (dt_node_is_integer(dnp) && dt_node_type_size(dnp) == sizeof (uint64_t)); } /*ARGSUSED*/ static int pfcheck_str(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { ctf_file_t *ctfp; ctf_encoding_t e; ctf_arinfo_t r; ctf_id_t base; uint_t kind; if (dt_node_is_string(dnp)) return (1); ctfp = dnp->dn_ctfp; base = ctf_type_resolve(ctfp, dnp->dn_type); kind = ctf_type_kind(ctfp, base); return (kind == CTF_K_ARRAY && ctf_array_info(ctfp, base, &r) == 0 && (base = ctf_type_resolve(ctfp, r.ctr_contents)) != CTF_ERR && ctf_type_encoding(ctfp, base, &e) == 0 && IS_CHAR(e)); } /*ARGSUSED*/ static int pfcheck_wstr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { ctf_file_t *ctfp = dnp->dn_ctfp; ctf_id_t base = ctf_type_resolve(ctfp, dnp->dn_type); uint_t kind = ctf_type_kind(ctfp, base); ctf_encoding_t e; ctf_arinfo_t r; return (kind == CTF_K_ARRAY && ctf_array_info(ctfp, base, &r) == 0 && (base = ctf_type_resolve(ctfp, r.ctr_contents)) != CTF_ERR && ctf_type_kind(ctfp, base) == CTF_K_INTEGER && ctf_type_encoding(ctfp, base, &e) == 0 && e.cte_bits == 32); } /*ARGSUSED*/ static int pfcheck_csi(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { return (dt_node_is_integer(dnp) && dt_node_type_size(dnp) <= sizeof (int)); } /*ARGSUSED*/ static int pfcheck_fp(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { return (dt_node_is_float(dnp)); } /*ARGSUSED*/ static int pfcheck_xint(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { return (dt_node_is_integer(dnp)); } /*ARGSUSED*/ static int pfcheck_dint(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { if (dnp->dn_flags & DT_NF_SIGNED) pfd->pfd_fmt[strlen(pfd->pfd_fmt) - 1] = 'i'; else pfd->pfd_fmt[strlen(pfd->pfd_fmt) - 1] = 'u'; return (dt_node_is_integer(dnp)); } /*ARGSUSED*/ static int pfcheck_xshort(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { ctf_file_t *ctfp = dnp->dn_ctfp; ctf_id_t type = ctf_type_resolve(ctfp, dnp->dn_type); char n[DT_TYPE_NAMELEN]; return (ctf_type_name(ctfp, type, n, sizeof (n)) != NULL && ( strcmp(n, "short") == 0 || strcmp(n, "signed short") == 0 || strcmp(n, "unsigned short") == 0)); } /*ARGSUSED*/ static int pfcheck_xlong(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { ctf_file_t *ctfp = dnp->dn_ctfp; ctf_id_t type = ctf_type_resolve(ctfp, dnp->dn_type); char n[DT_TYPE_NAMELEN]; return (ctf_type_name(ctfp, type, n, sizeof (n)) != NULL && ( strcmp(n, "long") == 0 || strcmp(n, "signed long") == 0 || strcmp(n, "unsigned long") == 0)); } /*ARGSUSED*/ static int pfcheck_xlonglong(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { ctf_file_t *ctfp = dnp->dn_ctfp; ctf_id_t type = dnp->dn_type; char n[DT_TYPE_NAMELEN]; if (ctf_type_name(ctfp, ctf_type_resolve(ctfp, type), n, sizeof (n)) != NULL && (strcmp(n, "long long") == 0 || strcmp(n, "signed long long") == 0 || strcmp(n, "unsigned long long") == 0)) return (1); /* * If the type used for %llx or %llX is not an [unsigned] long long, we * also permit it to be a [u]int64_t or any typedef thereof. We know * that these typedefs are guaranteed to work with %ll[xX] in either * compilation environment even though they alias to "long" in LP64. */ while (ctf_type_kind(ctfp, type) == CTF_K_TYPEDEF) { if (ctf_type_name(ctfp, type, n, sizeof (n)) != NULL && (strcmp(n, "int64_t") == 0 || strcmp(n, "uint64_t") == 0)) return (1); type = ctf_type_reference(ctfp, type); } return (0); } /*ARGSUSED*/ static int pfcheck_type(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) { return (ctf_type_compat(dnp->dn_ctfp, ctf_type_resolve(dnp->dn_ctfp, dnp->dn_type), pfd->pfd_conv->pfc_dctfp, pfd->pfd_conv->pfc_dtype)); } /*ARGSUSED*/ static int pfprint_sint(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t unormal) { int64_t normal = (int64_t)unormal; int32_t n = (int32_t)normal; switch (size) { case sizeof (int8_t): return (dt_printf(dtp, fp, format, (int32_t)*((int8_t *)addr) / n)); case sizeof (int16_t): return (dt_printf(dtp, fp, format, (int32_t)*((int16_t *)addr) / n)); case sizeof (int32_t): return (dt_printf(dtp, fp, format, *((int32_t *)addr) / n)); case sizeof (int64_t): return (dt_printf(dtp, fp, format, *((int64_t *)addr) / normal)); default: return (dt_set_errno(dtp, EDT_DMISMATCH)); } } /*ARGSUSED*/ static int pfprint_uint(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { uint32_t n = (uint32_t)normal; switch (size) { case sizeof (uint8_t): return (dt_printf(dtp, fp, format, (uint32_t)*((uint8_t *)addr) / n)); case sizeof (uint16_t): return (dt_printf(dtp, fp, format, (uint32_t)*((uint16_t *)addr) / n)); case sizeof (uint32_t): return (dt_printf(dtp, fp, format, *((uint32_t *)addr) / n)); case sizeof (uint64_t): return (dt_printf(dtp, fp, format, *((uint64_t *)addr) / normal)); default: return (dt_set_errno(dtp, EDT_DMISMATCH)); } } static int pfprint_dint(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { if (pfd->pfd_flags & DT_PFCONV_SIGNED) return (pfprint_sint(dtp, fp, format, pfd, addr, size, normal)); else return (pfprint_uint(dtp, fp, format, pfd, addr, size, normal)); } /*ARGSUSED*/ static int pfprint_fp(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { double n = (double)normal; long double ldn = (long double)normal; switch (size) { case sizeof (float): return (dt_printf(dtp, fp, format, (double)*((float *)addr) / n)); case sizeof (double): return (dt_printf(dtp, fp, format, *((double *)addr) / n)); #if !defined(__arm__) && !defined(__powerpc__) && !defined(__mips__) case sizeof (long double): return (dt_printf(dtp, fp, format, *((long double *)addr) / ldn)); #endif default: return (dt_set_errno(dtp, EDT_DMISMATCH)); } } /*ARGSUSED*/ static int pfprint_addr(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { char *s; int n, len = 256; uint64_t val; switch (size) { case sizeof (uint32_t): val = *((uint32_t *)addr); break; case sizeof (uint64_t): val = *((uint64_t *)addr); break; default: return (dt_set_errno(dtp, EDT_DMISMATCH)); } do { n = len; s = alloca(n); } while ((len = dtrace_addr2str(dtp, val, s, n)) > n); return (dt_printf(dtp, fp, format, s)); } /*ARGSUSED*/ static int pfprint_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { return (dt_print_mod(dtp, fp, format, (caddr_t)addr)); } /*ARGSUSED*/ static int pfprint_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { return (dt_print_umod(dtp, fp, format, (caddr_t)addr)); } /*ARGSUSED*/ static int pfprint_uaddr(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { char *s; int n, len = 256; uint64_t val, pid = 0; dt_ident_t *idp = dt_idhash_lookup(dtp->dt_macros, "target"); switch (size) { case sizeof (uint32_t): val = (u_longlong_t)*((uint32_t *)addr); break; case sizeof (uint64_t): val = (u_longlong_t)*((uint64_t *)addr); break; case sizeof (uint64_t) * 2: pid = ((uint64_t *)(uintptr_t)addr)[0]; val = ((uint64_t *)(uintptr_t)addr)[1]; break; default: return (dt_set_errno(dtp, EDT_DMISMATCH)); } if (pid == 0 && dtp->dt_vector == NULL && idp != NULL) pid = idp->di_id; do { n = len; s = alloca(n); } while ((len = dtrace_uaddr2str(dtp, pid, val, s, n)) > n); return (dt_printf(dtp, fp, format, s)); } /*ARGSUSED*/ static int pfprint_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *vaddr, size_t size, uint64_t normal) { int width; dtrace_optval_t saved = dtp->dt_options[DTRACEOPT_STACKINDENT]; const dtrace_recdesc_t *rec = pfd->pfd_rec; caddr_t addr = (caddr_t)vaddr; int err = 0; /* * We have stashed the value of the STACKINDENT option, and we will * now override it for the purposes of formatting the stack. If the * field has been specified as left-aligned (i.e. (%-#), we set the * indentation to be the width. This is a slightly odd semantic, but * it's useful functionality -- and it's slightly odd to begin with to * be using a single format specifier to be formatting multiple lines * of text... */ if (pfd->pfd_dynwidth < 0) { assert(pfd->pfd_flags & DT_PFCONV_DYNWIDTH); width = -pfd->pfd_dynwidth; } else if (pfd->pfd_flags & DT_PFCONV_LEFT) { width = pfd->pfd_dynwidth ? pfd->pfd_dynwidth : pfd->pfd_width; } else { width = 0; } dtp->dt_options[DTRACEOPT_STACKINDENT] = width; switch (rec->dtrd_action) { case DTRACEACT_USTACK: case DTRACEACT_JSTACK: err = dt_print_ustack(dtp, fp, format, addr, rec->dtrd_arg); break; case DTRACEACT_STACK: err = dt_print_stack(dtp, fp, format, addr, rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg); break; default: assert(0); } dtp->dt_options[DTRACEOPT_STACKINDENT] = saved; return (err); } /*ARGSUSED*/ static int pfprint_time(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { char src[32], buf[32], *dst = buf; hrtime_t time = *((uint64_t *)addr); time_t sec = (time_t)(time / NANOSEC); int i; /* * ctime(3C) returns a string of the form "Dec 3 17:20:00 1973\n\0". * Below, we turn this into the canonical adb/mdb /[yY] format, * "1973 Dec 3 17:20:00". */ #if defined(sun) (void) ctime_r(&sec, src, sizeof (src)); #else (void) ctime_r(&sec, src); #endif /* * Place the 4-digit year at the head of the string... */ for (i = 20; i < 24; i++) *dst++ = src[i]; /* * ...and follow it with the remainder (month, day, hh:mm:ss). */ for (i = 3; i < 19; i++) *dst++ = src[i]; *dst = '\0'; return (dt_printf(dtp, fp, format, buf)); } /* * This prints the time in RFC 822 standard form. This is useful for emitting * notions of time that are consumed by standard tools (e.g., as part of an * RSS feed). */ /*ARGSUSED*/ static int pfprint_time822(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { hrtime_t time = *((uint64_t *)addr); time_t sec = (time_t)(time / NANOSEC); struct tm tm; char buf[64]; (void) localtime_r(&sec, &tm); (void) strftime(buf, sizeof (buf), "%a, %d %b %G %T %Z", &tm); return (dt_printf(dtp, fp, format, buf)); } /*ARGSUSED*/ static int pfprint_port(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { uint16_t port = htons(*((uint16_t *)addr)); char buf[256]; struct servent *sv, res; #if defined(sun) if ((sv = getservbyport_r(port, NULL, &res, buf, sizeof (buf))) != NULL) #else if (getservbyport_r(port, NULL, &res, buf, sizeof (buf), &sv) > 0) #endif return (dt_printf(dtp, fp, format, sv->s_name)); (void) snprintf(buf, sizeof (buf), "%d", *((uint16_t *)addr)); return (dt_printf(dtp, fp, format, buf)); } /*ARGSUSED*/ static int pfprint_inetaddr(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { char *s = alloca(size + 1); struct hostent *host, res; char inetaddr[NS_IN6ADDRSZ]; char buf[1024]; int e; bcopy(addr, s, size); s[size] = '\0'; if (strchr(s, ':') == NULL && inet_pton(AF_INET, s, inetaddr) != -1) { #if defined(sun) if ((host = gethostbyaddr_r(inetaddr, NS_INADDRSZ, AF_INET, &res, buf, sizeof (buf), &e)) != NULL) #else if (gethostbyaddr_r(inetaddr, NS_INADDRSZ, AF_INET, &res, buf, sizeof (buf), &host, &e) > 0) #endif return (dt_printf(dtp, fp, format, host->h_name)); } else if (inet_pton(AF_INET6, s, inetaddr) != -1) { if ((host = getipnodebyaddr(inetaddr, NS_IN6ADDRSZ, AF_INET6, &e)) != NULL) return (dt_printf(dtp, fp, format, host->h_name)); } return (dt_printf(dtp, fp, format, s)); } /*ARGSUSED*/ static int pfprint_cstr(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { char *s = alloca(size + 1); bcopy(addr, s, size); s[size] = '\0'; return (dt_printf(dtp, fp, format, s)); } /*ARGSUSED*/ static int pfprint_wstr(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { wchar_t *ws = alloca(size + sizeof (wchar_t)); bcopy(addr, ws, size); ws[size / sizeof (wchar_t)] = L'\0'; return (dt_printf(dtp, fp, format, ws)); } /*ARGSUSED*/ static int pfprint_estr(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { char *s; int n; if ((s = strchr2esc(addr, size)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); n = dt_printf(dtp, fp, format, s); free(s); return (n); } static int pfprint_echr(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { char c; switch (size) { case sizeof (int8_t): c = *(int8_t *)addr; break; case sizeof (int16_t): c = *(int16_t *)addr; break; case sizeof (int32_t): c = *(int32_t *)addr; break; default: return (dt_set_errno(dtp, EDT_DMISMATCH)); } return (pfprint_estr(dtp, fp, format, pfd, &c, 1, normal)); } /*ARGSUSED*/ static int pfprint_pct(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { return (dt_printf(dtp, fp, "%%")); } static const char pfproto_xint[] = "char, short, int, long, or long long"; static const char pfproto_csi[] = "char, short, or int"; static const char pfproto_fp[] = "float, double, or long double"; static const char pfproto_addr[] = "pointer or integer"; static const char pfproto_uaddr[] = "pointer or integer (with -p/-c) or _usymaddr (without -p/-c)"; static const char pfproto_cstr[] = "char [] or string (or use stringof)"; static const char pfproto_wstr[] = "wchar_t []"; /* * Printf format conversion dictionary. This table should match the set of * conversions offered by printf(3C), as well as some additional extensions. * The second parameter is an ASCII string which is either an actual type * name we should look up (if pfcheck_type is specified), or just a descriptive * string of the types expected for use in error messages. */ static const dt_pfconv_t _dtrace_conversions[] = { { "a", "s", pfproto_addr, pfcheck_kaddr, pfprint_addr }, { "A", "s", pfproto_uaddr, pfcheck_uaddr, pfprint_uaddr }, { "c", "c", pfproto_csi, pfcheck_csi, pfprint_sint }, { "C", "s", pfproto_csi, pfcheck_csi, pfprint_echr }, { "d", "d", pfproto_xint, pfcheck_dint, pfprint_dint }, { "e", "e", pfproto_fp, pfcheck_fp, pfprint_fp }, { "E", "E", pfproto_fp, pfcheck_fp, pfprint_fp }, { "f", "f", pfproto_fp, pfcheck_fp, pfprint_fp }, { "g", "g", pfproto_fp, pfcheck_fp, pfprint_fp }, { "G", "G", pfproto_fp, pfcheck_fp, pfprint_fp }, { "hd", "d", "short", pfcheck_type, pfprint_sint }, { "hi", "i", "short", pfcheck_type, pfprint_sint }, { "ho", "o", "unsigned short", pfcheck_type, pfprint_uint }, { "hu", "u", "unsigned short", pfcheck_type, pfprint_uint }, { "hx", "x", "short", pfcheck_xshort, pfprint_uint }, { "hX", "X", "short", pfcheck_xshort, pfprint_uint }, { "i", "i", pfproto_xint, pfcheck_xint, pfprint_sint }, { "I", "s", pfproto_cstr, pfcheck_str, pfprint_inetaddr }, { "k", "s", "stack", pfcheck_stack, pfprint_stack }, { "lc", "lc", "int", pfcheck_type, pfprint_sint }, /* a.k.a. wint_t */ { "ld", "d", "long", pfcheck_type, pfprint_sint }, { "li", "i", "long", pfcheck_type, pfprint_sint }, { "lo", "o", "unsigned long", pfcheck_type, pfprint_uint }, { "lu", "u", "unsigned long", pfcheck_type, pfprint_uint }, { "ls", "ls", pfproto_wstr, pfcheck_wstr, pfprint_wstr }, { "lx", "x", "long", pfcheck_xlong, pfprint_uint }, { "lX", "X", "long", pfcheck_xlong, pfprint_uint }, { "lld", "d", "long long", pfcheck_type, pfprint_sint }, { "lli", "i", "long long", pfcheck_type, pfprint_sint }, { "llo", "o", "unsigned long long", pfcheck_type, pfprint_uint }, { "llu", "u", "unsigned long long", pfcheck_type, pfprint_uint }, { "llx", "x", "long long", pfcheck_xlonglong, pfprint_uint }, { "llX", "X", "long long", pfcheck_xlonglong, pfprint_uint }, { "Le", "e", "long double", pfcheck_type, pfprint_fp }, { "LE", "E", "long double", pfcheck_type, pfprint_fp }, { "Lf", "f", "long double", pfcheck_type, pfprint_fp }, { "Lg", "g", "long double", pfcheck_type, pfprint_fp }, { "LG", "G", "long double", pfcheck_type, pfprint_fp }, { "o", "o", pfproto_xint, pfcheck_xint, pfprint_uint }, { "p", "x", pfproto_addr, pfcheck_addr, pfprint_uint }, { "P", "s", "uint16_t", pfcheck_type, pfprint_port }, { "s", "s", "char [] or string (or use stringof)", pfcheck_str, pfprint_cstr }, { "S", "s", pfproto_cstr, pfcheck_str, pfprint_estr }, { "T", "s", "int64_t", pfcheck_time, pfprint_time822 }, { "u", "u", pfproto_xint, pfcheck_xint, pfprint_uint }, { "wc", "wc", "int", pfcheck_type, pfprint_sint }, /* a.k.a. wchar_t */ { "ws", "ws", pfproto_wstr, pfcheck_wstr, pfprint_wstr }, { "x", "x", pfproto_xint, pfcheck_xint, pfprint_uint }, { "X", "X", pfproto_xint, pfcheck_xint, pfprint_uint }, { "Y", "s", "int64_t", pfcheck_time, pfprint_time }, { "%", "%", "void", pfcheck_type, pfprint_pct }, { NULL, NULL, NULL, NULL, NULL } }; int dt_pfdict_create(dtrace_hdl_t *dtp) { uint_t n = _dtrace_strbuckets; const dt_pfconv_t *pfd; dt_pfdict_t *pdi; if ((pdi = malloc(sizeof (dt_pfdict_t))) == NULL || (pdi->pdi_buckets = malloc(sizeof (dt_pfconv_t *) * n)) == NULL) { free(pdi); return (dt_set_errno(dtp, EDT_NOMEM)); } dtp->dt_pfdict = pdi; bzero(pdi->pdi_buckets, sizeof (dt_pfconv_t *) * n); pdi->pdi_nbuckets = n; for (pfd = _dtrace_conversions; pfd->pfc_name != NULL; pfd++) { dtrace_typeinfo_t dtt; dt_pfconv_t *pfc; uint_t h; if ((pfc = malloc(sizeof (dt_pfconv_t))) == NULL) { dt_pfdict_destroy(dtp); return (dt_set_errno(dtp, EDT_NOMEM)); } bcopy(pfd, pfc, sizeof (dt_pfconv_t)); h = dt_strtab_hash(pfc->pfc_name, NULL) % n; pfc->pfc_next = pdi->pdi_buckets[h]; pdi->pdi_buckets[h] = pfc; dtt.dtt_ctfp = NULL; dtt.dtt_type = CTF_ERR; /* * The "D" container or its parent must contain a definition of * any type referenced by a printf conversion. If none can be * found, we fail to initialize the printf dictionary. */ if (pfc->pfc_check == &pfcheck_type && dtrace_lookup_by_type( dtp, DTRACE_OBJ_DDEFS, pfc->pfc_tstr, &dtt) != 0) { dt_pfdict_destroy(dtp); return (dt_set_errno(dtp, EDT_NOCONV)); } pfc->pfc_dctfp = dtt.dtt_ctfp; pfc->pfc_dtype = dtt.dtt_type; /* * The "C" container may contain an alternate definition of an * explicit conversion type. If it does, use it; otherwise * just set pfc_ctype to pfc_dtype so it is always valid. */ if (pfc->pfc_check == &pfcheck_type && dtrace_lookup_by_type( dtp, DTRACE_OBJ_CDEFS, pfc->pfc_tstr, &dtt) == 0) { pfc->pfc_cctfp = dtt.dtt_ctfp; pfc->pfc_ctype = dtt.dtt_type; } else { pfc->pfc_cctfp = pfc->pfc_dctfp; pfc->pfc_ctype = pfc->pfc_dtype; } if (pfc->pfc_check == NULL || pfc->pfc_print == NULL || pfc->pfc_ofmt == NULL || pfc->pfc_tstr == NULL) { dt_pfdict_destroy(dtp); return (dt_set_errno(dtp, EDT_BADCONV)); } dt_dprintf("loaded printf conversion %%%s\n", pfc->pfc_name); } return (0); } void dt_pfdict_destroy(dtrace_hdl_t *dtp) { dt_pfdict_t *pdi = dtp->dt_pfdict; dt_pfconv_t *pfc, *nfc; uint_t i; if (pdi == NULL) return; for (i = 0; i < pdi->pdi_nbuckets; i++) { for (pfc = pdi->pdi_buckets[i]; pfc != NULL; pfc = nfc) { nfc = pfc->pfc_next; free(pfc); } } free(pdi->pdi_buckets); free(pdi); dtp->dt_pfdict = NULL; } static const dt_pfconv_t * dt_pfdict_lookup(dtrace_hdl_t *dtp, const char *name) { dt_pfdict_t *pdi = dtp->dt_pfdict; uint_t h = dt_strtab_hash(name, NULL) % pdi->pdi_nbuckets; const dt_pfconv_t *pfc; for (pfc = pdi->pdi_buckets[h]; pfc != NULL; pfc = pfc->pfc_next) { if (strcmp(pfc->pfc_name, name) == 0) break; } return (pfc); } static dt_pfargv_t * dt_printf_error(dtrace_hdl_t *dtp, int err) { if (yypcb != NULL) longjmp(yypcb->pcb_jmpbuf, err); (void) dt_set_errno(dtp, err); return (NULL); } dt_pfargv_t * dt_printf_create(dtrace_hdl_t *dtp, const char *s) { dt_pfargd_t *pfd, *nfd = NULL; dt_pfargv_t *pfv; const char *p, *q; char *format; if ((pfv = malloc(sizeof (dt_pfargv_t))) == NULL || (format = strdup(s)) == NULL) { free(pfv); return (dt_printf_error(dtp, EDT_NOMEM)); } pfv->pfv_format = format; pfv->pfv_argv = NULL; pfv->pfv_argc = 0; pfv->pfv_flags = 0; pfv->pfv_dtp = dtp; for (q = format; (p = strchr(q, '%')) != NULL; q = *p ? p + 1 : p) { uint_t namelen = 0; int digits = 0; int dot = 0; char name[8]; char c; int n; if ((pfd = malloc(sizeof (dt_pfargd_t))) == NULL) { dt_printf_destroy(pfv); return (dt_printf_error(dtp, EDT_NOMEM)); } if (pfv->pfv_argv != NULL) nfd->pfd_next = pfd; else pfv->pfv_argv = pfd; bzero(pfd, sizeof (dt_pfargd_t)); pfv->pfv_argc++; nfd = pfd; if (p > q) { pfd->pfd_preflen = (size_t)(p - q); pfd->pfd_prefix = q; } fmt_switch: switch (c = *++p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (dot == 0 && digits == 0 && c == '0') { pfd->pfd_flags |= DT_PFCONV_ZPAD; pfd->pfd_flags &= ~DT_PFCONV_LEFT; goto fmt_switch; } for (n = 0; isdigit(c); c = *++p) n = n * 10 + c - '0'; if (dot) pfd->pfd_prec = n; else pfd->pfd_width = n; p--; digits++; goto fmt_switch; case '#': pfd->pfd_flags |= DT_PFCONV_ALT; goto fmt_switch; case '*': n = dot ? DT_PFCONV_DYNPREC : DT_PFCONV_DYNWIDTH; if (pfd->pfd_flags & n) { yywarn("format conversion #%u has more than " "one '*' specified for the output %s\n", pfv->pfv_argc, n ? "precision" : "width"); dt_printf_destroy(pfv); return (dt_printf_error(dtp, EDT_COMPILER)); } pfd->pfd_flags |= n; goto fmt_switch; case '+': pfd->pfd_flags |= DT_PFCONV_SPOS; goto fmt_switch; case '-': pfd->pfd_flags |= DT_PFCONV_LEFT; pfd->pfd_flags &= ~DT_PFCONV_ZPAD; goto fmt_switch; case '.': if (dot++ != 0) { yywarn("format conversion #%u has more than " "one '.' specified\n", pfv->pfv_argc); dt_printf_destroy(pfv); return (dt_printf_error(dtp, EDT_COMPILER)); } digits = 0; goto fmt_switch; case '?': if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) pfd->pfd_width = 16; else pfd->pfd_width = 8; goto fmt_switch; case '@': pfd->pfd_flags |= DT_PFCONV_AGG; goto fmt_switch; case '\'': pfd->pfd_flags |= DT_PFCONV_GROUP; goto fmt_switch; case ' ': pfd->pfd_flags |= DT_PFCONV_SPACE; goto fmt_switch; case '$': yywarn("format conversion #%u uses unsupported " "positional format (%%n$)\n", pfv->pfv_argc); dt_printf_destroy(pfv); return (dt_printf_error(dtp, EDT_COMPILER)); case '%': if (p[-1] == '%') goto default_lbl; /* if %% then use "%" conv */ yywarn("format conversion #%u cannot be combined " "with other format flags: %%%%\n", pfv->pfv_argc); dt_printf_destroy(pfv); return (dt_printf_error(dtp, EDT_COMPILER)); case '\0': yywarn("format conversion #%u name expected before " "end of format string\n", pfv->pfv_argc); dt_printf_destroy(pfv); return (dt_printf_error(dtp, EDT_COMPILER)); case 'h': case 'l': case 'L': case 'w': if (namelen < sizeof (name) - 2) name[namelen++] = c; goto fmt_switch; default_lbl: default: name[namelen++] = c; name[namelen] = '\0'; } pfd->pfd_conv = dt_pfdict_lookup(dtp, name); if (pfd->pfd_conv == NULL) { yywarn("format conversion #%u is undefined: %%%s\n", pfv->pfv_argc, name); dt_printf_destroy(pfv); return (dt_printf_error(dtp, EDT_COMPILER)); } } if (*q != '\0' || *format == '\0') { if ((pfd = malloc(sizeof (dt_pfargd_t))) == NULL) { dt_printf_destroy(pfv); return (dt_printf_error(dtp, EDT_NOMEM)); } if (pfv->pfv_argv != NULL) nfd->pfd_next = pfd; else pfv->pfv_argv = pfd; bzero(pfd, sizeof (dt_pfargd_t)); pfv->pfv_argc++; pfd->pfd_prefix = q; pfd->pfd_preflen = strlen(q); } return (pfv); } void dt_printf_destroy(dt_pfargv_t *pfv) { dt_pfargd_t *pfd, *nfd; for (pfd = pfv->pfv_argv; pfd != NULL; pfd = nfd) { nfd = pfd->pfd_next; free(pfd); } free(pfv->pfv_format); free(pfv); } void dt_printf_validate(dt_pfargv_t *pfv, uint_t flags, dt_ident_t *idp, int foff, dtrace_actkind_t kind, dt_node_t *dnp) { dt_pfargd_t *pfd = pfv->pfv_argv; const char *func = idp->di_name; char n[DT_TYPE_NAMELEN]; dtrace_typeinfo_t dtt; const char *aggtype; dt_node_t aggnode; int i, j; if (pfv->pfv_format[0] == '\0') { xyerror(D_PRINTF_FMT_EMPTY, "%s( ) format string is empty\n", func); } pfv->pfv_flags = flags; /* * We fake up a parse node representing the type that can be used with * an aggregation result conversion, which -- for all but count() -- * is a signed quantity. */ if (kind != DTRACEAGG_COUNT) aggtype = "int64_t"; else aggtype = "uint64_t"; if (dt_type_lookup(aggtype, &dtt) != 0) xyerror(D_TYPE_ERR, "failed to lookup agg type %s\n", aggtype); bzero(&aggnode, sizeof (aggnode)); - dt_node_type_assign(&aggnode, dtt.dtt_ctfp, dtt.dtt_type); + dt_node_type_assign(&aggnode, dtt.dtt_ctfp, dtt.dtt_type, B_FALSE); for (i = 0, j = 0; i < pfv->pfv_argc; i++, pfd = pfd->pfd_next) { const dt_pfconv_t *pfc = pfd->pfd_conv; const char *dyns[2]; int dync = 0; char vname[64]; dt_node_t *vnp; if (pfc == NULL) continue; /* no checking if argd is just a prefix */ if (pfc->pfc_print == &pfprint_pct) { (void) strcat(pfd->pfd_fmt, pfc->pfc_ofmt); continue; } if (pfd->pfd_flags & DT_PFCONV_DYNPREC) dyns[dync++] = ".*"; if (pfd->pfd_flags & DT_PFCONV_DYNWIDTH) dyns[dync++] = "*"; for (; dync != 0; dync--) { if (dnp == NULL) { xyerror(D_PRINTF_DYN_PROTO, "%s( ) prototype mismatch: conversion " "#%d (%%%s) is missing a corresponding " "\"%s\" argument\n", func, i + 1, pfc->pfc_name, dyns[dync - 1]); } if (dt_node_is_integer(dnp) == 0) { xyerror(D_PRINTF_DYN_TYPE, "%s( ) argument #%d is incompatible " "with conversion #%d prototype:\n" "\tconversion: %% %s %s\n" "\t prototype: int\n\t argument: %s\n", func, j + foff + 1, i + 1, dyns[dync - 1], pfc->pfc_name, dt_node_type_name(dnp, n, sizeof (n))); } dnp = dnp->dn_list; j++; } /* * If this conversion is consuming the aggregation data, set * the value node pointer (vnp) to a fake node based on the * aggregating function result type. Otherwise assign vnp to * the next parse node in the argument list, if there is one. */ if (pfd->pfd_flags & DT_PFCONV_AGG) { if (!(flags & DT_PRINTF_AGGREGATION)) { xyerror(D_PRINTF_AGG_CONV, "%%@ conversion requires an aggregation" " and is not for use with %s( )\n", func); } (void) strlcpy(vname, "aggregating action", sizeof (vname)); vnp = &aggnode; } else if (dnp == NULL) { xyerror(D_PRINTF_ARG_PROTO, "%s( ) prototype mismatch: conversion #%d (%%" "%s) is missing a corresponding value argument\n", func, i + 1, pfc->pfc_name); } else { (void) snprintf(vname, sizeof (vname), "argument #%d", j + foff + 1); vnp = dnp; dnp = dnp->dn_list; j++; } /* * Fill in the proposed final format string by prepending any * size-related prefixes to the pfconv's format string. The * pfc_check() function below may optionally modify the format * as part of validating the type of the input argument. */ if (pfc->pfc_print == &pfprint_sint || pfc->pfc_print == &pfprint_uint || pfc->pfc_print == &pfprint_dint) { if (dt_node_type_size(vnp) == sizeof (uint64_t)) (void) strcpy(pfd->pfd_fmt, "ll"); } else if (pfc->pfc_print == &pfprint_fp) { if (dt_node_type_size(vnp) == sizeof (long double)) (void) strcpy(pfd->pfd_fmt, "L"); } (void) strcat(pfd->pfd_fmt, pfc->pfc_ofmt); /* * Validate the format conversion against the value node type. * If the conversion is good, create the descriptor format * string by concatenating together any required printf(3C) * size prefixes with the conversion's native format string. */ if (pfc->pfc_check(pfv, pfd, vnp) == 0) { xyerror(D_PRINTF_ARG_TYPE, "%s( ) %s is incompatible with " "conversion #%d prototype:\n\tconversion: %%%s\n" "\t prototype: %s\n\t argument: %s\n", func, vname, i + 1, pfc->pfc_name, pfc->pfc_tstr, dt_node_type_name(vnp, n, sizeof (n))); } } if ((flags & DT_PRINTF_EXACTLEN) && dnp != NULL) { xyerror(D_PRINTF_ARG_EXTRA, "%s( ) prototype mismatch: only %d arguments " "required by this format string\n", func, j); } } void dt_printa_validate(dt_node_t *lhs, dt_node_t *rhs) { dt_ident_t *lid, *rid; dt_node_t *lproto, *rproto; int largc, rargc, argn; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; assert(lhs->dn_kind == DT_NODE_AGG); assert(rhs->dn_kind == DT_NODE_AGG); lid = lhs->dn_ident; rid = rhs->dn_ident; lproto = ((dt_idsig_t *)lid->di_data)->dis_args; rproto = ((dt_idsig_t *)rid->di_data)->dis_args; /* * First, get an argument count on each side. These must match. */ for (largc = 0; lproto != NULL; lproto = lproto->dn_list) largc++; for (rargc = 0; rproto != NULL; rproto = rproto->dn_list) rargc++; if (largc != rargc) { xyerror(D_PRINTA_AGGKEY, "printa( ): @%s and @%s do not have " "matching key signatures: @%s has %d key%s, @%s has %d " "key%s", lid->di_name, rid->di_name, lid->di_name, largc, largc == 1 ? "" : "s", rid->di_name, rargc, rargc == 1 ? "" : "s"); } /* * Now iterate over the keys to verify that each type matches. */ lproto = ((dt_idsig_t *)lid->di_data)->dis_args; rproto = ((dt_idsig_t *)rid->di_data)->dis_args; for (argn = 1; lproto != NULL; argn++, lproto = lproto->dn_list, rproto = rproto->dn_list) { assert(rproto != NULL); if (dt_node_is_argcompat(lproto, rproto)) continue; xyerror(D_PRINTA_AGGPROTO, "printa( ): @%s[ ] key #%d is " "incompatible with @%s:\n%9s key #%d: %s\n" "%9s key #%d: %s\n", rid->di_name, argn, lid->di_name, lid->di_name, argn, dt_node_type_name(lproto, n1, sizeof (n1)), rid->di_name, argn, dt_node_type_name(rproto, n2, sizeof (n2))); } } static int dt_printf_getint(dtrace_hdl_t *dtp, const dtrace_recdesc_t *recp, uint_t nrecs, const void *buf, size_t len, int *ip) { uintptr_t addr; if (nrecs == 0) return (dt_set_errno(dtp, EDT_DMISMATCH)); addr = (uintptr_t)buf + recp->dtrd_offset; if (addr + sizeof (int) > (uintptr_t)buf + len) return (dt_set_errno(dtp, EDT_DOFFSET)); if (addr & (recp->dtrd_alignment - 1)) return (dt_set_errno(dtp, EDT_DALIGN)); switch (recp->dtrd_size) { case sizeof (int8_t): *ip = (int)*((int8_t *)addr); break; case sizeof (int16_t): *ip = (int)*((int16_t *)addr); break; case sizeof (int32_t): *ip = (int)*((int32_t *)addr); break; case sizeof (int64_t): *ip = (int)*((int64_t *)addr); break; default: return (dt_set_errno(dtp, EDT_DMISMATCH)); } return (0); } /*ARGSUSED*/ static int pfprint_average(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { const uint64_t *data = addr; if (size != sizeof (uint64_t) * 2) return (dt_set_errno(dtp, EDT_DMISMATCH)); return (dt_printf(dtp, fp, format, data[0] ? data[1] / normal / data[0] : 0)); } /*ARGSUSED*/ static int pfprint_stddev(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { const uint64_t *data = addr; if (size != sizeof (uint64_t) * 4) return (dt_set_errno(dtp, EDT_DMISMATCH)); return (dt_printf(dtp, fp, format, dt_stddev((uint64_t *)data, normal))); } /*ARGSUSED*/ static int pfprint_quantize(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { return (dt_print_quantize(dtp, fp, addr, size, normal)); } /*ARGSUSED*/ static int pfprint_lquantize(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { return (dt_print_lquantize(dtp, fp, addr, size, normal)); } /*ARGSUSED*/ static int pfprint_llquantize(dtrace_hdl_t *dtp, FILE *fp, const char *format, const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal) { return (dt_print_llquantize(dtp, fp, addr, size, normal)); } static int dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, const dtrace_recdesc_t *recs, uint_t nrecs, const void *buf, size_t len, const dtrace_aggdata_t **aggsdata, int naggvars) { dt_pfargd_t *pfd = pfv->pfv_argv; const dtrace_recdesc_t *recp = recs; const dtrace_aggdata_t *aggdata; dtrace_aggdesc_t *agg; caddr_t lim = (caddr_t)buf + len, limit; char format[64] = "%"; int i, aggrec, curagg = -1; uint64_t normal; /* * If we are formatting an aggregation, set 'aggrec' to the index of * the final record description (the aggregation result) so we can use * this record index with any conversion where DT_PFCONV_AGG is set. * (The actual aggregation used will vary as we increment through the * aggregation variables that we have been passed.) Finally, we * decrement nrecs to prevent this record from being used with any * other conversion. */ if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) { assert(aggsdata != NULL); assert(naggvars > 0); if (nrecs == 0) return (dt_set_errno(dtp, EDT_DMISMATCH)); curagg = naggvars > 1 ? 1 : 0; aggdata = aggsdata[0]; aggrec = aggdata->dtada_desc->dtagd_nrecs - 1; nrecs--; } for (i = 0; i < pfv->pfv_argc; i++, pfd = pfd->pfd_next) { const dt_pfconv_t *pfc = pfd->pfd_conv; int width = pfd->pfd_width; int prec = pfd->pfd_prec; int rval; char *f = format + 1; /* skip initial '%' */ const dtrace_recdesc_t *rec; dt_pfprint_f *func; caddr_t addr; size_t size; uint32_t flags; if (pfd->pfd_preflen != 0) { char *tmp = alloca(pfd->pfd_preflen + 1); bcopy(pfd->pfd_prefix, tmp, pfd->pfd_preflen); tmp[pfd->pfd_preflen] = '\0'; if ((rval = dt_printf(dtp, fp, tmp)) < 0) return (rval); if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) { /* * For printa(), we flush the buffer after each * prefix, setting the flags to indicate that * this is part of the printa() format string. */ flags = DTRACE_BUFDATA_AGGFORMAT; if (pfc == NULL && i == pfv->pfv_argc - 1) flags |= DTRACE_BUFDATA_AGGLAST; if (dt_buffered_flush(dtp, NULL, NULL, aggdata, flags) < 0) return (-1); } } if (pfc == NULL) { if (pfv->pfv_argc == 1) return (nrecs != 0); continue; } /* * If the conversion is %%, just invoke the print callback * with no data record and continue; it consumes no record. */ if (pfc->pfc_print == &pfprint_pct) { if (pfc->pfc_print(dtp, fp, NULL, pfd, NULL, 0, 1) >= 0) continue; return (-1); /* errno is set for us */ } if (pfd->pfd_flags & DT_PFCONV_DYNWIDTH) { if (dt_printf_getint(dtp, recp++, nrecs--, buf, len, &width) == -1) return (-1); /* errno is set for us */ pfd->pfd_dynwidth = width; } else { pfd->pfd_dynwidth = 0; } if ((pfd->pfd_flags & DT_PFCONV_DYNPREC) && dt_printf_getint( dtp, recp++, nrecs--, buf, len, &prec) == -1) return (-1); /* errno is set for us */ if (pfd->pfd_flags & DT_PFCONV_AGG) { /* * This should be impossible -- the compiler shouldn't * create a DT_PFCONV_AGG conversion without an * aggregation present. Still, we'd rather fail * gracefully than blow up... */ if (aggsdata == NULL) return (dt_set_errno(dtp, EDT_DMISMATCH)); aggdata = aggsdata[curagg]; agg = aggdata->dtada_desc; /* * We increment the current aggregation variable, but * not beyond the number of aggregation variables that * we're printing. This has the (desired) effect that * DT_PFCONV_AGG conversions beyond the number of * aggregation variables (re-)convert the aggregation * value of the last aggregation variable. */ if (curagg < naggvars - 1) curagg++; rec = &agg->dtagd_rec[aggrec]; addr = aggdata->dtada_data + rec->dtrd_offset; limit = addr + aggdata->dtada_size; normal = aggdata->dtada_normal; flags = DTRACE_BUFDATA_AGGVAL; } else { if (nrecs == 0) return (dt_set_errno(dtp, EDT_DMISMATCH)); if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) { /* * When printing aggregation keys, we always * set the aggdata to be the representative * (zeroth) aggregation. The aggdata isn't * actually used here in this case, but it is * passed to the buffer handler and must * therefore still be correct. */ aggdata = aggsdata[0]; flags = DTRACE_BUFDATA_AGGKEY; } rec = recp++; nrecs--; addr = (caddr_t)buf + rec->dtrd_offset; limit = lim; normal = 1; } size = rec->dtrd_size; if (addr + size > limit) { dt_dprintf("bad size: addr=%p size=0x%x lim=%p\n", (void *)addr, rec->dtrd_size, (void *)lim); return (dt_set_errno(dtp, EDT_DOFFSET)); } if (rec->dtrd_alignment != 0 && ((uintptr_t)addr & (rec->dtrd_alignment - 1)) != 0) { dt_dprintf("bad align: addr=%p size=0x%x align=0x%x\n", (void *)addr, rec->dtrd_size, rec->dtrd_alignment); return (dt_set_errno(dtp, EDT_DALIGN)); } switch (rec->dtrd_action) { case DTRACEAGG_AVG: func = pfprint_average; break; case DTRACEAGG_STDDEV: func = pfprint_stddev; break; case DTRACEAGG_QUANTIZE: func = pfprint_quantize; break; case DTRACEAGG_LQUANTIZE: func = pfprint_lquantize; break; case DTRACEAGG_LLQUANTIZE: func = pfprint_llquantize; break; case DTRACEACT_MOD: func = pfprint_mod; break; case DTRACEACT_UMOD: func = pfprint_umod; break; default: func = pfc->pfc_print; break; } if (pfd->pfd_flags & DT_PFCONV_ALT) *f++ = '#'; if (pfd->pfd_flags & DT_PFCONV_ZPAD) *f++ = '0'; if (width < 0 || (pfd->pfd_flags & DT_PFCONV_LEFT)) *f++ = '-'; if (pfd->pfd_flags & DT_PFCONV_SPOS) *f++ = '+'; if (pfd->pfd_flags & DT_PFCONV_GROUP) *f++ = '\''; if (pfd->pfd_flags & DT_PFCONV_SPACE) *f++ = ' '; /* * If we're printing a stack and DT_PFCONV_LEFT is set, we * don't add the width to the format string. See the block * comment in pfprint_stack() for a description of the * behavior in this case. */ if (func == pfprint_stack && (pfd->pfd_flags & DT_PFCONV_LEFT)) width = 0; if (width != 0) f += snprintf(f, sizeof (format), "%d", ABS(width)); if (prec > 0) f += snprintf(f, sizeof (format), ".%d", prec); (void) strcpy(f, pfd->pfd_fmt); pfd->pfd_rec = rec; if (func(dtp, fp, format, pfd, addr, size, normal) < 0) return (-1); /* errno is set for us */ if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) { /* * For printa(), we flush the buffer after each tuple * element, inidicating that this is the last record * as appropriate. */ if (i == pfv->pfv_argc - 1) flags |= DTRACE_BUFDATA_AGGLAST; if (dt_buffered_flush(dtp, NULL, rec, aggdata, flags) < 0) return (-1); } } return ((int)(recp - recs)); } int dtrace_sprintf(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata, const dtrace_recdesc_t *recp, uint_t nrecs, const void *buf, size_t len) { dtrace_optval_t size; int rval; rval = dtrace_getopt(dtp, "strsize", &size); assert(rval == 0); assert(dtp->dt_sprintf_buflen == 0); if (dtp->dt_sprintf_buf != NULL) free(dtp->dt_sprintf_buf); if ((dtp->dt_sprintf_buf = malloc(size)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); bzero(dtp->dt_sprintf_buf, size); dtp->dt_sprintf_buflen = size; rval = dt_printf_format(dtp, fp, fmtdata, recp, nrecs, buf, len, NULL, 0); dtp->dt_sprintf_buflen = 0; if (rval == -1) free(dtp->dt_sprintf_buf); return (rval); } /*ARGSUSED*/ int dtrace_system(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata, const dtrace_probedata_t *data, const dtrace_recdesc_t *recp, uint_t nrecs, const void *buf, size_t len) { int rval = dtrace_sprintf(dtp, fp, fmtdata, recp, nrecs, buf, len); if (rval == -1) return (rval); /* * Before we execute the specified command, flush fp to assure that * any prior dt_printf()'s appear before the output of the command * not after it. */ (void) fflush(fp); if (system(dtp->dt_sprintf_buf) == -1) return (dt_set_errno(dtp, errno)); return (rval); } int dtrace_freopen(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata, const dtrace_probedata_t *data, const dtrace_recdesc_t *recp, uint_t nrecs, const void *buf, size_t len) { char selfbuf[40], restorebuf[40], *filename; FILE *nfp; int rval, errval; dt_pfargv_t *pfv = fmtdata; dt_pfargd_t *pfd = pfv->pfv_argv; rval = dtrace_sprintf(dtp, fp, fmtdata, recp, nrecs, buf, len); if (rval == -1 || fp == NULL) return (rval); #if defined(sun) if (pfd->pfd_preflen != 0 && strcmp(pfd->pfd_prefix, DT_FREOPEN_RESTORE) == 0) { /* * The only way to have the format string set to the value * DT_FREOPEN_RESTORE is via the empty freopen() string -- * denoting that we should restore the old stdout. */ assert(strcmp(dtp->dt_sprintf_buf, DT_FREOPEN_RESTORE) == 0); if (dtp->dt_stdout_fd == -1) { /* * We could complain here by generating an error, * but it seems like overkill: it seems that calling * freopen() to restore stdout when freopen() has * never before been called should just be a no-op, * so we just return in this case. */ return (rval); } (void) snprintf(restorebuf, sizeof (restorebuf), "/dev/fd/%d", dtp->dt_stdout_fd); filename = restorebuf; } else { filename = dtp->dt_sprintf_buf; } /* * freopen(3C) will always close the specified stream and underlying * file descriptor -- even if the specified file can't be opened. * Even for the semantic cesspool that is standard I/O, this is * surprisingly brain-dead behavior: it means that any failure to * open the specified file destroys the specified stream in the * process -- which is particularly relevant when the specified stream * happens (or rather, happened) to be stdout. This could be resolved * were there an "fdreopen()" equivalent of freopen() that allowed one * to pass a file descriptor instead of the name of a file, but there * is no such thing. However, we can effect this ourselves by first * fopen()'ing the desired file, and then (assuming that that works), * freopen()'ing "/dev/fd/[fileno]", where [fileno] is the underlying * file descriptor for the fopen()'d file. This way, if the fopen() * fails, we can fail the operation without destroying stdout. */ if ((nfp = fopen(filename, "aF")) == NULL) { char *msg = strerror(errno); char *faultstr; int len = 80; len += strlen(msg) + strlen(filename); faultstr = alloca(len); (void) snprintf(faultstr, len, "couldn't freopen() \"%s\": %s", filename, strerror(errno)); if ((errval = dt_handle_liberr(dtp, data, faultstr)) == 0) return (rval); return (errval); } (void) snprintf(selfbuf, sizeof (selfbuf), "/dev/fd/%d", fileno(nfp)); if (dtp->dt_stdout_fd == -1) { /* * If this is the first time that we're calling freopen(), * we're going to stash away the file descriptor for stdout. * We don't expect the dup(2) to fail, so if it does we must * return failure. */ if ((dtp->dt_stdout_fd = dup(fileno(fp))) == -1) { (void) fclose(nfp); return (dt_set_errno(dtp, errno)); } } if (freopen(selfbuf, "aF", fp) == NULL) { (void) fclose(nfp); return (dt_set_errno(dtp, errno)); } (void) fclose(nfp); #else /* * The 'standard output' (which is not necessarily stdout) * treatment on FreeBSD is implemented differently than on * Solaris because FreeBSD's freopen() will attempt to re-use * the current file descriptor, causing the previous file to * be closed and thereby preventing it from be re-activated * later. * * For FreeBSD we use the concept of setting an output file * pointer in the DTrace handle if a dtrace_freopen() has * enabled another output file and we leave the caller's * file pointer untouched. If it was actually stdout, then * stdout remains open. If it was another file, then that * file remains open. While a dtrace_freopen() has activated * another file, we keep a pointer to that which we use in * the output functions by preference and only use the caller's * file pointer if no dtrace_freopen() call has been made. * * The check to see if we're re-activating the caller's * output file is much the same as on Solaris. */ if (pfd->pfd_preflen != 0 && strcmp(pfd->pfd_prefix, DT_FREOPEN_RESTORE) == 0) { /* * The only way to have the format string set to the value * DT_FREOPEN_RESTORE is via the empty freopen() string -- * denoting that we should restore the old stdout. */ assert(strcmp(dtp->dt_sprintf_buf, DT_FREOPEN_RESTORE) == 0); if (dtp->dt_freopen_fp == NULL) { /* * We could complain here by generating an error, * but it seems like overkill: it seems that calling * freopen() to restore stdout when freopen() has * never before been called should just be a no-op, * so we just return in this case. */ return (rval); } /* * At this point, to re-active the original output file, * on FreeBSD we only code the current file that this * function opened previously. */ (void) fclose(dtp->dt_freopen_fp); dtp->dt_freopen_fp = NULL; return (rval); } if ((nfp = fopen(dtp->dt_sprintf_buf, "a")) == NULL) { char *msg = strerror(errno); char *faultstr; int len = 80; len += strlen(msg) + strlen(dtp->dt_sprintf_buf); faultstr = alloca(len); (void) snprintf(faultstr, len, "couldn't freopen() \"%s\": %s", dtp->dt_sprintf_buf, strerror(errno)); if ((errval = dt_handle_liberr(dtp, data, faultstr)) == 0) return (rval); return (errval); } if (dtp->dt_freopen_fp != NULL) (void) fclose(dtp->dt_freopen_fp); /* Remember that the output has been redirected to the new file. */ dtp->dt_freopen_fp = nfp; #endif return (rval); } /*ARGSUSED*/ int dtrace_fprintf(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata, const dtrace_probedata_t *data, const dtrace_recdesc_t *recp, uint_t nrecs, const void *buf, size_t len) { return (dt_printf_format(dtp, fp, fmtdata, recp, nrecs, buf, len, NULL, 0)); } void * dtrace_printf_create(dtrace_hdl_t *dtp, const char *s) { dt_pfargv_t *pfv = dt_printf_create(dtp, s); dt_pfargd_t *pfd; int i; if (pfv == NULL) return (NULL); /* errno has been set for us */ pfd = pfv->pfv_argv; for (i = 0; i < pfv->pfv_argc; i++, pfd = pfd->pfd_next) { const dt_pfconv_t *pfc = pfd->pfd_conv; if (pfc == NULL) continue; /* * If the output format is not %s then we assume that we have * been given a correctly-sized format string, so we copy the * true format name including the size modifier. If the output * format is %s, then either the input format is %s as well or * it is one of our custom formats (e.g. pfprint_addr), so we * must set pfd_fmt to be the output format conversion "s". */ if (strcmp(pfc->pfc_ofmt, "s") != 0) (void) strcat(pfd->pfd_fmt, pfc->pfc_name); else (void) strcat(pfd->pfd_fmt, pfc->pfc_ofmt); } return (pfv); } void * dtrace_printa_create(dtrace_hdl_t *dtp, const char *s) { dt_pfargv_t *pfv = dtrace_printf_create(dtp, s); if (pfv == NULL) return (NULL); /* errno has been set for us */ pfv->pfv_flags |= DT_PRINTF_AGGREGATION; return (pfv); } /*ARGSUSED*/ size_t dtrace_printf_format(dtrace_hdl_t *dtp, void *fmtdata, char *s, size_t len) { dt_pfargv_t *pfv = fmtdata; dt_pfargd_t *pfd = pfv->pfv_argv; /* * An upper bound on the string length is the length of the original * format string, plus three times the number of conversions (each * conversion could add up an additional "ll" and/or pfd_width digit * in the case of converting %? to %16) plus one for a terminating \0. */ size_t formatlen = strlen(pfv->pfv_format) + 3 * pfv->pfv_argc + 1; char *format = alloca(formatlen); char *f = format; int i, j; for (i = 0; i < pfv->pfv_argc; i++, pfd = pfd->pfd_next) { const dt_pfconv_t *pfc = pfd->pfd_conv; const char *str; int width = pfd->pfd_width; int prec = pfd->pfd_prec; if (pfd->pfd_preflen != 0) { for (j = 0; j < pfd->pfd_preflen; j++) *f++ = pfd->pfd_prefix[j]; } if (pfc == NULL) continue; *f++ = '%'; if (pfd->pfd_flags & DT_PFCONV_ALT) *f++ = '#'; if (pfd->pfd_flags & DT_PFCONV_ZPAD) *f++ = '0'; if (pfd->pfd_flags & DT_PFCONV_LEFT) *f++ = '-'; if (pfd->pfd_flags & DT_PFCONV_SPOS) *f++ = '+'; if (pfd->pfd_flags & DT_PFCONV_DYNWIDTH) *f++ = '*'; if (pfd->pfd_flags & DT_PFCONV_DYNPREC) { *f++ = '.'; *f++ = '*'; } if (pfd->pfd_flags & DT_PFCONV_GROUP) *f++ = '\''; if (pfd->pfd_flags & DT_PFCONV_SPACE) *f++ = ' '; if (pfd->pfd_flags & DT_PFCONV_AGG) *f++ = '@'; if (width != 0) f += snprintf(f, sizeof (format), "%d", width); if (prec != 0) f += snprintf(f, sizeof (format), ".%d", prec); /* * If the output format is %s, then either %s is the underlying * conversion or the conversion is one of our customized ones, * e.g. pfprint_addr. In these cases, put the original string * name of the conversion (pfc_name) into the pickled format * string rather than the derived conversion (pfd_fmt). */ if (strcmp(pfc->pfc_ofmt, "s") == 0) str = pfc->pfc_name; else str = pfd->pfd_fmt; for (j = 0; str[j] != '\0'; j++) *f++ = str[j]; } *f = '\0'; /* insert nul byte; do not count in return value */ assert(f < format + formatlen); (void) strncpy(s, format, len); return ((size_t)(f - format)); } static int dt_fprinta(const dtrace_aggdata_t *adp, void *arg) { const dtrace_aggdesc_t *agg = adp->dtada_desc; const dtrace_recdesc_t *recp = &agg->dtagd_rec[0]; uint_t nrecs = agg->dtagd_nrecs; dt_pfwalk_t *pfw = arg; dtrace_hdl_t *dtp = pfw->pfw_argv->pfv_dtp; int id; if (dt_printf_getint(dtp, recp++, nrecs--, adp->dtada_data, adp->dtada_size, &id) != 0 || pfw->pfw_aid != id) return (0); /* no aggregation id or id does not match */ if (dt_printf_format(dtp, pfw->pfw_fp, pfw->pfw_argv, recp, nrecs, adp->dtada_data, adp->dtada_size, &adp, 1) == -1) return (pfw->pfw_err = dtp->dt_errno); /* * Cast away the const to set the bit indicating that this aggregation * has been printed. */ ((dtrace_aggdesc_t *)agg)->dtagd_flags |= DTRACE_AGD_PRINTED; return (0); } static int dt_fprintas(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) { const dtrace_aggdata_t *aggdata = aggsdata[0]; const dtrace_aggdesc_t *agg = aggdata->dtada_desc; const dtrace_recdesc_t *rec = &agg->dtagd_rec[1]; uint_t nrecs = agg->dtagd_nrecs - 1; dt_pfwalk_t *pfw = arg; dtrace_hdl_t *dtp = pfw->pfw_argv->pfv_dtp; int i; if (dt_printf_format(dtp, pfw->pfw_fp, pfw->pfw_argv, rec, nrecs, aggdata->dtada_data, aggdata->dtada_size, aggsdata, naggvars) == -1) return (pfw->pfw_err = dtp->dt_errno); /* * For each aggregation, indicate that it has been printed, casting * away the const as necessary. */ for (i = 1; i < naggvars; i++) { agg = aggsdata[i]->dtada_desc; ((dtrace_aggdesc_t *)agg)->dtagd_flags |= DTRACE_AGD_PRINTED; } return (0); } /*ARGSUSED*/ int dtrace_fprinta(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata, const dtrace_probedata_t *data, const dtrace_recdesc_t *recs, uint_t nrecs, const void *buf, size_t len) { dt_pfwalk_t pfw; int i, naggvars = 0; dtrace_aggvarid_t *aggvars; aggvars = alloca(nrecs * sizeof (dtrace_aggvarid_t)); /* * This might be a printa() with multiple aggregation variables. We * need to scan forward through the records until we find a record from * a different statement. */ for (i = 0; i < nrecs; i++) { const dtrace_recdesc_t *nrec = &recs[i]; if (nrec->dtrd_uarg != recs->dtrd_uarg) break; if (nrec->dtrd_action != recs->dtrd_action) return (dt_set_errno(dtp, EDT_BADAGG)); aggvars[naggvars++] = /* LINTED - alignment */ *((dtrace_aggvarid_t *)((caddr_t)buf + nrec->dtrd_offset)); } if (naggvars == 0) return (dt_set_errno(dtp, EDT_BADAGG)); pfw.pfw_argv = fmtdata; pfw.pfw_fp = fp; pfw.pfw_err = 0; if (naggvars == 1) { pfw.pfw_aid = aggvars[0]; if (dtrace_aggregate_walk_sorted(dtp, dt_fprinta, &pfw) == -1 || pfw.pfw_err != 0) return (-1); /* errno is set for us */ } else { if (dtrace_aggregate_walk_joined(dtp, aggvars, naggvars, dt_fprintas, &pfw) == -1 || pfw.pfw_err != 0) return (-1); /* errno is set for us */ } return (i); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_provider.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_provider.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_provider.c (revision 268578) @@ -1,883 +1,902 @@ /* * 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) 2013, Joyent, Inc. All rights reserved. + */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include #if defined(sun) #include #endif #include #include #include #include #if defined(sun) #include #endif #include #include #include #include #include #include +#include +#include static dt_provider_t * dt_provider_insert(dtrace_hdl_t *dtp, dt_provider_t *pvp, uint_t h) { dt_list_append(&dtp->dt_provlist, pvp); pvp->pv_next = dtp->dt_provs[h]; dtp->dt_provs[h] = pvp; dtp->dt_nprovs++; return (pvp); } dt_provider_t * dt_provider_lookup(dtrace_hdl_t *dtp, const char *name) { uint_t h = dt_strtab_hash(name, NULL) % dtp->dt_provbuckets; dtrace_providerdesc_t desc; dt_provider_t *pvp; for (pvp = dtp->dt_provs[h]; pvp != NULL; pvp = pvp->pv_next) { if (strcmp(pvp->pv_desc.dtvd_name, name) == 0) return (pvp); } if (strisglob(name) || name[0] == '\0') { (void) dt_set_errno(dtp, EDT_NOPROV); return (NULL); } bzero(&desc, sizeof (desc)); (void) strlcpy(desc.dtvd_name, name, DTRACE_PROVNAMELEN); if (dt_ioctl(dtp, DTRACEIOC_PROVIDER, &desc) == -1) { (void) dt_set_errno(dtp, errno == ESRCH ? EDT_NOPROV : errno); return (NULL); } if ((pvp = dt_provider_create(dtp, name)) == NULL) return (NULL); /* dt_errno is set for us */ bcopy(&desc, &pvp->pv_desc, sizeof (desc)); pvp->pv_flags |= DT_PROVIDER_IMPL; return (pvp); } dt_provider_t * dt_provider_create(dtrace_hdl_t *dtp, const char *name) { dt_provider_t *pvp; if ((pvp = dt_zalloc(dtp, sizeof (dt_provider_t))) == NULL) return (NULL); (void) strlcpy(pvp->pv_desc.dtvd_name, name, DTRACE_PROVNAMELEN); pvp->pv_probes = dt_idhash_create(pvp->pv_desc.dtvd_name, NULL, 0, 0); pvp->pv_gen = dtp->dt_gen; pvp->pv_hdl = dtp; if (pvp->pv_probes == NULL) { dt_free(dtp, pvp); (void) dt_set_errno(dtp, EDT_NOMEM); return (NULL); } pvp->pv_desc.dtvd_attr.dtpa_provider = _dtrace_prvattr; pvp->pv_desc.dtvd_attr.dtpa_mod = _dtrace_prvattr; pvp->pv_desc.dtvd_attr.dtpa_func = _dtrace_prvattr; pvp->pv_desc.dtvd_attr.dtpa_name = _dtrace_prvattr; pvp->pv_desc.dtvd_attr.dtpa_args = _dtrace_prvattr; return (dt_provider_insert(dtp, pvp, dt_strtab_hash(name, NULL) % dtp->dt_provbuckets)); } void dt_provider_destroy(dtrace_hdl_t *dtp, dt_provider_t *pvp) { dt_provider_t **pp; uint_t h; assert(pvp->pv_hdl == dtp); h = dt_strtab_hash(pvp->pv_desc.dtvd_name, NULL) % dtp->dt_provbuckets; pp = &dtp->dt_provs[h]; while (*pp != NULL && *pp != pvp) pp = &(*pp)->pv_next; assert(*pp != NULL && *pp == pvp); *pp = pvp->pv_next; dt_list_delete(&dtp->dt_provlist, pvp); dtp->dt_nprovs--; if (pvp->pv_probes != NULL) dt_idhash_destroy(pvp->pv_probes); dt_node_link_free(&pvp->pv_nodes); dt_free(dtp, pvp->pv_xrefs); dt_free(dtp, pvp); } int dt_provider_xref(dtrace_hdl_t *dtp, dt_provider_t *pvp, id_t id) { size_t oldsize = BT_SIZEOFMAP(pvp->pv_xrmax); size_t newsize = BT_SIZEOFMAP(dtp->dt_xlatorid); assert(id >= 0 && id < dtp->dt_xlatorid); if (newsize > oldsize) { ulong_t *xrefs = dt_zalloc(dtp, newsize); if (xrefs == NULL) return (-1); bcopy(pvp->pv_xrefs, xrefs, oldsize); dt_free(dtp, pvp->pv_xrefs); pvp->pv_xrefs = xrefs; pvp->pv_xrmax = dtp->dt_xlatorid; } BT_SET(pvp->pv_xrefs, id); return (0); } static uint8_t dt_probe_argmap(dt_node_t *xnp, dt_node_t *nnp) { uint8_t i; for (i = 0; nnp != NULL; i++) { if (nnp->dn_string != NULL && strcmp(nnp->dn_string, xnp->dn_string) == 0) break; else nnp = nnp->dn_list; } return (i); } static dt_node_t * dt_probe_alloc_args(dt_provider_t *pvp, int argc) { dt_node_t *args = NULL, *pnp = NULL, *dnp; int i; for (i = 0; i < argc; i++, pnp = dnp) { if ((dnp = dt_node_xalloc(pvp->pv_hdl, DT_NODE_TYPE)) == NULL) return (NULL); dnp->dn_link = pvp->pv_nodes; pvp->pv_nodes = dnp; if (args == NULL) args = dnp; else pnp->dn_list = dnp; } return (args); } static size_t dt_probe_keylen(const dtrace_probedesc_t *pdp) { return (strlen(pdp->dtpd_mod) + 1 + strlen(pdp->dtpd_func) + 1 + strlen(pdp->dtpd_name) + 1); } static char * dt_probe_key(const dtrace_probedesc_t *pdp, char *s) { (void) snprintf(s, INT_MAX, "%s:%s:%s", pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); return (s); } /* * If a probe was discovered from the kernel, ask dtrace(7D) for a description * of each of its arguments, including native and translated types. */ static dt_probe_t * dt_probe_discover(dt_provider_t *pvp, const dtrace_probedesc_t *pdp) { dtrace_hdl_t *dtp = pvp->pv_hdl; char *name = dt_probe_key(pdp, alloca(dt_probe_keylen(pdp))); dt_node_t *xargs, *nargs; dt_ident_t *idp; dt_probe_t *prp; dtrace_typeinfo_t dtt; int i, nc, xc; int adc = _dtrace_argmax; dtrace_argdesc_t *adv = alloca(sizeof (dtrace_argdesc_t) * adc); dtrace_argdesc_t *adp = adv; assert(strcmp(pvp->pv_desc.dtvd_name, pdp->dtpd_provider) == 0); assert(pdp->dtpd_id != DTRACE_IDNONE); dt_dprintf("discovering probe %s:%s id=%d\n", pvp->pv_desc.dtvd_name, name, pdp->dtpd_id); for (nc = -1, i = 0; i < adc; i++, adp++) { bzero(adp, sizeof (dtrace_argdesc_t)); adp->dtargd_ndx = i; adp->dtargd_id = pdp->dtpd_id; if (dt_ioctl(dtp, DTRACEIOC_PROBEARG, adp) != 0) { (void) dt_set_errno(dtp, errno); return (NULL); } if (adp->dtargd_ndx == DTRACE_ARGNONE) break; /* all argument descs have been retrieved */ nc = MAX(nc, adp->dtargd_mapping); } xc = i; nc++; /* + * The pid provider believes in giving the kernel a break. No reason to + * give the kernel all the ctf containers that we're keeping ourselves + * just to get it back from it. So if we're coming from a pid provider + * probe and the kernel gave us no argument information we'll get some + * here. If for some crazy reason the kernel knows about our userland + * types then we just ignore this. + */ + if (xc == 0 && nc == 0 && + strncmp(pvp->pv_desc.dtvd_name, "pid", 3) == 0) { + nc = adc; + dt_pid_get_types(dtp, pdp, adv, &nc); + xc = nc; + } + + /* * Now that we have discovered the number of native and translated * arguments from the argument descriptions, allocate a new probe ident * and corresponding dt_probe_t and hash it into the provider. */ xargs = dt_probe_alloc_args(pvp, xc); nargs = dt_probe_alloc_args(pvp, nc); if ((xc != 0 && xargs == NULL) || (nc != 0 && nargs == NULL)) return (NULL); /* dt_errno is set for us */ idp = dt_ident_create(name, DT_IDENT_PROBE, DT_IDFLG_ORPHAN, pdp->dtpd_id, _dtrace_defattr, 0, &dt_idops_probe, NULL, dtp->dt_gen); if (idp == NULL) { (void) dt_set_errno(dtp, EDT_NOMEM); return (NULL); } if ((prp = dt_probe_create(dtp, idp, 2, nargs, nc, xargs, xc)) == NULL) { dt_ident_destroy(idp); return (NULL); } dt_probe_declare(pvp, prp); /* * Once our new dt_probe_t is fully constructed, iterate over the * cached argument descriptions and assign types to prp->pr_nargv[] * and prp->pr_xargv[] and assign mappings to prp->pr_mapping[]. */ for (adp = adv, i = 0; i < xc; i++, adp++) { if (dtrace_type_strcompile(dtp, adp->dtargd_native, &dtt) != 0) { dt_dprintf("failed to resolve input type %s " "for %s:%s arg #%d: %s\n", adp->dtargd_native, pvp->pv_desc.dtvd_name, name, i + 1, dtrace_errmsg(dtp, dtrace_errno(dtp))); dtt.dtt_object = NULL; dtt.dtt_ctfp = NULL; dtt.dtt_type = CTF_ERR; } else { dt_node_type_assign(prp->pr_nargv[adp->dtargd_mapping], - dtt.dtt_ctfp, dtt.dtt_type); + dtt.dtt_ctfp, dtt.dtt_type, + dtt.dtt_flags & DTT_FL_USER ? B_TRUE : B_FALSE); } if (dtt.dtt_type != CTF_ERR && (adp->dtargd_xlate[0] == '\0' || strcmp(adp->dtargd_native, adp->dtargd_xlate) == 0)) { dt_node_type_propagate(prp->pr_nargv[ adp->dtargd_mapping], prp->pr_xargv[i]); } else if (dtrace_type_strcompile(dtp, adp->dtargd_xlate, &dtt) != 0) { dt_dprintf("failed to resolve output type %s " "for %s:%s arg #%d: %s\n", adp->dtargd_xlate, pvp->pv_desc.dtvd_name, name, i + 1, dtrace_errmsg(dtp, dtrace_errno(dtp))); dtt.dtt_object = NULL; dtt.dtt_ctfp = NULL; dtt.dtt_type = CTF_ERR; } else { dt_node_type_assign(prp->pr_xargv[i], - dtt.dtt_ctfp, dtt.dtt_type); + dtt.dtt_ctfp, dtt.dtt_type, B_FALSE); } prp->pr_mapping[i] = adp->dtargd_mapping; prp->pr_argv[i] = dtt; } return (prp); } /* * Lookup a probe declaration based on a known provider and full or partially * specified module, function, and name. If the probe is not known to us yet, * ask dtrace(7D) to match the description and then cache any useful results. */ dt_probe_t * dt_probe_lookup(dt_provider_t *pvp, const char *s) { dtrace_hdl_t *dtp = pvp->pv_hdl; dtrace_probedesc_t pd; dt_ident_t *idp; size_t keylen; char *key; if (dtrace_str2desc(dtp, DTRACE_PROBESPEC_NAME, s, &pd) != 0) return (NULL); /* dt_errno is set for us */ keylen = dt_probe_keylen(&pd); key = dt_probe_key(&pd, alloca(keylen)); /* * If the probe is already declared, then return the dt_probe_t from * the existing identifier. This could come from a static declaration * or it could have been cached from an earlier call to this function. */ if ((idp = dt_idhash_lookup(pvp->pv_probes, key)) != NULL) return (idp->di_data); /* * If the probe isn't known, use the probe description computed above * to ask dtrace(7D) to find the first matching probe. */ if (dt_ioctl(dtp, DTRACEIOC_PROBEMATCH, &pd) == 0) return (dt_probe_discover(pvp, &pd)); if (errno == ESRCH || errno == EBADF) (void) dt_set_errno(dtp, EDT_NOPROBE); else (void) dt_set_errno(dtp, errno); return (NULL); } dt_probe_t * dt_probe_create(dtrace_hdl_t *dtp, dt_ident_t *idp, int protoc, dt_node_t *nargs, uint_t nargc, dt_node_t *xargs, uint_t xargc) { dt_module_t *dmp; dt_probe_t *prp; const char *p; uint_t i; assert(idp->di_kind == DT_IDENT_PROBE); assert(idp->di_data == NULL); /* * If only a single prototype is given, set xargc/s to nargc/s to * simplify subsequent use. Note that we can have one or both of nargs * and xargs be specified but set to NULL, indicating a void prototype. */ if (protoc < 2) { assert(xargs == NULL); assert(xargc == 0); xargs = nargs; xargc = nargc; } if ((prp = dt_alloc(dtp, sizeof (dt_probe_t))) == NULL) return (NULL); prp->pr_pvp = NULL; prp->pr_ident = idp; p = strrchr(idp->di_name, ':'); assert(p != NULL); prp->pr_name = p + 1; prp->pr_nargs = nargs; prp->pr_nargv = dt_alloc(dtp, sizeof (dt_node_t *) * nargc); prp->pr_nargc = nargc; prp->pr_xargs = xargs; prp->pr_xargv = dt_alloc(dtp, sizeof (dt_node_t *) * xargc); prp->pr_xargc = xargc; prp->pr_mapping = dt_alloc(dtp, sizeof (uint8_t) * xargc); prp->pr_inst = NULL; prp->pr_argv = dt_alloc(dtp, sizeof (dtrace_typeinfo_t) * xargc); prp->pr_argc = xargc; if ((prp->pr_nargc != 0 && prp->pr_nargv == NULL) || (prp->pr_xargc != 0 && prp->pr_xargv == NULL) || (prp->pr_xargc != 0 && prp->pr_mapping == NULL) || (prp->pr_argc != 0 && prp->pr_argv == NULL)) { dt_probe_destroy(prp); return (NULL); } for (i = 0; i < xargc; i++, xargs = xargs->dn_list) { if (xargs->dn_string != NULL) prp->pr_mapping[i] = dt_probe_argmap(xargs, nargs); else prp->pr_mapping[i] = i; prp->pr_xargv[i] = xargs; if ((dmp = dt_module_lookup_by_ctf(dtp, xargs->dn_ctfp)) != NULL) prp->pr_argv[i].dtt_object = dmp->dm_name; else prp->pr_argv[i].dtt_object = NULL; prp->pr_argv[i].dtt_ctfp = xargs->dn_ctfp; prp->pr_argv[i].dtt_type = xargs->dn_type; } for (i = 0; i < nargc; i++, nargs = nargs->dn_list) prp->pr_nargv[i] = nargs; idp->di_data = prp; return (prp); } void dt_probe_declare(dt_provider_t *pvp, dt_probe_t *prp) { assert(prp->pr_ident->di_kind == DT_IDENT_PROBE); assert(prp->pr_ident->di_data == prp); assert(prp->pr_pvp == NULL); if (prp->pr_xargs != prp->pr_nargs) pvp->pv_flags &= ~DT_PROVIDER_INTF; prp->pr_pvp = pvp; dt_idhash_xinsert(pvp->pv_probes, prp->pr_ident); } void dt_probe_destroy(dt_probe_t *prp) { dt_probe_instance_t *pip, *pip_next; dtrace_hdl_t *dtp; if (prp->pr_pvp != NULL) dtp = prp->pr_pvp->pv_hdl; else dtp = yypcb->pcb_hdl; dt_node_list_free(&prp->pr_nargs); dt_node_list_free(&prp->pr_xargs); dt_free(dtp, prp->pr_nargv); dt_free(dtp, prp->pr_xargv); for (pip = prp->pr_inst; pip != NULL; pip = pip_next) { pip_next = pip->pi_next; dt_free(dtp, pip->pi_offs); dt_free(dtp, pip->pi_enoffs); dt_free(dtp, pip); } dt_free(dtp, prp->pr_mapping); dt_free(dtp, prp->pr_argv); dt_free(dtp, prp); } int dt_probe_define(dt_provider_t *pvp, dt_probe_t *prp, const char *fname, const char *rname, uint32_t offset, int isenabled) { dtrace_hdl_t *dtp = pvp->pv_hdl; dt_probe_instance_t *pip; uint32_t **offs; uint_t *noffs, *maxoffs; assert(fname != NULL); for (pip = prp->pr_inst; pip != NULL; pip = pip->pi_next) { if (strcmp(pip->pi_fname, fname) == 0 && ((rname == NULL && pip->pi_rname[0] == '\0') || (rname != NULL && strcmp(pip->pi_rname, rname)) == 0)) break; } if (pip == NULL) { if ((pip = dt_zalloc(dtp, sizeof (*pip))) == NULL) return (-1); if ((pip->pi_offs = dt_zalloc(dtp, sizeof (uint32_t))) == NULL) { dt_free(dtp, pip); return (-1); } if ((pip->pi_enoffs = dt_zalloc(dtp, sizeof (uint32_t))) == NULL) { dt_free(dtp, pip->pi_offs); dt_free(dtp, pip); return (-1); } (void) strlcpy(pip->pi_fname, fname, sizeof (pip->pi_fname)); if (rname != NULL) { if (strlen(rname) + 1 > sizeof (pip->pi_rname)) { dt_free(dtp, pip->pi_offs); dt_free(dtp, pip); return (dt_set_errno(dtp, EDT_COMPILER)); } (void) strcpy(pip->pi_rname, rname); } pip->pi_noffs = 0; pip->pi_maxoffs = 1; pip->pi_nenoffs = 0; pip->pi_maxenoffs = 1; pip->pi_next = prp->pr_inst; prp->pr_inst = pip; } if (isenabled) { offs = &pip->pi_enoffs; noffs = &pip->pi_nenoffs; maxoffs = &pip->pi_maxenoffs; } else { offs = &pip->pi_offs; noffs = &pip->pi_noffs; maxoffs = &pip->pi_maxoffs; } if (*noffs == *maxoffs) { uint_t new_max = *maxoffs * 2; uint32_t *new_offs = dt_alloc(dtp, sizeof (uint32_t) * new_max); if (new_offs == NULL) return (-1); bcopy(*offs, new_offs, sizeof (uint32_t) * *maxoffs); dt_free(dtp, *offs); *maxoffs = new_max; *offs = new_offs; } dt_dprintf("defined probe %s %s:%s %s() +0x%x (%s)\n", isenabled ? "(is-enabled)" : "", pvp->pv_desc.dtvd_name, prp->pr_ident->di_name, fname, offset, rname != NULL ? rname : fname); assert(*noffs < *maxoffs); (*offs)[(*noffs)++] = offset; return (0); } /* * Lookup the dynamic translator type tag for the specified probe argument and * assign the type to the specified node. If the type is not yet defined, add * it to the "D" module's type container as a typedef for an unknown type. */ dt_node_t * dt_probe_tag(dt_probe_t *prp, uint_t argn, dt_node_t *dnp) { dtrace_hdl_t *dtp = prp->pr_pvp->pv_hdl; dtrace_typeinfo_t dtt; size_t len; char *tag; len = snprintf(NULL, 0, "__dtrace_%s___%s_arg%u", prp->pr_pvp->pv_desc.dtvd_name, prp->pr_name, argn); tag = alloca(len + 1); (void) snprintf(tag, len + 1, "__dtrace_%s___%s_arg%u", prp->pr_pvp->pv_desc.dtvd_name, prp->pr_name, argn); if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_DDEFS, tag, &dtt) != 0) { dtt.dtt_object = DTRACE_OBJ_DDEFS; dtt.dtt_ctfp = DT_DYN_CTFP(dtp); dtt.dtt_type = ctf_add_typedef(DT_DYN_CTFP(dtp), CTF_ADD_ROOT, tag, DT_DYN_TYPE(dtp)); if (dtt.dtt_type == CTF_ERR || ctf_update(dtt.dtt_ctfp) == CTF_ERR) { xyerror(D_UNKNOWN, "cannot define type %s: %s\n", tag, ctf_errmsg(ctf_errno(dtt.dtt_ctfp))); } } bzero(dnp, sizeof (dt_node_t)); dnp->dn_kind = DT_NODE_TYPE; - dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type); + dt_node_type_assign(dnp, dtt.dtt_ctfp, dtt.dtt_type, B_FALSE); dt_node_attr_assign(dnp, _dtrace_defattr); return (dnp); } /*ARGSUSED*/ static int dt_probe_desc(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) { if (((dtrace_probedesc_t *)arg)->dtpd_id == DTRACE_IDNONE) { bcopy(pdp, arg, sizeof (dtrace_probedesc_t)); return (0); } return (1); } dt_probe_t * dt_probe_info(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, dtrace_probeinfo_t *pip) { int m_is_glob = pdp->dtpd_mod[0] == '\0' || strisglob(pdp->dtpd_mod); int f_is_glob = pdp->dtpd_func[0] == '\0' || strisglob(pdp->dtpd_func); int n_is_glob = pdp->dtpd_name[0] == '\0' || strisglob(pdp->dtpd_name); dt_probe_t *prp = NULL; const dtrace_pattr_t *pap; dt_provider_t *pvp; dt_ident_t *idp; /* * Attempt to lookup the probe in our existing cache for this provider. * If none is found and an explicit probe ID was specified, discover * that specific probe and cache its description and arguments. */ if ((pvp = dt_provider_lookup(dtp, pdp->dtpd_provider)) != NULL) { size_t keylen = dt_probe_keylen(pdp); char *key = dt_probe_key(pdp, alloca(keylen)); if ((idp = dt_idhash_lookup(pvp->pv_probes, key)) != NULL) prp = idp->di_data; else if (pdp->dtpd_id != DTRACE_IDNONE) prp = dt_probe_discover(pvp, pdp); } /* * If no probe was found in our cache, convert the caller's partial * probe description into a fully-formed matching probe description by * iterating over up to at most two probes that match 'pdp'. We then * call dt_probe_discover() on the resulting probe identifier. */ if (prp == NULL) { dtrace_probedesc_t pd; int m; bzero(&pd, sizeof (pd)); pd.dtpd_id = DTRACE_IDNONE; /* * Call dtrace_probe_iter() to find matching probes. Our * dt_probe_desc() callback will produce the following results: * * m < 0 dtrace_probe_iter() found zero matches (or failed). * m > 0 dtrace_probe_iter() found more than one match. * m = 0 dtrace_probe_iter() found exactly one match. */ if ((m = dtrace_probe_iter(dtp, pdp, dt_probe_desc, &pd)) < 0) return (NULL); /* dt_errno is set for us */ if ((pvp = dt_provider_lookup(dtp, pd.dtpd_provider)) == NULL) return (NULL); /* dt_errno is set for us */ /* * If more than one probe was matched, then do not report probe * information if either of the following conditions is true: * * (a) The Arguments Data stability of the matched provider is * less than Evolving. * * (b) Any description component that is at least Evolving is * empty or is specified using a globbing expression. * * These conditions imply that providers that provide Evolving * or better Arguments Data stability must guarantee that all * probes with identical field names in a field of Evolving or * better Name stability have identical argument signatures. */ if (m > 0) { if (pvp->pv_desc.dtvd_attr.dtpa_args.dtat_data < DTRACE_STABILITY_EVOLVING) { (void) dt_set_errno(dtp, EDT_UNSTABLE); return (NULL); } if (pvp->pv_desc.dtvd_attr.dtpa_mod.dtat_name >= DTRACE_STABILITY_EVOLVING && m_is_glob) { (void) dt_set_errno(dtp, EDT_UNSTABLE); return (NULL); } if (pvp->pv_desc.dtvd_attr.dtpa_func.dtat_name >= DTRACE_STABILITY_EVOLVING && f_is_glob) { (void) dt_set_errno(dtp, EDT_UNSTABLE); return (NULL); } if (pvp->pv_desc.dtvd_attr.dtpa_name.dtat_name >= DTRACE_STABILITY_EVOLVING && n_is_glob) { (void) dt_set_errno(dtp, EDT_UNSTABLE); return (NULL); } } /* * If we matched a probe exported by dtrace(7D), then discover * the real attributes. Otherwise grab the static declaration. */ if (pd.dtpd_id != DTRACE_IDNONE) prp = dt_probe_discover(pvp, &pd); else prp = dt_probe_lookup(pvp, pd.dtpd_name); if (prp == NULL) return (NULL); /* dt_errno is set for us */ } assert(pvp != NULL && prp != NULL); /* * Compute the probe description attributes by taking the minimum of * the attributes of the specified fields. If no provider is specified * or a glob pattern is used for the provider, use Unstable attributes. */ if (pdp->dtpd_provider[0] == '\0' || strisglob(pdp->dtpd_provider)) pap = &_dtrace_prvdesc; else pap = &pvp->pv_desc.dtvd_attr; pip->dtp_attr = pap->dtpa_provider; if (!m_is_glob) pip->dtp_attr = dt_attr_min(pip->dtp_attr, pap->dtpa_mod); if (!f_is_glob) pip->dtp_attr = dt_attr_min(pip->dtp_attr, pap->dtpa_func); if (!n_is_glob) pip->dtp_attr = dt_attr_min(pip->dtp_attr, pap->dtpa_name); pip->dtp_arga = pap->dtpa_args; pip->dtp_argv = prp->pr_argv; pip->dtp_argc = prp->pr_argc; return (prp); } int dtrace_probe_info(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, dtrace_probeinfo_t *pip) { return (dt_probe_info(dtp, pdp, pip) != NULL ? 0 : -1); } /*ARGSUSED*/ static int dt_probe_iter(dt_idhash_t *ihp, dt_ident_t *idp, dt_probe_iter_t *pit) { const dt_probe_t *prp = idp->di_data; if (!dt_gmatch(prp->pr_name, pit->pit_pat)) return (0); /* continue on and examine next probe in hash */ (void) strlcpy(pit->pit_desc.dtpd_name, prp->pr_name, DTRACE_NAMELEN); pit->pit_desc.dtpd_id = idp->di_id; pit->pit_matches++; return (pit->pit_func(pit->pit_hdl, &pit->pit_desc, pit->pit_arg)); } int dtrace_probe_iter(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, dtrace_probe_f *func, void *arg) { const char *provider = pdp ? pdp->dtpd_provider : NULL; dtrace_id_t id = DTRACE_IDNONE; dtrace_probedesc_t pd; dt_probe_iter_t pit; int cmd, rv; bzero(&pit, sizeof (pit)); pit.pit_hdl = dtp; pit.pit_func = func; pit.pit_arg = arg; pit.pit_pat = pdp ? pdp->dtpd_name : NULL; for (pit.pit_pvp = dt_list_next(&dtp->dt_provlist); pit.pit_pvp != NULL; pit.pit_pvp = dt_list_next(pit.pit_pvp)) { if (pit.pit_pvp->pv_flags & DT_PROVIDER_IMPL) continue; /* we'll get these later using dt_ioctl() */ if (!dt_gmatch(pit.pit_pvp->pv_desc.dtvd_name, provider)) continue; (void) strlcpy(pit.pit_desc.dtpd_provider, pit.pit_pvp->pv_desc.dtvd_name, DTRACE_PROVNAMELEN); if ((rv = dt_idhash_iter(pit.pit_pvp->pv_probes, (dt_idhash_f *)dt_probe_iter, &pit)) != 0) return (rv); } if (pdp != NULL) cmd = DTRACEIOC_PROBEMATCH; else cmd = DTRACEIOC_PROBES; for (;;) { if (pdp != NULL) bcopy(pdp, &pd, sizeof (pd)); pd.dtpd_id = id; if (dt_ioctl(dtp, cmd, &pd) != 0) break; else if ((rv = func(dtp, &pd, arg)) != 0) return (rv); pit.pit_matches++; id = pd.dtpd_id + 1; } switch (errno) { case ESRCH: case EBADF: return (pit.pit_matches ? 0 : dt_set_errno(dtp, EDT_NOPROBE)); case EINVAL: return (dt_set_errno(dtp, EDT_BADPGLOB)); default: return (dt_set_errno(dtp, errno)); } } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_xlator.c =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_xlator.c (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dt_xlator.c (revision 268578) @@ -1,383 +1,386 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include #include #include #include #include #include #include /* * Create a member node corresponding to one of the output members of a dynamic * translator. We set the member's dn_membexpr to a DT_NODE_XLATOR node that * has dn_op set to DT_TOK_XLATE and refers back to the translator itself. The * code generator will then use this as the indicator for dynamic translation. */ /*ARGSUSED*/ static int dt_xlator_create_member(const char *name, ctf_id_t type, ulong_t off, void *arg) { dt_xlator_t *dxp = arg; dtrace_hdl_t *dtp = dxp->dx_hdl; dt_node_t *enp, *mnp; if ((enp = dt_node_xalloc(dtp, DT_NODE_XLATOR)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); enp->dn_link = dxp->dx_nodes; dxp->dx_nodes = enp; if ((mnp = dt_node_xalloc(dtp, DT_NODE_MEMBER)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); mnp->dn_link = dxp->dx_nodes; dxp->dx_nodes = mnp; /* * For the member expression, we use a DT_NODE_XLATOR/TOK_XLATE whose * xlator refers back to the translator and whose dn_xmember refers to * the current member. These refs will be used by dt_cg.c and dt_as.c. */ enp->dn_op = DT_TOK_XLATE; enp->dn_xlator = dxp; enp->dn_xmember = mnp; - dt_node_type_assign(enp, dxp->dx_dst_ctfp, type); + dt_node_type_assign(enp, dxp->dx_dst_ctfp, type, B_FALSE); /* * For the member itself, we use a DT_NODE_MEMBER as usual with the * appropriate name, output type, and member expression set to 'enp'. */ if (dxp->dx_members != NULL) { assert(enp->dn_link->dn_kind == DT_NODE_MEMBER); enp->dn_link->dn_list = mnp; } else dxp->dx_members = mnp; mnp->dn_membname = strdup(name); mnp->dn_membexpr = enp; - dt_node_type_assign(mnp, dxp->dx_dst_ctfp, type); + dt_node_type_assign(mnp, dxp->dx_dst_ctfp, type, B_FALSE); if (mnp->dn_membname == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); return (0); } dt_xlator_t * dt_xlator_create(dtrace_hdl_t *dtp, const dtrace_typeinfo_t *src, const dtrace_typeinfo_t *dst, const char *name, dt_node_t *members, dt_node_t *nodes) { dt_xlator_t *dxp = dt_zalloc(dtp, sizeof (dt_xlator_t)); dtrace_typeinfo_t ptr = *dst; dt_xlator_t **map; dt_node_t *dnp; uint_t kind; if (dxp == NULL) return (NULL); dxp->dx_hdl = dtp; dxp->dx_id = dtp->dt_xlatorid++; dxp->dx_gen = dtp->dt_gen; dxp->dx_arg = -1; if ((map = dt_alloc(dtp, sizeof (void *) * (dxp->dx_id + 1))) == NULL) { dt_free(dtp, dxp); return (NULL); } dt_list_append(&dtp->dt_xlators, dxp); bcopy(dtp->dt_xlatormap, map, sizeof (void *) * dxp->dx_id); dt_free(dtp, dtp->dt_xlatormap); dtp->dt_xlatormap = map; dtp->dt_xlatormap[dxp->dx_id] = dxp; if (dt_type_pointer(&ptr) == -1) { ptr.dtt_ctfp = NULL; ptr.dtt_type = CTF_ERR; } dxp->dx_ident = dt_ident_create(name ? name : "T", DT_IDENT_SCALAR, DT_IDFLG_REF | DT_IDFLG_ORPHAN, 0, _dtrace_defattr, 0, &dt_idops_thaw, NULL, dtp->dt_gen); if (dxp->dx_ident == NULL) goto err; /* no memory for identifier */ dxp->dx_ident->di_ctfp = src->dtt_ctfp; dxp->dx_ident->di_type = src->dtt_type; /* * If an input parameter name is given, this is a static translator * definition: create an idhash and identifier for the parameter. */ if (name != NULL) { dxp->dx_locals = dt_idhash_create("xlparams", NULL, 0, 0); if (dxp->dx_locals == NULL) goto err; /* no memory for identifier hash */ dt_idhash_xinsert(dxp->dx_locals, dxp->dx_ident); } dxp->dx_souid.di_name = "translator"; dxp->dx_souid.di_kind = DT_IDENT_XLSOU; dxp->dx_souid.di_flags = DT_IDFLG_REF; dxp->dx_souid.di_id = dxp->dx_id; dxp->dx_souid.di_attr = _dtrace_defattr; dxp->dx_souid.di_ops = &dt_idops_thaw; dxp->dx_souid.di_data = dxp; dxp->dx_souid.di_ctfp = dst->dtt_ctfp; dxp->dx_souid.di_type = dst->dtt_type; dxp->dx_souid.di_gen = dtp->dt_gen; dxp->dx_ptrid.di_name = "translator"; dxp->dx_ptrid.di_kind = DT_IDENT_XLPTR; dxp->dx_ptrid.di_flags = DT_IDFLG_REF; dxp->dx_ptrid.di_id = dxp->dx_id; dxp->dx_ptrid.di_attr = _dtrace_defattr; dxp->dx_ptrid.di_ops = &dt_idops_thaw; dxp->dx_ptrid.di_data = dxp; dxp->dx_ptrid.di_ctfp = ptr.dtt_ctfp; dxp->dx_ptrid.di_type = ptr.dtt_type; dxp->dx_ptrid.di_gen = dtp->dt_gen; /* * If a deferred pragma is pending on the keyword "translator", run all * the deferred pragmas on dx_souid and then copy results to dx_ptrid. * See the code in dt_pragma.c for details on deferred ident pragmas. */ if (dtp->dt_globals->dh_defer != NULL && yypcb->pcb_pragmas != NULL && dt_idhash_lookup(yypcb->pcb_pragmas, "translator") != NULL) { dtp->dt_globals->dh_defer(dtp->dt_globals, &dxp->dx_souid); dxp->dx_ptrid.di_attr = dxp->dx_souid.di_attr; dxp->dx_ptrid.di_vers = dxp->dx_souid.di_vers; } dxp->dx_src_ctfp = src->dtt_ctfp; dxp->dx_src_type = src->dtt_type; dxp->dx_src_base = ctf_type_resolve(src->dtt_ctfp, src->dtt_type); dxp->dx_dst_ctfp = dst->dtt_ctfp; dxp->dx_dst_type = dst->dtt_type; dxp->dx_dst_base = ctf_type_resolve(dst->dtt_ctfp, dst->dtt_type); kind = ctf_type_kind(dst->dtt_ctfp, dxp->dx_dst_base); assert(kind == CTF_K_STRUCT || kind == CTF_K_UNION); /* * If no input parameter is given, we're making a dynamic translator: * create member nodes for every member of the output type. Otherwise * retain the member and allocation node lists presented by the parser. */ if (name == NULL) { if (ctf_member_iter(dxp->dx_dst_ctfp, dxp->dx_dst_base, dt_xlator_create_member, dxp) != 0) goto err; } else { dxp->dx_members = members; dxp->dx_nodes = nodes; } /* * Assign member IDs to each member and allocate space for DIFOs * if and when this translator is eventually compiled. */ for (dnp = dxp->dx_members; dnp != NULL; dnp = dnp->dn_list) { dnp->dn_membxlator = dxp; dnp->dn_membid = dxp->dx_nmembers++; } dxp->dx_membdif = dt_zalloc(dtp, sizeof (dtrace_difo_t *) * dxp->dx_nmembers); if (dxp->dx_membdif == NULL) { dxp->dx_nmembers = 0; goto err; } return (dxp); err: dt_xlator_destroy(dtp, dxp); return (NULL); } void dt_xlator_destroy(dtrace_hdl_t *dtp, dt_xlator_t *dxp) { uint_t i; dt_node_link_free(&dxp->dx_nodes); if (dxp->dx_locals != NULL) dt_idhash_destroy(dxp->dx_locals); else if (dxp->dx_ident != NULL) dt_ident_destroy(dxp->dx_ident); for (i = 0; i < dxp->dx_nmembers; i++) dt_difo_free(dtp, dxp->dx_membdif[i]); dt_free(dtp, dxp->dx_membdif); dt_list_delete(&dtp->dt_xlators, dxp); dt_free(dtp, dxp); } dt_xlator_t * dt_xlator_lookup(dtrace_hdl_t *dtp, dt_node_t *src, dt_node_t *dst, int flags) { ctf_file_t *src_ctfp = src->dn_ctfp; ctf_id_t src_type = src->dn_type; ctf_id_t src_base = ctf_type_resolve(src_ctfp, src_type); ctf_file_t *dst_ctfp = dst->dn_ctfp; ctf_id_t dst_type = dst->dn_type; ctf_id_t dst_base = ctf_type_resolve(dst_ctfp, dst_type); uint_t dst_kind = ctf_type_kind(dst_ctfp, dst_base); int ptr = dst_kind == CTF_K_POINTER; dtrace_typeinfo_t src_dtt, dst_dtt; dt_node_t xn = { 0 }; dt_xlator_t *dxp = NULL; if (src_base == CTF_ERR || dst_base == CTF_ERR) return (NULL); /* fail if these are unresolvable types */ /* * Translators are always defined using a struct or union type, so if * we are attempting to translate to type "T *", we internally look * for a translation to type "T" by following the pointer reference. */ if (ptr) { dst_type = ctf_type_reference(dst_ctfp, dst_type); dst_base = ctf_type_resolve(dst_ctfp, dst_type); dst_kind = ctf_type_kind(dst_ctfp, dst_base); } if (dst_kind != CTF_K_UNION && dst_kind != CTF_K_STRUCT) return (NULL); /* fail if the output isn't a struct or union */ /* * In order to find a matching translator, we iterate over the set of * available translators in three passes. First, we look for a * translation from the exact source type to the resolved destination. * Second, we look for a translation from the resolved source type to * the resolved destination. Third, we look for a translation from a * compatible source type (using the same rules as parameter formals) * to the resolved destination. If all passes fail, return NULL. */ for (dxp = dt_list_next(&dtp->dt_xlators); dxp != NULL; dxp = dt_list_next(dxp)) { if (ctf_type_compat(dxp->dx_src_ctfp, dxp->dx_src_type, src_ctfp, src_type) && ctf_type_compat(dxp->dx_dst_ctfp, dxp->dx_dst_base, dst_ctfp, dst_base)) goto out; } if (flags & DT_XLATE_EXACT) goto out; /* skip remaining passes if exact match required */ for (dxp = dt_list_next(&dtp->dt_xlators); dxp != NULL; dxp = dt_list_next(dxp)) { if (ctf_type_compat(dxp->dx_src_ctfp, dxp->dx_src_base, src_ctfp, src_type) && ctf_type_compat(dxp->dx_dst_ctfp, dxp->dx_dst_base, dst_ctfp, dst_base)) goto out; } for (dxp = dt_list_next(&dtp->dt_xlators); dxp != NULL; dxp = dt_list_next(dxp)) { - dt_node_type_assign(&xn, dxp->dx_src_ctfp, dxp->dx_src_type); + dt_node_type_assign(&xn, dxp->dx_src_ctfp, dxp->dx_src_type, + B_FALSE); if (ctf_type_compat(dxp->dx_dst_ctfp, dxp->dx_dst_base, dst_ctfp, dst_base) && dt_node_is_argcompat(src, &xn)) goto out; } out: if (ptr && dxp != NULL && dxp->dx_ptrid.di_type == CTF_ERR) return (NULL); /* no translation available to pointer type */ if (dxp != NULL || !(flags & DT_XLATE_EXTERN) || dtp->dt_xlatemode == DT_XL_STATIC) return (dxp); /* we succeeded or not allowed to extern */ /* * If we get here, then we didn't find an existing translator, but the * caller and xlatemode permit us to create an extern to a dynamic one. */ src_dtt.dtt_object = dt_module_lookup_by_ctf(dtp, src_ctfp)->dm_name; src_dtt.dtt_ctfp = src_ctfp; src_dtt.dtt_type = src_type; dst_dtt.dtt_object = dt_module_lookup_by_ctf(dtp, dst_ctfp)->dm_name; dst_dtt.dtt_ctfp = dst_ctfp; dst_dtt.dtt_type = dst_type; return (dt_xlator_create(dtp, &src_dtt, &dst_dtt, NULL, NULL, NULL)); } dt_xlator_t * dt_xlator_lookup_id(dtrace_hdl_t *dtp, id_t id) { assert(id >= 0 && id < dtp->dt_xlatorid); return (dtp->dt_xlatormap[id]); } dt_ident_t * dt_xlator_ident(dt_xlator_t *dxp, ctf_file_t *ctfp, ctf_id_t type) { if (ctf_type_kind(ctfp, ctf_type_resolve(ctfp, type)) == CTF_K_POINTER) return (&dxp->dx_ptrid); else return (&dxp->dx_souid); } dt_node_t * dt_xlator_member(dt_xlator_t *dxp, const char *name) { dt_node_t *dnp; for (dnp = dxp->dx_members; dnp != NULL; dnp = dnp->dn_list) { if (strcmp(dnp->dn_membname, name) == 0) return (dnp); } return (NULL); } int dt_xlator_dynamic(const dt_xlator_t *dxp) { return (dxp->dx_locals == NULL); } Index: stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h =================================================================== --- stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h (revision 268577) +++ stable/10/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h (revision 268578) @@ -1,599 +1,613 @@ /* * 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) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #ifndef _DTRACE_H #define _DTRACE_H #include #include #include #include #include #if !defined(sun) #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; 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 /* Intepret 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_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 */ 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 *); /* * 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 { #if defined(sun) 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 #if !defined(sun) #define _SC_CPUID_MAX _SC_NPROCESSORS_CONF #define _SC_NPROCESSORS_MAX _SC_NPROCESSORS_CONF #endif #endif /* _DTRACE_H */ Index: stable/10/cddl/lib/libdtrace/libproc_compat.h =================================================================== --- stable/10/cddl/lib/libdtrace/libproc_compat.h (revision 268577) +++ stable/10/cddl/lib/libdtrace/libproc_compat.h (revision 268578) @@ -1,62 +1,64 @@ /* * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Compatibility functions between Solaris libproc and FreeBSD libproc. * Functions sorted alphabetically. */ #define PR_LMID_EVERY 0 #define Psetrun(p, a1, a2) proc_continue((p)) #define Pxlookup_by_addr(p, a, n, s, sym, i) \ proc_addr2sym(p, a, n, s, sym) #define Pxlookup_by_name(p, l, s1, s2, sym, a) \ proc_name2sym((p), (s1), (s2), (sym)) #define Paddr_to_map proc_addr2map #define Pcreate_error strerror #define Pdelbkpt proc_bkptdel #define Pgrab_error strerror #define Plmid_to_map(p, l, o) proc_obj2map((p), (o)) #define Plookup_by_addr proc_addr2sym +#define Pname_to_ctf(p, obj) NULL #define Pname_to_map proc_name2map #define Pobject_iter proc_iter_objs +#define Pobject_iter_resolved(p, f, arg) 1 #define Pobjname proc_objname #define Pread proc_read #define Prd_agent proc_rdagent #define Prelease proc_detach #define Psetbkpt proc_bkptset #define Psetflags proc_setflags #define Pstate proc_state #define Pstate proc_state #define Psymbol_iter_by_addr proc_iter_symbyaddr #define Punsetflags proc_clearflags #define Pupdate_maps(p) do { } while (0) #define Pupdate_syms proc_updatesyms #define Pxecbkpt proc_bkptexec Index: stable/10/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c =================================================================== --- stable/10/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c (revision 268577) +++ stable/10/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c (revision 268578) @@ -1,16983 +1,17924 @@ /* * 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 * * $FreeBSD$ */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ /* * DTrace - Dynamic Tracing for Solaris * * This is the implementation of the Solaris Dynamic Tracing framework * (DTrace). The user-visible interface to DTrace is described at length in * the "Solaris Dynamic Tracing Guide". The interfaces between the libdtrace * library, the in-kernel DTrace framework, and the DTrace providers are * described in the block comments in the header file. The * internal architecture of DTrace is described in the block comments in the * header file. The comments contained within the DTrace * implementation very much assume mastery of all of these sources; if one has * an unanswered question about the implementation, one should consult them * first. * * The functions here are ordered roughly as follows: * * - Probe context functions * - Probe hashing functions * - Non-probe context utility functions * - Matching functions * - Provider-to-Framework API functions * - Probe management functions * - DIF object functions * - Format functions * - Predicate functions * - ECB functions * - Buffer functions * - Enabling functions * - DOF functions * - Anonymous enabling functions * - Consumer state functions * - Helper functions * - Hook functions * - Driver cookbook functions * * Each group of functions begins with a block comment labelled the "DTrace * [Group] Functions", allowing one to find each block by searching forward * on capital-f functions. */ #include #if !defined(sun) #include #endif #include #include #include #include #if defined(sun) #include #include #endif #include #include #if defined(sun) #include #endif #include #include #include #include #if defined(sun) #include #include #endif #include #if defined(sun) #include #include #endif #include #if defined(sun) #include #include #endif #include #if defined(sun) #include #include #endif #include #include #include +#include "strtolctype.h" /* FreeBSD includes: */ #if !defined(sun) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dtrace_cddl.h" #include "dtrace_debug.c" #endif /* * DTrace Tunable Variables * * The following variables may be tuned by adding a line to /etc/system that * includes both the name of the DTrace module ("dtrace") and the name of the * variable. For example: * * set dtrace:dtrace_destructive_disallow = 1 * * In general, the only variables that one should be tuning this way are those * that affect system-wide DTrace behavior, and for which the default behavior * is undesirable. Most of these variables are tunable on a per-consumer * basis using DTrace options, and need not be tuned on a system-wide basis. * When tuning these variables, avoid pathological values; while some attempt * is made to verify the integrity of these variables, they are not considered * part of the supported interface to DTrace, and they are therefore not * checked comprehensively. Further, these variables should not be tuned * dynamically via "mdb -kw" or other means; they should only be tuned via * /etc/system. */ int dtrace_destructive_disallow = 0; dtrace_optval_t dtrace_nonroot_maxsize = (16 * 1024 * 1024); size_t dtrace_difo_maxsize = (256 * 1024); dtrace_optval_t dtrace_dof_maxsize = (8 * 1024 * 1024); size_t dtrace_global_maxsize = (16 * 1024); size_t dtrace_actions_max = (16 * 1024); size_t dtrace_retain_max = 1024; dtrace_optval_t dtrace_helper_actions_max = 128; dtrace_optval_t dtrace_helper_providers_max = 32; dtrace_optval_t dtrace_dstate_defsize = (1 * 1024 * 1024); size_t dtrace_strsize_default = 256; dtrace_optval_t dtrace_cleanrate_default = 9900990; /* 101 hz */ dtrace_optval_t dtrace_cleanrate_min = 200000; /* 5000 hz */ dtrace_optval_t dtrace_cleanrate_max = (uint64_t)60 * NANOSEC; /* 1/minute */ dtrace_optval_t dtrace_aggrate_default = NANOSEC; /* 1 hz */ dtrace_optval_t dtrace_statusrate_default = NANOSEC; /* 1 hz */ dtrace_optval_t dtrace_statusrate_max = (hrtime_t)10 * NANOSEC; /* 6/minute */ dtrace_optval_t dtrace_switchrate_default = NANOSEC; /* 1 hz */ dtrace_optval_t dtrace_nspec_default = 1; dtrace_optval_t dtrace_specsize_default = 32 * 1024; dtrace_optval_t dtrace_stackframes_default = 20; dtrace_optval_t dtrace_ustackframes_default = 20; dtrace_optval_t dtrace_jstackframes_default = 50; dtrace_optval_t dtrace_jstackstrsize_default = 512; int dtrace_msgdsize_max = 128; hrtime_t dtrace_chill_max = 500 * (NANOSEC / MILLISEC); /* 500 ms */ hrtime_t dtrace_chill_interval = NANOSEC; /* 1000 ms */ int dtrace_devdepth_max = 32; int dtrace_err_verbose; hrtime_t dtrace_deadman_interval = NANOSEC; hrtime_t dtrace_deadman_timeout = (hrtime_t)10 * NANOSEC; hrtime_t dtrace_deadman_user = (hrtime_t)30 * NANOSEC; hrtime_t dtrace_unregister_defunct_reap = (hrtime_t)60 * NANOSEC; /* * DTrace External Variables * * As dtrace(7D) is a kernel module, any DTrace variables are obviously * available to DTrace consumers via the backtick (`) syntax. One of these, * dtrace_zero, is made deliberately so: it is provided as a source of * well-known, zero-filled memory. While this variable is not documented, * it is used by some translators as an implementation detail. */ const char dtrace_zero[256] = { 0 }; /* zero-filled memory */ /* * DTrace Internal Variables */ #if defined(sun) static dev_info_t *dtrace_devi; /* device info */ #endif #if defined(sun) static vmem_t *dtrace_arena; /* probe ID arena */ static vmem_t *dtrace_minor; /* minor number arena */ #else static taskq_t *dtrace_taskq; /* task queue */ static struct unrhdr *dtrace_arena; /* Probe ID number. */ #endif static dtrace_probe_t **dtrace_probes; /* array of all probes */ static int dtrace_nprobes; /* number of probes */ static dtrace_provider_t *dtrace_provider; /* provider list */ static dtrace_meta_t *dtrace_meta_pid; /* user-land meta provider */ static int dtrace_opens; /* number of opens */ static int dtrace_helpers; /* number of helpers */ +static int dtrace_getf; /* number of unpriv getf()s */ #if defined(sun) static void *dtrace_softstate; /* softstate pointer */ #endif static dtrace_hash_t *dtrace_bymod; /* probes hashed by module */ static dtrace_hash_t *dtrace_byfunc; /* probes hashed by function */ static dtrace_hash_t *dtrace_byname; /* probes hashed by name */ static dtrace_toxrange_t *dtrace_toxrange; /* toxic range array */ static int dtrace_toxranges; /* number of toxic ranges */ static int dtrace_toxranges_max; /* size of toxic range array */ static dtrace_anon_t dtrace_anon; /* anonymous enabling */ static kmem_cache_t *dtrace_state_cache; /* cache for dynamic state */ static uint64_t dtrace_vtime_references; /* number of vtimestamp refs */ static kthread_t *dtrace_panicked; /* panicking thread */ static dtrace_ecb_t *dtrace_ecb_create_cache; /* cached created ECB */ static dtrace_genid_t dtrace_probegen; /* current probe generation */ static dtrace_helpers_t *dtrace_deferred_pid; /* deferred helper list */ static dtrace_enabling_t *dtrace_retained; /* list of retained enablings */ static dtrace_genid_t dtrace_retained_gen; /* current retained enab gen */ static dtrace_dynvar_t dtrace_dynhash_sink; /* end of dynamic hash chains */ #if !defined(sun) static struct mtx dtrace_unr_mtx; MTX_SYSINIT(dtrace_unr_mtx, &dtrace_unr_mtx, "Unique resource identifier", MTX_DEF); int dtrace_in_probe; /* non-zero if executing a probe */ #if defined(__i386__) || defined(__amd64__) || defined(__mips__) || defined(__powerpc__) uintptr_t dtrace_in_probe_addr; /* Address of invop when already in probe */ #endif static eventhandler_tag dtrace_kld_load_tag; static eventhandler_tag dtrace_kld_unload_try_tag; #endif /* * DTrace Locking * DTrace is protected by three (relatively coarse-grained) locks: * * (1) dtrace_lock is required to manipulate essentially any DTrace state, * including enabling state, probes, ECBs, consumer state, helper state, * etc. Importantly, dtrace_lock is _not_ required when in probe context; * probe context is lock-free -- synchronization is handled via the * dtrace_sync() cross call mechanism. * * (2) dtrace_provider_lock is required when manipulating provider state, or * when provider state must be held constant. * * (3) dtrace_meta_lock is required when manipulating meta provider state, or * when meta provider state must be held constant. * * The lock ordering between these three locks is dtrace_meta_lock before * dtrace_provider_lock before dtrace_lock. (In particular, there are * several places where dtrace_provider_lock is held by the framework as it * calls into the providers -- which then call back into the framework, * grabbing dtrace_lock.) * * There are two other locks in the mix: mod_lock and cpu_lock. With respect * to dtrace_provider_lock and dtrace_lock, cpu_lock continues its historical * role as a coarse-grained lock; it is acquired before both of these locks. * With respect to dtrace_meta_lock, its behavior is stranger: cpu_lock must * be acquired _between_ dtrace_meta_lock and any other DTrace locks. * mod_lock is similar with respect to dtrace_provider_lock in that it must be * acquired _between_ dtrace_provider_lock and dtrace_lock. */ static kmutex_t dtrace_lock; /* probe state lock */ static kmutex_t dtrace_provider_lock; /* provider state lock */ static kmutex_t dtrace_meta_lock; /* meta-provider state lock */ #if !defined(sun) /* XXX FreeBSD hacks. */ #define cr_suid cr_svuid #define cr_sgid cr_svgid #define ipaddr_t in_addr_t #define mod_modname pathname #define vuprintf vprintf #define ttoproc(_a) ((_a)->td_proc) #define crgetzoneid(_a) 0 #define NCPU MAXCPU #define SNOCD 0 #define CPU_ON_INTR(_a) 0 #define PRIV_EFFECTIVE (1 << 0) #define PRIV_DTRACE_KERNEL (1 << 1) #define PRIV_DTRACE_PROC (1 << 2) #define PRIV_DTRACE_USER (1 << 3) #define PRIV_PROC_OWNER (1 << 4) #define PRIV_PROC_ZONE (1 << 5) #define PRIV_ALL ~0 SYSCTL_DECL(_debug_dtrace); SYSCTL_DECL(_kern_dtrace); #endif #if defined(sun) #define curcpu CPU->cpu_id #endif /* * DTrace Provider Variables * * These are the variables relating to DTrace as a provider (that is, the * provider of the BEGIN, END, and ERROR probes). */ static dtrace_pattr_t dtrace_provider_attr = { { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, }; static void dtrace_nullop(void) {} static dtrace_pops_t dtrace_provider_ops = { (void (*)(void *, dtrace_probedesc_t *))dtrace_nullop, (void (*)(void *, modctl_t *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, NULL, NULL, NULL, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop }; static dtrace_id_t dtrace_probeid_begin; /* special BEGIN probe */ static dtrace_id_t dtrace_probeid_end; /* special END probe */ dtrace_id_t dtrace_probeid_error; /* special ERROR probe */ /* * DTrace Helper Tracing Variables */ uint32_t dtrace_helptrace_next = 0; uint32_t dtrace_helptrace_nlocals; char *dtrace_helptrace_buffer; int dtrace_helptrace_bufsize = 512 * 1024; #ifdef DEBUG int dtrace_helptrace_enabled = 1; #else int dtrace_helptrace_enabled = 0; #endif /* * DTrace Error Hashing * * On DEBUG kernels, DTrace will track the errors that has seen in a hash * table. This is very useful for checking coverage of tests that are * expected to induce DIF or DOF processing errors, and may be useful for * debugging problems in the DIF code generator or in DOF generation . The * error hash may be examined with the ::dtrace_errhash MDB dcmd. */ #ifdef DEBUG static dtrace_errhash_t dtrace_errhash[DTRACE_ERRHASHSZ]; static const char *dtrace_errlast; static kthread_t *dtrace_errthread; static kmutex_t dtrace_errlock; #endif /* * DTrace Macros and Constants * * These are various macros that are useful in various spots in the * implementation, along with a few random constants that have no meaning * outside of the implementation. There is no real structure to this cpp * mishmash -- but is there ever? */ #define DTRACE_HASHSTR(hash, probe) \ dtrace_hash_str(*((char **)((uintptr_t)(probe) + (hash)->dth_stroffs))) #define DTRACE_HASHNEXT(hash, probe) \ (dtrace_probe_t **)((uintptr_t)(probe) + (hash)->dth_nextoffs) #define DTRACE_HASHPREV(hash, probe) \ (dtrace_probe_t **)((uintptr_t)(probe) + (hash)->dth_prevoffs) #define DTRACE_HASHEQ(hash, lhs, rhs) \ (strcmp(*((char **)((uintptr_t)(lhs) + (hash)->dth_stroffs)), \ *((char **)((uintptr_t)(rhs) + (hash)->dth_stroffs))) == 0) #define DTRACE_AGGHASHSIZE_SLEW 17 #define DTRACE_V4MAPPED_OFFSET (sizeof (uint32_t) * 3) /* * The key for a thread-local variable consists of the lower 61 bits of the * t_did, plus the 3 bits of the highest active interrupt above LOCK_LEVEL. * We add DIF_VARIABLE_MAX to t_did to assure that the thread key is never * equal to a variable identifier. This is necessary (but not sufficient) to * assure that global associative arrays never collide with thread-local * variables. To guarantee that they cannot collide, we must also define the * order for keying dynamic variables. That order is: * * [ key0 ] ... [ keyn ] [ variable-key ] [ tls-key ] * * Because the variable-key and the tls-key are in orthogonal spaces, there is * no way for a global variable key signature to match a thread-local key * signature. */ #if defined(sun) #define DTRACE_TLS_THRKEY(where) { \ uint_t intr = 0; \ uint_t actv = CPU->cpu_intr_actv >> (LOCK_LEVEL + 1); \ for (; actv; actv >>= 1) \ intr++; \ ASSERT(intr < (1 << 3)); \ (where) = ((curthread->t_did + DIF_VARIABLE_MAX) & \ (((uint64_t)1 << 61) - 1)) | ((uint64_t)intr << 61); \ } #else #define DTRACE_TLS_THRKEY(where) { \ solaris_cpu_t *_c = &solaris_cpu[curcpu]; \ uint_t intr = 0; \ uint_t actv = _c->cpu_intr_actv; \ for (; actv; actv >>= 1) \ intr++; \ ASSERT(intr < (1 << 3)); \ (where) = ((curthread->td_tid + DIF_VARIABLE_MAX) & \ (((uint64_t)1 << 61) - 1)) | ((uint64_t)intr << 61); \ } #endif #define DT_BSWAP_8(x) ((x) & 0xff) #define DT_BSWAP_16(x) ((DT_BSWAP_8(x) << 8) | DT_BSWAP_8((x) >> 8)) #define DT_BSWAP_32(x) ((DT_BSWAP_16(x) << 16) | DT_BSWAP_16((x) >> 16)) #define DT_BSWAP_64(x) ((DT_BSWAP_32(x) << 32) | DT_BSWAP_32((x) >> 32)) #define DT_MASK_LO 0x00000000FFFFFFFFULL #define DTRACE_STORE(type, tomax, offset, what) \ *((type *)((uintptr_t)(tomax) + (uintptr_t)offset)) = (type)(what); #ifndef __x86 #define DTRACE_ALIGNCHECK(addr, size, flags) \ if (addr & (size - 1)) { \ *flags |= CPU_DTRACE_BADALIGN; \ cpu_core[curcpu].cpuc_dtrace_illval = addr; \ return (0); \ } #else #define DTRACE_ALIGNCHECK(addr, size, flags) #endif /* * Test whether a range of memory starting at testaddr of size testsz falls * within the range of memory described by addr, sz. We take care to avoid * problems with overflow and underflow of the unsigned quantities, and * disallow all negative sizes. Ranges of size 0 are allowed. */ #define DTRACE_INRANGE(testaddr, testsz, baseaddr, basesz) \ - ((testaddr) - (baseaddr) < (basesz) && \ - (testaddr) + (testsz) - (baseaddr) <= (basesz) && \ + ((testaddr) - (uintptr_t)(baseaddr) < (basesz) && \ + (testaddr) + (testsz) - (uintptr_t)(baseaddr) <= (basesz) && \ (testaddr) + (testsz) >= (testaddr)) /* * Test whether alloc_sz bytes will fit in the scratch region. We isolate * alloc_sz on the righthand side of the comparison in order to avoid overflow * or underflow in the comparison with it. This is simpler than the INRANGE * check above, because we know that the dtms_scratch_ptr is valid in the * range. Allocations of size zero are allowed. */ #define DTRACE_INSCRATCH(mstate, alloc_sz) \ ((mstate)->dtms_scratch_base + (mstate)->dtms_scratch_size - \ (mstate)->dtms_scratch_ptr >= (alloc_sz)) #define DTRACE_LOADFUNC(bits) \ /*CSTYLED*/ \ uint##bits##_t \ dtrace_load##bits(uintptr_t addr) \ { \ size_t size = bits / NBBY; \ /*CSTYLED*/ \ uint##bits##_t rval; \ int i; \ volatile uint16_t *flags = (volatile uint16_t *) \ &cpu_core[curcpu].cpuc_dtrace_flags; \ \ DTRACE_ALIGNCHECK(addr, size, flags); \ \ for (i = 0; i < dtrace_toxranges; i++) { \ if (addr >= dtrace_toxrange[i].dtt_limit) \ continue; \ \ if (addr + size <= dtrace_toxrange[i].dtt_base) \ continue; \ \ /* \ * This address falls within a toxic region; return 0. \ */ \ *flags |= CPU_DTRACE_BADADDR; \ cpu_core[curcpu].cpuc_dtrace_illval = addr; \ return (0); \ } \ \ *flags |= CPU_DTRACE_NOFAULT; \ /*CSTYLED*/ \ rval = *((volatile uint##bits##_t *)addr); \ *flags &= ~CPU_DTRACE_NOFAULT; \ \ return (!(*flags & CPU_DTRACE_FAULT) ? rval : 0); \ } #ifdef _LP64 #define dtrace_loadptr dtrace_load64 #else #define dtrace_loadptr dtrace_load32 #endif #define DTRACE_DYNHASH_FREE 0 #define DTRACE_DYNHASH_SINK 1 #define DTRACE_DYNHASH_VALID 2 #define DTRACE_MATCH_NEXT 0 #define DTRACE_MATCH_DONE 1 #define DTRACE_ANCHORED(probe) ((probe)->dtpr_func[0] != '\0') #define DTRACE_STATE_ALIGN 64 #define DTRACE_FLAGS2FLT(flags) \ (((flags) & CPU_DTRACE_BADADDR) ? DTRACEFLT_BADADDR : \ ((flags) & CPU_DTRACE_ILLOP) ? DTRACEFLT_ILLOP : \ ((flags) & CPU_DTRACE_DIVZERO) ? DTRACEFLT_DIVZERO : \ ((flags) & CPU_DTRACE_KPRIV) ? DTRACEFLT_KPRIV : \ ((flags) & CPU_DTRACE_UPRIV) ? DTRACEFLT_UPRIV : \ ((flags) & CPU_DTRACE_TUPOFLOW) ? DTRACEFLT_TUPOFLOW : \ ((flags) & CPU_DTRACE_BADALIGN) ? DTRACEFLT_BADALIGN : \ ((flags) & CPU_DTRACE_NOSCRATCH) ? DTRACEFLT_NOSCRATCH : \ ((flags) & CPU_DTRACE_BADSTACK) ? DTRACEFLT_BADSTACK : \ DTRACEFLT_UNKNOWN) #define DTRACEACT_ISSTRING(act) \ ((act)->dta_kind == DTRACEACT_DIFEXPR && \ (act)->dta_difo->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) /* Function prototype definitions: */ static size_t dtrace_strlen(const char *, size_t); static dtrace_probe_t *dtrace_probe_lookup_id(dtrace_id_t id); static void dtrace_enabling_provide(dtrace_provider_t *); static int dtrace_enabling_match(dtrace_enabling_t *, int *); static void dtrace_enabling_matchall(void); static void dtrace_enabling_reap(void); static dtrace_state_t *dtrace_anon_grab(void); static uint64_t dtrace_helper(int, dtrace_mstate_t *, dtrace_state_t *, uint64_t, uint64_t); static dtrace_helpers_t *dtrace_helpers_create(proc_t *); static void dtrace_buffer_drop(dtrace_buffer_t *); static int dtrace_buffer_consumed(dtrace_buffer_t *, hrtime_t when); static intptr_t dtrace_buffer_reserve(dtrace_buffer_t *, size_t, size_t, dtrace_state_t *, dtrace_mstate_t *); static int dtrace_state_option(dtrace_state_t *, dtrace_optid_t, dtrace_optval_t); static int dtrace_ecb_create_enable(dtrace_probe_t *, void *); static void dtrace_helper_provider_destroy(dtrace_helper_provider_t *); uint16_t dtrace_load16(uintptr_t); uint32_t dtrace_load32(uintptr_t); uint64_t dtrace_load64(uintptr_t); uint8_t dtrace_load8(uintptr_t); void dtrace_dynvar_clean(dtrace_dstate_t *); dtrace_dynvar_t *dtrace_dynvar(dtrace_dstate_t *, uint_t, dtrace_key_t *, size_t, dtrace_dynvar_op_t, dtrace_mstate_t *, dtrace_vstate_t *); uintptr_t dtrace_dif_varstr(uintptr_t, dtrace_state_t *, dtrace_mstate_t *); +static int dtrace_priv_proc(dtrace_state_t *); +static void dtrace_getf_barrier(void); /* * DTrace Probe Context Functions * * These functions are called from probe context. Because probe context is * any context in which C may be called, arbitrarily locks may be held, * interrupts may be disabled, we may be in arbitrary dispatched state, etc. * As a result, functions called from probe context may only call other DTrace * support functions -- they may not interact at all with the system at large. * (Note that the ASSERT macro is made probe-context safe by redefining it in * terms of dtrace_assfail(), a probe-context safe function.) If arbitrary * loads are to be performed from probe context, they _must_ be in terms of * the safe dtrace_load*() variants. * * Some functions in this block are not actually called from probe context; * for these functions, there will be a comment above the function reading * "Note: not called from probe context." */ void dtrace_panic(const char *format, ...) { va_list alist; va_start(alist, format); dtrace_vpanic(format, alist); va_end(alist); } int dtrace_assfail(const char *a, const char *f, int l) { dtrace_panic("assertion failed: %s, file: %s, line: %d", a, f, l); /* * We just need something here that even the most clever compiler * cannot optimize away. */ return (a[(uintptr_t)f]); } /* * Atomically increment a specified error counter from probe context. */ static void dtrace_error(uint32_t *counter) { /* * Most counters stored to in probe context are per-CPU counters. * However, there are some error conditions that are sufficiently * arcane that they don't merit per-CPU storage. If these counters * are incremented concurrently on different CPUs, scalability will be * adversely affected -- but we don't expect them to be white-hot in a * correctly constructed enabling... */ uint32_t oval, nval; do { oval = *counter; if ((nval = oval + 1) == 0) { /* * If the counter would wrap, set it to 1 -- assuring * that the counter is never zero when we have seen * errors. (The counter must be 32-bits because we * aren't guaranteed a 64-bit compare&swap operation.) * To save this code both the infamy of being fingered * by a priggish news story and the indignity of being * the target of a neo-puritan witch trial, we're * carefully avoiding any colorful description of the * likelihood of this condition -- but suffice it to * say that it is only slightly more likely than the * overflow of predicate cache IDs, as discussed in * dtrace_predicate_create(). */ nval = 1; } } while (dtrace_cas32(counter, oval, nval) != oval); } /* * Use the DTRACE_LOADFUNC macro to define functions for each of loading a * uint8_t, a uint16_t, a uint32_t and a uint64_t. */ DTRACE_LOADFUNC(8) DTRACE_LOADFUNC(16) DTRACE_LOADFUNC(32) DTRACE_LOADFUNC(64) static int dtrace_inscratch(uintptr_t dest, size_t size, dtrace_mstate_t *mstate) { if (dest < mstate->dtms_scratch_base) return (0); if (dest + size < dest) return (0); if (dest + size > mstate->dtms_scratch_ptr) return (0); return (1); } static int dtrace_canstore_statvar(uint64_t addr, size_t sz, dtrace_statvar_t **svars, int nsvars) { int i; for (i = 0; i < nsvars; i++) { dtrace_statvar_t *svar = svars[i]; if (svar == NULL || svar->dtsv_size == 0) continue; if (DTRACE_INRANGE(addr, sz, svar->dtsv_data, svar->dtsv_size)) return (1); } return (0); } /* * Check to see if the address is within a memory region to which a store may * be issued. This includes the DTrace scratch areas, and any DTrace variable * region. The caller of dtrace_canstore() is responsible for performing any * alignment checks that are needed before stores are actually executed. */ static int dtrace_canstore(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { /* * First, check to see if the address is in scratch space... */ if (DTRACE_INRANGE(addr, sz, mstate->dtms_scratch_base, mstate->dtms_scratch_size)) return (1); /* * Now check to see if it's a dynamic variable. This check will pick * up both thread-local variables and any global dynamically-allocated * variables. */ - if (DTRACE_INRANGE(addr, sz, (uintptr_t)vstate->dtvs_dynvars.dtds_base, + if (DTRACE_INRANGE(addr, sz, vstate->dtvs_dynvars.dtds_base, vstate->dtvs_dynvars.dtds_size)) { dtrace_dstate_t *dstate = &vstate->dtvs_dynvars; uintptr_t base = (uintptr_t)dstate->dtds_base + (dstate->dtds_hashsize * sizeof (dtrace_dynhash_t)); uintptr_t chunkoffs; /* * Before we assume that we can store here, we need to make * sure that it isn't in our metadata -- storing to our * dynamic variable metadata would corrupt our state. For * the range to not include any dynamic variable metadata, * it must: * * (1) Start above the hash table that is at the base of * the dynamic variable space * * (2) Have a starting chunk offset that is beyond the * dtrace_dynvar_t that is at the base of every chunk * * (3) Not span a chunk boundary * */ if (addr < base) return (0); chunkoffs = (addr - base) % dstate->dtds_chunksize; if (chunkoffs < sizeof (dtrace_dynvar_t)) return (0); if (chunkoffs + sz > dstate->dtds_chunksize) return (0); return (1); } /* * Finally, check the static local and global variables. These checks * take the longest, so we perform them last. */ if (dtrace_canstore_statvar(addr, sz, vstate->dtvs_locals, vstate->dtvs_nlocals)) return (1); if (dtrace_canstore_statvar(addr, sz, vstate->dtvs_globals, vstate->dtvs_nglobals)) return (1); return (0); } /* * Convenience routine to check to see if the address is within a memory * region in which a load may be issued given the user's privilege level; * if not, it sets the appropriate error flags and loads 'addr' into the * illegal value slot. * * DTrace subroutines (DIF_SUBR_*) should use this helper to implement * appropriate memory access protection. */ static int dtrace_canload(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval; + file_t *fp; /* * If we hold the privilege to read from kernel memory, then * everything is readable. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) return (1); /* * You can obviously read that which you can store. */ if (dtrace_canstore(addr, sz, mstate, vstate)) return (1); /* * We're allowed to read from our own string table. */ - if (DTRACE_INRANGE(addr, sz, (uintptr_t)mstate->dtms_difo->dtdo_strtab, + if (DTRACE_INRANGE(addr, sz, mstate->dtms_difo->dtdo_strtab, mstate->dtms_difo->dtdo_strlen)) return (1); + if (vstate->dtvs_state != NULL && + dtrace_priv_proc(vstate->dtvs_state)) { + proc_t *p; + + /* + * When we have privileges to the current process, there are + * several context-related kernel structures that are safe to + * read, even absent the privilege to read from kernel memory. + * These reads are safe because these structures contain only + * state that (1) we're permitted to read, (2) is harmless or + * (3) contains pointers to additional kernel state that we're + * not permitted to read (and as such, do not present an + * opportunity for privilege escalation). Finally (and + * critically), because of the nature of their relation with + * the current thread context, the memory associated with these + * structures cannot change over the duration of probe context, + * and it is therefore impossible for this memory to be + * deallocated and reallocated as something else while it's + * being operated upon. + */ + if (DTRACE_INRANGE(addr, sz, curthread, sizeof (kthread_t))) + return (1); + + if ((p = curthread->t_procp) != NULL && DTRACE_INRANGE(addr, + sz, curthread->t_procp, sizeof (proc_t))) { + return (1); + } + + if (curthread->t_cred != NULL && DTRACE_INRANGE(addr, sz, + curthread->t_cred, sizeof (cred_t))) { + return (1); + } + +#if defined(sun) + if (p != NULL && p->p_pidp != NULL && DTRACE_INRANGE(addr, sz, + &(p->p_pidp->pid_id), sizeof (pid_t))) { + return (1); + } + + if (curthread->t_cpu != NULL && DTRACE_INRANGE(addr, sz, + curthread->t_cpu, offsetof(cpu_t, cpu_pause_thread))) { + return (1); + } +#endif + } + + if ((fp = mstate->dtms_getf) != NULL) { + uintptr_t psz = sizeof (void *); + vnode_t *vp; + vnodeops_t *op; + + /* + * When getf() returns a file_t, the enabling is implicitly + * granted the (transient) right to read the returned file_t + * as well as the v_path and v_op->vnop_name of the underlying + * vnode. These accesses are allowed after a successful + * getf() because the members that they refer to cannot change + * once set -- and the barrier logic in the kernel's closef() + * path assures that the file_t and its referenced vode_t + * cannot themselves be stale (that is, it impossible for + * either dtms_getf itself or its f_vnode member to reference + * freed memory). + */ + if (DTRACE_INRANGE(addr, sz, fp, sizeof (file_t))) + return (1); + + if ((vp = fp->f_vnode) != NULL) { +#if defined(sun) + if (DTRACE_INRANGE(addr, sz, &vp->v_path, psz)) + return (1); + if (vp->v_path != NULL && DTRACE_INRANGE(addr, sz, + vp->v_path, strlen(vp->v_path) + 1)) { + return (1); + } +#endif + + if (DTRACE_INRANGE(addr, sz, &vp->v_op, psz)) + return (1); + +#if defined(sun) + if ((op = vp->v_op) != NULL && + DTRACE_INRANGE(addr, sz, &op->vnop_name, psz)) { + return (1); + } + + if (op != NULL && op->vnop_name != NULL && + DTRACE_INRANGE(addr, sz, op->vnop_name, + strlen(op->vnop_name) + 1)) { + return (1); + } +#endif + } + } + DTRACE_CPUFLAG_SET(CPU_DTRACE_KPRIV); *illval = addr; return (0); } /* * Convenience routine to check to see if a given string is within a memory * region in which a load may be issued given the user's privilege level; * this exists so that we don't need to issue unnecessary dtrace_strlen() * calls in the event that the user has all privileges. */ static int dtrace_strcanload(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { size_t strsz; /* * If we hold the privilege to read from kernel memory, then * everything is readable. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) return (1); strsz = 1 + dtrace_strlen((char *)(uintptr_t)addr, sz); if (dtrace_canload(addr, strsz, mstate, vstate)) return (1); return (0); } /* * Convenience routine to check to see if a given variable is within a memory * region in which a load may be issued given the user's privilege level. */ static int dtrace_vcanload(void *src, dtrace_diftype_t *type, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { size_t sz; ASSERT(type->dtdt_flags & DIF_TF_BYREF); /* * If we hold the privilege to read from kernel memory, then * everything is readable. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) return (1); if (type->dtdt_kind == DIF_TYPE_STRING) sz = dtrace_strlen(src, vstate->dtvs_state->dts_options[DTRACEOPT_STRSIZE]) + 1; else sz = type->dtdt_size; return (dtrace_canload((uintptr_t)src, sz, mstate, vstate)); } /* + * Convert a string to a signed integer using safe loads. + * + * NOTE: This function uses various macros from strtolctype.h to manipulate + * digit values, etc -- these have all been checked to ensure they make + * no additional function calls. + */ +static int64_t +dtrace_strtoll(char *input, int base, size_t limit) +{ + uintptr_t pos = (uintptr_t)input; + int64_t val = 0; + int x; + boolean_t neg = B_FALSE; + char c, cc, ccc; + uintptr_t end = pos + limit; + + /* + * Consume any whitespace preceding digits. + */ + while ((c = dtrace_load8(pos)) == ' ' || c == '\t') + pos++; + + /* + * Handle an explicit sign if one is present. + */ + if (c == '-' || c == '+') { + if (c == '-') + neg = B_TRUE; + c = dtrace_load8(++pos); + } + + /* + * Check for an explicit hexadecimal prefix ("0x" or "0X") and skip it + * if present. + */ + if (base == 16 && c == '0' && ((cc = dtrace_load8(pos + 1)) == 'x' || + cc == 'X') && isxdigit(ccc = dtrace_load8(pos + 2))) { + pos += 2; + c = ccc; + } + + /* + * Read in contiguous digits until the first non-digit character. + */ + for (; pos < end && c != '\0' && lisalnum(c) && (x = DIGIT(c)) < base; + c = dtrace_load8(++pos)) + val = val * base + x; + + return (neg ? -val : val); +} + +/* * Compare two strings using safe loads. */ static int dtrace_strncmp(char *s1, char *s2, size_t limit) { uint8_t c1, c2; volatile uint16_t *flags; if (s1 == s2 || limit == 0) return (0); flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; do { if (s1 == NULL) { c1 = '\0'; } else { c1 = dtrace_load8((uintptr_t)s1++); } if (s2 == NULL) { c2 = '\0'; } else { c2 = dtrace_load8((uintptr_t)s2++); } if (c1 != c2) return (c1 - c2); } while (--limit && c1 != '\0' && !(*flags & CPU_DTRACE_FAULT)); return (0); } /* * Compute strlen(s) for a string using safe memory accesses. The additional * len parameter is used to specify a maximum length to ensure completion. */ static size_t dtrace_strlen(const char *s, size_t lim) { uint_t len; for (len = 0; len != lim; len++) { if (dtrace_load8((uintptr_t)s++) == '\0') break; } return (len); } /* * Check if an address falls within a toxic region. */ static int dtrace_istoxic(uintptr_t kaddr, size_t size) { uintptr_t taddr, tsize; int i; for (i = 0; i < dtrace_toxranges; i++) { taddr = dtrace_toxrange[i].dtt_base; tsize = dtrace_toxrange[i].dtt_limit - taddr; if (kaddr - taddr < tsize) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = kaddr; return (1); } if (taddr - kaddr < size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = taddr; return (1); } } return (0); } /* * Copy src to dst using safe memory accesses. The src is assumed to be unsafe * memory specified by the DIF program. The dst is assumed to be safe memory * that we can store to directly because it is managed by DTrace. As with * standard bcopy, overlapping copies are handled properly. */ static void dtrace_bcopy(const void *src, void *dst, size_t len) { if (len != 0) { uint8_t *s1 = dst; const uint8_t *s2 = src; if (s1 <= s2) { do { *s1++ = dtrace_load8((uintptr_t)s2++); } while (--len != 0); } else { s2 += len; s1 += len; do { *--s1 = dtrace_load8((uintptr_t)--s2); } while (--len != 0); } } } /* * Copy src to dst using safe memory accesses, up to either the specified * length, or the point that a nul byte is encountered. The src is assumed to * be unsafe memory specified by the DIF program. The dst is assumed to be * safe memory that we can store to directly because it is managed by DTrace. * Unlike dtrace_bcopy(), overlapping regions are not handled. */ static void dtrace_strcpy(const void *src, void *dst, size_t len) { if (len != 0) { uint8_t *s1 = dst, c; const uint8_t *s2 = src; do { *s1++ = c = dtrace_load8((uintptr_t)s2++); } while (--len != 0 && c != '\0'); } } /* * Copy src to dst, deriving the size and type from the specified (BYREF) * variable type. The src is assumed to be unsafe memory specified by the DIF * program. The dst is assumed to be DTrace variable memory that is of the * specified type; we assume that we can store to directly. */ static void dtrace_vcopy(void *src, void *dst, dtrace_diftype_t *type) { ASSERT(type->dtdt_flags & DIF_TF_BYREF); if (type->dtdt_kind == DIF_TYPE_STRING) { dtrace_strcpy(src, dst, type->dtdt_size); } else { dtrace_bcopy(src, dst, type->dtdt_size); } } /* * Compare s1 to s2 using safe memory accesses. The s1 data is assumed to be * unsafe memory specified by the DIF program. The s2 data is assumed to be * safe memory that we can access directly because it is managed by DTrace. */ static int dtrace_bcmp(const void *s1, const void *s2, size_t len) { volatile uint16_t *flags; flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; if (s1 == s2) return (0); if (s1 == NULL || s2 == NULL) return (1); if (s1 != s2 && len != 0) { const uint8_t *ps1 = s1; const uint8_t *ps2 = s2; do { if (dtrace_load8((uintptr_t)ps1++) != *ps2++) return (1); } while (--len != 0 && !(*flags & CPU_DTRACE_FAULT)); } return (0); } /* * Zero the specified region using a simple byte-by-byte loop. Note that this * is for safe DTrace-managed memory only. */ static void dtrace_bzero(void *dst, size_t len) { uchar_t *cp; for (cp = dst; len != 0; len--) *cp++ = 0; } static void dtrace_add_128(uint64_t *addend1, uint64_t *addend2, uint64_t *sum) { uint64_t result[2]; result[0] = addend1[0] + addend2[0]; result[1] = addend1[1] + addend2[1] + (result[0] < addend1[0] || result[0] < addend2[0] ? 1 : 0); sum[0] = result[0]; sum[1] = result[1]; } /* * Shift the 128-bit value in a by b. If b is positive, shift left. * If b is negative, shift right. */ static void dtrace_shift_128(uint64_t *a, int b) { uint64_t mask; if (b == 0) return; if (b < 0) { b = -b; if (b >= 64) { a[0] = a[1] >> (b - 64); a[1] = 0; } else { a[0] >>= b; mask = 1LL << (64 - b); mask -= 1; a[0] |= ((a[1] & mask) << (64 - b)); a[1] >>= b; } } else { if (b >= 64) { a[1] = a[0] << (b - 64); a[0] = 0; } else { a[1] <<= b; mask = a[0] >> (64 - b); a[1] |= mask; a[0] <<= b; } } } /* * The basic idea is to break the 2 64-bit values into 4 32-bit values, * use native multiplication on those, and then re-combine into the * resulting 128-bit value. * * (hi1 << 32 + lo1) * (hi2 << 32 + lo2) = * hi1 * hi2 << 64 + * hi1 * lo2 << 32 + * hi2 * lo1 << 32 + * lo1 * lo2 */ static void dtrace_multiply_128(uint64_t factor1, uint64_t factor2, uint64_t *product) { uint64_t hi1, hi2, lo1, lo2; uint64_t tmp[2]; hi1 = factor1 >> 32; hi2 = factor2 >> 32; lo1 = factor1 & DT_MASK_LO; lo2 = factor2 & DT_MASK_LO; product[0] = lo1 * lo2; product[1] = hi1 * hi2; tmp[0] = hi1 * lo2; tmp[1] = 0; dtrace_shift_128(tmp, 32); dtrace_add_128(product, tmp, product); tmp[0] = hi2 * lo1; tmp[1] = 0; dtrace_shift_128(tmp, 32); dtrace_add_128(product, tmp, product); } /* * This privilege check should be used by actions and subroutines to * verify that the user credentials of the process that enabled the * invoking ECB match the target credentials */ static int dtrace_priv_proc_common_user(dtrace_state_t *state) { cred_t *cr, *s_cr = state->dts_cred.dcr_cred; /* * We should always have a non-NULL state cred here, since if cred * is null (anonymous tracing), we fast-path bypass this routine. */ ASSERT(s_cr != NULL); if ((cr = CRED()) != NULL && s_cr->cr_uid == cr->cr_uid && s_cr->cr_uid == cr->cr_ruid && s_cr->cr_uid == cr->cr_suid && s_cr->cr_gid == cr->cr_gid && s_cr->cr_gid == cr->cr_rgid && s_cr->cr_gid == cr->cr_sgid) return (1); return (0); } /* * This privilege check should be used by actions and subroutines to * verify that the zone of the process that enabled the invoking ECB * matches the target credentials */ static int dtrace_priv_proc_common_zone(dtrace_state_t *state) { #if defined(sun) cred_t *cr, *s_cr = state->dts_cred.dcr_cred; /* * We should always have a non-NULL state cred here, since if cred * is null (anonymous tracing), we fast-path bypass this routine. */ ASSERT(s_cr != NULL); - if ((cr = CRED()) != NULL && - s_cr->cr_zone == cr->cr_zone) + if ((cr = CRED()) != NULL && s_cr->cr_zone == cr->cr_zone) return (1); return (0); #else return (1); #endif } /* * This privilege check should be used by actions and subroutines to * verify that the process has not setuid or changed credentials. */ static int dtrace_priv_proc_common_nocd(void) { proc_t *proc; if ((proc = ttoproc(curthread)) != NULL && !(proc->p_flag & SNOCD)) return (1); return (0); } static int dtrace_priv_proc_destructive(dtrace_state_t *state) { int action = state->dts_cred.dcr_action; if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE) == 0) && dtrace_priv_proc_common_zone(state) == 0) goto bad; if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER) == 0) && dtrace_priv_proc_common_user(state) == 0) goto bad; if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG) == 0) && dtrace_priv_proc_common_nocd() == 0) goto bad; return (1); bad: cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV; return (0); } static int dtrace_priv_proc_control(dtrace_state_t *state) { if (state->dts_cred.dcr_action & DTRACE_CRA_PROC_CONTROL) return (1); if (dtrace_priv_proc_common_zone(state) && dtrace_priv_proc_common_user(state) && dtrace_priv_proc_common_nocd()) return (1); cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV; return (0); } static int dtrace_priv_proc(dtrace_state_t *state) { if (state->dts_cred.dcr_action & DTRACE_CRA_PROC) return (1); cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV; return (0); } static int dtrace_priv_kernel(dtrace_state_t *state) { if (state->dts_cred.dcr_action & DTRACE_CRA_KERNEL) return (1); cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_KPRIV; return (0); } static int dtrace_priv_kernel_destructive(dtrace_state_t *state) { if (state->dts_cred.dcr_action & DTRACE_CRA_KERNEL_DESTRUCTIVE) return (1); cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_KPRIV; return (0); } /* + * Determine if the dte_cond of the specified ECB allows for processing of + * the current probe to continue. Note that this routine may allow continued + * processing, but with access(es) stripped from the mstate's dtms_access + * field. + */ +static int +dtrace_priv_probe(dtrace_state_t *state, dtrace_mstate_t *mstate, + dtrace_ecb_t *ecb) +{ + dtrace_probe_t *probe = ecb->dte_probe; + dtrace_provider_t *prov = probe->dtpr_provider; + dtrace_pops_t *pops = &prov->dtpv_pops; + int mode = DTRACE_MODE_NOPRIV_DROP; + + ASSERT(ecb->dte_cond); + +#if defined(sun) + if (pops->dtps_mode != NULL) { + mode = pops->dtps_mode(prov->dtpv_arg, + probe->dtpr_id, probe->dtpr_arg); + + ASSERT((mode & DTRACE_MODE_USER) || + (mode & DTRACE_MODE_KERNEL)); + ASSERT((mode & DTRACE_MODE_NOPRIV_RESTRICT) || + (mode & DTRACE_MODE_NOPRIV_DROP)); + } + + /* + * If the dte_cond bits indicate that this consumer is only allowed to + * see user-mode firings of this probe, call the provider's dtps_mode() + * entry point to check that the probe was fired while in a user + * context. If that's not the case, use the policy specified by the + * provider to determine if we drop the probe or merely restrict + * operation. + */ + if (ecb->dte_cond & DTRACE_COND_USERMODE) { + ASSERT(mode != DTRACE_MODE_NOPRIV_DROP); + + if (!(mode & DTRACE_MODE_USER)) { + if (mode & DTRACE_MODE_NOPRIV_DROP) + return (0); + + mstate->dtms_access &= ~DTRACE_ACCESS_ARGS; + } + } +#endif + + /* + * This is more subtle than it looks. We have to be absolutely certain + * that CRED() isn't going to change out from under us so it's only + * legit to examine that structure if we're in constrained situations. + * Currently, the only times we'll this check is if a non-super-user + * has enabled the profile or syscall providers -- providers that + * allow visibility of all processes. For the profile case, the check + * above will ensure that we're examining a user context. + */ + if (ecb->dte_cond & DTRACE_COND_OWNER) { + cred_t *cr; + cred_t *s_cr = state->dts_cred.dcr_cred; + proc_t *proc; + + ASSERT(s_cr != NULL); + + if ((cr = CRED()) == NULL || + s_cr->cr_uid != cr->cr_uid || + s_cr->cr_uid != cr->cr_ruid || + s_cr->cr_uid != cr->cr_suid || + s_cr->cr_gid != cr->cr_gid || + s_cr->cr_gid != cr->cr_rgid || + s_cr->cr_gid != cr->cr_sgid || + (proc = ttoproc(curthread)) == NULL || + (proc->p_flag & SNOCD)) { + if (mode & DTRACE_MODE_NOPRIV_DROP) + return (0); + +#if defined(sun) + mstate->dtms_access &= ~DTRACE_ACCESS_PROC; +#endif + } + } + +#if defined(sun) + /* + * If our dte_cond is set to DTRACE_COND_ZONEOWNER and we are not + * in our zone, check to see if our mode policy is to restrict rather + * than to drop; if to restrict, strip away both DTRACE_ACCESS_PROC + * and DTRACE_ACCESS_ARGS + */ + if (ecb->dte_cond & DTRACE_COND_ZONEOWNER) { + cred_t *cr; + cred_t *s_cr = state->dts_cred.dcr_cred; + + ASSERT(s_cr != NULL); + + if ((cr = CRED()) == NULL || + s_cr->cr_zone->zone_id != cr->cr_zone->zone_id) { + if (mode & DTRACE_MODE_NOPRIV_DROP) + return (0); + + mstate->dtms_access &= + ~(DTRACE_ACCESS_PROC | DTRACE_ACCESS_ARGS); + } + } +#endif + + return (1); +} + +/* * Note: not called from probe context. This function is called * asynchronously (and at a regular interval) from outside of probe context to * clean the dirty dynamic variable lists on all CPUs. Dynamic variable * cleaning is explained in detail in . */ void dtrace_dynvar_clean(dtrace_dstate_t *dstate) { dtrace_dynvar_t *dirty; dtrace_dstate_percpu_t *dcpu; int i, work = 0; for (i = 0; i < NCPU; i++) { dcpu = &dstate->dtds_percpu[i]; ASSERT(dcpu->dtdsc_rinsing == NULL); /* * If the dirty list is NULL, there is no dirty work to do. */ if (dcpu->dtdsc_dirty == NULL) continue; /* * If the clean list is non-NULL, then we're not going to do * any work for this CPU -- it means that there has not been * a dtrace_dynvar() allocation on this CPU (or from this CPU) * since the last time we cleaned house. */ if (dcpu->dtdsc_clean != NULL) continue; work = 1; /* * Atomically move the dirty list aside. */ do { dirty = dcpu->dtdsc_dirty; /* * Before we zap the dirty list, set the rinsing list. * (This allows for a potential assertion in * dtrace_dynvar(): if a free dynamic variable appears * on a hash chain, either the dirty list or the * rinsing list for some CPU must be non-NULL.) */ dcpu->dtdsc_rinsing = dirty; dtrace_membar_producer(); } while (dtrace_casptr(&dcpu->dtdsc_dirty, dirty, NULL) != dirty); } if (!work) { /* * We have no work to do; we can simply return. */ return; } dtrace_sync(); for (i = 0; i < NCPU; i++) { dcpu = &dstate->dtds_percpu[i]; if (dcpu->dtdsc_rinsing == NULL) continue; /* * We are now guaranteed that no hash chain contains a pointer * into this dirty list; we can make it clean. */ ASSERT(dcpu->dtdsc_clean == NULL); dcpu->dtdsc_clean = dcpu->dtdsc_rinsing; dcpu->dtdsc_rinsing = NULL; } /* * Before we actually set the state to be DTRACE_DSTATE_CLEAN, make * sure that all CPUs have seen all of the dtdsc_clean pointers. * This prevents a race whereby a CPU incorrectly decides that * the state should be something other than DTRACE_DSTATE_CLEAN * after dtrace_dynvar_clean() has completed. */ dtrace_sync(); dstate->dtds_state = DTRACE_DSTATE_CLEAN; } /* * Depending on the value of the op parameter, this function looks-up, * allocates or deallocates an arbitrarily-keyed dynamic variable. If an * allocation is requested, this function will return a pointer to a * dtrace_dynvar_t corresponding to the allocated variable -- or NULL if no * variable can be allocated. If NULL is returned, the appropriate counter * will be incremented. */ dtrace_dynvar_t * dtrace_dynvar(dtrace_dstate_t *dstate, uint_t nkeys, dtrace_key_t *key, size_t dsize, dtrace_dynvar_op_t op, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { uint64_t hashval = DTRACE_DYNHASH_VALID; dtrace_dynhash_t *hash = dstate->dtds_hash; dtrace_dynvar_t *free, *new_free, *next, *dvar, *start, *prev = NULL; processorid_t me = curcpu, cpu = me; dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[me]; size_t bucket, ksize; size_t chunksize = dstate->dtds_chunksize; uintptr_t kdata, lock, nstate; uint_t i; ASSERT(nkeys != 0); /* * Hash the key. As with aggregations, we use Jenkins' "One-at-a-time" * algorithm. For the by-value portions, we perform the algorithm in * 16-bit chunks (as opposed to 8-bit chunks). This speeds things up a * bit, and seems to have only a minute effect on distribution. For * the by-reference data, we perform "One-at-a-time" iterating (safely) * over each referenced byte. It's painful to do this, but it's much * better than pathological hash distribution. The efficacy of the * hashing algorithm (and a comparison with other algorithms) may be * found by running the ::dtrace_dynstat MDB dcmd. */ for (i = 0; i < nkeys; i++) { if (key[i].dttk_size == 0) { uint64_t val = key[i].dttk_value; hashval += (val >> 48) & 0xffff; hashval += (hashval << 10); hashval ^= (hashval >> 6); hashval += (val >> 32) & 0xffff; hashval += (hashval << 10); hashval ^= (hashval >> 6); hashval += (val >> 16) & 0xffff; hashval += (hashval << 10); hashval ^= (hashval >> 6); hashval += val & 0xffff; hashval += (hashval << 10); hashval ^= (hashval >> 6); } else { /* * This is incredibly painful, but it beats the hell * out of the alternative. */ uint64_t j, size = key[i].dttk_size; uintptr_t base = (uintptr_t)key[i].dttk_value; if (!dtrace_canload(base, size, mstate, vstate)) break; for (j = 0; j < size; j++) { hashval += dtrace_load8(base + j); hashval += (hashval << 10); hashval ^= (hashval >> 6); } } } if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT)) return (NULL); hashval += (hashval << 3); hashval ^= (hashval >> 11); hashval += (hashval << 15); /* * There is a remote chance (ideally, 1 in 2^31) that our hashval * comes out to be one of our two sentinel hash values. If this * actually happens, we set the hashval to be a value known to be a * non-sentinel value. */ if (hashval == DTRACE_DYNHASH_FREE || hashval == DTRACE_DYNHASH_SINK) hashval = DTRACE_DYNHASH_VALID; /* * Yes, it's painful to do a divide here. If the cycle count becomes * important here, tricks can be pulled to reduce it. (However, it's * critical that hash collisions be kept to an absolute minimum; * they're much more painful than a divide.) It's better to have a * solution that generates few collisions and still keeps things * relatively simple. */ bucket = hashval % dstate->dtds_hashsize; if (op == DTRACE_DYNVAR_DEALLOC) { volatile uintptr_t *lockp = &hash[bucket].dtdh_lock; for (;;) { while ((lock = *lockp) & 1) continue; if (dtrace_casptr((volatile void *)lockp, (volatile void *)lock, (volatile void *)(lock + 1)) == (void *)lock) break; } dtrace_membar_producer(); } top: prev = NULL; lock = hash[bucket].dtdh_lock; dtrace_membar_consumer(); start = hash[bucket].dtdh_chain; ASSERT(start != NULL && (start->dtdv_hashval == DTRACE_DYNHASH_SINK || start->dtdv_hashval != DTRACE_DYNHASH_FREE || op != DTRACE_DYNVAR_DEALLOC)); for (dvar = start; dvar != NULL; dvar = dvar->dtdv_next) { dtrace_tuple_t *dtuple = &dvar->dtdv_tuple; dtrace_key_t *dkey = &dtuple->dtt_key[0]; if (dvar->dtdv_hashval != hashval) { if (dvar->dtdv_hashval == DTRACE_DYNHASH_SINK) { /* * We've reached the sink, and therefore the * end of the hash chain; we can kick out of * the loop knowing that we have seen a valid * snapshot of state. */ ASSERT(dvar->dtdv_next == NULL); ASSERT(dvar == &dtrace_dynhash_sink); break; } if (dvar->dtdv_hashval == DTRACE_DYNHASH_FREE) { /* * We've gone off the rails: somewhere along * the line, one of the members of this hash * chain was deleted. Note that we could also * detect this by simply letting this loop run * to completion, as we would eventually hit * the end of the dirty list. However, we * want to avoid running the length of the * dirty list unnecessarily (it might be quite * long), so we catch this as early as * possible by detecting the hash marker. In * this case, we simply set dvar to NULL and * break; the conditional after the loop will * send us back to top. */ dvar = NULL; break; } goto next; } if (dtuple->dtt_nkeys != nkeys) goto next; for (i = 0; i < nkeys; i++, dkey++) { if (dkey->dttk_size != key[i].dttk_size) goto next; /* size or type mismatch */ if (dkey->dttk_size != 0) { if (dtrace_bcmp( (void *)(uintptr_t)key[i].dttk_value, (void *)(uintptr_t)dkey->dttk_value, dkey->dttk_size)) goto next; } else { if (dkey->dttk_value != key[i].dttk_value) goto next; } } if (op != DTRACE_DYNVAR_DEALLOC) return (dvar); ASSERT(dvar->dtdv_next == NULL || dvar->dtdv_next->dtdv_hashval != DTRACE_DYNHASH_FREE); if (prev != NULL) { ASSERT(hash[bucket].dtdh_chain != dvar); ASSERT(start != dvar); ASSERT(prev->dtdv_next == dvar); prev->dtdv_next = dvar->dtdv_next; } else { if (dtrace_casptr(&hash[bucket].dtdh_chain, start, dvar->dtdv_next) != start) { /* * We have failed to atomically swing the * hash table head pointer, presumably because * of a conflicting allocation on another CPU. * We need to reread the hash chain and try * again. */ goto top; } } dtrace_membar_producer(); /* * Now set the hash value to indicate that it's free. */ ASSERT(hash[bucket].dtdh_chain != dvar); dvar->dtdv_hashval = DTRACE_DYNHASH_FREE; dtrace_membar_producer(); /* * Set the next pointer to point at the dirty list, and * atomically swing the dirty pointer to the newly freed dvar. */ do { next = dcpu->dtdsc_dirty; dvar->dtdv_next = next; } while (dtrace_casptr(&dcpu->dtdsc_dirty, next, dvar) != next); /* * Finally, unlock this hash bucket. */ ASSERT(hash[bucket].dtdh_lock == lock); ASSERT(lock & 1); hash[bucket].dtdh_lock++; return (NULL); next: prev = dvar; continue; } if (dvar == NULL) { /* * If dvar is NULL, it is because we went off the rails: * one of the elements that we traversed in the hash chain * was deleted while we were traversing it. In this case, * we assert that we aren't doing a dealloc (deallocs lock * the hash bucket to prevent themselves from racing with * one another), and retry the hash chain traversal. */ ASSERT(op != DTRACE_DYNVAR_DEALLOC); goto top; } if (op != DTRACE_DYNVAR_ALLOC) { /* * If we are not to allocate a new variable, we want to * return NULL now. Before we return, check that the value * of the lock word hasn't changed. If it has, we may have * seen an inconsistent snapshot. */ if (op == DTRACE_DYNVAR_NOALLOC) { if (hash[bucket].dtdh_lock != lock) goto top; } else { ASSERT(op == DTRACE_DYNVAR_DEALLOC); ASSERT(hash[bucket].dtdh_lock == lock); ASSERT(lock & 1); hash[bucket].dtdh_lock++; } return (NULL); } /* * We need to allocate a new dynamic variable. The size we need is the * size of dtrace_dynvar plus the size of nkeys dtrace_key_t's plus the * size of any auxiliary key data (rounded up to 8-byte alignment) plus * the size of any referred-to data (dsize). We then round the final * size up to the chunksize for allocation. */ for (ksize = 0, i = 0; i < nkeys; i++) ksize += P2ROUNDUP(key[i].dttk_size, sizeof (uint64_t)); /* * This should be pretty much impossible, but could happen if, say, * strange DIF specified the tuple. Ideally, this should be an * assertion and not an error condition -- but that requires that the * chunksize calculation in dtrace_difo_chunksize() be absolutely * bullet-proof. (That is, it must not be able to be fooled by * malicious DIF.) Given the lack of backwards branches in DIF, * solving this would presumably not amount to solving the Halting * Problem -- but it still seems awfully hard. */ if (sizeof (dtrace_dynvar_t) + sizeof (dtrace_key_t) * (nkeys - 1) + ksize + dsize > chunksize) { dcpu->dtdsc_drops++; return (NULL); } nstate = DTRACE_DSTATE_EMPTY; do { retry: free = dcpu->dtdsc_free; if (free == NULL) { dtrace_dynvar_t *clean = dcpu->dtdsc_clean; void *rval; if (clean == NULL) { /* * We're out of dynamic variable space on * this CPU. Unless we have tried all CPUs, * we'll try to allocate from a different * CPU. */ switch (dstate->dtds_state) { case DTRACE_DSTATE_CLEAN: { void *sp = &dstate->dtds_state; if (++cpu >= NCPU) cpu = 0; if (dcpu->dtdsc_dirty != NULL && nstate == DTRACE_DSTATE_EMPTY) nstate = DTRACE_DSTATE_DIRTY; if (dcpu->dtdsc_rinsing != NULL) nstate = DTRACE_DSTATE_RINSING; dcpu = &dstate->dtds_percpu[cpu]; if (cpu != me) goto retry; (void) dtrace_cas32(sp, DTRACE_DSTATE_CLEAN, nstate); /* * To increment the correct bean * counter, take another lap. */ goto retry; } case DTRACE_DSTATE_DIRTY: dcpu->dtdsc_dirty_drops++; break; case DTRACE_DSTATE_RINSING: dcpu->dtdsc_rinsing_drops++; break; case DTRACE_DSTATE_EMPTY: dcpu->dtdsc_drops++; break; } DTRACE_CPUFLAG_SET(CPU_DTRACE_DROP); return (NULL); } /* * The clean list appears to be non-empty. We want to * move the clean list to the free list; we start by * moving the clean pointer aside. */ if (dtrace_casptr(&dcpu->dtdsc_clean, clean, NULL) != clean) { /* * We are in one of two situations: * * (a) The clean list was switched to the * free list by another CPU. * * (b) The clean list was added to by the * cleansing cyclic. * * In either of these situations, we can * just reattempt the free list allocation. */ goto retry; } ASSERT(clean->dtdv_hashval == DTRACE_DYNHASH_FREE); /* * Now we'll move the clean list to the free list. * It's impossible for this to fail: the only way * the free list can be updated is through this * code path, and only one CPU can own the clean list. * Thus, it would only be possible for this to fail if * this code were racing with dtrace_dynvar_clean(). * (That is, if dtrace_dynvar_clean() updated the clean * list, and we ended up racing to update the free * list.) This race is prevented by the dtrace_sync() * in dtrace_dynvar_clean() -- which flushes the * owners of the clean lists out before resetting * the clean lists. */ rval = dtrace_casptr(&dcpu->dtdsc_free, NULL, clean); ASSERT(rval == NULL); goto retry; } dvar = free; new_free = dvar->dtdv_next; } while (dtrace_casptr(&dcpu->dtdsc_free, free, new_free) != free); /* * We have now allocated a new chunk. We copy the tuple keys into the * tuple array and copy any referenced key data into the data space * following the tuple array. As we do this, we relocate dttk_value * in the final tuple to point to the key data address in the chunk. */ kdata = (uintptr_t)&dvar->dtdv_tuple.dtt_key[nkeys]; dvar->dtdv_data = (void *)(kdata + ksize); dvar->dtdv_tuple.dtt_nkeys = nkeys; for (i = 0; i < nkeys; i++) { dtrace_key_t *dkey = &dvar->dtdv_tuple.dtt_key[i]; size_t kesize = key[i].dttk_size; if (kesize != 0) { dtrace_bcopy( (const void *)(uintptr_t)key[i].dttk_value, (void *)kdata, kesize); dkey->dttk_value = kdata; kdata += P2ROUNDUP(kesize, sizeof (uint64_t)); } else { dkey->dttk_value = key[i].dttk_value; } dkey->dttk_size = kesize; } ASSERT(dvar->dtdv_hashval == DTRACE_DYNHASH_FREE); dvar->dtdv_hashval = hashval; dvar->dtdv_next = start; if (dtrace_casptr(&hash[bucket].dtdh_chain, start, dvar) == start) return (dvar); /* * The cas has failed. Either another CPU is adding an element to * this hash chain, or another CPU is deleting an element from this * hash chain. The simplest way to deal with both of these cases * (though not necessarily the most efficient) is to free our * allocated block and tail-call ourselves. Note that the free is * to the dirty list and _not_ to the free list. This is to prevent * races with allocators, above. */ dvar->dtdv_hashval = DTRACE_DYNHASH_FREE; dtrace_membar_producer(); do { free = dcpu->dtdsc_dirty; dvar->dtdv_next = free; } while (dtrace_casptr(&dcpu->dtdsc_dirty, free, dvar) != free); return (dtrace_dynvar(dstate, nkeys, key, dsize, op, mstate, vstate)); } /*ARGSUSED*/ static void dtrace_aggregate_min(uint64_t *oval, uint64_t nval, uint64_t arg) { if ((int64_t)nval < (int64_t)*oval) *oval = nval; } /*ARGSUSED*/ static void dtrace_aggregate_max(uint64_t *oval, uint64_t nval, uint64_t arg) { if ((int64_t)nval > (int64_t)*oval) *oval = nval; } static void dtrace_aggregate_quantize(uint64_t *quanta, uint64_t nval, uint64_t incr) { int i, zero = DTRACE_QUANTIZE_ZEROBUCKET; int64_t val = (int64_t)nval; if (val < 0) { for (i = 0; i < zero; i++) { if (val <= DTRACE_QUANTIZE_BUCKETVAL(i)) { quanta[i] += incr; return; } } } else { for (i = zero + 1; i < DTRACE_QUANTIZE_NBUCKETS; i++) { if (val < DTRACE_QUANTIZE_BUCKETVAL(i)) { quanta[i - 1] += incr; return; } } quanta[DTRACE_QUANTIZE_NBUCKETS - 1] += incr; return; } ASSERT(0); } static void dtrace_aggregate_lquantize(uint64_t *lquanta, uint64_t nval, uint64_t incr) { uint64_t arg = *lquanta++; int32_t base = DTRACE_LQUANTIZE_BASE(arg); uint16_t step = DTRACE_LQUANTIZE_STEP(arg); uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg); int32_t val = (int32_t)nval, level; ASSERT(step != 0); ASSERT(levels != 0); if (val < base) { /* * This is an underflow. */ lquanta[0] += incr; return; } level = (val - base) / step; if (level < levels) { lquanta[level + 1] += incr; return; } /* * This is an overflow. */ lquanta[levels + 1] += incr; } static int dtrace_aggregate_llquantize_bucket(uint16_t factor, uint16_t low, uint16_t high, uint16_t nsteps, int64_t value) { int64_t this = 1, last, next; int base = 1, order; ASSERT(factor <= nsteps); ASSERT(nsteps % factor == 0); for (order = 0; order < low; order++) this *= factor; /* * If our value is less than our factor taken to the power of the * low order of magnitude, it goes into the zeroth bucket. */ if (value < (last = this)) return (0); for (this *= factor; order <= high; order++) { int nbuckets = this > nsteps ? nsteps : this; if ((next = this * factor) < this) { /* * We should not generally get log/linear quantizations * with a high magnitude that allows 64-bits to * overflow, but we nonetheless protect against this * by explicitly checking for overflow, and clamping * our value accordingly. */ value = this - 1; } if (value < this) { /* * If our value lies within this order of magnitude, * determine its position by taking the offset within * the order of magnitude, dividing by the bucket * width, and adding to our (accumulated) base. */ return (base + (value - last) / (this / nbuckets)); } base += nbuckets - (nbuckets / factor); last = this; this = next; } /* * Our value is greater than or equal to our factor taken to the * power of one plus the high magnitude -- return the top bucket. */ return (base); } static void dtrace_aggregate_llquantize(uint64_t *llquanta, uint64_t nval, uint64_t incr) { uint64_t arg = *llquanta++; uint16_t factor = DTRACE_LLQUANTIZE_FACTOR(arg); uint16_t low = DTRACE_LLQUANTIZE_LOW(arg); uint16_t high = DTRACE_LLQUANTIZE_HIGH(arg); uint16_t nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); llquanta[dtrace_aggregate_llquantize_bucket(factor, low, high, nsteps, nval)] += incr; } /*ARGSUSED*/ static void dtrace_aggregate_avg(uint64_t *data, uint64_t nval, uint64_t arg) { data[0]++; data[1] += nval; } /*ARGSUSED*/ static void dtrace_aggregate_stddev(uint64_t *data, uint64_t nval, uint64_t arg) { int64_t snval = (int64_t)nval; uint64_t tmp[2]; data[0]++; data[1] += nval; /* * What we want to say here is: * * data[2] += nval * nval; * * But given that nval is 64-bit, we could easily overflow, so * we do this as 128-bit arithmetic. */ if (snval < 0) snval = -snval; dtrace_multiply_128((uint64_t)snval, (uint64_t)snval, tmp); dtrace_add_128(data + 2, tmp, data + 2); } /*ARGSUSED*/ static void dtrace_aggregate_count(uint64_t *oval, uint64_t nval, uint64_t arg) { *oval = *oval + 1; } /*ARGSUSED*/ static void dtrace_aggregate_sum(uint64_t *oval, uint64_t nval, uint64_t arg) { *oval += nval; } /* * Aggregate given the tuple in the principal data buffer, and the aggregating * action denoted by the specified dtrace_aggregation_t. The aggregation * buffer is specified as the buf parameter. This routine does not return * failure; if there is no space in the aggregation buffer, the data will be * dropped, and a corresponding counter incremented. */ static void dtrace_aggregate(dtrace_aggregation_t *agg, dtrace_buffer_t *dbuf, intptr_t offset, dtrace_buffer_t *buf, uint64_t expr, uint64_t arg) { dtrace_recdesc_t *rec = &agg->dtag_action.dta_rec; uint32_t i, ndx, size, fsize; uint32_t align = sizeof (uint64_t) - 1; dtrace_aggbuffer_t *agb; dtrace_aggkey_t *key; uint32_t hashval = 0, limit, isstr; caddr_t tomax, data, kdata; dtrace_actkind_t action; dtrace_action_t *act; uintptr_t offs; if (buf == NULL) return; if (!agg->dtag_hasarg) { /* * Currently, only quantize() and lquantize() take additional * arguments, and they have the same semantics: an increment * value that defaults to 1 when not present. If additional * aggregating actions take arguments, the setting of the * default argument value will presumably have to become more * sophisticated... */ arg = 1; } action = agg->dtag_action.dta_kind - DTRACEACT_AGGREGATION; size = rec->dtrd_offset - agg->dtag_base; fsize = size + rec->dtrd_size; ASSERT(dbuf->dtb_tomax != NULL); data = dbuf->dtb_tomax + offset + agg->dtag_base; if ((tomax = buf->dtb_tomax) == NULL) { dtrace_buffer_drop(buf); return; } /* * The metastructure is always at the bottom of the buffer. */ agb = (dtrace_aggbuffer_t *)(tomax + buf->dtb_size - sizeof (dtrace_aggbuffer_t)); if (buf->dtb_offset == 0) { /* * We just kludge up approximately 1/8th of the size to be * buckets. If this guess ends up being routinely * off-the-mark, we may need to dynamically readjust this * based on past performance. */ uintptr_t hashsize = (buf->dtb_size >> 3) / sizeof (uintptr_t); if ((uintptr_t)agb - hashsize * sizeof (dtrace_aggkey_t *) < (uintptr_t)tomax || hashsize == 0) { /* * We've been given a ludicrously small buffer; * increment our drop count and leave. */ dtrace_buffer_drop(buf); return; } /* * And now, a pathetic attempt to try to get a an odd (or * perchance, a prime) hash size for better hash distribution. */ if (hashsize > (DTRACE_AGGHASHSIZE_SLEW << 3)) hashsize -= DTRACE_AGGHASHSIZE_SLEW; agb->dtagb_hashsize = hashsize; agb->dtagb_hash = (dtrace_aggkey_t **)((uintptr_t)agb - agb->dtagb_hashsize * sizeof (dtrace_aggkey_t *)); agb->dtagb_free = (uintptr_t)agb->dtagb_hash; for (i = 0; i < agb->dtagb_hashsize; i++) agb->dtagb_hash[i] = NULL; } ASSERT(agg->dtag_first != NULL); ASSERT(agg->dtag_first->dta_intuple); /* * Calculate the hash value based on the key. Note that we _don't_ * include the aggid in the hashing (but we will store it as part of * the key). The hashing algorithm is Bob Jenkins' "One-at-a-time" * algorithm: a simple, quick algorithm that has no known funnels, and * gets good distribution in practice. The efficacy of the hashing * algorithm (and a comparison with other algorithms) may be found by * running the ::dtrace_aggstat MDB dcmd. */ for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) { i = act->dta_rec.dtrd_offset - agg->dtag_base; limit = i + act->dta_rec.dtrd_size; ASSERT(limit <= size); isstr = DTRACEACT_ISSTRING(act); for (; i < limit; i++) { hashval += data[i]; hashval += (hashval << 10); hashval ^= (hashval >> 6); if (isstr && data[i] == '\0') break; } } hashval += (hashval << 3); hashval ^= (hashval >> 11); hashval += (hashval << 15); /* * Yes, the divide here is expensive -- but it's generally the least * of the performance issues given the amount of data that we iterate * over to compute hash values, compare data, etc. */ ndx = hashval % agb->dtagb_hashsize; for (key = agb->dtagb_hash[ndx]; key != NULL; key = key->dtak_next) { ASSERT((caddr_t)key >= tomax); ASSERT((caddr_t)key < tomax + buf->dtb_size); if (hashval != key->dtak_hashval || key->dtak_size != size) continue; kdata = key->dtak_data; ASSERT(kdata >= tomax && kdata < tomax + buf->dtb_size); for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) { i = act->dta_rec.dtrd_offset - agg->dtag_base; limit = i + act->dta_rec.dtrd_size; ASSERT(limit <= size); isstr = DTRACEACT_ISSTRING(act); for (; i < limit; i++) { if (kdata[i] != data[i]) goto next; if (isstr && data[i] == '\0') break; } } if (action != key->dtak_action) { /* * We are aggregating on the same value in the same * aggregation with two different aggregating actions. * (This should have been picked up in the compiler, * so we may be dealing with errant or devious DIF.) * This is an error condition; we indicate as much, * and return. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return; } /* * This is a hit: we need to apply the aggregator to * the value at this key. */ agg->dtag_aggregate((uint64_t *)(kdata + size), expr, arg); return; next: continue; } /* * We didn't find it. We need to allocate some zero-filled space, * link it into the hash table appropriately, and apply the aggregator * to the (zero-filled) value. */ offs = buf->dtb_offset; while (offs & (align - 1)) offs += sizeof (uint32_t); /* * If we don't have enough room to both allocate a new key _and_ * its associated data, increment the drop count and return. */ if ((uintptr_t)tomax + offs + fsize > agb->dtagb_free - sizeof (dtrace_aggkey_t)) { dtrace_buffer_drop(buf); return; } /*CONSTCOND*/ ASSERT(!(sizeof (dtrace_aggkey_t) & (sizeof (uintptr_t) - 1))); key = (dtrace_aggkey_t *)(agb->dtagb_free - sizeof (dtrace_aggkey_t)); agb->dtagb_free -= sizeof (dtrace_aggkey_t); key->dtak_data = kdata = tomax + offs; buf->dtb_offset = offs + fsize; /* * Now copy the data across. */ *((dtrace_aggid_t *)kdata) = agg->dtag_id; for (i = sizeof (dtrace_aggid_t); i < size; i++) kdata[i] = data[i]; /* * Because strings are not zeroed out by default, we need to iterate * looking for actions that store strings, and we need to explicitly * pad these strings out with zeroes. */ for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) { int nul; if (!DTRACEACT_ISSTRING(act)) continue; i = act->dta_rec.dtrd_offset - agg->dtag_base; limit = i + act->dta_rec.dtrd_size; ASSERT(limit <= size); for (nul = 0; i < limit; i++) { if (nul) { kdata[i] = '\0'; continue; } if (data[i] != '\0') continue; nul = 1; } } for (i = size; i < fsize; i++) kdata[i] = 0; key->dtak_hashval = hashval; key->dtak_size = size; key->dtak_action = action; key->dtak_next = agb->dtagb_hash[ndx]; agb->dtagb_hash[ndx] = key; /* * Finally, apply the aggregator. */ *((uint64_t *)(key->dtak_data + size)) = agg->dtag_initial; agg->dtag_aggregate((uint64_t *)(key->dtak_data + size), expr, arg); } /* * Given consumer state, this routine finds a speculation in the INACTIVE * state and transitions it into the ACTIVE state. If there is no speculation * in the INACTIVE state, 0 is returned. In this case, no error counter is * incremented -- it is up to the caller to take appropriate action. */ static int dtrace_speculation(dtrace_state_t *state) { int i = 0; dtrace_speculation_state_t current; uint32_t *stat = &state->dts_speculations_unavail, count; while (i < state->dts_nspeculations) { dtrace_speculation_t *spec = &state->dts_speculations[i]; current = spec->dtsp_state; if (current != DTRACESPEC_INACTIVE) { if (current == DTRACESPEC_COMMITTINGMANY || current == DTRACESPEC_COMMITTING || current == DTRACESPEC_DISCARDING) stat = &state->dts_speculations_busy; i++; continue; } if (dtrace_cas32((uint32_t *)&spec->dtsp_state, current, DTRACESPEC_ACTIVE) == current) return (i + 1); } /* * We couldn't find a speculation. If we found as much as a single * busy speculation buffer, we'll attribute this failure as "busy" * instead of "unavail". */ do { count = *stat; } while (dtrace_cas32(stat, count, count + 1) != count); return (0); } /* * This routine commits an active speculation. If the specified speculation * is not in a valid state to perform a commit(), this routine will silently do * nothing. The state of the specified speculation is transitioned according * to the state transition diagram outlined in */ static void dtrace_speculation_commit(dtrace_state_t *state, processorid_t cpu, dtrace_specid_t which) { dtrace_speculation_t *spec; dtrace_buffer_t *src, *dest; uintptr_t daddr, saddr, dlimit, slimit; dtrace_speculation_state_t current, new = 0; intptr_t offs; uint64_t timestamp; if (which == 0) return; if (which > state->dts_nspeculations) { cpu_core[cpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP; return; } spec = &state->dts_speculations[which - 1]; src = &spec->dtsp_buffer[cpu]; dest = &state->dts_buffer[cpu]; do { current = spec->dtsp_state; if (current == DTRACESPEC_COMMITTINGMANY) break; switch (current) { case DTRACESPEC_INACTIVE: case DTRACESPEC_DISCARDING: return; case DTRACESPEC_COMMITTING: /* * This is only possible if we are (a) commit()'ing * without having done a prior speculate() on this CPU * and (b) racing with another commit() on a different * CPU. There's nothing to do -- we just assert that * our offset is 0. */ ASSERT(src->dtb_offset == 0); return; case DTRACESPEC_ACTIVE: new = DTRACESPEC_COMMITTING; break; case DTRACESPEC_ACTIVEONE: /* * This speculation is active on one CPU. If our * buffer offset is non-zero, we know that the one CPU * must be us. Otherwise, we are committing on a * different CPU from the speculate(), and we must * rely on being asynchronously cleaned. */ if (src->dtb_offset != 0) { new = DTRACESPEC_COMMITTING; break; } /*FALLTHROUGH*/ case DTRACESPEC_ACTIVEMANY: new = DTRACESPEC_COMMITTINGMANY; break; default: ASSERT(0); } } while (dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new) != current); /* * We have set the state to indicate that we are committing this * speculation. Now reserve the necessary space in the destination * buffer. */ if ((offs = dtrace_buffer_reserve(dest, src->dtb_offset, sizeof (uint64_t), state, NULL)) < 0) { dtrace_buffer_drop(dest); goto out; } /* * We have sufficient space to copy the speculative buffer into the * primary buffer. First, modify the speculative buffer, filling * in the timestamp of all entries with the current time. The data * must have the commit() time rather than the time it was traced, * so that all entries in the primary buffer are in timestamp order. */ timestamp = dtrace_gethrtime(); saddr = (uintptr_t)src->dtb_tomax; slimit = saddr + src->dtb_offset; while (saddr < slimit) { size_t size; dtrace_rechdr_t *dtrh = (dtrace_rechdr_t *)saddr; if (dtrh->dtrh_epid == DTRACE_EPIDNONE) { saddr += sizeof (dtrace_epid_t); continue; } ASSERT3U(dtrh->dtrh_epid, <=, state->dts_necbs); size = state->dts_ecbs[dtrh->dtrh_epid - 1]->dte_size; ASSERT3U(saddr + size, <=, slimit); ASSERT3U(size, >=, sizeof (dtrace_rechdr_t)); ASSERT3U(DTRACE_RECORD_LOAD_TIMESTAMP(dtrh), ==, UINT64_MAX); DTRACE_RECORD_STORE_TIMESTAMP(dtrh, timestamp); saddr += size; } /* * Copy the buffer across. (Note that this is a * highly subobtimal bcopy(); in the unlikely event that this becomes * a serious performance issue, a high-performance DTrace-specific * bcopy() should obviously be invented.) */ daddr = (uintptr_t)dest->dtb_tomax + offs; dlimit = daddr + src->dtb_offset; saddr = (uintptr_t)src->dtb_tomax; /* * First, the aligned portion. */ while (dlimit - daddr >= sizeof (uint64_t)) { *((uint64_t *)daddr) = *((uint64_t *)saddr); daddr += sizeof (uint64_t); saddr += sizeof (uint64_t); } /* * Now any left-over bit... */ while (dlimit - daddr) *((uint8_t *)daddr++) = *((uint8_t *)saddr++); /* * Finally, commit the reserved space in the destination buffer. */ dest->dtb_offset = offs + src->dtb_offset; out: /* * If we're lucky enough to be the only active CPU on this speculation * buffer, we can just set the state back to DTRACESPEC_INACTIVE. */ if (current == DTRACESPEC_ACTIVE || (current == DTRACESPEC_ACTIVEONE && new == DTRACESPEC_COMMITTING)) { uint32_t rval = dtrace_cas32((uint32_t *)&spec->dtsp_state, DTRACESPEC_COMMITTING, DTRACESPEC_INACTIVE); ASSERT(rval == DTRACESPEC_COMMITTING); } src->dtb_offset = 0; src->dtb_xamot_drops += src->dtb_drops; src->dtb_drops = 0; } /* * This routine discards an active speculation. If the specified speculation * is not in a valid state to perform a discard(), this routine will silently * do nothing. The state of the specified speculation is transitioned * according to the state transition diagram outlined in */ static void dtrace_speculation_discard(dtrace_state_t *state, processorid_t cpu, dtrace_specid_t which) { dtrace_speculation_t *spec; dtrace_speculation_state_t current, new = 0; dtrace_buffer_t *buf; if (which == 0) return; if (which > state->dts_nspeculations) { cpu_core[cpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP; return; } spec = &state->dts_speculations[which - 1]; buf = &spec->dtsp_buffer[cpu]; do { current = spec->dtsp_state; switch (current) { case DTRACESPEC_INACTIVE: case DTRACESPEC_COMMITTINGMANY: case DTRACESPEC_COMMITTING: case DTRACESPEC_DISCARDING: return; case DTRACESPEC_ACTIVE: case DTRACESPEC_ACTIVEMANY: new = DTRACESPEC_DISCARDING; break; case DTRACESPEC_ACTIVEONE: if (buf->dtb_offset != 0) { new = DTRACESPEC_INACTIVE; } else { new = DTRACESPEC_DISCARDING; } break; default: ASSERT(0); } } while (dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new) != current); buf->dtb_offset = 0; buf->dtb_drops = 0; } /* * Note: not called from probe context. This function is called * asynchronously from cross call context to clean any speculations that are * in the COMMITTINGMANY or DISCARDING states. These speculations may not be * transitioned back to the INACTIVE state until all CPUs have cleaned the * speculation. */ static void dtrace_speculation_clean_here(dtrace_state_t *state) { dtrace_icookie_t cookie; processorid_t cpu = curcpu; dtrace_buffer_t *dest = &state->dts_buffer[cpu]; dtrace_specid_t i; cookie = dtrace_interrupt_disable(); if (dest->dtb_tomax == NULL) { dtrace_interrupt_enable(cookie); return; } for (i = 0; i < state->dts_nspeculations; i++) { dtrace_speculation_t *spec = &state->dts_speculations[i]; dtrace_buffer_t *src = &spec->dtsp_buffer[cpu]; if (src->dtb_tomax == NULL) continue; if (spec->dtsp_state == DTRACESPEC_DISCARDING) { src->dtb_offset = 0; continue; } if (spec->dtsp_state != DTRACESPEC_COMMITTINGMANY) continue; if (src->dtb_offset == 0) continue; dtrace_speculation_commit(state, cpu, i + 1); } dtrace_interrupt_enable(cookie); } /* * Note: not called from probe context. This function is called * asynchronously (and at a regular interval) to clean any speculations that * are in the COMMITTINGMANY or DISCARDING states. If it discovers that there * is work to be done, it cross calls all CPUs to perform that work; * COMMITMANY and DISCARDING speculations may not be transitioned back to the * INACTIVE state until they have been cleaned by all CPUs. */ static void dtrace_speculation_clean(dtrace_state_t *state) { int work = 0, rv; dtrace_specid_t i; for (i = 0; i < state->dts_nspeculations; i++) { dtrace_speculation_t *spec = &state->dts_speculations[i]; ASSERT(!spec->dtsp_cleaning); if (spec->dtsp_state != DTRACESPEC_DISCARDING && spec->dtsp_state != DTRACESPEC_COMMITTINGMANY) continue; work++; spec->dtsp_cleaning = 1; } if (!work) return; dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_speculation_clean_here, state); /* * We now know that all CPUs have committed or discarded their * speculation buffers, as appropriate. We can now set the state * to inactive. */ for (i = 0; i < state->dts_nspeculations; i++) { dtrace_speculation_t *spec = &state->dts_speculations[i]; dtrace_speculation_state_t current, new; if (!spec->dtsp_cleaning) continue; current = spec->dtsp_state; ASSERT(current == DTRACESPEC_DISCARDING || current == DTRACESPEC_COMMITTINGMANY); new = DTRACESPEC_INACTIVE; rv = dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new); ASSERT(rv == current); spec->dtsp_cleaning = 0; } } /* * Called as part of a speculate() to get the speculative buffer associated * with a given speculation. Returns NULL if the specified speculation is not * in an ACTIVE state. If the speculation is in the ACTIVEONE state -- and * the active CPU is not the specified CPU -- the speculation will be * atomically transitioned into the ACTIVEMANY state. */ static dtrace_buffer_t * dtrace_speculation_buffer(dtrace_state_t *state, processorid_t cpuid, dtrace_specid_t which) { dtrace_speculation_t *spec; dtrace_speculation_state_t current, new = 0; dtrace_buffer_t *buf; if (which == 0) return (NULL); if (which > state->dts_nspeculations) { cpu_core[cpuid].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP; return (NULL); } spec = &state->dts_speculations[which - 1]; buf = &spec->dtsp_buffer[cpuid]; do { current = spec->dtsp_state; switch (current) { case DTRACESPEC_INACTIVE: case DTRACESPEC_COMMITTINGMANY: case DTRACESPEC_DISCARDING: return (NULL); case DTRACESPEC_COMMITTING: ASSERT(buf->dtb_offset == 0); return (NULL); case DTRACESPEC_ACTIVEONE: /* * This speculation is currently active on one CPU. * Check the offset in the buffer; if it's non-zero, * that CPU must be us (and we leave the state alone). * If it's zero, assume that we're starting on a new * CPU -- and change the state to indicate that the * speculation is active on more than one CPU. */ if (buf->dtb_offset != 0) return (buf); new = DTRACESPEC_ACTIVEMANY; break; case DTRACESPEC_ACTIVEMANY: return (buf); case DTRACESPEC_ACTIVE: new = DTRACESPEC_ACTIVEONE; break; default: ASSERT(0); } } while (dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new) != current); ASSERT(new == DTRACESPEC_ACTIVEONE || new == DTRACESPEC_ACTIVEMANY); return (buf); } /* * Return a string. In the event that the user lacks the privilege to access * arbitrary kernel memory, we copy the string out to scratch memory so that we * don't fail access checking. * * dtrace_dif_variable() uses this routine as a helper for various * builtin values such as 'execname' and 'probefunc.' */ uintptr_t dtrace_dif_varstr(uintptr_t addr, dtrace_state_t *state, dtrace_mstate_t *mstate) { uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t ret; size_t strsz; /* * The easy case: this probe is allowed to read all of memory, so * we can just return this as a vanilla pointer. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) return (addr); /* * This is the tougher case: we copy the string in question from * kernel memory into scratch memory and return it that way: this * ensures that we won't trip up when access checking tests the * BYREF return value. */ strsz = dtrace_strlen((char *)addr, size) + 1; if (mstate->dtms_scratch_ptr + strsz > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); return (0); } dtrace_strcpy((const void *)addr, (void *)mstate->dtms_scratch_ptr, strsz); ret = mstate->dtms_scratch_ptr; mstate->dtms_scratch_ptr += strsz; return (ret); } /* * Return a string from a memoy address which is known to have one or * more concatenated, individually zero terminated, sub-strings. * In the event that the user lacks the privilege to access * arbitrary kernel memory, we copy the string out to scratch memory so that we * don't fail access checking. * * dtrace_dif_variable() uses this routine as a helper for various * builtin values such as 'execargs'. */ static uintptr_t dtrace_dif_varstrz(uintptr_t addr, size_t strsz, dtrace_state_t *state, dtrace_mstate_t *mstate) { char *p; size_t i; uintptr_t ret; if (mstate->dtms_scratch_ptr + strsz > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); return (0); } dtrace_bcopy((const void *)addr, (void *)mstate->dtms_scratch_ptr, strsz); /* Replace sub-string termination characters with a space. */ for (p = (char *) mstate->dtms_scratch_ptr, i = 0; i < strsz - 1; p++, i++) if (*p == '\0') *p = ' '; ret = mstate->dtms_scratch_ptr; mstate->dtms_scratch_ptr += strsz; return (ret); } /* * This function implements the DIF emulator's variable lookups. The emulator * passes a reserved variable identifier and optional built-in array index. */ static uint64_t dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v, uint64_t ndx) { /* * If we're accessing one of the uncached arguments, we'll turn this * into a reference in the args array. */ if (v >= DIF_VAR_ARG0 && v <= DIF_VAR_ARG9) { ndx = v - DIF_VAR_ARG0; v = DIF_VAR_ARGS; } switch (v) { case DIF_VAR_ARGS: ASSERT(mstate->dtms_present & DTRACE_MSTATE_ARGS); if (ndx >= sizeof (mstate->dtms_arg) / sizeof (mstate->dtms_arg[0])) { int aframes = mstate->dtms_probe->dtpr_aframes + 2; dtrace_provider_t *pv; uint64_t val; pv = mstate->dtms_probe->dtpr_provider; if (pv->dtpv_pops.dtps_getargval != NULL) val = pv->dtpv_pops.dtps_getargval(pv->dtpv_arg, mstate->dtms_probe->dtpr_id, mstate->dtms_probe->dtpr_arg, ndx, aframes); else val = dtrace_getarg(ndx, aframes); /* * This is regrettably required to keep the compiler * from tail-optimizing the call to dtrace_getarg(). * The condition always evaluates to true, but the * compiler has no way of figuring that out a priori. * (None of this would be necessary if the compiler * could be relied upon to _always_ tail-optimize * the call to dtrace_getarg() -- but it can't.) */ if (mstate->dtms_probe != NULL) return (val); ASSERT(0); } return (mstate->dtms_arg[ndx]); #if defined(sun) case DIF_VAR_UREGS: { klwp_t *lwp; if (!dtrace_priv_proc(state)) return (0); if ((lwp = curthread->t_lwp) == NULL) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = NULL; return (0); } return (dtrace_getreg(lwp->lwp_regs, ndx)); return (0); } #else case DIF_VAR_UREGS: { struct trapframe *tframe; if (!dtrace_priv_proc(state)) return (0); if ((tframe = curthread->td_frame) == NULL) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = 0; return (0); } return (dtrace_getreg(tframe, ndx)); } #endif case DIF_VAR_CURTHREAD: - if (!dtrace_priv_kernel(state)) + if (!dtrace_priv_proc(state)) return (0); return ((uint64_t)(uintptr_t)curthread); case DIF_VAR_TIMESTAMP: if (!(mstate->dtms_present & DTRACE_MSTATE_TIMESTAMP)) { mstate->dtms_timestamp = dtrace_gethrtime(); mstate->dtms_present |= DTRACE_MSTATE_TIMESTAMP; } return (mstate->dtms_timestamp); case DIF_VAR_VTIMESTAMP: ASSERT(dtrace_vtime_references != 0); return (curthread->t_dtrace_vtime); case DIF_VAR_WALLTIMESTAMP: if (!(mstate->dtms_present & DTRACE_MSTATE_WALLTIMESTAMP)) { mstate->dtms_walltimestamp = dtrace_gethrestime(); mstate->dtms_present |= DTRACE_MSTATE_WALLTIMESTAMP; } return (mstate->dtms_walltimestamp); #if defined(sun) case DIF_VAR_IPL: if (!dtrace_priv_kernel(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_IPL)) { mstate->dtms_ipl = dtrace_getipl(); mstate->dtms_present |= DTRACE_MSTATE_IPL; } return (mstate->dtms_ipl); #endif case DIF_VAR_EPID: ASSERT(mstate->dtms_present & DTRACE_MSTATE_EPID); return (mstate->dtms_epid); case DIF_VAR_ID: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (mstate->dtms_probe->dtpr_id); case DIF_VAR_STACKDEPTH: if (!dtrace_priv_kernel(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_STACKDEPTH)) { int aframes = mstate->dtms_probe->dtpr_aframes + 2; mstate->dtms_stackdepth = dtrace_getstackdepth(aframes); mstate->dtms_present |= DTRACE_MSTATE_STACKDEPTH; } return (mstate->dtms_stackdepth); case DIF_VAR_USTACKDEPTH: if (!dtrace_priv_proc(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_USTACKDEPTH)) { /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) { mstate->dtms_ustackdepth = 0; } else { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); mstate->dtms_ustackdepth = dtrace_getustackdepth(); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); } mstate->dtms_present |= DTRACE_MSTATE_USTACKDEPTH; } return (mstate->dtms_ustackdepth); case DIF_VAR_CALLER: if (!dtrace_priv_kernel(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_CALLER)) { int aframes = mstate->dtms_probe->dtpr_aframes + 2; if (!DTRACE_ANCHORED(mstate->dtms_probe)) { /* * If this is an unanchored probe, we are * required to go through the slow path: * dtrace_caller() only guarantees correct * results for anchored probes. */ pc_t caller[2] = {0, 0}; dtrace_getpcstack(caller, 2, aframes, (uint32_t *)(uintptr_t)mstate->dtms_arg[0]); mstate->dtms_caller = caller[1]; } else if ((mstate->dtms_caller = dtrace_caller(aframes)) == -1) { /* * We have failed to do this the quick way; * we must resort to the slower approach of * calling dtrace_getpcstack(). */ pc_t caller = 0; dtrace_getpcstack(&caller, 1, aframes, NULL); mstate->dtms_caller = caller; } mstate->dtms_present |= DTRACE_MSTATE_CALLER; } return (mstate->dtms_caller); case DIF_VAR_UCALLER: if (!dtrace_priv_proc(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_UCALLER)) { uint64_t ustack[3]; /* * dtrace_getupcstack() fills in the first uint64_t * with the current PID. The second uint64_t will * be the program counter at user-level. The third * uint64_t will contain the caller, which is what * we're after. */ ustack[2] = 0; DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_getupcstack(ustack, 3); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); mstate->dtms_ucaller = ustack[2]; mstate->dtms_present |= DTRACE_MSTATE_UCALLER; } return (mstate->dtms_ucaller); case DIF_VAR_PROBEPROV: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (dtrace_dif_varstr( (uintptr_t)mstate->dtms_probe->dtpr_provider->dtpv_name, state, mstate)); case DIF_VAR_PROBEMOD: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (dtrace_dif_varstr( (uintptr_t)mstate->dtms_probe->dtpr_mod, state, mstate)); case DIF_VAR_PROBEFUNC: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (dtrace_dif_varstr( (uintptr_t)mstate->dtms_probe->dtpr_func, state, mstate)); case DIF_VAR_PROBENAME: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (dtrace_dif_varstr( (uintptr_t)mstate->dtms_probe->dtpr_name, state, mstate)); case DIF_VAR_PID: if (!dtrace_priv_proc(state)) return (0); #if defined(sun) /* * Note that we are assuming that an unanchored probe is * always due to a high-level interrupt. (And we're assuming * that there is only a single high level interrupt.) */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return (pid0.pid_id); /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * Further, it is always safe to dereference the p_pidp member * of one's own proc structure. (These are truisms becuase * threads and processes don't clean up their own state -- * they leave that task to whomever reaps them.) */ return ((uint64_t)curthread->t_procp->p_pidp->pid_id); #else return ((uint64_t)curproc->p_pid); #endif case DIF_VAR_PPID: if (!dtrace_priv_proc(state)) return (0); #if defined(sun) /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return (pid0.pid_id); /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) */ return ((uint64_t)curthread->t_procp->p_ppid); #else return ((uint64_t)curproc->p_pptr->p_pid); #endif case DIF_VAR_TID: #if defined(sun) /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return (0); #endif return ((uint64_t)curthread->t_tid); case DIF_VAR_EXECARGS: { struct pargs *p_args = curthread->td_proc->p_args; if (p_args == NULL) return(0); return (dtrace_dif_varstrz( (uintptr_t) p_args->ar_args, p_args->ar_length, state, mstate)); } case DIF_VAR_EXECNAME: #if defined(sun) if (!dtrace_priv_proc(state)) return (0); /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return ((uint64_t)(uintptr_t)p0.p_user.u_comm); /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) */ return (dtrace_dif_varstr( (uintptr_t)curthread->t_procp->p_user.u_comm, state, mstate)); #else return (dtrace_dif_varstr( (uintptr_t) curthread->td_proc->p_comm, state, mstate)); #endif case DIF_VAR_ZONENAME: #if defined(sun) if (!dtrace_priv_proc(state)) return (0); /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return ((uint64_t)(uintptr_t)p0.p_zone->zone_name); /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) */ return (dtrace_dif_varstr( (uintptr_t)curthread->t_procp->p_zone->zone_name, state, mstate)); #else return (0); #endif case DIF_VAR_UID: if (!dtrace_priv_proc(state)) return (0); #if defined(sun) /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return ((uint64_t)p0.p_cred->cr_uid); #endif /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) * * Additionally, it is safe to dereference one's own process * credential, since this is never NULL after process birth. */ return ((uint64_t)curthread->t_procp->p_cred->cr_uid); case DIF_VAR_GID: if (!dtrace_priv_proc(state)) return (0); #if defined(sun) /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return ((uint64_t)p0.p_cred->cr_gid); #endif /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) * * Additionally, it is safe to dereference one's own process * credential, since this is never NULL after process birth. */ return ((uint64_t)curthread->t_procp->p_cred->cr_gid); case DIF_VAR_ERRNO: { #if defined(sun) klwp_t *lwp; if (!dtrace_priv_proc(state)) return (0); /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return (0); /* * It is always safe to dereference one's own t_lwp pointer in * the event that this pointer is non-NULL. (This is true * because threads and lwps don't clean up their own state -- * they leave that task to whomever reaps them.) */ if ((lwp = curthread->t_lwp) == NULL) return (0); return ((uint64_t)lwp->lwp_errno); #else return (curthread->td_errno); #endif } #if !defined(sun) case DIF_VAR_CPU: { return curcpu; } #endif default: DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return (0); } } + +typedef enum dtrace_json_state { + DTRACE_JSON_REST = 1, + DTRACE_JSON_OBJECT, + DTRACE_JSON_STRING, + DTRACE_JSON_STRING_ESCAPE, + DTRACE_JSON_STRING_ESCAPE_UNICODE, + DTRACE_JSON_COLON, + DTRACE_JSON_COMMA, + DTRACE_JSON_VALUE, + DTRACE_JSON_IDENTIFIER, + DTRACE_JSON_NUMBER, + DTRACE_JSON_NUMBER_FRAC, + DTRACE_JSON_NUMBER_EXP, + DTRACE_JSON_COLLECT_OBJECT +} dtrace_json_state_t; + /* + * This function possesses just enough knowledge about JSON to extract a single + * value from a JSON string and store it in the scratch buffer. It is able + * to extract nested object values, and members of arrays by index. + * + * elemlist is a list of JSON keys, stored as packed NUL-terminated strings, to + * be looked up as we descend into the object tree. e.g. + * + * foo[0].bar.baz[32] --> "foo" NUL "0" NUL "bar" NUL "baz" NUL "32" NUL + * with nelems = 5. + * + * The run time of this function must be bounded above by strsize to limit the + * amount of work done in probe context. As such, it is implemented as a + * simple state machine, reading one character at a time using safe loads + * until we find the requested element, hit a parsing error or run off the + * end of the object or string. + * + * As there is no way for a subroutine to return an error without interrupting + * clause execution, we simply return NULL in the event of a missing key or any + * other error condition. Each NULL return in this function is commented with + * the error condition it represents -- parsing or otherwise. + * + * The set of states for the state machine closely matches the JSON + * specification (http://json.org/). Briefly: + * + * DTRACE_JSON_REST: + * Skip whitespace until we find either a top-level Object, moving + * to DTRACE_JSON_OBJECT; or an Array, moving to DTRACE_JSON_VALUE. + * + * DTRACE_JSON_OBJECT: + * Locate the next key String in an Object. Sets a flag to denote + * the next String as a key string and moves to DTRACE_JSON_STRING. + * + * DTRACE_JSON_COLON: + * Skip whitespace until we find the colon that separates key Strings + * from their values. Once found, move to DTRACE_JSON_VALUE. + * + * DTRACE_JSON_VALUE: + * Detects the type of the next value (String, Number, Identifier, Object + * or Array) and routes to the states that process that type. Here we also + * deal with the element selector list if we are requested to traverse down + * into the object tree. + * + * DTRACE_JSON_COMMA: + * Skip whitespace until we find the comma that separates key-value pairs + * in Objects (returning to DTRACE_JSON_OBJECT) or values in Arrays + * (similarly DTRACE_JSON_VALUE). All following literal value processing + * states return to this state at the end of their value, unless otherwise + * noted. + * + * DTRACE_JSON_NUMBER, DTRACE_JSON_NUMBER_FRAC, DTRACE_JSON_NUMBER_EXP: + * Processes a Number literal from the JSON, including any exponent + * component that may be present. Numbers are returned as strings, which + * may be passed to strtoll() if an integer is required. + * + * DTRACE_JSON_IDENTIFIER: + * Processes a "true", "false" or "null" literal in the JSON. + * + * DTRACE_JSON_STRING, DTRACE_JSON_STRING_ESCAPE, + * DTRACE_JSON_STRING_ESCAPE_UNICODE: + * Processes a String literal from the JSON, whether the String denotes + * a key, a value or part of a larger Object. Handles all escape sequences + * present in the specification, including four-digit unicode characters, + * but merely includes the escape sequence without converting it to the + * actual escaped character. If the String is flagged as a key, we + * move to DTRACE_JSON_COLON rather than DTRACE_JSON_COMMA. + * + * DTRACE_JSON_COLLECT_OBJECT: + * This state collects an entire Object (or Array), correctly handling + * embedded strings. If the full element selector list matches this nested + * object, we return the Object in full as a string. If not, we use this + * state to skip to the next value at this level and continue processing. + * + * NOTE: This function uses various macros from strtolctype.h to manipulate + * digit values, etc -- these have all been checked to ensure they make + * no additional function calls. + */ +static char * +dtrace_json(uint64_t size, uintptr_t json, char *elemlist, int nelems, + char *dest) +{ + dtrace_json_state_t state = DTRACE_JSON_REST; + int64_t array_elem = INT64_MIN; + int64_t array_pos = 0; + uint8_t escape_unicount = 0; + boolean_t string_is_key = B_FALSE; + boolean_t collect_object = B_FALSE; + boolean_t found_key = B_FALSE; + boolean_t in_array = B_FALSE; + uint32_t braces = 0, brackets = 0; + char *elem = elemlist; + char *dd = dest; + uintptr_t cur; + + for (cur = json; cur < json + size; cur++) { + char cc = dtrace_load8(cur); + if (cc == '\0') + return (NULL); + + switch (state) { + case DTRACE_JSON_REST: + if (isspace(cc)) + break; + + if (cc == '{') { + state = DTRACE_JSON_OBJECT; + break; + } + + if (cc == '[') { + in_array = B_TRUE; + array_pos = 0; + array_elem = dtrace_strtoll(elem, 10, size); + found_key = array_elem == 0 ? B_TRUE : B_FALSE; + state = DTRACE_JSON_VALUE; + break; + } + + /* + * ERROR: expected to find a top-level object or array. + */ + return (NULL); + case DTRACE_JSON_OBJECT: + if (isspace(cc)) + break; + + if (cc == '"') { + state = DTRACE_JSON_STRING; + string_is_key = B_TRUE; + break; + } + + /* + * ERROR: either the object did not start with a key + * string, or we've run off the end of the object + * without finding the requested key. + */ + return (NULL); + case DTRACE_JSON_STRING: + if (cc == '\\') { + *dd++ = '\\'; + state = DTRACE_JSON_STRING_ESCAPE; + break; + } + + if (cc == '"') { + if (collect_object) { + /* + * We don't reset the dest here, as + * the string is part of a larger + * object being collected. + */ + *dd++ = cc; + collect_object = B_FALSE; + state = DTRACE_JSON_COLLECT_OBJECT; + break; + } + *dd = '\0'; + dd = dest; /* reset string buffer */ + if (string_is_key) { + if (dtrace_strncmp(dest, elem, + size) == 0) + found_key = B_TRUE; + } else if (found_key) { + if (nelems > 1) { + /* + * We expected an object, not + * this string. + */ + return (NULL); + } + return (dest); + } + state = string_is_key ? DTRACE_JSON_COLON : + DTRACE_JSON_COMMA; + string_is_key = B_FALSE; + break; + } + + *dd++ = cc; + break; + case DTRACE_JSON_STRING_ESCAPE: + *dd++ = cc; + if (cc == 'u') { + escape_unicount = 0; + state = DTRACE_JSON_STRING_ESCAPE_UNICODE; + } else { + state = DTRACE_JSON_STRING; + } + break; + case DTRACE_JSON_STRING_ESCAPE_UNICODE: + if (!isxdigit(cc)) { + /* + * ERROR: invalid unicode escape, expected + * four valid hexidecimal digits. + */ + return (NULL); + } + + *dd++ = cc; + if (++escape_unicount == 4) + state = DTRACE_JSON_STRING; + break; + case DTRACE_JSON_COLON: + if (isspace(cc)) + break; + + if (cc == ':') { + state = DTRACE_JSON_VALUE; + break; + } + + /* + * ERROR: expected a colon. + */ + return (NULL); + case DTRACE_JSON_COMMA: + if (isspace(cc)) + break; + + if (cc == ',') { + if (in_array) { + state = DTRACE_JSON_VALUE; + if (++array_pos == array_elem) + found_key = B_TRUE; + } else { + state = DTRACE_JSON_OBJECT; + } + break; + } + + /* + * ERROR: either we hit an unexpected character, or + * we reached the end of the object or array without + * finding the requested key. + */ + return (NULL); + case DTRACE_JSON_IDENTIFIER: + if (islower(cc)) { + *dd++ = cc; + break; + } + + *dd = '\0'; + dd = dest; /* reset string buffer */ + + if (dtrace_strncmp(dest, "true", 5) == 0 || + dtrace_strncmp(dest, "false", 6) == 0 || + dtrace_strncmp(dest, "null", 5) == 0) { + if (found_key) { + if (nelems > 1) { + /* + * ERROR: We expected an object, + * not this identifier. + */ + return (NULL); + } + return (dest); + } else { + cur--; + state = DTRACE_JSON_COMMA; + break; + } + } + + /* + * ERROR: we did not recognise the identifier as one + * of those in the JSON specification. + */ + return (NULL); + case DTRACE_JSON_NUMBER: + if (cc == '.') { + *dd++ = cc; + state = DTRACE_JSON_NUMBER_FRAC; + break; + } + + if (cc == 'x' || cc == 'X') { + /* + * ERROR: specification explicitly excludes + * hexidecimal or octal numbers. + */ + return (NULL); + } + + /* FALLTHRU */ + case DTRACE_JSON_NUMBER_FRAC: + if (cc == 'e' || cc == 'E') { + *dd++ = cc; + state = DTRACE_JSON_NUMBER_EXP; + break; + } + + if (cc == '+' || cc == '-') { + /* + * ERROR: expect sign as part of exponent only. + */ + return (NULL); + } + /* FALLTHRU */ + case DTRACE_JSON_NUMBER_EXP: + if (isdigit(cc) || cc == '+' || cc == '-') { + *dd++ = cc; + break; + } + + *dd = '\0'; + dd = dest; /* reset string buffer */ + if (found_key) { + if (nelems > 1) { + /* + * ERROR: We expected an object, not + * this number. + */ + return (NULL); + } + return (dest); + } + + cur--; + state = DTRACE_JSON_COMMA; + break; + case DTRACE_JSON_VALUE: + if (isspace(cc)) + break; + + if (cc == '{' || cc == '[') { + if (nelems > 1 && found_key) { + in_array = cc == '[' ? B_TRUE : B_FALSE; + /* + * If our element selector directs us + * to descend into this nested object, + * then move to the next selector + * element in the list and restart the + * state machine. + */ + while (*elem != '\0') + elem++; + elem++; /* skip the inter-element NUL */ + nelems--; + dd = dest; + if (in_array) { + state = DTRACE_JSON_VALUE; + array_pos = 0; + array_elem = dtrace_strtoll( + elem, 10, size); + found_key = array_elem == 0 ? + B_TRUE : B_FALSE; + } else { + found_key = B_FALSE; + state = DTRACE_JSON_OBJECT; + } + break; + } + + /* + * Otherwise, we wish to either skip this + * nested object or return it in full. + */ + if (cc == '[') + brackets = 1; + else + braces = 1; + *dd++ = cc; + state = DTRACE_JSON_COLLECT_OBJECT; + break; + } + + if (cc == '"') { + state = DTRACE_JSON_STRING; + break; + } + + if (islower(cc)) { + /* + * Here we deal with true, false and null. + */ + *dd++ = cc; + state = DTRACE_JSON_IDENTIFIER; + break; + } + + if (cc == '-' || isdigit(cc)) { + *dd++ = cc; + state = DTRACE_JSON_NUMBER; + break; + } + + /* + * ERROR: unexpected character at start of value. + */ + return (NULL); + case DTRACE_JSON_COLLECT_OBJECT: + if (cc == '\0') + /* + * ERROR: unexpected end of input. + */ + return (NULL); + + *dd++ = cc; + if (cc == '"') { + collect_object = B_TRUE; + state = DTRACE_JSON_STRING; + break; + } + + if (cc == ']') { + if (brackets-- == 0) { + /* + * ERROR: unbalanced brackets. + */ + return (NULL); + } + } else if (cc == '}') { + if (braces-- == 0) { + /* + * ERROR: unbalanced braces. + */ + return (NULL); + } + } else if (cc == '{') { + braces++; + } else if (cc == '[') { + brackets++; + } + + if (brackets == 0 && braces == 0) { + if (found_key) { + *dd = '\0'; + return (dest); + } + dd = dest; /* reset string buffer */ + state = DTRACE_JSON_COMMA; + } + break; + } + } + return (NULL); +} + +/* * Emulate the execution of DTrace ID subroutines invoked by the call opcode. * Notice that we don't bother validating the proper number of arguments or * their types in the tuple stack. This isn't needed because all argument * interpretation is safe because of our load safety -- the worst that can * happen is that a bogus program can obtain bogus results. */ static void dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs, dtrace_key_t *tupregs, int nargs, dtrace_mstate_t *mstate, dtrace_state_t *state) { volatile uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags; volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval; dtrace_vstate_t *vstate = &state->dts_vstate; #if defined(sun) union { mutex_impl_t mi; uint64_t mx; } m; union { krwlock_t ri; uintptr_t rw; } r; #else struct thread *lowner; union { struct lock_object *li; uintptr_t lx; } l; #endif switch (subr) { case DIF_SUBR_RAND: regs[rd] = (dtrace_gethrtime() * 2416 + 374441) % 1771875; break; #if defined(sun) case DIF_SUBR_MUTEX_OWNED: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), mstate, vstate)) { regs[rd] = 0; break; } m.mx = dtrace_load64(tupregs[0].dttk_value); if (MUTEX_TYPE_ADAPTIVE(&m.mi)) regs[rd] = MUTEX_OWNER(&m.mi) != MUTEX_NO_OWNER; else regs[rd] = LOCK_HELD(&m.mi.m_spin.m_spinlock); break; case DIF_SUBR_MUTEX_OWNER: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), mstate, vstate)) { regs[rd] = 0; break; } m.mx = dtrace_load64(tupregs[0].dttk_value); if (MUTEX_TYPE_ADAPTIVE(&m.mi) && MUTEX_OWNER(&m.mi) != MUTEX_NO_OWNER) regs[rd] = (uintptr_t)MUTEX_OWNER(&m.mi); else regs[rd] = 0; break; case DIF_SUBR_MUTEX_TYPE_ADAPTIVE: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), mstate, vstate)) { regs[rd] = 0; break; } m.mx = dtrace_load64(tupregs[0].dttk_value); regs[rd] = MUTEX_TYPE_ADAPTIVE(&m.mi); break; case DIF_SUBR_MUTEX_TYPE_SPIN: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), mstate, vstate)) { regs[rd] = 0; break; } m.mx = dtrace_load64(tupregs[0].dttk_value); regs[rd] = MUTEX_TYPE_SPIN(&m.mi); break; case DIF_SUBR_RW_READ_HELD: { uintptr_t tmp; if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), mstate, vstate)) { regs[rd] = 0; break; } r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_READ_HELD(&r.ri, tmp); break; } case DIF_SUBR_RW_WRITE_HELD: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (krwlock_t), mstate, vstate)) { regs[rd] = 0; break; } r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_WRITE_HELD(&r.ri); break; case DIF_SUBR_RW_ISWRITER: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (krwlock_t), mstate, vstate)) { regs[rd] = 0; break; } r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_ISWRITER(&r.ri); break; #else case DIF_SUBR_MUTEX_OWNED: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct lock_object), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); break; case DIF_SUBR_MUTEX_OWNER: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct lock_object), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); regs[rd] = (uintptr_t)lowner; break; case DIF_SUBR_MUTEX_TYPE_ADAPTIVE: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct mtx), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); /* XXX - should be only LC_SLEEPABLE? */ regs[rd] = (LOCK_CLASS(l.li)->lc_flags & (LC_SLEEPLOCK | LC_SLEEPABLE)) != 0; break; case DIF_SUBR_MUTEX_TYPE_SPIN: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct mtx), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); regs[rd] = (LOCK_CLASS(l.li)->lc_flags & LC_SPINLOCK) != 0; break; case DIF_SUBR_RW_READ_HELD: case DIF_SUBR_SX_SHARED_HELD: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner) && lowner == NULL; break; case DIF_SUBR_RW_WRITE_HELD: case DIF_SUBR_SX_EXCLUSIVE_HELD: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr(tupregs[0].dttk_value); LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); regs[rd] = (lowner == curthread); break; case DIF_SUBR_RW_ISWRITER: case DIF_SUBR_SX_ISEXCLUSIVE: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner) && lowner != NULL; break; #endif /* ! defined(sun) */ case DIF_SUBR_BCOPY: { /* * We need to be sure that the destination is in the scratch * region -- no other region is allowed. */ uintptr_t src = tupregs[0].dttk_value; uintptr_t dest = tupregs[1].dttk_value; size_t size = tupregs[2].dttk_value; if (!dtrace_inscratch(dest, size, mstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (!dtrace_canload(src, size, mstate, vstate)) { regs[rd] = 0; break; } dtrace_bcopy((void *)src, (void *)dest, size); break; } case DIF_SUBR_ALLOCA: case DIF_SUBR_COPYIN: { uintptr_t dest = P2ROUNDUP(mstate->dtms_scratch_ptr, 8); uint64_t size = tupregs[subr == DIF_SUBR_ALLOCA ? 0 : 1].dttk_value; size_t scratch_size = (dest - mstate->dtms_scratch_ptr) + size; /* * This action doesn't require any credential checks since * probes will not activate in user contexts to which the * enabling user does not have permissions. */ /* * Rounding up the user allocation size could have overflowed * a large, bogus allocation (like -1ULL) to 0. */ if (scratch_size < size || !DTRACE_INSCRATCH(mstate, scratch_size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if (subr == DIF_SUBR_COPYIN) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyin(tupregs[0].dttk_value, dest, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); } mstate->dtms_scratch_ptr += scratch_size; regs[rd] = dest; break; } case DIF_SUBR_COPYINTO: { uint64_t size = tupregs[1].dttk_value; uintptr_t dest = tupregs[2].dttk_value; /* * This action doesn't require any credential checks since * probes will not activate in user contexts to which the * enabling user does not have permissions. */ if (!dtrace_inscratch(dest, size, mstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyin(tupregs[0].dttk_value, dest, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; } case DIF_SUBR_COPYINSTR: { uintptr_t dest = mstate->dtms_scratch_ptr; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; if (nargs > 1 && tupregs[1].dttk_value < size) size = tupregs[1].dttk_value + 1; /* * This action doesn't require any credential checks since * probes will not activate in user contexts to which the * enabling user does not have permissions. */ if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyinstr(tupregs[0].dttk_value, dest, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); ((char *)dest)[size - 1] = '\0'; mstate->dtms_scratch_ptr += size; regs[rd] = dest; break; } #if defined(sun) case DIF_SUBR_MSGSIZE: case DIF_SUBR_MSGDSIZE: { uintptr_t baddr = tupregs[0].dttk_value, daddr; uintptr_t wptr, rptr; size_t count = 0; int cont = 0; while (baddr != 0 && !(*flags & CPU_DTRACE_FAULT)) { if (!dtrace_canload(baddr, sizeof (mblk_t), mstate, vstate)) { regs[rd] = 0; break; } wptr = dtrace_loadptr(baddr + offsetof(mblk_t, b_wptr)); rptr = dtrace_loadptr(baddr + offsetof(mblk_t, b_rptr)); if (wptr < rptr) { *flags |= CPU_DTRACE_BADADDR; *illval = tupregs[0].dttk_value; break; } daddr = dtrace_loadptr(baddr + offsetof(mblk_t, b_datap)); baddr = dtrace_loadptr(baddr + offsetof(mblk_t, b_cont)); /* * We want to prevent against denial-of-service here, * so we're only going to search the list for * dtrace_msgdsize_max mblks. */ if (cont++ > dtrace_msgdsize_max) { *flags |= CPU_DTRACE_ILLOP; break; } if (subr == DIF_SUBR_MSGDSIZE) { if (dtrace_load8(daddr + offsetof(dblk_t, db_type)) != M_DATA) continue; } count += wptr - rptr; } if (!(*flags & CPU_DTRACE_FAULT)) regs[rd] = count; break; } #endif case DIF_SUBR_PROGENYOF: { pid_t pid = tupregs[0].dttk_value; proc_t *p; int rval = 0; DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); for (p = curthread->t_procp; p != NULL; p = p->p_parent) { #if defined(sun) if (p->p_pidp->pid_id == pid) { #else if (p->p_pid == pid) { #endif rval = 1; break; } } DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); regs[rd] = rval; break; } case DIF_SUBR_SPECULATION: regs[rd] = dtrace_speculation(state); break; case DIF_SUBR_COPYOUT: { uintptr_t kaddr = tupregs[0].dttk_value; uintptr_t uaddr = tupregs[1].dttk_value; uint64_t size = tupregs[2].dttk_value; if (!dtrace_destructive_disallow && dtrace_priv_proc_control(state) && !dtrace_istoxic(kaddr, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyout(kaddr, uaddr, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); } break; } case DIF_SUBR_COPYOUTSTR: { uintptr_t kaddr = tupregs[0].dttk_value; uintptr_t uaddr = tupregs[1].dttk_value; uint64_t size = tupregs[2].dttk_value; if (!dtrace_destructive_disallow && dtrace_priv_proc_control(state) && !dtrace_istoxic(kaddr, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyoutstr(kaddr, uaddr, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); } break; } case DIF_SUBR_STRLEN: { size_t sz; uintptr_t addr = (uintptr_t)tupregs[0].dttk_value; sz = dtrace_strlen((char *)addr, state->dts_options[DTRACEOPT_STRSIZE]); if (!dtrace_canload(addr, sz + 1, mstate, vstate)) { regs[rd] = 0; break; } regs[rd] = sz; break; } case DIF_SUBR_STRCHR: case DIF_SUBR_STRRCHR: { /* * We're going to iterate over the string looking for the * specified character. We will iterate until we have reached * the string length or we have found the character. If this * is DIF_SUBR_STRRCHR, we will look for the last occurrence * of the specified character instead of the first. */ uintptr_t saddr = tupregs[0].dttk_value; uintptr_t addr = tupregs[0].dttk_value; uintptr_t limit = addr + state->dts_options[DTRACEOPT_STRSIZE]; char c, target = (char)tupregs[1].dttk_value; for (regs[rd] = 0; addr < limit; addr++) { if ((c = dtrace_load8(addr)) == target) { regs[rd] = addr; if (subr == DIF_SUBR_STRCHR) break; } if (c == '\0') break; } if (!dtrace_canload(saddr, addr - saddr, mstate, vstate)) { regs[rd] = 0; break; } break; } case DIF_SUBR_STRSTR: case DIF_SUBR_INDEX: case DIF_SUBR_RINDEX: { /* * We're going to iterate over the string looking for the * specified string. We will iterate until we have reached * the string length or we have found the string. (Yes, this * is done in the most naive way possible -- but considering * that the string we're searching for is likely to be * relatively short, the complexity of Rabin-Karp or similar * hardly seems merited.) */ char *addr = (char *)(uintptr_t)tupregs[0].dttk_value; char *substr = (char *)(uintptr_t)tupregs[1].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; size_t len = dtrace_strlen(addr, size); size_t sublen = dtrace_strlen(substr, size); char *limit = addr + len, *orig = addr; int notfound = subr == DIF_SUBR_STRSTR ? 0 : -1; int inc = 1; regs[rd] = notfound; if (!dtrace_canload((uintptr_t)addr, len + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!dtrace_canload((uintptr_t)substr, sublen + 1, mstate, vstate)) { regs[rd] = 0; break; } /* * strstr() and index()/rindex() have similar semantics if * both strings are the empty string: strstr() returns a * pointer to the (empty) string, and index() and rindex() * both return index 0 (regardless of any position argument). */ if (sublen == 0 && len == 0) { if (subr == DIF_SUBR_STRSTR) regs[rd] = (uintptr_t)addr; else regs[rd] = 0; break; } if (subr != DIF_SUBR_STRSTR) { if (subr == DIF_SUBR_RINDEX) { limit = orig - 1; addr += len; inc = -1; } /* * Both index() and rindex() take an optional position * argument that denotes the starting position. */ if (nargs == 3) { int64_t pos = (int64_t)tupregs[2].dttk_value; /* * If the position argument to index() is * negative, Perl implicitly clamps it at * zero. This semantic is a little surprising * given the special meaning of negative * positions to similar Perl functions like * substr(), but it appears to reflect a * notion that index() can start from a * negative index and increment its way up to * the string. Given this notion, Perl's * rindex() is at least self-consistent in * that it implicitly clamps positions greater * than the string length to be the string * length. Where Perl completely loses * coherence, however, is when the specified * substring is the empty string (""). In * this case, even if the position is * negative, rindex() returns 0 -- and even if * the position is greater than the length, * index() returns the string length. These * semantics violate the notion that index() * should never return a value less than the * specified position and that rindex() should * never return a value greater than the * specified position. (One assumes that * these semantics are artifacts of Perl's * implementation and not the results of * deliberate design -- it beggars belief that * even Larry Wall could desire such oddness.) * While in the abstract one would wish for * consistent position semantics across * substr(), index() and rindex() -- or at the * very least self-consistent position * semantics for index() and rindex() -- we * instead opt to keep with the extant Perl * semantics, in all their broken glory. (Do * we have more desire to maintain Perl's * semantics than Perl does? Probably.) */ if (subr == DIF_SUBR_RINDEX) { if (pos < 0) { if (sublen == 0) regs[rd] = 0; break; } if (pos > len) pos = len; } else { if (pos < 0) pos = 0; if (pos >= len) { if (sublen == 0) regs[rd] = len; break; } } addr = orig + pos; } } for (regs[rd] = notfound; addr != limit; addr += inc) { if (dtrace_strncmp(addr, substr, sublen) == 0) { if (subr != DIF_SUBR_STRSTR) { /* * As D index() and rindex() are * modeled on Perl (and not on awk), * we return a zero-based (and not a * one-based) index. (For you Perl * weenies: no, we're not going to add * $[ -- and shouldn't you be at a con * or something?) */ regs[rd] = (uintptr_t)(addr - orig); break; } ASSERT(subr == DIF_SUBR_STRSTR); regs[rd] = (uintptr_t)addr; break; } } break; } case DIF_SUBR_STRTOK: { uintptr_t addr = tupregs[0].dttk_value; uintptr_t tokaddr = tupregs[1].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t limit, toklimit = tokaddr + size; uint8_t c = 0, tokmap[32]; /* 256 / 8 */ char *dest = (char *)mstate->dtms_scratch_ptr; int i; /* * Check both the token buffer and (later) the input buffer, * since both could be non-scratch addresses. */ if (!dtrace_strcanload(tokaddr, size, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if (addr == 0) { /* * If the address specified is NULL, we use our saved * strtok pointer from the mstate. Note that this * means that the saved strtok pointer is _only_ * valid within multiple enablings of the same probe -- * it behaves like an implicit clause-local variable. */ addr = mstate->dtms_strtok; } else { /* * If the user-specified address is non-NULL we must * access check it. This is the only time we have * a chance to do so, since this address may reside * in the string table of this clause-- future calls * (when we fetch addr from mstate->dtms_strtok) * would fail this access check. */ if (!dtrace_strcanload(addr, size, mstate, vstate)) { regs[rd] = 0; break; } } /* * First, zero the token map, and then process the token * string -- setting a bit in the map for every character * found in the token string. */ for (i = 0; i < sizeof (tokmap); i++) tokmap[i] = 0; for (; tokaddr < toklimit; tokaddr++) { if ((c = dtrace_load8(tokaddr)) == '\0') break; ASSERT((c >> 3) < sizeof (tokmap)); tokmap[c >> 3] |= (1 << (c & 0x7)); } for (limit = addr + size; addr < limit; addr++) { /* * We're looking for a character that is _not_ contained * in the token string. */ if ((c = dtrace_load8(addr)) == '\0') break; if (!(tokmap[c >> 3] & (1 << (c & 0x7)))) break; } if (c == '\0') { /* * We reached the end of the string without finding * any character that was not in the token string. * We return NULL in this case, and we set the saved * address to NULL as well. */ regs[rd] = 0; mstate->dtms_strtok = 0; break; } /* * From here on, we're copying into the destination string. */ for (i = 0; addr < limit && i < size - 1; addr++) { if ((c = dtrace_load8(addr)) == '\0') break; if (tokmap[c >> 3] & (1 << (c & 0x7))) break; ASSERT(i < size); dest[i++] = c; } ASSERT(i < size); dest[i] = '\0'; regs[rd] = (uintptr_t)dest; mstate->dtms_scratch_ptr += size; mstate->dtms_strtok = addr; break; } case DIF_SUBR_SUBSTR: { uintptr_t s = tupregs[0].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; char *d = (char *)mstate->dtms_scratch_ptr; int64_t index = (int64_t)tupregs[1].dttk_value; int64_t remaining = (int64_t)tupregs[2].dttk_value; size_t len = dtrace_strlen((char *)s, size); int64_t i = 0; if (!dtrace_canload(s, len + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if (nargs <= 2) remaining = (int64_t)size; if (index < 0) { index += len; if (index < 0 && index + remaining > 0) { remaining += index; index = 0; } } if (index >= len || index < 0) { remaining = 0; } else if (remaining < 0) { remaining += len - index; } else if (index + remaining > size) { remaining = size - index; } for (i = 0; i < remaining; i++) { if ((d[i] = dtrace_load8(s + index + i)) == '\0') break; } d[i] = '\0'; mstate->dtms_scratch_ptr += size; regs[rd] = (uintptr_t)d; break; } + case DIF_SUBR_JSON: { + uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; + uintptr_t json = tupregs[0].dttk_value; + size_t jsonlen = dtrace_strlen((char *)json, size); + uintptr_t elem = tupregs[1].dttk_value; + size_t elemlen = dtrace_strlen((char *)elem, size); + + char *dest = (char *)mstate->dtms_scratch_ptr; + char *elemlist = (char *)mstate->dtms_scratch_ptr + jsonlen + 1; + char *ee = elemlist; + int nelems = 1; + uintptr_t cur; + + if (!dtrace_canload(json, jsonlen + 1, mstate, vstate) || + !dtrace_canload(elem, elemlen + 1, mstate, vstate)) { + regs[rd] = 0; + break; + } + + if (!DTRACE_INSCRATCH(mstate, jsonlen + 1 + elemlen + 1)) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); + regs[rd] = 0; + break; + } + + /* + * Read the element selector and split it up into a packed list + * of strings. + */ + for (cur = elem; cur < elem + elemlen; cur++) { + char cc = dtrace_load8(cur); + + if (cur == elem && cc == '[') { + /* + * If the first element selector key is + * actually an array index then ignore the + * bracket. + */ + continue; + } + + if (cc == ']') + continue; + + if (cc == '.' || cc == '[') { + nelems++; + cc = '\0'; + } + + *ee++ = cc; + } + *ee++ = '\0'; + + if ((regs[rd] = (uintptr_t)dtrace_json(size, json, elemlist, + nelems, dest)) != 0) + mstate->dtms_scratch_ptr += jsonlen + 1; + break; + } + case DIF_SUBR_TOUPPER: case DIF_SUBR_TOLOWER: { uintptr_t s = tupregs[0].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; char *dest = (char *)mstate->dtms_scratch_ptr, c; size_t len = dtrace_strlen((char *)s, size); char lower, upper, convert; int64_t i; if (subr == DIF_SUBR_TOUPPER) { lower = 'a'; upper = 'z'; convert = 'A'; } else { lower = 'A'; upper = 'Z'; convert = 'a'; } if (!dtrace_canload(s, len + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } for (i = 0; i < size - 1; i++) { if ((c = dtrace_load8(s + i)) == '\0') break; if (c >= lower && c <= upper) c = convert + (c - lower); dest[i] = c; } ASSERT(i < size); dest[i] = '\0'; regs[rd] = (uintptr_t)dest; mstate->dtms_scratch_ptr += size; break; } #if defined(sun) case DIF_SUBR_GETMAJOR: #ifdef _LP64 regs[rd] = (tupregs[0].dttk_value >> NBITSMINOR64) & MAXMAJ64; #else regs[rd] = (tupregs[0].dttk_value >> NBITSMINOR) & MAXMAJ; #endif break; case DIF_SUBR_GETMINOR: #ifdef _LP64 regs[rd] = tupregs[0].dttk_value & MAXMIN64; #else regs[rd] = tupregs[0].dttk_value & MAXMIN; #endif break; case DIF_SUBR_DDI_PATHNAME: { /* * This one is a galactic mess. We are going to roughly * emulate ddi_pathname(), but it's made more complicated * by the fact that we (a) want to include the minor name and * (b) must proceed iteratively instead of recursively. */ uintptr_t dest = mstate->dtms_scratch_ptr; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; char *start = (char *)dest, *end = start + size - 1; uintptr_t daddr = tupregs[0].dttk_value; int64_t minor = (int64_t)tupregs[1].dttk_value; char *s; int i, len, depth = 0; /* * Due to all the pointer jumping we do and context we must * rely upon, we just mandate that the user must have kernel * read privileges to use this routine. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) == 0) { *flags |= CPU_DTRACE_KPRIV; *illval = daddr; regs[rd] = 0; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } *end = '\0'; /* * We want to have a name for the minor. In order to do this, * we need to walk the minor list from the devinfo. We want * to be sure that we don't infinitely walk a circular list, * so we check for circularity by sending a scout pointer * ahead two elements for every element that we iterate over; * if the list is circular, these will ultimately point to the * same element. You may recognize this little trick as the * answer to a stupid interview question -- one that always * seems to be asked by those who had to have it laboriously * explained to them, and who can't even concisely describe * the conditions under which one would be forced to resort to * this technique. Needless to say, those conditions are * found here -- and probably only here. Is this the only use * of this infamous trick in shipping, production code? If it * isn't, it probably should be... */ if (minor != -1) { uintptr_t maddr = dtrace_loadptr(daddr + offsetof(struct dev_info, devi_minor)); uintptr_t next = offsetof(struct ddi_minor_data, next); uintptr_t name = offsetof(struct ddi_minor_data, d_minor) + offsetof(struct ddi_minor, name); uintptr_t dev = offsetof(struct ddi_minor_data, d_minor) + offsetof(struct ddi_minor, dev); uintptr_t scout; if (maddr != NULL) scout = dtrace_loadptr(maddr + next); while (maddr != NULL && !(*flags & CPU_DTRACE_FAULT)) { uint64_t m; #ifdef _LP64 m = dtrace_load64(maddr + dev) & MAXMIN64; #else m = dtrace_load32(maddr + dev) & MAXMIN; #endif if (m != minor) { maddr = dtrace_loadptr(maddr + next); if (scout == NULL) continue; scout = dtrace_loadptr(scout + next); if (scout == NULL) continue; scout = dtrace_loadptr(scout + next); if (scout == NULL) continue; if (scout == maddr) { *flags |= CPU_DTRACE_ILLOP; break; } continue; } /* * We have the minor data. Now we need to * copy the minor's name into the end of the * pathname. */ s = (char *)dtrace_loadptr(maddr + name); len = dtrace_strlen(s, size); if (*flags & CPU_DTRACE_FAULT) break; if (len != 0) { if ((end -= (len + 1)) < start) break; *end = ':'; } for (i = 1; i <= len; i++) end[i] = dtrace_load8((uintptr_t)s++); break; } } while (daddr != NULL && !(*flags & CPU_DTRACE_FAULT)) { ddi_node_state_t devi_state; devi_state = dtrace_load32(daddr + offsetof(struct dev_info, devi_node_state)); if (*flags & CPU_DTRACE_FAULT) break; if (devi_state >= DS_INITIALIZED) { s = (char *)dtrace_loadptr(daddr + offsetof(struct dev_info, devi_addr)); len = dtrace_strlen(s, size); if (*flags & CPU_DTRACE_FAULT) break; if (len != 0) { if ((end -= (len + 1)) < start) break; *end = '@'; } for (i = 1; i <= len; i++) end[i] = dtrace_load8((uintptr_t)s++); } /* * Now for the node name... */ s = (char *)dtrace_loadptr(daddr + offsetof(struct dev_info, devi_node_name)); daddr = dtrace_loadptr(daddr + offsetof(struct dev_info, devi_parent)); /* * If our parent is NULL (that is, if we're the root * node), we're going to use the special path * "devices". */ if (daddr == 0) s = "devices"; len = dtrace_strlen(s, size); if (*flags & CPU_DTRACE_FAULT) break; if ((end -= (len + 1)) < start) break; for (i = 1; i <= len; i++) end[i] = dtrace_load8((uintptr_t)s++); *end = '/'; if (depth++ > dtrace_devdepth_max) { *flags |= CPU_DTRACE_ILLOP; break; } } if (end < start) DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); if (daddr == 0) { regs[rd] = (uintptr_t)end; mstate->dtms_scratch_ptr += size; } break; } #endif case DIF_SUBR_STRJOIN: { char *d = (char *)mstate->dtms_scratch_ptr; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t s1 = tupregs[0].dttk_value; uintptr_t s2 = tupregs[1].dttk_value; int i = 0; if (!dtrace_strcanload(s1, size, mstate, vstate) || !dtrace_strcanload(s2, size, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } for (;;) { if (i >= size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if ((d[i++] = dtrace_load8(s1++)) == '\0') { i--; break; } } for (;;) { if (i >= size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if ((d[i++] = dtrace_load8(s2++)) == '\0') break; } if (i < size) { mstate->dtms_scratch_ptr += i; regs[rd] = (uintptr_t)d; } break; } + case DIF_SUBR_STRTOLL: { + uintptr_t s = tupregs[0].dttk_value; + uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; + int base = 10; + + if (nargs > 1) { + if ((base = tupregs[1].dttk_value) <= 1 || + base > ('z' - 'a' + 1) + ('9' - '0' + 1)) { + *flags |= CPU_DTRACE_ILLOP; + break; + } + } + + if (!dtrace_strcanload(s, size, mstate, vstate)) { + regs[rd] = INT64_MIN; + break; + } + + regs[rd] = dtrace_strtoll((char *)s, base, size); + break; + } + case DIF_SUBR_LLTOSTR: { int64_t i = (int64_t)tupregs[0].dttk_value; uint64_t val, digit; uint64_t size = 65; /* enough room for 2^64 in binary */ char *end = (char *)mstate->dtms_scratch_ptr + size - 1; int base = 10; if (nargs > 1) { if ((base = tupregs[1].dttk_value) <= 1 || base > ('z' - 'a' + 1) + ('9' - '0' + 1)) { *flags |= CPU_DTRACE_ILLOP; break; } } val = (base == 10 && i < 0) ? i * -1 : i; if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } for (*end-- = '\0'; val; val /= base) { if ((digit = val % base) <= '9' - '0') { *end-- = '0' + digit; } else { *end-- = 'a' + (digit - ('9' - '0') - 1); } } if (i == 0 && base == 16) *end-- = '0'; if (base == 16) *end-- = 'x'; if (i == 0 || base == 8 || base == 16) *end-- = '0'; if (i < 0 && base == 10) *end-- = '-'; regs[rd] = (uintptr_t)end + 1; mstate->dtms_scratch_ptr += size; break; } case DIF_SUBR_HTONS: case DIF_SUBR_NTOHS: #if BYTE_ORDER == BIG_ENDIAN regs[rd] = (uint16_t)tupregs[0].dttk_value; #else regs[rd] = DT_BSWAP_16((uint16_t)tupregs[0].dttk_value); #endif break; case DIF_SUBR_HTONL: case DIF_SUBR_NTOHL: #if BYTE_ORDER == BIG_ENDIAN regs[rd] = (uint32_t)tupregs[0].dttk_value; #else regs[rd] = DT_BSWAP_32((uint32_t)tupregs[0].dttk_value); #endif break; case DIF_SUBR_HTONLL: case DIF_SUBR_NTOHLL: #if BYTE_ORDER == BIG_ENDIAN regs[rd] = (uint64_t)tupregs[0].dttk_value; #else regs[rd] = DT_BSWAP_64((uint64_t)tupregs[0].dttk_value); #endif break; case DIF_SUBR_DIRNAME: case DIF_SUBR_BASENAME: { char *dest = (char *)mstate->dtms_scratch_ptr; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t src = tupregs[0].dttk_value; int i, j, len = dtrace_strlen((char *)src, size); int lastbase = -1, firstbase = -1, lastdir = -1; int start, end; if (!dtrace_canload(src, len + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } /* * The basename and dirname for a zero-length string is * defined to be "." */ if (len == 0) { len = 1; src = (uintptr_t)"."; } /* * Start from the back of the string, moving back toward the * front until we see a character that isn't a slash. That * character is the last character in the basename. */ for (i = len - 1; i >= 0; i--) { if (dtrace_load8(src + i) != '/') break; } if (i >= 0) lastbase = i; /* * Starting from the last character in the basename, move * towards the front until we find a slash. The character * that we processed immediately before that is the first * character in the basename. */ for (; i >= 0; i--) { if (dtrace_load8(src + i) == '/') break; } if (i >= 0) firstbase = i + 1; /* * Now keep going until we find a non-slash character. That * character is the last character in the dirname. */ for (; i >= 0; i--) { if (dtrace_load8(src + i) != '/') break; } if (i >= 0) lastdir = i; ASSERT(!(lastbase == -1 && firstbase != -1)); ASSERT(!(firstbase == -1 && lastdir != -1)); if (lastbase == -1) { /* * We didn't find a non-slash character. We know that * the length is non-zero, so the whole string must be * slashes. In either the dirname or the basename * case, we return '/'. */ ASSERT(firstbase == -1); firstbase = lastbase = lastdir = 0; } if (firstbase == -1) { /* * The entire string consists only of a basename * component. If we're looking for dirname, we need * to change our string to be just "."; if we're * looking for a basename, we'll just set the first * character of the basename to be 0. */ if (subr == DIF_SUBR_DIRNAME) { ASSERT(lastdir == -1); src = (uintptr_t)"."; lastdir = 0; } else { firstbase = 0; } } if (subr == DIF_SUBR_DIRNAME) { if (lastdir == -1) { /* * We know that we have a slash in the name -- * or lastdir would be set to 0, above. And * because lastdir is -1, we know that this * slash must be the first character. (That * is, the full string must be of the form * "/basename".) In this case, the last * character of the directory name is 0. */ lastdir = 0; } start = 0; end = lastdir; } else { ASSERT(subr == DIF_SUBR_BASENAME); ASSERT(firstbase != -1 && lastbase != -1); start = firstbase; end = lastbase; } for (i = start, j = 0; i <= end && j < size - 1; i++, j++) dest[j] = dtrace_load8(src + i); dest[j] = '\0'; regs[rd] = (uintptr_t)dest; mstate->dtms_scratch_ptr += size; break; } + case DIF_SUBR_GETF: { + uintptr_t fd = tupregs[0].dttk_value; + struct filedesc *fdp; + file_t *fp; + + if (!dtrace_priv_proc(state)) { + regs[rd] = 0; + break; + } + fdp = curproc->p_fd; + FILEDESC_SLOCK(fdp); + fp = fget_locked(fdp, fd); + mstate->dtms_getf = fp; + regs[rd] = (uintptr_t)fp; + FILEDESC_SUNLOCK(fdp); + break; + } + case DIF_SUBR_CLEANPATH: { char *dest = (char *)mstate->dtms_scratch_ptr, c; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t src = tupregs[0].dttk_value; int i = 0, j = 0; +#if defined(sun) + zone_t *z; +#endif if (!dtrace_strcanload(src, size, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } /* * Move forward, loading each character. */ do { c = dtrace_load8(src + i++); next: if (j + 5 >= size) /* 5 = strlen("/..c\0") */ break; if (c != '/') { dest[j++] = c; continue; } c = dtrace_load8(src + i++); if (c == '/') { /* * We have two slashes -- we can just advance * to the next character. */ goto next; } if (c != '.') { /* * This is not "." and it's not ".." -- we can * just store the "/" and this character and * drive on. */ dest[j++] = '/'; dest[j++] = c; continue; } c = dtrace_load8(src + i++); if (c == '/') { /* * This is a "/./" component. We're not going * to store anything in the destination buffer; * we're just going to go to the next component. */ goto next; } if (c != '.') { /* * This is not ".." -- we can just store the * "/." and this character and continue * processing. */ dest[j++] = '/'; dest[j++] = '.'; dest[j++] = c; continue; } c = dtrace_load8(src + i++); if (c != '/' && c != '\0') { /* * This is not ".." -- it's "..[mumble]". * We'll store the "/.." and this character * and continue processing. */ dest[j++] = '/'; dest[j++] = '.'; dest[j++] = '.'; dest[j++] = c; continue; } /* * This is "/../" or "/..\0". We need to back up * our destination pointer until we find a "/". */ i--; while (j != 0 && dest[--j] != '/') continue; if (c == '\0') dest[++j] = '/'; } while (c != '\0'); dest[j] = '\0'; + +#if defined(sun) + if (mstate->dtms_getf != NULL && + !(mstate->dtms_access & DTRACE_ACCESS_KERNEL) && + (z = state->dts_cred.dcr_cred->cr_zone) != kcred->cr_zone) { + /* + * If we've done a getf() as a part of this ECB and we + * don't have kernel access (and we're not in the global + * zone), check if the path we cleaned up begins with + * the zone's root path, and trim it off if so. Note + * that this is an output cleanliness issue, not a + * security issue: knowing one's zone root path does + * not enable privilege escalation. + */ + if (strstr(dest, z->zone_rootpath) == dest) + dest += strlen(z->zone_rootpath) - 1; + } +#endif + regs[rd] = (uintptr_t)dest; mstate->dtms_scratch_ptr += size; break; } case DIF_SUBR_INET_NTOA: case DIF_SUBR_INET_NTOA6: case DIF_SUBR_INET_NTOP: { size_t size; int af, argi, i; char *base, *end; if (subr == DIF_SUBR_INET_NTOP) { af = (int)tupregs[0].dttk_value; argi = 1; } else { af = subr == DIF_SUBR_INET_NTOA ? AF_INET: AF_INET6; argi = 0; } if (af == AF_INET) { ipaddr_t ip4; uint8_t *ptr8, val; /* * Safely load the IPv4 address. */ ip4 = dtrace_load32(tupregs[argi].dttk_value); /* * Check an IPv4 string will fit in scratch. */ size = INET_ADDRSTRLEN; if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } base = (char *)mstate->dtms_scratch_ptr; end = (char *)mstate->dtms_scratch_ptr + size - 1; /* * Stringify as a dotted decimal quad. */ *end-- = '\0'; ptr8 = (uint8_t *)&ip4; for (i = 3; i >= 0; i--) { val = ptr8[i]; if (val == 0) { *end-- = '0'; } else { for (; val; val /= 10) { *end-- = '0' + (val % 10); } } if (i > 0) *end-- = '.'; } ASSERT(end + 1 >= base); } else if (af == AF_INET6) { struct in6_addr ip6; int firstzero, tryzero, numzero, v6end; uint16_t val; const char digits[] = "0123456789abcdef"; /* * Stringify using RFC 1884 convention 2 - 16 bit * hexadecimal values with a zero-run compression. * Lower case hexadecimal digits are used. * eg, fe80::214:4fff:fe0b:76c8. * The IPv4 embedded form is returned for inet_ntop, * just the IPv4 string is returned for inet_ntoa6. */ /* * Safely load the IPv6 address. */ dtrace_bcopy( (void *)(uintptr_t)tupregs[argi].dttk_value, (void *)(uintptr_t)&ip6, sizeof (struct in6_addr)); /* * Check an IPv6 string will fit in scratch. */ size = INET6_ADDRSTRLEN; if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } base = (char *)mstate->dtms_scratch_ptr; end = (char *)mstate->dtms_scratch_ptr + size - 1; *end-- = '\0'; /* * Find the longest run of 16 bit zero values * for the single allowed zero compression - "::". */ firstzero = -1; tryzero = -1; numzero = 1; for (i = 0; i < sizeof (struct in6_addr); i++) { #if defined(sun) if (ip6._S6_un._S6_u8[i] == 0 && #else if (ip6.__u6_addr.__u6_addr8[i] == 0 && #endif tryzero == -1 && i % 2 == 0) { tryzero = i; continue; } if (tryzero != -1 && #if defined(sun) (ip6._S6_un._S6_u8[i] != 0 || #else (ip6.__u6_addr.__u6_addr8[i] != 0 || #endif i == sizeof (struct in6_addr) - 1)) { if (i - tryzero <= numzero) { tryzero = -1; continue; } firstzero = tryzero; numzero = i - i % 2 - tryzero; tryzero = -1; #if defined(sun) if (ip6._S6_un._S6_u8[i] == 0 && #else if (ip6.__u6_addr.__u6_addr8[i] == 0 && #endif i == sizeof (struct in6_addr) - 1) numzero += 2; } } ASSERT(firstzero + numzero <= sizeof (struct in6_addr)); /* * Check for an IPv4 embedded address. */ v6end = sizeof (struct in6_addr) - 2; if (IN6_IS_ADDR_V4MAPPED(&ip6) || IN6_IS_ADDR_V4COMPAT(&ip6)) { for (i = sizeof (struct in6_addr) - 1; i >= DTRACE_V4MAPPED_OFFSET; i--) { ASSERT(end >= base); #if defined(sun) val = ip6._S6_un._S6_u8[i]; #else val = ip6.__u6_addr.__u6_addr8[i]; #endif if (val == 0) { *end-- = '0'; } else { for (; val; val /= 10) { *end-- = '0' + val % 10; } } if (i > DTRACE_V4MAPPED_OFFSET) *end-- = '.'; } if (subr == DIF_SUBR_INET_NTOA6) goto inetout; /* * Set v6end to skip the IPv4 address that * we have already stringified. */ v6end = 10; } /* * Build the IPv6 string by working through the * address in reverse. */ for (i = v6end; i >= 0; i -= 2) { ASSERT(end >= base); if (i == firstzero + numzero - 2) { *end-- = ':'; *end-- = ':'; i -= numzero - 2; continue; } if (i < 14 && i != firstzero - 2) *end-- = ':'; #if defined(sun) val = (ip6._S6_un._S6_u8[i] << 8) + ip6._S6_un._S6_u8[i + 1]; #else val = (ip6.__u6_addr.__u6_addr8[i] << 8) + ip6.__u6_addr.__u6_addr8[i + 1]; #endif if (val == 0) { *end-- = '0'; } else { for (; val; val /= 16) { *end-- = digits[val % 16]; } } } ASSERT(end + 1 >= base); } else { /* * The user didn't use AH_INET or AH_INET6. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); regs[rd] = 0; break; } inetout: regs[rd] = (uintptr_t)end + 1; mstate->dtms_scratch_ptr += size; break; } case DIF_SUBR_MEMREF: { uintptr_t size = 2 * sizeof(uintptr_t); uintptr_t *memref = (uintptr_t *) P2ROUNDUP(mstate->dtms_scratch_ptr, sizeof(uintptr_t)); size_t scratch_size = ((uintptr_t) memref - mstate->dtms_scratch_ptr) + size; /* address and length */ memref[0] = tupregs[0].dttk_value; memref[1] = tupregs[1].dttk_value; regs[rd] = (uintptr_t) memref; mstate->dtms_scratch_ptr += scratch_size; break; } case DIF_SUBR_TYPEREF: { uintptr_t size = 4 * sizeof(uintptr_t); uintptr_t *typeref = (uintptr_t *) P2ROUNDUP(mstate->dtms_scratch_ptr, sizeof(uintptr_t)); size_t scratch_size = ((uintptr_t) typeref - mstate->dtms_scratch_ptr) + size; /* address, num_elements, type_str, type_len */ typeref[0] = tupregs[0].dttk_value; typeref[1] = tupregs[1].dttk_value; typeref[2] = tupregs[2].dttk_value; typeref[3] = tupregs[3].dttk_value; regs[rd] = (uintptr_t) typeref; mstate->dtms_scratch_ptr += scratch_size; break; } } } /* * Emulate the execution of DTrace IR instructions specified by the given * DIF object. This function is deliberately void of assertions as all of * the necessary checks are handled by a call to dtrace_difo_validate(). */ static uint64_t dtrace_dif_emulate(dtrace_difo_t *difo, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate, dtrace_state_t *state) { const dif_instr_t *text = difo->dtdo_buf; const uint_t textlen = difo->dtdo_len; const char *strtab = difo->dtdo_strtab; const uint64_t *inttab = difo->dtdo_inttab; uint64_t rval = 0; dtrace_statvar_t *svar; dtrace_dstate_t *dstate = &vstate->dtvs_dynvars; dtrace_difv_t *v; volatile uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags; volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval; dtrace_key_t tupregs[DIF_DTR_NREGS + 2]; /* +2 for thread and id */ uint64_t regs[DIF_DIR_NREGS]; uint64_t *tmp; uint8_t cc_n = 0, cc_z = 0, cc_v = 0, cc_c = 0; int64_t cc_r; uint_t pc = 0, id, opc = 0; uint8_t ttop = 0; dif_instr_t instr; uint_t r1, r2, rd; /* * We stash the current DIF object into the machine state: we need it * for subsequent access checking. */ mstate->dtms_difo = difo; regs[DIF_REG_R0] = 0; /* %r0 is fixed at zero */ while (pc < textlen && !(*flags & CPU_DTRACE_FAULT)) { opc = pc; instr = text[pc++]; r1 = DIF_INSTR_R1(instr); r2 = DIF_INSTR_R2(instr); rd = DIF_INSTR_RD(instr); switch (DIF_INSTR_OP(instr)) { case DIF_OP_OR: regs[rd] = regs[r1] | regs[r2]; break; case DIF_OP_XOR: regs[rd] = regs[r1] ^ regs[r2]; break; case DIF_OP_AND: regs[rd] = regs[r1] & regs[r2]; break; case DIF_OP_SLL: regs[rd] = regs[r1] << regs[r2]; break; case DIF_OP_SRL: regs[rd] = regs[r1] >> regs[r2]; break; case DIF_OP_SUB: regs[rd] = regs[r1] - regs[r2]; break; case DIF_OP_ADD: regs[rd] = regs[r1] + regs[r2]; break; case DIF_OP_MUL: regs[rd] = regs[r1] * regs[r2]; break; case DIF_OP_SDIV: if (regs[r2] == 0) { regs[rd] = 0; *flags |= CPU_DTRACE_DIVZERO; } else { regs[rd] = (int64_t)regs[r1] / (int64_t)regs[r2]; } break; case DIF_OP_UDIV: if (regs[r2] == 0) { regs[rd] = 0; *flags |= CPU_DTRACE_DIVZERO; } else { regs[rd] = regs[r1] / regs[r2]; } break; case DIF_OP_SREM: if (regs[r2] == 0) { regs[rd] = 0; *flags |= CPU_DTRACE_DIVZERO; } else { regs[rd] = (int64_t)regs[r1] % (int64_t)regs[r2]; } break; case DIF_OP_UREM: if (regs[r2] == 0) { regs[rd] = 0; *flags |= CPU_DTRACE_DIVZERO; } else { regs[rd] = regs[r1] % regs[r2]; } break; case DIF_OP_NOT: regs[rd] = ~regs[r1]; break; case DIF_OP_MOV: regs[rd] = regs[r1]; break; case DIF_OP_CMP: cc_r = regs[r1] - regs[r2]; cc_n = cc_r < 0; cc_z = cc_r == 0; cc_v = 0; cc_c = regs[r1] < regs[r2]; break; case DIF_OP_TST: cc_n = cc_v = cc_c = 0; cc_z = regs[r1] == 0; break; case DIF_OP_BA: pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BE: if (cc_z) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BNE: if (cc_z == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BG: if ((cc_z | (cc_n ^ cc_v)) == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BGU: if ((cc_c | cc_z) == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BGE: if ((cc_n ^ cc_v) == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BGEU: if (cc_c == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BL: if (cc_n ^ cc_v) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BLU: if (cc_c) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BLE: if (cc_z | (cc_n ^ cc_v)) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BLEU: if (cc_c | cc_z) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_RLDSB: - if (!dtrace_canstore(regs[r1], 1, mstate, vstate)) { - *flags |= CPU_DTRACE_KPRIV; - *illval = regs[r1]; + if (!dtrace_canload(regs[r1], 1, mstate, vstate)) break; - } /*FALLTHROUGH*/ case DIF_OP_LDSB: regs[rd] = (int8_t)dtrace_load8(regs[r1]); break; case DIF_OP_RLDSH: - if (!dtrace_canstore(regs[r1], 2, mstate, vstate)) { - *flags |= CPU_DTRACE_KPRIV; - *illval = regs[r1]; + if (!dtrace_canload(regs[r1], 2, mstate, vstate)) break; - } /*FALLTHROUGH*/ case DIF_OP_LDSH: regs[rd] = (int16_t)dtrace_load16(regs[r1]); break; case DIF_OP_RLDSW: - if (!dtrace_canstore(regs[r1], 4, mstate, vstate)) { - *flags |= CPU_DTRACE_KPRIV; - *illval = regs[r1]; + if (!dtrace_canload(regs[r1], 4, mstate, vstate)) break; - } /*FALLTHROUGH*/ case DIF_OP_LDSW: regs[rd] = (int32_t)dtrace_load32(regs[r1]); break; case DIF_OP_RLDUB: - if (!dtrace_canstore(regs[r1], 1, mstate, vstate)) { - *flags |= CPU_DTRACE_KPRIV; - *illval = regs[r1]; + if (!dtrace_canload(regs[r1], 1, mstate, vstate)) break; - } /*FALLTHROUGH*/ case DIF_OP_LDUB: regs[rd] = dtrace_load8(regs[r1]); break; case DIF_OP_RLDUH: - if (!dtrace_canstore(regs[r1], 2, mstate, vstate)) { - *flags |= CPU_DTRACE_KPRIV; - *illval = regs[r1]; + if (!dtrace_canload(regs[r1], 2, mstate, vstate)) break; - } /*FALLTHROUGH*/ case DIF_OP_LDUH: regs[rd] = dtrace_load16(regs[r1]); break; case DIF_OP_RLDUW: - if (!dtrace_canstore(regs[r1], 4, mstate, vstate)) { - *flags |= CPU_DTRACE_KPRIV; - *illval = regs[r1]; + if (!dtrace_canload(regs[r1], 4, mstate, vstate)) break; - } /*FALLTHROUGH*/ case DIF_OP_LDUW: regs[rd] = dtrace_load32(regs[r1]); break; case DIF_OP_RLDX: - if (!dtrace_canstore(regs[r1], 8, mstate, vstate)) { - *flags |= CPU_DTRACE_KPRIV; - *illval = regs[r1]; + if (!dtrace_canload(regs[r1], 8, mstate, vstate)) break; - } /*FALLTHROUGH*/ case DIF_OP_LDX: regs[rd] = dtrace_load64(regs[r1]); break; case DIF_OP_ULDSB: + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = (int8_t) dtrace_fuword8((void *)(uintptr_t)regs[r1]); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDSH: + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = (int16_t) dtrace_fuword16((void *)(uintptr_t)regs[r1]); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDSW: + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = (int32_t) dtrace_fuword32((void *)(uintptr_t)regs[r1]); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDUB: + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = dtrace_fuword8((void *)(uintptr_t)regs[r1]); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDUH: + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = dtrace_fuword16((void *)(uintptr_t)regs[r1]); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDUW: + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = dtrace_fuword32((void *)(uintptr_t)regs[r1]); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDX: + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = dtrace_fuword64((void *)(uintptr_t)regs[r1]); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_RET: rval = regs[rd]; pc = textlen; break; case DIF_OP_NOP: break; case DIF_OP_SETX: regs[rd] = inttab[DIF_INSTR_INTEGER(instr)]; break; case DIF_OP_SETS: regs[rd] = (uint64_t)(uintptr_t) (strtab + DIF_INSTR_STRING(instr)); break; case DIF_OP_SCMP: { size_t sz = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t s1 = regs[r1]; uintptr_t s2 = regs[r2]; if (s1 != 0 && !dtrace_strcanload(s1, sz, mstate, vstate)) break; if (s2 != 0 && !dtrace_strcanload(s2, sz, mstate, vstate)) break; cc_r = dtrace_strncmp((char *)s1, (char *)s2, sz); cc_n = cc_r < 0; cc_z = cc_r == 0; cc_v = cc_c = 0; break; } case DIF_OP_LDGA: regs[rd] = dtrace_dif_variable(mstate, state, r1, regs[r2]); break; case DIF_OP_LDGS: id = DIF_INSTR_VAR(instr); if (id >= DIF_VAR_OTHER_UBASE) { uintptr_t a; id -= DIF_VAR_OTHER_UBASE; svar = vstate->dtvs_globals[id]; ASSERT(svar != NULL); v = &svar->dtsv_var; if (!(v->dtdv_type.dtdt_flags & DIF_TF_BYREF)) { regs[rd] = svar->dtsv_data; break; } a = (uintptr_t)svar->dtsv_data; if (*(uint8_t *)a == UINT8_MAX) { /* * If the 0th byte is set to UINT8_MAX * then this is to be treated as a * reference to a NULL variable. */ regs[rd] = 0; } else { regs[rd] = a + sizeof (uint64_t); } break; } regs[rd] = dtrace_dif_variable(mstate, state, id, 0); break; case DIF_OP_STGS: id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; svar = vstate->dtvs_globals[id]; ASSERT(svar != NULL); v = &svar->dtsv_var; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { uintptr_t a = (uintptr_t)svar->dtsv_data; ASSERT(a != 0); ASSERT(svar->dtsv_size != 0); if (regs[rd] == 0) { *(uint8_t *)a = UINT8_MAX; break; } else { *(uint8_t *)a = 0; a += sizeof (uint64_t); } if (!dtrace_vcanload( (void *)(uintptr_t)regs[rd], &v->dtdv_type, mstate, vstate)) break; dtrace_vcopy((void *)(uintptr_t)regs[rd], (void *)a, &v->dtdv_type); break; } svar->dtsv_data = regs[rd]; break; case DIF_OP_LDTA: /* * There are no DTrace built-in thread-local arrays at * present. This opcode is saved for future work. */ *flags |= CPU_DTRACE_ILLOP; regs[rd] = 0; break; case DIF_OP_LDLS: id = DIF_INSTR_VAR(instr); if (id < DIF_VAR_OTHER_UBASE) { /* * For now, this has no meaning. */ regs[rd] = 0; break; } id -= DIF_VAR_OTHER_UBASE; ASSERT(id < vstate->dtvs_nlocals); ASSERT(vstate->dtvs_locals != NULL); svar = vstate->dtvs_locals[id]; ASSERT(svar != NULL); v = &svar->dtsv_var; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { uintptr_t a = (uintptr_t)svar->dtsv_data; size_t sz = v->dtdv_type.dtdt_size; sz += sizeof (uint64_t); ASSERT(svar->dtsv_size == NCPU * sz); a += curcpu * sz; if (*(uint8_t *)a == UINT8_MAX) { /* * If the 0th byte is set to UINT8_MAX * then this is to be treated as a * reference to a NULL variable. */ regs[rd] = 0; } else { regs[rd] = a + sizeof (uint64_t); } break; } ASSERT(svar->dtsv_size == NCPU * sizeof (uint64_t)); tmp = (uint64_t *)(uintptr_t)svar->dtsv_data; regs[rd] = tmp[curcpu]; break; case DIF_OP_STLS: id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; ASSERT(id < vstate->dtvs_nlocals); ASSERT(vstate->dtvs_locals != NULL); svar = vstate->dtvs_locals[id]; ASSERT(svar != NULL); v = &svar->dtsv_var; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { uintptr_t a = (uintptr_t)svar->dtsv_data; size_t sz = v->dtdv_type.dtdt_size; sz += sizeof (uint64_t); ASSERT(svar->dtsv_size == NCPU * sz); a += curcpu * sz; if (regs[rd] == 0) { *(uint8_t *)a = UINT8_MAX; break; } else { *(uint8_t *)a = 0; a += sizeof (uint64_t); } if (!dtrace_vcanload( (void *)(uintptr_t)regs[rd], &v->dtdv_type, mstate, vstate)) break; dtrace_vcopy((void *)(uintptr_t)regs[rd], (void *)a, &v->dtdv_type); break; } ASSERT(svar->dtsv_size == NCPU * sizeof (uint64_t)); tmp = (uint64_t *)(uintptr_t)svar->dtsv_data; tmp[curcpu] = regs[rd]; break; case DIF_OP_LDTS: { dtrace_dynvar_t *dvar; dtrace_key_t *key; id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; v = &vstate->dtvs_tlocals[id]; key = &tupregs[DIF_DTR_NREGS]; key[0].dttk_value = (uint64_t)id; key[0].dttk_size = 0; DTRACE_TLS_THRKEY(key[1].dttk_value); key[1].dttk_size = 0; dvar = dtrace_dynvar(dstate, 2, key, sizeof (uint64_t), DTRACE_DYNVAR_NOALLOC, mstate, vstate); if (dvar == NULL) { regs[rd] = 0; break; } if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { regs[rd] = (uint64_t)(uintptr_t)dvar->dtdv_data; } else { regs[rd] = *((uint64_t *)dvar->dtdv_data); } break; } case DIF_OP_STTS: { dtrace_dynvar_t *dvar; dtrace_key_t *key; id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; key = &tupregs[DIF_DTR_NREGS]; key[0].dttk_value = (uint64_t)id; key[0].dttk_size = 0; DTRACE_TLS_THRKEY(key[1].dttk_value); key[1].dttk_size = 0; v = &vstate->dtvs_tlocals[id]; dvar = dtrace_dynvar(dstate, 2, key, v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), regs[rd] ? DTRACE_DYNVAR_ALLOC : DTRACE_DYNVAR_DEALLOC, mstate, vstate); /* * Given that we're storing to thread-local data, * we need to flush our predicate cache. */ curthread->t_predcache = 0; if (dvar == NULL) break; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { if (!dtrace_vcanload( (void *)(uintptr_t)regs[rd], &v->dtdv_type, mstate, vstate)) break; dtrace_vcopy((void *)(uintptr_t)regs[rd], dvar->dtdv_data, &v->dtdv_type); } else { *((uint64_t *)dvar->dtdv_data) = regs[rd]; } break; } case DIF_OP_SRA: regs[rd] = (int64_t)regs[r1] >> regs[r2]; break; case DIF_OP_CALL: dtrace_dif_subr(DIF_INSTR_SUBR(instr), rd, regs, tupregs, ttop, mstate, state); break; case DIF_OP_PUSHTR: if (ttop == DIF_DTR_NREGS) { *flags |= CPU_DTRACE_TUPOFLOW; break; } if (r1 == DIF_TYPE_STRING) { /* * If this is a string type and the size is 0, * we'll use the system-wide default string * size. Note that we are _not_ looking at * the value of the DTRACEOPT_STRSIZE option; * had this been set, we would expect to have * a non-zero size value in the "pushtr". */ tupregs[ttop].dttk_size = dtrace_strlen((char *)(uintptr_t)regs[rd], regs[r2] ? regs[r2] : dtrace_strsize_default) + 1; } else { tupregs[ttop].dttk_size = regs[r2]; } tupregs[ttop++].dttk_value = regs[rd]; break; case DIF_OP_PUSHTV: if (ttop == DIF_DTR_NREGS) { *flags |= CPU_DTRACE_TUPOFLOW; break; } tupregs[ttop].dttk_value = regs[rd]; tupregs[ttop++].dttk_size = 0; break; case DIF_OP_POPTS: if (ttop != 0) ttop--; break; case DIF_OP_FLUSHTS: ttop = 0; break; case DIF_OP_LDGAA: case DIF_OP_LDTAA: { dtrace_dynvar_t *dvar; dtrace_key_t *key = tupregs; uint_t nkeys = ttop; id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; key[nkeys].dttk_value = (uint64_t)id; key[nkeys++].dttk_size = 0; if (DIF_INSTR_OP(instr) == DIF_OP_LDTAA) { DTRACE_TLS_THRKEY(key[nkeys].dttk_value); key[nkeys++].dttk_size = 0; v = &vstate->dtvs_tlocals[id]; } else { v = &vstate->dtvs_globals[id]->dtsv_var; } dvar = dtrace_dynvar(dstate, nkeys, key, v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), DTRACE_DYNVAR_NOALLOC, mstate, vstate); if (dvar == NULL) { regs[rd] = 0; break; } if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { regs[rd] = (uint64_t)(uintptr_t)dvar->dtdv_data; } else { regs[rd] = *((uint64_t *)dvar->dtdv_data); } break; } case DIF_OP_STGAA: case DIF_OP_STTAA: { dtrace_dynvar_t *dvar; dtrace_key_t *key = tupregs; uint_t nkeys = ttop; id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; key[nkeys].dttk_value = (uint64_t)id; key[nkeys++].dttk_size = 0; if (DIF_INSTR_OP(instr) == DIF_OP_STTAA) { DTRACE_TLS_THRKEY(key[nkeys].dttk_value); key[nkeys++].dttk_size = 0; v = &vstate->dtvs_tlocals[id]; } else { v = &vstate->dtvs_globals[id]->dtsv_var; } dvar = dtrace_dynvar(dstate, nkeys, key, v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), regs[rd] ? DTRACE_DYNVAR_ALLOC : DTRACE_DYNVAR_DEALLOC, mstate, vstate); if (dvar == NULL) break; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { if (!dtrace_vcanload( (void *)(uintptr_t)regs[rd], &v->dtdv_type, mstate, vstate)) break; dtrace_vcopy((void *)(uintptr_t)regs[rd], dvar->dtdv_data, &v->dtdv_type); } else { *((uint64_t *)dvar->dtdv_data) = regs[rd]; } break; } case DIF_OP_ALLOCS: { uintptr_t ptr = P2ROUNDUP(mstate->dtms_scratch_ptr, 8); size_t size = ptr - mstate->dtms_scratch_ptr + regs[r1]; /* * Rounding up the user allocation size could have * overflowed large, bogus allocations (like -1ULL) to * 0. */ if (size < regs[r1] || !DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } dtrace_bzero((void *) mstate->dtms_scratch_ptr, size); mstate->dtms_scratch_ptr += size; regs[rd] = ptr; break; } case DIF_OP_COPYS: if (!dtrace_canstore(regs[rd], regs[r2], mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (!dtrace_canload(regs[r1], regs[r2], mstate, vstate)) break; dtrace_bcopy((void *)(uintptr_t)regs[r1], (void *)(uintptr_t)regs[rd], (size_t)regs[r2]); break; case DIF_OP_STB: if (!dtrace_canstore(regs[rd], 1, mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } *((uint8_t *)(uintptr_t)regs[rd]) = (uint8_t)regs[r1]; break; case DIF_OP_STH: if (!dtrace_canstore(regs[rd], 2, mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (regs[rd] & 1) { *flags |= CPU_DTRACE_BADALIGN; *illval = regs[rd]; break; } *((uint16_t *)(uintptr_t)regs[rd]) = (uint16_t)regs[r1]; break; case DIF_OP_STW: if (!dtrace_canstore(regs[rd], 4, mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (regs[rd] & 3) { *flags |= CPU_DTRACE_BADALIGN; *illval = regs[rd]; break; } *((uint32_t *)(uintptr_t)regs[rd]) = (uint32_t)regs[r1]; break; case DIF_OP_STX: if (!dtrace_canstore(regs[rd], 8, mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (regs[rd] & 7) { *flags |= CPU_DTRACE_BADALIGN; *illval = regs[rd]; break; } *((uint64_t *)(uintptr_t)regs[rd]) = regs[r1]; break; } } if (!(*flags & CPU_DTRACE_FAULT)) return (rval); mstate->dtms_fltoffs = opc * sizeof (dif_instr_t); mstate->dtms_present |= DTRACE_MSTATE_FLTOFFS; return (0); } static void dtrace_action_breakpoint(dtrace_ecb_t *ecb) { dtrace_probe_t *probe = ecb->dte_probe; dtrace_provider_t *prov = probe->dtpr_provider; char c[DTRACE_FULLNAMELEN + 80], *str; char *msg = "dtrace: breakpoint action at probe "; char *ecbmsg = " (ecb "; uintptr_t mask = (0xf << (sizeof (uintptr_t) * NBBY / 4)); uintptr_t val = (uintptr_t)ecb; int shift = (sizeof (uintptr_t) * NBBY) - 4, i = 0; if (dtrace_destructive_disallow) return; /* * It's impossible to be taking action on the NULL probe. */ ASSERT(probe != NULL); /* * This is a poor man's (destitute man's?) sprintf(): we want to * print the provider name, module name, function name and name of * the probe, along with the hex address of the ECB with the breakpoint * action -- all of which we must place in the character buffer by * hand. */ while (*msg != '\0') c[i++] = *msg++; for (str = prov->dtpv_name; *str != '\0'; str++) c[i++] = *str; c[i++] = ':'; for (str = probe->dtpr_mod; *str != '\0'; str++) c[i++] = *str; c[i++] = ':'; for (str = probe->dtpr_func; *str != '\0'; str++) c[i++] = *str; c[i++] = ':'; for (str = probe->dtpr_name; *str != '\0'; str++) c[i++] = *str; while (*ecbmsg != '\0') c[i++] = *ecbmsg++; while (shift >= 0) { mask = (uintptr_t)0xf << shift; if (val >= ((uintptr_t)1 << shift)) c[i++] = "0123456789abcdef"[(val & mask) >> shift]; shift -= 4; } c[i++] = ')'; c[i] = '\0'; #if defined(sun) debug_enter(c); #else kdb_enter(KDB_WHY_DTRACE, "breakpoint action"); #endif } static void dtrace_action_panic(dtrace_ecb_t *ecb) { dtrace_probe_t *probe = ecb->dte_probe; /* * It's impossible to be taking action on the NULL probe. */ ASSERT(probe != NULL); if (dtrace_destructive_disallow) return; if (dtrace_panicked != NULL) return; if (dtrace_casptr(&dtrace_panicked, NULL, curthread) != NULL) return; /* * We won the right to panic. (We want to be sure that only one * thread calls panic() from dtrace_probe(), and that panic() is * called exactly once.) */ dtrace_panic("dtrace: panic action at probe %s:%s:%s:%s (ecb %p)", probe->dtpr_provider->dtpv_name, probe->dtpr_mod, probe->dtpr_func, probe->dtpr_name, (void *)ecb); } static void dtrace_action_raise(uint64_t sig) { if (dtrace_destructive_disallow) return; if (sig >= NSIG) { DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return; } #if defined(sun) /* * raise() has a queue depth of 1 -- we ignore all subsequent * invocations of the raise() action. */ if (curthread->t_dtrace_sig == 0) curthread->t_dtrace_sig = (uint8_t)sig; curthread->t_sig_check = 1; aston(curthread); #else struct proc *p = curproc; PROC_LOCK(p); kern_psignal(p, sig); PROC_UNLOCK(p); #endif } static void dtrace_action_stop(void) { if (dtrace_destructive_disallow) return; #if defined(sun) if (!curthread->t_dtrace_stop) { curthread->t_dtrace_stop = 1; curthread->t_sig_check = 1; aston(curthread); } #else struct proc *p = curproc; PROC_LOCK(p); kern_psignal(p, SIGSTOP); PROC_UNLOCK(p); #endif } static void dtrace_action_chill(dtrace_mstate_t *mstate, hrtime_t val) { hrtime_t now; volatile uint16_t *flags; #if defined(sun) cpu_t *cpu = CPU; #else cpu_t *cpu = &solaris_cpu[curcpu]; #endif if (dtrace_destructive_disallow) return; - flags = (volatile uint16_t *)&cpu_core[cpu->cpu_id].cpuc_dtrace_flags; + flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; now = dtrace_gethrtime(); if (now - cpu->cpu_dtrace_chillmark > dtrace_chill_interval) { /* * We need to advance the mark to the current time. */ cpu->cpu_dtrace_chillmark = now; cpu->cpu_dtrace_chilled = 0; } /* * Now check to see if the requested chill time would take us over * the maximum amount of time allowed in the chill interval. (Or * worse, if the calculation itself induces overflow.) */ if (cpu->cpu_dtrace_chilled + val > dtrace_chill_max || cpu->cpu_dtrace_chilled + val < cpu->cpu_dtrace_chilled) { *flags |= CPU_DTRACE_ILLOP; return; } while (dtrace_gethrtime() - now < val) continue; /* * Normally, we assure that the value of the variable "timestamp" does * not change within an ECB. The presence of chill() represents an * exception to this rule, however. */ mstate->dtms_present &= ~DTRACE_MSTATE_TIMESTAMP; cpu->cpu_dtrace_chilled += val; } static void dtrace_action_ustack(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t *buf, uint64_t arg) { int nframes = DTRACE_USTACK_NFRAMES(arg); int strsize = DTRACE_USTACK_STRSIZE(arg); uint64_t *pcs = &buf[1], *fps; char *str = (char *)&pcs[nframes]; int size, offs = 0, i, j; uintptr_t old = mstate->dtms_scratch_ptr, saved; uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags; char *sym; /* * Should be taking a faster path if string space has not been * allocated. */ ASSERT(strsize != 0); /* * We will first allocate some temporary space for the frame pointers. */ fps = (uint64_t *)P2ROUNDUP(mstate->dtms_scratch_ptr, 8); size = (uintptr_t)fps - mstate->dtms_scratch_ptr + (nframes * sizeof (uint64_t)); if (!DTRACE_INSCRATCH(mstate, size)) { /* * Not enough room for our frame pointers -- need to indicate * that we ran out of scratch space. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); return; } mstate->dtms_scratch_ptr += size; saved = mstate->dtms_scratch_ptr; /* * Now get a stack with both program counters and frame pointers. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_getufpstack(buf, fps, nframes + 1); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); /* * If that faulted, we're cooked. */ if (*flags & CPU_DTRACE_FAULT) goto out; /* * Now we want to walk up the stack, calling the USTACK helper. For * each iteration, we restore the scratch pointer. */ for (i = 0; i < nframes; i++) { mstate->dtms_scratch_ptr = saved; if (offs >= strsize) break; sym = (char *)(uintptr_t)dtrace_helper( DTRACE_HELPER_ACTION_USTACK, mstate, state, pcs[i], fps[i]); /* * If we faulted while running the helper, we're going to * clear the fault and null out the corresponding string. */ if (*flags & CPU_DTRACE_FAULT) { *flags &= ~CPU_DTRACE_FAULT; str[offs++] = '\0'; continue; } if (sym == NULL) { str[offs++] = '\0'; continue; } DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); /* * Now copy in the string that the helper returned to us. */ for (j = 0; offs + j < strsize; j++) { if ((str[offs + j] = sym[j]) == '\0') break; } DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); offs += j + 1; } if (offs >= strsize) { /* * If we didn't have room for all of the strings, we don't * abort processing -- this needn't be a fatal error -- but we * still want to increment a counter (dts_stkstroverflows) to * allow this condition to be warned about. (If this is from * a jstack() action, it is easily tuned via jstackstrsize.) */ dtrace_error(&state->dts_stkstroverflows); } while (offs < strsize) str[offs++] = '\0'; out: mstate->dtms_scratch_ptr = old; } +static void +dtrace_store_by_ref(dtrace_difo_t *dp, caddr_t tomax, size_t size, + size_t *valoffsp, uint64_t *valp, uint64_t end, int intuple, int dtkind) +{ + volatile uint16_t *flags; + uint64_t val = *valp; + size_t valoffs = *valoffsp; + + flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; + ASSERT(dtkind == DIF_TF_BYREF || dtkind == DIF_TF_BYUREF); + + /* + * If this is a string, we're going to only load until we find the zero + * byte -- after which we'll store zero bytes. + */ + if (dp->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) { + char c = '\0' + 1; + size_t s; + + for (s = 0; s < size; s++) { + if (c != '\0' && dtkind == DIF_TF_BYREF) { + c = dtrace_load8(val++); + } else if (c != '\0' && dtkind == DIF_TF_BYUREF) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); + c = dtrace_fuword8((void *)(uintptr_t)val++); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); + if (*flags & CPU_DTRACE_FAULT) + break; + } + + DTRACE_STORE(uint8_t, tomax, valoffs++, c); + + if (c == '\0' && intuple) + break; + } + } else { + uint8_t c; + while (valoffs < end) { + if (dtkind == DIF_TF_BYREF) { + c = dtrace_load8(val++); + } else if (dtkind == DIF_TF_BYUREF) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); + c = dtrace_fuword8((void *)(uintptr_t)val++); + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); + if (*flags & CPU_DTRACE_FAULT) + break; + } + + DTRACE_STORE(uint8_t, tomax, + valoffs++, c); + } + } + + *valp = val; + *valoffsp = valoffs; +} + /* * If you're looking for the epicenter of DTrace, you just found it. This * is the function called by the provider to fire a probe -- from which all * subsequent probe-context DTrace activity emanates. */ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4) { processorid_t cpuid; dtrace_icookie_t cookie; dtrace_probe_t *probe; dtrace_mstate_t mstate; dtrace_ecb_t *ecb; dtrace_action_t *act; intptr_t offs; size_t size; int vtime, onintr; volatile uint16_t *flags; hrtime_t now; if (panicstr != NULL) return; #if defined(sun) /* * Kick out immediately if this CPU is still being born (in which case * curthread will be set to -1) or the current thread can't allow * probes in its current context. */ if (((uintptr_t)curthread & 1) || (curthread->t_flag & T_DONTDTRACE)) return; #endif cookie = dtrace_interrupt_disable(); probe = dtrace_probes[id - 1]; cpuid = curcpu; onintr = CPU_ON_INTR(CPU); if (!onintr && probe->dtpr_predcache != DTRACE_CACHEIDNONE && probe->dtpr_predcache == curthread->t_predcache) { /* * We have hit in the predicate cache; we know that * this predicate would evaluate to be false. */ dtrace_interrupt_enable(cookie); return; } #if defined(sun) if (panic_quiesce) { #else if (panicstr != NULL) { #endif /* * We don't trace anything if we're panicking. */ dtrace_interrupt_enable(cookie); return; } now = dtrace_gethrtime(); vtime = dtrace_vtime_references != 0; if (vtime && curthread->t_dtrace_start) curthread->t_dtrace_vtime += now - curthread->t_dtrace_start; mstate.dtms_difo = NULL; mstate.dtms_probe = probe; mstate.dtms_strtok = 0; mstate.dtms_arg[0] = arg0; mstate.dtms_arg[1] = arg1; mstate.dtms_arg[2] = arg2; mstate.dtms_arg[3] = arg3; mstate.dtms_arg[4] = arg4; flags = (volatile uint16_t *)&cpu_core[cpuid].cpuc_dtrace_flags; for (ecb = probe->dtpr_ecb; ecb != NULL; ecb = ecb->dte_next) { dtrace_predicate_t *pred = ecb->dte_predicate; dtrace_state_t *state = ecb->dte_state; dtrace_buffer_t *buf = &state->dts_buffer[cpuid]; dtrace_buffer_t *aggbuf = &state->dts_aggbuffer[cpuid]; dtrace_vstate_t *vstate = &state->dts_vstate; dtrace_provider_t *prov = probe->dtpr_provider; uint64_t tracememsize = 0; int committed = 0; caddr_t tomax; /* * A little subtlety with the following (seemingly innocuous) * declaration of the automatic 'val': by looking at the * code, you might think that it could be declared in the * action processing loop, below. (That is, it's only used in * the action processing loop.) However, it must be declared * out of that scope because in the case of DIF expression * arguments to aggregating actions, one iteration of the * action loop will use the last iteration's value. */ uint64_t val = 0; mstate.dtms_present = DTRACE_MSTATE_ARGS | DTRACE_MSTATE_PROBE; + mstate.dtms_getf = NULL; + *flags &= ~CPU_DTRACE_ERROR; if (prov == dtrace_provider) { /* * If dtrace itself is the provider of this probe, * we're only going to continue processing the ECB if * arg0 (the dtrace_state_t) is equal to the ECB's * creating state. (This prevents disjoint consumers * from seeing one another's metaprobes.) */ if (arg0 != (uint64_t)(uintptr_t)state) continue; } if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE) { /* * We're not currently active. If our provider isn't * the dtrace pseudo provider, we're not interested. */ if (prov != dtrace_provider) continue; /* * Now we must further check if we are in the BEGIN * probe. If we are, we will only continue processing * if we're still in WARMUP -- if one BEGIN enabling * has invoked the exit() action, we don't want to * evaluate subsequent BEGIN enablings. */ if (probe->dtpr_id == dtrace_probeid_begin && state->dts_activity != DTRACE_ACTIVITY_WARMUP) { ASSERT(state->dts_activity == DTRACE_ACTIVITY_DRAINING); continue; } } if (ecb->dte_cond) { /* * If the dte_cond bits indicate that this * consumer is only allowed to see user-mode firings * of this probe, call the provider's dtps_usermode() * entry point to check that the probe was fired * while in a user context. Skip this ECB if that's * not the case. */ if ((ecb->dte_cond & DTRACE_COND_USERMODE) && prov->dtpv_pops.dtps_usermode(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg) == 0) continue; #if defined(sun) /* * This is more subtle than it looks. We have to be * absolutely certain that CRED() isn't going to * change out from under us so it's only legit to * examine that structure if we're in constrained * situations. Currently, the only times we'll this * check is if a non-super-user has enabled the * profile or syscall providers -- providers that * allow visibility of all processes. For the * profile case, the check above will ensure that * we're examining a user context. */ if (ecb->dte_cond & DTRACE_COND_OWNER) { cred_t *cr; cred_t *s_cr = ecb->dte_state->dts_cred.dcr_cred; proc_t *proc; ASSERT(s_cr != NULL); if ((cr = CRED()) == NULL || s_cr->cr_uid != cr->cr_uid || s_cr->cr_uid != cr->cr_ruid || s_cr->cr_uid != cr->cr_suid || s_cr->cr_gid != cr->cr_gid || s_cr->cr_gid != cr->cr_rgid || s_cr->cr_gid != cr->cr_sgid || (proc = ttoproc(curthread)) == NULL || (proc->p_flag & SNOCD)) continue; } if (ecb->dte_cond & DTRACE_COND_ZONEOWNER) { cred_t *cr; cred_t *s_cr = ecb->dte_state->dts_cred.dcr_cred; ASSERT(s_cr != NULL); if ((cr = CRED()) == NULL || s_cr->cr_zone->zone_id != cr->cr_zone->zone_id) continue; } #endif } if (now - state->dts_alive > dtrace_deadman_timeout) { /* * We seem to be dead. Unless we (a) have kernel * destructive permissions (b) have explicitly enabled * destructive actions and (c) destructive actions have * not been disabled, we're going to transition into * the KILLED state, from which no further processing * on this state will be performed. */ if (!dtrace_priv_kernel_destructive(state) || !state->dts_cred.dcr_destructive || dtrace_destructive_disallow) { void *activity = &state->dts_activity; dtrace_activity_t current; do { current = state->dts_activity; } while (dtrace_cas32(activity, current, DTRACE_ACTIVITY_KILLED) != current); continue; } } if ((offs = dtrace_buffer_reserve(buf, ecb->dte_needed, ecb->dte_alignment, state, &mstate)) < 0) continue; tomax = buf->dtb_tomax; ASSERT(tomax != NULL); if (ecb->dte_size != 0) { dtrace_rechdr_t dtrh; if (!(mstate.dtms_present & DTRACE_MSTATE_TIMESTAMP)) { mstate.dtms_timestamp = dtrace_gethrtime(); mstate.dtms_present |= DTRACE_MSTATE_TIMESTAMP; } ASSERT3U(ecb->dte_size, >=, sizeof (dtrace_rechdr_t)); dtrh.dtrh_epid = ecb->dte_epid; DTRACE_RECORD_STORE_TIMESTAMP(&dtrh, mstate.dtms_timestamp); *((dtrace_rechdr_t *)(tomax + offs)) = dtrh; } mstate.dtms_epid = ecb->dte_epid; mstate.dtms_present |= DTRACE_MSTATE_EPID; if (state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) mstate.dtms_access = DTRACE_ACCESS_KERNEL; else mstate.dtms_access = 0; if (pred != NULL) { dtrace_difo_t *dp = pred->dtp_difo; int rval; rval = dtrace_dif_emulate(dp, &mstate, vstate, state); if (!(*flags & CPU_DTRACE_ERROR) && !rval) { dtrace_cacheid_t cid = probe->dtpr_predcache; if (cid != DTRACE_CACHEIDNONE && !onintr) { /* * Update the predicate cache... */ ASSERT(cid == pred->dtp_cacheid); curthread->t_predcache = cid; } continue; } } for (act = ecb->dte_action; !(*flags & CPU_DTRACE_ERROR) && act != NULL; act = act->dta_next) { size_t valoffs; dtrace_difo_t *dp; dtrace_recdesc_t *rec = &act->dta_rec; size = rec->dtrd_size; valoffs = offs + rec->dtrd_offset; if (DTRACEACT_ISAGG(act->dta_kind)) { uint64_t v = 0xbad; dtrace_aggregation_t *agg; agg = (dtrace_aggregation_t *)act; if ((dp = act->dta_difo) != NULL) v = dtrace_dif_emulate(dp, &mstate, vstate, state); if (*flags & CPU_DTRACE_ERROR) continue; /* * Note that we always pass the expression * value from the previous iteration of the * action loop. This value will only be used * if there is an expression argument to the * aggregating action, denoted by the * dtag_hasarg field. */ dtrace_aggregate(agg, buf, offs, aggbuf, v, val); continue; } switch (act->dta_kind) { case DTRACEACT_STOP: if (dtrace_priv_proc_destructive(state)) dtrace_action_stop(); continue; case DTRACEACT_BREAKPOINT: if (dtrace_priv_kernel_destructive(state)) dtrace_action_breakpoint(ecb); continue; case DTRACEACT_PANIC: if (dtrace_priv_kernel_destructive(state)) dtrace_action_panic(ecb); continue; case DTRACEACT_STACK: if (!dtrace_priv_kernel(state)) continue; dtrace_getpcstack((pc_t *)(tomax + valoffs), size / sizeof (pc_t), probe->dtpr_aframes, DTRACE_ANCHORED(probe) ? NULL : (uint32_t *)arg0); continue; case DTRACEACT_JSTACK: case DTRACEACT_USTACK: if (!dtrace_priv_proc(state)) continue; /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate.dtms_probe) && CPU_ON_INTR(CPU)) { int depth = DTRACE_USTACK_NFRAMES( rec->dtrd_arg) + 1; dtrace_bzero((void *)(tomax + valoffs), DTRACE_USTACK_STRSIZE(rec->dtrd_arg) + depth * sizeof (uint64_t)); continue; } if (DTRACE_USTACK_STRSIZE(rec->dtrd_arg) != 0 && curproc->p_dtrace_helpers != NULL) { /* * This is the slow path -- we have * allocated string space, and we're * getting the stack of a process that * has helpers. Call into a separate * routine to perform this processing. */ dtrace_action_ustack(&mstate, state, (uint64_t *)(tomax + valoffs), rec->dtrd_arg); continue; } DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_getupcstack((uint64_t *) (tomax + valoffs), DTRACE_USTACK_NFRAMES(rec->dtrd_arg) + 1); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); continue; default: break; } dp = act->dta_difo; ASSERT(dp != NULL); val = dtrace_dif_emulate(dp, &mstate, vstate, state); if (*flags & CPU_DTRACE_ERROR) continue; switch (act->dta_kind) { case DTRACEACT_SPECULATE: { dtrace_rechdr_t *dtrh; ASSERT(buf == &state->dts_buffer[cpuid]); buf = dtrace_speculation_buffer(state, cpuid, val); if (buf == NULL) { *flags |= CPU_DTRACE_DROP; continue; } offs = dtrace_buffer_reserve(buf, ecb->dte_needed, ecb->dte_alignment, state, NULL); if (offs < 0) { *flags |= CPU_DTRACE_DROP; continue; } tomax = buf->dtb_tomax; ASSERT(tomax != NULL); if (ecb->dte_size == 0) continue; ASSERT3U(ecb->dte_size, >=, sizeof (dtrace_rechdr_t)); dtrh = ((void *)(tomax + offs)); dtrh->dtrh_epid = ecb->dte_epid; /* * When the speculation is committed, all of * the records in the speculative buffer will * have their timestamps set to the commit * time. Until then, it is set to a sentinel * value, for debugability. */ DTRACE_RECORD_STORE_TIMESTAMP(dtrh, UINT64_MAX); continue; } case DTRACEACT_PRINTM: { /* The DIF returns a 'memref'. */ uintptr_t *memref = (uintptr_t *)(uintptr_t) val; /* Get the size from the memref. */ size = memref[1]; /* * Check if the size exceeds the allocated * buffer size. */ if (size + sizeof(uintptr_t) > dp->dtdo_rtype.dtdt_size) { /* Flag a drop! */ *flags |= CPU_DTRACE_DROP; continue; } /* Store the size in the buffer first. */ DTRACE_STORE(uintptr_t, tomax, valoffs, size); /* * Offset the buffer address to the start * of the data. */ valoffs += sizeof(uintptr_t); /* * Reset to the memory address rather than * the memref array, then let the BYREF * code below do the work to store the * memory data in the buffer. */ val = memref[0]; break; } case DTRACEACT_PRINTT: { /* The DIF returns a 'typeref'. */ uintptr_t *typeref = (uintptr_t *)(uintptr_t) val; char c = '\0' + 1; size_t s; /* * Get the type string length and round it * up so that the data that follows is * aligned for easy access. */ size_t typs = strlen((char *) typeref[2]) + 1; typs = roundup(typs, sizeof(uintptr_t)); /* *Get the size from the typeref using the * number of elements and the type size. */ size = typeref[1] * typeref[3]; /* * Check if the size exceeds the allocated * buffer size. */ if (size + typs + 2 * sizeof(uintptr_t) > dp->dtdo_rtype.dtdt_size) { /* Flag a drop! */ *flags |= CPU_DTRACE_DROP; } /* Store the size in the buffer first. */ DTRACE_STORE(uintptr_t, tomax, valoffs, size); valoffs += sizeof(uintptr_t); /* Store the type size in the buffer. */ DTRACE_STORE(uintptr_t, tomax, valoffs, typeref[3]); valoffs += sizeof(uintptr_t); val = typeref[2]; for (s = 0; s < typs; s++) { if (c != '\0') c = dtrace_load8(val++); DTRACE_STORE(uint8_t, tomax, valoffs++, c); } /* * Reset to the memory address rather than * the typeref array, then let the BYREF * code below do the work to store the * memory data in the buffer. */ val = typeref[0]; break; } case DTRACEACT_CHILL: if (dtrace_priv_kernel_destructive(state)) dtrace_action_chill(&mstate, val); continue; case DTRACEACT_RAISE: if (dtrace_priv_proc_destructive(state)) dtrace_action_raise(val); continue; case DTRACEACT_COMMIT: ASSERT(!committed); /* * We need to commit our buffer state. */ if (ecb->dte_size) buf->dtb_offset = offs + ecb->dte_size; buf = &state->dts_buffer[cpuid]; dtrace_speculation_commit(state, cpuid, val); committed = 1; continue; case DTRACEACT_DISCARD: dtrace_speculation_discard(state, cpuid, val); continue; case DTRACEACT_DIFEXPR: case DTRACEACT_LIBACT: case DTRACEACT_PRINTF: case DTRACEACT_PRINTA: case DTRACEACT_SYSTEM: case DTRACEACT_FREOPEN: case DTRACEACT_TRACEMEM: break; case DTRACEACT_TRACEMEM_DYNSIZE: tracememsize = val; break; case DTRACEACT_SYM: case DTRACEACT_MOD: if (!dtrace_priv_kernel(state)) continue; break; case DTRACEACT_USYM: case DTRACEACT_UMOD: case DTRACEACT_UADDR: { #if defined(sun) struct pid *pid = curthread->t_procp->p_pidp; #endif if (!dtrace_priv_proc(state)) continue; DTRACE_STORE(uint64_t, tomax, #if defined(sun) valoffs, (uint64_t)pid->pid_id); #else valoffs, (uint64_t) curproc->p_pid); #endif DTRACE_STORE(uint64_t, tomax, valoffs + sizeof (uint64_t), val); continue; } case DTRACEACT_EXIT: { /* * For the exit action, we are going to attempt * to atomically set our activity to be * draining. If this fails (either because * another CPU has beat us to the exit action, * or because our current activity is something * other than ACTIVE or WARMUP), we will * continue. This assures that the exit action * can be successfully recorded at most once * when we're in the ACTIVE state. If we're * encountering the exit() action while in * COOLDOWN, however, we want to honor the new * status code. (We know that we're the only * thread in COOLDOWN, so there is no race.) */ void *activity = &state->dts_activity; dtrace_activity_t current = state->dts_activity; if (current == DTRACE_ACTIVITY_COOLDOWN) break; if (current != DTRACE_ACTIVITY_WARMUP) current = DTRACE_ACTIVITY_ACTIVE; if (dtrace_cas32(activity, current, DTRACE_ACTIVITY_DRAINING) != current) { *flags |= CPU_DTRACE_DROP; continue; } break; } default: ASSERT(0); } - if (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF) { + if (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF || + dp->dtdo_rtype.dtdt_flags & DIF_TF_BYUREF) { uintptr_t end = valoffs + size; if (tracememsize != 0 && valoffs + tracememsize < end) { end = valoffs + tracememsize; tracememsize = 0; } - if (!dtrace_vcanload((void *)(uintptr_t)val, + if (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF && + !dtrace_vcanload((void *)(uintptr_t)val, &dp->dtdo_rtype, &mstate, vstate)) continue; - /* - * If this is a string, we're going to only - * load until we find the zero byte -- after - * which we'll store zero bytes. - */ - if (dp->dtdo_rtype.dtdt_kind == - DIF_TYPE_STRING) { - char c = '\0' + 1; - int intuple = act->dta_intuple; - size_t s; - - for (s = 0; s < size; s++) { - if (c != '\0') - c = dtrace_load8(val++); - - DTRACE_STORE(uint8_t, tomax, - valoffs++, c); - - if (c == '\0' && intuple) - break; - } - - continue; - } - - while (valoffs < end) { - DTRACE_STORE(uint8_t, tomax, valoffs++, - dtrace_load8(val++)); - } - + dtrace_store_by_ref(dp, tomax, size, &valoffs, + &val, end, act->dta_intuple, + dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF ? + DIF_TF_BYREF: DIF_TF_BYUREF); continue; } switch (size) { case 0: break; case sizeof (uint8_t): DTRACE_STORE(uint8_t, tomax, valoffs, val); break; case sizeof (uint16_t): DTRACE_STORE(uint16_t, tomax, valoffs, val); break; case sizeof (uint32_t): DTRACE_STORE(uint32_t, tomax, valoffs, val); break; case sizeof (uint64_t): DTRACE_STORE(uint64_t, tomax, valoffs, val); break; default: /* * Any other size should have been returned by * reference, not by value. */ ASSERT(0); break; } } if (*flags & CPU_DTRACE_DROP) continue; if (*flags & CPU_DTRACE_FAULT) { int ndx; dtrace_action_t *err; buf->dtb_errors++; if (probe->dtpr_id == dtrace_probeid_error) { /* * There's nothing we can do -- we had an * error on the error probe. We bump an * error counter to at least indicate that * this condition happened. */ dtrace_error(&state->dts_dblerrors); continue; } if (vtime) { /* * Before recursing on dtrace_probe(), we * need to explicitly clear out our start * time to prevent it from being accumulated * into t_dtrace_vtime. */ curthread->t_dtrace_start = 0; } /* * Iterate over the actions to figure out which action * we were processing when we experienced the error. * Note that act points _past_ the faulting action; if * act is ecb->dte_action, the fault was in the * predicate, if it's ecb->dte_action->dta_next it's * in action #1, and so on. */ for (err = ecb->dte_action, ndx = 0; err != act; err = err->dta_next, ndx++) continue; dtrace_probe_error(state, ecb->dte_epid, ndx, (mstate.dtms_present & DTRACE_MSTATE_FLTOFFS) ? mstate.dtms_fltoffs : -1, DTRACE_FLAGS2FLT(*flags), cpu_core[cpuid].cpuc_dtrace_illval); continue; } if (!committed) buf->dtb_offset = offs + ecb->dte_size; } if (vtime) curthread->t_dtrace_start = dtrace_gethrtime(); dtrace_interrupt_enable(cookie); } /* * DTrace Probe Hashing Functions * * The functions in this section (and indeed, the functions in remaining * sections) are not _called_ from probe context. (Any exceptions to this are * marked with a "Note:".) Rather, they are called from elsewhere in the * DTrace framework to look-up probes in, add probes to and remove probes from * the DTrace probe hashes. (Each probe is hashed by each element of the * probe tuple -- allowing for fast lookups, regardless of what was * specified.) */ static uint_t dtrace_hash_str(const char *p) { unsigned int g; uint_t hval = 0; while (*p) { hval = (hval << 4) + *p++; if ((g = (hval & 0xf0000000)) != 0) hval ^= g >> 24; hval &= ~g; } return (hval); } static dtrace_hash_t * dtrace_hash_create(uintptr_t stroffs, uintptr_t nextoffs, uintptr_t prevoffs) { dtrace_hash_t *hash = kmem_zalloc(sizeof (dtrace_hash_t), KM_SLEEP); hash->dth_stroffs = stroffs; hash->dth_nextoffs = nextoffs; hash->dth_prevoffs = prevoffs; hash->dth_size = 1; hash->dth_mask = hash->dth_size - 1; hash->dth_tab = kmem_zalloc(hash->dth_size * sizeof (dtrace_hashbucket_t *), KM_SLEEP); return (hash); } static void dtrace_hash_destroy(dtrace_hash_t *hash) { #ifdef DEBUG int i; for (i = 0; i < hash->dth_size; i++) ASSERT(hash->dth_tab[i] == NULL); #endif kmem_free(hash->dth_tab, hash->dth_size * sizeof (dtrace_hashbucket_t *)); kmem_free(hash, sizeof (dtrace_hash_t)); } static void dtrace_hash_resize(dtrace_hash_t *hash) { int size = hash->dth_size, i, ndx; int new_size = hash->dth_size << 1; int new_mask = new_size - 1; dtrace_hashbucket_t **new_tab, *bucket, *next; ASSERT((new_size & new_mask) == 0); new_tab = kmem_zalloc(new_size * sizeof (void *), KM_SLEEP); for (i = 0; i < size; i++) { for (bucket = hash->dth_tab[i]; bucket != NULL; bucket = next) { dtrace_probe_t *probe = bucket->dthb_chain; ASSERT(probe != NULL); ndx = DTRACE_HASHSTR(hash, probe) & new_mask; next = bucket->dthb_next; bucket->dthb_next = new_tab[ndx]; new_tab[ndx] = bucket; } } kmem_free(hash->dth_tab, hash->dth_size * sizeof (void *)); hash->dth_tab = new_tab; hash->dth_size = new_size; hash->dth_mask = new_mask; } static void dtrace_hash_add(dtrace_hash_t *hash, dtrace_probe_t *new) { int hashval = DTRACE_HASHSTR(hash, new); int ndx = hashval & hash->dth_mask; dtrace_hashbucket_t *bucket = hash->dth_tab[ndx]; dtrace_probe_t **nextp, **prevp; for (; bucket != NULL; bucket = bucket->dthb_next) { if (DTRACE_HASHEQ(hash, bucket->dthb_chain, new)) goto add; } if ((hash->dth_nbuckets >> 1) > hash->dth_size) { dtrace_hash_resize(hash); dtrace_hash_add(hash, new); return; } bucket = kmem_zalloc(sizeof (dtrace_hashbucket_t), KM_SLEEP); bucket->dthb_next = hash->dth_tab[ndx]; hash->dth_tab[ndx] = bucket; hash->dth_nbuckets++; add: nextp = DTRACE_HASHNEXT(hash, new); ASSERT(*nextp == NULL && *(DTRACE_HASHPREV(hash, new)) == NULL); *nextp = bucket->dthb_chain; if (bucket->dthb_chain != NULL) { prevp = DTRACE_HASHPREV(hash, bucket->dthb_chain); ASSERT(*prevp == NULL); *prevp = new; } bucket->dthb_chain = new; bucket->dthb_len++; } static dtrace_probe_t * dtrace_hash_lookup(dtrace_hash_t *hash, dtrace_probe_t *template) { int hashval = DTRACE_HASHSTR(hash, template); int ndx = hashval & hash->dth_mask; dtrace_hashbucket_t *bucket = hash->dth_tab[ndx]; for (; bucket != NULL; bucket = bucket->dthb_next) { if (DTRACE_HASHEQ(hash, bucket->dthb_chain, template)) return (bucket->dthb_chain); } return (NULL); } static int dtrace_hash_collisions(dtrace_hash_t *hash, dtrace_probe_t *template) { int hashval = DTRACE_HASHSTR(hash, template); int ndx = hashval & hash->dth_mask; dtrace_hashbucket_t *bucket = hash->dth_tab[ndx]; for (; bucket != NULL; bucket = bucket->dthb_next) { if (DTRACE_HASHEQ(hash, bucket->dthb_chain, template)) return (bucket->dthb_len); } return (0); } static void dtrace_hash_remove(dtrace_hash_t *hash, dtrace_probe_t *probe) { int ndx = DTRACE_HASHSTR(hash, probe) & hash->dth_mask; dtrace_hashbucket_t *bucket = hash->dth_tab[ndx]; dtrace_probe_t **prevp = DTRACE_HASHPREV(hash, probe); dtrace_probe_t **nextp = DTRACE_HASHNEXT(hash, probe); /* * Find the bucket that we're removing this probe from. */ for (; bucket != NULL; bucket = bucket->dthb_next) { if (DTRACE_HASHEQ(hash, bucket->dthb_chain, probe)) break; } ASSERT(bucket != NULL); if (*prevp == NULL) { if (*nextp == NULL) { /* * The removed probe was the only probe on this * bucket; we need to remove the bucket. */ dtrace_hashbucket_t *b = hash->dth_tab[ndx]; ASSERT(bucket->dthb_chain == probe); ASSERT(b != NULL); if (b == bucket) { hash->dth_tab[ndx] = bucket->dthb_next; } else { while (b->dthb_next != bucket) b = b->dthb_next; b->dthb_next = bucket->dthb_next; } ASSERT(hash->dth_nbuckets > 0); hash->dth_nbuckets--; kmem_free(bucket, sizeof (dtrace_hashbucket_t)); return; } bucket->dthb_chain = *nextp; } else { *(DTRACE_HASHNEXT(hash, *prevp)) = *nextp; } if (*nextp != NULL) *(DTRACE_HASHPREV(hash, *nextp)) = *prevp; } /* * DTrace Utility Functions * * These are random utility functions that are _not_ called from probe context. */ static int dtrace_badattr(const dtrace_attribute_t *a) { return (a->dtat_name > DTRACE_STABILITY_MAX || a->dtat_data > DTRACE_STABILITY_MAX || a->dtat_class > DTRACE_CLASS_MAX); } /* * Return a duplicate copy of a string. If the specified string is NULL, * this function returns a zero-length string. */ static char * dtrace_strdup(const char *str) { char *new = kmem_zalloc((str != NULL ? strlen(str) : 0) + 1, KM_SLEEP); if (str != NULL) (void) strcpy(new, str); return (new); } #define DTRACE_ISALPHA(c) \ (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) static int dtrace_badname(const char *s) { char c; if (s == NULL || (c = *s++) == '\0') return (0); if (!DTRACE_ISALPHA(c) && c != '-' && c != '_' && c != '.') return (1); while ((c = *s++) != '\0') { if (!DTRACE_ISALPHA(c) && (c < '0' || c > '9') && c != '-' && c != '_' && c != '.' && c != '`') return (1); } return (0); } static void dtrace_cred2priv(cred_t *cr, uint32_t *privp, uid_t *uidp, zoneid_t *zoneidp) { uint32_t priv; #if defined(sun) if (cr == NULL || PRIV_POLICY_ONLY(cr, PRIV_ALL, B_FALSE)) { /* * For DTRACE_PRIV_ALL, the uid and zoneid don't matter. */ priv = DTRACE_PRIV_ALL; } else { *uidp = crgetuid(cr); *zoneidp = crgetzoneid(cr); priv = 0; if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE)) priv |= DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER; else if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE)) priv |= DTRACE_PRIV_USER; if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE)) priv |= DTRACE_PRIV_PROC; if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) priv |= DTRACE_PRIV_OWNER; if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) priv |= DTRACE_PRIV_ZONEOWNER; } #else priv = DTRACE_PRIV_ALL; #endif *privp = priv; } #ifdef DTRACE_ERRDEBUG static void dtrace_errdebug(const char *str) { int hval = dtrace_hash_str(str) % DTRACE_ERRHASHSZ; int occupied = 0; mutex_enter(&dtrace_errlock); dtrace_errlast = str; dtrace_errthread = curthread; while (occupied++ < DTRACE_ERRHASHSZ) { if (dtrace_errhash[hval].dter_msg == str) { dtrace_errhash[hval].dter_count++; goto out; } if (dtrace_errhash[hval].dter_msg != NULL) { hval = (hval + 1) % DTRACE_ERRHASHSZ; continue; } dtrace_errhash[hval].dter_msg = str; dtrace_errhash[hval].dter_count = 1; goto out; } panic("dtrace: undersized error hash"); out: mutex_exit(&dtrace_errlock); } #endif /* * DTrace Matching Functions * * These functions are used to match groups of probes, given some elements of * a probe tuple, or some globbed expressions for elements of a probe tuple. */ static int dtrace_match_priv(const dtrace_probe_t *prp, uint32_t priv, uid_t uid, zoneid_t zoneid) { if (priv != DTRACE_PRIV_ALL) { uint32_t ppriv = prp->dtpr_provider->dtpv_priv.dtpp_flags; uint32_t match = priv & ppriv; /* * No PRIV_DTRACE_* privileges... */ if ((priv & (DTRACE_PRIV_PROC | DTRACE_PRIV_USER | DTRACE_PRIV_KERNEL)) == 0) return (0); /* * No matching bits, but there were bits to match... */ if (match == 0 && ppriv != 0) return (0); /* * Need to have permissions to the process, but don't... */ if (((ppriv & ~match) & DTRACE_PRIV_OWNER) != 0 && uid != prp->dtpr_provider->dtpv_priv.dtpp_uid) { return (0); } /* * Need to be in the same zone unless we possess the * privilege to examine all zones. */ if (((ppriv & ~match) & DTRACE_PRIV_ZONEOWNER) != 0 && zoneid != prp->dtpr_provider->dtpv_priv.dtpp_zoneid) { return (0); } } return (1); } /* * dtrace_match_probe compares a dtrace_probe_t to a pre-compiled key, which * consists of input pattern strings and an ops-vector to evaluate them. * This function returns >0 for match, 0 for no match, and <0 for error. */ static int dtrace_match_probe(const dtrace_probe_t *prp, const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid, zoneid_t zoneid) { dtrace_provider_t *pvp = prp->dtpr_provider; int rv; if (pvp->dtpv_defunct) return (0); if ((rv = pkp->dtpk_pmatch(pvp->dtpv_name, pkp->dtpk_prov, 0)) <= 0) return (rv); if ((rv = pkp->dtpk_mmatch(prp->dtpr_mod, pkp->dtpk_mod, 0)) <= 0) return (rv); if ((rv = pkp->dtpk_fmatch(prp->dtpr_func, pkp->dtpk_func, 0)) <= 0) return (rv); if ((rv = pkp->dtpk_nmatch(prp->dtpr_name, pkp->dtpk_name, 0)) <= 0) return (rv); if (dtrace_match_priv(prp, priv, uid, zoneid) == 0) return (0); return (rv); } /* * dtrace_match_glob() is a safe kernel implementation of the gmatch(3GEN) * interface for matching a glob pattern 'p' to an input string 's'. Unlike * libc's version, the kernel version only applies to 8-bit ASCII strings. * In addition, all of the recursion cases except for '*' matching have been * unwound. For '*', we still implement recursive evaluation, but a depth * counter is maintained and matching is aborted if we recurse too deep. * The function returns 0 if no match, >0 if match, and <0 if recursion error. */ static int dtrace_match_glob(const char *s, const char *p, int depth) { const char *olds; char s1, c; int gs; if (depth > DTRACE_PROBEKEY_MAXDEPTH) return (-1); if (s == NULL) s = ""; /* treat NULL as empty string */ top: olds = s; s1 = *s++; if (p == NULL) return (0); if ((c = *p++) == '\0') return (s1 == '\0'); switch (c) { case '[': { int ok = 0, notflag = 0; char lc = '\0'; if (s1 == '\0') return (0); if (*p == '!') { notflag = 1; p++; } if ((c = *p++) == '\0') return (0); do { if (c == '-' && lc != '\0' && *p != ']') { if ((c = *p++) == '\0') return (0); if (c == '\\' && (c = *p++) == '\0') return (0); if (notflag) { if (s1 < lc || s1 > c) ok++; else return (0); } else if (lc <= s1 && s1 <= c) ok++; } else if (c == '\\' && (c = *p++) == '\0') return (0); lc = c; /* save left-hand 'c' for next iteration */ if (notflag) { if (s1 != c) ok++; else return (0); } else if (s1 == c) ok++; if ((c = *p++) == '\0') return (0); } while (c != ']'); if (ok) goto top; return (0); } case '\\': if ((c = *p++) == '\0') return (0); /*FALLTHRU*/ default: if (c != s1) return (0); /*FALLTHRU*/ case '?': if (s1 != '\0') goto top; return (0); case '*': while (*p == '*') p++; /* consecutive *'s are identical to a single one */ if (*p == '\0') return (1); for (s = olds; *s != '\0'; s++) { if ((gs = dtrace_match_glob(s, p, depth + 1)) != 0) return (gs); } return (0); } } /*ARGSUSED*/ static int dtrace_match_string(const char *s, const char *p, int depth) { return (s != NULL && strcmp(s, p) == 0); } /*ARGSUSED*/ static int dtrace_match_nul(const char *s, const char *p, int depth) { return (1); /* always match the empty pattern */ } /*ARGSUSED*/ static int dtrace_match_nonzero(const char *s, const char *p, int depth) { return (s != NULL && s[0] != '\0'); } static int dtrace_match(const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid, zoneid_t zoneid, int (*matched)(dtrace_probe_t *, void *), void *arg) { dtrace_probe_t template, *probe; dtrace_hash_t *hash = NULL; int len, best = INT_MAX, nmatched = 0; dtrace_id_t i; ASSERT(MUTEX_HELD(&dtrace_lock)); /* * If the probe ID is specified in the key, just lookup by ID and * invoke the match callback once if a matching probe is found. */ if (pkp->dtpk_id != DTRACE_IDNONE) { if ((probe = dtrace_probe_lookup_id(pkp->dtpk_id)) != NULL && dtrace_match_probe(probe, pkp, priv, uid, zoneid) > 0) { (void) (*matched)(probe, arg); nmatched++; } return (nmatched); } template.dtpr_mod = (char *)pkp->dtpk_mod; template.dtpr_func = (char *)pkp->dtpk_func; template.dtpr_name = (char *)pkp->dtpk_name; /* * We want to find the most distinct of the module name, function * name, and name. So for each one that is not a glob pattern or * empty string, we perform a lookup in the corresponding hash and * use the hash table with the fewest collisions to do our search. */ if (pkp->dtpk_mmatch == &dtrace_match_string && (len = dtrace_hash_collisions(dtrace_bymod, &template)) < best) { best = len; hash = dtrace_bymod; } if (pkp->dtpk_fmatch == &dtrace_match_string && (len = dtrace_hash_collisions(dtrace_byfunc, &template)) < best) { best = len; hash = dtrace_byfunc; } if (pkp->dtpk_nmatch == &dtrace_match_string && (len = dtrace_hash_collisions(dtrace_byname, &template)) < best) { best = len; hash = dtrace_byname; } /* * If we did not select a hash table, iterate over every probe and * invoke our callback for each one that matches our input probe key. */ if (hash == NULL) { for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL || dtrace_match_probe(probe, pkp, priv, uid, zoneid) <= 0) continue; nmatched++; if ((*matched)(probe, arg) != DTRACE_MATCH_NEXT) break; } return (nmatched); } /* * If we selected a hash table, iterate over each probe of the same key * name and invoke the callback for every probe that matches the other * attributes of our input probe key. */ for (probe = dtrace_hash_lookup(hash, &template); probe != NULL; probe = *(DTRACE_HASHNEXT(hash, probe))) { if (dtrace_match_probe(probe, pkp, priv, uid, zoneid) <= 0) continue; nmatched++; if ((*matched)(probe, arg) != DTRACE_MATCH_NEXT) break; } return (nmatched); } /* * Return the function pointer dtrace_probecmp() should use to compare the * specified pattern with a string. For NULL or empty patterns, we select * dtrace_match_nul(). For glob pattern strings, we use dtrace_match_glob(). * For non-empty non-glob strings, we use dtrace_match_string(). */ static dtrace_probekey_f * dtrace_probekey_func(const char *p) { char c; if (p == NULL || *p == '\0') return (&dtrace_match_nul); while ((c = *p++) != '\0') { if (c == '[' || c == '?' || c == '*' || c == '\\') return (&dtrace_match_glob); } return (&dtrace_match_string); } /* * Build a probe comparison key for use with dtrace_match_probe() from the * given probe description. By convention, a null key only matches anchored * probes: if each field is the empty string, reset dtpk_fmatch to * dtrace_match_nonzero(). */ static void dtrace_probekey(dtrace_probedesc_t *pdp, dtrace_probekey_t *pkp) { pkp->dtpk_prov = pdp->dtpd_provider; pkp->dtpk_pmatch = dtrace_probekey_func(pdp->dtpd_provider); pkp->dtpk_mod = pdp->dtpd_mod; pkp->dtpk_mmatch = dtrace_probekey_func(pdp->dtpd_mod); pkp->dtpk_func = pdp->dtpd_func; pkp->dtpk_fmatch = dtrace_probekey_func(pdp->dtpd_func); pkp->dtpk_name = pdp->dtpd_name; pkp->dtpk_nmatch = dtrace_probekey_func(pdp->dtpd_name); pkp->dtpk_id = pdp->dtpd_id; if (pkp->dtpk_id == DTRACE_IDNONE && pkp->dtpk_pmatch == &dtrace_match_nul && pkp->dtpk_mmatch == &dtrace_match_nul && pkp->dtpk_fmatch == &dtrace_match_nul && pkp->dtpk_nmatch == &dtrace_match_nul) pkp->dtpk_fmatch = &dtrace_match_nonzero; } /* * DTrace Provider-to-Framework API Functions * * These functions implement much of the Provider-to-Framework API, as * described in . The parts of the API not in this section are * the functions in the API for probe management (found below), and * dtrace_probe() itself (found above). */ /* * Register the calling provider with the DTrace framework. This should * generally be called by DTrace providers in their attach(9E) entry point. */ int dtrace_register(const char *name, const dtrace_pattr_t *pap, uint32_t priv, cred_t *cr, const dtrace_pops_t *pops, void *arg, dtrace_provider_id_t *idp) { dtrace_provider_t *provider; if (name == NULL || pap == NULL || pops == NULL || idp == NULL) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "arguments", name ? name : ""); return (EINVAL); } if (name[0] == '\0' || dtrace_badname(name)) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "provider name", name); return (EINVAL); } if ((pops->dtps_provide == NULL && pops->dtps_provide_module == NULL) || pops->dtps_enable == NULL || pops->dtps_disable == NULL || pops->dtps_destroy == NULL || ((pops->dtps_resume == NULL) != (pops->dtps_suspend == NULL))) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "provider ops", name); return (EINVAL); } if (dtrace_badattr(&pap->dtpa_provider) || dtrace_badattr(&pap->dtpa_mod) || dtrace_badattr(&pap->dtpa_func) || dtrace_badattr(&pap->dtpa_name) || dtrace_badattr(&pap->dtpa_args)) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "provider attributes", name); return (EINVAL); } if (priv & ~DTRACE_PRIV_ALL) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "privilege attributes", name); return (EINVAL); } if ((priv & DTRACE_PRIV_KERNEL) && (priv & (DTRACE_PRIV_USER | DTRACE_PRIV_OWNER)) && pops->dtps_usermode == NULL) { cmn_err(CE_WARN, "failed to register provider '%s': need " "dtps_usermode() op for given privilege attributes", name); return (EINVAL); } provider = kmem_zalloc(sizeof (dtrace_provider_t), KM_SLEEP); provider->dtpv_name = kmem_alloc(strlen(name) + 1, KM_SLEEP); (void) strcpy(provider->dtpv_name, name); provider->dtpv_attr = *pap; provider->dtpv_priv.dtpp_flags = priv; if (cr != NULL) { provider->dtpv_priv.dtpp_uid = crgetuid(cr); provider->dtpv_priv.dtpp_zoneid = crgetzoneid(cr); } provider->dtpv_pops = *pops; if (pops->dtps_provide == NULL) { ASSERT(pops->dtps_provide_module != NULL); provider->dtpv_pops.dtps_provide = (void (*)(void *, dtrace_probedesc_t *))dtrace_nullop; } if (pops->dtps_provide_module == NULL) { ASSERT(pops->dtps_provide != NULL); provider->dtpv_pops.dtps_provide_module = (void (*)(void *, modctl_t *))dtrace_nullop; } if (pops->dtps_suspend == NULL) { ASSERT(pops->dtps_resume == NULL); provider->dtpv_pops.dtps_suspend = (void (*)(void *, dtrace_id_t, void *))dtrace_nullop; provider->dtpv_pops.dtps_resume = (void (*)(void *, dtrace_id_t, void *))dtrace_nullop; } provider->dtpv_arg = arg; *idp = (dtrace_provider_id_t)provider; if (pops == &dtrace_provider_ops) { ASSERT(MUTEX_HELD(&dtrace_provider_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dtrace_anon.dta_enabling == NULL); /* * We make sure that the DTrace provider is at the head of * the provider chain. */ provider->dtpv_next = dtrace_provider; dtrace_provider = provider; return (0); } mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); /* * If there is at least one provider registered, we'll add this * provider after the first provider. */ if (dtrace_provider != NULL) { provider->dtpv_next = dtrace_provider->dtpv_next; dtrace_provider->dtpv_next = provider; } else { dtrace_provider = provider; } if (dtrace_retained != NULL) { dtrace_enabling_provide(provider); /* * Now we need to call dtrace_enabling_matchall() -- which * will acquire cpu_lock and dtrace_lock. We therefore need * to drop all of our locks before calling into it... */ mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); dtrace_enabling_matchall(); return (0); } mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); return (0); } /* * Unregister the specified provider from the DTrace framework. This should * generally be called by DTrace providers in their detach(9E) entry point. */ int dtrace_unregister(dtrace_provider_id_t id) { dtrace_provider_t *old = (dtrace_provider_t *)id; dtrace_provider_t *prev = NULL; int i, self = 0, noreap = 0; dtrace_probe_t *probe, *first = NULL; if (old->dtpv_pops.dtps_enable == (void (*)(void *, dtrace_id_t, void *))dtrace_nullop) { /* * If DTrace itself is the provider, we're called with locks * already held. */ ASSERT(old == dtrace_provider); #if defined(sun) ASSERT(dtrace_devi != NULL); #endif ASSERT(MUTEX_HELD(&dtrace_provider_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); self = 1; if (dtrace_provider->dtpv_next != NULL) { /* * There's another provider here; return failure. */ return (EBUSY); } } else { mutex_enter(&dtrace_provider_lock); #if defined(sun) mutex_enter(&mod_lock); #endif mutex_enter(&dtrace_lock); } /* * If anyone has /dev/dtrace open, or if there are anonymous enabled * probes, we refuse to let providers slither away, unless this * provider has already been explicitly invalidated. */ if (!old->dtpv_defunct && (dtrace_opens || (dtrace_anon.dta_state != NULL && dtrace_anon.dta_state->dts_necbs > 0))) { if (!self) { mutex_exit(&dtrace_lock); #if defined(sun) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); } return (EBUSY); } /* * Attempt to destroy the probes associated with this provider. */ for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_provider != old) continue; if (probe->dtpr_ecb == NULL) continue; /* * If we are trying to unregister a defunct provider, and the * provider was made defunct within the interval dictated by * dtrace_unregister_defunct_reap, we'll (asynchronously) * attempt to reap our enablings. To denote that the provider * should reattempt to unregister itself at some point in the * future, we will return a differentiable error code (EAGAIN * instead of EBUSY) in this case. */ if (dtrace_gethrtime() - old->dtpv_defunct > dtrace_unregister_defunct_reap) noreap = 1; if (!self) { mutex_exit(&dtrace_lock); #if defined(sun) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); } if (noreap) return (EBUSY); (void) taskq_dispatch(dtrace_taskq, (task_func_t *)dtrace_enabling_reap, NULL, TQ_SLEEP); return (EAGAIN); } /* * All of the probes for this provider are disabled; we can safely * remove all of them from their hash chains and from the probe array. */ for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_provider != old) continue; dtrace_probes[i] = NULL; dtrace_hash_remove(dtrace_bymod, probe); dtrace_hash_remove(dtrace_byfunc, probe); dtrace_hash_remove(dtrace_byname, probe); if (first == NULL) { first = probe; probe->dtpr_nextmod = NULL; } else { probe->dtpr_nextmod = first; first = probe; } } /* * The provider's probes have been removed from the hash chains and * from the probe array. Now issue a dtrace_sync() to be sure that * everyone has cleared out from any probe array processing. */ dtrace_sync(); for (probe = first; probe != NULL; probe = first) { first = probe->dtpr_nextmod; old->dtpv_pops.dtps_destroy(old->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1); kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1); kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1); #if defined(sun) vmem_free(dtrace_arena, (void *)(uintptr_t)(probe->dtpr_id), 1); #else free_unr(dtrace_arena, probe->dtpr_id); #endif kmem_free(probe, sizeof (dtrace_probe_t)); } if ((prev = dtrace_provider) == old) { #if defined(sun) ASSERT(self || dtrace_devi == NULL); ASSERT(old->dtpv_next == NULL || dtrace_devi == NULL); #endif dtrace_provider = old->dtpv_next; } else { while (prev != NULL && prev->dtpv_next != old) prev = prev->dtpv_next; if (prev == NULL) { panic("attempt to unregister non-existent " "dtrace provider %p\n", (void *)id); } prev->dtpv_next = old->dtpv_next; } if (!self) { mutex_exit(&dtrace_lock); #if defined(sun) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); } kmem_free(old->dtpv_name, strlen(old->dtpv_name) + 1); kmem_free(old, sizeof (dtrace_provider_t)); return (0); } /* * Invalidate the specified provider. All subsequent probe lookups for the * specified provider will fail, but its probes will not be removed. */ void dtrace_invalidate(dtrace_provider_id_t id) { dtrace_provider_t *pvp = (dtrace_provider_t *)id; ASSERT(pvp->dtpv_pops.dtps_enable != (void (*)(void *, dtrace_id_t, void *))dtrace_nullop); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); pvp->dtpv_defunct = dtrace_gethrtime(); mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); } /* * Indicate whether or not DTrace has attached. */ int dtrace_attached(void) { /* * dtrace_provider will be non-NULL iff the DTrace driver has * attached. (It's non-NULL because DTrace is always itself a * provider.) */ return (dtrace_provider != NULL); } /* * Remove all the unenabled probes for the given provider. This function is * not unlike dtrace_unregister(), except that it doesn't remove the provider * -- just as many of its associated probes as it can. */ int dtrace_condense(dtrace_provider_id_t id) { dtrace_provider_t *prov = (dtrace_provider_t *)id; int i; dtrace_probe_t *probe; /* * Make sure this isn't the dtrace provider itself. */ ASSERT(prov->dtpv_pops.dtps_enable != (void (*)(void *, dtrace_id_t, void *))dtrace_nullop); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); /* * Attempt to destroy the probes associated with this provider. */ for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_provider != prov) continue; if (probe->dtpr_ecb != NULL) continue; dtrace_probes[i] = NULL; dtrace_hash_remove(dtrace_bymod, probe); dtrace_hash_remove(dtrace_byfunc, probe); dtrace_hash_remove(dtrace_byname, probe); prov->dtpv_pops.dtps_destroy(prov->dtpv_arg, i + 1, probe->dtpr_arg); kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1); kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1); kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1); kmem_free(probe, sizeof (dtrace_probe_t)); #if defined(sun) vmem_free(dtrace_arena, (void *)((uintptr_t)i + 1), 1); #else free_unr(dtrace_arena, i + 1); #endif } mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); return (0); } /* * DTrace Probe Management Functions * * The functions in this section perform the DTrace probe management, * including functions to create probes, look-up probes, and call into the * providers to request that probes be provided. Some of these functions are * in the Provider-to-Framework API; these functions can be identified by the * fact that they are not declared "static". */ /* * Create a probe with the specified module name, function name, and name. */ dtrace_id_t dtrace_probe_create(dtrace_provider_id_t prov, const char *mod, const char *func, const char *name, int aframes, void *arg) { dtrace_probe_t *probe, **probes; dtrace_provider_t *provider = (dtrace_provider_t *)prov; dtrace_id_t id; if (provider == dtrace_provider) { ASSERT(MUTEX_HELD(&dtrace_lock)); } else { mutex_enter(&dtrace_lock); } #if defined(sun) id = (dtrace_id_t)(uintptr_t)vmem_alloc(dtrace_arena, 1, VM_BESTFIT | VM_SLEEP); #else id = alloc_unr(dtrace_arena); #endif probe = kmem_zalloc(sizeof (dtrace_probe_t), KM_SLEEP); probe->dtpr_id = id; probe->dtpr_gen = dtrace_probegen++; probe->dtpr_mod = dtrace_strdup(mod); probe->dtpr_func = dtrace_strdup(func); probe->dtpr_name = dtrace_strdup(name); probe->dtpr_arg = arg; probe->dtpr_aframes = aframes; probe->dtpr_provider = provider; dtrace_hash_add(dtrace_bymod, probe); dtrace_hash_add(dtrace_byfunc, probe); dtrace_hash_add(dtrace_byname, probe); if (id - 1 >= dtrace_nprobes) { size_t osize = dtrace_nprobes * sizeof (dtrace_probe_t *); size_t nsize = osize << 1; if (nsize == 0) { ASSERT(osize == 0); ASSERT(dtrace_probes == NULL); nsize = sizeof (dtrace_probe_t *); } probes = kmem_zalloc(nsize, KM_SLEEP); if (dtrace_probes == NULL) { ASSERT(osize == 0); dtrace_probes = probes; dtrace_nprobes = 1; } else { dtrace_probe_t **oprobes = dtrace_probes; bcopy(oprobes, probes, osize); dtrace_membar_producer(); dtrace_probes = probes; dtrace_sync(); /* * All CPUs are now seeing the new probes array; we can * safely free the old array. */ kmem_free(oprobes, osize); dtrace_nprobes <<= 1; } ASSERT(id - 1 < dtrace_nprobes); } ASSERT(dtrace_probes[id - 1] == NULL); dtrace_probes[id - 1] = probe; if (provider != dtrace_provider) mutex_exit(&dtrace_lock); return (id); } static dtrace_probe_t * dtrace_probe_lookup_id(dtrace_id_t id) { ASSERT(MUTEX_HELD(&dtrace_lock)); if (id == 0 || id > dtrace_nprobes) return (NULL); return (dtrace_probes[id - 1]); } static int dtrace_probe_lookup_match(dtrace_probe_t *probe, void *arg) { *((dtrace_id_t *)arg) = probe->dtpr_id; return (DTRACE_MATCH_DONE); } /* * Look up a probe based on provider and one or more of module name, function * name and probe name. */ dtrace_id_t dtrace_probe_lookup(dtrace_provider_id_t prid, char *mod, char *func, char *name) { dtrace_probekey_t pkey; dtrace_id_t id; int match; pkey.dtpk_prov = ((dtrace_provider_t *)prid)->dtpv_name; pkey.dtpk_pmatch = &dtrace_match_string; pkey.dtpk_mod = mod; pkey.dtpk_mmatch = mod ? &dtrace_match_string : &dtrace_match_nul; pkey.dtpk_func = func; pkey.dtpk_fmatch = func ? &dtrace_match_string : &dtrace_match_nul; pkey.dtpk_name = name; pkey.dtpk_nmatch = name ? &dtrace_match_string : &dtrace_match_nul; pkey.dtpk_id = DTRACE_IDNONE; mutex_enter(&dtrace_lock); match = dtrace_match(&pkey, DTRACE_PRIV_ALL, 0, 0, dtrace_probe_lookup_match, &id); mutex_exit(&dtrace_lock); ASSERT(match == 1 || match == 0); return (match ? id : 0); } /* * Returns the probe argument associated with the specified probe. */ void * dtrace_probe_arg(dtrace_provider_id_t id, dtrace_id_t pid) { dtrace_probe_t *probe; void *rval = NULL; mutex_enter(&dtrace_lock); if ((probe = dtrace_probe_lookup_id(pid)) != NULL && probe->dtpr_provider == (dtrace_provider_t *)id) rval = probe->dtpr_arg; mutex_exit(&dtrace_lock); return (rval); } /* * Copy a probe into a probe description. */ static void dtrace_probe_description(const dtrace_probe_t *prp, dtrace_probedesc_t *pdp) { bzero(pdp, sizeof (dtrace_probedesc_t)); pdp->dtpd_id = prp->dtpr_id; (void) strncpy(pdp->dtpd_provider, prp->dtpr_provider->dtpv_name, DTRACE_PROVNAMELEN - 1); (void) strncpy(pdp->dtpd_mod, prp->dtpr_mod, DTRACE_MODNAMELEN - 1); (void) strncpy(pdp->dtpd_func, prp->dtpr_func, DTRACE_FUNCNAMELEN - 1); (void) strncpy(pdp->dtpd_name, prp->dtpr_name, DTRACE_NAMELEN - 1); } /* * Called to indicate that a probe -- or probes -- should be provided by a * specfied provider. If the specified description is NULL, the provider will * be told to provide all of its probes. (This is done whenever a new * consumer comes along, or whenever a retained enabling is to be matched.) If * the specified description is non-NULL, the provider is given the * opportunity to dynamically provide the specified probe, allowing providers * to support the creation of probes on-the-fly. (So-called _autocreated_ * probes.) If the provider is NULL, the operations will be applied to all * providers; if the provider is non-NULL the operations will only be applied * to the specified provider. The dtrace_provider_lock must be held, and the * dtrace_lock must _not_ be held -- the provider's dtps_provide() operation * will need to grab the dtrace_lock when it reenters the framework through * dtrace_probe_lookup(), dtrace_probe_create(), etc. */ static void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv) { #if defined(sun) modctl_t *ctl; #endif int all = 0; ASSERT(MUTEX_HELD(&dtrace_provider_lock)); if (prv == NULL) { all = 1; prv = dtrace_provider; } do { /* * First, call the blanket provide operation. */ prv->dtpv_pops.dtps_provide(prv->dtpv_arg, desc); #if defined(sun) /* * Now call the per-module provide operation. We will grab * mod_lock to prevent the list from being modified. Note * that this also prevents the mod_busy bits from changing. * (mod_busy can only be changed with mod_lock held.) */ mutex_enter(&mod_lock); ctl = &modules; do { if (ctl->mod_busy || ctl->mod_mp == NULL) continue; prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, ctl); } while ((ctl = ctl->mod_next) != &modules); mutex_exit(&mod_lock); #endif } while (all && (prv = prv->dtpv_next) != NULL); } #if defined(sun) /* * Iterate over each probe, and call the Framework-to-Provider API function * denoted by offs. */ static void dtrace_probe_foreach(uintptr_t offs) { dtrace_provider_t *prov; void (*func)(void *, dtrace_id_t, void *); dtrace_probe_t *probe; dtrace_icookie_t cookie; int i; /* * We disable interrupts to walk through the probe array. This is * safe -- the dtrace_sync() in dtrace_unregister() assures that we * won't see stale data. */ cookie = dtrace_interrupt_disable(); for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_ecb == NULL) { /* * This probe isn't enabled -- don't call the function. */ continue; } prov = probe->dtpr_provider; func = *((void(**)(void *, dtrace_id_t, void *)) ((uintptr_t)&prov->dtpv_pops + offs)); func(prov->dtpv_arg, i + 1, probe->dtpr_arg); } dtrace_interrupt_enable(cookie); } #endif static int dtrace_probe_enable(dtrace_probedesc_t *desc, dtrace_enabling_t *enab) { dtrace_probekey_t pkey; uint32_t priv; uid_t uid; zoneid_t zoneid; ASSERT(MUTEX_HELD(&dtrace_lock)); dtrace_ecb_create_cache = NULL; if (desc == NULL) { /* * If we're passed a NULL description, we're being asked to * create an ECB with a NULL probe. */ (void) dtrace_ecb_create_enable(NULL, enab); return (0); } dtrace_probekey(desc, &pkey); dtrace_cred2priv(enab->dten_vstate->dtvs_state->dts_cred.dcr_cred, &priv, &uid, &zoneid); return (dtrace_match(&pkey, priv, uid, zoneid, dtrace_ecb_create_enable, enab)); } /* * DTrace Helper Provider Functions */ static void dtrace_dofattr2attr(dtrace_attribute_t *attr, const dof_attr_t dofattr) { attr->dtat_name = DOF_ATTR_NAME(dofattr); attr->dtat_data = DOF_ATTR_DATA(dofattr); attr->dtat_class = DOF_ATTR_CLASS(dofattr); } static void dtrace_dofprov2hprov(dtrace_helper_provdesc_t *hprov, const dof_provider_t *dofprov, char *strtab) { hprov->dthpv_provname = strtab + dofprov->dofpv_name; dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_provider, dofprov->dofpv_provattr); dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_mod, dofprov->dofpv_modattr); dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_func, dofprov->dofpv_funcattr); dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_name, dofprov->dofpv_nameattr); dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_args, dofprov->dofpv_argsattr); } static void dtrace_helper_provide_one(dof_helper_t *dhp, dof_sec_t *sec, pid_t pid) { uintptr_t daddr = (uintptr_t)dhp->dofhp_dof; dof_hdr_t *dof = (dof_hdr_t *)daddr; dof_sec_t *str_sec, *prb_sec, *arg_sec, *off_sec, *enoff_sec; dof_provider_t *provider; dof_probe_t *probe; uint32_t *off, *enoff; uint8_t *arg; char *strtab; uint_t i, nprobes; dtrace_helper_provdesc_t dhpv; dtrace_helper_probedesc_t dhpb; dtrace_meta_t *meta = dtrace_meta_pid; dtrace_mops_t *mops = &meta->dtm_mops; void *parg; provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset); str_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_strtab * dof->dofh_secsize); prb_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_probes * dof->dofh_secsize); arg_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_prargs * dof->dofh_secsize); off_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_proffs * dof->dofh_secsize); strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset); off = (uint32_t *)(uintptr_t)(daddr + off_sec->dofs_offset); arg = (uint8_t *)(uintptr_t)(daddr + arg_sec->dofs_offset); enoff = NULL; /* * See dtrace_helper_provider_validate(). */ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 && provider->dofpv_prenoffs != DOF_SECT_NONE) { enoff_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_prenoffs * dof->dofh_secsize); enoff = (uint32_t *)(uintptr_t)(daddr + enoff_sec->dofs_offset); } nprobes = prb_sec->dofs_size / prb_sec->dofs_entsize; /* * Create the provider. */ dtrace_dofprov2hprov(&dhpv, provider, strtab); if ((parg = mops->dtms_provide_pid(meta->dtm_arg, &dhpv, pid)) == NULL) return; meta->dtm_count++; /* * Create the probes. */ for (i = 0; i < nprobes; i++) { probe = (dof_probe_t *)(uintptr_t)(daddr + prb_sec->dofs_offset + i * prb_sec->dofs_entsize); dhpb.dthpb_mod = dhp->dofhp_mod; dhpb.dthpb_func = strtab + probe->dofpr_func; dhpb.dthpb_name = strtab + probe->dofpr_name; dhpb.dthpb_base = probe->dofpr_addr; dhpb.dthpb_offs = off + probe->dofpr_offidx; dhpb.dthpb_noffs = probe->dofpr_noffs; if (enoff != NULL) { dhpb.dthpb_enoffs = enoff + probe->dofpr_enoffidx; dhpb.dthpb_nenoffs = probe->dofpr_nenoffs; } else { dhpb.dthpb_enoffs = NULL; dhpb.dthpb_nenoffs = 0; } dhpb.dthpb_args = arg + probe->dofpr_argidx; dhpb.dthpb_nargc = probe->dofpr_nargc; dhpb.dthpb_xargc = probe->dofpr_xargc; dhpb.dthpb_ntypes = strtab + probe->dofpr_nargv; dhpb.dthpb_xtypes = strtab + probe->dofpr_xargv; mops->dtms_create_probe(meta->dtm_arg, parg, &dhpb); } } static void dtrace_helper_provide(dof_helper_t *dhp, pid_t pid) { uintptr_t daddr = (uintptr_t)dhp->dofhp_dof; dof_hdr_t *dof = (dof_hdr_t *)daddr; int i; ASSERT(MUTEX_HELD(&dtrace_meta_lock)); for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_PROVIDER) continue; dtrace_helper_provide_one(dhp, sec, pid); } /* * We may have just created probes, so we must now rematch against * any retained enablings. Note that this call will acquire both * cpu_lock and dtrace_lock; the fact that we are holding * dtrace_meta_lock now is what defines the ordering with respect to * these three locks. */ dtrace_enabling_matchall(); } static void dtrace_helper_provider_remove_one(dof_helper_t *dhp, dof_sec_t *sec, pid_t pid) { uintptr_t daddr = (uintptr_t)dhp->dofhp_dof; dof_hdr_t *dof = (dof_hdr_t *)daddr; dof_sec_t *str_sec; dof_provider_t *provider; char *strtab; dtrace_helper_provdesc_t dhpv; dtrace_meta_t *meta = dtrace_meta_pid; dtrace_mops_t *mops = &meta->dtm_mops; provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset); str_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_strtab * dof->dofh_secsize); strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset); /* * Create the provider. */ dtrace_dofprov2hprov(&dhpv, provider, strtab); mops->dtms_remove_pid(meta->dtm_arg, &dhpv, pid); meta->dtm_count--; } static void dtrace_helper_provider_remove(dof_helper_t *dhp, pid_t pid) { uintptr_t daddr = (uintptr_t)dhp->dofhp_dof; dof_hdr_t *dof = (dof_hdr_t *)daddr; int i; ASSERT(MUTEX_HELD(&dtrace_meta_lock)); for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_PROVIDER) continue; dtrace_helper_provider_remove_one(dhp, sec, pid); } } /* * DTrace Meta Provider-to-Framework API Functions * * These functions implement the Meta Provider-to-Framework API, as described * in . */ int dtrace_meta_register(const char *name, const dtrace_mops_t *mops, void *arg, dtrace_meta_provider_id_t *idp) { dtrace_meta_t *meta; dtrace_helpers_t *help, *next; int i; *idp = DTRACE_METAPROVNONE; /* * We strictly don't need the name, but we hold onto it for * debuggability. All hail error queues! */ if (name == NULL) { cmn_err(CE_WARN, "failed to register meta-provider: " "invalid name"); return (EINVAL); } if (mops == NULL || mops->dtms_create_probe == NULL || mops->dtms_provide_pid == NULL || mops->dtms_remove_pid == NULL) { cmn_err(CE_WARN, "failed to register meta-register %s: " "invalid ops", name); return (EINVAL); } meta = kmem_zalloc(sizeof (dtrace_meta_t), KM_SLEEP); meta->dtm_mops = *mops; meta->dtm_name = kmem_alloc(strlen(name) + 1, KM_SLEEP); (void) strcpy(meta->dtm_name, name); meta->dtm_arg = arg; mutex_enter(&dtrace_meta_lock); mutex_enter(&dtrace_lock); if (dtrace_meta_pid != NULL) { mutex_exit(&dtrace_lock); mutex_exit(&dtrace_meta_lock); cmn_err(CE_WARN, "failed to register meta-register %s: " "user-land meta-provider exists", name); kmem_free(meta->dtm_name, strlen(meta->dtm_name) + 1); kmem_free(meta, sizeof (dtrace_meta_t)); return (EINVAL); } dtrace_meta_pid = meta; *idp = (dtrace_meta_provider_id_t)meta; /* * If there are providers and probes ready to go, pass them * off to the new meta provider now. */ help = dtrace_deferred_pid; dtrace_deferred_pid = NULL; mutex_exit(&dtrace_lock); while (help != NULL) { for (i = 0; i < help->dthps_nprovs; i++) { dtrace_helper_provide(&help->dthps_provs[i]->dthp_prov, help->dthps_pid); } next = help->dthps_next; help->dthps_next = NULL; help->dthps_prev = NULL; help->dthps_deferred = 0; help = next; } mutex_exit(&dtrace_meta_lock); return (0); } int dtrace_meta_unregister(dtrace_meta_provider_id_t id) { dtrace_meta_t **pp, *old = (dtrace_meta_t *)id; mutex_enter(&dtrace_meta_lock); mutex_enter(&dtrace_lock); if (old == dtrace_meta_pid) { pp = &dtrace_meta_pid; } else { panic("attempt to unregister non-existent " "dtrace meta-provider %p\n", (void *)old); } if (old->dtm_count != 0) { mutex_exit(&dtrace_lock); mutex_exit(&dtrace_meta_lock); return (EBUSY); } *pp = NULL; mutex_exit(&dtrace_lock); mutex_exit(&dtrace_meta_lock); kmem_free(old->dtm_name, strlen(old->dtm_name) + 1); kmem_free(old, sizeof (dtrace_meta_t)); return (0); } /* * DTrace DIF Object Functions */ static int dtrace_difo_err(uint_t pc, const char *format, ...) { if (dtrace_err_verbose) { va_list alist; (void) uprintf("dtrace DIF object error: [%u]: ", pc); va_start(alist, format); (void) vuprintf(format, alist); va_end(alist); } #ifdef DTRACE_ERRDEBUG dtrace_errdebug(format); #endif return (1); } /* * Validate a DTrace DIF object by checking the IR instructions. The following * rules are currently enforced by dtrace_difo_validate(): * * 1. Each instruction must have a valid opcode * 2. Each register, string, variable, or subroutine reference must be valid * 3. No instruction can modify register %r0 (must be zero) * 4. All instruction reserved bits must be set to zero * 5. The last instruction must be a "ret" instruction * 6. All branch targets must reference a valid instruction _after_ the branch */ static int dtrace_difo_validate(dtrace_difo_t *dp, dtrace_vstate_t *vstate, uint_t nregs, cred_t *cr) { int err = 0, i; int (*efunc)(uint_t pc, const char *, ...) = dtrace_difo_err; int kcheckload; uint_t pc; kcheckload = cr == NULL || (vstate->dtvs_state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) == 0; dp->dtdo_destructive = 0; for (pc = 0; pc < dp->dtdo_len && err == 0; pc++) { dif_instr_t instr = dp->dtdo_buf[pc]; uint_t r1 = DIF_INSTR_R1(instr); uint_t r2 = DIF_INSTR_R2(instr); uint_t rd = DIF_INSTR_RD(instr); uint_t rs = DIF_INSTR_RS(instr); uint_t label = DIF_INSTR_LABEL(instr); uint_t v = DIF_INSTR_VAR(instr); uint_t subr = DIF_INSTR_SUBR(instr); uint_t type = DIF_INSTR_TYPE(instr); uint_t op = DIF_INSTR_OP(instr); switch (op) { case DIF_OP_OR: case DIF_OP_XOR: case DIF_OP_AND: case DIF_OP_SLL: case DIF_OP_SRL: case DIF_OP_SRA: case DIF_OP_SUB: case DIF_OP_ADD: case DIF_OP_MUL: case DIF_OP_SDIV: case DIF_OP_UDIV: case DIF_OP_SREM: case DIF_OP_UREM: case DIF_OP_COPYS: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_NOT: case DIF_OP_MOV: case DIF_OP_ALLOCS: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_LDSB: case DIF_OP_LDSH: case DIF_OP_LDSW: case DIF_OP_LDUB: case DIF_OP_LDUH: case DIF_OP_LDUW: case DIF_OP_LDX: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); if (kcheckload) dp->dtdo_buf[pc] = DIF_INSTR_LOAD(op + DIF_OP_RLDSB - DIF_OP_LDSB, r1, rd); break; case DIF_OP_RLDSB: case DIF_OP_RLDSH: case DIF_OP_RLDSW: case DIF_OP_RLDUB: case DIF_OP_RLDUH: case DIF_OP_RLDUW: case DIF_OP_RLDX: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_ULDSB: case DIF_OP_ULDSH: case DIF_OP_ULDSW: case DIF_OP_ULDUB: case DIF_OP_ULDUH: case DIF_OP_ULDUW: case DIF_OP_ULDX: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_STB: case DIF_OP_STH: case DIF_OP_STW: case DIF_OP_STX: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to 0 address\n"); break; case DIF_OP_CMP: case DIF_OP_SCMP: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rd != 0) err += efunc(pc, "non-zero reserved bits\n"); break; case DIF_OP_TST: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0 || rd != 0) err += efunc(pc, "non-zero reserved bits\n"); break; case DIF_OP_BA: case DIF_OP_BE: case DIF_OP_BNE: case DIF_OP_BG: case DIF_OP_BGU: case DIF_OP_BGE: case DIF_OP_BGEU: case DIF_OP_BL: case DIF_OP_BLU: case DIF_OP_BLE: case DIF_OP_BLEU: if (label >= dp->dtdo_len) { err += efunc(pc, "invalid branch target %u\n", label); } if (label <= pc) { err += efunc(pc, "backward branch to %u\n", label); } break; case DIF_OP_RET: if (r1 != 0 || r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); break; case DIF_OP_NOP: case DIF_OP_POPTS: case DIF_OP_FLUSHTS: if (r1 != 0 || r2 != 0 || rd != 0) err += efunc(pc, "non-zero reserved bits\n"); break; case DIF_OP_SETX: if (DIF_INSTR_INTEGER(instr) >= dp->dtdo_intlen) { err += efunc(pc, "invalid integer ref %u\n", DIF_INSTR_INTEGER(instr)); } if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_SETS: if (DIF_INSTR_STRING(instr) >= dp->dtdo_strlen) { err += efunc(pc, "invalid string ref %u\n", DIF_INSTR_STRING(instr)); } if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_LDGA: case DIF_OP_LDTA: if (r1 > DIF_VAR_ARRAY_MAX) err += efunc(pc, "invalid array %u\n", r1); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_LDGS: case DIF_OP_LDTS: case DIF_OP_LDLS: case DIF_OP_LDGAA: case DIF_OP_LDTAA: if (v < DIF_VAR_OTHER_MIN || v > DIF_VAR_OTHER_MAX) err += efunc(pc, "invalid variable %u\n", v); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_STGS: case DIF_OP_STTS: case DIF_OP_STLS: case DIF_OP_STGAA: case DIF_OP_STTAA: if (v < DIF_VAR_OTHER_UBASE || v > DIF_VAR_OTHER_MAX) err += efunc(pc, "invalid variable %u\n", v); if (rs >= nregs) err += efunc(pc, "invalid register %u\n", rd); break; case DIF_OP_CALL: if (subr > DIF_SUBR_MAX) err += efunc(pc, "invalid subr %u\n", subr); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); if (subr == DIF_SUBR_COPYOUT || subr == DIF_SUBR_COPYOUTSTR) { dp->dtdo_destructive = 1; } + + if (subr == DIF_SUBR_GETF) { + /* + * If we have a getf() we need to record that + * in our state. Note that our state can be + * NULL if this is a helper -- but in that + * case, the call to getf() is itself illegal, + * and will be caught (slightly later) when + * the helper is validated. + */ + if (vstate->dtvs_state != NULL) + vstate->dtvs_state->dts_getf++; + } + break; case DIF_OP_PUSHTR: if (type != DIF_TYPE_STRING && type != DIF_TYPE_CTF) err += efunc(pc, "invalid ref type %u\n", type); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rs >= nregs) err += efunc(pc, "invalid register %u\n", rs); break; case DIF_OP_PUSHTV: if (type != DIF_TYPE_CTF) err += efunc(pc, "invalid val type %u\n", type); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rs >= nregs) err += efunc(pc, "invalid register %u\n", rs); break; default: err += efunc(pc, "invalid opcode %u\n", DIF_INSTR_OP(instr)); } } if (dp->dtdo_len != 0 && DIF_INSTR_OP(dp->dtdo_buf[dp->dtdo_len - 1]) != DIF_OP_RET) { err += efunc(dp->dtdo_len - 1, "expected 'ret' as last DIF instruction\n"); } - if (!(dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) { + if (!(dp->dtdo_rtype.dtdt_flags & (DIF_TF_BYREF | DIF_TF_BYUREF))) { /* * If we're not returning by reference, the size must be either * 0 or the size of one of the base types. */ switch (dp->dtdo_rtype.dtdt_size) { case 0: case sizeof (uint8_t): case sizeof (uint16_t): case sizeof (uint32_t): case sizeof (uint64_t): break; default: err += efunc(dp->dtdo_len - 1, "bad return size\n"); } } for (i = 0; i < dp->dtdo_varlen && err == 0; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i], *existing = NULL; dtrace_diftype_t *vt, *et; uint_t id, ndx; if (v->dtdv_scope != DIFV_SCOPE_GLOBAL && v->dtdv_scope != DIFV_SCOPE_THREAD && v->dtdv_scope != DIFV_SCOPE_LOCAL) { err += efunc(i, "unrecognized variable scope %d\n", v->dtdv_scope); break; } if (v->dtdv_kind != DIFV_KIND_ARRAY && v->dtdv_kind != DIFV_KIND_SCALAR) { err += efunc(i, "unrecognized variable type %d\n", v->dtdv_kind); break; } if ((id = v->dtdv_id) > DIF_VARIABLE_MAX) { err += efunc(i, "%d exceeds variable id limit\n", id); break; } if (id < DIF_VAR_OTHER_UBASE) continue; /* * For user-defined variables, we need to check that this * definition is identical to any previous definition that we * encountered. */ ndx = id - DIF_VAR_OTHER_UBASE; switch (v->dtdv_scope) { case DIFV_SCOPE_GLOBAL: if (ndx < vstate->dtvs_nglobals) { dtrace_statvar_t *svar; if ((svar = vstate->dtvs_globals[ndx]) != NULL) existing = &svar->dtsv_var; } break; case DIFV_SCOPE_THREAD: if (ndx < vstate->dtvs_ntlocals) existing = &vstate->dtvs_tlocals[ndx]; break; case DIFV_SCOPE_LOCAL: if (ndx < vstate->dtvs_nlocals) { dtrace_statvar_t *svar; if ((svar = vstate->dtvs_locals[ndx]) != NULL) existing = &svar->dtsv_var; } break; } vt = &v->dtdv_type; if (vt->dtdt_flags & DIF_TF_BYREF) { if (vt->dtdt_size == 0) { err += efunc(i, "zero-sized variable\n"); break; } if (v->dtdv_scope == DIFV_SCOPE_GLOBAL && vt->dtdt_size > dtrace_global_maxsize) { err += efunc(i, "oversized by-ref global\n"); break; } } if (existing == NULL || existing->dtdv_id == 0) continue; ASSERT(existing->dtdv_id == v->dtdv_id); ASSERT(existing->dtdv_scope == v->dtdv_scope); if (existing->dtdv_kind != v->dtdv_kind) err += efunc(i, "%d changed variable kind\n", id); et = &existing->dtdv_type; if (vt->dtdt_flags != et->dtdt_flags) { err += efunc(i, "%d changed variable type flags\n", id); break; } if (vt->dtdt_size != 0 && vt->dtdt_size != et->dtdt_size) { err += efunc(i, "%d changed variable type size\n", id); break; } } return (err); } /* * Validate a DTrace DIF object that it is to be used as a helper. Helpers * are much more constrained than normal DIFOs. Specifically, they may * not: * * 1. Make calls to subroutines other than copyin(), copyinstr() or * miscellaneous string routines * 2. Access DTrace variables other than the args[] array, and the * curthread, pid, ppid, tid, execname, zonename, uid and gid variables. * 3. Have thread-local variables. * 4. Have dynamic variables. */ static int dtrace_difo_validate_helper(dtrace_difo_t *dp) { int (*efunc)(uint_t pc, const char *, ...) = dtrace_difo_err; int err = 0; uint_t pc; for (pc = 0; pc < dp->dtdo_len; pc++) { dif_instr_t instr = dp->dtdo_buf[pc]; uint_t v = DIF_INSTR_VAR(instr); uint_t subr = DIF_INSTR_SUBR(instr); uint_t op = DIF_INSTR_OP(instr); switch (op) { case DIF_OP_OR: case DIF_OP_XOR: case DIF_OP_AND: case DIF_OP_SLL: case DIF_OP_SRL: case DIF_OP_SRA: case DIF_OP_SUB: case DIF_OP_ADD: case DIF_OP_MUL: case DIF_OP_SDIV: case DIF_OP_UDIV: case DIF_OP_SREM: case DIF_OP_UREM: case DIF_OP_COPYS: case DIF_OP_NOT: case DIF_OP_MOV: case DIF_OP_RLDSB: case DIF_OP_RLDSH: case DIF_OP_RLDSW: case DIF_OP_RLDUB: case DIF_OP_RLDUH: case DIF_OP_RLDUW: case DIF_OP_RLDX: case DIF_OP_ULDSB: case DIF_OP_ULDSH: case DIF_OP_ULDSW: case DIF_OP_ULDUB: case DIF_OP_ULDUH: case DIF_OP_ULDUW: case DIF_OP_ULDX: case DIF_OP_STB: case DIF_OP_STH: case DIF_OP_STW: case DIF_OP_STX: case DIF_OP_ALLOCS: case DIF_OP_CMP: case DIF_OP_SCMP: case DIF_OP_TST: case DIF_OP_BA: case DIF_OP_BE: case DIF_OP_BNE: case DIF_OP_BG: case DIF_OP_BGU: case DIF_OP_BGE: case DIF_OP_BGEU: case DIF_OP_BL: case DIF_OP_BLU: case DIF_OP_BLE: case DIF_OP_BLEU: case DIF_OP_RET: case DIF_OP_NOP: case DIF_OP_POPTS: case DIF_OP_FLUSHTS: case DIF_OP_SETX: case DIF_OP_SETS: case DIF_OP_LDGA: case DIF_OP_LDLS: case DIF_OP_STGS: case DIF_OP_STLS: case DIF_OP_PUSHTR: case DIF_OP_PUSHTV: break; case DIF_OP_LDGS: if (v >= DIF_VAR_OTHER_UBASE) break; if (v >= DIF_VAR_ARG0 && v <= DIF_VAR_ARG9) break; if (v == DIF_VAR_CURTHREAD || v == DIF_VAR_PID || v == DIF_VAR_PPID || v == DIF_VAR_TID || v == DIF_VAR_EXECARGS || v == DIF_VAR_EXECNAME || v == DIF_VAR_ZONENAME || v == DIF_VAR_UID || v == DIF_VAR_GID) break; err += efunc(pc, "illegal variable %u\n", v); break; case DIF_OP_LDTA: case DIF_OP_LDTS: case DIF_OP_LDGAA: case DIF_OP_LDTAA: err += efunc(pc, "illegal dynamic variable load\n"); break; case DIF_OP_STTS: case DIF_OP_STGAA: case DIF_OP_STTAA: err += efunc(pc, "illegal dynamic variable store\n"); break; case DIF_OP_CALL: if (subr == DIF_SUBR_ALLOCA || subr == DIF_SUBR_BCOPY || subr == DIF_SUBR_COPYIN || subr == DIF_SUBR_COPYINTO || subr == DIF_SUBR_COPYINSTR || subr == DIF_SUBR_INDEX || subr == DIF_SUBR_INET_NTOA || subr == DIF_SUBR_INET_NTOA6 || subr == DIF_SUBR_INET_NTOP || + subr == DIF_SUBR_JSON || subr == DIF_SUBR_LLTOSTR || + subr == DIF_SUBR_STRTOLL || subr == DIF_SUBR_RINDEX || subr == DIF_SUBR_STRCHR || subr == DIF_SUBR_STRJOIN || subr == DIF_SUBR_STRRCHR || subr == DIF_SUBR_STRSTR || subr == DIF_SUBR_HTONS || subr == DIF_SUBR_HTONL || subr == DIF_SUBR_HTONLL || subr == DIF_SUBR_NTOHS || subr == DIF_SUBR_NTOHL || subr == DIF_SUBR_NTOHLL || subr == DIF_SUBR_MEMREF || subr == DIF_SUBR_TYPEREF) break; err += efunc(pc, "invalid subr %u\n", subr); break; default: err += efunc(pc, "invalid opcode %u\n", DIF_INSTR_OP(instr)); } } return (err); } /* * Returns 1 if the expression in the DIF object can be cached on a per-thread * basis; 0 if not. */ static int dtrace_difo_cacheable(dtrace_difo_t *dp) { int i; if (dp == NULL) return (0); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; if (v->dtdv_scope != DIFV_SCOPE_GLOBAL) continue; switch (v->dtdv_id) { case DIF_VAR_CURTHREAD: case DIF_VAR_PID: case DIF_VAR_TID: case DIF_VAR_EXECARGS: case DIF_VAR_EXECNAME: case DIF_VAR_ZONENAME: break; default: return (0); } } /* * This DIF object may be cacheable. Now we need to look for any * array loading instructions, any memory loading instructions, or * any stores to thread-local variables. */ for (i = 0; i < dp->dtdo_len; i++) { uint_t op = DIF_INSTR_OP(dp->dtdo_buf[i]); if ((op >= DIF_OP_LDSB && op <= DIF_OP_LDX) || (op >= DIF_OP_ULDSB && op <= DIF_OP_ULDX) || (op >= DIF_OP_RLDSB && op <= DIF_OP_RLDX) || op == DIF_OP_LDGA || op == DIF_OP_STTS) return (0); } return (1); } static void dtrace_difo_hold(dtrace_difo_t *dp) { int i; ASSERT(MUTEX_HELD(&dtrace_lock)); dp->dtdo_refcnt++; ASSERT(dp->dtdo_refcnt != 0); /* * We need to check this DIF object for references to the variable * DIF_VAR_VTIMESTAMP. */ for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; if (v->dtdv_id != DIF_VAR_VTIMESTAMP) continue; if (dtrace_vtime_references++ == 0) dtrace_vtime_enable(); } } /* * This routine calculates the dynamic variable chunksize for a given DIF * object. The calculation is not fool-proof, and can probably be tricked by * malicious DIF -- but it works for all compiler-generated DIF. Because this * calculation is likely imperfect, dtrace_dynvar() is able to gracefully fail * if a dynamic variable size exceeds the chunksize. */ static void dtrace_difo_chunksize(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { uint64_t sval = 0; dtrace_key_t tupregs[DIF_DTR_NREGS + 2]; /* +2 for thread and id */ const dif_instr_t *text = dp->dtdo_buf; uint_t pc, srd = 0; uint_t ttop = 0; size_t size, ksize; uint_t id, i; for (pc = 0; pc < dp->dtdo_len; pc++) { dif_instr_t instr = text[pc]; uint_t op = DIF_INSTR_OP(instr); uint_t rd = DIF_INSTR_RD(instr); uint_t r1 = DIF_INSTR_R1(instr); uint_t nkeys = 0; uchar_t scope = 0; dtrace_key_t *key = tupregs; switch (op) { case DIF_OP_SETX: sval = dp->dtdo_inttab[DIF_INSTR_INTEGER(instr)]; srd = rd; continue; case DIF_OP_STTS: key = &tupregs[DIF_DTR_NREGS]; key[0].dttk_size = 0; key[1].dttk_size = 0; nkeys = 2; scope = DIFV_SCOPE_THREAD; break; case DIF_OP_STGAA: case DIF_OP_STTAA: nkeys = ttop; if (DIF_INSTR_OP(instr) == DIF_OP_STTAA) key[nkeys++].dttk_size = 0; key[nkeys++].dttk_size = 0; if (op == DIF_OP_STTAA) { scope = DIFV_SCOPE_THREAD; } else { scope = DIFV_SCOPE_GLOBAL; } break; case DIF_OP_PUSHTR: if (ttop == DIF_DTR_NREGS) return; if ((srd == 0 || sval == 0) && r1 == DIF_TYPE_STRING) { /* * If the register for the size of the "pushtr" * is %r0 (or the value is 0) and the type is * a string, we'll use the system-wide default * string size. */ tupregs[ttop++].dttk_size = dtrace_strsize_default; } else { if (srd == 0) return; tupregs[ttop++].dttk_size = sval; } break; case DIF_OP_PUSHTV: if (ttop == DIF_DTR_NREGS) return; tupregs[ttop++].dttk_size = 0; break; case DIF_OP_FLUSHTS: ttop = 0; break; case DIF_OP_POPTS: if (ttop != 0) ttop--; break; } sval = 0; srd = 0; if (nkeys == 0) continue; /* * We have a dynamic variable allocation; calculate its size. */ for (ksize = 0, i = 0; i < nkeys; i++) ksize += P2ROUNDUP(key[i].dttk_size, sizeof (uint64_t)); size = sizeof (dtrace_dynvar_t); size += sizeof (dtrace_key_t) * (nkeys - 1); size += ksize; /* * Now we need to determine the size of the stored data. */ id = DIF_INSTR_VAR(instr); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; if (v->dtdv_id == id && v->dtdv_scope == scope) { size += v->dtdv_type.dtdt_size; break; } } if (i == dp->dtdo_varlen) return; /* * We have the size. If this is larger than the chunk size * for our dynamic variable state, reset the chunk size. */ size = P2ROUNDUP(size, sizeof (uint64_t)); if (size > vstate->dtvs_dynvars.dtds_chunksize) vstate->dtvs_dynvars.dtds_chunksize = size; } } static void dtrace_difo_init(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { int i, oldsvars, osz, nsz, otlocals, ntlocals; uint_t id; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dp->dtdo_buf != NULL && dp->dtdo_len != 0); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; dtrace_statvar_t *svar, ***svarp = NULL; size_t dsize = 0; uint8_t scope = v->dtdv_scope; int *np = NULL; if ((id = v->dtdv_id) < DIF_VAR_OTHER_UBASE) continue; id -= DIF_VAR_OTHER_UBASE; switch (scope) { case DIFV_SCOPE_THREAD: while (id >= (otlocals = vstate->dtvs_ntlocals)) { dtrace_difv_t *tlocals; if ((ntlocals = (otlocals << 1)) == 0) ntlocals = 1; osz = otlocals * sizeof (dtrace_difv_t); nsz = ntlocals * sizeof (dtrace_difv_t); tlocals = kmem_zalloc(nsz, KM_SLEEP); if (osz != 0) { bcopy(vstate->dtvs_tlocals, tlocals, osz); kmem_free(vstate->dtvs_tlocals, osz); } vstate->dtvs_tlocals = tlocals; vstate->dtvs_ntlocals = ntlocals; } vstate->dtvs_tlocals[id] = *v; continue; case DIFV_SCOPE_LOCAL: np = &vstate->dtvs_nlocals; svarp = &vstate->dtvs_locals; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) dsize = NCPU * (v->dtdv_type.dtdt_size + sizeof (uint64_t)); else dsize = NCPU * sizeof (uint64_t); break; case DIFV_SCOPE_GLOBAL: np = &vstate->dtvs_nglobals; svarp = &vstate->dtvs_globals; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) dsize = v->dtdv_type.dtdt_size + sizeof (uint64_t); break; default: ASSERT(0); } while (id >= (oldsvars = *np)) { dtrace_statvar_t **statics; int newsvars, oldsize, newsize; if ((newsvars = (oldsvars << 1)) == 0) newsvars = 1; oldsize = oldsvars * sizeof (dtrace_statvar_t *); newsize = newsvars * sizeof (dtrace_statvar_t *); statics = kmem_zalloc(newsize, KM_SLEEP); if (oldsize != 0) { bcopy(*svarp, statics, oldsize); kmem_free(*svarp, oldsize); } *svarp = statics; *np = newsvars; } if ((svar = (*svarp)[id]) == NULL) { svar = kmem_zalloc(sizeof (dtrace_statvar_t), KM_SLEEP); svar->dtsv_var = *v; if ((svar->dtsv_size = dsize) != 0) { svar->dtsv_data = (uint64_t)(uintptr_t) kmem_zalloc(dsize, KM_SLEEP); } (*svarp)[id] = svar; } svar->dtsv_refcnt++; } dtrace_difo_chunksize(dp, vstate); dtrace_difo_hold(dp); } static dtrace_difo_t * dtrace_difo_duplicate(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { dtrace_difo_t *new; size_t sz; ASSERT(dp->dtdo_buf != NULL); ASSERT(dp->dtdo_refcnt != 0); new = kmem_zalloc(sizeof (dtrace_difo_t), KM_SLEEP); ASSERT(dp->dtdo_buf != NULL); sz = dp->dtdo_len * sizeof (dif_instr_t); new->dtdo_buf = kmem_alloc(sz, KM_SLEEP); bcopy(dp->dtdo_buf, new->dtdo_buf, sz); new->dtdo_len = dp->dtdo_len; if (dp->dtdo_strtab != NULL) { ASSERT(dp->dtdo_strlen != 0); new->dtdo_strtab = kmem_alloc(dp->dtdo_strlen, KM_SLEEP); bcopy(dp->dtdo_strtab, new->dtdo_strtab, dp->dtdo_strlen); new->dtdo_strlen = dp->dtdo_strlen; } if (dp->dtdo_inttab != NULL) { ASSERT(dp->dtdo_intlen != 0); sz = dp->dtdo_intlen * sizeof (uint64_t); new->dtdo_inttab = kmem_alloc(sz, KM_SLEEP); bcopy(dp->dtdo_inttab, new->dtdo_inttab, sz); new->dtdo_intlen = dp->dtdo_intlen; } if (dp->dtdo_vartab != NULL) { ASSERT(dp->dtdo_varlen != 0); sz = dp->dtdo_varlen * sizeof (dtrace_difv_t); new->dtdo_vartab = kmem_alloc(sz, KM_SLEEP); bcopy(dp->dtdo_vartab, new->dtdo_vartab, sz); new->dtdo_varlen = dp->dtdo_varlen; } dtrace_difo_init(new, vstate); return (new); } static void dtrace_difo_destroy(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { int i; ASSERT(dp->dtdo_refcnt == 0); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; dtrace_statvar_t *svar, **svarp = NULL; uint_t id; uint8_t scope = v->dtdv_scope; int *np = NULL; switch (scope) { case DIFV_SCOPE_THREAD: continue; case DIFV_SCOPE_LOCAL: np = &vstate->dtvs_nlocals; svarp = vstate->dtvs_locals; break; case DIFV_SCOPE_GLOBAL: np = &vstate->dtvs_nglobals; svarp = vstate->dtvs_globals; break; default: ASSERT(0); } if ((id = v->dtdv_id) < DIF_VAR_OTHER_UBASE) continue; id -= DIF_VAR_OTHER_UBASE; ASSERT(id < *np); svar = svarp[id]; ASSERT(svar != NULL); ASSERT(svar->dtsv_refcnt > 0); if (--svar->dtsv_refcnt > 0) continue; if (svar->dtsv_size != 0) { ASSERT(svar->dtsv_data != 0); kmem_free((void *)(uintptr_t)svar->dtsv_data, svar->dtsv_size); } kmem_free(svar, sizeof (dtrace_statvar_t)); svarp[id] = NULL; } if (dp->dtdo_buf != NULL) kmem_free(dp->dtdo_buf, dp->dtdo_len * sizeof (dif_instr_t)); if (dp->dtdo_inttab != NULL) kmem_free(dp->dtdo_inttab, dp->dtdo_intlen * sizeof (uint64_t)); if (dp->dtdo_strtab != NULL) kmem_free(dp->dtdo_strtab, dp->dtdo_strlen); if (dp->dtdo_vartab != NULL) kmem_free(dp->dtdo_vartab, dp->dtdo_varlen * sizeof (dtrace_difv_t)); kmem_free(dp, sizeof (dtrace_difo_t)); } static void dtrace_difo_release(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { int i; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dp->dtdo_refcnt != 0); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; if (v->dtdv_id != DIF_VAR_VTIMESTAMP) continue; ASSERT(dtrace_vtime_references > 0); if (--dtrace_vtime_references == 0) dtrace_vtime_disable(); } if (--dp->dtdo_refcnt == 0) dtrace_difo_destroy(dp, vstate); } /* * DTrace Format Functions */ static uint16_t dtrace_format_add(dtrace_state_t *state, char *str) { char *fmt, **new; uint16_t ndx, len = strlen(str) + 1; fmt = kmem_zalloc(len, KM_SLEEP); bcopy(str, fmt, len); for (ndx = 0; ndx < state->dts_nformats; ndx++) { if (state->dts_formats[ndx] == NULL) { state->dts_formats[ndx] = fmt; return (ndx + 1); } } if (state->dts_nformats == USHRT_MAX) { /* * This is only likely if a denial-of-service attack is being * attempted. As such, it's okay to fail silently here. */ kmem_free(fmt, len); return (0); } /* * For simplicity, we always resize the formats array to be exactly the * number of formats. */ ndx = state->dts_nformats++; new = kmem_alloc((ndx + 1) * sizeof (char *), KM_SLEEP); if (state->dts_formats != NULL) { ASSERT(ndx != 0); bcopy(state->dts_formats, new, ndx * sizeof (char *)); kmem_free(state->dts_formats, ndx * sizeof (char *)); } state->dts_formats = new; state->dts_formats[ndx] = fmt; return (ndx + 1); } static void dtrace_format_remove(dtrace_state_t *state, uint16_t format) { char *fmt; ASSERT(state->dts_formats != NULL); ASSERT(format <= state->dts_nformats); ASSERT(state->dts_formats[format - 1] != NULL); fmt = state->dts_formats[format - 1]; kmem_free(fmt, strlen(fmt) + 1); state->dts_formats[format - 1] = NULL; } static void dtrace_format_destroy(dtrace_state_t *state) { int i; if (state->dts_nformats == 0) { ASSERT(state->dts_formats == NULL); return; } ASSERT(state->dts_formats != NULL); for (i = 0; i < state->dts_nformats; i++) { char *fmt = state->dts_formats[i]; if (fmt == NULL) continue; kmem_free(fmt, strlen(fmt) + 1); } kmem_free(state->dts_formats, state->dts_nformats * sizeof (char *)); state->dts_nformats = 0; state->dts_formats = NULL; } /* * DTrace Predicate Functions */ static dtrace_predicate_t * dtrace_predicate_create(dtrace_difo_t *dp) { dtrace_predicate_t *pred; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dp->dtdo_refcnt != 0); pred = kmem_zalloc(sizeof (dtrace_predicate_t), KM_SLEEP); pred->dtp_difo = dp; pred->dtp_refcnt = 1; if (!dtrace_difo_cacheable(dp)) return (pred); if (dtrace_predcache_id == DTRACE_CACHEIDNONE) { /* * This is only theoretically possible -- we have had 2^32 * cacheable predicates on this machine. We cannot allow any * more predicates to become cacheable: as unlikely as it is, * there may be a thread caching a (now stale) predicate cache * ID. (N.B.: the temptation is being successfully resisted to * have this cmn_err() "Holy shit -- we executed this code!") */ return (pred); } pred->dtp_cacheid = dtrace_predcache_id++; return (pred); } static void dtrace_predicate_hold(dtrace_predicate_t *pred) { ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(pred->dtp_difo != NULL && pred->dtp_difo->dtdo_refcnt != 0); ASSERT(pred->dtp_refcnt > 0); pred->dtp_refcnt++; } static void dtrace_predicate_release(dtrace_predicate_t *pred, dtrace_vstate_t *vstate) { dtrace_difo_t *dp = pred->dtp_difo; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dp != NULL && dp->dtdo_refcnt != 0); ASSERT(pred->dtp_refcnt > 0); if (--pred->dtp_refcnt == 0) { dtrace_difo_release(pred->dtp_difo, vstate); kmem_free(pred, sizeof (dtrace_predicate_t)); } } /* * DTrace Action Description Functions */ static dtrace_actdesc_t * dtrace_actdesc_create(dtrace_actkind_t kind, uint32_t ntuple, uint64_t uarg, uint64_t arg) { dtrace_actdesc_t *act; #if defined(sun) ASSERT(!DTRACEACT_ISPRINTFLIKE(kind) || (arg != NULL && arg >= KERNELBASE) || (arg == NULL && kind == DTRACEACT_PRINTA)); #endif act = kmem_zalloc(sizeof (dtrace_actdesc_t), KM_SLEEP); act->dtad_kind = kind; act->dtad_ntuple = ntuple; act->dtad_uarg = uarg; act->dtad_arg = arg; act->dtad_refcnt = 1; return (act); } static void dtrace_actdesc_hold(dtrace_actdesc_t *act) { ASSERT(act->dtad_refcnt >= 1); act->dtad_refcnt++; } static void dtrace_actdesc_release(dtrace_actdesc_t *act, dtrace_vstate_t *vstate) { dtrace_actkind_t kind = act->dtad_kind; dtrace_difo_t *dp; ASSERT(act->dtad_refcnt >= 1); if (--act->dtad_refcnt != 0) return; if ((dp = act->dtad_difo) != NULL) dtrace_difo_release(dp, vstate); if (DTRACEACT_ISPRINTFLIKE(kind)) { char *str = (char *)(uintptr_t)act->dtad_arg; #if defined(sun) ASSERT((str != NULL && (uintptr_t)str >= KERNELBASE) || (str == NULL && act->dtad_kind == DTRACEACT_PRINTA)); #endif if (str != NULL) kmem_free(str, strlen(str) + 1); } kmem_free(act, sizeof (dtrace_actdesc_t)); } /* * DTrace ECB Functions */ static dtrace_ecb_t * dtrace_ecb_add(dtrace_state_t *state, dtrace_probe_t *probe) { dtrace_ecb_t *ecb; dtrace_epid_t epid; ASSERT(MUTEX_HELD(&dtrace_lock)); ecb = kmem_zalloc(sizeof (dtrace_ecb_t), KM_SLEEP); ecb->dte_predicate = NULL; ecb->dte_probe = probe; /* * The default size is the size of the default action: recording * the header. */ ecb->dte_size = ecb->dte_needed = sizeof (dtrace_rechdr_t); ecb->dte_alignment = sizeof (dtrace_epid_t); epid = state->dts_epid++; if (epid - 1 >= state->dts_necbs) { dtrace_ecb_t **oecbs = state->dts_ecbs, **ecbs; int necbs = state->dts_necbs << 1; ASSERT(epid == state->dts_necbs + 1); if (necbs == 0) { ASSERT(oecbs == NULL); necbs = 1; } ecbs = kmem_zalloc(necbs * sizeof (*ecbs), KM_SLEEP); if (oecbs != NULL) bcopy(oecbs, ecbs, state->dts_necbs * sizeof (*ecbs)); dtrace_membar_producer(); state->dts_ecbs = ecbs; if (oecbs != NULL) { /* * If this state is active, we must dtrace_sync() * before we can free the old dts_ecbs array: we're * coming in hot, and there may be active ring * buffer processing (which indexes into the dts_ecbs * array) on another CPU. */ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) dtrace_sync(); kmem_free(oecbs, state->dts_necbs * sizeof (*ecbs)); } dtrace_membar_producer(); state->dts_necbs = necbs; } ecb->dte_state = state; ASSERT(state->dts_ecbs[epid - 1] == NULL); dtrace_membar_producer(); state->dts_ecbs[(ecb->dte_epid = epid) - 1] = ecb; return (ecb); } static void dtrace_ecb_enable(dtrace_ecb_t *ecb) { dtrace_probe_t *probe = ecb->dte_probe; ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(ecb->dte_next == NULL); if (probe == NULL) { /* * This is the NULL probe -- there's nothing to do. */ return; } if (probe->dtpr_ecb == NULL) { dtrace_provider_t *prov = probe->dtpr_provider; /* * We're the first ECB on this probe. */ probe->dtpr_ecb = probe->dtpr_ecb_last = ecb; if (ecb->dte_predicate != NULL) probe->dtpr_predcache = ecb->dte_predicate->dtp_cacheid; prov->dtpv_pops.dtps_enable(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); } else { /* * This probe is already active. Swing the last pointer to * point to the new ECB, and issue a dtrace_sync() to assure * that all CPUs have seen the change. */ ASSERT(probe->dtpr_ecb_last != NULL); probe->dtpr_ecb_last->dte_next = ecb; probe->dtpr_ecb_last = ecb; probe->dtpr_predcache = 0; dtrace_sync(); } } static void dtrace_ecb_resize(dtrace_ecb_t *ecb) { dtrace_action_t *act; uint32_t curneeded = UINT32_MAX; uint32_t aggbase = UINT32_MAX; /* * If we record anything, we always record the dtrace_rechdr_t. (And * we always record it first.) */ ecb->dte_size = sizeof (dtrace_rechdr_t); ecb->dte_alignment = sizeof (dtrace_epid_t); for (act = ecb->dte_action; act != NULL; act = act->dta_next) { dtrace_recdesc_t *rec = &act->dta_rec; ASSERT(rec->dtrd_size > 0 || rec->dtrd_alignment == 1); ecb->dte_alignment = MAX(ecb->dte_alignment, rec->dtrd_alignment); if (DTRACEACT_ISAGG(act->dta_kind)) { dtrace_aggregation_t *agg = (dtrace_aggregation_t *)act; ASSERT(rec->dtrd_size != 0); ASSERT(agg->dtag_first != NULL); ASSERT(act->dta_prev->dta_intuple); ASSERT(aggbase != UINT32_MAX); ASSERT(curneeded != UINT32_MAX); agg->dtag_base = aggbase; curneeded = P2ROUNDUP(curneeded, rec->dtrd_alignment); rec->dtrd_offset = curneeded; curneeded += rec->dtrd_size; ecb->dte_needed = MAX(ecb->dte_needed, curneeded); aggbase = UINT32_MAX; curneeded = UINT32_MAX; } else if (act->dta_intuple) { if (curneeded == UINT32_MAX) { /* * This is the first record in a tuple. Align * curneeded to be at offset 4 in an 8-byte * aligned block. */ ASSERT(act->dta_prev == NULL || !act->dta_prev->dta_intuple); ASSERT3U(aggbase, ==, UINT32_MAX); curneeded = P2PHASEUP(ecb->dte_size, sizeof (uint64_t), sizeof (dtrace_aggid_t)); aggbase = curneeded - sizeof (dtrace_aggid_t); ASSERT(IS_P2ALIGNED(aggbase, sizeof (uint64_t))); } curneeded = P2ROUNDUP(curneeded, rec->dtrd_alignment); rec->dtrd_offset = curneeded; curneeded += rec->dtrd_size; } else { /* tuples must be followed by an aggregation */ ASSERT(act->dta_prev == NULL || !act->dta_prev->dta_intuple); ecb->dte_size = P2ROUNDUP(ecb->dte_size, rec->dtrd_alignment); rec->dtrd_offset = ecb->dte_size; ecb->dte_size += rec->dtrd_size; ecb->dte_needed = MAX(ecb->dte_needed, ecb->dte_size); } } if ((act = ecb->dte_action) != NULL && !(act->dta_kind == DTRACEACT_SPECULATE && act->dta_next == NULL) && ecb->dte_size == sizeof (dtrace_rechdr_t)) { /* * If the size is still sizeof (dtrace_rechdr_t), then all * actions store no data; set the size to 0. */ ecb->dte_size = 0; } ecb->dte_size = P2ROUNDUP(ecb->dte_size, sizeof (dtrace_epid_t)); ecb->dte_needed = P2ROUNDUP(ecb->dte_needed, (sizeof (dtrace_epid_t))); ecb->dte_state->dts_needed = MAX(ecb->dte_state->dts_needed, ecb->dte_needed); } static dtrace_action_t * dtrace_ecb_aggregation_create(dtrace_ecb_t *ecb, dtrace_actdesc_t *desc) { dtrace_aggregation_t *agg; size_t size = sizeof (uint64_t); int ntuple = desc->dtad_ntuple; dtrace_action_t *act; dtrace_recdesc_t *frec; dtrace_aggid_t aggid; dtrace_state_t *state = ecb->dte_state; agg = kmem_zalloc(sizeof (dtrace_aggregation_t), KM_SLEEP); agg->dtag_ecb = ecb; ASSERT(DTRACEACT_ISAGG(desc->dtad_kind)); switch (desc->dtad_kind) { case DTRACEAGG_MIN: agg->dtag_initial = INT64_MAX; agg->dtag_aggregate = dtrace_aggregate_min; break; case DTRACEAGG_MAX: agg->dtag_initial = INT64_MIN; agg->dtag_aggregate = dtrace_aggregate_max; break; case DTRACEAGG_COUNT: agg->dtag_aggregate = dtrace_aggregate_count; break; case DTRACEAGG_QUANTIZE: agg->dtag_aggregate = dtrace_aggregate_quantize; size = (((sizeof (uint64_t) * NBBY) - 1) * 2 + 1) * sizeof (uint64_t); break; case DTRACEAGG_LQUANTIZE: { uint16_t step = DTRACE_LQUANTIZE_STEP(desc->dtad_arg); uint16_t levels = DTRACE_LQUANTIZE_LEVELS(desc->dtad_arg); agg->dtag_initial = desc->dtad_arg; agg->dtag_aggregate = dtrace_aggregate_lquantize; if (step == 0 || levels == 0) goto err; size = levels * sizeof (uint64_t) + 3 * sizeof (uint64_t); break; } case DTRACEAGG_LLQUANTIZE: { uint16_t factor = DTRACE_LLQUANTIZE_FACTOR(desc->dtad_arg); uint16_t low = DTRACE_LLQUANTIZE_LOW(desc->dtad_arg); uint16_t high = DTRACE_LLQUANTIZE_HIGH(desc->dtad_arg); uint16_t nsteps = DTRACE_LLQUANTIZE_NSTEP(desc->dtad_arg); int64_t v; agg->dtag_initial = desc->dtad_arg; agg->dtag_aggregate = dtrace_aggregate_llquantize; if (factor < 2 || low >= high || nsteps < factor) goto err; /* * Now check that the number of steps evenly divides a power * of the factor. (This assures both integer bucket size and * linearity within each magnitude.) */ for (v = factor; v < nsteps; v *= factor) continue; if ((v % nsteps) || (nsteps % factor)) goto err; size = (dtrace_aggregate_llquantize_bucket(factor, low, high, nsteps, INT64_MAX) + 2) * sizeof (uint64_t); break; } case DTRACEAGG_AVG: agg->dtag_aggregate = dtrace_aggregate_avg; size = sizeof (uint64_t) * 2; break; case DTRACEAGG_STDDEV: agg->dtag_aggregate = dtrace_aggregate_stddev; size = sizeof (uint64_t) * 4; break; case DTRACEAGG_SUM: agg->dtag_aggregate = dtrace_aggregate_sum; break; default: goto err; } agg->dtag_action.dta_rec.dtrd_size = size; if (ntuple == 0) goto err; /* * We must make sure that we have enough actions for the n-tuple. */ for (act = ecb->dte_action_last; act != NULL; act = act->dta_prev) { if (DTRACEACT_ISAGG(act->dta_kind)) break; if (--ntuple == 0) { /* * This is the action with which our n-tuple begins. */ agg->dtag_first = act; goto success; } } /* * This n-tuple is short by ntuple elements. Return failure. */ ASSERT(ntuple != 0); err: kmem_free(agg, sizeof (dtrace_aggregation_t)); return (NULL); success: /* * If the last action in the tuple has a size of zero, it's actually * an expression argument for the aggregating action. */ ASSERT(ecb->dte_action_last != NULL); act = ecb->dte_action_last; if (act->dta_kind == DTRACEACT_DIFEXPR) { ASSERT(act->dta_difo != NULL); if (act->dta_difo->dtdo_rtype.dtdt_size == 0) agg->dtag_hasarg = 1; } /* * We need to allocate an id for this aggregation. */ #if defined(sun) aggid = (dtrace_aggid_t)(uintptr_t)vmem_alloc(state->dts_aggid_arena, 1, VM_BESTFIT | VM_SLEEP); #else aggid = alloc_unr(state->dts_aggid_arena); #endif if (aggid - 1 >= state->dts_naggregations) { dtrace_aggregation_t **oaggs = state->dts_aggregations; dtrace_aggregation_t **aggs; int naggs = state->dts_naggregations << 1; int onaggs = state->dts_naggregations; ASSERT(aggid == state->dts_naggregations + 1); if (naggs == 0) { ASSERT(oaggs == NULL); naggs = 1; } aggs = kmem_zalloc(naggs * sizeof (*aggs), KM_SLEEP); if (oaggs != NULL) { bcopy(oaggs, aggs, onaggs * sizeof (*aggs)); kmem_free(oaggs, onaggs * sizeof (*aggs)); } state->dts_aggregations = aggs; state->dts_naggregations = naggs; } ASSERT(state->dts_aggregations[aggid - 1] == NULL); state->dts_aggregations[(agg->dtag_id = aggid) - 1] = agg; frec = &agg->dtag_first->dta_rec; if (frec->dtrd_alignment < sizeof (dtrace_aggid_t)) frec->dtrd_alignment = sizeof (dtrace_aggid_t); for (act = agg->dtag_first; act != NULL; act = act->dta_next) { ASSERT(!act->dta_intuple); act->dta_intuple = 1; } return (&agg->dtag_action); } static void dtrace_ecb_aggregation_destroy(dtrace_ecb_t *ecb, dtrace_action_t *act) { dtrace_aggregation_t *agg = (dtrace_aggregation_t *)act; dtrace_state_t *state = ecb->dte_state; dtrace_aggid_t aggid = agg->dtag_id; ASSERT(DTRACEACT_ISAGG(act->dta_kind)); #if defined(sun) vmem_free(state->dts_aggid_arena, (void *)(uintptr_t)aggid, 1); #else free_unr(state->dts_aggid_arena, aggid); #endif ASSERT(state->dts_aggregations[aggid - 1] == agg); state->dts_aggregations[aggid - 1] = NULL; kmem_free(agg, sizeof (dtrace_aggregation_t)); } static int dtrace_ecb_action_add(dtrace_ecb_t *ecb, dtrace_actdesc_t *desc) { dtrace_action_t *action, *last; dtrace_difo_t *dp = desc->dtad_difo; uint32_t size = 0, align = sizeof (uint8_t), mask; uint16_t format = 0; dtrace_recdesc_t *rec; dtrace_state_t *state = ecb->dte_state; dtrace_optval_t *opt = state->dts_options, nframes = 0, strsize; uint64_t arg = desc->dtad_arg; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(ecb->dte_action == NULL || ecb->dte_action->dta_refcnt == 1); if (DTRACEACT_ISAGG(desc->dtad_kind)) { /* * If this is an aggregating action, there must be neither * a speculate nor a commit on the action chain. */ dtrace_action_t *act; for (act = ecb->dte_action; act != NULL; act = act->dta_next) { if (act->dta_kind == DTRACEACT_COMMIT) return (EINVAL); if (act->dta_kind == DTRACEACT_SPECULATE) return (EINVAL); } action = dtrace_ecb_aggregation_create(ecb, desc); if (action == NULL) return (EINVAL); } else { if (DTRACEACT_ISDESTRUCTIVE(desc->dtad_kind) || (desc->dtad_kind == DTRACEACT_DIFEXPR && dp != NULL && dp->dtdo_destructive)) { state->dts_destructive = 1; } switch (desc->dtad_kind) { case DTRACEACT_PRINTF: case DTRACEACT_PRINTA: case DTRACEACT_SYSTEM: case DTRACEACT_FREOPEN: case DTRACEACT_DIFEXPR: /* * We know that our arg is a string -- turn it into a * format. */ if (arg == 0) { ASSERT(desc->dtad_kind == DTRACEACT_PRINTA || desc->dtad_kind == DTRACEACT_DIFEXPR); format = 0; } else { ASSERT(arg != 0); #if defined(sun) ASSERT(arg > KERNELBASE); #endif format = dtrace_format_add(state, (char *)(uintptr_t)arg); } /*FALLTHROUGH*/ case DTRACEACT_LIBACT: case DTRACEACT_TRACEMEM: case DTRACEACT_TRACEMEM_DYNSIZE: if (dp == NULL) return (EINVAL); if ((size = dp->dtdo_rtype.dtdt_size) != 0) break; if (dp->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) { if (!(dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) return (EINVAL); size = opt[DTRACEOPT_STRSIZE]; } break; case DTRACEACT_STACK: if ((nframes = arg) == 0) { nframes = opt[DTRACEOPT_STACKFRAMES]; ASSERT(nframes > 0); arg = nframes; } size = nframes * sizeof (pc_t); break; case DTRACEACT_JSTACK: if ((strsize = DTRACE_USTACK_STRSIZE(arg)) == 0) strsize = opt[DTRACEOPT_JSTACKSTRSIZE]; if ((nframes = DTRACE_USTACK_NFRAMES(arg)) == 0) nframes = opt[DTRACEOPT_JSTACKFRAMES]; arg = DTRACE_USTACK_ARG(nframes, strsize); /*FALLTHROUGH*/ case DTRACEACT_USTACK: if (desc->dtad_kind != DTRACEACT_JSTACK && (nframes = DTRACE_USTACK_NFRAMES(arg)) == 0) { strsize = DTRACE_USTACK_STRSIZE(arg); nframes = opt[DTRACEOPT_USTACKFRAMES]; ASSERT(nframes > 0); arg = DTRACE_USTACK_ARG(nframes, strsize); } /* * Save a slot for the pid. */ size = (nframes + 1) * sizeof (uint64_t); size += DTRACE_USTACK_STRSIZE(arg); size = P2ROUNDUP(size, (uint32_t)(sizeof (uintptr_t))); break; case DTRACEACT_SYM: case DTRACEACT_MOD: if (dp == NULL || ((size = dp->dtdo_rtype.dtdt_size) != sizeof (uint64_t)) || (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) return (EINVAL); break; case DTRACEACT_USYM: case DTRACEACT_UMOD: case DTRACEACT_UADDR: if (dp == NULL || (dp->dtdo_rtype.dtdt_size != sizeof (uint64_t)) || (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) return (EINVAL); /* * We have a slot for the pid, plus a slot for the * argument. To keep things simple (aligned with * bitness-neutral sizing), we store each as a 64-bit * quantity. */ size = 2 * sizeof (uint64_t); break; case DTRACEACT_STOP: case DTRACEACT_BREAKPOINT: case DTRACEACT_PANIC: break; case DTRACEACT_CHILL: case DTRACEACT_DISCARD: case DTRACEACT_RAISE: if (dp == NULL) return (EINVAL); break; case DTRACEACT_EXIT: if (dp == NULL || (size = dp->dtdo_rtype.dtdt_size) != sizeof (int) || (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) return (EINVAL); break; case DTRACEACT_SPECULATE: if (ecb->dte_size > sizeof (dtrace_rechdr_t)) return (EINVAL); if (dp == NULL) return (EINVAL); state->dts_speculates = 1; break; case DTRACEACT_PRINTM: size = dp->dtdo_rtype.dtdt_size; break; case DTRACEACT_PRINTT: size = dp->dtdo_rtype.dtdt_size; break; case DTRACEACT_COMMIT: { dtrace_action_t *act = ecb->dte_action; for (; act != NULL; act = act->dta_next) { if (act->dta_kind == DTRACEACT_COMMIT) return (EINVAL); } if (dp == NULL) return (EINVAL); break; } default: return (EINVAL); } if (size != 0 || desc->dtad_kind == DTRACEACT_SPECULATE) { /* * If this is a data-storing action or a speculate, * we must be sure that there isn't a commit on the * action chain. */ dtrace_action_t *act = ecb->dte_action; for (; act != NULL; act = act->dta_next) { if (act->dta_kind == DTRACEACT_COMMIT) return (EINVAL); } } action = kmem_zalloc(sizeof (dtrace_action_t), KM_SLEEP); action->dta_rec.dtrd_size = size; } action->dta_refcnt = 1; rec = &action->dta_rec; size = rec->dtrd_size; for (mask = sizeof (uint64_t) - 1; size != 0 && mask > 0; mask >>= 1) { if (!(size & mask)) { align = mask + 1; break; } } action->dta_kind = desc->dtad_kind; if ((action->dta_difo = dp) != NULL) dtrace_difo_hold(dp); rec->dtrd_action = action->dta_kind; rec->dtrd_arg = arg; rec->dtrd_uarg = desc->dtad_uarg; rec->dtrd_alignment = (uint16_t)align; rec->dtrd_format = format; if ((last = ecb->dte_action_last) != NULL) { ASSERT(ecb->dte_action != NULL); action->dta_prev = last; last->dta_next = action; } else { ASSERT(ecb->dte_action == NULL); ecb->dte_action = action; } ecb->dte_action_last = action; return (0); } static void dtrace_ecb_action_remove(dtrace_ecb_t *ecb) { dtrace_action_t *act = ecb->dte_action, *next; dtrace_vstate_t *vstate = &ecb->dte_state->dts_vstate; dtrace_difo_t *dp; uint16_t format; if (act != NULL && act->dta_refcnt > 1) { ASSERT(act->dta_next == NULL || act->dta_next->dta_refcnt == 1); act->dta_refcnt--; } else { for (; act != NULL; act = next) { next = act->dta_next; ASSERT(next != NULL || act == ecb->dte_action_last); ASSERT(act->dta_refcnt == 1); if ((format = act->dta_rec.dtrd_format) != 0) dtrace_format_remove(ecb->dte_state, format); if ((dp = act->dta_difo) != NULL) dtrace_difo_release(dp, vstate); if (DTRACEACT_ISAGG(act->dta_kind)) { dtrace_ecb_aggregation_destroy(ecb, act); } else { kmem_free(act, sizeof (dtrace_action_t)); } } } ecb->dte_action = NULL; ecb->dte_action_last = NULL; ecb->dte_size = 0; } static void dtrace_ecb_disable(dtrace_ecb_t *ecb) { /* * We disable the ECB by removing it from its probe. */ dtrace_ecb_t *pecb, *prev = NULL; dtrace_probe_t *probe = ecb->dte_probe; ASSERT(MUTEX_HELD(&dtrace_lock)); if (probe == NULL) { /* * This is the NULL probe; there is nothing to disable. */ return; } for (pecb = probe->dtpr_ecb; pecb != NULL; pecb = pecb->dte_next) { if (pecb == ecb) break; prev = pecb; } ASSERT(pecb != NULL); if (prev == NULL) { probe->dtpr_ecb = ecb->dte_next; } else { prev->dte_next = ecb->dte_next; } if (ecb == probe->dtpr_ecb_last) { ASSERT(ecb->dte_next == NULL); probe->dtpr_ecb_last = prev; } /* * The ECB has been disconnected from the probe; now sync to assure * that all CPUs have seen the change before returning. */ dtrace_sync(); if (probe->dtpr_ecb == NULL) { /* * That was the last ECB on the probe; clear the predicate * cache ID for the probe, disable it and sync one more time * to assure that we'll never hit it again. */ dtrace_provider_t *prov = probe->dtpr_provider; ASSERT(ecb->dte_next == NULL); ASSERT(probe->dtpr_ecb_last == NULL); probe->dtpr_predcache = DTRACE_CACHEIDNONE; prov->dtpv_pops.dtps_disable(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); dtrace_sync(); } else { /* * There is at least one ECB remaining on the probe. If there * is _exactly_ one, set the probe's predicate cache ID to be * the predicate cache ID of the remaining ECB. */ ASSERT(probe->dtpr_ecb_last != NULL); ASSERT(probe->dtpr_predcache == DTRACE_CACHEIDNONE); if (probe->dtpr_ecb == probe->dtpr_ecb_last) { dtrace_predicate_t *p = probe->dtpr_ecb->dte_predicate; ASSERT(probe->dtpr_ecb->dte_next == NULL); if (p != NULL) probe->dtpr_predcache = p->dtp_cacheid; } ecb->dte_next = NULL; } } static void dtrace_ecb_destroy(dtrace_ecb_t *ecb) { dtrace_state_t *state = ecb->dte_state; dtrace_vstate_t *vstate = &state->dts_vstate; dtrace_predicate_t *pred; dtrace_epid_t epid = ecb->dte_epid; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(ecb->dte_next == NULL); ASSERT(ecb->dte_probe == NULL || ecb->dte_probe->dtpr_ecb != ecb); if ((pred = ecb->dte_predicate) != NULL) dtrace_predicate_release(pred, vstate); dtrace_ecb_action_remove(ecb); ASSERT(state->dts_ecbs[epid - 1] == ecb); state->dts_ecbs[epid - 1] = NULL; kmem_free(ecb, sizeof (dtrace_ecb_t)); } static dtrace_ecb_t * dtrace_ecb_create(dtrace_state_t *state, dtrace_probe_t *probe, dtrace_enabling_t *enab) { dtrace_ecb_t *ecb; dtrace_predicate_t *pred; dtrace_actdesc_t *act; dtrace_provider_t *prov; dtrace_ecbdesc_t *desc = enab->dten_current; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(state != NULL); ecb = dtrace_ecb_add(state, probe); ecb->dte_uarg = desc->dted_uarg; if ((pred = desc->dted_pred.dtpdd_predicate) != NULL) { dtrace_predicate_hold(pred); ecb->dte_predicate = pred; } if (probe != NULL) { /* * If the provider shows more leg than the consumer is old * enough to see, we need to enable the appropriate implicit * predicate bits to prevent the ecb from activating at * revealing times. * * Providers specifying DTRACE_PRIV_USER at register time * are stating that they need the /proc-style privilege * model to be enforced, and this is what DTRACE_COND_OWNER * and DTRACE_COND_ZONEOWNER will then do at probe time. */ prov = probe->dtpr_provider; if (!(state->dts_cred.dcr_visible & DTRACE_CRV_ALLPROC) && (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_USER)) ecb->dte_cond |= DTRACE_COND_OWNER; if (!(state->dts_cred.dcr_visible & DTRACE_CRV_ALLZONE) && (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_USER)) ecb->dte_cond |= DTRACE_COND_ZONEOWNER; /* * If the provider shows us kernel innards and the user * is lacking sufficient privilege, enable the * DTRACE_COND_USERMODE implicit predicate. */ if (!(state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) && (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_KERNEL)) ecb->dte_cond |= DTRACE_COND_USERMODE; } if (dtrace_ecb_create_cache != NULL) { /* * If we have a cached ecb, we'll use its action list instead * of creating our own (saving both time and space). */ dtrace_ecb_t *cached = dtrace_ecb_create_cache; dtrace_action_t *act = cached->dte_action; if (act != NULL) { ASSERT(act->dta_refcnt > 0); act->dta_refcnt++; ecb->dte_action = act; ecb->dte_action_last = cached->dte_action_last; ecb->dte_needed = cached->dte_needed; ecb->dte_size = cached->dte_size; ecb->dte_alignment = cached->dte_alignment; } return (ecb); } for (act = desc->dted_action; act != NULL; act = act->dtad_next) { if ((enab->dten_error = dtrace_ecb_action_add(ecb, act)) != 0) { dtrace_ecb_destroy(ecb); return (NULL); } } dtrace_ecb_resize(ecb); return (dtrace_ecb_create_cache = ecb); } static int dtrace_ecb_create_enable(dtrace_probe_t *probe, void *arg) { dtrace_ecb_t *ecb; dtrace_enabling_t *enab = arg; dtrace_state_t *state = enab->dten_vstate->dtvs_state; ASSERT(state != NULL); if (probe != NULL && probe->dtpr_gen < enab->dten_probegen) { /* * This probe was created in a generation for which this * enabling has previously created ECBs; we don't want to * enable it again, so just kick out. */ return (DTRACE_MATCH_NEXT); } if ((ecb = dtrace_ecb_create(state, probe, enab)) == NULL) return (DTRACE_MATCH_DONE); dtrace_ecb_enable(ecb); return (DTRACE_MATCH_NEXT); } static dtrace_ecb_t * dtrace_epid2ecb(dtrace_state_t *state, dtrace_epid_t id) { dtrace_ecb_t *ecb; ASSERT(MUTEX_HELD(&dtrace_lock)); if (id == 0 || id > state->dts_necbs) return (NULL); ASSERT(state->dts_necbs > 0 && state->dts_ecbs != NULL); ASSERT((ecb = state->dts_ecbs[id - 1]) == NULL || ecb->dte_epid == id); return (state->dts_ecbs[id - 1]); } static dtrace_aggregation_t * dtrace_aggid2agg(dtrace_state_t *state, dtrace_aggid_t id) { dtrace_aggregation_t *agg; ASSERT(MUTEX_HELD(&dtrace_lock)); if (id == 0 || id > state->dts_naggregations) return (NULL); ASSERT(state->dts_naggregations > 0 && state->dts_aggregations != NULL); ASSERT((agg = state->dts_aggregations[id - 1]) == NULL || agg->dtag_id == id); return (state->dts_aggregations[id - 1]); } /* * DTrace Buffer Functions * * The following functions manipulate DTrace buffers. Most of these functions * are called in the context of establishing or processing consumer state; * exceptions are explicitly noted. */ /* * Note: called from cross call context. This function switches the two * buffers on a given CPU. The atomicity of this operation is assured by * disabling interrupts while the actual switch takes place; the disabling of * interrupts serializes the execution with any execution of dtrace_probe() on * the same CPU. */ static void dtrace_buffer_switch(dtrace_buffer_t *buf) { caddr_t tomax = buf->dtb_tomax; caddr_t xamot = buf->dtb_xamot; dtrace_icookie_t cookie; hrtime_t now; ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH)); ASSERT(!(buf->dtb_flags & DTRACEBUF_RING)); cookie = dtrace_interrupt_disable(); now = dtrace_gethrtime(); buf->dtb_tomax = xamot; buf->dtb_xamot = tomax; buf->dtb_xamot_drops = buf->dtb_drops; buf->dtb_xamot_offset = buf->dtb_offset; buf->dtb_xamot_errors = buf->dtb_errors; buf->dtb_xamot_flags = buf->dtb_flags; buf->dtb_offset = 0; buf->dtb_drops = 0; buf->dtb_errors = 0; buf->dtb_flags &= ~(DTRACEBUF_ERROR | DTRACEBUF_DROPPED); buf->dtb_interval = now - buf->dtb_switched; buf->dtb_switched = now; dtrace_interrupt_enable(cookie); } /* * Note: called from cross call context. This function activates a buffer * on a CPU. As with dtrace_buffer_switch(), the atomicity of the operation * is guaranteed by the disabling of interrupts. */ static void dtrace_buffer_activate(dtrace_state_t *state) { dtrace_buffer_t *buf; dtrace_icookie_t cookie = dtrace_interrupt_disable(); buf = &state->dts_buffer[curcpu]; if (buf->dtb_tomax != NULL) { /* * We might like to assert that the buffer is marked inactive, * but this isn't necessarily true: the buffer for the CPU * that processes the BEGIN probe has its buffer activated * manually. In this case, we take the (harmless) action * re-clearing the bit INACTIVE bit. */ buf->dtb_flags &= ~DTRACEBUF_INACTIVE; } dtrace_interrupt_enable(cookie); } static int dtrace_buffer_alloc(dtrace_buffer_t *bufs, size_t size, int flags, processorid_t cpu, int *factor) { #if defined(sun) cpu_t *cp; #endif dtrace_buffer_t *buf; int allocated = 0, desired = 0; #if defined(sun) ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); *factor = 1; if (size > dtrace_nonroot_maxsize && !PRIV_POLICY_CHOICE(CRED(), PRIV_ALL, B_FALSE)) return (EFBIG); cp = cpu_list; do { if (cpu != DTRACE_CPUALL && cpu != cp->cpu_id) continue; buf = &bufs[cp->cpu_id]; /* * If there is already a buffer allocated for this CPU, it * is only possible that this is a DR event. In this case, */ if (buf->dtb_tomax != NULL) { ASSERT(buf->dtb_size == size); continue; } ASSERT(buf->dtb_xamot == NULL); if ((buf->dtb_tomax = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) goto err; buf->dtb_size = size; buf->dtb_flags = flags; buf->dtb_offset = 0; buf->dtb_drops = 0; if (flags & DTRACEBUF_NOSWITCH) continue; if ((buf->dtb_xamot = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) goto err; } while ((cp = cp->cpu_next) != cpu_list); return (0); err: cp = cpu_list; do { if (cpu != DTRACE_CPUALL && cpu != cp->cpu_id) continue; buf = &bufs[cp->cpu_id]; desired += 2; if (buf->dtb_xamot != NULL) { ASSERT(buf->dtb_tomax != NULL); ASSERT(buf->dtb_size == size); kmem_free(buf->dtb_xamot, size); allocated++; } if (buf->dtb_tomax != NULL) { ASSERT(buf->dtb_size == size); kmem_free(buf->dtb_tomax, size); allocated++; } buf->dtb_tomax = NULL; buf->dtb_xamot = NULL; buf->dtb_size = 0; } while ((cp = cp->cpu_next) != cpu_list); #else int i; *factor = 1; #if defined(__amd64__) || defined(__mips__) || defined(__powerpc__) /* * FreeBSD isn't good at limiting the amount of memory we * ask to malloc, so let's place a limit here before trying * to do something that might well end in tears at bedtime. */ if (size > physmem * PAGE_SIZE / (128 * (mp_maxid + 1))) return (ENOMEM); #endif ASSERT(MUTEX_HELD(&dtrace_lock)); CPU_FOREACH(i) { if (cpu != DTRACE_CPUALL && cpu != i) continue; buf = &bufs[i]; /* * If there is already a buffer allocated for this CPU, it * is only possible that this is a DR event. In this case, * the buffer size must match our specified size. */ if (buf->dtb_tomax != NULL) { ASSERT(buf->dtb_size == size); continue; } ASSERT(buf->dtb_xamot == NULL); if ((buf->dtb_tomax = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) goto err; buf->dtb_size = size; buf->dtb_flags = flags; buf->dtb_offset = 0; buf->dtb_drops = 0; if (flags & DTRACEBUF_NOSWITCH) continue; if ((buf->dtb_xamot = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) goto err; } return (0); err: /* * Error allocating memory, so free the buffers that were * allocated before the failed allocation. */ CPU_FOREACH(i) { if (cpu != DTRACE_CPUALL && cpu != i) continue; buf = &bufs[i]; desired += 2; if (buf->dtb_xamot != NULL) { ASSERT(buf->dtb_tomax != NULL); ASSERT(buf->dtb_size == size); kmem_free(buf->dtb_xamot, size); allocated++; } if (buf->dtb_tomax != NULL) { ASSERT(buf->dtb_size == size); kmem_free(buf->dtb_tomax, size); allocated++; } buf->dtb_tomax = NULL; buf->dtb_xamot = NULL; buf->dtb_size = 0; } #endif *factor = desired / (allocated > 0 ? allocated : 1); return (ENOMEM); } /* * Note: called from probe context. This function just increments the drop * count on a buffer. It has been made a function to allow for the * possibility of understanding the source of mysterious drop counts. (A * problem for which one may be particularly disappointed that DTrace cannot * be used to understand DTrace.) */ static void dtrace_buffer_drop(dtrace_buffer_t *buf) { buf->dtb_drops++; } /* * Note: called from probe context. This function is called to reserve space * in a buffer. If mstate is non-NULL, sets the scratch base and size in the * mstate. Returns the new offset in the buffer, or a negative value if an * error has occurred. */ static intptr_t dtrace_buffer_reserve(dtrace_buffer_t *buf, size_t needed, size_t align, dtrace_state_t *state, dtrace_mstate_t *mstate) { intptr_t offs = buf->dtb_offset, soffs; intptr_t woffs; caddr_t tomax; size_t total; if (buf->dtb_flags & DTRACEBUF_INACTIVE) return (-1); if ((tomax = buf->dtb_tomax) == NULL) { dtrace_buffer_drop(buf); return (-1); } if (!(buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL))) { while (offs & (align - 1)) { /* * Assert that our alignment is off by a number which * is itself sizeof (uint32_t) aligned. */ ASSERT(!((align - (offs & (align - 1))) & (sizeof (uint32_t) - 1))); DTRACE_STORE(uint32_t, tomax, offs, DTRACE_EPIDNONE); offs += sizeof (uint32_t); } if ((soffs = offs + needed) > buf->dtb_size) { dtrace_buffer_drop(buf); return (-1); } if (mstate == NULL) return (offs); mstate->dtms_scratch_base = (uintptr_t)tomax + soffs; mstate->dtms_scratch_size = buf->dtb_size - soffs; mstate->dtms_scratch_ptr = mstate->dtms_scratch_base; return (offs); } if (buf->dtb_flags & DTRACEBUF_FILL) { if (state->dts_activity != DTRACE_ACTIVITY_COOLDOWN && (buf->dtb_flags & DTRACEBUF_FULL)) return (-1); goto out; } total = needed + (offs & (align - 1)); /* * For a ring buffer, life is quite a bit more complicated. Before * we can store any padding, we need to adjust our wrapping offset. * (If we've never before wrapped or we're not about to, no adjustment * is required.) */ if ((buf->dtb_flags & DTRACEBUF_WRAPPED) || offs + total > buf->dtb_size) { woffs = buf->dtb_xamot_offset; if (offs + total > buf->dtb_size) { /* * We can't fit in the end of the buffer. First, a * sanity check that we can fit in the buffer at all. */ if (total > buf->dtb_size) { dtrace_buffer_drop(buf); return (-1); } /* * We're going to be storing at the top of the buffer, * so now we need to deal with the wrapped offset. We * only reset our wrapped offset to 0 if it is * currently greater than the current offset. If it * is less than the current offset, it is because a * previous allocation induced a wrap -- but the * allocation didn't subsequently take the space due * to an error or false predicate evaluation. In this * case, we'll just leave the wrapped offset alone: if * the wrapped offset hasn't been advanced far enough * for this allocation, it will be adjusted in the * lower loop. */ if (buf->dtb_flags & DTRACEBUF_WRAPPED) { if (woffs >= offs) woffs = 0; } else { woffs = 0; } /* * Now we know that we're going to be storing to the * top of the buffer and that there is room for us * there. We need to clear the buffer from the current * offset to the end (there may be old gunk there). */ while (offs < buf->dtb_size) tomax[offs++] = 0; /* * We need to set our offset to zero. And because we * are wrapping, we need to set the bit indicating as * much. We can also adjust our needed space back * down to the space required by the ECB -- we know * that the top of the buffer is aligned. */ offs = 0; total = needed; buf->dtb_flags |= DTRACEBUF_WRAPPED; } else { /* * There is room for us in the buffer, so we simply * need to check the wrapped offset. */ if (woffs < offs) { /* * The wrapped offset is less than the offset. * This can happen if we allocated buffer space * that induced a wrap, but then we didn't * subsequently take the space due to an error * or false predicate evaluation. This is * okay; we know that _this_ allocation isn't * going to induce a wrap. We still can't * reset the wrapped offset to be zero, * however: the space may have been trashed in * the previous failed probe attempt. But at * least the wrapped offset doesn't need to * be adjusted at all... */ goto out; } } while (offs + total > woffs) { dtrace_epid_t epid = *(uint32_t *)(tomax + woffs); size_t size; if (epid == DTRACE_EPIDNONE) { size = sizeof (uint32_t); } else { ASSERT3U(epid, <=, state->dts_necbs); ASSERT(state->dts_ecbs[epid - 1] != NULL); size = state->dts_ecbs[epid - 1]->dte_size; } ASSERT(woffs + size <= buf->dtb_size); ASSERT(size != 0); if (woffs + size == buf->dtb_size) { /* * We've reached the end of the buffer; we want * to set the wrapped offset to 0 and break * out. However, if the offs is 0, then we're * in a strange edge-condition: the amount of * space that we want to reserve plus the size * of the record that we're overwriting is * greater than the size of the buffer. This * is problematic because if we reserve the * space but subsequently don't consume it (due * to a failed predicate or error) the wrapped * offset will be 0 -- yet the EPID at offset 0 * will not be committed. This situation is * relatively easy to deal with: if we're in * this case, the buffer is indistinguishable * from one that hasn't wrapped; we need only * finish the job by clearing the wrapped bit, * explicitly setting the offset to be 0, and * zero'ing out the old data in the buffer. */ if (offs == 0) { buf->dtb_flags &= ~DTRACEBUF_WRAPPED; buf->dtb_offset = 0; woffs = total; while (woffs < buf->dtb_size) tomax[woffs++] = 0; } woffs = 0; break; } woffs += size; } /* * We have a wrapped offset. It may be that the wrapped offset * has become zero -- that's okay. */ buf->dtb_xamot_offset = woffs; } out: /* * Now we can plow the buffer with any necessary padding. */ while (offs & (align - 1)) { /* * Assert that our alignment is off by a number which * is itself sizeof (uint32_t) aligned. */ ASSERT(!((align - (offs & (align - 1))) & (sizeof (uint32_t) - 1))); DTRACE_STORE(uint32_t, tomax, offs, DTRACE_EPIDNONE); offs += sizeof (uint32_t); } if (buf->dtb_flags & DTRACEBUF_FILL) { if (offs + needed > buf->dtb_size - state->dts_reserve) { buf->dtb_flags |= DTRACEBUF_FULL; return (-1); } } if (mstate == NULL) return (offs); /* * For ring buffers and fill buffers, the scratch space is always * the inactive buffer. */ mstate->dtms_scratch_base = (uintptr_t)buf->dtb_xamot; mstate->dtms_scratch_size = buf->dtb_size; mstate->dtms_scratch_ptr = mstate->dtms_scratch_base; return (offs); } static void dtrace_buffer_polish(dtrace_buffer_t *buf) { ASSERT(buf->dtb_flags & DTRACEBUF_RING); ASSERT(MUTEX_HELD(&dtrace_lock)); if (!(buf->dtb_flags & DTRACEBUF_WRAPPED)) return; /* * We need to polish the ring buffer. There are three cases: * * - The first (and presumably most common) is that there is no gap * between the buffer offset and the wrapped offset. In this case, * there is nothing in the buffer that isn't valid data; we can * mark the buffer as polished and return. * * - The second (less common than the first but still more common * than the third) is that there is a gap between the buffer offset * and the wrapped offset, and the wrapped offset is larger than the * buffer offset. This can happen because of an alignment issue, or * can happen because of a call to dtrace_buffer_reserve() that * didn't subsequently consume the buffer space. In this case, * we need to zero the data from the buffer offset to the wrapped * offset. * * - The third (and least common) is that there is a gap between the * buffer offset and the wrapped offset, but the wrapped offset is * _less_ than the buffer offset. This can only happen because a * call to dtrace_buffer_reserve() induced a wrap, but the space * was not subsequently consumed. In this case, we need to zero the * space from the offset to the end of the buffer _and_ from the * top of the buffer to the wrapped offset. */ if (buf->dtb_offset < buf->dtb_xamot_offset) { bzero(buf->dtb_tomax + buf->dtb_offset, buf->dtb_xamot_offset - buf->dtb_offset); } if (buf->dtb_offset > buf->dtb_xamot_offset) { bzero(buf->dtb_tomax + buf->dtb_offset, buf->dtb_size - buf->dtb_offset); bzero(buf->dtb_tomax, buf->dtb_xamot_offset); } } /* * This routine determines if data generated at the specified time has likely * been entirely consumed at user-level. This routine is called to determine * if an ECB on a defunct probe (but for an active enabling) can be safely * disabled and destroyed. */ static int dtrace_buffer_consumed(dtrace_buffer_t *bufs, hrtime_t when) { int i; for (i = 0; i < NCPU; i++) { dtrace_buffer_t *buf = &bufs[i]; if (buf->dtb_size == 0) continue; if (buf->dtb_flags & DTRACEBUF_RING) return (0); if (!buf->dtb_switched && buf->dtb_offset != 0) return (0); if (buf->dtb_switched - buf->dtb_interval < when) return (0); } return (1); } static void dtrace_buffer_free(dtrace_buffer_t *bufs) { int i; for (i = 0; i < NCPU; i++) { dtrace_buffer_t *buf = &bufs[i]; if (buf->dtb_tomax == NULL) { ASSERT(buf->dtb_xamot == NULL); ASSERT(buf->dtb_size == 0); continue; } if (buf->dtb_xamot != NULL) { ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH)); kmem_free(buf->dtb_xamot, buf->dtb_size); } kmem_free(buf->dtb_tomax, buf->dtb_size); buf->dtb_size = 0; buf->dtb_tomax = NULL; buf->dtb_xamot = NULL; } } /* * DTrace Enabling Functions */ static dtrace_enabling_t * dtrace_enabling_create(dtrace_vstate_t *vstate) { dtrace_enabling_t *enab; enab = kmem_zalloc(sizeof (dtrace_enabling_t), KM_SLEEP); enab->dten_vstate = vstate; return (enab); } static void dtrace_enabling_add(dtrace_enabling_t *enab, dtrace_ecbdesc_t *ecb) { dtrace_ecbdesc_t **ndesc; size_t osize, nsize; /* * We can't add to enablings after we've enabled them, or after we've * retained them. */ ASSERT(enab->dten_probegen == 0); ASSERT(enab->dten_next == NULL && enab->dten_prev == NULL); if (enab->dten_ndesc < enab->dten_maxdesc) { enab->dten_desc[enab->dten_ndesc++] = ecb; return; } osize = enab->dten_maxdesc * sizeof (dtrace_enabling_t *); if (enab->dten_maxdesc == 0) { enab->dten_maxdesc = 1; } else { enab->dten_maxdesc <<= 1; } ASSERT(enab->dten_ndesc < enab->dten_maxdesc); nsize = enab->dten_maxdesc * sizeof (dtrace_enabling_t *); ndesc = kmem_zalloc(nsize, KM_SLEEP); bcopy(enab->dten_desc, ndesc, osize); if (enab->dten_desc != NULL) kmem_free(enab->dten_desc, osize); enab->dten_desc = ndesc; enab->dten_desc[enab->dten_ndesc++] = ecb; } static void dtrace_enabling_addlike(dtrace_enabling_t *enab, dtrace_ecbdesc_t *ecb, dtrace_probedesc_t *pd) { dtrace_ecbdesc_t *new; dtrace_predicate_t *pred; dtrace_actdesc_t *act; /* * We're going to create a new ECB description that matches the * specified ECB in every way, but has the specified probe description. */ new = kmem_zalloc(sizeof (dtrace_ecbdesc_t), KM_SLEEP); if ((pred = ecb->dted_pred.dtpdd_predicate) != NULL) dtrace_predicate_hold(pred); for (act = ecb->dted_action; act != NULL; act = act->dtad_next) dtrace_actdesc_hold(act); new->dted_action = ecb->dted_action; new->dted_pred = ecb->dted_pred; new->dted_probe = *pd; new->dted_uarg = ecb->dted_uarg; dtrace_enabling_add(enab, new); } static void dtrace_enabling_dump(dtrace_enabling_t *enab) { int i; for (i = 0; i < enab->dten_ndesc; i++) { dtrace_probedesc_t *desc = &enab->dten_desc[i]->dted_probe; cmn_err(CE_NOTE, "enabling probe %d (%s:%s:%s:%s)", i, desc->dtpd_provider, desc->dtpd_mod, desc->dtpd_func, desc->dtpd_name); } } static void dtrace_enabling_destroy(dtrace_enabling_t *enab) { int i; dtrace_ecbdesc_t *ep; dtrace_vstate_t *vstate = enab->dten_vstate; ASSERT(MUTEX_HELD(&dtrace_lock)); for (i = 0; i < enab->dten_ndesc; i++) { dtrace_actdesc_t *act, *next; dtrace_predicate_t *pred; ep = enab->dten_desc[i]; if ((pred = ep->dted_pred.dtpdd_predicate) != NULL) dtrace_predicate_release(pred, vstate); for (act = ep->dted_action; act != NULL; act = next) { next = act->dtad_next; dtrace_actdesc_release(act, vstate); } kmem_free(ep, sizeof (dtrace_ecbdesc_t)); } if (enab->dten_desc != NULL) kmem_free(enab->dten_desc, enab->dten_maxdesc * sizeof (dtrace_enabling_t *)); /* * If this was a retained enabling, decrement the dts_nretained count * and take it off of the dtrace_retained list. */ if (enab->dten_prev != NULL || enab->dten_next != NULL || dtrace_retained == enab) { ASSERT(enab->dten_vstate->dtvs_state != NULL); ASSERT(enab->dten_vstate->dtvs_state->dts_nretained > 0); enab->dten_vstate->dtvs_state->dts_nretained--; dtrace_retained_gen++; } if (enab->dten_prev == NULL) { if (dtrace_retained == enab) { dtrace_retained = enab->dten_next; if (dtrace_retained != NULL) dtrace_retained->dten_prev = NULL; } } else { ASSERT(enab != dtrace_retained); ASSERT(dtrace_retained != NULL); enab->dten_prev->dten_next = enab->dten_next; } if (enab->dten_next != NULL) { ASSERT(dtrace_retained != NULL); enab->dten_next->dten_prev = enab->dten_prev; } kmem_free(enab, sizeof (dtrace_enabling_t)); } static int dtrace_enabling_retain(dtrace_enabling_t *enab) { dtrace_state_t *state; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(enab->dten_next == NULL && enab->dten_prev == NULL); ASSERT(enab->dten_vstate != NULL); state = enab->dten_vstate->dtvs_state; ASSERT(state != NULL); /* * We only allow each state to retain dtrace_retain_max enablings. */ if (state->dts_nretained >= dtrace_retain_max) return (ENOSPC); state->dts_nretained++; dtrace_retained_gen++; if (dtrace_retained == NULL) { dtrace_retained = enab; return (0); } enab->dten_next = dtrace_retained; dtrace_retained->dten_prev = enab; dtrace_retained = enab; return (0); } static int dtrace_enabling_replicate(dtrace_state_t *state, dtrace_probedesc_t *match, dtrace_probedesc_t *create) { dtrace_enabling_t *new, *enab; int found = 0, err = ENOENT; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(strlen(match->dtpd_provider) < DTRACE_PROVNAMELEN); ASSERT(strlen(match->dtpd_mod) < DTRACE_MODNAMELEN); ASSERT(strlen(match->dtpd_func) < DTRACE_FUNCNAMELEN); ASSERT(strlen(match->dtpd_name) < DTRACE_NAMELEN); new = dtrace_enabling_create(&state->dts_vstate); /* * Iterate over all retained enablings, looking for enablings that * match the specified state. */ for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) { int i; /* * dtvs_state can only be NULL for helper enablings -- and * helper enablings can't be retained. */ ASSERT(enab->dten_vstate->dtvs_state != NULL); if (enab->dten_vstate->dtvs_state != state) continue; /* * Now iterate over each probe description; we're looking for * an exact match to the specified probe description. */ for (i = 0; i < enab->dten_ndesc; i++) { dtrace_ecbdesc_t *ep = enab->dten_desc[i]; dtrace_probedesc_t *pd = &ep->dted_probe; if (strcmp(pd->dtpd_provider, match->dtpd_provider)) continue; if (strcmp(pd->dtpd_mod, match->dtpd_mod)) continue; if (strcmp(pd->dtpd_func, match->dtpd_func)) continue; if (strcmp(pd->dtpd_name, match->dtpd_name)) continue; /* * We have a winning probe! Add it to our growing * enabling. */ found = 1; dtrace_enabling_addlike(new, ep, create); } } if (!found || (err = dtrace_enabling_retain(new)) != 0) { dtrace_enabling_destroy(new); return (err); } return (0); } static void dtrace_enabling_retract(dtrace_state_t *state) { dtrace_enabling_t *enab, *next; ASSERT(MUTEX_HELD(&dtrace_lock)); /* * Iterate over all retained enablings, destroy the enablings retained * for the specified state. */ for (enab = dtrace_retained; enab != NULL; enab = next) { next = enab->dten_next; /* * dtvs_state can only be NULL for helper enablings -- and * helper enablings can't be retained. */ ASSERT(enab->dten_vstate->dtvs_state != NULL); if (enab->dten_vstate->dtvs_state == state) { ASSERT(state->dts_nretained > 0); dtrace_enabling_destroy(enab); } } ASSERT(state->dts_nretained == 0); } static int dtrace_enabling_match(dtrace_enabling_t *enab, int *nmatched) { int i = 0; int matched = 0; ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); for (i = 0; i < enab->dten_ndesc; i++) { dtrace_ecbdesc_t *ep = enab->dten_desc[i]; enab->dten_current = ep; enab->dten_error = 0; matched += dtrace_probe_enable(&ep->dted_probe, enab); if (enab->dten_error != 0) { /* * If we get an error half-way through enabling the * probes, we kick out -- perhaps with some number of * them enabled. Leaving enabled probes enabled may * be slightly confusing for user-level, but we expect * that no one will attempt to actually drive on in * the face of such errors. If this is an anonymous * enabling (indicated with a NULL nmatched pointer), * we cmn_err() a message. We aren't expecting to * get such an error -- such as it can exist at all, * it would be a result of corrupted DOF in the driver * properties. */ if (nmatched == NULL) { cmn_err(CE_WARN, "dtrace_enabling_match() " "error on %p: %d", (void *)ep, enab->dten_error); } return (enab->dten_error); } } enab->dten_probegen = dtrace_probegen; if (nmatched != NULL) *nmatched = matched; return (0); } static void dtrace_enabling_matchall(void) { dtrace_enabling_t *enab; mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); /* * Iterate over all retained enablings to see if any probes match * against them. We only perform this operation on enablings for which * we have sufficient permissions by virtue of being in the global zone * or in the same zone as the DTrace client. Because we can be called * after dtrace_detach() has been called, we cannot assert that there * are retained enablings. We can safely load from dtrace_retained, * however: the taskq_destroy() at the end of dtrace_detach() will * block pending our completion. */ for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) { #if defined(sun) cred_t *cr = enab->dten_vstate->dtvs_state->dts_cred.dcr_cred; if (INGLOBALZONE(curproc) || cr != NULL && getzoneid() == crgetzoneid(cr)) #endif (void) dtrace_enabling_match(enab, NULL); } mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); } /* * If an enabling is to be enabled without having matched probes (that is, if * dtrace_state_go() is to be called on the underlying dtrace_state_t), the * enabling must be _primed_ by creating an ECB for every ECB description. * This must be done to assure that we know the number of speculations, the * number of aggregations, the minimum buffer size needed, etc. before we * transition out of DTRACE_ACTIVITY_INACTIVE. To do this without actually * enabling any probes, we create ECBs for every ECB decription, but with a * NULL probe -- which is exactly what this function does. */ static void dtrace_enabling_prime(dtrace_state_t *state) { dtrace_enabling_t *enab; int i; for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) { ASSERT(enab->dten_vstate->dtvs_state != NULL); if (enab->dten_vstate->dtvs_state != state) continue; /* * We don't want to prime an enabling more than once, lest * we allow a malicious user to induce resource exhaustion. * (The ECBs that result from priming an enabling aren't * leaked -- but they also aren't deallocated until the * consumer state is destroyed.) */ if (enab->dten_primed) continue; for (i = 0; i < enab->dten_ndesc; i++) { enab->dten_current = enab->dten_desc[i]; (void) dtrace_probe_enable(NULL, enab); } enab->dten_primed = 1; } } /* * Called to indicate that probes should be provided due to retained * enablings. This is implemented in terms of dtrace_probe_provide(), but it * must take an initial lap through the enabling calling the dtps_provide() * entry point explicitly to allow for autocreated probes. */ static void dtrace_enabling_provide(dtrace_provider_t *prv) { int i, all = 0; dtrace_probedesc_t desc; dtrace_genid_t gen; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&dtrace_provider_lock)); if (prv == NULL) { all = 1; prv = dtrace_provider; } do { dtrace_enabling_t *enab; void *parg = prv->dtpv_arg; retry: gen = dtrace_retained_gen; for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) { for (i = 0; i < enab->dten_ndesc; i++) { desc = enab->dten_desc[i]->dted_probe; mutex_exit(&dtrace_lock); prv->dtpv_pops.dtps_provide(parg, &desc); mutex_enter(&dtrace_lock); /* * Process the retained enablings again if * they have changed while we weren't holding * dtrace_lock. */ if (gen != dtrace_retained_gen) goto retry; } } } while (all && (prv = prv->dtpv_next) != NULL); mutex_exit(&dtrace_lock); dtrace_probe_provide(NULL, all ? NULL : prv); mutex_enter(&dtrace_lock); } /* * Called to reap ECBs that are attached to probes from defunct providers. */ static void dtrace_enabling_reap(void) { dtrace_provider_t *prov; dtrace_probe_t *probe; dtrace_ecb_t *ecb; hrtime_t when; int i; mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_ecb == NULL) continue; prov = probe->dtpr_provider; if ((when = prov->dtpv_defunct) == 0) continue; /* * We have ECBs on a defunct provider: we want to reap these * ECBs to allow the provider to unregister. The destruction * of these ECBs must be done carefully: if we destroy the ECB * and the consumer later wishes to consume an EPID that * corresponds to the destroyed ECB (and if the EPID metadata * has not been previously consumed), the consumer will abort * processing on the unknown EPID. To reduce (but not, sadly, * eliminate) the possibility of this, we will only destroy an * ECB for a defunct provider if, for the state that * corresponds to the ECB: * * (a) There is no speculative tracing (which can effectively * cache an EPID for an arbitrary amount of time). * * (b) The principal buffers have been switched twice since the * provider became defunct. * * (c) The aggregation buffers are of zero size or have been * switched twice since the provider became defunct. * * We use dts_speculates to determine (a) and call a function * (dtrace_buffer_consumed()) to determine (b) and (c). Note * that as soon as we've been unable to destroy one of the ECBs * associated with the probe, we quit trying -- reaping is only * fruitful in as much as we can destroy all ECBs associated * with the defunct provider's probes. */ while ((ecb = probe->dtpr_ecb) != NULL) { dtrace_state_t *state = ecb->dte_state; dtrace_buffer_t *buf = state->dts_buffer; dtrace_buffer_t *aggbuf = state->dts_aggbuffer; if (state->dts_speculates) break; if (!dtrace_buffer_consumed(buf, when)) break; if (!dtrace_buffer_consumed(aggbuf, when)) break; dtrace_ecb_disable(ecb); ASSERT(probe->dtpr_ecb != ecb); dtrace_ecb_destroy(ecb); } } mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); } /* * DTrace DOF Functions */ /*ARGSUSED*/ static void dtrace_dof_error(dof_hdr_t *dof, const char *str) { if (dtrace_err_verbose) cmn_err(CE_WARN, "failed to process DOF: %s", str); #ifdef DTRACE_ERRDEBUG dtrace_errdebug(str); #endif } /* * Create DOF out of a currently enabled state. Right now, we only create * DOF containing the run-time options -- but this could be expanded to create * complete DOF representing the enabled state. */ static dof_hdr_t * dtrace_dof_create(dtrace_state_t *state) { dof_hdr_t *dof; dof_sec_t *sec; dof_optdesc_t *opt; int i, len = sizeof (dof_hdr_t) + roundup(sizeof (dof_sec_t), sizeof (uint64_t)) + sizeof (dof_optdesc_t) * DTRACEOPT_MAX; ASSERT(MUTEX_HELD(&dtrace_lock)); dof = kmem_zalloc(len, KM_SLEEP); dof->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0; dof->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1; dof->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2; dof->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3; dof->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_NATIVE; dof->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE; dof->dofh_ident[DOF_ID_VERSION] = DOF_VERSION; dof->dofh_ident[DOF_ID_DIFVERS] = DIF_VERSION; dof->dofh_ident[DOF_ID_DIFIREG] = DIF_DIR_NREGS; dof->dofh_ident[DOF_ID_DIFTREG] = DIF_DTR_NREGS; dof->dofh_flags = 0; dof->dofh_hdrsize = sizeof (dof_hdr_t); dof->dofh_secsize = sizeof (dof_sec_t); dof->dofh_secnum = 1; /* only DOF_SECT_OPTDESC */ dof->dofh_secoff = sizeof (dof_hdr_t); dof->dofh_loadsz = len; dof->dofh_filesz = len; dof->dofh_pad = 0; /* * Fill in the option section header... */ sec = (dof_sec_t *)((uintptr_t)dof + sizeof (dof_hdr_t)); sec->dofs_type = DOF_SECT_OPTDESC; sec->dofs_align = sizeof (uint64_t); sec->dofs_flags = DOF_SECF_LOAD; sec->dofs_entsize = sizeof (dof_optdesc_t); opt = (dof_optdesc_t *)((uintptr_t)sec + roundup(sizeof (dof_sec_t), sizeof (uint64_t))); sec->dofs_offset = (uintptr_t)opt - (uintptr_t)dof; sec->dofs_size = sizeof (dof_optdesc_t) * DTRACEOPT_MAX; for (i = 0; i < DTRACEOPT_MAX; i++) { opt[i].dofo_option = i; opt[i].dofo_strtab = DOF_SECIDX_NONE; opt[i].dofo_value = state->dts_options[i]; } return (dof); } static dof_hdr_t * dtrace_dof_copyin(uintptr_t uarg, int *errp) { dof_hdr_t hdr, *dof; ASSERT(!MUTEX_HELD(&dtrace_lock)); /* * First, we're going to copyin() the sizeof (dof_hdr_t). */ if (copyin((void *)uarg, &hdr, sizeof (hdr)) != 0) { dtrace_dof_error(NULL, "failed to copyin DOF header"); *errp = EFAULT; return (NULL); } /* * Now we'll allocate the entire DOF and copy it in -- provided * that the length isn't outrageous. */ if (hdr.dofh_loadsz >= dtrace_dof_maxsize) { dtrace_dof_error(&hdr, "load size exceeds maximum"); *errp = E2BIG; return (NULL); } if (hdr.dofh_loadsz < sizeof (hdr)) { dtrace_dof_error(&hdr, "invalid load size"); *errp = EINVAL; return (NULL); } dof = kmem_alloc(hdr.dofh_loadsz, KM_SLEEP); if (copyin((void *)uarg, dof, hdr.dofh_loadsz) != 0 || dof->dofh_loadsz != hdr.dofh_loadsz) { kmem_free(dof, hdr.dofh_loadsz); *errp = EFAULT; return (NULL); } return (dof); } #if !defined(sun) static __inline uchar_t dtrace_dof_char(char c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return (c - '0'); case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return (c - 'A' + 10); case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return (c - 'a' + 10); } /* Should not reach here. */ return (0); } #endif static dof_hdr_t * dtrace_dof_property(const char *name) { uchar_t *buf; uint64_t loadsz; unsigned int len, i; dof_hdr_t *dof; #if defined(sun) /* * Unfortunately, array of values in .conf files are always (and * only) interpreted to be integer arrays. We must read our DOF * as an integer array, and then squeeze it into a byte array. */ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dtrace_devi, 0, (char *)name, (int **)&buf, &len) != DDI_PROP_SUCCESS) return (NULL); for (i = 0; i < len; i++) buf[i] = (uchar_t)(((int *)buf)[i]); if (len < sizeof (dof_hdr_t)) { ddi_prop_free(buf); dtrace_dof_error(NULL, "truncated header"); return (NULL); } if (len < (loadsz = ((dof_hdr_t *)buf)->dofh_loadsz)) { ddi_prop_free(buf); dtrace_dof_error(NULL, "truncated DOF"); return (NULL); } if (loadsz >= dtrace_dof_maxsize) { ddi_prop_free(buf); dtrace_dof_error(NULL, "oversized DOF"); return (NULL); } dof = kmem_alloc(loadsz, KM_SLEEP); bcopy(buf, dof, loadsz); ddi_prop_free(buf); #else char *p; char *p_env; if ((p_env = getenv(name)) == NULL) return (NULL); len = strlen(p_env) / 2; buf = kmem_alloc(len, KM_SLEEP); dof = (dof_hdr_t *) buf; p = p_env; for (i = 0; i < len; i++) { buf[i] = (dtrace_dof_char(p[0]) << 4) | dtrace_dof_char(p[1]); p += 2; } freeenv(p_env); if (len < sizeof (dof_hdr_t)) { kmem_free(buf, 0); dtrace_dof_error(NULL, "truncated header"); return (NULL); } if (len < (loadsz = dof->dofh_loadsz)) { kmem_free(buf, 0); dtrace_dof_error(NULL, "truncated DOF"); return (NULL); } if (loadsz >= dtrace_dof_maxsize) { kmem_free(buf, 0); dtrace_dof_error(NULL, "oversized DOF"); return (NULL); } #endif return (dof); } static void dtrace_dof_destroy(dof_hdr_t *dof) { kmem_free(dof, dof->dofh_loadsz); } /* * Return the dof_sec_t pointer corresponding to a given section index. If the * index is not valid, dtrace_dof_error() is called and NULL is returned. If * a type other than DOF_SECT_NONE is specified, the header is checked against * this type and NULL is returned if the types do not match. */ static dof_sec_t * dtrace_dof_sect(dof_hdr_t *dof, uint32_t type, dof_secidx_t i) { dof_sec_t *sec = (dof_sec_t *)(uintptr_t) ((uintptr_t)dof + dof->dofh_secoff + i * dof->dofh_secsize); if (i >= dof->dofh_secnum) { dtrace_dof_error(dof, "referenced section index is invalid"); return (NULL); } if (!(sec->dofs_flags & DOF_SECF_LOAD)) { dtrace_dof_error(dof, "referenced section is not loadable"); return (NULL); } if (type != DOF_SECT_NONE && type != sec->dofs_type) { dtrace_dof_error(dof, "referenced section is the wrong type"); return (NULL); } return (sec); } static dtrace_probedesc_t * dtrace_dof_probedesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_probedesc_t *desc) { dof_probedesc_t *probe; dof_sec_t *strtab; uintptr_t daddr = (uintptr_t)dof; uintptr_t str; size_t size; if (sec->dofs_type != DOF_SECT_PROBEDESC) { dtrace_dof_error(dof, "invalid probe section"); return (NULL); } if (sec->dofs_align != sizeof (dof_secidx_t)) { dtrace_dof_error(dof, "bad alignment in probe description"); return (NULL); } if (sec->dofs_offset + sizeof (dof_probedesc_t) > dof->dofh_loadsz) { dtrace_dof_error(dof, "truncated probe description"); return (NULL); } probe = (dof_probedesc_t *)(uintptr_t)(daddr + sec->dofs_offset); strtab = dtrace_dof_sect(dof, DOF_SECT_STRTAB, probe->dofp_strtab); if (strtab == NULL) return (NULL); str = daddr + strtab->dofs_offset; size = strtab->dofs_size; if (probe->dofp_provider >= strtab->dofs_size) { dtrace_dof_error(dof, "corrupt probe provider"); return (NULL); } (void) strncpy(desc->dtpd_provider, (char *)(str + probe->dofp_provider), MIN(DTRACE_PROVNAMELEN - 1, size - probe->dofp_provider)); if (probe->dofp_mod >= strtab->dofs_size) { dtrace_dof_error(dof, "corrupt probe module"); return (NULL); } (void) strncpy(desc->dtpd_mod, (char *)(str + probe->dofp_mod), MIN(DTRACE_MODNAMELEN - 1, size - probe->dofp_mod)); if (probe->dofp_func >= strtab->dofs_size) { dtrace_dof_error(dof, "corrupt probe function"); return (NULL); } (void) strncpy(desc->dtpd_func, (char *)(str + probe->dofp_func), MIN(DTRACE_FUNCNAMELEN - 1, size - probe->dofp_func)); if (probe->dofp_name >= strtab->dofs_size) { dtrace_dof_error(dof, "corrupt probe name"); return (NULL); } (void) strncpy(desc->dtpd_name, (char *)(str + probe->dofp_name), MIN(DTRACE_NAMELEN - 1, size - probe->dofp_name)); return (desc); } static dtrace_difo_t * dtrace_dof_difo(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate, cred_t *cr) { dtrace_difo_t *dp; size_t ttl = 0; dof_difohdr_t *dofd; uintptr_t daddr = (uintptr_t)dof; size_t max = dtrace_difo_maxsize; int i, l, n; static const struct { int section; int bufoffs; int lenoffs; int entsize; int align; const char *msg; } difo[] = { { DOF_SECT_DIF, offsetof(dtrace_difo_t, dtdo_buf), offsetof(dtrace_difo_t, dtdo_len), sizeof (dif_instr_t), sizeof (dif_instr_t), "multiple DIF sections" }, { DOF_SECT_INTTAB, offsetof(dtrace_difo_t, dtdo_inttab), offsetof(dtrace_difo_t, dtdo_intlen), sizeof (uint64_t), sizeof (uint64_t), "multiple integer tables" }, { DOF_SECT_STRTAB, offsetof(dtrace_difo_t, dtdo_strtab), offsetof(dtrace_difo_t, dtdo_strlen), 0, sizeof (char), "multiple string tables" }, { DOF_SECT_VARTAB, offsetof(dtrace_difo_t, dtdo_vartab), offsetof(dtrace_difo_t, dtdo_varlen), sizeof (dtrace_difv_t), sizeof (uint_t), "multiple variable tables" }, { DOF_SECT_NONE, 0, 0, 0, 0, NULL } }; if (sec->dofs_type != DOF_SECT_DIFOHDR) { dtrace_dof_error(dof, "invalid DIFO header section"); return (NULL); } if (sec->dofs_align != sizeof (dof_secidx_t)) { dtrace_dof_error(dof, "bad alignment in DIFO header"); return (NULL); } if (sec->dofs_size < sizeof (dof_difohdr_t) || sec->dofs_size % sizeof (dof_secidx_t)) { dtrace_dof_error(dof, "bad size in DIFO header"); return (NULL); } dofd = (dof_difohdr_t *)(uintptr_t)(daddr + sec->dofs_offset); n = (sec->dofs_size - sizeof (*dofd)) / sizeof (dof_secidx_t) + 1; dp = kmem_zalloc(sizeof (dtrace_difo_t), KM_SLEEP); dp->dtdo_rtype = dofd->dofd_rtype; for (l = 0; l < n; l++) { dof_sec_t *subsec; void **bufp; uint32_t *lenp; if ((subsec = dtrace_dof_sect(dof, DOF_SECT_NONE, dofd->dofd_links[l])) == NULL) goto err; /* invalid section link */ if (ttl + subsec->dofs_size > max) { dtrace_dof_error(dof, "exceeds maximum size"); goto err; } ttl += subsec->dofs_size; for (i = 0; difo[i].section != DOF_SECT_NONE; i++) { if (subsec->dofs_type != difo[i].section) continue; if (!(subsec->dofs_flags & DOF_SECF_LOAD)) { dtrace_dof_error(dof, "section not loaded"); goto err; } if (subsec->dofs_align != difo[i].align) { dtrace_dof_error(dof, "bad alignment"); goto err; } bufp = (void **)((uintptr_t)dp + difo[i].bufoffs); lenp = (uint32_t *)((uintptr_t)dp + difo[i].lenoffs); if (*bufp != NULL) { dtrace_dof_error(dof, difo[i].msg); goto err; } if (difo[i].entsize != subsec->dofs_entsize) { dtrace_dof_error(dof, "entry size mismatch"); goto err; } if (subsec->dofs_entsize != 0 && (subsec->dofs_size % subsec->dofs_entsize) != 0) { dtrace_dof_error(dof, "corrupt entry size"); goto err; } *lenp = subsec->dofs_size; *bufp = kmem_alloc(subsec->dofs_size, KM_SLEEP); bcopy((char *)(uintptr_t)(daddr + subsec->dofs_offset), *bufp, subsec->dofs_size); if (subsec->dofs_entsize != 0) *lenp /= subsec->dofs_entsize; break; } /* * If we encounter a loadable DIFO sub-section that is not * known to us, assume this is a broken program and fail. */ if (difo[i].section == DOF_SECT_NONE && (subsec->dofs_flags & DOF_SECF_LOAD)) { dtrace_dof_error(dof, "unrecognized DIFO subsection"); goto err; } } if (dp->dtdo_buf == NULL) { /* * We can't have a DIF object without DIF text. */ dtrace_dof_error(dof, "missing DIF text"); goto err; } /* * Before we validate the DIF object, run through the variable table * looking for the strings -- if any of their size are under, we'll set * their size to be the system-wide default string size. Note that * this should _not_ happen if the "strsize" option has been set -- * in this case, the compiler should have set the size to reflect the * setting of the option. */ for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; dtrace_diftype_t *t = &v->dtdv_type; if (v->dtdv_id < DIF_VAR_OTHER_UBASE) continue; if (t->dtdt_kind == DIF_TYPE_STRING && t->dtdt_size == 0) t->dtdt_size = dtrace_strsize_default; } if (dtrace_difo_validate(dp, vstate, DIF_DIR_NREGS, cr) != 0) goto err; dtrace_difo_init(dp, vstate); return (dp); err: kmem_free(dp->dtdo_buf, dp->dtdo_len * sizeof (dif_instr_t)); kmem_free(dp->dtdo_inttab, dp->dtdo_intlen * sizeof (uint64_t)); kmem_free(dp->dtdo_strtab, dp->dtdo_strlen); kmem_free(dp->dtdo_vartab, dp->dtdo_varlen * sizeof (dtrace_difv_t)); kmem_free(dp, sizeof (dtrace_difo_t)); return (NULL); } static dtrace_predicate_t * dtrace_dof_predicate(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate, cred_t *cr) { dtrace_difo_t *dp; if ((dp = dtrace_dof_difo(dof, sec, vstate, cr)) == NULL) return (NULL); return (dtrace_predicate_create(dp)); } static dtrace_actdesc_t * dtrace_dof_actdesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate, cred_t *cr) { dtrace_actdesc_t *act, *first = NULL, *last = NULL, *next; dof_actdesc_t *desc; dof_sec_t *difosec; size_t offs; uintptr_t daddr = (uintptr_t)dof; uint64_t arg; dtrace_actkind_t kind; if (sec->dofs_type != DOF_SECT_ACTDESC) { dtrace_dof_error(dof, "invalid action section"); return (NULL); } if (sec->dofs_offset + sizeof (dof_actdesc_t) > dof->dofh_loadsz) { dtrace_dof_error(dof, "truncated action description"); return (NULL); } if (sec->dofs_align != sizeof (uint64_t)) { dtrace_dof_error(dof, "bad alignment in action description"); return (NULL); } if (sec->dofs_size < sec->dofs_entsize) { dtrace_dof_error(dof, "section entry size exceeds total size"); return (NULL); } if (sec->dofs_entsize != sizeof (dof_actdesc_t)) { dtrace_dof_error(dof, "bad entry size in action description"); return (NULL); } if (sec->dofs_size / sec->dofs_entsize > dtrace_actions_max) { dtrace_dof_error(dof, "actions exceed dtrace_actions_max"); return (NULL); } for (offs = 0; offs < sec->dofs_size; offs += sec->dofs_entsize) { desc = (dof_actdesc_t *)(daddr + (uintptr_t)sec->dofs_offset + offs); kind = (dtrace_actkind_t)desc->dofa_kind; if ((DTRACEACT_ISPRINTFLIKE(kind) && (kind != DTRACEACT_PRINTA || desc->dofa_strtab != DOF_SECIDX_NONE)) || (kind == DTRACEACT_DIFEXPR && desc->dofa_strtab != DOF_SECIDX_NONE)) { dof_sec_t *strtab; char *str, *fmt; uint64_t i; /* * The argument to these actions is an index into the * DOF string table. For printf()-like actions, this * is the format string. For print(), this is the * CTF type of the expression result. */ if ((strtab = dtrace_dof_sect(dof, DOF_SECT_STRTAB, desc->dofa_strtab)) == NULL) goto err; str = (char *)((uintptr_t)dof + (uintptr_t)strtab->dofs_offset); for (i = desc->dofa_arg; i < strtab->dofs_size; i++) { if (str[i] == '\0') break; } if (i >= strtab->dofs_size) { dtrace_dof_error(dof, "bogus format string"); goto err; } if (i == desc->dofa_arg) { dtrace_dof_error(dof, "empty format string"); goto err; } i -= desc->dofa_arg; fmt = kmem_alloc(i + 1, KM_SLEEP); bcopy(&str[desc->dofa_arg], fmt, i + 1); arg = (uint64_t)(uintptr_t)fmt; } else { if (kind == DTRACEACT_PRINTA) { ASSERT(desc->dofa_strtab == DOF_SECIDX_NONE); arg = 0; } else { arg = desc->dofa_arg; } } act = dtrace_actdesc_create(kind, desc->dofa_ntuple, desc->dofa_uarg, arg); if (last != NULL) { last->dtad_next = act; } else { first = act; } last = act; if (desc->dofa_difo == DOF_SECIDX_NONE) continue; if ((difosec = dtrace_dof_sect(dof, DOF_SECT_DIFOHDR, desc->dofa_difo)) == NULL) goto err; act->dtad_difo = dtrace_dof_difo(dof, difosec, vstate, cr); if (act->dtad_difo == NULL) goto err; } ASSERT(first != NULL); return (first); err: for (act = first; act != NULL; act = next) { next = act->dtad_next; dtrace_actdesc_release(act, vstate); } return (NULL); } static dtrace_ecbdesc_t * dtrace_dof_ecbdesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate, cred_t *cr) { dtrace_ecbdesc_t *ep; dof_ecbdesc_t *ecb; dtrace_probedesc_t *desc; dtrace_predicate_t *pred = NULL; if (sec->dofs_size < sizeof (dof_ecbdesc_t)) { dtrace_dof_error(dof, "truncated ECB description"); return (NULL); } if (sec->dofs_align != sizeof (uint64_t)) { dtrace_dof_error(dof, "bad alignment in ECB description"); return (NULL); } ecb = (dof_ecbdesc_t *)((uintptr_t)dof + (uintptr_t)sec->dofs_offset); sec = dtrace_dof_sect(dof, DOF_SECT_PROBEDESC, ecb->dofe_probes); if (sec == NULL) return (NULL); ep = kmem_zalloc(sizeof (dtrace_ecbdesc_t), KM_SLEEP); ep->dted_uarg = ecb->dofe_uarg; desc = &ep->dted_probe; if (dtrace_dof_probedesc(dof, sec, desc) == NULL) goto err; if (ecb->dofe_pred != DOF_SECIDX_NONE) { if ((sec = dtrace_dof_sect(dof, DOF_SECT_DIFOHDR, ecb->dofe_pred)) == NULL) goto err; if ((pred = dtrace_dof_predicate(dof, sec, vstate, cr)) == NULL) goto err; ep->dted_pred.dtpdd_predicate = pred; } if (ecb->dofe_actions != DOF_SECIDX_NONE) { if ((sec = dtrace_dof_sect(dof, DOF_SECT_ACTDESC, ecb->dofe_actions)) == NULL) goto err; ep->dted_action = dtrace_dof_actdesc(dof, sec, vstate, cr); if (ep->dted_action == NULL) goto err; } return (ep); err: if (pred != NULL) dtrace_predicate_release(pred, vstate); kmem_free(ep, sizeof (dtrace_ecbdesc_t)); return (NULL); } /* * Apply the relocations from the specified 'sec' (a DOF_SECT_URELHDR) to the * specified DOF. At present, this amounts to simply adding 'ubase' to the * site of any user SETX relocations to account for load object base address. * In the future, if we need other relocations, this function can be extended. */ static int dtrace_dof_relocate(dof_hdr_t *dof, dof_sec_t *sec, uint64_t ubase) { uintptr_t daddr = (uintptr_t)dof; dof_relohdr_t *dofr = (dof_relohdr_t *)(uintptr_t)(daddr + sec->dofs_offset); dof_sec_t *ss, *rs, *ts; dof_relodesc_t *r; uint_t i, n; if (sec->dofs_size < sizeof (dof_relohdr_t) || sec->dofs_align != sizeof (dof_secidx_t)) { dtrace_dof_error(dof, "invalid relocation header"); return (-1); } ss = dtrace_dof_sect(dof, DOF_SECT_STRTAB, dofr->dofr_strtab); rs = dtrace_dof_sect(dof, DOF_SECT_RELTAB, dofr->dofr_relsec); ts = dtrace_dof_sect(dof, DOF_SECT_NONE, dofr->dofr_tgtsec); if (ss == NULL || rs == NULL || ts == NULL) return (-1); /* dtrace_dof_error() has been called already */ if (rs->dofs_entsize < sizeof (dof_relodesc_t) || rs->dofs_align != sizeof (uint64_t)) { dtrace_dof_error(dof, "invalid relocation section"); return (-1); } r = (dof_relodesc_t *)(uintptr_t)(daddr + rs->dofs_offset); n = rs->dofs_size / rs->dofs_entsize; for (i = 0; i < n; i++) { uintptr_t taddr = daddr + ts->dofs_offset + r->dofr_offset; switch (r->dofr_type) { case DOF_RELO_NONE: break; case DOF_RELO_SETX: if (r->dofr_offset >= ts->dofs_size || r->dofr_offset + sizeof (uint64_t) > ts->dofs_size) { dtrace_dof_error(dof, "bad relocation offset"); return (-1); } if (!IS_P2ALIGNED(taddr, sizeof (uint64_t))) { dtrace_dof_error(dof, "misaligned setx relo"); return (-1); } *(uint64_t *)taddr += ubase; break; default: dtrace_dof_error(dof, "invalid relocation type"); return (-1); } r = (dof_relodesc_t *)((uintptr_t)r + rs->dofs_entsize); } return (0); } /* * The dof_hdr_t passed to dtrace_dof_slurp() should be a partially validated * header: it should be at the front of a memory region that is at least * sizeof (dof_hdr_t) in size -- and then at least dof_hdr.dofh_loadsz in * size. It need not be validated in any other way. */ static int dtrace_dof_slurp(dof_hdr_t *dof, dtrace_vstate_t *vstate, cred_t *cr, dtrace_enabling_t **enabp, uint64_t ubase, int noprobes) { uint64_t len = dof->dofh_loadsz, seclen; uintptr_t daddr = (uintptr_t)dof; dtrace_ecbdesc_t *ep; dtrace_enabling_t *enab; uint_t i; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dof->dofh_loadsz >= sizeof (dof_hdr_t)); /* * Check the DOF header identification bytes. In addition to checking * valid settings, we also verify that unused bits/bytes are zeroed so * we can use them later without fear of regressing existing binaries. */ if (bcmp(&dof->dofh_ident[DOF_ID_MAG0], DOF_MAG_STRING, DOF_MAG_STRLEN) != 0) { dtrace_dof_error(dof, "DOF magic string mismatch"); return (-1); } if (dof->dofh_ident[DOF_ID_MODEL] != DOF_MODEL_ILP32 && dof->dofh_ident[DOF_ID_MODEL] != DOF_MODEL_LP64) { dtrace_dof_error(dof, "DOF has invalid data model"); return (-1); } if (dof->dofh_ident[DOF_ID_ENCODING] != DOF_ENCODE_NATIVE) { dtrace_dof_error(dof, "DOF encoding mismatch"); return (-1); } if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 && dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_2) { dtrace_dof_error(dof, "DOF version mismatch"); return (-1); } if (dof->dofh_ident[DOF_ID_DIFVERS] != DIF_VERSION_2) { dtrace_dof_error(dof, "DOF uses unsupported instruction set"); return (-1); } if (dof->dofh_ident[DOF_ID_DIFIREG] > DIF_DIR_NREGS) { dtrace_dof_error(dof, "DOF uses too many integer registers"); return (-1); } if (dof->dofh_ident[DOF_ID_DIFTREG] > DIF_DTR_NREGS) { dtrace_dof_error(dof, "DOF uses too many tuple registers"); return (-1); } for (i = DOF_ID_PAD; i < DOF_ID_SIZE; i++) { if (dof->dofh_ident[i] != 0) { dtrace_dof_error(dof, "DOF has invalid ident byte set"); return (-1); } } if (dof->dofh_flags & ~DOF_FL_VALID) { dtrace_dof_error(dof, "DOF has invalid flag bits set"); return (-1); } if (dof->dofh_secsize == 0) { dtrace_dof_error(dof, "zero section header size"); return (-1); } /* * Check that the section headers don't exceed the amount of DOF * data. Note that we cast the section size and number of sections * to uint64_t's to prevent possible overflow in the multiplication. */ seclen = (uint64_t)dof->dofh_secnum * (uint64_t)dof->dofh_secsize; if (dof->dofh_secoff > len || seclen > len || dof->dofh_secoff + seclen > len) { dtrace_dof_error(dof, "truncated section headers"); return (-1); } if (!IS_P2ALIGNED(dof->dofh_secoff, sizeof (uint64_t))) { dtrace_dof_error(dof, "misaligned section headers"); return (-1); } if (!IS_P2ALIGNED(dof->dofh_secsize, sizeof (uint64_t))) { dtrace_dof_error(dof, "misaligned section size"); return (-1); } /* * Take an initial pass through the section headers to be sure that * the headers don't have stray offsets. If the 'noprobes' flag is * set, do not permit sections relating to providers, probes, or args. */ for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(daddr + (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize); if (noprobes) { switch (sec->dofs_type) { case DOF_SECT_PROVIDER: case DOF_SECT_PROBES: case DOF_SECT_PRARGS: case DOF_SECT_PROFFS: dtrace_dof_error(dof, "illegal sections " "for enabling"); return (-1); } } if (DOF_SEC_ISLOADABLE(sec->dofs_type) && !(sec->dofs_flags & DOF_SECF_LOAD)) { dtrace_dof_error(dof, "loadable section with load " "flag unset"); return (-1); } if (!(sec->dofs_flags & DOF_SECF_LOAD)) continue; /* just ignore non-loadable sections */ if (sec->dofs_align & (sec->dofs_align - 1)) { dtrace_dof_error(dof, "bad section alignment"); return (-1); } if (sec->dofs_offset & (sec->dofs_align - 1)) { dtrace_dof_error(dof, "misaligned section"); return (-1); } if (sec->dofs_offset > len || sec->dofs_size > len || sec->dofs_offset + sec->dofs_size > len) { dtrace_dof_error(dof, "corrupt section header"); return (-1); } if (sec->dofs_type == DOF_SECT_STRTAB && *((char *)daddr + sec->dofs_offset + sec->dofs_size - 1) != '\0') { dtrace_dof_error(dof, "non-terminating string table"); return (-1); } } /* * Take a second pass through the sections and locate and perform any * relocations that are present. We do this after the first pass to * be sure that all sections have had their headers validated. */ for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(daddr + (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize); if (!(sec->dofs_flags & DOF_SECF_LOAD)) continue; /* skip sections that are not loadable */ switch (sec->dofs_type) { case DOF_SECT_URELHDR: if (dtrace_dof_relocate(dof, sec, ubase) != 0) return (-1); break; } } if ((enab = *enabp) == NULL) enab = *enabp = dtrace_enabling_create(vstate); for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(daddr + (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_ECBDESC) continue; if ((ep = dtrace_dof_ecbdesc(dof, sec, vstate, cr)) == NULL) { dtrace_enabling_destroy(enab); *enabp = NULL; return (-1); } dtrace_enabling_add(enab, ep); } return (0); } /* * Process DOF for any options. This routine assumes that the DOF has been * at least processed by dtrace_dof_slurp(). */ static int dtrace_dof_options(dof_hdr_t *dof, dtrace_state_t *state) { int i, rval; uint32_t entsize; size_t offs; dof_optdesc_t *desc; for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)((uintptr_t)dof + (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_OPTDESC) continue; if (sec->dofs_align != sizeof (uint64_t)) { dtrace_dof_error(dof, "bad alignment in " "option description"); return (EINVAL); } if ((entsize = sec->dofs_entsize) == 0) { dtrace_dof_error(dof, "zeroed option entry size"); return (EINVAL); } if (entsize < sizeof (dof_optdesc_t)) { dtrace_dof_error(dof, "bad option entry size"); return (EINVAL); } for (offs = 0; offs < sec->dofs_size; offs += entsize) { desc = (dof_optdesc_t *)((uintptr_t)dof + (uintptr_t)sec->dofs_offset + offs); if (desc->dofo_strtab != DOF_SECIDX_NONE) { dtrace_dof_error(dof, "non-zero option string"); return (EINVAL); } if (desc->dofo_value == DTRACEOPT_UNSET) { dtrace_dof_error(dof, "unset option"); return (EINVAL); } if ((rval = dtrace_state_option(state, desc->dofo_option, desc->dofo_value)) != 0) { dtrace_dof_error(dof, "rejected option"); return (rval); } } } return (0); } /* * DTrace Consumer State Functions */ static int dtrace_dstate_init(dtrace_dstate_t *dstate, size_t size) { size_t hashsize, maxper, min, chunksize = dstate->dtds_chunksize; void *base; uintptr_t limit; dtrace_dynvar_t *dvar, *next, *start; int i; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dstate->dtds_base == NULL && dstate->dtds_percpu == NULL); bzero(dstate, sizeof (dtrace_dstate_t)); if ((dstate->dtds_chunksize = chunksize) == 0) dstate->dtds_chunksize = DTRACE_DYNVAR_CHUNKSIZE; if (size < (min = dstate->dtds_chunksize + sizeof (dtrace_dynhash_t))) size = min; if ((base = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) return (ENOMEM); dstate->dtds_size = size; dstate->dtds_base = base; dstate->dtds_percpu = kmem_cache_alloc(dtrace_state_cache, KM_SLEEP); bzero(dstate->dtds_percpu, NCPU * sizeof (dtrace_dstate_percpu_t)); hashsize = size / (dstate->dtds_chunksize + sizeof (dtrace_dynhash_t)); if (hashsize != 1 && (hashsize & 1)) hashsize--; dstate->dtds_hashsize = hashsize; dstate->dtds_hash = dstate->dtds_base; /* * Set all of our hash buckets to point to the single sink, and (if * it hasn't already been set), set the sink's hash value to be the * sink sentinel value. The sink is needed for dynamic variable * lookups to know that they have iterated over an entire, valid hash * chain. */ for (i = 0; i < hashsize; i++) dstate->dtds_hash[i].dtdh_chain = &dtrace_dynhash_sink; if (dtrace_dynhash_sink.dtdv_hashval != DTRACE_DYNHASH_SINK) dtrace_dynhash_sink.dtdv_hashval = DTRACE_DYNHASH_SINK; /* * Determine number of active CPUs. Divide free list evenly among * active CPUs. */ start = (dtrace_dynvar_t *) ((uintptr_t)base + hashsize * sizeof (dtrace_dynhash_t)); limit = (uintptr_t)base + size; maxper = (limit - (uintptr_t)start) / NCPU; maxper = (maxper / dstate->dtds_chunksize) * dstate->dtds_chunksize; #if !defined(sun) CPU_FOREACH(i) { #else for (i = 0; i < NCPU; i++) { #endif dstate->dtds_percpu[i].dtdsc_free = dvar = start; /* * If we don't even have enough chunks to make it once through * NCPUs, we're just going to allocate everything to the first * CPU. And if we're on the last CPU, we're going to allocate * whatever is left over. In either case, we set the limit to * be the limit of the dynamic variable space. */ if (maxper == 0 || i == NCPU - 1) { limit = (uintptr_t)base + size; start = NULL; } else { limit = (uintptr_t)start + maxper; start = (dtrace_dynvar_t *)limit; } ASSERT(limit <= (uintptr_t)base + size); for (;;) { next = (dtrace_dynvar_t *)((uintptr_t)dvar + dstate->dtds_chunksize); if ((uintptr_t)next + dstate->dtds_chunksize >= limit) break; dvar->dtdv_next = next; dvar = next; } if (maxper == 0) break; } return (0); } static void dtrace_dstate_fini(dtrace_dstate_t *dstate) { ASSERT(MUTEX_HELD(&cpu_lock)); if (dstate->dtds_base == NULL) return; kmem_free(dstate->dtds_base, dstate->dtds_size); kmem_cache_free(dtrace_state_cache, dstate->dtds_percpu); } static void dtrace_vstate_fini(dtrace_vstate_t *vstate) { /* * Logical XOR, where are you? */ ASSERT((vstate->dtvs_nglobals == 0) ^ (vstate->dtvs_globals != NULL)); if (vstate->dtvs_nglobals > 0) { kmem_free(vstate->dtvs_globals, vstate->dtvs_nglobals * sizeof (dtrace_statvar_t *)); } if (vstate->dtvs_ntlocals > 0) { kmem_free(vstate->dtvs_tlocals, vstate->dtvs_ntlocals * sizeof (dtrace_difv_t)); } ASSERT((vstate->dtvs_nlocals == 0) ^ (vstate->dtvs_locals != NULL)); if (vstate->dtvs_nlocals > 0) { kmem_free(vstate->dtvs_locals, vstate->dtvs_nlocals * sizeof (dtrace_statvar_t *)); } } #if defined(sun) static void dtrace_state_clean(dtrace_state_t *state) { if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) return; dtrace_dynvar_clean(&state->dts_vstate.dtvs_dynvars); dtrace_speculation_clean(state); } static void dtrace_state_deadman(dtrace_state_t *state) { hrtime_t now; dtrace_sync(); now = dtrace_gethrtime(); if (state != dtrace_anon.dta_state && now - state->dts_laststatus >= dtrace_deadman_user) return; /* * We must be sure that dts_alive never appears to be less than the * value upon entry to dtrace_state_deadman(), and because we lack a * dtrace_cas64(), we cannot store to it atomically. We thus instead * store INT64_MAX to it, followed by a memory barrier, followed by * the new value. This assures that dts_alive never appears to be * less than its true value, regardless of the order in which the * stores to the underlying storage are issued. */ state->dts_alive = INT64_MAX; dtrace_membar_producer(); state->dts_alive = now; } #else static void dtrace_state_clean(void *arg) { dtrace_state_t *state = arg; dtrace_optval_t *opt = state->dts_options; if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) return; dtrace_dynvar_clean(&state->dts_vstate.dtvs_dynvars); dtrace_speculation_clean(state); callout_reset(&state->dts_cleaner, hz * opt[DTRACEOPT_CLEANRATE] / NANOSEC, dtrace_state_clean, state); } static void dtrace_state_deadman(void *arg) { dtrace_state_t *state = arg; hrtime_t now; dtrace_sync(); dtrace_debug_output(); now = dtrace_gethrtime(); if (state != dtrace_anon.dta_state && now - state->dts_laststatus >= dtrace_deadman_user) return; /* * We must be sure that dts_alive never appears to be less than the * value upon entry to dtrace_state_deadman(), and because we lack a * dtrace_cas64(), we cannot store to it atomically. We thus instead * store INT64_MAX to it, followed by a memory barrier, followed by * the new value. This assures that dts_alive never appears to be * less than its true value, regardless of the order in which the * stores to the underlying storage are issued. */ state->dts_alive = INT64_MAX; dtrace_membar_producer(); state->dts_alive = now; callout_reset(&state->dts_deadman, hz * dtrace_deadman_interval / NANOSEC, dtrace_state_deadman, state); } #endif static dtrace_state_t * #if defined(sun) dtrace_state_create(dev_t *devp, cred_t *cr) #else dtrace_state_create(struct cdev *dev) #endif { #if defined(sun) minor_t minor; major_t major; #else cred_t *cr = NULL; int m = 0; #endif char c[30]; dtrace_state_t *state; dtrace_optval_t *opt; int bufsize = NCPU * sizeof (dtrace_buffer_t), i; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&cpu_lock)); #if defined(sun) minor = (minor_t)(uintptr_t)vmem_alloc(dtrace_minor, 1, VM_BESTFIT | VM_SLEEP); if (ddi_soft_state_zalloc(dtrace_softstate, minor) != DDI_SUCCESS) { vmem_free(dtrace_minor, (void *)(uintptr_t)minor, 1); return (NULL); } state = ddi_get_soft_state(dtrace_softstate, minor); #else if (dev != NULL) { cr = dev->si_cred; m = dev2unit(dev); } /* Allocate memory for the state. */ state = kmem_zalloc(sizeof(dtrace_state_t), KM_SLEEP); #endif state->dts_epid = DTRACE_EPIDNONE + 1; (void) snprintf(c, sizeof (c), "dtrace_aggid_%d", m); #if defined(sun) state->dts_aggid_arena = vmem_create(c, (void *)1, UINT32_MAX, 1, NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER); if (devp != NULL) { major = getemajor(*devp); } else { major = ddi_driver_major(dtrace_devi); } state->dts_dev = makedevice(major, minor); if (devp != NULL) *devp = state->dts_dev; #else state->dts_aggid_arena = new_unrhdr(1, INT_MAX, &dtrace_unr_mtx); state->dts_dev = dev; #endif /* * We allocate NCPU buffers. On the one hand, this can be quite * a bit of memory per instance (nearly 36K on a Starcat). On the * other hand, it saves an additional memory reference in the probe * path. */ state->dts_buffer = kmem_zalloc(bufsize, KM_SLEEP); state->dts_aggbuffer = kmem_zalloc(bufsize, KM_SLEEP); #if defined(sun) state->dts_cleaner = CYCLIC_NONE; state->dts_deadman = CYCLIC_NONE; #else callout_init(&state->dts_cleaner, CALLOUT_MPSAFE); callout_init(&state->dts_deadman, CALLOUT_MPSAFE); #endif state->dts_vstate.dtvs_state = state; for (i = 0; i < DTRACEOPT_MAX; i++) state->dts_options[i] = DTRACEOPT_UNSET; /* * Set the default options. */ opt = state->dts_options; opt[DTRACEOPT_BUFPOLICY] = DTRACEOPT_BUFPOLICY_SWITCH; opt[DTRACEOPT_BUFRESIZE] = DTRACEOPT_BUFRESIZE_AUTO; opt[DTRACEOPT_NSPEC] = dtrace_nspec_default; opt[DTRACEOPT_SPECSIZE] = dtrace_specsize_default; opt[DTRACEOPT_CPU] = (dtrace_optval_t)DTRACE_CPUALL; opt[DTRACEOPT_STRSIZE] = dtrace_strsize_default; opt[DTRACEOPT_STACKFRAMES] = dtrace_stackframes_default; opt[DTRACEOPT_USTACKFRAMES] = dtrace_ustackframes_default; opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_default; opt[DTRACEOPT_AGGRATE] = dtrace_aggrate_default; opt[DTRACEOPT_SWITCHRATE] = dtrace_switchrate_default; opt[DTRACEOPT_STATUSRATE] = dtrace_statusrate_default; opt[DTRACEOPT_JSTACKFRAMES] = dtrace_jstackframes_default; opt[DTRACEOPT_JSTACKSTRSIZE] = dtrace_jstackstrsize_default; state->dts_activity = DTRACE_ACTIVITY_INACTIVE; /* * Depending on the user credentials, we set flag bits which alter probe * visibility or the amount of destructiveness allowed. In the case of * actual anonymous tracing, or the possession of all privileges, all of * the normal checks are bypassed. */ if (cr == NULL || PRIV_POLICY_ONLY(cr, PRIV_ALL, B_FALSE)) { state->dts_cred.dcr_visible = DTRACE_CRV_ALL; state->dts_cred.dcr_action = DTRACE_CRA_ALL; } else { /* * Set up the credentials for this instantiation. We take a * hold on the credential to prevent it from disappearing on * us; this in turn prevents the zone_t referenced by this * credential from disappearing. This means that we can * examine the credential and the zone from probe context. */ crhold(cr); state->dts_cred.dcr_cred = cr; /* * CRA_PROC means "we have *some* privilege for dtrace" and * unlocks the use of variables like pid, zonename, etc. */ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE) || PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE)) { state->dts_cred.dcr_action |= DTRACE_CRA_PROC; } /* * dtrace_user allows use of syscall and profile providers. * If the user also has proc_owner and/or proc_zone, we * extend the scope to include additional visibility and * destructive power. */ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE)) { if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) { state->dts_cred.dcr_visible |= DTRACE_CRV_ALLPROC; state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER; } if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) { state->dts_cred.dcr_visible |= DTRACE_CRV_ALLZONE; state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE; } /* * If we have all privs in whatever zone this is, * we can do destructive things to processes which * have altered credentials. */ #if defined(sun) if (priv_isequalset(priv_getset(cr, PRIV_EFFECTIVE), cr->cr_zone->zone_privset)) { state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG; } #endif } /* * Holding the dtrace_kernel privilege also implies that * the user has the dtrace_user privilege from a visibility * perspective. But without further privileges, some * destructive actions are not available. */ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE)) { /* * Make all probes in all zones visible. However, * this doesn't mean that all actions become available * to all zones. */ state->dts_cred.dcr_visible |= DTRACE_CRV_KERNEL | DTRACE_CRV_ALLPROC | DTRACE_CRV_ALLZONE; state->dts_cred.dcr_action |= DTRACE_CRA_KERNEL | DTRACE_CRA_PROC; /* * Holding proc_owner means that destructive actions * for *this* zone are allowed. */ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER; /* * Holding proc_zone means that destructive actions * for this user/group ID in all zones is allowed. */ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE; #if defined(sun) /* * If we have all privs in whatever zone this is, * we can do destructive things to processes which * have altered credentials. */ if (priv_isequalset(priv_getset(cr, PRIV_EFFECTIVE), cr->cr_zone->zone_privset)) { state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG; } #endif } /* * Holding the dtrace_proc privilege gives control over fasttrap * and pid providers. We need to grant wider destructive * privileges in the event that the user has proc_owner and/or * proc_zone. */ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE)) { if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER; if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE; } } return (state); } static int dtrace_state_buffer(dtrace_state_t *state, dtrace_buffer_t *buf, int which) { dtrace_optval_t *opt = state->dts_options, size; processorid_t cpu = 0;; int flags = 0, rval, factor, divisor = 1; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(which < DTRACEOPT_MAX); ASSERT(state->dts_activity == DTRACE_ACTIVITY_INACTIVE || (state == dtrace_anon.dta_state && state->dts_activity == DTRACE_ACTIVITY_ACTIVE)); if (opt[which] == DTRACEOPT_UNSET || opt[which] == 0) return (0); if (opt[DTRACEOPT_CPU] != DTRACEOPT_UNSET) cpu = opt[DTRACEOPT_CPU]; if (which == DTRACEOPT_SPECSIZE) flags |= DTRACEBUF_NOSWITCH; if (which == DTRACEOPT_BUFSIZE) { if (opt[DTRACEOPT_BUFPOLICY] == DTRACEOPT_BUFPOLICY_RING) flags |= DTRACEBUF_RING; if (opt[DTRACEOPT_BUFPOLICY] == DTRACEOPT_BUFPOLICY_FILL) flags |= DTRACEBUF_FILL; if (state != dtrace_anon.dta_state || state->dts_activity != DTRACE_ACTIVITY_ACTIVE) flags |= DTRACEBUF_INACTIVE; } for (size = opt[which]; size >= sizeof (uint64_t); size /= divisor) { /* * The size must be 8-byte aligned. If the size is not 8-byte * aligned, drop it down by the difference. */ if (size & (sizeof (uint64_t) - 1)) size -= size & (sizeof (uint64_t) - 1); if (size < state->dts_reserve) { /* * Buffers always must be large enough to accommodate * their prereserved space. We return E2BIG instead * of ENOMEM in this case to allow for user-level * software to differentiate the cases. */ return (E2BIG); } rval = dtrace_buffer_alloc(buf, size, flags, cpu, &factor); if (rval != ENOMEM) { opt[which] = size; return (rval); } if (opt[DTRACEOPT_BUFRESIZE] == DTRACEOPT_BUFRESIZE_MANUAL) return (rval); for (divisor = 2; divisor < factor; divisor <<= 1) continue; } return (ENOMEM); } static int dtrace_state_buffers(dtrace_state_t *state) { dtrace_speculation_t *spec = state->dts_speculations; int rval, i; if ((rval = dtrace_state_buffer(state, state->dts_buffer, DTRACEOPT_BUFSIZE)) != 0) return (rval); if ((rval = dtrace_state_buffer(state, state->dts_aggbuffer, DTRACEOPT_AGGSIZE)) != 0) return (rval); for (i = 0; i < state->dts_nspeculations; i++) { if ((rval = dtrace_state_buffer(state, spec[i].dtsp_buffer, DTRACEOPT_SPECSIZE)) != 0) return (rval); } return (0); } static void dtrace_state_prereserve(dtrace_state_t *state) { dtrace_ecb_t *ecb; dtrace_probe_t *probe; state->dts_reserve = 0; if (state->dts_options[DTRACEOPT_BUFPOLICY] != DTRACEOPT_BUFPOLICY_FILL) return; /* * If our buffer policy is a "fill" buffer policy, we need to set the * prereserved space to be the space required by the END probes. */ probe = dtrace_probes[dtrace_probeid_end - 1]; ASSERT(probe != NULL); for (ecb = probe->dtpr_ecb; ecb != NULL; ecb = ecb->dte_next) { if (ecb->dte_state != state) continue; state->dts_reserve += ecb->dte_needed + ecb->dte_alignment; } } static int dtrace_state_go(dtrace_state_t *state, processorid_t *cpu) { dtrace_optval_t *opt = state->dts_options, sz, nspec; dtrace_speculation_t *spec; dtrace_buffer_t *buf; #if defined(sun) cyc_handler_t hdlr; cyc_time_t when; #endif int rval = 0, i, bufsize = NCPU * sizeof (dtrace_buffer_t); dtrace_icookie_t cookie; mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) { rval = EBUSY; goto out; } /* * Before we can perform any checks, we must prime all of the * retained enablings that correspond to this state. */ dtrace_enabling_prime(state); if (state->dts_destructive && !state->dts_cred.dcr_destructive) { rval = EACCES; goto out; } dtrace_state_prereserve(state); /* * Now we want to do is try to allocate our speculations. * We do not automatically resize the number of speculations; if * this fails, we will fail the operation. */ nspec = opt[DTRACEOPT_NSPEC]; ASSERT(nspec != DTRACEOPT_UNSET); if (nspec > INT_MAX) { rval = ENOMEM; goto out; } spec = kmem_zalloc(nspec * sizeof (dtrace_speculation_t), KM_NOSLEEP | KM_NORMALPRI); if (spec == NULL) { rval = ENOMEM; goto out; } state->dts_speculations = spec; state->dts_nspeculations = (int)nspec; for (i = 0; i < nspec; i++) { if ((buf = kmem_zalloc(bufsize, KM_NOSLEEP | KM_NORMALPRI)) == NULL) { rval = ENOMEM; goto err; } spec[i].dtsp_buffer = buf; } if (opt[DTRACEOPT_GRABANON] != DTRACEOPT_UNSET) { if (dtrace_anon.dta_state == NULL) { rval = ENOENT; goto out; } if (state->dts_necbs != 0) { rval = EALREADY; goto out; } state->dts_anon = dtrace_anon_grab(); ASSERT(state->dts_anon != NULL); state = state->dts_anon; /* * We want "grabanon" to be set in the grabbed state, so we'll * copy that option value from the grabbing state into the * grabbed state. */ state->dts_options[DTRACEOPT_GRABANON] = opt[DTRACEOPT_GRABANON]; *cpu = dtrace_anon.dta_beganon; /* * If the anonymous state is active (as it almost certainly * is if the anonymous enabling ultimately matched anything), * we don't allow any further option processing -- but we * don't return failure. */ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) goto out; } if (opt[DTRACEOPT_AGGSIZE] != DTRACEOPT_UNSET && opt[DTRACEOPT_AGGSIZE] != 0) { if (state->dts_aggregations == NULL) { /* * We're not going to create an aggregation buffer * because we don't have any ECBs that contain * aggregations -- set this option to 0. */ opt[DTRACEOPT_AGGSIZE] = 0; } else { /* * If we have an aggregation buffer, we must also have * a buffer to use as scratch. */ if (opt[DTRACEOPT_BUFSIZE] == DTRACEOPT_UNSET || opt[DTRACEOPT_BUFSIZE] < state->dts_needed) { opt[DTRACEOPT_BUFSIZE] = state->dts_needed; } } } if (opt[DTRACEOPT_SPECSIZE] != DTRACEOPT_UNSET && opt[DTRACEOPT_SPECSIZE] != 0) { if (!state->dts_speculates) { /* * We're not going to create speculation buffers * because we don't have any ECBs that actually * speculate -- set the speculation size to 0. */ opt[DTRACEOPT_SPECSIZE] = 0; } } /* * The bare minimum size for any buffer that we're actually going to * do anything to is sizeof (uint64_t). */ sz = sizeof (uint64_t); if ((state->dts_needed != 0 && opt[DTRACEOPT_BUFSIZE] < sz) || (state->dts_speculates && opt[DTRACEOPT_SPECSIZE] < sz) || (state->dts_aggregations != NULL && opt[DTRACEOPT_AGGSIZE] < sz)) { /* * A buffer size has been explicitly set to 0 (or to a size * that will be adjusted to 0) and we need the space -- we * need to return failure. We return ENOSPC to differentiate * it from failing to allocate a buffer due to failure to meet * the reserve (for which we return E2BIG). */ rval = ENOSPC; goto out; } if ((rval = dtrace_state_buffers(state)) != 0) goto err; if ((sz = opt[DTRACEOPT_DYNVARSIZE]) == DTRACEOPT_UNSET) sz = dtrace_dstate_defsize; do { rval = dtrace_dstate_init(&state->dts_vstate.dtvs_dynvars, sz); if (rval == 0) break; if (opt[DTRACEOPT_BUFRESIZE] == DTRACEOPT_BUFRESIZE_MANUAL) goto err; } while (sz >>= 1); opt[DTRACEOPT_DYNVARSIZE] = sz; if (rval != 0) goto err; if (opt[DTRACEOPT_STATUSRATE] > dtrace_statusrate_max) opt[DTRACEOPT_STATUSRATE] = dtrace_statusrate_max; if (opt[DTRACEOPT_CLEANRATE] == 0) opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_max; if (opt[DTRACEOPT_CLEANRATE] < dtrace_cleanrate_min) opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_min; if (opt[DTRACEOPT_CLEANRATE] > dtrace_cleanrate_max) opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_max; state->dts_alive = state->dts_laststatus = dtrace_gethrtime(); #if defined(sun) hdlr.cyh_func = (cyc_func_t)dtrace_state_clean; hdlr.cyh_arg = state; hdlr.cyh_level = CY_LOW_LEVEL; when.cyt_when = 0; when.cyt_interval = opt[DTRACEOPT_CLEANRATE]; state->dts_cleaner = cyclic_add(&hdlr, &when); hdlr.cyh_func = (cyc_func_t)dtrace_state_deadman; hdlr.cyh_arg = state; hdlr.cyh_level = CY_LOW_LEVEL; when.cyt_when = 0; when.cyt_interval = dtrace_deadman_interval; state->dts_deadman = cyclic_add(&hdlr, &when); #else callout_reset(&state->dts_cleaner, hz * opt[DTRACEOPT_CLEANRATE] / NANOSEC, dtrace_state_clean, state); callout_reset(&state->dts_deadman, hz * dtrace_deadman_interval / NANOSEC, dtrace_state_deadman, state); #endif state->dts_activity = DTRACE_ACTIVITY_WARMUP; +#if defined(sun) + if (state->dts_getf != 0 && + !(state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL)) { + /* + * We don't have kernel privs but we have at least one call + * to getf(); we need to bump our zone's count, and (if + * this is the first enabling to have an unprivileged call + * to getf()) we need to hook into closef(). + */ + state->dts_cred.dcr_cred->cr_zone->zone_dtrace_getf++; + + if (dtrace_getf++ == 0) { + ASSERT(dtrace_closef == NULL); + dtrace_closef = dtrace_getf_barrier; + } + } +#endif + /* * Now it's time to actually fire the BEGIN probe. We need to disable * interrupts here both to record the CPU on which we fired the BEGIN * probe (the data from this CPU will be processed first at user * level) and to manually activate the buffer for this CPU. */ cookie = dtrace_interrupt_disable(); *cpu = curcpu; ASSERT(state->dts_buffer[*cpu].dtb_flags & DTRACEBUF_INACTIVE); state->dts_buffer[*cpu].dtb_flags &= ~DTRACEBUF_INACTIVE; dtrace_probe(dtrace_probeid_begin, (uint64_t)(uintptr_t)state, 0, 0, 0, 0); dtrace_interrupt_enable(cookie); /* * We may have had an exit action from a BEGIN probe; only change our * state to ACTIVE if we're still in WARMUP. */ ASSERT(state->dts_activity == DTRACE_ACTIVITY_WARMUP || state->dts_activity == DTRACE_ACTIVITY_DRAINING); if (state->dts_activity == DTRACE_ACTIVITY_WARMUP) state->dts_activity = DTRACE_ACTIVITY_ACTIVE; /* * Regardless of whether or not now we're in ACTIVE or DRAINING, we * want each CPU to transition its principal buffer out of the * INACTIVE state. Doing this assures that no CPU will suddenly begin * processing an ECB halfway down a probe's ECB chain; all CPUs will * atomically transition from processing none of a state's ECBs to * processing all of them. */ dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_buffer_activate, state); goto out; err: dtrace_buffer_free(state->dts_buffer); dtrace_buffer_free(state->dts_aggbuffer); if ((nspec = state->dts_nspeculations) == 0) { ASSERT(state->dts_speculations == NULL); goto out; } spec = state->dts_speculations; ASSERT(spec != NULL); for (i = 0; i < state->dts_nspeculations; i++) { if ((buf = spec[i].dtsp_buffer) == NULL) break; dtrace_buffer_free(buf); kmem_free(buf, bufsize); } kmem_free(spec, nspec * sizeof (dtrace_speculation_t)); state->dts_nspeculations = 0; state->dts_speculations = NULL; out: mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); return (rval); } static int dtrace_state_stop(dtrace_state_t *state, processorid_t *cpu) { dtrace_icookie_t cookie; ASSERT(MUTEX_HELD(&dtrace_lock)); if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE && state->dts_activity != DTRACE_ACTIVITY_DRAINING) return (EINVAL); /* * We'll set the activity to DTRACE_ACTIVITY_DRAINING, and issue a sync * to be sure that every CPU has seen it. See below for the details * on why this is done. */ state->dts_activity = DTRACE_ACTIVITY_DRAINING; dtrace_sync(); /* * By this point, it is impossible for any CPU to be still processing * with DTRACE_ACTIVITY_ACTIVE. We can thus set our activity to * DTRACE_ACTIVITY_COOLDOWN and know that we're not racing with any * other CPU in dtrace_buffer_reserve(). This allows dtrace_probe() * and callees to know that the activity is DTRACE_ACTIVITY_COOLDOWN * iff we're in the END probe. */ state->dts_activity = DTRACE_ACTIVITY_COOLDOWN; dtrace_sync(); ASSERT(state->dts_activity == DTRACE_ACTIVITY_COOLDOWN); /* * Finally, we can release the reserve and call the END probe. We * disable interrupts across calling the END probe to allow us to * return the CPU on which we actually called the END probe. This * allows user-land to be sure that this CPU's principal buffer is * processed last. */ state->dts_reserve = 0; cookie = dtrace_interrupt_disable(); *cpu = curcpu; dtrace_probe(dtrace_probeid_end, (uint64_t)(uintptr_t)state, 0, 0, 0, 0); dtrace_interrupt_enable(cookie); state->dts_activity = DTRACE_ACTIVITY_STOPPED; dtrace_sync(); +#if defined(sun) + if (state->dts_getf != 0 && + !(state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL)) { + /* + * We don't have kernel privs but we have at least one call + * to getf(); we need to lower our zone's count, and (if + * this is the last enabling to have an unprivileged call + * to getf()) we need to clear the closef() hook. + */ + ASSERT(state->dts_cred.dcr_cred->cr_zone->zone_dtrace_getf > 0); + ASSERT(dtrace_closef == dtrace_getf_barrier); + ASSERT(dtrace_getf > 0); + + state->dts_cred.dcr_cred->cr_zone->zone_dtrace_getf--; + + if (--dtrace_getf == 0) + dtrace_closef = NULL; + } +#endif + return (0); } static int dtrace_state_option(dtrace_state_t *state, dtrace_optid_t option, dtrace_optval_t val) { ASSERT(MUTEX_HELD(&dtrace_lock)); if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) return (EBUSY); if (option >= DTRACEOPT_MAX) return (EINVAL); if (option != DTRACEOPT_CPU && val < 0) return (EINVAL); switch (option) { case DTRACEOPT_DESTRUCTIVE: if (dtrace_destructive_disallow) return (EACCES); state->dts_cred.dcr_destructive = 1; break; case DTRACEOPT_BUFSIZE: case DTRACEOPT_DYNVARSIZE: case DTRACEOPT_AGGSIZE: case DTRACEOPT_SPECSIZE: case DTRACEOPT_STRSIZE: if (val < 0) return (EINVAL); if (val >= LONG_MAX) { /* * If this is an otherwise negative value, set it to * the highest multiple of 128m less than LONG_MAX. * Technically, we're adjusting the size without * regard to the buffer resizing policy, but in fact, * this has no effect -- if we set the buffer size to * ~LONG_MAX and the buffer policy is ultimately set to * be "manual", the buffer allocation is guaranteed to * fail, if only because the allocation requires two * buffers. (We set the the size to the highest * multiple of 128m because it ensures that the size * will remain a multiple of a megabyte when * repeatedly halved -- all the way down to 15m.) */ val = LONG_MAX - (1 << 27) + 1; } } state->dts_options[option] = val; return (0); } static void dtrace_state_destroy(dtrace_state_t *state) { dtrace_ecb_t *ecb; dtrace_vstate_t *vstate = &state->dts_vstate; #if defined(sun) minor_t minor = getminor(state->dts_dev); #endif int i, bufsize = NCPU * sizeof (dtrace_buffer_t); dtrace_speculation_t *spec = state->dts_speculations; int nspec = state->dts_nspeculations; uint32_t match; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&cpu_lock)); /* * First, retract any retained enablings for this state. */ dtrace_enabling_retract(state); ASSERT(state->dts_nretained == 0); if (state->dts_activity == DTRACE_ACTIVITY_ACTIVE || state->dts_activity == DTRACE_ACTIVITY_DRAINING) { /* * We have managed to come into dtrace_state_destroy() on a * hot enabling -- almost certainly because of a disorderly * shutdown of a consumer. (That is, a consumer that is * exiting without having called dtrace_stop().) In this case, * we're going to set our activity to be KILLED, and then * issue a sync to be sure that everyone is out of probe * context before we start blowing away ECBs. */ state->dts_activity = DTRACE_ACTIVITY_KILLED; dtrace_sync(); } /* * Release the credential hold we took in dtrace_state_create(). */ if (state->dts_cred.dcr_cred != NULL) crfree(state->dts_cred.dcr_cred); /* * Now we can safely disable and destroy any enabled probes. Because * any DTRACE_PRIV_KERNEL probes may actually be slowing our progress * (especially if they're all enabled), we take two passes through the * ECBs: in the first, we disable just DTRACE_PRIV_KERNEL probes, and * in the second we disable whatever is left over. */ for (match = DTRACE_PRIV_KERNEL; ; match = 0) { for (i = 0; i < state->dts_necbs; i++) { if ((ecb = state->dts_ecbs[i]) == NULL) continue; if (match && ecb->dte_probe != NULL) { dtrace_probe_t *probe = ecb->dte_probe; dtrace_provider_t *prov = probe->dtpr_provider; if (!(prov->dtpv_priv.dtpp_flags & match)) continue; } dtrace_ecb_disable(ecb); dtrace_ecb_destroy(ecb); } if (!match) break; } /* * Before we free the buffers, perform one more sync to assure that * every CPU is out of probe context. */ dtrace_sync(); dtrace_buffer_free(state->dts_buffer); dtrace_buffer_free(state->dts_aggbuffer); for (i = 0; i < nspec; i++) dtrace_buffer_free(spec[i].dtsp_buffer); #if defined(sun) if (state->dts_cleaner != CYCLIC_NONE) cyclic_remove(state->dts_cleaner); if (state->dts_deadman != CYCLIC_NONE) cyclic_remove(state->dts_deadman); #else callout_stop(&state->dts_cleaner); callout_drain(&state->dts_cleaner); callout_stop(&state->dts_deadman); callout_drain(&state->dts_deadman); #endif dtrace_dstate_fini(&vstate->dtvs_dynvars); dtrace_vstate_fini(vstate); if (state->dts_ecbs != NULL) kmem_free(state->dts_ecbs, state->dts_necbs * sizeof (dtrace_ecb_t *)); if (state->dts_aggregations != NULL) { #ifdef DEBUG for (i = 0; i < state->dts_naggregations; i++) ASSERT(state->dts_aggregations[i] == NULL); #endif ASSERT(state->dts_naggregations > 0); kmem_free(state->dts_aggregations, state->dts_naggregations * sizeof (dtrace_aggregation_t *)); } kmem_free(state->dts_buffer, bufsize); kmem_free(state->dts_aggbuffer, bufsize); for (i = 0; i < nspec; i++) kmem_free(spec[i].dtsp_buffer, bufsize); if (spec != NULL) kmem_free(spec, nspec * sizeof (dtrace_speculation_t)); dtrace_format_destroy(state); if (state->dts_aggid_arena != NULL) { #if defined(sun) vmem_destroy(state->dts_aggid_arena); #else delete_unrhdr(state->dts_aggid_arena); #endif state->dts_aggid_arena = NULL; } #if defined(sun) ddi_soft_state_free(dtrace_softstate, minor); vmem_free(dtrace_minor, (void *)(uintptr_t)minor, 1); #endif } /* * DTrace Anonymous Enabling Functions */ static dtrace_state_t * dtrace_anon_grab(void) { dtrace_state_t *state; ASSERT(MUTEX_HELD(&dtrace_lock)); if ((state = dtrace_anon.dta_state) == NULL) { ASSERT(dtrace_anon.dta_enabling == NULL); return (NULL); } ASSERT(dtrace_anon.dta_enabling != NULL); ASSERT(dtrace_retained != NULL); dtrace_enabling_destroy(dtrace_anon.dta_enabling); dtrace_anon.dta_enabling = NULL; dtrace_anon.dta_state = NULL; return (state); } static void dtrace_anon_property(void) { int i, rv; dtrace_state_t *state; dof_hdr_t *dof; char c[32]; /* enough for "dof-data-" + digits */ ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&cpu_lock)); for (i = 0; ; i++) { (void) snprintf(c, sizeof (c), "dof-data-%d", i); dtrace_err_verbose = 1; if ((dof = dtrace_dof_property(c)) == NULL) { dtrace_err_verbose = 0; break; } #if defined(sun) /* * We want to create anonymous state, so we need to transition * the kernel debugger to indicate that DTrace is active. If * this fails (e.g. because the debugger has modified text in * some way), we won't continue with the processing. */ if (kdi_dtrace_set(KDI_DTSET_DTRACE_ACTIVATE) != 0) { cmn_err(CE_NOTE, "kernel debugger active; anonymous " "enabling ignored."); dtrace_dof_destroy(dof); break; } #endif /* * If we haven't allocated an anonymous state, we'll do so now. */ if ((state = dtrace_anon.dta_state) == NULL) { #if defined(sun) state = dtrace_state_create(NULL, NULL); #else state = dtrace_state_create(NULL); #endif dtrace_anon.dta_state = state; if (state == NULL) { /* * This basically shouldn't happen: the only * failure mode from dtrace_state_create() is a * failure of ddi_soft_state_zalloc() that * itself should never happen. Still, the * interface allows for a failure mode, and * we want to fail as gracefully as possible: * we'll emit an error message and cease * processing anonymous state in this case. */ cmn_err(CE_WARN, "failed to create " "anonymous state"); dtrace_dof_destroy(dof); break; } } rv = dtrace_dof_slurp(dof, &state->dts_vstate, CRED(), &dtrace_anon.dta_enabling, 0, B_TRUE); if (rv == 0) rv = dtrace_dof_options(dof, state); dtrace_err_verbose = 0; dtrace_dof_destroy(dof); if (rv != 0) { /* * This is malformed DOF; chuck any anonymous state * that we created. */ ASSERT(dtrace_anon.dta_enabling == NULL); dtrace_state_destroy(state); dtrace_anon.dta_state = NULL; break; } ASSERT(dtrace_anon.dta_enabling != NULL); } if (dtrace_anon.dta_enabling != NULL) { int rval; /* * dtrace_enabling_retain() can only fail because we are * trying to retain more enablings than are allowed -- but * we only have one anonymous enabling, and we are guaranteed * to be allowed at least one retained enabling; we assert * that dtrace_enabling_retain() returns success. */ rval = dtrace_enabling_retain(dtrace_anon.dta_enabling); ASSERT(rval == 0); dtrace_enabling_dump(dtrace_anon.dta_enabling); } } /* * DTrace Helper Functions */ static void dtrace_helper_trace(dtrace_helper_action_t *helper, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate, int where) { uint32_t size, next, nnext, i; dtrace_helptrace_t *ent; uint16_t flags = cpu_core[curcpu].cpuc_dtrace_flags; if (!dtrace_helptrace_enabled) return; ASSERT(vstate->dtvs_nlocals <= dtrace_helptrace_nlocals); /* * What would a tracing framework be without its own tracing * framework? (Well, a hell of a lot simpler, for starters...) */ size = sizeof (dtrace_helptrace_t) + dtrace_helptrace_nlocals * sizeof (uint64_t) - sizeof (uint64_t); /* * Iterate until we can allocate a slot in the trace buffer. */ do { next = dtrace_helptrace_next; if (next + size < dtrace_helptrace_bufsize) { nnext = next + size; } else { nnext = size; } } while (dtrace_cas32(&dtrace_helptrace_next, next, nnext) != next); /* * We have our slot; fill it in. */ if (nnext == size) next = 0; ent = (dtrace_helptrace_t *)&dtrace_helptrace_buffer[next]; ent->dtht_helper = helper; ent->dtht_where = where; ent->dtht_nlocals = vstate->dtvs_nlocals; ent->dtht_fltoffs = (mstate->dtms_present & DTRACE_MSTATE_FLTOFFS) ? mstate->dtms_fltoffs : -1; ent->dtht_fault = DTRACE_FLAGS2FLT(flags); ent->dtht_illval = cpu_core[curcpu].cpuc_dtrace_illval; for (i = 0; i < vstate->dtvs_nlocals; i++) { dtrace_statvar_t *svar; if ((svar = vstate->dtvs_locals[i]) == NULL) continue; ASSERT(svar->dtsv_size >= NCPU * sizeof (uint64_t)); ent->dtht_locals[i] = ((uint64_t *)(uintptr_t)svar->dtsv_data)[curcpu]; } } static uint64_t dtrace_helper(int which, dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t arg0, uint64_t arg1) { uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags; uint64_t sarg0 = mstate->dtms_arg[0]; uint64_t sarg1 = mstate->dtms_arg[1]; uint64_t rval = 0; dtrace_helpers_t *helpers = curproc->p_dtrace_helpers; dtrace_helper_action_t *helper; dtrace_vstate_t *vstate; dtrace_difo_t *pred; int i, trace = dtrace_helptrace_enabled; ASSERT(which >= 0 && which < DTRACE_NHELPER_ACTIONS); if (helpers == NULL) return (0); if ((helper = helpers->dthps_actions[which]) == NULL) return (0); vstate = &helpers->dthps_vstate; mstate->dtms_arg[0] = arg0; mstate->dtms_arg[1] = arg1; /* * Now iterate over each helper. If its predicate evaluates to 'true', * we'll call the corresponding actions. Note that the below calls * to dtrace_dif_emulate() may set faults in machine state. This is * okay: our caller (the outer dtrace_dif_emulate()) will simply plow * the stored DIF offset with its own (which is the desired behavior). * Also, note the calls to dtrace_dif_emulate() may allocate scratch * from machine state; this is okay, too. */ for (; helper != NULL; helper = helper->dtha_next) { if ((pred = helper->dtha_predicate) != NULL) { if (trace) dtrace_helper_trace(helper, mstate, vstate, 0); if (!dtrace_dif_emulate(pred, mstate, vstate, state)) goto next; if (*flags & CPU_DTRACE_FAULT) goto err; } for (i = 0; i < helper->dtha_nactions; i++) { if (trace) dtrace_helper_trace(helper, mstate, vstate, i + 1); rval = dtrace_dif_emulate(helper->dtha_actions[i], mstate, vstate, state); if (*flags & CPU_DTRACE_FAULT) goto err; } next: if (trace) dtrace_helper_trace(helper, mstate, vstate, DTRACE_HELPTRACE_NEXT); } if (trace) dtrace_helper_trace(helper, mstate, vstate, DTRACE_HELPTRACE_DONE); /* * Restore the arg0 that we saved upon entry. */ mstate->dtms_arg[0] = sarg0; mstate->dtms_arg[1] = sarg1; return (rval); err: if (trace) dtrace_helper_trace(helper, mstate, vstate, DTRACE_HELPTRACE_ERR); /* * Restore the arg0 that we saved upon entry. */ mstate->dtms_arg[0] = sarg0; mstate->dtms_arg[1] = sarg1; return (0); } static void dtrace_helper_action_destroy(dtrace_helper_action_t *helper, dtrace_vstate_t *vstate) { int i; if (helper->dtha_predicate != NULL) dtrace_difo_release(helper->dtha_predicate, vstate); for (i = 0; i < helper->dtha_nactions; i++) { ASSERT(helper->dtha_actions[i] != NULL); dtrace_difo_release(helper->dtha_actions[i], vstate); } kmem_free(helper->dtha_actions, helper->dtha_nactions * sizeof (dtrace_difo_t *)); kmem_free(helper, sizeof (dtrace_helper_action_t)); } static int dtrace_helper_destroygen(int gen) { proc_t *p = curproc; dtrace_helpers_t *help = p->p_dtrace_helpers; dtrace_vstate_t *vstate; int i; ASSERT(MUTEX_HELD(&dtrace_lock)); if (help == NULL || gen > help->dthps_generation) return (EINVAL); vstate = &help->dthps_vstate; for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) { dtrace_helper_action_t *last = NULL, *h, *next; for (h = help->dthps_actions[i]; h != NULL; h = next) { next = h->dtha_next; if (h->dtha_generation == gen) { if (last != NULL) { last->dtha_next = next; } else { help->dthps_actions[i] = next; } dtrace_helper_action_destroy(h, vstate); } else { last = h; } } } /* * Interate until we've cleared out all helper providers with the * given generation number. */ for (;;) { dtrace_helper_provider_t *prov; /* * Look for a helper provider with the right generation. We * have to start back at the beginning of the list each time * because we drop dtrace_lock. It's unlikely that we'll make * more than two passes. */ for (i = 0; i < help->dthps_nprovs; i++) { prov = help->dthps_provs[i]; if (prov->dthp_generation == gen) break; } /* * If there were no matches, we're done. */ if (i == help->dthps_nprovs) break; /* * Move the last helper provider into this slot. */ help->dthps_nprovs--; help->dthps_provs[i] = help->dthps_provs[help->dthps_nprovs]; help->dthps_provs[help->dthps_nprovs] = NULL; mutex_exit(&dtrace_lock); /* * If we have a meta provider, remove this helper provider. */ mutex_enter(&dtrace_meta_lock); if (dtrace_meta_pid != NULL) { ASSERT(dtrace_deferred_pid == NULL); dtrace_helper_provider_remove(&prov->dthp_prov, p->p_pid); } mutex_exit(&dtrace_meta_lock); dtrace_helper_provider_destroy(prov); mutex_enter(&dtrace_lock); } return (0); } static int dtrace_helper_validate(dtrace_helper_action_t *helper) { int err = 0, i; dtrace_difo_t *dp; if ((dp = helper->dtha_predicate) != NULL) err += dtrace_difo_validate_helper(dp); for (i = 0; i < helper->dtha_nactions; i++) err += dtrace_difo_validate_helper(helper->dtha_actions[i]); return (err == 0); } static int dtrace_helper_action_add(int which, dtrace_ecbdesc_t *ep) { dtrace_helpers_t *help; dtrace_helper_action_t *helper, *last; dtrace_actdesc_t *act; dtrace_vstate_t *vstate; dtrace_predicate_t *pred; int count = 0, nactions = 0, i; if (which < 0 || which >= DTRACE_NHELPER_ACTIONS) return (EINVAL); help = curproc->p_dtrace_helpers; last = help->dthps_actions[which]; vstate = &help->dthps_vstate; for (count = 0; last != NULL; last = last->dtha_next) { count++; if (last->dtha_next == NULL) break; } /* * If we already have dtrace_helper_actions_max helper actions for this * helper action type, we'll refuse to add a new one. */ if (count >= dtrace_helper_actions_max) return (ENOSPC); helper = kmem_zalloc(sizeof (dtrace_helper_action_t), KM_SLEEP); helper->dtha_generation = help->dthps_generation; if ((pred = ep->dted_pred.dtpdd_predicate) != NULL) { ASSERT(pred->dtp_difo != NULL); dtrace_difo_hold(pred->dtp_difo); helper->dtha_predicate = pred->dtp_difo; } for (act = ep->dted_action; act != NULL; act = act->dtad_next) { if (act->dtad_kind != DTRACEACT_DIFEXPR) goto err; if (act->dtad_difo == NULL) goto err; nactions++; } helper->dtha_actions = kmem_zalloc(sizeof (dtrace_difo_t *) * (helper->dtha_nactions = nactions), KM_SLEEP); for (act = ep->dted_action, i = 0; act != NULL; act = act->dtad_next) { dtrace_difo_hold(act->dtad_difo); helper->dtha_actions[i++] = act->dtad_difo; } if (!dtrace_helper_validate(helper)) goto err; if (last == NULL) { help->dthps_actions[which] = helper; } else { last->dtha_next = helper; } if (vstate->dtvs_nlocals > dtrace_helptrace_nlocals) { dtrace_helptrace_nlocals = vstate->dtvs_nlocals; dtrace_helptrace_next = 0; } return (0); err: dtrace_helper_action_destroy(helper, vstate); return (EINVAL); } static void dtrace_helper_provider_register(proc_t *p, dtrace_helpers_t *help, dof_helper_t *dofhp) { ASSERT(MUTEX_NOT_HELD(&dtrace_lock)); mutex_enter(&dtrace_meta_lock); mutex_enter(&dtrace_lock); if (!dtrace_attached() || dtrace_meta_pid == NULL) { /* * If the dtrace module is loaded but not attached, or if * there aren't isn't a meta provider registered to deal with * these provider descriptions, we need to postpone creating * the actual providers until later. */ if (help->dthps_next == NULL && help->dthps_prev == NULL && dtrace_deferred_pid != help) { help->dthps_deferred = 1; help->dthps_pid = p->p_pid; help->dthps_next = dtrace_deferred_pid; help->dthps_prev = NULL; if (dtrace_deferred_pid != NULL) dtrace_deferred_pid->dthps_prev = help; dtrace_deferred_pid = help; } mutex_exit(&dtrace_lock); } else if (dofhp != NULL) { /* * If the dtrace module is loaded and we have a particular * helper provider description, pass that off to the * meta provider. */ mutex_exit(&dtrace_lock); dtrace_helper_provide(dofhp, p->p_pid); } else { /* * Otherwise, just pass all the helper provider descriptions * off to the meta provider. */ int i; mutex_exit(&dtrace_lock); for (i = 0; i < help->dthps_nprovs; i++) { dtrace_helper_provide(&help->dthps_provs[i]->dthp_prov, p->p_pid); } } mutex_exit(&dtrace_meta_lock); } static int dtrace_helper_provider_add(dof_helper_t *dofhp, int gen) { dtrace_helpers_t *help; dtrace_helper_provider_t *hprov, **tmp_provs; uint_t tmp_maxprovs, i; ASSERT(MUTEX_HELD(&dtrace_lock)); help = curproc->p_dtrace_helpers; ASSERT(help != NULL); /* * If we already have dtrace_helper_providers_max helper providers, * we're refuse to add a new one. */ if (help->dthps_nprovs >= dtrace_helper_providers_max) return (ENOSPC); /* * Check to make sure this isn't a duplicate. */ for (i = 0; i < help->dthps_nprovs; i++) { if (dofhp->dofhp_dof == help->dthps_provs[i]->dthp_prov.dofhp_dof) return (EALREADY); } hprov = kmem_zalloc(sizeof (dtrace_helper_provider_t), KM_SLEEP); hprov->dthp_prov = *dofhp; hprov->dthp_ref = 1; hprov->dthp_generation = gen; /* * Allocate a bigger table for helper providers if it's already full. */ if (help->dthps_maxprovs == help->dthps_nprovs) { tmp_maxprovs = help->dthps_maxprovs; tmp_provs = help->dthps_provs; if (help->dthps_maxprovs == 0) help->dthps_maxprovs = 2; else help->dthps_maxprovs *= 2; if (help->dthps_maxprovs > dtrace_helper_providers_max) help->dthps_maxprovs = dtrace_helper_providers_max; ASSERT(tmp_maxprovs < help->dthps_maxprovs); help->dthps_provs = kmem_zalloc(help->dthps_maxprovs * sizeof (dtrace_helper_provider_t *), KM_SLEEP); if (tmp_provs != NULL) { bcopy(tmp_provs, help->dthps_provs, tmp_maxprovs * sizeof (dtrace_helper_provider_t *)); kmem_free(tmp_provs, tmp_maxprovs * sizeof (dtrace_helper_provider_t *)); } } help->dthps_provs[help->dthps_nprovs] = hprov; help->dthps_nprovs++; return (0); } static void dtrace_helper_provider_destroy(dtrace_helper_provider_t *hprov) { mutex_enter(&dtrace_lock); if (--hprov->dthp_ref == 0) { dof_hdr_t *dof; mutex_exit(&dtrace_lock); dof = (dof_hdr_t *)(uintptr_t)hprov->dthp_prov.dofhp_dof; dtrace_dof_destroy(dof); kmem_free(hprov, sizeof (dtrace_helper_provider_t)); } else { mutex_exit(&dtrace_lock); } } static int dtrace_helper_provider_validate(dof_hdr_t *dof, dof_sec_t *sec) { uintptr_t daddr = (uintptr_t)dof; dof_sec_t *str_sec, *prb_sec, *arg_sec, *off_sec, *enoff_sec; dof_provider_t *provider; dof_probe_t *probe; uint8_t *arg; char *strtab, *typestr; dof_stridx_t typeidx; size_t typesz; uint_t nprobes, j, k; ASSERT(sec->dofs_type == DOF_SECT_PROVIDER); if (sec->dofs_offset & (sizeof (uint_t) - 1)) { dtrace_dof_error(dof, "misaligned section offset"); return (-1); } /* * The section needs to be large enough to contain the DOF provider * structure appropriate for the given version. */ if (sec->dofs_size < ((dof->dofh_ident[DOF_ID_VERSION] == DOF_VERSION_1) ? offsetof(dof_provider_t, dofpv_prenoffs) : sizeof (dof_provider_t))) { dtrace_dof_error(dof, "provider section too small"); return (-1); } provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset); str_sec = dtrace_dof_sect(dof, DOF_SECT_STRTAB, provider->dofpv_strtab); prb_sec = dtrace_dof_sect(dof, DOF_SECT_PROBES, provider->dofpv_probes); arg_sec = dtrace_dof_sect(dof, DOF_SECT_PRARGS, provider->dofpv_prargs); off_sec = dtrace_dof_sect(dof, DOF_SECT_PROFFS, provider->dofpv_proffs); if (str_sec == NULL || prb_sec == NULL || arg_sec == NULL || off_sec == NULL) return (-1); enoff_sec = NULL; if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 && provider->dofpv_prenoffs != DOF_SECT_NONE && (enoff_sec = dtrace_dof_sect(dof, DOF_SECT_PRENOFFS, provider->dofpv_prenoffs)) == NULL) return (-1); strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset); if (provider->dofpv_name >= str_sec->dofs_size || strlen(strtab + provider->dofpv_name) >= DTRACE_PROVNAMELEN) { dtrace_dof_error(dof, "invalid provider name"); return (-1); } if (prb_sec->dofs_entsize == 0 || prb_sec->dofs_entsize > prb_sec->dofs_size) { dtrace_dof_error(dof, "invalid entry size"); return (-1); } if (prb_sec->dofs_entsize & (sizeof (uintptr_t) - 1)) { dtrace_dof_error(dof, "misaligned entry size"); return (-1); } if (off_sec->dofs_entsize != sizeof (uint32_t)) { dtrace_dof_error(dof, "invalid entry size"); return (-1); } if (off_sec->dofs_offset & (sizeof (uint32_t) - 1)) { dtrace_dof_error(dof, "misaligned section offset"); return (-1); } if (arg_sec->dofs_entsize != sizeof (uint8_t)) { dtrace_dof_error(dof, "invalid entry size"); return (-1); } arg = (uint8_t *)(uintptr_t)(daddr + arg_sec->dofs_offset); nprobes = prb_sec->dofs_size / prb_sec->dofs_entsize; /* * Take a pass through the probes to check for errors. */ for (j = 0; j < nprobes; j++) { probe = (dof_probe_t *)(uintptr_t)(daddr + prb_sec->dofs_offset + j * prb_sec->dofs_entsize); if (probe->dofpr_func >= str_sec->dofs_size) { dtrace_dof_error(dof, "invalid function name"); return (-1); } if (strlen(strtab + probe->dofpr_func) >= DTRACE_FUNCNAMELEN) { dtrace_dof_error(dof, "function name too long"); return (-1); } if (probe->dofpr_name >= str_sec->dofs_size || strlen(strtab + probe->dofpr_name) >= DTRACE_NAMELEN) { dtrace_dof_error(dof, "invalid probe name"); return (-1); } /* * The offset count must not wrap the index, and the offsets * must also not overflow the section's data. */ if (probe->dofpr_offidx + probe->dofpr_noffs < probe->dofpr_offidx || (probe->dofpr_offidx + probe->dofpr_noffs) * off_sec->dofs_entsize > off_sec->dofs_size) { dtrace_dof_error(dof, "invalid probe offset"); return (-1); } if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1) { /* * If there's no is-enabled offset section, make sure * there aren't any is-enabled offsets. Otherwise * perform the same checks as for probe offsets * (immediately above). */ if (enoff_sec == NULL) { if (probe->dofpr_enoffidx != 0 || probe->dofpr_nenoffs != 0) { dtrace_dof_error(dof, "is-enabled " "offsets with null section"); return (-1); } } else if (probe->dofpr_enoffidx + probe->dofpr_nenoffs < probe->dofpr_enoffidx || (probe->dofpr_enoffidx + probe->dofpr_nenoffs) * enoff_sec->dofs_entsize > enoff_sec->dofs_size) { dtrace_dof_error(dof, "invalid is-enabled " "offset"); return (-1); } if (probe->dofpr_noffs + probe->dofpr_nenoffs == 0) { dtrace_dof_error(dof, "zero probe and " "is-enabled offsets"); return (-1); } } else if (probe->dofpr_noffs == 0) { dtrace_dof_error(dof, "zero probe offsets"); return (-1); } if (probe->dofpr_argidx + probe->dofpr_xargc < probe->dofpr_argidx || (probe->dofpr_argidx + probe->dofpr_xargc) * arg_sec->dofs_entsize > arg_sec->dofs_size) { dtrace_dof_error(dof, "invalid args"); return (-1); } typeidx = probe->dofpr_nargv; typestr = strtab + probe->dofpr_nargv; for (k = 0; k < probe->dofpr_nargc; k++) { if (typeidx >= str_sec->dofs_size) { dtrace_dof_error(dof, "bad " "native argument type"); return (-1); } typesz = strlen(typestr) + 1; if (typesz > DTRACE_ARGTYPELEN) { dtrace_dof_error(dof, "native " "argument type too long"); return (-1); } typeidx += typesz; typestr += typesz; } typeidx = probe->dofpr_xargv; typestr = strtab + probe->dofpr_xargv; for (k = 0; k < probe->dofpr_xargc; k++) { if (arg[probe->dofpr_argidx + k] > probe->dofpr_nargc) { dtrace_dof_error(dof, "bad " "native argument index"); return (-1); } if (typeidx >= str_sec->dofs_size) { dtrace_dof_error(dof, "bad " "translated argument type"); return (-1); } typesz = strlen(typestr) + 1; if (typesz > DTRACE_ARGTYPELEN) { dtrace_dof_error(dof, "translated argument " "type too long"); return (-1); } typeidx += typesz; typestr += typesz; } } return (0); } static int dtrace_helper_slurp(dof_hdr_t *dof, dof_helper_t *dhp) { dtrace_helpers_t *help; dtrace_vstate_t *vstate; dtrace_enabling_t *enab = NULL; int i, gen, rv, nhelpers = 0, nprovs = 0, destroy = 1; uintptr_t daddr = (uintptr_t)dof; ASSERT(MUTEX_HELD(&dtrace_lock)); if ((help = curproc->p_dtrace_helpers) == NULL) help = dtrace_helpers_create(curproc); vstate = &help->dthps_vstate; if ((rv = dtrace_dof_slurp(dof, vstate, NULL, &enab, dhp != NULL ? dhp->dofhp_addr : 0, B_FALSE)) != 0) { dtrace_dof_destroy(dof); return (rv); } /* * Look for helper providers and validate their descriptions. */ if (dhp != NULL) { for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_PROVIDER) continue; if (dtrace_helper_provider_validate(dof, sec) != 0) { dtrace_enabling_destroy(enab); dtrace_dof_destroy(dof); return (-1); } nprovs++; } } /* * Now we need to walk through the ECB descriptions in the enabling. */ for (i = 0; i < enab->dten_ndesc; i++) { dtrace_ecbdesc_t *ep = enab->dten_desc[i]; dtrace_probedesc_t *desc = &ep->dted_probe; if (strcmp(desc->dtpd_provider, "dtrace") != 0) continue; if (strcmp(desc->dtpd_mod, "helper") != 0) continue; if (strcmp(desc->dtpd_func, "ustack") != 0) continue; if ((rv = dtrace_helper_action_add(DTRACE_HELPER_ACTION_USTACK, ep)) != 0) { /* * Adding this helper action failed -- we are now going * to rip out the entire generation and return failure. */ (void) dtrace_helper_destroygen(help->dthps_generation); dtrace_enabling_destroy(enab); dtrace_dof_destroy(dof); return (-1); } nhelpers++; } if (nhelpers < enab->dten_ndesc) dtrace_dof_error(dof, "unmatched helpers"); gen = help->dthps_generation++; dtrace_enabling_destroy(enab); if (dhp != NULL && nprovs > 0) { dhp->dofhp_dof = (uint64_t)(uintptr_t)dof; if (dtrace_helper_provider_add(dhp, gen) == 0) { mutex_exit(&dtrace_lock); dtrace_helper_provider_register(curproc, help, dhp); mutex_enter(&dtrace_lock); destroy = 0; } } if (destroy) dtrace_dof_destroy(dof); return (gen); } static dtrace_helpers_t * dtrace_helpers_create(proc_t *p) { dtrace_helpers_t *help; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(p->p_dtrace_helpers == NULL); help = kmem_zalloc(sizeof (dtrace_helpers_t), KM_SLEEP); help->dthps_actions = kmem_zalloc(sizeof (dtrace_helper_action_t *) * DTRACE_NHELPER_ACTIONS, KM_SLEEP); p->p_dtrace_helpers = help; dtrace_helpers++; return (help); } #if defined(sun) static #endif void dtrace_helpers_destroy(proc_t *p) { dtrace_helpers_t *help; dtrace_vstate_t *vstate; #if defined(sun) proc_t *p = curproc; #endif int i; mutex_enter(&dtrace_lock); ASSERT(p->p_dtrace_helpers != NULL); ASSERT(dtrace_helpers > 0); help = p->p_dtrace_helpers; vstate = &help->dthps_vstate; /* * We're now going to lose the help from this process. */ p->p_dtrace_helpers = NULL; dtrace_sync(); /* * Destory the helper actions. */ for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) { dtrace_helper_action_t *h, *next; for (h = help->dthps_actions[i]; h != NULL; h = next) { next = h->dtha_next; dtrace_helper_action_destroy(h, vstate); h = next; } } mutex_exit(&dtrace_lock); /* * Destroy the helper providers. */ if (help->dthps_maxprovs > 0) { mutex_enter(&dtrace_meta_lock); if (dtrace_meta_pid != NULL) { ASSERT(dtrace_deferred_pid == NULL); for (i = 0; i < help->dthps_nprovs; i++) { dtrace_helper_provider_remove( &help->dthps_provs[i]->dthp_prov, p->p_pid); } } else { mutex_enter(&dtrace_lock); ASSERT(help->dthps_deferred == 0 || help->dthps_next != NULL || help->dthps_prev != NULL || help == dtrace_deferred_pid); /* * Remove the helper from the deferred list. */ if (help->dthps_next != NULL) help->dthps_next->dthps_prev = help->dthps_prev; if (help->dthps_prev != NULL) help->dthps_prev->dthps_next = help->dthps_next; if (dtrace_deferred_pid == help) { dtrace_deferred_pid = help->dthps_next; ASSERT(help->dthps_prev == NULL); } mutex_exit(&dtrace_lock); } mutex_exit(&dtrace_meta_lock); for (i = 0; i < help->dthps_nprovs; i++) { dtrace_helper_provider_destroy(help->dthps_provs[i]); } kmem_free(help->dthps_provs, help->dthps_maxprovs * sizeof (dtrace_helper_provider_t *)); } mutex_enter(&dtrace_lock); dtrace_vstate_fini(&help->dthps_vstate); kmem_free(help->dthps_actions, sizeof (dtrace_helper_action_t *) * DTRACE_NHELPER_ACTIONS); kmem_free(help, sizeof (dtrace_helpers_t)); --dtrace_helpers; mutex_exit(&dtrace_lock); } #if defined(sun) static #endif void dtrace_helpers_duplicate(proc_t *from, proc_t *to) { dtrace_helpers_t *help, *newhelp; dtrace_helper_action_t *helper, *new, *last; dtrace_difo_t *dp; dtrace_vstate_t *vstate; int i, j, sz, hasprovs = 0; mutex_enter(&dtrace_lock); ASSERT(from->p_dtrace_helpers != NULL); ASSERT(dtrace_helpers > 0); help = from->p_dtrace_helpers; newhelp = dtrace_helpers_create(to); ASSERT(to->p_dtrace_helpers != NULL); newhelp->dthps_generation = help->dthps_generation; vstate = &newhelp->dthps_vstate; /* * Duplicate the helper actions. */ for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) { if ((helper = help->dthps_actions[i]) == NULL) continue; for (last = NULL; helper != NULL; helper = helper->dtha_next) { new = kmem_zalloc(sizeof (dtrace_helper_action_t), KM_SLEEP); new->dtha_generation = helper->dtha_generation; if ((dp = helper->dtha_predicate) != NULL) { dp = dtrace_difo_duplicate(dp, vstate); new->dtha_predicate = dp; } new->dtha_nactions = helper->dtha_nactions; sz = sizeof (dtrace_difo_t *) * new->dtha_nactions; new->dtha_actions = kmem_alloc(sz, KM_SLEEP); for (j = 0; j < new->dtha_nactions; j++) { dtrace_difo_t *dp = helper->dtha_actions[j]; ASSERT(dp != NULL); dp = dtrace_difo_duplicate(dp, vstate); new->dtha_actions[j] = dp; } if (last != NULL) { last->dtha_next = new; } else { newhelp->dthps_actions[i] = new; } last = new; } } /* * Duplicate the helper providers and register them with the * DTrace framework. */ if (help->dthps_nprovs > 0) { newhelp->dthps_nprovs = help->dthps_nprovs; newhelp->dthps_maxprovs = help->dthps_nprovs; newhelp->dthps_provs = kmem_alloc(newhelp->dthps_nprovs * sizeof (dtrace_helper_provider_t *), KM_SLEEP); for (i = 0; i < newhelp->dthps_nprovs; i++) { newhelp->dthps_provs[i] = help->dthps_provs[i]; newhelp->dthps_provs[i]->dthp_ref++; } hasprovs = 1; } mutex_exit(&dtrace_lock); if (hasprovs) dtrace_helper_provider_register(to, newhelp, NULL); } /* * DTrace Hook Functions */ static void dtrace_module_loaded(modctl_t *ctl) { dtrace_provider_t *prv; mutex_enter(&dtrace_provider_lock); #if defined(sun) mutex_enter(&mod_lock); #endif #if defined(sun) ASSERT(ctl->mod_busy); #endif /* * We're going to call each providers per-module provide operation * specifying only this module. */ for (prv = dtrace_provider; prv != NULL; prv = prv->dtpv_next) prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, ctl); #if defined(sun) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); /* * If we have any retained enablings, we need to match against them. * Enabling probes requires that cpu_lock be held, and we cannot hold * cpu_lock here -- it is legal for cpu_lock to be held when loading a * module. (In particular, this happens when loading scheduling * classes.) So if we have any retained enablings, we need to dispatch * our task queue to do the match for us. */ mutex_enter(&dtrace_lock); if (dtrace_retained == NULL) { mutex_exit(&dtrace_lock); return; } (void) taskq_dispatch(dtrace_taskq, (task_func_t *)dtrace_enabling_matchall, NULL, TQ_SLEEP); mutex_exit(&dtrace_lock); /* * And now, for a little heuristic sleaze: in general, we want to * match modules as soon as they load. However, we cannot guarantee * this, because it would lead us to the lock ordering violation * outlined above. The common case, of course, is that cpu_lock is * _not_ held -- so we delay here for a clock tick, hoping that that's * long enough for the task queue to do its work. If it's not, it's * not a serious problem -- it just means that the module that we * just loaded may not be immediately instrumentable. */ delay(1); } static void #if defined(sun) dtrace_module_unloaded(modctl_t *ctl) #else dtrace_module_unloaded(modctl_t *ctl, int *error) #endif { dtrace_probe_t template, *probe, *first, *next; dtrace_provider_t *prov; #if !defined(sun) char modname[DTRACE_MODNAMELEN]; size_t len; #endif #if defined(sun) template.dtpr_mod = ctl->mod_modname; #else /* Handle the fact that ctl->filename may end in ".ko". */ strlcpy(modname, ctl->filename, sizeof(modname)); len = strlen(ctl->filename); if (len > 3 && strcmp(modname + len - 3, ".ko") == 0) modname[len - 3] = '\0'; template.dtpr_mod = modname; #endif mutex_enter(&dtrace_provider_lock); #if defined(sun) mutex_enter(&mod_lock); #endif mutex_enter(&dtrace_lock); #if !defined(sun) if (ctl->nenabled > 0) { /* Don't allow unloads if a probe is enabled. */ mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); *error = -1; printf( "kldunload: attempt to unload module that has DTrace probes enabled\n"); return; } #endif if (dtrace_bymod == NULL) { /* * The DTrace module is loaded (obviously) but not attached; * we don't have any work to do. */ mutex_exit(&dtrace_provider_lock); #if defined(sun) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_lock); return; } for (probe = first = dtrace_hash_lookup(dtrace_bymod, &template); probe != NULL; probe = probe->dtpr_nextmod) { if (probe->dtpr_ecb != NULL) { mutex_exit(&dtrace_provider_lock); #if defined(sun) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_lock); /* * This shouldn't _actually_ be possible -- we're * unloading a module that has an enabled probe in it. * (It's normally up to the provider to make sure that * this can't happen.) However, because dtps_enable() * doesn't have a failure mode, there can be an * enable/unload race. Upshot: we don't want to * assert, but we're not going to disable the * probe, either. */ if (dtrace_err_verbose) { #if defined(sun) cmn_err(CE_WARN, "unloaded module '%s' had " "enabled probes", ctl->mod_modname); #else cmn_err(CE_WARN, "unloaded module '%s' had " "enabled probes", modname); #endif } return; } } probe = first; for (first = NULL; probe != NULL; probe = next) { ASSERT(dtrace_probes[probe->dtpr_id - 1] == probe); dtrace_probes[probe->dtpr_id - 1] = NULL; next = probe->dtpr_nextmod; dtrace_hash_remove(dtrace_bymod, probe); dtrace_hash_remove(dtrace_byfunc, probe); dtrace_hash_remove(dtrace_byname, probe); if (first == NULL) { first = probe; probe->dtpr_nextmod = NULL; } else { probe->dtpr_nextmod = first; first = probe; } } /* * We've removed all of the module's probes from the hash chains and * from the probe array. Now issue a dtrace_sync() to be sure that * everyone has cleared out from any probe array processing. */ dtrace_sync(); for (probe = first; probe != NULL; probe = first) { first = probe->dtpr_nextmod; prov = probe->dtpr_provider; prov->dtpv_pops.dtps_destroy(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1); kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1); kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1); #if defined(sun) vmem_free(dtrace_arena, (void *)(uintptr_t)probe->dtpr_id, 1); #else free_unr(dtrace_arena, probe->dtpr_id); #endif kmem_free(probe, sizeof (dtrace_probe_t)); } mutex_exit(&dtrace_lock); #if defined(sun) mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); } #if !defined(sun) static void dtrace_kld_load(void *arg __unused, linker_file_t lf) { dtrace_module_loaded(lf); } static void dtrace_kld_unload_try(void *arg __unused, linker_file_t lf, int *error) { if (*error != 0) /* We already have an error, so don't do anything. */ return; dtrace_module_unloaded(lf, error); } #endif #if defined(sun) static void dtrace_suspend(void) { dtrace_probe_foreach(offsetof(dtrace_pops_t, dtps_suspend)); } static void dtrace_resume(void) { dtrace_probe_foreach(offsetof(dtrace_pops_t, dtps_resume)); } #endif static int dtrace_cpu_setup(cpu_setup_t what, processorid_t cpu) { ASSERT(MUTEX_HELD(&cpu_lock)); mutex_enter(&dtrace_lock); switch (what) { case CPU_CONFIG: { dtrace_state_t *state; dtrace_optval_t *opt, rs, c; /* * For now, we only allocate a new buffer for anonymous state. */ if ((state = dtrace_anon.dta_state) == NULL) break; if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE) break; opt = state->dts_options; c = opt[DTRACEOPT_CPU]; if (c != DTRACE_CPUALL && c != DTRACEOPT_UNSET && c != cpu) break; /* * Regardless of what the actual policy is, we're going to * temporarily set our resize policy to be manual. We're * also going to temporarily set our CPU option to denote * the newly configured CPU. */ rs = opt[DTRACEOPT_BUFRESIZE]; opt[DTRACEOPT_BUFRESIZE] = DTRACEOPT_BUFRESIZE_MANUAL; opt[DTRACEOPT_CPU] = (dtrace_optval_t)cpu; (void) dtrace_state_buffers(state); opt[DTRACEOPT_BUFRESIZE] = rs; opt[DTRACEOPT_CPU] = c; break; } case CPU_UNCONFIG: /* * We don't free the buffer in the CPU_UNCONFIG case. (The * buffer will be freed when the consumer exits.) */ break; default: break; } mutex_exit(&dtrace_lock); return (0); } #if defined(sun) static void dtrace_cpu_setup_initial(processorid_t cpu) { (void) dtrace_cpu_setup(CPU_CONFIG, cpu); } #endif static void dtrace_toxrange_add(uintptr_t base, uintptr_t limit) { if (dtrace_toxranges >= dtrace_toxranges_max) { int osize, nsize; dtrace_toxrange_t *range; osize = dtrace_toxranges_max * sizeof (dtrace_toxrange_t); if (osize == 0) { ASSERT(dtrace_toxrange == NULL); ASSERT(dtrace_toxranges_max == 0); dtrace_toxranges_max = 1; } else { dtrace_toxranges_max <<= 1; } nsize = dtrace_toxranges_max * sizeof (dtrace_toxrange_t); range = kmem_zalloc(nsize, KM_SLEEP); if (dtrace_toxrange != NULL) { ASSERT(osize != 0); bcopy(dtrace_toxrange, range, osize); kmem_free(dtrace_toxrange, osize); } dtrace_toxrange = range; } ASSERT(dtrace_toxrange[dtrace_toxranges].dtt_base == 0); ASSERT(dtrace_toxrange[dtrace_toxranges].dtt_limit == 0); dtrace_toxrange[dtrace_toxranges].dtt_base = base; dtrace_toxrange[dtrace_toxranges].dtt_limit = limit; dtrace_toxranges++; } +static void +dtrace_getf_barrier() +{ +#if defined(sun) + /* + * When we have unprivileged (that is, non-DTRACE_CRV_KERNEL) enablings + * that contain calls to getf(), this routine will be called on every + * closef() before either the underlying vnode is released or the + * file_t itself is freed. By the time we are here, it is essential + * that the file_t can no longer be accessed from a call to getf() + * in probe context -- that assures that a dtrace_sync() can be used + * to clear out any enablings referring to the old structures. + */ + if (curthread->t_procp->p_zone->zone_dtrace_getf != 0 || + kcred->cr_zone->zone_dtrace_getf != 0) + dtrace_sync(); +#endif +} + /* * DTrace Driver Cookbook Functions */ #if defined(sun) /*ARGSUSED*/ static int dtrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { dtrace_provider_id_t id; dtrace_state_t *state = NULL; dtrace_enabling_t *enab; mutex_enter(&cpu_lock); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); if (ddi_soft_state_init(&dtrace_softstate, sizeof (dtrace_state_t), 0) != 0) { cmn_err(CE_NOTE, "/dev/dtrace failed to initialize soft state"); mutex_exit(&cpu_lock); mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); return (DDI_FAILURE); } if (ddi_create_minor_node(devi, DTRACEMNR_DTRACE, S_IFCHR, DTRACEMNRN_DTRACE, DDI_PSEUDO, NULL) == DDI_FAILURE || ddi_create_minor_node(devi, DTRACEMNR_HELPER, S_IFCHR, DTRACEMNRN_HELPER, DDI_PSEUDO, NULL) == DDI_FAILURE) { cmn_err(CE_NOTE, "/dev/dtrace couldn't create minor nodes"); ddi_remove_minor_node(devi, NULL); ddi_soft_state_fini(&dtrace_softstate); mutex_exit(&cpu_lock); mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); return (DDI_FAILURE); } ddi_report_dev(devi); dtrace_devi = devi; dtrace_modload = dtrace_module_loaded; dtrace_modunload = dtrace_module_unloaded; dtrace_cpu_init = dtrace_cpu_setup_initial; dtrace_helpers_cleanup = dtrace_helpers_destroy; dtrace_helpers_fork = dtrace_helpers_duplicate; dtrace_cpustart_init = dtrace_suspend; dtrace_cpustart_fini = dtrace_resume; dtrace_debugger_init = dtrace_suspend; dtrace_debugger_fini = dtrace_resume; register_cpu_setup_func((cpu_setup_func_t *)dtrace_cpu_setup, NULL); ASSERT(MUTEX_HELD(&cpu_lock)); dtrace_arena = vmem_create("dtrace", (void *)1, UINT32_MAX, 1, NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER); dtrace_minor = vmem_create("dtrace_minor", (void *)DTRACEMNRN_CLONE, UINT32_MAX - DTRACEMNRN_CLONE, 1, NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER); dtrace_taskq = taskq_create("dtrace_taskq", 1, maxclsyspri, 1, INT_MAX, 0); dtrace_state_cache = kmem_cache_create("dtrace_state_cache", sizeof (dtrace_dstate_percpu_t) * NCPU, DTRACE_STATE_ALIGN, NULL, NULL, NULL, NULL, NULL, 0); ASSERT(MUTEX_HELD(&cpu_lock)); dtrace_bymod = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_mod), offsetof(dtrace_probe_t, dtpr_nextmod), offsetof(dtrace_probe_t, dtpr_prevmod)); dtrace_byfunc = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_func), offsetof(dtrace_probe_t, dtpr_nextfunc), offsetof(dtrace_probe_t, dtpr_prevfunc)); dtrace_byname = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_name), offsetof(dtrace_probe_t, dtpr_nextname), offsetof(dtrace_probe_t, dtpr_prevname)); if (dtrace_retain_max < 1) { cmn_err(CE_WARN, "illegal value (%lu) for dtrace_retain_max; " "setting to 1", dtrace_retain_max); dtrace_retain_max = 1; } /* * Now discover our toxic ranges. */ dtrace_toxic_ranges(dtrace_toxrange_add); /* * Before we register ourselves as a provider to our own framework, * we would like to assert that dtrace_provider is NULL -- but that's * not true if we were loaded as a dependency of a DTrace provider. * Once we've registered, we can assert that dtrace_provider is our * pseudo provider. */ (void) dtrace_register("dtrace", &dtrace_provider_attr, DTRACE_PRIV_NONE, 0, &dtrace_provider_ops, NULL, &id); ASSERT(dtrace_provider != NULL); ASSERT((dtrace_provider_id_t)dtrace_provider == id); dtrace_probeid_begin = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "BEGIN", 0, NULL); dtrace_probeid_end = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "END", 0, NULL); dtrace_probeid_error = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "ERROR", 1, NULL); dtrace_anon_property(); mutex_exit(&cpu_lock); /* * If DTrace helper tracing is enabled, we need to allocate the * trace buffer and initialize the values. */ if (dtrace_helptrace_enabled) { ASSERT(dtrace_helptrace_buffer == NULL); dtrace_helptrace_buffer = kmem_zalloc(dtrace_helptrace_bufsize, KM_SLEEP); dtrace_helptrace_next = 0; } /* * If there are already providers, we must ask them to provide their * probes, and then match any anonymous enabling against them. Note * that there should be no other retained enablings at this time: * the only retained enablings at this time should be the anonymous * enabling. */ if (dtrace_anon.dta_enabling != NULL) { ASSERT(dtrace_retained == dtrace_anon.dta_enabling); dtrace_enabling_provide(NULL); state = dtrace_anon.dta_state; /* * We couldn't hold cpu_lock across the above call to * dtrace_enabling_provide(), but we must hold it to actually * enable the probes. We have to drop all of our locks, pick * up cpu_lock, and regain our locks before matching the * retained anonymous enabling. */ mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); mutex_enter(&cpu_lock); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); if ((enab = dtrace_anon.dta_enabling) != NULL) (void) dtrace_enabling_match(enab, NULL); mutex_exit(&cpu_lock); } mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); if (state != NULL) { /* * If we created any anonymous state, set it going now. */ (void) dtrace_state_go(state, &dtrace_anon.dta_beganon); } return (DDI_SUCCESS); } #endif #if !defined(sun) #if __FreeBSD_version >= 800039 static void dtrace_dtr(void *); #endif #endif /*ARGSUSED*/ static int #if defined(sun) dtrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) #else dtrace_open(struct cdev *dev, int oflags, int devtype, struct thread *td) #endif { dtrace_state_t *state; uint32_t priv; uid_t uid; zoneid_t zoneid; #if defined(sun) if (getminor(*devp) == DTRACEMNRN_HELPER) return (0); /* * If this wasn't an open with the "helper" minor, then it must be * the "dtrace" minor. */ if (getminor(*devp) == DTRACEMNRN_DTRACE) return (ENXIO); #else cred_t *cred_p = NULL; #if __FreeBSD_version < 800039 /* * The first minor device is the one that is cloned so there is * nothing more to do here. */ if (dev2unit(dev) == 0) return 0; /* * Devices are cloned, so if the DTrace state has already * been allocated, that means this device belongs to a * different client. Each client should open '/dev/dtrace' * to get a cloned device. */ if (dev->si_drv1 != NULL) return (EBUSY); #endif cred_p = dev->si_cred; #endif /* * If no DTRACE_PRIV_* bits are set in the credential, then the * caller lacks sufficient permission to do anything with DTrace. */ dtrace_cred2priv(cred_p, &priv, &uid, &zoneid); if (priv == DTRACE_PRIV_NONE) { #if !defined(sun) #if __FreeBSD_version < 800039 /* Destroy the cloned device. */ destroy_dev(dev); #endif #endif return (EACCES); } /* * Ask all providers to provide all their probes. */ mutex_enter(&dtrace_provider_lock); dtrace_probe_provide(NULL, NULL); mutex_exit(&dtrace_provider_lock); mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); dtrace_opens++; dtrace_membar_producer(); #if defined(sun) /* * If the kernel debugger is active (that is, if the kernel debugger * modified text in some way), we won't allow the open. */ if (kdi_dtrace_set(KDI_DTSET_DTRACE_ACTIVATE) != 0) { dtrace_opens--; mutex_exit(&cpu_lock); mutex_exit(&dtrace_lock); return (EBUSY); } state = dtrace_state_create(devp, cred_p); #else state = dtrace_state_create(dev); #if __FreeBSD_version < 800039 dev->si_drv1 = state; #else devfs_set_cdevpriv(state, dtrace_dtr); #endif #endif mutex_exit(&cpu_lock); if (state == NULL) { #if defined(sun) if (--dtrace_opens == 0 && dtrace_anon.dta_enabling == NULL) (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE); #else --dtrace_opens; #endif mutex_exit(&dtrace_lock); #if !defined(sun) #if __FreeBSD_version < 800039 /* Destroy the cloned device. */ destroy_dev(dev); #endif #endif return (EAGAIN); } mutex_exit(&dtrace_lock); return (0); } /*ARGSUSED*/ #if defined(sun) static int dtrace_close(dev_t dev, int flag, int otyp, cred_t *cred_p) #elif __FreeBSD_version < 800039 static int dtrace_close(struct cdev *dev, int flags, int fmt __unused, struct thread *td) #else static void dtrace_dtr(void *data) #endif { #if defined(sun) minor_t minor = getminor(dev); dtrace_state_t *state; if (minor == DTRACEMNRN_HELPER) return (0); state = ddi_get_soft_state(dtrace_softstate, minor); #else #if __FreeBSD_version < 800039 dtrace_state_t *state = dev->si_drv1; /* Check if this is not a cloned device. */ if (dev2unit(dev) == 0) return (0); #else dtrace_state_t *state = data; #endif #endif mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); if (state != NULL) { if (state->dts_anon) { /* * There is anonymous state. Destroy that first. */ ASSERT(dtrace_anon.dta_state == NULL); dtrace_state_destroy(state->dts_anon); } dtrace_state_destroy(state); #if !defined(sun) kmem_free(state, 0); #if __FreeBSD_version < 800039 dev->si_drv1 = NULL; #endif #endif } ASSERT(dtrace_opens > 0); #if defined(sun) /* * Only relinquish control of the kernel debugger interface when there * are no consumers and no anonymous enablings. */ if (--dtrace_opens == 0 && dtrace_anon.dta_enabling == NULL) (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE); #else --dtrace_opens; #endif mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); #if __FreeBSD_version < 800039 /* Schedule this cloned device to be destroyed. */ destroy_dev_sched(dev); #endif #if defined(sun) || __FreeBSD_version < 800039 return (0); #endif } #if defined(sun) /*ARGSUSED*/ static int dtrace_ioctl_helper(int cmd, intptr_t arg, int *rv) { int rval; dof_helper_t help, *dhp = NULL; switch (cmd) { case DTRACEHIOC_ADDDOF: if (copyin((void *)arg, &help, sizeof (help)) != 0) { dtrace_dof_error(NULL, "failed to copyin DOF helper"); return (EFAULT); } dhp = &help; arg = (intptr_t)help.dofhp_dof; /*FALLTHROUGH*/ case DTRACEHIOC_ADD: { dof_hdr_t *dof = dtrace_dof_copyin(arg, &rval); if (dof == NULL) return (rval); mutex_enter(&dtrace_lock); /* * dtrace_helper_slurp() takes responsibility for the dof -- * it may free it now or it may save it and free it later. */ if ((rval = dtrace_helper_slurp(dof, dhp)) != -1) { *rv = rval; rval = 0; } else { rval = EINVAL; } mutex_exit(&dtrace_lock); return (rval); } case DTRACEHIOC_REMOVE: { mutex_enter(&dtrace_lock); rval = dtrace_helper_destroygen(arg); mutex_exit(&dtrace_lock); return (rval); } default: break; } return (ENOTTY); } /*ARGSUSED*/ static int dtrace_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv) { minor_t minor = getminor(dev); dtrace_state_t *state; int rval; if (minor == DTRACEMNRN_HELPER) return (dtrace_ioctl_helper(cmd, arg, rv)); state = ddi_get_soft_state(dtrace_softstate, minor); if (state->dts_anon) { ASSERT(dtrace_anon.dta_state == NULL); state = state->dts_anon; } switch (cmd) { case DTRACEIOC_PROVIDER: { dtrace_providerdesc_t pvd; dtrace_provider_t *pvp; if (copyin((void *)arg, &pvd, sizeof (pvd)) != 0) return (EFAULT); pvd.dtvd_name[DTRACE_PROVNAMELEN - 1] = '\0'; mutex_enter(&dtrace_provider_lock); for (pvp = dtrace_provider; pvp != NULL; pvp = pvp->dtpv_next) { if (strcmp(pvp->dtpv_name, pvd.dtvd_name) == 0) break; } mutex_exit(&dtrace_provider_lock); if (pvp == NULL) return (ESRCH); bcopy(&pvp->dtpv_priv, &pvd.dtvd_priv, sizeof (dtrace_ppriv_t)); bcopy(&pvp->dtpv_attr, &pvd.dtvd_attr, sizeof (dtrace_pattr_t)); if (copyout(&pvd, (void *)arg, sizeof (pvd)) != 0) return (EFAULT); return (0); } case DTRACEIOC_EPROBE: { dtrace_eprobedesc_t epdesc; dtrace_ecb_t *ecb; dtrace_action_t *act; void *buf; size_t size; uintptr_t dest; int nrecs; if (copyin((void *)arg, &epdesc, sizeof (epdesc)) != 0) return (EFAULT); mutex_enter(&dtrace_lock); if ((ecb = dtrace_epid2ecb(state, epdesc.dtepd_epid)) == NULL) { mutex_exit(&dtrace_lock); return (EINVAL); } if (ecb->dte_probe == NULL) { mutex_exit(&dtrace_lock); return (EINVAL); } epdesc.dtepd_probeid = ecb->dte_probe->dtpr_id; epdesc.dtepd_uarg = ecb->dte_uarg; epdesc.dtepd_size = ecb->dte_size; nrecs = epdesc.dtepd_nrecs; epdesc.dtepd_nrecs = 0; for (act = ecb->dte_action; act != NULL; act = act->dta_next) { if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple) continue; epdesc.dtepd_nrecs++; } /* * Now that we have the size, we need to allocate a temporary * buffer in which to store the complete description. We need * the temporary buffer to be able to drop dtrace_lock() * across the copyout(), below. */ size = sizeof (dtrace_eprobedesc_t) + (epdesc.dtepd_nrecs * sizeof (dtrace_recdesc_t)); buf = kmem_alloc(size, KM_SLEEP); dest = (uintptr_t)buf; bcopy(&epdesc, (void *)dest, sizeof (epdesc)); dest += offsetof(dtrace_eprobedesc_t, dtepd_rec[0]); for (act = ecb->dte_action; act != NULL; act = act->dta_next) { if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple) continue; if (nrecs-- == 0) break; bcopy(&act->dta_rec, (void *)dest, sizeof (dtrace_recdesc_t)); dest += sizeof (dtrace_recdesc_t); } mutex_exit(&dtrace_lock); if (copyout(buf, (void *)arg, dest - (uintptr_t)buf) != 0) { kmem_free(buf, size); return (EFAULT); } kmem_free(buf, size); return (0); } case DTRACEIOC_AGGDESC: { dtrace_aggdesc_t aggdesc; dtrace_action_t *act; dtrace_aggregation_t *agg; int nrecs; uint32_t offs; dtrace_recdesc_t *lrec; void *buf; size_t size; uintptr_t dest; if (copyin((void *)arg, &aggdesc, sizeof (aggdesc)) != 0) return (EFAULT); mutex_enter(&dtrace_lock); if ((agg = dtrace_aggid2agg(state, aggdesc.dtagd_id)) == NULL) { mutex_exit(&dtrace_lock); return (EINVAL); } aggdesc.dtagd_epid = agg->dtag_ecb->dte_epid; nrecs = aggdesc.dtagd_nrecs; aggdesc.dtagd_nrecs = 0; offs = agg->dtag_base; lrec = &agg->dtag_action.dta_rec; aggdesc.dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - offs; for (act = agg->dtag_first; ; act = act->dta_next) { ASSERT(act->dta_intuple || DTRACEACT_ISAGG(act->dta_kind)); /* * If this action has a record size of zero, it * denotes an argument to the aggregating action. * Because the presence of this record doesn't (or * shouldn't) affect the way the data is interpreted, * we don't copy it out to save user-level the * confusion of dealing with a zero-length record. */ if (act->dta_rec.dtrd_size == 0) { ASSERT(agg->dtag_hasarg); continue; } aggdesc.dtagd_nrecs++; if (act == &agg->dtag_action) break; } /* * Now that we have the size, we need to allocate a temporary * buffer in which to store the complete description. We need * the temporary buffer to be able to drop dtrace_lock() * across the copyout(), below. */ size = sizeof (dtrace_aggdesc_t) + (aggdesc.dtagd_nrecs * sizeof (dtrace_recdesc_t)); buf = kmem_alloc(size, KM_SLEEP); dest = (uintptr_t)buf; bcopy(&aggdesc, (void *)dest, sizeof (aggdesc)); dest += offsetof(dtrace_aggdesc_t, dtagd_rec[0]); for (act = agg->dtag_first; ; act = act->dta_next) { dtrace_recdesc_t rec = act->dta_rec; /* * See the comment in the above loop for why we pass * over zero-length records. */ if (rec.dtrd_size == 0) { ASSERT(agg->dtag_hasarg); continue; } if (nrecs-- == 0) break; rec.dtrd_offset -= offs; bcopy(&rec, (void *)dest, sizeof (rec)); dest += sizeof (dtrace_recdesc_t); if (act == &agg->dtag_action) break; } mutex_exit(&dtrace_lock); if (copyout(buf, (void *)arg, dest - (uintptr_t)buf) != 0) { kmem_free(buf, size); return (EFAULT); } kmem_free(buf, size); return (0); } case DTRACEIOC_ENABLE: { dof_hdr_t *dof; dtrace_enabling_t *enab = NULL; dtrace_vstate_t *vstate; int err = 0; *rv = 0; /* * If a NULL argument has been passed, we take this as our * cue to reevaluate our enablings. */ if (arg == NULL) { dtrace_enabling_matchall(); return (0); } if ((dof = dtrace_dof_copyin(arg, &rval)) == NULL) return (rval); mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); vstate = &state->dts_vstate; if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) { mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); dtrace_dof_destroy(dof); return (EBUSY); } if (dtrace_dof_slurp(dof, vstate, cr, &enab, 0, B_TRUE) != 0) { mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); dtrace_dof_destroy(dof); return (EINVAL); } if ((rval = dtrace_dof_options(dof, state)) != 0) { dtrace_enabling_destroy(enab); mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); dtrace_dof_destroy(dof); return (rval); } if ((err = dtrace_enabling_match(enab, rv)) == 0) { err = dtrace_enabling_retain(enab); } else { dtrace_enabling_destroy(enab); } mutex_exit(&cpu_lock); mutex_exit(&dtrace_lock); dtrace_dof_destroy(dof); return (err); } case DTRACEIOC_REPLICATE: { dtrace_repldesc_t desc; dtrace_probedesc_t *match = &desc.dtrpd_match; dtrace_probedesc_t *create = &desc.dtrpd_create; int err; if (copyin((void *)arg, &desc, sizeof (desc)) != 0) return (EFAULT); match->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0'; match->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0'; match->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0'; match->dtpd_name[DTRACE_NAMELEN - 1] = '\0'; create->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0'; create->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0'; create->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0'; create->dtpd_name[DTRACE_NAMELEN - 1] = '\0'; mutex_enter(&dtrace_lock); err = dtrace_enabling_replicate(state, match, create); mutex_exit(&dtrace_lock); return (err); } case DTRACEIOC_PROBEMATCH: case DTRACEIOC_PROBES: { dtrace_probe_t *probe = NULL; dtrace_probedesc_t desc; dtrace_probekey_t pkey; dtrace_id_t i; int m = 0; uint32_t priv; uid_t uid; zoneid_t zoneid; if (copyin((void *)arg, &desc, sizeof (desc)) != 0) return (EFAULT); desc.dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0'; desc.dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0'; desc.dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0'; desc.dtpd_name[DTRACE_NAMELEN - 1] = '\0'; /* * Before we attempt to match this probe, we want to give * all providers the opportunity to provide it. */ if (desc.dtpd_id == DTRACE_IDNONE) { mutex_enter(&dtrace_provider_lock); dtrace_probe_provide(&desc, NULL); mutex_exit(&dtrace_provider_lock); desc.dtpd_id++; } if (cmd == DTRACEIOC_PROBEMATCH) { dtrace_probekey(&desc, &pkey); pkey.dtpk_id = DTRACE_IDNONE; } dtrace_cred2priv(cr, &priv, &uid, &zoneid); mutex_enter(&dtrace_lock); if (cmd == DTRACEIOC_PROBEMATCH) { for (i = desc.dtpd_id; i <= dtrace_nprobes; i++) { if ((probe = dtrace_probes[i - 1]) != NULL && (m = dtrace_match_probe(probe, &pkey, priv, uid, zoneid)) != 0) break; } if (m < 0) { mutex_exit(&dtrace_lock); return (EINVAL); } } else { for (i = desc.dtpd_id; i <= dtrace_nprobes; i++) { if ((probe = dtrace_probes[i - 1]) != NULL && dtrace_match_priv(probe, priv, uid, zoneid)) break; } } if (probe == NULL) { mutex_exit(&dtrace_lock); return (ESRCH); } dtrace_probe_description(probe, &desc); mutex_exit(&dtrace_lock); if (copyout(&desc, (void *)arg, sizeof (desc)) != 0) return (EFAULT); return (0); } case DTRACEIOC_PROBEARG: { dtrace_argdesc_t desc; dtrace_probe_t *probe; dtrace_provider_t *prov; if (copyin((void *)arg, &desc, sizeof (desc)) != 0) return (EFAULT); if (desc.dtargd_id == DTRACE_IDNONE) return (EINVAL); if (desc.dtargd_ndx == DTRACE_ARGNONE) return (EINVAL); mutex_enter(&dtrace_provider_lock); mutex_enter(&mod_lock); mutex_enter(&dtrace_lock); if (desc.dtargd_id > dtrace_nprobes) { mutex_exit(&dtrace_lock); mutex_exit(&mod_lock); mutex_exit(&dtrace_provider_lock); return (EINVAL); } if ((probe = dtrace_probes[desc.dtargd_id - 1]) == NULL) { mutex_exit(&dtrace_lock); mutex_exit(&mod_lock); mutex_exit(&dtrace_provider_lock); return (EINVAL); } mutex_exit(&dtrace_lock); prov = probe->dtpr_provider; if (prov->dtpv_pops.dtps_getargdesc == NULL) { /* * There isn't any typed information for this probe. * Set the argument number to DTRACE_ARGNONE. */ desc.dtargd_ndx = DTRACE_ARGNONE; } else { desc.dtargd_native[0] = '\0'; desc.dtargd_xlate[0] = '\0'; desc.dtargd_mapping = desc.dtargd_ndx; prov->dtpv_pops.dtps_getargdesc(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg, &desc); } mutex_exit(&mod_lock); mutex_exit(&dtrace_provider_lock); if (copyout(&desc, (void *)arg, sizeof (desc)) != 0) return (EFAULT); return (0); } case DTRACEIOC_GO: { processorid_t cpuid; rval = dtrace_state_go(state, &cpuid); if (rval != 0) return (rval); if (copyout(&cpuid, (void *)arg, sizeof (cpuid)) != 0) return (EFAULT); return (0); } case DTRACEIOC_STOP: { processorid_t cpuid; mutex_enter(&dtrace_lock); rval = dtrace_state_stop(state, &cpuid); mutex_exit(&dtrace_lock); if (rval != 0) return (rval); if (copyout(&cpuid, (void *)arg, sizeof (cpuid)) != 0) return (EFAULT); return (0); } case DTRACEIOC_DOFGET: { dof_hdr_t hdr, *dof; uint64_t len; if (copyin((void *)arg, &hdr, sizeof (hdr)) != 0) return (EFAULT); mutex_enter(&dtrace_lock); dof = dtrace_dof_create(state); mutex_exit(&dtrace_lock); len = MIN(hdr.dofh_loadsz, dof->dofh_loadsz); rval = copyout(dof, (void *)arg, len); dtrace_dof_destroy(dof); return (rval == 0 ? 0 : EFAULT); } case DTRACEIOC_AGGSNAP: case DTRACEIOC_BUFSNAP: { dtrace_bufdesc_t desc; caddr_t cached; dtrace_buffer_t *buf; if (copyin((void *)arg, &desc, sizeof (desc)) != 0) return (EFAULT); if (desc.dtbd_cpu < 0 || desc.dtbd_cpu >= NCPU) return (EINVAL); mutex_enter(&dtrace_lock); if (cmd == DTRACEIOC_BUFSNAP) { buf = &state->dts_buffer[desc.dtbd_cpu]; } else { buf = &state->dts_aggbuffer[desc.dtbd_cpu]; } if (buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL)) { size_t sz = buf->dtb_offset; if (state->dts_activity != DTRACE_ACTIVITY_STOPPED) { mutex_exit(&dtrace_lock); return (EBUSY); } /* * If this buffer has already been consumed, we're * going to indicate that there's nothing left here * to consume. */ if (buf->dtb_flags & DTRACEBUF_CONSUMED) { mutex_exit(&dtrace_lock); desc.dtbd_size = 0; desc.dtbd_drops = 0; desc.dtbd_errors = 0; desc.dtbd_oldest = 0; sz = sizeof (desc); if (copyout(&desc, (void *)arg, sz) != 0) return (EFAULT); return (0); } /* * If this is a ring buffer that has wrapped, we want * to copy the whole thing out. */ if (buf->dtb_flags & DTRACEBUF_WRAPPED) { dtrace_buffer_polish(buf); sz = buf->dtb_size; } if (copyout(buf->dtb_tomax, desc.dtbd_data, sz) != 0) { mutex_exit(&dtrace_lock); return (EFAULT); } desc.dtbd_size = sz; desc.dtbd_drops = buf->dtb_drops; desc.dtbd_errors = buf->dtb_errors; desc.dtbd_oldest = buf->dtb_xamot_offset; desc.dtbd_timestamp = dtrace_gethrtime(); mutex_exit(&dtrace_lock); if (copyout(&desc, (void *)arg, sizeof (desc)) != 0) return (EFAULT); buf->dtb_flags |= DTRACEBUF_CONSUMED; return (0); } if (buf->dtb_tomax == NULL) { ASSERT(buf->dtb_xamot == NULL); mutex_exit(&dtrace_lock); return (ENOENT); } cached = buf->dtb_tomax; ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH)); dtrace_xcall(desc.dtbd_cpu, (dtrace_xcall_t)dtrace_buffer_switch, buf); state->dts_errors += buf->dtb_xamot_errors; /* * If the buffers did not actually switch, then the cross call * did not take place -- presumably because the given CPU is * not in the ready set. If this is the case, we'll return * ENOENT. */ if (buf->dtb_tomax == cached) { ASSERT(buf->dtb_xamot != cached); mutex_exit(&dtrace_lock); return (ENOENT); } ASSERT(cached == buf->dtb_xamot); /* * We have our snapshot; now copy it out. */ if (copyout(buf->dtb_xamot, desc.dtbd_data, buf->dtb_xamot_offset) != 0) { mutex_exit(&dtrace_lock); return (EFAULT); } desc.dtbd_size = buf->dtb_xamot_offset; desc.dtbd_drops = buf->dtb_xamot_drops; desc.dtbd_errors = buf->dtb_xamot_errors; desc.dtbd_oldest = 0; desc.dtbd_timestamp = buf->dtb_switched; mutex_exit(&dtrace_lock); /* * Finally, copy out the buffer description. */ if (copyout(&desc, (void *)arg, sizeof (desc)) != 0) return (EFAULT); return (0); } case DTRACEIOC_CONF: { dtrace_conf_t conf; bzero(&conf, sizeof (conf)); conf.dtc_difversion = DIF_VERSION; conf.dtc_difintregs = DIF_DIR_NREGS; conf.dtc_diftupregs = DIF_DTR_NREGS; conf.dtc_ctfmodel = CTF_MODEL_NATIVE; if (copyout(&conf, (void *)arg, sizeof (conf)) != 0) return (EFAULT); return (0); } case DTRACEIOC_STATUS: { dtrace_status_t stat; dtrace_dstate_t *dstate; int i, j; uint64_t nerrs; /* * See the comment in dtrace_state_deadman() for the reason * for setting dts_laststatus to INT64_MAX before setting * it to the correct value. */ state->dts_laststatus = INT64_MAX; dtrace_membar_producer(); state->dts_laststatus = dtrace_gethrtime(); bzero(&stat, sizeof (stat)); mutex_enter(&dtrace_lock); if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) { mutex_exit(&dtrace_lock); return (ENOENT); } if (state->dts_activity == DTRACE_ACTIVITY_DRAINING) stat.dtst_exiting = 1; nerrs = state->dts_errors; dstate = &state->dts_vstate.dtvs_dynvars; for (i = 0; i < NCPU; i++) { dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[i]; stat.dtst_dyndrops += dcpu->dtdsc_drops; stat.dtst_dyndrops_dirty += dcpu->dtdsc_dirty_drops; stat.dtst_dyndrops_rinsing += dcpu->dtdsc_rinsing_drops; if (state->dts_buffer[i].dtb_flags & DTRACEBUF_FULL) stat.dtst_filled++; nerrs += state->dts_buffer[i].dtb_errors; for (j = 0; j < state->dts_nspeculations; j++) { dtrace_speculation_t *spec; dtrace_buffer_t *buf; spec = &state->dts_speculations[j]; buf = &spec->dtsp_buffer[i]; stat.dtst_specdrops += buf->dtb_xamot_drops; } } stat.dtst_specdrops_busy = state->dts_speculations_busy; stat.dtst_specdrops_unavail = state->dts_speculations_unavail; stat.dtst_stkstroverflows = state->dts_stkstroverflows; stat.dtst_dblerrors = state->dts_dblerrors; stat.dtst_killed = (state->dts_activity == DTRACE_ACTIVITY_KILLED); stat.dtst_errors = nerrs; mutex_exit(&dtrace_lock); if (copyout(&stat, (void *)arg, sizeof (stat)) != 0) return (EFAULT); return (0); } case DTRACEIOC_FORMAT: { dtrace_fmtdesc_t fmt; char *str; int len; if (copyin((void *)arg, &fmt, sizeof (fmt)) != 0) return (EFAULT); mutex_enter(&dtrace_lock); if (fmt.dtfd_format == 0 || fmt.dtfd_format > state->dts_nformats) { mutex_exit(&dtrace_lock); return (EINVAL); } /* * Format strings are allocated contiguously and they are * never freed; if a format index is less than the number * of formats, we can assert that the format map is non-NULL * and that the format for the specified index is non-NULL. */ ASSERT(state->dts_formats != NULL); str = state->dts_formats[fmt.dtfd_format - 1]; ASSERT(str != NULL); len = strlen(str) + 1; if (len > fmt.dtfd_length) { fmt.dtfd_length = len; if (copyout(&fmt, (void *)arg, sizeof (fmt)) != 0) { mutex_exit(&dtrace_lock); return (EINVAL); } } else { if (copyout(str, fmt.dtfd_string, len) != 0) { mutex_exit(&dtrace_lock); return (EINVAL); } } mutex_exit(&dtrace_lock); return (0); } default: break; } return (ENOTTY); } /*ARGSUSED*/ static int dtrace_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { dtrace_state_t *state; switch (cmd) { case DDI_DETACH: break; case DDI_SUSPEND: return (DDI_SUCCESS); default: return (DDI_FAILURE); } mutex_enter(&cpu_lock); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); ASSERT(dtrace_opens == 0); if (dtrace_helpers > 0) { mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); return (DDI_FAILURE); } if (dtrace_unregister((dtrace_provider_id_t)dtrace_provider) != 0) { mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); return (DDI_FAILURE); } dtrace_provider = NULL; if ((state = dtrace_anon_grab()) != NULL) { /* * If there were ECBs on this state, the provider should * have not been allowed to detach; assert that there is * none. */ ASSERT(state->dts_necbs == 0); dtrace_state_destroy(state); /* * If we're being detached with anonymous state, we need to * indicate to the kernel debugger that DTrace is now inactive. */ (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE); } bzero(&dtrace_anon, sizeof (dtrace_anon_t)); unregister_cpu_setup_func((cpu_setup_func_t *)dtrace_cpu_setup, NULL); dtrace_cpu_init = NULL; dtrace_helpers_cleanup = NULL; dtrace_helpers_fork = NULL; dtrace_cpustart_init = NULL; dtrace_cpustart_fini = NULL; dtrace_debugger_init = NULL; dtrace_debugger_fini = NULL; dtrace_modload = NULL; dtrace_modunload = NULL; + + ASSERT(dtrace_getf == 0); + ASSERT(dtrace_closef == NULL); mutex_exit(&cpu_lock); if (dtrace_helptrace_enabled) { kmem_free(dtrace_helptrace_buffer, dtrace_helptrace_bufsize); dtrace_helptrace_buffer = NULL; } kmem_free(dtrace_probes, dtrace_nprobes * sizeof (dtrace_probe_t *)); dtrace_probes = NULL; dtrace_nprobes = 0; dtrace_hash_destroy(dtrace_bymod); dtrace_hash_destroy(dtrace_byfunc); dtrace_hash_destroy(dtrace_byname); dtrace_bymod = NULL; dtrace_byfunc = NULL; dtrace_byname = NULL; kmem_cache_destroy(dtrace_state_cache); vmem_destroy(dtrace_minor); vmem_destroy(dtrace_arena); if (dtrace_toxrange != NULL) { kmem_free(dtrace_toxrange, dtrace_toxranges_max * sizeof (dtrace_toxrange_t)); dtrace_toxrange = NULL; dtrace_toxranges = 0; dtrace_toxranges_max = 0; } ddi_remove_minor_node(dtrace_devi, NULL); dtrace_devi = NULL; ddi_soft_state_fini(&dtrace_softstate); ASSERT(dtrace_vtime_references == 0); ASSERT(dtrace_opens == 0); ASSERT(dtrace_retained == NULL); mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); /* * We don't destroy the task queue until after we have dropped our * locks (taskq_destroy() may block on running tasks). To prevent * attempting to do work after we have effectively detached but before * the task queue has been destroyed, all tasks dispatched via the * task queue must check that DTrace is still attached before * performing any operation. */ taskq_destroy(dtrace_taskq); dtrace_taskq = NULL; return (DDI_SUCCESS); } #endif #if defined(sun) /*ARGSUSED*/ static int dtrace_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { int error; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: *result = (void *)dtrace_devi; error = DDI_SUCCESS; break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)0; error = DDI_SUCCESS; break; default: error = DDI_FAILURE; } return (error); } #endif #if defined(sun) static struct cb_ops dtrace_cb_ops = { dtrace_open, /* open */ dtrace_close, /* close */ nulldev, /* strategy */ nulldev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ dtrace_ioctl, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* cb_prop_op */ 0, /* streamtab */ D_NEW | D_MP /* Driver compatibility flag */ }; static struct dev_ops dtrace_ops = { DEVO_REV, /* devo_rev */ 0, /* refcnt */ dtrace_info, /* get_dev_info */ nulldev, /* identify */ nulldev, /* probe */ dtrace_attach, /* attach */ dtrace_detach, /* detach */ nodev, /* reset */ &dtrace_cb_ops, /* driver operations */ NULL, /* bus operations */ nodev /* dev power */ }; static struct modldrv modldrv = { &mod_driverops, /* module type (this is a pseudo driver) */ "Dynamic Tracing", /* name of module */ &dtrace_ops, /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modldrv, NULL }; int _init(void) { return (mod_install(&modlinkage)); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } int _fini(void) { return (mod_remove(&modlinkage)); } #else static d_ioctl_t dtrace_ioctl; static d_ioctl_t dtrace_ioctl_helper; static void dtrace_load(void *); static int dtrace_unload(void); #if __FreeBSD_version < 800039 static void dtrace_clone(void *, struct ucred *, char *, int , struct cdev **); static struct clonedevs *dtrace_clones; /* Ptr to the array of cloned devices. */ static eventhandler_tag eh_tag; /* Event handler tag. */ #else static struct cdev *dtrace_dev; static struct cdev *helper_dev; #endif void dtrace_invop_init(void); void dtrace_invop_uninit(void); static struct cdevsw dtrace_cdevsw = { .d_version = D_VERSION, #if __FreeBSD_version < 800039 .d_flags = D_TRACKCLOSE | D_NEEDMINOR, .d_close = dtrace_close, #endif .d_ioctl = dtrace_ioctl, .d_open = dtrace_open, .d_name = "dtrace", }; static struct cdevsw helper_cdevsw = { .d_version = D_VERSION, .d_ioctl = dtrace_ioctl_helper, .d_name = "helper", }; #include #if __FreeBSD_version < 800039 #include #endif #include #include #include #include #include #include #include #include SYSINIT(dtrace_load, SI_SUB_DTRACE, SI_ORDER_FIRST, dtrace_load, NULL); SYSUNINIT(dtrace_unload, SI_SUB_DTRACE, SI_ORDER_FIRST, dtrace_unload, NULL); SYSINIT(dtrace_anon_init, SI_SUB_DTRACE_ANON, SI_ORDER_FIRST, dtrace_anon_init, NULL); DEV_MODULE(dtrace, dtrace_modevent, NULL); MODULE_VERSION(dtrace, 1); MODULE_DEPEND(dtrace, cyclic, 1, 1, 1); MODULE_DEPEND(dtrace, opensolaris, 1, 1, 1); #endif Index: stable/10/sys/cddl/contrib/opensolaris/uts/common/dtrace/sdt_subr.c =================================================================== --- stable/10/sys/cddl/contrib/opensolaris/uts/common/dtrace/sdt_subr.c (revision 268577) +++ stable/10/sys/cddl/contrib/opensolaris/uts/common/dtrace/sdt_subr.c (revision 268578) @@ -1,888 +1,903 @@ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" #include static dtrace_pattr_t vtrace_attr = { { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_ISA }, }; static dtrace_pattr_t info_attr = { { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, }; static dtrace_pattr_t fpu_attr = { { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_CPU }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, }; static dtrace_pattr_t fsinfo_attr = { { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, }; static dtrace_pattr_t stab_attr = { { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, }; static dtrace_pattr_t sdt_attr = { { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, }; static dtrace_pattr_t xpv_attr = { { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_PLATFORM }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_PLATFORM }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_PLATFORM }, }; sdt_provider_t sdt_providers[] = { { "vtrace", "__vtrace_", &vtrace_attr, 0 }, - { "sysinfo", "__cpu_sysinfo_", &info_attr, 0 }, - { "vminfo", "__cpu_vminfo_", &info_attr, 0 }, + { "sysinfo", "__cpu_sysinfo_", &info_attr, DTRACE_PRIV_USER }, + { "vminfo", "__cpu_vminfo_", &info_attr, DTRACE_PRIV_USER }, { "fpuinfo", "__fpuinfo_", &fpu_attr, 0 }, - { "sched", "__sched_", &stab_attr, 0 }, - { "proc", "__proc_", &stab_attr, 0 }, + { "sched", "__sched_", &stab_attr, DTRACE_PRIV_USER }, + { "proc", "__proc_", &stab_attr, DTRACE_PRIV_USER }, { "io", "__io_", &stab_attr, 0 }, { "mib", "__mib_", &stab_attr, 0 }, { "fsinfo", "__fsinfo_", &fsinfo_attr, 0 }, { "nfsv3", "__nfsv3_", &stab_attr, 0 }, { "nfsv4", "__nfsv4_", &stab_attr, 0 }, { "xpv", "__xpv_", &xpv_attr, 0 }, { "sysevent", "__sysevent_", &stab_attr, 0 }, { "sdt", NULL, &sdt_attr, 0 }, { NULL } }; sdt_argdesc_t sdt_args[] = { { "sched", "wakeup", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "wakeup", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "dequeue", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "dequeue", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "dequeue", 2, 1, "disp_t *", "cpuinfo_t *" }, { "sched", "enqueue", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "enqueue", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "enqueue", 2, 1, "disp_t *", "cpuinfo_t *" }, { "sched", "enqueue", 3, 2, "int" }, { "sched", "off-cpu", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "off-cpu", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "tick", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "tick", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "change-pri", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "change-pri", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "change-pri", 2, 1, "pri_t" }, { "sched", "schedctl-nopreempt", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "schedctl-nopreempt", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "schedctl-nopreempt", 2, 1, "int" }, { "sched", "schedctl-preempt", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "schedctl-preempt", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "schedctl-yield", 0, 0, "int" }, { "sched", "surrender", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "surrender", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "cpucaps-sleep", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "cpucaps-sleep", 1, 0, "kthread_t *", "psinfo_t *" }, { "sched", "cpucaps-wakeup", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "sched", "cpucaps-wakeup", 1, 0, "kthread_t *", "psinfo_t *" }, { "proc", "create", 0, 0, "proc_t *", "psinfo_t *" }, { "proc", "exec", 0, 0, "string" }, { "proc", "exec-failure", 0, 0, "int" }, { "proc", "exit", 0, 0, "int" }, { "proc", "fault", 0, 0, "int" }, { "proc", "fault", 1, 1, "siginfo_t *" }, { "proc", "lwp-create", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "proc", "lwp-create", 1, 0, "kthread_t *", "psinfo_t *" }, { "proc", "signal-clear", 0, 0, "int" }, { "proc", "signal-clear", 1, 1, "siginfo_t *" }, { "proc", "signal-discard", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "proc", "signal-discard", 1, 1, "proc_t *", "psinfo_t *" }, { "proc", "signal-discard", 2, 2, "int" }, { "proc", "signal-handle", 0, 0, "int" }, { "proc", "signal-handle", 1, 1, "siginfo_t *" }, { "proc", "signal-handle", 2, 2, "void (*)(void)" }, { "proc", "signal-send", 0, 0, "kthread_t *", "lwpsinfo_t *" }, { "proc", "signal-send", 1, 0, "kthread_t *", "psinfo_t *" }, { "proc", "signal-send", 2, 1, "int" }, { "io", "start", 0, 0, "buf_t *", "bufinfo_t *" }, { "io", "start", 1, 0, "buf_t *", "devinfo_t *" }, { "io", "start", 2, 0, "buf_t *", "fileinfo_t *" }, { "io", "done", 0, 0, "buf_t *", "bufinfo_t *" }, { "io", "done", 1, 0, "buf_t *", "devinfo_t *" }, { "io", "done", 2, 0, "buf_t *", "fileinfo_t *" }, { "io", "wait-start", 0, 0, "buf_t *", "bufinfo_t *" }, { "io", "wait-start", 1, 0, "buf_t *", "devinfo_t *" }, { "io", "wait-start", 2, 0, "buf_t *", "fileinfo_t *" }, { "io", "wait-done", 0, 0, "buf_t *", "bufinfo_t *" }, { "io", "wait-done", 1, 0, "buf_t *", "devinfo_t *" }, { "io", "wait-done", 2, 0, "buf_t *", "fileinfo_t *" }, { "mib", NULL, 0, 0, "int" }, { "fsinfo", NULL, 0, 0, "vnode_t *", "fileinfo_t *" }, { "fsinfo", NULL, 1, 1, "int", "int" }, { "nfsv3", "op-getattr-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-getattr-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-getattr-start", 2, 3, "GETATTR3args *" }, { "nfsv3", "op-getattr-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-getattr-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-getattr-done", 2, 3, "GETATTR3res *" }, { "nfsv3", "op-setattr-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-setattr-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-setattr-start", 2, 3, "SETATTR3args *" }, { "nfsv3", "op-setattr-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-setattr-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-setattr-done", 2, 3, "SETATTR3res *" }, { "nfsv3", "op-lookup-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-lookup-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-lookup-start", 2, 3, "LOOKUP3args *" }, { "nfsv3", "op-lookup-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-lookup-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-lookup-done", 2, 3, "LOOKUP3res *" }, { "nfsv3", "op-access-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-access-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-access-start", 2, 3, "ACCESS3args *" }, { "nfsv3", "op-access-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-access-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-access-done", 2, 3, "ACCESS3res *" }, { "nfsv3", "op-commit-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-commit-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-commit-start", 2, 3, "COMMIT3args *" }, { "nfsv3", "op-commit-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-commit-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-commit-done", 2, 3, "COMMIT3res *" }, { "nfsv3", "op-create-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-create-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-create-start", 2, 3, "CREATE3args *" }, { "nfsv3", "op-create-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-create-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-create-done", 2, 3, "CREATE3res *" }, { "nfsv3", "op-fsinfo-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-fsinfo-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-fsinfo-start", 2, 3, "FSINFO3args *" }, { "nfsv3", "op-fsinfo-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-fsinfo-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-fsinfo-done", 2, 3, "FSINFO3res *" }, { "nfsv3", "op-fsstat-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-fsstat-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-fsstat-start", 2, 3, "FSSTAT3args *" }, { "nfsv3", "op-fsstat-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-fsstat-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-fsstat-done", 2, 3, "FSSTAT3res *" }, { "nfsv3", "op-link-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-link-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-link-start", 2, 3, "LINK3args *" }, { "nfsv3", "op-link-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-link-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-link-done", 2, 3, "LINK3res *" }, { "nfsv3", "op-mkdir-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-mkdir-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-mkdir-start", 2, 3, "MKDIR3args *" }, { "nfsv3", "op-mkdir-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-mkdir-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-mkdir-done", 2, 3, "MKDIR3res *" }, { "nfsv3", "op-mknod-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-mknod-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-mknod-start", 2, 3, "MKNOD3args *" }, { "nfsv3", "op-mknod-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-mknod-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-mknod-done", 2, 3, "MKNOD3res *" }, { "nfsv3", "op-null-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-null-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-null-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-null-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-pathconf-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-pathconf-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-pathconf-start", 2, 3, "PATHCONF3args *" }, { "nfsv3", "op-pathconf-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-pathconf-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-pathconf-done", 2, 3, "PATHCONF3res *" }, { "nfsv3", "op-read-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-read-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-read-start", 2, 3, "READ3args *" }, { "nfsv3", "op-read-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-read-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-read-done", 2, 3, "READ3res *" }, { "nfsv3", "op-readdir-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-readdir-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-readdir-start", 2, 3, "READDIR3args *" }, { "nfsv3", "op-readdir-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-readdir-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-readdir-done", 2, 3, "READDIR3res *" }, { "nfsv3", "op-readdirplus-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-readdirplus-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-readdirplus-start", 2, 3, "READDIRPLUS3args *" }, { "nfsv3", "op-readdirplus-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-readdirplus-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-readdirplus-done", 2, 3, "READDIRPLUS3res *" }, { "nfsv3", "op-readlink-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-readlink-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-readlink-start", 2, 3, "READLINK3args *" }, { "nfsv3", "op-readlink-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-readlink-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-readlink-done", 2, 3, "READLINK3res *" }, { "nfsv3", "op-remove-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-remove-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-remove-start", 2, 3, "REMOVE3args *" }, { "nfsv3", "op-remove-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-remove-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-remove-done", 2, 3, "REMOVE3res *" }, { "nfsv3", "op-rename-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-rename-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-rename-start", 2, 3, "RENAME3args *" }, { "nfsv3", "op-rename-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-rename-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-rename-done", 2, 3, "RENAME3res *" }, { "nfsv3", "op-rmdir-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-rmdir-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-rmdir-start", 2, 3, "RMDIR3args *" }, { "nfsv3", "op-rmdir-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-rmdir-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-rmdir-done", 2, 3, "RMDIR3res *" }, { "nfsv3", "op-setattr-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-setattr-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-setattr-start", 2, 3, "SETATTR3args *" }, { "nfsv3", "op-setattr-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-setattr-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-setattr-done", 2, 3, "SETATTR3res *" }, { "nfsv3", "op-symlink-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-symlink-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-symlink-start", 2, 3, "SYMLINK3args *" }, { "nfsv3", "op-symlink-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-symlink-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-symlink-done", 2, 3, "SYMLINK3res *" }, { "nfsv3", "op-write-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-write-start", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-write-start", 2, 3, "WRITE3args *" }, { "nfsv3", "op-write-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv3", "op-write-done", 1, 1, "nfsv3oparg_t *", "nfsv3opinfo_t *" }, { "nfsv3", "op-write-done", 2, 3, "WRITE3res *" }, { "nfsv4", "null-start", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv4", "null-done", 0, 0, "struct svc_req *", "conninfo_t *" }, { "nfsv4", "compound-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "compound-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "compound-start", 2, 1, "COMPOUND4args *" }, { "nfsv4", "compound-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "compound-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "compound-done", 2, 1, "COMPOUND4res *" }, { "nfsv4", "op-access-start", 0, 0, "struct compound_state *", "conninfo_t *"}, { "nfsv4", "op-access-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-access-start", 2, 1, "ACCESS4args *" }, { "nfsv4", "op-access-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-access-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-access-done", 2, 1, "ACCESS4res *" }, { "nfsv4", "op-close-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-close-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-close-start", 2, 1, "CLOSE4args *" }, { "nfsv4", "op-close-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-close-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-close-done", 2, 1, "CLOSE4res *" }, { "nfsv4", "op-commit-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-commit-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-commit-start", 2, 1, "COMMIT4args *" }, { "nfsv4", "op-commit-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-commit-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-commit-done", 2, 1, "COMMIT4res *" }, { "nfsv4", "op-create-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-create-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-create-start", 2, 1, "CREATE4args *" }, { "nfsv4", "op-create-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-create-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-create-done", 2, 1, "CREATE4res *" }, { "nfsv4", "op-delegpurge-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-delegpurge-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-delegpurge-start", 2, 1, "DELEGPURGE4args *" }, { "nfsv4", "op-delegpurge-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-delegpurge-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-delegpurge-done", 2, 1, "DELEGPURGE4res *" }, { "nfsv4", "op-delegreturn-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-delegreturn-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-delegreturn-start", 2, 1, "DELEGRETURN4args *" }, { "nfsv4", "op-delegreturn-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-delegreturn-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-delegreturn-done", 2, 1, "DELEGRETURN4res *" }, { "nfsv4", "op-getattr-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-getattr-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-getattr-start", 2, 1, "GETATTR4args *" }, { "nfsv4", "op-getattr-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-getattr-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-getattr-done", 2, 1, "GETATTR4res *" }, { "nfsv4", "op-getfh-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-getfh-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-getfh-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-getfh-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-getfh-done", 2, 1, "GETFH4res *" }, { "nfsv4", "op-link-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-link-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-link-start", 2, 1, "LINK4args *" }, { "nfsv4", "op-link-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-link-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-link-done", 2, 1, "LINK4res *" }, { "nfsv4", "op-lock-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-lock-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-lock-start", 2, 1, "LOCK4args *" }, { "nfsv4", "op-lock-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-lock-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-lock-done", 2, 1, "LOCK4res *" }, { "nfsv4", "op-lockt-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-lockt-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-lockt-start", 2, 1, "LOCKT4args *" }, { "nfsv4", "op-lockt-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-lockt-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-lockt-done", 2, 1, "LOCKT4res *" }, { "nfsv4", "op-locku-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-locku-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-locku-start", 2, 1, "LOCKU4args *" }, { "nfsv4", "op-locku-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-locku-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-locku-done", 2, 1, "LOCKU4res *" }, { "nfsv4", "op-lookup-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-lookup-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-lookup-start", 2, 1, "LOOKUP4args *" }, { "nfsv4", "op-lookup-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-lookup-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-lookup-done", 2, 1, "LOOKUP4res *" }, { "nfsv4", "op-lookupp-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-lookupp-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-lookupp-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-lookupp-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-lookupp-done", 2, 1, "LOOKUPP4res *" }, { "nfsv4", "op-nverify-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-nverify-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-nverify-start", 2, 1, "NVERIFY4args *" }, { "nfsv4", "op-nverify-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-nverify-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-nverify-done", 2, 1, "NVERIFY4res *" }, { "nfsv4", "op-open-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-open-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-open-start", 2, 1, "OPEN4args *" }, { "nfsv4", "op-open-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-open-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-open-done", 2, 1, "OPEN4res *" }, { "nfsv4", "op-open-confirm-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-open-confirm-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-open-confirm-start", 2, 1, "OPEN_CONFIRM4args *" }, { "nfsv4", "op-open-confirm-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-open-confirm-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-open-confirm-done", 2, 1, "OPEN_CONFIRM4res *" }, { "nfsv4", "op-open-downgrade-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-open-downgrade-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-open-downgrade-start", 2, 1, "OPEN_DOWNGRADE4args *" }, { "nfsv4", "op-open-downgrade-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-open-downgrade-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-open-downgrade-done", 2, 1, "OPEN_DOWNGRADE4res *" }, { "nfsv4", "op-openattr-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-openattr-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-openattr-start", 2, 1, "OPENATTR4args *" }, { "nfsv4", "op-openattr-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-openattr-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-openattr-done", 2, 1, "OPENATTR4res *" }, { "nfsv4", "op-putfh-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-putfh-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-putfh-start", 2, 1, "PUTFH4args *" }, { "nfsv4", "op-putfh-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-putfh-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-putfh-done", 2, 1, "PUTFH4res *" }, { "nfsv4", "op-putpubfh-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-putpubfh-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-putpubfh-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-putpubfh-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-putpubfh-done", 2, 1, "PUTPUBFH4res *" }, { "nfsv4", "op-putrootfh-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-putrootfh-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-putrootfh-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-putrootfh-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-putrootfh-done", 2, 1, "PUTROOTFH4res *" }, { "nfsv4", "op-read-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-read-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-read-start", 2, 1, "READ4args *" }, { "nfsv4", "op-read-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-read-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-read-done", 2, 1, "READ4res *" }, { "nfsv4", "op-readdir-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-readdir-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-readdir-start", 2, 1, "READDIR4args *" }, { "nfsv4", "op-readdir-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-readdir-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-readdir-done", 2, 1, "READDIR4res *" }, { "nfsv4", "op-readlink-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-readlink-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-readlink-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-readlink-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-readlink-done", 2, 1, "READLINK4res *" }, { "nfsv4", "op-release-lockowner-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-release-lockowner-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-release-lockowner-start", 2, 1, "RELEASE_LOCKOWNER4args *" }, { "nfsv4", "op-release-lockowner-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-release-lockowner-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-release-lockowner-done", 2, 1, "RELEASE_LOCKOWNER4res *" }, { "nfsv4", "op-remove-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-remove-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-remove-start", 2, 1, "REMOVE4args *" }, { "nfsv4", "op-remove-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-remove-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-remove-done", 2, 1, "REMOVE4res *" }, { "nfsv4", "op-rename-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-rename-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-rename-start", 2, 1, "RENAME4args *" }, { "nfsv4", "op-rename-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-rename-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-rename-done", 2, 1, "RENAME4res *" }, { "nfsv4", "op-renew-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-renew-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-renew-start", 2, 1, "RENEW4args *" }, { "nfsv4", "op-renew-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-renew-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-renew-done", 2, 1, "RENEW4res *" }, { "nfsv4", "op-restorefh-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-restorefh-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-restorefh-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-restorefh-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-restorefh-done", 2, 1, "RESTOREFH4res *" }, { "nfsv4", "op-savefh-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-savefh-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-savefh-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-savefh-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-savefh-done", 2, 1, "SAVEFH4res *" }, { "nfsv4", "op-secinfo-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-secinfo-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-secinfo-start", 2, 1, "SECINFO4args *" }, { "nfsv4", "op-secinfo-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-secinfo-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-secinfo-done", 2, 1, "SECINFO4res *" }, { "nfsv4", "op-setattr-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-setattr-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-setattr-start", 2, 1, "SETATTR4args *" }, { "nfsv4", "op-setattr-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-setattr-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-setattr-done", 2, 1, "SETATTR4res *" }, { "nfsv4", "op-setclientid-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-setclientid-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-setclientid-start", 2, 1, "SETCLIENTID4args *" }, { "nfsv4", "op-setclientid-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-setclientid-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-setclientid-done", 2, 1, "SETCLIENTID4res *" }, { "nfsv4", "op-setclientid-confirm-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-setclientid-confirm-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-setclientid-confirm-start", 2, 1, "SETCLIENTID_CONFIRM4args *" }, { "nfsv4", "op-setclientid-confirm-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-setclientid-confirm-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-setclientid-confirm-done", 2, 1, "SETCLIENTID_CONFIRM4res *" }, { "nfsv4", "op-verify-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-verify-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-verify-start", 2, 1, "VERIFY4args *" }, { "nfsv4", "op-verify-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-verify-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-verify-done", 2, 1, "VERIFY4res *" }, { "nfsv4", "op-write-start", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-write-start", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-write-start", 2, 1, "WRITE4args *" }, { "nfsv4", "op-write-done", 0, 0, "struct compound_state *", "conninfo_t *" }, { "nfsv4", "op-write-done", 1, 0, "struct compound_state *", "nfsv4opinfo_t *" }, { "nfsv4", "op-write-done", 2, 1, "WRITE4res *" }, { "nfsv4", "cb-recall-start", 0, 0, "rfs4_client_t *", "conninfo_t *" }, { "nfsv4", "cb-recall-start", 1, 1, "rfs4_deleg_state_t *", "nfsv4cbinfo_t *" }, { "nfsv4", "cb-recall-start", 2, 2, "CB_RECALL4args *" }, { "nfsv4", "cb-recall-done", 0, 0, "rfs4_client_t *", "conninfo_t *" }, { "nfsv4", "cb-recall-done", 1, 1, "rfs4_deleg_state_t *", "nfsv4cbinfo_t *" }, { "nfsv4", "cb-recall-done", 2, 2, "CB_RECALL4res *" }, { "sysevent", "post", 0, 0, "evch_bind_t *", "syseventchaninfo_t *" }, { "sysevent", "post", 1, 1, "sysevent_impl_t *", "syseventinfo_t *" }, { "xpv", "add-to-physmap-end", 0, 0, "int" }, { "xpv", "add-to-physmap-start", 0, 0, "domid_t" }, { "xpv", "add-to-physmap-start", 1, 1, "uint_t" }, { "xpv", "add-to-physmap-start", 2, 2, "ulong_t" }, { "xpv", "add-to-physmap-start", 3, 3, "ulong_t" }, { "xpv", "decrease-reservation-end", 0, 0, "int" }, { "xpv", "decrease-reservation-start", 0, 0, "domid_t" }, { "xpv", "decrease-reservation-start", 1, 1, "ulong_t" }, { "xpv", "decrease-reservation-start", 2, 2, "uint_t" }, { "xpv", "decrease-reservation-start", 3, 3, "ulong_t *" }, { "xpv", "dom-create-start", 0, 0, "xen_domctl_t *" }, { "xpv", "dom-destroy-start", 0, 0, "domid_t" }, { "xpv", "dom-pause-start", 0, 0, "domid_t" }, { "xpv", "dom-unpause-start", 0, 0, "domid_t" }, { "xpv", "dom-create-end", 0, 0, "int" }, { "xpv", "dom-destroy-end", 0, 0, "int" }, { "xpv", "dom-pause-end", 0, 0, "int" }, { "xpv", "dom-unpause-end", 0, 0, "int" }, { "xpv", "evtchn-op-end", 0, 0, "int" }, { "xpv", "evtchn-op-start", 0, 0, "int" }, { "xpv", "evtchn-op-start", 1, 1, "void *" }, { "xpv", "increase-reservation-end", 0, 0, "int" }, { "xpv", "increase-reservation-start", 0, 0, "domid_t" }, { "xpv", "increase-reservation-start", 1, 1, "ulong_t" }, { "xpv", "increase-reservation-start", 2, 2, "uint_t" }, { "xpv", "increase-reservation-start", 3, 3, "ulong_t *" }, { "xpv", "mmap-end", 0, 0, "int" }, { "xpv", "mmap-entry", 0, 0, "ulong_t" }, { "xpv", "mmap-entry", 1, 1, "ulong_t" }, { "xpv", "mmap-entry", 2, 2, "ulong_t" }, { "xpv", "mmap-start", 0, 0, "domid_t" }, { "xpv", "mmap-start", 1, 1, "int" }, { "xpv", "mmap-start", 2, 2, "privcmd_mmap_entry_t *" }, { "xpv", "mmapbatch-end", 0, 0, "int" }, { "xpv", "mmapbatch-end", 1, 1, "struct seg *" }, { "xpv", "mmapbatch-end", 2, 2, "caddr_t" }, { "xpv", "mmapbatch-start", 0, 0, "domid_t" }, { "xpv", "mmapbatch-start", 1, 1, "int" }, { "xpv", "mmapbatch-start", 2, 2, "caddr_t" }, { "xpv", "mmu-ext-op-end", 0, 0, "int" }, { "xpv", "mmu-ext-op-start", 0, 0, "int" }, { "xpv", "mmu-ext-op-start", 1, 1, "struct mmuext_op *" }, { "xpv", "mmu-update-start", 0, 0, "int" }, { "xpv", "mmu-update-start", 1, 1, "int" }, { "xpv", "mmu-update-start", 2, 2, "mmu_update_t *" }, { "xpv", "mmu-update-end", 0, 0, "int" }, { "xpv", "populate-physmap-end", 0, 0, "int" }, { "xpv", "populate-physmap-start", 0, 0, "domid_t" }, { "xpv", "populate-physmap-start", 1, 1, "ulong_t" }, { "xpv", "populate-physmap-start", 2, 2, "ulong_t *" }, { "xpv", "set-memory-map-end", 0, 0, "int" }, { "xpv", "set-memory-map-start", 0, 0, "domid_t" }, { "xpv", "set-memory-map-start", 1, 1, "int" }, { "xpv", "set-memory-map-start", 2, 2, "struct xen_memory_map *" }, { "xpv", "setvcpucontext-end", 0, 0, "int" }, { "xpv", "setvcpucontext-start", 0, 0, "domid_t" }, { "xpv", "setvcpucontext-start", 1, 1, "vcpu_guest_context_t *" }, { NULL } }; + +/*ARGSUSED*/ +int +sdt_mode(void *arg, dtrace_id_t id, void *parg) +{ + /* + * We tell DTrace that we're in kernel mode, that the firing needs to + * be dropped for anything that doesn't have necessary privileges, and + * that it needs to be restricted for anything that has restricted + * (i.e., not all-zone) privileges. + */ + return (DTRACE_MODE_KERNEL | DTRACE_MODE_NOPRIV_DROP | + DTRACE_MODE_LIMITEDPRIV_RESTRICT); +} /*ARGSUSED*/ void sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc) { sdt_probe_t *sdp = parg; int i; desc->dtargd_native[0] = '\0'; desc->dtargd_xlate[0] = '\0'; for (i = 0; sdt_args[i].sda_provider != NULL; i++) { sdt_argdesc_t *a = &sdt_args[i]; if (strcmp(sdp->sdp_provider->sdtp_name, a->sda_provider) != 0) continue; if (a->sda_name != NULL && strcmp(sdp->sdp_name, a->sda_name) != 0) continue; if (desc->dtargd_ndx != a->sda_ndx) continue; if (a->sda_native != NULL) (void) strcpy(desc->dtargd_native, a->sda_native); if (a->sda_xlate != NULL) (void) strcpy(desc->dtargd_xlate, a->sda_xlate); desc->dtargd_mapping = a->sda_mapping; return; } desc->dtargd_ndx = DTRACE_ARGNONE; } Index: stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/ctf_api.h =================================================================== --- stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/ctf_api.h (revision 268577) +++ stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/ctf_api.h (revision 268578) @@ -1,248 +1,251 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* * This header file defines the interfaces available from the CTF debugger * library, libctf, and an equivalent kernel module. This API can be used by * a debugger to operate on data in the Compact ANSI-C Type Format (CTF). * This is NOT a public interface, although it may eventually become one in * the fullness of time after we gain more experience with the interfaces. * * In the meantime, be aware that any program linked with this API in this * release of Solaris is almost guaranteed to break in the next release. * * In short, do not user this header file or the CTF routines for any purpose. */ #ifndef _CTF_API_H #define _CTF_API_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * Clients can open one or more CTF containers and obtain a pointer to an * opaque ctf_file_t. Types are identified by an opaque ctf_id_t token. * These opaque definitions allow libctf to evolve without breaking clients. */ typedef struct ctf_file ctf_file_t; typedef long ctf_id_t; /* * If the debugger needs to provide the CTF library with a set of raw buffers * for use as the CTF data, symbol table, and string table, it can do so by * filling in ctf_sect_t structures and passing them to ctf_bufopen(): */ typedef struct ctf_sect { const char *cts_name; /* section name (if any) */ ulong_t cts_type; /* section type (ELF SHT_... value) */ ulong_t cts_flags; /* section flags (ELF SHF_... value) */ #if defined(sun) const void *cts_data; /* pointer to section data */ #else void *cts_data; /* pointer to section data */ #endif size_t cts_size; /* size of data in bytes */ size_t cts_entsize; /* size of each section entry (symtab only) */ off64_t cts_offset; /* file offset of this section (if any) */ } ctf_sect_t; /* * Encoding information for integers, floating-point values, and certain other * intrinsics can be obtained by calling ctf_type_encoding(), below. The flags * field will contain values appropriate for the type defined in . */ typedef struct ctf_encoding { uint_t cte_format; /* data format (CTF_INT_* or CTF_FP_* flags) */ uint_t cte_offset; /* offset of value in bits */ uint_t cte_bits; /* size of storage in bits */ } ctf_encoding_t; typedef struct ctf_membinfo { ctf_id_t ctm_type; /* type of struct or union member */ ulong_t ctm_offset; /* offset of member in bits */ } ctf_membinfo_t; typedef struct ctf_arinfo { ctf_id_t ctr_contents; /* type of array contents */ ctf_id_t ctr_index; /* type of array index */ uint_t ctr_nelems; /* number of elements */ } ctf_arinfo_t; typedef struct ctf_funcinfo { ctf_id_t ctc_return; /* function return type */ uint_t ctc_argc; /* number of typed arguments to function */ uint_t ctc_flags; /* function attributes (see below) */ } ctf_funcinfo_t; typedef struct ctf_lblinfo { ctf_id_t ctb_typeidx; /* last type associated with the label */ } ctf_lblinfo_t; #define CTF_FUNC_VARARG 0x1 /* function arguments end with varargs */ /* * Functions that return integer status or a ctf_id_t use the following value * to indicate failure. ctf_errno() can be used to obtain an error code. */ #define CTF_ERR (-1L) /* * The CTF data model is inferred to be the caller's data model or the data * model of the given object, unless ctf_setmodel() is explicitly called. */ #define CTF_MODEL_ILP32 1 /* object data model is ILP32 */ #define CTF_MODEL_LP64 2 /* object data model is LP64 */ #ifdef _LP64 #define CTF_MODEL_NATIVE CTF_MODEL_LP64 #else #define CTF_MODEL_NATIVE CTF_MODEL_ILP32 #endif /* * Dynamic CTF containers can be created using ctf_create(). The ctf_add_* * routines can be used to add new definitions to the dynamic container. * New types are labeled as root or non-root to determine whether they are * visible at the top-level program scope when subsequently doing a lookup. */ #define CTF_ADD_NONROOT 0 /* type only visible in nested scope */ #define CTF_ADD_ROOT 1 /* type visible at top-level scope */ /* * These typedefs are used to define the signature for callback functions * that can be used with the iteration and visit functions below: */ typedef int ctf_visit_f(const char *, ctf_id_t, ulong_t, int, void *); typedef int ctf_member_f(const char *, ctf_id_t, ulong_t, void *); typedef int ctf_enum_f(const char *, int, void *); typedef int ctf_type_f(ctf_id_t, void *); typedef int ctf_label_f(const char *, const ctf_lblinfo_t *, void *); extern ctf_file_t *ctf_bufopen(const ctf_sect_t *, const ctf_sect_t *, const ctf_sect_t *, int *); extern ctf_file_t *ctf_fdopen(int, int *); extern ctf_file_t *ctf_open(const char *, int *); extern ctf_file_t *ctf_create(int *); +extern ctf_file_t *ctf_dup(ctf_file_t *); extern void ctf_close(ctf_file_t *); extern ctf_file_t *ctf_parent_file(ctf_file_t *); extern const char *ctf_parent_name(ctf_file_t *); extern int ctf_import(ctf_file_t *, ctf_file_t *); extern int ctf_setmodel(ctf_file_t *, int); extern int ctf_getmodel(ctf_file_t *); extern void ctf_setspecific(ctf_file_t *, void *); extern void *ctf_getspecific(ctf_file_t *); extern int ctf_errno(ctf_file_t *); extern const char *ctf_errmsg(int); extern int ctf_version(int); extern int ctf_func_info(ctf_file_t *, ulong_t, ctf_funcinfo_t *); extern int ctf_func_args(ctf_file_t *, ulong_t, uint_t, ctf_id_t *); extern ctf_id_t ctf_lookup_by_name(ctf_file_t *, const char *); extern ctf_id_t ctf_lookup_by_symbol(ctf_file_t *, ulong_t); extern ctf_id_t ctf_type_resolve(ctf_file_t *, ctf_id_t); extern ssize_t ctf_type_lname(ctf_file_t *, ctf_id_t, char *, size_t); extern char *ctf_type_name(ctf_file_t *, ctf_id_t, char *, size_t); +extern char *ctf_type_qname(ctf_file_t *, ctf_id_t, char *, size_t, + const char *); extern ssize_t ctf_type_size(ctf_file_t *, ctf_id_t); extern ssize_t ctf_type_align(ctf_file_t *, ctf_id_t); extern int ctf_type_kind(ctf_file_t *, ctf_id_t); extern ctf_id_t ctf_type_reference(ctf_file_t *, ctf_id_t); extern ctf_id_t ctf_type_pointer(ctf_file_t *, ctf_id_t); extern int ctf_type_encoding(ctf_file_t *, ctf_id_t, ctf_encoding_t *); extern int ctf_type_visit(ctf_file_t *, ctf_id_t, ctf_visit_f *, void *); extern int ctf_type_cmp(ctf_file_t *, ctf_id_t, ctf_file_t *, ctf_id_t); extern int ctf_type_compat(ctf_file_t *, ctf_id_t, ctf_file_t *, ctf_id_t); extern int ctf_member_info(ctf_file_t *, ctf_id_t, const char *, ctf_membinfo_t *); extern int ctf_array_info(ctf_file_t *, ctf_id_t, ctf_arinfo_t *); extern const char *ctf_enum_name(ctf_file_t *, ctf_id_t, int); extern int ctf_enum_value(ctf_file_t *, ctf_id_t, const char *, int *); extern const char *ctf_label_topmost(ctf_file_t *); extern int ctf_label_info(ctf_file_t *, const char *, ctf_lblinfo_t *); extern int ctf_member_iter(ctf_file_t *, ctf_id_t, ctf_member_f *, void *); extern int ctf_enum_iter(ctf_file_t *, ctf_id_t, ctf_enum_f *, void *); extern int ctf_type_iter(ctf_file_t *, ctf_type_f *, void *); extern int ctf_label_iter(ctf_file_t *, ctf_label_f *, void *); extern ctf_id_t ctf_add_array(ctf_file_t *, uint_t, const ctf_arinfo_t *); extern ctf_id_t ctf_add_const(ctf_file_t *, uint_t, ctf_id_t); extern ctf_id_t ctf_add_enum(ctf_file_t *, uint_t, const char *); extern ctf_id_t ctf_add_float(ctf_file_t *, uint_t, const char *, const ctf_encoding_t *); extern ctf_id_t ctf_add_forward(ctf_file_t *, uint_t, const char *, uint_t); extern ctf_id_t ctf_add_function(ctf_file_t *, uint_t, const ctf_funcinfo_t *, const ctf_id_t *); extern ctf_id_t ctf_add_integer(ctf_file_t *, uint_t, const char *, const ctf_encoding_t *); extern ctf_id_t ctf_add_pointer(ctf_file_t *, uint_t, ctf_id_t); extern ctf_id_t ctf_add_type(ctf_file_t *, ctf_file_t *, ctf_id_t); extern ctf_id_t ctf_add_typedef(ctf_file_t *, uint_t, const char *, ctf_id_t); extern ctf_id_t ctf_add_restrict(ctf_file_t *, uint_t, ctf_id_t); extern ctf_id_t ctf_add_struct(ctf_file_t *, uint_t, const char *); extern ctf_id_t ctf_add_union(ctf_file_t *, uint_t, const char *); extern ctf_id_t ctf_add_volatile(ctf_file_t *, uint_t, ctf_id_t); extern int ctf_add_enumerator(ctf_file_t *, ctf_id_t, const char *, int); extern int ctf_add_member(ctf_file_t *, ctf_id_t, const char *, ctf_id_t); extern int ctf_set_array(ctf_file_t *, ctf_id_t, const ctf_arinfo_t *); extern int ctf_delete_type(ctf_file_t *, ctf_id_t); extern int ctf_update(ctf_file_t *); extern int ctf_discard(ctf_file_t *); extern int ctf_write(ctf_file_t *, int); #ifdef _KERNEL struct module; extern ctf_file_t *ctf_modopen(struct module *, int *); #endif #ifdef __cplusplus } #endif #endif /* _CTF_API_H */ Index: stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h =================================================================== --- stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h (revision 268577) +++ stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h (revision 268578) @@ -1,2412 +1,2441 @@ /* * 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) 2011, Joyent, Inc. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2013 by Delphix. All rights reserved. */ #ifndef _SYS_DTRACE_H #define _SYS_DTRACE_H #pragma ident "%Z%%M% %I% %E% SMI" #ifdef __cplusplus extern "C" { #endif /* * DTrace Dynamic Tracing Software: Kernel 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. */ #ifndef _ASM #include #include #include #if defined(sun) #include #else #include #include #include #include typedef int model_t; #endif #include #include #if defined(sun) #include #else #include #endif /* * DTrace Universal Constants and Typedefs */ #define DTRACE_CPUALL -1 /* all CPUs */ #define DTRACE_IDNONE 0 /* invalid probe identifier */ #define DTRACE_EPIDNONE 0 /* invalid enabled probe identifier */ #define DTRACE_AGGIDNONE 0 /* invalid aggregation identifier */ #define DTRACE_AGGVARIDNONE 0 /* invalid aggregation variable ID */ #define DTRACE_CACHEIDNONE 0 /* invalid predicate cache */ #define DTRACE_PROVNONE 0 /* invalid provider identifier */ #define DTRACE_METAPROVNONE 0 /* invalid meta-provider identifier */ #define DTRACE_ARGNONE -1 /* invalid argument index */ #define DTRACE_PROVNAMELEN 64 #define DTRACE_MODNAMELEN 64 #define DTRACE_FUNCNAMELEN 128 #define DTRACE_NAMELEN 64 #define DTRACE_FULLNAMELEN (DTRACE_PROVNAMELEN + DTRACE_MODNAMELEN + \ DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 4) #define DTRACE_ARGTYPELEN 128 typedef uint32_t dtrace_id_t; /* probe identifier */ typedef uint32_t dtrace_epid_t; /* enabled probe identifier */ typedef uint32_t dtrace_aggid_t; /* aggregation identifier */ typedef int64_t dtrace_aggvarid_t; /* aggregation variable identifier */ typedef uint16_t dtrace_actkind_t; /* action kind */ typedef int64_t dtrace_optval_t; /* option value */ typedef uint32_t dtrace_cacheid_t; /* predicate cache identifier */ typedef enum dtrace_probespec { DTRACE_PROBESPEC_NONE = -1, DTRACE_PROBESPEC_PROVIDER = 0, DTRACE_PROBESPEC_MOD, DTRACE_PROBESPEC_FUNC, DTRACE_PROBESPEC_NAME } dtrace_probespec_t; /* * DTrace Intermediate Format (DIF) * * The following definitions describe the DTrace Intermediate Format (DIF), a * a RISC-like instruction set and program encoding used to represent * predicates and actions that can be bound to DTrace probes. The constants * below defining the number of available registers are suggested minimums; the * compiler should use DTRACEIOC_CONF to dynamically obtain the number of * registers provided by the current DTrace implementation. */ #define DIF_VERSION_1 1 /* DIF version 1: Solaris 10 Beta */ #define DIF_VERSION_2 2 /* DIF version 2: Solaris 10 FCS */ #define DIF_VERSION DIF_VERSION_2 /* latest DIF instruction set version */ #define DIF_DIR_NREGS 8 /* number of DIF integer registers */ #define DIF_DTR_NREGS 8 /* number of DIF tuple registers */ #define DIF_OP_OR 1 /* or r1, r2, rd */ #define DIF_OP_XOR 2 /* xor r1, r2, rd */ #define DIF_OP_AND 3 /* and r1, r2, rd */ #define DIF_OP_SLL 4 /* sll r1, r2, rd */ #define DIF_OP_SRL 5 /* srl r1, r2, rd */ #define DIF_OP_SUB 6 /* sub r1, r2, rd */ #define DIF_OP_ADD 7 /* add r1, r2, rd */ #define DIF_OP_MUL 8 /* mul r1, r2, rd */ #define DIF_OP_SDIV 9 /* sdiv r1, r2, rd */ #define DIF_OP_UDIV 10 /* udiv r1, r2, rd */ #define DIF_OP_SREM 11 /* srem r1, r2, rd */ #define DIF_OP_UREM 12 /* urem r1, r2, rd */ #define DIF_OP_NOT 13 /* not r1, rd */ #define DIF_OP_MOV 14 /* mov r1, rd */ #define DIF_OP_CMP 15 /* cmp r1, r2 */ #define DIF_OP_TST 16 /* tst r1 */ #define DIF_OP_BA 17 /* ba label */ #define DIF_OP_BE 18 /* be label */ #define DIF_OP_BNE 19 /* bne label */ #define DIF_OP_BG 20 /* bg label */ #define DIF_OP_BGU 21 /* bgu label */ #define DIF_OP_BGE 22 /* bge label */ #define DIF_OP_BGEU 23 /* bgeu label */ #define DIF_OP_BL 24 /* bl label */ #define DIF_OP_BLU 25 /* blu label */ #define DIF_OP_BLE 26 /* ble label */ #define DIF_OP_BLEU 27 /* bleu label */ #define DIF_OP_LDSB 28 /* ldsb [r1], rd */ #define DIF_OP_LDSH 29 /* ldsh [r1], rd */ #define DIF_OP_LDSW 30 /* ldsw [r1], rd */ #define DIF_OP_LDUB 31 /* ldub [r1], rd */ #define DIF_OP_LDUH 32 /* lduh [r1], rd */ #define DIF_OP_LDUW 33 /* lduw [r1], rd */ #define DIF_OP_LDX 34 /* ldx [r1], rd */ #define DIF_OP_RET 35 /* ret rd */ #define DIF_OP_NOP 36 /* nop */ #define DIF_OP_SETX 37 /* setx intindex, rd */ #define DIF_OP_SETS 38 /* sets strindex, rd */ #define DIF_OP_SCMP 39 /* scmp r1, r2 */ #define DIF_OP_LDGA 40 /* ldga var, ri, rd */ #define DIF_OP_LDGS 41 /* ldgs var, rd */ #define DIF_OP_STGS 42 /* stgs var, rs */ #define DIF_OP_LDTA 43 /* ldta var, ri, rd */ #define DIF_OP_LDTS 44 /* ldts var, rd */ #define DIF_OP_STTS 45 /* stts var, rs */ #define DIF_OP_SRA 46 /* sra r1, r2, rd */ #define DIF_OP_CALL 47 /* call subr, rd */ #define DIF_OP_PUSHTR 48 /* pushtr type, rs, rr */ #define DIF_OP_PUSHTV 49 /* pushtv type, rs, rv */ #define DIF_OP_POPTS 50 /* popts */ #define DIF_OP_FLUSHTS 51 /* flushts */ #define DIF_OP_LDGAA 52 /* ldgaa var, rd */ #define DIF_OP_LDTAA 53 /* ldtaa var, rd */ #define DIF_OP_STGAA 54 /* stgaa var, rs */ #define DIF_OP_STTAA 55 /* sttaa var, rs */ #define DIF_OP_LDLS 56 /* ldls var, rd */ #define DIF_OP_STLS 57 /* stls var, rs */ #define DIF_OP_ALLOCS 58 /* allocs r1, rd */ #define DIF_OP_COPYS 59 /* copys r1, r2, rd */ #define DIF_OP_STB 60 /* stb r1, [rd] */ #define DIF_OP_STH 61 /* sth r1, [rd] */ #define DIF_OP_STW 62 /* stw r1, [rd] */ #define DIF_OP_STX 63 /* stx r1, [rd] */ #define DIF_OP_ULDSB 64 /* uldsb [r1], rd */ #define DIF_OP_ULDSH 65 /* uldsh [r1], rd */ #define DIF_OP_ULDSW 66 /* uldsw [r1], rd */ #define DIF_OP_ULDUB 67 /* uldub [r1], rd */ #define DIF_OP_ULDUH 68 /* ulduh [r1], rd */ #define DIF_OP_ULDUW 69 /* ulduw [r1], rd */ #define DIF_OP_ULDX 70 /* uldx [r1], rd */ #define DIF_OP_RLDSB 71 /* rldsb [r1], rd */ #define DIF_OP_RLDSH 72 /* rldsh [r1], rd */ #define DIF_OP_RLDSW 73 /* rldsw [r1], rd */ #define DIF_OP_RLDUB 74 /* rldub [r1], rd */ #define DIF_OP_RLDUH 75 /* rlduh [r1], rd */ #define DIF_OP_RLDUW 76 /* rlduw [r1], rd */ #define DIF_OP_RLDX 77 /* rldx [r1], rd */ #define DIF_OP_XLATE 78 /* xlate xlrindex, rd */ #define DIF_OP_XLARG 79 /* xlarg xlrindex, rd */ #define DIF_INTOFF_MAX 0xffff /* highest integer table offset */ #define DIF_STROFF_MAX 0xffff /* highest string table offset */ #define DIF_REGISTER_MAX 0xff /* highest register number */ #define DIF_VARIABLE_MAX 0xffff /* highest variable identifier */ #define DIF_SUBROUTINE_MAX 0xffff /* highest subroutine code */ #define DIF_VAR_ARRAY_MIN 0x0000 /* lowest numbered array variable */ #define DIF_VAR_ARRAY_UBASE 0x0080 /* lowest user-defined array */ #define DIF_VAR_ARRAY_MAX 0x00ff /* highest numbered array variable */ #define DIF_VAR_OTHER_MIN 0x0100 /* lowest numbered scalar or assc */ #define DIF_VAR_OTHER_UBASE 0x0500 /* lowest user-defined scalar or assc */ #define DIF_VAR_OTHER_MAX 0xffff /* highest numbered scalar or assc */ #define DIF_VAR_ARGS 0x0000 /* arguments array */ #define DIF_VAR_REGS 0x0001 /* registers array */ #define DIF_VAR_UREGS 0x0002 /* user registers array */ #define DIF_VAR_CURTHREAD 0x0100 /* thread pointer */ #define DIF_VAR_TIMESTAMP 0x0101 /* timestamp */ #define DIF_VAR_VTIMESTAMP 0x0102 /* virtual timestamp */ #define DIF_VAR_IPL 0x0103 /* interrupt priority level */ #define DIF_VAR_EPID 0x0104 /* enabled probe ID */ #define DIF_VAR_ID 0x0105 /* probe ID */ #define DIF_VAR_ARG0 0x0106 /* first argument */ #define DIF_VAR_ARG1 0x0107 /* second argument */ #define DIF_VAR_ARG2 0x0108 /* third argument */ #define DIF_VAR_ARG3 0x0109 /* fourth argument */ #define DIF_VAR_ARG4 0x010a /* fifth argument */ #define DIF_VAR_ARG5 0x010b /* sixth argument */ #define DIF_VAR_ARG6 0x010c /* seventh argument */ #define DIF_VAR_ARG7 0x010d /* eighth argument */ #define DIF_VAR_ARG8 0x010e /* ninth argument */ #define DIF_VAR_ARG9 0x010f /* tenth argument */ #define DIF_VAR_STACKDEPTH 0x0110 /* stack depth */ #define DIF_VAR_CALLER 0x0111 /* caller */ #define DIF_VAR_PROBEPROV 0x0112 /* probe provider */ #define DIF_VAR_PROBEMOD 0x0113 /* probe module */ #define DIF_VAR_PROBEFUNC 0x0114 /* probe function */ #define DIF_VAR_PROBENAME 0x0115 /* probe name */ #define DIF_VAR_PID 0x0116 /* process ID */ #define DIF_VAR_TID 0x0117 /* (per-process) thread ID */ #define DIF_VAR_EXECNAME 0x0118 /* name of executable */ #define DIF_VAR_ZONENAME 0x0119 /* zone name associated with process */ #define DIF_VAR_WALLTIMESTAMP 0x011a /* wall-clock timestamp */ #define DIF_VAR_USTACKDEPTH 0x011b /* user-land stack depth */ #define DIF_VAR_UCALLER 0x011c /* user-level caller */ #define DIF_VAR_PPID 0x011d /* parent process ID */ #define DIF_VAR_UID 0x011e /* process user ID */ #define DIF_VAR_GID 0x011f /* process group ID */ #define DIF_VAR_ERRNO 0x0120 /* thread errno */ #define DIF_VAR_EXECARGS 0x0121 /* process arguments */ #if !defined(sun) #define DIF_VAR_CPU 0x0200 #endif #define DIF_SUBR_RAND 0 #define DIF_SUBR_MUTEX_OWNED 1 #define DIF_SUBR_MUTEX_OWNER 2 #define DIF_SUBR_MUTEX_TYPE_ADAPTIVE 3 #define DIF_SUBR_MUTEX_TYPE_SPIN 4 #define DIF_SUBR_RW_READ_HELD 5 #define DIF_SUBR_RW_WRITE_HELD 6 #define DIF_SUBR_RW_ISWRITER 7 #define DIF_SUBR_COPYIN 8 #define DIF_SUBR_COPYINSTR 9 #define DIF_SUBR_SPECULATION 10 #define DIF_SUBR_PROGENYOF 11 #define DIF_SUBR_STRLEN 12 #define DIF_SUBR_COPYOUT 13 #define DIF_SUBR_COPYOUTSTR 14 #define DIF_SUBR_ALLOCA 15 #define DIF_SUBR_BCOPY 16 #define DIF_SUBR_COPYINTO 17 #define DIF_SUBR_MSGDSIZE 18 #define DIF_SUBR_MSGSIZE 19 #define DIF_SUBR_GETMAJOR 20 #define DIF_SUBR_GETMINOR 21 #define DIF_SUBR_DDI_PATHNAME 22 #define DIF_SUBR_STRJOIN 23 #define DIF_SUBR_LLTOSTR 24 #define DIF_SUBR_BASENAME 25 #define DIF_SUBR_DIRNAME 26 #define DIF_SUBR_CLEANPATH 27 #define DIF_SUBR_STRCHR 28 #define DIF_SUBR_STRRCHR 29 #define DIF_SUBR_STRSTR 30 #define DIF_SUBR_STRTOK 31 #define DIF_SUBR_SUBSTR 32 #define DIF_SUBR_INDEX 33 #define DIF_SUBR_RINDEX 34 #define DIF_SUBR_HTONS 35 #define DIF_SUBR_HTONL 36 #define DIF_SUBR_HTONLL 37 #define DIF_SUBR_NTOHS 38 #define DIF_SUBR_NTOHL 39 #define DIF_SUBR_NTOHLL 40 #define DIF_SUBR_INET_NTOP 41 #define DIF_SUBR_INET_NTOA 42 #define DIF_SUBR_INET_NTOA6 43 #define DIF_SUBR_TOUPPER 44 #define DIF_SUBR_TOLOWER 45 #define DIF_SUBR_MEMREF 46 #define DIF_SUBR_TYPEREF 47 #define DIF_SUBR_SX_SHARED_HELD 48 #define DIF_SUBR_SX_EXCLUSIVE_HELD 49 #define DIF_SUBR_SX_ISEXCLUSIVE 50 +#define DIF_SUBR_GETF 52 +#define DIF_SUBR_JSON 53 +#define DIF_SUBR_STRTOLL 54 +#define DIF_SUBR_MAX 54 /* max subroutine value */ -#define DIF_SUBR_MAX 50 /* max subroutine value */ - typedef uint32_t dif_instr_t; #define DIF_INSTR_OP(i) (((i) >> 24) & 0xff) #define DIF_INSTR_R1(i) (((i) >> 16) & 0xff) #define DIF_INSTR_R2(i) (((i) >> 8) & 0xff) #define DIF_INSTR_RD(i) ((i) & 0xff) #define DIF_INSTR_RS(i) ((i) & 0xff) #define DIF_INSTR_LABEL(i) ((i) & 0xffffff) #define DIF_INSTR_VAR(i) (((i) >> 8) & 0xffff) #define DIF_INSTR_INTEGER(i) (((i) >> 8) & 0xffff) #define DIF_INSTR_STRING(i) (((i) >> 8) & 0xffff) #define DIF_INSTR_SUBR(i) (((i) >> 8) & 0xffff) #define DIF_INSTR_TYPE(i) (((i) >> 16) & 0xff) #define DIF_INSTR_XLREF(i) (((i) >> 8) & 0xffff) #define DIF_INSTR_FMT(op, r1, r2, d) \ (((op) << 24) | ((r1) << 16) | ((r2) << 8) | (d)) #define DIF_INSTR_NOT(r1, d) (DIF_INSTR_FMT(DIF_OP_NOT, r1, 0, d)) #define DIF_INSTR_MOV(r1, d) (DIF_INSTR_FMT(DIF_OP_MOV, r1, 0, d)) #define DIF_INSTR_CMP(op, r1, r2) (DIF_INSTR_FMT(op, r1, r2, 0)) #define DIF_INSTR_TST(r1) (DIF_INSTR_FMT(DIF_OP_TST, r1, 0, 0)) #define DIF_INSTR_BRANCH(op, label) (((op) << 24) | (label)) #define DIF_INSTR_LOAD(op, r1, d) (DIF_INSTR_FMT(op, r1, 0, d)) #define DIF_INSTR_STORE(op, r1, d) (DIF_INSTR_FMT(op, r1, 0, d)) #define DIF_INSTR_SETX(i, d) ((DIF_OP_SETX << 24) | ((i) << 8) | (d)) #define DIF_INSTR_SETS(s, d) ((DIF_OP_SETS << 24) | ((s) << 8) | (d)) #define DIF_INSTR_RET(d) (DIF_INSTR_FMT(DIF_OP_RET, 0, 0, d)) #define DIF_INSTR_NOP (DIF_OP_NOP << 24) #define DIF_INSTR_LDA(op, v, r, d) (DIF_INSTR_FMT(op, v, r, d)) #define DIF_INSTR_LDV(op, v, d) (((op) << 24) | ((v) << 8) | (d)) #define DIF_INSTR_STV(op, v, rs) (((op) << 24) | ((v) << 8) | (rs)) #define DIF_INSTR_CALL(s, d) ((DIF_OP_CALL << 24) | ((s) << 8) | (d)) #define DIF_INSTR_PUSHTS(op, t, r2, rs) (DIF_INSTR_FMT(op, t, r2, rs)) #define DIF_INSTR_POPTS (DIF_OP_POPTS << 24) #define DIF_INSTR_FLUSHTS (DIF_OP_FLUSHTS << 24) #define DIF_INSTR_ALLOCS(r1, d) (DIF_INSTR_FMT(DIF_OP_ALLOCS, r1, 0, d)) #define DIF_INSTR_COPYS(r1, r2, d) (DIF_INSTR_FMT(DIF_OP_COPYS, r1, r2, d)) #define DIF_INSTR_XLATE(op, r, d) (((op) << 24) | ((r) << 8) | (d)) #define DIF_REG_R0 0 /* %r0 is always set to zero */ /* * A DTrace Intermediate Format Type (DIF Type) is used to represent the types * of variables, function and associative array arguments, and the return type * for each DIF object (shown below). It contains a description of the type, * its size in bytes, and a module identifier. */ typedef struct dtrace_diftype { uint8_t dtdt_kind; /* type kind (see below) */ uint8_t dtdt_ckind; /* type kind in CTF */ uint8_t dtdt_flags; /* type flags (see below) */ uint8_t dtdt_pad; /* reserved for future use */ uint32_t dtdt_size; /* type size in bytes (unless string) */ } dtrace_diftype_t; #define DIF_TYPE_CTF 0 /* type is a CTF type */ #define DIF_TYPE_STRING 1 /* type is a D string */ #define DIF_TF_BYREF 0x1 /* type is passed by reference */ +#define DIF_TF_BYUREF 0x2 /* user type is passed by reference */ /* * A DTrace Intermediate Format variable record is used to describe each of the * variables referenced by a given DIF object. It contains an integer variable * identifier along with variable scope and properties, as shown below. The * size of this structure must be sizeof (int) aligned. */ typedef struct dtrace_difv { uint32_t dtdv_name; /* variable name index in dtdo_strtab */ uint32_t dtdv_id; /* variable reference identifier */ uint8_t dtdv_kind; /* variable kind (see below) */ uint8_t dtdv_scope; /* variable scope (see below) */ uint16_t dtdv_flags; /* variable flags (see below) */ dtrace_diftype_t dtdv_type; /* variable type (see above) */ } dtrace_difv_t; #define DIFV_KIND_ARRAY 0 /* variable is an array of quantities */ #define DIFV_KIND_SCALAR 1 /* variable is a scalar quantity */ #define DIFV_SCOPE_GLOBAL 0 /* variable has global scope */ #define DIFV_SCOPE_THREAD 1 /* variable has thread scope */ #define DIFV_SCOPE_LOCAL 2 /* variable has local scope */ #define DIFV_F_REF 0x1 /* variable is referenced by DIFO */ #define DIFV_F_MOD 0x2 /* variable is written by DIFO */ /* * DTrace Actions * * The upper byte determines the class of the action; the low bytes determines * the specific action within that class. The classes of actions are as * follows: * * [ no class ] <= May record process- or kernel-related data * DTRACEACT_PROC <= Only records process-related data * DTRACEACT_PROC_DESTRUCTIVE <= Potentially destructive to processes * DTRACEACT_KERNEL <= Only records kernel-related data * DTRACEACT_KERNEL_DESTRUCTIVE <= Potentially destructive to the kernel * DTRACEACT_SPECULATIVE <= Speculation-related action * DTRACEACT_AGGREGATION <= Aggregating action */ #define DTRACEACT_NONE 0 /* no action */ #define DTRACEACT_DIFEXPR 1 /* action is DIF expression */ #define DTRACEACT_EXIT 2 /* exit() action */ #define DTRACEACT_PRINTF 3 /* printf() action */ #define DTRACEACT_PRINTA 4 /* printa() action */ #define DTRACEACT_LIBACT 5 /* library-controlled action */ #define DTRACEACT_TRACEMEM 6 /* tracemem() action */ #define DTRACEACT_TRACEMEM_DYNSIZE 7 /* dynamic tracemem() size */ #define DTRACEACT_PRINTM 8 /* printm() action (BSD) */ #define DTRACEACT_PRINTT 9 /* printt() action (BSD) */ #define DTRACEACT_PROC 0x0100 #define DTRACEACT_USTACK (DTRACEACT_PROC + 1) #define DTRACEACT_JSTACK (DTRACEACT_PROC + 2) #define DTRACEACT_USYM (DTRACEACT_PROC + 3) #define DTRACEACT_UMOD (DTRACEACT_PROC + 4) #define DTRACEACT_UADDR (DTRACEACT_PROC + 5) #define DTRACEACT_PROC_DESTRUCTIVE 0x0200 #define DTRACEACT_STOP (DTRACEACT_PROC_DESTRUCTIVE + 1) #define DTRACEACT_RAISE (DTRACEACT_PROC_DESTRUCTIVE + 2) #define DTRACEACT_SYSTEM (DTRACEACT_PROC_DESTRUCTIVE + 3) #define DTRACEACT_FREOPEN (DTRACEACT_PROC_DESTRUCTIVE + 4) #define DTRACEACT_PROC_CONTROL 0x0300 #define DTRACEACT_KERNEL 0x0400 #define DTRACEACT_STACK (DTRACEACT_KERNEL + 1) #define DTRACEACT_SYM (DTRACEACT_KERNEL + 2) #define DTRACEACT_MOD (DTRACEACT_KERNEL + 3) #define DTRACEACT_KERNEL_DESTRUCTIVE 0x0500 #define DTRACEACT_BREAKPOINT (DTRACEACT_KERNEL_DESTRUCTIVE + 1) #define DTRACEACT_PANIC (DTRACEACT_KERNEL_DESTRUCTIVE + 2) #define DTRACEACT_CHILL (DTRACEACT_KERNEL_DESTRUCTIVE + 3) #define DTRACEACT_SPECULATIVE 0x0600 #define DTRACEACT_SPECULATE (DTRACEACT_SPECULATIVE + 1) #define DTRACEACT_COMMIT (DTRACEACT_SPECULATIVE + 2) #define DTRACEACT_DISCARD (DTRACEACT_SPECULATIVE + 3) #define DTRACEACT_CLASS(x) ((x) & 0xff00) #define DTRACEACT_ISDESTRUCTIVE(x) \ (DTRACEACT_CLASS(x) == DTRACEACT_PROC_DESTRUCTIVE || \ DTRACEACT_CLASS(x) == DTRACEACT_KERNEL_DESTRUCTIVE) #define DTRACEACT_ISSPECULATIVE(x) \ (DTRACEACT_CLASS(x) == DTRACEACT_SPECULATIVE) #define DTRACEACT_ISPRINTFLIKE(x) \ ((x) == DTRACEACT_PRINTF || (x) == DTRACEACT_PRINTA || \ (x) == DTRACEACT_SYSTEM || (x) == DTRACEACT_FREOPEN) /* * DTrace Aggregating Actions * * These are functions f(x) for which the following is true: * * f(f(x_0) U f(x_1) U ... U f(x_n)) = f(x_0 U x_1 U ... U x_n) * * where x_n is a set of arbitrary data. Aggregating actions are in their own * DTrace action class, DTTRACEACT_AGGREGATION. The macros provided here allow * for easier processing of the aggregation argument and data payload for a few * aggregating actions (notably: quantize(), lquantize(), and ustack()). */ #define DTRACEACT_AGGREGATION 0x0700 #define DTRACEAGG_COUNT (DTRACEACT_AGGREGATION + 1) #define DTRACEAGG_MIN (DTRACEACT_AGGREGATION + 2) #define DTRACEAGG_MAX (DTRACEACT_AGGREGATION + 3) #define DTRACEAGG_AVG (DTRACEACT_AGGREGATION + 4) #define DTRACEAGG_SUM (DTRACEACT_AGGREGATION + 5) #define DTRACEAGG_STDDEV (DTRACEACT_AGGREGATION + 6) #define DTRACEAGG_QUANTIZE (DTRACEACT_AGGREGATION + 7) #define DTRACEAGG_LQUANTIZE (DTRACEACT_AGGREGATION + 8) #define DTRACEAGG_LLQUANTIZE (DTRACEACT_AGGREGATION + 9) #define DTRACEACT_ISAGG(x) \ (DTRACEACT_CLASS(x) == DTRACEACT_AGGREGATION) #define DTRACE_QUANTIZE_NBUCKETS \ (((sizeof (uint64_t) * NBBY) - 1) * 2 + 1) #define DTRACE_QUANTIZE_ZEROBUCKET ((sizeof (uint64_t) * NBBY) - 1) #define DTRACE_QUANTIZE_BUCKETVAL(buck) \ (int64_t)((buck) < DTRACE_QUANTIZE_ZEROBUCKET ? \ -(1LL << (DTRACE_QUANTIZE_ZEROBUCKET - 1 - (buck))) : \ (buck) == DTRACE_QUANTIZE_ZEROBUCKET ? 0 : \ 1LL << ((buck) - DTRACE_QUANTIZE_ZEROBUCKET - 1)) #define DTRACE_LQUANTIZE_STEPSHIFT 48 #define DTRACE_LQUANTIZE_STEPMASK ((uint64_t)UINT16_MAX << 48) #define DTRACE_LQUANTIZE_LEVELSHIFT 32 #define DTRACE_LQUANTIZE_LEVELMASK ((uint64_t)UINT16_MAX << 32) #define DTRACE_LQUANTIZE_BASESHIFT 0 #define DTRACE_LQUANTIZE_BASEMASK UINT32_MAX #define DTRACE_LQUANTIZE_STEP(x) \ (uint16_t)(((x) & DTRACE_LQUANTIZE_STEPMASK) >> \ DTRACE_LQUANTIZE_STEPSHIFT) #define DTRACE_LQUANTIZE_LEVELS(x) \ (uint16_t)(((x) & DTRACE_LQUANTIZE_LEVELMASK) >> \ DTRACE_LQUANTIZE_LEVELSHIFT) #define DTRACE_LQUANTIZE_BASE(x) \ (int32_t)(((x) & DTRACE_LQUANTIZE_BASEMASK) >> \ DTRACE_LQUANTIZE_BASESHIFT) #define DTRACE_LLQUANTIZE_FACTORSHIFT 48 #define DTRACE_LLQUANTIZE_FACTORMASK ((uint64_t)UINT16_MAX << 48) #define DTRACE_LLQUANTIZE_LOWSHIFT 32 #define DTRACE_LLQUANTIZE_LOWMASK ((uint64_t)UINT16_MAX << 32) #define DTRACE_LLQUANTIZE_HIGHSHIFT 16 #define DTRACE_LLQUANTIZE_HIGHMASK ((uint64_t)UINT16_MAX << 16) #define DTRACE_LLQUANTIZE_NSTEPSHIFT 0 #define DTRACE_LLQUANTIZE_NSTEPMASK UINT16_MAX #define DTRACE_LLQUANTIZE_FACTOR(x) \ (uint16_t)(((x) & DTRACE_LLQUANTIZE_FACTORMASK) >> \ DTRACE_LLQUANTIZE_FACTORSHIFT) #define DTRACE_LLQUANTIZE_LOW(x) \ (uint16_t)(((x) & DTRACE_LLQUANTIZE_LOWMASK) >> \ DTRACE_LLQUANTIZE_LOWSHIFT) #define DTRACE_LLQUANTIZE_HIGH(x) \ (uint16_t)(((x) & DTRACE_LLQUANTIZE_HIGHMASK) >> \ DTRACE_LLQUANTIZE_HIGHSHIFT) #define DTRACE_LLQUANTIZE_NSTEP(x) \ (uint16_t)(((x) & DTRACE_LLQUANTIZE_NSTEPMASK) >> \ DTRACE_LLQUANTIZE_NSTEPSHIFT) #define DTRACE_USTACK_NFRAMES(x) (uint32_t)((x) & UINT32_MAX) #define DTRACE_USTACK_STRSIZE(x) (uint32_t)((x) >> 32) #define DTRACE_USTACK_ARG(x, y) \ ((((uint64_t)(y)) << 32) | ((x) & UINT32_MAX)) #ifndef _LP64 #if BYTE_ORDER == _BIG_ENDIAN #define DTRACE_PTR(type, name) uint32_t name##pad; type *name #else #define DTRACE_PTR(type, name) type *name; uint32_t name##pad #endif #else #define DTRACE_PTR(type, name) type *name #endif /* * DTrace Object Format (DOF) * * DTrace programs can be persistently encoded in the DOF format so that they * may be embedded in other programs (for example, in an ELF file) or in the * dtrace driver configuration file for use in anonymous tracing. The DOF * format is versioned and extensible so that it can be revised and so that * internal data structures can be modified or extended compatibly. All DOF * structures use fixed-size types, so the 32-bit and 64-bit representations * are identical and consumers can use either data model transparently. * * The file layout is structured as follows: * * +---------------+-------------------+----- ... ----+---- ... ------+ * | dof_hdr_t | dof_sec_t[ ... ] | loadable | non-loadable | * | (file header) | (section headers) | section data | section data | * +---------------+-------------------+----- ... ----+---- ... ------+ * |<------------ dof_hdr.dofh_loadsz --------------->| | * |<------------ dof_hdr.dofh_filesz ------------------------------->| * * The file header stores meta-data including a magic number, data model for * the instrumentation, data encoding, and properties of the DIF code within. * The header describes its own size and the size of the section headers. By * convention, an array of section headers follows the file header, and then * the data for all loadable sections and unloadable sections. This permits * consumer code to easily download the headers and all loadable data into the * DTrace driver in one contiguous chunk, omitting other extraneous sections. * * The section headers describe the size, offset, alignment, and section type * for each section. Sections are described using a set of #defines that tell * the consumer what kind of data is expected. Sections can contain links to * other sections by storing a dof_secidx_t, an index into the section header * array, inside of the section data structures. The section header includes * an entry size so that sections with data arrays can grow their structures. * * The DOF data itself can contain many snippets of DIF (i.e. >1 DIFOs), which * are represented themselves as a collection of related DOF sections. This * permits us to change the set of sections associated with a DIFO over time, * and also permits us to encode DIFOs that contain different sets of sections. * When a DOF section wants to refer to a DIFO, it stores the dof_secidx_t of a * section of type DOF_SECT_DIFOHDR. This section's data is then an array of * dof_secidx_t's which in turn denote the sections associated with this DIFO. * * This loose coupling of the file structure (header and sections) to the * structure of the DTrace program itself (ECB descriptions, action * descriptions, and DIFOs) permits activities such as relocation processing * to occur in a single pass without having to understand D program structure. * * Finally, strings are always stored in ELF-style string tables along with a * string table section index and string table offset. Therefore strings in * DOF are always arbitrary-length and not bound to the current implementation. */ #define DOF_ID_SIZE 16 /* total size of dofh_ident[] in bytes */ typedef struct dof_hdr { uint8_t dofh_ident[DOF_ID_SIZE]; /* identification bytes (see below) */ uint32_t dofh_flags; /* file attribute flags (if any) */ uint32_t dofh_hdrsize; /* size of file header in bytes */ uint32_t dofh_secsize; /* size of section header in bytes */ uint32_t dofh_secnum; /* number of section headers */ uint64_t dofh_secoff; /* file offset of section headers */ uint64_t dofh_loadsz; /* file size of loadable portion */ uint64_t dofh_filesz; /* file size of entire DOF file */ uint64_t dofh_pad; /* reserved for future use */ } dof_hdr_t; #define DOF_ID_MAG0 0 /* first byte of magic number */ #define DOF_ID_MAG1 1 /* second byte of magic number */ #define DOF_ID_MAG2 2 /* third byte of magic number */ #define DOF_ID_MAG3 3 /* fourth byte of magic number */ #define DOF_ID_MODEL 4 /* DOF data model (see below) */ #define DOF_ID_ENCODING 5 /* DOF data encoding (see below) */ #define DOF_ID_VERSION 6 /* DOF file format major version (see below) */ #define DOF_ID_DIFVERS 7 /* DIF instruction set version */ #define DOF_ID_DIFIREG 8 /* DIF integer registers used by compiler */ #define DOF_ID_DIFTREG 9 /* DIF tuple registers used by compiler */ #define DOF_ID_PAD 10 /* start of padding bytes (all zeroes) */ #define DOF_MAG_MAG0 0x7F /* DOF_ID_MAG[0-3] */ #define DOF_MAG_MAG1 'D' #define DOF_MAG_MAG2 'O' #define DOF_MAG_MAG3 'F' #define DOF_MAG_STRING "\177DOF" #define DOF_MAG_STRLEN 4 #define DOF_MODEL_NONE 0 /* DOF_ID_MODEL */ #define DOF_MODEL_ILP32 1 #define DOF_MODEL_LP64 2 #ifdef _LP64 #define DOF_MODEL_NATIVE DOF_MODEL_LP64 #else #define DOF_MODEL_NATIVE DOF_MODEL_ILP32 #endif #define DOF_ENCODE_NONE 0 /* DOF_ID_ENCODING */ #define DOF_ENCODE_LSB 1 #define DOF_ENCODE_MSB 2 #if BYTE_ORDER == _BIG_ENDIAN #define DOF_ENCODE_NATIVE DOF_ENCODE_MSB #else #define DOF_ENCODE_NATIVE DOF_ENCODE_LSB #endif #define DOF_VERSION_1 1 /* DOF version 1: Solaris 10 FCS */ #define DOF_VERSION_2 2 /* DOF version 2: Solaris Express 6/06 */ #define DOF_VERSION DOF_VERSION_2 /* Latest DOF version */ #define DOF_FL_VALID 0 /* mask of all valid dofh_flags bits */ typedef uint32_t dof_secidx_t; /* section header table index type */ typedef uint32_t dof_stridx_t; /* string table index type */ #define DOF_SECIDX_NONE (-1U) /* null value for section indices */ #define DOF_STRIDX_NONE (-1U) /* null value for string indices */ typedef struct dof_sec { uint32_t dofs_type; /* section type (see below) */ uint32_t dofs_align; /* section data memory alignment */ uint32_t dofs_flags; /* section flags (if any) */ uint32_t dofs_entsize; /* size of section entry (if table) */ uint64_t dofs_offset; /* offset of section data within file */ uint64_t dofs_size; /* size of section data in bytes */ } dof_sec_t; #define DOF_SECT_NONE 0 /* null section */ #define DOF_SECT_COMMENTS 1 /* compiler comments */ #define DOF_SECT_SOURCE 2 /* D program source code */ #define DOF_SECT_ECBDESC 3 /* dof_ecbdesc_t */ #define DOF_SECT_PROBEDESC 4 /* dof_probedesc_t */ #define DOF_SECT_ACTDESC 5 /* dof_actdesc_t array */ #define DOF_SECT_DIFOHDR 6 /* dof_difohdr_t (variable length) */ #define DOF_SECT_DIF 7 /* uint32_t array of byte code */ #define DOF_SECT_STRTAB 8 /* string table */ #define DOF_SECT_VARTAB 9 /* dtrace_difv_t array */ #define DOF_SECT_RELTAB 10 /* dof_relodesc_t array */ #define DOF_SECT_TYPTAB 11 /* dtrace_diftype_t array */ #define DOF_SECT_URELHDR 12 /* dof_relohdr_t (user relocations) */ #define DOF_SECT_KRELHDR 13 /* dof_relohdr_t (kernel relocations) */ #define DOF_SECT_OPTDESC 14 /* dof_optdesc_t array */ #define DOF_SECT_PROVIDER 15 /* dof_provider_t */ #define DOF_SECT_PROBES 16 /* dof_probe_t array */ #define DOF_SECT_PRARGS 17 /* uint8_t array (probe arg mappings) */ #define DOF_SECT_PROFFS 18 /* uint32_t array (probe arg offsets) */ #define DOF_SECT_INTTAB 19 /* uint64_t array */ #define DOF_SECT_UTSNAME 20 /* struct utsname */ #define DOF_SECT_XLTAB 21 /* dof_xlref_t array */ #define DOF_SECT_XLMEMBERS 22 /* dof_xlmember_t array */ #define DOF_SECT_XLIMPORT 23 /* dof_xlator_t */ #define DOF_SECT_XLEXPORT 24 /* dof_xlator_t */ #define DOF_SECT_PREXPORT 25 /* dof_secidx_t array (exported objs) */ #define DOF_SECT_PRENOFFS 26 /* uint32_t array (enabled offsets) */ #define DOF_SECF_LOAD 1 /* section should be loaded */ #define DOF_SEC_ISLOADABLE(x) \ (((x) == DOF_SECT_ECBDESC) || ((x) == DOF_SECT_PROBEDESC) || \ ((x) == DOF_SECT_ACTDESC) || ((x) == DOF_SECT_DIFOHDR) || \ ((x) == DOF_SECT_DIF) || ((x) == DOF_SECT_STRTAB) || \ ((x) == DOF_SECT_VARTAB) || ((x) == DOF_SECT_RELTAB) || \ ((x) == DOF_SECT_TYPTAB) || ((x) == DOF_SECT_URELHDR) || \ ((x) == DOF_SECT_KRELHDR) || ((x) == DOF_SECT_OPTDESC) || \ ((x) == DOF_SECT_PROVIDER) || ((x) == DOF_SECT_PROBES) || \ ((x) == DOF_SECT_PRARGS) || ((x) == DOF_SECT_PROFFS) || \ ((x) == DOF_SECT_INTTAB) || ((x) == DOF_SECT_XLTAB) || \ ((x) == DOF_SECT_XLMEMBERS) || ((x) == DOF_SECT_XLIMPORT) || \ ((x) == DOF_SECT_XLIMPORT) || ((x) == DOF_SECT_XLEXPORT) || \ ((x) == DOF_SECT_PREXPORT) || ((x) == DOF_SECT_PRENOFFS)) typedef struct dof_ecbdesc { dof_secidx_t dofe_probes; /* link to DOF_SECT_PROBEDESC */ dof_secidx_t dofe_pred; /* link to DOF_SECT_DIFOHDR */ dof_secidx_t dofe_actions; /* link to DOF_SECT_ACTDESC */ uint32_t dofe_pad; /* reserved for future use */ uint64_t dofe_uarg; /* user-supplied library argument */ } dof_ecbdesc_t; typedef struct dof_probedesc { dof_secidx_t dofp_strtab; /* link to DOF_SECT_STRTAB section */ dof_stridx_t dofp_provider; /* provider string */ dof_stridx_t dofp_mod; /* module string */ dof_stridx_t dofp_func; /* function string */ dof_stridx_t dofp_name; /* name string */ uint32_t dofp_id; /* probe identifier (or zero) */ } dof_probedesc_t; typedef struct dof_actdesc { dof_secidx_t dofa_difo; /* link to DOF_SECT_DIFOHDR */ dof_secidx_t dofa_strtab; /* link to DOF_SECT_STRTAB section */ uint32_t dofa_kind; /* action kind (DTRACEACT_* constant) */ uint32_t dofa_ntuple; /* number of subsequent tuple actions */ uint64_t dofa_arg; /* kind-specific argument */ uint64_t dofa_uarg; /* user-supplied argument */ } dof_actdesc_t; typedef struct dof_difohdr { dtrace_diftype_t dofd_rtype; /* return type for this fragment */ dof_secidx_t dofd_links[1]; /* variable length array of indices */ } dof_difohdr_t; typedef struct dof_relohdr { dof_secidx_t dofr_strtab; /* link to DOF_SECT_STRTAB for names */ dof_secidx_t dofr_relsec; /* link to DOF_SECT_RELTAB for relos */ dof_secidx_t dofr_tgtsec; /* link to section we are relocating */ } dof_relohdr_t; typedef struct dof_relodesc { dof_stridx_t dofr_name; /* string name of relocation symbol */ uint32_t dofr_type; /* relo type (DOF_RELO_* constant) */ uint64_t dofr_offset; /* byte offset for relocation */ uint64_t dofr_data; /* additional type-specific data */ } dof_relodesc_t; #define DOF_RELO_NONE 0 /* empty relocation entry */ #define DOF_RELO_SETX 1 /* relocate setx value */ typedef struct dof_optdesc { uint32_t dofo_option; /* option identifier */ dof_secidx_t dofo_strtab; /* string table, if string option */ uint64_t dofo_value; /* option value or string index */ } dof_optdesc_t; typedef uint32_t dof_attr_t; /* encoded stability attributes */ #define DOF_ATTR(n, d, c) (((n) << 24) | ((d) << 16) | ((c) << 8)) #define DOF_ATTR_NAME(a) (((a) >> 24) & 0xff) #define DOF_ATTR_DATA(a) (((a) >> 16) & 0xff) #define DOF_ATTR_CLASS(a) (((a) >> 8) & 0xff) typedef struct dof_provider { dof_secidx_t dofpv_strtab; /* link to DOF_SECT_STRTAB section */ dof_secidx_t dofpv_probes; /* link to DOF_SECT_PROBES section */ dof_secidx_t dofpv_prargs; /* link to DOF_SECT_PRARGS section */ dof_secidx_t dofpv_proffs; /* link to DOF_SECT_PROFFS section */ dof_stridx_t dofpv_name; /* provider name string */ dof_attr_t dofpv_provattr; /* provider attributes */ dof_attr_t dofpv_modattr; /* module attributes */ dof_attr_t dofpv_funcattr; /* function attributes */ dof_attr_t dofpv_nameattr; /* name attributes */ dof_attr_t dofpv_argsattr; /* args attributes */ dof_secidx_t dofpv_prenoffs; /* link to DOF_SECT_PRENOFFS section */ } dof_provider_t; typedef struct dof_probe { uint64_t dofpr_addr; /* probe base address or offset */ dof_stridx_t dofpr_func; /* probe function string */ dof_stridx_t dofpr_name; /* probe name string */ dof_stridx_t dofpr_nargv; /* native argument type strings */ dof_stridx_t dofpr_xargv; /* translated argument type strings */ uint32_t dofpr_argidx; /* index of first argument mapping */ uint32_t dofpr_offidx; /* index of first offset entry */ uint8_t dofpr_nargc; /* native argument count */ uint8_t dofpr_xargc; /* translated argument count */ uint16_t dofpr_noffs; /* number of offset entries for probe */ uint32_t dofpr_enoffidx; /* index of first is-enabled offset */ uint16_t dofpr_nenoffs; /* number of is-enabled offsets */ uint16_t dofpr_pad1; /* reserved for future use */ uint32_t dofpr_pad2; /* reserved for future use */ } dof_probe_t; typedef struct dof_xlator { dof_secidx_t dofxl_members; /* link to DOF_SECT_XLMEMBERS section */ dof_secidx_t dofxl_strtab; /* link to DOF_SECT_STRTAB section */ dof_stridx_t dofxl_argv; /* input parameter type strings */ uint32_t dofxl_argc; /* input parameter list length */ dof_stridx_t dofxl_type; /* output type string name */ dof_attr_t dofxl_attr; /* output stability attributes */ } dof_xlator_t; typedef struct dof_xlmember { dof_secidx_t dofxm_difo; /* member link to DOF_SECT_DIFOHDR */ dof_stridx_t dofxm_name; /* member name */ dtrace_diftype_t dofxm_type; /* member type */ } dof_xlmember_t; typedef struct dof_xlref { dof_secidx_t dofxr_xlator; /* link to DOF_SECT_XLATORS section */ uint32_t dofxr_member; /* index of referenced dof_xlmember */ uint32_t dofxr_argn; /* index of argument for DIF_OP_XLARG */ } dof_xlref_t; /* * DTrace Intermediate Format Object (DIFO) * * A DIFO is used to store the compiled DIF for a D expression, its return * type, and its string and variable tables. The string table is a single * buffer of character data into which sets instructions and variable * references can reference strings using a byte offset. The variable table * is an array of dtrace_difv_t structures that describe the name and type of * each variable and the id used in the DIF code. This structure is described * above in the DIF section of this header file. The DIFO is used at both * user-level (in the library) and in the kernel, but the structure is never * passed between the two: the DOF structures form the only interface. As a * result, the definition can change depending on the presence of _KERNEL. */ typedef struct dtrace_difo { dif_instr_t *dtdo_buf; /* instruction buffer */ uint64_t *dtdo_inttab; /* integer table (optional) */ char *dtdo_strtab; /* string table (optional) */ dtrace_difv_t *dtdo_vartab; /* variable table (optional) */ uint_t dtdo_len; /* length of instruction buffer */ uint_t dtdo_intlen; /* length of integer table */ uint_t dtdo_strlen; /* length of string table */ uint_t dtdo_varlen; /* length of variable table */ dtrace_diftype_t dtdo_rtype; /* return type */ uint_t dtdo_refcnt; /* owner reference count */ uint_t dtdo_destructive; /* invokes destructive subroutines */ #ifndef _KERNEL dof_relodesc_t *dtdo_kreltab; /* kernel relocations */ dof_relodesc_t *dtdo_ureltab; /* user relocations */ struct dt_node **dtdo_xlmtab; /* translator references */ uint_t dtdo_krelen; /* length of krelo table */ uint_t dtdo_urelen; /* length of urelo table */ uint_t dtdo_xlmlen; /* length of translator table */ #endif } dtrace_difo_t; /* * DTrace Enabling Description Structures * * When DTrace is tracking the description of a DTrace enabling entity (probe, * predicate, action, ECB, record, etc.), it does so in a description * structure. These structures all end in "desc", and are used at both * user-level and in the kernel -- but (with the exception of * dtrace_probedesc_t) they are never passed between them. Typically, * user-level will use the description structures when assembling an enabling. * It will then distill those description structures into a DOF object (see * above), and send it into the kernel. The kernel will again use the * description structures to create a description of the enabling as it reads * the DOF. When the description is complete, the enabling will be actually * created -- turning it into the structures that represent the enabling * instead of merely describing it. Not surprisingly, the description * structures bear a strong resemblance to the DOF structures that act as their * conduit. */ struct dtrace_predicate; typedef struct dtrace_probedesc { dtrace_id_t dtpd_id; /* probe identifier */ char dtpd_provider[DTRACE_PROVNAMELEN]; /* probe provider name */ char dtpd_mod[DTRACE_MODNAMELEN]; /* probe module name */ char dtpd_func[DTRACE_FUNCNAMELEN]; /* probe function name */ char dtpd_name[DTRACE_NAMELEN]; /* probe name */ } dtrace_probedesc_t; typedef struct dtrace_repldesc { dtrace_probedesc_t dtrpd_match; /* probe descr. to match */ dtrace_probedesc_t dtrpd_create; /* probe descr. to create */ } dtrace_repldesc_t; typedef struct dtrace_preddesc { dtrace_difo_t *dtpdd_difo; /* pointer to DIF object */ struct dtrace_predicate *dtpdd_predicate; /* pointer to predicate */ } dtrace_preddesc_t; typedef struct dtrace_actdesc { dtrace_difo_t *dtad_difo; /* pointer to DIF object */ struct dtrace_actdesc *dtad_next; /* next action */ dtrace_actkind_t dtad_kind; /* kind of action */ uint32_t dtad_ntuple; /* number in tuple */ uint64_t dtad_arg; /* action argument */ uint64_t dtad_uarg; /* user argument */ int dtad_refcnt; /* reference count */ } dtrace_actdesc_t; typedef struct dtrace_ecbdesc { dtrace_actdesc_t *dted_action; /* action description(s) */ dtrace_preddesc_t dted_pred; /* predicate description */ dtrace_probedesc_t dted_probe; /* probe description */ uint64_t dted_uarg; /* library argument */ int dted_refcnt; /* reference count */ } dtrace_ecbdesc_t; /* * DTrace Metadata Description Structures * * DTrace separates the trace data stream from the metadata stream. The only * metadata tokens placed in the data stream are the dtrace_rechdr_t (EPID + * timestamp) or (in the case of aggregations) aggregation identifiers. To * determine the structure of the data, DTrace consumers pass the token to the * kernel, and receive in return a corresponding description of the enabled * probe (via the dtrace_eprobedesc structure) or the aggregation (via the * dtrace_aggdesc structure). Both of these structures are expressed in terms * of record descriptions (via the dtrace_recdesc structure) that describe the * exact structure of the data. Some record descriptions may also contain a * format identifier; this additional bit of metadata can be retrieved from the * kernel, for which a format description is returned via the dtrace_fmtdesc * structure. Note that all four of these structures must be bitness-neutral * to allow for a 32-bit DTrace consumer on a 64-bit kernel. */ typedef struct dtrace_recdesc { dtrace_actkind_t dtrd_action; /* kind of action */ uint32_t dtrd_size; /* size of record */ uint32_t dtrd_offset; /* offset in ECB's data */ uint16_t dtrd_alignment; /* required alignment */ uint16_t dtrd_format; /* format, if any */ uint64_t dtrd_arg; /* action argument */ uint64_t dtrd_uarg; /* user argument */ } dtrace_recdesc_t; typedef struct dtrace_eprobedesc { dtrace_epid_t dtepd_epid; /* enabled probe ID */ dtrace_id_t dtepd_probeid; /* probe ID */ uint64_t dtepd_uarg; /* library argument */ uint32_t dtepd_size; /* total size */ int dtepd_nrecs; /* number of records */ dtrace_recdesc_t dtepd_rec[1]; /* records themselves */ } dtrace_eprobedesc_t; typedef struct dtrace_aggdesc { DTRACE_PTR(char, dtagd_name); /* not filled in by kernel */ dtrace_aggvarid_t dtagd_varid; /* not filled in by kernel */ int dtagd_flags; /* not filled in by kernel */ dtrace_aggid_t dtagd_id; /* aggregation ID */ dtrace_epid_t dtagd_epid; /* enabled probe ID */ uint32_t dtagd_size; /* size in bytes */ int dtagd_nrecs; /* number of records */ uint32_t dtagd_pad; /* explicit padding */ dtrace_recdesc_t dtagd_rec[1]; /* record descriptions */ } dtrace_aggdesc_t; typedef struct dtrace_fmtdesc { DTRACE_PTR(char, dtfd_string); /* format string */ int dtfd_length; /* length of format string */ uint16_t dtfd_format; /* format identifier */ } dtrace_fmtdesc_t; #define DTRACE_SIZEOF_EPROBEDESC(desc) \ (sizeof (dtrace_eprobedesc_t) + ((desc)->dtepd_nrecs ? \ (((desc)->dtepd_nrecs - 1) * sizeof (dtrace_recdesc_t)) : 0)) #define DTRACE_SIZEOF_AGGDESC(desc) \ (sizeof (dtrace_aggdesc_t) + ((desc)->dtagd_nrecs ? \ (((desc)->dtagd_nrecs - 1) * sizeof (dtrace_recdesc_t)) : 0)) /* * DTrace Option Interface * * Run-time DTrace options are set and retrieved via DOF_SECT_OPTDESC sections * in a DOF image. The dof_optdesc structure contains an option identifier and * an option value. The valid option identifiers are found below; the mapping * between option identifiers and option identifying strings is maintained at * user-level. Note that the value of DTRACEOPT_UNSET is such that all of the * following are potentially valid option values: all positive integers, zero * and negative one. Some options (notably "bufpolicy" and "bufresize") take * predefined tokens as their values; these are defined with * DTRACEOPT_{option}_{token}. */ #define DTRACEOPT_BUFSIZE 0 /* buffer size */ #define DTRACEOPT_BUFPOLICY 1 /* buffer policy */ #define DTRACEOPT_DYNVARSIZE 2 /* dynamic variable size */ #define DTRACEOPT_AGGSIZE 3 /* aggregation size */ #define DTRACEOPT_SPECSIZE 4 /* speculation size */ #define DTRACEOPT_NSPEC 5 /* number of speculations */ #define DTRACEOPT_STRSIZE 6 /* string size */ #define DTRACEOPT_CLEANRATE 7 /* dynvar cleaning rate */ #define DTRACEOPT_CPU 8 /* CPU to trace */ #define DTRACEOPT_BUFRESIZE 9 /* buffer resizing policy */ #define DTRACEOPT_GRABANON 10 /* grab anonymous state, if any */ #define DTRACEOPT_FLOWINDENT 11 /* indent function entry/return */ #define DTRACEOPT_QUIET 12 /* only output explicitly traced data */ #define DTRACEOPT_STACKFRAMES 13 /* number of stack frames */ #define DTRACEOPT_USTACKFRAMES 14 /* number of user stack frames */ #define DTRACEOPT_AGGRATE 15 /* aggregation snapshot rate */ #define DTRACEOPT_SWITCHRATE 16 /* buffer switching rate */ #define DTRACEOPT_STATUSRATE 17 /* status rate */ #define DTRACEOPT_DESTRUCTIVE 18 /* destructive actions allowed */ #define DTRACEOPT_STACKINDENT 19 /* output indent for stack traces */ #define DTRACEOPT_RAWBYTES 20 /* always print bytes in raw form */ #define DTRACEOPT_JSTACKFRAMES 21 /* number of jstack() frames */ #define DTRACEOPT_JSTACKSTRSIZE 22 /* size of jstack() string table */ #define DTRACEOPT_AGGSORTKEY 23 /* sort aggregations by key */ #define DTRACEOPT_AGGSORTREV 24 /* reverse-sort aggregations */ #define DTRACEOPT_AGGSORTPOS 25 /* agg. position to sort on */ #define DTRACEOPT_AGGSORTKEYPOS 26 /* agg. key position to sort on */ #define DTRACEOPT_TEMPORAL 27 /* temporally ordered output */ -#define DTRACEOPT_MAX 28 /* number of options */ +#define DTRACEOPT_AGGHIST 28 /* histogram aggregation output */ +#define DTRACEOPT_AGGPACK 29 /* packed aggregation output */ +#define DTRACEOPT_AGGZOOM 30 /* zoomed aggregation scaling */ +#define DTRACEOPT_ZONE 31 /* zone in which to enable probes */ +#define DTRACEOPT_MAX 32 /* number of options */ #define DTRACEOPT_UNSET (dtrace_optval_t)-2 /* unset option */ #define DTRACEOPT_BUFPOLICY_RING 0 /* ring buffer */ #define DTRACEOPT_BUFPOLICY_FILL 1 /* fill buffer, then stop */ #define DTRACEOPT_BUFPOLICY_SWITCH 2 /* switch buffers */ #define DTRACEOPT_BUFRESIZE_AUTO 0 /* automatic resizing */ #define DTRACEOPT_BUFRESIZE_MANUAL 1 /* manual resizing */ /* * DTrace Buffer Interface * * In order to get a snapshot of the principal or aggregation buffer, * user-level passes a buffer description to the kernel with the dtrace_bufdesc * structure. This describes which CPU user-level is interested in, and * where user-level wishes the kernel to snapshot the buffer to (the * dtbd_data field). The kernel uses the same structure to pass back some * information regarding the buffer: the size of data actually copied out, the * number of drops, the number of errors, the offset of the oldest record, * and the time of the snapshot. * * If the buffer policy is a "switch" policy, taking a snapshot of the * principal buffer has the additional effect of switching the active and * inactive buffers. Taking a snapshot of the aggregation buffer _always_ has * the additional effect of switching the active and inactive buffers. */ typedef struct dtrace_bufdesc { uint64_t dtbd_size; /* size of buffer */ uint32_t dtbd_cpu; /* CPU or DTRACE_CPUALL */ uint32_t dtbd_errors; /* number of errors */ uint64_t dtbd_drops; /* number of drops */ DTRACE_PTR(char, dtbd_data); /* data */ uint64_t dtbd_oldest; /* offset of oldest record */ uint64_t dtbd_timestamp; /* hrtime of snapshot */ } dtrace_bufdesc_t; /* * Each record in the buffer (dtbd_data) begins with a header that includes * the epid and a timestamp. The timestamp is split into two 4-byte parts * so that we do not require 8-byte alignment. */ typedef struct dtrace_rechdr { dtrace_epid_t dtrh_epid; /* enabled probe id */ uint32_t dtrh_timestamp_hi; /* high bits of hrtime_t */ uint32_t dtrh_timestamp_lo; /* low bits of hrtime_t */ } dtrace_rechdr_t; #define DTRACE_RECORD_LOAD_TIMESTAMP(dtrh) \ ((dtrh)->dtrh_timestamp_lo + \ ((uint64_t)(dtrh)->dtrh_timestamp_hi << 32)) #define DTRACE_RECORD_STORE_TIMESTAMP(dtrh, hrtime) { \ (dtrh)->dtrh_timestamp_lo = (uint32_t)hrtime; \ (dtrh)->dtrh_timestamp_hi = hrtime >> 32; \ } /* * DTrace Status * * The status of DTrace is relayed via the dtrace_status structure. This * structure contains members to count drops other than the capacity drops * available via the buffer interface (see above). This consists of dynamic * drops (including capacity dynamic drops, rinsing drops and dirty drops), and * speculative drops (including capacity speculative drops, drops due to busy * speculative buffers and drops due to unavailable speculative buffers). * Additionally, the status structure contains a field to indicate the number * of "fill"-policy buffers have been filled and a boolean field to indicate * that exit() has been called. If the dtst_exiting field is non-zero, no * further data will be generated until tracing is stopped (at which time any * enablings of the END action will be processed); if user-level sees that * this field is non-zero, tracing should be stopped as soon as possible. */ typedef struct dtrace_status { uint64_t dtst_dyndrops; /* dynamic drops */ uint64_t dtst_dyndrops_rinsing; /* dyn drops due to rinsing */ uint64_t dtst_dyndrops_dirty; /* dyn drops due to dirty */ uint64_t dtst_specdrops; /* speculative drops */ uint64_t dtst_specdrops_busy; /* spec drops due to busy */ uint64_t dtst_specdrops_unavail; /* spec drops due to unavail */ uint64_t dtst_errors; /* total errors */ uint64_t dtst_filled; /* number of filled bufs */ uint64_t dtst_stkstroverflows; /* stack string tab overflows */ uint64_t dtst_dblerrors; /* errors in ERROR probes */ char dtst_killed; /* non-zero if killed */ char dtst_exiting; /* non-zero if exit() called */ char dtst_pad[6]; /* pad out to 64-bit align */ } dtrace_status_t; /* * DTrace Configuration * * User-level may need to understand some elements of the kernel DTrace * configuration in order to generate correct DIF. This information is * conveyed via the dtrace_conf structure. */ typedef struct dtrace_conf { uint_t dtc_difversion; /* supported DIF version */ uint_t dtc_difintregs; /* # of DIF integer registers */ uint_t dtc_diftupregs; /* # of DIF tuple registers */ uint_t dtc_ctfmodel; /* CTF data model */ uint_t dtc_pad[8]; /* reserved for future use */ } dtrace_conf_t; /* * DTrace Faults * * The constants below DTRACEFLT_LIBRARY indicate probe processing faults; * constants at or above DTRACEFLT_LIBRARY indicate faults in probe * postprocessing at user-level. Probe processing faults induce an ERROR * probe and are replicated in unistd.d to allow users' ERROR probes to decode * the error condition using thse symbolic labels. */ #define DTRACEFLT_UNKNOWN 0 /* Unknown fault */ #define DTRACEFLT_BADADDR 1 /* Bad address */ #define DTRACEFLT_BADALIGN 2 /* Bad alignment */ #define DTRACEFLT_ILLOP 3 /* Illegal operation */ #define DTRACEFLT_DIVZERO 4 /* Divide-by-zero */ #define DTRACEFLT_NOSCRATCH 5 /* Out of scratch space */ #define DTRACEFLT_KPRIV 6 /* Illegal kernel access */ #define DTRACEFLT_UPRIV 7 /* Illegal user access */ #define DTRACEFLT_TUPOFLOW 8 /* Tuple stack overflow */ #define DTRACEFLT_BADSTACK 9 /* Bad stack */ #define DTRACEFLT_LIBRARY 1000 /* Library-level fault */ /* * DTrace Argument Types * * Because it would waste both space and time, argument types do not reside * with the probe. In order to determine argument types for args[X] * variables, the D compiler queries for argument types on a probe-by-probe * basis. (This optimizes for the common case that arguments are either not * used or used in an untyped fashion.) Typed arguments are specified with a * string of the type name in the dtragd_native member of the argument * description structure. Typed arguments may be further translated to types * of greater stability; the provider indicates such a translated argument by * filling in the dtargd_xlate member with the string of the translated type. * Finally, the provider may indicate which argument value a given argument * maps to by setting the dtargd_mapping member -- allowing a single argument * to map to multiple args[X] variables. */ typedef struct dtrace_argdesc { dtrace_id_t dtargd_id; /* probe identifier */ int dtargd_ndx; /* arg number (-1 iff none) */ int dtargd_mapping; /* value mapping */ char dtargd_native[DTRACE_ARGTYPELEN]; /* native type name */ char dtargd_xlate[DTRACE_ARGTYPELEN]; /* translated type name */ } dtrace_argdesc_t; /* * DTrace Stability Attributes * * Each DTrace provider advertises the name and data stability of each of its * probe description components, as well as its architectural dependencies. * The D compiler can query the provider attributes (dtrace_pattr_t below) in * order to compute the properties of an input program and report them. */ typedef uint8_t dtrace_stability_t; /* stability code (see attributes(5)) */ typedef uint8_t dtrace_class_t; /* architectural dependency class */ #define DTRACE_STABILITY_INTERNAL 0 /* private to DTrace itself */ #define DTRACE_STABILITY_PRIVATE 1 /* private to Sun (see docs) */ #define DTRACE_STABILITY_OBSOLETE 2 /* scheduled for removal */ #define DTRACE_STABILITY_EXTERNAL 3 /* not controlled by Sun */ #define DTRACE_STABILITY_UNSTABLE 4 /* new or rapidly changing */ #define DTRACE_STABILITY_EVOLVING 5 /* less rapidly changing */ #define DTRACE_STABILITY_STABLE 6 /* mature interface from Sun */ #define DTRACE_STABILITY_STANDARD 7 /* industry standard */ #define DTRACE_STABILITY_MAX 7 /* maximum valid stability */ #define DTRACE_CLASS_UNKNOWN 0 /* unknown architectural dependency */ #define DTRACE_CLASS_CPU 1 /* CPU-module-specific */ #define DTRACE_CLASS_PLATFORM 2 /* platform-specific (uname -i) */ #define DTRACE_CLASS_GROUP 3 /* hardware-group-specific (uname -m) */ #define DTRACE_CLASS_ISA 4 /* ISA-specific (uname -p) */ #define DTRACE_CLASS_COMMON 5 /* common to all systems */ #define DTRACE_CLASS_MAX 5 /* maximum valid class */ #define DTRACE_PRIV_NONE 0x0000 #define DTRACE_PRIV_KERNEL 0x0001 #define DTRACE_PRIV_USER 0x0002 #define DTRACE_PRIV_PROC 0x0004 #define DTRACE_PRIV_OWNER 0x0008 #define DTRACE_PRIV_ZONEOWNER 0x0010 #define DTRACE_PRIV_ALL \ (DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER | \ DTRACE_PRIV_PROC | DTRACE_PRIV_OWNER | DTRACE_PRIV_ZONEOWNER) typedef struct dtrace_ppriv { uint32_t dtpp_flags; /* privilege flags */ uid_t dtpp_uid; /* user ID */ zoneid_t dtpp_zoneid; /* zone ID */ } dtrace_ppriv_t; typedef struct dtrace_attribute { dtrace_stability_t dtat_name; /* entity name stability */ dtrace_stability_t dtat_data; /* entity data stability */ dtrace_class_t dtat_class; /* entity data dependency */ } dtrace_attribute_t; typedef struct dtrace_pattr { dtrace_attribute_t dtpa_provider; /* provider attributes */ dtrace_attribute_t dtpa_mod; /* module attributes */ dtrace_attribute_t dtpa_func; /* function attributes */ dtrace_attribute_t dtpa_name; /* name attributes */ dtrace_attribute_t dtpa_args; /* args[] attributes */ } dtrace_pattr_t; typedef struct dtrace_providerdesc { char dtvd_name[DTRACE_PROVNAMELEN]; /* provider name */ dtrace_pattr_t dtvd_attr; /* stability attributes */ dtrace_ppriv_t dtvd_priv; /* privileges required */ } dtrace_providerdesc_t; /* * DTrace Pseudodevice Interface * * DTrace is controlled through ioctl(2)'s to the in-kernel dtrace:dtrace * pseudodevice driver. These ioctls comprise the user-kernel interface to * DTrace. */ #if defined(sun) #define DTRACEIOC (('d' << 24) | ('t' << 16) | ('r' << 8)) #define DTRACEIOC_PROVIDER (DTRACEIOC | 1) /* provider query */ #define DTRACEIOC_PROBES (DTRACEIOC | 2) /* probe query */ #define DTRACEIOC_BUFSNAP (DTRACEIOC | 4) /* snapshot buffer */ #define DTRACEIOC_PROBEMATCH (DTRACEIOC | 5) /* match probes */ #define DTRACEIOC_ENABLE (DTRACEIOC | 6) /* enable probes */ #define DTRACEIOC_AGGSNAP (DTRACEIOC | 7) /* snapshot agg. */ #define DTRACEIOC_EPROBE (DTRACEIOC | 8) /* get eprobe desc. */ #define DTRACEIOC_PROBEARG (DTRACEIOC | 9) /* get probe arg */ #define DTRACEIOC_CONF (DTRACEIOC | 10) /* get config. */ #define DTRACEIOC_STATUS (DTRACEIOC | 11) /* get status */ #define DTRACEIOC_GO (DTRACEIOC | 12) /* start tracing */ #define DTRACEIOC_STOP (DTRACEIOC | 13) /* stop tracing */ #define DTRACEIOC_AGGDESC (DTRACEIOC | 15) /* get agg. desc. */ #define DTRACEIOC_FORMAT (DTRACEIOC | 16) /* get format str */ #define DTRACEIOC_DOFGET (DTRACEIOC | 17) /* get DOF */ #define DTRACEIOC_REPLICATE (DTRACEIOC | 18) /* replicate enab */ #else #define DTRACEIOC_PROVIDER _IOWR('x',1,dtrace_providerdesc_t) /* provider query */ #define DTRACEIOC_PROBES _IOWR('x',2,dtrace_probedesc_t) /* probe query */ #define DTRACEIOC_BUFSNAP _IOW('x',4,dtrace_bufdesc_t *) /* snapshot buffer */ #define DTRACEIOC_PROBEMATCH _IOWR('x',5,dtrace_probedesc_t) /* match probes */ typedef struct { void *dof; /* DOF userland address written to driver. */ int n_matched; /* # matches returned by driver. */ } dtrace_enable_io_t; #define DTRACEIOC_ENABLE _IOWR('x',6,dtrace_enable_io_t) /* enable probes */ #define DTRACEIOC_AGGSNAP _IOW('x',7,dtrace_bufdesc_t *) /* snapshot agg. */ #define DTRACEIOC_EPROBE _IOW('x',8,dtrace_eprobedesc_t) /* get eprobe desc. */ #define DTRACEIOC_PROBEARG _IOWR('x',9,dtrace_argdesc_t) /* get probe arg */ #define DTRACEIOC_CONF _IOR('x',10,dtrace_conf_t) /* get config. */ #define DTRACEIOC_STATUS _IOR('x',11,dtrace_status_t) /* get status */ #define DTRACEIOC_GO _IOR('x',12,processorid_t) /* start tracing */ #define DTRACEIOC_STOP _IOWR('x',13,processorid_t) /* stop tracing */ #define DTRACEIOC_AGGDESC _IOW('x',15,dtrace_aggdesc_t *) /* get agg. desc. */ #define DTRACEIOC_FORMAT _IOWR('x',16,dtrace_fmtdesc_t) /* get format str */ #define DTRACEIOC_DOFGET _IOW('x',17,dof_hdr_t *) /* get DOF */ #define DTRACEIOC_REPLICATE _IOW('x',18,dtrace_repldesc_t) /* replicate enab */ #endif /* * DTrace Helpers * * In general, DTrace establishes probes in processes and takes actions on * processes without knowing their specific user-level structures. Instead of * existing in the framework, process-specific knowledge is contained by the * enabling D program -- which can apply process-specific knowledge by making * appropriate use of DTrace primitives like copyin() and copyinstr() to * operate on user-level data. However, there may exist some specific probes * of particular semantic relevance that the application developer may wish to * explicitly export. For example, an application may wish to export a probe * at the point that it begins and ends certain well-defined transactions. In * addition to providing probes, programs may wish to offer assistance for * certain actions. For example, in highly dynamic environments (e.g., Java), * it may be difficult to obtain a stack trace in terms of meaningful symbol * names (the translation from instruction addresses to corresponding symbol * names may only be possible in situ); these environments may wish to define * a series of actions to be applied in situ to obtain a meaningful stack * trace. * * These two mechanisms -- user-level statically defined tracing and assisting * DTrace actions -- are provided via DTrace _helpers_. Helpers are specified * via DOF, but unlike enabling DOF, helper DOF may contain definitions of * providers, probes and their arguments. If a helper wishes to provide * action assistance, probe descriptions and corresponding DIF actions may be * specified in the helper DOF. For such helper actions, however, the probe * description describes the specific helper: all DTrace helpers have the * provider name "dtrace" and the module name "helper", and the name of the * helper is contained in the function name (for example, the ustack() helper * is named "ustack"). Any helper-specific name may be contained in the name * (for example, if a helper were to have a constructor, it might be named * "dtrace:helper::init"). Helper actions are only called when the * action that they are helping is taken. Helper actions may only return DIF * expressions, and may only call the following subroutines: * * alloca() <= Allocates memory out of the consumer's scratch space * bcopy() <= Copies memory to scratch space * copyin() <= Copies memory from user-level into consumer's scratch * copyinto() <= Copies memory into a specific location in scratch * copyinstr() <= Copies a string into a specific location in scratch * * Helper actions may only access the following built-in variables: * * curthread <= Current kthread_t pointer * tid <= Current thread identifier * pid <= Current process identifier * ppid <= Parent process identifier * uid <= Current user ID * gid <= Current group ID * execname <= Current executable name * zonename <= Current zone name * * Helper actions may not manipulate or allocate dynamic variables, but they * may have clause-local and statically-allocated global variables. The * helper action variable state is specific to the helper action -- variables * used by the helper action may not be accessed outside of the helper * action, and the helper action may not access variables that like outside * of it. Helper actions may not load from kernel memory at-large; they are * restricting to loading current user state (via copyin() and variants) and * scratch space. As with probe enablings, helper actions are executed in * program order. The result of the helper action is the result of the last * executing helper expression. * * Helpers -- composed of either providers/probes or probes/actions (or both) * -- are added by opening the "helper" minor node, and issuing an ioctl(2) * (DTRACEHIOC_ADDDOF) that specifies the dof_helper_t structure. This * encapsulates the name and base address of the user-level library or * executable publishing the helpers and probes as well as the DOF that * contains the definitions of those helpers and probes. * * The DTRACEHIOC_ADD and DTRACEHIOC_REMOVE are left in place for legacy * helpers and should no longer be used. No other ioctls are valid on the * helper minor node. */ #if defined(sun) #define DTRACEHIOC (('d' << 24) | ('t' << 16) | ('h' << 8)) #define DTRACEHIOC_ADD (DTRACEHIOC | 1) /* add helper */ #define DTRACEHIOC_REMOVE (DTRACEHIOC | 2) /* remove helper */ #define DTRACEHIOC_ADDDOF (DTRACEHIOC | 3) /* add helper DOF */ #else #define DTRACEHIOC_ADD _IOWR('z', 1, dof_hdr_t)/* add helper */ #define DTRACEHIOC_REMOVE _IOW('z', 2, int) /* remove helper */ #define DTRACEHIOC_ADDDOF _IOWR('z', 3, dof_helper_t)/* add helper DOF */ #endif typedef struct dof_helper { char dofhp_mod[DTRACE_MODNAMELEN]; /* executable or library name */ uint64_t dofhp_addr; /* base address of object */ uint64_t dofhp_dof; /* address of helper DOF */ #if !defined(sun) int gen; #endif } dof_helper_t; #define DTRACEMNR_DTRACE "dtrace" /* node for DTrace ops */ #define DTRACEMNR_HELPER "helper" /* node for helpers */ #define DTRACEMNRN_DTRACE 0 /* minor for DTrace ops */ #define DTRACEMNRN_HELPER 1 /* minor for helpers */ #define DTRACEMNRN_CLONE 2 /* first clone minor */ #ifdef _KERNEL /* * DTrace Provider API * * The following functions are implemented by the DTrace framework and are * used to implement separate in-kernel DTrace providers. Common functions * are provided in uts/common/os/dtrace.c. ISA-dependent subroutines are * defined in uts//dtrace/dtrace_asm.s or uts//dtrace/dtrace_isa.c. * * The provider API has two halves: the API that the providers consume from * DTrace, and the API that providers make available to DTrace. * * 1 Framework-to-Provider API * * 1.1 Overview * * The Framework-to-Provider API is represented by the dtrace_pops structure * that the provider passes to the framework when registering itself. This * structure consists of the following members: * * dtps_provide() <-- Provide all probes, all modules * dtps_provide_module() <-- Provide all probes in specified module * dtps_enable() <-- Enable specified probe * dtps_disable() <-- Disable specified probe * dtps_suspend() <-- Suspend specified probe * dtps_resume() <-- Resume specified probe * dtps_getargdesc() <-- Get the argument description for args[X] * dtps_getargval() <-- Get the value for an argX or args[X] variable * dtps_usermode() <-- Find out if the probe was fired in user mode * dtps_destroy() <-- Destroy all state associated with this probe * * 1.2 void dtps_provide(void *arg, const dtrace_probedesc_t *spec) * * 1.2.1 Overview * * Called to indicate that the provider should provide all probes. If the * specified description is non-NULL, dtps_provide() is being called because * no probe matched a specified probe -- if the provider has the ability to * create custom probes, it may wish to create a probe that matches the * specified description. * * 1.2.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is a pointer to a probe description that the provider may * wish to consider when creating custom probes. The provider is expected to * call back into the DTrace framework via dtrace_probe_create() to create * any necessary probes. dtps_provide() may be called even if the provider * has made available all probes; the provider should check the return value * of dtrace_probe_create() to handle this case. Note that the provider need * not implement both dtps_provide() and dtps_provide_module(); see * "Arguments and Notes" for dtrace_register(), below. * * 1.2.3 Return value * * None. * * 1.2.4 Caller's context * * dtps_provide() is typically called from open() or ioctl() context, but may * be called from other contexts as well. The DTrace framework is locked in * such a way that providers may not register or unregister. This means that * the provider may not call any DTrace API that affects its registration with * the framework, including dtrace_register(), dtrace_unregister(), * dtrace_invalidate(), and dtrace_condense(). However, the context is such * that the provider may (and indeed, is expected to) call probe-related * DTrace routines, including dtrace_probe_create(), dtrace_probe_lookup(), * and dtrace_probe_arg(). * * 1.3 void dtps_provide_module(void *arg, modctl_t *mp) * * 1.3.1 Overview * * Called to indicate that the provider should provide all probes in the * specified module. * * 1.3.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is a pointer to a modctl structure that indicates the * module for which probes should be created. * * 1.3.3 Return value * * None. * * 1.3.4 Caller's context * * dtps_provide_module() may be called from open() or ioctl() context, but * may also be called from a module loading context. mod_lock is held, and * the DTrace framework is locked in such a way that providers may not * register or unregister. This means that the provider may not call any * DTrace API that affects its registration with the framework, including * dtrace_register(), dtrace_unregister(), dtrace_invalidate(), and * dtrace_condense(). However, the context is such that the provider may (and * indeed, is expected to) call probe-related DTrace routines, including * dtrace_probe_create(), dtrace_probe_lookup(), and dtrace_probe_arg(). Note * that the provider need not implement both dtps_provide() and * dtps_provide_module(); see "Arguments and Notes" for dtrace_register(), * below. * * 1.4 void dtps_enable(void *arg, dtrace_id_t id, void *parg) * * 1.4.1 Overview * * Called to enable the specified probe. * * 1.4.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is the identifier of the probe to be enabled. The third * argument is the probe argument as passed to dtrace_probe_create(). * dtps_enable() will be called when a probe transitions from not being * enabled at all to having one or more ECB. The number of ECBs associated * with the probe may change without subsequent calls into the provider. * When the number of ECBs drops to zero, the provider will be explicitly * told to disable the probe via dtps_disable(). dtrace_probe() should never * be called for a probe identifier that hasn't been explicitly enabled via * dtps_enable(). * * 1.4.3 Return value * * None. * * 1.4.4 Caller's context * * The DTrace framework is locked in such a way that it may not be called * back into at all. cpu_lock is held. mod_lock is not held and may not * be acquired. * * 1.5 void dtps_disable(void *arg, dtrace_id_t id, void *parg) * * 1.5.1 Overview * * Called to disable the specified probe. * * 1.5.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is the identifier of the probe to be disabled. The third * argument is the probe argument as passed to dtrace_probe_create(). * dtps_disable() will be called when a probe transitions from being enabled * to having zero ECBs. dtrace_probe() should never be called for a probe * identifier that has been explicitly enabled via dtps_disable(). * * 1.5.3 Return value * * None. * * 1.5.4 Caller's context * * The DTrace framework is locked in such a way that it may not be called * back into at all. cpu_lock is held. mod_lock is not held and may not * be acquired. * * 1.6 void dtps_suspend(void *arg, dtrace_id_t id, void *parg) * * 1.6.1 Overview * * Called to suspend the specified enabled probe. This entry point is for * providers that may need to suspend some or all of their probes when CPUs * are being powered on or when the boot monitor is being entered for a * prolonged period of time. * * 1.6.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is the identifier of the probe to be suspended. The * third argument is the probe argument as passed to dtrace_probe_create(). * dtps_suspend will only be called on an enabled probe. Providers that * provide a dtps_suspend entry point will want to take roughly the action * that it takes for dtps_disable. * * 1.6.3 Return value * * None. * * 1.6.4 Caller's context * * Interrupts are disabled. The DTrace framework is in a state such that the * specified probe cannot be disabled or destroyed for the duration of * dtps_suspend(). As interrupts are disabled, the provider is afforded * little latitude; the provider is expected to do no more than a store to * memory. * * 1.7 void dtps_resume(void *arg, dtrace_id_t id, void *parg) * * 1.7.1 Overview * * Called to resume the specified enabled probe. This entry point is for * providers that may need to resume some or all of their probes after the * completion of an event that induced a call to dtps_suspend(). * * 1.7.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is the identifier of the probe to be resumed. The * third argument is the probe argument as passed to dtrace_probe_create(). * dtps_resume will only be called on an enabled probe. Providers that * provide a dtps_resume entry point will want to take roughly the action * that it takes for dtps_enable. * * 1.7.3 Return value * * None. * * 1.7.4 Caller's context * * Interrupts are disabled. The DTrace framework is in a state such that the * specified probe cannot be disabled or destroyed for the duration of * dtps_resume(). As interrupts are disabled, the provider is afforded * little latitude; the provider is expected to do no more than a store to * memory. * * 1.8 void dtps_getargdesc(void *arg, dtrace_id_t id, void *parg, * dtrace_argdesc_t *desc) * * 1.8.1 Overview * * Called to retrieve the argument description for an args[X] variable. * * 1.8.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is the identifier of the current probe. The third * argument is the probe argument as passed to dtrace_probe_create(). The * fourth argument is a pointer to the argument description. This * description is both an input and output parameter: it contains the * index of the desired argument in the dtargd_ndx field, and expects * the other fields to be filled in upon return. If there is no argument * corresponding to the specified index, the dtargd_ndx field should be set * to DTRACE_ARGNONE. * * 1.8.3 Return value * * None. The dtargd_ndx, dtargd_native, dtargd_xlate and dtargd_mapping * members of the dtrace_argdesc_t structure are all output values. * * 1.8.4 Caller's context * * dtps_getargdesc() is called from ioctl() context. mod_lock is held, and * the DTrace framework is locked in such a way that providers may not * register or unregister. This means that the provider may not call any * DTrace API that affects its registration with the framework, including * dtrace_register(), dtrace_unregister(), dtrace_invalidate(), and * dtrace_condense(). * * 1.9 uint64_t dtps_getargval(void *arg, dtrace_id_t id, void *parg, * int argno, int aframes) * * 1.9.1 Overview * * Called to retrieve a value for an argX or args[X] variable. * * 1.9.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is the identifier of the current probe. The third * argument is the probe argument as passed to dtrace_probe_create(). The * fourth argument is the number of the argument (the X in the example in * 1.9.1). The fifth argument is the number of stack frames that were used * to get from the actual place in the code that fired the probe to * dtrace_probe() itself, the so-called artificial frames. This argument may * be used to descend an appropriate number of frames to find the correct * values. If this entry point is left NULL, the dtrace_getarg() built-in * function is used. * * 1.9.3 Return value * * The value of the argument. * * 1.9.4 Caller's context * * This is called from within dtrace_probe() meaning that interrupts * are disabled. No locks should be taken within this entry point. * * 1.10 int dtps_usermode(void *arg, dtrace_id_t id, void *parg) * * 1.10.1 Overview * * Called to determine if the probe was fired in a user context. * * 1.10.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is the identifier of the current probe. The third * argument is the probe argument as passed to dtrace_probe_create(). This * entry point must not be left NULL for providers whose probes allow for * mixed mode tracing, that is to say those probes that can fire during * kernel- _or_ user-mode execution * * 1.10.3 Return value * - * A boolean value. + * A bitwise OR that encapsulates both the mode (either DTRACE_MODE_KERNEL + * or DTRACE_MODE_USER) and the policy when the privilege of the enabling + * is insufficient for that mode (a combination of DTRACE_MODE_NOPRIV_DROP, + * DTRACE_MODE_NOPRIV_RESTRICT, and DTRACE_MODE_LIMITEDPRIV_RESTRICT). If + * DTRACE_MODE_NOPRIV_DROP bit is set, insufficient privilege will result + * in the probe firing being silently ignored for the enabling; if the + * DTRACE_NODE_NOPRIV_RESTRICT bit is set, insufficient privilege will not + * prevent probe processing for the enabling, but restrictions will be in + * place that induce a UPRIV fault upon attempt to examine probe arguments + * or current process state. If the DTRACE_MODE_LIMITEDPRIV_RESTRICT bit + * is set, similar restrictions will be placed upon operation if the + * privilege is sufficient to process the enabling, but does not otherwise + * entitle the enabling to all zones. The DTRACE_MODE_NOPRIV_DROP and + * DTRACE_MODE_NOPRIV_RESTRICT are mutually exclusive (and one of these + * two policies must be specified), but either may be combined (or not) + * with DTRACE_MODE_LIMITEDPRIV_RESTRICT. * * 1.10.4 Caller's context * * This is called from within dtrace_probe() meaning that interrupts * are disabled. No locks should be taken within this entry point. * * 1.11 void dtps_destroy(void *arg, dtrace_id_t id, void *parg) * * 1.11.1 Overview * * Called to destroy the specified probe. * * 1.11.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_register(). The * second argument is the identifier of the probe to be destroyed. The third * argument is the probe argument as passed to dtrace_probe_create(). The * provider should free all state associated with the probe. The framework * guarantees that dtps_destroy() is only called for probes that have either * been disabled via dtps_disable() or were never enabled via dtps_enable(). * Once dtps_disable() has been called for a probe, no further call will be * made specifying the probe. * * 1.11.3 Return value * * None. * * 1.11.4 Caller's context * * The DTrace framework is locked in such a way that it may not be called * back into at all. mod_lock is held. cpu_lock is not held, and may not be * acquired. * * * 2 Provider-to-Framework API * * 2.1 Overview * * The Provider-to-Framework API provides the mechanism for the provider to * register itself with the DTrace framework, to create probes, to lookup * probes and (most importantly) to fire probes. The Provider-to-Framework * consists of: * * dtrace_register() <-- Register a provider with the DTrace framework * dtrace_unregister() <-- Remove a provider's DTrace registration * dtrace_invalidate() <-- Invalidate the specified provider * dtrace_condense() <-- Remove a provider's unenabled probes * dtrace_attached() <-- Indicates whether or not DTrace has attached * dtrace_probe_create() <-- Create a DTrace probe * dtrace_probe_lookup() <-- Lookup a DTrace probe based on its name * dtrace_probe_arg() <-- Return the probe argument for a specific probe * dtrace_probe() <-- Fire the specified probe * * 2.2 int dtrace_register(const char *name, const dtrace_pattr_t *pap, * uint32_t priv, cred_t *cr, const dtrace_pops_t *pops, void *arg, * dtrace_provider_id_t *idp) * * 2.2.1 Overview * * dtrace_register() registers the calling provider with the DTrace * framework. It should generally be called by DTrace providers in their * attach(9E) entry point. * * 2.2.2 Arguments and Notes * * The first argument is the name of the provider. The second argument is a * pointer to the stability attributes for the provider. The third argument * is the privilege flags for the provider, and must be some combination of: * * DTRACE_PRIV_NONE <= All users may enable probes from this provider * * DTRACE_PRIV_PROC <= Any user with privilege of PRIV_DTRACE_PROC may * enable probes from this provider * * DTRACE_PRIV_USER <= Any user with privilege of PRIV_DTRACE_USER may * enable probes from this provider * * DTRACE_PRIV_KERNEL <= Any user with privilege of PRIV_DTRACE_KERNEL * may enable probes from this provider * * DTRACE_PRIV_OWNER <= This flag places an additional constraint on * the privilege requirements above. These probes * require either (a) a user ID matching the user * ID of the cred passed in the fourth argument * or (b) the PRIV_PROC_OWNER privilege. * * DTRACE_PRIV_ZONEOWNER<= This flag places an additional constraint on * the privilege requirements above. These probes * require either (a) a zone ID matching the zone * ID of the cred passed in the fourth argument * or (b) the PRIV_PROC_ZONE privilege. * * Note that these flags designate the _visibility_ of the probes, not * the conditions under which they may or may not fire. * * The fourth argument is the credential that is associated with the * provider. This argument should be NULL if the privilege flags don't * include DTRACE_PRIV_OWNER or DTRACE_PRIV_ZONEOWNER. If non-NULL, the * framework stashes the uid and zoneid represented by this credential * for use at probe-time, in implicit predicates. These limit visibility * of the probes to users and/or zones which have sufficient privilege to * access them. * * The fifth argument is a DTrace provider operations vector, which provides * the implementation for the Framework-to-Provider API. (See Section 1, * above.) This must be non-NULL, and each member must be non-NULL. The * exceptions to this are (1) the dtps_provide() and dtps_provide_module() * members (if the provider so desires, _one_ of these members may be left * NULL -- denoting that the provider only implements the other) and (2) * the dtps_suspend() and dtps_resume() members, which must either both be * NULL or both be non-NULL. * * The sixth argument is a cookie to be specified as the first argument for * each function in the Framework-to-Provider API. This argument may have * any value. * * The final argument is a pointer to dtrace_provider_id_t. If * dtrace_register() successfully completes, the provider identifier will be * stored in the memory pointed to be this argument. This argument must be * non-NULL. * * 2.2.3 Return value * * On success, dtrace_register() returns 0 and stores the new provider's * identifier into the memory pointed to by the idp argument. On failure, * dtrace_register() returns an errno: * * EINVAL The arguments passed to dtrace_register() were somehow invalid. * This may because a parameter that must be non-NULL was NULL, * because the name was invalid (either empty or an illegal * provider name) or because the attributes were invalid. * * No other failure code is returned. * * 2.2.4 Caller's context * * dtrace_register() may induce calls to dtrace_provide(); the provider must * hold no locks across dtrace_register() that may also be acquired by * dtrace_provide(). cpu_lock and mod_lock must not be held. * * 2.3 int dtrace_unregister(dtrace_provider_t id) * * 2.3.1 Overview * * Unregisters the specified provider from the DTrace framework. It should * generally be called by DTrace providers in their detach(9E) entry point. * * 2.3.2 Arguments and Notes * * The only argument is the provider identifier, as returned from a * successful call to dtrace_register(). As a result of calling * dtrace_unregister(), the DTrace framework will call back into the provider * via the dtps_destroy() entry point. Once dtrace_unregister() successfully * completes, however, the DTrace framework will no longer make calls through * the Framework-to-Provider API. * * 2.3.3 Return value * * On success, dtrace_unregister returns 0. On failure, dtrace_unregister() * returns an errno: * * EBUSY There are currently processes that have the DTrace pseudodevice * open, or there exists an anonymous enabling that hasn't yet * been claimed. * * No other failure code is returned. * * 2.3.4 Caller's context * * Because a call to dtrace_unregister() may induce calls through the * Framework-to-Provider API, the caller may not hold any lock across * dtrace_register() that is also acquired in any of the Framework-to- * Provider API functions. Additionally, mod_lock may not be held. * * 2.4 void dtrace_invalidate(dtrace_provider_id_t id) * * 2.4.1 Overview * * Invalidates the specified provider. All subsequent probe lookups for the * specified provider will fail, but its probes will not be removed. * * 2.4.2 Arguments and note * * The only argument is the provider identifier, as returned from a * successful call to dtrace_register(). In general, a provider's probes * always remain valid; dtrace_invalidate() is a mechanism for invalidating * an entire provider, regardless of whether or not probes are enabled or * not. Note that dtrace_invalidate() will _not_ prevent already enabled * probes from firing -- it will merely prevent any new enablings of the * provider's probes. * * 2.5 int dtrace_condense(dtrace_provider_id_t id) * * 2.5.1 Overview * * Removes all the unenabled probes for the given provider. This function is * not unlike dtrace_unregister(), except that it doesn't remove the * provider just as many of its associated probes as it can. * * 2.5.2 Arguments and Notes * * As with dtrace_unregister(), the sole argument is the provider identifier * as returned from a successful call to dtrace_register(). As a result of * calling dtrace_condense(), the DTrace framework will call back into the * given provider's dtps_destroy() entry point for each of the provider's * unenabled probes. * * 2.5.3 Return value * * Currently, dtrace_condense() always returns 0. However, consumers of this * function should check the return value as appropriate; its behavior may * change in the future. * * 2.5.4 Caller's context * * As with dtrace_unregister(), the caller may not hold any lock across * dtrace_condense() that is also acquired in the provider's entry points. * Also, mod_lock may not be held. * * 2.6 int dtrace_attached() * * 2.6.1 Overview * * Indicates whether or not DTrace has attached. * * 2.6.2 Arguments and Notes * * For most providers, DTrace makes initial contact beyond registration. * That is, once a provider has registered with DTrace, it waits to hear * from DTrace to create probes. However, some providers may wish to * proactively create probes without first being told by DTrace to do so. * If providers wish to do this, they must first call dtrace_attached() to * determine if DTrace itself has attached. If dtrace_attached() returns 0, * the provider must not make any other Provider-to-Framework API call. * * 2.6.3 Return value * * dtrace_attached() returns 1 if DTrace has attached, 0 otherwise. * * 2.7 int dtrace_probe_create(dtrace_provider_t id, const char *mod, * const char *func, const char *name, int aframes, void *arg) * * 2.7.1 Overview * * Creates a probe with specified module name, function name, and name. * * 2.7.2 Arguments and Notes * * The first argument is the provider identifier, as returned from a * successful call to dtrace_register(). The second, third, and fourth * arguments are the module name, function name, and probe name, * respectively. Of these, module name and function name may both be NULL * (in which case the probe is considered to be unanchored), or they may both * be non-NULL. The name must be non-NULL, and must point to a non-empty * string. * * The fifth argument is the number of artificial stack frames that will be * found on the stack when dtrace_probe() is called for the new probe. These * artificial frames will be automatically be pruned should the stack() or * stackdepth() functions be called as part of one of the probe's ECBs. If * the parameter doesn't add an artificial frame, this parameter should be * zero. * * The final argument is a probe argument that will be passed back to the * provider when a probe-specific operation is called. (e.g., via * dtps_enable(), dtps_disable(), etc.) * * Note that it is up to the provider to be sure that the probe that it * creates does not already exist -- if the provider is unsure of the probe's * existence, it should assure its absence with dtrace_probe_lookup() before * calling dtrace_probe_create(). * * 2.7.3 Return value * * dtrace_probe_create() always succeeds, and always returns the identifier * of the newly-created probe. * * 2.7.4 Caller's context * * While dtrace_probe_create() is generally expected to be called from * dtps_provide() and/or dtps_provide_module(), it may be called from other * non-DTrace contexts. Neither cpu_lock nor mod_lock may be held. * * 2.8 dtrace_id_t dtrace_probe_lookup(dtrace_provider_t id, const char *mod, * const char *func, const char *name) * * 2.8.1 Overview * * Looks up a probe based on provdider and one or more of module name, * function name and probe name. * * 2.8.2 Arguments and Notes * * The first argument is the provider identifier, as returned from a * successful call to dtrace_register(). The second, third, and fourth * arguments are the module name, function name, and probe name, * respectively. Any of these may be NULL; dtrace_probe_lookup() will return * the identifier of the first probe that is provided by the specified * provider and matches all of the non-NULL matching criteria. * dtrace_probe_lookup() is generally used by a provider to be check the * existence of a probe before creating it with dtrace_probe_create(). * * 2.8.3 Return value * * If the probe exists, returns its identifier. If the probe does not exist, * return DTRACE_IDNONE. * * 2.8.4 Caller's context * * While dtrace_probe_lookup() is generally expected to be called from * dtps_provide() and/or dtps_provide_module(), it may also be called from * other non-DTrace contexts. Neither cpu_lock nor mod_lock may be held. * * 2.9 void *dtrace_probe_arg(dtrace_provider_t id, dtrace_id_t probe) * * 2.9.1 Overview * * Returns the probe argument associated with the specified probe. * * 2.9.2 Arguments and Notes * * The first argument is the provider identifier, as returned from a * successful call to dtrace_register(). The second argument is a probe * identifier, as returned from dtrace_probe_lookup() or * dtrace_probe_create(). This is useful if a probe has multiple * provider-specific components to it: the provider can create the probe * once with provider-specific state, and then add to the state by looking * up the probe based on probe identifier. * * 2.9.3 Return value * * Returns the argument associated with the specified probe. If the * specified probe does not exist, or if the specified probe is not provided * by the specified provider, NULL is returned. * * 2.9.4 Caller's context * * While dtrace_probe_arg() is generally expected to be called from * dtps_provide() and/or dtps_provide_module(), it may also be called from * other non-DTrace contexts. Neither cpu_lock nor mod_lock may be held. * * 2.10 void dtrace_probe(dtrace_id_t probe, uintptr_t arg0, uintptr_t arg1, * uintptr_t arg2, uintptr_t arg3, uintptr_t arg4) * * 2.10.1 Overview * * The epicenter of DTrace: fires the specified probes with the specified * arguments. * * 2.10.2 Arguments and Notes * * The first argument is a probe identifier as returned by * dtrace_probe_create() or dtrace_probe_lookup(). The second through sixth * arguments are the values to which the D variables "arg0" through "arg4" * will be mapped. * * dtrace_probe() should be called whenever the specified probe has fired -- * however the provider defines it. * * 2.10.3 Return value * * None. * * 2.10.4 Caller's context * * dtrace_probe() may be called in virtually any context: kernel, user, * interrupt, high-level interrupt, with arbitrary adaptive locks held, with * dispatcher locks held, with interrupts disabled, etc. The only latitude * that must be afforded to DTrace is the ability to make calls within * itself (and to its in-kernel subroutines) and the ability to access * arbitrary (but mapped) memory. On some platforms, this constrains * context. For example, on UltraSPARC, dtrace_probe() cannot be called * from any context in which TL is greater than zero. dtrace_probe() may * also not be called from any routine which may be called by dtrace_probe() * -- which includes functions in the DTrace framework and some in-kernel * DTrace subroutines. All such functions "dtrace_"; providers that * instrument the kernel arbitrarily should be sure to not instrument these * routines. */ typedef struct dtrace_pops { void (*dtps_provide)(void *arg, dtrace_probedesc_t *spec); void (*dtps_provide_module)(void *arg, modctl_t *mp); void (*dtps_enable)(void *arg, dtrace_id_t id, void *parg); void (*dtps_disable)(void *arg, dtrace_id_t id, void *parg); void (*dtps_suspend)(void *arg, dtrace_id_t id, void *parg); void (*dtps_resume)(void *arg, dtrace_id_t id, void *parg); void (*dtps_getargdesc)(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc); uint64_t (*dtps_getargval)(void *arg, dtrace_id_t id, void *parg, int argno, int aframes); int (*dtps_usermode)(void *arg, dtrace_id_t id, void *parg); void (*dtps_destroy)(void *arg, dtrace_id_t id, void *parg); } dtrace_pops_t; +#define DTRACE_MODE_KERNEL 0x01 +#define DTRACE_MODE_USER 0x02 +#define DTRACE_MODE_NOPRIV_DROP 0x10 +#define DTRACE_MODE_NOPRIV_RESTRICT 0x20 +#define DTRACE_MODE_LIMITEDPRIV_RESTRICT 0x40 + typedef uintptr_t dtrace_provider_id_t; extern int dtrace_register(const char *, const dtrace_pattr_t *, uint32_t, cred_t *, const dtrace_pops_t *, void *, dtrace_provider_id_t *); extern int dtrace_unregister(dtrace_provider_id_t); extern int dtrace_condense(dtrace_provider_id_t); extern void dtrace_invalidate(dtrace_provider_id_t); extern dtrace_id_t dtrace_probe_lookup(dtrace_provider_id_t, char *, char *, char *); extern dtrace_id_t dtrace_probe_create(dtrace_provider_id_t, const char *, const char *, const char *, int, void *); extern void *dtrace_probe_arg(dtrace_provider_id_t, dtrace_id_t); extern void dtrace_probe(dtrace_id_t, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); /* * DTrace Meta Provider API * * The following functions are implemented by the DTrace framework and are * used to implement meta providers. Meta providers plug into the DTrace * framework and are used to instantiate new providers on the fly. At * present, there is only one type of meta provider and only one meta * provider may be registered with the DTrace framework at a time. The * sole meta provider type provides user-land static tracing facilities * by taking meta probe descriptions and adding a corresponding provider * into the DTrace framework. * * 1 Framework-to-Provider * * 1.1 Overview * * The Framework-to-Provider API is represented by the dtrace_mops structure * that the meta provider passes to the framework when registering itself as * a meta provider. This structure consists of the following members: * * dtms_create_probe() <-- Add a new probe to a created provider * dtms_provide_pid() <-- Create a new provider for a given process * dtms_remove_pid() <-- Remove a previously created provider * * 1.2 void dtms_create_probe(void *arg, void *parg, * dtrace_helper_probedesc_t *probedesc); * * 1.2.1 Overview * * Called by the DTrace framework to create a new probe in a provider * created by this meta provider. * * 1.2.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_meta_register(). * The second argument is the provider cookie for the associated provider; * this is obtained from the return value of dtms_provide_pid(). The third * argument is the helper probe description. * * 1.2.3 Return value * * None * * 1.2.4 Caller's context * * dtms_create_probe() is called from either ioctl() or module load context. * The DTrace framework is locked in such a way that meta providers may not * register or unregister. This means that the meta provider cannot call * dtrace_meta_register() or dtrace_meta_unregister(). However, the context is * such that the provider may (and is expected to) call provider-related * DTrace provider APIs including dtrace_probe_create(). * * 1.3 void *dtms_provide_pid(void *arg, dtrace_meta_provider_t *mprov, * pid_t pid) * * 1.3.1 Overview * * Called by the DTrace framework to instantiate a new provider given the * description of the provider and probes in the mprov argument. The * meta provider should call dtrace_register() to insert the new provider * into the DTrace framework. * * 1.3.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_meta_register(). * The second argument is a pointer to a structure describing the new * helper provider. The third argument is the process identifier for * process associated with this new provider. Note that the name of the * provider as passed to dtrace_register() should be the contatenation of * the dtmpb_provname member of the mprov argument and the processs * identifier as a string. * * 1.3.3 Return value * * The cookie for the provider that the meta provider creates. This is * the same value that it passed to dtrace_register(). * * 1.3.4 Caller's context * * dtms_provide_pid() is called from either ioctl() or module load context. * The DTrace framework is locked in such a way that meta providers may not * register or unregister. This means that the meta provider cannot call * dtrace_meta_register() or dtrace_meta_unregister(). However, the context * is such that the provider may -- and is expected to -- call * provider-related DTrace provider APIs including dtrace_register(). * * 1.4 void dtms_remove_pid(void *arg, dtrace_meta_provider_t *mprov, * pid_t pid) * * 1.4.1 Overview * * Called by the DTrace framework to remove a provider that had previously * been instantiated via the dtms_provide_pid() entry point. The meta * provider need not remove the provider immediately, but this entry * point indicates that the provider should be removed as soon as possible * using the dtrace_unregister() API. * * 1.4.2 Arguments and notes * * The first argument is the cookie as passed to dtrace_meta_register(). * The second argument is a pointer to a structure describing the helper * provider. The third argument is the process identifier for process * associated with this new provider. * * 1.4.3 Return value * * None * * 1.4.4 Caller's context * * dtms_remove_pid() is called from either ioctl() or exit() context. * The DTrace framework is locked in such a way that meta providers may not * register or unregister. This means that the meta provider cannot call * dtrace_meta_register() or dtrace_meta_unregister(). However, the context * is such that the provider may -- and is expected to -- call * provider-related DTrace provider APIs including dtrace_unregister(). */ typedef struct dtrace_helper_probedesc { char *dthpb_mod; /* probe module */ char *dthpb_func; /* probe function */ char *dthpb_name; /* probe name */ uint64_t dthpb_base; /* base address */ uint32_t *dthpb_offs; /* offsets array */ uint32_t *dthpb_enoffs; /* is-enabled offsets array */ uint32_t dthpb_noffs; /* offsets count */ uint32_t dthpb_nenoffs; /* is-enabled offsets count */ uint8_t *dthpb_args; /* argument mapping array */ uint8_t dthpb_xargc; /* translated argument count */ uint8_t dthpb_nargc; /* native argument count */ char *dthpb_xtypes; /* translated types strings */ char *dthpb_ntypes; /* native types strings */ } dtrace_helper_probedesc_t; typedef struct dtrace_helper_provdesc { char *dthpv_provname; /* provider name */ dtrace_pattr_t dthpv_pattr; /* stability attributes */ } dtrace_helper_provdesc_t; typedef struct dtrace_mops { void (*dtms_create_probe)(void *, void *, dtrace_helper_probedesc_t *); void *(*dtms_provide_pid)(void *, dtrace_helper_provdesc_t *, pid_t); void (*dtms_remove_pid)(void *, dtrace_helper_provdesc_t *, pid_t); } dtrace_mops_t; typedef uintptr_t dtrace_meta_provider_id_t; extern int dtrace_meta_register(const char *, const dtrace_mops_t *, void *, dtrace_meta_provider_id_t *); extern int dtrace_meta_unregister(dtrace_meta_provider_id_t); /* * DTrace Kernel Hooks * * The following functions are implemented by the base kernel and form a set of * hooks used by the DTrace framework. DTrace hooks are implemented in either * uts/common/os/dtrace_subr.c, an ISA-specific assembly file, or in a * uts//os/dtrace_subr.c corresponding to each hardware platform. */ typedef enum dtrace_vtime_state { DTRACE_VTIME_INACTIVE = 0, /* No DTrace, no TNF */ DTRACE_VTIME_ACTIVE, /* DTrace virtual time, no TNF */ DTRACE_VTIME_INACTIVE_TNF, /* No DTrace, TNF active */ DTRACE_VTIME_ACTIVE_TNF /* DTrace virtual time _and_ TNF */ } dtrace_vtime_state_t; #if defined(sun) extern dtrace_vtime_state_t dtrace_vtime_active; #endif extern void dtrace_vtime_switch(kthread_t *next); extern void dtrace_vtime_enable_tnf(void); extern void dtrace_vtime_disable_tnf(void); extern void dtrace_vtime_enable(void); extern void dtrace_vtime_disable(void); struct regs; struct reg; #if defined(sun) extern int (*dtrace_pid_probe_ptr)(struct reg *); extern int (*dtrace_return_probe_ptr)(struct reg *); extern void (*dtrace_fasttrap_fork_ptr)(proc_t *, proc_t *); extern void (*dtrace_fasttrap_exec_ptr)(proc_t *); extern void (*dtrace_fasttrap_exit_ptr)(proc_t *); extern void dtrace_fasttrap_fork(proc_t *, proc_t *); #endif typedef uintptr_t dtrace_icookie_t; typedef void (*dtrace_xcall_t)(void *); extern dtrace_icookie_t dtrace_interrupt_disable(void); extern void dtrace_interrupt_enable(dtrace_icookie_t); extern void dtrace_membar_producer(void); extern void dtrace_membar_consumer(void); extern void (*dtrace_cpu_init)(processorid_t); #if defined(sun) extern void (*dtrace_modload)(modctl_t *); extern void (*dtrace_modunload)(modctl_t *); #endif extern void (*dtrace_helpers_cleanup)(void); extern void (*dtrace_helpers_fork)(proc_t *parent, proc_t *child); extern void (*dtrace_cpustart_init)(void); extern void (*dtrace_cpustart_fini)(void); +extern void (*dtrace_closef)(void); extern void (*dtrace_debugger_init)(void); extern void (*dtrace_debugger_fini)(void); extern dtrace_cacheid_t dtrace_predcache_id; #if defined(sun) extern hrtime_t dtrace_gethrtime(void); #else void dtrace_debug_printf(const char *, ...) __printflike(1, 2); #endif extern void dtrace_sync(void); extern void dtrace_toxic_ranges(void (*)(uintptr_t, uintptr_t)); extern void dtrace_xcall(processorid_t, dtrace_xcall_t, void *); extern void dtrace_vpanic(const char *, __va_list); extern void dtrace_panic(const char *, ...); extern int dtrace_safe_defer_signal(void); extern void dtrace_safe_synchronous_signal(void); extern int dtrace_mach_aframes(void); #if defined(__i386) || defined(__amd64) extern int dtrace_instr_size(uchar_t *instr); extern int dtrace_instr_size_isa(uchar_t *, model_t, int *); extern void dtrace_invop_callsite(void); #endif extern void dtrace_invop_add(int (*)(uintptr_t, uintptr_t *, uintptr_t)); extern void dtrace_invop_remove(int (*)(uintptr_t, uintptr_t *, uintptr_t)); #ifdef __sparc extern int dtrace_blksuword32(uintptr_t, uint32_t *, int); extern void dtrace_getfsr(uint64_t *); #endif #if !defined(sun) extern void dtrace_helpers_duplicate(proc_t *, proc_t *); extern void dtrace_helpers_destroy(proc_t *); #endif #define DTRACE_CPUFLAG_ISSET(flag) \ (cpu_core[curcpu].cpuc_dtrace_flags & (flag)) #define DTRACE_CPUFLAG_SET(flag) \ (cpu_core[curcpu].cpuc_dtrace_flags |= (flag)) #define DTRACE_CPUFLAG_CLEAR(flag) \ (cpu_core[curcpu].cpuc_dtrace_flags &= ~(flag)) #endif /* _KERNEL */ #endif /* _ASM */ #if defined(__i386) || defined(__amd64) #define DTRACE_INVOP_PUSHL_EBP 1 #define DTRACE_INVOP_POPL_EBP 2 #define DTRACE_INVOP_LEAVE 3 #define DTRACE_INVOP_NOP 4 #define DTRACE_INVOP_RET 5 #elif defined(__powerpc__) #define DTRACE_INVOP_RET 1 #define DTRACE_INVOP_BCTR 2 #define DTRACE_INVOP_BLR 3 #define DTRACE_INVOP_JUMP 4 #define DTRACE_INVOP_MFLR_R0 5 #define DTRACE_INVOP_NOP 6 #endif #ifdef __cplusplus } #endif #endif /* _SYS_DTRACE_H */ Index: stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h =================================================================== --- stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h (revision 268577) +++ stable/10/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h (revision 268578) @@ -1,1335 +1,1337 @@ /* * 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 * * $FreeBSD$ */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #ifndef _SYS_DTRACE_IMPL_H #define _SYS_DTRACE_IMPL_H #ifdef __cplusplus extern "C" { #endif /* * DTrace Dynamic Tracing Software: Kernel Implementation 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. */ #include #if !defined(sun) #ifdef __sparcv9 typedef uint32_t pc_t; #else typedef uintptr_t pc_t; #endif typedef u_long greg_t; #endif /* * DTrace Implementation Constants and Typedefs */ #define DTRACE_MAXPROPLEN 128 #define DTRACE_DYNVAR_CHUNKSIZE 256 struct dtrace_probe; struct dtrace_ecb; struct dtrace_predicate; struct dtrace_action; struct dtrace_provider; struct dtrace_state; typedef struct dtrace_probe dtrace_probe_t; typedef struct dtrace_ecb dtrace_ecb_t; typedef struct dtrace_predicate dtrace_predicate_t; typedef struct dtrace_action dtrace_action_t; typedef struct dtrace_provider dtrace_provider_t; typedef struct dtrace_meta dtrace_meta_t; typedef struct dtrace_state dtrace_state_t; typedef uint32_t dtrace_optid_t; typedef uint32_t dtrace_specid_t; typedef uint64_t dtrace_genid_t; /* * DTrace Probes * * The probe is the fundamental unit of the DTrace architecture. Probes are * created by DTrace providers, and managed by the DTrace framework. A probe * is identified by a unique tuple, and has * a unique probe identifier assigned to it. (Some probes are not associated * with a specific point in text; these are called _unanchored probes_ and have * no module or function associated with them.) Probes are represented as a * dtrace_probe structure. To allow quick lookups based on each element of the * probe tuple, probes are hashed by each of provider, module, function and * name. (If a lookup is performed based on a regular expression, a * dtrace_probekey is prepared, and a linear search is performed.) Each probe * is additionally pointed to by a linear array indexed by its identifier. The * identifier is the provider's mechanism for indicating to the DTrace * framework that a probe has fired: the identifier is passed as the first * argument to dtrace_probe(), where it is then mapped into the corresponding * dtrace_probe structure. From the dtrace_probe structure, dtrace_probe() can * iterate over the probe's list of enabling control blocks; see "DTrace * Enabling Control Blocks", below.) */ struct dtrace_probe { dtrace_id_t dtpr_id; /* probe identifier */ dtrace_ecb_t *dtpr_ecb; /* ECB list; see below */ dtrace_ecb_t *dtpr_ecb_last; /* last ECB in list */ void *dtpr_arg; /* provider argument */ dtrace_cacheid_t dtpr_predcache; /* predicate cache ID */ int dtpr_aframes; /* artificial frames */ dtrace_provider_t *dtpr_provider; /* pointer to provider */ char *dtpr_mod; /* probe's module name */ char *dtpr_func; /* probe's function name */ char *dtpr_name; /* probe's name */ dtrace_probe_t *dtpr_nextmod; /* next in module hash */ dtrace_probe_t *dtpr_prevmod; /* previous in module hash */ dtrace_probe_t *dtpr_nextfunc; /* next in function hash */ dtrace_probe_t *dtpr_prevfunc; /* previous in function hash */ dtrace_probe_t *dtpr_nextname; /* next in name hash */ dtrace_probe_t *dtpr_prevname; /* previous in name hash */ dtrace_genid_t dtpr_gen; /* probe generation ID */ }; typedef int dtrace_probekey_f(const char *, const char *, int); typedef struct dtrace_probekey { char *dtpk_prov; /* provider name to match */ dtrace_probekey_f *dtpk_pmatch; /* provider matching function */ char *dtpk_mod; /* module name to match */ dtrace_probekey_f *dtpk_mmatch; /* module matching function */ char *dtpk_func; /* func name to match */ dtrace_probekey_f *dtpk_fmatch; /* func matching function */ char *dtpk_name; /* name to match */ dtrace_probekey_f *dtpk_nmatch; /* name matching function */ dtrace_id_t dtpk_id; /* identifier to match */ } dtrace_probekey_t; typedef struct dtrace_hashbucket { struct dtrace_hashbucket *dthb_next; /* next on hash chain */ dtrace_probe_t *dthb_chain; /* chain of probes */ int dthb_len; /* number of probes here */ } dtrace_hashbucket_t; typedef struct dtrace_hash { dtrace_hashbucket_t **dth_tab; /* hash table */ int dth_size; /* size of hash table */ int dth_mask; /* mask to index into table */ int dth_nbuckets; /* total number of buckets */ uintptr_t dth_nextoffs; /* offset of next in probe */ uintptr_t dth_prevoffs; /* offset of prev in probe */ uintptr_t dth_stroffs; /* offset of str in probe */ } dtrace_hash_t; /* * DTrace Enabling Control Blocks * * When a provider wishes to fire a probe, it calls into dtrace_probe(), * passing the probe identifier as the first argument. As described above, * dtrace_probe() maps the identifier into a pointer to a dtrace_probe_t * structure. This structure contains information about the probe, and a * pointer to the list of Enabling Control Blocks (ECBs). Each ECB points to * DTrace consumer state, and contains an optional predicate, and a list of * actions. (Shown schematically below.) The ECB abstraction allows a single * probe to be multiplexed across disjoint consumers, or across disjoint * enablings of a single probe within one consumer. * * Enabling Control Block * dtrace_ecb_t * +------------------------+ * | dtrace_epid_t ---------+--------------> Enabled Probe ID (EPID) * | dtrace_state_t * ------+--------------> State associated with this ECB * | dtrace_predicate_t * --+---------+ * | dtrace_action_t * -----+----+ | * | dtrace_ecb_t * ---+ | | | Predicate (if any) * +-------------------+----+ | | dtrace_predicate_t * | | +---> +--------------------+ * | | | dtrace_difo_t * ---+----> DIFO * | | +--------------------+ * | | * Next ECB | | Action * (if any) | | dtrace_action_t * : +--> +-------------------+ * : | dtrace_actkind_t -+------> kind * v | dtrace_difo_t * --+------> DIFO (if any) * | dtrace_recdesc_t -+------> record descr. * | dtrace_action_t * +------+ * +-------------------+ | * | Next action * +-------------------------------+ (if any) * | * | Action * | dtrace_action_t * +--> +-------------------+ * | dtrace_actkind_t -+------> kind * | dtrace_difo_t * --+------> DIFO (if any) * | dtrace_action_t * +------+ * +-------------------+ | * | Next action * +-------------------------------+ (if any) * | * : * v * * * dtrace_probe() iterates over the ECB list. If the ECB needs less space * than is available in the principal buffer, the ECB is processed: if the * predicate is non-NULL, the DIF object is executed. If the result is * non-zero, the action list is processed, with each action being executed * accordingly. When the action list has been completely executed, processing * advances to the next ECB. The ECB abstraction allows disjoint consumers * to multiplex on single probes. * * Execution of the ECB results in consuming dte_size bytes in the buffer * to record data. During execution, dte_needed bytes must be available in * the buffer. This space is used for both recorded data and tuple data. */ struct dtrace_ecb { dtrace_epid_t dte_epid; /* enabled probe ID */ uint32_t dte_alignment; /* required alignment */ size_t dte_needed; /* space needed for execution */ size_t dte_size; /* size of recorded payload */ dtrace_predicate_t *dte_predicate; /* predicate, if any */ dtrace_action_t *dte_action; /* actions, if any */ dtrace_ecb_t *dte_next; /* next ECB on probe */ dtrace_state_t *dte_state; /* pointer to state */ uint32_t dte_cond; /* security condition */ dtrace_probe_t *dte_probe; /* pointer to probe */ dtrace_action_t *dte_action_last; /* last action on ECB */ uint64_t dte_uarg; /* library argument */ }; struct dtrace_predicate { dtrace_difo_t *dtp_difo; /* DIF object */ dtrace_cacheid_t dtp_cacheid; /* cache identifier */ int dtp_refcnt; /* reference count */ }; struct dtrace_action { dtrace_actkind_t dta_kind; /* kind of action */ uint16_t dta_intuple; /* boolean: in aggregation */ uint32_t dta_refcnt; /* reference count */ dtrace_difo_t *dta_difo; /* pointer to DIFO */ dtrace_recdesc_t dta_rec; /* record description */ dtrace_action_t *dta_prev; /* previous action */ dtrace_action_t *dta_next; /* next action */ }; typedef struct dtrace_aggregation { dtrace_action_t dtag_action; /* action; must be first */ dtrace_aggid_t dtag_id; /* identifier */ dtrace_ecb_t *dtag_ecb; /* corresponding ECB */ dtrace_action_t *dtag_first; /* first action in tuple */ uint32_t dtag_base; /* base of aggregation */ uint8_t dtag_hasarg; /* boolean: has argument */ uint64_t dtag_initial; /* initial value */ void (*dtag_aggregate)(uint64_t *, uint64_t, uint64_t); } dtrace_aggregation_t; /* * DTrace Buffers * * Principal buffers, aggregation buffers, and speculative buffers are all * managed with the dtrace_buffer structure. By default, this structure * includes twin data buffers -- dtb_tomax and dtb_xamot -- that serve as the * active and passive buffers, respectively. For speculative buffers, * dtb_xamot will be NULL; for "ring" and "fill" buffers, dtb_xamot will point * to a scratch buffer. For all buffer types, the dtrace_buffer structure is * always allocated on a per-CPU basis; a single dtrace_buffer structure is * never shared among CPUs. (That is, there is never true sharing of the * dtrace_buffer structure; to prevent false sharing of the structure, it must * always be aligned to the coherence granularity -- generally 64 bytes.) * * One of the critical design decisions of DTrace is that a given ECB always * stores the same quantity and type of data. This is done to assure that the * only metadata required for an ECB's traced data is the EPID. That is, from * the EPID, the consumer can determine the data layout. (The data buffer * layout is shown schematically below.) By assuring that one can determine * data layout from the EPID, the metadata stream can be separated from the * data stream -- simplifying the data stream enormously. The ECB always * proceeds the recorded data as part of the dtrace_rechdr_t structure that * includes the EPID and a high-resolution timestamp used for output ordering * consistency. * * base of data buffer ---> +--------+--------------------+--------+ * | rechdr | data | rechdr | * +--------+------+--------+----+--------+ * | data | rechdr | data | * +---------------+--------+-------------+ * | data, cont. | * +--------+--------------------+--------+ * | rechdr | data | | * +--------+--------------------+ | * | || | * | || | * | \/ | * : : * . . * . . * . . * : : * | | * limit of data buffer ---> +--------------------------------------+ * * When evaluating an ECB, dtrace_probe() determines if the ECB's needs of the * principal buffer (both scratch and payload) exceed the available space. If * the ECB's needs exceed available space (and if the principal buffer policy * is the default "switch" policy), the ECB is dropped, the buffer's drop count * is incremented, and processing advances to the next ECB. If the ECB's needs * can be met with the available space, the ECB is processed, but the offset in * the principal buffer is only advanced if the ECB completes processing * without error. * * When a buffer is to be switched (either because the buffer is the principal * buffer with a "switch" policy or because it is an aggregation buffer), a * cross call is issued to the CPU associated with the buffer. In the cross * call context, interrupts are disabled, and the active and the inactive * buffers are atomically switched. This involves switching the data pointers, * copying the various state fields (offset, drops, errors, etc.) into their * inactive equivalents, and clearing the state fields. Because interrupts are * disabled during this procedure, the switch is guaranteed to appear atomic to * dtrace_probe(). * * DTrace Ring Buffering * * To process a ring buffer correctly, one must know the oldest valid record. * Processing starts at the oldest record in the buffer and continues until * the end of the buffer is reached. Processing then resumes starting with * the record stored at offset 0 in the buffer, and continues until the * youngest record is processed. If trace records are of a fixed-length, * determining the oldest record is trivial: * * - If the ring buffer has not wrapped, the oldest record is the record * stored at offset 0. * * - If the ring buffer has wrapped, the oldest record is the record stored * at the current offset. * * With variable length records, however, just knowing the current offset * doesn't suffice for determining the oldest valid record: assuming that one * allows for arbitrary data, one has no way of searching forward from the * current offset to find the oldest valid record. (That is, one has no way * of separating data from metadata.) It would be possible to simply refuse to * process any data in the ring buffer between the current offset and the * limit, but this leaves (potentially) an enormous amount of otherwise valid * data unprocessed. * * To effect ring buffering, we track two offsets in the buffer: the current * offset and the _wrapped_ offset. If a request is made to reserve some * amount of data, and the buffer has wrapped, the wrapped offset is * incremented until the wrapped offset minus the current offset is greater * than or equal to the reserve request. This is done by repeatedly looking * up the ECB corresponding to the EPID at the current wrapped offset, and * incrementing the wrapped offset by the size of the data payload * corresponding to that ECB. If this offset is greater than or equal to the * limit of the data buffer, the wrapped offset is set to 0. Thus, the * current offset effectively "chases" the wrapped offset around the buffer. * Schematically: * * base of data buffer ---> +------+--------------------+------+ * | EPID | data | EPID | * +------+--------+------+----+------+ * | data | EPID | data | * +---------------+------+-----------+ * | data, cont. | * +------+---------------------------+ * | EPID | data | * current offset ---> +------+---------------------------+ * | invalid data | * wrapped offset ---> +------+--------------------+------+ * | EPID | data | EPID | * +------+--------+------+----+------+ * | data | EPID | data | * +---------------+------+-----------+ * : : * . . * . ... valid data ... . * . . * : : * +------+-------------+------+------+ * | EPID | data | EPID | data | * +------+------------++------+------+ * | data, cont. | leftover | * limit of data buffer ---> +-------------------+--------------+ * * If the amount of requested buffer space exceeds the amount of space * available between the current offset and the end of the buffer: * * (1) all words in the data buffer between the current offset and the limit * of the data buffer (marked "leftover", above) are set to * DTRACE_EPIDNONE * * (2) the wrapped offset is set to zero * * (3) the iteration process described above occurs until the wrapped offset * is greater than the amount of desired space. * * The wrapped offset is implemented by (re-)using the inactive offset. * In a "switch" buffer policy, the inactive offset stores the offset in * the inactive buffer; in a "ring" buffer policy, it stores the wrapped * offset. * * DTrace Scratch Buffering * * Some ECBs may wish to allocate dynamically-sized temporary scratch memory. * To accommodate such requests easily, scratch memory may be allocated in * the buffer beyond the current offset plus the needed memory of the current * ECB. If there isn't sufficient room in the buffer for the requested amount * of scratch space, the allocation fails and an error is generated. Scratch * memory is tracked in the dtrace_mstate_t and is automatically freed when * the ECB ceases processing. Note that ring buffers cannot allocate their * scratch from the principal buffer -- lest they needlessly overwrite older, * valid data. Ring buffers therefore have their own dedicated scratch buffer * from which scratch is allocated. */ #define DTRACEBUF_RING 0x0001 /* bufpolicy set to "ring" */ #define DTRACEBUF_FILL 0x0002 /* bufpolicy set to "fill" */ #define DTRACEBUF_NOSWITCH 0x0004 /* do not switch buffer */ #define DTRACEBUF_WRAPPED 0x0008 /* ring buffer has wrapped */ #define DTRACEBUF_DROPPED 0x0010 /* drops occurred */ #define DTRACEBUF_ERROR 0x0020 /* errors occurred */ #define DTRACEBUF_FULL 0x0040 /* "fill" buffer is full */ #define DTRACEBUF_CONSUMED 0x0080 /* buffer has been consumed */ #define DTRACEBUF_INACTIVE 0x0100 /* buffer is not yet active */ typedef struct dtrace_buffer { uint64_t dtb_offset; /* current offset in buffer */ uint64_t dtb_size; /* size of buffer */ uint32_t dtb_flags; /* flags */ uint32_t dtb_drops; /* number of drops */ caddr_t dtb_tomax; /* active buffer */ caddr_t dtb_xamot; /* inactive buffer */ uint32_t dtb_xamot_flags; /* inactive flags */ uint32_t dtb_xamot_drops; /* drops in inactive buffer */ uint64_t dtb_xamot_offset; /* offset in inactive buffer */ uint32_t dtb_errors; /* number of errors */ uint32_t dtb_xamot_errors; /* errors in inactive buffer */ #ifndef _LP64 uint64_t dtb_pad1; /* pad out to 64 bytes */ #endif uint64_t dtb_switched; /* time of last switch */ uint64_t dtb_interval; /* observed switch interval */ uint64_t dtb_pad2[6]; /* pad to avoid false sharing */ } dtrace_buffer_t; /* * DTrace Aggregation Buffers * * Aggregation buffers use much of the same mechanism as described above * ("DTrace Buffers"). However, because an aggregation is fundamentally a * hash, there exists dynamic metadata associated with an aggregation buffer * that is not associated with other kinds of buffers. This aggregation * metadata is _only_ relevant for the in-kernel implementation of * aggregations; it is not actually relevant to user-level consumers. To do * this, we allocate dynamic aggregation data (hash keys and hash buckets) * starting below the _limit_ of the buffer, and we allocate data from the * _base_ of the buffer. When the aggregation buffer is copied out, _only_ the * data is copied out; the metadata is simply discarded. Schematically, * aggregation buffers look like: * * base of data buffer ---> +-------+------+-----------+-------+ * | aggid | key | value | aggid | * +-------+------+-----------+-------+ * | key | * +-------+-------+-----+------------+ * | value | aggid | key | value | * +-------+------++-----+------+-----+ * | aggid | key | value | | * +-------+------+-------------+ | * | || | * | || | * | \/ | * : : * . . * . . * . . * : : * | /\ | * | || +------------+ * | || | | * +---------------------+ | * | hash keys | * | (dtrace_aggkey structures) | * | | * +----------------------------------+ * | hash buckets | * | (dtrace_aggbuffer structure) | * | | * limit of data buffer ---> +----------------------------------+ * * * As implied above, just as we assure that ECBs always store a constant * amount of data, we assure that a given aggregation -- identified by its * aggregation ID -- always stores data of a constant quantity and type. * As with EPIDs, this allows the aggregation ID to serve as the metadata for a * given record. * * Note that the size of the dtrace_aggkey structure must be sizeof (uintptr_t) * aligned. (If this the structure changes such that this becomes false, an * assertion will fail in dtrace_aggregate().) */ typedef struct dtrace_aggkey { uint32_t dtak_hashval; /* hash value */ uint32_t dtak_action:4; /* action -- 4 bits */ uint32_t dtak_size:28; /* size -- 28 bits */ caddr_t dtak_data; /* data pointer */ struct dtrace_aggkey *dtak_next; /* next in hash chain */ } dtrace_aggkey_t; typedef struct dtrace_aggbuffer { uintptr_t dtagb_hashsize; /* number of buckets */ uintptr_t dtagb_free; /* free list of keys */ dtrace_aggkey_t **dtagb_hash; /* hash table */ } dtrace_aggbuffer_t; /* * DTrace Speculations * * Speculations have a per-CPU buffer and a global state. Once a speculation * buffer has been comitted or discarded, it cannot be reused until all CPUs * have taken the same action (commit or discard) on their respective * speculative buffer. However, because DTrace probes may execute in arbitrary * context, other CPUs cannot simply be cross-called at probe firing time to * perform the necessary commit or discard. The speculation states thus * optimize for the case that a speculative buffer is only active on one CPU at * the time of a commit() or discard() -- for if this is the case, other CPUs * need not take action, and the speculation is immediately available for * reuse. If the speculation is active on multiple CPUs, it must be * asynchronously cleaned -- potentially leading to a higher rate of dirty * speculative drops. The speculation states are as follows: * * DTRACESPEC_INACTIVE <= Initial state; inactive speculation * DTRACESPEC_ACTIVE <= Allocated, but not yet speculatively traced to * DTRACESPEC_ACTIVEONE <= Speculatively traced to on one CPU * DTRACESPEC_ACTIVEMANY <= Speculatively traced to on more than one CPU * DTRACESPEC_COMMITTING <= Currently being commited on one CPU * DTRACESPEC_COMMITTINGMANY <= Currently being commited on many CPUs * DTRACESPEC_DISCARDING <= Currently being discarded on many CPUs * * The state transition diagram is as follows: * * +----------------------------------------------------------+ * | | * | +------------+ | * | +-------------------| COMMITTING |<-----------------+ | * | | +------------+ | | * | | copied spec. ^ commit() on | | discard() on * | | into principal | active CPU | | active CPU * | | | commit() | | * V V | | | * +----------+ +--------+ +-----------+ * | INACTIVE |---------------->| ACTIVE |--------------->| ACTIVEONE | * +----------+ speculation() +--------+ speculate() +-----------+ * ^ ^ | | | * | | | discard() | | * | | asynchronously | discard() on | | speculate() * | | cleaned V inactive CPU | | on inactive * | | +------------+ | | CPU * | +-------------------| DISCARDING |<-----------------+ | * | +------------+ | * | asynchronously ^ | * | copied spec. | discard() | * | into principal +------------------------+ | * | | V * +----------------+ commit() +------------+ * | COMMITTINGMANY |<----------------------------------| ACTIVEMANY | * +----------------+ +------------+ */ typedef enum dtrace_speculation_state { DTRACESPEC_INACTIVE = 0, DTRACESPEC_ACTIVE, DTRACESPEC_ACTIVEONE, DTRACESPEC_ACTIVEMANY, DTRACESPEC_COMMITTING, DTRACESPEC_COMMITTINGMANY, DTRACESPEC_DISCARDING } dtrace_speculation_state_t; typedef struct dtrace_speculation { dtrace_speculation_state_t dtsp_state; /* current speculation state */ int dtsp_cleaning; /* non-zero if being cleaned */ dtrace_buffer_t *dtsp_buffer; /* speculative buffer */ } dtrace_speculation_t; /* * DTrace Dynamic Variables * * The dynamic variable problem is obviously decomposed into two subproblems: * allocating new dynamic storage, and freeing old dynamic storage. The * presence of the second problem makes the first much more complicated -- or * rather, the absence of the second renders the first trivial. This is the * case with aggregations, for which there is effectively no deallocation of * dynamic storage. (Or more accurately, all dynamic storage is deallocated * when a snapshot is taken of the aggregation.) As DTrace dynamic variables * allow for both dynamic allocation and dynamic deallocation, the * implementation of dynamic variables is quite a bit more complicated than * that of their aggregation kin. * * We observe that allocating new dynamic storage is tricky only because the * size can vary -- the allocation problem is much easier if allocation sizes * are uniform. We further observe that in D, the size of dynamic variables is * actually _not_ dynamic -- dynamic variable sizes may be determined by static * analysis of DIF text. (This is true even of putatively dynamically-sized * objects like strings and stacks, the sizes of which are dictated by the * "stringsize" and "stackframes" variables, respectively.) We exploit this by * performing this analysis on all DIF before enabling any probes. For each * dynamic load or store, we calculate the dynamically-allocated size plus the * size of the dtrace_dynvar structure plus the storage required to key the * data. For all DIF, we take the largest value and dub it the _chunksize_. * We then divide dynamic memory into two parts: a hash table that is wide * enough to have every chunk in its own bucket, and a larger region of equal * chunksize units. Whenever we wish to dynamically allocate a variable, we * always allocate a single chunk of memory. Depending on the uniformity of * allocation, this will waste some amount of memory -- but it eliminates the * non-determinism inherent in traditional heap fragmentation. * * Dynamic objects are allocated by storing a non-zero value to them; they are * deallocated by storing a zero value to them. Dynamic variables are * complicated enormously by being shared between CPUs. In particular, * consider the following scenario: * * CPU A CPU B * +---------------------------------+ +---------------------------------+ * | | | | * | allocates dynamic object a[123] | | | * | by storing the value 345 to it | | | * | ---------> | * | | | wishing to load from object | * | | | a[123], performs lookup in | * | | | dynamic variable space | * | <--------- | * | deallocates object a[123] by | | | * | storing 0 to it | | | * | | | | * | allocates dynamic object b[567] | | performs load from a[123] | * | by storing the value 789 to it | | | * : : : : * . . . . * * This is obviously a race in the D program, but there are nonetheless only * two valid values for CPU B's load from a[123]: 345 or 0. Most importantly, * CPU B may _not_ see the value 789 for a[123]. * * There are essentially two ways to deal with this: * * (1) Explicitly spin-lock variables. That is, if CPU B wishes to load * from a[123], it needs to lock a[123] and hold the lock for the * duration that it wishes to manipulate it. * * (2) Avoid reusing freed chunks until it is known that no CPU is referring * to them. * * The implementation of (1) is rife with complexity, because it requires the * user of a dynamic variable to explicitly decree when they are done using it. * Were all variables by value, this perhaps wouldn't be debilitating -- but * dynamic variables of non-scalar types are tracked by reference. That is, if * a dynamic variable is, say, a string, and that variable is to be traced to, * say, the principal buffer, the DIF emulation code returns to the main * dtrace_probe() loop a pointer to the underlying storage, not the contents of * the storage. Further, code calling on DIF emulation would have to be aware * that the DIF emulation has returned a reference to a dynamic variable that * has been potentially locked. The variable would have to be unlocked after * the main dtrace_probe() loop is finished with the variable, and the main * dtrace_probe() loop would have to be careful to not call any further DIF * emulation while the variable is locked to avoid deadlock. More generally, * if one were to implement (1), DIF emulation code dealing with dynamic * variables could only deal with one dynamic variable at a time (lest deadlock * result). To sum, (1) exports too much subtlety to the users of dynamic * variables -- increasing maintenance burden and imposing serious constraints * on future DTrace development. * * The implementation of (2) is also complex, but the complexity is more * manageable. We need to be sure that when a variable is deallocated, it is * not placed on a traditional free list, but rather on a _dirty_ list. Once a * variable is on a dirty list, it cannot be found by CPUs performing a * subsequent lookup of the variable -- but it may still be in use by other * CPUs. To assure that all CPUs that may be seeing the old variable have * cleared out of probe context, a dtrace_sync() can be issued. Once the * dtrace_sync() has completed, it can be known that all CPUs are done * manipulating the dynamic variable -- the dirty list can be atomically * appended to the free list. Unfortunately, there's a slight hiccup in this * mechanism: dtrace_sync() may not be issued from probe context. The * dtrace_sync() must be therefore issued asynchronously from non-probe * context. For this we rely on the DTrace cleaner, a cyclic that runs at the * "cleanrate" frequency. To ease this implementation, we define several chunk * lists: * * - Dirty. Deallocated chunks, not yet cleaned. Not available. * * - Rinsing. Formerly dirty chunks that are currently being asynchronously * cleaned. Not available, but will be shortly. Dynamic variable * allocation may not spin or block for availability, however. * * - Clean. Clean chunks, ready for allocation -- but not on the free list. * * - Free. Available for allocation. * * Moreover, to avoid absurd contention, _each_ of these lists is implemented * on a per-CPU basis. This is only for performance, not correctness; chunks * may be allocated from another CPU's free list. The algorithm for allocation * then is this: * * (1) Attempt to atomically allocate from current CPU's free list. If list * is non-empty and allocation is successful, allocation is complete. * * (2) If the clean list is non-empty, atomically move it to the free list, * and reattempt (1). * * (3) If the dynamic variable space is in the CLEAN state, look for free * and clean lists on other CPUs by setting the current CPU to the next * CPU, and reattempting (1). If the next CPU is the current CPU (that * is, if all CPUs have been checked), atomically switch the state of * the dynamic variable space based on the following: * * - If no free chunks were found and no dirty chunks were found, * atomically set the state to EMPTY. * * - If dirty chunks were found, atomically set the state to DIRTY. * * - If rinsing chunks were found, atomically set the state to RINSING. * * (4) Based on state of dynamic variable space state, increment appropriate * counter to indicate dynamic drops (if in EMPTY state) vs. dynamic * dirty drops (if in DIRTY state) vs. dynamic rinsing drops (if in * RINSING state). Fail the allocation. * * The cleaning cyclic operates with the following algorithm: for all CPUs * with a non-empty dirty list, atomically move the dirty list to the rinsing * list. Perform a dtrace_sync(). For all CPUs with a non-empty rinsing list, * atomically move the rinsing list to the clean list. Perform another * dtrace_sync(). By this point, all CPUs have seen the new clean list; the * state of the dynamic variable space can be restored to CLEAN. * * There exist two final races that merit explanation. The first is a simple * allocation race: * * CPU A CPU B * +---------------------------------+ +---------------------------------+ * | | | | * | allocates dynamic object a[123] | | allocates dynamic object a[123] | * | by storing the value 345 to it | | by storing the value 567 to it | * | | | | * : : : : * . . . . * * Again, this is a race in the D program. It can be resolved by having a[123] * hold the value 345 or a[123] hold the value 567 -- but it must be true that * a[123] have only _one_ of these values. (That is, the racing CPUs may not * put the same element twice on the same hash chain.) This is resolved * simply: before the allocation is undertaken, the start of the new chunk's * hash chain is noted. Later, after the allocation is complete, the hash * chain is atomically switched to point to the new element. If this fails * (because of either concurrent allocations or an allocation concurrent with a * deletion), the newly allocated chunk is deallocated to the dirty list, and * the whole process of looking up (and potentially allocating) the dynamic * variable is reattempted. * * The final race is a simple deallocation race: * * CPU A CPU B * +---------------------------------+ +---------------------------------+ * | | | | * | deallocates dynamic object | | deallocates dynamic object | * | a[123] by storing the value 0 | | a[123] by storing the value 0 | * | to it | | to it | * | | | | * : : : : * . . . . * * Once again, this is a race in the D program, but it is one that we must * handle without corrupting the underlying data structures. Because * deallocations require the deletion of a chunk from the middle of a hash * chain, we cannot use a single-word atomic operation to remove it. For this, * we add a spin lock to the hash buckets that is _only_ used for deallocations * (allocation races are handled as above). Further, this spin lock is _only_ * held for the duration of the delete; before control is returned to the DIF * emulation code, the hash bucket is unlocked. */ typedef struct dtrace_key { uint64_t dttk_value; /* data value or data pointer */ uint64_t dttk_size; /* 0 if by-val, >0 if by-ref */ } dtrace_key_t; typedef struct dtrace_tuple { uint32_t dtt_nkeys; /* number of keys in tuple */ uint32_t dtt_pad; /* padding */ dtrace_key_t dtt_key[1]; /* array of tuple keys */ } dtrace_tuple_t; typedef struct dtrace_dynvar { uint64_t dtdv_hashval; /* hash value -- 0 if free */ struct dtrace_dynvar *dtdv_next; /* next on list or hash chain */ void *dtdv_data; /* pointer to data */ dtrace_tuple_t dtdv_tuple; /* tuple key */ } dtrace_dynvar_t; typedef enum dtrace_dynvar_op { DTRACE_DYNVAR_ALLOC, DTRACE_DYNVAR_NOALLOC, DTRACE_DYNVAR_DEALLOC } dtrace_dynvar_op_t; typedef struct dtrace_dynhash { dtrace_dynvar_t *dtdh_chain; /* hash chain for this bucket */ uintptr_t dtdh_lock; /* deallocation lock */ #ifdef _LP64 uintptr_t dtdh_pad[6]; /* pad to avoid false sharing */ #else uintptr_t dtdh_pad[14]; /* pad to avoid false sharing */ #endif } dtrace_dynhash_t; typedef struct dtrace_dstate_percpu { dtrace_dynvar_t *dtdsc_free; /* free list for this CPU */ dtrace_dynvar_t *dtdsc_dirty; /* dirty list for this CPU */ dtrace_dynvar_t *dtdsc_rinsing; /* rinsing list for this CPU */ dtrace_dynvar_t *dtdsc_clean; /* clean list for this CPU */ uint64_t dtdsc_drops; /* number of capacity drops */ uint64_t dtdsc_dirty_drops; /* number of dirty drops */ uint64_t dtdsc_rinsing_drops; /* number of rinsing drops */ #ifdef _LP64 uint64_t dtdsc_pad; /* pad to avoid false sharing */ #else uint64_t dtdsc_pad[2]; /* pad to avoid false sharing */ #endif } dtrace_dstate_percpu_t; typedef enum dtrace_dstate_state { DTRACE_DSTATE_CLEAN = 0, DTRACE_DSTATE_EMPTY, DTRACE_DSTATE_DIRTY, DTRACE_DSTATE_RINSING } dtrace_dstate_state_t; typedef struct dtrace_dstate { void *dtds_base; /* base of dynamic var. space */ size_t dtds_size; /* size of dynamic var. space */ size_t dtds_hashsize; /* number of buckets in hash */ size_t dtds_chunksize; /* size of each chunk */ dtrace_dynhash_t *dtds_hash; /* pointer to hash table */ dtrace_dstate_state_t dtds_state; /* current dynamic var. state */ dtrace_dstate_percpu_t *dtds_percpu; /* per-CPU dyn. var. state */ } dtrace_dstate_t; /* * DTrace Variable State * * The DTrace variable state tracks user-defined variables in its dtrace_vstate * structure. Each DTrace consumer has exactly one dtrace_vstate structure, * but some dtrace_vstate structures may exist without a corresponding DTrace * consumer (see "DTrace Helpers", below). As described in , * user-defined variables can have one of three scopes: * * DIFV_SCOPE_GLOBAL => global scope * DIFV_SCOPE_THREAD => thread-local scope (i.e. "self->" variables) * DIFV_SCOPE_LOCAL => clause-local scope (i.e. "this->" variables) * * The variable state tracks variables by both their scope and their allocation * type: * * - The dtvs_globals and dtvs_locals members each point to an array of * dtrace_statvar structures. These structures contain both the variable * metadata (dtrace_difv structures) and the underlying storage for all * statically allocated variables, including statically allocated * DIFV_SCOPE_GLOBAL variables and all DIFV_SCOPE_LOCAL variables. * * - The dtvs_tlocals member points to an array of dtrace_difv structures for * DIFV_SCOPE_THREAD variables. As such, this array tracks _only_ the * variable metadata for DIFV_SCOPE_THREAD variables; the underlying storage * is allocated out of the dynamic variable space. * * - The dtvs_dynvars member is the dynamic variable state associated with the * variable state. The dynamic variable state (described in "DTrace Dynamic * Variables", above) tracks all DIFV_SCOPE_THREAD variables and all * dynamically-allocated DIFV_SCOPE_GLOBAL variables. */ typedef struct dtrace_statvar { uint64_t dtsv_data; /* data or pointer to it */ size_t dtsv_size; /* size of pointed-to data */ int dtsv_refcnt; /* reference count */ dtrace_difv_t dtsv_var; /* variable metadata */ } dtrace_statvar_t; typedef struct dtrace_vstate { dtrace_state_t *dtvs_state; /* back pointer to state */ dtrace_statvar_t **dtvs_globals; /* statically-allocated glbls */ int dtvs_nglobals; /* number of globals */ dtrace_difv_t *dtvs_tlocals; /* thread-local metadata */ int dtvs_ntlocals; /* number of thread-locals */ dtrace_statvar_t **dtvs_locals; /* clause-local data */ int dtvs_nlocals; /* number of clause-locals */ dtrace_dstate_t dtvs_dynvars; /* dynamic variable state */ } dtrace_vstate_t; /* * DTrace Machine State * * In the process of processing a fired probe, DTrace needs to track and/or * cache some per-CPU state associated with that particular firing. This is * state that is always discarded after the probe firing has completed, and * much of it is not specific to any DTrace consumer, remaining valid across * all ECBs. This state is tracked in the dtrace_mstate structure. */ #define DTRACE_MSTATE_ARGS 0x00000001 #define DTRACE_MSTATE_PROBE 0x00000002 #define DTRACE_MSTATE_EPID 0x00000004 #define DTRACE_MSTATE_TIMESTAMP 0x00000008 #define DTRACE_MSTATE_STACKDEPTH 0x00000010 #define DTRACE_MSTATE_CALLER 0x00000020 #define DTRACE_MSTATE_IPL 0x00000040 #define DTRACE_MSTATE_FLTOFFS 0x00000080 #define DTRACE_MSTATE_WALLTIMESTAMP 0x00000100 #define DTRACE_MSTATE_USTACKDEPTH 0x00000200 #define DTRACE_MSTATE_UCALLER 0x00000400 typedef struct dtrace_mstate { uintptr_t dtms_scratch_base; /* base of scratch space */ uintptr_t dtms_scratch_ptr; /* current scratch pointer */ size_t dtms_scratch_size; /* scratch size */ uint32_t dtms_present; /* variables that are present */ uint64_t dtms_arg[5]; /* cached arguments */ dtrace_epid_t dtms_epid; /* current EPID */ uint64_t dtms_timestamp; /* cached timestamp */ hrtime_t dtms_walltimestamp; /* cached wall timestamp */ int dtms_stackdepth; /* cached stackdepth */ int dtms_ustackdepth; /* cached ustackdepth */ struct dtrace_probe *dtms_probe; /* current probe */ uintptr_t dtms_caller; /* cached caller */ uint64_t dtms_ucaller; /* cached user-level caller */ int dtms_ipl; /* cached interrupt pri lev */ int dtms_fltoffs; /* faulting DIFO offset */ uintptr_t dtms_strtok; /* saved strtok() pointer */ uint32_t dtms_access; /* memory access rights */ dtrace_difo_t *dtms_difo; /* current dif object */ + file_t *dtms_getf; /* cached rval of getf() */ } dtrace_mstate_t; #define DTRACE_COND_OWNER 0x1 #define DTRACE_COND_USERMODE 0x2 #define DTRACE_COND_ZONEOWNER 0x4 #define DTRACE_PROBEKEY_MAXDEPTH 8 /* max glob recursion depth */ /* * Access flag used by dtrace_mstate.dtms_access. */ #define DTRACE_ACCESS_KERNEL 0x1 /* the priv to read kmem */ /* * DTrace Activity * * Each DTrace consumer is in one of several states, which (for purposes of * avoiding yet-another overloading of the noun "state") we call the current * _activity_. The activity transitions on dtrace_go() (from DTRACIOCGO), on * dtrace_stop() (from DTRACIOCSTOP) and on the exit() action. Activities may * only transition in one direction; the activity transition diagram is a * directed acyclic graph. The activity transition diagram is as follows: * * * +----------+ +--------+ +--------+ * | INACTIVE |------------------>| WARMUP |------------------>| ACTIVE | * +----------+ dtrace_go(), +--------+ dtrace_go(), +--------+ * before BEGIN | after BEGIN | | | * | | | | * exit() action | | | | * from BEGIN ECB | | | | * | | | | * v | | | * +----------+ exit() action | | | * +-----------------------------| DRAINING |<-------------------+ | | * | +----------+ | | * | | | | * | dtrace_stop(), | | | * | before END | | | * | | | | * | v | | * | +---------+ +----------+ | | * | | STOPPED |<----------------| COOLDOWN |<----------------------+ | * | +---------+ dtrace_stop(), +----------+ dtrace_stop(), | * | after END before END | * | | * | +--------+ | * +----------------------------->| KILLED |<--------------------------+ * deadman timeout or +--------+ deadman timeout or * killed consumer killed consumer * * Note that once a DTrace consumer has stopped tracing, there is no way to * restart it; if a DTrace consumer wishes to restart tracing, it must reopen * the DTrace pseudodevice. */ typedef enum dtrace_activity { DTRACE_ACTIVITY_INACTIVE = 0, /* not yet running */ DTRACE_ACTIVITY_WARMUP, /* while starting */ DTRACE_ACTIVITY_ACTIVE, /* running */ DTRACE_ACTIVITY_DRAINING, /* before stopping */ DTRACE_ACTIVITY_COOLDOWN, /* while stopping */ DTRACE_ACTIVITY_STOPPED, /* after stopping */ DTRACE_ACTIVITY_KILLED /* killed */ } dtrace_activity_t; /* * DTrace Helper Implementation * * A description of the helper architecture may be found in . * Each process contains a pointer to its helpers in its p_dtrace_helpers * member. This is a pointer to a dtrace_helpers structure, which contains an * array of pointers to dtrace_helper structures, helper variable state (shared * among a process's helpers) and a generation count. (The generation count is * used to provide an identifier when a helper is added so that it may be * subsequently removed.) The dtrace_helper structure is self-explanatory, * containing pointers to the objects needed to execute the helper. Note that * helpers are _duplicated_ across fork(2), and destroyed on exec(2). No more * than dtrace_helpers_max are allowed per-process. */ #define DTRACE_HELPER_ACTION_USTACK 0 #define DTRACE_NHELPER_ACTIONS 1 typedef struct dtrace_helper_action { int dtha_generation; /* helper action generation */ int dtha_nactions; /* number of actions */ dtrace_difo_t *dtha_predicate; /* helper action predicate */ dtrace_difo_t **dtha_actions; /* array of actions */ struct dtrace_helper_action *dtha_next; /* next helper action */ } dtrace_helper_action_t; typedef struct dtrace_helper_provider { int dthp_generation; /* helper provider generation */ uint32_t dthp_ref; /* reference count */ dof_helper_t dthp_prov; /* DOF w/ provider and probes */ } dtrace_helper_provider_t; typedef struct dtrace_helpers { dtrace_helper_action_t **dthps_actions; /* array of helper actions */ dtrace_vstate_t dthps_vstate; /* helper action var. state */ dtrace_helper_provider_t **dthps_provs; /* array of providers */ uint_t dthps_nprovs; /* count of providers */ uint_t dthps_maxprovs; /* provider array size */ int dthps_generation; /* current generation */ pid_t dthps_pid; /* pid of associated proc */ int dthps_deferred; /* helper in deferred list */ struct dtrace_helpers *dthps_next; /* next pointer */ struct dtrace_helpers *dthps_prev; /* prev pointer */ } dtrace_helpers_t; /* * DTrace Helper Action Tracing * * Debugging helper actions can be arduous. To ease the development and * debugging of helpers, DTrace contains a tracing-framework-within-a-tracing- * framework: helper tracing. If dtrace_helptrace_enabled is non-zero (which * it is by default on DEBUG kernels), all helper activity will be traced to a * global, in-kernel ring buffer. Each entry includes a pointer to the specific * helper, the location within the helper, and a trace of all local variables. * The ring buffer may be displayed in a human-readable format with the * ::dtrace_helptrace mdb(1) dcmd. */ #define DTRACE_HELPTRACE_NEXT (-1) #define DTRACE_HELPTRACE_DONE (-2) #define DTRACE_HELPTRACE_ERR (-3) typedef struct dtrace_helptrace { dtrace_helper_action_t *dtht_helper; /* helper action */ int dtht_where; /* where in helper action */ int dtht_nlocals; /* number of locals */ int dtht_fault; /* type of fault (if any) */ int dtht_fltoffs; /* DIF offset */ uint64_t dtht_illval; /* faulting value */ uint64_t dtht_locals[1]; /* local variables */ } dtrace_helptrace_t; /* * DTrace Credentials * * In probe context, we have limited flexibility to examine the credentials * of the DTrace consumer that created a particular enabling. We use * the Least Privilege interfaces to cache the consumer's cred pointer and * some facts about that credential in a dtrace_cred_t structure. These * can limit the consumer's breadth of visibility and what actions the * consumer may take. */ #define DTRACE_CRV_ALLPROC 0x01 #define DTRACE_CRV_KERNEL 0x02 #define DTRACE_CRV_ALLZONE 0x04 #define DTRACE_CRV_ALL (DTRACE_CRV_ALLPROC | DTRACE_CRV_KERNEL | \ DTRACE_CRV_ALLZONE) #define DTRACE_CRA_PROC 0x0001 #define DTRACE_CRA_PROC_CONTROL 0x0002 #define DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER 0x0004 #define DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE 0x0008 #define DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG 0x0010 #define DTRACE_CRA_KERNEL 0x0020 #define DTRACE_CRA_KERNEL_DESTRUCTIVE 0x0040 #define DTRACE_CRA_ALL (DTRACE_CRA_PROC | \ DTRACE_CRA_PROC_CONTROL | \ DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER | \ DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE | \ DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG | \ DTRACE_CRA_KERNEL | \ DTRACE_CRA_KERNEL_DESTRUCTIVE) typedef struct dtrace_cred { cred_t *dcr_cred; uint8_t dcr_destructive; uint8_t dcr_visible; uint16_t dcr_action; } dtrace_cred_t; /* * DTrace Consumer State * * Each DTrace consumer has an associated dtrace_state structure that contains * its in-kernel DTrace state -- including options, credentials, statistics and * pointers to ECBs, buffers, speculations and formats. A dtrace_state * structure is also allocated for anonymous enablings. When anonymous state * is grabbed, the grabbing consumers dts_anon pointer is set to the grabbed * dtrace_state structure. */ struct dtrace_state { #if defined(sun) dev_t dts_dev; /* device */ #else struct cdev *dts_dev; /* device */ #endif int dts_necbs; /* total number of ECBs */ dtrace_ecb_t **dts_ecbs; /* array of ECBs */ dtrace_epid_t dts_epid; /* next EPID to allocate */ size_t dts_needed; /* greatest needed space */ struct dtrace_state *dts_anon; /* anon. state, if grabbed */ dtrace_activity_t dts_activity; /* current activity */ dtrace_vstate_t dts_vstate; /* variable state */ dtrace_buffer_t *dts_buffer; /* principal buffer */ dtrace_buffer_t *dts_aggbuffer; /* aggregation buffer */ dtrace_speculation_t *dts_speculations; /* speculation array */ int dts_nspeculations; /* number of speculations */ int dts_naggregations; /* number of aggregations */ dtrace_aggregation_t **dts_aggregations; /* aggregation array */ #if defined(sun) vmem_t *dts_aggid_arena; /* arena for aggregation IDs */ #else struct unrhdr *dts_aggid_arena; /* arena for aggregation IDs */ #endif uint64_t dts_errors; /* total number of errors */ uint32_t dts_speculations_busy; /* number of spec. busy */ uint32_t dts_speculations_unavail; /* number of spec unavail */ uint32_t dts_stkstroverflows; /* stack string tab overflows */ uint32_t dts_dblerrors; /* errors in ERROR probes */ uint32_t dts_reserve; /* space reserved for END */ hrtime_t dts_laststatus; /* time of last status */ #if defined(sun) cyclic_id_t dts_cleaner; /* cleaning cyclic */ cyclic_id_t dts_deadman; /* deadman cyclic */ #else struct callout dts_cleaner; /* Cleaning callout. */ struct callout dts_deadman; /* Deadman callout. */ #endif hrtime_t dts_alive; /* time last alive */ char dts_speculates; /* boolean: has speculations */ char dts_destructive; /* boolean: has dest. actions */ int dts_nformats; /* number of formats */ char **dts_formats; /* format string array */ dtrace_optval_t dts_options[DTRACEOPT_MAX]; /* options */ dtrace_cred_t dts_cred; /* credentials */ size_t dts_nretained; /* number of retained enabs */ + int dts_getf; /* number of getf() calls */ }; struct dtrace_provider { dtrace_pattr_t dtpv_attr; /* provider attributes */ dtrace_ppriv_t dtpv_priv; /* provider privileges */ dtrace_pops_t dtpv_pops; /* provider operations */ char *dtpv_name; /* provider name */ void *dtpv_arg; /* provider argument */ hrtime_t dtpv_defunct; /* when made defunct */ struct dtrace_provider *dtpv_next; /* next provider */ }; struct dtrace_meta { dtrace_mops_t dtm_mops; /* meta provider operations */ char *dtm_name; /* meta provider name */ void *dtm_arg; /* meta provider user arg */ uint64_t dtm_count; /* no. of associated provs. */ }; /* * DTrace Enablings * * A dtrace_enabling structure is used to track a collection of ECB * descriptions -- before they have been turned into actual ECBs. This is * created as a result of DOF processing, and is generally used to generate * ECBs immediately thereafter. However, enablings are also generally * retained should the probes they describe be created at a later time; as * each new module or provider registers with the framework, the retained * enablings are reevaluated, with any new match resulting in new ECBs. To * prevent probes from being matched more than once, the enabling tracks the * last probe generation matched, and only matches probes from subsequent * generations. */ typedef struct dtrace_enabling { dtrace_ecbdesc_t **dten_desc; /* all ECB descriptions */ int dten_ndesc; /* number of ECB descriptions */ int dten_maxdesc; /* size of ECB array */ dtrace_vstate_t *dten_vstate; /* associated variable state */ dtrace_genid_t dten_probegen; /* matched probe generation */ dtrace_ecbdesc_t *dten_current; /* current ECB description */ int dten_error; /* current error value */ int dten_primed; /* boolean: set if primed */ struct dtrace_enabling *dten_prev; /* previous enabling */ struct dtrace_enabling *dten_next; /* next enabling */ } dtrace_enabling_t; /* * DTrace Anonymous Enablings * * Anonymous enablings are DTrace enablings that are not associated with a * controlling process, but rather derive their enabling from DOF stored as * properties in the dtrace.conf file. If there is an anonymous enabling, a * DTrace consumer state and enabling are created on attach. The state may be * subsequently grabbed by the first consumer specifying the "grabanon" * option. As long as an anonymous DTrace enabling exists, dtrace(7D) will * refuse to unload. */ typedef struct dtrace_anon { dtrace_state_t *dta_state; /* DTrace consumer state */ dtrace_enabling_t *dta_enabling; /* pointer to enabling */ processorid_t dta_beganon; /* which CPU BEGIN ran on */ } dtrace_anon_t; /* * DTrace Error Debugging */ #ifdef DEBUG #define DTRACE_ERRDEBUG #endif #ifdef DTRACE_ERRDEBUG typedef struct dtrace_errhash { const char *dter_msg; /* error message */ int dter_count; /* number of times seen */ } dtrace_errhash_t; #define DTRACE_ERRHASHSZ 256 /* must be > number of err msgs */ #endif /* DTRACE_ERRDEBUG */ /* * DTrace Toxic Ranges * * DTrace supports safe loads from probe context; if the address turns out to * be invalid, a bit will be set by the kernel indicating that DTrace * encountered a memory error, and DTrace will propagate the error to the user * accordingly. However, there may exist some regions of memory in which an * arbitrary load can change system state, and from which it is impossible to * recover from such a load after it has been attempted. Examples of this may * include memory in which programmable I/O registers are mapped (for which a * read may have some implications for the device) or (in the specific case of * UltraSPARC-I and -II) the virtual address hole. The platform is required * to make DTrace aware of these toxic ranges; DTrace will then check that * target addresses are not in a toxic range before attempting to issue a * safe load. */ typedef struct dtrace_toxrange { uintptr_t dtt_base; /* base of toxic range */ uintptr_t dtt_limit; /* limit of toxic range */ } dtrace_toxrange_t; extern uint64_t dtrace_getarg(int, int); extern greg_t dtrace_getfp(void); extern int dtrace_getipl(void); extern uintptr_t dtrace_caller(int); extern uint32_t dtrace_cas32(uint32_t *, uint32_t, uint32_t); extern void *dtrace_casptr(volatile void *, volatile void *, volatile void *); extern void dtrace_copyin(uintptr_t, uintptr_t, size_t, volatile uint16_t *); extern void dtrace_copyinstr(uintptr_t, uintptr_t, size_t, volatile uint16_t *); extern void dtrace_copyout(uintptr_t, uintptr_t, size_t, volatile uint16_t *); extern void dtrace_copyoutstr(uintptr_t, uintptr_t, size_t, volatile uint16_t *); extern void dtrace_getpcstack(pc_t *, int, int, uint32_t *); extern ulong_t dtrace_getreg(struct trapframe *, uint_t); extern int dtrace_getstackdepth(int); extern void dtrace_getupcstack(uint64_t *, int); extern void dtrace_getufpstack(uint64_t *, uint64_t *, int); extern int dtrace_getustackdepth(void); extern uintptr_t dtrace_fulword(void *); extern uint8_t dtrace_fuword8(void *); extern uint16_t dtrace_fuword16(void *); extern uint32_t dtrace_fuword32(void *); extern uint64_t dtrace_fuword64(void *); extern void dtrace_probe_error(dtrace_state_t *, dtrace_epid_t, int, int, int, uintptr_t); extern int dtrace_assfail(const char *, const char *, int); extern int dtrace_attached(void); #if defined(sun) extern hrtime_t dtrace_gethrestime(void); #endif #ifdef __sparc extern void dtrace_flush_windows(void); extern void dtrace_flush_user_windows(void); extern uint_t dtrace_getotherwin(void); extern uint_t dtrace_getfprs(void); #else extern void dtrace_copy(uintptr_t, uintptr_t, size_t); extern void dtrace_copystr(uintptr_t, uintptr_t, size_t, volatile uint16_t *); #endif /* * DTrace Assertions * * DTrace calls ASSERT from probe context. To assure that a failed ASSERT * does not induce a markedly more catastrophic failure (e.g., one from which * a dump cannot be gleaned), DTrace must define its own ASSERT to be one that * may safely be called from probe context. This header file must thus be * included by any DTrace component that calls ASSERT from probe context, and * _only_ by those components. (The only exception to this is kernel * debugging infrastructure at user-level that doesn't depend on calling * ASSERT.) */ #undef ASSERT #ifdef DEBUG #define ASSERT(EX) ((void)((EX) || \ dtrace_assfail(#EX, __FILE__, __LINE__))) #else #define ASSERT(X) ((void)0) #endif #ifdef __cplusplus } #endif #endif /* _SYS_DTRACE_IMPL_H */ Index: stable/10/sys/cddl/dev/dtrace/dtrace_cddl.h =================================================================== --- stable/10/sys/cddl/dev/dtrace/dtrace_cddl.h (revision 268577) +++ stable/10/sys/cddl/dev/dtrace/dtrace_cddl.h (revision 268578) @@ -1,164 +1,165 @@ /* * 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 * * $FreeBSD$ * */ #ifndef _DTRACE_CDDL_H_ #define _DTRACE_CDDL_H_ #include #define LOCK_LEVEL 10 /* * Kernel DTrace extension to 'struct proc' for FreeBSD. */ typedef struct kdtrace_proc { int p_dtrace_probes; /* Are there probes for this proc? */ u_int64_t p_dtrace_count; /* Number of DTrace tracepoints */ void *p_dtrace_helpers; /* DTrace helpers, if any */ int p_dtrace_model; } kdtrace_proc_t; /* * Kernel DTrace extension to 'struct thread' for FreeBSD. */ typedef struct kdtrace_thread { u_int8_t td_dtrace_stop; /* Indicates a DTrace-desired stop */ u_int8_t td_dtrace_sig; /* Signal sent via DTrace's raise() */ u_int td_predcache; /* DTrace predicate cache */ u_int64_t td_dtrace_vtime; /* DTrace virtual time */ u_int64_t td_dtrace_start; /* DTrace slice start time */ union __tdu { struct __tds { u_int8_t _td_dtrace_on; /* Hit a fasttrap tracepoint. */ u_int8_t _td_dtrace_step; /* About to return to kernel. */ u_int8_t _td_dtrace_ret; /* Handling a return probe. */ u_int8_t _td_dtrace_ast; /* Saved ast flag. */ #ifdef __amd64__ u_int8_t _td_dtrace_reg; #endif } _tds; u_long _td_dtrace_ft; /* Bitwise or of these flags. */ } _tdu; #define td_dtrace_ft _tdu._td_dtrace_ft #define td_dtrace_on _tdu._tds._td_dtrace_on #define td_dtrace_step _tdu._tds._td_dtrace_step #define td_dtrace_ret _tdu._tds._td_dtrace_ret #define td_dtrace_ast _tdu._tds._td_dtrace_ast #define td_dtrace_reg _tdu._tds._td_dtrace_reg uintptr_t td_dtrace_pc; /* DTrace saved pc from fasttrap. */ uintptr_t td_dtrace_npc; /* DTrace next pc from fasttrap. */ uintptr_t td_dtrace_scrpc; /* DTrace per-thread scratch location. */ uintptr_t td_dtrace_astpc; /* DTrace return sequence location. */ #ifdef __amd64__ uintptr_t td_dtrace_regv; #endif u_int64_t td_hrtime; /* Last time on cpu. */ int td_errno; /* Syscall return value. */ } kdtrace_thread_t; /* * Definitions to reference fields in the FreeBSD DTrace structures defined * above using the names of fields in similar structures in Solaris. Note * that the separation on FreeBSD is a licensing constraint designed to * keep the GENERIC kernel BSD licensed. */ #define t_dtrace_vtime td_dtrace->td_dtrace_vtime #define t_dtrace_start td_dtrace->td_dtrace_start #define t_dtrace_stop td_dtrace->td_dtrace_stop #define t_dtrace_sig td_dtrace->td_dtrace_sig #define t_predcache td_dtrace->td_predcache #define t_dtrace_ft td_dtrace->td_dtrace_ft #define t_dtrace_on td_dtrace->td_dtrace_on #define t_dtrace_step td_dtrace->td_dtrace_step #define t_dtrace_ret td_dtrace->td_dtrace_ret #define t_dtrace_ast td_dtrace->td_dtrace_ast #define t_dtrace_reg td_dtrace->td_dtrace_reg #define t_dtrace_pc td_dtrace->td_dtrace_pc #define t_dtrace_npc td_dtrace->td_dtrace_npc #define t_dtrace_scrpc td_dtrace->td_dtrace_scrpc #define t_dtrace_astpc td_dtrace->td_dtrace_astpc #define t_dtrace_regv td_dtrace->td_dtrace_regv #define p_dtrace_helpers p_dtrace->p_dtrace_helpers #define p_dtrace_count p_dtrace->p_dtrace_count #define p_dtrace_probes p_dtrace->p_dtrace_probes #define p_model p_dtrace->p_dtrace_model #define DATAMODEL_NATIVE 0 #ifdef __amd64__ #define DATAMODEL_LP64 0 #define DATAMODEL_ILP32 1 #else #define DATAMODEL_LP64 1 #define DATAMODEL_ILP32 0 #endif /* * Definitions for fields in struct proc which are named differently in FreeBSD. */ #define p_cred p_ucred #define p_parent p_pptr /* * Definitions for fields in struct thread which are named differently in FreeBSD. */ #define t_procp td_proc #define t_tid td_tid #define t_did td_tid +#define t_cred td_ucred int priv_policy(const cred_t *, int, boolean_t, int, const char *); boolean_t priv_policy_only(const cred_t *, int, boolean_t); boolean_t priv_policy_choice(const cred_t *, int, boolean_t); /* * Test privilege. Audit success or failure, allow privilege debugging. * Returns 0 for success, err for failure. */ #define PRIV_POLICY(cred, priv, all, err, reason) \ priv_policy((cred), (priv), (all), (err), (reason)) /* * Test privilege. Audit success only, no privilege debugging. * Returns 1 for success, and 0 for failure. */ #define PRIV_POLICY_CHOICE(cred, priv, all) \ priv_policy_choice((cred), (priv), (all)) /* * Test privilege. No priv_debugging, no auditing. * Returns 1 for success, and 0 for failure. */ #define PRIV_POLICY_ONLY(cred, priv, all) \ priv_policy_only((cred), (priv), (all)) #endif /* !_DTRACE_CDDL_H_ */ Index: stable/10/sys/modules/dtrace/dtrace/Makefile =================================================================== --- stable/10/sys/modules/dtrace/dtrace/Makefile (revision 268577) +++ stable/10/sys/modules/dtrace/dtrace/Makefile (revision 268578) @@ -1,60 +1,61 @@ # $FreeBSD$ ARCHDIR= ${MACHINE_CPUARCH} .PATH: ${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common/dtrace .PATH: ${.CURDIR}/../../../cddl/compat/opensolaris/kern .PATH: ${.CURDIR}/../../../cddl/kern .PATH: ${.CURDIR}/../../../cddl/dev/dtrace .PATH: ${.CURDIR}/../../../cddl/dev/dtrace/${ARCHDIR} KMOD= dtrace SRCS= dtrace.c \ dtrace_asm.S \ dtrace_subr.c .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" .PATH: ${.CURDIR}/../../../cddl/dev/dtrace/x86 SRCS+= dis_tables.c \ instr_size.c CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/intel \ -I${.CURDIR}/../../../cddl/dev/dtrace/x86 .endif SRCS+= bus_if.h device_if.h vnode_if.h # Needed for dtrace_asm.S SRCS+= assym.s # These are needed for assym.s SRCS+= opt_compat.h opt_kstack_pages.h opt_nfs.h opt_hwpmc_hooks.h #This is needed for dtrace.c SRCS += opensolaris_taskq.c .if ${MACHINE_CPUARCH} == "i386" SRCS+= opt_apic.h .endif CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \ -I${.CURDIR}/../../../cddl/dev/dtrace \ -I${.CURDIR}/../../../cddl/dev/dtrace/${ARCHDIR} \ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \ + -I${.CURDIR}/../../../../cddl/contrib/opensolaris/common/util \ -I${.CURDIR}/../../.. -DDIS_MEM CFLAGS+= -DSMP EXPORT_SYMS= dtrace_register \ dtrace_unregister \ dtrace_probe_lookup dtrace_asm.o: assym.s .include CFLAGS+= -include ${.CURDIR}/../../../cddl/compat/opensolaris/sys/debug_compat.h CWARNFLAGS+= -Wno-parentheses CWARNFLAGS+= -Wno-uninitialized CWARNFLAGS+= -Wno-cast-qual CWARNFLAGS+= -Wno-unused Index: stable/10 =================================================================== --- stable/10 (revision 268577) +++ stable/10 (revision 268578) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r267929,267937,267939-267942,267987,268006