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) { } }