From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../VBoxDTrace/onnv/lib/libdtrace/Makefile | 63 + .../VBoxDTrace/onnv/lib/libdtrace/Makefile.com | 233 + .../VBoxDTrace/onnv/lib/libdtrace/Makefile.kup | 0 .../VBoxDTrace/onnv/lib/libdtrace/amd64/Makefile | 46 + .../onnv/lib/libdtrace/amd64/Makefile.kup | 0 .../onnv/lib/libdtrace/common/Makefile.kup | 0 .../VBoxDTrace/onnv/lib/libdtrace/common/drti.c | 190 + .../onnv/lib/libdtrace/common/dt_aggregate.c | 1917 ++++++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_as.c | 507 ++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_as.h | 66 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.c | 181 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.h | 71 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_cc.c | 2321 +++++++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_cg.c | 1973 ++++++++ .../onnv/lib/libdtrace/common/dt_consume.c | 2342 ++++++++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.c | 1130 +++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.h | 128 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_dis.c | 524 +++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.c | 969 ++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.h | 68 + .../onnv/lib/libdtrace/common/dt_error.c | 219 + .../onnv/lib/libdtrace/common/dt_errtags.h | 249 + .../onnv/lib/libdtrace/common/dt_grammar.y | 846 ++++ .../onnv/lib/libdtrace/common/dt_handle.c | 483 ++ .../onnv/lib/libdtrace/common/dt_ident.c | 1049 +++++ .../onnv/lib/libdtrace/common/dt_ident.h | 185 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_impl.h | 669 +++ .../onnv/lib/libdtrace/common/dt_inttab.c | 117 + .../onnv/lib/libdtrace/common/dt_inttab.h | 71 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_lex.l | 931 ++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_link.c | 1634 +++++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_list.c | 117 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_list.h | 57 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_map.c | 428 ++ .../onnv/lib/libdtrace/common/dt_module.c | 1348 ++++++ .../onnv/lib/libdtrace/common/dt_module.h | 58 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_open.c | 1500 ++++++ .../onnv/lib/libdtrace/common/dt_options.c | 1118 +++++ .../onnv/lib/libdtrace/common/dt_parser.c | 4907 ++++++++++++++++++++ .../onnv/lib/libdtrace/common/dt_parser.h | 290 ++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.c | 193 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.h | 105 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.c | 757 +++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.h | 66 + .../onnv/lib/libdtrace/common/dt_pragma.c | 509 ++ .../onnv/lib/libdtrace/common/dt_printf.c | 2057 ++++++++ .../onnv/lib/libdtrace/common/dt_printf.h | 137 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_proc.c | 1111 +++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_proc.h | 136 + .../onnv/lib/libdtrace/common/dt_program.c | 622 +++ .../onnv/lib/libdtrace/common/dt_program.h | 65 + .../onnv/lib/libdtrace/common/dt_provider.c | 890 ++++ .../onnv/lib/libdtrace/common/dt_provider.h | 120 + .../onnv/lib/libdtrace/common/dt_regset.c | 112 + .../onnv/lib/libdtrace/common/dt_regset.h | 57 + .../onnv/lib/libdtrace/common/dt_string.c | 348 ++ .../onnv/lib/libdtrace/common/dt_string.h | 51 + .../onnv/lib/libdtrace/common/dt_strtab.c | 295 ++ .../onnv/lib/libdtrace/common/dt_strtab.h | 76 + .../VBoxDTrace/onnv/lib/libdtrace/common/dt_subr.c | 1028 ++++ .../VBoxDTrace/onnv/lib/libdtrace/common/dt_work.c | 322 ++ .../onnv/lib/libdtrace/common/dt_xlator.c | 390 ++ .../onnv/lib/libdtrace/common/dt_xlator.h | 89 + .../VBoxDTrace/onnv/lib/libdtrace/common/dtrace.h | 595 +++ .../VBoxDTrace/onnv/lib/libdtrace/common/fc.d | 183 + .../VBoxDTrace/onnv/lib/libdtrace/common/io.d.in | 218 + .../VBoxDTrace/onnv/lib/libdtrace/common/io.sed.in | 69 + .../VBoxDTrace/onnv/lib/libdtrace/common/ip.d.in | 374 ++ .../VBoxDTrace/onnv/lib/libdtrace/common/ip.sed.in | 78 + .../VBoxDTrace/onnv/lib/libdtrace/common/iscsit.d | 454 ++ .../onnv/lib/libdtrace/common/llib-ldtrace | 32 + .../onnv/lib/libdtrace/common/mapfile-vers | 127 + .../onnv/lib/libdtrace/common/mkerrno.sh | 40 + .../onnv/lib/libdtrace/common/mkerrtags.sed | 33 + .../onnv/lib/libdtrace/common/mkerrtags.sh | 61 + .../onnv/lib/libdtrace/common/mknames.sed | 38 + .../onnv/lib/libdtrace/common/mknames.sh | 53 + .../onnv/lib/libdtrace/common/mksignal.sh | 40 + .../VBoxDTrace/onnv/lib/libdtrace/common/net.d.in | 44 + .../onnv/lib/libdtrace/common/net.sed.in | 41 + .../VBoxDTrace/onnv/lib/libdtrace/common/nfs.d | 138 + .../onnv/lib/libdtrace/common/procfs.d.in | 365 ++ .../onnv/lib/libdtrace/common/procfs.sed.in | 163 + .../VBoxDTrace/onnv/lib/libdtrace/common/sched.d | 82 + .../VBoxDTrace/onnv/lib/libdtrace/common/scsi.d | 89 + .../VBoxDTrace/onnv/lib/libdtrace/common/srp.d | 168 + .../onnv/lib/libdtrace/common/sysevent.d.in | 50 + .../onnv/lib/libdtrace/common/sysevent.sed.in | 34 + .../VBoxDTrace/onnv/lib/libdtrace/common/tcp.d.in | 269 ++ .../onnv/lib/libdtrace/common/tcp.sed.in | 53 + .../VBoxDTrace/onnv/lib/libdtrace/common/udp.d.in | 73 + .../onnv/lib/libdtrace/common/udp.sed.in | 29 + .../VBoxDTrace/onnv/lib/libdtrace/common/unistd.d | 56 + .../VBoxDTrace/onnv/lib/libdtrace/i386/Makefile | 54 + .../onnv/lib/libdtrace/i386/Makefile.kup | 0 .../VBoxDTrace/onnv/lib/libdtrace/i386/dt_isadep.c | 488 ++ .../VBoxDTrace/onnv/lib/libdtrace/i386/regs.d.in | 117 + .../VBoxDTrace/onnv/lib/libdtrace/i386/regs.sed.in | 82 + .../VBoxDTrace/onnv/lib/libdtrace/sparc/Makefile | 33 + .../onnv/lib/libdtrace/sparc/Makefile.kup | 0 .../onnv/lib/libdtrace/sparc/dt_isadep.c | 338 ++ .../VBoxDTrace/onnv/lib/libdtrace/sparc/regs.d | 120 + .../VBoxDTrace/onnv/lib/libdtrace/sparcv9/Makefile | 36 + .../onnv/lib/libdtrace/sparcv9/Makefile.kup | 0 104 files changed, 43858 insertions(+) create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile.com create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile.kup create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/amd64/Makefile create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/amd64/Makefile.kup create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/Makefile.kup create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/drti.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_aggregate.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_as.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_as.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_cc.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_cg.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_consume.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dis.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_error.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_errtags.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_grammar.y create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_handle.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_ident.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_ident.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_impl.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_inttab.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_inttab.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_lex.l create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_link.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_list.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_list.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_map.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_module.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_module.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_open.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_options.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_parser.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_parser.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pragma.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_printf.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_printf.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_proc.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_proc.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_program.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_program.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_provider.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_provider.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_regset.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_regset.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_string.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_string.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_strtab.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_strtab.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_subr.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_work.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_xlator.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_xlator.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dtrace.h create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/fc.d create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/io.d.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/io.sed.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/ip.d.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/ip.sed.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/iscsit.d create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/llib-ldtrace create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/mapfile-vers create mode 100755 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/mkerrno.sh create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/mkerrtags.sed create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/mkerrtags.sh create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/mknames.sed create mode 100755 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/mknames.sh create mode 100755 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/mksignal.sh create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/net.d.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/net.sed.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/nfs.d create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/procfs.d.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/procfs.sed.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/sched.d create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/scsi.d create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/srp.d create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/sysevent.d.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/sysevent.sed.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/tcp.d.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/tcp.sed.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/udp.d.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/udp.sed.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/unistd.d create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/i386/Makefile create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/i386/Makefile.kup create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/i386/dt_isadep.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/i386/regs.d.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/i386/regs.sed.in create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/sparc/Makefile create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/sparc/Makefile.kup create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/sparc/dt_isadep.c create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/sparc/regs.d create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/sparcv9/Makefile create mode 100644 src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/sparcv9/Makefile.kup (limited to 'src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace') diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile new file mode 100644 index 00000000..c24a0800 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile @@ -0,0 +1,63 @@ +# +# 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. +# + +include ../Makefile.lib + +HDRS = dtrace.h +HDRDIR = common + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint +yydebug := TARGET = yydebug + +.KEEP_STATE: + +all clean clobber lint yydebug: $(SUBDIRS) + +install: install_h $(SUBDIRS) + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ + +# +# Cross-reference customization: build the cross-reference only over the +# source directories, and ignore Makefiles and machine-generated source. +# +XRDIRS = common i386 sparc sparcv9 +XRDEL = dt_lex.c dt_grammar.c Makefile* diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile.com b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile.com new file mode 100644 index 00000000..c4f967fc --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile.com @@ -0,0 +1,233 @@ +# +# 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. +# +# + +LIBRARY = libdtrace.a +VERS = .1 + +LIBSRCS = \ + dt_aggregate.c \ + dt_as.c \ + dt_buf.c \ + dt_cc.c \ + dt_cg.c \ + dt_consume.c \ + dt_decl.c \ + dt_dis.c \ + dt_dof.c \ + dt_error.c \ + dt_errtags.c \ + dt_handle.c \ + dt_ident.c \ + dt_inttab.c \ + dt_link.c \ + dt_list.c \ + dt_open.c \ + dt_options.c \ + dt_program.c \ + dt_map.c \ + dt_module.c \ + dt_names.c \ + dt_parser.c \ + dt_pcb.c \ + dt_pid.c \ + dt_pragma.c \ + dt_printf.c \ + dt_proc.c \ + dt_provider.c \ + dt_regset.c \ + dt_string.c \ + dt_strtab.c \ + dt_subr.c \ + dt_work.c \ + dt_xlator.c + +LIBISASRCS = \ + dt_isadep.c + +OBJECTS = dt_lex.o dt_grammar.o $(MACHOBJS) $(LIBSRCS:%.c=%.o) $(LIBISASRCS:%.c=%.o) + +DRTISRC = drti.c +DRTIOBJ = $(DRTISRC:%.c=%.o) + +DLIBSRCS += \ + errno.d \ + fc.d \ + io.d \ + ip.d \ + iscsit.d \ + net.d \ + nfs.d \ + procfs.d \ + regs.d \ + sched.d \ + signal.d \ + scsi.d \ + srp.d \ + sysevent.d \ + tcp.d \ + udp.d \ + unistd.d + +include ../../Makefile.lib + +SRCS = $(LIBSRCS:%.c=../common/%.c) $(LIBISASRCS:%.c=../$(MACH)/%.c) +LIBS = $(DYNLIB) $(LINTLIB) + +SRCDIR = ../common + +CLEANFILES += dt_lex.c dt_grammar.c dt_grammar.h y.output +CLEANFILES += ../common/procfs.sed ../common/procfs.d +CLEANFILES += ../common/io.sed ../common/io.d +CLEANFILES += ../common/ip.sed ../common/ip.d +CLEANFILES += ../common/net.sed ../common/net.d +CLEANFILES += ../common/errno.d ../common/signal.d +CLEANFILES += ../common/dt_errtags.c ../common/dt_names.c +CLEANFILES += ../common/sysevent.sed ../common/sysevent.d +CLEANFILES += ../common/tcp.sed ../common/tcp.d +CLEANFILES += ../common/udp.sed ../common/udp.d + +CLOBBERFILES += drti.o + +CPPFLAGS += -I../common -I. +CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS) +CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS) +YYCFLAGS = +LDLIBS += -lgen -lproc -lrtld_db -lnsl -lsocket -lctf -lelf -lc +DRTILDLIBS = $(LDLIBS.lib) -lc + +yydebug := YYCFLAGS += -DYYDEBUG + +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) + +LFLAGS = -t -v +YFLAGS = -d -v + +ROOTDLIBDIR = $(ROOT)/usr/lib/dtrace +ROOTDLIBDIR64 = $(ROOT)/usr/lib/dtrace/64 + +ROOTDLIBS = $(DLIBSRCS:%=$(ROOTDLIBDIR)/%) +ROOTDOBJS = $(ROOTDLIBDIR)/$(DRTIOBJ) +ROOTDOBJS64 = $(ROOTDLIBDIR64)/$(DRTIOBJ) + +.KEEP_STATE: + +all: $(LIBS) $(DRTIOBJ) + +lint: lintdrti lintcheck + +lintdrti: ../common/$(DRTISRC) + $(LINT.c) ../common/$(DRTISRC) $(DRTILDLIBS) + +dt_lex.c: $(SRCDIR)/dt_lex.l dt_grammar.h + $(LEX) $(LFLAGS) $(SRCDIR)/dt_lex.l > $@ + +dt_grammar.c dt_grammar.h: $(SRCDIR)/dt_grammar.y + $(YACC) $(YFLAGS) $(SRCDIR)/dt_grammar.y + @mv y.tab.h dt_grammar.h + @mv y.tab.c dt_grammar.c + +pics/dt_lex.o pics/dt_grammar.o := CFLAGS += $(YYCFLAGS) +pics/dt_lex.o pics/dt_grammar.o := CFLAGS64 += $(YYCFLAGS) + +pics/dt_lex.o pics/dt_grammar.o := CERRWARN += -erroff=E_STATEMENT_NOT_REACHED +pics/dt_lex.o pics/dt_grammar.o := CCVERBOSE = + +../common/dt_errtags.c: ../common/mkerrtags.sh ../common/dt_errtags.h + sh ../common/mkerrtags.sh < ../common/dt_errtags.h > $@ + +../common/dt_names.c: ../common/mknames.sh $(SRC)/uts/common/sys/dtrace.h + sh ../common/mknames.sh < $(SRC)/uts/common/sys/dtrace.h > $@ + +../common/errno.d: ../common/mkerrno.sh $(SRC)/uts/common/sys/errno.h + sh ../common/mkerrno.sh < $(SRC)/uts/common/sys/errno.h > $@ + +../common/signal.d: ../common/mksignal.sh $(SRC)/uts/common/sys/iso/signal_iso.h + sh ../common/mksignal.sh < $(SRC)/uts/common/sys/iso/signal_iso.h > $@ + +../common/%.sed: ../common/%.sed.in + $(COMPILE.cpp) -D_KERNEL $< | tr -d ' ' | tr '"' '@' | \ + sed 's/\&/\\\&/g' | grep '^s/' > $@ + +../common/procfs.d: ../common/procfs.sed ../common/procfs.d.in + sed -f ../common/procfs.sed < ../common/procfs.d.in > $@ + +../common/io.d: ../common/io.sed ../common/io.d.in + sed -f ../common/io.sed < ../common/io.d.in > $@ + +../common/ip.d: ../common/ip.sed ../common/ip.d.in + sed -f ../common/ip.sed < ../common/ip.d.in > $@ + +../common/net.d: ../common/net.sed ../common/net.d.in + sed -f ../common/net.sed < ../common/net.d.in > $@ + +../common/sysevent.d: ../common/sysevent.sed ../common/sysevent.d.in + sed -f ../common/sysevent.sed < ../common/sysevent.d.in > $@ + +../common/tcp.d: ..//common/tcp.sed ../common/tcp.d.in + sed -f ../common/tcp.sed < ../common/tcp.d.in > $@ + +../common/udp.d: ../common/udp.sed ../common/udp.d.in + sed -f ../common/udp.sed < ../common/udp.d.in > $@ + +pics/%.o: ../$(MACH)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: ../$(MACH)/%.s + $(COMPILE.s) -o $@ $< + $(POST_PROCESS_O) + +%.o: ../common/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +$(ROOTDLIBDIR): + $(INS.dir) + +$(ROOTDLIBDIR64): $(ROOTDLIBDIR) + $(INS.dir) + +$(ROOTDLIBDIR)/%.d: ../common/%.d + $(INS.file) + +$(ROOTDLIBDIR)/%.d: ../$(MACH)/%.d + $(INS.file) + +$(ROOTDLIBDIR)/%.d: %.d + $(INS.file) + +$(ROOTDLIBDIR)/%.o: %.o + $(INS.file) + +$(ROOTDLIBDIR64)/%.o: %.o + $(INS.file) + +$(ROOTDLIBS): $(ROOTDLIBDIR) + +$(ROOTDOBJS): $(ROOTDLIBDIR) + +$(ROOTDOBJS64): $(ROOTDLIBDIR64) + +include ../../Makefile.targ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile.kup b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/amd64/Makefile b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/amd64/Makefile new file mode 100644 index 00000000..e9cdd5bd --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/amd64/Makefile @@ -0,0 +1,46 @@ +# +# 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. +# +# ident "%Z%%M% %I% %E% SMI" +# + +ASFLAGS += $(AS_PICFLAGS) -P -D_ASM + +MACHOBJS = dis_tables.o + +include ../Makefile.com +include ../../Makefile.lib.64 + +SRCS += $(SRC)/common/dis/i386/dis_tables.c +CPPFLAGS += -I$(SRC)/common/dis/i386 +CPPFLAGS += -D_ELF64 + +LINTFLAGS64 += -erroff=E_BAD_PTR_CAST_ALIGN + +pics/%.o: $(SRC)/common/dis/i386/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +install yydebug: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) \ + $(ROOTDLIBS) $(ROOTDOBJS64) diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/amd64/Makefile.kup b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/amd64/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/Makefile.kup b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/drti.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/drti.c new file mode 100644 index 00000000..3b5f0cbb --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/drti.c @@ -0,0 +1,190 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * In Solaris 10 GA, the only mechanism for communicating helper information + * is through the DTrace helper pseudo-device node in /devices; there is + * no /dev link. Because of this, USDT providers and helper actions don't + * work inside of non-global zones. This issue was addressed by adding + * the /dev and having this initialization code use that /dev link. If the + * /dev link doesn't exist it falls back to looking for the /devices node + * as this code may be embedded in a binary which runs on Solaris 10 GA. + * + * Users may set the following environment variable to affect the way + * helper initialization takes place: + * + * DTRACE_DOF_INIT_DEBUG enable debugging output + * DTRACE_DOF_INIT_DISABLE disable helper loading + * DTRACE_DOF_INIT_DEVNAME set the path to the helper node + */ + +static const char *devname = "/dev/dtrace/helper"; +static const char *olddevname = "/devices/pseudo/dtrace@0:helper"; + +static const char *modname; /* Name of this load object */ +static int gen; /* DOF helper generation */ +extern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */ +static boolean_t dof_init_debug = B_FALSE; /* From DTRACE_DOF_INIT_DEBUG */ + +static void +dprintf(int debug, const char *fmt, ...) +{ + va_list ap; + + if (debug && !dof_init_debug) + return; + + va_start(ap, fmt); + + if (modname == NULL) + (void) fprintf(stderr, "dtrace DOF: "); + else + (void) fprintf(stderr, "dtrace DOF %s: ", modname); + + (void) vfprintf(stderr, fmt, ap); + + if (fmt[strlen(fmt) - 1] != '\n') + (void) fprintf(stderr, ": %s\n", strerror(errno)); + + va_end(ap); +} + +#pragma init(dtrace_dof_init) +static void +dtrace_dof_init(void) +{ + dof_hdr_t *dof = &__SUNW_dof; +#ifdef _LP64 + Elf64_Ehdr *elf; +#else + Elf32_Ehdr *elf; +#endif + dof_helper_t dh; + Link_map *lmp; + Lmid_t lmid; + int fd; + const char *p; + + if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL) + return; + + if (getenv("DTRACE_DOF_INIT_DEBUG") != NULL) + dof_init_debug = B_TRUE; + + if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) { + dprintf(1, "couldn't discover module name or address\n"); + return; + } + + if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) { + dprintf(1, "couldn't discover link map ID\n"); + return; + } + + if ((modname = strrchr(lmp->l_name, '/')) == NULL) + modname = lmp->l_name; + else + modname++; + + if (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) { + dprintf(0, ".SUNW_dof section corrupt\n"); + return; + } + + elf = (void *)lmp->l_addr; + + dh.dofhp_dof = (uintptr_t)dof; + dh.dofhp_addr = elf->e_type == ET_DYN ? lmp->l_addr : 0; + + if (lmid == 0) { + (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), + "%s", modname); + } else { + (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), + "LM%lu`%s", lmid, modname); + } + + if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL) + devname = p; + + if ((fd = open64(devname, O_RDWR)) < 0) { + dprintf(1, "failed to open helper device %s", devname); + + /* + * If the device path wasn't explicitly set, try again with + * the old device path. + */ + if (p != NULL) + return; + + devname = olddevname; + + if ((fd = open64(devname, O_RDWR)) < 0) { + dprintf(1, "failed to open helper device %s", devname); + return; + } + } + + if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1) + dprintf(1, "DTrace ioctl failed for DOF at %p", dof); + else + dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof); + + (void) close(fd); +} + +#pragma fini(dtrace_dof_fini) +static void +dtrace_dof_fini(void) +{ + int fd; + + if ((fd = open64(devname, O_RDWR)) < 0) { + dprintf(1, "failed to open helper device %s", devname); + return; + } + + if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, gen)) == -1) + dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen); + else + dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen); + + (void) close(fd); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_aggregate.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_aggregate.c new file mode 100644 index 00000000..831b1081 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_aggregate.c @@ -0,0 +1,1917 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# include "dt_impl.h" +# ifdef _MSC_VER +# pragma warning(disable:4018) /* signed/unsigned comparsion mismatch warning */ +# endif +# include +#endif /* VBOX */ + +#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. + */ +#ifndef VBOX +static pthread_mutex_t dt_qsort_lock = PTHREAD_MUTEX_INITIALIZER; +#else +RTCRITSECT dt_qsort_lock; +#endif + +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) +{ + VBDTTYPE(size_t,int) 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) +{ + RT_NOREF1(size); + if (*new < *existing) + *existing = *new; +} + +/*ARGSUSED*/ +static void +dt_aggregate_max(int64_t *existing, int64_t *new, size_t size) +{ + RT_NOREF1(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; + RT_NOREF1(size); + + 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 int +dt_aggregate_quantizedcmp(int64_t *lhs, int64_t *rhs) +{ + int nbuckets = DTRACE_QUANTIZE_NBUCKETS, i; + long double ltotal = 0, rtotal = 0; + int64_t lzero VBDTMSC(0), rzero VBDTMSC(0); + + 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) +{ +#ifndef VBOX + 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); +#else + RT_NOREF2(dtp, data); +#endif +} + +static void +dt_aggregate_umod(dtrace_hdl_t *dtp, uint64_t *data) +{ +#ifndef VBOX + 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); +#else + RT_NOREF2(dtp, data); +#endif +} + +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 (dt_ioctl(dtp, DTRACEIOC_AGGSNAP, buf) == -1) { + 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 ((unsigned)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_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 VBDTMSC(NULL), *rrec VBDTMSC(NULL); + int64_t *laddr, *raddr; + int rval, i; + + if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0) + return (rval); + + if (lagg->dtagd_nrecs > ragg->dtagd_nrecs) + return (DT_GREATERTHAN); + + if (lagg->dtagd_nrecs < ragg->dtagd_nrecs) + return (DT_LESSTHAN); + + for (i = 0; i < lagg->dtagd_nrecs; i++) { + lrec = &lagg->dtagd_rec[i]; + rrec = &ragg->dtagd_rec[i]; + + if (lrec->dtrd_offset < rrec->dtrd_offset) + return (DT_LESSTHAN); + + if (lrec->dtrd_offset > rrec->dtrd_offset) + return (DT_GREATERTHAN); + + if (lrec->dtrd_action < rrec->dtrd_action) + return (DT_LESSTHAN); + + if (lrec->dtrd_action > rrec->dtrd_action) + return (DT_GREATERTHAN); + } + + 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_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); + +#ifndef VBOX + agp->dtat_maxcpu = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; + agp->dtat_ncpu = dt_sysconf(dtp, _SC_NPROCESSORS_MAX); +#else + agp->dtat_maxcpu = RTMpGetMaxCpuId() + 1; + agp->dtat_ncpu = RTMpGetCount(); +#endif + 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 i, 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_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; + + 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); + + for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) + sorted[i++] = h; + +#ifndef VBOX + (void) pthread_mutex_lock(&dt_qsort_lock); +#else + RTCritSectEnter(&dt_qsort_lock); +#endif + + 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); + } + +#ifndef VBOX + (void) pthread_mutex_unlock(&dt_qsort_lock); +#else + RTCritSectLeave(&dt_qsort_lock); +#endif + + 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); + } + } + + dt_free(dtp, sorted); + return (0); +} + +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 VBDTMSC(NULL), **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((size_t/*vbox*/)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. + */ +#ifndef VBOX + (void) pthread_mutex_lock(&dt_qsort_lock); +#else + RTCritSectEnter(&dt_qsort_lock); +#endif + + 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; (size_t/*vbox*/)i <= nentries; i++) { + if ((size_t/*vbox*/)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((size_t/*vbox*/)i - start <= (size_t/*vbox*/)naggvars); + bundlesize = (naggvars + 2) * sizeof (dt_ahashent_t *); + + if ((nbundle = dt_zalloc(dtp, bundlesize)) == NULL) { +#ifndef VBOX + (void) pthread_mutex_unlock(&dt_qsort_lock); +#else + RTCritSectLeave(&dt_qsort_lock); +#endif + goto out; + } + + for (j = VBDTCAST(int)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); + +#ifndef VBOX + (void) pthread_mutex_unlock(&dt_qsort_lock); +#else + RTCritSectLeave(&dt_qsort_lock); +#endif + + /* + * 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; (size_t/*vbox*/)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; (size_t/*vbox*/)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; + + 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); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_as.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_as.c new file mode 100644 index 00000000..728457cd --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_as.c @@ -0,0 +1,507 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#else /* VBOX */ +# include +#endif /* VBOX */ + +#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; + RT_NOREF1(dhp); + + 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; + RT_NOREF1(dhp); + + 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_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 VBDTMSC(0), kbits VBDTMSC(0), umask VBDTMSC(0), ubits VBDTMSC(0); + 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 = ~0u; + 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 = ~0u; + 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); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_as.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_as.h new file mode 100644 index 00000000..cb246402 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_as.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef _DT_AS_H +#define _DT_AS_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#endif +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dt_irnode { + uint_t di_label; /* label number or DT_LBL_NONE */ + dif_instr_t di_instr; /* instruction opcode */ + void *di_extern; /* opcode-specific external reference */ + struct dt_irnode *di_next; /* next instruction */ +} dt_irnode_t; + +#define DT_LBL_NONE 0 /* no label on this instruction */ + +typedef struct dt_irlist { + dt_irnode_t *dl_list; /* pointer to first node in list */ + dt_irnode_t *dl_last; /* pointer to last node in list */ + uint_t dl_len; /* number of valid instructions */ + uint_t dl_label; /* next label number to assign */ +} dt_irlist_t; + +extern void dt_irlist_create(dt_irlist_t *); +extern void dt_irlist_destroy(dt_irlist_t *); +extern void dt_irlist_append(dt_irlist_t *, dt_irnode_t *); +extern uint_t dt_irlist_label(dt_irlist_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_AS_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.c new file mode 100644 index 00000000..e7b22bde --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.c @@ -0,0 +1,181 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +/* + * DTrace Memory Buffer Routines + * + * The routines in this file are used to create an automatically resizing + * memory buffer that can be written to like a file. Memory buffers are + * used to construct DOF to ioctl() to dtrace(7D), and provide semantics that + * simplify caller code. Specifically, any allocation errors result in an + * error code being set inside the buffer which is maintained persistently and + * propagates to another buffer if the buffer in error is concatenated. These + * semantics permit callers to execute a large series of writes without needing + * to check for errors and then perform a single check before using the buffer. + */ + +#ifndef VBOX +#include +#include +#endif + +#include +#include + +void +dt_buf_create(dtrace_hdl_t *dtp, dt_buf_t *bp, const char *name, size_t len) +{ + if (len == 0) + len = _dtrace_bufsize; + + bp->dbu_buf = bp->dbu_ptr = dt_zalloc(dtp, len); + bp->dbu_len = len; + + if (bp->dbu_buf == NULL) + bp->dbu_err = dtrace_errno(dtp); + else + bp->dbu_err = 0; + + bp->dbu_resizes = 0; + bp->dbu_name = name; +} + +void +dt_buf_destroy(dtrace_hdl_t *dtp, dt_buf_t *bp) +{ + dt_dprintf("dt_buf_destroy(%s): size=%lu resizes=%u\n", + bp->dbu_name, (ulong_t)bp->dbu_len, bp->dbu_resizes); + + dt_free(dtp, bp->dbu_buf); +} + +void +dt_buf_reset(dtrace_hdl_t *dtp, dt_buf_t *bp) +{ + if ((bp->dbu_ptr = bp->dbu_buf) != NULL) + bp->dbu_err = 0; + else + dt_buf_create(dtp, bp, bp->dbu_name, bp->dbu_len); +} + +void +dt_buf_write(dtrace_hdl_t *dtp, dt_buf_t *bp, + const void *buf, size_t len, size_t align) +{ + size_t off = (size_t)(bp->dbu_ptr - bp->dbu_buf); + size_t adj = roundup(off, align) - off; + + if (bp->dbu_err != 0) { + (void) dt_set_errno(dtp, bp->dbu_err); + return; /* write silently fails */ + } + + if (bp->dbu_ptr + adj + len > bp->dbu_buf + bp->dbu_len) { + size_t new_len = bp->dbu_len * 2; + uchar_t *new_buf; + uint_t r = 1; + + while (bp->dbu_ptr + adj + len > bp->dbu_buf + new_len) { + new_len *= 2; + r++; + } + + if ((new_buf = dt_zalloc(dtp, new_len)) == NULL) { + bp->dbu_err = dtrace_errno(dtp); + return; + } + + bcopy(bp->dbu_buf, new_buf, off); + dt_free(dtp, bp->dbu_buf); + + bp->dbu_buf = new_buf; + bp->dbu_ptr = new_buf + off; + bp->dbu_len = new_len; + bp->dbu_resizes += r; + } + + bp->dbu_ptr += adj; + bcopy(buf, bp->dbu_ptr, len); + bp->dbu_ptr += len; +} + +void +dt_buf_concat(dtrace_hdl_t *dtp, dt_buf_t *dst, + const dt_buf_t *src, size_t align) +{ + if (dst->dbu_err == 0 && src->dbu_err != 0) { + (void) dt_set_errno(dtp, src->dbu_err); + dst->dbu_err = src->dbu_err; + } else { + dt_buf_write(dtp, dst, src->dbu_buf, + (size_t)(src->dbu_ptr - src->dbu_buf), align); + } +} + +size_t +dt_buf_offset(const dt_buf_t *bp, size_t align) +{ + size_t off = (size_t)(bp->dbu_ptr - bp->dbu_buf); + return (roundup(off, align)); +} + +size_t +dt_buf_len(const dt_buf_t *bp) +{ + return (bp->dbu_ptr - bp->dbu_buf); +} + +int +dt_buf_error(const dt_buf_t *bp) +{ + return (bp->dbu_err); +} + +void * +dt_buf_ptr(const dt_buf_t *bp) +{ + return (bp->dbu_buf); +} + +void * +dt_buf_claim(dtrace_hdl_t *dtp, dt_buf_t *bp) +{ + void *buf = bp->dbu_buf; + + if (bp->dbu_err != 0) { + dt_free(dtp, buf); + buf = NULL; + } + + bp->dbu_buf = bp->dbu_ptr = NULL; + bp->dbu_len = 0; + + return (buf); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.h new file mode 100644 index 00000000..a8b465f8 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_buf.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef _DT_BUF_H +#define _DT_BUF_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct dt_buf { + const char *dbu_name; /* string name for debugging */ + uchar_t *dbu_buf; /* buffer base address */ + uchar_t *dbu_ptr; /* current buffer location */ + size_t dbu_len; /* buffer size in bytes */ + int dbu_err; /* errno value if error */ + int dbu_resizes; /* number of resizes */ +} dt_buf_t; + +extern void dt_buf_create(dtrace_hdl_t *, dt_buf_t *, const char *, size_t); +extern void dt_buf_destroy(dtrace_hdl_t *, dt_buf_t *); +extern void dt_buf_reset(dtrace_hdl_t *, dt_buf_t *); + +extern void dt_buf_write(dtrace_hdl_t *, dt_buf_t *, + const void *, size_t, size_t); + +extern void dt_buf_concat(dtrace_hdl_t *, dt_buf_t *, + const dt_buf_t *, size_t); + +extern size_t dt_buf_offset(const dt_buf_t *, size_t); +extern size_t dt_buf_len(const dt_buf_t *); + +extern int dt_buf_error(const dt_buf_t *); +extern void *dt_buf_ptr(const dt_buf_t *); + +extern void *dt_buf_claim(dtrace_hdl_t *, dt_buf_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_BUF_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_cc.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_cc.c new file mode 100644 index 00000000..5315d88d --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_cc.c @@ -0,0 +1,2321 @@ +/* + * 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. + */ + +/* + * 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. + */ + +#ifndef VBOX +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# include +# include +# ifdef _MSC_VER +# include +# else +# include +# endif +# if defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) +# ifdef _MSC_VER +# define ftruncate64 _chsize +# else +# define ftruncate64 ftruncate +# endif +# define lseek64 lseek +# endif +#endif /* VBOX */ +#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) +{ + RT_NOREF2(dhp, 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) +{ + RT_NOREF2(dhp, ignored); + yylineno = idp->di_lineno; + xyerror(D_PRAGMA_UNUSED, "unused #pragma %s\n", (char *)idp->di_iarg); +#ifndef _MSC_VER /* unreachable */ + return (0); +#endif +} + +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) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + if (dt_node_is_void(dnp->dn_args)) { + dnerror(dnp->dn_args, D_TRACE_VOID, + "trace( ) may not be applied to a void expression\n"); + } + + if (dt_node_is_dynamic(dnp->dn_args)) { + dnerror(dnp->dn_args, D_TRACE_DYN, + "trace( ) may not be applied to a dynamic expression\n"); + } + + dt_cg(yypcb, dnp->dn_args); + 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 *size = dnp->dn_args->dn_list; + + 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(size) == 0) { + dnerror(size, D_TRACEMEM_SIZE, "tracemem( ) argument #2 must " + "be a non-zero positive integral constant expression\n"); + } + + dt_cg(yypcb, addr); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_DIFEXPR; + + ap->dtad_difo->dtdo_rtype.dtdt_flags |= DIF_TF_BYREF; + ap->dtad_difo->dtdo_rtype.dtdt_size = size->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) +{ + RT_NOREF1(dtp); + 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); + RT_NOREF1(dnp); + + /* + * 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); + RT_NOREF1(dnp); + + 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); + RT_NOREF1(dnp); + + 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); + RT_NOREF1(dnp); + + 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_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_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_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 VBDTMSC(0); + 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); + VBDTTYPE(unsigned,int) onlevels = DTRACE_LQUANTIZE_LEVELS(oarg); + VBDTTYPE(unsigned,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_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; +#ifndef VBOX + dt_provider_t *pvp; +#endif + dt_ident_t *idp; + char attrstr[8]; + int err; + +#ifndef VBOX + /* + * 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); + } +#endif + + /* + * 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) +{ +#ifndef VBOX + int argc = dtp->dt_cpp_argc; + char **argv = malloc(sizeof (char *) * (argc + 5)); + FILE *ofp = tmpfile(); + + char ipath[20], opath[20]; /* big enough for /dev/fd/ + INT_MAX + \0 */ + 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; + off64_t off; + 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); + } + + (void) snprintf(ipath, sizeof (ipath), "/dev/fd/%d", fileno(ifp)); + (void) snprintf(opath, sizeof (opath), "/dev/fd/%d", fileno(ofp)); + + 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; + + 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; + 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) { + (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); + +#else /* VBOX */ + RT_NOREF1(ifp); + (void) dt_set_errno(dtp, EDT_CPPERR); + return (NULL); +#endif /* VBOX */ +} + +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 of the .d library files found in the specified directory and + * 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_dir(dtrace_hdl_t *dtp, const char *path) +{ +#ifndef VBOX + struct dirent *dp; + const char *p; + DIR *dirp; +#else + RTDIR hDir; + RTDIRENTRY DirEntry; + const char *p; + int rc; +#endif + + char fname[PATH_MAX]; + dtrace_prog_t *pgp; + FILE *fp; + void *rv; + dt_lib_depend_t *dld; + +#ifndef VBOX + if ((dirp = opendir(path)) == NULL) { + dt_dprintf("skipping lib dir %s: %s\n", path, strerror(errno)); + return (0); + } +#else + rc = RTDirOpen(&hDir, path); + if (RT_FAILURE(rc)) { + char szTmp[160]; + RTErrQueryMsgShort(rc, szTmp, sizeof(szTmp), false /*fFaileIfUnknown*/); + dt_dprintf("skipping lib dir %s: %s\n", path, szTmp); + return (0); + } +#endif + + /* First, parse each file for library dependencies. */ +#ifndef VBOX + while ((dp = readdir(dirp)) != NULL) { +#else + while (RT_SUCCESS(RTDirRead(hDir, &DirEntry, 0))) { + struct FakeDirEntry { + const char *d_name; + } FakeDirEntry, *dp = &FakeDirEntry; + FakeDirEntry.d_name = DirEntry.szName; +#endif + 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; + } + + dtp->dt_filetag = fname; + if (dt_lib_depend_add(dtp, &dtp->dt_lib_dep, fname) != 0) + goto err; + + 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))) + goto err; + + 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; + } + +#ifndef VBOX + (void) closedir(dirp); +#else + RTDirClose(hDir); +#endif + /* + * 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; + + for (dirp = 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 */ + } + } + + 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; + +#ifdef USING_FLEX /* In case flex starts work too early. Moved from dt_pcb_push. */ + yyinit(&pcb); +#endif + if (context != DT_CTX_DPROG) + yybegin(YYS_EXPR); + else if (cflags & DTRACE_C_CTL) + yybegin(YYS_CONTROL); + else + yybegin(YYS_CLAUSE); + + rv = NULL; + 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 && DT_TREEDUMP_PASS(dtp, 3)) + dt_node_printr(yypcb->pcb_root, stderr, 0); + + if (dtp->dt_cdefs_fd != -1 && (ftruncate64(dtp->dt_cdefs_fd, 0) == -1 || + lseek64(dtp->dt_cdefs_fd, 0, SEEK_SET) == -1 || + ctf_write(dtp->dt_cdefs->dm_ctfp, dtp->dt_cdefs_fd) == CTF_ERR)) + dt_dprintf("failed to update CTF cache: %s\n", strerror(errno)); + + if (dtp->dt_ddefs_fd != -1 && (ftruncate64(dtp->dt_ddefs_fd, 0) == -1 || + lseek64(dtp->dt_ddefs_fd, 0, SEEK_SET) == -1 || + ctf_write(dtp->dt_ddefs->dm_ctfp, dtp->dt_ddefs_fd) == CTF_ERR)) + dt_dprintf("failed to update CTF cache: %s\n", strerror(errno)); + + if (yypcb->pcb_fileptr && (cflags & DTRACE_C_CPP)) + (void) fclose(yypcb->pcb_fileptr); /* close dt_preproc() file */ + + dt_pcb_pop(dtp, err); + (void) dt_set_errno(dtp, err); + return (err ? NULL : rv); +} + +dtrace_prog_t * +dtrace_program_strcompile(dtrace_hdl_t *dtp, const char *s, + dtrace_probespec_t spec, uint_t cflags, int argc, char *const argv[]) +{ + return (dt_compile(dtp, DT_CTX_DPROG, + spec, NULL, cflags, argc, argv, NULL, s)); +} + +dtrace_prog_t * +dtrace_program_fcompile(dtrace_hdl_t *dtp, FILE *fp, + uint_t cflags, int argc, char *const argv[]) +{ + return (dt_compile(dtp, DT_CTX_DPROG, + DTRACE_PROBESPEC_NAME, NULL, cflags, argc, argv, fp, NULL)); +} + +int +dtrace_type_strcompile(dtrace_hdl_t *dtp, const char *s, dtrace_typeinfo_t *dtt) +{ + (void) dt_compile(dtp, DT_CTX_DTYPE, + DTRACE_PROBESPEC_NONE, dtt, 0, 0, NULL, NULL, s); + return (dtp->dt_errno ? -1 : 0); +} + +int +dtrace_type_fcompile(dtrace_hdl_t *dtp, FILE *fp, dtrace_typeinfo_t *dtt) +{ + (void) dt_compile(dtp, DT_CTX_DTYPE, + DTRACE_PROBESPEC_NONE, dtt, 0, 0, NULL, fp, NULL); + return (dtp->dt_errno ? -1 : 0); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_cg.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_cg.c new file mode 100644 index 00000000..0ce6feae --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_cg.c @@ -0,0 +1,1973 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +#include +#include +#include +#include +#include +#else /* VBOX */ +#endif /* VBOX */ + +#include +#include +#include +#include + +static void dt_cg_node(dt_node_t *, dt_irlist_t *, dt_regset_t *); + +static dt_irnode_t * +dt_cg_node_alloc(uint_t label, dif_instr_t instr) +{ + dt_irnode_t *dip = malloc(sizeof (dt_irnode_t)); + + if (dip == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + dip->di_label = label; + dip->di_instr = instr; + dip->di_extern = NULL; + dip->di_next = NULL; + + return (dip); +} + +/* + * Code generator wrapper function for ctf_member_info. If we are given a + * reference to a forward declaration tag, search the entire type space for + * the actual definition and then call ctf_member_info on the result. + */ +static ctf_file_t * +dt_cg_membinfo(ctf_file_t *fp, ctf_id_t type, const char *s, ctf_membinfo_t *mp) +{ + while (ctf_type_kind(fp, type) == CTF_K_FORWARD) { + char n[DT_TYPE_NAMELEN]; + dtrace_typeinfo_t dtt; + + if (ctf_type_name(fp, type, n, sizeof (n)) == NULL || + dt_type_lookup(n, &dtt) == -1 || ( + dtt.dtt_ctfp == fp && dtt.dtt_type == type)) + break; /* unable to improve our position */ + + fp = dtt.dtt_ctfp; + type = ctf_type_resolve(fp, dtt.dtt_type); + } + + if (ctf_member_info(fp, type, s, mp) == CTF_ERR) + return (NULL); /* ctf_errno is set for us */ + + return (fp); +} + +static void +dt_cg_xsetx(dt_irlist_t *dlp, dt_ident_t *idp, uint_t lbl, int reg, uint64_t x) +{ + int flag = idp != NULL ? DT_INT_PRIVATE : DT_INT_SHARED; + int intoff = dt_inttab_insert(yypcb->pcb_inttab, x, flag); + dif_instr_t instr = DIF_INSTR_SETX((uint_t)intoff, reg); + + if (intoff == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + if (intoff > DIF_INTOFF_MAX) + longjmp(yypcb->pcb_jmpbuf, EDT_INT2BIG); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl, instr)); + + if (idp != NULL) + dlp->dl_last->di_extern = idp; +} + +static void +dt_cg_setx(dt_irlist_t *dlp, int reg, uint64_t x) +{ + dt_cg_xsetx(dlp, NULL, DT_LBL_NONE, reg, x); +} + +/* + * When loading bit-fields, we want to convert a byte count in the range + * 1-8 to the closest power of 2 (e.g. 3->4, 5->8, etc). The clp2() function + * is a clever implementation from "Hacker's Delight" by Henry Warren, Jr. + */ +static size_t +clp2(size_t x) +{ + x--; + + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + return (x + 1); +} + +/* + * Lookup the correct load opcode to use for the specified node and CTF type. + * We determine the size and convert it to a 3-bit index. Our lookup table + * is constructed to use a 5-bit index, consisting of the 3-bit size 0-7, a + * bit for the sign, and a bit for userland address. For example, a 4-byte + * signed load from userland would be at the following table index: + * user=1 sign=1 size=4 => binary index 11011 = decimal index 27 + */ +static uint_t +dt_cg_load(dt_node_t *dnp, ctf_file_t *ctfp, ctf_id_t type) +{ + static const uint_t ops[] = { + DIF_OP_LDUB, DIF_OP_LDUH, 0, DIF_OP_LDUW, + 0, 0, 0, DIF_OP_LDX, + DIF_OP_LDSB, DIF_OP_LDSH, 0, DIF_OP_LDSW, + 0, 0, 0, DIF_OP_LDX, + DIF_OP_ULDUB, DIF_OP_ULDUH, 0, DIF_OP_ULDUW, + 0, 0, 0, DIF_OP_ULDX, + DIF_OP_ULDSB, DIF_OP_ULDSH, 0, DIF_OP_ULDSW, + 0, 0, 0, DIF_OP_ULDX, + }; + + ctf_encoding_t e; + ssize_t size; + + /* + * If we're loading a bit-field, the size of our load is found by + * rounding cte_bits up to a byte boundary and then finding the + * nearest power of two to this value (see clp2(), above). + */ + if ((dnp->dn_flags & DT_NF_BITFIELD) && + ctf_type_encoding(ctfp, type, &e) != CTF_ERR) + size = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY); + else + size = ctf_type_size(ctfp, type); + + if (size < 1 || size > 8 || (size & (size - 1)) != 0) { + xyerror(D_UNKNOWN, "internal error -- cg cannot load " + "size %ld when passed by value\n", (long)size); + } + + size--; /* convert size to 3-bit index */ + + if (dnp->dn_flags & DT_NF_SIGNED) + size |= 0x08; + if (dnp->dn_flags & DT_NF_USERLAND) + size |= 0x10; + + return (ops[size]); +} + +static void +dt_cg_ptrsize(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, + uint_t op, int dreg) +{ + ctf_file_t *ctfp = dnp->dn_ctfp; + ctf_arinfo_t r; + dif_instr_t instr; + ctf_id_t type; + uint_t kind; + ssize_t size; + int sreg; + + if ((sreg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + type = ctf_type_resolve(ctfp, dnp->dn_type); + kind = ctf_type_kind(ctfp, type); + assert(kind == CTF_K_POINTER || kind == CTF_K_ARRAY); + + if (kind == CTF_K_ARRAY) { + if (ctf_array_info(ctfp, type, &r) != 0) { + yypcb->pcb_hdl->dt_ctferr = ctf_errno(ctfp); + longjmp(yypcb->pcb_jmpbuf, EDT_CTF); + } + type = r.ctr_contents; + } else + type = ctf_type_reference(ctfp, type); + + if ((size = ctf_type_size(ctfp, type)) == 1) + return; /* multiply or divide by one can be omitted */ + + dt_cg_setx(dlp, sreg, size); + instr = DIF_INSTR_FMT(op, dreg, sreg, dreg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, sreg); +} + +/* + * If the result of a "." or "->" operation is a bit-field, we use this routine + * to generate an epilogue to the load instruction that extracts the value. In + * the diagrams below the "ld??" is the load instruction that is generated to + * load the containing word that is generating prior to calling this function. + * + * Epilogue for unsigned fields: Epilogue for signed fields: + * + * ldu? [r1], r1 lds? [r1], r1 + * setx USHIFT, r2 setx 64 - SSHIFT, r2 + * srl r1, r2, r1 sll r1, r2, r1 + * setx (1 << bits) - 1, r2 setx 64 - bits, r2 + * and r1, r2, r1 sra r1, r2, r1 + * + * The *SHIFT constants above changes value depending on the endian-ness of our + * target architecture. Refer to the comments below for more details. + */ +static void +dt_cg_field_get(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, + ctf_file_t *fp, const ctf_membinfo_t *mp) +{ + ctf_encoding_t e; + dif_instr_t instr; + uint64_t shift; + int r1, r2; + + if (ctf_type_encoding(fp, mp->ctm_type, &e) != 0 || e.cte_bits > 64) { + xyerror(D_UNKNOWN, "cg: bad field: off %lu type <%ld> " + "bits %u\n", mp->ctm_offset, mp->ctm_type, e.cte_bits); + } + + assert(dnp->dn_op == DT_TOK_PTR || dnp->dn_op == DT_TOK_DOT); + r1 = dnp->dn_left->dn_reg; + + if ((r2 = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + /* + * On little-endian architectures, ctm_offset counts from the right so + * ctm_offset % NBBY itself is the amount we want to shift right to + * move the value bits to the little end of the register to mask them. + * On big-endian architectures, ctm_offset counts from the left so we + * must subtract (ctm_offset % NBBY + cte_bits) from the size in bits + * we used for the load. The size of our load in turn is found by + * rounding cte_bits up to a byte boundary and then finding the + * nearest power of two to this value (see clp2(), above). These + * properties are used to compute shift as USHIFT or SSHIFT, below. + */ + if (dnp->dn_flags & DT_NF_SIGNED) { +#ifdef _BIG_ENDIAN + shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - + mp->ctm_offset % NBBY; +#else + shift = mp->ctm_offset % NBBY + e.cte_bits; +#endif + dt_cg_setx(dlp, r2, 64 - shift); + instr = DIF_INSTR_FMT(DIF_OP_SLL, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r2, 64 - e.cte_bits); + instr = DIF_INSTR_FMT(DIF_OP_SRA, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } else { +#ifdef _BIG_ENDIAN + shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - + (mp->ctm_offset % NBBY + e.cte_bits); +#else + shift = mp->ctm_offset % NBBY; +#endif + dt_cg_setx(dlp, r2, shift); + instr = DIF_INSTR_FMT(DIF_OP_SRL, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r2, (1ULL << e.cte_bits) - 1); + instr = DIF_INSTR_FMT(DIF_OP_AND, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } + + dt_regset_free(drp, r2); +} + +/* + * If the destination of a store operation is a bit-field, we use this routine + * to generate a prologue to the store instruction that loads the surrounding + * bits, clears the destination field, and ORs in the new value of the field. + * In the diagram below the "st?" is the store instruction that is generated to + * store the containing word that is generating after calling this function. + * + * ld [dst->dn_reg], r1 + * setx ~(((1 << cte_bits) - 1) << (ctm_offset % NBBY)), r2 + * and r1, r2, r1 + * + * setx (1 << cte_bits) - 1, r2 + * and src->dn_reg, r2, r2 + * setx ctm_offset % NBBY, r3 + * sll r2, r3, r2 + * + * or r1, r2, r1 + * st? r1, [dst->dn_reg] + * + * This routine allocates a new register to hold the value to be stored and + * returns it. The caller is responsible for freeing this register later. + */ +static int +dt_cg_field_set(dt_node_t *src, dt_irlist_t *dlp, + dt_regset_t *drp, dt_node_t *dst) +{ + uint64_t cmask, fmask, shift; + dif_instr_t instr; + int r1, r2, r3; + + ctf_membinfo_t m; + ctf_encoding_t e; + ctf_file_t *fp, *ofp; + ctf_id_t type; + + assert(dst->dn_op == DT_TOK_PTR || dst->dn_op == DT_TOK_DOT); + assert(dst->dn_right->dn_kind == DT_NODE_IDENT); + + fp = dst->dn_left->dn_ctfp; + type = ctf_type_resolve(fp, dst->dn_left->dn_type); + + if (dst->dn_op == DT_TOK_PTR) { + type = ctf_type_reference(fp, type); + type = ctf_type_resolve(fp, type); + } + + if ((fp = dt_cg_membinfo(ofp = fp, type, + dst->dn_right->dn_string, &m)) == NULL) { + yypcb->pcb_hdl->dt_ctferr = ctf_errno(ofp); + longjmp(yypcb->pcb_jmpbuf, EDT_CTF); + } + + if (ctf_type_encoding(fp, m.ctm_type, &e) != 0 || e.cte_bits > 64) { + xyerror(D_UNKNOWN, "cg: bad field: off %lu type <%ld> " + "bits %u\n", m.ctm_offset, m.ctm_type, e.cte_bits); + } + + if ((r1 = dt_regset_alloc(drp)) == -1 || + (r2 = dt_regset_alloc(drp)) == -1 || + (r3 = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + /* + * Compute shifts and masks. We need to compute "shift" as the amount + * we need to shift left to position our field in the containing word. + * Refer to the comments in dt_cg_field_get(), above, for more info. + * We then compute fmask as the mask that truncates the value in the + * input register to width cte_bits, and cmask as the mask used to + * pass through the containing bits and zero the field bits. + */ +#ifdef _BIG_ENDIAN + shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - + (m.ctm_offset % NBBY + e.cte_bits); +#else + shift = m.ctm_offset % NBBY; +#endif + fmask = (1ULL << e.cte_bits) - 1; + cmask = ~(fmask << shift); + + instr = DIF_INSTR_LOAD( + dt_cg_load(dst, fp, m.ctm_type), dst->dn_reg, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r2, cmask); + instr = DIF_INSTR_FMT(DIF_OP_AND, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r2, fmask); + instr = DIF_INSTR_FMT(DIF_OP_AND, src->dn_reg, r2, r2); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r3, shift); + instr = DIF_INSTR_FMT(DIF_OP_SLL, r2, r3, r2); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_FMT(DIF_OP_OR, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_regset_free(drp, r3); + dt_regset_free(drp, r2); + + return (r1); +} + +static void +dt_cg_store(dt_node_t *src, dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *dst) +{ + ctf_encoding_t e; + dif_instr_t instr VBDTMSC(0); + size_t size; + int reg; + + /* + * If we're loading a bit-field, the size of our store is found by + * rounding dst's cte_bits up to a byte boundary and then finding the + * nearest power of two to this value (see clp2(), above). + */ + if ((dst->dn_flags & DT_NF_BITFIELD) && + ctf_type_encoding(dst->dn_ctfp, dst->dn_type, &e) != CTF_ERR) + size = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY); + else + size = dt_node_type_size(src); + + if (src->dn_flags & DT_NF_REF) { + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + dt_cg_setx(dlp, reg, size); + instr = DIF_INSTR_COPYS(src->dn_reg, reg, dst->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); + } else { + if (dst->dn_flags & DT_NF_BITFIELD) + reg = dt_cg_field_set(src, dlp, drp, dst); + else + reg = src->dn_reg; + + switch (size) { + case 1: + instr = DIF_INSTR_STORE(DIF_OP_STB, reg, dst->dn_reg); + break; + case 2: + instr = DIF_INSTR_STORE(DIF_OP_STH, reg, dst->dn_reg); + break; + case 4: + instr = DIF_INSTR_STORE(DIF_OP_STW, reg, dst->dn_reg); + break; + case 8: + instr = DIF_INSTR_STORE(DIF_OP_STX, reg, dst->dn_reg); + break; + default: + xyerror(D_UNKNOWN, "internal error -- cg cannot store " + "size %lu when passed by value\n", (ulong_t)size); + } + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + if (dst->dn_flags & DT_NF_BITFIELD) + dt_regset_free(drp, reg); + } +} + +/* + * Generate code for a typecast or for argument promotion from the type of the + * actual to the type of the formal. We need to generate code for casts when + * a scalar type is being narrowed or changing signed-ness. We first shift the + * desired bits high (losing excess bits if narrowing) and then shift them down + * using logical shift (unsigned result) or arithmetic shift (signed result). + */ +static void +dt_cg_typecast(const dt_node_t *src, const dt_node_t *dst, + dt_irlist_t *dlp, dt_regset_t *drp) +{ + size_t srcsize = dt_node_type_size(src); + size_t dstsize = dt_node_type_size(dst); + + dif_instr_t instr; +#ifndef VBOX + int reg, n; +#else + int reg; + uint64_t n; +#endif + + if (dt_node_is_scalar(dst) && (dstsize < srcsize || + (src->dn_flags & DT_NF_SIGNED) ^ (dst->dn_flags & DT_NF_SIGNED))) { + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + if (dstsize < srcsize) + n = sizeof (uint64_t) * NBBY - dstsize * NBBY; + else + n = sizeof (uint64_t) * NBBY - srcsize * NBBY; + + dt_cg_setx(dlp, reg, n); + + instr = DIF_INSTR_FMT(DIF_OP_SLL, + src->dn_reg, reg, dst->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_FMT((dst->dn_flags & DT_NF_SIGNED) ? + DIF_OP_SRA : DIF_OP_SRL, dst->dn_reg, reg, dst->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); + } +} + +/* + * Generate code to push the specified argument list on to the tuple stack. + * We use this routine for handling subroutine calls and associative arrays. + * We must first generate code for all subexpressions before loading the stack + * because any subexpression could itself require the use of the tuple stack. + * This holds a number of registers equal to the number of arguments, but this + * is not a huge problem because the number of arguments can't exceed the + * number of tuple register stack elements anyway. At most one extra register + * is required (either by dt_cg_typecast() or for dtdt_size, below). This + * implies that a DIF implementation should offer a number of general purpose + * registers at least one greater than the number of tuple registers. + */ +static void +dt_cg_arglist(dt_ident_t *idp, dt_node_t *args, + dt_irlist_t *dlp, dt_regset_t *drp) +{ + const dt_idsig_t *isp = idp->di_data; + dt_node_t *dnp; + VBDTTYPE(uint_t,int) i = 0; + + for (dnp = args; dnp != NULL; dnp = dnp->dn_list) + dt_cg_node(dnp, dlp, drp); + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, DIF_INSTR_FLUSHTS)); + + for (dnp = args; dnp != NULL; dnp = dnp->dn_list, i++) { + dtrace_diftype_t t; + dif_instr_t instr; + uint_t op; + int reg; + + dt_node_diftype(yypcb->pcb_hdl, dnp, &t); + + isp->dis_args[i].dn_reg = dnp->dn_reg; /* re-use register */ + dt_cg_typecast(dnp, &isp->dis_args[i], dlp, drp); + isp->dis_args[i].dn_reg = -1; + + if (t.dtdt_flags & DIF_TF_BYREF) + op = DIF_OP_PUSHTR; + else + op = DIF_OP_PUSHTV; + + if (t.dtdt_size != 0) { + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + dt_cg_setx(dlp, reg, t.dtdt_size); + } else + reg = DIF_REG_R0; + + instr = DIF_INSTR_PUSHTS(op, t.dtdt_kind, reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_reg); + + if (reg != DIF_REG_R0) + dt_regset_free(drp, reg); + } + + if (i > yypcb->pcb_hdl->dt_conf.dtc_diftupregs) + longjmp(yypcb->pcb_jmpbuf, EDT_NOTUPREG); +} + +static void +dt_cg_arithmetic_op(dt_node_t *dnp, dt_irlist_t *dlp, + dt_regset_t *drp, uint_t op) +{ + int is_ptr_op = (dnp->dn_op == DT_TOK_ADD || dnp->dn_op == DT_TOK_SUB || + dnp->dn_op == DT_TOK_ADD_EQ || dnp->dn_op == DT_TOK_SUB_EQ); + + int lp_is_ptr = dt_node_is_pointer(dnp->dn_left); + int rp_is_ptr = dt_node_is_pointer(dnp->dn_right); + + dif_instr_t instr; + + if (lp_is_ptr && rp_is_ptr) { + assert(dnp->dn_op == DT_TOK_SUB); + is_ptr_op = 0; + } + + dt_cg_node(dnp->dn_left, dlp, drp); + if (is_ptr_op && rp_is_ptr) + dt_cg_ptrsize(dnp, dlp, drp, DIF_OP_MUL, dnp->dn_left->dn_reg); + + dt_cg_node(dnp->dn_right, dlp, drp); + if (is_ptr_op && lp_is_ptr) + dt_cg_ptrsize(dnp, dlp, drp, DIF_OP_MUL, dnp->dn_right->dn_reg); + + instr = DIF_INSTR_FMT(op, dnp->dn_left->dn_reg, + dnp->dn_right->dn_reg, dnp->dn_left->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_right->dn_reg); + dnp->dn_reg = dnp->dn_left->dn_reg; + + if (lp_is_ptr && rp_is_ptr) + dt_cg_ptrsize(dnp->dn_right, + dlp, drp, DIF_OP_UDIV, dnp->dn_reg); +} + +static uint_t +dt_cg_stvar(const dt_ident_t *idp) +{ + static const uint_t aops[] = { DIF_OP_STGAA, DIF_OP_STTAA, DIF_OP_NOP }; + static const uint_t sops[] = { DIF_OP_STGS, DIF_OP_STTS, DIF_OP_STLS }; + + uint_t i = (((idp->di_flags & DT_IDFLG_LOCAL) != 0) << 1) | + ((idp->di_flags & DT_IDFLG_TLS) != 0); + + return (idp->di_kind == DT_IDENT_ARRAY ? aops[i] : sops[i]); +} + +static void +dt_cg_prearith_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, uint_t op) +{ + ctf_file_t *ctfp = dnp->dn_ctfp; + dif_instr_t instr; + ctf_id_t type; + ssize_t size = 1; + int reg; + + if (dt_node_is_pointer(dnp)) { + type = ctf_type_resolve(ctfp, dnp->dn_type); + assert(ctf_type_kind(ctfp, type) == CTF_K_POINTER); + size = ctf_type_size(ctfp, ctf_type_reference(ctfp, type)); + } + + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_setx(dlp, reg, size); + + instr = DIF_INSTR_FMT(op, dnp->dn_reg, reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); + + /* + * If we are modifying a variable, generate an stv instruction from + * the variable specified by the identifier. If we are storing to a + * memory address, generate code again for the left-hand side using + * DT_NF_REF to get the address, and then generate a store to it. + * In both paths, we store the value in dnp->dn_reg (the new value). + */ + if (dnp->dn_child->dn_kind == DT_NODE_VAR) { + dt_ident_t *idp = dt_ident_resolve(dnp->dn_child->dn_ident); + + idp->di_flags |= DT_IDFLG_DIFW; + instr = DIF_INSTR_STV(dt_cg_stvar(idp), + idp->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } else { + uint_t rbit = dnp->dn_child->dn_flags & DT_NF_REF; + + assert(dnp->dn_child->dn_flags & DT_NF_WRITABLE); + assert(dnp->dn_child->dn_flags & DT_NF_LVALUE); + + dnp->dn_child->dn_flags |= DT_NF_REF; /* force pass-by-ref */ + dt_cg_node(dnp->dn_child, dlp, drp); + + dt_cg_store(dnp, dlp, drp, dnp->dn_child); + dt_regset_free(drp, dnp->dn_child->dn_reg); + + dnp->dn_left->dn_flags &= ~DT_NF_REF; + dnp->dn_left->dn_flags |= rbit; + } +} + +static void +dt_cg_postarith_op(dt_node_t *dnp, dt_irlist_t *dlp, + dt_regset_t *drp, uint_t op) +{ + ctf_file_t *ctfp = dnp->dn_ctfp; + dif_instr_t instr; + ctf_id_t type; + ssize_t size = 1; + int nreg; + + if (dt_node_is_pointer(dnp)) { + type = ctf_type_resolve(ctfp, dnp->dn_type); + assert(ctf_type_kind(ctfp, type) == CTF_K_POINTER); + size = ctf_type_size(ctfp, ctf_type_reference(ctfp, type)); + } + + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + if ((nreg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_setx(dlp, nreg, size); + instr = DIF_INSTR_FMT(op, dnp->dn_reg, nreg, nreg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + /* + * If we are modifying a variable, generate an stv instruction from + * the variable specified by the identifier. If we are storing to a + * memory address, generate code again for the left-hand side using + * DT_NF_REF to get the address, and then generate a store to it. + * In both paths, we store the value from 'nreg' (the new value). + */ + if (dnp->dn_child->dn_kind == DT_NODE_VAR) { + dt_ident_t *idp = dt_ident_resolve(dnp->dn_child->dn_ident); + + idp->di_flags |= DT_IDFLG_DIFW; + instr = DIF_INSTR_STV(dt_cg_stvar(idp), idp->di_id, nreg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } else { + uint_t rbit = dnp->dn_child->dn_flags & DT_NF_REF; + int oreg = dnp->dn_reg; + + assert(dnp->dn_child->dn_flags & DT_NF_WRITABLE); + assert(dnp->dn_child->dn_flags & DT_NF_LVALUE); + + dnp->dn_child->dn_flags |= DT_NF_REF; /* force pass-by-ref */ + dt_cg_node(dnp->dn_child, dlp, drp); + + dnp->dn_reg = nreg; + dt_cg_store(dnp, dlp, drp, dnp->dn_child); + dnp->dn_reg = oreg; + + dt_regset_free(drp, dnp->dn_child->dn_reg); + dnp->dn_left->dn_flags &= ~DT_NF_REF; + dnp->dn_left->dn_flags |= rbit; + } + + dt_regset_free(drp, nreg); +} + +/* + * Determine if we should perform signed or unsigned comparison for an OP2. + * If both operands are of arithmetic type, perform the usual arithmetic + * conversions to determine the common real type for comparison [ISOC 6.5.8.3]. + */ +static int +dt_cg_compare_signed(dt_node_t *dnp) +{ + dt_node_t dn; + + if (dt_node_is_string(dnp->dn_left) || + dt_node_is_string(dnp->dn_right)) + return (1); /* strings always compare signed */ + else if (!dt_node_is_arith(dnp->dn_left) || + !dt_node_is_arith(dnp->dn_right)) + return (0); /* non-arithmetic types always compare unsigned */ + + bzero(&dn, sizeof (dn)); + dt_node_promote(dnp->dn_left, dnp->dn_right, &dn); + return (dn.dn_flags & DT_NF_SIGNED); +} + +static void +dt_cg_compare_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, uint_t op) +{ + uint_t lbl_true = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + uint_t opc; + + dt_cg_node(dnp->dn_left, dlp, drp); + dt_cg_node(dnp->dn_right, dlp, drp); + + if (dt_node_is_string(dnp->dn_left) || dt_node_is_string(dnp->dn_right)) + opc = DIF_OP_SCMP; + else + opc = DIF_OP_CMP; + + instr = DIF_INSTR_CMP(opc, dnp->dn_left->dn_reg, dnp->dn_right->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_right->dn_reg); + dnp->dn_reg = dnp->dn_left->dn_reg; + + instr = DIF_INSTR_BRANCH(op, lbl_true); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_MOV(DIF_REG_R0, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_xsetx(dlp, NULL, lbl_true, dnp->dn_reg, 1); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +/* + * Code generation for the ternary op requires some trickery with the assembler + * in order to conserve registers. We generate code for dn_expr and dn_left + * and free their registers so they do not have be consumed across codegen for + * dn_right. We insert a dummy MOV at the end of dn_left into the destination + * register, which is not yet known because we haven't done dn_right yet, and + * save the pointer to this instruction node. We then generate code for + * dn_right and use its register as our output. Finally, we reach back and + * patch the instruction for dn_left to move its output into this register. + */ +static void +dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_false = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + dt_irnode_t *dip; + + dt_cg_node(dnp->dn_expr, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_expr->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_expr->dn_reg); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_false); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_node(dnp->dn_left, dlp, drp); + instr = DIF_INSTR_MOV(dnp->dn_left->dn_reg, DIF_REG_R0); + dip = dt_cg_node_alloc(DT_LBL_NONE, instr); /* save dip for below */ + dt_irlist_append(dlp, dip); + dt_regset_free(drp, dnp->dn_left->dn_reg); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_false, DIF_INSTR_NOP)); + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + + /* + * Now that dn_reg is assigned, reach back and patch the correct MOV + * instruction into the tail of dn_left. We know dn_reg was unused + * at that point because otherwise dn_right couldn't have allocated it. + */ + dip->di_instr = DIF_INSTR_MOV(dnp->dn_left->dn_reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +static void +dt_cg_logical_and(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_false = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + + dt_cg_node(dnp->dn_left, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_left->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_left->dn_reg); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_false); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_node(dnp->dn_right, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_right->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dnp->dn_reg = dnp->dn_right->dn_reg; + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_false); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, dnp->dn_reg, 1); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_MOV(DIF_REG_R0, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_false, instr)); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +static void +dt_cg_logical_xor(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_next = dt_irlist_label(dlp); + uint_t lbl_tail = dt_irlist_label(dlp); + + dif_instr_t instr; + + dt_cg_node(dnp->dn_left, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_left->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_next); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_cg_setx(dlp, dnp->dn_left->dn_reg, 1); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_next, DIF_INSTR_NOP)); + dt_cg_node(dnp->dn_right, dlp, drp); + + instr = DIF_INSTR_TST(dnp->dn_right->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_tail); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_cg_setx(dlp, dnp->dn_right->dn_reg, 1); + + instr = DIF_INSTR_FMT(DIF_OP_XOR, dnp->dn_left->dn_reg, + dnp->dn_right->dn_reg, dnp->dn_left->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_tail, instr)); + + dt_regset_free(drp, dnp->dn_right->dn_reg); + dnp->dn_reg = dnp->dn_left->dn_reg; +} + +static void +dt_cg_logical_or(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_true = dt_irlist_label(dlp); + uint_t lbl_false = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + + dt_cg_node(dnp->dn_left, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_left->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_left->dn_reg); + + instr = DIF_INSTR_BRANCH(DIF_OP_BNE, lbl_true); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_node(dnp->dn_right, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_right->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dnp->dn_reg = dnp->dn_right->dn_reg; + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_false); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_xsetx(dlp, NULL, lbl_true, dnp->dn_reg, 1); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_MOV(DIF_REG_R0, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_false, instr)); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +static void +dt_cg_logical_neg(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_zero = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + instr = DIF_INSTR_TST(dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_zero); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_MOV(DIF_REG_R0, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_xsetx(dlp, NULL, lbl_zero, dnp->dn_reg, 1); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +static void +dt_cg_asgn_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dif_instr_t instr; + dt_ident_t *idp; + + /* + * If we are performing a structure assignment of a translated type, + * we must instantiate all members and create a snapshot of the object + * in scratch space. We allocs a chunk of memory, generate code for + * each member, and then set dnp->dn_reg to the scratch object address. + */ + if ((idp = dt_node_resolve(dnp->dn_right, DT_IDENT_XLSOU)) != NULL) { + ctf_membinfo_t ctm; + dt_xlator_t *dxp = idp->di_data; + dt_node_t *mnp, dn, mn; + int r1, r2; + + /* + * Create two fake dt_node_t's representing operator "." and a + * right-hand identifier child node. These will be repeatedly + * modified according to each instantiated member so that we + * can pass them to dt_cg_store() and effect a member store. + */ + bzero(&dn, sizeof (dt_node_t)); + dn.dn_kind = DT_NODE_OP2; + dn.dn_op = DT_TOK_DOT; + dn.dn_left = dnp; + dn.dn_right = &mn; + + bzero(&mn, sizeof (dt_node_t)); + mn.dn_kind = DT_NODE_IDENT; + mn.dn_op = DT_TOK_IDENT; + + /* + * Allocate a register for our scratch data pointer. First we + * set it to the size of our data structure, and then replace + * it with the result of an allocs of the specified size. + */ + if ((r1 = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_setx(dlp, r1, + ctf_type_size(dxp->dx_dst_ctfp, dxp->dx_dst_base)); + + instr = DIF_INSTR_ALLOCS(r1, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + /* + * When dt_cg_asgn_op() is called, we have already generated + * code for dnp->dn_right, which is the translator input. We + * now associate this register with the translator's input + * identifier so it can be referenced during our member loop. + */ + dxp->dx_ident->di_flags |= DT_IDFLG_CGREG; + dxp->dx_ident->di_id = dnp->dn_right->dn_reg; + + for (mnp = dxp->dx_members; mnp != NULL; mnp = mnp->dn_list) { + /* + * Generate code for the translator member expression, + * and then cast the result to the member type. + */ + dt_cg_node(mnp->dn_membexpr, dlp, drp); + mnp->dn_reg = mnp->dn_membexpr->dn_reg; + dt_cg_typecast(mnp->dn_membexpr, mnp, dlp, drp); + + /* + * Ask CTF for the offset of the member so we can store + * to the appropriate offset. This call has already + * been done once by the parser, so it should succeed. + */ + if (ctf_member_info(dxp->dx_dst_ctfp, dxp->dx_dst_base, + mnp->dn_membname, &ctm) == CTF_ERR) { + yypcb->pcb_hdl->dt_ctferr = + ctf_errno(dxp->dx_dst_ctfp); + longjmp(yypcb->pcb_jmpbuf, EDT_CTF); + } + + /* + * If the destination member is at offset 0, store the + * result directly to r1 (the scratch buffer address). + * Otherwise allocate another temporary for the offset + * and add r1 to it before storing the result. + */ + if (ctm.ctm_offset != 0) { + if ((r2 = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + /* + * Add the member offset rounded down to the + * nearest byte. If the offset was not aligned + * on a byte boundary, this member is a bit- + * field and dt_cg_store() will handle masking. + */ + dt_cg_setx(dlp, r2, ctm.ctm_offset / NBBY); + instr = DIF_INSTR_FMT(DIF_OP_ADD, r1, r2, r2); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_node_type_propagate(mnp, &dn); + dn.dn_right->dn_string = mnp->dn_membname; + dn.dn_reg = r2; + + dt_cg_store(mnp, dlp, drp, &dn); + dt_regset_free(drp, r2); + + } else { + dt_node_type_propagate(mnp, &dn); + dn.dn_right->dn_string = mnp->dn_membname; + dn.dn_reg = r1; + + dt_cg_store(mnp, dlp, drp, &dn); + } + + dt_regset_free(drp, mnp->dn_reg); + } + + dxp->dx_ident->di_flags &= ~DT_IDFLG_CGREG; + dxp->dx_ident->di_id = 0; + + if (dnp->dn_right->dn_reg != -1) + dt_regset_free(drp, dnp->dn_right->dn_reg); + + assert(dnp->dn_reg == dnp->dn_right->dn_reg); + dnp->dn_reg = r1; + } + + /* + * If we are storing to a variable, generate an stv instruction from + * the variable specified by the identifier. If we are storing to a + * memory address, generate code again for the left-hand side using + * DT_NF_REF to get the address, and then generate a store to it. + * In both paths, we assume dnp->dn_reg already has the new value. + */ + if (dnp->dn_left->dn_kind == DT_NODE_VAR) { + idp = dt_ident_resolve(dnp->dn_left->dn_ident); + + if (idp->di_kind == DT_IDENT_ARRAY) + dt_cg_arglist(idp, dnp->dn_left->dn_args, dlp, drp); + + idp->di_flags |= DT_IDFLG_DIFW; + instr = DIF_INSTR_STV(dt_cg_stvar(idp), + idp->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } else { + uint_t rbit = dnp->dn_left->dn_flags & DT_NF_REF; + + assert(dnp->dn_left->dn_flags & DT_NF_WRITABLE); + assert(dnp->dn_left->dn_flags & DT_NF_LVALUE); + + dnp->dn_left->dn_flags |= DT_NF_REF; /* force pass-by-ref */ + + dt_cg_node(dnp->dn_left, dlp, drp); + dt_cg_store(dnp, dlp, drp, dnp->dn_left); + dt_regset_free(drp, dnp->dn_left->dn_reg); + + dnp->dn_left->dn_flags &= ~DT_NF_REF; + dnp->dn_left->dn_flags |= rbit; + } +} + +static void +dt_cg_assoc_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dif_instr_t instr; + uint_t op; + + assert(dnp->dn_kind == DT_NODE_VAR); + assert(!(dnp->dn_ident->di_flags & DT_IDFLG_LOCAL)); + assert(dnp->dn_args != NULL); + + dt_cg_arglist(dnp->dn_ident, dnp->dn_args, dlp, drp); + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + if (dnp->dn_ident->di_flags & DT_IDFLG_TLS) + op = DIF_OP_LDTAA; + else + op = DIF_OP_LDGAA; + + dnp->dn_ident->di_flags |= DT_IDFLG_DIFR; + instr = DIF_INSTR_LDV(op, dnp->dn_ident->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + /* + * If the associative array is a pass-by-reference type, then we are + * loading its value as a pointer to either load or store through it. + * The array element in question may not have been faulted in yet, in + * which case DIF_OP_LD*AA will return zero. We append an epilogue + * of instructions similar to the following: + * + * ld?aa id, %r1 ! base ld?aa instruction above + * tst %r1 ! start of epilogue + * +--- bne label + * | setx size, %r1 + * | allocs %r1, %r1 + * | st?aa id, %r1 + * | ld?aa id, %r1 + * v + * label: < rest of code > + * + * The idea is that we allocs a zero-filled chunk of scratch space and + * do a DIF_OP_ST*AA to fault in and initialize the array element, and + * then reload it to get the faulted-in address of the new variable + * storage. This isn't cheap, but pass-by-ref associative array values + * are (thus far) uncommon and the allocs cost only occurs once. If + * this path becomes important to DTrace users, we can improve things + * by adding a new DIF opcode to fault in associative array elements. + */ + if (dnp->dn_flags & DT_NF_REF) { + uint_t stvop = op == DIF_OP_LDTAA ? DIF_OP_STTAA : DIF_OP_STGAA; + uint_t label = dt_irlist_label(dlp); + + instr = DIF_INSTR_TST(dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BNE, label); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, dnp->dn_reg, dt_node_type_size(dnp)); + instr = DIF_INSTR_ALLOCS(dnp->dn_reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dnp->dn_ident->di_flags |= DT_IDFLG_DIFW; + instr = DIF_INSTR_STV(stvop, dnp->dn_ident->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_LDV(op, dnp->dn_ident->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_irlist_append(dlp, dt_cg_node_alloc(label, DIF_INSTR_NOP)); + } +} + +static void +dt_cg_array_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dt_probe_t *prp = yypcb->pcb_probe; + uintmax_t saved = dnp->dn_args->dn_value; + dt_ident_t *idp = dnp->dn_ident; + + dif_instr_t instr; + uint_t op; + size_t size; +#ifndef VBOX + int reg, n; +#else + int reg; + uint64_t n; +#endif + + assert(dnp->dn_kind == DT_NODE_VAR); + assert(!(idp->di_flags & DT_IDFLG_LOCAL)); + + assert(dnp->dn_args->dn_kind == DT_NODE_INT); + assert(dnp->dn_args->dn_list == NULL); + + /* + * If this is a reference in the args[] array, temporarily modify the + * array index according to the static argument mapping (if any), + * unless the argument reference is provided by a dynamic translator. + * If we're using a dynamic translator for args[], then just set dn_reg + * to an invalid reg and return: DIF_OP_XLARG will fetch the arg later. + */ + if (idp->di_id == DIF_VAR_ARGS) { + if ((idp->di_kind == DT_IDENT_XLPTR || + idp->di_kind == DT_IDENT_XLSOU) && + dt_xlator_dynamic(idp->di_data)) { + dnp->dn_reg = -1; + return; + } + dnp->dn_args->dn_value = prp->pr_mapping[saved]; + } + + dt_cg_node(dnp->dn_args, dlp, drp); + dnp->dn_args->dn_value = saved; + + dnp->dn_reg = dnp->dn_args->dn_reg; + + if (idp->di_flags & DT_IDFLG_TLS) + op = DIF_OP_LDTA; + else + op = DIF_OP_LDGA; + + idp->di_flags |= DT_IDFLG_DIFR; + + instr = DIF_INSTR_LDA(op, idp->di_id, + dnp->dn_args->dn_reg, dnp->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + /* + * If this is a reference to the args[] array, we need to take the + * additional step of explicitly eliminating any bits larger than the + * type size: the DIF interpreter in the kernel will always give us + * the raw (64-bit) argument value, and any bits larger than the type + * size may be junk. As a practical matter, this arises only on 64-bit + * architectures and only when the argument index is larger than the + * number of arguments passed directly to DTrace: if a 8-, 16- or + * 32-bit argument must be retrieved from the stack, it is possible + * (and it some cases, likely) that the upper bits will be garbage. + */ + if (idp->di_id != DIF_VAR_ARGS || !dt_node_is_scalar(dnp)) + return; + + if ((size = dt_node_type_size(dnp)) == sizeof (uint64_t)) + return; + + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + assert(size < sizeof (uint64_t)); + n = sizeof (uint64_t) * NBBY - size * NBBY; + + dt_cg_setx(dlp, reg, n); + + instr = DIF_INSTR_FMT(DIF_OP_SLL, dnp->dn_reg, reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_FMT((dnp->dn_flags & DT_NF_SIGNED) ? + DIF_OP_SRA : DIF_OP_SRL, dnp->dn_reg, reg, dnp->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); +} + +/* + * Generate code for an inlined variable reference. Inlines can be used to + * define either scalar or associative array substitutions. For scalars, we + * simply generate code for the parse tree saved in the identifier's din_root, + * and then cast the resulting expression to the inline's declaration type. + * For arrays, we take the input parameter subtrees from dnp->dn_args and + * temporarily store them in the din_root of each din_argv[i] identifier, + * which are themselves inlines and were set up for us by the parser. The + * result is that any reference to the inlined parameter inside the top-level + * din_root will turn into a recursive call to dt_cg_inline() for a scalar + * inline whose din_root will refer to the subtree pointed to by the argument. + */ +static void +dt_cg_inline(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dt_ident_t *idp = dnp->dn_ident; + dt_idnode_t *inp = idp->di_iarg; + + dt_idnode_t *pinp; + dt_node_t *pnp; + int i; + + assert(idp->di_flags & DT_IDFLG_INLINE); + assert(idp->di_ops == &dt_idops_inline); + + if (idp->di_kind == DT_IDENT_ARRAY) { + for (i = 0, pnp = dnp->dn_args; + pnp != NULL; pnp = pnp->dn_list, i++) { + if (inp->din_argv[i] != NULL) { + pinp = inp->din_argv[i]->di_iarg; + pinp->din_root = pnp; + } + } + } + + dt_cg_node(inp->din_root, dlp, drp); + dnp->dn_reg = inp->din_root->dn_reg; + dt_cg_typecast(inp->din_root, dnp, dlp, drp); + + if (idp->di_kind == DT_IDENT_ARRAY) { + for (i = 0; i < inp->din_argc; i++) { + pinp = inp->din_argv[i]->di_iarg; + pinp->din_root = NULL; + } + } +} + +static void +dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + ctf_file_t *ctfp = dnp->dn_ctfp; + ctf_file_t *octfp; + ctf_membinfo_t m; + ctf_id_t type; + + dif_instr_t instr; + dt_ident_t *idp; + ssize_t stroff; + uint_t op; + int reg; + + switch (dnp->dn_op) { + case DT_TOK_COMMA: + dt_cg_node(dnp->dn_left, dlp, drp); + dt_regset_free(drp, dnp->dn_left->dn_reg); + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + break; + + case DT_TOK_ASGN: + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_ADD_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_ADD); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_SUB_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_SUB); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_MUL_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_MUL); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_DIV_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SDIV : DIF_OP_UDIV); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_MOD_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SREM : DIF_OP_UREM); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_AND_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_AND); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_XOR_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_XOR); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_OR_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_OR); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_LSH_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_SLL); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_RSH_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SRA : DIF_OP_SRL); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_QUESTION: + dt_cg_ternary_op(dnp, dlp, drp); + break; + + case DT_TOK_LOR: + dt_cg_logical_or(dnp, dlp, drp); + break; + + case DT_TOK_LXOR: + dt_cg_logical_xor(dnp, dlp, drp); + break; + + case DT_TOK_LAND: + dt_cg_logical_and(dnp, dlp, drp); + break; + + case DT_TOK_BOR: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_OR); + break; + + case DT_TOK_XOR: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_XOR); + break; + + case DT_TOK_BAND: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_AND); + break; + + case DT_TOK_EQU: + dt_cg_compare_op(dnp, dlp, drp, DIF_OP_BE); + break; + + case DT_TOK_NEQ: + dt_cg_compare_op(dnp, dlp, drp, DIF_OP_BNE); + break; + + case DT_TOK_LT: + dt_cg_compare_op(dnp, dlp, drp, + dt_cg_compare_signed(dnp) ? DIF_OP_BL : DIF_OP_BLU); + break; + + case DT_TOK_LE: + dt_cg_compare_op(dnp, dlp, drp, + dt_cg_compare_signed(dnp) ? DIF_OP_BLE : DIF_OP_BLEU); + break; + + case DT_TOK_GT: + dt_cg_compare_op(dnp, dlp, drp, + dt_cg_compare_signed(dnp) ? DIF_OP_BG : DIF_OP_BGU); + break; + + case DT_TOK_GE: + dt_cg_compare_op(dnp, dlp, drp, + dt_cg_compare_signed(dnp) ? DIF_OP_BGE : DIF_OP_BGEU); + break; + + case DT_TOK_LSH: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_SLL); + break; + + case DT_TOK_RSH: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SRA : DIF_OP_SRL); + break; + + case DT_TOK_ADD: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_ADD); + break; + + case DT_TOK_SUB: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_SUB); + break; + + case DT_TOK_MUL: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_MUL); + break; + + case DT_TOK_DIV: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SDIV : DIF_OP_UDIV); + break; + + case DT_TOK_MOD: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SREM : DIF_OP_UREM); + break; + + case DT_TOK_LNEG: + dt_cg_logical_neg(dnp, dlp, drp); + break; + + case DT_TOK_BNEG: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + instr = DIF_INSTR_NOT(dnp->dn_reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + + case DT_TOK_PREINC: + dt_cg_prearith_op(dnp, dlp, drp, DIF_OP_ADD); + break; + + case DT_TOK_POSTINC: + dt_cg_postarith_op(dnp, dlp, drp, DIF_OP_ADD); + break; + + case DT_TOK_PREDEC: + dt_cg_prearith_op(dnp, dlp, drp, DIF_OP_SUB); + break; + + case DT_TOK_POSTDEC: + dt_cg_postarith_op(dnp, dlp, drp, DIF_OP_SUB); + break; + + case DT_TOK_IPOS: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + break; + + case DT_TOK_INEG: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + instr = DIF_INSTR_FMT(DIF_OP_SUB, DIF_REG_R0, + dnp->dn_reg, dnp->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + + case DT_TOK_DEREF: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + if (!(dnp->dn_flags & DT_NF_REF)) { + uint_t ubit = dnp->dn_flags & DT_NF_USERLAND; + + /* + * Save and restore DT_NF_USERLAND across dt_cg_load(): + * we need the sign bit from dnp and the user bit from + * dnp->dn_child in order to get the proper opcode. + */ + dnp->dn_flags |= + (dnp->dn_child->dn_flags & DT_NF_USERLAND); + + instr = DIF_INSTR_LOAD(dt_cg_load(dnp, ctfp, + dnp->dn_type), dnp->dn_reg, dnp->dn_reg); + + dnp->dn_flags &= ~DT_NF_USERLAND; + dnp->dn_flags |= ubit; + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + } + break; + + case DT_TOK_ADDROF: { + uint_t rbit = dnp->dn_child->dn_flags & DT_NF_REF; + + dnp->dn_child->dn_flags |= DT_NF_REF; /* force pass-by-ref */ + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + dnp->dn_child->dn_flags &= ~DT_NF_REF; + dnp->dn_child->dn_flags |= rbit; + break; + } + + case DT_TOK_SIZEOF: { + size_t size = dt_node_sizeof(dnp->dn_child); + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + assert(size != 0); + dt_cg_setx(dlp, dnp->dn_reg, size); + break; + } + + case DT_TOK_STRINGOF: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + break; + + case DT_TOK_XLATE: + /* + * An xlate operator appears in either an XLATOR, indicating a + * reference to a dynamic translator, or an OP2, indicating + * use of the xlate operator in the user's program. For the + * dynamic case, generate an xlate opcode with a reference to + * the corresponding member, pre-computed for us in dn_members. + */ + if (dnp->dn_kind == DT_NODE_XLATOR) { + dt_xlator_t *dxp = dnp->dn_xlator; + + assert(dxp->dx_ident->di_flags & DT_IDFLG_CGREG); + assert(dxp->dx_ident->di_id != 0); + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + if (dxp->dx_arg == -1) { + instr = DIF_INSTR_MOV( + dxp->dx_ident->di_id, dnp->dn_reg); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + op = DIF_OP_XLATE; + } else + op = DIF_OP_XLARG; + + instr = DIF_INSTR_XLATE(op, 0, dnp->dn_reg); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dlp->dl_last->di_extern = dnp->dn_xmember; + break; + } + + assert(dnp->dn_kind == DT_NODE_OP2); + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + break; + + case DT_TOK_LPAR: + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + dt_cg_typecast(dnp->dn_right, dnp, dlp, drp); + break; + + case DT_TOK_PTR: + case DT_TOK_DOT: + assert(dnp->dn_right->dn_kind == DT_NODE_IDENT); + dt_cg_node(dnp->dn_left, dlp, drp); + + /* + * If the left-hand side of PTR or DOT is a dynamic variable, + * we expect it to be the output of a D translator. In this + * case, we look up the parse tree corresponding to the member + * that is being accessed and run the code generator over it. + * We then cast the result as if by the assignment operator. + */ + if ((idp = dt_node_resolve( + dnp->dn_left, DT_IDENT_XLSOU)) != NULL || + (idp = dt_node_resolve( + dnp->dn_left, DT_IDENT_XLPTR)) != NULL) { + + dt_xlator_t *dxp; + dt_node_t *mnp; + + dxp = idp->di_data; + mnp = dt_xlator_member(dxp, dnp->dn_right->dn_string); + assert(mnp != NULL); + + dxp->dx_ident->di_flags |= DT_IDFLG_CGREG; + dxp->dx_ident->di_id = dnp->dn_left->dn_reg; + + dt_cg_node(mnp->dn_membexpr, dlp, drp); + dnp->dn_reg = mnp->dn_membexpr->dn_reg; + dt_cg_typecast(mnp->dn_membexpr, dnp, dlp, drp); + + dxp->dx_ident->di_flags &= ~DT_IDFLG_CGREG; + dxp->dx_ident->di_id = 0; + + if (dnp->dn_left->dn_reg != -1) + dt_regset_free(drp, dnp->dn_left->dn_reg); + break; + } + + ctfp = dnp->dn_left->dn_ctfp; + type = ctf_type_resolve(ctfp, dnp->dn_left->dn_type); + + if (dnp->dn_op == DT_TOK_PTR) { + type = ctf_type_reference(ctfp, type); + type = ctf_type_resolve(ctfp, type); + } + + if ((ctfp = dt_cg_membinfo(octfp = ctfp, type, + dnp->dn_right->dn_string, &m)) == NULL) { + yypcb->pcb_hdl->dt_ctferr = ctf_errno(octfp); + longjmp(yypcb->pcb_jmpbuf, EDT_CTF); + } + + if (m.ctm_offset != 0) { + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + /* + * If the offset is not aligned on a byte boundary, it + * is a bit-field member and we will extract the value + * bits below after we generate the appropriate load. + */ + dt_cg_setx(dlp, reg, m.ctm_offset / NBBY); + + instr = DIF_INSTR_FMT(DIF_OP_ADD, + dnp->dn_left->dn_reg, reg, dnp->dn_left->dn_reg); + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); + } + + if (!(dnp->dn_flags & DT_NF_REF)) { + uint_t ubit = dnp->dn_flags & DT_NF_USERLAND; + + /* + * Save and restore DT_NF_USERLAND across dt_cg_load(): + * we need the sign bit from dnp and the user bit from + * dnp->dn_left in order to get the proper opcode. + */ + dnp->dn_flags |= + (dnp->dn_left->dn_flags & DT_NF_USERLAND); + + instr = DIF_INSTR_LOAD(dt_cg_load(dnp, + ctfp, m.ctm_type), dnp->dn_left->dn_reg, + dnp->dn_left->dn_reg); + + dnp->dn_flags &= ~DT_NF_USERLAND; + dnp->dn_flags |= ubit; + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + + if (dnp->dn_flags & DT_NF_BITFIELD) + dt_cg_field_get(dnp, dlp, drp, ctfp, &m); + } + + dnp->dn_reg = dnp->dn_left->dn_reg; + break; + + case DT_TOK_STRING: + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + assert(dnp->dn_kind == DT_NODE_STRING); + stroff = dt_strtab_insert(yypcb->pcb_strtab, dnp->dn_string); + + if (stroff == -1L) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + if (stroff > DIF_STROFF_MAX) + longjmp(yypcb->pcb_jmpbuf, EDT_STR2BIG); + + instr = DIF_INSTR_SETS((ulong_t)stroff, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + + case DT_TOK_IDENT: + /* + * If the specified identifier is a variable on which we have + * set the code generator register flag, then this variable + * has already had code generated for it and saved in di_id. + * Allocate a new register and copy the existing value to it. + */ + if (dnp->dn_kind == DT_NODE_VAR && + (dnp->dn_ident->di_flags & DT_IDFLG_CGREG)) { + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + instr = DIF_INSTR_MOV(dnp->dn_ident->di_id, + dnp->dn_reg); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + } + + /* + * Identifiers can represent function calls, variable refs, or + * symbols. First we check for inlined variables, and handle + * them by generating code for the inline parse tree. + */ + if (dnp->dn_kind == DT_NODE_VAR && + (dnp->dn_ident->di_flags & DT_IDFLG_INLINE)) { + dt_cg_inline(dnp, dlp, drp); + break; + } + + switch (dnp->dn_kind) { + case DT_NODE_FUNC: + if ((idp = dnp->dn_ident)->di_kind != DT_IDENT_FUNC) { + dnerror(dnp, D_CG_EXPR, "%s %s( ) may not be " + "called from a D expression (D program " + "context required)\n", + dt_idkind_name(idp->di_kind), idp->di_name); + } + + dt_cg_arglist(dnp->dn_ident, dnp->dn_args, dlp, drp); + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + instr = DIF_INSTR_CALL( + dnp->dn_ident->di_id, dnp->dn_reg); + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + + break; + + case DT_NODE_VAR: + if (dnp->dn_ident->di_kind == DT_IDENT_XLSOU || + dnp->dn_ident->di_kind == DT_IDENT_XLPTR) { + /* + * This can only happen if we have translated + * args[]. See dt_idcook_args() for details. + */ + assert(dnp->dn_ident->di_id == DIF_VAR_ARGS); + dt_cg_array_op(dnp, dlp, drp); + break; + } + + if (dnp->dn_ident->di_kind == DT_IDENT_ARRAY) { + if (dnp->dn_ident->di_id > DIF_VAR_ARRAY_MAX) + dt_cg_assoc_op(dnp, dlp, drp); + else + dt_cg_array_op(dnp, dlp, drp); + break; + } + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + if (dnp->dn_ident->di_flags & DT_IDFLG_LOCAL) + op = DIF_OP_LDLS; + else if (dnp->dn_ident->di_flags & DT_IDFLG_TLS) + op = DIF_OP_LDTS; + else + op = DIF_OP_LDGS; + + dnp->dn_ident->di_flags |= DT_IDFLG_DIFR; + + instr = DIF_INSTR_LDV(op, + dnp->dn_ident->di_id, dnp->dn_reg); + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + + case DT_NODE_SYM: { + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dtrace_syminfo_t *sip = dnp->dn_ident->di_data; + GElf_Sym sym; + + if (dtrace_lookup_by_name(dtp, + sip->dts_object, sip->dts_name, &sym, NULL) == -1) { + xyerror(D_UNKNOWN, "cg failed for symbol %s`%s:" + " %s\n", sip->dts_object, sip->dts_name, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + } + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_xsetx(dlp, dnp->dn_ident, + DT_LBL_NONE, dnp->dn_reg, sym.st_value); + + if (!(dnp->dn_flags & DT_NF_REF)) { + instr = DIF_INSTR_LOAD(dt_cg_load(dnp, ctfp, + dnp->dn_type), dnp->dn_reg, dnp->dn_reg); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + } + break; + } + + default: + xyerror(D_UNKNOWN, "internal error -- node type %u is " + "not valid for an identifier\n", dnp->dn_kind); + } + break; + + case DT_TOK_INT: + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_setx(dlp, dnp->dn_reg, dnp->dn_value); + break; + + default: + xyerror(D_UNKNOWN, "internal error -- token type %u is not a " + "valid D compilation token\n", dnp->dn_op); + } +} + +void +dt_cg(dt_pcb_t *pcb, dt_node_t *dnp) +{ + dif_instr_t instr; + dt_xlator_t *dxp VBDTMSC(NULL); + + if (pcb->pcb_regs == NULL && (pcb->pcb_regs = + dt_regset_create(pcb->pcb_hdl->dt_conf.dtc_difintregs)) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + dt_regset_reset(pcb->pcb_regs); + (void) dt_regset_alloc(pcb->pcb_regs); /* allocate %r0 */ + + if (pcb->pcb_inttab != NULL) + dt_inttab_destroy(pcb->pcb_inttab); + + if ((pcb->pcb_inttab = dt_inttab_create(yypcb->pcb_hdl)) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + if (pcb->pcb_strtab != NULL) + dt_strtab_destroy(pcb->pcb_strtab); + + if ((pcb->pcb_strtab = dt_strtab_create(BUFSIZ)) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + dt_irlist_destroy(&pcb->pcb_ir); + dt_irlist_create(&pcb->pcb_ir); + + assert(pcb->pcb_dret == NULL); + pcb->pcb_dret = dnp; + + if (dt_node_is_dynamic(dnp)) { + dnerror(dnp, D_CG_DYN, "expression cannot evaluate to result " + "of dynamic type\n"); + } + + /* + * If we're generating code for a translator body, assign the input + * parameter to the first available register (i.e. caller passes %r1). + */ + if (dnp->dn_kind == DT_NODE_MEMBER) { + dxp = dnp->dn_membxlator; + dnp = dnp->dn_membexpr; + + dxp->dx_ident->di_flags |= DT_IDFLG_CGREG; + dxp->dx_ident->di_id = dt_regset_alloc(pcb->pcb_regs); + } + + dt_cg_node(dnp, &pcb->pcb_ir, pcb->pcb_regs); + instr = DIF_INSTR_RET(dnp->dn_reg); + dt_regset_free(pcb->pcb_regs, dnp->dn_reg); + dt_irlist_append(&pcb->pcb_ir, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + if (dnp->dn_kind == DT_NODE_MEMBER) { + dt_regset_free(pcb->pcb_regs, dxp->dx_ident->di_id); + dxp->dx_ident->di_id = 0; + dxp->dx_ident->di_flags &= ~DT_IDFLG_CGREG; + } +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_consume.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_consume.c new file mode 100644 index 00000000..d12e4743 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_consume.c @@ -0,0 +1,2342 @@ +/* + * 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. + */ + +#ifndef VBOX +#include +#include +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# include +# include +# ifdef _MSC_VER +# pragma warning(disable:4146) /* -uint64_t warnings */ +# include +# define ftruncate _chsize +# define fseeko fseek +# else +# include +# endif +#endif /* VBOX */ +#include + +#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); +} + +/* + * 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; + VBDTTYPE(uint64_t,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 VBDTTYPE(size_t,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) { + /* + * We're at the end -- maybe. If the oldest + * record is non-zero, we need to wrap. + */ + if (buf->dtbd_oldest != 0) { + offs = 0; + } else { + 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(void) +{ + return (DTRACE_CONSUME_THIS); +} + +static int +dt_nullrec(void) +{ + return (DTRACE_CONSUME_NEXT); +} + +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) { + 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)); + } +} + +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. + */ + 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]); + } + + if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", + "------------- Distribution -------------", "count") < 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_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]); + } + + 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); + 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*/ +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; + RT_NOREF1(size); + + 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; + RT_NOREF1(size); + + return (dt_printf(dtp, fp, " %16llu", data[0] ? + (unsigned long long) dt_stddev(data, normal) : 0)); +} + +/*ARGSUSED*/ +int +dt_print_bytes(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, + size_t nbytes, int width, int quiet) +{ + /* + * 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. + */ +#ifdef VBOX + size_t i, j; + int margin = 5; +#else + int i, j, margin = 5; +#endif + char *c = (char *)addr; + + if (nbytes == 0) + return (0); + + 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) + return (dt_printf(dtp, fp, "%s", c)); + else + return (dt_printf(dtp, fp, " %-*s", 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 /*VBOX: NULL is a ptr */) + 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, + 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, pc); + } else { + (void) snprintf(c, sizeof (c), "0x%llx", 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; + +#ifndef VBOX + char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; + struct ps_prochandle *P; + GElf_Sym sym; +#else + char c[PATH_MAX * 2]; +#endif + 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. + */ +#ifndef VBOX + 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 */ +#endif + + for (i = 0; VBDTCAST(uint_t)i < depth && pc[i] != 0/*NULL*/; i++) { +#ifndef VBOX + const prmap_t *map; +#endif + + if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) + break; + +#ifndef VBOX + 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)) != NULL) { + (void) snprintf(c, sizeof (c), "%s`0x%llx", + dt_basename(objname), (u_longlong_t)pc[i]); + } else { +#else + { { +#endif + (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 ((uintptr_t/*vbox*/)(str - strbase) >= strsize) + str = NULL; + } + } + +#ifndef VBOX + if (P != NULL) { + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); + } +#endif + + 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; + +#ifndef VBOX + 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); + } + } +#else + RT_NOREF1(act); +#endif + + 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; + +#ifndef VBOX + char objname[PATH_MAX], c[PATH_MAX * 2]; + struct ps_prochandle *P; +#else + char c[64]; +#endif + + if (format == NULL) + format = " %-50s"; + +#ifndef VBOX + /* + * 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)) != NULL) { + (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); + } +#else /* VBOX */ + RT_NOREF_PV(pid); + snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); + err = dt_printf(dtp, fp, format, c); +#endif /* VBOX */ + + return (err); +} + +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) +{ + int err; + dtrace_actkind_t act = rec->dtrd_action; + + 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_AVG: + return (dt_print_average(dtp, fp, addr, size, normal)); + + case DTRACEAGG_STDDEV: + return (dt_print_stddev(dtp, fp, addr, size, normal)); + + default: + break; + } + + switch (size) { + case sizeof (uint64_t): + err = dt_printf(dtp, fp, " %16lld", + /* LINTED - alignment */ + (long long)*((uint64_t *)addr) / normal); + break; + case sizeof (uint32_t): + /* LINTED - alignment */ + err = dt_printf(dtp, fp, " %8d", *((uint32_t *)addr) / + (uint32_t)normal); + break; + case sizeof (uint16_t): + /* LINTED - alignment */ + err = dt_printf(dtp, fp, " %5d", *((uint16_t *)addr) / + (uint32_t)normal); + break; + case sizeof (uint8_t): + err = dt_printf(dtp, fp, " %3d", *((uint8_t *)addr) / + (uint32_t)normal); + break; + default: + err = dt_print_bytes(dtp, fp, addr, size, 50, 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; + + /* + * 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) + 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) + 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 (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) +{ +#ifndef VBOX + int len, rval; +#else + size_t len; + int rval; +#endif + 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, + dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) +{ + dtrace_epid_t id; + size_t offs, start = buf->dtbd_oldest, end = buf->dtbd_size; + int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); + int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); + int rval, i, n; + dtrace_epid_t last = DTRACE_EPIDNONE; + dtrace_probedata_t data; + uint64_t drops; + caddr_t addr; + + bzero(&data, sizeof (data)); + data.dtpda_handle = dtp; + data.dtpda_cpu = cpu; + +again: + for (offs = start; offs < end; ) { + 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, last, 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++) { + 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: + { + int res; + if (fp == NULL) + continue; + + (void) fflush(fp); + res = ftruncate(fileno(fp), 0); + NOREF(res); + (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; + } + } + + 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 (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) VBDTMSC(NULL); + + 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; + } + +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; + } + + 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); + 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; + last = id; + } + + if (buf->dtbd_oldest != 0 && start == buf->dtbd_oldest) { + end = buf->dtbd_oldest; + start = 0; + goto again; + } + + 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)); +} + +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 = (dt_begin_t *)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 = (dt_begin_t *)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_bufdesc_t *buf, + 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; + dtrace_bufdesc_t nbuf; + int rval, i; + static int max_ncpus; + dtrace_optval_t size; + + dtp->dt_beganon = (processorid_t)-1; + + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { + /* + * We really don't expect this to fail, but it is at least + * technically possible for this to fail with ENOENT. In this + * case, we just drive on... + */ + if (errno == ENOENT) + return (0); + + return (dt_set_errno(dtp, errno)); + } + + 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. + */ + return (dt_consume_cpu(dtp, fp, cpu, buf, pf, rf, arg)); + } + + 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, dt_consume_begin_probe, + dt_consume_begin_record, &begin); + + dtp->dt_errhdlr = begin.dtbgn_errhdlr; + dtp->dt_errarg = begin.dtbgn_errarg; + + if (rval != 0) + return (rval); + + /* + * Now allocate a new buffer. We'll use this to deal with every other + * CPU. + */ + bzero(&nbuf, sizeof (dtrace_bufdesc_t)); + (void) dtrace_getopt(dtp, "bufsize", &size); + if ((nbuf.dtbd_data = malloc(size)) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + if (max_ncpus == 0) +#ifndef VBOX + max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; +#else + max_ncpus = RTMpGetMaxCpuId() + 1; +#endif + + for (i = 0; i < max_ncpus; i++) { + nbuf.dtbd_cpu = i; + + if ((unsigned)i == cpu) + continue; + + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &nbuf) == -1) { + /* + * 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) + continue; + + free(nbuf.dtbd_data); + + return (dt_set_errno(dtp, errno)); + } + + if ((rval = dt_consume_cpu(dtp, fp, + i, &nbuf, pf, rf, arg)) != 0) { + free(nbuf.dtbd_data); + return (rval); + } + } + + free(nbuf.dtbd_data); + + /* + * 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, dt_consume_begin_probe, + dt_consume_begin_record, &begin); + + dtp->dt_errhdlr = begin.dtbgn_errhdlr; + dtp->dt_errarg = begin.dtbgn_errarg; + + return (rval); +} + +int +dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, + dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) +{ + dtrace_bufdesc_t *buf = &dtp->dt_buf; + 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 < VBDTCAST(hrtime_t)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) +#ifndef VBOX + max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; +#else + max_ncpus = RTMpGetMaxCpuId() + 1; +#endif + + if (pf == NULL) + pf = (dtrace_consume_probe_f *)(uintptr_t)dt_nullprobe; + + if (rf == NULL) + rf = (dtrace_consume_rec_f *)(uintptr_t)dt_nullrec; + + if (buf->dtbd_data == NULL) { + (void) dtrace_getopt(dtp, "bufsize", &size); + if ((buf->dtbd_data = malloc(size)) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + buf->dtbd_size = size; + } + + /* + * 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) { + buf->dtbd_cpu = dtp->dt_beganon; + if ((rval = dt_consume_begin(dtp, fp, buf, pf, rf, arg)) != 0) + return (rval); + } + + for (i = 0; i < max_ncpus; i++) { + buf->dtbd_cpu = i; + + /* + * 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 && ((unsigned)i == dtp->dt_endedon)) + continue; + + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { + /* + * 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) + continue; + + return (dt_set_errno(dtp, errno)); + } + + if ((rval = dt_consume_cpu(dtp, fp, i, buf, pf, rf, arg)) != 0) + return (rval); + } + + if (!dtp->dt_stopped) + return (0); + + buf->dtbd_cpu = dtp->dt_endedon; + + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { + /* + * This _really_ shouldn't fail, but it is strictly speaking + * possible for this to return ENOENT if the CPU that called + * the END enabling somehow managed to become unconfigured. + * It's unclear how the user can possibly expect anything + * rational to happen in this case -- the state has been thrown + * out along with the unconfigured CPU -- so we'll just drive + * on... + */ + if (errno == ENOENT) + return (0); + + return (dt_set_errno(dtp, errno)); + } + + return (dt_consume_cpu(dtp, fp, dtp->dt_endedon, buf, pf, rf, arg)); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.c new file mode 100644 index 00000000..5d79ab8c --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.c @@ -0,0 +1,1130 @@ +/* + * 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. + */ + +#ifndef VBOX +#include +#include +#include +#include +#include +#endif + +#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; + + if (name != NULL && strchr(name, '`') != NULL) { + xyerror(D_DECL_SCOPE, "D scoping operator may not be used " + "in a type name\n"); + } + + 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 VBDTMSC({0}); + 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) +{ + RT_NOREF3(name, value, 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; + +#ifndef VBOX + name = strdupa(s); +#else + MY_STRDUPA(name, s); +#endif + 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); + + 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; + + /* + * 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 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); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.h new file mode 100644 index 00000000..af383399 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_decl.h @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#ifndef _DT_DECL_H +#define _DT_DECL_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#endif +#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 */ + +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 */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dis.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dis.c new file mode 100644 index 00000000..44523931 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dis.c @@ -0,0 +1,524 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#endif + +#include +#include + +/*ARGSUSED*/ +static void +dt_dis_log(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + RT_NOREF1(dp); + (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) +{ + RT_NOREF1(dp); + (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) +{ + RT_NOREF1(dp); + (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) +{ + RT_NOREF1(dp); + (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) +{ + RT_NOREF2(dp, in); + (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) +{ + RT_NOREF1(dp); + (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) +{ + RT_NOREF1(dp); + (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) +{ + RT_NOREF1(dp); + (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 (~0u /*VBOX: was -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) +{ + RT_NOREF1(dp); + (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); + RT_NOREF1(dp); + + (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); + RT_NOREF1(dp); + + (void) fprintf(fp, "%-4s DT_TYPE(%u), %%r%u, %%r%u", + name, type, DIF_INSTR_R2(in), DIF_INSTR_RS(in)); + + if (type < sizeof (tnames) / sizeof (tnames[0])) + (void) fprintf(fp, "\t! DT_TYPE(%u) = %s", 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); + } 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); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.c new file mode 100644 index 00000000..bca55087 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.c @@ -0,0 +1,969 @@ +/* + * 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. + */ + +#ifndef VBOX +#include +#include + +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# include +#endif /* VBOX */ + +#include +#include +#include +#include +#include +#include + +void +dt_dof_init(dtrace_hdl_t *dtp) +{ + dt_dof_t *ddo = &dtp->dt_dof; + + ddo->ddo_hdl = dtp; + ddo->ddo_nsecs = 0; + ddo->ddo_strsec = DOF_SECIDX_NONE; + ddo->ddo_xlimport = NULL; + ddo->ddo_xlexport = NULL; + + dt_buf_create(dtp, &ddo->ddo_secs, "section headers", 0); + dt_buf_create(dtp, &ddo->ddo_strs, "string table", 0); + dt_buf_create(dtp, &ddo->ddo_ldata, "loadable data", 0); + dt_buf_create(dtp, &ddo->ddo_udata, "unloadable data", 0); + + dt_buf_create(dtp, &ddo->ddo_probes, "probe data", 0); + dt_buf_create(dtp, &ddo->ddo_args, "probe args", 0); + dt_buf_create(dtp, &ddo->ddo_offs, "probe offs", 0); + dt_buf_create(dtp, &ddo->ddo_enoffs, "probe is-enabled offs", 0); + dt_buf_create(dtp, &ddo->ddo_rels, "probe rels", 0); + + dt_buf_create(dtp, &ddo->ddo_xlms, "xlate members", 0); +} + +void +dt_dof_fini(dtrace_hdl_t *dtp) +{ + dt_dof_t *ddo = &dtp->dt_dof; + + dt_free(dtp, ddo->ddo_xlimport); + dt_free(dtp, ddo->ddo_xlexport); + + dt_buf_destroy(dtp, &ddo->ddo_secs); + dt_buf_destroy(dtp, &ddo->ddo_strs); + dt_buf_destroy(dtp, &ddo->ddo_ldata); + dt_buf_destroy(dtp, &ddo->ddo_udata); + + dt_buf_destroy(dtp, &ddo->ddo_probes); + dt_buf_destroy(dtp, &ddo->ddo_args); + dt_buf_destroy(dtp, &ddo->ddo_offs); + dt_buf_destroy(dtp, &ddo->ddo_enoffs); + dt_buf_destroy(dtp, &ddo->ddo_rels); + + dt_buf_destroy(dtp, &ddo->ddo_xlms); +} + +static int +dt_dof_reset(dtrace_hdl_t *dtp, dtrace_prog_t *pgp) +{ + dt_dof_t *ddo = &dtp->dt_dof; + uint_t i, nx = dtp->dt_xlatorid; + + assert(ddo->ddo_hdl == dtp); + ddo->ddo_pgp = pgp; + + ddo->ddo_nsecs = 0; + ddo->ddo_strsec = DOF_SECIDX_NONE; + + dt_free(dtp, ddo->ddo_xlimport); + dt_free(dtp, ddo->ddo_xlexport); + + ddo->ddo_xlimport = dt_alloc(dtp, sizeof (dof_secidx_t) * nx); + ddo->ddo_xlexport = dt_alloc(dtp, sizeof (dof_secidx_t) * nx); + + if (nx != 0 && (ddo->ddo_xlimport == NULL || ddo->ddo_xlexport == NULL)) + return (-1); /* errno is set for us */ + + for (i = 0; i < nx; i++) { + ddo->ddo_xlimport[i] = DOF_SECIDX_NONE; + ddo->ddo_xlexport[i] = DOF_SECIDX_NONE; + } + + dt_buf_reset(dtp, &ddo->ddo_secs); + dt_buf_reset(dtp, &ddo->ddo_strs); + dt_buf_reset(dtp, &ddo->ddo_ldata); + dt_buf_reset(dtp, &ddo->ddo_udata); + + dt_buf_reset(dtp, &ddo->ddo_probes); + dt_buf_reset(dtp, &ddo->ddo_args); + dt_buf_reset(dtp, &ddo->ddo_offs); + dt_buf_reset(dtp, &ddo->ddo_enoffs); + dt_buf_reset(dtp, &ddo->ddo_rels); + + dt_buf_reset(dtp, &ddo->ddo_xlms); + return (0); +} + +/* + * Add a loadable DOF section to the file using the specified data buffer and + * the specified DOF section attributes. DOF_SECF_LOAD must be set in flags. + * If 'data' is NULL, the caller is responsible for manipulating the ldata buf. + */ +static dof_secidx_t +dof_add_lsect(dt_dof_t *ddo, const void *data, uint32_t type, + uint32_t align, uint32_t flags, uint32_t entsize, uint64_t size) +{ + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dof_sec_t s; + + s.dofs_type = type; + s.dofs_align = align; + s.dofs_flags = flags | DOF_SECF_LOAD; + s.dofs_entsize = entsize; + s.dofs_offset = dt_buf_offset(&ddo->ddo_ldata, align); + s.dofs_size = size; + + dt_buf_write(dtp, &ddo->ddo_secs, &s, sizeof (s), sizeof (uint64_t)); + + if (data != NULL) + dt_buf_write(dtp, &ddo->ddo_ldata, data, size, align); + + return (ddo->ddo_nsecs++); +} + +/* + * Add an unloadable DOF section to the file using the specified data buffer + * and DOF section attributes. DOF_SECF_LOAD must *not* be set in flags. + * If 'data' is NULL, the caller is responsible for manipulating the udata buf. + */ +static dof_secidx_t +dof_add_usect(dt_dof_t *ddo, const void *data, uint32_t type, + uint32_t align, uint32_t flags, uint32_t entsize, uint64_t size) +{ + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dof_sec_t s; + + s.dofs_type = type; + s.dofs_align = align; + s.dofs_flags = flags & ~DOF_SECF_LOAD; + s.dofs_entsize = entsize; + s.dofs_offset = dt_buf_offset(&ddo->ddo_udata, align); + s.dofs_size = size; + + dt_buf_write(dtp, &ddo->ddo_secs, &s, sizeof (s), sizeof (uint64_t)); + + if (data != NULL) + dt_buf_write(dtp, &ddo->ddo_udata, data, size, align); + + return (ddo->ddo_nsecs++); +} + +/* + * Add a string to the global string table associated with the DOF. The offset + * of the string is returned as an index into the string table. + */ +static dof_stridx_t +dof_add_string(dt_dof_t *ddo, const char *s) +{ + dt_buf_t *bp = &ddo->ddo_strs; + dof_stridx_t i = VBDTCAST(dof_stridx_t)dt_buf_len(bp); + + if (i != 0 && (s == NULL || *s == '\0')) + return (0); /* string table has \0 at offset 0 */ + + dt_buf_write(ddo->ddo_hdl, bp, s, strlen(s) + 1, sizeof (char)); + return (i); +} + +static dof_attr_t +dof_attr(const dtrace_attribute_t *ap) +{ + return (DOF_ATTR(ap->dtat_name, ap->dtat_data, ap->dtat_class)); +} + +static dof_secidx_t +dof_add_difo(dt_dof_t *ddo, const dtrace_difo_t *dp) +{ + dof_secidx_t dsecs[5]; /* enough for all possible DIFO sections */ + uint_t nsecs = 0; + + dof_difohdr_t *dofd; + dof_relohdr_t dofr; + dof_secidx_t relsec; + + dof_secidx_t strsec = DOF_SECIDX_NONE; + dof_secidx_t intsec = DOF_SECIDX_NONE; + dof_secidx_t hdrsec = DOF_SECIDX_NONE; + + if (dp->dtdo_buf != NULL) { + dsecs[nsecs++] = dof_add_lsect(ddo, dp->dtdo_buf, + DOF_SECT_DIF, sizeof (dif_instr_t), 0, + sizeof (dif_instr_t), sizeof (dif_instr_t) * dp->dtdo_len); + } + + if (dp->dtdo_inttab != NULL) { + dsecs[nsecs++] = intsec = dof_add_lsect(ddo, dp->dtdo_inttab, + DOF_SECT_INTTAB, sizeof (uint64_t), 0, + sizeof (uint64_t), sizeof (uint64_t) * dp->dtdo_intlen); + } + + if (dp->dtdo_strtab != NULL) { + dsecs[nsecs++] = strsec = dof_add_lsect(ddo, dp->dtdo_strtab, + DOF_SECT_STRTAB, sizeof (char), 0, 0, dp->dtdo_strlen); + } + + if (dp->dtdo_vartab != NULL) { + dsecs[nsecs++] = dof_add_lsect(ddo, dp->dtdo_vartab, + DOF_SECT_VARTAB, sizeof (uint_t), 0, sizeof (dtrace_difv_t), + sizeof (dtrace_difv_t) * dp->dtdo_varlen); + } + + if (dp->dtdo_xlmtab != NULL) { + dof_xlref_t *xlt, *xlp; + dt_node_t **pnp; + + xlt = alloca(sizeof (dof_xlref_t) * dp->dtdo_xlmlen); + pnp = dp->dtdo_xlmtab; + + /* + * dtdo_xlmtab contains pointers to the translator members. + * The translator itself is in sect ddo_xlimport[dxp->dx_id]. + * The XLMEMBERS entries are in order by their dn_membid, so + * the member section offset is the population count of bits + * in ddo_pgp->dp_xlrefs[] up to and not including dn_membid. + */ + for (xlp = xlt; xlp < xlt + dp->dtdo_xlmlen; xlp++) { + dt_node_t *dnp = *pnp++; + dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator; + + xlp->dofxr_xlator = ddo->ddo_xlimport[dxp->dx_id]; + xlp->dofxr_member = dt_popcb( + ddo->ddo_pgp->dp_xrefs[dxp->dx_id], dnp->dn_membid); + xlp->dofxr_argn = (uint32_t)dxp->dx_arg; + } + + dsecs[nsecs++] = dof_add_lsect(ddo, xlt, DOF_SECT_XLTAB, + sizeof (dof_secidx_t), 0, sizeof (dof_xlref_t), + sizeof (dof_xlref_t) * dp->dtdo_xlmlen); + } + + /* + * Copy the return type and the array of section indices that form the + * DIFO into a single dof_difohdr_t and then add DOF_SECT_DIFOHDR. + */ + assert(nsecs <= sizeof (dsecs) / sizeof (dsecs[0])); + dofd = alloca(sizeof (dtrace_diftype_t) + sizeof (dsecs)); + bcopy(&dp->dtdo_rtype, &dofd->dofd_rtype, sizeof (dtrace_diftype_t)); + bcopy(dsecs, &dofd->dofd_links, sizeof (dof_secidx_t) * nsecs); + + hdrsec = dof_add_lsect(ddo, dofd, DOF_SECT_DIFOHDR, + sizeof (dof_secidx_t), 0, 0, + sizeof (dtrace_diftype_t) + sizeof (dof_secidx_t) * nsecs); + + /* + * Add any other sections related to dtrace_difo_t. These are not + * referenced in dof_difohdr_t because they are not used by emulation. + */ + if (dp->dtdo_kreltab != NULL) { + relsec = dof_add_lsect(ddo, dp->dtdo_kreltab, DOF_SECT_RELTAB, + sizeof (uint64_t), 0, sizeof (dof_relodesc_t), + sizeof (dof_relodesc_t) * dp->dtdo_krelen); + + /* + * This code assumes the target of all relocations is the + * integer table 'intsec' (DOF_SECT_INTTAB). If other sections + * need relocation in the future this will need to change. + */ + dofr.dofr_strtab = strsec; + dofr.dofr_relsec = relsec; + dofr.dofr_tgtsec = intsec; + + (void) dof_add_lsect(ddo, &dofr, DOF_SECT_KRELHDR, + sizeof (dof_secidx_t), 0, 0, sizeof (dof_relohdr_t)); + } + + if (dp->dtdo_ureltab != NULL) { + relsec = dof_add_lsect(ddo, dp->dtdo_ureltab, DOF_SECT_RELTAB, + sizeof (uint64_t), 0, sizeof (dof_relodesc_t), + sizeof (dof_relodesc_t) * dp->dtdo_urelen); + + /* + * This code assumes the target of all relocations is the + * integer table 'intsec' (DOF_SECT_INTTAB). If other sections + * need relocation in the future this will need to change. + */ + dofr.dofr_strtab = strsec; + dofr.dofr_relsec = relsec; + dofr.dofr_tgtsec = intsec; + + (void) dof_add_lsect(ddo, &dofr, DOF_SECT_URELHDR, + sizeof (dof_secidx_t), 0, 0, sizeof (dof_relohdr_t)); + } + + return (hdrsec); +} + +static void +dof_add_translator(dt_dof_t *ddo, const dt_xlator_t *dxp, uint_t type) +{ + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dof_xlmember_t dofxm; + dof_xlator_t dofxl; + dof_secidx_t *xst; + + char buf[DT_TYPE_NAMELEN]; + dt_node_t *dnp; + uint_t i = 0; + + assert(type == DOF_SECT_XLIMPORT || type == DOF_SECT_XLEXPORT); + xst = type == DOF_SECT_XLIMPORT ? ddo->ddo_xlimport : ddo->ddo_xlexport; + + if (xst[dxp->dx_id] != DOF_SECIDX_NONE) + return; /* translator has already been emitted */ + + dt_buf_reset(dtp, &ddo->ddo_xlms); + + /* + * Generate an array of dof_xlmember_t's into ddo_xlms. If we are + * importing the translator, add only those members referenced by the + * program and set the dofxm_difo reference of each member to NONE. If + * we're exporting the translator, add all members and a DIFO for each. + */ + for (dnp = dxp->dx_members; dnp != NULL; dnp = dnp->dn_list, i++) { + if (type == DOF_SECT_XLIMPORT) { + if (!BT_TEST(ddo->ddo_pgp->dp_xrefs[dxp->dx_id], i)) + continue; /* member is not referenced */ + dofxm.dofxm_difo = DOF_SECIDX_NONE; + } else { + dofxm.dofxm_difo = dof_add_difo(ddo, + dxp->dx_membdif[dnp->dn_membid]); + } + + dofxm.dofxm_name = dof_add_string(ddo, dnp->dn_membname); + dt_node_diftype(dtp, dnp, &dofxm.dofxm_type); + + dt_buf_write(dtp, &ddo->ddo_xlms, + &dofxm, sizeof (dofxm), sizeof (uint32_t)); + } + + dofxl.dofxl_members = dof_add_lsect(ddo, NULL, DOF_SECT_XLMEMBERS, + sizeof (uint32_t), 0, sizeof (dofxm), dt_buf_len(&ddo->ddo_xlms)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_xlms, sizeof (uint32_t)); + + dofxl.dofxl_strtab = ddo->ddo_strsec; + dofxl.dofxl_argv = dof_add_string(ddo, ctf_type_name( + dxp->dx_src_ctfp, dxp->dx_src_type, buf, sizeof (buf))); + dofxl.dofxl_argc = 1; + dofxl.dofxl_type = dof_add_string(ddo, ctf_type_name( + dxp->dx_dst_ctfp, dxp->dx_dst_type, buf, sizeof (buf))); + dofxl.dofxl_attr = dof_attr(&dxp->dx_souid.di_attr); + + xst[dxp->dx_id] = dof_add_lsect(ddo, &dofxl, type, + sizeof (uint32_t), 0, 0, sizeof (dofxl)); +} + +/*ARGSUSED*/ +static int +dof_add_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data) +{ + dt_dof_t *ddo = data; + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dt_probe_t *prp = idp->di_data; + + dof_probe_t dofpr; + dof_relodesc_t dofr; + dt_probe_instance_t *pip; + dt_node_t *dnp; + + char buf[DT_TYPE_NAMELEN]; + uint_t i; + RT_NOREF1(dhp); + + dofpr.dofpr_addr = 0; + dofpr.dofpr_name = dof_add_string(ddo, prp->pr_name); + dofpr.dofpr_nargv = VBDTCAST(dof_stridx_t)dt_buf_len(&ddo->ddo_strs); + + for (dnp = prp->pr_nargs; dnp != NULL; dnp = dnp->dn_list) { + (void) dof_add_string(ddo, ctf_type_name(dnp->dn_ctfp, + dnp->dn_type, buf, sizeof (buf))); + } + + dofpr.dofpr_xargv = VBDTCAST(dof_stridx_t)dt_buf_len(&ddo->ddo_strs); + + for (dnp = prp->pr_xargs; dnp != NULL; dnp = dnp->dn_list) { + (void) dof_add_string(ddo, ctf_type_name(dnp->dn_ctfp, + dnp->dn_type, buf, sizeof (buf))); + } + + dofpr.dofpr_argidx = VBDTCAST(uint32_t)dt_buf_len(&ddo->ddo_args) / sizeof (uint8_t); + + for (i = 0; i < prp->pr_xargc; i++) { + dt_buf_write(dtp, &ddo->ddo_args, &prp->pr_mapping[i], + sizeof (uint8_t), sizeof (uint8_t)); + } + + dofpr.dofpr_nargc = prp->pr_nargc; + dofpr.dofpr_xargc = prp->pr_xargc; + dofpr.dofpr_pad1 = 0; + dofpr.dofpr_pad2 = 0; + + for (pip = prp->pr_inst; pip != NULL; pip = pip->pi_next) { + dt_dprintf("adding probe for %s:%s\n", pip->pi_fname, + prp->pr_name); + + dofpr.dofpr_func = dof_add_string(ddo, pip->pi_fname); + + /* + * There should be one probe offset or is-enabled probe offset + * or else this probe instance won't have been created. The + * kernel will reject DOF which has a probe with no offsets. + */ + assert(pip->pi_noffs + pip->pi_nenoffs > 0); + + dofpr.dofpr_offidx = + VBDTCAST(uint32_t)(dt_buf_len(&ddo->ddo_offs) / sizeof (uint32_t)); + dofpr.dofpr_noffs = pip->pi_noffs; + dt_buf_write(dtp, &ddo->ddo_offs, pip->pi_offs, + pip->pi_noffs * sizeof (uint32_t), sizeof (uint32_t)); + + dofpr.dofpr_enoffidx = + VBDTCAST(uint32_t)(dt_buf_len(&ddo->ddo_enoffs) / sizeof (uint32_t)); + dofpr.dofpr_nenoffs = pip->pi_nenoffs; + dt_buf_write(dtp, &ddo->ddo_enoffs, pip->pi_enoffs, + pip->pi_nenoffs * sizeof (uint32_t), sizeof (uint32_t)); + + /* + * If pi_rname isn't set, the relocation will be against the + * function name. If it is, the relocation will be against + * pi_rname. This will be used if the function is scoped + * locally so an alternate symbol is added for the purpose + * of this relocation. + */ + if (pip->pi_rname[0] == '\0') + dofr.dofr_name = dofpr.dofpr_func; + else + dofr.dofr_name = dof_add_string(ddo, pip->pi_rname); + dofr.dofr_type = DOF_RELO_SETX; + dofr.dofr_offset = dt_buf_len(&ddo->ddo_probes); + dofr.dofr_data = 0; + + dt_buf_write(dtp, &ddo->ddo_rels, &dofr, + sizeof (dofr), sizeof (uint64_t)); + + dt_buf_write(dtp, &ddo->ddo_probes, &dofpr, + sizeof (dofpr), sizeof (uint64_t)); + } + + return (0); +} + +static void +dof_add_provider(dt_dof_t *ddo, const dt_provider_t *pvp) +{ + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dof_provider_t dofpv; + dof_relohdr_t dofr; + dof_secidx_t *dofs; + ulong_t xr, nxr; + size_t sz; + id_t i; + + if (pvp->pv_flags & DT_PROVIDER_IMPL) + return; /* ignore providers that are exported by dtrace(7D) */ + + nxr = dt_popcb(pvp->pv_xrefs, pvp->pv_xrmax); + dofs = alloca(sizeof (dof_secidx_t) * (nxr + 1)); + xr = 1; /* reserve dofs[0] for the provider itself */ + + /* + * For each translator referenced by the provider (pv_xrefs), emit an + * exported translator section for it if one hasn't been created yet. + */ + for (i = 0; i < pvp->pv_xrmax; i++) { + if (BT_TEST(pvp->pv_xrefs, i) && + dtp->dt_xlatemode == DT_XL_DYNAMIC) { + dof_add_translator(ddo, + dt_xlator_lookup_id(dtp, i), DOF_SECT_XLEXPORT); + dofs[xr++] = ddo->ddo_xlexport[i]; + } + } + + dt_buf_reset(dtp, &ddo->ddo_probes); + dt_buf_reset(dtp, &ddo->ddo_args); + dt_buf_reset(dtp, &ddo->ddo_offs); + dt_buf_reset(dtp, &ddo->ddo_enoffs); + dt_buf_reset(dtp, &ddo->ddo_rels); + + (void) dt_idhash_iter(pvp->pv_probes, dof_add_probe, ddo); + + dofpv.dofpv_probes = dof_add_lsect(ddo, NULL, DOF_SECT_PROBES, + sizeof (uint64_t), 0, sizeof (dof_probe_t), + dt_buf_len(&ddo->ddo_probes)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, + &ddo->ddo_probes, sizeof (uint64_t)); + + dofpv.dofpv_prargs = dof_add_lsect(ddo, NULL, DOF_SECT_PRARGS, + sizeof (uint8_t), 0, sizeof (uint8_t), dt_buf_len(&ddo->ddo_args)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_args, sizeof (uint8_t)); + + dofpv.dofpv_proffs = dof_add_lsect(ddo, NULL, DOF_SECT_PROFFS, + sizeof (uint_t), 0, sizeof (uint_t), dt_buf_len(&ddo->ddo_offs)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_offs, sizeof (uint_t)); + + if ((sz = dt_buf_len(&ddo->ddo_enoffs)) != 0) { + dofpv.dofpv_prenoffs = dof_add_lsect(ddo, NULL, + DOF_SECT_PRENOFFS, sizeof (uint_t), 0, sizeof (uint_t), sz); + } else { + dofpv.dofpv_prenoffs = DOF_SECT_NONE; + } + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_enoffs, sizeof (uint_t)); + + dofpv.dofpv_strtab = ddo->ddo_strsec; + dofpv.dofpv_name = dof_add_string(ddo, pvp->pv_desc.dtvd_name); + + dofpv.dofpv_provattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_provider); + dofpv.dofpv_modattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_mod); + dofpv.dofpv_funcattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_func); + dofpv.dofpv_nameattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_name); + dofpv.dofpv_argsattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_args); + + dofs[0] = dof_add_lsect(ddo, &dofpv, DOF_SECT_PROVIDER, + sizeof (dof_secidx_t), 0, 0, sizeof (dof_provider_t)); + + dofr.dofr_strtab = dofpv.dofpv_strtab; + dofr.dofr_tgtsec = dofpv.dofpv_probes; + dofr.dofr_relsec = dof_add_lsect(ddo, NULL, DOF_SECT_RELTAB, + sizeof (uint64_t), 0, sizeof (dof_relodesc_t), + dt_buf_len(&ddo->ddo_rels)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_rels, sizeof (uint64_t)); + + (void) dof_add_lsect(ddo, &dofr, DOF_SECT_URELHDR, + sizeof (dof_secidx_t), 0, 0, sizeof (dof_relohdr_t)); + + if (nxr != 0 && dtp->dt_xlatemode == DT_XL_DYNAMIC) { + (void) dof_add_lsect(ddo, dofs, DOF_SECT_PREXPORT, + sizeof (dof_secidx_t), 0, sizeof (dof_secidx_t), + sizeof (dof_secidx_t) * (nxr + 1)); + } +} + +static int +dof_hdr(dtrace_hdl_t *dtp, uint8_t dofversion, dof_hdr_t *hp) +{ + /* + * If our config values cannot fit in a uint8_t, we can't generate a + * DOF header since the values won't fit. This can only happen if the + * user forcibly compiles a program with an artificial configuration. + */ + if (dtp->dt_conf.dtc_difversion > UINT8_MAX || + dtp->dt_conf.dtc_difintregs > UINT8_MAX || + dtp->dt_conf.dtc_diftupregs > UINT8_MAX) + return (dt_set_errno(dtp, EOVERFLOW)); + + bzero(hp, sizeof (dof_hdr_t)); + + hp->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0; + hp->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1; + hp->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2; + hp->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3; + + if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) + hp->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_LP64; + else + hp->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_ILP32; + + hp->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE; + hp->dofh_ident[DOF_ID_VERSION] = dofversion; + hp->dofh_ident[DOF_ID_DIFVERS] = dtp->dt_conf.dtc_difversion; + hp->dofh_ident[DOF_ID_DIFIREG] = dtp->dt_conf.dtc_difintregs; + hp->dofh_ident[DOF_ID_DIFTREG] = dtp->dt_conf.dtc_diftupregs; + + hp->dofh_hdrsize = sizeof (dof_hdr_t); + hp->dofh_secsize = sizeof (dof_sec_t); + hp->dofh_secoff = sizeof (dof_hdr_t); + + return (0); +} + +void * +dtrace_dof_create(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t flags) +{ + dt_dof_t *ddo = &dtp->dt_dof; + + const dtrace_ecbdesc_t *edp, *last; + const dtrace_probedesc_t *pdp; + const dtrace_actdesc_t *ap; + const dt_stmt_t *stp; + + uint_t maxacts = 0; + uint_t maxfmt = 0; + + dt_provider_t *pvp; + dt_xlator_t *dxp; + dof_actdesc_t *dofa; + dof_sec_t *sp; + size_t ssize, lsize; + dof_hdr_t h; + + dt_buf_t dof; + char *fmt; + uint_t i; + + if (flags & ~DTRACE_D_MASK) { + (void) dt_set_errno(dtp, EINVAL); + return (NULL); + } + + flags |= dtp->dt_dflags; + + if (dof_hdr(dtp, pgp->dp_dofversion, &h) != 0) + return (NULL); + + if (dt_dof_reset(dtp, pgp) != 0) + return (NULL); + + /* + * Iterate through the statement list computing the maximum number of + * actions and the maximum format string for allocating local buffers. + */ + for (last = NULL, stp = dt_list_next(&pgp->dp_stmts); + stp != NULL; stp = dt_list_next(stp), last = edp) { + + dtrace_stmtdesc_t *sdp = stp->ds_desc; + dtrace_actdesc_t *ap = sdp->dtsd_action; + + if (sdp->dtsd_fmtdata != NULL) { + i = VBDTCAST(uint_t)dtrace_printf_format(dtp, + sdp->dtsd_fmtdata, NULL, 0); + maxfmt = MAX(maxfmt, i); + } + + if ((edp = sdp->dtsd_ecbdesc) == last) + continue; /* same ecb as previous statement */ + + for (i = 0, ap = edp->dted_action; ap; ap = ap->dtad_next) + i++; + + maxacts = MAX(maxacts, i); + } + + dofa = alloca(sizeof (dof_actdesc_t) * maxacts); + fmt = alloca(maxfmt + 1); + + ddo->ddo_strsec = dof_add_lsect(ddo, NULL, DOF_SECT_STRTAB, 1, 0, 0, 0); + (void) dof_add_string(ddo, ""); + + /* + * If there are references to dynamic translators in the program, add + * an imported translator table entry for each referenced translator. + */ + if (pgp->dp_xrefslen != 0) { + for (dxp = dt_list_next(&dtp->dt_xlators); + dxp != NULL; dxp = dt_list_next(dxp)) { + if (dxp->dx_id < pgp->dp_xrefslen && + pgp->dp_xrefs[dxp->dx_id] != NULL) + dof_add_translator(ddo, dxp, DOF_SECT_XLIMPORT); + } + } + + /* + * Now iterate through the statement list, creating the DOF section + * headers and data for each one and adding them to our buffers. + */ + for (last = NULL, stp = dt_list_next(&pgp->dp_stmts); + stp != NULL; stp = dt_list_next(stp), last = edp) { + + dof_secidx_t probesec = DOF_SECIDX_NONE; + dof_secidx_t prdsec = DOF_SECIDX_NONE; + dof_secidx_t actsec = DOF_SECIDX_NONE; + + const dt_stmt_t *next = stp; + dtrace_stmtdesc_t *sdp = stp->ds_desc; + dof_stridx_t strndx = 0; + dof_probedesc_t dofp; + dof_ecbdesc_t dofe; + uint_t i; + + if ((edp = stp->ds_desc->dtsd_ecbdesc) == last) + continue; /* same ecb as previous statement */ + + pdp = &edp->dted_probe; + + /* + * Add a DOF_SECT_PROBEDESC for the ECB's probe description, + * and copy the probe description strings into the string table. + */ + dofp.dofp_strtab = ddo->ddo_strsec; + dofp.dofp_provider = dof_add_string(ddo, pdp->dtpd_provider); + dofp.dofp_mod = dof_add_string(ddo, pdp->dtpd_mod); + dofp.dofp_func = dof_add_string(ddo, pdp->dtpd_func); + dofp.dofp_name = dof_add_string(ddo, pdp->dtpd_name); + dofp.dofp_id = pdp->dtpd_id; + + probesec = dof_add_lsect(ddo, &dofp, DOF_SECT_PROBEDESC, + sizeof (dof_secidx_t), 0, + sizeof (dof_probedesc_t), sizeof (dof_probedesc_t)); + + /* + * If there is a predicate DIFO associated with the ecbdesc, + * write out the DIFO sections and save the DIFO section index. + */ + if (edp->dted_pred.dtpdd_difo != NULL) + prdsec = dof_add_difo(ddo, edp->dted_pred.dtpdd_difo); + + /* + * Now iterate through the action list generating DIFOs as + * referenced therein and adding action descriptions to 'dofa'. + */ + for (i = 0, ap = edp->dted_action; + ap != NULL; ap = ap->dtad_next, i++) { + + if (ap->dtad_difo != NULL) { + dofa[i].dofa_difo = + dof_add_difo(ddo, ap->dtad_difo); + } else + dofa[i].dofa_difo = DOF_SECIDX_NONE; + + /* + * If the first action in a statement has format data, + * add the format string to the global string table. + */ + if (sdp != NULL && ap == sdp->dtsd_action) { + if (sdp->dtsd_fmtdata != NULL) { + (void) dtrace_printf_format(dtp, + sdp->dtsd_fmtdata, fmt, maxfmt + 1); + strndx = dof_add_string(ddo, fmt); + } else + strndx = 0; /* use dtad_arg instead */ + + if ((next = dt_list_next(next)) != NULL) + sdp = next->ds_desc; + else + sdp = NULL; + } + + if (strndx != 0) { + dofa[i].dofa_arg = strndx; + dofa[i].dofa_strtab = ddo->ddo_strsec; + } else { + dofa[i].dofa_arg = ap->dtad_arg; + dofa[i].dofa_strtab = DOF_SECIDX_NONE; + } + + dofa[i].dofa_kind = ap->dtad_kind; + dofa[i].dofa_ntuple = ap->dtad_ntuple; + dofa[i].dofa_uarg = ap->dtad_uarg; + } + + if (i > 0) { + actsec = dof_add_lsect(ddo, dofa, DOF_SECT_ACTDESC, + sizeof (uint64_t), 0, sizeof (dof_actdesc_t), + sizeof (dof_actdesc_t) * i); + } + + /* + * Now finally, add the DOF_SECT_ECBDESC referencing all the + * previously created sub-sections. + */ + dofe.dofe_probes = probesec; + dofe.dofe_pred = prdsec; + dofe.dofe_actions = actsec; + dofe.dofe_pad = 0; + dofe.dofe_uarg = edp->dted_uarg; + + (void) dof_add_lsect(ddo, &dofe, DOF_SECT_ECBDESC, + sizeof (uint64_t), 0, 0, sizeof (dof_ecbdesc_t)); + } + + /* + * If any providers are user-defined, output DOF sections corresponding + * to the providers and the probes and arguments that they define. + */ + if (flags & DTRACE_D_PROBES) { + for (pvp = dt_list_next(&dtp->dt_provlist); + pvp != NULL; pvp = dt_list_next(pvp)) + dof_add_provider(ddo, pvp); + } + + /* + * If we're not stripping unloadable sections, generate compiler + * comments and any other unloadable miscellany. + */ + if (!(flags & DTRACE_D_STRIP)) { + (void) dof_add_usect(ddo, _dtrace_version, DOF_SECT_COMMENTS, + sizeof (char), 0, 0, strlen(_dtrace_version) + 1); +#ifndef VBOX + (void) dof_add_usect(ddo, &dtp->dt_uts, DOF_SECT_UTSNAME, + sizeof (char), 0, 0, sizeof (struct utsname)); +#endif + } + + /* + * Compute and fill in the appropriate values for the dof_hdr_t's + * dofh_secnum, dofh_loadsz, and dofh_filez values. + */ + h.dofh_secnum = ddo->ddo_nsecs; + ssize = sizeof (h) + dt_buf_len(&ddo->ddo_secs); + + h.dofh_loadsz = ssize + + dt_buf_len(&ddo->ddo_ldata) + + dt_buf_len(&ddo->ddo_strs); + + if (dt_buf_len(&ddo->ddo_udata) != 0) { + lsize = roundup(h.dofh_loadsz, sizeof (uint64_t)); + h.dofh_filesz = lsize + dt_buf_len(&ddo->ddo_udata); + } else { + lsize = h.dofh_loadsz; + h.dofh_filesz = lsize; + } + + /* + * Set the global DOF_SECT_STRTAB's offset to be after the header, + * section headers, and other loadable data. Since we're going to + * iterate over the buffer data directly, we must check for errors. + */ + if ((i = dt_buf_error(&ddo->ddo_secs)) != 0) { + (void) dt_set_errno(dtp, i); + return (NULL); + } + + sp = dt_buf_ptr(&ddo->ddo_secs); + assert(sp[ddo->ddo_strsec].dofs_type == DOF_SECT_STRTAB); + assert(ssize == sizeof (h) + sizeof (dof_sec_t) * ddo->ddo_nsecs); + + sp[ddo->ddo_strsec].dofs_offset = ssize + dt_buf_len(&ddo->ddo_ldata); + sp[ddo->ddo_strsec].dofs_size = dt_buf_len(&ddo->ddo_strs); + + /* + * Now relocate all the other section headers by adding the appropriate + * delta to their respective dofs_offset values. + */ + for (i = 0; i < ddo->ddo_nsecs; i++, sp++) { + if (i == ddo->ddo_strsec) + continue; /* already relocated above */ + + if (sp->dofs_flags & DOF_SECF_LOAD) + sp->dofs_offset += ssize; + else + sp->dofs_offset += lsize; + } + + /* + * Finally, assemble the complete in-memory DOF buffer by writing the + * header and then concatenating all our buffers. dt_buf_concat() will + * propagate any errors and cause dt_buf_claim() to return NULL. + */ + dt_buf_create(dtp, &dof, "dof", h.dofh_filesz); + + dt_buf_write(dtp, &dof, &h, sizeof (h), sizeof (uint64_t)); + dt_buf_concat(dtp, &dof, &ddo->ddo_secs, sizeof (uint64_t)); + dt_buf_concat(dtp, &dof, &ddo->ddo_ldata, sizeof (uint64_t)); + dt_buf_concat(dtp, &dof, &ddo->ddo_strs, sizeof (char)); + dt_buf_concat(dtp, &dof, &ddo->ddo_udata, sizeof (uint64_t)); + + return (dt_buf_claim(dtp, &dof)); +} + +void +dtrace_dof_destroy(dtrace_hdl_t *dtp, void *dof) +{ + dt_free(dtp, dof); +} + +void * +dtrace_getopt_dof(dtrace_hdl_t *dtp) +{ + dof_hdr_t *dof; + dof_sec_t *sec; + dof_optdesc_t *dofo; + int i, nopts = 0, len = sizeof (dof_hdr_t) + + roundup(sizeof (dof_sec_t), sizeof (uint64_t)); + + for (i = 0; i < DTRACEOPT_MAX; i++) { + if (dtp->dt_options[i] != DTRACEOPT_UNSET) + nopts++; + } + + len += sizeof (dof_optdesc_t) * nopts; + + if ((dof = dt_zalloc(dtp, len)) == NULL || + dof_hdr(dtp, DOF_VERSION, dof) != 0) { + dt_free(dtp, dof); + return (NULL); + } + + dof->dofh_secnum = 1; /* only DOF_SECT_OPTDESC */ + dof->dofh_loadsz = len; + dof->dofh_filesz = len; + + /* + * 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); + + dofo = (dof_optdesc_t *)((uintptr_t)sec + + roundup(sizeof (dof_sec_t), sizeof (uint64_t))); + + sec->dofs_offset = (uintptr_t)dofo - (uintptr_t)dof; + sec->dofs_size = sizeof (dof_optdesc_t) * nopts; + + for (i = 0; i < DTRACEOPT_MAX; i++) { + if (dtp->dt_options[i] == DTRACEOPT_UNSET) + continue; + + dofo->dofo_option = i; + dofo->dofo_strtab = DOF_SECIDX_NONE; + dofo->dofo_value = dtp->dt_options[i]; + dofo++; + } + + return (dof); +} + +void * +dtrace_geterr_dof(dtrace_hdl_t *dtp) +{ + if (dtp->dt_errprog != NULL) + return (dtrace_dof_create(dtp, dtp->dt_errprog, 0)); + + (void) dt_set_errno(dtp, EDT_BADERROR); + return (NULL); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.h new file mode 100644 index 00000000..a896497d --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_dof.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef _DT_DOF_H +#define _DT_DOF_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct dt_dof { + dtrace_hdl_t *ddo_hdl; /* libdtrace handle */ + dtrace_prog_t *ddo_pgp; /* current program */ + uint_t ddo_nsecs; /* number of sections */ + dof_secidx_t ddo_strsec; /* global strings section index */ + dof_secidx_t *ddo_xlimport; /* imported xlator section indices */ + dof_secidx_t *ddo_xlexport; /* exported xlator section indices */ + dt_buf_t ddo_secs; /* section headers */ + dt_buf_t ddo_strs; /* global strings */ + dt_buf_t ddo_ldata; /* loadable section data */ + dt_buf_t ddo_udata; /* unloadable section data */ + dt_buf_t ddo_probes; /* probe section data */ + dt_buf_t ddo_args; /* probe arguments section data */ + dt_buf_t ddo_offs; /* probe offsets section data */ + dt_buf_t ddo_enoffs; /* is-enabled offsets section data */ + dt_buf_t ddo_rels; /* probe relocation section data */ + dt_buf_t ddo_xlms; /* xlate members section data */ +} dt_dof_t; + +extern void dt_dof_init(dtrace_hdl_t *); +extern void dt_dof_fini(dtrace_hdl_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_DOF_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_error.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_error.c new file mode 100644 index 00000000..ffcf4fcc --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_error.c @@ -0,0 +1,219 @@ +/* + * 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. + */ + +#ifndef VBOX +#include +#endif +#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_NOREG, "Insufficient registers to generate code" }, + { 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" } +}; + +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); +} + +int +dt_set_errno(dtrace_hdl_t *dtp, int err) +{ + dtp->dt_errno = err; + return (-1); +} + +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 } + }; + RT_NOREF1(dtp); + + for (i = 0; faults[i].str != NULL; i++) { + if (faults[i].code == fault) + return (faults[i].str); + } + + return ("unknown fault"); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_errtags.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_errtags.h new file mode 100644 index 00000000..c81142d3 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_errtags.h @@ -0,0 +1,249 @@ +/* + * 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. + */ + +#ifndef _DT_ERRTAGS_H +#define _DT_ERRTAGS_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This enum definition is used to define a set of error tags associated with + * the D compiler's various error conditions. The shell script mkerrtags.sh is + * used to parse this file and create a corresponding dt_errtags.c source file. + * If you do something other than add a new error tag here, you may need to + * update the mkerrtags shell script as it is based upon simple regexps. + */ +typedef enum { + D_UNKNOWN, /* unknown D compiler error */ + D_SYNTAX, /* syntax error in input stream */ + D_EMPTY, /* empty translation unit */ + D_TYPE_ERR, /* type definition missing */ + D_TYPE_MEMBER, /* type member not found */ + D_ASRELO, /* relocation remains against symbol */ + D_CG_EXPR, /* tracing function called from expr */ + D_CG_DYN, /* expression returns dynamic result */ + D_ATTR_MIN, /* attributes less than amin setting */ + D_ID_OFLOW, /* identifier space overflow */ + D_PDESC_ZERO, /* probedesc matches zero probes */ + D_PDESC_INVAL, /* probedesc is not valid */ + D_PRED_SCALAR, /* predicate must be of scalar type */ + D_FUNC_IDENT, /* function designator is not ident */ + D_FUNC_UNDEF, /* function ident is not defined */ + D_FUNC_IDKIND, /* function ident is of wrong idkind */ + D_OFFSETOF_TYPE, /* offsetof arg is not sou type */ + D_OFFSETOF_BITFIELD, /* offsetof applied to field member */ + D_SIZEOF_TYPE, /* invalid sizeof type */ + D_SIZEOF_BITFIELD, /* sizeof applied to field member */ + D_STRINGOF_TYPE, /* invalid stringof type */ + D_OP_IDENT, /* operand must be an identifier */ + D_OP_INT, /* operand must be integral type */ + D_OP_SCALAR, /* operand must be scalar type */ + D_OP_ARITH, /* operand must be arithmetic type */ + D_OP_WRITE, /* operand must be writable variable */ + D_OP_LVAL, /* operand must be lvalue */ + D_OP_INCOMPAT, /* operand types are not compatible */ + D_OP_VFPTR, /* operand cannot be void or func ptr */ + D_OP_ARRFUN, /* operand cannot be array or func */ + D_OP_PTR, /* operand must be a pointer */ + D_OP_SOU, /* operand must be struct or union */ + D_OP_INCOMPLETE, /* operand is an incomplete type */ + D_OP_DYN, /* operand cannot be of dynamic type */ + D_OP_ACT, /* operand cannot be action */ + D_AGG_REDEF, /* aggregation cannot be redefined */ + D_AGG_FUNC, /* aggregating function required */ + D_AGG_MDIM, /* aggregation used as multi-dim arr */ + D_ARR_BADREF, /* access non-array using tuple */ + D_ARR_LOCAL, /* cannot define local assc array */ + D_DIV_ZERO, /* division by zero detected */ + D_DEREF_NONPTR, /* dereference non-pointer type */ + D_DEREF_VOID, /* dereference void pointer */ + D_DEREF_FUNC, /* dereference function pointer */ + D_ADDROF_LVAL, /* unary & applied to non-lvalue */ + D_ADDROF_VAR, /* unary & applied to variable */ + D_ADDROF_BITFIELD, /* unary & applied to field member */ + D_XLATE_REDECL, /* translator redeclared */ + D_XLATE_NOCONV, /* no conversion for member defined */ + D_XLATE_NONE, /* no translator for type combo */ + D_XLATE_SOU, /* dst must be struct or union type */ + D_XLATE_INCOMPAT, /* translator member type incompat */ + D_XLATE_MEMB, /* translator member is not valid */ + D_CAST_INVAL, /* invalid cast expression */ + D_PRAGERR, /* #pragma error message */ + D_PRAGCTL_INVAL, /* invalid control directive */ + D_PRAGMA_INVAL, /* invalid compiler pragma */ + D_PRAGMA_UNUSED, /* unused compiler pragma */ + D_PRAGMA_MALFORM, /* malformed #pragma argument list */ + D_PRAGMA_OPTSET, /* failed to set #pragma option */ + D_PRAGMA_SCOPE, /* #pragma identifier scope error */ + D_PRAGMA_DEPEND, /* #pragma dependency not satisfied */ + D_MACRO_UNDEF, /* macro parameter is not defined */ + D_MACRO_OFLOW, /* macro parameter integer overflow */ + D_MACRO_UNUSED, /* macro parameter is never used */ + D_INT_OFLOW, /* integer constant overflow */ + D_INT_DIGIT, /* integer digit is not valid */ + D_STR_NL, /* newline in string literal */ + D_CHR_NL, /* newline in character constant */ + D_CHR_NULL, /* empty character constant */ + D_CHR_OFLOW, /* character constant is too long */ + D_IDENT_BADREF, /* identifier expected type mismatch */ + D_IDENT_UNDEF, /* identifier is not known/defined */ + D_IDENT_AMBIG, /* identifier is ambiguous (var/enum) */ + D_SYM_BADREF, /* kernel/user symbol ref mismatch */ + D_SYM_NOTYPES, /* no CTF data available for sym ref */ + D_SYM_MODEL, /* module/program data model mismatch */ + D_VAR_UNDEF, /* reference to undefined variable */ + D_VAR_UNSUP, /* unsupported variable specification */ + D_PROTO_LEN, /* prototype length mismatch */ + D_PROTO_ARG, /* prototype argument mismatch */ + D_ARGS_MULTI, /* description matches unstable set */ + D_ARGS_XLATOR, /* no args[] translator defined */ + D_ARGS_NONE, /* no args[] available */ + D_ARGS_TYPE, /* invalid args[] type */ + D_ARGS_IDX, /* invalid args[] index */ + D_REGS_IDX, /* invalid regs[] index */ + D_KEY_TYPE, /* invalid agg or array key type */ + D_PRINTF_DYN_PROTO, /* dynamic size argument missing */ + D_PRINTF_DYN_TYPE, /* dynamic size type mismatch */ + D_PRINTF_AGG_CONV, /* improper use of %@ conversion */ + D_PRINTF_ARG_PROTO, /* conversion missing value argument */ + D_PRINTF_ARG_TYPE, /* conversion arg has wrong type */ + D_PRINTF_ARG_EXTRA, /* extra arguments specified */ + D_PRINTF_ARG_FMT, /* format string is not a constant */ + D_PRINTF_FMT_EMPTY, /* format string is empty */ + D_DECL_CHARATTR, /* bad attributes for char decl */ + D_DECL_VOIDATTR, /* bad attributes for void decl */ + D_DECL_SIGNINT, /* sign/unsign with non-integer decl */ + D_DECL_LONGINT, /* long with non-arithmetic decl */ + D_DECL_IDENT, /* old-style declaration or bad type */ + D_DECL_CLASS, /* more than one storage class given */ + D_DECL_BADCLASS, /* decl class not supported in D */ + D_DECL_PARMCLASS, /* invalid class for parameter type */ + D_DECL_COMBO, /* bad decl specifier combination */ + D_DECL_ARRSUB, /* const int required for array size */ + D_DECL_ARRNULL, /* array decl requires dim or tuple */ + D_DECL_ARRBIG, /* array size too big */ + D_DECL_IDRED, /* decl identifier redeclared */ + D_DECL_TYPERED, /* decl type redeclared */ + D_DECL_MNAME, /* member name missing */ + D_DECL_SCOPE, /* scoping operator used in decl */ + D_DECL_BFCONST, /* bit-field requires const size expr */ + D_DECL_BFSIZE, /* bit-field size too big for type */ + D_DECL_BFTYPE, /* bit-field type is not valid */ + D_DECL_ENCONST, /* enum tag requires const size expr */ + D_DECL_ENOFLOW, /* enumerator value overflows INT_MAX */ + D_DECL_USELESS, /* useless external declaration */ + D_DECL_LOCASSC, /* attempt to decl local assc array */ + D_DECL_VOIDOBJ, /* attempt to decl void object */ + D_DECL_DYNOBJ, /* attempt to decl dynamic object */ + D_DECL_INCOMPLETE, /* declaration uses incomplete type */ + D_DECL_PROTO_VARARGS, /* varargs not allowed in prototype */ + D_DECL_PROTO_TYPE, /* type not allowed in prototype */ + D_DECL_PROTO_VOID, /* void must be sole parameter */ + D_DECL_PROTO_NAME, /* void parameter may not have a name */ + D_DECL_PROTO_FORM, /* parameter name has no formal */ + D_COMM_COMM, /* commit() after commit() */ + D_COMM_DREC, /* commit() after data action */ + D_SPEC_SPEC, /* speculate() after speculate() */ + D_SPEC_COMM, /* speculate() after commit() */ + D_SPEC_DREC, /* speculate() after data action */ + D_AGG_COMM, /* aggregating act after commit() */ + D_AGG_SPEC, /* aggregating act after speculate() */ + D_AGG_NULL, /* aggregation stmt has null effect */ + D_AGG_SCALAR, /* aggregating function needs scalar */ + D_ACT_SPEC, /* destructive action after speculate */ + D_EXIT_SPEC, /* exit() action after speculate */ + D_DREC_COMM, /* data action after commit() */ + D_PRINTA_PROTO, /* printa() prototype mismatch */ + D_PRINTA_AGGARG, /* aggregation arg type mismatch */ + D_PRINTA_AGGBAD, /* printa() aggregation not defined */ + D_PRINTA_AGGKEY, /* printa() aggregation key mismatch */ + D_PRINTA_AGGPROTO, /* printa() aggregation mismatch */ + D_TRACE_VOID, /* trace() argument has void type */ + D_TRACE_DYN, /* trace() argument has dynamic type */ + D_TRACEMEM_ADDR, /* tracemem() address bad type */ + D_TRACEMEM_SIZE, /* tracemem() size bad type */ + D_STACK_PROTO, /* stack() prototype mismatch */ + D_STACK_SIZE, /* stack() size argument bad type */ + D_USTACK_FRAMES, /* ustack() frames arg bad type */ + D_USTACK_STRSIZE, /* ustack() strsize arg bad type */ + D_USTACK_PROTO, /* ustack() prototype mismatch */ + D_LQUANT_BASETYPE, /* lquantize() bad base type */ + D_LQUANT_BASEVAL, /* lquantize() bad base value */ + D_LQUANT_LIMTYPE, /* lquantize() bad limit type */ + D_LQUANT_LIMVAL, /* lquantize() bad limit value */ + D_LQUANT_MISMATCH, /* lquantize() limit < base */ + D_LQUANT_STEPTYPE, /* lquantize() bad step type */ + D_LQUANT_STEPVAL, /* lquantize() bad step value */ + D_LQUANT_STEPLARGE, /* lquantize() step too large */ + D_LQUANT_STEPSMALL, /* lquantize() step too small */ + D_QUANT_PROTO, /* quantize() prototype mismatch */ + D_PROC_OFF, /* byte offset exceeds function size */ + D_PROC_ALIGN, /* byte offset has invalid alignment */ + D_PROC_NAME, /* invalid process probe name */ + D_PROC_GRAB, /* failed to grab process */ + D_PROC_DYN, /* process is not dynamically linked */ + D_PROC_LIB, /* invalid process library name */ + D_PROC_FUNC, /* no such function in process */ + D_PROC_CREATEFAIL, /* pid probe creation failed */ + D_PROC_NODEV, /* fasttrap device is not installed */ + D_PROC_BADPID, /* user probe pid invalid */ + D_PROC_BADPROV, /* user probe provider invalid */ + D_PROC_USDT, /* problem initializing usdt */ + D_CLEAR_PROTO, /* clear() prototype mismatch */ + D_CLEAR_AGGARG, /* aggregation arg type mismatch */ + D_CLEAR_AGGBAD, /* clear() aggregation not defined */ + D_NORMALIZE_PROTO, /* normalize() prototype mismatch */ + D_NORMALIZE_SCALAR, /* normalize() value must be scalar */ + D_NORMALIZE_AGGARG, /* aggregation arg type mismatch */ + D_NORMALIZE_AGGBAD, /* normalize() aggregation not def. */ + D_TRUNC_PROTO, /* trunc() prototype mismatch */ + D_TRUNC_SCALAR, /* trunc() value must be scalar */ + D_TRUNC_AGGARG, /* aggregation arg type mismatch */ + D_TRUNC_AGGBAD, /* trunc() aggregation not def. */ + D_PROV_BADNAME, /* invalid provider name */ + D_PROV_INCOMPAT, /* provider/probe interface mismatch */ + D_PROV_PRDUP, /* duplicate probe declaration */ + D_PROV_PRARGLEN, /* probe argument list too long */ + D_PROV_PRXLATOR, /* probe argument translator missing */ + D_FREOPEN_INVALID, /* frename() filename is invalid */ + D_LQUANT_MATCHBASE, /* lquantize() mismatch on base */ + D_LQUANT_MATCHLIM, /* lquantize() mismatch on limit */ + D_LQUANT_MATCHSTEP /* lquantize() mismatch on step */ +} dt_errtag_t; + +extern const char *dt_errtag(dt_errtag_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_ERRTAGS_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_grammar.y b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_grammar.y new file mode 100644 index 00000000..e83bb53e --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_grammar.y @@ -0,0 +1,846 @@ +%{ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#else +# ifdef _MSC_VER +# pragma warning(disable:4255 4702) +# endif +#endif + +#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) + +#ifdef VBOX +# define YYMALLOC RTMemAlloc +# define YYFREE RTMemFree +#endif + + +%} + +%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_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_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; } + ; + +%% diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_handle.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_handle.c new file mode 100644 index 00000000..b02e5cc8 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_handle.c @@ -0,0 +1,483 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include + +static const char _dt_errprog[] = +"dtrace:::ERROR" +"{" +" trace(arg1);" +" trace(arg2);" +" trace(arg3);" +" trace(arg4);" +" trace(arg5);" +"}"; + +int +dtrace_handle_err(dtrace_hdl_t *dtp, dtrace_handle_err_f *hdlr, void *arg) +{ + dtrace_prog_t *pgp = NULL; + dt_stmt_t *stp; + dtrace_ecbdesc_t *edp; + + /* + * We don't currently support multiple error handlers. + */ + if (dtp->dt_errhdlr != NULL) + return (dt_set_errno(dtp, EALREADY)); + + /* + * If the DTRACEOPT_GRABANON is enabled, the anonymous enabling will + * already have a dtrace:::ERROR probe enabled; save 'hdlr' and 'arg' + * but do not bother compiling and enabling _dt_errprog. + */ + if (dtp->dt_options[DTRACEOPT_GRABANON] != DTRACEOPT_UNSET) + goto out; + + if ((pgp = dtrace_program_strcompile(dtp, _dt_errprog, + DTRACE_PROBESPEC_NAME, DTRACE_C_ZDEFS, 0, NULL)) == NULL) + return (dt_set_errno(dtp, dtrace_errno(dtp))); + + stp = dt_list_next(&pgp->dp_stmts); + assert(stp != NULL); + + edp = stp->ds_desc->dtsd_ecbdesc; + assert(edp != NULL); + edp->dted_uarg = DT_ECB_ERROR; + +out: + dtp->dt_errhdlr = hdlr; + dtp->dt_errarg = arg; + dtp->dt_errprog = pgp; + + return (0); +} + +int +dtrace_handle_drop(dtrace_hdl_t *dtp, dtrace_handle_drop_f *hdlr, void *arg) +{ + if (dtp->dt_drophdlr != NULL) + return (dt_set_errno(dtp, EALREADY)); + + dtp->dt_drophdlr = hdlr; + dtp->dt_droparg = arg; + + return (0); +} + +int +dtrace_handle_proc(dtrace_hdl_t *dtp, dtrace_handle_proc_f *hdlr, void *arg) +{ + if (dtp->dt_prochdlr != NULL) + return (dt_set_errno(dtp, EALREADY)); + + dtp->dt_prochdlr = hdlr; + dtp->dt_procarg = arg; + + return (0); +} + +int +dtrace_handle_buffered(dtrace_hdl_t *dtp, dtrace_handle_buffered_f *hdlr, + void *arg) +{ + if (dtp->dt_bufhdlr != NULL) + return (dt_set_errno(dtp, EALREADY)); + + if (hdlr == NULL) + return (dt_set_errno(dtp, EINVAL)); + + dtp->dt_bufhdlr = hdlr; + dtp->dt_bufarg = arg; + + return (0); +} + +int +dtrace_handle_setopt(dtrace_hdl_t *dtp, dtrace_handle_setopt_f *hdlr, + void *arg) +{ + if (hdlr == NULL) + return (dt_set_errno(dtp, EINVAL)); + + dtp->dt_setopthdlr = hdlr; + dtp->dt_setoptarg = arg; + + return (0); +} + +#define DT_REC(type, ndx) *((type *)((uintptr_t)data->dtpda_data + \ + epd->dtepd_rec[(ndx)].dtrd_offset)) + +static int +dt_handle_err(dtrace_hdl_t *dtp, dtrace_probedata_t *data) +{ + dtrace_eprobedesc_t *epd = data->dtpda_edesc, *errepd; + dtrace_probedesc_t *pd = data->dtpda_pdesc, *errpd; + dtrace_errdata_t err; + dtrace_epid_t epid; + + char where[30]; + char details[30]; + char offinfo[30]; + const int slop = 80; + const char *faultstr; + char *str; + VBDTTYPE(size_t,int) len; + + assert(epd->dtepd_uarg == DT_ECB_ERROR); + + if (epd->dtepd_nrecs != 5 || strcmp(pd->dtpd_provider, "dtrace") != 0 || + strcmp(pd->dtpd_name, "ERROR") != 0) + return (dt_set_errno(dtp, EDT_BADERROR)); + + /* + * This is an error. We have the following items here: EPID, + * faulting action, DIF offset, fault code and faulting address. + */ + epid = (uint32_t)DT_REC(uint64_t, 0); + + if (dt_epid_lookup(dtp, epid, &errepd, &errpd) != 0) + return (dt_set_errno(dtp, EDT_BADERROR)); + + err.dteda_edesc = errepd; + err.dteda_pdesc = errpd; + err.dteda_cpu = data->dtpda_cpu; + err.dteda_action = (int)DT_REC(uint64_t, 1); + err.dteda_offset = (int)DT_REC(uint64_t, 2); + err.dteda_fault = (int)DT_REC(uint64_t, 3); + err.dteda_addr = DT_REC(uint64_t, 4); + + faultstr = dtrace_faultstr(dtp, err.dteda_fault); + len = sizeof (where) + sizeof (offinfo) + strlen(faultstr) + + strlen(errpd->dtpd_provider) + strlen(errpd->dtpd_mod) + + strlen(errpd->dtpd_name) + strlen(errpd->dtpd_func) + + slop; + + str = (char *)alloca(len); + + if (err.dteda_action == 0) { + (void) sprintf(where, "predicate"); + } else { + (void) sprintf(where, "action #%d", err.dteda_action); + } + + if (err.dteda_offset != -1) { + (void) sprintf(offinfo, " at DIF offset %d", err.dteda_offset); + } else { + offinfo[0] = 0; + } + + switch (err.dteda_fault) { + case DTRACEFLT_BADADDR: + case DTRACEFLT_BADALIGN: + case DTRACEFLT_BADSTACK: + (void) sprintf(details, " (0x%llx)", + (u_longlong_t)err.dteda_addr); + break; + + default: + details[0] = 0; + } + + (void) snprintf(str, len, "error on enabled probe ID %u " + "(ID %u: %s:%s:%s:%s): %s%s in %s%s\n", + epid, errpd->dtpd_id, errpd->dtpd_provider, + errpd->dtpd_mod, errpd->dtpd_func, + errpd->dtpd_name, dtrace_faultstr(dtp, err.dteda_fault), + details, where, offinfo); + + err.dteda_msg = str; + + if (dtp->dt_errhdlr == NULL) + return (dt_set_errno(dtp, EDT_ERRABORT)); + + if ((*dtp->dt_errhdlr)(&err, dtp->dt_errarg) == DTRACE_HANDLE_ABORT) + return (dt_set_errno(dtp, EDT_ERRABORT)); + + return (0); +} + +int +dt_handle_liberr(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, + const char *faultstr) +{ + dtrace_probedesc_t *errpd = data->dtpda_pdesc; + dtrace_errdata_t err; + const int slop = 80; + char *str; + VBDTTYPE(size_t,int) len; + + err.dteda_edesc = data->dtpda_edesc; + err.dteda_pdesc = errpd; + err.dteda_cpu = data->dtpda_cpu; + err.dteda_action = -1; + err.dteda_offset = -1; + err.dteda_fault = DTRACEFLT_LIBRARY; + err.dteda_addr = 0 /*NULL*/; + + len = strlen(faultstr) + + strlen(errpd->dtpd_provider) + strlen(errpd->dtpd_mod) + + strlen(errpd->dtpd_name) + strlen(errpd->dtpd_func) + + slop; + + str = alloca(len); + + (void) snprintf(str, len, "error on enabled probe ID %u " + "(ID %u: %s:%s:%s:%s): %s\n", + data->dtpda_edesc->dtepd_epid, + errpd->dtpd_id, errpd->dtpd_provider, + errpd->dtpd_mod, errpd->dtpd_func, + errpd->dtpd_name, faultstr); + + err.dteda_msg = str; + + if (dtp->dt_errhdlr == NULL) + return (dt_set_errno(dtp, EDT_ERRABORT)); + + if ((*dtp->dt_errhdlr)(&err, dtp->dt_errarg) == DTRACE_HANDLE_ABORT) + return (dt_set_errno(dtp, EDT_ERRABORT)); + + return (0); +} + +#define DROPTAG(x) x, #x + +static const struct { + dtrace_dropkind_t dtdrg_kind; + char *dtdrg_tag; +} _dt_droptags[] = { + { DROPTAG(DTRACEDROP_PRINCIPAL) }, + { DROPTAG(DTRACEDROP_AGGREGATION) }, + { DROPTAG(DTRACEDROP_DYNAMIC) }, + { DROPTAG(DTRACEDROP_DYNRINSE) }, + { DROPTAG(DTRACEDROP_DYNDIRTY) }, + { DROPTAG(DTRACEDROP_SPEC) }, + { DROPTAG(DTRACEDROP_SPECBUSY) }, + { DROPTAG(DTRACEDROP_SPECUNAVAIL) }, + { DROPTAG(DTRACEDROP_DBLERROR) }, + { DROPTAG(DTRACEDROP_STKSTROVERFLOW) }, + { 0, NULL } +}; + +static const char * +dt_droptag(dtrace_dropkind_t kind) +{ + int i; + + for (i = 0; _dt_droptags[i].dtdrg_tag != NULL; i++) { + if (_dt_droptags[i].dtdrg_kind == kind) + return (_dt_droptags[i].dtdrg_tag); + } + + return ("DTRACEDROP_UNKNOWN"); +} + +int +dt_handle_cpudrop(dtrace_hdl_t *dtp, processorid_t cpu, + dtrace_dropkind_t what, uint64_t howmany) +{ + dtrace_dropdata_t drop; + char str[80], *s; + VBDTTYPE(size_t,int) size; + + assert(what == DTRACEDROP_PRINCIPAL || what == DTRACEDROP_AGGREGATION); + + bzero(&drop, sizeof (drop)); + drop.dtdda_handle = dtp; + drop.dtdda_cpu = cpu; + drop.dtdda_kind = what; + drop.dtdda_drops = howmany; + drop.dtdda_msg = str; + + if (dtp->dt_droptags) { + (void) snprintf(str, sizeof (str), "[%s] ", dt_droptag(what)); + s = &str[strlen(str)]; + size = sizeof (str) - (s - str); + } else { + s = str; + size = sizeof (str); + } + + (void) snprintf(s, size, "%llu %sdrop%s on CPU %d\n", + howmany, what == DTRACEDROP_PRINCIPAL ? "" : "aggregation ", + howmany > 1 ? "s" : "", cpu); + + if (dtp->dt_drophdlr == NULL) + return (dt_set_errno(dtp, EDT_DROPABORT)); + + if ((*dtp->dt_drophdlr)(&drop, dtp->dt_droparg) == DTRACE_HANDLE_ABORT) + return (dt_set_errno(dtp, EDT_DROPABORT)); + + return (0); +} + +static const struct { + dtrace_dropkind_t dtdrt_kind; + uintptr_t dtdrt_offset; + const char *dtdrt_str; + const char *dtdrt_msg; +} _dt_droptab[] = { + { DTRACEDROP_DYNAMIC, + offsetof(dtrace_status_t, dtst_dyndrops), + "dynamic variable drop" }, + + { DTRACEDROP_DYNRINSE, + offsetof(dtrace_status_t, dtst_dyndrops_rinsing), + "dynamic variable drop", " with non-empty rinsing list" }, + + { DTRACEDROP_DYNDIRTY, + offsetof(dtrace_status_t, dtst_dyndrops_dirty), + "dynamic variable drop", " with non-empty dirty list" }, + + { DTRACEDROP_SPEC, + offsetof(dtrace_status_t, dtst_specdrops), + "speculative drop" }, + + { DTRACEDROP_SPECBUSY, + offsetof(dtrace_status_t, dtst_specdrops_busy), + "failed speculation", " (available buffer(s) still busy)" }, + + { DTRACEDROP_SPECUNAVAIL, + offsetof(dtrace_status_t, dtst_specdrops_unavail), + "failed speculation", " (no speculative buffer available)" }, + + { DTRACEDROP_STKSTROVERFLOW, + offsetof(dtrace_status_t, dtst_stkstroverflows), + "jstack()/ustack() string table overflow" }, + + { DTRACEDROP_DBLERROR, + offsetof(dtrace_status_t, dtst_dblerrors), + "error", " in ERROR probe enabling" }, + + { 0, 0, NULL } +}; + +int +dt_handle_status(dtrace_hdl_t *dtp, dtrace_status_t *old, dtrace_status_t *new) +{ + dtrace_dropdata_t drop; + char str[80], *s; + uintptr_t base = (uintptr_t)new, obase = (uintptr_t)old; + int i, size; + + bzero(&drop, sizeof (drop)); + drop.dtdda_handle = dtp; + drop.dtdda_cpu = DTRACE_CPUALL; + drop.dtdda_msg = str; + + /* + * First, check to see if we've been killed -- in which case we abort. + */ + if (new->dtst_killed && !old->dtst_killed) + return (dt_set_errno(dtp, EDT_BRICKED)); + + for (i = 0; _dt_droptab[i].dtdrt_str != NULL; i++) { + uintptr_t naddr = base + _dt_droptab[i].dtdrt_offset; + uintptr_t oaddr = obase + _dt_droptab[i].dtdrt_offset; + + uint64_t nval = *((uint64_t *)naddr); + uint64_t oval = *((uint64_t *)oaddr); + + if (nval == oval) + continue; + + if (dtp->dt_droptags) { + (void) snprintf(str, sizeof (str), "[%s] ", + dt_droptag(_dt_droptab[i].dtdrt_kind)); + s = &str[strlen(str)]; + size = VBDTCAST(int)sizeof (str) - (s - str); + } else { + s = str; + size = sizeof (str); + } + + (void) snprintf(s, size, "%llu %s%s%s\n", nval - oval, + _dt_droptab[i].dtdrt_str, (nval - oval > 1) ? "s" : "", + _dt_droptab[i].dtdrt_msg != NULL ? + _dt_droptab[i].dtdrt_msg : ""); + + drop.dtdda_kind = _dt_droptab[i].dtdrt_kind; + drop.dtdda_total = nval; + drop.dtdda_drops = nval - oval; + + if (dtp->dt_drophdlr == NULL) + return (dt_set_errno(dtp, EDT_DROPABORT)); + + if ((*dtp->dt_drophdlr)(&drop, + dtp->dt_droparg) == DTRACE_HANDLE_ABORT) + return (dt_set_errno(dtp, EDT_DROPABORT)); + } + + return (0); +} + +int +dt_handle_setopt(dtrace_hdl_t *dtp, dtrace_setoptdata_t *data) +{ + void *arg = dtp->dt_setoptarg; + + if (dtp->dt_setopthdlr == NULL) + return (0); + + if ((*dtp->dt_setopthdlr)(data, arg) == DTRACE_HANDLE_ABORT) + return (dt_set_errno(dtp, EDT_DIRABORT)); + + return (0); +} + +int +dt_handle(dtrace_hdl_t *dtp, dtrace_probedata_t *data) +{ + dtrace_eprobedesc_t *epd = data->dtpda_edesc; + int rval; + + switch (epd->dtepd_uarg) { + case DT_ECB_ERROR: + rval = dt_handle_err(dtp, data); + break; + + default: + return (DTRACE_CONSUME_THIS); + } + + if (rval == 0) + return (DTRACE_CONSUME_NEXT); + + return (DTRACE_CONSUME_ERROR); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_ident.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_ident.c new file mode 100644 index 00000000..37a80be8 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_ident.c @@ -0,0 +1,1049 @@ +/* + * 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. + */ + +#ifndef VBOX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# include +#endif /* VBOX */ + +#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); +} + +/* + * 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); + + } 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); +#ifndef VBOX + s = strdupa(idp->di_iarg); +#else + MY_STRDUPA(s, idp->di_iarg); +#endif + + 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); + } + } + + 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 >= VBDTCAST(uintmax_t)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); + + } 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)); + + } 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); +} + +/*ARGSUSED*/ +static void +dt_idcook_type(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) +{ + RT_NOREF2(argc, 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); +} + +/*ARGSUSED*/ +static void +dt_idcook_thaw(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) +{ + RT_NOREF2(argc, args); + if (idp->di_ctfp != NULL && idp->di_type != CTF_ERR) + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); +} + +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) +{ + RT_NOREF1(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) +{ + RT_NOREF1(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 (""); + } +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_ident.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_ident.h new file mode 100644 index 00000000..367ed5e7 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_ident.h @@ -0,0 +1,185 @@ +/* + * 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. + */ + +#ifndef _DT_IDENT_H +#define _DT_IDENT_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct dt_node; +struct dt_ident; +struct dt_idhash; +struct dt_irlist; +struct dt_regset; + +typedef struct dt_idsig { + int dis_varargs; /* argument index of start of varargs (or -1) */ + int dis_optargs; /* argument index of start of optargs (or -1) */ + int dis_argc; /* number of types in this signature */ + struct dt_node *dis_args; /* array of nodes representing formal types */ + uint64_t dis_auxinfo; /* auxiliary signature information, if any */ +} dt_idsig_t; + +typedef struct dt_idnode { + struct dt_node *din_list; /* allocation list for parse tree nodes */ + struct dt_node *din_root; /* root of this identifier's parse tree */ + struct dt_idhash *din_hash; /* identifiers private to this subtree */ + struct dt_ident **din_argv; /* identifiers in din_hash for arguments */ + int din_argc; /* length of din_argv[] array */ +} dt_idnode_t; + +typedef struct dt_idops { + void (*di_cook)(struct dt_node *, struct dt_ident *, + int, struct dt_node *); + void (*di_dtor)(struct dt_ident *); + size_t (*di_size)(struct dt_ident *); +} dt_idops_t; + +typedef struct dt_ident { + char *di_name; /* identifier name */ + ushort_t di_kind; /* identifier kind (see below) */ + ushort_t di_flags; /* identifier flags (see below) */ + uint_t di_id; /* variable or subr id (see ) */ + dtrace_attribute_t di_attr; /* identifier stability attributes */ + uint_t di_vers; /* identifier version number (dt_version_t) */ + const dt_idops_t *di_ops; /* identifier's class-specific ops vector */ + void *di_iarg; /* initial argument pointer for ops vector */ + void *di_data; /* private data pointer for ops vector */ + ctf_file_t *di_ctfp; /* CTF container for the variable data type */ + ctf_id_t di_type; /* CTF identifier for the variable data type */ + struct dt_ident *di_next; /* pointer to next ident in hash chain */ + ulong_t di_gen; /* generation number (pass that created me) */ + int di_lineno; /* line number that defined this identifier */ +} dt_ident_t; + +#define DT_IDENT_ARRAY 0 /* identifier is an array variable */ +#define DT_IDENT_SCALAR 1 /* identifier is a scalar variable */ +#define DT_IDENT_PTR 2 /* identifier is a magic pointer */ +#define DT_IDENT_FUNC 3 /* identifier is a built-in function */ +#define DT_IDENT_AGG 4 /* identifier is an aggregation */ +#define DT_IDENT_AGGFUNC 5 /* identifier is an aggregating function */ +#define DT_IDENT_ACTFUNC 6 /* identifier is an action function */ +#define DT_IDENT_XLSOU 7 /* identifier is a translated struct or union */ +#define DT_IDENT_XLPTR 8 /* identifier is a translated pointer */ +#define DT_IDENT_SYMBOL 9 /* identifier is an external symbol */ +#define DT_IDENT_ENUM 10 /* identifier is an enumerator */ +#define DT_IDENT_PRAGAT 11 /* identifier is #pragma attributes */ +#define DT_IDENT_PRAGBN 12 /* identifier is #pragma binding */ +#define DT_IDENT_PROBE 13 /* identifier is a probe definition */ + +#define DT_IDFLG_TLS 0x0001 /* variable is thread-local storage */ +#define DT_IDFLG_LOCAL 0x0002 /* variable is local storage */ +#define DT_IDFLG_WRITE 0x0004 /* variable is writable (can be modified) */ +#define DT_IDFLG_INLINE 0x0008 /* variable is an inline definition */ +#define DT_IDFLG_REF 0x0010 /* variable is referenced by this program */ +#define DT_IDFLG_MOD 0x0020 /* variable is modified by this program */ +#define DT_IDFLG_DIFR 0x0040 /* variable is referenced by current DIFO */ +#define DT_IDFLG_DIFW 0x0080 /* variable is modified by current DIFO */ +#define DT_IDFLG_CGREG 0x0100 /* variable is inlined by code generator */ +#define DT_IDFLG_USER 0x0200 /* variable is associated with userland */ +#define DT_IDFLG_PRIM 0x0400 /* variable is associated with primary object */ +#define DT_IDFLG_DECL 0x0800 /* variable is associated with explicit decl */ +#define DT_IDFLG_ORPHAN 0x1000 /* variable is in a dt_node and not dt_idhash */ + +typedef struct dt_idhash { + dt_list_t dh_list; /* list prev/next pointers for dt_idstack */ + const char *dh_name; /* name of this hash table */ + void (*dh_defer)(struct dt_idhash *, dt_ident_t *); /* defer callback */ + const dt_ident_t *dh_tmpl; /* template for initial ident population */ + uint_t dh_nextid; /* next id to be returned by idhash_nextid() */ + uint_t dh_minid; /* min id to be returned by idhash_nextid() */ + uint_t dh_maxid; /* max id to be returned by idhash_nextid() */ + ulong_t dh_nelems; /* number of identifiers in hash table */ + ulong_t dh_hashsz; /* number of entries in dh_buckets array */ + dt_ident_t *dh_hash[1]; /* array of hash table bucket pointers */ +} dt_idhash_t; + +typedef struct dt_idstack { + dt_list_t dids_list; /* list meta-data for dt_idhash_t stack */ +} dt_idstack_t; + +extern const dt_idops_t dt_idops_assc; /* associative array or aggregation */ +extern const dt_idops_t dt_idops_func; /* function call built-in */ +extern const dt_idops_t dt_idops_args; /* args[] built-in */ +extern const dt_idops_t dt_idops_regs; /* regs[]/uregs[] built-in */ +extern const dt_idops_t dt_idops_type; /* predefined type name string */ +extern const dt_idops_t dt_idops_thaw; /* prefrozen type identifier */ +extern const dt_idops_t dt_idops_inline; /* inline variable */ +extern const dt_idops_t dt_idops_probe; /* probe definition */ + +extern dt_idhash_t *dt_idhash_create(const char *, const dt_ident_t *, + uint_t, uint_t); +extern void dt_idhash_destroy(dt_idhash_t *); +extern void dt_idhash_update(dt_idhash_t *); +extern dt_ident_t *dt_idhash_lookup(dt_idhash_t *, const char *); +extern int dt_idhash_nextid(dt_idhash_t *, uint_t *); +extern ulong_t dt_idhash_size(const dt_idhash_t *); +extern const char *dt_idhash_name(const dt_idhash_t *); + +extern dt_ident_t *dt_idhash_insert(dt_idhash_t *, const char *, ushort_t, + ushort_t, uint_t, dtrace_attribute_t, uint_t, + const dt_idops_t *, void *, ulong_t); + +extern void dt_idhash_xinsert(dt_idhash_t *, dt_ident_t *); +extern void dt_idhash_delete(dt_idhash_t *, dt_ident_t *); + +typedef int dt_idhash_f(dt_idhash_t *, dt_ident_t *, void *); +extern int dt_idhash_iter(dt_idhash_t *, dt_idhash_f *, void *); + +extern dt_ident_t *dt_idstack_lookup(dt_idstack_t *, const char *); +extern void dt_idstack_push(dt_idstack_t *, dt_idhash_t *); +extern void dt_idstack_pop(dt_idstack_t *, dt_idhash_t *); + +extern dt_ident_t *dt_ident_create(const char *, ushort_t, ushort_t, uint_t, + dtrace_attribute_t, uint_t, const dt_idops_t *, void *, ulong_t); +extern void dt_ident_destroy(dt_ident_t *); +extern void dt_ident_morph(dt_ident_t *, ushort_t, const dt_idops_t *, void *); +extern dtrace_attribute_t dt_ident_cook(struct dt_node *, + dt_ident_t *, struct dt_node **); + +extern void dt_ident_type_assign(dt_ident_t *, ctf_file_t *, ctf_id_t); +extern dt_ident_t *dt_ident_resolve(dt_ident_t *); +extern size_t dt_ident_size(dt_ident_t *); +extern int dt_ident_unref(const dt_ident_t *); + +extern const char *dt_idkind_name(uint_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_IDENT_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_impl.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_impl.h new file mode 100644 index 00000000..0e40f722 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_impl.h @@ -0,0 +1,669 @@ +/* + * 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. + */ + +#ifndef _DT_IMPL_H +#define _DT_IMPL_H + +#ifndef VBOX +#include +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# include +# include +# include +# include +# include "VBoxDTraceLibCWrappers.h" +#endif /* VBOX*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +#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 */ +#ifndef VBOX + Elf *dm_elf; /* libelf handle for module object */ + objfs_info_t dm_info; /* object filesystem private info */ +#endif + 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 */ +} 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 */ +} 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 */ + 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 */ + dt_aggregate_t dt_aggregate; /* aggregate */ + dtrace_bufdesc_t dt_buf; /* staging buffer */ + 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_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 */ + 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 */ + int dt_stdout_fd; /* file descriptor for saved stdout */ + 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) */ +#ifndef VBOX + struct utsname dt_uts; /* uname(2) information for system */ +#endif + dt_list_t dt_lib_dep; /* scratch linked-list of lib dependencies */ + dt_list_t dt_lib_dep_sorted; /* dependency sorted library list */ +}; + +/* + * 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 */ + +/* + * 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 */ + +/* + * 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_NOREG, /* register allocation failure */ + 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 */ +}; + +/* + * 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 *); + +extern int dt_set_errno(dtrace_hdl_t *, int); +extern void dt_set_errmsg(dtrace_hdl_t *, const char *, const char *, + const char *, int, const char *, va_list); + +extern int dt_ioctl(dtrace_hdl_t *, int, void *); +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_options_load(dtrace_hdl_t *); + +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 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_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 */ +#ifdef VBOX +extern int yylex(void); +#endif + +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 */ + diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_inttab.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_inttab.c new file mode 100644 index 00000000..9055f439 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_inttab.c @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#endif + +#include +#include + +dt_inttab_t * +dt_inttab_create(dtrace_hdl_t *dtp) +{ + uint_t len = _dtrace_intbuckets; + dt_inttab_t *ip; + + assert((len & (len - 1)) == 0); + + if ((ip = dt_zalloc(dtp, sizeof (dt_inttab_t))) == NULL || + (ip->int_hash = dt_zalloc(dtp, sizeof (void *) * len)) == NULL) { + dt_free(dtp, ip); + return (NULL); + } + + ip->int_hdl = dtp; + ip->int_hashlen = len; + + return (ip); +} + +void +dt_inttab_destroy(dt_inttab_t *ip) +{ + dt_inthash_t *hp, *np; + + for (hp = ip->int_head; hp != NULL; hp = np) { + np = hp->inh_next; + dt_free(ip->int_hdl, hp); + } + + dt_free(ip->int_hdl, ip->int_hash); + dt_free(ip->int_hdl, ip); +} + +int +dt_inttab_insert(dt_inttab_t *ip, uint64_t value, uint_t flags) +{ + uint_t h = value & (ip->int_hashlen - 1); + dt_inthash_t *hp; + + if (flags & DT_INT_SHARED) { + for (hp = ip->int_hash[h]; hp != NULL; hp = hp->inh_hash) { + if (hp->inh_value == value && hp->inh_flags == flags) + return (hp->inh_index); + } + } + + if ((hp = dt_alloc(ip->int_hdl, sizeof (dt_inthash_t))) == NULL) + return (-1); + + hp->inh_hash = ip->int_hash[h]; + hp->inh_next = NULL; + hp->inh_value = value; + hp->inh_index = ip->int_index++; + hp->inh_flags = flags; + + ip->int_hash[h] = hp; + ip->int_nelems++; + + if (ip->int_head == NULL) + ip->int_head = hp; + else + ip->int_tail->inh_next = hp; + + ip->int_tail = hp; + return (hp->inh_index); +} + +uint_t +dt_inttab_size(const dt_inttab_t *ip) +{ + return (ip->int_nelems); +} + +void +dt_inttab_write(const dt_inttab_t *ip, uint64_t *dst) +{ + const dt_inthash_t *hp; + + for (hp = ip->int_head; hp != NULL; hp = hp->inh_next) + *dst++ = hp->inh_value; +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_inttab.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_inttab.h new file mode 100644 index 00000000..08daa4bb --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_inttab.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef _DT_INTTAB_H +#define _DT_INTTAB_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dt_inthash { + struct dt_inthash *inh_hash; /* next dt_inthash in hash chain */ + struct dt_inthash *inh_next; /* next dt_inthash in output table */ + uint64_t inh_value; /* value associated with this element */ + uint_t inh_index; /* index associated with this element */ + uint_t inh_flags; /* flags (see below) */ +} dt_inthash_t; + +typedef struct dt_inttab { + dtrace_hdl_t *int_hdl; /* pointer back to library handle */ + dt_inthash_t **int_hash; /* array of hash buckets */ + uint_t int_hashlen; /* size of hash bucket array */ + uint_t int_nelems; /* number of elements hashed */ + dt_inthash_t *int_head; /* head of table in index order */ + dt_inthash_t *int_tail; /* tail of table in index order */ + uint_t int_index; /* next index to hand out */ +} dt_inttab_t; + +#define DT_INT_PRIVATE 0 /* only a single ref for this entry */ +#define DT_INT_SHARED 1 /* multiple refs can share entry */ + +extern dt_inttab_t *dt_inttab_create(dtrace_hdl_t *); +extern void dt_inttab_destroy(dt_inttab_t *); +extern int dt_inttab_insert(dt_inttab_t *, uint64_t, uint_t); +extern uint_t dt_inttab_size(const dt_inttab_t *); +extern void dt_inttab_write(const dt_inttab_t *, uint64_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_INTTAB_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_lex.l b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_lex.l new file mode 100644 index 00000000..39228108 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_lex.l @@ -0,0 +1,931 @@ +%{ +/* + * 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. + */ + +#ifndef VBOX +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# ifdef RT_OS_WINDOWS /* No unistd.h on windows, avoid redef warnings of the [U]INTxx_MAX defines caused by no inttypes.h/stdint.h. */ +# define YY_NO_UNISTD_H +# undef INT8_MIN +# undef INT16_MIN +# undef INT32_MIN +# undef INT64_MIN +# undef INT8_MAX +# undef INT16_MAX +# undef INT32_MAX +# undef INT64_MAX +# undef UINT8_MAX +# undef UINT16_MAX +# undef UINT32_MAX +# undef UINT64_MAX +# endif +# ifdef _MSC_VER +# pragma warning(disable:4668 4131 4255) +# endif +# ifdef __GNUC__ +# if ((__GNUC__ << 16) + __GNUC_MINOR__) >= 0x40002 +# pragma GCC diagnostic ignored "-Wunused-function" +# endif +# endif +# include +# include +# define isdigit(a_ch) RT_C_IS_DIGIT(a_ch) +# ifdef _MSC_VER +# include /* win_flex: isatty */ +# endif +#endif /* VBOX */ + +#include +#include +#include +#include + +#ifndef USING_FLEX +/* + * 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. + */ +#undef input +#undef unput +#else /* USING_FLEX */ +# define YY_INPUT(buf, result, max_size) \ + do { \ + if (yypcb->pcb_fileptr != NULL) { \ + result = (int)fread(buf, 1, max_size, yypcb->pcb_fileptr); \ + if (!result && ferror(yypcb->pcb_fileptr)) \ + longjmp(yypcb->pcb_jmpbuf, EDT_FIO); \ + /*YY_FATAL_ERROR("input in flex scanner failed");*/ \ + } else { \ + size_t off = yypcb->pcb_strptr - yypcb->pcb_string; \ + if (off < yypcb->pcb_strlen) { \ + off = yypcb->pcb_strlen - off; \ + result = max_size; \ + if ((size_t)result > off) \ + result = (int)off; \ + memcpy(buf, yypcb->pcb_strptr, result); \ + yypcb->pcb_strptr += result; \ + /*fprintf(stderr, "yy_input -> %d '%.*s'\n", result, result, buf);*/ \ + } else { \ + buf[0] = '\0'; \ + result = 0; \ + /*fprintf(stderr, "yy_input -> %d\n", result);*/ \ + } \ + } \ + } while (0) +#endif /* USING_FLEX */ + +static int id_or_type(const char *); +#ifndef USING_FLEX +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 */ +%n 600 /* maximum states */ + +%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_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); +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] == '+') { +#ifdef VBOX + int rc; + uint64_t uTmp = 0; +#endif + if (isdigit(v[0])) + yyintprefix = 0; + else + yyintprefix = *v++; + +#ifndef VBOX + errno = 0; + yylval.l_int = strtoull(v, &p, 0); + (void) strncpy(yyintsuffix, p, + sizeof (yyintsuffix)); + yyintdecimal = *v != '0'; + if (errno == ERANGE) +#else + rc = RTStrToUInt64Ex(v, &p, 0, &uTmp); + yylval.l_int = uTmp; + RTStrCopy(yyintsuffix, sizeof(yyintsuffix), p); + yyintdecimal = *v != '0'; + if (rc == VWRN_NUMBER_TOO_BIG || rc == VWRN_NEGATIVE_UNSIGNED) +#endif + 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} { + 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; + +#ifndef VBOX + errno = 0; + yylval.l_int = strtoull(yytext, &p, 0); + yyintprefix = 0; + (void) strncpy(yyintsuffix, p, sizeof (yyintsuffix)); + yyintdecimal = yytext[0] != '0'; + if (errno == ERANGE) +#else + uint64_t uTmp = 0; + int rc = RTStrToUInt64Ex(yytext, &p, 0, &uTmp); + yylval.l_int = uTmp; + yyintprefix = 0; + RTStrCopy(yyintsuffix, sizeof(yyintsuffix), p); + yyintdecimal = yytext[0] != '0'; + if (rc == VWRN_NUMBER_TOO_BIG || rc == VWRN_NEGATIVE_UNSIGNED) +#endif + 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"); + + /* VBOX changed: +\"{RGX_STR}$ | +\"{RGX_STR}$ xyerror(D_STR_NL, "newline encountered in string literal"); + input: */ +\"{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"); + } +#ifdef _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; + + if (c != EOF) 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); + /* VBOX - START */ +<> yyerror("end-of-file encountered before matching */\n"); + /* VBOX - END */ +.|\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); + } + + /* VBOX - BEGIN */ +<> yyerror("end-of-file encountered before end of control line\n"); + /* VBOX - END */ +. 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) +{ +#if YYDEBUG /* VBOX: ifdef YYDEBUG => if 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; + RT_FALL_THRU(); + 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; +#ifndef USING_FLEX + yysptr = yysbuf; +#else + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); +#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; + if (c1 != EOF) unput(c1); + break; + + case '=': + if ((c1 = input()) != c0) + ttok = DT_TOK_IDENT; + if (c1 != EOF) 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); + } + + if (c0 != EOF) unput(c0); + return (ttok); +} + +#ifndef USING_FLEX + +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 /* USING_FLEX */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_link.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_link.c new file mode 100644 index 00000000..e910ac3f --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_link.c @@ -0,0 +1,1634 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define ELF_TARGET_ALL +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define ESHDR_NULL 0 +#define ESHDR_SHSTRTAB 1 +#define ESHDR_DOF 2 +#define ESHDR_STRTAB 3 +#define ESHDR_SYMTAB 4 +#define ESHDR_REL 5 +#define ESHDR_NUM 6 + +#define PWRITE_SCN(index, data) \ + (lseek64(fd, (off64_t)elf_file.shdr[(index)].sh_offset, SEEK_SET) != \ + (off64_t)elf_file.shdr[(index)].sh_offset || \ + dt_write(dtp, fd, (data), elf_file.shdr[(index)].sh_size) != \ + elf_file.shdr[(index)].sh_size) + +static const char DTRACE_SHSTRTAB32[] = "\0" +".shstrtab\0" /* 1 */ +".SUNW_dof\0" /* 11 */ +".strtab\0" /* 21 */ +".symtab\0" /* 29 */ +#ifdef __sparc +".rela.SUNW_dof"; /* 37 */ +#else +".rel.SUNW_dof"; /* 37 */ +#endif + +static const char DTRACE_SHSTRTAB64[] = "\0" +".shstrtab\0" /* 1 */ +".SUNW_dof\0" /* 11 */ +".strtab\0" /* 21 */ +".symtab\0" /* 29 */ +".rela.SUNW_dof"; /* 37 */ + +static const char DOFSTR[] = "__SUNW_dof"; +static const char DOFLAZYSTR[] = "___SUNW_dof"; + +typedef struct dt_link_pair { + struct dt_link_pair *dlp_next; /* next pair in linked list */ + void *dlp_str; /* buffer for string table */ + void *dlp_sym; /* buffer for symbol table */ +} dt_link_pair_t; + +typedef struct dof_elf32 { + uint32_t de_nrel; /* relocation count */ +#ifdef __sparc + Elf32_Rela *de_rel; /* array of relocations for sparc */ +#else + Elf32_Rel *de_rel; /* array of relocations for x86 */ +#endif + uint32_t de_nsym; /* symbol count */ + Elf32_Sym *de_sym; /* array of symbols */ + uint32_t de_strlen; /* size of of string table */ + char *de_strtab; /* string table */ + uint32_t de_global; /* index of the first global symbol */ +} dof_elf32_t; + +static int +prepare_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf32_t *dep) +{ + dof_sec_t *dofs, *s; + dof_relohdr_t *dofrh; + dof_relodesc_t *dofr; + char *strtab; + int i, j, nrel; + size_t strtabsz = 1; + uint32_t count = 0; + size_t base; + Elf32_Sym *sym; +#ifdef __sparc + Elf32_Rela *rel; +#else + Elf32_Rel *rel; +#endif + + /*LINTED*/ + dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff); + + /* + * First compute the size of the string table and the number of + * relocations present in the DOF. + */ + for (i = 0; i < dof->dofh_secnum; i++) { + if (dofs[i].dofs_type != DOF_SECT_URELHDR) + continue; + + /*LINTED*/ + dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); + + s = &dofs[dofrh->dofr_strtab]; + strtab = (char *)dof + s->dofs_offset; + assert(strtab[0] == '\0'); + strtabsz += s->dofs_size - 1; + + s = &dofs[dofrh->dofr_relsec]; + /*LINTED*/ + dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); + count += s->dofs_size / s->dofs_entsize; + } + + dep->de_strlen = strtabsz; + dep->de_nrel = count; + dep->de_nsym = count + 1; /* the first symbol is always null */ + + if (dtp->dt_lazyload) { + dep->de_strlen += sizeof (DOFLAZYSTR); + dep->de_nsym++; + } else { + dep->de_strlen += sizeof (DOFSTR); + dep->de_nsym++; + } + + if ((dep->de_rel = calloc(dep->de_nrel, + sizeof (dep->de_rel[0]))) == NULL) { + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf32_Sym))) == NULL) { + free(dep->de_rel); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) { + free(dep->de_rel); + free(dep->de_sym); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + count = 0; + strtabsz = 1; + dep->de_strtab[0] = '\0'; + rel = dep->de_rel; + sym = dep->de_sym; + dep->de_global = 1; + + /* + * The first symbol table entry must be zeroed and is always ignored. + */ + bzero(sym, sizeof (Elf32_Sym)); + sym++; + + /* + * Take a second pass through the DOF sections filling in the + * memory we allocated. + */ + for (i = 0; i < dof->dofh_secnum; i++) { + if (dofs[i].dofs_type != DOF_SECT_URELHDR) + continue; + + /*LINTED*/ + dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); + + s = &dofs[dofrh->dofr_strtab]; + strtab = (char *)dof + s->dofs_offset; + bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size); + base = strtabsz; + strtabsz += s->dofs_size - 1; + + s = &dofs[dofrh->dofr_relsec]; + /*LINTED*/ + dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); + nrel = s->dofs_size / s->dofs_entsize; + + s = &dofs[dofrh->dofr_tgtsec]; + + for (j = 0; j < nrel; j++) { +#if defined(__i386) || defined(__amd64) + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset; + rel->r_info = ELF32_R_INFO(count + dep->de_global, + R_386_32); +#elif defined(__sparc) + /* + * Add 4 bytes to hit the low half of this 64-bit + * big-endian address. + */ + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset + 4; + rel->r_info = ELF32_R_INFO(count + dep->de_global, + R_SPARC_32); +#else +#error unknown ISA +#endif + + sym->st_name = base + dofr[j].dofr_name - 1; + sym->st_value = 0; + sym->st_size = 0; + sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC); + sym->st_other = 0; + sym->st_shndx = SHN_UNDEF; + + rel++; + sym++; + count++; + } + } + + /* + * Add a symbol for the DOF itself. We use a different symbol for + * lazily and actively loaded DOF to make them easy to distinguish. + */ + sym->st_name = strtabsz; + sym->st_value = 0; + sym->st_size = dof->dofh_filesz; + sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT); + sym->st_other = 0; + sym->st_shndx = ESHDR_DOF; + sym++; + + if (dtp->dt_lazyload) { + bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz, + sizeof (DOFLAZYSTR)); + strtabsz += sizeof (DOFLAZYSTR); + } else { + bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR)); + strtabsz += sizeof (DOFSTR); + } + + assert(count == dep->de_nrel); + assert(strtabsz == dep->de_strlen); + + return (0); +} + + +typedef struct dof_elf64 { + uint32_t de_nrel; + Elf64_Rela *de_rel; + uint32_t de_nsym; + Elf64_Sym *de_sym; + + uint32_t de_strlen; + char *de_strtab; + + uint32_t de_global; +} dof_elf64_t; + +static int +prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) +{ + dof_sec_t *dofs, *s; + dof_relohdr_t *dofrh; + dof_relodesc_t *dofr; + char *strtab; + int i, j, nrel; + size_t strtabsz = 1; + uint32_t count = 0; + size_t base; + Elf64_Sym *sym; + Elf64_Rela *rel; + + /*LINTED*/ + dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff); + + /* + * First compute the size of the string table and the number of + * relocations present in the DOF. + */ + for (i = 0; i < dof->dofh_secnum; i++) { + if (dofs[i].dofs_type != DOF_SECT_URELHDR) + continue; + + /*LINTED*/ + dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); + + s = &dofs[dofrh->dofr_strtab]; + strtab = (char *)dof + s->dofs_offset; + assert(strtab[0] == '\0'); + strtabsz += s->dofs_size - 1; + + s = &dofs[dofrh->dofr_relsec]; + /*LINTED*/ + dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); + count += s->dofs_size / s->dofs_entsize; + } + + dep->de_strlen = strtabsz; + dep->de_nrel = count; + dep->de_nsym = count + 1; /* the first symbol is always null */ + + if (dtp->dt_lazyload) { + dep->de_strlen += sizeof (DOFLAZYSTR); + dep->de_nsym++; + } else { + dep->de_strlen += sizeof (DOFSTR); + dep->de_nsym++; + } + + if ((dep->de_rel = calloc(dep->de_nrel, + sizeof (dep->de_rel[0]))) == NULL) { + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf64_Sym))) == NULL) { + free(dep->de_rel); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) { + free(dep->de_rel); + free(dep->de_sym); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + count = 0; + strtabsz = 1; + dep->de_strtab[0] = '\0'; + rel = dep->de_rel; + sym = dep->de_sym; + dep->de_global = 1; + + /* + * The first symbol table entry must be zeroed and is always ignored. + */ + bzero(sym, sizeof (Elf64_Sym)); + sym++; + + /* + * Take a second pass through the DOF sections filling in the + * memory we allocated. + */ + for (i = 0; i < dof->dofh_secnum; i++) { + if (dofs[i].dofs_type != DOF_SECT_URELHDR) + continue; + + /*LINTED*/ + dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); + + s = &dofs[dofrh->dofr_strtab]; + strtab = (char *)dof + s->dofs_offset; + bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size); + base = strtabsz; + strtabsz += s->dofs_size - 1; + + s = &dofs[dofrh->dofr_relsec]; + /*LINTED*/ + dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); + nrel = s->dofs_size / s->dofs_entsize; + + s = &dofs[dofrh->dofr_tgtsec]; + + for (j = 0; j < nrel; j++) { +#if defined(__i386) || defined(__amd64) + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset; + rel->r_info = ELF64_R_INFO(count + dep->de_global, + R_AMD64_64); +#elif defined(__sparc) + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset; + rel->r_info = ELF64_R_INFO(count + dep->de_global, + R_SPARC_64); +#else +#error unknown ISA +#endif + + sym->st_name = base + dofr[j].dofr_name - 1; + sym->st_value = 0; + sym->st_size = 0; + sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_FUNC); + sym->st_other = 0; + sym->st_shndx = SHN_UNDEF; + + rel++; + sym++; + count++; + } + } + + /* + * Add a symbol for the DOF itself. We use a different symbol for + * lazily and actively loaded DOF to make them easy to distinguish. + */ + sym->st_name = strtabsz; + sym->st_value = 0; + sym->st_size = dof->dofh_filesz; + sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_OBJECT); + sym->st_other = 0; + sym->st_shndx = ESHDR_DOF; + sym++; + + if (dtp->dt_lazyload) { + bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz, + sizeof (DOFLAZYSTR)); + strtabsz += sizeof (DOFLAZYSTR); + } else { + bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR)); + strtabsz += sizeof (DOFSTR); + } + + assert(count == dep->de_nrel); + assert(strtabsz == dep->de_strlen); + + return (0); +} + +/* + * Write out an ELF32 file prologue consisting of a header, section headers, + * and a section header string table. The DOF data will follow this prologue + * and complete the contents of the given ELF file. + */ +static int +dump_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) +{ + struct { + Elf32_Ehdr ehdr; + Elf32_Shdr shdr[ESHDR_NUM]; + } elf_file; + + Elf32_Shdr *shp; + Elf32_Off off; + dof_elf32_t de; + int ret = 0; + uint_t nshdr; + + if (prepare_elf32(dtp, dof, &de) != 0) + return (-1); /* errno is set for us */ + + /* + * If there are no relocations, we only need enough sections for + * the shstrtab and the DOF. + */ + nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; + + bzero(&elf_file, sizeof (elf_file)); + + elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; + elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; + elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; + elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; + elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; + elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS32; +#if defined(_BIG_ENDIAN) + elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#elif defined(_LITTLE_ENDIAN) + elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#endif + elf_file.ehdr.e_type = ET_REL; +#if defined(__sparc) + elf_file.ehdr.e_machine = EM_SPARC; +#elif defined(__i386) || defined(__amd64) + elf_file.ehdr.e_machine = EM_386; +#endif + elf_file.ehdr.e_version = EV_CURRENT; + elf_file.ehdr.e_shoff = sizeof (Elf32_Ehdr); + elf_file.ehdr.e_ehsize = sizeof (Elf32_Ehdr); + elf_file.ehdr.e_phentsize = sizeof (Elf32_Phdr); + elf_file.ehdr.e_shentsize = sizeof (Elf32_Shdr); + elf_file.ehdr.e_shnum = nshdr; + elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; + off = sizeof (elf_file) + nshdr * sizeof (Elf32_Shdr); + + shp = &elf_file.shdr[ESHDR_SHSTRTAB]; + shp->sh_name = 1; /* DTRACE_SHSTRTAB32[1] = ".shstrtab" */ + shp->sh_type = SHT_STRTAB; + shp->sh_offset = off; + shp->sh_size = sizeof (DTRACE_SHSTRTAB32); + shp->sh_addralign = sizeof (char); + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); + + shp = &elf_file.shdr[ESHDR_DOF]; + shp->sh_name = 11; /* DTRACE_SHSTRTAB32[11] = ".SUNW_dof" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_SUNW_dof; + shp->sh_offset = off; + shp->sh_size = dof->dofh_filesz; + shp->sh_addralign = 8; + off = shp->sh_offset + shp->sh_size; + + shp = &elf_file.shdr[ESHDR_STRTAB]; + shp->sh_name = 21; /* DTRACE_SHSTRTAB32[21] = ".strtab" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_STRTAB; + shp->sh_offset = off; + shp->sh_size = de.de_strlen; + shp->sh_addralign = sizeof (char); + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 4); + + shp = &elf_file.shdr[ESHDR_SYMTAB]; + shp->sh_name = 29; /* DTRACE_SHSTRTAB32[29] = ".symtab" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_SYMTAB; + shp->sh_entsize = sizeof (Elf32_Sym); + shp->sh_link = ESHDR_STRTAB; + shp->sh_offset = off; + shp->sh_info = de.de_global; + shp->sh_size = de.de_nsym * sizeof (Elf32_Sym); + shp->sh_addralign = 4; + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 4); + + if (de.de_nrel == 0) { + if (dt_write(dtp, fd, &elf_file, + sizeof (elf_file)) != sizeof (elf_file) || + PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) || + PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || + PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || + PWRITE_SCN(ESHDR_DOF, dof)) { + ret = dt_set_errno(dtp, errno); + } + } else { + shp = &elf_file.shdr[ESHDR_REL]; + shp->sh_name = 37; /* DTRACE_SHSTRTAB32[37] = ".rel.SUNW_dof" */ + shp->sh_flags = SHF_ALLOC; +#ifdef __sparc + shp->sh_type = SHT_RELA; +#else + shp->sh_type = SHT_REL; +#endif + shp->sh_entsize = sizeof (de.de_rel[0]); + shp->sh_link = ESHDR_SYMTAB; + shp->sh_info = ESHDR_DOF; + shp->sh_offset = off; + shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); + shp->sh_addralign = 4; + + if (dt_write(dtp, fd, &elf_file, + sizeof (elf_file)) != sizeof (elf_file) || + PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) || + PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || + PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || + PWRITE_SCN(ESHDR_REL, de.de_rel) || + PWRITE_SCN(ESHDR_DOF, dof)) { + ret = dt_set_errno(dtp, errno); + } + } + + free(de.de_strtab); + free(de.de_sym); + free(de.de_rel); + + return (ret); +} + +/* + * Write out an ELF64 file prologue consisting of a header, section headers, + * and a section header string table. The DOF data will follow this prologue + * and complete the contents of the given ELF file. + */ +static int +dump_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) +{ + struct { + Elf64_Ehdr ehdr; + Elf64_Shdr shdr[ESHDR_NUM]; + } elf_file; + + Elf64_Shdr *shp; + Elf64_Off off; + dof_elf64_t de; + int ret = 0; + uint_t nshdr; + + if (prepare_elf64(dtp, dof, &de) != 0) + return (-1); /* errno is set for us */ + + /* + * If there are no relocations, we only need enough sections for + * the shstrtab and the DOF. + */ + nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; + + bzero(&elf_file, sizeof (elf_file)); + + elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; + elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; + elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; + elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; + elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; + elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS64; +#if defined(_BIG_ENDIAN) + elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#elif defined(_LITTLE_ENDIAN) + elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#endif + elf_file.ehdr.e_type = ET_REL; +#if defined(__sparc) + elf_file.ehdr.e_machine = EM_SPARCV9; +#elif defined(__i386) || defined(__amd64) + elf_file.ehdr.e_machine = EM_AMD64; +#endif + elf_file.ehdr.e_version = EV_CURRENT; + elf_file.ehdr.e_shoff = sizeof (Elf64_Ehdr); + elf_file.ehdr.e_ehsize = sizeof (Elf64_Ehdr); + elf_file.ehdr.e_phentsize = sizeof (Elf64_Phdr); + elf_file.ehdr.e_shentsize = sizeof (Elf64_Shdr); + elf_file.ehdr.e_shnum = nshdr; + elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; + off = sizeof (elf_file) + nshdr * sizeof (Elf64_Shdr); + + shp = &elf_file.shdr[ESHDR_SHSTRTAB]; + shp->sh_name = 1; /* DTRACE_SHSTRTAB64[1] = ".shstrtab" */ + shp->sh_type = SHT_STRTAB; + shp->sh_offset = off; + shp->sh_size = sizeof (DTRACE_SHSTRTAB64); + shp->sh_addralign = sizeof (char); + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); + + shp = &elf_file.shdr[ESHDR_DOF]; + shp->sh_name = 11; /* DTRACE_SHSTRTAB64[11] = ".SUNW_dof" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_SUNW_dof; + shp->sh_offset = off; + shp->sh_size = dof->dofh_filesz; + shp->sh_addralign = 8; + off = shp->sh_offset + shp->sh_size; + + shp = &elf_file.shdr[ESHDR_STRTAB]; + shp->sh_name = 21; /* DTRACE_SHSTRTAB64[21] = ".strtab" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_STRTAB; + shp->sh_offset = off; + shp->sh_size = de.de_strlen; + shp->sh_addralign = sizeof (char); + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); + + shp = &elf_file.shdr[ESHDR_SYMTAB]; + shp->sh_name = 29; /* DTRACE_SHSTRTAB64[29] = ".symtab" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_SYMTAB; + shp->sh_entsize = sizeof (Elf64_Sym); + shp->sh_link = ESHDR_STRTAB; + shp->sh_offset = off; + shp->sh_info = de.de_global; + shp->sh_size = de.de_nsym * sizeof (Elf64_Sym); + shp->sh_addralign = 8; + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); + + if (de.de_nrel == 0) { + if (dt_write(dtp, fd, &elf_file, + sizeof (elf_file)) != sizeof (elf_file) || + PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || + PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || + PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || + PWRITE_SCN(ESHDR_DOF, dof)) { + ret = dt_set_errno(dtp, errno); + } + } else { + shp = &elf_file.shdr[ESHDR_REL]; + shp->sh_name = 37; /* DTRACE_SHSTRTAB64[37] = ".rel.SUNW_dof" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_RELA; + shp->sh_entsize = sizeof (de.de_rel[0]); + shp->sh_link = ESHDR_SYMTAB; + shp->sh_info = ESHDR_DOF; + shp->sh_offset = off; + shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); + shp->sh_addralign = 8; + + if (dt_write(dtp, fd, &elf_file, + sizeof (elf_file)) != sizeof (elf_file) || + PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || + PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || + PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || + PWRITE_SCN(ESHDR_REL, de.de_rel) || + PWRITE_SCN(ESHDR_DOF, dof)) { + ret = dt_set_errno(dtp, errno); + } + } + + free(de.de_strtab); + free(de.de_sym); + free(de.de_rel); + + return (ret); +} + +static int +dt_symtab_lookup(Elf_Data *data_sym, int nsym, uintptr_t addr, uint_t shn, + GElf_Sym *sym) +{ + int i, ret = -1; + GElf_Sym s; + + for (i = 0; i < nsym && gelf_getsym(data_sym, i, sym) != NULL; i++) { + if (GELF_ST_TYPE(sym->st_info) == STT_FUNC && + shn == sym->st_shndx && + sym->st_value <= addr && + addr < sym->st_value + sym->st_size) { + if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL) + return (0); + + ret = 0; + s = *sym; + } + } + + if (ret == 0) + *sym = s; + return (ret); +} + +#if defined(__sparc) + +#define DT_OP_RET 0x81c7e008 +#define DT_OP_NOP 0x01000000 +#define DT_OP_CALL 0x40000000 +#define DT_OP_CLR_O0 0x90102000 + +#define DT_IS_MOV_O7(inst) (((inst) & 0xffffe000) == 0x9e100000) +#define DT_IS_RESTORE(inst) (((inst) & 0xc1f80000) == 0x81e80000) +#define DT_IS_RETL(inst) (((inst) & 0xfff83fff) == 0x81c02008) + +#define DT_RS2(inst) ((inst) & 0x1f) +#define DT_MAKE_RETL(reg) (0x81c02008 | ((reg) << 14)) + +/*ARGSUSED*/ +static int +dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, + uint32_t *off) +{ + uint32_t *ip; + + if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0) + return (-1); + + /*LINTED*/ + ip = (uint32_t *)(p + rela->r_offset); + + /* + * We only know about some specific relocation types. + */ + if (GELF_R_TYPE(rela->r_info) != R_SPARC_WDISP30 && + GELF_R_TYPE(rela->r_info) != R_SPARC_WPLT30) + return (-1); + + /* + * We may have already processed this object file in an earlier linker + * invocation. Check to see if the present instruction sequence matches + * the one we would install below. + */ + if (isenabled) { + if (ip[0] == DT_OP_NOP) { + (*off) += sizeof (ip[0]); + return (0); + } + } else { + if (DT_IS_RESTORE(ip[1])) { + if (ip[0] == DT_OP_RET) { + (*off) += sizeof (ip[0]); + return (0); + } + } else if (DT_IS_MOV_O7(ip[1])) { + if (DT_IS_RETL(ip[0])) + return (0); + } else { + if (ip[0] == DT_OP_NOP) { + (*off) += sizeof (ip[0]); + return (0); + } + } + } + + /* + * We only expect call instructions with a displacement of 0. + */ + if (ip[0] != DT_OP_CALL) { + dt_dprintf("found %x instead of a call instruction at %llx\n", + ip[0], (u_longlong_t)rela->r_offset); + return (-1); + } + + if (isenabled) { + /* + * It would necessarily indicate incorrect usage if an is- + * enabled probe were tail-called so flag that as an error. + * It's also potentially (very) tricky to handle gracefully, + * but could be done if this were a desired use scenario. + */ + if (DT_IS_RESTORE(ip[1]) || DT_IS_MOV_O7(ip[1])) { + dt_dprintf("tail call to is-enabled probe at %llx\n", + (u_longlong_t)rela->r_offset); + return (-1); + } + + + /* + * On SPARC, we take advantage of the fact that the first + * argument shares the same register as for the return value. + * The macro handles the work of zeroing that register so we + * don't need to do anything special here. We instrument the + * instruction in the delay slot as we'll need to modify the + * return register after that instruction has been emulated. + */ + ip[0] = DT_OP_NOP; + (*off) += sizeof (ip[0]); + } else { + /* + * If the call is followed by a restore, it's a tail call so + * change the call to a ret. If the call if followed by a mov + * of a register into %o7, it's a tail call in leaf context + * so change the call to a retl-like instruction that returns + * to that register value + 8 (rather than the typical %o7 + + * 8); the delay slot instruction is left, but should have no + * effect. Otherwise we change the call to be a nop. We + * identify the subsequent instruction as the probe point in + * all but the leaf tail-call case to ensure that arguments to + * the probe are complete and consistent. An astute, though + * largely hypothetical, observer would note that there is the + * possibility of a false-positive probe firing if the function + * contained a branch to the instruction in the delay slot of + * the call. Fixing this would require significant in-kernel + * modifications, and isn't worth doing until we see it in the + * wild. + */ + if (DT_IS_RESTORE(ip[1])) { + ip[0] = DT_OP_RET; + (*off) += sizeof (ip[0]); + } else if (DT_IS_MOV_O7(ip[1])) { + ip[0] = DT_MAKE_RETL(DT_RS2(ip[1])); + } else { + ip[0] = DT_OP_NOP; + (*off) += sizeof (ip[0]); + } + } + + return (0); +} + +#elif defined(__i386) || defined(__amd64) + +#define DT_OP_NOP 0x90 +#define DT_OP_RET 0xc3 +#define DT_OP_CALL 0xe8 +#define DT_OP_JMP32 0xe9 +#define DT_OP_REX_RAX 0x48 +#define DT_OP_XOR_EAX_0 0x33 +#define DT_OP_XOR_EAX_1 0xc0 + +static int +dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, + uint32_t *off) +{ + uint8_t *ip = (uint8_t *)(p + rela->r_offset - 1); + uint8_t ret; + + /* + * On x86, the first byte of the instruction is the call opcode and + * the next four bytes are the 32-bit address; the relocation is for + * the address operand. We back up the offset to the first byte of + * the instruction. For is-enabled probes, we later advance the offset + * so that it hits the first nop in the instruction sequence. + */ + (*off) -= 1; + + /* + * We only know about some specific relocation types. Luckily + * these types have the same values on both 32-bit and 64-bit + * x86 architectures. + */ + if (GELF_R_TYPE(rela->r_info) != R_386_PC32 && + GELF_R_TYPE(rela->r_info) != R_386_PLT32) + return (-1); + + /* + * We may have already processed this object file in an earlier linker + * invocation. Check to see if the present instruction sequence matches + * the one we would install. For is-enabled probes, we advance the + * offset to the first nop instruction in the sequence to match the + * text modification code below. + */ + if (!isenabled) { + if ((ip[0] == DT_OP_NOP || ip[0] == DT_OP_RET) && + ip[1] == DT_OP_NOP && ip[2] == DT_OP_NOP && + ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) + return (0); + } else if (dtp->dt_oflags & DTRACE_O_LP64) { + if (ip[0] == DT_OP_REX_RAX && + ip[1] == DT_OP_XOR_EAX_0 && ip[2] == DT_OP_XOR_EAX_1 && + (ip[3] == DT_OP_NOP || ip[3] == DT_OP_RET) && + ip[4] == DT_OP_NOP) { + (*off) += 3; + return (0); + } + } else { + if (ip[0] == DT_OP_XOR_EAX_0 && ip[1] == DT_OP_XOR_EAX_1 && + (ip[2] == DT_OP_NOP || ip[2] == DT_OP_RET) && + ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) { + (*off) += 2; + return (0); + } + } + + /* + * We expect either a call instrution with a 32-bit displacement or a + * jmp instruction with a 32-bit displacement acting as a tail-call. + */ + if (ip[0] != DT_OP_CALL && ip[0] != DT_OP_JMP32) { + dt_dprintf("found %x instead of a call or jmp instruction at " + "%llx\n", ip[0], (u_longlong_t)rela->r_offset); + return (-1); + } + + ret = (ip[0] == DT_OP_JMP32) ? DT_OP_RET : DT_OP_NOP; + + /* + * Establish the instruction sequence -- all nops for probes, and an + * instruction to clear the return value register (%eax/%rax) followed + * by nops for is-enabled probes. For is-enabled probes, we advance + * the offset to the first nop. This isn't stricly necessary but makes + * for more readable disassembly when the probe is enabled. + */ + if (!isenabled) { + ip[0] = ret; + ip[1] = DT_OP_NOP; + ip[2] = DT_OP_NOP; + ip[3] = DT_OP_NOP; + ip[4] = DT_OP_NOP; + } else if (dtp->dt_oflags & DTRACE_O_LP64) { + ip[0] = DT_OP_REX_RAX; + ip[1] = DT_OP_XOR_EAX_0; + ip[2] = DT_OP_XOR_EAX_1; + ip[3] = ret; + ip[4] = DT_OP_NOP; + (*off) += 3; + } else { + ip[0] = DT_OP_XOR_EAX_0; + ip[1] = DT_OP_XOR_EAX_1; + ip[2] = ret; + ip[3] = DT_OP_NOP; + ip[4] = DT_OP_NOP; + (*off) += 2; + } + + return (0); +} + +#else +#error unknown ISA +#endif + +/*PRINTFLIKE5*/ +static int +dt_link_error(dtrace_hdl_t *dtp, Elf *elf, int fd, dt_link_pair_t *bufs, + const char *format, ...) +{ + va_list ap; + dt_link_pair_t *pair; + + va_start(ap, format); + dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap); + va_end(ap); + + if (elf != NULL) + (void) elf_end(elf); + + if (fd >= 0) + (void) close(fd); + + while ((pair = bufs) != NULL) { + bufs = pair->dlp_next; + dt_free(dtp, pair->dlp_str); + dt_free(dtp, pair->dlp_sym); + dt_free(dtp, pair); + } + + return (dt_set_errno(dtp, EDT_COMPILER)); +} + +static int +process_obj(dtrace_hdl_t *dtp, const char *obj, int *eprobesp) +{ + static const char dt_prefix[] = "__dtrace"; + static const char dt_enabled[] = "enabled"; + static const char dt_symprefix[] = "$dtrace"; + static const char dt_symfmt[] = "%s%d.%s"; + int fd, i, ndx, eprobe, mod = 0; + Elf *elf = NULL; + GElf_Ehdr ehdr; + Elf_Scn *scn_rel, *scn_sym, *scn_str, *scn_tgt; + Elf_Data *data_rel, *data_sym, *data_str, *data_tgt; + GElf_Shdr shdr_rel, shdr_sym, shdr_str, shdr_tgt; + GElf_Sym rsym, fsym, dsym; + GElf_Rela rela; + char *s, *p, *r; + char pname[DTRACE_PROVNAMELEN]; + dt_provider_t *pvp; + dt_probe_t *prp; + uint32_t off, eclass, emachine1, emachine2; + size_t symsize, nsym, isym, istr, len; + key_t objkey; + dt_link_pair_t *pair, *bufs = NULL; + dt_strtab_t *strtab; + + if ((fd = open64(obj, O_RDWR)) == -1) { + return (dt_link_error(dtp, elf, fd, bufs, + "failed to open %s: %s", obj, strerror(errno))); + } + + if ((elf = elf_begin(fd, ELF_C_RDWR, NULL)) == NULL) { + return (dt_link_error(dtp, elf, fd, bufs, + "failed to process %s: %s", obj, elf_errmsg(elf_errno()))); + } + + switch (elf_kind(elf)) { + case ELF_K_ELF: + break; + case ELF_K_AR: + return (dt_link_error(dtp, elf, fd, bufs, "archives are not " + "permitted; use the contents of the archive instead: %s", + obj)); + default: + return (dt_link_error(dtp, elf, fd, bufs, + "invalid file type: %s", obj)); + } + + if (gelf_getehdr(elf, &ehdr) == NULL) { + return (dt_link_error(dtp, elf, fd, bufs, "corrupt file: %s", + obj)); + } + + if (dtp->dt_oflags & DTRACE_O_LP64) { + eclass = ELFCLASS64; +#if defined(__sparc) + emachine1 = emachine2 = EM_SPARCV9; +#elif defined(__i386) || defined(__amd64) + emachine1 = emachine2 = EM_AMD64; +#endif + symsize = sizeof (Elf64_Sym); + } else { + eclass = ELFCLASS32; +#if defined(__sparc) + emachine1 = EM_SPARC; + emachine2 = EM_SPARC32PLUS; +#elif defined(__i386) || defined(__amd64) + emachine1 = emachine2 = EM_386; +#endif + symsize = sizeof (Elf32_Sym); + } + + if (ehdr.e_ident[EI_CLASS] != eclass) { + return (dt_link_error(dtp, elf, fd, bufs, + "incorrect ELF class for object file: %s", obj)); + } + + if (ehdr.e_machine != emachine1 && ehdr.e_machine != emachine2) { + return (dt_link_error(dtp, elf, fd, bufs, + "incorrect ELF machine type for object file: %s", obj)); + } + + /* + * We use this token as a relatively unique handle for this file on the + * system in order to disambiguate potential conflicts between files of + * the same name which contain identially named local symbols. + */ + if ((objkey = ftok(obj, 0)) == (key_t)-1) { + return (dt_link_error(dtp, elf, fd, bufs, + "failed to generate unique key for object file: %s", obj)); + } + + scn_rel = NULL; + while ((scn_rel = elf_nextscn(elf, scn_rel)) != NULL) { + if (gelf_getshdr(scn_rel, &shdr_rel) == NULL) + goto err; + + /* + * Skip any non-relocation sections. + */ + if (shdr_rel.sh_type != SHT_RELA && shdr_rel.sh_type != SHT_REL) + continue; + + if ((data_rel = elf_getdata(scn_rel, NULL)) == NULL) + goto err; + + /* + * Grab the section, section header and section data for the + * symbol table that this relocation section references. + */ + if ((scn_sym = elf_getscn(elf, shdr_rel.sh_link)) == NULL || + gelf_getshdr(scn_sym, &shdr_sym) == NULL || + (data_sym = elf_getdata(scn_sym, NULL)) == NULL) + goto err; + + /* + * Ditto for that symbol table's string table. + */ + if ((scn_str = elf_getscn(elf, shdr_sym.sh_link)) == NULL || + gelf_getshdr(scn_str, &shdr_str) == NULL || + (data_str = elf_getdata(scn_str, NULL)) == NULL) + goto err; + + /* + * Grab the section, section header and section data for the + * target section for the relocations. For the relocations + * we're looking for -- this will typically be the text of the + * object file. + */ + if ((scn_tgt = elf_getscn(elf, shdr_rel.sh_info)) == NULL || + gelf_getshdr(scn_tgt, &shdr_tgt) == NULL || + (data_tgt = elf_getdata(scn_tgt, NULL)) == NULL) + goto err; + + /* + * We're looking for relocations to symbols matching this form: + * + * __dtrace[enabled]____ + * + * For the generated object, we need to record the location + * identified by the relocation, and create a new relocation + * in the generated object that will be resolved at link time + * to the location of the function in which the probe is + * embedded. In the target object, we change the matched symbol + * so that it will be ignored at link time, and we modify the + * target (text) section to replace the call instruction with + * one or more nops. + * + * If the function containing the probe is locally scoped + * (static), we create an alias used by the relocation in the + * generated object. The alias, a new symbol, will be global + * (so that the relocation from the generated object can be + * resolved), and hidden (so that it is converted to a local + * symbol at link time). Such aliases have this form: + * + * $dtrace. + * + * We take a first pass through all the relocations to + * populate our string table and count the number of extra + * symbols we'll require. + */ + strtab = dt_strtab_create(1); + nsym = 0; + isym = data_sym->d_size / symsize; + istr = data_str->d_size; + + for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { + + if (shdr_rel.sh_type == SHT_RELA) { + if (gelf_getrela(data_rel, i, &rela) == NULL) + continue; + } else { + GElf_Rel rel; + if (gelf_getrel(data_rel, i, &rel) == NULL) + continue; + rela.r_offset = rel.r_offset; + rela.r_info = rel.r_info; + rela.r_addend = 0; + } + + if (gelf_getsym(data_sym, GELF_R_SYM(rela.r_info), + &rsym) == NULL) { + dt_strtab_destroy(strtab); + goto err; + } + + s = (char *)data_str->d_buf + rsym.st_name; + + if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) + continue; + + if (dt_symtab_lookup(data_sym, isym, rela.r_offset, + shdr_rel.sh_info, &fsym) != 0) { + dt_strtab_destroy(strtab); + goto err; + } + + if (GELF_ST_BIND(fsym.st_info) != STB_LOCAL) + continue; + + if (fsym.st_name > data_str->d_size) { + dt_strtab_destroy(strtab); + goto err; + } + + s = (char *)data_str->d_buf + fsym.st_name; + + /* + * If this symbol isn't of type function, we've really + * driven off the rails or the object file is corrupt. + */ + if (GELF_ST_TYPE(fsym.st_info) != STT_FUNC) { + dt_strtab_destroy(strtab); + return (dt_link_error(dtp, elf, fd, bufs, + "expected %s to be of type function", s)); + } + + len = snprintf(NULL, 0, dt_symfmt, dt_symprefix, + objkey, s) + 1; + if ((p = dt_alloc(dtp, len)) == NULL) { + dt_strtab_destroy(strtab); + goto err; + } + (void) snprintf(p, len, dt_symfmt, dt_symprefix, + objkey, s); + + if (dt_strtab_index(strtab, p) == -1) { + nsym++; + (void) dt_strtab_insert(strtab, p); + } + + dt_free(dtp, p); + } + + /* + * If needed, allocate the additional space for the symbol + * table and string table copying the old data into the new + * buffers, and marking the buffers as dirty. We inject those + * newly allocated buffers into the libelf data structures, but + * are still responsible for freeing them once we're done with + * the elf handle. + */ + if (nsym > 0) { + /* + * The first byte of the string table is reserved for + * the \0 entry. + */ + len = dt_strtab_size(strtab) - 1; + + assert(len > 0); + assert(dt_strtab_index(strtab, "") == 0); + + dt_strtab_destroy(strtab); + + if ((pair = dt_alloc(dtp, sizeof (*pair))) == NULL) + goto err; + + if ((pair->dlp_str = dt_alloc(dtp, data_str->d_size + + len)) == NULL) { + dt_free(dtp, pair); + goto err; + } + + if ((pair->dlp_sym = dt_alloc(dtp, data_sym->d_size + + nsym * symsize)) == NULL) { + dt_free(dtp, pair->dlp_str); + dt_free(dtp, pair); + goto err; + } + + pair->dlp_next = bufs; + bufs = pair; + + bcopy(data_str->d_buf, pair->dlp_str, data_str->d_size); + data_str->d_buf = pair->dlp_str; + data_str->d_size += len; + (void) elf_flagdata(data_str, ELF_C_SET, ELF_F_DIRTY); + + shdr_str.sh_size += len; + (void) gelf_update_shdr(scn_str, &shdr_str); + + bcopy(data_sym->d_buf, pair->dlp_sym, data_sym->d_size); + data_sym->d_buf = pair->dlp_sym; + data_sym->d_size += nsym * symsize; + (void) elf_flagdata(data_sym, ELF_C_SET, ELF_F_DIRTY); + + shdr_sym.sh_size += nsym * symsize; + (void) gelf_update_shdr(scn_sym, &shdr_sym); + + nsym += isym; + } else { + dt_strtab_destroy(strtab); + } + + /* + * Now that the tables have been allocated, perform the + * modifications described above. + */ + for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { + + if (shdr_rel.sh_type == SHT_RELA) { + if (gelf_getrela(data_rel, i, &rela) == NULL) + continue; + } else { + GElf_Rel rel; + if (gelf_getrel(data_rel, i, &rel) == NULL) + continue; + rela.r_offset = rel.r_offset; + rela.r_info = rel.r_info; + rela.r_addend = 0; + } + + ndx = GELF_R_SYM(rela.r_info); + + if (gelf_getsym(data_sym, ndx, &rsym) == NULL || + rsym.st_name > data_str->d_size) + goto err; + + s = (char *)data_str->d_buf + rsym.st_name; + + if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) + continue; + + s += sizeof (dt_prefix) - 1; + + /* + * Check to see if this is an 'is-enabled' check as + * opposed to a normal probe. + */ + if (strncmp(s, dt_enabled, + sizeof (dt_enabled) - 1) == 0) { + s += sizeof (dt_enabled) - 1; + eprobe = 1; + *eprobesp = 1; + dt_dprintf("is-enabled probe\n"); + } else { + eprobe = 0; + dt_dprintf("normal probe\n"); + } + + if (*s++ != '_') + goto err; + + if ((p = strstr(s, "___")) == NULL || + p - s >= sizeof (pname)) + goto err; + + bcopy(s, pname, p - s); + pname[p - s] = '\0'; + + p = strhyphenate(p + 3); /* strlen("___") */ + + if (dt_symtab_lookup(data_sym, isym, rela.r_offset, + shdr_rel.sh_info, &fsym) != 0) + goto err; + + if (fsym.st_name > data_str->d_size) + goto err; + + assert(GELF_ST_TYPE(fsym.st_info) == STT_FUNC); + + /* + * If a NULL relocation name is passed to + * dt_probe_define(), the function name is used for the + * relocation. The relocation needs to use a mangled + * name if the symbol is locally scoped; the function + * name may need to change if we've found the global + * alias for the locally scoped symbol (we prefer + * global symbols to locals in dt_symtab_lookup()). + */ + s = (char *)data_str->d_buf + fsym.st_name; + r = NULL; + + if (GELF_ST_BIND(fsym.st_info) == STB_LOCAL) { + dsym = fsym; + dsym.st_name = istr; + dsym.st_info = GELF_ST_INFO(STB_GLOBAL, + STT_FUNC); + dsym.st_other = + ELF64_ST_VISIBILITY(STV_ELIMINATE); + (void) gelf_update_sym(data_sym, isym, &dsym); + + r = (char *)data_str->d_buf + istr; + istr += 1 + sprintf(r, dt_symfmt, + dt_symprefix, objkey, s); + isym++; + assert(isym <= nsym); + + } else if (strncmp(s, dt_symprefix, + strlen(dt_symprefix)) == 0) { + r = s; + if ((s = strchr(s, '.')) == NULL) + goto err; + s++; + } + + if ((pvp = dt_provider_lookup(dtp, pname)) == NULL) { + return (dt_link_error(dtp, elf, fd, bufs, + "no such provider %s", pname)); + } + + if ((prp = dt_probe_lookup(pvp, p)) == NULL) { + return (dt_link_error(dtp, elf, fd, bufs, + "no such probe %s", p)); + } + + assert(fsym.st_value <= rela.r_offset); + + off = rela.r_offset - fsym.st_value; + if (dt_modtext(dtp, data_tgt->d_buf, eprobe, + &rela, &off) != 0) { + goto err; + } + + if (dt_probe_define(pvp, prp, s, r, off, eprobe) != 0) { + return (dt_link_error(dtp, elf, fd, bufs, + "failed to allocate space for probe")); + } + + mod = 1; + (void) elf_flagdata(data_tgt, ELF_C_SET, ELF_F_DIRTY); + + /* + * This symbol may already have been marked to + * be ignored by another relocation referencing + * the same symbol or if this object file has + * already been processed by an earlier link + * invocation. + */ + if (rsym.st_shndx != SHN_SUNW_IGNORE) { + rsym.st_shndx = SHN_SUNW_IGNORE; + (void) gelf_update_sym(data_sym, ndx, &rsym); + } + } + } + + if (mod && elf_update(elf, ELF_C_WRITE) == -1) + goto err; + + (void) elf_end(elf); + (void) close(fd); + + while ((pair = bufs) != NULL) { + bufs = pair->dlp_next; + dt_free(dtp, pair->dlp_str); + dt_free(dtp, pair->dlp_sym); + dt_free(dtp, pair); + } + + return (0); + +err: + return (dt_link_error(dtp, elf, fd, bufs, + "an error was encountered while processing %s", obj)); +} + +int +dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, + const char *file, int objc, char *const objv[]) +{ + char drti[PATH_MAX]; + dof_hdr_t *dof; + int fd, status, i, cur; + char *cmd, tmp; + size_t len; + int eprobes = 0, ret = 0; + + /* + * A NULL program indicates a special use in which we just link + * together a bunch of object files specified in objv and then + * unlink(2) those object files. + */ + if (pgp == NULL) { + const char *fmt = "%s -o %s -r"; + + len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file) + 1; + + for (i = 0; i < objc; i++) + len += strlen(objv[i]) + 1; + + cmd = alloca(len); + + cur = snprintf(cmd, len, fmt, dtp->dt_ld_path, file); + + for (i = 0; i < objc; i++) + cur += snprintf(cmd + cur, len - cur, " %s", objv[i]); + + if ((status = system(cmd)) == -1) { + return (dt_link_error(dtp, NULL, -1, NULL, + "failed to run %s: %s", dtp->dt_ld_path, + strerror(errno))); + } + + if (WIFSIGNALED(status)) { + return (dt_link_error(dtp, NULL, -1, NULL, + "failed to link %s: %s failed due to signal %d", + file, dtp->dt_ld_path, WTERMSIG(status))); + } + + if (WEXITSTATUS(status) != 0) { + return (dt_link_error(dtp, NULL, -1, NULL, + "failed to link %s: %s exited with status %d\n", + file, dtp->dt_ld_path, WEXITSTATUS(status))); + } + + for (i = 0; i < objc; i++) { + if (strcmp(objv[i], file) != 0) + (void) unlink(objv[i]); + } + + return (0); + } + + for (i = 0; i < objc; i++) { + if (process_obj(dtp, objv[i], &eprobes) != 0) + return (-1); /* errno is set for us */ + } + + /* + * If there are is-enabled probes then we need to force use of DOF + * version 2. + */ + if (eprobes && pgp->dp_dofversion < DOF_VERSION_2) + pgp->dp_dofversion = DOF_VERSION_2; + + if ((dof = dtrace_dof_create(dtp, pgp, dflags)) == NULL) + return (-1); /* errno is set for us */ + + /* + * Create a temporary file and then unlink it if we're going to + * combine it with drti.o later. We can still refer to it in child + * processes as /dev/fd/. + */ + if ((fd = open64(file, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { + return (dt_link_error(dtp, NULL, -1, NULL, + "failed to open %s: %s", file, strerror(errno))); + } + + /* + * If -xlinktype=DOF has been selected, just write out the DOF. + * Otherwise proceed to the default of generating and linking ELF. + */ + switch (dtp->dt_linktype) { + case DT_LTYP_DOF: + if (dt_write(dtp, fd, dof, dof->dofh_filesz) < dof->dofh_filesz) + ret = errno; + + if (close(fd) != 0 && ret == 0) + ret = errno; + + if (ret != 0) { + return (dt_link_error(dtp, NULL, -1, NULL, + "failed to write %s: %s", file, strerror(ret))); + } + + return (0); + + case DT_LTYP_ELF: + break; /* fall through to the rest of dtrace_program_link() */ + + default: + return (dt_link_error(dtp, NULL, -1, NULL, + "invalid link type %u\n", dtp->dt_linktype)); + } + + + if (!dtp->dt_lazyload) + (void) unlink(file); + + if (dtp->dt_oflags & DTRACE_O_LP64) + status = dump_elf64(dtp, dof, fd); + else + status = dump_elf32(dtp, dof, fd); + + if (status != 0 || lseek(fd, 0, SEEK_SET) != 0) { + return (dt_link_error(dtp, NULL, -1, NULL, + "failed to write %s: %s", file, strerror(errno))); + } + + if (!dtp->dt_lazyload) { + const char *fmt = "%s -o %s -r -Blocal -Breduce /dev/fd/%d %s"; + + if (dtp->dt_oflags & DTRACE_O_LP64) { + (void) snprintf(drti, sizeof (drti), + "%s/64/drti.o", _dtrace_libdir); + } else { + (void) snprintf(drti, sizeof (drti), + "%s/drti.o", _dtrace_libdir); + } + + len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file, fd, + drti) + 1; + + cmd = alloca(len); + + (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, fd, drti); + + if ((status = system(cmd)) == -1) { + ret = dt_link_error(dtp, NULL, -1, NULL, + "failed to run %s: %s", dtp->dt_ld_path, + strerror(errno)); + goto done; + } + + (void) close(fd); /* release temporary file */ + + if (WIFSIGNALED(status)) { + ret = dt_link_error(dtp, NULL, -1, NULL, + "failed to link %s: %s failed due to signal %d", + file, dtp->dt_ld_path, WTERMSIG(status)); + goto done; + } + + if (WEXITSTATUS(status) != 0) { + ret = dt_link_error(dtp, NULL, -1, NULL, + "failed to link %s: %s exited with status %d\n", + file, dtp->dt_ld_path, WEXITSTATUS(status)); + goto done; + } + } else { + (void) close(fd); + } + +done: + dtrace_dof_destroy(dtp, dof); + return (ret); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_list.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_list.c new file mode 100644 index 00000000..598433ed --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_list.c @@ -0,0 +1,117 @@ +/* + * 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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +/* + * Simple doubly-linked list implementation. This implementation assumes that + * each list element contains an embedded dt_list_t (previous and next + * pointers), which is typically the first member of the element struct. + * An additional dt_list_t is used to store the head (dl_next) and tail + * (dl_prev) pointers. The current head and tail list elements have their + * previous and next pointers set to NULL, respectively. + */ + +#ifndef VBOX +#include +#include +#else +# include "VBoxDTraceLibCWrappers.h" +#endif +#include + +void +dt_list_append(dt_list_t *dlp, void *new) +{ + dt_list_t *p = dlp->dl_prev; /* p = tail list element */ + dt_list_t *q = new; /* q = new list element */ + + dlp->dl_prev = q; + q->dl_prev = p; + q->dl_next = NULL; + + if (p != NULL) { + assert(p->dl_next == NULL); + p->dl_next = q; + } else { + assert(dlp->dl_next == NULL); + dlp->dl_next = q; + } +} + +void +dt_list_prepend(dt_list_t *dlp, void *new) +{ + dt_list_t *p = new; /* p = new list element */ + dt_list_t *q = dlp->dl_next; /* q = head list element */ + + dlp->dl_next = p; + p->dl_prev = NULL; + p->dl_next = q; + + if (q != NULL) { + assert(q->dl_prev == NULL); + q->dl_prev = p; + } else { + assert(dlp->dl_prev == NULL); + dlp->dl_prev = p; + } +} + +void +dt_list_insert(dt_list_t *dlp, void *after_me, void *new) +{ + dt_list_t *p = after_me; + dt_list_t *q = new; + + if (p == NULL || p->dl_next == NULL) { + dt_list_append(dlp, new); + return; + } + + q->dl_next = p->dl_next; + q->dl_prev = p; + p->dl_next = q; + q->dl_next->dl_prev = q; +} + +void +dt_list_delete(dt_list_t *dlp, void *existing) +{ + dt_list_t *p = existing; + + if (p->dl_prev != NULL) + p->dl_prev->dl_next = p->dl_next; + else + dlp->dl_next = p->dl_next; + + if (p->dl_next != NULL) + p->dl_next->dl_prev = p->dl_prev; + else + dlp->dl_prev = p->dl_prev; +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_list.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_list.h new file mode 100644 index 00000000..d13987dc --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_list.h @@ -0,0 +1,57 @@ +/* + * 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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_LIST_H +#define _DT_LIST_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#else +# include "VBoxDTraceTypes.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dt_list { + struct dt_list *dl_prev; + struct dt_list *dl_next; +} dt_list_t; + +#define dt_list_prev(elem) ((void *)(((dt_list_t *)(elem))->dl_prev)) +#define dt_list_next(elem) ((void *)(((dt_list_t *)(elem))->dl_next)) + +extern void dt_list_append(dt_list_t *, void *); +extern void dt_list_prepend(dt_list_t *, void *); +extern void dt_list_insert(dt_list_t *, void *, void *); +extern void dt_list_delete(dt_list_t *, void *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_LIST_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_map.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_map.c new file mode 100644 index 00000000..89921dfb --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_map.c @@ -0,0 +1,428 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#endif + +#include +#include + +static int +dt_epid_add(dtrace_hdl_t *dtp, dtrace_epid_t id) +{ + dtrace_id_t max; + int rval, i, maxformat; + dtrace_eprobedesc_t *enabled, *nenabled; + dtrace_probedesc_t *probe; + + while (id >= (max = VBDTCAST(dtrace_id_t)dtp->dt_maxprobe) || dtp->dt_pdesc == NULL) { + dtrace_id_t new_max = max ? (max << 1) : 1; + size_t nsize = new_max * sizeof (void *); + dtrace_probedesc_t **new_pdesc; + dtrace_eprobedesc_t **new_edesc; + + if ((new_pdesc = malloc(nsize)) == NULL || + (new_edesc = malloc(nsize)) == NULL) { + free(new_pdesc); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + bzero(new_pdesc, nsize); + bzero(new_edesc, nsize); + + if (dtp->dt_pdesc != NULL) { + size_t osize = max * sizeof (void *); + + bcopy(dtp->dt_pdesc, new_pdesc, osize); + free(dtp->dt_pdesc); + + bcopy(dtp->dt_edesc, new_edesc, osize); + free(dtp->dt_edesc); + } + + dtp->dt_pdesc = new_pdesc; + dtp->dt_edesc = new_edesc; + dtp->dt_maxprobe = new_max; + } + + if (dtp->dt_pdesc[id] != NULL) + return (0); + + if ((enabled = malloc(sizeof (dtrace_eprobedesc_t))) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + bzero(enabled, sizeof (dtrace_eprobedesc_t)); + enabled->dtepd_epid = id; + enabled->dtepd_nrecs = 1; + + if (dt_ioctl(dtp, DTRACEIOC_EPROBE, enabled) == -1) { + rval = dt_set_errno(dtp, errno); + free(enabled); + return (rval); + } + + if (DTRACE_SIZEOF_EPROBEDESC(enabled) != sizeof (*enabled)) { + /* + * There must be more than one action. Allocate the + * appropriate amount of space and try again. + */ + if ((nenabled = + malloc(DTRACE_SIZEOF_EPROBEDESC(enabled))) != NULL) + bcopy(enabled, nenabled, sizeof (*enabled)); + + free(enabled); + + if ((enabled = nenabled) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + rval = dt_ioctl(dtp, DTRACEIOC_EPROBE, enabled); + + if (rval == -1) { + rval = dt_set_errno(dtp, errno); + free(enabled); + return (rval); + } + } + + if ((probe = malloc(sizeof (dtrace_probedesc_t))) == NULL) { + free(enabled); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + probe->dtpd_id = enabled->dtepd_probeid; + + if (dt_ioctl(dtp, DTRACEIOC_PROBES, probe) == -1) { + rval = dt_set_errno(dtp, errno); + goto err; + } + + for (i = 0; i < enabled->dtepd_nrecs; i++) { + dtrace_fmtdesc_t fmt; + dtrace_recdesc_t *rec = &enabled->dtepd_rec[i]; + + if (!DTRACEACT_ISPRINTFLIKE(rec->dtrd_action)) + continue; + + if (rec->dtrd_format == 0) + continue; + + if (rec->dtrd_format <= dtp->dt_maxformat && + dtp->dt_formats[rec->dtrd_format - 1] != NULL) + continue; + + bzero(&fmt, sizeof (fmt)); + fmt.dtfd_format = rec->dtrd_format; + fmt.dtfd_string = NULL; + fmt.dtfd_length = 0; + + if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) { + rval = dt_set_errno(dtp, errno); + goto err; + } + + if ((fmt.dtfd_string = malloc(fmt.dtfd_length)) == NULL) { + rval = dt_set_errno(dtp, EDT_NOMEM); + goto err; + } + + if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) { + rval = dt_set_errno(dtp, errno); + free(fmt.dtfd_string); + goto err; + } + + while (rec->dtrd_format > (maxformat = dtp->dt_maxformat)) { + int new_max = maxformat ? (maxformat << 1) : 1; + size_t nsize = new_max * sizeof (void *); + size_t osize = maxformat * sizeof (void *); + void **new_formats = malloc(nsize); + + if (new_formats == NULL) { + rval = dt_set_errno(dtp, EDT_NOMEM); + free(fmt.dtfd_string); + goto err; + } + + bzero(new_formats, nsize); + bcopy(dtp->dt_formats, new_formats, osize); + free(dtp->dt_formats); + + dtp->dt_formats = new_formats; + dtp->dt_maxformat = new_max; + } + + dtp->dt_formats[rec->dtrd_format - 1] = + rec->dtrd_action == DTRACEACT_PRINTA ? + dtrace_printa_create(dtp, fmt.dtfd_string) : + dtrace_printf_create(dtp, fmt.dtfd_string); + + free(fmt.dtfd_string); + + if (dtp->dt_formats[rec->dtrd_format - 1] == NULL) { + rval = -1; /* dt_errno is set for us */ + goto err; + } + } + + dtp->dt_pdesc[id] = probe; + dtp->dt_edesc[id] = enabled; + + return (0); + +err: + /* + * If we failed, free our allocated probes. Note that if we failed + * while allocating formats, we aren't going to free formats that + * we have already allocated. This is okay; these formats are + * hanging off of dt_formats and will therefore not be leaked. + */ + free(enabled); + free(probe); + return (rval); +} + +int +dt_epid_lookup(dtrace_hdl_t *dtp, dtrace_epid_t epid, + dtrace_eprobedesc_t **epdp, dtrace_probedesc_t **pdp) +{ + int rval; + + if (epid >= dtp->dt_maxprobe || dtp->dt_pdesc[epid] == NULL) { + if ((rval = dt_epid_add(dtp, epid)) != 0) + return (rval); + } + + assert(epid < dtp->dt_maxprobe); + assert(dtp->dt_edesc[epid] != NULL); + assert(dtp->dt_pdesc[epid] != NULL); + *epdp = dtp->dt_edesc[epid]; + *pdp = dtp->dt_pdesc[epid]; + + return (0); +} + +void +dt_epid_destroy(dtrace_hdl_t *dtp) +{ + size_t i; + + assert((dtp->dt_pdesc != NULL && dtp->dt_edesc != NULL && + dtp->dt_maxprobe > 0) || (dtp->dt_pdesc == NULL && + dtp->dt_edesc == NULL && dtp->dt_maxprobe == 0)); + + if (dtp->dt_pdesc == NULL) + return; + + for (i = 0; i < dtp->dt_maxprobe; i++) { + if (dtp->dt_edesc[i] == NULL) { + assert(dtp->dt_pdesc[i] == NULL); + continue; + } + + assert(dtp->dt_pdesc[i] != NULL); + free(dtp->dt_edesc[i]); + free(dtp->dt_pdesc[i]); + } + + free(dtp->dt_pdesc); + dtp->dt_pdesc = NULL; + + free(dtp->dt_edesc); + dtp->dt_edesc = NULL; + dtp->dt_maxprobe = 0; +} + +void * +dt_format_lookup(dtrace_hdl_t *dtp, int format) +{ + if (format == 0 || format > dtp->dt_maxformat) + return (NULL); + + if (dtp->dt_formats == NULL) + return (NULL); + + return (dtp->dt_formats[format - 1]); +} + +void +dt_format_destroy(dtrace_hdl_t *dtp) +{ + int i; + + for (i = 0; i < dtp->dt_maxformat; i++) { + if (dtp->dt_formats[i] != NULL) + dt_printf_destroy(dtp->dt_formats[i]); + } + + free(dtp->dt_formats); + dtp->dt_formats = NULL; +} + +static int +dt_aggid_add(dtrace_hdl_t *dtp, dtrace_aggid_t id) +{ + dtrace_id_t max; + dtrace_epid_t epid; + int rval; + + while (id >= (max = VBDTCAST(dtrace_id_t)dtp->dt_maxagg) || dtp->dt_aggdesc == NULL) { + dtrace_id_t new_max = max ? (max << 1) : 1; + size_t nsize = new_max * sizeof (void *); + dtrace_aggdesc_t **new_aggdesc; + + if ((new_aggdesc = malloc(nsize)) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + bzero(new_aggdesc, nsize); + + if (dtp->dt_aggdesc != NULL) { + bcopy(dtp->dt_aggdesc, new_aggdesc, + max * sizeof (void *)); + free(dtp->dt_aggdesc); + } + + dtp->dt_aggdesc = new_aggdesc; + dtp->dt_maxagg = new_max; + } + + if (dtp->dt_aggdesc[id] == NULL) { + dtrace_aggdesc_t *agg, *nagg; + + if ((agg = malloc(sizeof (dtrace_aggdesc_t))) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + bzero(agg, sizeof (dtrace_aggdesc_t)); + agg->dtagd_id = id; + agg->dtagd_nrecs = 1; + + if (dt_ioctl(dtp, DTRACEIOC_AGGDESC, agg) == -1) { + rval = dt_set_errno(dtp, errno); + free(agg); + return (rval); + } + + if (DTRACE_SIZEOF_AGGDESC(agg) != sizeof (*agg)) { + /* + * There must be more than one action. Allocate the + * appropriate amount of space and try again. + */ + if ((nagg = malloc(DTRACE_SIZEOF_AGGDESC(agg))) != NULL) + bcopy(agg, nagg, sizeof (*agg)); + + free(agg); + + if ((agg = nagg) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + rval = dt_ioctl(dtp, DTRACEIOC_AGGDESC, agg); + + if (rval == -1) { + rval = dt_set_errno(dtp, errno); + free(agg); + return (rval); + } + } + + /* + * If we have a uarg, it's a pointer to the compiler-generated + * statement; we'll use this value to get the name and + * compiler-generated variable ID for the aggregation. If + * we're grabbing an anonymous enabling, this pointer value + * is obviously meaningless -- and in this case, we can't + * provide the compiler-generated aggregation information. + */ + if (dtp->dt_options[DTRACEOPT_GRABANON] == DTRACEOPT_UNSET && + agg->dtagd_rec[0].dtrd_uarg != 0 /*NULL*/) { + dtrace_stmtdesc_t *sdp; + dt_ident_t *aid; + + sdp = (dtrace_stmtdesc_t *)(uintptr_t) + agg->dtagd_rec[0].dtrd_uarg; + aid = sdp->dtsd_aggdata; + agg->dtagd_name = aid->di_name; + agg->dtagd_varid = aid->di_id; + } else { + agg->dtagd_varid = DTRACE_AGGVARIDNONE; + } + + if ((epid = agg->dtagd_epid) >= dtp->dt_maxprobe || + dtp->dt_pdesc[epid] == NULL) { + if ((rval = dt_epid_add(dtp, epid)) != 0) { + free(agg); + return (rval); + } + } + + dtp->dt_aggdesc[id] = agg; + } + + return (0); +} + +int +dt_aggid_lookup(dtrace_hdl_t *dtp, dtrace_aggid_t aggid, + dtrace_aggdesc_t **adp) +{ + int rval; + + if (aggid >= dtp->dt_maxagg || dtp->dt_aggdesc[aggid] == NULL) { + if ((rval = dt_aggid_add(dtp, aggid)) != 0) + return (rval); + } + + assert(aggid < dtp->dt_maxagg); + assert(dtp->dt_aggdesc[aggid] != NULL); + *adp = dtp->dt_aggdesc[aggid]; + + return (0); +} + +void +dt_aggid_destroy(dtrace_hdl_t *dtp) +{ + size_t i; + + assert((dtp->dt_aggdesc != NULL && dtp->dt_maxagg != 0) || + (dtp->dt_aggdesc == NULL && dtp->dt_maxagg == 0)); + + if (dtp->dt_aggdesc == NULL) + return; + + for (i = 0; i < dtp->dt_maxagg; i++) { + if (dtp->dt_aggdesc[i] != NULL) + free(dtp->dt_aggdesc[i]); + } + + free(dtp->dt_aggdesc); + dtp->dt_aggdesc = NULL; + dtp->dt_maxagg = 0; +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_module.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_module.c new file mode 100644 index 00000000..8cafc7a8 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_module.c @@ -0,0 +1,1348 @@ +/* + * 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. + */ + +#ifndef VBOX +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# include +# ifdef _MSC_VER +# include +# define getuid() 0 +# define getgid() 0 +# define geteuid() 0 +# define getegid() 0 +# define getpid() _getpid() +# define getppid() 0 + +# define getpgid(a) 0 +# define getsid(a) 0 +# else +# include +# endif +# define getprojid() 0 +# define gettaskid() 0 + +#endif /* VBOX */ + +#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 + + const 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; + + 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 */ + + 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 + + const 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; + + 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 */ + + 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) +{ + 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; + + 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) +{ + RT_NOREF1(dtp); + return (ctfp ? ctf_getspecific(ctfp) : NULL); +} + +static int +dt_module_load_sect(dtrace_hdl_t *dtp, dt_module_t *dmp, ctf_sect_t *ctsp) +{ +#ifndef VBOX /** @todo consider this later */ + 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); + + ctsp->cts_data = dp->d_buf; + 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); + +#else /* VBOX */ + RT_NOREF3(dtp, dmp, ctsp); +#endif /* VBOX */ + 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 */ + + 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 = + VBDTCAST(uint_t)(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); +} + +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 != (uint_t)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) +{ + RT_NOREF1(dtp); + ctf_close(dmp->dm_ctfp); + dmp->dm_ctfp = NULL; + + 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; + } + + 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/*NULL*/; + dmp->dm_text_size = 0; + dmp->dm_data_va = 0/*NULL*/; + dmp->dm_data_size = 0; + dmp->dm_bss_va = 0/*NULL*/; + dmp->dm_bss_size = 0; + + if (dmp->dm_extern != NULL) { + dt_idhash_destroy(dmp->dm_extern); + dmp->dm_extern = NULL; + } + +#ifndef VBOX + (void) elf_end(dmp->dm_elf); + dmp->dm_elf = NULL; +#endif + + 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"); +} + +#ifndef VBOX +/* + * 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//. + */ +static void +dt_module_update(dtrace_hdl_t *dtp, const char *name) +{ + 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; + + (void) snprintf(fname, sizeof (fname), + "%s/%s/object", OBJFS_ROOT, name); + + 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; + } + + /* + * 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 (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; + dmp->dm_modid = (int)OBJFS_MODID(st.st_ino); + + 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); +} +#endif /* !VBOX */ + +/* + * 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) +{ +#ifndef VBOX + dt_module_t *dmp; + DIR *dirp; + + for (dmp = dt_list_next(&dtp->dt_modlist); + dmp != NULL; dmp = dt_list_next(dmp)) + dt_module_unload(dtp, dmp); + + /* + * 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); + } +#endif /* !VBOX */ + + /* + * 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(); + dt_idhash_lookup(dtp->dt_macros, "projid")->di_id = getprojid(); + dt_idhash_lookup(dtp->dt_macros, "sid")->di_id = getsid(0); + dt_idhash_lookup(dtp->dt_macros, "taskid")->di_id = gettaskid(); + 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 ((intptr_t)object) { + case DTRACE_OBJ_INT_EXEC: + dmp = dtp->dt_exec; + break; + case DTRACE_OBJ_INT_RTLD: + dmp = dtp->dt_rtld; + break; + case DTRACE_OBJ_INT_CDEFS: + dmp = dtp->dt_cdefs; + break; + case DTRACE_OBJ_INT_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 VBDTMSC(0); + 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; + int justone; + + 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 (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) { + tip->dtt_object = dmp->dm_name; + tip->dtt_ctfp = dmp->dm_ctfp; + tip->dtt_type = id; + + if (ctf_type_kind(dmp->dm_ctfp, ctf_type_resolve( + dmp->dm_ctfp, 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; + + 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); +} + diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_module.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_module.h new file mode 100644 index 00000000..bf6ceded --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_module.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef _DT_MODULE_H +#define _DT_MODULE_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#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 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 *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_MODULE_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_open.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_open.c new file mode 100644 index 00000000..88db7a9b --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_open.c @@ -0,0 +1,1500 @@ +/* + * 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. + */ + +#ifndef VBOX +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _MSC_VER +# define _POSIX_PTHREAD_SEMANTICS +# include +# undef _POSIX_PTHREAD_SEMANTICS +#endif + +#else /* VBOX */ +# ifndef _MSC_VER +# include +# include +# else +# include +# endif +# include +# include +# include +# include +# include +# include "../../../../../Main/include/ExtPackUtil.h" +#endif /* VBOX */ + +#include +#include +#include +#include +#include +#include + +/* + * 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 Solaris. 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_LATEST DT_VERS_1_6_3 +#define DT_VERS_STRING "Sun D 1.6.3" + +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 */ + 0 +}; + +/* + * 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, + &dt_idops_type, "genunix`kthread_t *" }, +{ "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" }, +{ "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)" }, +{ "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, + DT_VERS_1_5, &dt_idops_func, "string(ipaddr_t *)" }, +{ "inet_ntoa6", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA6, DT_ATTR_STABCMN, + DT_VERS_1_5, &dt_idops_func, "string(in6_addr_t *)" }, +{ "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" }, +{ "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)" }, +{ "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(@)" }, +{ "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 *)" }, +{ "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 *)" }, +{ "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" }, +{ "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(@, ...)" }, +{ "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])" }, +{ "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 *)" }, +{ "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 *)" }, +{ "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(@)" }, +{ "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" }, +{ "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(...)" }, +{ "uaddr", DT_IDENT_ACTFUNC, 0, DT_ACT_UADDR, DT_ATTR_STABCMN, + DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, +{ "ucaller", DT_IDENT_SCALAR, 0, DIF_VAR_UCALLER, DT_ATTR_STABCMN, + DT_VERS_1_2, &dt_idops_type, "uint64_t" }, +{ "ufunc", DT_IDENT_ACTFUNC, 0, DT_ACT_USYM, DT_ATTR_STABCMN, + DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, +{ "uid", DT_IDENT_SCALAR, 0, DIF_VAR_UID, DT_ATTR_STABCMN, DT_VERS_1_0, + &dt_idops_type, "uid_t" }, +{ "umod", DT_IDENT_ACTFUNC, 0, DT_ACT_UMOD, DT_ATTR_STABCMN, + DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, +{ "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" }, +{ "usym", DT_IDENT_ACTFUNC, 0, DT_ACT_USYM, DT_ATTR_STABCMN, + DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" }, +{ "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" }, +{ "zonename", DT_IDENT_SCALAR, 0, DIF_VAR_ZONENAME, + DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, +{ 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 }, +}; + +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 */ + +const char *_dtrace_libdir = "/usr/lib/dtrace"; /* default library directory */ +const char *_dtrace_provdir = "/dev/dtrace/provider"; /* provider directory */ + +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 */ +#ifndef VBOX +int _dtrace_rdvers = RD_VERSION; /* rtld_db feature version */ +#endif + +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; + + +#ifdef VBOX +# include +# include +extern RTCRITSECT dt_qsort_lock; /* dt_aggregate.c */ + +void dtrace_init(void) +#else /* !VBOX */ +#pragma init(_dtrace_init) +void +_dtrace_init(void) +#endif /* !VBOX */ +{ + _dtrace_debug = getenv("DTRACE_DEBUG") != NULL; + +#ifndef VBOX + for (; _dtrace_rdvers > 0; _dtrace_rdvers--) { + if (rd_init(_dtrace_rdvers) == RD_OK) + break; + } +#else + + libctf_init(); + RTCritSectInit(&dt_qsort_lock); +#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); +} + +#ifndef VBOX +static void +dt_provmod_open(dt_provmod_t **provmod, dt_fdlist_t *dfp) +{ + dt_provmod_t *prov; + char path[PATH_MAX]; + struct dirent *dp, *ep; + DIR *dirp; + int fd; + + 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); +} +#endif /* !VBOX */ + +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; +} + +#ifndef VBOX +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 /* !VBOX */ + +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; +#ifndef _MSC_VER + struct rlimit rl; +#endif + + 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; + +#ifndef VBOX + dt_fdlist_t df = { NULL, 0, 0 }; +#endif + + char isadef[32], utsdef[32]; +#ifndef VBOX + char s1[64], s2[64]; +#endif + +#ifdef VBOX + char szModPath[RTPATH_MAX]; + void *pvImageBase; + int rc; +#endif + + 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)); + +#ifndef VBOX + if (elf_version(EV_CURRENT) == EV_NONE) + return (set_open_errno(dtp, errp, EDT_ELFVERSION)); +#endif + + if (vector != NULL || (flags & DTRACE_O_NODEV)) + goto alloc; /* do not attempt to open dtrace device */ + +#ifndef _MSC_VER + /* + * 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); + } +#endif + +#ifndef VBOX + /* + * 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 */ + + ftfd = open("/dev/dtrace/provider/fasttrap", O_RDWR); + 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); + +#else /* VBOX */ + + rc = SUPR3Init(NULL); + if (RT_FAILURE(rc)) { + switch (rc) { + case VERR_VM_DRIVER_LOAD_ERROR: + case VERR_VM_DRIVER_OPEN_ERROR: + err = EDT_BUSY; + break; + case VERR_VM_DRIVER_NOT_INSTALLED: + err = EDT_NOENT; + break; + case VERR_VERSION_MISMATCH: + case VERR_VM_DRIVER_VERSION_MISMATCH: + err = EDT_VERSION; + break; + case VERR_VM_DRIVER_NOT_ACCESSIBLE: + default: + err = EDT_ACCESS; + break; + } + RTStrmPrintf(g_pStdErr, "SUPR3Init: -> %Rrc\n", rc); + return (set_open_errno(dtp, errp, err)); + } + + rc = RTPathAppPrivateArch(szModPath, sizeof(szModPath)); + if (RT_SUCCESS(rc)) + rc = RTPathAppend(szModPath, sizeof(szModPath), + VBOX_EXTPACK_INSTALL_DIR RTPATH_SLASH_STR VBOX_EXTPACK_VBOXDTRACE_MANGLED_NAME); + if (RT_SUCCESS(rc)) + rc = RTPathAppend(szModPath, sizeof(szModPath), RTBldCfgTargetDotArch()); + if (RT_SUCCESS(rc)) + rc = RTPathAppend(szModPath, sizeof(szModPath), "VBoxDTraceR0.r0"); + if (RT_SUCCESS(rc)) { + PRTERRINFO pErrInfo = RTErrInfoAlloc(1024); + rc = SUPR3LoadModule(szModPath, "VBoxDTraceR0.r0", &pvImageBase, pErrInfo); + if (RT_FAILURE(rc)) { + RTStrmPrintf(g_pStdErr, "SUPR3LoadModule: %s -> %Rrc; %s\n", szModPath, rc, pErrInfo->pszMsg); + RTErrInfoFree(pErrInfo); + return (set_open_errno(dtp, errp, EDT_NOTLOADED)); + } + RTErrInfoFree(pErrInfo); + } + if (RT_FAILURE(rc)) { + RTStrmPrintf(g_pStdErr, "SUPR3LoadModule: path buffer too small (%Rrc)\n", rc); + return (set_open_errno(dtp, errp, EDT_BUFTOOSMALL)); + } + + rc = SUPR3TracerOpen(RT_MAKE_U32_FROM_U8('V', 'B', 'D', 'T'), 0); + if (RT_FAILURE(rc)) { + RTStrmPrintf(g_pStdErr, "SUPR3TracerOpen: %Rrc\n", rc); + return (set_open_errno(dtp, errp, EDT_ACCESS)); + } +#endif /* VBOX */ + +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; + dtp->dt_prcmode = DT_PROC_STOP_PREINIT; + 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_version = version; + dtp->dt_fd = dtfd; + dtp->dt_ftfd = ftfd; + dtp->dt_fterr = fterr; + dtp->dt_cdefs_fd = -1; + dtp->dt_ddefs_fd = -1; + dtp->dt_stdout_fd = -1; + 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); +#ifndef VBOX + (void) uname(&dtp->dt_uts); +#endif + + 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] = (uint64_t)DTRACEOPT_UNSET; + + dtp->dt_cpp_argv[0] = (char *)strbasename(dtp->dt_cpp_path); + + (void) snprintf(isadef, sizeof (isadef), "-D__SUNW_D_%u", + (uint_t)(sizeof (void *) * NBBY)); + +#ifndef VBOX + (void) snprintf(utsdef, sizeof (utsdef), "-D__%s_%s", + dt_get_sysinfo(SI_SYSNAME, s1, sizeof (s1)), + dt_get_sysinfo(SI_RELEASE, s2, sizeof (s2))); +#endif + + 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)); + + 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 + +#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 + + 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)); + + 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); + +#ifndef VBOX + /* + * 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)); +#endif + + 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 (dtp->dt_stdout_fd != -1) + (void) close(dtp->dt_stdout_fd); + + dt_epid_destroy(dtp); + dt_aggid_destroy(dtp); + dt_format_destroy(dtp); + dt_buffered_destroy(dtp); + dt_aggregate_destroy(dtp); + free(dtp->dt_buf.dtbd_data); + 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); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_options.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_options.c new file mode 100644 index 00000000..b843cb82 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_options.c @@ -0,0 +1,1118 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#else /* VBOX */ +# ifndef _MSC_VER +# include +# include +# include +# if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) +# define open64 open +# endif +# else +# include +# define STDERR_FILENO 2 +# define open64 open +# endif +# include +# include +# include + +# if RT_GNUC_PREREQ(10, 0) +# pragma GCC diagnostic ignored "-Walloca-larger-than=" +# endif +#endif /* VBOX */ + +#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; + RT_NOREF1(option); + + 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"; + +#ifndef _MSC_VER + struct sigaction act; + struct rlimit lim; +#endif + + int res = write(STDERR_FILENO, msg, sizeof (msg) - 1); + NOREF(res); + +#ifndef _MSC_VER + 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); +#endif /* !_MSC_VER */ + abort(); +} + +/*ARGSUSED*/ +static int +dt_opt_core(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) +{ + static int enabled = 0; + RT_NOREF1(option); + + 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) +{ + RT_NOREF1(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; + RT_NOREF1(option); + + 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; + RT_NOREF1(option); + + 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) +{ + RT_NOREF2(option, arg); + + dtp->dt_droptags = 1; + return (0); +} + +/*ARGSUSED*/ +static int +dt_opt_dtypes(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) +{ + int fd; + RT_NOREF1(option); + + 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) +{ + RT_NOREF1(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; + RT_NOREF2(arg, option); + + 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) +{ + RT_NOREF2(arg, 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; + RT_NOREF1(option); + + 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; + RT_NOREF1(option); + + 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) +{ + RT_NOREF1(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) +{ + RT_NOREF1(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_evaltime(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) +{ + RT_NOREF1(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; + RT_NOREF1(option); + + if (arg == NULL || (n = atoi(arg)) < 0) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + + dtp->dt_procs->dph_lrulim = n; + return (0); +} + +/*ARGSUSED*/ +static int +dt_opt_stdc(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) +{ + RT_NOREF1(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; + RT_NOREF1(option); + + 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; + RT_NOREF1(option); + + 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; + RT_NOREF1(option); + + 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) +{ + RT_NOREF1(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; + RT_NOREF1(option); + + 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) +{ +#ifndef VBOX + char *end; +#endif + 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; + } + } + +#ifndef VBOX + errno = 0; + val = strtoull(arg, &end, 0); + + if (*end != '\0' || errno != 0 || val < 0) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); +#else + i = RTStrToInt64Full(arg, 0, &val); + if (i != VINF_SUCCESS) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); +#endif + } + +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; +#ifdef VBOX + int rc; +#endif + + len = strlen(arg); +#ifndef VBOX + errno = 0; +#endif + + switch (arg[len - 1]) { + case 't': + case 'T': + mul *= 1024; + RT_FALL_THRU(); + case 'g': + case 'G': + mul *= 1024; + RT_FALL_THRU(); + case 'm': + case 'M': + mul *= 1024; + RT_FALL_THRU(); + case 'k': + case 'K': + mul *= 1024; + RT_FALL_THRU(); + default: + break; + } + +#ifndef VBOX + 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); +#else + *rval = -1; + if (mul == 1) { + rc = RTStrToInt64Full(arg, 0, rval); + if (rc != VINF_SUCCESS || *rval < 0) + return (-1); + } else { + rc = RTStrToInt64Ex(arg, &end, 0, rval); + if ( rc != VWRN_TRAILING_CHARS + || end != &arg[len - 1] + || *rval < 0) + return (-1); + *rval *= mul; + if (*rval < 0) + return (-1); + } +#endif + + 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) { +#ifndef VBOX + errno = 0; + val = strtoull(arg, &end, 0); +#else + int rc = RTStrToInt64Ex(arg, &end, 0, &val); + if (rc != VWRN_TRAILING_CHARS) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); +#endif + + 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; + RT_NOREF1(option); + + 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; + RT_NOREF1(option); + + 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 VBDTMSC(NULL); + size_t offs; + VBDTTYPE(uint32_t,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 (dt_ioctl(dtp, DTRACEIOC_DOFGET, &hdr) == -1) + 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] = (uint64_t)DTRACEOPT_UNSET; + + if (dt_ioctl(dtp, DTRACEIOC_DOFGET, dof) == -1) + 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); +} + +/*ARGSUSED*/ +static int +dt_opt_preallocate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) +{ + dtrace_optval_t size; + void *p; + RT_NOREF1(option); + + if (arg == NULL || dt_optval_parse(arg, &size) != 0) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + + if (size > SIZE_MAX) + size = SIZE_MAX; + + if ((p = dt_zalloc(dtp, size)) == NULL) { + do { + size /= 2; + } while ((p = dt_zalloc(dtp, size)) == NULL); + } + + dt_free(dtp, p); + + 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 }, + { "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 }, + { "preallocate", dt_opt_preallocate }, + { "pspec", dt_opt_cflags, DTRACE_C_PSPEC }, + { "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 }, + { "verbose", dt_opt_cflags, DTRACE_C_DIFV }, + { "version", dt_opt_version }, + { "zdefs", dt_opt_cflags, DTRACE_C_ZDEFS }, + { NULL } +}; + +/* + * 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 }, + { NULL } +}; + +/* + * Dynamic run-time options. + */ +static const dt_option_t _dtrace_drtoptions[] = { + { "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 }, + { "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 } +}; + +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)); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_parser.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_parser.c new file mode 100644 index 00000000..7261b794 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_parser.c @@ -0,0 +1,4907 @@ +/* + * 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. + */ + +/* + * 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. + */ + +#ifndef VBOX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else /* VBOX */ +# include +# ifdef _MSC_VER +# pragma warning(disable:4146) /* -unsigned */ +# pragma warning(disable:4267) /* size_t to int */ +# endif +#endif /* VBOX */ + +#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; + + 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)); + + 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); + + 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; + + 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_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; + } + RT_FALL_THRU(); + 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) +{ + 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; + + 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) +{ + 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)); + + 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 VBDTMSC(0), rkind VBDTMSC(0); + 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); + + /* + * 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*/ +#ifndef _MSC_VER + return (NULL); /* keep gcc happy */ +#endif +} + +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)); + + 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); + + 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); + 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 */ + RT_FALL_THRU(); + 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; + +#ifndef VBOX + name = strdupa(s); +#else + MY_STRDUPA(name, s); +#endif + 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); + + 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); + } + RT_FALL_THRU(); + 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")); + + 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); +} + +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)); + 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)); + break; + case DT_TOK_LAND: + dnp->dn_value = l && r; + dt_node_type_assign(dnp, + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + 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)); + break; + case DT_TOK_NEQ: + dnp->dn_value = l != r; + dt_node_type_assign(dnp, + DT_INT_CTFP(dtp), DT_INT_TYPE(dtp)); + 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)); + 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)); + 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)); + 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)); + 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 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). + */ + if (op == DT_TOK_LPAR && rp->dn_kind == DT_NODE_INT && + dt_node_is_integer(lp)) { + size_t srcsize = dt_node_type_size(rp); + size_t dstsize = dt_node_type_size(lp); + + if ((dstsize < srcsize) || ((lp->dn_flags & DT_NF_SIGNED) ^ + (rp->dn_flags & DT_NF_SIGNED))) { + int n = dstsize < srcsize ? + (sizeof (uint64_t) * NBBY - dstsize * NBBY) : + (sizeof (uint64_t) * NBBY - srcsize * NBBY); + + rp->dn_value <<= n; + if (lp->dn_flags & DT_NF_SIGNED) + rp->dn_value = (intmax_t)rp->dn_value >> n; + else + rp->dn_value = rp->dn_value >> n; + } + + 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_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 VBDTMSC({NULL}); + 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); + + 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); + + bzero(&dn, sizeof (dn)); + dt_node_type_assign(&dn, dst.dtt_ctfp, dst.dtt_type); + + 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)); + 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_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)); + 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) +{ + RT_NOREF1(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); + } + + 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 can just set our output type to the base 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, + DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp)); + 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); + 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)); + 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; + 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")); + 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)); + 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 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_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_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_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_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_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: + 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)); + 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); + } + + RT_FALL_THRU(); + + 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_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_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)); + 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; + RT_NOREF1(idflags); + + 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_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; + RT_NOREF1(idflags); + + 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_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]; + RT_NOREF1(idflags); + + 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; + RT_NOREF1(idflags); + + /* + * 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); + 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_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; + RT_NOREF1(idflags); + + /* + * 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) +{ + RT_NOREF1(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_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*/ +DECL_NO_RETURN(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*/ +DECL_NO_RETURN(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*/ +DECL_NO_RETURN(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 */ +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_parser.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_parser.h new file mode 100644 index 00000000..cad26da9 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_parser.h @@ -0,0 +1,290 @@ +/* + * 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. + */ + +#ifndef _DT_PARSER_H +#define _DT_PARSER_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#include +#include +#include +#else /* VBOX */ +# include +# include +#endif /* VBOX */ + +#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_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 DECL_NO_RETURN(void) dnerror(const dt_node_t *, dt_errtag_t, const char *, ...); +extern void dnwarn(const dt_node_t *, dt_errtag_t, const char *, ...); + +extern DECL_NO_RETURN(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 DECL_NO_RETURN(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 */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.c new file mode 100644 index 00000000..05a99dd2 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.c @@ -0,0 +1,193 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +/* + * DTrace Parsing Control Block + * + * A DTrace Parsing Control Block (PCB) contains all of the state that is used + * by a single pass of the D compiler, other than the global variables used by + * lex and yacc. The routines in this file are used to set up and tear down + * PCBs, which are kept on a stack pointed to by the libdtrace global 'yypcb'. + * The main engine of the compiler, dt_compile(), is located in dt_cc.c and is + * responsible for calling these routines to begin and end a compilation pass. + * + * Sun's lex/yacc are not MT-safe or re-entrant, but we permit limited nested + * use of dt_compile() once the entire parse tree has been constructed but has + * not yet executed the "cooking" pass (see dt_cc.c for more information). The + * PCB design also makes it easier to debug (since all global state is kept in + * one place) and could permit us to make the D compiler MT-safe or re-entrant + * in the future by adding locks to libdtrace or switching to Flex and Bison. + */ + +#ifndef VBOX +#include +#include +#include +#endif + +#include +#include +#include +#include + +/* + * Initialize the specified PCB by zeroing it and filling in a few default + * members, and then pushing it on to the top of the PCB stack and setting + * yypcb to point to it. Increment the current handle's generation count. + */ +void +dt_pcb_push(dtrace_hdl_t *dtp, dt_pcb_t *pcb) +{ + /* + * Since lex/yacc are not re-entrant and we don't implement state save, + * assert that if another PCB is active, it is from the same handle and + * has completed execution of yyparse(). If the first assertion fires, + * the caller is calling libdtrace without proper MT locking. If the + * second assertion fires, dt_compile() is being called recursively + * from an illegal location in libdtrace, or a dt_pcb_pop() is missing. + */ + if (yypcb != NULL) { + assert(yypcb->pcb_hdl == dtp); + assert(yypcb->pcb_yystate == YYS_DONE); + } + + bzero(pcb, sizeof (dt_pcb_t)); + + dt_scope_create(&pcb->pcb_dstack); + dt_idstack_push(&pcb->pcb_globals, dtp->dt_globals); + dt_irlist_create(&pcb->pcb_ir); + + pcb->pcb_hdl = dtp; + pcb->pcb_prev = dtp->dt_pcb; + + dtp->dt_pcb = pcb; + dtp->dt_gen++; + +#ifndef USING_FLEX /* In case flex starts work too early. Moved to dt_compile. */ + yyinit(pcb); +#endif +} + +static int +dt_pcb_pop_ident(dt_idhash_t *dhp, dt_ident_t *idp, void *arg) +{ + dtrace_hdl_t *dtp = arg; + + if (idp->di_gen == dtp->dt_gen) + dt_idhash_delete(dhp, idp); + + return (0); +} + +/* + * Pop the topmost PCB from the PCB stack and destroy any data structures that + * are associated with it. If 'err' is non-zero, destroy any intermediate + * state that is left behind as part of a compilation that has failed. + */ +void +dt_pcb_pop(dtrace_hdl_t *dtp, int err) +{ + dt_pcb_t *pcb = yypcb; + uint_t i; + + assert(pcb != NULL); + assert(pcb == dtp->dt_pcb); + + while (pcb->pcb_dstack.ds_next != NULL) + (void) dt_scope_pop(); + + dt_scope_destroy(&pcb->pcb_dstack); + dt_irlist_destroy(&pcb->pcb_ir); + + dt_node_link_free(&pcb->pcb_list); + dt_node_link_free(&pcb->pcb_hold); + + if (err != 0) { + dt_xlator_t *dxp, *nxp; + dt_provider_t *pvp, *nvp; + + if (pcb->pcb_prog != NULL) + dt_program_destroy(dtp, pcb->pcb_prog); + if (pcb->pcb_stmt != NULL) + dtrace_stmt_destroy(dtp, pcb->pcb_stmt); + if (pcb->pcb_ecbdesc != NULL) + dt_ecbdesc_release(dtp, pcb->pcb_ecbdesc); + + for (dxp = dt_list_next(&dtp->dt_xlators); dxp; dxp = nxp) { + nxp = dt_list_next(dxp); + if (dxp->dx_gen == dtp->dt_gen) + dt_xlator_destroy(dtp, dxp); + } + + for (pvp = dt_list_next(&dtp->dt_provlist); pvp; pvp = nvp) { + nvp = dt_list_next(pvp); + if (pvp->pv_gen == dtp->dt_gen) + dt_provider_destroy(dtp, pvp); + } + + (void) dt_idhash_iter(dtp->dt_aggs, dt_pcb_pop_ident, dtp); + dt_idhash_update(dtp->dt_aggs); + + (void) dt_idhash_iter(dtp->dt_globals, dt_pcb_pop_ident, dtp); + dt_idhash_update(dtp->dt_globals); + + (void) dt_idhash_iter(dtp->dt_tls, dt_pcb_pop_ident, dtp); + dt_idhash_update(dtp->dt_tls); + + (void) ctf_discard(dtp->dt_cdefs->dm_ctfp); + (void) ctf_discard(dtp->dt_ddefs->dm_ctfp); + } + + if (pcb->pcb_pragmas != NULL) + dt_idhash_destroy(pcb->pcb_pragmas); + if (pcb->pcb_locals != NULL) + dt_idhash_destroy(pcb->pcb_locals); + if (pcb->pcb_idents != NULL) + dt_idhash_destroy(pcb->pcb_idents); + if (pcb->pcb_inttab != NULL) + dt_inttab_destroy(pcb->pcb_inttab); + if (pcb->pcb_strtab != NULL) + dt_strtab_destroy(pcb->pcb_strtab); + if (pcb->pcb_regs != NULL) + dt_regset_destroy(pcb->pcb_regs); + + for (i = 0; i < pcb->pcb_asxreflen; i++) + dt_free(dtp, pcb->pcb_asxrefs[i]); + + dt_free(dtp, pcb->pcb_asxrefs); + dt_difo_free(dtp, pcb->pcb_difo); + + free(pcb->pcb_filetag); + free(pcb->pcb_sflagv); + + dtp->dt_pcb = pcb->pcb_prev; + bzero(pcb, sizeof (dt_pcb_t)); + yyinit(dtp->dt_pcb); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.h new file mode 100644 index 00000000..04f73082 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pcb.h @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#ifndef _DT_PCB_H +#define _DT_PCB_H + +#ifndef VBOX +# pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include + +typedef struct dt_pcb { + dtrace_hdl_t *pcb_hdl; /* pointer to library handle */ + struct dt_pcb *pcb_prev; /* pointer to previous pcb in stack */ + FILE *pcb_fileptr; /* pointer to input file (or NULL) */ + char *pcb_filetag; /* optional file name string (or NULL) */ + const char *pcb_string; /* pointer to input string (or NULL) */ + const char *pcb_strptr; /* pointer to input position */ + size_t pcb_strlen; /* length of pcb_string */ + int pcb_sargc; /* number of script arguments (if any) */ + char *const *pcb_sargv; /* script argument strings (if any) */ + ushort_t *pcb_sflagv; /* script argument flags (DT_IDFLG_* bits) */ + dt_scope_t pcb_dstack; /* declaration processing stack */ + dt_node_t *pcb_list; /* list of allocated parse tree nodes */ + dt_node_t *pcb_hold; /* parse tree nodes on hold until end of defn */ + dt_node_t *pcb_root; /* root of current parse tree */ + dt_idstack_t pcb_globals; /* stack of global identifier hash tables */ + dt_idhash_t *pcb_locals; /* current hash table of local identifiers */ + dt_idhash_t *pcb_idents; /* current hash table of ambiguous idents */ + dt_idhash_t *pcb_pragmas; /* current hash table of pending pragmas */ + dt_inttab_t *pcb_inttab; /* integer table for constant references */ + dt_strtab_t *pcb_strtab; /* string table for string references */ + dt_regset_t *pcb_regs; /* register set for code generation */ + dt_irlist_t pcb_ir; /* list of unrelocated IR instructions */ + uint_t pcb_asvidx; /* assembler vartab index (see dt_as.c) */ + ulong_t **pcb_asxrefs; /* assembler imported xlators (see dt_as.c) */ + uint_t pcb_asxreflen; /* assembler xlator map length (see dt_as.c) */ + const dtrace_probedesc_t *pcb_pdesc; /* probedesc for current context */ + struct dt_probe *pcb_probe; /* probe associated with current context */ + dtrace_probeinfo_t pcb_pinfo; /* info associated with current context */ + dtrace_attribute_t pcb_amin; /* stability minimum for compilation */ + dt_node_t *pcb_dret; /* node containing return type for assembler */ + dtrace_difo_t *pcb_difo; /* intermediate DIF object made by assembler */ + dtrace_prog_t *pcb_prog; /* intermediate program made by compiler */ + dtrace_stmtdesc_t *pcb_stmt; /* intermediate stmt made by compiler */ + dtrace_ecbdesc_t *pcb_ecbdesc; /* intermediate ecbdesc made by cmplr */ + jmp_buf pcb_jmpbuf; /* setjmp(3C) buffer for error return */ + const char *pcb_region; /* optional region name for yyerror() suffix */ + dtrace_probespec_t pcb_pspec; /* probe description evaluation context */ + uint_t pcb_cflags; /* optional compilation flags (see dtrace.h) */ + uint_t pcb_idepth; /* preprocessor #include nesting depth */ + yystate_t pcb_yystate; /* lex/yacc parsing state (see yybegin()) */ + int pcb_context; /* yyparse() rules context (DT_CTX_* value) */ + int pcb_token; /* token to be returned by yylex() (if != 0) */ + int pcb_cstate; /* state to be restored by lexer at state end */ + int pcb_braces; /* number of open curly braces in lexer */ + int pcb_brackets; /* number of open square brackets in lexer */ + int pcb_parens; /* number of open parentheses in lexer */ +} dt_pcb_t; + +extern void dt_pcb_push(dtrace_hdl_t *, dt_pcb_t *); +extern void dt_pcb_pop(dtrace_hdl_t *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_PCB_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.c new file mode 100644 index 00000000..24180515 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.c @@ -0,0 +1,757 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#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 (lmid == LM_ID_BASE) + (void) strncpy(buf, obj, len); + else + (void) snprintf(buf, len, "LM%lx`%s", lmid, obj); +} + +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; + + pid = Pstatus(pp->dpp_pr)->pr_pid; + + 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); + + (void) Plmid(pp->dpp_pr, pmp->pr_vaddr, &pp->dpp_lmid); + + if ((pp->dpp_obj = strrchr(obj, '/')) == NULL) + pp->dpp_obj = obj; + else + pp->dpp_obj++; + + 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; + + 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; + sym.st_size = Pstatus(pp->dpp_pr)->pr_dmodel == + PR_MODEL_ILP32 ? -1U : -1ULL; + + } 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. + */ + if (Ppltdest(pp->dpp_pr, sym.st_value) != NULL) + return (0); + + (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 ((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)); + + (void) Plmid(pp->dpp_pr, pmp->pr_vaddr, &pp->dpp_lmid); + + 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]; + Lmid_t lmid = PR_LMID_EVERY; + const char *obj; + const prmap_t *pmp; + + /* + * 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; + } + + if ((pmp = Plmid_to_map(P, lmid, obj)) == NULL) + return (NULL); + + (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); + 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; + + /* + * 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])); + } + + 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; + prsyminfo_t sip; + 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), + sip.prs_lmid, mname); + + 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); + } + + if (fd != -1) + (void) pr_close(P, fd); + + 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(MUTEX_HELD(&dpr->dpr_lock)); + + (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", + (int)Pstatus(P)->pr_pid, strerror(errno)); + } + + /* + * 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_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. + */ + (void) dt_ioctl(dtp, DTRACEIOC_ENABLE, NULL); + } + + return (ret); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.h new file mode 100644 index 00000000..ca7162cc --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pid.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef _DT_PID_H +#define _DT_PID_H +#ifndef VBOX + +#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 *); + +#endif /* !VBOX */ +#ifdef __cplusplus +} +#endif + +#endif /* _DT_PID_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pragma.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pragma.c new file mode 100644 index 00000000..b84165ab --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/lib/libdtrace/common/dt_pragma.c @@ -0,0 +1,509 @@ +/* + * 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. + */ + +#ifndef VBOX +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +/* + * This callback function is installed in a given identifier hash to search for + * and apply deferred pragmas that are pending for a given new identifier name. + * Multiple pragmas may be pending for a given name; we processs all of them. + */ +/*ARGSUSED*/ +static void +dt_pragma_apply(dt_idhash_t *dhp, dt_ident_t *idp) +{ + dt_idhash_t *php; + dt_ident_t *pdp; + RT_NOREF1(dhp); + + if ((php = yypcb->pcb_pragmas) == NULL) + return; /* no pragmas pending for current compilation pass */ + + while ((pdp = dt_idhash_lookup(php, idp->di_name)) != NULL) { + switch (pdp->di_kind) { + case DT_IDENT_PRAGAT: + idp->di_attr = pdp->di_attr; + break; + case DT_IDENT_PRAGBN: + idp->di_vers = pdp->di_vers; + break; + } + dt_idhash_delete(php, pdp); + } +} + +/* + * The #pragma attributes directive can be used to reset stability attributes + * on a global identifier or inline definition. If the identifier is already + * defined, we can just change di_attr. If not, we insert the pragma into a + * hash table of the current pcb's deferred pragmas for later processing. + */ +static void +dt_pragma_attributes(const char *prname, dt_node_t *dnp) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dtrace_attribute_t attr, *a VBDTMSC(NULL); + dt_provider_t *pvp; + const char *name, *part; + dt_ident_t *idp; + + if (dnp == NULL || dnp->dn_kind != DT_NODE_IDENT || + dnp->dn_list == NULL || dnp->dn_list->dn_kind != DT_NODE_IDENT) { + xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s " + " \n", prname); + } + + if (dtrace_str2attr(dnp->dn_string, &attr) == -1) { + xyerror(D_PRAGMA_INVAL, "invalid attributes " + "specified by #pragma %s\n", prname); + } + + dnp = dnp->dn_list; + name = dnp->dn_string; + + if (strcmp(name, "provider") == 0) { + dnp = dnp->dn_list; + name = dnp->dn_string; + + dnp = dnp->dn_list; + part = dnp->dn_string; + + if ((pvp = dt_provider_lookup(dtp, name)) != NULL) { + if (strcmp(part, "provider") == 0) { + a = &pvp->pv_desc.dtvd_attr.dtpa_provider; + } else if (strcmp(part, "module") == 0) { + a = &pvp->pv_desc.dtvd_attr.dtpa_mod; + } else if (strcmp(part, "function") == 0) { + a = &pvp->pv_desc.dtvd_attr.dtpa_func; + } else if (strcmp(part, "name") == 0) { + a = &pvp->pv_desc.dtvd_attr.dtpa_name; + } else if (strcmp(part, "args") == 0) { + a = &pvp->pv_desc.dtvd_attr.dtpa_args; + } else { + xyerror(D_PRAGMA_INVAL, "invalid component " + "\"%s\" in attribute #pragma " + "for provider %s\n", name, part); + } + + *a = attr; + return; + } + + } else if ((idp = dt_idstack_lookup( + &yypcb->pcb_globals, name)) != NULL) { + + if (idp->di_gen != dtp->dt_gen) { + xyerror(D_PRAGMA_SCOPE, "#pragma %s cannot modify " + "entity defined outside program scope\n", prname); + } + + idp->di_attr = attr; + return; + } + + if (yypcb->pcb_pragmas == NULL && (yypcb->pcb_pragmas = + dt_idhash_create("pragma", NULL, 0, 0)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + idp = dt_idhash_insert(yypcb->pcb_pragmas, name, DT_IDENT_PRAGAT, 0, 0, + attr, 0, &dt_idops_thaw, (void *)prname, dtp->dt_gen); + + if (idp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + if (dtp->dt_globals->dh_defer == NULL) + dtp->dt_globals->dh_defer = &dt_pragma_apply; +} + +/* + * The #pragma binding directive can be used to reset the version binding + * on a global identifier or inline definition. If the identifier is already + * defined, we can just change di_vers. If not, we insert the pragma into a + * hash table of the current pcb's deferred pragmas for later processing. + */ +static void +dt_pragma_binding(const char *prname, dt_node_t *dnp) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dt_version_t vers; + const char *name; + dt_ident_t *idp; + + if (dnp == NULL || dnp->dn_kind != DT_NODE_STRING || + dnp->dn_list == NULL || dnp->dn_list->dn_kind != DT_NODE_IDENT) { + xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s " + "\"version\" \n", prname); + } + + if (dt_version_str2num(dnp->dn_string, &vers) == -1) { + xyerror(D_PRAGMA_INVAL, "invalid version string " + "specified by #pragma %s\n", prname); + } + + name = dnp->dn_list->dn_string; + idp = dt_idstack_lookup(&yypcb->pcb_globals, name); + + if (idp != NULL) { + if (idp->di_gen != dtp->dt_gen) { + xyerror(D_PRAGMA_SCOPE, "#pragma %s cannot modify " + "entity defined outside program scope\n", prname); + } + idp->di_vers = vers; + return; + } + + if (yypcb->pcb_pragmas == NULL && (yypcb->pcb_pragmas = + dt_idhash_create("pragma", NULL, 0, 0)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + idp = dt_idhash_insert(yypcb->pcb_pragmas, name, DT_IDENT_PRAGBN, 0, 0, + _dtrace_defattr, vers, &dt_idops_thaw, (void *)prname, dtp->dt_gen); + + if (idp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + if (dtp->dt_globals->dh_defer == NULL) + dtp->dt_globals->dh_defer = &dt_pragma_apply; +} + +/* + * The #pragma depends_on directive can be used to express a dependency on a + * module, provider or library which if not present will cause processing to + * abort. + */ +static void +dt_pragma_depends(const char *prname, dt_node_t *cnp) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dt_node_t *nnp = cnp ? cnp->dn_list : NULL; + int found VBDTMSC(B_FALSE); + dt_lib_depend_t *dld; + char lib[MAXPATHLEN]; + + if (cnp == NULL || nnp == NULL || + cnp->dn_kind != DT_NODE_IDENT || nnp->dn_kind != DT_NODE_IDENT) { + xyerror(D_PRAGMA_MALFORM, "malformed #pragma %s " + " \n", prname); + } + + if (strcmp(cnp->dn_string, "provider") == 0) + found = dt_provider_lookup(dtp, nnp->dn_string) != NULL; + else if (strcmp(cnp->dn_string, "module") == 0) { + dt_module_t *mp = dt_module_lookup_by_name(dtp, nnp->dn_string); + found = mp != NULL && dt_module_getctf(dtp, mp) != NULL; + } else if (strcmp(cnp->dn_string, "library") == 0) { + if (yypcb->pcb_cflags & DTRACE_C_CTL) { + assert(dtp->dt_filetag != NULL); + + /* + * We have the file we are working on in dtp->dt_filetag + * so find that node and add the dependency in. + */ + dld = dt_lib_depend_lookup(&dtp->dt_lib_dep, + dtp->dt_filetag); + assert(dld != NULL); + + (void) snprintf(lib, sizeof (lib), "%s%s", + dld->dtld_libpath, nnp->dn_string); + if ((dt_lib_depend_add(dtp, &dld->dtld_dependencies, + lib)) != 0) { + xyerror(D_PRAGMA_DEPEND, + "failed to add dependency %s:%s\n", lib, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + } + } else { + /* + * By this point we have already performed a topological + * sort of the dependencies; we process this directive + * as satisfied as long as the dependency was properly + * loaded. + */ + if (dtp->dt_filetag == NULL) + xyerror(D_PRAGMA_DEPEND, "main program may " + "not explicitly depend on a library"); + + dld = dt_lib_depend_lookup(&dtp->dt_lib_dep, + dtp->dt_filetag); + assert(dld != NULL); + + (void) snprintf(lib, sizeof (lib), "%s%s", + dld->dtld_libpath, nnp->dn_string); + dld = dt_lib_depend_lookup(&dtp->dt_lib_dep_sorted, + lib); + assert(dld != NULL); + + if (!dld->dtld_loaded) + xyerror(D_PRAGMA_DEPEND, "program requires " + "library \"%s\" which failed to load", + lib); + } + + found = B_TRUE; + } else { + xyerror(D_PRAGMA_INVAL, "invalid class %s " + "specified by #pragma %s\n", cnp->dn_string, prname); + } + + if (!found) { + xyerror(D_PRAGMA_DEPEND, "program requires %s %s\n", + cnp->dn_string, nnp->dn_string); + } +} + +/* + * The #pragma error directive can be followed by any list of tokens, which we + * just concatenate and print as part of our error message. + */ +static void +dt_pragma_error(const char *prname, dt_node_t *dnp) +{ + dt_node_t *enp; + size_t n = 0; + char *s; + + for (enp = dnp; enp != NULL; enp = enp->dn_list) { + if (enp->dn_kind == DT_NODE_IDENT || + enp->dn_kind == DT_NODE_STRING) + n += strlen(enp->dn_string) + 1; + } + + s = alloca(n + 1); + s[0] = '\0'; + + for (enp = dnp; enp != NULL; enp = enp->dn_list) { + if (enp->dn_kind == DT_NODE_IDENT || + enp->dn_kind == DT_NODE_STRING) { + (void) strcat(s, enp->dn_string); + (void) strcat(s, " "); + } + } + + xyerror(D_PRAGERR, "#%s: %s\n", prname, s); +} + +/*ARGSUSED*/ +static void +dt_pragma_ident(const char *prname, dt_node_t *dnp) +{ + /* ignore any #ident or #pragma ident lines */ + RT_NOREF2(prname, dnp); +} + +static void +dt_pragma_option(const char *prname, dt_node_t *dnp) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + char *opt, *val; + + if (dnp == NULL || dnp->dn_kind != DT_NODE_IDENT) { + xyerror(D_PRAGMA_MALFORM, + "malformed #pragma %s