jenkins-bot has submitted this change and it was merged.

Change subject: Add limit report data on preview pages
......................................................................


Add limit report data on preview pages

While we've long had the "NewPP limit report" hidden in an HTML comment,
it is hard for users to find this as they're not likely to look for
profiling information hidden in an HTML comment. Even for those aware of
it, it's not particularly convenient to find.

This changeset adds a table showing this information at the bottom of
the page preview. It also adds the ability for this information to be
added to the ParserOutput object in a structured manner, and various
messages so the report can be localized for the end user.

Note that, for backwards compatability, the default English messages are
used for the "NewPP limit report" comment rather than the localized
messages.

Change-Id: Ie065c7b5a17bbf1aa484d0ae1f3ee0f5d41f8495
---
M RELEASE-NOTES-1.22
M docs/hooks.txt
M includes/EditPage.php
M includes/parser/Parser.php
M includes/parser/ParserOutput.php
M languages/messages/MessagesEn.php
M languages/messages/MessagesQqq.php
M maintenance/language/messages.inc
M resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css
M resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js
10 files changed, 286 insertions(+), 13 deletions(-)

Approvals:
  Tim Starling: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/RELEASE-NOTES-1.22 b/RELEASE-NOTES-1.22
index af467a0..b9890a0 100644
--- a/RELEASE-NOTES-1.22
+++ b/RELEASE-NOTES-1.22
@@ -121,6 +121,10 @@
 * (bug 43689) The lists of templates used on the page and hidden categories it
   is a member of, shown below the edit form, are now collapsible (and collapsed
   by default).
+* Parser profiling data, formerly only available in the "NewPP limit report"
+  HTML comment, is now also displayed at the bottom of page previews.
+* Added ParserLimitReportPrepare and ParserLimitReportFormat hooks, deprecated
+  ParserLimitReport hook.
 * New user rights have been added to increase granularity in rights management
   for extensions such as OAuth:
 ** editmyusercss controls whether a user may edit their own CSS subpages.
diff --git a/docs/hooks.txt b/docs/hooks.txt
index 23ed032..50b97a3 100644
--- a/docs/hooks.txt
+++ b/docs/hooks.txt
@@ -1802,10 +1802,29 @@
 $parser: Parser object
 $varCache: variable cache (array)
 
-'ParserLimitReport': Called at the end of Parser:parse() when the parser will
+'ParserLimitReport': DEPRECATED, use ParserLimitReportPrepare and
+ParserLimitReportFormat instead.
+Called at the end of Parser:parse() when the parser will
 include comments about size of the text parsed.
 $parser: Parser object
-$limitReport: text that will be included (without comment tags)
+&$limitReport: text that will be included (without comment tags)
+
+'ParserLimitReportFormat': Called for each row in the parser limit report that
+needs formatting. If nothing handles this hook, the default is to use "$key" to
+get the label, and "$key-value" or "$key-value-text"/"$key-value-html" to
+format the value.
+$key: Key for the limit report item (string)
+$value: Value of the limit report item
+&$report: String onto which to append the data
+$isHTML: If true, $report is an HTML table with two columns; if false, it's
+       text intended for display in a monospaced font.
+$localize: If false, $report should be output in English.
+
+'ParserLimitReportPrepare': Called at the end of Parser:parse() when the 
parser will
+include comments about size of the text parsed. Hooks should use
+$output->setLimitReportData() to populate data.
+$parser: Parser object
+$output: ParserOutput object
 
 'ParserMakeImageParams': Called before the parser make an image link, use this
 to modify the parameters of the image.
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 17a1946..fe1ca00 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -2279,6 +2279,9 @@
                $wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 
'hiddencats' ),
                        Linker::formatHiddenCategories( 
$this->mArticle->getHiddenCategories() ) ) );
 
+               $wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 
'limitreport' ),
+                       self::getPreviewLimitReport( $this->mParserOutput ) ) );
+
                $wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
 
                if ( $this->isConflict ) {
@@ -2858,6 +2861,59 @@
                        call_user_func_array( 'wfMessage', $copywarnMsg 
)->plain() . "\n</div>";
        }
 
