Author: markt
Date: Wed Feb 24 00:07:06 2010
New Revision: 915603

URL: http://svn.apache.org/viewvc?rev=915603&view=rev
Log:
Add basic memory leak detection for JMX and manager (kkolinko/markt)

Modified:
    tomcat/tc6.0.x/trunk/STATUS.txt
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
    
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java
    
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java
    tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml
    tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml
    tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml
    tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml

Modified: tomcat/tc6.0.x/trunk/STATUS.txt
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS.txt?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/STATUS.txt (original)
+++ tomcat/tc6.0.x/trunk/STATUS.txt Wed Feb 24 00:07:06 2010
@@ -118,36 +118,6 @@
 
     http://tools.ietf.org/html/rfc2616
 
-
-* Memory leak detection for JMX and manager
-  
http://people.apache.org/~kkolinko/patches/2010-02-16_tc6_memory-leak-detection.patch
-  It is corrected markt's patch
-  (Added revs.909204,910584,910612
-   TC 6 specifics: /findleaks should be explicitly mapped in web.xml,
-   where TC 7 uses /text/findleaks, and that URL is mentioned in 
manager-howto.xml)
-  +1: kkolinko, fhanik, markt
-  -1:
-
-  kkolinko: Some further thoughts on naming/messages/documentation
-   - It might be not so clear to a newcomer, that to use this functionality 
you have
-     a) Use you application
-     b) Reload it
-     c) Press the "Find leaks" button
-     It does not work without a) and b).
-
-   - Saying that application "triggered" a memory leak sounds too harsh for
-   me. I think "has" or "suffered" would be better here.
-
-   - It is safe to press the "Find leaks" button several times. Besides the
-   gc call it just "lists" the contexts that are still present in memory.
-   It does not clear its own list.
-
-  kkolinko: The display fix for ROOT context (r910612) might be applied to
-   StandardHost.findReloadedContextMemoryLeaks() as well. Any comments? If
-   results of a JMX call are displayed to humans, I'd be better to have "/"
-   instead of an empty string.
-
-
 * Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=48371
   Take account of comments when working out where to insert generated web.xml 
   http://people.apache.org/~markt/patches/2010-02-13-bug48371.patch

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java 
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardHost.java Wed 
Feb 24 00:07:06 2010
@@ -19,14 +19,23 @@
 package org.apache.catalina.core;
 
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
 
 import org.apache.catalina.Container;
 import org.apache.catalina.Context;
 import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
 import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
 import org.apache.catalina.Valve;
+import org.apache.catalina.loader.WebappClassLoader;
 import org.apache.catalina.startup.HostConfig;
 import org.apache.catalina.valves.ValveBase;
 import org.apache.tomcat.util.modeler.Registry;
@@ -164,6 +173,12 @@
      */
      private boolean xmlNamespaceAware = false;
 
+     /**
+      * Track the class loaders for the child web applications so memory leaks
+      * can be detected.
+      */
+     private Map<ClassLoader, String> childClassLoaders =
+         new WeakHashMap<ClassLoader, String>();
 
     // ------------------------------------------------------------- Properties
 
@@ -520,6 +535,11 @@
      */
     public void addChild(Container child) {
 
+        if (child instanceof Lifecycle) {
+            ((Lifecycle) child).addLifecycleListener(
+                    new MemoryLeakTrackingListener());
+        }
+
         if (!(child instanceof Context))
             throw new IllegalArgumentException
                 (sm.getString("standardHost.notContext"));
@@ -529,6 +549,49 @@
 
 
     /**
+     * Used to ensure the regardless of {...@link Context} implementation, a 
record
+     * is kept of the class loader used every time a context starts.
+     */
+    private class MemoryLeakTrackingListener implements LifecycleListener {
+
+        public void lifecycleEvent(LifecycleEvent event) {
+            if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
+                if (event.getSource() instanceof Context) {
+                    Context context = ((Context) event.getSource());
+                    childClassLoaders.put(context.getLoader().getClassLoader(),
+                            context.getServletContext().getContextPath());
+                }
+            }
+        }
+    }
+    
+    
+    /**
+     * Attempt to identify the contexts that have a class loader memory leak.
+     * This is usually triggered on context reload. Note: This method attempts
+     * to force a full garbage collection. This should be used with extreme
+     * caution on a production system.
+     */
+    public String[] findReloadedContextMemoryLeaks() {
+        
+        System.gc();
+        
+        List<String> result = new ArrayList<String>();
+        
+        for (Map.Entry<ClassLoader, String> entry :
+                childClassLoaders.entrySet()) {
+            ClassLoader cl = entry.getKey();
+            if (cl instanceof WebappClassLoader) {
+                if (!((WebappClassLoader) cl).isStarted()) {
+                    result.add(entry.getValue());
+                }
+            }
+        }
+        
+        return result.toArray(new String[result.size()]);
+    }
+
+    /**
      * Return the set of alias names for this Host.  If none are defined,
      * a zero length array is returned.
      */

