Skip to content

Commit

Permalink
Merge 687e348 into f05ad5b
Browse files Browse the repository at this point in the history
  • Loading branch information
sj-i committed Dec 25, 2021
2 parents f05ad5b + 687e348 commit 88a3178
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 8 deletions.
10 changes: 10 additions & 0 deletions src/Command/Inspector/GetTraceCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use PhpProfiler\Lib\Elf\Process\ProcessSymbolReaderException;
use PhpProfiler\Lib\Elf\Tls\TlsFinderException;
use PhpProfiler\Lib\PhpProcessReader\PhpGlobalsFinder;
use PhpProfiler\Lib\PhpProcessReader\PhpVersionDetector;
use PhpProfiler\Lib\Process\MemoryReader\MemoryReaderException;
use PhpProfiler\Lib\PhpProcessReader\PhpMemoryReader\ExecutorGlobalsReader;
use PhpProfiler\Lib\Process\ProcessStopper\ProcessStopper;
Expand All @@ -51,6 +52,7 @@ public function __construct(
private ProcessStopper $process_stopper,
private TargetProcessResolver $target_process_resolver,
private RetryingLoopProvider $retrying_loop_provider,
private PhpVersionDetector $php_version_detector,
) {
parent::__construct();
}
Expand Down Expand Up @@ -85,6 +87,14 @@ public function execute(InputInterface $input, OutputInterface $output): int

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

$version = $this->php_version_detector->tryDetection(
$process_specifier,
$target_php_settings
);
if (!is_null($version)) {
$target_php_settings = $target_php_settings->alterPhpVersion($version);
}

// 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.
Expand Down
9 changes: 9 additions & 0 deletions src/Inspector/Daemon/Reader/Worker/PhpReaderTraceLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use PhpProfiler\Inspector\Settings\TraceLoopSettings\TraceLoopSettings;
use PhpProfiler\Lib\PhpProcessReader\PhpGlobalsFinder;
use PhpProfiler\Lib\PhpProcessReader\PhpMemoryReader\ExecutorGlobalsReader;
use PhpProfiler\Lib\PhpProcessReader\PhpVersionDetector;
use PhpProfiler\Lib\Process\ProcessSpecifier;
use PhpProfiler\Lib\Process\ProcessStopper\ProcessStopper;

Expand All @@ -33,6 +34,7 @@ public function __construct(
private ExecutorGlobalsReader $executor_globals_reader,
private ReaderLoopProvider $reader_loop_provider,
private ProcessStopper $process_stopper,
private PhpVersionDetector $php_version_detector,
) {
}

