merged branch vicb/profiler (PR #3190)
Commits -------b879397
[Profiler] Optimize time panel ISd4300b9
[WebProfilerBundle] Tweak the time view416a2a4
[Stopwatch] Fix some logic8c3505e
[Profiler] Tweak PHPDoc3bcd154
[HttpKernel] Tweak the Profile class - DRY Discussion ---------- [Profiler] Stopwatch related tweaks * Some fixes in the stopwatch logic, * Some JS fixes, * Make use of modern JS.
This commit is contained in:
commit
a72bf897d3
@ -48,7 +48,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Threshold</th>
|
<th>Threshold</th>
|
||||||
<td><input type="number" size="3" name="threshold" value="{{ threshold }}" /> ms</td>
|
<td><input type="number" size="3" name="threshold" value="{{ threshold }}" min="0" /> ms</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Width</th>
|
<th>Width</th>
|
||||||
@ -87,174 +87,153 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">//<![CDATA[
|
||||||
function drawCanvas(request, max, threshold, width) {
|
function drawCanvas(request, max, threshold, width) {
|
||||||
var colors = {
|
"use strict";
|
||||||
{% for name, color in colors %}
|
var text,
|
||||||
"{{ name }}": "{{ color }}"{{ loop.last ? '' : ', ' }}
|
ms,
|
||||||
{% endfor %}
|
xc,
|
||||||
};
|
mainEvents,
|
||||||
var space = 10.5;
|
colors = {{ colors|json_encode|raw }},
|
||||||
var ratio = (width - space * 2) / max;
|
space = 10.5,
|
||||||
var height = 0;
|
ratio = (width - space * 2) / max,
|
||||||
for (i = 0; i < request.events.length; i++) {
|
height = 0,
|
||||||
if (request.events[i].totaltime < threshold) {
|
h = space,
|
||||||
continue;
|
x = request.left * ratio + space,
|
||||||
}
|
canvas = document.getElementById('timeline_' + request.id),
|
||||||
|
ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
height = height + 38;
|
request.events = request.events.filter(function(event) {
|
||||||
}
|
return event.totaltime >= threshold;
|
||||||
var h = space;
|
});
|
||||||
var x = request.left * ratio + space;
|
|
||||||
|
|
||||||
var canvas = document.getElementById('timeline_' + request.id);
|
height += 38 * request.events.length;
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
ctx.lineWidth = 0;
|
||||||
|
|
||||||
var context = canvas.getContext("2d");
|
request.events.forEach(function(event) {
|
||||||
context.textBaseline = "middle";
|
event.periods.forEach(function(period) {
|
||||||
context.lineWidth = 0;
|
if ('section.child' === event.name) {
|
||||||
|
ctx.fillStyle = colors.child_sections;
|
||||||
for (i = 0; i < request.events.length; i++) {
|
ctx.fillRect(x + period.begin * ratio, 0, (period.end - period.begin) * ratio, height);
|
||||||
var event = request.events[i];
|
} else if ('section' === event.category) {
|
||||||
|
ctx.beginPath();
|
||||||
if (event.totaltime < threshold) {
|
ctx.strokeStyle = "#dfdfdf";
|
||||||
continue;
|
ctx.moveTo(x + period.begin * ratio, 0);
|
||||||
}
|
ctx.lineTo(x + period.begin * ratio, height);
|
||||||
|
ctx.moveTo(x + period.end * ratio, 0);
|
||||||
for (j = 0; j < event.periods.length; j++) {
|
ctx.lineTo(x + period.end * ratio, height);
|
||||||
var period = event.periods[j];
|
ctx.fill();
|
||||||
|
ctx.closePath();
|
||||||
if ('section.child' == event.name) {
|
ctx.stroke();
|
||||||
context.fillStyle = colors.child_sections;
|
|
||||||
context.fillRect(x + period.begin * ratio, 0, (period.end - period.begin) * ratio, height);
|
|
||||||
} else if ('section' == event.category) {
|
|
||||||
context.beginPath();
|
|
||||||
context.strokeStyle = "#dfdfdf";
|
|
||||||
context.moveTo(x + period.begin * ratio, 0);
|
|
||||||
context.lineTo(x + period.begin * ratio, height);
|
|
||||||
context.moveTo(x + period.end * ratio, 0);
|
|
||||||
context.lineTo(x + period.end * ratio, height);
|
|
||||||
context.fill();
|
|
||||||
context.closePath();
|
|
||||||
context.stroke();
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
for (i = 0; i < request.events.length; i++) {
|
mainEvents = request.events.filter(function(event) {
|
||||||
var event = request.events[i];
|
return 'section.child' !== event.name;
|
||||||
|
});
|
||||||
|
|
||||||
if (event.totaltime < threshold || 'section.child' == event.name) {
|
mainEvents.forEach(function(event) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
h += 8;
|
h += 8;
|
||||||
|
|
||||||
for (j = 0; j < event.periods.length; j++) {
|
event.periods.forEach(function(period) {
|
||||||
var period = event.periods[j];
|
|
||||||
|
|
||||||
if (colors[event.name]) {
|
if (colors[event.name]) {
|
||||||
context.fillStyle = colors[event.name];
|
ctx.fillStyle = colors[event.name];
|
||||||
context.strokeStyle = colors[event.name];
|
ctx.strokeStyle = colors[event.name];
|
||||||
} else if (colors[event.category]) {
|
} else if (colors[event.category]) {
|
||||||
context.fillStyle = colors[event.category];
|
ctx.fillStyle = colors[event.category];
|
||||||
context.strokeStyle = colors[event.category];
|
ctx.strokeStyle = colors[event.category];
|
||||||
} else {
|
} else {
|
||||||
context.fillStyle = colors.default;
|
ctx.fillStyle = colors['default'];
|
||||||
context.strokeStyle = colors.default;
|
ctx.strokeStyle = colors['default'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('section' != event.category) {
|
if ('section' !== event.category) {
|
||||||
context.fillRect(x + period.begin * ratio, h + 3, 2, 6);
|
ctx.fillRect(x + period.begin * ratio, h + 3, 2, 6);
|
||||||
context.fillRect(x + period.begin * ratio, h, (period.end - period.begin) * ratio ? (period.end - period.begin) * ratio : 2, 6);
|
ctx.fillRect(x + period.begin * ratio, h, (period.end - period.begin) * ratio || 2, 6);
|
||||||
} else {
|
} else {
|
||||||
context.beginPath();
|
ctx.beginPath();
|
||||||
context.moveTo(x + period.begin * ratio, h);
|
ctx.moveTo(x + period.begin * ratio, h);
|
||||||
context.lineTo(x + period.begin * ratio, h + 11);
|
ctx.lineTo(x + period.begin * ratio, h + 11);
|
||||||
context.lineTo(x + period.begin * ratio + 8, h);
|
ctx.lineTo(x + period.begin * ratio + 8, h);
|
||||||
context.lineTo(x + period.begin * ratio, h);
|
ctx.lineTo(x + period.begin * ratio, h);
|
||||||
context.fill();
|
ctx.fill();
|
||||||
context.closePath();
|
ctx.closePath();
|
||||||
context.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
context.beginPath();
|
ctx.beginPath();
|
||||||
context.moveTo(x + period.end * ratio, h);
|
ctx.moveTo(x + period.end * ratio, h);
|
||||||
context.lineTo(x + period.end * ratio, h + 11);
|
ctx.lineTo(x + period.end * ratio, h + 11);
|
||||||
context.lineTo(x + period.end * ratio - 8, h);
|
ctx.lineTo(x + period.end * ratio - 8, h);
|
||||||
context.lineTo(x + period.end * ratio, h);
|
ctx.lineTo(x + period.end * ratio, h);
|
||||||
context.fill();
|
ctx.fill();
|
||||||
context.closePath();
|
ctx.closePath();
|
||||||
context.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
context.beginPath();
|
ctx.beginPath();
|
||||||
context.moveTo(x + period.begin * ratio, h);
|
ctx.moveTo(x + period.begin * ratio, h);
|
||||||
context.lineTo(x + period.end * ratio, h);
|
ctx.lineTo(x + period.end * ratio, h);
|
||||||
context.lineTo(x + period.end * ratio, h + 2);
|
ctx.lineTo(x + period.end * ratio, h + 2);
|
||||||
context.lineTo(x + period.begin * ratio, h + 2);
|
ctx.lineTo(x + period.begin * ratio, h + 2);
|
||||||
context.lineTo(x + period.begin * ratio, h);
|
ctx.lineTo(x + period.begin * ratio, h);
|
||||||
context.fill();
|
ctx.fill();
|
||||||
context.closePath();
|
ctx.closePath();
|
||||||
context.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
h += 30;
|
h += 30;
|
||||||
|
|
||||||
context.beginPath();
|
ctx.beginPath();
|
||||||
context.strokeStyle = "#dfdfdf";
|
ctx.strokeStyle = "#dfdfdf";
|
||||||
context.moveTo(0, h - 10);
|
ctx.moveTo(0, h - 10);
|
||||||
context.lineTo(width, h - 10);
|
ctx.lineTo(width, h - 10);
|
||||||
context.closePath();
|
ctx.closePath();
|
||||||
context.stroke();
|
ctx.stroke();
|
||||||
}
|
});
|
||||||
|
|
||||||
h = space;
|
h = space;
|
||||||
|
|
||||||
for (i = 0; i < request.events.length; i++) {
|
mainEvents.forEach(function(event) {
|
||||||
var event = request.events[i];
|
|
||||||
|
|
||||||
if (event.totaltime < threshold || 'section.child' == event.name) {
|
ctx.fillStyle = "#444";
|
||||||
continue;
|
ctx.font = "12px sans-serif";
|
||||||
}
|
text = event.name;
|
||||||
|
ms = " ~ " + (event.totaltime < 1 ? event.totaltime : parseInt(event.totaltime, 10)) + " ms";
|
||||||
context.fillStyle = "#444";
|
if (x + event.starttime * ratio + ctx.measureText(text + ms).width > width) {
|
||||||
context.font = "12px sans-serif";
|
ctx.textAlign = "end";
|
||||||
var text = event.name;
|
ctx.font = "10px sans-serif";
|
||||||
var ms;
|
|
||||||
if (event.totaltime < 1) {
|
|
||||||
ms = " ~ " + event.totaltime + " ms";
|
|
||||||
} else {
|
|
||||||
ms = " ~ " + parseInt(event.totaltime) + " ms";
|
|
||||||
}
|
|
||||||
if (x + event.starttime * ratio + context.measureText(text + ms).width > width) {
|
|
||||||
context.textAlign = "end";
|
|
||||||
context.font = "10px sans-serif";
|
|
||||||
xc = x + event.endtime * ratio - 1;
|
xc = x + event.endtime * ratio - 1;
|
||||||
context.fillText(ms, xc, h);
|
ctx.fillText(ms, xc, h);
|
||||||
|
|
||||||
xc = xc - context.measureText(ms).width
|
xc -= ctx.measureText(ms).width;
|
||||||
context.font = "12px sans-serif";
|
ctx.font = "12px sans-serif";
|
||||||
context.fillText(text, xc, h);
|
ctx.fillText(text, xc, h);
|
||||||
} else {
|
} else {
|
||||||
context.textAlign = "start";
|
ctx.textAlign = "start";
|
||||||
context.font = "12px sans-serif";
|
ctx.font = "12px sans-serif";
|
||||||
xc = x + event.starttime * ratio + 1;
|
xc = x + event.starttime * ratio + 1;
|
||||||
context.fillText(text, xc, h);
|
ctx.fillText(text, xc, h);
|
||||||
|
|
||||||
xc = xc + context.measureText(text).width;
|
xc += ctx.measureText(text).width;
|
||||||
context.font = "10px sans-serif";
|
ctx.font = "10px sans-serif";
|
||||||
context.fillText(ms, xc, h);
|
ctx.fillText(ms, xc, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
h += 38;
|
h += 38;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawCanvases(width)
|
function drawCanvases(width)
|
||||||
{
|
{
|
||||||
for (k = 0; k < requests_data.requests.length; k++) {
|
"use strict";
|
||||||
drawCanvas(requests_data.requests[k], requests_data.max, {{ threshold }}, width);
|
requests_data.requests.forEach(function(request) {
|
||||||
}
|
drawCanvas(request, requests_data.max, {{ threshold }}, width);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var requests_data = {
|
var requests_data = {
|
||||||
@ -272,7 +251,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
drawCanvases({{ width }});
|
drawCanvases({{ width }});
|
||||||
</script>
|
//]]></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% macro dump_request_data(token, profile, events, origin) %}
|
{% macro dump_request_data(token, profile, events, origin) %}
|
||||||
|
@ -76,7 +76,7 @@ class Stopwatch
|
|||||||
public function start($name, $category = null)
|
public function start($name, $category = null)
|
||||||
{
|
{
|
||||||
if (!isset($this->events[$name])) {
|
if (!isset($this->events[$name])) {
|
||||||
$this->events[$name] = new StopwatchEvent($this->origin, $category);
|
$this->events[$name] = new StopwatchEvent($this->origin ?: microtime(true) * 1000, $category);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->events[$name]->start();
|
return $this->events[$name]->start();
|
||||||
|
@ -28,10 +28,12 @@ class StopwatchEvent
|
|||||||
*
|
*
|
||||||
* @param integer $origin The origin time in milliseconds
|
* @param integer $origin The origin time in milliseconds
|
||||||
* @param string $category The event category
|
* @param string $category The event category
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException When the raw time is not valid
|
||||||
*/
|
*/
|
||||||
public function __construct($origin, $category = null)
|
public function __construct($origin, $category = null)
|
||||||
{
|
{
|
||||||
$this->origin = $origin;
|
$this->origin = $this->formatTime($origin);
|
||||||
$this->category = is_string($category) ? $category : 'default';
|
$this->category = is_string($category) ? $category : 'default';
|
||||||
$this->started = array();
|
$this->started = array();
|
||||||
$this->periods = array();
|
$this->periods = array();
|
||||||
@ -132,7 +134,7 @@ class StopwatchEvent
|
|||||||
*/
|
*/
|
||||||
public function getEndTime()
|
public function getEndTime()
|
||||||
{
|
{
|
||||||
return count($this->periods) ? $this->periods[count($this->periods) - 1][1] : 0;
|
return ($count = count($this->periods)) ? $this->periods[$count - 1][1] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,11 +149,29 @@ class StopwatchEvent
|
|||||||
$total += $period[1] - $period[0];
|
$total += $period[1] - $period[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return sprintf('%.1f', $total);
|
return $this->formatTime($total);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getNow()
|
private function getNow()
|
||||||
{
|
{
|
||||||
return sprintf('%.1f', microtime(true) * 1000 - $this->origin);
|
return $this->formatTime(microtime(true) * 1000 - $this->origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a time.
|
||||||
|
*
|
||||||
|
* @param numerical $time A raw time
|
||||||
|
*
|
||||||
|
* @return float The formatted time
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException When the raw time is not valid
|
||||||
|
*/
|
||||||
|
private function formatTime($time)
|
||||||
|
{
|
||||||
|
if (!is_numeric($time)) {
|
||||||
|
throw new \InvalidArgumentException('The time must be a numerical value');
|
||||||
|
}
|
||||||
|
|
||||||
|
return round($time, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ class MongoDbProfilerStorage implements ProfilerStorageInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write data associated with the given token.
|
* Saves a Profile.
|
||||||
*
|
*
|
||||||
* @param Profile $profile A Profile instance
|
* @param Profile $profile A Profile instance
|
||||||
*
|
*
|
||||||
|
@ -18,7 +18,7 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
|
|||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
*/
|
*/
|
||||||
class Profile implements \Serializable
|
class Profile
|
||||||
{
|
{
|
||||||
private $token;
|
private $token;
|
||||||
private $collectors;
|
private $collectors;
|
||||||
@ -212,13 +212,8 @@ class Profile implements \Serializable
|
|||||||
return isset($this->collectors[$name]);
|
return isset($this->collectors[$name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function serialize()
|
public function __sleep()
|
||||||
{
|
{
|
||||||
return serialize(array($this->token, $this->parent, $this->children, $this->collectors, $this->ip, $this->method, $this->url, $this->time));
|
return array('token', 'parent', 'children', 'collectors', 'ip', 'method', 'url', 'time');
|
||||||
}
|
|
||||||
|
|
||||||
public function unserialize($data)
|
|
||||||
{
|
|
||||||
list($this->token, $this->parent, $this->children, $this->collectors, $this->ip, $this->method, $this->url, $this->time) = unserialize($data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ interface ProfilerStorageInterface
|
|||||||
function read($token);
|
function read($token);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write data associated with the given token.
|
* Saves a Profile.
|
||||||
*
|
*
|
||||||
* @param Profile $profile A Profile instance
|
* @param Profile $profile A Profile instance
|
||||||
*
|
*
|
||||||
|
@ -31,7 +31,7 @@ class StopwatchEventTest extends \PHPUnit_Framework_TestCase
|
|||||||
$event = new StopwatchEvent(microtime(true) * 1000);
|
$event = new StopwatchEvent(microtime(true) * 1000);
|
||||||
$this->assertEquals('default', $event->getCategory());
|
$this->assertEquals('default', $event->getCategory());
|
||||||
|
|
||||||
$event = new StopwatchEvent(time(), 'cat');
|
$event = new StopwatchEvent(microtime(true) * 1000, 'cat');
|
||||||
$this->assertEquals('cat', $event->getCategory());
|
$this->assertEquals('cat', $event->getCategory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,4 +141,12 @@ class StopwatchEventTest extends \PHPUnit_Framework_TestCase
|
|||||||
$end = $event->getEndTime();
|
$end = $event->getEndTime();
|
||||||
$this->assertTrue($end >= 18 && $end <= 30);
|
$this->assertTrue($end >= 18 && $end <= 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function testInvalidOriginThrowsAnException()
|
||||||
|
{
|
||||||
|
new StopwatchEvent("abc");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user