Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache symbols per PHP binary #230

Merged
merged 1 commit into from
Nov 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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