Skip to content

Commit

Permalink
SimpleProfiler 0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
Petr Knap committed Dec 19, 2015
2 parents 429ec47 + e180169 commit cc5890a
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.idea
vendor
phpunit.log
composer.lock
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
# php-profiler
PHP utils for code profiling

PHP profiler by [Petr Knap].



[Petr Knap]:http://petrknap.cz/
36 changes: 36 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "petrknap/php-profiler",
"description": "PHP profiler by Petr Knap",
"license": "MIT",
"authors": [
{
"name": "Petr Knap",
"email": "dev@petrknap.cz",
"homepage": "http://petrknap.cz",
"role": "Developer"
}
],
"homepage": "https://github.com/petrknap/php-profiler",
"config": {
"preferred-install": "dist"
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "4.*"
},
"scripts": {
"test": [
"./vendor/bin/phpunit"
],
"post-autoload-dump": [
"@test"
]
},
"autoload": {
"psr-4": {
"PetrKnap\\Php\\Profiler\\": "src/Profiler"
}
}
}
10 changes: 10 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<phpunit bootstrap="./vendor/autoload.php">
<testsuites>
<testsuite>
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<logging>
<log type="testdox-text" target="./tests/phpunit.log" />
</logging>
</phpunit>
117 changes: 117 additions & 0 deletions src/Profiler/SimpleProfiler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace PetrKnap\Php\Profiler;

/**
* Simple PHP class for profiling
*
* @author Petr Knap <dev@petrknap.cz>
* @since 2015-12-13
* @category Debug
* @package PetrKnap\Php\Profiler
* @version 0.5
* @license https://github.com/petrknap/php-profiler/blob/master/LICENSE MIT
*/
class SimpleProfiler
{
#region Result keys
const START_LABEL = "start_label"; // string
const START_TIME = "start_time"; // float start time in seconds
const START_MEMORY_USAGE = "start_memory_usage"; // int amount of used memory at start in bytes
const FINISH_LABEL = "finish_label"; // string
const FINISH_TIME = "finish_time"; // float finish time in seconds
const FINISH_MEMORY_USAGE = "finish_memory_usage"; // int amount of used memory at finish in bytes
const TIME_OFFSET = "time_offset"; // float time offset in seconds
const MEMORY_USAGE_OFFSET = "memory_usage_offset"; // int amount of memory usage offset in bytes
const ABSOLUTE_DURATION = "absolute_duration"; // float absolute duration in seconds
const DURATION = "duration"; // float duration in seconds
const ABSOLUTE_MEMORY_USAGE_CHANGE = "absolute_memory_usage_change"; // int absolute memory usage change in bytes
const MEMORY_USAGE_CHANGE = "memory_usage_change"; // int memory usage change in bytes
#endregion

private static $enabled = false;

private static $stack = [];

/**
* Enable profiler
*/
public static function enable()
{
self::$enabled = true;
}

/**
* Disable profiler
*/
public static function disable()
{
self::$enabled = false;
}

/**
* Start profiling
*
* @param string $label
* @return bool true on success or false on failure
*/
public static function start($label = null)
{
if(self::$enabled) {
$now = microtime(true);
$memoryUsage = memory_get_usage(true);

array_push(self::$stack, [
self::START_LABEL => $label,
self::TIME_OFFSET => 0,
self::START_TIME => $now,
self::MEMORY_USAGE_OFFSET => 0,
self::START_MEMORY_USAGE => $memoryUsage
]);

return true;
}

return false;
}

/**
* Finish profiling and get result
*
* @param string $label
* @return array|bool result as array on success or false on failure
*/
public static function finish($label = null)
{
if(self::$enabled) {
$now = microtime(true);
$memoryUsage = memory_get_usage(true);

if (empty(self::$stack)) {
throw new \OutOfRangeException("Call " . __CLASS__ . "::start() first.");
}

$result = array_pop(self::$stack);

$result[self::FINISH_LABEL] = $label;
$result[self::FINISH_TIME] = $now;
$result[self::FINISH_MEMORY_USAGE] = $memoryUsage;
$result[self::ABSOLUTE_DURATION] = $result[self::FINISH_TIME] - $result[self::START_TIME];
$result[self::DURATION] = $result[self::ABSOLUTE_DURATION] - $result[self::TIME_OFFSET];
$result[self::ABSOLUTE_MEMORY_USAGE_CHANGE] = $result[self::FINISH_MEMORY_USAGE] - $result[self::START_MEMORY_USAGE];
$result[self::MEMORY_USAGE_CHANGE] = $result[self::ABSOLUTE_MEMORY_USAGE_CHANGE] - $result[self::MEMORY_USAGE_OFFSET];

if (!empty(self::$stack)) {
$timeOffset = &self::$stack[count(self::$stack) - 1][self::TIME_OFFSET];
$timeOffset = $timeOffset + $result[self::ABSOLUTE_DURATION];

$memoryUsageOffset = &self::$stack[count(self::$stack) - 1][self::MEMORY_USAGE_OFFSET];
$memoryUsageOffset = $memoryUsageOffset + $result[self::ABSOLUTE_MEMORY_USAGE_CHANGE];
}

return $result;
}

return false;
}
}
157 changes: 157 additions & 0 deletions tests/Profiler/SimpleProfilerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<?php

use PetrKnap\Php\Profiler\SimpleProfiler;

