public inbox for frysk-cvs@sourceware.org
help / color / mirror / Atom feed
* [SCM]  master: New ltrace test testRecursive.
@ 2007-12-21 13:01 pmachata
  0 siblings, 0 replies; only message in thread
From: pmachata @ 2007-12-21 13:01 UTC (permalink / raw)
  To: frysk-cvs

The branch, master has been updated
       via  f9ead2a82c1c952359e78ac9b381250a70ebd97c (commit)
       via  4da0a3084a95a97800ac27133f342a82f582fca9 (commit)
       via  66db8e191dce8a8108603f7cf5b77416a1322f38 (commit)
       via  b55639cdbe386d1f621e96f37b5c23448bb8293b (commit)
       via  608c705c74c91801b90212134442c9fe5d413235 (commit)
       via  5a99f3ef410d7d024cf4d465dcb9d005a2362fff (commit)
       via  cc8f368113a4ce80ec0d2c2c964592a34cea3d93 (commit)
       via  3521fe061ae8830f26ddd500146a4642f48c97f4 (commit)
       via  735aa4afa7e7532ebd7c4c8313412abf8c1874f0 (commit)
       via  e900c2b7839a8b8661759ddf43e4faaf43b38c14 (commit)
      from  5f345af74c688d16da9c1edd8c79b6786ddba380 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email.

- Log -----------------------------------------------------------------
commit f9ead2a82c1c952359e78ac9b381250a70ebd97c
Author: Petr Machata <pmachata@redhat.com>
Date:   Fri Dec 21 13:45:14 2007 +0100

    New ltrace test testRecursive.

commit 4da0a3084a95a97800ac27133f342a82f582fca9
Author: Petr Machata <pmachata@redhat.com>
Date:   Thu Dec 20 17:53:16 2007 +0100

    New ltrace test testMultipleControlers.
    
    * This one caused all the rewrites in last few days.  Yaaay!

commit 66db8e191dce8a8108603f7cf5b77416a1322f38
Author: Petr Machata <pmachata@redhat.com>
Date:   Thu Dec 20 17:35:44 2007 +0100

    Ltrace testMultipleObservers turned on

commit b55639cdbe386d1f621e96f37b5c23448bb8293b
Author: Petr Machata <pmachata@redhat.com>
Date:   Thu Dec 20 16:44:36 2007 +0100

    Ltrace testTracingAlias turned on

commit 608c705c74c91801b90212134442c9fe5d413235
Author: Petr Machata <pmachata@redhat.com>
Date:   Thu Dec 20 16:20:26 2007 +0100

    Ltrace testArgumentsCorrect1 turned on
    
    * Also some refactoring took place to reduce boilerplate code.

commit 5a99f3ef410d7d024cf4d465dcb9d005a2362fff
Author: Petr Machata <pmachata@redhat.com>
Date:   Thu Dec 20 15:46:12 2007 +0100

    Ltrace testCallRecorded turned on

commit cc8f368113a4ce80ec0d2c2c964592a34cea3d93
Author: Petr Machata <pmachata@redhat.com>
Date:   Thu Dec 20 14:28:14 2007 +0100

    MappingGuard tests moved to their own file
    
    * ... and turned on. The rest of ltrace suite still off.

commit 3521fe061ae8830f26ddd500146a4642f48c97f4
Author: Petr Machata <pmachata@redhat.com>
Date:   Thu Dec 20 05:14:41 2007 +0100

    Ltrace a step closer to true observer.
    
    * Ltrace was refactored to allow being used by independent clients.  In particular:
    ** No more shared Controller necessary.  Ltrace doesn't know about controller, and is now able to observe particular TracePoints.
    ** Ltrace doesn't observe mapping, controller does (if there is any).
    ** Mapping events moved over from FunctionObserver to MappingObserver.  MappingGuard now calculates detailed mapping changes as needed.
    ** It's in semi-broken state, and testsuite was turned off.  Will work on that tomorrow.

commit 735aa4afa7e7532ebd7c4c8313412abf8c1874f0
Author: Petr Machata <pmachata@redhat.com>
Date:   Tue Dec 18 18:09:05 2007 +0100

    ftrace displays terminating task info

