1 /*
2  * Copyright (c) 2002, 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 package nsk.jdi.Method.isObsolete;
24 
25 import nsk.share.*;
26 import nsk.share.jpda.*;
27 import nsk.share.jdi.*;
28 
29 import com.sun.jdi.*;
30 import com.sun.jdi.connect.*;
31 import com.sun.jdi.request.*;
32 import com.sun.jdi.event.*;
33 import java.io.*;
34 import java.util.*;
35 
36 /**
37  */
38 public class isobsolete003 {
39 
40     private final static String prefix = "nsk.jdi.Method.isObsolete";
41     private final static String className = ".isobsolete003";
42     private final static String debuggerName = prefix + className;
43     private final static String debuggeeName = debuggerName + "a";
44     private final static int brkpMainLineNumber = 48;
45     private final static int brkpFooLineNumber = 33;
46 
47     private static int waitTime;
48     private static int exitStatus;
49     private static ArgumentHandler     argHandler;
50     private static Log                 log;
51     private static Debugee             debuggee;
52     private static VirtualMachine      vm;
53     private static ReferenceType       debuggeeClass;
54 
55     private static EventRequestManager eventRManager;
56     private static EventSet            eventSet;
57     private static EventIterator       eventIterator;
58 
main(String argv[])59     public static void main(String argv[]) {
60         System.exit(Consts.JCK_STATUS_BASE + run(argv, System.out));
61     }
62 
run(String argv[], PrintStream out)63     public static int run(String argv[], PrintStream out) {
64 
65         exitStatus = Consts.TEST_PASSED;
66 
67         argHandler = new ArgumentHandler(argv);
68         log = new Log(out, argHandler);
69         waitTime = argHandler.getWaitTime() * 60000;
70 
71         try {
72 
73             Binder binder = new Binder(argHandler, log);
74             debuggee = binder.bindToDebugee(debuggeeName);
75             debuggee.redirectStderr(log, "debuggee > ");
76             debuggee.createIOPipe();
77             eventRManager = debuggee.getEventRequestManager();
78 
79             vm = debuggee.VM();
80             eventRManager = vm.eventRequestManager();
81 
82             waitForDebuggeeClassPrepared();
83 
84             if (vm.canRedefineClasses()) {
85 
86                 execTest();
87 
88                 debuggee.resume();
89                 getEventSet();
90                 if (eventIterator.nextEvent() instanceof VMDeathEvent) {
91                     display("Waiting for the debuggee's finish...");
92                     debuggee.waitFor();
93 
94                     display("Getting the debuggee's exit status.");
95                     int status = debuggee.getStatus();
96                     if (status != (Consts.TEST_PASSED + Consts.JCK_STATUS_BASE)) {
97                         complain("Debuggee returned UNEXPECTED exit status: " + status);
98                         exitStatus = Consts.TEST_FAILED;
99                     }
100                 } else {
101                     throw new TestBug("Last event is not the VMDeathEvent");
102                 }
103 
104             } else {
105                 display("vm.canRedefineClasses() == false : test is cancelled");
106                 vm.exit(Consts.TEST_PASSED + Consts.JCK_STATUS_BASE);
107             }
108 
109         } catch (VMDisconnectedException e) {
110             exitStatus = Consts.TEST_FAILED;
111             complain("The test cancelled due to VMDisconnectedException.");
112             e.printStackTrace(out);
113             display("Trying: vm.process().destroy();");
114             if (vm != null) {
115                 Process vmProcess = vm.process();
116                 if (vmProcess != null) {
117                     vmProcess.destroy();
118                 }
119             }
120 
121         } catch (Exception e) {
122             exitStatus = Consts.TEST_FAILED;
123             complain("Unexpected Exception: " + e.getMessage());
124             e.printStackTrace(out);
125             complain("The test has not finished normally. Forcing: vm.exit().");
126             if (vm != null) {
127                 vm.exit(Consts.TEST_PASSED + Consts.JCK_STATUS_BASE);
128             }
129             debuggee.resume();
130             getEventSet();
131         }
132 
133         return exitStatus;
134     }
135 
136 
execTest()137     private static void execTest() {
138 
139         ThreadReference mainThread = debuggee.threadByName("main");
140 
141         // Set first breakpoint to have isobsolete003b class loaded.
142         BreakpointRequest bpRequest = debuggee.makeBreakpoint(debuggeeClass, "main", brkpMainLineNumber);
143         bpRequest.addThreadFilter(mainThread);
144         bpRequest.addCountFilter(1);
145         bpRequest.enable();
146 
147         waitForEvent(bpRequest);
148         bpRequest.disable();
149 
150         // At this point isobsolete003b class should be loaded in debuggee.
151         String redefName = prefix + ".isobsolete003b";
152 
153         ReferenceType redefClass = debuggee.classByName(redefName);
154         if (redefClass == null) {
155             throw new TestBug(redefName + "is not found in debuggee.");
156         }
157 
158         String methodName = "foo";
159         Method method = (Method) redefClass.methodsByName(methodName).get(0);
160 
161         // save some values for check in future
162         Method oldMethod = method;
163         Location oldLocation = method.location();
164         long oldCodeIndex = oldLocation.codeIndex();
165         String oldRetTypeName = method.returnTypeName();
166         int oldHashCode = method.hashCode();
167 
168         // Set new breakpoint to have isobsolete003b.foo() method on stack before redefinition.
169         bpRequest = debuggee.makeBreakpoint(redefClass, methodName, brkpFooLineNumber);
170         bpRequest.addThreadFilter(mainThread);
171         bpRequest.addCountFilter(1);
172         bpRequest.enable();
173 
174         waitForEvent(bpRequest);
175         bpRequest.disable();
176 
177         display("requested BreakpointEvent for foo() method received;");
178         try {
179             if (!mainThread.frame(0).location().method().equals(method)) {
180                 throw new TestBug("foo() method is not on the top of the main thread stack");
181             }
182         } catch (IncompatibleThreadStateException e) {
183             throw new Failure("Unexpected IncompatibleThreadStateException while comparing mainThread.frame(0).location().method(): " + e.getMessage());
184         }
185 
186         display("Making redefineClasses(mapClassToBytes()).");
187         vm.redefineClasses(mapClassToBytes());
188 
189         // Check isObsolete after redefinition
190         try {
191             method = mainThread.frame(0).location().method();
192         } catch (IncompatibleThreadStateException e) {
193             throw new Failure("Unexpected IncompatibleThreadStateException while getting mainThread.frame(0).location().method(): " + e.getMessage());
194         }
195         if (!method.isObsolete()) {
196             complain("method.isObsolete() == true for foo() method after redefineClasses()");
197             exitStatus = Consts.TEST_FAILED;
198         } else {
199             // Do other checks for obsolete method.
200 
201             if (method.equals(oldMethod)) {
202                 complain("equals(oldMethod) returned true for obsolete method.");
203                 exitStatus = Consts.TEST_FAILED;
204             }
205 
206             List l = null;
207             Location loc = null;
208             try {
209                 l = method.allLineLocations();
210                 if (l.size() > 0) {
211                     complain("allLineLocations() returned a list with non-zero size for obsolete method." +
212                         "Number of Locations :" + l.size());
213                     exitStatus = Consts.TEST_FAILED;
214                 }
215             } catch (AbsentInformationException e) {
216                 // it is expected
217             }
218 
219             try {
220                 l = method.allLineLocations(vm.getDefaultStratum(), null);
221                 if (l.size() > 0) {
222                     complain("allLineLocations(vm.getDefaultStratum(), null) returned a list with non-zero size for obsolete method." +
223                         "Number of Locations :" + l.size());
224                     exitStatus = Consts.TEST_FAILED;
225                 }
226             } catch (AbsentInformationException e) {
227                 // it is expected
228             }
229 
230             try {
231                 l = method.locationsOfLine(1);
232                 if (l.size() > 0) {
233                     complain("locationsOfLine(1) returned a list with non-zero size for obsolete method." +
234                         "Number of Locations :" + l.size());
235                     exitStatus = Consts.TEST_FAILED;
236                 }
237             } catch (AbsentInformationException e) {
238                 // it is expected
239             }
240 
241             try {
242                 l = method.locationsOfLine(vm.getDefaultStratum(), null, 1);
243                 if (l.size() > 0) {
244                     complain("locationsOfLine(vm.getDefaultStratum(), null, 1) returned a list with non-zero size for obsolete method." +
245                         "Number of Locations :" + l.size());
246                     exitStatus = Consts.TEST_FAILED;
247                 }
248             } catch (AbsentInformationException e) {
249                 // it is expected
250             }
251 
252             try {
253                 l = method.arguments();
254                 if (l.size() > 0) {
255                     complain("arguments() returned a list with non-zero size for obsolete method." +
256                         "Size of list  :" + l.size());
257                     exitStatus = Consts.TEST_FAILED;
258                 }
259             } catch (AbsentInformationException e) {
260                 // it is expected
261             }
262 
263             l = method.argumentTypeNames();
264             if (l.size() > 0) {
265                 complain("argumentTypeNames() returned a list with non-zero size for obsolete method." +
266                     "Size of list :" + l.size());
267                 exitStatus = Consts.TEST_FAILED;
268             }
269 
270             try {
271                 l = method.argumentTypes();
272                 if (l.size() > 0) {
273                     complain("argumentsTypes() returned a list with non-zero size for obsolete method." +
274                         "Size of list  :" + l.size());
275                     exitStatus = Consts.TEST_FAILED;
276                 }
277             } catch (ClassNotLoadedException e) {
278                 // it is expected
279             }
280 
281             try {
282                 l = method.variables();
283                 if (l.size() > 0) {
284                     complain("variables() returned a list with non-zero size for obsolete method." +
285                         "Size of list  :" + l.size());
286                     exitStatus = Consts.TEST_FAILED;
287                 }
288             } catch (AbsentInformationException e) {
289                 // it is expected
290             }
291 
292             try {
293                 l = method.variablesByName("dummyInt");
294                 if (l.size() > 0) {
295                     complain("variablesByName(oldVar.name()) returned a list with non-zero size for obsolete method." +
296                         "Size of list  :" + l.size());
297                     exitStatus = Consts.TEST_FAILED;
298                 }
299             } catch (AbsentInformationException e) {
300                 // it is expected
301             }
302 
303             byte[] b = method.bytecodes();
304             if (b.length > 0) {
305                 complain("bytecodes() returned an array with non-zero length for obsolete method." +
306                     "Number of bytes :" + b.length);
307                 exitStatus = Consts.TEST_FAILED;
308             }
309 
310             loc = method.location();
311             if (loc != null && loc == oldLocation) {
312                 complain("location() returned old location for obsolete method.");
313                 exitStatus = Consts.TEST_FAILED;
314             }
315 
316             loc = method.locationOfCodeIndex(oldCodeIndex);
317             if (loc != null) {
318                 complain("locationOfCodeIndex(oldCodeIndex) returned not-null location for obsolete method.");
319                 exitStatus = Consts.TEST_FAILED;
320             }
321 
322             String rtName = method.returnTypeName();
323             if (rtName.equals(oldRetTypeName)) {
324                 complain("returnTypeName() returned an old string for obsolete method: " + rtName);
325                 exitStatus = Consts.TEST_FAILED;
326             }
327 
328             try {
329                 Type rType = method.returnType();
330                 if (rType != null) {
331                     complain("returnType() returned not-null Type for obsolete method: " + rType.name());
332                     exitStatus = Consts.TEST_FAILED;
333                 }
334             } catch (ClassNotLoadedException e) {
335                 // it is expected
336             }
337 
338             int hashCode = method.hashCode();
339             if (hashCode == oldHashCode) {
340                 complain("hashCode() returned old value for obsolete method: " + hashCode);
341                 exitStatus = Consts.TEST_FAILED;
342             }
343         }
344     }
345 
display(String msg)346     private static void display(String msg) {
347         log.display("debugger > " + msg);
348     }
349 
complain(String msg)350     private static void complain(String msg) {
351         log.complain("debugger FAILURE > " + msg);
352     }
353 
354     /**
355      * Returns Map object for redefinition.
356      *
357      */
mapClassToBytes()358     private static Map<? extends com.sun.jdi.ReferenceType,byte[]> mapClassToBytes() {
359         String[] args = argHandler.getArguments();
360         if (args.length <= 0) {
361             throw new Failure("mapClassToBytes(): Test arguments are not found.");
362         }
363 
364         String testDir = args[0];
365         display("Test current dir = " + testDir);
366 
367         String filePrefix = File.separator + "nsk"
368                           + File.separator + "jdi"
369                           + File.separator + "Method"
370                           + File.separator + "isObsolete";
371 
372         String fileToRedefineName    = testDir +
373                                        File.separator + "newclass" + filePrefix
374                                        + File.separator + "isobsolete003b.class";
375 
376         display("fileToRedefineName : " + fileToRedefineName);
377 
378         byte[] arrayToRedefine;
379         try {
380             File fileToRedefine    = new File(fileToRedefineName);
381             if (!fileToRedefine.exists()) {
382                 throw new Failure("mapClassToBytes(): fileToRedefine does not exist");
383             }
384 
385             FileInputStream inputFile = new FileInputStream(fileToRedefine);
386             arrayToRedefine = new byte [(int) fileToRedefine.length()];
387             inputFile.read(arrayToRedefine);
388             inputFile.close();
389 
390         } catch (IOException e) {
391             complain("unexpected IOException: " + e);
392             throw new Failure(e);
393         }
394 
395         String testClassName = prefix + ".isobsolete003b";
396         ReferenceType testClass = debuggee.classByName(testClassName);
397 
398         HashMap<com.sun.jdi.ReferenceType,byte[]> mapForClass = new HashMap<com.sun.jdi.ReferenceType,byte[]>();
399         mapForClass.put(testClass, arrayToRedefine);
400 
401         return mapForClass;
402     }
403 
waitForEvent(EventRequest evRequest)404     private static Event waitForEvent (EventRequest evRequest) {
405 
406         vm.resume();
407         Event resultEvent = null;
408         try {
409             eventSet = null;
410             eventIterator = null;
411             eventSet = vm.eventQueue().remove(waitTime);
412             if (eventSet == null) {
413                 throw new Failure("TIMEOUT while waiting for an event");
414             }
415             eventIterator = eventSet.eventIterator();
416             while (eventIterator.hasNext()) {
417                  Event curEvent = eventIterator.nextEvent();
418                  if (curEvent instanceof VMDisconnectEvent) {
419                      throw new Failure("Unexpected VMDisconnectEvent received.");
420                  } else if (curEvent.request().equals(evRequest)) {
421                      display("Requested event received.");
422                      resultEvent = curEvent;
423                      break;
424                  } else {
425                      throw new TestBug("Unexpected Event received: " + curEvent.toString());
426                  }
427             }
428         } catch (Exception e) {
429             throw new Failure("Unexpected exception while waiting for an event: " + e);
430         }
431         return resultEvent;
432     }
433 
getEventSet()434     private static void getEventSet() {
435         try {
436             eventSet = vm.eventQueue().remove(waitTime);
437             if (eventSet == null) {
438                 throw new Failure("TIMEOUT while waiting for an event");
439             }
440             eventIterator = eventSet.eventIterator();
441         } catch (Exception e) {
442             throw new Failure("getEventSet(): Unexpected exception while waiting for an event: " + e);
443         }
444     }
445 
waitForDebuggeeClassPrepared()446     private static void waitForDebuggeeClassPrepared () {
447         display("Creating request for ClassPrepareEvent for debuggee.");
448         ClassPrepareRequest cpRequest = eventRManager.createClassPrepareRequest();
449         cpRequest.addClassFilter(debuggeeName);
450         cpRequest.addCountFilter(1);
451         cpRequest.enable();
452 
453         ClassPrepareEvent event = (ClassPrepareEvent) waitForEvent(cpRequest);
454         cpRequest.disable();
455 
456         debuggeeClass = event.referenceType();
457         if (!debuggeeClass.name().equals(debuggeeName))
458            throw new Failure("Unexpected class name for ClassPrepareEvent : " + debuggeeClass.name());
459     }
460 }
461