[DTrace-devel] [PATCH v3] pid: Support probe names specifying offsets

Kris Van Hees kris.van.hees at oracle.com
Thu Feb 1 05:38:41 UTC 2024


On Wed, Jan 31, 2024 at 08:35:16PM -0500, eugene.loh--- via DTrace-devel wrote:
> From: Eugene Loh <eugene.loh at oracle.com>
> 
> In DTrace, a pid-provider probe name can be "entry" or "return".
> The probe name can also specify offsets, including wildcards (globs).
> 
> Add support for "offset" probes.
> 
> Note that offset used for the pid probe name is different from that used
> for the underlying uprobe.  So introduce a new field pps_nameoff that
> allows us to track both offsets.
> 
> Also, the "float" trigger when launched with "before" timing will
> presumably never fire the entry or return probes.  So, tst.emptystack.d
> ought perhaps to be split into a entry/return test and an offset test.
> 
> Signed-off-by: Eugene Loh <eugene.loh at oracle.com>

Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>

> ---
>  Makeconfig                         |   8 +-
>  dtrace.spec                        |   6 +-
>  include/dtrace/pid.h               |   9 +-
>  libcommon/uprobes.c                |   4 +-
>  libdtrace/Build                    |  10 +-
>  libdtrace/dt_pid.c                 | 195 +++++++++++++++++----
>  libdtrace/dt_prov_uprobe.c         |   6 +-
>  test/unittest/pid/tst.emptystack.d |   3 +-
>  test/unittest/pid/tst.float.d      |  17 +-
>  test/unittest/pid/tst.offsets.r    |   1 +
>  test/unittest/pid/tst.offsets.sh   | 270 +++++++++++++++++++++++++++++
>  test/unittest/pid/tst.probefunc.d  |   3 +-
>  12 files changed, 466 insertions(+), 66 deletions(-)
>  create mode 100644 test/unittest/pid/tst.offsets.r
>  create mode 100755 test/unittest/pid/tst.offsets.sh
> 
> diff --git a/Makeconfig b/Makeconfig
> index 08cb4810..f666b8ec 100644
> --- a/Makeconfig
> +++ b/Makeconfig
> @@ -1,7 +1,7 @@
>  # Determine properties of the system and write a config.h.
>  #
>  # Oracle Linux DTrace.
> -# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
> +# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
>  # Licensed under the Universal Permissive License v 1.0 as shown at
>  # http://oss.oracle.com/licenses/upl.
>  
> @@ -40,7 +40,7 @@ endef
>  # The first argument must be suitable for a filename fragment,
>  # for a makefile rule name and for a #define.
>  #
> -# Syntax: $(call check-symbol,name,symbol,library)
> +# Syntax: $(call check-symbol-rule,name,symbol,library)
>  define check-symbol-rule
>  $(objdir)/.config/config.$(1).h $(objdir)/.config/config.$(1).mk: $(objdir)/.config/.dir.stamp
>  	if echo 'void $(2)(); int main(void) { $(2)(); }' | \
> @@ -103,3 +103,7 @@ endif
>  $(eval $(call check-header-rule,FUSE_NUMA,fuse_set_numa,fuse/fuse_lowlevel))
>  $(eval $(call check-header-symbol-rule,CLOSE_RANGE,close_range(3,~0U,0),c,unistd))
>  $(eval $(call check-header-rule,GETTID,gettid,unistd))
> +$(eval $(call check-header-rule,DIS1,disassembler(NULL),dis-asm))
> +$(eval $(call check-header-rule,DIS4,disassembler(0,0,0,NULL),dis-asm))
> +$(eval $(call check-header-rule,INITDISINFO3,init_disassemble_info(NULL,NULL,NULL),dis-asm))
> +$(eval $(call check-header-rule,INITDISINFO4,init_disassemble_info(NULL,NULL,NULL,NULL),dis-asm))
> diff --git a/dtrace.spec b/dtrace.spec
> index baf56d62..7f61be86 100644
> --- a/dtrace.spec
> +++ b/dtrace.spec
> @@ -1,7 +1,7 @@
>  # spec file for package dtrace
>  #
>  # Oracle Linux DTrace.
> -# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
> +# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
>  # Licensed under the Universal Permissive License v 1.0 as shown at
>  # http://oss.oracle.com/licenses/upl.
>  
> @@ -74,6 +74,10 @@ BuildRequires: kernel%{variant}-devel = 5.15.0-0.16.2%{?dist}uek
>  %endif
>  BuildRequires: gcc-bpf-unknown-none
>  BuildRequires: binutils-bpf-unknown-none
> +%ifnarch aarch64
> +Requires:     binutils
> +BuildRequires: binutils-devel
> +%endif
>  %if %{with_libctf}
>  Requires:     binutils >= 2.30-58.0.8
>  BuildRequires: binutils-devel >= 2.30-58.0.8
> diff --git a/include/dtrace/pid.h b/include/dtrace/pid.h
> index 5e966825..1fd594b6 100644
> --- a/include/dtrace/pid.h
> +++ b/include/dtrace/pid.h
> @@ -2,7 +2,7 @@
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   *
> - * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved.
>   */
>  
>  /*
> @@ -38,13 +38,10 @@ typedef struct pid_probespec {
>  	uint64_t pps_off;			/* probe offset (in object) */
>  
>  	/*
> -	 * Fields below this point do not apply to probes of type
> -	 * DTPPT_UNDERLYING.
> +	 * Fields below this point do not apply to underlying probes.
>  	 */
>  	pid_t pps_pid;				/* task PID */
> -	uint64_t pps_vaddr;			/* object base address */
> -	uint64_t pps_size;			/* function size (in bytes) */
> -	char pps_gstr[1];			/* glob pattern string */
> +	uint64_t pps_nameoff;			/* offset to use for name */
>  } pid_probespec_t;
>  
>  #endif /* _DTRACE_PID_H */
> diff --git a/libcommon/uprobes.c b/libcommon/uprobes.c
> index 3bb423fc..c3b21d60 100644
> --- a/libcommon/uprobes.c
> +++ b/libcommon/uprobes.c
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -275,7 +275,7 @@ out:
>  }
>  
>  /*
> - * Like uprobe_create, but do not specify the name of a corresponding DTrace
> + * Like uprobe_create_named, but do not specify the name of a corresponding DTrace
>   * probe.  (Used when the caller already knows what probe will be needed, and
>   * there is no possibility of another DTrace having to pick it up from the
>   * systemwide uprobe list.)
> diff --git a/libdtrace/Build b/libdtrace/Build
> index 3929a9c0..4ed04fdf 100644
> --- a/libdtrace/Build
> +++ b/libdtrace/Build
> @@ -82,16 +82,22 @@ SHLIBS += libdtrace
>  libdtrace_DIR := $(current-dir)
>  libdtrace_TARGET = libdtrace
>  ifdef HAVE_LIBCTF
> -libdtrace_LIBS := -lctf -lelf -lz -lrt -lpcap -lpthread -ldl -lm -lpfm
> +libdtrace_LIBS := -lctf
>  else
> -libdtrace_LIBS := -ldtrace-ctf -lelf -lz -lrt -lpcap -lpthread -ldl -lm -lpfm
> +libdtrace_LIBS := -ldtrace-ctf
>  endif
> +libdtrace_LIBS += -lelf -lz -lrt -lpcap -lpthread -ldl -lm -lpfm
>  libdtrace_VERSION := 2.0.0
>  libdtrace_SONAME := libdtrace.so.2
>  libdtrace_VERSCRIPT := libdtrace.ver
>  libdtrace_LIBSOURCES := libdtrace-build libproc libport libcommon
>  libdtrace_SECONDARY := libproc libport
>  
> +# Required for dt_pid.c for varying instruction lengths on x86
> +ifeq ($(shell uname -m), x86_64)
> +libdtrace_LIBS += -lopcodes
> +endif
> +
>  # Disable certain warnings for these files
>  dt_btf.c_CFLAGS := -Wno-pedantic
>  dt_consume.c_CFLAGS := -Wno-pedantic
> diff --git a/libdtrace/dt_pid.c b/libdtrace/dt_pid.c
> index 70e0330f..758ab832 100644
> --- a/libdtrace/dt_pid.c
> +++ b/libdtrace/dt_pid.c
> @@ -1,6 +1,6 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> @@ -9,6 +9,7 @@
>  #include <string.h>
>  #include <stdlib.h>
>  #include <stdio.h>
> +#include <stdbool.h>
>  #include <errno.h>
>  #include <ctype.h>
>  #include <alloca.h>
> @@ -16,6 +17,9 @@
>  #include <stddef.h>
>  #include <sys/ioctl.h>
>  #include <sys/sysmacros.h>
> +#if defined(__amd64)
> +#include <dis-asm.h>
> +#endif
>  
>  #include <port.h>
>  #include <uprobes.h>
> @@ -115,19 +119,15 @@ dt_pid_free_uprobespecs(dtrace_hdl_t *dtp)
>  }
>  
>  static int
> -dt_pid_create_fbt_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
> +dt_pid_create_one_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
>      pid_probespec_t *psp, const GElf_Sym *symp, pid_probetype_t type)
>  {
> -	const dt_provider_t	*pvp;
> +	const dt_provider_t	*pvp = dtp->dt_prov_pid;
>  
> -	psp->pps_prv = "pid";
>  	psp->pps_type = type;
> -	psp->pps_off = symp->st_value - psp->pps_vaddr;
> -	psp->pps_size = (size_t)symp->st_size;
> -	psp->pps_gstr[0] = '\0';		/* no glob pattern */
> +	psp->pps_prv = "pid";
>  
>  	/* Make sure we have a PID provider. */
> -	pvp = dtp->dt_prov_pid;
>  	if (pvp == NULL) {
>  		pvp = dt_provider_lookup(dtp, psp->pps_prv);
>  		if (pvp == NULL)
> @@ -143,20 +143,22 @@ dt_pid_create_fbt_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
>  	return pvp->impl->provide_probe(dtp, psp);
>  }
>  
> +#if defined(__amd64)
> +#if defined(HAVE_INITDISINFO3) == defined(HAVE_INITDISINFO4)
> +#error expect init_disassembler_info() to have 3 or else 4 arguments
> +#endif
> +#ifdef HAVE_INITDISINFO4
> +static int
> +my_callback2(void *stream, enum disassembler_style style, const char *fmt, ...) {
> +	return 0;
> +}
> +#endif
>  static int
> -dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp,
> -    pid_probespec_t *psp, const GElf_Sym *symp, const char *pattern)
> +my_callback(void *stream, const char *fmt, ...)
>  {
> -	psp->pps_type = DTPPT_OFFSETS;
> -	psp->pps_off = symp->st_value - psp->pps_vaddr;
> -	psp->pps_size = (size_t)symp->st_size;
> -
> -	strcpy(psp->pps_gstr, pattern);
> -
> -	/* Create a probe using 'psp'. */
> -
> -	return 1;
> +	return 0;
>  }
> +#endif
>  
>  static int
>  dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
> @@ -166,7 +168,6 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
>  	dt_proc_t *dpr = pp->dpp_dpr;
>  	pid_probespec_t *psp;
>  	uint64_t off;
> -	char *end;
>  	uint_t nmatches = 0;
>  	ulong_t sz;
>  	int glob, rc = 0;
> @@ -182,7 +183,7 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
>  	dt_dprintf("creating probe pid%d:%s:%s:%s at %lx\n", (int)pid,
>  	    pp->dpp_obj, func, pp->dpp_name, symp->st_value);
>  
> -	sz = sizeof(pid_probespec_t) + strlen(pp->dpp_name);
> +	sz = sizeof(pid_probespec_t);
>  	psp = dt_zalloc(dtp, sz);
>  	if (psp == NULL) {
>  		dt_dprintf("proc_per_sym: dt_alloc(%lu) failed\n", sz);
> @@ -194,11 +195,12 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
>  	psp->pps_dev = pp->dpp_dev;
>  	psp->pps_inum = pp->dpp_inum;
>  	psp->pps_fn = strdup(pp->dpp_fname);
> -	psp->pps_vaddr = pp->dpp_vaddr;
>  	psp->pps_fun = (char *) func;
> +	psp->pps_nameoff = 0;
> +	psp->pps_off = symp->st_value - pp->dpp_vaddr;
>  
>  	if (!isdash && gmatch("return", pp->dpp_name)) {
> -		if (dt_pid_create_fbt_probe(pp->dpp_pr, dtp, psp, symp,
> +		if (dt_pid_create_one_probe(pp->dpp_pr, dtp, psp, symp,
>  		    DTPPT_RETURN) < 0) {
>  			rc = dt_pid_error(
>  				dtp, pcb, dpr, D_PROC_CREATEFAIL,
> @@ -211,7 +213,7 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
>  	}
>  
>  	if (!isdash && gmatch("entry", pp->dpp_name)) {
> -		if (dt_pid_create_fbt_probe(pp->dpp_pr, dtp, psp, symp,
> +		if (dt_pid_create_one_probe(pp->dpp_pr, dtp, psp, symp,
>  		    DTPPT_ENTRY) < 0) {
>  			rc = dt_pid_error(
>  				dtp, pcb, dpr, D_PROC_CREATEFAIL,
> @@ -225,6 +227,8 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
>  
>  	glob = strisglob(pp->dpp_name);
>  	if (!glob && nmatches == 0) {
> +		char *end;
> +
>  		off = strtoull(pp->dpp_name, &end, 16);
>  		if (*end != '\0') {
>  			rc = dt_pid_error(dtp, pcb, dpr, D_PROC_NAME,
> @@ -241,8 +245,10 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
>  			goto out;
>  		}
>  
> -		if (dt_pid_create_glob_offset_probes(pp->dpp_pr, pp->dpp_dtp,
> -					psp, symp, pp->dpp_name) < 0) {
> +		psp->pps_nameoff = off;
> +		psp->pps_off = symp->st_value - pp->dpp_vaddr + off;
> +		if (dt_pid_create_one_probe(pp->dpp_pr, pp->dpp_dtp,
> +					psp, symp, DTPPT_OFFSETS) < 0) {
>  			rc = dt_pid_error(
>  				dtp, pcb, dpr, D_PROC_CREATEFAIL,
>  				"failed to create probes at '%s+0x%llx': %s",
> @@ -253,16 +259,138 @@ dt_pid_per_sym(dt_pid_probe_t *pp, const GElf_Sym *symp, const char *func)
>  
>  		nmatches++;
>  	} else if (glob && !isdash) {
> -		if (dt_pid_create_glob_offset_probes(pp->dpp_pr, pp->dpp_dtp,
> -					psp, symp, pp->dpp_name) < 0) {
> -			rc = dt_pid_error(
> -				dtp, pcb, dpr, D_PROC_CREATEFAIL,
> -				"failed to create offset probes in '%s': %s",
> -				func, dtrace_errmsg(dtp, dtrace_errno(dtp)));
> +#if defined(__amd64)
> +		/*
> +		 * We need to step through the instructions to find their
> +		 * offsets.  This is difficult on x86, which has variable
> +		 * instruction lengths.  We invoke the disassembler in
> +		 * libopcodes.
> +		 *
> +		 * We look for the Elf pointer.  It is already stored in
> +		 * file_elf in file_info_t, but getting it back over here
> +		 * means introducing new struct members, new arguments to
> +		 * functions, etc.  So just call elf_begin() again here.
> +		 */
> +		int fd, i;
> +		Elf *elf;
> +		Elf_Scn *scn = NULL;
> +		GElf_Sym sym;
> +		GElf_Shdr shdr;
> +		Elf_Data *data;
> +		size_t shstrndx, off;
> +		disassembler_ftype disasm;
> +
> +		/* Set things up. */
> +		fd = open(pp->dpp_fname, O_RDONLY);
> +		elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);   // ELF_C_READ ?
> +		assert(elf_kind(elf) == ELF_K_ELF);
> +		elf_getshdrstrndx(elf, &shstrndx);
> +
> +		/* Look for the symbol table. */
> +		while (1) {
> +			scn = elf_nextscn(elf, scn);
> +			if (scn == NULL)
> +				goto out;
> +			assert(gelf_getshdr(scn, &shdr) != NULL);
> +			if (shdr.sh_type == SHT_SYMTAB)
> +				break;
> +		}
> +
> +		/* Look for the symbol in the symbol table. */
> +		data = elf_getdata(scn, NULL);
> +		for (i = 0; i < data->d_size / sizeof(GElf_Sym); i++) {
> +			if (!gelf_getsym(data, i, &sym))
> +				continue;
> +			if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL)
> +				continue;
> +			if (strcmp(elf_strptr(elf, shdr.sh_link, sym.st_name), func) == 0)
> +				break;
> +		}
> +		if (i >= data->d_size / sizeof(GElf_Sym))
>  			goto out;
> +
> +		/* Get the section for our symbol. */
> +		scn = elf_getscn(elf, sym.st_shndx);
> +		assert(gelf_getshdr(scn, &shdr) != NULL);
> +
> +		/* Check that the section is text. */
> +		if (shdr.sh_type != SHT_PROGBITS ||
> +		    shdr.sh_size <= 0 ||
> +		    (shdr.sh_flags & SHF_EXECINSTR) == 0) {
> +			assert(0);
>  		}
> +		assert(strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".text") == 0);
>  
> -		nmatches++;
> +		/* Get the instructions. */
> +		data = elf_getdata(scn, NULL);
> +
> +		/*
> +		 * "Disassemble" instructions just to get the offsets.
> +		 *
> +		 * Unfortunately, libopcodes's disassembler() has a different
> +		 * interface in binutils versions before 2.29.
> +		 */
> +#if defined(HAVE_DIS1) == defined(HAVE_DIS4)
> +#error expect disassembler() to have 1 or else 4 arguments
> +#endif
> +#ifdef HAVE_DIS1
> +		bfd			*abfd;
> +		struct disassemble_info	disasm_info;
> +
> +		bfd_init();
> +		abfd = bfd_openr(pp->dpp_fname, NULL);
> +		if (!bfd_check_format(abfd, bfd_object))
> +			return 1;
> +
> +		disasm = disassembler(abfd);
> +#else
> +		disassemble_info disasm_info;
> +
> +		disasm = disassembler(bfd_arch_i386, false, bfd_mach_x86_64, NULL);
> +#endif
> +#ifdef HAVE_INITDISINFO4
> +		init_disassemble_info(&disasm_info, NULL, my_callback, my_callback2);
> +#else
> +		init_disassemble_info(&disasm_info, NULL, my_callback);
> +#endif
> +		disasm_info.buffer = data->d_buf + (sym.st_value - shdr.sh_addr);
> +		disasm_info.buffer_length = sym.st_size;
> +#else
> +		/*
> +		 * The situation on aarch64 is much simpler:  each instruction
> +		 * is 4 bytes.
> +		 */
> +#define disasm(x, y) 4
> +#endif
> +
> +		for (off = 0; off < symp->st_size; off += disasm(off, &disasm_info)) {
> +			char offstr[32];
> +
> +#if defined(__amd64)
> +			/* Newer kernels do not allow uprobes on "hlt" instructions. */
> +			if ((unsigned int)disasm_info.buffer[off] == 0xf4)
> +				continue;
> +#endif
> +
> +			snprintf(offstr, sizeof(offstr), "%lx", off);
> +			if (!gmatch(offstr, pp->dpp_name))
> +				continue;
> +
> +			psp->pps_nameoff = off;
> +			psp->pps_off = symp->st_value - pp->dpp_vaddr + off;
> +			if (dt_pid_create_one_probe(pp->dpp_pr, pp->dpp_dtp,
> +						psp, symp, DTPPT_OFFSETS) >= 0)
> +				nmatches++;
> +		}
> +
> +#if defined(__amd64)
> +		/* Shut things down. */
> +		elf_end(elf);
> +		close(fd);
> +#ifdef HAVE_DIS1
> +		bfd_close(abfd);
> +#endif
> +#endif
>  	}
>  
>  	pp->dpp_nmatches += nmatches;
> @@ -710,6 +838,7 @@ dt_pid_scan_uprobes(dtrace_hdl_t *dtp, dt_pcb_t *pcb)
>  
>  		psp = &dtp->dt_uprobespecs[i++];
>  		psp->pps_type = linep->is_enabled ? DTPPT_IS_ENABLED : DTPPT_OFFSETS;
> +		psp->pps_nameoff = 0;
>  
>  		/*
>  		 * These components are only used for creation of an underlying
> diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c
> index 6c3b004f..9206b084 100644
> --- a/libdtrace/dt_prov_uprobe.c
> +++ b/libdtrace/dt_prov_uprobe.c
> @@ -26,8 +26,6 @@
>  static const char	prvname[] = "uprobe";
>  static const char	prvname_is_enabled[] = "uprobe__is_enabled";
>  
> -#define UPROBE_EVENTS	TRACEFS "uprobe_events"
> -
>  #define PP_IS_MINE	1
>  #define PP_IS_RETURN	2
>  #define PP_IS_FUNCALL	4
> @@ -313,7 +311,7 @@ static int provide_pid_probe(dtrace_hdl_t *dtp, const pid_probespec_t *psp)
>  		strcpy(prb, "return");
>  		break;
>  	case DTPPT_OFFSETS:
> -		snprintf(prb, sizeof(prb), "%lx", psp->pps_off);
> +		snprintf(prb, sizeof(prb), "%lx", psp->pps_nameoff);
>  		break;
>  	default:
>  		dt_dprintf("pid: unknown PID probe type %i\n", psp->pps_type);
> @@ -615,7 +613,7 @@ static int attach(dtrace_hdl_t *dtp, const dt_probe_t *prp, int bpf_fd)
>  
>  		/*
>  		 * If the uprobe creation failed, it is possible it already
> -		 * existed because someone else created it.  Tey to access its
> +		 * existed because someone else created it.  Try to access its
>  		 * tracefs info and if that fail, we really failed.
>  		 */
>  	}
> diff --git a/test/unittest/pid/tst.emptystack.d b/test/unittest/pid/tst.emptystack.d
> index 478557e4..2581e6b6 100644
> --- a/test/unittest/pid/tst.emptystack.d
> +++ b/test/unittest/pid/tst.emptystack.d
> @@ -1,10 +1,9 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
> -/* @@xfail: dtv2 */
>  /* @@runtest-opts: $_pid */
>  /* @@trigger-timing: before */
>  /* @@trigger: pid-tst-float */
> diff --git a/test/unittest/pid/tst.float.d b/test/unittest/pid/tst.float.d
> index f5ec25ef..d4f40409 100644
> --- a/test/unittest/pid/tst.float.d
> +++ b/test/unittest/pid/tst.float.d
> @@ -1,13 +1,13 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  /* @@runtest-opts: $_pid */
>  /* @@trigger: pid-tst-float */
> -/* @@trigger-timing: after */
> +/* @@trigger-timing: before */
>  
>  /*
>   * ASSERTION: Make sure we can work on processes that use the FPU
> @@ -15,19 +15,12 @@
>   * SECTION: pid provider
>   */
>  
> -BEGIN
> +pid$1:a.out:main:
>  {
> -	/*
> -	 * Let's just do this for 5 seconds.
> -	 */
> -	timeout = timestamp + 5000000000;
> +	@[probename] = count();
>  }
>  
> -pid$1:a.out:main:
> -{}
> -
> -profile:::tick-4
> -/timestamp > timeout/
> +profile:::tick-5s
>  {
>  	exit(0);
>  }
> diff --git a/test/unittest/pid/tst.offsets.r b/test/unittest/pid/tst.offsets.r
> new file mode 100644
> index 00000000..2e9ba477
> --- /dev/null
> +++ b/test/unittest/pid/tst.offsets.r
> @@ -0,0 +1 @@
> +success
> diff --git a/test/unittest/pid/tst.offsets.sh b/test/unittest/pid/tst.offsets.sh
> new file mode 100755
> index 00000000..c0b23fa9
> --- /dev/null
> +++ b/test/unittest/pid/tst.offsets.sh
> @@ -0,0 +1,270 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
> +# Licensed under the Universal Permissive License v 1.0 as shown at
> +# http://oss.oracle.com/licenses/upl.
> +#
> +# This test verifies that a glob in a pid-provider probe name works.
> +#
> +
> +dtrace=$1
> +
> +DIR=$tmpdir/pid-offsets.$$
> +mkdir $DIR
> +cd $DIR
> +
> +nouter=10
> +ninner=1000
> +
> +# Some instructions are "hot" (executed $nouter*$ninner times).
> +# Some instructions are "cold" (executed only $nouter times).
> +# Function leaffunc() is hot.
> +# Function coldfunc() is cold.
> +# Function loopfunc() has instructions that are:
> +#     hot   (inside the function's loop)
> +#     cold (outside the function's loop)
> +#
> +# Variable "i" is always manipulated after a function call to prevent
> +# tail-call optimization.
> +
> +cat > main.c << EOF
> +int leaffunc(int i) {
> +	return 2 * i - 1;
> +}
> +
> +int loopfunc(int i) {
> +	int j;
> +	for (j = 0; j < $ninner; j++)
> +		i = 2 * leaffunc(i) - 1;
> +	return i;
> +}
> +
> +int coldfunc(int i) {
> +	return (2 * loopfunc(i) - 1);
> +}
> +
> +int
> +main(int c, char **v)
> +{
> +	int i = 1, j;
> +	for (j = 0; j < $nouter; j++)
> +		i = 2 * coldfunc(i) - 1;
> +	return i;
> +}
> +EOF
> +
> +gcc main.c
> +if [ $? -ne 0 ]; then
> +	echo ERROR in compiling
> +	exit 1
> +fi
> +
> +#
> +# Now run DTrace.
> +# We are testing a glob in the pid$target:a.out:loopfunc: probe name.
> +#
> +
> +$dtrace -xstrsize=16 -qn '
> +pid$target:a.out:coldfunc:entry,
> +pid$target:a.out:loopfunc:,
> +pid$target:a.out:leaffunc:entry
> +{
> +	@[probefunc, probename] = count();
> +}' -c ./a.out -o D.out
> +if [ $? -ne 0 ]; then
> +	echo DTrace did not run
> +	cat D.out
> +	exit 1
> +fi
> +
> +#
> +# Use objdump and awk to extract the instruction PCs for function loopfunc().
> +# There should be a loop (jump to an earlier instruction), so also report
> +# the PCs for the jump and target instructions.  This will help identify
> +# hot and cold instructions in the function.
> +#
> +
> +objdump -d a.out | awk '
> +BEGIN {
> +	pc0 = 0;	# First PC of loopfunc()
> +	pcjump = 0;	# PC of the jump
> +	pctarget = 0;	# PC of the target
> +}
> +
> +# Look for loopfunc()
> +!/^[0-9a-f]* <loopfunc>:$/ { next; }
> +
> +# Process instructions in the loopfunc() function
> +{
> +	# Get the first PC
> +	sub("^0*", "", $1);
> +	pc0 = strtonum("0x"$1);
> +
> +	# Walk instructions until a blank line indicates end of function
> +	getline;
> +	while (NF > 0) {
> +		# Figure instruction offset and add to set of offsets
> +		off = strtonum("0x"$1) - pc0;
> +		offs[off] = 1;
> +		printf(" %x", off);
> +
> +		# Check for jump instruction to some <loopfunc+0x$target> target
> +		if (match($NF, "^<loopfunc\\+0x[0-9a-f]*>$")) {
> +			# Figure the offset of the target
> +			sub("^.*<loopfunc\\+", "", $NF);	# Get "0x$target>"
> +			sub(">.*$", "", $NF);			# Get "0x$target"
> +			$NF = strtonum($NF);			# Get $target
> +
> +			# Use it if a back jump (an offset we have already seen)
> +			if ($NF in offs) {
> +				pctarget = $NF;
> +				pcjump = off;
> +			}
> +		}
> +
> +		# Next line
> +		getline;
> +	}
> +
> +	# Report PCs for jump instruction and target
> +	printf("\nfrom %x to %x\n", pcjump, pctarget);
> +	exit(0);
> +}' > pcs.out
> +if [ $? -ne 0 ]; then
> +	echo awk processing of objdump output did not run
> +	cat   D.out
> +	cat pcs.out
> +	exit 1
> +fi
> +
> +#
> +# Use files pcs.out and D.out to check results.
> +#
> +
> +awk '
> +BEGIN {
> +   # Determine the expected counts for cold and hot instructions
> +   ncold = '$nouter';
> +   nhot = ncold * '$ninner';
> +   nhot2 = nhot + '$nouter';   # An instruction within the loopfunc() loop may be executed an extra time
> +
> +   # Track counts for a few of the pid probes specially
> +   n_coldfunc_entry  = -1;
> +   n_loopfunc_return = -1;
> +   n_leaffunc_entry  = -1;
> +}
> +
> +#
> +# Process first file:  pcs.out
> +# - which PCs are in loopfunc()
> +# - which PCs are hot and which are cold
> +#
> +
> +# Read the list of instruction PCs into array "offs"
> +NR == 1 { if (split($0, offs) < 4) {
> +              # We do not know exactly how many instructions to expect,
> +              # but perform a sanity check for some minimum.
> +              print "ERROR: few PCs found";
> +              exit(1)
> +          };
> +          next
> +        }
> +
> +# Read the jump and target PCs and classify other PCs as hot and cold
> +NR == 2 { pcjump   = strtonum("0x"$2);
> +          pctarget = strtonum("0x"$4);
> +          for (i in offs) {
> +              off = strtonum("0x"offs[i]);
> +              if (off >= pctarget && off <= pcjump) {  hot[off] = 1 }
> +              else                                  { cold[off] = 1 }
> +          }
> +          next
> +        }
> +
> +#
> +# Process second file:  D.out
> +# - get DTrace output and check the counts for the various pid probes
> +#
> +
> +NF == 0 { next }
> +
> +NF != 3 { print "ERROR: line does not have 3 fields:", $0; exit(1) }
> +
> +# coldfunc:entry
> +$1 == "coldfunc" && $2 != "entry" { print "ERROR: coldfunc probe other than entry"; exit(1) }
> +$1 == "coldfunc" && n_coldfunc_entry != -1 { print "ERROR: second coldfunc:entry line"; exit(1) }
> +$1 == "coldfunc" { n_coldfunc_entry = strtonum($3); next }
> +
> +# leaffunc:entry
> +$1 == "leaffunc" && $2 != "entry" { print "ERROR: leaffunc probe other than entry"; exit(1) }
> +$1 == "leaffunc" && n_leaffunc_entry != -1 { print "ERROR: second leaffunc:entry line"; exit(1) }
> +$1 == "leaffunc" { n_leaffunc_entry = strtonum($3); next }
> +
> +# The rest should be loopfunc:*
> +$1 != "loopfunc" { print "ERROR: probe func is not recognized", $1; exit(1) }
> +
> +# loopfunc:return
> +$2 == "return" && n_loopfunc_return != -1 { print "ERROR: second loopfunc:return line"; exit(1) }
> +$2 == "return" { n_loopfunc_return = strtonum($3); next }
> +
> +# loopfunc:entry, which we handle as loopfunc:0
> +$2 == "entry" {
> +                  if (strtonum($3) != '$nouter') {
> +                      print "ERROR: loopfunc:entry mismatch";
> +                      exit(1);
> +                  }
> +                  delete cold[0];
> +                  next
> +              }
> +
> +# loopfunc:$off
> +{
> +    off = strtonum("0x"$2);
> +    cnt = strtonum($3);
> +
> +    if (off in hot) {
> +        if (cnt != nhot && cnt != nhot2) {
> +            printf("ERROR: loopfunc:$off mismatch hot: %x %d\n", off, cnt);
> +            exit(1);
> +        }
> +        delete hot[off];
> +    } else if (off in cold) {
> +        if (cnt != ncold) {
> +            printf("ERROR: loopfunc:$off mismatch cold: %x %d\n", off, cnt);
> +            exit(1);
> +        }
> +        delete cold[off];
> +    } else {
> +        printf("ERROR: found unanticipated PC: %x\n", off);
> +        exit(1);
> +    }
> +}
> +
> +# Final checks
> +
> +END {
> +    # Check coldfunc:entry, loopfunc:return, and leaffunc:entry counts (and that they were seen)
> +    if (n_coldfunc_entry  != '$nouter') { print "ERROR: coldfunc:entry mismatch"; exit(1) }
> +    if (n_loopfunc_return != '$nouter') { print "ERROR: loopfunc:return mismatch"; exit(1) }
> +    if (n_leaffunc_entry  != nhot) { print "ERROR: leaffunc:entry mismatch"; exit(1) }
> +
> +    # Report any remaining hot or cold PCs not found (if any)
> +    num_not_found = 0;
> +    for (off in  hot) { print "ERROR: hot PC not found:", off; num_not_found++ }
> +    for (off in cold) { print "ERROR: hot PC not found:", off; num_not_found++ }
> +    if (num_not_found) exit(1);
> +
> +    # Done
> +    print "success";
> +    exit(0);
> +}
> +' pcs.out D.out
> +if [ $? -ne 0 ]; then
> +	cat   D.out
> +	cat pcs.out
> +	echo ERROR: awk postprocess
> +	exit 1
> +fi
> +
> +exit 0
> diff --git a/test/unittest/pid/tst.probefunc.d b/test/unittest/pid/tst.probefunc.d
> index 85b916ad..6afb93b1 100644
> --- a/test/unittest/pid/tst.probefunc.d
> +++ b/test/unittest/pid/tst.probefunc.d
> @@ -1,12 +1,11 @@
>  /*
>   * Oracle Linux DTrace.
> - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
>   * Licensed under the Universal Permissive License v 1.0 as shown at
>   * http://oss.oracle.com/licenses/upl.
>   */
>  
>  /* @@runtest-opts: -c /bin/date */
> -/* @@trigger: none */
>  
>  pid$target:libc.so:malloc:entry
>  {
> -- 
> 2.18.4
> 
> 
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel



More information about the DTrace-devel mailing list