Skip to content

Commit

Permalink
add automatic detection of the target php version
Browse files Browse the repository at this point in the history
  • Loading branch information
sj-i committed Dec 25, 2021
1 parent f185571 commit 687e348
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 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
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;
}
}
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 687e348

Please sign in to comment.