Improve ProfilerXhprof's blacklist/whitelist capabilities
authorOri Livneh <ori@wikimedia.org>
Thu, 21 May 2015 18:36:51 +0000 (11:36 -0700)
committerOri Livneh <ori@wikimedia.org>
Thu, 21 May 2015 19:09:12 +0000 (12:09 -0700)
* Apply the blacklist / whitelist to profiled sections, not just function names.
* Allow shell-style wildcard patterns in blacklist / whitelist.
* Prefix all profiled section names with 'section.', to distinguish them from
  functions.

Note that shell-style wildcard patterns are not supported by xhprof natively,
but it won't barf on them either, nor will they match against actual function
names (since shell wildcard characters are not valid for PHP function names),
and the filtering will still be enforced in ProfilerXhprof.

This has the side-effect of working around https://github.com/facebook/hhvm/issues/4385

Bug: T99829
Change-Id: I8354ed922fa7b42857eda03be8f62b89ac78d0d6

includes/profiler/ProfilerXhprof.php

index f36cdc1..5f7fc00 100644 (file)
  *
  * To restrict the functions for which profiling data is collected, you can
  * use either a whitelist ($wgProfiler['include']) or a blacklist
- * ($wgProfiler['exclude']) containing an array of function names. The
- * blacklist functionality is built into HHVM and will completely exclude the
- * named functions from profiling collection. The whitelist is implemented by
- * Xhprof class which will filter the data collected by XHProf before reporting.
- * See documentation for the Xhprof class and the XHProf extension for
- * additional information.
+ * ($wgProfiler['exclude']) containing an array of function names.
+ * Shell-style patterns are also accepted.
  *
  * @author Bryan Davis <bd808@wikimedia.org>
  * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
@@ -77,7 +73,8 @@ class ProfilerXhprof extends Profiler {
        }
 
        public function scopedProfileIn( $section ) {
-               return $this->sprofiler->scopedProfileIn( $section );
+               $key = 'section.' . ltrim( $section, '.' );
+               return $this->sprofiler->scopedProfileIn( $key );
        }
 
        /**
@@ -86,12 +83,43 @@ class ProfilerXhprof extends Profiler {
        public function close() {
        }
 
+       /**
+        * Check if a function or section should be excluded from the output.
+        *
+        * @param string $name Function or section name.
+        * @return bool
+        */
+       private function shouldExclude( $name ) {
+               if ( $name === '-total' ) {
+                       return true;
+               }
+               if ( !empty( $this->params['include'] ) ) {
+                       foreach ( $this->params['include'] as $pattern ) {
+                               if ( fnmatch( $pattern, $name, FNM_NOESCAPE ) ) {
+                                       return false;
+                               }
+                       }
+                       return true;
+               }
+               if ( !empty( $this->params['exclude'] ) ) {
+                       foreach ( $this->params['exclude'] as $pattern ) {
+                               if ( fnmatch( $pattern, $name, FNM_NOESCAPE ) ) {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
        public function getFunctionStats() {
                $metrics = $this->xhprof->getCompleteMetrics();
                $profile = array();
 
                $main = null; // units in ms
                foreach ( $metrics as $fname => $stats ) {
+                       if ( $this->shouldExclude( $fname ) ) {
+                               continue;
+                       }
                        // Convert elapsed times from μs to ms to match interface
                        $entry = array(
                                'name' => $fname,
@@ -113,8 +141,7 @@ class ProfilerXhprof extends Profiler {
 
                // Merge in all of the custom profile sections
                foreach ( $this->sprofiler->getFunctionStats() as $stats ) {
-                       if ( $stats['name'] === '-total' ) {
-                               // Discard section profiler running totals
+                       if ( $this->shouldExclude( $stats['name'] ) ) {
                                continue;
                        }