view analyzer/cli/src/main/java/jp/co/ntt/oss/heapstats/cli/Options.java @ 238:55773172374f

Bug 3219: Upload artifacts to the Maven Central Repository Reviewed-by: yasuenag GitHub: https://github.com/HeapStats/heapstats/pull/105
author KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp>
date Mon, 26 Jun 2017 21:05:32 +0900
parents f2da2ff49cbc
children
line wrap: on
line source

/*
 * Copyright (C) 2015 Yasumasa Suenaga
 *
 * This program 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
 * of the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package jp.co.ntt.oss.heapstats.cli;

import java.io.File;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.management.remote.JMXServiceURL;
import javax.xml.bind.JAXB;
import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData;
import jp.co.ntt.oss.heapstats.container.threadrecord.ThreadStat;
import jp.co.ntt.oss.heapstats.xml.binding.Filters;

/**
 * HeapStats CLI commandline options.
 * 
 * @author Yasumasa Suenaga
 */
public class Options {
    
    /**
     * File type to parse.
     */
    public enum FileType{
        LOG,
        SNAPSHOT,
        THREADRECORD,
        JMX
    }
    
    /**
     * Parse mode.
     */
    public static enum Mode{
        /* Log (-log) */
        JAVA_CPU,       /* -j */
        SYSTEM_CPU,     /* -c */
        MEMORIES,       /* -m */
        SAFEPOINTS,     /* -s */
        MONITORS,       /* -l */
        THREADS,        /* -t */
        PARSE_ARCHIVES, /* -a */
        
        /* Heap SnapShot (-snapshot) */
        SNAPSHOT_SUMMARY, /* -s */
        CLASS_HISTO,      /* -c */
        DIFF_HISTO,       /* -d */
        CLASS_REFERENCES, /* -r */
        HEAP_CSV,         /* -e */
        GC_CSV,           /* -g */
        
        /* Thread Recorder (-record) */
        SHOW_THREAD_RECORD_ID, /* -threads */
        DUMP_THREAD_RECORD,    /* -a */
        DUMP_SUSPEND_EVENTS,   /* -s */
        DUMP_LOCK_EVENTS,      /* -l */
        DUMP_IO_EVENTS,        /* -i */
        
        /* JMX access (-jmx) */
        JMX_GET_VERSION,     /* -v */
        JMX_GET_SNAPSHOT,    /* -s */
        JMX_GET_LOG,         /* -r */
        JMX_GET_CONFIG,      /* -c */
        JMX_CHANGE_CONFIG,   /* -n */
        JMX_INVOKE_SNAPSHOT, /* -is */
        JMX_INVOKE_LOG,      /* -ir */
        JMX_INVOKE_ALL_LOG   /* -ia */
    }
    
    /**
     * File list to parse.
     */
    private List<Path> file;
    
    /**
     * File type.
     */
    private FileType type;
    
    /**
     * Parse mode.
     */
    private Mode mode;
    
    /* -filter */
    private Optional<Pattern> filter = Optional.empty();
    
    /* -exclude */
    private Optional<Path> excludeFilterFile = Optional.empty();
    
    /* -start */
    private OptionalInt start = OptionalInt.empty();
    
    /* -end */
    private OptionalInt end = OptionalInt.empty();
    
    /* -showids */
    private boolean showId = false;
    
    /**
     * Start of reference.
     * This option effects -snapshot -r only.
     */
    private long refStartTag;
    
    /**
     * Direction of reference traverse.
     * This option effects -snapshot -r only.
     */
    private boolean refToParent;
    
    /**
     * CSV file name to dump.
     * This option effects -snapshot -e or -g only.
     */
    private File csvFile;

    /**
     * Thread ID to dump.
     * This option effects -event only.
     */
    private OptionalLong tid = OptionalLong.empty();
    
    /**
     * JMX URL to connect.
     * This value affects -jmx only.
     */
    private JMXServiceURL jmxURL;
    
    /**
     * Configuration key of HeapStats agent.
     * This value affects -jmx only.
     */
    private String configKey;
    
    /**
     * New configuration value of HeapStats agent.
     * This value affects -jmx only.
     */
    private String newConfigValue;
    
