Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/hotspot/agent/test/jdi/TestScaffold.java
38764 views
1
/*
2
* Copyright (c) 2002, 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.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*
23
*/
24
25
import com.sun.jdi.*;
26
import com.sun.jdi.request.*;
27
import com.sun.jdi.event.*;
28
import java.util.*;
29
import java.io.*;
30
31
/**
32
* Framework used by all JDI regression tests
33
*/
34
abstract public class TestScaffold extends TargetAdapter {
35
private boolean shouldTrace = false;
36
private VMConnection connection;
37
private VirtualMachine vm;
38
private EventRequestManager requestManager;
39
private List listeners = Collections.synchronizedList(new LinkedList());
40
41
/**
42
* We create a VMDeathRequest, SUSPEND_ALL, to sync the BE and FE.
43
*/
44
//private VMDeathRequest ourVMDeathRequest = null;
45
Object ourVMDeathRequest = null;
46
47
/**
48
* We create an ExceptionRequest, SUSPEND_NONE so that we can
49
* catch it and output a msg if an exception occurs in the
50
* debuggee.
51
*/
52
private ExceptionRequest ourExceptionRequest = null;
53
54
/**
55
* If we do catch an uncaught exception, we set this true
56
* so the testcase can find out if it wants to.
57
*/
58
private boolean exceptionCaught = false;
59
ThreadReference vmStartThread = null;
60
boolean vmDied = false;
61
boolean vmDisconnected = false;
62
final String[] args;
63
protected boolean testFailed = false;
64
65
static private class ArgInfo {
66
String targetVMArgs = "";
67
String targetAppCommandLine = "";
68
String connectorSpec = "com.sun.jdi.CommandLineLaunch:";
69
int traceFlags = 0;
70
}
71
72
/**
73
* An easy way to sleep for awhile
74
*/
75
public void mySleep(int millis) {
76
try {
77
Thread.sleep(millis);
78
} catch (InterruptedException ee) {
79
}
80
}
81
82
boolean getExceptionCaught() {
83
return exceptionCaught;
84
}
85
86
void setExceptionCaught(boolean value) {
87
exceptionCaught = value;
88
}
89
90
/**
91
* Return true if eventSet contains the VMDeathEvent for the request in
92
* the ourVMDeathRequest ivar.
93
*/
94
private boolean containsOurVMDeathRequest(EventSet eventSet) {
95
if (ourVMDeathRequest != null) {
96
Iterator myIter = eventSet.iterator();
97
while (myIter.hasNext()) {
98
Event myEvent = (Event)myIter.next();
99
if (!(myEvent instanceof VMDeathEvent)) {
100
// We assume that an EventSet contains only VMDeathEvents
101
// or no VMDeathEvents.
102
break;
103
}
104
if (ourVMDeathRequest.equals(myEvent.request())) {
105
return true;
106
}
107
}
108
}
109
return false;
110
}
111
112
/************************************************************************
113
* The following methods override those in our base class, TargetAdapter.
114
*************************************************************************/
115
116
/**
117
* Events handled directly by scaffold always resume (well, almost always)
118
*/
119
public void eventSetComplete(EventSet set) {
120
// The listener in connect(..) resumes after receiving our
121
// special VMDeathEvent. We can't also do the resume
122
// here or we will probably get a VMDisconnectedException
123
if (!containsOurVMDeathRequest(set)) {
124
traceln("TS: set.resume() called");
125
set.resume();
126
}
127
}
128
129
/**
130
* This method sets up default requests.
131
* Testcases can override this to change default behavior.
132
*/
133
protected void createDefaultEventRequests() {
134
createDefaultVMDeathRequest();
135
createDefaultExceptionRequest();
136
}
137
138
/**
139
* We want the BE to stop when it issues a VMDeathEvent in order to
140
* give the FE time to complete handling events that occured before
141
* the VMDeath. When we get the VMDeathEvent for this request in
142
* the listener in connect(), we will do a resume.
143
* If a testcase wants to do something special with VMDeathEvent's,
144
* then it should override this method with an empty method or
145
* whatever in order to suppress the automatic resume. The testcase
146
* will then be responsible for the handling of VMDeathEvents. It
147
* has to be sure that it does a resume if it gets a VMDeathEvent
148
* with SUSPEND_ALL, and it has to be sure that it doesn't do a
149
* resume after getting a VMDeath with SUSPEND_NONE (the automatically
150
* generated VMDeathEvent.)
151
*/
152
protected void createDefaultVMDeathRequest() {
153
// ourVMDeathRequest = requestManager.createVMDeathRequest();
154
// ourVMDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
155
// ourVMDeathRequest.enable();
156
}
157
158
/**
159
* This will allow us to print a warning if a debuggee gets an
160
* unexpected exception. The unexpected exception will be handled in
161
* the exceptionThrown method in the listener created in the connect()
162
* method.
163
* If a testcase does not want an uncaught exception to cause a
164
* msg, it must override this method.
165
*/
166
protected void createDefaultExceptionRequest() {
167
ourExceptionRequest = requestManager.createExceptionRequest(null,
168
false, true);
169
170
// We can't afford to make this be other than SUSPEND_NONE. Otherwise,
171
// it would have to be resumed. If our connect() listener resumes it,
172
// what about the case where the EventSet contains other events with
173
// SUSPEND_ALL and there are other listeners who expect the BE to still
174
// be suspended when their handlers get called?
175
ourExceptionRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
176
ourExceptionRequest.enable();
177
}
178
179
private class EventHandler implements Runnable {
180
EventHandler() {
181
Thread thread = new Thread(this);
182
thread.setDaemon(true);
183
thread.start();
184
}
185
186
private void notifyEvent(TargetListener listener, Event event) {
187
if (event instanceof BreakpointEvent) {
188
listener.breakpointReached((BreakpointEvent)event);
189
} else if (event instanceof ExceptionEvent) {
190
listener.exceptionThrown((ExceptionEvent)event);
191
} else if (event instanceof StepEvent) {
192
listener.stepCompleted((StepEvent)event);
193
} else if (event instanceof ClassPrepareEvent) {
194
listener.classPrepared((ClassPrepareEvent)event);
195
} else if (event instanceof ClassUnloadEvent) {
196
listener.classUnloaded((ClassUnloadEvent)event);
197
} else if (event instanceof MethodEntryEvent) {
198
listener.methodEntered((MethodEntryEvent)event);
199
} else if (event instanceof MethodExitEvent) {
200
listener.methodExited((MethodExitEvent)event);
201
} else if (event instanceof AccessWatchpointEvent) {
202
listener.fieldAccessed((AccessWatchpointEvent)event);
203
} else if (event instanceof ModificationWatchpointEvent) {
204
listener.fieldModified((ModificationWatchpointEvent)event);
205
} else if (event instanceof ThreadStartEvent) {
206
listener.threadStarted((ThreadStartEvent)event);
207
} else if (event instanceof ThreadDeathEvent) {
208
listener.threadDied((ThreadDeathEvent)event);
209
} else if (event instanceof VMStartEvent) {
210
listener.vmStarted((VMStartEvent)event);
211
} else if (event instanceof VMDeathEvent) {
212
listener.vmDied((VMDeathEvent)event);
213
} else if (event instanceof VMDisconnectEvent) {
214
listener.vmDisconnected((VMDisconnectEvent)event);
215
} else {
216
throw new InternalError("Unknown event type: " + event.getClass());
217
}
218
}
219
220
private void traceSuspendPolicy(int policy) {
221
if (shouldTrace) {
222
switch (policy) {
223
case EventRequest.SUSPEND_NONE:
224
traceln("TS: eventHandler: suspend = SUSPEND_NONE");
225
break;
226
case EventRequest.SUSPEND_ALL:
227
traceln("TS: eventHandler: suspend = SUSPEND_ALL");
228
break;
229
case EventRequest.SUSPEND_EVENT_THREAD:
230
traceln("TS: eventHandler: suspend = SUSPEND_EVENT_THREAD");
231
break;
232
}
233
}
234
}
235
236
public void run() {
237
boolean connected = true;
238
do {
239
try {
240
EventSet set = vm.eventQueue().remove();
241
traceSuspendPolicy(set.suspendPolicy());
242
synchronized (listeners) {
243
ListIterator iter = listeners.listIterator();
244
while (iter.hasNext()) {
245
TargetListener listener = (TargetListener)iter.next();
246
traceln("TS: eventHandler: listener = " + listener);
247
listener.eventSetReceived(set);
248
if (listener.shouldRemoveListener()) {
249
iter.remove();
250
} else {
251
Iterator jter = set.iterator();
252
while (jter.hasNext()) {
253
Event event = (Event)jter.next();
254
traceln("TS: eventHandler: event = " + event.getClass());
255
256
if (event instanceof VMDisconnectEvent) {
257
connected = false;
258
}
259
listener.eventReceived(event);
260
if (listener.shouldRemoveListener()) {
261
iter.remove();
262
break;
263
}
264
notifyEvent(listener, event);
265
if (listener.shouldRemoveListener()) {
266
iter.remove();
267
break;
268
}
269
}
270
traceln("TS: eventHandler: end of events loop");
271
if (!listener.shouldRemoveListener()) {
272
traceln("TS: eventHandler: calling ESC");
273
listener.eventSetComplete(set);
274
if (listener.shouldRemoveListener()) {
275
iter.remove();
276
}
277
}
278
}
279
traceln("TS: eventHandler: end of listeners loop");
280
}
281
}
282
} catch (InterruptedException e) {
283
traceln("TS: eventHandler: InterruptedException");
284
} catch (Exception e) {
285
failure("FAILED: Exception occured in eventHandler: " + e);
286
e.printStackTrace();
287
connected = false;
288
synchronized(TestScaffold.this) {
289
// This will make the waiters such as waitForVMDisconnect
290
// exit their wait loops.
291
vmDisconnected = true;
292
TestScaffold.this.notifyAll();
293
}
294
}
295
traceln("TS: eventHandler: End of outerloop");
296
} while (connected);
297
traceln("TS: eventHandler: finished");
298
}
299
}
300
301
/**
302
* Constructor
303
*/
304
public TestScaffold(String[] args) {
305
this.args = args;
306
}
307
308
public void enableScaffoldTrace() {
309
this.shouldTrace = true;
310
}
311
312
public void disableScaffoldTrace() {
313
this.shouldTrace = false;
314
}
315
316
317
protected void startUp(String targetName) {
318
List argList = new ArrayList(Arrays.asList(args));
319
argList.add(targetName);
320
println("run args: " + argList);
321
connect((String[]) argList.toArray(args));
322
waitForVMStart();
323
}
324
325
protected BreakpointEvent startToMain(String targetName) {
326
startUp(targetName);
327
traceln("TS: back from startUp");
328
BreakpointEvent bpr = resumeTo(targetName, "main", "([Ljava/lang/String;)V");
329
waitForInput();
330
return bpr;
331
}
332
333
protected void waitForInput() {
334
if (System.getProperty("jpda.wait") != null) {
335
try {
336
System.err.println("Press <enter> to continue");
337
System.in.read();
338
System.err.println("running...");
339
340
} catch(Exception e) {
341
}
342
}
343
}
344
345
/*
346
* Test cases should implement tests in runTests and should
347
* initiate testing by calling run().
348
*/
349
abstract protected void runTests() throws Exception;
350
351
final public void startTests() throws Exception {
352
try {
353
runTests();
354
} finally {
355
shutdown();
356
}
357
}
358
359
protected void println(String str) {
360
System.err.println(str);
361
}
362
363
protected void print(String str) {
364
System.err.print(str);
365
}
366
367
protected void traceln(String str) {
368
if (shouldTrace) {
369
println(str);
370
}
371
}
372
373
protected void failure(String str) {
374
println(str);
375
testFailed = true;
376
}
377
378
private ArgInfo parseArgs(String args[]) {
379
ArgInfo argInfo = new ArgInfo();
380
for (int i = 0; i < args.length; i++) {
381
if (args[i].equals("-connect")) {
382
i++;
383
argInfo.connectorSpec = args[i];
384
} else if (args[i].equals("-trace")) {
385
i++;
386
argInfo.traceFlags = Integer.decode(args[i]).intValue();
387
} else if (args[i].startsWith("-J")) {
388
argInfo.targetVMArgs += (args[i].substring(2) + ' ');
389
390
/*
391
* classpath can span two arguments so we need to handle
392
* it specially.
393
*/
394
if (args[i].equals("-J-classpath")) {
395
i++;
396
argInfo.targetVMArgs += (args[i] + ' ');
397
}
398
} else {
399
argInfo.targetAppCommandLine += (args[i] + ' ');
400
}
401
}
402
return argInfo;
403
}
404
405
/**
406
* This is called to connect to a debuggee VM. It starts the VM and
407
* installs a listener to catch VMStartEvent, our default events, and
408
* VMDisconnectedEvent. When these events appear, that is remembered
409
* and waiters are notified.
410
* This is normally called in the main thread of the test case.
411
* It starts up an EventHandler thread that gets events coming in
412
* from the debuggee and distributes them to listeners. That thread
413
* keeps running until a VMDisconnectedEvent occurs or some exception
414
* occurs during its processing.
415
*
416
* The 'listenUntilVMDisconnect' method adds 'this' as a listener.
417
* This means that 'this's vmDied method will get called. This has a
418
* default impl in TargetAdapter.java which can be overridden in the
419
* testcase.
420
*
421
* waitForRequestedEvent also adds an adaptor listener that listens
422
* for the particular event it is supposed to wait for (and it also
423
* catches VMDisconnectEvents.) This listener is removed once
424
* its eventReceived method is called.
425
* waitForRequestedEvent is called by most of the methods to do bkpts,
426
* etc.
427
*/
428
public void connect(String args[]) {
429
ArgInfo argInfo = parseArgs(args);
430
431
argInfo.targetVMArgs += VMConnection.getDebuggeeVMOptions();
432
connection = new VMConnection(argInfo.connectorSpec,
433
argInfo.traceFlags);
434
435
addListener(new TargetAdapter() {
436
public void eventSetComplete(EventSet set) {
437
if (TestScaffold.this.containsOurVMDeathRequest(set)) {
438
traceln("TS: connect: set.resume() called");
439
set.resume();
440
441
// Note that we want to do the above resume before
442
// waking up any sleepers.
443
synchronized(TestScaffold.this) {
444
TestScaffold.this.notifyAll();
445
}
446
}
447
}
448
449
public void vmStarted(VMStartEvent event) {
450
synchronized(TestScaffold.this) {
451
vmStartThread = event.thread();
452
TestScaffold.this.notifyAll();
453
}
454
}
455
/**
456
* By default, we catch uncaught exceptions and print a msg.
457
* The testcase must override the createDefaultExceptionRequest
458
* method if it doesn't want this behavior.
459
*/
460
public void exceptionThrown(ExceptionEvent event) {
461
if (TestScaffold.this.ourExceptionRequest != null &&
462
TestScaffold.this.ourExceptionRequest.equals(
463
event.request())) {
464
println("Note: Unexpected Debuggee Exception: " +
465
event.exception().referenceType().name() +
466
" at line " + event.location().lineNumber());
467
TestScaffold.this.exceptionCaught = true;
468
}
469
}
470
471
public void vmDied(VMDeathEvent event) {
472
vmDied = true;
473
traceln("TS: vmDied called");
474
}
475
476
public void vmDisconnected(VMDisconnectEvent event) {
477
synchronized(TestScaffold.this) {
478
vmDisconnected = true;
479
TestScaffold.this.notifyAll();
480
}
481
}
482
});
483
if (connection.connector().name().equals("com.sun.jdi.CommandLineLaunch")) {
484
if (argInfo.targetVMArgs.length() > 0) {
485
if (connection.connectorArg("options").length() > 0) {
486
throw new IllegalArgumentException("VM options in two places");
487
}
488
connection.setConnectorArg("options", argInfo.targetVMArgs);
489
}
490
if (argInfo.targetAppCommandLine.length() > 0) {
491
if (connection.connectorArg("main").length() > 0) {
492
throw new IllegalArgumentException("Command line in two places");
493
}
494
connection.setConnectorArg("main", argInfo.targetAppCommandLine);
495
}
496
}
497
498
vm = connection.open();
499
requestManager = vm.eventRequestManager();
500
createDefaultEventRequests();
501
new EventHandler();
502
}
503
504
505
public VirtualMachine vm() {
506
return vm;
507
}
508
509
public EventRequestManager eventRequestManager() {
510
return requestManager;
511
}
512
513
public void addListener(TargetListener listener) {
514
traceln("TS: Adding listener " + listener);
515
listeners.add(listener);
516
}
517
518
public void removeListener(TargetListener listener) {
519
traceln("TS: Removing listener " + listener);
520
listeners.remove(listener);
521
}
522
523
524
protected void listenUntilVMDisconnect() {
525
try {
526
addListener (this);
527
} catch (Exception ex){
528
ex.printStackTrace();
529
testFailed = true;
530
} finally {
531
// Allow application to complete and shut down
532
resumeToVMDisconnect();
533
}
534
}
535
536
public synchronized ThreadReference waitForVMStart() {
537
while ((vmStartThread == null) && !vmDisconnected) {
538
try {
539
wait();
540
} catch (InterruptedException e) {
541
}
542
}
543
544
if (vmStartThread == null) {
545
throw new VMDisconnectedException();
546
}
547
548
return vmStartThread;
549
}
550
551
public synchronized void waitForVMDisconnect() {
552
traceln("TS: waitForVMDisconnect");
553
while (!vmDisconnected) {
554
try {
555
wait();
556
} catch (InterruptedException e) {
557
}
558
}
559
traceln("TS: waitForVMDisconnect: done");
560
}
561
562
public Event waitForRequestedEvent(final EventRequest request) {
563
class EventNotification {
564
Event event;
565
boolean disconnected = false;
566
}
567
final EventNotification en = new EventNotification();
568
569
TargetAdapter adapter = new TargetAdapter() {
570
public void eventReceived(Event event) {
571
if (request.equals(event.request())) {
572
traceln("TS:Listener2: got requested event");
573
synchronized (en) {
574
en.event = event;
575
en.notifyAll();
576
}
577
removeThisListener();
578
} else if (event instanceof VMDisconnectEvent) {
579
traceln("TS:Listener2: got VMDisconnectEvent");
580
synchronized (en) {
581
en.disconnected = true;
582
en.notifyAll();
583
}
584
removeThisListener();
585
}
586
}
587
};
588
589
addListener(adapter);
590
591
try {
592
synchronized (en) {
593
traceln("TS: waitForRequestedEvent: vm.resume called");
594
vm.resume();
595
596
while (!en.disconnected && (en.event == null)) {
597
en.wait();
598
}
599
}
600
} catch (InterruptedException e) {
601
return null;
602
}
603
604
if (en.disconnected) {
605
throw new RuntimeException("VM Disconnected before requested event occurred");
606
}
607
return en.event;
608
}
609
610
private StepEvent doStep(ThreadReference thread, int gran, int depth) {
611
final StepRequest sr =
612
requestManager.createStepRequest(thread, gran, depth);
613
614
sr.addClassExclusionFilter("java.*");
615
sr.addClassExclusionFilter("sun.*");
616
sr.addClassExclusionFilter("com.sun.*");
617
sr.addCountFilter(1);
618
sr.enable();
619
StepEvent retEvent = (StepEvent)waitForRequestedEvent(sr);
620
requestManager.deleteEventRequest(sr);
621
return retEvent;
622
}
623
624
public StepEvent stepIntoInstruction(ThreadReference thread) {
625
return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);
626
}
627
628
public StepEvent stepIntoLine(ThreadReference thread) {
629
return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO);
630
}
631
632
public StepEvent stepOverInstruction(ThreadReference thread) {
633
return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER);
634
}
635
636
public StepEvent stepOverLine(ThreadReference thread) {
637
return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER);
638
}
639
640
public StepEvent stepOut(ThreadReference thread) {
641
return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT);
642
}
643
644
public BreakpointEvent resumeTo(Location loc) {
645
final BreakpointRequest request =
646
requestManager.createBreakpointRequest(loc);
647
request.addCountFilter(1);
648
request.enable();
649
return (BreakpointEvent)waitForRequestedEvent(request);
650
}
651
652
public ReferenceType findReferenceType(String name) {
653
List rts = vm.classesByName(name);
654
Iterator iter = rts.iterator();
655
while (iter.hasNext()) {
656
ReferenceType rt = (ReferenceType)iter.next();
657
if (rt.name().equals(name)) {
658
return rt;
659
}
660
}
661
return null;
662
}
663
664
public Method findMethod(ReferenceType rt, String name, String signature) {
665
List methods = rt.methods();
666
Iterator iter = methods.iterator();
667
while (iter.hasNext()) {
668
Method method = (Method)iter.next();
669
if (method.name().equals(name) &&
670
method.signature().equals(signature)) {
671
return method;
672
}
673
}
674
return null;
675
}
676
677
public Location findLocation(ReferenceType rt, int lineNumber)
678
throws AbsentInformationException {
679
List locs = rt.locationsOfLine(lineNumber);
680
if (locs.size() == 0) {
681
throw new IllegalArgumentException("Bad line number");
682
} else if (locs.size() > 1) {
683
throw new IllegalArgumentException("Line number has multiple locations");
684
}
685
686
return (Location)locs.get(0);
687
}
688
689
public BreakpointEvent resumeTo(String clsName, String methodName,
690
String methodSignature) {
691
ReferenceType rt = findReferenceType(clsName);
692
if (rt == null) {
693
rt = resumeToPrepareOf(clsName).referenceType();
694
}
695
696
Method method = findMethod(rt, methodName, methodSignature);
697
if (method == null) {
698
throw new IllegalArgumentException("Bad method name/signature");
699
}
700
701
return resumeTo(method.location());
702
}
703
704
public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException {
705
ReferenceType rt = findReferenceType(clsName);
706
if (rt == null) {
707
rt = resumeToPrepareOf(clsName).referenceType();
708
}
709
710
return resumeTo(findLocation(rt, lineNumber));
711
}
712
713
public ClassPrepareEvent resumeToPrepareOf(String className) {
714
final ClassPrepareRequest request =
715
requestManager.createClassPrepareRequest();
716
request.addClassFilter(className);
717
request.addCountFilter(1);
718
request.enable();
719
return (ClassPrepareEvent)waitForRequestedEvent(request);
720
}
721
722
public void resumeToVMDisconnect() {
723
try {
724
traceln("TS: resumeToVMDisconnect: vm.resume called");
725
vm.resume();
726
} catch (VMDisconnectedException e) {
727
// clean up below
728
}
729
waitForVMDisconnect();
730
}
731
732
public void shutdown() {
733
shutdown(null);
734
}
735
736
public void shutdown(String message) {
737
traceln("TS: shutdown: vmDied= " + vmDied +
738
", vmDisconnected= " + vmDisconnected +
739
", connection = " + connection);
740
741
if ((connection != null)) {
742
try {
743
connection.disposeVM();
744
} catch (VMDisconnectedException e) {
745
// Shutting down after the VM has gone away. This is
746
// not an error, and we just ignore it.
747
}
748
} else {
749
traceln("TS: shutdown: disposeVM not called");
750
}
751
if (message != null) {
752
println(message);
753
}
754
755
vmDied = true;
756
vmDisconnected = true;
757
}
758
}
759
760