Expand All @@ -49,6 +51,13 @@ public function run(
TargetPhpSettings $target_php_settings,
GetTraceSettings $get_trace_settings
): Generator {
$version = $this->php_version_detector->tryDetection(
$process_specifier,
$target_php_settings
);
if (!is_null($version)) {
$target_php_settings = $target_php_settings->alterPhpVersion($version);
}
$eg_address = $this->php_globals_finder->findExecutorGlobals($process_specifier, $target_php_settings);

$loop = $this->reader_loop_provider->getMainLoop(
Expand Down
34 changes: 30 additions & 4 deletions src/Inspector/Settings/TargetPhpSettings/TargetPhpSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use PhpProfiler\Lib\PhpInternals\ZendTypeReader;

/** @psalm-immutable */
final class TargetPhpSettings
{
public const PHP_REGEX_DEFAULT = '.*/(php(74|7.4|80|8.0)?|php-fpm|libphp[78]?.*\.so)$';
Expand All @@ -23,13 +24,38 @@ final class TargetPhpSettings

/** @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version */
public function __construct(
public string $php_regex = self::PHP_REGEX_DEFAULT,
public string $libpthread_regex = self::LIBPTHREAD_REGEX_DEFAULT,
private string $php_regex = self::PHP_REGEX_DEFAULT,
private string $libpthread_regex = self::LIBPTHREAD_REGEX_DEFAULT,
public string $php_version = self::TARGET_PHP_VERSION_DEFAULT,
public ?string $php_path = null,
public ?string $libpthread_path = null
) {
$this->php_regex = '{' . $php_regex . '}';
$this->libpthread_regex = '{' . $libpthread_regex . '}';
}

public function getDelimitedPhpRegex(): string
{
return $this->getDelimitedRegex($this->php_regex);
}

public function getDelimitedLibPthreadRegex(): string
{
return $this->getDelimitedRegex($this->libpthread_regex);
}

private function getDelimitedRegex(string $regex): string
{
return '{' . $regex . '}';
}

/** @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version */
public function alterPhpVersion(string $php_version): self
{
return new self(
php_regex: $this->php_regex,
libpthread_regex: $this->libpthread_regex,
php_version: $php_version,
php_path: $this->php_path,
libpthread_path: $this->libpthread_path,
);
}
}
4 changes: 2 additions & 2 deletions src/Lib/PhpProcessReader/PhpGlobalsFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public function getSymbolReader(
): ProcessSymbolReaderInterface {
return $this->php_symbol_reader_creator->create(
$process_specifier->pid,
$target_php_settings->php_regex,
$target_php_settings->libpthread_regex,
$target_php_settings->getDelimitedPhpRegex(),
$target_php_settings->getDelimitedLibPthreadRegex(),
$target_php_settings->php_path,
$target_php_settings->libpthread_path
);
Expand Down
74 changes: 74 additions & 0 deletions src/Lib/PhpProcessReader/PhpVersionDetector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?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\Lib\PhpProcessReader;

use FFI\CPointer;
use PhpProfiler\Inspector\Settings\TargetPhpSettings\TargetPhpSettings;
use PhpProfiler\Lib\PhpInternals\ZendTypeCData;
use PhpProfiler\Lib\PhpInternals\ZendTypeReader;
use PhpProfiler\Lib\Process\MemoryReader\MemoryReaderInterface;
use PhpProfiler\Lib\Process\ProcessSpecifier;

final class PhpVersionDetector
{
private const VERSION_STRING_CONVERTOR = [
'7.0' => ZendTypeReader::V70,
'7.1' => ZendTypeReader::V71,
'7.2' => ZendTypeReader::V72,
'7.3' => ZendTypeReader::V73,
'7.4' => ZendTypeReader::V74,
'8.0' => ZendTypeReader::V80,
'8.1' => ZendTypeReader::V81,
];


public function __construct(
private PhpSymbolReaderCreator $php_symbol_reader_creator,
private ZendTypeReader $zend_type_reader,
private MemoryReaderInterface $memory_reader,
) {
}

/** @return null|value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> */
public function tryDetection(
ProcessSpecifier $process_specifier,
TargetPhpSettings $target_php_settings,
): ?string {
try {
$php_symbol_reader = $this->php_symbol_reader_creator->create(
$process_specifier->pid,
$target_php_settings->getDelimitedPhpRegex(),
$target_php_settings->getDelimitedLibPthreadRegex(),
$target_php_settings->php_path,
$target_php_settings->libpthread_path,
);
$basic_functions_module = $php_symbol_reader->read('basic_functions_module')
?? throw new \Exception();
/** @var ZendTypeCData<\FFI\PhpInternals\zend_module_entry> $module_entry */
$module_entry = $this->zend_type_reader->readAs('zend_module_entry', $basic_functions_module);
/** @var CPointer $version_string_pointer */
$version_string_pointer = \FFI::cast('long', $module_entry->typed->version)
?? throw new \Exception();
$version_string_cdata = $this->memory_reader->read(
$process_specifier->pid,
$version_string_pointer->cdata,
3
);
$php_version = \FFI::string($version_string_cdata, 3);
} catch (\Throwable) {
return null;
}
return self::VERSION_STRING_CONVERTOR[$php_version] ?? null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public function testFromConsoleInput(): void

$settings = (new TargetPhpSettingsFromConsoleInput())->createSettings($input);

$this->assertSame('{abc}', $settings->php_regex);
$this->assertSame('{def}', $settings->libpthread_regex);
$this->assertSame('{abc}', $settings->getDelimitedPhpRegex());
$this->assertSame('{def}', $settings->getDelimitedLibPthreadRegex());
$this->assertSame('v74', $settings->php_version);
$this->assertSame('ghi', $settings->php_path);
$this->assertSame('jkl', $settings->libpthread_path);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?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\Settings\TargetPhpSettings;

use PhpProfiler\Lib\PhpInternals\ZendTypeReader;
use PHPUnit\Framework\TestCase;

class TargetPhpSettingsTest extends TestCase
{
public function testAlterPhpVersion(): void
{
$settings = new TargetPhpSettings(
php_version: ZendTypeReader::V74,
);
$settings_altered = $settings->alterPhpVersion(ZendTypeReader::V81);
$this->assertSame($settings->php_path, $settings_altered->php_path);
$this->assertSame($settings->getDelimitedPhpRegex(), $settings_altered->getDelimitedPhpRegex());
$this->assertSame($settings->libpthread_path, $settings_altered->libpthread_path);
$this->assertSame($settings->getDelimitedLibPthreadRegex(), $settings_altered->getDelimitedLibPthreadRegex());
$this->assertSame(ZendTypeReader::V81, $settings_altered->php_version);
}

public function testGetDelimitedPhpRegex(): void
{
$settings = new TargetPhpSettings(
php_regex: 'test',
);
$this->assertSame('{test}', $settings->getDelimitedPhpRegex());
}

public function testGetDelimitedLibPthreadRegex(): void
{
$settings = new TargetPhpSettings(
libpthread_regex: 'test',
);
$this->assertSame('{test}', $settings->getDelimitedLibPthreadRegex());
}
}
5 changes: 5 additions & 0 deletions tools/stubs/ffi/php.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,8 @@ class zend_class_entry extends CData
{
public zend_string $name;
}

class zend_module_entry extends CData
{
public int $version; // pointer
}

0 comments on commit 88a3178

Please sign in to comment.