Passed
Push — 0.6.x ( 9a1948...a98caa )
by Shinji
03:23 queued 01:32
created

CallTraceReader::readCallTrace()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 75
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 41
nc 9
nop 6
dl 0
loc 75
rs 7.7084
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of the reliforp/reli-prof package.
5
 *
6
 * (c) sji <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Reli\Lib\PhpProcessReader\PhpMemoryReader;
15
16
use Reli\Lib\PhpInternals\Opcodes\OpcodeFactory;
17
use Reli\Lib\PhpInternals\Types\C\RawDouble;
18
use Reli\Lib\PhpInternals\Types\Zend\Opline;
19
use Reli\Lib\PhpInternals\Types\Zend\ZendCastedTypeProvider;
20
use Reli\Lib\PhpInternals\Types\Zend\ZendExecuteData;
21
use Reli\Lib\PhpInternals\Types\Zend\ZendExecutorGlobals;
22
use Reli\Lib\PhpInternals\Types\Zend\ZendFunction;
23
use Reli\Lib\PhpInternals\Types\Zend\ZendOp;
24
use Reli\Lib\PhpInternals\ZendTypeReader;
25
use Reli\Lib\PhpInternals\ZendTypeReaderCreator;
26
use Reli\Lib\PhpProcessReader\CallFrame;
27
use Reli\Lib\PhpProcessReader\CallTrace;
28
use Reli\Lib\PhpProcessReader\TraceCache;
29
use Reli\Lib\Process\MemoryReader\MemoryReaderInterface;
30
use Reli\Lib\Process\MemoryReader\MemoryReaderException;
31
use Reli\Lib\Process\Pointer\Dereferencer;
32
use Reli\Lib\Process\Pointer\Pointer;
33
use Reli\Lib\Process\Pointer\RemoteProcessDereferencer;
34
use Reli\Lib\Process\ProcessSpecifier;
35
36
final class CallTraceReader
37
{
38
    private ?ZendTypeReader $zend_type_reader = null;
39
40
    public function __construct(
41
        private MemoryReaderInterface $memory_reader,
42
        private ZendTypeReaderCreator $zend_type_reader_creator,
43
        private OpcodeFactory $opcode_factory,
44
    ) {
45
    }
46
47
    /**
48
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
49
     */
50
    public function getTypeReader(string $php_version): ZendTypeReader
51
    {
52
        if (is_null($this->zend_type_reader)) {
53
            $this->zend_type_reader = $this->zend_type_reader_creator->create($php_version);
54
        }
55
        return $this->zend_type_reader;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->zend_type_reader could return the type null which is incompatible with the type-hinted return Reli\Lib\PhpInternals\ZendTypeReader. Consider adding an additional type-check to rule them out.
Loading history...
56
    }
57
58
    /**
59
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
60
     */
61
    private function getDereferencer(int $pid, string $php_version): Dereferencer
62
    {
63
        return new RemoteProcessDereferencer(
64
            $this->memory_reader,
65
            new ProcessSpecifier($pid),
66
            new ZendCastedTypeProvider(
67
                $this->getTypeReader($php_version),
68
            )
69
        );
70
    }
71
72
    /**
73
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
74
     */
75
    private function getExecutorGlobals(
76
        int $eg_address,
77
        string $php_version,
78
        Dereferencer $dereferencer
79
    ): ZendExecutorGlobals {
80
        $zend_type_reader = $this->getTypeReader($php_version);
81
        $eg_pointer = new Pointer(
82
            ZendExecutorGlobals::class,
83
            $eg_address,
84
            $zend_type_reader->sizeOf('zend_executor_globals')
85
        );
86
        return $dereferencer->deref($eg_pointer);
87
    }
88
89
    /**
90
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
91
     */
92
    public function getGlobalRequestTime(
93
        int $sg_address,
94
        string $php_version,
95
        Dereferencer $dereferencer,
96
    ): float {
97
        $zend_type_reader = $this->getTypeReader($php_version);
98
        [$offset, $size] = $zend_type_reader->getOffsetAndSizeOfMember(
99
            'sapi_globals_struct',
100
            'global_request_time'
101
        );
102
103
        $pointer = new Pointer(
104
            RawDouble::class,
105
            $sg_address + $offset,
106
            $size
107
        );
108
        return $dereferencer->deref($pointer)->value;
109
    }
110
111
    /**
112
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
113
     * @throws MemoryReaderException
114
     */
115
    public function readCallTrace(
116
        int $pid,
117
        string $php_version,
118
        int $executor_globals_address,
119
        int $sapi_globals_address,
120
        int $depth,
121
        TraceCache $trace_cache,
122
    ): ?CallTrace {
123
        $dereferencer = $this->getDereferencer($pid, $php_version);
124
        $eg = $this->getExecutorGlobals($executor_globals_address, $php_version, $dereferencer);
125
        if (is_null($eg->current_execute_data)) {
126
            return null;
127
        }
128
129
        $trace_cache->updateCacheKey($this->getGlobalRequestTime($sapi_globals_address, $php_version, $dereferencer));
130
        $cached_deereferencer = $trace_cache->getDereferencer($dereferencer);
131
132
        /**
133
         * @var ZendExecuteData $current_execute_data
134
         * @psalm-ignore-var
135
         */
136
        $current_execute_data = $dereferencer->deref($eg->current_execute_data);
137
138
        $stack = [];
139
        $stack[] = $current_execute_data;
140
        for ($i = 0; $i < $depth; $i++) {
141
            if (is_null($current_execute_data->prev_execute_data)) {
142
                break;
143
            }
144
            $current_execute_data = $dereferencer->deref($current_execute_data->prev_execute_data);
145
            $stack[] = $current_execute_data;
146
        }
147
148
        $result = [];
149
        foreach ($stack as $current_execute_data) {
150
            if (is_null($current_execute_data->func)) {
151
                $result[] = new CallFrame(
152
                    '',
153
                    '<unknown>',
154
                    '<unknown>',
155
                    null
156
                );
157
                continue;
158
            }
159
            /**
160
             * @var ZendFunction $current_function
161
             * @psalm-ignore-var
162
             */
163
            $current_function = $cached_deereferencer->deref($current_execute_data->func);
164
165
            $function_name = $current_function->getFunctionName($cached_deereferencer) ?? '<main>';
166
            $class_name = $current_function->getClassName($cached_deereferencer) ?? '';
167
            $file_name = $current_function->getFileName($cached_deereferencer) ?? '<unknown>';
168
169
            $opline = null;
170
            if (
171
                $file_name !== '<internal>'
172
                and $file_name !== '<unknown>'
173
                and !is_null($current_execute_data->opline)
174
            ) {
175
                $opline = $this->readOpline(
176
                    $php_version,
177
                    $cached_deereferencer->deref($current_execute_data->opline)
178
                );
179
            }
180
181
            $result[] = new CallFrame(
182
                $class_name,
183
                $function_name,
184
                $file_name,
185
                $opline
186
            );
187
        }
188
189
        return new CallTrace(...$result);
190
    }
191
192
    /**
193
     * @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
0 ignored issues
show
Documentation Bug introduced by
The doc comment value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> at position 0 could not be parsed: Unknown type name 'value-of' at position 0 in value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS>.
Loading history...
194
     */
195
    private function readOpline(string $php_version, ZendOp $zend_op): Opline
196
    {
197
        return new Opline(
198
            $zend_op->op1,
199
            $zend_op->op2,
200
            $zend_op->result,
201
            $zend_op->extended_value,
202
            $zend_op->lineno,
203
            $this->opcode_factory->create($php_version, $zend_op->opcode),
204
            $zend_op->op1_type,
205
            $zend_op->op2_type,
206
            $zend_op->result_type
207
        );
208
    }
209
}
210