Skip to content

Commit

Permalink
- Changed xdebug.profiler_output_name to use modifier tags:
Browse files Browse the repository at this point in the history
  %p = pid
  %r = random number
  %s = script name
  %t = timestamp (seconds)
  %u = timestamp (microseconds)
  %S = session_id (from $_COOKIE if set)
- Fixed some memleaks

SVN Rev: 2335
  • Loading branch information
Jani Taskinen committed May 14, 2007
1 parent e4983f9 commit 5852b71
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 62 deletions.
109 changes: 92 additions & 17 deletions contrib/online_profiling_prepend.php
Expand Up @@ -3,14 +3,14 @@
/*
* Online profiling dump - Written by Jani Taskinen <sniper@iki.fi> A.D. 2007
*
* $Id: online_profiling_prepend.php,v 1.1 2007-04-11 22:37:11 sniper Exp $
* $Id: online_profiling_prepend.php,v 1.2 2007-05-14 14:20:40 sniper Exp $
*
* Usage:
*
* You can either have this file included by using the php.ini
* directive "auto_prepend_file" or including it in your script.
*
* Passing XDEBUG_PROFILE in GET/POST enables the output.
* Passing XDEBUG_PROFILE in GET/POST/COOKIE enables the output.
*
* Example of download.php:
Expand All @@ -21,8 +21,9 @@
$filesize=filesize("/{$_GET['file']}");
$file=basename("/{$_GET['file']}");
// If you want to have always same filename uncomment this:
// header("Content-Disposition: attachment; filename=\"cachegrind.out\"");
header("Content-Type: application/x-kcachegrind");
header("Content-Disposition: attachment; filename=\"cachegrind.out\"");
header("Content-Length: {$filesize}");
passthru("cat /tmp/xdebug/{$file}",$err);
}
Expand All @@ -31,11 +32,27 @@
?>
*
* Example of php.ini options:
*
[xdebug]
zend_extension_debug = ${extension_dir}"/xdebug.so"
xdebug.profiler_enable = Off
xdebug.profiler_enable_trigger = On
xdebug.profiler_output_dir = /tmp/xdebug
xdebug.profiler_append = On
xdebug.profiler_aggregate = Off
xdebug.profiler_output_name = %H.%S ; <HTTP_HOST>.<SESSION_ID>
xdebug.extended_info = 1
*
*/

