Bug 9060: TracingBroker printOpenTransactions
[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 static java.util.Arrays.asList;
11 import static java.util.Collections.emptyList;
12
13 import java.util.Arrays;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.concurrent.ConcurrentSkipListSet;
21 import javax.annotation.concurrent.ThreadSafe;
22
23 /**
24  * Registry of {@link CloseTracked} instances.
25  *
26  * @author Michael Vorburger.ch
27  */
28 @ThreadSafe
29 public class CloseTrackedRegistry<T extends CloseTracked<T>> {
30
31     private final Object anchor;
32     private final String createDescription;
33
34     private final Set<CloseTracked<T>> tracked = new ConcurrentSkipListSet<>(
35         (o1, o2) -> Integer.compare(System.identityHashCode(o1), System.identityHashCode(o2)));
36
37     private final boolean isDebugContextEnabled;
38
39     /**
40      * Constructor.
41      *
42      * @param anchor
43      *            object where this registry is stored in, used for human output in
44      *            logging and other output
45      * @param createDescription
46      *            description of creator of instances of this registry, typically
47      *            e.g. name of method in the anchor class
48      * @param isDebugContextEnabled
49      *            whether or not the call stack should be preserved; this is (of
50      *            course) an expensive operation, and should only be used during
51      *            troubleshooting
52      */
53     public CloseTrackedRegistry(Object anchor, String createDescription, boolean isDebugContextEnabled) {
54         this.anchor = anchor;
55         this.createDescription = createDescription;
56         this.isDebugContextEnabled = isDebugContextEnabled;
57     }
58
59     public boolean isDebugContextEnabled() {
60         return isDebugContextEnabled;
61     }
62
63     public Object getAnchor() {
64         return anchor;
65     }
66
67     public String getCreateDescription() {
68         return createDescription;
69     }
70
71     // package protected, not public; only CloseTrackedTrait invokes this
72     void add(CloseTracked<T> closeTracked) {
73         tracked.add(closeTracked);
74     }
75
76     // package protected, not public; only CloseTrackedTrait invokes this
77     void remove(CloseTracked<T> closeTracked) {
78         tracked.remove(closeTracked);
79     }
80
81     /**
82      * Creates and returns a "report" of (currently) tracked but not (yet) closed
83      * instances.
84      *
85      * @return Set of CloseTrackedRegistryReportEntry, of which each the stack trace
86      *         element identifies a unique allocation context (or an empty List if
87      *         debugContextEnabled is false), and value is the number of open
88      *         instances created at that place in the code.
89      */
90     public Set<CloseTrackedRegistryReportEntry<T>> getAllUnique() {
91         Map<List<StackTraceElement>, Long> map = new HashMap<>();
92         Set<CloseTracked<T>> copyOfTracked = new HashSet<>(tracked);
93         for (CloseTracked<T> closeTracked : copyOfTracked) {
94             final StackTraceElement[] stackTraceArray = closeTracked.getAllocationContextStackTrace();
95             List<StackTraceElement> stackTraceElements =
96                     stackTraceArray != null ? Arrays.asList(stackTraceArray) : Collections.emptyList();
97             map.merge(stackTraceElements, 1L, (oldValue, value) -> oldValue + 1);
98         }
99
100         Set<CloseTrackedRegistryReportEntry<T>> report = new HashSet<>();
101         map.forEach((stackTraceElements, number) -> {
102             copyOfTracked.stream().filter(closeTracked -> {
103                 StackTraceElement[] closeTrackedStackTraceArray = closeTracked.getAllocationContextStackTrace();
104                 List<StackTraceElement> closeTrackedStackTraceElements =
105                         closeTrackedStackTraceArray != null ? asList(closeTrackedStackTraceArray) : emptyList();
106                 return closeTrackedStackTraceElements.equals(stackTraceElements);
107             }).findAny().ifPresent(exampleCloseTracked -> {
108                 report.add(new CloseTrackedRegistryReportEntry<>(exampleCloseTracked, number, stackTraceElements));
109             });
110         });
111         return report;
112     }
113
114 }