*/
package org.opendaylight.controller.md.sal.trace.dom.impl;
+import static java.util.Objects.requireNonNull;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
-import javax.annotation.Nonnull;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
* </ul>
*
*/
+@Deprecated
public class TracingBroker implements TracingDOMDataBroker {
-
+ @SuppressFBWarnings("SLF4J_LOGGER_SHOULD_BE_PRIVATE")
static final Logger LOG = LoggerFactory.getLogger(TracingBroker.class);
private static final int STACK_TRACE_FIRST_RELEVANT_FRAME = 2;
+ private final String type; // "default" VS "pingpong"
private final BindingNormalizedNodeSerializer codec;
private final DOMDataBroker delegate;
private final List<Watch> registrationWatches = new ArrayList<>();
return child.startsWith(parent.substring(parentOffset), childOffset);
}
- public boolean subtreesOverlap(YangInstanceIdentifier iid, LogicalDatastoreType store,
- AsyncDataBroker.DataChangeScope scope) {
+ @SuppressWarnings({ "checkstyle:hiddenField", "hiding" })
+ public boolean subtreesOverlap(YangInstanceIdentifier iid, LogicalDatastoreType store) {
if (this.store != null && !this.store.equals(store)) {
return false;
}
String otherIidString = toIidCompString(iid);
- switch (scope) {
- case BASE:
- return isParent(iidString, otherIidString);
- case ONE: //for now just treat like SUBTREE, even though it's not
- case SUBTREE:
- return isParent(iidString, otherIidString) || isParent(otherIidString, iidString);
- default:
- return false;
- }
+ return isParent(iidString, otherIidString) || isParent(otherIidString, iidString);
}
+ @SuppressWarnings({ "checkstyle:hiddenField", "hiding" })
public boolean eventIsOfInterest(YangInstanceIdentifier iid, LogicalDatastoreType store) {
if (this.store != null && !this.store.equals(store)) {
return false;
}
}
- public TracingBroker(DOMDataBroker delegate, Config config, BindingNormalizedNodeSerializer codec) {
- this.delegate = Objects.requireNonNull(delegate);
- this.codec = Objects.requireNonNull(codec);
+ public TracingBroker(String type, DOMDataBroker delegate, Config config, BindingNormalizedNodeSerializer codec) {
+ this.type = requireNonNull(type, "type");
+ this.delegate = requireNonNull(delegate, "delegate");
+ this.codec = requireNonNull(codec, "codec");
configure(config);
if (config.isTransactionDebugContextEnabled() != null) {
writeWatches.add(watch);
}
- private boolean isRegistrationWatched(YangInstanceIdentifier iid,
- LogicalDatastoreType store, DataChangeScope scope) {
+ @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+ justification = "https://github.com/spotbugs/spotbugs/issues/811")
+ private boolean isRegistrationWatched(YangInstanceIdentifier iid, LogicalDatastoreType store) {
if (registrationWatches.isEmpty()) {
return true;
}
for (Watch regInterest : registrationWatches) {
- if (regInterest.subtreesOverlap(iid, store, scope)) {
+ if (regInterest.subtreesOverlap(iid, store)) {
return true;
}
}
}
}
- private void reconstructIidPathString(YangInstanceIdentifier yiid, StringBuilder sb) {
+ private static void reconstructIidPathString(YangInstanceIdentifier yiid, StringBuilder sb) {
sb.append("<RECONSTRUCTED FROM: \"").append(yiid.toString()).append("\">");
for (YangInstanceIdentifier.PathArgument pathArg : yiid.getPathArguments()) {
if (pathArg instanceof YangInstanceIdentifier.AugmentationIdentifier) {
return new TracingWriteTransaction(delegate.newWriteOnlyTransaction(), this, writeTransactionsRegistry);
}
- @Override
- public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(
- LogicalDatastoreType store, YangInstanceIdentifier yiid,
- DOMDataChangeListener listener, DataChangeScope scope) {
- if (isRegistrationWatched(yiid, store, scope)) {
- LOG.warn("Registration (registerDataChangeListener) for {} from {}",
- toPathString(yiid), getStackSummary());
- }
- return delegate.registerDataChangeListener(store, yiid, listener, scope);
- }
-
@Override
public DOMTransactionChain createTransactionChain(TransactionChainListener transactionChainListener) {
return new TracingTransactionChain(
@Override
public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
- return new TracingReadOnlyTransaction(delegate.newReadOnlyTransaction(), this, readOnlyTransactionsRegistry);
+ return new TracingReadOnlyTransaction(delegate.newReadOnlyTransaction(), readOnlyTransactionsRegistry);
}
- @Nonnull
@Override
public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> res = delegate.getSupportedExtensions();
res = new HashMap<>(res);
res.put(DOMDataTreeChangeService.class, new DOMDataTreeChangeService() {
- @Nonnull
@Override
public <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(
- @Nonnull DOMDataTreeIdentifier domDataTreeIdentifier, @Nonnull L listener) {
+ DOMDataTreeIdentifier domDataTreeIdentifier, L listener) {
if (isRegistrationWatched(domDataTreeIdentifier.getRootIdentifier(),
- domDataTreeIdentifier.getDatastoreType(), DataChangeScope.SUBTREE)) {
+ domDataTreeIdentifier.getDatastoreType())) {
LOG.warn("{} registration (registerDataTreeChangeListener) for {} from {}.",
listener instanceof ClusteredDOMDataTreeChangeListener ? "Clustered" : "Non-clustered",
toPathString(domDataTreeIdentifier.getRootIdentifier()), getStackSummary());
}
@Override
- public boolean printOpenTransactions(PrintStream ps) {
+ public boolean printOpenTransactions(PrintStream ps, int minOpenTXs) {
if (transactionChainsRegistry.getAllUnique().isEmpty()
&& readOnlyTransactionsRegistry.getAllUnique().isEmpty()
&& writeTransactionsRegistry.getAllUnique().isEmpty()
&& readWriteTransactionsRegistry.getAllUnique().isEmpty()) {
+ ps.println(type + ": No open transactions, great!");
return false;
}
- ps.println(getClass().getSimpleName() + " found some not yet (or never..) closed transaction[chain]s!");
+ ps.println(type + ": " + getClass().getSimpleName()
+ + " found some not yet (or never..) closed transaction[chain]s!");
ps.println("[NB: If no stack traces are shown below, then "
+ "enable transaction-debug-context-enabled in mdsaltrace_config.xml]");
ps.println();
- printRegistryOpenTransactions(readOnlyTransactionsRegistry, ps, " ");
- printRegistryOpenTransactions(writeTransactionsRegistry, ps, " ");
- printRegistryOpenTransactions(readWriteTransactionsRegistry, ps, " ");
+ // Flag to track if we really found any real leaks with more (or equal) to minOpenTXs
+ boolean hasFound = print(readOnlyTransactionsRegistry, ps, " ", minOpenTXs);
+ hasFound |= print(writeTransactionsRegistry, ps, " ", minOpenTXs);
+ hasFound |= print(readWriteTransactionsRegistry, ps, " ", minOpenTXs);
// Now print details for each non-closed TransactionChain
// incl. in turn each ones own read/Write[Only]TransactionsRegistry
ps.println(" " + transactionChainsRegistry.getAnchor() + " : "
+ transactionChainsRegistry.getCreateDescription());
}
- entries.forEach(entry -> {
+ for (CloseTrackedRegistryReportEntry<TracingTransactionChain> entry : entries) {
ps.println(" " + entry.getNumberAddedNotRemoved() + "x TransactionChains opened but not closed here:");
printStackTraceElements(ps, " ", entry.getStackTraceElements());
@SuppressWarnings("resource")
TracingTransactionChain txChain = (TracingTransactionChain) entry
.getExampleCloseTracked().getRealCloseTracked();
- printRegistryOpenTransactions(txChain.getReadOnlyTransactionsRegistry(), ps, " ");
- printRegistryOpenTransactions(txChain.getWriteTransactionsRegistry(), ps, " ");
- printRegistryOpenTransactions(txChain.getReadWriteTransactionsRegistry(), ps, " ");
- });
+ hasFound |= print(txChain.getReadOnlyTransactionsRegistry(), ps, " ", minOpenTXs);
+ hasFound |= print(txChain.getWriteTransactionsRegistry(), ps, " ", minOpenTXs);
+ hasFound |= print(txChain.getReadWriteTransactionsRegistry(), ps, " ", minOpenTXs);
+ }
ps.println();
- return true;
+ return hasFound;
}
- private <T extends CloseTracked<T>> void printRegistryOpenTransactions(
- CloseTrackedRegistry<T> registry, PrintStream ps, String indent) {
- Set<CloseTrackedRegistryReportEntry<T>> entries = registry.getAllUnique();
+ private <T extends CloseTracked<T>> boolean print(
+ CloseTrackedRegistry<T> registry, PrintStream ps, String indent, int minOpenTransactions) {
+ Set<CloseTrackedRegistryReportEntry<T>> unsorted = registry.getAllUnique();
+ if (unsorted.size() < minOpenTransactions) {
+ return false;
+ }
+
+ List<CloseTrackedRegistryReportEntry<T>> entries = new ArrayList<>(unsorted);
+ entries.sort((o1, o2) -> Long.compare(o2.getNumberAddedNotRemoved(), o1.getNumberAddedNotRemoved()));
+
if (!entries.isEmpty()) {
ps.println(indent + registry.getAnchor() + " : " + registry.getCreateDescription());
}
if (!entries.isEmpty()) {
ps.println();
}
+ return true;
}
private void printStackTraceElements(PrintStream ps, String indent, List<StackTraceElement> stackTraceElements) {