fixed bugs, reduced memory overhead
This commit is contained in:
parent
77404d0be6
commit
63b8694eb2
7 changed files with 53 additions and 81 deletions
|
|
@ -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() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
long getDuration() {
|
|
||||||
return t1 - t0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getDuration() {
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerEndingTime(long t1) {
|
||||||
|
duration = t1 - timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ 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;
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
{Math.floor(r.invocation.duration / 1000) / 1000} ms
|
||||||
- {r.report.invocations} inv.
|
{r.name}
|
||||||
{r.report.name}
|
|
||||||
{this.renderChildren(r.children)}
|
{this.renderChildren(r.children)}
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue