/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* 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 and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser 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 org.rhq.core.system;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hyperic.sigar.ProcCpu;
import org.hyperic.sigar.ProcFd;
import org.hyperic.sigar.ProcMem;
import org.hyperic.sigar.ProcState;
import org.hyperic.sigar.ProcTime;
import org.hyperic.sigar.SigarProxy;
/**
* Tracks the historical usage for child processes as they come and go to give an estimation of the total resources used
* by a process and all its children, past and present.
*
* @author Greg Hinkle
* @author John Mazzitelli
*/
public class AggregateProcessInfo extends ProcessInfo {
private Map<Long, ProcessStats> childProcessStats = new HashMap<Long, ProcessStats>();
private AggregateProcTime aggregateProcTime;
private AggregateProcMem aggregateProcMem;
private AggregateProcCpu aggregateProcCpu;
private AggregateProcFd aggregateProcFd;
/**
* Creates an aggregate process info object that will aggregate data related to the process with the given pid and
* all its child processes.
*
* @param pid the parent pid whose data, along with its children process data, will be aggregated
*/
public AggregateProcessInfo(long pid) {
super(pid);
}
@Override
public void refresh() throws SystemInfoException {
// first make sure this process itself is refreshed
super.refresh();
try {
// go through the entire process table and find the children of this parent process
long[] pids = null;
try {
pids = sigar.getProcList();
} catch (Exception e) { // ignore - SIGAR just wasn't able to get the list of processes for some reason
}
if (pids != null) {
Set<Long> runningPids = new HashSet<Long>();
for (long pid : pids) {
runningPids.add(pid);
try {
ProcState processProcState = sigar.getProcState(pid);
if (processProcState.getPpid() == super.pid) {
ProcessStats ps = childProcessStats.get(pid);
if (ps == null) {
ps = new ProcessStats(pid);
ps.isRunning = true;
childProcessStats.put(pid, ps);
}
ps.refresh(sigar);
}
} catch (Exception e) {
// ignore this process, SIGAR can't get its procState for some reason (permissions? process died?)
}
}
for (ProcessStats ps : childProcessStats.values()) {
if (!runningPids.contains(ps.childPid))
ps.isRunning = false;
}
}
List<ProcTime> procTimes = new ArrayList<ProcTime>();
List<ProcMem> procMems = new ArrayList<ProcMem>();
List<ProcCpu> procCpus = new ArrayList<ProcCpu>();
List<ProcFd> procFds = new ArrayList<ProcFd>();
// get the parent process data first - we'll aggregate the children's data with their parent
// There are some instances where SIGAR can't get one or more of these (maybe permission issues?).
// Do not bomb if we can't get one or more of these - we'll just handle nulls appropriately.
// It is safe to get the prior snapshot as we just called super.refresh
ProcessInfoSnapshot priorSnaphot = priorSnaphot();
try {
procTimes.add(priorSnaphot.getTime());
} catch (Exception e) {
}
try {
procMems.add(priorSnaphot.getMemory());
} catch (Exception e) {
}
try {
procCpus.add(priorSnaphot.getCpu());
} catch (Exception e) {
}
try {
procFds.add(priorSnaphot.getFileDescriptor());
} catch (Exception e) {
}
// now get all the children's data
for (ProcessStats ps : childProcessStats.values()) {
procTimes.add(ps.childProcTime);
// Only add running processes data
if (ps.isRunning) {
procMems.add(ps.childProcMem);
procCpus.add(ps.childProcCpu);
procFds.add(ps.childProcFd);
}
}
// calculate the aggregate data now
this.aggregateProcTime = new AggregateProcTime(procTimes);
this.aggregateProcMem = new AggregateProcMem(procMems);
this.aggregateProcCpu = new AggregateProcCpu(procCpus);
this.aggregateProcFd = new AggregateProcFd(procFds);
} catch (Exception e) {
throw new SystemInfoException(e);
}
}
public AggregateProcTime getAggregateTime() {
return this.aggregateProcTime;
}
public AggregateProcMem getAggregateMemory() {
return this.aggregateProcMem;
}
public AggregateProcCpu getAggregateCpu() {
return this.aggregateProcCpu;
}
public AggregateProcFd getAggregateFileDescriptor() {
return this.aggregateProcFd;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder(super.toString());
if (this.childProcessStats != null) {
str.append(", child-pids=" + this.childProcessStats.keySet());
}
return str.toString();
}
/**
* Aggregates {@link ProcTime} data into a single object. This has attributes that are analogous to aggregatable
* versions of those attributes found in {@link ProcTime}.
*/
public class AggregateProcTime {
long sys;
long user;
long total;
public long getSys() {
return this.sys;
}
public long getUser() {
return this.user;
}
public long getTotal() {
return this.total;
}
public AggregateProcTime(List<ProcTime> times) {
for (ProcTime time : times) {
if (time != null) {
sys += time.getSys();
user += time.getUser();
total += time.getTotal();
}
}
}
}
public class AggregateProcCpu {
private long sys;
private long user;
private long total;
private double percent;
public long getSys() {
return this.sys;
}
public long getUser() {
return this.user;
}
public long getTotal() {
return this.total;
}
public double getPercent() {
return percent;
}
public AggregateProcCpu(List<ProcCpu> cpus) {
for (ProcCpu cpu : cpus) {
if (cpu != null) {
sys += cpu.getSys();
user += cpu.getUser();
total += cpu.getTotal();
percent += cpu.getPercent();
// TODO: this looks the same as ProcTime - what's the diff?
// TODO: what about percent and the others?
}
}
}
}
public class AggregateProcMem {
private long majorFaults;
private long minorFaults;
private long pageFaults;
private long resident;
private long share;
private long size;
public long getMajorFaults() {
return this.majorFaults;
}
public long getMinorFaults() {
return this.minorFaults;
}
public long getPageFaults() {
return this.pageFaults;
}
public long getResident() {
return this.resident;
}
public long getShare() {
return this.share;
}
public long getSize() {
return this.size;
}
public AggregateProcMem(List<ProcMem> mems) {
for (ProcMem mem : mems) {
if (mem != null) {
majorFaults += mem.getMajorFaults();
minorFaults += mem.getMinorFaults();
pageFaults += mem.getPageFaults();
resident += mem.getResident();
share += mem.getShare();
size += mem.getSize();
}
}
}
}
public class AggregateProcFd {
private long total;
public long getTotal() {
return this.total;
}
public AggregateProcFd(List<ProcFd> fds) {
for (ProcFd fd : fds) {
if (fd != null) {
total += fd.getTotal();
}
}
}
}
private static class ProcessStats {
public long childPid;
public boolean isRunning;
public ProcTime childProcTime;
public ProcMem childProcMem;
public ProcCpu childProcCpu;
public ProcFd childProcFd;
public ProcessStats(long myPid) {
this.childPid = myPid;
}
public void refresh(SigarProxy sigar) throws Exception {
// There are some instances where SIGAR can't get one or more of these (maybe permission issues?).
// Do not bomb if we can't get one or more of these - we'll just handle nulls appropriately.
try {
childProcTime = sigar.getProcTime(this.childPid);
} catch (Exception e) {
}
try {
childProcMem = sigar.getProcMem(this.childPid);
} catch (Exception e) {
}
try {
childProcCpu = sigar.getProcCpu(this.childPid);
} catch (Exception e) {
}
try {
childProcFd = sigar.getProcFd(this.childPid);
} catch (Exception e) {
}
}
}
}