All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.unlogged.core.bytecode.Weaver Maven / Gradle / Ivy

There is a newer version: 0.7.6
Show newest version
package io.unlogged.core.bytecode;


import com.insidious.common.weaver.ClassInfo;
import com.insidious.common.weaver.DataInfo;
import com.insidious.common.weaver.LogLevel;
import com.insidious.common.weaver.MethodInfo;
import io.unlogged.weaver.DataInfoProvider;
import io.unlogged.weaver.TypeHierarchy;
import io.unlogged.weaver.WeaveLog;

import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

/**
 * This class manages bytecode injection process and weaving logs.
 */
public class Weaver {

    //    public static final String PROPERTY_FILE = "weaving.properties";
    public static final String SEPARATOR = ",";
//    public static final char SEPARATOR_CHAR = ',';
//    public static final String CLASS_ID_FILE = "classes.txt";
//    public static final String METHOD_ID_FILE = "methods.txt";
//    public static final String DATA_ID_FILE = "dataids.txt";
//    public static final String ERROR_LOG_FILE = "log.txt";

//    public static final String CATEGORY_WOVEN_CLASSES = "woven-classes";
//    public static final String CATEGORY_ERROR_CLASSES = "error-classes";

    //    private final File outputDir;
    private final String lineSeparator = "\n";
    private final WeaveConfig config;
    private final TypeHierarchy typeHierarchy;
    private DataInfoProvider dataInfoProvider;
    private Writer dataIdWriter;
    private PrintStream logger;
    private int confirmedDataId;
    private int confirmedMethodId;
    private Writer methodIdWriter;
    private boolean dumpOption;
    private MessageDigest digest;

