Passed
Pull Request — master (#9)
by Shinji
07:48
created

DaemonCommand   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
eloc 81
c 4
b 0
f 1
dl 0
loc 124
rs 10
wmc 6

2 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 52 1
A execute() 0 61 5
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 Amp\Loop;
0 ignored issues
show
Bug introduced by
The type Amp\Loop was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use Amp\Promise;
0 ignored issues
show
Bug introduced by
The type Amp\Promise was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Amp\Parallel\Context;
0 ignored issues
show
Bug introduced by
The type Amp\Parallel\Context was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use PhpProfiler\Command\Inspector\Settings\GetTraceSettings;
20
use PhpProfiler\Command\Inspector\Settings\TargetPhpSettings;
21
use PhpProfiler\Command\Inspector\Settings\TraceLoopSettings;
22
use Symfony\Component\Console\Command\Command;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Command\Command was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use Symfony\Component\Console\Input\InputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use Symfony\Component\Console\Input\InputOption;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputOption was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use Symfony\Component\Console\Output\OutputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Output\OutputInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
27
final class DaemonCommand extends Command
28
{
29
    public function configure(): void
30
    {
31
        $this->setName('inspector:daemon')
32
            ->setDescription('periodically get running function name from an outer process or thread')
33
            ->addOption(
34
                'target-regex',
35
                'P',
36
                InputOption::VALUE_OPTIONAL,
37
                'regex to find the php binary loaded in the target process'
38
            )
39
            ->addOption('depth', 'd', InputOption::VALUE_OPTIONAL, 'max depth')
40
            ->addOption(
41
                'sleep-ns',
42
                's',
43
                InputOption::VALUE_OPTIONAL,
44
                'nanoseconds between traces (default: 1000 * 1000 * 10)'
45
            )
46
            ->addOption(
47
                'max-retries',
48
                'r',
49
                InputOption::VALUE_OPTIONAL,
50
                'max retries on contiguous errors of read (default: 10)'
51
            )
52
            ->addOption(
53
                'php-regex',
54
                null,
55
                InputOption::VALUE_OPTIONAL,
56
                'regex to find the php binary loaded in the target process'
57
            )
58
            ->addOption(
59
                'libpthread-regex',
60
                null,
61
                InputOption::VALUE_OPTIONAL,
62
                'regex to find the libpthread.so loaded in the target process'
63
            )
64
            ->addOption(
65
                'php-version',
66
                null,
67
                InputOption::VALUE_OPTIONAL,
68
                'php version of the target'
69
            )
70
            ->addOption(
71
                'php-path',
72
                null,
73
                InputOption::VALUE_OPTIONAL,
74
                'path to the php binary (only needed in tracing chrooted ZTS target)'
75
            )
76
            ->addOption(
77
                'libpthread-path',
78
                null,
79
                InputOption::VALUE_OPTIONAL,
80
                'path to the libpthread.so (only needed in tracing chrooted ZTS target)'
81
            )
82
        ;
83
    }
84
85
    /**
86
     * @param InputInterface $input
87
     * @param OutputInterface $output
88
     * @return int
89
     */
90
    public function execute(InputInterface $input, OutputInterface $output): int
91
    {
92
        $target_php_settings = TargetPhpSettings::fromConsoleInput($input);
93
        $loop_settings = TraceLoopSettings::fromConsoleInput($input);
94
        $get_trace_settings = GetTraceSettings::fromConsoleInput($input);
95
96
        /** @var string $target_regex */
97
        $target_regex = '{' . ($input->getOption('target-regex') ?? '^php-fpm') . '}';
98
        $context = Context\create(__DIR__ . '/Worker/php-searcher.php');
0 ignored issues
show
Bug introduced by
The function create was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

98
        $context = /** @scrutinizer ignore-call */ Context\create(__DIR__ . '/Worker/php-searcher.php');
Loading history...
99
        /** @var int $searcher_pid */
100
        $searcher_pid = Promise\wait($context->start());
0 ignored issues
show
Bug introduced by
The function wait was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

100
        $searcher_pid = /** @scrutinizer ignore-call */ Promise\wait($context->start());
Loading history...
Unused Code introduced by
The assignment to $searcher_pid is dead and can be removed.
Loading history...
101
        Promise\wait($context->send($target_regex));
102
        /** @var int[] $pid_list */
103
        $pid_list = Promise\wait($context->receive());
104
        $readers = [];
105
        foreach ($pid_list as $target_pid) {
106
            $context = Context\create(__DIR__ . '/Worker/php-reader.php');
107
            Promise\wait($context->start());
108
            Promise\wait($context->send([$target_pid, $target_php_settings, $loop_settings, $get_trace_settings]));
109
            $readers[$target_pid] = $context;
110
        }
111
        exec('stty -icanon -echo');
112
113
        Loop::run(function () use (&$readers, $output) {
114
            Loop::onReadable(
115
                STDIN,
116
                /** @param resource $stream */
117
                function (string $watcher_id, $stream) {
118
                    $key = fread($stream, 1);
119
                    if ($key === 'q') {
120
                        Loop::cancel($watcher_id);
121
                        Loop::stop();
122
                    }
123
                }
124
            );
125
            Loop::repeat(10, function () use (&$readers, $output) {
126
                /** @var array<int, Context\Context> $readers */
127
128
                $promises = [];
129
                foreach ($readers as $pid => $reader) {
130
                    if (!$reader->isRunning()) {
131
                        /** @psalm-suppress MixedArrayAccess*/
132
                        unset($readers[$pid]);
133
                        continue;
134
                    }
135
                    $promises[] = \Amp\call(
0 ignored issues
show
Bug introduced by
The function call was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

135
                    $promises[] = /** @scrutinizer ignore-call */ \Amp\call(
Loading history...
136
                        function () use ($reader, &$readers, $pid, $output) {
137
                            /** @psalm-suppress MixedArrayAccess*/
138
                            unset($readers[$pid]);
139
                            /** @var string $result */
140
                            $result = yield $reader->receive();
141
                            $output->write($result);
142
                            $readers[$pid] = $reader;
143
                        }
144
                    );
145
                }
146
                yield $promises;
147
            });
148
        });
149
150
        return 0;
151
    }
152
}
153