Mercurial > hg > truffle
annotate agent/src/share/native/jvmdi/sa.cpp @ 3237:399aa66d375e
Fixed a bug in which the valueEquals method was misused. The method does only check the equality of the node data and not full GVN equality by taking inputs and successors into account.
author | Thomas Wuerthinger <thomas@wuerthinger.net> |
---|---|
date | Wed, 27 Jul 2011 14:16:38 -0700 |
parents | c18cbe5936b8 |
children |
rev | line source |
---|---|
0 | 1 /* |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
2 * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. |
0 | 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 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
21 * questions. |
0 | 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 }; |