[DTrace-devel] [PATCH 03/16] cpc: Add some hardware-counter tests

Kris Van Hees kris.van.hees at oracle.com
Mon Feb 20 16:57:51 UTC 2023


On Thu, Jan 26, 2023 at 09:23:16PM -0500, eugene.loh--- via DTrace-devel wrote:
> From: Eugene Loh <eugene.loh at oracle.com>
> 
> Test hardware counters using both "branches" and "instructions" events.
> Use "perf list hw" to check if these events are supported on the test
> system.
> 
> A few utilities are introduced for these and future tests:
> 
>   * workload_user.c is a simple, user-space-intensive
>     workload with very simple performance behavior:
> 
>       * one branch per iteration
> 
>       * a number of instructions per iteration that
>         is easily determined from its disassembly
> 
>   * workload_get_iterations.sh estimates how many iterations
>     to use to run the desired number of seconds
> 
>   * workload_analyze_loop.sh analyzes the disassembly of
>     the innermost loop of something like workload_user.c;
>     reporting the number of instructions per iteration and
>     which PCs are in the loop
> 
>   * check_result.sh checks an actual count against
>     an expected count to within some margin
> 
> Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
> ---
>  dtrace.spec                           |  1 +
>  test/unittest/cpc/tst.branches.sh     | 60 +++++++++++++++++++++++
>  test/unittest/cpc/tst.branches.x      | 13 +++++
>  test/unittest/cpc/tst.instructions.sh | 68 +++++++++++++++++++++++++++
>  test/unittest/cpc/tst.instructions.x  | 13 +++++
>  test/utils/.gitignore                 |  1 +
>  test/utils/Build                      |  4 +-
>  test/utils/check_result.sh            | 24 ++++++++++
>  test/utils/workload_analyze_loop.sh   | 63 +++++++++++++++++++++++++
>  test/utils/workload_get_iterations.sh | 54 +++++++++++++++++++++
>  test/utils/workload_user.c            | 33 +++++++++++++
>  11 files changed, 332 insertions(+), 2 deletions(-)
>  create mode 100755 test/unittest/cpc/tst.branches.sh
>  create mode 100755 test/unittest/cpc/tst.branches.x
>  create mode 100755 test/unittest/cpc/tst.instructions.sh
>  create mode 100755 test/unittest/cpc/tst.instructions.x
>  create mode 100755 test/utils/check_result.sh
>  create mode 100755 test/utils/workload_analyze_loop.sh
>  create mode 100755 test/utils/workload_get_iterations.sh
>  create mode 100644 test/utils/workload_user.c
> 
> diff --git a/dtrace.spec b/dtrace.spec
> index d12a2a82..9e1a1948 100644
> --- a/dtrace.spec
> +++ b/dtrace.spec
> @@ -148,6 +148,7 @@ Requires:     %{name}-devel = %{version}-%{release} perl gcc java
>  Requires:     java-1.8.0-openjdk-devel perl-IO-Socket-IP xfsprogs
>  Requires:     exportfs vim-minimal %{name}%{?_isa} = %{version}-%{release}
>  Requires:     coreutils wireshark %{glibc32}
> +Requires:     perf
>  Autoreq:      0
>  Group:	      Development/System
>  
> diff --git a/test/unittest/cpc/tst.branches.sh b/test/unittest/cpc/tst.branches.sh
> new file mode 100755
> index 00000000..87250c37
> --- /dev/null
> +++ b/test/unittest/cpc/tst.branches.sh
> @@ -0,0 +1,60 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2023, 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.
> +
> +# @@reinvoke-failure: 1
> +
> +utils=`pwd`/test/utils
> +
> +dtrace=$1
> +DIRNAME=$tmpdir/tst.branches.$$
> +mkdir $DIRNAME
> +cd $DIRNAME
> +
> +# determine number of iterations for target number of seconds
> +nsecs=1
> +niters=`$utils/workload_get_iterations.sh workload_user $nsecs`
> +if [ $niters -lt 0 ]; then
> +	echo "workload_get_iterations.sh failed with workload_user"
> +	exit 1
> +fi
> +
> +# pick a sampling period
> +period=$(($niters / 100))
> +
> +# run DTrace
> +$dtrace $dt_flags -qn '
> +BEGIN
> +{
> +	n = 0;
> +}
> +
> +branches-all-'$period'
> +/pid == $target/
> +{
> +	n++;
> +}
> +
> +END
> +{
> +	printf("%d\n", n);
> +}' -c "$utils/workload_user $niters" -o tmp.txt
> +
> +if [[ $? -ne 0 ]]; then
> +	echo ERROR running DTrace
> +	cat tmp.txt
> +	exit 1
> +fi
> +
> +# estimate actual count (sampling period * # of samples)
> +actual=$(($period * `cat tmp.txt`))
> +
> +# determine expected count (one branch per interation)
> +expect=$niters
> +
> +# check
> +$utils/check_result.sh $actual $expect $(($expect / 4))
> +exit $?
> diff --git a/test/unittest/cpc/tst.branches.x b/test/unittest/cpc/tst.branches.x
> new file mode 100755
> index 00000000..a8e07af1
> --- /dev/null
> +++ b/test/unittest/cpc/tst.branches.x
> @@ -0,0 +1,13 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2023, 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.
> +
> +if ! perf list hw | grep -qw branches; then
> +	echo 'no "branches" event on this system'
> +	exit 2
> +fi
> +
> +exit 0
> diff --git a/test/unittest/cpc/tst.instructions.sh b/test/unittest/cpc/tst.instructions.sh
> new file mode 100755
> index 00000000..a4663837
> --- /dev/null
> +++ b/test/unittest/cpc/tst.instructions.sh
> @@ -0,0 +1,68 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2023, 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.
> +
> +# @@reinvoke-failure: 1
> +
> +utils=`pwd`/test/utils
> +
> +dtrace=$1
> +DIRNAME=$tmpdir/tst.instructions.$$
> +mkdir $DIRNAME
> +cd $DIRNAME
> +
> +# determine number of iterations for target number of seconds
> +nsecs=1
> +niters=`$utils/workload_get_iterations.sh workload_user $nsecs`
> +if [ $niters -lt 0 ]; then
> +	echo "workload_get_iterations.sh failed with workload_user"
> +	exit 1
> +fi
> +
> +# determine the number of instructions per loop iteration
> +ninstructions_per_iter=`$utils/workload_analyze_loop.sh workload_user | awk '{print $1; exit 0}'`
> +if [ $ninstructions_per_iter -lt 0 ]; then
> +	echo could not determine number of instructions per loop
> +	exit 1
> +fi
> +echo $ninstructions_per_iter instructions per loop iteration
> +
> +# pick a sampling period
> +period=$(($niters / 100))
> +
> +# run DTrace
> +$dtrace $dt_flags -qn '
> +BEGIN
> +{
> +	n = 0;
> +}
> +
> +instructions-all-'$period'
> +/pid == $target/
> +{
> +	n++;
> +}
> +
> +END
> +{
> +	printf("%d\n", n);
> +}' -c "$utils/workload_user $niters" -o tmp.txt
> +
> +if [[ $? -ne 0 ]]; then
> +	echo ERROR running DTrace
> +	cat tmp.txt
> +	exit 1
> +fi
> +
> +# estimate actual count (sampling period * # of samples)
> +actual=$(($period * `cat tmp.txt`))
> +
> +# determine expected count
> +expect=$(($niters * $ninstructions_per_iter))
> +
> +# check
> +$utils/check_result.sh $actual $expect $(($expect / 4))
> +exit $?
> diff --git a/test/unittest/cpc/tst.instructions.x b/test/unittest/cpc/tst.instructions.x
> new file mode 100755
> index 00000000..d47b7d99
> --- /dev/null
> +++ b/test/unittest/cpc/tst.instructions.x
> @@ -0,0 +1,13 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2023, 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.
> +
> +if ! perf list hw | grep -qw instructions; then
> +	echo 'no "instructions" event on this system'
> +	exit 2
> +fi
> +
> +exit 0
> diff --git a/test/utils/.gitignore b/test/utils/.gitignore
> index 27c8cfc6..d8e2ccb5 100644
> --- a/test/utils/.gitignore
> +++ b/test/utils/.gitignore
> @@ -1,5 +1,6 @@
>  # In-place built executables
>  baddof
>  badioctl
> +workload_user
>  print-stack-layout
>  showUSDT
> diff --git a/test/utils/Build b/test/utils/Build
> index 992fca19..b57cdda8 100644
> --- a/test/utils/Build
> +++ b/test/utils/Build
> @@ -1,9 +1,9 @@
>  # Oracle Linux DTrace.
> -# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
> +# Copyright (c) 2011, 2023, 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.
>  
> -TEST_UTILS = baddof badioctl showUSDT print-stack-layout
> +TEST_UTILS = baddof badioctl workload_user showUSDT print-stack-layout
>  
>  define test-util-template
>  CMDS += $(1)
> diff --git a/test/utils/check_result.sh b/test/utils/check_result.sh
> new file mode 100755
> index 00000000..9509512a
> --- /dev/null
> +++ b/test/utils/check_result.sh
> @@ -0,0 +1,24 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2023, 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.
> +
> +actual=$1
> +expect=$2
> +margin=$3
> +
> +printf " check %10s;  against %10s;  margin %10s:  " $actual $expect $margin
> +
> +if [ $actual -lt $(($expect - $margin)) ]; then
> +	echo ERROR too small
> +	exit 1
> +fi
> +if [ $actual -gt $(($expect + $margin)) ]; then
> +	echo ERROR too large
> +	exit 1
> +fi
> +
> +echo success
> +exit 0
> diff --git a/test/utils/workload_analyze_loop.sh b/test/utils/workload_analyze_loop.sh
> new file mode 100755
> index 00000000..28f5fb4b
> --- /dev/null
> +++ b/test/utils/workload_analyze_loop.sh
> @@ -0,0 +1,63 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2023, 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.
> +
> +# Analyze the loop in the specified workload program ($1), in test/utils/.
> +
> +prog=`dirname $0`/$1
> +if [ ! -e $prog ]; then
> +	echo -1 -1 -1
> +	exit 1
> +fi
> +
> +objdump -d $prog | awk -v myarch=$(uname -m) '
> +# decide whether to track instructions (which we number n = 1, 2, 3, ...) or not (n < 0)
> +# specifically, do not track instructions until we find the disassembly for <main>
> +BEGIN { n = -1; }
> +/[0-9a-f]* <main>:$/ { n = 0; next }
> +
> +# skip over instructions we are not tracking and null instructions
> +n < 0 { next }
> +/^ *[0-9a-f]*:	[0 ]*$/ { next }
> +
> +# if we are tracking but hit a blank line, the <main> disassembly is over and we found no loop
> +NF == 0 {
> +	print -1, -1, -1;
> +	exit 1;
> +}
> +
> +# track this instruction
> +{ n++; instr[n] = $1; }
> +
> +# remove the instruction hexcodes (between the first two tabs)
> +{ sub("	[^	]*	", "	"); }
> +
> +# look for a conditional jump
> +#   * x86: look for a j* instruction, but not ja or jmp
> +#   * ARM: look for a b.* instruction
> +( myarch == "x86_64" && ( /	j/ && !/	ja / && !/	jmp / ) ) ||
> +( myarch == "aarch64" && /	b\./ ) {
> +
> +	# look for the jump target among past instructions
> +	target = $3;
> +	for (i = 1; i <= n; i++) {
> +		if (index(instr[i], target)) {
> +			break;
> +		}
> +	}
> +
> +	# if we found the target, we have identified a loop
> +	if (i <= n) {
> +		# report: num of instructions per loop, first PC, and last PC
> +		print n + 1 - i, instr[i], instr[n];
> +
> +		# report: all the PCs in the loop (one per line)
> +		for (i = i; i <= n; i++) print instr[i];
> +		exit 0;
> +	}
> +}' | tr ":" " "      # and get rid of those extraneous ":"
> +
> +exit $?
> diff --git a/test/utils/workload_get_iterations.sh b/test/utils/workload_get_iterations.sh
> new file mode 100755
> index 00000000..a2803186
> --- /dev/null
> +++ b/test/utils/workload_get_iterations.sh
> @@ -0,0 +1,54 @@
> +#!/bin/bash
> +#
> +# Oracle Linux DTrace.
> +# Copyright (c) 2023, 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.
> +
> +# Get the number of iterations needed for the target program ($1)
> +# to run in the specified number of seconds ($2).
> +
> +# The target program, in test/utils/, which takes some iteration count
> +# as a command-line argument.
> +prog=`dirname $0`/$1
> +if [ ! -e $prog ]; then
> +	echo -2
> +	exit 1
> +fi
> +
> +# The target number of seconds we want the program to run.
> +nsecs=$2
> +
> +# For the target program and number of seconds, return an estimate of how
> +# many iterations are needed.
> +
> +function time_me() {
> +	# FIXME: exit this script if we encounter an error
> +	/usr/bin/time -f "%e" $prog $1 2>&1

You should add the 'time' package to the list of runtime dependencies for the
testsuite to ensure that the 'time' package is installed.  E.g. on my Debian
VMs it is not installed by default.

> +}
> +
> +# Confirm that one iteration is not enough.
> +t=$(time_me 1)
> +if [ `echo "$t >= 1" | bc` -eq 1 ]; then
> +	echo -1
> +	exit 1
> +fi
> +
> +# Increase estimate exponentially until we we are in range.
> +n=1
> +while [ `echo "$t < 0.1 * $nsecs" | bc` -eq 1 ]; do
> +
> +	# protect against run-away values
> +	if [ `echo "$n > 30000000000" | bc` -eq 1 ]; then
> +		echo -$n
> +		exit 1
> +	fi
> +
> +	# increase n
> +	n=`echo "$n * 10" | bc`
> +	t=$(time_me $n)
> +done
> +
> +# At this point, we should be close enough that we can extrapolate.
> +echo "$nsecs * $n / $t" | bc
> +exit 0
> diff --git a/test/utils/workload_user.c b/test/utils/workload_user.c
> new file mode 100644
> index 00000000..4ce623b4
> --- /dev/null
> +++ b/test/utils/workload_user.c
> @@ -0,0 +1,33 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2023, 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.
> + */
> +
> +#include <stdlib.h>
> +
> +/*
> + * The command-line argument specifies how many iterations to execute
> + * in this user-space-intensive loop to run.
> + */
> +
> +int
> +main(int argc, const char **argv)
> +{
> +	double x = 0.;
> +	long long n;
> +
> +	if (argc < 2)
> +		return 1;
> +	n = atoll(argv[1]);
> +
> +	for (long long i = 0; i < n; i++)
> +		x = 0.5 * x + 1.;
> +
> +	/*
> +	 * Check the result (albeit simplistically)
> +	 * so compiler won't optimize away the loop.
> +	 */
> +	return (x > 5.) ? 1 : 0;
> +}
> -- 
> 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