blob: 76110b0217e30630a34950eb43531e74b6397378 [file] [log] [blame]
alshabib1f44e8e2014-08-14 15:19:57 -07001package net.onrc.onos.of.ctl.debugcounter;
2
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.Collections;
6import java.util.HashSet;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
10import java.util.concurrent.ConcurrentHashMap;
11import java.util.concurrent.atomic.AtomicInteger;
12import java.util.concurrent.atomic.AtomicLong;
13
14import org.slf4j.Logger;
15import org.slf4j.LoggerFactory;
16
17
18import com.google.common.collect.Sets;
19
20
21
22/**
23 * This class implements a central store for all counters used for debugging the
24 * system. For counters based on traffic-type, see ICounterStoreService.
25 *
26 */
tom0eb04ca2014-08-25 14:34:51 -070027//CHECKSTYLE:OFF
alshabib1f44e8e2014-08-14 15:19:57 -070028public class DebugCounter implements IDebugCounterService {
29 protected static final Logger log = LoggerFactory.getLogger(DebugCounter.class);
30
31 /**
32 * registered counters need a counter id.
33 */
34 protected AtomicInteger counterIdCounter = new AtomicInteger();
35
36 /**
37 * The counter value.
38 */
39 protected static class MutableLong {
40 long value = 0;
41 public void increment() { value += 1; }
42 public void increment(long incr) { value += incr; }
43 public long get() { return value; }
44 public void set(long val) { value = val; }
45 }
46
47 /**
48 * protected class to store counter information.
49 */
50 public static class CounterInfo {
51 String moduleCounterHierarchy;
52 String counterDesc;
53 CounterType ctype;
54 String moduleName;
55 String counterHierarchy;
56 int counterId;
57 boolean enabled;
58 String[] metaData;
59
60 public CounterInfo(int counterId, boolean enabled,
61 String moduleName, String counterHierarchy,
62 String desc, CounterType ctype, String... metaData) {
63 this.moduleCounterHierarchy = moduleName + "/" + counterHierarchy;
64 this.moduleName = moduleName;
65 this.counterHierarchy = counterHierarchy;
66 this.counterDesc = desc;
67 this.ctype = ctype;
68 this.counterId = counterId;
69 this.enabled = enabled;
70 this.metaData = metaData;
71 }
72
73 public String getModuleCounterHierarchy() { return moduleCounterHierarchy; }
74 public String getCounterDesc() { return counterDesc; }
75 public CounterType getCtype() { return ctype; }
76 public String getModuleName() { return moduleName; }
77 public String getCounterHierarchy() { return counterHierarchy; }
78 public int getCounterId() { return counterId; }
79 public boolean isEnabled() { return enabled; }
80 public String[] getMetaData() { return this.metaData.clone(); }
81 }
82
83 //******************
84 // Global stores
85 //******************
86
87 /**
88 * Counter info for a debug counter.
89 */
90 public static class DebugCounterInfo {
91 CounterInfo cinfo;
92 AtomicLong cvalue;
93
94 public DebugCounterInfo(CounterInfo cinfo) {
95 this.cinfo = cinfo;
96 this.cvalue = new AtomicLong();
97 }
98 public CounterInfo getCounterInfo() {
99 return cinfo;
100 }
101 public Long getCounterValue() {
102 return cvalue.get();
103 }
104 }
105
106 /**
107 * Global debug-counter storage across all threads. These are
108 * updated from the local per thread counters by the flush counters method.
109 */
110 private static final DebugCounterInfo[] ALLCOUNTERS =
111 new DebugCounterInfo[MAX_COUNTERS];
112
113
114 /**
115 * per module counters, indexed by the module name and storing three levels
116 * of Counter information in the form of CounterIndexStore.
117 */
118 protected ConcurrentHashMap<String, ConcurrentHashMap<String, CounterIndexStore>>
119 moduleCounters = new ConcurrentHashMap<String,
120 ConcurrentHashMap<String,
121 CounterIndexStore>>();
122
123 protected static class CounterIndexStore {
124 int index;
125 Map<String, CounterIndexStore> nextLevel;
126
127 public CounterIndexStore(int index, Map<String, CounterIndexStore> cis) {
128 this.index = index;
129 this.nextLevel = cis;
130 }
131 }
132
133 /**
134 * fast global cache for counter ids that are currently active.
135 */
136 protected Set<Integer> currentCounters = Collections.newSetFromMap(
137 new ConcurrentHashMap<Integer, Boolean>());
138
139 //******************
140 // Thread local stores
141 //******************
142
143 /**
144 * Thread local storage of counter info.
145 */
146 protected static class LocalCounterInfo {
147 boolean enabled;
148 MutableLong cvalue;
149
150 public LocalCounterInfo(boolean enabled) {
151 this.enabled = enabled;
152 this.cvalue = new MutableLong();
153 }
154 }
155
156 /**
157 * Thread local debug counters used for maintaining counters local to a thread.
158 */
159 protected final ThreadLocal<LocalCounterInfo[]> threadlocalCounters =
160 new ThreadLocal<LocalCounterInfo[]>() {
161 @Override
162 protected LocalCounterInfo[] initialValue() {
163 return new LocalCounterInfo[MAX_COUNTERS];
164 }
165 };
166
167 /**
168 * Thread local cache for counter ids that are currently active.
169 */
170 protected final ThreadLocal<Set<Integer>> threadlocalCurrentCounters =
171 new ThreadLocal<Set<Integer>>() {
172 @Override
173 protected Set<Integer> initialValue() {
174 return new HashSet<Integer>();
175 }
176 };
177
178 //*******************************
179 // IDebugCounter
180 //*******************************
181
182 protected class CounterImpl implements IDebugCounter {
183 private final int counterId;
184
185 public CounterImpl(int counterId) {
186 this.counterId = counterId;
187 }
188
189 @Override
190 public void updateCounterWithFlush() {
191 if (!validCounterId()) {
192 return;
193 }
194 updateCounter(counterId, 1, true);
195 }
196
197 @Override
198 public void updateCounterNoFlush() {
199 if (!validCounterId()) {
200 return;
201 }
202 updateCounter(counterId, 1, false);
203 }
204
205 @Override
206 public void updateCounterWithFlush(int incr) {
207 if (!validCounterId()) {
208 return;
209 }
210 updateCounter(counterId, incr, true);
211 }
212
213 @Override
214 public void updateCounterNoFlush(int incr) {
215 if (!validCounterId()) {
216 return;
217 }
218 updateCounter(counterId, incr, false);
219 }
220
221 @Override
222 public long getCounterValue() {
223 if (!validCounterId()) {
224 return -1;
225 }
226 return ALLCOUNTERS[counterId].cvalue.get();
227 }
228
229 /**
230 * Checks if this is a valid counter.
231 * @return true if the counter id is valid
232 */
233 private boolean validCounterId() {
234 if (counterId < 0 || counterId >= MAX_COUNTERS) {
235 log.error("Invalid counterId invoked");
236 return false;
237 }
238 return true;
239 }
240
241 }
242
243 //*******************************
244 // IDebugCounterService
245 //*******************************
246
247 @Override
248 public IDebugCounter registerCounter(String moduleName, String counterHierarchy,
249 String counterDescription, CounterType counterType,
250 String... metaData)
251 throws CounterException {
252 // check if counter already exists
253 if (!moduleCounters.containsKey(moduleName)) {
254 moduleCounters.putIfAbsent(moduleName,
255 new ConcurrentHashMap<String, CounterIndexStore>());
256 }
257 RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
258 if (rci.allLevelsFound) {
259 // counter exists
260 log.info("Counter exists for {}/{} -- resetting counters", moduleName,
261 counterHierarchy);
262 resetCounterHierarchy(moduleName, counterHierarchy);
263 return new CounterImpl(rci.ctrIds[rci.foundUptoLevel - 1]);
264 }
265 // check for validity of counter
266 if (rci.levels.length > MAX_HIERARCHY) {
267 String err = "Registry of counterHierarchy " + counterHierarchy +
268 " exceeds max hierachy " + MAX_HIERARCHY + ".. aborting";
269 throw new MaxHierarchyRegistered(err);
270 }
271 if (rci.foundUptoLevel < rci.levels.length - 1) {
272 StringBuilder sb = new StringBuilder();
273 for (int i = 0; i <= rci.foundUptoLevel; i++) {
274 sb.append(rci.levels[i]);
275 }
276 String needToRegister = sb.toString();
277 String err = "Attempting to register hierarchical counterHierarchy " +
278 counterHierarchy + " but parts of hierarchy missing. " +
279 "Please register " + needToRegister + " first";
280 throw new MissingHierarchicalLevel(err);
281 }
282
283 // get a new counter id
284 int counterId = counterIdCounter.getAndIncrement();
285 if (counterId >= MAX_COUNTERS) {
286 throw new MaxCountersRegistered("max counters reached");
287 }
288 // create storage for counter
289 boolean enabled = (counterType == CounterType.ALWAYS_COUNT) ? true : false;
290 CounterInfo ci = new CounterInfo(counterId, enabled, moduleName,
291 counterHierarchy, counterDescription,
292 counterType, metaData);
293 ALLCOUNTERS[counterId] = new DebugCounterInfo(ci);
294
295 // account for the new counter in the module counter hierarchy
296 addToModuleCounterHierarchy(moduleName, counterId, rci);
297
298 // finally add to active counters
299 if (enabled) {
300 currentCounters.add(counterId);
301 }
302 return new CounterImpl(counterId);
303 }
304
305 private void updateCounter(int counterId, int incr, boolean flushNow) {
306 if (counterId < 0 || counterId >= MAX_COUNTERS) {
307 return;
308 }
309
310 LocalCounterInfo[] thiscounters = this.threadlocalCounters.get();
311 if (thiscounters[counterId] == null) {
312 // seeing this counter for the first time in this thread - create local
313 // store by consulting global store
314 DebugCounterInfo dc = ALLCOUNTERS[counterId];
315 if (dc != null) {
316 thiscounters[counterId] = new LocalCounterInfo(dc.cinfo.enabled);
317 if (dc.cinfo.enabled) {
318 Set<Integer> thisset = this.threadlocalCurrentCounters.get();
319 thisset.add(counterId);
320 }
321 } else {
322 log.error("updateCounter seen locally for counter {} but no global"
323 + "storage exists for it yet .. not updating", counterId);
324 return;
325 }
326 }
327
328 // update local store if enabled locally for updating
329 LocalCounterInfo lc = thiscounters[counterId];
330 if (lc.enabled) {
331 lc.cvalue.increment(incr);
332 if (flushNow) {
333 DebugCounterInfo dc = ALLCOUNTERS[counterId];
334 if (dc.cinfo.enabled) {
335 // globally enabled - flush now
336 dc.cvalue.addAndGet(lc.cvalue.get());
337 lc.cvalue.set(0);
338 } else {
339 // global counter is disabled - don't flush, disable locally
340 lc.enabled = false;
341 Set<Integer> thisset = this.threadlocalCurrentCounters.get();
342 thisset.remove(counterId);
343 }
344 }
345 }
346 }
347
348 @Override
349 public void flushCounters() {
350 LocalCounterInfo[] thiscounters = this.threadlocalCounters.get();
351 Set<Integer> thisset = this.threadlocalCurrentCounters.get();
352 ArrayList<Integer> temp = new ArrayList<Integer>();
353
354 for (int counterId : thisset) {
355 LocalCounterInfo lc = thiscounters[counterId];
356 if (lc.cvalue.get() > 0) {
357 DebugCounterInfo dc = ALLCOUNTERS[counterId];
358 if (dc.cinfo.enabled) {
359 // globally enabled - flush now
360 dc.cvalue.addAndGet(lc.cvalue.get());
361 lc.cvalue.set(0);
362 } else {
363 // global counter is disabled - don't flush, disable locally
364 lc.enabled = false;
365 temp.add(counterId);
366 }
367 }
368 }
369 for (int cId : temp) {
370 thisset.remove(cId);
371 }
372
373 // At this point it is possible that the thread-local set does not
374 // include a counter that has been enabled and is present in the global set.
375 // We need to sync thread-local currently enabled set of counterIds with
376 // the global set.
377 Sets.SetView<Integer> sv = Sets.difference(currentCounters, thisset);
378 for (int counterId : sv) {
379 if (thiscounters[counterId] != null) {
380 thiscounters[counterId].enabled = true;
381 thisset.add(counterId);
382 }
383 }
384 }
385
386 @Override
387 public void resetCounterHierarchy(String moduleName, String counterHierarchy) {
388 RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
389 if (!rci.allLevelsFound) {
390 String missing = rci.levels[rci.foundUptoLevel];
391 log.error("Cannot reset counter hierarchy - missing counter {}", missing);
392 return;
393 }
394 // reset at this level
395 ALLCOUNTERS[rci.ctrIds[rci.foundUptoLevel - 1]].cvalue.set(0);
396 // reset all levels below
397 ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
398 for (int index : resetIds) {
399 ALLCOUNTERS[index].cvalue.set(0);
400 }
401 }
402
403 @Override
404 public void resetAllCounters() {
405 RetCtrInfo rci = new RetCtrInfo();
406 rci.levels = "".split("/");
407 for (String moduleName : moduleCounters.keySet()) {
408 ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
409 for (int index : resetIds) {
410 ALLCOUNTERS[index].cvalue.set(0);
411 }
412 }
413 }
414
415 @Override
416 public void resetAllModuleCounters(String moduleName) {
417 Map<String, CounterIndexStore> target = moduleCounters.get(moduleName);
418 RetCtrInfo rci = new RetCtrInfo();
419 rci.levels = "".split("/");
420
421 if (target != null) {
422 ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
423 for (int index : resetIds) {
424 ALLCOUNTERS[index].cvalue.set(0);
425 }
426 } else {
427 if (log.isDebugEnabled()) {
428 log.debug("No module found with name {}", moduleName);
429 }
430 }
431 }
432
433 @Override
434 public void enableCtrOnDemand(String moduleName, String counterHierarchy) {
435 RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
436 if (!rci.allLevelsFound) {
437 String missing = rci.levels[rci.foundUptoLevel];
438 log.error("Cannot enable counter - counter not found {}", missing);
439 return;
440 }
441 // enable specific counter
442 DebugCounterInfo dc = ALLCOUNTERS[rci.ctrIds[rci.foundUptoLevel - 1]];
443 dc.cinfo.enabled = true;
444 currentCounters.add(dc.cinfo.counterId);
445 }
446
447 @Override
448 public void disableCtrOnDemand(String moduleName, String counterHierarchy) {
449 RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
450 if (!rci.allLevelsFound) {
451 String missing = rci.levels[rci.foundUptoLevel];
452 log.error("Cannot disable counter - counter not found {}", missing);
453 return;
454 }
455 // disable specific counter
456 DebugCounterInfo dc = ALLCOUNTERS[rci.ctrIds[rci.foundUptoLevel - 1]];
457 if (dc.cinfo.ctype == CounterType.COUNT_ON_DEMAND) {
458 dc.cinfo.enabled = false;
459 dc.cvalue.set(0);
460 currentCounters.remove(dc.cinfo.counterId);
461 }
462 }
463
464 @Override
465 public List<DebugCounterInfo> getCounterHierarchy(String moduleName,
466 String counterHierarchy) {
467 RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
468 if (!rci.allLevelsFound) {
469 String missing = rci.levels[rci.foundUptoLevel];
470 log.error("Cannot fetch counter - counter not found {}", missing);
471 return Collections.emptyList();
472 }
473 ArrayList<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>();
474 // get counter and all below it
475 DebugCounterInfo dc = ALLCOUNTERS[rci.ctrIds[rci.foundUptoLevel - 1]];
476 dcilist.add(dc);
477 ArrayList<Integer> belowIds = getHierarchyBelow(moduleName, rci);
478 for (int index : belowIds) {
479 dcilist.add(ALLCOUNTERS[index]);
480 }
481 return dcilist;
482 }
483
484 @Override
485 public List<DebugCounterInfo> getAllCounterValues() {
486 List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>();
487 RetCtrInfo rci = new RetCtrInfo();
488 rci.levels = "".split("/");
489
490 for (String moduleName : moduleCounters.keySet()) {
491 ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
492 for (int index : resetIds) {
493 dcilist.add(ALLCOUNTERS[index]);
494 }
495 }
496 return dcilist;
497 }
498
499 @Override
500 public List<DebugCounterInfo> getModuleCounterValues(String moduleName) {
501 List<DebugCounterInfo> dcilist = new ArrayList<DebugCounterInfo>();
502 RetCtrInfo rci = new RetCtrInfo();
503 rci.levels = "".split("/");
504
505 if (moduleCounters.containsKey(moduleName)) {
506 ArrayList<Integer> resetIds = getHierarchyBelow(moduleName, rci);
507 for (int index : resetIds) {
508 dcilist.add(ALLCOUNTERS[index]);
509 }
510 }
511 return dcilist;
512 }
513
514 @Override
515 public boolean containsModuleCounterHierarchy(String moduleName,
516 String counterHierarchy) {
517 if (!moduleCounters.containsKey(moduleName)) {
518 return false;
519 }
520 RetCtrInfo rci = getCounterId(moduleName, counterHierarchy);
521 return rci.allLevelsFound;
522 }
523
524 @Override
525 public boolean containsModuleName(String moduleName) {
526 return (moduleCounters.containsKey(moduleName)) ? true : false;
527 }
528
529 @Override
530 public List<String> getModuleList() {
531 List<String> retval = new ArrayList<String>();
532 retval.addAll(moduleCounters.keySet());
533 return retval;
534 }
535
536 @Override
537 public List<String> getModuleCounterList(String moduleName) {
538 if (!moduleCounters.containsKey(moduleName)) {
539 return Collections.emptyList();
540 }
541
542 List<String> retval = new ArrayList<String>();
543 RetCtrInfo rci = new RetCtrInfo();
544 rci.levels = "".split("/");
545
546 ArrayList<Integer> cids = getHierarchyBelow(moduleName, rci);
547 for (int index : cids) {
548 retval.add(ALLCOUNTERS[index].cinfo.counterHierarchy);
549 }
550 return retval;
551 }
552
553 //*******************************
554 // Internal Methods
555 //*******************************
556
557 protected class RetCtrInfo {
558 boolean allLevelsFound; // counter indices found all the way down the hierarchy
559 boolean hierarchical; // true if counterHierarchy is hierarchical
560 int foundUptoLevel;
561 int[] ctrIds;
562 String[] levels;
563
564 public RetCtrInfo() {
565 ctrIds = new int[MAX_HIERARCHY];
566 for (int i = 0; i < MAX_HIERARCHY; i++) {
567 ctrIds[i] = -1;
568 }
569 }
570
571 @Override
572 public int hashCode() {
573 final int prime = 31;
574 int result = 1;
575 result = prime * result + getOuterType().hashCode();
576 result = prime * result + (allLevelsFound ? 1231 : 1237);
577 result = prime * result + Arrays.hashCode(ctrIds);
578 result = prime * result + foundUptoLevel;
579 result = prime * result + (hierarchical ? 1231 : 1237);
580 result = prime * result + Arrays.hashCode(levels);
581 return result;
582 }
583
584 @Override
585 public boolean equals(Object oth) {
586 if (!(oth instanceof RetCtrInfo)) {
587 return false;
588 }
589 RetCtrInfo other = (RetCtrInfo) oth;
590 if (other.allLevelsFound != this.allLevelsFound) {
591 return false;
592 }
593 if (other.hierarchical != this.hierarchical) {
594 return false;
595 }
596 if (other.foundUptoLevel != this.foundUptoLevel) {
597 return false;
598 }
599 if (!Arrays.equals(other.ctrIds, this.ctrIds)) {
600 return false;
601 }
602 if (!Arrays.equals(other.levels, this.levels)) {
603 return false;
604 }
605 return true;
606 }
607
608 private DebugCounter getOuterType() {
609 return DebugCounter.this;
610 }
611
612
613
614 }
615
616 protected RetCtrInfo getCounterId(String moduleName, String counterHierarchy) {
617 RetCtrInfo rci = new RetCtrInfo();
618 Map<String, CounterIndexStore> templevel = moduleCounters.get(moduleName);
619 rci.levels = counterHierarchy.split("/");
620 if (rci.levels.length > 1) {
621 rci.hierarchical = true;
622 }
623 if (templevel == null) {
624 log.error("moduleName {} does not exist in debugCounters", moduleName);
625 return rci;
626 }
627
628 /*
629 if (rci.levels.length > MAX_HIERARCHY) {
630 // chop off all array elems greater that MAX_HIERARCHY
631 String[] temp = new String[MAX_HIERARCHY];
632 System.arraycopy(rci.levels, 0, temp, 0, MAX_HIERARCHY);
633 rci.levels = temp;
634 }
635 */
636 for (int i = 0; i < rci.levels.length; i++) {
637 if (templevel != null) {
638 CounterIndexStore cis = templevel.get(rci.levels[i]);
639 if (cis == null) {
640 // could not find counterHierarchy part at this level
641 break;
642 } else {
643 rci.ctrIds[i] = cis.index;
644 templevel = cis.nextLevel;
645 rci.foundUptoLevel++;
646 if (i == rci.levels.length - 1) {
647 rci.allLevelsFound = true;
648 }
649 }
650 } else {
651 // there are no more levels, which means that some part of the
652 // counterHierarchy has no corresponding map
653 break;
654 }
655 }
656 return rci;
657 }
658
659 protected void addToModuleCounterHierarchy(String moduleName, int counterId,
660 RetCtrInfo rci) {
661 Map<String, CounterIndexStore> target = moduleCounters.get(moduleName);
662 if (target == null) {
663 return;
664 }
665 CounterIndexStore cis = null;
666
667 for (int i = 0; i < rci.foundUptoLevel; i++) {
668 cis = target.get(rci.levels[i]);
669 target = cis.nextLevel;
670 }
671 if (cis != null) {
672 if (cis.nextLevel == null) {
673 cis.nextLevel = new ConcurrentHashMap<String, CounterIndexStore>();
674 }
675 cis.nextLevel.put(rci.levels[rci.foundUptoLevel],
676 new CounterIndexStore(counterId, null));
677 } else {
678 target.put(rci.levels[rci.foundUptoLevel],
679 new CounterIndexStore(counterId, null));
680 }
681 }
682
683 // given a partial hierarchical counter, return the rest of the hierarchy
684 protected ArrayList<Integer> getHierarchyBelow(String moduleName, RetCtrInfo rci) {
685 Map<String, CounterIndexStore> target = moduleCounters.get(moduleName);
686 CounterIndexStore cis = null;
687 ArrayList<Integer> retval = new ArrayList<Integer>();
688 if (target == null) {
689 return retval;
690 }
691
692 // get to the level given
693 for (int i = 0; i < rci.foundUptoLevel; i++) {
694 cis = target.get(rci.levels[i]);
695 target = cis.nextLevel;
696 }
697
698 if (target == null || rci.foundUptoLevel == MAX_HIERARCHY) {
699 // no more levels
700 return retval;
701 } else {
702 // recursively get all ids
703 getIdsAtLevel(target, retval, rci.foundUptoLevel + 1);
704 }
705
706 return retval;
707 }
708
709 protected void getIdsAtLevel(Map<String, CounterIndexStore> hcy,
710 ArrayList<Integer> retval, int level) {
711 if (level > MAX_HIERARCHY) {
712 return;
713 }
714 if (hcy == null || retval == null) {
715 return;
716 }
717
718 // Can return the counter names as well but for now ids are enough.
719 for (CounterIndexStore cistemp : hcy.values()) {
720 retval.add(cistemp.index); // value at this level
721 if (cistemp.nextLevel != null) {
722 getIdsAtLevel(cistemp.nextLevel, retval, level + 1);
723 }
724 }
725 }
726
727}