All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.main.CmsSingleThreadDumperThread Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 17.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.main;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import com.google.common.collect.Lists;

/**
 * Profiling thread used for the startup process.

* * Periodically creates thread dumps for a single thread, saves them zo a ZIP file, and also * generates a summary XML files presenting the stack frames with with their sample counts as a tree * structure.

*/ public class CmsSingleThreadDumperThread extends Thread { /** * Node for the summary tree generated from the thread dumps.

* * The tree keeps track of sample counts for stack trace frames. */ public static class SampleNode { /** The map of children, with their keys used as map keys. */ private Map m_children = new LinkedHashMap<>(); /** The key for the node. */ private Object m_key; /** The sample count. */ private long m_samples; /** * Creates a new node.

* * @param key the key of the node, identifying it among its siblings */ public SampleNode(Object key) { m_key = key; } /** * Compare nodes by descending sample count.

* * @param a first node * @param b second node * @return the comparison result */ public static int compareBySamplesDescending(SampleNode a, SampleNode b) { return -Long.compare(a.getSamples(), b.getSamples()); } /** * Increment sample count for all nodes along the given path.

* * @param root the root node * @param path a sequence of keys constituting a path in the tree */ public static void incrementPath(SampleNode root, List path) { List nodes = nodesForPath(root, path); for (SampleNode node : nodes) { node.increment(); } } /** * Given a path consisting of a list of node keys, this method collects all nodes along that path, * including the given root node, and creates nodes if they don't exist in the tree yet.

* * @param root the root of the tree * @param path the path * * @return the sequence of nodes along the path */ public static List nodesForPath(SampleNode root, List path) { List result = new ArrayList<>(); result.add(root); SampleNode current = root; for (Object key : path) { current = current.getOrAddChild(key); result.add(current); } return result; } /** * Dumps the tree node to XML.

* * @param parent the parent XML to append the XML to */ public void appendToXml(Element parent) { Element element = parent.addElement("location").addAttribute("key", "" + m_key).addAttribute( "samples", "" + m_samples); for (SampleNode child : m_children.values()) { child.appendToXml(element); } } /** * Gets the key of the node.

* * @return the key of the node */ public Object getKey() { return m_key; } /** * Gets the child node for the given key, creating it it it doesn't exist yet.

* * @param key the key * @return the child for the key */ public SampleNode getOrAddChild(Object key) { SampleNode child = m_children.get(key); if (child == null) { child = new SampleNode(key); m_children.put(key, child); } return child; } /** * Returns the sample count.

* * @return the sample count */ public long getSamples() { return m_samples; } /** * Check if this node has children.

* * @return true if this node has children */ public boolean hasChildren() { return m_children.size() > 0; } /** * Increments the sample count for this node.

*/ public void increment() { m_samples += 1; } /** * Sorts the children of this node by descending sample count.

*/ public void sortChildren() { ArrayList children = new ArrayList<>(m_children.values()); children.sort(SampleNode::compareBySamplesDescending); m_children.clear(); for (SampleNode node : children) { m_children.put(node.getKey(), node); } } /** * Recursively sorts this node's and all its descendants' children.

*/ public void sortTree() { sortChildren(); for (SampleNode child : m_children.values()) { child.sortTree(); } } } /** The file name to save the zip file to. */ private String m_filename; /** The file name for the summary file. */ private String m_summaryFilename; /** The id of the thread to monitor. */ private long m_threadId; /** The thread management bean. */ private ThreadMXBean m_threadMx; /** * Creates a new instance.

* * @param filename the name of the zip file to generate * @param summaryFilename the name of the summary file to generate * @param id the id of the thread to monitor */ public CmsSingleThreadDumperThread(String filename, String summaryFilename, long id) { super("" + CmsSingleThreadDumperThread.class.getName()); m_threadMx = ManagementFactory.getThreadMXBean(); m_threadId = id; m_filename = filename; m_summaryFilename = summaryFilename; // If startup process fails, we don't want this thread to hang around setDaemon(true); } /** * @see java.lang.Thread#run() */ @Override public void run() { int count = 1; SampleNode root = new SampleNode("ROOT"); try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(m_filename))) { while (!isInterrupted()) { ThreadInfo info = m_threadMx.getThreadInfo(m_threadId, Integer.MAX_VALUE); List path = Lists.reverse(Arrays.asList(info.getStackTrace())); SampleNode.incrementPath(root, path); StringBuffer buffer = new StringBuffer(); buffer.append(formatThreadInfo(info)); byte[] dumpData = buffer.toString().getBytes("UTF-8"); ZipEntry entry = new ZipEntry("dump_" + count + ".txt"); zip.putNextEntry(entry); zip.write(dumpData); count += 1; Thread.sleep(10); } } catch (InterruptedException e) { return; } catch (Exception e) { e.printStackTrace(); } finally { saveSummaryXml(root); } } /** * Formats the thread information.

* * @param info thread information bean * @return the formatted thread info */ String formatThreadInfo(ThreadInfo info) { StringBuilder sb = new StringBuilder( "\"" + info.getThreadName() + "\"" + " Id=" + info.getThreadId() + " " + info.getThreadState()); if (info.getLockName() != null) { sb.append(" on " + info.getLockName()); } if (info.getLockOwnerName() != null) { sb.append(" owned by \"" + info.getLockOwnerName() + "\" Id=" + info.getLockOwnerId()); } if (info.isSuspended()) { sb.append(" (suspended)"); } if (info.isInNative()) { sb.append(" (in native)"); } sb.append('\n'); int i = 0; for (; (i < info.getStackTrace().length); i++) { StackTraceElement ste = info.getStackTrace()[i]; sb.append("\tat " + ste.toString()); sb.append('\n'); if ((i == 0) && (info.getLockInfo() != null)) { Thread.State ts = info.getThreadState(); switch (ts) { case BLOCKED: sb.append("\t- blocked on " + info.getLockInfo()); sb.append('\n'); break; case WAITING: sb.append("\t- waiting on " + info.getLockInfo()); sb.append('\n'); break; case TIMED_WAITING: sb.append("\t- waiting on " + info.getLockInfo()); sb.append('\n'); break; default: } } for (MonitorInfo mi : info.getLockedMonitors()) { if (mi.getLockedStackDepth() == i) { sb.append("\t- locked " + mi); sb.append('\n'); } } } return sb.toString(); } /** * Saves the stack trace summary tree starting from the given root node to an XML file.

* * @param root the root node */ private void saveSummaryXml(SampleNode root) { root.sortTree(); Document doc = DocumentHelper.createDocument(); Element rootElem = doc.addElement("root"); root.appendToXml(rootElem); OutputFormat outformat = OutputFormat.createPrettyPrint(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); outformat.setEncoding("UTF-8"); try { XMLWriter writer = new XMLWriter(buffer, outformat); writer.write(doc); writer.flush(); try (FileOutputStream fos = new FileOutputStream(m_summaryFilename)) { fos.write(buffer.toByteArray()); } } catch (Exception e) { e.printStackTrace(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy