mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-27 06:34:06 +00:00
Bugfix/inaccurate process metrics (#6257)
This commit is contained in:
committed by
GitHub
parent
b3ffc904ad
commit
bc4062b950
@@ -115,6 +115,237 @@ const StyledWrapper = styled.div`
|
||||
color: ${(props) => props.theme.console.buttonColor};
|
||||
}
|
||||
}
|
||||
|
||||
.performance-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid ${(props) => props.theme.console.border};
|
||||
padding: 12px 16px;
|
||||
background: ${(props) => props.theme.console.headerBg};
|
||||
}
|
||||
|
||||
.performance-selector-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.performance-selector-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: ${(props) => props.theme.console.titleColor};
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.performance-selector {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.performance-select {
|
||||
appearance: none;
|
||||
background: ${(props) => props.theme.console.bg};
|
||||
border: 1px solid ${(props) => props.theme.console.border};
|
||||
border-radius: 4px;
|
||||
padding: 6px 32px 6px 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: ${(props) => props.theme.console.titleColor};
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
transition: all 0.2s ease;
|
||||
min-width: 250px;
|
||||
max-width: 400px;
|
||||
|
||||
&:hover {
|
||||
border-color: ${(props) => props.theme.colors.primary};
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: ${(props) => props.theme.colors.primary};
|
||||
box-shadow: 0 0 0 2px ${(props) => props.theme.colors.primary}33;
|
||||
}
|
||||
|
||||
option {
|
||||
background: ${(props) => props.theme.console.bg};
|
||||
color: ${(props) => props.theme.console.titleColor};
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.performance-select-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
pointer-events: none;
|
||||
color: ${(props) => props.theme.console.buttonColor};
|
||||
}
|
||||
|
||||
.processes-table-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
|
||||
h2 {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.console.titleColor};
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.no-processes {
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
color: ${(props) => props.theme.console.buttonColor};
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.processes-table-wrapper {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.processes-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: ${(props) => props.theme.console.headerBg};
|
||||
border: 1px solid ${(props) => props.theme.console.border};
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
thead {
|
||||
background: ${(props) => props.theme.console.bg};
|
||||
border-bottom: 1px solid ${(props) => props.theme.console.border};
|
||||
|
||||
th {
|
||||
padding: 10px 12px;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: ${(props) => props.theme.console.titleColor};
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
border-bottom: 1px solid ${(props) => props.theme.console.border};
|
||||
transition: background 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.console.bg};
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
color: ${(props) => props.theme.console.textColor};
|
||||
|
||||
&:first-child {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pid-cell {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 12px;
|
||||
color: ${(props) => props.theme.console.buttonColor};
|
||||
}
|
||||
|
||||
.type-cell {
|
||||
.process-type-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
text-transform: lowercase;
|
||||
background: ${(props) => props.theme.console.border};
|
||||
color: ${(props) => props.theme.console.buttonColor};
|
||||
|
||||
&.Browser {
|
||||
background: rgba(59, 130, 246, 0.2);
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
&.Renderer {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
&.Utility {
|
||||
background: rgba(139, 92, 246, 0.2);
|
||||
color: #8b5cf6;
|
||||
}
|
||||
|
||||
&.Zygote {
|
||||
background: rgba(245, 158, 11, 0.2);
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
&.Sandbox {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title-cell {
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cpu-cell {
|
||||
font-weight: 500;
|
||||
|
||||
.high-cpu {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.medium-cpu {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.low-cpu {
|
||||
color: ${(props) => props.theme.console.buttonColor};
|
||||
}
|
||||
}
|
||||
|
||||
.memory-cell {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.created-cell {
|
||||
font-size: 12px;
|
||||
color: ${(props) => props.theme.console.buttonColor};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import {
|
||||
@@ -6,11 +6,22 @@ import {
|
||||
IconDatabase,
|
||||
IconClock,
|
||||
IconServer,
|
||||
IconChartLine
|
||||
IconChevronDown
|
||||
} from '@tabler/icons';
|
||||
|
||||
const getProcessOptions = (processes) => {
|
||||
return [
|
||||
{ value: 'cumulative', label: 'Cumulative (All Processes)' },
|
||||
...(processes ?? []).map((process) => ({
|
||||
value: String(process.pid),
|
||||
label: `PID ${process.pid}${process.title ? ` - ${process.title}` : ''}${process.type ? ` (${process.type})` : ''}`
|
||||
}))
|
||||
];
|
||||
};
|
||||
|
||||
const Performance = () => {
|
||||
const { systemResources } = useSelector((state) => state.performance);
|
||||
const [selectedPid, setSelectedPid] = useState('cumulative');
|
||||
|
||||
useEffect(() => {
|
||||
const { ipcRenderer } = window;
|
||||
@@ -82,47 +93,140 @@ const Performance = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
// Get process options for dropdown
|
||||
const processOptions = useMemo(() => getProcessOptions(systemResources.processes), [systemResources.processes]);
|
||||
|
||||
// Get selected process data
|
||||
const selectedProcess = useMemo(() => {
|
||||
if (selectedPid === 'cumulative') {
|
||||
return null; // Show cumulative view
|
||||
}
|
||||
const processes = systemResources.processes || [];
|
||||
return processes.find((p) => String(p.pid) === selectedPid) || null;
|
||||
}, [selectedPid, systemResources.processes]);
|
||||
|
||||
// Reset to cumulative if selected PID no longer exists
|
||||
useEffect(() => {
|
||||
if (selectedPid !== 'cumulative' && !selectedProcess) {
|
||||
setSelectedPid('cumulative');
|
||||
}
|
||||
}, [selectedPid, selectedProcess]);
|
||||
|
||||
const renderCumulativeView = () => (
|
||||
<div className="system-resources">
|
||||
<h2>System Resources</h2>
|
||||
<div className="resource-cards">
|
||||
<SystemResourceCard
|
||||
icon={IconCpu}
|
||||
title="CPU Usage"
|
||||
value={`${systemResources.cpu.toFixed(1)}%`}
|
||||
subtitle="Total CPU usage"
|
||||
color={systemResources.cpu > 80 ? 'danger' : systemResources.cpu > 60 ? 'warning' : 'success'}
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconDatabase}
|
||||
title="Memory Usage"
|
||||
value={formatBytes(systemResources.memory)}
|
||||
subtitle="Total memory usage"
|
||||
color={systemResources.memory > (500 * 1024 * 1024) ? 'danger' : 'default'}
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconClock}
|
||||
title="Uptime"
|
||||
value={formatUptime(systemResources.uptime)}
|
||||
subtitle="Process runtime"
|
||||
color="info"
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconServer}
|
||||
title="Process ID"
|
||||
value={systemResources.pid || 'N/A'}
|
||||
subtitle="Main process PID"
|
||||
color="default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderProcessView = (process) => {
|
||||
if (!process) return null;
|
||||
|
||||
// Calculate uptime for individual process
|
||||
const processUptime = process.creationTime
|
||||
? (new Date() - new Date(process.creationTime)) / 1000
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<div className="system-resources">
|
||||
<h2>System Resources</h2>
|
||||
<div className="resource-cards">
|
||||
<SystemResourceCard
|
||||
icon={IconCpu}
|
||||
title="CPU Usage"
|
||||
value={`${process.cpu.toFixed(1)}%`}
|
||||
subtitle="Current CPU usage"
|
||||
color={process.cpu > 80 ? 'danger' : process.cpu > 60 ? 'warning' : 'success'}
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconDatabase}
|
||||
title="Memory Usage"
|
||||
value={formatBytes(process.memory)}
|
||||
subtitle="Current memory usage"
|
||||
color={process.memory > (500 * 1024 * 1024) ? 'danger' : 'default'}
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconClock}
|
||||
title="Uptime"
|
||||
value={formatUptime(processUptime)}
|
||||
subtitle="Process runtime"
|
||||
color="info"
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconServer}
|
||||
title="Process ID"
|
||||
value={process.pid}
|
||||
subtitle="Process PID"
|
||||
color="default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="tab-content">
|
||||
<div className="tab-content-area">
|
||||
<div className="system-resources">
|
||||
<h2>System Resources</h2>
|
||||
<div className="resource-cards">
|
||||
<SystemResourceCard
|
||||
icon={IconCpu}
|
||||
title="CPU Usage"
|
||||
value={`${systemResources.cpu.toFixed(1)}%`}
|
||||
subtitle="Current process"
|
||||
color={systemResources.cpu > 80 ? 'danger' : systemResources.cpu > 60 ? 'warning' : 'success'}
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconDatabase}
|
||||
title="Memory Usage"
|
||||
value={formatBytes(systemResources.memory)}
|
||||
subtitle="Current process"
|
||||
color={systemResources.memory > 500 * 1024 * 1024 ? 'danger' : 'default'}
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconClock}
|
||||
title="Uptime"
|
||||
value={formatUptime(systemResources.uptime)}
|
||||
subtitle="Process runtime"
|
||||
color="info"
|
||||
/>
|
||||
|
||||
<SystemResourceCard
|
||||
icon={IconServer}
|
||||
title="Process ID"
|
||||
value={systemResources.pid || 'N/A'}
|
||||
subtitle="Current PID"
|
||||
color="default"
|
||||
/>
|
||||
<div className="performance-header">
|
||||
<div className="performance-selector-wrapper">
|
||||
<label htmlFor="process-selector" className="performance-selector-label">
|
||||
View:
|
||||
</label>
|
||||
<div className="performance-selector">
|
||||
<select
|
||||
id="process-selector"
|
||||
value={selectedPid}
|
||||
onChange={(e) => setSelectedPid(e.target.value)}
|
||||
className="performance-select"
|
||||
>
|
||||
{processOptions.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<IconChevronDown size={16} className="performance-select-icon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tab-content-area">
|
||||
{selectedPid === 'cumulative' ? renderCumulativeView() : renderProcessView(selectedProcess)}
|
||||
</div>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -65,17 +65,25 @@ class SystemMonitor {
|
||||
|
||||
for (const metric of metrics) {
|
||||
totalCPU += metric.cpu.percentCPUUsage;
|
||||
totalMemory += metric.memory.workingSetSize;
|
||||
totalMemory += metric.memory.workingSetSize * 1024;
|
||||
}
|
||||
|
||||
const uptime = (currentTime - this.startTime) / 1000;
|
||||
|
||||
var processes = metrics.map((metric) => ({
|
||||
pid: metric.pid,
|
||||
title: metric.title,
|
||||
memory: metric.memory.workingSetSize * 1024,
|
||||
cpu: metric.cpu.percentCPUUsage,
|
||||
type: metric.type || 'unknown',
|
||||
creationTime: metric.creationTime
|
||||
}));
|
||||
const systemResources = {
|
||||
cpu: totalCPU,
|
||||
memory: totalMemory,
|
||||
pid: process.pid,
|
||||
uptime: uptime,
|
||||
timestamp: currentTime.toISOString()
|
||||
timestamp: currentTime.toISOString(),
|
||||
processes: processes
|
||||
};
|
||||
|
||||
if (win && !win.isDestroyed()) {
|
||||
@@ -95,7 +103,8 @@ class SystemMonitor {
|
||||
memory: memory.rss,
|
||||
pid: process.pid,
|
||||
uptime: uptime,
|
||||
timestamp: currentTime.toISOString()
|
||||
timestamp: currentTime.toISOString(),
|
||||
processes: []
|
||||
};
|
||||
|
||||
if (win && !win.isDestroyed()) {
|
||||
|
||||
Reference in New Issue
Block a user