You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:19:05 UTC

[sling-org-apache-sling-commons-threaddump] 01/14: SLING-3466 move Thread Dumper to bundles/commons

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-threaddump.git

commit 1a149235ae0fb5115b43ed9b739dfad6c04e871b
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Sun Mar 23 14:27:19 2014 +0000

    SLING-3466 move Thread Dumper to bundles/commons
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1580506 13f79535-47bb-0310-9956-ffa450edef68
---
 README.txt                                         |  27 ++++
 pom.xml                                            | 105 ++++++++++++++
 .../commons/threaddump/internal/Activator.java     | 122 ++++++++++++++++
 .../threaddump/internal/BaseThreadDumper.java      | 155 +++++++++++++++++++++
 .../threaddump/internal/ThreadDumpCommand.java     |  92 ++++++++++++
 .../threaddump/internal/ThreadDumperPanel.java     |  33 +++++
 6 files changed, 534 insertions(+)

diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..be2025f
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,27 @@
+Apache Sling Thread Dumper
+
+Provides a plugin for the Apache Felix Web Console to dump Threads.
+
+Getting Started
+===============
+
+This component uses a Maven 2 (http://maven.apache.org/) build
+environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
+2.0.7 or later. We recommend to use the latest Maven version.
+
+If you have Maven 2 installed, you can compile and
+package the jar using the following command:
+
+    mvn package
+
+See the Maven 2 documentation for other build features.
+
+The latest source code for this component is available in the
+Subversion (http://subversion.tigris.org/) source repository of
+the Apache Software Foundation. If you have Subversion installed,
+you can checkout the latest source using the following command:
+
+    svn checkout http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/threaddump
+
+See the Subversion documentation for other source control features.
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d9bf674
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<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/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>18</version>
+        <relativePath>../../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.sling.commons.threaddump</artifactId>
+    <packaging>bundle</packaging>
+    <version>0.2.3-SNAPSHOT</version>
+
+    <name>Apache Sling Thread Dumper</name>
+    <description>
+        Plugin providing plugins to the Felix Shell and Web Console to
+        have the current threads with the stack traces dumped.
+    </description>
+
+    <scm>
+        <connection>
+            scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/threaddump
+        </connection>
+        <developerConnection>
+            scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/threaddump
+        </developerConnection>
+        <url>
+            http://svn.apache.org/viewvc/sling/trunk/bundles/commons/threaddump
+        </url>
+    </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            !org.apache.felix.shell,
+                            *
+                        </Import-Package>
+                        <DynamicImport-Package>
+                            org.apache.felix.shell;version="[1.0,2)"
+                        </DynamicImport-Package>
+                        <Private-Package>
+                            org.apache.sling.commons.threaddump.*
+                        </Private-Package>
+                        <Bundle-Activator>
+                            org.apache.sling.commons.threaddump.internal.Activator
+                        </Bundle-Activator>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.shell</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/commons/threaddump/internal/Activator.java b/src/main/java/org/apache/sling/commons/threaddump/internal/Activator.java
new file mode 100644
index 0000000..2141c55
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/threaddump/internal/Activator.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.commons.threaddump.internal;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator implements BundleActivator, UncaughtExceptionHandler {
+
+    private static final String LOG_NAME = "org.apache.sling.commons.threaddump.internal.UncaughtExceptionHandler";
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(LOG_NAME);
+
+    private UncaughtExceptionHandler oldHandler;
+
+    public void start(BundleContext bundleContext) {
+
+        // install handler for uncaught exceptions
+        oldHandler = Thread.getDefaultUncaughtExceptionHandler();
+        Thread.setDefaultUncaughtExceptionHandler(this);
+
+        // install thread handler shell command
+        register(bundleContext,
+            new String[] { "org.apache.felix.shell.Command" },
+            new ServiceFactory() {
+
+                public void ungetService(final Bundle bundle,
+                    final ServiceRegistration reg,
+                    final Object consoleObject) {
+                    // nothing to do
+                }
+
+                public Object getService(final Bundle bundle,
+                    final ServiceRegistration reg) {
+                     return new ThreadDumpCommand();
+                }
+            }, null);
+
+        // install Web Console configuration printer
+        final Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put("felix.webconsole.label", "slingthreads");
+        props.put("felix.webconsole.title", "Threads");
+        props.put("felix.webconsole.configprinter.modes", "always");
+
+        final ThreadDumperPanel tdp = new ThreadDumperPanel();
+
+        register(bundleContext, new String[] {
+            tdp.getClass().getName() }, tdp, props);
+    }
+
+    public void stop(BundleContext bundleContext) {
+        Thread.setDefaultUncaughtExceptionHandler(oldHandler);
+    }
+
+    private void register(final BundleContext context,
+            final String[] serviceNames,
+            final Object service,
+            final Dictionary<String, Object> properties) {
+
+        final Dictionary<String, Object> props =
+                (properties == null ? new Hashtable<String, Object>() : properties);
+
+        // default settings
+        props.put(Constants.SERVICE_DESCRIPTION, "Thread Dumper ("
+            + serviceNames[0] + ")");
+        props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
+
+        context.registerService(serviceNames, service, props);
+    }
+
+    // ---------- UncaughtExceptionHandler
+
+    /**
+     * Logs the uncaught exception for the thread at level ERROR and chains to
+     * the old handler, which was installed before this handler has been
+     * installed.
+     *
+     * @param t The <code>Thread</code> which got the exception but did not
+     *            handle it.
+     * @param e The uncaught <code>Throwable</code> causing the thread to die.
+     */
+    public void uncaughtException(Thread t, Throwable e) {
+        if (e instanceof ThreadDeath) {
+            log.error("Thread " + t + " has just been killed", e);
+        } else {
+            log.error("Uncaught exception in Thread " + t, e);
+        }
+
+        // chain to original handler
+        if (oldHandler != null) {
+            oldHandler.uncaughtException(t, e);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/commons/threaddump/internal/BaseThreadDumper.java b/src/main/java/org/apache/sling/commons/threaddump/internal/BaseThreadDumper.java
new file mode 100644
index 0000000..33bc41e
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/threaddump/internal/BaseThreadDumper.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.commons.threaddump.internal;
+
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+class BaseThreadDumper {
+
+    boolean printThread(PrintWriter pw, long threadId, boolean withStackTrace) {
+        // first get the root thread group
+        ThreadGroup rootGroup = getRootThreadGroup();
+        int numThreads = rootGroup.activeCount();
+        Thread[] threads = new Thread[numThreads * 2];
+        rootGroup.enumerate(threads);
+
+        for (Thread thread : threads) {
+            if (thread != null && thread.getId() == threadId) {
+                printThread(pw, thread, withStackTrace);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    void printThreads(PrintWriter pw, boolean withStackTrace) {
+        // first get the root thread group
+        ThreadGroup rootGroup = getRootThreadGroup();
+
+        printThreadGroup(pw, rootGroup, withStackTrace);
+
+        int numGroups = rootGroup.activeGroupCount();
+        ThreadGroup[] groups = new ThreadGroup[2 * numGroups];
+        rootGroup.enumerate(groups);
+        for (int i = 0; i < groups.length; i++) {
+            printThreadGroup(pw, groups[i], withStackTrace);
+        }
+
+        pw.println();
+    }
+
+    private ThreadGroup getRootThreadGroup() {
+        ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
+        while (rootGroup.getParent() != null) {
+            rootGroup = rootGroup.getParent();
+        }
+        return rootGroup;
+    }
+
+    private void printThreadGroup(PrintWriter pw, ThreadGroup group, boolean withStackTrace) {
+        if (group != null) {
+            StringBuffer info = new StringBuffer();
+            info.append("ThreadGroup ").append(group.getName());
+            info.append(" [");
+            info.append("maxprio=").append(group.getMaxPriority());
+
+            info.append(", parent=");
+            if (group.getParent() != null) {
+                info.append(group.getParent().getName());
+            } else {
+                info.append('-');
+            }
+
+            info.append(", isDaemon=").append(group.isDaemon());
+            info.append(", isDestroyed=").append(group.isDestroyed());
+            info.append(']');
+
+            pw.println(info);
+
+            int numThreads = group.activeCount();
+            Thread[] threads = new Thread[numThreads * 2];
+            group.enumerate(threads, false);
+            for (int i = 0; i < threads.length; i++) {
+                printThread(pw, threads[i], withStackTrace);
+            }
+
+            pw.println();
+        }
+    }
+
+    private void printThread(PrintWriter pw, Thread thread, boolean withStackTrace) {
+        if (thread != null) {
+            StringBuffer info = new StringBuffer();
+            info.append("  Thread ").append(thread.getId());
+            info.append('/').append(thread.getName());
+            info.append(" [");
+            info.append("priority=").append(thread.getPriority());
+            info.append(", alive=").append(thread.isAlive());
+            info.append(", daemon=").append(thread.isDaemon());
+            info.append(", interrupted=").append(thread.isInterrupted());
+            info.append(", loader=").append(thread.getContextClassLoader());
+            info.append(']');
+
+            pw.println(info);
+
+            if (withStackTrace) {
+                printClassLoader(pw, thread.getContextClassLoader());
+                printStackTrace(pw, thread.getStackTrace());
+                pw.println();
+            }
+        }
+    }
+
+    private void printClassLoader(PrintWriter pw, ClassLoader classLoader) {
+        if (classLoader != null) {
+            pw.print("    ClassLoader=");
+            pw.println(classLoader);
+            pw.print("      Parent=");
+            pw.println(classLoader.getParent());
+
+            if (classLoader instanceof URLClassLoader) {
+                URLClassLoader loader = (URLClassLoader) classLoader;
+                URL[] urls = loader.getURLs();
+                if (urls != null && urls.length > 0) {
+                    for (int i = 0; i < urls.length; i++) {
+                        pw.print("      ");
+                        pw.print(i);
+                        pw.print(" - ");
+                        pw.println(urls[i]);
+                    }
+                }
+            }
+        }
+    }
+
+    private void printStackTrace(PrintWriter pw, StackTraceElement[] stackTrace) {
+        pw.println("    Stacktrace");
+        if (stackTrace == null || stackTrace.length == 0) {
+            pw.println("      -");
+        } else {
+            for (StackTraceElement stackTraceElement : stackTrace) {
+                pw.print("      ");
+                pw.println(stackTraceElement);
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/commons/threaddump/internal/ThreadDumpCommand.java b/src/main/java/org/apache/sling/commons/threaddump/internal/ThreadDumpCommand.java
new file mode 100644
index 0000000..763514c
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/threaddump/internal/ThreadDumpCommand.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.commons.threaddump.internal;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+
+import org.apache.felix.shell.Command;
+
+public class ThreadDumpCommand extends BaseThreadDumper implements Command {
+
+    private static final String CMD_NAME = "threads";
+
+    private static final String OPT_STACK = "-s";
+
+    public String getName() {
+        return CMD_NAME;
+    }
+
+    public String getShortDescription() {
+        return "dumps the JVM threads";
+    }
+
+    public String getUsage() {
+        return CMD_NAME + " [" + OPT_STACK + "] <id> ...";
+    }
+
+    public void execute(String command, PrintStream out, PrintStream err) {
+
+        // cut off leading command name
+        if (command.startsWith(CMD_NAME)) {
+            command = command.substring(CMD_NAME.length());
+        }
+
+        boolean longListing = false;
+        LinkedList<Long> threadIds = new LinkedList<Long>();
+
+        StringTokenizer tokener = new StringTokenizer(command, ", \t");
+        while (tokener.hasMoreTokens()) {
+            String token = tokener.nextToken().trim();
+            if (OPT_STACK.equals(token)) {
+                longListing = true;
+            } else {
+                try {
+                    long threadId = Long.parseLong(token);
+                    threadIds.add(threadId);
+                } catch (NumberFormatException nfe) {
+                    noSuchThread(err, token);
+                }
+            }
+        }
+
+        PrintWriter pw = new PrintWriter(out);
+
+        if (threadIds.isEmpty()) {
+            printThreads(pw, longListing);
+        } else {
+            while (!threadIds.isEmpty()) {
+                Long threadId = threadIds.removeFirst();
+                if (!printThread(pw, threadId, longListing)) {
+                    noSuchThread(err, threadId);
+                }
+            }
+        }
+
+        pw.flush();
+    }
+
+    private void noSuchThread(PrintStream err, Object threadId) {
+        err.println("No such Thread: " + threadId);
+        err.flush();
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/commons/threaddump/internal/ThreadDumperPanel.java b/src/main/java/org/apache/sling/commons/threaddump/internal/ThreadDumperPanel.java
new file mode 100644
index 0000000..5a12d43
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/threaddump/internal/ThreadDumperPanel.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.commons.threaddump.internal;
+
+import java.io.PrintWriter;
+
+public class ThreadDumperPanel {
+
+    private final BaseThreadDumper baseThreadDumper = new BaseThreadDumper();
+
+    // ---------- ConfigurationPrinter
+
+    public void printConfiguration(final PrintWriter pw) {
+        pw.println("*** Threads Dumps:");
+        baseThreadDumper.printThreads(pw, true);
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.