function xdebug_profiler_shutdown()
function xdebug_profiler_shutdown_cb()
{
$is_xmlhttprequest = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');

if (isset($_REQUEST['XDEBUG_PROFILE']))
{
$dump = '';
Expand All @@ -44,30 +61,88 @@ function xdebug_profiler_shutdown()
$used_memory = round($used_memory/pow(1024, ($i = floor(log($used_memory, 1024)))), 2) . $sizename[$i];
$elapsed_time = round(xdebug_time_index() * 1000, 3);
$profile = xdebug_get_profiler_filename();
$profile_id = md5($profile);

if (file_exists($profile))
if (file_exists($profile) && !$is_xmlhttprequest) // Fixme: How to provide profiler links for these without breaking possible json?
{
$view_type_url = '<a href="?XDEBUG_PROFILE">[Annotate]</a>';

if ($_REQUEST['XDEBUG_PROFILE'] != 'short')
if ($_REQUEST['XDEBUG_PROFILE'] == 'long')
{
$view_type_url = '<a href="?XDEBUG_PROFILE=short">[No annotation]</a>';
$dump = shell_exec("/usr/bin/callgrind_annotate --inclusive=yes --tree=both $profile");
}

echo <<< DATA
<div style="position: absolute; top: 0; z-index: 5000; border: dashed black 1px; background-color: #fff;" id="xdebug_profile_{$profile_id}">
<a href="#" style="font-size: 11px;" onclick="javascript: document.getElementById('xdebug_profile_{$profile_id}').style.display = 'none'; return false;">[close]</a>
<pre style="padding: 5px;">
<b>Page generated in</b> {$elapsed_time} ms <b>Used memory:</b> {$used_memory}
<b>Profiler dump:</b> <a href="/download.php?file={$profile}">$profile</a>
{$dump}</pre>
<a href="#" style="font-size: 11px;" onclick="javascript: document.getElementById('xdebug_profile_{$profile_id}').style.display = 'none'; return false;">[close]</a>
</div>
DATA;
}
}

/* Output button to enable/disable profiler */
if (!$is_xmlhttprequest)
{
$profiler = isset($_REQUEST['XDEBUG_PROFILE']) ?
array
(
'enabled' => 1,
'checked' => 'checked="checked"',
'display' => 'inline',
) :
array
(
'enabled' => 0,
'checked' => '',
'display' => 'none',
);

echo <<< DATA
<hr />
<pre style="border: dashed black 1px; margin: 5px; padding: 5px; background-color: #fff;">
<b>Page generated in</b> {$elapsed_time} ms <b>Used memory:</b> {$used_memory}
<b>Profiler dump:</b> <a href="/download.php?file={$profile}">$profile</a> {$view_type_url}
{$dump}</pre>
<!-- XDEBUG Dynamic Profiler -->
<script type="text/javascript">
<!--
var xdebug_Profiler = {$profiler['enabled']};
function xdebug_setCookie(value)
{
if (value == '')
document.cookie = "XDEBUG_PROFILE=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT";
else
document.cookie = "XDEBUG_PROFILE=" + value + "; path=/; expires=Fri, 01-Jan-2038 00:00:01 GMT";
}
function xdebug_toggleProfiler(output)
{
var annotate = document.getElementById('xdebug_profiler_annotate');
if (xdebug_Profiler) {
xdebug_setCookie('');
xdebug_Profiler = 0;
annotate.style.display = 'none';
} else {
xdebug_setCookie(output);
xdebug_Profiler = 1;
annotate.style.display = 'inline';
}
return xdebug_Profiler;
}
// -->
</script>
<div style="padding: 5px; border: dashed black 1px; background-color: #fff; position: absolute; top: 0; z-index: 1000;" id="xdebug_profile_enable_cookie">
<label for="xdebug_toggler" style="vertical-align: top">Toggle Profiler</label>
<input id="xdebug_toggler" type="checkbox" onclick="this.checked = xdebug_toggleProfiler(this.value);" value="short" {$profiler['checked']} />
<div id="xdebug_profiler_annotate" style="display: {$profiler['display']}">
<label for="xdebug_annotate" style="vertical-align: top">Annotate</label>
<input id="xdebug_annotate" type="checkbox" onclick="xdebug_setCookie((this.checked)?this.value:'short');" value="long" />
</div>
</div>
DATA;
}
}

/* Only register shutdown function if profiling is enabled */
if (isset($_REQUEST['XDEBUG_PROFILE']))
/* Register shutdown function */
if (PHP_SAPI != 'cli')
{
register_shutdown_function('xdebug_profiler_shutdown');
register_shutdown_function('xdebug_profiler_shutdown_cb');
}
148 changes: 128 additions & 20 deletions usefulstuff.c
Expand Up @@ -38,6 +38,7 @@
#include "usefulstuff.h"
#include "ext/standard/php_lcg.h"
#include "ext/standard/flock_compat.h"
#include "main/php_ini.h"

#define READ_BUFFER_SIZE 128

Expand Down Expand Up @@ -387,11 +388,9 @@ static FILE *xdebug_open_file(char *fname, char *mode, char *extension, char **n
}
fh = fopen(tmp_fname, mode);
if (fh && new_fname) {
if (new_fname) {
*new_fname = tmp_fname;
} else {
xdfree(tmp_fname);
}
*new_fname = tmp_fname;
} else {
xdfree(tmp_fname);
}
return fh;
}
Expand All @@ -403,9 +402,9 @@ static FILE *xdebug_open_file_with_random_ext(char *fname, char *mode, char *ext
TSRMLS_FETCH();

if (extension) {
tmp_fname = xdebug_sprintf("%s.%08x.%s", fname, php_combined_lcg(TSRMLS_C), extension);
tmp_fname = xdebug_sprintf("%s.%06x.%s", fname, (long) (1000000 * php_combined_lcg(TSRMLS_C)), extension);
} else {
tmp_fname = xdebug_sprintf("%s.%08x", fname, php_combined_lcg(TSRMLS_C));
tmp_fname = xdebug_sprintf("%s.%06x", fname, (long) (1000000 * php_combined_lcg(TSRMLS_C)), extension);
}
fh = fopen(tmp_fname, mode);
if (fh && new_fname) {
Expand Down Expand Up @@ -434,40 +433,36 @@ FILE *xdebug_fopen(char *fname, char *mode, char *extension, char **new_fname)
if (extension) {
tmp_fname = xdebug_sprintf("%s.%s", fname, extension);
} else {
tmp_fname = xdebug_sprintf("%s", fname);
tmp_fname = xdstrdup(fname);
}
r = stat(tmp_fname, &buf);
/* We're not freeing "tmp_fname" as that is used in the freopen as well. */

if (r == -1) {
xdfree(tmp_fname);
tmp_fname = NULL;
/* 2. Cool, the file doesn't exist so we can open it without probs now. */
fh = xdebug_open_file(fname, "w", extension, new_fname);
goto lock;
}

/* 3. It exists, check if we can open it. */
fh = xdebug_open_file(fname, "r+", extension, (char**) &tmp_fname);
fh = xdebug_open_file(fname, "r+", extension, new_fname);
if (!fh) {
xdfree(tmp_fname);
tmp_fname = NULL;
/* 4. If fh == null we couldn't even open the file, so open a new one with a new name */
fh = xdebug_open_file_with_random_ext(fname, "w", extension, new_fname);
goto lock;
}

/* 5. It exists and we can open it, check if we can exclusively lock it. */
r = flock(fileno(fh), LOCK_EX | LOCK_NB);
if (r == -1) {
if (errno == EWOULDBLOCK) {
fclose(fh);
xdfree(tmp_fname);
tmp_fname = NULL;
/* 6. The file is in use, so we open one with a new name. */
fh = xdebug_open_file_with_random_ext(fname, "w", extension, new_fname);
goto lock;
}
}

/* 7. We established a lock, now we truncate and return the handle */
fh = freopen(tmp_fname, "w", fh);

Expand All @@ -477,10 +472,6 @@ FILE *xdebug_fopen(char *fname, char *mode, char *extension, char **new_fname)
* the file and opens it again. There is a small race condition here...
*/
flock(fileno(fh), LOCK_EX | LOCK_NB);
if (new_fname && tmp_fname) {
*new_fname = tmp_fname;
return fh;
}
}
xdfree(tmp_fname);
return fh;
Expand All @@ -493,11 +484,128 @@ FILE *xdebug_fopen(char *fname, char *mode, char *extension, char **new_fname)
if (extension) {
tmp_fname = xdebug_sprintf("%s.%s", fname, extension);
} else {
tmp_fname = xdebug_sprintf("%s", fname);
tmp_fname = xdstrdup(fname);
}
if (new_fname) {
*new_fname = tmp_fname;
} else {
xdfree(tmp_fname);
}
return fopen(tmp_fname, mode);
}
#endif

