Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14007983
D10465.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
D10465.diff
View Options
diff --git a/src/applications/config/option/PhabricatorDeveloperConfigOptions.php b/src/applications/config/option/PhabricatorDeveloperConfigOptions.php
--- a/src/applications/config/option/PhabricatorDeveloperConfigOptions.php
+++ b/src/applications/config/option/PhabricatorDeveloperConfigOptions.php
@@ -46,6 +46,31 @@
"enable this option in production.\n\n".
"You must enable DarkConsole by setting {{darkconsole.enabled}} ".
"before this option will have any effect.")),
+ $this->newOption('debug.time-limit', 'int', null)
+ ->setSummary(
+ pht(
+ 'Limit page execution time to debug hangs.'))
+ ->setDescription(
+ pht(
+ "This option can help debug pages which are taking a very ".
+ "long time (more than 30 seconds) to render.\n\n".
+ "If a page is slow to render (but taking less than 30 seconds), ".
+ "the best tools to use to figure out why it is slow are usually ".
+ "the DarkConsole service call profiler and XHProf.\n\n".
+ "However, if a request takes a very long time to return, some ".
+ "components (like Apache, nginx, or PHP itself) may abort the ".
+ "request before it finishes. This can prevent you from using ".
+ "profiling tools to understand page performance in detail.\n\n".
+ "In these cases, you can use this option to force the page to ".
+ "abort after a smaller number of seconds (for example, 10), and ".
+ "dump a useful stack trace. This can provide useful information ".
+ "about why a page is hanging.\n\n".
+ "To use this option, set it to a small number (like 10), and ".
+ "reload a hanging page. The page should exit after 10 seconds ".
+ "and give you a stack trace.\n\n".
+ "You should turn this option off (set it to 0) when you are ".
+ "done with it. Leaving it on creates a small amount of overhead ".
+ "for all requests, even if they do not hit the time limit.")),
$this->newOption('debug.stop-on-redirect', 'bool', false)
->setBoolOptions(
array(
diff --git a/support/PhabricatorStartup.php b/support/PhabricatorStartup.php
--- a/support/PhabricatorStartup.php
+++ b/support/PhabricatorStartup.php
@@ -38,6 +38,7 @@
final class PhabricatorStartup {
private static $startTime;
+ private static $debugTimeLimit;
private static $globals = array();
private static $capturingOutput;
private static $rawInput;
@@ -226,6 +227,70 @@
}
+/* -( Debug Time Limit )--------------------------------------------------- */
+
+
+ /**
+ * Set a time limit (in seconds) for the current script. After time expires,
+ * the script fatals.
+ *
+ * This works like `max_execution_time`, but prints out a useful stack trace
+ * when the time limit expires. This is primarily intended to make it easier
+ * to debug pages which hang by allowing extraction of a stack trace: set a
+ * short debug limit, then use the trace to figure out what's happening.
+ *
+ * The limit is implemented with a tick function, so enabling it implies
+ * some accounting overhead.
+ *
+ * @param int Time limit in seconds.
+ * @return void
+ */
+ public static function setDebugTimeLimit($limit) {
+ self::$debugTimeLimit = $limit;
+
+ static $initialized;
+ if (!$initialized) {
+ declare(ticks=1);
+ register_tick_function(array('PhabricatorStartup', 'onDebugTick'));
+ }
+ }
+
+
+ /**
+ * Callback tick function used by @{method:setDebugTimeLimit}.
+ *
+ * Fatals with a useful stack trace after the time limit expires.
+ *
+ * @return void
+ */
+ public static function onDebugTick() {
+ $limit = self::$debugTimeLimit;
+ if (!$limit) {
+ return;
+ }
+
+ $elapsed = (microtime(true) - self::getStartTime());
+ if ($elapsed > $limit) {
+ $frames = array();
+ foreach (debug_backtrace() as $frame) {
+ $file = isset($frame['file']) ? $frame['file'] : '-';
+ $file = basename($file);
+
+ $line = isset($frame['line']) ? $frame['line'] : '-';
+ $class = isset($frame['class']) ? $frame['class'].'->' : null;
+ $func = isset($frame['function']) ? $frame['function'].'()' : '?';
+
+ $frames[] = "{$file}:{$line} {$class}{$func}";
+ }
+
+ self::didFatal(
+ "Request aborted by debug time limit after {$limit} seconds.\n\n".
+ "STACK TRACE\n".
+ implode("\n", $frames));
+ }
+ }
+
+
/* -( In Case of Apocalypse )---------------------------------------------- */
diff --git a/webroot/index.php b/webroot/index.php
--- a/webroot/index.php
+++ b/webroot/index.php
@@ -16,6 +16,12 @@
PhabricatorStartup::loadCoreLibraries();
PhabricatorEnv::initializeWebEnvironment();
+
+ $debug_time_limit = PhabricatorEnv::getEnvConfig('debug.time-limit');
+ if ($debug_time_limit) {
+ PhabricatorStartup::setDebugTimeLimit($debug_time_limit);
+ }
+
$show_unexpected_traces = PhabricatorEnv::getEnvConfig(
'phabricator.developer-mode');
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Oct 30, 1:42 PM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6718676
Default Alt Text
D10465.diff (5 KB)
Attached To
Mode
D10465: Add an option to make it easier to debug page hangs
Attached
Detach File
Event Timeline
Log In to Comment