File: SerialExecutionDebugger.java

package info (click to toggle)
openjdk-11 11.0.16%2B8-1~deb10u1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 784,576 kB
  • sloc: java: 5,128,021; xml: 1,192,224; cpp: 1,124,021; ansic: 422,433; javascript: 155,577; sh: 17,084; objc: 13,327; python: 4,522; asm: 3,570; makefile: 2,858; awk: 351; sed: 172; perl: 114; jsp: 24; csh: 3
file content (337 lines) | stat: -rw-r--r-- 13,463 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/*
 * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package nsk.share.jdi;

import nsk.share.Consts;
import nsk.share.TestBug;
import nsk.share.jpda.AbstractDebuggeeTest;
import java.io.*;
import java.util.*;

/*
 * This class serial executes several JDI tests based on nsk.share.jdi.TestDebuggerType2 in single VM
 * SerialExecutionDebugger is used together with SerialExecutionDebuggee, execution process is following:
 *
 *  - SerialExecutionDebugger reads tests to execute from input file, test description is debugger class name and test's parameters,
 *  if 'shuffle' option is specified in input file debugger executes tests in random order (input file should contain line "OPTIONS:shuffle").
 *  SerialExecutionDebugger can execute tests several times in loop, number of iterations should be specified in input file in following manner:
 *  OPTIONS:iterations <iterations_number>.
 *
 *  - SerialExecutionDebugger starts debuggee VM with main class SerialExecutionDebuggee,
 *  initializes IOPipe and 'debuggee' object which represents debuggee VM
 *
 *  - for each test from input file:
 *
 *      - SerialExecutionDebugger creates object of current debugger and initializes it with already created pipe and debuggee
 *      - SerialExecutionDebugger sends command to SerialExecutionDebuggee:  'COMMAND_EXECUTE_DEBUGGEE <CurrentDebuggeeName>'
 *      (CurrentDebuggeeName name should provide current debugger), and waits READY signal from debuggee
 *      - SerialExecutionDebuggee parses received command, extracts debugee name, creates object of current debuggee, which should be
 *      subclass of nsk.share.jpda.AbstractDebuggeeTestName
 *      - SerialExecutionDebuggee executes current debuggee's method 'doTest()', in this method debuggee sends signal READY
 *      and waits debugger command
 *      - SerialExecutionDebugger receives signal READY and executes current debugger's method 'doTest()', in
 *      this method debugger should perform test
 *      - when debugger method doTest() finishes SerialExecutionDebugger checks is this test passed or failed and
 *      sends command QUIT to the current debuggee, and when current debuggee finishes sends command 'COMMAND_CLEAR_DEBUGGEE' to the SerialExecutionDebuggee,
 *      after this command SerialExecutionDebugger and SerialExecutionDebuggee ready to execute next test
 *
 *  - when all tests was executed SerialExecutionDebugger sends command QUIT to the SerialExecutionDebuggee and exits
 *
 * SerialExecutionDebugger requires "-configFile <ini-file>" parameter, <ini-file> - file with list of tests for execution
 */
public class SerialExecutionDebugger extends TestDebuggerType2 {
    static public void main(String[] args) {
        System.exit(Consts.JCK_STATUS_BASE + new SerialExecutionDebugger().runIt(args, System.out));
    }

    public String debuggeeClassName() {
        return SerialExecutionDebuggee.class.getName();
    }

    // contains test's debugger class name and test parameters
    static class Test {
        public Test(String debuggerClassName, String[] arguments) {
            this.debuggerClassName = debuggerClassName;
            this.arguments = arguments;
        }

        public String argumentsToString() {
            String result = "";

            for (String argument : arguments)
                result += argument + " ";

            return result;
        }

        String debuggerClassName;

        String arguments[];
    }

    private Test tests[];

    // how many times execute tests
    private int iterations = 1;

    // requires "-configFile <ini-file>" parameter, <ini-file> - file with list
    // of tests for execution
    protected String[] doInit(String args[], PrintStream out) {
        args = super.doInit(args, out);

        String configFileName = null;

        ArrayList<String> standardArgs = new ArrayList<String>();

        for (int i = 0; i < args.length; i++) {
            if (args[i].equals("-configFile") && (i < args.length - 1)) {
                configFileName = args[i + 1];
                i++;
            } else
                standardArgs.add(args[i]);
        }

        if (configFileName == null) {
            throw new TestBug("Config file wasn't specified (use option -configFile <file name>)");
        }

        tests = parseConfigFile(configFileName);

        if (tests.length == 0)
            throw new TestBug("Tests to run were not specified");

        return standardArgs.toArray(new String[] {});
    }

