Skip to content

Commit

Permalink
refactor finding EG
Browse files Browse the repository at this point in the history
  • Loading branch information
sj-i committed Sep 26, 2021
1 parent 5cbecec commit 59c4207
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 51 deletions.
27 changes: 12 additions & 15 deletions src/Command/Inspector/GetCurrentFunctionNameCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

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;
Expand All @@ -39,6 +40,7 @@ public function __construct(
private TargetProcessSettingsFromConsoleInput $target_process_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 @@ -68,25 +70,20 @@ public function execute(InputInterface $input, OutputInterface $output): int

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

$last_exception = null;
for ($i = 0; $i < 10; $i++) {
try {
// 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
);
break;
} catch (\Throwable $e) {
$last_exception = $e;
/** @psalm-suppress UnusedFunctionCall */
\time_nanosleep(0, 1000 * 1000 * 10);
continue;
}
}
if ($i === 10) {
assert(isset($last_exception) and $last_exception instanceof \Exception);
throw new \Exception('cannot find executor globals', 0, $last_exception);
}
},
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 ($process_specifier, $target_php_settings, $eg_address, $output): bool {
Expand Down
27 changes: 12 additions & 15 deletions src/Command/Inspector/GetEgAddressCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

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;
Expand All @@ -36,6 +37,7 @@ public function __construct(
private TargetPhpSettingsFromConsoleInput $target_php_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 Down Expand Up @@ -63,25 +65,20 @@ public function execute(InputInterface $input, OutputInterface $output): int

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

$last_exception = null;
for ($i = 0; $i < 10; $i++) {
try {
// 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
);
break;
} catch (\Throwable $e) {
$last_exception = $e;
/** @psalm-suppress UnusedFunctionCall */
\time_nanosleep(0, 1000 * 1000 * 10);
continue;
}
}
if ($i === 10) {
assert(isset($last_exception) and $last_exception instanceof \Exception);
throw new \Exception('cannot find executor globals', 0, $last_exception);
}
},
retry_on: [\Throwable::class],
max_retry: 10,
interval_on_retry_ns: 1000 * 1000 * 10,
);
assert(is_int($eg_address));

$output->writeln(
sprintf(
Expand Down
30 changes: 14 additions & 16 deletions src/Command/Inspector/GetTraceCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
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;
Expand Down Expand Up @@ -49,6 +50,7 @@ public function __construct(
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 @@ -83,25 +85,22 @@ public function execute(InputInterface $input, OutputInterface $output): int

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

$last_exception = null;
for ($i = 0; $i < 10; $i++) {
try {
// 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
);
break;
} catch (\Throwable $e) {
$last_exception = $e;
/** @psalm-suppress UnusedFunctionCall */
\time_nanosleep(0, 1000 * 1000 * 10);
continue;
}
}
if ($i === 10) {
assert(isset($last_exception) and $last_exception instanceof \Exception);
throw new \Exception('cannot find executor globals', 0, $last_exception);
}
},
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 (
Expand All @@ -113,7 +112,6 @@ function () use (
$output,
$formatter
): bool {

if ($loop_settings->stop_process and $this->process_stopper->stop($process_specifier->pid)) {
defer($_, fn () => $this->process_stopper->resume($process_specifier->pid));
}
Expand Down
48 changes: 48 additions & 0 deletions src/Inspector/RetryingLoopProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/**
* This file is part of the sj-i/php-profiler package.
*
* (c) sji <sji@sj-i.dev>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace PhpProfiler\Inspector;

use PhpProfiler\Lib\Loop\LoopBuilder;
use PhpProfiler\Lib\Loop\LoopMiddleware\CallableMiddleware;
use PhpProfiler\Lib\Loop\LoopMiddleware\NanoSleepMiddleware;
use PhpProfiler\Lib\Loop\LoopMiddleware\RetryOnExceptionMiddleware;

class RetryingLoopProvider
{
public function __construct(
private LoopBuilder $loop_builder
) {
}

/** @param class-string<\Throwable>[] $retry_on */
public function do(
callable $try,
array $retry_on,
int $max_retry,
int $interval_on_retry_ns,
): void {
// one successful execution is enough
$loop_canceller = function () use ($try): bool {
$try();
return false;
};

$this->loop_builder
->addProcess(RetryOnExceptionMiddleware::class, [$max_retry, $retry_on])
->addProcess(NanoSleepMiddleware::class, [$interval_on_retry_ns])
->addProcess(CallableMiddleware::class, [$loop_canceller])
->build()
->invoke();
}
}
10 changes: 6 additions & 4 deletions src/Lib/Loop/LoopMiddleware/RetryOnExceptionMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ public function invoke(): bool
$result = $this->chain->invoke();
$this->current_retry_count = 0;
return $result;
} catch (Exception $e) {
if (in_array(get_class($e), $this->exception_names, true)) {
$this->current_retry_count++;
continue;
} catch (\Throwable $e) {
foreach ($this->exception_names as $exception_name) {
if (is_a($e, $exception_name)) {
$this->current_retry_count++;
continue 2;
}
}
throw $e;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Lib/Process/Exec/TraceeExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use PhpProfiler\Lib\Process\Exec\Internal\Pcntl;
use PhpProfiler\Lib\System\OnShutdown;

final class TraceeExecutor
class TraceeExecutor
{
public function __construct(
private Pcntl $pcntl,
Expand Down

0 comments on commit 59c4207

Please sign in to comment.