commit e900c2b7839a8b8661759ddf43e4faaf43b38c14
Author: Petr Machata <pmachata@redhat.com>
Date:   Thu Dec 13 14:59:21 2007 +0100

    New function observer test.

-----------------------------------------------------------------------

Summary of changes:
 frysk-core/frysk/bindir/ChangeLog             |    5 +
 frysk-core/frysk/bindir/ftrace.java           |   14 +-
 frysk-core/frysk/ftrace/ChangeLog             |   65 +++
 frysk-core/frysk/ftrace/Ftrace.java           |  503 +++++++++++++------
 frysk-core/frysk/ftrace/FunctionObserver.java |    8 -
 frysk-core/frysk/ftrace/Ltrace.java           |  662 +++++++++----------------
 frysk-core/frysk/ftrace/LtraceController.java |   53 --
 frysk-core/frysk/ftrace/MappingGuard.java     |  128 +++++-
 frysk-core/frysk/ftrace/MappingObserver.java  |   29 +-
 frysk-core/frysk/ftrace/TestLtrace.java       |  498 ++++++++++++-------
 frysk-core/frysk/ftrace/TestMappingGuard.java |  138 +++++
 frysk-core/frysk/pkglibdir/ChangeLog          |    5 +
 frysk-core/frysk/pkglibdir/funit-calls.c      |    9 +
 13 files changed, 1273 insertions(+), 844 deletions(-)
 delete mode 100644 frysk-core/frysk/ftrace/LtraceController.java
 create mode 100644 frysk-core/frysk/ftrace/TestMappingGuard.java

First 500 lines of diff:
diff --git a/frysk-core/frysk/bindir/ChangeLog b/frysk-core/frysk/bindir/ChangeLog
index f01a5c8..1bbe15f 100644
--- a/frysk-core/frysk/bindir/ChangeLog
+++ b/frysk-core/frysk/bindir/ChangeLog
@@ -2,6 +2,11 @@
 
 	* TestFauxv.java (fauxv):  Delete System.out.
 
+2007-12-20  Petr Machata  <pmachata@redhat.com>
+
+	* ftrace.java (MyLtraceController): renamed to MyFtraceController.
+	Using Ftrace.Driver instead of Ltrace.Driver consistently.
+
 2007-12-19  cagney  <cagney@redhat.com>
 
 	* fhpd.java: Explicitly import frysk.sys.FileDescriptor.
diff --git a/frysk-core/frysk/bindir/ftrace.java b/frysk-core/frysk/bindir/ftrace.java
index 55e8524..9dde5c7 100644
--- a/frysk-core/frysk/bindir/ftrace.java
+++ b/frysk-core/frysk/bindir/ftrace.java
@@ -59,8 +59,6 @@ import frysk.proc.Task;
 import frysk.util.CommandlineParser;
 
 import frysk.ftrace.Ftrace;
-import frysk.ftrace.Ltrace;
-import frysk.ftrace.LtraceController;
 import frysk.ftrace.ObjectFile;
 import frysk.ftrace.TracePoint;
 import frysk.ftrace.TracePointOrigin;
@@ -103,8 +101,8 @@ class WorkingSetRule
     }
 }
 
-class MyLtraceController
-    implements LtraceController,
+class MyFtraceController
+    implements Ftrace.Controller,
 	       Ftrace.StackTracedSymbolsProvider
 {
     protected static final Logger logger = Logger.getLogger("frysk");
@@ -121,7 +119,7 @@ class MyLtraceController
 	return symbolsStackTraceSet.contains(symbol);
     }
 
-    public MyLtraceController() { }
+    public MyFtraceController() { }
 
     public void gotPltRules(List rules) {
 	logger.log(Level.FINER, "Got " + rules.size() + " PLT rules.");
@@ -189,7 +187,7 @@ class MyLtraceController
 	return objffn.equals(interpfn);
     }
 
