Coverage Report - sk.baka.webvm.analyzer.HistorySampler
 
Classes in this File Line Coverage Branch Coverage Complexity
HistorySampler
96%
27/28
N/A
2.333
HistorySampler$1
N/A
N/A
2.333
HistorySampler$GCCpuUsageMeasure
89%
17/19
70%
7/10
2.333
HistorySampler$ProblemSampler
85%
12/14
100%
6/6
2.333
HistorySampler$Sampler
84%
11/13
50%
3/6
2.333
 
 1  
 /**
 2  
  * Copyright 2009 Martin Vysny.
 3  
  *
 4  
  * This file is part of WebVM.
 5  
  *
 6  
  * WebVM is free software: you can redistribute it and/or modify
 7  
  * it under the terms of the GNU General Public License as published by
 8  
  * the Free Software Foundation, either version 3 of the License, or
 9  
  * (at your option) any later version.
 10  
  *
 11  
  * WebVM is distributed in the hope that it will be useful,
 12  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  
  * GNU General Public License for more details.
 15  
  *
 16  
  * You should have received a copy of the GNU General Public License
 17  
  * along with WebVM.  If not, see <http://www.gnu.org/licenses/>.
 18  
  */
 19  
 package sk.baka.webvm.analyzer;
 20  
 
 21  
 import java.lang.management.GarbageCollectorMXBean;
 22  
 import java.lang.management.ManagementFactory;
 23  
 import java.util.List;
 24  
 import java.util.concurrent.ScheduledExecutorService;
 25  
 import java.util.concurrent.TimeUnit;
 26  
 import java.util.logging.Level;
 27  
 import java.util.logging.Logger;
 28  
 import sk.baka.webvm.analyzer.hostos.Cpu;
 29  
 import sk.baka.webvm.config.Config;
 30  
 import sk.baka.webvm.misc.BackgroundService;
 31  
 import sk.baka.webvm.misc.NotificationDelivery;
 32  
 import sk.baka.webvm.misc.SimpleFixedSizeFIFO;
 33  
 import static sk.baka.webvm.misc.Constants.*;
 34  
 
 35  
 /**
 36  
  * Samples the VM history regularly. You need to invoke {@link #start()} to start the sampler, {@link #stop()} to stop it. Thread-safe.
 37  
  * @author Martin Vysny
 38  
  */
 39  50
 public final class HistorySampler extends BackgroundService {
 40  
 
 41  1
     private static final Logger LOG = Logger.getLogger(HistorySampler.class.getName());
 42  
     private final ProblemAnalyzer analyzer;
 43  
 
 44  
     /**
 45  
      * Creates new sampler instance with default values.
 46  
      * @param analyzer a configured instance of the analyzer
 47  
      */
 48  
     public HistorySampler(final ProblemAnalyzer analyzer) {
 49  1
         this(HISTORY_VMSTAT, HISTORY_PROBLEMS, analyzer);
 50  1
     }
 51  
 
 52  
     /**
 53  
      * Creates new sampler instance.
 54  
      * @param vmstatConfig the vmstat sampler config
 55  
      * @param problemConfig the problem sampler config
 56  
      * @param analyzer a configured instance of the analyzer
 57  
      */
 58  
     public HistorySampler(final SamplerConfig vmstatConfig, final SamplerConfig problemConfig, final ProblemAnalyzer analyzer) {
 59  2
         super("Sampler", 1);
 60  2
         this.vmstatConfig = vmstatConfig;
 61  2
         this.problemConfig = problemConfig;
 62  2
         vmstatHistory = new SimpleFixedSizeFIFO<HistorySample>(vmstatConfig.getHistoryLength());
 63  2
         problemHistory = new SimpleFixedSizeFIFO<List<ProblemReport>>(problemConfig.getHistoryLength());
 64  2
         this.analyzer = analyzer;
 65  2
     }
 66  
     private final SamplerConfig problemConfig;
 67  
     /**
 68  
      * Default VMStat history.
 69  
      */
 70  1
     public static final SamplerConfig HISTORY_VMSTAT = new SamplerConfig(150, 1000, 0);
 71  
     /**
 72  
      * Default Problems history.
 73  
      */
 74  1
     public static final SamplerConfig HISTORY_PROBLEMS = new SamplerConfig(20, 10 * 1000, 500);
 75  2
     private final NotificationDelivery notificator = new NotificationDelivery();
 76  
 
 77  
     /**
 78  
      * Sets the new configuration file.
 79  
      * @param config the new config file.
 80  
      */
 81  
     public void configure(final Config config) {
 82  1
         notificator.configure(config);
 83  1
     }
 84  
 
 85  
     @Override
 86  
     protected void started(final ScheduledExecutorService executor) {
 87  2
         notificator.start();
 88  2
         executor.scheduleWithFixedDelay(new Sampler(), vmstatConfig.getInitialDelay(), vmstatConfig.getHistorySampleDelayMs(), TimeUnit.MILLISECONDS);
 89  2
         executor.scheduleWithFixedDelay(new ProblemSampler(), problemConfig.getInitialDelay(), problemConfig.getHistorySampleDelayMs(), TimeUnit.MILLISECONDS);
 90  2
     }
 91  
 
 92  
     @Override
 93  
     protected void stopped() {
 94  1
         notificator.stop();
 95  1
     }
 96  
     private final SimpleFixedSizeFIFO<HistorySample> vmstatHistory;
 97  
 
 98  
     /**
 99  
      * Returns a snapshot of the history values.
 100  
      * @return modifiable snapshot.
 101  
      */
 102  
     public List<HistorySample> getVmstatHistory() {
 103  0
         return vmstatHistory.toList();
 104  
     }
 105  
     /**
 106  
      * Serves for Host OS CPU usage measurement.
 107  
      */
 108  2
     private final CpuUsage cpuOS = Cpu.newHostCpu();
 109  
     /**
 110  
      * Serves for Java CPU usage measurement.
 111  
      */
 112  2
     private final CpuUsage cpuJava = Cpu.newJavaCpu();
 113  
     /**
 114  
      * Serves for Host OS CPU IO usage measurement.
 115  
      */
 116  2
     private final CpuUsage cpuOSIO = Cpu.newHostIOCpu();
 117  
 
 118  4
     private final class Sampler implements Runnable {
 119  
 
 120  
         public void run() {
 121  
             try {
 122  2
                 final int cpuUsageByGC = gcCpuUsage.getCpuUsage();
 123  2
                 int usage = cpuOS.getCpuUsage();
 124  2
                 usage = usage < 0 ? 0 : usage;
 125  2
                 int javaUsage = cpuJava.getCpuUsage();
 126  2
                 javaUsage = javaUsage < 0 ? 0 : javaUsage;
 127  2
                 int ioUsage = cpuOSIO.getCpuUsage();
 128  2
                 ioUsage = ioUsage < 0 ? 0 : ioUsage;
 129  2
                 vmstatHistory.add(new HistorySample(cpuUsageByGC, usage, javaUsage, ioUsage));
 130  0
             } catch (Throwable e) {
 131  
                 // catch all throwables as the thread is going to terminate anyway
 132  0
                 LOG.log(Level.SEVERE, "The Sampler thread failed", e);
 133  2
             }
 134  2
         }
 135  
     }
 136  
     private SamplerConfig vmstatConfig;
 137  
     private final SimpleFixedSizeFIFO<List<ProblemReport>> problemHistory;
 138  2
     private final CpuUsage gcCpuUsage = new CpuUsage(new GCCpuUsageMeasure());
 139  
 
 140  
     /**
 141  
      * Measures the GC CPU usage.
 142  
      */
 143  4
     private static final class GCCpuUsageMeasure implements ICpuUsageMeasure {
 144  
 
 145  
         public boolean supported() {
 146  2
             return true;
 147  
         }
 148  
 
 149  
         public Object measure() throws Exception {
 150  
             // get the GC CPU usage
 151  2
             long collectTime = 0;
 152  2
             final List<GarbageCollectorMXBean> beans = ManagementFactory.getGarbageCollectorMXBeans();
 153  2
             if (beans != null) {
 154  2
                 for (final GarbageCollectorMXBean bean : beans) {
 155  4
                     if (!bean.isValid()) {
 156  0
                         continue;
 157  
                     }
 158  4
                     if (bean.getCollectionTime() > 0) {
 159  2
                         collectTime += bean.getCollectionTime();
 160  
                     }
 161  
                 }
 162  
             }
 163  2
             final long currentTimeMillis = System.currentTimeMillis();
 164  2
             return new long[]{collectTime, currentTimeMillis};
 165  
         }
 166  
 
 167  
         public int getAvgCpuUsage(Object o1, Object o2) {
 168  1
             final long[] m1 = (long[]) o1;
 169  1
             final long[] m2 = (long[]) o2;
 170  1
             final long gcTimeDelta = m2[0] - m1[0];
 171  1
             final long gcSampleTakenDelta = m2[1] - m1[1];
 172  1
             if (gcSampleTakenDelta == 0) {
 173  0
                 return 0;
 174  
             }
 175  1
             return (int) (gcTimeDelta * HUNDRED_PERCENT / gcSampleTakenDelta);
 176  
         }
 177  
     }
 178  
 
 179  4
     private final class ProblemSampler implements Runnable {
 180  
 
 181  
         public void run() {
 182  
             try {
 183  12
                 final List<ProblemReport> currentProblems = analyzer.getProblems(vmstatHistory.toList());
 184  12
                 final List<ProblemReport> last = problemHistory.getNewest();
 185  12
                 if (last == null) {
 186  5
                     if (!ProblemReport.isProblem(currentProblems)) {
 187  4
                         return;
 188  
                     }
 189  
                 } else {
 190  7
                     if (ProblemReport.equals(last, currentProblems)) {
 191  6
                         return;
 192  
                     }
 193  
                 }
 194  2
                 problemHistory.add(currentProblems);
 195  2
                 notificator.deliverAsync(currentProblems);
 196  0
             } catch (Throwable e) {
 197  
                 // catch all throwables as the thread is going to terminate anyway
 198  0
                 LOG.log(Level.SEVERE, "The ProblemSampler timer failed", e);
 199  2
             }
 200  2
         }
 201  
     }
 202  
 
 203  
     /**
 204  
      * Returns a snapshot view over the problem history.
 205  
      * @return the history.
 206  
      */
 207  
     public List<List<ProblemReport>> getProblemHistory() {
 208  3
         return problemHistory.toList();
 209  
     }
 210  
 }