/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.http; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONStreamAware; import javax.servlet.http.HttpServletRequest; import java.lang.management.LockInfo; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; /** * <p>The GetStackTraces API will return the current stack trace for * each Nxt thread.</p> * * <p>Request parameters:</p> * <ul> * <li>depth - Stack trace depth (minimum 1, defaults to full trace)</li> * </ul> * * <p>Response parameters:</p> * <ul> * <li>locks - An array of lock objects for locks with waiters</li> * <li>threads - An array of thread objects</li> * </ul> * * <p>Lock object:</p> * <ul> * <li>name - Lock class name</li> * <li>hash - Lock identity hash code</li> * <li>thread - Identifier of thread holding the lock</li> * </ul> * * <p>Monitor object:</p> * <ul> * <li>name - Monitor class name</li> * <li>hash - Monitor identity hash</li> * <li>depth - Stack depth where monitor locked</li> * <li>trace - Stack element where monitor locked</li> * </ul> * * <p>Thread object:</p> * <ul> * <li>blocked - Lock object if thread is waiting on a lock</li> * <li>id - Thread identifier</li> * <li>locks - Array of monitor objects for locks held by this thread</li> * <li>name - Thread name</li> * <li>state - Thread state</li> * <li> trace - Array of stack trace elements</li> * </ul> */ public class GetStackTraces extends APIServlet.APIRequestHandler { /** GetLog instance */ static final GetStackTraces instance = new GetStackTraces(); /** * Create the GetStackTraces instance */ private GetStackTraces() { super(new APITag[] {APITag.DEBUG}, "depth"); } /** * Process the GetStackTraces API request * * @param req API request * @return API response */ @Override JSONStreamAware processRequest(HttpServletRequest req) { String value; // // Get the number of trace lines to return // int depth; value = req.getParameter("depth"); if (value != null) depth = Math.max(Integer.valueOf(value), 1); else depth = Integer.MAX_VALUE; // // Get the thread information // JSONArray threadsJSON = new JSONArray(); JSONArray locksJSON = new JSONArray(); ThreadMXBean tmxBean = ManagementFactory.getThreadMXBean(); boolean tmxMI = tmxBean.isObjectMonitorUsageSupported(); ThreadInfo[] tList = tmxBean.dumpAllThreads(tmxMI, false); // // Generate the response // for (ThreadInfo tInfo : tList) { JSONObject threadJSON = new JSONObject(); // // General thread information // threadJSON.put("id", tInfo.getThreadId()); threadJSON.put("name", tInfo.getThreadName()); threadJSON.put("state", tInfo.getThreadState().toString()); // // Gather lock usage // if (tmxMI) { MonitorInfo[] mList = tInfo.getLockedMonitors(); if (mList.length > 0) { JSONArray monitorsJSON = new JSONArray(); for (MonitorInfo mInfo : mList) { JSONObject lockJSON = new JSONObject(); lockJSON.put("name", mInfo.getClassName()); lockJSON.put("hash", mInfo.getIdentityHashCode()); lockJSON.put("depth", mInfo.getLockedStackDepth()); lockJSON.put("trace", mInfo.getLockedStackFrame().toString()); monitorsJSON.add(lockJSON); } threadJSON.put("locks", monitorsJSON); } if (tInfo.getThreadState() == Thread.State.BLOCKED) { LockInfo lInfo = tInfo.getLockInfo(); if (lInfo != null) { JSONObject lockJSON = new JSONObject(); lockJSON.put("name", lInfo.getClassName()); lockJSON.put("hash", lInfo.getIdentityHashCode()); lockJSON.put("thread", tInfo.getLockOwnerId()); threadJSON.put("blocked", lockJSON); boolean addLock = true; for (Object lock : locksJSON){ if (((JSONObject)lock).get("name").equals(lInfo.getClassName())) { addLock = false; break; } } if (addLock) locksJSON.add(lockJSON); } } } // // Add the stack trace // StackTraceElement[] elements = tInfo.getStackTrace(); JSONArray traceJSON = new JSONArray(); int ix = 0; for (StackTraceElement element : elements) { traceJSON.add(element.toString()); if (++ix == depth) break; } threadJSON.put("trace", traceJSON); // // Add the thread to the response // threadsJSON.add(threadJSON); } // // Return the response // JSONObject response = new JSONObject(); response.put("threads", threadsJSON); response.put("locks", locksJSON); return response; } /** * Require the administrator password * * @return TRUE if the admin password is required */ @Override boolean requirePassword() { return true; } @Override boolean allowRequiredBlockParameters() { return false; } @Override boolean requireBlockchain() { return false; } }