1 /*
   2  * Copyright (c) 2000, 2013, 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 
  52 package bench.rmi;
  53 
  54 import bench.ConfigFormatException;
  55 import bench.Harness;
  56 import bench.HtmlReporter;
  57 import bench.Reporter;
  58 import bench.TextReporter;
  59 import bench.XmlReporter;
  60 import static bench.rmi.Main.OutputFormat.*;
  61 import java.io.File;
  62 import java.io.FileInputStream;
  63 import java.io.FileNotFoundException;
  64 import java.io.FileOutputStream;
  65 import java.io.InputStream;
  66 import java.io.IOException;
  67 import java.io.OutputStream;
  68 import java.io.PrintStream;
  69 import java.rmi.AlreadyBoundException;
  70 import java.rmi.NotBoundException;
  71 import java.rmi.RemoteException;
  72 import java.rmi.registry.LocateRegistry;
  73 import java.rmi.registry.Registry;
  74 import java.rmi.server.RemoteObject;
  75 import java.util.ArrayList;
  76 import java.util.List;
  77 import java.util.Timer;
  78 import java.util.TimerTask;
  79 
  80 /**
  81  * RMI/Serialization benchmark tests.
  82  */
  83 public 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                         client.waitFor();
 281                         int exitValue = client.exitValue();
 282                         if (0 != exitValue) {
 283                             die("Error: error happened in client process, exitValue = " + exitValue);
 284                         }
 285                     } catch (IOException ex) {
 286                         die("Error: Unable start client process, ex=" + ex.getMessage());
 287                     } catch (InterruptedException ex) {
 288                         die("Error: Error happening to client process, ex=" + ex.getMessage());
 289                     }
 290                     break;
 291             }
 292         }
 293     }
 294 
 295     /**
 296      * Parse command-line arguments.
 297      */
 298     static void parseArgs(String[] args) {
 299         for (int i = 0; i < args.length; i++) {
 300             switch (args[i]) {
 301                 case "-h":
 302                     usage();
 303                     System.exit(0);
 304                     break;
 305                 case "-v":
 306                     verbose = true;
 307                     break;
 308                 case "-l":
 309                     list = true;
 310                     break;
 311                 case "-t":
 312                     if (++i >= args.length) {
 313                         die("Error: no timeout value specified");
 314                     }
 315                     try {
 316                         exitOnTimer = true;
 317                         testDurationSeconds = Integer.parseInt(args[i]) * 3600;
 318                     } catch (NumberFormatException e) {
 319                         die("Error: unable to determine timeout value");
 320                     }
 321                     break;
 322                 case "-o":
 323                     if (++i >= args.length) {
 324                         die("Error: no output file specified");
 325                     }
 326                     try {
 327                         repFile = args[i];
 328                         repstr = new FileOutputStream(repFile);
 329                     } catch (FileNotFoundException e) {
 330                         die("Error: unable to open \"" + args[i] + "\"");
 331                     }
 332                     break;
 333                 case "-c":
 334                     if (++i >= args.length) {
 335                         die("Error: no config file specified");
 336                     }
 337                     confFile = args[i];
 338                     String confFullPath = TEST_SRC_PATH + confFile;
 339                     try {
 340                         confstr = new FileInputStream(confFullPath);
 341                     } catch (FileNotFoundException e) {
 342                         die("Error: unable to open \"" + confFullPath + "\"");
 343                     }
 344                     break;
 345                 case "-html":
 346                     if (format != TEXT) {
 347                         die("Error: conflicting formats");
 348                     }
 349                     format = HTML;
 350                     break;
 351                 case "-xml":
 352                     if (format != TEXT) {
 353                         die("Error: conflicting formats");
 354                     }
 355                     format = XML;
 356                     break;
 357                 case "-client":
 358                     if (runmode == CLIENT) {
 359                         die("Error: multiple -client options");
 360                     }
 361                     if (runmode == SERVER) {
 362                         die("Error: -client and -server options conflict");
 363                     }
 364                     if (++i >= args.length) {
 365                         die("Error: -client missing host/port");
 366                     }
 367                     try {
 368                         String[] hostAndPort = args[i].split(":");
 369                         if (hostAndPort.length != 2) {
 370                             die("Error: Invalid format host/port:" + args[i]);
 371                         }
 372                         host = hostAndPort[0];
 373                         port = Integer.parseInt(hostAndPort[1]);
 374                     } catch (NumberFormatException e) {
 375                         die("Error: illegal host/port specified for -client");
 376                     }
 377                     runmode = CLIENT;
 378                     break;
 379                 case "-server":
 380                     if (runmode == CLIENT) {
 381                         die("Error: -client and -server options conflict");
 382                     }
 383                     if (runmode == SERVER) {
 384                         die("Error: multiple -server options");
 385                     }
 386                     try {
 387                         //This is the hack code because named package class has
 388                         //difficulty in accessing unamed package class. This
 389                         //should be removed ater JDK-8003358 is finished.
 390                         port = (int) Class.forName("TestLibrary")
 391                                 .getMethod("getUnusedRandomPort")
 392                                 .invoke(null);
 393                     } catch (ReflectiveOperationException ex) {
 394                         die("Error: can't get a free port " + ex);
 395                     }
 396                     runmode = SERVER;
 397                     break;
 398                 default:
 399                     usage();
 400                     die("Illegal option: \"" + args[i] + "\"");
 401             }
 402         }
 403     }
 404 
 405     /**
 406      * Set up security manager and policy, if not set already.
 407      */
 408     static void setupSecurity() {
 409         if (System.getSecurityManager() != null) {
 410             return;
 411         }
 412 
 413         /* As of 1.4, it is too late to set the security policy
 414          * file at this point so these line have been commented out.
 415          */
 416         //System.setProperty("java.security.policy",
 417         //      Main.class.getResource("/bench/rmi/policy.all").toString());
 418         System.setSecurityManager(new SecurityManager());
 419     }
 420 
 421     /**
 422      * Set up configuration file and report streams, if not set already.
 423      */
 424     static void setupStreams() {
 425         if (repstr == null) {
 426             repstr = System.out;
 427         }
 428         if (confstr == null) {
 429             confstr = Main.class.getResourceAsStream(TEST_SRC_PATH + CONFFILE);
 430         }
 431         if (confstr == null) {
 432             die("Error: unable to find default config file");
 433         }
 434     }
 435 
 436     /**
 437      * Print contents of configuration file to selected output stream.
 438      */
 439     static void listConfig() {
 440         try {
 441             byte[] buf = new byte[256];
 442             int len;
 443             while ((len = confstr.read(buf)) != -1)
 444                 repstr.write(buf, 0, len);
 445         } catch (IOException e) {
 446             die("Error: failed to list config file");
 447         }
 448     }
 449 
 450     /**
 451      * Setup benchmark server.
 452      */
 453     static void setupServer() {
 454         switch (runmode) {
 455             case SAMEVM:
 456                 try {
 457                     serverImpl = new BenchServerImpl();
 458                     server = (BenchServer) RemoteObject.toStub(serverImpl);
 459                 } catch (RemoteException e) {
 460                     die("Error: failed to create local server: " + e);
 461                 }
 462                 if (verbose)
 463                     System.out.println("Benchmark server created locally");
 464                 break;
 465 
 466             case CLIENT:
 467                 try {
 468                     Registry reg = LocateRegistry.getRegistry(host, port);
 469                     server = (BenchServer) reg.lookup(REGNAME);
 470                 } catch (NotBoundException | RemoteException e) {
 471                     die("Error: failed to connect to server: " + e);
 472                 }
 473                 if (server == null) {
 474                     die("Error: server not found");
 475                 }
 476                 if (verbose) {
 477                     System.out.println("Connected to benchmark server on " +
 478                             host + ":" + port);
 479                 }
 480                 break;
 481 
 482             case SERVER:
 483                 try {
 484                     Registry reg = LocateRegistry.createRegistry(port);
 485                     serverImpl = new BenchServerImpl();
 486                     reg.bind(REGNAME, serverImpl);
 487                 } catch (AlreadyBoundException | RemoteException e) {
 488                     die("Error: failed to initialize server: " + e);
 489                 }
 490                 if (verbose) {
 491                     System.out.println("Benchmark server started on port " +
 492                             port);
 493                 }
 494                 break;
 495 
 496             default:
 497                 throw new InternalError("illegal runmode");
 498         }
 499     }
 500 
 501     /**
 502      * Set up the timer to end the test.
 503      *
 504      * @param delay the amount of delay, in seconds, before requesting the
 505      * process exit
 506      */
 507     static void setupTimer(int delay) {
 508         timer = new Timer(true);
 509         timer.schedule(
 510                 new TimerTask() {
 511                     @Override
 512                     public void run() {
 513                         exitRequested = true;
 514                     }
 515                 },
 516                 delay * 1000);
 517     }
 518 
 519     /**
 520      * Set up benchmark harness.
 521      */
 522     static void setupHarness() {
 523         try {
 524             harness = new RMIHarness(confstr);
 525         } catch (ConfigFormatException e) {
 526             String errmsg = e.getMessage();
 527             if (errmsg != null) {
 528                 die("Error parsing config file: " + errmsg);
 529             } else {
 530                 die("Error: illegal config file syntax");
 531             }
 532         } catch (IOException e) {
 533             die("Error: failed to read config file");
 534         }
 535     }
 536 
 537     /**
 538      * Setup benchmark reporter.
 539      */
 540     static void setupReporter() {
 541         reporter = format.getReport("RMI Benchmark, v" + VERSION);
 542     }
 543 
 544     /**
 545      * Run benchmarks.
 546      */
 547     static void runBenchmarks() {
 548         harness.runBenchmarks(reporter, verbose);
 549     }
 550 }