Skip to content

Commit 4b1815d

Browse files
author
epriestley
committedAug 21, 2015
Add a "Startup" to DarkConsole
Summary: Ref T8588. It looks like something slow is happening //before// we start DarkConsole. Add some crude reporting to try to narrow it down. Test Plan: {F743050} Reviewers: chad Reviewed By: chad Maniphest Tasks: T8588 Differential Revision: https://secure.phabricator.com/D13956
1 parent d825aae commit 4b1815d

File tree

7 files changed

+219
-15
lines changed

7 files changed

+219
-15
lines changed
 

‎src/__phutil_library_map__.php

+2
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@
286286
'DarkConsolePlugin' => 'applications/console/plugin/DarkConsolePlugin.php',
287287
'DarkConsoleRequestPlugin' => 'applications/console/plugin/DarkConsoleRequestPlugin.php',
288288
'DarkConsoleServicesPlugin' => 'applications/console/plugin/DarkConsoleServicesPlugin.php',
289+
'DarkConsoleStartupPlugin' => 'applications/console/plugin/DarkConsoleStartupPlugin.php',
289290
'DarkConsoleXHProfPlugin' => 'applications/console/plugin/DarkConsoleXHProfPlugin.php',
290291
'DarkConsoleXHProfPluginAPI' => 'applications/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php',
291292
'DatabaseConfigurationProvider' => 'infrastructure/storage/configuration/DatabaseConfigurationProvider.php',
@@ -3912,6 +3913,7 @@
39123913
'DarkConsolePlugin' => 'Phobject',
39133914
'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
39143915
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
3916+
'DarkConsoleStartupPlugin' => 'DarkConsolePlugin',
39153917
'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
39163918
'DarkConsoleXHProfPluginAPI' => 'Phobject',
39173919
'DefaultDatabaseConfigurationProvider' => array(

‎src/aphront/configuration/AphrontApplicationConfiguration.php

+8
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public function willBuildRequest() {}
5858
* @phutil-external-symbol class PhabricatorStartup
5959
*/
6060
public static function runHTTPRequest(AphrontHTTPSink $sink) {
61+
PhabricatorStartup::beginStartupPhase('multimeter');
6162
$multimeter = MultimeterControl::newInstance();
6263
$multimeter->setEventContext('<http-init>');
6364
$multimeter->setEventViewer('<none>');
@@ -67,6 +68,7 @@ public static function runHTTPRequest(AphrontHTTPSink $sink) {
6768
// request object first.
6869
$write_guard = new AphrontWriteGuard('id');
6970

71+
PhabricatorStartup::beginStartupPhase('env.init');
7072
PhabricatorEnv::initializeWebEnvironment();
7173

7274
$multimeter->setSampleRate(
@@ -78,6 +80,7 @@ public static function runHTTPRequest(AphrontHTTPSink $sink) {
7880
}
7981

8082
// This is the earliest we can get away with this, we need env config first.
83+
PhabricatorStartup::beginStartupPhase('log.access');
8184
PhabricatorAccessLog::init();
8285
$access_log = PhabricatorAccessLog::getLog();
8386
PhabricatorStartup::setAccessLog($access_log);
@@ -89,6 +92,11 @@ public static function runHTTPRequest(AphrontHTTPSink $sink) {
8992
));
9093

9194
DarkConsoleXHProfPluginAPI::hookProfiler();
95+
96+
// We just activated the profiler, so we don't need to keep track of
97+
// startup phases anymore: it can take over from here.
98+
PhabricatorStartup::beginStartupPhase('startup.done');
99+
92100
DarkConsoleErrorLogPluginAPI::registerErrorHandler();
93101

94102
$response = PhabricatorSetupCheck::willProcessRequest();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
final class DarkConsoleStartupPlugin extends DarkConsolePlugin {
4+
5+
public function getName() {
6+
return pht('Startup');
7+
}
8+
9+
public function getDescription() {
10+
return pht('Timing information about the startup sequence.');
11+
}
12+
13+
/**
14+
* @phutil-external-symbol class PhabricatorStartup
15+
*/
16+
public function generateData() {
17+
return PhabricatorStartup::getPhases();
18+
}
19+
20+
public function renderPanel() {
21+
$data = $this->getData();
22+
23+
// Compute the time offset and duration of each startup phase.
24+
$prev_key = null;
25+
$init = null;
26+
$phases = array();
27+
foreach ($data as $key => $value) {
28+
if ($init === null) {
29+
$init = $value;
30+
}
31+
32+
$offset = (int)floor(1000 * ($value - $init));
33+
34+
$phases[$key] = array(
35+
'time' => $value,
36+
'offset' => $value - $init,
37+
);
38+
39+
40+
if ($prev_key !== null) {
41+
$phases[$prev_key]['duration'] = $value - $phases[$prev_key]['time'];
42+
}
43+
$prev_key = $key;
44+
}
45+
46+
// Render the phases.
47+
$rows = array();
48+
foreach ($phases as $key => $phase) {
49+
$offset_ms = (int)floor(1000 * $phase['offset']);
50+
51+
if (isset($phase['duration'])) {
52+
$duration_us = (int)floor(1000000 * $phase['duration']);
53+
} else {
54+
$duration_us = null;
55+
}
56+
57+
$rows[] = array(
58+
$key,
59+
pht('+%s ms', new PhutilNumber($offset_ms)),
60+
($duration_us === null)
61+
? pht('-')
62+
: pht('%s us', new PhutilNumber($duration_us)),
63+
null,
64+
);
65+
}
66+
67+
return id(new AphrontTableView($rows))
68+
->setHeaders(
69+
array(
70+
pht('Phase'),
71+
pht('Offset'),
72+
pht('Duration'),
73+
null,
74+
))
75+
->setColumnClasses(
76+
array(
77+
'',
78+
'n right',
79+
'n right',
80+
'wide',
81+
));
82+
}
83+
84+
}

‎src/applications/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php

+17-3
Original file line numberDiff line numberDiff line change
@@ -124,23 +124,37 @@ public static function hookProfiler() {
124124
self::startProfiler();
125125
}
126126

127+
128+
/**
129+
* @phutil-external-symbol class PhabricatorStartup
130+
*/
127131
private static function startProfiler() {
132+
PhabricatorStartup::beginStartupPhase('profiler.init');
133+
128134
self::includeXHProfLib();
129135
xhprof_enable();
130136

131137
self::$profilerStarted = true;
132138
self::$profilerRunning = true;
133139
}
134140

141+
142+
/**
143+
* @phutil-external-symbol class PhabricatorStartup
144+
*/
135145
public static function getProfileFilePHID() {
146+
if (!self::isProfilerRunning()) {
147+
return;
148+
}
149+
150+
PhabricatorStartup::beginStartupPhase('profiler.stop');
136151
self::stopProfiler();
152+
PhabricatorStartup::beginStartupPhase('profiler.done');
153+
137154
return self::$profileFilePHID;
138155
}
139156

140157
private static function stopProfiler() {
141-
if (!self::isProfilerRunning()) {
142-
return;
143-
}
144158

145159
$data = xhprof_disable();
146160
$data = @json_encode($data);

‎src/docs/user/field/darkconsole.diviner

+19
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,25 @@ If the services tab looks fine, and particularly if a page is slow but the
138138
tool to understand problems in PHP is XHProf.
139139

140140

141+
Plugin: Startup
142+
===============
143+
144+
The "Startup" plugin shows information about startup phases. This information
145+
can provide insight about performance problems which occur before the profiler
146+
can start.
147+
148+
Normally, the profiler is the best tool for understanding runtime performance,
149+
but some work is performed before the profiler starts (for example, loading
150+
libraries and configuration). If there is a substantial difference between the
151+
wall time reported by the profiler and the "Entire Page" cost reported by the
152+
Services tab, the Startup tab can help account for that time.
153+
154+
It is normal for starting the profiler to increase the cost of the page
155+
somewhat: the profiler itself adds overhead while it is running, and the page
156+
must do some work after the profiler is stopped to save the profile and
157+
complete other shutdown operations.
158+
159+
141160
Plugin: XHProf
142161
==============
143162

‎support/PhabricatorStartup.php

+62-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* @task apocalypse In Case Of Apocalypse
3535
* @task validation Validation
3636
* @task ratelimit Rate Limiting
37+
* @task phases Startup Phase Timers
3738
*/
3839
final class PhabricatorStartup {
3940

@@ -43,6 +44,7 @@ final class PhabricatorStartup {
4344
private static $capturingOutput;
4445
private static $rawInput;
4546
private static $oldMemoryLimit;
47+
private static $phases;
4648

4749
// TODO: For now, disable rate limiting entirely by default. We need to
4850
// iterate on it a bit for Conduit, some of the specific score levels, and
@@ -89,10 +91,14 @@ public static function getRawInput() {
8991

9092

9193
/**
94+
* @param float Request start time, from `microtime(true)`.
9295
* @task hook
9396
*/
94-
public static function didStartup() {
95-
self::$startTime = microtime(true);
97+
public static function didStartup($start_time) {
98+
self::$startTime = $start_time;
99+
100+
self::$phases = array();
101+
96102
self::$accessLog = null;
97103

98104
static $registered;
@@ -854,4 +860,58 @@ private static function didRateLimit() {
854860
exit(1);
855861
}
856862

863+
864+
/* -( Startup Timers )----------------------------------------------------- */
865+
866+
867+
/**
868+
* Record the beginning of a new startup phase.
869+
*
870+
* For phases which occur before @{class:PhabricatorStartup} loads, save the
871+
* time and record it with @{method:recordStartupPhase} after the class is
872+
* available.
873+
*
874+
* @param string Phase name.
875+
* @task phases
876+
*/
877+
public static function beginStartupPhase($phase) {
878+
self::recordStartupPhase($phase, microtime(true));
879+
}
880+
881+
882+
/**
883+
* Record the start time of a previously executed startup phase.
884+
*
885+
* For startup phases which occur after @{class:PhabricatorStartup} loads,
886+
* use @{method:beginStartupPhase} instead. This method can be used to
887+
* record a time before the class loads, then hand it over once the class
888+
* becomes available.
889+
*
890+
* @param string Phase name.
891+
* @param float Phase start time, from `microtime(true)`.
892+
* @task phases
893+
*/
894+
public static function recordStartupPhase($phase, $time) {
895+
self::$phases[$phase] = $time;
896+
}
897+
898+
899+
/**
900+
* Get information about startup phase timings.
901+
*
902+
* Sometimes, performance problems can occur before we start the profiler.
903+
* Since the profiler can't examine these phases, it isn't useful in
904+
* understanding their performance costs.
905+
*
906+
* Instead, the startup process marks when it enters various phases using
907+
* @{method:beginStartupPhase}. A later call to this method can retrieve this
908+
* information, which can be examined to gain greater insight into where
909+
* time was spent. The output is still crude, but better than nothing.
910+
*
911+
* @task phases
912+
*/
913+
public static function getPhases() {
914+
return self::$phases;
915+
}
916+
857917
}

‎webroot/index.php

+27-10
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
<?php
22

3-
$phabricator_root = dirname(dirname(__FILE__));
4-
require_once $phabricator_root.'/support/PhabricatorStartup.php';
5-
6-
// If the preamble script exists, load it.
7-
$preamble_path = $phabricator_root.'/support/preamble.php';
8-
if (file_exists($preamble_path)) {
9-
require_once $preamble_path;
10-
}
11-
12-
PhabricatorStartup::didStartup();
3+
phabricator_startup();
134

145
try {
6+
PhabricatorStartup::beginStartupPhase('libraries');
157
PhabricatorStartup::loadCoreLibraries();
8+
9+
PhabricatorStartup::beginStartupPhase('purge');
1610
PhabricatorCaches::destroyRequestCache();
1711

12+
PhabricatorStartup::beginStartupPhase('sink');
1813
$sink = new AphrontPHPHTTPSink();
1914

2015
try {
16+
PhabricatorStartup::beginStartupPhase('run');
2117
AphrontApplicationConfiguration::runHTTPRequest($sink);
2218
} catch (Exception $ex) {
2319
try {
@@ -36,3 +32,24 @@
3632
} catch (Exception $ex) {
3733
PhabricatorStartup::didEncounterFatalException('Core Exception', $ex, false);
3834
}
35+
36+
function phabricator_startup() {
37+
// Load the PhabricatorStartup class itself.
38+
$t_startup = microtime(true);
39+
$root = dirname(dirname(__FILE__));
40+
require_once $root.'/support/PhabricatorStartup.php';
41+
42+
// If the preamble script exists, load it.
43+
$t_preamble = microtime(true);
44+
$preamble_path = $root.'/support/preamble.php';
45+
if (file_exists($preamble_path)) {
46+
require_once $preamble_path;
47+
}
48+
49+
$t_hook = microtime(true);
50+
PhabricatorStartup::didStartup($t_startup);
51+
52+
PhabricatorStartup::recordStartupPhase('startup.init', $t_startup);
53+
PhabricatorStartup::recordStartupPhase('preamble', $t_preamble);
54+
PhabricatorStartup::recordStartupPhase('hook', $t_hook);
55+
}

0 commit comments

Comments
 (0)
Failed to load comments.