diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index f77e716..8ef936d 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -19,7 +19,7 @@ import { Loader2, } from "lucide-react" import useSWR from "swr" -import { useState, useEffect } from "react" +import { useState } from "react" import { type HardwareData, type GPU, type PCIDevice, type StorageDevice, fetcher } from "../types/hardware" const getDeviceTypeColor = (type: string): string => { @@ -62,7 +62,6 @@ export default function Hardware() { const [selectedGPU, setSelectedGPU] = useState(null) const [realtimeGPUData, setRealtimeGPUData] = useState(null) const [detailsLoading, setDetailsLoading] = useState(false) - const [isPolling, setIsPolling] = useState(false) const [selectedPCIDevice, setSelectedPCIDevice] = useState(null) const [selectedDisk, setSelectedDisk] = useState(null) const [selectedNetwork, setSelectedNetwork] = useState(null) @@ -79,14 +78,12 @@ export default function Hardware() { setSelectedGPU(gpu) setDetailsLoading(true) setRealtimeGPUData(null) - setIsPolling(true) console.log("[v0] Modal opened, fetching realtime data...") if (!fullSlot) { console.log("[v0] No slot found, showing basic info only") setDetailsLoading(false) - setIsPolling(false) return } @@ -110,49 +107,12 @@ export default function Hardware() { } catch (error) { console.error("[v0] Error fetching GPU realtime data:", error) setRealtimeGPUData({ has_monitoring_tool: false }) - setIsPolling(false) } finally { setDetailsLoading(false) console.log("[v0] Finished loading GPU data") } } - useEffect(() => { - if (!isPolling || !selectedGPU || !realtimeGPUData?.has_monitoring_tool) { - return - } - - const pciDevice = findPCIDeviceForGPU(selectedGPU) - const fullSlot = pciDevice?.slot || selectedGPU.slot - - if (!fullSlot) { - return - } - - const pollInterval = setInterval(async () => { - try { - const response = await fetch(`http://localhost:8008/api/gpu/${fullSlot}/realtime`, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - signal: AbortSignal.timeout(5000), - }) - - if (response.ok) { - const data = await response.json() - setRealtimeGPUData(data) - } - } catch (error) { - console.error("[v0] Error polling GPU data:", error) - } - }, 2000) // Poll every 2 seconds - - return () => { - clearInterval(pollInterval) - } - }, [isPolling, selectedGPU, realtimeGPUData?.has_monitoring_tool]) - const findPCIDeviceForGPU = (gpu: GPU): PCIDevice | null => { if (!hardwareData?.pci_devices || !gpu.slot) return null @@ -436,7 +396,6 @@ export default function Hardware() { onOpenChange={() => { setSelectedGPU(null) setRealtimeGPUData(null) - setIsPolling(false) }} > @@ -445,11 +404,7 @@ export default function Hardware() { {selectedGPU.name} - {detailsLoading - ? "Loading real-time monitoring data..." - : isPolling && realtimeGPUData?.has_monitoring_tool - ? "Live monitoring (updates every 2s)" - : "GPU Information"} + {detailsLoading ? "Loading real-time monitoring data..." : "GPU Information"} @@ -499,13 +454,6 @@ export default function Hardware() { ) : realtimeGPUData?.has_monitoring_tool === true ? ( <> - {isPolling && ( -
-
- Live monitoring active -
- )} -

Real-Time Metrics @@ -521,37 +469,61 @@ export default function Hardware() {

- ) : null} - -
-
- GPU Utilization - {realtimeGPUData.utilization_gpu || "0.0%"} + ) : ( +
+ Temperature + N/A
- -
+ )} - {realtimeGPUData.clock_graphics !== undefined && realtimeGPUData.clock_graphics !== null && ( + {realtimeGPUData.utilization_gpu !== undefined && realtimeGPUData.utilization_gpu !== null ? ( +
+
+ GPU Utilization + + {typeof realtimeGPUData.utilization_gpu === "string" + ? realtimeGPUData.utilization_gpu + : `${realtimeGPUData.utilization_gpu}%`} + +
+ +
+ ) : ( +
+
+ GPU Utilization + 0.0% +
+ +
+ )} + + {realtimeGPUData.clock_graphics && (
Graphics Clock {realtimeGPUData.clock_graphics}
)} - - {realtimeGPUData.power_draw !== undefined && realtimeGPUData.power_draw !== null && ( + {realtimeGPUData.clock_memory && ( +
+ Memory Clock + {realtimeGPUData.clock_memory} +
+ )} + {realtimeGPUData.power_draw && realtimeGPUData.power_draw !== "N/A" && (
Power Draw {realtimeGPUData.power_draw}
)} - {realtimeGPUData.power_limit !== undefined && realtimeGPUData.power_limit !== null && ( + {realtimeGPUData.power_limit && (
Power Limit {realtimeGPUData.power_limit} @@ -560,7 +532,7 @@ export default function Hardware() {
- {/* Engine Utilization (Intel/AMD) - Always show even if 0% */} + {/* Engine Utilization (Intel/AMD) */} {(realtimeGPUData.engine_render !== undefined || realtimeGPUData.engine_blitter !== undefined || realtimeGPUData.engine_video !== undefined || @@ -574,20 +546,9 @@ export default function Hardware() {
Render/3D - - {typeof realtimeGPUData.engine_render === "string" - ? realtimeGPUData.engine_render - : `${realtimeGPUData.engine_render.toFixed(1)}%`} - + {realtimeGPUData.engine_render.toFixed(2)}%
- +
)} {realtimeGPUData.engine_blitter !== undefined && ( @@ -595,39 +556,19 @@ export default function Hardware() {
Blitter - {typeof realtimeGPUData.engine_blitter === "string" - ? realtimeGPUData.engine_blitter - : `${realtimeGPUData.engine_blitter.toFixed(1)}%`} + {realtimeGPUData.engine_blitter.toFixed(2)}%
- + )} {realtimeGPUData.engine_video !== undefined && (
Video - - {typeof realtimeGPUData.engine_video === "string" - ? realtimeGPUData.engine_video - : `${realtimeGPUData.engine_video.toFixed(1)}%`} - + {realtimeGPUData.engine_video.toFixed(2)}%
- +
)} {realtimeGPUData.engine_video_enhance !== undefined && ( @@ -635,19 +576,10 @@ export default function Hardware() {
VideoEnhance - {typeof realtimeGPUData.engine_video_enhance === "string" - ? realtimeGPUData.engine_video_enhance - : `${realtimeGPUData.engine_video_enhance.toFixed(1)}%`} + {realtimeGPUData.engine_video_enhance.toFixed(2)}%
- + )} diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 2f6e79a..479cbd1 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -1626,25 +1626,6 @@ def get_detailed_gpu_info(gpu): print(f"[v0] Current user: {os.getenv('USER', 'unknown')}", flush=True) print(f"[v0] Current working directory: {os.getcwd()}", flush=True) - try: - version_result = subprocess.run(['intel_gpu_top', '--version'], - capture_output=True, text=True, timeout=2) - if version_result.returncode == 0: - version_info = version_result.stdout.strip() - print(f"[v0] intel_gpu_top version: {version_info}", flush=True) - except Exception as e: - print(f"[v0] Could not get intel_gpu_top version: {e}", flush=True) - - # Check DRM device permissions - drm_devices = ['/dev/dri/card0', '/dev/dri/renderD128'] - for drm_dev in drm_devices: - if os.path.exists(drm_dev): - can_read = os.access(drm_dev, os.R_OK) - can_write = os.access(drm_dev, os.W_OK) - print(f"[v0] {drm_dev}: read={can_read}, write={can_write}", flush=True) - else: - print(f"[v0] {drm_dev}: does not exist", flush=True) - cmd = 'intel_gpu_top -J' print(f"[v0] Executing command: {cmd}", flush=True) @@ -1654,8 +1635,8 @@ def get_detailed_gpu_info(gpu): stderr=subprocess.PIPE, text=True, bufsize=1, - shell=True, - env=os.environ.copy() + shell=True, # Execute in shell context for proper DRM access + env=os.environ.copy() # Copy all environment variables ) print(f"[v0] Process started with PID: {process.pid}", flush=True) @@ -1665,7 +1646,7 @@ def get_detailed_gpu_info(gpu): print(f"[v0] Starting to read JSON objects...", flush=True) start_time = time.time() - timeout = 3 # Reduced timeout for faster has_monitoring_tool detection + timeout = 8 # Increased timeout from 5 to 8 seconds json_objects = [] buffer = "" brace_count = 0 @@ -1728,17 +1709,7 @@ def get_detailed_gpu_info(gpu): process.terminate() process.wait(timeout=1) - print(f"[v0] Checking if any JSON has clients...", flush=True) - - if not any('clients' in obj for obj in json_objects): - try: - stderr_output = process.stderr.read() - if stderr_output: - print(f"[v0] intel_gpu_top stderr: {stderr_output[:500]}", flush=True) - except: - pass - - print(f"[v0] Selecting best JSON object...", flush=True) + print(f"[v0] Collected {len(json_objects)} JSON objects total", flush=True) best_json = None for json_obj in json_objects: @@ -1751,133 +1722,115 @@ def get_detailed_gpu_info(gpu): best_json = json_objects[0] print(f"[v0] No JSON with clients found, using first JSON object as fallback", flush=True) - print(f"[v0] About to parse JSON object (best_json is None: {best_json is None})", flush=True) - if best_json: print(f"[v0] Parsing selected JSON object...", flush=True) + data_retrieved = False - try: - data_retrieved = False - - # Initialize engine totals - engine_totals = { - 'Render/3D': 0.0, - 'Blitter': 0.0, - 'Video': 0.0, - 'VideoEnhance': 0.0 - } - client_engine_totals = { - 'Render/3D': 0.0, - 'Blitter': 0.0, - 'Video': 0.0, - 'VideoEnhance': 0.0 - } - - print(f"[v0] Initialized engine totals", flush=True) - - # Parse clients section (processes using GPU) - if 'clients' in best_json: - print(f"[v0] Parsing clients section...", flush=True) - clients = best_json['clients'] - processes = [] + # Initialize engine totals + engine_totals = { + 'Render/3D': 0.0, + 'Blitter': 0.0, + 'Video': 0.0, + 'VideoEnhance': 0.0 + } + client_engine_totals = { + 'Render/3D': 0.0, + 'Blitter': 0.0, + 'Video': 0.0, + 'VideoEnhance': 0.0 + } + + # Parse clients section (processes using GPU) + if 'clients' in best_json: + print(f"[v0] Parsing clients section...", flush=True) + clients = best_json['clients'] + processes = [] - for client_id, client_data in clients.items(): - process_info = { - 'name': client_data.get('name', 'Unknown'), - 'pid': client_data.get('pid', 'Unknown'), - 'memory': { - 'total': client_data.get('memory', {}).get('system', {}).get('total', 0), - 'shared': client_data.get('memory', {}).get('system', {}).get('shared', 0), - 'resident': client_data.get('memory', {}).get('system', {}).get('resident', 0) - }, - 'engines': {} - } - - # Parse engine utilization for this process - engine_classes = client_data.get('engine-classes', {}) - for engine_name, engine_data in engine_classes.items(): - busy_value = float(engine_data.get('busy', 0)) - process_info['engines'][engine_name] = f"{busy_value:.1f}%" - - # Sum up engine utilization across all processes - if engine_name in client_engine_totals: - client_engine_totals[engine_name] += busy_value - - processes.append(process_info) - print(f"[v0] Added process: {process_info['name']} (PID: {process_info['pid']})", flush=True) + for client_id, client_data in clients.items(): + process_info = { + 'name': client_data.get('name', 'Unknown'), + 'pid': client_data.get('pid', 'Unknown'), + 'memory': { + 'total': client_data.get('memory', {}).get('system', {}).get('total', 0), + 'shared': client_data.get('memory', {}).get('system', {}).get('shared', 0), + 'resident': client_data.get('memory', {}).get('system', {}).get('resident', 0) + }, + 'engines': {} + } - detailed_info['processes'] = processes - print(f"[v0] Total processes found: {len(processes)}", flush=True) - else: - print(f"[v0] WARNING: No 'clients' section in selected JSON", flush=True) - - print(f"[v0] About to parse engines section...", flush=True) - - # Parse global engines section - if 'engines' in best_json: - print(f"[v0] Parsing engines section...", flush=True) - engines = best_json['engines'] - - for engine_name, engine_data in engines.items(): - # Remove the /0 suffix if present - clean_name = engine_name.replace('/0', '') + # Parse engine utilization for this process + engine_classes = client_data.get('engine-classes', {}) + for engine_name, engine_data in engine_classes.items(): busy_value = float(engine_data.get('busy', 0)) + process_info['engines'][engine_name] = f"{busy_value:.1f}%" - if clean_name in engine_totals: - engine_totals[clean_name] = busy_value + # Sum up engine utilization across all processes + if engine_name in client_engine_totals: + client_engine_totals[engine_name] += busy_value + + processes.append(process_info) + print(f"[v0] Added process: {process_info['name']} (PID: {process_info['pid']})", flush=True) - print(f"[v0] Calculating final engines...", flush=True) - - # Use client engine totals if available, otherwise use global engines - final_engines = client_engine_totals if any(v > 0 for v in client_engine_totals.values()) else engine_totals - - detailed_info['engine_render'] = f"{final_engines['Render/3D']:.1f}%" - detailed_info['engine_blitter'] = f"{final_engines['Blitter']:.1f}%" - detailed_info['engine_video'] = f"{final_engines['Video']:.1f}%" - detailed_info['engine_video_enhance'] = f"{final_engines['VideoEnhance']:.1f}%" - - # Calculate overall GPU utilization (max of all engines) - max_utilization = max(final_engines.values()) - detailed_info['utilization_gpu'] = f"{max_utilization:.1f}%" - - print(f"[v0] Parsing frequency...", flush=True) - - # Parse frequency - if 'frequency' in best_json: - freq_data = best_json['frequency'] - actual_freq = freq_data.get('actual', 0) - detailed_info['clock_graphics'] = f"{actual_freq} MHz" - data_retrieved = True - - print(f"[v0] Parsing power...", flush=True) - - # Parse power - if 'power' in best_json: - power_data = best_json['power'] - gpu_power = power_data.get('GPU', 0) - package_power = power_data.get('Package', 0) - detailed_info['power_draw'] = f"{gpu_power:.2f} W" - detailed_info['power_limit'] = f"{package_power:.2f} W" - data_retrieved = True - - print(f"[v0] Finalizing data (data_retrieved={data_retrieved})...", flush=True) - - if data_retrieved: - detailed_info['has_monitoring_tool'] = True - print(f"[v0] Intel GPU monitoring successful", flush=True) - print(f"[v0] - Utilization: {detailed_info['utilization_gpu']}", flush=True) - print(f"[v0] - Engines: R={detailed_info['engine_render']}, B={detailed_info['engine_blitter']}, V={detailed_info['engine_video']}, VE={detailed_info['engine_video_enhance']}", flush=True) - print(f"[v0] - Processes: {len(detailed_info['processes'])}", flush=True) - else: - print(f"[v0] WARNING: No data retrieved from intel_gpu_top", flush=True) + detailed_info['processes'] = processes + print(f"[v0] Total processes found: {len(processes)}", flush=True) + else: + print(f"[v0] WARNING: No 'clients' section in selected JSON", flush=True) - except Exception as parse_error: - print(f"[v0] ERROR parsing JSON data: {parse_error}", flush=True) - import traceback - traceback.print_exc() + # Parse global engines section + if 'engines' in best_json: + print(f"[v0] Parsing engines section...", flush=True) + engines = best_json['engines'] + for engine_name, engine_data in engines.items(): + # Remove the /0 suffix if present + clean_name = engine_name.replace('/0', '') + busy_value = float(engine_data.get('busy', 0)) + + if clean_name in engine_totals: + engine_totals[clean_name] = busy_value + + # Use client engine totals if available, otherwise use global engines + final_engines = client_engine_totals if any(v > 0 for v in client_engine_totals.values()) else engine_totals + + detailed_info['engine_render'] = f"{final_engines['Render/3D']:.1f}%" + detailed_info['engine_blitter'] = f"{final_engines['Blitter']:.1f}%" + detailed_info['engine_video'] = f"{final_engines['Video']:.1f}%" + detailed_info['engine_video_enhance'] = f"{final_engines['VideoEnhance']:.1f}%" + + # Calculate overall GPU utilization (max of all engines) + max_utilization = max(final_engines.values()) + detailed_info['utilization_gpu'] = f"{max_utilization:.1f}%" + + # Parse frequency + if 'frequency' in best_json: + freq_data = best_json['frequency'] + actual_freq = freq_data.get('actual', 0) + detailed_info['clock_graphics'] = f"{actual_freq} MHz" + data_retrieved = True + + # Parse power + if 'power' in best_json: + power_data = best_json['power'] + gpu_power = power_data.get('GPU', 0) + package_power = power_data.get('Package', 0) + detailed_info['power_draw'] = f"{gpu_power:.2f} W" + detailed_info['power_limit'] = f"{package_power:.2f} W" + data_retrieved = True + + if data_retrieved: + detailed_info['has_monitoring_tool'] = True + print(f"[v0] Intel GPU monitoring successful", flush=True) + print(f"[v0] - Utilization: {detailed_info['utilization_gpu']}", flush=True) + print(f"[v0] - Engines: R={detailed_info['engine_render']}, B={detailed_info['engine_blitter']}, V={detailed_info['engine_video']}, VE={detailed_info['engine_video_enhance']}", flush=True) + print(f"[v0] - Processes: {len(detailed_info['processes'])}", flush=True) + else: + print(f"[v0] WARNING: No data retrieved from intel_gpu_top", flush=True) else: print(f"[v0] WARNING: No valid JSON objects found", flush=True) + # Check stderr for errors + stderr_output = process.stderr.read() + if stderr_output: + print(f"[v0] intel_gpu_top stderr: {stderr_output}", flush=True) except Exception as e: print(f"[v0] Error running intel_gpu_top: {e}", flush=True) @@ -2332,6 +2285,9 @@ def get_hardware_info(): print(f"[v0] Error getting memory info: {e}") storage_info = get_storage_info() + for device in storage_info.get('disks', []): + hw_info = get_disk_hardware_info(device['name']) + device.update(hw_info) hardware_data['storage_devices'] = storage_info.get('disks', []) # Graphics Cards (from lspci - will be duplicated by new PCI device listing, but kept for now)