+       /**
+        * Get the Limit report for page previews
+        *
+        * @since 1.22
+        * @param ParserOutput $output ParserOutput object from the parse
+        * @return string HTML
+        */
+       public static function getPreviewLimitReport( $output ) {
+               if ( !$output || !$output->getLimitReportData() ) {
+                       return '';
+               }
+
+               wfProfileIn( __METHOD__ );
+
+               $limitReport = Html::rawElement( 'div', array( 'class' => 
'mw-limitReportExplanation' ),
+                       wfMessage( 'limitreport-title' )->parseAsBlock()
+               );
+
+               // Show/hide animation doesn't work correctly on a table, so 
wrap it in a div.
+               $limitReport .= Html::openElement( 'div', array( 'class' => 
'preview-limit-report-wrapper' ) );
+
+               $limitReport .= Html::openElement( 'table', array(
+                       'class' => 'preview-limit-report wikitable'
+               ) ) .
+                       Html::openElement( 'tbody' );
+
+               foreach ( $output->getLimitReportData() as $key => $value ) {
+                       if ( wfRunHooks( 'ParserLimitReportFormat',
+                               array( $key, $value, &$limitReport, true, true )
+                       ) ) {
+                               $keyMsg = wfMessage( $key );
+                               $valueMsg = wfMessage( array( 
"$key-value-html", "$key-value" ) );
+                               if ( !$valueMsg->exists() ) {
+                                       $valueMsg = new RawMessage( '$1' );
+                               }
+                               if ( !$keyMsg->isDisabled() && 
!$valueMsg->isDisabled() ) {
+                                       $limitReport .= Html::openElement( 'tr' 
) .
+                                               Html::rawElement( 'th', null, 
$keyMsg->parse() ) .
+                                               Html::rawElement( 'td', null, 
$valueMsg->params( $value )->parse() ) .
+                                               Html::closeElement( 'tr' );
+                               }
+                       }
+               }
+
+               $limitReport .= Html::closeElement( 'tbody' ) .
+                       Html::closeElement( 'table' ) .
+                       Html::closeElement( 'div' );
+
+               wfProfileOut( __METHOD__ );
+
+               return $limitReport;
+       }
+
        protected function showStandardInputs( &$tabindex = 2 ) {
                global $wgOut;
                $wgOut->addHTML( "<div class='editOptions'>\n" );
diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php
index 4a16f56..1bba876 100644
--- a/includes/parser/Parser.php
+++ b/includes/parser/Parser.php
@@ -362,6 +362,9 @@
                $this->startParse( $title, $options, self::OT_HTML, $clearState 
);
 
                $this->mInputSize = strlen( $text );
+               if ( $this->mOptions->getEnableLimitReport() ) {
+                       $this->mOutput->resetParseStartTime();
+               }
 
                # Remove the strip marker tag prefix from the input, if present.
                if ( $clearState ) {
@@ -492,22 +495,64 @@
                # Information on include size limits, for the benefit of users 
who try to skirt them
                if ( $this->mOptions->getEnableLimitReport() ) {
                        $max = $this->mOptions->getMaxIncludeSize();
-                       $PFreport = "Expensive parser function count: 
{$this->mExpensiveFunctionCount}/{$this->mOptions->getExpensiveParserFunctionLimit()}\n";
-                       $limitReport =
-                               "NewPP limit report\n" .
-                               "Preprocessor visited node count: 
{$this->mPPNodeCount}/{$this->mOptions->getMaxPPNodeCount()}\n" .
-                               "Preprocessor generated node count: " .
-                                       
"{$this->mGeneratedPPNodeCount}/{$this->mOptions->getMaxGeneratedPPNodeCount()}\n"
 .
-                               "Post-expand include size: 
{$this->mIncludeSizes['post-expand']}/$max bytes\n" .
-                               "Template argument size: 
{$this->mIncludeSizes['arg']}/$max bytes\n" .
-                               "Highest expansion depth: 
{$this->mHighestExpansionDepth}/{$this->mOptions->getMaxPPExpandDepth()}\n" .
-                               $PFreport;
+
+                       $cpuTime = $this->mOutput->getTimeSinceStart( 'cpu' );
+                       if ( $cpuTime !== null ) {
+                               $this->mOutput->setLimitReportData( 
'limitreport-cputime',
+                                       sprintf( "%.3f", $cpuTime )
+                               );
+                       }
+
+                       $wallTime = $this->mOutput->getTimeSinceStart( 'wall' );
+                       $this->mOutput->setLimitReportData( 
'limitreport-walltime',
+                               sprintf( "%.3f", $wallTime )
+                       );
+
+                       $this->mOutput->setLimitReportData( 
'limitreport-ppvisitednodes',
+                               array( $this->mPPNodeCount, 
$this->mOptions->getMaxPPNodeCount() )
+                       );
+                       $this->mOutput->setLimitReportData( 
'limitreport-ppgeneratednodes',
+                               array( $this->mGeneratedPPNodeCount, 
$this->mOptions->getMaxGeneratedPPNodeCount() )
+                       );
+                       $this->mOutput->setLimitReportData( 
'limitreport-postexpandincludesize',
+                               array( $this->mIncludeSizes['post-expand'], 
$max )
+                       );
+                       $this->mOutput->setLimitReportData( 
'limitreport-templateargumentsize',
+                               array( $this->mIncludeSizes['arg'], $max )
+                       );
+                       $this->mOutput->setLimitReportData( 
'limitreport-expansiondepth',
+                               array( $this->mHighestExpansionDepth, 
$this->mOptions->getMaxPPExpandDepth() )
+                       );
+                       $this->mOutput->setLimitReportData( 
'limitreport-expensivefunctioncount',
+                               array( $this->mExpensiveFunctionCount, 
$this->mOptions->getExpensiveParserFunctionLimit() )
+                       );
+                       wfRunHooks( 'ParserLimitReportPrepare', array( $this, 
$this->mOutput ) );
+
+                       $limitReport = "NewPP limit report\n";
+                       foreach ( $this->mOutput->getLimitReportData() as $key 
=> $value ) {
+                               if ( wfRunHooks( 'ParserLimitReportFormat',
+                                       array( $key, $value, &$limitReport, 
false, false )
+                               ) ) {
+                                       $keyMsg = wfMessage( $key 
)->inLanguage( 'en' )->useDatabase( false );
+                                       $valueMsg = wfMessage( array( 
"$key-value-text", "$key-value" ) )
+                                               ->inLanguage( 'en' 
)->useDatabase( false );
+                                       if ( !$valueMsg->exists() ) {
+                                               $valueMsg = new RawMessage( 
'$1' );
+                                       }
+                                       if ( !$keyMsg->isDisabled() && 
!$valueMsg->isDisabled() ) {
+                                               $valueMsg->params( $value );
+                                               $limitReport .= 
"{$keyMsg->text()}: {$valueMsg->text()}\n";
+                                       }
+                               }
+                       }
+                       // Since we're not really outputting HTML, decode the 
entities and
+                       // then re-encode the things that need hiding inside 
HTML comments.
+                       $limitReport = htmlspecialchars_decode( $limitReport );
                        wfRunHooks( 'ParserLimitReport', array( $this, 
&$limitReport ) );
 
                        // Sanitize for comment. Note '‐' in the replacement is 
U+2010,
                        // which looks much like the problematic '-'.
                        $limitReport = str_replace( array( '-', '&' ), array( 
'‐', '&amp;' ), $limitReport );
-
                        $text .= "\n<!-- \n$limitReport-->\n";
 
                        if ( $this->mGeneratedPPNodeCount > 
$this->mOptions->getMaxGeneratedPPNodeCount() / 10 ) {
diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php
index c5e42a4..5cb70cb 100644
--- a/includes/parser/ParserOutput.php
+++ b/includes/parser/ParserOutput.php
@@ -52,6 +52,8 @@
                private $mAccessedOptions = array(); # List of ParserOptions 
(stored in the keys)
                private $mSecondaryDataUpdates = array(); # List of DataUpdate, 
used to save info from the page somewhere else.
                private $mExtensionData = array(); # extra data used by 
extensions
+               private $mLimitReportData = array(); # Parser limit report data
+               private $mParseStartTime = array(); # Timestamps for 
getTimeSinceStart()
 
        const EDITSECTION_REGEX = '#<(?:mw:)?editsection page="(.*?)" 
section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
 
@@ -120,6 +122,7 @@
        function getIndexPolicy()            { return $this->mIndexPolicy; }
        function getTOCHTML()                { return $this->mTOCHTML; }
        function getTimestamp()              { return $this->mTimestamp; }
+       function getLimitReportData()        { return $this->mLimitReportData; }
 
        function setText( $text )            { return wfSetVar( $this->mText, 
$text ); }
        function setLanguageLinks( $ll )     { return wfSetVar( 
$this->mLanguageLinks, $ll ); }
@@ -544,4 +547,67 @@
                return null;
        }
 
+       private static function getTimes( $clock = null ) {
+               $ret = array();
+               if ( !$clock || $clock === 'wall' ) {
+                       $ret['wall'] = microtime( true );
+               }
+               if ( ( !$clock || $clock === 'cpu' ) && function_exists( 
'getrusage' ) ) {
+                       $ru = getrusage();
+                       $ret['cpu'] = $ru['ru_utime.tv_sec'] + 
$ru['ru_utime.tv_usec'] / 1e6;
+                       $ret['cpu'] += $ru['ru_stime.tv_sec'] + 
$ru['ru_stime.tv_usec'] / 1e6;
+               }
+               return $ret;
+       }
+
+       /**
+        * Resets the parse start timestamps for future calls to 
getTimeSinceStart()
+        * @since 1.22
+        */
+       function resetParseStartTime() {
+               $this->mParseStartTime = self::getTimes();
+       }
+
+       /**
+        * Returns the time since resetParseStartTime() was last called
+        *
+        * Clocks available are:
+        *  - wall: Wall clock time
+        *  - cpu: CPU time (requires getrusage)
+        *
+        * @since 1.22
+        * @param string $clock
+        * @return float|null
+        */
+       function getTimeSinceStart( $clock ) {
+               if ( !isset( $this->mParseStartTime[$clock] ) ) {
+                       return null;
+               }
+
+               $end = self::getTimes( $clock );
+               return $end[$clock] - $this->mParseStartTime[$clock];
+       }
+
+       /**
+        * Sets parser limit report data for a key
+        *
+        * The key is used as the prefix for various messages used for 
formatting:
+        *  - $key: The label for the field in the limit report
+        *  - $key-value-text: Message used to format the value in the "NewPP 
limit
+        *      report" HTML comment. If missing, uses $key-format.
+        *  - $key-value-html: Message used to format the value in the preview
+        *      limit report table. If missing, uses $key-format.
+        *  - $key-value: Message used to format the value. If missing, uses 
"$1".
+        *
+        * Note that all values are interpreted as wikitext, and so should be
+        * encoded with htmlspecialchars() as necessary, but should avoid 
complex
+        * HTML for sanity of display in the "NewPP limit report" comment.
+        *
+        * @since 1.22
+        * @param string $key Message key
+        * @param mixed $value Appropriate for Message::params()
+        */
+       function setLimitReportData( $key, $value ) {
+               $this->mLimitReportData[$key] = $value;
+       }
 }
diff --git a/languages/messages/MessagesEn.php 
b/languages/messages/MessagesEn.php
index 57e2cc3..b60ff6e 100644
--- a/languages/messages/MessagesEn.php
+++ b/languages/messages/MessagesEn.php
@@ -5118,4 +5118,23 @@
 # Image rotation
 'rotate-comment' => 'Image rotated by $1 {{PLURAL:$1|degree|degrees}} 
clockwise',
 
+# Limit report
+'limitreport-title' => 'Parser profiling data:',
+'limitreport-cputime' => 'CPU time usage',
+'limitreport-cputime-value' => '$1 {{PLURAL:$1|second|seconds}}',
+'limitreport-walltime' => 'Real time usage',
+'limitreport-walltime-value' => '$1 {{PLURAL:$1|second|seconds}}',
+'limitreport-ppvisitednodes' => 'Preprocessor visited node count',
+'limitreport-ppvisitednodes-value' => '$1/$2',
+'limitreport-ppgeneratednodes' => 'Preprocessor generated node count',
+'limitreport-ppgeneratednodes-value' => '$1/$2',
+'limitreport-postexpandincludesize' => 'Post-expand include size',
+'limitreport-postexpandincludesize-value' => '$1/$2 bytes',
+'limitreport-templateargumentsize' => 'Template argument size',
+'limitreport-templateargumentsize-value' => '$1/$2 bytes',
+'limitreport-expansiondepth' => 'Highest expansion depth',
+'limitreport-expansiondepth-value' => '$1/$2',
+'limitreport-expensivefunctioncount' => 'Expensive parser function count',
+'limitreport-expensivefunctioncount-value' => '$1/$2',
+
 );
diff --git a/languages/messages/MessagesQqq.php 
b/languages/messages/MessagesQqq.php
index 5dbdbeb..62b50d4 100644
--- a/languages/messages/MessagesQqq.php
+++ b/languages/messages/MessagesQqq.php
@@ -9266,4 +9266,37 @@
 # Image rotation
 'rotate-comment' => 'Edit summary for the act of rotating an image.',
 
+# Limit report
+'limitreport-title' => 'Title for the preview limit report table.',
+'limitreport-cputime' => 'Label for the "CPU time usage" row in the limit 
report table',
+'limitreport-cputime-value' => 'Format for the "CPU time usage" value in the 
limit report table.
+* $1 is the time usage in seconds',
+'limitreport-walltime' => 'Label for the "Real time usage" row in the limit 
report table',
+'limitreport-walltime-value' => 'Format for the "Real time usage" value in the 
limit report table.
+* $1 is the time usage in seconds',
+'limitreport-ppvisitednodes' => 'Label for the "Preprocessor visited node 
count" row in the limit report table',
+'limitreport-ppvisitednodes-value' => 'Format for the "Preprocessor visited 
node count" row in the limit report table.
+* $1 is the usage
+* $2 is the maximum',
+'limitreport-ppgeneratednodes' => 'Label for the "Preprocessor generated node 
count" row in the limit report table',
+'limitreport-ppgeneratednodes-value' => 'Format for the "Preprocessor 
generated node count" row in the limit report table.
+* $1 is the usage
+* $2 is the maximum',
+'limitreport-postexpandincludesize' => 'Label for the "Post-expand include 
size" row in the limit report table',
+'limitreport-postexpandincludesize-value' => 'Format for the "Post-expand 
include size" row in the limit report table.
+* $1 is the usage in bytes
+* $2 is the maximum',
+'limitreport-templateargumentsize' => 'Label for the "Template argument size" 
row in the limit report table',
+'limitreport-templateargumentsize-value' => 'Format for the "Template argument 
size" row in the limit report table.
+* $1 is the usage in bytes
+* $2 is the maximum',
+'limitreport-expansiondepth' => 'Label for the "Highest expansion depth" row 
in the limit report table',
+'limitreport-expansiondepth-value' => 'Format for the "Highest expansion 
depth" row in the limit report table.
+* $1 is the depth
+* $2 is the maximum',
+'limitreport-expensivefunctioncount' => 'Label for the "Expensive parser 
function count" row in the limit report table',
+'limitreport-expensivefunctioncount-value' => 'Format for the "Expensive 
parser function count" row in the limit report table.
+* $1 is the usage
+* $2 is the maximum',
+
 );