Modified: 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml 
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/mbeans-descriptors.xml 
Wed Feb 24 00:07:06 2010
@@ -542,6 +542,12 @@
     <operation name="stop" description="Stop" impact="ACTION" 
returnType="void" />
     <operation name="init" description="Init" impact="ACTION" 
returnType="void" />
     <operation name="destroy" description="Destroy" impact="ACTION" 
returnType="void" />
+    
+    <operation name="findReloadedContextMemoryLeaks"
+               description="Provide a list of contexts that have leaked memory 
on reload. This will attempt to force a full garbage collection. Use with 
extreme caution on production systems."
+               impact="ACTION"
+               returnType="[Ljava.lang.String;" />
+
   </mbean>
   
   <mbean name="StandardHostValve"

Modified: 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/loader/WebappClassLoader.java 
Wed Feb 24 00:07:06 2010
@@ -1642,6 +1642,10 @@
     }
 
 
+    public boolean isStarted() {
+        return started;
+    }
+
     /**
      * Stop the class loader.
      *

Modified: 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java 
(original)
+++ 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/HTMLManagerServlet.java 
Wed Feb 24 00:07:06 2010
@@ -136,6 +136,8 @@
             message = start(path);
         } else if (command.equals("/stop")) {
             message = stop(path);
+        } else if (command.equals("/findleaks")) {
+            message = findleaks();
         } else {
             message =
                 sm.getString("managerServlet.unknownCommand", command);
@@ -498,6 +500,16 @@
         args[3] = sm.getString("htmlManagerServlet.deployButton");
         writer.print(MessageFormat.format(UPLOAD_SECTION, args));
 
+        // Diagnostics section
+        args = new Object[5];
+        args[0] = sm.getString("htmlManagerServlet.diagnosticsTitle");
+        args[1] = sm.getString("htmlManagerServlet.diagnosticsLeak");
+        args[2] = response.encodeURL(request.getContextPath() +
+                "/html/findleaks");
+        args[3] = sm.getString("htmlManagerServlet.diagnosticsLeakWarning");
+        args[4] = sm.getString("htmlManagerServlet.diagnosticsLeakButton");
+        writer.print(MessageFormat.format(DIAGNOSTICS_SECTION, args));
+
         // Server Header Section
         args = new Object[7];
         args[0] = sm.getString("htmlManagerServlet.serverTitle");
@@ -631,6 +643,33 @@
 
         return stringWriter.toString();
     }
+    
+    /**
+     * Find potential memory leaks caused by web application reload.
+     *
+     * @see ManagerServlet#findleaks(PrintWriter) 
+     *
+     * @return message String
+     */
+    protected String findleaks() {
+
+        StringBuilder msg = new StringBuilder();
+
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+
+        super.findleaks(printWriter);
+
+        if (stringWriter.getBuffer().length() > 0) {
+            msg.append(sm.getString("htmlManagerServlet.findleaksList"));
+            msg.append(stringWriter.toString());
+        } else {
+            msg.append(sm.getString("htmlManagerServlet.findleaksNone"));
+        }
+
+        return msg.toString();
+    }
+
 
     /**
      * @see javax.servlet.Servlet#getServletInfo()
@@ -1136,4 +1175,31 @@
         "<br>\n" +
         "\n";
 
+    private static final String DIAGNOSTICS_SECTION =
+        "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\" class=\"title\">{0}</td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\" class=\"header-left\"><small>{1}</small></td>\n" +
+        "</tr>\n" +
+        "<tr>\n" +
+        " <td colspan=\"2\">\n" +
+        "<form method=\"post\" action=\"{2}\">\n" +
+        "<table cellspacing=\"0\" cellpadding=\"3\">\n" +
+        "<tr>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <input type=\"submit\" value=\"{4}\">\n" +
+        " </td>\n" +
+        " <td class=\"row-left\">\n" +
+        "  <small>{3}</small>\n" +
+        " </td>\n" +
+        "</tr>\n" +
+        "</table>\n" +
+        "</form>\n" +
+        "</td>\n" +
+        "</tr>\n" +
+        "</table>\n" +
+        "<br>";
+
 }

Modified: 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties 
(original)
+++ 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/LocalStrings.properties 
Wed Feb 24 00:07:06 2010
@@ -43,6 +43,12 @@
 htmlManagerServlet.deployUploadNoFile=FAIL - File upload failed, no file
 htmlManagerServlet.deployUploadWarExists=FAIL - War file \"{0}\" already 
exists on server
 htmlManagerServlet.deployWar=WAR or Directory URL:
+htmlManagerServlet.diagnosticsLeak=Check to see if a web application has 
caused a memory leak on reload
+htmlManagerServlet.diagnosticsLeakButton=Find leaks
+htmlManagerServlet.diagnosticsLeakWarning=This diagnostic check will trigger a 
full garbage collection. Use it with extreme caution on production systems.
+htmlManagerServlet.diagnosticsTitle=Diagnostics
+htmlManagerServlet.findleaksList=The following web applications appear to have 
triggered a memory leak on reload (use a profiler to confirm):\n
+htmlManagerServlet.findleaksNone=No web applications appear to have triggered 
a memory leak on reload.
 htmlManagerServlet.list=List Applications
 htmlManagerServlet.manager=Manager
 htmlManagerServlet.messageLabel=Message:
@@ -63,6 +69,7 @@
 managerServlet.deployFailed=FAIL - Failed to deploy application at context 
path {0}
 managerServlet.deployedButNotStarted=FAIL - Deployed application at context 
path {0} but context failed to start
 managerServlet.exception=FAIL - Encountered exception {0}
+managerServlet.findleaksFail=FAIL - Find leaks failed: Host not instance of 
StandardHost
 managerServlet.invalidPath=FAIL - Invalid context path {0} was specified
 managerServlet.invalidWar=FAIL - Invalid application URL {0} was specified
 managerServlet.listed=OK - Listed applications for virtual host {0}

Modified: 
tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java 
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/manager/ManagerServlet.java 
Wed Feb 24 00:07:06 2010
@@ -54,6 +54,7 @@
 import org.apache.catalina.Session;
 import org.apache.catalina.UserDatabase;
 import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.StandardHost;
 import org.apache.catalina.core.StandardServer;
 import org.apache.catalina.util.RequestUtil;
 import org.apache.catalina.util.ServerInfo;
@@ -379,6 +380,8 @@
             stop(writer, path);
         } else if (command.equals("/undeploy")) {
             undeploy(writer, path);
+        } else if (command.equals("/findleaks")) {
+            findleaks(writer);
         } else {
             writer.println(sm.getString("managerServlet.unknownCommand",
                                         command));
@@ -523,6 +526,28 @@
 
 
     /**
+     * Find potential memory leaks caused by web application reload.
+     */
+    protected void findleaks(PrintWriter writer) {
+        
+        if (!(host instanceof StandardHost)) {
+            writer.println(sm.getString("managerServlet.findleaksFail"));
+            return;
+        }
+        
+        String[] results =
+            ((StandardHost) host).findReloadedContextMemoryLeaks();
+        
+        for (String result : results) {
+            if ("".equals(result)) {
+                result = "/";
+            }
+            writer.println(result);
+        }
+    }
+    
+    
+    /**
      * Store server configuration.
      * 
      * @param path Optional context path to save

Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Wed Feb 24 00:07:06 2010
@@ -108,6 +108,11 @@
         Modify ThreadLocal memory leak detection to not report false positives
         and to simplify implementation. (markt/kkolinko)
       </update>
+      <add>
+        Basic memory leak detection was added to the standard Host
+        implementation and exposed via JMX to detect memory leaks on web
+        application reload. (markt/kkolinko)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">
@@ -150,6 +155,14 @@
       </fix>
     </changelog>
   </subsection>
+  <subsection name="Webapps">
+    <changelog>
+      <add>
+        Make the new web application reload memory leak detection available
+        through the Manager application. (markt/kkolinko)
+      </add>
+    </changelog>
+  </subsection>
   <subsection name="Other">
     <changelog>
       <fix>

Modified: tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/html-manager-howto.xml Wed Feb 24 
00:07:06 2010
@@ -36,7 +36,7 @@
 Tomcat.  This document is for the HTML web interface to the web application
 <a href="manager-howto.html">manager</a>.</p>
 
-<p>The interface is divided into five sections:
+<p>The interface is divided into six sections:
 <ul>
   <li><strong>Message</strong> - Displays success and failure messages.</li>
   <li><strong>Manager</strong> - General manager operations like list and
@@ -44,6 +44,7 @@
   <li><strong>Applications</strong> - List of web applications and
       commands.</li>
   <li><strong>Deploy</strong> - Deploying web applications.</li>
+  <li><strong>Diagnostics</strong> - Identifying potential problems.</li>
   <li><strong>Server Information</strong> - Information about the Tomcat
       server.</li>
 </ul>
@@ -546,6 +547,19 @@
 </subsection>
 </section>
 
+<section name="Diagnostics">
+
+<p><strong>The find leaks diagnostic triggers a full garbage collection. It
+should be used with extreme caution on production systems.</strong></p>
+
+<p>The find leaks diagnostic attempts to identify web applications that have
+caused memory leaks when they were reloaded. Results should always be confirmed
+with a profiler. The diagnostic uses additional functionality provided by the
+StandardHost implementation. It will not work if a custom host is used that
+does not extend StandardHost.</p>
+
+</section>
+
 <section name="Server Information">
 
 <p>This section displays information about Tomcat, the operating system of

Modified: tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/manager-howto.xml Wed Feb 24 00:07:06 2010
@@ -51,6 +51,8 @@
 <a href="#Stop an Existing Application">Stop an Existing Application</a><br />
 <a href="#Undeploy an Existing Application">
 Undeploy an Existing Application</a><br />
+<a href="#Server Status">Server Status</a><br />
+<a href="#Finding memory leaks">Finding memory leaks</a><br />
 </blockquote>
 <a href="#Executing Manager Commands With Ant">
 Executing Manager Commands With Ant</a><br />
@@ -897,6 +899,35 @@
 
 </subsection>
 
+<subsection name="Finding memory leaks">
+
+<source>
+http://localhost:8080/manager/findleaks
+</source>
+
+<p><strong>The find leaks diagnostic triggers a full garbage collection. It
+should be used with extreme caution on production systems.</strong></p>
+
+<p>The find leaks diagnostic attempts to identify web applications that have
+caused memory leaks when they were reloaded. Results should always be confirmed
+with a profiler. The diagnostic uses additional functionality provided by the
+StandardHost implementation. It will not work if a custom host is used that
+does not extend StandardHost.</p>
+
+<p>If this command succeeds, you will see a response like this:</p>
+<source>
+/leaking-webapp
+</source>
+
+<p>Each context path for a web application that is believed to have triggered a
+memory leak when it was reloaded will be listed on a new line. If an 
application
+has been reloaded several times, it may be listed several times.</p>
+
+<p>If the command does not succeed, the response will start with
+<code>FAIL</code> and include an error message.</p>
+ 
+</subsection>
+
 <subsection name="Server Status">
 
 <p>From this link , you can view information about the server.</p>

Modified: tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml?rev=915603&r1=915602&r2=915603&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/manager/WEB-INF/web.xml Wed Feb 24 00:07:06 
2010
@@ -121,6 +121,10 @@
       <url-pattern>/resources</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
+    <servlet-name>Manager</servlet-name>
+      <url-pattern>/findleaks</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
     <servlet-name>Status</servlet-name>
     <url-pattern>/status/*</url-pattern>
   </servlet-mapping>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to