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

com.bigdata.counters.linux.SarCpuUtilizationCollector Maven / Gradle / Ivy

/*

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

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; version 2 of the License.

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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
/*
 * Created on Mar 26, 2008
 */

package com.bigdata.counters.linux;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import com.bigdata.counters.AbstractProcessCollector;
import com.bigdata.counters.AbstractProcessReader;
import com.bigdata.counters.ActiveProcess;
import com.bigdata.counters.CounterSet;
import com.bigdata.counters.ICounterHierarchy;
import com.bigdata.counters.ICounterSet;
import com.bigdata.counters.IHostCounters;
import com.bigdata.counters.IInstrument;
import com.bigdata.counters.IRequiredHostCounters;
import com.bigdata.counters.ProcessReaderHelper;

/**
 * Collects statistics on the CPU utilization for the entire host using
 * sar -u.
 * 
 * @author Bryan Thompson
 */
public class SarCpuUtilizationCollector extends AbstractProcessCollector
        implements ICounterHierarchy, IRequiredHostCounters, IHostCounters {

    /**
     * Inner class integrating the current values with the {@link ICounterSet}
     * hierarchy.
     * 
     * @author Bryan
     *         Thompson
     */
    abstract class I implements IInstrument {
        
        protected final String path;
        
        public String getPath() {
            
            return path;
            
        }
        
        public I(final String path) {
            
            if (path == null)
                throw new IllegalArgumentException();
            
            this.path = path;
            
        }

        @Override
        public long lastModified() {

            return lastModified.get();
            
        }

        /**
         * @throws UnsupportedOperationException
         *             always.
         */
        @Override
        public void setValue(final T value, final long timestamp) {
           
            throw new UnsupportedOperationException();
            
        }

    }
    
    /**
     * Double precision counter with scaling factor.
     * 
     * @author Bryan Thompson
     */
    class DI extends I {

        protected final double scale;
        
        DI(final String path, final double scale) {
            
            super( path );
            
            this.scale = scale;
            
        }
        
        @Override
        public Double getValue() {
         
            final Double value = (Double) vals.get(path);

            // no value is defined.
            if (value == null)
                return 0d;

            final double d = value.doubleValue() * scale;

            return d;
            
        }
        
    }

    /**
     * Map containing the current values for the configured counters. The
     * keys are paths into the {@link CounterSet}. The values are the data
     * most recently read from sar.
     */
    private final Map vals = new ConcurrentHashMap();
    
    /**
     * The timestamp associated with the most recently collected values.
     */
    private final AtomicLong lastModified = new AtomicLong(System.currentTimeMillis());

    /**
     * 
     * @param interval
     *            The reporting interval in seconds.
     * @param kernelVersion
     */
    public SarCpuUtilizationCollector(final int interval,
            final KernelVersion kernelVersion) {

        super(interval);

    }
    
    @Override
    public List getCommand() {

        final List command = new LinkedList();
        
        command.add(SysstatUtil.getPath("sar").getPath());

        // Note: Request the CPU stats.
        command.add("-u");
        
        // Note: configured interval.
        command.add(""+getInterval());
        
        // Note: count of zero means to repeat for ever.
        command.add("0");
        
        return command;
        
    }

    @Override
    public CounterSet getCounters() {
        
        final CounterSet root = new CounterSet();

        @SuppressWarnings("rawtypes")
        final List inst = new LinkedList();

        /*
         * Note: Counters are all declared as Double to facilitate aggregation.
         * 
         * Note: sar reports percentages in [0:100] so we convert them to [0:1]
         * using a scaling factor.
         */

        inst.add(new DI(IRequiredHostCounters.CPU_PercentProcessorTime, .01d));

        inst.add(new DI(IHostCounters.CPU_PercentUserTime, .01d));
        inst.add(new DI(IHostCounters.CPU_PercentSystemTime, .01d));
        inst.add(new DI(IHostCounters.CPU_PercentIOWait, .01d));

        for (@SuppressWarnings("rawtypes") I i : inst) {

            root.addCounter(i.getPath(), i);

        }
        
        return root;
        
    }
   
    /**
     * Extended to force sar to use a consistent timestamp
     * format regardless of locale by setting
     * S_TIME_FORMAT="ISO" in the environment.
     */
    @Override
    protected void setEnvironment(final Map env) {

        super.setEnvironment(env);
        
        env.put("S_TIME_FORMAT", "ISO");
        
    }

    @Override
    public AbstractProcessReader getProcessReader() {
        
        return new SarReader();
        
    }

    /**
     * Sample output for sar -u 1 10
     * 
     *    Linux 2.6.22.14-72.fc6 (hostname)    2008-03-17
     *   
     *   04:14:45 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
     *   04:14:46 PM     all      0.00      0.00      0.00      0.00      0.00    100.00
     *   ...
     *   Average:        all      0.00      0.00      0.00      0.00      0.00    100.00
     * 
* * There is a banner, which is followed by the a repeating sequence * {blank line, header, data line(s)}. This sequence repeats every * so often when sar emits new headers. * * @author Bryan * Thompson */ private class SarReader extends ProcessReaderHelper { @Override protected ActiveProcess getActiveProcess() { if (activeProcess == null) throw new IllegalStateException(); return activeProcess; } public SarReader() { super(); } @Override protected void readProcess() throws Exception { if(log.isInfoEnabled()) log.info("begin"); for(int i=0; i<10 && !getActiveProcess().isAlive(); i++) { if(log.isInfoEnabled()) log.info("waiting for the readerFuture to be set."); Thread.sleep(100/*ms*/); } if(log.isInfoEnabled()) log.info("running"); // The most recently read header. String header; // skip banner. final String banner = readLine(); if(log.isInfoEnabled()) log.info("banner: " + banner); { // skip blank line. final String blank = readLine(); assert blank.trim().length() == 0 : "Expecting a blank line"; // header. header = readLine(); if (log.isInfoEnabled()) log.info("header: "+header); } while(true) { // data. final String data = readLine(); if(data.trim().length()==0) { header = readLine(); if (log.isInfoEnabled()) log.info("header: "+header); continue; } try { // * 04:14:45 PM CPU %user %nice %system %iowait %steal %idle // * 04:14:46 PM all 0.00 0.00 0.00 0.00 0.00 100.00 // { // final String s = data.substring(0, 11); // try { // lastModified = f.parse(s).getTime(); // if(log.isInfoEnabled()) // log.info("[" // + s // + "] parsed as milliseconds=" // + lastModified // + ", date=" // + DateFormat.getDateTimeInstance( // DateFormat.FULL, // DateFormat.FULL).format(new Date(lastModified))); // } catch (Exception e) { // log.warn("Could not parse time: [" + s + "] : " + e); // // should be pretty close. // lastModified = System.currentTimeMillis(); // } // } /* * Note: This timestamp should be _very_ close to the value * reported by sysstat. Also, using the current time is MUCH * easier and less error prone than attempting to parse the TIME * OF DAY written by sysstat and correct it into a UTC time by * adjusting for the UTC time of the start of the current day, * which is what we would have to do. */ lastModified.set(System.currentTimeMillis()); // final String user = data.substring(20-1, 30-1); //// final String nice = data.substring(30-1, 40-1); // final String system = data.substring(40-1, 50-1); // final String iowait = data.substring(50-1, 60-1); //// final String steal = data.substring(60-1, 70-1); // final String idle = data.substring(70-1, 80-1); final String[] fields = SysstatUtil.splitDataLine(data); final String user = fields[2]; // final String nice = fields[3]; final String system = fields[4]; final String iowait = fields[5]; // final String steal = fields[6]; final String idle = fields[7]; if (log.isInfoEnabled()) log.info("\n%user=" + user + ", %system=" + system + ", iowait=" + iowait + ", idle="+idle+ "\n" + header + "\n" + data); vals.put(IHostCounters.CPU_PercentUserTime, Double.parseDouble(user)); vals.put(IHostCounters.CPU_PercentSystemTime, Double.parseDouble(system)); vals.put(IHostCounters.CPU_PercentIOWait, Double.parseDouble(iowait)); vals.put(IRequiredHostCounters.CPU_PercentProcessorTime, (100d - Double.parseDouble(idle))); } catch(Exception ex) { /* * Issue warning for parsing problems. */ log.warn(ex.getMessage() // + "\nheader: " + header // + "\n data: " + data // , ex ); } } } } }