Passed
Pull Request — 0.6.x (#217)
by Shinji
01:38
created

GetTraceCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 13
dl 0
loc 16
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * This file is part of the sj-i/php-profiler 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 PhpProfiler\Command\Inspector;
15
16
use PhpProfiler\Inspector\Output\TraceOutput\TraceOutputFactory;
17
use PhpProfiler\Inspector\RetryingLoopProvider;
18
use PhpProfiler\Inspector\Settings\GetTraceSettings\GetTraceSettingsFromConsoleInput;
19
use PhpProfiler\Inspector\Settings\InspectorSettingsException;
20
use PhpProfiler\Inspector\Settings\OutputSettings\OutputSettingsFromConsoleInput;
21
use PhpProfiler\Inspector\Settings\TargetPhpSettings\TargetPhpSettingsFromConsoleInput;
22
use PhpProfiler\Inspector\Settings\TargetProcessSettings\TargetProcessSettingsFromConsoleInput;
23
use PhpProfiler\Inspector\Settings\TraceLoopSettings\TraceLoopSettingsFromConsoleInput;
24
use PhpProfiler\Inspector\TargetProcess\TargetProcessResolver;
25
use PhpProfiler\Inspector\TraceLoopProvider;
26
use PhpProfiler\Lib\Elf\Parser\ElfParserException;
27
use PhpProfiler\Lib\Elf\Process\ProcessSymbolReaderException;
28
use PhpProfiler\Lib\Elf\Tls\TlsFinderException;
29
use PhpProfiler\Lib\PhpProcessReader\PhpGlobalsFinder;
30
use PhpProfiler\Lib\PhpProcessReader\PhpVersionDetector;
31
use PhpProfiler\Lib\PhpProcessReader\TraceCache;
32
use PhpProfiler\Lib\Process\MemoryReader\MemoryReaderException;
33
use PhpProfiler\Lib\PhpProcessReader\PhpMemoryReader\CallTraceReader;
34
use PhpProfiler\Lib\Process\ProcessStopper\ProcessStopper;
35
use Symfony\Component\Console\Command\Command;
36
use Symfony\Component\Console\Input\InputInterface;
37
use Symfony\Component\Console\Output\OutputInterface;
38
39
use function PhpProfiler\Lib\Defer\defer;
40
41
final class GetTraceCommand extends Command
42
{
43
    public function __construct(
44
        private PhpGlobalsFinder $php_globals_finder,
45
        private PhpVersionDetector $php_version_detector,
46
        private CallTraceReader $executor_globals_reader,
47
        private TraceLoopProvider $loop_provider,
48
        private GetTraceSettingsFromConsoleInput $get_trace_settings_from_console_input,
49
        private TargetPhpSettingsFromConsoleInput $target_php_settings_from_console_input,
50
        private TargetProcessSettingsFromConsoleInput $target_process_settings_from_console_input,
51
        private TraceLoopSettingsFromConsoleInput $trace_loop_settings_from_console_input,
52
        private OutputSettingsFromConsoleInput $output_settings_from_console_input,
53
        private TraceOutputFactory $trace_output_factory,
54
        private ProcessStopper $process_stopper,
55
        private TargetProcessResolver $target_process_resolver,
56
        private RetryingLoopProvider $retrying_loop_provider,
57
    ) {
58
        parent::__construct();
59
    }
60
61
    public function configure(): void
62
    {
63
        $this->setName('inspector:trace')
64
            ->setDescription('periodically get call trace from an outer process or thread')
65
        ;
66
        $this->target_process_settings_from_console_input->setOptions($this);
67
        $this->get_trace_settings_from_console_input->setOptions($this);
68
        $this->trace_loop_settings_from_console_input->setOptions($this);
69
        $this->target_php_settings_from_console_input->setOptions($this);
70
        $this->output_settings_from_console_input->setOptions($this);
71
    }
72
73
    /**
74
     * @throws MemoryReaderException
75
     * @throws ProcessSymbolReaderException
76
     * @throws ElfParserException
77
     * @throws TlsFinderException
78
     * @throws InspectorSettingsException
79
     */
80
    public function execute(InputInterface $input, OutputInterface $output): int
81
    {
82
        $get_trace_settings = $this->get_trace_settings_from_console_input->createSettings($input);
83
        $target_php_settings = $this->target_php_settings_from_console_input->createSettings($input);
84
        $target_process_settings = $this->target_process_settings_from_console_input->createSettings($input);
85
        $loop_settings = $this->trace_loop_settings_from_console_input->createSettings($input);
86
        $trace_output = $this->trace_output_factory->fromSettingsAndConsoleOutput(
87
            $output,
88
            $this->output_settings_from_console_input->createSettings($input),
89
        );
90
91
        $process_specifier = $this->target_process_resolver->resolve($target_process_settings);
92
93
        $target_php_settings = $this->php_version_detector->decidePhpVersion(
94
            $process_specifier,
95
            $target_php_settings
96
        );
97
98
        // On targeting ZTS, it's possible that libpthread.so of the target process isn't yet loaded
99
        // at this point. In that case the TLS block can't be located, then the address of EG can't
100
        // be found also. So simply retrying the whole process of finding EG here.
101
        $eg_address = $this->retrying_loop_provider->do(
102
            try: fn () => $this->php_globals_finder->findExecutorGlobals(
103
                $process_specifier,
104
                $target_php_settings
105
            ),
106
            retry_on: [\Throwable::class],
107
            max_retry: 10,
108
            interval_on_retry_ns: 1000 * 1000 * 10,
109
        );
110
111
        $sg_address = $this->php_globals_finder->findSAPIGlobals(
112
            $process_specifier,
113
            $target_php_settings
114
        );
115
116
        $trace_cache = new TraceCache();
117
        $this->loop_provider->getMainLoop(
118
            function () use (
119
                $get_trace_settings,
120
                $process_specifier,
121
                $target_php_settings,
122
                $loop_settings,
123
                $eg_address,
124
                $sg_address,
125
                $trace_output,
126
                $trace_cache,
127
            ): bool {
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected ')', expecting T_VARIABLE on line 127 at column 12
Loading history...
128
                assert($target_php_settings->isDecided());
129
                if ($loop_settings->stop_process and $this->process_stopper->stop($process_specifier->pid)) {
130
                    defer($_, fn () => $this->process_stopper->resume($process_specifier->pid));
131
                }
132
                $call_trace = $this->executor_globals_reader->readCallTrace(
133
                    $process_specifier->pid,
134
                    $target_php_settings->php_version,
135
                    $eg_address,
136
                    $sg_address,
137
                    $get_trace_settings->depth,
138
                    $trace_cache
139
                );
140
                if (!is_null($call_trace)) {
141
                    $trace_output->output($call_trace);
142
                }
143
                return true;
144
            },
145
            $loop_settings
146
        )->invoke();
147
148
        return 0;
149
    }
150
}
151