Author: cwiklik
Date: Fri Nov 12 20:33:20 2010
New Revision: 1034555

URL: http://svn.apache.org/viewvc?rev=1034555&view=rev
Log:
UIMA-1938 Added instrumentation to optionally force JVM to dump the stack

Modified:
    
uima/uima-as/trunk/uimaj-as-core/src/main/java/org/apache/uima/aae/controller/PrimitiveAnalysisEngineController_impl.java

Modified: 
uima/uima-as/trunk/uimaj-as-core/src/main/java/org/apache/uima/aae/controller/PrimitiveAnalysisEngineController_impl.java
URL: 
http://svn.apache.org/viewvc/uima/uima-as/trunk/uimaj-as-core/src/main/java/org/apache/uima/aae/controller/PrimitiveAnalysisEngineController_impl.java?rev=1034555&r1=1034554&r2=1034555&view=diff
==============================================================================
--- 
uima/uima-as/trunk/uimaj-as-core/src/main/java/org/apache/uima/aae/controller/PrimitiveAnalysisEngineController_impl.java
 (original)
+++ 
uima/uima-as/trunk/uimaj-as-core/src/main/java/org/apache/uima/aae/controller/PrimitiveAnalysisEngineController_impl.java
 Fri Nov 12 20:33:20 2010
@@ -19,9 +19,12 @@
 
 package org.apache.uima.aae.controller;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
 import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Semaphore;
@@ -63,7 +66,8 @@ import org.apache.uima.util.Level;
 public class PrimitiveAnalysisEngineController_impl extends 
BaseAnalysisEngineController implements
         PrimitiveAnalysisEngineController {
   private static final Class CLASS_NAME = 
PrimitiveAnalysisEngineController_impl.class;
-
+  private static final String DUMP_HEAP_THRESHOLD = "dumpHeapThreshold";
+  
   // Stores AE metadata
   private AnalysisEngineMetaData analysisEngineMetadata;
 
@@ -89,6 +93,8 @@ public class PrimitiveAnalysisEngineCont
   // guards uima core code that is not thread safe.
   private static Semaphore sharedInitSemaphore = new Semaphore(1);
 
+  static private Long lastDump = new Long(0);
+
   public PrimitiveAnalysisEngineController_impl(String anEndpointName,
           String anAnalysisEngineDescriptor, AsynchAECasManager aCasManager,
           InProcessCache anInProcessCache, int aWorkQueueSize, int 
anAnalysisEnginePoolSize)
@@ -155,6 +161,7 @@ public class PrimitiveAnalysisEngineCont
 
   public void initializeAnalysisEngine() throws 
ResourceInitializationException {
     ResourceSpecifier rSpecifier = null;
+    
     try {
       //  Acquire single-permit semaphore to serialize instantiation of AEs.
       //  This is done to control access to non-thread safe structures in the
@@ -419,7 +426,28 @@ public class PrimitiveAnalysisEngineCont
       }
     }
   }
