4 * @phutil-external-symbol function xhprof_enable
5 * @phutil-external-symbol function xhprof_disable
7 final class DarkConsoleXHProfPluginAPI
extends Phobject
{
9 private static $profilerStarted;
10 private static $profilerRunning;
11 private static $profileFilePHID;
13 public static function isProfilerAvailable() {
14 return extension_loaded('xhprof');
17 public static function getProfilerHeader() {
18 return 'X-Phabricator-Profiler';
21 public static function isProfilerRequested() {
22 if (!empty($_REQUEST['__profile__'])) {
23 return $_REQUEST['__profile__'];
26 $header = AphrontRequest
::getHTTPHeader(self
::getProfilerHeader());
34 private static function shouldStartProfiler() {
35 if (self
::isProfilerRequested()) {
39 static $sample_request = null;
41 if ($sample_request === null) {
42 if (PhabricatorEnv
::getEnvConfig('debug.profile-rate')) {
43 $rate = PhabricatorEnv
::getEnvConfig('debug.profile-rate');
44 if (mt_rand(1, $rate) == 1) {
45 $sample_request = true;
47 $sample_request = false;
52 return $sample_request;
55 public static function isProfilerStarted() {
56 return self
::$profilerStarted;
59 private static function isProfilerRunning() {
60 return self
::$profilerRunning;
63 public static function includeXHProfLib() {
64 // TODO: this is incredibly stupid, but we may not have Phutil metamodule
65 // stuff loaded yet so we can't just phutil_get_library_root() our way
68 for ($ii = 0; $ii < 6; $ii++
) {
69 $root = dirname($root);
72 require_once $root.'/externals/xhprof/xhprof_lib.php';
76 public static function saveProfilerSample(PhutilDeferredLog
$access_log) {
77 $file_phid = self
::getProfileFilePHID();
82 if (self
::isProfilerRequested()) {
85 $sample_rate = PhabricatorEnv
::getEnvConfig('debug.profile-rate');
88 $profile_sample = id(new PhabricatorXHProfSample())
89 ->setFilePHID($file_phid)
90 ->setSampleRate($sample_rate)
91 ->setUsTotal($access_log->getData('T'))
92 ->setHostname($access_log->getData('h'))
93 ->setRequestPath($access_log->getData('U'))
94 ->setController($access_log->getData('C'))
95 ->setUserPHID($access_log->getData('P'));
97 AphrontWriteGuard
::allowDangerousUnguardedWrites(true);
100 $profile_sample->save();
101 } catch (Exception
$ex) {
104 AphrontWriteGuard
::allowDangerousUnguardedWrites(false);
111 public static function hookProfiler() {
112 if (!self
::shouldStartProfiler()) {
116 if (!self
::isProfilerAvailable()) {
120 if (self
::$profilerStarted) {
124 self
::startProfiler();
129 * @phutil-external-symbol class PhabricatorStartup
131 private static function startProfiler() {
132 PhabricatorStartup
::beginStartupPhase('profiler.init');
134 self
::includeXHProfLib();
137 self
::$profilerStarted = true;
138 self
::$profilerRunning = true;
143 * @phutil-external-symbol class PhabricatorStartup
145 public static function getProfileFilePHID() {
146 if (!self
::isProfilerRunning()) {
150 PhabricatorStartup
::beginStartupPhase('profiler.stop');
151 self
::stopProfiler();
152 PhabricatorStartup
::beginStartupPhase('profiler.done');
154 return self
::$profileFilePHID;
157 private static function stopProfiler() {
159 $data = xhprof_disable();
160 $data = @json_encode
($data);
161 self
::$profilerRunning = false;
163 // Since these happen on GET we can't do guarded writes. These also
164 // sometimes happen after we've disposed of the write guard; in this
165 // case we need to disable the whole mechanism.
167 $use_scope = AphrontWriteGuard
::isGuardActive();
169 $unguarded = AphrontWriteGuard
::beginScopedUnguardedWrites();
171 AphrontWriteGuard
::allowDangerousUnguardedWrites(true);
176 $file = call_user_func(
177 array('PhabricatorFile', 'newFromFileData'),
180 'mime-type' => 'application/xhprof',
181 'name' => 'profile.xhprof',
183 } catch (Exception
$ex) {
190 AphrontWriteGuard
::allowDangerousUnguardedWrites(false);
197 self
::$profileFilePHID = $file->getPHID();