@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 200 lines 4.8 kB view raw
1<?php 2 3/** 4 * @phutil-external-symbol function xhprof_enable 5 * @phutil-external-symbol function xhprof_disable 6 */ 7final class DarkConsoleXHProfPluginAPI extends Phobject { 8 9 private static $profilerStarted; 10 private static $profilerRunning; 11 private static $profileFilePHID; 12 13 public static function isProfilerAvailable() { 14 return extension_loaded('xhprof'); 15 } 16 17 public static function getProfilerHeader() { 18 return 'X-Phabricator-Profiler'; 19 } 20 21 public static function isProfilerRequested() { 22 if (!empty($_REQUEST['__profile__'])) { 23 return $_REQUEST['__profile__']; 24 } 25 26 $header = AphrontRequest::getHTTPHeader(self::getProfilerHeader()); 27 if ($header) { 28 return $header; 29 } 30 31 return false; 32 } 33 34 private static function shouldStartProfiler() { 35 if (self::isProfilerRequested()) { 36 return true; 37 } 38 39 static $sample_request = null; 40 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; 46 } else { 47 $sample_request = false; 48 } 49 } 50 } 51 52 return $sample_request; 53 } 54 55 public static function isProfilerStarted() { 56 return self::$profilerStarted; 57 } 58 59 private static function isProfilerRunning() { 60 return self::$profilerRunning; 61 } 62 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 66 // to victory. 67 $root = __FILE__; 68 for ($ii = 0; $ii < 6; $ii++) { 69 $root = dirname($root); 70 } 71 72 require_once $root.'/externals/xhprof/xhprof_lib.php'; 73 } 74 75 76 public static function saveProfilerSample(PhutilDeferredLog $access_log) { 77 $file_phid = self::getProfileFilePHID(); 78 if (!$file_phid) { 79 return; 80 } 81 82 if (self::isProfilerRequested()) { 83 $sample_rate = 0; 84 } else { 85 $sample_rate = PhabricatorEnv::getEnvConfig('debug.profile-rate'); 86 } 87 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')); 96 97 AphrontWriteGuard::allowDangerousUnguardedWrites(true); 98 $caught = null; 99 try { 100 $profile_sample->save(); 101 } catch (Exception $ex) { 102 $caught = $ex; 103 } 104 AphrontWriteGuard::allowDangerousUnguardedWrites(false); 105 106 if ($caught) { 107 throw $caught; 108 } 109 } 110 111 public static function hookProfiler() { 112 if (!self::shouldStartProfiler()) { 113 return; 114 } 115 116 if (!self::isProfilerAvailable()) { 117 return; 118 } 119 120 if (self::$profilerStarted) { 121 return; 122 } 123 124 self::startProfiler(); 125 } 126 127 128 /** 129 * @phutil-external-symbol class PhabricatorStartup 130 */ 131 private static function startProfiler() { 132 PhabricatorStartup::beginStartupPhase('profiler.init'); 133 134 self::includeXHProfLib(); 135 xhprof_enable(); 136 137 self::$profilerStarted = true; 138 self::$profilerRunning = true; 139 } 140 141 142 /** 143 * @phutil-external-symbol class PhabricatorStartup 144 */ 145 public static function getProfileFilePHID() { 146 if (!self::isProfilerRunning()) { 147 return; 148 } 149 150 PhabricatorStartup::beginStartupPhase('profiler.stop'); 151 self::stopProfiler(); 152 PhabricatorStartup::beginStartupPhase('profiler.done'); 153 154 return self::$profileFilePHID; 155 } 156 157 private static function stopProfiler() { 158 159 $data = xhprof_disable(); 160 $data = @json_encode($data); 161 self::$profilerRunning = false; 162 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. 166 167 $use_scope = AphrontWriteGuard::isGuardActive(); 168 if ($use_scope) { 169 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 170 } else { 171 AphrontWriteGuard::allowDangerousUnguardedWrites(true); 172 } 173 174 $caught = null; 175 try { 176 $file = call_user_func( 177 array('PhabricatorFile', 'newFromFileData'), 178 $data, 179 array( 180 'mime-type' => 'application/xhprof', 181 'name' => 'profile.xhprof', 182 )); 183 } catch (Exception $ex) { 184 $caught = $ex; 185 } 186 187 if ($use_scope) { 188 unset($unguarded); 189 } else { 190 AphrontWriteGuard::allowDangerousUnguardedWrites(false); 191 } 192 193 if ($caught) { 194 throw $caught; 195 } 196 197 self::$profileFilePHID = $file->getPHID(); 198 } 199 200}