1 | /*
|
---|
2 | * Copyright (c) 1998, 2007, 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. Oracle designates this
|
---|
8 | * particular file as subject to the "Classpath" exception as provided
|
---|
9 | * by Oracle in the LICENSE file that accompanied this code.
|
---|
10 | *
|
---|
11 | * This code is distributed in the hope that it will be useful, but WITHOUT
|
---|
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
---|
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
---|
14 | * version 2 for more details (a copy is included in the LICENSE file that
|
---|
15 | * accompanied this code).
|
---|
16 | *
|
---|
17 | * You should have received a copy of the GNU General Public License version
|
---|
18 | * 2 along with this work; if not, write to the Free Software Foundation,
|
---|
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
---|
20 | *
|
---|
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
---|
22 | * or visit www.oracle.com if you need additional information or have any
|
---|
23 | * questions.
|
---|
24 | */
|
---|
25 |
|
---|
26 | #include "util.h"
|
---|
27 | #include "eventHandler.h"
|
---|
28 | #include "threadControl.h"
|
---|
29 | #include "commonRef.h"
|
---|
30 | #include "eventHelper.h"
|
---|
31 | #include "stepControl.h"
|
---|
32 | #include "invoker.h"
|
---|
33 | #include "bag.h"
|
---|
34 |
|
---|
35 | #define HANDLING_EVENT(node) ((node)->current_ei != 0)
|
---|
36 |
|
---|
37 | /*
|
---|
38 | * Collection of info for properly handling co-located events.
|
---|
39 | * If the ei field is non-zero, then one of the possible
|
---|
40 | * co-located events has been posted and the other fields describe
|
---|
41 | * the event's location.
|
---|
42 | */
|
---|
43 | typedef struct CoLocatedEventInfo_ {
|
---|
44 | EventIndex ei;
|
---|
45 | jclass clazz;
|
---|
46 | jmethodID method;
|
---|
47 | jlocation location;
|
---|
48 | } CoLocatedEventInfo;
|
---|
49 |
|
---|
50 | /**
|
---|
51 | * The main data structure in threadControl is the ThreadNode.
|
---|
52 | * This is a per-thread structure that is allocated on the
|
---|
53 | * first event that occurs in a thread. It is freed after the
|
---|
54 | * thread's thread end event has completed processing. The
|
---|
55 | * structure contains state information on its thread including
|
---|
56 | * suspend counts. It also acts as a repository for other
|
---|
57 | * per-thread state such as the current method invocation or
|
---|
58 | * current step.
|
---|
59 | *
|
---|
60 | * suspendCount is the number of outstanding suspends
|
---|
61 | * from the debugger. suspends from the app itself are
|
---|
62 | * not included in this count.
|
---|
63 | */
|
---|
64 | typedef struct ThreadNode {
|
---|
65 | jthread thread;
|
---|
66 | unsigned int toBeResumed : 1;
|
---|
67 | unsigned int pendingInterrupt : 1;
|
---|
68 | unsigned int isDebugThread : 1;
|
---|
69 | unsigned int suspendOnStart : 1;
|
---|
70 | unsigned int isStarted : 1;
|
---|
71 | unsigned int popFrameEvent : 1;
|
---|
72 | unsigned int popFrameProceed : 1;
|
---|
73 | unsigned int popFrameThread : 1;
|
---|
74 | EventIndex current_ei;
|
---|
75 | jobject pendingStop;
|
---|
76 | jint suspendCount;
|
---|
77 | jint resumeFrameDepth; /* !=0 => This thread is in a call to Thread.resume() */
|
---|
78 | jvmtiEventMode instructionStepMode;
|
---|
79 | StepRequest currentStep;
|
---|
80 | InvokeRequest currentInvoke;
|
---|
81 | struct bag *eventBag;
|
---|
82 | CoLocatedEventInfo cleInfo;
|
---|
83 | struct ThreadNode *next;
|
---|
84 | struct ThreadNode *prev;
|
---|
85 | jlong frameGeneration;
|
---|
86 | struct ThreadList *list; /* Tells us what list this thread is in */
|
---|
87 | } ThreadNode;
|
---|
88 |
|
---|
89 | static jint suspendAllCount;
|
---|
90 |
|
---|
91 | typedef struct ThreadList {
|
---|
92 | ThreadNode *first;
|
---|
93 | } ThreadList;
|
---|
94 |
|
---|
95 | /*
|
---|
96 | * popFrameEventLock is used to notify that the event has been received
|
---|
97 | */
|
---|
98 | static jrawMonitorID popFrameEventLock = NULL;
|
---|
99 |
|
---|
100 | /*
|
---|
101 | * popFrameProceedLock is used to assure that the event thread is
|
---|
102 | * re-suspended immediately after the event is acknowledged.
|
---|
103 | */
|
---|
104 | static jrawMonitorID popFrameProceedLock = NULL;
|
---|
105 |
|
---|
106 | static jrawMonitorID threadLock;
|
---|
107 | static jlocation resumeLocation;
|
---|
108 | static HandlerNode *breakpointHandlerNode;
|
---|
109 | static HandlerNode *framePopHandlerNode;
|
---|
110 | static HandlerNode *catchHandlerNode;
|
---|
111 |
|
---|
112 | static jvmtiError threadControl_removeDebugThread(jthread thread);
|
---|
113 |
|
---|
114 | /*
|
---|
115 | * Threads which have issued thread start events and not yet issued thread
|
---|
116 | * end events are maintained in the "runningThreads" list. All other threads known
|
---|
117 | * to this module are kept in the "otherThreads" list.
|
---|
118 | */
|
---|
119 | static ThreadList runningThreads;
|
---|
120 | static ThreadList otherThreads;
|
---|
121 |
|
---|
122 | #define MAX_DEBUG_THREADS 10
|
---|
123 | static int debugThreadCount;
|
---|
124 | static jthread debugThreads[MAX_DEBUG_THREADS];
|
---|
125 |
|
---|
126 | typedef struct DeferredEventMode {
|
---|
127 | EventIndex ei;
|
---|
128 | jvmtiEventMode mode;
|
---|
129 | jthread thread;
|
---|
130 | struct DeferredEventMode *next;
|
---|
131 | } DeferredEventMode;
|
---|
132 |
|
---|
133 | typedef struct {
|
---|
134 | DeferredEventMode *first;
|
---|
135 | DeferredEventMode *last;
|
---|
136 | } DeferredEventModeList;
|
---|
137 |
|
---|
138 | static DeferredEventModeList deferredEventModes;
|
---|
139 |
|
---|
140 | static jint
|
---|
141 | getStackDepth(jthread thread)
|
---|
142 | {
|
---|
143 | jint count = 0;
|
---|
144 | jvmtiError error;
|
---|
145 |
|
---|
146 | error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameCount)
|
---|
147 | (gdata->jvmti, thread, &count);
|
---|
148 | if (error != JVMTI_ERROR_NONE) {
|
---|
149 | EXIT_ERROR(error, "getting frame count");
|
---|
150 | }
|
---|
151 | return count;
|
---|
152 | }
|
---|
153 |
|
---|
154 | /* Get the state of the thread direct from JVMTI */
|
---|
155 | static jvmtiError
|
---|
156 | threadState(jthread thread, jint *pstate)
|
---|
157 | {
|
---|
158 | *pstate = 0;
|
---|
159 | return JVMTI_FUNC_PTR(gdata->jvmti,GetThreadState)
|
---|
160 | (gdata->jvmti, thread, pstate);
|
---|
161 | }
|
---|
162 |
|
---|
163 | /* Set TLS on a specific jthread to the ThreadNode* */
|
---|
164 | static void
|
---|
165 | setThreadLocalStorage(jthread thread, ThreadNode *node)
|
---|
166 | {
|
---|
167 | jvmtiError error;
|
---|
168 |
|
---|
169 | error = JVMTI_FUNC_PTR(gdata->jvmti,SetThreadLocalStorage)
|
---|
170 | (gdata->jvmti, thread, (void*)node);
|
---|
171 | if ( error == JVMTI_ERROR_THREAD_NOT_ALIVE ) {
|
---|
172 | /* Just return, thread hasn't started yet */
|
---|
173 | return;
|
---|
174 | } else if ( error != JVMTI_ERROR_NONE ) {
|
---|
175 | /* The jthread object must be valid, so this must be a fatal error */
|
---|
176 | EXIT_ERROR(error, "cannot set thread local storage");
|
---|
177 | }
|
---|
178 | }
|
---|
179 |
|
---|
180 | /* Get TLS on a specific jthread, which is the ThreadNode* */
|
---|
181 | static ThreadNode *
|
---|
182 | getThreadLocalStorage(jthread thread)
|
---|
183 | {
|
---|
184 | jvmtiError error;
|
---|
185 | ThreadNode *node;
|
---|
186 |
|
---|
187 | node = NULL;
|
---|
188 | error = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadLocalStorage)
|
---|
189 | (gdata->jvmti, thread, (void**)&node);
|
---|
190 | if ( error == JVMTI_ERROR_THREAD_NOT_ALIVE ) {
|
---|
191 | /* Just return NULL, thread hasn't started yet */
|
---|
192 | return NULL;
|
---|
193 | } else if ( error != JVMTI_ERROR_NONE ) {
|
---|
194 | /* The jthread object must be valid, so this must be a fatal error */
|
---|
195 | EXIT_ERROR(error, "cannot get thread local storage");
|
---|
196 | }
|
---|
197 | return node;
|
---|
198 | }
|
---|
199 |
|
---|
200 | /* Search list for nodes that don't have TLS set and match this thread.
|
---|
201 | * It assumed that this logic is never dealing with terminated threads,
|
---|
202 | * since the ThreadEnd events always delete the ThreadNode while the
|
---|
203 | * jthread is still alive. So we can only look at the ThreadNode's that
|
---|
204 | * have never had their TLS set, making the search much faster.
|
---|
205 | * But keep in mind, this kind of search should rarely be needed.
|
---|
206 | */
|
---|
207 | static ThreadNode *
|
---|
208 | nonTlsSearch(JNIEnv *env, ThreadList *list, jthread thread)
|
---|
209 | {
|
---|
210 | ThreadNode *node;
|
---|
211 |
|
---|
212 | for (node = list->first; node != NULL; node = node->next) {
|
---|
213 | if (isSameObject(env, node->thread, thread)) {
|
---|
214 | break;
|
---|
215 | }
|
---|
216 | }
|
---|
217 | return node;
|
---|
218 | }
|
---|
219 |
|
---|
220 | /*
|
---|
221 | * These functions maintain the linked list of currently running threads.
|
---|
222 | * All assume that the threadLock is held before calling.
|
---|
223 | * If list==NULL, search both lists.
|
---|
224 | */
|
---|
225 | static ThreadNode *
|
---|
226 | findThread(ThreadList *list, jthread thread)
|
---|
227 | {
|
---|
228 | ThreadNode *node;
|
---|
229 |
|
---|
230 | /* Get thread local storage for quick thread -> node access */
|
---|
231 | node = getThreadLocalStorage(thread);
|
---|
232 |
|
---|
233 | /* In some rare cases we might get NULL, so we check the list manually for
|
---|
234 | * any threads that we could match.
|
---|
235 | */
|
---|
236 | if ( node == NULL ) {
|
---|
237 | JNIEnv *env;
|
---|
238 |
|
---|
239 | env = getEnv();
|
---|
240 | if ( list != NULL ) {
|
---|
241 | node = nonTlsSearch(env, list, thread);
|
---|
242 | } else {
|
---|
243 | node = nonTlsSearch(env, &runningThreads, thread);
|
---|
244 | if ( node == NULL ) {
|
---|
245 | node = nonTlsSearch(env, &otherThreads, thread);
|
---|
246 | }
|
---|
247 | }
|
---|
248 | if ( node != NULL ) {
|
---|
249 | /* Here we make another attempt to set TLS, it's ok if this fails */
|
---|
250 | setThreadLocalStorage(thread, (void*)node);
|
---|
251 | }
|
---|
252 | }
|
---|
253 |
|
---|
254 | /* If a list is supplied, only return ones in this list */
|
---|
255 | if ( node != NULL && list != NULL && node->list != list ) {
|
---|
256 | return NULL;
|
---|
257 | }
|
---|
258 | return node;
|
---|
259 | }
|
---|
260 |
|
---|
261 | /* Remove a ThreadNode from a ThreadList */
|
---|
262 | static void
|
---|
263 | removeNode(ThreadList *list, ThreadNode *node)
|
---|
264 | {
|
---|
265 | ThreadNode *prev;
|
---|
266 | ThreadNode *next;
|
---|
267 |
|
---|
268 | prev = node->prev;
|
---|
269 | next = node->next;
|
---|
270 | if ( prev != NULL ) {
|
---|
271 | prev->next = next;
|
---|
272 | }
|
---|
273 | if ( next != NULL ) {
|
---|
274 | next->prev = prev;
|
---|
275 | }
|
---|
276 | if ( prev == NULL ) {
|
---|
277 | list->first = next;
|
---|
278 | }
|
---|
279 | node->next = NULL;
|
---|
280 | node->prev = NULL;
|
---|
281 | node->list = NULL;
|
---|
282 | }
|
---|
283 |
|
---|
284 | /* Add a ThreadNode to a ThreadList */
|
---|
285 | static void
|
---|
286 | addNode(ThreadList *list, ThreadNode *node)
|
---|
287 | {
|
---|
288 | node->next = NULL;
|
---|
289 | node->prev = NULL;
|
---|
290 | node->list = NULL;
|
---|
291 | if ( list->first == NULL ) {
|
---|
292 | list->first = node;
|
---|
293 | } else {
|
---|
294 | list->first->prev = node;
|
---|
295 | node->next = list->first;
|
---|
296 | list->first = node;
|
---|
297 | }
|
---|
298 | node->list = list;
|
---|
299 | }
|
---|
300 |
|
---|
301 | static ThreadNode *
|
---|
302 | insertThread(JNIEnv *env, ThreadList *list, jthread thread)
|
---|
303 | {
|
---|
304 | ThreadNode *node;
|
---|
305 | struct bag *eventBag;
|
---|
306 |
|
---|
307 | node = findThread(list, thread);
|
---|
308 | if (node == NULL) {
|
---|
309 | node = jvmtiAllocate(sizeof(*node));
|
---|
310 | if (node == NULL) {
|
---|
311 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"thread table entry");
|
---|
312 | return NULL;
|
---|
313 | }
|
---|
314 | (void)memset(node, 0, sizeof(*node));
|
---|
315 | eventBag = eventHelper_createEventBag();
|
---|
316 | if (eventBag == NULL) {
|
---|
317 | jvmtiDeallocate(node);
|
---|
318 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"thread table entry");
|
---|
319 | return NULL;
|
---|
320 | }
|
---|
321 |
|
---|
322 | /*
|
---|
323 | * Init all flags false, all refs NULL, all counts 0
|
---|
324 | */
|
---|
325 |
|
---|
326 | saveGlobalRef(env, thread, &(node->thread));
|
---|
327 | if (node->thread == NULL) {
|
---|
328 | jvmtiDeallocate(node);
|
---|
329 | bagDestroyBag(eventBag);
|
---|
330 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"thread table entry");
|
---|
331 | return NULL;
|
---|
332 | }
|
---|
333 | /*
|
---|
334 | * Remember if it is a debug thread
|
---|
335 | */
|
---|
336 | if (threadControl_isDebugThread(node->thread)) {
|
---|
337 | node->isDebugThread = JNI_TRUE;
|
---|
338 | } else if (suspendAllCount > 0){
|
---|
339 | /*
|
---|
340 | * If there is a pending suspendAll, all new threads should
|
---|
341 | * be initialized as if they were suspended by the suspendAll,
|
---|
342 | * and the thread will need to be suspended when it starts.
|
---|
343 | */
|
---|
344 | node->suspendCount = suspendAllCount;
|
---|
345 | node->suspendOnStart = JNI_TRUE;
|
---|
346 | }
|
---|
347 | node->current_ei = 0;
|
---|
348 | node->instructionStepMode = JVMTI_DISABLE;
|
---|
349 | node->eventBag = eventBag;
|
---|
350 | addNode(list, node);
|
---|
351 |
|
---|
352 | /* Set thread local storage for quick thread -> node access.
|
---|
353 | * Some threads may not be in a state that allows setting of TLS,
|
---|
354 | * which is ok, see findThread, it deals with threads without TLS set.
|
---|
355 | */
|
---|
356 | setThreadLocalStorage(node->thread, (void*)node);
|
---|
357 | }
|
---|
358 |
|
---|
359 | return node;
|
---|
360 | }
|
---|
361 |
|
---|
362 | static void
|
---|
363 | clearThread(JNIEnv *env, ThreadNode *node)
|
---|
364 | {
|
---|
365 | if (node->pendingStop != NULL) {
|
---|
366 | tossGlobalRef(env, &(node->pendingStop));
|
---|
367 | }
|
---|
368 | stepControl_clearRequest(node->thread, &node->currentStep);
|
---|
369 | if (node->isDebugThread) {
|
---|
370 | (void)threadControl_removeDebugThread(node->thread);
|
---|
371 | }
|
---|
372 | /* Clear out TLS on this thread (just a cleanup action) */
|
---|
373 | setThreadLocalStorage(node->thread, NULL);
|
---|
374 | tossGlobalRef(env, &(node->thread));
|
---|
375 | bagDestroyBag(node->eventBag);
|
---|
376 | jvmtiDeallocate(node);
|
---|
377 | }
|
---|
378 |
|
---|
379 | static void
|
---|
380 | removeThread(JNIEnv *env, ThreadList *list, jthread thread)
|
---|
381 | {
|
---|
382 | ThreadNode *node;
|
---|
383 |
|
---|
384 | node = findThread(list, thread);
|
---|
385 | if (node != NULL) {
|
---|
386 | removeNode(list, node);
|
---|
387 | clearThread(env, node);
|
---|
388 | }
|
---|
389 | }
|
---|
390 |
|
---|
391 | static void
|
---|
392 | removeResumed(JNIEnv *env, ThreadList *list)
|
---|
393 | {
|
---|
394 | ThreadNode *node;
|
---|
395 |
|
---|
396 | node = list->first;
|
---|
397 | while (node != NULL) {
|
---|
398 | ThreadNode *temp = node->next;
|
---|
399 | if (node->suspendCount == 0) {
|
---|
400 | removeThread(env, list, node->thread);
|
---|
401 | }
|
---|
402 | node = temp;
|
---|
403 | }
|
---|
404 | }
|
---|
405 |
|
---|
406 | static void
|
---|
407 | moveNode(ThreadList *source, ThreadList *dest, ThreadNode *node)
|
---|
408 | {
|
---|
409 | removeNode(source, node);
|
---|
410 | JDI_ASSERT(findThread(dest, node->thread) == NULL);
|
---|
411 | addNode(dest, node);
|
---|
412 | }
|
---|
413 |
|
---|
414 | typedef jvmtiError (*ThreadEnumerateFunction)(JNIEnv *, ThreadNode *, void *);
|
---|
415 |
|
---|
416 | static jvmtiError
|
---|
417 | enumerateOverThreadList(JNIEnv *env, ThreadList *list,
|
---|
418 | ThreadEnumerateFunction function, void *arg)
|
---|
419 | {
|
---|
420 | ThreadNode *node;
|
---|
421 | jvmtiError error = JVMTI_ERROR_NONE;
|
---|
422 |
|
---|
423 | for (node = list->first; node != NULL; node = node->next) {
|
---|
424 | error = (*function)(env, node, arg);
|
---|
425 | if ( error != JVMTI_ERROR_NONE ) {
|
---|
426 | break;
|
---|
427 | }
|
---|
428 | }
|
---|
429 | return error;
|
---|
430 | }
|
---|
431 |
|
---|
432 | static void
|
---|
433 | insertEventMode(DeferredEventModeList *list, DeferredEventMode *eventMode)
|
---|
434 | {
|
---|
435 | if (list->last != NULL) {
|
---|
436 | list->last->next = eventMode;
|
---|
437 | } else {
|
---|
438 | list->first = eventMode;
|
---|
439 | }
|
---|
440 | list->last = eventMode;
|
---|
441 | }
|
---|
442 |
|
---|
443 | static void
|
---|
444 | removeEventMode(DeferredEventModeList *list, DeferredEventMode *eventMode, DeferredEventMode *prev)
|
---|
445 | {
|
---|
446 | if (prev == NULL) {
|
---|
447 | list->first = eventMode->next;
|
---|
448 | } else {
|
---|
449 | prev->next = eventMode->next;
|
---|
450 | }
|
---|
451 | if (eventMode->next == NULL) {
|
---|
452 | list->last = prev;
|
---|
453 | }
|
---|
454 | }
|
---|
455 |
|
---|
456 | static jvmtiError
|
---|
457 | addDeferredEventMode(JNIEnv *env, jvmtiEventMode mode, EventIndex ei, jthread thread)
|
---|
458 | {
|
---|
459 | DeferredEventMode *eventMode;
|
---|
460 |
|
---|
461 | /*LINTED*/
|
---|
462 | eventMode = jvmtiAllocate((jint)sizeof(DeferredEventMode));
|
---|
463 | if (eventMode == NULL) {
|
---|
464 | return AGENT_ERROR_OUT_OF_MEMORY;
|
---|
465 | }
|
---|
466 | eventMode->thread = NULL;
|
---|
467 | saveGlobalRef(env, thread, &(eventMode->thread));
|
---|
468 | eventMode->mode = mode;
|
---|
469 | eventMode->ei = ei;
|
---|
470 | eventMode->next = NULL;
|
---|
471 | insertEventMode(&deferredEventModes, eventMode);
|
---|
472 | return JVMTI_ERROR_NONE;
|
---|
473 | }
|
---|
474 |
|
---|
475 | static void
|
---|
476 | freeDeferredEventModes(JNIEnv *env)
|
---|
477 | {
|
---|
478 | DeferredEventMode *eventMode;
|
---|
479 | eventMode = deferredEventModes.first;
|
---|
480 | while (eventMode != NULL) {
|
---|
481 | DeferredEventMode *next;
|
---|
482 | next = eventMode->next;
|
---|
483 | tossGlobalRef(env, &(eventMode->thread));
|
---|
484 | jvmtiDeallocate(eventMode);
|
---|
485 | eventMode = next;
|
---|
486 | }
|
---|
487 | deferredEventModes.first = NULL;
|
---|
488 | deferredEventModes.last = NULL;
|
---|
489 | }
|
---|
490 |
|
---|
491 | static jvmtiError
|
---|
492 | threadSetEventNotificationMode(ThreadNode *node,
|
---|
493 | jvmtiEventMode mode, EventIndex ei, jthread thread)
|
---|
494 | {
|
---|
495 | jvmtiError error;
|
---|
496 |
|
---|
497 | /* record single step mode */
|
---|
498 | if (ei == EI_SINGLE_STEP) {
|
---|
499 | node->instructionStepMode = mode;
|
---|
500 | }
|
---|
501 | error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventNotificationMode)
|
---|
502 | (gdata->jvmti, mode, eventIndex2jvmti(ei), thread);
|
---|
503 | return error;
|
---|
504 | }
|
---|
505 |
|
---|
506 | static void
|
---|
507 | processDeferredEventModes(JNIEnv *env, jthread thread, ThreadNode *node)
|
---|
508 | {
|
---|
509 | jvmtiError error;
|
---|
510 | DeferredEventMode *eventMode;
|
---|
511 | DeferredEventMode *prev;
|
---|
512 |
|
---|
513 | prev = NULL;
|
---|
514 | eventMode = deferredEventModes.first;
|
---|
515 | while (eventMode != NULL) {
|
---|
516 | DeferredEventMode *next = eventMode->next;
|
---|
517 | if (isSameObject(env, thread, eventMode->thread)) {
|
---|
518 | error = threadSetEventNotificationMode(node,
|
---|
519 | eventMode->mode, eventMode->ei, eventMode->thread);
|
---|
520 | if (error != JVMTI_ERROR_NONE) {
|
---|
521 | EXIT_ERROR(error, "cannot process deferred thread event notifications at thread start");
|
---|
522 | }
|
---|
523 | removeEventMode(&deferredEventModes, eventMode, prev);
|
---|
524 | tossGlobalRef(env, &(eventMode->thread));
|
---|
525 | jvmtiDeallocate(eventMode);
|
---|
526 | } else {
|
---|
527 | prev = eventMode;
|
---|
528 | }
|
---|
529 | eventMode = next;
|
---|
530 | }
|
---|
531 | }
|
---|
532 |
|
---|
533 | static void
|
---|
534 | getLocks(void)
|
---|
535 | {
|
---|
536 | /*
|
---|
537 | * Anything which might be locked as part of the handling of
|
---|
538 | * a JVMTI event (which means: might be locked by an application
|
---|
539 | * thread) needs to be grabbed here. This allows thread control
|
---|
540 | * code to safely suspend and resume the application threads
|
---|
541 | * while ensuring they don't hold a critical lock.
|
---|
542 | */
|
---|
543 |
|
---|
544 | eventHandler_lock();
|
---|
545 | invoker_lock();
|
---|
546 | eventHelper_lock();
|
---|
547 | stepControl_lock();
|
---|
548 | commonRef_lock();
|
---|
549 | debugMonitorEnter(threadLock);
|
---|
550 |
|
---|
551 | }
|
---|
552 |
|
---|
553 | static void
|
---|
554 | releaseLocks(void)
|
---|
555 | {
|
---|
556 | debugMonitorExit(threadLock);
|
---|
557 | commonRef_unlock();
|
---|
558 | stepControl_unlock();
|
---|
559 | eventHelper_unlock();
|
---|
560 | invoker_unlock();
|
---|
561 | eventHandler_unlock();
|
---|
562 | }
|
---|
563 |
|
---|
564 | void
|
---|
565 | threadControl_initialize(void)
|
---|
566 | {
|
---|
567 | jlocation unused;
|
---|
568 | jvmtiError error;
|
---|
569 |
|
---|
570 | suspendAllCount = 0;
|
---|
571 | runningThreads.first = NULL;
|
---|
572 | otherThreads.first = NULL;
|
---|
573 | debugThreadCount = 0;
|
---|
574 | threadLock = debugMonitorCreate("JDWP Thread Lock");
|
---|
575 | if (gdata->threadClass==NULL) {
|
---|
576 | EXIT_ERROR(AGENT_ERROR_NULL_POINTER, "no java.lang.thread class");
|
---|
577 | }
|
---|
578 | if (gdata->threadResume==0) {
|
---|
579 | EXIT_ERROR(AGENT_ERROR_NULL_POINTER, "cannot resume thread");
|
---|
580 | }
|
---|
581 | /* Get the java.lang.Thread.resume() method beginning location */
|
---|
582 | error = methodLocation(gdata->threadResume, &resumeLocation, &unused);
|
---|
583 | if (error != JVMTI_ERROR_NONE) {
|
---|
584 | EXIT_ERROR(error, "getting method location");
|
---|
585 | }
|
---|
586 | }
|
---|
587 |
|
---|
588 | static jthread
|
---|
589 | getResumee(jthread resumingThread)
|
---|
590 | {
|
---|
591 | jthread resumee = NULL;
|
---|
592 | jvmtiError error;
|
---|
593 | jobject object;
|
---|
594 | FrameNumber fnum = 0;
|
---|
595 |
|
---|
596 | error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalObject)
|
---|
597 | (gdata->jvmti, resumingThread, fnum, 0, &object);
|
---|
598 | if (error == JVMTI_ERROR_NONE) {
|
---|
599 | resumee = object;
|
---|
600 | }
|
---|
601 | return resumee;
|
---|
602 | }
|
---|
603 |
|
---|
604 |
|
---|
605 | static jboolean
|
---|
606 | pendingAppResume(jboolean includeSuspended)
|
---|
607 | {
|
---|
608 | ThreadList *list;
|
---|
609 | ThreadNode *node;
|
---|
610 |
|
---|
611 | list = &runningThreads;
|
---|
612 | node = list->first;
|
---|
613 | while (node != NULL) {
|
---|
614 | if (node->resumeFrameDepth > 0) {
|
---|
615 | if (includeSuspended) {
|
---|
616 | return JNI_TRUE;
|
---|
617 | } else {
|
---|
618 | jvmtiError error;
|
---|
619 | jint state;
|
---|
620 |
|
---|
621 | error = threadState(node->thread, &state);
|
---|
622 | if (error != JVMTI_ERROR_NONE) {
|
---|
623 | EXIT_ERROR(error, "getting thread state");
|
---|
624 | }
|
---|
625 | if (!(state & JVMTI_THREAD_STATE_SUSPENDED)) {
|
---|
626 | return JNI_TRUE;
|
---|
627 | }
|
---|
628 | }
|
---|
629 | }
|
---|
630 | node = node->next;
|
---|
631 | }
|
---|
632 | return JNI_FALSE;
|
---|
633 | }
|
---|
634 |
|
---|
635 | static void
|
---|
636 | notifyAppResumeComplete(void)
|
---|
637 | {
|
---|
638 | debugMonitorNotifyAll(threadLock);
|
---|
639 | if (!pendingAppResume(JNI_TRUE)) {
|
---|
640 | if (framePopHandlerNode != NULL) {
|
---|
641 | (void)eventHandler_free(framePopHandlerNode);
|
---|
642 | framePopHandlerNode = NULL;
|
---|
643 | }
|
---|
644 | if (catchHandlerNode != NULL) {
|
---|
645 | (void)eventHandler_free(catchHandlerNode);
|
---|
646 | catchHandlerNode = NULL;
|
---|
647 | }
|
---|
648 | }
|
---|
649 | }
|
---|
650 |
|
---|
651 | static void
|
---|
652 | handleAppResumeCompletion(JNIEnv *env, EventInfo *evinfo,
|
---|
653 | HandlerNode *handlerNode,
|
---|
654 | struct bag *eventBag)
|
---|
655 | {
|
---|
656 | ThreadNode *node;
|
---|
657 | jthread thread;
|
---|
658 |
|
---|
659 | thread = evinfo->thread;
|
---|
660 |
|
---|
661 | debugMonitorEnter(threadLock);
|
---|
662 |
|
---|
663 | node = findThread(&runningThreads, thread);
|
---|
664 | if (node != NULL) {
|
---|
665 | if (node->resumeFrameDepth > 0) {
|
---|
666 | jint compareDepth = getStackDepth(thread);
|
---|
667 | if (evinfo->ei == EI_FRAME_POP) {
|
---|
668 | compareDepth--;
|
---|
669 | }
|
---|
670 | if (compareDepth < node->resumeFrameDepth) {
|
---|
671 | node->resumeFrameDepth = 0;
|
---|
672 | notifyAppResumeComplete();
|
---|
673 | }
|
---|
674 | }
|
---|
675 | }
|
---|
676 |
|
---|
677 | debugMonitorExit(threadLock);
|
---|
678 | }
|
---|
679 |
|
---|
680 | static void
|
---|
681 | blockOnDebuggerSuspend(jthread thread)
|
---|
682 | {
|
---|
683 | ThreadNode *node;
|
---|
684 |
|
---|
685 | node = findThread(NULL, thread);
|
---|
686 | if (node != NULL) {
|
---|
687 | while (node && node->suspendCount > 0) {
|
---|
688 | debugMonitorWait(threadLock);
|
---|
689 | node = findThread(NULL, thread);
|
---|
690 | }
|
---|
691 | }
|
---|
692 | }
|
---|
693 |
|
---|
694 | static void
|
---|
695 | trackAppResume(jthread thread)
|
---|
696 | {
|
---|
697 | jvmtiError error;
|
---|
698 | FrameNumber fnum;
|
---|
699 | ThreadNode *node;
|
---|
700 |
|
---|
701 | fnum = 0;
|
---|
702 | node = findThread(&runningThreads, thread);
|
---|
703 | if (node != NULL) {
|
---|
704 | JDI_ASSERT(node->resumeFrameDepth == 0);
|
---|
705 | error = JVMTI_FUNC_PTR(gdata->jvmti,NotifyFramePop)
|
---|
706 | (gdata->jvmti, thread, fnum);
|
---|
707 | if (error == JVMTI_ERROR_NONE) {
|
---|
708 | jint frameDepth = getStackDepth(thread);
|
---|
709 | if ((frameDepth > 0) && (framePopHandlerNode == NULL)) {
|
---|
710 | framePopHandlerNode = eventHandler_createInternalThreadOnly(
|
---|
711 | EI_FRAME_POP,
|
---|
712 | handleAppResumeCompletion,
|
---|
713 | thread);
|
---|
714 | catchHandlerNode = eventHandler_createInternalThreadOnly(
|
---|
715 | EI_EXCEPTION_CATCH,
|
---|
716 | handleAppResumeCompletion,
|
---|
717 | thread);
|
---|
718 | if ((framePopHandlerNode == NULL) ||
|
---|
719 | (catchHandlerNode == NULL)) {
|
---|
720 | (void)eventHandler_free(framePopHandlerNode);
|
---|
721 | framePopHandlerNode = NULL;
|
---|
722 | (void)eventHandler_free(catchHandlerNode);
|
---|
723 | catchHandlerNode = NULL;
|
---|
724 | }
|
---|
725 | }
|
---|
726 | if ((framePopHandlerNode != NULL) &&
|
---|
727 | (catchHandlerNode != NULL) &&
|
---|
728 | (frameDepth > 0)) {
|
---|
729 | node->resumeFrameDepth = frameDepth;
|
---|
730 | }
|
---|
731 | }
|
---|
732 | }
|
---|
733 | }
|
---|
734 |
|
---|
735 | static void
|
---|
736 | handleAppResumeBreakpoint(JNIEnv *env, EventInfo *evinfo,
|
---|
737 | HandlerNode *handlerNode,
|
---|
738 | struct bag *eventBag)
|
---|
739 | {
|
---|
740 | jthread resumer = evinfo->thread;
|
---|
741 | jthread resumee = getResumee(resumer);
|
---|
742 |
|
---|
743 | debugMonitorEnter(threadLock);
|
---|
744 | if (resumee != NULL) {
|
---|
745 | /*
|
---|
746 | * Hold up any attempt to resume as long as the debugger
|
---|
747 | * has suspended the resumee.
|
---|
748 | */
|
---|
749 | blockOnDebuggerSuspend(resumee);
|
---|
750 | }
|
---|
751 |
|
---|
752 | if (resumer != NULL) {
|
---|
753 | /*
|
---|
754 | * Track the resuming thread by marking it as being within
|
---|
755 | * a resume and by setting up for notification on
|
---|
756 | * a frame pop or exception. We won't allow the debugger
|
---|
757 | * to suspend threads while any thread is within a
|
---|
758 | * call to resume. This (along with the block above)
|
---|
759 | * ensures that when the debugger
|
---|
760 | * suspends a thread it will remain suspended.
|
---|
761 | */
|
---|
762 | trackAppResume(resumer);
|
---|
763 | }
|
---|
764 |
|
---|
765 | debugMonitorExit(threadLock);
|
---|
766 | }
|
---|
767 |
|
---|
768 | void
|
---|
769 | threadControl_onConnect(void)
|
---|
770 | {
|
---|
771 | breakpointHandlerNode = eventHandler_createInternalBreakpoint(
|
---|
772 | handleAppResumeBreakpoint, NULL,
|
---|
773 | gdata->threadClass, gdata->threadResume, resumeLocation);
|
---|
774 | }
|
---|
775 |
|
---|
776 | void
|
---|
777 | threadControl_onDisconnect(void)
|
---|
778 | {
|
---|
779 | if (breakpointHandlerNode != NULL) {
|
---|
780 | (void)eventHandler_free(breakpointHandlerNode);
|
---|
781 | breakpointHandlerNode = NULL;
|
---|
782 | }
|
---|
783 | if (framePopHandlerNode != NULL) {
|
---|
784 | (void)eventHandler_free(framePopHandlerNode);
|
---|
785 | framePopHandlerNode = NULL;
|
---|
786 | }
|
---|
787 | if (catchHandlerNode != NULL) {
|
---|
788 | (void)eventHandler_free(catchHandlerNode);
|
---|
789 | catchHandlerNode = NULL;
|
---|
790 | }
|
---|
791 | }
|
---|
792 |
|
---|
793 | void
|
---|
794 | threadControl_onHook(void)
|
---|
795 | {
|
---|
796 | /*
|
---|
797 | * As soon as the event hook is in place, we need to initialize
|
---|
798 | * the thread list with already-existing threads. The threadLock
|
---|
799 | * has been held since initialize, so we don't need to worry about
|
---|
800 | * insertions or deletions from the event handlers while we do this
|
---|
801 | */
|
---|
802 | JNIEnv *env;
|
---|
803 |
|
---|
804 | env = getEnv();
|
---|
805 |
|
---|
806 | /*
|
---|
807 | * Prevent any event processing until OnHook has been called
|
---|
808 | */
|
---|
809 | debugMonitorEnter(threadLock);
|
---|
810 |
|
---|
811 | WITH_LOCAL_REFS(env, 1) {
|
---|
812 |
|
---|
813 | jint threadCount;
|
---|
814 | jthread *threads;
|
---|
815 |
|
---|
816 | threads = allThreads(&threadCount);
|
---|
817 | if (threads == NULL) {
|
---|
818 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"thread table");
|
---|
819 | } else {
|
---|
820 |
|
---|
821 | int i;
|
---|
822 |
|
---|
823 | for (i = 0; i < threadCount; i++) {
|
---|
824 | ThreadNode *node;
|
---|
825 | jthread thread = threads[i];
|
---|
826 | node = insertThread(env, &runningThreads, thread);
|
---|
827 |
|
---|
828 | /*
|
---|
829 | * This is a tiny bit risky. We have to assume that the
|
---|
830 | * pre-existing threads have been started because we
|
---|
831 | * can't rely on a thread start event for them. The chances
|
---|
832 | * of a problem related to this are pretty slim though, and
|
---|
833 | * there's really no choice because without setting this flag
|
---|
834 | * there is no way to enable stepping and other events on
|
---|
835 | * the threads that already exist (e.g. the finalizer thread).
|
---|
836 | */
|
---|
837 | node->isStarted = JNI_TRUE;
|
---|
838 | }
|
---|
839 | }
|
---|
840 |
|
---|
841 | } END_WITH_LOCAL_REFS(env)
|
---|
842 |
|
---|
843 | debugMonitorExit(threadLock);
|
---|
844 | }
|
---|
845 |
|
---|
846 | static jvmtiError
|
---|
847 | commonSuspendByNode(ThreadNode *node)
|
---|
848 | {
|
---|
849 | jvmtiError error;
|
---|
850 |
|
---|
851 | LOG_MISC(("thread=%p suspended", node->thread));
|
---|
852 | error = JVMTI_FUNC_PTR(gdata->jvmti,SuspendThread)
|
---|
853 | (gdata->jvmti, node->thread);
|
---|
854 |
|
---|
855 | /*
|
---|
856 | * Mark for resume only if suspend succeeded
|
---|
857 | */
|
---|
858 | if (error == JVMTI_ERROR_NONE) {
|
---|
859 | node->toBeResumed = JNI_TRUE;
|
---|
860 | }
|
---|
861 |
|
---|
862 | /*
|
---|
863 | * If the thread was suspended by another app thread,
|
---|
864 | * do nothing and report no error (we won't resume it later).
|
---|
865 | */
|
---|
866 | if (error == JVMTI_ERROR_THREAD_SUSPENDED) {
|
---|
867 | error = JVMTI_ERROR_NONE;
|
---|
868 | }
|
---|
869 |
|
---|
870 | return error;
|
---|
871 | }
|
---|
872 |
|
---|
873 | /*
|
---|
874 | * Deferred suspends happen when the suspend is attempted on a thread
|
---|
875 | * that is not started. Bookkeeping (suspendCount,etc.)
|
---|
876 | * is handled by the original request, and once the thread actually
|
---|
877 | * starts, an actual suspend is attempted. This function does the
|
---|
878 | * deferred suspend without changing the bookkeeping that is already
|
---|
879 | * in place.
|
---|
880 | */
|
---|
881 | static jint
|
---|
882 | deferredSuspendThreadByNode(ThreadNode *node)
|
---|
883 | {
|
---|
884 | jvmtiError error;
|
---|
885 |
|
---|
886 | error = JVMTI_ERROR_NONE;
|
---|
887 | if (node->isDebugThread) {
|
---|
888 | /* Ignore requests for suspending debugger threads */
|
---|
889 | return JVMTI_ERROR_NONE;
|
---|
890 | }
|
---|
891 |
|
---|
892 | /*
|
---|
893 | * Do the actual suspend only if a subsequent resume hasn't
|
---|
894 | * made it irrelevant.
|
---|
895 | */
|
---|
896 | if (node->suspendCount > 0) {
|
---|
897 | error = commonSuspendByNode(node);
|
---|
898 |
|
---|
899 | /*
|
---|
900 | * Attempt to clean up from any error by decrementing the
|
---|
901 | * suspend count. This compensates for the increment that
|
---|
902 | * happens when suspendOnStart is set to true.
|
---|
903 | */
|
---|
904 | if (error != JVMTI_ERROR_NONE) {
|
---|
905 | node->suspendCount--;
|
---|
906 | }
|
---|
907 | }
|
---|
908 |
|
---|
909 | node->suspendOnStart = JNI_FALSE;
|
---|
910 |
|
---|
911 | debugMonitorNotifyAll(threadLock);
|
---|
912 |
|
---|
913 | return error;
|
---|
914 | }
|
---|
915 |
|
---|
916 | static jvmtiError
|
---|
917 | suspendThreadByNode(ThreadNode *node)
|
---|
918 | {
|
---|
919 | jvmtiError error = JVMTI_ERROR_NONE;
|
---|
920 | if (node->isDebugThread) {
|
---|
921 | /* Ignore requests for suspending debugger threads */
|
---|
922 | return JVMTI_ERROR_NONE;
|
---|
923 | }
|
---|
924 |
|
---|
925 | /*
|
---|
926 | * Just increment the suspend count if we are waiting
|
---|
927 | * for a deferred suspend.
|
---|
928 | */
|
---|
929 | if (node->suspendOnStart) {
|
---|
930 | node->suspendCount++;
|
---|
931 | return JVMTI_ERROR_NONE;
|
---|
932 | }
|
---|
933 |
|
---|
934 | if (node->suspendCount == 0) {
|
---|
935 | error = commonSuspendByNode(node);
|
---|
936 |
|
---|
937 | if (error == JVMTI_ERROR_THREAD_NOT_ALIVE) {
|
---|
938 | /*
|
---|
939 | * This error means that the thread is either a zombie or not yet
|
---|
940 | * started. In either case, we ignore the error. If the thread
|
---|
941 | * is a zombie, suspend/resume are no-ops. If the thread is not
|
---|
942 | * started, it will be suspended for real during the processing
|
---|
943 | * of its thread start event.
|
---|
944 | */
|
---|
945 | node->suspendOnStart = JNI_TRUE;
|
---|
946 | error = JVMTI_ERROR_NONE;
|
---|
947 | }
|
---|
948 | }
|
---|
949 |
|
---|
950 | if (error == JVMTI_ERROR_NONE) {
|
---|
951 | node->suspendCount++;
|
---|
952 | }
|
---|
953 |
|
---|
954 | debugMonitorNotifyAll(threadLock);
|
---|
955 |
|
---|
956 | return error;
|
---|
957 | }
|
---|
958 |
|
---|
959 | static jvmtiError
|
---|
960 | resumeThreadByNode(ThreadNode *node)
|
---|
961 | {
|
---|
962 | jvmtiError error = JVMTI_ERROR_NONE;
|
---|
963 |
|
---|
964 | if (node->isDebugThread) {
|
---|
965 | /* never suspended by debugger => don't ever try to resume */
|
---|
966 | return JVMTI_ERROR_NONE;
|
---|
967 | }
|
---|
968 | if (node->suspendCount > 0) {
|
---|
969 | node->suspendCount--;
|
---|
970 | debugMonitorNotifyAll(threadLock);
|
---|
971 | if ((node->suspendCount == 0) && node->toBeResumed &&
|
---|
972 | !node->suspendOnStart) {
|
---|
973 | LOG_MISC(("thread=%p resumed", node->thread));
|
---|
974 | error = JVMTI_FUNC_PTR(gdata->jvmti,ResumeThread)
|
---|
975 | (gdata->jvmti, node->thread);
|
---|
976 | node->frameGeneration++; /* Increment on each resume */
|
---|
977 | node->toBeResumed = JNI_FALSE;
|
---|
978 | if (error == JVMTI_ERROR_THREAD_NOT_ALIVE && !node->isStarted) {
|
---|
979 | /*
|
---|
980 | * We successfully "suspended" this thread, but
|
---|
981 | * we never received a THREAD_START event for it.
|
---|
982 | * Since the thread never ran, we can ignore our
|
---|
983 | * failure to resume the thread.
|
---|
984 | */
|
---|
985 | error = JVMTI_ERROR_NONE;
|
---|
986 | }
|
---|
987 | }
|
---|
988 | }
|
---|
989 |
|
---|
990 | return error;
|
---|
991 | }
|
---|
992 |
|
---|
993 | /*
|
---|
994 | * Functions which respond to user requests to suspend/resume
|
---|
995 | * threads.
|
---|
996 | * Suspends and resumes add and subtract from a count respectively.
|
---|
997 | * The thread is only suspended when the count goes from 0 to 1 and
|
---|
998 | * resumed only when the count goes from 1 to 0.
|
---|
999 | *
|
---|
1000 | * These functions suspend and resume application threads
|
---|
1001 | * without changing the
|
---|
1002 | * state of threads that were already suspended beforehand.
|
---|
1003 | * They must not be called from an application thread because
|
---|
1004 | * that thread may be suspended somewhere in the middle of things.
|
---|
1005 | */
|
---|
1006 | static void
|
---|
1007 | preSuspend(void)
|
---|
1008 | {
|
---|
1009 | getLocks(); /* Avoid debugger deadlocks */
|
---|
1010 |
|
---|
1011 | /*
|
---|
1012 | * Delay any suspend while a call to java.lang.Thread.resume is in
|
---|
1013 | * progress (not including those in suspended threads). The wait is
|
---|
1014 | * timed because the threads suspended through
|
---|
1015 | * java.lang.Thread.suspend won't result in a notify even though
|
---|
1016 | * it may change the result of pendingAppResume()
|
---|
1017 | */
|
---|
1018 | while (pendingAppResume(JNI_FALSE)) {
|
---|
1019 | /*
|
---|
1020 | * This is ugly but we need to release the locks from getLocks
|
---|
1021 | * or else the notify will never happen. The locks must be
|
---|
1022 | * released and reacquired in the right order. else deadlocks
|
---|
1023 | * can happen. It is possible that, during this dance, the
|
---|
1024 | * notify will be missed, but since the wait needs to be timed
|
---|
1025 | * anyway, it won't be a disaster. Note that this code will
|
---|
1026 | * execute only on very rare occasions anyway.
|
---|
1027 | */
|
---|
1028 | releaseLocks();
|
---|
1029 |
|
---|
1030 | debugMonitorEnter(threadLock);
|
---|
1031 | debugMonitorTimedWait(threadLock, 1000);
|
---|
1032 | debugMonitorExit(threadLock);
|
---|
1033 |
|
---|
1034 | getLocks();
|
---|
1035 | }
|
---|
1036 | }
|
---|
1037 |
|
---|
1038 | static void
|
---|
1039 | postSuspend(void)
|
---|
1040 | {
|
---|
1041 | releaseLocks();
|
---|
1042 | }
|
---|
1043 |
|
---|
1044 | /*
|
---|
1045 | * This function must be called after preSuspend and before postSuspend.
|
---|
1046 | */
|
---|
1047 | static jvmtiError
|
---|
1048 | commonSuspend(JNIEnv *env, jthread thread, jboolean deferred)
|
---|
1049 | {
|
---|
1050 | ThreadNode *node;
|
---|
1051 |
|
---|
1052 | /*
|
---|
1053 | * If the thread is not between its start and end events, we should
|
---|
1054 | * still suspend it. To keep track of things, add the thread
|
---|
1055 | * to a separate list of threads so that we'll resume it later.
|
---|
1056 | */
|
---|
1057 | node = findThread(&runningThreads, thread);
|
---|
1058 | if (node == NULL) {
|
---|
1059 | node = insertThread(env, &otherThreads, thread);
|
---|
1060 | }
|
---|
1061 |
|
---|
1062 | if ( deferred ) {
|
---|
1063 | return deferredSuspendThreadByNode(node);
|
---|
1064 | } else {
|
---|
1065 | return suspendThreadByNode(node);
|
---|
1066 | }
|
---|
1067 | }
|
---|
1068 |
|
---|
1069 |
|
---|
1070 | static jvmtiError
|
---|
1071 | resumeCopyHelper(JNIEnv *env, ThreadNode *node, void *arg)
|
---|
1072 | {
|
---|
1073 | if (node->isDebugThread) {
|
---|
1074 | /* never suspended by debugger => don't ever try to resume */
|
---|
1075 | return JVMTI_ERROR_NONE;
|
---|
1076 | }
|
---|
1077 |
|
---|
1078 | if (node->suspendCount > 1) {
|
---|
1079 | node->suspendCount--;
|
---|
1080 | /* nested suspend so just undo one level */
|
---|
1081 | return JVMTI_ERROR_NONE;
|
---|
1082 | }
|
---|
1083 |
|
---|
1084 | /*
|
---|
1085 | * This thread was marked for suspension since its THREAD_START
|
---|
1086 | * event came in during a suspendAll, but the helper hasn't
|
---|
1087 | * completed the job yet. We decrement the count so the helper
|
---|
1088 | * won't suspend this thread after we are done with the resumeAll.
|
---|
1089 | * Another case to be handled here is when the debugger suspends
|
---|
1090 | * the thread while the app has it suspended. In this case,
|
---|
1091 | * the toBeResumed flag has been cleared indicating that
|
---|
1092 | * the thread should not be resumed when the debugger does a resume.
|
---|
1093 | * In this case, we also have to decrement the suspend count.
|
---|
1094 | * If we don't then when the app resumes the thread and our Thread.resume
|
---|
1095 | * bkpt handler is called, blockOnDebuggerSuspend will not resume
|
---|
1096 | * the thread because suspendCount will be 1 meaning that the
|
---|
1097 | * debugger has the thread suspended. See bug 6224859.
|
---|
1098 | */
|
---|
1099 | if (node->suspendCount == 1 && (!node->toBeResumed || node->suspendOnStart)) {
|
---|
1100 | node->suspendCount--;
|
---|
1101 | return JVMTI_ERROR_NONE;
|
---|
1102 | }
|
---|
1103 |
|
---|
1104 | if (arg == NULL) {
|
---|
1105 | /* nothing to hard resume so we're done */
|
---|
1106 | return JVMTI_ERROR_NONE;
|
---|
1107 | }
|
---|
1108 |
|
---|
1109 | /*
|
---|
1110 | * This is tricky. A suspendCount of 1 and toBeResumed means that
|
---|
1111 | * JVM/DI SuspendThread() or JVM/DI SuspendThreadList() was called
|
---|
1112 | * on this thread. The check for !suspendOnStart is paranoia that
|
---|
1113 | * we inherited from resumeThreadByNode().
|
---|
1114 | */
|
---|
1115 | if (node->suspendCount == 1 && node->toBeResumed && !node->suspendOnStart) {
|
---|
1116 | jthread **listPtr = (jthread **)arg;
|
---|
1117 |
|
---|
1118 | **listPtr = node->thread;
|
---|
1119 | (*listPtr)++;
|
---|
1120 | }
|
---|
1121 | return JVMTI_ERROR_NONE;
|
---|
1122 | }
|
---|
1123 |
|
---|
1124 |
|
---|
1125 | static jvmtiError
|
---|
1126 | resumeCountHelper(JNIEnv *env, ThreadNode *node, void *arg)
|
---|
1127 | {
|
---|
1128 | if (node->isDebugThread) {
|
---|
1129 | /* never suspended by debugger => don't ever try to resume */
|
---|
1130 | return JVMTI_ERROR_NONE;
|
---|
1131 | }
|
---|
1132 |
|
---|
1133 | /*
|
---|
1134 | * This is tricky. A suspendCount of 1 and toBeResumed means that
|
---|
1135 | * JVM/DI SuspendThread() or JVM/DI SuspendThreadList() was called
|
---|
1136 | * on this thread. The check for !suspendOnStart is paranoia that
|
---|
1137 | * we inherited from resumeThreadByNode().
|
---|
1138 | */
|
---|
1139 | if (node->suspendCount == 1 && node->toBeResumed && !node->suspendOnStart) {
|
---|
1140 | jint *counter = (jint *)arg;
|
---|
1141 |
|
---|
1142 | (*counter)++;
|
---|
1143 | }
|
---|
1144 | return JVMTI_ERROR_NONE;
|
---|
1145 | }
|
---|
1146 |
|
---|
1147 | static void *
|
---|
1148 | newArray(jint length, size_t nbytes)
|
---|
1149 | {
|
---|
1150 | void *ptr;
|
---|
1151 | ptr = jvmtiAllocate(length*(jint)nbytes);
|
---|
1152 | if ( ptr != NULL ) {
|
---|
1153 | (void)memset(ptr, 0, length*nbytes);
|
---|
1154 | }
|
---|
1155 | return ptr;
|
---|
1156 | }
|
---|
1157 |
|
---|
1158 | static void
|
---|
1159 | deleteArray(void *ptr)
|
---|
1160 | {
|
---|
1161 | jvmtiDeallocate(ptr);
|
---|
1162 | }
|
---|
1163 |
|
---|
1164 | /*
|
---|
1165 | * This function must be called with the threadLock held.
|
---|
1166 | *
|
---|
1167 | * Two facts conspire to make this routine complicated:
|
---|
1168 | *
|
---|
1169 | * 1) the VM doesn't support nested external suspend
|
---|
1170 | * 2) the original resumeAll code structure doesn't retrieve the
|
---|
1171 | * entire thread list from JVMTI so we use the runningThreads
|
---|
1172 | * list and two helpers to get the job done.
|
---|
1173 | *
|
---|
1174 | * Because we hold the threadLock, state seen by resumeCountHelper()
|
---|
1175 | * is the same state seen in resumeCopyHelper(). resumeCountHelper()
|
---|
1176 | * just counts up the number of threads to be hard resumed.
|
---|
1177 | * resumeCopyHelper() does the accounting for nested suspends and
|
---|
1178 | * special cases and, finally, populates the list of hard resume
|
---|
1179 | * threads to be passed to ResumeThreadList().
|
---|
1180 | *
|
---|
1181 | * At first glance, you might think that the accounting could be done
|
---|
1182 | * in resumeCountHelper(), but then resumeCopyHelper() would see
|
---|
1183 | * "post-resume" state in the accounting values (suspendCount and
|
---|
1184 | * toBeResumed) and would not be able to distinguish between a thread
|
---|
1185 | * that needs a hard resume versus a thread that is already running.
|
---|
1186 | */
|
---|
1187 | static jvmtiError
|
---|
1188 | commonResumeList(JNIEnv *env)
|
---|
1189 | {
|
---|
1190 | jvmtiError error;
|
---|
1191 | jint i;
|
---|
1192 | jint reqCnt;
|
---|
1193 | jthread *reqList;
|
---|
1194 | jthread *reqPtr;
|
---|
1195 | jvmtiError *results;
|
---|
1196 |
|
---|
1197 | reqCnt = 0;
|
---|
1198 |
|
---|
1199 | /* count number of threads to hard resume */
|
---|
1200 | (void) enumerateOverThreadList(env, &runningThreads, resumeCountHelper,
|
---|
1201 | &reqCnt);
|
---|
1202 | if (reqCnt == 0) {
|
---|
1203 | /* nothing to hard resume so do just the accounting part */
|
---|
1204 | (void) enumerateOverThreadList(env, &runningThreads, resumeCopyHelper,
|
---|
1205 | NULL);
|
---|
1206 | return JVMTI_ERROR_NONE;
|
---|
1207 | }
|
---|
1208 |
|
---|
1209 | /*LINTED*/
|
---|
1210 | reqList = newArray(reqCnt, sizeof(jthread));
|
---|
1211 | if (reqList == NULL) {
|
---|
1212 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"resume request list");
|
---|
1213 | }
|
---|
1214 | /*LINTED*/
|
---|
1215 | results = newArray(reqCnt, sizeof(jvmtiError));
|
---|
1216 | if (results == NULL) {
|
---|
1217 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"resume list");
|
---|
1218 | }
|
---|
1219 |
|
---|
1220 | /* copy the jthread values for threads to hard resume */
|
---|
1221 | reqPtr = reqList;
|
---|
1222 | (void) enumerateOverThreadList(env, &runningThreads, resumeCopyHelper,
|
---|
1223 | &reqPtr);
|
---|
1224 |
|
---|
1225 | error = JVMTI_FUNC_PTR(gdata->jvmti,ResumeThreadList)
|
---|
1226 | (gdata->jvmti, reqCnt, reqList, results);
|
---|
1227 | for (i = 0; i < reqCnt; i++) {
|
---|
1228 | ThreadNode *node;
|
---|
1229 |
|
---|
1230 | node = findThread(&runningThreads, reqList[i]);
|
---|
1231 | if (node == NULL) {
|
---|
1232 | EXIT_ERROR(AGENT_ERROR_INVALID_THREAD,"missing entry in running thread table");
|
---|
1233 | }
|
---|
1234 | LOG_MISC(("thread=%p resumed as part of list", node->thread));
|
---|
1235 |
|
---|
1236 | /*
|
---|
1237 | * resumeThreadByNode() assumes that JVM/DI ResumeThread()
|
---|
1238 | * always works and does all the accounting updates. We do
|
---|
1239 | * the same here. We also don't clear the error.
|
---|
1240 | */
|
---|
1241 | node->suspendCount--;
|
---|
1242 | node->toBeResumed = JNI_FALSE;
|
---|
1243 | node->frameGeneration++; /* Increment on each resume */
|
---|
1244 | }
|
---|
1245 | deleteArray(results);
|
---|
1246 | deleteArray(reqList);
|
---|
1247 |
|
---|
1248 | debugMonitorNotifyAll(threadLock);
|
---|
1249 |
|
---|
1250 | return error;
|
---|
1251 | }
|
---|
1252 |
|
---|
1253 |
|
---|
1254 | /*
|
---|
1255 | * This function must be called after preSuspend and before postSuspend.
|
---|
1256 | */
|
---|
1257 | static jvmtiError
|
---|
1258 | commonSuspendList(JNIEnv *env, jint initCount, jthread *initList)
|
---|
1259 | {
|
---|
1260 | jvmtiError error;
|
---|
1261 | jint i;
|
---|
1262 | jint reqCnt;
|
---|
1263 | jthread *reqList;
|
---|
1264 |
|
---|
1265 | error = JVMTI_ERROR_NONE;
|
---|
1266 | reqCnt = 0;
|
---|
1267 | reqList = newArray(initCount, sizeof(jthread));
|
---|
1268 | if (reqList == NULL) {
|
---|
1269 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"request list");
|
---|
1270 | }
|
---|
1271 |
|
---|
1272 | /*
|
---|
1273 | * Go through the initial list and see if we have anything to suspend.
|
---|
1274 | */
|
---|
1275 | for (i = 0; i < initCount; i++) {
|
---|
1276 | ThreadNode *node;
|
---|
1277 |
|
---|
1278 | /*
|
---|
1279 | * If the thread is not between its start and end events, we should
|
---|
1280 | * still suspend it. To keep track of things, add the thread
|
---|
1281 | * to a separate list of threads so that we'll resume it later.
|
---|
1282 | */
|
---|
1283 | node = findThread(&runningThreads, initList[i]);
|
---|
1284 | if (node == NULL) {
|
---|
1285 | node = insertThread(env, &otherThreads, initList[i]);
|
---|
1286 | }
|
---|
1287 |
|
---|
1288 | if (node->isDebugThread) {
|
---|
1289 | /* Ignore requests for suspending debugger threads */
|
---|
1290 | continue;
|
---|
1291 | }
|
---|
1292 |
|
---|
1293 | /*
|
---|
1294 | * Just increment the suspend count if we are waiting
|
---|
1295 | * for a deferred suspend or if this is a nested suspend.
|
---|
1296 | */
|
---|
1297 | if (node->suspendOnStart || node->suspendCount > 0) {
|
---|
1298 | node->suspendCount++;
|
---|
1299 | continue;
|
---|
1300 | }
|
---|
1301 |
|
---|
1302 | if (node->suspendCount == 0) {
|
---|
1303 | /* thread is not suspended yet so put it on the request list */
|
---|
1304 | reqList[reqCnt++] = initList[i];
|
---|
1305 | }
|
---|
1306 | }
|
---|
1307 |
|
---|
1308 | if (reqCnt > 0) {
|
---|
1309 | jvmtiError *results = newArray(reqCnt, sizeof(jvmtiError));
|
---|
1310 |
|
---|
1311 | if (results == NULL) {
|
---|
1312 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"suspend list results");
|
---|
1313 | }
|
---|
1314 |
|
---|
1315 | /*
|
---|
1316 | * We have something to suspend so try to do it.
|
---|
1317 | */
|
---|
1318 | error = JVMTI_FUNC_PTR(gdata->jvmti,SuspendThreadList)
|
---|
1319 | (gdata->jvmti, reqCnt, reqList, results);
|
---|
1320 | for (i = 0; i < reqCnt; i++) {
|
---|
1321 | ThreadNode *node;
|
---|
1322 |
|
---|
1323 | node = findThread(NULL, reqList[i]);
|
---|
1324 | if (node == NULL) {
|
---|
1325 | EXIT_ERROR(AGENT_ERROR_INVALID_THREAD,"missing entry in thread tables");
|
---|
1326 | }
|
---|
1327 | LOG_MISC(("thread=%p suspended as part of list", node->thread));
|
---|
1328 |
|
---|
1329 | if (results[i] == JVMTI_ERROR_NONE) {
|
---|
1330 | /* thread was suspended as requested */
|
---|
1331 | node->toBeResumed = JNI_TRUE;
|
---|
1332 | } else if (results[i] == JVMTI_ERROR_THREAD_SUSPENDED) {
|
---|
1333 | /*
|
---|
1334 | * If the thread was suspended by another app thread,
|
---|
1335 | * do nothing and report no error (we won't resume it later).
|
---|
1336 | */
|
---|
1337 | results[i] = JVMTI_ERROR_NONE;
|
---|
1338 | } else if (results[i] == JVMTI_ERROR_THREAD_NOT_ALIVE) {
|
---|
1339 | /*
|
---|
1340 | * This error means that the suspend request failed
|
---|
1341 | * because the thread is either a zombie or not yet
|
---|
1342 | * started. In either case, we ignore the error. If the
|
---|
1343 | * thread is a zombie, suspend/resume are no-ops. If the
|
---|
1344 | * thread is not started, it will be suspended for real
|
---|
1345 | * during the processing of its thread start event.
|
---|
1346 | */
|
---|
1347 | node->suspendOnStart = JNI_TRUE;
|
---|
1348 | results[i] = JVMTI_ERROR_NONE;
|
---|
1349 | }
|
---|
1350 |
|
---|
1351 | /* count real, app and deferred (suspendOnStart) suspensions */
|
---|
1352 | if (results[i] == JVMTI_ERROR_NONE) {
|
---|
1353 | node->suspendCount++;
|
---|
1354 | }
|
---|
1355 | }
|
---|
1356 | deleteArray(results);
|
---|
1357 | }
|
---|
1358 | deleteArray(reqList);
|
---|
1359 |
|
---|
1360 | debugMonitorNotifyAll(threadLock);
|
---|
1361 |
|
---|
1362 | return error;
|
---|
1363 | }
|
---|
1364 |
|
---|
1365 |
|
---|
1366 | static jvmtiError
|
---|
1367 | commonResume(jthread thread)
|
---|
1368 | {
|
---|
1369 | jvmtiError error;
|
---|
1370 | ThreadNode *node;
|
---|
1371 |
|
---|
1372 | /*
|
---|
1373 | * The thread is normally between its start and end events, but if
|
---|
1374 | * not, check the auxiliary list used by threadControl_suspendThread.
|
---|
1375 | */
|
---|
1376 | node = findThread(NULL, thread);
|
---|
1377 |
|
---|
1378 | /*
|
---|
1379 | * If the node is in neither list, the debugger never suspended
|
---|
1380 | * this thread, so do nothing.
|
---|
1381 | */
|
---|
1382 | error = JVMTI_ERROR_NONE;
|
---|
1383 | if (node != NULL) {
|
---|
1384 | error = resumeThreadByNode(node);
|
---|
1385 | }
|
---|
1386 | return error;
|
---|
1387 | }
|
---|
1388 |
|
---|
1389 |
|
---|
1390 | jvmtiError
|
---|
1391 | threadControl_suspendThread(jthread thread, jboolean deferred)
|
---|
1392 | {
|
---|
1393 | jvmtiError error;
|
---|
1394 | JNIEnv *env;
|
---|
1395 |
|
---|
1396 | env = getEnv();
|
---|
1397 |
|
---|
1398 | log_debugee_location("threadControl_suspendThread()", thread, NULL, 0);
|
---|
1399 |
|
---|
1400 | preSuspend();
|
---|
1401 | error = commonSuspend(env, thread, deferred);
|
---|
1402 | postSuspend();
|
---|
1403 |
|
---|
1404 | return error;
|
---|
1405 | }
|
---|
1406 |
|
---|
1407 | jvmtiError
|
---|
1408 | threadControl_resumeThread(jthread thread, jboolean do_unblock)
|
---|
1409 | {
|
---|
1410 | jvmtiError error;
|
---|
1411 | JNIEnv *env;
|
---|
1412 |
|
---|
1413 | env = getEnv();
|
---|
1414 |
|
---|
1415 | log_debugee_location("threadControl_resumeThread()", thread, NULL, 0);
|
---|
1416 |
|
---|
1417 | eventHandler_lock(); /* for proper lock order */
|
---|
1418 | debugMonitorEnter(threadLock);
|
---|
1419 | error = commonResume(thread);
|
---|
1420 | removeResumed(env, &otherThreads);
|
---|
1421 | debugMonitorExit(threadLock);
|
---|
1422 | eventHandler_unlock();
|
---|
1423 |
|
---|
1424 | if (do_unblock) {
|
---|
1425 | /* let eventHelper.c: commandLoop() know we resumed one thread */
|
---|
1426 | unblockCommandLoop();
|
---|
1427 | }
|
---|
1428 |
|
---|
1429 | return error;
|
---|
1430 | }
|
---|
1431 |
|
---|
1432 | jvmtiError
|
---|
1433 | threadControl_suspendCount(jthread thread, jint *count)
|
---|
1434 | {
|
---|
1435 | jvmtiError error;
|
---|
1436 | ThreadNode *node;
|
---|
1437 |
|
---|
1438 | debugMonitorEnter(threadLock);
|
---|
1439 |
|
---|
1440 | node = findThread(&runningThreads, thread);
|
---|
1441 | if (node == NULL) {
|
---|
1442 | node = findThread(&otherThreads, thread);
|
---|
1443 | }
|
---|
1444 |
|
---|
1445 | error = JVMTI_ERROR_NONE;
|
---|
1446 | if (node != NULL) {
|
---|
1447 | *count = node->suspendCount;
|
---|
1448 | } else {
|
---|
1449 | /*
|
---|
1450 | * If the node is in neither list, the debugger never suspended
|
---|
1451 | * this thread, so the suspend count is 0.
|
---|
1452 | */
|
---|
1453 | *count = 0;
|
---|
1454 | }
|
---|
1455 |
|
---|
1456 | debugMonitorExit(threadLock);
|
---|
1457 |
|
---|
1458 | return error;
|
---|
1459 | }
|
---|
1460 |
|
---|
1461 | static jboolean
|
---|
1462 | contains(JNIEnv *env, jthread *list, jint count, jthread item)
|
---|
1463 | {
|
---|
1464 | int i;
|
---|
1465 |
|
---|
1466 | for (i = 0; i < count; i++) {
|
---|
1467 | if (isSameObject(env, list[i], item)) {
|
---|
1468 | return JNI_TRUE;
|
---|
1469 | }
|
---|
1470 | }
|
---|
1471 | return JNI_FALSE;
|
---|
1472 | }
|
---|
1473 |
|
---|
1474 |
|
---|
1475 | typedef struct {
|
---|
1476 | jthread *list;
|
---|
1477 | jint count;
|
---|
1478 | } SuspendAllArg;
|
---|
1479 |
|
---|
1480 | static jvmtiError
|
---|
1481 | suspendAllHelper(JNIEnv *env, ThreadNode *node, void *arg)
|
---|
1482 | {
|
---|
1483 | SuspendAllArg *saArg = (SuspendAllArg *)arg;
|
---|
1484 | jvmtiError error = JVMTI_ERROR_NONE;
|
---|
1485 | jthread *list = saArg->list;
|
---|
1486 | jint count = saArg->count;
|
---|
1487 | if (!contains(env, list, count, node->thread)) {
|
---|
1488 | error = commonSuspend(env, node->thread, JNI_FALSE);
|
---|
1489 | }
|
---|
1490 | return error;
|
---|
1491 | }
|
---|
1492 |
|
---|
1493 | jvmtiError
|
---|
1494 | threadControl_suspendAll(void)
|
---|
1495 | {
|
---|
1496 | jvmtiError error;
|
---|
1497 | JNIEnv *env;
|
---|
1498 |
|
---|
1499 | env = getEnv();
|
---|
1500 |
|
---|
1501 | log_debugee_location("threadControl_suspendAll()", NULL, NULL, 0);
|
---|
1502 |
|
---|
1503 | preSuspend();
|
---|
1504 |
|
---|
1505 | /*
|
---|
1506 | * Get a list of all threads and suspend them.
|
---|
1507 | */
|
---|
1508 | WITH_LOCAL_REFS(env, 1) {
|
---|
1509 |
|
---|
1510 | jthread *threads;
|
---|
1511 | jint count;
|
---|
1512 |
|
---|
1513 | threads = allThreads(&count);
|
---|
1514 | if (threads == NULL) {
|
---|
1515 | error = AGENT_ERROR_OUT_OF_MEMORY;
|
---|
1516 | goto err;
|
---|
1517 | }
|
---|
1518 | if (canSuspendResumeThreadLists()) {
|
---|
1519 | error = commonSuspendList(env, count, threads);
|
---|
1520 | if (error != JVMTI_ERROR_NONE) {
|
---|
1521 | goto err;
|
---|
1522 | }
|
---|
1523 | } else {
|
---|
1524 |
|
---|
1525 | int i;
|
---|
1526 |
|
---|
1527 | for (i = 0; i < count; i++) {
|
---|
1528 | error = commonSuspend(env, threads[i], JNI_FALSE);
|
---|
1529 |
|
---|
1530 | if (error != JVMTI_ERROR_NONE) {
|
---|
1531 | goto err;
|
---|
1532 | }
|
---|
1533 | }
|
---|
1534 | }
|
---|
1535 |
|
---|
1536 | /*
|
---|
1537 | * Update the suspend count of any threads not yet (or no longer)
|
---|
1538 | * in the thread list above.
|
---|
1539 | */
|
---|
1540 | {
|
---|
1541 | SuspendAllArg arg;
|
---|
1542 | arg.list = threads;
|
---|
1543 | arg.count = count;
|
---|
1544 | error = enumerateOverThreadList(env, &otherThreads,
|
---|
1545 | suspendAllHelper, &arg);
|
---|
1546 | }
|
---|
1547 |
|
---|
1548 | if (error == JVMTI_ERROR_NONE) {
|
---|
1549 | suspendAllCount++;
|
---|
1550 | }
|
---|
1551 |
|
---|
1552 | err: ;
|
---|
1553 |
|
---|
1554 | } END_WITH_LOCAL_REFS(env)
|
---|
1555 |
|
---|
1556 | postSuspend();
|
---|
1557 |
|
---|
1558 | return error;
|
---|
1559 | }
|
---|
1560 |
|
---|
1561 | static jvmtiError
|
---|
1562 | resumeHelper(JNIEnv *env, ThreadNode *node, void *ignored)
|
---|
1563 | {
|
---|
1564 | /*
|
---|
1565 | * Since this helper is called with the threadLock held, we
|
---|
1566 | * don't need to recheck to see if the node is still on one
|
---|
1567 | * of the two thread lists.
|
---|
1568 | */
|
---|
1569 | return resumeThreadByNode(node);
|
---|
1570 | }
|
---|
1571 |
|
---|
1572 | jvmtiError
|
---|
1573 | threadControl_resumeAll(void)
|
---|
1574 | {
|
---|
1575 | jvmtiError error;
|
---|
1576 | JNIEnv *env;
|
---|
1577 |
|
---|
1578 | env = getEnv();
|
---|
1579 |
|
---|
1580 | log_debugee_location("threadControl_resumeAll()", NULL, NULL, 0);
|
---|
1581 |
|
---|
1582 | eventHandler_lock(); /* for proper lock order */
|
---|
1583 | debugMonitorEnter(threadLock);
|
---|
1584 |
|
---|
1585 | /*
|
---|
1586 | * Resume only those threads that the debugger has suspended. All
|
---|
1587 | * such threads must have a node in one of the thread lists, so there's
|
---|
1588 | * no need to get the whole thread list from JVMTI (unlike
|
---|
1589 | * suspendAll).
|
---|
1590 | */
|
---|
1591 | if (canSuspendResumeThreadLists()) {
|
---|
1592 | error = commonResumeList(env);
|
---|
1593 | } else {
|
---|
1594 | error = enumerateOverThreadList(env, &runningThreads,
|
---|
1595 | resumeHelper, NULL);
|
---|
1596 | }
|
---|
1597 | if ((error == JVMTI_ERROR_NONE) && (otherThreads.first != NULL)) {
|
---|
1598 | error = enumerateOverThreadList(env, &otherThreads,
|
---|
1599 | resumeHelper, NULL);
|
---|
1600 | removeResumed(env, &otherThreads);
|
---|
1601 | }
|
---|
1602 |
|
---|
1603 | if (suspendAllCount > 0) {
|
---|
1604 | suspendAllCount--;
|
---|
1605 | }
|
---|
1606 |
|
---|
1607 | debugMonitorExit(threadLock);
|
---|
1608 | eventHandler_unlock();
|
---|
1609 | /* let eventHelper.c: commandLoop() know we are resuming */
|
---|
1610 | unblockCommandLoop();
|
---|
1611 |
|
---|
1612 | return error;
|
---|
1613 | }
|
---|
1614 |
|
---|
1615 |
|
---|
1616 | StepRequest *
|
---|
1617 | threadControl_getStepRequest(jthread thread)
|
---|
1618 | {
|
---|
1619 | ThreadNode *node;
|
---|
1620 | StepRequest *step;
|
---|
1621 |
|
---|
1622 | step = NULL;
|
---|
1623 |
|
---|
1624 | debugMonitorEnter(threadLock);
|
---|
1625 |
|
---|
1626 | node = findThread(&runningThreads, thread);
|
---|
1627 | if (node != NULL) {
|
---|
1628 | step = &node->currentStep;
|
---|
1629 | }
|
---|
1630 |
|
---|
1631 | debugMonitorExit(threadLock);
|
---|
1632 |
|
---|
1633 | return step;
|
---|
1634 | }
|
---|
1635 |
|
---|
1636 | InvokeRequest *
|
---|
1637 | threadControl_getInvokeRequest(jthread thread)
|
---|
1638 | {
|
---|
1639 | ThreadNode *node;
|
---|
1640 | InvokeRequest *request;
|
---|
1641 |
|
---|
1642 | request = NULL;
|
---|
1643 |
|
---|
1644 | debugMonitorEnter(threadLock);
|
---|
1645 |
|
---|
1646 | node = findThread(&runningThreads, thread);
|
---|
1647 | if (node != NULL) {
|
---|
1648 | request = &node->currentInvoke;
|
---|
1649 | }
|
---|
1650 |
|
---|
1651 | debugMonitorExit(threadLock);
|
---|
1652 |
|
---|
1653 | return request;
|
---|
1654 | }
|
---|
1655 |
|
---|
1656 | jvmtiError
|
---|
1657 | threadControl_addDebugThread(jthread thread)
|
---|
1658 | {
|
---|
1659 | jvmtiError error;
|
---|
1660 |
|
---|
1661 | debugMonitorEnter(threadLock);
|
---|
1662 | if (debugThreadCount >= MAX_DEBUG_THREADS) {
|
---|
1663 | error = AGENT_ERROR_OUT_OF_MEMORY;
|
---|
1664 | } else {
|
---|
1665 | JNIEnv *env;
|
---|
1666 |
|
---|
1667 | env = getEnv();
|
---|
1668 | debugThreads[debugThreadCount] = NULL;
|
---|
1669 | saveGlobalRef(env, thread, &(debugThreads[debugThreadCount]));
|
---|
1670 | if (debugThreads[debugThreadCount] == NULL) {
|
---|
1671 | error = AGENT_ERROR_OUT_OF_MEMORY;
|
---|
1672 | } else {
|
---|
1673 | debugThreadCount++;
|
---|
1674 | error = JVMTI_ERROR_NONE;
|
---|
1675 | }
|
---|
1676 | }
|
---|
1677 | debugMonitorExit(threadLock);
|
---|
1678 | return error;
|
---|
1679 | }
|
---|
1680 |
|
---|
1681 | static jvmtiError
|
---|
1682 | threadControl_removeDebugThread(jthread thread)
|
---|
1683 | {
|
---|
1684 | jvmtiError error;
|
---|
1685 | JNIEnv *env;
|
---|
1686 | int i;
|
---|
1687 |
|
---|
1688 | error = AGENT_ERROR_INVALID_THREAD;
|
---|
1689 | env = getEnv();
|
---|
1690 |
|
---|
1691 | debugMonitorEnter(threadLock);
|
---|
1692 | for (i = 0; i< debugThreadCount; i++) {
|
---|
1693 | if (isSameObject(env, thread, debugThreads[i])) {
|
---|
1694 | int j;
|
---|
1695 |
|
---|
1696 | tossGlobalRef(env, &(debugThreads[i]));
|
---|
1697 | for (j = i+1; j < debugThreadCount; j++) {
|
---|
1698 | debugThreads[j-1] = debugThreads[j];
|
---|
1699 | }
|
---|
1700 | debugThreadCount--;
|
---|
1701 | error = JVMTI_ERROR_NONE;
|
---|
1702 | break;
|
---|
1703 | }
|
---|
1704 | }
|
---|
1705 | debugMonitorExit(threadLock);
|
---|
1706 | return error;
|
---|
1707 | }
|
---|
1708 |
|
---|
1709 | jboolean
|
---|
1710 | threadControl_isDebugThread(jthread thread)
|
---|
1711 | {
|
---|
1712 | int i;
|
---|
1713 | jboolean rc;
|
---|
1714 | JNIEnv *env;
|
---|
1715 |
|
---|
1716 | rc = JNI_FALSE;
|
---|
1717 | env = getEnv();
|
---|
1718 |
|
---|
1719 | debugMonitorEnter(threadLock);
|
---|
1720 | for (i = 0; i < debugThreadCount; i++) {
|
---|
1721 | if (isSameObject(env, thread, debugThreads[i])) {
|
---|
1722 | rc = JNI_TRUE;
|
---|
1723 | break;
|
---|
1724 | }
|
---|
1725 | }
|
---|
1726 | debugMonitorExit(threadLock);
|
---|
1727 | return rc;
|
---|
1728 | }
|
---|
1729 |
|
---|
1730 | static void
|
---|
1731 | initLocks(void)
|
---|
1732 | {
|
---|
1733 | if (popFrameEventLock == NULL) {
|
---|
1734 | popFrameEventLock = debugMonitorCreate("JDWP PopFrame Event Lock");
|
---|
1735 | popFrameProceedLock = debugMonitorCreate("JDWP PopFrame Proceed Lock");
|
---|
1736 | }
|
---|
1737 | }
|
---|
1738 |
|
---|
1739 | static jboolean
|
---|
1740 | getPopFrameThread(jthread thread)
|
---|
1741 | {
|
---|
1742 | jboolean popFrameThread;
|
---|
1743 |
|
---|
1744 | debugMonitorEnter(threadLock);
|
---|
1745 | {
|
---|
1746 | ThreadNode *node;
|
---|
1747 |
|
---|
1748 | node = findThread(NULL, thread);
|
---|
1749 | if (node == NULL) {
|
---|
1750 | popFrameThread = JNI_FALSE;
|
---|
1751 | } else {
|
---|
1752 | popFrameThread = node->popFrameThread;
|
---|
1753 | }
|
---|
1754 | }
|
---|
1755 | debugMonitorExit(threadLock);
|
---|
1756 |
|
---|
1757 | return popFrameThread;
|
---|
1758 | }
|
---|
1759 |
|
---|
1760 | static void
|
---|
1761 | setPopFrameThread(jthread thread, jboolean value)
|
---|
1762 | {
|
---|
1763 | debugMonitorEnter(threadLock);
|
---|
1764 | {
|
---|
1765 | ThreadNode *node;
|
---|
1766 |
|
---|
1767 | node = findThread(NULL, thread);
|
---|
1768 | if (node == NULL) {
|
---|
1769 | EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"entry in thread table");
|
---|
1770 | } else {
|
---|
1771 | node->popFrameThread = value;
|
---|
1772 | }
|
---|
1773 | }
|
---|
1774 | debugMonitorExit(threadLock);
|
---|
1775 | }
|
---|
1776 |
|
---|
1777 | static jboolean
|
---|
1778 | getPopFrameEvent(jthread thread)
|
---|
1779 | {
|
---|
1780 | jboolean popFrameEvent;
|
---|
1781 |
|
---|
1782 | debugMonitorEnter(threadLock);
|
---|
1783 | {
|
---|
1784 | ThreadNode *node;
|
---|
1785 |
|
---|
1786 | node = findThread(NULL, thread);
|
---|
1787 | if (node == NULL) {
|
---|
1788 | popFrameEvent = JNI_FALSE;
|
---|
1789 | EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"entry in thread table");
|
---|
1790 | } else {
|
---|
1791 | popFrameEvent = node->popFrameEvent;
|
---|
1792 | }
|
---|
1793 | }
|
---|
1794 | debugMonitorExit(threadLock);
|
---|
1795 |
|
---|
1796 | return popFrameEvent;
|
---|
1797 | }
|
---|
1798 |
|
---|
1799 | static void
|
---|
1800 | setPopFrameEvent(jthread thread, jboolean value)
|
---|
1801 | {
|
---|
1802 | debugMonitorEnter(threadLock);
|
---|
1803 | {
|
---|
1804 | ThreadNode *node;
|
---|
1805 |
|
---|
1806 | node = findThread(NULL, thread);
|
---|
1807 | if (node == NULL) {
|
---|
1808 | EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"entry in thread table");
|
---|
1809 | } else {
|
---|
1810 | node->popFrameEvent = value;
|
---|
1811 | node->frameGeneration++; /* Increment on each resume */
|
---|
1812 | }
|
---|
1813 | }
|
---|
1814 | debugMonitorExit(threadLock);
|
---|
1815 | }
|
---|
1816 |
|
---|
1817 | static jboolean
|
---|
1818 | getPopFrameProceed(jthread thread)
|
---|
1819 | {
|
---|
1820 | jboolean popFrameProceed;
|
---|
1821 |
|
---|
1822 | debugMonitorEnter(threadLock);
|
---|
1823 | {
|
---|
1824 | ThreadNode *node;
|
---|
1825 |
|
---|
1826 | node = findThread(NULL, thread);
|
---|
1827 | if (node == NULL) {
|
---|
1828 | popFrameProceed = JNI_FALSE;
|
---|
1829 | EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"entry in thread table");
|
---|
1830 | } else {
|
---|
1831 | popFrameProceed = node->popFrameProceed;
|
---|
1832 | }
|
---|
1833 | }
|
---|
1834 | debugMonitorExit(threadLock);
|
---|
1835 |
|
---|
1836 | return popFrameProceed;
|
---|
1837 | }
|
---|
1838 |
|
---|
1839 | static void
|
---|
1840 | setPopFrameProceed(jthread thread, jboolean value)
|
---|
1841 | {
|
---|
1842 | debugMonitorEnter(threadLock);
|
---|
1843 | {
|
---|
1844 | ThreadNode *node;
|
---|
1845 |
|
---|
1846 | node = findThread(NULL, thread);
|
---|
1847 | if (node == NULL) {
|
---|
1848 | EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"entry in thread table");
|
---|
1849 | } else {
|
---|
1850 | node->popFrameProceed = value;
|
---|
1851 | }
|
---|
1852 | }
|
---|
1853 | debugMonitorExit(threadLock);
|
---|
1854 | }
|
---|
1855 |
|
---|
1856 | /**
|
---|
1857 | * Special event handler for events on the popped thread
|
---|
1858 | * that occur during the pop operation.
|
---|
1859 | */
|
---|
1860 | static void
|
---|
1861 | popFrameCompleteEvent(jthread thread)
|
---|
1862 | {
|
---|
1863 | debugMonitorEnter(popFrameProceedLock);
|
---|
1864 | {
|
---|
1865 | /* notify that we got the event */
|
---|
1866 | debugMonitorEnter(popFrameEventLock);
|
---|
1867 | {
|
---|
1868 | setPopFrameEvent(thread, JNI_TRUE);
|
---|
1869 | debugMonitorNotify(popFrameEventLock);
|
---|
1870 | }
|
---|
1871 | debugMonitorExit(popFrameEventLock);
|
---|
1872 |
|
---|
1873 | /* make sure we get suspended again */
|
---|
1874 | setPopFrameProceed(thread, JNI_FALSE);
|
---|
1875 | while (getPopFrameProceed(thread) == JNI_FALSE) {
|
---|
1876 | debugMonitorWait(popFrameProceedLock);
|
---|
1877 | }
|
---|
1878 | }
|
---|
1879 | debugMonitorExit(popFrameProceedLock);
|
---|
1880 | }
|
---|
1881 |
|
---|
1882 | /**
|
---|
1883 | * Pop one frame off the stack of thread.
|
---|
1884 | * popFrameEventLock is already held
|
---|
1885 | */
|
---|
1886 | static jvmtiError
|
---|
1887 | popOneFrame(jthread thread)
|
---|
1888 | {
|
---|
1889 | jvmtiError error;
|
---|
1890 |
|
---|
1891 | error = JVMTI_FUNC_PTR(gdata->jvmti,PopFrame)(gdata->jvmti, thread);
|
---|
1892 | if (error != JVMTI_ERROR_NONE) {
|
---|
1893 | return error;
|
---|
1894 | }
|
---|
1895 |
|
---|
1896 | /* resume the popped thread so that the pop occurs and so we */
|
---|
1897 | /* will get the event (step or method entry) after the pop */
|
---|
1898 | LOG_MISC(("thread=%p resumed in popOneFrame", thread));
|
---|
1899 | error = JVMTI_FUNC_PTR(gdata->jvmti,ResumeThread)(gdata->jvmti, thread);
|
---|
1900 | if (error != JVMTI_ERROR_NONE) {
|
---|
1901 | return error;
|
---|
1902 | }
|
---|
1903 |
|
---|
1904 | /* wait for the event to occur */
|
---|
1905 | setPopFrameEvent(thread, JNI_FALSE);
|
---|
1906 | while (getPopFrameEvent(thread) == JNI_FALSE) {
|
---|
1907 | debugMonitorWait(popFrameEventLock);
|
---|
1908 | }
|
---|
1909 |
|
---|
1910 | /* make sure not to suspend until the popped thread is on the wait */
|
---|
1911 | debugMonitorEnter(popFrameProceedLock);
|
---|
1912 | {
|
---|
1913 | /* return popped thread to suspended state */
|
---|
1914 | LOG_MISC(("thread=%p suspended in popOneFrame", thread));
|
---|
1915 | error = JVMTI_FUNC_PTR(gdata->jvmti,SuspendThread)(gdata->jvmti, thread);
|
---|
1916 |
|
---|
1917 | /* notify popped thread so it can proceed when resumed */
|
---|
1918 | setPopFrameProceed(thread, JNI_TRUE);
|
---|
1919 | debugMonitorNotify(popFrameProceedLock);
|
---|
1920 | }
|
---|
1921 | debugMonitorExit(popFrameProceedLock);
|
---|
1922 |
|
---|
1923 | return error;
|
---|
1924 | }
|
---|
1925 |
|
---|
1926 | /**
|
---|
1927 | * pop frames of the stack of 'thread' until 'frame' is popped.
|
---|
1928 | */
|
---|
1929 | jvmtiError
|
---|
1930 | threadControl_popFrames(jthread thread, FrameNumber fnum)
|
---|
1931 | {
|
---|
1932 | jvmtiError error;
|
---|
1933 | jvmtiEventMode prevStepMode;
|
---|
1934 | jint framesPopped = 0;
|
---|
1935 | jint popCount;
|
---|
1936 | jboolean prevInvokeRequestMode;
|
---|
1937 |
|
---|
1938 | log_debugee_location("threadControl_popFrames()", thread, NULL, 0);
|
---|
1939 |
|
---|
1940 | initLocks();
|
---|
1941 |
|
---|
1942 | /* compute the number of frames to pop */
|
---|
1943 | popCount = fnum+1;
|
---|
1944 | if (popCount < 1) {
|
---|
1945 | return AGENT_ERROR_NO_MORE_FRAMES;
|
---|
1946 | }
|
---|
1947 |
|
---|
1948 | /* enable instruction level single step, but first note prev value */
|
---|
1949 | prevStepMode = threadControl_getInstructionStepMode(thread);
|
---|
1950 |
|
---|
1951 | /*
|
---|
1952 | * Fix bug 6517249. The pop processing will disable invokes,
|
---|
1953 | * so remember if invokes are enabled now and restore
|
---|
1954 | * that state after we finish popping.
|
---|
1955 | */
|
---|
1956 | prevInvokeRequestMode = invoker_isEnabled(thread);
|
---|
1957 |
|
---|
1958 | error = threadControl_setEventMode(JVMTI_ENABLE,
|
---|
1959 | EI_SINGLE_STEP, thread);
|
---|
1960 | if (error != JVMTI_ERROR_NONE) {
|
---|
1961 | return error;
|
---|
1962 | }
|
---|
1963 |
|
---|
1964 | /* Inform eventHandler logic we are in a popFrame for this thread */
|
---|
1965 | debugMonitorEnter(popFrameEventLock);
|
---|
1966 | {
|
---|
1967 | setPopFrameThread(thread, JNI_TRUE);
|
---|
1968 | /* pop frames using single step */
|
---|
1969 | while (framesPopped++ < popCount) {
|
---|
1970 | error = popOneFrame(thread);
|
---|
1971 | if (error != JVMTI_ERROR_NONE) {
|
---|
1972 | break;
|
---|
1973 | }
|
---|
1974 | }
|
---|
1975 | setPopFrameThread(thread, JNI_FALSE);
|
---|
1976 | }
|
---|
1977 | debugMonitorExit(popFrameEventLock);
|
---|
1978 |
|
---|
1979 | /* Reset StepRequest info (fromLine and stackDepth) after popframes
|
---|
1980 | * only if stepping is enabled.
|
---|
1981 | */
|
---|
1982 | if (prevStepMode == JVMTI_ENABLE) {
|
---|
1983 | stepControl_resetRequest(thread);
|
---|
1984 | }
|
---|
1985 |
|
---|
1986 | if (prevInvokeRequestMode) {
|
---|
1987 | invoker_enableInvokeRequests(thread);
|
---|
1988 | }
|
---|
1989 |
|
---|
1990 | /* restore state */
|
---|
1991 | (void)threadControl_setEventMode(prevStepMode,
|
---|
1992 | EI_SINGLE_STEP, thread);
|
---|
1993 |
|
---|
1994 | return error;
|
---|
1995 | }
|
---|
1996 |
|
---|
1997 | /* Check to see if any events are being consumed by a popFrame(). */
|
---|
1998 | static jboolean
|
---|
1999 | checkForPopFrameEvents(JNIEnv *env, EventIndex ei, jthread thread)
|
---|
2000 | {
|
---|
2001 | if ( getPopFrameThread(thread) ) {
|
---|
2002 | switch (ei) {
|
---|
2003 | case EI_THREAD_START:
|
---|
2004 | /* Excuse me? */
|
---|
2005 | EXIT_ERROR(AGENT_ERROR_INTERNAL, "thread start during pop frame");
|
---|
2006 | break;
|
---|
2007 | case EI_THREAD_END:
|
---|
2008 | /* Thread wants to end? let it. */
|
---|
2009 | setPopFrameThread(thread, JNI_FALSE);
|
---|
2010 | popFrameCompleteEvent(thread);
|
---|
2011 | break;
|
---|
2012 | case EI_SINGLE_STEP:
|
---|
2013 | /* This is an event we requested to mark the */
|
---|
2014 | /* completion of the pop frame */
|
---|
2015 | popFrameCompleteEvent(thread);
|
---|
2016 | return JNI_TRUE;
|
---|
2017 | case EI_BREAKPOINT:
|
---|
2018 | case EI_EXCEPTION:
|
---|
2019 | case EI_FIELD_ACCESS:
|
---|
2020 | case EI_FIELD_MODIFICATION:
|
---|
2021 | case EI_METHOD_ENTRY:
|
---|
2022 | case EI_METHOD_EXIT:
|
---|
2023 | /* Tell event handler to assume event has been consumed. */
|
---|
2024 | return JNI_TRUE;
|
---|
2025 | default:
|
---|
2026 | break;
|
---|
2027 | }
|
---|
2028 | }
|
---|
2029 | /* Pretend we were never called */
|
---|
2030 | return JNI_FALSE;
|
---|
2031 | }
|
---|
2032 |
|
---|
2033 | struct bag *
|
---|
2034 | threadControl_onEventHandlerEntry(jbyte sessionID, EventIndex ei, jthread thread, jobject currentException)
|
---|
2035 | {
|
---|
2036 | ThreadNode *node;
|
---|
2037 | JNIEnv *env;
|
---|
2038 | struct bag *eventBag;
|
---|
2039 | jthread threadToSuspend;
|
---|
2040 | jboolean consumed;
|
---|
2041 |
|
---|
2042 | env = getEnv();
|
---|
2043 | threadToSuspend = NULL;
|
---|
2044 |
|
---|
2045 | log_debugee_location("threadControl_onEventHandlerEntry()", thread, NULL, 0);
|
---|
2046 |
|
---|
2047 | /* Events during pop commands may need to be ignored here. */
|
---|
2048 | consumed = checkForPopFrameEvents(env, ei, thread);
|
---|
2049 | if ( consumed ) {
|
---|
2050 | /* Always restore any exception (see below). */
|
---|
2051 | if (currentException != NULL) {
|
---|
2052 | JNI_FUNC_PTR(env,Throw)(env, currentException);
|
---|
2053 | } else {
|
---|
2054 | JNI_FUNC_PTR(env,ExceptionClear)(env);
|
---|
2055 | }
|
---|
2056 | return NULL;
|
---|
2057 | }
|
---|
2058 |
|
---|
2059 | debugMonitorEnter(threadLock);
|
---|
2060 |
|
---|
2061 | /*
|
---|
2062 | * Check the list of unknown threads maintained by suspend
|
---|
2063 | * and resume. If this thread is currently present in the
|
---|
2064 | * list, it should be
|
---|
2065 | * moved to the runningThreads list, since it is a
|
---|
2066 | * well-known thread now.
|
---|
2067 | */
|
---|
2068 | node = findThread(&otherThreads, thread);
|
---|
2069 | if (node != NULL) {
|
---|
2070 | moveNode(&otherThreads, &runningThreads, node);
|
---|
2071 | } else {
|
---|
2072 | /*
|
---|
2073 | * Get a thread node for the reporting thread. For thread start
|
---|
2074 | * events, or if this event precedes a thread start event,
|
---|
2075 | * the thread node may need to be created.
|
---|
2076 | *
|
---|
2077 | * It is possible for certain events (notably method entry/exit)
|
---|
2078 | * to precede thread start for some VM implementations.
|
---|
2079 | */
|
---|
2080 | node = insertThread(env, &runningThreads, thread);
|
---|
2081 | }
|
---|
2082 |
|
---|
2083 | if (ei == EI_THREAD_START) {
|
---|
2084 | node->isStarted = JNI_TRUE;
|
---|
2085 | processDeferredEventModes(env, thread, node);
|
---|
2086 | }
|
---|
2087 |
|
---|
2088 | node->current_ei = ei;
|
---|
2089 | eventBag = node->eventBag;
|
---|
2090 | if (node->suspendOnStart) {
|
---|
2091 | threadToSuspend = node->thread;
|
---|
2092 | }
|
---|
2093 | debugMonitorExit(threadLock);
|
---|
2094 |
|
---|
2095 | if (threadToSuspend != NULL) {
|
---|
2096 | /*
|
---|
2097 | * An attempt was made to suspend this thread before it started.
|
---|
2098 | * We must suspend it now, before it starts to run. This must
|
---|
2099 | * be done with no locks held.
|
---|
2100 | */
|
---|
2101 | eventHelper_suspendThread(sessionID, threadToSuspend);
|
---|
2102 | }
|
---|
2103 |
|
---|
2104 | return eventBag;
|
---|
2105 | }
|
---|
2106 |
|
---|
2107 | static void
|
---|
2108 | doPendingTasks(JNIEnv *env, ThreadNode *node)
|
---|
2109 | {
|
---|
2110 | /*
|
---|
2111 | * Take care of any pending interrupts/stops, and clear out
|
---|
2112 | * info on pending interrupts/stops.
|
---|
2113 | */
|
---|
2114 | if (node->pendingInterrupt) {
|
---|
2115 | JVMTI_FUNC_PTR(gdata->jvmti,InterruptThread)
|
---|
2116 | (gdata->jvmti, node->thread);
|
---|
2117 | /*
|
---|
2118 | * TO DO: Log error
|
---|
2119 | */
|
---|
2120 | node->pendingInterrupt = JNI_FALSE;
|
---|
2121 | }
|
---|
2122 |
|
---|
2123 | if (node->pendingStop != NULL) {
|
---|
2124 | JVMTI_FUNC_PTR(gdata->jvmti,StopThread)
|
---|
2125 | (gdata->jvmti, node->thread, node->pendingStop);
|
---|
2126 | /*
|
---|
2127 | * TO DO: Log error
|
---|
2128 | */
|
---|
2129 | tossGlobalRef(env, &(node->pendingStop));
|
---|
2130 | }
|
---|
2131 | }
|
---|
2132 |
|
---|
2133 | void
|
---|
2134 | threadControl_onEventHandlerExit(EventIndex ei, jthread thread,
|
---|
2135 | struct bag *eventBag)
|
---|
2136 | {
|
---|
2137 | ThreadNode *node;
|
---|
2138 |
|
---|
2139 | log_debugee_location("threadControl_onEventHandlerExit()", thread, NULL, 0);
|
---|
2140 |
|
---|
2141 | if (ei == EI_THREAD_END) {
|
---|
2142 | eventHandler_lock(); /* for proper lock order */
|
---|
2143 | }
|
---|
2144 | debugMonitorEnter(threadLock);
|
---|
2145 |
|
---|
2146 | node = findThread(&runningThreads, thread);
|
---|
2147 | if (node == NULL) {
|
---|
2148 | EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"thread list corrupted");
|
---|
2149 | } else {
|
---|
2150 | JNIEnv *env;
|
---|
2151 |
|
---|
2152 | env = getEnv();
|
---|
2153 | if (ei == EI_THREAD_END) {
|
---|
2154 | jboolean inResume = (node->resumeFrameDepth > 0);
|
---|
2155 | removeThread(env, &runningThreads, thread);
|
---|
2156 | node = NULL; /* has been freed */
|
---|
2157 |
|
---|
2158 | /*
|
---|
2159 | * Clean up mechanism used to detect end of
|
---|
2160 | * resume.
|
---|
2161 | */
|
---|
2162 | if (inResume) {
|
---|
2163 | notifyAppResumeComplete();
|
---|
2164 | }
|
---|
2165 | } else {
|
---|
2166 | /* No point in doing this if the thread is about to die.*/
|
---|
2167 | doPendingTasks(env, node);
|
---|
2168 | node->eventBag = eventBag;
|
---|
2169 | node->current_ei = 0;
|
---|
2170 | }
|
---|
2171 | }
|
---|
2172 |
|
---|
2173 | debugMonitorExit(threadLock);
|
---|
2174 | if (ei == EI_THREAD_END) {
|
---|
2175 | eventHandler_unlock();
|
---|
2176 | }
|
---|
2177 | }
|
---|
2178 |
|
---|
2179 | /* Returns JDWP flavored status and status flags. */
|
---|
2180 | jvmtiError
|
---|
2181 | threadControl_applicationThreadStatus(jthread thread,
|
---|
2182 | jdwpThreadStatus *pstatus, jint *statusFlags)
|
---|
2183 | {
|
---|
2184 | ThreadNode *node;
|
---|
2185 | jvmtiError error;
|
---|
2186 | jint state;
|
---|
2187 |
|
---|
2188 | log_debugee_location("threadControl_applicationThreadStatus()", thread, NULL, 0);
|
---|
2189 |
|
---|
2190 | debugMonitorEnter(threadLock);
|
---|
2191 |
|
---|
2192 | error = threadState(thread, &state);
|
---|
2193 | *pstatus = map2jdwpThreadStatus(state);
|
---|
2194 | *statusFlags = map2jdwpSuspendStatus(state);
|
---|
2195 |
|
---|
2196 | if (error == JVMTI_ERROR_NONE) {
|
---|
2197 | node = findThread(&runningThreads, thread);
|
---|
2198 | if ((node != NULL) && HANDLING_EVENT(node)) {
|
---|
2199 | /*
|
---|
2200 | * While processing an event, an application thread is always
|
---|
2201 | * considered to be running even if its handler happens to be
|
---|
2202 | * cond waiting on an internal debugger monitor, etc.
|
---|
2203 | *
|
---|
2204 | * Leave suspend status untouched since it is not possible
|
---|
2205 | * to distinguish debugger suspends from app suspends.
|
---|
2206 | */
|
---|
2207 | *pstatus = JDWP_THREAD_STATUS(RUNNING);
|
---|
2208 | }
|
---|
2209 | }
|
---|
2210 |
|
---|
2211 | debugMonitorExit(threadLock);
|
---|
2212 |
|
---|
2213 | return error;
|
---|
2214 | }
|
---|
2215 |
|
---|
2216 | jvmtiError
|
---|
2217 | threadControl_interrupt(jthread thread)
|
---|
2218 | {
|
---|
2219 | ThreadNode *node;
|
---|
2220 | jvmtiError error;
|
---|
2221 |
|
---|
2222 | error = JVMTI_ERROR_NONE;
|
---|
2223 |
|
---|
2224 | log_debugee_location("threadControl_interrupt()", thread, NULL, 0);
|
---|
2225 |
|
---|
2226 | debugMonitorEnter(threadLock);
|
---|
2227 |
|
---|
2228 | node = findThread(&runningThreads, thread);
|
---|
2229 | if ((node == NULL) || !HANDLING_EVENT(node)) {
|
---|
2230 | error = JVMTI_FUNC_PTR(gdata->jvmti,InterruptThread)
|
---|
2231 | (gdata->jvmti, thread);
|
---|
2232 | } else {
|
---|
2233 | /*
|
---|
2234 | * Hold any interrupts until after the event is processed.
|
---|
2235 | */
|
---|
2236 | node->pendingInterrupt = JNI_TRUE;
|
---|
2237 | }
|
---|
2238 |
|
---|
2239 | debugMonitorExit(threadLock);
|
---|
2240 |
|
---|
2241 | return error;
|
---|
2242 | }
|
---|
2243 |
|
---|
2244 | void
|
---|
2245 | threadControl_clearCLEInfo(JNIEnv *env, jthread thread)
|
---|
2246 | {
|
---|
2247 | ThreadNode *node;
|
---|
2248 |
|
---|
2249 | debugMonitorEnter(threadLock);
|
---|
2250 |
|
---|
2251 | node = findThread(&runningThreads, thread);
|
---|
2252 | if (node != NULL) {
|
---|
2253 | node->cleInfo.ei = 0;
|
---|
2254 | if (node->cleInfo.clazz != NULL) {
|
---|
2255 | tossGlobalRef(env, &(node->cleInfo.clazz));
|
---|
2256 | }
|
---|
2257 | }
|
---|
2258 |
|
---|
2259 | debugMonitorExit(threadLock);
|
---|
2260 | }
|
---|
2261 |
|
---|
2262 | jboolean
|
---|
2263 | threadControl_cmpCLEInfo(JNIEnv *env, jthread thread, jclass clazz,
|
---|
2264 | jmethodID method, jlocation location)
|
---|
2265 | {
|
---|
2266 | ThreadNode *node;
|
---|
2267 | jboolean result;
|
---|
2268 |
|
---|
2269 | result = JNI_FALSE;
|
---|
2270 |
|
---|
2271 | debugMonitorEnter(threadLock);
|
---|
2272 |
|
---|
2273 | node = findThread(&runningThreads, thread);
|
---|
2274 | if (node != NULL && node->cleInfo.ei != 0 &&
|
---|
2275 | node->cleInfo.method == method &&
|
---|
2276 | node->cleInfo.location == location &&
|
---|
2277 | (isSameObject(env, node->cleInfo.clazz, clazz))) {
|
---|
2278 | result = JNI_TRUE; /* we have a match */
|
---|
2279 | }
|
---|
2280 |
|
---|
2281 | debugMonitorExit(threadLock);
|
---|
2282 |
|
---|
2283 | return result;
|
---|
2284 | }
|
---|
2285 |
|
---|
2286 | void
|
---|
2287 | threadControl_saveCLEInfo(JNIEnv *env, jthread thread, EventIndex ei,
|
---|
2288 | jclass clazz, jmethodID method, jlocation location)
|
---|
2289 | {
|
---|
2290 | ThreadNode *node;
|
---|
2291 |
|
---|
2292 | debugMonitorEnter(threadLock);
|
---|
2293 |
|
---|
2294 | node = findThread(&runningThreads, thread);
|
---|
2295 | if (node != NULL) {
|
---|
2296 | node->cleInfo.ei = ei;
|
---|
2297 | /* Create a class ref that will live beyond */
|
---|
2298 | /* the end of this call */
|
---|
2299 | saveGlobalRef(env, clazz, &(node->cleInfo.clazz));
|
---|
2300 | /* if returned clazz is NULL, we just won't match */
|
---|
2301 | node->cleInfo.method = method;
|
---|
2302 | node->cleInfo.location = location;
|
---|
2303 | }
|
---|
2304 |
|
---|
2305 | debugMonitorExit(threadLock);
|
---|
2306 | }
|
---|
2307 |
|
---|
2308 | void
|
---|
2309 | threadControl_setPendingInterrupt(jthread thread)
|
---|
2310 | {
|
---|
2311 | ThreadNode *node;
|
---|
2312 |
|
---|
2313 | debugMonitorEnter(threadLock);
|
---|
2314 |
|
---|
2315 | node = findThread(&runningThreads, thread);
|
---|
2316 | if (node != NULL) {
|
---|
2317 | node->pendingInterrupt = JNI_TRUE;
|
---|
2318 | }
|
---|
2319 |
|
---|
2320 | debugMonitorExit(threadLock);
|
---|
2321 | }
|
---|
2322 |
|
---|
2323 | jvmtiError
|
---|
2324 | threadControl_stop(jthread thread, jobject throwable)
|
---|
2325 | {
|
---|
2326 | ThreadNode *node;
|
---|
2327 | jvmtiError error;
|
---|
2328 |
|
---|
2329 | error = JVMTI_ERROR_NONE;
|
---|
2330 |
|
---|
2331 | log_debugee_location("threadControl_stop()", thread, NULL, 0);
|
---|
2332 |
|
---|
2333 | debugMonitorEnter(threadLock);
|
---|
2334 |
|
---|
2335 | node = findThread(&runningThreads, thread);
|
---|
2336 | if ((node == NULL) || !HANDLING_EVENT(node)) {
|
---|
2337 | error = JVMTI_FUNC_PTR(gdata->jvmti,StopThread)
|
---|
2338 | (gdata->jvmti, thread, throwable);
|
---|
2339 | } else {
|
---|
2340 | JNIEnv *env;
|
---|
2341 |
|
---|
2342 | /*
|
---|
2343 | * Hold any stops until after the event is processed.
|
---|
2344 | */
|
---|
2345 | env = getEnv();
|
---|
2346 | saveGlobalRef(env, throwable, &(node->pendingStop));
|
---|
2347 | }
|
---|
2348 |
|
---|
2349 | debugMonitorExit(threadLock);
|
---|
2350 |
|
---|
2351 | return error;
|
---|
2352 | }
|
---|
2353 |
|
---|
2354 | static jvmtiError
|
---|
2355 | detachHelper(JNIEnv *env, ThreadNode *node, void *arg)
|
---|
2356 | {
|
---|
2357 | invoker_detach(&node->currentInvoke);
|
---|
2358 | return JVMTI_ERROR_NONE;
|
---|
2359 | }
|
---|
2360 |
|
---|
2361 | void
|
---|
2362 | threadControl_detachInvokes(void)
|
---|
2363 | {
|
---|
2364 | JNIEnv *env;
|
---|
2365 |
|
---|
2366 | env = getEnv();
|
---|
2367 | invoker_lock(); /* for proper lock order */
|
---|
2368 | debugMonitorEnter(threadLock);
|
---|
2369 | (void)enumerateOverThreadList(env, &runningThreads, detachHelper, NULL);
|
---|
2370 | debugMonitorExit(threadLock);
|
---|
2371 | invoker_unlock();
|
---|
2372 | }
|
---|
2373 |
|
---|
2374 | static jvmtiError
|
---|
2375 | resetHelper(JNIEnv *env, ThreadNode *node, void *arg)
|
---|
2376 | {
|
---|
2377 | if (node->toBeResumed) {
|
---|
2378 | LOG_MISC(("thread=%p resumed", node->thread));
|
---|
2379 | (void)JVMTI_FUNC_PTR(gdata->jvmti,ResumeThread)(gdata->jvmti, node->thread);
|
---|
2380 | node->frameGeneration++; /* Increment on each resume */
|
---|
2381 | }
|
---|
2382 | stepControl_clearRequest(node->thread, &node->currentStep);
|
---|
2383 | node->toBeResumed = JNI_FALSE;
|
---|
2384 | node->suspendCount = 0;
|
---|
2385 | node->suspendOnStart = JNI_FALSE;
|
---|
2386 |
|
---|
2387 | return JVMTI_ERROR_NONE;
|
---|
2388 | }
|
---|
2389 |
|
---|
2390 | void
|
---|
2391 | threadControl_reset(void)
|
---|
2392 | {
|
---|
2393 | JNIEnv *env;
|
---|
2394 |
|
---|
2395 | env = getEnv();
|
---|
2396 | eventHandler_lock(); /* for proper lock order */
|
---|
2397 | debugMonitorEnter(threadLock);
|
---|
2398 | (void)enumerateOverThreadList(env, &runningThreads, resetHelper, NULL);
|
---|
2399 | (void)enumerateOverThreadList(env, &otherThreads, resetHelper, NULL);
|
---|
2400 |
|
---|
2401 | removeResumed(env, &otherThreads);
|
---|
2402 |
|
---|
2403 | freeDeferredEventModes(env);
|
---|
2404 |
|
---|
2405 | suspendAllCount = 0;
|
---|
2406 |
|
---|
2407 | /* Everything should have been resumed */
|
---|
2408 | JDI_ASSERT(otherThreads.first == NULL);
|
---|
2409 |
|
---|
2410 | debugMonitorExit(threadLock);
|
---|
2411 | eventHandler_unlock();
|
---|
2412 | }
|
---|
2413 |
|
---|
2414 | jvmtiEventMode
|
---|
2415 | threadControl_getInstructionStepMode(jthread thread)
|
---|
2416 | {
|
---|
2417 | ThreadNode *node;
|
---|
2418 | jvmtiEventMode mode;
|
---|
2419 |
|
---|
2420 | mode = JVMTI_DISABLE;
|
---|
2421 |
|
---|
2422 | debugMonitorEnter(threadLock);
|
---|
2423 | node = findThread(&runningThreads, thread);
|
---|
2424 | if (node != NULL) {
|
---|
2425 | mode = node->instructionStepMode;
|
---|
2426 | }
|
---|
2427 | debugMonitorExit(threadLock);
|
---|
2428 | return mode;
|
---|
2429 | }
|
---|
2430 |
|
---|
2431 | jvmtiError
|
---|
2432 | threadControl_setEventMode(jvmtiEventMode mode, EventIndex ei, jthread thread)
|
---|
2433 | {
|
---|
2434 | jvmtiError error;
|
---|
2435 |
|
---|
2436 | /* Global event */
|
---|
2437 | if ( thread == NULL ) {
|
---|
2438 | error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventNotificationMode)
|
---|
2439 | (gdata->jvmti, mode, eventIndex2jvmti(ei), thread);
|
---|
2440 | } else {
|
---|
2441 | /* Thread event */
|
---|
2442 | ThreadNode *node;
|
---|
2443 |
|
---|
2444 | debugMonitorEnter(threadLock);
|
---|
2445 | {
|
---|
2446 | node = findThread(&runningThreads, thread);
|
---|
2447 | if ((node == NULL) || (!node->isStarted)) {
|
---|
2448 | JNIEnv *env;
|
---|
2449 |
|
---|
2450 | env = getEnv();
|
---|
2451 | error = addDeferredEventMode(env, mode, ei, thread);
|
---|
2452 | } else {
|
---|
2453 | error = threadSetEventNotificationMode(node,
|
---|
2454 | mode, ei, thread);
|
---|
2455 | }
|
---|
2456 | }
|
---|
2457 | debugMonitorExit(threadLock);
|
---|
2458 |
|
---|
2459 | }
|
---|
2460 | return error;
|
---|
2461 | }
|
---|
2462 |
|
---|
2463 | /*
|
---|
2464 | * Returns the current thread, if the thread has generated at least
|
---|
2465 | * one event, and has not generated a thread end event.
|
---|
2466 | */
|
---|
2467 | jthread threadControl_currentThread(void)
|
---|
2468 | {
|
---|
2469 | jthread thread;
|
---|
2470 |
|
---|
2471 | debugMonitorEnter(threadLock);
|
---|
2472 | {
|
---|
2473 | ThreadNode *node;
|
---|
2474 |
|
---|
2475 | node = findThread(&runningThreads, NULL);
|
---|
2476 | thread = (node == NULL) ? NULL : node->thread;
|
---|
2477 | }
|
---|
2478 | debugMonitorExit(threadLock);
|
---|
2479 |
|
---|
2480 | return thread;
|
---|
2481 | }
|
---|
2482 |
|
---|
2483 | jlong
|
---|
2484 | threadControl_getFrameGeneration(jthread thread)
|
---|
2485 | {
|
---|
2486 | jlong frameGeneration = -1;
|
---|
2487 |
|
---|
2488 | debugMonitorEnter(threadLock);
|
---|
2489 | {
|
---|
2490 | ThreadNode *node;
|
---|
2491 |
|
---|
2492 | node = findThread(NULL, thread);
|
---|
2493 |
|
---|
2494 | if (node != NULL) {
|
---|
2495 | frameGeneration = node->frameGeneration;
|
---|
2496 | }
|
---|
2497 | }
|
---|
2498 | debugMonitorExit(threadLock);
|
---|
2499 |
|
---|
2500 | return frameGeneration;
|
---|
2501 | }
|
---|