Skip to content

Commit

Permalink
Merge pull request #230 from reliforp/per-php-binary-symbol-cache
Browse files Browse the repository at this point in the history
Cache symbols per PHP binary
  • Loading branch information
sj-i authored Nov 6, 2022
2 parents b9fc782 + 4aa3971 commit 233e499
Show file tree
Hide file tree
Showing 18 changed files with 302 additions and 49 deletions.
44 changes: 44 additions & 0 deletions src/Lib/Elf/Process/BinaryFingerprint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

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

declare(strict_types=1);

namespace Reli\Lib\Elf\Process;

use Reli\Lib\Process\MemoryMap\ProcessModuleMemoryMap;

final class BinaryFingerprint
{
public function __construct(
private string $fingerprint
) {
}

public static function fromProcessModuleMemoryMap(
ProcessModuleMemoryMap $process_module_memory_map
): self {
return new self(
join(
'_',
[
$process_module_memory_map->getDeviceId(),
$process_module_memory_map->getInodeNumber(),
$process_module_memory_map->getModuleName(),
]
)
);
}

public function __toString(): string
{
return $this->fingerprint;
}
}
60 changes: 60 additions & 0 deletions src/Lib/Elf/Process/Elf64LazyParseSymbolResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

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

declare(strict_types=1);

namespace Reli\Lib\Elf\Process;

use Reli\Lib\Elf\Parser\ElfParserException;
use Reli\Lib\Elf\Structure\Elf64\Elf64SymbolTableEntry;
use Reli\Lib\Elf\SymbolResolver\Elf64SymbolResolver;
use Reli\Lib\Elf\SymbolResolver\SymbolResolverCreatorInterface;
use Reli\Lib\Process\MemoryMap\ProcessModuleMemoryMap;
use Reli\Lib\Process\MemoryReader\MemoryReaderInterface;

final class Elf64LazyParseSymbolResolver implements Elf64SymbolResolver
{
private ?Elf64SymbolResolver $resolver_cache = null;

public function __construct(
private string $path,
private MemoryReaderInterface $memory_reader,
private int $pid,
private ProcessModuleMemoryMap $module_memory_map,
private SymbolResolverCreatorInterface $symbol_resolver_creator,
) {
}

private function loadResolver(): Elf64SymbolResolver
{
try {
return $this->symbol_resolver_creator->createLinearScanResolverFromPath($this->path);
} catch (ElfParserException $e) {
try {
return $this->symbol_resolver_creator->createDynamicResolverFromPath($this->path);
} catch (ElfParserException $e) {
return $this->symbol_resolver_creator->createDynamicResolverFromProcessMemory(
$this->memory_reader,
$this->pid,
$this->module_memory_map
);
}
}
}

public function resolve(string $symbol_name): Elf64SymbolTableEntry
{
if (!isset($this->resolver_cache)) {
$this->resolver_cache = $this->loadResolver();
}
return $this->resolver_cache->resolve($symbol_name);
}
}
30 changes: 30 additions & 0 deletions src/Lib/Elf/Process/PerBinarySymbolCacheRetriever.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

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

declare(strict_types=1);

namespace Reli\Lib\Elf\Process;

use Reli\Lib\Elf\SymbolResolver\Elf64SymbolCache;

final class PerBinarySymbolCacheRetriever
{
/** @var array<string, Elf64SymbolCache> */
private array $cache = [];

public function get(BinaryFingerprint $binary_fingerprint): Elf64SymbolCache
{
if (!isset($this->cache[(string)$binary_fingerprint])) {
$this->cache[(string)$binary_fingerprint] = new Elf64SymbolCache();
}
return $this->cache[(string)$binary_fingerprint];
}
}
56 changes: 22 additions & 34 deletions src/Lib/Elf/Process/ProcessModuleSymbolReaderCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace Reli\Lib\Elf\Process;

use Reli\Lib\Elf\Parser\ElfParserException;
use Reli\Lib\Elf\SymbolResolver\Elf64CachedSymbolResolver;
use Reli\Lib\Elf\SymbolResolver\SymbolResolverCreatorInterface;
use Reli\Lib\Process\MemoryMap\ProcessMemoryMap;
use Reli\Lib\Process\MemoryMap\ProcessModuleMemoryMap;
Expand All @@ -23,7 +24,8 @@ final class ProcessModuleSymbolReaderCreator
{
public function __construct(
private SymbolResolverCreatorInterface $symbol_resolver_creator,
private MemoryReaderInterface $memory_reader
private MemoryReaderInterface $memory_reader,
private PerBinarySymbolCacheRetriever $per_binary_symbol_cache_retriever,
) {
}

Expand All @@ -40,42 +42,28 @@ public function createModuleReaderByNameRegex(
}
$module_memory_map = new ProcessModuleMemoryMap($memory_areas);

$module_name = current($memory_areas)->name;
$module_name = $module_memory_map->getModuleName();
$path = $binary_path ?? $this->createContainerAwarePath($pid, $module_name);
try {
$symbol_resolver = $this->symbol_resolver_creator->createLinearScanResolverFromPath($path);
return new ProcessModuleSymbolReader(

$symbol_resolver = new Elf64CachedSymbolResolver(
new Elf64LazyParseSymbolResolver(
$path,
$this->memory_reader,
$pid,
$symbol_resolver,
$module_memory_map,
$this->memory_reader,
$tls_block_address
);
} catch (ElfParserException $e) {
try {
$symbol_resolver = $this->symbol_resolver_creator->createDynamicResolverFromPath($path);
return new ProcessModuleSymbolReader(
$pid,
$symbol_resolver,
$module_memory_map,
$this->memory_reader,
$tls_block_address
);
} catch (ElfParserException $e) {
$symbol_resolver = $this->symbol_resolver_creator->createDynamicResolverFromProcessMemory(
$this->memory_reader,
$pid,
$module_memory_map
);
return new ProcessModuleSymbolReader(
$pid,
$symbol_resolver,
$module_memory_map,
$this->memory_reader,
$tls_block_address
);
}
}
$this->symbol_resolver_creator,
),
$this->per_binary_symbol_cache_retriever->get(
BinaryFingerprint::fromProcessModuleMemoryMap($module_memory_map)
),
);
return new ProcessModuleSymbolReader(
$pid,
$symbol_resolver,
$module_memory_map,
$this->memory_reader,
$tls_block_address
);
}

