Skip to content

Commit

Permalink
feat: sort profiler edges using Tree abstraction; add envs TRAP_XHPRO…
Browse files Browse the repository at this point in the history
…F_SORT and TRAP_XHPROF_PATH
  • Loading branch information
roxblnfk committed Jun 1, 2024
1 parent 75bfef3 commit de2277c
Show file tree
Hide file tree
Showing 6 changed files with 402 additions and 99 deletions.
6 changes: 6 additions & 0 deletions src/Config/Server/Files/XHProf.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Buggregator\Trap\Config\Server\Files;

use Buggregator\Trap\Service\Config\Env;
use Buggregator\Trap\Service\Config\PhpIni;
use Buggregator\Trap\Service\FilesObserver\Converter\XHProf as Converter;

Expand All @@ -12,7 +13,12 @@
*/
final class XHProf extends ObserverConfig
{
/** @var int<0, 3> Edges sorting algorithm */
#[Env('TRAP_XHPROF_SORT')]
public int $algorithm = 1;

/** @var non-empty-string|null Path to XHProf files */
#[Env('TRAP_XHPROF_PATH')]
#[PhpIni('xhprof.output_dir')]
public ?string $path = null;

Check failure on line 23 in src/Config/Server/Files/XHProf.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/XHProf.php:23:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\XHProf::$path has type non-empty-string|null, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$path of type null|string (see https://psalm.dev/267)

Check failure on line 23 in src/Config/Server/Files/XHProf.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/XHProf.php:23:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\XHProf::$path has type non-empty-string|null, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$path of type null|string (see https://psalm.dev/267)

Expand Down
33 changes: 33 additions & 0 deletions src/Service/FilesObserver/Converter/Branch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Service\FilesObserver\Converter;

/**
* @template-covariant T of object
*
* @internal
*/
final class Branch
{
/**
* @param T $item
* @param non-empty-string $id
* @param non-empty-string|null $parentId
* @param list<Branch<T>> $children
* @param Branch<T>|null $parent
*/
public function __construct(

Check warning on line 21 in src/Service/FilesObserver/Converter/Branch.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Branch.php#L21

Added line #L21 was not covered by tests
public object $item,
public readonly string $id,
public readonly ?string $parentId,
public array $children = [],
public ?Branch $parent = null,
) {}

Check warning on line 27 in src/Service/FilesObserver/Converter/Branch.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Branch.php#L27

Added line #L27 was not covered by tests

public function __destruct()

Check warning on line 29 in src/Service/FilesObserver/Converter/Branch.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Branch.php#L29

Added line #L29 was not covered by tests
{
unset($this->item, $this->children, $this->parent);

Check warning on line 31 in src/Service/FilesObserver/Converter/Branch.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Branch.php#L31

Added line #L31 was not covered by tests
}
}
129 changes: 129 additions & 0 deletions src/Service/FilesObserver/Converter/Cost.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Service\FilesObserver\Converter;

/**
* @internal
*/
final class Cost implements \JsonSerializable
{
public float $p_cpu = 0;
public float $p_ct = 0;
public float $p_mu = 0;
public float $p_pmu = 0;
public float $p_wt = 0;

/** @var int<0, max> */
public int $d_cpu = 0;

/** @var int<0, max> */
public int $d_ct = 0;

/** @var int<0, max> */
public int $d_mu = 0;

/** @var int<0, max> */
public int $d_pmu = 0;

/** @var int<0, max> */
public int $d_wt = 0;

/**
* @param int<0, max> $ct
* @param int<0, max> $wt
* @param int<0, max> $cpu
* @param int<0, max> $mu
* @param int<0, max> $pmu
*/
public function __construct(

Check warning on line 40 in src/Service/FilesObserver/Converter/Cost.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Cost.php#L40

Added line #L40 was not covered by tests
public readonly int $ct,
public readonly int $wt,
public readonly int $cpu,
public readonly int $mu,
public readonly int $pmu,
) {}

Check warning on line 46 in src/Service/FilesObserver/Converter/Cost.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Cost.php#L46

Added line #L46 was not covered by tests

/**
* @param array{
* ct: int<0, max>,
* wt: int<0, max>,
* cpu: int<0, max>,
* mu: int<0, max>,
* pmu: int<0, max>,
* p_ct?: float<0, 100>,
* p_wt?: float<0, 100>,
* p_cpu?: float<0, 100>,
* p_mu?: float<0, 100>,
* p_pmu?: float<0, 100>,
* d_ct?: int<0, max>,
* d_wt?: int<0, max>,
* d_cpu?: int<0, max>,
* d_mu?: int<0, max>,
* d_pmu?: int<0, max>
* } $data
*/
public static function fromArray(array $data): self

Check warning on line 67 in src/Service/FilesObserver/Converter/Cost.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Cost.php#L67

Added line #L67 was not covered by tests
{
$self = new self(
$data['ct'],
$data['wt'],
$data['cpu'],
$data['mu'],
$data['pmu'],

Check warning on line 74 in src/Service/FilesObserver/Converter/Cost.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Cost.php#L69-L74

Added lines #L69 - L74 were not covered by tests
);
\array_key_exists('p_ct', $data) and $self->p_ct = $data['p_ct'];
\array_key_exists('p_wt', $data) and $self->p_wt = $data['p_wt'];
\array_key_exists('p_cpu', $data) and $self->p_cpu = $data['p_cpu'];
\array_key_exists('p_mu', $data) and $self->p_mu = $data['p_mu'];
\array_key_exists('p_pmu', $data) and $self->p_pmu = $data['p_pmu'];
\array_key_exists('d_ct', $data) and $self->d_ct = $data['d_ct'];
\array_key_exists('d_wt', $data) and $self->d_wt = $data['d_wt'];
\array_key_exists('d_cpu', $data) and $self->d_cpu = $data['d_cpu'];
\array_key_exists('d_mu', $data) and $self->d_mu = $data['d_mu'];
\array_key_exists('d_pmu', $data) and $self->d_pmu = $data['d_pmu'];

Check warning on line 85 in src/Service/FilesObserver/Converter/Cost.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Cost.php#L76-L85

Added lines #L76 - L85 were not covered by tests

return $self;

Check warning on line 87 in src/Service/FilesObserver/Converter/Cost.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Cost.php#L87

Added line #L87 was not covered by tests
}

/**
* @return array{
* ct: int<0, max>,
* wt: int<0, max>,
* cpu: int<0, max>,
* mu: int<0, max>,
* pmu: int<0, max>,
* p_ct: float<0, 100>,
* p_wt: float<0, 100>,
* p_cpu: float<0, 100>,
* p_mu: float<0, 100>,
* p_pmu: float<0, 100>,
* d_ct: int<0, max>,
* d_wt: int<0, max>,
* d_cpu: int<0, max>,
* d_mu: int<0, max>,
* d_pmu: int<0, max>
* }
*/
public function jsonSerialize(): array

Check warning on line 109 in src/Service/FilesObserver/Converter/Cost.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Cost.php#L109

Added line #L109 was not covered by tests
{
return [
'ct' => $this->ct,
'wt' => $this->wt,
'cpu' => $this->cpu,
'mu' => $this->mu,
'pmu' => $this->pmu,
'p_ct' => $this->p_ct,
'p_wt' => $this->p_wt,
'p_cpu' => $this->p_cpu,
'p_mu' => $this->p_mu,
'p_pmu' => $this->p_pmu,
'd_ct' => $this->d_ct,
'd_wt' => $this->d_wt,
'd_cpu' => $this->d_cpu,
'd_mu' => $this->d_mu,
'd_pmu' => $this->d_pmu,
];

Check warning on line 127 in src/Service/FilesObserver/Converter/Cost.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Cost.php#L111-L127

Added lines #L111 - L127 were not covered by tests
}
}
26 changes: 26 additions & 0 deletions src/Service/FilesObserver/Converter/Edge.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Service\FilesObserver\Converter;

/**
* @internal
*/
final class Edge implements \JsonSerializable
{
public function __construct(

Check warning on line 12 in src/Service/FilesObserver/Converter/Edge.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Edge.php#L12

Added line #L12 was not covered by tests
public readonly ?string $caller,
public readonly string $callee,
public readonly Cost $cost,
) {}

Check warning on line 16 in src/Service/FilesObserver/Converter/Edge.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Edge.php#L16

Added line #L16 was not covered by tests

public function jsonSerialize(): array

Check warning on line 18 in src/Service/FilesObserver/Converter/Edge.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Edge.php#L18

Added line #L18 was not covered by tests
{
return [
'caller' => $this->caller,
'callee' => $this->callee,
'cost' => $this->cost,
];

Check warning on line 24 in src/Service/FilesObserver/Converter/Edge.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Edge.php#L20-L24

Added lines #L20 - L24 were not covered by tests
}
}
144 changes: 144 additions & 0 deletions src/Service/FilesObserver/Converter/Tree.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Service\FilesObserver\Converter;

/**
* @template-covariant TItem of object
*
* @implements \IteratorAggregate<Branch<TItem>>
*
* @internal
*/
final class Tree implements \IteratorAggregate
{
/** @var array<non-empty-string, Branch<TItem>> */
private array $root = [];

/** @var array<non-empty-string, Branch<TItem>> */
private array $all = [];

/** @var array<non-empty-string, Branch<TItem>> */
private array $lostChildren = [];

/**
* @template-covariant T of object
*
* @param list<T> $edges
* @param callable(T): non-empty-string $getCurrent Get current node id
* @param callable(T): (non-empty-string|null) $getParent Get parent node id
*
* @return self<T>
*/
public static function fromEdgesList(array $edges, callable $getCurrent, callable $getParent): self

Check warning on line 34 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L34

Added line #L34 was not covered by tests
{
$tree = new self();

Check warning on line 36 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L36

Added line #L36 was not covered by tests

foreach ($edges as $edge) {
$current = $getCurrent($edge);
$parent = $getParent($edge);

Check warning on line 40 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L38-L40

Added lines #L38 - L40 were not covered by tests

$tree->addItem($edge, $current, $parent);

Check warning on line 42 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L42

Added line #L42 was not covered by tests
}

return $tree;

Check warning on line 45 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L45

Added line #L45 was not covered by tests
}

/**
* @param TItem $item
* @param non-empty-string $id
* @param non-empty-string|null $parentId
*/
public function addItem(object $item, string $id, ?string $parentId): void

Check warning on line 53 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L53

Added line #L53 was not covered by tests
{
$branch = new Branch($item, $id, $parentId);
$this->all[$id] = $branch;

Check warning on line 56 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L55-L56

Added lines #L55 - L56 were not covered by tests

if ($parentId === null) {
$this->root[$id] = $branch;

Check warning on line 59 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L58-L59

Added lines #L58 - L59 were not covered by tests
} else {
$branch->parent = $this->all[$parentId] ?? null;

Check warning on line 61 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L61

Added line #L61 was not covered by tests

$branch->parent === null
? $this->lostChildren[$id] = $branch
: $branch->parent->children[] = $branch;

Check warning on line 65 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L63-L65

Added lines #L63 - L65 were not covered by tests
}

foreach ($this->lostChildren as $lostChild) {
if ($lostChild->parentId === $id) {
$branch->children[] = $lostChild;
unset($this->lostChildren[$lostChild->id]);

Check warning on line 71 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L68-L71

Added lines #L68 - L71 were not covered by tests
}
}
}

/**
* Iterate all the branches without sorting and hierarchy.
*
* @return \Traversable<non-empty-string, Branch<TItem>>
*/
public function getIterator(): \Traversable

Check warning on line 81 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L81

Added line #L81 was not covered by tests
{
yield from $this->all;

Check warning on line 83 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L83

Added line #L83 was not covered by tests
}

/**
* Yield items by the level in the hierarchy with custom sorting in level scope
*
* @param callable(Branch<TItem>, Branch<TItem>): int $sorter
*
* @return \Traversable<TItem>
*/
public function getItemsSortedV1(?callable $sorter): \Traversable

Check warning on line 93 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L93

Added line #L93 was not covered by tests
{
$level = 0;
$queue = [$level => $this->root];

Check warning on line 96 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L95-L96

Added lines #L95 - L96 were not covered by tests
processLevel:
while ($queue[$level] !== []) {
$branch = \array_shift($queue[$level]);
yield $branch->item;

Check warning on line 100 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L98-L100

Added lines #L98 - L100 were not covered by tests

// Fill the next level
$queue[$level + 1] ??= [];
\array_unshift($queue[$level + 1], ...$branch->children);

Check warning on line 104 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L103-L104

Added lines #L103 - L104 were not covered by tests
}

if (\array_key_exists(++$level, $queue)) {
$sorter === null or \usort($queue[$level], $sorter);

Check warning on line 108 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L107-L108

Added lines #L107 - L108 were not covered by tests

goto processLevel;

Check warning on line 110 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L110

Added line #L110 was not covered by tests
}
}


/**
* Yield items deep-first.
*
* @param callable(Branch<TItem>, Branch<TItem>): int $sorter
*
* @return \Traversable<TItem>
*/
public function getItemsSortedV0(?callable $sorter): \Traversable

Check warning on line 122 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L122

Added line #L122 was not covered by tests
{
$queue = $this->root;
while (\count($queue) > 0) {
$branch = \array_shift($queue);
yield $branch->item;

Check warning on line 127 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L124-L127

Added lines #L124 - L127 were not covered by tests

$children = $branch->children;
$sorter === null or \usort($children, $sorter);

Check warning on line 130 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L129-L130

Added lines #L129 - L130 were not covered by tests

\array_unshift($queue, ...$children);

Check warning on line 132 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L132

Added line #L132 was not covered by tests
}
}

public function __destruct()

Check warning on line 136 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L136

Added line #L136 was not covered by tests
{
foreach ($this->all as $branch) {
$branch->__destruct();

Check warning on line 139 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L138-L139

Added lines #L138 - L139 were not covered by tests
}

unset($this->all, $this->root, $this->lostChildren);

Check warning on line 142 in src/Service/FilesObserver/Converter/Tree.php

View check run for this annotation

Codecov / codecov/patch

src/Service/FilesObserver/Converter/Tree.php#L142

Added line #L142 was not covered by tests
}
}
Loading

0 comments on commit de2277c

Please sign in to comment.