fixed bugs, reduced memory overhead

This commit is contained in:
Sander Hautvast 2018-11-03 21:17:33 +01:00
parent 77404d0be6
commit 63b8694eb2
7 changed files with 53 additions and 81 deletions

View file

@ -4,25 +4,19 @@ package perfix;
* contains start and stop time for method/query/servlet * contains start and stop time for method/query/servlet
*/ */
public class MethodInvocation { public class MethodInvocation {
private final long t0; private final long timestamp;
private final String name; long duration;
long t1;
MethodInvocation(String name) { MethodInvocation(String name) {
t0 = System.nanoTime(); timestamp = System.nanoTime();
if (name != null) {
this.name = name;
} else {
this.name = "<error occurred>";
}
} }
String getName() { public long getDuration() {
return name; return duration;
} }
long getDuration() { public void registerEndingTime(long t1) {
return t1 - t0; duration = t1 - timestamp;
} }
} }

View file

@ -8,14 +8,14 @@ public class MethodNode {
public final String name; public final String name;
public final List<MethodNode> children; public final List<MethodNode> children;
public MethodNode parent; public MethodNode parent;
public Report report; private MethodInvocation invocation;
public MethodNode(String name) { public MethodNode(String name) {
this.name = name; this.name = name;
this.children = new ArrayList<>(); this.children = new ArrayList<>();
} }
public void addChild(MethodNode child){ public void addChild(MethodNode child) {
children.add(child); children.add(child);
} }
@ -23,10 +23,6 @@ public class MethodNode {
return name; return name;
} }
public Report getReport() {
return report;
}
public List<MethodNode> getChildren() { public List<MethodNode> getChildren() {
return children; return children;
} }
@ -51,5 +47,11 @@ public class MethodNode {
return Objects.hash(name); return Objects.hash(name);
} }
public MethodInvocation getInvocation() {
return invocation;
}
public void setInvocation(MethodInvocation invocation) {
this.invocation = invocation;
}
} }

View file

