commit 119736b42c32510ebe0f9e6b37136b218027d2ab
Author: James Livingston <jlivings@redhat.com>
Date:   Wed May 9 10:10:41 2012 +1000

    support tracing stacks of other threads

diff --git a/agent/src/main/java/org/jboss/byteman/rule/helper/Helper.java b/agent/src/main/java/org/jboss/byteman/rule/helper/Helper.java
index e3b9fd5..4514acb 100644
--- a/agent/src/main/java/org/jboss/byteman/rule/helper/Helper.java
+++ b/agent/src/main/java/org/jboss/byteman/rule/helper/Helper.java
@@ -1288,7 +1288,7 @@ public class Helper
     }
 
     /**
-     * print a stack trace to System.out by calling traceStack(prefix, key, 0)
+     * print a stack trace to the trace stream identified by key by calling traceStack(prefix, key, 0)
      */
     public void traceStack(String prefix, Object key)
     {
@@ -1325,6 +1325,119 @@ public class Helper
         trace(key, stackTrace);
     }
 
+    // tracing frames of all stacks
+
+    /**
+     * print trace of all threads' stacks to System.out by calling traceAllStacks(null)
+     */
+    public void traceAllStacks()
+    {
+    	traceAllStacks(null);
+    }
+
+    /**
+     * print trace of all threads' stacks to System.out by calling traceAllStacks(prefix, "out")
+     */
+    public void traceAllStacks(String prefix)
+    {
+    	traceAllStacks(prefix, "out");
+    }
+
+    /**
+     * print trace of all threads' stacks to the trace stream identified by key by calling traceAllStacks(prefix, key, 0)
+     */
+    public void traceAllStacks(String prefix, Object key)
+    {
+    	traceAllStacks(prefix, key, 0);
+    }
+
+    /**
+     * print trace of all threads' stacks to System.out by calling traceAllStacks(null, maxFrames)
+     */
+    public void traceAllStacks(int maxFrames)
+    {
+    	traceAllStacks(null, maxFrames);
+    }
+
+    /**
+     * print trace of all threads' stacks to System.out by calling traceAllStacks(prefix, "out", maxFrames)
+     */
+    public void traceAllStacks(String prefix, int maxFrames)
+    {
+    	traceAllStacks(prefix, "out", maxFrames);
+    }
+
+    /**
+     * print trace of all threads' stacks to the trace stream identified by key
+     *
+     * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null
+     * then the prefix "Stack trace for thread " + Thread.currentThread().getName() + "\n" is used
+     * @param key an object identifying the trace stream to which output should be generated
+     * @param maxFrames the maximum number of frames to print or 0 if no limit should apply
+     */
+    public void traceAllStacks(String prefix, Object key, int maxFrames)
+    {
+    	trace(key, formatAllStacks(prefix, maxFrames));
+    }
+    
+    
+    // trace stack of a specific thread
+
+    /**
+     * print a stack trace of a specific thread to System.out by calling traceThreadStack(threadName, null)
+     */
+    public void traceThreadStack(String threadName)
+    {
+    	traceThreadStack(threadName, null);
+    }
+
+    /**
+     * print a stack trace of a specific thread to System.out by calling traceThreadStack(threadName, prefix, "out")
+     */
+    public void traceThreadStack(String threadName, String prefix)
+    {
+    	traceThreadStack(threadName, prefix, "out");
+    }
+
+    /**
+     * print a stack trace of a specific thread to the trace stream identified by key by calling traceThreadStack(threadName, prefix, key, 0)
+     */
+    public void traceThreadStack(String threadName, String prefix, Object key)
+    {
+    	traceThreadStack(threadName, prefix, key, 0);
+    }
+
+    /**
+     * print a stack trace of a specific thread to System.out by calling traceThreadStack(threadName, null, maxFrames)
+     */
+    public void traceThreadStack(String threadName, int maxFrames)
+    {
+    	traceThreadStack(threadName, null, maxFrames);
+    }
+
+    /**
+     * print a stack trace of a specific thread of a specific thread to System.out by calling traceThreadStack(threadName, prefix, "out", maxFrames)
+     */
+    public void traceThreadStack(String threadName, String prefix, int maxFrames)
+    {
+    	traceThreadStack(threadName, prefix, "out", maxFrames);
+    }
+
+    /**
+     * print a stack trace to the trace stream identified by key
+     *
+     * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null
+     * then the prefix "Stack trace for thread " + threadName + "\n" is used
+     * @param key an object identifying the trace stream to which output should be generated
+     * @param maxFrames the maximum number of frames to print or 0 if no limit should apply
+     */
+    public void traceThreadStack(String threadName, String prefix, Object key, int maxFrames)
+    {
+        String stackTrace = formatThreadStack(threadName, prefix, maxFrames);
+        trace(key, stackTrace);
+    }
+
+
     /**
      * print all stack frames which match pattern to System.out by calling traceStackMatching(pattern, null)
      */
