diff --git a/src/aphront/console/plugin/DarkConsoleXHProfPlugin.php b/src/aphront/console/plugin/DarkConsoleXHProfPlugin.php
index 4574056deb..0cae8541d2 100644
--- a/src/aphront/console/plugin/DarkConsoleXHProfPlugin.php
+++ b/src/aphront/console/plugin/DarkConsoleXHProfPlugin.php
@@ -1,106 +1,105 @@
getData();
if ($data['xhprofID']) {
return '#ff00ff';
}
return null;
}
public function getDescription() {
return 'Provides detailed PHP profiling information through XHProf.';
}
public function generateData() {
return array(
'xhprofID' => $this->xhprofID,
'profileURI' => (string)$this
->getRequestURI()
->alter('__profile__', 'page'),
);
}
public function getXHProfRunID() {
return $this->xhprofID;
}
public function renderPanel() {
$data = $this->getData();
$run = $data['xhprofID'];
$profile_uri = $data['profileURI'];
if (!DarkConsoleXHProfPluginAPI::isProfilerAvailable()) {
$href = PhabricatorEnv::getDoclink('article/Installation_Guide.html');
$install_guide = phutil_tag(
'a',
array(
'href' => $href,
'class' => 'bright-link',
),
'Installation Guide');
return
'
'.
'The "xhprof" PHP extension is not available. Install xhprof '.
'to enable the XHProf console plugin. You can find instructions in '.
'the '.$install_guide.'.'.
'
';
}
$result = array();
$header =
'';
$result[] = $header;
if ($run) {
$result[] =
''.
'Profiling was not enabled for this page. Use the button above '.
'to enable it.'.
'
';
}
return implode("\n", $result);
}
public function willShutdown() {
- if (DarkConsoleXHProfPluginAPI::isProfilerRequested() &&
- (DarkConsoleXHProfPluginAPI::isProfilerRequested() !== 'all')) {
+ if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) {
$this->xhprofID = DarkConsoleXHProfPluginAPI::stopProfiler();
}
}
}
diff --git a/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php b/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php
index e2f9cad21d..afad7f1b8e 100644
--- a/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php
+++ b/src/aphront/console/plugin/xhprof/DarkConsoleXHProfPluginAPI.php
@@ -1,127 +1,171 @@
setFilePHID($profile);
+
+ if (self::isProfilerRequested()) {
+ $sample_rate = 0;
+ } else {
+ $sample_rate = PhabricatorEnv::getEnvConfig('debug.profile-rate');
+ }
+
+ $profile_sample->setSampleRate($sample_rate);
+
+ if ($access_log) {
+ $profile_sample
+ ->setUsTotal($access_log->getData('T'))
+ ->setHostname($access_log->getData('h'))
+ ->setRequestPath($access_log->getData('U'))
+ ->setController($access_log->getData('C'))
+ ->setUserPHID($request->getUser()->getPHID());
+ }
+
+ $profile_sample->save();
+ }
+
public static function hookProfiler() {
- if (!self::isProfilerRequested()) {
+ if (!self::shouldStartProfiler()) {
return;
}
if (!self::isProfilerAvailable()) {
return;
}
if (self::$profilerStarted) {
return;
}
self::startProfiler();
self::$profilerStarted = true;
}
public static function startProfiler() {
self::includeXHProfLib();
xhprof_enable();
}
public static function stopProfiler() {
- if (self::$profilerStarted) {
- $data = xhprof_disable();
- $data = serialize($data);
- $file_class = 'PhabricatorFile';
-
- // Since these happen on GET we can't do guarded writes. These also
- // sometimes happen after we've disposed of the write guard; in this
- // case we need to disable the whole mechanism.
-
- $use_scope = AphrontWriteGuard::isGuardActive();
- if ($use_scope) {
- $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- } else {
- AphrontWriteGuard::allowDangerousUnguardedWrites(true);
- }
+ if (!self::isProfilerStarted()) {
+ return null;
+ }
- $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;
- }
+ $data = xhprof_disable();
+ $data = serialize($data);
+ $file_class = 'PhabricatorFile';
- if ($use_scope) {
- unset($unguarded);
- } else {
- AphrontWriteGuard::allowDangerousUnguardedWrites(false);
- }
+ // Since these happen on GET we can't do guarded writes. These also
+ // sometimes happen after we've disposed of the write guard; in this
+ // case we need to disable the whole mechanism.
- if ($caught) {
- throw $caught;
- } else {
- return $file->getPHID();
- }
+ $use_scope = AphrontWriteGuard::isGuardActive();
+ if ($use_scope) {
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
} else {
- return null;
+ AphrontWriteGuard::allowDangerousUnguardedWrites(true);
+ }
+
+ $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();
}
}
}
diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php
index ea9cc314da..80c7a37f5d 100644
--- a/src/view/page/PhabricatorStandardPageView.php
+++ b/src/view/page/PhabricatorStandardPageView.php
@@ -1,393 +1,393 @@
applicationMenu = $application_menu;
return $this;
}
public function getApplicationMenu() {
return $this->applicationMenu;
}
public function setApplicationName($application_name) {
$this->applicationName = $application_name;
return $this;
}
public function setDisableConsole($disable) {
$this->disableConsole = $disable;
return $this;
}
public function getApplicationName() {
return $this->applicationName;
}
public function setBaseURI($base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function getBaseURI() {
return $this->baseURI;
}
public function setShowChrome($show_chrome) {
$this->showChrome = $show_chrome;
return $this;
}
public function getShowChrome() {
return $this->showChrome;
}
public function setSearchDefaultScope($search_default_scope) {
$this->searchDefaultScope = $search_default_scope;
return $this;
}
public function getSearchDefaultScope() {
return $this->searchDefaultScope;
}
public function appendPageObjects(array $objs) {
foreach ($objs as $obj) {
$this->pageObjects[] = $obj;
}
}
public function getTitle() {
$use_glyph = true;
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user && $user->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_TITLES) !== 'glyph') {
$use_glyph = false;
}
}
return ($use_glyph ?
$this->getGlyph() : '['.$this->getApplicationName().']').
' '.parent::getTitle();
}
protected function willRenderPage() {
parent::willRenderPage();
if (!$this->getRequest()) {
throw new Exception(
"You must set the Request to render a PhabricatorStandardPageView.");
}
$console = $this->getConsole();
require_celerity_resource('phabricator-core-css');
require_celerity_resource('phabricator-zindex-css');
require_celerity_resource('phabricator-core-buttons-css');
require_celerity_resource('sprite-gradient-css');
require_celerity_resource('phabricator-standard-page-view');
Javelin::initBehavior('workflow', array());
$request = $this->getRequest();
$user = null;
if ($request) {
$user = $request->getUser();
}
if ($user) {
$default_img_uri =
PhabricatorEnv::getCDNURI(
'/rsrc/image/icon/fatcow/document_black.png'
);
$download_form = phabricator_form(
$user,
array(
'action' => '#',
'method' => 'POST',
'class' => 'lightbox-download-form',
'sigil' => 'download',
),
phutil_tag(
'button',
array(),
pht('Download')));
Javelin::initBehavior(
'lightbox-attachments',
array(
'defaultImageUri' => $default_img_uri,
'downloadForm' => $download_form,
));
}
Javelin::initBehavior('aphront-form-disable-on-submit');
Javelin::initBehavior('toggle-class', array());
Javelin::initBehavior('konami', array());
$current_token = null;
if ($user) {
$current_token = $user->getCSRFToken();
}
Javelin::initBehavior(
'refresh-csrf',
array(
'tokenName' => AphrontRequest::getCSRFTokenName(),
'header' => AphrontRequest::getCSRFHeaderName(),
'current' => $current_token,
));
Javelin::initBehavior('device');
if ($console) {
require_celerity_resource('aphront-dark-console-css');
$headers = array();
- if (DarkConsoleXHProfPluginAPI::isProfilerRequested()) {
+ if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) {
$headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page';
}
Javelin::initBehavior(
'dark-console',
array(
'uri' => $request ? (string)$request->getRequestURI() : '?',
'selected' => $user ? $user->getConsoleTab() : null,
'visible' => $user ? (int)$user->getConsoleVisible() : true,
'headers' => $headers,
));
// Change this to initBehavior when there is some behavior to initialize
require_celerity_resource('javelin-behavior-error-log');
}
$menu = id(new PhabricatorMainMenuView())
->setUser($request->getUser())
->setDefaultSearchScope($this->getSearchDefaultScope());
if ($this->getController()) {
$menu->setController($this->getController());
}
if ($this->getApplicationMenu()) {
$menu->setApplicationMenu($this->getApplicationMenu());
}
$this->menuContent = $menu->render();
}
protected function getHead() {
$monospaced = PhabricatorEnv::getEnvConfig('style.monospace');
$request = $this->getRequest();
if ($request) {
$user = $request->getUser();
if ($user) {
$monospaced = nonempty(
$user->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_MONOSPACED),
$monospaced);
}
}
$response = CelerityAPI::getStaticResourceResponse();
$head = array(
parent::getHead(),
'',
$response->renderSingleResource('javelin-magical-init'),
);
return implode("\n", $head);
}
public function setGlyph($glyph) {
$this->glyph = $glyph;
return $this;
}
public function getGlyph() {
return $this->glyph;
}
protected function willSendResponse($response) {
$request = $this->getRequest();
$response = parent::willSendResponse($response);
$console = $request->getApplicationConfiguration()->getConsole();
if ($console) {
$response = str_replace(
''.
($console ? '
' : null).
parent::getBody().
'
'.
'
');
}
protected function getTail() {
$request = $this->getRequest();
$user = $request->getUser();
$container = null;
if ($user->isLoggedIn()) {
$aphlict_object_id = celerity_generate_unique_node_id();
$aphlict_container_id = celerity_generate_unique_node_id();
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
$client_uri = new PhutilURI($client_uri);
if ($client_uri->getDomain() == 'localhost') {
$this_host = $this->getRequest()->getHost();
$this_host = new PhutilURI('http://'.$this_host.'/');
$client_uri->setDomain($this_host->getDomain());
}
$enable_debug = PhabricatorEnv::getEnvConfig('notification.debug');
Javelin::initBehavior(
'aphlict-listen',
array(
'id' => $aphlict_object_id,
'containerID' => $aphlict_container_id,
'server' => $client_uri->getDomain(),
'port' => $client_uri->getPort(),
'debug' => $enable_debug,
'pageObjects' => array_fill_keys($this->pageObjects, true),
));
$container = phutil_tag(
'div',
array(
'id' => $aphlict_container_id,
'style' => 'position: absolute; width: 0; height: 0;',
),
'');
}
$response = CelerityAPI::getStaticResourceResponse();
$tail = array(
parent::getTail(),
$container,
$response->renderHTMLFooter(),
);
return implode("\n", $tail);
}
protected function getBodyClasses() {
$classes = array();
if (!$this->getShowChrome()) {
$classes[] = 'phabricator-chromeless-page';
}
$agent = AphrontRequest::getHTTPHeader('User-Agent');
// Try to guess the device resolution based on UA strings to avoid a flash
// of incorrectly-styled content.
$device_guess = 'device-desktop';
if (preg_match('@iPhone|iPod|(Android.*Chrome/[.0-9]* Mobile)@', $agent)) {
$device_guess = 'device-phone device';
} else if (preg_match('@iPad|(Android.*Chrome/)@', $agent)) {
$device_guess = 'device-tablet device';
}
$classes[] = $device_guess;
return implode(' ', $classes);
}
private function getConsole() {
if ($this->disableConsole) {
return null;
}
return $this->getRequest()->getApplicationConfiguration()->getConsole();
}
}
diff --git a/webroot/index.php b/webroot/index.php
index 38e776f241..c6af0494f7 100644
--- a/webroot/index.php
+++ b/webroot/index.php
@@ -1,158 +1,140 @@
setData(
array(
'R' => AphrontRequest::getHTTPHeader('Referer', '-'),
'r' => idx($_SERVER, 'REMOTE_ADDR', '-'),
'M' => idx($_SERVER, 'REQUEST_METHOD', '-'),
));
}
DarkConsoleXHProfPluginAPI::hookProfiler();
PhutilErrorHandler::setErrorListener(
array('DarkConsoleErrorLogPluginAPI', 'handleErrors'));
$sink = new AphrontPHPHTTPSink();
$response = PhabricatorSetupCheck::willProcessRequest();
if ($response) {
$sink->writeResponse($response);
return;
}
$host = AphrontRequest::getHTTPHeader('Host');
$path = $_REQUEST['__path__'];
switch ($host) {
default:
$config_key = 'aphront.default-application-configuration-class';
$application = PhabricatorEnv::newObjectFromConfig($config_key);
break;
}
$application->setHost($host);
$application->setPath($path);
$application->willBuildRequest();
$request = $application->buildRequest();
// Until an administrator sets "phabricator.base-uri", assume it is the same
// as the request URI. This will work fine in most cases, it just breaks down
// when daemons need to do things.
$request_protocol = ($request->isHTTPS() ? 'https' : 'http');
$request_base_uri = "{$request_protocol}://{$host}/";
PhabricatorEnv::setRequestBaseURI($request_base_uri);
$write_guard = new AphrontWriteGuard(array($request, 'validateCSRF'));
$application->setRequest($request);
list($controller, $uri_data) = $application->buildController();
if ($access_log) {
$access_log->setData(
array(
'U' => (string)$request->getRequestURI()->getPath(),
'C' => get_class($controller),
));
}
// If execution throws an exception and then trying to render that exception
// throws another exception, we want to show the original exception, as it is
// likely the root cause of the rendering exception.
$original_exception = null;
try {
$response = $controller->willBeginExecution();
if ($access_log) {
if ($request->getUser() && $request->getUser()->getPHID()) {
$access_log->setData(
array(
'u' => $request->getUser()->getUserName(),
));
}
}
if (!$response) {
$controller->willProcessRequest($uri_data);
$response = $controller->processRequest();
}
} catch (AphrontRedirectException $ex) {
$response = id(new AphrontRedirectResponse())
->setURI($ex->getURI());
} catch (Exception $ex) {
$original_exception = $ex;
$response = $application->handleException($ex);
}
try {
$response = $controller->didProcessRequest($response);
$response = $application->willSendResponse($response, $controller);
$response->setRequest($request);
$sink->writeResponse($response);
} catch (Exception $ex) {
$write_guard->dispose();
if ($access_log) {
$access_log->write();
}
if ($original_exception) {
$ex = new PhutilAggregateException(
"Multiple exceptions during processing and rendering.",
array(
$original_exception,
$ex,
));
}
PhabricatorStartup::didFatal('[Rendering Exception] '.$ex->getMessage());
}
$write_guard->dispose();
if ($access_log) {
$request_start = PhabricatorStartup::getStartTime();
$access_log->setData(
array(
'c' => $response->getHTTPResponseCode(),
'T' => (int)(1000000 * (microtime(true) - $request_start)),
));
$access_log->write();
}
- if (DarkConsoleXHProfPluginAPI::isProfilerRequested()) {
- $profile = DarkConsoleXHProfPluginAPI::stopProfiler();
- $profile_sample = id(new PhabricatorXHProfSample())
- ->setFilePHID($profile);
- if (empty($_REQUEST['__profile__'])) {
- $sample_rate = PhabricatorEnv::getEnvConfig('debug.profile-rate');
- } else {
- $sample_rate = 0;
- }
- $profile_sample->setSampleRate($sample_rate);
- if ($access_log) {
- $profile_sample->setUsTotal($access_log->getData('T'))
- ->setHostname($access_log->getData('h'))
- ->setRequestPath($access_log->getData('U'))
- ->setController($access_log->getData('C'))
- ->setUserPHID($request->getUser()->getPHID());
- }
- $profile_sample->save();
- }
+ DarkConsoleXHProfPluginAPI::saveProfilerSample($request, $access_log);
} catch (Exception $ex) {
PhabricatorStartup::didFatal("[Exception] ".$ex->getMessage());
}