Initial commit of EMFTVM profiler.
diff --git a/plugins/org.eclipse.m2m.atl.emftvm.launcher/src/org/eclipse/m2m/atl/emftvm/launcher/profiler/Profiler.java b/plugins/org.eclipse.m2m.atl.emftvm.launcher/src/org/eclipse/m2m/atl/emftvm/launcher/profiler/Profiler.java
new file mode 100644
index 0000000..92ef458
--- /dev/null
+++ b/plugins/org.eclipse.m2m.atl.emftvm.launcher/src/org/eclipse/m2m/atl/emftvm/launcher/profiler/Profiler.java
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * 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.launcher.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.debug.core.ILaunch;
+import org.eclipse.m2m.atl.common.ATLLogger;
+import org.eclipse.m2m.atl.emftvm.CodeBlock;
+import org.eclipse.m2m.atl.emftvm.launcher.LaunchAdapter;
+import org.eclipse.m2m.atl.emftvm.util.StackFrame;
+
+/**
+ * Profiler for EMFTVM.
+ * 
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ * 
+ */
+public class Profiler extends LaunchAdapter {
+
+	/**
+	 * 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;
+		}
+
+	}
+
+	private static final double DIVISOR = 1E9;
+
+	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>>();
+
+	/**
+	 * Creates a new {@link Profiler}.
+	 * 
+	 * @param launch
+	 *            the launch object to wrap
+	 */
+	public Profiler(ILaunch launch) {
+		super(launch);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void enter(final StackFrame frame) {
+		final Pair<Long, StopWatch> timings = getTimings(frame);
+		if (timings != null) {
+			if (!timingStack.isEmpty()) {
+				timingStack.peek().getValue().stop();
+			}
+			timings.setKey(timings.getKey() + 1L);
+			timings.getValue().start();
+			timingStack.push(timings);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void leave(final StackFrame frame) {
+		if (!timingStack.isEmpty()) {
+			final Pair<Long, StopWatch> timings = timingStack.pop();
+			assert timings == getTimings(frame);
+			timings.getValue().stop();
+			if (!timingStack.isEmpty()) {
+				timingStack.peek().getValue().start();
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void terminated() {
+		while (!timingStack.isEmpty()) {
+			timingStack.pop().getValue().stop();
+		}
+
+		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()));
+			}
+		});
+		final StringBuilder sb = new StringBuilder("Profiling results:\n\tDuration (sec.)\tInvocations\tOperation\n");
+		for (Object op : sorted) {
+			Pair<Long, StopWatch> timings = getTimings(op);
+			sb.append(String.format("\t%15.6f\t%11d\t%s\n", timings.getValue().getDuration() / DIVISOR, timings.getKey(), op));
+		}
+		ATLLogger.info(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.launcher/src/org/eclipse/m2m/atl/emftvm/launcher/profiler/StopWatch.java b/plugins/org.eclipse.m2m.atl.emftvm.launcher/src/org/eclipse/m2m/atl/emftvm/launcher/profiler/StopWatch.java
new file mode 100644
index 0000000..121d0b9
--- /dev/null
+++ b/plugins/org.eclipse.m2m.atl.emftvm.launcher/src/org/eclipse/m2m/atl/emftvm/launcher/profiler/StopWatch.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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.launcher.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;
+	}
+
+}