Skip to content

Commit

Permalink
Merge pull request #96 from sj-i/invoke-child
Browse files Browse the repository at this point in the history
Execute a command and attach to the process
  • Loading branch information
sj-i committed Sep 26, 2021
2 parents 8d30904 + 59c4207 commit 5940602
Show file tree
Hide file tree
Showing 45 changed files with 983 additions and 247 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"amphp/amp": "2.6.1",
"hassankhan/config": "2.2.0",
"sj-i/php-cast": "1.0.0",
"monolog/monolog": "2.3.4"
"monolog/monolog": "2.3.4",
"myclabs/php-enum": "1.8.3"
},
"require-dev": {
"ext-posix": "*",
Expand Down
76 changes: 68 additions & 8 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion config/di.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
use PhpProfiler\Lib\Elf\SymbolResolver\SymbolResolverCreatorInterface;
use PhpProfiler\Lib\File\FileReaderInterface;
use PhpProfiler\Lib\File\NativeFileReader;
use PhpProfiler\Lib\Libc\Sys\Ptrace\Ptrace;
use PhpProfiler\Lib\Libc\Sys\Ptrace\PtraceX64;
use PhpProfiler\Lib\Log\StateCollector\CallerStateCollector;
use PhpProfiler\Lib\Log\StateCollector\GroupedStateCollector;
use PhpProfiler\Lib\Log\StateCollector\ProcessStateCollector;
Expand Down Expand Up @@ -71,5 +73,6 @@
$handler->setFormatter(new JsonFormatter());
$logger->pushHandler($handler);
return $logger;
}
},
Ptrace::class => autowire(PtraceX64::class),
];
5 changes: 5 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
<file name="vendor/symfony/console/Command/Command.php"/>
</errorLevel>
</PossiblyNullArgument>
<UnnecessaryVarAnnotation>
<errorLevel type="suppress">
<file name="vendor/myclabs/php-enum/src/Enum.php"/>
</errorLevel>
</UnnecessaryVarAnnotation>
</issueHandlers>
<stubs>
<file name="vendor/jetbrains/phpstorm-stubs/FFI/FFI.php"/>
Expand Down
27 changes: 23 additions & 4 deletions src/Command/Inspector/GetCurrentFunctionNameCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@

namespace PhpProfiler\Command\Inspector;

use PhpProfiler\Inspector\RetryingLoopProvider;
use PhpProfiler\Inspector\Settings\InspectorSettingsException;
use PhpProfiler\Inspector\Settings\TargetPhpSettings\TargetPhpSettingsFromConsoleInput;
use PhpProfiler\Inspector\Settings\TargetProcessSettings\TargetProcessSettingsFromConsoleInput;
use PhpProfiler\Inspector\Settings\TraceLoopSettings\TraceLoopSettingsFromConsoleInput;
use PhpProfiler\Inspector\TargetProcess\TargetProcessResolver;
use PhpProfiler\Inspector\TraceLoopProvider;
use PhpProfiler\Lib\Elf\Parser\ElfParserException;
use PhpProfiler\Lib\Elf\Process\ProcessSymbolReaderException;
Expand All @@ -36,7 +38,9 @@ public function __construct(
private TraceLoopProvider $loop_provider,
private TargetPhpSettingsFromConsoleInput $target_php_settings_from_console_input,
private TargetProcessSettingsFromConsoleInput $target_process_settings_from_console_input,
private TraceLoopSettingsFromConsoleInput $trace_loop_settings_from_console_input
private TraceLoopSettingsFromConsoleInput $trace_loop_settings_from_console_input,
private TargetProcessResolver $target_process_resolver,
private RetryingLoopProvider $retrying_loop_provider,
) {
parent::__construct();
}
Expand Down Expand Up @@ -64,13 +68,28 @@ public function execute(InputInterface $input, OutputInterface $output): int
$target_process_settings = $this->target_process_settings_from_console_input->createSettings($input);
$loop_settings = $this->trace_loop_settings_from_console_input->createSettings($input);

$eg_address = $this->php_globals_finder->findExecutorGlobals($target_process_settings, $target_php_settings);
$process_specifier = $this->target_process_resolver->resolve($target_process_settings);

// see the comment at GetTraceCommand::execute()
$eg_address = null;
$this->retrying_loop_provider->do(
try: function () use (&$eg_address, $process_specifier, $target_php_settings) {
$eg_address = $this->php_globals_finder->findExecutorGlobals(
$process_specifier,
$target_php_settings
);
},
retry_on: [\Throwable::class],
max_retry: 10,
interval_on_retry_ns: 1000 * 1000 * 10,
);
assert(is_int($eg_address));

$this->loop_provider->getMainLoop(
function () use ($target_process_settings, $target_php_settings, $eg_address, $output): bool {
function () use ($process_specifier, $target_php_settings, $eg_address, $output): bool {
$output->writeln(
$this->executor_globals_reader->readCurrentFunctionName(
$target_process_settings->pid,
$process_specifier->pid,
$target_php_settings->php_version,
$eg_address
)
Expand Down
25 changes: 23 additions & 2 deletions src/Command/Inspector/GetEgAddressCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@

namespace PhpProfiler\Command\Inspector;

use PhpProfiler\Inspector\RetryingLoopProvider;
use PhpProfiler\Inspector\Settings\InspectorSettingsException;
use PhpProfiler\Inspector\Settings\TargetPhpSettings\TargetPhpSettingsFromConsoleInput;
use PhpProfiler\Inspector\Settings\TargetProcessSettings\TargetProcessSettingsFromConsoleInput;
use PhpProfiler\Inspector\TargetProcess\TargetProcessResolver;
use PhpProfiler\Lib\Elf\Parser\ElfParserException;
use PhpProfiler\Lib\Elf\Tls\TlsFinderException;
use PhpProfiler\Lib\PhpProcessReader\PhpGlobalsFinder;
Expand All @@ -33,7 +35,9 @@ final class GetEgAddressCommand extends Command
public function __construct(
private PhpGlobalsFinder $php_globals_finder,
private TargetPhpSettingsFromConsoleInput $target_php_settings_from_console_input,
private TargetProcessSettingsFromConsoleInput $target_process_settings_from_console_input
private TargetProcessSettingsFromConsoleInput $target_process_settings_from_console_input,
private TargetProcessResolver $target_process_resolver,
private RetryingLoopProvider $retrying_loop_provider,
) {
parent::__construct();
}
Expand All @@ -59,10 +63,27 @@ public function execute(InputInterface $input, OutputInterface $output): int
$target_php_settings = $this->target_php_settings_from_console_input->createSettings($input);
$target_process_settings = $this->target_process_settings_from_console_input->createSettings($input);

$process_specifier = $this->target_process_resolver->resolve($target_process_settings);

// see the comment at GetTraceCommand::execute()
$eg_address = null;
$this->retrying_loop_provider->do(
try: function () use (&$eg_address, $process_specifier, $target_php_settings) {
$eg_address = $this->php_globals_finder->findExecutorGlobals(
$process_specifier,
$target_php_settings
);
},
retry_on: [\Throwable::class],
max_retry: 10,
interval_on_retry_ns: 1000 * 1000 * 10,
);
assert(is_int($eg_address));

$output->writeln(
sprintf(
'0x%s',
dechex($this->php_globals_finder->findExecutorGlobals($target_process_settings, $target_php_settings))
dechex($eg_address)
)
);

Expand Down
31 changes: 26 additions & 5 deletions src/Command/Inspector/GetTraceCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
namespace PhpProfiler\Command\Inspector;

use PhpProfiler\Inspector\Output\TraceFormatter\Templated\TemplatedTraceFormatterFactory;
use PhpProfiler\Inspector\RetryingLoopProvider;
use PhpProfiler\Inspector\Settings\GetTraceSettings\GetTraceSettingsFromConsoleInput;
use PhpProfiler\Inspector\Settings\InspectorSettingsException;
use PhpProfiler\Inspector\Settings\TargetPhpSettings\TargetPhpSettingsFromConsoleInput;
use PhpProfiler\Inspector\Settings\TargetProcessSettings\TargetProcessSettingsFromConsoleInput;
use PhpProfiler\Inspector\Settings\TemplatedTraceFormatterSettings\TemplateSettingsFromConsoleInput;
use PhpProfiler\Inspector\Settings\TraceLoopSettings\TraceLoopSettingsFromConsoleInput;
use PhpProfiler\Inspector\TargetProcess\TargetProcessResolver;
use PhpProfiler\Inspector\TraceLoopProvider;
use PhpProfiler\Lib\Elf\Parser\ElfParserException;
use PhpProfiler\Lib\Elf\Process\ProcessSymbolReaderException;
Expand Down Expand Up @@ -47,6 +49,8 @@ public function __construct(
private TemplateSettingsFromConsoleInput $template_settings_from_console_input,
private TemplatedTraceFormatterFactory $templated_trace_formatter_factory,
private ProcessStopper $process_stopper,
private TargetProcessResolver $target_process_resolver,
private RetryingLoopProvider $retrying_loop_provider,
) {
parent::__construct();
}
Expand Down Expand Up @@ -79,23 +83,40 @@ public function execute(InputInterface $input, OutputInterface $output): int
$template_settings = $this->template_settings_from_console_input->createSettings($input);
$formatter = $this->templated_trace_formatter_factory->createFromSettings($template_settings);

$eg_address = $this->php_globals_finder->findExecutorGlobals($target_process_settings, $target_php_settings);
$process_specifier = $this->target_process_resolver->resolve($target_process_settings);

// On targeting ZTS, it's possible that libpthread.so of the target process isn't yet loaded
// at this point. In that case the TLS block can't be located, then the address of EG can't
// be found also. So simply retrying the whole process of finding EG here.
$eg_address = null;
$this->retrying_loop_provider->do(
try: function () use (&$eg_address, $process_specifier, $target_php_settings) {
$eg_address = $this->php_globals_finder->findExecutorGlobals(
$process_specifier,
$target_php_settings
);
},
retry_on: [\Throwable::class],
max_retry: 10,
interval_on_retry_ns: 1000 * 1000 * 10,
);
assert(is_int($eg_address));

$this->loop_provider->getMainLoop(
function () use (
$get_trace_settings,
$target_process_settings,
$process_specifier,
$target_php_settings,
$loop_settings,
$eg_address,
$output,
$formatter
): bool {
if ($loop_settings->stop_process and $this->process_stopper->stop($target_process_settings->pid)) {
defer($_, fn () => $this->process_stopper->resume($target_process_settings->pid));
if ($loop_settings->stop_process and $this->process_stopper->stop($process_specifier->pid)) {
defer($_, fn () => $this->process_stopper->resume($process_specifier->pid));
}
$call_trace = $this->executor_globals_reader->readCallTrace(
$target_process_settings->pid,
$process_specifier->pid,
$target_php_settings->php_version,
$eg_address,
$get_trace_settings->depth
Expand Down
6 changes: 3 additions & 3 deletions src/Inspector/Daemon/Reader/Worker/PhpReaderEntryPoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
use PhpProfiler\Inspector\Daemon\Reader\Protocol\Message\AttachMessage;
use PhpProfiler\Inspector\Daemon\Reader\Protocol\Message\SetSettingsMessage;
use PhpProfiler\Inspector\Daemon\Reader\Protocol\PhpReaderWorkerProtocolInterface;
use PhpProfiler\Inspector\Settings\TargetProcessSettings\TargetProcessSettings;
use PhpProfiler\Lib\Amphp\WorkerEntryPointInterface;
use PhpProfiler\Lib\Log\Log;
use PhpProfiler\Lib\Process\ProcessSpecifier;

final class PhpReaderEntryPoint implements WorkerEntryPointInterface
{
Expand All @@ -46,13 +46,13 @@ public function run(): \Generator
$attach_message = yield $this->protocol->receiveAttach();
Log::debug('attach_message', [$attach_message]);

$target_process_settings = new TargetProcessSettings(
$process_specifier = new ProcessSpecifier(
$attach_message->pid
);

try {
$loop_runner = $this->trace_loop->run(
$target_process_settings,
$process_specifier,
$set_settings_message->trace_loop_settings,
$set_settings_message->target_php_settings,
$set_settings_message->get_trace_settings
Expand Down

0 comments on commit 5940602

Please sign in to comment.