# Performance Profiling Comprehensive Performance Profiling System mit Flamegraph-Visualisierung und Timeline-Analyse. ## Übersicht Das Framework bietet ein leistungsstarkes Performance Profiling System mit: - **Nested Performance Tracking**: Hierarchische Performance-Messungen - **Flamegraph Visualization**: Brendan Gregg Flamegraph Format - **Timeline Analysis**: Chronologische Event-Visualisierung - **Memory Tracking**: Speicherverbrauch-Überwachung - **LiveComponent Integration**: Spezialisiertes Component-Profiling ## NestedPerformanceTracker ### Grundlegende Verwendung ```php use App\Framework\Performance\NestedPerformanceTracker; use App\Framework\Performance\PerformanceCategory; use App\Framework\Core\ValueObjects\Duration; // Tracker initialisieren $tracker = $container->get(NestedPerformanceTracker::class); // Einfache Operation messen $result = $tracker->measure( 'database.query', PerformanceCategory::DATABASE, function () use ($userRepository) { return $userRepository->findAll(); } ); // Mit Context-Informationen $result = $tracker->measure( 'api.request', PerformanceCategory::API, function () { return $this->apiClient->fetch('/users'); }, context: ['endpoint' => '/users', 'method' => 'GET'] ); ``` ### Verschachtelte Operationen ```php // Nested Performance Tracking $tracker->measure( 'http.request', PerformanceCategory::CONTROLLER, function () use ($tracker) { // Controller-Logik usleep(5000); // Nested Database Query $tracker->measure( 'database.query', PerformanceCategory::DATABASE, function () { usleep(3000); return ['user_id' => 123]; } ); // Nested Cache Operation $tracker->measure( 'cache.get', PerformanceCategory::CACHE, function () { usleep(1000); return ['cached' => true]; } ); return ['status' => 'success']; } ); ``` ### Manuelle Start/Stop ```php // Für komplexere Szenarien $operationId = $tracker->startOperation( 'complex.operation', PerformanceCategory::CUSTOM, context: ['user_id' => 123] ); try { // Business Logic $this->processData(); $this->saveResults(); } finally { $measurement = $tracker->endOperation($operationId); } ``` ## Flamegraph Visualisierung ### Flamegraph Daten Generieren ```php // Nach Performance-Messungen $tracker->measure('parent.operation', PerformanceCategory::CONTROLLER, function () { $this->tracker->measure('child.operation.1', PerformanceCategory::DATABASE, fn() => sleep(1)); $this->tracker->measure('child.operation.2', PerformanceCategory::CACHE, fn() => sleep(1)); }); // Flamegraph-Daten exportieren $flamegraphData = $tracker->generateFlamegraph(); /* Ausgabe-Format: [ ['stack_trace' => 'parent.operation', 'samples' => 50.5], ['stack_trace' => 'parent.operation;child.operation.1', 'samples' => 1000.2], ['stack_trace' => 'parent.operation;child.operation.2', 'samples' => 1000.1] ] */ ``` ### Flamegraph Visualisierung mit flamegraph.pl ```php // Flamegraph-Format für Brendan Gregg's flamegraph.pl Tool $flamegraphData = $tracker->generateFlamegraph(); // In Brendan Gregg Format konvertieren $output = []; foreach ($flamegraphData as $entry) { $output[] = sprintf( "%s %d", $entry['stack_trace'], (int) ceil($entry['samples']) ); } // In Datei schreiben file_put_contents('/tmp/flamegraph.txt', implode("\n", $output)); // SVG generieren mit flamegraph.pl // flamegraph.pl /tmp/flamegraph.txt > /tmp/flamegraph.svg ``` ### Flamegraph in HTML einbetten ```html Performance Flamegraph
``` ## Timeline Visualisierung ### Timeline Daten Generieren ```php // Nach Performance-Messungen $timeline = $tracker->generateTimeline(); /* Ausgabe-Format: [ [ 'name' => 'database.query', 'category' => 'database', 'start_time' => 1704067200.123456, 'end_time' => 1704067200.234567, 'duration_ms' => 111.111, 'depth' => 0, 'operation_id' => 'database_query_1_abc123', 'parent_id' => null, 'self_time_ms' => 111.111, 'memory_delta_mb' => 2.5, 'context' => ['query' => 'SELECT * FROM users'] ], // ... weitere Events ] */ ``` ### Chrome DevTools Timeline Format ```php // Timeline für Chrome DevTools Performance Tab $timeline = $tracker->generateTimeline(); // In Chrome Trace Event Format konvertieren $traceEvents = []; foreach ($timeline as $event) { // Begin Event $traceEvents[] = [ 'name' => $event['name'], 'cat' => $event['category'], 'ph' => 'B', // Begin 'ts' => (int)($event['start_time'] * 1_000_000), // Microseconds 'pid' => 1, 'tid' => 1, 'args' => $event['context'] ]; // End Event $traceEvents[] = [ 'name' => $event['name'], 'cat' => $event['category'], 'ph' => 'E', // End 'ts' => (int)($event['end_time'] * 1_000_000), 'pid' => 1, 'tid' => 1 ]; } $chromeTrace = [ 'traceEvents' => $traceEvents, 'displayTimeUnit' => 'ms', 'otherData' => [ 'version' => '1.0' ] ]; // Als JSON speichern file_put_contents('/tmp/chrome-trace.json', json_encode($chromeTrace)); // In Chrome öffnen: chrome://tracing → Load → chrome-trace.json ``` ### Waterfall Visualisierung mit HTML/CSS ```html
``` ### Gantt Chart Visualisierung ```php // Gantt Chart Daten vorbereiten $timeline = $tracker->generateTimeline(); $ganttData = array_map(function($event) { return [ 'id' => $event['operation_id'], 'name' => $event['name'], 'start' => date('Y-m-d H:i:s', (int)$event['start_time']), 'end' => date('Y-m-d H:i:s', (int)$event['end_time']), 'duration' => $event['duration_ms'], 'category' => $event['category'], 'parent' => $event['parent_id'], 'metadata' => [ 'self_time' => $event['self_time_ms'], 'memory' => $event['memory_delta_mb'], 'context' => $event['context'] ] ]; }, $timeline); // JSON für Frontend header('Content-Type: application/json'); echo json_encode($ganttData); ``` ## API Controller Integration ### Performance Profiling Endpoint ```php use App\Framework\Attributes\Route; use App\Framework\Http\Method; use App\Framework\Http\Responses\JsonResult; final readonly class PerformanceProfilingController { public function __construct( private NestedPerformanceTracker $tracker ) {} #[Route(path: '/api/performance/flamegraph', method: Method::GET)] public function getFlamegraph(): JsonResult { $flamegraphData = $this->tracker->generateFlamegraph(); return new JsonResult([ 'data' => $flamegraphData, 'total_operations' => count($flamegraphData), 'generated_at' => date('Y-m-d H:i:s') ]); } #[Route(path: '/api/performance/timeline', method: Method::GET)] public function getTimeline(): JsonResult { $timeline = $this->tracker->generateTimeline(); return new JsonResult([ 'events' => $timeline, 'total_events' => count($timeline), 'generated_at' => date('Y-m-d H:i:s') ]); } #[Route(path: '/api/performance/chrome-trace', method: Method::GET)] public function getChromeTrace(): JsonResult { $timeline = $this->tracker->generateTimeline(); // In Chrome Trace Event Format konvertieren $traceEvents = $this->convertToChromeTrace($timeline); return new JsonResult([ 'traceEvents' => $traceEvents, 'displayTimeUnit' => 'ms', 'otherData' => [ 'version' => '1.0', 'framework' => 'Custom PHP Framework' ] ]); } #[Route(path: '/api/performance/summary', method: Method::GET)] public function getSummary(): JsonResult { $summary = $this->tracker->getSummary(); return new JsonResult($summary); } #[Route(path: '/api/performance/reset', method: Method::POST)] public function reset(): JsonResult { $this->tracker->reset(); return new JsonResult([ 'status' => 'reset', 'message' => 'Performance tracker reset successfully' ]); } private function convertToChromeTrace(array $timeline): array { $traceEvents = []; foreach ($timeline as $event) { // Begin Event $traceEvents[] = [ 'name' => $event['name'], 'cat' => $event['category'], 'ph' => 'B', 'ts' => (int)($event['start_time'] * 1_000_000), 'pid' => 1, 'tid' => 1, 'args' => $event['context'] ]; // End Event $traceEvents[] = [ 'name' => $event['name'], 'cat' => $event['category'], 'ph' => 'E', 'ts' => (int)($event['end_time'] * 1_000_000), 'pid' => 1, 'tid' => 1 ]; } return $traceEvents; } } ``` ## LiveComponent Profiling ### Component-spezifisches Profiling ```php use App\Framework\LiveComponents\Profiling\LiveComponentProfiler; final readonly class UserProfileComponent { public function __construct( private LiveComponentProfiler $profiler ) {} public function resolve(array $data): array { // Session-basiertes Profiling starten $sessionId = $this->profiler->startSession('user-profile'); // Resolve Phase profilen $userData = $this->profiler->profileResolve( 'user-profile', fn() => $this->userRepository->find($data['user_id']) ); // Render Phase profilen $html = $this->profiler->profileRender( 'user-profile', fn() => $this->renderTemplate($userData) ); // Session beenden und Ergebnisse erhalten $result = $this->profiler->endSession($sessionId); // Flamegraph exportieren $flamegraph = $result->exportFlamegraph(); // Timeline exportieren $timeline = $result->exportTimeline(); return [ 'html' => $html, 'profiling' => [ 'flamegraph' => $flamegraph, 'timeline' => $timeline, 'total_duration' => $result->getTotalDuration()->toMilliseconds() ] ]; } } ``` ## Performance-Metriken Sammeln ### Metriken zu Prometheus exportieren ```php use App\Framework\Performance\NestedPerformanceTracker; final readonly class PerformanceMetricsExporter { public function __construct( private NestedPerformanceTracker $tracker ) {} public function exportToPrometheus(): string { $summary = $this->tracker->getSummary(); $timeline = $this->tracker->generateTimeline(); $metrics = []; // Total operations counter $metrics[] = sprintf( "# HELP performance_operations_total Total number of operations\n" . "# TYPE performance_operations_total counter\n" . "performance_operations_total %d", $summary['total_operations'] ); // Average duration gauge $metrics[] = sprintf( "# HELP performance_average_duration_ms Average operation duration in milliseconds\n" . "# TYPE performance_average_duration_ms gauge\n" . "performance_average_duration_ms %.2f", $summary['average_duration_ms'] ); // Per-category metrics $byCategory = $this->groupByCategory($timeline); foreach ($byCategory as $category => $events) { $avgDuration = array_sum(array_column($events, 'duration_ms')) / count($events); $metrics[] = sprintf( "performance_category_duration_ms{category=\"%s\"} %.2f", $category, $avgDuration ); } return implode("\n\n", $metrics) . "\n"; } private function groupByCategory(array $timeline): array { $grouped = []; foreach ($timeline as $event) { $category = $event['category']; if (!isset($grouped[$category])) { $grouped[$category] = []; } $grouped[$category][] = $event; } return $grouped; } } ``` ## Best Practices ### 1. Performance Budget Monitoring ```php // Performance Budget definieren $budgets = [ 'database.query' => 100, // max 100ms 'api.request' => 500, // max 500ms 'cache.get' => 10, // max 10ms ]; // Nach Messung validieren $timeline = $tracker->generateTimeline(); foreach ($timeline as $event) { $budget = $budgets[$event['name']] ?? null; if ($budget && $event['duration_ms'] > $budget) { $this->logger->warning('Performance budget exceeded', [ 'operation' => $event['name'], 'duration' => $event['duration_ms'], 'budget' => $budget, 'exceeded_by' => $event['duration_ms'] - $budget ]); } } ``` ### 2. Production Performance Monitoring ```php // Nur bei langsamen Requests profilen final readonly class PerformanceMiddleware implements Middleware { public function process(Request $request, callable $next): Response { $startTime = microtime(true); $response = $next($request); $duration = (microtime(true) - $startTime) * 1000; // Nur bei langsamen Requests (>500ms) Profiling-Daten speichern if ($duration > 500) { $flamegraph = $this->tracker->generateFlamegraph(); $timeline = $this->tracker->generateTimeline(); $this->performanceLogger->logSlowRequest([ 'url' => (string) $request->uri, 'duration_ms' => $duration, 'flamegraph' => $flamegraph, 'timeline' => $timeline ]); } $this->tracker->reset(); return $response; } } ``` ### 3. Automatische Bottleneck-Erkennung ```php // Bottlenecks automatisch identifizieren $timeline = $tracker->generateTimeline(); // Sortiere nach self_time_ms (Zeit ohne Children) usort($timeline, fn($a, $b) => $b['self_time_ms'] <=> $a['self_time_ms']); // Top 5 Bottlenecks $bottlenecks = array_slice($timeline, 0, 5); foreach ($bottlenecks as $bottleneck) { $this->logger->info('Performance bottleneck detected', [ 'operation' => $bottleneck['name'], 'category' => $bottleneck['category'], 'self_time_ms' => $bottleneck['self_time_ms'], 'total_time_ms' => $bottleneck['duration_ms'], 'percentage' => ($bottleneck['self_time_ms'] / $bottleneck['duration_ms']) * 100 ]); } ``` ### 4. Memory Leak Detection ```php // Memory Leaks über Timeline identifizieren $timeline = $tracker->generateTimeline(); $totalMemoryDelta = 0; $suspiciousOperations = []; foreach ($timeline as $event) { $totalMemoryDelta += $event['memory_delta_mb']; // Operationen mit hohem Memory-Verbrauch markieren if ($event['memory_delta_mb'] > 10) { // > 10MB $suspiciousOperations[] = [ 'operation' => $event['name'], 'memory_mb' => $event['memory_delta_mb'], 'context' => $event['context'] ]; } } if (!empty($suspiciousOperations)) { $this->logger->warning('High memory usage detected', [ 'total_memory_delta_mb' => $totalMemoryDelta, 'suspicious_operations' => $suspiciousOperations ]); } ``` ## Testing Performance Profiling ```php // Performance Profiling in Tests it('profiles nested operations correctly', function () { $tracker = new NestedPerformanceTracker( new SystemClock(), new SystemHighResolutionClock(), new MemoryMonitor() ); // Perform tracked operations $tracker->measure('parent', PerformanceCategory::CONTROLLER, function () use ($tracker) { usleep(5000); $tracker->measure('child', PerformanceCategory::DATABASE, function () { usleep(3000); }); }); // Validate flamegraph $flamegraph = $tracker->generateFlamegraph(); expect($flamegraph)->toHaveCount(2); expect($flamegraph[0]['stack_trace'])->toBe('parent'); expect($flamegraph[1]['stack_trace'])->toBe('parent;child'); // Validate timeline $timeline = $tracker->generateTimeline(); expect($timeline)->toHaveCount(2); expect($timeline[0]['name'])->toBe('parent'); expect($timeline[1]['name'])->toBe('child'); expect($timeline[1]['parent_id'])->toBe($timeline[0]['operation_id']); }); ``` ## Troubleshooting ### Flamegraph zeigt keine Daten **Problem**: `generateFlamegraph()` gibt leeres Array zurück **Lösung**: ```php // Überprüfe ob Operationen completed sind $summary = $tracker->getSummary(); dd($summary); // Stelle sicher dass measure() verwendet wird $tracker->measure('operation', PerformanceCategory::CUSTOM, fn() => doWork()); // Nicht: startOperation() ohne endOperation() ``` ### Timeline Events haben 0ms Duration **Problem**: Events zeigen `duration_ms: 0` **Lösung**: ```php // usleep() verwenden für Tests (mindestens 1000 microseconds) usleep(1000); // 1ms // Oder echte Operationen durchführen $data = $repository->findAll(); // Reale Database Query ``` ### Memory Delta ist immer 0 **Problem**: `memory_delta_mb` zeigt 0.00 **Lösung**: ```php // Memory-intensive Operationen durchführen $tracker->measure('memory-test', PerformanceCategory::CUSTOM, function () { $data = array_fill(0, 10000, 'test'); // Allocate memory return $data; }); ``` ## Zusammenfassung Das Performance Profiling System bietet: - ✅ **Nested Performance Tracking** mit hierarchischen Messungen - ✅ **Flamegraph Export** im Brendan Gregg Format - ✅ **Timeline Visualisierung** mit Chrome DevTools Support - ✅ **Memory Tracking** für Leak Detection - ✅ **LiveComponent Integration** für Component-spezifisches Profiling - ✅ **Production Monitoring** mit Performance Budget Validation - ✅ **Automatische Bottleneck Detection** und Alerting **Verwendung**: 1. Operations mit `measure()` tracken 2. `generateFlamegraph()` für Flamegraph-Visualisierung 3. `generateTimeline()` für Timeline-Analyse 4. In Visualisierungs-Tools integrieren (Chrome DevTools, flamegraph.pl, D3.js) 5. Performance Budgets definieren und monitoren