removed UI and added bound variable inspection
This commit is contained in:
parent
14e759f7fb
commit
78f8d406f4
17 changed files with 193 additions and 423 deletions
|
|
@ -19,10 +19,9 @@
|
|||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testapp/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testapp/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
|
|
|||
2
pom.xml
2
pom.xml
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>perfix</groupId>
|
||||
<artifactId>agent</artifactId>
|
||||
<artifactId>perfix-agent</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
|
|
|||
|
|
@ -40,11 +40,13 @@ public class Registry {
|
|||
});
|
||||
return sortedByTotal;
|
||||
}
|
||||
|
||||
//work in progress
|
||||
// public static Map<String, Set<Report>> getCallStack() {
|
||||
// callstack.forEach((name,children)->{
|
||||
//
|
||||
// });
|
||||
// }
|
||||
public static Map<String, Set<Report>> getCallStack() {
|
||||
callstack.forEach((name, children) -> {
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ import javassist.NotFoundException;
|
|||
import perfix.MutableBoolean;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
|
|
@ -30,9 +32,14 @@ public class ClassInstrumentor extends Instrumentor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void instrumentCode(Instrumentation inst) {
|
||||
inst.addTransformer((classLoader, resource, aClass, protectionDomain, uninstrumentedByteCode)
|
||||
-> instrumentCodeForClass(includes, resource, uninstrumentedByteCode));
|
||||
inst.addTransformer(new ClassFileTransformer() {
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
||||
return instrumentCodeForClass(includes, className, classfileBuffer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private byte[] instrumentCodeForClass(List<String> includes, String resource, byte[] uninstrumentedByteCode) {
|
||||
|
|
|
|||
|
|
@ -8,23 +8,28 @@ import java.util.List;
|
|||
|
||||
public abstract class Instrumentor {
|
||||
static final String JAVA_STRING = "java.lang.String";
|
||||
static final String JAVA_HASHMAP = "java.util.HashMap";
|
||||
static final String PERFIX_METHODINVOCATION_CLASS = "perfix.MethodInvocation";
|
||||
static final String JAVASSIST_FIRST_ARGUMENT_NAME = "$1";
|
||||
static final String JAVASSIST_RETURNVALUE = "$_";
|
||||
|
||||
final ClassPool classpool;
|
||||
final List<String> includes;
|
||||
CtClass perfixMethodInvocationClass;
|
||||
CtClass stringClass;
|
||||
protected CtClass stringClass;
|
||||
protected CtClass hashMapClass;
|
||||
protected CtClass perfixMethodInvocationClass;
|
||||
|
||||
Instrumentor(List<String> includes, ClassPool classPool) {
|
||||
this.includes = includes;
|
||||
this.classpool = classPool;
|
||||
|
||||
try {
|
||||
perfixMethodInvocationClass = getCtClass(PERFIX_METHODINVOCATION_CLASS);
|
||||
stringClass = classpool.get(JAVA_STRING);
|
||||
hashMapClass = classPool.get(JAVA_HASHMAP);
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
//suppress TODO implement trace
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +42,8 @@ public abstract class Instrumentor {
|
|||
return classInstrumentor;
|
||||
}
|
||||
|
||||
public void instrumentCode(Instrumentation inst){}
|
||||
public void instrumentCode(Instrumentation inst) {
|
||||
}
|
||||
|
||||
void instrumentMethod(CtMethod methodToinstrument) {
|
||||
instrumentMethod(methodToinstrument, "\"" + methodToinstrument.getLongName() + "\"");
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ package perfix.instrument;
|
|||
import javassist.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
|
||||
|
|
@ -22,10 +25,15 @@ public class JdbcInstrumentor extends Instrumentor {
|
|||
|
||||
private static final String PERFIX_SQLSTATEMENT_FIELD = "_perfixSqlStatement";
|
||||
private static final String PERFIX_SETSQL_METHOD = "setSqlForPerfix";
|
||||
|
||||
private static CtClass statementTextClass = null;
|
||||
|
||||
JdbcInstrumentor(List<String> includes, ClassPool classPool) {
|
||||
super(includes, classPool);
|
||||
try {
|
||||
statementTextClass = classpool.get("perfix.instrument.StatementText");
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/* Need to enhance interface to be able to set a statement (string) for perfix. */
|
||||
|
|
@ -33,17 +41,20 @@ public class JdbcInstrumentor extends Instrumentor {
|
|||
try {
|
||||
preparedStatementInterface.getDeclaredMethod(PERFIX_SETSQL_METHOD);
|
||||
} catch (NotFoundException e1) {
|
||||
e1.printStackTrace();
|
||||
try {
|
||||
CtMethod setSqlForPerfix = new CtMethod(CtClass.voidType, PERFIX_SETSQL_METHOD, new CtClass[]{stringClass}, preparedStatementInterface);
|
||||
preparedStatementInterface.addMethod(setSqlForPerfix);
|
||||
|
||||
} catch (CannotCompileException e2) {
|
||||
e2.printStackTrace();
|
||||
return uninstrumentedByteCode;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return bytecode(preparedStatementInterface);
|
||||
} catch (CannotCompileException | IOException e) {
|
||||
e.printStackTrace();
|
||||
return uninstrumentedByteCode;
|
||||
}
|
||||
}
|
||||
|
|
@ -79,6 +90,7 @@ public class JdbcInstrumentor extends Instrumentor {
|
|||
return bytecode(classToInstrument);
|
||||
} catch (NotFoundException | CannotCompileException | IOException e) {
|
||||
// suppress
|
||||
e.printStackTrace();
|
||||
}
|
||||
return uninstrumentedByteCode;
|
||||
}
|
||||
|
|
@ -86,22 +98,51 @@ public class JdbcInstrumentor extends Instrumentor {
|
|||
/* */
|
||||
byte[] instrumentJdbcPreparedStatementImpl(CtClass preparedStatementClass, byte[] uninstrumentedByteCode) {
|
||||
try {
|
||||
addPerfixStatementField(preparedStatementClass);
|
||||
addPerfixFields(preparedStatementClass);
|
||||
addPerfixStatementSetter(preparedStatementClass);
|
||||
|
||||
// instrument execute to record query duration
|
||||
stream(preparedStatementClass.getDeclaredMethods(JAVASQL_EXECUTE_METHOD)).forEach(method ->
|
||||
instrumentMethod(method, PERFIX_SQLSTATEMENT_FIELD)
|
||||
instrumentMethod(method, PERFIX_SQLSTATEMENT_FIELD + ".toString()")
|
||||
);
|
||||
|
||||
// instrument executeQuery to record query duration
|
||||
stream(preparedStatementClass.getDeclaredMethods(JAVASQL_EXECUTEQUERY_METHOD)).forEach(method ->
|
||||
instrumentMethod(method, PERFIX_SQLSTATEMENT_FIELD)
|
||||
instrumentMethod(method, PERFIX_SQLSTATEMENT_FIELD + ".toString()")
|
||||
);
|
||||
|
||||
// instrument executeUpdate to record query duration
|
||||
stream(preparedStatementClass.getDeclaredMethods(JAVASQL_EXECUTEUPDATE_METHOD)).forEach(method ->
|
||||
instrumentMethod(method, PERFIX_SQLSTATEMENT_FIELD)
|
||||
instrumentMethod(method, PERFIX_SQLSTATEMENT_FIELD + ".toString()")
|
||||
);
|
||||
|
||||
getDeclaredMethods(preparedStatementClass, "setString", "setObject", "setDate", "setTime", "setTimestamp")
|
||||
.forEach(method -> {
|
||||
try {
|
||||
method.insertBefore(PERFIX_SQLSTATEMENT_FIELD + ".set($1, \"\'\"+$2+\"\'\");");
|
||||
} catch (CannotCompileException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
getDeclaredMethods(preparedStatementClass,
|
||||
"setInt", "setFloat", "setDouble", "setBoolean", "setLong", "setShort")
|
||||
.forEach(method -> {
|
||||
try {
|
||||
method.insertBefore(PERFIX_SQLSTATEMENT_FIELD + ".set($1, $2);");
|
||||
} catch (CannotCompileException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
getDeclaredMethods(preparedStatementClass, "setNull")
|
||||
.forEach(method -> {
|
||||
try {
|
||||
method.insertBefore(PERFIX_SQLSTATEMENT_FIELD + ".set($1, \"[NULL]\");");
|
||||
} catch (CannotCompileException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return bytecode(preparedStatementClass);
|
||||
|
|
@ -111,6 +152,21 @@ public class JdbcInstrumentor extends Instrumentor {
|
|||
}
|
||||
}
|
||||
|
||||
private Stream<CtMethod> getDeclaredMethods(CtClass preparedStatementClass, String... methodnames) {
|
||||
List<CtMethod> methods = new ArrayList<>();
|
||||
for (String methodname : methodnames) {
|
||||
try {
|
||||
methods.addAll(Arrays.asList(preparedStatementClass.getDeclaredMethods(methodname)));
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return methods.stream();
|
||||
}
|
||||
|
||||
|
||||
byte[] instrumentJdbcStatement(CtClass classToInstrument, byte[] uninstrumentedByteCode) {
|
||||
try {
|
||||
|
|
@ -124,14 +180,15 @@ public class JdbcInstrumentor extends Instrumentor {
|
|||
);
|
||||
return bytecode(classToInstrument);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return uninstrumentedByteCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addPerfixStatementField(CtClass preparedStatementClass) throws CannotCompileException {
|
||||
private void addPerfixFields(CtClass preparedStatementClass) throws CannotCompileException, NotFoundException {
|
||||
// add a String field that will contain the statement
|
||||
CtField perfixSqlField = new CtField(stringClass, PERFIX_SQLSTATEMENT_FIELD, preparedStatementClass);
|
||||
CtField perfixSqlField = new CtField(statementTextClass, PERFIX_SQLSTATEMENT_FIELD, preparedStatementClass);
|
||||
perfixSqlField.setModifiers(Modifier.PRIVATE);
|
||||
preparedStatementClass.addField(perfixSqlField);
|
||||
}
|
||||
|
|
@ -140,7 +197,7 @@ public class JdbcInstrumentor extends Instrumentor {
|
|||
// add setter for the new field
|
||||
CtMethod setSqlForPerfix = new CtMethod(CtClass.voidType, PERFIX_SETSQL_METHOD, new CtClass[]{stringClass}, preparedStatementImplClass);
|
||||
setSqlForPerfix.setModifiers(Modifier.PUBLIC);
|
||||
setSqlForPerfix.setBody(PERFIX_SQLSTATEMENT_FIELD + "=" + JAVASSIST_FIRST_ARGUMENT_NAME + ";");
|
||||
setSqlForPerfix.setBody(PERFIX_SQLSTATEMENT_FIELD + "= new perfix.instrument.StatementText(" + JAVASSIST_FIRST_ARGUMENT_NAME + ");");
|
||||
preparedStatementImplClass.addMethod(setSqlForPerfix);
|
||||
}
|
||||
|
||||
|
|
|
|||
67
src/main/java/perfix/instrument/StatementText.java
Normal file
67
src/main/java/perfix/instrument/StatementText.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package perfix.instrument;
|
||||
|
||||
public class StatementText {
|
||||
|
||||
private static final char BOUNDVAR_MARK = '?';
|
||||
|
||||
private final Object[] vars;
|
||||
private final StringBuilder sql;
|
||||
|
||||
public StatementText(String sql) {
|
||||
vars = new Object[countVars(sql)];
|
||||
this.sql = new StringBuilder(sql);
|
||||
}
|
||||
|
||||
public void set(int index, Object value) {
|
||||
if (index < 1 || index > vars.length + 1) {
|
||||
throw new IndexOutOfBoundsException("" + index);
|
||||
}
|
||||
vars[index - 1] = value;
|
||||
}
|
||||
|
||||
public void set(int index, int value){
|
||||
set(index, Integer.toString(value));
|
||||
}
|
||||
|
||||
public void set(int index, long value){
|
||||
set(index, Long.toString(value));
|
||||
}
|
||||
|
||||
public void set(int index, double value){
|
||||
set(index, Double.toString(value));
|
||||
}
|
||||
|
||||
public void set(int index, boolean value){
|
||||
set(index, Boolean.toString(value));
|
||||
}
|
||||
|
||||
public void set(int index, float value){
|
||||
set(index, Float.toString(value));
|
||||
}
|
||||
|
||||
public void set(int index, short value){
|
||||
set(index, Short.toString(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
int found = 0;
|
||||
for (int i = 0; i < sql.length(); i++) {
|
||||
if (sql.charAt(i) == BOUNDVAR_MARK) {
|
||||
sql.deleteCharAt(i);
|
||||
sql.insert(i, vars[found++]);
|
||||
}
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
private int countVars(String sql) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < sql.length(); i++) {
|
||||
if (sql.charAt(i) == BOUNDVAR_MARK) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
|
@ -33,58 +33,34 @@ public class HTTPServer extends NanoHTTPD {
|
|||
return perfixMetrics();
|
||||
case "/callstack":
|
||||
return perfixCallstack();
|
||||
default:
|
||||
return serveStaticContent(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private Response serveStaticContent(String uri) {
|
||||
if (uri.equals("/")) {
|
||||
uri = "/index.html";
|
||||
}
|
||||
try {
|
||||
InputStream stream = getClass().getResourceAsStream(uri);
|
||||
if (stream == null) {
|
||||
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "resource not found");
|
||||
}
|
||||
return newFixedLengthResponse(Response.Status.OK, determineContentType(uri), readFile(stream));
|
||||
} catch (IOException e) {
|
||||
return newFixedLengthResponse(e.toString());
|
||||
default: return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "NOT FOUND");
|
||||
}
|
||||
}
|
||||
|
||||
private Response perfixMetrics() {
|
||||
try {
|
||||
return newFixedLengthResponse(Response.Status.OK, "application/json", Serializer.toJSONString(new ArrayList<>(Registry.sortedMethodsByDuration().values())));
|
||||
return addCors(newFixedLengthResponse(Response.Status.OK, "application/json", Serializer.toJSONString(new ArrayList<>(Registry.sortedMethodsByDuration().values()))));
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return newFixedLengthResponse(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private Response addCors(Response response) {
|
||||
response.addHeader("Access-Control-Allow-Origin","*");
|
||||
response.addHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept");
|
||||
return response;
|
||||
}
|
||||
|
||||
private Response perfixCallstack() {
|
||||
try {
|
||||
return newFixedLengthResponse(Response.Status.OK, "application/json", Serializer.toJSONString(Registry.getCallStack()));
|
||||
return addCors(newFixedLengthResponse(Response.Status.OK, "application/json", Serializer.toJSONString(Registry.getCallStack())));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return newFixedLengthResponse(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private String readFile(InputStream stream) throws IOException {
|
||||
int nbytes = stream.available();
|
||||
byte[] bytes = new byte[nbytes];
|
||||
stream.read(bytes);
|
||||
return new String(bytes);
|
||||
}
|
||||
|
||||
private String determineContentType(String uri) {
|
||||
if (uri.endsWith(".js")) {
|
||||
return "application/javascript";
|
||||
} else if (uri.endsWith(".css")) {
|
||||
return "text/css";
|
||||
} else {
|
||||
return "text/html";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
src/main/resources/d3.js
vendored
2
src/main/resources/d3.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,23 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Perfix console</title>
|
||||
<script src="/d3.js"></script>
|
||||
<script src="/perfix.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/main.css">
|
||||
</head>
|
||||
<body class="datagrid">
|
||||
<table></table>
|
||||
</body>
|
||||
<script>
|
||||
function loadData() {
|
||||
d3.json("/report").then(function (data) {
|
||||
// render the table(s)
|
||||
tabulate(data, ['name', 'invocations', 'totalDuration', 'average']);
|
||||
});
|
||||
}
|
||||
|
||||
loadData();
|
||||
</script>
|
||||
<button type="button" onclick="loadData()">refresh</button>
|
||||
</html>
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
.datagrid table {
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.datagrid {
|
||||
font: normal 12px/150% Arial, Helvetica, sans-serif;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
border: 1px solid #006699;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.datagrid table td, .datagrid table th {
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
.datagrid table thead th {
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0.05, #006699), color-stop(1, #00557F));
|
||||
background: -moz-linear-gradient(center top, #006699 5%, #00557F 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#006699', endColorstr='#00557F');
|
||||
background-color: #006699;
|
||||
color: #ffffff;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
border-left: 1px solid #0070A8;
|
||||
}
|
||||
|
||||
.datagrid table thead th:first-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.datagrid table tbody td {
|
||||
color: #00496B;
|
||||
border-left: 1px solid #E1EEF4;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.datagrid table tbody .alt td {
|
||||
background: #E1EEF4;
|
||||
color: #00496B;
|
||||
}
|
||||
|
||||
.datagrid table tbody td:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.datagrid table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.datagrid table tfoot td div {
|
||||
border-top: 1px solid #006699;
|
||||
background: #E1EEF4;
|
||||
}
|
||||
|
||||
.datagrid table tfoot td {
|
||||
padding: 0;
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.datagrid table tfoot td div {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.datagrid table tfoot td ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.datagrid table tfoot li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.datagrid table tfoot li a {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
margin: 1px;
|
||||
color: #FFFFFF;
|
||||
border: 1px solid #006699;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0.05, #006699), color-stop(1, #00557F));
|
||||
background: -moz-linear-gradient(center top, #006699 5%, #00557F 100%);
|
||||
background-color: #006699;
|
||||
}
|
||||
|
||||
.datagrid table tfoot ul.active, .datagrid table tfoot ul a:hover {
|
||||
text-decoration: none;
|
||||
border-color: #006699;
|
||||
color: #FFFFFF;
|
||||
background: none;
|
||||
background-color: #00557F;
|
||||
}
|
||||
|
||||
div.dhtmlx_window_active, div.dhx_modal_cover_dv {
|
||||
position: fixed !important;
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
function tabulate(data, columns) {
|
||||
d3.select('table').remove();
|
||||
var table = d3.select('body').append('table')
|
||||
var thead = table.append('thead')
|
||||
var tbody = table.append('tbody');
|
||||
|
||||
thead.append('tr')
|
||||
.selectAll('th')
|
||||
.data(columns).enter()
|
||||
.append('th')
|
||||
.text(function (column) { return column; });
|
||||
|
||||
var rows = tbody.selectAll('tr')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('tr');
|
||||
|
||||
rows.selectAll('td')
|
||||
.data(function (row) {
|
||||
return columns.map(function (column) {
|
||||
return {column: column, value: row[column]};
|
||||
});
|
||||
})
|
||||
.enter()
|
||||
.append('td')
|
||||
.text(function (d) { return d.value; });
|
||||
|
||||
return table;
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "testperfix.Main.main(java.lang.String[])",
|
||||
"invocations": 1,
|
||||
"totalDuration": 1414878875,
|
||||
"average": 1.414878875E9
|
||||
},
|
||||
{
|
||||
"name": "testperfix.Main.run()",
|
||||
"invocations": 1,
|
||||
"totalDuration": 1414756159,
|
||||
"average": 1.414756159E9
|
||||
},
|
||||
{
|
||||
"name": "testperfix.Main.someJdbcStatentMethod()",
|
||||
"invocations": 1,
|
||||
"totalDuration": 399009103,
|
||||
"average": 3.99009103E8
|
||||
},
|
||||
{
|
||||
"name": "select CURRENT_DATE() -- simple statement",
|
||||
"invocations": 1,
|
||||
"totalDuration": 99705675,
|
||||
"average": 9.9705675E7
|
||||
},
|
||||
{
|
||||
"name": "testperfix.Main.someJdbcPreparedStatementMethod()",
|
||||
"invocations": 1,
|
||||
"totalDuration": 5880308,
|
||||
"average": 5880308.0
|
||||
},
|
||||
{
|
||||
"name": "testperfix.Main.someOtherMethod()",
|
||||
"invocations": 1,
|
||||
"totalDuration": 1285359,
|
||||
"average": 1285359.0
|
||||
},
|
||||
{
|
||||
"name": "select CURRENT_DATE() -- prepared statement",
|
||||
"invocations": 1,
|
||||
"totalDuration": 98241,
|
||||
"average": 98241.0
|
||||
}
|
||||
]
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
var m = [20, 120, 20, 120],
|
||||
w = 1280 - m[1] - m[3],
|
||||
h = 800 - m[0] - m[2],
|
||||
i = 0,
|
||||
root;
|
||||
|
||||
var tree = d3.layout.tree()
|
||||
.size([h, w]);
|
||||
|
||||
var diagonal = d3.svg.diagonal()
|
||||
.projection(function(d) { return [d.y, d.x]; });
|
||||
|
||||
var vis = d3.select("#body").append("svg:svg")
|
||||
.attr("width", w + m[1] + m[3])
|
||||
.attr("height", h + m[0] + m[2])
|
||||
.append("svg:g")
|
||||
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
|
||||
|
||||
d3.json("flare.json", function(json) {
|
||||
root = json;
|
||||
root.x0 = h / 2;
|
||||
root.y0 = 0;
|
||||
|
||||
function toggleAll(d) {
|
||||
if (d.children) {
|
||||
d.children.forEach(toggleAll);
|
||||
toggle(d);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the display to show a few nodes.
|
||||
root.children.forEach(toggleAll);
|
||||
toggle(root.children[1]);
|
||||
toggle(root.children[1].children[2]);
|
||||
toggle(root.children[9]);
|
||||
toggle(root.children[9].children[0]);
|
||||
|
||||
update(root);
|
||||
});
|
||||
|
||||
function update(source) {
|
||||
var duration = d3.event && d3.event.altKey ? 5000 : 500;
|
||||
|
||||
// Compute the new tree layout.
|
||||
var nodes = tree.nodes(root).reverse();
|
||||
|
||||
// Normalize for fixed-depth.
|
||||
nodes.forEach(function(d) { d.y = d.depth * 180; });
|
||||
|
||||
// Update the nodes…
|
||||
var node = vis.selectAll("g.node")
|
||||
.data(nodes, function(d) { return d.id || (d.id = ++i); });
|
||||
|
||||
// Enter any new nodes at the parent's previous position.
|
||||
var nodeEnter = node.enter().append("svg:g")
|
||||
.attr("class", "node")
|
||||
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
|
||||
.on("click", function(d) { toggle(d); update(d); });
|
||||
|
||||
nodeEnter.append("svg:circle")
|
||||
.attr("r", 1e-6)
|
||||
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
|
||||
|
||||
nodeEnter.append("svg:text")
|
||||
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
|
||||
.text(function(d) { return d.name; })
|
||||
.style("fill-opacity", 1e-6);
|
||||
|
||||
// Transition nodes to their new position.
|
||||
var nodeUpdate = node.transition()
|
||||
.duration(duration)
|
||||
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
|
||||
|
||||
nodeUpdate.select("circle")
|
||||
.attr("r", 4.5)
|
||||
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
|
||||
|
||||
nodeUpdate.select("text")
|
||||
.style("fill-opacity", 1);
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
var nodeExit = node.exit().transition()
|
||||
.duration(duration)
|
||||
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
|
||||
.remove();
|
||||
|
||||
nodeExit.select("circle")
|
||||
.attr("r", 1e-6);
|
||||
|
||||
nodeExit.select("text")
|
||||
.style("fill-opacity", 1e-6);
|
||||
|
||||
// Update the links…
|
||||
var link = vis.selectAll("path.link")
|
||||
.data(tree.links(nodes), function(d) { return d.target.id; });
|
||||
|
||||
// Enter any new links at the parent's previous position.
|
||||
link.enter().insert("svg:path", "g")
|
||||
.attr("class", "link")
|
||||
.attr("d", function(d) {
|
||||
var o = {x: source.x0, y: source.y0};
|
||||
return diagonal({source: o, target: o});
|
||||
})
|
||||
.transition()
|
||||
.duration(duration)
|
||||
.attr("d", diagonal);
|
||||
|
||||
// Transition links to their new position.
|
||||
link.transition()
|
||||
.duration(duration)
|
||||
.attr("d", diagonal);
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
link.exit().transition()
|
||||
.duration(duration)
|
||||
.attr("d", function(d) {
|
||||
var o = {x: source.x, y: source.y};
|
||||
return diagonal({source: o, target: o});
|
||||
})
|
||||
.remove();
|
||||
|
||||
// Stash the old positions for transition.
|
||||
nodes.forEach(function(d) {
|
||||
d.x0 = d.x;
|
||||
d.y0 = d.y;
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle children.
|
||||
function toggle(d) {
|
||||
if (d.children) {
|
||||
d._children = d.children;
|
||||
d.children = null;
|
||||
} else {
|
||||
d.children = d._children;
|
||||
d._children = null;
|
||||
}
|
||||
}
|
||||
|
||||
14
src/test/java/StatementTextTest.java
Normal file
14
src/test/java/StatementTextTest.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import perfix.instrument.StatementText;
|
||||
|
||||
public class StatementTextTest {
|
||||
|
||||
@Test
|
||||
public void testReplace(){
|
||||
StatementText b = new StatementText("select ? from ?");
|
||||
b.set(1, "alpha");
|
||||
b.set(2, "beta");
|
||||
Assert.assertEquals("select alpha from beta",b.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
import org.junit.Test;
|
||||
import perfix.MethodInvocation;
|
||||
import perfix.Registry;
|
||||
|
||||
public class TestMethod {
|
||||
@Test
|
||||
public void testAddMethodToRegistry() {
|
||||
MethodInvocation method = MethodInvocation.start("somename");
|
||||
method.stop();
|
||||
MethodInvocation method2 = MethodInvocation.start("somename");
|
||||
method2.stop();
|
||||
|
||||
Registry.report(System.out);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -42,8 +42,11 @@ public class App {
|
|||
private static void someJdbcPreparedStatementMethod() {
|
||||
try {
|
||||
Connection connection = DriverManager.getConnection("jdbc:h2:mem:default", "sa", "");
|
||||
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("select CURRENT_DATE() -- prepared statement");
|
||||
Statement statement = connection.createStatement();
|
||||
statement.execute("create table t (v varchar(2))");
|
||||
statement.close();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("select * from t where v=? -- prepared statement");
|
||||
preparedStatement.setDate(1, new Date(new java.util.Date().getTime()));
|
||||
preparedStatement.executeQuery();
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue