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
Bob Somers
committed
Jan 27, 2012
0 parents
commit ae1c7d5
Showing
4 changed files
with
336 additions
and
0 deletions.
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,124 @@ | ||
<?php | ||
|
||
/** | ||
* This class can be used to log memory usage of your PHP code over time, | ||
* with the intention of helping you locate where you're running into memory | ||
* usage problems. It is not a full-featured profiling tool like Xdebug or | ||
* Webgrind. | ||
* | ||
* MemLog writes out data to a log file, which can be analyzed with another | ||
* tool (or by hand if you wish, it's just pseudo-JSON). Each line is a JSON | ||
* array with the following fields: | ||
* | ||
* [time since logging began, in seconds (float), | ||
* current PHP memory usage, in bytes (integer), | ||
* file name of the currently executing code, if any (string), | ||
* class name of the currently executing code, if any (string), | ||
* function name of the currently executing code, if any (string)] | ||
* | ||
* Basic usage is as follows: | ||
* | ||
* $memlog = new MemLog(); | ||
* $memlog->start('My Terrible Code'); | ||
* | ||
* ...your bad code eats gobs of memory... | ||
* | ||
* $memlog->stop(); | ||
* | ||
* Log files are saved with a base path of MemLog::$basePath, Unless you have | ||
* a compelling reason, /tmp is a good choice for $basePath. | ||
* | ||
* @author Bob Somers | ||
*/ | ||
class MemLog { | ||
public static $basePath = '/tmp'; | ||
private static $maxBufferSize = 100; | ||
|
||
private $isLogging = false; | ||
private $fp = null; | ||
private $buffer = array(); | ||
private $startTime = 0.0; | ||
|
||
public function __construct() { | ||
// Tweak this if necessary. | ||
declare(ticks=1); | ||
} | ||
|
||
public function __destruct() { | ||
if ($this->isLogging) { | ||
$this->stop(); | ||
} | ||
} | ||
|
||
public function start($name = 'MemLog') { | ||
if ($this->isLogging) return; | ||
|
||
$name .= ' - ' . date('r'); | ||
|
||
list($usec, $time) = explode(' ', microtime()); | ||
list($junk, $usec) = explode('.', $usec); | ||
$fileName = 'php_mem_usage.' . date('Y-m-d-H-i-s', $time) . "-$usec.log"; | ||
|
||
$this->fp = fopen(self::$basePath . '/' . $fileName, 'w'); | ||
fwrite($this->fp, $name); | ||
$this->buffer = array(); | ||
|
||
$this->isLogging = true; | ||
|
||
$this->startTime = microtime(true); | ||
register_tick_function(array(&$this, 'tick')); | ||
} | ||
|
||
public function stop() { | ||
if (!$this->isLogging) return; | ||
|
||
unregister_tick_function(array(&$this, 'tick')); | ||
|
||
$this->flush(); | ||
fclose($this->fp); | ||
|
||
$this->isLogging = false; | ||
} | ||
|
||
public function tick() { | ||
$timestamp = microtime(true) - $this->startTime; | ||
$memory = memory_get_usage(true); | ||
|
||
$file = ''; | ||
$class = ''; | ||
$function = ''; | ||
$backtrace = debug_backtrace(); | ||
if (isset($backtrace[1])) { | ||
if (isset($backtrace[1]['file'])) { | ||
$file = basename($backtrace[1]['file']); | ||
} | ||
if (isset($backtrace[1]['class'])) { | ||
$class = $backtrace[1]['class']; | ||
} | ||
if (isset($backtrace[1]['function'])) { | ||
$function = $backtrace[1]['function']; | ||
} | ||
} | ||
|
||
$this->buffer[] = array( | ||
$timestamp, | ||
$memory, | ||
$file, | ||
$class, | ||
$function | ||
); | ||
|
||
if (count($this->buffer) > self::$maxBufferSize) { | ||
$this->flush(); | ||
} | ||
} | ||
|
||
private function flush() { | ||
for ($i = 0; $i < count($this->buffer); $i++) { | ||
fwrite($this->fp, "\n" . json_encode($this->buffer[$i])); | ||
} | ||
$this->buffer = array(); | ||
} | ||
} | ||
|
||
?> |
Large diffs are not rendered by default.
Oops, something went wrong.
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,137 @@ | ||
<?php | ||
|
||
/* | ||
* DO NOT USE THIS ON A PRODUCTION MACHINE. | ||
* | ||
* There is an easy way to get this script to basically dump out any file on | ||
* the machine that the web server user has read access to, which is absolutely | ||
* atrociously bad for security. | ||
* | ||
* This is a developer tool. Use it on a machine that only your developers have | ||
* access to. Failure to heed this advice means you are willing to accept | ||
* whatever punishment the intertubes inflict upon you. | ||
* | ||
* YOU HAVE BEEN WARNED. | ||
* | ||
* @author Bob Somers | ||
*/ | ||
|
||
if (isset($_POST['logfile'])) { | ||
$logfile = base64_decode($_POST['logfile']); | ||
} else if (isset($_GET['logfile'])) { | ||
header('Content-type: application/json'); | ||
|
||
// HERE BE DRAGONS. This is insanely bad. Never ever do this. | ||
$lines = file(base64_decode($_GET['logfile']), FILE_IGNORE_NEW_LINES); | ||
|
||
$name = array_shift($lines); | ||
|
||
echo "{\n"; | ||
echo "\t\"name\": " . json_encode($name) . ",\n"; | ||
echo "\t\"data\": [\n"; | ||
$first = true; | ||
foreach ($lines as $line) { | ||
if (!$first) { | ||
echo ",\n"; | ||
} else { | ||
$first = false; | ||
} | ||
echo "\t\t" . $line; | ||
} | ||
echo "\n\t]\n"; | ||
echo "}\n"; | ||
|
||
exit(); | ||
} | ||
|
||
?> | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<title>MemLog Viewer</title> | ||
|
||
<link href="http://fonts.googleapis.com/css?family=Ubuntu:700" | ||
rel="stylesheet" type="text/css"> | ||
<link href="http://fonts.googleapis.com/css?family=Anonymous+Pro:400,700" | ||
rel="stylesheet" type="text/css"> | ||
|
||
<script src="https://ajax.googleapis.com/ajax/libs/mootools/1.4.1/mootools-yui-compressed.js" | ||
type="text/javascript"></script> | ||
<script src="dygraph-combined.js" | ||
type="text/javascript"></script> | ||
<script src="memlog.js" | ||
type="text/javascript"></script> | ||
|
||
<?php if (isset($logfile)): ?> | ||
<script type="text/javascript"> | ||
window.addEvent('domready', function() { | ||
new Request.JSON({ | ||
url: '?logfile=<?php echo base64_encode($logfile); ?>', | ||
onSuccess: function(resp) { | ||
$('name').set('text', resp.name); | ||
memlog(resp.data); | ||
} | ||
}).get(); | ||
}); | ||
</script> | ||
<?php endif; ?> | ||
<style type="text/css"> | ||
#codeloc { | ||
font: 24px 'Anonymous Pro'; | ||
width 960px; | ||
text-align: right; | ||
line-height: 30px; | ||
width: 960px; | ||
height: 30px; | ||
} | ||
|
||
#choose { | ||
position: absolute; | ||
top: 28px; | ||
left: 480px; | ||
} | ||
|
||
h1 { | ||
font: bold 48px 'Ubuntu'; | ||
margin: 0; | ||
padding-left: 20px; | ||
height: 64px; | ||
line-height: 64px; | ||
border-bottom: 1px solid #aaa; | ||
} | ||
|
||
h2 { | ||
font: bold 24px 'Ubuntu'; | ||
margin: 0; | ||
padding: 10px 0 0 10px; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<h1>MemLog Viewer</h1> | ||
<form id="choose" method="post"> | ||
<select name="logfile"> | ||
<?php | ||
$files = glob('/tmp/php_mem_usage*.log'); | ||
rsort($files); | ||
foreach ($files as $file) { | ||
echo '<option value="' . base64_encode($file) . '"'; | ||
if ($logfile == $file) { | ||
echo ' selected'; | ||
} | ||
echo ">$file</option>\n"; | ||
} | ||
?> | ||
</select> | ||
<input type="submit" value="View"> | ||
</form> | ||
<?php if (isset($logfile)): ?> | ||
|
||
<h2 id="name">Loading...</h2> | ||
<div id="codeloc"></div> | ||
<div id="graph"></div> | ||
|
||
<?php endif; ?> | ||
</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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
function memlog(data) { | ||
var series = []; | ||
var details = {}; | ||
for (var i = 0; i < data.length; i++) { | ||
var sample = data[i]; | ||
|
||
var timestamp = sample[0] * 1000; // convert to ms | ||
var memory = sample[1]; | ||
var file = sample[2]; | ||
var klass = sample[3]; | ||
var func = sample[4]; | ||
|
||
series.push([timestamp, memory]); | ||
details[String(timestamp)] = { | ||
file: file, | ||
klass: klass, | ||
func: func | ||
}; | ||
} | ||
|
||
// Helper functions for formatting. Thanks to: | ||
// http://dygraphs.com/tests/labelsKMB.html | ||
function round(num, places) { | ||
var shift = Math.pow(10, places); | ||
return Math.round(num * shift)/shift; | ||
} | ||
|
||
function formatBytes(v) { | ||
var suffixes = ['', 'k', 'M', 'G', 'T']; | ||
if (v < 1000) return v; | ||
|
||
var magnitude = Math.floor(String(Math.floor(v)).length / 3); | ||
if (magnitude > suffixes.length - 1) { | ||
magnitude = suffixes.length - 1; | ||
} | ||
|
||
return String(round(v / Math.pow(10, magnitude * 3), 2)) + suffixes[magnitude]; | ||
} | ||
|
||
function formatMs(v) { | ||
return String(round(v, 3) + " ms"); | ||
} | ||
|
||
var codeloc = $('codeloc'); | ||
function showDetails(e, x) { | ||
var info = details[String(x)]; | ||
var where = ''; | ||
if (info.file) { | ||
where += '<strong>' + info.file + '</strong>: '; | ||
} | ||
if (info.klass) { | ||
where += info.klass + '::'; | ||
} | ||
if (info.func) { | ||
where += info.func + '()'; | ||
} | ||
codeloc.set('html', where); | ||
} | ||
|
||
var graph = new Dygraph(document.getElementById('graph'), series, { | ||
width: 960, | ||
height: 300, | ||
fillGraph: true, | ||
stepPlot: true, | ||
labels: ['Time', 'Mem'], | ||
xlabel: 'Time (ms)', | ||
ylabel: 'Memory Usage', | ||
labelsKMG2: true, | ||
xValueFormatter: formatMs, | ||
yValueFormatter: formatBytes, | ||
highlightCallback: showDetails, | ||
colors: ["#336DA8"] | ||
}); | ||
} |