Skip to content

Commit

Permalink
Merge pull request #199 from buggregator/feature/195
Browse files Browse the repository at this point in the history
Improved Profiler Module:
  • Loading branch information
butschster committed Jun 9, 2024
2 parents ccc422c + 8caa026 commit 5ea86e4
Show file tree
Hide file tree
Showing 38 changed files with 1,564 additions and 11 deletions.
1 change: 1 addition & 0 deletions app/config/queue.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'aliases' => [
'webhooks' => $defaultConnection,
'events' => $defaultConnection,
'profiler' => $defaultConnection,
],
'pipelines' => [
'memory' => [
Expand Down
17 changes: 15 additions & 2 deletions app/config/storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
'public' => ['file' => 0644, 'dir' => 0755],
'private' => ['file' => 0600, 'dir' => 0700],

'default' => 'public',
],
],
'profiles' => [
'adapter' => 'local',
'directory' => directory('runtime') . 'profiles',
'visibility' => [
'public' => ['file' => 0644, 'dir' => 0755],
'private' => ['file' => 0600, 'dir' => 0700],

'default' => 'public',
],
],
Expand All @@ -21,11 +31,14 @@
'buckets' => [
'smtp' => [
'server' => 'attachments',
'prefix' => 'attachments',
'prefix' => 'smtp',
],
'http_dumps' => [
'server' => 'attachments',
'prefix' => 'attachments',
'prefix' => 'http_dumps',
],
'profiles' => [
'server' => 'profiles',
],
],
];
1 change: 1 addition & 0 deletions app/config/tokenizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
'exclude' => [
directory('resources'),
directory('config'),
directory('vendor'),
'tests',
],
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Database\Migrations;

use Cycle\Migrations\Migration;