diff --git a/maintenance/language/messages.inc 
b/maintenance/language/messages.inc
index aa9fa9e..c266f89 100644
--- a/maintenance/language/messages.inc
+++ b/maintenance/language/messages.inc
@@ -3954,6 +3954,25 @@
        'rotation' => array(
                'rotate-comment',
        ),
+       'limitreport' => array(
+               'limitreport-title',
+               'limitreport-cputime',
+               'limitreport-cputime-value',
+               'limitreport-walltime',
+               'limitreport-walltime-value',
+               'limitreport-ppvisitednodes',
+               'limitreport-ppvisitednodes-value',
+               'limitreport-ppgeneratednodes',
+               'limitreport-ppgeneratednodes-value',
+               'limitreport-postexpandincludesize',
+               'limitreport-postexpandincludesize-value',
+               'limitreport-templateargumentsize',
+               'limitreport-templateargumentsize-value',
+               'limitreport-expansiondepth',
+               'limitreport-expansiondepth-value',
+               'limitreport-expensivefunctioncount',
+               'limitreport-expensivefunctioncount-value',
+       ),
 );
 
 /** Comments for each block */
@@ -4198,4 +4217,5 @@
        'duration'              => 'Durations',
        'cachedspecial'         => 'SpecialCachedPage',
        'rotation'              => 'Image rotation',
