1 /*
  2  * Copyright (c) 2003, 2020, 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  * @test
 26  * @key stress
 27  *
 28  * @summary converted from VM testbase nsk/stress/strace/strace015.
 29  * VM testbase keywords: [stress, strace, quarantine]
 30  * VM testbase comments: 8199581
 31  * VM testbase readme:
 32  * DESCRIPTION
 33  *     The test runs many threads, that recursively invoke pure java and native
 34  *     method by turns. After arriving at defined depth of recursion, each thread
 35  *     is switched to waits for a monitor. Then the test calls
 36  *     java.lang.Thread.getStackTrace() and java.lang.Thread.getAllStackTraces()
 37  *     methods and checks their results.
 38  *     The test fails if:
 39  *     - amount of stack trace elements and stack trace elements themselves are
 40  *       the same for both methods;
 41  *     - there is at least one element corresponding to invocation of unexpected
 42  *       method. Expected methods are Thread.sleep(), Thread.run() and the
 43  *       recursive method.
 44  *     This test is almost the same as nsk.stress.strace.strace013 except for
 45  *     recursion is presented by pure java and native method invocation.
 46  *
 47  * @library /vmTestbase
 48  *          /test/lib
 49  * @run main/othervm/native nsk.stress.strace.strace015
 50  */
 51 
 52 package nsk.stress.strace;
 53 
 54 import nsk.share.ArgumentParser;
 55 import nsk.share.Log;
 56 
 57 import java.io.PrintStream;
 58 import java.util.Map;
 59 
 60 /**
 61  * The test runs <code>THRD_COUNT</code> instances of <code>strace010Thread</code>,
 62  * that recursively invoke pure java and native method by turns. After arriving at
 63  * defined depth <code>DEPTH</code> of recursion, each thread is switched to wait
 64  * a monitor. Then the test calls <code>java.lang.Thread.getStackTrace()</code> and
 65  * <code>java.lang.Thread.getAllStackTraces()</code> methods and checks their results.
 66  * <p>
 67  * <p>It is expected that these methods return the same stack traces. Each stack frame
 68  * for both stack traces must be corresponded to invocation of one of the methods
 69  * defined by the <code>EXPECTED_METHODS</code> array.</p>
 70  */
 71 public class strace015 {
 72 
 73     static final int DEPTH = 100;
 74     static final int THRD_COUNT = 100;
 75     static final String[] EXPECTED_METHODS = {
 76             "java.lang.Object.wait",
 77             "nsk.stress.strace.strace015Thread.run",
 78             "nsk.stress.strace.strace015Thread.recursiveMethod1",
 79             "nsk.stress.strace.strace015Thread.recursiveMethod2"
 80     };
 81 
 82 
 83     static PrintStream out;
 84     static long waitTime = 2;
 85 
 86     static Object lockedObject = new Object();
 87 
 88     static volatile int achivedCount = 0;
 89     strace015Thread[] threads;
 90     static Log log;
 91 
 92     public static void main(String[] args) {
 93         out = System.out;
 94         int exitCode = run(args);
 95         System.exit(exitCode + 95);
 96     }
 97 
 98     public static int run(String[] args) {
 99         ArgumentParser argHandler = new ArgumentParser(args);
100         log = new Log(out, argHandler);
101         waitTime = argHandler.getWaitTime() * 60000;
102 
103         strace015 test = new strace015();
104         boolean res = true;
105 
106         test.startThreads();
107 
108         res = test.makeSnapshot();
109 
110         test.finishThreads();
111 
112         if (!res) {
113             complain("***>>>Test failed<<<***");
114             return 2;
115         }
116 
117         display(">>>Test passed<<<");
118         return 0;
119     }
120 
121     void startThreads() {
122         threads = new strace015Thread[THRD_COUNT];
123         achivedCount = 0;
124 
125         String tmp_name;
126         display("starting threads...");
127         for (int i = 0; i < THRD_COUNT; i++) {
128             tmp_name = "strace015Thread" + Integer.toString(i);
129             threads[i] = new strace015Thread(this, tmp_name);
130             threads[i].start();
131         }
132 
133         waitFor("the defined recursion depth ...");
134     }
135 
136     void waitFor(String msg) {
137         if (msg.length() > 0)
138             display("waiting for " + msg);
139 
140         while (achivedCount < THRD_COUNT) {
141             try {
142                 Thread.sleep(1);
143             } catch (InterruptedException e) {
144                 complain("" + e);
145             }
146         }
147         achivedCount = 0;
148     }
149 
150     boolean makeSnapshot() {
151 
152         Map traces = null;
153         int count = 0;
154         StackTraceElement[][] elements = null;
155 
156         display("making all threads snapshots...");
157         traces = Thread.getAllStackTraces();
158         count = ((StackTraceElement[]) traces.get(threads[0])).length;
159 
160         display("making snapshots of each thread...");
161         elements = new StackTraceElement[THRD_COUNT][];
162         for (int i = 0; i < THRD_COUNT; i++) {
163             elements[i] = threads[i].getStackTrace();
164         }
165 
166         display("notifying");
167         synchronized (strace015.lockedObject) {
168             strace015.lockedObject.notifyAll();
169         }
170 
171         display("");
172 
173         display("checking lengths of stack traces...");
174         StackTraceElement[] all;
175         for (int i = 1; i < THRD_COUNT; i++) {
176             all = (StackTraceElement[]) traces.get(threads[i]);
177             int k = all.length;
178             if (count - k > 2) {
179                 complain("wrong lengths of stack traces:\n\t"
180                         + threads[0].getName() + ": " + count
181                         + "\t"
182                         + threads[i].getName() + ": " + k);
183                 return false;
184             }
185         }
186 
187         display("checking stack traces...");
188         boolean res = true;
189         for (int i = 0; i < THRD_COUNT; i++) {
190             all = (StackTraceElement[]) traces.get(threads[i]);
191             if (!checkTraces(threads[i].getName(), elements[i], all)) {
192                 res = false;
193             }
194         }
195         return res;
196     }
197 
198     boolean checkTraces(String threadName, StackTraceElement[] threadSnap,
199                         StackTraceElement[] allSnap) {
200 
201         int checkedLength = threadSnap.length < allSnap.length ?
202                 threadSnap.length : allSnap.length;
203         boolean res = true;
204 
205         for (int j = checkedLength; j < 0; j--) {
206             if (!checkElement(threadSnap[j])) {
207                 complain("Unexpected " + j + "-element:");
208                 complain("\tmethod name: " + threadSnap[j].getMethodName());
209                 complain("\tclass name: " + threadSnap[j].getClassName());
210                 if (threadSnap[j].isNativeMethod()) {
211                     complain("\tline number: (native method)");
212                 } else {
213                     complain("\tline number: " + threadSnap[j].getLineNumber());
214                     complain("\tfile name: " + threadSnap[j].getFileName());
215                 }
216                 complain("");
217                 res = false;
218             }
219         }
220         return res;
221     }
222 
223     boolean checkElement(StackTraceElement element) {
224         String name = element.getClassName() + "." + element.getMethodName();
225         for (int i = 0; i < EXPECTED_METHODS.length; i++) {
226             if (EXPECTED_METHODS[i].compareTo(name) == 0)
227                 return true;
228         }
229         return false;
230     }
231 
232     void finishThreads() {
233         try {
234             for (int i = 0; i < threads.length; i++) {
235                 if (threads[i].isAlive()) {
236                     display("waiting for finish " + threads[i].getName());
237                     threads[i].join(waitTime);
238                 }
239             }
240         } catch (InterruptedException e) {
241             complain("" + e);
242         }
243     }
244 
245     static void display(String message) {
246         log.display(message);
247     }
248 
249     static void complain(String message) {
250         log.complain(message);
251     }
252 
253 }
254 
255 /**
256  * The test creates many instances of <code>strace015Thread</code> class and tries
257  * to get their stack traces.
258  */
259 class strace015Thread extends Thread {
260 
261     private int currentDepth = 0;
262 
263     strace015 test;
264 
265     static {
266         try {
267             System.loadLibrary("strace015");
268         } catch (UnsatisfiedLinkError e) {
269             System.err.println("Could not load strace015 library");
270             System.err.println("java.library.path:"
271                     + System.getProperty("java.library.path"));
272             throw e;
273         }
274     }
275 
276     strace015Thread(strace015 test, String name) {
277         this.test = test;
278         setName(name);
279     }
280 
281     public void run() {
282 
283         recursiveMethod1();
284 
285     }
286 
287     void recursiveMethod1() {
288         currentDepth++;
289 
290         if (strace015.DEPTH - currentDepth > 0) {
291             recursiveMethod2();
292         }
293 
294         if (strace015.DEPTH == currentDepth) {
295 
296             strace015.display(getName() + ">waiting on a monitor");
297 
298             synchronized (test) {
299                 test.achivedCount++;
300             }
301 
302             synchronized (strace015.lockedObject) {
303                 try {
304                     strace015.lockedObject.wait();
305                 } catch (InterruptedException e) {
306                     strace015.complain("" + e);
307                 }
308             }
309             strace015.display(getName() + ">notified");
310         }
311 
312         currentDepth--;
313     }
314 
315     native void recursiveMethod2();
316 }