[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
  . . .






More information about the hotspot-dev mailing list