From fb62471a0743cb1d36bc8feb2bccb550ef1eee40 Mon Sep 17 00:00:00 2001
From: Saurav Das <saurav.das@bigswitch.com>
Date: Tue, 26 Mar 2013 22:00:54 -0700
Subject: [PATCH] Implementing the rest of the debugCounters API

---
 .../debugcounter/DebugCounter.java            | 165 ++++++++++++++----
 .../debugcounter/IDebugCounterService.java    |  65 ++++++-
 .../debugcounter/NullDebugCounter.java        |  18 +-
 3 files changed, 188 insertions(+), 60 deletions(-)

diff --git a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java
index 0b6555e03..10fd4656c 100644
--- a/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java
+++ b/src/main/java/net/floodlightcontroller/debugcounter/DebugCounter.java
@@ -4,6 +4,7 @@ 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;
@@ -39,7 +40,7 @@ public class DebugCounter implements IFloodlightModule, IDebugCounterService {
 
     /**
      * Global debug-counter storage across all threads. These are
-     * updated from the local per thread counters by the update FIXME method.
+     * updated from the local per thread counters by the flush counters method.
      */
     protected ConcurrentHashMap<String, AtomicLong> debugCounters =
             new ConcurrentHashMap<String, AtomicLong>();
@@ -82,11 +83,22 @@ public class DebugCounter implements IFloodlightModule, IDebugCounterService {
             new ConcurrentHashMap<String, List<CounterInfo>>();
 
     /**
-     * fast cache for counter names that are currently active
+     * 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
    //*******************************
@@ -118,31 +130,27 @@ public class DebugCounter implements IFloodlightModule, IDebugCounterService {
        a.add(new CounterInfo(moduleCounterName, counterDescription, counterType));
 
        // create counter in global map
-       debugCounters.put(moduleCounterName, new AtomicLong());
-
-       // create counter in local thread map
-       Map<String, MutableLong> thismap =  this.threadlocalCounters.get();
-       MutableLong ml = thismap.get(moduleCounterName);
-       if (ml == null) {
-           thismap.put(moduleCounterName, new MutableLong());
-       }
-
-       // finally add to cache if it is meant to be always counted
+       // 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) {
-       if (currentCounters.contains(moduleCounterName)) {
-           Map<String, MutableLong> thismap =  this.threadlocalCounters.get();
-           MutableLong ml = thismap.get(moduleCounterName);
-           if (ml == null) {
+       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();
        }
    }
@@ -154,58 +162,143 @@ public class DebugCounter implements IFloodlightModule, IDebugCounterService {
            MutableLong curval = thismap.get(key);
            long delta = curval.get();
            if (delta > 0) {
-               debugCounters.get(key).addAndGet(delta);
-               curval.set(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);
+                   thismap.remove(key);
+               } else {
+                   ctr.addAndGet(delta);
+                   curval.set(0);
+               }
            }
        }
+       // 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
+       // current counter store. 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);
+       }
+       //printAllCounters();
    }
 
    @Override
    public void resetCounter(String moduleCounterName) {
-       // TODO Auto-generated method stub
-
+       if (debugCounters.containsKey(moduleCounterName)) {
+           debugCounters.get(moduleCounterName).set(0);
+       }
    }
 
    @Override
    public void resetAllCounters() {
-       // TODO Auto-generated method stub
-
+       for (AtomicLong v : debugCounters.values()) {
+           v.set(0);
+       }
    }
 
    @Override
    public void resetAllModuleCounters(String moduleName) {
-       // TODO Auto-generated method stub
-
+       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) {
-       // TODO Auto-generated method stub
-
+       currentCounters.add(moduleCounterName);
+       debugCounters.putIfAbsent(moduleCounterName, new AtomicLong());
    }
 
    @Override
    public void disableCtrOnDemand(String moduleCounterName) {
-       // TODO Auto-generated method stub
-
+       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) {
-       // TODO Auto-generated method stub
+       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() {
-       // TODO Auto-generated method stub
-       return null;
+       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() {
-       // TODO Auto-generated method stub
-       return null;
+   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;
    }
 
 
@@ -246,19 +339,17 @@ public class DebugCounter implements IFloodlightModule, IDebugCounterService {
 
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
-        // TODO Auto-generated method stub
         return null;
     }
 
     @Override
     public void init(FloodlightModuleContext context) throws FloodlightModuleException {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void startUp(FloodlightModuleContext context) {
-        // TODO Auto-generated method stub
+
     }
 
 }
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java
index 6e10e13ac..807144021 100644
--- a/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java
+++ b/src/main/java/net/floodlightcontroller/debugcounter/IDebugCounterService.java
@@ -7,7 +7,8 @@ import java.util.List;
 public interface IDebugCounterService extends IFloodlightService {
 
     /**
-     * Different counter types.
+     * Different counter types. Counters that are meant to be counted on demand
+     * need to be separately enabled/disabled.
      */
     public enum CounterType {
         ALWAYS_COUNT,
@@ -28,6 +29,7 @@ public interface IDebugCounterService extends IFloodlightService {
      * @param moduleCounterName    the counter name which MUST be have the following
      *                             syntax:  <module name>-<counter name>
      *                             eg.: linkdiscovery-incoming
+     *                             There should be only a single '-' in the name
      * @param counterDescription   a descriptive string that gives more information
      *                             of what the counter is measuring. For example,
      *                             "Measures the number of incoming packets seen by
@@ -43,34 +45,85 @@ public interface IDebugCounterService extends IFloodlightService {
                                    CounterType counterType);
 
     /**
-     * Increments the counter by 1.
+     * Increments the counter by 1, if the counter is meant to be always counted,
+     * or if the counter has been enabled for counting.
      * @param moduleCounterName   the registered counter name.
      */
     public void updateCounter(String moduleCounterName);
 
     /**
-     * Update the global counter map with values from the thread local maps
+     * Update the global counter map with values from the thread local maps. This
+     * method is not intended for use by any module. It's typical usage is from
+     * floodlight core. As far as the modules are concerned, this should happen
+     * automatically for their counters.
      */
     public void flushCounters();
 
     /**
-     *
-     * @param moduleCounterName
+     * Resets the value of the counter to zero if it is currently enabled. Note
+     * that with live traffic, it is not necessary that the counter will display
+     * zero with a get call as it may get updated between the reset and get calls.
+     * @param moduleCounterName the registered counter name.
      */
     public void resetCounter(String moduleCounterName);
 
+    /**
+     * Resets the values of all counters that are currently enabled to zero.
+     */
     public void resetAllCounters();
 
+    /**
+     * Resets the values of all counters that are currently active and belong
+     * to a module with the given 'moduleName'. The moduleName MUST be the
+     * part of the moduleCounterName with which the counters were registered.
+     * eg. if 'linkdiscovery-incoming' and 'linkdiscovery-lldpeol' are two counters
+     * the module name is 'linkdiscovery'
+     * @param moduleName
+     */
     public void resetAllModuleCounters(String moduleName);
 
+    /**
+     * This method applies only to CounterType.COUNT_ON_DEMAND. It is used to
+     * enable counting on the counter. Note that this step is necessary to start
+     * counting for these counter types - merely registering the counter is not
+     * enough (as is the case for CounterType.ALWAYS_COUNT). Note that newly
+     * enabled counter starts from an initial value of zero.
+     *
+     * @param moduleCounterName  the registered counter name.
+     */
     public void enableCtrOnDemand(String moduleCounterName);
 
+    /**
+     * This method applies only to CounterType.ALWAYS_COUNT. It is used to disable
+     * counting on this counter. Note that disabling a counter results in a loss
+     * of the counter value. When re-enabled the counter will restart from zero.
+     *
+     * @param moduleCounterName the registered counter name.
+     */
     public void disableCtrOnDemand(String moduleCounterName);
 
+    /**
+     * Get counter value and associated information for a specific counter
+     *
+     * @param moduleCounterName
+     * @return DebugCounterInfo or null if the counter could not be found
+     */
     public DebugCounterInfo getCounterValue(String moduleCounterName);
 
+    /**
+     * Get counter values and associated information for all counters
+     *
+     * @return the list of values/info or an empty list
+     */
     public  List<DebugCounterInfo> getAllCounterValues();
 
-    public  List<DebugCounterInfo> getModuleCounterValues();
+    /**
+     * Get counter values and associated information for all counters associated
+     * with a module.
+     *
+     * @param moduleName
+     * @return the list of values/info or an empty list
+     */
+    public  List<DebugCounterInfo> getModuleCounterValues(String moduleName);
 
 }
diff --git a/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java b/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java
index 3807f9d96..8b6a1cb19 100644
--- a/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java
+++ b/src/main/java/net/floodlightcontroller/debugcounter/NullDebugCounter.java
@@ -15,28 +15,24 @@ public class NullDebugCounter implements IFloodlightModule, IDebugCounterService
     public boolean registerCounter(String moduleCounterName,
                                    String counterDescription,
                                    CounterType counterType) {
-        // TODO Auto-generated method stub
         return false;
     }
 
     @Override
     public Collection<Class<? extends IFloodlightService>>
             getModuleServices() {
-        // TODO Auto-generated method stub
         return null;
     }
 
     @Override
     public Map<Class<? extends IFloodlightService>, IFloodlightService>
             getServiceImpls() {
-        // TODO Auto-generated method stub
         return null;
     }
 
     @Override
     public Collection<Class<? extends IFloodlightService>>
             getModuleDependencies() {
-        // TODO Auto-generated method stub
         return null;
     }
 
@@ -45,73 +41,61 @@ public class NullDebugCounter implements IFloodlightModule, IDebugCounterService
             void
             init(FloodlightModuleContext context)
                                                  throws FloodlightModuleException {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void startUp(FloodlightModuleContext context) {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void updateCounter(String moduleCounterName) {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void flushCounters() {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void resetCounter(String moduleCounterName) {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void resetAllCounters() {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void resetAllModuleCounters(String moduleName) {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void enableCtrOnDemand(String moduleCounterName) {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public void disableCtrOnDemand(String moduleCounterName) {
-        // TODO Auto-generated method stub
 
     }
 
     @Override
     public DebugCounterInfo getCounterValue(String moduleCounterName) {
-        // TODO Auto-generated method stub
         return null;
     }
 
     @Override
     public List<DebugCounterInfo> getAllCounterValues() {
-        // TODO Auto-generated method stub
         return null;
     }
 
     @Override
-    public List<DebugCounterInfo> getModuleCounterValues() {
-        // TODO Auto-generated method stub
+    public List<DebugCounterInfo> getModuleCounterValues(String moduleName) {
         return null;
     }
 
-- 
GitLab