package net.floodlightcontroller.debugcounter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
/**
* This class implements a central store for all counters used for debugging the
* system. For counters based on traffic-type, see ICounterStoreService.
*
* @author Saurav
*/
public class DebugCounter implements IFloodlightModule, IDebugCounterService {
protected static Logger log = LoggerFactory.getLogger(DebugCounter.class);
/**
* The counter value
*/
protected class MutableLong {
long value = 0;
public void increment() { value += 1; }
public long get() { return value; }
public void set(long val) { value = val; }
}
/**
* Global debug-counter storage across all threads. These are
* updated from the local per thread counters by the flush counters method.
*/
protected ConcurrentHashMap<String, AtomicLong> debugCounters =
new ConcurrentHashMap<String, AtomicLong>();
/**
* Thread local debug counters used for maintaining counters local to a thread.
*/
protected final ThreadLocal<Map<String, MutableLong>> threadlocalCounters =
new ThreadLocal<Map<String, MutableLong>>() {
@Override
protected Map<String, MutableLong> initialValue() {
return new HashMap<String, MutableLong>();
}
};
/**
* protected class to store counter information
*/
protected class CounterInfo {
String moduleCounterName;
String counterDesc;
CounterType ctype;
String moduleName;
String counterName;
public CounterInfo(String name, String desc, CounterType ctype) {
this.moduleCounterName = name;
String[] temp = name.split("-");
this.moduleName = temp[0];
this.counterName = temp[1];
this.counterDesc = desc;
this.ctype = ctype;
}
public String getModuleCounterName() { return moduleCounterName; }
public String getCounterDesc() { return counterDesc; }
public CounterType getCtype() { return ctype; }
public String getModuleName() { return moduleName; }
public String getCounterName() { return counterName; }
}
/**
* per module counters, indexed by the module name and storing Counter information.
*/
protected ConcurrentHashMap<String, List<CounterInfo>> moduleCounters =
new ConcurrentHashMap<String, List<CounterInfo>>();
/**
* fast global cache for counter names that are currently active
*/
Set<String> currentCounters = Collections.newSetFromMap(
new ConcurrentHashMap<String,Boolean>());
/**
* Thread local cache for counter names that are currently active.
*/
protected final ThreadLocal<Set<String>> threadlocalCurrentCounters =
new ThreadLocal<Set<String>>() {
@Override
protected Set<String> initialValue() {
return new HashSet<String>();
}
};
//*******************************
// IDebugCounterService
//*******************************
@Override
public boolean registerCounter(String moduleCounterName, String counterDescription,
CounterType counterType) {
if (debugCounters.containsKey(moduleCounterName)) {
log.error("Cannot register counter: {}. Counter already exists",
moduleCounterName);
return false;
}
String[] temp = moduleCounterName.split("-");
if (temp.length < 2) {
log.error("Cannot register counter: {}. Name not of type " +
" <module name>-<counter name>", moduleCounterName);
return false;
}
// store counter information on a per module basis
String moduleName = temp[0];
List<CounterInfo> a;
if (moduleCounters.containsKey(moduleName)) {
a = moduleCounters.get(moduleName);
} else {
a = new ArrayList<CounterInfo>();
moduleCounters.put(moduleName, a);
}
a.add(new CounterInfo(moduleCounterName, counterDescription, counterType));
// create counter in global map
// and add to counter name cache if it is meant to be always counted
if (counterType == CounterType.ALWAYS_COUNT) {
currentCounters.add(moduleCounterName);
debugCounters.put(moduleCounterName, new AtomicLong());
}
return true;
}
@Override
public void updateCounter(String moduleCounterName) {
Map<String, MutableLong> thismap = this.threadlocalCounters.get();
MutableLong ml = thismap.get(moduleCounterName);
if (ml == null) {
// check locally to see if this counter should be created or not
Set<String> thisset = this.threadlocalCurrentCounters.get();
if (thisset.contains(moduleCounterName)) {
ml = new MutableLong();
ml.increment();
thismap.put(moduleCounterName, ml);
}
} else {
ml.increment();
}
}
@Override
public void flushCounters() {
Map<String, MutableLong> thismap = this.threadlocalCounters.get();
ArrayList<String> deleteKeys = new ArrayList<String>();
for (String key : thismap.keySet()) {
MutableLong curval = thismap.get(key);
long delta = curval.get();
if (delta > 0) {
AtomicLong ctr = debugCounters.get(key);
if (ctr == null) {
// The global counter does not exist possibly because it has been
// disabled. It should thus be removed from the thread-local
// map (the counter) and set (the counter name). Removing it
// from the threadlocal set ensures that the counter will not be
// recreated (see updateCounter)
Set<String> thisset = this.threadlocalCurrentCounters.get();
thisset.remove(key);
deleteKeys.add(key);
} else {
ctr.addAndGet(delta);
curval.set(0);
}
}
}
for (String dkey : deleteKeys)
thismap.remove(dkey);
// At this point it is also possible that the threadlocal map/set does not
// include a counter that has been enabled and is present in the global
// currentCounters set. If so we need to sync such state so that the
// thread local counter can be created (in the updateCounter method)
Set<String> thisset = this.threadlocalCurrentCounters.get();
if (thisset.size() != currentCounters.size()) {
thisset.addAll(currentCounters);
}
}
@Override
public void resetCounter(String moduleCounterName) {
if (debugCounters.containsKey(moduleCounterName)) {
debugCounters.get(moduleCounterName).set(0);
}
}
@Override
public void resetAllCounters() {
for (AtomicLong v : debugCounters.values()) {
v.set(0);
}
}
@Override
public void resetAllModuleCounters(String moduleName) {
List<CounterInfo> cil = moduleCounters.get(moduleName);
if (cil != null) {
for (CounterInfo ci : cil) {
if (debugCounters.containsKey(ci.moduleCounterName)) {
debugCounters.get(ci.moduleCounterName).set(0);
}
}
} else {
if (log.isDebugEnabled())
log.debug("No module found with name {}", moduleName);
}
}
@Override
public void enableCtrOnDemand(String moduleCounterName) {
currentCounters.add(moduleCounterName);
debugCounters.putIfAbsent(moduleCounterName, new AtomicLong());
}
@Override
public void disableCtrOnDemand(String moduleCounterName) {
String[] temp = moduleCounterName.split("-");
if (temp.length < 2) {
log.error("moduleCounterName {} not recognized", moduleCounterName);
return;
}
String moduleName = temp[0];
List<CounterInfo> cil = moduleCounters.get(moduleName);
for (CounterInfo ci : cil) {
if (ci.moduleCounterName.equals(moduleCounterName) &&
ci.ctype == CounterType.COUNT_ON_DEMAND) {
currentCounters.remove(moduleCounterName);
debugCounters.remove(moduleCounterName);
return;
}
}
}
@Override
public DebugCounterInfo getCounterValue(String moduleCounterName) {
if (!debugCounters.containsKey(moduleCounterName)) return null;
long counterValue = debugCounters.get(moduleCounterName).longValue();
String[] temp = moduleCounterName.split("-");
if (temp.length < 2) {
log.error("moduleCounterName {} not recognized", moduleCounterName);
return null;
}
String moduleName = temp[0];
List<CounterInfo> cil = moduleCounters.get(moduleName);
for (CounterInfo ci : cil) {
if (ci.moduleCounterName.equals(moduleCounterName)) {
DebugCounterInfo dci = new DebugCounterInfo();
dci.counterInfo = ci;
dci.counterValue = counterValue;
return dci;
}
}
return null;
}
@Override
public List<DebugCounterInfo> getAllCounterValues() {
List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>();
for (List<CounterInfo> cil : moduleCounters.values()) {
for (CounterInfo ci : cil) {
AtomicLong ctr = debugCounters.get(ci.moduleCounterName);
if (ctr != null) {
DebugCounterInfo dci = new DebugCounterInfo();
dci.counterInfo = ci;
dci.counterValue = ctr.longValue();
dcilist.add(dci);
}
}
}
return dcilist;
}
@Override
public List<DebugCounterInfo> getModuleCounterValues(String moduleName) {
List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>();
if (moduleCounters.containsKey(moduleName)) {
List<CounterInfo> cil = moduleCounters.get(moduleName);
for (CounterInfo ci : cil) {
AtomicLong ctr = debugCounters.get(ci.moduleCounterName);
if (ctr != null) {
DebugCounterInfo dci = new DebugCounterInfo();
dci.counterInfo = ci;
dci.counterValue = ctr.longValue();
dcilist.add(dci);
}
}
}
return dcilist;
}
@Override
public boolean containsMCName(String moduleCounterName) {
if (debugCounters.containsKey(moduleCounterName)) return true;
// it is possible that the counter may be disabled
for (List<CounterInfo> cil : moduleCounters.values()) {
for (CounterInfo ci : cil) {
if (ci.moduleCounterName.equals(moduleCounterName))
return true;
}
}
return false;
}
@Override
public boolean containsModName(String moduleName) {
return (moduleCounters.containsKey(moduleName)) ? true : false;
}
//*******************************
// Internal Methods
//*******************************
protected void printAllCounters() {
for (List<CounterInfo> cilist : moduleCounters.values()) {
for (CounterInfo ci : cilist) {
log.info("Countername {} Countervalue {}", new Object[] {
ci.moduleCounterName, debugCounters.get(ci.moduleCounterName)
});
}
}
}
//*******************************
// IFloodlightModule
//*******************************
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IDebugCounterService.class);
return l;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
Map<Class<? extends IFloodlightService>, IFloodlightService> m =
new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
m.put(IDebugCounterService.class, this);
return m;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
return null;
}
@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
}
@Override
public void startUp(FloodlightModuleContext context) {
}
}