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
*/
public class MethodInvocation {
private final long t0;
private final String name;
long t1;
private final long timestamp;
long duration;
MethodInvocation(String name) {
t0 = System.nanoTime();
if (name != null) {
this.name = name;
} else {
this.name = "<error occurred>";
}
}
String getName() {
return name;
}
long getDuration() {
return t1 - t0;
}
timestamp = System.nanoTime();
}
public long getDuration() {
return duration;
}
public void registerEndingTime(long t1) {
duration = t1 - timestamp;
}
}

View file

@ -8,7 +8,7 @@ public class MethodNode {
public final String name;
public final List<MethodNode> children;
public MethodNode parent;
public Report report;
private MethodInvocation invocation;
public MethodNode(String name) {
this.name = name;
@ -23,10 +23,6 @@ public class MethodNode {
return name;
}
public Report getReport() {
return report;
}
public List<MethodNode> getChildren() {
return children;
}
@ -51,5 +47,11 @@ public class MethodNode {
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.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.LongAdder;
public class Registry {
private static final Map<String, List<MethodInvocation>> methods = new ConcurrentHashMap<>();
private static final List<MethodNode> callstack = new ArrayList<>();
private static final ThreadLocal<MethodNode> currentMethod = new ThreadLocal<>();
@ -26,7 +24,7 @@ public class Registry {
@SuppressWarnings("unused")
public static MethodInvocation start(String name) {
MethodInvocation methodInvocation = new MethodInvocation(name);
MethodNode newNode = new MethodNode(methodInvocation.getName());
MethodNode newNode = new MethodNode(name);
MethodNode parent = currentMethod.get();
if (parent != null) {
@ -52,44 +50,43 @@ public class Registry {
@SuppressWarnings("unused")
public static void stop(MethodInvocation methodInvocation) {
if (methodInvocation != null) {
methodInvocation.t1 = System.nanoTime();
methods.computeIfAbsent(methodInvocation.getName(), key -> new ArrayList<>()).add(methodInvocation);
methodInvocation.registerEndingTime(System.nanoTime());
}
MethodNode methodNode = currentMethod.get();
if (methodNode != null) {
methodNode.setInvocation(methodInvocation);
currentMethod.set(methodNode.parent);
}
}
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());
methods.forEach((name, measurements) -> {
LongAdder totalDuration = new LongAdder();
measurements.stream()
long totalDuration = measurements.stream()
.filter(Objects::nonNull)
.forEach(m -> totalDuration.add(m.getDuration()));
sortedByTotal.put(totalDuration.longValue(), new Report(name, measurements.size(), totalDuration.longValue()));
.mapToLong(MethodInvocation::getDuration).sum();
sortedByTotal.put(totalDuration, new Report(name, measurements.size(), totalDuration));
});
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() {
addReport(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() {
methods.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
*/
private <T> String createToJSONStringMethodSource(CtClass beanClass) throws NotFoundException {
private String createToJSONStringMethodSource(CtClass beanClass) throws NotFoundException {
String source = "public String handle(Object object){\n";
if (beanClass.isArray()) {
source += "\tObject[] array=(Object[])object;\n";
@ -191,14 +191,10 @@ public class SynthSerializerFactory implements SerializerFactory {
}
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);
for (CtClass interfaze : interfaces) {
if (interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET)) {
return true;
}
}
return false;
boolean is = interfaces.stream().anyMatch(interfaze -> interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET));
return is;
}
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 {
source += jsonKey(getter);
source += ": "; // what is the rule when it comes to spaces in json?
source += ":";
source += jsonValue(classToSerialize, getter);
return source;
}

View file

@ -22,7 +22,7 @@ public class App {
try {
someJdbcStatementMethod();
someJdbcPreparedStatementMethod();
someOtherMethod();
someOtherMethod(0);
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
@ -61,11 +61,15 @@ public class App {
}
}
private static void someOtherMethod() {
private static void someOtherMethod(int level) {
try {
TimeUnit.NANOSECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (level < 1000) {
someOtherMethod(level + 1);
}
}
}

View file

@ -26,35 +26,15 @@ body {
left:-20px;
}
.tree li input ~ ul{
display: none;
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{
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 li:before {

View file

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