Passed
Pull Request — 0.9.x (#345)
by Shinji
02:55 queued 01:05
created

MemoryCommand::execute()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 90
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 55
c 1
b 0
f 0
nc 8
nop 2
dl 0
loc 90
rs 8.9818

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\Command\Inspector;
15
16
use Reli\Inspector\Settings\TargetPhpSettings\TargetPhpSettingsFromConsoleInput;
17
use Reli\Inspector\Settings\TargetProcessSettings\TargetProcessSettingsFromConsoleInput;
18
use Reli\Inspector\TargetProcess\TargetProcessResolver;
19
use Reli\Lib\PhpProcessReader\PhpGlobalsFinder;
20
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ContextAnalyzer\ContextAnalyzer;
21
use Reli\Lib\PhpProcessReader\PhpMemoryReader\LocationTypeAnalyzer\LocationTypeAnalyzer;
22
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocationsCollector;
23
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ObjectClassAnalyzer\ObjectClassAnalyzer;
24
use Reli\Lib\PhpProcessReader\PhpMemoryReader\RegionAnalyzer\RegionAnalyzer;
25
use Reli\Lib\PhpProcessReader\PhpVersionDetector;
26
use Reli\Lib\Process\ProcessStopper\ProcessStopper;
27
use Reli\ReliProfiler;
28
use Symfony\Component\Console\Command\Command;
29
use Symfony\Component\Console\Input\InputInterface;
30
use Symfony\Component\Console\Input\InputOption;
31
use Symfony\Component\Console\Output\OutputInterface;
32
33
use function Reli\Lib\Defer\defer;
34
35
final class MemoryCommand extends Command
36
{
37
    public function __construct(
38
        private PhpGlobalsFinder $php_globals_finder,
39
        private TargetPhpSettingsFromConsoleInput $target_php_settings_from_console_input,
40
        private TargetProcessSettingsFromConsoleInput $target_process_settings_from_console_input,
41
        private TargetProcessResolver $target_process_resolver,
42
        private PhpVersionDetector $php_version_detector,
43
        private MemoryLocationsCollector $memory_locations_collector,
44
        private ProcessStopper $process_stopper,
45
    ) {
46
        parent::__construct();
47
    }
48
49
    public function configure(): void
50
    {
51
        $this->setName('inspector:memory')
52
            ->setDescription('[experimental] get memory usage from an outer process')
53
        ;
54
        $this->addOption(
55
            'stop-process',
56
            null,
57
            InputOption::VALUE_OPTIONAL,
58
            'stop the process while inspecting',
59
            true,
60
        );
61
        $this->addOption(
62
            'pretty-print',
63
            null,
64
            InputOption::VALUE_OPTIONAL,
65
            'pretty print the result',
66
            false,
67
        );
68
        $this->target_process_settings_from_console_input->setOptions($this);
69
        $this->target_php_settings_from_console_input->setOptions($this);
70
    }
71
72
    public function execute(InputInterface $input, OutputInterface $output): int
73
    {
74
        $target_php_settings = $this->target_php_settings_from_console_input->createSettings($input);
75
        $target_process_settings = $this->target_process_settings_from_console_input->createSettings($input);
76
77
        $process_specifier = $this->target_process_resolver->resolve($target_process_settings);
78
79
        $target_php_settings_version_decided = $this->php_version_detector->decidePhpVersion(
80
            $process_specifier,
81
            $target_php_settings
82
        );
83
84
        if ($input->getOption('stop-process')) {
85
            $this->process_stopper->stop($process_specifier->pid);
86
            defer($scope_guard, fn () => $this->process_stopper->resume($process_specifier->pid));
87
        }
88
89
        $eg_address = $this->php_globals_finder->findExecutorGlobals($process_specifier, $target_php_settings);
90
        $cg_address = $this->php_globals_finder->findCompilerGlobals($process_specifier, $target_php_settings);
91
92
        $collected_memories = $this->memory_locations_collector->collectAll(
93
            $process_specifier,
94
            $target_php_settings_version_decided,
95
            $eg_address,
96
            $cg_address
97
        );
98
99
        $region_analyzer = new RegionAnalyzer(
100
            $collected_memories->chunk_memory_locations,
101
            $collected_memories->huge_memory_locations,
102
            $collected_memories->vm_stack_memory_locations,
103
            $collected_memories->compiler_arena_memory_locations,
104
        );
105
106
        $analyzed_regions = $region_analyzer->analyze(
107
            $collected_memories->memory_locations,
108
        );
109
        $location_type_analyzer = new LocationTypeAnalyzer();
110
        $heap_location_type_summary = $location_type_analyzer->analyze(
111
            $analyzed_regions->regional_memory_locations->locations_in_zend_mm_heap,
112
        );
113
114
        $object_class_analyzer = new ObjectClassAnalyzer();
115
        $object_class_summary = $object_class_analyzer->analyze(
116
            $analyzed_regions->regional_memory_locations->locations_in_zend_mm_heap,
117
        );
118
119
        $summary = [
120
            $analyzed_regions->summary->toArray()
121
            + [
122
                'memory_get_usage' => $collected_memories->memory_get_usage_size,
123
                'memory_get_real_usage' => $collected_memories->memory_get_usage_real_size,
124
                'cached_chunks_size' => $collected_memories->cached_chunks_size,
125
            ]
126
            + [
127
                'heap_memory_analyzed_percentage' =>
128
                    $analyzed_regions->summary->zend_mm_heap_usage
129
                    /
130
                    $collected_memories->memory_get_usage_size * 100
131
                ,
132
            ]
133
            + [
134
                'php_version' => $target_php_settings_version_decided->php_version,
135
                'analyzer' => ReliProfiler::toolSignature(),
136
            ]
137
        ];
138
139
        $context_analyzer = new ContextAnalyzer();
140
        $analyzed_context = $context_analyzer->analyze(
141
            $collected_memories->top_reference_context,
142
        );
143
144
        $flags = JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE;
145
        if ($input->getOption('pretty-print')) {
146
            $flags |= JSON_PRETTY_PRINT;
147
        }
148
        echo json_encode(
149
            [
150
                'summary' => $summary,
151
                "location_types_summary" => $heap_location_type_summary->per_type_usage,
152
                'class_objects_summary' => $object_class_summary->per_class_usage,
153
                'context' => $analyzed_context,
154
            ],
155
            $flags,
156
            2147483647
157
        );
158
        if (json_last_error() !== JSON_ERROR_NONE) {
159
            throw new \RuntimeException(json_last_error_msg());
160
        }
161
        return 0;
162
    }
163
}
164