class SimpleProfilerTest extends PHPUnit_Framework_TestCase
{
const ACCEPTABLE_DELAY = 0.002; // 2 ms

public function setUp()
{
parent::setUp();

SimpleProfiler::enable();
}

private function checkResult(array $result, $startLabel, $finishLabel)
{
$this->assertArrayHasKey(SimpleProfiler::START_LABEL, $result);
$this->assertArrayHasKey(SimpleProfiler::START_TIME, $result);
$this->assertArrayHasKey(SimpleProfiler::START_MEMORY_USAGE, $result);
$this->assertArrayHasKey(SimpleProfiler::FINISH_LABEL, $result);
$this->assertArrayHasKey(SimpleProfiler::FINISH_TIME, $result);
$this->assertArrayHasKey(SimpleProfiler::FINISH_MEMORY_USAGE, $result);
$this->assertArrayHasKey(SimpleProfiler::TIME_OFFSET, $result);
$this->assertArrayHasKey(SimpleProfiler::ABSOLUTE_DURATION, $result);
$this->assertArrayHasKey(SimpleProfiler::DURATION, $result);

$this->assertLessThanOrEqual($result[SimpleProfiler::ABSOLUTE_DURATION], $result[SimpleProfiler::DURATION]);

$this->assertEquals($startLabel, $result[SimpleProfiler::START_LABEL]);
$this->assertEquals($finishLabel, $result[SimpleProfiler::FINISH_LABEL]);
}

public function testEmptyStack()
{
$this->setExpectedException(get_class(new \OutOfRangeException()));

SimpleProfiler::finish();
}

public function testEnable()
{
SimpleProfiler::enable();

$this->assertTrue(SimpleProfiler::start());
$this->assertTrue(is_array(SimpleProfiler::finish()));
}

public function testDisable()
{
SimpleProfiler::disable();

$this->assertFalse(SimpleProfiler::start());
$this->assertFalse(SimpleProfiler::finish());
}

public function testOneLevelProfiling()
{
SimpleProfiler::start("start");

$result = SimpleProfiler::finish("finish");

$this->checkResult($result, "start", "finish");
}

public function testTwoLevelProfiling()
{
#region First level
SimpleProfiler::start("L1_S");

#region Second level A
SimpleProfiler::start("L2A_S");

$result = SimpleProfiler::finish("L2A_F");

$this->checkResult($result, "L2A_S", "L2A_F");
#endregion

#region Second level B
SimpleProfiler::start("L2B_S");

$result = SimpleProfiler::finish("L2B_F");

$this->checkResult($result, "L2B_S", "L2B_F");
#endregion

$result = SimpleProfiler::finish("L1_F");

$this->checkResult($result, "L1_S", "L1_F");
#endregion
}

public function testTimeProfiling()
{
#region First level (A)
SimpleProfiler::start();

sleep(1);

#region Second level - first (B)
SimpleProfiler::start();

sleep(3);

$B = SimpleProfiler::finish();
#endregion

#region Second level - second (C)
SimpleProfiler::start();

sleep(2);

$C = SimpleProfiler::finish();
#endregion

$A = SimpleProfiler::finish();
#endregion

$this->assertEquals(6, $A[SimpleProfiler::ABSOLUTE_DURATION], "", self::ACCEPTABLE_DELAY);
$this->assertEquals(3, $B[SimpleProfiler::ABSOLUTE_DURATION], "", self::ACCEPTABLE_DELAY);
$this->assertEquals(2, $C[SimpleProfiler::ABSOLUTE_DURATION], "", self::ACCEPTABLE_DELAY);

$this->assertEquals(1, $A[SimpleProfiler::DURATION], "", self::ACCEPTABLE_DELAY);
$this->assertEquals(3, $B[SimpleProfiler::DURATION], "", self::ACCEPTABLE_DELAY);
$this->assertEquals(2, $C[SimpleProfiler::DURATION], "", self::ACCEPTABLE_DELAY);
}

public function testMemoryProfiling()
{
SimpleProfiler::start();

$largeObject = null;
for($i = 0; $i < 1000; $i++) {
SimpleProfiler::start();
$largeObject = new \Exception("Large object", 0, $largeObject);
SimpleProfiler::finish();
}

$result = SimpleProfiler::finish();

$this->assertGreaterThan($result[SimpleProfiler::START_MEMORY_USAGE], $result[SimpleProfiler::FINISH_MEMORY_USAGE]);
$this->assertGreaterThan($result[SimpleProfiler::MEMORY_USAGE_CHANGE], $result[SimpleProfiler::ABSOLUTE_MEMORY_USAGE_CHANGE]);
$this->assertGreaterThan(0, $result[SimpleProfiler::ABSOLUTE_MEMORY_USAGE_CHANGE]);
$this->assertGreaterThan(0, $result[SimpleProfiler::MEMORY_USAGE_CHANGE]);

SimpleProfiler::start();

unset($largeObject);

$result = SimpleProfiler::finish();

$this->assertLessThan($result[SimpleProfiler::START_MEMORY_USAGE], $result[SimpleProfiler::FINISH_MEMORY_USAGE]);
$this->assertEquals($result[SimpleProfiler::ABSOLUTE_MEMORY_USAGE_CHANGE], $result[SimpleProfiler::MEMORY_USAGE_CHANGE]);
$this->assertLessThan(0, $result[SimpleProfiler::ABSOLUTE_MEMORY_USAGE_CHANGE]);
$this->assertLessThan(0, $result[SimpleProfiler::MEMORY_USAGE_CHANGE]);
}
}

0 comments on commit cc5890a

Please sign in to comment.