1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 10:12:41 +01:00

Add "stop on redirect" and "always profile" debugging options

Summary:
Currently, it's hard to debug performance issues on POST pages. Add flags to stop redirects and always collect profiles.

Also fix an issue with "all" profiles. This feature is mostly just for profiling DarkConsole itself and is rarely used, I think it's been broken for some time. There's no way to get to it with the UI.

NOTE: Some JS workflows don't stop on redirect because they use JS/AJAX redirects.

Test Plan: Enabled options, browsed, got stopped on redirects and had profiles generated. Disabled options and verified redirects and profiles work normally.

Reviewers: vrana, btrahan

Reviewed By: vrana

CC: aran

Differential Revision: https://secure.phabricator.com/D2990
This commit is contained in:
epriestley 2012-07-17 12:06:25 -07:00
parent 9d26ef4248
commit ae2e73ce80
6 changed files with 115 additions and 18 deletions

View file

@ -1199,4 +1199,21 @@ return array(
'style.monospace' => '10px "Menlo", "Consolas", "Monaco", monospace', 'style.monospace' => '10px "Menlo", "Consolas", "Monaco", monospace',
// -- Debugging ------------------------------------------------------------- //
// Enable this to change HTTP redirects into normal pages with a link to the
// redirection target. For example, after you submit a form you'll get a page
// saying "normally, you'd be redirected...". This is useful to examine
// service or profiler information on write pathways, or debug redirects. It
// also makes the UX horrible for normal use, so you should enable it only
// when debugging.
//
// NOTE: This does not currently work for forms with Javascript "workflow",
// since the redirect happens in Javascript.
'debug.stop-on-redirect' => false,
// Enable this to always profile every page. This is very slow! You should
// only enable it when debugging.
'debug.profile-every-request' => false,
); );

View file

@ -103,8 +103,8 @@ final class DarkConsoleXHProfPlugin extends DarkConsolePlugin {
public function willShutdown() { public function willShutdown() {
if (isset($_REQUEST['__profile__']) && if (DarkConsoleXHProfPluginAPI::isProfilerRequested() &&
$_REQUEST['__profile__'] != 'all') { (DarkConsoleXHProfPluginAPI::isProfilerRequested() !== 'all')) {
$this->xhprofID = DarkConsoleXHProfPluginAPI::stopProfiler(); $this->xhprofID = DarkConsoleXHProfPluginAPI::stopProfiler();
} }
} }

View file

@ -29,6 +29,18 @@ final class DarkConsoleXHProfPluginAPI {
return extension_loaded('xhprof'); return extension_loaded('xhprof');
} }
public static function isProfilerRequested() {
if (!empty($_REQUEST['__profile__'])) {
return $_REQUEST['__profile__'];
}
if (PhabricatorEnv::getEnvConfig('debug.profile-every-request')) {
return PhabricatorEnv::getEnvConfig('debug.profile-every-request');
}
return false;
}
public static function includeXHProfLib() { public static function includeXHProfLib() {
// TODO: this is incredibly stupid, but we may not have Phutil metamodule // TODO: this is incredibly stupid, but we may not have Phutil metamodule
// stuff loaded yet so we can't just phutil_get_library_root() our way // stuff loaded yet so we can't just phutil_get_library_root() our way
@ -41,8 +53,9 @@ final class DarkConsoleXHProfPluginAPI {
require_once $root.'/externals/xhprof/xhprof_lib.php'; require_once $root.'/externals/xhprof/xhprof_lib.php';
} }
public static function hookProfiler() { public static function hookProfiler() {
if (empty($_REQUEST['__profile__'])) { if (!self::isProfilerRequested()) {
return; return;
} }
@ -71,17 +84,41 @@ final class DarkConsoleXHProfPluginAPI {
$data = serialize($data); $data = serialize($data);
$file_class = 'PhabricatorFile'; $file_class = 'PhabricatorFile';
// Since these happen on GET we can't do guarded writes. // Since these happen on GET we can't do guarded writes. These also
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); // sometimes happen after we've disposed of the write guard; in this
// case we need to disable the whole mechanism.
$file = call_user_func( $use_scope = AphrontWriteGuard::isGuardActive();
array($file_class, 'newFromFileData'), if ($use_scope) {
$data, $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
array( } else {
'mime-type' => 'application/xhprof', AphrontWriteGuard::allowDangerousUnguardedWrites(true);
'name' => 'profile.xhprof', }
));
return $file->getPHID(); $caught = null;
try {
$file = call_user_func(
array($file_class, 'newFromFileData'),
$data,
array(
'mime-type' => 'application/xhprof',
'name' => 'profile.xhprof',
));
} catch (Exception $ex) {
$caught = $ex;
}
if ($use_scope) {
unset($unguarded);
} else {
AphrontWriteGuard::allowDangerousUnguardedWrites(false);
}
if ($caught) {
throw $caught;
} else {
return $file->getPHID();
}
} else { } else {
return null; return null;
} }

View file

@ -34,15 +34,47 @@ class AphrontRedirectResponse extends AphrontResponse {
return (string)$this->uri; return (string)$this->uri;
} }
public function shouldStopForDebugging() {
return PhabricatorEnv::getEnvConfig('debug.stop-on-redirect');
}
public function getHeaders() { public function getHeaders() {
$headers = array( $headers = array();
array('Location', $this->uri), if (!$this->shouldStopForDebugging()) {
); $headers[] = array('Location', $this->uri);
}
$headers = array_merge(parent::getHeaders(), $headers); $headers = array_merge(parent::getHeaders(), $headers);
return $headers; return $headers;
} }
public function buildResponseString() { public function buildResponseString() {
if ($this->shouldStopForDebugging()) {
$view = new PhabricatorStandardPageView();
$view->setRequest($this->getRequest());
$view->setApplicationName('Debug');
$view->setTitle('Stopped on Redirect');
$error = new AphrontErrorView();
$error->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$error->setTitle('Stopped on Redirect');
$link = phutil_render_tag(
'a',
array(
'href' => $this->getURI(),
),
'Continue to: '.phutil_escape_html($this->getURI()));
$error->appendChild(
'<p>You were stopped here because <tt>debug.stop-on-redirect</tt> '.
'is set in your configuration.</p>'.
'<p>'.$link.'</p>');
$view->appendChild($error);
return $view->render();
}
return ''; return '';
} }

View file

@ -108,6 +108,17 @@ final class AphrontWriteGuard {
} }
/**
* Determine if there is an active write guard.
*
* @return bool
* @task manage
*/
public static function isGuardActive() {
return (bool)self::$instance;
}
/* -( Protecting Writes )-------------------------------------------------- */ /* -( Protecting Writes )-------------------------------------------------- */

View file

@ -220,8 +220,8 @@ $headers = array_merge($headers, $response->getHeaders());
$sink->writeHeaders($headers); $sink->writeHeaders($headers);
// TODO: This shouldn't be possible in a production-configured environment. // TODO: This shouldn't be possible in a production-configured environment.
if (isset($_REQUEST['__profile__']) && if (DarkConsoleXHProfPluginAPI::isProfilerRequested() &&
($_REQUEST['__profile__'] == 'all')) { DarkConsoleXHProfPluginAPI::isProfilerRequested() === 'all') {
$profile = DarkConsoleXHProfPluginAPI::stopProfiler(); $profile = DarkConsoleXHProfPluginAPI::stopProfiler();
$profile = $profile =
'<div style="text-align: center; background: #ff00ff; padding: 1em; '<div style="text-align: center; background: #ff00ff; padding: 1em;