Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ProfilerExcimer
0.00% covered (danger)
0.00%
0 / 96
0.00% covered (danger)
0.00%
0 / 5
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
 scopedProfileIn
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 close
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getFunctionStats
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
20
 getOutput
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
110
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Profiler;
8
9use ExcimerProfiler;
10
11/**
12 * @ingroup Profiler
13 * @since 1.33
14 * @see $wgProfiler
15 */
16class ProfilerExcimer extends Profiler {
17    /** @var ExcimerProfiler */
18    private $cpuProf;
19    /** @var ExcimerProfiler */
20    private $realProf;
21    /** @var float */
22    private $period;
23
24    /**
25     * @param array $params Associative array of parameters:
26     *   - period: The sampling period
27     *   - maxDepth: The maximum stack depth collected
28     *   - cpuProfiler: A pre-started ExcimerProfiler instance for CPU
29     *     profiling of the entire request including configuration.
30     *   - realProfiler: A pre-started ExcimerProfiler instance for wall
31     *     clock profiling of the entire request.
32     */
33    public function __construct( array $params = [] ) {
34        parent::__construct( $params );
35
36        $this->period = $params['period'] ?? 0.01;
37        $maxDepth = $params['maxDepth'] ?? 100;
38
39        if ( isset( $params['cpuProfiler'] ) ) {
40            $this->cpuProf = $params['cpuProfiler'];
41        } else {
42            $this->cpuProf = new ExcimerProfiler;
43            $this->cpuProf->setEventType( EXCIMER_CPU );
44            $this->cpuProf->setPeriod( $this->period );
45            $this->cpuProf->setMaxDepth( $maxDepth );
46            $this->cpuProf->start();
47        }
48
49        if ( isset( $params['realProfiler'] ) ) {
50            $this->realProf = $params['realProfiler'];
51        } else {
52            $this->realProf = new ExcimerProfiler;
53            $this->realProf->setEventType( EXCIMER_REAL );
54            $this->realProf->setPeriod( $this->period );
55            $this->realProf->setMaxDepth( $maxDepth );
56            $this->realProf->start();
57        }
58    }
59
60    /** @inheritDoc */
61    #[\NoDiscard]
62    public function scopedProfileIn( $section ): ?SectionProfileCallback {
63        return null;
64    }
65
66    public function close() {
67        $this->cpuProf->stop();
68        $this->realProf->stop();
69    }
70
71    /** @inheritDoc */
72    public function getFunctionStats() {
73        $this->close();
74        $cpuStats = $this->cpuProf->getLog()->aggregateByFunction();
75        '@phan-var array $cpuStats';
76        $realStats = $this->realProf->getLog()->aggregateByFunction();
77        '@phan-var array $realStats';
78        $allNames = array_keys( $realStats + $cpuStats );
79        $cpuSamples = $this->cpuProf->getLog()->getEventCount();
80        $realSamples = $this->realProf->getLog()->getEventCount();
81
82        $resultStats = [ [
83            'name' => '-total',
84            'calls' => 1,
85            'memory' => 0,
86            '%memory' => 0,
87            'min_real' => 0,
88            'max_real' => 0,
89            'cpu' => $cpuSamples * $this->period * 1000,
90            '%cpu' => 100,
91            'real' => $realSamples * $this->period * 1000,
92            '%real' => 100,
93        ] ];
94
95        foreach ( $allNames as $funcName ) {
96            $cpuEntry = $cpuStats[$funcName] ?? false;
97            $realEntry = $realStats[$funcName] ?? false;
98            $resultEntry = [
99                'name' => $funcName,
100                'calls' => 0,
101                'memory' => 0,
102                '%memory' => 0,
103                'min_real' => 0,
104                'max_real' => 0,
105            ];
106
107            if ( $cpuEntry ) {
108                $resultEntry['cpu'] = $cpuEntry['inclusive'] * $this->period * 1000;
109                $resultEntry['%cpu'] = $cpuEntry['inclusive'] / $cpuSamples * 100;
110            } else {
111                $resultEntry['cpu'] = 0;
112                $resultEntry['%cpu'] = 0;
113            }
114            if ( $realEntry ) {
115                $resultEntry['real'] = $realEntry['inclusive'] * $this->period * 1000;
116                $resultEntry['%real'] = $realEntry['inclusive'] / $realSamples * 100;
117            } else {
118                $resultEntry['real'] = 0;
119                $resultEntry['%real'] = 0;
120            }
121
122            $resultStats[] = $resultEntry;
123        }
124        return $resultStats;
125    }
126
127    /** @inheritDoc */
128    public function getOutput() {
129        $this->close();
130        $cpuLog = $this->cpuProf->getLog();
131        $realLog = $this->realProf->getLog();
132        $cpuStats = $cpuLog->aggregateByFunction();
133        '@phan-var array $cpuStats';
134        $realStats = $realLog->aggregateByFunction();
135        '@phan-var array $realStats';
136        $allNames = array_keys( $cpuStats + $realStats );
137        $cpuSamples = $cpuLog->getEventCount();
138        $realSamples = $realLog->getEventCount();
139
140        $result = '';
141
142        $titleFormat = "%-70s %10s %11s %10s %11s %10s %11s %10s %11s\n";
143        $statsFormat = "%-70s %10d %10.1f%% %10d %10.1f%% %10d %10.1f%% %10d %10.1f%%\n";
144        $result .= sprintf( $titleFormat,
145            'Name',
146            'CPU incl', 'CPU incl%', 'CPU self', 'CPU self%',
147            'Real incl', 'Real incl%', 'Real self', 'Real self%'
148        );
149
150        foreach ( $allNames as $funcName ) {
151            $realEntry = $realStats[$funcName] ?? false;
152            $cpuEntry = $cpuStats[$funcName] ?? false;
153            $realIncl = $realEntry ? $realEntry['inclusive'] : 0;
154            $realSelf = $realEntry ? $realEntry['self'] : 0;
155            $cpuIncl = $cpuEntry ? $cpuEntry['inclusive'] : 0;
156            $cpuSelf = $cpuEntry ? $cpuEntry['self'] : 0;
157            $result .= sprintf( $statsFormat,
158                $funcName,
159                $cpuIncl * $this->period * 1000,
160                $cpuIncl == 0 ? 0 : $cpuIncl / $cpuSamples * 100,
161                $cpuSelf * $this->period * 1000,
162                $cpuSelf == 0 ? 0 : $cpuSelf / $cpuSamples * 100,
163                $realIncl * $this->period * 1000,
164                $realIncl == 0 ? 0 : $realIncl / $realSamples * 100,
165                $realSelf * $this->period * 1000,
166                $realSelf == 0 ? 0 : $realSelf / $realSamples * 100
167            );
168        }
169
170        return $result;
171    }
172}
173
174/** @deprecated class alias since 1.46 */
175class_alias( ProfilerExcimer::class, 'ProfilerExcimer' );