[PATCH 0 of 3] Add support for dtrace compatible sdt probes on GNU/Linux
serguei.spitsyn at oracle.com
serguei.spitsyn at oracle.com
Wed Jul 18 11:41:34 PDT 2012
On 7/18/12 10:23 AM, Mark Wielaard wrote:
> On Wed, Jul 18, 2012 at 10:10:43AM -0700, serguei.spitsyn at oracle.com wrote:
>> This work includes investigation to find proper linux machine,
>> installation of the systemTap, building and testing, and so,
>> needs some engineering resources.
> I can do that, the patch has already been included in various
> distros so it shouldn't be a problem. Just let me know what you
> need.
>
>> Also, it needs a new unit test and submitting a CCC request.
>> You only can help by providing a test for the patch.
> Sure, I was looking for existing dtrace tests to see if I can
> match them. Do you have a good example I can follow?
>
> Thanks,
>
> Mark
Normally a dtrace test needs 4 parts:
java test program, d-script, shell script and README
All automated dtrace tests usually depend on the SQE testbase environment:
.cfg file, testbase libraries and testbase build system
So that, I do not have a simple standalone test for the HotSpot probes.
But I can provide an example (just make a correction on this kind of
dependency).
The test ThreadLifecycle001 includes ThreadLifecycle.d,
Please, see below and let me know if you have any questions.
I can send you more examples if needed.
Thanks,
Serguei
% cat ThreadLifecycle.d
#!/usr/sbin/dtrace -Zs
#pragma D option quiet
#pragma D option destructive
/* %W% %G%
* Copyright %G% Sun Microsystems, Inc.
*/
/*
* The script trace hotspot:::thread-start and hotspot:::thread-stop
events.
*/
self char *str_ptr;
self string thread_name;
:::BEGIN {
TEST_NAME = "Thread lifecycle";
TEST_PASS = 1;
EXIT_CODE_PASS = 0;
EXIT_CODE_FAIL = 1;
printf("BEGIN %s probes testing\n\n", TEST_NAME);
}
/*
* hotspot:::thread-start, hotspot:::thread-stop probe arguments:
* arg0: char*, thread name passed as UTF8 string
* arg1: uintptr_t, thread name length
* arg2: uintptr_t, Java thread id
* arg3: uintptr_t, native/OS thread id
* arg4: uintptr_t, is a daemon or not
*/
/* Check is_daemon is correct */
hotspot$target:::thread-start,
hotspot$target:::thread-stop
/ arg4 > 1 /
{
TEST_PASS = 0;
printf("FAIL: wrong is_daemon=%u is passed in %s\n",
arg4, probename);
}
hotspot$target:::thread-start
{
THREAD_START_CNT ++;
}
hotspot$target:::thread-stop
{
THREAD_STOP_CNT ++;
}
hotspot$target:::thread-start,
hotspot$target:::thread-stop
{
/* Yes, we copy for one byte more from user space. This is temporal
solution till support for
* not-null terminated strings will be donein DTRACE
*/
self->str_ptr = (char*) copyin(arg0, arg1+1);
self->str_ptr[arg1] = '\0';
self->thread_name = (string) self->str_ptr;
printf("\nPROBE ARGS: %s:\n", probename);
printf("PROBE ARGS: arg0=%p (thread name pointer)\n", arg0);
printf("PROBE ARGS: arg1=%u (thread name length)\n", arg1);
printf("PROBE ARGS: arg2=%u (Java thread id)\n", arg2);
printf("PROBE ARGS: arg3=%u (native/OS id)\n", arg3);
printf("PROBE ARGS: arg4=%u (is daemon)\n", arg4);
/* this output will be used by Perl script */
printf("%s: id=%u, is_daemon=%u, name=%s\n",
probename, arg2, arg4, self->thread_name);
}
/* check exit status of traced java process */
syscall::rexit:entry
/pid == $target && arg0 != 0 && arg0 != 95 /
{
printf("\n");
printf("ERROR: java process failed with status=%d\n", arg0);
TEST_PASS = 0;
}
syscall::rexit:entry
/pid == $target && !THREAD_START_CNT/
{
printf("ERROR: no 'thread-start' probes were fired\n");
TEST_PASS = 0;
}
syscall::rexit:entry
/pid == $target && !THREAD_STOP_CNT/
{
printf("ERROR: no 'thread-stop' probes were fired\n");
TEST_PASS = 0;
}
syscall::rexit:entry
/pid == $target && !TEST_PASS/
{
printf("FAIL\n");
exit(EXIT_CODE_FAIL);
}
syscall::rexit:entry
/pid == $target && TEST_PASS/
{
printf("PASS\n");
exit(EXIT_CODE_PASS);
}
:::END {
printf("\nEND %s tracing\n", TEST_NAME);
}
sspitsyn at hsdev-10 more *.* | cat
::::::::::::::
ThreadLifecycle001.cfg
::::::::::::::
# %W% %G%
# Copyright %G% Sun Microsystems, Inc.
DSCRIPT=${TESTBASE}/src/dtrace/share/dscripts/hotspot/ThreadLifecycle.d
EXECUTE_CLASS=dtrace.hotspot.ThreadLifecycle.ThreadLifecycle001.ThreadLifecycle001
export DSCRIPT
export JAVA
export JAVA_OPTS
export TESTDIR
export TEST_ARGS
export EXECUTE_CLASS
export TESTBASE
export TESTARGS
DTRACE_RESULTS_ANALYZER_SCRIPT=${TESTBASE}/src/dtrace/hotspot/ThreadLifecycle/ThreadLifecycle001/ThreadLifecycle001.pl
export DTRACE_RESULTS_ANALYZER_SCRIPT
TONGA_EXECUTE=$PERL ${TESTBASE}/src/dtrace/share/run_dtrace.pl
::::::::::::::
ThreadLifecycle001.java
::::::::::::::
/* %W% %G%
* Copyright %G% Sun Microsystems, Inc.
*/
package dtrace.hotspot.ThreadLifecycle.ThreadLifecycle001;
import java.lang.*;
class NewThread extends Thread
{
volatile private boolean stop = false;
private boolean started = false;
public NewThread(ThreadGroup group, String name)
{
super(group, name);
}
public void run()
{
synchronized(this) { started = true; }
while (!stop)
{
for (int i=0; i<100; i++);
}
}
synchronized public boolean isStarted()
{
return started;
}
public void terminate()
{
stop = true;
}
}
public class ThreadLifecycle001
{
public static void main(String[] args)
{
System.out.println("ThreadLifecycle001 test");
// create and start NewThread
ThreadGroup newGroup = new ThreadGroup("newGroup");
NewThread newThread = new NewThread(newGroup, "NewThread");
newThread.start();
ThreadGroup parentGroup = Thread.currentThread().getThreadGroup();
if (parentGroup == null)
return;
while (parentGroup.getParent() != null)
{
parentGroup = parentGroup.getParent();
}
// wait if newThread is not yet started
while (!newThread.isStarted())
{
for (int i = 0; i < 100; i++);
}
System.out.println();
listThreads(parentGroup);
newThread.terminate();
try
{
newThread.join();
}
catch(InterruptedException ex) {}
}
static void listThreads(ThreadGroup currentGroup)
{
int threadCount = currentGroup.activeCount();
Thread listOfThreads[] = new Thread[threadCount];
currentGroup.enumerate(listOfThreads, true);
for (int i = 0; i < threadCount; i++)
{
System.out.println(
"Thread: id=" + listOfThreads[i].getId() +
", is_daemon=" + (listOfThreads[i].isDaemon() ? "1" :
"0") +
", name=" + listOfThreads[i].getName() +
", groupName=" +
listOfThreads[i].getThreadGroup().getName() );
}
}
}
::::::::::::::
ThreadLifecycle001.pl
::::::::::::::
# %W% %G%
# Copyright %G% Sun Microsystems, Inc.
#
# Implement analyze_dtrace_results_proc function for ThreadLifecycle001
test.
#
sub analyze_dtrace_results_proc($$$)
{
my ($dtrace_log_filename, $java_log_filename, $args) = @_;
my $test_fail = 0;
my %java_threads = (); # threads started according to Java
log file
my %dtrace_start_threads = (); # threads started according to
hotspot:::thread-start probes
my %dtrace_stop_threads = (); # threads started according to
hotspot:::thread-start probes
if ( &read_java_log($java_log_filename, \%java_threads) ||
&read_dtrace_log($dtrace_log_filename, \%dtrace_start_threads,
\%dtrace_stop_threads) )
{
return 1;
}
# Check that all probes have been fired
foreach (keys %java_threads)
{
unless (exists $dtrace_start_threads{$_}) {
print STDOUT "ERROR: no 'thread-start' probe was fired for
thread: $_\n";
$test_fail = 1;
}
unless (exists $dtrace_stop_threads{$_}) {
print STDOUT "ERROR: no 'thread-stop' probe was fired for
thread: $_\n";
$test_fail = 1;
}
}
# check that 'thread-start' probes have been fired exactly 1 time
for each thread
foreach (keys %dtrace_start_threads) {
if ($dtrace_start_threads{$_} > 1) {
print STDOUT "ERROR: 'thread-start' probe for '$_' fired " .
$dtrace_start_threads{$_} . " (>1) times\n";
$test_fail = 1;
}
}
# check that 'thread-stop' probes have been fired exactly 1 time
for each thread
foreach (keys %dtrace_stop_threads) {
if ($dtrace_stop_threads{$_} > 1) {
print STDOUT "ERROR: 'thread-stop' probe for '$_' fired " .
$dtrace_stop_threads{$_} . " (>1) times\n";
$test_fail = 1;
}
}
return $test_fail;
}
sub read_java_log($$)
{
my ($java_log_filename, $java_threads) = @_;
unless (open(IN, "<$java_log_filename"))
{
print STDOUT "Failed to read $java_log_filename: $!\n";
return 1;
}
while(my $str = <IN>)
{
$str =~ s/\r*\n//;
if ($str =~ s/^Thread: //)
{
# thread group name is not passed in dtrace probes, so
delete it
$str =~ s/, groupName=.*$//;
$java_threads->{$str} ++;
}
}
close(IN);
return 0;
}
sub read_dtrace_log($$$)
{
my ($dtrace_log_filename, $dtrace_start_threads,
$dtrace_stop_threads) = @_;
unless (open(IN, "<$dtrace_log_filename"))
{
print STDOUT "Failed to read $java_log_filename: $!\n";
return 1;
}
my $probe_name;
while(my $str = <IN>)
{
$str =~ s/\r*\n//;
if ($str =~ s/^thread-start: //)
{
$dtrace_start_threads->{$str} ++;
}
elsif ($str =~ s/^thread-stop: //)
{
$dtrace_stop_threads->{$str} ++;
}
}
close(IN);
return 0;
}
1;
::::::::::::::
ThreadLifecycle001.README
::::::::::::::
%W% %G%
Copyright %G% Sun Microsystems, Inc.
DESCRIPTION
The test exercise hotspot:::thread-start and hotspot:::thread-stop
Dtrace probes functionality.
The test do:
- run ThreadLifecycle001 java programm which dumps information abould
all running threads in VM.
- check if:
- for each Java thread thread-start and thread-stop probes are
fired the same time
- input probe's arguments are passed correctly
(attempt to print them doesn't cause the error)
- no any Dtrace errors are thrown
COMMENTS
AUTHOR
. . .
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/serviceability-dev/attachments/20120718/630086e2/attachment-0001.html
More information about the serviceability-dev
mailing list