Do sampled profiling of requests
Summary: People have occasionally complained about phabricator being slow. We have the access log to look at to see when slowness happens, but it doesn't tell us much about why it happened. Since it's usually a sporadic issue that's reported, it's hard to reproduce and then profile. This change will allow us to collect sampled profiles so we can look at them when slowness occurs. Test Plan: checking that sampling works correctly: - set rate to 0; do several page loads; check no new entries in table - set rate to 1; check that there's a new row in the table for each page load - set rate to 10; check that some requests write to table and some don't check new ui for samples: - load /xhprof/list/all/, see a list with a lot of samples - load /xhprof/list/sampled/, see only sampled runs - load /xhprof/list/manual/, see only non-sampled runs - load /xhprof/list/my-runs/, se only my manual runs Reviewers: vrana, epriestley Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D3458
This commit is contained in:
@@ -1116,10 +1116,14 @@ phutil_register_library_map(array(
|
||||
'PhabricatorXHPASTViewStreamController' => 'applications/xhpastview/controller/PhabricatorXHPASTViewStreamController.php',
|
||||
'PhabricatorXHPASTViewTreeController' => 'applications/xhpastview/controller/PhabricatorXHPASTViewTreeController.php',
|
||||
'PhabricatorXHProfController' => 'applications/xhprof/controller/PhabricatorXHProfController.php',
|
||||
'PhabricatorXHProfDAO' => 'applications/xhprof/storage/PhabricatorXHProfDAO.php',
|
||||
'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/PhabricatorXHProfProfileController.php',
|
||||
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/PhabricatorXHProfProfileSymbolView.php',
|
||||
'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/PhabricatorXHProfProfileTopLevelView.php',
|
||||
'PhabricatorXHProfProfileView' => 'applications/xhprof/view/PhabricatorXHProfProfileView.php',
|
||||
'PhabricatorXHProfSample' => 'applications/xhprof/storage/PhabricatorXHProfSample.php',
|
||||
'PhabricatorXHProfSampleListController' => 'applications/xhprof/controller/PhabricatorXHProfSampleListController.php',
|
||||
'PhabricatorXHProfSampleListView' => 'applications/xhprof/view/PhabricatorXHProfSampleListView.php',
|
||||
'PhameAllBlogListController' => 'applications/phame/controller/blog/list/PhameAllBlogListController.php',
|
||||
'PhameAllPostListController' => 'applications/phame/controller/post/list/PhameAllPostListController.php',
|
||||
'PhameBlog' => 'applications/phame/storage/PhameBlog.php',
|
||||
@@ -2205,10 +2209,14 @@ phutil_register_library_map(array(
|
||||
'PhabricatorXHPASTViewStreamController' => 'PhabricatorXHPASTViewPanelController',
|
||||
'PhabricatorXHPASTViewTreeController' => 'PhabricatorXHPASTViewPanelController',
|
||||
'PhabricatorXHProfController' => 'PhabricatorController',
|
||||
'PhabricatorXHProfDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController',
|
||||
'PhabricatorXHProfProfileSymbolView' => 'PhabricatorXHProfProfileView',
|
||||
'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView',
|
||||
'PhabricatorXHProfProfileView' => 'AphrontView',
|
||||
'PhabricatorXHProfSample' => 'PhabricatorXHProfDAO',
|
||||
'PhabricatorXHProfSampleListController' => 'PhabricatorXHProfController',
|
||||
'PhabricatorXHProfSampleListView' => 'AphrontView',
|
||||
'PhameAllBlogListController' => 'PhameBlogListBaseController',
|
||||
'PhameAllPostListController' => 'PhamePostListBaseController',
|
||||
'PhameBlog' => 'PhameDAO',
|
||||
|
||||
@@ -126,6 +126,7 @@ class AphrontDefaultApplicationConfiguration
|
||||
),
|
||||
|
||||
'/xhprof/' => array(
|
||||
'list/(?P<view>[^/]+)/' => 'PhabricatorXHProfSampleListController',
|
||||
'profile/(?P<phid>[^/]+)/' => 'PhabricatorXHProfProfileController',
|
||||
),
|
||||
|
||||
|
||||
@@ -34,11 +34,20 @@ final class DarkConsoleXHProfPluginAPI {
|
||||
return $_REQUEST['__profile__'];
|
||||
}
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('debug.profile-every-request')) {
|
||||
return PhabricatorEnv::getEnvConfig('debug.profile-every-request');
|
||||
static $profilerRequested = null;
|
||||
|
||||
if (!isset($profilerRequested)) {
|
||||
if (PhabricatorEnv::getEnvConfig('debug.profile-rate')) {
|
||||
$rate = PhabricatorEnv::getEnvConfig('debug.profile-rate');
|
||||
if (mt_rand(1, $rate) == 1) {
|
||||
$profilerRequested = true;
|
||||
} else {
|
||||
$profilerRequested = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $profilerRequested;
|
||||
}
|
||||
|
||||
public static function includeXHProfLib() {
|
||||
@@ -73,9 +82,7 @@ final class DarkConsoleXHProfPluginAPI {
|
||||
|
||||
public static function startProfiler() {
|
||||
self::includeXHProfLib();
|
||||
// Note: HPHP's implementation of XHProf currently requires an argument
|
||||
// to xhprof_enable() -- see Facebook Task #531011.
|
||||
xhprof_enable(0);
|
||||
xhprof_enable();
|
||||
}
|
||||
|
||||
public static function stopProfiler() {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorXHProfSampleListController
|
||||
extends PhabricatorXHProfController {
|
||||
|
||||
private $view;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->view = $data['view'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
|
||||
$pager = new AphrontPagerView();
|
||||
$pager->setOffset($request->getInt('page'));
|
||||
|
||||
switch ($this->view) {
|
||||
case 'sampled':
|
||||
$clause = '`sampleRate` > 0';
|
||||
$show_type = false;
|
||||
break;
|
||||
case 'my-runs':
|
||||
$clause = qsprintf(
|
||||
id(new PhabricatorXHProfSample())->establishConnection('r'),
|
||||
'`sampleRate` = 0 AND `userPHID` = %s',
|
||||
$request->getUser()->getPHID());
|
||||
$show_type = false;
|
||||
break;
|
||||
case 'manual':
|
||||
$clause = '`sampleRate` = 0';
|
||||
$show_type = false;
|
||||
break;
|
||||
case 'all':
|
||||
default:
|
||||
$clause = '1 = 1';
|
||||
$show_type = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$samples = id(new PhabricatorXHProfSample())->loadAllWhere(
|
||||
'%Q ORDER BY dateCreated DESC LIMIT %d, %d',
|
||||
$clause,
|
||||
$pager->getOffset(),
|
||||
$pager->getPageSize() + 1);
|
||||
|
||||
$samples = $pager->sliceResults($samples);
|
||||
$pager->setURI($request->getRequestURI(), 'page');
|
||||
|
||||
$table = new PhabricatorXHProfSampleListView();
|
||||
$table->setUser($request->getUser());
|
||||
$table->setSamples($samples);
|
||||
$table->setShowType($show_type);
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader('XHProf Samples');
|
||||
$panel->appendChild($table);
|
||||
$panel->appendChild($pager);
|
||||
|
||||
return $this->buildStandardPageResponse(
|
||||
$panel,
|
||||
array('title' => 'XHProf Samples'));
|
||||
|
||||
}
|
||||
}
|
||||
24
src/applications/xhprof/storage/PhabricatorXHProfDAO.php
Normal file
24
src/applications/xhprof/storage/PhabricatorXHProfDAO.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
abstract class PhabricatorXHProfDAO extends PhabricatorLiskDAO {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'xhprof';
|
||||
}
|
||||
}
|
||||
28
src/applications/xhprof/storage/PhabricatorXHProfSample.php
Normal file
28
src/applications/xhprof/storage/PhabricatorXHProfSample.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorXHProfSample extends PhabricatorXHProfDAO {
|
||||
|
||||
protected $filePHID;
|
||||
protected $usTotal;
|
||||
protected $sampleRate;
|
||||
protected $hostname;
|
||||
protected $requestPath;
|
||||
protected $controller;
|
||||
protected $userPHID;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorXHProfSampleListView extends AphrontView {
|
||||
|
||||
private $samples;
|
||||
private $user;
|
||||
private $showType = false;
|
||||
|
||||
public function setSamples(array $samples) {
|
||||
assert_instances_of($samples, 'PhabricatorXHProfSample');
|
||||
$this->samples = $samples;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUser(PhabricatorUser $user) {
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setShowType($show_type) {
|
||||
$this->showType = $show_type;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$rows = array();
|
||||
|
||||
if (!$this->user) {
|
||||
throw new Exception("Call setUser() before rendering!");
|
||||
}
|
||||
|
||||
$user_phids = mpull($this->samples, 'getUserPHID');
|
||||
$users = id(new PhabricatorObjectHandleData($user_phids))->loadObjects();
|
||||
foreach ($this->samples as $sample) {
|
||||
$sample_link = phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/xhprof/profile/'.$sample->getFilePHID().'/',
|
||||
),
|
||||
$sample->getFilePHID());
|
||||
if ($this->showType) {
|
||||
if ($sample->getSampleRate() == 0) {
|
||||
$sample_link .= ' (manual run)';
|
||||
} else {
|
||||
$sample_link .= ' (sampled)';
|
||||
}
|
||||
}
|
||||
$rows[] = array(
|
||||
$sample_link,
|
||||
phabricator_datetime($sample->getDateCreated(), $this->user),
|
||||
number_format($sample->getUsTotal())." \xCE\xBCs",
|
||||
$sample->getHostname(),
|
||||
$sample->getRequestPath(),
|
||||
$sample->getController(),
|
||||
idx($users, $sample->getUserPHID()),
|
||||
);
|
||||
}
|
||||
|
||||
$table = new AphrontTableView($rows);
|
||||
$table->setHeaders(
|
||||
array(
|
||||
'Sample',
|
||||
'Date',
|
||||
'Wall Time',
|
||||
'Hostname',
|
||||
'Request Path',
|
||||
'Controller',
|
||||
'User',
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'',
|
||||
'right',
|
||||
'wide wrap',
|
||||
'',
|
||||
'',
|
||||
));
|
||||
|
||||
return $table->render();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -979,6 +979,9 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
||||
'pastepolicy.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('pastepolicy.sql'),
|
||||
'xhprof.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('xhprof.sql'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user