-    public void applyTracingRules(final Task task, final ObjectFile objf, final Ltrace.Driver driver,
+    public void applyTracingRules(final Task task, final ObjectFile objf, final Ftrace.Driver driver,
 				  final List rules, final TracePointOrigin origin)
 	throws lib.dwfl.ElfException
     {
@@ -276,7 +274,7 @@ class MyLtraceController
 	    symbolsStackTraceSet.add(((TracePoint)it.next()).symbol);
     }
 
-    public void fileMapped(final Task task, final ObjectFile objf, final Ltrace.Driver driver) {
+    public void fileMapped(final Task task, final ObjectFile objf, final Ftrace.Driver driver) {
 	try {
 	    applyTracingRules(task, objf, driver, pltRules, TracePointOrigin.PLT);
 	    applyTracingRules(task, objf, driver, dynRules, TracePointOrigin.DYNAMIC);
@@ -307,7 +305,7 @@ class ftrace
     final List pltRules = new ArrayList();
     final List dynRules = new ArrayList();
     final List symRules = new ArrayList();
-    final MyLtraceController controller = new MyLtraceController();
+    final MyFtraceController controller = new MyFtraceController();
     boolean allowInterpTracing = false;
 
     Ftrace tracer = new Ftrace();
diff --git a/frysk-core/frysk/ftrace/ChangeLog b/frysk-core/frysk/ftrace/ChangeLog
index 7e53eef..c28dc52 100644
--- a/frysk-core/frysk/ftrace/ChangeLog
+++ b/frysk-core/frysk/ftrace/ChangeLog
@@ -1,3 +1,68 @@
+2007-12-21  Petr Machata  <pmachata@redhat.com>
+
+	* TestLtrace.java (testRecursive): New test.
+
+2007-12-20  Petr Machata  <pmachata@redhat.com>
+
+	* TestLtrace.java: Refactoring.
+	(testArgumentsCorrect1): Turned the test on.
+	(testTracingAlias): Dtto.
+	(testMultipleObservers): Dtto.
+	(testMultipleControlers): New test.
+
+2007-12-20  Petr Machata  <pmachata@redhat.com>
+
+	* Ftrace.java: Commentary changes & reorganizations.
+	* TestMappingGuard: Dtto.
+	* TestLtrace.java (testCallRecorded): Turned on the test.
+
+2007-12-20  Petr Machata  <pmachata@redhat.com>
+
+	* TestMappingGuard: New file.
+	(testDebugStateMappingGuard): New test, moved from TestLtrace.
+	(testSyscallMappingGuard): Dtto.
+
+2007-12-20  Petr Machata  <pmachata@redhat.com>
+
+	* MappingGuard.java: Now provides more fine-grained events.
+	(updateMappedPart): New method, brought over from Ltrace.
+	(updateUnmappedPart): Dtto.
+	(updateMappedFile): Dtto.
+	(updateUnmappedFile): Dtto.
+	(updateMapping): Dtto.
+	(DebugStateMappingGuard): Now implements terminating observer.
+	* MappingObserver.java: Added more fine-grained events.
+	* Ltrace.java: Doesn't use mapping guard at all.  Rewritten to
+	allow adding several observers to single tracepoint, without a
+	need to share one controller.  Ltrace doesn't know about the
+	controller anymore.
+	(Driver): Interface moved to Ftrace.
+	(requestAddFunctionObserver): Now provides observing per tracepoint.
+	(requestDeleteMappingObserver): Dtto.
+	* FunctionObserver.java: Doesn't provide mapping events anymore.
+	* Ftrace.java: Reindent.
+	Using terminated observer instead of terminating, which didn't
+	fire with fatal signals.
+	Uses mapping guard to direct Ltrace tracing.
+	(Driver, Controller): New interfaces, brought over from Ltrace and
+	LtraceController.  Using new interfaces where appropriate.
+	(functionObserver): New member variable.
+	(TracePointWorkingSet): New class, mostly brought over from Ltrace.
+	(MyMappingObserver): Dtto.
+	* TestLtrace.java: Temporarily shut down.
+
+2007-12-18  Petr Machata  <pmachata@redhat.com>
+
+	* Ftrace.java: Use terminating observer to inform about the task
+	termination events.
+	(MyTerminatingObserver): New class.
+
+2007-12-13  Petr Machata  <pmachata@redhat.com>
+
+	* TestLtrace.java: Reindented.
+	(MyController4): Moved from testTracingAlias to class scope.
+	(testMultipleObservers): New test.
+
 2007-12-12  Petr Machata  <pmachata@redhat.com>
 
 	* Ftrace.java (handleTask): Add cloned observer to task.
diff --git a/frysk-core/frysk/ftrace/Ftrace.java b/frysk-core/frysk/ftrace/Ftrace.java
index e21663b..5a5d7c5 100644
--- a/frysk-core/frysk/ftrace/Ftrace.java
+++ b/frysk-core/frysk/ftrace/Ftrace.java
@@ -50,23 +50,27 @@ import frysk.proc.ProcTasksObserver;
 import frysk.proc.Task;
 import frysk.proc.TaskObserver;
 
-import inua.util.PrintWriter;
+import frysk.sys.Signal;
 
+import inua.util.PrintWriter;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Observable;
 import java.util.Observer;
+import java.util.Set;
 import java.util.Iterator;
-
-import java.io.File;
+import java.util.logging.*;
 
 public class Ftrace
 {
+    static final Logger logger = Logger.getLogger(FtraceLogger.LOGGER_ID);
+
     // Where to send output.
     Reporter reporter;
 
-  // True if we're tracing children as well.
-  boolean traceChildren = false;
+    // True if we're tracing children as well.
+    boolean traceChildren = false;
 
     // True if we're tracing syscalls.
     boolean traceSyscalls = true;
@@ -74,20 +78,52 @@ public class Ftrace
     // True if we're tracing mmaps/unmaps.
     boolean traceMmapUnmap = false;
 
-    // Non-null if we're using ltrace.
-    LtraceController ltraceController = null;
-    StackTracedSymbolsProvider stackTraceSetProvider = null;
+    HashSet syscallStackTraceSet = null;
 
-  HashSet syscallStackTraceSet = null;
+    // Set of ProcId objects we trace; if traceChildren is set, we also
+    // look for their children.
+    HashSet tracedParents = new HashSet();
 
-  // Set of ProcId objects we trace; if traceChildren is set, we also
-  // look for their children.
-  HashSet tracedParents = new HashSet();
+    HashMap syscallCache = new HashMap();
 
-  HashMap syscallCache = new HashMap();
+    // The number of processes we're tracing.
+    int numProcesses;
 
-  // The number of processes we're tracing.
-  int numProcesses;
+    /**
+     * Controller has to be implemented externally.  Each time a
+     * mapping changes, it is called for consulation and has a chance
+     * to change working set of Ftrace via provided Driver interface.
+     */
+    public static interface Controller {
+	/**
+	 * New library FILE was mapped in task TASK.  Use DRIVER to tell
+	 * ltrace what to do.
+	 */
+	void fileMapped(frysk.proc.Task task, ObjectFile file, Driver driver);
+    }
+
+    /**
+     * External entity implementing this interface is called out each
+     * time an entry point is hit.  It can decide whether the stack
+     * trace should be generated or not.
+     * XXX: Ideally, this would also operate on tracepoints.
+     */
+    public static interface StackTracedSymbolsProvider {
+	boolean shouldStackTraceOn(Symbol symbol);
+    }
+
+    /**
+     * Driver implementation is placed here in Ftrace, and handed over
+     * via this interface to allow external controller to aid which
+     * tracepoints should be traced.
+     */
+    public static interface Driver {
+	void tracePoint(Task task, TracePoint tp);
+    }
+
+    // Non-null if we're using ltrace.
+    Controller ftraceController = null;
+    StackTracedSymbolsProvider stackTraceSetProvider = null;
 
     public void setTraceChildren ()
     {
@@ -104,91 +140,88 @@ public class Ftrace
 	traceMmapUnmap = true;
     }
 
-    public void setTraceFunctions (LtraceController functionController,
+    public void setTraceFunctions (Controller ftraceController,
 				   StackTracedSymbolsProvider stackTraceSetProvider)
     {
-	if (functionController == null
+	if (ftraceController == null
 	    || stackTraceSetProvider == null)
-	    throw new AssertionError("functonController != null && stackTraceSetProvider != null");
+	    throw new AssertionError("ftraceController != null && stackTraceSetProvider != null");
 
-	if (this.ltraceController == null
+	if (this.ftraceController == null
 	    && this.stackTraceSetProvider == null) {
-	    this.ltraceController = functionController;
+	    this.ftraceController = ftraceController;
 	    this.stackTraceSetProvider = stackTraceSetProvider;
 	}
 	else
-	    throw new AssertionError("LtraceController already assigned.");
+	    throw new AssertionError("FtraceController already assigned.");
+    }
+
+    public void addTracePid (ProcId id) {
+	tracedParents.add(id);
     }
 
-  public void addTracePid (ProcId id)
-  {
-    tracedParents.add(id);
-  }
+    public void setSyscallStackTracing (HashSet syscallSet) {
+	syscallStackTraceSet = syscallSet;
+    }
 
-  public void setSyscallStackTracing (HashSet syscallSet)
-  {
-    syscallStackTraceSet = syscallSet;
-  }
+    public void setWriter (PrintWriter writer) {
+	this.reporter = new Reporter(writer);
+    }
 
-    public void setWriter (PrintWriter writer)
+    private void init ()
     {
-	this.reporter = new Reporter(writer);
+	if (reporter == null)
+	    reporter = new Reporter(new PrintWriter(System.out));
+
+	functionObserver = new MyFunctionObserver(reporter, stackTraceSetProvider);
+
+	// this observer should only be used to pick up a proc if we
+	// are tracing a process given a pid
+	// otherwise use forkobserver.
+	Manager.host.observableProcAddedXXX.addObserver(new Observer()
+	    {
+		public void update (Observable observable, Object arg)
+		{
+		    Proc proc = (Proc) arg;
+		    ProcId id = proc.getId();
+		    if (tracedParents.contains(id)){
+			// In case we're tracing a new child, add it.
+			//tracedParents.add(proc.getId()); XXX: why is this needed ?
+			// Weird API... unfortunately we can't fetch the
+			// Proc's main task here, as it will be null. Instead
+			// we have to request it and handle it in a callback.
+			addProc(proc);
+		    }
+		}
+	    });
     }
 
-  private void init ()
-  {
-    if (reporter == null)
-	reporter = new Reporter(new PrintWriter(System.out));
+    private void addProc (Proc proc) {
+	new ProcTasksObserver(proc, tasksObserver);
+    }
 
-    // this observer should only be used to pick up a proc if we
-    // are tracing a process given a pid
-    // otherwise use forkobserver.
-    Manager.host.observableProcAddedXXX.addObserver(new Observer()
-    {
-      public void update (Observable observable, Object arg)
-      {
-	Proc proc = (Proc) arg;
-	ProcId id = proc.getId();
-	if (tracedParents.contains(id)){
-	    // In case we're tracing a new child, add it.
-//	    tracedParents.add(proc.getId()); XXX: why is this needed ?
-	    // Weird API... unfortunately we can't fetch the
-	    // Proc's main task here, as it will be null. Instead
-	    // we have to request it and handle it in a callback.
-	  addProc(proc);
-	}
-      }
-    });
-  }
-
-  private void addProc(Proc proc){
-    new ProcTasksObserver(proc, tasksObserver);
-  }
-
-  public void trace (String[] command)
-  {
-    init();
-    Manager.host.requestCreateAttachedProc(command, attachedObserver);
-    Manager.eventLoop.run();
-  }
-
-  public void trace ()
-  {
-    init();
-    for (Iterator it = tracedParents.iterator(); it.hasNext(); ){
-      Manager.host.requestFindProc
-	  ((ProcId)it.next(),
-	   new FindProc() {
-	       public void procFound (ProcId procId) {}
-	       public void procNotFound (ProcId procId, Exception e) {
-		   System.err.println("No process with ID " + procId.intValue() + " found.");
-		   Manager.eventLoop.requestStop();
-	       }
-	   }
-	   );
-      Manager.eventLoop.run();
+    public void trace (String[] command) {
+	init();
+	Manager.host.requestCreateAttachedProc(command, attachedObserver);
+	Manager.eventLoop.run();
+    }
+
+    public void trace () {
+	init();
+	for (Iterator it = tracedParents.iterator(); it.hasNext(); ){
+	    Manager.host.requestFindProc
+		((ProcId)it.next(),
+		 new FindProc() {
+		     public void procFound (ProcId procId) {}
+		     public void procNotFound (ProcId procId, Exception e) {
+			 System.err.println("No process with ID " + procId.intValue() + " found.");
+			 Manager.eventLoop.requestStop();
+		     }
+		 }
+		 );
+	    Manager.eventLoop.run();
+	}
     }
-  }
 
     private HashMap observationCounters = new HashMap();
 
@@ -229,18 +262,80 @@ public class Ftrace
 	task.requestAddClonedObserver(clonedObserver);
 	observationRequested(task);
 
-	if (ltraceController != null) {
-	    MyFunctionObserver functionObserver
-		= new MyFunctionObserver(reporter, stackTraceSetProvider);
-	    Ltrace.requestAddFunctionObserver(task, functionObserver, ltraceController);
-	    observationRequested(task);
-	}
+	task.requestAddTerminatedObserver(new MyTerminatedObserver());
+	observationRequested(task);
+
+	MappingGuard.requestAddMappingObserver(task, new MyMappingObserver(ftraceController));
+	observationRequested(task);
 
 	Manager.host.observableProcRemovedXXX.addObserver(new ProcRemovedObserver(proc));
 	reporter.eventSingle(task, "attached " + proc.getExe());
 	++numProcesses;
     }
 
+    /** Remembers working set preferences for each task.
+	Map&lt;Task, Map&lt;File, TracePointWorkingSet&gt;&gt; */
+    private final HashMap driversForTask = new HashMap();
+
+    private class TracePointWorkingSet
+	implements Driver
+    {
+	private Set tracePoints = new HashSet();
+
+	public void tracePoint(Task task, TracePoint tp)
+	{
+	    logger.log(Level.CONFIG, "Request for tracing `{0}'", tp.symbol.name);
+	    tracePoints.add(tp);
+	}
+
+	public void populateBreakpoints(Task task, MemoryMapping mapping, MemoryMapping.Part part)
+	{
+	    Set request = new HashSet();
+	    for (Iterator it = tracePoints.iterator(); it.hasNext(); ) {
+		TracePoint tp = (TracePoint)it.next();
+		if (tp.offset >= part.offset
+		    && tp.offset < part.offset + part.addressHigh - part.addressLow) {
+		    logger.log(Level.FINER,
+			       "Will trace `" + tp.symbol.name + "', "
+			       + "address=0x" + Long.toHexString(tp.address) + "; "
+			       + "offset=0x" + Long.toHexString(tp.offset) + "; "
+			       + "part at=0x" + Long.toHexString(part.addressLow)
+			       + ".." + Long.toHexString(part.addressHigh) + "; "
+			       + "part off=0x" + Long.toHexString(part.offset) + ";");
+
+		    long actualAddress = tp.offset - part.offset + part.addressLow;
+		    logger.log(Level.CONFIG,
+			       "Will trace `" + tp.symbol.name
+			       + "' at 0x" + Long.toHexString(actualAddress));
+
+		    request.add(tp);
+		}
+	    }
+	    if (!request.isEmpty())
+		Ltrace.requestAddFunctionObserver(task, functionObserver, request);
+	}
+
+	public void evacuateBreakpoints(Task task, MemoryMapping mapping, MemoryMapping.Part part)
+	{
+	    Set request = new HashSet();
+	    for (Iterator it = tracePoints.iterator(); it.hasNext(); ) {
+		TracePoint tp = (TracePoint)it.next();
+		if (tp.offset >= part.offset
+		    && tp.offset < part.offset + part.addressHigh - part.addressLow) {
+
+		    long actualAddress = tp.offset - part.offset + part.addressLow;
+		    logger.log(Level.CONFIG,
+			       "Stopping tracing of `" + tp.symbol.name
+			       + "' at 0x" + Long.toHexString(actualAddress));
+
+		    request.add(tp);
+		}
+	    }
+	    if (!request.isEmpty())
+		Ltrace.requestDeleteFunctionObserver(task, functionObserver, request);
+	}
+    }
+
     ProcObserver.ProcTasks tasksObserver = new ProcObserver.ProcTasks()
     {
 	public void existingTask (Task task)
@@ -270,69 +365,61 @@ public class Ftrace
 	public void deletedFrom (Object observable) {}
     };
 
-  /**
-   * An observer to stop the eventloop when the traced process exits.
-   */


hooks/post-receive
--
frysk system monitor/debugger


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2007-12-21 13:01 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-12-21 13:01 [SCM] master: New ltrace test testRecursive pmachata

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).