Created
December 21, 2016 11:07
-
-
Save lizmat/afb86eeb90576ed32cb3f2b361fa2870 to your computer and use it in GitHub Desktop.
variance in Num.floor performance in "my int $a; for <a b c>.roll(*) { last if ($a = $a + 1) > 100000 }"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en" ng-app="moarProfApp" ng-controller="NavigationController"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>MoarVM Profiler Results</title> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"> | |
<style> | |
body { | |
min-height: 500px; | |
padding-top: 70px; | |
} | |
.icyclegraph .call { | |
display: block; | |
border: 1px solid black; | |
overflow: hidden; | |
} | |
.icyclegraph .child { | |
float: left; | |
} | |
.icyclegraph a { | |
color: black; | |
} | |
</style> | |
</head> | |
<body > | |
<div class="navbar navbar-default navbar-fixed-top" role="navigation"> | |
<div class="container"> | |
<div class="navbar-header"> | |
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> | |
<span class="sr-only">Toggle navigation</span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
</button> | |
<a class="navbar-brand">MoarVM Profiler Results</a> | |
</div> | |
<div class="navbar-collapse collapse"> | |
<ul class="nav navbar-nav"> | |
<li ng-class="Tab == 'Overview' ? 'active' : ''"><a href="#overview" ng-click="Tab = 'Overview'">Overview</a></li> | |
<li ng-class="Tab == 'Routines' ? 'active' : ''"><a href="#routines" ng-click="Tab = 'Routines'">Routines</a></li> | |
<li ng-class="Tab == 'Call Graph' ? 'active' : ''"><a href="#callgraph" ng-click="Tab = 'Call Graph'">Call Graph</a></li> | |
<li ng-class="Tab == 'Allocations' ? 'active' : ''"><a href="#allocations" ng-click="Tab = 'Allocations'">Allocations</a></li> | |
<li ng-class="Tab == 'GC' ? 'active' : ''"><a href="#gc" ng-click="Tab = 'GC'">GC</a></li> | |
<li ng-class="Tab == 'OSR/Deopt' ? 'active' : ''"><a href="#osrdeopt" ng-click="Tab = 'OSR/Deopt'">OSR / Deopt</a></li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'Overview'"> | |
<div ng-controller="OverviewController"> | |
<h3>Time Spent</h3> | |
<p>The profiled code ran for <strong>{{TotalTime}}ms</strong>. Of this, | |
<strong>{{OverheadTime}}ms</strong> were spent on garbage collection | |
and dynamic optimization (that's <strong>{{OverheadTimePercent}}%</strong>). | |
</p> | |
<table class="table table-striped table-condensed table-bordered"> | |
<tbody> | |
<tr> | |
<td><strong>Executing Code</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ExecutingTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{ExecutingTimePercent}}% | |
({{ExecutingTime}}ms) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Garbage Collection</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{GCTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{GCTimePercent}}% | |
({{GCTime}}ms) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Dynamic Optimization</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{SpeshTimePercent}}% | |
({{SpeshTime}}ms) | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<h3>Call Frames</h3> | |
<p>In total, <strong>{{EntriesWithoutInline}} call frames</strong> were | |
entered and exited by the profiled code. Inlining eliminated the need | |
to create <strong>{{EntriesInline}} call frames</strong> (that's | |
<strong>{{InlinePercent}}%</strong>). | |
</p> | |
<table class="table table-striped table-condensed table-bordered"> | |
<tbody> | |
<tr> | |
<td><strong>Interpreted Frames</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{InterpFramesPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{InterpFramesPercent}}% | |
({{InterpFrames}}) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Specialized Frames</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshFramesPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{SpeshFramesPercent}}% | |
({{SpeshFrames}}) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>JIT-Compiled Frames</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{JITFramesPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{JITFramesPercent}}% | |
({{JITFrames}}) | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<h3>Garbage Collection</h3> | |
<p>The profiled code did <strong>{{GCRuns}} garbage collections</strong>. | |
There were <strong>{{FullGCRuns}} full collections</strong> involving | |
the entire heap. | |
</p> | |
<p ng-show="{{GCRuns > 0}}"> | |
The average nursery collection time was <strong>{{NurseryAverage}}ms</strong>. | |
<span ng-show="{{FullGCRuns > 0}}"> | |
The average full collection time was <strong>{{FullAverage}}ms</strong>. | |
</span> | |
</p> | |
<h3>Dynamic Optimization</h3> | |
<p>Of {{OptimizedFrames}} specialized or JIT-compiled frames, there were | |
<strong>{{DeoptOnes}} deoptimizations</strong> (that's <strong> | |
{{DeoptOnePercent}}%</strong> of all optimized frames). | |
</p> | |
<p ng-show="DeoptAlls == 0"> | |
There was <strong>no global deoptimization</strong> triggered by the | |
profiled code. | |
</p> | |
<p ng-show="DeoptAlls == 1"> | |
There was <strong>one global deoptimization</strong> triggered by the | |
profiled code. | |
</p> | |
<p ng-show="DeoptAlls > 1"> | |
There were <strong>{{DeoptAlls}} global deoptimization</strong> triggered | |
by the profiled code. | |
</p> | |
<p ng-show="OSRs == 0"> | |
There was <strong>no On Stack Replacement</strong> performed while | |
executing the profiled code (normal if the code lacks long-running | |
loops with many iterations). | |
</p> | |
<p ng-show="OSRs == 1"> | |
There was <strong>one On Stack Replacement</strong> performed while | |
executing the profiled code. | |
</p> | |
<p ng-show="OSRs > 1"> | |
There were <strong>{{OSRs}} On Stack Replacements</strong> performed | |
while executing the profiled code. | |
</p> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'Routines'"> | |
<div ng-controller="RoutinesController"> | |
<table class="table table-striped table-condensed table-bordered"> | |
<thead> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a> | |
<input ng-model="NameFilter"> | |
</th> | |
<th><a href="" ng-click="reverse = predicate == 'Entries' ? !reverse : true; predicate = 'Entries';">Entries</a></th> | |
<th><a href="" ng-click="reverse = predicate == 'InclusiveTimePercent' ? !reverse : true; predicate = 'InclusiveTimePercent';">Inclusive Time</a></th> | |
<th><a href="" ng-click="reverse = predicate == 'ExclusiveTimePercent' ? !reverse : true; predicate = 'ExclusiveTimePercent';">Exclusive Time</a></th> | |
<th> | |
<span class="text-danger" tooltip-placement="bottom" tooltip="Unoptimized interpreted code">Interp</span> / | |
<span class="text-warning" tooltip-placement="bottom" tooltip="Type-specialized interpreted code">Spesh</span> / | |
<span class="text-success" tooltip-placement="bottom" tooltip="Type-specialized, JIT-compiled code">JIT</span> | |
</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="routine in Routines | filter:NameFilter | orderBy:predicate:reverse"> | |
<td> | |
<strong>{{routine.Name}}</strong><br /> | |
<span class="text-muted">{{routine.File}}:{{routine.Line}}</span> | |
</td> | |
<td>{{routine.Entries}}</td> | |
<td> | |
<div class="pull-left" style="width: 70px"> | |
<div class="progress" style="width: 60px"> | |
<div class="progress-bar" role="progressbar" style="width: {{routine.InclusiveTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
<strong>{{routine.InclusiveTimePercent}}%</strong> | |
({{routine.InclusiveTime}}ms) | |
</div> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 70px"> | |
<div class="progress" style="width: 60px"> | |
<div class="progress-bar" role="progressbar" style="width: {{routine.ExclusiveTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
<strong>{{routine.ExclusiveTimePercent}}%</strong> | |
({{routine.ExclusiveTime}}ms) | |
</div> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 110px"> | |
<div class="progress" style="width: 100px"> | |
<div class="progress-bar progress-bar-danger" style="width: {{routine.InterpEntriesPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-warning" style="width: {{routine.SpeshEntriesPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-success" style="width: {{routine.JITEntriesPercent}}%"> | |
</div> | |
</div> | |
</div> | |
<div> | |
<span class="label label-default" ng-show="routine.OSR">OSR</span> | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<script type="text/ng-template" id="icycle_graph_callee_renderer.html"> | |
<a href="#" class="call" style="background-color:{{backgroundColor(callee)}}" ng-click="toCallee(callee)">{{callee.name}}</a> | |
<div class="child" style="width: {{callee.inclusive_time * 100 / Current.inclusive_time}}%;" ng-repeat="callee in callee.callees" title="{{callee.name}}" ng-include="'icycle_graph_callee_renderer.html'"></div> | |
</script> | |
<div class="container" ng-show="Tab == 'Call Graph'"> | |
<div ng-controller="CallGraphController"> | |
<ol class="breadcrumb"> | |
<li><strong>Callers:</strong></li> | |
<li ng-show="RecentCallers.length == 0"><em>None</em></li> | |
<li ng-repeat="caller in RecentCallers"> | |
<a href="" ng-click="toCaller(caller)"> | |
{{caller.name == '' ? '<anon>' : caller.name}} | |
</a> | |
</li> | |
</ol> | |
<p> | |
<span class="lead">{{Current.name == '' ? '<anon>' : Current.name}}</span><br /> | |
<span class="text-muted">{{File}}:{{Line}}</span> | |
</p> | |
<div class="icyclegraph"> | |
<div class="child" ng-repeat="callee in [Current]" ng-include="'icycle_graph_callee_renderer.html'"></div> | |
</div> | |
<table class="table table-striped table-condensed table-bordered"> | |
<tbody> | |
<tr> | |
<td><strong>Calls <span class="text-success">(Inlined)</span></strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar" role="progressbar" style="width: {{Percent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{InlinePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div ng-show="InlinePercent == 0"> | |
{{Entries}} | |
</div> | |
<div ng-show="InlinePercent != 0"> | |
{{Entries}} + | |
<span class="text-success">{{InlineEntries}} ({{InlinePercent}}%)</span> | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Interpreted Calls</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{InterpPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{InterpPercent}}% | |
({{InterpEntries}}) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Specialized Calls</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{SpeshPercent}}% | |
({{SpeshEntries}}) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>JIT-Compiled Calls</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{JITPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{JITPercent}}% | |
({{JITEntries}}) | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<div class="panel panel-default"> | |
<div class="panel-heading">Callees</div> | |
<table class="table table-striped table-condensed table-bordered" ng-show="Callees.length > 0"> | |
<thead> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a> | |
<input ng-model="NameFilter"> | |
</th> | |
<th><a href="" ng-click="reverse = predicate == 'Calls' ? !reverse : true; predicate = 'Calls';">Calls</a></th> | |
<th><a href="" ng-click="reverse = predicate == 'TimePercent' ? !reverse : true; predicate = 'TimePercent';">Time In Callee</a></th> | |
<th> | |
<span class="text-danger" tooltip-placement="bottom" tooltip="Unoptimized interpreted code">Interp</span> / | |
<span class="text-warning" tooltip-placement="bottom" tooltip="Type-specialized interpreted code">Spesh</span> / | |
<span class="text-success" tooltip-placement="bottom" tooltip="Type-specialized, JIT-compiled code">JIT</span> | |
</th> | |
<th> | |
<span tooltip-placement="bottom" tooltip="Code from this callee was flattened into the routine by the optimizer">Inlined</span> | |
</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="callee in Callees | filter:NameFilter | orderBy:predicate:reverse"> | |
<td> | |
<strong><a href="" ng-click="toCallee(callee.Node)">{{callee.Name}}</a></strong><br /> | |
<span class="text-muted">{{callee.File}}:{{callee.Line}}</span> | |
</td> | |
<td>{{callee.Calls}}</td> | |
<td> | |
<div class="pull-left" style="width: 70px"> | |
<div class="progress" style="width: 60px"> | |
<div class="progress-bar" role="progressbar" style="width: {{callee.TimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
<strong>{{callee.TimePercent}}%</strong> | |
({{callee.Time}}ms) | |
</div> | |
</td> | |
<td> | |
<div class="progress" style="width: 100px"> | |
<div class="progress-bar progress-bar-danger" style="width: {{callee.InterpCallsPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-warning" style="width: {{callee.SpeshCallsPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-success" style="width: {{callee.JITCallsPercent}}%"> | |
</div> | |
</div> | |
</td> | |
<td> | |
<div ng-show="callee.VeryInline"> | |
<span class="text-success"> | |
<span class="glyphicon glyphicon-star"></span> | |
</span> | |
{{callee.InlinedPercent}}% | |
</div> | |
<div ng-show="callee.SometimesInline"> | |
<span class="text-muted"> | |
<span class="glyphicon glyphicon-star-empty"></span> | |
</span> | |
{{callee.InlinedPercent}}% | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<div class="panel-body" ng-show="Callees.length == 0"> | |
This code has no callees. | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'Allocations'"> | |
<div ng-controller="AllocationsController"> | |
<table class="table table-striped table-condensed table-bordered"> | |
<thead> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a> | |
<input ng-model="NameFilter"> | |
</th> | |
<th><a href="" ng-click="reverse = predicate == 'Allocations' ? !reverse : true; predicate = 'Allocations';">Allocations</a></th> | |
<th>Allocating Routines</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="alloc in AllocationSummary | filter:NameFilter | orderBy:predicate:reverse"> | |
<td><strong>{{alloc.Name}}</strong></td> | |
<td> | |
<div class="pull-left" style="width: 210px"> | |
<div class="progress" style="width: 200px"> | |
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{alloc.AllocationsInterpPercent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{alloc.AllocationsSpeshPercent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{alloc.AllocationsJitPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{alloc.Allocations}} | |
</div> | |
</td> | |
<td> | |
<a href="" ng-click="showAllocatingRoutines(alloc)">View</a> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<script type="text/ng-template" id="myModalContent.html"> | |
<div class="modal-header"> | |
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> | |
<h4 class="modal-title" id="allocatingRoutinesModalLabel"> | |
Routines Allocating {{CurrentAllocatingRoutine}} | |
</h4> | |
</div> | |
<div class="modal-body"> | |
<table class="table table-striped table-condensed table-bordered"> | |
<thead> | |
<th> | |
<a href="" ng-click="routineReverse = routinePredicate == 'Name' ? !routineReverse : false; routinePredicate = 'Name';">Name</a> | |
<input ng-model="RoutineNameFilter"> | |
</th> | |
<th><a href="" ng-click="routineReverse = routinePredicate == 'Allocations' ? !routineReverse : true; routinePredicate = 'Allocations';">Allocations</a></th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="routine in CurrentAllocatingRoutineStats | filter:RoutineNameFilter | orderBy:routinePredicate:routineReverse"> | |
<td> | |
<strong>{{routine.Name}}</strong><br /> | |
<span class="text-muted">{{routine.File}}:{{routine.Line}}</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 210px"> | |
<div class="progress" style="width: 200px"> | |
<div class="progress-bar progress-bar-danger" style="width: {{routine.AllocationsInterpPercent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-warning" style="width: {{routine.AllocationsSpeshPercent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-success" style="width: {{routine.AllocationsJitPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{routine.Allocations}} | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> | |
</div> | |
</script> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'GC'"> | |
<div ng-controller="GCController"> | |
<table class="table table-striped table-condensed table-bordered"> | |
<thead> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Run' ? !reverse : false; predicate = 'Run';">Run</a> | |
</th> | |
<th>Full</th> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Time' ? !reverse : false; predicate = 'Time';">Time</a> | |
</th> | |
<th> | |
Nursery: | |
<span class="text-danger" tooltip-placement="bottom" tooltip="Bytes retained in the nursery">Retained</span> / | |
<span class="text-warning" tooltip-placement="bottom" tooltip="Bytes promoted to gen2 and now available in nursery">Promoted</span> / | |
<span class="text-success" tooltip-placement="bottom" tooltip="Bytes released and now availabe in nursery">Freed</span> | |
</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="gc in GCs | orderBy:predicate:reverse"> | |
<td><strong>{{gc.Run}}</strong></td> | |
<td> | |
<span class="text-success" ng-show="gc.Full"> | |
<span class="glyphicon glyphicon-star"></span> | |
</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 210px"> | |
<div class="progress" style="width: 200px"> | |
<div class="progress-bar" role="progressbar" style="width: {{gc.TimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{gc.Time}}ms | |
</div> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 210px"> | |
<div class="progress" style="width: 200px"> | |
<div class="progress-bar progress-bar-danger" style="width: {{gc.RetainedPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-warning" style="width: {{gc.PromotedPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-success" style="width: {{gc.ClearedPercent}}%"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{gc.RetainedKilobytes}}KB / | |
{{gc.PromotedKilobytes}}KB / | |
{{gc.ClearedKilobytes}}KB ... | |
<small>{{gc.Gen2Roots}} gen2 roots</small> | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'OSR/Deopt'"> | |
<div ng-controller="OSRDeoptController"> | |
<h3>OSR</h3> | |
<p>On Stack Replacement detects routines containing hot loops that are | |
being interpreted, and replaces them with specialized or JIT-compiled | |
code.</p> | |
<table class="table table-striped table-condensed table-bordered" ng-show="OSRs.length > 0"> | |
<thead> | |
<th> | |
Routine | |
</th> | |
<th>On Stack Replacements</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="osr in OSRs | orderBy:predicate:reverse"> | |
<td> | |
<strong>{{osr.Name}}</strong><br /> | |
<span class="text-muted">{{osr.File}}:{{osr.Line}}</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar" role="progressbar" style="width: {{osr.Percent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{osr.Count}} | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<p ng-show="OSRs.length == 0"> | |
<em>No OSR was performed during this profile.</em> | |
</p> | |
<h3>Local Deoptimization</h3> | |
<p>Local deoptimization happens when a guard in specialized or JIT-compiled | |
code fails. Since the code was produced assuming the guard would hold, | |
the VM falls back to running the safe, but slower, interpreted code.</p> | |
<table class="table table-striped table-condensed table-bordered" ng-show="DeoptOnes.length > 0"> | |
<thead> | |
<th> | |
Routine | |
</th> | |
<th>Deoptimizations</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="deopt in DeoptOnes | orderBy:predicate:reverse"> | |
<td> | |
<strong>{{deopt.Name}}</strong><br /> | |
<span class="text-muted">{{deopt.File}}:{{deopt.Line}}</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar" role="progressbar" style="width: {{deopt.Percent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{deopt.Count}} | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<p ng-show="DeoptOnes.length == 0"> | |
<em>No local deoptimizations occurred during this profile.</em> | |
</p> | |
<h3>Global Deoptimization</h3> | |
<p>Global deoptimization happens when an event occurs that renders | |
all currently type-specialized or JIT-compiled code on the call | |
stack potentially invalid. Mixins - changing the type of an object | |
in place - are a common reason.</p> | |
<table class="table table-striped table-condensed table-bordered" ng-show="DeoptAlls.length > 0"> | |
<thead> | |
<th> | |
Routine | |
</th> | |
<th>Deoptimizations</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="deopt in DeoptAlls | orderBy:predicate:reverse"> | |
<td> | |
<strong>{{deopt.Name}}</strong><br /> | |
<span class="text-muted">{{deopt.File}}:{{deopt.Line}}</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar" role="progressbar" style="width: {{deopt.Percent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{deopt.Count}} | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<p ng-show="DeoptAlls.length == 0"> | |
<em>No global deoptimizations occurred during this profile.</em> | |
</p> | |
</div> | |
</div> | |
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.2.0/ui-bootstrap-tpls.js"></script> | |
<script> | |
var rawData = JSON.parse('[{"9":"List","8":{"name":"","line":3152,"file":"gen/moar/BOOTSTRAP.nqp"},"7":"CallCapture","6":"BOOTHash","57":{"name":"sink","line":13,"file":"SETTING::src/core/Mu.pm"},"56":{"name":"","line":-1,"file":"/Users/liz/Github/rakudo.moar/install/share/perl6/runtime/CORE.setting.moarvm"},"55":{"name":"last","line":77,"file":"SETTING::src/core/control.pm"},"54":{"name":"last","line":76,"file":"SETTING::src/core/control.pm"},"53":{"name":"","line":1,"file":"-e"},"52":"Int","51":{"name":"floor","line":135,"file":"SETTING::src/core/Num.pm"},"50":{"name":"rand","line":126,"file":"SETTING::src/core/Num.pm"},"5":{"name":"roll","line":1079,"file":"SETTING::src/core/List.pm"},"49":"Num","48":{"name":"Bridge","line":54,"file":"SETTING::src/core/Int.pm"},"47":{"name":"rand","line":9,"file":"SETTING::src/core/Real.pm"},"46":{"name":"","line":1093,"file":"SETTING::src/core/List.pm"},"45":{"name":"pull-one","line":962,"file":"SETTING::src/core/Rakudo/Internals.pm"},"44":{"name":"","line":448,"file":"SETTING::src/core/Any-iterable-methods.pm"},"43":{"name":"sink-all","line":440,"file":"SETTING::src/core/Any-iterable-methods.pm"},"42":{"name":"sink","line":158,"file":"SETTING::src/core/Seq.pm"},"41":"Any::IterateOneWithoutPhasers","40":{"name":"new","line":334,"file":"SETTING::src/core/Any-iterable-methods.pm"},"4":{"name":"<unit>","line":1,"file":"-e"},"39":{"name":"","line":1437,"file":"gen/moar/BOOTSTRAP.nqp"},"38":{"name":"returns","line":7,"file":"SETTING::src/core/Block.pm"},"37":{"name":"infix:«<»","line":338,"file":"SETTING::src/core/Int.pm"},"36":{"name":"has-phasers","line":36,"file":"SETTING::src/core/Block.pm"},"35":{"name":"count","line":13,"file":"SETTING::src/core/Code.pm"},"34":{"name":"sequential-map","line":779,"file":"SETTING::src/core/Any-iterable-methods.pm"},"33":{"name":"type_check","line":2659,"file":"gen/moar/Metamodel.nqp"},"32":{"name":"iterator","line":52,"file":"SETTING::src/core/Seq.pm"},"31":{"name":"map","line":17,"file":"SETTING::src/core/Any-iterable-methods.pm"},"30":{"name":"map","line":11,"file":"SETTING::src/core/Any-iterable-methods.pm"},"3":"BOOTArray","29":"Seq","28":{"name":"new","line":42,"file":"SETTING::src/core/Seq.pm"},"27":"<anon|140492422530512>","26":{"name":"new","line":959,"file":"SETTING::src/core/Rakudo/Internals.pm"},"25":{"name":"","line":957,"file":"SETTING::src/core/Rakudo/Internals.pm"},"24":{"name":"UnendingCallableIterator","line":956,"file":"SETTING::src/core/Rakudo/Internals.pm"},"23":"Block","22":{"name":"","line":1660,"file":"gen/moar/BOOTSTRAP.nqp"},"21":{"name":"elems","line":536,"file":"SETTING::src/core/List.pm"},"20":{"name":"is-lazy","line":1001,"file":"SETTING::src/core/List.pm"},"2":"BOOTCode","19":"Scalar","18":{"name":"roll","line":1087,"file":"SETTING::src/core/List.pm"},"17":{"name":"add_to_cache","line":2451,"file":"gen/moar/BOOTSTRAP.nqp"},"16":{"name":"is_narrower","line":1853,"file":"gen/moar/BOOTSTRAP.nqp"},"15":"BOOTIntArray","14":"NQPArrayIter","13":{"name":"","line":1823,"file":"gen/moar/BOOTSTRAP.nqp"},"12":"BOOTInt","11":"NQPArray","10":{"name":"","line":2167,"file":"gen/moar/BOOTSTRAP.nqp"},"1":{"name":"<unit-outer>","line":1,"file":"-e"},"0":{"name":"","line":1570,"file":"gen/moar/stage2/NQPHLL.nqp"}},{"total_time":259159,"spesh_time":1485,"gcs":[{"time":10285,"retained_bytes":1040272,"promoted_bytes":0,"gen2_roots":12677,"full":0,"cleared_bytes":3154024},{"time":11431,"retained_bytes":40,"promoted_bytes":6137103,"gen2_roots":17186,"full":0,"cleared_bytes":4293024425},{"time":11876,"retained_bytes":40,"promoted_bytes":0,"gen2_roots":2068,"full":0,"cleared_bytes":4194240}],"call_graph":{"inclusive_time":223393,"id":"0","exclusive_time":9,"entries":1,"callees":[{"inclusive_time":223383,"id":"1","exclusive_time":17,"entries":1,"callees":[{"inclusive_time":223366,"id":"4","exclusive_time":34,"entries":1,"callees":[{"inclusive_time":547,"id":"5","exclusive_time":149,"entries":1,"callees":[{"inclusive_time":0,"id":"8","exclusive_time":0,"entries":1,"allocations":[{"id":"9","count":1}]},{"inclusive_time":227,"id":"10","exclusive_time":83,"entries":1,"callees":[{"inclusive_time":143,"id":"13","exclusive_time":136,"entries":1,"callees":[{"inclusive_time":6,"id":"16","exclusive_time":6,"entries":6}],"allocations":[{"id":"11","count":17},{"id":"14","count":4},{"id":"6","count":6},{"id":"15","count":6},{"id":"12","count":3}]},{"inclusive_time":0,"id":"17","exclusive_time":0,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":169,"id":"18","exclusive_time":82,"entries":1,"callees":[{"inclusive_time":1,"id":"20","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1},{"id":"19","count":1}]},{"inclusive_time":0,"id":"21","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"22","exclusive_time":1,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"inclusive_time":83,"id":"24","exclusive_time":80,"entries":1,"callees":[{"inclusive_time":1,"id":"25","exclusive_time":1,"entries":1,"allocations":[{"id":"19","count":1}]},{"inclusive_time":1,"id":"26","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1},{"id":"27","count":1}]}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"28","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1},{"id":"29","count":1}]}],"allocations":[{"id":"6","count":1},{"id":"19","count":1}]}],"allocations":[{"id":"6","count":1},{"id":"3","count":1},{"id":"7","count":3}]},{"inclusive_time":0,"id":"22","exclusive_time":0,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"inclusive_time":119,"id":"30","exclusive_time":14,"entries":1,"callees":[{"inclusive_time":0,"id":"8","exclusive_time":0,"entries":1,"allocations":[{"id":"9","count":1}]},{"inclusive_time":10,"id":"10","exclusive_time":10,"entries":1,"callees":[{"inclusive_time":0,"id":"17","exclusive_time":0,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":93,"id":"31","exclusive_time":36,"entries":1,"callees":[{"inclusive_time":13,"id":"32","exclusive_time":13,"entries":1,"callees":[{"inclusive_time":0,"id":"33","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":43,"id":"34","exclusive_time":34,"entries":1,"callees":[{"inclusive_time":0,"id":"35","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"36","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"37","exclusive_time":0,"entries":1},{"inclusive_time":4,"id":"38","exclusive_time":4,"entries":1,"callees":[{"inclusive_time":0,"id":"39","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"40","exclusive_time":1,"entries":1,"allocations":[{"id":"19","count":2},{"id":"6","count":1},{"id":"41","count":1}]},{"inclusive_time":0,"id":"28","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1},{"id":"29","count":1}]}],"allocations":[{"id":"19","count":2}]}],"allocations":[{"id":"19","count":2},{"id":"6","count":1}]}],"allocations":[{"id":"3","count":1},{"id":"7","count":3}]},{"inclusive_time":222664,"id":"42","exclusive_time":26,"entries":1,"callees":[{"inclusive_time":1,"id":"32","exclusive_time":1,"entries":1,"callees":[{"inclusive_time":0,"id":"33","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":222634,"id":"43","exclusive_time":13,"entries":1,"callees":[{"inclusive_time":0,"id":"22","exclusive_time":0,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"osr":1,"jit_entries":1,"inclusive_time":222620,"id":"44","exclusive_time":36572,"entries":1,"callees":[{"jit_entries":99844,"inclusive_time":180059,"id":"45","exclusive_time":19367,"entries":100001,"callees":[{"jit_entries":99844,"inclusive_time":160691,"id":"46","exclusive_time":41156,"entries":100001,"callees":[{"spesh_entries":99844,"inclusive_time":57240,"id":"47","exclusive_time":35025,"entries":100001,"callees":[{"spesh_entries":99844,"inclusive_time":10623,"id":"48","exclusive_time":10623,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"49","count":100001}]},{"spesh_entries":99844,"inclusive_time":11591,"id":"50","exclusive_time":11591,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"49","count":100001}]}],"allocations":[{"id":"6","count":157}]},{"spesh_entries":99844,"inclusive_time":62295,"id":"51","exclusive_time":62295,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"52","count":100001}]}],"allocations":[{"id":"19","count":1}]}],"allocations":[{"id":"6","count":157}]},{"jit_entries":99844,"inclusive_time":5989,"id":"53","exclusive_time":5821,"entries":100001,"callees":[{"inclusive_time":167,"id":"54","exclusive_time":64,"entries":1,"callees":[{"inclusive_time":12,"id":"10","exclusive_time":11,"entries":1,"callees":[{"inclusive_time":1,"id":"17","exclusive_time":1,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":90,"id":"55","exclusive_time":87,"entries":1,"callees":[{"inclusive_time":3,"id":"56","exclusive_time":3,"entries":1,"allocations":[{"id":"52","count":1}]}]}],"allocations":[{"id":"7","count":3}]}]}],"allocations":[{"id":"19","count":1}]}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"57","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1}]}],"allocations":[{"id":"6","count":1}]}]}],"allocations":[{"id":"2","count":1},{"id":"3","count":1}]}]}}]'); | |
// grab the "dictionary" from the very first entry | |
var id_to_things = rawData[0]; | |
// the rest of the program expects the list to contain callframes, not the dictionary | |
// so we just shift it off the beginning. | |
rawData.shift(); | |
// Extract some common things out of the raw data. | |
var nodeIdToName = {}; | |
var nodeIdToFile = {}; | |
var nodeIdToLine = {}; | |
(function () { | |
function walkCallGraphNode(node) { | |
if (!nodeIdToName[node.id]) { | |
var existing_data = id_to_things[node.id.toString()]; | |
node.name = existing_data.name; | |
node.line = existing_data.line; | |
node.file = existing_data.file; | |
nodeIdToName[node.id] = node.name == "" ? "<anon>" : node.name; | |
nodeIdToLine[node.id] = node.line < 1 ? "<unknown>" : node.line; | |
nodeIdToFile[node.id] = node.file == "" ? "<unknown>" : node.file; | |
} | |
if (node.callees) | |
node.callees.map(walkCallGraphNode); | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
}()); | |
// Register application and add controllers, increase recursion depth for callGraphController | |
var moarProfApp = angular.module('moarProfApp', ['ui.bootstrap']).config(function($rootScopeProvider) { | |
$rootScopeProvider.digestTtl(100); | |
}); | |
moarProfApp.controller('NavigationController', function ($scope) { | |
$scope.Tab = 'Overview'; | |
}); | |
moarProfApp.controller('OverviewController', function ($scope) { | |
var totalTime = rawData[0].total_time; | |
var speshTime = rawData[0].spesh_time; | |
var gcTime = 0; | |
var gcNursery = 0; | |
var gcFull = 0; | |
var gcNurseryTime = 0; | |
var gcFullTime = 0; | |
var totalEntries = 0; | |
var inlineEntries = 0; | |
var jitEntries = 0; | |
var speshEntries = 0; | |
var deoptOnes = 0; | |
var deoptAlls = 0; | |
var osrs = 0; | |
rawData[0].gcs.map(function (gc) { | |
gcTime += gc.time; | |
if (gc.full) { | |
gcFull++; | |
gcFullTime += gc.time; | |
} | |
else { | |
gcNursery++; | |
gcNurseryTime += gc.time; | |
} | |
}); | |
function walkCallGraphNode(node) { | |
totalEntries += node.entries || 0; | |
inlineEntries += node.inlined_entries || 0; | |
speshEntries += node.spesh_entries || 0; | |
jitEntries += node.jit_entries || 0; | |
deoptOnes += node.deopt_one || 0; | |
deoptAlls += node.deopt_all || 0; | |
osrs += node.osr || 0; | |
if (node.callees) | |
node.callees.map(walkCallGraphNode); | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
// Time spent | |
var overheadTime = speshTime + gcTime; | |
var executingTime = totalTime - overheadTime; | |
$scope.TotalTime = +(totalTime / 1000).toFixed(2); | |
$scope.OverheadTime = +(overheadTime / 1000).toFixed(2); | |
$scope.OverheadTimePercent = +(100 * overheadTime / totalTime).toFixed(2); | |
$scope.ExecutingTime = +(executingTime / 1000).toFixed(2); | |
$scope.ExecutingTimePercent = +(100 * executingTime / totalTime).toFixed(2); | |
$scope.GCTime = +(gcTime / 1000).toFixed(2); | |
$scope.GCTimePercent = +(100 * gcTime / totalTime).toFixed(2); | |
$scope.SpeshTime = +(speshTime / 1000).toFixed(2); | |
$scope.SpeshTimePercent = +(100 * speshTime / totalTime).toFixed(2); | |
// Routines | |
var interpEntries = totalEntries - (jitEntries + speshEntries); | |
$scope.EntriesWithoutInline = totalEntries - inlineEntries; | |
$scope.EntriesInline = inlineEntries; | |
$scope.InlinePercent = +(100 * inlineEntries / totalEntries).toFixed(2); | |
$scope.InterpFrames = interpEntries; | |
$scope.InterpFramesPercent = +(100 * interpEntries / totalEntries).toFixed(2); | |
$scope.SpeshFrames = speshEntries; | |
$scope.SpeshFramesPercent = +(100 * speshEntries / totalEntries).toFixed(2); | |
$scope.JITFrames = jitEntries; | |
$scope.JITFramesPercent = +(100 * jitEntries / totalEntries).toFixed(2); | |
// Garbage collection | |
$scope.GCRuns = gcNursery + gcFull; | |
$scope.FullGCRuns = gcFull; | |
$scope.NurseryAverage = +((gcNurseryTime / 1000) / gcNursery).toFixed(2); | |
$scope.FullAverage = +((gcFullTime / 1000) / gcFull).toFixed(2); | |
// Dynamic optimization | |
var optimizedFrames = speshEntries + jitEntries; | |
$scope.OptimizedFrames = optimizedFrames; | |
$scope.DeoptOnes = deoptOnes; | |
$scope.DeoptOnePercent = +(100 * deoptOnes / (optimizedFrames || 1)).toFixed(2); | |
$scope.DeoptAlls = deoptAlls; | |
$scope.OSRs = osrs; | |
}); | |
moarProfApp.controller('RoutinesController', function ($scope) { | |
// Walk call graph to build data. | |
var idToEntries = {}; | |
var idToSpeshEntries = {}; | |
var idToJITEntries = {}; | |
var idToExclusive = {}; | |
var idToInclusive = {}; | |
var idToOSR = {}; | |
var idRecDepth = {}; | |
var totalExclusive = 0; | |
var totalInclusive = rawData[0].call_graph.inclusive_time; | |
function walkCallGraphNode(node) { | |
if (!idToEntries[node.id]) { | |
idToEntries[node.id] = 0; | |
idToSpeshEntries[node.id] = 0; | |
idToJITEntries[node.id] = 0; | |
idToExclusive[node.id] = 0; | |
idToInclusive[node.id] = 0; | |
idToOSR[node.id] = false; | |
idRecDepth[node.id] = 0; | |
} | |
idToEntries[node.id] += node.entries || 0; | |
idToSpeshEntries[node.id] += node.spesh_entries || 0; | |
idToJITEntries[node.id] += node.jit_entries || 0; | |
idToExclusive[node.id] += node.exclusive_time || 0; | |
totalExclusive += node.exclusive_time || 0; | |
if (node.osr > 0) | |
idToOSR[node.id] = true; | |
if (idRecDepth[node.id] == 0) | |
idToInclusive[node.id] += node.inclusive_time; | |
if (node.callees) { | |
idRecDepth[node.id]++; | |
node.callees.map(walkCallGraphNode); | |
idRecDepth[node.id]--; | |
} | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
// Build object list per routine. | |
var routineList = []; | |
for (id in idToEntries) { | |
var speshEntriesPercent = +(100 * idToSpeshEntries[id] / idToEntries[id]).toFixed(2); | |
var jitEntriesPercent = +(100 * idToJITEntries[id] / idToEntries[id]).toFixed(2); | |
var interpEntriesPercent = 100 - (speshEntriesPercent + jitEntriesPercent); | |
var entry = { | |
Name: nodeIdToName[id], | |
Line: nodeIdToLine[id], | |
File: nodeIdToFile[id], | |
Entries: idToEntries[id], | |
InterpEntriesPercent: interpEntriesPercent, | |
SpeshEntriesPercent: speshEntriesPercent, | |
JITEntriesPercent: jitEntriesPercent, | |
InclusiveTime: +(idToInclusive[id] / 1000).toFixed(2), | |
InclusiveTimePercent: +(100 * idToInclusive[id] / totalInclusive).toFixed(2), | |
ExclusiveTime: +(idToExclusive[id] / 1000).toFixed(2), | |
ExclusiveTimePercent: +(100 * idToExclusive[id] / totalExclusive).toFixed(2), | |
OSR: idToOSR[id] | |
}; | |
routineList.push(entry); | |
} | |
$scope.Routines = routineList; | |
$scope.predicate = "InclusiveTimePercent"; | |
$scope.reverse = true; | |
}); | |
moarProfApp.controller('CallGraphController', function ($scope) { | |
$scope.Current = rawData[0].call_graph; | |
$scope.total_time = rawData[0].total_time; | |
$scope.SuchCallers = false; | |
$scope.RecentCallers = []; | |
$scope.predicate = "TimePercent"; | |
$scope.reverse = true; | |
var all_callers = []; | |
updateCurrentData(); | |
$scope.toCallee = function (callee) { | |
// Update caller history. | |
all_callers.push($scope.Current); | |
$scope.RecentCallers.push($scope.Current); | |
if ($scope.RecentCallers.length > 5) | |
$scope.RecentCallers.shift(); | |
// Update current node and callees. | |
$scope.Current = callee; | |
updateCurrentData(); | |
}; | |
$scope.toCaller = function (caller) { | |
// Update caller history. | |
while (all_callers.length > 0) { | |
var removed = all_callers.pop(); | |
if ($scope.RecentCallers.length > 0) | |
$scope.RecentCallers.pop(); | |
if (removed == caller) | |
break; | |
} | |
if (all_callers.length > $scope.RecentCallers.length) { | |
var ptr = all_callers.length - $scope.RecentCallers.length; | |
while (ptr >= 0 && $scope.RecentCallers.length < 5) { | |
$scope.RecentCallers.unshift(all_callers[ptr]); | |
ptr--; | |
} | |
} | |
// Update current node and callees. | |
$scope.Current = caller; | |
updateCurrentData(); | |
} | |
/* | |
* Given a callee, create a unique, repeatable color; | |
* h/t https://stackoverflow.com/questions/3426404 | |
*/ | |
$scope.backgroundColor = function (callee) { | |
var str = callee.$$hashKey + callee.file + callee.name; | |
for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash)); | |
for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((hash >> i++ * 8) & 0xFF).toString(16)).slice(-2)); | |
return colour; | |
} | |
function updateCurrentData() { | |
// Line and file. | |
var current = $scope.Current; | |
$scope.Line = current.line; | |
$scope.File = current.file; | |
// Entry statistics. | |
var interpEntries = current.entries - (current.spesh_entries + current.jit_entries); | |
var nonInlineEntries = current.entries - current.inlined_entries; | |
$scope.Entries = nonInlineEntries; | |
$scope.Percent = (100 * nonInlineEntries / current.entries).toFixed(2); | |
$scope.InlineEntries = current.inlined_entries; | |
$scope.InlinePercent = (100 * current.inlined_entries / current.entries).toFixed(2); | |
$scope.InterpEntries = interpEntries; | |
$scope.InterpPercent = (100 * interpEntries / current.entries).toFixed(2); | |
$scope.SpeshEntries = current.spesh_entries; | |
$scope.SpeshPercent = (100 * current.spesh_entries / current.entries).toFixed(2); | |
$scope.JITEntries = current.jit_entries; | |
$scope.JITPercent = (100 * current.jit_entries / current.entries).toFixed(2); | |
// Callees. | |
$scope.Callees = calleesOf(current); | |
} | |
function calleesOf(node) { | |
if (!node.callees) | |
return []; | |
var totalExclusive = 0.0; | |
node.callees.map(function (c) { totalExclusive += c.exclusive_time; }); | |
return node.callees.map(function (c) { | |
var speshCallsPercent = +(100 * c.spesh_entries / c.entries).toFixed(2); | |
var jitCallsPercent = +(100 * c.jit_entries / c.entries).toFixed(2); | |
var interpCallsPercent = 100 - (speshCallsPercent + jitCallsPercent); | |
var inlinedPercent = +(100 * c.inlined_entries / c.entries).toFixed(2); | |
return { | |
Name: nodeIdToName[c.id], | |
Line: nodeIdToLine[c.id], | |
File: nodeIdToFile[c.id], | |
Calls: c.entries, | |
Time: +(c.inclusive_time / 1000).toFixed(2), | |
TimePercent: +(100 * c.inclusive_time / node.inclusive_time).toFixed(2), | |
InterpCallsPercent: interpCallsPercent, | |
SpeshCallsPercent: speshCallsPercent, | |
JITCallsPercent: jitCallsPercent, | |
InlinedPercent: inlinedPercent, | |
VeryInline: inlinedPercent >= 95, | |
SometimesInline: inlinedPercent < 95 && inlinedPercent > 10, | |
Node: c | |
}; | |
}); | |
} | |
}); | |
moarProfApp.controller('AllocationsController', function ($scope, $modal) { | |
// Traverse all call nodes, counting up the allocations. | |
var typeIdToName = {}; | |
var typeIdToAllocations = {}; | |
var typeIdToAllocationsByType = {}; | |
var typeIdToRoutineStats = {}; | |
var maxAllocations = 1; | |
function walkCallGraphNode(node) { | |
if (node.allocations) { | |
node.allocations.map(function (alloc) { | |
alloc.type = id_to_things[alloc.id]; | |
if (!typeIdToName[alloc.id]) { | |
typeIdToName[alloc.id] = alloc.type == "" ? "<anon>" : alloc.type; | |
typeIdToAllocations[alloc.id] = 0; | |
typeIdToAllocationsByType[alloc.id] = [0, 0, 0]; | |
typeIdToRoutineStats[alloc.id] = {}; | |
} | |
typeIdToAllocations[alloc.id] += alloc.count; | |
typeIdToAllocationsByType[alloc.id][0] += alloc.count - (alloc.spesh || 0) - (alloc.jit || 0); | |
typeIdToAllocationsByType[alloc.id][1] += alloc.spesh || 0; | |
typeIdToAllocationsByType[alloc.id][2] += alloc.jit || 0; | |
if (typeIdToAllocations[alloc.id] > maxAllocations) | |
maxAllocations = typeIdToAllocations[alloc.id]; | |
if (typeIdToRoutineStats[alloc.id][node.id]) { | |
typeIdToRoutineStats[alloc.id][node.id]['count'] += alloc.count || 0; | |
typeIdToRoutineStats[alloc.id][node.id]['spesh'] += alloc.spesh || 0; | |
typeIdToRoutineStats[alloc.id][node.id]['jit'] += alloc.jit || 0; | |
} else { | |
typeIdToRoutineStats[alloc.id][node.id] = { | |
count: alloc.count || 0, | |
spesh: alloc.spesh || 0, | |
jit: alloc.jit || 0 | |
}; | |
} | |
}); | |
} | |
if (node.callees) { | |
node.callees.map(walkCallGraphNode); | |
} | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
// Build allocation summary. | |
var allocationSummary = []; | |
for (id in typeIdToName) { | |
var maxAllocationByRoutine = 1; | |
for (var rid in typeIdToRoutineStats[id]) | |
if (typeIdToRoutineStats[id][rid]['count'] > maxAllocationByRoutine) | |
maxAllocationByRoutine = typeIdToRoutineStats[id][rid]['count']; | |
var routineStats = []; | |
for (var rid in typeIdToRoutineStats[id]) | |
routineStats.push({ | |
Name: nodeIdToName[rid], | |
Line: nodeIdToLine[rid], | |
File: nodeIdToFile[rid], | |
Allocations: typeIdToRoutineStats[id][rid]['count'], | |
AllocationsSpesh: typeIdToRoutineStats[id][rid]['spesh'], | |
AllocationsJit: typeIdToRoutineStats[id][rid]['jit'], | |
AllocationsPercent: (100 * typeIdToRoutineStats[id][rid]['count'] / maxAllocationByRoutine), | |
AllocationsInterpPercent: (100 * (typeIdToRoutineStats[id][rid]['count'] - typeIdToRoutineStats[id][rid]['jit'] - typeIdToRoutineStats[id][rid]['spesh']) / maxAllocationByRoutine), | |
AllocationsSpeshPercent: (100 * typeIdToRoutineStats[id][rid]['spesh'] / maxAllocationByRoutine), | |
AllocationsJitPercent: (100 * typeIdToRoutineStats[id][rid]['jit'] / maxAllocationByRoutine) | |
}); | |
var entry = { | |
Name: typeIdToName[id], | |
Allocations: typeIdToAllocations[id], | |
AllocationsPercent: +(100 * typeIdToAllocations[id] / maxAllocations).toFixed(2), | |
AllocationsInterpPercent: +(100 * typeIdToAllocationsByType[id][0] / maxAllocations).toFixed(2), | |
AllocationsSpeshPercent: +(100 * typeIdToAllocationsByType[id][1] / maxAllocations).toFixed(2), | |
AllocationsJitPercent: +(100 * typeIdToAllocationsByType[id][2] / maxAllocations).toFixed(2), | |
RoutineStats: routineStats | |
}; | |
allocationSummary.push(entry); | |
} | |
$scope.AllocationSummary = allocationSummary; | |
$scope.predicate = "Allocations"; | |
$scope.reverse = true; | |
$scope.routinePredicate = "Allocations"; | |
$scope.routineReverse = true; | |
// Allocating routines handlng. | |
$scope.showAllocatingRoutines = function (alloc) { | |
// Show modal dialog with data. | |
$scope.CurrentAllocatingRoutine = alloc.Name; | |
$scope.CurrentAllocatingRoutineStats = alloc.RoutineStats; | |
var modalInstance = $modal.open({ | |
templateUrl: 'myModalContent.html', | |
scope: $scope | |
}); | |
} | |
}); | |
moarProfApp.controller('GCController', function ($scope) { | |
// Find longest GC run. | |
var longestGC = 0; | |
rawData[0].gcs.map(function (gc) { | |
if (gc.time > longestGC) | |
longestGC = gc.time; | |
}); | |
// Produce something nice to render. | |
var run = 0; | |
$scope.GCs = rawData[0].gcs.map(function (gc) { | |
var totalBytes = gc.cleared_bytes + gc.retained_bytes + gc.promoted_bytes; | |
return { | |
Run: ++run, | |
Time: +(gc.time / 1000).toFixed(2), | |
Full: (gc.full != 0), | |
TimePercent: +(100 * gc.time / longestGC).toFixed(2), | |
RetainedKilobytes: Math.round(gc.retained_bytes / 1024), | |
PromotedKilobytes: Math.round(gc.promoted_bytes / 1024), | |
ClearedKilobytes: Math.round(gc.cleared_bytes / 1024), | |
RetainedPercent: +(100 * gc.retained_bytes / totalBytes).toFixed(2), | |
PromotedPercent: +(100 * gc.promoted_bytes / totalBytes).toFixed(2), | |
ClearedPercent: +(100 * gc.cleared_bytes / totalBytes).toFixed(2), | |
Gen2Roots: 'gen2_roots' in gc ? gc.gen2_roots : 0 | |
}; | |
}); | |
$scope.predicate = 'Run'; | |
$scope.reverse = false; | |
}); | |
moarProfApp.controller('OSRDeoptController', function ($scope) { | |
// Walk call graph to build data. | |
var idToOSR = {}; | |
var idToDeoptOne = {}; | |
var idToDeoptAll = {}; | |
var maxOSR = 1; | |
var maxDeoptOne = 1; | |
var maxDeoptAll = 1; | |
function walkCallGraphNode(node) { | |
if (!idToOSR[node.id]) { | |
idToOSR[node.id] = 0; | |
idToDeoptOne[node.id] = 0; | |
idToDeoptAll[node.id] = 0; | |
} | |
idToOSR[node.id] += node.osr || 0; | |
idToDeoptOne[node.id] += node.deopt_one || 0; | |
idToDeoptAll[node.id] += node.deopt_all || 0; | |
if (idToOSR[node.id] > maxOSR) | |
maxOSR = idToOSR[node.id]; | |
if (idToDeoptOne[node.id] > maxDeoptOne) | |
maxDeoptOne = idToDeoptOne[node.id]; | |
if (idToDeoptAll[node.id] > maxDeoptAll) | |
maxDeoptAll = idToDeoptAll[node.id]; | |
if (node.callees) | |
node.callees.map(walkCallGraphNode); | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
// Build up OSR, deopt one, and deopt all tables. | |
var osrs = []; | |
var deoptOnes = []; | |
var deoptAlls = []; | |
for (id in idToOSR) { | |
if (idToOSR[id] > 0) { | |
osrs.push({ | |
Name: nodeIdToName[id], | |
Line: nodeIdToLine[id], | |
File: nodeIdToFile[id], | |
Count: idToOSR[id], | |
Percent: Math.round(100 * idToOSR[id] / maxOSR) | |
}); | |
} | |
if (idToDeoptOne[id] > 0) { | |
deoptOnes.push({ | |
Name: nodeIdToName[id], | |
Line: nodeIdToLine[id], | |
File: nodeIdToFile[id], | |
Count: idToDeoptOne[id], | |
Percent: Math.round(100 * idToDeoptOne[id] / maxDeoptOne) | |
}); | |
} | |
if (idToDeoptAll[id] > 0) { | |
deoptAlls.push({ | |
Name: nodeIdToName[id], | |
Line: nodeIdToLine[id], | |
File: nodeIdToFile[id], | |
Count: idToDeoptAll[id], | |
Percent: Math.round(100 * idToDeoptAll[id] / maxDeoptAll) | |
}); | |
} | |
} | |
$scope.OSRs = osrs; | |
$scope.DeoptOnes = deoptOnes; | |
$scope.DeoptAlls = deoptAlls; | |
$scope.predicate = 'Count'; | |
$scope.reverse = true; | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en" ng-app="moarProfApp" ng-controller="NavigationController"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>MoarVM Profiler Results</title> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"> | |
<style> | |
body { | |
min-height: 500px; | |
padding-top: 70px; | |
} | |
.icyclegraph .call { | |
display: block; | |
border: 1px solid black; | |
overflow: hidden; | |
} | |
.icyclegraph .child { | |
float: left; | |
} | |
.icyclegraph a { | |
color: black; | |
} | |
</style> | |
</head> | |
<body > | |
<div class="navbar navbar-default navbar-fixed-top" role="navigation"> | |
<div class="container"> | |
<div class="navbar-header"> | |
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> | |
<span class="sr-only">Toggle navigation</span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
</button> | |
<a class="navbar-brand">MoarVM Profiler Results</a> | |
</div> | |
<div class="navbar-collapse collapse"> | |
<ul class="nav navbar-nav"> | |
<li ng-class="Tab == 'Overview' ? 'active' : ''"><a href="#overview" ng-click="Tab = 'Overview'">Overview</a></li> | |
<li ng-class="Tab == 'Routines' ? 'active' : ''"><a href="#routines" ng-click="Tab = 'Routines'">Routines</a></li> | |
<li ng-class="Tab == 'Call Graph' ? 'active' : ''"><a href="#callgraph" ng-click="Tab = 'Call Graph'">Call Graph</a></li> | |
<li ng-class="Tab == 'Allocations' ? 'active' : ''"><a href="#allocations" ng-click="Tab = 'Allocations'">Allocations</a></li> | |
<li ng-class="Tab == 'GC' ? 'active' : ''"><a href="#gc" ng-click="Tab = 'GC'">GC</a></li> | |
<li ng-class="Tab == 'OSR/Deopt' ? 'active' : ''"><a href="#osrdeopt" ng-click="Tab = 'OSR/Deopt'">OSR / Deopt</a></li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'Overview'"> | |
<div ng-controller="OverviewController"> | |
<h3>Time Spent</h3> | |
<p>The profiled code ran for <strong>{{TotalTime}}ms</strong>. Of this, | |
<strong>{{OverheadTime}}ms</strong> were spent on garbage collection | |
and dynamic optimization (that's <strong>{{OverheadTimePercent}}%</strong>). | |
</p> | |
<table class="table table-striped table-condensed table-bordered"> | |
<tbody> | |
<tr> | |
<td><strong>Executing Code</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ExecutingTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{ExecutingTimePercent}}% | |
({{ExecutingTime}}ms) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Garbage Collection</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{GCTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{GCTimePercent}}% | |
({{GCTime}}ms) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Dynamic Optimization</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{SpeshTimePercent}}% | |
({{SpeshTime}}ms) | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<h3>Call Frames</h3> | |
<p>In total, <strong>{{EntriesWithoutInline}} call frames</strong> were | |
entered and exited by the profiled code. Inlining eliminated the need | |
to create <strong>{{EntriesInline}} call frames</strong> (that's | |
<strong>{{InlinePercent}}%</strong>). | |
</p> | |
<table class="table table-striped table-condensed table-bordered"> | |
<tbody> | |
<tr> | |
<td><strong>Interpreted Frames</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{InterpFramesPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{InterpFramesPercent}}% | |
({{InterpFrames}}) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Specialized Frames</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshFramesPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{SpeshFramesPercent}}% | |
({{SpeshFrames}}) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>JIT-Compiled Frames</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{JITFramesPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{JITFramesPercent}}% | |
({{JITFrames}}) | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<h3>Garbage Collection</h3> | |
<p>The profiled code did <strong>{{GCRuns}} garbage collections</strong>. | |
There were <strong>{{FullGCRuns}} full collections</strong> involving | |
the entire heap. | |
</p> | |
<p ng-show="{{GCRuns > 0}}"> | |
The average nursery collection time was <strong>{{NurseryAverage}}ms</strong>. | |
<span ng-show="{{FullGCRuns > 0}}"> | |
The average full collection time was <strong>{{FullAverage}}ms</strong>. | |
</span> | |
</p> | |
<h3>Dynamic Optimization</h3> | |
<p>Of {{OptimizedFrames}} specialized or JIT-compiled frames, there were | |
<strong>{{DeoptOnes}} deoptimizations</strong> (that's <strong> | |
{{DeoptOnePercent}}%</strong> of all optimized frames). | |
</p> | |
<p ng-show="DeoptAlls == 0"> | |
There was <strong>no global deoptimization</strong> triggered by the | |
profiled code. | |
</p> | |
<p ng-show="DeoptAlls == 1"> | |
There was <strong>one global deoptimization</strong> triggered by the | |
profiled code. | |
</p> | |
<p ng-show="DeoptAlls > 1"> | |
There were <strong>{{DeoptAlls}} global deoptimization</strong> triggered | |
by the profiled code. | |
</p> | |
<p ng-show="OSRs == 0"> | |
There was <strong>no On Stack Replacement</strong> performed while | |
executing the profiled code (normal if the code lacks long-running | |
loops with many iterations). | |
</p> | |
<p ng-show="OSRs == 1"> | |
There was <strong>one On Stack Replacement</strong> performed while | |
executing the profiled code. | |
</p> | |
<p ng-show="OSRs > 1"> | |
There were <strong>{{OSRs}} On Stack Replacements</strong> performed | |
while executing the profiled code. | |
</p> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'Routines'"> | |
<div ng-controller="RoutinesController"> | |
<table class="table table-striped table-condensed table-bordered"> | |
<thead> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a> | |
<input ng-model="NameFilter"> | |
</th> | |
<th><a href="" ng-click="reverse = predicate == 'Entries' ? !reverse : true; predicate = 'Entries';">Entries</a></th> | |
<th><a href="" ng-click="reverse = predicate == 'InclusiveTimePercent' ? !reverse : true; predicate = 'InclusiveTimePercent';">Inclusive Time</a></th> | |
<th><a href="" ng-click="reverse = predicate == 'ExclusiveTimePercent' ? !reverse : true; predicate = 'ExclusiveTimePercent';">Exclusive Time</a></th> | |
<th> | |
<span class="text-danger" tooltip-placement="bottom" tooltip="Unoptimized interpreted code">Interp</span> / | |
<span class="text-warning" tooltip-placement="bottom" tooltip="Type-specialized interpreted code">Spesh</span> / | |
<span class="text-success" tooltip-placement="bottom" tooltip="Type-specialized, JIT-compiled code">JIT</span> | |
</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="routine in Routines | filter:NameFilter | orderBy:predicate:reverse"> | |
<td> | |
<strong>{{routine.Name}}</strong><br /> | |
<span class="text-muted">{{routine.File}}:{{routine.Line}}</span> | |
</td> | |
<td>{{routine.Entries}}</td> | |
<td> | |
<div class="pull-left" style="width: 70px"> | |
<div class="progress" style="width: 60px"> | |
<div class="progress-bar" role="progressbar" style="width: {{routine.InclusiveTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
<strong>{{routine.InclusiveTimePercent}}%</strong> | |
({{routine.InclusiveTime}}ms) | |
</div> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 70px"> | |
<div class="progress" style="width: 60px"> | |
<div class="progress-bar" role="progressbar" style="width: {{routine.ExclusiveTimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
<strong>{{routine.ExclusiveTimePercent}}%</strong> | |
({{routine.ExclusiveTime}}ms) | |
</div> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 110px"> | |
<div class="progress" style="width: 100px"> | |
<div class="progress-bar progress-bar-danger" style="width: {{routine.InterpEntriesPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-warning" style="width: {{routine.SpeshEntriesPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-success" style="width: {{routine.JITEntriesPercent}}%"> | |
</div> | |
</div> | |
</div> | |
<div> | |
<span class="label label-default" ng-show="routine.OSR">OSR</span> | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<script type="text/ng-template" id="icycle_graph_callee_renderer.html"> | |
<a href="#" class="call" style="background-color:{{backgroundColor(callee)}}" ng-click="toCallee(callee)">{{callee.name}}</a> | |
<div class="child" style="width: {{callee.inclusive_time * 100 / Current.inclusive_time}}%;" ng-repeat="callee in callee.callees" title="{{callee.name}}" ng-include="'icycle_graph_callee_renderer.html'"></div> | |
</script> | |
<div class="container" ng-show="Tab == 'Call Graph'"> | |
<div ng-controller="CallGraphController"> | |
<ol class="breadcrumb"> | |
<li><strong>Callers:</strong></li> | |
<li ng-show="RecentCallers.length == 0"><em>None</em></li> | |
<li ng-repeat="caller in RecentCallers"> | |
<a href="" ng-click="toCaller(caller)"> | |
{{caller.name == '' ? '<anon>' : caller.name}} | |
</a> | |
</li> | |
</ol> | |
<p> | |
<span class="lead">{{Current.name == '' ? '<anon>' : Current.name}}</span><br /> | |
<span class="text-muted">{{File}}:{{Line}}</span> | |
</p> | |
<div class="icyclegraph"> | |
<div class="child" ng-repeat="callee in [Current]" ng-include="'icycle_graph_callee_renderer.html'"></div> | |
</div> | |
<table class="table table-striped table-condensed table-bordered"> | |
<tbody> | |
<tr> | |
<td><strong>Calls <span class="text-success">(Inlined)</span></strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar" role="progressbar" style="width: {{Percent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{InlinePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div ng-show="InlinePercent == 0"> | |
{{Entries}} | |
</div> | |
<div ng-show="InlinePercent != 0"> | |
{{Entries}} + | |
<span class="text-success">{{InlineEntries}} ({{InlinePercent}}%)</span> | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Interpreted Calls</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{InterpPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{InterpPercent}}% | |
({{InterpEntries}}) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>Specialized Calls</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{SpeshPercent}}% | |
({{SpeshEntries}}) | |
</div> | |
</td> | |
</tr> | |
<tr> | |
<td><strong>JIT-Compiled Calls</strong></td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{JITPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{JITPercent}}% | |
({{JITEntries}}) | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<div class="panel panel-default"> | |
<div class="panel-heading">Callees</div> | |
<table class="table table-striped table-condensed table-bordered" ng-show="Callees.length > 0"> | |
<thead> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a> | |
<input ng-model="NameFilter"> | |
</th> | |
<th><a href="" ng-click="reverse = predicate == 'Calls' ? !reverse : true; predicate = 'Calls';">Calls</a></th> | |
<th><a href="" ng-click="reverse = predicate == 'TimePercent' ? !reverse : true; predicate = 'TimePercent';">Time In Callee</a></th> | |
<th> | |
<span class="text-danger" tooltip-placement="bottom" tooltip="Unoptimized interpreted code">Interp</span> / | |
<span class="text-warning" tooltip-placement="bottom" tooltip="Type-specialized interpreted code">Spesh</span> / | |
<span class="text-success" tooltip-placement="bottom" tooltip="Type-specialized, JIT-compiled code">JIT</span> | |
</th> | |
<th> | |
<span tooltip-placement="bottom" tooltip="Code from this callee was flattened into the routine by the optimizer">Inlined</span> | |
</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="callee in Callees | filter:NameFilter | orderBy:predicate:reverse"> | |
<td> | |
<strong><a href="" ng-click="toCallee(callee.Node)">{{callee.Name}}</a></strong><br /> | |
<span class="text-muted">{{callee.File}}:{{callee.Line}}</span> | |
</td> | |
<td>{{callee.Calls}}</td> | |
<td> | |
<div class="pull-left" style="width: 70px"> | |
<div class="progress" style="width: 60px"> | |
<div class="progress-bar" role="progressbar" style="width: {{callee.TimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
<strong>{{callee.TimePercent}}%</strong> | |
({{callee.Time}}ms) | |
</div> | |
</td> | |
<td> | |
<div class="progress" style="width: 100px"> | |
<div class="progress-bar progress-bar-danger" style="width: {{callee.InterpCallsPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-warning" style="width: {{callee.SpeshCallsPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-success" style="width: {{callee.JITCallsPercent}}%"> | |
</div> | |
</div> | |
</td> | |
<td> | |
<div ng-show="callee.VeryInline"> | |
<span class="text-success"> | |
<span class="glyphicon glyphicon-star"></span> | |
</span> | |
{{callee.InlinedPercent}}% | |
</div> | |
<div ng-show="callee.SometimesInline"> | |
<span class="text-muted"> | |
<span class="glyphicon glyphicon-star-empty"></span> | |
</span> | |
{{callee.InlinedPercent}}% | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<div class="panel-body" ng-show="Callees.length == 0"> | |
This code has no callees. | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'Allocations'"> | |
<div ng-controller="AllocationsController"> | |
<table class="table table-striped table-condensed table-bordered"> | |
<thead> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a> | |
<input ng-model="NameFilter"> | |
</th> | |
<th><a href="" ng-click="reverse = predicate == 'Allocations' ? !reverse : true; predicate = 'Allocations';">Allocations</a></th> | |
<th>Allocating Routines</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="alloc in AllocationSummary | filter:NameFilter | orderBy:predicate:reverse"> | |
<td><strong>{{alloc.Name}}</strong></td> | |
<td> | |
<div class="pull-left" style="width: 210px"> | |
<div class="progress" style="width: 200px"> | |
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{alloc.AllocationsInterpPercent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{alloc.AllocationsSpeshPercent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{alloc.AllocationsJitPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{alloc.Allocations}} | |
</div> | |
</td> | |
<td> | |
<a href="" ng-click="showAllocatingRoutines(alloc)">View</a> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<script type="text/ng-template" id="myModalContent.html"> | |
<div class="modal-header"> | |
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> | |
<h4 class="modal-title" id="allocatingRoutinesModalLabel"> | |
Routines Allocating {{CurrentAllocatingRoutine}} | |
</h4> | |
</div> | |
<div class="modal-body"> | |
<table class="table table-striped table-condensed table-bordered"> | |
<thead> | |
<th> | |
<a href="" ng-click="routineReverse = routinePredicate == 'Name' ? !routineReverse : false; routinePredicate = 'Name';">Name</a> | |
<input ng-model="RoutineNameFilter"> | |
</th> | |
<th><a href="" ng-click="routineReverse = routinePredicate == 'Allocations' ? !routineReverse : true; routinePredicate = 'Allocations';">Allocations</a></th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="routine in CurrentAllocatingRoutineStats | filter:RoutineNameFilter | orderBy:routinePredicate:routineReverse"> | |
<td> | |
<strong>{{routine.Name}}</strong><br /> | |
<span class="text-muted">{{routine.File}}:{{routine.Line}}</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 210px"> | |
<div class="progress" style="width: 200px"> | |
<div class="progress-bar progress-bar-danger" style="width: {{routine.AllocationsInterpPercent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-warning" style="width: {{routine.AllocationsSpeshPercent}}%;"> | |
</div> | |
<div class="progress-bar progress-bar-success" style="width: {{routine.AllocationsJitPercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{routine.Allocations}} | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> | |
</div> | |
</script> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'GC'"> | |
<div ng-controller="GCController"> | |
<table class="table table-striped table-condensed table-bordered"> | |
<thead> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Run' ? !reverse : false; predicate = 'Run';">Run</a> | |
</th> | |
<th>Full</th> | |
<th> | |
<a href="" ng-click="reverse = predicate == 'Time' ? !reverse : false; predicate = 'Time';">Time</a> | |
</th> | |
<th> | |
Nursery: | |
<span class="text-danger" tooltip-placement="bottom" tooltip="Bytes retained in the nursery">Retained</span> / | |
<span class="text-warning" tooltip-placement="bottom" tooltip="Bytes promoted to gen2 and now available in nursery">Promoted</span> / | |
<span class="text-success" tooltip-placement="bottom" tooltip="Bytes released and now availabe in nursery">Freed</span> | |
</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="gc in GCs | orderBy:predicate:reverse"> | |
<td><strong>{{gc.Run}}</strong></td> | |
<td> | |
<span class="text-success" ng-show="gc.Full"> | |
<span class="glyphicon glyphicon-star"></span> | |
</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 210px"> | |
<div class="progress" style="width: 200px"> | |
<div class="progress-bar" role="progressbar" style="width: {{gc.TimePercent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{gc.Time}}ms | |
</div> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 210px"> | |
<div class="progress" style="width: 200px"> | |
<div class="progress-bar progress-bar-danger" style="width: {{gc.RetainedPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-warning" style="width: {{gc.PromotedPercent}}%"> | |
</div> | |
<div class="progress-bar progress-bar-success" style="width: {{gc.ClearedPercent}}%"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{gc.RetainedKilobytes}}KB / | |
{{gc.PromotedKilobytes}}KB / | |
{{gc.ClearedKilobytes}}KB ... | |
<small>{{gc.Gen2Roots}} gen2 roots</small> | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<div class="container" ng-show="Tab == 'OSR/Deopt'"> | |
<div ng-controller="OSRDeoptController"> | |
<h3>OSR</h3> | |
<p>On Stack Replacement detects routines containing hot loops that are | |
being interpreted, and replaces them with specialized or JIT-compiled | |
code.</p> | |
<table class="table table-striped table-condensed table-bordered" ng-show="OSRs.length > 0"> | |
<thead> | |
<th> | |
Routine | |
</th> | |
<th>On Stack Replacements</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="osr in OSRs | orderBy:predicate:reverse"> | |
<td> | |
<strong>{{osr.Name}}</strong><br /> | |
<span class="text-muted">{{osr.File}}:{{osr.Line}}</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar" role="progressbar" style="width: {{osr.Percent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{osr.Count}} | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<p ng-show="OSRs.length == 0"> | |
<em>No OSR was performed during this profile.</em> | |
</p> | |
<h3>Local Deoptimization</h3> | |
<p>Local deoptimization happens when a guard in specialized or JIT-compiled | |
code fails. Since the code was produced assuming the guard would hold, | |
the VM falls back to running the safe, but slower, interpreted code.</p> | |
<table class="table table-striped table-condensed table-bordered" ng-show="DeoptOnes.length > 0"> | |
<thead> | |
<th> | |
Routine | |
</th> | |
<th>Deoptimizations</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="deopt in DeoptOnes | orderBy:predicate:reverse"> | |
<td> | |
<strong>{{deopt.Name}}</strong><br /> | |
<span class="text-muted">{{deopt.File}}:{{deopt.Line}}</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar" role="progressbar" style="width: {{deopt.Percent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{deopt.Count}} | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<p ng-show="DeoptOnes.length == 0"> | |
<em>No local deoptimizations occurred during this profile.</em> | |
</p> | |
<h3>Global Deoptimization</h3> | |
<p>Global deoptimization happens when an event occurs that renders | |
all currently type-specialized or JIT-compiled code on the call | |
stack potentially invalid. Mixins - changing the type of an object | |
in place - are a common reason.</p> | |
<table class="table table-striped table-condensed table-bordered" ng-show="DeoptAlls.length > 0"> | |
<thead> | |
<th> | |
Routine | |
</th> | |
<th>Deoptimizations</th> | |
</thead> | |
<tbody> | |
<tr ng-repeat="deopt in DeoptAlls | orderBy:predicate:reverse"> | |
<td> | |
<strong>{{deopt.Name}}</strong><br /> | |
<span class="text-muted">{{deopt.File}}:{{deopt.Line}}</span> | |
</td> | |
<td> | |
<div class="pull-left" style="width: 310px"> | |
<div class="progress" style="width: 300px"> | |
<div class="progress-bar" role="progressbar" style="width: {{deopt.Percent}}%;"> | |
</div> | |
</div> | |
</div> | |
<div> | |
{{deopt.Count}} | |
</div> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<p ng-show="DeoptAlls.length == 0"> | |
<em>No global deoptimizations occurred during this profile.</em> | |
</p> | |
</div> | |
</div> | |
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.2.0/ui-bootstrap-tpls.js"></script> | |
<script> | |
var rawData = JSON.parse('[{"9":"List","8":{"name":"","line":3152,"file":"gen/moar/BOOTSTRAP.nqp"},"7":"CallCapture","6":"BOOTHash","57":{"name":"sink","line":13,"file":"SETTING::src/core/Mu.pm"},"56":{"name":"","line":-1,"file":"/Users/liz/Github/rakudo.moar/install/share/perl6/runtime/CORE.setting.moarvm"},"55":{"name":"last","line":77,"file":"SETTING::src/core/control.pm"},"54":{"name":"last","line":76,"file":"SETTING::src/core/control.pm"},"53":{"name":"","line":1,"file":"-e"},"52":"Int","51":{"name":"floor","line":135,"file":"SETTING::src/core/Num.pm"},"50":{"name":"rand","line":126,"file":"SETTING::src/core/Num.pm"},"5":{"name":"roll","line":1079,"file":"SETTING::src/core/List.pm"},"49":"Num","48":{"name":"Bridge","line":54,"file":"SETTING::src/core/Int.pm"},"47":{"name":"rand","line":9,"file":"SETTING::src/core/Real.pm"},"46":{"name":"","line":1093,"file":"SETTING::src/core/List.pm"},"45":{"name":"pull-one","line":962,"file":"SETTING::src/core/Rakudo/Internals.pm"},"44":{"name":"","line":448,"file":"SETTING::src/core/Any-iterable-methods.pm"},"43":{"name":"sink-all","line":440,"file":"SETTING::src/core/Any-iterable-methods.pm"},"42":{"name":"sink","line":158,"file":"SETTING::src/core/Seq.pm"},"41":"Any::IterateOneWithoutPhasers","40":{"name":"new","line":334,"file":"SETTING::src/core/Any-iterable-methods.pm"},"4":{"name":"<unit>","line":1,"file":"-e"},"39":{"name":"","line":1437,"file":"gen/moar/BOOTSTRAP.nqp"},"38":{"name":"returns","line":7,"file":"SETTING::src/core/Block.pm"},"37":{"name":"infix:«<»","line":338,"file":"SETTING::src/core/Int.pm"},"36":{"name":"has-phasers","line":36,"file":"SETTING::src/core/Block.pm"},"35":{"name":"count","line":13,"file":"SETTING::src/core/Code.pm"},"34":{"name":"sequential-map","line":779,"file":"SETTING::src/core/Any-iterable-methods.pm"},"33":{"name":"type_check","line":2659,"file":"gen/moar/Metamodel.nqp"},"32":{"name":"iterator","line":52,"file":"SETTING::src/core/Seq.pm"},"31":{"name":"map","line":17,"file":"SETTING::src/core/Any-iterable-methods.pm"},"30":{"name":"map","line":11,"file":"SETTING::src/core/Any-iterable-methods.pm"},"3":"BOOTArray","29":"Seq","28":{"name":"new","line":42,"file":"SETTING::src/core/Seq.pm"},"27":"<anon|140492422530512>","26":{"name":"new","line":959,"file":"SETTING::src/core/Rakudo/Internals.pm"},"25":{"name":"","line":957,"file":"SETTING::src/core/Rakudo/Internals.pm"},"24":{"name":"UnendingCallableIterator","line":956,"file":"SETTING::src/core/Rakudo/Internals.pm"},"23":"Block","22":{"name":"","line":1660,"file":"gen/moar/BOOTSTRAP.nqp"},"21":{"name":"elems","line":536,"file":"SETTING::src/core/List.pm"},"20":{"name":"is-lazy","line":1001,"file":"SETTING::src/core/List.pm"},"2":"BOOTCode","19":"Scalar","18":{"name":"roll","line":1087,"file":"SETTING::src/core/List.pm"},"17":{"name":"add_to_cache","line":2451,"file":"gen/moar/BOOTSTRAP.nqp"},"16":{"name":"is_narrower","line":1853,"file":"gen/moar/BOOTSTRAP.nqp"},"15":"BOOTIntArray","14":"NQPArrayIter","13":{"name":"","line":1823,"file":"gen/moar/BOOTSTRAP.nqp"},"12":"BOOTInt","11":"NQPArray","10":{"name":"","line":2167,"file":"gen/moar/BOOTSTRAP.nqp"},"1":{"name":"<unit-outer>","line":1,"file":"-e"},"0":{"name":"","line":1570,"file":"gen/moar/stage2/NQPHLL.nqp"}},{"total_time":464179,"spesh_time":1182,"gcs":[{"time":10260,"retained_bytes":1040272,"promoted_bytes":0,"gen2_roots":12677,"full":0,"cleared_bytes":3154024},{"time":11064,"retained_bytes":40,"promoted_bytes":6137103,"gen2_roots":17186,"full":0,"cleared_bytes":4293024425},{"time":9444,"retained_bytes":40,"promoted_bytes":0,"gen2_roots":2068,"full":0,"cleared_bytes":4194240}],"call_graph":{"inclusive_time":431864,"id":"0","exclusive_time":6,"entries":1,"callees":[{"inclusive_time":431857,"id":"1","exclusive_time":19,"entries":1,"callees":[{"inclusive_time":431838,"id":"4","exclusive_time":32,"entries":1,"callees":[{"inclusive_time":501,"id":"5","exclusive_time":150,"entries":1,"callees":[{"inclusive_time":0,"id":"8","exclusive_time":0,"entries":1,"allocations":[{"id":"9","count":1}]},{"inclusive_time":182,"id":"10","exclusive_time":83,"entries":1,"callees":[{"inclusive_time":97,"id":"13","exclusive_time":91,"entries":1,"callees":[{"inclusive_time":6,"id":"16","exclusive_time":6,"entries":6}],"allocations":[{"id":"11","count":17},{"id":"14","count":4},{"id":"6","count":6},{"id":"15","count":6},{"id":"12","count":3}]},{"inclusive_time":0,"id":"17","exclusive_time":0,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":168,"id":"18","exclusive_time":80,"entries":1,"callees":[{"inclusive_time":1,"id":"20","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1},{"id":"19","count":1}]},{"inclusive_time":0,"id":"21","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"22","exclusive_time":1,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"inclusive_time":83,"id":"24","exclusive_time":80,"entries":1,"callees":[{"inclusive_time":1,"id":"25","exclusive_time":1,"entries":1,"allocations":[{"id":"19","count":1}]},{"inclusive_time":1,"id":"26","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1},{"id":"27","count":1}]}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"28","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1},{"id":"29","count":1}]}],"allocations":[{"id":"6","count":1},{"id":"19","count":1}]}],"allocations":[{"id":"6","count":1},{"id":"3","count":1},{"id":"7","count":3}]},{"inclusive_time":0,"id":"22","exclusive_time":0,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"inclusive_time":122,"id":"30","exclusive_time":16,"entries":1,"callees":[{"inclusive_time":0,"id":"8","exclusive_time":0,"entries":1,"allocations":[{"id":"9","count":1}]},{"inclusive_time":11,"id":"10","exclusive_time":10,"entries":1,"callees":[{"inclusive_time":0,"id":"17","exclusive_time":0,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":94,"id":"31","exclusive_time":39,"entries":1,"callees":[{"inclusive_time":11,"id":"32","exclusive_time":10,"entries":1,"callees":[{"inclusive_time":0,"id":"33","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":43,"id":"34","exclusive_time":34,"entries":1,"callees":[{"inclusive_time":0,"id":"35","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"36","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"37","exclusive_time":0,"entries":1},{"inclusive_time":4,"id":"38","exclusive_time":4,"entries":1,"callees":[{"inclusive_time":0,"id":"39","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"40","exclusive_time":1,"entries":1,"allocations":[{"id":"19","count":2},{"id":"6","count":1},{"id":"41","count":1}]},{"inclusive_time":0,"id":"28","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1},{"id":"29","count":1}]}],"allocations":[{"id":"19","count":2}]}],"allocations":[{"id":"19","count":2},{"id":"6","count":1}]}],"allocations":[{"id":"3","count":1},{"id":"7","count":3}]},{"inclusive_time":431181,"id":"42","exclusive_time":17,"entries":1,"callees":[{"inclusive_time":1,"id":"32","exclusive_time":1,"entries":1,"callees":[{"inclusive_time":0,"id":"33","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":431161,"id":"43","exclusive_time":15,"entries":1,"callees":[{"inclusive_time":0,"id":"22","exclusive_time":0,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"osr":1,"jit_entries":1,"inclusive_time":431145,"id":"44","exclusive_time":41062,"entries":1,"callees":[{"jit_entries":99844,"inclusive_time":384006,"id":"45","exclusive_time":21074,"entries":100001,"callees":[{"jit_entries":99844,"inclusive_time":362931,"id":"46","exclusive_time":44566,"entries":100001,"callees":[{"spesh_entries":99844,"inclusive_time":60854,"id":"47","exclusive_time":38201,"entries":100001,"callees":[{"spesh_entries":99844,"inclusive_time":10547,"id":"48","exclusive_time":10547,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"49","count":100001}]},{"spesh_entries":99844,"inclusive_time":12104,"id":"50","exclusive_time":12104,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"49","count":100001}]}],"allocations":[{"id":"6","count":157}]},{"spesh_entries":99844,"inclusive_time":257511,"id":"51","exclusive_time":257511,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"52","count":100001}]}],"allocations":[{"id":"19","count":1}]}],"allocations":[{"id":"6","count":157}]},{"jit_entries":99844,"inclusive_time":6077,"id":"53","exclusive_time":6036,"entries":100001,"callees":[{"inclusive_time":40,"id":"54","exclusive_time":13,"entries":1,"callees":[{"inclusive_time":12,"id":"10","exclusive_time":11,"entries":1,"callees":[{"inclusive_time":1,"id":"17","exclusive_time":1,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":14,"id":"55","exclusive_time":13,"entries":1,"callees":[{"inclusive_time":1,"id":"56","exclusive_time":1,"entries":1,"allocations":[{"id":"52","count":1}]}]}],"allocations":[{"id":"7","count":3}]}]}],"allocations":[{"id":"19","count":1}]}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"57","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]}],"allocations":[{"id":"6","count":1}]}]}],"allocations":[{"id":"2","count":1},{"id":"3","count":1}]}]}}]'); | |
// grab the "dictionary" from the very first entry | |
var id_to_things = rawData[0]; | |
// the rest of the program expects the list to contain callframes, not the dictionary | |
// so we just shift it off the beginning. | |
rawData.shift(); | |
// Extract some common things out of the raw data. | |
var nodeIdToName = {}; | |
var nodeIdToFile = {}; | |
var nodeIdToLine = {}; | |
(function () { | |
function walkCallGraphNode(node) { | |
if (!nodeIdToName[node.id]) { | |
var existing_data = id_to_things[node.id.toString()]; | |
node.name = existing_data.name; | |
node.line = existing_data.line; | |
node.file = existing_data.file; | |
nodeIdToName[node.id] = node.name == "" ? "<anon>" : node.name; | |
nodeIdToLine[node.id] = node.line < 1 ? "<unknown>" : node.line; | |
nodeIdToFile[node.id] = node.file == "" ? "<unknown>" : node.file; | |
} | |
if (node.callees) | |
node.callees.map(walkCallGraphNode); | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
}()); | |
// Register application and add controllers, increase recursion depth for callGraphController | |
var moarProfApp = angular.module('moarProfApp', ['ui.bootstrap']).config(function($rootScopeProvider) { | |
$rootScopeProvider.digestTtl(100); | |
}); | |
moarProfApp.controller('NavigationController', function ($scope) { | |
$scope.Tab = 'Overview'; | |
}); | |
moarProfApp.controller('OverviewController', function ($scope) { | |
var totalTime = rawData[0].total_time; | |
var speshTime = rawData[0].spesh_time; | |
var gcTime = 0; | |
var gcNursery = 0; | |
var gcFull = 0; | |
var gcNurseryTime = 0; | |
var gcFullTime = 0; | |
var totalEntries = 0; | |
var inlineEntries = 0; | |
var jitEntries = 0; | |
var speshEntries = 0; | |
var deoptOnes = 0; | |
var deoptAlls = 0; | |
var osrs = 0; | |
rawData[0].gcs.map(function (gc) { | |
gcTime += gc.time; | |
if (gc.full) { | |
gcFull++; | |
gcFullTime += gc.time; | |
} | |
else { | |
gcNursery++; | |
gcNurseryTime += gc.time; | |
} | |
}); | |
function walkCallGraphNode(node) { | |
totalEntries += node.entries || 0; | |
inlineEntries += node.inlined_entries || 0; | |
speshEntries += node.spesh_entries || 0; | |
jitEntries += node.jit_entries || 0; | |
deoptOnes += node.deopt_one || 0; | |
deoptAlls += node.deopt_all || 0; | |
osrs += node.osr || 0; | |
if (node.callees) | |
node.callees.map(walkCallGraphNode); | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
// Time spent | |
var overheadTime = speshTime + gcTime; | |
var executingTime = totalTime - overheadTime; | |
$scope.TotalTime = +(totalTime / 1000).toFixed(2); | |
$scope.OverheadTime = +(overheadTime / 1000).toFixed(2); | |
$scope.OverheadTimePercent = +(100 * overheadTime / totalTime).toFixed(2); | |
$scope.ExecutingTime = +(executingTime / 1000).toFixed(2); | |
$scope.ExecutingTimePercent = +(100 * executingTime / totalTime).toFixed(2); | |
$scope.GCTime = +(gcTime / 1000).toFixed(2); | |
$scope.GCTimePercent = +(100 * gcTime / totalTime).toFixed(2); | |
$scope.SpeshTime = +(speshTime / 1000).toFixed(2); | |
$scope.SpeshTimePercent = +(100 * speshTime / totalTime).toFixed(2); | |
// Routines | |
var interpEntries = totalEntries - (jitEntries + speshEntries); | |
$scope.EntriesWithoutInline = totalEntries - inlineEntries; | |
$scope.EntriesInline = inlineEntries; | |
$scope.InlinePercent = +(100 * inlineEntries / totalEntries).toFixed(2); | |
$scope.InterpFrames = interpEntries; | |
$scope.InterpFramesPercent = +(100 * interpEntries / totalEntries).toFixed(2); | |
$scope.SpeshFrames = speshEntries; | |
$scope.SpeshFramesPercent = +(100 * speshEntries / totalEntries).toFixed(2); | |
$scope.JITFrames = jitEntries; | |
$scope.JITFramesPercent = +(100 * jitEntries / totalEntries).toFixed(2); | |
// Garbage collection | |
$scope.GCRuns = gcNursery + gcFull; | |
$scope.FullGCRuns = gcFull; | |
$scope.NurseryAverage = +((gcNurseryTime / 1000) / gcNursery).toFixed(2); | |
$scope.FullAverage = +((gcFullTime / 1000) / gcFull).toFixed(2); | |
// Dynamic optimization | |
var optimizedFrames = speshEntries + jitEntries; | |
$scope.OptimizedFrames = optimizedFrames; | |
$scope.DeoptOnes = deoptOnes; | |
$scope.DeoptOnePercent = +(100 * deoptOnes / (optimizedFrames || 1)).toFixed(2); | |
$scope.DeoptAlls = deoptAlls; | |
$scope.OSRs = osrs; | |
}); | |
moarProfApp.controller('RoutinesController', function ($scope) { | |
// Walk call graph to build data. | |
var idToEntries = {}; | |
var idToSpeshEntries = {}; | |
var idToJITEntries = {}; | |
var idToExclusive = {}; | |
var idToInclusive = {}; | |
var idToOSR = {}; | |
var idRecDepth = {}; | |
var totalExclusive = 0; | |
var totalInclusive = rawData[0].call_graph.inclusive_time; | |
function walkCallGraphNode(node) { | |
if (!idToEntries[node.id]) { | |
idToEntries[node.id] = 0; | |
idToSpeshEntries[node.id] = 0; | |
idToJITEntries[node.id] = 0; | |
idToExclusive[node.id] = 0; | |
idToInclusive[node.id] = 0; | |
idToOSR[node.id] = false; | |
idRecDepth[node.id] = 0; | |
} | |
idToEntries[node.id] += node.entries || 0; | |
idToSpeshEntries[node.id] += node.spesh_entries || 0; | |
idToJITEntries[node.id] += node.jit_entries || 0; | |
idToExclusive[node.id] += node.exclusive_time || 0; | |
totalExclusive += node.exclusive_time || 0; | |
if (node.osr > 0) | |
idToOSR[node.id] = true; | |
if (idRecDepth[node.id] == 0) | |
idToInclusive[node.id] += node.inclusive_time; | |
if (node.callees) { | |
idRecDepth[node.id]++; | |
node.callees.map(walkCallGraphNode); | |
idRecDepth[node.id]--; | |
} | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
// Build object list per routine. | |
var routineList = []; | |
for (id in idToEntries) { | |
var speshEntriesPercent = +(100 * idToSpeshEntries[id] / idToEntries[id]).toFixed(2); | |
var jitEntriesPercent = +(100 * idToJITEntries[id] / idToEntries[id]).toFixed(2); | |
var interpEntriesPercent = 100 - (speshEntriesPercent + jitEntriesPercent); | |
var entry = { | |
Name: nodeIdToName[id], | |
Line: nodeIdToLine[id], | |
File: nodeIdToFile[id], | |
Entries: idToEntries[id], | |
InterpEntriesPercent: interpEntriesPercent, | |
SpeshEntriesPercent: speshEntriesPercent, | |
JITEntriesPercent: jitEntriesPercent, | |
InclusiveTime: +(idToInclusive[id] / 1000).toFixed(2), | |
InclusiveTimePercent: +(100 * idToInclusive[id] / totalInclusive).toFixed(2), | |
ExclusiveTime: +(idToExclusive[id] / 1000).toFixed(2), | |
ExclusiveTimePercent: +(100 * idToExclusive[id] / totalExclusive).toFixed(2), | |
OSR: idToOSR[id] | |
}; | |
routineList.push(entry); | |
} | |
$scope.Routines = routineList; | |
$scope.predicate = "InclusiveTimePercent"; | |
$scope.reverse = true; | |
}); | |
moarProfApp.controller('CallGraphController', function ($scope) { | |
$scope.Current = rawData[0].call_graph; | |
$scope.total_time = rawData[0].total_time; | |
$scope.SuchCallers = false; | |
$scope.RecentCallers = []; | |
$scope.predicate = "TimePercent"; | |
$scope.reverse = true; | |
var all_callers = []; | |
updateCurrentData(); | |
$scope.toCallee = function (callee) { | |
// Update caller history. | |
all_callers.push($scope.Current); | |
$scope.RecentCallers.push($scope.Current); | |
if ($scope.RecentCallers.length > 5) | |
$scope.RecentCallers.shift(); | |
// Update current node and callees. | |
$scope.Current = callee; | |
updateCurrentData(); | |
}; | |
$scope.toCaller = function (caller) { | |
// Update caller history. | |
while (all_callers.length > 0) { | |
var removed = all_callers.pop(); | |
if ($scope.RecentCallers.length > 0) | |
$scope.RecentCallers.pop(); | |
if (removed == caller) | |
break; | |
} | |
if (all_callers.length > $scope.RecentCallers.length) { | |
var ptr = all_callers.length - $scope.RecentCallers.length; | |
while (ptr >= 0 && $scope.RecentCallers.length < 5) { | |
$scope.RecentCallers.unshift(all_callers[ptr]); | |
ptr--; | |
} | |
} | |
// Update current node and callees. | |
$scope.Current = caller; | |
updateCurrentData(); | |
} | |
/* | |
* Given a callee, create a unique, repeatable color; | |
* h/t https://stackoverflow.com/questions/3426404 | |
*/ | |
$scope.backgroundColor = function (callee) { | |
var str = callee.$$hashKey + callee.file + callee.name; | |
for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash)); | |
for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((hash >> i++ * 8) & 0xFF).toString(16)).slice(-2)); | |
return colour; | |
} | |
function updateCurrentData() { | |
// Line and file. | |
var current = $scope.Current; | |
$scope.Line = current.line; | |
$scope.File = current.file; | |
// Entry statistics. | |
var interpEntries = current.entries - (current.spesh_entries + current.jit_entries); | |
var nonInlineEntries = current.entries - current.inlined_entries; | |
$scope.Entries = nonInlineEntries; | |
$scope.Percent = (100 * nonInlineEntries / current.entries).toFixed(2); | |
$scope.InlineEntries = current.inlined_entries; | |
$scope.InlinePercent = (100 * current.inlined_entries / current.entries).toFixed(2); | |
$scope.InterpEntries = interpEntries; | |
$scope.InterpPercent = (100 * interpEntries / current.entries).toFixed(2); | |
$scope.SpeshEntries = current.spesh_entries; | |
$scope.SpeshPercent = (100 * current.spesh_entries / current.entries).toFixed(2); | |
$scope.JITEntries = current.jit_entries; | |
$scope.JITPercent = (100 * current.jit_entries / current.entries).toFixed(2); | |
// Callees. | |
$scope.Callees = calleesOf(current); | |
} | |
function calleesOf(node) { | |
if (!node.callees) | |
return []; | |
var totalExclusive = 0.0; | |
node.callees.map(function (c) { totalExclusive += c.exclusive_time; }); | |
return node.callees.map(function (c) { | |
var speshCallsPercent = +(100 * c.spesh_entries / c.entries).toFixed(2); | |
var jitCallsPercent = +(100 * c.jit_entries / c.entries).toFixed(2); | |
var interpCallsPercent = 100 - (speshCallsPercent + jitCallsPercent); | |
var inlinedPercent = +(100 * c.inlined_entries / c.entries).toFixed(2); | |
return { | |
Name: nodeIdToName[c.id], | |
Line: nodeIdToLine[c.id], | |
File: nodeIdToFile[c.id], | |
Calls: c.entries, | |
Time: +(c.inclusive_time / 1000).toFixed(2), | |
TimePercent: +(100 * c.inclusive_time / node.inclusive_time).toFixed(2), | |
InterpCallsPercent: interpCallsPercent, | |
SpeshCallsPercent: speshCallsPercent, | |
JITCallsPercent: jitCallsPercent, | |
InlinedPercent: inlinedPercent, | |
VeryInline: inlinedPercent >= 95, | |
SometimesInline: inlinedPercent < 95 && inlinedPercent > 10, | |
Node: c | |
}; | |
}); | |
} | |
}); | |
moarProfApp.controller('AllocationsController', function ($scope, $modal) { | |
// Traverse all call nodes, counting up the allocations. | |
var typeIdToName = {}; | |
var typeIdToAllocations = {}; | |
var typeIdToAllocationsByType = {}; | |
var typeIdToRoutineStats = {}; | |
var maxAllocations = 1; | |
function walkCallGraphNode(node) { | |
if (node.allocations) { | |
node.allocations.map(function (alloc) { | |
alloc.type = id_to_things[alloc.id]; | |
if (!typeIdToName[alloc.id]) { | |
typeIdToName[alloc.id] = alloc.type == "" ? "<anon>" : alloc.type; | |
typeIdToAllocations[alloc.id] = 0; | |
typeIdToAllocationsByType[alloc.id] = [0, 0, 0]; | |
typeIdToRoutineStats[alloc.id] = {}; | |
} | |
typeIdToAllocations[alloc.id] += alloc.count; | |
typeIdToAllocationsByType[alloc.id][0] += alloc.count - (alloc.spesh || 0) - (alloc.jit || 0); | |
typeIdToAllocationsByType[alloc.id][1] += alloc.spesh || 0; | |
typeIdToAllocationsByType[alloc.id][2] += alloc.jit || 0; | |
if (typeIdToAllocations[alloc.id] > maxAllocations) | |
maxAllocations = typeIdToAllocations[alloc.id]; | |
if (typeIdToRoutineStats[alloc.id][node.id]) { | |
typeIdToRoutineStats[alloc.id][node.id]['count'] += alloc.count || 0; | |
typeIdToRoutineStats[alloc.id][node.id]['spesh'] += alloc.spesh || 0; | |
typeIdToRoutineStats[alloc.id][node.id]['jit'] += alloc.jit || 0; | |
} else { | |
typeIdToRoutineStats[alloc.id][node.id] = { | |
count: alloc.count || 0, | |
spesh: alloc.spesh || 0, | |
jit: alloc.jit || 0 | |
}; | |
} | |
}); | |
} | |
if (node.callees) { | |
node.callees.map(walkCallGraphNode); | |
} | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
// Build allocation summary. | |
var allocationSummary = []; | |
for (id in typeIdToName) { | |
var maxAllocationByRoutine = 1; | |
for (var rid in typeIdToRoutineStats[id]) | |
if (typeIdToRoutineStats[id][rid]['count'] > maxAllocationByRoutine) | |
maxAllocationByRoutine = typeIdToRoutineStats[id][rid]['count']; | |
var routineStats = []; | |
for (var rid in typeIdToRoutineStats[id]) | |
routineStats.push({ | |
Name: nodeIdToName[rid], | |
Line: nodeIdToLine[rid], | |
File: nodeIdToFile[rid], | |
Allocations: typeIdToRoutineStats[id][rid]['count'], | |
AllocationsSpesh: typeIdToRoutineStats[id][rid]['spesh'], | |
AllocationsJit: typeIdToRoutineStats[id][rid]['jit'], | |
AllocationsPercent: (100 * typeIdToRoutineStats[id][rid]['count'] / maxAllocationByRoutine), | |
AllocationsInterpPercent: (100 * (typeIdToRoutineStats[id][rid]['count'] - typeIdToRoutineStats[id][rid]['jit'] - typeIdToRoutineStats[id][rid]['spesh']) / maxAllocationByRoutine), | |
AllocationsSpeshPercent: (100 * typeIdToRoutineStats[id][rid]['spesh'] / maxAllocationByRoutine), | |
AllocationsJitPercent: (100 * typeIdToRoutineStats[id][rid]['jit'] / maxAllocationByRoutine) | |
}); | |
var entry = { | |
Name: typeIdToName[id], | |
Allocations: typeIdToAllocations[id], | |
AllocationsPercent: +(100 * typeIdToAllocations[id] / maxAllocations).toFixed(2), | |
AllocationsInterpPercent: +(100 * typeIdToAllocationsByType[id][0] / maxAllocations).toFixed(2), | |
AllocationsSpeshPercent: +(100 * typeIdToAllocationsByType[id][1] / maxAllocations).toFixed(2), | |
AllocationsJitPercent: +(100 * typeIdToAllocationsByType[id][2] / maxAllocations).toFixed(2), | |
RoutineStats: routineStats | |
}; | |
allocationSummary.push(entry); | |
} | |
$scope.AllocationSummary = allocationSummary; | |
$scope.predicate = "Allocations"; | |
$scope.reverse = true; | |
$scope.routinePredicate = "Allocations"; | |
$scope.routineReverse = true; | |
// Allocating routines handlng. | |
$scope.showAllocatingRoutines = function (alloc) { | |
// Show modal dialog with data. | |
$scope.CurrentAllocatingRoutine = alloc.Name; | |
$scope.CurrentAllocatingRoutineStats = alloc.RoutineStats; | |
var modalInstance = $modal.open({ | |
templateUrl: 'myModalContent.html', | |
scope: $scope | |
}); | |
} | |
}); | |
moarProfApp.controller('GCController', function ($scope) { | |
// Find longest GC run. | |
var longestGC = 0; | |
rawData[0].gcs.map(function (gc) { | |
if (gc.time > longestGC) | |
longestGC = gc.time; | |
}); | |
// Produce something nice to render. | |
var run = 0; | |
$scope.GCs = rawData[0].gcs.map(function (gc) { | |
var totalBytes = gc.cleared_bytes + gc.retained_bytes + gc.promoted_bytes; | |
return { | |
Run: ++run, | |
Time: +(gc.time / 1000).toFixed(2), | |
Full: (gc.full != 0), | |
TimePercent: +(100 * gc.time / longestGC).toFixed(2), | |
RetainedKilobytes: Math.round(gc.retained_bytes / 1024), | |
PromotedKilobytes: Math.round(gc.promoted_bytes / 1024), | |
ClearedKilobytes: Math.round(gc.cleared_bytes / 1024), | |
RetainedPercent: +(100 * gc.retained_bytes / totalBytes).toFixed(2), | |
PromotedPercent: +(100 * gc.promoted_bytes / totalBytes).toFixed(2), | |
ClearedPercent: +(100 * gc.cleared_bytes / totalBytes).toFixed(2), | |
Gen2Roots: 'gen2_roots' in gc ? gc.gen2_roots : 0 | |
}; | |
}); | |
$scope.predicate = 'Run'; | |
$scope.reverse = false; | |
}); | |
moarProfApp.controller('OSRDeoptController', function ($scope) { | |
// Walk call graph to build data. | |
var idToOSR = {}; | |
var idToDeoptOne = {}; | |
var idToDeoptAll = {}; | |
var maxOSR = 1; | |
var maxDeoptOne = 1; | |
var maxDeoptAll = 1; | |
function walkCallGraphNode(node) { | |
if (!idToOSR[node.id]) { | |
idToOSR[node.id] = 0; | |
idToDeoptOne[node.id] = 0; | |
idToDeoptAll[node.id] = 0; | |
} | |
idToOSR[node.id] += node.osr || 0; | |
idToDeoptOne[node.id] += node.deopt_one || 0; | |
idToDeoptAll[node.id] += node.deopt_all || 0; | |
if (idToOSR[node.id] > maxOSR) | |
maxOSR = idToOSR[node.id]; | |
if (idToDeoptOne[node.id] > maxDeoptOne) | |
maxDeoptOne = idToDeoptOne[node.id]; | |
if (idToDeoptAll[node.id] > maxDeoptAll) | |
maxDeoptAll = idToDeoptAll[node.id]; | |
if (node.callees) | |
node.callees.map(walkCallGraphNode); | |
} | |
walkCallGraphNode(rawData[0].call_graph); | |
// Build up OSR, deopt one, and deopt all tables. | |
var osrs = []; | |
var deoptOnes = []; | |
var deoptAlls = []; | |
for (id in idToOSR) { | |
if (idToOSR[id] > 0) { | |
osrs.push({ | |
Name: nodeIdToName[id], | |
Line: nodeIdToLine[id], | |
File: nodeIdToFile[id], | |
Count: idToOSR[id], | |
Percent: Math.round(100 * idToOSR[id] / maxOSR) | |
}); | |
} | |
if (idToDeoptOne[id] > 0) { | |
deoptOnes.push({ | |
Name: nodeIdToName[id], | |
Line: nodeIdToLine[id], | |
File: nodeIdToFile[id], | |
Count: idToDeoptOne[id], | |
Percent: Math.round(100 * idToDeoptOne[id] / maxDeoptOne) | |
}); | |
} | |
if (idToDeoptAll[id] > 0) { | |
deoptAlls.push({ | |
Name: nodeIdToName[id], | |
Line: nodeIdToLine[id], | |
File: nodeIdToFile[id], | |
Count: idToDeoptAll[id], | |
Percent: Math.round(100 * idToDeoptAll[id] / maxDeoptAll) | |
}); | |
} | |
} | |
$scope.OSRs = osrs; | |
$scope.DeoptOnes = deoptOnes; | |
$scope.DeoptAlls = deoptAlls; | |
$scope.predicate = 'Count'; | |
$scope.reverse = true; | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment