Bug 9060: Karaf CLI command to print open transactions 04/62504/2
authorMichael Vorburger <vorburger@redhat.com>
Tue, 29 Aug 2017 12:22:59 +0000 (14:22 +0200)
committerMichael Vorburger <vorburger@redhat.com>
Thu, 31 Aug 2017 15:54:19 +0000 (17:54 +0200)
This is not a 1:1 cherry-pick from master, but includes manual work to:
  1. fix versions and more in mdsal-trace/cli/pom.xml
  2. rework PrintOpenTransactionsCommand.java from Karaf 4 API to v3
  3. incl. for ^^^ a new BP commands.xml (not needed anymore on Karaf 4)
  4. features-mdsal-trace POM and features.xml include new cli bundle

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 <vorburger@redhat.com>
12 files changed:
opendaylight/md-sal/mdsal-trace/api/src/main/java/org/opendaylight/controller/md/sal/trace/api/TracingDOMDataBroker.java
opendaylight/md-sal/mdsal-trace/api/src/main/resources/initial/mdsaltrace_config.xml
opendaylight/md-sal/mdsal-trace/cli/pom.xml [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/cli/src/main/java/org/opendaylight/controller/md/sal/trace/cli/PrintOpenTransactionsCommand.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/cli/src/main/resources/org/opendaylight/blueprint/commands.xml [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/test/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/tests/TracingBrokerTest.java
opendaylight/md-sal/mdsal-trace/features/features-mdsal-trace/pom.xml
opendaylight/md-sal/mdsal-trace/features/features-mdsal-trace/src/main/features/features.xml
opendaylight/md-sal/mdsal-trace/features/odl-mdsal-trace/pom.xml
opendaylight/md-sal/mdsal-trace/pom.xml

index 34d5eb4a2ad18aabaa4f16f6cc482ffd98b9e846..3b62189bd9cdc6d3543c823e1425f444f3660f43 100644 (file)
@@ -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 {
 
index 8e17c8b5feefd16e5e9b4ea914ad0d95b858cb0f..13b5d86fdd636479177d57c14229c256f5a915b3 100644 (file)
@@ -2,4 +2,5 @@
     <!-- <registration-watches>/neutron-router-dpns/router-dpn-list</registration-watches> -->
     <!-- <registration-watches>/tunnels_state/state-tunnel-list</registration-watches> -->
     <!-- <write-watches> /NetworkTopology/Topology</write-watches> -->
+    <transaction-debug-context-enabled>true</transaction-debug-context-enabled>
 </config>
diff --git a/opendaylight/md-sal/mdsal-trace/cli/pom.xml b/opendaylight/md-sal/mdsal-trace/cli/pom.xml
new file mode 100644 (file)
index 0000000..c6fb184
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright © 2017 Red Hat 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.opendaylight.odlparent</groupId>
+    <artifactId>bundle-parent</artifactId>
+    <version>1.8.2-SNAPSHOT</version>
+    <relativePath />
+  </parent>
+
+  <groupId>org.opendaylight.controller</groupId>
+  <artifactId>mdsal-trace-cli</artifactId>
+  <version>1.5.2-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>mdsal-artifacts</artifactId>
+        <version>${project.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mdsal-trace-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.karaf.shell</groupId>
+      <artifactId>org.apache.karaf.shell.console</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>*</groupId>
+          <artifactId>*</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+           <!-- This bundle works with Karaf 3 and 4.0, see https://wiki.opendaylight.org/view/Karaf_4_migration#Karaf_CLI_commands -->
+           <Import-Package>
+             org.apache.karaf.shell.commands;version="[3.0.0,4.1)",
+             org.apache.karaf.shell.console;version="[3.0.0,4.1)",
+             *
+           </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <configuration>
+          <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
+        </configuration>
+      </plugin>
+      <plugin>
+       <groupId>org.codehaus.mojo</groupId>
+       <artifactId>findbugs-maven-plugin</artifactId>
+       <configuration>
+         <failOnError>true</failOnError>
+       </configuration>
+     </plugin>
+    </plugins>
+  </build>
+</project>
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 (file)
index 0000000..f7da921
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.md.sal.trace.api.TracingDOMDataBroker;
+
+/**
+ * Karaf CLI command to dump all open transactions.
+ *
+ * @author Michael Vorburger.ch
+ */
+@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 extends OsgiCommandSupport {
+
+    @Override
+    @SuppressWarnings("checkstyle:RegexpSingleLineJava")
+    public Object doExecute() throws Exception {
+        try {
+            TracingDOMDataBroker tracingDOMDataBroker = getService(TracingDOMDataBroker.class);
+            if (!tracingDOMDataBroker.printOpenTransactions(System.out)) {
+                System.out.println("No open transactions, great!");
+            }
+            return null;
+        } finally {
+            ungetServices();
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/mdsal-trace/cli/src/main/resources/org/opendaylight/blueprint/commands.xml b/opendaylight/md-sal/mdsal-trace/cli/src/main/resources/org/opendaylight/blueprint/commands.xml
new file mode 100644 (file)
index 0000000..ee78add
--- /dev/null
@@ -0,0 +1,9 @@
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.opendaylight.controller.md.sal.trace.cli.PrintOpenTransactionsCommand"/>
+        </command>
+    </command-bundle>
+
+</blueprint>
index b08e19ace7337d4a4d2e5c954845ba4e486a34e3..2150e28905a8cfbee60116324f1e6cb3aa11b652 100644 (file)
@@ -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;
  *
  * <p>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.
  *
  * <h1>Wiring:</h1>
  * 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
  *  <dependency>
  *    <groupId>org.opendaylight.controller</groupId>
- *    <artifactId>mdsal-trace-features</artifactId>
+ *    <artifactId>features-mdsal-trace</artifactId>
+ *    <version>1.7.0-SNAPSHOT</version>
  *    <classifier>features</classifier>
  *    <type>xml</type>
  *    <scope>runtime</scope>
- *    <version>0.1.6-SNAPSHOT</version>
  *  </dependency>
  * }
  * </pre>
  * </li>
  * <li>
- * 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 <a href="https://bugs.opendaylight.org/show_bug.cgi?id=9068">Bug 9068</a>
+ * you'll probably have to use feature:install's --no-auto-refresh flag when installing your "real" feature.
  * </li>
  * </ol>
  * 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<CloseTrackedRegistryReportEntry<TracingTransactionChain>>
+            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 <T extends CloseTracked<T>> void printRegistryOpenTransactions(
+            CloseTrackedRegistry<T> registry, PrintStream ps, String indent) {
+        Set<CloseTrackedRegistryReportEntry<T>> 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();
+        }
     }
 }
index ac6aaa8bdb3c94587413bf43931c05c4ab969a83..bb6503184376dc3ee528ffca1d7c663825f78a76 100644 (file)
@@ -30,10 +30,10 @@ class TracingTransactionChain extends AbstractCloseTracked<TracingTransactionCha
         this.tracingBroker = Objects.requireNonNull(tracingBroker);
 
         final boolean isDebug = transactionChainsRegistry.isDebugContextEnabled();
-        String pf = "TransactionChain_" + toString();
-        this.readOnlyTransactionsRegistry  = new CloseTrackedRegistry<>(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
index 0324986dbd3fa032973f2f45ac1974e7197563fd..0c4c6c57d1fb685a9c036b876cf2f2bf5b634ec7 100644 (file)
@@ -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);
index f1c126aeec4821e72cd490d1b51a76362cf2eef7..f746b018b6ec2dab5628a93c7308401a78c1bed9 100644 (file)
@@ -60,5 +60,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
       <groupId>${project.groupId}</groupId>
       <artifactId>mdsal-trace-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mdsal-trace-cli</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
index 3eb6d7b32515aa5d89aaf0b70fe5bf87754af49f..3c66e4d82466ffaeeef1a8231912e3ca256374d7 100644 (file)
@@ -19,6 +19,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <bundle>mvn:org.opendaylight.controller/mdsal-trace-api/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.controller/mdsal-trace-dom-impl/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.controller/mdsal-trace-binding-impl/{{VERSION}}</bundle>
+    <bundle>mvn:org.opendaylight.controller/mdsal-trace-cli/{{VERSION}}</bundle>
     <configfile finalname="etc/opendaylight/datastore/initial/config/mdsaltrace_config.xml">mvn:org.opendaylight.controller/mdsal-trace-api/{{VERSION}}/xml/config</configfile>
   </feature>
 </features>
index e0d260a9b912f88d9124b3128e6822dd8e926155..f27c592027202c0161ae25dc942deeaa37b004b4 100644 (file)
             <artifactId>mdsal-trace-binding-impl</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>mdsal-trace-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 </project>
index 573b4e89b27796fdc77c119ed786e01c60ed3ebb..56a581cf0657ba326ee1ccccbd5b6a2d1e41b0ad 100644 (file)
@@ -25,6 +25,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
     <module>api</module>
     <module>dom-impl</module>
     <module>binding-impl</module>
+    <module>cli</module>
     <module>features</module>
   </modules>
 
@@ -77,13 +78,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
             </execution>
             </executions>
           </plugin>
-          <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-checkstyle-plugin</artifactId>
-            <configuration>
-              <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
-            </configuration>
-          </plugin>
         </plugins>
       </build>
     </profile>