private function createContainerAwarePath(int $pid, string $path): string
Expand Down
36 changes: 36 additions & 0 deletions src/Lib/Elf/SymbolResolver/Elf64CachedSymbolResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

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

declare(strict_types=1);

namespace Reli\Lib\Elf\SymbolResolver;

use Reli\Lib\Elf\Structure\Elf64\Elf64SymbolTableEntry;

final class Elf64CachedSymbolResolver implements Elf64SymbolResolver
{
public function __construct(
private Elf64SymbolResolver $resolver,
private Elf64SymbolCache $symbol_cache,
) {
}

public function resolve(string $symbol_name): Elf64SymbolTableEntry
{
if (!$this->symbol_cache->has($symbol_name)) {
$this->symbol_cache->set(
$symbol_name,
$this->resolver->resolve($symbol_name),
);
}
return $this->symbol_cache->get($symbol_name);
}
}
37 changes: 37 additions & 0 deletions src/Lib/Elf/SymbolResolver/Elf64SymbolCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

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

declare(strict_types=1);

namespace Reli\Lib\Elf\SymbolResolver;

use Reli\Lib\Elf\Structure\Elf64\Elf64SymbolTableEntry;

final class Elf64SymbolCache
{
/** @var array<string, Elf64SymbolTableEntry> */
private array $cache = [];

public function has(string $symbol_name): bool
{
return isset($this->cache[$symbol_name]);
}

public function set(string $symbol_name, Elf64SymbolTableEntry $entry): void
{
$this->cache[$symbol_name] = $entry;
}

public function get(string $symbol_name): Elf64SymbolTableEntry
{
return $this->cache[$symbol_name];
}
}
4 changes: 3 additions & 1 deletion src/Lib/Process/MemoryMap/ProcessMemoryArea.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public function __construct(
public string $end,
public string $file_offset,
public ProcessMemoryAttribute $attribute,
public string $name
public string $device_id,
public int $inode_num,
public string $name,
) {
}

Expand Down
24 changes: 14 additions & 10 deletions src/Lib/Process/MemoryMap/ProcessMemoryMapParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Reli\Lib\Process\MemoryMap;

use PhpCast\Cast;
use Reli\Lib\String\LineFetcher;

final class ProcessMemoryMapParser
Expand Down Expand Up @@ -46,17 +47,20 @@ private function parseLine(string $line): ?ProcessMemoryArea
if ($matches === []) {
return null;
}
$begin = $matches[1];
$end = $matches[2];
$attribute_string = $matches[3];
$attribute = new ProcessMemoryAttribute(
$attribute_string[0] === 'r',
$attribute_string[1] === 'w',
$attribute_string[2] === 'x',
$attribute_string[3] === 'p',
return new ProcessMemoryArea(
begin: $matches[1],
end: $matches[2],
file_offset: $matches[4],
attribute: new ProcessMemoryAttribute(
read: $attribute_string[0] === 'r',
write: $attribute_string[1] === 'w',
execute: $attribute_string[2] === 'x',
protected: $attribute_string[3] === 'p',
),
device_id: $matches[5],
inode_num: Cast::toInt($matches[6]),
name: $matches[7],
);
$file_offset = $matches[4];
$name = $matches[7];
return new ProcessMemoryArea($begin, $end, $file_offset, $attribute, $name);
}
}
15 changes: 15 additions & 0 deletions src/Lib/Process/MemoryMap/ProcessModuleMemoryMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,19 @@ private function getSortedOffsetToMemoryAreaMap(): array
}
return $this->sorted_offset_to_memory_map;
}

public function getDeviceId(): string
{
return $this->memory_areas[0]->device_id;
}

public function getInodeNumber(): int
{
return $this->memory_areas[0]->inode_num;
}

public function getModuleName(): string
{
return $this->memory_areas[0]->name;
}
}
6 changes: 6 additions & 0 deletions src/Lib/Process/MemoryMap/ProcessModuleMemoryMapInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@ public function getBaseAddress(): int;
public function getMemoryAddressFromOffset(int $offset): int;

public function isInRange(int $address): bool;

public function getDeviceId(): string;

public function getInodeNumber(): int;

public function getModuleName(): string;
}
Loading

0 comments on commit 233e499

Please sign in to comment.