Permalink
Browse files

Add graph merging feature + graph filtering by predicate (capture lev…

…el only)
  • Loading branch information...
1 parent 06dd048 commit 2cb9fe8285ad06ad76b2cee1f7f0b5f7172b4a68 @stevegury committed May 30, 2011
Showing with 37 additions and 21 deletions.
  1. +37 −21 src/main/scala/Profiler.scala
@@ -5,8 +5,8 @@ object Profiler {
val DateRegex = """\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d""".r
val ThreadNameRegex = """"([^"]+)".*""".r
- val CallRegex = """\tat (.*)""".r
- val StateRegex = """ java.lang.Thread.State: (\w+)\s*\(*.*\)*""".r
+ val CallRegex = """\s+at\s+(.*)""".r
+ val StateRegex = """\s+java.lang.Thread.State:\s+(\w+)\s*\(*.*\)*""".r
abstract class ThreadState
case object New extends ThreadState
@@ -26,7 +26,7 @@ object Profiler {
case _ => throw new IllegalArgumentException("Bad thread state: '" + string + "'")
}
- class CallGraph( val name : String , val state : ThreadState = Runnable, val count : Int = 1, val descendants : Map[String,CallGraph] = Map.empty[String,CallGraph] ) {
+ class CallGraph( val name : String = "root" , val state : ThreadState = Runnable, val count : Int = 1, val descendants : Map[String,CallGraph] = Map.empty[String,CallGraph] ) {
val CallRegex = """([^\(]+)\([^\(]+\)""".r
lazy val CallRegex(line,file) = name
lazy val nameSplit = line.split('.')
@@ -48,30 +48,43 @@ object Profiler {
if( Nil == calls )
new CallGraph(name, state)
else{
- val nextCall : CallGraph = descendants.get( calls.head ) match {
- case Some(nextCall) => nextCall
- case _ => new CallGraph(calls.head)
- }
+ val nextCall = descendants.getOrElse( calls.head, new CallGraph(calls.head) )
val newNextCall = nextCall.update( calls.tail , state )
new CallGraph(name, Runnable, count + 1, descendants + (newNextCall.name -> newNextCall) )
}
}
// Return the most sampled chain of calls
- def criticalPath : List[String] = {
+ def criticalPath : List[String] = innerCriticalPath.tail // remove the "root" node
+ private def innerCriticalPath : List[String] = {
if( descendants.isEmpty )
List(name)
else {
// select the most sampled child and append it to the result
descendants.values.toSeq.sortWith(_.count > _.count).headOption.map{
- bestChild => name :: bestChild.criticalPath
+ bestChild => name :: bestChild.innerCriticalPath
}.getOrElse( List(name) )
}
}
// merge two call graphs together, useful for profiling a group of thread (as thread pool)
def merge( graph : CallGraph ) : CallGraph = {
- this
+ if( name == graph.name ) {
+ // If nodes are equal (same name), only merge map of descendants
+ val (smallest,biggest) = if( count < graph.count )
+ (descendants, graph.descendants)
+ else
+ (graph.descendants, descendants)
+ val newDescendants : Map[String,CallGraph] = biggest.map{ case (name,node) =>
+ smallest.get(name) match {
+ case Some(otherNode) => (name,node.merge(otherNode)) // merge nodes present in both descendant list
+ case None => (name,node)
+ }
+ } ++ (smallest -- biggest.keys) // add node only present in smallest descendant list
+ new CallGraph(name, Runnable, count + graph.count, newDescendants )
+ }
+ else // in case of root inequality, return a new node with the two graphs as children
+ new CallGraph(name + "#" + graph.name, Runnable, count + graph.count, Map(name -> this, graph.name -> graph))
}
// filter the graph based on the predicate
@@ -81,7 +94,7 @@ object Profiler {
}
}
- def profileThread( it : Iterator[String] ) : Map[String,CallGraph] = {
+ def profileThread( it : Iterator[String] , predicate : List[String] => Boolean = _ => true ) : Map[String,CallGraph] = {
def recProfile( threadName : String , state : String, callStack : List[String], profilingPerThread : Map[String,CallGraph] ) : Map[String,CallGraph] = {
if( it.hasNext ) {
val line = it.next()
@@ -90,10 +103,10 @@ object Profiler {
case CallRegex(call) => recProfile(threadName, state, call :: callStack ,profilingPerThread)
case StateRegex(newState) => recProfile(threadName, newState, callStack, profilingPerThread)
case "" =>
- if( callStack.isEmpty )
+ if( callStack.isEmpty || ! predicate(callStack) )
recProfile("", "", Nil, profilingPerThread)
else {
- val root = profilingPerThread.getOrElse(threadName, new CallGraph(threadName, Runnable))
+ val root = profilingPerThread.getOrElse(threadName, new CallGraph())
val callGraph = root.update( callStack , state )
recProfile("", "", Nil, profilingPerThread + (threadName -> callGraph))
}
@@ -116,14 +129,17 @@ object Profiler {
}
else {
val file = new File( args(0) )
- val profiling = profileThread( Source.fromFile(file).getLines() )
-
- println( "10 most active thread:" )
- sortByUsage(profiling).take(10).foreach{ case (name,callGraph) => println("- %s (%d samples)".format(callGraph.name,callGraph.count)) }
- val (firstThread,graph) = sortByUsage(profiling).head
- println("")
- println( "Critical path of thread: " + firstThread )
- graph.criticalPath.foreach{ x => println(" + "+x) }
+ def filter( word : String )( callstack : List[String] ) = callstack.foldLeft(true){
+ case (containsNotString,line) => containsNotString && line.indexOf(word) == -1
+ }
+ val profilingPerThread = profileThread( Source.fromFile(file).getLines() , filter("YJP") )
+ val mergedProfiling = profilingPerThread.values.reduceLeft{ _.merge(_) }
+ mergedProfiling.criticalPath.foreach{ x => println(" + "+x) }
+
+ // val runningThreads = profilingPerThread.filter( _.state == Running )
+ // val firstThread = sortByUsage(runningThreads).head
+ // println( "Critical path of thread: " + firstThread.name )
+ // firstThread.criticalPath.foreach{ x => println(" + "+x) }
}
}

0 comments on commit 2cb9fe8

Please sign in to comment.