Mercurial > hg > truffle
annotate agent/src/share/native/jvmdi/sa.cpp @ 6117:4434fdad6b37
Merge
author | dholmes |
---|---|
date | Sat, 02 Jun 2012 07:32:21 -0400 |
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 }; |