@ -5,11 +5,9 @@ import perfix.instrument.Util;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.LongAdder;
public class Registry { public class Registry {
private static final Map<String, List<MethodInvocation>> methods = new ConcurrentHashMap<>();
private static final List<MethodNode> callstack = new ArrayList<>(); private static final List<MethodNode> callstack = new ArrayList<>();
private static final ThreadLocal<MethodNode> currentMethod = new ThreadLocal<>(); private static final ThreadLocal<MethodNode> currentMethod = new ThreadLocal<>();
@ -26,7 +24,7 @@ public class Registry {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static MethodInvocation start(String name) { public static MethodInvocation start(String name) {
MethodInvocation methodInvocation = new MethodInvocation(name); MethodInvocation methodInvocation = new MethodInvocation(name);
MethodNode newNode = new MethodNode(methodInvocation.getName()); MethodNode newNode = new MethodNode(name);
MethodNode parent = currentMethod.get(); MethodNode parent = currentMethod.get();
if (parent != null) { if (parent != null) {
@ -52,44 +50,43 @@ public class Registry {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static void stop(MethodInvocation methodInvocation) { public static void stop(MethodInvocation methodInvocation) {
if (methodInvocation != null) { if (methodInvocation != null) {
methodInvocation.t1 = System.nanoTime(); methodInvocation.registerEndingTime(System.nanoTime());
methods.computeIfAbsent(methodInvocation.getName(), key -> new ArrayList<>()).add(methodInvocation);
} }
MethodNode methodNode = currentMethod.get(); MethodNode methodNode = currentMethod.get();
if (methodNode != null) { methodNode.setInvocation(methodInvocation);
currentMethod.set(methodNode.parent); currentMethod.set(methodNode.parent);
} }
}
public static SortedMap<Long, Report> sortedMethodsByDuration() { public static SortedMap<Long, Report> sortedMethodsByDuration() {
//walk the stack to group methods by their name
Map<String, List<MethodInvocation>> methods = new ConcurrentHashMap<>();
collectInvocationsPerMethodName(methods, callstack);
//gather invocations by method name and calculate statistics
SortedMap<Long, Report> sortedByTotal = new ConcurrentSkipListMap<>(Comparator.reverseOrder()); SortedMap<Long, Report> sortedByTotal = new ConcurrentSkipListMap<>(Comparator.reverseOrder());
methods.forEach((name, measurements) -> { methods.forEach((name, measurements) -> {
LongAdder totalDuration = new LongAdder(); long totalDuration = measurements.stream()
measurements.stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)
.forEach(m -> totalDuration.add(m.getDuration())); .mapToLong(MethodInvocation::getDuration).sum();
sortedByTotal.put(totalDuration.longValue(), new Report(name, measurements.size(), totalDuration.longValue())); sortedByTotal.put(totalDuration, new Report(name, measurements.size(), totalDuration));
}); });
return sortedByTotal; return sortedByTotal;
} }
private static void collectInvocationsPerMethodName(Map<String, List<MethodInvocation>> invocations, List<MethodNode> nodes) {
nodes.forEach(methodNode -> {
invocations.computeIfAbsent(methodNode.getName(), key -> new ArrayList<>()).add(methodNode.getInvocation());
collectInvocationsPerMethodName(invocations, methodNode.children);
});
}
public static List<MethodNode> getCallStack() { public static List<MethodNode> getCallStack() {
addReport(callstack);
return callstack; return callstack;
} }
private static void addReport(List<MethodNode> callstack) {
callstack.forEach(methodNode -> {
LongAdder totalDuration = new LongAdder();
List<MethodInvocation> methodInvocations = methods.get(methodNode.name);
methodInvocations.forEach(methodInvocation -> totalDuration.add(methodInvocation.getDuration()));
methodNode.report = new Report(methodNode.name, methodInvocations.size(), totalDuration.longValue());
addReport(methodNode.children);
});
}
public static void clear() { public static void clear() {
methods.clear();
callstack.clear(); callstack.clear();
} }
} }

View file

@ -102,7 +102,7 @@ public class SynthSerializerFactory implements SerializerFactory {
/* /*
* Creates the source, handling the for JSON different types of classes * Creates the source, handling the for JSON different types of classes
*/ */
private <T> String createToJSONStringMethodSource(CtClass beanClass) throws NotFoundException { private String createToJSONStringMethodSource(CtClass beanClass) throws NotFoundException {
String source = "public String handle(Object object){\n"; String source = "public String handle(Object object){\n";
if (beanClass.isArray()) { if (beanClass.isArray()) {
source += "\tObject[] array=(Object[])object;\n"; source += "\tObject[] array=(Object[])object;\n";
@ -191,14 +191,10 @@ public class SynthSerializerFactory implements SerializerFactory {
} }
private boolean isCollection(CtClass beanClass) throws NotFoundException { private boolean isCollection(CtClass beanClass) throws NotFoundException {
List<CtClass> interfaces = new ArrayList<CtClass>(asList(beanClass.getInterfaces())); List<CtClass> interfaces = new ArrayList<>(asList(beanClass.getInterfaces()));
interfaces.add(beanClass); interfaces.add(beanClass);
for (CtClass interfaze : interfaces) { boolean is = interfaces.stream().anyMatch(interfaze -> interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET));
if (interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET)) { return is;
return true;
}
}
return false;
} }
private boolean isMap(CtClass beanClass) throws NotFoundException { private boolean isMap(CtClass beanClass) throws NotFoundException {
@ -212,7 +208,7 @@ public class SynthSerializerFactory implements SerializerFactory {
*/ */
private String addPair(CtClass classToSerialize, String source, CtMethod getter) throws NotFoundException { private String addPair(CtClass classToSerialize, String source, CtMethod getter) throws NotFoundException {
source += jsonKey(getter); source += jsonKey(getter);
source += ": "; // what is the rule when it comes to spaces in json? source += ":";
source += jsonValue(classToSerialize, getter); source += jsonValue(classToSerialize, getter);
return source; return source;
} }

View file

@ -22,7 +22,7 @@ public class App {
try { try {
someJdbcStatementMethod(); someJdbcStatementMethod();
someJdbcPreparedStatementMethod(); someJdbcPreparedStatementMethod();
someOtherMethod(); someOtherMethod(0);
TimeUnit.SECONDS.sleep(1); TimeUnit.SECONDS.sleep(1);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -31,7 +31,7 @@ public class App {
private static void someJdbcStatementMethod() { private static void someJdbcStatementMethod() {
try { try {
BasicDataSource dataSource=new BasicDataSource(); BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.h2.Driver"); dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:default"); dataSource.setUrl("jdbc:h2:mem:default");
dataSource.setUsername("sa"); dataSource.setUsername("sa");
@ -61,11 +61,15 @@ public class App {
} }
} }
private static void someOtherMethod() { private static void someOtherMethod(int level) {
try { try {
TimeUnit.NANOSECONDS.sleep(1); TimeUnit.NANOSECONDS.sleep(1);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (level < 1000) {
someOtherMethod(level + 1);
}
} }
} }

View file

@ -26,35 +26,15 @@ body {
left:-20px; left:-20px;
} }
.tree li input ~ ul{ .tree li input ~ ul{
display: none; display: none;
height: 0; height: 0;
} }
/*
.tree input {
background: url(toggle-small-expand.png) no-repeat;
height: 1em;
content: "\00a0\00a0\00a0";
overflow: hidden;
width: 16px;
height: 16px;
}
.tree input:checked{
background: url(toggle-small.png) no-repeat;
}*/
.tree li input:checked ~ ul{ .tree li input:checked ~ ul{
display: inline; display: inline;
} }
/* .tree li input:checked { display: block; margin: 0 0 0.125em; 2px} */
/* .tree li input:checked li:last-child { margin: 0 0 0.063em; 1px } */
.tree ul {margin-left:1em} /* (indentation/2) */ .tree ul {margin-left:1em} /* (indentation/2) */
.tree li:before { .tree li:before {

View file

@ -24,10 +24,9 @@ class Tree extends Component {
return (<ul className="tree"> return (<ul className="tree">
{children.map( {children.map(
r => r =>
<li key={r.report.name}><input type="checkbox" className="tree"></input> <li key={r.name}><input type="checkbox" className="tree"/>
{Math.floor(r.report.average / 1000) / 1000} ms &nbsp; {Math.floor(r.invocation.duration / 1000) / 1000} ms &nbsp;
- {r.report.invocations} inv. &nbsp; {r.name}
{r.report.name}
{this.renderChildren(r.children)} {this.renderChildren(r.children)}
</li> </li>
)} )}