summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-11-16 23:08:45 +0000
committerPaul Phillips <paulp@improving.org>2010-11-16 23:08:45 +0000
commitb7fcc7c73e41b326fe4d85d81c49c50fa954c990 (patch)
tree2d2310bed191bdb565095526d8a14e813f2f2958 /src
parent03b3f7d4a1995c735ba95080f7dc5e0c68d07ca3 (diff)
downloadscala-b7fcc7c73e41b326fe4d85d81c49c50fa954c990.tar.gz
scala-b7fcc7c73e41b326fe4d85d81c49c50fa954c990.tar.bz2
scala-b7fcc7c73e41b326fe4d85d81c49c50fa954c990.zip
Some profiling infrastructure.
I avoided creating any dependency on yourkit. In addition, there was no way to give arguments to the JVM without losing the ones defined in ANT_OPTS, which has been a massive pain for a while. So there is now "jvm.opts" which is simply appended to ANT_OPTS, e.g. % ant -Djvm.opts=-verbose [echo] Forking with JVM opts: -Xms1536M -Xmx2g -Xss1M -XX:MaxPermSize=192M -XX:+UseParallelGC -verbose There is a minimal stub defining a profiler interface: scala.tools.util.Profiling Then the yourkit wrapper implements that interface. Once your locker has been rebuilt once, you can do this: ant yourkit.run And it will build quick.lib/comp with profiling enabled, assuming it can find the necessary files. See the yourkit.init target for values to change: or ant -Dyourkit.home=/path/to/it might be enough. Review by dragos.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala33
-rw-r--r--src/compiler/scala/tools/nsc/io/package.scala9
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala2
-rw-r--r--src/compiler/scala/tools/util/Profiling.scala32
-rw-r--r--src/yourkit/scala/tools/util/YourkitProfiling.scala36
5 files changed, 106 insertions, 6 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index d05eb26443..f8dd356937 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -9,6 +9,7 @@ import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundE
import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException }
import compat.Platform.currentTime
+import scala.tools.util.Profiling
import scala.collection.{ mutable, immutable }
import io.{ SourceReader, AbstractFile, Path }
import reporters.{ Reporter, ConsoleReporter }
@@ -232,6 +233,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
def logClasspath = settings.Ylogcp.value
def printLate = settings.printLate.value
def printStats = settings.Ystatistics.value
+ def profileClass = settings.YprofileClass.value
def richExes = settings.YrichExes.value
def showTrees = settings.Xshowtrees.value
def target = settings.target.value
@@ -242,14 +244,16 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
def declsOnly = false
/** Flags as applied to the current or previous phase */
- def browsePhase = isActive(settings.browse)
- def checkPhase = wasActive(settings.check)
- def logPhase = isActive(settings.log)
- def printPhase = isActive(settings.Xprint)
- def showPhase = isActive(settings.Yshow)
+ def browsePhase = isActive(settings.browse)
+ def checkPhase = wasActive(settings.check)
+ def logPhase = isActive(settings.log)
+ def printPhase = isActive(settings.Xprint)
+ def showPhase = isActive(settings.Yshow)
+ def profilePhase = isActive(settings.Yprofile) && !profileAll
/** Derived values */
def showNames = List(showClass, showObject).flatten
+ def profileAll = settings.Yprofile.doAllPhases
def jvm = target startsWith "jvm"
def msil = target == "msil"
def verboseDebug = debug && verbose
@@ -818,6 +822,9 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
private def showMembers() =
opt.showNames foreach (x => showDef(x, opt.declsOnly, globalPhase))
+ // If -Yprofile isn't given this will never be triggered.
+ lazy val profiler = Class.forName(opt.profileClass).newInstance().asInstanceOf[Profiling]
+
/** Compile list of source files */
def compileSources(_sources: List[SourceFile]) {
val depSources = dependencyAnalysis.calculateFiles(_sources.distinct) // bug #1268, scalac confused by duplicated filenames
@@ -830,10 +837,20 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
for (source <- sources) addUnit(new CompilationUnit(source))
globalPhase = firstPhase
+
+ if (opt.profileAll) {
+ inform("starting CPU profiling on compilation run")
+ profiler.startProfiling()
+ }
while (globalPhase != terminalPhase && !reporter.hasErrors) {
val startTime = currentTime
phase = globalPhase
- globalPhase.run
+
+ if (opt.profilePhase) {
+ inform("starting CPU profiling on phase " + globalPhase.name)
+ profiler profile globalPhase.run
+ }
+ else globalPhase.run
// write icode to *.icode files
if (opt.writeICode && (runIsAt(icodePhase) || opt.printPhase && runIsPast(icodePhase)))
@@ -866,6 +883,10 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
advancePhase
}
+ if (opt.profileAll) {
+ profiler.stopProfiling()
+ profiler.captureSnapshot()
+ }
// If no phase was specified for -Xshow-class/object, show it now.
if (settings.Yshow.isDefault)
diff --git a/src/compiler/scala/tools/nsc/io/package.scala b/src/compiler/scala/tools/nsc/io/package.scala
index 18d374a617..2016fb5518 100644
--- a/src/compiler/scala/tools/nsc/io/package.scala
+++ b/src/compiler/scala/tools/nsc/io/package.scala
@@ -15,4 +15,13 @@ package object io {
def runnableFn(f: () => Unit): Runnable = runnable(f())
def callableFn[T](f: () => T): Callable[T] = callable(f())
def spawnFn[T](f: () => T): Future[T] = spawn(f())
+
+ // Create, start, and return a background thread
+ // If isDaemon is true, it is marked as daemon (and will not interfere with JVM shutdown)
+ def daemonize(isDaemon: Boolean)(body: => Unit): Thread = {
+ val thread = new Thread(runnable(body))
+ thread setDaemon isDaemon
+ thread.start
+ thread
+ }
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index d47aff0327..a1225346b0 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -122,6 +122,8 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java")
val noimports = BooleanSetting ("-Yno-imports", "Compile without any implicit imports")
val nopredefs = BooleanSetting ("-Yno-predefs", "Compile without any implicit predefined values")
+ val Yprofile = PhasesSetting ("-Yprofile", "Profile the given phase. Needs yjpagent to run.")
+ val YprofileClass = StringSetting ("-Yprofile-class", "class", "Name of profiler class", "scala.tools.util.YourkitProfiling")
val Yrecursion = IntSetting ("-Yrecursion", "Recursion depth used when locking symbols", 0, Some(0, Int.MaxValue), (_: String) => None)
val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations")
val Xshowtrees = BooleanSetting ("-Yshow-trees", "Show detailed trees when used in connection with -Xprint:<phase>")
diff --git a/src/compiler/scala/tools/util/Profiling.scala b/src/compiler/scala/tools/util/Profiling.scala
new file mode 100644
index 0000000000..3b45762bd8
--- /dev/null
+++ b/src/compiler/scala/tools/util/Profiling.scala
@@ -0,0 +1,32 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package util
+
+/** This is a (very) minimal stub for profiling, the purpose
+ * of which is making it possible to integrate profiling hooks in
+ * the compiler without creating a dependency on any particular
+ * profiler. You can specify a profiler class (which must be an
+ * instance of this class) like so:
+ *
+ * // or -Yprofile:phase to profile individual phases
+ * scalac -Yprofile-class your.profiler.Class -Yprofile:all <files>
+ *
+ */
+abstract class Profiling {
+ def isActive: Boolean
+ def startProfiling(): Unit
+ def stopProfiling(): Unit
+ def captureSnapshot(): Unit
+
+ def profile[T](body: => T): T = {
+ startProfiling()
+ val result = body
+ stopProfiling()
+ captureSnapshot()
+ result
+ }
+}
diff --git a/src/yourkit/scala/tools/util/YourkitProfiling.scala b/src/yourkit/scala/tools/util/YourkitProfiling.scala
new file mode 100644
index 0000000000..d66cdc9ee1
--- /dev/null
+++ b/src/yourkit/scala/tools/util/YourkitProfiling.scala
@@ -0,0 +1,36 @@
+package scala.tools
+package util
+
+import com.yourkit.api._
+import nsc.io._
+
+class YourkitProfiling extends Profiling {
+ @volatile private var active = false
+ private var recordAllocation = false
+ lazy val controller = new Controller
+
+ def startProfiling(): Unit = {
+ if (isActive)
+ return
+
+ active = true
+ daemonize(true) {
+ controller.startCPUProfiling(ProfilingModes.CPU_SAMPLING, Controller.DEFAULT_FILTERS)
+ if (recordAllocation)
+ controller.startAllocationRecording(true, 100, false, 0)
+ }
+ }
+
+ def captureSnapshot() =
+ daemonize(false)(controller.captureSnapshot(ProfilingModes.SNAPSHOT_WITH_HEAP))
+
+ def stopProfiling() = {
+ if (recordAllocation)
+ controller.stopAllocationRecording()
+
+ controller.stopCPUProfiling()
+ active = false
+ }
+
+ def isActive = active
+} \ No newline at end of file