+       'limitreport'           => 'Limit report',
 );
diff --git 
a/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css 
b/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css
index 89f54c4..1af4a7a 100644
--- a/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css
+++ b/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.css
@@ -9,3 +9,9 @@
        margin-bottom: 1em;
        margin-left: 2.5em;
 }
+
+/* Show/hide animation is incorrect if the table has a margin set. Extra
+ * "table.wikitable" is needed in the selector for CSS specificity. */
+table.wikitable.preview-limit-report {
+       margin: 0;
+}
diff --git 
a/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js 
b/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js
index 0fb5912..7ae51ab 100644
--- a/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js
+++ b/resources/mediawiki.action/mediawiki.action.edit.collapsibleFooter.js
@@ -12,6 +12,11 @@
                        $list: $( '.hiddencats ul' ),
                        $toggler: $( '.mw-hiddenCategoriesExplanation' ),
                        cookieName: 'hidden-categories-list'
+               },
+               {
+                       $list: $( '.preview-limit-report-wrapper' ),
+                       $toggler: $( '.mw-limitReportExplanation' ),
+                       cookieName: 'preview-limit-report'
                }
        ];
 

-- 
To view, visit https://gerrit.wikimedia.org/r/53802
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ie065c7b5a17bbf1aa484d0ae1f3ee0f5d41f8495
Gerrit-PatchSet: 16
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Anomie <[email protected]>
Gerrit-Reviewer: Anomie <[email protected]>
Gerrit-Reviewer: Daniel Friesen <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: MZMcBride <[email protected]>
Gerrit-Reviewer: Nikerabbit <[email protected]>
Gerrit-Reviewer: Tim Starling <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to