提交 8c586588 authored 作者: Thomas Mueller's avatar Thomas Mueller

Simple built-in profiler

上级 e3840433
......@@ -7,16 +7,10 @@
package org.h2.server.web;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.security.SecureClassLoader;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ParameterMetaData;
......@@ -56,6 +50,7 @@ import org.h2.util.MathUtils;
import org.h2.util.MemoryUtils;
import org.h2.util.New;
import org.h2.util.ObjectArray;
import org.h2.util.Profiler;
import org.h2.util.ScriptReader;
import org.h2.util.SortedProperties;
import org.h2.util.StatementBuilder;
......@@ -79,6 +74,8 @@ public class WebApp implements DatabaseEventListener {
protected boolean stop;
protected String headerLanguage;
protected Profiler profiler;
WebApp(WebServer server) {
this.server = server;
}
......@@ -794,9 +791,19 @@ public class WebApp implements DatabaseEventListener {
session.put("user", user);
boolean isH2 = url.startsWith("jdbc:h2:");
try {
Profiler profiler = new Profiler();
profiler.startCollecting();
Connection conn = server.getConnection(driver, url, user, password, this);
JdbcUtils.closeSilently(conn);
session.put("error", "${text.login.testSuccessful}");
profiler.stopCollecting();
String success = "<a class=\"error\" href=\"#\" onclick=\"var x=document.getElementById('prof').style;x.display=x.display==''?'none':'';\">" +
"${text.login.testSuccessful}</a>" +
"<span style=\"display: none;\" id=\"prof\"><br />" +
PageParser.escapeHtml(profiler.getTop(1)) +
"</span>";
session.put("error", success);
// session.put("error", "${text.login.testSuccessful}");
return "login.jsp";
} catch (Exception e) {
session.put("error", getLoginError(e, isH2));
......@@ -884,17 +891,7 @@ public class WebApp implements DatabaseEventListener {
try {
Connection conn = session.getConnection();
String result;
if (sql.startsWith("@JAVA")) {
if (server.getAllowScript()) {
try {
result = executeJava(sql.substring("@JAVA".length()));
} catch (Throwable t) {
result = getStackTrace(0, t, false);
}
} else {
result = "Executing Java code is not allowed, use command line parameter -webScript";
}
} else if ("@AUTOCOMMIT TRUE".equals(sql)) {
if ("@AUTOCOMMIT TRUE".equals(sql)) {
conn.setAutoCommit(true);
result = "${text.result.autoCommitOn}";
} else if ("@AUTOCOMMIT FALSE".equals(sql)) {
......@@ -942,79 +939,6 @@ public class WebApp implements DatabaseEventListener {
return "result.jsp";
}
/**
* This class allows to load Java code dynamically.
*/
static class DynamicClassLoader extends SecureClassLoader {
private String name;
private byte[] data;
private Class< ? > clazz;
DynamicClassLoader(String name, byte[] data) {
super(DynamicClassLoader.class.getClassLoader());
this.name = name;
this.data = data;
}
public Class< ? > loadClass(String className) throws ClassNotFoundException {
return findClass(className);
}
public Class< ? > findClass(String className) throws ClassNotFoundException {
if (className.equals(name)) {
if (clazz == null) {
clazz = defineClass(className, data, 0, data.length);
}
return clazz;
}
try {
return findSystemClass(className);
} catch (Exception e) {
// ignore
}
return super.findClass(className);
}
}
private String executeJava(String code) throws Exception {
File javaFile = new File("Java.java");
File classFile = new File("Java.class");
try {
PrintWriter out = new PrintWriter(new FileWriter(javaFile));
classFile.delete();
int endImport = code.indexOf("@CODE");
String importCode = "import java.util.*; import java.math.*; import java.sql.*;";
if (endImport >= 0) {
importCode = code.substring(0, endImport);
code = code.substring("@CODE".length() + endImport);
}
out.println(importCode);
out.println("public class Java { public static Object run() throws Throwable {" + code + "}}");
out.close();
Class< ? > javacClass = Class.forName("com.sun.tools.javac.Main");
Method compile = javacClass.getMethod("compile", String[].class);
Object javac = javacClass.newInstance();
compile.invoke(javac, new Object[] { new String[]{"Java.java"}});
byte[] data = new byte[(int) classFile.length()];
DataInputStream in = new DataInputStream(new FileInputStream(classFile));
in.readFully(data);
in.close();
DynamicClassLoader cl = new DynamicClassLoader("Java", data);
Class< ? > clazz = cl.loadClass("Java");
Method[] methods = clazz.getMethods();
for (Method m : methods) {
if (m.getName().equals("run")) {
return "" + m.invoke(null);
}
}
return null;
} finally {
javaFile.delete();
classFile.delete();
}
}
private String editResult() {
ResultSet rs = session.result;
int row = Integer.parseInt(attributes.getProperty("row"));
......@@ -1255,6 +1179,21 @@ public class WebApp implements DatabaseEventListener {
String[] p = split(sql);
return meta.getAttributes(p[1], p[2], p[3], p[4]);
//## Java 1.4 end ##
} else if (sql.startsWith("@PROF_START")) {
if (profiler != null) {
profiler.stopCollecting();
}
profiler = new Profiler();
profiler.startCollecting();
} else if (sql.startsWith("@PROF_STOP")) {
if (profiler != null) {
profiler.stopCollecting();
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("Top Stack Trace(s)", Types.VARCHAR, 0, 0);
rs.addRow(profiler.getTop(1));
profiler = null;
return rs;
}
}
return null;
}
......
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.h2.engine.Constants;
/**
* A simple CPU profiling tool similar to java -Xrunhprof.
*/
public class Profiler implements Runnable {
private static final int MAX_ELEMENTS = 1000;
private int interval = 10;
private int depth = 16;
private String[] ignoreLines = StringUtils.arraySplit("", ',', true);
private String[] ignoreThreads = StringUtils.arraySplit(
"java.net.,java.lang.Thread.", ',', true);
private volatile boolean stop;
private HashMap<String, Integer> counts = new HashMap<String, Integer>();
private int minCount = 1;
private int total;
private Thread thread;
/**
* Start collecting profiling data.
*/
public void startCollecting() {
thread = new Thread(this);
thread.setName("Profiler");
thread.setDaemon(true);
thread.start();
}
/**
* Stop collecting.
*/
public void stopCollecting() {
stop = true;
}
public void run() {
while (!stop) {
if (interval > 0) {
try {
Thread.sleep(interval);
} catch (Exception e) {
// ignore
}
}
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
for (Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
Thread t = entry.getKey();
if (t.getState() != Thread.State.RUNNABLE) {
continue;
}
StackTraceElement[] dump = entry.getValue();
if (dump.length == 0) {
continue;
}
boolean ignoreThis = false;
for (String ig : ignoreThreads) {
if (ig.length() > 0 && dump[0].toString().startsWith(ig)) {
ignoreThis = true;
break;
}
}
if (ignoreThis) {
continue;
}
StringBuilder buff = new StringBuilder();
// simple recursive calls are ignored
String last = null;
for (int j = 0, i = 0; i < dump.length && j < depth; i++) {
String el = dump[i].toString();
ignoreThis = false;
for (String ig : ignoreLines) {
if (ig.length() > 0 && el.startsWith(ig)) {
ignoreThis = true;
break;
}
}
if (!ignoreThis && !el.equals(last)) {
last = el;
buff.append("at ").append(el).append('\n');
j++;
}
}
if (buff.length() > 0) {
increment(buff.toString());
}
}
}
}
private void increment(String trace) {
total++;
Integer oldCount = counts.get(trace);
if (oldCount == null) {
counts.put(trace, 1);
} else {
counts.put(trace, oldCount + 1);
}
if (counts.size() > MAX_ELEMENTS) {
for (Iterator<Map.Entry<String, Integer>> ei = counts.entrySet().iterator(); ei.hasNext();) {
Map.Entry<String, Integer> e = ei.next();
if (e.getValue() <= minCount) {
ei.remove();
}
}
if (counts.size() > MAX_ELEMENTS) {
minCount++;
}
}
}
/**
* Get the top stack traces.
*
* @param max the maximum number of results
* @return the stack traces.
*/
public String getTop(int max) {
StringBuilder buff = new StringBuilder();
buff.append("Profiler: top stack trace(s) [build-").append(Constants.BUILD_ID).append("]\n");
@SuppressWarnings("unchecked")
Map.Entry<String, Integer>[] array = new Map.Entry[counts.size()];
counts.entrySet().toArray(array);
Arrays.sort(array, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
return b.getValue() - a.getValue();
}
});
int x = 0, min = 0;
for (Map.Entry<String, Integer> el : array) {
if (++x >= max) {
if (el.getValue() < min) {
break;
}
min = el.getValue();
}
buff.append(el.getValue()).append('/').append(total).
append('\n').append(el.getKey());
}
return buff.toString();
}
}
......@@ -624,4 +624,4 @@ selector descendant isdescendantnode issamenode ischildnode localname
weakreference ancestor junctions wake fills rail sleeps turns grammars straight
answers attachments emails clipboard prioritize tips urgently standby
checklists serves gbif biodiversity wakes taxon ratio ended ipt auckland
galapagos pacific pastebin mystic posting mysticpaste
galapagos pacific pastebin mystic posting mysticpaste reject prof
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论