/*  -*- Mode: java -*-*/

package ncomp;

import java.io.*;
import java.util.*;

EXTEND ncomp.Macros;

DO_NOT_EDIT
/**
 * This class tests the NCOMP tool and checks the ncomp status of the
 * system classes using exception traces. A method runs ncomped when
 * an exception stack trace entry for it has no line number.  Also,
 * this class compares interpreted and ncomped performance of an
 * iterative factorial algorithm, for types 'long'. Method factorial
 * is expected to run ncomped, method factorial_intp -
 * interpreted. The body of factorial_intp contains a very large block
 * of never-executed code. Since the ncomp tool skips very large
 * methods, factorial_intp runs interpreted. The ratio of execution
 * time for factorial / factorial_intp gives an idea about system
 * operations and effectiveness. If this ratio is ~ 10, NCOMP works as
 * expected. If it is in 1.5 .. 4 range, NCOMP may be using C compiler
 * options with low or no optimization. If this ratio in around 1, the
 * NCOMP tool is not working properly or disabled, or both factorial
 * and factorial_intp run ncomped.
 */
public class Test {
  static String pad = "................................................";
  static int padLimit = pad.length() - 4;

  public static void main(String[] args) { 
    long iterate = 100000;
    int argLen = args.length;
    if (argLen == 1) try { iterate = Long.parseLong(args[0]); } catch (Exception e) {}
    test(iterate); 
  }
    
  public static void test(long iterate) {
    
    report("");
    report("NCOMP TEST  " + new Date());
    report("");
    
    int classesTried = 0;
    int classesNcomped = 0;

    LINENUM_IN_TRACE new Test().clone() "clone" "class java.lang.Object";
    LINENUM_IN_TRACE "".charAt(1) "charAt" "class java.lang.String";
    LINENUM_IN_TRACE new StringReader(null) "StringReader" "class java.io.StringReader";
    LINENUM_IN_TRACE new LinkedList().getFirst() "getFirst" "class java.util.LinkedList";
    
    String status = "FULLY NCOMPED";
    if (classesNcomped < classesTried) status = "PARTLY NCOMPED";
    if (classesNcomped == 0) status = "NOT NCOMPED";
    reportWithPad("SYSTEM CLASSES STATUS", status);    
    report("");

    boolean factorial_isNcomped = false, factorial_intp_isNcomped = false;
    try{ factorial(0);} catch (Exception e) { factorial_isNcomped = reportNcomping(e, "factorial(",")", "method ncomp.Test.factorial") == 1;}
    try{ factorial_intp(0);} catch (Exception e) { factorial_intp_isNcomped = reportNcomping(e, "factorial_intp(",")", "method ncomp.Test.factorial_intp") == 1;}

    long time_2, time_3;

    WITH_TIMER time_2 iterate factorial(20);
    WITH_TIMER time_3 iterate factorial_intp(20);

    String user_ncomp_status = (factorial_isNcomped) ? "ENABLED" : "DISABLED";

    if (factorial_isNcomped && factorial_intp_isNcomped)
      user_ncomp_status += ", PERFORMANCE UNKNOWN SINCE factorial_intp is NCOMPED";
    else {
      if (time_3 > time_2 * 1.5 && time_3 < time_2 * 4) user_ncomp_status += ", C OPTIMIZATION LOW OR DISABLED";
      else if (time_3 > time_2 * 5) {
        String ratioStr = ", RATIO " + ((double)time_3/(double)time_2);
        int dotIdx = ratioStr.indexOf('.');
        if (dotIdx  > -1 && (ratioStr.length() - dotIdx) > 2) ratioStr = ratioStr.substring(0,dotIdx+2);
        user_ncomp_status += ratioStr;
      }
    }
    
    reportWithPad("USER NCOMP STATUS", user_ncomp_status);
    report("");
  }

  static void report (String msg) { System.out.println("# " + msg); }

  static void reportWithPad (String beg, String end) {
    int len = beg.length() + end.length();
    if (len > padLimit) len = padLimit;
    report(beg + pad.substring(len) + end);
  }

  static int reportNcomping (Exception e, String beg, String end, String msg) {
    String traceFragment = traceToSubstring(e, beg, end);
    boolean ncomped  = traceFragment.indexOf(":") < 0;
    String status = (ncomped? "ncomped" : "not ncomped");
    reportWithPad(msg, status);
    return ncomped ? 1 : 0;
  }

  static String traceToSubstring (Exception e, String beg, String end) {
    StringWriter sw = new StringWriter();
    PrintWriter out = new PrintWriter(sw);
    e.printStackTrace(out);
    String trace =  sw.toString();
    return trace.substring(trace.indexOf(beg), trace.indexOf(end)+1);
  }

  static long factorial(long aNumber)
  {
    DEF_FACTORIAL;
  }

  /** 
   * The body of this method contains a large piece of code that is
   * never executed which purpose is to force the NCOMP translator to
   * skip translation of this method. The size is chosen according to 
   * oracle/aurora/ncomp/jvmc/BinaryJavaMethodTranslator#MAX_TRANSLATABLE_METHOD_SIZE.
   * 
   * See Test.jsl and Macros.jsl for details.
   * 
   */
  static long factorial_intp(long aNumber)
  {
    DUMMY_CODE aNumber < -1;
    DEF_FACTORIAL;
  }


}
