MediaWiki  REL1_22
profileinfo.php
Go to the documentation of this file.
00001 <?php
00028 ini_set( 'zlib.output_compression', 'off' );
00029 
00030 $wgEnableProfileInfo = $wgProfileToDatabase = false;
00031 require __DIR__ . '/includes/WebStart.php';
00032 
00033 header( 'Content-Type: text/html; charset=utf-8' );
00034 
00035 ?>
00036 <!DOCTYPE html>
00037 <html>
00038 <head>
00039     <meta charset="UTF-8">
00040     <title>Profiling data</title>
00041     <style>
00042         /* noc.wikimedia.org/base.css */
00043 
00044         * {
00045             margin: 0;
00046             padding: 0;
00047         }
00048 
00049         body {
00050             padding: 0.5em 1em;
00051             background: #fff;
00052             font: 14px/1.6 sans-serif;
00053             color: #333;
00054         }
00055 
00056         p, ul, ol, table {
00057             margin: 0.5em 0;
00058         }
00059 
00060         a {
00061             color: #0645AD;
00062             text-decoration: none;
00063         }
00064 
00065         a:hover {
00066             text-decoration: underline;
00067         }
00068 
00079         table {
00080             max-width: 100%;
00081             background-color: transparent;
00082             border-collapse: collapse;
00083             border-spacing: 0;
00084         }
00085 
00086         .table {
00087             width: 100%;
00088             margin-bottom: 20px;
00089         }
00090 
00091         .table th,
00092         .table td {
00093             padding: 0.1em;
00094             text-align: left;
00095             vertical-align: top;
00096             border-top: 1px solid #ddd;
00097         }
00098 
00099         .table th {
00100             font-weight: bold;
00101         }
00102 
00103         .table thead th {
00104             vertical-align: bottom;
00105         }
00106 
00107         .table thead:first-child tr:first-child th,
00108         .table thead:first-child tr:first-child td {
00109             border-top: 0;
00110         }
00111 
00112         .table tbody + tbody {
00113             border-top: 2px solid #ddd;
00114         }
00115 
00116         .table-condensed th,
00117         .table-condensed td {
00118             padding: 4px 5px;
00119         }
00120 
00121         .table-striped tbody tr:nth-child(odd) td,
00122         .table-striped tbody tr:nth-child(odd) th {
00123             background-color: #f9f9f9;
00124         }
00125 
00126         .table-hover tbody tr:hover td,
00127         .table-hover tbody tr:hover th {
00128             background-color: #f5f5f5;
00129         }
00130 
00131         hr {
00132             margin: 20px 0;
00133             border: 0;
00134             border-top: 1px solid #eee;
00135             border-bottom: 1px solid #fff;
00136         }
00137     </style>
00138 </head>
00139 <body>
00140 <?php
00141 
00142 if ( !$wgEnableProfileInfo ) {
00143     echo '<p>Disabled</p>'
00144         . '</body></html>';
00145     exit( 1 );
00146 }
00147 
00148 $dbr = wfGetDB( DB_SLAVE );
00149 
00150 if ( !$dbr->tableExists( 'profiling' ) ) {
00151     echo '<p>No <code>profiling</code> table exists, so we can\'t show you anything.</p>'
00152         . '<p>If you want to log profiling data, enable <code>$wgProfileToDatabase</code>'
00153         . ' in your LocalSettings.php and run <code>maintenance/update.php</code> to'
00154         . ' create the profiling table.'
00155         . '</body></html>';
00156     exit( 1 );
00157 }
00158 
00159 $expand = array();
00160 if ( isset( $_REQUEST['expand'] ) ) {
00161     foreach ( explode( ',', $_REQUEST['expand'] ) as $f ) {
00162         $expand[$f] = true;
00163     }
00164 }
00165 
00166 class profile_point {
00167     var $name;
00168     var $count;
00169     var $time;
00170     var $children;
00171 
00172     static $totaltime, $totalmemory, $totalcount;
00173 
00174     function __construct( $name, $count, $time, $memory ) {
00175         $this->name = $name;
00176         $this->count = $count;
00177         $this->time = $time;
00178         $this->memory = $memory;
00179         $this->children = array();
00180     }
00181 
00182     function add_child( $child ) {
00183         $this->children[] = $child;
00184     }
00185 
00186     function display( $expand, $indent = 0.0 ) {
00187         usort( $this->children, 'compare_point' );
00188 
00189         $ex = isset( $expand[$this->name()] );
00190 
00191         $anchor = str_replace( '"', '', $this->name() );
00192 
00193         if ( !$ex ) {
00194             if ( count( $this->children ) ) {
00195                 $url = getEscapedProfileUrl( false, false, $expand + array( $this->name() => true ) );
00196                 $extet = " <a id=\"{$anchor}\" href=\"{$url}#{$anchor}\">[+]</a>";
00197             } else {
00198                 $extet = '';
00199             }
00200         } else {
00201             $e = array();
00202             foreach ( $expand as $name => $ep ) {
00203                 if ( $name != $this->name() ) {
00204                     $e += array( $name => $ep );
00205                 }
00206             }
00207             $url = getEscapedProfileUrl( false, false, $e );
00208             $extet = " <a id=\"{$anchor}\" href=\"{$url}#{$anchor}\">[–]</a>";
00209         }
00210         ?>
00211     <tr>
00212         <th>
00213             <div style="margin-left: <?php echo (int)$indent; ?>em;">
00214                 <?php echo htmlspecialchars( str_replace( ',', ', ', $this->name() ) ) . $extet ?>
00215             </div>
00216         </th>
00217         <td class="mw-profileinfo-timep"><?php echo @wfPercent( $this->time() / self::$totaltime * 100 ); ?></td>
00218         <td class="mw-profileinfo-memoryp"><?php echo @wfPercent( $this->memory() / self::$totalmemory * 100 ); ?></td>
00219         <td class="mw-profileinfo-count"><?php echo $this->count(); ?></td>
00220         <td class="mw-profileinfo-cpr"><?php echo round( sprintf( '%.2f', $this->callsPerRequest() ), 2 ); ?></td>
00221         <td class="mw-profileinfo-tpc"><?php echo round( sprintf( '%.2f', $this->timePerCall() ), 2 ); ?></td>
00222         <td class="mw-profileinfo-mpc"><?php echo round( sprintf( '%.2f', $this->memoryPerCall() / 1024 ), 2 ); ?></td>
00223         <td class="mw-profileinfo-tpr"><?php echo @round( sprintf( '%.2f', $this->time() / self::$totalcount ), 2 ); ?></td>
00224         <td class="mw-profileinfo-mpr"><?php echo @round( sprintf( '%.2f', $this->memory() / self::$totalcount / 1024 ), 2 ); ?></td>
00225     </tr>
00226         <?php
00227         if ( $ex ) {
00228             foreach ( $this->children as $child ) {
00229                 $child->display( $expand, $indent + 2 );
00230             }
00231         }
00232     }
00233 
00234     function name() {
00235         return $this->name;
00236     }
00237 
00238     function count() {
00239         return $this->count;
00240     }
00241 
00242     function time() {
00243         return $this->time;
00244     }
00245 
00246     function memory() {
00247         return $this->memory;
00248     }
00249 
00250     function timePerCall() {
00251         return @( $this->time / $this->count );
00252     }
00253 
00254     function memoryPerCall() {
00255         return @( $this->memory / $this->count );
00256     }
00257 
00258     function callsPerRequest() {
00259         return @( $this->count / self::$totalcount );
00260     }
00261 
00262     function timePerRequest() {
00263         return @( $this->time / self::$totalcount );
00264     }
00265 
00266     function memoryPerRequest() {
00267         return @( $this->memory / self::$totalcount );
00268     }
00269 
00270     function fmttime() {
00271         return sprintf( '%5.02f', $this->time );
00272     }
00273 };
00274 
00275 function compare_point( profile_point $a, profile_point $b ) {
00276     global $sort;
00277     switch ( $sort ) {
00278         case 'name':
00279             return strcmp( $a->name(), $b->name() );
00280         case 'time':
00281             return $a->time() > $b->time() ? -1 : 1;
00282         case 'memory':
00283             return $a->memory() > $b->memory() ? -1 : 1;
00284         case 'count':
00285             return $a->count() > $b->count() ? -1 : 1;
00286         case 'time_per_call':
00287             return $a->timePerCall() > $b->timePerCall() ? -1 : 1;
00288         case 'memory_per_call':
00289             return $a->memoryPerCall() > $b->memoryPerCall() ? -1 : 1;
00290         case 'calls_per_req':
00291             return $a->callsPerRequest() > $b->callsPerRequest() ? -1 : 1;
00292         case 'time_per_req':
00293             return $a->timePerRequest() > $b->timePerRequest() ? -1 : 1;
00294         case 'memory_per_req':
00295             return $a->memoryPerRequest() > $b->memoryPerRequest() ? -1 : 1;
00296     }
00297 }
00298 
00299 $sorts = array( 'time', 'memory', 'count', 'calls_per_req', 'name',
00300     'time_per_call', 'memory_per_call', 'time_per_req', 'memory_per_req' );
00301 $sort = 'time';
00302 if ( isset( $_REQUEST['sort'] ) && in_array( $_REQUEST['sort'], $sorts ) ) {
00303     $sort = $_REQUEST['sort'];
00304 }
00305 
00306 $res = $dbr->select( 'profiling', '*', array(), 'profileinfo.php', array( 'ORDER BY' => 'pf_name ASC' ) );
00307 
00308 if ( isset( $_REQUEST['filter'] ) ) {
00309     $filter = $_REQUEST['filter'];
00310 } else {
00311     $filter = '';
00312 }
00313 
00314 ?>
00315 <form method="get" action="profileinfo.php">
00316     <p>
00317         <input type="text" name="filter" value="<?php echo htmlspecialchars( $filter ); ?>">
00318         <input type="hidden" name="sort" value="<?php echo htmlspecialchars( $sort ); ?>">
00319         <input type="hidden" name="expand" value="<?php echo htmlspecialchars( implode( ",", array_keys( $expand ) ) ); ?>">
00320         <input type="submit" value="Filter">
00321     </p>
00322 </form>
00323 
00324 <table class="mw-profileinfo-table table table-striped table-hover">
00325     <thead>
00326     <tr>
00327         <th><a href="<?php echo getEscapedProfileUrl( false, 'name' ); ?>">Name</a></th>
00328         <th><a href="<?php echo getEscapedProfileUrl( false, 'time' ); ?>">Time (%)</a></th>
00329         <th><a href="<?php echo getEscapedProfileUrl( false, 'memory' ); ?>">Memory (%)</a></th>
00330         <th><a href="<?php echo getEscapedProfileUrl( false, 'count' ); ?>">Count</a></th>
00331         <th><a href="<?php echo getEscapedProfileUrl( false, 'calls_per_req' ); ?>">Calls/req</a></th>
00332         <th><a href="<?php echo getEscapedProfileUrl( false, 'time_per_call' ); ?>">ms/call</a></th>
00333         <th><a href="<?php echo getEscapedProfileUrl( false, 'memory_per_call' ); ?>">kb/call</a></th>
00334         <th><a href="<?php echo getEscapedProfileUrl( false, 'time_per_req' ); ?>">ms/req</a></th>
00335         <th><a href="<?php echo getEscapedProfileUrl( false, 'memory_per_req' ); ?>">kb/req</a></th>
00336     </tr>
00337     </thead>
00338     <tbody>
00339     <?php
00340     profile_point::$totaltime = 0.0;
00341     profile_point::$totalcount = 0;
00342     profile_point::$totalmemory = 0.0;
00343 
00344     function getEscapedProfileUrl( $_filter = false, $_sort = false, $_expand = false ) {
00345         global $filter, $sort, $expand;
00346 
00347         if ( $_expand === false ) {
00348             $_expand = $expand;
00349         }
00350 
00351         return htmlspecialchars(
00352             '?' .
00353                 wfArrayToCgi( array(
00354                     'filter' => $_filter ? $_filter : $filter,
00355                     'sort' => $_sort ? $_sort : $sort,
00356                     'expand' => implode( ',', array_keys( $_expand ) )
00357                 ) )
00358         );
00359     }
00360 
00361     $points = array();
00362     $queries = array();
00363     $sqltotal = 0.0;
00364 
00365     $last = false;
00366     foreach ( $res as $o ) {
00367         $next = new profile_point( $o->pf_name, $o->pf_count, $o->pf_time, $o->pf_memory );
00368         if ( $next->name() == '-total' ) {
00369             profile_point::$totaltime = $next->time();
00370             profile_point::$totalcount = $next->count();
00371             profile_point::$totalmemory = $next->memory();
00372         }
00373         if ( $last !== false ) {
00374             if ( preg_match( '/^' . preg_quote( $last->name(), '/' ) . '/', $next->name() ) ) {
00375                 $last->add_child( $next );
00376                 continue;
00377             }
00378         }
00379         $last = $next;
00380         if ( preg_match( '/^query: /', $next->name() ) || preg_match( '/^query-m: /', $next->name() ) ) {
00381             $sqltotal += $next->time();
00382             $queries[] = $next;
00383         } else {
00384             $points[] = $next;
00385         }
00386     }
00387 
00388     $s = new profile_point( 'SQL Queries', 0, $sqltotal, 0, 0 );
00389     foreach ( $queries as $q ) {
00390         $s->add_child( $q );
00391     }
00392     $points[] = $s;
00393 
00394     usort( $points, 'compare_point' );
00395 
00396     foreach ( $points as $point ) {
00397         if ( strlen( $filter ) && !strstr( $point->name(), $filter ) ) {
00398             continue;
00399         }
00400 
00401         $point->display( $expand );
00402     }
00403     ?>
00404     </tbody>
00405 </table>
00406 <hr>
00407 <p>Total time: <code><?php printf( '%5.02f', profile_point::$totaltime ); ?></code></p>
00408 
00409 <p>Total memory: <code><?php printf( '%5.02f', profile_point::$totalmemory / 1024 ); ?></code></p>
00410 <hr />
00411 </body>
00412 </html>