mirror of
https://devops.lemonos.cn/lawson/FendxPHP.git
synced 2026-06-15 23:12:49 +08:00
670 lines
19 KiB
PHP
670 lines
19 KiB
PHP
|
|
<?php
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Fendx\Debug;
|
||
|
|
|
||
|
|
use Fendx\Debug\Memory\MemorySnapshot;
|
||
|
|
use Fendx\Debug\Memory\MemoryLeakDetector;
|
||
|
|
use Fendx\Debug\Memory\MemoryUsageTracker;
|
||
|
|
|
||
|
|
class MemoryAnalyzer
|
||
|
|
{
|
||
|
|
protected static ?self $instance = null;
|
||
|
|
protected bool $enabled = false;
|
||
|
|
protected array $snapshots = [];
|
||
|
|
protected MemoryUsageTracker $tracker;
|
||
|
|
protected MemoryLeakDetector $leakDetector;
|
||
|
|
protected array $config = [];
|
||
|
|
protected int $baselineMemory;
|
||
|
|
|
||
|
|
public function __construct(array $config = [])
|
||
|
|
{
|
||
|
|
$this->config = array_merge($this->getDefaultConfig(), $config);
|
||
|
|
$this->tracker = new MemoryUsageTracker();
|
||
|
|
$this->leakDetector = new MemoryLeakDetector();
|
||
|
|
$this->baselineMemory = memory_get_usage(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get memory analyzer instance.
|
||
|
|
*/
|
||
|
|
public static function getInstance(array $config = []): self
|
||
|
|
{
|
||
|
|
if (self::$instance === null) {
|
||
|
|
self::$instance = new self($config);
|
||
|
|
}
|
||
|
|
|
||
|
|
return self::$instance;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Enable memory analyzer.
|
||
|
|
*/
|
||
|
|
public function enable(): void
|
||
|
|
{
|
||
|
|
$this->enabled = true;
|
||
|
|
$this->tracker->enable();
|
||
|
|
$this->leakDetector->enable();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Disable memory analyzer.
|
||
|
|
*/
|
||
|
|
public function disable(): void
|
||
|
|
{
|
||
|
|
$this->enabled = false;
|
||
|
|
$this->tracker->disable();
|
||
|
|
$this->leakDetector->disable();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if analyzer is enabled.
|
||
|
|
*/
|
||
|
|
public function isEnabled(): bool
|
||
|
|
{
|
||
|
|
return $this->enabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Take memory snapshot.
|
||
|
|
*/
|
||
|
|
public function snapshot(string $name, array $context = []): MemorySnapshot
|
||
|
|
{
|
||
|
|
if (!$this->enabled) {
|
||
|
|
throw new \RuntimeException('Memory analyzer is not enabled');
|
||
|
|
}
|
||
|
|
|
||
|
|
$snapshot = new MemorySnapshot($name, $context);
|
||
|
|
$snapshot->capture();
|
||
|
|
|
||
|
|
$this->snapshots[$name] = $snapshot;
|
||
|
|
|
||
|
|
return $snapshot;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get memory snapshot by name.
|
||
|
|
*/
|
||
|
|
public function getSnapshot(string $name): ?MemorySnapshot
|
||
|
|
{
|
||
|
|
return $this->snapshots[$name] ?? null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get all snapshots.
|
||
|
|
*/
|
||
|
|
public function getSnapshots(): array
|
||
|
|
{
|
||
|
|
return $this->snapshots;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Compare two snapshots.
|
||
|
|
*/
|
||
|
|
public function compare(string $from, string $to): array
|
||
|
|
{
|
||
|
|
$fromSnapshot = $this->getSnapshot($from);
|
||
|
|
$toSnapshot = $this->getSnapshot($to);
|
||
|
|
|
||
|
|
if (!$fromSnapshot || !$toSnapshot) {
|
||
|
|
throw new \InvalidArgumentException('Both snapshots must exist');
|
||
|
|
}
|
||
|
|
|
||
|
|
return [
|
||
|
|
'from' => $fromSnapshot->getName(),
|
||
|
|
'to' => $toSnapshot->getName(),
|
||
|
|
'memory_diff' => $toSnapshot->getMemoryUsage() - $fromSnapshot->getMemoryUsage(),
|
||
|
|
'memory_diff_percent' => $this->calculatePercentageDiff(
|
||
|
|
$fromSnapshot->getMemoryUsage(),
|
||
|
|
$toSnapshot->getMemoryUsage()
|
||
|
|
),
|
||
|
|
'peak_memory_diff' => $toSnapshot->getPeakMemory() - $fromSnapshot->getPeakMemory(),
|
||
|
|
'objects_diff' => $toSnapshot->getObjectCount() - $fromSnapshot->getObjectCount(),
|
||
|
|
'time_diff' => $toSnapshot->getTimestamp() - $fromSnapshot->getTimestamp()
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get memory usage trend.
|
||
|
|
*/
|
||
|
|
public function getTrend(): array
|
||
|
|
{
|
||
|
|
if (empty($this->snapshots)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$trend = [];
|
||
|
|
$previousMemory = $this->baselineMemory;
|
||
|
|
|
||
|
|
foreach ($this->snapshots as $name => $snapshot) {
|
||
|
|
$currentMemory = $snapshot->getMemoryUsage();
|
||
|
|
$diff = $currentMemory - $previousMemory;
|
||
|
|
|
||
|
|
$trend[] = [
|
||
|
|
'name' => $name,
|
||
|
|
'timestamp' => $snapshot->getTimestamp(),
|
||
|
|
'memory_usage' => $currentMemory,
|
||
|
|
'memory_diff' => $diff,
|
||
|
|
'memory_diff_percent' => $this->calculatePercentageDiff($previousMemory, $currentMemory),
|
||
|
|
'peak_memory' => $snapshot->getPeakMemory(),
|
||
|
|
'object_count' => $snapshot->getObjectCount()
|
||
|
|
];
|
||
|
|
|
||
|
|
$previousMemory = $currentMemory;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $trend;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Detect memory leaks.
|
||
|
|
*/
|
||
|
|
public function detectLeaks(): array
|
||
|
|
{
|
||
|
|
if (!$this->enabled) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->leakDetector->detect($this->snapshots);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get memory usage statistics.
|
||
|
|
*/
|
||
|
|
public function getStatistics(): array
|
||
|
|
{
|
||
|
|
if (empty($this->snapshots)) {
|
||
|
|
return [
|
||
|
|
'current' => memory_get_usage(true),
|
||
|
|
'peak' => memory_get_peak_usage(true),
|
||
|
|
'baseline' => $this->baselineMemory,
|
||
|
|
'growth' => memory_get_usage(true) - $this->baselineMemory,
|
||
|
|
'snapshots_count' => 0
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
$current = memory_get_usage(true);
|
||
|
|
$peak = memory_get_peak_usage(true);
|
||
|
|
$growth = $current - $this->baselineMemory;
|
||
|
|
|
||
|
|
$memoryUsages = array_map(fn($s) => $s->getMemoryUsage(), $this->snapshots);
|
||
|
|
$objectCounts = array_map(fn($s) => $s->getObjectCount(), $this->snapshots);
|
||
|
|
|
||
|
|
return [
|
||
|
|
'current' => $current,
|
||
|
|
'peak' => $peak,
|
||
|
|
'baseline' => $this->baselineMemory,
|
||
|
|
'growth' => $growth,
|
||
|
|
'growth_percent' => $this->calculatePercentageDiff($this->baselineMemory, $current),
|
||
|
|
'snapshots_count' => count($this->snapshots),
|
||
|
|
'min_memory' => min($memoryUsages),
|
||
|
|
'max_memory' => max($memoryUsages),
|
||
|
|
'avg_memory' => array_sum($memoryUsages) / count($memoryUsages),
|
||
|
|
'min_objects' => min($objectCounts),
|
||
|
|
'max_objects' => max($objectCounts),
|
||
|
|
'avg_objects' => array_sum($objectCounts) / count($objectCounts),
|
||
|
|
'memory_efficiency' => $this->calculateMemoryEfficiency(),
|
||
|
|
'leak_detected' => !empty($this->detectLeaks())
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get memory usage by type.
|
||
|
|
*/
|
||
|
|
public function getUsageByType(): array
|
||
|
|
{
|
||
|
|
if (empty($this->snapshots)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$latest = end($this->snapshots);
|
||
|
|
return $latest->getUsageByType();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get largest memory consumers.
|
||
|
|
*/
|
||
|
|
public function getLargestConsumers(int $limit = 10): array
|
||
|
|
{
|
||
|
|
if (empty($this->snapshots)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$latest = end($this->snapshots);
|
||
|
|
return $latest->getLargestConsumers($limit);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate memory analysis report.
|
||
|
|
*/
|
||
|
|
public function generateReport(): array
|
||
|
|
{
|
||
|
|
$report = [
|
||
|
|
'summary' => $this->getStatistics(),
|
||
|
|
'trend' => $this->getTrend(),
|
||
|
|
'snapshots' => [],
|
||
|
|
'leaks' => $this->detectLeaks(),
|
||
|
|
'usage_by_type' => $this->getUsageByType(),
|
||
|
|
'largest_consumers' => $this->getLargestConsumers(),
|
||
|
|
'recommendations' => $this->generateRecommendations(),
|
||
|
|
'generated_at' => time()
|
||
|
|
];
|
||
|
|
|
||
|
|
foreach ($this->snapshots as $name => $snapshot) {
|
||
|
|
$report['snapshots'][$name] = $snapshot->toArray();
|
||
|
|
}
|
||
|
|
|
||
|
|
return $report;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Save memory analysis report.
|
||
|
|
*/
|
||
|
|
public function saveReport(string $filename): bool
|
||
|
|
{
|
||
|
|
$report = $this->generateReport();
|
||
|
|
$json = json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||
|
|
|
||
|
|
return file_put_contents($filename, $json) !== false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Export memory data.
|
||
|
|
*/
|
||
|
|
public function export(string $format = 'json'): string
|
||
|
|
{
|
||
|
|
$report = $this->generateReport();
|
||
|
|
|
||
|
|
switch (strtolower($format)) {
|
||
|
|
case 'json':
|
||
|
|
return json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||
|
|
|
||
|
|
case 'csv':
|
||
|
|
return $this->exportToCsv($report);
|
||
|
|
|
||
|
|
case 'html':
|
||
|
|
return $this->exportToHtml($report);
|
||
|
|
|
||
|
|
default:
|
||
|
|
throw new \InvalidArgumentException("Unsupported export format: {$format}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Export to CSV format.
|
||
|
|
*/
|
||
|
|
protected function exportToCsv(array $report): string
|
||
|
|
{
|
||
|
|
$csv = "Snapshot,Timestamp,Memory Usage (bytes),Peak Memory (bytes),Object Count\n";
|
||
|
|
|
||
|
|
foreach ($report['snapshots'] as $name => $snapshot) {
|
||
|
|
$csv .= sprintf(
|
||
|
|
"%s,%d,%d,%d,%d\n",
|
||
|
|
$name,
|
||
|
|
$snapshot['timestamp'],
|
||
|
|
$snapshot['memory_usage'],
|
||
|
|
$snapshot['peak_memory'],
|
||
|
|
$snapshot['object_count']
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return $csv;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Export to HTML format.
|
||
|
|
*/
|
||
|
|
protected function exportToHtml(array $report): string
|
||
|
|
{
|
||
|
|
$html = '<!DOCTYPE html><html><head><title>Memory Analysis Report</title>';
|
||
|
|
$html .= '<style>body{font-family:Arial,sans-serif;margin:20px;}';
|
||
|
|
$html .= 'table{border-collapse:collapse;width:100%;margin-bottom:20px;}';
|
||
|
|
$html .= 'th,td{border:1px solid #ddd;padding:8px;text-align:left;}';
|
||
|
|
$html .= 'th{background-color:#f2f2f2;}';
|
||
|
|
$html .= '.warning{color:#f39c12;}.error{color:#e74c3c;}.success{color:#27ae60;}</style>';
|
||
|
|
$html .= '</head><body>';
|
||
|
|
|
||
|
|
$html .= '<h1>Memory Analysis Report</h1>';
|
||
|
|
|
||
|
|
// Summary section
|
||
|
|
$html .= '<h2>Summary</h2>';
|
||
|
|
$html .= '<table><tr><th>Metric</th><th>Value</th></tr>';
|
||
|
|
|
||
|
|
foreach ($report['summary'] as $key => $value) {
|
||
|
|
if (is_array($value)) {
|
||
|
|
$value = json_encode($value);
|
||
|
|
} elseif (is_bool($value)) {
|
||
|
|
$value = $value ? 'Yes' : 'No';
|
||
|
|
}
|
||
|
|
|
||
|
|
$class = '';
|
||
|
|
if (str_contains($key, 'leak') && $value) {
|
||
|
|
$class = 'error';
|
||
|
|
} elseif (str_contains($key, 'growth') && $value > 0) {
|
||
|
|
$class = 'warning';
|
||
|
|
}
|
||
|
|
|
||
|
|
$html .= "<tr><td>{$key}</td><td class='{$class}'>{$value}</td></tr>";
|
||
|
|
}
|
||
|
|
|
||
|
|
$html .= '</table>';
|
||
|
|
|
||
|
|
// Snapshots section
|
||
|
|
$html .= '<h2>Memory Snapshots</h2>';
|
||
|
|
$html .= '<table><tr><th>Name</th><th>Memory (MB)</th><th>Peak (MB)</th><th>Objects</th></tr>';
|
||
|
|
|
||
|
|
foreach ($report['snapshots'] as $name => $snapshot) {
|
||
|
|
$html .= sprintf(
|
||
|
|
"<tr><td>%s</td><td>%.2f</td><td>%.2f</td><td>%d</td></tr>",
|
||
|
|
$name,
|
||
|
|
$snapshot['memory_usage'] / 1024 / 1024,
|
||
|
|
$snapshot['peak_memory'] / 1024 / 1024,
|
||
|
|
$snapshot['object_count']
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
$html .= '</table>';
|
||
|
|
|
||
|
|
// Recommendations section
|
||
|
|
if (!empty($report['recommendations'])) {
|
||
|
|
$html .= '<h2>Recommendations</h2>';
|
||
|
|
$html .= '<ul>';
|
||
|
|
|
||
|
|
foreach ($report['recommendations'] as $rec) {
|
||
|
|
$class = $rec['severity'] === 'error' ? 'error' :
|
||
|
|
($rec['severity'] === 'warning' ? 'warning' : '');
|
||
|
|
$html .= "<li class='{$class}'>{$rec['message']}</li>";
|
||
|
|
}
|
||
|
|
|
||
|
|
$html .= '</ul>';
|
||
|
|
}
|
||
|
|
|
||
|
|
$html .= '</body></html>';
|
||
|
|
|
||
|
|
return $html;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clear all snapshots.
|
||
|
|
*/
|
||
|
|
public function clear(): void
|
||
|
|
{
|
||
|
|
$this->snapshots = [];
|
||
|
|
$this->tracker->clear();
|
||
|
|
$this->leakDetector->clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Reset analyzer.
|
||
|
|
*/
|
||
|
|
public function reset(): void
|
||
|
|
{
|
||
|
|
$this->clear();
|
||
|
|
$this->baselineMemory = memory_get_usage(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get configuration.
|
||
|
|
*/
|
||
|
|
public function getConfig(): array
|
||
|
|
{
|
||
|
|
return $this->config;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set configuration.
|
||
|
|
*/
|
||
|
|
public function setConfig(array $config): void
|
||
|
|
{
|
||
|
|
$this->config = array_merge($this->config, $config);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get memory usage tracker.
|
||
|
|
*/
|
||
|
|
public function getTracker(): MemoryUsageTracker
|
||
|
|
{
|
||
|
|
return $this->tracker;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get memory leak detector.
|
||
|
|
*/
|
||
|
|
public function getLeakDetector(): MemoryLeakDetector
|
||
|
|
{
|
||
|
|
return $this->leakDetector;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Calculate percentage difference.
|
||
|
|
*/
|
||
|
|
protected function calculatePercentageDiff(int $from, int $to): float
|
||
|
|
{
|
||
|
|
if ($from === 0) {
|
||
|
|
return $to > 0 ? 100 : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (($to - $from) / $from) * 100;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Calculate memory efficiency.
|
||
|
|
*/
|
||
|
|
protected function calculateMemoryEfficiency(): float
|
||
|
|
{
|
||
|
|
if (empty($this->snapshots)) {
|
||
|
|
return 100;
|
||
|
|
}
|
||
|
|
|
||
|
|
$totalMemory = array_sum(array_map(fn($s) => $s->getMemoryUsage(), $this->snapshots));
|
||
|
|
$avgMemory = $totalMemory / count($this->snapshots);
|
||
|
|
$peakMemory = memory_get_peak_usage(true);
|
||
|
|
|
||
|
|
if ($peakMemory === 0) {
|
||
|
|
return 100;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ($avgMemory / $peakMemory) * 100;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate memory recommendations.
|
||
|
|
*/
|
||
|
|
protected function generateRecommendations(): array
|
||
|
|
{
|
||
|
|
$recommendations = [];
|
||
|
|
$stats = $this->getStatistics();
|
||
|
|
|
||
|
|
// Check for memory growth
|
||
|
|
if ($stats['growth'] > $this->config['growth_threshold']) {
|
||
|
|
$recommendations[] = [
|
||
|
|
'type' => 'memory',
|
||
|
|
'severity' => 'warning',
|
||
|
|
'message' => sprintf(
|
||
|
|
'High memory growth detected: %s (%.1f%%)',
|
||
|
|
$this->formatBytes($stats['growth']),
|
||
|
|
$stats['growth_percent']
|
||
|
|
),
|
||
|
|
'suggestion' => 'Review memory allocation patterns and consider optimization'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for memory leaks
|
||
|
|
if ($stats['leak_detected']) {
|
||
|
|
$recommendations[] = [
|
||
|
|
'type' => 'leak',
|
||
|
|
'severity' => 'error',
|
||
|
|
'message' => 'Memory leaks detected',
|
||
|
|
'suggestion' => 'Investigate object references and ensure proper cleanup'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check memory efficiency
|
||
|
|
if ($stats['memory_efficiency'] < $this->config['efficiency_threshold']) {
|
||
|
|
$recommendations[] = [
|
||
|
|
'type' => 'efficiency',
|
||
|
|
'severity' => 'info',
|
||
|
|
'message' => sprintf(
|
||
|
|
'Low memory efficiency: %.1f%%',
|
||
|
|
$stats['memory_efficiency']
|
||
|
|
),
|
||
|
|
'suggestion' => 'Consider memory optimization techniques'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check peak memory usage
|
||
|
|
if ($stats['peak'] > $this->config['peak_threshold']) {
|
||
|
|
$recommendations[] = [
|
||
|
|
'type' => 'peak',
|
||
|
|
'severity' => 'warning',
|
||
|
|
'message' => sprintf(
|
||
|
|
'High peak memory usage: %s',
|
||
|
|
$this->formatBytes($stats['peak'])
|
||
|
|
),
|
||
|
|
'suggestion' => 'Monitor memory usage patterns and optimize peak consumption'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
return $recommendations;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get default configuration.
|
||
|
|
*/
|
||
|
|
protected function getDefaultConfig(): array
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'growth_threshold' => 50 * 1024 * 1024, // 50MB
|
||
|
|
'peak_threshold' => 100 * 1024 * 1024, // 100MB
|
||
|
|
'efficiency_threshold' => 70, // 70%
|
||
|
|
'auto_snapshot' => true,
|
||
|
|
'snapshot_interval' => 1000, // ms
|
||
|
|
'max_snapshots' => 100,
|
||
|
|
'track_objects' => true,
|
||
|
|
'track_types' => true,
|
||
|
|
'detect_leaks' => true
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Format bytes to human readable format.
|
||
|
|
*/
|
||
|
|
protected function formatBytes(int $bytes): string
|
||
|
|
{
|
||
|
|
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||
|
|
$bytes = max($bytes, 0);
|
||
|
|
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||
|
|
$pow = min($pow, count($units) - 1);
|
||
|
|
|
||
|
|
$bytes /= (1 << (10 * $pow));
|
||
|
|
|
||
|
|
return round($bytes, 2) . ' ' . $units[$pow];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Monitor memory usage continuously.
|
||
|
|
*/
|
||
|
|
public function startMonitoring(): void
|
||
|
|
{
|
||
|
|
if (!$this->enabled || !$this->config['auto_snapshot']) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$interval = $this->config['snapshot_interval'] * 1000; // Convert to microseconds
|
||
|
|
|
||
|
|
while ($this->enabled) {
|
||
|
|
$this->snapshot('auto_' . time());
|
||
|
|
usleep($interval);
|
||
|
|
|
||
|
|
// Limit number of snapshots
|
||
|
|
if (count($this->snapshots) > $this->config['max_snapshots']) {
|
||
|
|
array_shift($this->snapshots);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get memory heatmap data.
|
||
|
|
*/
|
||
|
|
public function getHeatmap(): array
|
||
|
|
{
|
||
|
|
if (empty($this->snapshots)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$heatmap = [];
|
||
|
|
$maxMemory = max(array_map(fn($s) => $s->getMemoryUsage(), $this->snapshots));
|
||
|
|
|
||
|
|
foreach ($this->snapshots as $name => $snapshot) {
|
||
|
|
$memory = $snapshot->getMemoryUsage();
|
||
|
|
$intensity = $maxMemory > 0 ? ($memory / $maxMemory) : 0;
|
||
|
|
|
||
|
|
$heatmap[] = [
|
||
|
|
'name' => $name,
|
||
|
|
'timestamp' => $snapshot->getTimestamp(),
|
||
|
|
'memory' => $memory,
|
||
|
|
'intensity' => $intensity,
|
||
|
|
'color' => $this->getHeatmapColor($intensity)
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
return $heatmap;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get heatmap color based on intensity.
|
||
|
|
*/
|
||
|
|
protected function getHeatmapColor(float $intensity): string
|
||
|
|
{
|
||
|
|
if ($intensity < 0.3) {
|
||
|
|
return '#27ae60'; // Green
|
||
|
|
} elseif ($intensity < 0.7) {
|
||
|
|
return '#f39c12'; // Orange
|
||
|
|
} else {
|
||
|
|
return '#e74c3c'; // Red
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Analyze memory patterns.
|
||
|
|
*/
|
||
|
|
public function analyzePatterns(): array
|
||
|
|
{
|
||
|
|
if (empty($this->snapshots)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$patterns = [];
|
||
|
|
$memoryUsages = array_map(fn($s) => $s->getMemoryUsage(), $this->snapshots);
|
||
|
|
|
||
|
|
// Detect growth pattern
|
||
|
|
$isGrowing = $memoryUsages[count($memoryUsages) - 1] > $memoryUsages[0];
|
||
|
|
$patterns['growth_trend'] = $isGrowing ? 'increasing' : 'stable';
|
||
|
|
|
||
|
|
// Detect volatility
|
||
|
|
$avg = array_sum($memoryUsages) / count($memoryUsages);
|
||
|
|
$variance = array_sum(array_map(fn($m) => pow($m - $avg, 2), $memoryUsages)) / count($memoryUsages);
|
||
|
|
$stdDev = sqrt($variance);
|
||
|
|
$patterns['volatility'] = $stdDev / $avg; // Coefficient of variation
|
||
|
|
|
||
|
|
// Detect spikes
|
||
|
|
$spikes = [];
|
||
|
|
for ($i = 1; $i < count($memoryUsages) - 1; $i++) {
|
||
|
|
$prev = $memoryUsages[$i - 1];
|
||
|
|
$current = $memoryUsages[$i];
|
||
|
|
$next = $memoryUsages[$i + 1];
|
||
|
|
|
||
|
|
if ($current > $prev && $current > $next && $current > $avg + $stdDev) {
|
||
|
|
$spikes[] = [
|
||
|
|
'index' => $i,
|
||
|
|
'memory' => $current,
|
||
|
|
'timestamp' => $this->snapshots[$i]->getTimestamp()
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$patterns['spikes'] = $spikes;
|
||
|
|
|
||
|
|
return $patterns;
|
||
|
|
}
|
||
|
|
}
|