diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-07-19 17:33:17 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-08-20 16:16:02 -0700 |
commit | 473a1692abf4d64e5df81cd19be214fe5bfa06ec (patch) | |
tree | c5f26f42296e3e585fe211b5a4e93f7c45d3b543 /src/partest-extras/scala/tools/partest/instrumented | |
parent | 738441cf58136bd4af9985886dd0cd38ccda0777 (diff) | |
download | scala-473a1692abf4d64e5df81cd19be214fe5bfa06ec.tar.gz scala-473a1692abf4d64e5df81cd19be214fe5bfa06ec.tar.bz2 scala-473a1692abf4d64e5df81cd19be214fe5bfa06ec.zip |
Move partest to https://github.com/scala/scala-partest
As partest is now resolved from maven, `test/partest` uses `ant test.suite.init`
to determine the classpath (serialized to build/pack/partest.properties)
that's necessary to run `scala.tools.partest.nest.ConsoleRunner`.
Thus, partest gets exactly the same classpath, whether run from
the command line through `test/partest` or via `ant test`.
The version of partest we're using is specified by
properties defined in versions.properties (formerly `starr.number`).
Currently, we're using:
```
scala.binary.version=2.11.0-M4
partest.version.number=1.0-RC3
```
NOTES:
- The version of Scala being tested must be backwards binary compatible with
the version of Scala that was used to compile partest.
- Once 2.11 goes final, `scala.binary.version=2.11`, and `starr.version=2.11.0`.
- Need scalacheck on classpath for test/partest scalacheck tests.
- Removed atrophied ant tests (haven't been run/changed for at least two years
I checked 81d659141a as a "random" sample).
- Removed scalacheck. It's resolved as a partest dependency.
- For now, use a locally built scalap
- Kept the trace macro in the main repo (partest-extras)
- New targets for faster pr validation: test-core-opt, test-stab-opt
- Reused partest eclipse/intellij project to partest-extras
(note: the partest dependency is hard-coded)
Diffstat (limited to 'src/partest-extras/scala/tools/partest/instrumented')
-rw-r--r-- | src/partest-extras/scala/tools/partest/instrumented/Instrumentation.scala | 93 | ||||
-rw-r--r-- | src/partest-extras/scala/tools/partest/instrumented/Profiler.java | 82 |
2 files changed, 175 insertions, 0 deletions
diff --git a/src/partest-extras/scala/tools/partest/instrumented/Instrumentation.scala b/src/partest-extras/scala/tools/partest/instrumented/Instrumentation.scala new file mode 100644 index 0000000000..18dd740208 --- /dev/null +++ b/src/partest-extras/scala/tools/partest/instrumented/Instrumentation.scala @@ -0,0 +1,93 @@ +/* NEST (New Scala Test) + * Copyright 2007-2013 LAMP/EPFL + * @author Grzegorz Kossakowski + */ + +package scala.tools.partest.instrumented + +import scala.collection.JavaConverters._ + +case class MethodCallTrace(className: String, methodName: String, methodDescriptor: String) { + override def toString(): String = className + "." + methodName + methodDescriptor +} +object MethodCallTrace { + implicit val ordering: Ordering[MethodCallTrace] = Ordering.by(x => (x.className, x.methodName, x.methodDescriptor)) +} + +/** + * An object that controls profiling of instrumented byte-code. The instrumentation is achieved + * by using `java.lang.instrument` package. The instrumentation agent can be found in + * `scala.tools.partest.javaagent` package. + * + * At the moment the following classes are being instrumented: + * * all classes with empty package + * * all classes from scala package (except for classes responsible for instrumentation) + * + * The canonical way of using instrumentation is have a test-case in `files/instrumented` directory. + * The following code in main: + * + * {{{ + * import scala.tools.partest.instrumented.Instrumentation._ + * def main(args: Array[String]): Unit = { + * startProfiling() + * // should box the boolean + println(true) + stopProfiling() + printStatistics() + * } + * }}} + * + * + * should print: + * + * {{{ + * true + * Method call statistics: + * scala/Predef$.println(Ljava/lang/Object;)V: 1 + * scala/runtime/BoxesRunTime.boxToBoolean(Z)Ljava/lang/Boolean;: 1 + * }}} + */ +object Instrumentation { + + type Statistics = Map[MethodCallTrace, Int] + + def startProfiling(): Unit = Profiler.startProfiling() + def stopProfiling(): Unit = Profiler.stopProfiling() + def resetProfiling(): Unit = Profiler.resetProfiling() + def isProfiling(): Boolean = Profiler.isProfiling() + + def getStatistics: Statistics = { + val isProfiling = Profiler.isProfiling() + if (isProfiling) { + Profiler.stopProfiling() + } + val stats = Profiler.getStatistics().asScala.toSeq.map { + case (trace, count) => MethodCallTrace(trace.className, trace.methodName, trace.methodDescriptor) -> count.intValue + } + val res = Map(stats: _*) + if (isProfiling) { + Profiler.startProfiling() + } + res + } + + val standardFilter: MethodCallTrace => Boolean = t => { + // ignore all calls to Console trigger by printing + t.className != "scala/Console$" && + // console accesses DynamicVariable, let's discard it too + !t.className.startsWith("scala/util/DynamicVariable") + } + + // Used in tests. + def printStatistics(stats: Statistics = getStatistics, filter: MethodCallTrace => Boolean = standardFilter): Unit = { + val stats = getStatistics + println("Method call statistics:") + val toBePrinted = stats.toSeq.filter(p => filter(p._1)).sortBy(_._1) + // <count> <trace> + val format = "%5d %s\n" + toBePrinted foreach { + case (trace, count) => printf(format, count, trace) + } + } + +} diff --git a/src/partest-extras/scala/tools/partest/instrumented/Profiler.java b/src/partest-extras/scala/tools/partest/instrumented/Profiler.java new file mode 100644 index 0000000000..d6b62e1d9e --- /dev/null +++ b/src/partest-extras/scala/tools/partest/instrumented/Profiler.java @@ -0,0 +1,82 @@ +/* NEST (New Scala Test) + * Copyright 2007-2013 LAMP/EPFL + * @author Grzegorz Kossakowski + */ + +package scala.tools.partest.instrumented; + +import java.util.HashMap; +import java.util.Map; + +/** + * A simple profiler class that counts method invocations. It is being used in byte-code instrumentation by inserting + * call to {@link Profiler#methodCalled(String, String, String)} at the beginning of every instrumented class. + * + * WARANING: This class is INTERNAL implementation detail and should never be used directly. It's made public only + * because it must be universally accessible for instrumentation needs. If you want to profile your test use + * {@link Instrumentation} instead. + */ +public class Profiler { + + private static boolean isProfiling = false; + private static Map<MethodCallTrace, Integer> counts = new HashMap<MethodCallTrace, Integer>(); + + static public class MethodCallTrace { + final String className; + final String methodName; + final String methodDescriptor; + + public MethodCallTrace(final String className, final String methodName, final String methodDescriptor) { + this.className = className; + this.methodName = methodName; + this.methodDescriptor = methodDescriptor; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MethodCallTrace)) { + return false; + } else { + MethodCallTrace that = (MethodCallTrace) obj; + return that.className.equals(className) && that.methodName.equals(methodName) && that.methodDescriptor.equals(methodDescriptor); + } + } + @Override + public int hashCode() { + return className.hashCode() ^ methodName.hashCode() ^ methodDescriptor.hashCode(); + } + } + + public static void startProfiling() { + isProfiling = true; + } + + public static void stopProfiling() { + isProfiling = false; + } + + public static boolean isProfiling() { + return isProfiling; + } + + public static void resetProfiling() { + counts = new HashMap<MethodCallTrace, Integer>(); + } + + public static void methodCalled(final String className, final String methodName, final String methodDescriptor) { + if (isProfiling) { + MethodCallTrace trace = new MethodCallTrace(className, methodName, methodDescriptor); + Integer counter = counts.get(trace); + if (counter == null) { + counts.put(trace, 1); + } else { + counts.put(trace, counter+1); + } + } + } + + public static Map<MethodCallTrace, Integer> getStatistics() { + return new HashMap<MethodCallTrace, Integer>(counts); + } + +} |