    /**
     * Print help message of HeapStats CLI.
     */
    public void printHelp(){
        System.out.println("Usage:");
        System.out.println("  java -jar heapstats-cli.jar <common options> <mode> <options> <file...>");
        System.out.println();
        System.out.println("common options:");
        System.out.println("  -start: Start ID");
        System.out.println("  -end  : End ID");
        System.out.println();
        System.out.println("mode:");
        System.out.println("  -log     : Process HeapStats resource log file.");
        System.out.println("  -snapshot: Process HeapStats snapshot file.");
        System.out.println("  -event   : Process HeapStats thread recorder file.");
        System.out.println("  -jmx     : Control remote HeapStats agent through JMX.");
        System.out.println();
        System.out.println("options:");
        System.out.println("  -log:");
        System.out.println("    -showids: List all IDs in files.");
        System.out.println("    -i      : Show CPU percentage in java process.");
        System.out.println("    -c      : Show CPU usage all over the system.");
        System.out.println("    -m      : Show VSZ/RSS usage at java process.");
        System.out.println("    -s      : Show Safepoint count/time at java process.");
        System.out.println("    -l      : Show monitor contention count at java process.");
        System.out.println("    -t      : Show count of live java threads at java process.");
        System.out.println("    -a      : Report and extract error archive in resource file.");
        System.out.println("  -snapshot:");
        System.out.println("    -showids      : List all IDs in files.");
        System.out.println("    -filter       : Set filter to out. You can use regex.");
        System.out.println("    -exclude      : Set exclude filter to out. You have to pass exclude XML file.");
        System.out.println("    -s            : Show snapshot summary.");
        System.out.println("    -c            : Show class histogram.");
        System.out.println("    -d            : Show histogram from diff of snapshots.");
        System.out.println("    -r <class tag>: Show class references. You have to pass class id as start point.");
        System.out.println("      -d <p|c>: Select the direction to traverse references. p means parent, c means child.");
        System.out.println("    -e <CSV file> : Dump class histogram(s) as CSV.");
        System.out.println("    -g <CSV file> : Dump GC information as CSV.");
        System.out.println("  -event: Process HeapStats thread recorder file.");
        System.out.println("    -showids      : List all IDs in files.");
        System.out.println("    -threads: List all thread IDs in files.");
        System.out.println("    -id <ID>: Choose specified thread ID.");
        System.out.println("    -a      : List all events.");
        System.out.println("    -s      : List suspend events.");
        System.out.println("    -l      : List lock events.");
        System.out.println("    -i      : List I/O events.");
        System.out.println("  -jmx <URL> : Access remote HeapStats agent through JMX.");
        System.out.println("    -v               : Get version of HeapStats agent.");
        System.out.println("    -s               : Save remote HeapStats snapshot to file.");
        System.out.println("    -r               : Save remote HeapStats resource log to file.");
        System.out.println("    -c <name>        : Get configuration value of <name> in remote HeapStats agent.");
        System.out.println("    -n <name> <value>: Change configuration value of <name> to <value> in remote HeapStats agent.");
        System.out.println("    -is              : Invoke SnapShot collection.");
        System.out.println("    -ir              : Invoke resource log collection.");
        System.out.println("    -ia              : Invoke all log collection.");
    }
    
    /**
     * Get next value from iterator.
     * @param itr Iterator of commandline options.
     * @param errorMessage Exception message if iterator has no more option.
     * @return Option value.
     */
    private String getNextValue(Iterator<String> itr, String errorMessage){
        
        if(!itr.hasNext()){
            throw new IllegalArgumentException(errorMessage);
        }
        
        return itr.next();
    }
    
    /**
     * Parse commandline options for -log option.
     * @param itr List iterator of commandline options.
     */
    private void parseLogOptions(Iterator<String> itr){
        String option = getNextValue(itr, "-log option need more argument.");
        type = FileType.LOG;
        
        switch(option){
            case "-showids":
                showId = true;
                break;
            case "-i":
                mode = Mode.JAVA_CPU;
                break;
            case "-c":
                mode = Mode.SYSTEM_CPU;
                break;
            case "-m":
                mode = Mode.MEMORIES;
                break;
            case "-s":
                mode = Mode.SAFEPOINTS;
                break;
            case "-l":
                mode = Mode.MONITORS;
                break;
            case "-t":
                mode = Mode.THREADS;
                break;
            case "-a":
                mode = Mode.PARSE_ARCHIVES;
                break;
            default:
                throw new IllegalArgumentException("Unknown option: " + option);
        }
        
    }
    
