1 /*
2  * Copyright (c) 2001, 2018, 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 package nsk.jdwp.ThreadReference.Status;
25 
26 import java.io.*;
27 
28 import nsk.share.*;
29 import nsk.share.jpda.*;
30 import nsk.share.jdwp.*;
31 
32 /**
33  * Test for JDWP command: ThreadReference.Status.
34  *
35  * See status001.README for description of test execution.
36  *
37  * This class represents debugger part of the test.
38  * Test is executed by invoking method runIt().
39  * JDWP command is tested in the method testCommand().
40  *
41  * @see #runIt()
42  * @see #testCommand()
43  */
44 public class status001 {
45 
46     // exit status constants
47     static final int JCK_STATUS_BASE = 95;
48     static final int PASSED = 0;
49     static final int FAILED = 2;
50 
51     // communication signals constants
52     static final String READY = "ready";
53     static final String ERROR = "error";
54     static final String QUIT = "quit";
55 
56     // package and classes names constants
57     static final String PACKAGE_NAME = "nsk.jdwp.ThreadReference.Status";
58     static final String TEST_CLASS_NAME = PACKAGE_NAME + "." + "status001";
59     static final String DEBUGEE_CLASS_NAME = TEST_CLASS_NAME + "a";
60 
61     // tested JDWP command constants
62     static final String JDWP_COMMAND_NAME = "ThreadReference.Status";
63     static final int JDWP_COMMAND_ID = JDWP.Command.ThreadReference.Status;
64 
65     // tested class name and signature constants
66     static final String TESTED_CLASS_NAME = DEBUGEE_CLASS_NAME + "$" + "TestedClass";
67     static final String TESTED_CLASS_SIGNATURE = "L" + TESTED_CLASS_NAME.replace('.', '/') + ";";
68 
69     // name of the tested thread and statioc field with thread value
70     static final String TESTED_CLASS_FIELD_NAME = status001a.FIELD_NAME;
71     static final String TESTED_THREAD_NAME = status001a.THREAD_NAME;
72 
73     // usual scaffold objects
74     ArgumentHandler argumentHandler = null;
75     Log log = null;
76     Binder binder = null;
77     Debugee debugee = null;
78     Transport transport = null;
79     IOPipe pipe = null;
80 
81     // test passed or not
82     boolean success = true;
83 
84     // -------------------------------------------------------------------
85 
86     /**
87      * Start test from command line.
88      */
main(String argv[])89     public static void main (String argv[]) {
90         System.exit(run(argv,System.out) + JCK_STATUS_BASE);
91     }
92 
93     /**
94      * Start JCK-compilant test.
95      */
run(String argv[], PrintStream out)96     public static int run(String argv[], PrintStream out) {
97         return new status001().runIt(argv, out);
98     }
99 
100     // -------------------------------------------------------------------
101 
102     /**
103      * Perform test execution.
104      */
runIt(String argv[], PrintStream out)105     public int runIt(String argv[], PrintStream out) {
106 
107         // make log for debugger messages
108         argumentHandler = new ArgumentHandler(argv);
109         log = new Log(out, argumentHandler);
110 
111         // execute test and display results
112         try {
113             log.display("\n>>> Preparing debugee for testing \n");
114 
115             // launch debuggee
116             binder = new Binder(argumentHandler, log);
117             log.display("Launching debugee");
118             debugee = binder.bindToDebugee(DEBUGEE_CLASS_NAME);
119             transport = debugee.getTransport();
120             pipe = debugee.createIOPipe();
121 
122             // make debuggee ready for testing
123             prepareDebugee();
124 
125             // work with prepared debuggee
126             try {
127                 log.display("\n>>> Obtaining requred data from debugee \n");
128 
129                 // query debuggee for classID of tested class
130                 log.display("Getting classID by signature:\n"
131                             + "  " + TESTED_CLASS_SIGNATURE);
132                 long classID = debugee.getReferenceTypeID(TESTED_CLASS_SIGNATURE);
133                 log.display("  got classID: " + classID);
134 
135                 // query debuggee for threadID value from a static field
136                 log.display("Getting threadID value from static field: "
137                             + TESTED_CLASS_FIELD_NAME);
138                 long threadID = queryThreadID(classID, TESTED_CLASS_FIELD_NAME);
139                 log.display("  got threadID: " + threadID);
140 
141                 // perform testing JDWP command
142                 log.display("\n>>> Testing JDWP command \n");
143                 testCommand(threadID);
144 
145             } finally {
146                 // quit debugee
147                 log.display("\n>>> Finishing test \n");
148                 quitDebugee();
149             }
150 
151         } catch (Failure e) {
152             log.complain("TEST FAILED: " + e.getMessage());
153             success = false;
154         } catch (Exception e) {
155             e.printStackTrace(out);
156             log.complain("Caught unexpected exception while running the test:\n\t" + e);
157             success = false;
158         }
159 
160         if (!success) {
161             log.complain("TEST FAILED");
162             return FAILED;
163         }
164 
165         out.println("TEST PASSED");
166         return PASSED;
167 
168     }
169 
170     /**
171      * Prepare debugee for testing and waiting for ready signal.
172      */
prepareDebugee()173     void prepareDebugee() {
174         // wait for VM_INIT event from debugee
175         log.display("Waiting for VM_INIT event");
176         debugee.waitForVMInit();
177 
178         // query debugee for VM-dependent ID sizes
179         log.display("Querying for IDSizes");
180         debugee.queryForIDSizes();
181 
182         // resume initially suspended debugee
183         log.display("Resuming debugee VM");
184         debugee.resume();
185 
186         // wait for READY signal from debugee
187         log.display("Waiting for signal from debugee: " + READY);
188         String signal = pipe.readln();
189         log.display("Received signal from debugee: " + signal);
190         if (signal == null) {
191             throw new TestBug("Null signal received from debugee: " + signal
192                             + " (expected: " + READY + ")");
193         } else if (signal.equals(ERROR)) {
194             throw new TestBug("Debugee was not able to start tested thread"
195                             + " (received signal: " + signal + ")");
196         } else if (!signal.equals(READY)) {
197             throw new TestBug("Unexpected signal received from debugee: " + signal
198                             + " (expected: " + READY + ")");
199         }
200     }
201 
202     /**
203      * Sending debugee signal to quit and waiting for it exits.
204      */
quitDebugee()205     void quitDebugee() {
206         // send debugee signal to quit
207         log.display("Sending signal to debugee: " + QUIT);
208         pipe.println(QUIT);
209 
210         // wait for debugee exits
211         log.display("Waiting for debugee exits");
212         int code = debugee.waitFor();
213 
214         // analize debugee exit status code
215         if (code == JCK_STATUS_BASE + PASSED) {
216             log.display("Debugee PASSED with exit code: " + code);
217         } else {
218             log.complain("Debugee FAILED with exit code: " + code);
219             success = false;
220         }
221     }
222 
223     /**
224      * Query debuggee for threadID value of statuic field of the class.
225      */
queryThreadID(long classID, String fieldName)226     long queryThreadID(long classID, String fieldName) {
227         // get fieledID for static field (declared in the class)
228         long fieldID = debugee.getClassFieldID(classID, fieldName, true);
229         // get value of the field
230         JDWP.Value value = debugee.getStaticFieldValue(classID, fieldID);
231 
232         // check that value has THREAD tag
233         if (value.getTag() != JDWP.Tag.THREAD) {
234             throw new Failure("Not threadID value returned from debuggee: " + value);
235         }
236 
237         // extract threadID from the value
238         long threadID = ((Long)value.getValue()).longValue();
239         return threadID;
240     }
241 
242     /**
243      * Perform testing JDWP command for specified threadID.
244      */
testCommand(long threadID)245     void testCommand(long threadID) {
246         // create command packet and fill requred out data
247         log.display("Create command packet:");
248         log.display("Command: " + JDWP_COMMAND_NAME);
249         CommandPacket command = new CommandPacket(JDWP_COMMAND_ID);
250         log.display("  threadID: " + threadID);
251         command.addObjectID(threadID);
252         command.setLength();
253 
254         // send command packet to debugee
255         try {
256             log.display("Sending command packet:\n" + command);
257             transport.write(command);
258         } catch (IOException e) {
259             log.complain("Unable to send command packet:\n\t" + e);
260             success = false;
261             return;
262         }
263 
264         ReplyPacket reply = new ReplyPacket();
265 
266         // receive reply packet from debugee
267         try {
268             log.display("Waiting for reply packet");
269             transport.read(reply);
270             log.display("Reply packet received:\n" + reply);
271         } catch (IOException e) {
272             log.complain("Unable to read reply packet:\n\t" + e);
273             success = false;
274             return;
275         }
276 
277         // check reply packet header
278         try{
279             log.display("Checking reply packet header");
280             reply.checkHeader(command.getPacketID());
281         } catch (BoundException e) {
282             log.complain("Bad header of reply packet:\n\t" + e.getMessage());
283             success = false;
284             return;
285         }
286 
287         // start parsing reply packet data
288         log.display("Parsing reply packet:");
289         reply.resetPosition();
290 
291         // extract thread status
292         int threadStatus = 0;
293         try {
294             threadStatus = reply.getInt();
295             log.display("  threadStatus: " + threadStatusString(threadStatus));
296         } catch (BoundException e) {
297             log.complain("Unable to extract thread status from reply packet:\n\t"
298                         + e.getMessage());
299             success = false;
300             return;
301         }
302 
303         // extract suspend status
304         int suspendStatus = 0;
305         try {
306             suspendStatus = reply.getInt();
307             log.display("  suspendStatus: " + suspendStatusString(suspendStatus));
308         } catch (BoundException e) {
309             log.complain("Unable to extract thread status from reply packet:\n\t"
310                         + e.getMessage());
311             success = false;
312             return;
313         }
314 
315         // check that both status code are not negative values
316         if (threadStatus < 0) {
317             log.complain("Negative value of thread status in reply packet: "
318                         + threadStatusString(threadStatus));
319             success = false;
320         }
321         if (suspendStatus < 0) {
322             log.complain("Negative value of suspend status in reply packet: "
323                         + suspendStatusString(suspendStatus));
324             success = false;
325         }
326 
327         // check that thread has an expected state
328         if (!(threadStatus == JDWP.ThreadStatus.RUNNING
329                 || threadStatus == JDWP.ThreadStatus.MONITOR)) {
330             log.complain("Unexpected thread status returned in the reply packet: "
331                         + threadStatusString(threadStatus)
332                         + " (expected: " + threadStatusString(JDWP.ThreadStatus.RUNNING)
333                         + " or " + threadStatusString(JDWP.ThreadStatus.MONITOR) + ")");
334             success = false;
335         }
336 
337         // check that thread was not suspended
338         if ((suspendStatus & JDWP.SuspendStatus.SUSPEND_STATUS_SUSPENDED) != 0) {
339             log.complain("Unexpected suspend status returned in the reply packet: "
340                         + threadStatusString(threadStatus)
341                         + " (expected: " + "not suspended" + ")");
342             success = false;
343         }
344 
345         // check for extra data in reply packet
346         if (!reply.isParsed()) {
347             log.complain("Extra trailing bytes found in reply packet at: "
348                         + reply.offsetString());
349             success = false;
350         }
351     }
352 
353     /**
354      * Return string representation of thread status code.
355      */
threadStatusString(int status)356     private static String threadStatusString(int status) {
357         String s = null;
358         switch (status) {
359             case JDWP.ThreadStatus.MONITOR:
360                 s = "MONITOR";
361                 break;
362             case JDWP.ThreadStatus.RUNNING:
363                 s = "RUNNING";
364                 break;
365             case JDWP.ThreadStatus.SLEEPING:
366                 s = "SLEEPING";
367                 break;
368             case JDWP.ThreadStatus.WAIT:
369                 s = "WAIT";
370                 break;
371             case JDWP.ThreadStatus.ZOMBIE:
372                 s = "ZOMBIE";
373                 break;
374             default:
375                 s = "unknown";
376                 break;
377         }
378         return status + "=" + s;
379     }
380 
381     /**
382      * Return string representation of thread suspend status.
383      */
suspendStatusString(int status)384     private static String suspendStatusString(int status) {
385         String s = null;
386         if ((status & JDWP.SuspendStatus.SUSPEND_STATUS_SUSPENDED) != 0) {
387             s = "SUSPEND_STATUS_SUSPENDED";
388         } else  if (status == 0) {
389             s = "NONE";
390         } else {
391             s = "unknown";
392         }
393         return status + "=" + s;
394     }
395 }
396