-
+  /**
+   * Conditionally creates and starts a timer thread that will time a 
task/method.
+   * The timer is only created if the System property -DheapDumpThreshold=<x> 
is set.
+   * The x is the max number of seconds the task/method is allowed before the 
timer pops.
+   * 
+   * @return instance of HeapDumpTimer or null
+   */
+  private StackDumpTimer ifEnabledStartHeapDumpTimer() {
+         try {
+                 if ( System.getProperty(DUMP_HEAP_THRESHOLD) != null) {
+                         int thresholdInSeconds = 
Integer.parseInt(System.getProperty(DUMP_HEAP_THRESHOLD));
+                         //    Creates and returns an instance of a timer. The 
timer is auto started in a 
+                         //    constructor and will expire in a given number 
of seconds
+                         return new StackDumpTimer(thresholdInSeconds);
+                 }
+         } catch( NumberFormatException e) {
+                 UIMAFramework.getLogger(CLASS_NAME).logrb(Level.WARNING, 
getClass().getName(),
+                       "ifEnabledStartHeapDumpTimer", 
UIMAEE_Constants.JMS_LOG_RESOURCE_BUNDLE,
+                       "UIMAEE_exception__WARNING", e);
+         }
+         return null;
+  }
   /**
    * This is called when a Stop request is received from a client. Add the 
provided Cas id to the
    * list of aborted CASes. The process() method checks this list to determine 
if it should continue
@@ -438,6 +466,9 @@ public class PrimitiveAnalysisEngineCont
       return;
     }
     CasStateEntry parentCasStateEntry = null;
+    // If enabled, keep a reference to a timer which
+    //  when it expires, will cause a JVM to dump a stack
+    StackDumpTimer stackDumpTimer = null;
     try {
       parentCasStateEntry = getLocalCache().lookupEntry(aCasReferenceId);
     } catch (Exception e) {
@@ -466,7 +497,18 @@ public class PrimitiveAnalysisEngineCont
       ae = aeInstancePool.checkout();
       // Get input CAS entry from the InProcess cache
       long time = super.getCpuTime();
+      //       Start the heap dump timer. This timer is only started if 
explicitly enabled
+      //       via System property: -DheapDumpThreshold=<x> where is number of 
seconds the 
+      //       method is allowed to complete. If the method is not complete in 
allowed window
+      //       the heap and stack trace dump of all threads will be produced.
+      stackDumpTimer = ifEnabledStartHeapDumpTimer();
       CasIterator casIterator = ae.processAndOutputNewCASes(aCAS);
+      if ( stackDumpTimer != null ) {
+         stackDumpTimer.cancel();
+         stackDumpTimer = null;   // nullify timer instance so that we dont 
have to worry about
+          // it in case an exception happens below
+      }
+
       // Store how long it took to call processAndOutputNewCASes()
       totalProcessTime = (super.getCpuTime() - time);
       long sequence = 1;
@@ -477,17 +519,42 @@ public class PrimitiveAnalysisEngineCont
       while (moreCASesToProcess) {
         long timeToProcessCAS = 0; // stores time in hasNext() and next() for 
each CAS
         hasNextTime = super.getCpuTime();
+        //     Start the heap dump timer. This timer is only started if 
explicitly enabled
+        //     via System property: -DheapDumpThreshold=<x> where x is a 
number of seconds the 
+        //     method is allowed to complete. If the method is not complete in 
allowed window
+        //     the heap and stack trace dump of all threads will be produced.
+        stackDumpTimer = ifEnabledStartHeapDumpTimer();
         if (!casIterator.hasNext()) {
           moreCASesToProcess = false;
           // Measure how long it took to call hasNext()
           timeToProcessCAS = (super.getCpuTime() - hasNextTime);
           totalProcessTime += timeToProcessCAS;
+          if ( stackDumpTimer != null ) {
+                 stackDumpTimer.cancel();
+                 stackDumpTimer = null;   // nullify timer instance so that we 
dont have to worry about
+              // it in case an exception happens below
+          }
           break; // from while
         }
+        if ( stackDumpTimer != null ) {
+               stackDumpTimer.cancel();
+               stackDumpTimer = null;   // nullify timer instance so that we 
dont have to worry about
+                                    // it in case an exception happens below
+        }
         // Measure how long it took to call hasNext()
         timeToProcessCAS = (super.getCpuTime() - hasNextTime);
         getNextTime = super.getCpuTime();
+        //     Start the heap dump timer. This timer is only started if 
explicitly enabled
+        //     via System property: -DheapDumpThreshold=<x> where is number of 
seconds the 
+        //     method is allowed to complete. If the method is not complete in 
allowed window
+        //     the heap and stack trace dump of all threads will be produced.
+        stackDumpTimer = ifEnabledStartHeapDumpTimer();
         CAS casProduced = casIterator.next();
+        if ( stackDumpTimer != null ) {
+               stackDumpTimer.cancel();
+               stackDumpTimer = null;   // nullify timer instance so that we 
dont have to worry about
+            // it in case an exception happens below
+        }
         // Add how long it took to call next()
         timeToProcessCAS += (super.getCpuTime() - getNextTime);
         // Add time to call hasNext() and next() to the running total
@@ -628,8 +695,9 @@ public class PrimitiveAnalysisEngineCont
         } else {
           // Send generated CAS to the client
           if (!stopped) {
-            getOutputChannel().sendReply(newEntry, anEndpoint);
-            // Check for delivery failure. The client may have terminated 
while an input CAS was being processed
+              getOutputChannel().sendReply(newEntry, anEndpoint);
+            
+              //       Check for delivery failure. The client may have 
terminated while an input CAS was being processed
             if ( childCasStateEntry.deliveryToClientFailed() ) {
                  if ( 
cmOutstandingCASes.containsKey(childCasStateEntry.getCasReferenceId())) {
                  
cmOutstandingCASes.remove(childCasStateEntry.getCasReferenceId());
@@ -669,6 +737,7 @@ public class PrimitiveAnalysisEngineCont
       // Set total number of children generated from this CAS
       // Store total time spent processing this input CAS
       
getCasStatistics(aCasReferenceId).incrementAnalysisTime(totalProcessTime);
+      
       if (!anEndpoint.isRemote()) {
         inputCASReturned = true;
         UimaTransport transport = getTransport(anEndpoint.getEndpoint());
@@ -692,17 +761,21 @@ public class PrimitiveAnalysisEngineCont
         }
       } else {
         if (!stopped) {
-          getOutputChannel().sendReply(aCasReferenceId, anEndpoint);
+            getOutputChannel().sendReply(aCasReferenceId, anEndpoint);
         }
 
         inputCASReturned = true;
       }
+      
       // Remove input CAS state entry from the local cache
       if (!isTopLevelComponent()) {
         localCache.lookupEntry(aCasReferenceId).setDropped(true);
         localCache.remove(aCasReferenceId);
       }
     } catch (Throwable e) {
+       if ( stackDumpTimer != null ) {
+               stackDumpTimer.cancel();
+       }
       processingFailed = true;
       ErrorContext errorContext = new ErrorContext();
       errorContext.add(AsynchAEMessage.CasReference, aCasReferenceId);
@@ -914,5 +987,59 @@ public class PrimitiveAnalysisEngineCont
     }
     System.out.println("Service:" + getComponentName() + " Stopped");
   }
+  /**
+   This method forces a heap and java dump. It only works with IBM jvm. 
+   **/
+  protected final synchronized void forceStackDump() {
+         try {
+                 Class<?> params[] = {};
+             Object paramsObj[] = {};
+             
+                 Class<?> dumpClass = Class.forName("com.ibm.jvm.Dump");
+             //        Get a handle to the static method
+          Method javaDumpMethod = dumpClass.getDeclaredMethod("JavaDump", 
params);
+          //   dump the stack of all threads
+          javaDumpMethod.invoke(null, paramsObj);
+                 
+         } catch( Exception e) {
+                 //    ignore ClassNotFoundException in case of sun jvm
+         }
+  }
+  
+  /**
+   * The HeapDumpTimer is optionally used to dump the heap if a task takes too 
much time to finish.
+   * It is enabled from the System property -DheapDumpThreshold=<x> where x is 
a number of seconds 
+   * the task is allowed to complete. If the task is not completed, the heap 
dump will be created. 
+   * 
+   *
+   */
+  public class StackDumpTimer  {
+         Timer timer;
+         volatile boolean timerCancelled;
+         
+         public StackDumpTimer ( int seconds )   {
+           timer = new Timer (  ) ;
+           timer.schedule ( new dumpTheStackTask (  ) , seconds*1000 ) ;
+         }
+         public void cancel() {
+                 timer.cancel();
+                 timer.purge();
+         }
+         class dumpTheStackTask extends TimerTask  {
+           public void run (  )   {
+               timerCancelled = true;
+               timer.cancel (  ) ; //Terminate the thread
+               // create a heap dump. NOTE: this only works with IBM jvm
+               synchronized (lastDump) {
+                   long now = System.currentTimeMillis();
+                   if (30000 < (now-lastDump)) {
+                     // create a stack dump. NOTE: this only works with IBM jvm
+                     forceStackDump();
+                     lastDump = now;
+                   }
+          }
+           }
+         }
 
+  }
 }


Reply via email to