com.bigdata.counters.osx.VMStatCollector 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 Dec 9, 2008
*/
package com.bigdata.counters.osx;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.bigdata.counters.*;
/**
* Collects some counters using vmstat
.
*
* @author Bryan Thompson
*/
public class VMStatCollector 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(T value, 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) {
this(path, 1d);
}
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;
}
}
/**
* A map from the name of a column to the order of that column in the output (origin ZERO).
* This mapping was added when the format was changed to make the parser more robust to such
* changes.
*
* @see #1125 (OSX vm_stat output has changed)
*/
private final Map keys = new LinkedHashMap();
/**
* 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 vmstat
.
*/
private final Map vals = new ConcurrentHashMap();
/**
* The timestamp associated with the most recently collected values.
*/
private final AtomicLong lastModified = new AtomicLong(
System.currentTimeMillis());
/**
* The {@link Pattern} used to split apart the rows read from
* vmstat
.
*/
final static Pattern pattern = Pattern.compile("\\s+");
/**
*
* @param interval
* The interval for the collected statistics.
*/
public VMStatCollector(final int interval) {
super(interval);
}
@Override
public List getCommand() {
final List command = new LinkedList();
command.add("/usr/bin/vm_stat");
// Note: The configured interval in seconds between reports.
command.add("" + getInterval());
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: [pageins] is "the #of blocks swapped in".
*/
inst.add(new DI(IRequiredHostCounters.Memory_majorFaultsPerSecond));
/*
* This is an aggregation of the counters for "active", "wire", and
* "spec".
*/
inst.add(new DI(IHostCounters.Memory_SwapBytesUsed));
/*
* Note: [free] is "the total number of free pages in the system".
*/
inst.add(new DI(IHostCounters.Memory_Bytes_Free));
for (@SuppressWarnings("rawtypes") I i : inst) {
root.addCounter(i.getPath(), i);
}
return root;
}
@Override
public AbstractProcessReader getProcessReader() {
return new VMStatReader();
}
/**
* Sample output for vm_stat 60
, where 60
is the
* interval. Unlike the linux vmstat
, there is no option to
* suppress the periodic repeat of the header. The header repeats in its
* entirety every "page" full.
*
*
* Mach Virtual Memory Statistics: (page size of 4096 bytes, cache hits 0%)
* free active spec inactive wire faults copy 0fill reactive pageins pageout
* 346061 1246K 51002 196922 190727 1004215K 19835511 525442K 7555575 2897558 2092534
* 346423 1247K 50754 196922 189767 2707 0 1263 0 0 0
* 343123 1247K 52180 198906 189767 3885 0 1754 0 0 0
* 342474 1247K 52101 198978 190116 21739 2013 4651 0 0 0
* 342542 1247K 52016 199319 189799 5792 17 2561 0 0 0
*
*
* For more recent OSX releases (e.g., Yosemite) there are now additional
* columns:
*
*
* Mach Virtual Memory Statistics: (page size of 4096 bytes)
* free active specul inactive throttle wired prgable faults copy 0fill reactive purged file-backed anonymous cmprssed cmprssor dcomprs comprs pageins pageout swapins swapouts
* 17656 1502912 110218 1329298 0 783121 32302 1122834K 22138464 897721K 4538117 2220029 382716 2559712 1093181 449012 1856773 4298532 67248050 263644 779970 886296
* 41993 1479331 110174 1329290 0 782241 33940 2115 1 1316 0 0 382671 2536124 1093181 449012 0 0 24 0 0 0
*
*
* @author Bryan
* Thompson
*/
protected class VMStatReader extends ProcessReaderHelper {
@Override
protected ActiveProcess getActiveProcess() {
if (activeProcess == null)
throw new IllegalStateException();
return activeProcess;
}
public VMStatReader() {
super();
}
private final Pattern pageSizePattern = Pattern.compile("size of (\\d+) ");
/**
* The index of the field associated with the "free" counter.
*/
private final AtomicInteger INDEX_FREE = new AtomicInteger();
/**
* The index of the field associated with the "active" counter (in use and
* pageable).
*/
private AtomicInteger INDEX_ACTIVE = new AtomicInteger();
/**
* The index of the field associated with "spec" or "specul" (aka
* speculative) counter.
*/
private AtomicInteger INDEX_SPEC = new AtomicInteger();
/**
* The index of the field associated with "speculative" counter.
*/
private AtomicInteger INDEX_WIRED = new AtomicInteger();
/**
* The index of the field associated with "pagein" counter.
*/
private AtomicInteger INDEX_PAGEINS = new AtomicInteger();
/**
* The index of the field associated with "pageout" counter.
*/
private AtomicInteger INDEX_PAGEOUT = new AtomicInteger();
/**
*
* @see TestParse_vm_stat#test_vmstat_header_and_data_parse()
*/
@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");
// 1st header: we need the pageSize.
final String h0;
final int pageSize;
{
h0 = readLine();
if (log.isInfoEnabled())
log.info("header: " + h0);
if (!h0.contains("Mach")) {
throw new RuntimeException("Wrong input, expected hader: " + h0);
}
Matcher matcher = pageSizePattern.matcher(h0);
if (matcher.find()) {
pageSize = Integer.valueOf(matcher.group(1));
} else {
throw new RuntimeException("Failed to extract page size");
}
if (pageSize <= 0 || (pageSize % 512 != 0))
throw new RuntimeException("pageSize=" + pageSize);
if (log.isInfoEnabled())
log.info("pageSize: " + pageSize);
}
// read 2nd header and verify expected fields.
final String h1;
{
h1 = readLine();
if (log.isInfoEnabled())
log.info("header: " + h1);
final String[] fields = pattern.split(h1.trim(), 0/* limit */);
// Store mapping from field name to ordinal index (origin zero).
for (int i = 0; i < fields.length; i++) {
keys.put(fields[i], i);
}
// Check for field names that we use.
getIndexIfDefined(INDEX_FREE, "free");
getIndexIfDefined(INDEX_ACTIVE, "active");
getIndexIfDefined(INDEX_SPEC, "spec", "specul");
getIndexIfDefined(INDEX_WIRED, "wire", "wired");
getIndexIfDefined(INDEX_PAGEINS, "pageins");
getIndexIfDefined(INDEX_PAGEOUT, "pageout");
}
/*
* Note: Some fields are reported with a 'K' suffix. Some are in
* "pages". The pageSize was extracted from the header above.
*/
// Mach Virtual Memory Statistics: (page size of 4096 bytes, cache hits
// 0%)
// free active spec inactive wire faults copy 0fill reactive pageins
// pageout
// 398649 1196K 47388 203418 185941 145234K 2036323 82012367 1353888
// 351301 149940
// 400080 1197K 44784 205254 183886 1829 0 1046 0 0 0
// read lines until interrupted.
long pageout_tm1 = 0;
boolean first = true;
while (true) {
// read the next line of data.
final String data;
{
String s = readLine();
if (s.startsWith("Mach")) { // 1st header line.
s = readLine(); // 2nd header line.
s = readLine(); // data line.
if (log.isInfoEnabled())
log.info("Skipped headers.");
}
data = s;
}
try {
// timestamp
lastModified.set(System.currentTimeMillis());
final String[] fields = pattern.split(data.trim(), 0/* limit */);
final String free = fields[INDEX_FREE.get()]; // free
final String active = fields[INDEX_ACTIVE.get()]; // in use and pageable
final String spec = fields[INDEX_SPEC.get()]; // speculative
// final String inactive = fields[3]; //
final String wire = fields[INDEX_WIRED.get()]; // wired down
// final String faults = fields[5]; // translation faults
final String pageins = fields[INDEX_PAGEINS.get()]; // pageins
final String pageout = fields[INDEX_PAGEOUT.get()]; // pageout
if (log.isInfoEnabled())
log.info("\nfree=" + free + ", active=" + active + ", spec="
+ spec + ", wire=" + wire + ", si=" + pageins + ", so="
+ pageout + "\n" + h1 + "\n" + data);
{
final double _free = parseDouble(free);
final double _active = parseDouble(active);
final double _spec = parseDouble(spec);
final double _wire = parseDouble(wire);
final double swapBytesUsed = _active + _spec + _wire;
vals.put(IHostCounters.Memory_Bytes_Free, _free * pageSize);
vals.put(IHostCounters.Memory_SwapBytesUsed, swapBytesUsed
* pageSize);
}
/*
* pageout is reported as a total over time. we have to compute a
* delta, then divide through by the interval to get
* pages/second.
*/
{
final double _pageout = parseDouble(pageout);
if (!first) {
final double delta = _pageout - pageout_tm1;
final double majorPageFaultsPerSec = delta / getInterval();
vals.put(
IRequiredHostCounters.Memory_majorFaultsPerSecond,
majorPageFaultsPerSec);
}
}
first = false;
} catch (Exception ex) {
/*
* Issue warning for parsing problems.
*/
log.warn(ex.getMessage() //
+ "\nheader: " + h1 //
+ "\n data: " + data //
, ex);
}
} // while(true)
} // readProcess()
} // class VMStatReader
/**
* Parse a string which may have a "K" suffix, returning a double. If the "K"
* suffix is present, then the returned value is scaled by 1000. This handles
* an OSX specific oddity for vm_stat
*
* @param s
* The string.
*
* @return The (possibly scaled) value.
*/
private static double parseDouble(final String s) {
final int pos = s.indexOf("K");
if (pos == -1) {
return Long.valueOf(s);
}
final long val = Long.valueOf(s.substring(0, pos));
return val * 1000;
}
private static void assertFieldByPosition(final int index,
final String[] fields, final String expected) {
if (!expected.equals(fields[index]))
throw new RuntimeException("Expecting '" + expected + "', found: '"
+ fields[0] + "'");
}
/**
* If the name of the performance counter appears in {@link #keys} then set
* the index of that counter on the indexOf field.
*
* @param indexOf
* The index of that performance counter in the table generated by
* the OS.
* @param name
* The name(s) of the performance counter.
*/
private void getIndexIfDefined(final AtomicInteger indexOf,
final String... name) {
Integer index = null;
for (String s : name) {
index = keys.get(s);
if (index != null) {
indexOf.set(index);
return;
}
}
throw new RuntimeException("Required performance counter not found: '"
+ Arrays.toString(name) + "'");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy