Mercurial > hg > thermostat-ng > agent
view plugins/vm-byteman/agent/src/main/java/com/redhat/thermostat/vm/byteman/agent/internal/BytemanAgentAttachManager.java @ 2760:22f2b3ea3609
Add byteman integration services
Reviewed-by: jerboaa
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-September/025152.html
author | Jie Kang <jkang@redhat.com> |
---|---|
date | Fri, 22 Sep 2017 10:10:52 -0400 |
parents | 65616775c98f |
children |
line wrap: on
line source
/* * Copyright 2012-2017 Red Hat, Inc. * * This file is part of Thermostat. * * Thermostat is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. * * Thermostat 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 for more details. * * You should have received a copy of the GNU General Public License * along with Thermostat; see the file COPYING. If not see * <http://www.gnu.org/licenses/>. * * Linking this code with other modules is making a combined work * based on this code. Thus, the terms and conditions of the GNU * General Public License cover the whole combination. * * As a special exception, the copyright holders of this code give * you permission to link this code with independent modules to * produce an executable, regardless of the license terms of these * independent modules, and to copy and distribute the resulting * executable under terms of your choice, provided that you also * meet, for each linked independent module, the terms and conditions * of the license of that module. An independent module is a module * which is not derived from or based on this code. If you modify * this code, you may extend this exception to your version of the * library, but you are not obligated to do so. If you do not wish * to do so, delete this exception statement from your version. */ package com.redhat.thermostat.vm.byteman.agent.internal; import java.io.File; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.attribute.UserPrincipal; import java.nio.file.attribute.UserPrincipalLookupService; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import org.jboss.byteman.agent.submit.Submit; import com.redhat.thermostat.agent.ipc.server.ThermostatIPCCallbacks; import com.redhat.thermostat.common.portability.ProcessUserInfo; import com.redhat.thermostat.common.portability.ProcessUserInfoBuilder; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.jvm.overview.agent.model.VmId; import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.storage.core.WriterID; import com.redhat.thermostat.vm.byteman.agent.VmBytemanDAO; import com.redhat.thermostat.vm.byteman.agent.VmBytemanStatus; /** * Manages attaching of the byteman agent as well as adding * helper jars to the target JVMs classpath. * */ class BytemanAgentAttachManager { private static final String BYTEMAN_PLUGIN_DIR = System.getProperty("thermostat.plugin", "vm-byteman"); private static final String BYTEMAN_PLUGIN_LIBS_DIR = BYTEMAN_PLUGIN_DIR + File.separator + "plugin-libs"; private static final String BYTEMAN_INSTALL_HOME = BYTEMAN_PLUGIN_LIBS_DIR + File.separator + "byteman-install"; private static final String BYTEMAN_HELPER_DIR = BYTEMAN_PLUGIN_LIBS_DIR + File.separator + "thermostat-helper"; private static final String BYTEMAN_HOME_PROPERTY = "org.jboss.byteman.home"; private static final Logger logger = LoggingUtils.getLogger(BytemanAgentAttachManager.class); // package-private for testing static final String THERMOSTAT_HELPER_SOCKET_NAME_PROPERTY = "org.jboss.byteman.thermostat.socketName"; static List<String> helperJars; private final SubmitHelper submit; private final FileSystemUtils fsUtils; private BytemanAttacher attacher; private IPCEndpointsManager ipcManager; private VmBytemanDAO vmBytemanDao; private WriterID writerId; private ProcessUserInfoBuilder userInfoBuilder; BytemanAgentAttachManager() { this.submit = new SubmitHelper(); this.fsUtils = new FileSystemUtils(); } // for testing only BytemanAgentAttachManager(BytemanAttacher attacher, IPCEndpointsManager ipcManager, VmBytemanDAO vmBytemanDao, SubmitHelper submit, WriterID writerId, ProcessUserInfoBuilder userInfoBuilder, FileSystemUtils fsUtils) { this.attacher = attacher; this.ipcManager = ipcManager; this.vmBytemanDao = vmBytemanDao; this.submit = submit; this.writerId = writerId; this.userInfoBuilder = userInfoBuilder; this.fsUtils = fsUtils; } VmBytemanStatus attachBytemanToVm(VmId vmId, int vmPid) { logger.fine("Attaching byteman agent to VM '" + vmPid + "'"); // Fail early if we can't determine process owner UserPrincipal owner = getUserPrincipalForPid(vmPid); if (owner == null) { return null; } BytemanAgentInfo info = attacher.attach(vmId.get(), vmPid, writerId.getWriterID()); if (info == null) { logger.warning("Failed to attach byteman agent for VM '" + vmPid + "'. Skipping rule updater and IPC channel."); return null; } VmSocketIdentifier socketId = new VmSocketIdentifier(vmId.get(), vmPid, writerId.getWriterID()); boolean postAttachGood = performPostAttachSteps(info, socketId); if (!postAttachGood) { return null; } ThermostatIPCCallbacks callback = new BytemanMetricsReceiver(vmBytemanDao, socketId); ipcManager.startIPCEndpoint(socketId, callback, owner); // Add a status record to storage VmBytemanStatus status = new VmBytemanStatus(writerId.getWriterID()); status.setListenPort(info.getAgentListenPort()); status.setTimeStamp(System.currentTimeMillis()); status.setJvmId(vmId.get()); vmBytemanDao.addBytemanStatus(status); return status; } private UserPrincipal getUserPrincipalForPid(int vmPid) { UserPrincipal principal = null; ProcessUserInfo info = userInfoBuilder.build(vmPid); String username = info.getUsername(); if (username == null) { logger.warning("Unable to determine owner of VM '" + vmPid + "'. Skipping rule updater and IPC channel."); } else { UserPrincipalLookupService lookup = fsUtils.getUserPrincipalLookupService(); try { principal = lookup.lookupPrincipalByName(username); } catch (IOException e) { logger.log(Level.WARNING, "Invalid user name '" + username + "' for VM '" + vmPid + "'. Skipping rule updater and IPC channel.", e); } } return principal; } private boolean performPostAttachSteps(BytemanAgentInfo info, VmSocketIdentifier socketId) { if (info.isAttachFailedNoSuchProcess()) { logger.finest("Process with pid " + info.getVmPid() + " went away before we could attach the byteman agent to it."); return false; } if (info.isOldAttach()) { logger.finest("Proceeding with post-attach steps for already attached byteman agent"); Properties properties = new Properties(); properties.setProperty(THERMOSTAT_HELPER_SOCKET_NAME_PROPERTY, socketId.getName()); return submit.setSystemProperties(properties, info); } else { logger.fine("Attached byteman agent to VM '" + info.getVmPid() + "' at port: '" + info.getAgentListenPort()); boolean addJarsSuccess = addThermostatHelperJarsToClasspath(info); if (!addJarsSuccess) { logger.warning("VM '" + info.getVmPid() + "': Failed to add helper jars to target VM's classpath."); } return addJarsSuccess; } } private boolean addThermostatHelperJarsToClasspath(BytemanAgentInfo info) { return submit.addJarsToSystemClassLoader(helperJars, info); } static List<String> initListOfHelperJars(CommonPaths commonPaths) { File bytemanHelperDir = new File(commonPaths.getSystemPluginRoot(), BYTEMAN_HELPER_DIR); return initListOfHelperJars(bytemanHelperDir); } // package private for testing static synchronized List<String> initListOfHelperJars(File helperDir) { if (helperJars == null) { List<String> jars = new ArrayList<>(); for (File f: helperDir.listFiles()) { jars.add(f.getAbsolutePath()); } helperJars = jars; } return helperJars; } static synchronized void setBytemanHomeProperty(CommonPaths commonPaths) { final String bytemanHomePropVal = System.getProperty(BYTEMAN_HOME_PROPERTY); if (bytemanHomePropVal == null) { String bytemanHome = commonPaths.getSystemPluginRoot().getAbsolutePath() + File.separator + BYTEMAN_INSTALL_HOME; // This will depend on BYTEMAN-303 being fixed and incorporated in a release logger.fine("Setting system property " + BYTEMAN_HOME_PROPERTY + "=" + bytemanHome); System.setProperty(BYTEMAN_HOME_PROPERTY, bytemanHome); } } void setAttacher(BytemanAttacher attacher) { this.attacher = attacher; } void setPaths(CommonPaths paths) { initListOfHelperJars(paths); setBytemanHomeProperty(paths); } void setIpcManager(IPCEndpointsManager ipcManager) { this.ipcManager = ipcManager; } void setVmBytemanDao(VmBytemanDAO vmBytemanDao) { this.vmBytemanDao = vmBytemanDao; } void setWriterId(WriterID writerId) { this.writerId = writerId; } void setUserInfoBuilder(ProcessUserInfoBuilder userInfoBuilder) { this.userInfoBuilder = userInfoBuilder; } static class SubmitHelper { boolean addJarsToSystemClassLoader(List<String> jars, BytemanAgentInfo info) { Submit submit = new Submit(null /* localhost */, info.getAgentListenPort()); try { String addJarsResult = submit.addJarsToSystemClassloader(jars); logger.fine("Added jars for byteman helper with result: " + addJarsResult); return true; } catch (Exception e) { logger.log(Level.INFO, e.getMessage(), e); return false; } } boolean setSystemProperties(Properties properties, BytemanAgentInfo info) { Submit submit = new Submit(null /* localhost */, info.getAgentListenPort()); try { String result = submit.setSystemProperties(properties); logger.fine("Re-set system properties with result: " + result); return true; } catch (Exception e) { logger.log(Level.INFO, e.getMessage(), e); return false; } } } static class FileSystemUtils { UserPrincipalLookupService getUserPrincipalLookupService() { return FileSystems.getDefault().getUserPrincipalLookupService(); } } }