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

org.apache.cassandra.utils.HeapUtils Maven / Gradle / Ivy

There is a newer version: 4.3.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.cassandra.utils;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.text.StrBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility to generate heap dumps.
 *
 */
public final class HeapUtils
{
    private static final Logger logger = LoggerFactory.getLogger(HeapUtils.class);

    /**
     * Generates a HEAP dump in the directory specified by the HeapDumpPath JVM option
     * or in the CASSANDRA_HOME directory.
     */
    public static void generateHeapDump()
    {
        Long processId = getProcessId();
        if (processId == null)
        {
            logger.error("The process ID could not be retrieved. Skipping heap dump generation.");
            return;
        }

        String heapDumpPath = getHeapDumpPathOption();
        if (heapDumpPath == null)
        {
            String cassandraHome = System.getenv("CASSANDRA_HOME");
            if (cassandraHome == null)
            {
                return;
            }

            heapDumpPath = cassandraHome;
        }

        Path dumpPath = FileSystems.getDefault().getPath(heapDumpPath);
        if (Files.isDirectory(dumpPath))
        {
            dumpPath = dumpPath.resolve("java_pid" + processId + ".hprof");
        }

        String jmapPath = getJmapPath();

        // The jmap file could not be found. In this case let's default to jmap in the hope that it is in the path.
        String jmapCommand = jmapPath == null ? "jmap" : jmapPath;

        String[] dumpCommands = new String[] {jmapCommand,
                                              "-dump:format=b,file=" + dumpPath,
                                              processId.toString()};

        // Lets also log the Heap histogram
        String[] histoCommands = new String[] {jmapCommand,
                                               "-histo",
                                               processId.toString()};
        try
        {
            logProcessOutput(Runtime.getRuntime().exec(dumpCommands));
            logProcessOutput(Runtime.getRuntime().exec(histoCommands));
        }
        catch (IOException e)
        {
            logger.error("The heap dump could not be generated due to the following error: ", e);
        }
    }

    /**
     * Retrieve the path to the JMAP executable.
     * @return the path to the JMAP executable or null if it cannot be found.
     */
    private static String getJmapPath()
    {
        // Searching in the JAVA_HOME is safer than searching into System.getProperty("java.home") as the Oracle
        // JVM might use the JRE which do not contains jmap.
        String javaHome = System.getenv("JAVA_HOME");
        if (javaHome == null)
            return null;

        File javaBinDirectory = new File(javaHome, "bin");
        File[] files = javaBinDirectory.listFiles(new FilenameFilter()
        {
            public boolean accept(File dir, String name)
            {
                return name.startsWith("jmap");
            }
        });
        return ArrayUtils.isEmpty(files) ? null : files[0].getPath();
    }

    /**
     * Logs the output of the specified process.
     *
     * @param p the process
     * @throws IOException if an I/O problem occurs
     */
    private static void logProcessOutput(Process p) throws IOException
    {
        try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream())))
        {
            StrBuilder builder = new StrBuilder();
            String line;
            while ((line = input.readLine()) != null)
            {
                builder.appendln(line);
            }
            logger.info(builder.toString());
        }
    }

    /**
     * Retrieves the value of the HeapDumpPath JVM option.
     * @return the value of the HeapDumpPath JVM option or null if the value has not been
     * specified.
     */
    private static String getHeapDumpPathOption()
    {
        RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
        List inputArguments = runtimeMxBean.getInputArguments();
        String heapDumpPathOption = null;
        for (String argument : inputArguments)
        {
            if (argument.startsWith("-XX:HeapDumpPath="))
            {
                heapDumpPathOption = argument;
                // We do not break in case the option has been specified several times.
                // In general it seems that JVMs use the right-most argument as the winner.
            }
        }

        if (heapDumpPathOption == null)
            return null;

        return heapDumpPathOption.substring(17, heapDumpPathOption.length());
    }

    /**
     * Retrieves the process ID or null if the process ID cannot be retrieved.
     * @return the process ID or null if the process ID cannot be retrieved.
     */
    private static Long getProcessId()
    {
        long pid = NativeLibrary.getProcessID();
        if (pid >= 0)
            return pid;

        return getProcessIdFromJvmName();
    }

    /**
     * Retrieves the process ID from the JVM name.
     * @return the process ID or null if the process ID cannot be retrieved.
     */
    private static Long getProcessIdFromJvmName()
    {
        // the JVM name in Oracle JVMs is: '@' but this might not be the case on all JVMs
        String jvmName = ManagementFactory.getRuntimeMXBean().getName();
        try
        {
            return Long.valueOf(jvmName.split("@")[0]);
        }
        catch (NumberFormatException e)
        {
            // ignore
        }
        return null;
    }

    /**
     * The class must not be instantiated.
     */
    private HeapUtils()
    {
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy