1
1
package build
2
2
3
+ import sbt .Keys .scalaInstance
4
+
3
5
import java .io .File
4
6
import sbt ._
5
7
import sbt .complete .DefaultParsers .OptSpace
6
8
import sbt .complete .Parser
9
+ import sbt .internal .inc .classpath .ClasspathUtilities
10
+
11
+ import java .util .stream .Collectors
7
12
8
13
abstract class Profiler (val name : String ) {
9
14
def command (outDir : File ): String
10
15
val flameGraphOpts = s " --minwidth,1,--colors,java,--cp,--width,1800 "
11
16
}
12
17
object Profiler {
13
- def all = List (basic, jfr, asyncAlloc, asyncCpu, asyncWall, perfNorm)
18
+ def all = List (basic, jfr, asyncAlloc, asyncCpu, asyncCpuAlloc, asyncWall, perfNorm)
14
19
15
20
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
17
22
18
23
def profParser (name : String )(s : State ): Parser [String ] = {
19
24
import Parser ._
20
25
token(name ~> OptSpace ) flatMap { _ => matched(s.combinedParser)} map (_.trim)
21
26
}
22
27
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
+
23
56
def commandImpl (profs : List [Profiler ]) = (s : State , line : String ) => {
24
57
val commands : List [String ] = profs.flatMap { (prof : Profiler ) =>
25
58
val outDir = file(s " target/profile- ${prof.name}" )
@@ -37,16 +70,30 @@ case object basic extends Profiler("basic") {
37
70
case object jfr extends Profiler (" jfr" ) {
38
71
def command (outDir : File ): String = s """ -jvmArgs -XX:+UnlockCommercialFeatures -prof "jfr:dir= ${outDir.getAbsolutePath};stackDepth=1024;postProcessor=scala.bench.JfrToFlamegraph;verbose=true" """
39
72
}
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)
42
75
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(" ;" )}" """
44
90
println(r)
45
91
r
46
92
}
47
93
}
48
94
case object asyncCpu extends async(" cpu" )
49
95
case object asyncAlloc extends async(" alloc" )
96
+ case object asyncCpuAlloc extends async(" cpu" , true )
50
97
case object asyncWall extends async(" wall" )
51
98
case object perfNorm extends Profiler (" perfNorm" ) {
52
99
def command (outDir : File ): String = " -prof perfnorm"
0 commit comments