int xdebug_format_output_filename(char **filename, char *format, char *script_name)
{
xdebug_str fname = {0, 0, NULL};
TSRMLS_FETCH();

while (*format)
{
if (*format != '%') {
xdebug_str_addl(&fname, (char *) format, 1, 0);
} else {
format++;
switch (*format)
{
case 'p': /* pid */
xdebug_str_add(&fname, xdebug_sprintf("%ld", getpid()), 1);
break;

case 'r': /* random number */
xdebug_str_add(&fname, xdebug_sprintf("%06x", (long) (1000000 * php_combined_lcg(TSRMLS_C))), 1);
break;

case 's': { /* script fname */
char *char_ptr, *script_name_tmp = xdstrdup(script_name);

/* replace slashes and whitespace with underscores */
while ((char_ptr = strpbrk(script_name_tmp, "/\\ ")) != NULL) {
char_ptr[0] = '_';
}
/* replace .php with _php */
char_ptr = strrchr(script_name_tmp, '.');
if (char_ptr) {
char_ptr[0] = '_';
}
xdebug_str_add(&fname, script_name_tmp, 0);
xdfree(script_name_tmp);
} break;

case 't': { /* timestamp (in seconds) */
time_t the_time = time(NULL);
xdebug_str_add(&fname, xdebug_sprintf("%ld", the_time), 1);
} break;

case 'u': { /* timestamp (in microseconds) */
char *char_ptr, *utime = xdebug_sprintf("%f", xdebug_get_utime());

/* Replace . with _ (or should it be nuked?) */
char_ptr = strrchr(utime, '.');
if (char_ptr) {
char_ptr[0] = '_';
}
xdebug_str_add(&fname, utime, 1);
} break;

case 'H': /* $_SERVER['HTTP_HOST'] */
case 'R': { /* $_SERVER['REQUEST_URI'] */
zval **data;
char *char_ptr, *strval;
int retval;

if (PG(http_globals)[TRACK_VARS_SERVER]) {
switch (*format) {
case 'H':
retval = zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_HOST", sizeof("HTTP_HOST"), (void **) &data);
break;
case 'R':
retval = zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &data);
break;
}

if (retval == SUCCESS) {
strval = estrdup(Z_STRVAL_PP(data));

/* replace slashes, dots, question marks, plus signs,
* ambersands and spaces with underscores */
while ((char_ptr = strpbrk(strval, "/\\.?&+ ")) != NULL) {
char_ptr[0] = '_';
}
xdebug_str_add(&fname, strval, 0);
efree(strval);
}
}
} break;

case 'S': { /* session id */
zval **data;
char *char_ptr, *strval;
char *sess_name;

sess_name = zend_ini_string("session.name", sizeof("session.name"), 0);

if (sess_name && PG(http_globals)[TRACK_VARS_COOKIE] &&
zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]), sess_name, strlen(sess_name) + 1, (void **) &data) == SUCCESS &&
Z_STRLEN_PP(data) < 100 /* Prevent any unrealistically long data being set as filename */
) {
strval = estrdup(Z_STRVAL_PP(data));

/* replace slashes, dots, question marks, plus signs,
* ambersands and spaces with underscores */
while ((char_ptr = strpbrk(strval, "/\\.?&+ ")) != NULL) {
char_ptr[0] = '_';
}
xdebug_str_add(&fname, strval, 0);
efree(strval);
}
} break;
}
}
format++;
}

*filename = fname.d;

return fname.l;
}
1 change: 1 addition & 0 deletions usefulstuff.h
Expand Up @@ -60,6 +60,7 @@ char* xdebug_get_time(void);
char *xdebug_path_to_url(const char *fileurl TSRMLS_DC);
char *xdebug_path_from_url(const char *fileurl TSRMLS_DC);
FILE *xdebug_fopen(char *fname, char *mode, char *extension, char **new_fname);
int xdebug_format_output_filename(char **filename, char *format, char *script_name);

#define XDEBUG_CRC32(crc, ch) (crc = (crc >> 8) ^ xdebug_crc32tab[(crc ^ (ch)) & 0xff])

Expand Down
2 changes: 1 addition & 1 deletion xdebug.c
Expand Up @@ -289,7 +289,7 @@ PHP_INI_BEGIN()
/* Profiler settings */
STD_PHP_INI_BOOLEAN("xdebug.profiler_enable", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, profiler_enable, zend_xdebug_globals, xdebug_globals)
STD_PHP_INI_ENTRY("xdebug.profiler_output_dir", "/tmp", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, profiler_output_dir, zend_xdebug_globals, xdebug_globals)
STD_PHP_INI_ENTRY("xdebug.profiler_output_name", "crc32", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, profiler_output_name, zend_xdebug_globals, xdebug_globals)
STD_PHP_INI_ENTRY("xdebug.profiler_output_name", "cachegrind.out.%p", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, profiler_output_name, zend_xdebug_globals, xdebug_globals)
STD_PHP_INI_BOOLEAN("xdebug.profiler_enable_trigger", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, profiler_enable_trigger, zend_xdebug_globals, xdebug_globals)
STD_PHP_INI_BOOLEAN("xdebug.profiler_append", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, profiler_append, zend_xdebug_globals, xdebug_globals)
STD_PHP_INI_BOOLEAN("xdebug.profiler_aggregate", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, profiler_aggregate, zend_xdebug_globals, xdebug_globals)
Expand Down

0 comments on commit 5852b71

Please sign in to comment.