-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Scala 2 forwardport: -Yprofile-trace
#19897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b9af5a2
3450dfb
016dfdd
4077575
94f7613
c0ecfe0
485c044
592a892
dc10c53
e76fce4
718af3a
b8c501d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ import java.nio.channels.ClosedByInterruptException | |
import scala.util.control.NonFatal | ||
|
||
import dotty.tools.dotc.classpath.FileUtils.{hasTastyExtension, hasBetastyExtension} | ||
import dotty.tools.io.{ ClassPath, ClassRepresentation, AbstractFile } | ||
import dotty.tools.io.{ ClassPath, ClassRepresentation, AbstractFile, NoAbstractFile } | ||
import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions | ||
|
||
import Contexts.*, Symbols.*, Flags.*, SymDenotations.*, Types.*, Scopes.*, Names.* | ||
|
@@ -333,7 +333,15 @@ abstract class SymbolLoader extends LazyType { self => | |
def description(using Context): String = s"proxy to ${self.description}" | ||
} | ||
|
||
override def complete(root: SymDenotation)(using Context): Unit = { | ||
private inline def profileCompletion[T](root: SymDenotation)(inline body: T)(using Context): T = { | ||
val sym = root.symbol | ||
def associatedFile = root.symbol.associatedFile match | ||
case file: AbstractFile => file | ||
case _ => NoAbstractFile | ||
ctx.profiler.onCompletion(sym, associatedFile)(body) | ||
} | ||
|
||
override def complete(root: SymDenotation)(using Context): Unit = profileCompletion(root) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked that the But I am still not 100% convinced that is zero cost—it seems that calls to default implementations of Unfortunately, the benchmarks bot is currently down, so we can't run the benchmarks now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The allocations have been reduced by preallocating empty (default) outputs. This should reduce any overhead when -Yprofile-trace is disabled. The 2 additional calls to before/after completion might still be present, but maybe JVM can optimize these? Do you maybe know if the benchmarks bot is working now? We could run it now, after the rebase, to ensure the overhead is not significant when profile-tracing is disabled. |
||
def signalError(ex: Exception): Unit = { | ||
if (ctx.debug) ex.printStackTrace() | ||
val msg = ex.getMessage() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
// Scala 2 compiler backport of https://github.com/scala/scala/pull/7364 | ||
/* | ||
* Scala (https://www.scala-lang.org) | ||
* | ||
* Copyright EPFL and Lightbend, Inc. | ||
* | ||
* Licensed under Apache License 2.0 | ||
* (http://www.apache.org/licenses/LICENSE-2.0). | ||
* | ||
* See the NOTICE file distributed with this work for | ||
* additional information regarding copyright ownership. | ||
*/ | ||
|
||
package dotty.tools.dotc.profile | ||
|
||
import scala.language.unsafeNulls | ||
|
||
import java.io.Closeable | ||
import java.lang.management.ManagementFactory | ||
import java.nio.file.{Files, Path} | ||
import java.util | ||
import java.util.concurrent.TimeUnit | ||
|
||
import scala.collection.mutable | ||
|
||
object ChromeTrace { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't read this class and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've ported the FileUtilsTest from Scala 2 repo and added a ChromeTraceTest to check the outputs and structure of tracing files |
||
private object EventType { | ||
final val Start = "B" | ||
final val Instant = "I" | ||
final val End = "E" | ||
final val Complete = "X" | ||
|
||
final val Counter = "C" | ||
|
||
final val AsyncStart = "b" | ||
final val AsyncInstant = "n" | ||
final val AsyncEnd = "e" | ||
} | ||
} | ||
|
||
/** Allows writing a subset of captrue traces based on https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview# | ||
* Can be visualized using https://ui.perfetto.dev/, Chrome's about://tracing (outdated) or the tooling in https://www.google.com.au/search?q=catapult+tracing&oq=catapult+tracing+&aqs=chrome..69i57.3974j0j4&sourceid=chrome&ie=UTF-8 */ | ||
final class ChromeTrace(f: Path) extends Closeable { | ||
import ChromeTrace.EventType | ||
private val traceWriter = FileUtils.newAsyncBufferedWriter(f) | ||
private val context = mutable.Stack[JsonContext](TopContext) | ||
private val tidCache = new ThreadLocal[String]() { | ||
override def initialValue(): String = "%05d".format(Thread.currentThread().getId()) | ||
} | ||
objStart() | ||
fld("traceEvents") | ||
context.push(ValueContext) | ||
arrStart() | ||
traceWriter.newLine() | ||
|
||
private val pid = ManagementFactory.getRuntimeMXBean().getName().replaceAll("@.*", "") | ||
|
||
override def close(): Unit = { | ||
arrEnd() | ||
objEnd() | ||
context.pop() | ||
tidCache.remove() | ||
traceWriter.close() | ||
} | ||
|
||
def traceDurationEvent(name: String, startNanos: Long, durationNanos: Long, tid: String = this.tid(), pidSuffix: String = ""): Unit = { | ||
val durationMicros = nanosToMicros(durationNanos) | ||
val startMicros = nanosToMicros(startNanos) | ||
objStart() | ||
str("cat", "scalac") | ||
str("name", name) | ||
str("ph", EventType.Complete) | ||
str("tid", tid) | ||
writePid(pidSuffix) | ||
lng("ts", startMicros) | ||
lng("dur", durationMicros) | ||
objEnd() | ||
traceWriter.newLine() | ||
} | ||
|
||
private def writePid(pidSuffix: String) = { | ||
if (pidSuffix == "") | ||
str("pid", pid) | ||
else | ||
str2("pid", pid, "-", pidSuffix) | ||
} | ||
|
||
def traceCounterEvent(name: String, counterName: String, count: Long, processWide: Boolean): Unit = { | ||
objStart() | ||
str("cat", "scalac") | ||
str("name", name) | ||
str("ph", EventType.Counter) | ||
str("tid", tid()) | ||
writePid(pidSuffix = if (processWide) "" else tid()) | ||
lng("ts", microTime()) | ||
fld("args") | ||
objStart() | ||
lng(counterName, count) | ||
objEnd() | ||
objEnd() | ||
traceWriter.newLine() | ||
} | ||
|
||
def traceDurationEventStart(cat: String, name: String, colour: String = "", pidSuffix: String = tid()): Unit = traceDurationEventStartEnd(EventType.Start, cat, name, colour, pidSuffix) | ||
def traceDurationEventEnd(cat: String, name: String, colour: String = "", pidSuffix: String = tid()): Unit = traceDurationEventStartEnd(EventType.End, cat, name, colour, pidSuffix) | ||
|
||
private def traceDurationEventStartEnd(eventType: String, cat: String, name: String, colour: String, pidSuffix: String = ""): Unit = { | ||
objStart() | ||
str("cat", cat) | ||
str("name", name) | ||
str("ph", eventType) | ||
writePid(pidSuffix) | ||
str("tid", tid()) | ||
lng("ts", microTime()) | ||
if (colour != "") { | ||
str("cname", colour) | ||
} | ||
objEnd() | ||
traceWriter.newLine() | ||
} | ||
|
||
private def tid(): String = tidCache.get() | ||
|
||
private def nanosToMicros(t: Long): Long = TimeUnit.NANOSECONDS.toMicros(t) | ||
|
||
private def microTime(): Long = nanosToMicros(System.nanoTime()) | ||
|
||
private sealed abstract class JsonContext | ||
private case class ArrayContext(var first: Boolean) extends JsonContext | ||
private case class ObjectContext(var first: Boolean) extends JsonContext | ||
private case object ValueContext extends JsonContext | ||
private case object TopContext extends JsonContext | ||
|
||
private def str(name: String, value: String): Unit = { | ||
fld(name) | ||
traceWriter.write("\"") | ||
traceWriter.write(value) // This assumes no escaping is needed | ||
traceWriter.write("\"") | ||
} | ||
private def str2(name: String, value: String, valueContinued1: String, valueContinued2: String): Unit = { | ||
fld(name) | ||
traceWriter.write("\"") | ||
traceWriter.write(value) // This assumes no escaping is needed | ||
traceWriter.write(valueContinued1) // This assumes no escaping is needed | ||
traceWriter.write(valueContinued2) // This assumes no escaping is needed | ||
traceWriter.write("\"") | ||
} | ||
private def lng(name: String, value: Long): Unit = { | ||
fld(name) | ||
traceWriter.write(String.valueOf(value)) | ||
traceWriter.write("") | ||
} | ||
private def objStart(): Unit = { | ||
context.top match { | ||
case ac @ ArrayContext(first) => | ||
if (first) ac.first = false | ||
else traceWriter.write(",") | ||
case _ => | ||
} | ||
context.push(ObjectContext(true)) | ||
traceWriter.write("{") | ||
} | ||
private def objEnd(): Unit = { | ||
traceWriter.write("}") | ||
context.pop() | ||
} | ||
private def arrStart(): Unit = { | ||
traceWriter.write("[") | ||
context.push(ArrayContext(true)) | ||
} | ||
private def arrEnd(): Unit = { | ||
traceWriter.write("]") | ||
context.pop() | ||
} | ||
|
||
private def fld(name: String) = { | ||
val topContext = context.top | ||
topContext match { | ||
case oc @ ObjectContext(first) => | ||
if (first) oc.first = false | ||
else traceWriter.write(",") | ||
case context => | ||
throw new IllegalStateException("Wrong context: " + context) | ||
} | ||
traceWriter.write("\"") | ||
traceWriter.write(name) | ||
traceWriter.write("\"") | ||
traceWriter.write(":") | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.