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