hotspot/agent/src/share/native/jvmdi/sa.cpp
changeset 15836 e7427054542d
parent 15835 55eb22e60638
parent 15834 89c34ec6d638
child 15838 8fe45216f573
equal deleted inserted replaced
15835:55eb22e60638 15836:e7427054542d
     1 /*
       
     2  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  *
       
    23  */
       
    24 
       
    25 #include <stdio.h>
       
    26 #include <stdarg.h>
       
    27 #include <stdlib.h>
       
    28 #include <vector>
       
    29 #include "sa.hpp"
       
    30 #include "jni.h"
       
    31 #include "jvmdi.h"
       
    32 
       
    33 #ifndef WIN32
       
    34  #include <inttypes.h>
       
    35 #else
       
    36  typedef int int32_t;
       
    37 #endif
       
    38 
       
    39 #ifdef WIN32
       
    40  #include <windows.h>
       
    41  #define YIELD() Sleep(0)
       
    42  #define SLEEP() Sleep(10)
       
    43  #define vsnprintf _vsnprintf
       
    44 #else
       
    45  Error: please port YIELD() and SLEEP() macros to your platform
       
    46 #endif
       
    47 
       
    48 using namespace std;
       
    49 
       
    50 //////////////////////////////////////////////////////////////////////
       
    51 //                                                                  //
       
    52 // Exported "interface" for Java language-level interaction between //
       
    53 // the SA and the VM. Note that the SA knows about the layout of    //
       
    54 // certain VM data structures and that knowledge is taken advantage //
       
    55 // of in this code, although this interfaces with the VM via JVMDI. //
       
    56 //                                                                  //
       
    57 //////////////////////////////////////////////////////////////////////
       
    58 
       
    59 extern "C" {
       
    60   /////////////////////////////////////
       
    61   //                                 //
       
    62   // Events sent by the VM to the SA //
       
    63   //                                 //
       
    64   /////////////////////////////////////
       
    65 
       
    66   // Set by the SA when it attaches. Indicates that events should be
       
    67   // posted via these exported variables, and that the VM should wait
       
    68   // for those events to be acknowledged by the SA (via its setting
       
    69   // saEventPending to 0).
       
    70   JNIEXPORT volatile int32_t saAttached     = 0;
       
    71 
       
    72   // Set to nonzero value by the VM when an event has been posted; set
       
    73   // back to 0 by the SA when it has processed that event.
       
    74   JNIEXPORT volatile int32_t saEventPending = 0;
       
    75 
       
    76   // Kind of the event (from jvmdi.h)
       
    77   JNIEXPORT volatile int32_t saEventKind    = 0;
       
    78 
       
    79   //
       
    80   // Exception events
       
    81   //
       
    82   JNIEXPORT jthread   saExceptionThread;
       
    83   JNIEXPORT jclass    saExceptionClass;
       
    84   JNIEXPORT jmethodID saExceptionMethod;
       
    85   JNIEXPORT int32_t   saExceptionLocation;
       
    86   JNIEXPORT jobject   saExceptionException;
       
    87   JNIEXPORT jclass    saExceptionCatchClass;
       
    88   JNIEXPORT jmethodID saExceptionCatchMethod;
       
    89   JNIEXPORT int32_t   saExceptionCatchLocation;
       
    90 
       
    91   //
       
    92   // Breakpoint events
       
    93   //
       
    94   JNIEXPORT jthread   saBreakpointThread;
       
    95   JNIEXPORT jclass    saBreakpointClass;
       
    96   JNIEXPORT jmethodID saBreakpointMethod;
       
    97   JNIEXPORT jlocation saBreakpointLocation;
       
    98 
       
    99   ///////////////////////////////////////
       
   100   //                                   //
       
   101   // Commands sent by the SA to the VM //
       
   102   //                                   //
       
   103   ///////////////////////////////////////
       
   104 
       
   105   extern JNIEXPORT const int32_t SA_CMD_SUSPEND_ALL       = 0;
       
   106   extern JNIEXPORT const int32_t SA_CMD_RESUME_ALL        = 1;
       
   107   extern JNIEXPORT const int32_t SA_CMD_TOGGLE_BREAKPOINT = 2;
       
   108   extern JNIEXPORT const int32_t SA_CMD_BUF_SIZE          = 1024;
       
   109 
       
   110   // SA sets this to a nonzero value when it is requesting a command
       
   111   // to be processed; VM sets it back to 0 when the command has been
       
   112   // executed
       
   113   JNIEXPORT volatile int32_t saCmdPending   = 0;
       
   114 
       
   115   // SA sets this to one of the manifest constants above to indicate
       
   116   // the kind of command to be executed
       
   117   JNIEXPORT volatile int32_t saCmdType      = 0;
       
   118 
       
   119   // VM sets this to 0 if the last command succeeded or a nonzero
       
   120   // value if it failed
       
   121   JNIEXPORT volatile int32_t saCmdResult    = 0;
       
   122 
       
   123   // If last command failed, this buffer will contain a descriptive
       
   124   // error message
       
   125   JNIEXPORT char             saCmdResultErrMsg[SA_CMD_BUF_SIZE];
       
   126 
       
   127   //
       
   128   // Toggling of breakpoint command arguments.
       
   129   //
       
   130   // Originally there were separate set/clear breakpoint commands
       
   131   // taking a class name, method name and signature, and the iteration
       
   132   // through the debug information was done in the SA. It turns out
       
   133   // that doing this work in the target VM is significantly faster,
       
   134   // and since interactivity when setting and clearing breakpoints is
       
   135   // important, the solution which resulted in more C/C++ code was used.
       
   136   //
       
   137 
       
   138   // Source file name
       
   139   JNIEXPORT char    saCmdBkptSrcFileName[SA_CMD_BUF_SIZE];
       
   140 
       
   141   // Package name ('/' as separator instead of '.')
       
   142   JNIEXPORT char    saCmdBkptPkgName[SA_CMD_BUF_SIZE];
       
   143 
       
   144   // Line number
       
   145   JNIEXPORT int32_t saCmdBkptLineNumber;
       
   146 
       
   147   // Output back to SA: indicator whether the last failure of a
       
   148   // breakpoint toggle command was really an error or just a lack of
       
   149   // debug information covering the requested line. 0 if not error.
       
   150   // Valid only if saCmdResult != 0.
       
   151   JNIEXPORT int32_t saCmdBkptResWasError;
       
   152 
       
   153   // Output back to SA: resulting line number at which the breakpoint
       
   154   // was set or cleared (valid only if saCmdResult == 0)
       
   155   JNIEXPORT int32_t saCmdBkptResLineNumber;
       
   156 
       
   157   // Output back to SA: resulting byte code index at which the
       
   158   // breakpoint was set or cleared (valid only if saCmdResult == 0)
       
   159   JNIEXPORT int32_t saCmdBkptResBCI;
       
   160 
       
   161   // Output back to SA: indicator whether the breakpoint operation
       
   162   // resulted in a set or cleared breakpoint; nonzero if set, zero if
       
   163   // cleared (valid only if saCmdResult == 0)
       
   164   JNIEXPORT int32_t saCmdBkptResWasSet;
       
   165 
       
   166   // Output back to SA: method name the breakpoint was set in (valid
       
   167   // only if saCmdResult == 0)
       
   168   JNIEXPORT char    saCmdBkptResMethodName[SA_CMD_BUF_SIZE];
       
   169 
       
   170   // Output back to SA: method signature (JNI style) the breakpoint
       
   171   // was set in (valid only if saCmdResult == 0)
       
   172   JNIEXPORT char    saCmdBkptResMethodSig[SA_CMD_BUF_SIZE];
       
   173 }
       
   174 
       
   175 // Internal state
       
   176 static JavaVM* jvm = NULL;
       
   177 static JVMDI_Interface_1* jvmdi = NULL;
       
   178 static jthread debugThreadObj = NULL;
       
   179 static bool suspended = false;
       
   180 static vector<jthread> suspendedThreads;
       
   181 static JVMDI_RawMonitor eventLock = NULL;
       
   182 
       
   183 class MonitorLocker {
       
   184 private:
       
   185   JVMDI_RawMonitor lock;
       
   186 public:
       
   187   MonitorLocker(JVMDI_RawMonitor lock) {
       
   188     this->lock = lock;
       
   189     if (lock != NULL) {
       
   190       jvmdi->RawMonitorEnter(lock);
       
   191     }
       
   192   }
       
   193   ~MonitorLocker() {
       
   194     if (lock != NULL) {
       
   195       jvmdi->RawMonitorExit(lock);
       
   196     }
       
   197   }
       
   198 };
       
   199 
       
   200 class JvmdiDeallocator {
       
   201 private:
       
   202   void* ptr;
       
   203 public:
       
   204   JvmdiDeallocator(void* ptr) {
       
   205     this->ptr = ptr;
       
   206   }
       
   207   ~JvmdiDeallocator() {
       
   208     jvmdi->Deallocate((jbyte*) ptr);
       
   209   }
       
   210 };
       
   211 
       
   212 class JvmdiRefListDeallocator {
       
   213 private:
       
   214   JNIEnv* env;
       
   215   jobject* refList;
       
   216   jint refCount;
       
   217 public:
       
   218   JvmdiRefListDeallocator(JNIEnv* env, jobject* refList, jint refCount) {
       
   219     this->env = env;
       
   220     this->refList = refList;
       
   221     this->refCount = refCount;
       
   222   }
       
   223   ~JvmdiRefListDeallocator() {
       
   224     for (int i = 0; i < refCount; i++) {
       
   225       env->DeleteGlobalRef(refList[i]);
       
   226     }
       
   227     jvmdi->Deallocate((jbyte*) refList);
       
   228   }
       
   229 };
       
   230 
       
   231 static void
       
   232 stop(char* msg) {
       
   233   fprintf(stderr, "%s", msg);
       
   234   fprintf(stderr, "\n");
       
   235   exit(1);
       
   236 }
       
   237 
       
   238 // This fills in the command result error message, sets the command
       
   239 // result to -1, and clears the pending command flag
       
   240 static void
       
   241 reportErrorToSA(const char* str, ...) {
       
   242   va_list varargs;
       
   243   va_start(varargs, str);
       
   244   vsnprintf(saCmdResultErrMsg, sizeof(saCmdResultErrMsg), str, varargs);
       
   245   va_end(varargs);
       
   246   saCmdResult = -1;
       
   247   saCmdPending = 0;
       
   248 }
       
   249 
       
   250 static bool
       
   251 packageNameMatches(char* clazzName, char* pkg) {
       
   252   int pkgLen = strlen(pkg);
       
   253   int clazzNameLen = strlen(clazzName);
       
   254 
       
   255   if (pkgLen >= clazzNameLen + 1) {
       
   256     return false;
       
   257   }
       
   258 
       
   259   if (strncmp(clazzName, pkg, pkgLen)) {
       
   260     return false;
       
   261   }
       
   262 
       
   263   // Ensure that '/' is the next character if non-empty package name
       
   264   int l = pkgLen;
       
   265   if (l > 0) {
       
   266     if (clazzName[l] != '/') {
       
   267       return false;
       
   268     }
       
   269     l++;
       
   270   }
       
   271   // Ensure that there are no more trailing slashes
       
   272   while (l < clazzNameLen) {
       
   273     if (clazzName[l++] == '/') {
       
   274       return false;
       
   275     }
       
   276   }
       
   277   return true;
       
   278 }
       
   279 
       
   280 static void
       
   281 executeOneCommand(JNIEnv* env) {
       
   282   switch (saCmdType) {
       
   283   case SA_CMD_SUSPEND_ALL: {
       
   284     if (suspended) {
       
   285       reportErrorToSA("Target process already suspended");
       
   286       return;
       
   287     }
       
   288 
       
   289     // We implement this by getting all of the threads and calling
       
   290     // SuspendThread on each one, except for the thread object
       
   291     // corresponding to this thread. Each thread for which the call
       
   292     // succeeded (i.e., did not return JVMDI_ERROR_INVALID_THREAD)
       
   293     // is added to a list which is remembered for later resumption.
       
   294     // Note that this currently has race conditions since a thread
       
   295     // might be started after we call GetAllThreads and since a
       
   296     // thread for which we got an error earlier might be resumed by
       
   297     // the VM while we are busy suspending other threads. We could
       
   298     // solve this by looping until there are no more threads we can
       
   299     // suspend, but a more robust and scalable solution is to add
       
   300     // this functionality to the JVMDI interface (i.e.,
       
   301     // "suspendAll"). Probably need to provide an exclude list for
       
   302     // such a routine.
       
   303     jint threadCount;
       
   304     jthread* threads;
       
   305     if (jvmdi->GetAllThreads(&threadCount, &threads) != JVMDI_ERROR_NONE) {
       
   306       reportErrorToSA("Error while getting thread list");
       
   307       return;
       
   308     }
       
   309 
       
   310 
       
   311     for (int i = 0; i < threadCount; i++) {
       
   312       jthread thr = threads[i];
       
   313       if (!env->IsSameObject(thr, debugThreadObj)) {
       
   314         jvmdiError err = jvmdi->SuspendThread(thr);
       
   315         if (err == JVMDI_ERROR_NONE) {
       
   316           // Remember this thread and do not free it
       
   317           suspendedThreads.push_back(thr);
       
   318           continue;
       
   319         } else {
       
   320           fprintf(stderr, " SA: Error %d while suspending thread\n", err);
       
   321           // FIXME: stop, resume all threads, report error
       
   322         }
       
   323       }
       
   324       env->DeleteGlobalRef(thr);
       
   325     }
       
   326 
       
   327     // Free up threads
       
   328     jvmdi->Deallocate((jbyte*) threads);
       
   329 
       
   330     // Suspension is complete
       
   331     suspended = true;
       
   332     break;
       
   333   }
       
   334 
       
   335   case SA_CMD_RESUME_ALL: {
       
   336     if (!suspended) {
       
   337       reportErrorToSA("Target process already suspended");
       
   338       return;
       
   339     }
       
   340 
       
   341     saCmdResult = 0;
       
   342     bool errorOccurred = false;
       
   343     jvmdiError firstError;
       
   344     for (int i = 0; i < suspendedThreads.size(); i++) {
       
   345       jthread thr = suspendedThreads[i];
       
   346       jvmdiError err = jvmdi->ResumeThread(thr);
       
   347       env->DeleteGlobalRef(thr);
       
   348       if (err != JVMDI_ERROR_NONE) {
       
   349         if (!errorOccurred) {
       
   350           errorOccurred = true;
       
   351           firstError = err;
       
   352         }
       
   353       }
       
   354     }
       
   355     suspendedThreads.clear();
       
   356     suspended = false;
       
   357     if (errorOccurred) {
       
   358       reportErrorToSA("Error %d while resuming threads", firstError);
       
   359       return;
       
   360     }
       
   361     break;
       
   362   }
       
   363 
       
   364   case SA_CMD_TOGGLE_BREAKPOINT: {
       
   365     saCmdBkptResWasError = 1;
       
   366 
       
   367     // Search line number info for all loaded classes
       
   368     jint classCount;
       
   369     jclass* classes;
       
   370 
       
   371     jvmdiError glcRes = jvmdi->GetLoadedClasses(&classCount, &classes);
       
   372     if (glcRes != JVMDI_ERROR_NONE) {
       
   373       reportErrorToSA("Error %d while getting loaded classes", glcRes);
       
   374       return;
       
   375     }
       
   376     JvmdiRefListDeallocator rld(env, (jobject*) classes, classCount);
       
   377 
       
   378     bool done = false;
       
   379     bool gotOne = false;
       
   380     jclass targetClass;
       
   381     jmethodID targetMethod;
       
   382     jlocation targetLocation;
       
   383     jint targetLineNumber;
       
   384 
       
   385     for (int i = 0; i < classCount && !done; i++) {
       
   386       fflush(stderr);
       
   387       jclass clazz = classes[i];
       
   388       char* srcName;
       
   389       jvmdiError sfnRes = jvmdi->GetSourceFileName(clazz, &srcName);
       
   390       if (sfnRes == JVMDI_ERROR_NONE) {
       
   391         JvmdiDeallocator de1(srcName);
       
   392         if (!strcmp(srcName, saCmdBkptSrcFileName)) {
       
   393           // Got a match. Now see whether the package name of the class also matches
       
   394           char* clazzName;
       
   395           jvmdiError sigRes = jvmdi->GetClassSignature(clazz, &clazzName);
       
   396           if (sigRes != JVMDI_ERROR_NONE) {
       
   397             reportErrorToSA("Error %d while getting a class's signature", sigRes);
       
   398             return;
       
   399           }
       
   400           JvmdiDeallocator de2(clazzName);
       
   401           if (packageNameMatches(clazzName + 1, saCmdBkptPkgName)) {
       
   402             // Iterate through all methods
       
   403             jint methodCount;
       
   404             jmethodID* methods;
       
   405             if (jvmdi->GetClassMethods(clazz, &methodCount, &methods) != JVMDI_ERROR_NONE) {
       
   406               reportErrorToSA("Error while getting methods of class %s", clazzName);
       
   407               return;
       
   408             }
       
   409             JvmdiDeallocator de3(methods);
       
   410             for (int j = 0; j < methodCount && !done; j++) {
       
   411               jmethodID m = methods[j];
       
   412               jint entryCount;
       
   413               JVMDI_line_number_entry* table;
       
   414               jvmdiError lnRes = jvmdi->GetLineNumberTable(clazz, m, &entryCount, &table);
       
   415               if (lnRes == JVMDI_ERROR_NONE) {
       
   416                 JvmdiDeallocator de4(table);
       
   417                 // Look for line number greater than or equal to requested line
       
   418                 for (int k = 0; k < entryCount && !done; k++) {
       
   419                   JVMDI_line_number_entry& entry = table[k];
       
   420                   if (entry.line_number >= saCmdBkptLineNumber &&
       
   421                       (!gotOne || entry.line_number < targetLineNumber)) {
       
   422                     gotOne = true;
       
   423                     targetClass = clazz;
       
   424                     targetMethod = m;
       
   425                     targetLocation = entry.start_location;
       
   426                     targetLineNumber = entry.line_number;
       
   427                     done = (targetLineNumber == saCmdBkptLineNumber);
       
   428                   }
       
   429                 }
       
   430               } else if (lnRes != JVMDI_ERROR_ABSENT_INFORMATION) {
       
   431                 reportErrorToSA("Unexpected error %d while fetching line number table", lnRes);
       
   432                 return;
       
   433               }
       
   434             }
       
   435           }
       
   436         }
       
   437       } else if (sfnRes != JVMDI_ERROR_ABSENT_INFORMATION) {
       
   438         reportErrorToSA("Unexpected error %d while fetching source file name", sfnRes);
       
   439         return;
       
   440       }
       
   441     }
       
   442 
       
   443     bool wasSet = true;
       
   444     if (gotOne) {
       
   445       // Really toggle this breakpoint
       
   446       jvmdiError bpRes;
       
   447       bpRes = jvmdi->SetBreakpoint(targetClass, targetMethod, targetLocation);
       
   448       if (bpRes == JVMDI_ERROR_DUPLICATE) {
       
   449         bpRes = jvmdi->ClearBreakpoint(targetClass, targetMethod, targetLocation);
       
   450         wasSet = false;
       
   451       }
       
   452       if (bpRes != JVMDI_ERROR_NONE) {
       
   453         reportErrorToSA("Unexpected error %d while setting or clearing breakpoint at bci %d, line %d",
       
   454                         bpRes, targetLocation, targetLineNumber);
       
   455         return;
       
   456       }
       
   457     } else {
       
   458       saCmdBkptResWasError = 0;
       
   459       reportErrorToSA("No debug information found covering this line");
       
   460       return;
       
   461     }
       
   462 
       
   463     // Provide result
       
   464     saCmdBkptResLineNumber = targetLineNumber;
       
   465     saCmdBkptResBCI        = targetLocation;
       
   466     saCmdBkptResWasSet     = (wasSet ? 1 : 0);
       
   467     {
       
   468       char* methodName;
       
   469       char* methodSig;
       
   470       if (jvmdi->GetMethodName(targetClass, targetMethod, &methodName, &methodSig)
       
   471           == JVMDI_ERROR_NONE) {
       
   472         JvmdiDeallocator mnd(methodName);
       
   473         JvmdiDeallocator msd(methodSig);
       
   474         strncpy(saCmdBkptResMethodName, methodName, SA_CMD_BUF_SIZE);
       
   475         strncpy(saCmdBkptResMethodSig,  methodSig, SA_CMD_BUF_SIZE);
       
   476       } else {
       
   477         strncpy(saCmdBkptResMethodName, "<error>", SA_CMD_BUF_SIZE);
       
   478         strncpy(saCmdBkptResMethodSig,  "<error>", SA_CMD_BUF_SIZE);
       
   479       }
       
   480     }
       
   481     break;
       
   482   }
       
   483 
       
   484   default:
       
   485     reportErrorToSA("Command %d not yet supported", saCmdType);
       
   486     return;
       
   487   }
       
   488 
       
   489   // Successful command execution
       
   490   saCmdResult = 0;
       
   491   saCmdPending = 0;
       
   492 }
       
   493 
       
   494 static void
       
   495 saCommandThread(void *arg) {
       
   496   JNIEnv* env = NULL;
       
   497   if (jvm->GetEnv((void **) &env, JNI_VERSION_1_2) != JNI_OK) {
       
   498     stop("Error while starting Serviceability Agent "
       
   499          "command thread: could not get JNI environment");
       
   500   }
       
   501 
       
   502   while (1) {
       
   503     // Wait for command
       
   504     while (!saCmdPending) {
       
   505       SLEEP();
       
   506     }
       
   507 
       
   508     executeOneCommand(env);
       
   509   }
       
   510 }
       
   511 
       
   512 static void
       
   513 saEventHook(JNIEnv *env, JVMDI_Event *event)
       
   514 {
       
   515   MonitorLocker ml(eventLock);
       
   516 
       
   517   saEventKind = event->kind;
       
   518 
       
   519   if (event->kind == JVMDI_EVENT_VM_INIT) {
       
   520     // Create event lock
       
   521     if (jvmdi->CreateRawMonitor("Serviceability Agent Event Lock", &eventLock)
       
   522         != JVMDI_ERROR_NONE) {
       
   523       stop("Unable to create Serviceability Agent's event lock");
       
   524     }
       
   525     // Start thread which receives commands from the SA.
       
   526     jclass threadClass = env->FindClass("java/lang/Thread");
       
   527     if (threadClass == NULL) stop("Unable to find class java/lang/Thread");
       
   528     jstring threadName = env->NewStringUTF("Serviceability Agent Command Thread");
       
   529     if (threadName == NULL) stop("Unable to allocate debug thread name");
       
   530     jmethodID ctor = env->GetMethodID(threadClass, "<init>", "(Ljava/lang/String;)V");
       
   531     if (ctor == NULL) stop("Unable to find appropriate constructor for java/lang/Thread");
       
   532     // Allocate thread object
       
   533     jthread thr = (jthread) env->NewObject(threadClass, ctor, threadName);
       
   534     if (thr == NULL) stop("Unable to allocate debug thread's java/lang/Thread instance");
       
   535     // Remember which thread this is
       
   536     debugThreadObj = env->NewGlobalRef(thr);
       
   537     if (debugThreadObj == NULL) stop("Unable to allocate global ref for debug thread object");
       
   538     // Start thread
       
   539     jvmdiError err;
       
   540     if ((err = jvmdi->RunDebugThread(thr, &saCommandThread, NULL, JVMDI_THREAD_NORM_PRIORITY))
       
   541         != JVMDI_ERROR_NONE) {
       
   542       char buf[256];
       
   543       sprintf(buf, "Error %d while starting debug thread", err);
       
   544       stop(buf);
       
   545     }
       
   546     // OK, initialization is done
       
   547     return;
       
   548   }
       
   549 
       
   550   if (!saAttached) {
       
   551     return;
       
   552   }
       
   553 
       
   554   switch (event->kind) {
       
   555   case JVMDI_EVENT_EXCEPTION: {
       
   556     fprintf(stderr, "SA: Exception thrown -- ignoring\n");
       
   557     saExceptionThread        = event->u.exception.thread;
       
   558     saExceptionClass         = event->u.exception.clazz;
       
   559     saExceptionMethod        = event->u.exception.method;
       
   560     saExceptionLocation      = event->u.exception.location;
       
   561     saExceptionException     = event->u.exception.exception;
       
   562     saExceptionCatchClass    = event->u.exception.catch_clazz;
       
   563     saExceptionCatchClass    = event->u.exception.catch_clazz;
       
   564     saExceptionCatchMethod   = event->u.exception.catch_method;
       
   565     saExceptionCatchLocation = event->u.exception.catch_location;
       
   566     //    saEventPending = 1;
       
   567     break;
       
   568   }
       
   569 
       
   570   case JVMDI_EVENT_BREAKPOINT: {
       
   571     saBreakpointThread       = event->u.breakpoint.thread;
       
   572     saBreakpointClass        = event->u.breakpoint.clazz;
       
   573     saBreakpointMethod       = event->u.breakpoint.method;
       
   574     saBreakpointLocation     = event->u.breakpoint.location;
       
   575     saEventPending = 1;
       
   576     break;
       
   577   }
       
   578 
       
   579   default:
       
   580     break;
       
   581   }
       
   582 
       
   583   while (saAttached && saEventPending) {
       
   584     SLEEP();
       
   585   }
       
   586 }
       
   587 
       
   588 extern "C" {
       
   589 JNIEXPORT jint JNICALL
       
   590 JVM_OnLoad(JavaVM *vm, char *options, void *reserved)
       
   591 {
       
   592   jvm = vm;
       
   593   if (jvm->GetEnv((void**) &jvmdi, JVMDI_VERSION_1) != JNI_OK) {
       
   594     return -1;
       
   595   }
       
   596   if (jvmdi->SetEventHook(&saEventHook) != JVMDI_ERROR_NONE) {
       
   597     return -1;
       
   598   }
       
   599   return 0;
       
   600 }
       
   601 };