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