/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ package com.liferay.whip.instrument; import com.liferay.whip.agent.InstrumentationAgent; import com.liferay.whip.coveragedata.ProjectData; import com.liferay.whip.coveragedata.ProjectDataUtil; import com.liferay.whip.util.ReflectionUtil; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.security.ProtectionDomain; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; /** * @author Shuyang Zhou */ public class WhipClassFileTransformer implements ClassFileTransformer { public WhipClassFileTransformer(String[] includes, String[] excludes) { _includePatterns = new Pattern[includes.length]; for (int i = 0; i < includes.length; i++) { Pattern pattern = Pattern.compile(includes[i]); _includePatterns[i] = pattern; } _excludePatterns = new Pattern[excludes.length]; for (int i = 0; i < excludes.length; i++) { Pattern pattern = Pattern.compile(excludes[i]); _excludePatterns[i] = pattern; } } public boolean matches(String className) { if (className == null) { return false; } if (_excludePatterns.length != 0) { for (Pattern excludePattern : _excludePatterns) { Matcher matcher = excludePattern.matcher(className); if (matcher.matches()) { return false; } } } if (_includePatterns.length != 0) { for (Pattern includePattern : _includePatterns) { Matcher matcher = includePattern.matcher(className); if (matcher.matches()) { return true; } } } return false; } @Override public byte[] transform( ClassLoader classLoader, String className, Class<?> refinedClass, ProtectionDomain protectionDomain, byte[] classfileBuffer) { try { if (matches(className)) { InstrumentationAgent.recordInstrumentation( classLoader, className, classfileBuffer); ProjectData projectData = ProjectDataUtil.getProjectData(); ClassWriter classWriter = new ContextAwareClassWriter( ClassWriter.COMPUTE_FRAMES); String name = className.replace('/', '.'); ClassVisitor classVisitor = new WhipClassVisitor( projectData.getOrCreateClassData(name), classWriter); ClassReader classReader = new ClassReader(classfileBuffer); synchronized (projectData) { classReader.accept(classVisitor, 0); } byte[] data = classWriter.toByteArray(); dumpInstrumentedClass(classLoader, className, data); return data; } } catch (IOException ioe) { ReflectionUtil.throwException(ioe); } return null; } protected void dumpInstrumentedClass( ClassLoader classLoader, String className, byte[] data) throws IOException { if (!Boolean.getBoolean("whip.instrument.dump")) { return; } File logFile = new File(_dumpDir, "instrument.log"); File dumpDir = _dumpDir; int index = className.lastIndexOf('/'); if (index != -1) { dumpDir = new File( dumpDir + "/" + classLoader.toString(), className.substring(0, index)); className = className.substring(index + 1); } dumpDir.mkdirs(); File classFile = new File(dumpDir, className + ".class"); try (OutputStream outputStream = new FileOutputStream(classFile)) { outputStream.write(data); } try (FileWriter fileWriter = new FileWriter(logFile, true)) { fileWriter.write( "Instrumented " + className + " from " + classLoader + " and dumped to " + classFile.getAbsolutePath() + "\n"); } } private static final File _dumpDir; static { RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); String name = runtimeMXBean.getName(); int index = name.indexOf('@'); String processId = null; if (index == -1) { processId = Long.toString(System.currentTimeMillis()); } else { processId = name.substring(0, index); } _dumpDir = new File( System.getProperty("java.io.tmpdir"), "whip-dump/" + processId); } private final Pattern[] _excludePatterns; private final Pattern[] _includePatterns; }