diff --git a/src/applications/cache/spec/PhabricatorDataCacheSpec.php b/src/applications/cache/spec/PhabricatorDataCacheSpec.php index ca840c10f8..0d78f01ff5 100644 --- a/src/applications/cache/spec/PhabricatorDataCacheSpec.php +++ b/src/applications/cache/spec/PhabricatorDataCacheSpec.php @@ -1,84 +1,132 @@ cacheSummary = $cache_summary; + return $this; + } + + public function getCacheSummary() { + return $this->cacheSummary; + } + public static function getActiveCacheSpec() { $spec = new PhabricatorDataCacheSpec(); // NOTE: If APCu is installed, it reports that APC is installed. if (extension_loaded('apc') && !extension_loaded('apcu')) { return self::getAPCSpec($spec); } else if (extension_loaded('apcu')) { return self::getAPCuSpec($spec); } else { return self::getNoneSpec($spec); } } private static function getAPCSpec(PhabricatorDataCacheSpec $spec) { $spec ->setName(pht('APC User Cache')) ->setVersion(phpversion('apc')); if (ini_get('apc.enabled')) { $spec->setIsEnabled(true); self::getAPCCommonSpec($spec); } else { $spec->setIsEnabled(false); $spec->newIssue( pht('Enable APC'), pht( 'The "APC" extension is currently disabled. Set "apc.enabled" to '. 'true to provide caching.'), 'apc.enabled'); } return $spec; } private static function getAPCuSpec(PhabricatorDataCacheSpec $spec) { $spec ->setName(pht('APCu')) ->setVersion(phpversion('apcu')); if (ini_get('apc.enabled')) { $spec->setIsEnabled(true); self::getAPCCommonSpec($spec); } else { $spec->setIsEnabled(false); $spec->newissue( pht('Enable APCu'), pht( 'The "APCu" extension is currently disabled. Set '. '"apc.enabled" to true to provide caching.'), 'apc.enabled'); } return $spec; } private static function getNoneSpec(PhabricatorDataCacheSpec $spec) { if (version_compare(phpversion(), '5.5', '>=')) { $spec->newIssue( pht('Install APCu'), pht( 'Install the "APCu" PHP extension to provide data caching.')); } else { $spec->newIssue( pht('Install APC'), pht( 'Install the "APC" PHP extension to provide data caching.')); } return $spec; } private static function getAPCCommonSpec(PhabricatorDataCacheSpec $spec) { $mem = apc_sma_info(); $spec->setTotalMemory($mem['num_seg'] * $mem['seg_size']); $info = apc_cache_info('user'); $spec->setUsedMemory($info['mem_size']); $spec->setEntryCount(count($info['cache_list'])); + + $cache = $info['cache_list']; + $state = array(); + foreach ($cache as $item) { + $key = self::getKeyPattern($item['info']); + if (empty($state[$key])) { + $state[$key] = array( + 'max' => 0, + 'total' => 0, + 'count' => 0, + ); + } + $state[$key]['max'] = max($state[$key]['max'], $item['mem_size']); + $state[$key]['total'] += $item['mem_size']; + $state[$key]['count']++; + } + + $spec->setCacheSummary($state); + } + + private static function getKeyPattern($key) { + // If this key isn't in the current cache namespace, don't reveal any + // information about it. + $namespace = PhabricatorEnv::getEnvConfig('phabricator.cache-namespace'); + if (strncmp($key, $namespace.':', strlen($namespace) + 1)) { + return ''; + } + + $key = preg_replace('/(?getViewer(); $nav = $this->buildSideNavView(); $nav->selectFilter('cache/'); $title = pht('Cache Status'); $crumbs = $this ->buildApplicationCrumbs() ->addTextCrumb(pht('Cache Status')); $code_box = $this->renderCodeBox(); $data_box = $this->renderDataBox(); $nav->appendChild( array( $crumbs, $code_box, $data_box, )); return $this->buildApplicationPage( $nav, array( 'title' => $title, )); } private function renderCodeBox() { $cache = PhabricatorOpcodeCacheSpec::getActiveCacheSpec(); $properties = id(new PHUIPropertyListView()); $this->renderCommonProperties($properties, $cache); return id(new PHUIObjectBoxView()) ->setFormErrors($this->renderIssues($cache->getIssues())) ->setHeaderText(pht('Opcode Cache')) ->addPropertyList($properties); } private function renderDataBox() { $cache = PhabricatorDataCacheSpec::getActiveCacheSpec(); $properties = id(new PHUIPropertyListView()); $this->renderCommonProperties($properties, $cache); + $table = null; + if ($cache->getName() !== null) { + $total_memory = $cache->getTotalMemory(); + + $summary = $cache->getCacheSummary(); + $summary = isort($summary, 'total'); + $summary = array_reverse($summary, true); + + $rows = array(); + foreach ($summary as $key => $info) { + $rows[] = array( + $key, + pht('%s', new PhutilNumber($info['count'])), + phutil_format_bytes($info['max']), + phutil_format_bytes($info['total']), + sprintf('%.1f%%', (100 * ($info['total'] / $total_memory))), + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Pattern'), + pht('Count'), + pht('Largest'), + pht('Total'), + pht('Usage'), + )) + ->setColumnClasses( + array( + 'wide', + 'n', + 'n', + 'n', + 'n', + )); + } + return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Data Cache')) - ->addPropertyList($properties); + ->addPropertyList($properties) + ->appendChild($table); } private function renderCommonProperties( PHUIPropertyListView $properties, PhabricatorCacheSpec $cache) { if ($cache->getName() !== null) { $name = $this->renderYes($cache->getName()); } else { $name = $this->renderNo(pht('None')); } $properties->addProperty(pht('Cache'), $name); if ($cache->getIsEnabled()) { $enabled = $this->renderYes(pht('Enabled')); } else { $enabled = $this->renderNo(pht('Not Enabled')); } $properties->addProperty(pht('Enabled'), $enabled); $version = $cache->getVersion(); if ($version) { $properties->addProperty(pht('Version'), $this->renderInfo($version)); } if ($cache->getName() === null) { return; } $mem_total = $cache->getTotalMemory(); $mem_used = $cache->getUsedMemory(); if ($mem_total) { $percent = 100 * ($mem_used / $mem_total); $properties->addProperty( pht('Memory Usage'), pht( '%s of %s', phutil_tag('strong', array(), sprintf('%.1f%%', $percent)), phutil_format_bytes($mem_total))); } $entry_count = $cache->getEntryCount(); if ($entry_count !== null) { $properties->addProperty( pht('Cache Entries'), pht('%s', new PhutilNumber($entry_count))); } } private function renderIssues(array $issues) { $result = array(); foreach ($issues as $issue) { $title = $issue['title']; $body = $issue['body']; $result[] = array( phutil_tag('strong', array(), $title.':'), ' ', $body, ); } return $result; } private function renderYes($info) { return array( id(new PHUIIconView())->setIconFont('fa-check', 'green'), ' ', $info, ); } private function renderNo($info) { return array( id(new PHUIIconView())->setIconFont('fa-times-circle', 'red'), ' ', $info, ); } private function renderInfo($info) { return array( id(new PHUIIconView())->setIconFont('fa-info-circle', 'grey'), ' ', $info, ); } }