    /**
     * Parse commandline options for -snapshot option.
     * @param itr List iterator of commandline options.
     */
    private void parseSnapShotOptions(ListIterator<String> itr){
        type = FileType.SNAPSHOT;
        
        while(true){
            String option = getNextValue(itr, "-snapshot option need more argument.");
            if(option.charAt(0) != '-'){
                itr.previous();
                break;
            }
            
            switch(option){
                case "-showids":
                    showId = true;
                    break;
                case "-filter":
                    filter = Optional.of(Pattern.compile(getNextValue(itr, "-filter option needs regex pattern.")));
                    break;
                case "-exclude":
                    excludeFilterFile = Optional.of(Paths.get(getNextValue(itr, "-exclude option needs exclude XML file.")));
                    break;
                case "-s":
                    mode = Mode.SNAPSHOT_SUMMARY;
                    break;
                case "-c":
                    mode = Mode.CLASS_HISTO;
                    break;
                case "-d":
                    mode = Mode.DIFF_HISTO;
                    break;
                case "-r":
                    mode = Mode.CLASS_REFERENCES;
                    refStartTag = Long.decode(getNextValue(itr, "Class Reference option (-r) needs class tag value."));

                    if(!getNextValue(itr, "Class Reference option (-r) needs direction option (-d <p|c>)").equals("-d")){
                        throw new IllegalArgumentException("Class Reference option (-r) needs direction option (-d <p|c>)");
                    }

                    switch(getNextValue(itr, "Class Reference option (-r) needs direction option (-d <p|c>)")){
                        case "p":
                            refToParent = true;
                            break;
                        case "c":
                            refToParent = false;
                            break;
                        default:
                            throw new IllegalArgumentException("Class Reference option (-r) needs direction option (-d <p|c>)");
                    }

                    break;
                case "-e":
                    mode = Mode.HEAP_CSV;
                    csvFile = new File(getNextValue(itr, "Heap CSV option (-e) needs file name of CSV."));
                    break;
                case "-g":
                    mode = Mode.GC_CSV;
                    csvFile = new File(getNextValue(itr, "GC CSV option (-g) needs file name of CSV."));
                    break;
                default:
                    throw new IllegalArgumentException("Unknown option: " + option);
            }
            
        }
        
    }
    
    /**
     * Parse commandline options for -event option.
     * @param itr List iterator of commandline options.
     */
    private void parseThreadRecorderOptions(ListIterator<String> itr){
        type = FileType.THREADRECORD;
        
        while(true){
            String option = getNextValue(itr, "-event option need more argument.");
            if(option.charAt(0) != '-'){
                itr.previous();
                break;
            }
            
            switch(option){
                case "-showids":
                    showId = true;
                    break;
                case "-id":
                    tid = OptionalLong.of(Long.parseLong(getNextValue(itr, "-id option needs Thrad ID.")));
                    break;
                case "-threads":
                    mode = Mode.SHOW_THREAD_RECORD_ID;
                    break;
                case "-a":
                    mode = Mode.DUMP_THREAD_RECORD;
                    break;
                case "-s":
                    mode = Mode.DUMP_SUSPEND_EVENTS;
                    break;
                case "-l":
                    mode = Mode.DUMP_LOCK_EVENTS;
                    break;
                case "-i":
                    mode = Mode.DUMP_IO_EVENTS;
                    break;
                default:
                    throw new IllegalArgumentException("Unknown option: " + option);
            }
            
        }
        
    }
    
    /**
     * Parse commandline options for -jmx option.
     * @param itr List iterator of commandline options.
     */
    private void parseJMXOptions(ListIterator<String> itr){
        type = FileType.JMX;
        
        try {
            jmxURL = new JMXServiceURL(getNextValue(itr, "-jmx option need a URL to connect."));
        } catch (MalformedURLException ex) {
            throw new IllegalArgumentException("Invalid JMX URL", ex);
        }
        
        String option = getNextValue(itr, "-jmx option need more argument.");
        switch(option){
            case "-v":
                mode = Mode.JMX_GET_VERSION;
                break;
            case "-s":
                mode = Mode.JMX_GET_SNAPSHOT;
                break;
            case "-r":
                mode = Mode.JMX_GET_LOG;
                break;
            case "-c":
                mode = Mode.JMX_GET_CONFIG;
                configKey = getNextValue(itr, "-jmx option need more argument.");
                break;
            case "-n":
                mode = Mode.JMX_CHANGE_CONFIG;
                configKey = getNextValue(itr, "-jmx option need more argument.");
                newConfigValue = getNextValue(itr, "-jmx option need more argument.");
                break;
            case "-is":
                mode = Mode.JMX_INVOKE_SNAPSHOT;
                break;
            case "-ir":
                mode = Mode.JMX_INVOKE_LOG;
                break;
            case "-ia":
                mode = Mode.JMX_INVOKE_ALL_LOG;
                break;
            default:
                throw new IllegalArgumentException("Unknown option: " + option);
        }
        
    }

