Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php /** * This file is part of the reliforp/reli-prof package. * * (c) sji <sji@sj-i..
Decoded Output download
<?php
/**
* This file is part of the reliforp/reli-prof package.
*
* (c) sji <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Reli\Lib\PhpProcessReader\PhpMemoryReader;
use Reli\Inspector\Settings\MemoryProfilerSettings\MemoryLimitErrorDetails;
use Reli\Inspector\Settings\TargetPhpSettings\TargetPhpSettings;
use Reli\Lib\Log\Log;
use Reli\Lib\PhpInternals\Types\Zend\Bucket;
use Reli\Lib\PhpInternals\Types\Zend\ZendArray;
use Reli\Lib\PhpInternals\Types\Zend\ZendCastedTypeProvider;
use Reli\Lib\PhpInternals\Types\Zend\ZendClassConstant;
use Reli\Lib\PhpInternals\Types\Zend\ZendClassEntry;
use Reli\Lib\PhpInternals\Types\Zend\ZendClosure;
use Reli\Lib\PhpInternals\Types\Zend\ZendCompilerGlobals;
use Reli\Lib\PhpInternals\Types\Zend\ZendConstant;
use Reli\Lib\PhpInternals\Types\Zend\ZendExecuteData;
use Reli\Lib\PhpInternals\Types\Zend\ZendExecutorGlobals;
use Reli\Lib\PhpInternals\Types\Zend\ZendFunction;
use Reli\Lib\PhpInternals\Types\Zend\ZendMmChunk;
use Reli\Lib\PhpInternals\Types\Zend\ZendObject;
use Reli\Lib\PhpInternals\Types\Zend\ZendObjectsStore;
use Reli\Lib\PhpInternals\Types\Zend\ZendReference;
use Reli\Lib\PhpInternals\Types\Zend\ZendResource;
use Reli\Lib\PhpInternals\Types\Zend\ZendString;
use Reli\Lib\PhpInternals\Types\Zend\Zval;
use Reli\Lib\PhpInternals\ZendTypeReader;
use Reli\Lib\PhpInternals\ZendTypeReaderCreator;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\CallFrameHeaderMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\CallFrameVariableTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DefaultPropertiesTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DefaultStaticMembersTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DynamicFuncDefsTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\LocalVariableNameTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\MemoryLocations;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ObjectsStoreMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\RuntimeCacheMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\StaticMembersTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\VmStackMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArenaMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArgInfosMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayTableOverheadMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendClassConstantMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendClassEntryMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendConstantMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendMmChunkMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendMmHugeListMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendObjectHandlersMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendObjectMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendOpArrayBodyMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendOpArrayHeaderMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendPropertyInfoMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendReferenceMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendResourceMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendStringMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArgInfoContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArgInfosContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayElementContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayElementsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayHeaderContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayPossibleOverheadContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFrameContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFramesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFrameVariableTableContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantInfoContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassDefinitionContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassEntryContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassStaticPropertiesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClosureContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefaultPropertiesTableContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefaultStaticPropertiesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefinedClassesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefinedFunctionsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DynamicFuncDefsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\FunctionDefinitionContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalConstantContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalConstantsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalVariablesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\IncludedFilesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\InternalFunctionDefinitionContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\LocalVariableNameTableContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectPropertiesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectsStoreContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\OpArrayContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PhpReferenceContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PropertiesInfoContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PropertyInfoContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ReferenceContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ResourceContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\RuntimeCacheContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ScalarValueContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\StringContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\TopReferenceContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\UserFunctionDefinitionContext;
use Reli\Lib\PhpProcessReader\PhpZendMemoryManagerChunkFinder;
use Reli\Lib\Process\MemoryReader\MemoryReaderInterface;
use Reli\Lib\Process\Pointer\Dereferencer;
use Reli\Lib\Process\Pointer\PointedTypeResolver;
use Reli\Lib\Process\Pointer\Pointer;
use Reli\Lib\Process\Pointer\RemoteProcessDereferencer;
use Reli\Lib\Process\ProcessSpecifier;
/** @psalm-import-type VersionDecided from TargetPhpSettings */
final class MemoryLocationsCollector
{
private ?ZendTypeReader $zend_type_reader = null;
private ?UserFunctionDefinitionContext $memory_limit_error_function_context = null;
public function __construct(
private MemoryReaderInterface $memory_reader,
private ZendTypeReaderCreator $zend_type_reader_creator,
private PhpZendMemoryManagerChunkFinder $chunk_finder,
) {
}
/**
* @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
*/
public function getTypeReader(string $php_version): ZendTypeReader
{
if (is_null($this->zend_type_reader)) {
$this->zend_type_reader = $this->zend_type_reader_creator->create($php_version);
}
return $this->zend_type_reader;
}
/**
* @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
*/
private function getDereferencer(int $pid, string $php_version): Dereferencer
{
return new RemoteProcessDereferencer(
$this->memory_reader,
new ProcessSpecifier($pid),
new ZendCastedTypeProvider(
$this->getTypeReader($php_version),
),
new class ($php_version) implements PointedTypeResolver {
public function __construct(
private string $php_version,
) {
}
public function resolve(string $type_name): string
{
return match ($this->php_version) {
ZendTypeReader::V70,
ZendTypeReader::V71,
ZendTypeReader::V72 => match ($type_name) {
Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V70\Bucket::class,
ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V70\ZendArray::class,
Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V70\Zval::class,
default => $type_name,
},
ZendTypeReader::V73 => match ($type_name) {
Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V73\Bucket::class,
ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V73\ZendArray::class,
Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V73\Zval::class,
default => $type_name,
},
ZendTypeReader::V74 => match ($type_name) {
Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V74\Bucket::class,
ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V74\ZendArray::class,
Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V74\Zval::class,
default => $type_name,
},
ZendTypeReader::V80,
ZendTypeReader::V81 => match ($type_name) {
ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V80\ZendArray::class,
default => $type_name,
},
ZendTypeReader::V82,
ZendTypeReader::V83 => $type_name,
};
}
}
);
}
/** @param TargetPhpSettings<VersionDecided> $target_php_settings */
private function getMainChunkAddress(
ProcessSpecifier $process_specifier,
TargetPhpSettings $target_php_settings,
int $eg_address,
Dereferencer $dereferencer,
): int {
$chunk_address = $this->chunk_finder->findAddress(
$process_specifier,
$target_php_settings,
$eg_address,
$dereferencer,
);
if (is_null($chunk_address)) {
throw new \RuntimeException('chunk address not found');
}
return $chunk_address;
}
/** @param TargetPhpSettings<VersionDecided> $target_php_settings */
public function collectAll(
ProcessSpecifier $process_specifier,
TargetPhpSettings $target_php_settings,
int $eg_address,
int $cg_address,
?MemoryLimitErrorDetails $memory_limit_error_details = null,
): CollectedMemories {
$pid = $process_specifier->pid;
$php_version = $target_php_settings->php_version;
$dereferencer = $this->getDereferencer($pid, $php_version);
$zend_type_reader = $this->zend_type_reader_creator->create($php_version);
$main_chunk_header_pointer = new Pointer(
ZendMmChunk::class,
$this->getMainChunkAddress(
$process_specifier,
$target_php_settings,
$eg_address,
$dereferencer,
),
$zend_type_reader->sizeOf('zend_mm_chunk'),
);
$memory_locations = new MemoryLocations();
$chunk_memory_locations = new MemoryLocations();
$zend_mm_main_chunk = $dereferencer->deref($main_chunk_header_pointer);
foreach ($zend_mm_main_chunk->iterateChunks($dereferencer) as $chunk) {
$chunk_memory_location = ZendMmChunkMemoryLocation::fromZendMmChunk($chunk);
$chunk_memory_locations->add(
$chunk_memory_location
);
}
$huge_memory_locations = new MemoryLocations();
foreach ($zend_mm_main_chunk->heap_slot->iterateHugeList($dereferencer) as $huge_list) {
$huge_memory_locations->add(
ZendMmChunkMemoryLocation::fromZendMmHugeList($huge_list)
);
$memory_locations->add(
ZendMmHugeListMemoryLocation::fromZendMmHugeList($huge_list)
);
}
$memory_get_usage_size = $zend_mm_main_chunk->heap_slot->size;
$memory_get_usage_real_size = $zend_mm_main_chunk->heap_slot->real_size;
$cached_chunks_size = $zend_mm_main_chunk->heap_slot->cached_chunks_count * ZendMmChunk::SIZE;
$eg_pointer = new Pointer(
ZendExecutorGlobals::class,
$eg_address,
$zend_type_reader->sizeOf('zend_executor_globals')
);
$cg_pointer = new Pointer(
ZendCompilerGlobals::class,
$cg_address,
$zend_type_reader->sizeOf('zend_compiler_globals')
);
$compiler_arena_memory_locations = new MemoryLocations();
/** @var ZendCompilerGlobals $cg */
$cg = $dereferencer->deref($cg_pointer);
if ($cg->arena !== null) {
$arena_root = $dereferencer->deref($cg->arena);
foreach ($arena_root->iterateChain($dereferencer) as $arena) {
$compiler_arena_memory_locations->add(
ZendArenaMemoryLocation::fromZendArena($arena)
);
}
}
if ($cg->ast_arena !== null) {
$ast_arena_root = $dereferencer->deref($cg->ast_arena);
foreach ($ast_arena_root->iterateChain($dereferencer) as $ast_arena) {
$compiler_arena_memory_locations->add(
ZendArenaMemoryLocation::fromZendArena($ast_arena)
);
}
}
/** @var ZendExecutorGlobals $eg */
$eg = $dereferencer->deref($eg_pointer);
$vm_stack_memory_locations = new MemoryLocations();
if (!is_null($eg->vm_stack)) {
$vm_stack_curent = $dereferencer->deref($eg->vm_stack);
foreach ($vm_stack_curent->iterateStackChain($dereferencer) as $vm_stack) {
$vm_stack_memory_locations->add(
VmStackMemoryLocation::fromZendVmStack($vm_stack),
);
}
}
$context_pools = ContextPools::createDefault();
$included_files_context = $this->collectIncludedFiles(
$eg->included_files,
$dereferencer,
$memory_locations,
$context_pools,
);
$interned_strings_context = $this->collectInternedStrings(
$cg->interned_strings,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
);
assert(!is_null($eg->function_table));
assert(!is_null($eg->class_table));
assert(!is_null($eg->zend_constants));
$function_table = $dereferencer->deref($eg->function_table);
$class_table = $dereferencer->deref($eg->class_table);
$zend_constants = $dereferencer->deref($eg->zend_constants);
$global_variables_context = $this->collectGlobalVariables(
$eg->symbol_table,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$call_frames_context = $this->collectCallFrames(
$eg,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$defined_functions_context = $this->collectFunctionTable(
$function_table,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$defined_classes_context = $this->collectClassTable(
$class_table,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$global_constants_context = $this->collectGlobalConstants(
$zend_constants,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$objects_store_context = $this->collectObjectsStore(
$eg->objects_store,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if ($memory_limit_error_details and !is_null($this->memory_limit_error_function_context)) {
$call_frames_context = $this->collectRealCallStackOnMemoryLimitViolation(
$this->memory_limit_error_function_context,
$memory_limit_error_details->max_challenge_depth,
$call_frames_context,
$eg,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
);
}
$top_reference_context = new TopReferenceContext(
$call_frames_context,
$global_variables_context,
$defined_functions_context,
$defined_classes_context,
$global_constants_context,
$included_files_context,
$interned_strings_context,
$objects_store_context,
);
return new CollectedMemories(
$chunk_memory_locations,
$huge_memory_locations,
$vm_stack_memory_locations,
$compiler_arena_memory_locations,
$cached_chunks_size,
$memory_locations,
$top_reference_context,
$memory_get_usage_size,
$memory_get_usage_real_size,
);
}
public function collectZval(
Zval $zval,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ?ReferenceContext {
if ($zval->isArray()) {
assert(!is_null($zval->value->arr));
return $this->collectZendArrayPointer(
$zval->value->arr,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
} elseif ($zval->isObject()) {
if ($zval->value->obj === null) {
return null;
}
return $this->collectZendObjectPointer(
$zval->value->obj,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
} elseif ($zval->isString()) {
assert(!is_null($zval->value->str));
return $this->collectZendStringPointer(
$zval->value->str,
$memory_locations,
$dereferencer,
$context_pools,
);
} elseif (
$zval->isBool()
or $zval->isLong()
or $zval->isDouble()
or $zval->isNull()
) {
return match ($zval->getType()) {
'IS_TRUE' => new ScalarValueContext(true),
'IS_FALSE' => new ScalarValueContext(false),
'IS_LONG' => new ScalarValueContext($zval->value->lval),
'IS_DOUBLE' => new ScalarValueContext($zval->value->dval),
'IS_NULL' => new ScalarValueContext(null),
};
} elseif ($zval->isReference()) {
assert(!is_null($zval->value->ref));
return $this->collectPhpReferencePointer(
$zval->value->ref,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
} elseif ($zval->isResource()) {
assert(!is_null($zval->value->res));
return $this->collectResourcePointer(
$zval->value->res,
$memory_locations,
$dereferencer,
$context_pools,
);
} elseif ($zval->isIndirect()) {
$zval = $dereferencer->deref(
$zval->value->getAsPointer(Zval::class, $zend_type_reader->sizeOf('zval'))
);
return $this->collectZval(
$zval,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
return null;
}
/** @param Pointer<ZendResource> $pointer */
public function collectResourcePointer(
Pointer $pointer,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ContextPools $context_pools
): ResourceContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendResourceMemoryLocation) {
return $context_pools
->resource_context_pool
->getContextForLocation($memory_location)
;
}
}
$resource = $dereferencer->deref($pointer);
$memory_location = ZendResourceMemoryLocation::fromZendReference($resource);
$memory_locations->add($memory_location);
return $context_pools
->resource_context_pool
->getContextForLocation($memory_location)
;
}
/** @param Pointer<ZendReference> $pointer */
public function collectPhpReferencePointer(
Pointer $pointer,
int $map_ptr_base,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): PhpReferenceContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendReferenceMemoryLocation) {
return $context_pools
->php_reference_context_pool
->getContextForLocation($memory_location)
;
}
}
$php_reference = $dereferencer->deref($pointer);
$memory_location = ZendReferenceMemoryLocation::fromZendReference($php_reference);
$memory_locations->add($memory_location);
$php_referencecontext = $context_pools
->php_reference_context_pool
->getContextForLocation($memory_location)
;
$zval_context = $this->collectZval(
$php_reference->val,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($zval_context)) {
$php_referencecontext->add('referenced', $zval_context);
}
return $php_referencecontext;
}
/** @param Pointer<ZendArray> $pointer */
public function collectZendArrayPointer(
Pointer $pointer,
int $map_ptr_base,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ArrayHeaderContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendArrayMemoryLocation) {
return $context_pools
->array_context_pool
->getContextForLocation($memory_location)
;
}
}
$array = $dereferencer->deref($pointer);
return $this->collectZendArray(
$array,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
/** @param Pointer<ZendObject> $pointer */
public function collectZendObjectPointer(
Pointer $pointer,
int $map_ptr_base,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ObjectContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendArrayTableOverheadMemoryLocation) {
unset($memory_location);
} else {
assert($memory_location instanceof ZendObjectMemoryLocation);
return $context_pools
->object_context_pool
->getContextForLocation($memory_location)
;
}
}
$obj = $dereferencer->deref($pointer);
return $this->collectZendObject(
$obj,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
/** @param Pointer<ZendString> $pointer */
public function collectZendStringPointer(
Pointer $pointer,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ContextPools $context_pools
): StringContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendArrayTableOverheadMemoryLocation) {
$memory_location = null;
} else {
assert($memory_location instanceof ZendStringMemoryLocation);
}
}
if (!isset($memory_location)) {
$str = $dereferencer->deref($pointer);
$memory_location = ZendStringMemoryLocation::fromZendString(
$str,
$dereferencer,
);
$memory_locations->add($memory_location);
}
assert($memory_location instanceof ZendStringMemoryLocation);
return $context_pools
->string_context_pool
->getContextForLocation($memory_location)
;
}
public function collectCallFrames(
ZendExecutorGlobals $eg,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): CallFramesContext {
$call_frames_context = new CallFramesContext();
if (is_null($eg->current_execute_data)) {
return $call_frames_context;
}
$execute_data = $dereferencer->deref($eg->current_execute_data);
foreach ($execute_data->iterateStackChain($dereferencer) as $key => $execute_data) {
$call_frame_context = $this->collectCallFrame(
$execute_data,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$call_frames_context->add((string)$key, $call_frame_context);
}
return $call_frames_context;
}
public function collectRealCallStackOnMemoryLimitViolation(
UserFunctionDefinitionContext $memory_limit_error_function_context,
int $max_challenge_depth,
CallFramesContext $call_frames_context,
ZendExecutorGlobals $eg,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
): CallFramesContext {
$op_array_address = $memory_limit_error_function_context->getOpArrayAddress();
if (is_null($eg->vm_stack)) {
return $call_frames_context;
}
if (is_null($eg->vm_stack_top)) {
return $call_frames_context;
}
$last_vm_stack = $dereferencer->deref($eg->vm_stack);
$root_vm_stack = $last_vm_stack->getRootStack($dereferencer);
if (is_null($root_vm_stack->top)) {
return $call_frames_context;
}
$first_stack = true;
foreach ($last_vm_stack->iterateStackChain($dereferencer) as $vm_stack) {
if ($first_stack) {
$first_stack = false;
$stack_end_address = $eg->vm_stack_top->address;
} else {
if (is_null($vm_stack->end)) {
break;
}
$stack_end_address = $vm_stack->end->address;
}
$materialized_vm_stack = $vm_stack->materializeAsPointerArray(
$dereferencer,
$stack_end_address
);
foreach ($materialized_vm_stack->getReverseIteratorAsInt() as $key => $value) {
if ($value !== $op_array_address) {
continue;
}
$pointer_address = $key * 8 + $materialized_vm_stack->getPointer()->address - 24;
Log::debug('candidate frame found', ['frame_address' => $pointer_address]);
$frame_candidate = new Pointer(
ZendExecuteData::class,
$pointer_address,
$zend_type_reader->sizeOf('zend_execute_data')
);
try {
$execute_data_candidate = $dereferencer->deref($frame_candidate);
$root_execute_data_candidate = $execute_data_candidate->getRootFrame(
$dereferencer,
$max_challenge_depth,
);
if ($root_vm_stack->top->address !== $root_execute_data_candidate->getPointer()->address) {
continue;
}
Log::debug('root candidate frame found', ['frame_address' => $root_vm_stack->top->address]);
$frame_start = count($call_frames_context->getLinks());
foreach ($execute_data_candidate->iterateStackChain($dereferencer) as $frame_no => $execute_data) {
$call_frame_context = $this->collectCallFrame(
$execute_data,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
null,
);
$call_frames_context->add((string)($frame_no + $frame_start), $call_frame_context);
}
return $call_frames_context;
} catch (\Throwable $e) {
Log::debug(
'failed to collect real call stack from this candidate',
[
'exception' => $e,
'frame_address' => $pointer_address
]
);
continue;
}
}
}
return $call_frames_context;
}
public function collectCallFrame(
ZendExecuteData $execute_data,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): CallFrameContext {
$function_name = $execute_data->getFullyQualifiedFunctionName(
$dereferencer,
$zend_type_reader,
);
$lineno = -1;
if ($execute_data->opline !== null and !$execute_data->isInternalCall($dereferencer)) {
$opline = $dereferencer->deref($execute_data->opline);
$lineno = $opline->lineno;
}
$header_memory_location = CallFrameHeaderMemoryLocation::fromZendExecuteData(
$execute_data,
);
$call_frame_context = new CallFrameContext(
$function_name,
$lineno,
);
$variable_table_memory_location = CallFrameVariableTableMemoryLocation::fromZendExecuteData(
$execute_data,
$dereferencer
);
$memory_locations->add($variable_table_memory_location);
$memory_locations->add($header_memory_location);
if ($execute_data->hasThis()) {
$this_context = $this->collectZval(
$execute_data->This,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($this_context)) {
$call_frame_context->add('this', $this_context);
}
}
$has_local_variables = false;
$variable_table_context = new CallFrameVariableTableContext();
$local_variables_iterator = $execute_data->getVariables($dereferencer, $zend_type_reader);
foreach ($local_variables_iterator as $name => $value) {
$local_variable_context = $this->collectZval(
$value,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($local_variable_context)) {
$variable_table_context->add($name, $local_variable_context);
$has_local_variables = true;
}
}
if ($has_local_variables) {
$call_frame_context->add('local_variables', $variable_table_context);
}
if ($execute_data->hasSymbolTable() and !is_null($execute_data->symbol_table)) {
$symbol_table_context = $this->collectZendArrayPointer(
$execute_data->symbol_table,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
$call_frame_context->add('symbol_table', $symbol_table_context);
}
if ($execute_data->hasExtraNamedParams() and !is_null($execute_data->extra_named_params)) {
$extra_named_params_context = $this->collectZendArrayPointer(
$execute_data->extra_named_params,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
$call_frame_context->add('extra_named_params', $extra_named_params_context);
}
return $call_frame_context;
}
public function collectGlobalVariables(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): GlobalVariablesContext {
return GlobalVariablesContext::fromArrayContext(
$this->collectZendArray(
$array,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
)
);
}
public function collectZendArray(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ArrayHeaderContext {
$array_header_location = ZendArrayMemoryLocation::fromZendArray($array);
$memory_locations->add($array_header_location);
$array_header_context = $context_pools
->array_context_pool
->getContextForLocation($array_header_location)
;
if (is_null($array->arData)) {
return $array_header_context;
}
$array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
$array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
$array,
$array_table_location
);
$memory_locations->add($array_table_location);
$memory_locations->add($array_table_overhead_location);
$array_context = new ArrayElementsContext($array_table_location);
$overhead_context = new ArrayPossibleOverheadContext($array_table_overhead_location);
foreach ($array->getItemIteratorWithZendStringKeyIfAssoc($dereferencer) as $key => $zval) {
$element_context = new ArrayElementContext();
if ($key instanceof Pointer) {
$key_context = $this->collectZendStringPointer(
$key,
$memory_locations,
$dereferencer,
$context_pools,
);
$zend_string = $dereferencer->deref($key);
$key_string = $zend_string->toString($dereferencer);
$element_context->add('key', $key_context);
} else {
$key_string = (string)$key;
}
$array_context->add($key_string, $element_context);
$value_context = $this->collectZval(
$zval,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($value_context)) {
$element_context->add('value', $value_context);
}
}
$array_header_context->add('possible_unused_area', $overhead_context);
$array_header_context->add('array_elements', $array_context);
return $array_header_context;
}
public function collectZendObject(
ZendObject $object,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ObjectContext {
$object_location = ZendObjectMemoryLocation::fromZendObject(
$object,
$dereferencer,
$zend_type_reader,
);
$object_handlers_memory_location = ZendObjectHandlersMemoryLocation::fromZendObject(
$object,
$zend_type_reader,
);
$memory_locations->add($object_location);
$memory_locations->add($object_handlers_memory_location);
$object_context = $context_pools
->object_context_pool->getContextForLocation(
$object_location,
)
;
$object_handlers_context = $context_pools
->object_context_pool
->getHandlersContextForLocation(
$object_handlers_memory_location,
)
;
$object_context->add('object_handlers', $object_handlers_context);
$properties_exists = false;
$object_properties_context = new ObjectPropertiesContext();
$properties_iterator = $object->getPropertiesIterator(
$dereferencer,
$zend_type_reader,
);
foreach ($properties_iterator as $name => $property) {
assert(is_string($name));
$property_context = $this->collectZval(
$property,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($property_context)) {
$object_properties_context->add($name, $property_context);
$properties_exists = true;
}
}
if ($properties_exists) {
$object_context->add('object_properties', $object_properties_context);
}
if (
!is_null($object->properties)
and !is_null($object->ce)
and !$object->isEnum($dereferencer)
) {
$dynamic_properties_context = $this->collectZendArray(
$dereferencer->deref($object->properties),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$object_context->add('dynamic_properties', $dynamic_properties_context);
}
assert(!is_null($object->ce));
$class_entry = $dereferencer->deref($object->ce);
if (
$class_entry->getClassName($dereferencer) === 'Closure'
and !$zend_type_reader->isPhpVersionLowerThan(ZendTypeReader::V71)
) {
$closure_context = $this->collectClosure(
$dereferencer->deref(
ZendClosure::getPointerFromZendObjectPointer(
$object->getPointer(),
$zend_type_reader,
),
),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$object_context->add('closure', $closure_context);
}
return $object_context;
}
public function collectClosure(
ZendClosure $zend_closure,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ClosureContext {
$closure_context = new ClosureContext();
$closure_context->add(
'func',
$this->collectZendFunctionPointer(
$zend_closure->func->getPointer(),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
)
);
$zval_context = $this->collectZval(
$zend_closure->this_ptr,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($zval_context)) {
$closure_context->add(
'this_ptr',
$zval_context,
);
}
return $closure_context;
}
public function collectFunctionTable(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): DefinedFunctionsContext {
$array_header_location = ZendArrayMemoryLocation::fromZendArray($array);
$array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
$array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
$array,
$array_table_location
);
$memory_locations->add($array_header_location);
$memory_locations->add($array_table_location);
$memory_locations->add($array_table_overhead_location);
$defined_functions_context = new DefinedFunctionsContext(
$array_header_location,
$array_table_location,
);
foreach ($array->getItemIterator($dereferencer) as $function_name => $zval) {
assert(is_string($function_name));
assert(!is_null($zval->value->func));
$function_context = $this->collectZendFunctionPointer(
$zval->value->func,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$defined_functions_context->add($function_name, $function_context);
}
return $defined_functions_context;
}
/** @param Pointer<ZendFunction> $pointer */
public function collectZendFunctionPointer(
Pointer $pointer,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): FunctionDefinitionContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendOpArrayHeaderMemoryLocation) {
return $context_pools
->user_function_definition_context_pool
->getContextForLocation($memory_location)
;
}
}
$func = $dereferencer->deref($pointer);
return $this->collectZendFunction(
$func,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
public function collectZendFunction(
ZendFunction $func,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): FunctionDefinitionContext {
if ($func->isUserFunction()) {
$function_definition_context = $this->collectUserFunctionDefinition(
$func,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
} else {
$function_definition_context = new InternalFunctionDefinitionContext();
}
if (!is_null($func->function_name)) {
$function_name_context = $this->collectZendStringPointer(
$func->function_name,
$memory_locations,
$dereferencer,
$context_pools,
);
$function_definition_context->add('name', $function_name_context);
}
return $function_definition_context;
}
public function collectUserFunctionDefinition(
ZendFunction $func,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): UserFunctionDefinitionContext {
$function_name = $func->getFullyQualifiedFunctionName(
$dereferencer,
$zend_type_reader,
);
$op_array_header_memory_location = ZendOpArrayHeaderMemoryLocation::fromZendFunction(
$func,
$zend_type_reader,
$dereferencer,
);
$op_array_body_memory_location = ZendOpArrayBodyMemoryLocation::fromZendFunction(
$func,
$zend_type_reader,
$function_name
);
$memory_locations->add($op_array_header_memory_location);
$memory_locations->add($op_array_body_memory_location);
$function_definition_context = $context_pools
->user_function_definition_context_pool
->getContextForLocation($op_array_header_memory_location)
;
$op_array_context = new OpArrayContext(
$op_array_header_memory_location,
$op_array_body_memory_location,
);
$function_definition_context->add('op_array', $op_array_context);
if ($func->op_array->cache_size > 0) {
$runtime_cache_memory_location = RuntimeCacheMemoryLocation::fromZendOpArray(
$func->op_array,
$dereferencer,
$zend_type_reader,
$map_ptr_base,
);
if ($runtime_cache_memory_location->address !== 0) {
$memory_locations->add($runtime_cache_memory_location);
$run_time_cache_context = new RuntimeCacheContext($runtime_cache_memory_location);
$op_array_context->add('run_time_cache', $run_time_cache_context);
}
}
if (!is_null($func->op_array->arg_info)) {
$arginfos_memory_location = ZendArgInfosMemoryLocation::fromZendOpArray(
$func->op_array,
$zend_type_reader,
);
$memory_locations->add($arginfos_memory_location);
$arginfos_context = new ArgInfosContext($arginfos_memory_location);
$op_array_context->add('arg_infos', $arginfos_context);
foreach ($func->op_array->iterateArgInfo($dereferencer, $zend_type_reader) as $arg_info) {
if (!is_null($arg_info->name)) {
$arg_info_context = new ArgInfoContext();
$arg_info_name_context = $this->collectZendStringPointer(
$arg_info->name,
$memory_locations,
$dereferencer,
$context_pools,
);
$arg_info_name = $dereferencer
->deref($arg_info->name)
->toString($dereferencer)
;
$arg_info_context->add('name', $arg_info_name_context);
$arginfos_context->add($arg_info_name, $arg_info_context);
}
}
}
if (!is_null($func->op_array->doc_comment)) {
$doc_comment_context = $this->collectZendStringPointer(
$func->op_array->doc_comment,
$memory_locations,
$dereferencer,
$context_pools,
);
$op_array_context->add('doc_comment', $doc_comment_context);
}
if (!is_null($func->op_array->filename)) {
$file_name_context = $this->collectZendStringPointer(
$func->op_array->filename,
$memory_locations,
$dereferencer,
$context_pools,
);
$op_array_context->add('filename', $file_name_context);
}
if (!is_null($func->op_array->static_variables)) {
$static_variables_context = $this->collectZendArray(
$dereferencer->deref($func->op_array->static_variables),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$op_array_context->add('static_variables', $static_variables_context);
}
if (!is_null($func->op_array->vars)) {
$local_variable_name_table_location = LocalVariableNameTableMemoryLocation::fromZendOpArray(
$func->op_array
);
$memory_locations->add($local_variable_name_table_location);
$variable_name_table_context = new LocalVariableNameTableContext(
$local_variable_name_table_location
);
$op_array_context->add('variable_name_table', $variable_name_table_context);
$variable_names_iterator = $func->op_array->getVariableNamesAsIteratorOfPointersToZendStrings(
$dereferencer,
$zend_type_reader,
);
foreach ($variable_names_iterator as $key => $variable_name) {
$variable_name_context = $this->collectZendStringPointer(
$variable_name,
$memory_locations,
$dereferencer,
$context_pools,
);
$variable_name_table_context->add((string)$key, $variable_name_context);
}
}
if ($func->op_array->num_dynamic_func_defs > 0) {
$dynamic_func_defs_table_memory_location = DynamicFuncDefsTableMemoryLocation::fromZendOpArray(
$func->op_array,
);
$memory_locations->add($dynamic_func_defs_table_memory_location);
$dynamic_func_defs_context = new DynamicFuncDefsContext(
$dynamic_func_defs_table_memory_location
);
$dynamic_func_defs_iterator = $func->op_array->iterateDynamicFunctionDefinitions(
$dereferencer,
$zend_type_reader,
);
foreach ($dynamic_func_defs_iterator as $key => $dynamic_func_def) {
$dynamic_function_context = $this->collectZendFunctionPointer(
$dynamic_func_def,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$dynamic_func_defs_context->add((string)$key, $dynamic_function_context);
}
$op_array_context->add('dynamic_function_definitions', $dynamic_func_defs_context);
}
if (!is_null($memory_limit_error_details)) {
if (
$function_definition_context->isThisContext(
$memory_limit_error_details->file,
$memory_limit_error_details->line,
)
) {
if (
is_null($this->memory_limit_error_function_context)
or $function_definition_context->isClosureOf($this->memory_limit_error_function_context)
) {
$this->memory_limit_error_function_context = $function_definition_context;
}
}
}
return $function_definition_context;
}
public function collectClassConstantsTable(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ClassConstantsContext {
$array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
$array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
$array,
$array_table_location
);
$memory_locations->add($array_table_location);
$memory_locations->add($array_table_overhead_location);
$class_constants_context = new ClassConstantsContext($array_table_location);
$array_iterator = $array->getItemIteratorWithZendStringKeyIfAssoc($dereferencer);
foreach ($array_iterator as $constant_name => $zval_or_ptr) {
assert($constant_name instanceof Pointer);
$constant_name_context = $this->collectZendStringPointer(
$constant_name,
$memory_locations,
$dereferencer,
$context_pools,
);
$zend_string = $dereferencer->deref($constant_name);
$constant_name_string = $zend_string->toString($dereferencer);
$constant_context = new ClassConstantContext();
$class_constants_context->add($constant_name_string, $constant_context);
$constant_context->add('name', $constant_name_context);
if ($zend_type_reader->isPhpVersionLowerThan(ZendTypeReader::V71)) {
$zval = $zval_or_ptr;
} else {
$class_constant_ptr = $zval_or_ptr->value->getAsPointer(
ZendClassConstant::class,
$zend_type_reader->sizeOf(ZendClassConstant::getCTypeName()),
);
$class_constant = $dereferencer->deref(
$class_constant_ptr
);
$memory_location = ZendClassConstantMemoryLocation::fromZendClassConstant(
$class_constant,
);
$memory_locations->add($memory_location);
$zval = $class_constant->value;
$info_context = new ClassConstantInfoContext($memory_location);
$constant_context->add('info', $info_context);
}
$value_context = $this->collectZval(
$zval,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($value_context)) {
$constant_context->add('value', $value_context);
}
}
return $class_constants_context;
}
public function collectClassTable(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): DefinedClassesContext {
$defined_classes_context = new DefinedClassesContext();
foreach ($array->getItemIterator($dereferencer) as $class_name => $zval) {
assert(!is_null($zval->value->ce));
$class_definition_context = $this->collectClassDefinitionPointer(
$zval->value->ce,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($class_definition_context)) {
$defined_classes_context->add((string)$class_name, $class_definition_context);
}
}
return $defined_classes_context;
}
/** @param Pointer<ZendClassEntry> $pointer */
private function collectClassDefinitionPointer(
Pointer $pointer,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ?ClassDefinitionContext {
if ($memory_locations->has($pointer->address)) {
return null;
}
$class_entry = $dereferencer->deref($pointer);
return $this->collectClassDefinition(
$class_entry,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
private function collectClassDefinition(
ZendClassEntry $class_entry,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ClassDefinitionContext {
$class_definition_context = new ClassDefinitionContext($class_entry->isInternal());
$memory_location = ZendClassEntryMemoryLocation::fromZendClassEntry($class_entry);
$memory_locations->add($memory_location);
$class_entry_context = new ClassEntryContext($memory_location);
$class_definition_context->add('class_entry', $class_entry_context);
$class_name_context = $this->collectZendStringPointer(
$class_entry->name,
$memory_locations,
$dereferencer,
$context_pools,
);
$class_definition_context->add('name', $class_name_context);
if (!$class_entry->isInternal()) {
if (!is_null($class_entry->info->user->filename)) {
$file_name_context = $this->collectZendStringPointer(
$class_entry->info->user->filename,
$memory_locations,
$dereferencer,
$context_pools,
);
$class_definition_context->add('filename', $file_name_context);
}
if (!is_null($class_entry->info->user->doc_comment)) {
$doc_comment_context = $this->collectZendStringPointer(
$class_entry->info->user->doc_comment,
$memory_locations,
$dereferencer,
$context_pools,
);
$class_definition_context->add('doc_comment', $doc_comment_context);
}
}
if (
$class_entry->default_static_members_count > 0
and !is_null($class_entry->static_members_table)
) {
$static_members_table_memory_location = StaticMembersTableMemoryLocation::fromZendClassEntry(
$class_entry,
$zend_type_reader,
$dereferencer,
$map_ptr_base,
);
$memory_locations->add($static_members_table_memory_location);
$static_properties_context = new ClassStaticPropertiesContext(
$static_members_table_memory_location
);
$static_property_iterator = $class_entry->getStaticPropertyIterator(
$dereferencer,
$zend_type_reader,
$map_ptr_base,
);
foreach ($static_property_iterator as $name => $value) {
$static_property_context = $this->collectZval(
$value,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($static_property_context)) {
$static_properties_context->add($name, $static_property_context);
}
}
$class_definition_context->add('static_properties', $static_properties_context);
}
$properties_info_context = $this->collectPropertiesInfo(
$class_entry,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
);
$class_definition_context->add('property_info', $properties_info_context);
if (!is_null($class_entry->default_properties_table)) {
$default_properties_table_memory_location = DefaultPropertiesTableMemoryLocation::fromZendClassEntry(
$class_entry,
);
$memory_locations->add($default_properties_table_memory_location);
$default_properties_context = new DefaultPropertiesTableContext(
$default_properties_table_memory_location
);
$class_definition_context->add('default_properties', $default_properties_context);
}
if (!is_null($class_entry->default_static_members_table)) {
$default_static_members_memory_location = DefaultStaticMembersTableMemoryLocation::fromZendClassEntry(
$class_entry,
);
$memory_locations->add($default_static_members_memory_location);
$default_static_properties_context = new DefaultStaticPropertiesContext(
$default_static_members_memory_location
);
$class_definition_context->add('default_static_properties', $default_static_properties_context);
}
$methods_context = $this->collectFunctionTable(
$dereferencer->deref($class_entry->function_table->getPointer()),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$class_definition_context->add('methods', $methods_context);
$class_constants_context = $this->collectClassConstantsTable(
$dereferencer->deref($class_entry->constants_table->getPointer()),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$class_definition_context->add('constants', $class_constants_context);
return $class_definition_context;
}
private function collectPropertiesInfo(
ZendClassEntry $class_entry,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools
): PropertiesInfoContext {
$memory_location = ZendArrayTableMemoryLocation::fromZendArray($class_entry->properties_info);
$memory_locations->add($memory_location);
$properties_info_context = new PropertiesInfoContext($memory_location);
foreach ($class_entry->iteratePropertyInfo($dereferencer, $zend_type_reader) as $key => $property_info) {
$property_info_memory_location = ZendPropertyInfoMemoryLocation::fromZendPropertyInfo(
$property_info,
);
$memory_locations->add($property_info_memory_location);
$property_info_context = new PropertyInfoContext($property_info_memory_location);
if (!is_null($property_info->name)) {
$property_info_name_context = $this->collectZendStringPointer(
$property_info->name,
$memory_locations,
$dereferencer,
$context_pools,
);
$property_info_context->add('name', $property_info_name_context);
}
if (!is_null($property_info->doc_comment)) {
$property_info_doc_comment_context = $this->collectZendStringPointer(
$property_info->doc_comment,
$memory_locations,
$dereferencer,
$context_pools,
);
$property_info_context->add('doc_comment', $property_info_doc_comment_context);
}
$properties_info_context->add($key, $property_info_context);
}
return $properties_info_context;
}
private function collectGlobalConstants(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): GlobalConstantsContext {
$memory_location = ZendArrayTableMemoryLocation::fromZendArray($array);
$memory_locations->add($memory_location);
$global_constants_context = new GlobalConstantsContext($memory_location);
foreach ($array->getItemIterator($dereferencer) as $constant_name => $zval) {
assert(is_string($constant_name));
$zend_constant = $dereferencer->deref(
$zval->value->getAsPointer(
ZendConstant::class,
$zend_type_reader->sizeOf(ZendConstant::getCTypeName()),
)
);
$zend_constant_memory_location = ZendConstantMemoryLocation::fromZendConstant(
$zend_constant
);
$constant_context = new GlobalConstantContext($zend_constant_memory_location);
$global_constants_context->add($constant_name, $constant_context);
if (!is_null($zend_constant->name)) {
$constant_name_context = $this->collectZendStringPointer(
$zend_constant->name,
$memory_locations,
$dereferencer,
$context_pools,
);
$constant_context->add('name', $constant_name_context);
}
$value_context = $this->collectZval(
$zend_constant->value,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($value_context)) {
$constant_context->add('value', $value_context);
}
}
return $global_constants_context;
}
private function collectIncludedFiles(
ZendArray $included_files,
Dereferencer $dereferencer,
MemoryLocations $memory_locations,
ContextPools $context_pools
): IncludedFilesContext {
$array_table_location = ZendArrayTableMemoryLocation::fromZendArray($included_files);
$included_files_context = new IncludedFilesContext($array_table_location);
$memory_locations->add($array_table_location);
$iterator = $included_files->getItemIteratorWithZendStringKeyIfAssoc($dereferencer);
foreach ($iterator as $filename => $_) {
assert($filename instanceof Pointer);
$raw_string = $dereferencer->deref($filename)->toString($dereferencer);
$included_file_context = $this->collectZendStringPointer(
$filename,
$memory_locations,
$dereferencer,
$context_pools,
);
$included_files_context->add($raw_string, $included_file_context);
}
return $included_files_context;
}
private function collectInternedStrings(
ZendArray $interned_string,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools
): ArrayHeaderContext {
return $this->collectZendArray(
$interned_string,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
null
);
}
private function collectObjectsStore(
ZendObjectsStore $objects_store,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ObjectsStoreContext {
$objects_store_memory_location = ObjectsStoreMemoryLocation::fromZendObjectsStore(
$objects_store,
);
$objects_store_context = new ObjectsStoreContext($objects_store_memory_location);
$memory_locations->add($objects_store_memory_location);
assert($objects_store->object_buckets instanceof Pointer);
$buckets = $dereferencer->deref($objects_store->object_buckets);
$bucket_iterator = $buckets->getIteratorOfPointersTo(
ZendObject::class,
$zend_type_reader,
);
foreach ($bucket_iterator as $key => $bucket) {
if ($key === 0) {
continue;
}
if ($bucket->address & 1) {
continue;
}
if ($bucket->address === 0) {
continue;
}
if ($key >= $objects_store->top) {
break;
}
$objects_store_bucket_context = $this->collectZendObjectPointer(
$bucket,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
$objects_store_context->add((string)$key, $objects_store_bucket_context);
}
return $objects_store_context;
}
}
?>
Did this file decode correctly?
Original Code
<?php
/**
* This file is part of the reliforp/reli-prof package.
*
* (c) sji <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Reli\Lib\PhpProcessReader\PhpMemoryReader;
use Reli\Inspector\Settings\MemoryProfilerSettings\MemoryLimitErrorDetails;
use Reli\Inspector\Settings\TargetPhpSettings\TargetPhpSettings;
use Reli\Lib\Log\Log;
use Reli\Lib\PhpInternals\Types\Zend\Bucket;
use Reli\Lib\PhpInternals\Types\Zend\ZendArray;
use Reli\Lib\PhpInternals\Types\Zend\ZendCastedTypeProvider;
use Reli\Lib\PhpInternals\Types\Zend\ZendClassConstant;
use Reli\Lib\PhpInternals\Types\Zend\ZendClassEntry;
use Reli\Lib\PhpInternals\Types\Zend\ZendClosure;
use Reli\Lib\PhpInternals\Types\Zend\ZendCompilerGlobals;
use Reli\Lib\PhpInternals\Types\Zend\ZendConstant;
use Reli\Lib\PhpInternals\Types\Zend\ZendExecuteData;
use Reli\Lib\PhpInternals\Types\Zend\ZendExecutorGlobals;
use Reli\Lib\PhpInternals\Types\Zend\ZendFunction;
use Reli\Lib\PhpInternals\Types\Zend\ZendMmChunk;
use Reli\Lib\PhpInternals\Types\Zend\ZendObject;
use Reli\Lib\PhpInternals\Types\Zend\ZendObjectsStore;
use Reli\Lib\PhpInternals\Types\Zend\ZendReference;
use Reli\Lib\PhpInternals\Types\Zend\ZendResource;
use Reli\Lib\PhpInternals\Types\Zend\ZendString;
use Reli\Lib\PhpInternals\Types\Zend\Zval;
use Reli\Lib\PhpInternals\ZendTypeReader;
use Reli\Lib\PhpInternals\ZendTypeReaderCreator;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\CallFrameHeaderMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\CallFrameVariableTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DefaultPropertiesTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DefaultStaticMembersTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\DynamicFuncDefsTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\LocalVariableNameTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\MemoryLocations;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ObjectsStoreMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\RuntimeCacheMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\StaticMembersTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\VmStackMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArenaMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArgInfosMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayTableMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendArrayTableOverheadMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendClassConstantMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendClassEntryMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendConstantMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendMmChunkMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendMmHugeListMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendObjectHandlersMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendObjectMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendOpArrayBodyMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendOpArrayHeaderMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendPropertyInfoMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendReferenceMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendResourceMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\MemoryLocation\ZendStringMemoryLocation;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArgInfoContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArgInfosContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayElementContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayElementsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayHeaderContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ArrayPossibleOverheadContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFrameContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFramesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\CallFrameVariableTableContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantInfoContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassConstantsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassDefinitionContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassEntryContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClassStaticPropertiesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ClosureContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefaultPropertiesTableContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefaultStaticPropertiesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefinedClassesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DefinedFunctionsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\DynamicFuncDefsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\FunctionDefinitionContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalConstantContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalConstantsContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\GlobalVariablesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\IncludedFilesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\InternalFunctionDefinitionContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\LocalVariableNameTableContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectPropertiesContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ObjectsStoreContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\OpArrayContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PhpReferenceContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PropertiesInfoContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\PropertyInfoContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ReferenceContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ResourceContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\RuntimeCacheContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\ScalarValueContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\StringContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\TopReferenceContext;
use Reli\Lib\PhpProcessReader\PhpMemoryReader\ReferenceContext\UserFunctionDefinitionContext;
use Reli\Lib\PhpProcessReader\PhpZendMemoryManagerChunkFinder;
use Reli\Lib\Process\MemoryReader\MemoryReaderInterface;
use Reli\Lib\Process\Pointer\Dereferencer;
use Reli\Lib\Process\Pointer\PointedTypeResolver;
use Reli\Lib\Process\Pointer\Pointer;
use Reli\Lib\Process\Pointer\RemoteProcessDereferencer;
use Reli\Lib\Process\ProcessSpecifier;
/** @psalm-import-type VersionDecided from TargetPhpSettings */
final class MemoryLocationsCollector
{
private ?ZendTypeReader $zend_type_reader = null;
private ?UserFunctionDefinitionContext $memory_limit_error_function_context = null;
public function __construct(
private MemoryReaderInterface $memory_reader,
private ZendTypeReaderCreator $zend_type_reader_creator,
private PhpZendMemoryManagerChunkFinder $chunk_finder,
) {
}
/**
* @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
*/
public function getTypeReader(string $php_version): ZendTypeReader
{
if (is_null($this->zend_type_reader)) {
$this->zend_type_reader = $this->zend_type_reader_creator->create($php_version);
}
return $this->zend_type_reader;
}
/**
* @param value-of<ZendTypeReader::ALL_SUPPORTED_VERSIONS> $php_version
*/
private function getDereferencer(int $pid, string $php_version): Dereferencer
{
return new RemoteProcessDereferencer(
$this->memory_reader,
new ProcessSpecifier($pid),
new ZendCastedTypeProvider(
$this->getTypeReader($php_version),
),
new class ($php_version) implements PointedTypeResolver {
public function __construct(
private string $php_version,
) {
}
public function resolve(string $type_name): string
{
return match ($this->php_version) {
ZendTypeReader::V70,
ZendTypeReader::V71,
ZendTypeReader::V72 => match ($type_name) {
Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V70\Bucket::class,
ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V70\ZendArray::class,
Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V70\Zval::class,
default => $type_name,
},
ZendTypeReader::V73 => match ($type_name) {
Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V73\Bucket::class,
ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V73\ZendArray::class,
Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V73\Zval::class,
default => $type_name,
},
ZendTypeReader::V74 => match ($type_name) {
Bucket::class => \Reli\Lib\PhpInternals\Types\Zend\V74\Bucket::class,
ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V74\ZendArray::class,
Zval::class => \Reli\Lib\PhpInternals\Types\Zend\V74\Zval::class,
default => $type_name,
},
ZendTypeReader::V80,
ZendTypeReader::V81 => match ($type_name) {
ZendArray::class => \Reli\Lib\PhpInternals\Types\Zend\V80\ZendArray::class,
default => $type_name,
},
ZendTypeReader::V82,
ZendTypeReader::V83 => $type_name,
};
}
}
);
}
/** @param TargetPhpSettings<VersionDecided> $target_php_settings */
private function getMainChunkAddress(
ProcessSpecifier $process_specifier,
TargetPhpSettings $target_php_settings,
int $eg_address,
Dereferencer $dereferencer,
): int {
$chunk_address = $this->chunk_finder->findAddress(
$process_specifier,
$target_php_settings,
$eg_address,
$dereferencer,
);
if (is_null($chunk_address)) {
throw new \RuntimeException('chunk address not found');
}
return $chunk_address;
}
/** @param TargetPhpSettings<VersionDecided> $target_php_settings */
public function collectAll(
ProcessSpecifier $process_specifier,
TargetPhpSettings $target_php_settings,
int $eg_address,
int $cg_address,
?MemoryLimitErrorDetails $memory_limit_error_details = null,
): CollectedMemories {
$pid = $process_specifier->pid;
$php_version = $target_php_settings->php_version;
$dereferencer = $this->getDereferencer($pid, $php_version);
$zend_type_reader = $this->zend_type_reader_creator->create($php_version);
$main_chunk_header_pointer = new Pointer(
ZendMmChunk::class,
$this->getMainChunkAddress(
$process_specifier,
$target_php_settings,
$eg_address,
$dereferencer,
),
$zend_type_reader->sizeOf('zend_mm_chunk'),
);
$memory_locations = new MemoryLocations();
$chunk_memory_locations = new MemoryLocations();
$zend_mm_main_chunk = $dereferencer->deref($main_chunk_header_pointer);
foreach ($zend_mm_main_chunk->iterateChunks($dereferencer) as $chunk) {
$chunk_memory_location = ZendMmChunkMemoryLocation::fromZendMmChunk($chunk);
$chunk_memory_locations->add(
$chunk_memory_location
);
}
$huge_memory_locations = new MemoryLocations();
foreach ($zend_mm_main_chunk->heap_slot->iterateHugeList($dereferencer) as $huge_list) {
$huge_memory_locations->add(
ZendMmChunkMemoryLocation::fromZendMmHugeList($huge_list)
);
$memory_locations->add(
ZendMmHugeListMemoryLocation::fromZendMmHugeList($huge_list)
);
}
$memory_get_usage_size = $zend_mm_main_chunk->heap_slot->size;
$memory_get_usage_real_size = $zend_mm_main_chunk->heap_slot->real_size;
$cached_chunks_size = $zend_mm_main_chunk->heap_slot->cached_chunks_count * ZendMmChunk::SIZE;
$eg_pointer = new Pointer(
ZendExecutorGlobals::class,
$eg_address,
$zend_type_reader->sizeOf('zend_executor_globals')
);
$cg_pointer = new Pointer(
ZendCompilerGlobals::class,
$cg_address,
$zend_type_reader->sizeOf('zend_compiler_globals')
);
$compiler_arena_memory_locations = new MemoryLocations();
/** @var ZendCompilerGlobals $cg */
$cg = $dereferencer->deref($cg_pointer);
if ($cg->arena !== null) {
$arena_root = $dereferencer->deref($cg->arena);
foreach ($arena_root->iterateChain($dereferencer) as $arena) {
$compiler_arena_memory_locations->add(
ZendArenaMemoryLocation::fromZendArena($arena)
);
}
}
if ($cg->ast_arena !== null) {
$ast_arena_root = $dereferencer->deref($cg->ast_arena);
foreach ($ast_arena_root->iterateChain($dereferencer) as $ast_arena) {
$compiler_arena_memory_locations->add(
ZendArenaMemoryLocation::fromZendArena($ast_arena)
);
}
}
/** @var ZendExecutorGlobals $eg */
$eg = $dereferencer->deref($eg_pointer);
$vm_stack_memory_locations = new MemoryLocations();
if (!is_null($eg->vm_stack)) {
$vm_stack_curent = $dereferencer->deref($eg->vm_stack);
foreach ($vm_stack_curent->iterateStackChain($dereferencer) as $vm_stack) {
$vm_stack_memory_locations->add(
VmStackMemoryLocation::fromZendVmStack($vm_stack),
);
}
}
$context_pools = ContextPools::createDefault();
$included_files_context = $this->collectIncludedFiles(
$eg->included_files,
$dereferencer,
$memory_locations,
$context_pools,
);
$interned_strings_context = $this->collectInternedStrings(
$cg->interned_strings,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
);
assert(!is_null($eg->function_table));
assert(!is_null($eg->class_table));
assert(!is_null($eg->zend_constants));
$function_table = $dereferencer->deref($eg->function_table);
$class_table = $dereferencer->deref($eg->class_table);
$zend_constants = $dereferencer->deref($eg->zend_constants);
$global_variables_context = $this->collectGlobalVariables(
$eg->symbol_table,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$call_frames_context = $this->collectCallFrames(
$eg,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$defined_functions_context = $this->collectFunctionTable(
$function_table,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$defined_classes_context = $this->collectClassTable(
$class_table,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$global_constants_context = $this->collectGlobalConstants(
$zend_constants,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$objects_store_context = $this->collectObjectsStore(
$eg->objects_store,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if ($memory_limit_error_details and !is_null($this->memory_limit_error_function_context)) {
$call_frames_context = $this->collectRealCallStackOnMemoryLimitViolation(
$this->memory_limit_error_function_context,
$memory_limit_error_details->max_challenge_depth,
$call_frames_context,
$eg,
$cg->map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
);
}
$top_reference_context = new TopReferenceContext(
$call_frames_context,
$global_variables_context,
$defined_functions_context,
$defined_classes_context,
$global_constants_context,
$included_files_context,
$interned_strings_context,
$objects_store_context,
);
return new CollectedMemories(
$chunk_memory_locations,
$huge_memory_locations,
$vm_stack_memory_locations,
$compiler_arena_memory_locations,
$cached_chunks_size,
$memory_locations,
$top_reference_context,
$memory_get_usage_size,
$memory_get_usage_real_size,
);
}
public function collectZval(
Zval $zval,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ?ReferenceContext {
if ($zval->isArray()) {
assert(!is_null($zval->value->arr));
return $this->collectZendArrayPointer(
$zval->value->arr,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
} elseif ($zval->isObject()) {
if ($zval->value->obj === null) {
return null;
}
return $this->collectZendObjectPointer(
$zval->value->obj,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
} elseif ($zval->isString()) {
assert(!is_null($zval->value->str));
return $this->collectZendStringPointer(
$zval->value->str,
$memory_locations,
$dereferencer,
$context_pools,
);
} elseif (
$zval->isBool()
or $zval->isLong()
or $zval->isDouble()
or $zval->isNull()
) {
return match ($zval->getType()) {
'IS_TRUE' => new ScalarValueContext(true),
'IS_FALSE' => new ScalarValueContext(false),
'IS_LONG' => new ScalarValueContext($zval->value->lval),
'IS_DOUBLE' => new ScalarValueContext($zval->value->dval),
'IS_NULL' => new ScalarValueContext(null),
};
} elseif ($zval->isReference()) {
assert(!is_null($zval->value->ref));
return $this->collectPhpReferencePointer(
$zval->value->ref,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
} elseif ($zval->isResource()) {
assert(!is_null($zval->value->res));
return $this->collectResourcePointer(
$zval->value->res,
$memory_locations,
$dereferencer,
$context_pools,
);
} elseif ($zval->isIndirect()) {
$zval = $dereferencer->deref(
$zval->value->getAsPointer(Zval::class, $zend_type_reader->sizeOf('zval'))
);
return $this->collectZval(
$zval,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
return null;
}
/** @param Pointer<ZendResource> $pointer */
public function collectResourcePointer(
Pointer $pointer,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ContextPools $context_pools
): ResourceContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendResourceMemoryLocation) {
return $context_pools
->resource_context_pool
->getContextForLocation($memory_location)
;
}
}
$resource = $dereferencer->deref($pointer);
$memory_location = ZendResourceMemoryLocation::fromZendReference($resource);
$memory_locations->add($memory_location);
return $context_pools
->resource_context_pool
->getContextForLocation($memory_location)
;
}
/** @param Pointer<ZendReference> $pointer */
public function collectPhpReferencePointer(
Pointer $pointer,
int $map_ptr_base,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): PhpReferenceContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendReferenceMemoryLocation) {
return $context_pools
->php_reference_context_pool
->getContextForLocation($memory_location)
;
}
}
$php_reference = $dereferencer->deref($pointer);
$memory_location = ZendReferenceMemoryLocation::fromZendReference($php_reference);
$memory_locations->add($memory_location);
$php_referencecontext = $context_pools
->php_reference_context_pool
->getContextForLocation($memory_location)
;
$zval_context = $this->collectZval(
$php_reference->val,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($zval_context)) {
$php_referencecontext->add('referenced', $zval_context);
}
return $php_referencecontext;
}
/** @param Pointer<ZendArray> $pointer */
public function collectZendArrayPointer(
Pointer $pointer,
int $map_ptr_base,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ArrayHeaderContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendArrayMemoryLocation) {
return $context_pools
->array_context_pool
->getContextForLocation($memory_location)
;
}
}
$array = $dereferencer->deref($pointer);
return $this->collectZendArray(
$array,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
/** @param Pointer<ZendObject> $pointer */
public function collectZendObjectPointer(
Pointer $pointer,
int $map_ptr_base,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ObjectContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendArrayTableOverheadMemoryLocation) {
unset($memory_location);
} else {
assert($memory_location instanceof ZendObjectMemoryLocation);
return $context_pools
->object_context_pool
->getContextForLocation($memory_location)
;
}
}
$obj = $dereferencer->deref($pointer);
return $this->collectZendObject(
$obj,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
/** @param Pointer<ZendString> $pointer */
public function collectZendStringPointer(
Pointer $pointer,
MemoryLocations $memory_locations,
Dereferencer $dereferencer,
ContextPools $context_pools
): StringContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendArrayTableOverheadMemoryLocation) {
$memory_location = null;
} else {
assert($memory_location instanceof ZendStringMemoryLocation);
}
}
if (!isset($memory_location)) {
$str = $dereferencer->deref($pointer);
$memory_location = ZendStringMemoryLocation::fromZendString(
$str,
$dereferencer,
);
$memory_locations->add($memory_location);
}
assert($memory_location instanceof ZendStringMemoryLocation);
return $context_pools
->string_context_pool
->getContextForLocation($memory_location)
;
}
public function collectCallFrames(
ZendExecutorGlobals $eg,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): CallFramesContext {
$call_frames_context = new CallFramesContext();
if (is_null($eg->current_execute_data)) {
return $call_frames_context;
}
$execute_data = $dereferencer->deref($eg->current_execute_data);
foreach ($execute_data->iterateStackChain($dereferencer) as $key => $execute_data) {
$call_frame_context = $this->collectCallFrame(
$execute_data,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$call_frames_context->add((string)$key, $call_frame_context);
}
return $call_frames_context;
}
public function collectRealCallStackOnMemoryLimitViolation(
UserFunctionDefinitionContext $memory_limit_error_function_context,
int $max_challenge_depth,
CallFramesContext $call_frames_context,
ZendExecutorGlobals $eg,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
): CallFramesContext {
$op_array_address = $memory_limit_error_function_context->getOpArrayAddress();
if (is_null($eg->vm_stack)) {
return $call_frames_context;
}
if (is_null($eg->vm_stack_top)) {
return $call_frames_context;
}
$last_vm_stack = $dereferencer->deref($eg->vm_stack);
$root_vm_stack = $last_vm_stack->getRootStack($dereferencer);
if (is_null($root_vm_stack->top)) {
return $call_frames_context;
}
$first_stack = true;
foreach ($last_vm_stack->iterateStackChain($dereferencer) as $vm_stack) {
if ($first_stack) {
$first_stack = false;
$stack_end_address = $eg->vm_stack_top->address;
} else {
if (is_null($vm_stack->end)) {
break;
}
$stack_end_address = $vm_stack->end->address;
}
$materialized_vm_stack = $vm_stack->materializeAsPointerArray(
$dereferencer,
$stack_end_address
);
foreach ($materialized_vm_stack->getReverseIteratorAsInt() as $key => $value) {
if ($value !== $op_array_address) {
continue;
}
$pointer_address = $key * 8 + $materialized_vm_stack->getPointer()->address - 24;
Log::debug('candidate frame found', ['frame_address' => $pointer_address]);
$frame_candidate = new Pointer(
ZendExecuteData::class,
$pointer_address,
$zend_type_reader->sizeOf('zend_execute_data')
);
try {
$execute_data_candidate = $dereferencer->deref($frame_candidate);
$root_execute_data_candidate = $execute_data_candidate->getRootFrame(
$dereferencer,
$max_challenge_depth,
);
if ($root_vm_stack->top->address !== $root_execute_data_candidate->getPointer()->address) {
continue;
}
Log::debug('root candidate frame found', ['frame_address' => $root_vm_stack->top->address]);
$frame_start = count($call_frames_context->getLinks());
foreach ($execute_data_candidate->iterateStackChain($dereferencer) as $frame_no => $execute_data) {
$call_frame_context = $this->collectCallFrame(
$execute_data,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
null,
);
$call_frames_context->add((string)($frame_no + $frame_start), $call_frame_context);
}
return $call_frames_context;
} catch (\Throwable $e) {
Log::debug(
'failed to collect real call stack from this candidate',
[
'exception' => $e,
'frame_address' => $pointer_address
]
);
continue;
}
}
}
return $call_frames_context;
}
public function collectCallFrame(
ZendExecuteData $execute_data,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): CallFrameContext {
$function_name = $execute_data->getFullyQualifiedFunctionName(
$dereferencer,
$zend_type_reader,
);
$lineno = -1;
if ($execute_data->opline !== null and !$execute_data->isInternalCall($dereferencer)) {
$opline = $dereferencer->deref($execute_data->opline);
$lineno = $opline->lineno;
}
$header_memory_location = CallFrameHeaderMemoryLocation::fromZendExecuteData(
$execute_data,
);
$call_frame_context = new CallFrameContext(
$function_name,
$lineno,
);
$variable_table_memory_location = CallFrameVariableTableMemoryLocation::fromZendExecuteData(
$execute_data,
$dereferencer
);
$memory_locations->add($variable_table_memory_location);
$memory_locations->add($header_memory_location);
if ($execute_data->hasThis()) {
$this_context = $this->collectZval(
$execute_data->This,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($this_context)) {
$call_frame_context->add('this', $this_context);
}
}
$has_local_variables = false;
$variable_table_context = new CallFrameVariableTableContext();
$local_variables_iterator = $execute_data->getVariables($dereferencer, $zend_type_reader);
foreach ($local_variables_iterator as $name => $value) {
$local_variable_context = $this->collectZval(
$value,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($local_variable_context)) {
$variable_table_context->add($name, $local_variable_context);
$has_local_variables = true;
}
}
if ($has_local_variables) {
$call_frame_context->add('local_variables', $variable_table_context);
}
if ($execute_data->hasSymbolTable() and !is_null($execute_data->symbol_table)) {
$symbol_table_context = $this->collectZendArrayPointer(
$execute_data->symbol_table,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
$call_frame_context->add('symbol_table', $symbol_table_context);
}
if ($execute_data->hasExtraNamedParams() and !is_null($execute_data->extra_named_params)) {
$extra_named_params_context = $this->collectZendArrayPointer(
$execute_data->extra_named_params,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
$call_frame_context->add('extra_named_params', $extra_named_params_context);
}
return $call_frame_context;
}
public function collectGlobalVariables(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): GlobalVariablesContext {
return GlobalVariablesContext::fromArrayContext(
$this->collectZendArray(
$array,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
)
);
}
public function collectZendArray(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ArrayHeaderContext {
$array_header_location = ZendArrayMemoryLocation::fromZendArray($array);
$memory_locations->add($array_header_location);
$array_header_context = $context_pools
->array_context_pool
->getContextForLocation($array_header_location)
;
if (is_null($array->arData)) {
return $array_header_context;
}
$array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
$array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
$array,
$array_table_location
);
$memory_locations->add($array_table_location);
$memory_locations->add($array_table_overhead_location);
$array_context = new ArrayElementsContext($array_table_location);
$overhead_context = new ArrayPossibleOverheadContext($array_table_overhead_location);
foreach ($array->getItemIteratorWithZendStringKeyIfAssoc($dereferencer) as $key => $zval) {
$element_context = new ArrayElementContext();
if ($key instanceof Pointer) {
$key_context = $this->collectZendStringPointer(
$key,
$memory_locations,
$dereferencer,
$context_pools,
);
$zend_string = $dereferencer->deref($key);
$key_string = $zend_string->toString($dereferencer);
$element_context->add('key', $key_context);
} else {
$key_string = (string)$key;
}
$array_context->add($key_string, $element_context);
$value_context = $this->collectZval(
$zval,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($value_context)) {
$element_context->add('value', $value_context);
}
}
$array_header_context->add('possible_unused_area', $overhead_context);
$array_header_context->add('array_elements', $array_context);
return $array_header_context;
}
public function collectZendObject(
ZendObject $object,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ObjectContext {
$object_location = ZendObjectMemoryLocation::fromZendObject(
$object,
$dereferencer,
$zend_type_reader,
);
$object_handlers_memory_location = ZendObjectHandlersMemoryLocation::fromZendObject(
$object,
$zend_type_reader,
);
$memory_locations->add($object_location);
$memory_locations->add($object_handlers_memory_location);
$object_context = $context_pools
->object_context_pool->getContextForLocation(
$object_location,
)
;
$object_handlers_context = $context_pools
->object_context_pool
->getHandlersContextForLocation(
$object_handlers_memory_location,
)
;
$object_context->add('object_handlers', $object_handlers_context);
$properties_exists = false;
$object_properties_context = new ObjectPropertiesContext();
$properties_iterator = $object->getPropertiesIterator(
$dereferencer,
$zend_type_reader,
);
foreach ($properties_iterator as $name => $property) {
assert(is_string($name));
$property_context = $this->collectZval(
$property,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($property_context)) {
$object_properties_context->add($name, $property_context);
$properties_exists = true;
}
}
if ($properties_exists) {
$object_context->add('object_properties', $object_properties_context);
}
if (
!is_null($object->properties)
and !is_null($object->ce)
and !$object->isEnum($dereferencer)
) {
$dynamic_properties_context = $this->collectZendArray(
$dereferencer->deref($object->properties),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$object_context->add('dynamic_properties', $dynamic_properties_context);
}
assert(!is_null($object->ce));
$class_entry = $dereferencer->deref($object->ce);
if (
$class_entry->getClassName($dereferencer) === 'Closure'
and !$zend_type_reader->isPhpVersionLowerThan(ZendTypeReader::V71)
) {
$closure_context = $this->collectClosure(
$dereferencer->deref(
ZendClosure::getPointerFromZendObjectPointer(
$object->getPointer(),
$zend_type_reader,
),
),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$object_context->add('closure', $closure_context);
}
return $object_context;
}
public function collectClosure(
ZendClosure $zend_closure,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ClosureContext {
$closure_context = new ClosureContext();
$closure_context->add(
'func',
$this->collectZendFunctionPointer(
$zend_closure->func->getPointer(),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
)
);
$zval_context = $this->collectZval(
$zend_closure->this_ptr,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($zval_context)) {
$closure_context->add(
'this_ptr',
$zval_context,
);
}
return $closure_context;
}
public function collectFunctionTable(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): DefinedFunctionsContext {
$array_header_location = ZendArrayMemoryLocation::fromZendArray($array);
$array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
$array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
$array,
$array_table_location
);
$memory_locations->add($array_header_location);
$memory_locations->add($array_table_location);
$memory_locations->add($array_table_overhead_location);
$defined_functions_context = new DefinedFunctionsContext(
$array_header_location,
$array_table_location,
);
foreach ($array->getItemIterator($dereferencer) as $function_name => $zval) {
assert(is_string($function_name));
assert(!is_null($zval->value->func));
$function_context = $this->collectZendFunctionPointer(
$zval->value->func,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$defined_functions_context->add($function_name, $function_context);
}
return $defined_functions_context;
}
/** @param Pointer<ZendFunction> $pointer */
public function collectZendFunctionPointer(
Pointer $pointer,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): FunctionDefinitionContext {
if ($memory_locations->has($pointer->address)) {
$memory_location = $memory_locations->get($pointer->address);
if ($memory_location instanceof ZendOpArrayHeaderMemoryLocation) {
return $context_pools
->user_function_definition_context_pool
->getContextForLocation($memory_location)
;
}
}
$func = $dereferencer->deref($pointer);
return $this->collectZendFunction(
$func,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
public function collectZendFunction(
ZendFunction $func,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): FunctionDefinitionContext {
if ($func->isUserFunction()) {
$function_definition_context = $this->collectUserFunctionDefinition(
$func,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
} else {
$function_definition_context = new InternalFunctionDefinitionContext();
}
if (!is_null($func->function_name)) {
$function_name_context = $this->collectZendStringPointer(
$func->function_name,
$memory_locations,
$dereferencer,
$context_pools,
);
$function_definition_context->add('name', $function_name_context);
}
return $function_definition_context;
}
public function collectUserFunctionDefinition(
ZendFunction $func,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): UserFunctionDefinitionContext {
$function_name = $func->getFullyQualifiedFunctionName(
$dereferencer,
$zend_type_reader,
);
$op_array_header_memory_location = ZendOpArrayHeaderMemoryLocation::fromZendFunction(
$func,
$zend_type_reader,
$dereferencer,
);
$op_array_body_memory_location = ZendOpArrayBodyMemoryLocation::fromZendFunction(
$func,
$zend_type_reader,
$function_name
);
$memory_locations->add($op_array_header_memory_location);
$memory_locations->add($op_array_body_memory_location);
$function_definition_context = $context_pools
->user_function_definition_context_pool
->getContextForLocation($op_array_header_memory_location)
;
$op_array_context = new OpArrayContext(
$op_array_header_memory_location,
$op_array_body_memory_location,
);
$function_definition_context->add('op_array', $op_array_context);
if ($func->op_array->cache_size > 0) {
$runtime_cache_memory_location = RuntimeCacheMemoryLocation::fromZendOpArray(
$func->op_array,
$dereferencer,
$zend_type_reader,
$map_ptr_base,
);
if ($runtime_cache_memory_location->address !== 0) {
$memory_locations->add($runtime_cache_memory_location);
$run_time_cache_context = new RuntimeCacheContext($runtime_cache_memory_location);
$op_array_context->add('run_time_cache', $run_time_cache_context);
}
}
if (!is_null($func->op_array->arg_info)) {
$arginfos_memory_location = ZendArgInfosMemoryLocation::fromZendOpArray(
$func->op_array,
$zend_type_reader,
);
$memory_locations->add($arginfos_memory_location);
$arginfos_context = new ArgInfosContext($arginfos_memory_location);
$op_array_context->add('arg_infos', $arginfos_context);
foreach ($func->op_array->iterateArgInfo($dereferencer, $zend_type_reader) as $arg_info) {
if (!is_null($arg_info->name)) {
$arg_info_context = new ArgInfoContext();
$arg_info_name_context = $this->collectZendStringPointer(
$arg_info->name,
$memory_locations,
$dereferencer,
$context_pools,
);
$arg_info_name = $dereferencer
->deref($arg_info->name)
->toString($dereferencer)
;
$arg_info_context->add('name', $arg_info_name_context);
$arginfos_context->add($arg_info_name, $arg_info_context);
}
}
}
if (!is_null($func->op_array->doc_comment)) {
$doc_comment_context = $this->collectZendStringPointer(
$func->op_array->doc_comment,
$memory_locations,
$dereferencer,
$context_pools,
);
$op_array_context->add('doc_comment', $doc_comment_context);
}
if (!is_null($func->op_array->filename)) {
$file_name_context = $this->collectZendStringPointer(
$func->op_array->filename,
$memory_locations,
$dereferencer,
$context_pools,
);
$op_array_context->add('filename', $file_name_context);
}
if (!is_null($func->op_array->static_variables)) {
$static_variables_context = $this->collectZendArray(
$dereferencer->deref($func->op_array->static_variables),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$op_array_context->add('static_variables', $static_variables_context);
}
if (!is_null($func->op_array->vars)) {
$local_variable_name_table_location = LocalVariableNameTableMemoryLocation::fromZendOpArray(
$func->op_array
);
$memory_locations->add($local_variable_name_table_location);
$variable_name_table_context = new LocalVariableNameTableContext(
$local_variable_name_table_location
);
$op_array_context->add('variable_name_table', $variable_name_table_context);
$variable_names_iterator = $func->op_array->getVariableNamesAsIteratorOfPointersToZendStrings(
$dereferencer,
$zend_type_reader,
);
foreach ($variable_names_iterator as $key => $variable_name) {
$variable_name_context = $this->collectZendStringPointer(
$variable_name,
$memory_locations,
$dereferencer,
$context_pools,
);
$variable_name_table_context->add((string)$key, $variable_name_context);
}
}
if ($func->op_array->num_dynamic_func_defs > 0) {
$dynamic_func_defs_table_memory_location = DynamicFuncDefsTableMemoryLocation::fromZendOpArray(
$func->op_array,
);
$memory_locations->add($dynamic_func_defs_table_memory_location);
$dynamic_func_defs_context = new DynamicFuncDefsContext(
$dynamic_func_defs_table_memory_location
);
$dynamic_func_defs_iterator = $func->op_array->iterateDynamicFunctionDefinitions(
$dereferencer,
$zend_type_reader,
);
foreach ($dynamic_func_defs_iterator as $key => $dynamic_func_def) {
$dynamic_function_context = $this->collectZendFunctionPointer(
$dynamic_func_def,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$dynamic_func_defs_context->add((string)$key, $dynamic_function_context);
}
$op_array_context->add('dynamic_function_definitions', $dynamic_func_defs_context);
}
if (!is_null($memory_limit_error_details)) {
if (
$function_definition_context->isThisContext(
$memory_limit_error_details->file,
$memory_limit_error_details->line,
)
) {
if (
is_null($this->memory_limit_error_function_context)
or $function_definition_context->isClosureOf($this->memory_limit_error_function_context)
) {
$this->memory_limit_error_function_context = $function_definition_context;
}
}
}
return $function_definition_context;
}
public function collectClassConstantsTable(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ClassConstantsContext {
$array_table_location = ZendArrayTableMemoryLocation::fromZendArray($array);
$array_table_overhead_location = ZendArrayTableOverheadMemoryLocation::fromZendArrayAndUsedLocation(
$array,
$array_table_location
);
$memory_locations->add($array_table_location);
$memory_locations->add($array_table_overhead_location);
$class_constants_context = new ClassConstantsContext($array_table_location);
$array_iterator = $array->getItemIteratorWithZendStringKeyIfAssoc($dereferencer);
foreach ($array_iterator as $constant_name => $zval_or_ptr) {
assert($constant_name instanceof Pointer);
$constant_name_context = $this->collectZendStringPointer(
$constant_name,
$memory_locations,
$dereferencer,
$context_pools,
);
$zend_string = $dereferencer->deref($constant_name);
$constant_name_string = $zend_string->toString($dereferencer);
$constant_context = new ClassConstantContext();
$class_constants_context->add($constant_name_string, $constant_context);
$constant_context->add('name', $constant_name_context);
if ($zend_type_reader->isPhpVersionLowerThan(ZendTypeReader::V71)) {
$zval = $zval_or_ptr;
} else {
$class_constant_ptr = $zval_or_ptr->value->getAsPointer(
ZendClassConstant::class,
$zend_type_reader->sizeOf(ZendClassConstant::getCTypeName()),
);
$class_constant = $dereferencer->deref(
$class_constant_ptr
);
$memory_location = ZendClassConstantMemoryLocation::fromZendClassConstant(
$class_constant,
);
$memory_locations->add($memory_location);
$zval = $class_constant->value;
$info_context = new ClassConstantInfoContext($memory_location);
$constant_context->add('info', $info_context);
}
$value_context = $this->collectZval(
$zval,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($value_context)) {
$constant_context->add('value', $value_context);
}
}
return $class_constants_context;
}
public function collectClassTable(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): DefinedClassesContext {
$defined_classes_context = new DefinedClassesContext();
foreach ($array->getItemIterator($dereferencer) as $class_name => $zval) {
assert(!is_null($zval->value->ce));
$class_definition_context = $this->collectClassDefinitionPointer(
$zval->value->ce,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($class_definition_context)) {
$defined_classes_context->add((string)$class_name, $class_definition_context);
}
}
return $defined_classes_context;
}
/** @param Pointer<ZendClassEntry> $pointer */
private function collectClassDefinitionPointer(
Pointer $pointer,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ?ClassDefinitionContext {
if ($memory_locations->has($pointer->address)) {
return null;
}
$class_entry = $dereferencer->deref($pointer);
return $this->collectClassDefinition(
$class_entry,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
}
private function collectClassDefinition(
ZendClassEntry $class_entry,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ClassDefinitionContext {
$class_definition_context = new ClassDefinitionContext($class_entry->isInternal());
$memory_location = ZendClassEntryMemoryLocation::fromZendClassEntry($class_entry);
$memory_locations->add($memory_location);
$class_entry_context = new ClassEntryContext($memory_location);
$class_definition_context->add('class_entry', $class_entry_context);
$class_name_context = $this->collectZendStringPointer(
$class_entry->name,
$memory_locations,
$dereferencer,
$context_pools,
);
$class_definition_context->add('name', $class_name_context);
if (!$class_entry->isInternal()) {
if (!is_null($class_entry->info->user->filename)) {
$file_name_context = $this->collectZendStringPointer(
$class_entry->info->user->filename,
$memory_locations,
$dereferencer,
$context_pools,
);
$class_definition_context->add('filename', $file_name_context);
}
if (!is_null($class_entry->info->user->doc_comment)) {
$doc_comment_context = $this->collectZendStringPointer(
$class_entry->info->user->doc_comment,
$memory_locations,
$dereferencer,
$context_pools,
);
$class_definition_context->add('doc_comment', $doc_comment_context);
}
}
if (
$class_entry->default_static_members_count > 0
and !is_null($class_entry->static_members_table)
) {
$static_members_table_memory_location = StaticMembersTableMemoryLocation::fromZendClassEntry(
$class_entry,
$zend_type_reader,
$dereferencer,
$map_ptr_base,
);
$memory_locations->add($static_members_table_memory_location);
$static_properties_context = new ClassStaticPropertiesContext(
$static_members_table_memory_location
);
$static_property_iterator = $class_entry->getStaticPropertyIterator(
$dereferencer,
$zend_type_reader,
$map_ptr_base,
);
foreach ($static_property_iterator as $name => $value) {
$static_property_context = $this->collectZval(
$value,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($static_property_context)) {
$static_properties_context->add($name, $static_property_context);
}
}
$class_definition_context->add('static_properties', $static_properties_context);
}
$properties_info_context = $this->collectPropertiesInfo(
$class_entry,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
);
$class_definition_context->add('property_info', $properties_info_context);
if (!is_null($class_entry->default_properties_table)) {
$default_properties_table_memory_location = DefaultPropertiesTableMemoryLocation::fromZendClassEntry(
$class_entry,
);
$memory_locations->add($default_properties_table_memory_location);
$default_properties_context = new DefaultPropertiesTableContext(
$default_properties_table_memory_location
);
$class_definition_context->add('default_properties', $default_properties_context);
}
if (!is_null($class_entry->default_static_members_table)) {
$default_static_members_memory_location = DefaultStaticMembersTableMemoryLocation::fromZendClassEntry(
$class_entry,
);
$memory_locations->add($default_static_members_memory_location);
$default_static_properties_context = new DefaultStaticPropertiesContext(
$default_static_members_memory_location
);
$class_definition_context->add('default_static_properties', $default_static_properties_context);
}
$methods_context = $this->collectFunctionTable(
$dereferencer->deref($class_entry->function_table->getPointer()),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$class_definition_context->add('methods', $methods_context);
$class_constants_context = $this->collectClassConstantsTable(
$dereferencer->deref($class_entry->constants_table->getPointer()),
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
$class_definition_context->add('constants', $class_constants_context);
return $class_definition_context;
}
private function collectPropertiesInfo(
ZendClassEntry $class_entry,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools
): PropertiesInfoContext {
$memory_location = ZendArrayTableMemoryLocation::fromZendArray($class_entry->properties_info);
$memory_locations->add($memory_location);
$properties_info_context = new PropertiesInfoContext($memory_location);
foreach ($class_entry->iteratePropertyInfo($dereferencer, $zend_type_reader) as $key => $property_info) {
$property_info_memory_location = ZendPropertyInfoMemoryLocation::fromZendPropertyInfo(
$property_info,
);
$memory_locations->add($property_info_memory_location);
$property_info_context = new PropertyInfoContext($property_info_memory_location);
if (!is_null($property_info->name)) {
$property_info_name_context = $this->collectZendStringPointer(
$property_info->name,
$memory_locations,
$dereferencer,
$context_pools,
);
$property_info_context->add('name', $property_info_name_context);
}
if (!is_null($property_info->doc_comment)) {
$property_info_doc_comment_context = $this->collectZendStringPointer(
$property_info->doc_comment,
$memory_locations,
$dereferencer,
$context_pools,
);
$property_info_context->add('doc_comment', $property_info_doc_comment_context);
}
$properties_info_context->add($key, $property_info_context);
}
return $properties_info_context;
}
private function collectGlobalConstants(
ZendArray $array,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): GlobalConstantsContext {
$memory_location = ZendArrayTableMemoryLocation::fromZendArray($array);
$memory_locations->add($memory_location);
$global_constants_context = new GlobalConstantsContext($memory_location);
foreach ($array->getItemIterator($dereferencer) as $constant_name => $zval) {
assert(is_string($constant_name));
$zend_constant = $dereferencer->deref(
$zval->value->getAsPointer(
ZendConstant::class,
$zend_type_reader->sizeOf(ZendConstant::getCTypeName()),
)
);
$zend_constant_memory_location = ZendConstantMemoryLocation::fromZendConstant(
$zend_constant
);
$constant_context = new GlobalConstantContext($zend_constant_memory_location);
$global_constants_context->add($constant_name, $constant_context);
if (!is_null($zend_constant->name)) {
$constant_name_context = $this->collectZendStringPointer(
$zend_constant->name,
$memory_locations,
$dereferencer,
$context_pools,
);
$constant_context->add('name', $constant_name_context);
}
$value_context = $this->collectZval(
$zend_constant->value,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
$memory_limit_error_details,
);
if (!is_null($value_context)) {
$constant_context->add('value', $value_context);
}
}
return $global_constants_context;
}
private function collectIncludedFiles(
ZendArray $included_files,
Dereferencer $dereferencer,
MemoryLocations $memory_locations,
ContextPools $context_pools
): IncludedFilesContext {
$array_table_location = ZendArrayTableMemoryLocation::fromZendArray($included_files);
$included_files_context = new IncludedFilesContext($array_table_location);
$memory_locations->add($array_table_location);
$iterator = $included_files->getItemIteratorWithZendStringKeyIfAssoc($dereferencer);
foreach ($iterator as $filename => $_) {
assert($filename instanceof Pointer);
$raw_string = $dereferencer->deref($filename)->toString($dereferencer);
$included_file_context = $this->collectZendStringPointer(
$filename,
$memory_locations,
$dereferencer,
$context_pools,
);
$included_files_context->add($raw_string, $included_file_context);
}
return $included_files_context;
}
private function collectInternedStrings(
ZendArray $interned_string,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools
): ArrayHeaderContext {
return $this->collectZendArray(
$interned_string,
$map_ptr_base,
$dereferencer,
$zend_type_reader,
$memory_locations,
$context_pools,
null
);
}
private function collectObjectsStore(
ZendObjectsStore $objects_store,
int $map_ptr_base,
Dereferencer $dereferencer,
ZendTypeReader $zend_type_reader,
MemoryLocations $memory_locations,
ContextPools $context_pools,
?MemoryLimitErrorDetails $memory_limit_error_details,
): ObjectsStoreContext {
$objects_store_memory_location = ObjectsStoreMemoryLocation::fromZendObjectsStore(
$objects_store,
);
$objects_store_context = new ObjectsStoreContext($objects_store_memory_location);
$memory_locations->add($objects_store_memory_location);
assert($objects_store->object_buckets instanceof Pointer);
$buckets = $dereferencer->deref($objects_store->object_buckets);
$bucket_iterator = $buckets->getIteratorOfPointersTo(
ZendObject::class,
$zend_type_reader,
);
foreach ($bucket_iterator as $key => $bucket) {
if ($key === 0) {
continue;
}
if ($bucket->address & 1) {
continue;
}
if ($bucket->address === 0) {
continue;
}
if ($key >= $objects_store->top) {
break;
}
$objects_store_bucket_context = $this->collectZendObjectPointer(
$bucket,
$map_ptr_base,
$memory_locations,
$dereferencer,
$zend_type_reader,
$context_pools,
$memory_limit_error_details,
);
$objects_store_context->add((string)$key, $objects_store_bucket_context);
}
return $objects_store_context;
}
}
Function Calls
None |
Stats
MD5 | 1fc69b52c3b61f7591c9488e989f6915 |
Eval Count | 0 |
Decode Time | 182 ms |