From: Michael Vorburger Date: Tue, 29 Aug 2017 12:22:59 +0000 (+0200) Subject: Bug 9060: Karaf CLI command to print open transactions X-Git-Tag: release/oxygen~119 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=9f3c837502dbffe101a3571b73d567899b9a9945 Bug 9060: Karaf CLI command to print open transactions including some minor changes to make output more pretty / readable. This is, for now, the last in a serious of commits which is part of a solution I'm proposing in order to be able to detect OOM issues such as Bug 9034, based on using the mdsal-trace DataBroker. Change-Id: I83af00a0713be4e8fab3085942b7b57d7183a20c Signed-off-by: Michael Vorburger --- diff --git a/opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java b/opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java index 34d5eb4a2a..3b62189bd9 100644 --- a/opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java +++ b/opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java @@ -11,7 +11,7 @@ import java.io.PrintStream; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; /** - * Tagging interface so that the tracing broker service can be more explicitly imported. + * Interface so that the tracing broker service can be more explicitly imported. */ public interface TracingDOMDataBroker extends DOMDataBroker { diff --git a/opendaylight/md-sal/mdsal-trace/api/src/main/resources/initial/mdsaltrace_config.xml b/opendaylight/md-sal/mdsal-trace/api/src/main/resources/initial/mdsaltrace_config.xml index 8e17c8b5fe..13b5d86fdd 100644 --- a/opendaylight/md-sal/mdsal-trace/api/src/main/resources/initial/mdsaltrace_config.xml +++ b/opendaylight/md-sal/mdsal-trace/api/src/main/resources/initial/mdsaltrace_config.xml @@ -2,4 +2,5 @@ + true diff --git a/opendaylight/md-sal/mdsal-trace/cli/pom.xml b/opendaylight/md-sal/mdsal-trace/cli/pom.xml new file mode 100644 index 0000000000..7ae7b58e94 --- /dev/null +++ b/opendaylight/md-sal/mdsal-trace/cli/pom.xml @@ -0,0 +1,84 @@ + + + + 4.0.0 + + + org.opendaylight.odlparent + bundle-parent + 2.0.4 + + + + org.opendaylight.controller + mdsal-trace-cli + 1.7.0-SNAPSHOT + bundle + + + + + org.opendaylight.controller + mdsal-artifacts + ${project.version} + pom + import + + + + + + + ${project.groupId} + mdsal-trace-api + + + org.apache.karaf.shell + org.apache.karaf.shell.core + + 4.0.9 + + + * + * + + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + * + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + checkstyle.violationSeverity=error + + + + org.codehaus.mojo + findbugs-maven-plugin + + true + + + + + diff --git a/opendaylight/md-sal/mdsal-trace/cli/src/main/java/org/opendaylight/controller/md/sal/trace/cli/PrintOpenTransactionsCommand.java b/opendaylight/md-sal/mdsal-trace/cli/src/main/java/org/opendaylight/controller/md/sal/trace/cli/PrintOpenTransactionsCommand.java new file mode 100644 index 0000000000..7f26f0dc6a --- /dev/null +++ b/opendaylight/md-sal/mdsal-trace/cli/src/main/java/org/opendaylight/controller/md/sal/trace/cli/PrintOpenTransactionsCommand.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.trace.cli; + +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.lifecycle.Reference; +import org.apache.karaf.shell.api.action.lifecycle.Service; +import org.opendaylight.controller.md.sal.trace.api.TracingDOMDataBroker; + +/** + * Karaf CLI command to dump all open transactions. + * + * @author Michael Vorburger.ch + */ +@Service +@Command(scope = "trace", name = "transactions", + description = "Show all (still) open transactions; including stack trace of creator, " + + "if transaction-debug-context-enabled is true in mdsaltrace_config.xml") +public class PrintOpenTransactionsCommand implements Action { + + @Reference + private TracingDOMDataBroker tracingDOMDataBroker; + + // NB: Do NOT have a non-default constructor for injection of @Reference + // Karaf needs a default constructor to create the command - and it works as is. + + @Override + @SuppressWarnings("checkstyle:RegexpSingleLineJava") + public Object execute() throws Exception { + if (!tracingDOMDataBroker.printOpenTransactions(System.out)) { + System.out.println("No open transactions, great!"); + } + return null; + } + +} diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java index b08e19ace7..2150e28905 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java @@ -13,6 +13,7 @@ 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; @@ -28,7 +29,9 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; import org.opendaylight.controller.md.sal.trace.api.TracingDOMDataBroker; +import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTracked; import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistry; +import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistryReportEntry; import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsaltrace.rev160908.Config; import org.opendaylight.yangtools.concepts.ListenerRegistration; @@ -49,7 +52,7 @@ import org.slf4j.LoggerFactory; * *

