[PATCH V2] oprofile-tests Add test to exercise libjvmti_oprofile.so
will schmidt <[email protected]> Tue, 15 Oct 2019 13:21:25 -0500
| Newsgroups | gmane.linux.oprofile |
|---|---|
| Message-ID | <[email protected]> |
Hi,
For V2 of this patch, I have updated the logic that looks for
libjvmti_oprofile.so to also search /usr/lib64/ and /usr/lib/
for the library, stopping when the library is found. I have
tested a few additional variations locally.
(V1:)
Assorted changes to exercise the use of oprofiles JVMTI hook.
(libjvmti_oprofile.so - Java Virtual Machine Tools Interface).
This has been tested this on assorted ppc64* systems with a mixture
of having the libjvmti_oprofile present and not.
testsuite/lib/op_util.exp:
* Update compile_workload() procedure to allow building of .java files.
* Add procedure op_resolve_java_dependencies() to ensure that dependencies
for building and running the java test can be found. This includes java,
javac, and libjvmti_oprofile.so. Since the libjvmti_oprofile.so object
can't be readily found via 'which', logic is in place that assumes it can
be found in a path relative to where the operf binary is found.
testsuite/lib/op_workloads.exp:
* Add an op_java_workload_table containing the new test.
testsuite/lib/operf_util.exp:
* Add procedure (check_java_samples_exist) to see that samples can be
found that are associated with the generated .jo object.
* Add procedure (check_java_object_file_exists) to ensure that at
least one "*.jo" file was created during the run.
* Add procedure (do_java_test) to do the actual test.
testsuite/workloads/java/Fibonacci.java:
* Add the java test itself.
--
Thanks,
-Will (Schmidt)
diff --git a/testsuite/lib/op_util.exp b/testsuite/lib/op_util.exp
index 7f0473d..863d46f 100644
--- a/testsuite/lib/op_util.exp
+++ b/testsuite/lib/op_util.exp
@@ -179,15 +179,15 @@ proc check_user {} {
proc compile_workload {workload_src compile_options } {
# Ideally the TCL command target_compile should be used. However,
# target_compile is ignoring the compiler options argument.
- set compiler "cc"
set extension [lindex [split $workload_src "."] 1]
- set workload_exc [ lindex [split $workload_src "."] 0]_bin
if { [string compare $extension "c"] == 0 } {
+ set workload_exc [ lindex [split $workload_src "."] 0]_bin
+ set compiler "cc"
# workload is a C program, see if it needs to be compiled.
set test [local_exec "ls $workload_exc" "" "" 10]
if { [lindex $test 0] == 1 } {
# workload has not been compiled
@@ -198,10 +198,28 @@ proc compile_workload {workload_src compile_options } {
}
}
return [ lindex [split $workload_src "."] 0]_bin
}
+ if { [string compare $extension "java"] == 0 } {
+ set compiler "javac"
+ set workload_exc [ lindex [split $workload_src "."] 0].class
+ # workload is a java program, see if it needs to be compiled.
+ set test [local_exec "ls $workload_exc" "" "" 10]
+ if { [lindex $test 0] == 1 } {
+ # workload has not been compiled
+ set result [local_exec "$compiler $workload_src $compile_options" "" "" 10 ]
+ if { [lindex $result 0] == 1 } {
+ print "\nERROR compiling workload: $workload_src"
+ print "Compiler error message: $result\n"
+ }
+ }
+ set path_and_basename [lindex [split $workload_src "."] 0]
+ set classonly [ lindex [ split $path_and_basename "/"] 2]
+ return $classonly
+ }
+
# Assume the workload is a script, set the executable
# name to the same as the source name.
return $workload_src
}
@@ -243,5 +261,67 @@ proc op_check_dependencies {} {
set op_deps_ok 0
} else {
set op_deps_ok 1
}
}
+
+proc op_resolve_java_dependencies {} {
+ global java_deps_ok
+ global path_to_libjvmti
+ set libjvmti_oprofile_present 0
+ set java_present 0
+ set javac_present 0
+ set operf_present 0
+
+ set path_to_operf [ local_exec "which operf" "" "" 100 ]
+ if {[regexp "operf" $path_to_operf operf_present] == 1} {
+ verbose "Found operf ($operf_present) at $path_to_operf ."
+ } else {
+ send "Didn't find operf.\n"
+ }
+
+ set path_to_javac [ local_exec "which javac" "" "" 100 ]
+ if {[regexp "javac" $path_to_javac javac_present] == 1} {
+ verbose "Found javac ($javac_present) at $path_to_javac ."
+ } else {
+ send "javac is require to build the java testcase. I didn't find javac via 'which'.\n"
+ }
+
+ set path_to_java [ local_exec "which java" "" "" 100 ]
+ if {[regexp "java" $path_to_java java_present] == 1} {
+ verbose "Found java ($java_present) at $path_to_java ."
+ } else {
+ send "java is require to run the java testcase. I didn't find java via 'which'.\n"
+ }
+
+ # Finding libjvmti_oprofile.so.
+ # In the case of a userspace build, the oprofile install location has
+ # sibling /bin/ and /lib/ directories, so as long as we have found the
+ # operf binary, we should be able to infer the location of libjvmti_oprofile.so.
+ set path_to_libjvmti [string map {bin/operf lib/oprofile/libjvmti_oprofile.so} $path_to_operf ]
+ set path_to_libjvmti [string trimleft $path_to_libjvmti "0\ \{"]
+ set path_to_libjvmti [string trim $path_to_libjvmti "\ \n\r\}"]
+ set libjvmti_oprofile_present [file exists $path_to_libjvmti ]
+ if {$libjvmti_oprofile_present == 1 } {
+ send "found libjvmti_oprofile ($libjvmti_oprofile_present) at $path_to_libjvmti . \n"
+ } else {
+ send "did not find libjvmti_oprofile in $path_to_libjvmti ) .\n"
+ # search a few other common spots for the library.
+ foreach path_to_libjvmti { /usr/lib64/libjvmti_oprofile.so /usr/lib/libjvmti_oprofile.so } {
+ verbose "looking for $path_to_libjvmti . "
+ set libjvmti_oprofile_present [file exists $path_to_libjvmti ]
+ if {$libjvmti_oprofile_present == 1 } {
+ send "found libjvmti_oprofile ($libjvmti_oprofile_present) at $path_to_libjvmti . \n"
+ break
+ } else {
+ send "did not find libjvmti_oprofile in $path_to_libjvmti ) .\n"
+ }
+ }
+ }
+
+ if {$operf_present == 0 || $java_present == 0 || $javac_present == 0 || $libjvmti_oprofile_present == 0} {
+ set java_deps_ok 0
+ } else {
+ set java_deps_ok 1
+ }
+}
+
diff --git a/testsuite/lib/op_workloads.exp b/testsuite/lib/op_workloads.exp
index 661e280..9e96b52 100644
--- a/testsuite/lib/op_workloads.exp
+++ b/testsuite/lib/op_workloads.exp
@@ -2,10 +2,14 @@
if {! [array exists op_workload_table]} {
array set op_workload_table {}
}
+if {! [array exists op_java_workload_table]} {
+ array set op_java_workload_table {}
+}
+
# Make sure the workload binary name is unique and not a substring of the
# directory or the test to see if the symbol exists may find the symbol in
# the directory name not the executable.
#
# The op_workload_table consists of three or more entries. The entries
@@ -59,5 +63,15 @@ set op_workload_table(anon_remap) \
{workloads/anon_remap_src/anon_remap.c} \
{} \
{} \
{ anon } \
}
+
+# A new/different table for the java workload(s).
+set op_java_workload_table(fibonacci) \
+ { \
+ {workloads/java/Fibonacci.java} \
+ {} \
+ {Fibonacci.main} \
+ {} \
+}
+
diff --git a/testsuite/lib/operf_util.exp b/testsuite/lib/operf_util.exp
index b34fd33..10ea9ac 100644
--- a/testsuite/lib/operf_util.exp
+++ b/testsuite/lib/operf_util.exp
@@ -1,7 +1,7 @@
-# op_util.exp
-# Copyright (C) 2012 IBM
+# operf_util.exp
+# Copyright (C) 2012-2019 IBM
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
@@ -86,10 +86,41 @@ proc operf_check_samples_exist {ctrs} {
pass "$test: {$event} created nonzero sample files"
}
}
}
+proc operf_check_java_samples_exist {ctrs} {
+ set test "checking for samples associated with *.jo"
+ verbose "checking for samples associated with *.jo"
+
+ foreach ctr $ctrs {
+ set n [lindex $ctr 0]
+ set event [lindex $ctr 1]
+ # add "-t 3" as a percentage threshold to cull some noise.
+ # add "-n" to suppress the opreport header (easier for debug).
+ # add a regexp blurb to get just the .jo entries.
+ set result [ local_exec "opreport event:$event -t 3 -n " "" "" 100 ]
+ if { [regexp "\[0-9\].jo" "$result" ]} {
+ pass "$test: {$event} found samples associated with *.jo"
+ } else {
+ fail "$test: {$event} didn't find samples associated with *.jo"
+ }
+ }
+}
+
+proc operf_check_java_object_file_exists {} {
+ set test "At least one java object (.jo) file exists."
+ verbose "At least one java object (.jo) file exists."
+
+ set result [ local_exec "find . -name \"*.jo\" " "" "" 100 ]
+ if {[regexp ".jo" "$result"]} {
+ pass "$test: found at least one java object file."
+ } else {
+ fail "$test: Did not find a java object (*.jo) file."
+ }
+}
+
proc operf_ocount_get_version {tool} {
global operf_ocount_version
global user_is_root
if { [string compare $tool "operf"] == 0 } {
@@ -159,10 +190,85 @@ proc operf_get_version {} {
set test "OProfile operf version check"
operf_ocount_get_version "operf"
set operf_version $operf_ocount_version
}
+# run a java test.
+proc do_java_test {ctrs output_check test_debug_option} {
+
+ global op_java_workload_table
+ global op_workload_table
+ global path_to_libjvmti
+ set kernel 1
+ set user 1
+ set ctr_opts {}
+
+ if { $output_check == 1 && $test_debug_option == 0 } {
+ validate_xml_help
+ }
+
+ foreach ctr $ctrs {
+# n - counter number, no longer used but didn't want to change
+# specification format
+ set n [lindex $ctr 0]
+ set event [lindex $ctr 1]
+ set um [lindex $ctr 2]
+ if {$output_check == 3} {
+ # With callgraph profiling, we only use the cycles-based event for the
+ # architecture. Just set the count value high enough to make sure it's
+ # at least 15x the minimum; 1,500,000 should do it.
+ set count 1500000
+ } else {
+ set count [lindex $ctr 3]
+ }
+ append ctr_opts "${event}:${count}:${um}:${kernel}:${user},"
+ }
+
+ foreach entry [array name op_java_workload_table] {
+ set workload_src [lindex $op_java_workload_table($entry) 0]
+ set compile_options [lindex $op_java_workload_table($entry) 1]
+ set workload_exc [compile_workload $workload_src $compile_options]
+ verbose "Workload: $workload_exc\n"
+ verbose "Counter options: $ctr_opts\n"
+ # Run the workload
+ set java_classpath "workloads/java/"
+ set cmd "operf java -agentpath:${path_to_libjvmti} -cp ${java_classpath} ${workload_exc}"
+ verbose "operf command is: $cmd \n"
+
+ if {$test_debug_option == 1} {
+ set dbg_test " (--debug-info)"
+ } else {
+ set dbg_test ""
+ }
+ set test "operf test against java: $cmd"
+ verbose "\nTest: $test\n"
+ if {[lindex [local_exec $cmd "" "" 100] 0] == 0} {
+ pass $test
+ } else {
+ fail $test
+ }
+
+ # - verify that the test ran.
+ operf_check_samples_exist $ctrs
+ # - verify that at least one .jo file exists.
+ operf_check_java_object_file_exists
+ # - verify that the .jo entry shows up in the output.
+ operf_check_java_samples_exist $ctrs
+
+ # Verify the interesting symbols for this test are found in
+ # the output. Only checking the symbols for the first group of
+ # events.
+ if {$output_check == 1} {
+ local_exec "sleep 1" "" "" 10
+ verify_output $op_java_workload_table($entry) $workload_exc $ctr_opts $test_debug_option
+ }
+
+ operf_delete_samples
+ }
+}
+
+
proc do_single_process_test {ctrs output_check test_debug_option} {
# The tests are run in single process mode which does not require root
#
# output_check values:
# 1 - check output for symbols from workload table
diff --git a/testsuite/oprofile-java/oprofile-java.exp b/testsuite/oprofile-java/oprofile-java.exp
new file mode 100644
index 0000000..1c9cd0b
--- /dev/null
+++ b/testsuite/oprofile-java/oprofile-java.exp
@@ -0,0 +1,112 @@
+# oprofile-java.exp
+# Copyright (C) 2019 IBM Corporation
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+global path_to_libjvmti
+
+check_user
+load_lib "op_events.exp"
+load_lib "op_util.exp"
+load_lib "operf_util.exp"
+load_lib "op_workloads.exp"
+
+# run operf against a java utility, verify that a .jo file is created,
+# and that an oreport can run successfully against the run.
+
+proc oprofile_run_java_tests {} {
+ global op_event_table
+
+ set cpu [operf_cpu_type]
+ if {$cpu == "bogus"} {
+ send "Error, not able to find a valid cpu type. Exiting.\n"
+ } else {
+ set output_check 2
+ set test_debug_option 0
+ # Test using the first event entry only.
+ set spec [lindex $op_event_table($cpu) 0]
+ do_java_test $spec $output_check $test_debug_option
+ }
+}
+
+#main
+
+set operf_version 0
+set op_version 0
+
+op_check_dependencies
+if {$op_deps_ok == 0} {
+ return
+}
+
+op_resolve_java_dependencies
+if {$java_deps_ok == 0} {
+#send "returning from java_deps_ok check"
+ send "Skipping the java test due to missing dependencies."
+ send "Oprofile needs to be built --with-java=<path/to/jdk/>, such "
+ send "that the libjvmti_oprofile.so object is built and installed."
+ send "java, javac, operf also need to be available and in the PATH."
+ return
+}
+
+# Check to see of opcontrol is installed. Need to do this first as
+# to set opcontrol_installed. If opcontrol is not installed, then we do
+# not make any opcontrol calls to do cleanup of existing opcontrol data samples
+# or call --deinit when exiting.
+
+operf_get_version
+op_get_version
+
+if {$op_version == 0} {
+ # could not find opcontrol
+ set opcontrol_installed 0
+} else {
+ if {$op_version < $operf_version} {
+ # Don't run the opcontrol tests at all if the opcontrol binary appears to
+ # be from an earlier version of OProfile.
+ set opcontrol_installed 0
+ } else {
+ set opcontrol_installed 1
+ }
+}
+
+if { $operf_version == 0 } {
+ send "Skipping the operf tests.\n"
+ send "Unable to find or verify the version of the OProfile operf utility.\n"
+} else {
+ # Ensure that the oprofile kernel module is unloaded, since it can
+ # interfere with proper operf operation on some platforms.
+# if {$opcontrol_installed == 1} {
+# local_exec "opcontrol --deinit" "" "" 100
+# # Make sure there are no samples from the opcontrol laying
+# # around that opreport might inadvertently pick up.
+# op_daemon_delete_any_samples
+# }
+ # Make sure there are no samples from the operf laying
+ # around that opreport might inadvertently pick up.
+ operf_delete_samples
+
+ case $operf_version {
+ { 0.* } {
+ # Just don't run this on old versions.
+ send "v==0, skipping. \n"
+ }
+ { * } {
+ send "Running operf against a java workload. \n"
+ oprofile_run_java_tests
+ }
+ }
+}
diff --git a/testsuite/workloads/java/Fibonacci.java b/testsuite/workloads/java/Fibonacci.java
new file mode 100644
index 0000000..8d92b6a
--- /dev/null
+++ b/testsuite/workloads/java/Fibonacci.java
@@ -0,0 +1,25 @@
+
+/* A simple java test to generate some activity to verify operf is working
+ * properly. */
+
+public class Fibonacci {
+ public static void main(String[] args) {
+ long sum=0;
+ int i = 1;
+ /* The test harness does not easily handle passing arguments
+ * to the test, so hardcode a value here.
+ * This (sum) will overflow, but that is fine as we are only after
+ * activity, and are not concerned with the result. */
+ int n = 12345; /*Integer.parseInt(args[0]);*/
+ long f1 = 0;
+ long f2 = f1 + 1;
+ while (i <= n) {
+ sum = f1 + f2;
+ f1 = f2;
+ f2 = sum;
+ i++;
+ }
+ System.out.print(sum);
+ System.out.print("\n");
+ }
+}