    // read test names and test parameters from ini-file
    private Test[] parseConfigFile(String fileName) {
        List<Test> result = new ArrayList<Test>();

        boolean shuffle = false;

        try {
            File file = new File(fileName);

            LineNumberReader lineReader = new LineNumberReader(new FileReader(file));

            String line = null;

            while ((line = lineReader.readLine()) != null) {
                // skip empty lines and comments started with '#"
                if (line.length() == 0 || line.startsWith("#"))
                    continue;

                if (line.startsWith("OPTIONS:")) {
                    String arguments[] = line.substring(8).split(" ");

                    for (int i = 0; i < arguments.length; i++) {
                        if (arguments[i].equalsIgnoreCase("shuffle"))
                            shuffle = true;
                        else if (arguments[i].equalsIgnoreCase("iterations") && (i < (arguments.length - 1))) {
                            iterations = Integer.parseInt(arguments[i + 1]);
                            i++;
                        }
                    }

                    continue;
                }

                StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(line));
                tokenizer.resetSyntax();
                tokenizer.wordChars(Integer.MIN_VALUE, Integer.MAX_VALUE);
                tokenizer.whitespaceChars(' ', ' ');

                if (tokenizer.nextToken() != StreamTokenizer.TT_WORD)
                    throw new TestBug("Invalid ini file format");

                String testClassName = tokenizer.sval;
                List<String> parameters = new ArrayList<String>();

                int token;
                while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
                    if (token == StreamTokenizer.TT_WORD) {
                        if (tokenizer.sval.equals("$CLASSPATH"))
                            parameters.add(classpath);
                        else
                            parameters.add(tokenizer.sval);
                    }

                    if (token == StreamTokenizer.TT_NUMBER) {
                        parameters.add("" + tokenizer.nval);
                    }
                }

                result.add(new Test(testClassName, parameters.toArray(new String[] {})));
            }

        } catch (IOException e) {
            throw new TestBug("Exception during config file parsing: " + e);
        }

        if (shuffle) {
            if (testWorkDir == null)
                throw new TestBug("Debugger requires -testWorkDir parameter");

            Collections.shuffle(result);

            // save resulted tests sequence in file (to simplify reproducing)
            try {
                File file = new File(testWorkDir + File.separator + "run.tests");
                file.createNewFile();

                PrintWriter writer = new PrintWriter(new FileWriter(file));

                for (Test test : result) {
                    writer.println(test.debuggerClassName + " " + test.argumentsToString());
                }

                writer.close();
            } catch (IOException e) {
                throw new TestBug("Unexpected IOException: " + e);
            }
        }

        System.out.println("Tests execution order: ");
        for (Test test : result) {
            System.out.println(test.debuggerClassName + " " + test.argumentsToString());
        }

        return result.toArray(new Test[] {});
    }

    public void doTest() {

        stresser.start(iterations);

        try {
            if (iterations == 1) {
                /*
                 * Since many test couldn't be run in single VM twice and test config specifies only 1 iteration don't
                 * multiple iterations by iterations factor and execute tests once (not depending on iterations factor)
                 */
                executeTests();
            } else {
                while (stresser.continueExecution()) {
                    if (!executeTests()) {
                        // if error occured stop execution
                        break;
                    }
                }
            }
        } finally {
            stresser.finish();
        }
    }

    boolean executeTests() {
        // maximum execution time of single test
        long maxExecutionTime = 0;

        for (Test test : tests) {
            long testStartTime = System.currentTimeMillis();

            TestDebuggerType2 debugger = null;

            try {
                // create debugger object
                Class debuggerClass = Class.forName(test.debuggerClassName);

                if (!TestDebuggerType2.class.isAssignableFrom(debuggerClass)) {
                    setSuccess(false);
                    log.complain("Invalid debugger class: " + debuggerClass);
                    return false;
                }

                // init test debugger, pass to the debugger already created
                // objects: argHandler, log, pipe, debuggee, vm
                debugger = (TestDebuggerType2) debuggerClass.newInstance();
                debugger.initDebugger(argHandler, log, pipe, debuggee, vm);
                debugger.doInit(test.arguments, System.out);
            } catch (Exception e) {
                setSuccess(false);
                log.complain("Unexpected exception during debugger initialization: " + e);
                e.printStackTrace(log.getOutStream());

                return false;
            }

            log.display("Execute debugger: " + debugger);

            // send command to the SerialExecutionDebuggee (create debuggee
            // object)
            pipe.println(SerialExecutionDebuggee.COMMAND_EXECUTE_DEBUGGEE + ":" + debugger.debuggeeClassName());

            // wait first READY from AbstractDebuggeeTest.doTest() (debuggee
            // sends this command when it was initialized and ready for
            // test)
            if (!isDebuggeeReady())
                return false;

            try {
                // here debuggee should be ready for test and current
                // debugger may perform test
                debugger.doTest();

                if (debugger.getSuccess()) {
                    log.display("Debugger " + debugger + " finished successfully");
                } else {
                    setSuccess(false);
                    log.complain("Debugger " + debugger + " finished with errors");
                }
            } catch (TestBug testBug) {
                setSuccess(false);
                log.complain("Test bug in " + debugger + ": " + testBug);
                testBug.printStackTrace(log.getOutStream());
            } catch (Throwable t) {
                setSuccess(false);
                log.complain("Unexpected exception during test execution(debugger: " + debugger + "): " + t);
                t.printStackTrace(log.getOutStream());
            }

            // send QUIT command to the current debuggee
            pipe.println(AbstractDebuggeeTest.COMMAND_QUIT);

            if (!isDebuggeeReady())
                return false;

            // send command to the SerialExecutionDebuggee
            pipe.println(SerialExecutionDebuggee.COMMAND_CLEAR_DEBUGGEE);

            if (!isDebuggeeReady())
                return false;

            long testExecutionTime = System.currentTimeMillis() - testStartTime;

            if (testExecutionTime > maxExecutionTime)
                maxExecutionTime = testExecutionTime;

            if (maxExecutionTime > stresser.getTimeLeft()) {
                log.display("WARNING: stop test execution because of timeout " +
                                "(max execution time for single test: " + maxExecutionTime + ", time left: " + stresser.getTimeLeft() + ")");
                return false;
            }
        }

        return true;
    }
}