Author: gnodet
Date: Mon Dec 16 16:21:13 2013
New Revision: 1551253

URL: http://svn.apache.org/r1551253
Log:
[KARAF-2626] Add load-test and threads commands

Added:
    
karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/LoadTest.java
    
karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/Threads.java
Modified:
    
karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml
    karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/bundle.info

Added: 
karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/LoadTest.java
URL: 
http://svn.apache.org/viewvc/karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/LoadTest.java?rev=1551253&view=auto
==============================================================================
--- 
karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/LoadTest.java
 (added)
+++ 
karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/LoadTest.java
 Mon Dec 16 16:21:13 2013
@@ -0,0 +1,173 @@
+/*
+ * 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.karaf.shell.dev;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jline.console.ConsoleReader;
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Option;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+@Command(scope = "dev", name = "load-test", description = "Load test bundle 
lifecycle")
+public class LoadTest extends OsgiCommandSupport {
+
+    @Option(name = "--threads", description = "number of concurrent threads")
+    int threads = 2;
+
+    @Option(name = "--delay", description = "maximum delay between actions")
+    int delay = 1;
+
+    @Option(name = "--iterations", description = "number of iterations per 
thread")
+    int iterations = 100;
+
+    @Option(name = "--refresh", description = "percentage of bundle refresh vs 
restart")
+    int refresh = 20;
+
+    @Option(name = "--excludes", description = "List of bundles (ids or 
symbolic names) to exclude")
+    List<String> excludes = Arrays.asList("0", "org.ops4j.pax.url.mvn", 
"org.ops4j.pax.logging.pax-logging-api", 
"org.ops4j.pax.logging.pax-logging-service");
+
+    @Override
+    protected Object doExecute() throws Exception {
+        if (!confirm(session)) {
+            return null;
+        }
+        final BundleContext bundleContext = 
getBundleContext().getBundle(0).getBundleContext();
+        final FrameworkWiring wiring = 
bundleContext.getBundle().adapt(FrameworkWiring.class);
+        final CountDownLatch latch = new CountDownLatch(threads);
+        final Bundle[] bundles = bundleContext.getBundles();
+        final AtomicBoolean[] locks = new AtomicBoolean[bundles.length];
+        for (int b = 0; b < locks.length; b++) {
+            locks[b] = new AtomicBoolean(true);
+            // Avoid touching excluded bundles
+            if (excludes.contains(Long.toString(bundles[b].getBundleId()))
+                    || excludes.contains(bundles[b].getSymbolicName())) {
+                continue;
+            }
+            // Only touch active bundles
+            if (bundles[b].getState() != Bundle.ACTIVE) {
+                continue;
+            }
+            // Now set the lock to available
+            locks[b].set(false);
+        }
+        for (int i = 0; i < threads; i++) {
+            new Thread() {
+                public void run() {
+                    try {
+                        Random rand = new Random();
+                        for (int j = 0; j < iterations; j++) {
+                            for (;;) {
+                                int b = rand.nextInt(bundles.length);
+                                if (locks[b].compareAndSet(false, true)) {
+                                    try {
+                                        // Only touch active bundles
+                                        if (bundles[b].getState() != 
Bundle.ACTIVE) {
+                                            continue;
+                                        }
+                                        if (rand.nextInt(100) < refresh) {
+                                            try {
+                                                bundles[b].update();
+                                                final CountDownLatch latch = 
new CountDownLatch(1);
+                                                
wiring.refreshBundles(Collections.singletonList(bundles[b]), new 
FrameworkListener() {
+                                                    public void 
frameworkEvent(FrameworkEvent event) {
+                                                        latch.countDown();
+                                                    }
+                                                });
+                                                latch.await();
+                                            } finally {
+                                                while (true) {
+                                                    try {
+                                                        
bundles[b].start(Bundle.START_TRANSIENT);
+                                                        break;
+                                                    } catch (Exception e) {
+                                                        Thread.sleep(1);
+                                                    }
+                                                }
+                                            }
+                                        } else {
+                                            try {
+                                                
bundles[b].stop(Bundle.STOP_TRANSIENT);
+                                            } finally {
+                                                while (true) {
+                                                    try {
+                                                        
bundles[b].start(Bundle.START_TRANSIENT);
+                                                        break;
+                                                    } catch (Exception e) {
+                                                        Thread.sleep(1);
+                                                    }
+                                                }
+                                            }
+                                        }
+                                        Thread.sleep(rand.nextInt(delay));
+                                    } catch (Exception e) {
+                                        e.printStackTrace();
+                                    } finally {
+                                        locks[b].set(false);
+                                    }
+                                }
+                                break;
+                            }
+                        }
+                    } catch (Throwable t) {
+                        t.printStackTrace();
+                    } finally {
+                        latch.countDown();
+                    }
+                }
+            }.start();
+        }
+        new Thread() {
+            @Override
+            public void run() {
+                try {
+                    latch.await();
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                System.err.println("Load test finished");
+            }
+        }.start();
+        return null;
+    }
+
+    private boolean confirm(CommandSession session) throws IOException {
+        for (;;) {
+            ConsoleReader reader = (ConsoleReader) 
session.get(".jline.reader");
+            String msg = "You are about to perform a start/stop/refresh load 
test on bundles.\nDo you wish to continue (yes/no): ";
+            String str = reader.readLine(msg);
+            if ("yes".equalsIgnoreCase(str)) {
+                return true;
+            }
+            if ("no".equalsIgnoreCase(str)) {
+                return false;
+            }
+        }
+    }
+}

Added: 
karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/Threads.java
URL: 
http://svn.apache.org/viewvc/karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/Threads.java?rev=1551253&view=auto
==============================================================================
--- 
karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/Threads.java
 (added)
+++ 
karaf/branches/karaf-2.x/shell/dev/src/main/java/org/apache/karaf/shell/dev/Threads.java
 Mon Dec 16 16:21:13 2013
@@ -0,0 +1,318 @@
+/*
+ * 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.karaf.shell.dev;
+
+import java.lang.management.LockInfo;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Option;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+
+@Command(scope = "dev", name = "threads", description = "Show threads in the 
JVM.")
+public class Threads extends OsgiCommandSupport {
+
+    @Option(name = "--tree" , description = "Display threads as a tree")
+    private boolean tree = false;
+
+    @Option(name = "--list" , description = "Display threads as a list")
+    private boolean list = false;
+
+    @Option(name = "-e", aliases = { "--empty-groups" }, description = "Show 
empty groups")
+    private boolean empty = false;
+
+    @Option(name = "-t", aliases = { "--threshold" }, description = "Minimal 
number of interesting stack trace line to display a thread")
+    private int threshold = 1;
+
+    @Option(name = "--locks", description = "Display locks")
+    private boolean locks = false;
+
+    @Option(name = "--monitors", description = "Display monitors")
+    private boolean monitors = false;
+
+    @Option(name = "--packages", description = "Pruned packages")
+    private List<String> packages = Arrays.asList("java.", "sun.");
+
+    @Argument(name = "id", description = "Show details for thread with this 
Id", required = false, multiValued = false)
+    private Long id;
+
+    @Override
+    protected Object doExecute() throws Exception {
+        Map<Long, ThreadInfo> threadInfos = new TreeMap<Long, ThreadInfo>();
+        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+        ThreadInfo[] infos;
+        if (threadMXBean.isObjectMonitorUsageSupported() && 
threadMXBean.isSynchronizerUsageSupported()) {
+            infos = threadMXBean.dumpAllThreads(true, true);
+        } else {
+            infos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 
Integer.MAX_VALUE);
+        }
+        for (ThreadInfo info : infos) {
+            threadInfos.put(info.getThreadId(), info);
+        }
+
+        if (id != null) {
+            ThreadInfo ti = threadInfos.get(id);
+            if (ti != null) {
+                System.out.println("Thread " + ti.getThreadId() + " " + 
ti.getThreadName() + " " + ti.getThreadState());
+                System.out.println("Stacktrace:");
+                StackTraceElement[] st = ti.getStackTrace();
+                for (StackTraceElement ste : st) {
+                    System.out.println(ste.getClassName() + "." + 
ste.getMethodName() + " line: " + ste.getLineNumber());
+                }
+            }
+        } else if (list) {
+            System.out.println("   ID   State           CPU time   Usr Time  
Name");
+            for (ThreadInfo thread : threadInfos.values()) {
+                String id = String.valueOf(thread.getThreadId());
+                while (id.length() < 4) {
+                    id = " " + id;
+                }
+                String state = thread.getThreadState().toString();
+                while (state.length() < 13) {
+                    state = state + " ";
+                }
+                String cpu = 
String.valueOf(threadMXBean.getThreadCpuTime(thread.getThreadId()) / 1000000);
+                while (cpu.length() < 8) {
+                    cpu = " " + cpu;
+                }
+                String user = 
String.valueOf(threadMXBean.getThreadUserTime(thread.getThreadId()) / 1000000);
+                while (user.length() < 8) {
+                    user = " " + user;
+                }
+                System.out.println("[" + id + "] [" + state + "] [" + cpu + "] 
[" + user + "] " + thread.getThreadName());
+            }
+        } else {
+            ThreadGroup group = Thread.currentThread().getThreadGroup();
+            while (group.getParent() != null) {
+                group = group.getParent();
+            }
+            ThreadGroupData data = new ThreadGroupData(group, threadInfos);
+            data.print();
+        }
+
+        return null;
+    }
+
+    public class ThreadGroupData {
+        private final ThreadGroup group;
+        private final List<ThreadGroupData> groups;
+        private final List<ThreadData> threads;
+
+        public ThreadGroupData(ThreadGroup group, Map<Long, ThreadInfo> infos) 
{
+            this.group = group;
+            int nbGroups;
+            int nbThreads;
+            ThreadGroup[] childGroups = new ThreadGroup[32];
+            while (true) {
+                nbGroups = group.enumerate(childGroups, false);
+                if (nbGroups == childGroups.length) {
+                    childGroups = new ThreadGroup[childGroups.length * 2];
+                } else {
+                    break;
+                }
+            }
+            groups = new ArrayList<ThreadGroupData>();
+            for (ThreadGroup tg : childGroups) {
+                if (tg != null) {
+                    groups.add(new ThreadGroupData(tg, infos));
+                }
+            }
+            Thread[] childThreads = new Thread[32];
+            while (true) {
+                nbThreads = group.enumerate(childThreads, false);
+                if (nbThreads == childThreads.length) {
+                    childThreads = new Thread[childThreads.length * 2];
+                } else {
+                    break;
+                }
+            }
+            threads = new ArrayList<ThreadData>();
+            for (Thread t : childThreads) {
+                if (t != null) {
+                    threads.add(new ThreadData(t, infos.get(t.getId())));
+                }
+            }
+        }
+
+        public void print() {
+            if (tree) {
+                printTree("");
+            } else {
+                printDump("");
+            }
+        }
+
+        private void printTree(String indent) {
+            if (empty || hasInterestingThreads()) {
+                System.out.println(indent + "Thread Group \"" + 
group.getName() + "\"");
+                for (ThreadGroupData tgd : groups) {
+                    tgd.printTree(indent + "    ");
+                }
+                for (ThreadData td : threads) {
+                    if (td.isInteresting()) {
+                        td.printTree(indent + "    ");
+                    }
+                }
+            }
+        }
+
+        private void printDump(String indent) {
+            if (empty || hasInterestingThreads()) {
+                for (ThreadGroupData tgd : groups) {
+                    tgd.printDump(indent);
+                }
+                for (ThreadData td : threads) {
+                    if (td.isInteresting()) {
+                        td.printDump(indent);
+                    }
+                }
+            }
+        }
+
+        public boolean hasInterestingThreads() {
+            for (ThreadData td : threads) {
+                if (td.isInteresting()) {
+                    return true;
+                }
+            }
+            for (ThreadGroupData tgd : groups) {
+                if (tgd.hasInterestingThreads()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    public class ThreadData {
+        private final Thread thread;
+        private ThreadInfo info;
+
+        public ThreadData(Thread thread, ThreadInfo info) {
+            this.thread = thread;
+            this.info = info;
+        }
+
+        public void printTree(String indent) {
+            System.out.println(indent + "    " + "\"" + thread.getName() + 
"\": " + thread.getState());
+        }
+
+        public void printDump(String indent) {
+            if (info != null && isInteresting()) {
+                printThreadInfo("    ");
+                if (locks) {
+                    printLockInfo("    ");
+                }
+                if (monitors) {
+                    printMonitorInfo("    ");
+                }
+            }
+        }
+
+        public boolean isInteresting() {
+            int nb = 0;
+            StackTraceElement[] stacktrace = info.getStackTrace();
+            for (int i = 0; i < stacktrace.length; i++) {
+                StackTraceElement ste = stacktrace[i];
+                boolean interestingLine = true;
+                for (String pkg : packages) {
+                    if (ste.getClassName().startsWith(pkg)) {
+                        interestingLine = false;
+                        break;
+                    }
+                }
+                if (interestingLine) {
+                    nb++;
+                }
+            }
+            return nb >= threshold;
+        }
+
+        private void printThreadInfo(String indent) {
+            // print thread information
+            printThread(indent);
+
+            // print stack trace with locks
+            StackTraceElement[] stacktrace = info.getStackTrace();
+            MonitorInfo[] monitors = info.getLockedMonitors();
+            for (int i = 0; i < stacktrace.length; i++) {
+                StackTraceElement ste = stacktrace[i];
+                System.out.println(indent + "at " + ste.toString());
+                for (MonitorInfo mi : monitors) {
+                    if (mi.getLockedStackDepth() == i) {
+                        System.out.println(indent + "  - locked " + mi);
+                    }
+                }
+            }
+            System.out.println();
+        }
+
+        private void printThread(String indent) {
+            StringBuilder sb = new StringBuilder("\"" + info.getThreadName() + 
"\"" + " Id="
+                    + info.getThreadId() + " in " + info.getThreadState());
+            if (info.getLockName() != null) {
+                sb.append(" on lock=" + info.getLockName());
+            }
+            if (info.isSuspended()) {
+                sb.append(" (suspended)");
+            }
+            if (info.isInNative()) {
+                sb.append(" (running in native)");
+            }
+            System.out.println(sb.toString());
+            if (info.getLockOwnerName() != null) {
+                System.out.println(indent + " owned by " + 
info.getLockOwnerName() + " Id="
+                        + info.getLockOwnerId());
+            }
+        }
+
+        private void printMonitorInfo(String indent) {
+            MonitorInfo[] monitors = info.getLockedMonitors();
+            if (monitors != null && monitors.length > 0) {
+                System.out.println(indent + "Locked monitors: count = " + 
monitors.length);
+                for (MonitorInfo mi : monitors) {
+                    System.out.println(indent + "  - " + mi + " locked at ");
+                    System.out.println(indent + "      " + 
mi.getLockedStackDepth() + " "
+                            + mi.getLockedStackFrame());
+                }
+                System.out.println();
+            }
+        }
+
+        private void printLockInfo(String indent) {
+            LockInfo[] locks = info.getLockedSynchronizers();
+            if (locks != null && locks.length > 0) {
+                System.out.println(indent + "Locked synchronizers: count = " + 
locks.length);
+                for (LockInfo li : locks) {
+                    System.out.println(indent + "  - " + li);
+                }
+                System.out.println();
+            }
+        }
+
+    }
+
+}

Modified: 
karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml
URL: 
http://svn.apache.org/viewvc/karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml?rev=1551253&r1=1551252&r2=1551253&view=diff
==============================================================================
--- 
karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml
 (original)
+++ 
karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/blueprint/shell-dev.xml
 Mon Dec 16 16:21:13 2013
@@ -46,6 +46,12 @@
                 <property name="watcher" ref="watcher"/>
             </action>
         </command>
+        <command name="dev/load-test">
+            <action class="org.apache.karaf.shell.dev.LoadTest" />
+        </command>
+        <command name="dev/threads">
+            <action class="org.apache.karaf.shell.dev.Threads" />
+        </command>
     </command-bundle>
 
     <!-- Referenace to the Configuration Admin Service -->

Modified: 
karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/bundle.info
URL: 
http://svn.apache.org/viewvc/karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/bundle.info?rev=1551253&r1=1551252&r2=1551253&view=diff
==============================================================================
--- karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/bundle.info 
(original)
+++ karaf/branches/karaf-2.x/shell/dev/src/main/resources/OSGI-INF/bundle.info 
Mon Dec 16 16:21:13 2013
@@ -19,6 +19,8 @@
     \u001B[36mdev:restart\u001B[0m Restart Karaf.
     \u001B[36mdev:show-tree\u001B[0m Shows the tree of bundles based on the 
wiring information.
     \u001B[36mdev:watch\u001B[0m Watches and updates bundles.
+    \u001B[36mdev:load-test\u001B[0m Load test bundle lifecycle.
+    \u001B[36mdev:threads\u001B[0m Show threads in the JVM.
 
 \u001B[1mSEE ALSO\u001B[0m
     \u001B[36mCommands\u001B[0m section of the Karaf User Guide.
\ No newline at end of file


Reply via email to