History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/tools/jdi/ThreadReferenceImpl.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/tools/jdi/ThreadReferenceImpl.java+644−0
@@ -0,0 +1,644 @@
1/*
2 * Copyright (c) 1998, 2008, 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
26package com.sun.tools.jdi;
27
28import com.sun.jdi.*;
29import com.sun.jdi.request.BreakpointRequest;
30import java.util.*;
31import java.lang.ref.WeakReference;
32
33public class ThreadReferenceImpl extends ObjectReferenceImpl
34 implements ThreadReference, VMListener {
35 static final int SUSPEND_STATUS_SUSPENDED = 0x1;
36 static final int SUSPEND_STATUS_BREAK = 0x2;
37
38 private int suspendedZombieCount = 0;
39
40 /*
41 * Some objects can only be created while a thread is suspended and are valid
42 * only while the thread remains suspended. Examples are StackFrameImpl
43 * and MonitorInfoImpl. When the thread resumes, these objects have to be
44 * marked as invalid so that their methods can throw
45 * InvalidStackFrameException if they are called. To do this, such objects
46 * register themselves as listeners of the associated thread. When the
47 * thread is resumed, its listeners are notified and mark themselves
48 * invalid.
49 * Also, note that ThreadReferenceImpl itself caches some info that
50 * is valid only as long as the thread is suspended. When the thread
51 * is resumed, that cache must be purged.
52 * Lastly, note that ThreadReferenceImpl and its super, ObjectReferenceImpl
53 * cache some info that is only valid as long as the entire VM is suspended.
54 * If _any_ thread is resumed, this cache must be purged. To handle this,
55 * both ThreadReferenceImpl and ObjectReferenceImpl register themselves as
56 * VMListeners so that they get notified when all threads are suspended and
57 * when any thread is resumed.
58 */
59
60 // This is cached for the life of the thread
61 private ThreadGroupReference threadGroup;
62
63 // This is cached only while this one thread is suspended. Each time
64 // the thread is resumed, we abandon the current cache object and
65 // create a new intialized one.
66 private static class LocalCache {
67 JDWP.ThreadReference.Status status = null;
68 List<StackFrame> frames = null;
69 int framesStart = -1;
70 int framesLength = 0;
71 int frameCount = -1;
72 List<ObjectReference> ownedMonitors = null;
73 List<MonitorInfo> ownedMonitorsInfo = null;
74 ObjectReference contendedMonitor = null;
75 boolean triedCurrentContended = false;
76 }
77
78 /*
79 * The localCache instance var is set by resetLocalCache to an initialized
80 * object as shown above. This occurs when the ThreadReference
81 * object is created, and when the mirrored thread is resumed.
82 * The fields are then filled in by the relevant methods as they
83 * are called. A problem can occur if resetLocalCache is called
84 * (ie, a resume() is executed) at certain points in the execution
85 * of some of these methods - see 6751643. To avoid this, each
86 * method that wants to use this cache must make a local copy of
87 * this variable and use that. This means that each invocation of
88 * these methods will use a copy of the cache object that was in
89 * effect at the point that the copy was made; if a racy resume
90 * occurs, it won't affect the method's local copy. This means that
91 * the values returned by these calls may not match the state of
92 * the debuggee at the time the caller gets the values. EG,
93 * frameCount() is called and comes up with 5 frames. But before
94 * it returns this, a resume of the debuggee thread is executed in a
95 * different debugger thread. The thread is resumed and running at
96 * the time that the value 5 is returned. Or even worse, the thread
97 * could be suspended again and have a different number of frames, eg, 24,
98 * but this call will still return 5.
99 */
100 private LocalCache localCache;
101
102 private void resetLocalCache() {
103 localCache = new LocalCache();
104 }
105
106 // This is cached only while all threads in the VM are suspended
107 // Yes, someone could change the name of a thread while it is suspended.
108 private static class Cache extends ObjectReferenceImpl.Cache {
109 String name = null;
110 }
111 protected ObjectReferenceImpl.Cache newCache() {
112 return new Cache();
113 }
114
115 // Listeners - synchronized on vm.state()
116 private List<WeakReference<ThreadListener>> listeners = new ArrayList<WeakReference<ThreadListener>>();
117
118
119 ThreadReferenceImpl(VirtualMachine aVm, long aRef) {
120 super(aVm,aRef);
121 resetLocalCache();
122 vm.state().addListener(this);
123 }
124
125 protected String description() {
126 return "ThreadReference " + uniqueID();
127 }
128
129 /*
130 * VMListener implementation
131 */
132 public boolean vmNotSuspended(VMAction action) {
133 if (action.resumingThread() == null) {
134 // all threads are being resumed
135 synchronized (vm.state()) {
136 processThreadAction(new ThreadAction(this,
137 ThreadAction.THREAD_RESUMABLE));
138 }
139
140 }
141
142 /*
143 * Othewise, only one thread is being resumed:
144 * if it is us,
145 * we have already done our processThreadAction to notify our
146 * listeners when we processed the resume.
147 * if it is not us,
148 * we don't want to notify our listeners
149 * because we are not being resumed.
150 */
151 return super.vmNotSuspended(action);
152 }
153
154 /**
155 * Note that we only cache the name string while the entire VM is suspended
156 * because the name can change via Thread.setName arbitrarily while this
157 * thread is running.
158 */
159 public String name() {
160 String name = null;
161 try {
162 Cache local = (Cache)getCache();
163
164 if (local != null) {
165 name = local.name;
166 }
167 if (name == null) {
168 name = JDWP.ThreadReference.Name.process(vm, this)
169 .threadName;
170 if (local != null) {
171 local.name = name;
172 }
173 }
174 } catch (JDWPException exc) {
175 throw exc.toJDIException();
176 }
177 return name;
178 }
179
180 /*
181 * Sends a command to the back end which is defined to do an
182 * implicit vm-wide resume.
183 */
184 PacketStream sendResumingCommand(CommandSender sender) {
185 synchronized (vm.state()) {
186 processThreadAction(new ThreadAction(this,
187 ThreadAction.THREAD_RESUMABLE));
188 return sender.send();
189 }
190 }
191
192 public void suspend() {
193 try {
194 JDWP.ThreadReference.Suspend.process(vm, this);
195 } catch (JDWPException exc) {
196 throw exc.toJDIException();
197 }
198 // Don't consider the thread suspended yet. On reply, notifySuspend()
199 // will be called.
200 }
201
202 public void resume() {
203 /*
204 * If it's a zombie, we can just update internal state without
205 * going to back end.
206 */
207 if (suspendedZombieCount > 0) {
208 suspendedZombieCount--;
209 return;
210 }
211
212 PacketStream stream;
213 synchronized (vm.state()) {
214 processThreadAction(new ThreadAction(this,
215 ThreadAction.THREAD_RESUMABLE));
216 stream = JDWP.ThreadReference.Resume.enqueueCommand(vm, this);
217 }
218 try {
219 JDWP.ThreadReference.Resume.waitForReply(vm, stream);
220 } catch (JDWPException exc) {
221 throw exc.toJDIException();
222 }
223 }
224
225 public int suspendCount() {
226 /*
227 * If it's a zombie, we maintain the count in the front end.
228 */
229 if (suspendedZombieCount > 0) {
230 return suspendedZombieCount;
231 }
232
233 try {
234 return JDWP.ThreadReference.SuspendCount.process(vm, this).suspendCount;
235 } catch (JDWPException exc) {
236 throw exc.toJDIException();
237 }
238 }
239
240 public void stop(ObjectReference throwable) throws InvalidTypeException {
241 validateMirror(throwable);
242 // Verify that the given object is a Throwable instance
243 List list = vm.classesByName("java.lang.Throwable");
244 ClassTypeImpl throwableClass = (ClassTypeImpl)list.get(0);
245 if ((throwable == null) ||
246 !throwableClass.isAssignableFrom(throwable)) {
247 throw new InvalidTypeException("Not an instance of Throwable");
248 }
249
250 try {
251 JDWP.ThreadReference.Stop.process(vm, this,
252 (ObjectReferenceImpl)throwable);
253 } catch (JDWPException exc) {
254 throw exc.toJDIException();
255 }
256 }
257
258 public void interrupt() {
259 try {
260 JDWP.ThreadReference.Interrupt.process(vm, this);
261 } catch (JDWPException exc) {
262 throw exc.toJDIException();
263 }
264 }
265
266 private JDWP.ThreadReference.Status jdwpStatus() {
267 LocalCache snapshot = localCache;
268 JDWP.ThreadReference.Status myStatus = snapshot.status;
269 try {
270 if (myStatus == null) {
271 myStatus = JDWP.ThreadReference.Status.process(vm, this);
272 if ((myStatus.suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0) {
273 // thread is suspended, we can cache the status.
274 snapshot.status = myStatus;
275 }
276 }
277 } catch (JDWPException exc) {
278 throw exc.toJDIException();
279 }
280 return myStatus;
281 }
282
283 public int status() {
284 return jdwpStatus().threadStatus;
285 }
286
287 public boolean isSuspended() {
288 return ((suspendedZombieCount > 0) ||
289 ((jdwpStatus().suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0));
290 }
291
292 public boolean isAtBreakpoint() {
293 /*
294 * TO DO: This fails to take filters into account.
295 */
296 try {
297 StackFrame frame = frame(0);
298 Location location = frame.location();
299 List requests = vm.eventRequestManager().breakpointRequests();
300 Iterator iter = requests.iterator();
301 while (iter.hasNext()) {
302 BreakpointRequest request = (BreakpointRequest)iter.next();
303 if (location.equals(request.location())) {
304 return true;
305 }
306 }
307 return false;
308 } catch (IndexOutOfBoundsException iobe) {
309 return false; // no frames on stack => not at breakpoint
310 } catch (IncompatibleThreadStateException itse) {
311 // Per the javadoc, not suspended => return false
312 return false;
313 }
314 }
315
316 public ThreadGroupReference threadGroup() {
317 /*
318 * Thread group can't change, so it's cached once and for all.
319 */
320 if (threadGroup == null) {
321 try {
322 threadGroup = JDWP.ThreadReference.ThreadGroup.
323 process(vm, this).group;
324 } catch (JDWPException exc) {
325 throw exc.toJDIException();
326 }
327 }
328 return threadGroup;
329 }
330
331 public int frameCount() throws IncompatibleThreadStateException {
332 LocalCache snapshot = localCache;
333 try {
334 if (snapshot.frameCount == -1) {
335 snapshot.frameCount = JDWP.ThreadReference.FrameCount
336 .process(vm, this).frameCount;
337 }
338 } catch (JDWPException exc) {
339 switch (exc.errorCode()) {
340 case JDWP.Error.THREAD_NOT_SUSPENDED:
341 case JDWP.Error.INVALID_THREAD: /* zombie */
342 throw new IncompatibleThreadStateException();
343 default:
344 throw exc.toJDIException();
345 }
346 }
347 return snapshot.frameCount;
348 }
349
350 public List<StackFrame> frames() throws IncompatibleThreadStateException {
351 return privateFrames(0, -1);
352 }
353
354 public StackFrame frame(int index) throws IncompatibleThreadStateException {
355 List list = privateFrames(index, 1);
356 return (StackFrame)list.get(0);
357 }
358
359 /**
360 * Is the requested subrange within what has been retrieved?
361 * local is known to be non-null. Should only be called from
362 * a sync method.
363 */
364 private boolean isSubrange(LocalCache snapshot,
365 int start, int length) {
366 if (start < snapshot.framesStart) {
367 return false;
368 }
369 if (length == -1) {
370 return (snapshot.framesLength == -1);
371 }
372 if (snapshot.framesLength == -1) {
373 if ((start + length) > (snapshot.framesStart +
374 snapshot.frames.size())) {
375 throw new IndexOutOfBoundsException();
376 }
377 return true;
378 }
379 return ((start + length) <= (snapshot.framesStart + snapshot.framesLength));
380 }
381
382 public List<StackFrame> frames(int start, int length)
383 throws IncompatibleThreadStateException {
384 if (length < 0) {
385 throw new IndexOutOfBoundsException(
386 "length must be greater than or equal to zero");
387 }
388 return privateFrames(start, length);
389 }
390
391 /**
392 * Private version of frames() allows "-1" to specify all
393 * remaining frames.
394 */
395 synchronized private List<StackFrame> privateFrames(int start, int length)
396 throws IncompatibleThreadStateException {
397
398 // Lock must be held while creating stack frames so if that two threads
399 // do this at the same time, one won't clobber the subset created by the other.
400 LocalCache snapshot = localCache;
401 try {
402 if (snapshot.frames == null || !isSubrange(snapshot, start, length)) {
403 JDWP.ThreadReference.Frames.Frame[] jdwpFrames
404 = JDWP.ThreadReference.Frames.
405 process(vm, this, start, length).frames;
406 int count = jdwpFrames.length;
407 snapshot.frames = new ArrayList<StackFrame>(count);
408
409 for (int i = 0; i<count; i++) {
410 if (jdwpFrames[i].location == null) {
411 throw new InternalException("Invalid frame location");
412 }
413 StackFrame frame = new StackFrameImpl(vm, this,
414 jdwpFrames[i].frameID,
415 jdwpFrames[i].location);
416 // Add to the frame list
417 snapshot.frames.add(frame);
418 }
419 snapshot.framesStart = start;
420 snapshot.framesLength = length;
421 return Collections.unmodifiableList(snapshot.frames);
422 } else {
423 int fromIndex = start - snapshot.framesStart;
424 int toIndex;
425 if (length == -1) {
426 toIndex = snapshot.frames.size() - fromIndex;
427 } else {
428 toIndex = fromIndex + length;
429 }
430 return Collections.unmodifiableList(snapshot.frames.subList(fromIndex, toIndex));
431 }
432 } catch (JDWPException exc) {
433 switch (exc.errorCode()) {
434 case JDWP.Error.THREAD_NOT_SUSPENDED:
435 case JDWP.Error.INVALID_THREAD: /* zombie */
436 throw new IncompatibleThreadStateException();
437 default:
438 throw exc.toJDIException();
439 }
440 }
441 }
442
443 public List<ObjectReference> ownedMonitors() throws IncompatibleThreadStateException {
444 LocalCache snapshot = localCache;
445 try {
446 if (snapshot.ownedMonitors == null) {
447 snapshot.ownedMonitors = Arrays.asList(
448 (ObjectReference[])JDWP.ThreadReference.OwnedMonitors.
449 process(vm, this).owned);
450 if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
451 vm.printTrace(description() +
452 " temporarily caching owned monitors"+
453 " (count = " + snapshot.ownedMonitors.size() + ")");
454 }
455 }
456 } catch (JDWPException exc) {
457 switch (exc.errorCode()) {
458 case JDWP.Error.THREAD_NOT_SUSPENDED:
459 case JDWP.Error.INVALID_THREAD: /* zombie */
460 throw new IncompatibleThreadStateException();
461 default:
462 throw exc.toJDIException();
463 }
464 }
465 return snapshot.ownedMonitors;
466 }
467
468 public ObjectReference currentContendedMonitor()
469 throws IncompatibleThreadStateException {
470 LocalCache snapshot = localCache;
471 try {
472 if (snapshot.contendedMonitor == null &&
473 !snapshot.triedCurrentContended) {
474 snapshot.contendedMonitor = JDWP.ThreadReference.CurrentContendedMonitor.
475 process(vm, this).monitor;
476 snapshot.triedCurrentContended = true;
477 if ((snapshot.contendedMonitor != null) &&
478 ((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) {
479 vm.printTrace(description() +
480 " temporarily caching contended monitor"+
481 " (id = " + snapshot.contendedMonitor.uniqueID() + ")");
482 }
483 }
484 } catch (JDWPException exc) {
485 switch (exc.errorCode()) {
486 case JDWP.Error.THREAD_NOT_SUSPENDED:
487 case JDWP.Error.INVALID_THREAD: /* zombie */
488 throw new IncompatibleThreadStateException();
489 default:
490 throw exc.toJDIException();
491 }
492 }
493 return snapshot.contendedMonitor;
494 }
495
496 public List<MonitorInfo> ownedMonitorsAndFrames() throws IncompatibleThreadStateException {
497 LocalCache snapshot = localCache;
498 try {
499 if (snapshot.ownedMonitorsInfo == null) {
500 JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo;
501 minfo = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.process(vm, this).owned;
502
503 snapshot.ownedMonitorsInfo = new ArrayList<MonitorInfo>(minfo.length);
504
505 for (int i=0; i < minfo.length; i++) {
506 JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi =
507 minfo[i];
508 MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth);
509 snapshot.ownedMonitorsInfo.add(mon);
510 }
511
512 if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
513 vm.printTrace(description() +
514 " temporarily caching owned monitors"+
515 " (count = " + snapshot.ownedMonitorsInfo.size() + ")");
516 }
517 }
518
519 } catch (JDWPException exc) {
520 switch (exc.errorCode()) {
521 case JDWP.Error.THREAD_NOT_SUSPENDED:
522 case JDWP.Error.INVALID_THREAD: /* zombie */
523 throw new IncompatibleThreadStateException();
524 default:
525 throw exc.toJDIException();
526 }
527 }
528 return snapshot.ownedMonitorsInfo;
529 }
530
531 public void popFrames(StackFrame frame) throws IncompatibleThreadStateException {
532 // Note that interface-wise this functionality belongs
533 // here in ThreadReference, but implementation-wise it
534 // belongs in StackFrame, so we just forward it.
535 if (!frame.thread().equals(this)) {
536 throw new IllegalArgumentException("frame does not belong to this thread");
537 }
538 if (!vm.canPopFrames()) {
539 throw new UnsupportedOperationException(
540 "target does not support popping frames");
541 }
542 ((StackFrameImpl)frame).pop();
543 }
544
545 public void forceEarlyReturn(Value returnValue) throws InvalidTypeException,
546 ClassNotLoadedException,
547 IncompatibleThreadStateException {
548 if (!vm.canForceEarlyReturn()) {
549 throw new UnsupportedOperationException(
550 "target does not support the forcing of a method to return early");
551 }
552
553 validateMirrorOrNull(returnValue);
554
555 StackFrameImpl sf;
556 try {
557 sf = (StackFrameImpl)frame(0);
558 } catch (IndexOutOfBoundsException exc) {
559 throw new InvalidStackFrameException("No more frames on the stack");
560 }
561 sf.validateStackFrame();
562 MethodImpl meth = (MethodImpl)sf.location().method();
563 ValueImpl convertedValue = ValueImpl.prepareForAssignment(returnValue,
564 meth.getReturnValueContainer());
565
566 try {
567 JDWP.ThreadReference.ForceEarlyReturn.process(vm, this, convertedValue);
568 } catch (JDWPException exc) {
569 switch (exc.errorCode()) {
570 case JDWP.Error.OPAQUE_FRAME:
571 throw new NativeMethodException();
572 case JDWP.Error.THREAD_NOT_SUSPENDED:
573 throw new IncompatibleThreadStateException(
574 "Thread not suspended");
575 case JDWP.Error.THREAD_NOT_ALIVE:
576 throw new IncompatibleThreadStateException(
577 "Thread has not started or has finished");
578 case JDWP.Error.NO_MORE_FRAMES:
579 throw new InvalidStackFrameException(
580 "No more frames on the stack");
581 default:
582 throw exc.toJDIException();
583 }
584 }
585 }
586
587 public String toString() {
588 return "instance of " + referenceType().name() +
589 "(name='" + name() + "', " + "id=" + uniqueID() + ")";
590 }
591
592 byte typeValueKey() {
593 return JDWP.Tag.THREAD;
594 }
595
596 void addListener(ThreadListener listener) {
597 synchronized (vm.state()) {
598 listeners.add(new WeakReference<ThreadListener>(listener));
599 }
600 }
601
602 void removeListener(ThreadListener listener) {
603 synchronized (vm.state()) {
604 Iterator iter = listeners.iterator();
605 while (iter.hasNext()) {
606 WeakReference ref = (WeakReference)iter.next();
607 if (listener.equals(ref.get())) {
608 iter.remove();
609 break;
610 }
611 }
612 }
613 }
614
615 /**
616 * Propagate the the thread state change information
617 * to registered listeners.
618 * Must be entered while synchronized on vm.state()
619 */
620 private void processThreadAction(ThreadAction action) {
621 synchronized (vm.state()) {
622 Iterator iter = listeners.iterator();
623 while (iter.hasNext()) {
624 WeakReference ref = (WeakReference)iter.next();
625 ThreadListener listener = (ThreadListener)ref.get();
626 if (listener != null) {
627 switch (action.id()) {
628 case ThreadAction.THREAD_RESUMABLE:
629 if (!listener.threadResumable(action)) {
630 iter.remove();
631 }
632 break;
633 }
634 } else {
635 // Listener is unreachable; clean up
636 iter.remove();
637 }
638 }
639
640 // Discard our local cache
641 resetLocalCache();
642 }
643 }
644}