class OrmDefault178280ff82db54b14ccc25ce1028b465 extends Migration
{
protected const DATABASE = 'default';

public function up(): void
{
$this->table('profiles')
->addColumn('uuid', 'string', ['nullable' => false, 'defaultValue' => null, 'size' => 36])
->addColumn('name', 'string', ['nullable' => false, 'defaultValue' => null, 'size' => 255])
->addColumn('cpu', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('wt', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('ct', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('mu', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('pmu', 'integer', ['nullable' => false, 'defaultValue' => null])
->setPrimaryKeys(['uuid'])
->create();
}

public function down(): void
{
$this->table('profiles')->drop();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace Database\Migrations;

use Cycle\Migrations\Migration;

class OrmDefault972859df19369e40b49f2fae46c0a310 extends Migration
{
protected const DATABASE = 'default';

public function up(): void
{
$this->table('profile_edges')
->addColumn('uuid', 'string', ['nullable' => false, 'defaultValue' => null, 'size' => 36])
->addColumn('profile_uuid', 'string', ['nullable' => false, 'defaultValue' => null, 'size' => 36])
->addColumn('order', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('callee', 'text', ['nullable' => false, 'defaultValue' => null])
->addColumn('caller', 'text', ['nullable' => true, 'defaultValue' => null])
->addColumn('parent_uuid', 'string', ['nullable' => true, 'defaultValue' => null, 'size' => 36])
->addColumn('cpu', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('wt', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('ct', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('mu', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('pmu', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('d_cpu', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('d_wt', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('d_ct', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('d_mu', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('d_pmu', 'integer', ['nullable' => false, 'defaultValue' => null])
->addColumn('p_cpu', 'float', ['nullable' => false, 'defaultValue' => null])
->addColumn('p_wt', 'float', ['nullable' => false, 'defaultValue' => null])
->addColumn('p_ct', 'float', ['nullable' => false, 'defaultValue' => null])
->addColumn('p_mu', 'float', ['nullable' => false, 'defaultValue' => null])
->addColumn('p_pmu', 'float', ['nullable' => false, 'defaultValue' => null])
->addIndex(['profile_uuid'], ['name' => 'profile_edges_index_profile_uuid_66643ff3139b6', 'unique' => false],
)
->addIndex(['parent_uuid'], ['name' => 'profile_edges_index_parent_uuid_66643ff3139e7', 'unique' => false])
->addForeignKey(['profile_uuid'], 'profiles', ['uuid'], [
'name' => 'profile_edges_foreign_profile_uuid_66643ff3139c9',
'delete' => 'CASCADE',
'update' => 'CASCADE',
'indexCreate' => true,
])
->addForeignKey(['parent_uuid'], 'profile_edges', ['uuid'], [
'name' => 'profile_edges_foreign_parent_uuid_66643ff3139f1',
'delete' => 'CASCADE',
'update' => 'CASCADE',
'indexCreate' => true,
])
->setPrimaryKeys(['uuid'])
->create();
}

public function down(): void
{
$this->table('profile_edges')->drop();
}
}
11 changes: 9 additions & 2 deletions app/modules/Events/Integration/CycleOrm/EventRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,16 @@ public function deleteAll(array $scope = []): void
->where($this->buildScope($scope))
->fetchAll();

$batchSize = 100;

$batch = 0;
foreach ($events as $event) {
$this->em->delete($event);

if ($batch++ % $batchSize === 0) {
$this->em->run();
$batch = 0;
}
}

$this->em->run();
Expand All @@ -58,8 +66,7 @@ public function deleteByPK(string $uuid): bool
return false;
}

$this->em->delete($event);
$this->em->run();
$this->em->delete($event)->run();

return true;
}
Expand Down
14 changes: 14 additions & 0 deletions app/modules/Profiler/Application/CallGraph/Metric.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Modules\Profiler\Application\CallGraph;

enum Metric: string
{
case CPU = 'cpu';
case WallTime = 'wt';
case MemoryChange = 'pmu';
case Memory = 'mu';
case Calls = 'ct';
}
126 changes: 126 additions & 0 deletions app/modules/Profiler/Application/CallGraph/Node.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

declare(strict_types=1);

namespace Modules\Profiler\Application\CallGraph;

use App\Application\Domain\ValueObjects\Uuid;
use Modules\Profiler\Domain\Edge;

final readonly class Node implements \JsonSerializable
{
public static function fromEdge(
Edge $edge,
Metric $metric,
int $maxColorPercentage,
): self {
return new self(
$edge->getUuid(),
$edge->getCallee(),
$metric,
$edge->getCost(),
$edge->getPercents(),
$maxColorPercentage,
);
}

public string $color;
public string $textColor;
public string $label;

public function __construct(
public Uuid $uuid,
public string $callee,
public Metric $metric,
public Edge\Cost $cost,
public Edge\Percents $percents,
public int $maxColorPercentage,
) {
$this->color = $this->isImportant() ? $this->detectNodeColor() : '#FFFFFF';
$this->textColor = $this->isImportant() ? $this->detectTextColor($this->color) : '#000000';
$this->label = $this->buildLabel();
}

public function isImportant(): bool
{
return $this->getPercentsMetric() >= $this->maxColorPercentage;
}

public function isSatisfied(int|float $threshold): bool
{
return $this->getPercentsMetric() <= $threshold;
}

public function getCostMetric(): float|int
{
return $this->cost->{$this->metric->value};
}

public function getPercentsMetric(): float|int
{
return $this->percents->{$this->metric->value};
}

public function jsonSerialize(): array
{
return [
'id' => (string) $this->uuid,
'name' => $this->label,
'cost' => [
'cpu' => $this->cost->cpu,
'wt' => $this->cost->wt,
'pmu' => $this->cost->pmu,
'mu' => $this->cost->mu,
'ct' => $this->cost->ct,
],
'metrics' => [
'cost' => $this->getCostMetric(),
'percents' => $this->getPercentsMetric(),
],
'color' => $this->color,
'textColor' => $this->textColor,
];
}

private function buildLabel(): string
{
if ($this->cost->ct > 0) {
return \sprintf(
'%s (%sx)',
$this->callee,
$this->cost->ct,
);
}

return $this->callee;
}

private function detectNodeColor(): string
{
$percent = $this->getPercentsMetric();

return match (true) {
$percent <= 10 => '#FFFFFF', // White
$percent <= 20 => '#f19797', // Lighter shade towards dark red
$percent <= 30 => '#d93939', // Light shade towards dark red
$percent <= 40 => '#ad1e1e', // Intermediate lighter shade towards dark red
$percent <= 50 => '#982525', // Intermediate shade towards dark red
$percent <= 60 => '#862323', // Intermediate darker shade towards dark red
$percent <= 70 => '#671d1d', // Darker shade towards dark red
$percent <= 80 => '#540d0d', // More towards dark red
$percent <= 90 => '#340707', // Almost dark red
default => '#000000', // Black
};
}

private function detectTextColor(string $nodeColor): string
{
$hex = \ltrim($nodeColor, '#');
$r = \hexdec(\substr($hex, 0, 2));
$g = \hexdec(\substr($hex, 2, 2));
$b = \hexdec(\substr($hex, 4, 2));
$brightness = ($r * 299 + $g * 587 + $b * 114) / 1000;

return $brightness > 125 ? '#000000' : '#FFFFFF';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Modules\Profiler\Application\EventHandlerInterface;

// TODO: fix diff calculation
final class CalculateDiffsBetweenEdges implements EventHandlerInterface
{
public function handle(array $event): array
Expand Down
26 changes: 20 additions & 6 deletions app/modules/Profiler/Application/Handlers/PrepareEdges.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,39 @@ public function handle(array $event): array
$data = \array_reverse($event['profile'] ?? []);

$event['edges'] = [];
$parents = [];

$prev = null;

$id = 1;
foreach ($data as $name => $values) {
[$parent, $func] = $this->splitName($name);
$values = \array_merge($values, [
'p_cpu' => round($values['cpu'] > 0 ? ($values['cpu'] / $event['peaks']['cpu'] * 100) : 0, 2),
'p_mu' => round($values['mu'] > 0 ? ($values['mu'] / $event['peaks']['mu'] * 100) : 0, 2),
'p_pmu' => round($values['pmu'] > 0 ? ($values['pmu'] / $event['peaks']['pmu'] * 100) : 0, 2),
'p_wt' => round($values['wt'] > 0 ? ($values['wt'] / $event['peaks']['wt'] * 100) : 0, 2),
]);

$parentId = $parents[$parent] ?? $prev;

foreach (['cpu', 'mu', 'pmu', 'wt'] as $key) {
$values['p_' . $key] = \round(
$values[$key] > 0 ? ($values[$key]) / $event['peaks'][$key] * 100 : 0,
3,
);
}

$event['edges']['e' . $id] = [
'id' => 'e' . $id,
'caller' => $parent,
'callee' => $func,
'cost' => $values,
'parent' => $parentId,
];

$parents[$func] = 'e' . $id;
$prev = 'e' . $id;

$id++;
}

$event['total_edges'] = \count($event['edges']);

return $event;
}

Expand Down
3 changes: 3 additions & 0 deletions app/modules/Profiler/Application/Handlers/PreparePeaks.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ final class PreparePeaks implements EventHandlerInterface
{
public function handle(array $event): array
{
// TODO: fix peaks calculation
// @see \Modules\Profiler\Interfaces\Queries\FindTopFunctionsByUuidHandler:$overallTotals
$event['peaks'] = $event['profile']['main()'] ?? [
'wt' => 0,
'ct' => 0,
'mu' => 0,
'pmu' => 0,
'cpu' => 0,
];

return $event;
Expand Down
Loading

0 comments on commit 5ea86e4

Please sign in to comment.