diff --git a/kernel_ai/services/execution.py b/kernel_ai/services/execution.py new file mode 100644 index 0000000..c7aca77 --- /dev/null +++ b/kernel_ai/services/execution.py @@ -0,0 +1,385 @@ +"""Execution context and kernel DNA services.""" + +from __future__ import annotations + +import os +import platform +import time +from datetime import datetime + +import psutil + + +def get_execution_context_data(syscall_names, map_interrupt_to_subsystem_fn, exec_context_prev): + """Collect execution context payload used by Ring-1 visualization.""" + if platform.system() != "Linux": + return { + "mode": "kernel", + "cpu_state": "running", + "syscall_active": False, + "syscall_name": None, + "interrupts": [], + "preempted": False, + "preempted_pid": None, + } + + mode = "user" + syscall_active = False + syscall_name = None + active_pid = None + active_syscalls = [] + + try: + proc_dirs = [d for d in os.listdir("/proc") if d.isdigit()] + sampled_procs = proc_dirs[:100] + + syscall_count = 0 + for pid in sampled_procs: + try: + syscall_path = f"/proc/{pid}/syscall" + if os.path.exists(syscall_path): + with open(syscall_path, "r", encoding="utf-8", errors="ignore") as f: + line = f.read().strip() + if line and line != "-1": + parts = line.split() + if parts: + syscall_num = int(parts[0]) + if syscall_num > 0: + syscall_count += 1 + current_syscall_name = syscall_names.get(syscall_num, f"syscall_{syscall_num}") + active_syscalls.append({"pid": int(pid), "syscall_name": current_syscall_name}) + if not syscall_active: + syscall_active = True + syscall_name = current_syscall_name + active_pid = int(pid) + mode = "kernel" + except (ValueError, IOError, PermissionError): + continue + + if syscall_count > 0: + mode = "kernel" + except PermissionError: + pass + + cpu_state = "running" + try: + with open("/proc/stat", "r", encoding="utf-8", errors="ignore") as f: + cpu_line = f.readline() + if cpu_line.startswith("cpu "): + parts = cpu_line.split() + if len(parts) >= 5: + idle_time = int(parts[4]) + total_time = sum(int(p) for p in parts[1:11] if p.isdigit()) + if total_time > 0: + idle_percent = (idle_time / total_time) * 100 + if idle_percent > 90: + cpu_state = "idle" + elif idle_percent > 50: + cpu_state = "sleeping" + except (IOError, ValueError, IndexError): + pass + + interrupts = [] + all_process_pids = [] + try: + proc_dirs = [d for d in os.listdir("/proc") if d.isdigit()] + all_process_pids = [int(pid) for pid in proc_dirs[:200] if pid.isdigit()] + except PermissionError: + pass + + try: + with open("/proc/interrupts", "r", encoding="utf-8", errors="ignore") as f: + lines = f.readlines() + for line in lines[1:]: + if line.strip(): + parts = line.split() + if len(parts) > 1: + for i in range(1, min(len(parts), 5)): + try: + count = int(parts[i]) + if count > 0: + irq_num = parts[0].rstrip(":") + associated_pid = None + if all_process_pids: + hash_value = (i - 1) * 100 + int(irq_num) if irq_num.isdigit() else (i - 1) * 100 + process_index = hash_value % len(all_process_pids) + associated_pid = all_process_pids[process_index] + + interrupts.append( + { + "cpu": i - 1, + "irq": irq_num, + "count": count, + "pid": associated_pid, + "timestamp": datetime.now().isoformat(), + } + ) + break + except (ValueError, IndexError): + continue + except (IOError, PermissionError): + pass + + now_ts = time.time() + prev_ts = exec_context_prev.get("timestamp") + dt = (now_ts - prev_ts) if prev_ts else None + if dt is not None and dt <= 0: + dt = None + + irq_totals_now = {} + irq_rows = [] + try: + with open("/proc/interrupts", "r", encoding="utf-8", errors="ignore") as f: + lines = f.readlines() + for raw in lines[1:]: + if ":" not in raw: + continue + left, right = raw.split(":", 1) + irq_name = left.strip() + tokens = right.split() + if not tokens: + continue + + counts = [] + idx = 0 + while idx < len(tokens) and tokens[idx].isdigit(): + counts.append(int(tokens[idx])) + idx += 1 + if not counts: + continue + total = sum(counts) + desc = " ".join(tokens[idx:]).strip() or irq_name + key = f"{irq_name}:{desc}" + irq_totals_now[key] = total + + prev_total = exec_context_prev["irq_totals"].get(key) + per_sec = 0.0 + if dt and prev_total is not None: + per_sec = max(0.0, (total - prev_total) / dt) + + top_cpu = int(max(range(len(counts)), key=lambda i: counts[i])) if counts else None + irq_rows.append( + { + "irq": irq_name, + "label": desc, + "total": int(total), + "per_sec": round(per_sec, 2), + "top_cpu": top_cpu, + "subsystem": map_interrupt_to_subsystem_fn(desc), + } + ) + except (IOError, PermissionError): + pass + + softirq_totals_now = {} + softirq_rows = [] + try: + with open("/proc/softirqs", "r", encoding="utf-8", errors="ignore") as f: + lines = f.readlines() + for raw in lines[1:]: + if ":" not in raw: + continue + left, right = raw.split(":", 1) + name = left.strip() + counts = [] + for tok in right.split(): + if tok.isdigit(): + counts.append(int(tok)) + if not counts: + continue + total = sum(counts) + softirq_totals_now[name] = total + prev_total = exec_context_prev["softirq_totals"].get(name) + per_sec = 0.0 + if dt and prev_total is not None: + per_sec = max(0.0, (total - prev_total) / dt) + softirq_rows.append({"name": name, "total": int(total), "per_sec": round(per_sec, 2)}) + except (IOError, PermissionError): + pass + + irq_rows.sort(key=lambda row: (row["per_sec"], row["total"]), reverse=True) + softirq_rows.sort(key=lambda row: (row["per_sec"], row["total"]), reverse=True) + hard_top = irq_rows[:5] + soft_top = softirq_rows[:4] + + hard_total_rate = sum(row["per_sec"] for row in irq_rows) + soft_total_rate = sum(row["per_sec"] for row in softirq_rows) + net_softirq_rate = 0.0 + block_softirq_rate = 0.0 + timer_softirq_rate = 0.0 + for row in softirq_rows: + nm = row["name"].upper() + if nm in ("NET_RX", "NET_TX"): + net_softirq_rate += row["per_sec"] + elif nm == "BLOCK": + block_softirq_rate += row["per_sec"] + elif nm == "TIMER": + timer_softirq_rate += row["per_sec"] + + exec_context_prev["timestamp"] = now_ts + exec_context_prev["irq_totals"] = irq_totals_now + exec_context_prev["softirq_totals"] = softirq_totals_now + + preempted = False + preempted_pid = None + try: + proc_dirs = [d for d in os.listdir("/proc") if d.isdigit()] + for pid in proc_dirs[:20]: + try: + stat_path = f"/proc/{pid}/stat" + if os.path.exists(stat_path): + with open(stat_path, "r", encoding="utf-8", errors="ignore") as f: + stat_data = f.read().split() + if len(stat_data) > 2: + state = stat_data[2] + if state == "R" and active_pid and int(pid) != active_pid: + preempted = True + preempted_pid = int(pid) + break + except (ValueError, IOError, PermissionError, IndexError): + continue + except PermissionError: + pass + + return { + "mode": mode, + "cpu_state": cpu_state, + "syscall_active": syscall_active, + "syscall_name": syscall_name, + "active_pid": active_pid, + "active_syscalls": active_syscalls, + "interrupts": interrupts[:10], + "irq_stack": { + "hard": hard_top, + "soft": soft_top, + "summary": { + "hard_total_per_sec": round(hard_total_rate, 2), + "soft_total_per_sec": round(soft_total_rate, 2), + "net_softirq_per_sec": round(net_softirq_rate, 2), + "block_softirq_per_sec": round(block_softirq_rate, 2), + "timer_softirq_per_sec": round(timer_softirq_rate, 2), + }, + }, + "preempted": preempted, + "preempted_pid": preempted_pid, + "cpu_count": psutil.cpu_count(), + "timestamp": datetime.now().isoformat(), + } + + +def get_kernel_dna_data(get_real_system_calls_fn, map_syscall_to_subsystem_fn, map_interrupt_to_subsystem_fn, softirq_nucleotides_fn): + """Collect Kernel DNA data for visualization.""" + dna_data = {"nucleotides": [], "genes": [], "mutations": [], "timestamp": datetime.now().isoformat()} + + try: + syscalls = get_real_system_calls_fn() + for syscall in syscalls[:20]: + dna_data["nucleotides"].append( + { + "type": "syscall", + "code": "A", + "name": syscall["name"], + "count": syscall.get("count", 0), + "subsystem": syscall.get("subsystem") or map_syscall_to_subsystem_fn(syscall["name"]), + "timestamp": datetime.now().isoformat(), + } + ) + except Exception: + pass + + try: + with open("/proc/interrupts", "r", encoding="utf-8", errors="ignore") as f: + interrupt_lines = f.readlines() + for line in interrupt_lines[1:11]: + parts = line.strip().split() + if len(parts) > 1: + interrupt_name = parts[0].rstrip(":") + total_count = sum(int(x) for x in parts[1:] if x.isdigit()) + if total_count > 0: + dna_data["nucleotides"].append( + { + "type": "interrupt", + "code": "T", + "name": interrupt_name, + "count": total_count, + "subsystem": map_interrupt_to_subsystem_fn(interrupt_name), + "timestamp": datetime.now().isoformat(), + } + ) + except (IOError, ValueError, PermissionError): + dna_data["nucleotides"].extend(softirq_nucleotides_fn()) + + try: + with open("/proc/stat", "r", encoding="utf-8", errors="ignore") as f: + for line in f: + if line.startswith("ctxt "): + ctxt_count = int(line.split()[1]) + dna_data["nucleotides"].append( + { + "type": "context_switch", + "code": "C", + "name": "context_switch", + "count": ctxt_count, + "subsystem": "sched", + "timestamp": datetime.now().isoformat(), + } + ) + break + except (IOError, ValueError, PermissionError): + pass + + try: + with open("/proc/locks", "r", encoding="utf-8", errors="ignore") as f: + lock_lines = f.readlines() + lock_count = len(lock_lines) + if lock_count > 0: + dna_data["nucleotides"].append( + { + "type": "lock", + "code": "G", + "name": "mutex/lock", + "count": lock_count, + "subsystem": "kernel", + "timestamp": datetime.now().isoformat(), + } + ) + except (IOError, PermissionError): + try: + process_count = len(psutil.pids()) + estimated_locks = process_count // 10 + dna_data["nucleotides"].append( + { + "type": "lock", + "code": "G", + "name": "mutex/lock", + "count": estimated_locks, + "subsystem": "kernel", + "timestamp": datetime.now().isoformat(), + } + ) + except Exception: + pass + + dna_data["genes"] = [ + {"name": "sched", "start": 0, "end": 0.2, "color": "#58b6d8"}, + {"name": "net", "start": 0.2, "end": 0.4, "color": "#4a9eff"}, + {"name": "fs", "start": 0.4, "end": 0.6, "color": "#6bcf7f"}, + {"name": "mm", "start": 0.6, "end": 0.8, "color": "#ffa94d"}, + {"name": "drivers", "start": 0.8, "end": 1.0, "color": "#ff6b9d"}, + ] + + mutations = [] + syscall_count = sum(1 for n in dna_data["nucleotides"] if n["type"] == "syscall") + if syscall_count > 15: + mutations.append({"type": "syscall_flood", "severity": "high", "message": f"Syscall flood detected: {syscall_count} active syscalls", "position": 0.3}) + + ctxt_switches = [n for n in dna_data["nucleotides"] if n["type"] == "context_switch"] + if ctxt_switches and ctxt_switches[0]["count"] > 1000000: + mutations.append({"type": "abnormal_context_switch", "severity": "medium", "message": "Abnormal context switching rate detected", "position": 0.5}) + + locks = [n for n in dna_data["nucleotides"] if n["type"] == "lock"] + if locks and locks[0]["count"] > 100: + mutations.append({"type": "lock_contention", "severity": "medium", "message": f'High lock contention: {locks[0]["count"]} active locks', "position": 0.7}) + + dna_data["mutations"] = mutations + return dna_data diff --git a/kernel_ai/webapp.py b/kernel_ai/webapp.py index a88fa1f..a6f05ce 100644 --- a/kernel_ai/webapp.py +++ b/kernel_ai/webapp.py @@ -22,6 +22,7 @@ from kernel_ai.http.register import register_http_routes from kernel_ai.prometheus_setup import init_prometheus from kernel_ai.collectors import proc_fs as _proc_fs +from kernel_ai.services import execution as _execution_service from kernel_ai.services import network as _network_service from kernel_ai.services import process_inspect as _process_inspect_service from kernel_ai.services import processes as _processes_service @@ -1429,441 +1430,25 @@ def get_proc_timeline(): return jsonify({'error': str(e)}), 500 def get_execution_context(): - """Get execution context data for Ring-1 visualization""" + """Get execution context data for Ring-1 visualization.""" try: - if platform.system() != 'Linux': - return jsonify({ - 'mode': 'kernel', - 'cpu_state': 'running', - 'syscall_active': False, - 'syscall_name': None, - 'interrupts': [], - 'preempted': False, - 'preempted_pid': None - }) - - # Determine mode (user/kernel) by checking active processes - mode = 'user' # Default - syscall_active = False - syscall_name = None - active_pid = None - active_syscalls = [] # List of processes with active syscalls: [{pid, syscall_name}] - - # Check for active syscalls - try: - proc_dirs = [d for d in os.listdir('/proc') if d.isdigit()] - sampled_procs = proc_dirs[:100] # Sample more processes - - syscall_count = 0 - for pid in sampled_procs: - try: - syscall_path = f'/proc/{pid}/syscall' - if os.path.exists(syscall_path): - with open(syscall_path, 'r') as f: - line = f.read().strip() - if line and line != '-1': - parts = line.split() - if parts: - syscall_num = int(parts[0]) - if syscall_num > 0: - syscall_count += 1 - current_syscall_name = SYSCALL_NAMES.get(syscall_num, f'syscall_{syscall_num}') - - # Add to list of active syscalls - active_syscalls.append({ - 'pid': int(pid), - 'syscall_name': current_syscall_name - }) - - if not syscall_active: # Get first active syscall for main display - syscall_active = True - syscall_name = current_syscall_name - active_pid = int(pid) - mode = 'kernel' # Syscall means kernel mode - except (ValueError, IOError, PermissionError): - continue - - # If we found syscalls, we're in kernel mode - if syscall_count > 0: - mode = 'kernel' - except PermissionError: - pass - - # Get CPU state from /proc/stat - cpu_state = 'running' - try: - with open('/proc/stat', 'r') as f: - cpu_line = f.readline() - if cpu_line.startswith('cpu '): - parts = cpu_line.split() - if len(parts) >= 5: - idle_time = int(parts[4]) - total_time = sum(int(p) for p in parts[1:11] if p.isdigit()) - if total_time > 0: - idle_percent = (idle_time / total_time) * 100 - if idle_percent > 90: - cpu_state = 'idle' - elif idle_percent > 50: - cpu_state = 'sleeping' - except (IOError, ValueError, IndexError): - pass - - # Get recent interrupts and associate with processes - interrupts = [] - # Get list of ALL processes (not just active syscalls) for better distribution - all_process_pids = [] - try: - proc_dirs = [d for d in os.listdir('/proc') if d.isdigit()] - # Get all process PIDs (limit to reasonable number) - all_process_pids = [int(pid) for pid in proc_dirs[:200] if pid.isdigit()] - except PermissionError: - pass - - # Track previous interrupt counts to detect new interrupts - previous_interrupt_counts = {} - try: - # Try to read previous counts from a simple cache (in-memory) - # For now, we'll just report all interrupts and let frontend handle distribution - with open('/proc/interrupts', 'r') as f: - lines = f.readlines() - # Parse interrupt counts - for line in lines[1:]: # Skip header - if line.strip(): - parts = line.split() - if len(parts) > 1: - # Check if any CPU has non-zero interrupts - for i in range(1, min(len(parts), 5)): # Check first 4 CPUs - try: - count = int(parts[i]) - # Report interrupts more frequently (every 10 instead of 100) - if count > 0: - # Extract IRQ number - irq_num = parts[0].rstrip(':') - - # Associate with a process - use CPU and IRQ to select process - # This ensures consistent mapping: same CPU+IRQ = same process - associated_pid = None - if all_process_pids: - # Use CPU and IRQ to create a consistent hash for process selection - hash_value = (i - 1) * 100 + int(irq_num) if irq_num.isdigit() else (i - 1) * 100 - process_index = hash_value % len(all_process_pids) - associated_pid = all_process_pids[process_index] - - interrupts.append({ - 'cpu': i - 1, - 'irq': irq_num, - 'count': count, - 'pid': associated_pid, # Always associate with a process - 'timestamp': datetime.now().isoformat() - }) - break # Only one per IRQ line - except (ValueError, IndexError): - continue - except (IOError, PermissionError): - pass - - # Build IRQ/SoftIRQ stack data with rates for a compact "IRQ stack" UI panel. - now_ts = time.time() - prev_ts = EXEC_CONTEXT_PREV.get("timestamp") - dt = (now_ts - prev_ts) if prev_ts else None - if dt is not None and dt <= 0: - dt = None - - irq_totals_now = {} - irq_rows = [] - try: - with open('/proc/interrupts', 'r') as f: - lines = f.readlines() - for raw in lines[1:]: - if ":" not in raw: - continue - left, right = raw.split(":", 1) - irq_name = left.strip() - tokens = right.split() - if not tokens: - continue - - counts = [] - idx = 0 - while idx < len(tokens) and tokens[idx].isdigit(): - counts.append(int(tokens[idx])) - idx += 1 - if not counts: - continue - total = sum(counts) - desc = " ".join(tokens[idx:]).strip() or irq_name - key = f"{irq_name}:{desc}" - irq_totals_now[key] = total - - prev_total = EXEC_CONTEXT_PREV["irq_totals"].get(key) - per_sec = 0.0 - if dt and prev_total is not None: - per_sec = max(0.0, (total - prev_total) / dt) - - top_cpu = None - if counts: - top_cpu = int(max(range(len(counts)), key=lambda i: counts[i])) - - irq_rows.append({ - "irq": irq_name, - "label": desc, - "total": int(total), - "per_sec": round(per_sec, 2), - "top_cpu": top_cpu, - "subsystem": map_interrupt_to_subsystem(desc) - }) - except (IOError, PermissionError): - pass - - softirq_totals_now = {} - softirq_rows = [] - try: - with open('/proc/softirqs', 'r') as f: - lines = f.readlines() - for raw in lines[1:]: - if ":" not in raw: - continue - left, right = raw.split(":", 1) - name = left.strip() - counts = [] - for tok in right.split(): - if tok.isdigit(): - counts.append(int(tok)) - if not counts: - continue - total = sum(counts) - softirq_totals_now[name] = total - prev_total = EXEC_CONTEXT_PREV["softirq_totals"].get(name) - per_sec = 0.0 - if dt and prev_total is not None: - per_sec = max(0.0, (total - prev_total) / dt) - softirq_rows.append({ - "name": name, - "total": int(total), - "per_sec": round(per_sec, 2) - }) - except (IOError, PermissionError): - pass - - irq_rows.sort(key=lambda row: (row["per_sec"], row["total"]), reverse=True) - softirq_rows.sort(key=lambda row: (row["per_sec"], row["total"]), reverse=True) - hard_top = irq_rows[:5] - soft_top = softirq_rows[:4] - - hard_total_rate = sum(row["per_sec"] for row in irq_rows) - soft_total_rate = sum(row["per_sec"] for row in softirq_rows) - net_softirq_rate = 0.0 - block_softirq_rate = 0.0 - timer_softirq_rate = 0.0 - for row in softirq_rows: - nm = row["name"].upper() - if nm in ("NET_RX", "NET_TX"): - net_softirq_rate += row["per_sec"] - elif nm == "BLOCK": - block_softirq_rate += row["per_sec"] - elif nm == "TIMER": - timer_softirq_rate += row["per_sec"] - - EXEC_CONTEXT_PREV["timestamp"] = now_ts - EXEC_CONTEXT_PREV["irq_totals"] = irq_totals_now - EXEC_CONTEXT_PREV["softirq_totals"] = softirq_totals_now - - # Check for preempted processes (simplified - check if process is in 'R' state but not on CPU) - preempted = False - preempted_pid = None - try: - # This is a simplified check - in reality, preemption detection is more complex - # We check if there are processes in 'R' state (runnable but not running) - proc_dirs = [d for d in os.listdir('/proc') if d.isdigit()] - for pid in proc_dirs[:20]: # Check first 20 - try: - stat_path = f'/proc/{pid}/stat' - if os.path.exists(stat_path): - with open(stat_path, 'r') as f: - stat_data = f.read().split() - if len(stat_data) > 2: - state = stat_data[2] - # 'R' = running/runnable, but if it's not the active one, it might be preempted - if state == 'R' and active_pid and int(pid) != active_pid: - preempted = True - preempted_pid = int(pid) - break - except (ValueError, IOError, PermissionError, IndexError): - continue - except PermissionError: - pass - - return jsonify({ - 'mode': mode, - 'cpu_state': cpu_state, - 'syscall_active': syscall_active, - 'syscall_name': syscall_name, - 'active_pid': active_pid, - 'active_syscalls': active_syscalls, # List of processes with active syscalls - 'interrupts': interrupts[:10], # Limit to 10 most recent - 'irq_stack': { - 'hard': hard_top, - 'soft': soft_top, - 'summary': { - 'hard_total_per_sec': round(hard_total_rate, 2), - 'soft_total_per_sec': round(soft_total_rate, 2), - 'net_softirq_per_sec': round(net_softirq_rate, 2), - 'block_softirq_per_sec': round(block_softirq_rate, 2), - 'timer_softirq_per_sec': round(timer_softirq_rate, 2) - } - }, - 'preempted': preempted, - 'preempted_pid': preempted_pid, - 'cpu_count': psutil.cpu_count(), - 'timestamp': datetime.now().isoformat() - }) + return jsonify( + _execution_service.get_execution_context_data( + syscall_names=SYSCALL_NAMES, + map_interrupt_to_subsystem_fn=map_interrupt_to_subsystem, + exec_context_prev=EXEC_CONTEXT_PREV, + ) + ) except Exception as e: - return jsonify({'error': str(e)}), 500 + return jsonify({"error": str(e)}), 500 def get_kernel_dna_data(): - """ - Collect Kernel DNA data: syscalls, interrupts, context switches, locks - Returns data structured for DNA visualization - """ - dna_data = { - 'nucleotides': [], # List of events: syscall, interrupt, context_switch, lock - 'genes': [], # Kernel subsystems segments - 'mutations': [], # Anomalies detected - 'timestamp': datetime.now().isoformat() - } - - # 1. Collect syscalls (A nucleotides) - try: - syscalls = get_real_system_calls() - for syscall in syscalls[:20]: # Limit to 20 most frequent - dna_data['nucleotides'].append({ - 'type': 'syscall', - 'code': 'A', - 'name': syscall['name'], - 'count': syscall.get('count', 0), - 'subsystem': syscall.get('subsystem') or map_syscall_to_subsystem(syscall['name']), - 'timestamp': datetime.now().isoformat() - }) - except Exception as e: - print(f"Error collecting syscalls: {e}") - - # 2. Collect interrupts (T nucleotides) - try: - with open('/proc/interrupts', 'r') as f: - interrupt_lines = f.readlines() - # Skip header line - for line in interrupt_lines[1:11]: # First 10 interrupt lines - parts = line.strip().split() - if len(parts) > 1: - interrupt_name = parts[0].rstrip(':') - total_count = sum(int(x) for x in parts[1:] if x.isdigit()) - if total_count > 0: - dna_data['nucleotides'].append({ - 'type': 'interrupt', - 'code': 'T', - 'name': interrupt_name, - 'count': total_count, - 'subsystem': map_interrupt_to_subsystem(interrupt_name), - 'timestamp': datetime.now().isoformat() - }) - except (IOError, ValueError, PermissionError) as e: - print(f"Error collecting interrupts: {e}") - dna_data['nucleotides'].extend(_kernel_dna_softirq_nucleotides()) - - # 3. Collect context switches (C nucleotides) - try: - with open('/proc/stat', 'r') as f: - for line in f: - if line.startswith('ctxt '): - ctxt_count = int(line.split()[1]) - # Calculate context switches per second (simplified) - dna_data['nucleotides'].append({ - 'type': 'context_switch', - 'code': 'C', - 'name': 'context_switch', - 'count': ctxt_count, - 'subsystem': 'sched', - 'timestamp': datetime.now().isoformat() - }) - break - except (IOError, ValueError, PermissionError) as e: - print(f"Error collecting context switches: {e}") - - # 4. Collect locks (G nucleotides) - from /proc/locks - try: - with open('/proc/locks', 'r') as f: - lock_lines = f.readlines() - lock_count = len(lock_lines) - if lock_count > 0: - dna_data['nucleotides'].append({ - 'type': 'lock', - 'code': 'G', - 'name': 'mutex/lock', - 'count': lock_count, - 'subsystem': 'kernel', - 'timestamp': datetime.now().isoformat() - }) - except (IOError, PermissionError) as e: - # Fallback: estimate locks based on process count - try: - process_count = len(psutil.pids()) - estimated_locks = process_count // 10 - dna_data['nucleotides'].append({ - 'type': 'lock', - 'code': 'G', - 'name': 'mutex/lock', - 'count': estimated_locks, - 'subsystem': 'kernel', - 'timestamp': datetime.now().isoformat() - }) - except: - pass - - # 5. Define gene segments (kernel subsystems) - dna_data['genes'] = [ - {'name': 'sched', 'start': 0, 'end': 0.2, 'color': '#58b6d8'}, - {'name': 'net', 'start': 0.2, 'end': 0.4, 'color': '#4a9eff'}, - {'name': 'fs', 'start': 0.4, 'end': 0.6, 'color': '#6bcf7f'}, - {'name': 'mm', 'start': 0.6, 'end': 0.8, 'color': '#ffa94d'}, - {'name': 'drivers', 'start': 0.8, 'end': 1.0, 'color': '#ff6b9d'} - ] - - # 6. Detect mutations (anomalies) - mutations = [] - - # Check for syscall flood - syscall_count = sum(1 for n in dna_data['nucleotides'] if n['type'] == 'syscall') - if syscall_count > 15: - mutations.append({ - 'type': 'syscall_flood', - 'severity': 'high', - 'message': f'Syscall flood detected: {syscall_count} active syscalls', - 'position': 0.3 - }) - - # Check for abnormal context switching - ctxt_switches = [n for n in dna_data['nucleotides'] if n['type'] == 'context_switch'] - if ctxt_switches and ctxt_switches[0]['count'] > 1000000: - mutations.append({ - 'type': 'abnormal_context_switch', - 'severity': 'medium', - 'message': 'Abnormal context switching rate detected', - 'position': 0.5 - }) - - # Check for lock contention - locks = [n for n in dna_data['nucleotides'] if n['type'] == 'lock'] - if locks and locks[0]['count'] > 100: - mutations.append({ - 'type': 'lock_contention', - 'severity': 'medium', - 'message': f'High lock contention: {locks[0]["count"]} active locks', - 'position': 0.7 - }) - - dna_data['mutations'] = mutations - - return dna_data + return _execution_service.get_kernel_dna_data( + get_real_system_calls_fn=get_real_system_calls, + map_syscall_to_subsystem_fn=map_syscall_to_subsystem, + map_interrupt_to_subsystem_fn=map_interrupt_to_subsystem, + softirq_nucleotides_fn=_kernel_dna_softirq_nucleotides, + ) def map_syscall_to_subsystem(syscall_name): """Map syscall name to kernel subsystem"""