diff --git a/src/App.tsx b/src/App.tsx index 3634a6e..bb9e188 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from 'react'; +import { useState, useEffect, useMemo, Fragment } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer, @@ -45,15 +45,19 @@ interface ProcessHistoryPoint { } interface AggregatedProcess { + pid: number; name: string; avg_cpu: number; peak_cpu: number; avg_memory_mb: number; peak_memory_mb: number; + inclusive_avg_cpu: number; + inclusive_avg_memory_mb: number; instance_count: number; warnings: string[]; history: ProcessHistoryPoint[]; is_syspulse: boolean; + children: AggregatedProcess[]; } interface ProfilingReport { @@ -148,7 +152,7 @@ function App() { ); } - if (!stats) return
Initializing SysPulse...
; + if (!stats) return
Initializing SysPulse...
; const avgCpu = stats.cpu_usage.reduce((a, b) => a + b, 0) / stats.cpu_usage.length; const memoryPercent = (stats.used_memory / stats.total_memory) * 100; @@ -261,7 +265,7 @@ function App() { Processes
- Automatic aggregation is active. Child processes are merged for cleaner profiling. + Live system feed. Profiling session will provide a full hierarchical tree.
@@ -296,7 +300,7 @@ function App() { )} /> {proc.name} -
{proc.pid}
+
{proc.pid}
{proc.cpu_usage.toFixed(1)}%
{(proc.memory / 1024 / 1024).toFixed(0)} MB
@@ -319,13 +323,21 @@ function App() { ); } -type SortField = 'name' | 'avg_cpu' | 'peak_cpu' | 'avg_memory_mb' | 'peak_memory_mb' | 'instance_count'; +type SortField = 'name' | 'pid' | 'inclusive_avg_cpu' | 'peak_cpu' | 'inclusive_avg_memory_mb' | 'peak_memory_mb'; function ReportView({ report, onBack }: { report: ProfilingReport, onBack: () => void }) { - const [sortField, setSortField] = useState('avg_cpu'); + const [sortField, setSortField] = useState('inclusive_avg_cpu'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); const [selectedProcess, setSelectedProcess] = useState(null); const [hideProfiler, setHideProfiler] = useState(true); + const [expandedNodes, setExpandedNodes] = useState>(new Set()); + + const toggleExpand = (pid: number) => { + const newExpanded = new Set(expandedNodes); + if (newExpanded.has(pid)) newExpanded.delete(pid); + else newExpanded.add(pid); + setExpandedNodes(newExpanded); + }; const handleSort = (field: SortField) => { if (sortField === field) { @@ -363,6 +375,73 @@ function ReportView({ report, onBack }: { report: ProfilingReport, onBack: () => } }; + const renderTreeRows = (nodes: AggregatedProcess[], depth = 0): React.ReactNode => { + return nodes.map((proc) => { + const isExpanded = expandedNodes.has(proc.pid); + const hasChildren = proc.children && proc.children.length > 0; + + return ( + +
{ + e.stopPropagation(); + setSelectedProcess(proc); + }} + > +
+ {hasChildren ? ( + + ) : ( +
+ )} +
+ {proc.name} +
+ +
{proc.pid}
+ +
+ {proc.inclusive_avg_cpu.toFixed(1)}% + {proc.children.length > 0 && ({proc.avg_cpu.toFixed(1)}% self)} +
+ +
{proc.peak_cpu.toFixed(1)}%
+ +
+ {proc.inclusive_avg_memory_mb.toFixed(0)}MB + {proc.children.length > 0 && ({proc.avg_memory_mb.toFixed(0)}MB self)} +
+ +
{proc.peak_memory_mb.toFixed(0)}MB
+ +
+ {proc.warnings.length > 0 ? proc.warnings.map((w, idx) => ( + + {w} + + )) : ( + proc.children.length > 0 ? {proc.children.length} units : null + )} +
+
+ {hasChildren && isExpanded && renderTreeRows(proc.children, depth + 1)} + + ); + }); + }; + return (
@@ -410,7 +489,7 @@ function ReportView({ report, onBack }: { report: ProfilingReport, onBack: () =>
{new Date(report.end_time).toLocaleTimeString()}
-
Unique Processes
+
Root Processes
{report.aggregated_processes.length}
@@ -453,50 +532,35 @@ function ReportView({ report, onBack }: { report: ProfilingReport, onBack: () =>

- Analysis Matrix + Hierarchical Matrix

- Select Process to Inspect +
+
+
Inclusive Sum +
+ Toggle Nodes to Expand +
handleSort('name')}> Process {sortField === 'name' && (sortOrder === 'asc' ? '↑' : '↓')}
-
handleSort('instance_count')}>Units
-
handleSort('avg_cpu')}>Avg CPU
-
handleSort('peak_cpu')}>Peak CPU
-
handleSort('avg_memory_mb')}>Avg Mem
-
handleSort('peak_memory_mb')}>Peak Mem
+
handleSort('pid')}>PID
+
handleSort('inclusive_avg_cpu')}>Total CPU
+
handleSort('peak_cpu')}>Peak
+
handleSort('inclusive_avg_memory_mb')}>Total Mem
+
handleSort('peak_memory_mb')}>Peak
Insights
- {sortedProcesses.map((proc, i) => ( -
setSelectedProcess(proc)} - className="grid grid-cols-[1fr_80px_100px_100px_100px_100px_200px] gap-4 px-4 py-4 border-b border-surface1/20 hover:bg-surface1/20 cursor-pointer transition-all rounded-xl group" - > -
-
- {proc.name} -
-
{proc.instance_count}
-
{proc.avg_cpu.toFixed(1)}%
-
{proc.peak_cpu.toFixed(1)}%
-
{proc.avg_memory_mb.toFixed(0)}MB
-
{proc.peak_memory_mb.toFixed(0)}MB
-
- {proc.warnings.length > 0 ? proc.warnings.map((w, idx) => ( - - {w} - - )) : ( - Healthy - )} -
-
- ))} + {renderTreeRows(sortedProcesses)} +
+ +
+ + Memory is Resident Set Size (RSS). Summed totals may exceed physical RAM due to shared segments.
@@ -506,7 +570,7 @@ function ReportView({ report, onBack }: { report: ProfilingReport, onBack: () =>
-
Process Inspector
+
Process Inspector (PID: {selectedProcess.pid})

{selectedProcess.name}