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>.