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.SuspendCount;
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.SuspendCount.
34  *
35  * See suspendcnt001.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 suspendcnt001 {
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.SuspendCount";
58     static final String TEST_CLASS_NAME = PACKAGE_NAME + "." + "suspendcnt001";
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.SuspendCount";
63     static final int JDWP_COMMAND_ID = JDWP.Command.ThreadReference.SuspendCount;
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 = suspendcnt001a.FIELD_NAME;
71     static final String TESTED_THREAD_NAME = suspendcnt001a.THREAD_NAME;
72 
73     // expected number od suspend count
74     static final int SUSPEND_COUNT = 5;
75 
76     // usual scaffold objects
77     ArgumentHandler argumentHandler = null;
78     Log log = null;
79     Binder binder = null;
80     Debugee debugee = null;
81     Transport transport = null;
82     IOPipe pipe = null;
83 
84     // test passed or not
85     boolean success = true;
86 
87     // -------------------------------------------------------------------
88 
89     /**
90      * Start test from command line.
91      */
main(String argv[])92     public static void main (String argv[]) {
93         System.exit(run(argv,System.out) + JCK_STATUS_BASE);
94     }
95 
96     /**
97      * Start JCK-compilant test.
98      */
run(String argv[], PrintStream out)99     public static int run(String argv[], PrintStream out) {
100         return new suspendcnt001().runIt(argv, out);
101     }
102 
103     // -------------------------------------------------------------------
104 
105     /**
106      * Perform test execution.
107      */
runIt(String argv[], PrintStream out)108     public int runIt(String argv[], PrintStream out) {
109 
110         // make log for debugger messages
111         argumentHandler = new ArgumentHandler(argv);
112         log = new Log(out, argumentHandler);
113 
114         // execute test and display results
115         try {
116             log.display("\n>>> Preparing debugee for testing \n");
117 
118             // launch debuggee
119             binder = new Binder(argumentHandler, log);
120             log.display("Launching debugee");
121             debugee = binder.bindToDebugee(DEBUGEE_CLASS_NAME);
122             transport = debugee.getTransport();
123             pipe = debugee.createIOPipe();
124 
125             // make debuggee ready for testing
126             prepareDebugee();
127 
128             long threadID = 0;
129 
130             // work with prepared debuggee
131             try {
132                 log.display("\n>>> Obtaining requred data from debugee \n");
133 
134                 // query debuggee for classID of tested class
135                 log.display("Getting classID by signature:\n"
136                             + "  " + TESTED_CLASS_SIGNATURE);
137                 long classID = debugee.getReferenceTypeID(TESTED_CLASS_SIGNATURE);
138                 log.display("  got classID: " + classID);
139 
140                 // query debuggee for threadID value from a static field
141                 log.display("Getting threadID value from static field: "
142                             + TESTED_CLASS_FIELD_NAME);
143                 threadID = queryThreadID(classID, TESTED_CLASS_FIELD_NAME);
144                 log.display("  got threadID: " + threadID);
145 
146                 // request debuggee to suspend tested thread
147                 log.display("Suspendig thread " + SUSPEND_COUNT + " times for threadID: " + threadID);
148                 for (int i = 0; i < SUSPEND_COUNT; i++) {
149                     debugee.suspendThread(threadID);
150                 }
151 
152                 // perform testing JDWP command
153                 log.display("\n>>> Testing JDWP command \n");
154                 testCommand(threadID);
155 
156             } finally {
157 
158                 log.display("\n>>> Finishing test \n");
159 
160                 // resumme suspended thread
161                 if (threadID != 0) {
162                     log.display("Resuming " + SUSPEND_COUNT + " times suspended thread: " + threadID);
163                     for (int i = 0; i < SUSPEND_COUNT; i++) {
164                         debugee.resumeThread(threadID);
165                     }
166                 }
167 
168                 // quit debugee
169                 quitDebugee();
170             }
171 
172         } catch (Failure e) {
173             log.complain("TEST FAILED: " + e.getMessage());
174             success = false;
175         } catch (Exception e) {
176             e.printStackTrace(out);
177             log.complain("Caught unexpected exception while running the test:\n\t" + e);
178             success = false;
179         }
180 
181         if (!success) {
182             log.complain("TEST FAILED");
183             return FAILED;
184         }
185 
186         out.println("TEST PASSED");
187         return PASSED;
188 
189     }
190 
191     /**
192      * Prepare debugee for testing and waiting for ready signal.
193      */
prepareDebugee()194     void prepareDebugee() {
195         // wait for VM_INIT event from debugee
196         log.display("Waiting for VM_INIT event");
197         debugee.waitForVMInit();
198 
199         // query debugee for VM-dependent ID sizes
200         log.display("Querying for IDSizes");
201         debugee.queryForIDSizes();
202 
203         // resume initially suspended debugee
204         log.display("Resuming debugee VM");
205         debugee.resume();
206 
207         // wait for READY signal from debugee
208         log.display("Waiting for signal from debugee: " + READY);
209         String signal = pipe.readln();
210         log.display("Received signal from debugee: " + signal);
211         if (signal == null) {
212             throw new TestBug("Null signal received from debugee: " + signal
213                             + " (expected: " + READY + ")");
214         } else if (signal.equals(ERROR)) {
215             throw new TestBug("Debugee was not able to start tested thread"
216                             + " (received signal: " + signal + ")");
217         } else if (!signal.equals(READY)) {
218             throw new TestBug("Unexpected signal received from debugee: " + signal
219                             + " (expected: " + READY + ")");
220         }
221     }
222 
223     /**
224      * Sending debugee signal to quit and waiting for it exits.
225      */
quitDebugee()226     void quitDebugee() {
227         // send debugee signal to quit
228         log.display("Sending signal to debugee: " + QUIT);
229         pipe.println(QUIT);
230 
231         // wait for debugee exits
232         log.display("Waiting for debugee exits");
233         int code = debugee.waitFor();
234 
235         // analize debugee exit status code
236         if (code == JCK_STATUS_BASE + PASSED) {
237             log.display("Debugee PASSED with exit code: " + code);
238         } else {
239             log.complain("Debugee FAILED with exit code: " + code);
240             success = false;
241         }
242     }
243 
244     /**
245      * Query debuggee for threadID value of statuic field of the class.
246      */
queryThreadID(long classID, String fieldName)247     long queryThreadID(long classID, String fieldName) {
248         // get fieledID for static field (declared in the class)
249         long fieldID = debugee.getClassFieldID(classID, fieldName, true);
250         // get value of the field
251         JDWP.Value value = debugee.getStaticFieldValue(classID, fieldID);
252 
253         // check that value has THREAD tag
254         if (value.getTag() != JDWP.Tag.THREAD) {
255             throw new Failure("Not threadID value returned from debuggee: " + value);
256         }
257 
258         // extract threadID from the value
259         long threadID = ((Long)value.getValue()).longValue();
260         return threadID;
261     }
262 
263     /**
264      * Query debuggee for suspend status of the thread.
265      */
querySuspendStatus(long threadID)266     int querySuspendStatus(long threadID) {
267         log.display("Getting suspend status for threadID: " + threadID);
268         CommandPacket command = new CommandPacket(JDWP.Command.ThreadReference.Status);
269         command.addObjectID(threadID);
270         ReplyPacket reply = debugee.receiveReplyFor(command);
271 
272         try {
273             reply.resetPosition();
274 
275             int threadStatus = reply.getInt();
276             int suspendStatus = reply.getInt();
277             log.display("  got suspendStatus: " + suspendStatusString(suspendStatus));
278             return suspendStatus;
279         } catch (BoundException e) {
280             throw new Failure("Caught BoundException while parsing reply for ThreadReference.Status:\n\t"
281                         + e);
282         }
283     }
284 
285     /**
286      * Perform testing JDWP command for specified threadID.
287      */
testCommand(long threadID)288     void testCommand(long threadID) {
289         // create command packet and fill requred out data
290         log.display("Create command packet:");
291         log.display("Command: " + JDWP_COMMAND_NAME);
292         CommandPacket command = new CommandPacket(JDWP_COMMAND_ID);
293         log.display("  threadID: " + threadID);
294         command.addObjectID(threadID);
295         command.setLength();
296 
297         // send command packet to debugee
298         try {
299             log.display("Sending command packet:\n" + command);
300             transport.write(command);
301         } catch (IOException e) {
302             log.complain("Unable to send command packet:\n\t" + e);
303             success = false;
304             return;
305         }
306 
307         ReplyPacket reply = new ReplyPacket();
308 
309         // receive reply packet from debugee
310         try {
311             log.display("Waiting for reply packet");
312             transport.read(reply);
313             log.display("Reply packet received:\n" + reply);
314         } catch (IOException e) {
315             log.complain("Unable to read reply packet:\n\t" + e);
316             success = false;
317             return;
318         }
319 
320         // check reply packet header
321         try{
322             log.display("Checking reply packet header");
323             reply.checkHeader(command.getPacketID());
324         } catch (BoundException e) {
325             log.complain("Bad header of reply packet:\n\t" + e.getMessage());
326             success = false;
327             return;
328         }
329 
330         // start parsing reply packet data
331         log.display("Parsing reply packet:");
332         reply.resetPosition();
333 
334         int suspendCount = 0;
335         try {
336             suspendCount = reply.getInt();
337             log.display("  suspendCount: " + suspendCount);
338         } catch (BoundException e) {
339             log.complain("Unable to extract suspend count number form the reply packet:\n\t"
340                         + e.getMessage());
341             success = false;
342             return;
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         if (suspendCount < 0) {
353             log.complain("Negative number of suspend count returned: " + suspendCount
354                         + "(expected: " + SUSPEND_COUNT + ")");
355             success = false;
356         }
357 
358         if (suspendCount != SUSPEND_COUNT) {
359             log.complain("Unexpected number of suspend count returned: " + suspendCount
360                         + "(expected: " + SUSPEND_COUNT + ")");
361             success = false;
362         }
363 
364     }
365 
366     /**
367      * Return string representation of thread suspend status.
368      */
suspendStatusString(int status)369     private static String suspendStatusString(int status) {
370         String s = null;
371         if ((status & JDWP.SuspendStatus.SUSPEND_STATUS_SUSPENDED) != 0) {
372             s = "SUSPEND_STATUS_SUSPENDED";
373         } else  if (status == 0) {
374             s = "NONE";
375         } else {
376             s = "unknown";
377         }
378         return status + "=" + s;
379     }
380 }
381