@@ -1671,23 +1784,141 @@ public class Helper
     public String formatStack(String prefix, int maxFrames)
     {
         StringBuffer buffer = new StringBuffer();
-        StackTraceElement[] stack = getStack();
+        appendStack(buffer, prefix, maxFrames, Thread.currentThread(), getStack());
+        return buffer.toString();
+    }
+    
+
+    //
+    // retrieving frames for all threads
+
+    /**
+     * return all stack traces by calling formatAllStacks(null)
+     */
+    public String formatAllStacks()
+    {
+        return formatAllStacks(null);
+    }
+
+    /**
+     * return all stack traces by calling formatAllStacks(prefix, 0)
+     */
+    public String formatAllStacks(String prefix)
+    {
+        return formatAllStacks(prefix, 0);
+    }
+
+    /**
+     * return all stack traces by calling formatAllStacks(null, maxFrames)
+     */
+    public String formatAllStacks(int maxFrames)
+    {
+        return formatAllStacks(null, maxFrames);
+    }
+
+    /**
+     * return all stack traces
+     *
+     * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null
+     * then the prefix "Stack trace for thread " + Thread.currentThread().getName() + "\n" is used
+     * @param maxFrames the maximum number of frames to print or 0 if no limit should apply
+     */
+
+    public String formatAllStacks(String prefix, int maxFrames)
+    {
+    	StringBuffer buffer = new StringBuffer();
+
+    	Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces();
+    	for (Map.Entry<Thread, StackTraceElement[]> entry : stacks.entrySet()) {
+	    	appendStack(buffer, prefix, maxFrames, entry.getKey(), entry.getValue());
+    		buffer.append('\n');
+    	}
+
+    	return buffer.toString();
+    }
+
+    //
+    // retrieving frames for all threads
+
+    /**
+     * return stack traces of a specific thread by calling formatThreadStack(threadName, null)
+     */
+    public String formatThreadStack(String threadName)
+    {
+        return formatThreadStack(threadName, null);
+    }
+
+    /**
+     * return all stack traces by calling formatThreadStack(threadName, prefix, 0)
+     */
+    public String formatThreadStack(String threadName, String prefix)
+    {
+        return formatThreadStack(threadName, prefix, 0);
+    }
+
+    /**
+     * return all stack traces by calling formatThreadStack(threadName, null, maxFrames)
+     */
+    public String formatThreadStack(String threadName, int maxFrames)
+    {
+        return formatThreadStack(threadName, null, maxFrames);
+    }
+
+    /**
+     * return all stack traces
+     *
+     * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null
+     * then the prefix "Stack trace for thread " + threadName + "\n" is used
+     * @param maxFrames the maximum number of frames to print or 0 if no limit should apply
+     */
+
+    public String formatThreadStack(String threadName, String prefix, int maxFrames)
+    {
+    	StringBuffer buffer = new StringBuffer();
+    	Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces();
+
+    	boolean found = false;
+
+    	for (Map.Entry<Thread, StackTraceElement[]> entry : stacks.entrySet()) {
+    		Thread thread = entry.getKey();
+    		if (thread.getName().equals(threadName)) {
+        		appendStack(buffer, prefix, maxFrames, thread, entry.getValue());
+        		found = true;
+    		}
+    	}
+
+    	if (!found) {
+    		buffer.append("Thread ");
+    		buffer.append(threadName);
+    		buffer.append(" not found\n");
+    	}
+
+    	return buffer.toString();
+    }
+
+    private void appendStack(StringBuffer buffer, String prefix, int maxFrames, Thread thread, StackTraceElement[] stack) {
         int l = stack.length;
-        int i = triggerIndex(stack);
+        int i;
 
-        if (i < 0) {
-            return "";
-        }
+    	if (thread == Thread.currentThread()) {
+	    	// trim off the byteman trigger parts
+            i = triggerIndex(stack);
+            if (i < 0) {
+                return;
+            }
+    	} else {
+    		i = 0;
+    	}
+
+    	if (prefix != null) {
+    		buffer.append(prefix);
+    	} else {
+    		buffer.append("Stack trace for thread ");
+    		buffer.append(thread.getName());
+    		buffer.append('\n');
+    	}
 
-        if (prefix != null) {
-            buffer.append(prefix);
-        } else {
-            buffer.append("Stack trace for thread ");
-            buffer.append(Thread.currentThread().getName());
-            buffer.append('\n');
-        }
         boolean dotdotdot = false;
-
         if (maxFrames > 0 && (i + maxFrames) < l) {
             l = i + maxFrames;
             dotdotdot = true;
@@ -1699,8 +1930,6 @@ public class Helper
         if (dotdotdot) {
             buffer.append("  . . .\n");
         }
-
-        return buffer.toString();
     }
 
     // retrieving caller frames which match a regular expression
diff --git a/agent/src/test/java/org/jboss/byteman/tests/helpers/TestThread.java b/agent/src/test/java/org/jboss/byteman/tests/helpers/TestThread.java
new file mode 100644
index 0000000..bfbf2c2
--- /dev/null
+++ b/agent/src/test/java/org/jboss/byteman/tests/helpers/TestThread.java
@@ -0,0 +1,40 @@
+package org.jboss.byteman.tests.helpers;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+import org.jboss.byteman.tests.helpertests.TestStackTrace;
+
+
+public class TestThread extends Thread {
+	private static CyclicBarrier barrier = new CyclicBarrier(2);
+	
+	private final boolean trigger;
+	private final TestStackTrace inner;
+
+	public TestThread(TestStackTrace inner, boolean trigger) {
+		super("TestThread-" + trigger);
+		this.trigger = trigger;
+		this.inner = inner;
+	}
+	
+	public void run() {
+		sync();
+		if (trigger)
+			fire();
+		sync();
+	}
+
+	private void sync() {
+		try {
+			barrier.await();
+		} catch (InterruptedException ex) { 
+			return; 
+		} catch (BrokenBarrierException ex) { 
+			return; 
+		}
+	}
+
+	private void fire() {
+	} 
+}
\ No newline at end of file
diff --git a/agent/src/test/java/org/jboss/byteman/tests/helpertests/TestStackTrace.java b/agent/src/test/java/org/jboss/byteman/tests/helpertests/TestStackTrace.java
index 45e6012..aac58e9 100644
--- a/agent/src/test/java/org/jboss/byteman/tests/helpertests/TestStackTrace.java
+++ b/agent/src/test/java/org/jboss/byteman/tests/helpertests/TestStackTrace.java
@@ -24,6 +24,7 @@
 package org.jboss.byteman.tests.helpertests;
 
 import org.jboss.byteman.tests.Test;
+import org.jboss.byteman.tests.helpers.TestThread;
 
 /**
  * Test to ensure arithmetic operations compute as expected
@@ -71,6 +72,23 @@ public class TestStackTrace extends Test
         }
 
         checkOutput(true);
+
+        runNumber = 4;
+        try {
+            log("calling TestStackTrace.triggerMethod4");
+        	Thread t1 = new TestThread(this, true);
+        	Thread t2 = new TestThread(this, false);
+
+        	t1.start();
+        	t2.start();
+        	t1.join();
+        	t2.join();
+            log("called TestStackTrace.triggerMethod4");
+        } catch (Exception e) {
+            log(e);
+        }
+
+        checkOutput(true);
     }
 
     public void triggerMethod1()
@@ -132,8 +150,8 @@ public class TestStackTrace extends Test
 
     @Override
     public String getExpected() {
-        int lineTriggerMethod1 = 76;
-        int lineTest = 40;
+        int lineTriggerMethod1 = 94;
+        int lineTest = 41;
         switch (runNumber) {
             case 1:
             {
@@ -194,8 +212,23 @@ public class TestStackTrace extends Test
                 logExpected("called TestStackTrace.triggerMethod3");
             }
             break;
+            case 4:
+            {
+                logExpected("calling TestStackTrace.triggerMethod4");
+                logExpected("Stack trace for thread TestThread-false");
+                logExpected("sun.misc.Unsafe.park(Unsafe.java:-2)");
+                logExpected("java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)");
+                logExpected("java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)");
+                logExpected("java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:227)");
+                logExpected("java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:355)");
+                logExpected("org.jboss.byteman.tests.helpers.TestThread.sync(TestThread.java:30)");
+                logExpected("org.jboss.byteman.tests.helpers.TestThread.run(TestThread.java:25)");
+                logExpected("");
+                logExpected("called TestStackTrace.triggerMethod4");
+            }
+            break;
         }
 
         return super.getExpected();
     }
-}
\ No newline at end of file
+}
diff --git a/agent/src/test/resources/scripts/helpertests/TestStackTrace.btm b/agent/src/test/resources/scripts/helpertests/TestStackTrace.btm
index 2f8fd43..cd9ef3f 100644
--- a/agent/src/test/resources/scripts/helpertests/TestStackTrace.btm
+++ b/agent/src/test/resources/scripts/helpertests/TestStackTrace.btm
@@ -96,3 +96,14 @@ DO test.log(formatStackBetween("org.jboss.byteman.tests.helpertests.TestStackTra
                                 "org.jboss.byteman.tests.helpertests.TestStackTrace.triggerMethod3",
                                 true, true, "stacktrace between frames\n"))
 ENDRULE
+
+
+RULE test all-threads stacktrace, dump stacks
+CLASS org.jboss.byteman.tests.helpers.TestThread
+METHOD fire
+HELPER org.jboss.byteman.tests.helpers.Default
+AT EXIT
+BIND test : TestStackTrace = $0.inner
+IF TRUE
+DO test.log(formatThreadStack("TestThread-false"))
+ENDRULE
