/* * Copyright 2015 the original author or authors. * @https://github.com/scouter-project/scouter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package scouter.agent.util; import scouter.agent.Configure; import scouter.agent.proxy.ToolsMainFactory; import scouter.agent.trace.TraceContext; import scouter.agent.trace.TraceContextManager; import scouter.lang.pack.MapPack; import scouter.lang.pack.Pack; import scouter.lang.value.ListValue; import scouter.util.*; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Enumeration; public class DumpUtil extends Thread { private static DumpUtil instance = null; public final static synchronized DumpUtil getInstance() { if (instance == null) { instance = new DumpUtil(); instance.setDaemon(true); instance.setName(ThreadUtil.getName(instance)); instance.start(); } return instance; } protected DumpUtil() { } public static File getDumpFile(String prefix) { String name = prefix + "." + DateUtil.ymdhms(System.currentTimeMillis()) + ".dump"; return new File(Configure.getInstance().dump_dir, name); } public static Pack triggerHeapHisto() { PrintWriter out = null; MapPack pack = new MapPack(); try { File file = DumpUtil.getDumpFile("scouter.heaphisto"); out = new PrintWriter(new FileWriter(file)); ToolsMainFactory.heaphisto(out); pack.put("name", file.getName()); } catch (Throwable e) { e.printStackTrace(); } finally { FileUtil.close(out); } return pack; } public static Pack triggerThreadDump() { PrintWriter out = null; MapPack pack = new MapPack(); try { File file = DumpUtil.getDumpFile("scouter.threaddump"); out = new PrintWriter(new FileWriter(file)); ToolsMainFactory.threadDump(out); pack.put("name", file.getName()); } catch (Throwable e) { e.printStackTrace(); } finally { FileUtil.close(out); } return pack; } public static Pack triggerThreadList() { PrintWriter out = null; MapPack pack = new MapPack(); try { File file = DumpUtil.getDumpFile("scouter.threads"); out = new PrintWriter(new FileWriter(file)); MapPack mpack = ThreadUtil.getThreadList(); ListValue ids = mpack.getList("id"); ListValue name = mpack.getList("name"); ListValue stat = mpack.getList("stat"); ListValue cpu = mpack.getList("cpu"); for (int i = 0; i < ids.size(); i++) { long tid = CastUtil.clong(ids.get(i)); out.print(i + ":"); out.print(tid + ":"); out.print(name.get(i) + ":"); out.print(stat.get(i) + ":"); out.print("cpu " + cpu.get(i)); TraceContext ctx = TraceContextManager.getContext(tid); if (ctx != null) { out.print(":service " + Hexa32.toString32(ctx.txid) + ":"); out.print(ctx.serviceName + ":"); long etime = System.currentTimeMillis() - ctx.startTime; out.print(etime + " ms"); } out.println(""); printStack(out, tid); out.println(""); pack.put("name", file.getName()); } } catch (Throwable e) { e.printStackTrace(); } finally { FileUtil.close(out); } return pack; } static Configure conf = Configure.getInstance(); public static Pack triggerActiveService() { PrintWriter out = null; MapPack pack = new MapPack(); try { File file = DumpUtil.getDumpFile("scouter.activeservice"); out = new PrintWriter(new FileWriter(file)); Enumeration<TraceContext> en = TraceContextManager.getContextEnumeration(); for (int n = 0; en.hasMoreElements(); n++) { TraceContext ctx = en.nextElement(); out.print(n + ":"); out.print(ctx.thread.getId() + ":"); out.print(ctx.thread.getName() + ":"); out.print(ctx.thread.getState().name() + ":"); out.print("cpu " + SysJMX.getThreadCpuTime(ctx.thread) + ":"); out.print(Hexa32.toString32(ctx.txid) + ":"); out.print(ctx.serviceName + ":"); long etime = System.currentTimeMillis() - ctx.startTime; out.print(etime + " ms"); if (ctx.sqltext != null) { out.print(":sql=" + ctx.sqltext + ":"); } if (ctx.apicall_name != null) { out.println(":subcall=" + ctx.apicall_name); } out.println(""); printStack(out, ctx.thread.getId()); out.println(""); pack.put("name", file.getName()); } } catch (Throwable e) { e.printStackTrace(); } finally { FileUtil.close(out); } return pack; } public static void printStack(PrintWriter out, long tid) { ThreadMXBean tmb = ManagementFactory.getThreadMXBean(); ThreadInfo f = tmb.getThreadInfo(tid, 500); StackTraceElement[] se = f.getStackTrace(); if (se != null) { for (int i = 0; i < se.length; i++) { if (se[i] != null) { out.println("\t" + se[i]); } } } } private void trigger() { synchronized (this) { this.notifyAll(); } } public void run() { while (true) { synchronized (this) { ThreadUtil.wait(this); } switch (conf.autodump_level) { case 1: DumpUtil.triggerThreadDump(); break; case 2: DumpUtil.triggerActiveService(); break; case 3: DumpUtil.triggerThreadList(); break; default: DumpUtil.triggerThreadDump(); break; } } } private static long last_auto_dump = 0; private static boolean stopAutoDumpTemporarily = false; public static void autoDump() { if (conf.autodump_enabled == false || stopAutoDumpTemporarily == true) return; long now = System.currentTimeMillis(); if (now < last_auto_dump + conf.autodump_interval_ms) return; last_auto_dump = now; DumpUtil.getInstance().trigger(); } public static void autoDumpByCpuExceedance() { if (conf.autodump_enabled && conf.autodump_interval_ms <= conf.autodump_cpu_exceeded_dump_interval_ms) { return; } if(conf.autodump_cpu_exceeded_enabled == false) { return; } stopAutoDumpTemporarily = true; try{ for(int i=0; i<conf.autodump_cpu_exceeded_dump_cnt; i++) { long now = System.currentTimeMillis(); if(now < last_auto_dump + conf.autodump_cpu_exceeded_dump_interval_ms) { continue; } last_auto_dump = now; DumpUtil.getInstance().trigger(); Thread.sleep(conf.autodump_cpu_exceeded_dump_interval_ms); } } catch (Throwable t) { } finally { stopAutoDumpTemporarily = false; } } }