In addition, it (optionally) can also keep track of the stack trace of all new transaction allocations * (including TransactionChains, and transactions created in turn from them), in order to detect and report leaks - * results from transactions which were not closed. + * from transactions which were not closed. * *

Wiring:

* TracingBroker is designed to be easy to use. In fact, for bundles using Blueprint to inject their DataBroker @@ -62,17 +65,19 @@ import org.slf4j.LoggerFactory; * {@code * * org.opendaylight.controller - * mdsal-trace-features + * features-mdsal-trace + * 1.7.0-SNAPSHOT * features * xml * runtime - * 0.1.6-SNAPSHOT * * } * * *
  • - * Then just load the odl-mdsal-trace feature before your feature and you're done. + * Then just "feature:install odl-mdsal-trace" before you install your "real" feature(s) and you're done. + * Beware that with Karaf 4 due to Bug 9068 + * you'll probably have to use feature:install's --no-auto-refresh flag when installing your "real" feature. *
  • * * This works because the mdsaltrace-impl bundle registers its service implementing DOMDataBroker with a higher @@ -178,10 +183,11 @@ public class TracingBroker implements TracingDOMDataBroker { } else { this.isDebugging = false; } - this.transactionChainsRegistry = new CloseTrackedRegistry<>(this, "createTransactionChain", isDebugging); - this.readOnlyTransactionsRegistry = new CloseTrackedRegistry<>(this, "newReadOnlyTransaction", isDebugging); - this.writeTransactionsRegistry = new CloseTrackedRegistry<>(this, "newWriteOnlyTransaction", isDebugging); - this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(this, "newReadWriteTransaction", isDebugging); + final String db = "DataBroker"; + this.transactionChainsRegistry = new CloseTrackedRegistry<>(db, "createTransactionChain()", isDebugging); + this.readOnlyTransactionsRegistry = new CloseTrackedRegistry<>(db, "newReadOnlyTransaction()", isDebugging); + this.writeTransactionsRegistry = new CloseTrackedRegistry<>(db, "newWriteOnlyTransaction()", isDebugging); + this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(db, "newReadWriteTransaction()", isDebugging); } private void configure(Config config) { @@ -357,6 +363,7 @@ public class TracingBroker implements TracingDOMDataBroker { return res; } + @Override public boolean printOpenTransactions(PrintStream ps) { if (transactionChainsRegistry.getAllUnique().isEmpty() && readOnlyTransactionsRegistry.getAllUnique().isEmpty() @@ -366,37 +373,50 @@ public class TracingBroker implements TracingDOMDataBroker { return false; } - ps.println(getClass().getSimpleName() + " found not yet (or never..) closed transaction[chain]s"); + ps.println(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, ""); + printRegistryOpenTransactions(readOnlyTransactionsRegistry, ps, " "); + printRegistryOpenTransactions(writeTransactionsRegistry, ps, " "); + printRegistryOpenTransactions(readWriteTransactionsRegistry, ps, " "); // Now print details for each non-closed TransactionChain // incl. in turn each ones own read/Write[Only]TransactionsRegistry - ps.println(transactionChainsRegistry.getCreateDescription()); - transactionChainsRegistry.getAllUnique().forEach(entry -> { - ps.println(" " + entry.getNumberAddedNotRemoved() + "x TransactionChains opened, which are not closed:"); - entry.getStackTraceElements().forEach(line -> ps.println(" " + line)); + Set> + entries = transactionChainsRegistry.getAllUnique(); + if (!entries.isEmpty()) { + ps.println(" " + transactionChainsRegistry.getAnchor() + " : " + + transactionChainsRegistry.getCreateDescription()); + } + entries.forEach(entry -> { + ps.println(" " + entry.getNumberAddedNotRemoved() + "x TransactionChains opened but not closed here:"); + entry.getStackTraceElements().forEach(line -> ps.println(" " + line)); @SuppressWarnings("resource") TracingTransactionChain txChain = (TracingTransactionChain) entry .getExampleCloseTracked().getRealCloseTracked(); - printRegistryOpenTransactions(txChain.getReadOnlyTransactionsRegistry(), ps, " "); - printRegistryOpenTransactions(txChain.getWriteTransactionsRegistry(), ps, " "); - printRegistryOpenTransactions(txChain.getReadWriteTransactionsRegistry(), ps, " "); + printRegistryOpenTransactions(txChain.getReadOnlyTransactionsRegistry(), ps, " "); + printRegistryOpenTransactions(txChain.getWriteTransactionsRegistry(), ps, " "); + printRegistryOpenTransactions(txChain.getReadWriteTransactionsRegistry(), ps, " "); }); ps.println(); return true; } - private void printRegistryOpenTransactions(CloseTrackedRegistry registry, PrintStream ps, String indent) { - ps.println(indent + registry.getCreateDescription()); - registry.getAllUnique().forEach(entry -> { + private > void printRegistryOpenTransactions( + CloseTrackedRegistry registry, PrintStream ps, String indent) { + Set> entries = registry.getAllUnique(); + if (!entries.isEmpty()) { + ps.println(indent + registry.getAnchor() + " : " + registry.getCreateDescription()); + } + entries.forEach(entry -> { ps.println(indent + " " + entry.getNumberAddedNotRemoved() + "x transactions opened here, which are not closed:"); - entry.getStackTraceElements().forEach(line -> ps.print(" " + line)); + entry.getStackTraceElements().forEach(line -> ps.println(indent + " " + line)); }); - ps.println(); + if (!entries.isEmpty()) { + ps.println(); + } } } diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java index ac6aaa8bdb..bb65031843 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java @@ -30,10 +30,10 @@ class TracingTransactionChain extends AbstractCloseTracked(this, pf + "newReadOnlyTransaction", isDebug); - this.writeTransactionsRegistry = new CloseTrackedRegistry<>(this, pf + "newWriteOnlyTransaction", isDebug); - this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(this, pf + "newReadWriteTransaction", isDebug); + String anchor = "TransactionChain@" + Integer.toHexString(hashCode()); + this.readOnlyTransactionsRegistry = new CloseTrackedRegistry<>(anchor, "newReadOnlyTransaction()", isDebug); + this.writeTransactionsRegistry = new CloseTrackedRegistry<>(anchor, "newWriteOnlyTransaction()", isDebug); + this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(anchor, "newReadWriteTransaction()", isDebug); } @Override diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java index 0324986dbd..0c4c6c57d1 100644 --- a/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java +++ b/opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java @@ -38,8 +38,9 @@ public class TracingBrokerTest { BindingNormalizedNodeSerializer codec = mock(BindingNormalizedNodeSerializer.class); TracingBroker tracingBroker = new TracingBroker(domDataBroker, config, codec); + DOMDataReadWriteTransaction tx = tracingBroker.newReadWriteTransaction(); DOMTransactionChain txChain = tracingBroker.createTransactionChain(null); - DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction(); + DOMDataReadWriteTransaction txFromChain = txChain.newReadWriteTransaction(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); diff --git a/opendaylight/md-sal/mdsal-trace/features/odl-mdsal-trace/pom.xml b/opendaylight/md-sal/mdsal-trace/features/odl-mdsal-trace/pom.xml index 83654dc551..a01b9e91ae 100644 --- a/opendaylight/md-sal/mdsal-trace/features/odl-mdsal-trace/pom.xml +++ b/opendaylight/md-sal/mdsal-trace/features/odl-mdsal-trace/pom.xml @@ -60,5 +60,10 @@ mdsal-trace-binding-impl ${project.version} + + org.opendaylight.controller + mdsal-trace-cli + ${project.version} + diff --git a/opendaylight/md-sal/mdsal-trace/pom.xml b/opendaylight/md-sal/mdsal-trace/pom.xml index 5e66f8a0c9..f05f9964aa 100644 --- a/opendaylight/md-sal/mdsal-trace/pom.xml +++ b/opendaylight/md-sal/mdsal-trace/pom.xml @@ -25,6 +25,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL api dom-impl binding-impl + cli features @@ -77,13 +78,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL - - org.apache.maven.plugins - maven-checkstyle-plugin - - checkstyle.violationSeverity=error - -