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