diff options
author | Paul Phillips <paulp@improving.org> | 2010-11-16 23:08:45 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-11-16 23:08:45 +0000 |
commit | b7fcc7c73e41b326fe4d85d81c49c50fa954c990 (patch) | |
tree | 2d2310bed191bdb565095526d8a14e813f2f2958 /src | |
parent | 03b3f7d4a1995c735ba95080f7dc5e0c68d07ca3 (diff) | |
download | scala-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.scala | 33 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/io/package.scala | 9 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/util/Profiling.scala | 32 | ||||
-rw-r--r-- | src/yourkit/scala/tools/util/YourkitProfiling.scala | 36 |
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 |