[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class DarkConsoleServicesPlugin extends DarkConsolePlugin { 4 5 protected $observations; 6 7 public function getName() { 8 return 'Services'; 9 } 10 11 public function getDescription() { 12 return 'Information about services.'; 13 } 14 15 public static function getQueryAnalyzerHeader() { 16 return 'X-Phabricator-QueryAnalyzer'; 17 } 18 19 public static function isQueryAnalyzerRequested() { 20 if (!empty($_REQUEST['__analyze__'])) { 21 return true; 22 } 23 24 $header = AphrontRequest::getHTTPHeader(self::getQueryAnalyzerHeader()); 25 if ($header) { 26 return true; 27 } 28 29 return false; 30 } 31 32 /** 33 * @phutil-external-symbol class PhabricatorStartup 34 */ 35 public function generateData() { 36 $should_analyze = self::isQueryAnalyzerRequested(); 37 38 $log = PhutilServiceProfiler::getInstance()->getServiceCallLog(); 39 foreach ($log as $key => $entry) { 40 $config = idx($entry, 'config', array()); 41 unset($log[$key]['config']); 42 43 if (!$should_analyze) { 44 $log[$key]['explain'] = array( 45 'sev' => 7, 46 'size' => null, 47 'reason' => 'Disabled', 48 ); 49 // Query analysis is disabled for this request, so don't do any of it. 50 continue; 51 } 52 53 if ($entry['type'] != 'query') { 54 continue; 55 } 56 57 // For each SELECT query, go issue an EXPLAIN on it so we can flag stuff 58 // causing table scans, etc. 59 if (preg_match('/^\s*SELECT\b/i', $entry['query'])) { 60 $conn = PhabricatorEnv::newObjectFromConfig( 61 'mysql.implementation', 62 array($entry['config'])); 63 try { 64 $explain = queryfx_all( 65 $conn, 66 'EXPLAIN %Q', 67 $entry['query']); 68 69 $badness = 0; 70 $size = 1; 71 $reason = null; 72 73 foreach ($explain as $table) { 74 $size *= (int)$table['rows']; 75 76 switch ($table['type']) { 77 case 'index': 78 $cur_badness = 1; 79 $cur_reason = 'Index'; 80 break; 81 case 'const': 82 $cur_badness = 1; 83 $cur_reason = 'Const'; 84 break; 85 case 'eq_ref'; 86 $cur_badness = 2; 87 $cur_reason = 'EqRef'; 88 break; 89 case 'range': 90 $cur_badness = 3; 91 $cur_reason = 'Range'; 92 break; 93 case 'ref': 94 $cur_badness = 3; 95 $cur_reason = 'Ref'; 96 break; 97 case 'fulltext': 98 $cur_badness = 3; 99 $cur_reason = 'Fulltext'; 100 break; 101 case 'ALL': 102 if (preg_match('/Using where/', $table['Extra'])) { 103 if ($table['rows'] < 256 && !empty($table['possible_keys'])) { 104 $cur_badness = 2; 105 $cur_reason = 'Small Table Scan'; 106 } else { 107 $cur_badness = 6; 108 $cur_reason = 'TABLE SCAN!'; 109 } 110 } else { 111 $cur_badness = 3; 112 $cur_reason = 'Whole Table'; 113 } 114 break; 115 default: 116 if (preg_match('/No tables used/i', $table['Extra'])) { 117 $cur_badness = 1; 118 $cur_reason = 'No Tables'; 119 } else if (preg_match('/Impossible/i', $table['Extra'])) { 120 $cur_badness = 1; 121 $cur_reason = 'Empty'; 122 } else { 123 $cur_badness = 4; 124 $cur_reason = "Can't Analyze"; 125 } 126 break; 127 } 128 129 if ($cur_badness > $badness) { 130 $badness = $cur_badness; 131 $reason = $cur_reason; 132 } 133 } 134 135 $log[$key]['explain'] = array( 136 'sev' => $badness, 137 'size' => $size, 138 'reason' => $reason, 139 ); 140 } catch (Exception $ex) { 141 $log[$key]['explain'] = array( 142 'sev' => 5, 143 'size' => null, 144 'reason' => $ex->getMessage(), 145 ); 146 } 147 } 148 } 149 150 return array( 151 'start' => PhabricatorStartup::getStartTime(), 152 'end' => microtime(true), 153 'log' => $log, 154 'analyzeURI' => (string)$this 155 ->getRequestURI() 156 ->alter('__analyze__', true), 157 'didAnalyze' => $should_analyze, 158 ); 159 } 160 161 public function renderPanel() { 162 $data = $this->getData(); 163 164 $log = $data['log']; 165 $results = array(); 166 167 $results[] = phutil_tag( 168 'div', 169 array('class' => 'dark-console-panel-header'), 170 array( 171 phutil_tag( 172 'a', 173 array( 174 'href' => $data['analyzeURI'], 175 'class' => $data['didAnalyze'] ? 'disabled button' : 'green button', 176 ), 177 pht('Analyze Query Plans')), 178 phutil_tag('h1', array(), pht('Calls to External Services')), 179 phutil_tag('div', array('style' => 'clear: both;')), 180 )); 181 182 $page_total = $data['end'] - $data['start']; 183 $totals = array(); 184 $counts = array(); 185 186 foreach ($log as $row) { 187 $totals[$row['type']] = idx($totals, $row['type'], 0) + $row['duration']; 188 $counts[$row['type']] = idx($counts, $row['type'], 0) + 1; 189 } 190 $totals['All Services'] = array_sum($totals); 191 $counts['All Services'] = array_sum($counts); 192 193 $totals['Entire Page'] = $page_total; 194 $counts['Entire Page'] = 0; 195 196 $summary = array(); 197 foreach ($totals as $type => $total) { 198 $summary[] = array( 199 $type, 200 number_format($counts[$type]), 201 number_format((int)(1000000 * $totals[$type])).' us', 202 sprintf('%.1f%%', 100 * $totals[$type] / $page_total), 203 ); 204 } 205 $summary_table = new AphrontTableView($summary); 206 $summary_table->setColumnClasses( 207 array( 208 '', 209 'n', 210 'n', 211 'wide', 212 )); 213 $summary_table->setHeaders( 214 array( 215 'Type', 216 'Count', 217 'Total Cost', 218 'Page Weight', 219 )); 220 221 $results[] = $summary_table->render(); 222 223 $rows = array(); 224 foreach ($log as $row) { 225 226 $analysis = null; 227 228 switch ($row['type']) { 229 case 'query': 230 $info = $row['query']; 231 $info = wordwrap($info, 128, "\n", true); 232 233 if (!empty($row['explain'])) { 234 $analysis = phutil_tag( 235 'span', 236 array( 237 'class' => 'explain-sev-'.$row['explain']['sev'], 238 ), 239 $row['explain']['reason']); 240 } 241 242 break; 243 case 'connect': 244 $info = $row['host'].':'.$row['database']; 245 break; 246 case 'exec': 247 $info = $row['command']; 248 break; 249 case 's3': 250 case 'conduit': 251 $info = $row['method']; 252 break; 253 case 'http': 254 $info = $row['uri']; 255 break; 256 default: 257 $info = '-'; 258 break; 259 } 260 261 $rows[] = array( 262 $row['type'], 263 '+'.number_format(1000 * ($row['begin'] - $data['start'])).' ms', 264 number_format(1000000 * $row['duration']).' us', 265 $info, 266 $analysis, 267 ); 268 } 269 270 $table = new AphrontTableView($rows); 271 $table->setColumnClasses( 272 array( 273 null, 274 'n', 275 'n', 276 'wide', 277 '', 278 )); 279 $table->setHeaders( 280 array( 281 'Event', 282 'Start', 283 'Duration', 284 'Details', 285 'Analysis', 286 )); 287 288 $results[] = $table->render(); 289 290 return phutil_implode_html("\n", $results); 291 } 292 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |