@ 2014-10-02T12:43:43Z <?php
/*
* This file is part of the SensioLabsProfiler SDK package.
*
* (c) SensioLabs <contact@sensiolabs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* This is a PHP 5.2 compatible fallback implementation of the Sprofiler extension.
* The interfaces and behavior are the same, or as close as possible.
* It uses xhprof or uprofiler to gather profiling metrics and push them to SensioLabsProfiler.
* When the extension is loaded, this PHP fallback is not loaded.
*
* A general rule of design is that this fallback (as the extension) does not generate any exception
* nor any PHP notice/warning/etc. Instead, a log facility is provided where all messages shall be written.
*/
class SprofilerProbe
{
private $fileFormat = 'SensioLabs\Profiler\Probe';
private $profiler;
private $outputUrl;
private $outputTimeout;
private $outputStream;
private $logLevel = 1;
private $logUrl = 'php://stderr';
private $isEnabled = false;
private $responseLine = '';
private $challenge;
private $signature;
private $flags;
private $options = array(
'server_keys' => array(
'HTTP_HOST',
'HTTP_USER_AGENT',
'HTTPS',
'REQUEST_METHOD',
'REQUEST_URI',
'SERVER_ADDR',
'SERVER_SOFTWARE',
'_',
'argv',
),
'ignored_functions' => array(
'array_map',
'array_filter',
'array_reduce',
'array_walk',
'array_walk_recursive',
'call_user_func',
'call_user_func_array',
'call_user_method',
'call_user_method_array',
'forward_static_call',
'forward_static_call_array',
'iterator_apply',
),
);
private static $probe;
private static $profilerIsEnabled = false;
private static $defaultOutputUrl = 'unix:///var/run/sprofiler/agent.sock';
private static $urlEncMap = array(
'%21' => '!', '%22' => '"', '%23' => '#', '%24' => '$', '%27' => "'",
'%28' => '(', '%29' => ')', '%2A' => '*', '%2C' => ',', '%2F' => '/',
'%3A' => ':', '%3B' => ';', '%3C' => '<', '%3D' => '=', '%3E' => '>',
'%40' => '@', '%5B' => '[', '%5C' => '\\','%5D' => ']', '%5E' => '^',
'%60' => '`', '%7B' => '{', '%7C' => '|', '%7D' => '}', '%7E' => '~',
);
/**
* Returns a global singleton and enables it by default.
*
* Uses an HTTP header or an env vars to create this singleton on its first use:
* 1. the value of the X-SensioLabsProfiler-Query HTTP header (when applicable)
* 2. or the SPROFILER_QUERY environment var
* 3. or the empty string otherwise
* is used as the first argument of the constructor below to instantiate the main probe.
*
* Additionally, this function enables the probe, except when the just said string
* contains an auto_enable=0 URL parameter.
*
* @return self
*
* @api
*/
public static function getMainInstance()
{
if (isset($_SERVER['HTTP_X_SENSIOLABSPROFILER_QUERY'])) {
$query = $_SERVER['HTTP_X_SENSIOLABSPROFILER_QUERY'];
} elseif (isset($_SERVER['SPROFILER_QUERY'])) {
$query = $_SERVER['SPROFILER_QUERY'];
} else {
$query = '';
}
if (null !== self::$probe) {
return self::$probe;
}
if (!file_exists(substr(self::$defaultOutputUrl, 7))) {
self::$defaultOutputUrl = null;
}
$probe = new self($query);
parse_str($query, $query);
if (!isset($query['auto_enable']) || $query['auto_enable']) {
if ($probe->isVerified()) {
self::boxPostEnable($probe, $probe->enable());
$probe->debug($probe->getResponseLine());
}
}
return self::$probe = $probe;
}
/**
* Instantiate a probe object.
*
* @param string $query An URL-encoded string that configures the probe. Part of the string is signed.
* @param string $probeId An id that is given to the agent for signature impersonation.
* @param string $probeToken The token associated to $probeId.
* @param string $outputUrl The URL where profiles will be written (directory, socket or TCP destination).
*
* @api
*/
public function __construct($query, $probeId = null, $probeToken = null, $outputUrl = null)
{
$query = preg_split('/(?:^|&)signature=(.+?)(?:&|$)/', $query, 2, PREG_SPLIT_DELIM_CAPTURE);
list($this->challenge, $this->signature, $args) = $query + array(1 => '', '');
$this->signature = rawurldecode($this->signature);
parse_str($args, $args);
$query = array(
'SPROFILER_PROBE_ID' => null,
'SPROFILER_PROBE_TOKEN' => null,
'SPROFILER_OUTPUT_URL' => null,
'SPROFILER_OUTPUT_TIMEOUT' => null,
'SPROFILER_LOG_LEVEL' => null,
'SPROFILER_LOG_URL' => null,
);
foreach ($query as $k => $v) {
if (isset($_ENV[$k])) {
$query[$k] = $_ENV[$k];
} elseif (isset($_SERVER[$k])) {
$query[$k] = $_SERVER[$k];
}
}
$this->probeId = $probeId ?: $query['SPROFILER_PROBE_ID'];
$this->probeToken = $probeToken ?: $query['SPROFILER_PROBE_TOKEN'];
$this->outputUrl = $outputUrl ?: $query['SPROFILER_OUTPUT_URL'] ?: self::$defaultOutputUrl ?: ini_get('uprofiler.output_dir') ?: ini_get('xhprof.output_dir');
$this->outputTimeout = 1000000 * ($query['SPROFILER_OUTPUT_TIMEOUT'] ?: 0.25);
$this->logLevel = $query['SPROFILER_LOG_LEVEL'] ?: $this->logLevel;
$this->logUrl = $query['SPROFILER_LOG_URL'] ?: $this->logUrl;
$this->aggregSamples = isset($args['aggreg_samples']) && is_string($args['aggreg_samples']) ? max((int) $args['aggreg_samples'], 1) : 1;
if (!$this->logUrl || 'stderr' === $this->logUrl) {
$this->logUrl = 'php://stderr';
}
empty($args['flag_cpu']) or $this->flags |= UPROFILER_FLAGS_CPU;
empty($args['flag_memory']) or $this->flags |= UPROFILER_FLAGS_MEMORY;
empty($args['flag_no_builtins']) or $this->flags |= UPROFILER_FLAGS_NO_BUILTINS;
if (function_exists('uprofiler_enable')) {
$this->profiler = 'uprofiler';
} elseif (function_exists('xhprof_enable')) {
$this->profiler = 'xhprof';
}
if ($this->logLevel >= 4) {
$this->debug('New probe instanciated');
foreach ($this as $k => $v) {
if ('options' !== $k) {
if ('' !== $v = (string) $v) {
$this->debug(' '.$k.': '.$v);
}
}
}
}
}
/**
* Tells if the probe is cryptographically verified, i.e. if the signature in $query is valid.
*
* @return bool
*
* @api
*/
public function isVerified()
{
return $this->box('doVerify', false);
}
/**
* Gets the response message/status/line
*
* This lines gives details about the status of the probe. That can be:
* - an error: `SensioLabsProfiler-Error: $errNumber $urlEncodedErrorMessage`
* - or not: `SensioLabsProfiler-Response: $rfc1738EncodedMessage`
*
* @return string The response line
*
* @api
*/
public function getResponseLine()
{
return $this->responseLine;
}
/**
* Enables profiling instrumentation and data aggregation.
*
* One and only one probe can be enabled at the same time.
*
* @see getResponseLine() for error/status reporting
*
* @return bool False if enabling failed.
*
* @api
*/
public function enable()
{
return $this->box('doEnable', false,
$this->getErrorHandler('error', array(__CLASS__, 'onError'))
.$this->getErrorHandler('exception', array($this, 'onException'))
);
}
/**
* Disables profiling instrumentation and data aggregation.
*
* As a side-effect, flushes the collected profile to the output.
*
* @param bool $close Not closing allows to re-enable the probe later and aggregate data in the same profile. Closing means that a later enable() will create a new profile on the output.
*
* @return bool False if the probe was not enabled.
*
* @api
*/
public function disable($close = false)
{
return $this->box('doDisable', true, $close);
}
// XXX
// XXX - END OF PUBLIC API - XXX
// XXX
/**
* @internal
*/
private static function boxPostEnable($probe, $isEnabled) {
if ($isEnabled) {
register_shutdown_function(array($probe, 'onShutdown'));
}
$probe->box('sendHeaderLine', null);
}
/**
* @internal
*/
private static function restoreErrorHandler() {
restore_error_handler();
}
/**
* @internal
*/
public function __destruct()
{
$this->disable(true);
}
/**
* Wraps internal functions and handles any error/exception.
*
* @internal
*/
private function box($method, $returnValue)
{
set_error_handler(__CLASS__.'::onInternalError');
try {
$args = func_get_args();
unset($args[0], $args[1]);
$this->debug('Boxing '.$method);
$returnValue = call_user_func_array(array($this, $method), $args);
} catch (Exception $e) {
$this->warn(get_class($e).': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine());
restore_error_handler();
$this->profilerDisable();
$this->responseLine = 'SensioLabsProfiler-Error: 101 '.rawurlencode($e->getMessage().' in '.$e->getFile().':'.$e->getLine());
}
self::restoreErrorHandler();
return $returnValue;
}
/**
* @internal
*/
private function doVerify()
{
if (null === $this->outputStream) {
$signature = strtr($this->signature, '-_', '+/');
$signature = base64_decode($signature, true);
// XXX Crypto checks are done here in the C version.
// In the PHP version, this is delegated to the agent,
// no verification occurs when the output is a directory.
if ($signature) {
$this->debug('Signature looks OK');
$this->openOutput();
} else {
$this->info('Invalid signature');
}
}
return (bool) $this->outputStream;
}
/**
* @internal
*/
private function doEnable($extra)
{
if ($this->isEnabled) {
return true;
}
if (self::$profilerIsEnabled) {
$this->responseLine = "SensioLabsProfiler-Error: 101 An other probe is already profiling";
return false;
}
if ($this->doVerify()) {
$this->writeChunkProlog($extra);
$this->profilerEnable();
$this->isEnabled = true;
}
return $this->isEnabled;
}
/**
* @internal
*/
private function doDisable($close = false)
{
if (!$this->isEnabled) {
return false;
}
$this->isEnabled = false;
$this->profilerWrite(true);
if ($close && $this->outputStream) {
$this->debug('Closing output stream');
flock($this->outputStream, LOCK_UN);
fclose($this->outputStream);
$this->outputStream = null;
}
return true;
}
/**
* @internal
*/
private function sendHeaderLine()
{
header('X-'.$this->getResponseLine());
}
/**
* @internal
*/
private function openOutput()
{
if (null !== $this->outputStream) {
return $this->outputStream;
}
$this->outputStream = false;
$url = $this->outputUrl;
if (($i = strpos($url, '://')) && in_array(substr($url, 0, $i), stream_get_transports(), true)) {
$this->debug('Lets open '.$url);
if ($h = stream_socket_client($url, $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT)) {
stream_set_timeout($h, 0, $this->outputTimeout);
stream_set_write_buffer($h, 0);
$i = array(null, array($h), null);
if (stream_select($i[0], $i[1], $i[2], 0, $this->outputTimeout)) {
$this->writeHelloProlog($h);
$response = rtrim(fgets($h, 4096));
while ('' !== rtrim(fgets($h, 4096))) {
// No-op
}
if (0 !== strpos($response, 'SensioLabsProfiler-Response: ')) {
fclose($h);
$h = false;
if (0 !== strpos($response, 'SensioLabsProfiler-Error: ')) {
$response = "SensioLabsProfiler-Error: 102 Invalid agent response ($response)";
}
}
} else {
fclose($h);
$h = false;
$response = "SensioLabsProfiler-Error: 101 Agent connection timeout";
}
} else {
$response = "SensioLabsProfiler-Error: 101 $errstr ($errno)";
}
} else {
$i = sprintf('%019.6F', microtime(true)).'-';
$i .= substr(str_replace(array('+', '/'), array('', ''), base64_encode(md5(mt_rand(), true))), 0, 6);
$url .= '/'.$i.'.log';
$this->debug('Lets open '.$url);
$h = fopen($url, 'wb');
if (stream_is_local($h)) {
flock($h, LOCK_SH); // This shared lock allows readers to wait for the end of the stream
stream_set_write_buffer($h, 0);
} else {
$this->writeHelloProlog($h);
}
$response = "SensioLabsProfiler-Response: continue=false";
}
$this->responseLine = $response;
$this->outputStream = $h;
if ($h) {
$this->writeMainProlog();
}
return $h;
}
/**
* @internal
*/
private function writeHelloProlog($h)
{
$hello = '';
if ($this->probeId && $this->probeToken) {
$line = $this->probeId.':'.$this->probeToken;
if (strlen($line) !== strcspn($line, "\r\n") || 1 < substr_count($line, ':')) {
$this->warn('Invalid probe_id/probe_token');
} else {
$hello .= 'SensioLabsProfiler-Auth: '.$line."\n";
}
}
$line = 'signature='.$this->signature.'&aggreg_samples='.$this->aggregSamples."\n";
isset($this->challenge[0]) and $line = $this->challenge.'&'.$line;
$hello .= 'SensioLabsProfiler-Query: '.$line."\n";
self::fwrite($h, $hello);
}
/**
* @internal
*/
private function writeMainProlog()
{
// Loaded extensions list helps understanding runtime behavior
$extensions = array();
foreach (get_loaded_extensions() as $e) {
$extensions[$e] = phpversion($e);
}
// Keep only keys from $_COOKIE
$cookies = array_keys($_COOKIE);
// Keep selected keys from $_SERVER
$servers = array();
foreach ($this->options['server_keys'] as $e) {
if (isset($_SERVER[$e])) {
$servers[$e] = $_SERVER[$e];
}
}
// Get request's URI
if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) {
$e = $_SERVER['HTTP_X_ORIGINAL_URL'];
} elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
$e = $_SERVER['HTTP_X_REWRITE_URL'];
} elseif (!empty($_SERVER['IIS_WasUrlRewritten']) && !empty($_SERVER['UNENCODED_URL'])) {
$e = $_SERVER['UNENCODED_URL'];
} elseif (isset($_SERVER['REQUEST_URI'][0])) {
$e = $_SERVER['REQUEST_URI'];
if ('/' !== $e[0]) {
$e = preg_replace('#^https?://[^/]+#', '', $e);
}
} elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
$e = $_SERVER['ORIG_PATH_INFO'];
if (!empty($_SERVER['QUERY_STRING'])) {
$e .= '?'.$_SERVER['QUERY_STRING'];
}
} else {
$e = '';
}
if (!empty($e)) {
$servers['REQUEST_URI'] = $e;
}
self::fwrite($this->outputStream, 'file-format: '.$this->fileFormat."\n"
.'php-os: '.PHP_OS."\n"
.'php-sapi: '.PHP_SAPI."\n"
.'php-version: '.PHP_VERSION_ID."\n"
.'php-extensions: '.strtr(http_build_query($extensions, '', '&'), self::$urlEncMap)."\n"
.'_COOKIE: '.strtr(http_build_query($cookies, '', '&'), self::$urlEncMap)."\n"
.'_SERVER: '.strtr(http_build_query($servers, '', '&'), self::$urlEncMap)."\n"
."\nmain()//1 0 0 0 0\n\n"
);
$this->debug('Main prolog pushed');
}
/**
* @internal
*/
private function writeChunkProlog($extra)
{
$data = 'request-mu: '.memory_get_usage(true)."\n"
.'request-pmu: '.memory_get_peak_usage(true)."\n"
.'request-start: '.microtime(true)."\n"
.$extra;
if (function_exists('sys_getloadavg')) {
$data .= 'sys-load-avg: '.implode(' ', sys_getloadavg())."\n";
}
self::fwrite($this->outputStream, $data);
}
/**
* @internal
*/
private function getErrorHandler($type, $default = 'var_dump')
{
$s = "set_{$type}_handler";
if ($h = $s($default)) {
$s = "restore_{$type}_handler";
$s();
} elseif ('var_dump' !== $default) {
$h = $default;
}
$type .= '-handler: ';
if ($h instanceof Closure) {
$h = new ReflectionFunction($h);
if (PHP_VERSION_ID >= 50400 && $s = $h->getClosureScopeClass()) {
$h = $s->name.'::{closure}/'.$h->getStartLine().'-'.$h->getEndLine();
} else {
$h = $h->name.'::'.implode('/', array_slice(explode('/', $h->getFileName()), -2)).'/'.$h->getStartLine().'-'.$h->getEndLine();
}
} else {
if (!is_array($h)) {
if (is_object($h)) {
$h = array($h, '__invoke');
} else {
$h = explode('::', $h, 2);
}
}
if (isset($h[1])) {
$h = new ReflectionMethod($h[0], $h[1]);
$h = $h->getDeclaringClass()->name.'::'.$h->name;
} else {
$h = $h[0];
}
}
$type .= $h;
$this->debug('Extracted '.$type);
return $type."\n";
}
/**
* @internal
*/
private function profilerEnable()
{
self::$profilerIsEnabled = true;
if (is_string($this->profiler)) {
$p = $this->profiler.'_enable';
$this->debug($p);
$p($this->flags, $this->options);
} else {
$this->info('No profiler to enable');
}
}
/**
* @internal
*/
private function profilerDisable()
{
self::$profilerIsEnabled = false;
if (is_string($this->profiler)) {
$p = $this->profiler.'_disable';
$this->debug($p);
return $p();
} elseif (is_array($this->profiler)) {
$this->debug('data array profiler_disable');
return $this->profiler;
} else {
$this->info('No profiler to disable');
return array();
}
}
/**
* @internal
*/
private function profilerWrite($disable, $chunk = '')
{
$data = $this->profilerDisable();
$chunk .= "request-end: ".microtime(true)
."\nrequest-mu: ".memory_get_usage(true)
."\nrequest-pmu: ".memory_get_peak_usage(true)
."\n\n";
$this->debug('Pushing '.count($data).' call pairs');
if (!$disable) {
$this->profilerEnable();
}
$h = $this->outputStream;
$i = 50; // 50 ~= 4Ko chunks
// Speed optimized paths
if (!$data) {
// No-op
} elseif ((UPROFILER_FLAGS_CPU & $this->flags) && (UPROFILER_FLAGS_MEMORY & $this->flags)) {
foreach ($data as $k => $v) {
$chunk .= "{$k}//{$v['ct']} {$v['wt']} {$v['cpu']} {$v['mu']} {$v['pmu']}\n";
if (0 === --$i) {
self::fwrite($h, $chunk);
$chunk = '';
$i = 50;
}
}
} elseif (UPROFILER_FLAGS_MEMORY & $this->flags) {
foreach ($data as $k => $v) {
$chunk .= "{$k}//{$v['ct']} {$v['wt']} 0 {$v['mu']} {$v['pmu']}\n";
if (0 === --$i) {
self::fwrite($h, $chunk);
$chunk = '';
$i = 50;
}
}
} elseif (UPROFILER_FLAGS_CPU & $this->flags) {
foreach ($data as $k => $v) {
$chunk .= "{$k}//{$v['ct']} {$v['wt']} {$v['cpu']} 0 0\n";
if (0 === --$i) {
self::fwrite($h, $chunk);
$chunk = '';
$i = 50;
}
}
} else {
foreach ($data as $k => $v) {
$chunk .= "{$k}//{$v['ct']} {$v['wt']} 0 0 0\n";
if (0 === --$i) {
self::fwrite($h, $chunk);
$chunk = '';
$i = 50;
}
}
}
if (isset($data['main()'])) {
$chunk .= "main()//-{$data['main()']['ct']} 0 0 0 0\n";
}
$chunk .= "\n";
return self::fwrite($h, $chunk);
}
/**
* @internal
*/
private static function fwrite($stream, $data)
{
$len = strlen($data);
$written = fwrite($stream, $data);
if (false !== $written) {
while ($written < $len) {
fflush($stream);
$w = fwrite($stream, substr($data, $written));
$written += $w ?: $len + 1;
}
if ($written === $len) {
return true;
}
}
}
/**
* @internal
*/
public static function onInternalError($type, $message, $file, $line)
{
throw new ErrorException($message, 0, $type, $file, $line);
}
/**
* @internal
*/
public static function onError()
{
return false; // Delegate error handling to the internal handler, but adds a line in profiler's data
}
/**
* @internal
*/
public function onException($e)
{
// Rethrow only, but adds a line in profiler's data
$this->box('profilerWrite', null, true); // Prevents a crash with XHProf
throw $e;
}
/**
* @internal
*/
public function onShutdown()
{
$this->box('doShutdown', null,
$this->getErrorHandler('error')
.$this->getErrorHandler('exception')
);
}
/**
* @internal
*/
private function doShutdown($extra)
{
// Get and write data now so that any later fatal error
// does not prevent collecting what we already have.
if (!$this->isEnabled) {
return;
}
$e = error_get_last();
if (function_exists('http_response_code')) {
$extra .= 'response-code: '.http_response_code()."\n";
}
if (isset($e['type'])) {
switch ($e['type']) {
case E_ERROR:
case E_PARSE:
case E_USER_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_RECOVERABLE_ERROR:
$h = explode("\r", $e['message'], 2);
$h = explode("\n", $h[0], 2);
$h[1] = " in {$e['file']}:{$e['line']}";
$h[0] = str_replace($h[1], '', $h[0]);
$h = "fatal-error: {$h[0]}{$h[1]}\n";
$this->info('Got '.$h);
self::fwrite($this->outputStream, $h);
break;
}
}
$this->profilerWrite(false, $extra);
}
/**
* @internal
*/
private function warn($msg)
{
if ($this->logLevel >= 2) {
file_put_contents($this->logUrl, 'WARN: '.$msg."\n", FILE_APPEND);
}
}
/**
* @internal
*/
private function info($msg)
{
if ($this->logLevel >= 3) {
file_put_contents($this->logUrl, 'Info: '.$msg."\n", FILE_APPEND);
}
}
/**
* @internal
*/
private function debug($msg)
{
if ($this->logLevel >= 4) {
file_put_contents($this->logUrl, 'dbug: '.$msg."\n", FILE_APPEND);
}
}
}
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).
Version System time (s) User time (s) Memory (MiB) 8.3.7 0.006 0.010 18.06 8.3.6 0.003 0.013 16.62 8.3.5 0.011 0.007 22.04 8.3.4 0.012 0.003 18.92 8.3.3 0.007 0.007 19.34 8.3.2 0.004 0.004 20.39 8.3.1 0.003 0.006 20.53 8.3.0 0.009 0.000 19.16 8.2.18 0.010 0.007 16.50 8.2.17 0.013 0.003 22.96 8.2.16 0.009 0.006 20.53 8.2.15 0.003 0.006 24.18 8.2.14 0.000 0.008 24.66 8.2.13 0.008 0.000 26.16 8.2.12 0.004 0.004 22.23 8.2.11 0.003 0.007 22.13 8.2.10 0.009 0.003 18.16 8.2.9 0.000 0.009 19.34 8.2.8 0.009 0.000 17.97 8.2.7 0.003 0.006 17.88 8.2.6 0.009 0.000 18.16 8.2.5 0.006 0.003 18.07 8.2.4 0.003 0.006 18.22 8.2.3 0.006 0.003 19.77 8.2.2 0.000 0.008 17.95 8.2.1 0.003 0.006 18.10 8.2.0 0.000 0.009 17.89 8.1.28 0.007 0.014 25.92 8.1.27 0.005 0.005 22.18 8.1.26 0.005 0.003 26.35 8.1.25 0.000 0.009 28.09 8.1.24 0.000 0.009 23.83 8.1.23 0.010 0.003 19.22 8.1.22 0.000 0.008 17.79 8.1.21 0.000 0.011 18.77 8.1.20 0.009 0.003 17.25 8.1.19 0.006 0.003 17.36 8.1.18 0.006 0.003 18.10 8.1.17 0.004 0.004 18.55 8.1.16 0.005 0.003 22.11 8.1.15 0.000 0.008 18.96 8.1.14 0.005 0.003 17.64 8.1.13 0.000 0.008 18.11 8.1.12 0.008 0.000 17.61 8.1.11 0.004 0.004 17.50 8.1.10 0.004 0.004 17.50 8.1.9 0.000 0.008 17.67 8.1.8 0.004 0.004 17.46 8.1.7 0.000 0.008 17.58 8.1.6 0.000 0.009 17.76 8.1.5 0.003 0.006 17.66 8.1.4 0.006 0.003 17.66 8.1.3 0.000 0.009 17.66 8.1.2 0.004 0.004 17.84 8.1.1 0.003 0.006 17.66 8.1.0 0.000 0.008 17.59 8.0.30 0.000 0.008 18.77 8.0.29 0.003 0.005 16.75 8.0.28 0.004 0.004 18.65 8.0.27 0.000 0.008 17.33 8.0.26 0.003 0.003 17.42 8.0.25 0.004 0.004 17.07 8.0.24 0.004 0.004 17.09 8.0.23 0.008 0.000 17.05 8.0.22 0.000 0.008 17.09 8.0.21 0.003 0.005 17.04 8.0.20 0.000 0.007 17.09 8.0.19 0.004 0.004 17.16 8.0.18 0.004 0.004 16.97 8.0.17 0.003 0.006 17.05 8.0.16 0.006 0.003 17.10 8.0.15 0.006 0.003 17.04 8.0.14 0.000 0.009 16.91 8.0.13 0.000 0.007 13.55 8.0.12 0.004 0.004 17.02 8.0.11 0.005 0.003 17.10 8.0.10 0.005 0.003 16.84 8.0.9 0.008 0.000 17.08 8.0.8 0.007 0.010 17.05 8.0.7 0.009 0.000 16.93 8.0.6 0.000 0.008 16.86 8.0.5 0.000 0.008 16.92 8.0.3 0.012 0.012 17.29 8.0.2 0.010 0.011 17.40 8.0.1 0.004 0.004 17.21 8.0.0 0.010 0.010 16.87 7.4.33 0.000 0.005 15.00 7.4.32 0.004 0.004 16.67 7.4.30 0.004 0.004 16.67 7.4.29 0.000 0.008 16.75 7.4.28 0.000 0.008 16.75 7.4.27 0.004 0.004 16.77 7.4.26 0.000 0.007 16.64 7.4.25 0.004 0.004 16.53 7.4.24 0.005 0.003 16.65 7.4.23 0.004 0.004 16.74 7.4.22 0.009 0.009 16.74 7.4.21 0.006 0.012 16.77 7.4.20 0.000 0.008 16.79 7.4.16 0.017 0.010 16.66 7.4.15 0.018 0.003 17.40 7.4.14 0.012 0.006 17.86 7.4.13 0.008 0.011 16.64 7.4.12 0.012 0.008 16.63 7.4.11 0.006 0.012 16.72 7.4.10 0.016 0.003 16.75 7.4.9 0.015 0.003 16.49 7.4.8 0.007 0.016 16.54 7.4.7 0.012 0.006 16.65 7.4.6 0.003 0.014 16.68 7.4.5 0.000 0.009 16.48 7.4.4 0.018 0.000 16.62 7.4.3 0.011 0.011 16.89 7.4.0 0.008 0.010 15.00 7.3.33 0.008 0.000 13.52 7.3.32 0.003 0.003 13.47 7.3.31 0.002 0.005 16.31 7.3.30 0.000 0.008 16.32 7.3.29 0.015 0.006 16.49 7.3.28 0.011 0.005 16.38 7.3.27 0.009 0.009 17.40 7.3.26 0.009 0.015 16.46 7.3.25 0.014 0.010 16.76 7.3.24 0.009 0.009 16.71 7.3.23 0.007 0.011 16.50 7.3.21 0.014 0.005 16.30 7.3.20 0.006 0.013 19.39 7.3.19 0.000 0.001 16.45 7.3.18 0.015 0.003 16.65 7.3.17 0.013 0.010 16.30 7.3.16 0.010 0.007 16.55 7.3.12 0.007 0.013 15.06 7.3.11 0.010 0.010 15.15 7.3.10 0.010 0.007 14.75 7.3.9 0.011 0.004 15.05 7.3.8 0.000 0.017 14.93 7.3.7 0.009 0.003 14.86 7.3.6 0.000 0.016 15.05 7.3.5 0.004 0.007 14.83 7.3.4 0.000 0.018 14.97 7.3.3 0.000 0.009 15.00 7.3.2 0.005 0.010 16.46 7.3.1 0.003 0.006 16.49 7.3.0 0.004 0.012 16.45 7.2.33 0.010 0.009 16.81 7.2.32 0.009 0.009 16.50 7.2.31 0.009 0.012 16.65 7.2.30 0.015 0.009 16.51 7.2.29 0.010 0.007 16.64 7.2.25 0.003 0.016 15.08 7.2.24 0.010 0.014 15.05 7.2.23 0.013 0.000 14.91 7.2.22 0.005 0.005 15.10 7.2.21 0.004 0.011 15.01 7.2.20 0.006 0.003 15.23 7.2.19 0.004 0.011 15.02 7.2.18 0.004 0.011 15.29 7.2.17 0.003 0.013 14.98 7.2.6 0.010 0.003 16.72 7.1.33 0.003 0.013 15.55 7.1.32 0.003 0.007 15.67 7.1.31 0.004 0.011 15.73 7.1.30 0.000 0.014 15.52 7.1.29 0.006 0.007 15.58 7.1.28 0.004 0.011 15.64 7.1.27 0.000 0.012 15.40 7.1.26 0.010 0.003 15.71 7.1.20 0.000 0.012 15.59 7.1.10 0.006 0.006 18.11 7.1.7 0.000 0.007 16.91 7.1.6 0.013 0.013 19.32 7.1.5 0.007 0.017 16.73 7.1.0 0.003 0.077 22.31 7.0.20 0.004 0.004 16.44 7.0.14 0.003 0.077 22.18 7.0.10 0.010 0.083 20.33 7.0.9 0.017 0.073 20.18 7.0.8 0.027 0.057 20.13 7.0.7 0.010 0.077 20.21 7.0.6 0.003 0.047 20.23 7.0.5 0.007 0.047 20.43 7.0.4 0.007 0.087 20.13 7.0.3 0.003 0.087 20.16 7.0.2 0.013 0.070 20.06 7.0.1 0.010 0.083 20.11 7.0.0 0.007 0.090 20.13 5.6.28 0.017 0.060 20.96 5.6.25 0.013 0.070 20.79 5.6.24 0.003 0.080 20.83 5.6.23 0.010 0.073 20.80 5.6.22 0.010 0.080 20.69 5.6.21 0.000 0.047 20.63 5.6.20 0.017 0.070 21.27 5.6.19 0.017 0.077 21.20 5.6.18 0.007 0.047 21.21 5.6.17 0.010 0.053 21.12 5.6.16 0.007 0.050 21.28 5.6.15 0.013 0.067 21.13 5.6.14 0.003 0.087 21.21 5.6.13 0.007 0.097 21.30 5.6.12 0.017 0.073 21.22 5.6.11 0.010 0.080 21.22 5.6.10 0.013 0.073 21.17 5.6.9 0.010 0.050 21.25 5.6.8 0.007 0.067 20.69 5.6.7 0.010 0.040 20.55 5.6.6 0.003 0.063 20.49 5.6.5 0.023 0.063 20.61 5.6.4 0.007 0.047 20.63 5.6.3 0.010 0.057 20.56 5.6.2 0.000 0.067 20.46 5.6.1 0.007 0.070 20.48 5.6.0 0.000 0.053 20.59 5.5.38 0.010 0.040 20.61 5.5.37 0.007 0.050 20.59 5.5.36 0.003 0.057 20.72 5.5.35 0.010 0.077 20.59 5.5.34 0.010 0.080 20.91 5.5.33 0.017 0.077 21.05 5.5.32 0.010 0.047 20.88 5.5.31 0.000 0.073 21.04 5.5.30 0.007 0.087 20.81 5.5.29 0.003 0.090 21.05 5.5.28 0.007 0.083 21.09 5.5.27 0.017 0.077 20.80 5.5.26 0.013 0.070 20.89 5.5.25 0.010 0.040 20.73 5.5.24 0.000 0.053 20.43 5.5.23 0.013 0.033 20.31 5.5.22 0.003 0.083 20.27 5.5.21 0.007 0.063 20.41 5.5.20 0.017 0.077 20.16 5.5.19 0.010 0.087 20.39 5.5.18 0.007 0.073 20.45 5.5.16 0.007 0.080 20.38 5.5.15 0.010 0.077 20.24 5.5.14 0.010 0.080 20.30 5.5.13 0.007 0.077 20.44 5.5.12 0.007 0.073 20.40 5.5.11 0.013 0.073 20.34 5.5.10 0.000 0.067 20.25 5.5.9 0.003 0.040 20.33 5.5.8 0.007 0.050 20.26 5.5.7 0.007 0.067 20.25 5.5.6 0.000 0.087 20.32 5.5.5 0.017 0.037 20.11 5.5.4 0.000 0.070 20.36 5.5.3 0.010 0.067 20.24 5.5.2 0.007 0.043 20.10 5.5.1 0.007 0.067 20.20 5.5.0 0.023 0.060 20.21 5.4.45 0.010 0.073 19.40 5.4.44 0.013 0.083 19.41 5.4.43 0.007 0.040 19.27 5.4.42 0.007 0.070 19.44 5.4.41 0.007 0.043 19.35 5.4.40 0.007 0.077 19.15 5.4.39 0.013 0.070 19.13 5.4.38 0.010 0.073 19.17 5.4.37 0.013 0.070 18.96 5.4.36 0.003 0.063 19.21 5.4.35 0.010 0.040 19.24 5.4.34 0.000 0.050 19.05 5.4.32 0.017 0.063 19.19 5.4.31 0.003 0.037 18.96 5.4.30 0.010 0.063 19.21 5.4.29 0.003 0.040 19.23 5.4.28 0.010 0.033 19.15 5.4.27 0.000 0.043 18.95 5.4.26 0.013 0.030 18.98 5.4.25 0.007 0.057 19.11 5.4.24 0.003 0.040 19.31 5.4.23 0.010 0.070 18.99 5.4.22 0.003 0.087 18.96 5.4.21 0.013 0.053 18.95 5.4.20 0.007 0.083 19.04 5.4.19 0.007 0.043 19.20 5.4.18 0.010 0.077 19.30 5.4.17 0.013 0.073 19.17 5.4.16 0.003 0.047 19.22 5.4.15 0.003 0.043 19.00 5.4.14 0.003 0.077 16.58 5.4.13 0.010 0.070 16.56 5.4.12 0.010 0.053 16.46 5.4.11 0.000 0.087 16.57 5.4.10 0.003 0.043 16.65 5.4.9 0.000 0.073 16.65 5.4.8 0.000 0.060 16.63 5.4.7 0.010 0.053 16.42 5.4.6 0.007 0.067 16.55 5.4.5 0.003 0.037 16.45 5.4.4 0.003 0.037 16.46 5.4.3 0.007 0.067 16.45 5.4.2 0.007 0.047 16.54 5.4.1 0.017 0.067 16.45 5.4.0 0.010 0.067 16.05
preferences:dark mode live preview
42.07 ms | 401 KiB | 5 Q