    /**
     * Set up the object to manage a weaving process.
     * This constructor creates files to store the information.
     * 

* // * @param outputDir location to save the weave data * * @param config weave configuration */ public Weaver(WeaveConfig config, TypeHierarchy typeHierarchy) { // assert outputDir.isDirectory() && outputDir.canWrite(); this.typeHierarchy = typeHierarchy; // this.outputDir = outputDir; this.config = config; confirmedDataId = 0; confirmedMethodId = 0; // try { // logger = new PrintStream(new File(outputDir, ERROR_LOG_FILE)); // } catch (FileNotFoundException e) { logger = System.out; // logger.println("[unlogged] failed to open " + ERROR_LOG_FILE + " in " + outputDir.getAbsolutePath()); // logger.println("[unlogged] using System.out instead."); // } // logger.printf("Java version: %s%n", System.getProperty("java.version")); // logger.printf("Agent version: %s%n", agentVersion); try { this.digest = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { this.digest = null; } } public void setDataInfoProvider(DataInfoProvider dataInfoProvider) { this.dataInfoProvider = dataInfoProvider; } /** * Set the bytecode dump option. * * @param dump If true is set, the weaver writes the woven class files to the output directory. */ public void setDumpEnabled(boolean dump) { this.dumpOption = dump; } /** * Execute bytecode injection for a given class. * * @param container specifies a location (e.g. a Jar file path) where a class is loaded. * @param className specifies a class file path. * @param target is the content of the class. * @return a byte array containing the woven class. This method returns null if an error occurred. */ public InstrumentedClass weave(String container, String className, byte[] target) throws IOException { assert container != null; String hash = getClassHash(target); LogLevel level = LogLevel.Normal; int classId = dataInfoProvider.nextClassId(); // System.out.println("Probe class [" + className + "] => " + classId); WeaveLog weaveLog = new WeaveLog(classId, dataInfoProvider); // try { ClassTransformer classTransformer; try { classTransformer = new ClassTransformer(weaveLog, config, target, typeHierarchy); } catch (RuntimeException e) { if (e.getMessage() != null && e.getMessage().startsWith("Method too large")) { // Retry to generate a smaller bytecode by ignoring a large array init block try { weaveLog = new WeaveLog(classId, dataInfoProvider); level = LogLevel.IgnoreArrayInitializer; WeaveConfig smallerConfig = new WeaveConfig(config, level); classTransformer = new ClassTransformer(weaveLog, smallerConfig, target, typeHierarchy); // log("LogLevel.IgnoreArrayInitializer: " + container + "/" + className); } catch (RuntimeException e2) { e2.printStackTrace(); if (e.getMessage() != null && e.getMessage().startsWith("Method too large")) { weaveLog = new WeaveLog(classId, dataInfoProvider); // Retry to generate further smaller bytecode by ignoring except for entry and exit events level = LogLevel.OnlyEntryExit; WeaveConfig smallestConfig = new WeaveConfig(config, level); classTransformer = new ClassTransformer(weaveLog, smallestConfig, target, typeHierarchy); // log("LogLevel.OnlyEntryExit: " + container + "/" + className); } else { // this jumps to catch (Throwable e) in this method // log("Failed to weave class: " + className); // log(e); return new InstrumentedClass(target, new byte[0]); // throw e2; } } } else { // log("Failed to weave class: " + className); // log(e); e.printStackTrace(); return new InstrumentedClass(target, new byte[0]); // this jumps to catch (Throwable e) in this method // throw e; } } ClassInfo classIdEntry = new ClassInfo( classId, container, classTransformer.getSourceFileName(), weaveLog.getFullClassName(), level, hash, classTransformer.getClassLoaderIdentifier(), classTransformer.getInterfaces(), classTransformer.getSuperName(), classTransformer.getSignature() ); // System.err.println("New Class [" + classIdEntry.getClassId() + "] in file [" // + classIdEntry.getFilename() + "] => [" + classIdEntry.getClassName() + "]"); byte[] classWeaveInfoByteArray = finishClassProcess(classIdEntry, weaveLog); // Logging.recordWeaveInfo(classWeaveInfoByteArray, classIdEntry, log); return new InstrumentedClass(classTransformer.getWeaveResult(), classWeaveInfoByteArray); // } catch (Throwable e) { // if (container != null && container.length() > 0) { // log("Failed to weave " + className + " in " + container); // } else { // log("Failed to weave " + className); // } // log(e); // if (dumpOption) doSave(className, target, CATEGORY_ERROR_CLASSES); // return null; // } } /** * Write the weaving result to files. * Without calling this method, this object discards data when a weaving failed. * * @param classInfo records the class information. * @param result records the state after weaving. * @return updated byte code with probes added */ public byte[] finishClassProcess(ClassInfo classInfo, WeaveLog result) { ByteArrayOutputStream boas = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(boas); try { classInfo.writeToOutputStream(out); } catch (IOException e) { e.printStackTrace(logger); } try { ArrayList dataInfoEntries = result.getDataEntries(); out.writeInt(dataInfoEntries.size()); for (DataInfo dataInfo : dataInfoEntries) { dataInfo.writeToStream(out); } } catch (IOException e) { e.printStackTrace(logger); } // Commit method IDs to the final output // confirmedMethodId = result.getNextMethodId(); try { ArrayList methods = result.getMethods(); out.writeInt(methods.size()); for (MethodInfo method : methods) { method.writeToOutputStream(out); } } catch (IOException e) { e.printStackTrace(logger); } return boas.toByteArray(); } /** * Compute SHA-1 Hash for logging. * The hash is important to identify an exact class because * multiple versions of a class may be loaded on a Java Virtual Machine. * * @param targetClass byte content of a class file. * @return a string representation of SHA-1 hash. */ private String getClassHash(byte[] targetClass) { if (digest != null) { byte[] hash = digest.digest(targetClass); StringBuilder hex = new StringBuilder(hash.length * 2); for (byte b : hash) { String l = "0" + Integer.toHexString(b); hex.append(l.substring(l.length() - 2)); } return hex.toString(); } else { return ""; } } // /** // * Write a woven class into a file for a user who // * would like to see the actual file (e.g. for debugging). // * // * @param name specifies a class name. // * @param b is the bytecode content. // * @param category specifies a directory name (CATEGORY_WOVEN_CLASSES, CATEGORY_ERROR_CLASSES). // */ // private void doSave(String name, byte[] b, String category) { // try { // File classDir = new File(outputDir, category); // File classFile = new File(classDir, name + ".class"); // classFile.getParentFile().mkdirs(); // Files.write(classFile.toPath(), b, StandardOpenOption.CREATE, StandardOpenOption.WRITE, // StandardOpenOption.TRUNCATE_EXISTING); // log("Saved " + name + " to " + classFile.getAbsolutePath()); // } catch (IOException e) { // log(e); // } // } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy