/hudson-core/src/main/java/hudson/util/RemotingDiagnostics.java

http://github.com/hudson/hudson · Java · 203 lines · 135 code · 21 blank · 47 comment · 7 complexity · 5a204d8a1c2df9e861455e884c63a08e MD5 · raw file

  1. /*
  2. * The MIT License
  3. *
  4. * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, CloudBees, Inc.
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. package hudson.util;
  25. import groovy.lang.GroovyShell;
  26. import hudson.FilePath;
  27. import hudson.Functions;
  28. import hudson.model.Hudson;
  29. import hudson.remoting.Callable;
  30. import hudson.remoting.DelegatingCallable;
  31. import hudson.remoting.VirtualChannel;
  32. import hudson.security.AccessControlled;
  33. import org.kohsuke.stapler.StaplerRequest;
  34. import org.kohsuke.stapler.StaplerResponse;
  35. import org.kohsuke.stapler.WebMethod;
  36. import javax.management.JMException;
  37. import javax.management.MBeanServer;
  38. import javax.management.ObjectName;
  39. import java.io.File;
  40. import java.io.IOException;
  41. import java.io.PrintWriter;
  42. import java.io.StringWriter;
  43. import java.lang.management.ManagementFactory;
  44. import java.lang.management.ThreadInfo;
  45. import java.util.Collections;
  46. import java.util.LinkedHashMap;
  47. import java.util.Map;
  48. import java.util.TreeMap;
  49. /**
  50. * Various remoting operations related to diagnostics.
  51. *
  52. * <p>
  53. * These code are useful wherever {@link VirtualChannel} is used, such as master, slaves, Maven JVMs, etc.
  54. *
  55. * @author Kohsuke Kawaguchi
  56. * @since 1.175
  57. */
  58. public final class RemotingDiagnostics {
  59. public static Map<Object,Object> getSystemProperties(VirtualChannel channel) throws IOException, InterruptedException {
  60. if(channel==null)
  61. return Collections.<Object,Object>singletonMap("N/A","N/A");
  62. return channel.call(new GetSystemProperties());
  63. }
  64. private static final class GetSystemProperties implements Callable<Map<Object,Object>,RuntimeException> {
  65. public Map<Object,Object> call() {
  66. return new TreeMap<Object,Object>(System.getProperties());
  67. }
  68. private static final long serialVersionUID = 1L;
  69. }
  70. public static Map<String,String> getThreadDump(VirtualChannel channel) throws IOException, InterruptedException {
  71. if(channel==null)
  72. return Collections.singletonMap("N/A","N/A");
  73. return channel.call(new GetThreadDump());
  74. }
  75. private static final class GetThreadDump implements Callable<Map<String,String>,RuntimeException> {
  76. public Map<String,String> call() {
  77. Map<String,String> r = new LinkedHashMap<String,String>();
  78. try {
  79. ThreadInfo[] data = Functions.getThreadInfos();
  80. Functions.ThreadGroupMap map = Functions.sortThreadsAndGetGroupMap(data);
  81. for (ThreadInfo ti : data)
  82. r.put(ti.getThreadName(),Functions.dumpThreadInfo(ti,map));
  83. } catch (LinkageError _) {
  84. // not in JDK6. fall back to JDK5
  85. r.clear();
  86. for (Map.Entry<Thread,StackTraceElement[]> t : Functions.dumpAllThreads().entrySet()) {
  87. StringBuilder buf = new StringBuilder();
  88. for (StackTraceElement e : t.getValue())
  89. buf.append(e).append('\n');
  90. r.put(t.getKey().getName(),buf.toString());
  91. }
  92. }
  93. return r;
  94. }
  95. private static final long serialVersionUID = 1L;
  96. }
  97. /**
  98. * Executes Groovy script remotely.
  99. */
  100. public static String executeGroovy(String script, VirtualChannel channel) throws IOException, InterruptedException {
  101. return channel.call(new Script(script));
  102. }
  103. private static final class Script implements DelegatingCallable<String,RuntimeException> {
  104. private final String script;
  105. private transient ClassLoader cl;
  106. private Script(String script) {
  107. this.script = script;
  108. cl = getClassLoader();
  109. }
  110. public ClassLoader getClassLoader() {
  111. return Hudson.getInstance().getPluginManager().uberClassLoader;
  112. }
  113. public String call() throws RuntimeException {
  114. // if we run locally, cl!=null. Otherwise the delegating classloader will be available as context classloader.
  115. if (cl==null) cl = Thread.currentThread().getContextClassLoader();
  116. GroovyShell shell = new GroovyShell(cl);
  117. StringWriter out = new StringWriter();
  118. PrintWriter pw = new PrintWriter(out);
  119. shell.setVariable("out", pw);
  120. try {
  121. Object output = shell.evaluate(script);
  122. if(output!=null)
  123. pw.print(output);
  124. } catch (Throwable t) {
  125. t.printStackTrace(pw);
  126. }
  127. return out.toString();
  128. }
  129. }
  130. /**
  131. * Obtains the heap dump in an HPROF file.
  132. */
  133. public static FilePath getHeapDump(VirtualChannel channel) throws IOException, InterruptedException {
  134. return channel.call(new Callable<FilePath, IOException>() {
  135. public FilePath call() throws IOException {
  136. final File hprof = File.createTempFile("hudson-heapdump", "hprof");
  137. hprof.delete();
  138. try {
  139. MBeanServer server = ManagementFactory.getPlatformMBeanServer();
  140. server.invoke(new ObjectName("com.sun.management:type=HotSpotDiagnostic"), "dumpHeap",
  141. new Object[]{hprof.getAbsolutePath(), true}, new String[]{String.class.getName(), boolean.class.getName()});
  142. return new FilePath(hprof);
  143. } catch (JMException e) {
  144. throw new IOException2(e);
  145. }
  146. }
  147. private static final long serialVersionUID = 1L;
  148. });
  149. }
  150. /**
  151. * Heap dump, exposable to URL via Stapler.
  152. *
  153. */
  154. public static class HeapDump {
  155. private final AccessControlled owner;
  156. private final VirtualChannel channel;
  157. public HeapDump(AccessControlled owner, VirtualChannel channel) {
  158. this.owner = owner;
  159. this.channel = channel;
  160. }
  161. /**
  162. * Obtains the heap dump.
  163. */
  164. public void doIndex(StaplerResponse rsp) throws IOException {
  165. rsp.sendRedirect("heapdump.hprof");
  166. }
  167. @WebMethod(name="heapdump.hprof")
  168. public void doHeapDump(StaplerRequest req, StaplerResponse rsp) throws IOException, InterruptedException {
  169. owner.checkPermission(Hudson.ADMINISTER);
  170. rsp.setContentType("application/octet-stream");
  171. FilePath dump = obtain();
  172. try {
  173. dump.copyTo(rsp.getCompressedOutputStream(req));
  174. } finally {
  175. dump.delete();
  176. }
  177. }
  178. public FilePath obtain() throws IOException, InterruptedException {
  179. return RemotingDiagnostics.getHeapDump(channel);
  180. }
  181. }
  182. }