/* * Copyright (c) 2012, 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 com.sun.max.vm.ext.jvmti; import static com.sun.max.vm.jni.JniFunctionsGenerator.Customizer; import java.io.*; import com.sun.max.annotate.*; import com.sun.max.vm.jni.*; import com.sun.max.vm.jni.JniFunctionsGenerator.VmEntryFunctionDeclaration; @HOSTED_ONLY public class JVMTIFunctionsGenerator { private static final String PHASES = "// PHASES: "; private static final String NULLCHECK = "// NULLCHECK: "; private static final String HANDLECHECK = "// HANDLECHECK: "; private static final String HANDLECHECK_NULLOK = "// HANDLECHECK_NULLOK: "; private static final String CAPABILITIES = "// CAPABILITIES: "; private static final String MEMBERID = "// MEMBERID: "; private static final String ENVCHECK = "// ENVCHECK"; private static final String LOGARGS = "// LOGARGS: "; private static final String INDENT4 = " "; private static final String INDENT8 = INDENT4 + INDENT4; private static final String INDENT12 = INDENT8 + INDENT4; private static final String INDENT16 = INDENT12 + INDENT4; private static final String INDENT20 = INDENT16 + INDENT4; private static final String FIRST_LINE_INDENT = INDENT8; public static class JVMTICustomizer extends Customizer { static boolean currentMethodEnvChecked; static String[] logArgs; @Override public String customizeBody(String line) { // a 4 space indent has already been appended String result = customizePhases(line); if (result != null) { return result; } result = customizeNullCheck(line); if (result != null) { return result; } result = customizeTypeCheck(line); if (result != null) { return result; } result = customizeCapabilities(line); if (result != null) { return result; } result = customizeID(line); if (result != null) { return result; } // check for LOGARGS String[] lr = checkLogArgs(line); if (lr != null) { return ""; // drop LOGARGS } // if we get here this a real statement in the body // check environment valid, (once) result = envCheck(line); if (result != null) { return result; } return line; } /** * Check for a PHASES comment. If found generate the check against the current phase. * @param line * @return null for no change or the string to replace the line */ private String customizePhases(String line) { String[] tagArgs = getTagArgs(line, PHASES); if (tagArgs == null) { return null; } if (tagArgs[0].equals("ANY")) { return ""; } StringBuilder sb = new StringBuilder(FIRST_LINE_INDENT); sb.append("if (!("); for (int i = 0; i < tagArgs.length; i++) { if (i > 0) { sb.append(" || "); } sb.append("phase == JVMTI_PHASE_"); sb.append(tagArgs[i]); } sb.append(")"); return closeCheck(sb, "WRONG_PHASE"); } private String customizeNullCheck(String line) { String[] tagArgs = getTagArgs(line, NULLCHECK); if (tagArgs == null) { return null; } StringBuilder sb = new StringBuilder(FIRST_LINE_INDENT); sb.append("if ("); for (int i = 0; i < tagArgs.length; i++) { if (i > 0) { sb.append(" || "); } sb.append(tagArgs[i]); sb.append(".isZero()"); } return closeCheck(sb, "NULL_POINTER"); } private String customizeTypeCheck(String line) { boolean nullCheck = true; String[] tagArgs = getTagArgs(line, HANDLECHECK); if (tagArgs == null) { tagArgs = getTagArgs(line, HANDLECHECK_NULLOK); if (tagArgs == null) { return null; } nullCheck = false; } StringBuilder sb = new StringBuilder(FIRST_LINE_INDENT); for (int i = 0; i < tagArgs.length; i++) { String[] tagParts = tagArgs[i].split("="); String className = tagParts[1]; String varName = tagParts[0]; sb.append(className); sb.append(" handleAs"); sb.append(className); sb.append(";\n"); sb.append(INDENT12); sb.append("try {\n"); sb.append(INDENT16); sb.append("handleAs"); sb.append(className); sb.append(" = "); sb.append("("); sb.append(className); sb.append(") "); sb.append(varName); sb.append(".unhand();\n"); if (nullCheck) { sb.append(INDENT16); sb.append("if (handleAs"); sb.append(className); sb.append(" == null) {\n"); sb.append(INDENT20); sb.append("return JVMTI_ERROR_INVALID_"); sb.append(invalidName(className).toUpperCase()); sb.append(";\n"); sb.append(INDENT16); sb.append("}\n"); } // catch sb.append(INDENT12); sb.append("} catch (ClassCastException ex) {\n"); sb.append(INDENT16); sb.append("return JVMTI_ERROR_INVALID_"); sb.append(invalidName(className).toUpperCase()); sb.append(";\n"); sb.append(INDENT12); sb.append("}"); } return sb.toString(); } private String customizeCapabilities(String line) { String[] tagArgs = getTagArgs(line, CAPABILITIES); if (tagArgs == null) { return null; } StringBuilder sb = new StringBuilder(FIRST_LINE_INDENT); sb.append("if (!("); for (int i = 0; i < tagArgs.length; i++) { if (i > 0) { sb.append(" || "); } sb.append(tagArgs[i]); sb.append(".get(CAPABILITIES.getPtr(env))"); } sb.append(")"); return closeCheck(sb, "MUST_POSSESS_CAPABILITY"); } private String customizeID(String line) { String[] tagArgs = getTagArgs(line, MEMBERID); if (tagArgs == null) { return null; } StringBuilder sb = new StringBuilder(FIRST_LINE_INDENT); for (int i = 0; i < tagArgs.length; i++) { String[] tagParts = tagArgs[i].split("="); String actorNamePair = tagParts[1]; String varName = tagParts[0]; String[] pairParts = actorNamePair.split(":"); boolean subClass = pairParts.length == 2; String actorName = !subClass ? pairParts[0] : pairParts[0] + pairParts[1]; String actorBaseName = !subClass ? pairParts[0] : pairParts[1]; sb.append(actorName); sb.append("Actor "); String lowerActorName = toFirstLower(actorName); sb.append(lowerActorName); sb.append("Actor"); sb.append(" = "); if (subClass) { sb.append("JVMTIUtil.to"); } else { sb.append(actorName); sb.append("ID.to"); } sb.append(actorName); sb.append("Actor("); sb.append(varName); sb.append(");\n"); sb.append(INDENT12); sb.append("if ("); sb.append(lowerActorName); sb.append("Actor == null) {\n"); sb.append(INDENT16); sb.append("return JVMTI_ERROR_INVALID_"); sb.append(actorBaseName.toUpperCase()); sb.append("ID;\n" + INDENT12 + "}"); } return sb.toString(); } private String invalidName(String className) { if (className.equals("Thread") || className.equals("Class")) { return className; } else if (className.equals("ThreadGroup")) { return "Thread_Group"; } else { return "Object"; } } private String envCheck(String line) { if (currentMethodEnvChecked) { return line; } StringBuilder sb = new StringBuilder(FIRST_LINE_INDENT); sb.append("Env jvmtiEnv = JVMTI.getEnv(env);\n"); sb.append(INDENT12); sb.append("if (jvmtiEnv == null) {\n"); sb.append(INDENT16); sb.append("return JVMTI_ERROR_INVALID_ENVIRONMENT;\n"); sb.append(INDENT12); sb.append("}\n "); sb.append(line); currentMethodEnvChecked = true; return sb.toString(); } @Override public String customizeHandler(String returnStatement) { // any failure just means an internal error return " return JVMTI_ERROR_INTERNAL;"; } @Override public void startFunction(VmEntryFunctionDeclaration decl) { super.startFunction(decl); currentMethodEnvChecked = decl.name.equals("SetJVMTIEnv") ? true : false; logArgs = null; } @Override public String customizeTracePrologue(VmEntryFunctionDeclaration decl) { return logging(); } @Override public String customizeTraceEpilogue(VmEntryFunctionDeclaration decl) { return INDENT12 + "// currrently no return logging"; } @Override public void close(PrintWriter writer) throws Exception { super.close(writer); } @Override public void customizeOperations(PrintWriter writer) { } private static String toFirstLower(String s) { return s.substring(0, 1).toLowerCase() + s.substring(1); } private static String[] checkLogArgs(String line) { String[] s = getTagArgs(line, LOGARGS); if (s != null) { logArgs = s; String[] envLogArgs = new String[logArgs.length + 1]; envLogArgs[0] = "env"; System.arraycopy(logArgs, 0, envLogArgs, 1, logArgs.length); logArgs = envLogArgs; } return s; } private static String logging() { StringBuilder sb = new StringBuilder(); String[] args = logArgs; if (args == null) { // no customization, get defaults args = JniFunctionsGenerator.getDefaultArgs(); } sb.append(INDENT8); sb.append("if (logger.enabled()) {\n"); sb.append(INDENT12); sb.append("logger.log(LogOperations."); sb.append(JniFunctionsGenerator.currentMethod.name); sb.append('.'); sb.append("ordinal()"); for (int i = 0; i < args.length; i++) { String tag = args[i]; sb.append(", "); sb.append(tag); } sb.append(");\n"); sb.append(INDENT8); sb.append("}"); return sb.toString(); } } private static String[] getTagArgs(String line, String tag) { int index = line.indexOf(tag); if (index < 0) { return null; } String argList = line.substring(index + tag.length()); return argList.split(","); } private static String closeCheck(StringBuilder sb, String error) { sb.append(") {\n"); sb.append(INDENT16 + "return JVMTI_ERROR_"); sb.append(error); sb.append(";\n" + INDENT12 + "}"); return sb.toString(); } public static void main(String[] args) throws Exception { boolean updated = false; if (JniFunctionsGenerator.generate(false, "com.oracle.max.vm.ext.jvmti", JVMTIFunctionsSource.class, JVMTIFunctions.class, new JVMTICustomizer())) { System.out.println("Source for " + JVMTIFunctions.class + " was updated"); updated = true; } if (updated) { System.exit(1); } } }