    /**
     * Parse commandline options.
     * @param options Array of commandline options.
     */
    public void parse(String[] options){
        file = new ArrayList<>();
        ListIterator<String> itr = Arrays.asList(options).listIterator();
        
        while(itr.hasNext()){
            String option = itr.next();
            
            switch(option){
                case "-start":
                    start = OptionalInt.of(Integer.parseInt(getNextValue(itr, "-start option needs valid ID.")));
                    break;
                case "-end":
                    end = OptionalInt.of(Integer.parseInt(getNextValue(itr, "-end option needs valid ID.")) + 1);
                    break;
                case "-log":
                    parseLogOptions(itr);
                    break;
                case "-snapshot":
                    parseSnapShotOptions(itr);
                    break;
                case "-event":
                    parseThreadRecorderOptions(itr);
                    break;
                case "-jmx":
                    parseJMXOptions(itr);
                    break;
                default:
                    file.add(Paths.get(option));
            }
            
        }
        
    }

    /**
     * Get file list to parse.
     * @return File list to parse.
     */
    public List<Path> getFile() {
        return file;
    }

    /**
     * Get parse mode.
     * @return Parse mode.
     */
    public Mode getMode() {
        return mode;
    }

    /**
     * Get class regex filter.
     * @return Class regex filter.
     */
    public Optional<Pattern> getFilter() {
        return filter;
    }

    /**
     * Get class exclude filter.
     * @return Class exclude filter.
     */
    public Optional<Path> getExcludeFilterFile() {
        return excludeFilterFile;
    }

    /**
     * Get id to parse starting.
     * @return Start ID
     */
    public OptionalInt getStart() {
        return start;
    }

    /**
     * Get id to parse ending.
     * @return End ID
     */
    public OptionalInt getEnd() {
        return end;
    }

    /**
     * Get start of reference.
     * This option effects -snapshot -r only.
     * @return Start tag of reference.
     */
    public long getRefStartTag() {
        return refStartTag;
    }

    /**
     * Direction of reference traverse.
     * This option effects -snapshot -r only.
     * @return true if parent.
     */
    public boolean isRefToParent() {
        return refToParent;
    }

    /**
     * Show ID option.
     * @return true if show id.
     */
    public boolean isShowId() {
        return showId;
    }
    
    /**
     * Get file type to parse.
     * @return File type.
     */
    public FileType getType() {
        return type;
    }
    
    /**
     * Get clsss filter predicate.
     * @return Class filter predicate.
     */
    public Predicate<? super ObjectData> getFilterPredicate(){
        Optional<Predicate<ObjectData>> includePredicate = filter.map(p -> (o -> p.asPredicate().test(o.getName())));
        List<String> excludeFilterList = excludeFilterFile.map(f -> ((Filters)JAXB.unmarshal(f.toFile(), Filters.class)).getFilter().stream()
                                                                                                                                    .filter(l -> l != null)
                                                                                                                                    .flatMap(l -> l.getClasses().getName().stream())
                                                                                                                                    .map(s -> ".*" + s + ".*")
                                                                                                                                    .collect(Collectors.toList()))
                                                          .orElse(new ArrayList<>());
        Predicate<ObjectData> excludePredicate = o -> excludeFilterList.stream().noneMatch(s -> o.getName().matches(s));
        
        Predicate<? super ObjectData> pred = null;
        
        if(includePredicate.isPresent()){
            pred = excludeFilterList.isEmpty() ? includePredicate.get() : includePredicate.get().and(excludePredicate);
        }
        else if(!excludeFilterList.isEmpty()){
            pred = excludePredicate;
        }
        
        return pred;
    }

    /**
     * Get CSV file name.
     * @return CSV file name.
     */
    public File getCsvFile() {
        return csvFile;
    }
    
    /**
     * Get predicate of Thread ID filter.
     * @return Thread ID filter.
     */
    public Predicate<? super ThreadStat> getIdPredicate(){
        return tid.isPresent() ? (s -> tid.getAsLong() == s.getId()) : (s -> true);
    }

    /**
     * Get JMX URL to connect.
     * @return JMX URL.
     */
    public JMXServiceURL getJmxURL() {
        return jmxURL;
    }

    /**
     * Get configuration key of HeapStats agent.
     * @return Configuration key.
     */
    public String getConfigKey() {
        return configKey;
    }

    /**
     * Get new configuration value of HeapStats agent.
     * @return New configuration value.
     */
    public String getNewConfigValue() {
        return newConfigValue;
    }
    
}