11package build
22
3+ import sbt .Keys .scalaInstance
4+
35import java .io .File
46import sbt ._
57import sbt .complete .DefaultParsers .OptSpace
68import sbt .complete .Parser
9+ import sbt .internal .inc .classpath .ClasspathUtilities
10+
11+ import java .util .stream .Collectors
712
813abstract class Profiler (val name : String ) {
914 def command (outDir : File ): String
1015 val flameGraphOpts = s " --minwidth,1,--colors,java,--cp,--width,1800 "
1116}
1217object Profiler {
13- def all = List (basic, jfr, asyncAlloc, asyncCpu, asyncWall, perfNorm)
18+ def all = List (basic, jfr, asyncAlloc, asyncCpu, asyncCpuAlloc, asyncWall, perfNorm)
1419
1520 def commands = Profiler .all.map { prof => Command .arb(profParser(" prof" + prof.toString.capitalize))(commandImpl(List (prof))) } :+
16- Command .arb(profParser(" prof" ))(commandImpl(Profiler .all))
21+ Command .arb(profParser(" prof" ))(commandImpl(Profiler .all)) :+ jfr2flameCommand
1722
1823 def profParser (name : String )(s : State ): Parser [String ] = {
1924 import Parser ._
2025 token(name ~> OptSpace ) flatMap { _ => matched(s.combinedParser)} map (_.trim)
2126 }
2227
28+ def jfr2flameCommand = Command .command(" jfr2flame" ) { state =>
29+ import java .nio .file ._
30+ import scala .collection .JavaConverters ._
31+ val jfrFiles : List [Path ] = Files .walk(Paths .get(System .getProperty(" user.dir" ))).iterator.asScala.filter(x => x.toString.endsWith(" .jfr" )).toList
32+ for (jfrFile <- jfrFiles) {
33+ val converterJar = file(System .getenv(" ASYNC_PROFILER_DIR" )) / " build" / " converter.jar"
34+ val (_, si) = Project .extract(state).runTask(scalaInstance, state)
35+ val run = new Run (cp => ClasspathUtilities .makeLoader(cp, si), trapExit = true )
36+ def jfr2flame (forward : Boolean , event : String ): Unit = {
37+ val jfrFileNameString = jfrFile.toAbsolutePath.toString
38+ val flameFileName = jfrFileNameString.replaceAll(""" .jfr$""" , s " ${if (event == " cpu" ) " " else " -" + event}- ${if (forward) " forward" else " reverse" }.html " )
39+ val directionOpts = if (forward) Nil else List (" --reverse" )
40+ val eventOpts = if (event == " cpu" ) Nil else List (" --" + event)
41+ val flameOpts = List (" --minwidth" , " 0.2" )
42+ run.run(" jfr2flame" , converterJar :: Nil , eventOpts ++ flameOpts ++ directionOpts ++ List (jfrFileNameString, flameFileName), state.log).get
43+ }
44+ for (forward <- List (true , false )) {
45+ jfr2flame(forward, " cpu" )
46+ jfr2flame(forward, " alloc" )
47+ }
48+ }
49+ state
50+ }
51+ def jfr2flameParser (name : String )(s : State ): Parser [String ] = {
52+ import Parser ._
53+ token(" jfr2flame" ~> OptSpace ) flatMap { _ => matched(s.combinedParser)} map (_.trim)
54+ }
55+
2356 def commandImpl (profs : List [Profiler ]) = (s : State , line : String ) => {
2457 val commands : List [String ] = profs.flatMap { (prof : Profiler ) =>
2558 val outDir = file(s " target/profile- ${prof.name}" )
@@ -37,16 +70,30 @@ case object basic extends Profiler("basic") {
3770case object jfr extends Profiler (" jfr" ) {
3871 def command (outDir : File ): String = s """ -jvmArgs -XX:+UnlockCommercialFeatures -prof "jfr:dir= ${outDir.getAbsolutePath};stackDepth=1024;postProcessor=scala.bench.JfrToFlamegraph;verbose=true" """
3972}
40- sealed abstract class async (event : String ) extends Profiler (" async-" + event) {
41- val framebuf = 33554432
73+ sealed abstract class async (event : String , secondaryAlloc : Boolean = false ) extends Profiler (" async-" + event + ( if (secondaryAlloc) " -alloc " else " " ) ) {
74+ require(event != " alloc " || ! secondaryAlloc)
4275 def command (outDir : File ): String = {
43- val r = s """ -prof "async:dir= ${outDir.getAbsolutePath};libPath= ${System .getenv(" ASYNC_PROFILER_DIR" )}/build/libasyncProfiler.so;minwidth=1;width=1800;verbose=true;event= $event;filter= ${event == " wall" };flat=40;trace=10;framebuf= ${framebuf};output=flamegraph,jfr,text" """
76+ val opts = collection.mutable.ListBuffer [String ]()
77+ val doFlame = ! secondaryAlloc
78+ opts ++= List (s " dir= ${outDir.getAbsolutePath}" , s " libPath= ${System .getenv(" ASYNC_PROFILER_DIR" )}/build/libasyncProfiler.so " , " verbose=true" )
79+ opts += s " event= ${event}"
80+ if (doFlame) {
81+ opts ++= List (" output=flamegraph" , " minwidth=1" ," width=1800" , s " filter= ${event == " wall" }" )
82+ }
83+ if (secondaryAlloc) {
84+ opts += " alloc"
85+ opts += " output=jfr"
86+ } else {
87+ opts ++= List (" flat=40" , " traces=10" , " output=text" )
88+ }
89+ val r = s """ -prof "async: ${opts.mkString(" ;" )}" """
4490 println(r)
4591 r
4692 }
4793}
4894case object asyncCpu extends async(" cpu" )
4995case object asyncAlloc extends async(" alloc" )
96+ case object asyncCpuAlloc extends async(" cpu" , true )
5097case object asyncWall extends async(" wall" )
5198case object perfNorm extends Profiler (" perfNorm" ) {
5299 def command (outDir : File ): String = " -prof perfnorm"
0 commit comments