1/*
2 * Copyright (c) 2000, 2017, 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 * @summary The RMI benchmark test. This java class is used to run the test
27 *          under JTREG.
28 * @library ../../../../testlibrary ../../
29 * @modules java.desktop
30 *          java.rmi/sun.rmi.registry
31 *          java.rmi/sun.rmi.server
32 *          java.rmi/sun.rmi.transport
33 *          java.rmi/sun.rmi.transport.tcp
34 * @build TestLibrary bench.BenchInfo bench.HtmlReporter bench.Util
35 * bench.Benchmark bench.Reporter bench.XmlReporter bench.ConfigFormatException
36 * bench.Harness bench.TextReporter bench.rmi.BenchServer
37 * bench.rmi.DoubleArrayCalls bench.rmi.LongCalls bench.rmi.ShortCalls
38 * bench.rmi.BenchServerImpl bench.rmi.DoubleCalls bench.rmi.Main
39 * bench.rmi.SmallObjTreeCalls bench.rmi.BooleanArrayCalls
40 * bench.rmi.ExceptionCalls bench.rmi.NullCalls bench.rmi.BooleanCalls
41 * bench.rmi.ExportObjs bench.rmi.ObjArrayCalls bench.rmi.ByteArrayCalls
42 * bench.rmi.FloatArrayCalls bench.rmi.ObjTreeCalls bench.rmi.ByteCalls
43 * bench.rmi.FloatCalls bench.rmi.ProxyArrayCalls bench.rmi.CharArrayCalls
44 * bench.rmi.IntArrayCalls bench.rmi.RemoteObjArrayCalls bench.rmi.CharCalls
45 * bench.rmi.IntCalls bench.rmi.ClassLoading bench.rmi.LongArrayCalls
46 * bench.rmi.ShortArrayCalls
47 * bench.rmi.altroot.Node
48 * @run main/othervm/policy=policy.all/timeout=1800 bench.rmi.Main -server -c config
49 * @author Mike Warres, Nigel Daley
50 */
51
52package bench.rmi;
53
54import bench.ConfigFormatException;
55import bench.Harness;
56import bench.HtmlReporter;
57import bench.Reporter;
58import bench.TextReporter;
59import bench.XmlReporter;
60import static bench.rmi.Main.OutputFormat.*;
61import java.io.File;
62import java.io.FileInputStream;
63import java.io.FileNotFoundException;
64import java.io.FileOutputStream;
65import java.io.InputStream;
66import java.io.IOException;
67import java.io.OutputStream;
68import java.io.PrintStream;
69import java.rmi.AlreadyBoundException;
70import java.rmi.NotBoundException;
71import java.rmi.RemoteException;
72import java.rmi.registry.LocateRegistry;
73import java.rmi.registry.Registry;
74import java.rmi.server.RemoteObject;
75import java.util.ArrayList;
76import java.util.List;
77import java.util.Timer;
78import java.util.TimerTask;
79
80/**
81 * RMI/Serialization benchmark tests.
82 */
83public class Main {
84
85    /**
86     * RMI-specific benchmark harness.
87     */
88    static class RMIHarness extends Harness {
89        /**
90         * Construct new RMI benchmark harness.
91         */
92        RMIHarness(InputStream in) throws IOException, ConfigFormatException {
93            super(in);
94        }
95
96        /**
97         * Cleanup both client and server side in between each benchmark.
98         */
99        @Override
100        protected void cleanup() {
101            System.gc();
102            if (Main.runmode == CLIENT) {
103                try {
104                    Main.server.gc();
105                } catch (RemoteException e) {
106                    System.err.println("Warning: server gc failed: " + e);
107                }
108            }
109        }
110    }
111
112    static final String CONFFILE = "config";
113    static final String VERSION = "1.3";
114    static final String REGNAME = "server";
115
116    static final int SAMEVM = 0;
117    static final int CLIENT = 1;
118    static final int SERVER = 2;
119
120    static enum OutputFormat {
121
122        TEXT {
123            @Override
124            Reporter getReport(String title) {
125                return new TextReporter(repstr, title);
126            }
127        },
128        HTML {
129
130            @Override
131            Reporter getReport(String title) {
132                return new HtmlReporter(repstr, title);
133            }
134        },
135        XML {
136            @Override
137            Reporter getReport(String title) {
138                return new XmlReporter(repstr, title);
139            }
140        };
141
142        abstract Reporter getReport(String title);
143    };
144
145    static final String TEST_SRC_PATH = System.getProperty("test.src") + File.separator;
146
147    static boolean verbose;
148    static boolean list;
149    static boolean exitOnTimer;
150    static int testDurationSeconds;
151    static volatile boolean exitRequested;
152    static Timer timer;
153    static OutputFormat format = TEXT;
154    static int runmode;
155    static String confFile;
156    static InputStream confstr;
157    static String repFile;
158    static OutputStream repstr;
159    static String host;
160    static int port;
161    static RMIHarness harness;
162    static Reporter reporter;
163    static BenchServer server;
164    static BenchServerImpl serverImpl;
165
166    /**
167     * Returns reference to benchmark server.
168     *
169     * @return a benchmark server
170     */
171    public static BenchServer getBenchServer() {
172        return server;
173    }
174
175    /**
176     * Prints help message.
177     */
178    static void usage() {
179        PrintStream p = System.err;
180        p.println("\nUsage: java -jar rmibench.jar [-options]");
181        p.println("\nwhere options are:");
182        p.println("  -h                   print this message");
183        p.println("  -v                   verbose mode");
184        p.println("  -l                   list configuration file");
185        p.println("  -t <num hours>       repeat benchmarks for specified number of hours");
186        p.println("  -o <file>            specify output file");
187        p.println("  -c <file>            specify (non-default) "
188                + "configuration file");
189        p.println("  -html                format output as html "
190                + "(default is text)");
191        p.println("  -xml                 format output as xml");
192        p.println("  -server              run benchmark server ");
193        p.println("  -client <host:port>  run benchmark client using server "
194                + "on specified host/port");
195    }
196
197    /**
198     * Throw RuntimeException that wrap message.
199     *
200     * @param mesg a message will be wrapped in the RuntimeException.
201     */
202    static void die(String mesg) {
203        throw new RuntimeException(mesg);
204    }
205
206    /**
207     * Benchmark mainline.
208     *
209     * @param args
210     */
211    public static void main(String[] args) {
212        setupSecurity();
213        parseArgs(args);
214        setupStreams();
215        if (list) {
216            listConfig();
217        } else {
218            setupServer();
219            switch (runmode) {
220                case SAMEVM:
221                case CLIENT:
222                    setupHarness();
223                    setupReporter();
224                    if (exitOnTimer) {
225                        setupTimer(testDurationSeconds);
226                        do {
227                            runBenchmarks();
228                        } while (!exitRequested);
229                    } else {
230                        runBenchmarks();
231                    }
232                    break;
233                case SERVER:
234                    //Setup for client mode, server will fork client process
235                    //after its initiation.
236                    List<String> clientProcessStr = new ArrayList<>();
237                    clientProcessStr.add(System.getProperty("test.jdk") +
238                            File.separator + "bin" + File.separator + "java");
239                    String classpath = System.getProperty("java.class.path");
240                    if (classpath != null) {
241                        clientProcessStr.add("-cp");
242                        clientProcessStr.add(classpath);
243                    }
244                    clientProcessStr.add("-Djava.security.policy=" + TEST_SRC_PATH + "policy.all");
245                    clientProcessStr.add("-Dtest.src=" + TEST_SRC_PATH);
246                    clientProcessStr.add("bench.rmi.Main"); //Client mode
247                    if (verbose) {
248                        clientProcessStr.add("-v");
249                    }
250                    if (list) {
251                        clientProcessStr.add("-l");
252                    }
253                    clientProcessStr.add("-client");
254                    clientProcessStr.add("localhost:" + port);
255
256                    if (exitOnTimer) {
257                        clientProcessStr.add("-t");
258                        clientProcessStr.add(String.valueOf(testDurationSeconds / 3600));
259                    }
260                    if (repFile != null) {
261                        clientProcessStr.add("-o");
262                        clientProcessStr.add(repFile);
263                    }
264                    if (confFile != null) {
265                        clientProcessStr.add("-c");
266                        clientProcessStr.add(confFile);
267                    }
268                    switch (format) {
269                        case HTML:
270                            clientProcessStr.add("-html");
271                            break;
272                        case XML:
273                            clientProcessStr.add("-xml");
274                            break;
275                    }
276
277                    try {
278                        Process client = new ProcessBuilder(clientProcessStr).
279                                inheritIO().start();
280                        try {
281                            client.waitFor();
282                            int exitValue = client.exitValue();
283                            if (0 != exitValue) {
284                                die("Error: error happened in client process, exitValue = " + exitValue);
285                            }
286                        } finally {
287                            client.destroyForcibly();
288                        }
289                    } catch (IOException ex) {
290                        die("Error: Unable start client process, ex=" + ex.getMessage());
291                    } catch (InterruptedException ex) {
292                        die("Error: Error happening to client process, ex=" + ex.getMessage());
293                    }
294                    break;
295            }
296        }
297    }
298
299    /**
300     * Parse command-line arguments.
301     */
302    static void parseArgs(String[] args) {
303        for (int i = 0; i < args.length; i++) {
304            switch (args[i]) {
305                case "-h":
306                    usage();
307                    System.exit(0);
308                    break;
309                case "-v":
310                    verbose = true;
311                    break;
312                case "-l":
313                    list = true;
314                    break;
315                case "-t":
316                    if (++i >= args.length) {
317                        die("Error: no timeout value specified");
318                    }
319                    try {
320                        exitOnTimer = true;
321                        testDurationSeconds = Integer.parseInt(args[i]) * 3600;
322                    } catch (NumberFormatException e) {
323                        die("Error: unable to determine timeout value");
324                    }
325                    break;
326                case "-o":
327                    if (++i >= args.length) {
328                        die("Error: no output file specified");
329                    }
330                    try {
331                        repFile = args[i];
332                        repstr = new FileOutputStream(repFile);
333                    } catch (FileNotFoundException e) {
334                        die("Error: unable to open \"" + args[i] + "\"");
335                    }
336                    break;
337                case "-c":
338                    if (++i >= args.length) {
339                        die("Error: no config file specified");
340                    }
341                    confFile = args[i];
342                    String confFullPath = TEST_SRC_PATH + confFile;
343                    try {
344                        confstr = new FileInputStream(confFullPath);
345                    } catch (FileNotFoundException e) {
346                        die("Error: unable to open \"" + confFullPath + "\"");
347                    }
348                    break;
349                case "-html":
350                    if (format != TEXT) {
351                        die("Error: conflicting formats");
352                    }
353                    format = HTML;
354                    break;
355                case "-xml":
356                    if (format != TEXT) {
357                        die("Error: conflicting formats");
358                    }
359                    format = XML;
360                    break;
361                case "-client":
362                    if (runmode == CLIENT) {
363                        die("Error: multiple -client options");
364                    }
365                    if (runmode == SERVER) {
366                        die("Error: -client and -server options conflict");
367                    }
368                    if (++i >= args.length) {
369                        die("Error: -client missing host/port");
370                    }
371                    try {
372                        String[] hostAndPort = args[i].split(":");
373                        if (hostAndPort.length != 2) {
374                            die("Error: Invalid format host/port:" + args[i]);
375                        }
376                        host = hostAndPort[0];
377                        port = Integer.parseInt(hostAndPort[1]);
378                    } catch (NumberFormatException e) {
379                        die("Error: illegal host/port specified for -client");
380                    }
381                    runmode = CLIENT;
382                    break;
383                case "-server":
384                    if (runmode == CLIENT) {
385                        die("Error: -client and -server options conflict");
386                    }
387                    if (runmode == SERVER) {
388                        die("Error: multiple -server options");
389                    }
390                    try {
391                        //This is the hack code because named package class has
392                        //difficulty in accessing unamed package class. This
393                        //should be removed ater JDK-8003358 is finished.
394                        port = (int) Class.forName("TestLibrary")
395                                .getMethod("getUnusedRandomPort")
396                                .invoke(null);
397                    } catch (ReflectiveOperationException ex) {
398                        die("Error: can't get a free port " + ex);
399                    }
400                    runmode = SERVER;
401                    break;
402                default:
403                    usage();
404                    die("Illegal option: \"" + args[i] + "\"");
405            }
406        }
407    }
408
409    /**
410     * Set up security manager and policy, if not set already.
411     */
412    static void setupSecurity() {
413        if (System.getSecurityManager() != null) {
414            return;
415        }
416
417        /* As of 1.4, it is too late to set the security policy
418         * file at this point so these line have been commented out.
419         */
420        //System.setProperty("java.security.policy",
421        //      Main.class.getResource("/bench/rmi/policy.all").toString());
422        System.setSecurityManager(new SecurityManager());
423    }
424
425    /**
426     * Set up configuration file and report streams, if not set already.
427     */
428    static void setupStreams() {
429        if (repstr == null) {
430            repstr = System.out;
431        }
432        if (confstr == null) {
433            confstr = Main.class.getResourceAsStream(TEST_SRC_PATH + CONFFILE);
434        }
435        if (confstr == null) {
436            die("Error: unable to find default config file");
437        }
438    }
439
440    /**
441     * Print contents of configuration file to selected output stream.
442     */
443    static void listConfig() {
444        try {
445            byte[] buf = new byte[256];
446            int len;
447            while ((len = confstr.read(buf)) != -1)
448                repstr.write(buf, 0, len);
449        } catch (IOException e) {
450            die("Error: failed to list config file");
451        }
452    }
453
454    /**
455     * Setup benchmark server.
456     */
457    static void setupServer() {
458        switch (runmode) {
459            case SAMEVM:
460                try {
461                    serverImpl = new BenchServerImpl();
462                    server = (BenchServer) RemoteObject.toStub(serverImpl);
463                } catch (RemoteException e) {
464                    die("Error: failed to create local server: " + e);
465                }
466                if (verbose)
467                    System.out.println("Benchmark server created locally");
468                break;
469
470            case CLIENT:
471                try {
472                    Registry reg = LocateRegistry.getRegistry(host, port);
473                    server = (BenchServer) reg.lookup(REGNAME);
474                } catch (NotBoundException | RemoteException e) {
475                    die("Error: failed to connect to server: " + e);
476                }
477                if (server == null) {
478                    die("Error: server not found");
479                }
480                if (verbose) {
481                    System.out.println("Connected to benchmark server on " +
482                            host + ":" + port);
483                }
484                break;
485
486            case SERVER:
487                try {
488                    Registry reg = LocateRegistry.createRegistry(port);
489                    serverImpl = new BenchServerImpl();
490                    reg.bind(REGNAME, serverImpl);
491                } catch (AlreadyBoundException | RemoteException e) {
492                    die("Error: failed to initialize server: " + e);
493                }
494                if (verbose) {
495                    System.out.println("Benchmark server started on port " +
496                            port);
497                }
498                break;
499
500            default:
501                throw new InternalError("illegal runmode");
502        }
503    }
504
505    /**
506     * Set up the timer to end the test.
507     *
508     * @param delay the amount of delay, in seconds, before requesting the
509     * process exit
510     */
511    static void setupTimer(int delay) {
512        timer = new Timer(true);
513        timer.schedule(
514                new TimerTask() {
515                    @Override
516                    public void run() {
517                        exitRequested = true;
518                    }
519                },
520                delay * 1000);
521    }
522
523    /**
524     * Set up benchmark harness.
525     */
526    static void setupHarness() {
527        try {
528            harness = new RMIHarness(confstr);
529        } catch (ConfigFormatException e) {
530            String errmsg = e.getMessage();
531            if (errmsg != null) {
532                die("Error parsing config file: " + errmsg);
533            } else {
534                die("Error: illegal config file syntax");
535            }
536        } catch (IOException e) {
537            die("Error: failed to read config file");
538        }
539    }
540
541    /**
542     * Setup benchmark reporter.
543     */
544    static void setupReporter() {
545        reporter = format.getReport("RMI Benchmark, v" + VERSION);
546    }
547
548    /**
549     * Run benchmarks.
550     */
551    static void runBenchmarks() {
552        harness.runBenchmarks(reporter, verbose);
553    }
554}
555