Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordwagelaar2014-02-27 21:38:04 +0000
committerdwagelaar2014-02-27 21:38:04 +0000
commitc1441969b2666c489f638d499be7eabbffc377fe (patch)
treeb28bc1a617b6a971a481c384ee8c94f782492d10 /plugins/org.eclipse.m2m.atl.emftvm
parent29754183ebc4521d7af450b667fd51ec5f54b7e6 (diff)
downloadorg.eclipse.atl-c1441969b2666c489f638d499be7eabbffc377fe.tar.gz
org.eclipse.atl-c1441969b2666c489f638d499be7eabbffc377fe.tar.xz
org.eclipse.atl-c1441969b2666c489f638d499be7eabbffc377fe.zip
Added EMFTVM Profiler.
Diffstat (limited to 'plugins/org.eclipse.m2m.atl.emftvm')
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF2
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/Profiler.java361
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/StopWatch.java79
3 files changed, 442 insertions, 0 deletions
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF b/plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF
index f9717275..0ae52606 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF
@@ -12,6 +12,8 @@ Export-Package: org.eclipse.m2m.atl.emftvm,
org.eclipse.m2m.atl.emftvm.constraints,
org.eclipse.m2m.atl.emftvm.impl,
org.eclipse.m2m.atl.emftvm.impl.resource,
+ org.eclipse.m2m.atl.emftvm.jit,
+ org.eclipse.m2m.atl.emftvm.profiler,
org.eclipse.m2m.atl.emftvm.util
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.emf.ecore;visibility:=reexport;bundle-version="2.4.0",
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/Profiler.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/Profiler.java
new file mode 100644
index 00000000..156e1790
--- /dev/null
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/Profiler.java
@@ -0,0 +1,361 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Dennis Wagelaar.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Dennis Wagelaar - initial API and
+ * implementation and/or initial documentation
+ *******************************************************************************/
+package org.eclipse.m2m.atl.emftvm.profiler;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.eclipse.m2m.atl.emftvm.CodeBlock;
+import org.eclipse.m2m.atl.emftvm.util.StackFrame;
+import org.eclipse.m2m.atl.emftvm.util.VMMonitor;
+
+/**
+ * Profiler for EMFTVM.
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ */
+public class Profiler implements VMMonitor {
+
+ /**
+ * Key-value pair.
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ *
+ * @param <K>
+ * the key type
+ * @param <V>
+ * the value type
+ */
+ static class Pair<K, V> {
+
+ private K key;
+ private V value;
+
+ public Pair(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * @return the key
+ */
+ public K getKey() {
+ return key;
+ }
+
+ /**
+ * @param key
+ * the key to set
+ */
+ public void setKey(K key) {
+ this.key = key;
+ }
+
+ /**
+ * @return the value
+ */
+ public V getValue() {
+ return value;
+ }
+
+ /**
+ * @param value
+ * the value to set
+ */
+ public void setValue(V value) {
+ this.value = value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "Pair [key=" + key + ", value=" + value + "]";
+ }
+
+ }
+
+ /**
+ * Profiling data for a single operation ({@link CodeBlock} or {@link Method}).
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ */
+ public static class ProfilingData {
+
+ private final CodeBlock codeBlock;
+ private final Method method;
+ private final long duration;
+ private final double durationRatio;
+ private final long invocations;
+
+ /**
+ * Creates a new {@link ProfilingData}.
+ *
+ * @param codeBlock
+ * the {@link CodeBlock}, if applicable
+ * @param method
+ * the native {@link Method}, if applicable
+ * @param duration
+ * the duration spent in nanoseconds
+ * @param durationRatio
+ * the duration ratio to the total execution time
+ * @param invocations
+ * the amount of invocations
+ */
+ public ProfilingData(CodeBlock codeBlock, Method method, long duration, double durationRatio, long invocations) {
+ this.codeBlock = codeBlock;
+ this.method = method;
+ this.duration = duration;
+ this.durationRatio = durationRatio;
+ this.invocations = invocations;
+ }
+
+ /**
+ * Returns the {@link CodeBlock}, if applicable.
+ *
+ * @return the codeBlock, or <code>null</code>
+ */
+ public CodeBlock getCodeBlock() {
+ return codeBlock;
+ }
+
+ /**
+ * Returns the native {@link Method}, if applicable.
+ *
+ * @return the method, or <code>null</code>
+ */
+ public Method getMethod() {
+ return method;
+ }
+
+ /**
+ * Returns the duration spent in nanoseconds.
+ *
+ * @return the duration
+ */
+ public long getDuration() {
+ return duration;
+ }
+
+ /**
+ * Returns the duration ratio to the total execution time.
+ *
+ * @return the durationRatio
+ */
+ public double getDurationRatio() {
+ return durationRatio;
+ }
+
+ /**
+ * Returns the amount of invocations.
+ *
+ * @return the invocations
+ */
+ public long getInvocations() {
+ return invocations;
+ }
+
+ /**
+ * Returns {@link #getCodeBlock()} or {@link #getMethod()}, whichever is not <code>null</code>.
+ * @return {@link #getCodeBlock()}, {@link #getMethod()}, or <code>null</code>
+ */
+ public Object getOperation() {
+ Object operation = getCodeBlock();
+ if (operation == null) {
+ operation = getMethod();
+ }
+ return operation;
+ }
+
+ }
+
+ private static final double DIVISOR = 1E9;
+ private static final double HUNDRED = 1E2;
+
+ private final StopWatch stopWatch = new StopWatch();
+ private final Map<CodeBlock, Pair<Long, StopWatch>> codeBlockTimings = new HashMap<CodeBlock, Pair<Long, StopWatch>>();
+ private final Map<Method, Pair<Long, StopWatch>> nativeMethodTimings = new HashMap<Method, Pair<Long, StopWatch>>();
+ private final Stack<Pair<Long, StopWatch>> timingStack = new Stack<Pair<Long, StopWatch>>();
+ private final List<ProfilingData> results = new ArrayList<ProfilingData>();
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isTerminated() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void enter(final StackFrame frame) {
+ // Start global stopwatch on first entry
+ if (timingStack.isEmpty()) {
+ stopWatch.start();
+ }
+ // Process current operation timings, and pause parent operation timings
+ final Pair<Long, StopWatch> timings = getTimings(frame);
+ if (timings != null) {
+ if (!timingStack.isEmpty()) {
+ Pair<Long, StopWatch> currentTimings = timingStack.peek();
+ if (currentTimings != timings) {
+ currentTimings.getValue().stop();
+ }
+ }
+ timings.setKey(timings.getKey() + 1L);
+ timings.getValue().start();
+ timingStack.push(timings);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void leave(final StackFrame frame) {
+ if (!timingStack.isEmpty()) {
+ final Pair<Long, StopWatch> timings = timingStack.pop();
+ assert timings == getTimings(frame);
+ if (!timingStack.isEmpty()) {
+ Pair<Long, StopWatch> parentTimings = timingStack.peek();
+ if (parentTimings != timings) {
+ timings.getValue().stop();
+ parentTimings.getValue().start();
+ }
+ } else {
+ timings.getValue().stop();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void step(StackFrame frame) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void terminated() {
+ // Stop any running stopwatches
+ while (!timingStack.isEmpty()) {
+ timingStack.pop().getValue().stop();
+ }
+ stopWatch.stop();
+ final long globalDuration = stopWatch.getDuration();
+ stopWatch.reset();
+
+ // Sort results by descending duration
+ final List<Object> sorted = new ArrayList<Object>(codeBlockTimings.size() + nativeMethodTimings.size());
+ sorted.addAll(codeBlockTimings.keySet());
+ sorted.addAll(nativeMethodTimings.keySet());
+ Collections.sort(sorted, new Comparator<Object>() {
+ public int compare(final Object o1, final Object o2) {
+ return -Long.valueOf(getTimings(o1).getValue().getDuration()).compareTo(
+ Long.valueOf(getTimings(o2).getValue().getDuration()));
+ }
+ });
+
+ // Compile profiling results
+ for (Object op : sorted) {
+ ProfilingData profilingData;
+ Pair<Long, StopWatch> timings = getTimings(op);
+ long duration = timings.getValue().getDuration();
+ double ratio = (double) duration / (double) globalDuration;
+ if (op instanceof CodeBlock) {
+ profilingData = new ProfilingData((CodeBlock) op, null, duration, ratio, timings.getKey());
+ } else {
+ profilingData = new ProfilingData(null, (Method) op, duration, ratio, timings.getKey());
+ }
+ results.add(profilingData);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void error(StackFrame frame, String msg, Exception e) {
+ }
+
+ /**
+ * Returns the profiling results.
+ *
+ * @return the results
+ */
+ public List<ProfilingData> getResults() {
+ return results;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("Profiling results:\n\tDuration (sec.)\tDuration (%)\tInvocations\tOperation\n");
+ for (ProfilingData profilingData : getResults()) {
+ sb.append(String.format("\t%15.6f\t%12.2f\t%11d\t%s\n", profilingData.getDuration() / DIVISOR, profilingData.getDurationRatio() * HUNDRED, profilingData.getInvocations(), profilingData.getOperation()));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the timings for the given stack frame.
+ *
+ * @param frame
+ * the stack frame
+ * @return the timings for the given stack frame, or <code>null</code> if no operation is linked to the stack frame
+ */
+ private Pair<Long, StopWatch> getTimings(final StackFrame frame) {
+ final CodeBlock cb = frame.getCodeBlock();
+ if (cb != null) {
+ Pair<Long, StopWatch> timings = codeBlockTimings.get(cb);
+ if (timings == null) {
+ timings = new Pair<Long, StopWatch>(0L, new StopWatch());
+ codeBlockTimings.put(cb, timings);
+ }
+ return timings;
+ }
+ final Method m = frame.getNativeMethod();
+ if (m != null) {
+ Pair<Long, StopWatch> timings = nativeMethodTimings.get(m);
+ if (timings == null) {
+ timings = new Pair<Long, StopWatch>(0L, new StopWatch());
+ nativeMethodTimings.put(m, timings);
+ }
+ return timings;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the timings for the given codeblock or native method.
+ *
+ * @param operation
+ * the codeblock or native method
+ * @return the timings for the given codeblock or native method, or <code>null</code> if no operation is linked to the stack frame
+ */
+ private Pair<Long, StopWatch> getTimings(final Object operation) {
+ Pair<Long, StopWatch> timings = codeBlockTimings.get(operation);
+ if (timings == null) {
+ timings = nativeMethodTimings.get(operation);
+ }
+ return timings;
+ }
+
+}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/StopWatch.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/StopWatch.java
new file mode 100644
index 00000000..91e8d48d
--- /dev/null
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/StopWatch.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Dennis Wagelaar.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Dennis Wagelaar - initial API and
+ * implementation and/or initial documentation
+ *******************************************************************************/
+package org.eclipse.m2m.atl.emftvm.profiler;
+
+/**
+ * Stopwatch utility class.
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ */
+public class StopWatch {
+
+ private long duration;
+ private long start;
+ private boolean started;
+
+ /**
+ * Returns <code>true</code> if the stopwatch is started.
+ *
+ * @return <code>true</code> if the stopwatch is started
+ */
+ public boolean isStarted() {
+ return started;
+ }
+
+ /**
+ * Starts the stopwatch.
+ */
+ public void start() {
+ if (!isStarted()) {
+ start = System.nanoTime();
+ started = true;
+ }
+ }
+
+ /**
+ * Stops the stopwatch.
+ */
+ public void stop() {
+ if (isStarted()) {
+ duration += System.nanoTime() - start;
+ started = false;
+ }
+ }
+
+ /**
+ * Resets the stopwatch to "0".
+ */
+ public void reset() {
+ duration = 0L;
+ start = System.nanoTime();
+ }
+
+ /**
+ * Returns the measured duration in nanoseconds.
+ *
+ * @return the measured duration in nanoseconds
+ */
+ public long getDuration() {
+ return duration;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "StopWatch [isStarted()=" + isStarted() + ", getDuration()=" + getDuration() + "]";
+ }
+
+}

Back to the top