diff --git a/Makefile.inc1 b/Makefile.inc1 index 58a9b1fd4f3e..d317e3acdd7f 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -1,3791 +1,3794 @@ # # # Make command line options: # -DNO_CLEANDIR run ${MAKE} clean, instead of ${MAKE} cleandir # -DNO_CLEAN do not clean at all # -DDB_FROM_SRC use the user/group databases in src/etc instead of # the system database when installing. # -DNO_SHARE do not go into share subdir # -DKERNFAST define NO_KERNEL{CONFIG,CLEAN,OBJ} # -DNO_KERNELCONFIG do not run config in ${MAKE} buildkernel # -DNO_KERNELCLEAN do not run ${MAKE} clean in ${MAKE} buildkernel # -DNO_KERNELOBJ do not run ${MAKE} obj in ${MAKE} buildkernel # -DNO_ROOT install without using root privilege # -DWITHOUT_CTF do not run the DTrace CTF conversion tools on built objects # LOCAL_DIRS="list of dirs" to add additional dirs to the SUBDIR list # LOCAL_ITOOLS="list of tools" to add additional tools to the ITOOLS list # LOCAL_LIB_DIRS="list of dirs" to add additional dirs to libraries target # LOCAL_MTREE="list of mtree files" to process to allow local directories # to be created before files are installed # LOCAL_LEGACY_DIRS="list of dirs" to add additional dirs to the legacy # target # LOCAL_BSTOOL_DIRS="list of dirs" to add additional dirs to the # bootstrap-tools target # LOCAL_TOOL_DIRS="list of dirs" to add additional dirs to the build-tools # target # LOCAL_XTOOL_DIRS="list of dirs" to add additional dirs to the # cross-tools target # METALOG="path to metadata log" to write permission and ownership # when NO_ROOT is set. (default: ${DESTDIR}/${DISTDIR}/METALOG, # check /etc/make.conf for DISTDIR) # TARGET="machine" to crossbuild world for a different machine type # TARGET_ARCH= may be required when a TARGET supports multiple endians # BUILDENV_SHELL= shell to launch for the buildenv target (def:${SHELL}) # WORLD_FLAGS= additional flags to pass to make(1) during buildworld # KERNEL_FLAGS= additional flags to pass to make(1) during buildkernel # SUBDIR_OVERRIDE="list of dirs" to build rather than everything. # All libraries and includes, and some build tools will still build. # # The intended user-driven targets are: # buildworld - rebuild *everything*, including glue to help do upgrades # installworld- install everything built by "buildworld" # checkworld - run test suite on installed world # doxygen - build API documentation of the kernel # # Standard targets (not defined here) are documented in the makefiles in # /usr/share/mk. These include: # obj depend all install clean cleandepend cleanobj .if !defined(TARGET) || !defined(TARGET_ARCH) .error Both TARGET and TARGET_ARCH must be defined. .endif .if make(showconfig) || make(test-system-*) _MKSHOWCONFIG= t .endif SRCDIR?= ${.CURDIR} LOCALBASE?= /usr/local TIME_ENV ?= time env .include "share/mk/src.tools.mk" # Cross toolchain changes must be in effect before bsd.compiler.mk # so that gets the right CC, and pass CROSS_TOOLCHAIN to submakes. .if defined(CROSS_TOOLCHAIN) .if exists(${LOCALBASE}/share/toolchains/${CROSS_TOOLCHAIN}.mk) .include "${LOCALBASE}/share/toolchains/${CROSS_TOOLCHAIN}.mk" .elif exists(${CROSS_TOOLCHAIN}) .include "${CROSS_TOOLCHAIN}" .else .error CROSS_TOOLCHAIN ${CROSS_TOOLCHAIN} not found .endif CROSSENV+=CROSS_TOOLCHAIN="${CROSS_TOOLCHAIN}" .elif defined(UNIVERSE_TOOLCHAIN) UNIVERSE_TOOLCHAIN_PATH?=${HOST_OBJTOP}/tmp/usr/bin XCC?="${UNIVERSE_TOOLCHAIN_PATH}/cc" XCXX?="${UNIVERSE_TOOLCHAIN_PATH}/c++" XCPP?="${UNIVERSE_TOOLCHAIN_PATH}/cpp" XLD?="${UNIVERSE_TOOLCHAIN_PATH}/ld" .endif .if defined(CROSS_TOOLCHAIN_PREFIX) CROSS_COMPILER_PREFIX?=${CROSS_TOOLCHAIN_PREFIX} .endif XCOMPILERS= CC CXX CPP .for COMPILER in ${XCOMPILERS} .if defined(CROSS_COMPILER_PREFIX) X${COMPILER}?= ${CROSS_COMPILER_PREFIX}${${COMPILER}} .else X${COMPILER}?= ${${COMPILER}} .endif .endfor # If a full path to an external cross compiler is given, don't build # a cross compiler. .if ${XCC:N${CCACHE_BIN}:M/*} MK_CLANG_BOOTSTRAP= no # Make sure sub-makes see the option as disabled so the hack in bsd.sys.mk to # work around incompatible headers in Clang's resource directory is enabled. .MAKEOVERRIDES+= MK_CLANG_BOOTSTRAP .endif # Pull in compiler metadata from buildworld/toolchain if possible to avoid # running CC from bsd.compiler.mk. .if make(installworld) || make(install) || make(distributeworld) || \ make(stageworld) .-include "${OBJTOP}/toolchain-metadata.mk" .if !defined(_LOADED_TOOLCHAIN_METADATA) .error A build is required first. You may have the wrong MAKEOBJDIRPREFIX set. .endif .endif # Pull in COMPILER_TYPE and COMPILER_FREEBSD_VERSION early. Pull it from the # tree to be friendlier to foreign OS builds. It's safe to do so unconditionally # here since we will always have the right make, unlike in src/Makefile # Don't include bsd.linker.mk yet until XBINUTILS is handled (after src.opts.mk) _NO_INCLUDE_LINKERMK= t # We also want the X_COMPILER* variables if we are using an external toolchain. _WANT_TOOLCHAIN_CROSS_VARS= t .include "share/mk/bsd.compiler.mk" .undef _NO_INCLUDE_LINKERMK .undef _WANT_TOOLCHAIN_CROSS_VARS # src.opts.mk depends on COMPILER_FEATURES .include "share/mk/src.opts.mk" .if ${TARGET} == ${MACHINE} TARGET_CPUTYPE?=${CPUTYPE} .else TARGET_CPUTYPE?= .endif .if !empty(TARGET_CPUTYPE) _TARGET_CPUTYPE=${TARGET_CPUTYPE} .else _TARGET_CPUTYPE=dummy .endif .if ${TARGET} == "arm" .if ${TARGET_CPUTYPE:M*soft*} == "" TARGET_TRIPLE_ABI= gnueabihf .else TARGET_TRIPLE_ABI= gnueabi .endif .endif MACHINE_TRIPLE_ABI?= unknown MACHINE_TRIPLE?=${MACHINE_ARCH:S/amd64/x86_64/}-${MACHINE_TRIPLE_ABI}-freebsd${OS_REVISION} TARGET_TRIPLE_ABI?= unknown TARGET_TRIPLE?= ${TARGET_ARCH:S/amd64/x86_64/}-${TARGET_TRIPLE_ABI}-freebsd${OS_REVISION} KNOWN_ARCHES?= aarch64/arm64 \ amd64 \ armv6/arm \ armv7/arm \ i386 \ powerpc \ powerpc64/powerpc \ powerpc64le/powerpc \ powerpcspe/powerpc \ riscv64/riscv .if ${TARGET} == ${TARGET_ARCH} _t= ${TARGET} .else _t= ${TARGET_ARCH}/${TARGET} .endif .for _t in ${_t} .if empty(KNOWN_ARCHES:M${_t}) .error Unknown target ${TARGET_ARCH}:${TARGET}. .endif .endfor .if ${TARGET_ARCH} == "amd64" LIBCOMPAT_INCLUDE_DIRS+= i386 .elif ${TARGET_ARCH} == "aarch64" LIBCOMPAT_INCLUDE_DIRS+= arm .endif .if ${.MAKE.OS} != "FreeBSD" CROSSBUILD_HOST=${.MAKE.OS} .if ${.MAKE.OS} != "Linux" && ${.MAKE.OS} != "Darwin" .warning Unsupported crossbuild system: ${.MAKE.OS}. Build will probably fail! .endif # We need to force NO_ROOT/DB_FROM_SRC builds when building on other operating # systems since the BSD.foo.dist specs contain users and groups that do not # exist by default on a Linux/MacOS system. NO_ROOT:= 1 DB_FROM_SRC:= 1 .export NO_ROOT .endif # If all targets are disabled for system llvm then don't expect it to work # for cross-builds. .if !defined(TOOLS_PREFIX) && ${MK_LLVM_TARGET_ALL} == "no" && \ ${MACHINE} != ${TARGET} && ${MACHINE_ARCH} != ${TARGET_ARCH} && \ !make(showconfig) MK_SYSTEM_COMPILER= no MK_SYSTEM_LINKER= no .endif # Handle external binutils. .if defined(CROSS_TOOLCHAIN_PREFIX) CROSS_BINUTILS_PREFIX?=${CROSS_TOOLCHAIN_PREFIX} .endif XBINUTILS= AS AR ELFCTL LD NM OBJCOPY RANLIB SIZE STRINGS STRIPBIN .for BINUTIL in ${XBINUTILS} .if defined(CROSS_BINUTILS_PREFIX) && \ exists(${CROSS_BINUTILS_PREFIX}/${${BINUTIL}}) X${BINUTIL}?= ${CROSS_BINUTILS_PREFIX:C,/*$,,}/${${BINUTIL}} .else X${BINUTIL}?= ${${BINUTIL}} .endif .endfor # If a full path to an external linker is given, don't build lld. .if ${XLD:M/*} MK_LLD_BOOTSTRAP= no .endif # We also want the X_LINKER* variables if we are using an external toolchain. _WANT_TOOLCHAIN_CROSS_VARS= t .include "share/mk/bsd.linker.mk" .undef _WANT_TOOLCHAIN_CROSS_VARS # Begin WITH_SYSTEM_COMPILER / WITH_SYSTEM_LD # WITH_SYSTEM_COMPILER - Pull in needed values and make a decision. # Check if there is a local compiler that can satisfy as an external compiler. # Which compiler is expected to be used? .if ${MK_CLANG_BOOTSTRAP} == "yes" WANT_COMPILER_TYPE= clang .else WANT_COMPILER_TYPE= .endif .if !defined(WANT_COMPILER_FREEBSD_VERSION) && !make(showconfig) && \ !make(test-system-linker) .if ${WANT_COMPILER_TYPE} == "clang" WANT_COMPILER_FREEBSD_VERSION_FILE= lib/clang/freebsd_cc_version.h WANT_COMPILER_FREEBSD_VERSION!= \ awk '$$2 == "FREEBSD_CC_VERSION" {printf("%d\n", $$3)}' \ ${SRCDIR}/${WANT_COMPILER_FREEBSD_VERSION_FILE} || echo unknown WANT_COMPILER_VERSION_FILE= lib/clang/include/clang/Basic/Version.inc WANT_COMPILER_VERSION!= \ awk '$$2 == "CLANG_VERSION" {split($$3, a, "."); print a[1] * 10000 + a[2] * 100 + a[3]}' \ ${SRCDIR}/${WANT_COMPILER_VERSION_FILE} || echo unknown .endif .export WANT_COMPILER_FREEBSD_VERSION WANT_COMPILER_VERSION .endif # !defined(WANT_COMPILER_FREEBSD_VERSION) # It needs to be the same revision as we would build for the bootstrap. # If the expected vs CC is different then we can't skip. # GCC cannot be used for cross-arch yet. For clang we pass -target later if # TARGET_ARCH!=MACHINE_ARCH. .if ${MK_SYSTEM_COMPILER} == "yes" && \ defined(WANT_COMPILER_FREEBSD_VERSION) && \ ${MK_CLANG_BOOTSTRAP} == "yes" && \ !make(xdev*) && \ ${X_COMPILER_TYPE} == ${WANT_COMPILER_TYPE} && \ (${X_COMPILER_TYPE} == "clang" || ${TARGET_ARCH} == ${MACHINE_ARCH}) && \ ${X_COMPILER_VERSION} == ${WANT_COMPILER_VERSION} && \ ${X_COMPILER_FREEBSD_VERSION} == ${WANT_COMPILER_FREEBSD_VERSION} # Everything matches, disable the bootstrap compiler. MK_CLANG_BOOTSTRAP= no USING_SYSTEM_COMPILER= yes .endif # ${WANT_COMPILER_TYPE} == ${COMPILER_TYPE} # WITH_SYSTEM_LD - Pull in needed values and make a decision. # Check if there is a local linker that can satisfy as an external linker. # Which linker is expected to be used? .if ${MK_LLD_BOOTSTRAP} == "yes" WANT_LINKER_TYPE= lld .else WANT_LINKER_TYPE= .endif .if !defined(WANT_LINKER_FREEBSD_VERSION) && !make(showconfig) && \ !make(test-system-compiler) .if ${WANT_LINKER_TYPE} == "lld" WANT_LINKER_FREEBSD_VERSION_FILE= lib/clang/include/lld/Common/Version.inc WANT_LINKER_FREEBSD_VERSION!= \ awk '$$2 == "LLD_FREEBSD_VERSION" {print $$3}' \ ${SRCDIR}/${WANT_LINKER_FREEBSD_VERSION_FILE} || echo unknown WANT_LINKER_VERSION_FILE= lib/clang/include/lld/Common/Version.inc WANT_LINKER_VERSION!= \ awk '$$2 == "LLD_VERSION_STRING" {gsub("\"", "", $$3); split($$3, a, "."); print a[1] * 10000 + a[2] * 100 + a[3]}' \ ${SRCDIR}/${WANT_LINKER_VERSION_FILE} || echo unknown .else WANT_LINKER_FREEBSD_VERSION_FILE= WANT_LINKER_FREEBSD_VERSION= .endif .export WANT_LINKER_FREEBSD_VERSION WANT_LINKER_VERSION .endif # !defined(WANT_LINKER_FREEBSD_VERSION) .if ${MK_SYSTEM_LINKER} == "yes" && \ defined(WANT_LINKER_FREEBSD_VERSION) && \ (${MK_LLD_BOOTSTRAP} == "yes") && \ !make(xdev*) && \ ${X_LINKER_TYPE} == ${WANT_LINKER_TYPE} && \ ${X_LINKER_VERSION} == ${WANT_LINKER_VERSION} && \ ${X_LINKER_FREEBSD_VERSION} == ${WANT_LINKER_FREEBSD_VERSION} # Everything matches, disable the bootstrap linker. MK_LLD_BOOTSTRAP= no USING_SYSTEM_LINKER= yes .endif # ${WANT_LINKER_TYPE} == ${LINKER_TYPE} # WITH_SYSTEM_COMPILER / WITH_SYSTEM_LINKER - Handle defaults and debug. USING_SYSTEM_COMPILER?= no USING_SYSTEM_LINKER?= no TEST_SYSTEM_COMPILER_VARS= \ USING_SYSTEM_COMPILER MK_SYSTEM_COMPILER \ MK_CROSS_COMPILER MK_CLANG_BOOTSTRAP \ WANT_COMPILER_TYPE WANT_COMPILER_VERSION WANT_COMPILER_VERSION_FILE \ WANT_COMPILER_FREEBSD_VERSION WANT_COMPILER_FREEBSD_VERSION_FILE \ CC COMPILER_TYPE COMPILER_FEATURES COMPILER_VERSION \ COMPILER_FREEBSD_VERSION \ XCC X_COMPILER_TYPE X_COMPILER_FEATURES X_COMPILER_VERSION \ X_COMPILER_FREEBSD_VERSION TEST_SYSTEM_LINKER_VARS= \ USING_SYSTEM_LINKER MK_SYSTEM_LINKER \ MK_LLD_BOOTSTRAP \ WANT_LINKER_TYPE WANT_LINKER_VERSION WANT_LINKER_VERSION_FILE \ WANT_LINKER_FREEBSD_VERSION WANT_LINKER_FREEBSD_VERSION_FILE \ LD LINKER_TYPE LINKER_FEATURES LINKER_VERSION \ LINKER_FREEBSD_VERSION \ XLD X_LINKER_TYPE X_LINKER_FEATURES X_LINKER_VERSION \ X_LINKER_FREEBSD_VERSION .for _t in compiler linker test-system-${_t}: .PHONY .for v in ${TEST_SYSTEM_${_t:tu}_VARS} ${_+_}@printf "%-35s= %s\n" "${v}" "${${v}}" .endfor .endfor .if (make(buildworld) || make(buildkernel) || make(kernel-toolchain) || \ make(toolchain) || make(_cross-tools)) .if ${USING_SYSTEM_COMPILER} == "yes" .info SYSTEM_COMPILER: Determined that CC=${CC} matches the source tree. Not bootstrapping a cross-compiler. .elif ${MK_CLANG_BOOTSTRAP} == "yes" .info SYSTEM_COMPILER: libclang will be built for bootstrapping a cross-compiler. .endif .if ${USING_SYSTEM_LINKER} == "yes" .info SYSTEM_LINKER: Determined that LD=${LD} matches the source tree. Not bootstrapping a cross-linker. .elif ${MK_LLD_BOOTSTRAP} == "yes" .info SYSTEM_LINKER: libclang will be built for bootstrapping a cross-linker. .endif .endif # End WITH_SYSTEM_COMPILER / WITH_SYSTEM_LD # Store some compiler metadata for use in installworld where we don't # want to invoke CC at all. _TOOLCHAIN_METADATA_VARS= COMPILER_VERSION \ COMPILER_TYPE \ COMPILER_FEATURES \ COMPILER_FREEBSD_VERSION \ COMPILER_RESOURCE_DIR \ LINKER_VERSION \ LINKER_FEATURES \ LINKER_TYPE \ LINKER_FREEBSD_VERSION toolchain-metadata.mk: .PHONY .META @: > ${.TARGET} @echo ".info Using cached toolchain metadata from build at $$(hostname) on $$(date)" \ > ${.TARGET} @echo "_LOADED_TOOLCHAIN_METADATA=t" >> ${.TARGET} .for v in ${_TOOLCHAIN_METADATA_VARS} @echo "${v}=${${v}}" >> ${.TARGET} @echo "X_${v}=${X_${v}}" >> ${.TARGET} .endfor @echo ".export ${_TOOLCHAIN_METADATA_VARS}" >> ${.TARGET} @echo ".export ${_TOOLCHAIN_METADATA_VARS:C,^,X_,}" >> ${.TARGET} # We must do lib/ and libexec/ before bin/ in case of a mid-install error to # keep the users system reasonably usable. For static->dynamic root upgrades, # we don't want to install a dynamic binary without rtld and the needed # libraries. More commonly, for dynamic root, we don't want to install a # binary that requires a newer library version that hasn't been installed yet. # This ordering is not a guarantee though. The only guarantee of a working # system here would require fine-grained ordering of all components based # on their dependencies. .if !empty(SUBDIR_OVERRIDE) SUBDIR= ${SUBDIR_OVERRIDE} .else SUBDIR= lib libexec # Add LOCAL_LIB_DIRS, but only if they will not be picked up as a SUBDIR # of a LOCAL_DIRS directory. This allows LOCAL_DIRS=foo and # LOCAL_LIB_DIRS=foo/lib to behave as expected. .for _DIR in ${LOCAL_DIRS:M*/} ${LOCAL_DIRS:N*/:S|$|/|} _REDUNDANT_LIB_DIRS+= ${LOCAL_LIB_DIRS:M${_DIR}*} .endfor .for _DIR in ${LOCAL_LIB_DIRS} .if ${_DIR} == ".WAIT" || (empty(_REDUNDANT_LIB_DIRS:M${_DIR}) && exists(${.CURDIR}/${_DIR}/Makefile)) SUBDIR+= ${_DIR} .endif .endfor .if !defined(NO_ROOT) && (make(installworld) || make(install)) # Ensure libraries are installed before progressing. SUBDIR+=.WAIT .endif SUBDIR+=bin .if ${MK_CDDL} != "no" SUBDIR+=cddl .endif SUBDIR+=gnu include .if ${MK_KERBEROS} != "no" SUBDIR+=kerberos5 .endif .if ${MK_RESCUE} != "no" SUBDIR+=rescue .endif SUBDIR+=sbin .if ${MK_CRYPT} != "no" SUBDIR+=secure .endif .if !defined(NO_SHARE) SUBDIR+=share .endif .if ${MK_BOOT} != "no" SUBDIR+=stand .endif SUBDIR+=sys usr.bin usr.sbin .if ${MK_TESTS} != "no" SUBDIR+= tests .endif # Local directories are built in parallel with the base system directories. # Users may insert a .WAIT directive at the beginning or elsewhere within # the LOCAL_DIRS and LOCAL_LIB_DIRS lists as needed. .for _DIR in ${LOCAL_DIRS} .if ${_DIR} == ".WAIT" || exists(${.CURDIR}/${_DIR}/Makefile) SUBDIR+= ${_DIR} .endif .endfor # We must do etc/ last as it hooks into building the man whatis file # by calling 'makedb' in share/man. This is only relevant for # install/distribute so they build the whatis file after every manpage is # installed. .if make(installworld) || make(install) SUBDIR+=.WAIT .endif SUBDIR+=etc .endif # !empty(SUBDIR_OVERRIDE) .if defined(NOCLEAN) .warning The src.conf WITHOUT_CLEAN option can now be used instead of NOCLEAN. MK_CLEAN:= no .endif .if defined(NO_CLEAN) .info The src.conf WITHOUT_CLEAN option can now be used instead of NO_CLEAN. MK_CLEAN:= no .endif .if defined(NO_CLEANDIR) CLEANDIR= clean cleandepend .else CLEANDIR= cleandir .endif .if defined(WORLDFAST) MK_CLEAN:= no NO_OBJWALK= t .endif .if ${MK_META_MODE} == "yes" # If filemon is used then we can rely on the build being incremental-safe. # The .meta files will also track the build command and rebuild should # it change. .if empty(.MAKE.MODE:Mnofilemon) MK_CLEAN:= no .endif .endif .if defined(NO_OBJWALK) || ${MK_AUTO_OBJ} == "yes" NO_OBJWALK= t NO_KERNELOBJ= t .endif .if !defined(NO_OBJWALK) _obj= obj .endif LOCAL_TOOL_DIRS?= PACKAGEDIR?= ${DESTDIR}/${DISTDIR} .if empty(SHELL:M*csh*) BUILDENV_SHELL?=${SHELL} .else BUILDENV_SHELL?=/bin/sh .endif .if !defined(_MKSHOWCONFIG) .if !defined(VCS_REVISION) || empty(VCS_REVISION) .if !defined(SVNVERSION_CMD) || empty(SVNVERSION_CMD) . for _D in ${PATH:S,:, ,g} . if exists(${_D}/svnversion) SVNVERSION_CMD?=${_D}/svnversion . endif . if exists(${_D}/svnliteversion) SVNVERSION_CMD?=${_D}/svnliteversion . endif . endfor .endif .if defined(SVNVERSION_CMD) && !empty(SVNVERSION_CMD) _VCS_REVISION?= $$(eval ${SVNVERSION_CMD} ${SRCDIR}) . if !empty(_VCS_REVISION) VCS_REVISION= $$(echo r${_VCS_REVISION}) .export VCS_REVISION . endif .endif .endif .if !defined(GIT_CMD) || empty(GIT_CMD) . for _P in /usr/bin /usr/local/bin . if exists(${_P}/git) GIT_CMD= ${_P}/git . endif . endfor .export GIT_CMD .endif .if !defined(OSRELDATE) .if exists(/usr/include/osreldate.h) OSRELDATE!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ /usr/include/osreldate.h .else OSRELDATE= 0 .endif .export OSRELDATE .endif # Set VERSION for CTFMERGE to use via the default CTFFLAGS=-L VERSION. .for _V in BRANCH REVISION TYPE .if !defined(_${_V}) _${_V}!= eval $$(awk '/^${_V}=/{print}' ${SRCTOP}/sys/conf/newvers.sh); echo $$${_V} .export _${_V} .endif .endfor .if !defined(SRCRELDATE) SRCRELDATE!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ ${SRCDIR}/sys/sys/param.h .export SRCRELDATE .endif .if !defined(VERSION) VERSION= FreeBSD ${_REVISION}-${_BRANCH:C/-p[0-9]+$//} ${TARGET_ARCH} ${SRCRELDATE} .export VERSION .endif MAJOR_REVISION= ${_REVISION:R} .if !defined(PKG_VERSION) _PKG_REVISION= ${_REVISION} _STRTIMENOW= %Y%m%d%H%M%S _TIMENOW= ${_STRTIMENOW:gmtime} .if ${_BRANCH:MCURRENT*} || ${_BRANCH:MSTABLE*} || ${_BRANCH:MPRERELEASE*} _PKG_REVISION= ${MAJOR_REVISION} EXTRA_REVISION= .snap${_TIMENOW} .elif ${_BRANCH:MALPHA*} EXTRA_REVISION= .a${_BRANCH:C/ALPHA([0-9]+).*/\1/}.${_TIMENOW} .elif ${_BRANCH:MBETA*} EXTRA_REVISION= .b${_BRANCH:C/BETA([0-9]+).*/\1/}.${_TIMENOW} .elif ${_BRANCH:MRC*} EXTRA_REVISION= .rc${_BRANCH:C/RC([0-9]+).*/\1/}.${_TIMENOW} .elif ${_BRANCH:M*-p*} EXTRA_REVISION= p${_BRANCH:C/.*-p([0-9]+$)/\1/} .endif PKG_VERSION:= ${_PKG_REVISION}${EXTRA_REVISION:C/[[:space:]]//g} .endif .endif # !defined(PKG_VERSION) .if !defined(PKG_TIMESTAMP) TIMEEPOCHNOW= %s SOURCE_DATE_EPOCH= ${TIMEEPOCHNOW:gmtime} .else SOURCE_DATE_EPOCH= ${PKG_TIMESTAMP} .endif PKG_NAME_PREFIX?= FreeBSD PKG_MAINTAINER?= re@FreeBSD.org PKG_WWW?= https://www.FreeBSD.org .export PKG_NAME_PREFIX .export PKG_MAINTAINER .export PKG_WWW .if !defined(_MKSHOWCONFIG) _CPUTYPE!= MAKEFLAGS= CPUTYPE=${_TARGET_CPUTYPE} ${MAKE} -f /dev/null \ -m ${.CURDIR}/share/mk MK_AUTO_OBJ=no -V CPUTYPE .if ${_CPUTYPE} != ${_TARGET_CPUTYPE} .error CPUTYPE global should be set with ?=. .endif .endif .if make(buildworld) BUILD_ARCH!= uname -p # On some Linux systems uname -p returns "unknown" so skip this check there. # This check only exists to tell people to use TARGET_ARCH instead of # MACHINE_ARCH so skipping it when crossbuilding on non-FreeBSD should be fine. .if ${MACHINE_ARCH} != ${BUILD_ARCH} && ${.MAKE.OS} == "FreeBSD" .error To cross-build, set TARGET_ARCH. .endif .endif WORLDTMP?= ${OBJTOP}/tmp BPATH= ${CCACHE_WRAPPER_PATH_PFX}${WORLDTMP}/legacy/usr/sbin:${WORLDTMP}/legacy/usr/bin:${WORLDTMP}/legacy/bin:${WORLDTMP}/legacy/usr/libexec XPATH= ${WORLDTMP}/bin:${WORLDTMP}/usr/sbin:${WORLDTMP}/usr/bin # When building we want to find the cross tools before the host tools in ${BPATH}. # We also need to add UNIVERSE_TOOLCHAIN_PATH so that we can find the shared # toolchain files (clang, lld, etc.) during make universe/tinderbox STRICTTMPPATH= ${XPATH}:${BPATH}:${UNIVERSE_TOOLCHAIN_PATH} # We should not be using tools from /usr/bin accidentally since this could cause # the build to break on other systems that don't have that tool. For now we # still allow using the old behaviour (inheriting $PATH) if # BUILD_WITH_STRICT_TMPPATH is set to 0 but this will eventually be removed. # Currently strict $PATH can cause build failures. Once the remaining issues # have been resolved it will be turned on by default. BUILD_WITH_STRICT_TMPPATH?=0 .if defined(CROSSBUILD_HOST) # When building on non-FreeBSD we can't rely on the tools in /usr/bin being compatible # with what FreeBSD expects. Therefore we only use tools from STRICTTMPPATH # during the world build stage. We build most tools during the bootstrap-tools # phase but symlink host tools that are known to work instead of building them BUILD_WITH_STRICT_TMPPATH:=1 .endif .if ${BUILD_WITH_STRICT_TMPPATH} != 0 TMPPATH= ${STRICTTMPPATH} .else TMPPATH= ${STRICTTMPPATH}:${PATH} .endif # # Avoid running mktemp(1) unless actually needed. # It may not be functional, e.g., due to new ABI # when in the middle of installing over this system. # .if make(distributeworld) || make(installworld) || make(stageworld) .if ${BUILD_WITH_STRICT_TMPPATH} != 0 MKTEMP=${WORLDTMP}/legacy/usr/bin/mktemp .if !exists(${MKTEMP}) .error mktemp binary doesn't exist in expected location: ${MKTEMP} .endif .else MKTEMP=mktemp .endif INSTALLTMP!= ${MKTEMP} -d -u -t install .if ${.MAKE.OS} == "FreeBSD" # When building on FreeBSD we always copy the host tools instead of linking # into INSTALLTMP to avoid issues with incompatible libraries (see r364030). # Note: we could create links if we don't intend to update the current machine. INSTALLTMP_COPY_HOST_TOOL=cp .else # However, this is not necessary on Linux/macOS. Additionally, copying the host # tools to another directory with cp results in AMFI Launch Constraint # Violations on macOS Ventura as part of its System Integrity Protection. INSTALLTMP_COPY_HOST_TOOL=ln -s .endif .endif .if make(stagekernel) || make(distributekernel) TAGS+= kernel PACKAGE= kernel .endif # # Building a world goes through the following stages # # 1. legacy stage [BMAKE] # This stage is responsible for creating compatibility # shims that are needed by the bootstrap-tools, # build-tools and cross-tools stages. These are generally # APIs that tools from one of those three stages need to # build that aren't present on the host. # 1. bootstrap-tools stage [BMAKE] # This stage is responsible for creating programs that # are needed for backward compatibility reasons. They # are not built as cross-tools. # 2. build-tools stage [TMAKE] # This stage is responsible for creating the object # tree and building any tools that are needed during # the build process. Some programs are listed during # this phase because they build binaries to generate # files needed to build these programs. This stage also # builds the 'build-tools' target rather than 'all'. # 3. cross-tools stage [XMAKE] # This stage is responsible for creating any tools that # are needed for building the system. A cross-compiler is one # of them. This differs from build tools in two ways: # 1. the 'all' target is built rather than 'build-tools' # 2. these tools are installed into TMPPATH for stage 4. # 4. world stage [WMAKE] # This stage actually builds the world. # 5. install stage (optional) [IMAKE] # This stage installs a previously built world. # BOOTSTRAPPING?= 0 # Keep these in sync MINIMUM_SUPPORTED_OSREL?= 1104001 MINIMUM_SUPPORTED_REL?= 11.4 # Common environment for world related stages CROSSENV+= \ MACHINE_ARCH=${TARGET_ARCH} \ MACHINE=${TARGET} \ CPUTYPE=${TARGET_CPUTYPE} .if ${MK_META_MODE} != "no" # Don't rebuild build-tools targets during normal build. CROSSENV+= BUILD_TOOLS_META=.NOMETA .endif .if defined(TARGET_CFLAGS) CROSSENV+= ${TARGET_CFLAGS} .endif .if (${TARGET} != ${MACHINE} && !defined(WITH_LOCAL_MODULES)) || \ defined(WITHOUT_LOCAL_MODULES) CROSSENV+= LOCAL_MODULES= .endif BOOTSTRAPPING_OSRELDATE?=${OSRELDATE} # bootstrap-tools stage BMAKEENV= INSTALL="sh ${.CURDIR}/tools/install.sh" \ TOOLS_PREFIX=${TOOLS_PREFIX_UNDEF:U${WORLDTMP}} \ PATH=${BPATH:Q}:${PATH:Q} \ WORLDTMP=${WORLDTMP} \ MAKEFLAGS="-m ${.CURDIR}/tools/build/mk ${.MAKEFLAGS}" # need to keep this in sync with targets/pseudo/bootstrap-tools/Makefile BSARGS= DESTDIR= \ OBJTOP='${WORLDTMP}/obj-tools' \ OBJROOT='$${OBJTOP}/' \ UNIVERSE_TOOLCHAIN_PATH=${UNIVERSE_TOOLCHAIN_PATH} \ MAKEOBJDIRPREFIX= \ BOOTSTRAPPING=${BOOTSTRAPPING_OSRELDATE} \ BWPHASE=${.TARGET:C,^_,,} \ -DNO_CPU_CFLAGS \ -DNO_LINT \ -DNO_PIC \ -DNO_SHARED \ MK_ASAN=no \ MK_CTF=no \ MK_CLANG_EXTRAS=no \ MK_CLANG_FORMAT=no \ MK_CLANG_FULL=no \ MK_HTML=no \ MK_MAN=no \ MK_PROFILE=no \ MK_RETPOLINE=no \ MK_SSP=no \ MK_TESTS=no \ MK_UBSAN=no \ MK_WERROR=no \ MK_INCLUDES=yes \ MK_MAN_UTILS=yes BMAKE= \ ${TIME_ENV} ${BMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ ${BSARGS} .if empty(.MAKEOVERRIDES:MMK_LLVM_TARGET_ALL) BMAKE+= MK_LLVM_TARGET_ALL=no .endif # build-tools stage TMAKE= \ ${TIME_ENV} ${BMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ DESTDIR= \ BOOTSTRAPPING=${BOOTSTRAPPING_OSRELDATE} \ BWPHASE=${.TARGET:C,^_,,} \ -DNO_CPU_CFLAGS \ -DNO_LINT \ MK_ASAN=no \ MK_CTF=no \ MK_CLANG_EXTRAS=no \ MK_CLANG_FORMAT=no \ MK_CLANG_FULL=no \ MK_LLDB=no \ MK_RETPOLINE=no \ MK_SSP=no \ MK_TESTS=no \ MK_UBSAN=no \ MK_WERROR=no # cross-tools stage # TOOLS_PREFIX set in BMAKE XMAKE= ${BMAKE} \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ MK_LLDB=no \ MK_LLVM_BINUTILS=no \ MK_TESTS=no # kernel-tools stage KTMAKEENV= INSTALL="sh ${.CURDIR}/tools/install.sh" \ PATH=${BPATH:Q}:${PATH:Q} \ WORLDTMP=${WORLDTMP} \ MAKEFLAGS="-m ${.CURDIR}/tools/build/mk ${.MAKEFLAGS}" KTMAKE= ${TIME_ENV} \ TOOLS_PREFIX=${TOOLS_PREFIX_UNDEF:U${WORLDTMP}} \ ${KTMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ DESTDIR= \ OBJTOP='${WORLDTMP}/obj-kernel-tools' \ OBJROOT='$${OBJTOP}/' \ UNIVERSE_TOOLCHAIN_PATH=${UNIVERSE_TOOLCHAIN_PATH} \ MAKEOBJDIRPREFIX= \ BOOTSTRAPPING=${BOOTSTRAPPING_OSRELDATE} \ -DNO_CPU_CFLAGS \ -DNO_LINT \ -DNO_PIC \ -DNO_SHARED \ MK_CTF=no \ MK_HTML=no \ MK_MAN=no \ MK_PROFILE=no \ MK_SSP=no \ MK_RETPOLINE=no \ MK_WERROR=no # world stage WMAKEENV= ${CROSSENV} \ INSTALL="${INSTALL_CMD} -U" \ PATH=${TMPPATH:Q} \ SYSROOT=${WORLDTMP} # make hierarchy HMAKE= PATH=${TMPPATH:Q} ${MAKE} LOCAL_MTREE=${LOCAL_MTREE:Q} .if defined(NO_ROOT) HMAKE+= PATH=${TMPPATH:Q} METALOG=${METALOG} -DNO_ROOT .endif CROSSENV+= CC="${XCC} ${XCFLAGS}" CXX="${XCXX} ${XCXXFLAGS} ${XCFLAGS}" \ CPP="${XCPP} ${XCFLAGS}" \ AS="${XAS}" AR="${XAR}" ELFCTL="${XELFCTL}" LD="${XLD}" \ LLVM_LINK="${XLLVM_LINK}" NM=${XNM} OBJCOPY="${XOBJCOPY}" \ RANLIB=${XRANLIB} STRINGS=${XSTRINGS} \ SIZE="${XSIZE}" STRIPBIN="${XSTRIPBIN}" .if defined(CROSS_BINUTILS_PREFIX) && exists(${CROSS_BINUTILS_PREFIX}) # In the case of xdev-build tools, CROSS_BINUTILS_PREFIX won't be a # directory, but the compiler will look in the right place for its # tools so we don't need to tell it where to look. BFLAGS+= -B${CROSS_BINUTILS_PREFIX} .endif # The internal bootstrap compiler has a default sysroot set by TOOLS_PREFIX # and target set by TARGET/TARGET_ARCH. However, there are several needs to # always pass an explicit --sysroot and -target. # - External compiler needs sysroot and target flags. # - External ld needs sysroot. # - To be clear about the use of a sysroot when using the internal compiler. # - Easier debugging. # - Allowing WITH_SYSTEM_COMPILER+WITH_META_MODE to work together due to # the flip-flopping build command when sometimes using external and # sometimes using internal. # - Allow using lld which has no support for default paths. .if !defined(CROSS_BINUTILS_PREFIX) || !exists(${CROSS_BINUTILS_PREFIX}) BFLAGS+= -B${WORLDTMP}/usr/bin .endif .if ${WANT_COMPILER_TYPE} == gcc || \ (defined(X_COMPILER_TYPE) && ${X_COMPILER_TYPE} == gcc) .elif ${WANT_COMPILER_TYPE} == clang || \ (defined(X_COMPILER_TYPE) && ${X_COMPILER_TYPE} == clang) XCFLAGS+= -target ${TARGET_TRIPLE} .endif XCFLAGS+= --sysroot=${WORLDTMP} .if !empty(BFLAGS) XCFLAGS+= ${BFLAGS} .endif .include "share/mk/bsd.compat.pre.mk" .for LIBCOMPAT in ${_ALL_LIBCOMPATS} .if ${MK_LIB${LIBCOMPAT}} == "yes" _LIBCOMPATS+= ${LIBCOMPAT} .endif .endfor .include "Makefile.libcompat" # META_MODE normally ignores host file changes since every build updates # timestamps (see NO_META_IGNORE_HOST in sys.mk). There are known times # when the ABI breaks though that we want to force rebuilding WORLDTMP # to get updated host tools. .if ${MK_META_MODE} == "yes" && ${MK_CLEAN} == "no" && \ !defined(NO_META_IGNORE_HOST) && !defined(NO_META_IGNORE_HOST_HEADERS) && \ !defined(_MKSHOWCONFIG) # r318736 - ino64 major ABI breakage META_MODE_BAD_ABI_VERS+= 1200031 .if !defined(OBJDIR_HOST_OSRELDATE) .if exists(${OBJTOP}/host-osreldate.h) OBJDIR_HOST_OSRELDATE!= \ awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ ${OBJTOP}/host-osreldate.h .elif exists(${WORLDTMP}/usr/include/osreldate.h) OBJDIR_HOST_OSRELDATE= 0 .endif .export OBJDIR_HOST_OSRELDATE .endif # Note that this logic is the opposite of normal BOOTSTRAP handling. We want # to compare the WORLDTMP's OSRELDATE to the host's OSRELDATE. If the WORLDTMP # is older than the ABI-breakage OSRELDATE of the HOST then we rebuild. .if defined(OBJDIR_HOST_OSRELDATE) .for _ver in ${META_MODE_BAD_ABI_VERS} .if ${OSRELDATE} >= ${_ver} && ${OBJDIR_HOST_OSRELDATE} < ${_ver} _meta_mode_need_rebuild= ${_ver} .endif .endfor .if defined(_meta_mode_need_rebuild) .info META_MODE: Rebuilding host tools due to ABI breakage in __FreeBSD_version ${_meta_mode_need_rebuild}. NO_META_IGNORE_HOST_HEADERS= 1 .export NO_META_IGNORE_HOST_HEADERS .endif # defined(_meta_mode_need_rebuild) .endif # defined(OBJDIR_HOST_OSRELDATE) .endif # ${MK_META_MODE} == "yes" && ${MK_CLEAN} == "no" ... # This is only used for META_MODE+filemon to track what the oldest # __FreeBSD_version is in WORLDTMP. This purposely does NOT have # a make dependency on /usr/include/osreldate.h as the file should # only be copied when it is missing or meta mode determines it has changed. # Since host files are normally ignored without NO_META_IGNORE_HOST # the file will never be updated unless that flag is specified. This # allows tracking the oldest osreldate to force rebuilds via # META_MODE_BADABI_REVS above. host-osreldate.h: # DO NOT ADD /usr/include/osreldate.h here .if !defined(CROSSBUILD_HOST) @cp -f /usr/include/osreldate.h ${.TARGET} .else @echo "#ifndef __FreeBSD_version" > ${.TARGET} @echo "#define __FreeBSD_version ${OSRELDATE}" >> ${.TARGET} @echo "#endif" >> ${.TARGET} .endif WMAKE= ${TIME_ENV} ${WMAKEENV} ${MAKE} ${WORLD_FLAGS} -f Makefile.inc1 \ BWPHASE=${.TARGET:C,^_,,} \ DESTDIR=${WORLDTMP} IMAKEENV= ${CROSSENV} IMAKE= ${TIME_ENV} ${IMAKEENV} ${MAKE} -f Makefile.inc1 \ ${IMAKE_INSTALL} ${IMAKE_MTREE} .if empty(.MAKEFLAGS:M-n) IMAKEENV+= PATH=${STRICTTMPPATH:Q}:${INSTALLTMP:Q} \ LD_LIBRARY_PATH=${INSTALLTMP:Q} \ PATH_LOCALE=${INSTALLTMP}/locale IMAKE+= __MAKE_SHELL=${INSTALLTMP}/sh .else IMAKEENV+= PATH=${TMPPATH:Q}:${INSTALLTMP:Q} .endif # When generating install media, do not allow user and group information from # the build host to affect the contents of the distribution. .if make(distributeworld) || make(distrib-dirs) || make(distribution) || \ make(stageworld) DB_FROM_SRC= yes .endif .if defined(DB_FROM_SRC) INSTALLFLAGS+= -N ${.CURDIR}/etc MTREEFLAGS+= -N ${.CURDIR}/etc .endif _INSTALL_DDIR= ${DESTDIR}/${DISTDIR} INSTALL_DDIR= ${_INSTALL_DDIR:S://:/:g:C:/$::} .if defined(NO_ROOT) METALOG?= ${DESTDIR}/${DISTDIR}/METALOG METALOG:= ${METALOG:C,//+,/,g} IMAKE+= -DNO_ROOT METALOG=${METALOG} METALOG_INSTALLFLAGS= -U -M ${METALOG} -D ${INSTALL_DDIR} INSTALLFLAGS+= ${METALOG_INSTALLFLAGS} CERTCTLFLAGS= ${METALOG_INSTALLFLAGS} MTREEFLAGS+= -W .endif .if defined(BUILD_PKGS) INSTALLFLAGS+= -h sha256 .endif .if defined(DB_FROM_SRC) || defined(NO_ROOT) IMAKE_INSTALL= INSTALL="${INSTALL_CMD} ${INSTALLFLAGS}" IMAKE_MTREE= MTREE_CMD="${MTREE_CMD} ${MTREEFLAGS}" .endif .if make(distributeworld) CERTCTLDESTDIR= ${DESTDIR}/${DISTDIR} CERTCTLFLAGS+= -d /base .else CERTCTLDESTDIR= ${DESTDIR} .endif CERTCTLFLAGS+= -D "${CERTCTLDESTDIR}" DESTDIR_MTREEFLAGS= -deU # When creating worldtmp we don't need to set the directories as owned by root # so we also pass -W WORLDTMP_MTREEFLAGS= -deUW .if defined(NO_ROOT) # When building with -DNO_ROOT we shouldn't be changing the directories # that are created by mtree to be owned by root/wheel. DESTDIR_MTREEFLAGS+= -W .endif .if defined(DB_FROM_SRC) DISTR_MTREEFLAGS= -N ${.CURDIR}/etc .endif DISTR_MTREECMD= ${MTREE_CMD} .if ${BUILD_WITH_STRICT_TMPPATH} != 0 DISTR_MTREECMD= ${WORLDTMP}/legacy/usr/sbin/mtree .endif DISTR_MTREE= ${DISTR_MTREECMD} ${DISTR_MTREEFLAGS} WORLDTMP_MTREE= ${DISTR_MTREECMD} ${WORLDTMP_MTREEFLAGS} DESTDIR_MTREE= ${DISTR_MTREECMD} ${DESTDIR_MTREEFLAGS} METALOG_SORT_CMD= env -i LC_COLLATE=C sort # kernel stage KMAKEENV= ${WMAKEENV:NSYSROOT=*} KMAKE= ${TIME_ENV} ${KMAKEENV} ${MAKE} ${.MAKEFLAGS} ${KERNEL_FLAGS} KERNEL=${INSTKERNNAME} # # buildworld # # Attempt to rebuild the entire system, with reasonable chance of # success, regardless of how old your existing system is. # _sanity_check: .PHONY .MAKE .if ${.CURDIR:C/[^,]//g} != "" # The m4 build of sendmail files doesn't like it if ',' is used # anywhere in the path of it's files. @echo @echo "*** Error: path to source tree contains a comma ','" @echo @false .elif ${.CURDIR:M*\:*} != "" # Using ':' leaks into PATH and breaks finding cross-tools. @echo @echo "*** Error: path to source tree contains a colon ':'" @echo @false .endif # Our current approach to dependency tracking cannot cope with certain source # tree changes, particularly with respect to removing source files and # replacing generated files. Handle these cases here in an ad-hoc fashion. _cleanobj_fast_depend_hack: .PHONY @echo ">>> Deleting stale dependencies..."; MACHINE=${MACHINE} MACHINE_ARCH=${MACHINE_ARCH} \ ALL_libcompats=${_ALL_libcompats:Q} \ sh ${.CURDIR}/tools/build/depend-cleanup.sh ${OBJTOP} _cleanworldtmp: .PHONY .if ${MK_CLEAN} == "yes" @echo @echo "--------------------------------------------------------------" @echo ">>> Cleaning up the temporary build tree" @echo "--------------------------------------------------------------" rm -rf ${WORLDTMP} .else # Note: for delete-old we need to set $PATH to also include the host $PATH # since otherwise a partial build with missing symlinks in ${WORLDTMP}/legacy/ # will fail to run due to missing binaries. $WMAKE sets PATH to only ${TMPPATH} # so we remove that assingnment from $WMAKE and prepend the new $PATH ${_+_}@if [ -e "${WORLDTMP}" ]; then \ echo ">>> Deleting stale files in build tree..."; \ cd ${.CURDIR}; env PATH=${TMPPATH:Q}:${PATH:Q} ${WMAKE:NPATH=*} \ _NO_INCLUDE_COMPILERMK=t -DBATCH_DELETE_OLD_FILES delete-old \ delete-old-libs >/dev/null; \ fi rm -rf ${WORLDTMP}/legacy/usr/include .if ${USING_SYSTEM_COMPILER} == "yes" .for cc in cc c++ if [ -x ${WORLDTMP}/usr/bin/${cc} ]; then \ inum=$$(stat -f %i ${WORLDTMP}/usr/bin/${cc}); \ find ${WORLDTMP}/usr/bin -inum $${inum} -delete; \ fi .endfor .endif # ${USING_SYSTEM_COMPILER} == "yes" .if ${USING_SYSTEM_LINKER} == "yes" @rm -f ${WORLDTMP}/usr/bin/ld ${WORLDTMP}/usr/bin/ld.lld .endif # ${USING_SYSTEM_LINKER} == "yes" .endif # ${MK_CLEAN} == "yes" _worldtmp: .PHONY @echo @echo "--------------------------------------------------------------" @echo ">>> Rebuilding the temporary build tree" @echo "--------------------------------------------------------------" @mkdir -p ${WORLDTMP} @touch ${WORLDTMP}/${.TARGET} # We can't use mtree to create the worldtmp directories since it may not be # available on the target system (this happens e.g. when building on non-FreeBSD) ${_+_}cd ${.CURDIR}/tools/build; \ ${MAKE} DIRPRFX=tools/build/ DESTDIR=${WORLDTMP}/legacy installdirs # In order to build without inheriting $PATH we need to add symlinks to the host # tools in $WORLDTMP for the tools that we don't build during bootstrap-tools ${_+_}cd ${.CURDIR}/tools/build; \ ${MAKE} DIRPRFX=tools/build/ DESTDIR=${WORLDTMP}/legacy host-symlinks _legacy: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1.1: legacy release compatibility shims" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${BMAKE} legacy _bootstrap-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1.2: bootstrap tools" @echo "--------------------------------------------------------------" .if ${MK_CLEAN} != "yes" ${_+_}cd ${.CURDIR}; ${BMAKE} _NO_INCLUDE_COMPILERMK=t _cleanobj_fast_depend_hack .endif ${_+_}cd ${.CURDIR}; ${BMAKE} bootstrap-tools mkdir -p ${WORLDTMP}/usr ${WORLDTMP}/lib/geom ${WORLDTMP}/bin ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${WORLDTMP}/usr >/dev/null ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${WORLDTMP}/usr/include >/dev/null .for d in ${LIBCOMPAT_INCLUDE_DIRS} mkdir -p ${WORLDTMP}/usr/include/${d} .endfor ln -sf ${.CURDIR}/sys ${WORLDTMP} .if ${MK_DEBUG_FILES} != "no" ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.debug.dist \ -p ${WORLDTMP}/usr/lib >/dev/null .endif .for _mtree in ${LOCAL_MTREE} ${WORLDTMP_MTREE} -f ${.CURDIR}/${_mtree} -p ${WORLDTMP} > /dev/null .endfor _cleanobj: .if ${MK_CLEAN} == "yes" @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.1: cleaning up the object tree" @echo "--------------------------------------------------------------" # Avoid including bsd.compiler.mk in clean and obj with _NO_INCLUDE_COMPILERMK # since the restricted $PATH might not contain a valid cc binary ${_+_}cd ${.CURDIR}; ${WMAKE} _NO_INCLUDE_COMPILERMK=t ${CLEANDIR} .for LIBCOMPAT in ${_LIBCOMPATS} ${_+_}cd ${.CURDIR}; ${LIB${LIBCOMPAT}WMAKE} _NO_INCLUDE_COMPILERMK=t -f Makefile.inc1 ${CLEANDIR} .endfor .else ${_+_}cd ${.CURDIR}; ${WMAKE} _NO_INCLUDE_COMPILERMK=t _cleanobj_fast_depend_hack .endif # ${MK_CLEAN} == "yes" _obj: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.2: rebuilding the object tree" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} _NO_INCLUDE_COMPILERMK=t obj _build-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.3: build tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${TMAKE} build-tools _cross-tools: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3: cross tools" @echo "--------------------------------------------------------------" @rm -f ${OBJTOP}/toolchain-metadata.mk ${_+_}cd ${.CURDIR}; ${XMAKE} cross-tools ${_+_}cd ${.CURDIR}; ${XMAKE} kernel-tools _build-metadata: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3.1: recording build metadata" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${WMAKE} toolchain-metadata.mk ${_+_}cd ${.CURDIR}; ${WMAKE} host-osreldate.h _includes: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.1: building includes" @echo "--------------------------------------------------------------" # Special handling for SUBDIR_OVERRIDE in buildworld as they most likely need # headers from default SUBDIR. Do SUBDIR_OVERRIDE includes last. ${_+_}cd ${.CURDIR}; ${WMAKE} SUBDIR_OVERRIDE= SHARED=symlinks \ MK_INCLUDES=yes includes .if !empty(SUBDIR_OVERRIDE) && make(buildworld) ${_+_}cd ${.CURDIR}; ${WMAKE} MK_INCLUDES=yes SHARED=symlinks includes .endif ${_+_}cd ${.CURDIR}; ${WMAKE} test-includes _libraries: @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.2: building libraries" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; \ ${WMAKE} -DNO_FSCHG MK_HTML=no -DNO_LINT MK_MAN=no \ MK_PROFILE=no MK_TESTS=no MK_TESTS_SUPPORT=${MK_TESTS_SUPPORT} \ libraries everything: .PHONY @echo @echo "--------------------------------------------------------------" @echo ">>> stage 4.4: building everything" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; _PARALLEL_SUBDIR_OK=1 ${WMAKE} all WMAKE_TGTS= .if !defined(WORLDFAST) WMAKE_TGTS+= _sanity_check _cleanworldtmp _worldtmp _legacy .if empty(SUBDIR_OVERRIDE) WMAKE_TGTS+= _bootstrap-tools .endif WMAKE_TGTS+= _cleanobj .if !defined(NO_OBJWALK) WMAKE_TGTS+= _obj .endif WMAKE_TGTS+= _build-tools _cross-tools WMAKE_TGTS+= _build-metadata WMAKE_TGTS+= _includes .endif .if !defined(NO_LIBS) WMAKE_TGTS+= _libraries .endif .if empty(SUBDIR_OVERRIDE) .for libcompat in ${libcompats} WMAKE_TGTS+= build${libcompat} .endfor .endif WMAKE_TGTS+= everything # record buildworld time in seconds .if make(buildworld) _BUILDWORLD_START!= date '+%s' .export _BUILDWORLD_START .endif buildworld: buildworld_prologue ${WMAKE_TGTS} buildworld_epilogue .PHONY .ORDER: buildworld_prologue ${WMAKE_TGTS} buildworld_epilogue _ncpu_cmd=sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo unknown buildworld_prologue: .PHONY @echo "--------------------------------------------------------------" @echo ">>> World build started on `LC_ALL=C date`" @echo "--------------------------------------------------------------" buildworld_epilogue: .PHONY @echo @echo "--------------------------------------------------------------" @echo ">>> World build completed on `LC_ALL=C date`" @seconds=$$(($$(date '+%s') - ${_BUILDWORLD_START})); \ echo -n ">>> World built in $$seconds seconds, "; \ echo "ncpu: $$(${_ncpu_cmd})${.MAKE.JOBS:S/^/, make -j/}" @echo "--------------------------------------------------------------" # # We need to have this as a target because the indirection between Makefile # and Makefile.inc1 causes the correct PATH to be used, rather than a # modification of the current environment's PATH. In addition, we need # to quote multiword values. # buildenvvars: .PHONY @echo ${WMAKEENV:Q} ${.MAKE.EXPORTED:@v@$v=\"${$v}\"@} .if ${.TARGETS:Mbuildenv} .if ${.MAKEFLAGS:M-j} .error The buildenv target is incompatible with -j .endif .endif BUILDENV_DIR?= ${.CURDIR} # # Note: make will report any errors the shell reports. This can # be odd if the last command in an interactive shell generates an # error or is terminated by SIGINT. These reported errors look bad, # but are harmless. Allowing them also allows BUILDENV_SHELL to # be a complex command whose status will be returned to the caller. # Some scripts in tools rely on this behavior to report build errors. # buildenv: .PHONY @echo Entering world for ${TARGET_ARCH}:${TARGET} .if ${BUILDENV_SHELL:M*zsh*} @echo For ZSH you must run: export CPUTYPE=${TARGET_CPUTYPE} .endif @cd ${BUILDENV_DIR} && env ${WMAKEENV} \ INSTALL="${INSTALL_CMD} ${INSTALLFLAGS}" \ MTREE_CMD="${MTREE_CMD} ${MTREEFLAGS}" BUILDENV=1 ${BUILDENV_SHELL} TOOLCHAIN_TGTS= ${WMAKE_TGTS:Neverything:${libcompats:@v@Nbuild${v}@:ts:}} toolchain: ${TOOLCHAIN_TGTS} .PHONY KERNEL_TOOLCHAIN_TGTS= ${TOOLCHAIN_TGTS:N_obj:N_cleanobj:N_includes:N_libraries} .if make(kernel-toolchain) .ORDER: ${KERNEL_TOOLCHAIN_TGTS} .endif kernel-toolchain: ${KERNEL_TOOLCHAIN_TGTS} .PHONY # # installcheck # # Checks to be sure system is ready for installworld/installkernel. # installcheck: _installcheck_world _installcheck_kernel .PHONY _installcheck_world: .PHONY @echo "--------------------------------------------------------------" @echo ">>> Install check world" @echo "--------------------------------------------------------------" _installcheck_kernel: .PHONY @echo "--------------------------------------------------------------" @echo ">>> Install check kernel" @echo "--------------------------------------------------------------" # # Require DESTDIR to be set if installing for a different architecture or # using the user/group database in the source tree. # .if ${TARGET_ARCH} != ${MACHINE_ARCH} || ${TARGET} != ${MACHINE} || \ defined(DB_FROM_SRC) .if !make(distributeworld) _installcheck_world: __installcheck_DESTDIR _installcheck_kernel: __installcheck_DESTDIR __installcheck_DESTDIR: .PHONY .if !defined(DESTDIR) || empty(DESTDIR) @echo "ERROR: Please set DESTDIR!"; \ false .endif .endif .endif .if !defined(DB_FROM_SRC) # # Check for missing UIDs/GIDs. # CHECK_UIDS= auditdistd CHECK_GIDS= audit CHECK_UIDS+= ntpd CHECK_GIDS+= ntpd CHECK_UIDS+= proxy CHECK_GIDS+= proxy authpf CHECK_UIDS+= smmsp CHECK_GIDS+= smmsp CHECK_UIDS+= unbound CHECK_GIDS+= unbound _installcheck_world: __installcheck_UGID __installcheck_UGID: .PHONY .for uid in ${CHECK_UIDS} @if ! `id -u ${uid} >/dev/null 2>&1`; then \ echo "ERROR: Required ${uid} user is missing, see /usr/src/UPDATING."; \ false; \ fi .endfor .for gid in ${CHECK_GIDS} @if ! `find / -prune -group ${gid} >/dev/null 2>&1`; then \ echo "ERROR: Required ${gid} group is missing, see /usr/src/UPDATING."; \ false; \ fi .endfor .endif # # If installing over the running system (DESTDIR is / or unset) and the install # includes rescue, try running rescue from the objdir as a sanity check. If # rescue is not functional (e.g., because it depends on a system call not # supported by the currently running kernel), abort the installation. # .if !make(distributeworld) && ${MK_RESCUE} != "no" && \ (empty(DESTDIR) || ${DESTDIR} == "/") && empty(BYPASS_INSTALLCHECK_SH) _installcheck_world: __installcheck_sh_check __installcheck_sh_check: .PHONY @if [ "`${OBJTOP}/rescue/rescue/rescue sh -c 'echo OK'`" != \ OK ]; then \ echo "rescue/sh check failed, installation aborted" >&2; \ false; \ fi .endif # # Required install tools to be saved in a scratch dir for safety. # .if !defined(CROSSBUILD_HOST) _sysctl=sysctl .endif ITOOLS= [ awk cap_mkdb cat chflags chmod chown cmp cp \ date echo egrep find grep id install ${_install-info} \ ln make mkdir mtree mv pwd_mkdb \ rm sed services_mkdb sh sort strip ${_sysctl} test time true uname wc .if ${MK_ZONEINFO} != "no" ITOOLS+=tzsetup .endif # Needed for share/man .if ${MK_MAN_UTILS} != "no" ITOOLS+=makewhatis .endif ITOOLS+=${LOCAL_ITOOLS} # # distributeworld # # Distributes everything compiled by a `buildworld'. # # installworld # # Installs everything compiled by a 'buildworld'. # # Non-base distributions produced by the base system EXTRA_DISTRIBUTIONS= .for libcompat in ${libcompats} EXTRA_DISTRIBUTIONS+= lib${libcompat} .endfor .if ${MK_TESTS} != "no" EXTRA_DISTRIBUTIONS+= tests .endif DEBUG_DISTRIBUTIONS= .if ${MK_DEBUG_FILES} != "no" DEBUG_DISTRIBUTIONS+= base ${EXTRA_DISTRIBUTIONS:S,tests,,} .endif MTREE_MAGIC?= mtree 2.0 distributeworld installworld stageworld: _installcheck_world .PHONY mkdir -p ${INSTALLTMP} progs=$$(for prog in ${ITOOLS}; do \ if progpath=`env PATH=${TMPPATH:Q} which $$prog`; then \ echo $$progpath; \ else \ echo "Required tool $$prog not found in PATH ("${TMPPATH:Q}")." >&2; \ exit 1; \ fi; \ done); \ if [ -z "${CROSSBUILD_HOST}" ] ; then \ libs=$$(ldd -f "%o %p\n" -f "%o %p\n" $$progs 2>/dev/null | sort -u | grep -Ev '\[.*]' | \ while read line; do \ set -- $$line; \ if [ "$$2 $$3" != "not found" ]; then \ echo $$2; \ else \ echo "Required library $$1 not found." >&2; \ exit 1; \ fi; \ done); \ fi; \ ${INSTALLTMP_COPY_HOST_TOOL} $$libs $$progs ${INSTALLTMP} cp -R $${PATH_LOCALE:-"/usr/share/locale"} ${INSTALLTMP}/locale .if defined(NO_ROOT) -mkdir -p ${METALOG:H} echo "#${MTREE_MAGIC}" > ${METALOG} .endif .if make(distributeworld) .for dist in ${EXTRA_DISTRIBUTIONS} -mkdir ${DESTDIR}/${DISTDIR}/${dist} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.root.dist \ -p ${DESTDIR}/${DISTDIR}/${dist} >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/include >/dev/null .for d in ${LIBCOMPAT_INCLUDE_DIRS} -mkdir ${DESTDIR}/${DISTDIR}/${dist}/usr/include/${d} .endfor .if ${MK_DEBUG_FILES} != "no" ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.debug.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/lib >/dev/null .endif .for libcompat in ${libcompats} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.lib${libcompat}.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr >/dev/null .if ${MK_DEBUG_FILES} != "no" ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.lib${libcompat}.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/lib/debug/usr >/dev/null .endif .endfor .if ${MK_TESTS} != "no" && ${dist} == "tests" -mkdir -p ${DESTDIR}/${DISTDIR}/${dist}${TESTSBASE} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}${TESTSBASE} >/dev/null .if ${MK_DEBUG_FILES} != "no" ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${DESTDIR}/${DISTDIR}/${dist}/usr/lib/debug/${TESTSBASE} >/dev/null .endif .endif .if defined(NO_ROOT) ${IMAKEENV} ${DISTR_MTREE} -C -f ${.CURDIR}/etc/mtree/BSD.root.dist | \ sed -e 's#^\./#./${dist}/#' >> ${METALOG} ${IMAKEENV} ${DISTR_MTREE} -C -f ${.CURDIR}/etc/mtree/BSD.usr.dist | \ sed -e 's#^\./#./${dist}/usr/#' >> ${METALOG} ${IMAKEENV} ${DISTR_MTREE} -C -f ${.CURDIR}/etc/mtree/BSD.include.dist | \ sed -e 's#^\./#./${dist}/usr/include/#' >> ${METALOG} .for d in ${LIBCOMPAT_INCLUDE_DIRS} echo "./${dist}/usr/include/${d} type=dir uname=root gname=wheel mode=0755" >> ${METALOG} .endfor .for libcompat in ${libcompats} ${IMAKEENV} ${DISTR_MTREE} -C -f ${.CURDIR}/etc/mtree/BSD.lib${libcompat}.dist | \ sed -e 's#^\./#./${dist}/usr/#' >> ${METALOG} .endfor .endif .endfor -mkdir ${DESTDIR}/${DISTDIR}/base ${_+_}cd ${.CURDIR}/etc; ${CROSSENV} PATH=${TMPPATH:Q} ${MAKE} \ METALOG=${METALOG} ${IMAKE_INSTALL} ${IMAKE_MTREE} \ DISTBASE=/base DESTDIR=${INSTALL_DDIR}/base \ LOCAL_MTREE=${LOCAL_MTREE:Q} distrib-dirs ${INSTALL_SYMLINK} ${INSTALLFLAGS} usr/src/sys ${INSTALL_DDIR}/base/sys .endif # make(distributeworld) ${_+_}cd ${.CURDIR}; ${IMAKE} re${.TARGET:S/world$//}; \ ${IMAKEENV} rm -rf ${INSTALLTMP} .if !make(packageworld) && ${MK_CAROOT} != "no" @if which openssl>/dev/null; then \ PATH=${TMPPATH:Q}:${PATH:Q} \ sh ${SRCTOP}/usr.sbin/certctl/certctl.sh ${CERTCTLFLAGS} rehash; \ else \ echo "No openssl on the host, not rehashing certificates target -- /etc/ssl may not be populated."; \ fi .endif .if make(distributeworld) .for dist in ${EXTRA_DISTRIBUTIONS} find ${DESTDIR}/${DISTDIR}/${dist} -mindepth 1 -type d -empty -delete .endfor .if defined(NO_ROOT) .for dist in base ${EXTRA_DISTRIBUTIONS} @# For each file that exists in this dist, print the corresponding @# line from the METALOG. This relies on the fact that @# a line containing only the filename will sort immediately before @# the relevant mtree line. cd ${DESTDIR}/${DISTDIR}; \ find ./${dist} | ${METALOG_SORT_CMD} -u ${METALOG} - | \ awk 'BEGIN { print "#${MTREE_MAGIC}" } !/ type=/ { file = $$1 } / type=/ { if ($$1 == file) { sub(/^\.\/${dist}/, "."); print } }' > \ ${DESTDIR}/${DISTDIR}/${dist}.meta .endfor .for dist in ${DEBUG_DISTRIBUTIONS} @# For each file that exists in this dist, print the corresponding @# line from the METALOG. This relies on the fact that @# a line containing only the filename will sort immediately before @# the relevant mtree line. cd ${DESTDIR}/${DISTDIR}; \ find ./${dist}/usr/lib/debug | ${METALOG_SORT_CMD} -u ${METALOG} - | \ awk 'BEGIN { print "#${MTREE_MAGIC}" } !/ type=/ { file = $$1 } / type=/ { if ($$1 == file) { sub(/^\.\/${dist}/, "."); print } }' > \ ${DESTDIR}/${DISTDIR}/${dist}.debug.meta .endfor .endif .endif # make(distributeworld) packageworld: .PHONY .for dist in base ${EXTRA_DISTRIBUTIONS} .if defined(NO_ROOT) ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ ${TAR_CMD} cvf - --exclude usr/lib/debug \ @${DESTDIR}/${DISTDIR}/${dist}.meta | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}.txz .else ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ ${TAR_CMD} cvf - --exclude usr/lib/debug . | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}.txz .endif .endfor .for dist in ${DEBUG_DISTRIBUTIONS} . if defined(NO_ROOT) ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ ${TAR_CMD} cvf - @${DESTDIR}/${DISTDIR}/${dist}.debug.meta | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}-dbg.txz . else ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ ${TAR_CMD} cvLf - usr/lib/debug | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}-dbg.txz . endif .endfor makeman: .PHONY ${_+_}cd ${.CURDIR}/tools/build/options; sh makeman > \ ${.CURDIR}/share/man/man5/src.conf.5 # Ensure no regressions in self-includeability of sys/*.h and net*/*.h test-includes: .PHONY ${_+_}cd ${.CURDIR}/tools/build/test-includes; \ ${WMAKEENV} ${MAKE} ${WORLD_FLAGS} DESTDIR=${WORLDTMP} test-includes # We can't assume here that ${TMPPATH} will include ${PATH} or /usr/libexec # because we may be building with a STRICTTMPPATH, so we explicitly include # /usr/libexec here for flua. ${TMPPATH} still usefully includes anything else # we may need to function. _sysent_PATH= ${TMPPATH}:/usr/libexec _sysent_dirs= sys/kern _sysent_dirs+= sys/compat/freebsd32 _sysent_dirs+= sys/amd64/linux \ sys/amd64/linux32 \ sys/arm64/linux \ sys/i386/linux sysent: .PHONY .for _dir in ${_sysent_dirs} sysent-${_dir}: .PHONY @echo "${MAKE} -C ${.CURDIR}/${_dir} sysent" ${_+_}@env PATH=${_sysent_PATH:Q} ${MAKE} -C ${.CURDIR}/${_dir} sysent sysent: sysent-${_dir} .endfor # # reinstall # # If you have a build server, you can NFS mount the source and obj directories # and do a 'make reinstall' on the *client* to install new binaries from the # most recent server build. # restage reinstall: .MAKE .PHONY @echo "--------------------------------------------------------------" @echo ">>> Making hierarchy" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 \ LOCAL_MTREE=${LOCAL_MTREE:Q} hierarchy .if make(restage) @echo "--------------------------------------------------------------" @echo ">>> Making distribution" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 \ LOCAL_MTREE=${LOCAL_MTREE:Q} distribution .endif @echo @echo "--------------------------------------------------------------" @echo ">>> Installing everything started on `LC_ALL=C date`" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 install .for libcompat in ${libcompats} ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 install${libcompat} .endfor @echo "--------------------------------------------------------------" @echo ">>> Installing everything completed on `LC_ALL=C date`" @echo "--------------------------------------------------------------" redistribute: .MAKE .PHONY @echo "--------------------------------------------------------------" @echo ">>> Distributing everything" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 distribute .for libcompat in ${libcompats} ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 distribute${libcompat} \ DISTRIBUTION=lib${libcompat} .endfor distrib-dirs distribution: .MAKE .PHONY ${_+_}cd ${.CURDIR}/etc; ${CROSSENV} PATH=${TMPPATH:Q} ${MAKE} \ ${IMAKE_INSTALL} ${IMAKE_MTREE} METALOG=${METALOG} ${.TARGET} .if make(distribution) ${_+_}cd ${.CURDIR}; ${CROSSENV} PATH=${TMPPATH:Q} \ ${MAKE} -f Makefile.inc1 ${IMAKE_INSTALL} \ METALOG=${METALOG} MK_TESTS=no \ MK_TESTS_SUPPORT=${MK_TESTS_SUPPORT} installconfig .endif # # buildetc and installetc # buildetc: .MAKE .PHONY ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 _worldtmp ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 _legacy ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 _bootstrap-tools \ MK_CROSS_COMPILER=no MK_TOOLCHAIN=no ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 _obj \ SUBDIR_OVERRIDE=etc ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 everything \ SUBDIR_OVERRIDE=etc installetc: .MAKE .PHONY @echo "--------------------------------------------------------------" @echo ">>> Making hierarchy" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 distrib-dirs @echo "--------------------------------------------------------------" @echo ">>> Making distribution" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${MAKE} -f Makefile.inc1 distribution # # buildkernel and installkernel # # Which kernels to build and/or install is specified by setting # KERNCONF. If not defined a GENERIC kernel is built/installed. # Only the existing (depending TARGET) config files are used # for building kernels and only the first of these is designated # as the one being installed. # # Note that we have to use TARGET instead of TARGET_ARCH when # we're in kernel-land. Since only TARGET_ARCH is (expected) to # be set to cross-build, we have to make sure TARGET is set # properly. .if defined(KERNFAST) NO_KERNELCLEAN= t NO_KERNELCONFIG= t NO_KERNELOBJ= t # Shortcut for KERNCONF=Blah -DKERNFAST is now KERNFAST=Blah .if !defined(KERNCONF) && ${KERNFAST} != "1" KERNCONF=${KERNFAST} .endif .endif GENERIC_KERNCONF_SUFX_powerpc64= 64 GENERIC_KERNCONF_SUFX_powerpc64le= 64LE GENERIC_KERNCONF_powerpcspe= MPC85XXSPE GENERIC_KERNCONF?= ${GENERIC_KERNCONF_${TARGET_ARCH}:UGENERIC${GENERIC_KERNCONF_SUFX_${TARGET_ARCH}}} INSTKERNNAME?= kernel KERNSRCDIR?= ${.CURDIR}/sys KRNLCONFDIR= ${KERNSRCDIR}/${TARGET}/conf KRNLOBJDIR= ${OBJTOP}${KERNSRCDIR:C,^${.CURDIR},,} KERNCONFDIR?= ${KRNLCONFDIR} .for _k in ${GENERIC_KERNCONF} MINIMAL${GENERIC_KERNCONF_SUFX_${TARGET_ARCH}} ${GENERIC_KERNCONF}-MMCCAM .if exists(${KERNCONFDIR}/${_k}) PKG_KERNCONF+= ${_k} .for _dbg in NODEBUG DEBUG .if exists(${KERNCONFDIR}/${_k}-${_dbg}) PKG_KERNCONF+= ${_k}-${_dbg} .endif .endfor .endif .endfor .if defined(PACKAGE_BUILDING) KERNCONF?= ${PKG_KERNCONF} .else KERNCONF?= ${GENERIC_KERNCONF} .endif BUILDKERNELS= INSTALLKERNEL= .if defined(NO_INSTALLKERNEL) # All of the BUILDKERNELS loops start at index 1. BUILDKERNELS+= dummy .endif .for _kernel in ${KERNCONF} .if !defined(_MKSHOWCONFIG) && exists(${KERNCONFDIR}/${_kernel}) BUILDKERNELS+= ${_kernel} .if empty(INSTALLKERNEL) && !defined(NO_INSTALLKERNEL) INSTALLKERNEL= ${_kernel} .endif .else .if make(buildkernel) .error Missing KERNCONF ${KERNCONFDIR}/${_kernel} .endif .endif .endfor _cleankernobj_fast_depend_hack: .PHONY # 20191009 r353340 removal of opensolaris_atomic.S (also r353381) .if ${MACHINE} != i386 .for f in opensolaris_atomic .for m in opensolaris zfs @if [ -e "${KRNLOBJDIR}/${KERNCONF}/modules${SRCTOP}/sys/modules/${m}/.depend.${f}.o" ] && \ grep -q ${f}.S "${KRNLOBJDIR}/${KERNCONF}/modules${SRCTOP}/sys/modules/${m}/.depend.${f}.o"; then \ echo "Removing stale dependencies for opensolaris_atomic"; \ rm -f ${KRNLOBJDIR}/${KERNCONF}/modules${SRCTOP}/sys/modules/${m}/.depend.${f}.*; \ fi .endfor .endfor .endif ${WMAKE_TGTS:N_cleanworldtmp:N_worldtmp:${libcompats:@v@Nbuild${v}@:ts:}}: .MAKE .PHONY ${.ALLTARGETS:M_*:N_cleanworldtmp:N_worldtmp}: .MAKE .PHONY # record kernel(s) build time in seconds .if make(buildkernel) _BUILDKERNEL_START!= date '+%s' .endif # # buildkernel # # Builds all kernels defined by BUILDKERNELS. # buildkernel: .MAKE .PHONY .if empty(BUILDKERNELS:Ndummy) @echo "ERROR: Missing kernel configuration file(s) (${KERNCONF})."; \ false .endif @echo .for _kernel in ${BUILDKERNELS:Ndummy} @echo "--------------------------------------------------------------" @echo ">>> Kernel build for ${_kernel} started on `LC_ALL=C date`" @echo "--------------------------------------------------------------" @echo "===> ${_kernel}" mkdir -p ${KRNLOBJDIR} .if !defined(NO_KERNELCONFIG) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 1: configuring the kernel" @echo "--------------------------------------------------------------" cd ${KRNLCONFDIR}; \ PATH=${TMPPATH:Q} \ config ${CONFIGARGS} -d ${KRNLOBJDIR}/${_kernel} \ -I '${KERNCONFDIR}' -I '${KRNLCONFDIR}' \ '${KERNCONFDIR}/${_kernel}' .endif .if ${MK_CLEAN} == "yes" && !defined(NO_KERNELCLEAN) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.1: cleaning up the object tree" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} ${CLEANDIR} .else ${_+_}cd ${.CURDIR}; ${WMAKE} _cleankernobj_fast_depend_hack .endif .if !defined(NO_KERNELOBJ) @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.2: rebuilding the object tree" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} obj .endif @echo @echo "--------------------------------------------------------------" @echo ">>> stage 2.3: build tools" @echo "--------------------------------------------------------------" ${_+_}cd ${.CURDIR}; ${KTMAKE} kernel-tools @echo @echo "--------------------------------------------------------------" @echo ">>> stage 3.1: building everything" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${_kernel}; ${KMAKE} all -DNO_MODULES_OBJ @echo "--------------------------------------------------------------" @echo ">>> Kernel build for ${_kernel} completed on `LC_ALL=C date`" @echo "--------------------------------------------------------------" .endfor @seconds=$$(($$(date '+%s') - ${_BUILDKERNEL_START})); \ echo -n ">>> Kernel(s) ${BUILDKERNELS} built in $$seconds seconds, "; \ echo "ncpu: $$(${_ncpu_cmd})${.MAKE.JOBS:S/^/, make -j/}" @echo "--------------------------------------------------------------" .if !make(packages) && !make(update-packages) NO_INSTALLEXTRAKERNELS?= yes .else # packages/update-packages installs kernels to a staging directory then builds # packages from the result to be installed, typically to other systems. It is # less surprising for these targets to honor KERNCONF if multiple kernels are # specified. NO_INSTALLEXTRAKERNELS?= no .endif # # installkernel, etc. # # Install the kernel defined by INSTALLKERNEL # installkernel installkernel.debug \ reinstallkernel reinstallkernel.debug: _installcheck_kernel .PHONY .if !defined(NO_INSTALLKERNEL) .if empty(INSTALLKERNEL) @echo "ERROR: No kernel \"${KERNCONF}\" to install."; \ false .endif @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${INSTALLKERNEL} on $$(LC_ALL=C date)" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${INSTALLKERNEL}; \ ${CROSSENV} PATH=${TMPPATH:Q} \ ${MAKE} ${IMAKE_INSTALL} KERNEL=${INSTKERNNAME} METALOG=${METALOG} \ ${.TARGET:S/kernel//} @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${INSTALLKERNEL} completed on $$(LC_ALL=C date)" @echo "--------------------------------------------------------------" .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${_kernel} $$(LC_ALL=C date)" @echo "--------------------------------------------------------------" ${_+_}cd ${KRNLOBJDIR}/${_kernel}; \ ${CROSSENV} PATH=${TMPPATH:Q} \ ${MAKE} ${IMAKE_INSTALL} KERNEL=${INSTKERNNAME}.${_kernel} METALOG=${METALOG} \ ${.TARGET:S/kernel//} @echo "--------------------------------------------------------------" @echo ">>> Installing kernel ${_kernel} completed on $$(LC_ALL=C date)" @echo "--------------------------------------------------------------" .endfor .endif distributekernel distributekernel.debug: .PHONY .if !defined(NO_INSTALLKERNEL) .if empty(INSTALLKERNEL) @echo "ERROR: No kernel \"${KERNCONF}\" to install."; \ false .endif mkdir -p ${DESTDIR}/${DISTDIR} .if defined(NO_ROOT) @echo "#${MTREE_MAGIC}" > ${DESTDIR}/${DISTDIR}/kernel.premeta .endif ${_+_}cd ${KRNLOBJDIR}/${INSTALLKERNEL}; \ ${IMAKEENV} ${IMAKE_INSTALL:S/METALOG/kernel.premeta/} \ ${IMAKE_MTREE} PATH=${TMPPATH:Q} ${MAKE} KERNEL=${INSTKERNNAME} \ DISTBASE=/kernel DESTDIR=${INSTALL_DDIR}/kernel \ METALOG=${METALOG:S/METALOG/kernel.premeta/} \ ${.TARGET:S/distributekernel/install/} .if defined(NO_ROOT) @sed -e 's|^./kernel|.|' ${DESTDIR}/${DISTDIR}/kernel.premeta > \ ${DESTDIR}/${DISTDIR}/kernel.meta .endif .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} .if defined(NO_ROOT) @echo "#${MTREE_MAGIC}" > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.premeta .endif ${_+_}cd ${KRNLOBJDIR}/${_kernel}; \ ${IMAKEENV} ${IMAKE_INSTALL:S/METALOG/kernel.${_kernel}.premeta/} \ ${IMAKE_MTREE} PATH=${TMPPATH:Q} ${MAKE} \ KERNEL=${INSTKERNNAME}.${_kernel} \ DISTBASE=/kernel.${_kernel} DESTDIR=${INSTALL_DDIR}/kernel.${_kernel} \ METALOG=${METALOG:S/METALOG/kernel.${_kernel}.premeta/} \ ${.TARGET:S/distributekernel/install/} .if defined(NO_ROOT) @sed -e "s|^./kernel.${_kernel}|.|" \ ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.premeta > \ ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta .endif .endfor .endif packagekernel: .PHONY .if defined(NO_ROOT) .if !defined(NO_INSTALLKERNEL) cd ${DESTDIR}/${DISTDIR}/kernel; \ ${TAR_CMD} cvf - --exclude '*.debug' \ @${DESTDIR}/${DISTDIR}/kernel.meta | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.txz .endif .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel; \ ${TAR_CMD} cvf - --include '*/*/*.debug' \ @${DESTDIR}/${DISTDIR}/kernel.meta | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel-dbg.txz .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ ${TAR_CMD} cvf - --exclude '*.debug' \ @${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.${_kernel}.txz .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ ${TAR_CMD} cvf - --include '*/*/*.debug' \ @${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}-dbg.txz .endif .endfor .endif .else .if !defined(NO_INSTALLKERNEL) cd ${DESTDIR}/${DISTDIR}/kernel; \ ${TAR_CMD} cvf - --exclude '*.debug' . | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.txz .endif .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel; \ ${TAR_CMD} cvf - --include '*/*/*.debug' $$(eval find .) | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel-dbg.txz .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ ${TAR_CMD} cvf - --exclude '*.debug' . | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.${_kernel}.txz .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ ${TAR_CMD} cvf - --include '*/*/*.debug' $$(eval find .) | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}-dbg.txz .endif .endfor .endif .endif stagekernel: .PHONY ${_+_}${MAKE} -C ${.CURDIR} ${.MAKEFLAGS} distributekernel PORTSDIR?= /usr/ports WSTAGEDIR?= ${OBJTOP}/worldstage KSTAGEDIR?= ${OBJTOP}/kernelstage SSTAGEDIR?= ${OBJTOP}/sourcestage REPODIR?= ${OBJROOT}repo PKG_FORMAT?= tzst PKG_REPO_SIGNING_KEY?= # empty PKG_OUTPUT_DIR?= ${PKG_VERSION} .ORDER: stage-packages create-packages .ORDER: create-packages create-world-packages .ORDER: create-packages create-source-packages .ORDER: create-packages create-kernel-packages .ORDER: create-packages sign-packages _pkgbootstrap: .PHONY .if make(*package*) && !exists(${LOCALBASE}/sbin/pkg) @env ASSUME_ALWAYS_YES=YES pkg bootstrap .endif # # Determine PKG_ABI from newvers.sh if not already set. # .if !defined(PKG_ABI) && (make(create-world-packages-jobs) || make(create-kernel-packages*) || make(real-update-packages) || make (create-source-packages) || make(sign-packages)) PKG_ABI=${_TYPE}:${MAJOR_REVISION}:${TARGET_ARCH} .endif PKG_BIN_VERSION!=${PKG_CMD} --version /dev/null |\ awk -F. '/^[0-9.]+$$/ {print $$1 * 10000 + $$2 * 100 + $$3}' .if ${PKG_BIN_VERSION} < 11700 PKG_EXT= ${PKG_FORMAT} .else PKG_EXT= pkg .endif .if !defined(PKG_VERSION_FROM) && make(real-update-packages) .if defined(PKG_ABI) .if exists(${REPODIR}/${PKG_ABI}) PKG_VERSION_FROM!=/usr/bin/readlink ${REPODIR}/${PKG_ABI}/latest PKG_VERSION_FROM_DIR= ${REPODIR}/${PKG_ABI}/${PKG_VERSION_FROM} .else PKG_VERSION_FROM= PKG_VERSION_FROM_DIR= .endif .endif .endif PKGMAKEARGS+= PKG_VERSION=${PKG_VERSION} \ NO_INSTALLEXTRAKERNELS=${NO_INSTALLEXTRAKERNELS} packages: .PHONY ${_+_}${MAKE} -C ${.CURDIR} ${PKGMAKEARGS} real-packages update-packages: .PHONY ${_+_}${MAKE} -C ${.CURDIR} ${PKGMAKEARGS} real-update-packages package-pkg: .PHONY rm -rf /tmp/ports.${TARGET} || : env ${WMAKEENV:Q} SRCDIR=${.CURDIR} PORTSDIR=${PORTSDIR} REVISION=${_REVISION} \ PKG_CMD=${PKG_CMD} PKG_VERSION=${PKG_VERSION} REPODIR=${REPODIR} \ WSTAGEDIR=${WSTAGEDIR} \ OSVERSION="${SRCRELDATE}" \ sh ${.CURDIR}/release/scripts/make-pkg-package.sh real-packages: stage-packages create-packages sign-packages .PHONY real-update-packages: stage-packages .PHONY ${_+_}${MAKE} -C ${.CURDIR} PKG_VERSION=${PKG_VERSION} create-packages .if empty(PKG_VERSION_FROM_DIR) @echo "==> Bootstrapping repository, not checking for new packages" .else @echo "==> Checking for new packages (comparing ${PKG_VERSION} to ${PKG_VERSION_FROM})" @for pkg in ${PKG_VERSION_FROM_DIR}/${PKG_NAME_PREFIX}-*; do \ pkgname=$$(pkg query -F $${pkg} '%n' | sed 's/${PKG_NAME_PREFIX}-\(.*\)/\1/') ; \ newpkgname=${PKG_NAME_PREFIX}-$${pkgname}-${PKG_VERSION}.${PKG_EXT} ; \ oldsum=$$(pkg query -F $${pkg} '%X') ; \ if [ ! -f ${REPODIR}/${PKG_ABI}/${PKG_VERSION}/$${newpkgname} ]; then \ continue; \ fi ; \ newsum=$$(pkg query -F ${REPODIR}/${PKG_ABI}/${PKG_VERSION}/$${newpkgname} '%X') ; \ if [ "$${oldsum}" == "$${newsum}" ]; then \ echo "==> Keeping old ${PKG_NAME_PREFIX}-$${pkgname}-${PKG_VERSION_FROM}.${PKG_EXT}" ; \ rm ${REPODIR}/${PKG_ABI}/${PKG_VERSION}/$${newpkgname} ; \ cp $${pkg} ${REPODIR}/${PKG_ABI}/${PKG_VERSION} ; \ else \ echo "==> New package $${newpkgname}" ; \ fi ; \ done .endif ${_+_}@cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 PKG_VERSION=${PKG_VERSION} sign-packages stage-packages-world: .PHONY @mkdir -p ${WSTAGEDIR} ${_+_}@cd ${.CURDIR}; \ ${MAKE} DESTDIR=${WSTAGEDIR} -DNO_ROOT stageworld stage-packages-kernel: .PHONY @mkdir -p ${KSTAGEDIR} ${_+_}@cd ${.CURDIR}; \ ${MAKE} DESTDIR=${KSTAGEDIR} -DNO_ROOT stagekernel stage-packages-source: .PHONY @mkdir -p ${SSTAGEDIR}; stage-packages: .PHONY stage-packages-world stage-packages-kernel stage-packages-source _repodir: .PHONY @mkdir -p ${REPODIR} create-packages-world: _pkgbootstrap _repodir .PHONY ${_+_}@cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 \ DESTDIR=${WSTAGEDIR} \ PKG_VERSION=${PKG_VERSION} create-world-packages create-packages-kernel: _pkgbootstrap _repodir .PHONY ${_+_}@cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 \ DESTDIR=${KSTAGEDIR} \ PKG_VERSION=${PKG_VERSION} DISTDIR=kernel \ SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \ create-kernel-packages create-packages-source: _pkgbootstrap _repodir .PHONY ${_+_}@cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 \ DESTDIR=${SSTAGEDIR} \ PKG_VERSION=${PKG_VERSION} create-source-packages create-packages: .PHONY create-packages-world create-packages-kernel create-packages-source create-source-packages: _pkgbootstrap .PHONY rm -f ${SSTAGEDIR}/*.plist 2>/dev/null || : .if !empty(GIT_CMD) && exists(${GIT_CMD}) && exists(${SRCDIR}/.git) @cd ${SRCDIR}; \ ( echo "@override_prefix /usr/src" ; \ ${GIT_CMD} ls-files --recurse-submodules ":!:sys/" ) \ > ${SSTAGEDIR}/src.plist @cd ${SRCDIR}; \ ( echo "@override_prefix /usr/src" ; \ ${GIT_CMD} ls-files --recurse-submodules "sys/" ) \ > ${SSTAGEDIR}/src-sys.plist ${SRCDIR}/release/packages/generate-ucl.lua \ PKGNAME "src" \ PKGGENNAME "src" \ VERSION "${PKG_VERSION}" \ DESC "FreeBSD Kernel Sources" \ COMMENT "FreeBSD Userland Sources" \ PKG_NAME_PREFIX "${PKG_NAME_PREFIX}" \ PKG_MAINTAINER "${PKG_MAINTAINER}" \ PKG_WWW "${PKG_WWW}" \ ${SRCDIR}/release/packages/template.ucl \ ${SSTAGEDIR}/src.ucl ${SRCDIR}/release/packages/generate-ucl.lua \ PKGNAME "src-sys" \ PKGGENNAME "src" \ VERSION "${PKG_VERSION}" \ DESC "FreeBSD Kernel Sources" \ COMMENT "FreeBSD Kernel Sources" \ PKG_NAME_PREFIX "${PKG_NAME_PREFIX}" \ PKG_MAINTAINER "${PKG_MAINTAINER}" \ PKG_WWW "${PKG_WWW}" \ ${SRCDIR}/release/packages/template.ucl \ ${SSTAGEDIR}/src-sys.ucl ${PKG_CMD} -o ABI=${PKG_ABI} \ -o OSVERSION="${SRCRELDATE}" \ create -f ${PKG_FORMAT} \ -M ${SSTAGEDIR}/src.ucl \ -p ${SSTAGEDIR}/src.plist \ -r ${SRCDIR} \ -o ${REPODIR}/${PKG_ABI}/${PKG_OUTPUT_DIR} ${PKG_CMD} -o ABI=${PKG_ABI} \ -o OSVERSION="${SRCRELDATE}" \ create -f ${PKG_FORMAT} \ -M ${SSTAGEDIR}/src-sys.ucl \ -p ${SSTAGEDIR}/src-sys.plist \ -r ${SRCDIR} \ -o ${REPODIR}/${PKG_ABI}/${PKG_OUTPUT_DIR} .endif create-world-packages: _pkgbootstrap .PHONY @rm -f ${WSTAGEDIR}/*.plist 2>/dev/null || : @cd ${WSTAGEDIR} ; \ ${METALOG_SORT_CMD} ${WSTAGEDIR}/${DISTDIR}/METALOG | \ awk -f ${SRCDIR}/release/scripts/mtree-to-plist.awk @for plist in ${WSTAGEDIR}/*.plist; do \ plist=$${plist##*/} ; \ pkgname=$${plist%.plist} ; \ echo "_PKGS+= $${pkgname}" ; \ done > ${WSTAGEDIR}/packages.mk ${_+_}@cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 create-world-packages-jobs \ SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \ .MAKE.JOB.PREFIX= .if make(create-world-packages-jobs) .include "${WSTAGEDIR}/packages.mk" .endif create-world-packages-jobs: .PHONY .for pkgname in ${_PKGS} create-world-packages-jobs: create-world-package-${pkgname} create-world-package-${pkgname}: .PHONY @sh ${SRCDIR}/release/packages/generate-ucl.sh -o ${pkgname} \ -s ${SRCDIR} -u ${WSTAGEDIR}/${pkgname}.ucl @awk -F\" ' \ /^name/ { printf("===> Creating %s-", $$2); next } \ /^version/ { print $$2; next } \ ' ${WSTAGEDIR}/${pkgname}.ucl @if [ "${pkgname}" == "runtime" ]; then \ sed -i '' -e "s/%VCS_REVISION%/${VCS_REVISION}/" ${WSTAGEDIR}/${pkgname}.ucl ; \ fi ${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/usr/bin/uname -o ALLOW_BASE_SHLIBS=yes \ -o OSVERSION="${SRCRELDATE}" \ create -f ${PKG_FORMAT} -M ${WSTAGEDIR}/${pkgname}.ucl \ -p ${WSTAGEDIR}/${pkgname}.plist \ -r ${WSTAGEDIR} \ -o ${REPODIR}/${PKG_ABI}/${PKG_OUTPUT_DIR} .endfor _default_flavor= -default .if make(*package*) && exists(${KSTAGEDIR}/kernel.meta) . if ${MK_DEBUG_FILES} != "no" _debug=-dbg . endif create-kernel-packages: .PHONY . for flavor in "" ${_debug} create-kernel-packages: create-kernel-packages-flavor${flavor:C,^""$,${_default_flavor},} create-kernel-packages-flavor${flavor:C,^""$,${_default_flavor},}: _pkgbootstrap .PHONY @cd ${KSTAGEDIR}/${DISTDIR} ; \ ${METALOG_SORT_CMD} ${KSTAGEDIR}/kernel.meta | \ awk -f ${SRCDIR}/release/scripts/mtree-to-plist.awk \ -v kernel=yes -v _kernconf=${INSTALLKERNEL} ; \ ${SRCDIR}/release/packages/generate-ucl.lua \ PKGNAME "kernel-${INSTALLKERNEL:tl}${flavor}" \ VERSION "${PKG_VERSION}" \ KERNELDIR "kernel" \ COMMENT "FreeBSD ${INSTALLKERNEL} kernel ${flavor}" \ DESC "FreeBSD ${INSTALLKERNEL} kernel ${flavor}" \ PKG_NAME_PREFIX "${PKG_NAME_PREFIX}" \ PKG_MAINTAINER "${PKG_MAINTAINER}" \ PKG_WWW "${PKG_WWW}" \ ${SRCDIR}/release/packages/template.ucl \ ${KSTAGEDIR}/${DISTDIR}/kernel.${INSTALLKERNEL}${flavor}.ucl ; \ awk -F\" ' \ /name/ { printf("===> Creating %s-", $$2); next } \ /version/ {print $$2; next } ' \ ${KSTAGEDIR}/${DISTDIR}/kernel.${INSTALLKERNEL}${flavor}.ucl ; \ ${PKG_CMD} -o ABI=${PKG_ABI} -o ALLOW_BASE_SHLIBS=yes \ -o OSVERSION="${SRCRELDATE}" \ create -f ${PKG_FORMAT} \ -M ${KSTAGEDIR}/${DISTDIR}/kernel.${INSTALLKERNEL}${flavor}.ucl \ -p ${KSTAGEDIR}/${DISTDIR}/kernel.${INSTALLKERNEL}${flavor}.plist \ -r ${KSTAGEDIR}/${DISTDIR} \ -o ${REPODIR}/${PKG_ABI}/${PKG_OUTPUT_DIR} . endfor .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" . for _kernel in ${BUILDKERNELS:[2..-1]} . if exists(${KSTAGEDIR}/kernel.${_kernel}.meta) . if ${MK_DEBUG_FILES} != "no" _debug=-dbg . endif . for flavor in "" ${_debug} create-kernel-packages: create-kernel-packages-extra-flavor${flavor:C,^""$,${_default_flavor},}-${_kernel} create-kernel-packages-extra-flavor${flavor:C,^""$,${_default_flavor},}-${_kernel}: _pkgbootstrap .PHONY @cd ${KSTAGEDIR}/kernel.${_kernel} ; \ ${METALOG_SORT_CMD} ${KSTAGEDIR}/kernel.${_kernel}.meta | \ awk -f ${SRCDIR}/release/scripts/mtree-to-plist.awk \ -v kernel=yes -v _kernconf=${_kernel} ; \ ${SRCDIR}/release/packages/generate-ucl.lua \ PKGNAME "kernel-${_kernel:tl}${flavor}" \ PKGGENNAME "kernel" \ FORCEINCLUDE "kernel${flavor}" \ UCLFILES "${SRCDIR}/release/packages/" \ VERSION "${PKG_VERSION}" \ KERNELDIR "kernel.${_kernel}" \ DESC "FreeBSD ${_kernel} kernel ${flavor}" \ COMMENT "FreeBSD ${_kernel} kernel ${flavor}" \ PKG_NAME_PREFIX "${PKG_NAME_PREFIX}" \ PKG_MAINTAINER "${PKG_MAINTAINER}" \ PKG_WWW "${PKG_WWW}" \ ${SRCDIR}/release/packages/template.ucl \ ${KSTAGEDIR}/kernel.${_kernel}/kernel.${_kernel}${flavor}.ucl ; \ awk -F\" ' \ /name/ { printf("===> Creating %s-", $$2); next } \ /version/ {print $$2; next } ' \ ${KSTAGEDIR}/kernel.${_kernel}/kernel.${_kernel}${flavor}.ucl ; \ ${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/usr/bin/uname -o ALLOW_BASE_SHLIBS=yes \ -o OSVERSION="${SRCRELDATE}" \ create -f ${PKG_FORMAT} \ -M ${KSTAGEDIR}/kernel.${_kernel}/kernel.${_kernel}${flavor}.ucl \ -p ${KSTAGEDIR}/kernel.${_kernel}/kernel.${_kernel}${flavor}.plist \ -r ${KSTAGEDIR}/kernel.${_kernel} \ -o ${REPODIR}/${PKG_ABI}/${PKG_OUTPUT_DIR} . endfor . endif . endfor .endif sign-packages: _pkgbootstrap .PHONY printf "version = 2;\n" > ${WSTAGEDIR}/meta .if ${PKG_BIN_VERSION} < 11700 printf "packing_format = \"${PKG_FORMAT}\";\n" >> ${WSTAGEDIR}/meta .endif @[ -L "${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/usr/bin/uname config ABI)/latest" ] && \ unlink ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/usr/bin/uname config ABI)/latest ; \ ${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/usr/bin/uname repo \ -o OSVERSION="${SRCRELDATE}" \ -m ${WSTAGEDIR}/meta \ -o ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/usr/bin/uname config ABI)/${PKG_VERSION} \ ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/usr/bin/uname config ABI)/${PKG_VERSION} \ ${PKG_REPO_SIGNING_KEY} ; \ cd ${REPODIR}/$$(${PKG_CMD} -o ABI_FILE=${WSTAGEDIR}/usr/bin/uname config ABI); \ ln -s ${PKG_OUTPUT_DIR} latest # # # checkworld # # Run test suite on installed world. # checkworld: .PHONY @if [ ! -x "${LOCALBASE}/bin/kyua" ] && [ ! -x "/usr/bin/kyua" ]; then \ echo "You need kyua (devel/kyua) to run the test suite." | /usr/bin/fmt; \ exit 1; \ fi ${_+_}PATH="$$PATH:${LOCALBASE}/bin" kyua test -k ${TESTSBASE}/Kyuafile # # # doxygen # # Build the API documentation with doxygen # doxygen: .PHONY @if [ ! -x "${LOCALBASE}/bin/doxygen" ]; then \ echo "You need doxygen (devel/doxygen) to generate the API documentation of the kernel." | /usr/bin/fmt; \ exit 1; \ fi ${_+_}cd ${.CURDIR}/tools/kerneldoc/subsys; ${MAKE} obj all # # ------------------------------------------------------------------------ # # From here onwards are utility targets used by the 'make world' and # related targets. If your 'world' breaks, you may like to try to fix # the problem and manually run the following targets to attempt to # complete the build. Beware, this is *not* guaranteed to work, you # need to have a pretty good grip on the current state of the system # to attempt to manually finish it. If in doubt, 'make world' again. # # # legacy: Build compatibility shims for the next three targets. This is a # minimal set of tools and shims necessary to compensate for older systems # which don't have the APIs required by the targets built in bootstrap-tools, # build-tools or cross-tools. # legacy: .PHONY .if ${BOOTSTRAPPING} < ${MINIMUM_SUPPORTED_OSREL} && ${BOOTSTRAPPING} != 0 @echo "ERROR: Source upgrades from versions prior to ${MINIMUM_SUPPORTED_REL} are not supported."; \ false .endif .for _tool in \ tools/build \ ${LOCAL_LEGACY_DIRS} ${_+_}@${ECHODIR} "===> ${_tool} (obj,includes,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${WORLDTMP}/legacy includes; \ ${MAKE} DIRPRFX=${_tool}/ MK_INCLUDES=no all; \ ${MAKE} DIRPRFX=${_tool}/ MK_INCLUDES=no \ DESTDIR=${WORLDTMP}/legacy install .endfor # # bootstrap-tools: Build tools needed for compatibility. These are binaries that # are built to build other binaries in the system. However, the focus of these # binaries is usually quite narrow. Bootstrap tools use the host's compiler and # libraries, augmented by -legacy, in addition to the libraries built during # bootstrap-tools. # _bt= _bootstrap-tools # We want to run the build with only ${WORLDTMP} in $PATH to ensure we don't # accidentally run tools that are incompatible but happen to be in $PATH. # This is especially important when building on Linux/MacOS where many of the # programs used during the build accept different flags or generate different # output. On those platforms we only symlink the tools known to be compatible # (e.g. basic utilities such as mkdir) into ${WORLDTMP} and build all others # from the FreeBSD sources during the bootstrap-tools stage. # We want to build without the user's $PATH starting in the bootstrap-tools # phase so the tools used in that phase (ln, cp, etc) must have already been # linked to $WORLDTMP. The tools are listed in the _host_tools_to_symlink # variable in tools/build/Makefile and are linked during the legacy phase. # Since they could be Linux or MacOS binaries, too we must only use flags that # are portable across operating systems. # If BOOTSTRAP_ALL_TOOLS is set we will build all the required tools from the # current source tree. Otherwise we create a symlink to the version found in # $PATH during the bootstrap-tools stage. # When building on non-FreeBSD systems we can't assume that the host binaries # accept compatible flags or produce compatible output. Therefore we force # BOOTSTRAP_ALL_TOOLS and just build the FreeBSD version of the binary. .if defined(CROSSBUILD_HOST) BOOTSTRAP_ALL_TOOLS:= 1 .endif .if defined(BOOTSTRAP_ALL_TOOLS) # BOOTSTRAPPING will be set on the command line so we can't override it here. # Instead set BOOTSTRAPPING_OSRELDATE so that the value 0 is set ${BSARGS} BOOTSTRAPPING_OSRELDATE:= 0 .endif .if ${MK_GAMES} != "no" _strfile= usr.bin/fortune/strfile .endif # vtfontcvt is used to build font files for loader and to generate # C source for loader built in font (8x16.c). _vtfontcvt= usr.bin/vtfontcvt # zic is used to compile timezone data .if ${MK_ZONEINFO} != "no" _zic= usr.sbin/zic .endif # If we are not building the bootstrap because BOOTSTRAPPING is sufficient # we symlink the host version to $WORLDTMP instead. By doing this we can also # detect when a bootstrap tool is being used without the required MK_FOO. # If you add a new bootstrap tool where we could also use the host version, # please ensure that you also add a .else case where you add the tool to the # _bootstrap_tools_links variable. .if ${BOOTSTRAPPING} < 1000033 # Note: lex needs m4 to build but m4 also depends on lex (which needs m4 to # generate any files). To fix this cyclic dependency we can build a bootstrap # version of m4 (with pre-generated files) then use that to build the real m4. # We can't simply use the host m4 since e.g. the macOS version does not accept # the flags that are passed by lex. # For lex we also use the pre-gerated files since we would otherwise need to # build awk and sed first (which need lex to build) # TODO: add a _bootstrap_lex and then build the real lex afterwards _lex= usr.bin/lex _m4= tools/build/bootstrap-m4 usr.bin/m4 ${_bt}-tools/build/bootstrap-m4: ${_bt}-usr.bin/lex ${_bt}-lib/libopenbsd ${_bt}-usr.bin/yacc ${_bt}-usr.bin/m4: ${_bt}-lib/libopenbsd ${_bt}-usr.bin/yacc ${_bt}-usr.bin/lex ${_bt}-tools/build/bootstrap-m4 _bt_m4_depend=${_bt}-usr.bin/m4 _bt_lex_depend=${_bt}-usr.bin/lex ${_bt_m4_depend} .else _bootstrap_tools_links+=m4 lex .endif # ELF Tool Chain libraries are needed for ELF tools and dtrace tools. # r296685 fix cross-endian objcopy # r310724 fixed PR 215350, a crash in libdwarf with objects built by GCC 6.2. # r334881 added libdwarf constants used by ctfconvert. # r338478 fixed a crash in objcopy for mips64el objects # r339083 libelf: correct mips64el test to use ELF header # r348347 Add missing powerpc64 relocation support to libdwarf .if ${BOOTSTRAPPING} < 1300030 _elftoolchain_libs= lib/libelf lib/libdwarf lib/libzstd ${_bt}-lib/libelf: ${_bt_m4_depend} ${_bt}-lib/libdwarf: ${_bt_m4_depend} _bt_libelf_depend=${_bt}-lib/libelf .endif _kldxref= usr.sbin/kldxref ${_bt}-usr.sbin/kldxref: ${_bt_libelf_depend} # flua is required to regenerate syscall files. It first appeared during the # 13.0-CURRENT cycle, thus needs to be built on -older releases and stable # branches. .if ${BOOTSTRAPPING} < 1300059 ${_bt}-libexec/flua: ${_bt}-lib/liblua ${_bt}-lib/libucl _flua= lib/liblua lib/libucl libexec/flua .endif # r245440 mtree -N support added # r313404 requires sha384.h for libnetbsd, added to libmd in r292782 .if ${BOOTSTRAPPING} < 1100093 _libnetbsd= lib/libnetbsd _nmtree= lib/libmd \ usr.sbin/nmtree ${_bt}-lib/libnetbsd: ${_bt}-lib/libmd ${_bt}-usr.sbin/nmtree: ${_bt}-lib/libnetbsd .else _bootstrap_tools_links+=mtree .endif # r246097: log addition login.conf.db, passwd, pwd.db, and spwd.db with cat -l .if ${BOOTSTRAPPING} < 1000027 _cat= bin/cat .else _bootstrap_tools_links+=cat .endif # r277259 crunchide: Correct 64-bit section header offset # r281674 crunchide: always include both 32- and 64-bit ELF support .if ${BOOTSTRAPPING} < 1100078 _crunchide= usr.sbin/crunch/crunchide .else _bootstrap_tools_links+=crunchide .endif # 1400052, 1300526, 1203507: Removed -dc from linker invocation .if ${BOOTSTRAPPING} < 1203507 || \ (${BOOTSTRAPPING} > 1300000 && ${BOOTSTRAPPING} < 1300526) || \ (${BOOTSTRAPPING} > 1400000 && ${BOOTSTRAPPING} < 1400052) _crunchgen= usr.sbin/crunch/crunchgen .else _bootstrap_tools_links+=crunchgen .endif # The ATKBD_DFLT_KEYMAP, UKBD_DFLT_KEYMAP and KBDMUX_DFLT_KEYMAP kernel options # require kbdcontrol. Note that, even on FreeBSD, the host will lack kbdcontrol # if built with WITHOUT_LEGACY_CONSOLE. .if defined(BOOTSTRAP_ALL_TOOLS) || !exists(/usr/sbin/kbdcontrol) _kbdcontrol= usr.sbin/kbdcontrol .else _bootstrap_tools_links+=kbdcontrol .endif .if ${MK_DISK_IMAGE_TOOLS_BOOTSTRAP} != "no" _etdump= usr.bin/etdump _makefs= usr.sbin/makefs _libnetbsd= lib/libnetbsd ${_bt}-usr.sbin/makefs: ${_bt}-lib/libnetbsd .if defined(BOOTSTRAP_ALL_TOOLS) _libsbuf= lib/libsbuf ${_bt}-usr.sbin/makefs: ${_bt}-lib/libsbuf .endif .endif # 1300102: VHDX support .if ${BOOTSTRAPPING} < 1201520 || \ (${BOOTSTRAPPING} > 1300000 && ${BOOTSTRAPPING} < 1300102) || \ ${MK_DISK_IMAGE_TOOLS_BOOTSTRAP} != "no" _mkimg= usr.bin/mkimg .else _bootstrap_tools_links+=mkimg .endif _yacc= usr.bin/yacc .if ${MK_BSNMP} != "no" _gensnmptree= usr.sbin/bsnmpd/gensnmptree .endif # We need to build tblgen when we're building clang or lld, either as # bootstrap tools, or as the part of the normal build. # llvm-tblgen is also needed for various llvm binutils (e.g. objcopy). .if ${MK_CLANG_BOOTSTRAP} != "no" || ${MK_CLANG} != "no" || \ ${MK_LLD_BOOTSTRAP} != "no" || ${MK_LLD} != "no" || \ ${MK_LLDB} != "no" || ${MK_LLVM_BINUTILS} != "no" _clang_tblgen= \ lib/clang/libllvmminimal \ usr.bin/clang/llvm-min-tblgen \ usr.bin/clang/llvm-tblgen .if ${MK_CLANG_BOOTSTRAP} != "no" || ${MK_CLANG} != "no" || \ ${MK_LLDB} != "no" _clang_tblgen+= lib/clang/libclangminimal _clang_tblgen+= usr.bin/clang/clang-tblgen .endif .if ${MK_LLDB} != "no" _clang_tblgen+= usr.bin/clang/lldb-tblgen .endif ${_bt}-usr.bin/clang/clang-tblgen: ${_bt}-lib/clang/libllvmminimal ${_bt}-usr.bin/clang/lldb-tblgen: ${_bt}-lib/clang/libllvmminimal ${_bt}-usr.bin/clang/llvm-min-tblgen: ${_bt}-lib/clang/libllvmminimal ${_bt}-usr.bin/clang/llvm-tblgen: ${_bt}-lib/clang/libllvmminimal ${_bt}-usr.bin/clang/llvm-tblgen: ${_bt}-usr.bin/clang/llvm-min-tblgen .endif # C.UTF-8 is always built in share/ctypes and we need localedef for that. _localedef= usr.bin/localedef ${_bt}-usr.bin/localedef: ${_bt}-usr.bin/yacc ${_bt_lex_depend} .if ${MK_ICONV} != "no" _mkesdb= usr.bin/mkesdb _mkcsmapper= usr.bin/mkcsmapper ${_bt}-usr.bin/mkesdb: ${_bt}-usr.bin/yacc ${_bt_lex_depend} ${_bt}-usr.bin/mkcsmapper: ${_bt}-usr.bin/yacc ${_bt_lex_depend} .endif .if ${MK_KERBEROS} != "no" _kerberos5_bootstrap_tools= \ kerberos5/tools/make-roken \ kerberos5/lib/libroken \ kerberos5/lib/libvers \ kerberos5/tools/asn1_compile \ kerberos5/tools/slc \ usr.bin/compile_et .ORDER: ${_kerberos5_bootstrap_tools:C/^/${_bt}-/g} .for _tool in ${_kerberos5_bootstrap_tools} ${_bt}-${_tool}: ${_bt}-usr.bin/yacc ${_bt_lex_depend} .endfor .endif ${_bt}-usr.bin/mandoc: ${_bt}-lib/libopenbsd # The tools listed in _basic_bootstrap_tools will generally not be # bootstrapped unless BOOTSTRAP_ALL_TOOL is set. However, when building on a # Linux or MacOS host the host versions are incompatible so we need to build # them from the source tree. Usually the link name will be the same as the subdir, # but some directories such as grep or test install multiple binaries. In that # case we use the _basic_bootstrap_tools_multilink variable which is a list of # subdirectory and comma-separated list of files. _basic_bootstrap_tools_multilink=usr.bin/grep grep,egrep,fgrep _basic_bootstrap_tools_multilink+=bin/test test,[ # bootstrap tools needed by buildworld: _basic_bootstrap_tools+=usr.bin/cut bin/expr usr.bin/gencat usr.bin/join \ usr.bin/mktemp bin/realpath bin/rmdir usr.bin/sed usr.bin/sort \ usr.bin/truncate usr.bin/tsort # Some build scripts use nawk instead of awk (this happens at least in # cddl/contrib/opensolaris/lib/libdtrace/common/mknames.sh) so we need both awk # and nawk in ${WORLDTMP}/legacy/bin. _basic_bootstrap_tools_multilink+=usr.bin/awk awk,nawk # file2c is required for building usr.sbin/config: _basic_bootstrap_tools+=usr.bin/file2c # uuencode/uudecode required for share/tabset _basic_bootstrap_tools_multilink+=usr.bin/bintrans uuencode,uudecode # xargs is required by mkioctls _basic_bootstrap_tools+=usr.bin/xargs # cap_mkdb is required for share/termcap: _basic_bootstrap_tools+=usr.bin/cap_mkdb # services_mkdb/pwd_mkdb are required for installworld: _basic_bootstrap_tools+=usr.sbin/services_mkdb usr.sbin/pwd_mkdb # ldd is required for installcheck (TODO: just always use /usr/bin/ldd instead?) .if !defined(CROSSBUILD_HOST) # ldd is only needed for updating the running system so we don't need to # bootstrap ldd on non-FreeBSD systems _basic_bootstrap_tools+=usr.bin/ldd .endif # sysctl/chflags are required for installkernel: .if !defined(CROSSBUILD_HOST) _basic_bootstrap_tools+=bin/chflags # Note: sysctl does not bootstrap on FreeBSD < 13 anymore, but that doesn't # matter since we don't need any of the new features for the build. _bootstrap_tools_links+=sysctl .else # When building on non-FreeBSD, install a fake chflags instead since the # version from the source tree cannot work. We also don't need sysctl since we # are install with -DNO_ROOT. _other_bootstrap_tools+=tools/build/cross-build/fake_chflags .endif # mkfifo is used by sys/conf/newvers.sh _basic_bootstrap_tools+=usr.bin/mkfifo # jot is needed for the mkimg tests _basic_bootstrap_tools+=usr.bin/jot .if ${MK_BOOT} != "no" # md5 is used by boot/beri (and possibly others) _basic_bootstrap_tools+=sbin/md5 .endif # tzsetup is needed as an install tool .if ${MK_ZONEINFO} != "no" _basic_bootstrap_tools+=usr.sbin/tzsetup .endif .if defined(BOOTSTRAP_ALL_TOOLS) _other_bootstrap_tools+=${_basic_bootstrap_tools} .for _subdir _links in ${_basic_bootstrap_tools_multilink} _other_bootstrap_tools+=${_subdir} .endfor ${_bt}-usr.bin/awk: ${_bt_lex_depend} ${_bt}-usr.bin/yacc ${_bt}-bin/expr: ${_bt_lex_depend} ${_bt}-usr.bin/yacc # If we are bootstrapping file2c, we have to build it before config: ${_bt}-usr.sbin/config: ${_bt}-usr.bin/file2c ${_bt_lex_depend} # Note: no symlink to make/bmake in the !BOOTSTRAP_ALL_TOOLS case here since # the links to make/bmake make links will have already have been created in the # `make legacy` step. Not adding a link to make is important on non-FreeBSD # since "make" will usually point to GNU make there. _other_bootstrap_tools+=usr.bin/bmake # Avoid dependency on host bz2 headers: _other_bootstrap_tools+=lib/libbz2 ${_bt}-usr.bin/grep: ${_bt}-lib/libbz2 # libdwarf depends on libz _other_bootstrap_tools+=lib/libz ${_bt}-lib/libdwarf: ${_bt}-lib/libz # libroken depends on libcrypt _other_bootstrap_tools+=lib/libcrypt ${_bt}-lib/libroken: ${_bt}-lib/libcrypt .else # All tools in _basic_bootstrap_tools have the same name as the subdirectory # so we can use :T to get the name of the symlinks that we need to create. _bootstrap_tools_links+=${_basic_bootstrap_tools:T} .for _subdir _links in ${_basic_bootstrap_tools_multilink} _bootstrap_tools_links+=${_links:S/,/ /g} .endfor .endif # defined(BOOTSTRAP_ALL_TOOLS) # Link the tools that we need for building but don't need to bootstrap because # the host version is known to be compatible into ${WORLDTMP}/legacy # We do this before building any of the bootstrap tools in case they depend on # the presence of any of the links (e.g. as m4/lex/awk) ${_bt}-links: .PHONY .for _tool in ${_bootstrap_tools_links} ${_bt}-link-${_tool}: .PHONY @rm -f "${WORLDTMP}/legacy/bin/${_tool}"; \ source_path=`which ${_tool}`; \ if [ ! -e "$${source_path}" ] ; then \ echo "Cannot find host tool '${_tool}'"; false; \ fi; \ cp -pf "$${source_path}" "${WORLDTMP}/legacy/bin/${_tool}" ${_bt}-links: ${_bt}-link-${_tool} .endfor bootstrap-tools: ${_bt}-links .PHONY # Please document (add comment) why something is in 'bootstrap-tools'. # Try to bound the building of the bootstrap-tool to just the # FreeBSD versions that need the tool built at this stage of the build. .for _tool in \ ${_clang_tblgen} \ ${_kerberos5_bootstrap_tools} \ ${_strfile} \ usr.bin/dtc \ ${_cat} \ ${_kbdcontrol} \ ${_elftoolchain_libs} \ ${_kldxref} \ lib/libopenbsd \ usr.bin/mandoc \ usr.bin/rpcgen \ ${_yacc} \ ${_m4} \ ${_lex} \ ${_other_bootstrap_tools} \ usr.bin/xinstall \ ${_gensnmptree} \ usr.sbin/config \ ${_flua} \ ${_crunchide} \ ${_crunchgen} \ ${_etdump} \ ${_libnetbsd} \ ${_libsbuf} \ ${_makefs} \ ${_mkimg} \ ${_nmtree} \ ${_vtfontcvt} \ ${_localedef} \ ${_mkcsmapper} \ ${_mkesdb} \ ${_zic} \ ${LOCAL_BSTOOL_DIRS} ${_bt}-${_tool}: ${_bt}-links .PHONY .MAKE ${_+_}@${ECHODIR} "===> ${_tool} (obj,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ if [ "${_tool}" = "usr.bin/lex" ]; then \ ${MAKE} DIRPRFX=${_tool}/ bootstrap; \ fi; \ ${MAKE} DIRPRFX=${_tool}/ all; \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${WORLDTMP}/legacy install bootstrap-tools: ${_bt}-${_tool} .endfor .if target(${_bt}-lib/libmd) # If we are bootstrapping libmd (e.g. when building on macOS/Linux) add the # necessary dependencies: ${_bt}-usr.bin/sort: ${_bt}-lib/libmd ${_bt}-usr.bin/xinstall: ${_bt}-lib/libmd ${_bt}-sbin/md5: ${_bt}-lib/libmd .endif # # build-tools: Build special purpose build tools # .if !defined(NO_SHARE) && ${MK_SYSCONS} != "no" _share= share/syscons/scrnmaps .endif .if ${MK_RESCUE} != "no" # rescue includes programs that have build-tools targets _rescue=rescue/rescue .endif .if ${MK_TCSH} != "no" _tcsh=bin/csh .endif .if ${MK_FILE} != "no" _libmagic=lib/libmagic .endif .if ${MK_PMC} != "no" _jevents=lib/libpmc/pmu-events .endif # kernel-toolchain skips _cleanobj, so handle cleaning up previous # build-tools directories if needed. .if ${MK_CLEAN} == "yes" && make(kernel-toolchain) _bt_clean= ${CLEANDIR} .endif .for _tool in \ ${_tcsh} \ bin/sh \ ${LOCAL_TOOL_DIRS} \ ${_jevents} \ lib/ncurses/tinfo \ ${_rescue} \ ${_share} \ usr.bin/awk \ ${_libmagic} \ usr.bin/vi/catalog build-tools_${_tool}: .PHONY ${_+_}@${ECHODIR} "===> ${_tool} (${_bt_clean:D${_bt_clean},}obj,build-tools)"; \ cd ${.CURDIR}/${_tool}; \ if [ -n "${_bt_clean}" ]; then ${MAKE} DIRPRFX=${_tool}/ ${_bt_clean}; fi; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ ${MAKE} DIRPRFX=${_tool}/ build-tools build-tools: build-tools_${_tool} .endfor # # kernel-tools: Build kernel-building tools # kernel-tools: .PHONY mkdir -p ${WORLDTMP}/usr ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${WORLDTMP}/usr >/dev/null # # cross-tools: All the tools needed to build the rest of the system after # we get done with the earlier stages. It is the last set of tools needed # to begin building the target binaries. # .if ${TARGET_ARCH} != ${MACHINE_ARCH} || ${BUILD_WITH_STRICT_TMPPATH} != 0 .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" _btxld= usr.sbin/btxld .endif .endif # Rebuild ctfconvert and ctfmerge to avoid difficult-to-diagnose failures # resulting from missing bug fixes or ELF Toolchain updates. .if ${MK_CDDL} != "no" _dtrace_tools= cddl/lib/libctf cddl/lib/libspl cddl/usr.bin/ctfconvert \ cddl/usr.bin/ctfmerge .endif # If we're given an XAS, don't build binutils. .if ${XAS:M/*} == "" .if ${MK_ELFTOOLCHAIN_BOOTSTRAP} != "no" _elftctools= lib/libelftc \ lib/libpe \ usr.bin/elfctl \ usr.bin/elfdump \ usr.bin/objcopy \ usr.bin/nm \ usr.bin/size \ usr.bin/strings # These are not required by the build, but can be useful for developers who # cross-build on a FreeBSD 10 host: _elftctools+= usr.bin/addr2line .endif .elif ${TARGET_ARCH} != ${MACHINE_ARCH} && ${MK_ELFTOOLCHAIN_BOOTSTRAP} != "no" # If cross-building with an external binutils we still need to build strip for # the target (for at least crunchide). _elftctools= lib/libelftc \ lib/libpe \ usr.bin/elfctl \ usr.bin/elfdump \ usr.bin/objcopy .endif .if ${MK_CLANG_BOOTSTRAP} != "no" _clang= usr.bin/clang .endif .if ${MK_LLD_BOOTSTRAP} != "no" _lld= usr.bin/clang/lld .endif .if ${MK_CLANG_BOOTSTRAP} != "no" || ${MK_LLD_BOOTSTRAP} != "no" _clang_libs= lib/clang .endif .if ${MK_USB} != "no" _usb_tools= stand/usb/tools .endif .if ${BUILD_WITH_STRICT_TMPPATH} != 0 || defined(BOOTSTRAP_ALL_TOOLS) _ar=usr.bin/ar .endif cross-tools: .MAKE .PHONY .for _tool in \ ${LOCAL_XTOOL_DIRS} \ ${_ar} \ ${_clang_libs} \ ${_clang} \ ${_lld} \ ${_elftctools} \ ${_dtrace_tools} \ ${_btxld} \ ${_usb_tools} ${_+_}@${ECHODIR} "===> ${_tool} (obj,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ ${MAKE} DIRPRFX=${_tool}/ all; \ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${WORLDTMP} install .endfor # # native-xtools is the current target for qemu-user cross builds of ports # via poudriere and the imgact_binmisc kernel module. # This target merely builds a toolchan/sysroot, then builds the tools it wants # with the options it wants in a special MAKEOBJDIRPREFIX, using the toolchain # already built. It then installs the static tools to NXBDESTDIR for Poudriere # to pickup. # NXBOBJROOT= ${OBJROOT}${MACHINE}.${MACHINE_ARCH}/nxb/ NXBOBJTOP= ${NXBOBJROOT}${NXB_TARGET}.${NXB_TARGET_ARCH} NXTP?= /nxb-bin .if ${NXTP:N/*} .error NXTP variable should be an absolute path .endif NXBDESTDIR?= ${DESTDIR}${NXTP} # This is the list of tools to be built/installed as static and where # appropriate to build for the given TARGET.TARGET_ARCH. NXBDIRS+= \ bin/cat \ bin/chmod \ bin/cp \ ${_tcsh} \ bin/echo \ bin/expr \ bin/hostname \ bin/ln \ bin/ls \ bin/mkdir \ bin/mv \ bin/ps \ bin/realpath \ bin/rm \ bin/rmdir \ bin/sh \ bin/sleep \ sbin/md5 \ sbin/sysctl \ usr.bin/addr2line \ usr.bin/ar \ usr.bin/awk \ usr.bin/basename \ usr.bin/bmake \ usr.bin/bzip2 \ usr.bin/cmp \ usr.bin/diff \ usr.bin/dirname \ usr.bin/objcopy \ usr.bin/env \ usr.bin/fetch \ usr.bin/find \ usr.bin/grep \ usr.bin/gzip \ usr.bin/head \ usr.bin/id \ usr.bin/lex \ usr.bin/limits \ usr.bin/mandoc \ usr.bin/mktemp \ usr.bin/mt \ usr.bin/nm \ usr.bin/patch \ usr.bin/readelf \ usr.bin/sed \ usr.bin/size \ usr.bin/sort \ usr.bin/strings \ usr.bin/tar \ usr.bin/touch \ usr.bin/tr \ usr.bin/true \ usr.bin/uniq \ usr.bin/unzip \ usr.bin/wc \ usr.bin/xargs \ usr.bin/xinstall \ usr.bin/xz \ usr.bin/yacc \ usr.sbin/chown SUBDIR_DEPEND_usr.bin/clang= lib/clang .if ${MK_CLANG} != "no" NXBDIRS+= lib/clang NXBDIRS+= usr.bin/clang .endif # XXX: native-xtools passes along ${NXBDIRS} in SUBDIR_OVERRIDE that needs # to be evaluated after NXBDIRS is set. .if make(install) && !empty(SUBDIR_OVERRIDE) SUBDIR= ${SUBDIR_OVERRIDE} .endif NXBMAKEARGS+= \ OBJTOP=${NXBOBJTOP:Q} \ OBJROOT=${NXBOBJROOT:Q} \ -DNO_SHARED \ -DNO_CPU_CFLAGS \ -DNO_PIC \ MK_CASPER=no \ MK_CLANG_EXTRAS=no \ MK_CLANG_FORMAT=no \ MK_CLANG_FULL=no \ MK_CTF=no \ MK_DEBUG_FILES=no \ MK_HTML=no \ MK_LLDB=no \ MK_MAN=no \ MK_MAN_UTILS=yes \ MK_OFED=no \ MK_OPENSSH=no \ MK_PROFILE=no \ MK_RETPOLINE=no \ MK_SENDMAIL=no \ MK_SSP=no \ MK_TESTS=no \ MK_WERROR=no \ MK_ZFS=no NXBMAKEENV+= \ MAKEOBJDIRPREFIX= # This should match all of the knobs in lib/Makefile that it takes to avoid # descending into lib/clang! NXBTNOTOOLS= MK_CLANG=no MK_LLD=no MK_LLDB=no MK_LLVM_BINUTILS=no .if make(native-xtools*) && \ (!defined(NXB_TARGET) || !defined(NXB_TARGET_ARCH)) .error Missing NXB_TARGET / NXB_TARGET_ARCH .endif # For 'toolchain' we want to produce native binaries that themselves generate # native binaries. NXBTMAKE= ${NXBMAKEENV} ${MAKE} ${NXBMAKEARGS:N-DNO_PIC:N-DNO_SHARED} \ TARGET=${MACHINE} TARGET_ARCH=${MACHINE_ARCH} # For 'everything' we want to produce native binaries (hence -target to # be MACHINE) that themselves generate TARGET.TARGET_ARCH binaries. # TARGET/TARGET_ARCH are still passed along from user. # # Use the toolchain we create as an external toolchain. .if ${USING_SYSTEM_COMPILER} == "yes" || ${XCC:N${CCACHE_BIN}:M/*} NXBMAKE+= XCC="${XCC}" \ XCXX="${XCXX}" \ XCPP="${XCPP}" .else NXBMAKE+= XCC="${NXBOBJTOP}/tmp/usr/bin/cc" \ XCXX="${NXBOBJTOP}/tmp/usr/bin/c++" \ XCPP="${NXBOBJTOP}/tmp/usr/bin/cpp" .endif NXBMAKE+= ${NXBMAKEENV} ${MAKE} -f Makefile.inc1 ${NXBMAKEARGS} \ MACHINE=${MACHINE} MACHINE_ARCH=${MACHINE_ARCH} \ TARGET=${NXB_TARGET} TARGET_ARCH=${NXB_TARGET_ARCH} \ TARGET_TRIPLE=${MACHINE_TRIPLE:Q} # NXBDIRS is improperly based on MACHINE rather than NXB_TARGET. Need to # invoke a sub-make to reevaluate MK_CLANG, etc, for NXBDIRS. NXBMAKE+= SUBDIR_OVERRIDE='$${NXBDIRS:M*}' # Need to avoid the -isystem logic when using clang as an external toolchain # even if the TARGET being built for wants GCC. NXBMAKE+= WANT_COMPILER_TYPE='$${X_COMPILER_TYPE}' native-xtools: .PHONY ${_+_}cd ${.CURDIR}; ${NXBTMAKE} _cleanobj # Build the bootstrap/host/cross tools that produce native binaries ${_+_}cd ${.CURDIR}; ${NXBTMAKE} kernel-toolchain # Populate includes/libraries sysroot that produce native binaries. # This is split out from 'toolchain' above mostly so that target LLVM # libraries have a proper LLVM_DEFAULT_TARGET_TRIPLE without # polluting the cross-compiler build. The LLVM libs are skipped # here to avoid the problem but are kept in 'toolchain' so that # needed build tools are built. ${_+_}cd ${.CURDIR}; ${NXBTMAKE} _includes ${NXBTNOTOOLS} ${_+_}cd ${.CURDIR}; ${NXBTMAKE} _libraries ${NXBTNOTOOLS} .if !defined(NO_OBJWALK) ${_+_}cd ${.CURDIR}; ${NXBMAKE} _obj .endif ${_+_}cd ${.CURDIR}; ${NXBMAKE} everything @echo ">> native-xtools done. Use 'make native-xtools-install' to install to a given DESTDIR" native-xtools-install: .PHONY mkdir -p ${NXBDESTDIR}/bin ${NXBDESTDIR}/sbin ${NXBDESTDIR}/usr ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${NXBDESTDIR}/usr >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${NXBDESTDIR}/usr/include >/dev/null .for d in ${LIBCOMPAT_INCLUDE_DIRS} mkdir -p ${NXBDESTDIR}/usr/include/${d} .endfor ${_+_}cd ${.CURDIR}; ${NXBMAKE} \ DESTDIR=${NXBDESTDIR} \ -DNO_ROOT \ install # # hierarchy - ensure that all the needed directories are present # hierarchy hier: .MAKE .PHONY ${_+_}cd ${.CURDIR}/etc; ${HMAKE} distrib-dirs # # libraries - build all libraries, and install them under ${DESTDIR}. # # The list of libraries with dependents (${_prebuild_libs}) and their # interdependencies (__L) are built automatically by the # ${.CURDIR}/tools/make_libdeps.sh script. # libraries: .MAKE .PHONY ${_+_}cd ${.CURDIR}; \ ${MAKE} -f Makefile.inc1 _prereq_libs; \ ${MAKE} -f Makefile.inc1 _startup_libs; \ ${MAKE} -f Makefile.inc1 _prebuild_libs -DLIBCRYPTO_WITHOUT_SUBDIRS; \ ${MAKE} -f Makefile.inc1 _generic_libs # # static libgcc.a prerequisite for shared libc # _prereq_libs= lib/libcompiler_rt .if ${MK_SSP} != "no" _prereq_libs+= lib/libssp_nonshared .endif .if ${MK_ASAN} != "no" _prereq_libs+= lib/libclang_rt/asan _prereq_libs+= lib/libclang_rt/asan-preinit _prereq_libs+= lib/libclang_rt/asan_cxx _prereq_libs+= lib/libclang_rt/asan_static .endif .if ${MK_UBSAN} != "no" _prereq_libs+= lib/libclang_rt/ubsan_minimal _prereq_libs+= lib/libclang_rt/ubsan_standalone _prereq_libs+= lib/libclang_rt/ubsan_standalone_cxx .endif # These dependencies are not automatically generated: # # lib/csu and lib/libc must be built before # all shared libraries for ELF. # _startup_libs= lib/csu _startup_libs+= lib/libc _startup_libs+= lib/libc_nonshared _startup_libs+= lib/libcxxrt _prereq_libs+= lib/libgcc_eh lib/libgcc_s _startup_libs+= lib/libgcc_eh lib/libgcc_s lib/libgcc_s__L: lib/libc__L lib/libgcc_s__L: lib/libc_nonshared__L lib/libcxxrt__L: lib/libgcc_s__L _prebuild_libs= ${_kerberos5_lib_libasn1} \ ${_kerberos5_lib_libhdb} \ ${_kerberos5_lib_libheimbase} \ ${_kerberos5_lib_libheimntlm} \ ${_libsqlite3} \ ${_kerberos5_lib_libheimipcc} \ ${_kerberos5_lib_libhx509} ${_kerberos5_lib_libkrb5} \ ${_kerberos5_lib_libroken} \ ${_kerberos5_lib_libwind} \ lib/libbz2 ${_libcom_err} lib/libcrypt \ lib/libc++ \ lib/libelf lib/libexpat \ lib/libfigpar \ ${_lib_libgssapi} \ lib/libjail \ lib/libkiconv lib/libkvm lib/liblzma lib/libmd lib/libnv \ lib/libzstd \ ${_lib_casper} \ lib/ncurses/tinfo \ lib/ncurses/ncurses \ lib/ncurses/form \ lib/libpam/libpam lib/libthr \ ${_lib_libradius} lib/libsbuf lib/libtacplus \ lib/libgeom \ ${_lib_librt} \ ${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \ ${_cddl_lib_libuutil} \ ${_cddl_lib_libavl} \ ${_cddl_lib_libicp} \ ${_cddl_lib_libicp_rescue} \ ${_cddl_lib_libspl} \ ${_cddl_lib_libtpool} \ ${_cddl_lib_libzfs_core} ${_cddl_lib_libzfs} \ ${_cddl_lib_libzutil} \ ${_cddl_lib_libctf} ${_cddl_lib_libzfsbootenv} \ lib/libufs \ lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \ + lib/libxo \ ${_secure_lib_libcrypto} ${_secure_lib_libssl} \ ${_lib_libldns} ${_secure_lib_libssh} .if ${MK_DIALOG} != "no" _prebuild_libs+= gnu/lib/libdialog gnu/lib/libdialog__L: lib/msun__L lib/ncurses/tinfo__L lib/ncurses/ncurses__L .endif .if ${MK_GOOGLETEST} != "no" _prebuild_libs+= lib/libregex .endif lib/libgeom__L: lib/libexpat__L lib/libsbuf__L lib/libkvm__L: lib/libelf__L .if ${MK_RADIUS_SUPPORT} != "no" _lib_libradius= lib/libradius .endif lib/ncurses/ncurses__L: lib/ncurses/tinfo__L lib/ncurses/form__L: lib/ncurses/ncurses__L .if ${MK_OFED} != "no" _prebuild_libs+= \ lib/ofed/libibverbs \ lib/ofed/libibmad \ lib/ofed/libibumad \ lib/ofed/complib \ lib/ofed/libmlx5 lib/ofed/libibmad__L: lib/ofed/libibumad__L lib/ofed/complib__L: lib/libthr__L lib/ofed/libmlx5__L: lib/ofed/libibverbs__L lib/libthr__L .endif .if ${MK_CASPER} != "no" _lib_casper= lib/libcasper .endif lib/libpjdlog__L: lib/libutil__L lib/libcasper__L: lib/libnv__L lib/liblzma__L: lib/libmd__L lib/libthr__L lib/libzstd__L: lib/libthr__L lib/librt__L: lib/libthr__L _generic_libs= ${_cddl_lib} gnu/lib ${_kerberos5_lib} lib ${_secure_lib} .if ${MK_IPFILTER} != "no" _generic_libs+= sbin/ipf/libipf .endif .for _DIR in ${LOCAL_LIB_DIRS} .if ${_DIR} == ".WAIT" || (empty(_generic_libs:M${_DIR}) && exists(${.CURDIR}/${_DIR}/Makefile)) _generic_libs+= ${_DIR} .endif .endfor lib/libtacplus__L: lib/libmd__L lib/libpam/libpam__L +lib/libxo__L: lib/libutil__L .if ${MK_CDDL} != "no" _cddl_lib_libumem= cddl/lib/libumem _cddl_lib_libnvpair= cddl/lib/libnvpair _cddl_lib_libavl= cddl/lib/libavl _cddl_lib_libuutil= cddl/lib/libuutil _cddl_lib_libspl= cddl/lib/libspl cddl/lib/libavl__L: cddl/lib/libspl__L cddl/lib/libnvpair__L: cddl/lib/libspl__L cddl/lib/libuutil__L: cddl/lib/libavl__L cddl/lib/libspl__L .if ${MK_ZFS} != "no" _lib_librt= lib/librt _cddl_lib_libicp= cddl/lib/libicp _cddl_lib_libicp_rescue= cddl/lib/libicp_rescue _cddl_lib_libtpool= cddl/lib/libtpool _cddl_lib_libzutil= cddl/lib/libzutil _cddl_lib_libzfs_core= cddl/lib/libzfs_core _cddl_lib_libzfs= cddl/lib/libzfs _cddl_lib_libzfsbootenv= cddl/lib/libzfsbootenv cddl/lib/libtpool__L: cddl/lib/libspl__L cddl/lib/libzutil__L: cddl/lib/libavl__L lib/libgeom__L lib/msun__L cddl/lib/libtpool__L cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L cddl/lib/libspl__L cddl/lib/libzutil__L cddl/lib/libzfs__L: cddl/lib/libzfs_core__L lib/msun__L lib/libutil__L lib/librt__L cddl/lib/libzfs__L: lib/libthr__L lib/libmd__L lib/libz__L cddl/lib/libumem__L cddl/lib/libzfs__L: cddl/lib/libuutil__L cddl/lib/libavl__L lib/libgeom__L cddl/lib/libzfs__L: cddl/lib/libnvpair__L cddl/lib/libzutil__L cddl/lib/libzfs__L: secure/lib/libcrypto__L cddl/lib/libzfsbootenv__L: cddl/lib/libzfs__L lib/libbe__L: cddl/lib/libzfs__L cddl/lib/libzfsbootenv__L .endif _cddl_lib_libctf= cddl/lib/libctf _cddl_lib= cddl/lib cddl/lib/libctf__L: lib/libz__L cddl/lib/libspl__L +cddl/lib/libdtrace__L: lib/libxo__L .endif # cddl/lib/libdtrace requires lib/libproc and lib/librtld_db _prebuild_libs+= lib/libprocstat lib/libproc lib/librtld_db lib/libprocstat__L: lib/libelf__L lib/libkvm__L lib/libutil__L lib/libproc__L: lib/libprocstat__L lib/librtld_db__L: lib/libprocstat__L .if ${MK_CRYPT} != "no" .if ${MK_OPENSSL} != "no" _secure_lib_libcrypto= secure/lib/libcrypto _secure_lib_libssl= secure/lib/libssl lib/libradius__L secure/lib/libssl__L: secure/lib/libcrypto__L secure/lib/libcrypto__L: lib/libthr__L .if ${MK_LDNS} != "no" _lib_libldns= lib/libldns lib/libldns__L: secure/lib/libssl__L .endif .if ${MK_OPENSSH} != "no" _secure_lib_libssh= secure/lib/libssh secure/lib/libssh__L: lib/libz__L secure/lib/libcrypto__L lib/libcrypt__L .if ${MK_LDNS} != "no" secure/lib/libssh__L: lib/libldns__L .endif .if ${MK_GSSAPI} != "no" && ${MK_KERBEROS_SUPPORT} != "no" secure/lib/libssh__L: lib/libgssapi__L kerberos5/lib/libkrb5__L \ kerberos5/lib/libhx509__L kerberos5/lib/libasn1__L lib/libcom_err__L \ lib/libmd__L kerberos5/lib/libroken__L .endif .endif .endif _secure_lib= secure/lib .endif .if ${MK_KERBEROS} != "no" kerberos5/lib/libasn1__L: lib/libcom_err__L kerberos5/lib/libroken__L kerberos5/lib/libhdb__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ kerberos5/lib/libkrb5__L kerberos5/lib/libroken__L \ kerberos5/lib/libwind__L lib/libsqlite3__L kerberos5/lib/libheimntlm__L: secure/lib/libcrypto__L kerberos5/lib/libkrb5__L \ kerberos5/lib/libroken__L lib/libcom_err__L kerberos5/lib/libhx509__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ secure/lib/libcrypto__L kerberos5/lib/libroken__L kerberos5/lib/libwind__L kerberos5/lib/libkrb5__L: kerberos5/lib/libasn1__L lib/libcom_err__L \ lib/libcrypt__L secure/lib/libcrypto__L kerberos5/lib/libhx509__L \ kerberos5/lib/libroken__L kerberos5/lib/libwind__L \ kerberos5/lib/libheimbase__L kerberos5/lib/libheimipcc__L kerberos5/lib/libroken__L: lib/libcrypt__L kerberos5/lib/libwind__L: kerberos5/lib/libroken__L lib/libcom_err__L kerberos5/lib/libheimbase__L: lib/libthr__L kerberos5/lib/libheimipcc__L: kerberos5/lib/libroken__L kerberos5/lib/libheimbase__L lib/libthr__L .endif lib/libsqlite3__L: lib/libthr__L .if ${MK_GSSAPI} != "no" _lib_libgssapi= lib/libgssapi .endif .if ${MK_KERBEROS} != "no" _kerberos5_lib= kerberos5/lib _kerberos5_lib_libasn1= kerberos5/lib/libasn1 _kerberos5_lib_libhdb= kerberos5/lib/libhdb _kerberos5_lib_libheimbase= kerberos5/lib/libheimbase _kerberos5_lib_libkrb5= kerberos5/lib/libkrb5 _kerberos5_lib_libhx509= kerberos5/lib/libhx509 _kerberos5_lib_libroken= kerberos5/lib/libroken _kerberos5_lib_libheimntlm= kerberos5/lib/libheimntlm _libsqlite3= lib/libsqlite3 _kerberos5_lib_libheimipcc= kerberos5/lib/libheimipcc _kerberos5_lib_libwind= kerberos5/lib/libwind _libcom_err= lib/libcom_err .endif .if ${MK_NIS} != "no" _lib_libypclnt= lib/libypclnt .endif .if ${MK_OPENSSL} == "no" lib/libradius__L: lib/libmd__L .endif lib/libproc__L: \ ${_cddl_lib_libctf:D${_cddl_lib_libctf}__L} \ lib/libelf__L lib/librtld_db__L lib/libutil__L lib/libcxxrt__L .for _lib in ${_prereq_libs} ${_lib}__PL: .PHONY .MAKE .if !defined(_MKSHOWCONFIG) && exists(${.CURDIR}/${_lib}) ${_+_}@${ECHODIR} "===> ${_lib} (obj,all,install)"; \ cd ${.CURDIR}/${_lib}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ obj; fi; \ ${MAKE} MK_TESTS=no MK_PROFILE=no -DNO_PIC \ DIRPRFX=${_lib}/ all; \ ${MAKE} MK_TESTS=no MK_PROFILE=no -DNO_PIC \ DIRPRFX=${_lib}/ install .endif .endfor .for _lib in ${_startup_libs} ${_prebuild_libs} ${_generic_libs} ${_lib}__L: .PHONY .MAKE .if !defined(_MKSHOWCONFIG) && exists(${.CURDIR}/${_lib}) ${_+_}@${ECHODIR} "===> ${_lib} (obj,all,install)"; \ cd ${.CURDIR}/${_lib}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ obj; fi; \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ all; \ ${MAKE} MK_TESTS=no DIRPRFX=${_lib}/ install .endif .endfor _prereq_libs: ${_prereq_libs:S/$/__PL/} _startup_libs: ${_startup_libs:S/$/__L/} _prebuild_libs: ${_prebuild_libs:S/$/__L/} _generic_libs: ${_generic_libs:S/$/__L/} # Enable SUBDIR_PARALLEL when not calling 'make all', unless called from # 'everything' with _PARALLEL_SUBDIR_OK set. This is because it is unlikely # that running 'make all' from the top-level, especially with a SUBDIR_OVERRIDE # or LOCAL_DIRS set, will have a reliable build if SUBDIRs are built in # parallel. This is safe for the world stage of buildworld though since it has # already built libraries in a proper order and installed includes into # WORLDTMP. Special handling is done for SUBDIR ordering for 'install*' to # avoid trashing a system if it crashes mid-install. .if !make(all) || defined(_PARALLEL_SUBDIR_OK) SUBDIR_PARALLEL= .endif .include .if make(check-old) || make(check-old-dirs) || \ make(check-old-files) || make(check-old-libs) || \ make(delete-old) || make(delete-old-dirs) || \ make(delete-old-files) || make(delete-old-libs) || \ make(list-old-dirs) || make(list-old-files) || make(list-old-libs) # # check for / delete old files section # .include "ObsoleteFiles.inc" OLD_LIBS_MESSAGE="Please be sure no application still uses those libraries, \ else you can not start such an application. Consult UPDATING for more \ information regarding how to cope with the removal/revision bump of a \ specific library." .if !defined(BATCH_DELETE_OLD_FILES) RM_I=-i .else RM_I=-fv .endif list-old-files: .PHONY @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V "OLD_FILES:ts\n" -V "OLD_FILES:Musr/share/*.gz:R:ts\n" \ -V "MOVED_LIBS:ts\n" \ ${_ALL_libcompats:@v@-V "OLD_FILES:Mlib/*.so.*:S,^lib,usr/lib$v,:ts\n"@} \ ${_ALL_libcompats:@v@-V "OLD_FILES:Musr/lib/*:S,^usr/lib,usr/lib$v,:ts\n"@} | \ sort delete-old-files: .PHONY @echo ">>> Removing old files (only deletes safe to delete libs)" # Ask for every old file if the user really wants to remove it. # It's annoying, but better safe than sorry. # NB: We cannot pass the list of OLD_FILES as a parameter because the # argument list will get too long. Using .for/.endfor make "loops" will make # the Makefile parser segfault. @exec 3<&0; \ cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} list-old-files | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ chflags noschg "${DESTDIR}/$${file}" 2>/dev/null || true; \ rm ${RM_I} "${DESTDIR}/$${file}" <&3; \ fi; \ for ext in debug symbols; do \ if ! [ -e "${DESTDIR}/$${file}" ] && [ -f \ "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ rm ${RM_I} "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" \ <&3; \ fi; \ done; \ done # Remove catpages without corresponding manpages. @exec 3<&0; \ find ${DESTDIR}/usr/share/man/cat* ! -type d 2>/dev/null | sort | \ sed -ep -e's:${DESTDIR}/usr/share/man/cat:${DESTDIR}/usr/share/man/man:' | \ while read catpage; do \ read manpage; \ if [ ! -e "$${manpage}" ]; then \ rm ${RM_I} $${catpage} <&3; \ fi; \ done @echo ">>> Old files removed" check-old-files: .PHONY @echo ">>> Checking for old files" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} list-old-files | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ echo "${DESTDIR}/$${file}"; \ fi; \ for ext in debug symbols; do \ if [ -f "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}"; \ fi; \ done; \ done # Check for catpages without corresponding manpages. @find ${DESTDIR}/usr/share/man/cat* ! -type d 2>/dev/null | \ sed -ep -e's:${DESTDIR}/usr/share/man/cat:${DESTDIR}/usr/share/man/man:' | \ while read catpage; do \ read manpage; \ if [ ! -e "$${manpage}" ]; then \ echo $${catpage}; \ fi; \ done | sort list-old-libs: .PHONY @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V "OLD_LIBS:ts\n" \ ${_ALL_libcompats:@v@-V "OLD_LIBS:Mlib/*:S,^lib,usr/lib$v,:ts\n"@} \ ${_ALL_libcompats:@v@-V "OLD_LIBS:Musr/lib/*:S,^usr/lib,usr/lib$v,:ts\n"@} \ ${_ALL_libcompats:@v@-V "OLD_LIBS:Mlib/casper/*:S,^lib/casper,usr/lib$v,:ts\n"@} | \ sort delete-old-libs: .PHONY @echo ">>> Removing old libraries" @echo "${OLD_LIBS_MESSAGE}" | fmt @exec 3<&0; \ cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} list-old-libs | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ chflags noschg "${DESTDIR}/$${file}" 2>/dev/null || true; \ rm ${RM_I} "${DESTDIR}/$${file}" <&3; \ fi; \ for ext in debug symbols; do \ if ! [ -e "${DESTDIR}/$${file}" ] && [ -f \ "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ rm ${RM_I} "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" \ <&3; \ fi; \ done; \ done @echo ">>> Old libraries removed" check-old-libs: .PHONY @echo ">>> Checking for old libraries" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} list-old-libs | \ while read file; do \ if [ -f "${DESTDIR}/$${file}" -o -L "${DESTDIR}/$${file}" ]; then \ echo "${DESTDIR}/$${file}"; \ fi; \ for ext in debug symbols; do \ if [ -f "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${file}.$${ext}"; \ fi; \ done; \ done list-old-dirs: .PHONY @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} ${.TARGET} \ -V OLD_DIRS | sed -E 's/[[:space:]]+/\n/g' | sort -r delete-old-dirs: .PHONY @echo ">>> Removing old directories" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} list-old-dirs | \ while read dir; do \ if [ -d "${DESTDIR}/$${dir}" ]; then \ rmdir -v "${DESTDIR}/$${dir}" || true; \ elif [ -L "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ if [ -d "${DESTDIR}${DEBUGDIR}/$${dir}" ]; then \ rmdir -v "${DESTDIR}${DEBUGDIR}/$${dir}" || true; \ elif [ -L "${DESTDIR}${DEBUGDIR}/$${dir}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ done @echo ">>> Old directories removed" check-old-dirs: .PHONY @echo ">>> Checking for old directories" @cd ${.CURDIR}; \ ${MAKE} -f ${.CURDIR}/Makefile.inc1 ${.MAKEFLAGS} list-old-dirs | \ while read dir; do \ if [ -d "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir}"; \ elif [ -L "${DESTDIR}/$${dir}" ]; then \ echo "${DESTDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ if [ -d "${DESTDIR}${DEBUGDIR}/$${dir}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${dir}"; \ elif [ -L "${DESTDIR}${DEBUGDIR}/$${dir}" ]; then \ echo "${DESTDIR}${DEBUGDIR}/$${dir} is a link, please remove everything manually."; \ fi; \ done delete-old: delete-old-files delete-old-dirs .PHONY @echo "To remove old libraries run '${MAKE_CMD} delete-old-libs'." check-old: check-old-files check-old-libs check-old-dirs .PHONY @echo "To remove old files and directories run '${MAKE_CMD} delete-old'." @echo "To remove old libraries run '${MAKE_CMD} delete-old-libs'." .endif # # showconfig - show build configuration. # # ignore lower case knobs (not for use with WITH*) # showconfig: .PHONY @(${MAKE} -n -f ${.CURDIR}/sys/conf/kern.opts.mk -V dummy -dg1 UPDATE_DEPENDFILE=no NO_OBJ=yes MACHINE=${TARGET} MACHINE_ARCH=${TARGET_ARCH}; \ ${MAKE} -n -f ${.CURDIR}/share/mk/src.opts.mk -V dummy -dg1 UPDATE_DEPENDFILE=no NO_OBJ=yes) 2>&1 | grep '^MK_[A-Z]' | sort -u .if !empty(KRNLOBJDIR) && !empty(KERNCONF) DTBOUTPUTPATH= ${KRNLOBJDIR}/${KERNCONF}/ .if !defined(FDT_DTS_FILE) || empty(FDT_DTS_FILE) .if !defined(_MKSHOWCONFIG) && exists(${KERNCONFDIR}/${KERNCONF}) FDT_DTS_FILE!= awk 'BEGIN {FS="="} /^makeoptions[[:space:]]+FDT_DTS_FILE/ {print $$2}' \ '${KERNCONFDIR}/${KERNCONF}' ; echo .endif .endif .endif .if !defined(DTBOUTPUTPATH) || !exists(${DTBOUTPUTPATH}) DTBOUTPUTPATH= ${.CURDIR} .endif # # Build 'standalone' Device Tree Blob # builddtb: .PHONY @PATH=${TMPPATH:Q} MACHINE=${TARGET} \ sh ${.CURDIR}/sys/tools/fdt/make_dtb.sh ${.CURDIR}/sys \ "${FDT_DTS_FILE}" ${DTBOUTPUTPATH} ############### # cleanworld # In the following, the first 'rm' in a series will usually remove all # files and directories. If it does not, then there are probably some # files with file flags set, so this unsets them and tries the 'rm' a # second time. There are situations where this target will be cleaning # some directories via more than one method, but that duplication is # needed to correctly handle all the possible situations. Removing all # files without file flags set in the first 'rm' instance saves time, # because 'chflags' will need to operate on fewer files afterwards. # # It is expected that BW_CANONICALOBJDIR == the CANONICALOBJDIR as would be # created by bsd.obj.mk, except that we don't want to .include that file # in this makefile. We don't do a cleandir walk if MK_AUTO_OBJ is yes # since it is not possible for files to land in the wrong place. # .if make(cleanworld) BW_CANONICALOBJDIR:=${OBJTOP}/ .elif make(cleankernel) BW_CANONICALOBJDIR:=${KRNLOBJDIR}/${KERNCONF}/ .elif make(cleanuniverse) BW_CANONICALOBJDIR:=${OBJROOT} .if ${MK_UNIFIED_OBJDIR} == "no" .error ${.TARGETS} only supported with WITH_UNIFIED_OBJDIR enabled. .endif .endif cleanworld cleanuniverse cleankernel: .PHONY .if !empty(BW_CANONICALOBJDIR) && exists(${BW_CANONICALOBJDIR}) && \ ${.CURDIR:tA} != ${BW_CANONICALOBJDIR:tA} -(cd ${BW_CANONICALOBJDIR} && rm -rf *) -chflags -R 0 ${BW_CANONICALOBJDIR} -(cd ${BW_CANONICALOBJDIR} && rm -rf *) .endif .if make(cleanworld) && ${MK_AUTO_OBJ} == "no" && \ (empty(BW_CANONICALOBJDIR) || ${.CURDIR:tA} == ${BW_CANONICALOBJDIR:tA}) .if ${.CURDIR} == ${.OBJDIR} || ${.CURDIR}/obj == ${.OBJDIR} # To be safe in this case, fall back to a 'make cleandir' ${_+_}@cd ${.CURDIR}; ${MAKE} cleandir .endif .endif .if ${TARGET} == ${MACHINE} && ${TARGET_ARCH} == ${MACHINE_ARCH} XDEV_CPUTYPE?=${CPUTYPE} .else XDEV_CPUTYPE?=${TARGET_CPUTYPE} .endif NOFUN=-DNO_FSCHG MK_HTML=no -DNO_LINT \ MK_MAN=no MK_NLS=no MK_PROFILE=no \ MK_KERBEROS=no MK_RESCUE=no MK_TESTS=no MK_WERROR=no \ TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ CPUTYPE=${XDEV_CPUTYPE} XDDIR=${TARGET_ARCH}-freebsd XDTP?=/usr/${XDDIR} .if ${XDTP:N/*} .error XDTP variable should be an absolute path .endif CDBOBJROOT= ${OBJROOT}${MACHINE}.${MACHINE_ARCH}/xdev/ CDBOBJTOP= ${CDBOBJROOT}${XDDIR} CDBENV= \ INSTALL="sh ${.CURDIR}/tools/install.sh" CDENV= ${CDBENV} \ TOOLS_PREFIX=${XDTP} CDMAKEARGS= \ OBJTOP=${CDBOBJTOP:Q} \ OBJROOT=${CDBOBJROOT:Q} CD2MAKEARGS= ${CDMAKEARGS} .if ${WANT_COMPILER_TYPE} == gcc || \ (defined(X_COMPILER_TYPE) && ${X_COMPILER_TYPE} == gcc) # GCC requires -isystem and -L when using a cross-compiler. --sysroot # won't set header path and -L is used to ensure the base library path # is added before the port PREFIX library path. CD2CFLAGS+= -isystem ${XDDESTDIR}/usr/include -L${XDDESTDIR}/usr/lib # GCC requires -B to find /usr/lib/crti.o when using a cross-compiler # combined with --sysroot. CD2CFLAGS+= -B${XDDESTDIR}/usr/lib # Force using libc++ for external GCC. .if defined(X_COMPILER_TYPE) && \ ${X_COMPILER_TYPE} == gcc && ${X_COMPILER_VERSION} >= 40800 CD2CXXFLAGS+= -isystem ${XDDESTDIR}/usr/include/c++/v1 -std=c++11 \ -nostdinc++ .endif .endif CD2CFLAGS+= --sysroot=${XDDESTDIR}/ CD2ENV=${CDENV} CC="${CC} ${CD2CFLAGS}" CXX="${CXX} ${CD2CXXFLAGS} ${CD2CFLAGS}" \ CPP="${CPP} ${CD2CFLAGS}" \ MACHINE=${TARGET} MACHINE_ARCH=${TARGET_ARCH} CDTMP= ${OBJTOP}/${XDDIR}/tmp CDMAKE=${CDENV} PATH=${CDTMP:Q}/usr/bin:${PATH:Q} ${MAKE} ${CDMAKEARGS} ${NOFUN} CD2MAKE=${CD2ENV} PATH=${CDTMP:Q}/usr/bin:${XDDESTDIR:Q}/usr/bin:${PATH:Q} \ ${MAKE} ${CD2MAKEARGS} ${NOFUN} .if ${MK_META_MODE} != "no" # Don't rebuild build-tools targets during normal build. CD2MAKE+= BUILD_TOOLS_META=.NOMETA .endif XDDESTDIR=${DESTDIR}${XDTP} .ORDER: xdev-build xdev-install xdev-links xdev: xdev-build xdev-install .PHONY .ORDER: _xb-worldtmp _xb-bootstrap-tools _xb-build-tools _xb-cross-tools xdev-build: _xb-worldtmp _xb-bootstrap-tools _xb-build-tools _xb-cross-tools .PHONY _xb-worldtmp: .PHONY mkdir -p ${CDTMP}/usr ${WORLDTMP_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${CDTMP}/usr >/dev/null _xb-bootstrap-tools: .PHONY .for _tool in \ ${_clang_tblgen} \ ${_yacc} ${_+_}@${ECHODIR} "===> ${_tool} (obj,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${CDMAKE} DIRPRFX=${_tool}/ obj; fi; \ ${CDMAKE} DIRPRFX=${_tool}/ all; \ ${CDMAKE} DIRPRFX=${_tool}/ DESTDIR=${CDTMP} install .endfor _xb-build-tools: .PHONY ${_+_}@cd ${.CURDIR}; \ ${CDBENV} ${MAKE} ${CDMAKEARGS} -f Makefile.inc1 ${NOFUN} build-tools XDEVDIRS= \ ${_clang_libs} \ ${_lld} \ ${_elftctools} \ usr.bin/ar \ ${_clang} _xb-cross-tools: .PHONY .for _tool in ${XDEVDIRS} ${_+_}@${ECHODIR} "===> xdev ${_tool} (obj,all)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${CDMAKE} DIRPRFX=${_tool}/ obj; fi; \ ${CDMAKE} DIRPRFX=${_tool}/ all .endfor _xi-mtree: .PHONY ${_+_}@${ECHODIR} "mtree populating ${XDDESTDIR}" mkdir -p ${XDDESTDIR} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.root.dist \ -p ${XDDESTDIR} >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.usr.dist \ -p ${XDDESTDIR}/usr >/dev/null ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.include.dist \ -p ${XDDESTDIR}/usr/include >/dev/null .for d in ${LIBCOMPAT_INCLUDE_DIRS} mkdir -p ${XDDESTDIR}/usr/include/${d} .endfor .for libcompat in ${libcompats} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.lib${libcompat}.dist \ -p ${XDDESTDIR}/usr >/dev/null .endfor .if ${MK_TESTS} != "no" mkdir -p ${XDDESTDIR}${TESTSBASE} ${DESTDIR_MTREE} -f ${.CURDIR}/etc/mtree/BSD.tests.dist \ -p ${XDDESTDIR}${TESTSBASE} >/dev/null .endif .ORDER: xdev-build _xi-mtree _xi-cross-tools _xi-includes _xi-libraries xdev-install: xdev-build _xi-mtree _xi-cross-tools _xi-includes _xi-libraries .PHONY _xi-cross-tools: .PHONY @echo "_xi-cross-tools" .for _tool in ${XDEVDIRS} ${_+_}@${ECHODIR} "===> xdev ${_tool} (install)"; \ cd ${.CURDIR}/${_tool}; \ ${CDMAKE} DIRPRFX=${_tool}/ install DESTDIR=${XDDESTDIR} .endfor _xi-includes: .PHONY .if !defined(NO_OBJWALK) ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 _obj \ DESTDIR=${XDDESTDIR} .endif ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 includes \ DESTDIR=${XDDESTDIR} _xi-libraries: .PHONY ${_+_}cd ${.CURDIR}; ${CD2MAKE} -f Makefile.inc1 libraries \ DESTDIR=${XDDESTDIR} xdev-links: .PHONY ${_+_}cd ${XDDESTDIR}/usr/bin; \ mkdir -p ../../../../usr/bin; \ for i in *; do \ ln -sf ../../${XDTP}/usr/bin/$$i \ ../../../../usr/bin/${XDDIR}-$$i; \ ln -sf ../../${XDTP}/usr/bin/$$i \ ../../../../usr/bin/${XDDIR}${_REVISION}-$$i; \ done diff --git a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 index 1745519c2c1a..ab8c672a95a1 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 +++ b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 @@ -1,821 +1,1233 @@ .\" CDDL HEADER START .\" .\" The contents of this file are subject to the terms of the .\" Common Development and Distribution License (the "License"). .\" You may not use this file except in compliance with the License. .\" .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE .\" or http://www.opensolaris.org/os/licensing. .\" See the License for the specific language governing permissions .\" and limitations under the License. .\" .\" When distributing Covered Code, include this CDDL HEADER in each .\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. .\" If applicable, add the following below this CDDL HEADER, with the .\" fields enclosed by brackets "[]" replaced with your own identifying .\" information: Portions Copyright [yyyy] [name of copyright owner] .\" .\" CDDL HEADER END .\" Copyright (c) 2006, Sun Microsystems, Inc. All Rights Reserved. .\" -.Dd February 24, 2023 +.\" $FreeBSD$ +.\" +.Dd September 8, 2023 .Dt DTRACE 1 .Os .Sh NAME .Nm dtrace .Nd dynamic tracing compiler and tracing utility .Sh SYNOPSIS .Nm .Op Fl 32 | Fl 64 -.Op Fl aACdeFGhHlqSvVwZ +.Op Fl aACdeFGhHlOqSvVwZ +.Op Fl -libxo .Op Fl b Ar bufsz .Op Fl c Ar cmd .Op Fl D Ar name Op Ns = Ns value .Op Fl I Ar path .Op Fl L Ar path .Op Fl o Ar output .Op Fl s Ar script .Op Fl U Ar name .Op Fl x Ar arg Op Ns = Ns value .Op Fl X Cm a | c | s | t .Op Fl p Ar pid .Op Fl P Ar provider Oo Oo Ar predicate Oc Ar action Oc .Op Fl m Oo Ar provider : Oc Ar module Oo Oo Ar predicate Oc Ar action Oc .Op Fl f Oo Oo Ar provider : Oc Ar module : Oc Ar function Oo Oo Ar predicate \ Oc Ar action Oc .Op Fl n Oo Oo Oo Ar provider : Oc Ar module : Oc Ar function : Oc Ar name \ Oo Oo Ar predicate Oc Ar action Oc .Op Fl i Ar probe-id Oo Oo Ar predicate Oc Ar action Oc .Sh DESCRIPTION DTrace is a comprehensive dynamic tracing framework ported from Solaris. DTrace provides a powerful infrastructure that permits administrators, developers, and service personnel to concisely answer arbitrary questions about the behavior of the operating system and user programs. .Pp The .Nm command provides a generic interface to the essential services provided by the DTrace facility, including: .Bl -bullet -offset indent .It Options that list the set of probes and providers currently published by DTrace .It Options that enable probes directly using any of the probe description specifiers (provider, module, function, name) .It Options that run the D compiler and compile one or more D program files or programs written directly on the command line .It Options that generate anonymous tracing programs .It Options that generate program stability reports .It Options that modify DTrace tracing and buffering behavior and enable additional D compiler features .El .Pp You can use .Nm to create D scripts by using it in a shebang declaration to create an interpreter file. You can also use .Nm to attempt to compile D programs and determine their properties without actually enabling traces using the .Fl e option. .Sh OPTIONS The arguments accepted by the .Fl P , .Fl m , .Fl f , .Fl n , and .Fl i options can include an optional D language .Ar predicate enclosed in slashes and an optional D language .Ar action statement list enclosed in braces. D program code specified on the command line must be appropriately quoted to avoid interpretation of meta-characters by the shell. .Pp The following options are supported: .Bl -tag -width indent .It Fl 32 | Fl 64 The D compiler produces programs using the native data model of the operating system kernel. If the .Fl 32 option is specified, .Nm forces the D compiler to compile a D program using the 32-bit data model. If the .Fl 64 option is specified, .Nm forces the D compiler to compile a D program using the 64-bit data model. These options are typically not required as .Nm selects the native data model as the default. The data model affects the sizes of integer types and other language properties. D programs compiled for either data model can be executed on both 32-bit and 64-bit kernels. The .Fl 32 and .Fl 64 options also determine the .Xr elf 5 file format (ELF32 or ELF64) produced by the .Fl G option. .It Fl a Claim anonymous tracing state and display the traced data. You can combine the .Fl a option with the .Fl e option to force .Nm to exit immediately after consuming the anonymous tracing state rather than continuing to wait for new data. .It Fl A Generate directives for anonymous tracing and write them to .Pa /boot/dtrace.dof . This option constructs a set of dtrace configuration file directives to enable the specified probes for anonymous tracing and then exits. By default, .Nm attempts to store the directives to the file .Pa /boot/dtrace.dof . This behavior can be modified using the .Fl o option to specify an alternate output file. .It Fl b Ar bufsz Set the principal trace buffer size to .Ar bufsz . The trace buffer size can include any of the size suffixes k, m, g, or t. If the buffer space cannot be allocated, .Nm dtrace attempts to reduce the buffer size or exit depending on the setting of the bufresize property. .It Fl c Ar cmd Run the specified command .Ar cmd and exit upon its completion. If more than one .Fl c option is present on the command line, .Nm dtrace exits when all commands have exited, reporting the exit status for each child process as it terminates. The process ID of the first command is made available to any D programs specified on the command line or using the .Fl s option through the .Li $target macro variable. .It Fl C Run the C preprocessor .Xr cpp 1 over D programs before compiling them. You can pass options to the C preprocessor using the .Fl D , .Fl U , .Fl I , and .Fl H options. You can select the degree of C standard conformance if you use the .Fl X option. For a description of the set of tokens defined by the D compiler when invoking the C preprocessor, see .Fl X . .It Fl d Dump the D script to standard output, after syntactic transformations have been applied. For example, if-statements in D are implemented using such transformations: a conditional clause in a probe body is replaced at compile-time by a separate probe predicated on the original condition. .It Fl D Ar name Op Ns = Ns value Define .Ar name when invoking .Xr cpp 1 (enabled using the .Fl C option). If you specify an additional .Ar value , the name is assigned the corresponding value. This option passes the .Fl D option to each .Xr cpp 1 invocation. .It Fl e Exit after compiling any requests and consuming anonymous tracing state .Fl ( a option) but prior to enabling any probes. You can combine this option with the .Fl a option to print anonymous tracing data and exit. You can also combine this option with D compiler options. This combination verifies that the programs compile without actually executing them and enabling the corresponding instrumentation. .It Fl f Oo Oo Ar provider : Oc Ar module : Oc Ar function Oo Oo Ar predicate \ Oc Ar action Oc Specify function name to trace or list .Fl ( l option). The corresponding argument can include any of the probe description forms .Ar provider:module:function , .Ar module:function , or .Ar function . Unspecified probe description fields are left blank and match any probes regardless of the values in those fields. If no qualifiers other than .Ar function are specified in the description, all probes with the corresponding .Ar function are matched. The .Fl f argument can be suffixed with an optional D probe clause. You can specify more than one .Fl f option on the command line at a time. .It Fl F Coalesce trace output by identifying function entry and return. Function entry probe reports are indented and their output is prefixed with .Ql -> . Function return probe reports are unindented and their output is prefixed with .Ql <- . System call entry probe reports are indented and their output is prefixed with .Ql => . System call return probe reports are unindented and their output is prefixed with .Ql <= . .It Fl G Generate an ELF file containing an embedded DTrace program. The DTrace probes specified in the program are saved inside of a relocatable ELF object which can be linked into another program. If the .Fl o option is present, the ELF file is saved using the pathname specified as the argument for this operand. If the .Fl o option is not present and the DTrace program is contained with a file whose name is .Ar filename.d , then the ELF file is saved using the name .Ar filename.o . Otherwise the ELF file is saved using the name d.out. .It Fl h Generate a header file containing macros that correspond to probes in the specified provider definitions. This option should be used to generate a header file that is included by other source files for later use with the .Fl G option. If the .Fl o option is present, the header file is saved using the pathname specified as the argument for that option. If the .Fl o option is not present and the DTrace program is contained within a file whose name is .Ar filename.d , then the header file is saved using the name .Ar filename.h . .It Fl H Print the pathnames of included files when invoking .Xr cpp 1 (enabled using the .Fl C option). This option passes the .Fl H option to each .Xr cpp 1 invocation, causing it to display the list of pathnames, one for each line, to standard error. .It Fl i Ar probe-id Op Oo Ar predicate Oc Ar action Specify probe identifier .Ar ( probe-id ) to trace or list .Ar ( l option). You can specify probe IDs using decimal integers as shown by `dtrace -l`. The .Fl i argument can be suffixed with an optional D probe clause. You can specify more than one .Fl i option at a time. .It Fl I Ar path Add the specified directory .Ar path to the search path for #include files when invoking .Xr cpp 1 (enabled using the .Fl C option). This option passes the .Fl I option to each .Xr cpp 1 invocation. The specified .Ar path is inserted into the search path ahead of the default directory list. .It Fl l List probes instead of enabling them. If the .Fl l option is specified, .Nm produces a report of the probes matching the descriptions given using the .Fl P , m , f , n , i , and .Fl s options. If none of these options are specified, this option lists all probes. .It Fl L Ar path Add the specified directory .Ar path to the search path for DTrace libraries. DTrace libraries are used to contain common definitions that can be used when writing D programs. The specified .Ar path is added after the default library search path. +.It Fl -libxo +Generate output via +.Xr libxo 3 . +This option is the same as specifying +.Sy oformat . .It Fl m Oo Ar provider : Oc Ar module Oo Oo Ar predicate Oc Ar action Oc Specify module name to trace or list .Fl ( l option). The corresponding argument can include any of the probe description forms .Ar provider:module or .Ar module . Unspecified probe description fields are left blank and match any probes regardless of the values in those fields. If no qualifiers other than .Ar module are specified in the description, all probes with a corresponding .Ar module are matched. The .Fl m argument can be suffixed with an optional D probe clause. More than one .Fl m option can be specified on the command line at a time. .It Fl n Oo Oo Oo Ar provider : Oc Ar module : Oc Ar function : Oc Ar name \ Oo Oo Ar predicate Oc Ar action Oc Specify probe name to trace or list .Fl ( l option). The corresponding argument can include any of the probe description forms .Ar provider:module:function:name , module:function:name , function:name , or .Ar name . Unspecified probe description fields are left blank and match any probes regardless of the values in those fields. If no qualifiers other than .Ar name are specified in the description, all probes with a corresponding .Ar name are matched. The .Fl n argument can be suffixed with an optional D probe clause. More than one .Fl n option can be specified on the command line at a time. +.It Fl O +This option causes +.Nm +to print all the aggregations upon exiting if +.Sy oformat +or +.Fl -libxo +are specified. .It Fl o Ar output Specify the .Ar output file for the .Fl A , G , and .Fl l options, or for the traced data itself. If the .Fl A option is present and .Fl o is not present, the default output file is .Pa /boot/dtrace.dof . If the .Fl G option is present and the .Fl s option's argument is of the form .Ar filename.d and .Fl o is not present, the default output file is .Ar filename.o . Otherwise the default output file is .Ar d.out . .It Fl p Ar pid Grab the specified process-ID .Ar pid , cache its symbol tables, and exit upon its completion. If more than one .Fl p option is present on the command line, .Nm exits when all commands have exited, reporting the exit status for each process as it terminates. The first process-ID is made available to any D programs specified on the command line or using the .Fl s option through the .Li $target macro variable. .It Fl P Ar provider Oo Oo Ar predicate Oc Ar action Oc Specify provider name to trace or list .Fl ( l option). The remaining probe description fields module, function, and name are left blank and match any probes regardless of the values in those fields. The .Fl P argument can be suffixed with an optional D probe clause. You can specify more than one .Fl P option on the command line at a time. .It Fl q Set quiet mode. .Nm suppresses messages such as the number of probes matched by the specified options and D programs and does not print column headers, the CPU ID, the probe ID, or insert newlines into the output. Only data traced and formatted by D program statements such as .Ql dtrace() and .Ql printf() is displayed to standard output. .It Fl s Ar script Compile the specified D program source file. If the .Fl e option is present, the program is compiled but instrumentation is not enabled. If the .Fl l option is present, the program is compiled and the set of probes matched by it is listed, but instrumentation is not enabled. If none of .Fl e , l , G , or .Fl A are present, the instrumentation specified by the D program is enabled and tracing begins. .It Fl S Show D compiler intermediate code. The D compiler produces a report of the intermediate code generated for each D program to standard error. .It Fl U Ar name Undefine the specified .Ar name when invoking .Xr cpp 1 (enabled using the .Fl C option). This option passes the .Fl U option to each .Xr cpp 1 invocation. .It Fl v Set verbose mode. If the .Fl v option is specified, .Nm produces a program stability report showing the minimum interface stability and dependency level for the specified D programs. .It Fl V Report the highest D programming interface version supported by .Nm . The version information is printed to standard output and the .Nm command exits. .It Fl w Permit destructive actions in D programs specified using the .Fl s , P , m , f , n , or .Fl i options. If the .Fl w option is not specified, .Nm does not permit the compilation or enabling of a D program that contains destructive actions. .It Fl x Ar arg Op Ns = Ns value Enable or modify a DTrace runtime option or D compiler option. Boolean options are enabled by specifying their name. Options with values are set by separating the option name and value with an equals sign (=). .Pp A .Ar size argument may be suffixed with one of .Cm K , .Cm M , .Cm G or .Cm T (either upper or lower case) to indicate a multiple of Kilobytes, Megabytes, Gigabytes or Terabytes respectively. .Pp A .Ar time argument may be suffixed with one of .Cm ns , .Cm nsec , .Cm us , .Cm usec , .Cm ms , .Cm msec , .Cm s , .Cm sec , .Cm m , .Cm min , .Cm h , .Cm hour , .Cm d , .Cm day , .Cm hz . If no suffix is specified .Cm hz will be used as the unit. .Bl -tag -width indent .It Sy aggrate Ns = Ns Ar time Rate of aggregation reading. .It Sy aggsize Ns = Ns Ar size Size of the aggregation buffer. .It Sy bufpolicy Ns = Ns Cm fill Ns | Ns Cm switch Ns | Ns Cm ring Specifies the buffer policy for the principal buffer. .It Sy bufresize Ns = Ns Cm auto Ns | Ns Cm manual Buffer resizing policy. .It Sy bufsize Ns = Ns Ar size Size of the per-CPU principal buffer. Same as the .Fl b flag. .It Sy cleanrate Ns = Ns Ar time Cleaning rate. Must be specified in number-per-second with the .Dq Li hz suffix. .It Sy cpu Ns = Ns Ar scalar Specifies the CPU on which to enable tracing. .It Sy cpp Run a C preprocessor over input files. Same as the .Fl C flag. .It Sy cpppath Ns = Ns Ar path Use the specified path for the C preprocessor rather than searching for .Dq cpp in .Ev PATH . .It Sy defaultargs Allow references to unspecified macro arguments. .It Sy destructive Allow destructive actions. Same as the .Fl w flag. .It Sy dynvarsize Ns = Ns Ar size Size of the dynamic variable space. .It Sy flowindent Turn on flow indentation. Same as the .Fl F flag. .It Sy grabanon Claim anonymous state. Same as the .Fl a flag. .It Sy jstackframes Ns = Ns Ar scalar Number of default stack frames for .Fn jstack . .It Sy jstackstrsize Ns = Ns Ar scalar Default string space size for .Fn jstack . .It Sy ldpath Ns = Ns Ar path When .Fl G is specified, use the specified path for a static linker rather than searching for .Dq "ld" in .Ev PATH . .It Sy libdir Ns = Ns Ar path Add a directory to the system library path. .It Sy nspec Ns = Ns Ar scalar Number of speculations. .It Sy nolibs Do not load D system libraries. .It Sy quiet Set quiet mode. Same as the .Fl q flag. .It Sy specsize Ns = Ns Ar size Size of the speculation buffer. .It Sy strsize Ns = Ns Ar size Maximum size of strings. .It Sy stackframes Ns = Ns Ar scalar Maximum number of kernelspace stack frames to unwind when executing the .Fn stack action. .It Sy stackindent Ns = Ns Ar scalar Number of whitespace characters to use when indenting .Fn stack and .Fn ustack output. +.It Sy oformat Ns = Ns Ar format +Specify the format to use for output. +Setting +.Sy oformat +to +.Ql text +makes +.Nm +use regular human-readable output which is its default behavior. +The options passed to +.Sy oformat +are directly forwarded to +.Xr libxo 3 . +Some of the supported formatters include +.Ql json , +.Ql xml +and +.Ql html . +Note that this option will cause +.Nm +to not produce any output unless printing functions are explicitly called, +or the +.Fl O +flag is specified. +For more information see +.Sx STRUCTURED OUTPUT . .It Sy statusrate Ns = Ns Ar time Rate of status checking. .It Sy switchrate Ns = Ns Ar time Rate of buffer switching. .It Sy syslibdir Ns = Ns Ar path Path to system libraries. Defaults to .Pa /usr/lib/dtrace . .It Sy ustackframes Ns = Ns Ar scalar Maximum number of userspace stack frames to unwind when executing the .Fn ustack action. .El .It Fl X Cm a | c | s | t Specify the degree of conformance to the ISO C standard that should be selected when invoking .Xr cpp 1 (enabled using the .Fl C option). The .Fl X option argument affects the value and presence of the __STDC__ macro depending upon the value of the argument letter. .sp The .Fl X option supports the following arguments: .Bl -tag -width indent .It a Default. ISO C plus K&R compatibility extensions, with semantic changes required by ISO C. This is the default mode if .Fl X is not specified. The predefined macro __STDC__ has a value of 0 when .Xr cpp 1 is invoked in conjunction with the .Fl Xa option. .It c Conformance. Strictly conformant ISO C, without K&R C compatibility extensions. The predefined macro __STDC__ has a value of 1 when .Xr cpp 1 is invoked in conjunction with the .Fl \&Xc option. .It s K&R C only. The macro __STDC__ is not defined when .Xr cpp 1 is invoked in conjunction with the .Fl Xs option. .It t Transition. ISO C plus K&R C compatibility extensions, without semantic changes required by ISO C. The predefined macro __STDC__ has a value of 0 when .Xr cpp 1 is invoked in conjunction with the .Fl Xt option. .El .Pp As the .Fl X option only affects how the D compiler invokes the C preprocessor, the .Fl Xa and .Fl Xt options are equivalent from the perspective of D and both are provided only to ease re-use of settings from a C build environment. .Pp Regardless of the .Fl X mode, the following additional C preprocessor definitions are always specified and valid in all modes: .Bl -bullet -offset indent .It __sun .It __unix .It __SVR4 .It __sparc (on SPARC systems only) .It __sparcv9 (on SPARC systems only when 64-bit programs are compiled) .It __i386 (on x86 systems only when 32-bit programs are compiled) .It __amd64 (on x86 systems only when 64-bit programs are compiled) .It __`uname -s`_`uname -r` (for example, .Ql FreeBSD_9.2-RELEASE . .It __SUNW_D=1 .It .No __SUNW_D_VERSION=0x Ns Ar MMmmmuuu .Pp Where .Ar MM is the major release value in hexadecimal, .Ar mmm is the minor release value in hexadecimal, and .Ar uuu is the micro release value in hexadecimal. .El .It Fl Z Permit probe descriptions that match zero probes. If the .Fl Z option is not specified, .Nm reports an error and exits if any probe descriptions specified in D program files .Fl ( s option) or on the command line .Fl ( P , m , f , n , or .Fl i options) contain descriptions that do not match any known probes. .El +.Sh STRUCTURED OUTPUT +.Nm +supports structured output using +.Xr libxo 3 . +The output will always have a top-level object called +.Dq dtrace , +followed by a list of objects +.Dq probes . +Each of the probe objects will to have a timestamp which is generated at +output time rather than probe firing time, an identifier for the CPU on +which the probe was executed, and the probe's full specification: +.Bd -literal +{ + "dtrace": { + "probes": [ + { + "timestamp": ..., + "cpu": ..., + "id": ..., + "provider": ..., + "module": ..., + "function": ..., + "name": ..., + "output": [ + ... (script-specific output) + ] + } + ] + } +} + + + + + ... + ... + ... + ... + ... + ... + ... + + ... (script-specific output) + + + +.Ed +.Pp +It is also possible for XML output to take the following form if some +of the fields are empty (in this example, module and function values +are absent): +.Bd -literal + + + + ... + + + ... + + ... (script-specific output) + + + +.Ed +.Pp +Similarly, +.Sy oformat +can be used to generate HTML: +.Bd -literal +
+
...
+
+
...
+
+
...
+
+
...
+
+
...
+
+
...
+
+
...
+
...
+
+.Ed +.Pp +Unlike JSON and XML, the +.Dq output +array is not present. +Instead, data is simply formatted into a div of class +.Dq data +and a data-tag is associated with each of the keys. +.Pp +The +.Dq output +array's contents depend on the probes' actions and is explained below. +The examples here are presented in JSON form as opposed to XML or HTML, +however the conversion explained above applies for all output formats. +.Pp +Any scalar output, such as output produced by the +.Fn trace +action is of form: +.Bd -literal +{ + "value": ... +} +.Ed +.Pp +The +.Fn printf +action begins with an object containing the formatted output of the +.Fn printf +action. +Subsequent objects contains the value of each of the arguments to +.Fn printf +in its raw form as if the +.Fn trace +action was used instead. +A +.Fn printf +statement which contains no arguments other than the message will only have +one object following the message object and its value will always be 0. +This is an artefact of the implementation and can safely be ignored. +.Bd -literal +# dtrace --libxo json,pretty -n 'BEGIN { printf("... %Y, ..", walltimestamp); }' + +{ + "message": "... 2023 Sep 7 16:49:02, .." +}, +{ + "value": 1694105342633402400 +}, +{ + ... +} +.Ed +.Pp +Scalar aggregations are aggregations which produce a single value for a given +key. +These aggregations include +.Fn count , +.Fn min , +.Fn max , +.Fn stddev +and +.Fn sum . +Each one of them is represented by the key containing their name. +For example, the output of a +.Fn stddev +aggregation will contain a key +.Dq stddev +inside an +.Dq aggregation-data +object: +.Bd -literal +{ + "aggregation-data": [ + { + "keys": [ + ... + ], + "stddev": ... + } + ], + "aggregation-name": ... +} +.Ed +.Pp +The +.Dq keys +field remains consistent across all aggregations, however +.Fn quantize , +.Fn lquantize +and +.Fn llquantize +need to be treated differently. +.Sy oformat +will create a new array of objects called +.Dq buckets . +Each of the objects contains a +.Dq value +and a +.Dq count +field which are +the left-hand side and the right-hand side of human-readable +.Nm +output respectively. +The full object has the following format: +.Bd -literal +{ + "aggregation-data": [ + ... + { + "keys": [ + ... + ], + "buckets": [ + { + "value": 32, + "count": 0 + }, + { + "value": 64, + "count": 17 + }, + ... + ], + }, + ... + ] + "aggregation-name": ... +} +.Ed +.Pp +Similar to scalar aggregations, named scalar actions such as +.Fn mod , +.Fn umod , +.Fn usym , +.Fn tracemem +and +.Fn printm +will output an object with the key being equal to the +name of the action. +For example, +.Fn printm +output would produce the following object: +.Bd -literal +{ + "printm": "0x4054171100" +} +.Ed +.Pp +.Fn sym +is slightly different. +While it will create a +.Dq sym +field which contains its value, in some cases it will also create additional +fields +.Dq object , +.Dq name +and +.Dq offset : +.Bd -literal +# dtrace -x oformat=json,pretty -On 'BEGIN { sym((uintptr_t)&`prison0); }' + +{ + "sym": "kernel`prison0", + "object": "kernel", + "name": "prison0" +} + +# dtrace --libxo json,pretty -On 'BEGIN { sym((uintptr_t)curthread); }' + +{ + "sym": "0xfffffe00c18d2000", + "offset": "0xfffffe00c18d2000" +} +.Ed +.Pp +.Fn stack +and +.Fn ustack +actions unroll each of the stack frames into its own object in an array. +The only real difference between them is that the +.Fn stack +action will produce a list called +.Dq stack-frames +while +.Fn ustack +will produce one called +.Dq ustack-frames . +The following is an example of their +.Sy oformat +output: +.Bd -literal +{ + "stack-frames": [ + { + "symbol": "dtrace.ko`dtrace_dof_create+0x35", + "module": "dtrace.ko", + "name": "dtrace_dof_create", + "offset": "0x35" + }, + { + "symbol": "dtrace.ko`dtrace_ioctl+0x81c", + "module": "dtrace.ko", + "name": "dtrace_ioctl", + "offset": "0x81c" + }, + ... + ] +} + +{ + "ustack-frames": [ + { + "symbol": "libc.so.7`ioctl+0xa", + "module": "libc.so.7", + "name": "ioctl", + "offset": "0xa" + }, + { + "symbol": "libdtrace.so.2`dtrace_go+0xf3", + "module": "libdtrace.so.2", + "name": "dtrace_go", + "offset": "0xf3" + }, + ... + ] +} +.Ed +.Pp +The +.Fn print +action produces a +.Dq type +list in the following form: +.Bd -literal +{ + "type": [ + { + "object-name": "kernel", + "name": "struct thread", + "ctfid": 2372 + }, + { + "member-name": "td_lock", + "name": "struct mtx *volatile", + "ctfid": 2035, + "value": "0xffffffff82158440" + }, + ... +} +.Ed +.Pp +If the type is invalid, a +.Dq warning +object will be produced containing the diagnostic message as well as two +possible optional fields: +.Dq type-identifier +which contains the CTF identifier of the type and +.Dq size containing the size of an integer, enum or float. +The fields generated will depend on the kind of error that was encountered +while processing the trace data. +.Pp +Finally, +.Sy oformat +provides a special pseudo-probe to represent drops. +As +.Nm +polls for various kinds of drops +.Sy oformat +will produce output similar to the following in order to represent drops: +.Bd -literal +{ + "cpu": -1, + "id": -1, + "provider": "dtrace", + "module": "INTERNAL", + "function": "INTERNAL", + "name": "DROP", + "timestamp": ..., + "count": ..., + "total": ..., + "kind": 2, + "msg": "... dynamic variable drops\n" +} +.Ed .Sh OPERANDS You can specify zero or more additional arguments on the .Nm command line to define a set of macro variables and so forth). The additional arguments can be used in D programs specified using the .Fl s option or on the command line. .Sh FILES .Bl -tag -width /boot/dtrace.dof -compact .It Pa /boot/dtrace.dof File for anonymous tracing directives. .El .Sh EXIT STATUS The following exit statuses are returned: .Bl -tag -width indent .It 0 Successful completion. .Pp For D program requests, an exit status of 0 indicates that programs were successfully compiled, probes were successfully enabled, or anonymous state was successfully retrieved. .Nm returns 0 even if the specified tracing requests encountered errors or drops. .It 1 An error occurred. .Pp For D program requests, an exit status of 1 indicates that program compilation failed or that the specified request could not be satisfied. .It 2 Invalid command line options or arguments were specified. .El .Sh SEE ALSO .Xr cpp 1 , .Xr elf 5 , .Xr SDT 9 .Rs .%T Solaris Dynamic Tracing Guide .Re .Sh HISTORY The .Nm utility first appeared in .Fx 7.1 . diff --git a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c index cdc476a43b08..dc68c6ef5f72 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c +++ b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c @@ -1,2026 +1,2084 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2012 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2023, Domagoj Stolfa. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef illumos #include #endif #include #ifdef illumos #include #endif #ifdef __FreeBSD__ #include #include #endif +#undef NORETURN /* needed because libxo redefines it */ +#include + typedef struct dtrace_cmd { void (*dc_func)(struct dtrace_cmd *); /* function to compile arg */ dtrace_probespec_t dc_spec; /* probe specifier context */ char *dc_arg; /* argument from main argv */ const char *dc_name; /* name for error messages */ const char *dc_desc; /* desc for error messages */ dtrace_prog_t *dc_prog; /* program compiled from arg */ char dc_ofile[PATH_MAX]; /* derived output file name */ } dtrace_cmd_t; #define DMODE_VERS 0 /* display version information and exit (-V) */ #define DMODE_EXEC 1 /* compile program for enabling (-a/e/E) */ #define DMODE_ANON 2 /* compile program for anonymous tracing (-A) */ #define DMODE_LINK 3 /* compile program for linking with ELF (-G) */ #define DMODE_LIST 4 /* compile program and list probes (-l) */ #define DMODE_HEADER 5 /* compile program for headergen (-h) */ #define E_SUCCESS 0 #define E_ERROR 1 #define E_USAGE 2 static const char DTRACE_OPTSTR[] = - "3:6:aAb:Bc:CdD:ef:FGhHi:I:lL:m:n:o:p:P:qs:SU:vVwx:X:Z"; + "3:6:aAb:Bc:CdD:ef:FGhHi:I:lL:m:n:o:Op:P:qs:SU:vVwx:X:Z"; static char **g_argv; static int g_argc; static char **g_objv; static int g_objc; static dtrace_cmd_t *g_cmdv; static int g_cmdc; static struct ps_prochandle **g_psv; static int g_psc; static int g_pslive; static char *g_pname; static int g_quiet; static int g_flowindent; static int g_intr; static int g_impatient; static int g_newline; #ifdef __FreeBSD__ static int g_siginfo; #endif static int g_total; static int g_cflags; static int g_oflags; static int g_verbose; static int g_exec = 1; static int g_mode = DMODE_EXEC; static int g_status = E_SUCCESS; static int g_grabanon = 0; static const char *g_ofile = NULL; static FILE *g_ofp; static dtrace_hdl_t *g_dtp; #ifdef illumos static char *g_etcfile = "/etc/system"; static const char *g_etcbegin = "* vvvv Added by DTrace"; static const char *g_etcend = "* ^^^^ Added by DTrace"; static const char *g_etc[] = { "*", "* The following forceload directives were added by dtrace(1M) to allow for", "* tracing during boot. If these directives are removed, the system will", "* continue to function, but tracing will not occur during boot as desired.", "* To remove these directives (and this block comment) automatically, run", "* \"dtrace -A\" without additional arguments. See the \"Anonymous Tracing\"", "* chapter of the Solaris Dynamic Tracing Guide for details.", "*", NULL }; #endif static int usage(FILE *fp) { static const char predact[] = "[[ predicate ] action ]"; (void) fprintf(fp, "Usage: %s [-32|-64] [-aACdeFGhHlqSvVwZ] " "[-b bufsz] [-c cmd] [-D name[=def]]\n\t[-I path] [-L path] " "[-o output] [-p pid] [-s script] [-U name]\n\t" "[-x opt[=val]] [-X a|c|s|t]\n\n" "\t[-P provider %s]\n" "\t[-m [ provider: ] module %s]\n" "\t[-f [[ provider: ] module: ] func %s]\n" "\t[-n [[[ provider: ] module: ] func: ] name %s]\n" "\t[-i probe-id %s] [ args ... ]\n\n", g_pname, predact, predact, predact, predact, predact); (void) fprintf(fp, "\tpredicate -> '/' D-expression '/'\n"); (void) fprintf(fp, "\t action -> '{' D-statements '}'\n"); (void) fprintf(fp, "\n" "\t-32 generate 32-bit D programs and ELF files\n" "\t-64 generate 64-bit D programs and ELF files\n\n" "\t-a claim anonymous tracing state\n" "\t-A generate driver.conf(4) directives for anonymous tracing\n" "\t-b set trace buffer size\n" "\t-c run specified command and exit upon its completion\n" "\t-C run cpp(1) preprocessor on script files\n" "\t-d dump script after syntactic transformations\n" "\t-D define symbol when invoking preprocessor\n" "\t-e exit after compiling request but prior to enabling probes\n" "\t-f enable or list probes matching the specified function name\n" "\t-F coalesce trace output by function\n" "\t-G generate an ELF file containing embedded dtrace program\n" "\t-h generate a header file with definitions for static probes\n" "\t-H print included files when invoking preprocessor\n" "\t-i enable or list probes matching the specified probe id\n" "\t-I add include directory to preprocessor search path\n" "\t-l list probes matching specified criteria\n" "\t-L add library directory to library search path\n" "\t-m enable or list probes matching the specified module name\n" "\t-n enable or list probes matching the specified probe name\n" "\t-o set output file\n" + "\t-O print output upon exiting (specific to oformat)\n" "\t-p grab specified process-ID and cache its symbol tables\n" "\t-P enable or list probes matching the specified provider name\n" "\t-q set quiet mode (only output explicitly traced data)\n" "\t-s enable or list probes according to the specified D script\n" "\t-S print D compiler intermediate code\n" "\t-U undefine symbol when invoking preprocessor\n" "\t-v set verbose mode (report stability attributes, arguments)\n" "\t-V report DTrace API version\n" "\t-w permit destructive actions\n" "\t-x enable or modify compiler and tracing options\n" "\t-X specify ISO C conformance settings for preprocessor\n" "\t-Z permit probe descriptions that match zero probes\n"); return (E_USAGE); } static void verror(const char *fmt, va_list ap) { int error = errno; (void) fprintf(stderr, "%s: ", g_pname); (void) vfprintf(stderr, fmt, ap); if (fmt[strlen(fmt) - 1] != '\n') (void) fprintf(stderr, ": %s\n", strerror(error)); } /*PRINTFLIKE1*/ static void fatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); verror(fmt, ap); va_end(ap); /* * Close the DTrace handle to ensure that any controlled processes are * correctly restored and continued. */ if (g_dtp) dtrace_close(g_dtp); exit(E_ERROR); } /*PRINTFLIKE1*/ static void dfatal(const char *fmt, ...) { #if !defined(illumos) && defined(NEED_ERRLOC) char *p_errfile = NULL; int errline = 0; #endif va_list ap; va_start(ap, fmt); (void) fprintf(stderr, "%s: ", g_pname); if (fmt != NULL) (void) vfprintf(stderr, fmt, ap); va_end(ap); if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') { (void) fprintf(stderr, ": %s\n", dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); } else if (fmt == NULL) { (void) fprintf(stderr, "%s\n", dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); } #if !defined(illumos) && defined(NEED_ERRLOC) dt_get_errloc(g_dtp, &p_errfile, &errline); if (p_errfile != NULL) printf("File '%s', line %d\n", p_errfile, errline); #endif /* * Close the DTrace handle to ensure that any controlled processes are * correctly restored and continued. */ dtrace_close(g_dtp); exit(E_ERROR); } /*PRINTFLIKE1*/ static void error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); verror(fmt, ap); va_end(ap); } /*PRINTFLIKE1*/ static void notice(const char *fmt, ...) { va_list ap; if (g_quiet) return; /* -q or quiet pragma suppresses notice()s */ va_start(ap, fmt); verror(fmt, ap); va_end(ap); } /*PRINTFLIKE1*/ static void oprintf(const char *fmt, ...) { va_list ap; int n; if (g_ofp == NULL) return; va_start(ap, fmt); n = vfprintf(g_ofp, fmt, ap); va_end(ap); if (n < 0) { if (errno != EINTR) { fatal("failed to write to %s", g_ofile ? g_ofile : ""); } clearerr(g_ofp); } } static char ** make_argv(char *s) { const char *ws = "\f\n\r\t\v "; char **argv = malloc(sizeof (char *) * (strlen(s) / 2 + 1)); int argc = 0; char *p = s; if (argv == NULL) return (NULL); for (p = strtok(s, ws); p != NULL; p = strtok(NULL, ws)) argv[argc++] = p; if (argc == 0) argv[argc++] = s; argv[argc] = NULL; return (argv); } static void dof_prune(const char *fname) { struct stat sbuf; size_t sz, i, j, mark, len; char *buf; int msg = 0, fd; if ((fd = open(fname, O_RDONLY)) == -1) { /* * This is okay only if the file doesn't exist at all. */ if (errno != ENOENT) fatal("failed to open %s", fname); return; } if (fstat(fd, &sbuf) == -1) fatal("failed to fstat %s", fname); if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL) fatal("failed to allocate memory for %s", fname); if (read(fd, buf, sz) != sz) fatal("failed to read %s", fname); buf[sz] = '\0'; (void) close(fd); if ((fd = open(fname, O_WRONLY | O_TRUNC)) == -1) fatal("failed to open %s for writing", fname); len = strlen("dof-data-"); for (mark = 0, i = 0; i < sz; i++) { if (strncmp(&buf[i], "dof-data-", len) != 0) continue; /* * This is only a match if it's in the 0th column. */ if (i != 0 && buf[i - 1] != '\n') continue; if (msg++ == 0) { error("cleaned up old anonymous " "enabling in %s\n", fname); } /* * We have a match. First write out our data up until now. */ if (i != mark) { if (write(fd, &buf[mark], i - mark) != i - mark) fatal("failed to write to %s", fname); } /* * Now scan forward until we scan past a newline. */ for (j = i; j < sz && buf[j] != '\n'; j++) continue; /* * Reset our mark. */ if ((mark = j + 1) >= sz) break; i = j; } if (mark < sz) { if (write(fd, &buf[mark], sz - mark) != sz - mark) fatal("failed to write to %s", fname); } (void) close(fd); free(buf); } #ifdef __FreeBSD__ /* * Use nextboot(8) to tell the loader to load DTrace kernel modules during * the next boot of the system. The nextboot(8) configuration is removed during * boot, so it will not persist indefinitely. */ static void bootdof_add(void) { char * const nbargv[] = { "nextboot", "-a", "-e", "dtraceall_load=\"YES\"", "-e", "dtrace_dof_load=\"YES\"", "-e", "dtrace_dof_name=\"/boot/dtrace.dof\"", "-e", "dtrace_dof_type=\"dtrace_dof\"", NULL, }; pid_t child; int err, status; err = posix_spawnp(&child, "nextboot", NULL, NULL, nbargv, NULL); if (err != 0) { error("failed to execute nextboot: %s", strerror(err)); exit(E_ERROR); } if (waitpid(child, &status, 0) != child) fatal("waiting for nextboot"); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { error("nextboot returned with status %d", status); exit(E_ERROR); } } #else static void etcsystem_prune(void) { struct stat sbuf; size_t sz; char *buf, *start, *end; int fd; char *fname = g_etcfile, *tmpname; if ((fd = open(fname, O_RDONLY)) == -1) fatal("failed to open %s", fname); if (fstat(fd, &sbuf) == -1) fatal("failed to fstat %s", fname); if ((buf = malloc((sz = sbuf.st_size) + 1)) == NULL) fatal("failed to allocate memory for %s", fname); if (read(fd, buf, sz) != sz) fatal("failed to read %s", fname); buf[sz] = '\0'; (void) close(fd); if ((start = strstr(buf, g_etcbegin)) == NULL) goto out; if (strlen(buf) != sz) { fatal("embedded nul byte in %s; manual repair of %s " "required\n", fname, fname); } if (strstr(start + 1, g_etcbegin) != NULL) { fatal("multiple start sentinels in %s; manual repair of %s " "required\n", fname, fname); } if ((end = strstr(buf, g_etcend)) == NULL) { fatal("missing end sentinel in %s; manual repair of %s " "required\n", fname, fname); } if (start > end) { fatal("end sentinel preceeds start sentinel in %s; manual " "repair of %s required\n", fname, fname); } end += strlen(g_etcend) + 1; bcopy(end, start, strlen(end) + 1); tmpname = alloca(sz = strlen(fname) + 80); (void) snprintf(tmpname, sz, "%s.dtrace.%d", fname, getpid()); if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, sbuf.st_mode)) == -1) fatal("failed to create %s", tmpname); if (write(fd, buf, strlen(buf)) < strlen(buf)) { (void) unlink(tmpname); fatal("failed to write to %s", tmpname); } (void) close(fd); if (chown(tmpname, sbuf.st_uid, sbuf.st_gid) != 0) { (void) unlink(tmpname); fatal("failed to chown(2) %s to uid %d, gid %d", tmpname, (int)sbuf.st_uid, (int)sbuf.st_gid); } if (rename(tmpname, fname) == -1) fatal("rename of %s to %s failed", tmpname, fname); error("cleaned up forceload directives in %s\n", fname); out: free(buf); } static void etcsystem_add(void) { const char *mods[20]; int nmods, line; if ((g_ofp = fopen(g_ofile = g_etcfile, "a")) == NULL) fatal("failed to open output file '%s'", g_ofile); oprintf("%s\n", g_etcbegin); for (line = 0; g_etc[line] != NULL; line++) oprintf("%s\n", g_etc[line]); nmods = dtrace_provider_modules(g_dtp, mods, sizeof (mods) / sizeof (char *) - 1); if (nmods >= sizeof (mods) / sizeof (char *)) fatal("unexpectedly large number of modules!"); mods[nmods++] = "dtrace"; for (line = 0; line < nmods; line++) oprintf("forceload: drv/%s\n", mods[line]); oprintf("%s\n", g_etcend); if (fclose(g_ofp) == EOF) fatal("failed to close output file '%s'", g_ofile); error("added forceload directives to %s\n", g_ofile); } #endif /* !__FreeBSD__ */ static void print_probe_info(const dtrace_probeinfo_t *p) { char buf[BUFSIZ]; char *user; int i; oprintf("\n\tProbe Description Attributes\n"); oprintf("\t\tIdentifier Names: %s\n", dtrace_stability_name(p->dtp_attr.dtat_name)); oprintf("\t\tData Semantics: %s\n", dtrace_stability_name(p->dtp_attr.dtat_data)); oprintf("\t\tDependency Class: %s\n", dtrace_class_name(p->dtp_attr.dtat_class)); oprintf("\n\tArgument Attributes\n"); oprintf("\t\tIdentifier Names: %s\n", dtrace_stability_name(p->dtp_arga.dtat_name)); oprintf("\t\tData Semantics: %s\n", dtrace_stability_name(p->dtp_arga.dtat_data)); oprintf("\t\tDependency Class: %s\n", dtrace_class_name(p->dtp_arga.dtat_class)); oprintf("\n\tArgument Types\n"); for (i = 0; i < p->dtp_argc; i++) { if (p->dtp_argv[i].dtt_flags & DTT_FL_USER) user = "userland "; else user = ""; if (ctf_type_name(p->dtp_argv[i].dtt_ctfp, p->dtp_argv[i].dtt_type, buf, sizeof (buf)) == NULL) (void) strlcpy(buf, "(unknown)", sizeof (buf)); oprintf("\t\targs[%d]: %s%s\n", i, user, buf); } if (p->dtp_argc == 0) oprintf("\t\tNone\n"); oprintf("\n"); } /*ARGSUSED*/ static int info_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last) { dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; dtrace_probedesc_t *pdp = &edp->dted_probe; dtrace_probeinfo_t p; if (edp == *last) return (0); oprintf("\n%s:%s:%s:%s\n", pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); if (dtrace_probe_info(dtp, pdp, &p) == 0) print_probe_info(&p); *last = edp; return (0); } /* * Execute the specified program by enabling the corresponding instrumentation. * If -e has been specified, we get the program info but do not enable it. If * -v has been specified, we print a stability report for the program. */ static void exec_prog(const dtrace_cmd_t *dcp) { dtrace_ecbdesc_t *last = NULL; dtrace_proginfo_t dpi; if (!g_exec) { dtrace_program_info(g_dtp, dcp->dc_prog, &dpi); } else if (dtrace_program_exec(g_dtp, dcp->dc_prog, &dpi) == -1) { dfatal("failed to enable '%s'", dcp->dc_name); } else { notice("%s '%s' matched %u probe%s\n", dcp->dc_desc, dcp->dc_name, dpi.dpi_matches, dpi.dpi_matches == 1 ? "" : "s"); } if (g_verbose) { oprintf("\nStability attributes for %s %s:\n", dcp->dc_desc, dcp->dc_name); oprintf("\n\tMinimum Probe Description Attributes\n"); oprintf("\t\tIdentifier Names: %s\n", dtrace_stability_name(dpi.dpi_descattr.dtat_name)); oprintf("\t\tData Semantics: %s\n", dtrace_stability_name(dpi.dpi_descattr.dtat_data)); oprintf("\t\tDependency Class: %s\n", dtrace_class_name(dpi.dpi_descattr.dtat_class)); oprintf("\n\tMinimum Statement Attributes\n"); oprintf("\t\tIdentifier Names: %s\n", dtrace_stability_name(dpi.dpi_stmtattr.dtat_name)); oprintf("\t\tData Semantics: %s\n", dtrace_stability_name(dpi.dpi_stmtattr.dtat_data)); oprintf("\t\tDependency Class: %s\n", dtrace_class_name(dpi.dpi_stmtattr.dtat_class)); if (!g_exec) { (void) dtrace_stmt_iter(g_dtp, dcp->dc_prog, (dtrace_stmt_f *)info_stmt, &last); } else oprintf("\n"); } g_total += dpi.dpi_matches; } /* * Print out the specified DOF buffer as a set of ASCII bytes appropriate for * storing in a driver.conf(4) file associated with the dtrace driver. */ static void anon_prog(const dtrace_cmd_t *dcp, dof_hdr_t *dof, int n) { const uchar_t *p, *q; if (dof == NULL) dfatal("failed to create DOF image for '%s'", dcp->dc_name); p = (uchar_t *)dof; q = p + dof->dofh_filesz; #ifdef __FreeBSD__ /* * On FreeBSD, the DOF file is read directly during boot - just write * two hex characters per byte. */ oprintf("dof-data-%d=", n); while (p < q) oprintf("%02x", *p++); oprintf("\n"); #else oprintf("dof-data-%d=0x%x", n, *p++); while (p < q) oprintf(",0x%x", *p++); oprintf(";\n"); #endif dtrace_dof_destroy(g_dtp, dof); } /* * Link the specified D program in DOF form into an ELF file for use in either * helpers, userland provider definitions, or both. If -o was specified, that * path is used as the output file name. If -o wasn't specified and the input * program is from a script whose name is %.d, use basename(%.o) as the output * file name. Otherwise we use "d.out" as the default output file name. */ static void link_prog(dtrace_cmd_t *dcp) { char *p; if (g_cmdc == 1 && g_ofile != NULL) { (void) strlcpy(dcp->dc_ofile, g_ofile, sizeof (dcp->dc_ofile)); } else if ((p = strrchr(dcp->dc_arg, '.')) != NULL && strcmp(p, ".d") == 0) { p[0] = '\0'; /* strip .d suffix */ (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), "%s.o", basename(dcp->dc_arg)); } else if (g_cmdc > 1) { (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), "d.out.%td", dcp - g_cmdv); } else { (void) snprintf(dcp->dc_ofile, sizeof (dcp->dc_ofile), "d.out"); } if (dtrace_program_link(g_dtp, dcp->dc_prog, DTRACE_D_PROBES, dcp->dc_ofile, g_objc, g_objv) != 0) dfatal("failed to link %s %s", dcp->dc_desc, dcp->dc_name); } /*ARGSUSED*/ static int list_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) { dtrace_probeinfo_t p; oprintf("%5d %10s %17s %33s %s\n", pdp->dtpd_id, pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); if (g_verbose && dtrace_probe_info(dtp, pdp, &p) == 0) print_probe_info(&p); if (g_intr != 0) return (1); return (0); } /*ARGSUSED*/ static int list_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *stp, dtrace_ecbdesc_t **last) { dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; if (edp == *last) return (0); if (dtrace_probe_iter(g_dtp, &edp->dted_probe, list_probe, NULL) != 0) { error("failed to match %s:%s:%s:%s: %s\n", edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod, edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); } *last = edp; return (0); } /* * List the probes corresponding to the specified program by iterating over * each statement and then matching probes to the statement probe descriptions. */ static void list_prog(const dtrace_cmd_t *dcp) { dtrace_ecbdesc_t *last = NULL; (void) dtrace_stmt_iter(g_dtp, dcp->dc_prog, (dtrace_stmt_f *)list_stmt, &last); } static void compile_file(dtrace_cmd_t *dcp) { char *arg0; FILE *fp; if ((fp = fopen(dcp->dc_arg, "r")) == NULL) fatal("failed to open %s", dcp->dc_arg); arg0 = g_argv[0]; g_argv[0] = dcp->dc_arg; if ((dcp->dc_prog = dtrace_program_fcompile(g_dtp, fp, g_cflags, g_argc, g_argv)) == NULL) dfatal("failed to compile script %s", dcp->dc_arg); g_argv[0] = arg0; (void) fclose(fp); dcp->dc_desc = "script"; dcp->dc_name = dcp->dc_arg; } static void compile_str(dtrace_cmd_t *dcp) { char *p; if ((dcp->dc_prog = dtrace_program_strcompile(g_dtp, dcp->dc_arg, dcp->dc_spec, g_cflags | DTRACE_C_PSPEC, g_argc, g_argv)) == NULL) dfatal("invalid probe specifier %s", dcp->dc_arg); if ((p = strpbrk(dcp->dc_arg, "{/;")) != NULL) *p = '\0'; /* crop name for reporting */ dcp->dc_desc = "description"; dcp->dc_name = dcp->dc_arg; } /*ARGSUSED*/ static void prochandler(struct ps_prochandle *P, const char *msg, void *arg) { #ifdef illumos const psinfo_t *prp = Ppsinfo(P); int pid = Pstatus(P)->pr_pid; char name[SIG2STR_MAX]; #else int wstatus = proc_getwstat(P); int pid = proc_getpid(P); #endif if (msg != NULL) { notice("pid %d: %s\n", pid, msg); return; } #ifdef illumos switch (Pstate(P)) { #else switch (proc_state(P)) { #endif case PS_UNDEAD: #ifdef illumos /* * Ideally we would like to always report pr_wstat here, but it * isn't possible given current /proc semantics. If we grabbed * the process, Ppsinfo() will either fail or return a zeroed * psinfo_t depending on how far the parent is in reaping it. * When /proc provides a stable pr_wstat in the status file, * this code can be improved by examining this new pr_wstat. */ if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) { notice("pid %d terminated by %s\n", pid, proc_signame(WTERMSIG(prp->pr_wstat), name, sizeof (name))); #else if (WIFSIGNALED(wstatus)) { notice("pid %d terminated by %d\n", pid, WTERMSIG(wstatus)); #endif #ifdef illumos } else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) { notice("pid %d exited with status %d\n", pid, WEXITSTATUS(prp->pr_wstat)); #else } else if (WEXITSTATUS(wstatus) != 0) { notice("pid %d exited with status %d\n", pid, WEXITSTATUS(wstatus)); #endif } else { notice("pid %d has exited\n", pid); } g_pslive--; break; case PS_LOST: notice("pid %d exec'd a set-id or unobservable program\n", pid); g_pslive--; break; } } /*ARGSUSED*/ static int errhandler(const dtrace_errdata_t *data, void *arg) { error(data->dteda_msg); return (DTRACE_HANDLE_OK); } /*ARGSUSED*/ static int drophandler(const dtrace_dropdata_t *data, void *arg) { - error(data->dtdda_msg); + if (!dtrace_oformat(g_dtp)) { + error(data->dtdda_msg); + } + return (DTRACE_HANDLE_OK); } /*ARGSUSED*/ static int setopthandler(const dtrace_setoptdata_t *data, void *arg) { if (strcmp(data->dtsda_option, "quiet") == 0) g_quiet = data->dtsda_newval != DTRACEOPT_UNSET; if (strcmp(data->dtsda_option, "flowindent") == 0) g_flowindent = data->dtsda_newval != DTRACEOPT_UNSET; return (DTRACE_HANDLE_OK); } #define BUFDUMPHDR(hdr) \ (void) printf("%s: %s%s\n", g_pname, hdr, strlen(hdr) > 0 ? ":" : ""); #define BUFDUMPSTR(ptr, field) \ (void) printf("%s: %20s => ", g_pname, #field); \ if ((ptr)->field != NULL) { \ const char *c = (ptr)->field; \ (void) printf("\""); \ do { \ if (*c == '\n') { \ (void) printf("\\n"); \ continue; \ } \ \ (void) printf("%c", *c); \ } while (*c++ != '\0'); \ (void) printf("\"\n"); \ } else { \ (void) printf("\n"); \ } #define BUFDUMPASSTR(ptr, field, str) \ (void) printf("%s: %20s => %s\n", g_pname, #field, str); #define BUFDUMP(ptr, field) \ (void) printf("%s: %20s => %lld\n", g_pname, #field, \ (long long)(ptr)->field); #define BUFDUMPPTR(ptr, field) \ (void) printf("%s: %20s => %s\n", g_pname, #field, \ (ptr)->field != NULL ? "" : ""); /*ARGSUSED*/ static int bufhandler(const dtrace_bufdata_t *bufdata, void *arg) { const dtrace_aggdata_t *agg = bufdata->dtbda_aggdata; const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc; const dtrace_probedesc_t *pd; uint32_t flags = bufdata->dtbda_flags; char buf[512], *c = buf, *end = c + sizeof (buf); int i, printed; struct { const char *name; uint32_t value; } flagnames[] = { { "AGGVAL", DTRACE_BUFDATA_AGGVAL }, { "AGGKEY", DTRACE_BUFDATA_AGGKEY }, { "AGGFORMAT", DTRACE_BUFDATA_AGGFORMAT }, { "AGGLAST", DTRACE_BUFDATA_AGGLAST }, { "???", UINT32_MAX }, { NULL } }; if (bufdata->dtbda_probe != NULL) { pd = bufdata->dtbda_probe->dtpda_pdesc; } else if (agg != NULL) { pd = agg->dtada_pdesc; } else { pd = NULL; } BUFDUMPHDR(">>> Called buffer handler"); BUFDUMPHDR(""); BUFDUMPHDR(" dtrace_bufdata"); BUFDUMPSTR(bufdata, dtbda_buffered); BUFDUMPPTR(bufdata, dtbda_probe); BUFDUMPPTR(bufdata, dtbda_aggdata); BUFDUMPPTR(bufdata, dtbda_recdesc); (void) snprintf(c, end - c, "0x%x ", bufdata->dtbda_flags); c += strlen(c); for (i = 0, printed = 0; flagnames[i].name != NULL; i++) { if (!(flags & flagnames[i].value)) continue; (void) snprintf(c, end - c, "%s%s", printed++ ? " | " : "(", flagnames[i].name); c += strlen(c); flags &= ~flagnames[i].value; } if (printed) (void) snprintf(c, end - c, ")"); BUFDUMPASSTR(bufdata, dtbda_flags, buf); BUFDUMPHDR(""); if (pd != NULL) { BUFDUMPHDR(" dtrace_probedesc"); BUFDUMPSTR(pd, dtpd_provider); BUFDUMPSTR(pd, dtpd_mod); BUFDUMPSTR(pd, dtpd_func); BUFDUMPSTR(pd, dtpd_name); BUFDUMPHDR(""); } if (rec != NULL) { BUFDUMPHDR(" dtrace_recdesc"); BUFDUMP(rec, dtrd_action); BUFDUMP(rec, dtrd_size); if (agg != NULL) { uint8_t *data; int lim = rec->dtrd_size; (void) sprintf(buf, "%d (data: ", rec->dtrd_offset); c = buf + strlen(buf); if (lim > sizeof (uint64_t)) lim = sizeof (uint64_t); data = (uint8_t *)agg->dtada_data + rec->dtrd_offset; for (i = 0; i < lim; i++) { (void) snprintf(c, end - c, "%s%02x", i == 0 ? "" : " ", *data++); c += strlen(c); } (void) snprintf(c, end - c, "%s)", lim < rec->dtrd_size ? " ..." : ""); BUFDUMPASSTR(rec, dtrd_offset, buf); } else { BUFDUMP(rec, dtrd_offset); } BUFDUMPHDR(""); } if (agg != NULL) { dtrace_aggdesc_t *desc = agg->dtada_desc; BUFDUMPHDR(" dtrace_aggdesc"); BUFDUMPSTR(desc, dtagd_name); BUFDUMP(desc, dtagd_varid); BUFDUMP(desc, dtagd_id); BUFDUMP(desc, dtagd_nrecs); BUFDUMPHDR(""); } return (DTRACE_HANDLE_OK); } /*ARGSUSED*/ static int chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) { dtrace_actkind_t act; uintptr_t addr; if (rec == NULL) { /* * We have processed the final record; output the newline if * we're not in quiet mode. */ if (!g_quiet) oprintf("\n"); return (DTRACE_CONSUME_NEXT); } act = rec->dtrd_action; addr = (uintptr_t)data->dtpda_data; if (act == DTRACEACT_EXIT) { g_status = *((uint32_t *)addr); return (DTRACE_CONSUME_NEXT); } return (DTRACE_CONSUME_THIS); } /*ARGSUSED*/ static int chew(const dtrace_probedata_t *data, void *arg) { dtrace_probedesc_t *pd = data->dtpda_pdesc; processorid_t cpu = data->dtpda_cpu; static int heading; if (g_impatient) { g_newline = 0; return (DTRACE_CONSUME_ABORT); } if (heading == 0) { if (!g_flowindent) { if (!g_quiet) { oprintf("%3s %6s %32s\n", "CPU", "ID", "FUNCTION:NAME"); } } else { oprintf("%3s %-41s\n", "CPU", "FUNCTION"); } heading = 1; } if (!g_flowindent) { - if (!g_quiet) { + if (dtrace_oformat(g_dtp)) { + dtrace_oformat_probe(g_dtp, data, cpu, pd); + } else if (!g_quiet) { char name[DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 2]; (void) snprintf(name, sizeof (name), "%s:%s", pd->dtpd_func, pd->dtpd_name); oprintf("%3d %6d %32s ", cpu, pd->dtpd_id, name); } } else { int indent = data->dtpda_indent; char *name; size_t len; if (data->dtpda_flow == DTRACEFLOW_NONE) { len = indent + DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 5; name = alloca(len); (void) snprintf(name, len, "%*s%s%s:%s", indent, "", data->dtpda_prefix, pd->dtpd_func, pd->dtpd_name); } else { len = indent + DTRACE_FUNCNAMELEN + 5; name = alloca(len); (void) snprintf(name, len, "%*s%s%s", indent, "", data->dtpda_prefix, pd->dtpd_func); } oprintf("%3d %-41s ", cpu, name); } return (DTRACE_CONSUME_THIS); } static void go(void) { int i; struct { char *name; char *optname; dtrace_optval_t val; } bufs[] = { { "buffer size", "bufsize" }, { "aggregation size", "aggsize" }, { "speculation size", "specsize" }, { "dynamic variable size", "dynvarsize" }, { NULL } }, rates[] = { { "cleaning rate", "cleanrate" }, { "status rate", "statusrate" }, { NULL } }; for (i = 0; bufs[i].name != NULL; i++) { if (dtrace_getopt(g_dtp, bufs[i].optname, &bufs[i].val) == -1) fatal("couldn't get option %s", bufs[i].optname); } for (i = 0; rates[i].name != NULL; i++) { if (dtrace_getopt(g_dtp, rates[i].optname, &rates[i].val) == -1) fatal("couldn't get option %s", rates[i].optname); } if (dtrace_go(g_dtp) == -1) dfatal("could not enable tracing"); for (i = 0; bufs[i].name != NULL; i++) { dtrace_optval_t j = 0, mul = 10; dtrace_optval_t nsize; if (bufs[i].val == DTRACEOPT_UNSET) continue; (void) dtrace_getopt(g_dtp, bufs[i].optname, &nsize); if (nsize == DTRACEOPT_UNSET || nsize == 0) continue; if (nsize >= bufs[i].val - sizeof (uint64_t)) continue; for (; (INT64_C(1) << mul) <= nsize; j++, mul += 10) continue; if (!(nsize & ((INT64_C(1) << (mul - 10)) - 1))) { error("%s lowered to %lld%c\n", bufs[i].name, (long long)nsize >> (mul - 10), " kmgtpe"[j]); } else { error("%s lowered to %lld bytes\n", bufs[i].name, (long long)nsize); } } for (i = 0; rates[i].name != NULL; i++) { dtrace_optval_t nval; char *dir; if (rates[i].val == DTRACEOPT_UNSET) continue; (void) dtrace_getopt(g_dtp, rates[i].optname, &nval); if (nval == DTRACEOPT_UNSET || nval == 0) continue; if (rates[i].val == nval) continue; dir = nval > rates[i].val ? "reduced" : "increased"; if (nval <= NANOSEC && (NANOSEC % nval) == 0) { error("%s %s to %lld hz\n", rates[i].name, dir, (long long)NANOSEC / (long long)nval); continue; } if ((nval % NANOSEC) == 0) { error("%s %s to once every %lld seconds\n", rates[i].name, dir, (long long)nval / (long long)NANOSEC); continue; } error("%s %s to once every %lld nanoseconds\n", rates[i].name, dir, (long long)nval); } } /*ARGSUSED*/ static void intr(int signo) { if (!g_intr) g_newline = 1; if (g_intr++) g_impatient = 1; } #ifdef __FreeBSD__ static void siginfo(int signo __unused) { g_siginfo++; g_newline = 1; } #endif static void installsighands(void) { struct sigaction act, oact; (void) sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = intr; if (sigaction(SIGINT, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGINT, &act, NULL); if (sigaction(SIGTERM, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGTERM, &act, NULL); #ifdef __FreeBSD__ if (sigaction(SIGPIPE, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGPIPE, &act, NULL); if (sigaction(SIGUSR1, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGUSR1, &act, NULL); act.sa_handler = siginfo; if (sigaction(SIGINFO, NULL, &oact) == 0 && oact.sa_handler != SIG_IGN) (void) sigaction(SIGINFO, &act, NULL); #endif } int main(int argc, char *argv[]) { dtrace_bufdesc_t buf; dtrace_status_t status[2]; dtrace_optval_t opt; dtrace_cmd_t *dcp; g_ofp = stdout; int done = 0, mode = 0; - int err, i, c; + int err, i, c, new_argc, libxo_specified; + int print_upon_exit = 0; char *p, **v; struct ps_prochandle *P; pid_t pid; #ifdef __FreeBSD__ /* For %'d and the like. */ (void) setlocale(LC_NUMERIC, ""); /* For %T. */ (void) setlocale(LC_TIME, ""); #endif g_pname = basename(argv[0]); if (argc == 1) return (usage(stderr)); if ((g_argv = malloc(sizeof (char *) * argc)) == NULL || (g_cmdv = malloc(sizeof (dtrace_cmd_t) * argc)) == NULL || (g_psv = malloc(sizeof (struct ps_prochandle *) * argc)) == NULL) fatal("failed to allocate memory for arguments"); + new_argc = xo_parse_args(argc, argv); + if (new_argc < 0) + return (usage(stderr)); + + if (new_argc != argc) + libxo_specified = 1; + + argc = new_argc; + g_argv[g_argc++] = argv[0]; /* propagate argv[0] to D as $0/$$0 */ argv[0] = g_pname; /* rewrite argv[0] for getopt errors */ bzero(status, sizeof (status)); bzero(&buf, sizeof (buf)); /* * Make an initial pass through argv[] processing any arguments that * affect our behavior mode (g_mode) and flags used for dtrace_open(). * We also accumulate arguments that are not affiliated with getopt * options into g_argv[], and abort if any invalid options are found. */ for (optind = 1; optind < argc; optind++) { while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { switch (c) { case '3': if (strcmp(optarg, "2") != 0) { (void) fprintf(stderr, "%s: illegal option -- 3%s\n", argv[0], optarg); return (usage(stderr)); } g_oflags &= ~DTRACE_O_LP64; g_oflags |= DTRACE_O_ILP32; break; case '6': if (strcmp(optarg, "4") != 0) { (void) fprintf(stderr, "%s: illegal option -- 6%s\n", argv[0], optarg); return (usage(stderr)); } g_oflags &= ~DTRACE_O_ILP32; g_oflags |= DTRACE_O_LP64; break; case 'a': g_grabanon++; /* also checked in pass 2 below */ break; case 'A': g_mode = DMODE_ANON; g_exec = 0; mode++; break; case 'e': g_exec = 0; done = 1; break; case 'h': g_mode = DMODE_HEADER; g_oflags |= DTRACE_O_NODEV; g_cflags |= DTRACE_C_ZDEFS; /* -h implies -Z */ g_exec = 0; mode++; break; case 'G': g_mode = DMODE_LINK; g_oflags |= DTRACE_O_NODEV; g_cflags |= DTRACE_C_ZDEFS; /* -G implies -Z */ g_exec = 0; mode++; break; case 'l': g_mode = DMODE_LIST; g_cflags |= DTRACE_C_ZDEFS; /* -l implies -Z */ mode++; break; case 'V': g_mode = DMODE_VERS; mode++; break; default: if (strchr(DTRACE_OPTSTR, c) == NULL) return (usage(stderr)); } } if (optind < argc) g_argv[g_argc++] = argv[optind]; } if (mode > 1) { (void) fprintf(stderr, "%s: only one of the [-AGhlV] options " "can be specified at a time\n", g_pname); return (E_USAGE); } if (g_mode == DMODE_VERS) return (printf("%s: %s\n", g_pname, _dtrace_version) <= 0); /* * If we're in linker mode and the data model hasn't been specified, * we try to guess the appropriate setting by examining the object * files. We ignore certain errors since we'll catch them later when * we actually process the object files. */ if (g_mode == DMODE_LINK && (g_oflags & (DTRACE_O_ILP32 | DTRACE_O_LP64)) == 0 && elf_version(EV_CURRENT) != EV_NONE) { int fd; Elf *elf; GElf_Ehdr ehdr; for (i = 1; i < g_argc; i++) { if ((fd = open64(g_argv[i], O_RDONLY)) == -1) break; if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { (void) close(fd); break; } if (elf_kind(elf) != ELF_K_ELF || gelf_getehdr(elf, &ehdr) == NULL) { (void) close(fd); (void) elf_end(elf); break; } (void) close(fd); (void) elf_end(elf); if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { if (g_oflags & DTRACE_O_ILP32) { fatal("can't mix 32-bit and 64-bit " "object files\n"); } g_oflags |= DTRACE_O_LP64; } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) { if (g_oflags & DTRACE_O_LP64) { fatal("can't mix 32-bit and 64-bit " "object files\n"); } g_oflags |= DTRACE_O_ILP32; } else { break; } } } /* * Open libdtrace. If we are not actually going to be enabling any * instrumentation attempt to reopen libdtrace using DTRACE_O_NODEV. */ while ((g_dtp = dtrace_open(DTRACE_VERSION, g_oflags, &err)) == NULL) { if (!(g_oflags & DTRACE_O_NODEV) && !g_exec && !g_grabanon) { g_oflags |= DTRACE_O_NODEV; continue; } fatal("failed to initialize dtrace: %s\n", dtrace_errmsg(NULL, err)); } #if defined(__i386__) /* XXX The 32-bit seems to need more buffer space by default -sson */ (void) dtrace_setopt(g_dtp, "bufsize", "12m"); (void) dtrace_setopt(g_dtp, "aggsize", "12m"); #else (void) dtrace_setopt(g_dtp, "bufsize", "4m"); (void) dtrace_setopt(g_dtp, "aggsize", "4m"); #endif (void) dtrace_setopt(g_dtp, "temporal", "yes"); /* * If -G is specified, enable -xlink=dynamic and -xunodefs to permit * references to undefined symbols to remain as unresolved relocations. * If -A is specified, enable -xlink=primary to permit static linking * only to kernel symbols that are defined in a primary kernel module. */ if (g_mode == DMODE_LINK) { (void) dtrace_setopt(g_dtp, "linkmode", "dynamic"); (void) dtrace_setopt(g_dtp, "unodefs", NULL); /* * Use the remaining arguments as the list of object files * when in linker mode. */ g_objc = g_argc - 1; g_objv = g_argv + 1; /* * We still use g_argv[0], the name of the executable. */ g_argc = 1; } else if (g_mode == DMODE_ANON) (void) dtrace_setopt(g_dtp, "linkmode", "primary"); + + if (libxo_specified) + dtrace_oformat_configure(g_dtp); + /* * Now that we have libdtrace open, make a second pass through argv[] * to perform any dtrace_setopt() calls and change any compiler flags. * We also accumulate any program specifications into our g_cmdv[] at * this time; these will compiled as part of the fourth processing pass. */ for (optind = 1; optind < argc; optind++) { while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { switch (c) { case 'a': if (dtrace_setopt(g_dtp, "grabanon", 0) != 0) dfatal("failed to set -a"); break; case 'b': if (dtrace_setopt(g_dtp, "bufsize", optarg) != 0) dfatal("failed to set -b %s", optarg); break; case 'B': g_ofp = NULL; break; case 'C': g_cflags |= DTRACE_C_CPP; break; case 'd': g_cflags |= DTRACE_C_SUGAR; break; case 'D': if (dtrace_setopt(g_dtp, "define", optarg) != 0) dfatal("failed to set -D %s", optarg); break; case 'f': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_FUNC; dcp->dc_arg = optarg; break; case 'F': if (dtrace_setopt(g_dtp, "flowindent", 0) != 0) dfatal("failed to set -F"); break; case 'H': if (dtrace_setopt(g_dtp, "cpphdrs", 0) != 0) dfatal("failed to set -H"); break; case 'i': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_NAME; dcp->dc_arg = optarg; break; case 'I': if (dtrace_setopt(g_dtp, "incdir", optarg) != 0) dfatal("failed to set -I %s", optarg); break; case 'L': if (dtrace_setopt(g_dtp, "libdir", optarg) != 0) dfatal("failed to set -L %s", optarg); break; case 'm': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_MOD; dcp->dc_arg = optarg; break; case 'n': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_NAME; dcp->dc_arg = optarg; break; case 'P': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_str; dcp->dc_spec = DTRACE_PROBESPEC_PROVIDER; dcp->dc_arg = optarg; break; + case 'O': + print_upon_exit = 1; + break; + case 'q': if (dtrace_setopt(g_dtp, "quiet", 0) != 0) dfatal("failed to set -q"); break; case 'o': g_ofile = optarg; break; case 's': dcp = &g_cmdv[g_cmdc++]; dcp->dc_func = compile_file; dcp->dc_spec = DTRACE_PROBESPEC_NONE; dcp->dc_arg = optarg; break; case 'S': g_cflags |= DTRACE_C_DIFV; break; case 'U': if (dtrace_setopt(g_dtp, "undef", optarg) != 0) dfatal("failed to set -U %s", optarg); break; case 'v': g_verbose++; break; case 'w': if (dtrace_setopt(g_dtp, "destructive", 0) != 0) dfatal("failed to set -w"); break; case 'x': if ((p = strchr(optarg, '=')) != NULL) *p++ = '\0'; if (dtrace_setopt(g_dtp, optarg, p) != 0) dfatal("failed to set -x %s", optarg); break; case 'X': if (dtrace_setopt(g_dtp, "stdc", optarg) != 0) dfatal("failed to set -X %s", optarg); break; case 'Z': g_cflags |= DTRACE_C_ZDEFS; break; default: if (strchr(DTRACE_OPTSTR, c) == NULL) return (usage(stderr)); } } } if (g_ofp == NULL && g_mode != DMODE_EXEC) { (void) fprintf(stderr, "%s: -B not valid in combination" " with [-AGl] options\n", g_pname); return (E_USAGE); } if (g_ofp == NULL && g_ofile != NULL) { (void) fprintf(stderr, "%s: -B not valid in combination" " with -o option\n", g_pname); return (E_USAGE); } /* * In our third pass we handle any command-line options related to * grabbing or creating victim processes. The behavior of these calls * may been affected by any library options set by the second pass. */ for (optind = 1; optind < argc; optind++) { while ((c = getopt(argc, argv, DTRACE_OPTSTR)) != -1) { switch (c) { case 'c': if ((v = make_argv(optarg)) == NULL) fatal("failed to allocate memory"); P = dtrace_proc_create(g_dtp, v[0], v, NULL, NULL); if (P == NULL) dfatal(NULL); /* dtrace_errmsg() only */ g_psv[g_psc++] = P; free(v); break; case 'p': errno = 0; pid = strtol(optarg, &p, 10); if (errno != 0 || p == optarg || p[0] != '\0') fatal("invalid pid: %s\n", optarg); P = dtrace_proc_grab(g_dtp, pid, 0); if (P == NULL) dfatal(NULL); /* dtrace_errmsg() only */ g_psv[g_psc++] = P; break; } } } /* * In our fourth pass we finish g_cmdv[] by calling dc_func to convert * each string or file specification into a compiled program structure. */ for (i = 0; i < g_cmdc; i++) g_cmdv[i].dc_func(&g_cmdv[i]); if (g_mode != DMODE_LIST) { if (dtrace_handle_err(g_dtp, &errhandler, NULL) == -1) dfatal("failed to establish error handler"); if (dtrace_handle_drop(g_dtp, &drophandler, NULL) == -1) dfatal("failed to establish drop handler"); if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1) dfatal("failed to establish proc handler"); if (dtrace_handle_setopt(g_dtp, &setopthandler, NULL) == -1) dfatal("failed to establish setopt handler"); if (g_ofp == NULL && dtrace_handle_buffered(g_dtp, &bufhandler, NULL) == -1) dfatal("failed to establish buffered handler"); } (void) dtrace_getopt(g_dtp, "flowindent", &opt); g_flowindent = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "grabanon", &opt); g_grabanon = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "quiet", &opt); g_quiet = opt != DTRACEOPT_UNSET; + if (dtrace_oformat(g_dtp)) { + if (dtrace_setopt(g_dtp, "quiet", 0) != 0) + dfatal("failed to set quiet (caused by oformat)"); + } + /* * Now make a fifth and final pass over the options that have been * turned into programs and saved in g_cmdv[], performing any mode- * specific processing. If g_mode is DMODE_EXEC, we will break out * of the switch() and continue on to the data processing loop. For * other modes, we will exit dtrace once mode-specific work is done. */ switch (g_mode) { case DMODE_EXEC: if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) fatal("failed to open output file '%s'", g_ofile); + if (dtrace_oformat(g_dtp)) + dtrace_set_outfp(g_ofp); + for (i = 0; i < g_cmdc; i++) exec_prog(&g_cmdv[i]); if (done && !g_grabanon) { dtrace_close(g_dtp); return (g_status); } break; case DMODE_ANON: if (g_ofile == NULL) #ifdef illumos g_ofile = "/kernel/drv/dtrace.conf"; #else /* * On FreeBSD, anonymous DOF data is written to * the DTrace DOF file. */ g_ofile = "/boot/dtrace.dof"; #endif dof_prune(g_ofile); /* strip out any old DOF directives */ #ifdef illumos etcsystem_prune(); /* string out any forceload directives */ #endif if (g_cmdc == 0) { dtrace_close(g_dtp); return (g_status); } if ((g_ofp = fopen(g_ofile, "a")) == NULL) fatal("failed to open output file '%s'", g_ofile); + if (dtrace_oformat(g_dtp)) + dtrace_set_outfp(g_ofp); + for (i = 0; i < g_cmdc; i++) { anon_prog(&g_cmdv[i], dtrace_dof_create(g_dtp, g_cmdv[i].dc_prog, 0), i); } /* * Dump out the DOF corresponding to the error handler and the * current options as the final DOF property in the .conf file. */ anon_prog(NULL, dtrace_geterr_dof(g_dtp), i++); anon_prog(NULL, dtrace_getopt_dof(g_dtp), i++); if (fclose(g_ofp) == EOF) fatal("failed to close output file '%s'", g_ofile); /* * These messages would use notice() rather than error(), but * we don't want them suppressed when -A is run on a D program * that itself contains a #pragma D option quiet. */ error("saved anonymous enabling in %s\n", g_ofile); #ifdef __FreeBSD__ bootdof_add(); #else etcsystem_add(); error("run update_drv(1M) or reboot to enable changes\n"); #endif dtrace_close(g_dtp); return (g_status); case DMODE_LINK: if (g_cmdc == 0) { (void) fprintf(stderr, "%s: -G requires one or more " "scripts or enabling options\n", g_pname); dtrace_close(g_dtp); return (E_USAGE); } for (i = 0; i < g_cmdc; i++) link_prog(&g_cmdv[i]); if (g_cmdc > 1 && g_ofile != NULL) { char **objv = alloca(g_cmdc * sizeof (char *)); for (i = 0; i < g_cmdc; i++) objv[i] = g_cmdv[i].dc_ofile; if (dtrace_program_link(g_dtp, NULL, DTRACE_D_PROBES, g_ofile, g_cmdc, objv) != 0) dfatal(NULL); /* dtrace_errmsg() only */ } dtrace_close(g_dtp); return (g_status); case DMODE_LIST: if (g_ofile != NULL && (g_ofp = fopen(g_ofile, "a")) == NULL) fatal("failed to open output file '%s'", g_ofile); installsighands(); oprintf("%5s %10s %17s %33s %s\n", "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME"); for (i = 0; i < g_cmdc; i++) list_prog(&g_cmdv[i]); if (g_cmdc == 0) (void) dtrace_probe_iter(g_dtp, NULL, list_probe, NULL); dtrace_close(g_dtp); return (g_status); case DMODE_HEADER: if (g_cmdc == 0) { (void) fprintf(stderr, "%s: -h requires one or more " "scripts or enabling options\n", g_pname); dtrace_close(g_dtp); return (E_USAGE); } if (g_ofile == NULL) { char *p; if (g_cmdc > 1) { (void) fprintf(stderr, "%s: -h requires an " "output file if multiple scripts are " "specified\n", g_pname); dtrace_close(g_dtp); return (E_USAGE); } if ((p = strrchr(g_cmdv[0].dc_arg, '.')) == NULL || strcmp(p, ".d") != 0) { (void) fprintf(stderr, "%s: -h requires an " "output file if no scripts are " "specified\n", g_pname); dtrace_close(g_dtp); return (E_USAGE); } p[0] = '\0'; /* strip .d suffix */ g_ofile = p = g_cmdv[0].dc_ofile; (void) snprintf(p, sizeof (g_cmdv[0].dc_ofile), "%s.h", basename(g_cmdv[0].dc_arg)); } if ((g_ofp = fopen(g_ofile, "w")) == NULL) fatal("failed to open header file '%s'", g_ofile); oprintf("/*\n * Generated by dtrace(1M).\n */\n\n"); if (dtrace_program_header(g_dtp, g_ofp, g_ofile) != 0 || fclose(g_ofp) == EOF) dfatal("failed to create header file %s", g_ofile); dtrace_close(g_dtp); return (g_status); } /* * If -a and -Z were not specified and no probes have been matched, no * probe criteria was specified on the command line and we abort. */ if (g_total == 0 && !g_grabanon && !(g_cflags & DTRACE_C_ZDEFS)) dfatal("no probes %s\n", g_cmdc ? "matched" : "specified"); /* * Start tracing. Once we dtrace_go(), reload any options that affect * our globals in case consuming anonymous state has changed them. */ go(); (void) dtrace_getopt(g_dtp, "flowindent", &opt); g_flowindent = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "grabanon", &opt); g_grabanon = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "quiet", &opt); g_quiet = opt != DTRACEOPT_UNSET; (void) dtrace_getopt(g_dtp, "destructive", &opt); if (opt != DTRACEOPT_UNSET) notice("allowing destructive actions\n"); installsighands(); /* * Now that tracing is active and we are ready to consume trace data, * continue any grabbed or created processes, setting them running * using the /proc control mechanism inside of libdtrace. */ for (i = 0; i < g_psc; i++) dtrace_proc_continue(g_dtp, g_psv[i]); g_pslive = g_psc; /* count for prochandler() */ + dtrace_oformat_setup(g_dtp); do { if (!g_intr && !done) dtrace_sleep(g_dtp); #ifdef __FreeBSD__ - if (g_siginfo) { + /* + * XXX: Supporting SIGINFO with oformat makes little sense, as + * it can't really produce sensible DTrace output. + * + * If needed, we could support it by having an imaginary + * "SIGINFO" probe that we can construct in the output but leave + * it out for now. + */ + if (g_siginfo && !dtrace_oformat(g_dtp)) { (void)dtrace_aggregate_print(g_dtp, g_ofp, NULL); g_siginfo = 0; } #endif if (g_newline) { /* * Output a newline just to make the output look * slightly cleaner. Note that we do this even in * "quiet" mode... */ oprintf("\n"); g_newline = 0; } if (done || g_intr || (g_psc != 0 && g_pslive == 0)) { done = 1; if (dtrace_stop(g_dtp) == -1) dfatal("couldn't stop tracing"); } switch (dtrace_work(g_dtp, g_ofp, chew, chewrec, NULL)) { case DTRACE_WORKSTATUS_DONE: done = 1; break; case DTRACE_WORKSTATUS_OKAY: break; default: if (!g_impatient && dtrace_errno(g_dtp) != EINTR) dfatal("processing aborted"); } if (g_ofp != NULL && fflush(g_ofp) == EOF) clearerr(g_ofp); } while (!done); - oprintf("\n"); + if (!dtrace_oformat(g_dtp)) + oprintf("\n"); - if (!g_impatient) { + /* + * Since there is no way to format a probe here and machine-readable + * output makes little sense without explicitly asking for it, we print + * nothing upon Ctrl-C if oformat is specified. If the user wishes to + * get output upon exit, they must write an explicit dtrace:::END probe + * to do so. + */ + if ((!g_impatient && !dtrace_oformat(g_dtp)) || + (!g_impatient && print_upon_exit)) { if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 && dtrace_errno(g_dtp) != EINTR) dfatal("failed to print aggregations"); } + dtrace_oformat_teardown(g_dtp); dtrace_close(g_dtp); return (g_status); } diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.avg.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.avg.ksh new file mode 100644 index 000000000000..daf5c7f4e9bb --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.avg.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[probefunc] = avg(tid); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.count.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.count.ksh new file mode 100644 index 000000000000..ab7c5fa28471 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.count.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[execname] = count(); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.llquantize.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.llquantize.ksh new file mode 100644 index 000000000000..380335de000a --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.llquantize.ksh @@ -0,0 +1,73 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + self->ts = timestamp; +} + +syscall:::return +/self->ts/ +{ + @[probefunc] = llquantize(timestamp - self->ts, 2, 1, 32, 32); + self->ts = 0; +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.lquantize.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.lquantize.ksh new file mode 100644 index 000000000000..5ec6c725a7f0 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.lquantize.ksh @@ -0,0 +1,73 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + self->ts = timestamp; +} + +syscall:::return +/self->ts/ +{ + @[probefunc] = lquantize(timestamp - self->ts, 0, 1000000, 100); + self->ts = 0; +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.max.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.max.ksh new file mode 100644 index 000000000000..45f790aae030 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.max.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[probefunc] = max(tid); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.min.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.min.ksh new file mode 100644 index 000000000000..6f40acdfff40 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.min.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[probefunc] = min(tid); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.quantize.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.quantize.ksh new file mode 100644 index 000000000000..c0ba0f4a6c0f --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.quantize.ksh @@ -0,0 +1,73 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + self->ts = timestamp; +} + +syscall:::return +/self->ts/ +{ + @[probefunc] = quantize(timestamp - self->ts); + self->ts = 0; +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.stddev.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.stddev.ksh new file mode 100644 index 000000000000..7b06a67b9a8a --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.stddev.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[probefunc] = stddev(tid); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.sum.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.sum.ksh new file mode 100644 index 000000000000..f7c323c6baac --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.agg.sum.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[probefunc] = sum(1); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggmod.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggmod.ksh new file mode 100644 index 000000000000..335819cbcfc9 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggmod.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[mod((uintptr_t)rand())] = count(); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggstack.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggstack.ksh new file mode 100644 index 000000000000..30af498c2498 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggstack.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +fbt:::entry +{ + @[stack()] = count(); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggsym.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggsym.ksh new file mode 100644 index 000000000000..d0da3e5086ed --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggsym.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[sym((uintptr_t)rand())] = count(); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggustack.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggustack.ksh new file mode 100644 index 000000000000..98cf2a0562a4 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggustack.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[ustack()] = count(); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggusym.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggusym.ksh new file mode 100644 index 000000000000..7945b83250c3 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.aggusym.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + @[usym((uintptr_t)rand())] = count(); +} + +tick-5s +{ + exit(0); +} + +END +{ + printa(@); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.drop.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.drop.ksh new file mode 100644 index 000000000000..7f17c321ff74 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.drop.ksh @@ -0,0 +1,61 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +fbt:::entry +{ + x[timestamp] = 1; +} + +tick-5s +{ + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.mod.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.mod.ksh new file mode 100644 index 000000000000..b0e6ce3cc380 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.mod.ksh @@ -0,0 +1,57 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + mod(0); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.print.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.print.ksh new file mode 100644 index 000000000000..68d675f20329 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.print.ksh @@ -0,0 +1,60 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + print(*curthread); + print(*curthread->td_proc); + print(*curthread->td_ucred); + print(*curthread->td_ucred->cr_prison); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printf.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printf.ksh new file mode 100644 index 000000000000..a25352c4a1ca --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printf.ksh @@ -0,0 +1,57 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + printf("Hello World!"); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printm.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printm.ksh new file mode 100644 index 000000000000..634606fa574e --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.printm.ksh @@ -0,0 +1,57 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + printm(100, memref(curthread, 10)); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.stack.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.stack.ksh new file mode 100644 index 000000000000..33d1ee6dc1d7 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.stack.ksh @@ -0,0 +1,61 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + stack(); +} + +tick-1s +{ + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.sym.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.sym.ksh new file mode 100644 index 000000000000..5e9bb3fea58e --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.sym.ksh @@ -0,0 +1,57 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + sym(0); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.trace.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.trace.ksh new file mode 100644 index 000000000000..42f714a1a500 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.trace.ksh @@ -0,0 +1,66 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + trace(0); + trace(1); + trace(2); + trace(3); + trace(4); + trace(5); + trace(6); + trace(7); + trace(8); + trace(9); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.tracemem.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.tracemem.ksh new file mode 100644 index 000000000000..dd9dbff3c191 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.tracemem.ksh @@ -0,0 +1,57 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + tracemem(curthread, 10); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.umod.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.umod.ksh new file mode 100644 index 000000000000..0d50e34dfff1 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.umod.ksh @@ -0,0 +1,57 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + umod(0); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.ustack.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.ustack.ksh new file mode 100644 index 000000000000..1b1cc0a247d3 --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.ustack.ksh @@ -0,0 +1,61 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +syscall:::entry +{ + ustack(); +} + +tick-1s +{ + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.usym.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.usym.ksh new file mode 100644 index 000000000000..36372d05aa2f --- /dev/null +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/oformat/tst.usym.ksh @@ -0,0 +1,57 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2023 Domagoj Stolfa +# + +bname=`basename $0` +dtraceout=/tmp/dtrace.$bname + +script() +{ + $dtrace -o $dtraceout.$1 -x oformat=$1 -s /dev/stdin <<__EOF__ +BEGIN +{ + usym(0); + exit(0); +} +__EOF__ +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +script json +jq . $dtraceout.json + +if [ $? != 0 ]; then + echo $bname: failed to produce valid JSON. see $dtraceout.json + exit 1 +fi + +script xml +xmllint $dtraceout.xml + +if [ $? != 0 ]; then + echo $bname: failed to produce valid XML. see $dtraceout.xml + exit 1 +fi + +rm $dtraceout.json +rm $dtraceout.xml + +exit 0 diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c index 5ea55a05faf0..643e7fae8ace 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_aggregate.c @@ -1,2198 +1,2209 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include +#include #ifdef illumos #include #else #include #include #endif #include #define DTRACE_AHASHSIZE 32779 /* big 'ol prime */ /* * Because qsort(3C) does not allow an argument to be passed to a comparison * function, the variables that affect comparison must regrettably be global; * they are protected by a global static lock, dt_qsort_lock. */ static pthread_mutex_t dt_qsort_lock = PTHREAD_MUTEX_INITIALIZER; static int dt_revsort; static int dt_keysort; static int dt_keypos; #define DT_LESSTHAN (dt_revsort == 0 ? -1 : 1) #define DT_GREATERTHAN (dt_revsort == 0 ? 1 : -1) static void dt_aggregate_count(int64_t *existing, int64_t *new, size_t size) { uint_t i; for (i = 0; i < size / sizeof (int64_t); i++) existing[i] = existing[i] + new[i]; } static int dt_aggregate_countcmp(int64_t *lhs, int64_t *rhs) { int64_t lvar = *lhs; int64_t rvar = *rhs; if (lvar < rvar) return (DT_LESSTHAN); if (lvar > rvar) return (DT_GREATERTHAN); return (0); } /*ARGSUSED*/ static void dt_aggregate_min(int64_t *existing, int64_t *new, size_t size) { if (*new < *existing) *existing = *new; } /*ARGSUSED*/ static void dt_aggregate_max(int64_t *existing, int64_t *new, size_t size) { if (*new > *existing) *existing = *new; } static int dt_aggregate_averagecmp(int64_t *lhs, int64_t *rhs) { int64_t lavg = lhs[0] ? (lhs[1] / lhs[0]) : 0; int64_t ravg = rhs[0] ? (rhs[1] / rhs[0]) : 0; if (lavg < ravg) return (DT_LESSTHAN); if (lavg > ravg) return (DT_GREATERTHAN); return (0); } static int dt_aggregate_stddevcmp(int64_t *lhs, int64_t *rhs) { uint64_t lsd = dt_stddev((uint64_t *)lhs, 1); uint64_t rsd = dt_stddev((uint64_t *)rhs, 1); if (lsd < rsd) return (DT_LESSTHAN); if (lsd > rsd) return (DT_GREATERTHAN); return (0); } /*ARGSUSED*/ static void dt_aggregate_lquantize(int64_t *existing, int64_t *new, size_t size) { int64_t arg = *existing++; uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg); int i; for (i = 0; i <= levels + 1; i++) existing[i] = existing[i] + new[i + 1]; } static long double dt_aggregate_lquantizedsum(int64_t *lquanta) { int64_t arg = *lquanta++; int32_t base = DTRACE_LQUANTIZE_BASE(arg); uint16_t step = DTRACE_LQUANTIZE_STEP(arg); uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg), i; long double total = (long double)lquanta[0] * (long double)(base - 1); for (i = 0; i < levels; base += step, i++) total += (long double)lquanta[i + 1] * (long double)base; return (total + (long double)lquanta[levels + 1] * (long double)(base + 1)); } static int64_t dt_aggregate_lquantizedzero(int64_t *lquanta) { int64_t arg = *lquanta++; int32_t base = DTRACE_LQUANTIZE_BASE(arg); uint16_t step = DTRACE_LQUANTIZE_STEP(arg); uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg), i; if (base - 1 == 0) return (lquanta[0]); for (i = 0; i < levels; base += step, i++) { if (base != 0) continue; return (lquanta[i + 1]); } if (base + 1 == 0) return (lquanta[levels + 1]); return (0); } static int dt_aggregate_lquantizedcmp(int64_t *lhs, int64_t *rhs) { long double lsum = dt_aggregate_lquantizedsum(lhs); long double rsum = dt_aggregate_lquantizedsum(rhs); int64_t lzero, rzero; if (lsum < rsum) return (DT_LESSTHAN); if (lsum > rsum) return (DT_GREATERTHAN); /* * If they're both equal, then we will compare based on the weights at * zero. If the weights at zero are equal (or if zero is not within * the range of the linear quantization), then this will be judged a * tie and will be resolved based on the key comparison. */ lzero = dt_aggregate_lquantizedzero(lhs); rzero = dt_aggregate_lquantizedzero(rhs); if (lzero < rzero) return (DT_LESSTHAN); if (lzero > rzero) return (DT_GREATERTHAN); return (0); } static void dt_aggregate_llquantize(int64_t *existing, int64_t *new, size_t size) { int i; for (i = 1; i < size / sizeof (int64_t); i++) existing[i] = existing[i] + new[i]; } static long double dt_aggregate_llquantizedsum(int64_t *llquanta) { int64_t arg = *llquanta++; uint16_t factor = DTRACE_LLQUANTIZE_FACTOR(arg); uint16_t low = DTRACE_LLQUANTIZE_LOW(arg); uint16_t high = DTRACE_LLQUANTIZE_HIGH(arg); uint16_t nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); int bin = 0, order; int64_t value = 1, next, step; long double total; assert(nsteps >= factor); assert(nsteps % factor == 0); for (order = 0; order < low; order++) value *= factor; total = (long double)llquanta[bin++] * (long double)(value - 1); next = value * factor; step = next > nsteps ? next / nsteps : 1; while (order <= high) { assert(value < next); total += (long double)llquanta[bin++] * (long double)(value); if ((value += step) != next) continue; next = value * factor; step = next > nsteps ? next / nsteps : 1; order++; } return (total + (long double)llquanta[bin] * (long double)value); } static int dt_aggregate_llquantizedcmp(int64_t *lhs, int64_t *rhs) { long double lsum = dt_aggregate_llquantizedsum(lhs); long double rsum = dt_aggregate_llquantizedsum(rhs); int64_t lzero, rzero; if (lsum < rsum) return (DT_LESSTHAN); if (lsum > rsum) return (DT_GREATERTHAN); /* * If they're both equal, then we will compare based on the weights at * zero. If the weights at zero are equal, then this will be judged a * tie and will be resolved based on the key comparison. */ lzero = lhs[1]; rzero = rhs[1]; if (lzero < rzero) return (DT_LESSTHAN); if (lzero > rzero) return (DT_GREATERTHAN); return (0); } static int dt_aggregate_quantizedcmp(int64_t *lhs, int64_t *rhs) { int nbuckets = DTRACE_QUANTIZE_NBUCKETS; long double ltotal = 0, rtotal = 0; int64_t lzero, rzero; uint_t i; for (i = 0; i < nbuckets; i++) { int64_t bucketval = DTRACE_QUANTIZE_BUCKETVAL(i); if (bucketval == 0) { lzero = lhs[i]; rzero = rhs[i]; } ltotal += (long double)bucketval * (long double)lhs[i]; rtotal += (long double)bucketval * (long double)rhs[i]; } if (ltotal < rtotal) return (DT_LESSTHAN); if (ltotal > rtotal) return (DT_GREATERTHAN); /* * If they're both equal, then we will compare based on the weights at * zero. If the weights at zero are equal, then this will be judged a * tie and will be resolved based on the key comparison. */ if (lzero < rzero) return (DT_LESSTHAN); if (lzero > rzero) return (DT_GREATERTHAN); return (0); } static void dt_aggregate_usym(dtrace_hdl_t *dtp, uint64_t *data) { uint64_t pid = data[0]; uint64_t *pc = &data[1]; struct ps_prochandle *P; GElf_Sym sym; if (dtp->dt_vector != NULL) return; if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) == NULL) return; dt_proc_lock(dtp, P); if (Plookup_by_addr(P, *pc, NULL, 0, &sym) == 0) *pc = sym.st_value; dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } static void dt_aggregate_umod(dtrace_hdl_t *dtp, uint64_t *data) { uint64_t pid = data[0]; uint64_t *pc = &data[1]; struct ps_prochandle *P; const prmap_t *map; if (dtp->dt_vector != NULL) return; if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) == NULL) return; dt_proc_lock(dtp, P); if ((map = Paddr_to_map(P, *pc)) != NULL) *pc = map->pr_vaddr; dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } static void dt_aggregate_sym(dtrace_hdl_t *dtp, uint64_t *data) { GElf_Sym sym; uint64_t *pc = data; if (dtrace_lookup_by_addr(dtp, *pc, &sym, NULL) == 0) *pc = sym.st_value; } static void dt_aggregate_mod(dtrace_hdl_t *dtp, uint64_t *data) { uint64_t *pc = data; dt_module_t *dmp; if (dtp->dt_vector != NULL) { /* * We don't have a way of just getting the module for a * vectored open, and it doesn't seem to be worth defining * one. This means that use of mod() won't get true * aggregation in the postmortem case (some modules may * appear more than once in aggregation output). It seems * unlikely that anyone will ever notice or care... */ return; } for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; dmp = dt_list_next(dmp)) { if (*pc - dmp->dm_text_va < dmp->dm_text_size) { *pc = dmp->dm_text_va; return; } } } static dtrace_aggvarid_t dt_aggregate_aggvarid(dt_ahashent_t *ent) { dtrace_aggdesc_t *agg = ent->dtahe_data.dtada_desc; caddr_t data = ent->dtahe_data.dtada_data; dtrace_recdesc_t *rec = agg->dtagd_rec; /* * First, we'll check the variable ID in the aggdesc. If it's valid, * we'll return it. If not, we'll use the compiler-generated ID * present as the first record. */ if (agg->dtagd_varid != DTRACE_AGGVARIDNONE) return (agg->dtagd_varid); agg->dtagd_varid = *((dtrace_aggvarid_t *)(uintptr_t)(data + rec->dtrd_offset)); return (agg->dtagd_varid); } static int dt_aggregate_snap_cpu(dtrace_hdl_t *dtp, processorid_t cpu) { dtrace_epid_t id; uint64_t hashval; size_t offs, roffs, size, ndx; int i, j, rval; caddr_t addr, data; dtrace_recdesc_t *rec; dt_aggregate_t *agp = &dtp->dt_aggregate; dtrace_aggdesc_t *agg; dt_ahash_t *hash = &agp->dtat_hash; dt_ahashent_t *h; dtrace_bufdesc_t b = agp->dtat_buf, *buf = &b; dtrace_aggdata_t *aggdata; int flags = agp->dtat_flags; buf->dtbd_cpu = cpu; #ifdef illumos if (dt_ioctl(dtp, DTRACEIOC_AGGSNAP, buf) == -1) { #else if (dt_ioctl(dtp, DTRACEIOC_AGGSNAP, &buf) == -1) { #endif if (errno == ENOENT) { /* * If that failed with ENOENT, it may be because the * CPU was unconfigured. This is okay; we'll just * do nothing but return success. */ return (0); } return (dt_set_errno(dtp, errno)); } if (buf->dtbd_drops != 0) { + xo_open_instance("probes"); + dt_oformat_drop(dtp, cpu); if (dt_handle_cpudrop(dtp, cpu, - DTRACEDROP_AGGREGATION, buf->dtbd_drops) == -1) + DTRACEDROP_AGGREGATION, buf->dtbd_drops) == -1) { + xo_close_instance("probes"); return (-1); + } + xo_close_instance("probes"); } if (buf->dtbd_size == 0) return (0); if (hash->dtah_hash == NULL) { size_t size; hash->dtah_size = DTRACE_AHASHSIZE; size = hash->dtah_size * sizeof (dt_ahashent_t *); if ((hash->dtah_hash = malloc(size)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); bzero(hash->dtah_hash, size); } for (offs = 0; offs < buf->dtbd_size; ) { /* * We're guaranteed to have an ID. */ id = *((dtrace_epid_t *)((uintptr_t)buf->dtbd_data + (uintptr_t)offs)); if (id == DTRACE_AGGIDNONE) { /* * This is filler to assure proper alignment of the * next record; we simply ignore it. */ offs += sizeof (id); continue; } if ((rval = dt_aggid_lookup(dtp, id, &agg)) != 0) return (rval); addr = buf->dtbd_data + offs; size = agg->dtagd_size; hashval = 0; for (j = 0; j < agg->dtagd_nrecs - 1; j++) { rec = &agg->dtagd_rec[j]; roffs = rec->dtrd_offset; switch (rec->dtrd_action) { case DTRACEACT_USYM: dt_aggregate_usym(dtp, /* LINTED - alignment */ (uint64_t *)&addr[roffs]); break; case DTRACEACT_UMOD: dt_aggregate_umod(dtp, /* LINTED - alignment */ (uint64_t *)&addr[roffs]); break; case DTRACEACT_SYM: /* LINTED - alignment */ dt_aggregate_sym(dtp, (uint64_t *)&addr[roffs]); break; case DTRACEACT_MOD: /* LINTED - alignment */ dt_aggregate_mod(dtp, (uint64_t *)&addr[roffs]); break; default: break; } for (i = 0; i < rec->dtrd_size; i++) hashval += addr[roffs + i]; } ndx = hashval % hash->dtah_size; for (h = hash->dtah_hash[ndx]; h != NULL; h = h->dtahe_next) { if (h->dtahe_hashval != hashval) continue; if (h->dtahe_size != size) continue; aggdata = &h->dtahe_data; data = aggdata->dtada_data; for (j = 0; j < agg->dtagd_nrecs - 1; j++) { rec = &agg->dtagd_rec[j]; roffs = rec->dtrd_offset; for (i = 0; i < rec->dtrd_size; i++) if (addr[roffs + i] != data[roffs + i]) goto hashnext; } /* * We found it. Now we need to apply the aggregating * action on the data here. */ rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; roffs = rec->dtrd_offset; /* LINTED - alignment */ h->dtahe_aggregate((int64_t *)&data[roffs], /* LINTED - alignment */ (int64_t *)&addr[roffs], rec->dtrd_size); /* * If we're keeping per CPU data, apply the aggregating * action there as well. */ if (aggdata->dtada_percpu != NULL) { data = aggdata->dtada_percpu[cpu]; /* LINTED - alignment */ h->dtahe_aggregate((int64_t *)data, /* LINTED - alignment */ (int64_t *)&addr[roffs], rec->dtrd_size); } goto bufnext; hashnext: continue; } /* * If we're here, we couldn't find an entry for this record. */ if ((h = malloc(sizeof (dt_ahashent_t))) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); bzero(h, sizeof (dt_ahashent_t)); aggdata = &h->dtahe_data; if ((aggdata->dtada_data = malloc(size)) == NULL) { free(h); return (dt_set_errno(dtp, EDT_NOMEM)); } bcopy(addr, aggdata->dtada_data, size); aggdata->dtada_size = size; aggdata->dtada_desc = agg; aggdata->dtada_handle = dtp; (void) dt_epid_lookup(dtp, agg->dtagd_epid, &aggdata->dtada_edesc, &aggdata->dtada_pdesc); aggdata->dtada_normal = 1; h->dtahe_hashval = hashval; h->dtahe_size = size; (void) dt_aggregate_aggvarid(h); rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; if (flags & DTRACE_A_PERCPU) { int max_cpus = agp->dtat_maxcpu; caddr_t *percpu = malloc(max_cpus * sizeof (caddr_t)); if (percpu == NULL) { free(aggdata->dtada_data); free(h); return (dt_set_errno(dtp, EDT_NOMEM)); } for (j = 0; j < max_cpus; j++) { percpu[j] = malloc(rec->dtrd_size); if (percpu[j] == NULL) { while (--j >= 0) free(percpu[j]); free(aggdata->dtada_data); free(h); return (dt_set_errno(dtp, EDT_NOMEM)); } if (j == cpu) { bcopy(&addr[rec->dtrd_offset], percpu[j], rec->dtrd_size); } else { bzero(percpu[j], rec->dtrd_size); } } aggdata->dtada_percpu = percpu; } switch (rec->dtrd_action) { case DTRACEAGG_MIN: h->dtahe_aggregate = dt_aggregate_min; break; case DTRACEAGG_MAX: h->dtahe_aggregate = dt_aggregate_max; break; case DTRACEAGG_LQUANTIZE: h->dtahe_aggregate = dt_aggregate_lquantize; break; case DTRACEAGG_LLQUANTIZE: h->dtahe_aggregate = dt_aggregate_llquantize; break; case DTRACEAGG_COUNT: case DTRACEAGG_SUM: case DTRACEAGG_AVG: case DTRACEAGG_STDDEV: case DTRACEAGG_QUANTIZE: h->dtahe_aggregate = dt_aggregate_count; break; default: return (dt_set_errno(dtp, EDT_BADAGG)); } if (hash->dtah_hash[ndx] != NULL) hash->dtah_hash[ndx]->dtahe_prev = h; h->dtahe_next = hash->dtah_hash[ndx]; hash->dtah_hash[ndx] = h; if (hash->dtah_all != NULL) hash->dtah_all->dtahe_prevall = h; h->dtahe_nextall = hash->dtah_all; hash->dtah_all = h; bufnext: offs += agg->dtagd_size; } return (0); } int dtrace_aggregate_snap(dtrace_hdl_t *dtp) { int i, rval; dt_aggregate_t *agp = &dtp->dt_aggregate; hrtime_t now = gethrtime(); dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_AGGRATE]; if (dtp->dt_lastagg != 0) { if (now - dtp->dt_lastagg < interval) return (0); dtp->dt_lastagg += interval; } else { dtp->dt_lastagg = now; } if (!dtp->dt_active) return (dt_set_errno(dtp, EINVAL)); if (agp->dtat_buf.dtbd_size == 0) return (0); for (i = 0; i < agp->dtat_ncpus; i++) { if ((rval = dt_aggregate_snap_cpu(dtp, agp->dtat_cpus[i]))) return (rval); } return (0); } static int dt_aggregate_hashcmp(const void *lhs, const void *rhs) { dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; if (lagg->dtagd_nrecs < ragg->dtagd_nrecs) return (DT_LESSTHAN); if (lagg->dtagd_nrecs > ragg->dtagd_nrecs) return (DT_GREATERTHAN); return (0); } static int dt_aggregate_varcmp(const void *lhs, const void *rhs) { dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); dtrace_aggvarid_t lid, rid; lid = dt_aggregate_aggvarid(lh); rid = dt_aggregate_aggvarid(rh); if (lid < rid) return (DT_LESSTHAN); if (lid > rid) return (DT_GREATERTHAN); return (0); } static int dt_aggregate_keycmp(const void *lhs, const void *rhs) { dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; dtrace_recdesc_t *lrec, *rrec; char *ldata, *rdata; int rval, i, j, keypos, nrecs; if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0) return (rval); nrecs = lagg->dtagd_nrecs - 1; assert(nrecs == ragg->dtagd_nrecs - 1); keypos = dt_keypos + 1 >= nrecs ? 0 : dt_keypos; for (i = 1; i < nrecs; i++) { uint64_t lval, rval; int ndx = i + keypos; if (ndx >= nrecs) ndx = ndx - nrecs + 1; lrec = &lagg->dtagd_rec[ndx]; rrec = &ragg->dtagd_rec[ndx]; ldata = lh->dtahe_data.dtada_data + lrec->dtrd_offset; rdata = rh->dtahe_data.dtada_data + rrec->dtrd_offset; if (lrec->dtrd_size < rrec->dtrd_size) return (DT_LESSTHAN); if (lrec->dtrd_size > rrec->dtrd_size) return (DT_GREATERTHAN); switch (lrec->dtrd_size) { case sizeof (uint64_t): /* LINTED - alignment */ lval = *((uint64_t *)ldata); /* LINTED - alignment */ rval = *((uint64_t *)rdata); break; case sizeof (uint32_t): /* LINTED - alignment */ lval = *((uint32_t *)ldata); /* LINTED - alignment */ rval = *((uint32_t *)rdata); break; case sizeof (uint16_t): /* LINTED - alignment */ lval = *((uint16_t *)ldata); /* LINTED - alignment */ rval = *((uint16_t *)rdata); break; case sizeof (uint8_t): lval = *((uint8_t *)ldata); rval = *((uint8_t *)rdata); break; default: switch (lrec->dtrd_action) { case DTRACEACT_UMOD: case DTRACEACT_UADDR: case DTRACEACT_USYM: for (j = 0; j < 2; j++) { /* LINTED - alignment */ lval = ((uint64_t *)ldata)[j]; /* LINTED - alignment */ rval = ((uint64_t *)rdata)[j]; if (lval < rval) return (DT_LESSTHAN); if (lval > rval) return (DT_GREATERTHAN); } break; default: for (j = 0; j < lrec->dtrd_size; j++) { lval = ((uint8_t *)ldata)[j]; rval = ((uint8_t *)rdata)[j]; if (lval < rval) return (DT_LESSTHAN); if (lval > rval) return (DT_GREATERTHAN); } } continue; } if (lval < rval) return (DT_LESSTHAN); if (lval > rval) return (DT_GREATERTHAN); } return (0); } static int dt_aggregate_valcmp(const void *lhs, const void *rhs) { dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; caddr_t ldata = lh->dtahe_data.dtada_data; caddr_t rdata = rh->dtahe_data.dtada_data; dtrace_recdesc_t *lrec, *rrec; int64_t *laddr, *raddr; int rval; assert(lagg->dtagd_nrecs == ragg->dtagd_nrecs); lrec = &lagg->dtagd_rec[lagg->dtagd_nrecs - 1]; rrec = &ragg->dtagd_rec[ragg->dtagd_nrecs - 1]; assert(lrec->dtrd_action == rrec->dtrd_action); laddr = (int64_t *)(uintptr_t)(ldata + lrec->dtrd_offset); raddr = (int64_t *)(uintptr_t)(rdata + rrec->dtrd_offset); switch (lrec->dtrd_action) { case DTRACEAGG_AVG: rval = dt_aggregate_averagecmp(laddr, raddr); break; case DTRACEAGG_STDDEV: rval = dt_aggregate_stddevcmp(laddr, raddr); break; case DTRACEAGG_QUANTIZE: rval = dt_aggregate_quantizedcmp(laddr, raddr); break; case DTRACEAGG_LQUANTIZE: rval = dt_aggregate_lquantizedcmp(laddr, raddr); break; case DTRACEAGG_LLQUANTIZE: rval = dt_aggregate_llquantizedcmp(laddr, raddr); break; case DTRACEAGG_COUNT: case DTRACEAGG_SUM: case DTRACEAGG_MIN: case DTRACEAGG_MAX: rval = dt_aggregate_countcmp(laddr, raddr); break; default: assert(0); } return (rval); } static int dt_aggregate_valkeycmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_valcmp(lhs, rhs)) != 0) return (rval); /* * If we're here, the values for the two aggregation elements are * equal. We already know that the key layout is the same for the two * elements; we must now compare the keys themselves as a tie-breaker. */ return (dt_aggregate_keycmp(lhs, rhs)); } static int dt_aggregate_keyvarcmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_keycmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_varcmp(lhs, rhs)); } static int dt_aggregate_varkeycmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_keycmp(lhs, rhs)); } static int dt_aggregate_valvarcmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_valkeycmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_varcmp(lhs, rhs)); } static int dt_aggregate_varvalcmp(const void *lhs, const void *rhs) { int rval; if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_valkeycmp(lhs, rhs)); } static int dt_aggregate_keyvarrevcmp(const void *lhs, const void *rhs) { return (dt_aggregate_keyvarcmp(rhs, lhs)); } static int dt_aggregate_varkeyrevcmp(const void *lhs, const void *rhs) { return (dt_aggregate_varkeycmp(rhs, lhs)); } static int dt_aggregate_valvarrevcmp(const void *lhs, const void *rhs) { return (dt_aggregate_valvarcmp(rhs, lhs)); } static int dt_aggregate_varvalrevcmp(const void *lhs, const void *rhs) { return (dt_aggregate_varvalcmp(rhs, lhs)); } static int dt_aggregate_bundlecmp(const void *lhs, const void *rhs) { dt_ahashent_t **lh = *((dt_ahashent_t ***)lhs); dt_ahashent_t **rh = *((dt_ahashent_t ***)rhs); int i, rval; if (dt_keysort) { /* * If we're sorting on keys, we need to scan until we find the * last entry -- that's the representative key. (The order of * the bundle is values followed by key to accommodate the * default behavior of sorting by value.) If the keys are * equal, we'll fall into the value comparison loop, below. */ for (i = 0; lh[i + 1] != NULL; i++) continue; assert(i != 0); assert(rh[i + 1] == NULL); if ((rval = dt_aggregate_keycmp(&lh[i], &rh[i])) != 0) return (rval); } for (i = 0; ; i++) { if (lh[i + 1] == NULL) { /* * All of the values are equal; if we're sorting on * keys, then we're only here because the keys were * found to be equal and these records are therefore * equal. If we're not sorting on keys, we'll use the * key comparison from the representative key as the * tie-breaker. */ if (dt_keysort) return (0); assert(i != 0); assert(rh[i + 1] == NULL); return (dt_aggregate_keycmp(&lh[i], &rh[i])); } else { if ((rval = dt_aggregate_valcmp(&lh[i], &rh[i])) != 0) return (rval); } } } int dt_aggregate_go(dtrace_hdl_t *dtp) { dt_aggregate_t *agp = &dtp->dt_aggregate; dtrace_optval_t size, cpu; dtrace_bufdesc_t *buf = &agp->dtat_buf; int rval, i; assert(agp->dtat_maxcpu == 0); assert(agp->dtat_ncpu == 0); assert(agp->dtat_cpus == NULL); agp->dtat_maxcpu = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; agp->dtat_ncpu = dt_sysconf(dtp, _SC_NPROCESSORS_MAX); agp->dtat_cpus = malloc(agp->dtat_ncpu * sizeof (processorid_t)); if (agp->dtat_cpus == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); /* * Use the aggregation buffer size as reloaded from the kernel. */ size = dtp->dt_options[DTRACEOPT_AGGSIZE]; rval = dtrace_getopt(dtp, "aggsize", &size); assert(rval == 0); if (size == 0 || size == DTRACEOPT_UNSET) return (0); buf = &agp->dtat_buf; buf->dtbd_size = size; if ((buf->dtbd_data = malloc(buf->dtbd_size)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); /* * Now query for the CPUs enabled. */ rval = dtrace_getopt(dtp, "cpu", &cpu); assert(rval == 0 && cpu != DTRACEOPT_UNSET); if (cpu != DTRACE_CPUALL) { assert(cpu < agp->dtat_ncpu); agp->dtat_cpus[agp->dtat_ncpus++] = (processorid_t)cpu; return (0); } agp->dtat_ncpus = 0; for (i = 0; i < agp->dtat_maxcpu; i++) { if (dt_status(dtp, i) == -1) continue; agp->dtat_cpus[agp->dtat_ncpus++] = i; } return (0); } static int dt_aggwalk_rval(dtrace_hdl_t *dtp, dt_ahashent_t *h, int rval) { dt_aggregate_t *agp = &dtp->dt_aggregate; dtrace_aggdata_t *data; dtrace_aggdesc_t *aggdesc; dtrace_recdesc_t *rec; int i; switch (rval) { case DTRACE_AGGWALK_NEXT: break; case DTRACE_AGGWALK_CLEAR: { uint32_t size, offs = 0; aggdesc = h->dtahe_data.dtada_desc; rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; size = rec->dtrd_size; data = &h->dtahe_data; if (rec->dtrd_action == DTRACEAGG_LQUANTIZE) { offs = sizeof (uint64_t); size -= sizeof (uint64_t); } bzero(&data->dtada_data[rec->dtrd_offset] + offs, size); if (data->dtada_percpu == NULL) break; for (i = 0; i < dtp->dt_aggregate.dtat_maxcpu; i++) bzero(data->dtada_percpu[i] + offs, size); break; } case DTRACE_AGGWALK_ERROR: /* * We assume that errno is already set in this case. */ return (dt_set_errno(dtp, errno)); case DTRACE_AGGWALK_ABORT: return (dt_set_errno(dtp, EDT_DIRABORT)); case DTRACE_AGGWALK_DENORMALIZE: h->dtahe_data.dtada_normal = 1; return (0); case DTRACE_AGGWALK_NORMALIZE: if (h->dtahe_data.dtada_normal == 0) { h->dtahe_data.dtada_normal = 1; return (dt_set_errno(dtp, EDT_BADRVAL)); } return (0); case DTRACE_AGGWALK_REMOVE: { dtrace_aggdata_t *aggdata = &h->dtahe_data; int max_cpus = agp->dtat_maxcpu; /* * First, remove this hash entry from its hash chain. */ if (h->dtahe_prev != NULL) { h->dtahe_prev->dtahe_next = h->dtahe_next; } else { dt_ahash_t *hash = &agp->dtat_hash; size_t ndx = h->dtahe_hashval % hash->dtah_size; assert(hash->dtah_hash[ndx] == h); hash->dtah_hash[ndx] = h->dtahe_next; } if (h->dtahe_next != NULL) h->dtahe_next->dtahe_prev = h->dtahe_prev; /* * Now remove it from the list of all hash entries. */ if (h->dtahe_prevall != NULL) { h->dtahe_prevall->dtahe_nextall = h->dtahe_nextall; } else { dt_ahash_t *hash = &agp->dtat_hash; assert(hash->dtah_all == h); hash->dtah_all = h->dtahe_nextall; } if (h->dtahe_nextall != NULL) h->dtahe_nextall->dtahe_prevall = h->dtahe_prevall; /* * We're unlinked. We can safely destroy the data. */ if (aggdata->dtada_percpu != NULL) { for (i = 0; i < max_cpus; i++) free(aggdata->dtada_percpu[i]); free(aggdata->dtada_percpu); } free(aggdata->dtada_data); free(h); return (0); } default: return (dt_set_errno(dtp, EDT_BADRVAL)); } return (0); } void dt_aggregate_qsort(dtrace_hdl_t *dtp, void *base, size_t nel, size_t width, int (*compar)(const void *, const void *)) { int rev = dt_revsort, key = dt_keysort, keypos = dt_keypos; dtrace_optval_t keyposopt = dtp->dt_options[DTRACEOPT_AGGSORTKEYPOS]; dt_revsort = (dtp->dt_options[DTRACEOPT_AGGSORTREV] != DTRACEOPT_UNSET); dt_keysort = (dtp->dt_options[DTRACEOPT_AGGSORTKEY] != DTRACEOPT_UNSET); if (keyposopt != DTRACEOPT_UNSET && keyposopt <= INT_MAX) { dt_keypos = (int)keyposopt; } else { dt_keypos = 0; } if (compar == NULL) { if (!dt_keysort) { compar = dt_aggregate_varvalcmp; } else { compar = dt_aggregate_varkeycmp; } } qsort(base, nel, width, compar); dt_revsort = rev; dt_keysort = key; dt_keypos = keypos; } int dtrace_aggregate_walk(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { dt_ahashent_t *h, *next; dt_ahash_t *hash = &dtp->dt_aggregate.dtat_hash; for (h = hash->dtah_all; h != NULL; h = next) { /* * dt_aggwalk_rval() can potentially remove the current hash * entry; we need to load the next hash entry before calling * into it. */ next = h->dtahe_nextall; if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) return (-1); } return (0); } static int dt_aggregate_total(dtrace_hdl_t *dtp, boolean_t clear) { dt_ahashent_t *h; dtrace_aggdata_t **total; dtrace_aggid_t max = DTRACE_AGGVARIDNONE, id; dt_aggregate_t *agp = &dtp->dt_aggregate; dt_ahash_t *hash = &agp->dtat_hash; uint32_t tflags; tflags = DTRACE_A_TOTAL | DTRACE_A_HASNEGATIVES | DTRACE_A_HASPOSITIVES; /* * If we need to deliver per-aggregation totals, we're going to take * three passes over the aggregate: one to clear everything out and * determine our maximum aggregation ID, one to actually total * everything up, and a final pass to assign the totals to the * individual elements. */ for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { dtrace_aggdata_t *aggdata = &h->dtahe_data; if ((id = dt_aggregate_aggvarid(h)) > max) max = id; aggdata->dtada_total = 0; aggdata->dtada_flags &= ~tflags; } if (clear || max == DTRACE_AGGVARIDNONE) return (0); total = dt_zalloc(dtp, (max + 1) * sizeof (dtrace_aggdata_t *)); if (total == NULL) return (-1); for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { dtrace_aggdata_t *aggdata = &h->dtahe_data; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_recdesc_t *rec; caddr_t data; int64_t val, *addr; rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; data = aggdata->dtada_data; addr = (int64_t *)(uintptr_t)(data + rec->dtrd_offset); switch (rec->dtrd_action) { case DTRACEAGG_STDDEV: val = dt_stddev((uint64_t *)addr, 1); break; case DTRACEAGG_SUM: case DTRACEAGG_COUNT: val = *addr; break; case DTRACEAGG_AVG: val = addr[0] ? (addr[1] / addr[0]) : 0; break; default: continue; } if (total[agg->dtagd_varid] == NULL) { total[agg->dtagd_varid] = aggdata; aggdata->dtada_flags |= DTRACE_A_TOTAL; } else { aggdata = total[agg->dtagd_varid]; } if (val > 0) aggdata->dtada_flags |= DTRACE_A_HASPOSITIVES; if (val < 0) { aggdata->dtada_flags |= DTRACE_A_HASNEGATIVES; val = -val; } if (dtp->dt_options[DTRACEOPT_AGGZOOM] != DTRACEOPT_UNSET) { val = (int64_t)((long double)val * (1 / DTRACE_AGGZOOM_MAX)); if (val > aggdata->dtada_total) aggdata->dtada_total = val; } else { aggdata->dtada_total += val; } } /* * And now one final pass to set everyone's total. */ for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { dtrace_aggdata_t *aggdata = &h->dtahe_data, *t; dtrace_aggdesc_t *agg = aggdata->dtada_desc; if ((t = total[agg->dtagd_varid]) == NULL || aggdata == t) continue; aggdata->dtada_total = t->dtada_total; aggdata->dtada_flags |= (t->dtada_flags & tflags); } dt_free(dtp, total); return (0); } static int dt_aggregate_minmaxbin(dtrace_hdl_t *dtp, boolean_t clear) { dt_ahashent_t *h; dtrace_aggdata_t **minmax; dtrace_aggid_t max = DTRACE_AGGVARIDNONE, id; dt_aggregate_t *agp = &dtp->dt_aggregate; dt_ahash_t *hash = &agp->dtat_hash; for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { dtrace_aggdata_t *aggdata = &h->dtahe_data; if ((id = dt_aggregate_aggvarid(h)) > max) max = id; aggdata->dtada_minbin = 0; aggdata->dtada_maxbin = 0; aggdata->dtada_flags &= ~DTRACE_A_MINMAXBIN; } if (clear || max == DTRACE_AGGVARIDNONE) return (0); minmax = dt_zalloc(dtp, (max + 1) * sizeof (dtrace_aggdata_t *)); if (minmax == NULL) return (-1); for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { dtrace_aggdata_t *aggdata = &h->dtahe_data; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_recdesc_t *rec; caddr_t data; int64_t *addr; int minbin = -1, maxbin = -1, i; int start = 0, size; rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; size = rec->dtrd_size / sizeof (int64_t); data = aggdata->dtada_data; addr = (int64_t *)(uintptr_t)(data + rec->dtrd_offset); switch (rec->dtrd_action) { case DTRACEAGG_LQUANTIZE: /* * For lquantize(), we always display the entire range * of the aggregation when aggpack is set. */ start = 1; minbin = start; maxbin = size - 1 - start; break; case DTRACEAGG_QUANTIZE: for (i = start; i < size; i++) { if (!addr[i]) continue; if (minbin == -1) minbin = i - start; maxbin = i - start; } if (minbin == -1) { /* * If we have no data (e.g., due to a clear() * or negative increments), we'll use the * zero bucket as both our min and max. */ minbin = maxbin = DTRACE_QUANTIZE_ZEROBUCKET; } break; default: continue; } if (minmax[agg->dtagd_varid] == NULL) { minmax[agg->dtagd_varid] = aggdata; aggdata->dtada_flags |= DTRACE_A_MINMAXBIN; aggdata->dtada_minbin = minbin; aggdata->dtada_maxbin = maxbin; continue; } if (minbin < minmax[agg->dtagd_varid]->dtada_minbin) minmax[agg->dtagd_varid]->dtada_minbin = minbin; if (maxbin > minmax[agg->dtagd_varid]->dtada_maxbin) minmax[agg->dtagd_varid]->dtada_maxbin = maxbin; } /* * And now one final pass to set everyone's minbin and maxbin. */ for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { dtrace_aggdata_t *aggdata = &h->dtahe_data, *mm; dtrace_aggdesc_t *agg = aggdata->dtada_desc; if ((mm = minmax[agg->dtagd_varid]) == NULL || aggdata == mm) continue; aggdata->dtada_minbin = mm->dtada_minbin; aggdata->dtada_maxbin = mm->dtada_maxbin; aggdata->dtada_flags |= DTRACE_A_MINMAXBIN; } dt_free(dtp, minmax); return (0); } static int dt_aggregate_walk_sorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg, int (*sfunc)(const void *, const void *)) { dt_aggregate_t *agp = &dtp->dt_aggregate; dt_ahashent_t *h, **sorted; dt_ahash_t *hash = &agp->dtat_hash; size_t i, nentries = 0; int rval = -1; agp->dtat_flags &= ~(DTRACE_A_TOTAL | DTRACE_A_MINMAXBIN); if (dtp->dt_options[DTRACEOPT_AGGHIST] != DTRACEOPT_UNSET) { agp->dtat_flags |= DTRACE_A_TOTAL; if (dt_aggregate_total(dtp, B_FALSE) != 0) return (-1); } if (dtp->dt_options[DTRACEOPT_AGGPACK] != DTRACEOPT_UNSET) { agp->dtat_flags |= DTRACE_A_MINMAXBIN; if (dt_aggregate_minmaxbin(dtp, B_FALSE) != 0) return (-1); } for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) nentries++; sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *)); if (sorted == NULL) goto out; for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) sorted[i++] = h; (void) pthread_mutex_lock(&dt_qsort_lock); if (sfunc == NULL) { dt_aggregate_qsort(dtp, sorted, nentries, sizeof (dt_ahashent_t *), NULL); } else { /* * If we've been explicitly passed a sorting function, * we'll use that -- ignoring the values of the "aggsortrev", * "aggsortkey" and "aggsortkeypos" options. */ qsort(sorted, nentries, sizeof (dt_ahashent_t *), sfunc); } (void) pthread_mutex_unlock(&dt_qsort_lock); for (i = 0; i < nentries; i++) { h = sorted[i]; if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) goto out; } rval = 0; out: if (agp->dtat_flags & DTRACE_A_TOTAL) (void) dt_aggregate_total(dtp, B_TRUE); if (agp->dtat_flags & DTRACE_A_MINMAXBIN) (void) dt_aggregate_minmaxbin(dtp, B_TRUE); dt_free(dtp, sorted); return (rval); } int dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, NULL)); } int dtrace_aggregate_walk_keysorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_varkeycmp)); } int dtrace_aggregate_walk_valsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_varvalcmp)); } int dtrace_aggregate_walk_keyvarsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_keyvarcmp)); } int dtrace_aggregate_walk_valvarsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_valvarcmp)); } int dtrace_aggregate_walk_keyrevsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_varkeyrevcmp)); } int dtrace_aggregate_walk_valrevsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_varvalrevcmp)); } int dtrace_aggregate_walk_keyvarrevsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_keyvarrevcmp)); } int dtrace_aggregate_walk_valvarrevsorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { return (dt_aggregate_walk_sorted(dtp, func, arg, dt_aggregate_valvarrevcmp)); } int dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars, int naggvars, dtrace_aggregate_walk_joined_f *func, void *arg) { dt_aggregate_t *agp = &dtp->dt_aggregate; dt_ahashent_t *h, **sorted = NULL, ***bundle, **nbundle; const dtrace_aggdata_t **data; dt_ahashent_t *zaggdata = NULL; dt_ahash_t *hash = &agp->dtat_hash; size_t nentries = 0, nbundles = 0, start, zsize = 0, bundlesize; dtrace_aggvarid_t max = 0, aggvar; int rval = -1, *map, *remap = NULL; int i, j; dtrace_optval_t sortpos = dtp->dt_options[DTRACEOPT_AGGSORTPOS]; /* * If the sorting position is greater than the number of aggregation * variable IDs, we silently set it to 0. */ if (sortpos == DTRACEOPT_UNSET || sortpos >= naggvars) sortpos = 0; /* * First we need to translate the specified aggregation variable IDs * into a linear map that will allow us to translate an aggregation * variable ID into its position in the specified aggvars. */ for (i = 0; i < naggvars; i++) { if (aggvars[i] == DTRACE_AGGVARIDNONE || aggvars[i] < 0) return (dt_set_errno(dtp, EDT_BADAGGVAR)); if (aggvars[i] > max) max = aggvars[i]; } if ((map = dt_zalloc(dtp, (max + 1) * sizeof (int))) == NULL) return (-1); zaggdata = dt_zalloc(dtp, naggvars * sizeof (dt_ahashent_t)); if (zaggdata == NULL) goto out; for (i = 0; i < naggvars; i++) { int ndx = i + sortpos; if (ndx >= naggvars) ndx -= naggvars; aggvar = aggvars[ndx]; assert(aggvar <= max); if (map[aggvar]) { /* * We have an aggregation variable that is present * more than once in the array of aggregation * variables. While it's unclear why one might want * to do this, it's legal. To support this construct, * we will allocate a remap that will indicate the * position from which this aggregation variable * should be pulled. (That is, where the remap will * map from one position to another.) */ if (remap == NULL) { remap = dt_zalloc(dtp, naggvars * sizeof (int)); if (remap == NULL) goto out; } /* * Given that the variable is already present, assert * that following through the mapping and adjusting * for the sort position yields the same aggregation * variable ID. */ assert(aggvars[(map[aggvar] - 1 + sortpos) % naggvars] == aggvars[ndx]); remap[i] = map[aggvar]; continue; } map[aggvar] = i + 1; } /* * We need to take two passes over the data to size our allocation, so * we'll use the first pass to also fill in the zero-filled data to be * used to properly format a zero-valued aggregation. */ for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { dtrace_aggvarid_t id; int ndx; if ((id = dt_aggregate_aggvarid(h)) > max || !(ndx = map[id])) continue; if (zaggdata[ndx - 1].dtahe_size == 0) { zaggdata[ndx - 1].dtahe_size = h->dtahe_size; zaggdata[ndx - 1].dtahe_data = h->dtahe_data; } nentries++; } if (nentries == 0) { /* * We couldn't find any entries; there is nothing else to do. */ rval = 0; goto out; } /* * Before we sort the data, we're going to look for any holes in our * zero-filled data. This will occur if an aggregation variable that * we are being asked to print has not yet been assigned the result of * any aggregating action for _any_ tuple. The issue becomes that we * would like a zero value to be printed for all columns for this * aggregation, but without any record description, we don't know the * aggregating action that corresponds to the aggregation variable. To * try to find a match, we're simply going to lookup aggregation IDs * (which are guaranteed to be contiguous and to start from 1), looking * for the specified aggregation variable ID. If we find a match, * we'll use that. If we iterate over all aggregation IDs and don't * find a match, then we must be an anonymous enabling. (Anonymous * enablings can't currently derive either aggregation variable IDs or * aggregation variable names given only an aggregation ID.) In this * obscure case (anonymous enabling, multiple aggregation printa() with * some aggregations not represented for any tuple), our defined * behavior is that the zero will be printed in the format of the first * aggregation variable that contains any non-zero value. */ for (i = 0; i < naggvars; i++) { if (zaggdata[i].dtahe_size == 0) { dtrace_aggvarid_t aggvar; aggvar = aggvars[(i - sortpos + naggvars) % naggvars]; assert(zaggdata[i].dtahe_data.dtada_data == NULL); for (j = DTRACE_AGGIDNONE + 1; ; j++) { dtrace_aggdesc_t *agg; dtrace_aggdata_t *aggdata; if (dt_aggid_lookup(dtp, j, &agg) != 0) break; if (agg->dtagd_varid != aggvar) continue; /* * We have our description -- now we need to * cons up the zaggdata entry for it. */ aggdata = &zaggdata[i].dtahe_data; aggdata->dtada_size = agg->dtagd_size; aggdata->dtada_desc = agg; aggdata->dtada_handle = dtp; (void) dt_epid_lookup(dtp, agg->dtagd_epid, &aggdata->dtada_edesc, &aggdata->dtada_pdesc); aggdata->dtada_normal = 1; zaggdata[i].dtahe_hashval = 0; zaggdata[i].dtahe_size = agg->dtagd_size; break; } if (zaggdata[i].dtahe_size == 0) { caddr_t data; /* * We couldn't find this aggregation, meaning * that we have never seen it before for any * tuple _and_ this is an anonymous enabling. * That is, we're in the obscure case outlined * above. In this case, our defined behavior * is to format the data in the format of the * first non-zero aggregation -- of which, of * course, we know there to be at least one * (or nentries would have been zero). */ for (j = 0; j < naggvars; j++) { if (zaggdata[j].dtahe_size != 0) break; } assert(j < naggvars); zaggdata[i] = zaggdata[j]; data = zaggdata[i].dtahe_data.dtada_data; assert(data != NULL); } } } /* * Now we need to allocate our zero-filled data for use for * aggregations that don't have a value corresponding to a given key. */ for (i = 0; i < naggvars; i++) { dtrace_aggdata_t *aggdata = &zaggdata[i].dtahe_data; dtrace_aggdesc_t *aggdesc = aggdata->dtada_desc; dtrace_recdesc_t *rec; uint64_t larg; caddr_t zdata; zsize = zaggdata[i].dtahe_size; assert(zsize != 0); if ((zdata = dt_zalloc(dtp, zsize)) == NULL) { /* * If we failed to allocated some zero-filled data, we * need to zero out the remaining dtada_data pointers * to prevent the wrong data from being freed below. */ for (j = i; j < naggvars; j++) zaggdata[j].dtahe_data.dtada_data = NULL; goto out; } aggvar = aggvars[(i - sortpos + naggvars) % naggvars]; /* * First, the easy bit. To maintain compatibility with * consumers that pull the compiler-generated ID out of the * data, we put that ID at the top of the zero-filled data. */ rec = &aggdesc->dtagd_rec[0]; /* LINTED - alignment */ *((dtrace_aggvarid_t *)(zdata + rec->dtrd_offset)) = aggvar; rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; /* * Now for the more complicated part. If (and only if) this * is an lquantize() aggregating action, zero-filled data is * not equivalent to an empty record: we must also get the * parameters for the lquantize(). */ if (rec->dtrd_action == DTRACEAGG_LQUANTIZE) { if (aggdata->dtada_data != NULL) { /* * The easier case here is if we actually have * some prototype data -- in which case we * manually dig it out of the aggregation * record. */ /* LINTED - alignment */ larg = *((uint64_t *)(aggdata->dtada_data + rec->dtrd_offset)); } else { /* * We don't have any prototype data. As a * result, we know that we _do_ have the * compiler-generated information. (If this * were an anonymous enabling, all of our * zero-filled data would have prototype data * -- either directly or indirectly.) So as * gross as it is, we'll grovel around in the * compiler-generated information to find the * lquantize() parameters. */ dtrace_stmtdesc_t *sdp; dt_ident_t *aid; dt_idsig_t *isp; sdp = (dtrace_stmtdesc_t *)(uintptr_t) aggdesc->dtagd_rec[0].dtrd_uarg; aid = sdp->dtsd_aggdata; isp = (dt_idsig_t *)aid->di_data; assert(isp->dis_auxinfo != 0); larg = isp->dis_auxinfo; } /* LINTED - alignment */ *((uint64_t *)(zdata + rec->dtrd_offset)) = larg; } aggdata->dtada_data = zdata; } /* * Now that we've dealt with setting up our zero-filled data, we can * allocate our sorted array, and take another pass over the data to * fill it. */ sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *)); if (sorted == NULL) goto out; for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) { dtrace_aggvarid_t id; if ((id = dt_aggregate_aggvarid(h)) > max || !map[id]) continue; sorted[i++] = h; } assert(i == nentries); /* * We've loaded our array; now we need to sort by value to allow us * to create bundles of like value. We're going to acquire the * dt_qsort_lock here, and hold it across all of our subsequent * comparison and sorting. */ (void) pthread_mutex_lock(&dt_qsort_lock); qsort(sorted, nentries, sizeof (dt_ahashent_t *), dt_aggregate_keyvarcmp); /* * Now we need to go through and create bundles. Because the number * of bundles is bounded by the size of the sorted array, we're going * to reuse the underlying storage. And note that "bundle" is an * array of pointers to arrays of pointers to dt_ahashent_t -- making * its type (regrettably) "dt_ahashent_t ***". (Regrettable because * '*' -- like '_' and 'X' -- should never appear in triplicate in * an ideal world.) */ bundle = (dt_ahashent_t ***)sorted; for (i = 1, start = 0; i <= nentries; i++) { if (i < nentries && dt_aggregate_keycmp(&sorted[i], &sorted[i - 1]) == 0) continue; /* * We have a bundle boundary. Everything from start to * (i - 1) belongs in one bundle. */ assert(i - start <= naggvars); bundlesize = (naggvars + 2) * sizeof (dt_ahashent_t *); if ((nbundle = dt_zalloc(dtp, bundlesize)) == NULL) { (void) pthread_mutex_unlock(&dt_qsort_lock); goto out; } for (j = start; j < i; j++) { dtrace_aggvarid_t id = dt_aggregate_aggvarid(sorted[j]); assert(id <= max); assert(map[id] != 0); assert(map[id] - 1 < naggvars); assert(nbundle[map[id] - 1] == NULL); nbundle[map[id] - 1] = sorted[j]; if (nbundle[naggvars] == NULL) nbundle[naggvars] = sorted[j]; } for (j = 0; j < naggvars; j++) { if (nbundle[j] != NULL) continue; /* * Before we assume that this aggregation variable * isn't present (and fall back to using the * zero-filled data allocated earlier), check the * remap. If we have a remapping, we'll drop it in * here. Note that we might be remapping an * aggregation variable that isn't present for this * key; in this case, the aggregation data that we * copy will point to the zeroed data. */ if (remap != NULL && remap[j]) { assert(remap[j] - 1 < j); assert(nbundle[remap[j] - 1] != NULL); nbundle[j] = nbundle[remap[j] - 1]; } else { nbundle[j] = &zaggdata[j]; } } bundle[nbundles++] = nbundle; start = i; } /* * Now we need to re-sort based on the first value. */ dt_aggregate_qsort(dtp, bundle, nbundles, sizeof (dt_ahashent_t **), dt_aggregate_bundlecmp); (void) pthread_mutex_unlock(&dt_qsort_lock); /* * We're done! Now we just need to go back over the sorted bundles, * calling the function. */ data = alloca((naggvars + 1) * sizeof (dtrace_aggdata_t *)); for (i = 0; i < nbundles; i++) { for (j = 0; j < naggvars; j++) data[j + 1] = NULL; for (j = 0; j < naggvars; j++) { int ndx = j - sortpos; if (ndx < 0) ndx += naggvars; assert(bundle[i][ndx] != NULL); data[j + 1] = &bundle[i][ndx]->dtahe_data; } for (j = 0; j < naggvars; j++) assert(data[j + 1] != NULL); /* * The representative key is the last element in the bundle. * Assert that we have one, and then set it to be the first * element of data. */ assert(bundle[i][j] != NULL); data[0] = &bundle[i][j]->dtahe_data; if ((rval = func(data, naggvars + 1, arg)) == -1) goto out; } rval = 0; out: for (i = 0; i < nbundles; i++) dt_free(dtp, bundle[i]); if (zaggdata != NULL) { for (i = 0; i < naggvars; i++) dt_free(dtp, zaggdata[i].dtahe_data.dtada_data); } dt_free(dtp, zaggdata); dt_free(dtp, sorted); dt_free(dtp, remap); dt_free(dtp, map); return (rval); } int dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp, dtrace_aggregate_walk_f *func) { dt_print_aggdata_t pd; bzero(&pd, sizeof (pd)); pd.dtpa_dtp = dtp; pd.dtpa_fp = fp; pd.dtpa_allunprint = 1; if (func == NULL) func = dtrace_aggregate_walk_sorted; - if ((*func)(dtp, dt_print_agg, &pd) == -1) - return (dt_set_errno(dtp, dtp->dt_errno)); + if (dtp->dt_oformat) { + if ((*func)(dtp, dt_format_agg, &pd) == -1) + return (dt_set_errno(dtp, dtp->dt_errno)); + } else { + 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/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c index 862d4b9222d7..6a32235f7e39 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c @@ -1,3084 +1,4228 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* + * Copyright (c) 2023, Domagoj Stolfa. All rights reserved. * Copyright (c) 2017, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include #ifdef illumos #include #endif #include #include +#include #ifndef illumos #include #endif #define DT_MASK_LO 0x00000000FFFFFFFFULL +#define dt_format_sym(dtp, addr) dt_print_sym((dtp), NULL, NULL, addr) + +typedef struct dt_prepare_args { + int first_bin; + int last_bin; + union { + struct lquantize_args { +#define lquantize_step u.lquantize.step +#define lquantize_levels u.lquantize.levels +#define lquantize_base u.lquantize.base + int base; + uint16_t step; + uint16_t levels; + } lquantize; + struct llquantize_args { +#define llquantize_next u.llquantize.next +#define llquantize_step u.llquantize.step +#define llquantize_value u.llquantize.value +#define llquantize_levels u.llquantize.levels +#define llquantize_order u.llquantize.order +#define llquantize_factor u.llquantize.factor +#define llquantize_low u.llquantize.low +#define llquantize_high u.llquantize.high +#define llquantize_nsteps u.llquantize.nsteps + int64_t next; + int64_t step; + int64_t value; + int levels; + int order; + uint16_t factor; + uint16_t low; + uint16_t high; + uint16_t nsteps; + } llquantize; + } u; +} dt_prepare_args_t; + /* * We declare this here because (1) we need it and (2) we want to avoid a * dependency on libm in libdtrace. */ static long double dt_fabsl(long double x) { if (x < 0) return (-x); return (x); } static int dt_ndigits(long long val) { int rval = 1; long long cmp = 10; if (val < 0) { val = val == INT64_MIN ? INT64_MAX : -val; rval++; } while (val > cmp && cmp > 0) { rval++; cmp *= 10; } return (rval < 4 ? 4 : rval); } /* * 128-bit arithmetic functions needed to support the stddev() aggregating * action. */ static int dt_gt_128(uint64_t *a, uint64_t *b) { return (a[1] > b[1] || (a[1] == b[1] && a[0] > b[0])); } static int dt_ge_128(uint64_t *a, uint64_t *b) { return (a[1] > b[1] || (a[1] == b[1] && a[0] >= b[0])); } static int dt_le_128(uint64_t *a, uint64_t *b) { return (a[1] < b[1] || (a[1] == b[1] && a[0] <= b[0])); } /* * Shift the 128-bit value in a by b. If b is positive, shift left. * If b is negative, shift right. */ static void dt_shift_128(uint64_t *a, int b) { uint64_t mask; if (b == 0) return; if (b < 0) { b = -b; if (b >= 64) { a[0] = a[1] >> (b - 64); a[1] = 0; } else { a[0] >>= b; mask = 1LL << (64 - b); mask -= 1; a[0] |= ((a[1] & mask) << (64 - b)); a[1] >>= b; } } else { if (b >= 64) { a[1] = a[0] << (b - 64); a[0] = 0; } else { a[1] <<= b; mask = a[0] >> (64 - b); a[1] |= mask; a[0] <<= b; } } } static int dt_nbits_128(uint64_t *a) { int nbits = 0; uint64_t tmp[2]; uint64_t zero[2] = { 0, 0 }; tmp[0] = a[0]; tmp[1] = a[1]; dt_shift_128(tmp, -1); while (dt_gt_128(tmp, zero)) { dt_shift_128(tmp, -1); nbits++; } return (nbits); } static void dt_subtract_128(uint64_t *minuend, uint64_t *subtrahend, uint64_t *difference) { uint64_t result[2]; result[0] = minuend[0] - subtrahend[0]; result[1] = minuend[1] - subtrahend[1] - (minuend[0] < subtrahend[0] ? 1 : 0); difference[0] = result[0]; difference[1] = result[1]; } static void dt_add_128(uint64_t *addend1, uint64_t *addend2, uint64_t *sum) { uint64_t result[2]; result[0] = addend1[0] + addend2[0]; result[1] = addend1[1] + addend2[1] + (result[0] < addend1[0] || result[0] < addend2[0] ? 1 : 0); sum[0] = result[0]; sum[1] = result[1]; } /* * The basic idea is to break the 2 64-bit values into 4 32-bit values, * use native multiplication on those, and then re-combine into the * resulting 128-bit value. * * (hi1 << 32 + lo1) * (hi2 << 32 + lo2) = * hi1 * hi2 << 64 + * hi1 * lo2 << 32 + * hi2 * lo1 << 32 + * lo1 * lo2 */ static void dt_multiply_128(uint64_t factor1, uint64_t factor2, uint64_t *product) { uint64_t hi1, hi2, lo1, lo2; uint64_t tmp[2]; hi1 = factor1 >> 32; hi2 = factor2 >> 32; lo1 = factor1 & DT_MASK_LO; lo2 = factor2 & DT_MASK_LO; product[0] = lo1 * lo2; product[1] = hi1 * hi2; tmp[0] = hi1 * lo2; tmp[1] = 0; dt_shift_128(tmp, 32); dt_add_128(product, tmp, product); tmp[0] = hi2 * lo1; tmp[1] = 0; dt_shift_128(tmp, 32); dt_add_128(product, tmp, product); } /* * This is long-hand division. * * We initialize subtrahend by shifting divisor left as far as possible. We * loop, comparing subtrahend to dividend: if subtrahend is smaller, we * subtract and set the appropriate bit in the result. We then shift * subtrahend right by one bit for the next comparison. */ static void dt_divide_128(uint64_t *dividend, uint64_t divisor, uint64_t *quotient) { uint64_t result[2] = { 0, 0 }; uint64_t remainder[2]; uint64_t subtrahend[2]; uint64_t divisor_128[2]; uint64_t mask[2] = { 1, 0 }; int log = 0; assert(divisor != 0); divisor_128[0] = divisor; divisor_128[1] = 0; remainder[0] = dividend[0]; remainder[1] = dividend[1]; subtrahend[0] = divisor; subtrahend[1] = 0; while (divisor > 0) { log++; divisor >>= 1; } dt_shift_128(subtrahend, 128 - log); dt_shift_128(mask, 128 - log); while (dt_ge_128(remainder, divisor_128)) { if (dt_ge_128(remainder, subtrahend)) { dt_subtract_128(remainder, subtrahend, remainder); result[0] |= mask[0]; result[1] |= mask[1]; } dt_shift_128(subtrahend, -1); dt_shift_128(mask, -1); } quotient[0] = result[0]; quotient[1] = result[1]; } /* * This is the long-hand method of calculating a square root. * The algorithm is as follows: * * 1. Group the digits by 2 from the right. * 2. Over the leftmost group, find the largest single-digit number * whose square is less than that group. * 3. Subtract the result of the previous step (2 or 4, depending) and * bring down the next two-digit group. * 4. For the result R we have so far, find the largest single-digit number * x such that 2 * R * 10 * x + x^2 is less than the result from step 3. * (Note that this is doubling R and performing a decimal left-shift by 1 * and searching for the appropriate decimal to fill the one's place.) * The value x is the next digit in the square root. * Repeat steps 3 and 4 until the desired precision is reached. (We're * dealing with integers, so the above is sufficient.) * * In decimal, the square root of 582,734 would be calculated as so: * * __7__6__3 * | 58 27 34 * -49 (7^2 == 49 => 7 is the first digit in the square root) * -- * 9 27 (Subtract and bring down the next group.) * 146 8 76 (2 * 7 * 10 * 6 + 6^2 == 876 => 6 is the next digit in * ----- the square root) * 51 34 (Subtract and bring down the next group.) * 1523 45 69 (2 * 76 * 10 * 3 + 3^2 == 4569 => 3 is the next digit in * ----- the square root) * 5 65 (remainder) * * The above algorithm applies similarly in binary, but note that the * only possible non-zero value for x in step 4 is 1, so step 4 becomes a * simple decision: is 2 * R * 2 * 1 + 1^2 (aka R << 2 + 1) less than the * preceding difference? * * In binary, the square root of 11011011 would be calculated as so: * * __1__1__1__0 * | 11 01 10 11 * 01 (0 << 2 + 1 == 1 < 11 => this bit is 1) * -- * 10 01 10 11 * 101 1 01 (1 << 2 + 1 == 101 < 1001 => next bit is 1) * ----- * 1 00 10 11 * 1101 11 01 (11 << 2 + 1 == 1101 < 10010 => next bit is 1) * ------- * 1 01 11 * 11101 1 11 01 (111 << 2 + 1 == 11101 > 10111 => last bit is 0) * */ static uint64_t dt_sqrt_128(uint64_t *square) { uint64_t result[2] = { 0, 0 }; uint64_t diff[2] = { 0, 0 }; uint64_t one[2] = { 1, 0 }; uint64_t next_pair[2]; uint64_t next_try[2]; uint64_t bit_pairs, pair_shift; int i; bit_pairs = dt_nbits_128(square) / 2; pair_shift = bit_pairs * 2; for (i = 0; i <= bit_pairs; i++) { /* * Bring down the next pair of bits. */ next_pair[0] = square[0]; next_pair[1] = square[1]; dt_shift_128(next_pair, -pair_shift); next_pair[0] &= 0x3; next_pair[1] = 0; dt_shift_128(diff, 2); dt_add_128(diff, next_pair, diff); /* * next_try = R << 2 + 1 */ next_try[0] = result[0]; next_try[1] = result[1]; dt_shift_128(next_try, 2); dt_add_128(next_try, one, next_try); if (dt_le_128(next_try, diff)) { dt_subtract_128(diff, next_try, diff); dt_shift_128(result, 1); dt_add_128(result, one, result); } else { dt_shift_128(result, 1); } pair_shift -= 2; } assert(result[1] == 0); return (result[0]); } uint64_t dt_stddev(uint64_t *data, uint64_t normal) { uint64_t avg_of_squares[2]; uint64_t square_of_avg[2]; int64_t norm_avg; uint64_t diff[2]; if (data[0] == 0) return (0); /* * 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. * When normalizing, we should divide the sum of x**2 by normal**2. */ dt_divide_128(data + 2, normal, avg_of_squares); dt_divide_128(avg_of_squares, normal, avg_of_squares); dt_divide_128(avg_of_squares, data[0], avg_of_squares); norm_avg = (int64_t)data[1] / (int64_t)normal / (int64_t)data[0]; if (norm_avg < 0) norm_avg = -norm_avg; dt_multiply_128((uint64_t)norm_avg, (uint64_t)norm_avg, square_of_avg); dt_subtract_128(avg_of_squares, square_of_avg, diff); return (dt_sqrt_128(diff)); } static int dt_flowindent(dtrace_hdl_t *dtp, dtrace_probedata_t *data, dtrace_epid_t last, dtrace_bufdesc_t *buf, size_t offs) { dtrace_probedesc_t *pd = data->dtpda_pdesc, *npd; dtrace_eprobedesc_t *epd = data->dtpda_edesc, *nepd; char *p = pd->dtpd_provider, *n = pd->dtpd_name, *sub; dtrace_flowkind_t flow = DTRACEFLOW_NONE; const char *str = NULL; static const char *e_str[2] = { " -> ", " => " }; static const char *r_str[2] = { " <- ", " <= " }; static const char *ent = "entry", *ret = "return"; static int entlen = 0, retlen = 0; dtrace_epid_t next, id = epd->dtepd_epid; int rval; if (entlen == 0) { assert(retlen == 0); entlen = strlen(ent); retlen = strlen(ret); } /* * If the name of the probe is "entry" or ends with "-entry", we * treat it as an entry; if it is "return" or ends with "-return", * we treat it as a return. (This allows application-provided probes * like "method-entry" or "function-entry" to participate in flow * indentation -- without accidentally misinterpreting popular probe * names like "carpentry", "gentry" or "Coventry".) */ if ((sub = strstr(n, ent)) != NULL && sub[entlen] == '\0' && (sub == n || sub[-1] == '-')) { flow = DTRACEFLOW_ENTRY; str = e_str[strcmp(p, "syscall") == 0]; } else if ((sub = strstr(n, ret)) != NULL && sub[retlen] == '\0' && (sub == n || sub[-1] == '-')) { flow = DTRACEFLOW_RETURN; str = r_str[strcmp(p, "syscall") == 0]; } /* * If we're going to indent this, we need to check the ID of our last * call. If we're looking at the same probe ID but a different EPID, * we _don't_ want to indent. (Yes, there are some minor holes in * this scheme -- it's a heuristic.) */ if (flow == DTRACEFLOW_ENTRY) { if ((last != DTRACE_EPIDNONE && id != last && pd->dtpd_id == dtp->dt_pdesc[last]->dtpd_id)) flow = DTRACEFLOW_NONE; } /* * If we're going to unindent this, it's more difficult to see if * we don't actually want to unindent it -- we need to look at the * _next_ EPID. */ if (flow == DTRACEFLOW_RETURN) { offs += epd->dtepd_size; do { if (offs >= buf->dtbd_size) goto out; next = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); if (next == DTRACE_EPIDNONE) offs += sizeof (id); } while (next == DTRACE_EPIDNONE); if ((rval = dt_epid_lookup(dtp, next, &nepd, &npd)) != 0) return (rval); if (next != id && npd->dtpd_id == pd->dtpd_id) flow = DTRACEFLOW_NONE; } out: if (flow == DTRACEFLOW_ENTRY || flow == DTRACEFLOW_RETURN) { data->dtpda_prefix = str; } else { data->dtpda_prefix = "| "; } if (flow == DTRACEFLOW_RETURN && data->dtpda_indent > 0) data->dtpda_indent -= 2; data->dtpda_flow = flow; return (0); } static int dt_nullprobe() { return (DTRACE_CONSUME_THIS); } static int dt_nullrec() { return (DTRACE_CONSUME_NEXT); } static void dt_quantize_total(dtrace_hdl_t *dtp, int64_t datum, long double *total) { long double val = dt_fabsl((long double)datum); if (dtp->dt_options[DTRACEOPT_AGGZOOM] == DTRACEOPT_UNSET) { *total += val; return; } /* * If we're zooming in on an aggregation, we want the height of the * highest value to be approximately 95% of total bar height -- so we * adjust up by the reciprocal of DTRACE_AGGZOOM_MAX when comparing to * our highest value. */ val *= 1 / DTRACE_AGGZOOM_MAX; if (*total < val) *total = val; } static int dt_print_quanthdr(dtrace_hdl_t *dtp, FILE *fp, int width) { return (dt_printf(dtp, fp, "\n%*s %41s %-9s\n", width ? width : 16, width ? "key" : "value", "------------- Distribution -------------", "count")); } static int dt_print_quanthdr_packed(dtrace_hdl_t *dtp, FILE *fp, int width, const dtrace_aggdata_t *aggdata, dtrace_actkind_t action) { int min = aggdata->dtada_minbin, max = aggdata->dtada_maxbin; int minwidth, maxwidth, i; assert(action == DTRACEAGG_QUANTIZE || action == DTRACEAGG_LQUANTIZE); if (action == DTRACEAGG_QUANTIZE) { if (min != 0 && min != DTRACE_QUANTIZE_ZEROBUCKET) min--; if (max < DTRACE_QUANTIZE_NBUCKETS - 1) max++; minwidth = dt_ndigits(DTRACE_QUANTIZE_BUCKETVAL(min)); maxwidth = dt_ndigits(DTRACE_QUANTIZE_BUCKETVAL(max)); } else { maxwidth = 8; minwidth = maxwidth - 1; max++; } if (dt_printf(dtp, fp, "\n%*s %*s .", width, width > 0 ? "key" : "", minwidth, "min") < 0) return (-1); for (i = min; i <= max; i++) { if (dt_printf(dtp, fp, "-") < 0) return (-1); } return (dt_printf(dtp, fp, ". %*s | count\n", -maxwidth, "max")); } /* * We use a subset of the Unicode Block Elements (U+2588 through U+258F, * inclusive) to represent aggregations via UTF-8 -- which are expressed via * 3-byte UTF-8 sequences. */ #define DTRACE_AGGUTF8_FULL 0x2588 #define DTRACE_AGGUTF8_BASE 0x258f #define DTRACE_AGGUTF8_LEVELS 8 #define DTRACE_AGGUTF8_BYTE0(val) (0xe0 | ((val) >> 12)) #define DTRACE_AGGUTF8_BYTE1(val) (0x80 | (((val) >> 6) & 0x3f)) #define DTRACE_AGGUTF8_BYTE2(val) (0x80 | ((val) & 0x3f)) static int dt_print_quantline_utf8(dtrace_hdl_t *dtp, FILE *fp, int64_t val, uint64_t normal, long double total) { uint_t len = 40, i, whole, partial; long double f = (dt_fabsl((long double)val) * len) / total; const char *spaces = " "; whole = (uint_t)f; partial = (uint_t)((f - (long double)(uint_t)f) * (long double)DTRACE_AGGUTF8_LEVELS); if (dt_printf(dtp, fp, "|") < 0) return (-1); for (i = 0; i < whole; i++) { if (dt_printf(dtp, fp, "%c%c%c", DTRACE_AGGUTF8_BYTE0(DTRACE_AGGUTF8_FULL), DTRACE_AGGUTF8_BYTE1(DTRACE_AGGUTF8_FULL), DTRACE_AGGUTF8_BYTE2(DTRACE_AGGUTF8_FULL)) < 0) return (-1); } if (partial != 0) { partial = DTRACE_AGGUTF8_BASE - (partial - 1); if (dt_printf(dtp, fp, "%c%c%c", DTRACE_AGGUTF8_BYTE0(partial), DTRACE_AGGUTF8_BYTE1(partial), DTRACE_AGGUTF8_BYTE2(partial)) < 0) return (-1); i++; } return (dt_printf(dtp, fp, "%s %-9lld\n", spaces + i, (long long)val / normal)); } static int dt_print_quantline(dtrace_hdl_t *dtp, FILE *fp, int64_t val, uint64_t normal, long double total, char positives, char negatives) { long double f; uint_t depth, len = 40; const char *ats = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"; const char *spaces = " "; assert(strlen(ats) == len && strlen(spaces) == len); assert(!(total == 0 && (positives || negatives))); assert(!(val < 0 && !negatives)); assert(!(val > 0 && !positives)); assert(!(val != 0 && total == 0)); if (!negatives) { if (positives) { if (dtp->dt_encoding == DT_ENCODING_UTF8) { return (dt_print_quantline_utf8(dtp, fp, val, normal, total)); } f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); } else { depth = 0; } return (dt_printf(dtp, fp, "|%s%s %-9lld\n", ats + len - depth, spaces + depth, (long long)val / normal)); } if (!positives) { f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); return (dt_printf(dtp, fp, "%s%s| %-9lld\n", spaces + depth, ats + len - depth, (long long)val / normal)); } /* * If we're here, we have both positive and negative bucket values. * To express this graphically, we're going to generate both positive * and negative bars separated by a centerline. These bars are half * the size of normal quantize()/lquantize() bars, so we divide the * length in half before calculating the bar length. */ len /= 2; ats = &ats[len]; spaces = &spaces[len]; f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); if (val <= 0) { return (dt_printf(dtp, fp, "%s%s|%*s %-9lld\n", spaces + depth, ats + len - depth, len, "", (long long)val / normal)); } else { return (dt_printf(dtp, fp, "%20s|%s%s %-9lld\n", "", ats + len - depth, spaces + depth, (long long)val / normal)); } } /* * As with UTF-8 printing of aggregations, we use a subset of the Unicode * Block Elements (U+2581 through U+2588, inclusive) to represent our packed * aggregation. */ #define DTRACE_AGGPACK_BASE 0x2581 #define DTRACE_AGGPACK_LEVELS 8 static int dt_print_packed(dtrace_hdl_t *dtp, FILE *fp, long double datum, long double total) { static boolean_t utf8_checked = B_FALSE; static boolean_t utf8; char *ascii = "__xxxxXX"; char *neg = "vvvvVV"; unsigned int len; long double val; if (!utf8_checked) { char *term; /* * We want to determine if we can reasonably emit UTF-8 for our * packed aggregation. To do this, we will check for terminals * that are known to be primitive to emit UTF-8 on these. */ utf8_checked = B_TRUE; if (dtp->dt_encoding == DT_ENCODING_ASCII) { utf8 = B_FALSE; } else if (dtp->dt_encoding == DT_ENCODING_UTF8) { utf8 = B_TRUE; } else if ((term = getenv("TERM")) != NULL && (strcmp(term, "sun") == 0 || strcmp(term, "sun-color") == 0 || strcmp(term, "dumb") == 0)) { utf8 = B_FALSE; } else { utf8 = B_TRUE; } } if (datum == 0) return (dt_printf(dtp, fp, " ")); if (datum < 0) { len = strlen(neg); val = dt_fabsl(datum * (len - 1)) / total; return (dt_printf(dtp, fp, "%c", neg[(uint_t)(val + 0.5)])); } if (utf8) { int block = DTRACE_AGGPACK_BASE + (unsigned int)(((datum * (DTRACE_AGGPACK_LEVELS - 1)) / total) + 0.5); return (dt_printf(dtp, fp, "%c%c%c", DTRACE_AGGUTF8_BYTE0(block), DTRACE_AGGUTF8_BYTE1(block), DTRACE_AGGUTF8_BYTE2(block))); } len = strlen(ascii); val = (datum * (len - 1)) / total; return (dt_printf(dtp, fp, "%c", ascii[(uint_t)(val + 0.5)])); } -int -dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, - size_t size, uint64_t normal) +static const int64_t * +dt_format_quantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size, + dt_prepare_args_t *args) { 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; + int first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; - if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) - return (dt_set_errno(dtp, EDT_DMISMATCH)); + if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) { + (void) dt_set_errno(dtp, EDT_DMISMATCH); + return (NULL); + } 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 the aggregation * has been clear()'d or if negative increment values have been * used. Regardless, we'll print the buckets around 0. */ first_bin = DTRACE_QUANTIZE_ZEROBUCKET - 1; last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 1; } else { if (first_bin > 0) first_bin--; while (last_bin > 0 && data[last_bin] == 0) last_bin--; if (last_bin < DTRACE_QUANTIZE_NBUCKETS - 1) last_bin++; } + args->first_bin = first_bin; + args->last_bin = last_bin; + return (data); +} + +int +dt_format_quantize(dtrace_hdl_t *dtp, const void *addr, size_t size, + uint64_t normal) +{ + const int64_t *data; + dt_prepare_args_t args = { 0 }; + int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; + + data = dt_format_quantize_prepare(dtp, addr, size, &args); + /* dt_errno is set for us */ + if (data == NULL) + return (-1); + + first_bin = args.first_bin; + last_bin = args.last_bin; + + xo_open_list("buckets"); + for (i = first_bin; i <= last_bin; i++) { + long long value = (long long)DTRACE_QUANTIZE_BUCKETVAL(i); + xo_open_instance("buckets"); + xo_emit("{:value/%lld} {:count/%lld}", value, + (long long)data[i] / normal); + xo_close_instance("buckets"); + } + xo_close_list("buckets"); + + return (0); +} + +int +dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, + size_t size, uint64_t normal) +{ + const int64_t *data; + dt_prepare_args_t args = { 0 }; + int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; + long double total = 0; + char positives = 0, negatives = 0; + + data = dt_format_quantize_prepare(dtp, addr, size, &args); + /* dt_errno is set for us */ + if (data == NULL) + return (-1); + + first_bin = args.first_bin; + last_bin = args.last_bin; + for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); dt_quantize_total(dtp, data[i], &total); } if (dt_print_quanthdr(dtp, fp, 0) < 0) return (-1); for (i = first_bin; i <= last_bin; i++) { if (dt_printf(dtp, fp, "%16lld ", (long long)DTRACE_QUANTIZE_BUCKETVAL(i)) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[i], normal, total, positives, negatives) < 0) return (-1); } return (0); } int dt_print_quantize_packed(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, const dtrace_aggdata_t *aggdata) { const int64_t *data = addr; long double total = 0, count = 0; int min = aggdata->dtada_minbin, max = aggdata->dtada_maxbin, i; int64_t minval, maxval; if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); if (min != 0 && min != DTRACE_QUANTIZE_ZEROBUCKET) min--; if (max < DTRACE_QUANTIZE_NBUCKETS - 1) max++; minval = DTRACE_QUANTIZE_BUCKETVAL(min); maxval = DTRACE_QUANTIZE_BUCKETVAL(max); if (dt_printf(dtp, fp, " %*lld :", dt_ndigits(minval), (long long)minval) < 0) return (-1); for (i = min; i <= max; i++) { dt_quantize_total(dtp, data[i], &total); count += data[i]; } for (i = min; i <= max; i++) { if (dt_print_packed(dtp, fp, data[i], total) < 0) return (-1); } if (dt_printf(dtp, fp, ": %*lld | %lld\n", -dt_ndigits(maxval), (long long)maxval, (long long)count) < 0) return (-1); return (0); } -int -dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, - size_t size, uint64_t normal) +static const int64_t * +dt_format_lquantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size, + dt_prepare_args_t *args) { const int64_t *data = addr; - int i, first_bin, last_bin, base; + int first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1, 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)); + if (size < sizeof (uint64_t)) { + (void) dt_set_errno(dtp, EDT_DMISMATCH); + return (NULL); + } 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)); + if (size != sizeof (uint64_t) * (levels + 2)) { + (void) dt_set_errno(dtp, EDT_DMISMATCH); + return (NULL); + } 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++; } + args->first_bin = first_bin; + args->last_bin = last_bin; + args->lquantize_base = base; + args->lquantize_step = step; + args->lquantize_levels = levels; + return (data); +} + +int +dt_format_lquantize(dtrace_hdl_t *dtp, const void *addr, size_t size, + uint64_t normal) +{ + const int64_t *data; + dt_prepare_args_t args = { 0 }; + int i, first_bin, last_bin, base; + uint16_t step, levels; + + data = dt_format_lquantize_prepare(dtp, addr, size, &args); + /* dt_errno is set for us */ + if (data == NULL) + return (-1); + + first_bin = args.first_bin; + last_bin = args.last_bin; + step = args.lquantize_step; + levels = args.lquantize_levels; + base = args.lquantize_base; + + xo_open_list("buckets"); + for (i = first_bin; i <= last_bin; i++) { + char c[32]; + int err; + + xo_open_instance("buckets"); + if (i == 0) { + xo_emit("{:value/%d} {:operator/%s}", base, "<"); + } else if (i == levels + 1) { + xo_emit("{:value/%d} {:operator/%s}", + base + (levels * step), ">="); + } else { + xo_emit("{:value/%d}", base + (i - 1) * step); + } + + xo_emit("{:count/%lld}", (long long)data[i] / normal); + xo_close_instance("buckets"); + } + xo_close_list("buckets"); + + 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; + dt_prepare_args_t args = { 0 }; + int i, first_bin, last_bin, base; + uint64_t arg; + long double total = 0; + uint16_t step, levels; + char positives = 0, negatives = 0; + + data = dt_format_lquantize_prepare(dtp, addr, size, &args); + /* dt_errno is set for us */ + if (data == NULL) + return (-1); + + first_bin = args.first_bin; + last_bin = args.last_bin; + step = args.lquantize_step; + levels = args.lquantize_levels; + base = args.lquantize_base; + for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); dt_quantize_total(dtp, data[i], &total); } if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", "------------- Distribution -------------", "count") < 0) return (-1); for (i = first_bin; i <= last_bin; i++) { char c[32]; int err; if (i == 0) { (void) snprintf(c, sizeof (c), "< %d", base); err = dt_printf(dtp, fp, "%16s ", c); } else if (i == levels + 1) { (void) snprintf(c, sizeof (c), ">= %d", base + (levels * step)); err = dt_printf(dtp, fp, "%16s ", c); } else { err = dt_printf(dtp, fp, "%16d ", base + (i - 1) * step); } if (err < 0 || dt_print_quantline(dtp, fp, data[i], normal, total, positives, negatives) < 0) return (-1); } return (0); } /*ARGSUSED*/ int dt_print_lquantize_packed(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, const dtrace_aggdata_t *aggdata) { const int64_t *data = addr; long double total = 0, count = 0; int min, max, base, err; uint64_t arg; uint16_t step, levels; char c[32]; unsigned int i; if (size < sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); arg = *data++; size -= sizeof (uint64_t); base = DTRACE_LQUANTIZE_BASE(arg); step = DTRACE_LQUANTIZE_STEP(arg); levels = DTRACE_LQUANTIZE_LEVELS(arg); if (size != sizeof (uint64_t) * (levels + 2)) return (dt_set_errno(dtp, EDT_DMISMATCH)); min = 0; max = levels + 1; if (min == 0) { (void) snprintf(c, sizeof (c), "< %d", base); err = dt_printf(dtp, fp, "%8s :", c); } else { err = dt_printf(dtp, fp, "%8d :", base + (min - 1) * step); } if (err < 0) return (-1); for (i = min; i <= max; i++) { dt_quantize_total(dtp, data[i], &total); count += data[i]; } for (i = min; i <= max; i++) { if (dt_print_packed(dtp, fp, data[i], total) < 0) return (-1); } (void) snprintf(c, sizeof (c), ">= %d", base + (levels * step)); return (dt_printf(dtp, fp, ": %-8s | %lld\n", c, (long long)count)); } -int -dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, - size_t size, uint64_t normal) +static const int64_t * +dt_format_llquantize_prepare(dtrace_hdl_t *dtp, const void *addr, size_t size, + dt_prepare_args_t *args) { int i, first_bin, last_bin, bin = 1, order, levels; uint16_t factor, low, high, nsteps; const int64_t *data = addr; int64_t value = 1, next, step; - char positives = 0, negatives = 0; - long double total = 0; uint64_t arg; - char c[32]; - if (size < sizeof (uint64_t)) - return (dt_set_errno(dtp, EDT_DMISMATCH)); + if (size < sizeof(uint64_t)) { + (void) dt_set_errno(dtp, EDT_DMISMATCH); + return (NULL); + } arg = *data++; size -= sizeof (uint64_t); factor = DTRACE_LLQUANTIZE_FACTOR(arg); low = DTRACE_LLQUANTIZE_LOW(arg); high = DTRACE_LLQUANTIZE_HIGH(arg); nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); /* * We don't expect to be handed invalid llquantize() parameters here, * but sanity check them (to a degree) nonetheless. */ if (size > INT32_MAX || factor < 2 || low >= high || - nsteps == 0 || factor > nsteps) - return (dt_set_errno(dtp, EDT_DMISMATCH)); + nsteps == 0 || factor > nsteps) { + (void) dt_set_errno(dtp, EDT_DMISMATCH); + return (NULL); + } levels = (int)size / sizeof (uint64_t); first_bin = 0; last_bin = levels - 1; while (first_bin < levels && data[first_bin] == 0) first_bin++; if (first_bin == levels) { first_bin = 0; last_bin = 1; } else { if (first_bin > 0) first_bin--; while (last_bin > 0 && data[last_bin] == 0) last_bin--; if (last_bin < levels - 1) last_bin++; } + for (order = 0; order < low; order++) + value *= factor; + + next = value * factor; + step = next > nsteps ? next / nsteps : 1; + + args->first_bin = first_bin; + args->last_bin = last_bin; + args->llquantize_factor = factor; + args->llquantize_low = low; + args->llquantize_high = high; + args->llquantize_nsteps = nsteps; + args->llquantize_levels = levels; + args->llquantize_order = order; + args->llquantize_next = next; + args->llquantize_step = step; + args->llquantize_value = value; + + return (data); +} + +int +dt_format_llquantize(dtrace_hdl_t *dtp, const void *addr, size_t size, + uint64_t normal) +{ + int first_bin, last_bin, bin = 1, order, levels; + uint16_t factor, low, high, nsteps; + const int64_t *data; + dt_prepare_args_t args = { 0 }; + int64_t value = 1, next, step; + uint64_t arg; + char c[32]; + + data = dt_format_llquantize_prepare(dtp, addr, size, &args); + /* dt_errno is set for us */ + if (data == NULL) + return (-1); + + first_bin = args.first_bin; + last_bin = args.last_bin; + factor = args.llquantize_factor; + low = args.llquantize_low; + high = args.llquantize_high; + nsteps = args.llquantize_nsteps; + levels = args.llquantize_levels; + order = args.llquantize_order; + next = args.llquantize_next; + step = args.llquantize_step; + value = args.llquantize_value; + + xo_open_list("buckets"); + if (first_bin == 0) { + /* + * We have to represent < value somehow in JSON, so we bundle an + * optional "operator" in llquantize buckets. + */ + xo_open_instance("buckets"); + xo_emit("{:value/%lld} {:count/%lld} {:operator/%s}", + (long long)value, (long long)data[0] / normal, "<"); + xo_close_instance("buckets"); + } + + while (order <= high) { + if (bin >= first_bin && bin <= last_bin) { + xo_open_instance("buckets"); + xo_emit("{:value/%lld} {:count/%lld}", (long long)value, + (long long)data[bin] / normal); + xo_close_instance("buckets"); + } + + assert(value < next); + bin++; + + if ((value += step) != next) + continue; + + next = value * factor; + step = next > nsteps ? next / nsteps : 1; + order++; + } + + if (last_bin < bin) { + xo_close_list("buckets"); + return (0); + } + + assert(last_bin == bin); + xo_open_instance("buckets"); + xo_emit("{:value/%lld} {:count/%lld} {:operator/%s}", (long long)value, + (long long)data[bin] / normal, ">="); + xo_close_instance("buckets"); + + xo_close_list("buckets"); + return (0); +} + +int +dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, + size_t size, uint64_t normal) +{ + int i, first_bin, last_bin, bin = 1, order, levels; + uint16_t factor, low, high, nsteps; + const int64_t *data; + dt_prepare_args_t args = { 0 }; + int64_t value = 1, next, step; + char positives = 0, negatives = 0; + long double total = 0; + uint64_t arg; + char c[32]; + + data = dt_format_llquantize_prepare(dtp, addr, size, &args); + /* dt_errno is set for us */ + if (data == NULL) + return (-1); + + first_bin = args.first_bin; + last_bin = args.last_bin; + factor = args.llquantize_factor; + low = args.llquantize_low; + high = args.llquantize_high; + nsteps = args.llquantize_nsteps; + levels = args.llquantize_levels; + order = args.llquantize_order; + next = args.llquantize_next; + step = args.llquantize_step; + value = args.llquantize_value; + for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); dt_quantize_total(dtp, data[i], &total); } if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", "------------- Distribution -------------", "count") < 0) return (-1); - for (order = 0; order < low; order++) - value *= factor; - - next = value * factor; - step = next > nsteps ? next / nsteps : 1; - if (first_bin == 0) { (void) snprintf(c, sizeof (c), "< %lld", (long long)value); if (dt_printf(dtp, fp, "%16s ", c) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[0], normal, total, positives, negatives) < 0) return (-1); } while (order <= high) { if (bin >= first_bin && bin <= last_bin) { if (dt_printf(dtp, fp, "%16lld ", (long long)value) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[bin], normal, total, positives, negatives) < 0) return (-1); } assert(value < next); bin++; if ((value += step) != next) continue; next = value * factor; step = next > nsteps ? next / nsteps : 1; order++; } if (last_bin < bin) return (0); assert(last_bin == bin); (void) snprintf(c, sizeof (c), ">= %lld", (long long)value); if (dt_printf(dtp, fp, "%16s ", c) < 0) return (-1); return (dt_print_quantline(dtp, fp, data[bin], normal, total, positives, negatives)); } +static int +dt_format_average(dtrace_hdl_t *dtp, caddr_t addr, size_t size, uint64_t normal) +{ + int64_t *data = (int64_t *)addr; + + xo_emit("{:average/%lld}", + data[0] ? (long long)(data[1] / (int64_t)normal / data[0]) : 0); + 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; return (dt_printf(dtp, fp, " %16lld", data[0] ? (long long)(data[1] / (int64_t)normal / data[0]) : 0)); } +static int +dt_format_stddev(dtrace_hdl_t *dtp, caddr_t addr, size_t size, uint64_t normal) +{ + uint64_t *data = (uint64_t *)addr; + + xo_emit("{:stddev/%llu}", + data[0] ? (unsigned long long)dt_stddev(data, normal) : 0); + return (0); +} + /*ARGSUSED*/ static int dt_print_stddev(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t size, uint64_t normal) { /* LINTED - alignment */ uint64_t *data = (uint64_t *)addr; return (dt_printf(dtp, fp, " %16llu", data[0] ? (unsigned long long) dt_stddev(data, normal) : 0)); } /*ARGSUSED*/ static int dt_print_bytes(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t nbytes, int width, int quiet, int forceraw) { /* * If the byte stream is a series of printable characters, followed by * a terminating byte, we print it out as a string. Otherwise, we * assume that it's something else and just print the bytes. */ int i, j, margin = 5; char *c = (char *)addr; if (nbytes == 0) return (0); if (forceraw) goto raw; if (dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET) goto raw; for (i = 0; i < nbytes; i++) { /* * We define a "printable character" to be one for which * isprint(3C) returns non-zero, isspace(3C) returns non-zero, * or a character which is either backspace or the bell. * Backspace and the bell are regrettably special because * they fail the first two tests -- and yet they are entirely * printable. These are the only two control characters that * have meaning for the terminal and for which isprint(3C) and * isspace(3C) return 0. */ if (isprint(c[i]) || isspace(c[i]) || c[i] == '\b' || c[i] == '\a') continue; if (c[i] == '\0' && i > 0) { /* * This looks like it might be a string. Before we * assume that it is indeed a string, check the * remainder of the byte range; if it contains * additional non-nul characters, we'll assume that * it's a binary stream that just happens to look like * a string, and we'll print out the individual bytes. */ for (j = i + 1; j < nbytes; j++) { if (c[j] != '\0') break; } if (j != nbytes) break; if (quiet) { return (dt_printf(dtp, fp, "%s", c)); } else { return (dt_printf(dtp, fp, " %s%*s", width < 0 ? " " : "", width, c)); } } break; } if (i == nbytes) { /* * The byte range is all printable characters, but there is * no trailing nul byte. We'll assume that it's a string and * print it as such. */ char *s = alloca(nbytes + 1); bcopy(c, s, nbytes); s[nbytes] = '\0'; return (dt_printf(dtp, fp, " %-*s", width, s)); } raw: if (dt_printf(dtp, fp, "\n%*s ", margin, "") < 0) return (-1); for (i = 0; i < 16; i++) if (dt_printf(dtp, fp, " %c", "0123456789abcdef"[i]) < 0) return (-1); if (dt_printf(dtp, fp, " 0123456789abcdef\n") < 0) return (-1); for (i = 0; i < nbytes; i += 16) { if (dt_printf(dtp, fp, "%*s%5x:", margin, "", i) < 0) return (-1); for (j = i; j < i + 16 && j < nbytes; j++) { if (dt_printf(dtp, fp, " %02x", (uchar_t)c[j]) < 0) return (-1); } while (j++ % 16) { if (dt_printf(dtp, fp, " ") < 0) return (-1); } if (dt_printf(dtp, fp, " ") < 0) return (-1); for (j = i; j < i + 16 && j < nbytes; j++) { if (dt_printf(dtp, fp, "%c", c[j] < ' ' || c[j] > '~' ? '.' : c[j]) < 0) return (-1); } if (dt_printf(dtp, fp, "\n") < 0) return (-1); } return (0); } int -dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, - caddr_t addr, int depth, int size) +dt_format_stack(dtrace_hdl_t *dtp, caddr_t addr, int depth, int size) { dtrace_syminfo_t dts; GElf_Sym sym; - int i, indent; - char c[PATH_MAX * 2]; + int i; 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; - + xo_open_list("stack-frames"); for (i = 0; i < depth; i++) { switch (size) { case sizeof (uint32_t): - /* LINTED - alignment */ pc = *((uint32_t *)addr); break; case sizeof (uint64_t): - /* LINTED - alignment */ pc = *((uint64_t *)addr); break; default: return (dt_set_errno(dtp, EDT_BADSTACKPC)); } if (pc == 0) break; addr += size; - if (dt_printf(dtp, fp, "%*s", indent, "") < 0) - return (-1); - + xo_open_instance("stack-frames"); if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { if (pc > sym.st_value) { - (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", + xo_emit("{:symbol/%s`%s+0x%llx} {:module/%s} " + "{:name/%s} {:offset/0x%llx}", + dts.dts_object, dts.dts_name, + (u_longlong_t)(pc - sym.st_value), dts.dts_object, dts.dts_name, (u_longlong_t)(pc - sym.st_value)); } else { - (void) snprintf(c, sizeof (c), "%s`%s", + xo_emit("{:symbol/%s`%s} {:module/%s} " + "{:name/%s}", + dts.dts_object, dts.dts_name, 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", + xo_emit("{:symbol/%s`0x%llx} {:module/%s} " + "{:offset/0x%llx}", + dts.dts_object, (u_longlong_t)pc, dts.dts_object, (u_longlong_t)pc); } else { - (void) snprintf(c, sizeof (c), "0x%llx", - (u_longlong_t)pc); + xo_emit("{:symbol/0x%llx} {:offset/0x%llx}", + (u_longlong_t)pc, (u_longlong_t)pc); } } - - if (dt_printf(dtp, fp, format, c) < 0) - return (-1); - - if (dt_printf(dtp, fp, "\n") < 0) - return (-1); + xo_close_instance("stack-frames"); } + xo_close_list("stack-frames"); return (0); } int -dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format, - caddr_t addr, uint64_t arg) +dt_format_ustack(dtrace_hdl_t *dtp, caddr_t addr, uint64_t arg) { - /* LINTED - alignment */ uint64_t *pc = (uint64_t *)addr; uint32_t depth = DTRACE_USTACK_NFRAMES(arg); uint32_t strsize = DTRACE_USTACK_STRSIZE(arg); const char *strbase = addr + (depth + 1) * sizeof (uint64_t); const char *str = strsize ? strbase : NULL; int err = 0; char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; struct ps_prochandle *P; GElf_Sym sym; int i, indent; pid_t pid; if (depth == 0) return (0); pid = (pid_t)*pc++; - if (dt_printf(dtp, fp, "\n") < 0) - return (-1); - - if (format == NULL) - format = "%s"; - - if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) - indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; - else - indent = _dtrace_stkindent; - /* * Ultimately, we need to add an entry point in the library vector for * determining from . For now, if * this is a vector open, we just print the raw address or string. */ if (dtp->dt_vector == NULL) P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); else P = NULL; if (P != NULL) dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ + xo_open_list("ustack-frames"); for (i = 0; i < depth && pc[i] != 0; i++) { const prmap_t *map; - if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) - break; - + xo_open_instance("ustack-frames"); 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, + xo_emit("{:symbol/%s`%s+0x%llx} {:module/%s} " + "{:name/%s} {:offset/0x%llx}", + dt_basename(objname), name, + (u_longlong_t)(pc[i] - sym.st_value), + dt_basename(objname), name, (u_longlong_t)(pc[i] - sym.st_value)); } else { - (void) snprintf(c, sizeof (c), - "%s`%s", dt_basename(objname), name); + xo_emit("{:symbol/%s`%s} {:module/%s} " + "{:name/%s}", + dt_basename(objname), name, + 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); + xo_emit("{:symbol/%s}", str); } else { if (P != NULL && Pobjname(P, pc[i], objname, sizeof (objname)) != 0) { - (void) snprintf(c, sizeof (c), "%s`0x%llx", + xo_emit("{:symbol/%s`0x%llx} {:module/%s} " + "{:offset/0x%llx}", + dt_basename(objname), (u_longlong_t)pc[i], dt_basename(objname), (u_longlong_t)pc[i]); } else { - (void) snprintf(c, sizeof (c), "0x%llx", - (u_longlong_t)pc[i]); + xo_emit("{:symbol/0x%llx} {:offset/0x%llx}", + (u_longlong_t)pc[i], (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; + xo_emit("{:annotation/%s}", &str[1]); } if (str != NULL) { str += strlen(str) + 1; if (str - strbase >= strsize) str = NULL; } + xo_close_instance("ustack-frames"); } + xo_close_list("ustack-frames"); if (P != NULL) { dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } return (err); } -static int -dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act) +int +dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, + caddr_t addr, int depth, int size) +{ + dtrace_syminfo_t dts; + GElf_Sym sym; + int i, indent; + char c[PATH_MAX * 2]; + uint64_t pc; + + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + + if (format == NULL) + format = "%s"; + + if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) + indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; + else + indent = _dtrace_stkindent; + + for (i = 0; i < depth; i++) { + switch (size) { + case sizeof (uint32_t): + /* LINTED - alignment */ + pc = *((uint32_t *)addr); + break; + + case sizeof (uint64_t): + /* LINTED - alignment */ + pc = *((uint64_t *)addr); + break; + + default: + return (dt_set_errno(dtp, EDT_BADSTACKPC)); + } + + if (pc == 0) + break; + + addr += size; + + if (dt_printf(dtp, fp, "%*s", indent, "") < 0) + return (-1); + + if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { + if (pc > sym.st_value) { + (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", + dts.dts_object, dts.dts_name, + (u_longlong_t)(pc - sym.st_value)); + } else { + (void) snprintf(c, sizeof (c), "%s`%s", + dts.dts_object, dts.dts_name); + } + } else { + /* + * We'll repeat the lookup, but this time we'll specify + * a NULL GElf_Sym -- indicating that we're only + * interested in the containing module. + */ + if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { + (void) snprintf(c, sizeof (c), "%s`0x%llx", + dts.dts_object, (u_longlong_t)pc); + } else { + (void) snprintf(c, sizeof (c), "0x%llx", + (u_longlong_t)pc); + } + } + + if (dt_printf(dtp, fp, format, c) < 0) + return (-1); + + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + } + + return (0); +} + +int +dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format, + caddr_t addr, uint64_t arg) +{ + /* LINTED - alignment */ + uint64_t *pc = (uint64_t *)addr; + uint32_t depth = DTRACE_USTACK_NFRAMES(arg); + uint32_t strsize = DTRACE_USTACK_STRSIZE(arg); + const char *strbase = addr + (depth + 1) * sizeof (uint64_t); + const char *str = strsize ? strbase : NULL; + int err = 0; + + char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; + struct ps_prochandle *P; + GElf_Sym sym; + int i, indent; + pid_t pid; + + if (depth == 0) + return (0); + + pid = (pid_t)*pc++; + + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + + if (format == NULL) + format = "%s"; + + if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) + indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; + else + indent = _dtrace_stkindent; + + /* + * Ultimately, we need to add an entry point in the library vector for + * determining from . For now, if + * this is a vector open, we just print the raw address or string. + */ + if (dtp->dt_vector == NULL) + P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); + else + P = NULL; + + if (P != NULL) + dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ + + for (i = 0; i < depth && pc[i] != 0; i++) { + const prmap_t *map; + + if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) + break; + + if (P != NULL && Plookup_by_addr(P, pc[i], + name, sizeof (name), &sym) == 0) { + (void) Pobjname(P, pc[i], objname, sizeof (objname)); + + if (pc[i] > sym.st_value) { + (void) snprintf(c, sizeof (c), + "%s`%s+0x%llx", dt_basename(objname), name, + (u_longlong_t)(pc[i] - sym.st_value)); + } else { + (void) snprintf(c, sizeof (c), + "%s`%s", dt_basename(objname), name); + } + } else if (str != NULL && str[0] != '\0' && str[0] != '@' && + (P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL || + (map->pr_mflags & MA_WRITE)))) { + /* + * If the current string pointer in the string table + * does not point to an empty string _and_ the program + * counter falls in a writable region, we'll use the + * string from the string table instead of the raw + * address. This last condition is necessary because + * some (broken) ustack helpers will return a string + * even for a program counter that they can't + * identify. If we have a string for a program + * counter that falls in a segment that isn't + * writable, we assume that we have fallen into this + * case and we refuse to use the string. + */ + (void) snprintf(c, sizeof (c), "%s", str); + } else { + if (P != NULL && Pobjname(P, pc[i], objname, + sizeof (objname)) != 0) { + (void) snprintf(c, sizeof (c), "%s`0x%llx", + dt_basename(objname), (u_longlong_t)pc[i]); + } else { + (void) snprintf(c, sizeof (c), "0x%llx", + (u_longlong_t)pc[i]); + } + } + + if ((err = dt_printf(dtp, fp, format, c)) < 0) + break; + + if ((err = dt_printf(dtp, fp, "\n")) < 0) + break; + + if (str != NULL && str[0] == '@') { + /* + * If the first character of the string is an "at" sign, + * then the string is inferred to be an annotation -- + * and it is printed out beneath the frame and offset + * with brackets. + */ + if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) + break; + + (void) snprintf(c, sizeof (c), " [ %s ]", &str[1]); + + if ((err = dt_printf(dtp, fp, format, c)) < 0) + break; + + if ((err = dt_printf(dtp, fp, "\n")) < 0) + break; + } + + if (str != NULL) { + str += strlen(str) + 1; + if (str - strbase >= strsize) + str = NULL; + } + } + + if (P != NULL) { + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); + } + + return (err); +} + +static int +dt_format_usym(dtrace_hdl_t *dtp, caddr_t addr, dtrace_actkind_t act) +{ + uint64_t pid = ((uint64_t *)addr)[0]; + uint64_t pc = ((uint64_t *)addr)[1]; + char *s; + int n, len = 256; + + if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) { + struct ps_prochandle *P; + + if ((P = dt_proc_grab(dtp, pid, + PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) { + GElf_Sym sym; + + dt_proc_lock(dtp, P); + + if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0) + pc = sym.st_value; + + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); + } + } + + do { + n = len; + s = alloca(n); + } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n); + + xo_emit("{:usym/%s}", s); + return (0); +} + + +static int +dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act) { /* LINTED - alignment */ uint64_t pid = ((uint64_t *)addr)[0]; /* LINTED - alignment */ uint64_t pc = ((uint64_t *)addr)[1]; const char *format = " %-50s"; char *s; int n, len = 256; if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) { struct ps_prochandle *P; if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) { GElf_Sym sym; dt_proc_lock(dtp, P); if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0) pc = sym.st_value; dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } } do { n = len; s = alloca(n); } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n); return (dt_printf(dtp, fp, format, s)); } +int +dt_format_umod(dtrace_hdl_t *dtp, caddr_t addr) +{ + uint64_t pid = ((uint64_t *)addr)[0]; + uint64_t pc = ((uint64_t *)addr)[1]; + int err = 0; + + char objname[PATH_MAX]; + struct ps_prochandle *P; + + /* + * See the comment in dt_print_ustack() for the rationale for + * printing raw addresses in the vectored case. + */ + if (dtp->dt_vector == NULL) + P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); + else + P = NULL; + + if (P != NULL) + dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ + + if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != 0) { + xo_emit("{:umod/%s}", dt_basename(objname)); + } else { + xo_emit("{:umod/0x%llx}", (u_longlong_t)pc); + } + + if (P != NULL) { + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); + } + + return (0); +} + int dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) { /* LINTED - alignment */ uint64_t pid = ((uint64_t *)addr)[0]; /* LINTED - alignment */ uint64_t pc = ((uint64_t *)addr)[1]; int err = 0; char objname[PATH_MAX], c[PATH_MAX * 2]; struct ps_prochandle *P; if (format == NULL) format = " %-50s"; /* * See the comment in dt_print_ustack() for the rationale for * printing raw addresses in the vectored case. */ if (dtp->dt_vector == NULL) P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); else P = NULL; if (P != NULL) dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != 0) { (void) snprintf(c, sizeof (c), "%s", dt_basename(objname)); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } err = dt_printf(dtp, fp, format, c); if (P != NULL) { dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } return (err); } 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); + if (dtp->dt_oformat) + xo_emit("{:sym/%s`%s} {:object/%s} {:name/%s}", + dts.dts_object, dts.dts_name, dts.dts_object, + dts.dts_name); + else + (void) snprintf(c, sizeof (c), "%s`%s", + dts.dts_object, dts.dts_name); } else { /* * We'll repeat the lookup, but this time we'll specify a * NULL GElf_Sym -- indicating that we're only interested in * the containing module. */ if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { - (void) snprintf(c, sizeof (c), "%s`0x%llx", - dts.dts_object, (u_longlong_t)pc); + if (dtp->dt_oformat) + xo_emit("{:sym/%s`0x%llx} {:object/%s} " + "{:offset/0x%llx}", + dts.dts_object, (u_longlong_t)pc, + dts.dts_object, (u_longlong_t)pc); + else + (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 (dtp->dt_oformat) + xo_emit("{:sym/0x%llx} {:offset/0x%llx}", + (u_longlong_t)pc, (u_longlong_t)pc); + else + (void) snprintf(c, sizeof (c), "0x%llx", + (u_longlong_t)pc); } } - if (dt_printf(dtp, fp, format, c) < 0) + if (dtp->dt_oformat != 0 && dt_printf(dtp, fp, format, c) < 0) return (-1); return (0); } +int +dt_format_mod(dtrace_hdl_t *dtp, caddr_t addr) +{ + /* LINTED - alignment */ + uint64_t pc = *((uint64_t *)addr); + dtrace_syminfo_t dts; + + if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { + xo_emit("{:mod/%s}", dts.dts_object); + } else { + xo_emit("{:mod/0x%llx}", (u_longlong_t)pc); + } + + 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); } +static char * +dt_format_bytes_get(dtrace_hdl_t *dtp, caddr_t addr, size_t nbytes) +{ + char *s = dt_alloc(dtp, nbytes * 2 + 2 + 1); /* 2 bytes per byte + 0x + '\0' */ + char t[6]; + char *c = (char *)addr; + size_t i, j; + + if (s == NULL) + return (NULL); + + /* + * XXX: Some duplication with dt_print_bytes(). + */ + for (i = 0; i < nbytes; i++) { + if (isprint(c[i]) || isspace(c[i]) || c[i] == '\b' || c[i] == '\a') + continue; + + if (c[i] == '\0' && i > 0) { + for (j = i + 1; j < nbytes; j++) { + if (c[j] != '\0') + break; + } + + if (j != nbytes) + break; + + memcpy(s, c, nbytes); + return (s); + } + + break; + } + + if (i == nbytes) { + memcpy(s, c, nbytes); + s[nbytes] = '\0'; + return (s); + } + + s[0] = '0'; + s[1] = 'x'; + for (i = 0; i < nbytes; i++) { + snprintf(t, sizeof(t), "%02x", (uchar_t)c[i]); + memcpy(s + (i * 2) + 2, t, 2); + } + + s[nbytes * 2 + 2] = 0; + return (s); +} + +static int +dt_format_memory(dtrace_hdl_t *dtp, caddr_t addr) +{ + + size_t nbytes = *((uintptr_t *) addr); + char *s; + + s = dt_format_bytes_get(dtp, addr + sizeof(uintptr_t), nbytes); + if (s == NULL) + return (-1); + + xo_emit("{:printm/%s}", s); + dt_free(dtp, s); + + return (0); +} + static int dt_print_memory(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) { int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); size_t nbytes = *((uintptr_t *) addr); return (dt_print_bytes(dtp, fp, addr + sizeof(uintptr_t), nbytes, 50, quiet, 1)); } typedef struct dt_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; + /* + * 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_format_datum(dtrace_hdl_t *dtp, dtrace_recdesc_t *rec, caddr_t addr, + size_t size, const dtrace_aggdata_t *aggdata, uint64_t normal, + dt_print_aggdata_t *pd) +{ + dtrace_actkind_t act = rec->dtrd_action; + boolean_t packed = pd->dtpa_agghist || pd->dtpa_aggpack; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + char fmt[512]; + char *s; + + if (packed && pd->dtpa_agghisthdr != agg->dtagd_varid) + pd->dtpa_agghisthdr = agg->dtagd_varid; + + switch (act) { + case DTRACEACT_STACK: + return (dt_format_stack(dtp, addr, rec->dtrd_arg, + rec->dtrd_size / rec->dtrd_arg)); + + case DTRACEACT_USTACK: + case DTRACEACT_JSTACK: + return (dt_format_ustack(dtp, addr, rec->dtrd_arg)); + + case DTRACEACT_USYM: + case DTRACEACT_UADDR: + return (dt_format_usym(dtp, addr, act)); + + case DTRACEACT_UMOD: + return (dt_format_umod(dtp, addr)); + + case DTRACEACT_SYM: + return (dt_format_sym(dtp, addr)); + case DTRACEACT_MOD: + return (dt_format_mod(dtp, addr)); - if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) - return (dt_set_errno(dtp, EDT_BADTRUNC)); + case DTRACEAGG_QUANTIZE: + return (dt_format_quantize(dtp, addr, size, normal)); - /* LINTED - alignment */ - trunc.dttd_id = *((dtrace_aggvarid_t *)addr); - rec++; + case DTRACEAGG_LQUANTIZE: + return (dt_format_lquantize(dtp, addr, size, normal)); - if (rec->dtrd_action != DTRACEACT_LIBACT) - return (dt_set_errno(dtp, EDT_BADTRUNC)); + case DTRACEAGG_LLQUANTIZE: + return (dt_format_llquantize(dtp, addr, size, normal)); - if (rec->dtrd_arg != DT_ACT_TRUNC) - return (dt_set_errno(dtp, EDT_BADTRUNC)); + case DTRACEAGG_AVG: + return (dt_format_average(dtp, addr, size, normal)); - addr = base + rec->dtrd_offset; + case DTRACEAGG_STDDEV: + return (dt_format_stddev(dtp, addr, size, normal)); - switch (rec->dtrd_size) { + default: + break; + } + + switch (size) { case sizeof (uint64_t): - /* LINTED - alignment */ - remaining = *((int64_t *)addr); + snprintf(fmt, sizeof(fmt), "{:%s/%%lld}", pd->dtpa_keyname); + xo_emit(fmt, (long long)*((uint64_t *)addr) / normal); break; case sizeof (uint32_t): - /* LINTED - alignment */ - remaining = *((int32_t *)addr); + snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); + xo_emit(fmt, *((uint32_t *)addr) / (uint32_t)normal); break; case sizeof (uint16_t): - /* LINTED - alignment */ - remaining = *((int16_t *)addr); + snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); + xo_emit(fmt, *((uint16_t *)addr) / (uint32_t)normal); break; case sizeof (uint8_t): - remaining = *((int8_t *)addr); + snprintf(fmt, sizeof(fmt), "{:%s/%%d}", pd->dtpa_keyname); + xo_emit(fmt, *((uint8_t *)addr) / (uint32_t)normal); break; default: - return (dt_set_errno(dtp, EDT_BADNORMAL)); - } + s = dt_format_bytes_get(dtp, addr, size); + if (s == NULL) + return (-1); - if (remaining < 0) { - func = dtrace_aggregate_walk_valsorted; - remaining = -remaining; - } else { - func = dtrace_aggregate_walk_valrevsorted; + xo_emit("{:value/%s}", s); + dt_free(dtp, s); + break; } - 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, const dtrace_aggdata_t *aggdata, uint64_t normal, dt_print_aggdata_t *pd) { int err, width; dtrace_actkind_t act = rec->dtrd_action; boolean_t packed = pd->dtpa_agghist || pd->dtpa_aggpack; dtrace_aggdesc_t *agg = aggdata->dtada_desc; static struct { size_t size; int width; int packedwidth; } *fmt, fmttab[] = { { sizeof (uint8_t), 3, 3 }, { sizeof (uint16_t), 5, 5 }, { sizeof (uint32_t), 8, 8 }, { sizeof (uint64_t), 16, 16 }, { 0, -50, 16 } }; if (packed && pd->dtpa_agghisthdr != agg->dtagd_varid) { dtrace_recdesc_t *r; width = 0; /* * To print our quantization header for either an agghist or * aggpack aggregation, we need to iterate through all of our * of our records to determine their width. */ for (r = rec; !DTRACEACT_ISAGG(r->dtrd_action); r++) { for (fmt = fmttab; fmt->size && fmt->size != r->dtrd_size; fmt++) continue; width += fmt->packedwidth + 1; } if (pd->dtpa_agghist) { if (dt_print_quanthdr(dtp, fp, width) < 0) return (-1); } else { if (dt_print_quanthdr_packed(dtp, fp, width, aggdata, r->dtrd_action) < 0) return (-1); } pd->dtpa_agghisthdr = agg->dtagd_varid; } if (pd->dtpa_agghist && DTRACEACT_ISAGG(act)) { char positives = aggdata->dtada_flags & DTRACE_A_HASPOSITIVES; char negatives = aggdata->dtada_flags & DTRACE_A_HASNEGATIVES; int64_t val; assert(act == DTRACEAGG_SUM || act == DTRACEAGG_COUNT); val = (long long)*((uint64_t *)addr); if (dt_printf(dtp, fp, " ") < 0) return (-1); return (dt_print_quantline(dtp, fp, val, normal, aggdata->dtada_total, positives, negatives)); } if (pd->dtpa_aggpack && DTRACEACT_ISAGG(act)) { switch (act) { case DTRACEAGG_QUANTIZE: return (dt_print_quantize_packed(dtp, fp, addr, size, aggdata)); case DTRACEAGG_LQUANTIZE: return (dt_print_lquantize_packed(dtp, fp, addr, size, aggdata)); default: break; } } switch (act) { case DTRACEACT_STACK: return (dt_print_stack(dtp, fp, NULL, addr, rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg)); case DTRACEACT_USTACK: case DTRACEACT_JSTACK: return (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg)); case DTRACEACT_USYM: case DTRACEACT_UADDR: return (dt_print_usym(dtp, fp, addr, act)); case DTRACEACT_UMOD: return (dt_print_umod(dtp, fp, NULL, addr)); case DTRACEACT_SYM: return (dt_print_sym(dtp, fp, NULL, addr)); case DTRACEACT_MOD: return (dt_print_mod(dtp, fp, NULL, addr)); case DTRACEAGG_QUANTIZE: return (dt_print_quantize(dtp, fp, addr, size, normal)); case DTRACEAGG_LQUANTIZE: return (dt_print_lquantize(dtp, fp, addr, size, normal)); case DTRACEAGG_LLQUANTIZE: return (dt_print_llquantize(dtp, fp, addr, size, normal)); case DTRACEAGG_AVG: return (dt_print_average(dtp, fp, addr, size, normal)); case DTRACEAGG_STDDEV: return (dt_print_stddev(dtp, fp, addr, size, normal)); default: break; } for (fmt = fmttab; fmt->size && fmt->size != size; fmt++) continue; width = packed ? fmt->packedwidth : fmt->width; switch (size) { case sizeof (uint64_t): err = dt_printf(dtp, fp, " %*lld", width, /* LINTED - alignment */ (long long)*((uint64_t *)addr) / normal); break; case sizeof (uint32_t): /* LINTED - alignment */ err = dt_printf(dtp, fp, " %*d", width, *((uint32_t *)addr) / (uint32_t)normal); break; case sizeof (uint16_t): /* LINTED - alignment */ err = dt_printf(dtp, fp, " %*d", width, *((uint16_t *)addr) / (uint32_t)normal); break; case sizeof (uint8_t): err = dt_printf(dtp, fp, " %*d", width, *((uint8_t *)addr) / (uint32_t)normal); break; default: err = dt_print_bytes(dtp, fp, addr, size, width, 0, 0); break; } return (err); } +int +dt_format_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; + dtrace_hdl_t *dtp = pd->dtpa_dtp; + dtrace_recdesc_t *rec; + dtrace_actkind_t act; + caddr_t addr; + size_t size; + + if (pd->dtpa_aggname == NULL) + pd->dtpa_aggname = agg->dtagd_name; + + xo_open_instance("aggregation-data"); + strcpy(pd->dtpa_keyname, "value"); + xo_open_list("keys"); + + /* + * 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; + } + + xo_open_instance("keys"); + if (dt_format_datum(dtp, rec, addr, + size, aggdata, 1, pd) < 0) { + xo_close_instance("keys"); + xo_close_instance("aggregation-data"); + return (-1); + } + xo_close_instance("keys"); + + if (dt_buffered_flush(dtp, NULL, rec, aggdata, + DTRACE_BUFDATA_AGGKEY) < 0) { + xo_close_instance("aggregation-data"); + return (-1); + } + } + xo_close_list("keys"); + + 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)); + + switch (act) { + case DTRACEAGG_MIN: + strcpy(pd->dtpa_keyname, "min"); + break; + case DTRACEAGG_MAX: + strcpy(pd->dtpa_keyname, "max"); + break; + case DTRACEAGG_COUNT: + strcpy(pd->dtpa_keyname, "count"); + break; + case DTRACEAGG_SUM: + strcpy(pd->dtpa_keyname, "sum"); + break; + default: + strcpy(pd->dtpa_keyname, "UNKNOWN"); + break; + } + + normal = aggdata->dtada_normal; + + if (dt_format_datum(dtp, rec, addr, size, + aggdata, normal, pd) < 0) { + xo_close_instance("aggregation-data"); + return (-1); + } + + if (dt_buffered_flush(dtp, NULL, rec, aggdata, + DTRACE_BUFDATA_AGGVAL) < 0) { + xo_close_instance("aggregation-data"); + return (-1); + } + + if (!pd->dtpa_allunprint) + agg->dtagd_flags |= DTRACE_AGD_PRINTED; + } + + if (dt_buffered_flush(dtp, NULL, NULL, aggdata, + DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) { + xo_close_instance("aggregation-data"); + return (-1); + } + + xo_close_instance("aggregation-data"); + return (0); +} + int dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) { int i, aggact = 0; dt_print_aggdata_t *pd = arg; const dtrace_aggdata_t *aggdata = aggsdata[0]; dtrace_aggdesc_t *agg = aggdata->dtada_desc; FILE *fp = pd->dtpa_fp; dtrace_hdl_t *dtp = pd->dtpa_dtp; dtrace_recdesc_t *rec; dtrace_actkind_t act; caddr_t addr; size_t size; pd->dtpa_agghist = (aggdata->dtada_flags & DTRACE_A_TOTAL); pd->dtpa_aggpack = (aggdata->dtada_flags & DTRACE_A_MINMAXBIN); /* * Iterate over each record description in the key, printing the traced * data, skipping the first datum (the tuple member created by the * compiler). */ for (i = 1; i < agg->dtagd_nrecs; i++) { rec = &agg->dtagd_rec[i]; act = rec->dtrd_action; addr = aggdata->dtada_data + rec->dtrd_offset; size = rec->dtrd_size; if (DTRACEACT_ISAGG(act)) { aggact = i; break; } if (dt_print_datum(dtp, fp, rec, addr, size, aggdata, 1, pd) < 0) return (-1); if (dt_buffered_flush(dtp, NULL, rec, aggdata, DTRACE_BUFDATA_AGGKEY) < 0) return (-1); } assert(aggact != 0); for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) { uint64_t normal; aggdata = aggsdata[i]; agg = aggdata->dtada_desc; rec = &agg->dtagd_rec[aggact]; act = rec->dtrd_action; addr = aggdata->dtada_data + rec->dtrd_offset; size = rec->dtrd_size; assert(DTRACEACT_ISAGG(act)); normal = aggdata->dtada_normal; if (dt_print_datum(dtp, fp, rec, addr, size, aggdata, normal, pd) < 0) return (-1); if (dt_buffered_flush(dtp, NULL, rec, aggdata, DTRACE_BUFDATA_AGGVAL) < 0) return (-1); if (!pd->dtpa_allunprint) agg->dtagd_flags |= DTRACE_AGD_PRINTED; } if (!pd->dtpa_agghist && !pd->dtpa_aggpack) { if (dt_printf(dtp, fp, "\n") < 0) return (-1); } if (dt_buffered_flush(dtp, NULL, NULL, aggdata, DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) return (-1); return (0); } +int +dt_format_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_format_aggs(&aggdata, 1, arg)); +} + int dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) { dt_print_aggdata_t *pd = arg; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t aggvarid = pd->dtpa_id; if (pd->dtpa_allunprint) { if (agg->dtagd_flags & DTRACE_AGD_PRINTED) return (0); } else { /* * If we're not printing all unprinted aggregations, then the * aggregation variable ID denotes a specific aggregation * variable that we should print -- skip any other aggregations * that we encounter. */ if (agg->dtagd_nrecs == 0) return (0); if (aggvarid != agg->dtagd_varid) return (0); } return (dt_print_aggs(&aggdata, 1, arg)); } int dt_setopt(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, const char *option, const char *value) { int len, rval; char *msg; const char *errstr; dtrace_setoptdata_t optdata; bzero(&optdata, sizeof (optdata)); (void) dtrace_getopt(dtp, option, &optdata.dtsda_oldval); if (dtrace_setopt(dtp, option, value) == 0) { (void) dtrace_getopt(dtp, option, &optdata.dtsda_newval); optdata.dtsda_probe = data; optdata.dtsda_option = option; optdata.dtsda_handle = dtp; if ((rval = dt_handle_setopt(dtp, &optdata)) != 0) return (rval); return (0); } errstr = dtrace_errmsg(dtp, dtrace_errno(dtp)); len = strlen(option) + strlen(value) + strlen(errstr) + 80; msg = alloca(len); (void) snprintf(msg, len, "couldn't set option \"%s\" to \"%s\": %s\n", option, value, errstr); if ((rval = dt_handle_liberr(dtp, data, msg)) == 0) return (0); return (rval); } +/* + * Helper functions to help maintain style(9) in dt_consume_cpu(). + */ +static int +dt_oformat_agg_sorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, + dt_print_aggdata_t *pd) +{ + int r; + + r = dtrace_aggregate_walk_sorted(dtp, dt_format_agg, pd); + if (r < 0) { + xo_close_list("aggregation-data"); + xo_emit("{:aggregation-name/%s}", pd->dtpa_aggname); + xo_close_instance("output"); + } + + return (r); +} + +static void +dt_oformat_agg_name(dt_print_aggdata_t *pd) +{ + + xo_close_list("aggregation-data"); + xo_emit("{:aggregation-name/%s}", pd->dtpa_aggname); +} + static int dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, dtrace_bufdesc_t *buf, boolean_t just_one, dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) { dtrace_epid_t id; size_t offs; int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); int rval, i, n; uint64_t tracememsize = 0; dtrace_probedata_t data; uint64_t drops; + size_t skip_format; bzero(&data, sizeof (data)); data.dtpda_handle = dtp; data.dtpda_cpu = cpu; data.dtpda_flow = dtp->dt_flow; data.dtpda_indent = dtp->dt_indent; data.dtpda_prefix = dtp->dt_prefix; for (offs = buf->dtbd_oldest; offs < buf->dtbd_size; ) { dtrace_eprobedesc_t *epd; /* * We're guaranteed to have an ID. */ id = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); if (id == DTRACE_EPIDNONE) { /* * This is filler to assure proper alignment of the * next record; we simply ignore it. */ offs += sizeof (id); continue; } if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc, &data.dtpda_pdesc)) != 0) return (rval); epd = data.dtpda_edesc; data.dtpda_data = buf->dtbd_data + offs; + data.dtpda_timestamp = DTRACE_RECORD_LOAD_TIMESTAMP( + (struct dtrace_rechdr *)data.dtpda_data); if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) { rval = dt_handle(dtp, &data); if (rval == DTRACE_CONSUME_NEXT) goto nextepid; if (rval == DTRACE_CONSUME_ERROR) return (-1); } if (flow) (void) dt_flowindent(dtp, &data, dtp->dt_last_epid, buf, offs); + if (dtp->dt_oformat) + xo_open_instance("probes"); 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)); + skip_format = 0; + if (dtp->dt_oformat) + xo_open_list("output"); for (i = 0; i < epd->dtepd_nrecs; i++) { caddr_t addr; dtrace_recdesc_t *rec = &epd->dtepd_rec[i]; dtrace_actkind_t act = rec->dtrd_action; + if (skip_format > 0) + skip_format--; + data.dtpda_data = buf->dtbd_data + offs + rec->dtrd_offset; addr = data.dtpda_data; if (act == DTRACEACT_LIBACT) { uint64_t arg = rec->dtrd_arg; dtrace_aggvarid_t id; switch (arg) { case DT_ACT_CLEAR: /* LINTED - alignment */ id = *((dtrace_aggvarid_t *)addr); (void) dtrace_aggregate_walk(dtp, dt_clear_agg, &id); continue; case DT_ACT_DENORMALIZE: /* LINTED - alignment */ id = *((dtrace_aggvarid_t *)addr); (void) dtrace_aggregate_walk(dtp, dt_denormalize_agg, &id); continue; case DT_ACT_FTRUNCATE: if (fp == NULL) continue; (void) fflush(fp); (void) ftruncate(fileno(fp), 0); (void) fseeko(fp, 0, SEEK_SET); continue; case DT_ACT_NORMALIZE: if (i == epd->dtepd_nrecs - 1) return (dt_set_errno(dtp, EDT_BADNORMAL)); if (dt_normalize(dtp, buf->dtbd_data + offs, rec) != 0) return (-1); i++; continue; case DT_ACT_SETOPT: { uint64_t *opts = dtp->dt_options; dtrace_recdesc_t *valrec; uint32_t valsize; caddr_t val; int rv; if (i == epd->dtepd_nrecs - 1) { return (dt_set_errno(dtp, EDT_BADSETOPT)); } valrec = &epd->dtepd_rec[++i]; valsize = valrec->dtrd_size; if (valrec->dtrd_action != act || valrec->dtrd_arg != arg) { return (dt_set_errno(dtp, EDT_BADSETOPT)); } if (valsize > sizeof (uint64_t)) { val = buf->dtbd_data + offs + valrec->dtrd_offset; } else { val = "1"; } rv = dt_setopt(dtp, &data, addr, val); if (rv != 0) return (-1); flow = (opts[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); quiet = (opts[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); continue; } case DT_ACT_TRUNC: if (i == epd->dtepd_nrecs - 1) return (dt_set_errno(dtp, EDT_BADTRUNC)); if (dt_trunc(dtp, buf->dtbd_data + offs, rec) != 0) return (-1); i++; continue; default: continue; } } if (act == DTRACEACT_TRACEMEM_DYNSIZE && rec->dtrd_size == sizeof (uint64_t)) { /* LINTED - alignment */ tracememsize = *((unsigned long long *)addr); continue; } rval = (*rfunc)(&data, rec, arg); if (rval == DTRACE_CONSUME_NEXT) continue; if (rval == DTRACE_CONSUME_ABORT) return (dt_set_errno(dtp, EDT_DIRABORT)); if (rval != DTRACE_CONSUME_THIS) return (dt_set_errno(dtp, EDT_BADRVAL)); + if (dtp->dt_oformat && rec->dtrd_size > 0) + xo_open_instance("output"); if (act == DTRACEACT_STACK) { int depth = rec->dtrd_arg; - if (dt_print_stack(dtp, fp, NULL, addr, depth, - rec->dtrd_size / depth) < 0) + if (dtp->dt_oformat) { + if (dt_format_stack(dtp, addr, depth, + rec->dtrd_size / depth) < 0) { + xo_close_instance("output"); + return (-1); + } + } else { + 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); + if (dtp->dt_oformat) { + if (dt_format_ustack(dtp, addr, + rec->dtrd_arg) < 0) { + xo_close_instance("output"); + return (-1); + } + } else { + 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); + if (dtp->dt_oformat) { + if (dt_format_sym(dtp, addr) < 0) { + xo_close_instance("output"); + return (-1); + } + } else { + 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); + if (dtp->dt_oformat) { + if (dt_format_mod(dtp, addr) < 0) { + xo_close_instance("output"); + return (-1); + } + } else { + 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); + if (dtp->dt_oformat) { + if (dt_format_usym(dtp, addr, act) < 0) { + xo_close_instance("output"); + return (-1); + } + } else { + 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); + if (dtp->dt_oformat) { + if (dt_format_umod(dtp, addr) < 0) { + xo_close_instance("output"); + return (-1); + } + } else { + if (dt_print_umod(dtp, fp, NULL, addr) < 0) + return (-1); + } goto nextrec; } if (act == DTRACEACT_PRINTM) { - if (dt_print_memory(dtp, fp, addr) < 0) - return (-1); + if (dtp->dt_oformat) { + if (dt_format_memory(dtp, addr) < 0) { + xo_close_instance("output"); + return (-1); + } + } else { + if (dt_print_memory(dtp, fp, addr) < 0) + return (-1); + } goto nextrec; } - if (DTRACEACT_ISPRINTFLIKE(act)) { + if (dtp->dt_oformat == DTRACE_OFORMAT_TEXT && + DTRACEACT_ISPRINTFLIKE(act)) { void *fmtdata; int (*func)(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *buf, size_t); if ((fmtdata = dt_format_lookup(dtp, rec->dtrd_format)) == NULL) goto nofmt; switch (act) { case DTRACEACT_PRINTF: func = dtrace_fprintf; break; case DTRACEACT_PRINTA: func = dtrace_fprinta; break; case DTRACEACT_SYSTEM: func = dtrace_system; break; case DTRACEACT_FREOPEN: func = dtrace_freopen; break; } n = (*func)(dtp, fp, fmtdata, &data, rec, epd->dtepd_nrecs - i, (uchar_t *)buf->dtbd_data + offs, buf->dtbd_size - offs); if (n < 0) return (-1); /* errno is set for us */ if (n > 0) i += n - 1; goto nextrec; } + /* + * We don't care about a formatted printa, system or + * freopen for oformat. + */ + if (dtp->dt_oformat && act == DTRACEACT_PRINTF && + skip_format == 0) { + void *fmtdata; + if ((fmtdata = dt_format_lookup(dtp, + rec->dtrd_format)) == NULL) + goto nofmt; + + n = dtrace_sprintf(dtp, fp, fmtdata, rec, + epd->dtepd_nrecs - i, + (uchar_t *)buf->dtbd_data + offs, + buf->dtbd_size - offs); + + if (n < 0) { + xo_close_instance("output"); + return (-1); /* errno is set for us */ + } + + xo_emit("{:message/%s}", dtp->dt_sprintf_buf); + skip_format += n; + + /* + * We want the "message" object to be its own + * thing, but we still want to process the + * current DIFEXPR in case there is a value + * attached to it. If there is, we need to + * re-open a new output instance, as otherwise + * the message ends up bundled with the first + * value. + * + * XXX: There is an edge case where a + * printf("hello"); will produce a DIFO that + * returns 0 attached to it and we have no good + * way to determine if this 0 value is because + * there's no real data attached to the printf + * as an argument, or it's because the argument + * actually returns 0. + */ + if (skip_format == 0) + goto nextrec; + + xo_close_instance("output"); + xo_open_instance("output"); + } + /* * If this is a DIF expression, and the record has a * format set, this indicates we have a CTF type name * associated with the data and we should try to print * it out by type. */ if (act == DTRACEACT_DIFEXPR) { const char *strdata = dt_strdata_lookup(dtp, rec->dtrd_format); if (strdata != NULL) { - n = dtrace_print(dtp, fp, strdata, - addr, rec->dtrd_size); + if (dtp->dt_oformat) + n = dtrace_format_print(dtp, fp, + strdata, addr, + rec->dtrd_size); + else + n = dtrace_print(dtp, fp, + strdata, addr, + rec->dtrd_size); /* * dtrace_print() will return -1 on * error, or return the number of bytes * consumed. It will return 0 if the * type couldn't be determined, and we * should fall through to the normal * trace method. */ - if (n < 0) + if (n < 0) { + if (dtp->dt_oformat) + xo_close_instance( + "output"); return (-1); + } if (n > 0) goto nextrec; } } nofmt: if (act == DTRACEACT_PRINTA) { dt_print_aggdata_t pd; dtrace_aggvarid_t *aggvars; int j, naggvars = 0; size_t size = ((epd->dtepd_nrecs - i) * sizeof (dtrace_aggvarid_t)); - if ((aggvars = dt_alloc(dtp, size)) == NULL) + if ((aggvars = dt_alloc(dtp, size)) == NULL) { + if (dtp->dt_oformat) + xo_close_instance("output"); 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) { + if (dtp->dt_oformat) + xo_close_instance( + "output"); 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 (dtp->dt_oformat) + xo_open_list("aggregation-data"); 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); + if (dtp->dt_oformat) { + n = dt_oformat_agg_sorted(dtp, + dt_format_agg, &pd); + if (n < 0) + return (-1); + } else { + if (dt_printf(dtp, fp, "\n") < 0 || + dtrace_aggregate_walk_sorted(dtp, + dt_print_agg, &pd) < 0) + return (-1); + } + + if (dtp->dt_oformat) + dt_oformat_agg_name(&pd); 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); + if (dtp->dt_oformat) { + if (dtrace_aggregate_walk_joined(dtp, + aggvars, naggvars, + dt_format_aggs, &pd) < 0) { + dt_oformat_agg_name(&pd); + xo_close_instance("output"); + dt_free(dtp, aggvars); + return (-1); + } + } else { + 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); + } } + if (dtp->dt_oformat) + dt_oformat_agg_name(&pd); dt_free(dtp, aggvars); goto nextrec; } if (act == DTRACEACT_TRACEMEM) { if (tracememsize == 0 || tracememsize > rec->dtrd_size) { tracememsize = rec->dtrd_size; } - n = dt_print_bytes(dtp, fp, addr, - tracememsize, -33, quiet, 1); + if (dtp->dt_oformat) { + char *s; + + s = dt_format_bytes_get(dtp, addr, + tracememsize); + n = xo_emit("{:tracemem/%s}", s); + dt_free(dtp, s); + } else { + n = dt_print_bytes(dtp, fp, addr, + tracememsize, -33, quiet, 1); + } tracememsize = 0; if (n < 0) return (-1); goto nextrec; } switch (rec->dtrd_size) { case sizeof (uint64_t): - n = dt_printf(dtp, fp, - quiet ? "%lld" : " %16lld", - /* LINTED - alignment */ - *((unsigned long long *)addr)); + if (dtp->dt_oformat) { + xo_emit("{:value/%lld}", + *((unsigned long long *)addr)); + n = 0; + } else + 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)); + if (dtp->dt_oformat) { + xo_emit("{:value/%d}", + *((uint32_t *)addr)); + n = 0; + } else + 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)); + if (dtp->dt_oformat) { + xo_emit("{:value/%d}", + *((uint16_t *)addr)); + n = 0; + } else + 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)); + if (dtp->dt_oformat) { + xo_emit("{:value/%d}", + *((uint8_t *)addr)); + n = 0; + } else + n = dt_printf(dtp, fp, + quiet ? "%d" : " %3d", + *((uint8_t *)addr)); break; default: - n = dt_print_bytes(dtp, fp, addr, - rec->dtrd_size, -33, quiet, 0); + if (dtp->dt_oformat && rec->dtrd_size > 0) { + char *s; + + s = dt_format_bytes_get(dtp, addr, + rec->dtrd_size); + xo_emit("{:value/%s}", s); + dt_free(dtp, s); + n = 0; + } else { + n = dt_print_bytes(dtp, fp, addr, + rec->dtrd_size, -33, quiet, 0); + } break; } + if (dtp->dt_oformat && rec->dtrd_size > 0) + xo_close_instance("output"); + if (n < 0) return (-1); /* errno is set for us */ nextrec: if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) return (-1); /* errno is set for us */ } /* * Call the record callback with a NULL record to indicate * that we're done processing this EPID. */ rval = (*rfunc)(&data, NULL, arg); nextepid: offs += epd->dtepd_size; dtp->dt_last_epid = id; + + if (dtp->dt_oformat) { + xo_close_list("output"); + xo_close_instance("probes"); + xo_flush(); + } if (just_one) { buf->dtbd_oldest = offs; break; } } dtp->dt_flow = data.dtpda_flow; dtp->dt_indent = data.dtpda_indent; dtp->dt_prefix = data.dtpda_prefix; if ((drops = buf->dtbd_drops) == 0) return (0); /* * Explicitly zero the drops to prevent us from processing them again. */ buf->dtbd_drops = 0; - return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops)); + xo_open_instance("probes"); + dt_oformat_drop(dtp, cpu); + rval = dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops); + xo_close_instance("probes"); + + return (rval); } /* * Reduce memory usage by shrinking the buffer if it's no more than half full. * Note, we need to preserve the alignment of the data at dtbd_oldest, which is * only 4-byte aligned. */ static void dt_realloc_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf, int cursize) { uint64_t used = buf->dtbd_size - buf->dtbd_oldest; if (used < cursize / 2) { int misalign = buf->dtbd_oldest & (sizeof (uint64_t) - 1); char *newdata = dt_alloc(dtp, used + misalign); if (newdata == NULL) return; bzero(newdata, misalign); bcopy(buf->dtbd_data + buf->dtbd_oldest, newdata + misalign, used); dt_free(dtp, buf->dtbd_data); buf->dtbd_oldest = misalign; buf->dtbd_size = used + misalign; buf->dtbd_data = newdata; } } /* * If the ring buffer has wrapped, the data is not in order. Rearrange it * so that it is. Note, we need to preserve the alignment of the data at * dtbd_oldest, which is only 4-byte aligned. */ static int dt_unring_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf) { int misalign; char *newdata, *ndp; if (buf->dtbd_oldest == 0) return (0); misalign = buf->dtbd_oldest & (sizeof (uint64_t) - 1); newdata = ndp = dt_alloc(dtp, buf->dtbd_size + misalign); if (newdata == NULL) return (-1); assert(0 == (buf->dtbd_size & (sizeof (uint64_t) - 1))); bzero(ndp, misalign); ndp += misalign; bcopy(buf->dtbd_data + buf->dtbd_oldest, ndp, buf->dtbd_size - buf->dtbd_oldest); ndp += buf->dtbd_size - buf->dtbd_oldest; bcopy(buf->dtbd_data, ndp, buf->dtbd_oldest); dt_free(dtp, buf->dtbd_data); buf->dtbd_oldest = misalign; buf->dtbd_data = newdata; buf->dtbd_size += misalign; return (0); } static void dt_put_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf) { dt_free(dtp, buf->dtbd_data); dt_free(dtp, buf); } /* * Returns 0 on success, in which case *cbp will be filled in if we retrieved * data, or NULL if there is no data for this CPU. * Returns -1 on failure and sets dt_errno. */ static int dt_get_buf(dtrace_hdl_t *dtp, int cpu, dtrace_bufdesc_t **bufp) { dtrace_optval_t size; dtrace_bufdesc_t *buf = dt_zalloc(dtp, sizeof (*buf)); int error, rval; if (buf == NULL) return (-1); (void) dtrace_getopt(dtp, "bufsize", &size); buf->dtbd_data = dt_alloc(dtp, size); if (buf->dtbd_data == NULL) { dt_free(dtp, buf); return (-1); } buf->dtbd_size = size; buf->dtbd_cpu = cpu; #ifdef illumos if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { #else if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &buf) == -1) { #endif /* * If we failed with ENOENT, it may be because the * CPU was unconfigured -- this is okay. Any other * error, however, is unexpected. */ if (errno == ENOENT) { *bufp = NULL; rval = 0; } else rval = dt_set_errno(dtp, errno); dt_put_buf(dtp, buf); return (rval); } error = dt_unring_buf(dtp, buf); if (error != 0) { dt_put_buf(dtp, buf); return (error); } dt_realloc_buf(dtp, buf, size); *bufp = buf; return (0); } typedef struct dt_begin { dtrace_consume_probe_f *dtbgn_probefunc; dtrace_consume_rec_f *dtbgn_recfunc; void *dtbgn_arg; dtrace_handle_err_f *dtbgn_errhdlr; void *dtbgn_errarg; int dtbgn_beginonly; } dt_begin_t; static int dt_consume_begin_probe(const dtrace_probedata_t *data, void *arg) { dt_begin_t *begin = arg; dtrace_probedesc_t *pd = data->dtpda_pdesc; int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); if (begin->dtbgn_beginonly) { if (!(r1 && r2)) return (DTRACE_CONSUME_NEXT); } else { if (r1 && r2) return (DTRACE_CONSUME_NEXT); } /* * We have a record that we're interested in. Now call the underlying * probe function... */ return (begin->dtbgn_probefunc(data, begin->dtbgn_arg)); } static int dt_consume_begin_record(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) { dt_begin_t *begin = arg; return (begin->dtbgn_recfunc(data, rec, begin->dtbgn_arg)); } static int dt_consume_begin_error(const dtrace_errdata_t *data, void *arg) { dt_begin_t *begin = (dt_begin_t *)arg; dtrace_probedesc_t *pd = data->dteda_pdesc; int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); if (begin->dtbgn_beginonly) { if (!(r1 && r2)) return (DTRACE_HANDLE_OK); } else { if (r1 && r2) return (DTRACE_HANDLE_OK); } return (begin->dtbgn_errhdlr(data, begin->dtbgn_errarg)); } static int dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) { /* * There's this idea that the BEGIN probe should be processed before * everything else, and that the END probe should be processed after * anything else. In the common case, this is pretty easy to deal * with. However, a situation may arise where the BEGIN enabling and * END enabling are on the same CPU, and some enabling in the middle * occurred on a different CPU. To deal with this (blech!) we need to * consume the BEGIN buffer up until the end of the BEGIN probe, and * then set it aside. We will then process every other CPU, and then * we'll return to the BEGIN CPU and process the rest of the data * (which will inevitably include the END probe, if any). Making this * even more complicated (!) is the library's ERROR enabling. Because * this enabling is processed before we even get into the consume call * back, any ERROR firing would result in the library's ERROR enabling * being processed twice -- once in our first pass (for BEGIN probes), * and again in our second pass (for everything but BEGIN probes). To * deal with this, we interpose on the ERROR handler to assure that we * only process ERROR enablings induced by BEGIN enablings in the * first pass, and that we only process ERROR enablings _not_ induced * by BEGIN enablings in the second pass. */ dt_begin_t begin; processorid_t cpu = dtp->dt_beganon; int rval, i; static int max_ncpus; dtrace_bufdesc_t *buf; dtp->dt_beganon = -1; if (dt_get_buf(dtp, cpu, &buf) != 0) return (-1); if (buf == NULL) return (0); if (!dtp->dt_stopped || buf->dtbd_cpu != dtp->dt_endedon) { /* * This is the simple case. We're either not stopped, or if * we are, we actually processed any END probes on another * CPU. We can simply consume this buffer and return. */ rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); return (rval); } begin.dtbgn_probefunc = pf; begin.dtbgn_recfunc = rf; begin.dtbgn_arg = arg; begin.dtbgn_beginonly = 1; /* * We need to interpose on the ERROR handler to be sure that we * only process ERRORs induced by BEGIN. */ begin.dtbgn_errhdlr = dtp->dt_errhdlr; begin.dtbgn_errarg = dtp->dt_errarg; dtp->dt_errhdlr = dt_consume_begin_error; dtp->dt_errarg = &begin; rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, dt_consume_begin_probe, dt_consume_begin_record, &begin); dtp->dt_errhdlr = begin.dtbgn_errhdlr; dtp->dt_errarg = begin.dtbgn_errarg; if (rval != 0) { dt_put_buf(dtp, buf); return (rval); } if (max_ncpus == 0) max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *nbuf; if (i == cpu) continue; if (dt_get_buf(dtp, i, &nbuf) != 0) { dt_put_buf(dtp, buf); return (-1); } if (nbuf == NULL) continue; rval = dt_consume_cpu(dtp, fp, i, nbuf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, nbuf); if (rval != 0) { dt_put_buf(dtp, buf); return (rval); } } /* * Okay -- we're done with the other buffers. Now we want to * reconsume the first buffer -- but this time we're looking for * everything _but_ BEGIN. And of course, in order to only consume * those ERRORs _not_ associated with BEGIN, we need to reinstall our * ERROR interposition function... */ begin.dtbgn_beginonly = 0; assert(begin.dtbgn_errhdlr == dtp->dt_errhdlr); assert(begin.dtbgn_errarg == dtp->dt_errarg); dtp->dt_errhdlr = dt_consume_begin_error; dtp->dt_errarg = &begin; rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, dt_consume_begin_probe, dt_consume_begin_record, &begin); dtp->dt_errhdlr = begin.dtbgn_errhdlr; dtp->dt_errarg = begin.dtbgn_errarg; return (rval); } /* ARGSUSED */ static uint64_t dt_buf_oldest(void *elem, void *arg) { dtrace_bufdesc_t *buf = elem; size_t offs = buf->dtbd_oldest; while (offs < buf->dtbd_size) { dtrace_rechdr_t *dtrh = /* LINTED - alignment */ (dtrace_rechdr_t *)(buf->dtbd_data + offs); if (dtrh->dtrh_epid == DTRACE_EPIDNONE) { offs += sizeof (dtrace_epid_t); } else { return (DTRACE_RECORD_LOAD_TIMESTAMP(dtrh)); } } /* There are no records left; use the time the buffer was retrieved. */ return (buf->dtbd_timestamp); } int dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) { dtrace_optval_t size; static int max_ncpus; int i, rval; dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_SWITCHRATE]; hrtime_t now = gethrtime(); if (dtp->dt_lastswitch != 0) { if (now - dtp->dt_lastswitch < interval) return (0); dtp->dt_lastswitch += interval; } else { dtp->dt_lastswitch = now; } if (!dtp->dt_active) return (dt_set_errno(dtp, EINVAL)); if (max_ncpus == 0) max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; if (pf == NULL) pf = (dtrace_consume_probe_f *)dt_nullprobe; if (rf == NULL) rf = (dtrace_consume_rec_f *)dt_nullrec; if (dtp->dt_options[DTRACEOPT_TEMPORAL] == DTRACEOPT_UNSET) { /* * The output will not be in the order it was traced. Rather, * we will consume all of the data from each CPU's buffer in * turn. We apply special handling for the records from BEGIN * and END probes so that they are consumed first and last, * respectively. * * If we have just begun, we want to first process the CPU that * executed the BEGIN probe (if any). */ if (dtp->dt_active && dtp->dt_beganon != -1 && (rval = dt_consume_begin(dtp, fp, pf, rf, arg)) != 0) return (rval); for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *buf; /* * If we have stopped, we want to process the CPU on * which the END probe was processed only _after_ we * have processed everything else. */ if (dtp->dt_stopped && (i == dtp->dt_endedon)) continue; if (dt_get_buf(dtp, i, &buf) != 0) return (-1); if (buf == NULL) continue; dtp->dt_flow = 0; dtp->dt_indent = 0; dtp->dt_prefix = NULL; rval = dt_consume_cpu(dtp, fp, i, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); if (rval != 0) return (rval); } if (dtp->dt_stopped) { dtrace_bufdesc_t *buf; if (dt_get_buf(dtp, dtp->dt_endedon, &buf) != 0) return (-1); if (buf == NULL) return (0); rval = dt_consume_cpu(dtp, fp, dtp->dt_endedon, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); return (rval); } } else { /* * The output will be in the order it was traced (or for * speculations, when it was committed). We retrieve a buffer * from each CPU and put it into a priority queue, which sorts * based on the first entry in the buffer. This is sufficient * because entries within a buffer are already sorted. * * We then consume records one at a time, always consuming the * oldest record, as determined by the priority queue. When * we reach the end of the time covered by these buffers, * we need to stop and retrieve more records on the next pass. * The kernel tells us the time covered by each buffer, in * dtbd_timestamp. The first buffer's timestamp tells us the * time covered by all buffers, as subsequently retrieved * buffers will cover to a more recent time. */ uint64_t *drops = alloca(max_ncpus * sizeof (uint64_t)); uint64_t first_timestamp = 0; uint_t cookie = 0; dtrace_bufdesc_t *buf; bzero(drops, max_ncpus * sizeof (uint64_t)); if (dtp->dt_bufq == NULL) { dtp->dt_bufq = dt_pq_init(dtp, max_ncpus * 2, dt_buf_oldest, NULL); if (dtp->dt_bufq == NULL) /* ENOMEM */ return (-1); } /* Retrieve data from each CPU. */ (void) dtrace_getopt(dtp, "bufsize", &size); for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *buf; if (dt_get_buf(dtp, i, &buf) != 0) return (-1); if (buf != NULL) { if (first_timestamp == 0) first_timestamp = buf->dtbd_timestamp; assert(buf->dtbd_timestamp >= first_timestamp); dt_pq_insert(dtp->dt_bufq, buf); drops[i] = buf->dtbd_drops; buf->dtbd_drops = 0; } } /* Consume records. */ for (;;) { dtrace_bufdesc_t *buf = dt_pq_pop(dtp->dt_bufq); uint64_t timestamp; if (buf == NULL) break; timestamp = dt_buf_oldest(buf, dtp); if (timestamp == buf->dtbd_timestamp) { /* * We've reached the end of the time covered * by this buffer. If this is the oldest * buffer, we must do another pass * to retrieve more data. */ dt_put_buf(dtp, buf); if (timestamp == first_timestamp && !dtp->dt_stopped) break; continue; } assert(timestamp >= dtp->dt_last_timestamp); dtp->dt_last_timestamp = timestamp; if ((rval = dt_consume_cpu(dtp, fp, buf->dtbd_cpu, buf, B_TRUE, pf, rf, arg)) != 0) return (rval); dt_pq_insert(dtp->dt_bufq, buf); } /* Consume drops. */ for (i = 0; i < max_ncpus; i++) { if (drops[i] != 0) { - int error = dt_handle_cpudrop(dtp, i, + int error; + xo_open_instance("probes"); + dt_oformat_drop(dtp, i); + error = dt_handle_cpudrop(dtp, i, DTRACEDROP_PRINCIPAL, drops[i]); + xo_close_instance("probes"); if (error != 0) return (error); } } /* * Reduce memory usage by re-allocating smaller buffers * for the "remnants". */ while (buf = dt_pq_walk(dtp->dt_bufq, &cookie)) dt_realloc_buf(dtp, buf, buf->dtbd_size); } return (0); } + +void +dtrace_oformat_probe(dtrace_hdl_t *dtp __unused, const dtrace_probedata_t *data, + processorid_t cpu, dtrace_probedesc_t *pd) +{ + + xo_emit("{:timestamp/%llu} {:cpu/%d} {:id/%d} {:provider/%s} " + "{:module/%s} {:function/%s} {:name/%s}", + (unsigned long long)data->dtpda_timestamp, cpu, pd->dtpd_id, + pd->dtpd_provider, pd->dtpd_mod, pd->dtpd_func, pd->dtpd_name); +} + +void +dt_oformat_drop(dtrace_hdl_t *dtp, processorid_t cpu) +{ + xo_emit("{:cpu/%d} {:id/%d} {:provider/%s} " + "{:module/%s} {:function/%s} {:name/%s}", + cpu, -1, "dtrace", "INTERNAL", "INTERNAL", "DROP"); +} diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_handle.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_handle.c index f26126bba517..91a4871f56b5 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_handle.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_handle.c @@ -1,485 +1,518 @@ /* * 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. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #ifdef illumos #include #endif #include +#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; 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; 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; 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; int size; + struct timeval tv; 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", (u_longlong_t)howmany, what == DTRACEDROP_PRINCIPAL ? "" : "aggregation ", howmany > 1 ? "s" : "", cpu); + if (dtp->dt_oformat) { + (void) gettimeofday(&tv, NULL); + xo_emit("{:timestamp/%ld.%06ld} {:count/%ju} " + "{:total/%ju} {:kind/%d} {:msg/%s}", + tv.tv_sec, tv.tv_usec, (uintmax_t)drop.dtdda_drops, + (uintmax_t)drop.dtdda_total, drop.dtdda_kind, + drop.dtdda_msg); + } + 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; + struct timeval tv; 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)); + (void) gettimeofday(&tv, NULL); + 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 = sizeof (str) - (s - str); } else { s = str; size = sizeof (str); } (void) snprintf(s, size, "%llu %s%s%s\n", (u_longlong_t)(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) + if (dtp->dt_oformat) { + xo_open_instance("probes"); + dt_oformat_drop(dtp, DTRACE_CPUALL); + xo_emit("{:timestamp/%ld.%06ld} {:count/%ju} " + "{:total/%ju} {:kind/%d} {:msg/%s}", + tv.tv_sec, tv.tv_usec, (uintmax_t)drop.dtdda_drops, + (uintmax_t)drop.dtdda_total, drop.dtdda_kind, + drop.dtdda_msg); + } + + if (dtp->dt_drophdlr == NULL) { + if (dtp->dt_oformat) + xo_close_instance("probes"); return (dt_set_errno(dtp, EDT_DROPABORT)); + } if ((*dtp->dt_drophdlr)(&drop, - dtp->dt_droparg) == DTRACE_HANDLE_ABORT) + dtp->dt_droparg) == DTRACE_HANDLE_ABORT) { + if (dtp->dt_oformat) + xo_close_instance("probes"); return (dt_set_errno(dtp, EDT_DROPABORT)); + } + + if (dtp->dt_oformat) + xo_close_instance("probes"); } 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/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h index 1e62bd6b21db..1be984f28001 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_impl.h @@ -1,757 +1,764 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2011, 2016 by Delphix. All rights reserved. */ #ifndef _DT_IMPL_H #define _DT_IMPL_H #include #include #ifndef illumos #include #include #include #include #include #endif #include #include #include #include #ifdef illumos #include #endif #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include struct dt_module; /* see below */ struct dt_pfdict; /* see */ struct dt_arg; /* see below */ struct dt_provider; /* see */ struct dt_xlator; /* see */ typedef struct dt_intrinsic { const char *din_name; /* string name of the intrinsic type */ ctf_encoding_t din_data; /* integer or floating-point CTF encoding */ uint_t din_kind; /* CTF type kind to instantiate */ } dt_intrinsic_t; typedef struct dt_typedef { const char *dty_src; /* string name of typedef source type */ const char *dty_dst; /* string name of typedef destination type */ } dt_typedef_t; typedef struct dt_intdesc { const char *did_name; /* string name of the integer type */ ctf_file_t *did_ctfp; /* CTF container for this type reference */ ctf_id_t did_type; /* CTF type reference for this type */ uintmax_t did_limit; /* maximum positive value held by type */ } dt_intdesc_t; typedef struct dt_modops { uint_t (*do_syminit)(struct dt_module *); void (*do_symsort)(struct dt_module *); GElf_Sym *(*do_symname)(struct dt_module *, const char *, GElf_Sym *, uint_t *); GElf_Sym *(*do_symaddr)(struct dt_module *, GElf_Addr, GElf_Sym *, uint_t *); } dt_modops_t; typedef struct dt_arg { int da_ndx; /* index of this argument */ int da_mapping; /* mapping of argument indices to arguments */ ctf_id_t da_type; /* type of argument */ ctf_file_t *da_ctfp; /* CTF container for type */ dt_ident_t *da_xlator; /* translator, if any */ struct dt_arg *da_next; /* next argument */ } dt_arg_t; typedef struct dt_sym { uint_t ds_symid; /* id of corresponding symbol */ uint_t ds_next; /* index of next element in hash chain */ } dt_sym_t; typedef struct dt_module { dt_list_t dm_list; /* list forward/back pointers */ char dm_name[DTRACE_MODNAMELEN]; /* string name of module */ char dm_file[MAXPATHLEN]; /* file path of module (if any) */ struct dt_module *dm_next; /* pointer to next module in hash chain */ const dt_modops_t *dm_ops; /* pointer to data model's ops vector */ Elf *dm_elf; /* libelf handle for module object */ objfs_info_t dm_info; /* object filesystem private info */ ctf_sect_t dm_symtab; /* symbol table for module */ ctf_sect_t dm_strtab; /* string table for module */ ctf_sect_t dm_ctdata; /* CTF data for module */ ctf_file_t *dm_ctfp; /* CTF container handle */ uint_t *dm_symbuckets; /* symbol table hash buckets (chain indices) */ dt_sym_t *dm_symchains; /* symbol table hash chains buffer */ void *dm_asmap; /* symbol pointers sorted by value */ uint_t dm_symfree; /* index of next free hash element */ uint_t dm_nsymbuckets; /* number of elements in bucket array */ uint_t dm_nsymelems; /* number of elements in hash table */ uint_t dm_asrsv; /* actual reserved size of dm_asmap */ uint_t dm_aslen; /* number of entries in dm_asmap */ uint_t dm_flags; /* module flags (see below) */ int dm_modid; /* modinfo(1M) module identifier */ GElf_Addr dm_text_va; /* virtual address of text section */ GElf_Xword dm_text_size; /* size in bytes of text section */ GElf_Addr dm_data_va; /* virtual address of data section */ GElf_Xword dm_data_size; /* size in bytes of data section */ GElf_Addr dm_bss_va; /* virtual address of BSS */ GElf_Xword dm_bss_size; /* size in bytes of BSS */ dt_idhash_t *dm_extern; /* external symbol definitions */ #ifndef illumos caddr_t dm_reloc_offset; /* Symbol relocation offset. */ uintptr_t *dm_sec_offsets; #endif pid_t dm_pid; /* pid for this module */ uint_t dm_nctflibs; /* number of ctf children libraries */ ctf_file_t **dm_libctfp; /* process library ctf pointers */ char **dm_libctfn; /* names of process ctf containers */ } dt_module_t; #define DT_DM_LOADED 0x1 /* module symbol and type data is loaded */ #define DT_DM_KERNEL 0x2 /* module is associated with a kernel object */ #define DT_DM_PRIMARY 0x4 /* module is a krtld primary kernel object */ #ifdef __FreeBSD__ /* * A representation of a FreeBSD kernel module, used when checking module * dependencies. This differs from dt_module_t, which refers to a KLD in the * case of kernel probes. Since modules can be identified regardless of whether * they've been compiled into the kernel, we use them to identify DTrace * modules. */ typedef struct dt_kmodule { struct dt_kmodule *dkm_next; /* hash table entry */ char *dkm_name; /* string name of module */ dt_module_t *dkm_module; /* corresponding KLD module */ } dt_kmodule_t; #endif typedef struct dt_provmod { char *dp_name; /* name of provider module */ struct dt_provmod *dp_next; /* next module */ } dt_provmod_t; typedef struct dt_ahashent { struct dt_ahashent *dtahe_prev; /* prev on hash chain */ struct dt_ahashent *dtahe_next; /* next on hash chain */ struct dt_ahashent *dtahe_prevall; /* prev on list of all */ struct dt_ahashent *dtahe_nextall; /* next on list of all */ uint64_t dtahe_hashval; /* hash value */ size_t dtahe_size; /* size of data */ dtrace_aggdata_t dtahe_data; /* data */ void (*dtahe_aggregate)(int64_t *, int64_t *, size_t); /* function */ } dt_ahashent_t; typedef struct dt_ahash { dt_ahashent_t **dtah_hash; /* hash table */ dt_ahashent_t *dtah_all; /* list of all elements */ size_t dtah_size; /* size of hash table */ } dt_ahash_t; typedef struct dt_aggregate { dtrace_bufdesc_t dtat_buf; /* buf aggregation snapshot */ int dtat_flags; /* aggregate flags */ processorid_t dtat_ncpus; /* number of CPUs in aggregate */ processorid_t *dtat_cpus; /* CPUs in aggregate */ processorid_t dtat_ncpu; /* size of dtat_cpus array */ processorid_t dtat_maxcpu; /* maximum number of CPUs */ dt_ahash_t dtat_hash; /* aggregate hash table */ } dt_aggregate_t; typedef struct dt_print_aggdata { dtrace_hdl_t *dtpa_dtp; /* pointer to libdtrace handle */ dtrace_aggvarid_t dtpa_id; /* aggregation variable of interest */ FILE *dtpa_fp; /* file pointer */ int dtpa_allunprint; /* print only unprinted aggregations */ int dtpa_agghist; /* print aggregation as histogram */ int dtpa_agghisthdr; /* aggregation histogram hdr printed */ int dtpa_aggpack; /* pack quantized aggregations */ + char dtpa_keyname[256]; /* key name for oformat */ + char *dtpa_aggname; /* aggregate name for oformat */ } 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 */ #ifdef __FreeBSD__ dt_kmodule_t **dt_kmods; /* hash table of dt_kmodule_t's */ #endif uint_t dt_modbuckets; /* number of module hash buckets */ uint_t dt_nmods; /* number of modules in hash and list */ dt_provmod_t *dt_provmod; /* linked list of provider modules */ dt_module_t *dt_exec; /* pointer to executable module */ dt_module_t *dt_rtld; /* pointer to run-time linker module */ dt_module_t *dt_cdefs; /* pointer to C dynamic type module */ dt_module_t *dt_ddefs; /* pointer to D dynamic type module */ dt_list_t dt_provlist; /* linked list of dt_provider_t's */ struct dt_provider **dt_provs; /* hash table of dt_provider_t's */ uint_t dt_provbuckets; /* number of provider hash buckets */ uint_t dt_nprovs; /* number of providers in hash and list */ dt_proc_hash_t *dt_procs; /* hash table of grabbed process handles */ char **dt_proc_env; /* additional environment variables */ dt_intdesc_t dt_ints[6]; /* cached integer type descriptions */ ctf_id_t dt_type_func; /* cached CTF identifier for function type */ ctf_id_t dt_type_fptr; /* cached CTF identifier for function pointer */ ctf_id_t dt_type_str; /* cached CTF identifier for string type */ ctf_id_t dt_type_dyn; /* cached CTF identifier for type */ ctf_id_t dt_type_stack; /* cached CTF identifier for stack type */ ctf_id_t dt_type_symaddr; /* cached CTF identifier for _symaddr type */ ctf_id_t dt_type_usymaddr; /* cached CTF ident. for _usymaddr type */ size_t dt_maxprobe; /* max enabled probe ID */ dtrace_eprobedesc_t **dt_edesc; /* enabled probe descriptions */ dtrace_probedesc_t **dt_pdesc; /* probe descriptions for enabled prbs */ size_t dt_maxagg; /* max aggregation ID */ dtrace_aggdesc_t **dt_aggdesc; /* aggregation descriptions */ int dt_maxformat; /* max format ID */ void **dt_formats; /* pointer to format array */ int dt_maxstrdata; /* max strdata ID */ char **dt_strdata; /* pointer to strdata array */ dt_aggregate_t dt_aggregate; /* aggregate */ dt_pq_t *dt_bufq; /* CPU-specific data queue */ struct dt_pfdict *dt_pfdict; /* dictionary of printf conversions */ dt_version_t dt_vmax; /* optional ceiling on program API binding */ dtrace_attribute_t dt_amin; /* optional floor on program attributes */ char *dt_cpp_path; /* pathname of cpp(1) to invoke if needed */ char **dt_cpp_argv; /* argument vector for exec'ing cpp(1) */ int dt_cpp_argc; /* count of initialized cpp(1) arguments */ int dt_cpp_args; /* size of dt_cpp_argv[] array */ char *dt_ld_path; /* pathname of ld(1) to invoke if needed */ #ifdef __FreeBSD__ char *dt_objcopy_path; /* pathname of objcopy(1) to invoke if needed */ #endif dt_list_t dt_lib_path; /* linked-list forming library search path */ uint_t dt_lazyload; /* boolean: set via -xlazyload */ uint_t dt_droptags; /* boolean: set via -xdroptags */ uint_t dt_active; /* boolean: set once tracing is active */ uint_t dt_stopped; /* boolean: set once tracing is stopped */ processorid_t dt_beganon; /* CPU that executed BEGIN probe (if any) */ processorid_t dt_endedon; /* CPU that executed END probe (if any) */ uint_t dt_oflags; /* dtrace open-time options (see dtrace.h) */ uint_t dt_cflags; /* dtrace compile-time options (see dtrace.h) */ uint_t dt_dflags; /* dtrace link-time options (see dtrace.h) */ uint_t dt_prcmode; /* dtrace process create mode (see dt_proc.h) */ uint_t dt_linkmode; /* dtrace symbol linking mode (see below) */ uint_t dt_linktype; /* dtrace link output file type (see below) */ uint_t dt_xlatemode; /* dtrace translator linking mode (see below) */ uint_t dt_stdcmode; /* dtrace stdc compatibility mode (see below) */ uint_t dt_encoding; /* dtrace output encoding (see below) */ uint_t dt_treedump; /* dtrace tree debug bitmap (see below) */ uint64_t dt_options[DTRACEOPT_MAX]; /* dtrace run-time options */ int dt_version; /* library version requested by client */ int dt_ctferr; /* error resulting from last CTF failure */ int dt_errno; /* error resulting from last failed operation */ #ifndef illumos const char *dt_errfile; int dt_errline; #endif int dt_fd; /* file descriptor for dtrace pseudo-device */ int dt_ftfd; /* file descriptor for fasttrap pseudo-device */ int dt_kinstfd; /* file descriptor for kinst 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 */ #ifdef illumos int dt_stdout_fd; /* file descriptor for saved stdout */ #else FILE *dt_freopen_fp; /* file pointer for freopened stdout */ #endif dtrace_handle_err_f *dt_errhdlr; /* error handler, if any */ void *dt_errarg; /* error handler argument */ dtrace_prog_t *dt_errprog; /* error handler program, if any */ dtrace_handle_drop_f *dt_drophdlr; /* drop handler, if any */ void *dt_droparg; /* drop handler argument */ dtrace_handle_proc_f *dt_prochdlr; /* proc handler, if any */ void *dt_procarg; /* proc handler argument */ dtrace_handle_setopt_f *dt_setopthdlr; /* setopt handler, if any */ void *dt_setoptarg; /* setopt handler argument */ dtrace_status_t dt_status[2]; /* status cache */ int dt_statusgen; /* current status generation */ hrtime_t dt_laststatus; /* last status */ hrtime_t dt_lastswitch; /* last switch of buffer data */ hrtime_t dt_lastagg; /* last snapshot of aggregation data */ char *dt_sprintf_buf; /* buffer for dtrace_sprintf() */ int dt_sprintf_buflen; /* length of dtrace_sprintf() buffer */ const char *dt_filetag; /* default filetag for dt_set_errmsg() */ char *dt_buffered_buf; /* buffer for buffered output */ size_t dt_buffered_offs; /* current offset into buffered buffer */ size_t dt_buffered_size; /* size of buffered buffer */ dtrace_handle_buffered_f *dt_bufhdlr; /* buffered handler, if any */ void *dt_bufarg; /* buffered handler argument */ dt_dof_t dt_dof; /* DOF generation buffers (see dt_dof.c) */ struct utsname dt_uts; /* uname(2) information for system */ dt_list_t dt_lib_dep; /* scratch linked-list of lib dependencies */ dt_list_t dt_lib_dep_sorted; /* dependency sorted library list */ dtrace_flowkind_t dt_flow; /* flow kind */ const char *dt_prefix; /* recommended flow prefix */ int dt_indent; /* recommended flow indent */ dtrace_epid_t dt_last_epid; /* most recently consumed EPID */ uint64_t dt_last_timestamp; /* most recently consumed timestamp */ boolean_t dt_has_sugar; /* syntactic sugar used? */ + int dt_oformat; /* output format (none, json, xml, html) */ }; /* * Values for the user arg of the ECB. */ #define DT_ECB_DEFAULT 0 #define DT_ECB_ERROR 1 /* * Values for the dt_linkmode property, which is used by the assembler when * processing external symbol references. User can set using -xlink=. */ #define DT_LINK_KERNEL 0 /* kernel syms static, user syms dynamic */ #define DT_LINK_PRIMARY 1 /* primary kernel syms static, others dynamic */ #define DT_LINK_DYNAMIC 2 /* all symbols dynamic */ #define DT_LINK_STATIC 3 /* all symbols static */ /* * Values for the dt_linktype property, which is used by dtrace_program_link() * to determine the type of output file that is desired by the client. */ #define DT_LTYP_ELF 0 /* produce ELF containing DOF */ #define DT_LTYP_DOF 1 /* produce stand-alone DOF */ /* * Values for the dt_xlatemode property, which is used to determine whether * references to dynamic translators are permitted. Set using -xlate=. */ #define DT_XL_STATIC 0 /* require xlators to be statically defined */ #define DT_XL_DYNAMIC 1 /* produce references to dynamic translators */ /* * Values for the dt_stdcmode property, which is used by the compiler when * running cpp to determine the presence and setting of the __STDC__ macro. */ #define DT_STDC_XA 0 /* ISO C + K&R C compat w/o ISO: __STDC__=0 */ #define DT_STDC_XC 1 /* Strict ISO C: __STDC__=1 */ #define DT_STDC_XS 2 /* K&R C: __STDC__ not defined */ #define DT_STDC_XT 3 /* ISO C + K&R C compat with ISO: __STDC__=0 */ /* * Values for the dt_encoding property, which is used to force a particular * character encoding (overriding default behavior and/or automatic detection). */ #define DT_ENCODING_UNSET 0 #define DT_ENCODING_ASCII 1 #define DT_ENCODING_UTF8 2 /* * Macro to test whether a given pass bit is set in the dt_treedump bit-vector. * If the bit for pass 'p' is set, the D compiler displays the parse tree for * the program by printing it to stderr at the end of compiler pass 'p'. */ #define DT_TREEDUMP_PASS(dtp, p) ((dtp)->dt_treedump & (1 << ((p) - 1))) /* * Macros for accessing the cached CTF container and type ID for the common * types "int", "string", and , which we need to use frequently in the D * compiler. The DT_INT_* macro relies upon "int" being at index 0 in the * _dtrace_ints_* tables in dt_open.c; the others are also set up there. */ #define DT_INT_CTFP(dtp) ((dtp)->dt_ints[0].did_ctfp) #define DT_INT_TYPE(dtp) ((dtp)->dt_ints[0].did_type) #define DT_FUNC_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_FUNC_TYPE(dtp) ((dtp)->dt_type_func) #define DT_FPTR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_FPTR_TYPE(dtp) ((dtp)->dt_type_fptr) #define DT_STR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_STR_TYPE(dtp) ((dtp)->dt_type_str) #define DT_DYN_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_DYN_TYPE(dtp) ((dtp)->dt_type_dyn) #define DT_STACK_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_STACK_TYPE(dtp) ((dtp)->dt_type_stack) #define DT_SYMADDR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_SYMADDR_TYPE(dtp) ((dtp)->dt_type_symaddr) #define DT_USYMADDR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) #define DT_USYMADDR_TYPE(dtp) ((dtp)->dt_type_usymaddr) /* * Actions and subroutines are both DT_NODE_FUNC nodes; to avoid confusing * an action for a subroutine (or vice versa), we assure that the DT_ACT_* * constants and the DIF_SUBR_* constants occupy non-overlapping ranges by * starting the DT_ACT_* constants at DIF_SUBR_MAX + 1. */ #define DT_ACT_BASE DIF_SUBR_MAX + 1 #define DT_ACT(n) (DT_ACT_BASE + (n)) #define DT_ACT_PRINTF DT_ACT(0) /* printf() action */ #define DT_ACT_TRACE DT_ACT(1) /* trace() action */ #define DT_ACT_TRACEMEM DT_ACT(2) /* tracemem() action */ #define DT_ACT_STACK DT_ACT(3) /* stack() action */ #define DT_ACT_STOP DT_ACT(4) /* stop() action */ #define DT_ACT_BREAKPOINT DT_ACT(5) /* breakpoint() action */ #define DT_ACT_PANIC DT_ACT(6) /* panic() action */ #define DT_ACT_SPECULATE DT_ACT(7) /* speculate() action */ #define DT_ACT_COMMIT DT_ACT(8) /* commit() action */ #define DT_ACT_DISCARD DT_ACT(9) /* discard() action */ #define DT_ACT_CHILL DT_ACT(10) /* chill() action */ #define DT_ACT_EXIT DT_ACT(11) /* exit() action */ #define DT_ACT_USTACK DT_ACT(12) /* ustack() action */ #define DT_ACT_PRINTA DT_ACT(13) /* printa() action */ #define DT_ACT_RAISE DT_ACT(14) /* raise() action */ #define DT_ACT_CLEAR DT_ACT(15) /* clear() action */ #define DT_ACT_NORMALIZE DT_ACT(16) /* normalize() action */ #define DT_ACT_DENORMALIZE DT_ACT(17) /* denormalize() action */ #define DT_ACT_TRUNC DT_ACT(18) /* trunc() action */ #define DT_ACT_SYSTEM DT_ACT(19) /* system() action */ #define DT_ACT_JSTACK DT_ACT(20) /* jstack() action */ #define DT_ACT_FTRUNCATE DT_ACT(21) /* ftruncate() action */ #define DT_ACT_FREOPEN DT_ACT(22) /* freopen() action */ #define DT_ACT_SYM DT_ACT(23) /* sym()/func() actions */ #define DT_ACT_MOD DT_ACT(24) /* mod() action */ #define DT_ACT_USYM DT_ACT(25) /* usym()/ufunc() actions */ #define DT_ACT_UMOD DT_ACT(26) /* umod() action */ #define DT_ACT_UADDR DT_ACT(27) /* uaddr() action */ #define DT_ACT_SETOPT DT_ACT(28) /* setopt() action */ #define DT_ACT_PRINT DT_ACT(29) /* print() action */ #define DT_ACT_PRINTM DT_ACT(30) /* printm() action */ /* * Sentinel to tell freopen() to restore the saved stdout. This must not * be ever valid for opening for write access via freopen(3C), which of * course, "." never is. */ #define DT_FREOPEN_RESTORE "." #define EDT_BASE 1000 /* base value for libdtrace errnos */ enum { EDT_VERSION = EDT_BASE, /* client is requesting unsupported version */ EDT_VERSINVAL, /* version string is invalid or overflows */ EDT_VERSUNDEF, /* requested API version is not defined */ EDT_VERSREDUCED, /* requested API version has been reduced */ EDT_CTF, /* libctf called failed (dt_ctferr has more) */ EDT_COMPILER, /* error in D program compilation */ EDT_NOTUPREG, /* tuple register allocation failure */ EDT_NOMEM, /* memory allocation failure */ EDT_INT2BIG, /* integer limit exceeded */ EDT_STR2BIG, /* string limit exceeded */ EDT_NOMOD, /* unknown module name */ EDT_NOPROV, /* unknown provider name */ EDT_NOPROBE, /* unknown probe name */ EDT_NOSYM, /* unknown symbol name */ EDT_NOSYMADDR, /* no symbol corresponds to address */ EDT_NOTYPE, /* unknown type name */ EDT_NOVAR, /* unknown variable name */ EDT_NOAGG, /* unknown aggregation name */ EDT_BADSCOPE, /* improper use of type name scoping operator */ EDT_BADSPEC, /* overspecified probe description */ EDT_BADSPCV, /* bad macro variable in probe description */ EDT_BADID, /* invalid probe identifier */ EDT_NOTLOADED, /* module is not currently loaded */ EDT_NOCTF, /* module does not contain any CTF data */ EDT_DATAMODEL, /* module and program data models don't match */ EDT_DIFVERS, /* library has newer DIF version than driver */ EDT_BADAGG, /* unrecognized aggregating action */ EDT_FIO, /* file i/o error */ EDT_DIFINVAL, /* invalid DIF program */ EDT_DIFSIZE, /* invalid DIF size */ EDT_DIFFAULT, /* failed to copyin DIF program */ EDT_BADPROBE, /* bad probe description */ EDT_BADPGLOB, /* bad probe description globbing pattern */ EDT_NOSCOPE, /* declaration scope stack underflow */ EDT_NODECL, /* declaration stack underflow */ EDT_DMISMATCH, /* record list does not match statement */ EDT_DOFFSET, /* record data offset error */ EDT_DALIGN, /* record data alignment error */ EDT_BADOPTNAME, /* invalid dtrace_setopt option name */ EDT_BADOPTVAL, /* invalid dtrace_setopt option value */ EDT_BADOPTCTX, /* invalid dtrace_setopt option context */ EDT_CPPFORK, /* failed to fork preprocessor */ EDT_CPPEXEC, /* failed to exec preprocessor */ EDT_CPPENT, /* preprocessor not found */ EDT_CPPERR, /* unknown preprocessor error */ EDT_SYMOFLOW, /* external symbol table overflow */ EDT_ACTIVE, /* operation illegal when tracing is active */ EDT_DESTRUCTIVE, /* destructive actions not allowed */ EDT_NOANON, /* no anonymous tracing state */ EDT_ISANON, /* can't claim anon state and enable probes */ EDT_ENDTOOBIG, /* END enablings exceed size of prncpl buffer */ EDT_NOCONV, /* failed to load type for printf conversion */ EDT_BADCONV, /* incomplete printf conversion */ EDT_BADERROR, /* invalid library ERROR action */ EDT_ERRABORT, /* abort due to error */ EDT_DROPABORT, /* abort due to drop */ EDT_DIRABORT, /* abort explicitly directed */ EDT_BADRVAL, /* invalid return value from callback */ EDT_BADNORMAL, /* invalid normalization */ EDT_BUFTOOSMALL, /* enabling exceeds size of buffer */ EDT_BADTRUNC, /* invalid truncation */ EDT_BUSY, /* device busy (active kernel debugger) */ EDT_ACCESS, /* insufficient privileges to use DTrace */ EDT_NOENT, /* dtrace device not available */ EDT_BRICKED, /* abort due to systemic unresponsiveness */ EDT_HARDWIRE, /* failed to load hard-wired definitions */ EDT_ELFVERSION, /* libelf is out-of-date w.r.t libdtrace */ EDT_NOBUFFERED, /* attempt to buffer output without handler */ EDT_UNSTABLE, /* description matched unstable set of probes */ EDT_BADSETOPT, /* invalid setopt library action */ EDT_BADSTACKPC, /* invalid stack program counter size */ EDT_BADAGGVAR, /* invalid aggregation variable identifier */ EDT_OVERSION, /* client is requesting deprecated version */ EDT_ENABLING_ERR, /* failed to enable probe */ EDT_NOPROBES, /* no probes sites for declared provider */ EDT_CANTLOAD /* failed to load a module */ }; /* * Interfaces for parsing and comparing DTrace attribute tuples, which describe * stability and architectural binding information. The dtrace_attribute_t * structure and associated constant definitions are found in . */ extern dtrace_attribute_t dt_attr_min(dtrace_attribute_t, dtrace_attribute_t); extern dtrace_attribute_t dt_attr_max(dtrace_attribute_t, dtrace_attribute_t); extern char *dt_attr_str(dtrace_attribute_t, char *, size_t); extern int dt_attr_cmp(dtrace_attribute_t, dtrace_attribute_t); /* * Interfaces for parsing and handling DTrace version strings. Version binding * is a feature of the D compiler that is handled completely independently of * the DTrace kernel infrastructure, so the definitions are here in libdtrace. * Version strings are compiled into an encoded uint32_t which can be compared * using C comparison operators. Version definitions are found in dt_open.c. */ #define DT_VERSION_STRMAX 16 /* enough for "255.4095.4095\0" */ #define DT_VERSION_MAJMAX 0xFF /* maximum major version number */ #define DT_VERSION_MINMAX 0xFFF /* maximum minor version number */ #define DT_VERSION_MICMAX 0xFFF /* maximum micro version number */ #define DT_VERSION_NUMBER(M, m, u) \ ((((M) & 0xFF) << 24) | (((m) & 0xFFF) << 12) | ((u) & 0xFFF)) #define DT_VERSION_MAJOR(v) (((v) & 0xFF000000) >> 24) #define DT_VERSION_MINOR(v) (((v) & 0x00FFF000) >> 12) #define DT_VERSION_MICRO(v) ((v) & 0x00000FFF) extern char *dt_version_num2str(dt_version_t, char *, size_t); extern int dt_version_str2num(const char *, dt_version_t *); extern int dt_version_defined(dt_version_t); /* * Miscellaneous internal libdtrace interfaces. The definitions below are for * libdtrace routines that do not yet merit their own separate header file. */ extern char *dt_cpp_add_arg(dtrace_hdl_t *, const char *); extern char *dt_cpp_pop_arg(dtrace_hdl_t *); #ifdef illumos extern int dt_set_errno(dtrace_hdl_t *, int); #else int _dt_set_errno(dtrace_hdl_t *, int, const char *, int); void dt_get_errloc(dtrace_hdl_t *, const char **, int *); #define dt_set_errno(_a,_b) _dt_set_errno(_a,_b,__FILE__,__LINE__) #endif extern void dt_set_errmsg(dtrace_hdl_t *, const char *, const char *, const char *, int, const char *, va_list); #ifdef illumos extern int dt_ioctl(dtrace_hdl_t *, int, void *); #else extern int dt_ioctl(dtrace_hdl_t *, u_long, void *); #endif extern int dt_status(dtrace_hdl_t *, processorid_t); extern long dt_sysconf(dtrace_hdl_t *, int); extern ssize_t dt_write(dtrace_hdl_t *, int, const void *, size_t); extern int dt_printf(dtrace_hdl_t *, FILE *, const char *, ...); extern void *dt_zalloc(dtrace_hdl_t *, size_t); extern void *dt_alloc(dtrace_hdl_t *, size_t); extern void dt_free(dtrace_hdl_t *, void *); extern void dt_difo_free(dtrace_hdl_t *, dtrace_difo_t *); extern int dt_gmatch(const char *, const char *); extern char *dt_basename(char *); extern ulong_t dt_popc(ulong_t); extern ulong_t dt_popcb(const ulong_t *, ulong_t); extern int dt_buffered_enable(dtrace_hdl_t *); extern int dt_buffered_flush(dtrace_hdl_t *, dtrace_probedata_t *, const dtrace_recdesc_t *, const dtrace_aggdata_t *, uint32_t flags); extern void dt_buffered_disable(dtrace_hdl_t *); extern void dt_buffered_destroy(dtrace_hdl_t *); extern uint64_t dt_stddev(uint64_t *, uint64_t); extern int dt_rw_read_held(pthread_rwlock_t *); extern int dt_rw_write_held(pthread_rwlock_t *); extern int dt_mutex_held(pthread_mutex_t *); extern int dt_options_load(dtrace_hdl_t *); #define DT_RW_READ_HELD(x) dt_rw_read_held(x) #define DT_RW_WRITE_HELD(x) dt_rw_write_held(x) #define DT_RW_LOCK_HELD(x) (DT_RW_READ_HELD(x) || DT_RW_WRITE_HELD(x)) #define DT_MUTEX_HELD(x) dt_mutex_held(x) extern void dt_dprintf(const char *, ...); extern void dt_setcontext(dtrace_hdl_t *, dtrace_probedesc_t *); extern void dt_endcontext(dtrace_hdl_t *); extern void dt_pragma(dt_node_t *); extern int dt_reduce(dtrace_hdl_t *, dt_version_t); extern void dt_cg(dt_pcb_t *, dt_node_t *); extern dtrace_difo_t *dt_as(dt_pcb_t *); extern void dt_dis(const dtrace_difo_t *, FILE *); extern int dt_aggregate_go(dtrace_hdl_t *); extern int dt_aggregate_init(dtrace_hdl_t *); extern void dt_aggregate_destroy(dtrace_hdl_t *); extern int dt_epid_lookup(dtrace_hdl_t *, dtrace_epid_t, dtrace_eprobedesc_t **, dtrace_probedesc_t **); extern void dt_epid_destroy(dtrace_hdl_t *); extern int dt_aggid_lookup(dtrace_hdl_t *, dtrace_aggid_t, dtrace_aggdesc_t **); extern void dt_aggid_destroy(dtrace_hdl_t *); extern void *dt_format_lookup(dtrace_hdl_t *, int); extern void dt_format_destroy(dtrace_hdl_t *); extern const char *dt_strdata_lookup(dtrace_hdl_t *, int); extern void dt_strdata_destroy(dtrace_hdl_t *); extern int dt_print_quantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_lquantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_llquantize(dtrace_hdl_t *, FILE *, const void *, size_t, uint64_t); extern int dt_print_agg(const dtrace_aggdata_t *, void *); +extern int dt_format_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 void dt_oformat_drop(dtrace_hdl_t *, processorid_t); + extern int dt_lib_depend_add(dtrace_hdl_t *, dt_list_t *, const char *); extern dt_lib_depend_t *dt_lib_depend_lookup(dt_list_t *, const char *); extern dt_pcb_t *yypcb; /* pointer to current parser control block */ extern char yyintprefix; /* int token prefix for macros (+/-) */ extern char yyintsuffix[4]; /* int token suffix ([uUlL]*) */ extern int yyintdecimal; /* int token is decimal (1) or octal/hex (0) */ extern char *yytext; /* lex input buffer */ extern int yylineno; /* lex line number */ extern int yydebug; /* lex debugging */ extern dt_node_t *yypragma; /* lex token list for control lines */ extern const dtrace_attribute_t _dtrace_maxattr; /* maximum attributes */ extern const dtrace_attribute_t _dtrace_defattr; /* default attributes */ extern const dtrace_attribute_t _dtrace_symattr; /* symbol ref attributes */ extern const dtrace_attribute_t _dtrace_typattr; /* type ref attributes */ extern const dtrace_attribute_t _dtrace_prvattr; /* provider attributes */ extern const dtrace_pattr_t _dtrace_prvdesc; /* provider attribute bundle */ extern const dt_version_t _dtrace_versions[]; /* array of valid versions */ extern const char *const _dtrace_version; /* current version string */ extern int _dtrace_strbuckets; /* number of hash buckets for strings */ extern int _dtrace_intbuckets; /* number of hash buckets for ints */ extern uint_t _dtrace_stkindent; /* default indent for stack/ustack */ extern uint_t _dtrace_pidbuckets; /* number of hash buckets for pids */ extern uint_t _dtrace_pidlrulim; /* number of proc handles to cache */ extern int _dtrace_debug; /* debugging messages enabled */ extern size_t _dtrace_bufsize; /* default dt_buf_create() size */ extern int _dtrace_argmax; /* default maximum probe arguments */ extern const char *_dtrace_libdir; /* default library directory */ extern const char *_dtrace_moddir; /* default kernel module directory */ #ifdef __FreeBSD__ extern int gmatch(const char *, const char *); extern int yylex(void); #endif #ifdef __cplusplus } #endif #endif /* _DT_IMPL_H */ diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_oformat.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_oformat.h new file mode 100644 index 000000000000..bea7dbd19b4d --- /dev/null +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_oformat.h @@ -0,0 +1,34 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Domagoj Stolfa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _DT_OFORMAT_H_ +#define _DT_OFORMAT_H_ + +#undef NORETURN /* needed because libxo redefines it */ +#include + +#endif /* _DT_OFORMAT_H_ */ diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c index fd770e180b47..8f8d20298e4c 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_open.c @@ -1,1760 +1,1763 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012, 2016 by Delphix. All rights reserved. */ #include #ifdef illumos #include #include #else #include #include #include #endif #include #include #include #ifdef illumos #include #endif #include #include #include #include #include #include #include #define _POSIX_PTHREAD_SEMANTICS #include #undef _POSIX_PTHREAD_SEMANTICS #include #include #include #include #include #include +#include #ifndef illumos #include #include #endif #if defined(__i386__) #include #endif /* * Stability and versioning definitions. These #defines are used in the tables * of identifiers below to fill in the attribute and version fields associated * with each identifier. The DT_ATTR_* macros are a convenience to permit more * concise declarations of common attributes such as Stable/Stable/Common. The * DT_VERS_* macros declare the encoded integer values of all versions used so * far. DT_VERS_LATEST must correspond to the latest version value among all * versions exported by the D compiler. DT_VERS_STRING must be an ASCII string * that contains DT_VERS_LATEST within it along with any suffixes (e.g. Beta). * You must update DT_VERS_LATEST and DT_VERS_STRING when adding a new version, * and then add the new version to the _dtrace_versions[] array declared below. * Refer to the Solaris Dynamic Tracing Guide Stability and Versioning chapters * respectively for an explanation of these DTrace features and their values. * * NOTE: Although the DTrace versioning scheme supports the labeling and * introduction of incompatible changes (e.g. dropping an interface in a * major release), the libdtrace code does not currently support this. * All versions are assumed to strictly inherit from one another. If * we ever need to provide divergent interfaces, this will need work. */ #define DT_ATTR_STABCMN { DTRACE_STABILITY_STABLE, \ DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON } #define DT_ATTR_EVOLCMN { DTRACE_STABILITY_EVOLVING, \ DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON \ } /* * The version number should be increased for every customer visible release * of DTrace. The major number should be incremented when a fundamental * change has been made that would affect all consumers, and would reflect * sweeping changes to DTrace or the D language. The minor number should be * incremented when a change is introduced that could break scripts that had * previously worked; for example, adding a new built-in variable could break * a script which was already using that identifier. The micro number should * be changed when introducing functionality changes or major bug fixes that * do not affect backward compatibility -- this is merely to make capabilities * easily determined from the version number. Minor bugs do not require any * modification to the version number. */ #define DT_VERS_1_0 DT_VERSION_NUMBER(1, 0, 0) #define DT_VERS_1_1 DT_VERSION_NUMBER(1, 1, 0) #define DT_VERS_1_2 DT_VERSION_NUMBER(1, 2, 0) #define DT_VERS_1_2_1 DT_VERSION_NUMBER(1, 2, 1) #define DT_VERS_1_2_2 DT_VERSION_NUMBER(1, 2, 2) #define DT_VERS_1_3 DT_VERSION_NUMBER(1, 3, 0) #define DT_VERS_1_4 DT_VERSION_NUMBER(1, 4, 0) #define DT_VERS_1_4_1 DT_VERSION_NUMBER(1, 4, 1) #define DT_VERS_1_5 DT_VERSION_NUMBER(1, 5, 0) #define DT_VERS_1_6 DT_VERSION_NUMBER(1, 6, 0) #define DT_VERS_1_6_1 DT_VERSION_NUMBER(1, 6, 1) #define DT_VERS_1_6_2 DT_VERSION_NUMBER(1, 6, 2) #define DT_VERS_1_6_3 DT_VERSION_NUMBER(1, 6, 3) #define DT_VERS_1_7 DT_VERSION_NUMBER(1, 7, 0) #define DT_VERS_1_7_1 DT_VERSION_NUMBER(1, 7, 1) #define DT_VERS_1_8 DT_VERSION_NUMBER(1, 8, 0) #define DT_VERS_1_8_1 DT_VERSION_NUMBER(1, 8, 1) #define DT_VERS_1_9 DT_VERSION_NUMBER(1, 9, 0) #define DT_VERS_1_9_1 DT_VERSION_NUMBER(1, 9, 1) #define DT_VERS_1_10 DT_VERSION_NUMBER(1, 10, 0) #define DT_VERS_1_11 DT_VERSION_NUMBER(1, 11, 0) #define DT_VERS_1_12 DT_VERSION_NUMBER(1, 12, 0) #define DT_VERS_1_12_1 DT_VERSION_NUMBER(1, 12, 1) #define DT_VERS_1_13 DT_VERSION_NUMBER(1, 13, 0) #define DT_VERS_LATEST DT_VERS_1_13 #define DT_VERS_STRING "Sun D 1.13" const dt_version_t _dtrace_versions[] = { DT_VERS_1_0, /* D API 1.0.0 (PSARC 2001/466) Solaris 10 FCS */ DT_VERS_1_1, /* D API 1.1.0 Solaris Express 6/05 */ DT_VERS_1_2, /* D API 1.2.0 Solaris 10 Update 1 */ DT_VERS_1_2_1, /* D API 1.2.1 Solaris Express 4/06 */ DT_VERS_1_2_2, /* D API 1.2.2 Solaris Express 6/06 */ DT_VERS_1_3, /* D API 1.3 Solaris Express 10/06 */ DT_VERS_1_4, /* D API 1.4 Solaris Express 2/07 */ DT_VERS_1_4_1, /* D API 1.4.1 Solaris Express 4/07 */ DT_VERS_1_5, /* D API 1.5 Solaris Express 7/07 */ DT_VERS_1_6, /* D API 1.6 */ DT_VERS_1_6_1, /* D API 1.6.1 */ DT_VERS_1_6_2, /* D API 1.6.2 */ DT_VERS_1_6_3, /* D API 1.6.3 */ DT_VERS_1_7, /* D API 1.7 */ DT_VERS_1_7_1, /* D API 1.7.1 */ DT_VERS_1_8, /* D API 1.8 */ DT_VERS_1_8_1, /* D API 1.8.1 */ DT_VERS_1_9, /* D API 1.9 */ DT_VERS_1_9_1, /* D API 1.9.1 */ DT_VERS_1_10, /* D API 1.10 */ DT_VERS_1_11, /* D API 1.11 */ DT_VERS_1_12, /* D API 1.12 */ DT_VERS_1_12_1, /* D API 1.12.1 */ DT_VERS_1_13, /* D API 1.13 */ 0 }; /* * Global variables that are formatted on FreeBSD based on the kernel file name. */ #ifndef illumos static char curthread_str[MAXPATHLEN]; static char intmtx_str[MAXPATHLEN]; static char threadmtx_str[MAXPATHLEN]; static char rwlock_str[MAXPATHLEN]; static char sxlock_str[MAXPATHLEN]; #endif /* * Table of global identifiers. This is used to populate the global identifier * hash when a new dtrace client open occurs. For more info see dt_ident.h. * The global identifiers that represent functions use the dt_idops_func ops * and specify the private data pointer as a prototype string which is parsed * when the identifier is first encountered. These prototypes look like ANSI * C function prototypes except that the special symbol "@" can be used as a * wildcard to represent a single parameter of any type (i.e. any dt_node_t). * The standard "..." notation can also be used to represent varargs. An empty * parameter list is taken to mean void (that is, no arguments are permitted). * A parameter enclosed in square brackets (e.g. "[int]") denotes an optional * argument. */ static const dt_ident_t _dtrace_globals[] = { { "alloca", DT_IDENT_FUNC, 0, DIF_SUBR_ALLOCA, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void *(size_t)" }, { "arg0", DT_IDENT_SCALAR, 0, DIF_VAR_ARG0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg1", DT_IDENT_SCALAR, 0, DIF_VAR_ARG1, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg2", DT_IDENT_SCALAR, 0, DIF_VAR_ARG2, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg3", DT_IDENT_SCALAR, 0, DIF_VAR_ARG3, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg4", DT_IDENT_SCALAR, 0, DIF_VAR_ARG4, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg5", DT_IDENT_SCALAR, 0, DIF_VAR_ARG5, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg6", DT_IDENT_SCALAR, 0, DIF_VAR_ARG6, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg7", DT_IDENT_SCALAR, 0, DIF_VAR_ARG7, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg8", DT_IDENT_SCALAR, 0, DIF_VAR_ARG8, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "arg9", DT_IDENT_SCALAR, 0, DIF_VAR_ARG9, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int64_t" }, { "args", DT_IDENT_ARRAY, 0, DIF_VAR_ARGS, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_args, NULL }, { "avg", DT_IDENT_AGGFUNC, 0, DTRACEAGG_AVG, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "basename", DT_IDENT_FUNC, 0, DIF_SUBR_BASENAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "bcopy", DT_IDENT_FUNC, 0, DIF_SUBR_BCOPY, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(void *, void *, size_t)" }, { "breakpoint", DT_IDENT_ACTFUNC, 0, DT_ACT_BREAKPOINT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "caller", DT_IDENT_SCALAR, 0, DIF_VAR_CALLER, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uintptr_t" }, { "chill", DT_IDENT_ACTFUNC, 0, DT_ACT_CHILL, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "cleanpath", DT_IDENT_FUNC, 0, DIF_SUBR_CLEANPATH, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "clear", DT_IDENT_ACTFUNC, 0, DT_ACT_CLEAR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "commit", DT_IDENT_ACTFUNC, 0, DT_ACT_COMMIT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "copyin", DT_IDENT_FUNC, 0, DIF_SUBR_COPYIN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void *(uintptr_t, size_t)" }, { "copyinstr", DT_IDENT_FUNC, 0, DIF_SUBR_COPYINSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(uintptr_t, [size_t])" }, { "copyinto", DT_IDENT_FUNC, 0, DIF_SUBR_COPYINTO, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(uintptr_t, size_t, void *)" }, { "copyout", DT_IDENT_FUNC, 0, DIF_SUBR_COPYOUT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(void *, uintptr_t, size_t)" }, { "copyoutstr", DT_IDENT_FUNC, 0, DIF_SUBR_COPYOUTSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(char *, uintptr_t, size_t)" }, { "count", DT_IDENT_AGGFUNC, 0, DTRACEAGG_COUNT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "curthread", DT_IDENT_SCALAR, 0, DIF_VAR_CURTHREAD, { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_COMMON }, DT_VERS_1_0, #ifdef illumos &dt_idops_type, "genunix`kthread_t *" }, #else &dt_idops_type, curthread_str }, #endif { "ddi_pathname", DT_IDENT_FUNC, 0, DIF_SUBR_DDI_PATHNAME, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "string(void *, int64_t)" }, { "denormalize", DT_IDENT_ACTFUNC, 0, DT_ACT_DENORMALIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "dirname", DT_IDENT_FUNC, 0, DIF_SUBR_DIRNAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *)" }, { "discard", DT_IDENT_ACTFUNC, 0, DT_ACT_DISCARD, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "epid", DT_IDENT_SCALAR, 0, DIF_VAR_EPID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, { "errno", DT_IDENT_SCALAR, 0, DIF_VAR_ERRNO, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "int" }, { "execargs", DT_IDENT_SCALAR, 0, DIF_VAR_EXECARGS, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "execname", DT_IDENT_SCALAR, 0, DIF_VAR_EXECNAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { "exit", DT_IDENT_ACTFUNC, 0, DT_ACT_EXIT, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "freopen", DT_IDENT_ACTFUNC, 0, DT_ACT_FREOPEN, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "void(@, ...)" }, { "ftruncate", DT_IDENT_ACTFUNC, 0, DT_ACT_FTRUNCATE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "func", DT_IDENT_ACTFUNC, 0, DT_ACT_SYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_symaddr(uintptr_t)" }, { "getmajor", DT_IDENT_FUNC, 0, DIF_SUBR_GETMAJOR, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "genunix`major_t(genunix`dev_t)" }, { "getminor", DT_IDENT_FUNC, 0, DIF_SUBR_GETMINOR, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "genunix`minor_t(genunix`dev_t)" }, { "htonl", DT_IDENT_FUNC, 0, DIF_SUBR_HTONL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint32_t(uint32_t)" }, { "htonll", DT_IDENT_FUNC, 0, DIF_SUBR_HTONLL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint64_t(uint64_t)" }, { "htons", DT_IDENT_FUNC, 0, DIF_SUBR_HTONS, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint16_t(uint16_t)" }, { "getf", DT_IDENT_FUNC, 0, DIF_SUBR_GETF, DT_ATTR_STABCMN, DT_VERS_1_10, &dt_idops_func, "file_t *(int)" }, { "gid", DT_IDENT_SCALAR, 0, DIF_VAR_GID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "gid_t" }, { "id", DT_IDENT_SCALAR, 0, DIF_VAR_ID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, { "index", DT_IDENT_FUNC, 0, DIF_SUBR_INDEX, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "int(const char *, const char *, [int])" }, { "inet_ntoa", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA, DT_ATTR_STABCMN, #ifdef illumos DT_VERS_1_5, &dt_idops_func, "string(ipaddr_t *)" }, #else DT_VERS_1_5, &dt_idops_func, "string(in_addr_t *)" }, #endif { "inet_ntoa6", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOA6, DT_ATTR_STABCMN, #ifdef illumos DT_VERS_1_5, &dt_idops_func, "string(in6_addr_t *)" }, #else DT_VERS_1_5, &dt_idops_func, "string(struct in6_addr *)" }, #endif { "inet_ntop", DT_IDENT_FUNC, 0, DIF_SUBR_INET_NTOP, DT_ATTR_STABCMN, DT_VERS_1_5, &dt_idops_func, "string(int, void *)" }, { "ipl", DT_IDENT_SCALAR, 0, DIF_VAR_IPL, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint_t" }, #ifdef __FreeBSD__ { "jailname", DT_IDENT_SCALAR, 0, DIF_VAR_JAILNAME, DT_ATTR_STABCMN, DT_VERS_1_13, &dt_idops_type, "string" }, { "jid", DT_IDENT_SCALAR, 0, DIF_VAR_JID, DT_ATTR_STABCMN, DT_VERS_1_13, &dt_idops_type, "int" }, #endif { "json", DT_IDENT_FUNC, 0, DIF_SUBR_JSON, DT_ATTR_STABCMN, DT_VERS_1_11, &dt_idops_func, "string(const char *, const char *)" }, { "jstack", DT_IDENT_ACTFUNC, 0, DT_ACT_JSTACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "lltostr", DT_IDENT_FUNC, 0, DIF_SUBR_LLTOSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(int64_t, [int])" }, { "llquantize", DT_IDENT_AGGFUNC, 0, DTRACEAGG_LLQUANTIZE, DT_ATTR_STABCMN, DT_VERS_1_7, &dt_idops_func, "void(@, int32_t, int32_t, int32_t, int32_t, ...)" }, { "lquantize", DT_IDENT_AGGFUNC, 0, DTRACEAGG_LQUANTIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, int32_t, int32_t, ...)" }, { "max", DT_IDENT_AGGFUNC, 0, DTRACEAGG_MAX, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "memref", DT_IDENT_FUNC, 0, DIF_SUBR_MEMREF, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "uintptr_t *(void *, size_t)" }, #ifndef illumos { "memstr", DT_IDENT_FUNC, 0, DIF_SUBR_MEMSTR, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(void *, char, size_t)" }, #endif { "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)" }, #ifdef illumos { "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 *)" }, #else { "mutex_owned", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNED, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, { "mutex_owner", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_OWNER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, threadmtx_str }, { "mutex_type_adaptive", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_ADAPTIVE, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, { "mutex_type_spin", DT_IDENT_FUNC, 0, DIF_SUBR_MUTEX_TYPE_SPIN, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, intmtx_str }, #endif { "ntohl", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint32_t(uint32_t)" }, { "ntohll", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHLL, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint64_t(uint64_t)" }, { "ntohs", DT_IDENT_FUNC, 0, DIF_SUBR_NTOHS, DT_ATTR_EVOLCMN, DT_VERS_1_3, &dt_idops_func, "uint16_t(uint16_t)" }, { "normalize", DT_IDENT_ACTFUNC, 0, DT_ACT_NORMALIZE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "panic", DT_IDENT_ACTFUNC, 0, DT_ACT_PANIC, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "pid", DT_IDENT_SCALAR, 0, DIF_VAR_PID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "pid_t" }, { "ppid", DT_IDENT_SCALAR, 0, DIF_VAR_PPID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "pid_t" }, { "print", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINT, DT_ATTR_STABCMN, DT_VERS_1_9, &dt_idops_func, "void(@)" }, { "printa", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTA, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "printf", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTF, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "printm", DT_IDENT_ACTFUNC, 0, DT_ACT_PRINTM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(size_t, uintptr_t *)" }, { "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()" }, { "regs", DT_IDENT_ARRAY, 0, DIF_VAR_REGS, DT_ATTR_STABCMN, DT_VERS_1_13, &dt_idops_regs, NULL }, { "rindex", DT_IDENT_FUNC, 0, DIF_SUBR_RINDEX, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "int(const char *, const char *, [int])" }, #ifdef illumos { "rw_iswriter", DT_IDENT_FUNC, 0, DIF_SUBR_RW_ISWRITER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, { "rw_read_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_READ_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, { "rw_write_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_WRITE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, "int(genunix`krwlock_t *)" }, #else { "rw_iswriter", DT_IDENT_FUNC, 0, DIF_SUBR_RW_ISWRITER, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, { "rw_read_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_READ_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, { "rw_write_held", DT_IDENT_FUNC, 0, DIF_SUBR_RW_WRITE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, rwlock_str }, #endif { "self", DT_IDENT_PTR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "void" }, { "setopt", DT_IDENT_ACTFUNC, 0, DT_ACT_SETOPT, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "void(const char *, [const char *])" }, { "speculate", DT_IDENT_ACTFUNC, 0, DT_ACT_SPECULATE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(int)" }, { "speculation", DT_IDENT_FUNC, 0, DIF_SUBR_SPECULATION, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "int()" }, { "stack", DT_IDENT_ACTFUNC, 0, DT_ACT_STACK, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "stack(...)" }, { "stackdepth", DT_IDENT_SCALAR, 0, DIF_VAR_STACKDEPTH, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint32_t" }, { "stddev", DT_IDENT_AGGFUNC, 0, DTRACEAGG_STDDEV, DT_ATTR_STABCMN, DT_VERS_1_6, &dt_idops_func, "void(@)" }, { "stop", DT_IDENT_ACTFUNC, 0, DT_ACT_STOP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void()" }, { "strchr", DT_IDENT_FUNC, 0, DIF_SUBR_STRCHR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, char)" }, { "strlen", DT_IDENT_FUNC, 0, DIF_SUBR_STRLEN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "size_t(const char *)" }, { "strjoin", DT_IDENT_FUNC, 0, DIF_SUBR_STRJOIN, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "string(const char *, const char *)" }, { "strrchr", DT_IDENT_FUNC, 0, DIF_SUBR_STRRCHR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, char)" }, { "strstr", DT_IDENT_FUNC, 0, DIF_SUBR_STRSTR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, const char *)" }, { "strtok", DT_IDENT_FUNC, 0, DIF_SUBR_STRTOK, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, const char *)" }, { "strtoll", DT_IDENT_FUNC, 0, DIF_SUBR_STRTOLL, DT_ATTR_STABCMN, DT_VERS_1_11, &dt_idops_func, "int64_t(const char *, [int])" }, { "substr", DT_IDENT_FUNC, 0, DIF_SUBR_SUBSTR, DT_ATTR_STABCMN, DT_VERS_1_1, &dt_idops_func, "string(const char *, int, [int])" }, { "sum", DT_IDENT_AGGFUNC, 0, DTRACEAGG_SUM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, #ifndef illumos { "sx_isexclusive", DT_IDENT_FUNC, 0, DIF_SUBR_SX_ISEXCLUSIVE, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, { "sx_shared_held", DT_IDENT_FUNC, 0, DIF_SUBR_SX_SHARED_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, { "sx_exclusive_held", DT_IDENT_FUNC, 0, DIF_SUBR_SX_EXCLUSIVE_HELD, DT_ATTR_EVOLCMN, DT_VERS_1_0, &dt_idops_func, sxlock_str }, #endif { "sym", DT_IDENT_ACTFUNC, 0, DT_ACT_SYM, DT_ATTR_STABCMN, DT_VERS_1_2, &dt_idops_func, "_symaddr(uintptr_t)" }, { "system", DT_IDENT_ACTFUNC, 0, DT_ACT_SYSTEM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, ...)" }, { "this", DT_IDENT_PTR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "void" }, { "tid", DT_IDENT_SCALAR, 0, DIF_VAR_TID, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "id_t" }, { "timestamp", DT_IDENT_SCALAR, 0, DIF_VAR_TIMESTAMP, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "uint64_t" }, { "tolower", DT_IDENT_FUNC, 0, DIF_SUBR_TOLOWER, DT_ATTR_STABCMN, DT_VERS_1_8, &dt_idops_func, "string(const char *)" }, { "toupper", DT_IDENT_FUNC, 0, DIF_SUBR_TOUPPER, DT_ATTR_STABCMN, DT_VERS_1_8, &dt_idops_func, "string(const char *)" }, { "trace", DT_IDENT_ACTFUNC, 0, DT_ACT_TRACE, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@)" }, { "tracemem", DT_IDENT_ACTFUNC, 0, DT_ACT_TRACEMEM, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(@, size_t, ...)" }, { "trunc", DT_IDENT_ACTFUNC, 0, DT_ACT_TRUNC, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_func, "void(...)" }, { "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" }, #ifndef illumos { "cpu", DT_IDENT_SCALAR, 0, DIF_VAR_CPU, DT_ATTR_STABCMN, DT_VERS_1_6_3, &dt_idops_type, "int" }, #endif { NULL, 0, 0, 0, { 0, 0, 0 }, 0, NULL, NULL } }; /* * Tables of ILP32 intrinsic integer and floating-point type templates to use * to populate the dynamic "C" CTF type container. */ static const dt_intrinsic_t _dtrace_intrinsics_32[] = { { "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER }, { "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "unsigned", { 0, 0, 32 }, CTF_K_INTEGER }, { "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER }, { "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER }, { "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER }, { "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT }, { "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT }, { "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT }, { "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT }, { "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT }, { "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT }, { "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT }, { "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT }, { "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT }, { NULL, { 0, 0, 0 }, 0 } }; /* * Tables of LP64 intrinsic integer and floating-point type templates to use * to populate the dynamic "C" CTF type container. */ static const dt_intrinsic_t _dtrace_intrinsics_64[] = { { "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER }, { "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "unsigned", { 0, 0, 32 }, CTF_K_INTEGER }, { "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER }, { "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER }, { "signed long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER }, { "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER }, { "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER }, { "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER }, { "unsigned long", { 0, 0, 64 }, CTF_K_INTEGER }, { "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER }, { "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER }, { "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT }, { "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT }, { "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT }, { "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT }, { "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT }, { "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT }, { "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT }, { "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT }, { "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT }, { NULL, { 0, 0, 0 }, 0 } }; /* * Tables of ILP32 typedefs to use to populate the dynamic "D" CTF container. * These aliases ensure that D definitions can use typical names. */ static const dt_typedef_t _dtrace_typedefs_32[] = { { "char", "int8_t" }, { "short", "int16_t" }, { "int", "int32_t" }, { "long long", "int64_t" }, { "int", "intptr_t" }, { "int", "ssize_t" }, { "unsigned char", "uint8_t" }, { "unsigned short", "uint16_t" }, { "unsigned", "uint32_t" }, { "unsigned long long", "uint64_t" }, { "unsigned char", "uchar_t" }, { "unsigned short", "ushort_t" }, { "unsigned", "uint_t" }, { "unsigned long", "ulong_t" }, { "unsigned long long", "u_longlong_t" }, { "int", "ptrdiff_t" }, { "unsigned", "uintptr_t" }, { "unsigned", "size_t" }, { "long", "id_t" }, { "long", "pid_t" }, { NULL, NULL } }; /* * Tables of LP64 typedefs to use to populate the dynamic "D" CTF container. * These aliases ensure that D definitions can use typical names. */ static const dt_typedef_t _dtrace_typedefs_64[] = { { "char", "int8_t" }, { "short", "int16_t" }, { "int", "int32_t" }, { "long", "int64_t" }, { "long", "intptr_t" }, { "long", "ssize_t" }, { "unsigned char", "uint8_t" }, { "unsigned short", "uint16_t" }, { "unsigned", "uint32_t" }, { "unsigned long", "uint64_t" }, { "unsigned char", "uchar_t" }, { "unsigned short", "ushort_t" }, { "unsigned", "uint_t" }, { "unsigned long", "ulong_t" }, { "unsigned long long", "u_longlong_t" }, { "long", "ptrdiff_t" }, { "unsigned long", "uintptr_t" }, { "unsigned long", "size_t" }, { "int", "id_t" }, { "int", "pid_t" }, { NULL, NULL } }; /* * Tables of ILP32 integer type templates used to populate the dtp->dt_ints[] * cache when a new dtrace client open occurs. Values are set by dtrace_open(). */ static const dt_intdesc_t _dtrace_ints_32[] = { { "int", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned int", NULL, CTF_ERR, 0xffffffffULL }, { "long", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned long", NULL, CTF_ERR, 0xffffffffULL }, { "long long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long long", NULL, CTF_ERR, 0xffffffffffffffffULL } }; /* * Tables of LP64 integer type templates used to populate the dtp->dt_ints[] * cache when a new dtrace client open occurs. Values are set by dtrace_open(). */ static const dt_intdesc_t _dtrace_ints_64[] = { { "int", NULL, CTF_ERR, 0x7fffffffULL }, { "unsigned int", NULL, CTF_ERR, 0xffffffffULL }, { "long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long", NULL, CTF_ERR, 0xffffffffffffffffULL }, { "long long", NULL, CTF_ERR, 0x7fffffffffffffffULL }, { "unsigned long long", NULL, CTF_ERR, 0xffffffffffffffffULL } }; /* * Table of macro variable templates used to populate the macro identifier hash * when a new dtrace client open occurs. Values are set by dtrace_update(). */ static const dt_ident_t _dtrace_macros[] = { { "egid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "euid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "gid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "pid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "pgid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "ppid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "projid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "sid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "taskid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "target", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { "uid", DT_IDENT_SCALAR, 0, 0, DT_ATTR_STABCMN, DT_VERS_1_0 }, { NULL, 0, 0, 0, { 0, 0, 0 }, 0 } }; /* * Hard-wired definition string to be compiled and cached every time a new * DTrace library handle is initialized. This string should only be used to * contain definitions that should be present regardless of DTRACE_O_NOLIBS. */ static const char _dtrace_hardwire[] = "\ inline long NULL = 0; \n\ #pragma D binding \"1.0\" NULL\n\ "; /* * Default DTrace configuration to use when opening libdtrace DTRACE_O_NODEV. * If DTRACE_O_NODEV is not set, we load the configuration from the kernel. * The use of CTF_MODEL_NATIVE is more subtle than it might appear: we are * relying on the fact that when running dtrace(1M), isaexec will invoke the * binary with the same bitness as the kernel, which is what we want by default * when generating our DIF. The user can override the choice using oflags. */ static const dtrace_conf_t _dtrace_conf = { DIF_VERSION, /* dtc_difversion */ DIF_DIR_NREGS, /* dtc_difintregs */ DIF_DTR_NREGS, /* dtc_diftupregs */ CTF_MODEL_NATIVE /* dtc_ctfmodel */ }; const dtrace_attribute_t _dtrace_maxattr = { DTRACE_STABILITY_MAX, DTRACE_STABILITY_MAX, DTRACE_CLASS_MAX }; const dtrace_attribute_t _dtrace_defattr = { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }; const dtrace_attribute_t _dtrace_symattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_attribute_t _dtrace_typattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_attribute_t _dtrace_prvattr = { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }; const dtrace_pattr_t _dtrace_prvdesc = { { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, }; #ifdef illumos const char *_dtrace_defcpp = "/usr/ccs/lib/cpp"; /* default cpp(1) to invoke */ const char *_dtrace_defld = "/usr/ccs/bin/ld"; /* default ld(1) to invoke */ #else const char *_dtrace_defcpp = "cpp"; /* default cpp(1) to invoke */ const char *_dtrace_defld = "ld"; /* default ld(1) to invoke */ const char *_dtrace_defobjcopy = "objcopy"; /* default objcopy(1) to invoke */ #endif const char *_dtrace_libdir = "/usr/lib/dtrace"; /* default library directory */ #ifdef illumos const char *_dtrace_provdir = "/dev/dtrace/provider"; /* provider directory */ #else const char *_dtrace_libdir32 = "/usr/lib32/dtrace"; const char *_dtrace_provdir = "/dev/dtrace"; /* provider directory */ #endif int _dtrace_strbuckets = 211; /* default number of hash buckets (prime) */ int _dtrace_intbuckets = 256; /* default number of integer buckets (Pof2) */ uint_t _dtrace_strsize = 256; /* default size of string intrinsic type */ uint_t _dtrace_stkindent = 14; /* default whitespace indent for stack/ustack */ uint_t _dtrace_pidbuckets = 64; /* default number of pid hash buckets */ uint_t _dtrace_pidlrulim = 8; /* default number of pid handles to cache */ size_t _dtrace_bufsize = 512; /* default dt_buf_create() size */ int _dtrace_argmax = 32; /* default maximum number of probe arguments */ int _dtrace_debug = 0; /* debug messages enabled (off) */ const char *const _dtrace_version = DT_VERS_STRING; /* API version string */ int _dtrace_rdvers = RD_VERSION; /* rtld_db feature version */ typedef struct dt_fdlist { int *df_fds; /* array of provider driver file descriptors */ uint_t df_ents; /* number of valid elements in df_fds[] */ uint_t df_size; /* size of df_fds[] */ } dt_fdlist_t; #ifdef illumos #pragma init(_dtrace_init) #else void _dtrace_init(void) __attribute__ ((constructor)); #endif void _dtrace_init(void) { _dtrace_debug = getenv("DTRACE_DEBUG") != NULL; for (; _dtrace_rdvers > 0; _dtrace_rdvers--) { if (rd_init(_dtrace_rdvers) == RD_OK) break; } #if defined(__i386__) /* make long doubles 64 bits -sson */ (void) fpsetprec(FP_PE); #endif } static dtrace_hdl_t * set_open_errno(dtrace_hdl_t *dtp, int *errp, int err) { if (dtp != NULL) dtrace_close(dtp); if (errp != NULL) *errp = err; return (NULL); } static void dt_provmod_open(dt_provmod_t **provmod, dt_fdlist_t *dfp) { dt_provmod_t *prov; char path[PATH_MAX]; int fd; #ifdef illumos struct dirent *dp, *ep; DIR *dirp; if ((dirp = opendir(_dtrace_provdir)) == NULL) return; /* failed to open directory; just skip it */ ep = alloca(sizeof (struct dirent) + PATH_MAX + 1); bzero(ep, sizeof (struct dirent) + PATH_MAX + 1); while (readdir_r(dirp, ep, &dp) == 0 && dp != NULL) { if (dp->d_name[0] == '.') continue; /* skip "." and ".." */ if (dfp->df_ents == dfp->df_size) { uint_t size = dfp->df_size ? dfp->df_size * 2 : 16; int *fds = realloc(dfp->df_fds, size * sizeof (int)); if (fds == NULL) break; /* skip the rest of this directory */ dfp->df_fds = fds; dfp->df_size = size; } (void) snprintf(path, sizeof (path), "%s/%s", _dtrace_provdir, dp->d_name); if ((fd = open(path, O_RDONLY)) == -1) continue; /* failed to open driver; just skip it */ if (((prov = malloc(sizeof (dt_provmod_t))) == NULL) || (prov->dp_name = malloc(strlen(dp->d_name) + 1)) == NULL) { free(prov); (void) close(fd); break; } (void) strcpy(prov->dp_name, dp->d_name); prov->dp_next = *provmod; *provmod = prov; dt_dprintf("opened provider %s\n", dp->d_name); dfp->df_fds[dfp->df_ents++] = fd; } (void) closedir(dirp); #else /* !illumos */ char *p; char *p1; char *p_providers = NULL; int error; size_t len = 0; /* * Loop to allocate/reallocate memory for the string of provider * names and retry: */ while(1) { /* * The first time around, get the string length. The next time, * hopefully we've allocated enough memory. */ error = sysctlbyname("debug.dtrace.providers",p_providers,&len,NULL,0); if (len == 0) /* No providers? That's strange. Where's dtrace? */ break; else if (error == 0 && p_providers == NULL) { /* * Allocate the initial memory which should be enough * unless another provider loads before we have * time to go back and get the string. */ if ((p_providers = malloc(len)) == NULL) /* How do we report errors here? */ return; } else if (error == -1 && errno == ENOMEM) { /* * The current buffer isn't large enough, so * reallocate it. We normally won't need to do this * because providers aren't being loaded all the time. */ if ((p = realloc(p_providers,len)) == NULL) { free(p_providers); /* How do we report errors here? */ return; } p_providers = p; } else break; } /* Check if we got a string of provider names: */ if (error == 0 && len > 0 && p_providers != NULL) { p = p_providers; /* * Parse the string containing the space separated * provider names. */ while ((p1 = strsep(&p," ")) != NULL) { if (dfp->df_ents == dfp->df_size) { uint_t size = dfp->df_size ? dfp->df_size * 2 : 16; int *fds = realloc(dfp->df_fds, size * sizeof (int)); if (fds == NULL) break; dfp->df_fds = fds; dfp->df_size = size; } (void) snprintf(path, sizeof (path), "/dev/dtrace/%s", p1); if ((fd = open(path, O_RDONLY | O_CLOEXEC)) == -1) continue; /* failed to open driver; just skip it */ if (((prov = malloc(sizeof (dt_provmod_t))) == NULL) || (prov->dp_name = malloc(strlen(p1) + 1)) == NULL) { free(prov); (void) close(fd); break; } (void) strcpy(prov->dp_name, p1); prov->dp_next = *provmod; *provmod = prov; dt_dprintf("opened provider %s\n", p1); dfp->df_fds[dfp->df_ents++] = fd; } } if (p_providers != NULL) free(p_providers); #endif /* illumos */ } 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; } #ifdef illumos static const char * dt_get_sysinfo(int cmd, char *buf, size_t len) { ssize_t rv = sysinfo(cmd, buf, len); char *p = buf; if (rv < 0 || rv > len) (void) snprintf(buf, len, "%s", "Unknown"); while ((p = strchr(p, '.')) != NULL) *p++ = '_'; return (buf); } #endif static dtrace_hdl_t * dt_vopen(int version, int flags, int *errp, const dtrace_vector_t *vector, void *arg) { dtrace_hdl_t *dtp = NULL; int dtfd = -1, ftfd = -1, fterr = 0; dtrace_prog_t *pgp; dt_module_t *dmp; dt_provmod_t *provmod = NULL; int i, err; struct rlimit rl; const dt_intrinsic_t *dinp; const dt_typedef_t *dtyp; const dt_ident_t *idp; dtrace_typeinfo_t dtt; ctf_funcinfo_t ctc; ctf_arinfo_t ctr; dt_fdlist_t df = { NULL, 0, 0 }; char isadef[32], utsdef[32]; char s1[64], s2[64]; if (version <= 0) return (set_open_errno(dtp, errp, EINVAL)); if (version > DTRACE_VERSION) return (set_open_errno(dtp, errp, EDT_VERSION)); if (version < DTRACE_VERSION) { /* * Currently, increasing the library version number is used to * denote a binary incompatible change. That is, a consumer * of the library cannot run on a version of the library with * a higher DTRACE_VERSION number than the consumer compiled * against. Once the library API has been committed to, * backwards binary compatibility will be required; at that * time, this check should change to return EDT_OVERSION only * if the specified version number is less than the version * number at the time of interface commitment. */ return (set_open_errno(dtp, errp, EDT_OVERSION)); } if (flags & ~DTRACE_O_MASK) return (set_open_errno(dtp, errp, EINVAL)); if ((flags & DTRACE_O_LP64) && (flags & DTRACE_O_ILP32)) return (set_open_errno(dtp, errp, EINVAL)); if (vector == NULL && arg != NULL) return (set_open_errno(dtp, errp, EINVAL)); if (elf_version(EV_CURRENT) == EV_NONE) return (set_open_errno(dtp, errp, EDT_ELFVERSION)); if (vector != NULL || (flags & DTRACE_O_NODEV)) goto alloc; /* do not attempt to open dtrace device */ /* * Before we get going, crank our limit on file descriptors up to the * hard limit. This is to allow for the fact that libproc keeps file * descriptors to objects open for the lifetime of the proc handle; * without raising our hard limit, we would have an acceptably small * bound on the number of processes that we could concurrently * instrument with the pid provider. */ if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { rl.rlim_cur = rl.rlim_max; (void) setrlimit(RLIMIT_NOFILE, &rl); } /* * Get the device path of each of the providers. We hold them open * in the df.df_fds list until we open the DTrace driver itself, * allowing us to see all of the probes provided on this system. Once * we have the DTrace driver open, we can safely close all the providers * now that they have registered with the framework. */ dt_provmod_open(&provmod, &df); dtfd = open("/dev/dtrace/dtrace", O_RDWR | O_CLOEXEC); err = dtfd == -1 ? errno : 0; /* save errno from opening dtfd */ #if defined(__FreeBSD__) /* * Automatically load the 'dtraceall' module if we couldn't open the * char device. */ if (err == ENOENT && modfind("dtraceall") < 0) { kldload("dtraceall"); /* ignore the error */ #if __SIZEOF_LONG__ == 8 if (modfind("linux64elf") >= 0) kldload("systrace_linux"); if (modfind("linuxelf") >= 0) kldload("systrace_linux32"); #else if (modfind("linuxelf") >= 0) kldload("systrace_linux"); #endif dtfd = open("/dev/dtrace/dtrace", O_RDWR | O_CLOEXEC); err = errno; } #endif #ifdef illumos ftfd = open("/dev/dtrace/provider/fasttrap", O_RDWR); #else ftfd = open("/dev/dtrace/fasttrap", O_RDWR | O_CLOEXEC); #endif fterr = ftfd == -1 ? errno : 0; /* save errno from open ftfd */ while (df.df_ents-- != 0) (void) close(df.df_fds[df.df_ents]); free(df.df_fds); /* * If we failed to open the dtrace device, fail dtrace_open(). * We convert some kernel errnos to custom libdtrace errnos to * improve the resulting message from the usual strerror(). */ if (dtfd == -1) { dt_provmod_destroy(&provmod); switch (err) { case ENOENT: err = EDT_NOENT; break; case EBUSY: err = EDT_BUSY; break; case EACCES: err = EDT_ACCESS; break; } return (set_open_errno(dtp, errp, err)); } alloc: if ((dtp = malloc(sizeof (dtrace_hdl_t))) == NULL) { dt_provmod_destroy(&provmod); return (set_open_errno(dtp, errp, EDT_NOMEM)); } bzero(dtp, sizeof (dtrace_hdl_t)); dtp->dt_oflags = flags; #ifdef illumos dtp->dt_prcmode = DT_PROC_STOP_PREINIT; #else dtp->dt_prcmode = DT_PROC_STOP_POSTINIT; #endif dtp->dt_linkmode = DT_LINK_KERNEL; dtp->dt_linktype = DT_LTYP_ELF; dtp->dt_xlatemode = DT_XL_STATIC; dtp->dt_stdcmode = DT_STDC_XA; dtp->dt_encoding = DT_ENCODING_UNSET; dtp->dt_version = version; dtp->dt_fd = dtfd; dtp->dt_ftfd = ftfd; dtp->dt_kinstfd = -1; dtp->dt_fterr = fterr; dtp->dt_cdefs_fd = -1; dtp->dt_ddefs_fd = -1; #ifdef illumos dtp->dt_stdout_fd = -1; #else dtp->dt_freopen_fp = NULL; #endif dtp->dt_modbuckets = _dtrace_strbuckets; dtp->dt_mods = calloc(dtp->dt_modbuckets, sizeof (dt_module_t *)); #ifdef __FreeBSD__ dtp->dt_kmods = calloc(dtp->dt_modbuckets, sizeof (dt_module_t *)); #endif dtp->dt_provbuckets = _dtrace_strbuckets; dtp->dt_provs = calloc(dtp->dt_provbuckets, sizeof (dt_provider_t *)); dt_proc_init(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); #ifdef __FreeBSD__ dtp->dt_objcopy_path = strdup(_dtrace_defobjcopy); #endif dtp->dt_provmod = provmod; dtp->dt_vector = vector; dtp->dt_varg = arg; dt_dof_init(dtp); (void) uname(&dtp->dt_uts); if (dtp->dt_mods == NULL || dtp->dt_provs == NULL || dtp->dt_procs == NULL || dtp->dt_proc_env == NULL || dtp->dt_ld_path == NULL || dtp->dt_cpp_path == NULL || #ifdef __FreeBSD__ dtp->dt_kmods == NULL || dtp->dt_objcopy_path == NULL || #endif dtp->dt_cpp_argv == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); for (i = 0; i < DTRACEOPT_MAX; i++) dtp->dt_options[i] = DTRACEOPT_UNSET; dtp->dt_cpp_argv[0] = (char *)strbasename(dtp->dt_cpp_path); #ifdef illumos (void) snprintf(isadef, sizeof (isadef), "-D__SUNW_D_%u", (uint_t)(sizeof (void *) * NBBY)); (void) snprintf(utsdef, sizeof (utsdef), "-D__%s_%s", dt_get_sysinfo(SI_SYSNAME, s1, sizeof (s1)), dt_get_sysinfo(SI_RELEASE, s2, sizeof (s2))); if (dt_cpp_add_arg(dtp, "-D__sun") == NULL || dt_cpp_add_arg(dtp, "-D__unix") == NULL || dt_cpp_add_arg(dtp, "-D__SVR4") == NULL || dt_cpp_add_arg(dtp, "-D__SUNW_D=1") == NULL || dt_cpp_add_arg(dtp, isadef) == NULL || dt_cpp_add_arg(dtp, utsdef) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); #endif if (flags & DTRACE_O_NODEV) bcopy(&_dtrace_conf, &dtp->dt_conf, sizeof (_dtrace_conf)); else if (dt_ioctl(dtp, DTRACEIOC_CONF, &dtp->dt_conf) != 0) return (set_open_errno(dtp, errp, errno)); if (flags & DTRACE_O_LP64) dtp->dt_conf.dtc_ctfmodel = CTF_MODEL_LP64; else if (flags & DTRACE_O_ILP32) dtp->dt_conf.dtc_ctfmodel = CTF_MODEL_ILP32; #ifdef __sparc /* * On SPARC systems, __sparc is always defined for * and __sparcv9 is defined if we are doing a 64-bit compile. */ if (dt_cpp_add_arg(dtp, "-D__sparc") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64 && dt_cpp_add_arg(dtp, "-D__sparcv9") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); #endif #ifdef illumos #ifdef __x86 /* * On x86 systems, __i386 is defined for for 32-bit * compiles and __amd64 is defined for 64-bit compiles. Unlike SPARC, * they are defined exclusive of one another (see PSARC 2004/619). */ if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) { if (dt_cpp_add_arg(dtp, "-D__amd64") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } else { if (dt_cpp_add_arg(dtp, "-D__i386") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } #endif #else #if defined(__amd64__) || defined(__i386__) if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) { if (dt_cpp_add_arg(dtp, "-m64") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } else { if (dt_cpp_add_arg(dtp, "-m32") == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } #endif #endif if (dtp->dt_conf.dtc_difversion < DIF_VERSION) return (set_open_errno(dtp, errp, EDT_DIFVERS)); if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_ILP32) bcopy(_dtrace_ints_32, dtp->dt_ints, sizeof (_dtrace_ints_32)); else bcopy(_dtrace_ints_64, dtp->dt_ints, sizeof (_dtrace_ints_64)); /* * On FreeBSD the kernel module name can't be hard-coded. The * 'kern.bootfile' sysctl value tells us exactly which file is being * used as the kernel. */ #ifndef illumos { char bootfile[MAXPATHLEN]; char *p; int i; size_t len = sizeof(bootfile); /* This call shouldn't fail, but use a default just in case. */ if (sysctlbyname("kern.bootfile", bootfile, &len, NULL, 0) != 0) strlcpy(bootfile, "kernel", sizeof(bootfile)); if ((p = strrchr(bootfile, '/')) != NULL) p++; else p = bootfile; /* * Format the global variables based on the kernel module name. */ snprintf(curthread_str, sizeof(curthread_str), "%s`struct thread *",p); snprintf(intmtx_str, sizeof(intmtx_str), "int(%s`struct mtx *)",p); snprintf(threadmtx_str, sizeof(threadmtx_str), "struct thread *(%s`struct mtx *)",p); snprintf(rwlock_str, sizeof(rwlock_str), "int(%s`struct rwlock *)",p); snprintf(sxlock_str, sizeof(sxlock_str), "int(%s`struct sx *)",p); } #endif dtp->dt_macros = dt_idhash_create("macro", NULL, 0, UINT_MAX); dtp->dt_aggs = dt_idhash_create("aggregation", NULL, DTRACE_AGGVARIDNONE + 1, UINT_MAX); dtp->dt_globals = dt_idhash_create("global", _dtrace_globals, DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); dtp->dt_tls = dt_idhash_create("thread local", NULL, DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); if (dtp->dt_macros == NULL || dtp->dt_aggs == NULL || dtp->dt_globals == NULL || dtp->dt_tls == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); /* * Populate the dt_macros identifier hash table by hand: we can't use * the dt_idhash_populate() mechanism because we're not yet compiling * and dtrace_update() needs to immediately reference these idents. */ for (idp = _dtrace_macros; idp->di_name != NULL; idp++) { if (dt_idhash_insert(dtp->dt_macros, idp->di_name, idp->di_kind, idp->di_flags, idp->di_id, idp->di_attr, idp->di_vers, idp->di_ops ? idp->di_ops : &dt_idops_thaw, idp->di_iarg, 0) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); } /* * Update the module list using /system/object and load the values for * the macro variable definitions according to the current process. */ dtrace_update(dtp); /* * Select the intrinsics and typedefs we want based on the data model. * The intrinsics are under "C". The typedefs are added under "D". */ if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_ILP32) { dinp = _dtrace_intrinsics_32; dtyp = _dtrace_typedefs_32; } else { dinp = _dtrace_intrinsics_64; dtyp = _dtrace_typedefs_64; } /* * Create a dynamic CTF container under the "C" scope for intrinsic * types and types defined in ANSI-C header files that are included. */ if ((dmp = dtp->dt_cdefs = dt_module_create(dtp, "C")) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if ((dmp->dm_ctfp = ctf_create(&dtp->dt_ctferr)) == NULL) return (set_open_errno(dtp, errp, EDT_CTF)); dt_dprintf("created CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); (void) ctf_setmodel(dmp->dm_ctfp, dtp->dt_conf.dtc_ctfmodel); ctf_setspecific(dmp->dm_ctfp, dmp); dmp->dm_flags = DT_DM_LOADED; /* fake up loaded bit */ dmp->dm_modid = -1; /* no module ID */ /* * Fill the dynamic "C" CTF container with all of the intrinsic * integer and floating-point types appropriate for this data model. */ for (; dinp->din_name != NULL; dinp++) { if (dinp->din_kind == CTF_K_INTEGER) { err = ctf_add_integer(dmp->dm_ctfp, CTF_ADD_ROOT, dinp->din_name, &dinp->din_data); } else { err = ctf_add_float(dmp->dm_ctfp, CTF_ADD_ROOT, dinp->din_name, &dinp->din_data); } if (err == CTF_ERR) { dt_dprintf("failed to add %s to C container: %s\n", dinp->din_name, ctf_errmsg( ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } } if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed to update C container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Add intrinsic pointer types that are needed to initialize printf * format dictionary types (see table in dt_printf.c). */ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "void")); (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "char")); (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, ctf_lookup_by_name(dmp->dm_ctfp, "int")); if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed to update C container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Create a dynamic CTF container under the "D" scope for types that * are defined by the D program itself or on-the-fly by the D compiler. * The "D" CTF container is a child of the "C" CTF container. */ if ((dmp = dtp->dt_ddefs = dt_module_create(dtp, "D")) == NULL) return (set_open_errno(dtp, errp, EDT_NOMEM)); if ((dmp->dm_ctfp = ctf_create(&dtp->dt_ctferr)) == NULL) return (set_open_errno(dtp, errp, EDT_CTF)); dt_dprintf("created CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); (void) ctf_setmodel(dmp->dm_ctfp, dtp->dt_conf.dtc_ctfmodel); ctf_setspecific(dmp->dm_ctfp, dmp); dmp->dm_flags = DT_DM_LOADED; /* fake up loaded bit */ dmp->dm_modid = -1; /* no module ID */ if (ctf_import(dmp->dm_ctfp, dtp->dt_cdefs->dm_ctfp) == CTF_ERR) { dt_dprintf("failed to import D parent container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Fill the dynamic "D" CTF container with all of the built-in typedefs * that we need to use for our D variable and function definitions. * This ensures that basic inttypes.h names are always available to us. */ for (; dtyp->dty_src != NULL; dtyp++) { if (ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, dtyp->dty_dst, ctf_lookup_by_name(dmp->dm_ctfp, dtyp->dty_src)) == CTF_ERR) { dt_dprintf("failed to add typedef %s %s to D " "container: %s\n", dtyp->dty_src, dtyp->dty_dst, ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } } /* * Insert a CTF ID corresponding to a pointer to a type of kind * CTF_K_FUNCTION we can use in the compiler for function pointers. * CTF treats all function pointers as "int (*)()" so we only need one. */ ctc.ctc_return = ctf_lookup_by_name(dmp->dm_ctfp, "int"); ctc.ctc_argc = 0; ctc.ctc_flags = 0; dtp->dt_type_func = ctf_add_function(dmp->dm_ctfp, CTF_ADD_ROOT, &ctc, NULL); dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, dtp->dt_type_func); /* * We also insert CTF definitions for the special D intrinsic types * string and into the D container. The string type is added * as a typedef of char[n]. The type is an alias for void. * We compare types to these special CTF ids throughout the compiler. */ ctr.ctr_contents = ctf_lookup_by_name(dmp->dm_ctfp, "char"); ctr.ctr_index = ctf_lookup_by_name(dmp->dm_ctfp, "long"); ctr.ctr_nelems = _dtrace_strsize; dtp->dt_type_str = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "string", ctf_add_array(dmp->dm_ctfp, CTF_ADD_ROOT, &ctr)); dtp->dt_type_dyn = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_stack = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "stack", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_symaddr = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "_symaddr", ctf_lookup_by_name(dmp->dm_ctfp, "void")); dtp->dt_type_usymaddr = ctf_add_typedef(dmp->dm_ctfp, CTF_ADD_ROOT, "_usymaddr", ctf_lookup_by_name(dmp->dm_ctfp, "void")); if (dtp->dt_type_func == CTF_ERR || dtp->dt_type_fptr == CTF_ERR || dtp->dt_type_str == CTF_ERR || dtp->dt_type_dyn == CTF_ERR || dtp->dt_type_stack == CTF_ERR || dtp->dt_type_symaddr == CTF_ERR || dtp->dt_type_usymaddr == CTF_ERR) { dt_dprintf("failed to add intrinsic to D container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } if (ctf_update(dmp->dm_ctfp) != 0) { dt_dprintf("failed update D container: %s\n", ctf_errmsg(ctf_errno(dmp->dm_ctfp))); return (set_open_errno(dtp, errp, EDT_CTF)); } /* * Initialize the integer description table used to convert integer * constants to the appropriate types. Refer to the comments above * dt_node_int() for a complete description of how this table is used. */ for (i = 0; i < sizeof (dtp->dt_ints) / sizeof (dtp->dt_ints[0]); i++) { if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY, dtp->dt_ints[i].did_name, &dtt) != 0) { dt_dprintf("failed to lookup integer type %s: %s\n", dtp->dt_ints[i].did_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); return (set_open_errno(dtp, errp, dtp->dt_errno)); } dtp->dt_ints[i].did_ctfp = dtt.dtt_ctfp; dtp->dt_ints[i].did_type = dtt.dtt_type; } /* * Now that we've created the "C" and "D" containers, move them to the * start of the module list so that these types and symbols are found * first (for stability) when iterating through the module list. */ dt_list_delete(&dtp->dt_modlist, dtp->dt_ddefs); dt_list_prepend(&dtp->dt_modlist, dtp->dt_ddefs); dt_list_delete(&dtp->dt_modlist, dtp->dt_cdefs); dt_list_prepend(&dtp->dt_modlist, dtp->dt_cdefs); if (dt_pfdict_create(dtp) == -1) return (set_open_errno(dtp, errp, dtp->dt_errno)); /* * If we are opening libdtrace DTRACE_O_NODEV enable C_ZDEFS by default * because without /dev/dtrace open, we will not be able to load the * names and attributes of any providers or probes from the kernel. */ if (flags & DTRACE_O_NODEV) dtp->dt_cflags |= DTRACE_C_ZDEFS; /* * Load hard-wired inlines into the definition cache by calling the * compiler on the raw definition string defined above. */ if ((pgp = dtrace_program_strcompile(dtp, _dtrace_hardwire, DTRACE_PROBESPEC_NONE, DTRACE_C_EMPTY, 0, NULL)) == NULL) { dt_dprintf("failed to load hard-wired definitions: %s\n", dtrace_errmsg(dtp, dtrace_errno(dtp))); return (set_open_errno(dtp, errp, EDT_HARDWIRE)); } dt_program_destroy(dtp, pgp); /* * Set up the default DTrace library path. Once set, the next call to * dt_compile() will compile all the libraries. We intentionally defer * library processing to improve overhead for clients that don't ever * compile, and to provide better error reporting (because the full * reporting of compiler errors requires dtrace_open() to succeed). */ #ifdef __FreeBSD__ #ifdef __LP64__ if ((dtp->dt_oflags & DTRACE_O_ILP32) != 0) { if (dtrace_setopt(dtp, "libdir", _dtrace_libdir32) != 0) return (set_open_errno(dtp, errp, dtp->dt_errno)); } #endif if (dtrace_setopt(dtp, "libdir", _dtrace_libdir) != 0) return (set_open_errno(dtp, errp, dtp->dt_errno)); #else 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; #ifdef __FreeBSD__ dt_kmodule_t *dkm; uint_t h; #endif int i; if (dtp->dt_procs != NULL) dt_proc_fini(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); #ifdef __FreeBSD__ for (h = 0; h < dtp->dt_modbuckets; h++) while ((dkm = dtp->dt_kmods[h]) != NULL) { dtp->dt_kmods[h] = dkm->dkm_next; free(dkm->dkm_name); free(dkm); } #endif 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_kinstfd != -1) (void) close(dtp->dt_kinstfd); if (dtp->dt_cdefs_fd != -1) (void) close(dtp->dt_cdefs_fd); if (dtp->dt_ddefs_fd != -1) (void) close(dtp->dt_ddefs_fd); #ifdef illumos if (dtp->dt_stdout_fd != -1) (void) close(dtp->dt_stdout_fd); #else if (dtp->dt_freopen_fp != NULL) (void) fclose(dtp->dt_freopen_fp); #endif dt_epid_destroy(dtp); dt_aggid_destroy(dtp); dt_format_destroy(dtp); dt_strdata_destroy(dtp); dt_buffered_destroy(dtp); dt_aggregate_destroy(dtp); dt_pfdict_destroy(dtp); dt_provmod_destroy(&dtp->dt_provmod); dt_dof_fini(dtp); for (i = 1; i < dtp->dt_cpp_argc; i++) free(dtp->dt_cpp_argv[i]); while ((dirp = dt_list_next(&dtp->dt_lib_path)) != NULL) { dt_list_delete(&dtp->dt_lib_path, dirp); free(dirp->dir_path); free(dirp); } free(dtp->dt_cpp_argv); free(dtp->dt_cpp_path); free(dtp->dt_ld_path); #ifdef __FreeBSD__ free(dtp->dt_objcopy_path); #endif free(dtp->dt_mods); #ifdef __FreeBSD__ free(dtp->dt_kmods); #endif free(dtp->dt_provs); + + xo_finish(); 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/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c index ce13659f1685..1e7a8115c960 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_options.c @@ -1,1139 +1,1153 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include +#include static int dt_opt_agg(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dt_aggregate_t *agp = &dtp->dt_aggregate; if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); agp->dtat_flags |= option; return (0); } /*ARGSUSED*/ static int dt_opt_amin(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char str[DTRACE_ATTR2STR_MAX]; dtrace_attribute_t attr; if (arg == NULL || dtrace_str2attr(arg, &attr) == -1) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dt_dprintf("set compiler attribute minimum to %s\n", dtrace_attr2str(attr, str, sizeof (str))); if (dtp->dt_pcb != NULL) { dtp->dt_pcb->pcb_cflags |= DTRACE_C_EATTR; dtp->dt_pcb->pcb_amin = attr; } else { dtp->dt_cflags |= DTRACE_C_EATTR; dtp->dt_amin = attr; } return (0); } static void dt_coredump(void) { const char msg[] = "libdtrace DEBUG: [ forcing coredump ]\n"; struct sigaction act; struct rlimit lim; (void) write(STDERR_FILENO, msg, sizeof (msg) - 1); act.sa_handler = SIG_DFL; act.sa_flags = 0; (void) sigemptyset(&act.sa_mask); (void) sigaction(SIGABRT, &act, NULL); lim.rlim_cur = RLIM_INFINITY; lim.rlim_max = RLIM_INFINITY; (void) setrlimit(RLIMIT_CORE, &lim); abort(); } /*ARGSUSED*/ static int dt_opt_core(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { static int enabled = 0; if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (enabled++ || atexit(dt_coredump) == 0) return (0); return (dt_set_errno(dtp, errno)); } /*ARGSUSED*/ static int dt_opt_cpp_hdrs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if (dt_cpp_add_arg(dtp, "-H") == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); return (0); } /*ARGSUSED*/ static int dt_opt_cpp_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *cpp; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if ((cpp = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); dtp->dt_cpp_argv[0] = (char *)strbasename(cpp); free(dtp->dt_cpp_path); dtp->dt_cpp_path = cpp; return (0); } static int dt_opt_cpp_opts(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *buf = NULL; size_t len; const char *opt = (const char *)option; int ret; if (opt == NULL || arg == NULL) { ret = dt_set_errno(dtp, EDT_BADOPTVAL); goto out; } if (dtp->dt_pcb != NULL) { ret = dt_set_errno(dtp, EDT_BADOPTCTX); goto out; } len = strlen(opt) + strlen(arg) + 1; if ((buf = dt_alloc(dtp, len)) == NULL) { ret = dt_set_errno(dtp, EDT_NOMEM); goto out; } (void) strcpy(buf, opt); (void) strcat(buf, arg); if (dt_cpp_add_arg(dtp, buf) == NULL) { ret = dt_set_errno(dtp, EDT_NOMEM); goto out; } ret = 0; out: if (buf != NULL) dt_free(dtp, buf); return (ret); } /*ARGSUSED*/ static int dt_opt_ctypes(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int fd; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if ((fd = open64(arg, O_CREAT | O_WRONLY, 0666)) == -1) return (dt_set_errno(dtp, errno)); (void) close(dtp->dt_cdefs_fd); dtp->dt_cdefs_fd = fd; return (0); } /*ARGSUSED*/ static int dt_opt_droptags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtp->dt_droptags = 1; return (0); } /*ARGSUSED*/ static int dt_opt_dtypes(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int fd; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if ((fd = open64(arg, O_CREAT | O_WRONLY, 0666)) == -1) return (dt_set_errno(dtp, errno)); (void) close(dtp->dt_ddefs_fd); dtp->dt_ddefs_fd = fd; return (0); } /*ARGSUSED*/ static int dt_opt_debug(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); _dtrace_debug = 1; return (0); } /*ARGSUSED*/ static int dt_opt_iregs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int n; if (arg == NULL || (n = atoi(arg)) <= 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_conf.dtc_difintregs = n; return (0); } /*ARGSUSED*/ static int dt_opt_lazyload(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtp->dt_lazyload = 1; return (0); } /*ARGSUSED*/ static int dt_opt_ld_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *ld; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if ((ld = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); free(dtp->dt_ld_path); dtp->dt_ld_path = ld; return (0); } #ifdef __FreeBSD__ static int dt_opt_objcopy_path(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *objcopy; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if ((objcopy = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); free(dtp->dt_objcopy_path); dtp->dt_objcopy_path = objcopy; return (0); } #endif /*ARGSUSED*/ static int dt_opt_libdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dt_dirpath_t *dp; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if ((dp = malloc(sizeof (dt_dirpath_t))) == NULL || (dp->dir_path = strdup(arg)) == NULL) { free(dp); return (dt_set_errno(dtp, EDT_NOMEM)); } dt_list_append(&dtp->dt_lib_path, dp); return (0); } /*ARGSUSED*/ static int dt_opt_linkmode(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcmp(arg, "kernel") == 0) dtp->dt_linkmode = DT_LINK_KERNEL; else if (strcmp(arg, "primary") == 0) dtp->dt_linkmode = DT_LINK_PRIMARY; else if (strcmp(arg, "dynamic") == 0) dtp->dt_linkmode = DT_LINK_DYNAMIC; else if (strcmp(arg, "static") == 0) dtp->dt_linkmode = DT_LINK_STATIC; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_linktype(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcasecmp(arg, "elf") == 0) dtp->dt_linktype = DT_LTYP_ELF; else if (strcasecmp(arg, "dof") == 0) dtp->dt_linktype = DT_LTYP_DOF; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_encoding(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcmp(arg, "ascii") == 0) dtp->dt_encoding = DT_ENCODING_ASCII; else if (strcmp(arg, "utf8") == 0) dtp->dt_encoding = DT_ENCODING_UTF8; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_evaltime(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcmp(arg, "exec") == 0) dtp->dt_prcmode = DT_PROC_STOP_CREATE; else if (strcmp(arg, "preinit") == 0) dtp->dt_prcmode = DT_PROC_STOP_PREINIT; else if (strcmp(arg, "postinit") == 0) dtp->dt_prcmode = DT_PROC_STOP_POSTINIT; else if (strcmp(arg, "main") == 0) dtp->dt_prcmode = DT_PROC_STOP_MAIN; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_pgmax(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int n; if (arg == NULL || (n = atoi(arg)) < 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_procs->dph_lrulim = n; return (0); } static int dt_opt_setenv(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char **p; char *var; int nvars; /* * We can't effectively set environment variables from #pragma lines * since the processes have already been spawned. */ if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (!option && strchr(arg, '=') != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); for (nvars = 0, p = dtp->dt_proc_env; *p != NULL; nvars++, p++) continue; for (p = dtp->dt_proc_env; *p != NULL; p++) { var = strchr(*p, '='); if (var == NULL) var = *p + strlen(*p); if (strncmp(*p, arg, var - *p) == 0) { dt_free(dtp, *p); *p = dtp->dt_proc_env[nvars - 1]; dtp->dt_proc_env[nvars - 1] = NULL; nvars--; } } if (option) { if ((var = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); nvars++; if ((p = dt_alloc(dtp, sizeof(char *) * (nvars + 1))) == NULL) { dt_free(dtp, var); return (dt_set_errno(dtp, EDT_NOMEM)); } bcopy(dtp->dt_proc_env, p, sizeof(char *) * nvars); dt_free(dtp, dtp->dt_proc_env); dtp->dt_proc_env = p; dtp->dt_proc_env[nvars - 1] = var; dtp->dt_proc_env[nvars] = NULL; } return (0); } /*ARGSUSED*/ static int dt_opt_stdc(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) return (dt_set_errno(dtp, EDT_BADOPTCTX)); if (strcmp(arg, "a") == 0) dtp->dt_stdcmode = DT_STDC_XA; else if (strcmp(arg, "c") == 0) dtp->dt_stdcmode = DT_STDC_XC; else if (strcmp(arg, "s") == 0) dtp->dt_stdcmode = DT_STDC_XS; else if (strcmp(arg, "t") == 0) dtp->dt_stdcmode = DT_STDC_XT; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_syslibdir(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dt_dirpath_t *dp = dt_list_next(&dtp->dt_lib_path); char *path; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if ((path = strdup(arg)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); free(dp->dir_path); dp->dir_path = path; return (0); } /*ARGSUSED*/ static int dt_opt_tree(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int m; if (arg == NULL || (m = atoi(arg)) <= 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_treedump = m; return (0); } /*ARGSUSED*/ static int dt_opt_tregs(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { int n; if (arg == NULL || (n = atoi(arg)) <= 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_conf.dtc_diftupregs = n; return (0); } /*ARGSUSED*/ static int dt_opt_xlate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (strcmp(arg, "dynamic") == 0) dtp->dt_xlatemode = DT_XL_DYNAMIC; else if (strcmp(arg, "static") == 0) dtp->dt_xlatemode = DT_XL_STATIC; else return (dt_set_errno(dtp, EDT_BADOPTVAL)); return (0); } /*ARGSUSED*/ static int dt_opt_cflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) dtp->dt_pcb->pcb_cflags |= option; else dtp->dt_cflags |= option; return (0); } static int dt_opt_dflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_dflags |= option; return (0); } static int dt_opt_invcflags(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { if (arg != NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dtp->dt_pcb != NULL) dtp->dt_pcb->pcb_cflags &= ~option; else dtp->dt_cflags &= ~option; return (0); } /*ARGSUSED*/ static int dt_opt_version(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dt_version_t v; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (dt_version_str2num(arg, &v) == -1) return (dt_set_errno(dtp, EDT_VERSINVAL)); if (!dt_version_defined(v)) return (dt_set_errno(dtp, EDT_VERSUNDEF)); return (dt_reduce(dtp, v)); } static int dt_opt_runtime(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *end; dtrace_optval_t val = 0; int i; const struct { char *positive; char *negative; } couples[] = { { "yes", "no" }, { "enable", "disable" }, { "enabled", "disabled" }, { "true", "false" }, { "on", "off" }, { "set", "unset" }, { NULL } }; if (arg != NULL) { if (arg[0] == '\0') { val = DTRACEOPT_UNSET; goto out; } for (i = 0; couples[i].positive != NULL; i++) { if (strcasecmp(couples[i].positive, arg) == 0) { val = 1; goto out; } if (strcasecmp(couples[i].negative, arg) == 0) { val = DTRACEOPT_UNSET; goto out; } } errno = 0; val = strtoull(arg, &end, 0); if (*end != '\0' || errno != 0 || val < 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); } out: dtp->dt_options[option] = val; return (0); } static int dt_optval_parse(const char *arg, dtrace_optval_t *rval) { dtrace_optval_t mul = 1; size_t len; char *end; len = strlen(arg); errno = 0; switch (arg[len - 1]) { case 't': case 'T': mul *= 1024; /*FALLTHRU*/ case 'g': case 'G': mul *= 1024; /*FALLTHRU*/ case 'm': case 'M': mul *= 1024; /*FALLTHRU*/ case 'k': case 'K': mul *= 1024; /*FALLTHRU*/ default: break; } errno = 0; *rval = strtoull(arg, &end, 0) * mul; if ((mul > 1 && end != &arg[len - 1]) || (mul == 1 && *end != '\0') || *rval < 0 || errno != 0) return (-1); return (0); } static int dt_opt_size(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtrace_optval_t val = 0; if (arg != NULL && dt_optval_parse(arg, &val) != 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_options[option] = val; return (0); } +static int +dt_opt_oformat(dtrace_hdl_t *dtp, const char *arg, uintptr_t option __unused) +{ + if (arg == NULL) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + + if (xo_set_options(NULL, arg) < 0) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + + return (dtrace_oformat_configure(dtp)); +} + static int dt_opt_rate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *end; int i; dtrace_optval_t mul = 1, val = 0; const struct { char *name; hrtime_t mul; } suffix[] = { { "ns", NANOSEC / NANOSEC }, { "nsec", NANOSEC / NANOSEC }, { "us", NANOSEC / MICROSEC }, { "usec", NANOSEC / MICROSEC }, { "ms", NANOSEC / MILLISEC }, { "msec", NANOSEC / MILLISEC }, { "s", NANOSEC / SEC }, { "sec", NANOSEC / SEC }, { "m", NANOSEC * (hrtime_t)60 }, { "min", NANOSEC * (hrtime_t)60 }, { "h", NANOSEC * (hrtime_t)60 * (hrtime_t)60 }, { "hour", NANOSEC * (hrtime_t)60 * (hrtime_t)60 }, { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) }, { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) }, { "hz", 0 }, { NULL } }; if (arg != NULL) { errno = 0; val = strtoull(arg, &end, 0); for (i = 0; suffix[i].name != NULL; i++) { if (strcasecmp(suffix[i].name, end) == 0) { mul = suffix[i].mul; break; } } if (suffix[i].name == NULL && *end != '\0' || val < 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (mul == 0) { /* * The rate has been specified in frequency-per-second. */ if (val != 0) val = NANOSEC / val; } else { val *= mul; } } dtp->dt_options[option] = val; return (0); } /* * When setting the strsize option, set the option in the dt_options array * using dt_opt_size() as usual, and then update the definition of the CTF * type for the D intrinsic "string" to be an array of the corresponding size. * If any errors occur, reset dt_options[option] to its previous value. */ static int dt_opt_strsize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtrace_optval_t val = dtp->dt_options[option]; ctf_file_t *fp = DT_STR_CTFP(dtp); ctf_id_t type = ctf_type_resolve(fp, DT_STR_TYPE(dtp)); ctf_arinfo_t r; if (dt_opt_size(dtp, arg, option) != 0) return (-1); /* dt_errno is set for us */ if (dtp->dt_options[option] > UINT_MAX) { dtp->dt_options[option] = val; return (dt_set_errno(dtp, EOVERFLOW)); } if (ctf_array_info(fp, type, &r) == CTF_ERR) { dtp->dt_options[option] = val; dtp->dt_ctferr = ctf_errno(fp); return (dt_set_errno(dtp, EDT_CTF)); } r.ctr_nelems = (uint_t)dtp->dt_options[option]; if (ctf_set_array(fp, type, &r) == CTF_ERR || ctf_update(fp) == CTF_ERR) { dtp->dt_options[option] = val; dtp->dt_ctferr = ctf_errno(fp); return (dt_set_errno(dtp, EDT_CTF)); } return (0); } static const struct { const char *dtbp_name; int dtbp_policy; } _dtrace_bufpolicies[] = { { "ring", DTRACEOPT_BUFPOLICY_RING }, { "fill", DTRACEOPT_BUFPOLICY_FILL }, { "switch", DTRACEOPT_BUFPOLICY_SWITCH }, { NULL, 0 } }; /*ARGSUSED*/ static int dt_opt_bufpolicy(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtrace_optval_t policy = DTRACEOPT_UNSET; int i; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); for (i = 0; _dtrace_bufpolicies[i].dtbp_name != NULL; i++) { if (strcmp(_dtrace_bufpolicies[i].dtbp_name, arg) == 0) { policy = _dtrace_bufpolicies[i].dtbp_policy; break; } } if (policy == DTRACEOPT_UNSET) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_options[DTRACEOPT_BUFPOLICY] = policy; return (0); } static const struct { const char *dtbr_name; int dtbr_policy; } _dtrace_bufresize[] = { { "auto", DTRACEOPT_BUFRESIZE_AUTO }, { "manual", DTRACEOPT_BUFRESIZE_MANUAL }, { NULL, 0 } }; /*ARGSUSED*/ static int dt_opt_bufresize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { dtrace_optval_t policy = DTRACEOPT_UNSET; int i; if (arg == NULL) return (dt_set_errno(dtp, EDT_BADOPTVAL)); for (i = 0; _dtrace_bufresize[i].dtbr_name != NULL; i++) { if (strcmp(_dtrace_bufresize[i].dtbr_name, arg) == 0) { policy = _dtrace_bufresize[i].dtbr_policy; break; } } if (policy == DTRACEOPT_UNSET) return (dt_set_errno(dtp, EDT_BADOPTVAL)); dtp->dt_options[DTRACEOPT_BUFRESIZE] = policy; return (0); } int dt_options_load(dtrace_hdl_t *dtp) { dof_hdr_t hdr, *dof; dof_sec_t *sec; size_t offs; int i, ret; /* * 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. */ dof = &hdr; bzero(&hdr, sizeof (dof_hdr_t)); hdr.dofh_loadsz = sizeof (dof_hdr_t); #ifdef illumos if (dt_ioctl(dtp, DTRACEIOC_DOFGET, dof) == -1) #else if (dt_ioctl(dtp, DTRACEIOC_DOFGET, &dof) == -1) #endif { ret = dt_set_errno(dtp, errno); goto out; } if (hdr.dofh_loadsz < sizeof (dof_hdr_t)) { ret = dt_set_errno(dtp, EINVAL); goto out; } if ((dof = dt_alloc(dtp, hdr.dofh_loadsz)) == NULL) { ret = dt_set_errno(dtp, EDT_NOMEM); goto out; } bzero(dof, sizeof (dof_hdr_t)); dof->dofh_loadsz = hdr.dofh_loadsz; for (i = 0; i < DTRACEOPT_MAX; i++) dtp->dt_options[i] = DTRACEOPT_UNSET; #ifdef illumos if (dt_ioctl(dtp, DTRACEIOC_DOFGET, dof) == -1) #else if (dt_ioctl(dtp, DTRACEIOC_DOFGET, &dof) == -1) #endif { ret = dt_set_errno(dtp, errno); goto out; } 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; } ret = 0; out: if (dof != NULL && dof != &hdr) dt_free(dtp, dof); return (ret); } typedef struct dt_option { const char *o_name; int (*o_func)(dtrace_hdl_t *, const char *, uintptr_t); uintptr_t o_option; } dt_option_t; /* * Compile-time options. */ static const dt_option_t _dtrace_ctoptions[] = { { "aggpercpu", dt_opt_agg, DTRACE_A_PERCPU }, { "amin", dt_opt_amin }, { "argref", dt_opt_cflags, DTRACE_C_ARGREF }, { "core", dt_opt_core }, { "cpp", dt_opt_cflags, DTRACE_C_CPP }, { "cpphdrs", dt_opt_cpp_hdrs }, { "cpppath", dt_opt_cpp_path }, { "ctypes", dt_opt_ctypes }, { "defaultargs", dt_opt_cflags, DTRACE_C_DEFARG }, { "dtypes", dt_opt_dtypes }, { "debug", dt_opt_debug }, { "define", dt_opt_cpp_opts, (uintptr_t)"-D" }, { "droptags", dt_opt_droptags }, { "empty", dt_opt_cflags, DTRACE_C_EMPTY }, { "encoding", dt_opt_encoding }, { "errtags", dt_opt_cflags, DTRACE_C_ETAGS }, { "evaltime", dt_opt_evaltime }, { "incdir", dt_opt_cpp_opts, (uintptr_t)"-I" }, { "iregs", dt_opt_iregs }, { "kdefs", dt_opt_invcflags, DTRACE_C_KNODEF }, { "knodefs", dt_opt_cflags, DTRACE_C_KNODEF }, { "late", dt_opt_xlate }, { "lazyload", dt_opt_lazyload }, { "ldpath", dt_opt_ld_path }, { "libdir", dt_opt_libdir }, { "linkmode", dt_opt_linkmode }, { "linktype", dt_opt_linktype }, { "nolibs", dt_opt_cflags, DTRACE_C_NOLIBS }, #ifdef __FreeBSD__ { "objcopypath", dt_opt_objcopy_path }, #endif { "pgmax", dt_opt_pgmax }, { "pspec", dt_opt_cflags, DTRACE_C_PSPEC }, { "setenv", dt_opt_setenv, 1 }, { "stdc", dt_opt_stdc }, { "strip", dt_opt_dflags, DTRACE_D_STRIP }, { "syslibdir", dt_opt_syslibdir }, { "tree", dt_opt_tree }, { "tregs", dt_opt_tregs }, { "udefs", dt_opt_invcflags, DTRACE_C_UNODEF }, { "undef", dt_opt_cpp_opts, (uintptr_t)"-U" }, { "unodefs", dt_opt_cflags, DTRACE_C_UNODEF }, { "unsetenv", dt_opt_setenv, 0 }, { "verbose", dt_opt_cflags, DTRACE_C_DIFV }, { "version", dt_opt_version }, { "zdefs", dt_opt_cflags, DTRACE_C_ZDEFS }, { NULL, NULL, 0 } }; /* * Run-time options. */ static const dt_option_t _dtrace_rtoptions[] = { { "aggsize", dt_opt_size, DTRACEOPT_AGGSIZE }, { "bufsize", dt_opt_size, DTRACEOPT_BUFSIZE }, { "bufpolicy", dt_opt_bufpolicy, DTRACEOPT_BUFPOLICY }, { "bufresize", dt_opt_bufresize, DTRACEOPT_BUFRESIZE }, { "cleanrate", dt_opt_rate, DTRACEOPT_CLEANRATE }, { "cpu", dt_opt_runtime, DTRACEOPT_CPU }, { "destructive", dt_opt_runtime, DTRACEOPT_DESTRUCTIVE }, { "dynvarsize", dt_opt_size, DTRACEOPT_DYNVARSIZE }, { "grabanon", dt_opt_runtime, DTRACEOPT_GRABANON }, { "jstackframes", dt_opt_runtime, DTRACEOPT_JSTACKFRAMES }, { "jstackstrsize", dt_opt_size, DTRACEOPT_JSTACKSTRSIZE }, { "nspec", dt_opt_runtime, DTRACEOPT_NSPEC }, + { "oformat", dt_opt_oformat, 0 }, { "specsize", dt_opt_size, DTRACEOPT_SPECSIZE }, { "stackframes", dt_opt_runtime, DTRACEOPT_STACKFRAMES }, { "statusrate", dt_opt_rate, DTRACEOPT_STATUSRATE }, { "strsize", dt_opt_strsize, DTRACEOPT_STRSIZE }, { "ustackframes", dt_opt_runtime, DTRACEOPT_USTACKFRAMES }, { "temporal", dt_opt_runtime, DTRACEOPT_TEMPORAL }, { NULL, NULL, 0 } }; /* * Dynamic run-time options. */ static const dt_option_t _dtrace_drtoptions[] = { { "agghist", dt_opt_runtime, DTRACEOPT_AGGHIST }, { "aggpack", dt_opt_runtime, DTRACEOPT_AGGPACK }, { "aggrate", dt_opt_rate, DTRACEOPT_AGGRATE }, { "aggsortkey", dt_opt_runtime, DTRACEOPT_AGGSORTKEY }, { "aggsortkeypos", dt_opt_runtime, DTRACEOPT_AGGSORTKEYPOS }, { "aggsortpos", dt_opt_runtime, DTRACEOPT_AGGSORTPOS }, { "aggsortrev", dt_opt_runtime, DTRACEOPT_AGGSORTREV }, { "aggzoom", dt_opt_runtime, DTRACEOPT_AGGZOOM }, { "flowindent", dt_opt_runtime, DTRACEOPT_FLOWINDENT }, { "quiet", dt_opt_runtime, DTRACEOPT_QUIET }, { "rawbytes", dt_opt_runtime, DTRACEOPT_RAWBYTES }, { "stackindent", dt_opt_runtime, DTRACEOPT_STACKINDENT }, { "switchrate", dt_opt_rate, DTRACEOPT_SWITCHRATE }, { NULL, NULL, 0 } }; int dtrace_getopt(dtrace_hdl_t *dtp, const char *opt, dtrace_optval_t *val) { const dt_option_t *op; if (opt == NULL) return (dt_set_errno(dtp, EINVAL)); /* * We only need to search the run-time options -- it's not legal * to get the values of compile-time options. */ for (op = _dtrace_rtoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) { *val = dtp->dt_options[op->o_option]; return (0); } } for (op = _dtrace_drtoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) { *val = dtp->dt_options[op->o_option]; return (0); } } return (dt_set_errno(dtp, EDT_BADOPTNAME)); } int dtrace_setopt(dtrace_hdl_t *dtp, const char *opt, const char *val) { const dt_option_t *op; if (opt == NULL) return (dt_set_errno(dtp, EINVAL)); for (op = _dtrace_ctoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) return (op->o_func(dtp, val, op->o_option)); } for (op = _dtrace_drtoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) return (op->o_func(dtp, val, op->o_option)); } for (op = _dtrace_rtoptions; op->o_name != NULL; op++) { if (strcmp(op->o_name, opt) == 0) { /* * Only dynamic run-time options may be set while * tracing is active. */ if (dtp->dt_active) return (dt_set_errno(dtp, EDT_ACTIVE)); return (op->o_func(dtp, val, op->o_option)); } } return (dt_set_errno(dtp, EDT_BADOPTNAME)); } diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c index 97da0c3a5ac2..9bbd12e5544f 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c @@ -1,705 +1,845 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2011 by Delphix. All rights reserved. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2023, Domagoj Stolfa. All rights reserved. */ /* * DTrace print() action * * This file contains the post-processing logic for the print() action. The * print action behaves identically to trace() in that it generates a * DTRACEACT_DIFEXPR action, but the action argument field refers to a CTF type * string stored in the DOF string table (similar to printf formats). We * take the result of the trace action and post-process it in the fashion of * MDB's ::print dcmd. * * This implementation differs from MDB's in the following ways: * * - We do not expose any options or flags. The behavior of print() is * equivalent to "::print -tn". * * - MDB will display "holes" in structures (unused padding between * members). * * - When printing arrays of structures, MDB will leave a trailing ',' * after the last element. * * - MDB will print time_t types as date and time. * * - MDB will detect when an enum is actually the OR of several flags, * and print it out with the constituent flags separated. * * - For large arrays, MDB will print the first few members and then * print a "..." continuation line. * * - MDB will break and wrap arrays at 80 columns. * * - MDB prints out floats and doubles by hand, as it must run in kmdb * context. We're able to leverage the printf() format strings, * but the result is a slightly different format. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include /* determines whether the given integer CTF encoding is a character */ #define CTF_IS_CHAR(e) \ (((e).cte_format & (CTF_INT_CHAR | CTF_INT_SIGNED)) == \ (CTF_INT_CHAR | CTF_INT_SIGNED) && (e).cte_bits == NBBY) /* determines whether the given CTF kind is a struct or union */ #define CTF_IS_STRUCTLIKE(k) \ ((k) == CTF_K_STRUCT || (k) == CTF_K_UNION) /* * Print structure passed down recursively through printing algorithm. */ typedef struct dt_printarg { dtrace_hdl_t *pa_dtp; /* libdtrace handle */ caddr_t pa_addr; /* base address of trace data */ ctf_file_t *pa_ctfp; /* CTF container */ int pa_depth; /* member depth */ int pa_nest; /* nested array depth */ FILE *pa_file; /* output file */ + const char *pa_object; /* object name */ } dt_printarg_t; +static int dt_format_member(const char *, ctf_id_t, ulong_t, int, void *); static int dt_print_member(const char *, ctf_id_t, ulong_t, int, void *); /* * Safe version of ctf_type_name() that will fall back to just "" if it * can't resolve the type. */ static void dt_print_type_name(ctf_file_t *ctfp, ctf_id_t id, char *buf, size_t buflen) { if (ctf_type_name(ctfp, id, buf, buflen) == NULL) (void) snprintf(buf, buflen, "<%ld>", id); } /* * Print any necessary trailing braces for structures or unions. We don't get * invoked when a struct or union ends, so we infer the need to print braces * based on the depth the last time we printed something and the new depth. */ static void dt_print_trailing_braces(dt_printarg_t *pap, int depth) { int d; for (d = pap->pa_depth; d > depth; d--) { (void) fprintf(pap->pa_file, "%*s}%s", (d + pap->pa_nest - 1) * 4, "", d == depth + 1 ? "" : "\n"); } } /* * Print the appropriate amount of indentation given the current depth and * array nesting. */ static void dt_print_indent(dt_printarg_t *pap) { (void) fprintf(pap->pa_file, "%*s", (pap->pa_depth + pap->pa_nest) * 4, ""); } /* * Print a bitfield. It's worth noting that the D compiler support for * bitfields is currently broken; printing "D`user_desc_t" (pulled in by the * various D provider files) will produce incorrect results compared to * "genunix`user_desc_t". */ static void print_bitfield(dt_printarg_t *pap, ulong_t off, ctf_encoding_t *ep) { FILE *fp = pap->pa_file; caddr_t addr = pap->pa_addr + off / NBBY; uint64_t mask = (1ULL << ep->cte_bits) - 1; uint64_t value = 0; size_t size = (ep->cte_bits + (NBBY - 1)) / NBBY; uint8_t *buf = (uint8_t *)&value; uint8_t shift; /* * On big-endian machines, we need to adjust the buf pointer to refer * to the lowest 'size' bytes in 'value', and we need to shift based on * the offset from the end of the data, not the offset of the start. */ #if BYTE_ORDER == _BIG_ENDIAN buf += sizeof (value) - size; off += ep->cte_bits; #endif bcopy(addr, buf, size); shift = off % NBBY; /* * Offsets are counted from opposite ends on little- and * big-endian machines. */ #if BYTE_ORDER == _BIG_ENDIAN shift = NBBY - shift; #endif /* * If the bits we want do not begin on a byte boundary, shift the data * right so that the value is in the lowest 'cte_bits' of 'value'. */ if (off % NBBY != 0) value >>= shift; value &= mask; - (void) fprintf(fp, "%#llx", (u_longlong_t)value); + xo_emit("{:value/%#llx}", (u_longlong_t)value); + + /* Flush in order to ensure output is aligned properly */ + xo_flush(); } /* * Dump the contents of memory as a fixed-size integer in hex. */ static void dt_print_hex(FILE *fp, caddr_t addr, size_t size) { switch (size) { case sizeof (uint8_t): - (void) fprintf(fp, "%#x", *(uint8_t *)addr); + xo_emit("{:value/%#x}", *(uint8_t *)addr); break; case sizeof (uint16_t): - /* LINTED - alignment */ - (void) fprintf(fp, "%#x", *(uint16_t *)addr); + xo_emit("{:value/%#x}", *(uint16_t *)addr); break; case sizeof (uint32_t): - /* LINTED - alignment */ - (void) fprintf(fp, "%#x", *(uint32_t *)addr); + xo_emit("{:value/%#x}", *(uint32_t *)addr); break; case sizeof (uint64_t): - (void) fprintf(fp, "%#llx", - /* LINTED - alignment */ + xo_emit("{:value/%#llx}", (unsigned long long)*(uint64_t *)addr); break; default: - (void) fprintf(fp, "", (uint_t)size); + xo_emit("<{:warning} {:size/%u}>", "invalid size", + (uint_t)size); } + + /* Flush in order to ensure output is aligned properly */ + xo_flush(); } /* * Print an integer type. Before dumping the contents via dt_print_hex(), we * first check the encoding to see if it's part of a bitfield or a character. */ static void dt_print_int(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; + dtrace_hdl_t *dtp = pap->pa_dtp; ctf_encoding_t e; size_t size; caddr_t addr = pap->pa_addr + off / NBBY; if (ctf_type_encoding(ctfp, base, &e) == CTF_ERR) { - (void) fprintf(fp, ""); + xo_emit("<{:warning}>", "unknown encoding"); + + /* Flush in order to ensure output is aligned properly */ + xo_flush(); return; } /* * This comes from MDB - it's not clear under what circumstances this * would be found. */ if (e.cte_format & CTF_INT_VARARGS) { - (void) fprintf(fp, "..."); + if (!dtp->dt_oformat) + (void)fprintf(fp, "..."); return; } /* * We print this as a bitfield if the bit encoding indicates it's not * an even power of two byte size, or is larger than 8 bytes. */ size = e.cte_bits / NBBY; if (size > 8 || (e.cte_bits % NBBY) != 0 || (size & (size - 1)) != 0) { print_bitfield(pap, off, &e); return; } /* * If this is a character, print it out as such. */ if (CTF_IS_CHAR(e)) { char c = *(char *)addr; if (isprint(c)) - (void) fprintf(fp, "'%c'", c); + xo_emit("'{:value/%c}'", c); else if (c == 0) - (void) fprintf(fp, "'\\0'"); + xo_emit("'\\{:value/0}'"); else - (void) fprintf(fp, "'\\%03o'", c); + xo_emit("'\\{:value/%03o}'", c); + + /* Flush in order to ensure output is aligned properly */ + xo_flush(); return; } dt_print_hex(fp, addr, size); } /* * Print a floating point (float, double, long double) value. */ /* ARGSUSED */ static void dt_print_float(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; ctf_encoding_t e; caddr_t addr = pap->pa_addr + off / NBBY; + dtrace_hdl_t *dtp = pap->pa_dtp; if (ctf_type_encoding(ctfp, base, &e) == 0) { if (e.cte_format == CTF_FP_SINGLE && e.cte_bits == sizeof (float) * NBBY) { - /* LINTED - alignment */ - (void) fprintf(fp, "%+.7e", *((float *)addr)); + xo_emit("{:value/%+.7e}", *((float *)addr)); } else if (e.cte_format == CTF_FP_DOUBLE && e.cte_bits == sizeof (double) * NBBY) { - /* LINTED - alignment */ - (void) fprintf(fp, "%+.7e", *((double *)addr)); + xo_emit("{:value/%+.7e}", *((double *)addr)); } else if (e.cte_format == CTF_FP_LDOUBLE && e.cte_bits == sizeof (long double) * NBBY) { - /* LINTED - alignment */ - (void) fprintf(fp, "%+.16LE", *((long double *)addr)); + xo_emit("{:value/%+.16LE}", *((long double *)addr)); } else { - (void) fprintf(fp, ""); + xo_emit("<{:warning}>", "unknown encoding"); } } } /* * A pointer is generally printed as a fixed-size integer. If we have a * function pointer, we try to look up its name. */ static void dt_print_ptr(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; caddr_t addr = pap->pa_addr + off / NBBY; size_t size = ctf_type_size(ctfp, base); ctf_id_t bid = ctf_type_reference(ctfp, base); uint64_t pc; dtrace_syminfo_t dts; GElf_Sym sym; if (bid == CTF_ERR || ctf_type_kind(ctfp, bid) != CTF_K_FUNCTION) { dt_print_hex(fp, addr, size); } else { /* LINTED - alignment */ pc = *((uint64_t *)addr); if (dtrace_lookup_by_addr(pap->pa_dtp, pc, &sym, &dts) != 0) { dt_print_hex(fp, addr, size); } else { - (void) fprintf(fp, "%s`%s", dts.dts_object, - dts.dts_name); + xo_emit("{:value/%s`%s}", dts.dts_object, dts.dts_name); } } } /* * Print out an array. This is somewhat complex, as we must manually visit * each member, and recursively invoke ctf_type_visit() for each member. If * the members are non-structs, then we print them out directly: * * [ 0x14, 0x2e, 0 ] * * If they are structs, then we print out the necessary leading and trailing * braces, to end up with: * * [ * type { * ... * }, * type { * ... * } * ] * * We also use a heuristic to detect whether the array looks like a character * array. If the encoding indicates it's a character, and we have all * printable characters followed by a null byte, then we display it as a * string: * * [ "string" ] */ static void dt_print_array(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; caddr_t addr = pap->pa_addr + off / NBBY; + char *str; ctf_arinfo_t car; ssize_t eltsize; ctf_encoding_t e; int i; boolean_t isstring; int kind; ctf_id_t rtype; + dtrace_hdl_t *dtp = pap->pa_dtp; if (ctf_array_info(ctfp, base, &car) == CTF_ERR) { - (void) fprintf(fp, "%p", (void *)addr); + xo_emit("{:value/%p}", (void *)addr); return; } if ((eltsize = ctf_type_size(ctfp, car.ctr_contents)) < 0 || (rtype = ctf_type_resolve(ctfp, car.ctr_contents)) == CTF_ERR || (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR) { - (void) fprintf(fp, "", car.ctr_contents); + xo_emit("<{:warning} {:type-identifier/%lu}>", "invalid type", + car.ctr_contents); return; } /* see if this looks like a string */ isstring = B_FALSE; if (kind == CTF_K_INTEGER && ctf_type_encoding(ctfp, rtype, &e) != CTF_ERR && CTF_IS_CHAR(e)) { char c; for (i = 0; i < car.ctr_nelems; i++) { c = *((char *)addr + eltsize * i); if (!isprint(c) || c == '\0') break; } if (i != car.ctr_nelems && c == '\0') isstring = B_TRUE; } /* * As a slight aesthetic optimization, if we are a top-level type, then * don't bother printing out the brackets. This lets print("foo") look * like: * * string "foo" * * As D will internally represent this as a char[256] array. */ - if (!isstring || pap->pa_depth != 0) - (void) fprintf(fp, "[ "); + if (dtp->dt_oformat) { + if (!isstring) + xo_open_list("value"); + else { + str = malloc(car.ctr_nelems); + if (str == NULL) + return; + *str = 0; + } + } else { + if (!isstring || pap->pa_depth != 0) + (void)fprintf(fp, "[ "); - if (isstring) - (void) fprintf(fp, "\""); + if (isstring) + (void)fprintf(fp, "\""); + } for (i = 0; i < car.ctr_nelems; i++) { if (isstring) { char c = *((char *)addr + eltsize * i); - if (c == '\0') + if (c == '\0') { + if (dtp->dt_oformat) + str[i] = 0; break; - (void) fprintf(fp, "%c", c); + } + + if (dtp->dt_oformat) + str[i] = c; + else + (void)fprintf(fp, "%c", c); + } else if (dtp->dt_oformat) { + dt_printarg_t pa = *pap; + pa.pa_nest += pap->pa_depth + 1; + pa.pa_depth = 0; + pa.pa_addr = addr + eltsize * i; + + (void) ctf_type_visit(ctfp, car.ctr_contents, + dt_format_member, &pa); } else { /* * Recursively invoke ctf_type_visit() on each member. * We setup a new printarg struct with 'pa_nest' set to * indicate that we are within a nested array. */ dt_printarg_t pa = *pap; pa.pa_nest += pap->pa_depth + 1; pa.pa_depth = 0; pa.pa_addr = addr + eltsize * i; (void) ctf_type_visit(ctfp, car.ctr_contents, dt_print_member, &pa); dt_print_trailing_braces(&pa, 0); if (i != car.ctr_nelems - 1) (void) fprintf(fp, ", "); else if (CTF_IS_STRUCTLIKE(kind)) (void) fprintf(fp, "\n"); } } - if (isstring) - (void) fprintf(fp, "\""); - - if (!isstring || pap->pa_depth != 0) { - if (CTF_IS_STRUCTLIKE(kind)) - dt_print_indent(pap); - else - (void) fprintf(fp, " "); - (void) fprintf(fp, "]"); + if (dtp->dt_oformat) { + if (!isstring) + xo_close_list("value"); + else { + xo_emit("{:value/%s}", str); + free(str); + } + } else { + if (isstring) + (void)fprintf(fp, "\""); + + if (!isstring || pap->pa_depth != 0) { + if (CTF_IS_STRUCTLIKE(kind)) + dt_print_indent(pap); + else + (void)fprintf(fp, " "); + (void)fprintf(fp, "]"); + } } } /* * This isued by both structs and unions to print the leading brace. */ /* ARGSUSED */ static void dt_print_structlike(ctf_id_t id, ulong_t off, dt_printarg_t *pap) { - (void) fprintf(pap->pa_file, "{"); + if (pap->pa_dtp->dt_oformat == DTRACE_OFORMAT_TEXT) + (void)fprintf(pap->pa_file, "{"); } /* * For enums, we try to print the enum name, and fall back to the value if it * can't be determined. We do not do any fancy flag processing like mdb. */ /* ARGSUSED */ static void dt_print_enum(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; const char *ename; ssize_t size; caddr_t addr = pap->pa_addr + off / NBBY; int value = 0; + dtrace_hdl_t *dtp = pap->pa_dtp; /* * The C standard says that an enum will be at most the sizeof (int). * But if all the values are less than that, the compiler can use a * smaller size. Thanks standards. */ size = ctf_type_size(ctfp, base); switch (size) { case sizeof (uint8_t): value = *(uint8_t *)addr; break; case sizeof (uint16_t): value = *(uint16_t *)addr; break; case sizeof (int32_t): value = *(int32_t *)addr; break; default: - (void) fprintf(fp, "", (uint_t)size); + xo_emit("<{:warning} {:size/%u}>", "invalid enum size", + (uint_t)size); return; } - if ((ename = ctf_enum_name(ctfp, base, value)) != NULL) - (void) fprintf(fp, "%s", ename); - else - (void) fprintf(fp, "%d", value); + if ((ename = ctf_enum_name(ctfp, base, value)) != NULL) { + xo_emit("{:value/%s}", ename); + } else { + xo_emit("{:value/%d}", value); + } + + /* Flush in order to ensure output is aligned properly */ + xo_flush(); } /* * Forward declaration. There's not much to do here without the complete * type information, so just print out this fact and drive on. */ /* ARGSUSED */ static void dt_print_tag(ctf_id_t base, ulong_t off, dt_printarg_t *pap) { - (void) fprintf(pap->pa_file, ""); + if (pap->pa_dtp->dt_oformat == DTRACE_OFORMAT_TEXT) + (void)fprintf(pap->pa_file, ""); } typedef void dt_printarg_f(ctf_id_t, ulong_t, dt_printarg_t *); static dt_printarg_f *const dt_printfuncs[] = { dt_print_int, /* CTF_K_INTEGER */ dt_print_float, /* CTF_K_FLOAT */ dt_print_ptr, /* CTF_K_POINTER */ dt_print_array, /* CTF_K_ARRAY */ dt_print_ptr, /* CTF_K_FUNCTION */ dt_print_structlike, /* CTF_K_STRUCT */ dt_print_structlike, /* CTF_K_UNION */ dt_print_enum, /* CTF_K_ENUM */ dt_print_tag /* CTF_K_FORWARD */ }; +static int +dt_format_member(const char *name, ctf_id_t id, ulong_t off, int depth, + void *data) +{ + char type[DT_TYPE_NAMELEN]; + int kind; + dt_printarg_t *pap = data; + FILE *fp = pap->pa_file; + ctf_file_t *ctfp = pap->pa_ctfp; + boolean_t arraymember; + boolean_t brief; + ctf_encoding_t e; + ctf_id_t rtype; + + if ((rtype = ctf_type_resolve(ctfp, id)) == CTF_ERR || + (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR || + kind < CTF_K_INTEGER || kind > CTF_K_FORWARD) { + xo_emit("{:name/%s} <{:warning} {:type-identifier/%lu}>" + " {:value/0x%llx}", + name, "invalid type", id, pap->pa_addr); + return (0); + } + + dt_print_type_name(ctfp, id, type, sizeof (type)); + xo_open_instance("type"); + if (pap->pa_object) { + xo_emit("{:object-name/%s}", pap->pa_object); + /* Clear the object to avoid duplication */ + pap->pa_object = NULL; + } + + if (*name != 0) + xo_emit("{:member-name/%s}", name); + xo_emit("{:name/%s} {:ctfid/%ld}", type, id); + dt_printfuncs[kind - 1](rtype, off, pap); + + xo_close_instance("type"); + return (0); +} + /* * Print one member of a structure. This callback is invoked from * ctf_type_visit() recursively. */ static int dt_print_member(const char *name, ctf_id_t id, ulong_t off, int depth, void *data) { char type[DT_TYPE_NAMELEN]; int kind; dt_printarg_t *pap = data; FILE *fp = pap->pa_file; ctf_file_t *ctfp = pap->pa_ctfp; boolean_t arraymember; boolean_t brief; ctf_encoding_t e; ctf_id_t rtype; dt_print_trailing_braces(pap, depth); /* * dt_print_trailing_braces() doesn't include the trailing newline; add * it here if necessary. */ if (depth < pap->pa_depth) (void) fprintf(fp, "\n"); pap->pa_depth = depth; if ((rtype = ctf_type_resolve(ctfp, id)) == CTF_ERR || (kind = ctf_type_kind(ctfp, rtype)) == CTF_ERR || kind < CTF_K_INTEGER || kind > CTF_K_FORWARD) { dt_print_indent(pap); (void) fprintf(fp, "%s = ", name, id); return (0); } dt_print_type_name(ctfp, id, type, sizeof (type)); arraymember = (pap->pa_nest != 0 && depth == 0); brief = (arraymember && !CTF_IS_STRUCTLIKE(kind)); if (!brief) { /* * If this is a direct array member and a struct (otherwise * brief would be true), then print a trailing newline, as the * array printing code doesn't include it because it might be a * simple type. */ if (arraymember) (void) fprintf(fp, "\n"); dt_print_indent(pap); /* always print the type */ (void) fprintf(fp, "%s", type); if (name[0] != '\0') { /* * For aesthetics, we don't include a space between the * type name and member name if the type is a pointer. * This will give us "void *foo =" instead of "void * * foo =". Unions also have the odd behavior that the * type name is returned as "union ", with a trailing * space, so we also avoid printing a space if the type * name already ends with a space. */ if (type[strlen(type) - 1] != '*' && type[strlen(type) -1] != ' ') { (void) fprintf(fp, " "); } (void) fprintf(fp, "%s", name); /* * If this looks like a bitfield, or is an integer not * aligned on a byte boundary, print the number of * bits after the name. */ if (kind == CTF_K_INTEGER && ctf_type_encoding(ctfp, id, &e) == 0) { ulong_t bits = e.cte_bits; ulong_t size = bits / NBBY; if (bits % NBBY != 0 || off % NBBY != 0 || size > 8 || size != ctf_type_size(ctfp, id)) { (void) fprintf(fp, " :%lu", bits); } } (void) fprintf(fp, " ="); } (void) fprintf(fp, " "); } dt_printfuncs[kind - 1](rtype, off, pap); /* direct simple array members are not separated by newlines */ if (!brief) (void) fprintf(fp, "\n"); return (0); } -/* - * Main print function invoked by dt_consume_cpu(). - */ -int -dtrace_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename, - caddr_t addr, size_t len) +static ctf_id_t +dt_print_prepare(dtrace_hdl_t *dtp, const char *typename, caddr_t addr, + size_t len, dt_printarg_t *pa) { const char *s; char *object; - dt_printarg_t pa; ctf_id_t id; dt_module_t *dmp; ctf_file_t *ctfp; int libid; /* * Split the fully-qualified type ID (module`id). This should * always be the format, but if for some reason we don't find the * expected value, return 0 to fall back to the generic trace() * behavior. In the case of userland CTF modules this will actually be * of the format (module`lib`id). This is due to the fact that those * modules have multiple CTF containers which `lib` identifies. */ for (s = typename; *s != '\0' && *s != '`'; s++) ; if (*s != '`') - return (0); + return (CTF_ERR); object = alloca(s - typename + 1); bcopy(typename, object, s - typename); object[s - typename] = '\0'; dmp = dt_module_lookup_by_name(dtp, object); if (dmp == NULL) - return (0); + return (CTF_ERR); if (dmp->dm_pid != 0) { libid = atoi(s + 1); s = strchr(s + 1, '`'); if (s == NULL || libid > dmp->dm_nctflibs) - return (0); + return (CTF_ERR); ctfp = dmp->dm_libctfp[libid]; } else { ctfp = dt_module_getctf(dtp, dmp); } id = atoi(s + 1); /* * Try to get the CTF kind for this id. If something has gone horribly * wrong and we can't resolve the ID, bail out and let trace() do the * work. */ if (ctfp == NULL || ctf_type_kind(ctfp, id) == CTF_ERR) + return (CTF_ERR); + + pa->pa_dtp = dtp; + pa->pa_addr = addr; + pa->pa_ctfp = ctfp; + pa->pa_nest = 0; + pa->pa_depth = 0; + pa->pa_object = strdup(object); + return (id); +} + +/* + * Main print function invoked by dt_consume_cpu(). + */ +int +dtrace_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename, + caddr_t addr, size_t len) +{ + dt_printarg_t pa; + ctf_id_t id; + + id = dt_print_prepare(dtp, typename, addr, len, &pa); + if (id == CTF_ERR) return (0); - /* setup the print structure and kick off the main print routine */ - pa.pa_dtp = dtp; - pa.pa_addr = addr; - pa.pa_ctfp = ctfp; - pa.pa_nest = 0; - pa.pa_depth = 0; pa.pa_file = fp; (void) ctf_type_visit(pa.pa_ctfp, id, dt_print_member, &pa); dt_print_trailing_braces(&pa, 0); + dt_free(dtp, (void *)pa.pa_object); + + return (len); +} + +/* + * Main format function invoked by dt_consume_cpu(). + */ +int +dtrace_format_print(dtrace_hdl_t *dtp, FILE *fp, const char *typename, + caddr_t addr, size_t len) +{ + dt_printarg_t pa; + ctf_id_t id; + char toplevel[1024]; + + id = dt_print_prepare(dtp, typename, addr, len, &pa); + if (id == CTF_ERR) + return (0); + + if (ctf_type_name(pa.pa_ctfp, id, toplevel, sizeof(toplevel)) < 0) + return (0); + + xo_open_list("type"); + (void) ctf_type_visit(pa.pa_ctfp, id, dt_format_member, &pa); + xo_close_list("type"); + dt_free(dtp, (void *)pa.pa_object); return (len); } + diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.h index b3b5b8b94bf6..58e345b204ac 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.h +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.h @@ -1,135 +1,141 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2023 Domagoj Stolfa. All rights reserved. * Use is subject to license terms. */ #ifndef _DT_PRINTF_H #define _DT_PRINTF_H #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #ifdef __cplusplus extern "C" { #endif struct dt_node; struct dt_ident; struct dt_pfconv; struct dt_pfargv; struct dt_pfargd; typedef int dt_pfcheck_f(struct dt_pfargv *, struct dt_pfargd *, struct dt_node *); typedef int dt_pfprint_f(dtrace_hdl_t *, FILE *, const char *, const struct dt_pfargd *, const void *, size_t, uint64_t); typedef struct dt_pfconv { const char *pfc_name; /* string name of input conversion */ const char *pfc_ofmt; /* string name of output conversion */ const char *pfc_tstr; /* string name for conversion type */ dt_pfcheck_f *pfc_check; /* function to use for type checking */ dt_pfprint_f *pfc_print; /* function to use for formatting */ ctf_file_t *pfc_cctfp; /* CTF container for "C" defn of type */ ctf_id_t pfc_ctype; /* CTF type ID for "C" defn of type */ ctf_file_t *pfc_dctfp; /* CTF container for "D" defn of type */ ctf_id_t pfc_dtype; /* CTF type ID for "D" defn of type */ struct dt_pfconv *pfc_next; /* next conversion in hash chain */ } dt_pfconv_t; typedef struct dt_pfdict { dt_pfconv_t **pdi_buckets; /* hash bucket array */ uint_t pdi_nbuckets; /* size of hash bucket array */ } dt_pfdict_t; typedef struct dt_pfargd { const char *pfd_prefix; /* prefix string pointer (or NULL) */ size_t pfd_preflen; /* length of prefix in bytes */ char pfd_fmt[8]; /* output format name to use */ uint_t pfd_flags; /* format flags (see below) */ int pfd_width; /* field width (or 0) */ int pfd_dynwidth; /* dynamic field width (or 0) */ int pfd_prec; /* field precision (or 0) */ const dt_pfconv_t *pfd_conv; /* conversion specification */ const dtrace_recdesc_t *pfd_rec; /* pointer to current record */ struct dt_pfargd *pfd_next; /* pointer to next arg descriptor */ } dt_pfargd_t; #define DT_PFCONV_ALT 0x0001 /* alternate print format (%#) */ #define DT_PFCONV_ZPAD 0x0002 /* zero-pad integer field (%0) */ #define DT_PFCONV_LEFT 0x0004 /* left-align field (%-) */ #define DT_PFCONV_SPOS 0x0008 /* sign positive values (%+) */ #define DT_PFCONV_DYNWIDTH 0x0010 /* dynamic width (%*.) */ #define DT_PFCONV_DYNPREC 0x0020 /* dynamic precision (%.*) */ #define DT_PFCONV_GROUP 0x0040 /* group thousands (%') */ #define DT_PFCONV_SPACE 0x0080 /* insert leading space (% ) */ #define DT_PFCONV_AGG 0x0100 /* use aggregation result (%@) */ #define DT_PFCONV_SIGNED 0x0200 /* arg is a signed integer */ typedef struct dt_pfargv { dtrace_hdl_t *pfv_dtp; /* libdtrace client handle */ char *pfv_format; /* format string pointer */ dt_pfargd_t *pfv_argv; /* list of argument descriptors */ uint_t pfv_argc; /* number of argument descriptors */ uint_t pfv_flags; /* flags used for validation */ } dt_pfargv_t; typedef struct dt_pfwalk { const dt_pfargv_t *pfw_argv; /* argument description list */ uint_t pfw_aid; /* aggregation variable identifier */ FILE *pfw_fp; /* file pointer to use for output */ int pfw_err; /* error status code */ } dt_pfwalk_t; extern int dt_pfdict_create(dtrace_hdl_t *); extern void dt_pfdict_destroy(dtrace_hdl_t *); extern dt_pfargv_t *dt_printf_create(dtrace_hdl_t *, const char *); extern void dt_printf_destroy(dt_pfargv_t *); #define DT_PRINTF_EXACTLEN 0x1 /* do not permit extra arguments */ #define DT_PRINTF_AGGREGATION 0x2 /* enable aggregation conversion */ extern void dt_printf_validate(dt_pfargv_t *, uint_t, struct dt_ident *, int, dtrace_actkind_t, struct dt_node *); extern void dt_printa_validate(struct dt_node *, struct dt_node *); extern int dt_print_stack(dtrace_hdl_t *, FILE *, const char *, caddr_t, int, int); extern int dt_print_ustack(dtrace_hdl_t *, FILE *, const char *, caddr_t, uint64_t); extern int dt_print_mod(dtrace_hdl_t *, FILE *, const char *, caddr_t); extern int dt_print_umod(dtrace_hdl_t *, FILE *, const char *, caddr_t); +extern int dt_format_stack(dtrace_hdl_t *, caddr_t, int, int); +extern int dt_format_ustack(dtrace_hdl_t *, caddr_t, uint64_t); +extern int dt_format_mod(dtrace_hdl_t *, caddr_t); +extern int dt_format_umod(dtrace_hdl_t *, caddr_t); + #ifdef __cplusplus } #endif #endif /* _DT_PRINTF_H */ diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c index 1729eaf2e7be..5976333e1b16 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_subr.c @@ -1,994 +1,1036 @@ /* * 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. * Copyright (c) 2012 by Delphix. All rights reserved. * Use is subject to license terms. */ #ifdef illumos #include #endif #include #include #include #include #include #include #include #include #include #ifdef illumos #include #else #include #include #endif #include #include #include #include #include +#include static const struct { size_t dtps_offset; size_t dtps_len; } dtrace_probespecs[] = { { offsetof(dtrace_probedesc_t, dtpd_provider), DTRACE_PROVNAMELEN }, { offsetof(dtrace_probedesc_t, dtpd_mod), DTRACE_MODNAMELEN }, { offsetof(dtrace_probedesc_t, dtpd_func), DTRACE_FUNCNAMELEN }, { offsetof(dtrace_probedesc_t, dtpd_name), DTRACE_NAMELEN } }; int dtrace_xstr2desc(dtrace_hdl_t *dtp, dtrace_probespec_t spec, const char *s, int argc, char *const argv[], dtrace_probedesc_t *pdp) { size_t off, len, vlen, wlen; const char *p, *q, *v, *w; char buf[32]; /* for id_t as %d (see below) */ if (spec < DTRACE_PROBESPEC_NONE || spec > DTRACE_PROBESPEC_NAME) return (dt_set_errno(dtp, EINVAL)); bzero(pdp, sizeof (dtrace_probedesc_t)); p = s + strlen(s) - 1; do { for (len = 0; p >= s && *p != ':'; len++) p--; /* move backward until we find a delimiter */ q = p + 1; vlen = 0; w = NULL; wlen = 0; if ((v = strchr(q, '$')) != NULL && v < q + len) { /* * Set vlen to the length of the variable name and then * reset len to the length of the text prior to '$'. If * the name begins with a digit, interpret it using the * the argv[] array. Otherwise we look in dt_macros. * For the moment, all dt_macros variables are of type * id_t (see dtrace_update() for more details on that). */ vlen = (size_t)(q + len - v); len = (size_t)(v - q); /* * If the variable string begins with $$, skip past the * leading dollar sign since $ and $$ are equivalent * macro reference operators in a probe description. */ if (vlen > 2 && v[1] == '$') { vlen--; v++; } if (isdigit(v[1])) { long i; errno = 0; i = strtol(v + 1, (char **)&w, 10); wlen = vlen - (w - v); if (i < 0 || i >= argc || errno != 0) return (dt_set_errno(dtp, EDT_BADSPCV)); v = argv[i]; vlen = strlen(v); if (yypcb != NULL && yypcb->pcb_sargv == argv) yypcb->pcb_sflagv[i] |= DT_IDFLG_REF; } else if (vlen > 1) { char *vstr = alloca(vlen); dt_ident_t *idp; (void) strncpy(vstr, v + 1, vlen - 1); vstr[vlen - 1] = '\0'; idp = dt_idhash_lookup(dtp->dt_macros, vstr); if (idp == NULL) return (dt_set_errno(dtp, EDT_BADSPCV)); v = buf; vlen = snprintf(buf, 32, "%d", idp->di_id); } else return (dt_set_errno(dtp, EDT_BADSPCV)); } if (spec == DTRACE_PROBESPEC_NONE) return (dt_set_errno(dtp, EDT_BADSPEC)); if (len + vlen >= dtrace_probespecs[spec].dtps_len) return (dt_set_errno(dtp, ENAMETOOLONG)); off = dtrace_probespecs[spec--].dtps_offset; bcopy(q, (char *)pdp + off, len); bcopy(v, (char *)pdp + off + len, vlen); bcopy(w, (char *)pdp + off + len + vlen, wlen); } while (--p >= s); pdp->dtpd_id = DTRACE_IDNONE; return (0); } int dtrace_str2desc(dtrace_hdl_t *dtp, dtrace_probespec_t spec, const char *s, dtrace_probedesc_t *pdp) { return (dtrace_xstr2desc(dtp, spec, s, 0, NULL, pdp)); } int dtrace_id2desc(dtrace_hdl_t *dtp, dtrace_id_t id, dtrace_probedesc_t *pdp) { bzero(pdp, sizeof (dtrace_probedesc_t)); pdp->dtpd_id = id; if (dt_ioctl(dtp, DTRACEIOC_PROBES, pdp) == -1 || pdp->dtpd_id != id) return (dt_set_errno(dtp, EDT_BADID)); return (0); } char * dtrace_desc2str(const dtrace_probedesc_t *pdp, char *buf, size_t len) { if (pdp->dtpd_id == 0) { (void) snprintf(buf, len, "%s:%s:%s:%s", pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name); } else (void) snprintf(buf, len, "%u", pdp->dtpd_id); return (buf); } char * dtrace_attr2str(dtrace_attribute_t attr, char *buf, size_t len) { const char *name = dtrace_stability_name(attr.dtat_name); const char *data = dtrace_stability_name(attr.dtat_data); const char *class = dtrace_class_name(attr.dtat_class); if (name == NULL || data == NULL || class == NULL) return (NULL); /* one or more invalid attributes */ (void) snprintf(buf, len, "%s/%s/%s", name, data, class); return (buf); } static char * dt_getstrattr(char *p, char **qp) { char *q; if (*p == '\0') return (NULL); if ((q = strchr(p, '/')) == NULL) q = p + strlen(p); else *q++ = '\0'; *qp = q; return (p); } int dtrace_str2attr(const char *str, dtrace_attribute_t *attr) { dtrace_stability_t s; dtrace_class_t c; char *p, *q; if (str == NULL || attr == NULL) return (-1); /* invalid function arguments */ *attr = _dtrace_maxattr; p = alloca(strlen(str) + 1); (void) strcpy(p, str); if ((p = dt_getstrattr(p, &q)) == NULL) return (0); for (s = 0; s <= DTRACE_STABILITY_MAX; s++) { if (strcasecmp(p, dtrace_stability_name(s)) == 0) { attr->dtat_name = s; break; } } if (s > DTRACE_STABILITY_MAX) return (-1); if ((p = dt_getstrattr(q, &q)) == NULL) return (0); for (s = 0; s <= DTRACE_STABILITY_MAX; s++) { if (strcasecmp(p, dtrace_stability_name(s)) == 0) { attr->dtat_data = s; break; } } if (s > DTRACE_STABILITY_MAX) return (-1); if ((p = dt_getstrattr(q, &q)) == NULL) return (0); for (c = 0; c <= DTRACE_CLASS_MAX; c++) { if (strcasecmp(p, dtrace_class_name(c)) == 0) { attr->dtat_class = c; break; } } if (c > DTRACE_CLASS_MAX || (p = dt_getstrattr(q, &q)) != NULL) return (-1); return (0); } const char * dtrace_stability_name(dtrace_stability_t s) { switch (s) { case DTRACE_STABILITY_INTERNAL: return ("Internal"); case DTRACE_STABILITY_PRIVATE: return ("Private"); case DTRACE_STABILITY_OBSOLETE: return ("Obsolete"); case DTRACE_STABILITY_EXTERNAL: return ("External"); case DTRACE_STABILITY_UNSTABLE: return ("Unstable"); case DTRACE_STABILITY_EVOLVING: return ("Evolving"); case DTRACE_STABILITY_STABLE: return ("Stable"); case DTRACE_STABILITY_STANDARD: return ("Standard"); default: return (NULL); } } const char * dtrace_class_name(dtrace_class_t c) { switch (c) { case DTRACE_CLASS_UNKNOWN: return ("Unknown"); case DTRACE_CLASS_CPU: return ("CPU"); case DTRACE_CLASS_PLATFORM: return ("Platform"); case DTRACE_CLASS_GROUP: return ("Group"); case DTRACE_CLASS_ISA: return ("ISA"); case DTRACE_CLASS_COMMON: return ("Common"); default: return (NULL); } } dtrace_attribute_t dt_attr_min(dtrace_attribute_t a1, dtrace_attribute_t a2) { dtrace_attribute_t am; am.dtat_name = MIN(a1.dtat_name, a2.dtat_name); am.dtat_data = MIN(a1.dtat_data, a2.dtat_data); am.dtat_class = MIN(a1.dtat_class, a2.dtat_class); return (am); } dtrace_attribute_t dt_attr_max(dtrace_attribute_t a1, dtrace_attribute_t a2) { dtrace_attribute_t am; am.dtat_name = MAX(a1.dtat_name, a2.dtat_name); am.dtat_data = MAX(a1.dtat_data, a2.dtat_data); am.dtat_class = MAX(a1.dtat_class, a2.dtat_class); return (am); } /* * Compare two attributes and return an integer value in the following ranges: * * <0 if any of a1's attributes are less than a2's attributes * =0 if all of a1's attributes are equal to a2's attributes * >0 if all of a1's attributes are greater than or equal to a2's attributes * * To implement this function efficiently, we subtract a2's attributes from * a1's to obtain a negative result if an a1 attribute is less than its a2 * counterpart. We then OR the intermediate results together, relying on the * twos-complement property that if any result is negative, the bitwise union * will also be negative since the highest bit will be set in the result. */ int dt_attr_cmp(dtrace_attribute_t a1, dtrace_attribute_t a2) { return (((int)a1.dtat_name - a2.dtat_name) | ((int)a1.dtat_data - a2.dtat_data) | ((int)a1.dtat_class - a2.dtat_class)); } char * dt_attr_str(dtrace_attribute_t a, char *buf, size_t len) { static const char stability[] = "ipoxuesS"; static const char class[] = "uCpgIc"; if (a.dtat_name < sizeof (stability) && a.dtat_data < sizeof (stability) && a.dtat_class < sizeof (class)) { (void) snprintf(buf, len, "[%c/%c/%c]", stability[a.dtat_name], stability[a.dtat_data], class[a.dtat_class]); } else { (void) snprintf(buf, len, "[%u/%u/%u]", a.dtat_name, a.dtat_data, a.dtat_class); } return (buf); } char * dt_version_num2str(dt_version_t v, char *buf, size_t len) { uint_t M = DT_VERSION_MAJOR(v); uint_t m = DT_VERSION_MINOR(v); uint_t u = DT_VERSION_MICRO(v); if (u == 0) (void) snprintf(buf, len, "%u.%u", M, m); else (void) snprintf(buf, len, "%u.%u.%u", M, m, u); return (buf); } int dt_version_str2num(const char *s, dt_version_t *vp) { int i = 0, n[3] = { 0, 0, 0 }; char c; while ((c = *s++) != '\0') { if (isdigit(c)) n[i] = n[i] * 10 + c - '0'; else if (c != '.' || i++ >= sizeof (n) / sizeof (n[0]) - 1) return (-1); } if (n[0] > DT_VERSION_MAJMAX || n[1] > DT_VERSION_MINMAX || n[2] > DT_VERSION_MICMAX) return (-1); if (vp != NULL) *vp = DT_VERSION_NUMBER(n[0], n[1], n[2]); return (0); } int dt_version_defined(dt_version_t v) { int i; for (i = 0; _dtrace_versions[i] != 0; i++) { if (_dtrace_versions[i] == v) return (1); } return (0); } char * dt_cpp_add_arg(dtrace_hdl_t *dtp, const char *str) { char *arg; if (dtp->dt_cpp_argc == dtp->dt_cpp_args) { int olds = dtp->dt_cpp_args; int news = olds * 2; char **argv = realloc(dtp->dt_cpp_argv, sizeof (char *) * news); if (argv == NULL) return (NULL); bzero(&argv[olds], sizeof (char *) * olds); dtp->dt_cpp_argv = argv; dtp->dt_cpp_args = news; } if ((arg = strdup(str)) == NULL) return (NULL); assert(dtp->dt_cpp_argc < dtp->dt_cpp_args); dtp->dt_cpp_argv[dtp->dt_cpp_argc++] = arg; return (arg); } char * dt_cpp_pop_arg(dtrace_hdl_t *dtp) { char *arg; if (dtp->dt_cpp_argc <= 1) return (NULL); /* dt_cpp_argv[0] cannot be popped */ arg = dtp->dt_cpp_argv[--dtp->dt_cpp_argc]; dtp->dt_cpp_argv[dtp->dt_cpp_argc] = NULL; return (arg); } /*PRINTFLIKE1*/ void dt_dprintf(const char *format, ...) { if (_dtrace_debug) { va_list alist; va_start(alist, format); (void) fputs("libdtrace DEBUG: ", stderr); (void) vfprintf(stderr, format, alist); va_end(alist); } } int #ifdef illumos dt_ioctl(dtrace_hdl_t *dtp, int val, void *arg) #else dt_ioctl(dtrace_hdl_t *dtp, u_long val, void *arg) #endif { const dtrace_vector_t *v = dtp->dt_vector; #ifndef illumos /* Avoid sign extension. */ val &= 0xffffffff; #endif if (v != NULL) return (v->dtv_ioctl(dtp->dt_varg, val, arg)); if (dtp->dt_fd >= 0) return (ioctl(dtp->dt_fd, val, arg)); errno = EBADF; return (-1); } int dt_status(dtrace_hdl_t *dtp, processorid_t cpu) { const dtrace_vector_t *v = dtp->dt_vector; if (v == NULL) { #ifdef illumos return (p_online(cpu, P_STATUS)); #else int maxid = 0; size_t len = sizeof(maxid); if (sysctlbyname("kern.smp.maxid", &maxid, &len, NULL, 0) != 0) return (cpu == 0 ? 1 : -1); else return (cpu <= maxid ? 1 : -1); #endif } return (v->dtv_status(dtp->dt_varg, cpu)); } long dt_sysconf(dtrace_hdl_t *dtp, int name) { const dtrace_vector_t *v = dtp->dt_vector; if (v == NULL) return (sysconf(name)); return (v->dtv_sysconf(dtp->dt_varg, name)); } /* * Wrapper around write(2) to handle partial writes. For maximum safety of * output files and proper error reporting, we continuing writing in the * face of partial writes until write(2) fails or 'buf' is completely written. * We also record any errno in the specified dtrace_hdl_t as well as 'errno'. */ ssize_t dt_write(dtrace_hdl_t *dtp, int fd, const void *buf, size_t n) { ssize_t resid = n; ssize_t len; while (resid != 0) { if ((len = write(fd, buf, resid)) <= 0) break; resid -= len; buf = (char *)buf + len; } if (resid == n && n != 0) return (dt_set_errno(dtp, errno)); return (n - resid); } /* * This function handles all output from libdtrace, as well as the * dtrace_sprintf() case. If we're here due to dtrace_sprintf(), then * dt_sprintf_buflen will be non-zero; in this case, we sprintf into the * specified buffer and return. Otherwise, if output is buffered (denoted by * a NULL fp), we sprintf the desired output into the buffered buffer * (expanding the buffer if required). If we don't satisfy either of these * conditions (that is, if we are to actually generate output), then we call * fprintf with the specified fp. In this case, we need to deal with one of * the more annoying peculiarities of libc's printf routines: any failed * write persistently sets an error flag inside the FILE causing every * subsequent write to fail, but only the caller that initiated the error gets * the errno. Since libdtrace clients often intercept SIGINT, this case is * particularly frustrating since we don't want the EINTR on one attempt to * write to the output file to preclude later attempts to write. This * function therefore does a clearerr() if any error occurred, and saves the * errno for the caller inside the specified dtrace_hdl_t. */ /*PRINTFLIKE3*/ int dt_printf(dtrace_hdl_t *dtp, FILE *fp, const char *format, ...) { va_list ap; va_list ap2; int n; #ifndef illumos /* * On FreeBSD, check if output is currently being re-directed * to another file. If so, output to that file instead of the * one the caller has specified. */ if (dtp->dt_freopen_fp != NULL) fp = dtp->dt_freopen_fp; #endif va_start(ap, format); if (dtp->dt_sprintf_buflen != 0) { int len; char *buf; assert(dtp->dt_sprintf_buf != NULL); buf = &dtp->dt_sprintf_buf[len = strlen(dtp->dt_sprintf_buf)]; len = dtp->dt_sprintf_buflen - len; assert(len >= 0); va_copy(ap2, ap); if ((n = vsnprintf(buf, len, format, ap2)) < 0) n = dt_set_errno(dtp, errno); va_end(ap2); va_end(ap); return (n); } if (fp == NULL) { int needed, rval; size_t avail; /* * Using buffered output is not allowed if a handler has * not been installed. */ if (dtp->dt_bufhdlr == NULL) { va_end(ap); return (dt_set_errno(dtp, EDT_NOBUFFERED)); } if (dtp->dt_buffered_buf == NULL) { assert(dtp->dt_buffered_size == 0); dtp->dt_buffered_size = 1; dtp->dt_buffered_buf = malloc(dtp->dt_buffered_size); if (dtp->dt_buffered_buf == NULL) { va_end(ap); return (dt_set_errno(dtp, EDT_NOMEM)); } dtp->dt_buffered_offs = 0; dtp->dt_buffered_buf[0] = '\0'; } va_copy(ap2, ap); if ((needed = vsnprintf(NULL, 0, format, ap2)) < 0) { rval = dt_set_errno(dtp, errno); va_end(ap2); va_end(ap); return (rval); } va_end(ap2); if (needed == 0) { va_end(ap); return (0); } for (;;) { char *newbuf; assert(dtp->dt_buffered_offs < dtp->dt_buffered_size); avail = dtp->dt_buffered_size - dtp->dt_buffered_offs; if (needed + 1 < avail) break; if ((newbuf = realloc(dtp->dt_buffered_buf, dtp->dt_buffered_size << 1)) == NULL) { va_end(ap); return (dt_set_errno(dtp, EDT_NOMEM)); } dtp->dt_buffered_buf = newbuf; dtp->dt_buffered_size <<= 1; } va_copy(ap2, ap); if (vsnprintf(&dtp->dt_buffered_buf[dtp->dt_buffered_offs], avail, format, ap2) < 0) { rval = dt_set_errno(dtp, errno); va_end(ap2); va_end(ap); return (rval); } va_end(ap2); dtp->dt_buffered_offs += needed; assert(dtp->dt_buffered_buf[dtp->dt_buffered_offs] == '\0'); va_end(ap); return (0); } va_copy(ap2, ap); n = vfprintf(fp, format, ap2); va_end(ap2); va_end(ap); if (n < 0) { clearerr(fp); return (dt_set_errno(dtp, errno)); } return (n); } int dt_buffered_flush(dtrace_hdl_t *dtp, dtrace_probedata_t *pdata, const dtrace_recdesc_t *rec, const dtrace_aggdata_t *agg, uint32_t flags) { dtrace_bufdata_t data; if (dtp->dt_buffered_offs == 0) return (0); data.dtbda_handle = dtp; data.dtbda_buffered = dtp->dt_buffered_buf; data.dtbda_probe = pdata; data.dtbda_recdesc = rec; data.dtbda_aggdata = agg; data.dtbda_flags = flags; if ((*dtp->dt_bufhdlr)(&data, dtp->dt_bufarg) == DTRACE_HANDLE_ABORT) return (dt_set_errno(dtp, EDT_DIRABORT)); dtp->dt_buffered_offs = 0; dtp->dt_buffered_buf[0] = '\0'; return (0); } void dt_buffered_destroy(dtrace_hdl_t *dtp) { free(dtp->dt_buffered_buf); dtp->dt_buffered_buf = NULL; dtp->dt_buffered_offs = 0; dtp->dt_buffered_size = 0; } void * dt_zalloc(dtrace_hdl_t *dtp, size_t size) { void *data; if ((data = malloc(size)) == NULL) (void) dt_set_errno(dtp, EDT_NOMEM); else bzero(data, size); return (data); } void * dt_alloc(dtrace_hdl_t *dtp, size_t size) { void *data; if ((data = malloc(size)) == NULL) (void) dt_set_errno(dtp, EDT_NOMEM); return (data); } void dt_free(dtrace_hdl_t *dtp, void *data) { assert(dtp != NULL); /* ensure sane use of this interface */ free(data); } void dt_difo_free(dtrace_hdl_t *dtp, dtrace_difo_t *dp) { if (dp == NULL) return; /* simplify caller code */ dt_free(dtp, dp->dtdo_buf); dt_free(dtp, dp->dtdo_inttab); dt_free(dtp, dp->dtdo_strtab); dt_free(dtp, dp->dtdo_vartab); dt_free(dtp, dp->dtdo_kreltab); dt_free(dtp, dp->dtdo_ureltab); dt_free(dtp, dp->dtdo_xlmtab); dt_free(dtp, dp); } /* * dt_gmatch() is similar to gmatch(3GEN) and dtrace(7D) globbing, but also * implements the behavior that an empty pattern matches any string. */ int dt_gmatch(const char *s, const char *p) { return (p == NULL || *p == '\0' || gmatch(s, p)); } char * dt_basename(char *str) { char *last = strrchr(str, '/'); if (last == NULL) return (str); return (last + 1); } /* * dt_popc() is a fast implementation of population count. The algorithm is * from "Hacker's Delight" by Henry Warren, Jr with a 64-bit equivalent added. */ ulong_t dt_popc(ulong_t x) { #if defined(_ILP32) x = x - ((x >> 1) & 0x55555555UL); x = (x & 0x33333333UL) + ((x >> 2) & 0x33333333UL); x = (x + (x >> 4)) & 0x0F0F0F0FUL; x = x + (x >> 8); x = x + (x >> 16); return (x & 0x3F); #elif defined(_LP64) x = x - ((x >> 1) & 0x5555555555555555ULL); x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0FULL; x = x + (x >> 8); x = x + (x >> 16); x = x + (x >> 32); return (x & 0x7F); #else /* This should be a #warning but for now ignore error. Err: "need td_popc() implementation" */ #endif } /* * dt_popcb() is a bitmap-based version of population count that returns the * number of one bits in the specified bitmap 'bp' at bit positions below 'n'. */ ulong_t dt_popcb(const ulong_t *bp, ulong_t n) { ulong_t maxb = n & BT_ULMASK; ulong_t maxw = n >> BT_ULSHIFT; ulong_t w, popc = 0; if (n == 0) return (0); for (w = 0; w < maxw; w++) popc += dt_popc(bp[w]); return (popc + dt_popc(bp[maxw] & ((1UL << maxb) - 1))); } #ifdef illumos struct _rwlock; struct _lwp_mutex; int dt_rw_read_held(pthread_rwlock_t *lock) { extern int _rw_read_held(struct _rwlock *); return (_rw_read_held((struct _rwlock *)lock)); } int dt_rw_write_held(pthread_rwlock_t *lock) { extern int _rw_write_held(struct _rwlock *); return (_rw_write_held((struct _rwlock *)lock)); } #endif int dt_mutex_held(pthread_mutex_t *lock) { #ifdef illumos extern int _mutex_held(struct _lwp_mutex *); return (_mutex_held((struct _lwp_mutex *)lock)); #else return (1); #endif } static int dt_string2str(char *s, char *str, int nbytes) { int len = strlen(s); if (nbytes == 0) { /* * Like snprintf(3C), we don't check the value of str if the * number of bytes is 0. */ return (len); } if (nbytes <= len) { (void) strncpy(str, s, nbytes - 1); /* * Like snprintf(3C) (and unlike strncpy(3C)), we guarantee * that the string is null-terminated. */ str[nbytes - 1] = '\0'; } else { (void) strcpy(str, s); } return (len); } int dtrace_addr2str(dtrace_hdl_t *dtp, uint64_t addr, char *str, int nbytes) { dtrace_syminfo_t dts; GElf_Sym sym; size_t n = 20; /* for 0x%llx\0 */ char *s; int err; if ((err = dtrace_lookup_by_addr(dtp, addr, &sym, &dts)) == 0) n += strlen(dts.dts_object) + strlen(dts.dts_name) + 2; /* +` */ s = alloca(n); if (err == 0 && addr != sym.st_value) { (void) snprintf(s, n, "%s`%s+0x%llx", dts.dts_object, dts.dts_name, (u_longlong_t)addr - sym.st_value); } else if (err == 0) { (void) snprintf(s, n, "%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, addr, NULL, &dts) == 0) { (void) snprintf(s, n, "%s`0x%llx", dts.dts_object, (u_longlong_t)addr); } else { (void) snprintf(s, n, "0x%llx", (u_longlong_t)addr); } } return (dt_string2str(s, str, nbytes)); } int dtrace_uaddr2str(dtrace_hdl_t *dtp, pid_t pid, uint64_t addr, char *str, int nbytes) { char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; struct ps_prochandle *P = NULL; GElf_Sym sym; char *obj; if (pid != 0) P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); if (P == NULL) { (void) snprintf(c, sizeof (c), "0x%jx", (uintmax_t)addr); return (dt_string2str(c, str, nbytes)); } dt_proc_lock(dtp, P); if (Plookup_by_addr(P, addr, name, sizeof (name), &sym) == 0) { (void) Pobjname(P, addr, objname, sizeof (objname)); obj = dt_basename(objname); if (addr > sym.st_value) { (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", obj, name, (u_longlong_t)(addr - sym.st_value)); } else { (void) snprintf(c, sizeof (c), "%s`%s", obj, name); } } else if (Pobjname(P, addr, objname, sizeof (objname)) != 0) { (void) snprintf(c, sizeof (c), "%s`0x%jx", dt_basename(objname), (uintmax_t)addr); } else { (void) snprintf(c, sizeof (c), "0x%jx", (uintmax_t)addr); } dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); return (dt_string2str(c, str, nbytes)); } + +int +dtrace_oformat_configure(dtrace_hdl_t *dtp) +{ + + dtp->dt_oformat = xo_get_style(NULL) == XO_STYLE_TEXT ? + DTRACE_OFORMAT_TEXT : + DTRACE_OFORMAT_STRUCTURED; + xo_set_flags(NULL, XOF_DTRT); + return (0); +} + +int +dtrace_oformat(dtrace_hdl_t *dtp) +{ + + return (dtp->dt_oformat != DTRACE_OFORMAT_TEXT); +} + +void +dtrace_set_outfp(const FILE *ofp) +{ + + xo_set_file((FILE *)ofp); +} + +void +dtrace_oformat_setup(dtrace_hdl_t *dtp) +{ + + xo_open_container("dtrace"); + xo_open_list("probes"); +} + +void +dtrace_oformat_teardown(dtrace_hdl_t *dtp) +{ + + xo_close_list("probes"); + xo_close_container("dtrace"); +} diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h b/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h index af0213695f32..b380f5eb3313 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h @@ -1,619 +1,641 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* + * Copyright (c) 2023 by Domagoj Stolfa. All rights reserved. * Copyright (c) 2014, 2016 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #ifndef _DTRACE_H #define _DTRACE_H #include #include #include #include #include #ifndef illumos #include #endif #ifdef __cplusplus extern "C" { #endif /* * DTrace Dynamic Tracing Software: Library Interfaces * * Note: The contents of this file are private to the implementation of the * Solaris system and DTrace subsystem and are subject to change at any time * without notice. Applications and drivers using these interfaces will fail * to run on future releases. These interfaces should not be used for any * purpose except those expressly outlined in dtrace(7D) and libdtrace(3LIB). * Please refer to the "Solaris Dynamic Tracing Guide" for more information. */ #define DTRACE_VERSION 3 /* library ABI interface version */ struct ps_prochandle; struct dt_node; typedef struct dtrace_hdl dtrace_hdl_t; typedef struct dtrace_prog dtrace_prog_t; typedef struct dtrace_vector dtrace_vector_t; typedef struct dtrace_aggdata dtrace_aggdata_t; #define DTRACE_O_NODEV 0x01 /* do not open dtrace(7D) device */ #define DTRACE_O_NOSYS 0x02 /* do not load /system/object modules */ #define DTRACE_O_LP64 0x04 /* force D compiler to be LP64 */ #define DTRACE_O_ILP32 0x08 /* force D compiler to be ILP32 */ #define DTRACE_O_MASK 0x0f /* mask of valid flags to dtrace_open */ extern dtrace_hdl_t *dtrace_open(int, int, int *); extern dtrace_hdl_t *dtrace_vopen(int, int, int *, const dtrace_vector_t *, void *); extern int dtrace_go(dtrace_hdl_t *); extern int dtrace_stop(dtrace_hdl_t *); extern void dtrace_sleep(dtrace_hdl_t *); extern void dtrace_close(dtrace_hdl_t *); extern int dtrace_errno(dtrace_hdl_t *); extern const char *dtrace_errmsg(dtrace_hdl_t *, int); extern const char *dtrace_faultstr(dtrace_hdl_t *, int); extern const char *dtrace_subrstr(dtrace_hdl_t *, int); extern int dtrace_setopt(dtrace_hdl_t *, const char *, const char *); extern int dtrace_getopt(dtrace_hdl_t *, const char *, dtrace_optval_t *); extern void dtrace_update(dtrace_hdl_t *); extern int dtrace_ctlfd(dtrace_hdl_t *); /* * DTrace Program Interface * * DTrace programs can be created by compiling ASCII text files containing * D programs or by compiling in-memory C strings that specify a D program. * Once created, callers can examine the list of program statements and * enable the probes and actions described by these statements. */ typedef struct dtrace_proginfo { dtrace_attribute_t dpi_descattr; /* minimum probedesc attributes */ dtrace_attribute_t dpi_stmtattr; /* minimum statement attributes */ uint_t dpi_aggregates; /* number of aggregates specified in program */ uint_t dpi_recgens; /* number of record generating probes in prog */ uint_t dpi_matches; /* number of probes matched by program */ uint_t dpi_speculations; /* number of speculations specified in prog */ } dtrace_proginfo_t; #define DTRACE_C_DIFV 0x0001 /* DIF verbose mode: show each compiled DIFO */ #define DTRACE_C_EMPTY 0x0002 /* Permit compilation of empty D source files */ #define DTRACE_C_ZDEFS 0x0004 /* Permit probe defs that match zero probes */ #define DTRACE_C_EATTR 0x0008 /* Error if program attributes less than min */ #define DTRACE_C_CPP 0x0010 /* Preprocess input file with cpp(1) utility */ #define DTRACE_C_KNODEF 0x0020 /* Permit unresolved kernel symbols in DIFO */ #define DTRACE_C_UNODEF 0x0040 /* Permit unresolved user symbols in DIFO */ #define DTRACE_C_PSPEC 0x0080 /* Interpret ambiguous specifiers as probes */ #define DTRACE_C_ETAGS 0x0100 /* Prefix error messages with error tags */ #define DTRACE_C_ARGREF 0x0200 /* Do not require all macro args to be used */ #define DTRACE_C_SUGAR 0x0400 /* Dump D script post-dt_sugar */ #define DTRACE_C_DEFARG 0x0800 /* Use 0/"" as value for unspecified args */ #define DTRACE_C_NOLIBS 0x1000 /* Do not process D system libraries */ #define DTRACE_C_CTL 0x2000 /* Only process control directives */ #define DTRACE_C_MASK 0x3fff /* mask of all valid flags to dtrace_*compile */ extern dtrace_prog_t *dtrace_program_strcompile(dtrace_hdl_t *, const char *, dtrace_probespec_t, uint_t, int, char *const []); extern dtrace_prog_t *dtrace_program_fcompile(dtrace_hdl_t *, FILE *, uint_t, int, char *const []); extern int dtrace_program_exec(dtrace_hdl_t *, dtrace_prog_t *, dtrace_proginfo_t *); extern void dtrace_program_info(dtrace_hdl_t *, dtrace_prog_t *, dtrace_proginfo_t *); #define DTRACE_D_STRIP 0x01 /* strip non-loadable sections from program */ #define DTRACE_D_PROBES 0x02 /* include provider and probe definitions */ #define DTRACE_D_MASK 0x03 /* mask of valid flags to dtrace_dof_create */ extern int dtrace_program_link(dtrace_hdl_t *, dtrace_prog_t *, uint_t, const char *, int, char *const []); extern int dtrace_program_header(dtrace_hdl_t *, FILE *, const char *); extern void *dtrace_dof_create(dtrace_hdl_t *, dtrace_prog_t *, uint_t); extern void dtrace_dof_destroy(dtrace_hdl_t *, void *); extern void *dtrace_getopt_dof(dtrace_hdl_t *); extern void *dtrace_geterr_dof(dtrace_hdl_t *); typedef struct dtrace_stmtdesc { dtrace_ecbdesc_t *dtsd_ecbdesc; /* ECB description */ dtrace_actdesc_t *dtsd_action; /* action list */ dtrace_actdesc_t *dtsd_action_last; /* last action in action list */ void *dtsd_aggdata; /* aggregation data */ void *dtsd_fmtdata; /* type-specific output data */ void *dtsd_strdata; /* type-specific string data */ void (*dtsd_callback)(void); /* callback function for EPID */ void *dtsd_data; /* callback data pointer */ dtrace_attribute_t dtsd_descattr; /* probedesc attributes */ dtrace_attribute_t dtsd_stmtattr; /* statement attributes */ } dtrace_stmtdesc_t; typedef int dtrace_stmt_f(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *, void *); extern dtrace_stmtdesc_t *dtrace_stmt_create(dtrace_hdl_t *, dtrace_ecbdesc_t *); extern dtrace_actdesc_t *dtrace_stmt_action(dtrace_hdl_t *, dtrace_stmtdesc_t *); extern int dtrace_stmt_add(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *); extern int dtrace_stmt_iter(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmt_f *, void *); extern void dtrace_stmt_destroy(dtrace_hdl_t *, dtrace_stmtdesc_t *); /* * DTrace Data Consumption Interface */ typedef enum { DTRACEFLOW_ENTRY, DTRACEFLOW_RETURN, DTRACEFLOW_NONE } dtrace_flowkind_t; #define DTRACE_CONSUME_ERROR -1 /* error while processing */ #define DTRACE_CONSUME_THIS 0 /* consume this probe/record */ #define DTRACE_CONSUME_NEXT 1 /* advance to next probe/rec */ #define DTRACE_CONSUME_ABORT 2 /* abort consumption */ typedef struct dtrace_probedata { dtrace_hdl_t *dtpda_handle; /* handle to DTrace library */ dtrace_eprobedesc_t *dtpda_edesc; /* enabled probe description */ dtrace_probedesc_t *dtpda_pdesc; /* probe description */ processorid_t dtpda_cpu; /* CPU for data */ caddr_t dtpda_data; /* pointer to raw data */ dtrace_flowkind_t dtpda_flow; /* flow kind */ const char *dtpda_prefix; /* recommended flow prefix */ int dtpda_indent; /* recommended flow indent */ + uint64_t dtpda_timestamp; /* hrtime of snapshot */ } dtrace_probedata_t; typedef int dtrace_consume_probe_f(const dtrace_probedata_t *, void *); typedef int dtrace_consume_rec_f(const dtrace_probedata_t *, const dtrace_recdesc_t *, void *); extern int dtrace_consume(dtrace_hdl_t *, FILE *, dtrace_consume_probe_f *, dtrace_consume_rec_f *, void *); #define DTRACE_STATUS_NONE 0 /* no status; not yet time */ #define DTRACE_STATUS_OKAY 1 /* status okay */ #define DTRACE_STATUS_EXITED 2 /* exit() was called; tracing stopped */ #define DTRACE_STATUS_FILLED 3 /* fill buffer filled; tracing stoped */ #define DTRACE_STATUS_STOPPED 4 /* tracing already stopped */ extern int dtrace_status(dtrace_hdl_t *); /* * DTrace Formatted Output Interfaces * * To format output associated with a given dtrace_stmtdesc, the caller can * invoke one of the following functions, passing the opaque dtsd_fmtdata and a * list of record descriptions. These functions return either -1 to indicate * an error, or a positive integer indicating the number of records consumed. * For anonymous enablings, the consumer can use the dtrd_format member of * the record description to obtain a format description. The dtfd_string * member of the format description may be passed to dtrace_print{fa}_create() * to create the opaque format data. */ extern void *dtrace_printf_create(dtrace_hdl_t *, const char *); extern void *dtrace_printa_create(dtrace_hdl_t *, const char *); extern size_t dtrace_printf_format(dtrace_hdl_t *, void *, char *, size_t); +extern int dtrace_sprintf(dtrace_hdl_t *, FILE *, void *, + const dtrace_recdesc_t *, uint_t, + const void *, size_t); + extern int dtrace_fprintf(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *, size_t); extern int dtrace_fprinta(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *, size_t); extern int dtrace_system(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *, size_t); extern int dtrace_freopen(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *, size_t); /* * Type-specific output printing * * The print() action will associate a string data record that is actually the * fully-qualified type name of the data traced by the DIFEXPR action. This is * stored in the same 'format' record from the kernel, but we know by virtue of * the fact that the action is still DIFEXPR that it is actually a reference to * plain string data. */ extern int dtrace_print(dtrace_hdl_t *, FILE *, const char *, caddr_t, size_t); +extern int dtrace_format_print(dtrace_hdl_t *, FILE *, const char *, + caddr_t, size_t); /* * DTrace Work Interface */ typedef enum { DTRACE_WORKSTATUS_ERROR = -1, DTRACE_WORKSTATUS_OKAY, DTRACE_WORKSTATUS_DONE } dtrace_workstatus_t; extern dtrace_workstatus_t dtrace_work(dtrace_hdl_t *, FILE *, dtrace_consume_probe_f *, dtrace_consume_rec_f *, void *); /* * DTrace Handler Interface */ #define DTRACE_HANDLE_ABORT -1 /* abort current operation */ #define DTRACE_HANDLE_OK 0 /* handled okay; continue */ typedef struct dtrace_errdata { dtrace_hdl_t *dteda_handle; /* handle to DTrace library */ dtrace_eprobedesc_t *dteda_edesc; /* enabled probe inducing err */ dtrace_probedesc_t *dteda_pdesc; /* probe inducing error */ processorid_t dteda_cpu; /* CPU of error */ int dteda_action; /* action inducing error */ int dteda_offset; /* offset in DIFO of error */ int dteda_fault; /* specific fault */ uint64_t dteda_addr; /* address of fault, if any */ const char *dteda_msg; /* preconstructed message */ } dtrace_errdata_t; typedef int dtrace_handle_err_f(const dtrace_errdata_t *, void *); extern int dtrace_handle_err(dtrace_hdl_t *, dtrace_handle_err_f *, void *); typedef enum { DTRACEDROP_PRINCIPAL, /* drop to principal buffer */ DTRACEDROP_AGGREGATION, /* drop to aggregation buffer */ DTRACEDROP_DYNAMIC, /* dynamic drop */ DTRACEDROP_DYNRINSE, /* dyn drop due to rinsing */ DTRACEDROP_DYNDIRTY, /* dyn drop due to dirty */ DTRACEDROP_SPEC, /* speculative drop */ DTRACEDROP_SPECBUSY, /* spec drop due to busy */ DTRACEDROP_SPECUNAVAIL, /* spec drop due to unavail */ DTRACEDROP_STKSTROVERFLOW, /* stack string tab overflow */ DTRACEDROP_DBLERROR /* error in ERROR probe */ } dtrace_dropkind_t; typedef struct dtrace_dropdata { dtrace_hdl_t *dtdda_handle; /* handle to DTrace library */ processorid_t dtdda_cpu; /* CPU, if any */ dtrace_dropkind_t dtdda_kind; /* kind of drop */ uint64_t dtdda_drops; /* number of drops */ uint64_t dtdda_total; /* total drops */ const char *dtdda_msg; /* preconstructed message */ } dtrace_dropdata_t; typedef int dtrace_handle_drop_f(const dtrace_dropdata_t *, void *); extern int dtrace_handle_drop(dtrace_hdl_t *, dtrace_handle_drop_f *, void *); typedef void dtrace_handle_proc_f(struct ps_prochandle *, const char *, void *); extern int dtrace_handle_proc(dtrace_hdl_t *, dtrace_handle_proc_f *, void *); #define DTRACE_BUFDATA_AGGKEY 0x0001 /* aggregation key */ #define DTRACE_BUFDATA_AGGVAL 0x0002 /* aggregation value */ #define DTRACE_BUFDATA_AGGFORMAT 0x0004 /* aggregation format data */ #define DTRACE_BUFDATA_AGGLAST 0x0008 /* last for this key/val */ typedef struct dtrace_bufdata { dtrace_hdl_t *dtbda_handle; /* handle to DTrace library */ const char *dtbda_buffered; /* buffered output */ dtrace_probedata_t *dtbda_probe; /* probe data */ const dtrace_recdesc_t *dtbda_recdesc; /* record description */ const dtrace_aggdata_t *dtbda_aggdata; /* aggregation data, if agg. */ uint32_t dtbda_flags; /* flags; see above */ } dtrace_bufdata_t; typedef int dtrace_handle_buffered_f(const dtrace_bufdata_t *, void *); extern int dtrace_handle_buffered(dtrace_hdl_t *, dtrace_handle_buffered_f *, void *); typedef struct dtrace_setoptdata { dtrace_hdl_t *dtsda_handle; /* handle to DTrace library */ const dtrace_probedata_t *dtsda_probe; /* probe data */ const char *dtsda_option; /* option that was set */ dtrace_optval_t dtsda_oldval; /* old value */ dtrace_optval_t dtsda_newval; /* new value */ } dtrace_setoptdata_t; typedef int dtrace_handle_setopt_f(const dtrace_setoptdata_t *, void *); extern int dtrace_handle_setopt(dtrace_hdl_t *, dtrace_handle_setopt_f *, void *); /* * DTrace Aggregate Interface */ #define DTRACE_A_PERCPU 0x0001 #define DTRACE_A_KEEPDELTA 0x0002 #define DTRACE_A_ANONYMOUS 0x0004 #define DTRACE_A_TOTAL 0x0008 #define DTRACE_A_MINMAXBIN 0x0010 #define DTRACE_A_HASNEGATIVES 0x0020 #define DTRACE_A_HASPOSITIVES 0x0040 #define DTRACE_AGGZOOM_MAX 0.95 /* height of max bar */ #define DTRACE_AGGWALK_ERROR -1 /* error while processing */ #define DTRACE_AGGWALK_NEXT 0 /* proceed to next element */ #define DTRACE_AGGWALK_ABORT 1 /* abort aggregation walk */ #define DTRACE_AGGWALK_CLEAR 2 /* clear this element */ #define DTRACE_AGGWALK_NORMALIZE 3 /* normalize this element */ #define DTRACE_AGGWALK_DENORMALIZE 4 /* denormalize this element */ #define DTRACE_AGGWALK_REMOVE 5 /* remove this element */ struct dtrace_aggdata { dtrace_hdl_t *dtada_handle; /* handle to DTrace library */ dtrace_aggdesc_t *dtada_desc; /* aggregation description */ dtrace_eprobedesc_t *dtada_edesc; /* enabled probe description */ dtrace_probedesc_t *dtada_pdesc; /* probe description */ caddr_t dtada_data; /* pointer to raw data */ uint64_t dtada_normal; /* the normal -- 1 for denorm */ size_t dtada_size; /* total size of the data */ caddr_t dtada_delta; /* delta data, if available */ caddr_t *dtada_percpu; /* per CPU data, if avail */ caddr_t *dtada_percpu_delta; /* per CPU delta, if avail */ int64_t dtada_total; /* per agg total, if avail */ uint16_t dtada_minbin; /* minimum bin, if avail */ uint16_t dtada_maxbin; /* maximum bin, if avail */ uint32_t dtada_flags; /* flags */ }; typedef int dtrace_aggregate_f(const dtrace_aggdata_t *, void *); typedef int dtrace_aggregate_walk_f(dtrace_hdl_t *, dtrace_aggregate_f *, void *); typedef int dtrace_aggregate_walk_joined_f(const dtrace_aggdata_t **, const int, void *); extern void dtrace_aggregate_clear(dtrace_hdl_t *); extern int dtrace_aggregate_snap(dtrace_hdl_t *); extern int dtrace_aggregate_print(dtrace_hdl_t *, FILE *, dtrace_aggregate_walk_f *); extern int dtrace_aggregate_walk(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_joined(dtrace_hdl_t *, dtrace_aggvarid_t *, int, dtrace_aggregate_walk_joined_f *, void *); extern int dtrace_aggregate_walk_sorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_keysorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_valsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_keyvarsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_valvarsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_keyrevsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_valrevsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_keyvarrevsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); extern int dtrace_aggregate_walk_valvarrevsorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); #define DTRACE_AGD_PRINTED 0x1 /* aggregation printed in program */ /* * DTrace Process Control Interface * * Library clients who wish to have libdtrace create or grab processes for * monitoring of their symbol table changes may use these interfaces to * request that libdtrace obtain control of the process using libproc. */ extern struct ps_prochandle *dtrace_proc_create(dtrace_hdl_t *, const char *, char *const *, proc_child_func *, void *); extern struct ps_prochandle *dtrace_proc_grab(dtrace_hdl_t *, pid_t, int); extern void dtrace_proc_release(dtrace_hdl_t *, struct ps_prochandle *); extern void dtrace_proc_continue(dtrace_hdl_t *, struct ps_prochandle *); /* * DTrace Object, Symbol, and Type Interfaces * * Library clients can use libdtrace to perform symbol and C type information * lookups by symbol name, symbol address, or C type name, or to lookup meta- * information cached for each of the program objects in use by DTrace. The * resulting struct contain pointers to arbitrary-length strings, including * object, symbol, and type names, that are persistent until the next call to * dtrace_update(). Once dtrace_update() is called, any cached values must * be flushed and not used subsequently by the client program. */ #define DTRACE_OBJ_EXEC ((const char *)0L) /* primary executable file */ #define DTRACE_OBJ_RTLD ((const char *)1L) /* run-time link-editor */ #define DTRACE_OBJ_CDEFS ((const char *)2L) /* C include definitions */ #define DTRACE_OBJ_DDEFS ((const char *)3L) /* D program definitions */ #define DTRACE_OBJ_EVERY ((const char *)-1L) /* all known objects */ #define DTRACE_OBJ_KMODS ((const char *)-2L) /* all kernel objects */ #define DTRACE_OBJ_UMODS ((const char *)-3L) /* all user objects */ typedef struct dtrace_objinfo { const char *dto_name; /* object file scope name */ const char *dto_file; /* object file path (if any) */ int dto_id; /* object file id (if any) */ uint_t dto_flags; /* object flags (see below) */ GElf_Addr dto_text_va; /* address of text section */ GElf_Xword dto_text_size; /* size of text section */ GElf_Addr dto_data_va; /* address of data section */ GElf_Xword dto_data_size; /* size of data section */ GElf_Addr dto_bss_va; /* address of BSS */ GElf_Xword dto_bss_size; /* size of BSS */ } dtrace_objinfo_t; #define DTRACE_OBJ_F_KERNEL 0x1 /* object is a kernel module */ #define DTRACE_OBJ_F_PRIMARY 0x2 /* object is a primary module */ typedef int dtrace_obj_f(dtrace_hdl_t *, const dtrace_objinfo_t *, void *); extern int dtrace_object_iter(dtrace_hdl_t *, dtrace_obj_f *, void *); extern int dtrace_object_info(dtrace_hdl_t *, const char *, dtrace_objinfo_t *); typedef struct dtrace_syminfo { const char *dts_object; /* object name */ const char *dts_name; /* symbol name */ ulong_t dts_id; /* symbol id */ } dtrace_syminfo_t; extern int dtrace_lookup_by_name(dtrace_hdl_t *, const char *, const char *, GElf_Sym *, dtrace_syminfo_t *); extern int dtrace_lookup_by_addr(dtrace_hdl_t *, GElf_Addr addr, GElf_Sym *, dtrace_syminfo_t *); typedef struct dtrace_typeinfo { const char *dtt_object; /* object containing type */ ctf_file_t *dtt_ctfp; /* CTF container handle */ ctf_id_t dtt_type; /* CTF type identifier */ uint_t dtt_flags; /* Misc. flags */ } dtrace_typeinfo_t; #define DTT_FL_USER 0x1 /* user type */ extern int dtrace_lookup_by_type(dtrace_hdl_t *, const char *, const char *, dtrace_typeinfo_t *); extern int dtrace_symbol_type(dtrace_hdl_t *, const GElf_Sym *, const dtrace_syminfo_t *, dtrace_typeinfo_t *); extern int dtrace_type_strcompile(dtrace_hdl_t *, const char *, dtrace_typeinfo_t *); extern int dtrace_type_fcompile(dtrace_hdl_t *, FILE *, dtrace_typeinfo_t *); extern struct dt_node *dt_compile_sugar(dtrace_hdl_t *, struct dt_node *); /* * DTrace Probe Interface * * Library clients can use these functions to iterate over the set of available * probe definitions and inquire as to their attributes. The probe iteration * interfaces report probes that are declared as well as those from dtrace(7D). */ typedef struct dtrace_probeinfo { dtrace_attribute_t dtp_attr; /* name attributes */ dtrace_attribute_t dtp_arga; /* arg attributes */ const dtrace_typeinfo_t *dtp_argv; /* arg types */ int dtp_argc; /* arg count */ } dtrace_probeinfo_t; typedef int dtrace_probe_f(dtrace_hdl_t *, const dtrace_probedesc_t *, void *); extern int dtrace_probe_iter(dtrace_hdl_t *, const dtrace_probedesc_t *pdp, dtrace_probe_f *, void *); extern int dtrace_probe_info(dtrace_hdl_t *, const dtrace_probedesc_t *, dtrace_probeinfo_t *); /* * DTrace Vector Interface * * The DTrace library normally speaks directly to dtrace(7D). However, * this communication may be vectored elsewhere. Consumers who wish to * perform a vectored open must fill in the vector, and use the dtrace_vopen() * entry point to obtain a library handle. */ struct dtrace_vector { #ifdef illumos int (*dtv_ioctl)(void *, int, void *); #else int (*dtv_ioctl)(void *, u_long, void *); #endif int (*dtv_lookup_by_addr)(void *, GElf_Addr, GElf_Sym *, dtrace_syminfo_t *); int (*dtv_status)(void *, processorid_t); long (*dtv_sysconf)(void *, int); }; /* * DTrace Utility Functions * * Library clients can use these functions to convert addresses strings, to * convert between string and integer probe descriptions and the * dtrace_probedesc_t representation, and to perform similar conversions on * stability attributes. */ extern int dtrace_addr2str(dtrace_hdl_t *, uint64_t, char *, int); extern int dtrace_uaddr2str(dtrace_hdl_t *, pid_t, uint64_t, char *, int); extern int dtrace_xstr2desc(dtrace_hdl_t *, dtrace_probespec_t, const char *, int, char *const [], dtrace_probedesc_t *); extern int dtrace_str2desc(dtrace_hdl_t *, dtrace_probespec_t, const char *, dtrace_probedesc_t *); extern int dtrace_id2desc(dtrace_hdl_t *, dtrace_id_t, dtrace_probedesc_t *); #define DTRACE_DESC2STR_MAX 1024 /* min buf size for dtrace_desc2str() */ extern char *dtrace_desc2str(const dtrace_probedesc_t *, char *, size_t); #define DTRACE_ATTR2STR_MAX 64 /* min buf size for dtrace_attr2str() */ extern char *dtrace_attr2str(dtrace_attribute_t, char *, size_t); extern int dtrace_str2attr(const char *, dtrace_attribute_t *); extern const char *dtrace_stability_name(dtrace_stability_t); extern const char *dtrace_class_name(dtrace_class_t); extern int dtrace_provider_modules(dtrace_hdl_t *, const char **, int); extern const char *const _dtrace_version; extern int _dtrace_debug; #ifdef __cplusplus } #endif #ifndef illumos #define _SC_CPUID_MAX _SC_NPROCESSORS_CONF #define _SC_NPROCESSORS_MAX _SC_NPROCESSORS_CONF #endif +/* + * Values for the dt_oformat property. + */ +#define DTRACE_OFORMAT_TEXT 0 +#define DTRACE_OFORMAT_STRUCTURED 1 + +extern int dtrace_oformat_configure(dtrace_hdl_t *); +extern int dtrace_oformat(dtrace_hdl_t *); +extern void dtrace_set_outfp(const FILE *); +extern void dtrace_oformat_setup(dtrace_hdl_t *); +extern void dtrace_oformat_teardown(dtrace_hdl_t *); +extern void dtrace_oformat_probe(dtrace_hdl_t *, const dtrace_probedata_t *, + processorid_t, dtrace_probedesc_t *); + #endif /* _DTRACE_H */ diff --git a/cddl/lib/libdtrace/Makefile b/cddl/lib/libdtrace/Makefile index 0b12410fbb47..d5c857bc06a0 100644 --- a/cddl/lib/libdtrace/Makefile +++ b/cddl/lib/libdtrace/Makefile @@ -1,160 +1,160 @@ SHLIBDIR?= /lib .include .PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libdtrace/common .PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libgen/common PACKAGE= dtrace LIB= dtrace SRCS= 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_grammar.y \ dt_handle.c \ dt_ident.c \ dt_isadep.c \ dt_inttab.c \ dt_lex.l \ dt_link.c \ dt_list.c \ dt_map.c \ dt_module.c \ dt_names.c \ dt_open.c \ dt_options.c \ dt_parser.c \ dt_pcb.c \ dt_pid.c \ dt_pq.c \ dt_pragma.c \ dt_print.c \ dt_printf.c \ dt_proc.c \ dt_program.c \ dt_provider.c \ dt_regset.c \ dt_string.c \ dt_strtab.c \ dt_subr.c \ dt_sugar.c \ dt_work.c \ dt_xlator.c \ gmatch.c DSRCS= errno.d \ io.d \ ip.d \ psinfo.d \ sctp.d \ siftr.d \ signal.d \ tcp.d \ socket.d \ udp.d \ udplite.d \ unistd.d FILES= ${DSRCS} FILESDIR= /usr/lib/dtrace FILESMODE= ${NOBINMODE} INCSGROUPS= INCS SYSINCS SYSINCSDIR= ${INCLUDEDIR}/sys INCS+= ${SRCTOP}/cddl/contrib/opensolaris/lib/libdtrace/common/dtrace.h SYSINCS+= ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h SYSINCS+= ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/sys/ctf_api.h WARNS?= 1 CFLAGS+= -DIN_BASE CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/ CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd CFLAGS+= -I${SRCTOP}/sys CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h CFLAGS+= -DHAVE_ISSETUGID CFLAGS+= -I${.OBJDIR} -I${.CURDIR} \ -I${SRCTOP}/sys/cddl/dev/dtrace/${MACHINE_ARCH} \ -I${SRCTOP}/sys/cddl/dev/kinst \ -I${SRCTOP}/sys/cddl/compat/opensolaris \ -I${SRCTOP}/cddl/compat/opensolaris/include \ -I${OPENSOLARIS_USR_DISTDIR}/head \ -I${OPENSOLARIS_USR_DISTDIR}/lib/libctf/common \ -I${OPENSOLARIS_USR_DISTDIR}/lib/libdtrace/common \ -I${OPENSOLARIS_SYS_DISTDIR}/uts/common #CFLAGS+= -DYYDEBUG .if ${MACHINE_CPUARCH} == "aarch64" CFLAGS+= -I${OPENSOLARIS_SYS_DISTDIR}/uts/aarch64 .PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libdtrace/aarch64 .PATH: ${SRCTOP}/sys/cddl/dev/dtrace/aarch64 .elif ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" CFLAGS+= -I${SRCTOP}/sys/cddl/dev/dtrace/x86 CFLAGS+= -I${OPENSOLARIS_SYS_DISTDIR}/uts/intel -DDIS_MEM .PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libdtrace/i386 .PATH: ${SRCTOP}/sys/cddl/dev/dtrace/${MACHINE_ARCH} .PATH: ${SRCTOP}/sys/cddl/dev/dtrace/x86 .elif ${MACHINE_CPUARCH} == "arm" CFLAGS+= -I${OPENSOLARIS_SYS_DISTDIR}/uts/arm .PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libdtrace/arm .PATH: ${SRCTOP}/sys/cddl/dev/dtrace/arm .elif ${MACHINE_CPUARCH} == "powerpc" CFLAGS+= -I${OPENSOLARIS_SYS_DISTDIR}/uts/powerpc .PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libdtrace/powerpc .PATH: ${SRCTOP}/sys/cddl/dev/dtrace/powerpc .elif ${MACHINE_CPUARCH} == "riscv" CFLAGS+= -I${OPENSOLARIS_SYS_DISTDIR}/uts/riscv .PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libdtrace/riscv .PATH: ${SRCTOP}/sys/cddl/dev/dtrace/riscv .else # temporary hack CFLAGS+= -I${OPENSOLARIS_SYS_DISTDIR}/uts/intel .endif .if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64" SRCS+= dis_tables.c instr_size.c DSRCS+= regs_x86.d .endif .if ${MACHINE_CPUARCH} == "riscv" SRCS+= instr_size.c DSRCS+= regs_riscv.d .endif .if ${MACHINE_CPUARCH} == "aarch64" SRCS+= instr_size.c DSRCS+= regs_aarch64.d .endif YFLAGS+=-d .if ${MK_DTRACE_ASAN} != "no" CFLAGS+= -fsanitize=address -fsanitize=undefined LDFLAGS+= -fsanitize=address -fsanitize=undefined .endif -LIBADD= ctf elf proc pthread rtld_db +LIBADD= ctf elf proc pthread rtld_db xo CLEANFILES= dt_errtags.c dt_names.c .include dt_errtags.c: ${OPENSOLARIS_USR_DISTDIR}/lib/libdtrace/common/dt_errtags.h sh ${OPENSOLARIS_USR_DISTDIR}/lib/libdtrace/common/mkerrtags.sh < ${.ALLSRC} > ${.TARGET} dt_names.c: ${OPENSOLARIS_SYS_DISTDIR}/uts/common/sys/dtrace.h sh ${OPENSOLARIS_USR_DISTDIR}/lib/libdtrace/common/mknames.sh < ${.ALLSRC} > ${.TARGET} diff --git a/cddl/usr.sbin/dtrace/Makefile b/cddl/usr.sbin/dtrace/Makefile index 4dcfc0ebef7b..0a1ad0b81da0 100644 --- a/cddl/usr.sbin/dtrace/Makefile +++ b/cddl/usr.sbin/dtrace/Makefile @@ -1,43 +1,43 @@ .include .PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/dtrace PACKAGE= dtrace PROG= dtrace SRCS= dtrace.c BINDIR?= /usr/sbin WARNS?= 1 CFLAGS+= -DIN_BASE CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/ CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd CFLAGS+= -I${SRCTOP}/sys CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \ -I${SRCTOP}/cddl/compat/opensolaris/include \ -I${OPENSOLARIS_USR_DISTDIR}/head \ -I${OPENSOLARIS_USR_DISTDIR}/lib/libdtrace/common \ -I${OPENSOLARIS_USR_DISTDIR}/lib/libproc/common \ -I${OPENSOLARIS_SYS_DISTDIR}/uts/common \ -I${OPENSOLARIS_SYS_DISTDIR}/compat CFLAGS+= -DHAVE_ISSETUGID # Optional debugging stuff... #CFLAGS+= -DNEED_ERRLOC #YFLAGS+= -d .if ${MK_DTRACE_ASAN} != "no" CFLAGS+= -fsanitize=address -fsanitize=undefined LDFLAGS+= -fsanitize=address -fsanitize=undefined .endif -LIBADD= dtrace ctf elf proc spl +LIBADD= dtrace ctf elf proc spl xo .if ${MK_DTRACE_TESTS} != "no" SUBDIR+= tests .endif .include diff --git a/cddl/usr.sbin/dtrace/tests/common/Makefile b/cddl/usr.sbin/dtrace/tests/common/Makefile index cbcb41316094..9f694cabf99a 100644 --- a/cddl/usr.sbin/dtrace/tests/common/Makefile +++ b/cddl/usr.sbin/dtrace/tests/common/Makefile @@ -1,91 +1,92 @@ # We exclude several subdirectories: nfs and sysevent do not compile on # FreeBSD, and docsExamples doesn't seem amenable to automated testing. TESTSDIR= ${TESTSBASE}/cddl/usr.sbin/dtrace/common TESTS_SUBDIRS+= aggs \ arithmetic \ arrays \ assocs \ begin \ bitfields \ buffering \ builtinvar \ cg \ clauses \ cpc \ decls \ drops \ dtraceUtil \ end \ env \ enum \ error \ exit \ fbtprovider \ funcs \ grammar \ include \ inline \ io \ ip \ java_api \ json \ kinst \ lexer \ llquantize \ mdb \ mib \ misc \ multiaggs \ offsetof \ + oformat \ operators \ pid \ plockstat \ pointers \ pragma \ predicates \ preprocessor \ print \ printa \ printf \ privs \ probes \ proc \ profile-n \ providers \ raise \ rates \ safety \ scalars \ sched \ scripting \ sdt \ sizeof \ speculation \ stability \ stack \ stackdepth \ stop \ strlen \ strtoll \ struct \ sugar \ syscall \ tick-n \ trace \ tracemem \ translators \ typedef \ types \ uctf \ union \ usdt \ ustack \ vars \ version \ .PATH: ${.CURDIR:H:H:H:H:H}/tests KYUAFILE= YES .include diff --git a/cddl/usr.sbin/dtrace/tests/common/oformat/Makefile b/cddl/usr.sbin/dtrace/tests/common/oformat/Makefile new file mode 100644 index 000000000000..219833285d0c --- /dev/null +++ b/cddl/usr.sbin/dtrace/tests/common/oformat/Makefile @@ -0,0 +1,42 @@ +# +# This Makefile was generated by $srcdir/cddl/usr.sbin/dtrace/tests/tools/genmakefiles.sh. +# + +PACKAGE= tests + +${PACKAGE}FILES= \ + tst.agg.avg.ksh \ + tst.agg.count.ksh \ + tst.agg.llquantize.ksh \ + tst.agg.lquantize.ksh \ + tst.agg.max.ksh \ + tst.agg.min.ksh \ + tst.agg.quantize.ksh \ + tst.agg.stddev.ksh \ + tst.agg.sum.ksh \ + tst.aggmod.ksh \ + tst.aggstack.ksh \ + tst.aggsym.ksh \ + tst.aggustack.ksh \ + tst.aggusym.ksh \ + tst.drop.ksh \ + tst.mod.ksh \ + tst.print.ksh \ + tst.printf.ksh \ + tst.printm.ksh \ + tst.stack.ksh \ + tst.sym.ksh \ + tst.trace.ksh \ + tst.tracemem.ksh \ + tst.umod.ksh \ + tst.ustack.ksh \ + tst.usym.ksh \ + +TESTEXES= \ + + +CFILES= \ + + + +.include "../../dtrace.test.mk" diff --git a/cddl/usr.sbin/dtrace/tests/dtrace.test.mk b/cddl/usr.sbin/dtrace/tests/dtrace.test.mk index 022c42f7617e..001dc6253f82 100644 --- a/cddl/usr.sbin/dtrace/tests/dtrace.test.mk +++ b/cddl/usr.sbin/dtrace/tests/dtrace.test.mk @@ -1,49 +1,49 @@ TESTGROUP= ${.CURDIR:H:T}/${.CURDIR:T} TESTBASE= ${SRCTOP}/cddl/contrib/opensolaris/cmd/dtrace/test/tst TESTSRC= ${TESTBASE}/${TESTGROUP} TESTSDIR= ${TESTSBASE}/cddl/usr.sbin/dtrace/${TESTGROUP} FILESGROUPS+= ${TESTGROUP}EXE ${TESTGROUP}EXE= ${TESTEXES} ${TESTGROUP}EXEMODE= 0555 ${TESTGROUP}EXEPACKAGE= ${PACKAGE} TESTWRAPPER= t_dtrace_contrib ATF_TESTS_SH+= ${TESTWRAPPER} -TEST_METADATA.t_dtrace_contrib+= required_files="/usr/local/bin/ksh /usr/local/bin/perl" +TEST_METADATA.t_dtrace_contrib+= required_files="/usr/local/bin/ksh /usr/local/bin/jq /usr/local/bin/perl /usr/local/bin/xmllint" TEST_METADATA.t_dtrace_contrib+= required_user="root" GENTEST?= ${.CURDIR:H:H}/tools/gentest.sh EXCLUDE= ${.CURDIR:H:H}/tools/exclude.sh ${TESTWRAPPER}.sh: ${GENTEST} ${EXCLUDE} ${${PACKAGE}FILES} env TESTBASE=${TESTBASE:Q} \ sh ${GENTEST} -e ${EXCLUDE} ${TESTGROUP} ${${PACKAGE}FILES:S/ */ /} > ${.TARGET} CLEANFILES+= ${TESTWRAPPER}.sh .PATH: ${TESTSRC} PROGS= ${CFILES:T:S/.c$/.exe/g} .for prog in ${PROGS} SRCS.${prog}+= ${prog:S/.exe$/.c/} .if exists(${prog:S/^tst.//:S/.exe$/.d/}) SRCS.${prog}+= ${prog:S/^tst.//:S/.exe$/.d/} .endif .endfor BINDIR= ${TESTSDIR} MAN= # Some tests depend on the internals of their corresponding test programs, # so make sure the optimizer doesn't interfere with them. CFLAGS+= -O0 # Test programs shouldn't be stripped; else we generally can't use the PID # provider. DEBUG_FLAGS= -g STRIP= .include diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 81cd5baca513..1b37720363f1 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -1,1201 +1,1203 @@ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 tags=package=tests . bin cat .. chflags .. chmod .. cp .. date .. dd .. echo .. expr .. hostname .. ln .. ls .. mkdir .. mv .. pax .. pkill .. pwait .. rm .. rmdir .. sh builtins .. errors .. execution .. expansion .. invocation .. parameters .. parser .. set-e .. .. sleep .. test .. timeout .. .. cddl lib .. sbin .. usr.bin ctfconvert .. ztest .. .. usr.sbin dtrace common aggs .. arithmetic .. arrays .. assocs .. begin .. bitfields .. buffering .. builtinvar .. cg .. clauses .. cpc .. decls .. drops .. dtraceUtil .. end .. env .. enum .. error .. exit .. fbtprovider .. funcs .. grammar .. include .. inline .. io .. ip .. java_api .. json .. kinst .. lexer .. llquantize .. mdb .. mib .. misc .. multiaggs .. offsetof .. + oformat + .. operators .. pid .. plockstat .. pointers .. pragma .. predicates .. preprocessor .. print .. printa .. printf .. privs .. probes .. proc .. profile-n .. providers .. raise .. rates .. safety .. scalars .. sched .. scripting .. sdt .. sizeof .. speculation .. stability .. stack .. stackdepth .. stop .. strlen .. strtoll .. struct .. sugar .. syscall .. sysevent .. tick-n .. trace .. tracemem .. translators .. typedef .. types .. uctf .. union .. usdt .. ustack .. vars .. version .. .. i386 arrays .. funcs .. pid .. ustack .. .. amd64 arrays .. .. .. zfsd .. .. .. etc rc.d .. .. examples .. games .. gnu lib .. usr.bin diff .. .. .. include .. lib atf libatf-c detail .. .. libatf-c++ detail .. .. test-programs .. .. csu dynamic .. dynamiclib .. static .. .. googletest gmock .. gmock_main .. gtest .. gtest_main .. .. libarchive .. libbe .. libc c063 .. db .. gen execve .. posix_spawn .. .. hash data .. .. iconv .. inet .. locale .. net getaddrinfo data .. .. .. nss .. regex data .. .. resolv .. rpc .. ssp .. setjmp .. stdio .. stdlib .. string .. sys .. time .. tls dso .. .. termios .. ttyio .. .. libcam .. libcasper services cap_dns .. cap_grp .. cap_pwd .. cap_sysctl .. .. .. libcrypt .. libdevdctl .. libexecinfo .. libkvm .. libmp .. libnv .. libproc .. libregex data .. .. librt .. libsbuf .. libsysdecode .. libthr dlopen .. .. libutil .. libxo .. msun .. .. libexec atf atf-check .. atf-pytest-wrapper .. atf-sh .. .. nuageinit .. rc .. rtld-elf rtld_deepbind .. .. tftpd .. .. sbin bectl .. dhclient .. devd .. growfs .. ifconfig .. ipfw .. md5 .. mdconfig .. newfs_msdos .. nvmecontrol .. pfctl files .. .. ping .. route .. sysctl .. .. secure lib .. libexec .. usr.bin .. usr.sbin .. .. share examples tests atf .. googletest .. plain .. tap .. .. .. zoneinfo .. .. sys acl .. aio .. audit .. auditpipe .. capsicum .. cddl zfs bin .. include .. tests acl cifs .. nontrivial .. trivial .. .. atime .. bootfs .. cache .. cachefile .. clean_mirror .. cli_root zfs_upgrade .. zfs_promote .. zfs_clone .. zfs_property .. zfs_destroy .. zpool_create .. zpool_history .. zpool_expand .. zpool_remove .. zfs_mount .. zfs_unshare .. zdb .. zpool_online .. zpool_get .. zpool_export .. zfs_copies .. zfs_get .. zfs .. zpool_clear .. zpool_import blockfiles .. .. zpool .. zpool_offline .. zpool_replace .. zfs_rollback .. zpool_set .. zfs_send .. zfs_set .. zpool_detach .. zfs_diff .. zpool_scrub .. zfs_inherit .. zfs_snapshot .. zfs_share .. zpool_destroy .. zpool_status .. zfs_unmount .. zfs_receive .. zfs_create .. zpool_upgrade blockfiles .. .. zpool_add .. zfs_rename .. zpool_attach .. zfs_reservation .. .. cli_user misc .. zfs_list .. zpool_iostat .. zpool_list .. .. compression .. ctime .. delegate .. devices .. exec .. grow_pool .. grow_replicas .. history .. hotplug .. hotspare .. inheritance .. interop .. inuse .. iscsi .. large_files .. largest_pool .. link_count .. migration .. mmap .. mount .. mv_files .. nestedfs .. no_space .. online_offline .. pool_names .. poolversion .. quota .. redundancy .. refquota .. refreserv .. rename_dirs .. replacement .. reservation .. rootpool .. rsend .. scrub_mirror .. slog .. snapshot .. snapused .. sparse .. threadsappend .. truncate .. txg_integrity .. userquota .. utils_test .. write_dirs .. xattr .. zfsd .. zil .. zinject .. zones .. zvol zvol_ENOSPC .. zvol_cli .. zvol_misc .. zvol_swap .. .. zvol_thrash .. .. .. .. compat32 .. devrandom .. dtrace .. fifo .. file .. fs fusefs .. tarfs .. tmpfs .. .. geom class concat .. eli .. gate .. gpt .. mirror .. multipath .. nop .. part .. raid3 .. shsec .. stripe .. uzip etalon .. .. .. .. kern acct .. execve .. pipe .. .. kqueue libkqueue .. .. mac bsdextended .. ipacl .. portacl .. .. mqueue .. net if_ovpn .. routing .. .. netgraph .. netinet .. netinet6 frag6 .. .. netipsec tunnel .. .. netlink .. netmap .. netpfil common .. ipfw .. pf ioctl .. .. .. opencrypto .. pjdfstest chflags .. chmod .. chown .. ftruncate .. granular .. link .. mkdir .. mkfifo .. mknod .. open .. rename .. rmdir .. symlink .. truncate .. unlink .. utimensat .. .. posixshm .. sys .. vfs .. vm stack .. .. vmm .. .. usr.bin apply .. asa .. awk bugs-fixed .. netbsd .. .. basename .. bintrans .. bmake archives fmt_44bsd .. fmt_44bsd_mod .. fmt_oldbsd .. .. basic t0 .. t1 .. t2 .. t3 .. .. execution ellipsis .. empty .. joberr .. plus .. .. shell builtin .. meta .. path .. path_select .. replace .. select .. .. suffixes basic .. src_wild1 .. src_wild2 .. .. syntax directive-t0 .. enl .. funny-targets .. semi .. .. sysmk t0 2 1 .. .. mk .. .. t1 2 1 .. .. mk .. .. t2 2 1 .. .. mk .. .. .. variables modifier_M .. modifier_t .. opt_V .. t0 .. .. .. bsdcat .. calendar .. cmp .. compress .. cpio .. col .. comm .. csplit .. cut .. dc .. diff .. diff3 .. dirname .. du .. factor .. file2c .. file .. find .. fold .. getconf .. gh-bc .. grep .. gzip .. head .. hexdump .. ident .. indent .. join .. jot .. lastcomm .. limits .. locale .. lockf .. lorder .. m4 .. mkimg .. mktemp .. ncal .. opensm .. patch .. pr .. printf .. procstat .. renice .. rs .. sdiff .. sed regress.multitest.out .. .. seq .. soelim .. sort .. split .. stat .. tail .. tar .. tftp .. touch .. tr .. truncate .. tsort .. units .. unifdef .. uniq .. unzip .. vmstat .. wc .. xargs .. xinstall .. xo .. yacc yacc .. .. .. usr.sbin chown .. daemon .. etcupdate .. extattr .. fstyp .. jail .. makefs .. newsyslog .. nmtree .. praudit .. pw .. rpcbind .. sa .. syslogd .. .. .. # vim: set expandtab ts=4 sw=4: diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index 8885835c0632..98702814c911 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -1,797 +1,797 @@ # # The include file define library names suitable # for INTERNALLIB and PRIVATELIB definition .if !target(____) .error src.libnames.mk cannot be included directly. .endif .if !target(____) ____: .include _PRIVATELIBS= \ atf_c \ atf_cxx \ auditd \ bsddialog \ bsdstat \ cbor \ devdctl \ event1 \ fido2 \ gmock \ gtest \ gmock_main \ gtest_main \ heimipcc \ heimipcs \ ldns \ sqlite3 \ ssh \ ucl \ unbound \ zstd # Let projects based on FreeBSD append to _PRIVATELIBS # by maintaining their own LOCAL_PRIVATELIBS list. _PRIVATELIBS+= ${LOCAL_PRIVATELIBS} _INTERNALLIBS= \ amu \ bsnmptools \ c_nossp_pic \ cron \ elftc \ fdt \ fifolog \ ifconfig \ ipf \ iscsiutil \ lpr \ lua \ lutok \ netbsd \ ntp \ ntpevent \ openbsd \ opts \ parse \ pe \ pfctl \ pmcstat \ sl \ sm \ smdb \ smutil \ telnet \ vers \ wpaap \ wpacommon \ wpacrypto \ wpadrivers \ wpaeap_common \ wpaeap_peer \ wpaeap_server \ wpaeapol_auth \ wpaeapol_supp \ wpal2_packet \ wparadius \ wparsn_supp \ wpatls \ wpautils \ wpawps # Let projects based on FreeBSD append to _INTERNALLIBS # by maintaining their own LOCAL_INTERNALLIBS list. _INTERNALLIBS+= ${LOCAL_INTERNALLIBS} _LIBRARIES= \ ${_PRIVATELIBS} \ ${_INTERNALLIBS} \ ${LOCAL_LIBRARIES} \ 80211 \ 9p \ alias \ archive \ asn1 \ avl \ BlocksRuntime \ be \ begemot \ bluetooth \ bsdxml \ bsm \ bsnmp \ bz2 \ c \ c_pic \ calendar \ cam \ casper \ cap_dns \ cap_fileargs \ cap_grp \ cap_net \ cap_netdb \ cap_pwd \ cap_sysctl \ cap_syslog \ com_err \ compiler_rt \ crypt \ crypto \ ctf \ cuse \ cxxrt \ devctl \ devdctl \ devinfo \ devstat \ dialog \ dl \ dpv \ dtrace \ dwarf \ edit \ efivar \ elf \ execinfo \ fetch \ figpar \ formw \ geom \ gpio \ gssapi \ gssapi_krb5 \ hdb \ heimbase \ heimntlm \ heimsqlite \ hx509 \ icp \ ipsec \ ipt \ jail \ kadm5clnt \ kadm5srv \ kafs5 \ kdc \ kiconv \ krb5 \ kvm \ l \ lzma \ m \ magic \ md \ memstat \ mp \ mt \ ncursesw \ netgraph \ netmap \ ngatm \ nv \ nvpair \ opencsd \ pam \ panel \ panelw \ pcap \ pcsclite \ pjdlog \ pmc \ proc \ procstat \ pthread \ radius \ regex \ roken \ rpcsec_gss \ rpcsvc \ rt \ rtld_db \ sbuf \ sdp \ sm \ smb \ spl \ ssl \ ssp_nonshared \ stats \ stdthreads \ supcplusplus \ sysdecode \ tacplus \ termcapw \ tinfow \ tpool \ ufs \ ugidfw \ ulog \ umem \ usb \ usbhid \ util \ uutil \ vmmapi \ wind \ wrap \ xo \ y \ ypclnt \ z \ zfs_core \ zfs \ zfsbootenv \ zpool \ zutil .if ${MK_BLACKLIST} != "no" _LIBRARIES+= \ blacklist \ .endif .if ${MK_OFED} != "no" _LIBRARIES+= \ cxgb4 \ ibcm \ ibmad \ ibnetdisc \ ibumad \ ibverbs \ irdma \ mlx4 \ mlx5 \ rdmacm \ osmcomp \ opensm \ osmvendor .endif .if ${MK_BEARSSL} == "yes" _LIBRARIES+= \ bearssl \ secureboot \ LIBBEARSSL?= ${LIBBEARSSLDIR}/libbearssl.a LIBSECUREBOOT?= ${LIBSECUREBOOTDIR}/libsecureboot.a .endif .if ${MK_VERIEXEC} == "yes" _LIBRARIES+= veriexec LIBVERIEXEC?= ${LIBVERIEXECDIR}/libveriexec.a .endif # Each library's LIBADD needs to be duplicated here for static linkage of # 2nd+ order consumers. Auto-generating this would be better. _DP_80211= sbuf bsdxml _DP_9p= sbuf .if ${MK_CASPER} != "no" _DP_9p+= casper cap_pwd cap_grp .endif # XXX: Not bootstrapped so uses host version on non-FreeBSD, so don't use a # FreeBSD-specific dependency list .if ${.MAKE.OS} == "FreeBSD" || !defined(BOOTSTRAPPING) _DP_archive= z bz2 lzma bsdxml zstd .endif _DP_avl= spl _DP_bsddialog= formw ncursesw tinfow _DP_zstd= pthread .if ${MK_BLACKLIST} != "no" _DP_blacklist+= pthread .endif _DP_crypto= pthread # See comment by _DP_archive above .if ${.MAKE.OS} == "FreeBSD" || !defined(BOOTSTRAPPING) .if ${MK_OPENSSL} != "no" _DP_archive+= crypto .else _DP_archive+= md .endif .endif _DP_sqlite3= pthread _DP_ssl= crypto _DP_ssh= crypto crypt z .if ${MK_LDNS} != "no" _DP_ssh+= ldns .endif _DP_edit= tinfow .if ${MK_OPENSSL} != "no" _DP_bsnmp= crypto .endif _DP_geom= bsdxml sbuf _DP_cam= sbuf _DP_kvm= elf _DP_casper= nv _DP_cap_dns= nv _DP_cap_fileargs= nv _DP_cap_grp= nv _DP_cap_pwd= nv _DP_cap_sysctl= nv _DP_cap_syslog= nv .if ${MK_OFED} != "no" _DP_pcap= ibverbs mlx5 .endif _DP_pjdlog= util _DP_usb= pthread _DP_unbound= ssl crypto pthread _DP_rt= pthread .if ${MK_OPENSSL} == "no" _DP_radius= md .else _DP_radius= crypto .endif _DP_rtld_db= elf procstat _DP_procstat= kvm util elf _DP_proc= cxxrt .if ${MK_CDDL} != "no" _DP_proc+= ctf .endif _DP_proc+= elf procstat rtld_db util z _DP_mp= crypto _DP_memstat= kvm _DP_magic= z _DP_mt= sbuf bsdxml _DP_ldns= ssl crypto _DP_lua= m _DP_lutok= lua .if ${MK_OPENSSL} != "no" _DP_fetch= ssl crypto .else _DP_fetch= md .endif _DP_execinfo= elf _DP_dwarf= elf z _DP_dpv= dialog figpar util tinfow ncursesw _DP_dialog= tinfow ncursesw m _DP_cuse= pthread _DP_atf_cxx= atf_c _DP_gtest= pthread regex _DP_gmock= gtest _DP_gmock_main= gmock _DP_gtest_main= gtest _DP_devstat= kvm _DP_pam= radius tacplus md util .if ${MK_KERBEROS} != "no" _DP_pam+= krb5 .endif .if ${MK_OPENSSH} != "no" _DP_fido2+= crypto z _DP_pam+= ssh .endif .if ${MK_NIS} != "no" _DP_pam+= ypclnt .endif _DP_roken= crypt _DP_kadm5clnt= com_err krb5 roken _DP_kadm5srv= com_err hdb krb5 roken _DP_heimntlm= crypto com_err krb5 roken _DP_hx509= asn1 com_err crypto roken wind _DP_hdb= asn1 com_err krb5 roken sqlite3 _DP_asn1= com_err roken _DP_kdc= roken hdb hx509 krb5 heimntlm asn1 crypto _DP_wind= com_err roken _DP_heimbase= pthread _DP_heimipcc= heimbase roken pthread _DP_heimipcs= heimbase roken pthread _DP_kafs5= asn1 krb5 roken _DP_krb5= asn1 com_err crypt crypto hx509 roken wind heimbase heimipcc _DP_gssapi_krb5= gssapi krb5 crypto roken asn1 com_err _DP_lzma= md pthread _DP_ucl= m _DP_vmmapi= util _DP_opencsd= cxxrt _DP_ctf= spl z -_DP_dtrace= ctf elf proc pthread rtld_db +_DP_dtrace= ctf elf proc pthread rtld_db xo _DP_xo= util _DP_ztest= geom m nvpair umem zpool pthread avl zfs_core spl zutil zfs uutil icp # The libc dependencies are not strictly needed but are defined to make the # assert happy. _DP_c= compiler_rt # Use libssp_nonshared only on i386 and power*. Other archs emit direct calls # to __stack_chk_fail, not __stack_chk_fail_local provided by libssp_nonshared. .if ${MK_SSP} != "no" && \ (${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH:Mpower*} != "") _DP_c+= ssp_nonshared .endif _DP_stats= sbuf pthread _DP_stdthreads= pthread _DP_tacplus= md pam _DP_ncursesw= tinfow _DP_formw= ncursesw _DP_nvpair= spl _DP_panelw= ncursesw _DP_rpcsec_gss= gssapi _DP_smb= kiconv _DP_ulog= md _DP_fifolog= z _DP_ipf= kvm _DP_tpool= spl _DP_uutil= avl spl _DP_zfs= md pthread rt umem util uutil m avl bsdxml crypto geom nvpair \ z zfs_core zutil _DP_zfsbootenv= zfs nvpair _DP_zfs_core= nvpair spl zutil _DP_zpool= md pthread z icp spl nvpair avl umem _DP_zutil= avl geom m tpool _DP_be= zfs spl nvpair zfsbootenv _DP_netmap= _DP_ifconfig= m _DP_pfctl= nv # OFED support .if ${MK_OFED} != "no" _DP_cxgb4= ibverbs pthread _DP_ibcm= ibverbs _DP_ibmad= ibumad _DP_ibnetdisc= osmcomp ibmad ibumad _DP_ibumad= _DP_ibverbs= _DP_irdma= ibverbs pthread _DP_mlx4= ibverbs pthread _DP_mlx5= ibverbs pthread _DP_rdmacm= ibverbs _DP_osmcomp= pthread _DP_opensm= pthread _DP_osmvendor= ibumad pthread .endif # Define special cases LDADD_supcplusplus= -lsupc++ LIBATF_C= ${LIBDESTDIR}${LIBDIR_BASE}/libprivateatf-c.a LIBATF_CXX= ${LIBDESTDIR}${LIBDIR_BASE}/libprivateatf-c++.a LDADD_atf_c= -lprivateatf-c LDADD_atf_cxx= -lprivateatf-c++ LIBGMOCK= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategmock.a LIBGMOCK_MAIN= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategmock_main.a LIBGTEST= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategtest.a LIBGTEST_MAIN= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategtest_main.a LDADD_gmock= -lprivategmock LDADD_gtest= -lprivategtest LDADD_gmock_main= -lprivategmock_main LDADD_gtest_main= -lprivategtest_main .for _l in ${_PRIVATELIBS} LIB${_l:tu}?= ${LIBDESTDIR}${LIBDIR_BASE}/libprivate${_l}.a .endfor .if ${MK_PIE} != "no" PIE_SUFFIX= _pie .endif .for _l in ${_LIBRARIES} .if ${_INTERNALLIBS:M${_l}} || !defined(SYSROOT) LDADD_${_l}_L+= -L${LIB${_l:tu}DIR} .endif DPADD_${_l}?= ${LIB${_l:tu}} .if ${_PRIVATELIBS:M${_l}} LDADD_${_l}?= -lprivate${_l} .elif ${_INTERNALLIBS:M${_l}} LDADD_${_l}?= ${LDADD_${_l}_L} -l${_l:S/${PIE_SUFFIX}//}${PIE_SUFFIX} .else LDADD_${_l}?= ${LDADD_${_l}_L} -l${_l} .endif # Add in all dependencies for static linkage. # Bootstrapping from non-FreeBSD needs special handling, since it overrides # NO_SHARED back to yes despite only building static versions of bootstrap # libraries (see tools/build/mk/Makefile.boot.pre). .if defined(_DP_${_l}) && (${_INTERNALLIBS:M${_l}} || \ (defined(NO_SHARED) && ${NO_SHARED:tl} != "no") || \ (defined(BOOTSTRAPPING) && ${.MAKE.OS} != "FreeBSD")) .for _d in ${_DP_${_l}} DPADD_${_l}+= ${DPADD_${_d}} LDADD_${_l}+= ${LDADD_${_d}} .endfor .endif .endfor # These are special cases where the library is broken and anything that uses # it needs to add more dependencies. Broken usually means that it has a # cyclic dependency and cannot link its own dependencies. This is bad, please # fix the library instead. # Unless the library itself is broken then the proper place to define # dependencies is _DP_* above. # libatf-c++ exposes libatf-c abi hence we need to explicit link to atf_c for # atf_cxx DPADD_atf_cxx+= ${DPADD_atf_c} LDADD_atf_cxx+= ${LDADD_atf_c} DPADD_gmock+= ${DPADD_gtest} LDADD_gmock+= ${LDADD_gtest} DPADD_gmock_main+= ${DPADD_gmock} LDADD_gmock_main+= ${LDADD_gmock} DPADD_gtest_main+= ${DPADD_gtest} LDADD_gtest_main+= ${LDADD_gtest} # Detect LDADD/DPADD that should be LIBADD, before modifying LDADD here. _BADLDADD= .for _l in ${LDADD:M-l*:N-l*/*:C,^-l,,} .if ${_LIBRARIES:M${_l}} && !${_PRIVATELIBS:M${_l}} _BADLDADD+= ${_l} .endif .endfor .if !empty(_BADLDADD) .error ${.CURDIR}: These libraries should be LIBADD+=foo rather than DPADD/LDADD+=-lfoo: ${_BADLDADD} .endif .for _l in ${LIBADD} DPADD+= ${DPADD_${_l}} LDADD+= ${LDADD_${_l}} .endfor _LIB_OBJTOP?= ${OBJTOP} # INTERNALLIB definitions. LIBELFTCDIR= ${_LIB_OBJTOP}/lib/libelftc LIBELFTC?= ${LIBELFTCDIR}/libelftc${PIE_SUFFIX}.a LIBFDTDIR= ${_LIB_OBJTOP}/lib/libfdt LIBFDT?= ${LIBFDTDIR}/libfdt${PIE_SUFFIX}.a LIBLUADIR= ${_LIB_OBJTOP}/lib/liblua LIBLUA?= ${LIBLUADIR}/liblua${PIE_SUFFIX}.a LIBLUTOKDIR= ${_LIB_OBJTOP}/lib/liblutok LIBLUTOK?= ${LIBLUTOKDIR}/liblutok${PIE_SUFFIX}.a LIBPEDIR= ${_LIB_OBJTOP}/lib/libpe LIBPE?= ${LIBPEDIR}/libpe${PIE_SUFFIX}.a LIBOPENBSDDIR= ${_LIB_OBJTOP}/lib/libopenbsd LIBOPENBSD?= ${LIBOPENBSDDIR}/libopenbsd${PIE_SUFFIX}.a LIBSMDIR= ${_LIB_OBJTOP}/lib/libsm LIBSM?= ${LIBSMDIR}/libsm${PIE_SUFFIX}.a LIBSMDBDIR= ${_LIB_OBJTOP}/lib/libsmdb LIBSMDB?= ${LIBSMDBDIR}/libsmdb${PIE_SUFFIX}.a LIBSMUTILDIR= ${_LIB_OBJTOP}/lib/libsmutil LIBSMUTIL?= ${LIBSMUTILDIR}/libsmutil${PIE_SUFFIX}.a LIBNETBSDDIR?= ${_LIB_OBJTOP}/lib/libnetbsd LIBNETBSD?= ${LIBNETBSDDIR}/libnetbsd${PIE_SUFFIX}.a LIBVERSDIR?= ${_LIB_OBJTOP}/kerberos5/lib/libvers LIBVERS?= ${LIBVERSDIR}/libvers${PIE_SUFFIX}.a LIBSLDIR= ${_LIB_OBJTOP}/kerberos5/lib/libsl LIBSL?= ${LIBSLDIR}/libsl${PIE_SUFFIX}.a LIBIFCONFIGDIR= ${_LIB_OBJTOP}/lib/libifconfig LIBIFCONFIG?= ${LIBIFCONFIGDIR}/libifconfig${PIE_SUFFIX}.a LIBIPFDIR= ${_LIB_OBJTOP}/sbin/ipf/libipf LIBIPF?= ${LIBIPFDIR}/libipf${PIE_SUFFIX}.a LIBNVDIR= ${_LIB_OBJTOP}/lib/libnv LIBNV?= ${LIBNVDIR}/libnv${PIE_SUFFIX}.a LIBISCSIUTILDIR= ${_LIB_OBJTOP}/lib/libiscsiutil LIBISCSIUTIL?= ${LIBISCSIUTILDIR}/libiscsiutil${PIE_SUFFIX}.a LIBTELNETDIR= ${_LIB_OBJTOP}/lib/libtelnet LIBTELNET?= ${LIBTELNETDIR}/libtelnet${PIE_SUFFIX}.a LIBCRONDIR= ${_LIB_OBJTOP}/usr.sbin/cron/lib LIBCRON?= ${LIBCRONDIR}/libcron${PIE_SUFFIX}.a LIBNTPDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libntp LIBNTP?= ${LIBNTPDIR}/libntp${PIE_SUFFIX}.a LIBNTPEVENTDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libntpevent LIBNTPEVENT?= ${LIBNTPEVENTDIR}/libntpevent${PIE_SUFFIX}.a LIBOPTSDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libopts LIBOPTS?= ${LIBOPTSDIR}/libopts${PIE_SUFFIX}.a LIBPARSEDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libparse LIBPARSE?= ${LIBPARSEDIR}/libparse${PIE_SUFFIX}.a LIBPFCTL= ${_LIB_OBJTOP}/lib/libpfctl LIBPFCTL?= ${LIBPFCTLDIR}/libpfctl${PIE_SUFFIX}.a LIBLPRDIR= ${_LIB_OBJTOP}/usr.sbin/lpr/common_source LIBLPR?= ${LIBLPRDIR}/liblpr${PIE_SUFFIX}.a LIBFIFOLOGDIR= ${_LIB_OBJTOP}/usr.sbin/fifolog/lib LIBFIFOLOG?= ${LIBFIFOLOGDIR}/libfifolog${PIE_SUFFIX}.a LIBBSNMPTOOLSDIR= ${_LIB_OBJTOP}/usr.sbin/bsnmpd/tools/libbsnmptools LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools${PIE_SUFFIX}.a LIBBE?= ${LIBBEDIR}/libbe${PIE_SUFFIX}.a LIBPMCSTATDIR= ${_LIB_OBJTOP}/lib/libpmcstat LIBPMCSTAT?= ${LIBPMCSTATDIR}/libpmcstat${PIE_SUFFIX}.a LIBWPAAPDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/ap LIBWPAAP?= ${LIBWPAAPDIR}/libwpaap${PIE_SUFFIX}.a LIBWPACOMMONDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/common LIBWPACOMMON?= ${LIBWPACOMMONDIR}/libwpacommon${PIE_SUFFIX}.a LIBWPACRYPTODIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/crypto LIBWPACRYPTO?= ${LIBWPACRYPTODIR}/libwpacrypto${PIE_SUFFIX}.a LIBWPADRIVERSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/drivers LIBWPADRIVERS?= ${LIBWPADRIVERSDIR}/libwpadrivers${PIE_SUFFIX}.a LIBWPAEAP_COMMONDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eap_common LIBWPAEAP_COMMON?= ${LIBWPAEAP_COMMONDIR}/libwpaeap_common${PIE_SUFFIX}.a LIBWPAEAP_PEERDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eap_peer LIBWPAEAP_PEER?= ${LIBWPAEAP_PEERDIR}/libwpaeap_peer${PIE_SUFFIX}.a LIBWPAEAP_SERVERDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eap_server LIBWPAEAP_SERVER?= ${LIBWPAEAP_SERVERDIR}/libwpaeap_server${PIE_SUFFIX}.a LIBWPAEAPOL_AUTHDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eapol_auth LIBWPAEAPOL_AUTH?= ${LIBWPAEAPOL_AUTHDIR}/libwpaeapol_auth${PIE_SUFFIX}.a LIBWPAEAPOL_SUPPDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/eapol_supp LIBWPAEAPOL_SUPP?= ${LIBWPAEAPOL_SUPPDIR}/libwpaeapol_supp${PIE_SUFFIX}.a LIBWPAL2_PACKETDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/l2_packet LIBWPAL2_PACKET?= ${LIBWPAL2_PACKETDIR}/libwpal2_packet${PIE_SUFFIX}.a LIBWPARADIUSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/radius LIBWPARADIUS?= ${LIBWPARADIUSDIR}/libwparadius${PIE_SUFFIX}.a LIBWPARSN_SUPPDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/rsn_supp LIBWPARSN_SUPP?= ${LIBWPARSN_SUPPDIR}/libwparsn_supp${PIE_SUFFIX}.a LIBWPATLSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/tls LIBWPATLS?= ${LIBWPATLSDIR}/libwpatls${PIE_SUFFIX}.a LIBWPAUTILSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/utils LIBWPAUTILS?= ${LIBWPAUTILSDIR}/libwpautils${PIE_SUFFIX}.a LIBWPAWPSDIR= ${_LIB_OBJTOP}/usr.sbin/wpa/src/wps LIBWPAWPS?= ${LIBWPAWPSDIR}/libwpawps${PIE_SUFFIX}.a LIBC_NOSSP_PICDIR= ${_LIB_OBJTOP}/lib/libc LIBC_NOSSP_PIC?= ${LIBC_NOSSP_PICDIR}/libc_nossp_pic.a # Define a directory for each library. This is useful for adding -L in when # not using a --sysroot or for meta mode bootstrapping when there is no # Makefile.depend. These are sorted by directory. LIBAVLDIR= ${_LIB_OBJTOP}/cddl/lib/libavl LIBCTFDIR= ${_LIB_OBJTOP}/cddl/lib/libctf LIBDTRACEDIR= ${_LIB_OBJTOP}/cddl/lib/libdtrace LIBICPDIR= ${_LIB_OBJTOP}/cddl/lib/libicp LIBICP?= ${LIBICPDIR}/libicp${PIE_SUFFIX}.a LIBICP_RESCUEDIR= ${_LIB_OBJTOP}/cddl/lib/libicp_rescue LIBICP_RESCUE?= ${LIBICP_RESCUEDIR}/libicp_rescue${PIE_SUFFIX}.a LIBNVPAIRDIR= ${_LIB_OBJTOP}/cddl/lib/libnvpair LIBNVPAIR?= ${LIBNVPAIRDIR}/libnvpair${PIE_SUFFIX}.a LIBUMEMDIR= ${_LIB_OBJTOP}/cddl/lib/libumem LIBUUTILDIR= ${_LIB_OBJTOP}/cddl/lib/libuutil LIBZFSDIR= ${_LIB_OBJTOP}/cddl/lib/libzfs LIBZFS?= ${LIBZFSDIR}/libzfs${PIE_SUFFIX}.a LIBZFS_COREDIR= ${_LIB_OBJTOP}/cddl/lib/libzfs_core LIBZFS_CORE?= ${LIBZFS_COREDIR}/libzfs_core${PIE_SUFFIX}.a LIBZFSBOOTENVDIR= ${_LIB_OBJTOP}/cddl/lib/libzfsbootenv LIBZFSBOOTENV?= ${LIBZFSBOOTENVDIR}/libzfsbootenv${PIE_SUFFIX}.a LIBZPOOLDIR= ${_LIB_OBJTOP}/cddl/lib/libzpool LIBZPOOL?= ${LIBZPOOLDIR}/libzpool${PIE_SUFFIX}.a LIBZUTILDIR= ${_LIB_OBJTOP}/cddl/lib/libzutil LIBZUTIL?= ${LIBZUTILDIR}/libzutil${PIE_SUFFIX}.a LIBTPOOLDIR= ${_LIB_OBJTOP}/cddl/lib/libtpool # OFED support LIBCXGB4DIR= ${_LIB_OBJTOP}/lib/ofed/libcxgb4 LIBIBCMDIR= ${_LIB_OBJTOP}/lib/ofed/libibcm LIBIBMADDIR= ${_LIB_OBJTOP}/lib/ofed/libibmad LIBIBNETDISCDIR=${_LIB_OBJTOP}/lib/ofed/libibnetdisc LIBIBUMADDIR= ${_LIB_OBJTOP}/lib/ofed/libibumad LIBIBVERBSDIR= ${_LIB_OBJTOP}/lib/ofed/libibverbs LIBIRDMADIR= ${_LIB_OBJTOP}/lib/ofed/libirdma LIBMLX4DIR= ${_LIB_OBJTOP}/lib/ofed/libmlx4 LIBMLX5DIR= ${_LIB_OBJTOP}/lib/ofed/libmlx5 LIBRDMACMDIR= ${_LIB_OBJTOP}/lib/ofed/librdmacm LIBOSMCOMPDIR= ${_LIB_OBJTOP}/lib/ofed/complib LIBOPENSMDIR= ${_LIB_OBJTOP}/lib/ofed/libopensm LIBOSMVENDORDIR=${_LIB_OBJTOP}/lib/ofed/libvendor LIBDIALOGDIR= ${_LIB_OBJTOP}/gnu/lib/libdialog LIBSSPDIR= ${_LIB_OBJTOP}/lib/libssp LIBSSP_NONSHAREDDIR= ${_LIB_OBJTOP}/lib/libssp_nonshared LIBASN1DIR= ${_LIB_OBJTOP}/kerberos5/lib/libasn1 LIBGSSAPI_KRB5DIR= ${_LIB_OBJTOP}/kerberos5/lib/libgssapi_krb5 LIBGSSAPI_NTLMDIR= ${_LIB_OBJTOP}/kerberos5/lib/libgssapi_ntlm LIBGSSAPI_SPNEGODIR= ${_LIB_OBJTOP}/kerberos5/lib/libgssapi_spnego LIBHDBDIR= ${_LIB_OBJTOP}/kerberos5/lib/libhdb LIBHEIMBASEDIR= ${_LIB_OBJTOP}/kerberos5/lib/libheimbase LIBHEIMIPCCDIR= ${_LIB_OBJTOP}/kerberos5/lib/libheimipcc LIBHEIMIPCSDIR= ${_LIB_OBJTOP}/kerberos5/lib/libheimipcs LIBHEIMNTLMDIR= ${_LIB_OBJTOP}/kerberos5/lib/libheimntlm LIBHX509DIR= ${_LIB_OBJTOP}/kerberos5/lib/libhx509 LIBKADM5CLNTDIR= ${_LIB_OBJTOP}/kerberos5/lib/libkadm5clnt LIBKADM5SRVDIR= ${_LIB_OBJTOP}/kerberos5/lib/libkadm5srv LIBKAFS5DIR= ${_LIB_OBJTOP}/kerberos5/lib/libkafs5 LIBKDCDIR= ${_LIB_OBJTOP}/kerberos5/lib/libkdc LIBKRB5DIR= ${_LIB_OBJTOP}/kerberos5/lib/libkrb5 LIBROKENDIR= ${_LIB_OBJTOP}/kerberos5/lib/libroken LIBWINDDIR= ${_LIB_OBJTOP}/kerberos5/lib/libwind LIBATF_CDIR= ${_LIB_OBJTOP}/lib/atf/libatf-c LIBATF_CXXDIR= ${_LIB_OBJTOP}/lib/atf/libatf-c++ LIBGMOCKDIR= ${_LIB_OBJTOP}/lib/googletest/gmock LIBGMOCK_MAINDIR= ${_LIB_OBJTOP}/lib/googletest/gmock_main LIBGTESTDIR= ${_LIB_OBJTOP}/lib/googletest/gtest LIBGTEST_MAINDIR= ${_LIB_OBJTOP}/lib/googletest/gtest_main LIBALIASDIR= ${_LIB_OBJTOP}/lib/libalias/libalias LIBBLACKLISTDIR= ${_LIB_OBJTOP}/lib/libblacklist LIBBLOCKSRUNTIMEDIR= ${_LIB_OBJTOP}/lib/libblocksruntime LIBBSNMPDIR= ${_LIB_OBJTOP}/lib/libbsnmp/libbsnmp LIBCASPERDIR= ${_LIB_OBJTOP}/lib/libcasper/libcasper LIBCAP_DNSDIR= ${_LIB_OBJTOP}/lib/libcasper/services/cap_dns LIBCAP_GRPDIR= ${_LIB_OBJTOP}/lib/libcasper/services/cap_grp LIBCAP_NETDIR= ${_LIB_OBJTOP}/lib/libcasper/services/cap_net LIBCAP_PWDDIR= ${_LIB_OBJTOP}/lib/libcasper/services/cap_pwd LIBCAP_SYSCTLDIR= ${_LIB_OBJTOP}/lib/libcasper/services/cap_sysctl LIBCAP_SYSLOGDIR= ${_LIB_OBJTOP}/lib/libcasper/services/cap_syslog LIBCBORDIR= ${_LIB_OBJTOP}/lib/libcbor LIBBSDXMLDIR= ${_LIB_OBJTOP}/lib/libexpat LIBFIDO2DIR= ${_LIB_OBJTOP}/lib/libfido2 LIBKVMDIR= ${_LIB_OBJTOP}/lib/libkvm LIBPTHREADDIR= ${_LIB_OBJTOP}/lib/libthr LIBMDIR= ${_LIB_OBJTOP}/lib/msun LIBFORMWDIR= ${_LIB_OBJTOP}/lib/ncurses/form LIBMENUWDIR= ${_LIB_OBJTOP}/lib/ncurses/menu LIBNCURSESWDIR= ${_LIB_OBJTOP}/lib/ncurses/ncurses LIBTINFOWDIR= ${_LIB_OBJTOP}/lib/ncurses/tinfo LIBPANELWDIR= ${_LIB_OBJTOP}/lib/ncurses/panel LIBCRYPTODIR= ${_LIB_OBJTOP}/secure/lib/libcrypto LIBSPLDIR= ${_LIB_OBJTOP}/cddl/lib/libspl LIBSSHDIR= ${_LIB_OBJTOP}/secure/lib/libssh LIBSSLDIR= ${_LIB_OBJTOP}/secure/lib/libssl LIBTEKENDIR= ${_LIB_OBJTOP}/sys/teken/libteken LIBEGACYDIR= ${_LIB_OBJTOP}/tools/build LIBLNDIR= ${_LIB_OBJTOP}/usr.bin/lex/lib LIBTERMCAPWDIR= ${LIBTINFOWDIR} .-include # Default other library directories to lib/libNAME. .for lib in ${_LIBRARIES} LIB${lib:tu}DIR?= ${OBJTOP}/lib/lib${lib} .endfor # Validate that listed LIBADD are valid. .for _l in ${LIBADD} .if empty(_LIBRARIES:M${_l}) _BADLIBADD+= ${_l} .endif .endfor .if !empty(_BADLIBADD) .error ${.CURDIR}: Invalid LIBADD used which may need to be added to ${_this:T}: ${_BADLIBADD} .endif # Sanity check that libraries are defined here properly when building them. .if defined(LIB) && ${_LIBRARIES:M${LIB}} != "" .if !empty(LIBADD) && \ (!defined(_DP_${LIB}) || ${LIBADD:O:u} != ${_DP_${LIB}:O:u}) .error ${.CURDIR}: Missing or incorrect _DP_${LIB} entry in ${_this:T}. Should match LIBADD for ${LIB} ('${LIBADD}' vs '${_DP_${LIB}}') .endif # Note that OBJTOP is not yet defined here but for the purpose of the check # it is fine as it resolves to the SRC directory. .if !defined(LIB${LIB:tu}DIR) || !exists(${SRCTOP}/${LIB${LIB:tu}DIR:S,^${OBJTOP}/,,}) .error ${.CURDIR}: Missing or incorrect value for LIB${LIB:tu}DIR in ${_this:T}: ${LIB${LIB:tu}DIR:S,^${OBJTOP}/,,} .endif .if ${_INTERNALLIBS:M${LIB}} != "" && !defined(LIB${LIB:tu}) .error ${.CURDIR}: Missing value for LIB${LIB:tu} in ${_this:T}. Likely should be: LIB${LIB:tu}?= $${LIB${LIB:tu}DIR}/lib${LIB}.a .endif .endif .endif # !target(____)