Bug 9060: Karaf CLI command to print open transactions 02/62402/8
authorMichael Vorburger <vorburger@redhat.com>
Tue, 29 Aug 2017 12:22:59 +0000 (14:22 +0200)
committerTom Pantelis <tompantelis@gmail.com>
Wed, 30 Aug 2017 15:56:44 +0000 (15:56 +0000)
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>
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/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/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;
 
 /**
 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 {
 
  */
 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> -->
     <!-- <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>
 </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..7ae7b58
--- /dev/null
@@ -0,0 +1,84 @@
+<?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>2.0.4</version>
+    <relativePath />
+  </parent>
+
+  <groupId>org.opendaylight.controller</groupId>
+  <artifactId>mdsal-trace-cli</artifactId>
+  <version>1.7.0-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.core</artifactId>
+      <!-- TODO remove <version> once https://git.opendaylight.org/gerrit/#/c/62397/ is merged-->
+      <version>4.0.9</version>
+      <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>
+            <Karaf-Commands>*</Karaf-Commands>
+          </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..7f26f0d
--- /dev/null
@@ -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;
+    }
+
+}
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.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 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.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.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;
 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
  *
  * <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
  *
  * <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>
  * {@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>
  *    <classifier>features</classifier>
  *    <type>xml</type>
  *    <scope>runtime</scope>
- *    <version>0.1.6-SNAPSHOT</version>
  *  </dependency>
  * }
  * </pre>
  * </li>
  * <li>
  *  </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
  * </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;
         }
         } 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) {
     }
 
     private void configure(Config config) {
@@ -357,6 +363,7 @@ public class TracingBroker implements TracingDOMDataBroker {
         return res;
     }
 
         return res;
     }
 
+    @Override
     public boolean printOpenTransactions(PrintStream ps) {
         if (transactionChainsRegistry.getAllUnique().isEmpty()
             && readOnlyTransactionsRegistry.getAllUnique().isEmpty()
     public boolean printOpenTransactions(PrintStream ps) {
         if (transactionChainsRegistry.getAllUnique().isEmpty()
             && readOnlyTransactionsRegistry.getAllUnique().isEmpty()
@@ -366,37 +373,50 @@ public class TracingBroker implements TracingDOMDataBroker {
             return false;
         }
 
             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();
         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
 
         // 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();
             @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;
     }
 
         });
         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:");
             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();
         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
     }
 
     @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);
 
         BindingNormalizedNodeSerializer codec = mock(BindingNormalizedNodeSerializer.class);
         TracingBroker tracingBroker = new TracingBroker(domDataBroker, config, codec);
 
+        DOMDataReadWriteTransaction tx = tracingBroker.newReadWriteTransaction();
         DOMTransactionChain txChain = tracingBroker.createTransactionChain(null);
         DOMTransactionChain txChain = tracingBroker.createTransactionChain(null);
-        DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+        DOMDataReadWriteTransaction txFromChain = txChain.newReadWriteTransaction();
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         PrintStream ps = new PrintStream(baos);
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         PrintStream ps = new PrintStream(baos);
index 83654dc5518be8a3d87fe34953c6d20414e26b59..a01b9e91aecabdfb40ae1021dee8c4ce6e790d70 100644 (file)
             <artifactId>mdsal-trace-binding-impl</artifactId>
             <version>${project.version}</version>
         </dependency>
             <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>
     </dependencies>
 </project>
index 5e66f8a0c9f6c198fa06cb5d30e5d0b3320888e9..f05f9964aa0726c2d1d5a3a688b65fc3cdaf1b9b 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>api</module>
     <module>dom-impl</module>
     <module>binding-impl</module>
+    <module>cli</module>
     <module>features</module>
   </modules>
 
     <module>features</module>
   </modules>
 
@@ -77,13 +78,6 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
             </execution>
             </executions>
           </plugin>
             </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>
         </plugins>
       </build>
     </profile>