Passed
Pull Request — master (#8)
by Shinji
08:28
created

GetTraceCommand::runPeriodically()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 13
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 17
rs 9.8333
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\Lib\Elf\Parser\ElfParserException;
17
use PhpProfiler\Lib\Elf\Process\ProcessSymbolReaderException;
18
use PhpProfiler\Lib\Elf\Tls\TlsFinderException;
19
use PhpProfiler\Lib\PhpProcessReader\PhpGlobalsFinder;
20
use PhpProfiler\Lib\Process\MemoryReader\MemoryReaderException;
21
use PhpProfiler\Lib\PhpProcessReader\PhpMemoryReader\ExecutorGlobalsReader;
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\ConsoleOutputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Consol...\ConsoleOutputInterface 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
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...
27
28
final class GetTraceCommand extends Command
29
{
30
    private const SLEEP_NANO_SECONDS_DEFAULT = 1000 * 1000 * 10;
31
32
    private PhpGlobalsFinder $php_globals_finder;
33
    private ExecutorGlobalsReader $executor_globals_reader;
34
    private TraceLoopProvider $loop_provider;
35
36
    /**
37
     * GetTraceCommand constructor.
38
     *
39
     * @param PhpGlobalsFinder $php_globals_finder
40
     * @param ExecutorGlobalsReader $executor_globals_reader
41
     * @param TraceLoopProvider $loop_provider
42
     * @param string|null $name
43
     */
44
    public function __construct(
45
        PhpGlobalsFinder $php_globals_finder,
46
        ExecutorGlobalsReader $executor_globals_reader,
47
        TraceLoopProvider $loop_provider,
48
        string $name = null
49
    ) {
50
        parent::__construct($name);
51
        $this->php_globals_finder = $php_globals_finder;
52
        $this->executor_globals_reader = $executor_globals_reader;
53
        $this->loop_provider = $loop_provider;
54
    }
55
56
    public function configure(): void
57
    {
58
        $this->setName('inspector:trace')
59
            ->setDescription('periodically get call trace from an outer process or thread')
60
            ->addOption('pid', 'p', InputOption::VALUE_REQUIRED, 'process id')
61
            ->addOption('depth', 'd', InputOption::VALUE_OPTIONAL, 'max depth')
62
            ->addOption(
63
                'sleep-ns',
64
                's',
65
                InputOption::VALUE_OPTIONAL,
66
                'nanoseconds between traces (default: 1000 * 1000 * 10)'
67
            );
68
    }
69
70
    /**
71
     * @param InputInterface $input
72
     * @param OutputInterface $output
73
     * @return int
74
     * @throws MemoryReaderException
75
     * @throws ProcessSymbolReaderException
76
     * @throws ElfParserException
77
     * @throws TlsFinderException
78
     */
79
    public function execute(InputInterface $input, OutputInterface $output): int
80
    {
81
        $pid = $input->getOption('pid');
82
        if (is_null($pid)) {
83
            $error_output = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
84
            $error_output->writeln('pid is not specified');
85
            return 1;
86
        }
87
        $pid = filter_var($pid, FILTER_VALIDATE_INT);
88
        if ($pid === false) {
89
            $error_output = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
90
            $error_output->writeln('pid is not integer');
91
            return 2;
92
        }
93
94
        $depth = $input->getOption('depth');
95
        if (is_null($depth)) {
96
            $depth = PHP_INT_MAX;
97
        }
98
        $depth = filter_var($depth, FILTER_VALIDATE_INT);
99
        if ($depth === false) {
100
            $error_output = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
101
            $error_output->writeln('depth is not integer');
102
            return 2;
103
        }
104
105
        $sleep_nano_seconds = $input->getOption('sleep-ns');
106
        if (is_null($sleep_nano_seconds)) {
107
            $sleep_nano_seconds = self::SLEEP_NANO_SECONDS_DEFAULT;
108
        }
109
        $sleep_nano_seconds = filter_var($sleep_nano_seconds, FILTER_VALIDATE_INT);
110
        if ($sleep_nano_seconds === false) {
111
            $error_output = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
112
            $error_output->writeln('sleep-ns is not integer');
113
            return 2;
114
        }
115
116
        $eg_address = $this->php_globals_finder->findExecutorGlobals($pid);
117
118
        $this->loop_provider->getMainLoop(
119
            function () use ($pid, $eg_address, $depth, $output): bool {
120
                $call_trace = $this->executor_globals_reader->readCallTrace(
121
                    $pid,
122
                    $eg_address,
123
                    $depth
124
                );
125
                $output->writeln(join(PHP_EOL, $call_trace) . PHP_EOL);
126
                return true;
127
            },
128
            $sleep_nano_seconds
129
        )->invoke();
130
131
        return 0;
132
    }
133
}
134