Bug 9060: mdsal-trace tooling with getAllUnique() to find Tx leaks
[controller.git] / opendaylight / md-sal / mdsal-trace / dom-impl / src / main / java / org / opendaylight / controller / md / sal / trace / closetracker / impl / CloseTrackedRegistry.java
1 /*
2  * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.md.sal.trace.closetracker.impl;
9
10 import java.util.Arrays;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.concurrent.ConcurrentSkipListSet;
18 import javax.annotation.concurrent.ThreadSafe;
19
20 /**
21  * Registry of {@link CloseTracked} instances.
22  *
23  * @author Michael Vorburger.ch
24  */
25 @ThreadSafe
26 public class CloseTrackedRegistry<T extends CloseTracked<T>> {
27
28     // unused OK for now, at least we'll be able to see this in HPROF heap dumps and know what is which
29     private final @SuppressWarnings("unused") Object anchor;
30     private final @SuppressWarnings("unused") String createDescription;
31
32     private final Set<CloseTracked<T>> tracked = new ConcurrentSkipListSet<>(
33         (o1, o2) -> Integer.compare(System.identityHashCode(o1), System.identityHashCode(o2)));
34
35     private final boolean isDebugContextEnabled;
36
37     /**
38      * Constructor.
39      *
40      * @param anchor
41      *            object where this registry is stored in, used for human output in
42      *            logging and other output
43      * @param createDescription
44      *            description of creator of instances of this registry, typically
45      *            e.g. name of method in the anchor class
46      * @param isDebugContextEnabled
47      *            whether or not the call stack should be preserved; this is (of
48      *            course) an expensive operation, and should only be used during
49      *            troubleshooting
50      */
51     public CloseTrackedRegistry(Object anchor, String createDescription, boolean isDebugContextEnabled) {
52         this.anchor = anchor;
53         this.createDescription = createDescription;
54         this.isDebugContextEnabled = isDebugContextEnabled;
55     }
56
57     public boolean isDebugContextEnabled() {
58         return isDebugContextEnabled;
59     }
60
61     // package protected, not public; only CloseTrackedTrait invokes this
62     void add(CloseTracked<T> closeTracked) {
63         tracked.add(closeTracked);
64     }
65
66     // package protected, not public; only CloseTrackedTrait invokes this
67     void remove(CloseTracked<T> closeTracked) {
68         tracked.remove(closeTracked);
69     }
70
71     /**
72      * Creates and returns a "report" of (currently) tracked but not (yet) closed
73      * instances.
74      *
75      * @return Map where key is the StackTraceElement[] identifying a unique
76      *         allocation contexts (or an empty List if debugContextEnabled is false),
77      *         and value is the number of open instances created at that point.
78      */
79     public Map<List<StackTraceElement>, Long> getAllUnique() {
80         Map<List<StackTraceElement>,Long> mapToReturn = new HashMap<>();
81         Set<CloseTracked<T>> copyOfTracked = new HashSet<>(tracked);
82         for (CloseTracked<T> closeTracked : copyOfTracked) {
83             final StackTraceElement[] stackTraceArray = closeTracked.getAllocationContextStackTrace();
84             List<StackTraceElement> stackTraceElements =
85                     stackTraceArray != null ? Arrays.asList(stackTraceArray) : Collections.emptyList();
86             mapToReturn.merge(stackTraceElements, 1L, (oldValue, value) -> oldValue + 1);
87         }
88         return mapToReturn;
89     }
90
91 }