-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
330 additions
and
1 deletion.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.idea | ||
vendor | ||
phpunit.log | ||
composer.lock |
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
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/ |
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
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" | ||
} | ||
} | ||
} |
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
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> |
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
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; | ||
} | ||
} |
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
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]); | ||
} | ||
} |