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

Kris Van Hees kris.van.hees at oracle.com
Fri Feb 24 00:50:20 UTC 2023


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

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
> +}
> +
> +# 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