diff --git a/docs/input-commands.md b/docs/input-commands.md index e1c559bb..cf4b0afc 100644 --- a/docs/input-commands.md +++ b/docs/input-commands.md @@ -262,6 +262,36 @@ i s # Short form **Description:** Display all loaded source files with debug information. +### info file - Executable File Information + +**Syntax:** +``` +info file +i file # Short form +i f # Shortest form (no args) +``` + +**Description:** +Display executable file information including: +- File path and type (ELF 64-bit/32-bit) +- Entry point address +- Symbol table status +- Debug information status (includes .gnu_debuglink detection) +- Section addresses (.text, .data) +- Launch mode (PID mode or static analysis mode) + +**Output:** +- **ELF virtual addresses**: For `-t` mode (static analysis) +- **Runtime loaded addresses**: For `-p` mode (attached to process) +- **Debug link**: Shows separate debug file path if using .gnu_debuglink + +**Examples:** +``` +info file # Show executable info +i file # Short form +i f # Shortest form +``` + ### info share - List Shared Libraries **Syntax:** @@ -271,7 +301,24 @@ i sh # Short form ``` **Description:** -Display all loaded shared libraries (dynamic libraries). +Display all loaded shared libraries (dynamic libraries) with: +- Memory address ranges (from/to) +- Symbol table status +- Debug information status +- Library file paths +- Separate debug files (if using .gnu_debuglink) + +**Output Format:** +``` +📚 Shared Libraries (N): + +From To Syms Debug Shared Object +────────────────────────────────────────────────────────────────────── +0x00007f... 0x00007f... ✓ ✓ /lib/libc.so.6 + +Debug files (.gnu_debuglink): + /lib/libc.so.6 → /usr/lib/debug/.build-id/ab/cdef.debug +``` ### info function - Function Debug Info @@ -578,10 +625,11 @@ Exit GhostScope. You can also use `Ctrl+C` twice to quit. | `disable` | `dis` | Disable trace | | `delete` | `del` | Delete trace | | `info` | `i` | View information | +| `info file` | `i f`, `i file` | View executable file info | | `info trace` | `i t` | View trace status | | `info source` | `i s` | View source files | | `info share` | `i sh` | View shared libraries | -| `info function` | `i f` | View function info | +| `info function` | `i f ` | View function info | | `info line` | `i l` | View line info | | `info address` | `i a` | View address info | | `save traces` | `s t` | Save trace points | diff --git a/docs/zh/input-commands.md b/docs/zh/input-commands.md index b38ea97e..1fcaf27c 100644 --- a/docs/zh/input-commands.md +++ b/docs/zh/input-commands.md @@ -258,6 +258,36 @@ i s # 缩写形式 **说明:** 显示所有已加载的带调试信息的源文件。 +### info file - 可执行文件信息 + +**语法:** +``` +info file +i file # 缩写形式 +i f # 最短形式(无参数) +``` + +**说明:** +显示可执行文件的详细信息,包括: +- 文件路径和类型(ELF 64位/32位) +- 入口点地址 +- 符号表状态 +- 调试信息状态(包括 .gnu_debuglink 检测) +- 段地址(.text、.data) +- 启动模式(PID 模式或静态分析模式) + +**输出说明:** +- **ELF 虚拟地址**:`-t` 模式(静态分析) +- **运行时加载地址**:`-p` 模式(附加到进程) +- **调试链接**:使用 .gnu_debuglink 时显示独立调试文件路径 + +**示例:** +``` +info file # 显示可执行文件信息 +i file # 缩写形式 +i f # 最短形式 +``` + ### info share - 列出共享库 **语法:** @@ -267,7 +297,24 @@ i sh # 缩写形式 ``` **说明:** -显示所有已加载的共享库(动态库)。 +显示所有已加载的共享库(动态库),包括: +- 内存地址范围(起始/结束) +- 符号表状态 +- 调试信息状态 +- 库文件路径 +- 独立调试文件(使用 .gnu_debuglink 时) + +**输出格式:** +``` +📚 Shared Libraries (N): + +From To Syms Debug Shared Object +────────────────────────────────────────────────────────────────────── +0x00007f... 0x00007f... ✓ ✓ /lib/libc.so.6 + +Debug files (.gnu_debuglink): + /lib/libc.so.6 → /usr/lib/debug/.build-id/ab/cdef.debug +``` ### info function - 函数调试信息 @@ -603,10 +650,11 @@ exit | `disable` | `dis` | 禁用追踪 | | `delete` | `del` | 删除追踪 | | `info` | `i` | 查看信息 | +| `info file` | `i f`, `i file` | 查看可执行文件信息 | | `info trace` | `i t` | 查看追踪状态 | | `info source` | `i s` | 查看源文件 | | `info share` | `i sh` | 查看共享库 | -| `info function` | `i f` | 查看函数信息 | +| `info function` | `i f ` | 查看函数信息 | | `info line` | `i l` | 查看行信息 | | `info address` | `i a` | 查看地址信息 | | `save traces` | `s t` | 保存追踪点 | diff --git a/ghostscope-dwarf/src/analyzer.rs b/ghostscope-dwarf/src/analyzer.rs index e05bfdc2..f145ac30 100644 --- a/ghostscope-dwarf/src/analyzer.rs +++ b/ghostscope-dwarf/src/analyzer.rs @@ -643,6 +643,10 @@ impl DwarfAnalyzer { .filter(|(path, _)| self.is_shared_library(path)) .map(|(path, module_data)| { let mapping = module_data.module_mapping(); + let debug_file_path = module_data + .get_debug_file_path() + .map(|p| p.to_string_lossy().to_string()); + SharedLibraryInfo { from_address: mapping.loaded_address.unwrap_or(0), to_address: mapping.loaded_address.map_or(0, |addr| addr + mapping.size), @@ -650,11 +654,105 @@ impl DwarfAnalyzer { debug_info_available: true, // DWARF modules always have debug info library_path: path.to_string_lossy().to_string(), size: mapping.size, + debug_file_path, } }) .collect() } + /// Get executable file information (for "info file" command) + pub fn get_executable_file_info(&self) -> Option { + // Find the primary executable (not a shared library) + let executable = self + .modules + .iter() + .find(|(path, _)| !self.is_shared_library(path))?; + + let (exe_path, module_data) = executable; + let file_path = exe_path.to_string_lossy().to_string(); + + // Parse the ELF file to get detailed information + let file_bytes = std::fs::read(exe_path).ok()?; + let obj = object::File::parse(&file_bytes[..]).ok()?; + + // Get file type + let file_type = match obj.format() { + object::BinaryFormat::Elf => { + if obj.is_64() { + "ELF 64-bit executable" + } else { + "ELF 32-bit executable" + } + } + _ => "Unknown format", + } + .to_string(); + + // Check if has symbols + let has_symbols = !module_data.get_function_names().is_empty() + || obj.symbols().count() > 0 + || obj.dynamic_symbols().count() > 0; + + // Check if has debug info - check if DWARF was successfully loaded + // This includes both embedded DWARF and debug link external files + let has_debug_info = module_data.has_dwarf_info(); + + // Get debug file path if using separate debug file (e.g., via .gnu_debuglink) + let debug_file_path = module_data.get_debug_file_path(); + + // Get load bias for PID mode (ASLR offset) + // In PID mode, we need to add the runtime load address to ELF VMAs + let load_bias = if self.pid != 0 { + module_data.module_mapping().loaded_address.unwrap_or(0) + } else { + 0 + }; + + // Get entry point (add load bias in PID mode) + let entry_point = Some(obj.entry() + load_bias); + + // Get .text section info (add load bias in PID mode) + let text_section = obj.section_by_name(".text").map(|section| { + let addr = section.address() + load_bias; + let size = section.size(); + SectionInfo { + start_address: addr, + end_address: addr + size, + size, + } + }); + + // Get .data section info (add load bias in PID mode) + let data_section = obj.section_by_name(".data").map(|section| { + let addr = section.address() + load_bias; + let size = section.size(); + SectionInfo { + start_address: addr, + end_address: addr + size, + size, + } + }); + + // Determine mode description based on pid + let mode_description = if self.pid != 0 { + format!("Attached to process {} (PID mode)", self.pid) + } else { + "Static analysis mode (target file specified with -t)".to_string() + }; + + Some(ExecutableFileInfo { + file_path, + file_type, + entry_point, + has_symbols, + has_debug_info, + debug_file_path: debug_file_path.map(|p| p.to_string_lossy().to_string()), + text_section, + data_section, + mode_description, + }) + } + /// Compute per-module section offsets (runtime bias) using /proc/[pid]/maps /// Only available in -p mode; returns a vector of (module_path, offsets) pub fn compute_section_offsets(&self) -> Result> { @@ -889,12 +987,35 @@ pub struct AnalyzerStats { /// Shared library information (compatible with ghostscope-ui) #[derive(Debug, Clone)] pub struct SharedLibraryInfo { - pub from_address: u64, // Starting address in memory - pub to_address: u64, // Ending address in memory - pub symbols_read: bool, // Whether symbols were successfully read - pub debug_info_available: bool, // Whether debug information is available - pub library_path: String, // Full path to the library file - pub size: u64, // Size of the library in memory + pub from_address: u64, // Starting address in memory + pub to_address: u64, // Ending address in memory + pub symbols_read: bool, // Whether symbols were successfully read + pub debug_info_available: bool, // Whether debug information is available + pub library_path: String, // Full path to the library file + pub size: u64, // Size of the library in memory + pub debug_file_path: Option, // Path to separate debug file (if via .gnu_debuglink) +} + +/// Executable file information (for "info file" command) +#[derive(Debug, Clone)] +pub struct ExecutableFileInfo { + pub file_path: String, + pub file_type: String, + pub entry_point: Option, + pub has_symbols: bool, + pub has_debug_info: bool, + pub debug_file_path: Option, + pub text_section: Option, + pub data_section: Option, + pub mode_description: String, +} + +/// Section information for executable files +#[derive(Debug, Clone)] +pub struct SectionInfo { + pub start_address: u64, + pub end_address: u64, + pub size: u64, } /// Simple file information compatible with ghostscope-binary diff --git a/ghostscope-dwarf/src/module/data.rs b/ghostscope-dwarf/src/module/data.rs index 978682ee..a7773937 100644 --- a/ghostscope-dwarf/src/module/data.rs +++ b/ghostscope-dwarf/src/module/data.rs @@ -1331,6 +1331,24 @@ impl ModuleData { self.scoped_file_manager.get_stats().1 } + /// Check if DWARF debug information is available (including debug link) + pub(crate) fn has_dwarf_info(&self) -> bool { + self.get_line_header_count() > 0 + } + + /// Get debug file path (if different from binary, e.g., via .gnu_debuglink) + pub(crate) fn get_debug_file_path(&self) -> Option { + let dwarf_path = &self._dwarf_mapped_file.path; + let binary_path = &self._binary_mapped_file.path; + + // If DWARF file path is different from binary path, it's a separate debug file + if dwarf_path != binary_path { + Some(dwarf_path.clone()) + } else { + None + } + } + /// Get cache statistics pub(crate) fn get_cache_stats(&self) -> (usize, usize) { self.resolver.get_cache_stats() diff --git a/ghostscope-ui/src/components/app.rs b/ghostscope-ui/src/components/app.rs index 0de29105..dd6157f3 100644 --- a/ghostscope-ui/src/components/app.rs +++ b/ghostscope-ui/src/components/app.rs @@ -2267,6 +2267,48 @@ impl App { }; let _ = self.handle_action(action); } + RuntimeStatus::ExecutableFileInfo { + file_path, + file_type, + entry_point, + has_symbols, + has_debug_info, + debug_file_path, + text_section, + data_section, + mode_description, + } => { + self.clear_waiting_state(); + let info_display = + crate::components::command_panel::response_formatter::ExecutableFileInfoDisplay { + file_path: &file_path, + file_type: &file_type, + entry_point, + has_symbols, + has_debug_info, + debug_file_path: &debug_file_path, + text_section: &text_section, + data_section: &data_section, + mode_description: &mode_description, + }; + let formatted_info = + crate::components::command_panel::ResponseFormatter::format_executable_file_info( + &info_display, + ); + let action = Action::AddResponse { + content: formatted_info, + response_type: crate::action::ResponseType::Success, + }; + let _ = self.handle_action(action); + } + RuntimeStatus::ExecutableFileInfoFailed { error } => { + self.clear_waiting_state(); + let action = Action::AddResponse { + content: format!("Failed to get executable file information: {error}"), + response_type: crate::action::ResponseType::Error, + }; + let _ = self.handle_action(action); + } RuntimeStatus::SrcPathInfo { info } => { self.clear_waiting_state(); let formatted = info.format_for_display(); @@ -2682,6 +2724,7 @@ impl App { | RuntimeStatus::TraceInfoFailed { .. } | RuntimeStatus::FileInfoFailed { .. } | RuntimeStatus::ShareInfoFailed { .. } + | RuntimeStatus::ExecutableFileInfoFailed { .. } | RuntimeStatus::SrcPathFailed { .. } ); diff --git a/ghostscope-ui/src/components/command_panel/command_parser.rs b/ghostscope-ui/src/components/command_panel/command_parser.rs index 21c3fd0e..699c57aa 100644 --- a/ghostscope-ui/src/components/command_panel/command_parser.rs +++ b/ghostscope-ui/src/components/command_panel/command_parser.rs @@ -175,6 +175,7 @@ impl CommandParser { [ "🔍 Information Commands:", " info - Show available info commands", + " info file - Show executable file info and sections (i f, i file)", " info trace [id] - Show trace status (i t [id])", " info source - Show all source files (i s)", " info share - Show loaded shared libraries (i sh)", @@ -277,6 +278,7 @@ impl CommandParser { "stop output", "stop session", // Info subcommands + "info file", "info trace", "info source", "info share", @@ -292,6 +294,7 @@ impl CommandParser { "srcpath reset", // Shortcut commands "i", + "i file", "i s", "i sh", "i t", @@ -478,6 +481,15 @@ impl CommandParser { }]); } + if command == "info file" { + state.input_state = InputState::WaitingResponse { + command: command.to_string(), + sent_time: Instant::now(), + command_type: CommandType::InfoFile, + }; + return Some(vec![Action::SendRuntimeCommand(RuntimeCommand::InfoFile)]); + } + if command == "info source" { state.input_state = InputState::WaitingResponse { command: command.to_string(), @@ -910,6 +922,16 @@ impl CommandParser { return Some(vec![Action::SendRuntimeCommand(RuntimeCommand::InfoShare)]); } + // Handle "i file" or "i f" (no args) -> "info file" + if command == "i file" || command == "i f" { + state.input_state = InputState::WaitingResponse { + command: "info file".to_string(), + sent_time: Instant::now(), + command_type: CommandType::InfoFile, + }; + return Some(vec![Action::SendRuntimeCommand(RuntimeCommand::InfoFile)]); + } + // Handle "i t" -> "info trace" if command == "i t" { return Some(Self::parse_info_trace_command(state, None)); @@ -944,7 +966,7 @@ impl CommandParser { )]); } else { return Some(vec![Action::AddResponse { - content: "Usage: i f ".to_string(), + content: "Usage: i f (or 'i f' for file info)".to_string(), response_type: ResponseType::Error, }]); } @@ -1013,6 +1035,7 @@ impl CommandParser { "🔍 Info Commands Usage:", "", " info - Show this help message", + " info file - Show executable file info and sections (i f, i file)", " info trace [id] - Show trace status (i t [id])", " info source - Show all source files by module (i s)", " info share - Show loaded shared libraries (i sh)", @@ -1021,6 +1044,7 @@ impl CommandParser { " info address - Show debug info for address (i a ) [TODO]", "", "💡 Shortcuts:", + " i f / i file - Same as 'info file'", " i s - Same as 'info source'", " i sh - Same as 'info share'", " i t [id] - Same as 'info trace [id]'", @@ -1029,6 +1053,7 @@ impl CommandParser { " i a - Same as 'info address ' [TODO]", "", "Examples:", + " info file - Show executable file information", " info trace - Show all traces", " i t 1 - Show specific trace info", " i f main - Show debug info for 'main' function", diff --git a/ghostscope-ui/src/components/command_panel/response_formatter.rs b/ghostscope-ui/src/components/command_panel/response_formatter.rs index de566283..44bb0f79 100644 --- a/ghostscope-ui/src/components/command_panel/response_formatter.rs +++ b/ghostscope-ui/src/components/command_panel/response_formatter.rs @@ -11,6 +11,19 @@ use ratatui::{ }; use unicode_width::UnicodeWidthChar; +/// Parameter struct for format_executable_file_info to avoid too many function arguments +pub struct ExecutableFileInfoDisplay<'a> { + pub file_path: &'a str, + pub file_type: &'a str, + pub entry_point: Option, + pub has_symbols: bool, + pub has_debug_info: bool, + pub debug_file_path: &'a Option, + pub text_section: &'a Option, + pub data_section: &'a Option, + pub mode_description: &'a str, +} + /// Handles response formatting and display for the command panel pub struct ResponseFormatter; @@ -538,6 +551,9 @@ impl ResponseFormatter { response.push_str(&UIStrings::SCRIPT_SEPARATOR.repeat(90)); response.push('\n'); + // Collect libraries with debug links for later display + let mut debug_links = Vec::new(); + for lib in libraries { let from_str = format!("0x{:016x}", lib.from_address); let to_str = format!("0x{:016x}", lib.to_address); @@ -550,6 +566,11 @@ impl ResponseFormatter { from_str, to_str, syms_read, debug_read, lib.library_path )); + // Collect debug link info + if let Some(ref debug_path) = lib.debug_file_path { + debug_links.push((lib.library_path.clone(), debug_path.clone())); + } + if !lib.debug_info_available { let library_name = lib .library_path @@ -562,6 +583,15 @@ impl ResponseFormatter { )); } } + + // Show debug links section if any + if !debug_links.is_empty() { + response.push('\n'); + response.push_str("Debug files (.gnu_debuglink):\n"); + for (lib_path, debug_path) in debug_links { + response.push_str(&format!(" {lib_path} → {debug_path}\n")); + } + } } else { response.push_str(&format!(" {}\n", UIStrings::NO_SHARED_LIBRARIES)); } @@ -569,6 +599,90 @@ impl ResponseFormatter { response } + /// Format executable file information for display + pub fn format_executable_file_info(info: &ExecutableFileInfoDisplay) -> String { + let ExecutableFileInfoDisplay { + file_path, + file_type, + entry_point, + has_symbols, + has_debug_info, + debug_file_path, + text_section, + data_section, + mode_description, + } = info; + let mut response = String::new(); + + // Header + response.push_str("📄 Executable File Information:\n\n"); + + // File path + response.push_str(&format!(" File: {file_path}\n")); + + // File type + response.push_str(&format!(" Type: {file_type}\n")); + + // Entry point + if let Some(entry) = entry_point { + response.push_str(&format!(" Entry point: 0x{entry:x}\n")); + } + + response.push('\n'); + + // Symbol and debug information status + response.push_str(" Symbols: "); + if *has_symbols { + response.push_str("✓ Available\n"); + } else { + response.push_str("✗ Not available\n"); + } + + response.push_str(" Debug info: "); + if *has_debug_info { + response.push_str("✓ Available"); + if let Some(ref debug_path) = debug_file_path { + response.push_str(&format!(" (via debug link: {debug_path})")); + } + response.push('\n'); + } else { + response.push_str("✗ Not available\n"); + } + + response.push('\n'); + + // Determine if this is static analysis mode + let is_static_mode = mode_description.contains("Static analysis mode"); + + // Sections + if is_static_mode { + response.push_str(" Sections (ELF virtual addresses):\n"); + } else { + response.push_str(" Sections (runtime loaded addresses):\n"); + } + + if let Some(text) = text_section { + response.push_str(&format!( + " .text: 0x{:016x} - 0x{:016x} (size: {} bytes)\n", + text.start_address, text.end_address, text.size + )); + } + + if let Some(data) = data_section { + response.push_str(&format!( + " .data: 0x{:016x} - 0x{:016x} (size: {} bytes)\n", + data.start_address, data.end_address, data.size + )); + } + + response.push('\n'); + + // Mode description + response.push_str(&format!(" Mode: {mode_description}\n")); + + response + } + /// Render the command panel content pub fn render_panel(f: &mut Frame, area: Rect, state: &CommandPanelState) { // Calculate inner area (excluding borders) diff --git a/ghostscope-ui/src/events.rs b/ghostscope-ui/src/events.rs index 3b57cc80..00ac5f4b 100644 --- a/ghostscope-ui/src/events.rs +++ b/ghostscope-ui/src/events.rs @@ -339,6 +339,7 @@ pub enum RuntimeCommand { InfoTraceAll, InfoSource, // Get all source files information InfoShare, // Get shared library information (like GDB's "info share") + InfoFile, // Get executable file information and sections (like GDB's "info file") SaveTraces { filename: Option, filter: crate::components::command_panel::trace_persistence::SaveFilter, @@ -553,6 +554,22 @@ pub enum RuntimeStatus { ShareInfoFailed { error: String, }, + /// Executable file information response + ExecutableFileInfo { + file_path: String, + file_type: String, + entry_point: Option, + has_symbols: bool, + has_debug_info: bool, + debug_file_path: Option, + text_section: Option, + data_section: Option, + mode_description: String, + }, + /// Failed to get executable file information + ExecutableFileInfoFailed { + error: String, + }, // Module-level loading progress (new) DwarfModuleDiscovered { module_path: String, @@ -647,12 +664,21 @@ pub struct SourceFileGroup { /// Shared library information (similar to GDB's "info share" output) #[derive(Debug, Clone)] pub struct SharedLibraryInfo { - pub from_address: u64, // Starting address in memory - pub to_address: u64, // Ending address in memory - pub symbols_read: bool, // Whether symbols were successfully read - pub debug_info_available: bool, // Whether debug information is available - pub library_path: String, // Full path to the library file - pub size: u64, // Size of the library in memory + pub from_address: u64, // Starting address in memory + pub to_address: u64, // Ending address in memory + pub symbols_read: bool, // Whether symbols were successfully read + pub debug_info_available: bool, // Whether debug information is available + pub library_path: String, // Full path to the library file + pub size: u64, // Size of the library in memory + pub debug_file_path: Option, // Path to separate debug file (if via .gnu_debuglink) +} + +/// Section information for executable files +#[derive(Debug, Clone)] +pub struct SectionInfo { + pub start_address: u64, // Starting address of the section + pub end_address: u64, // Ending address of the section + pub size: u64, // Size of the section in bytes } impl EventRegistry { diff --git a/ghostscope-ui/src/model/panel_state.rs b/ghostscope-ui/src/model/panel_state.rs index b678a189..9c0ffe78 100644 --- a/ghostscope-ui/src/model/panel_state.rs +++ b/ghostscope-ui/src/model/panel_state.rs @@ -417,6 +417,7 @@ pub enum CommandType { InfoTraceAll, InfoSource, InfoShare, + InfoFile, SaveTraces, LoadTraces, SrcPath, diff --git a/ghostscope/src/runtime/coordinator.rs b/ghostscope/src/runtime/coordinator.rs index d491469c..3b7dffa2 100644 --- a/ghostscope/src/runtime/coordinator.rs +++ b/ghostscope/src/runtime/coordinator.rs @@ -199,6 +199,9 @@ async fn run_runtime_coordinator( RuntimeCommand::InfoShare => { info_handlers::handle_info_share(&session, &mut runtime_channels).await; } + RuntimeCommand::InfoFile => { + info_handlers::handle_info_file(&session, &mut runtime_channels).await; + } RuntimeCommand::RequestSourceCode => { source_handlers::handle_main_source_request(&mut session, &mut runtime_channels).await; } diff --git a/ghostscope/src/runtime/info_handlers.rs b/ghostscope/src/runtime/info_handlers.rs index 33bf8bea..4a2577bb 100644 --- a/ghostscope/src/runtime/info_handlers.rs +++ b/ghostscope/src/runtime/info_handlers.rs @@ -106,6 +106,73 @@ pub async fn handle_info_source( crate::runtime::source_handlers::handle_request_source_code(session, runtime_channels).await; } +/// Handle InfoFile command +pub async fn handle_info_file( + session: &Option, + runtime_channels: &mut RuntimeChannels, +) { + if let Some(ref session) = session { + // Get executable file information from ProcessAnalyzer + if let Some(ref analyzer) = session.process_analyzer { + // Get primary executable file information + let file_info = analyzer.get_executable_file_info(); + + if let Some(info) = file_info { + // Convert SectionInfo from ghostscope-dwarf to ghostscope-ui + let text_section = + info.text_section + .map(|section| ghostscope_ui::events::SectionInfo { + start_address: section.start_address, + end_address: section.end_address, + size: section.size, + }); + + let data_section = + info.data_section + .map(|section| ghostscope_ui::events::SectionInfo { + start_address: section.start_address, + end_address: section.end_address, + size: section.size, + }); + + let _ = runtime_channels + .status_sender + .send(RuntimeStatus::ExecutableFileInfo { + file_path: info.file_path, + file_type: info.file_type, + entry_point: info.entry_point, + has_symbols: info.has_symbols, + has_debug_info: info.has_debug_info, + debug_file_path: info.debug_file_path, + text_section, + data_section, + mode_description: info.mode_description, + }); + } else { + let _ = + runtime_channels + .status_sender + .send(RuntimeStatus::ExecutableFileInfoFailed { + error: "No executable file information available".to_string(), + }); + } + } else { + let _ = runtime_channels + .status_sender + .send(RuntimeStatus::ExecutableFileInfoFailed { + error: "No process analyzer available".to_string(), + }); + } + } else { + let _ = runtime_channels + .status_sender + .send(RuntimeStatus::ExecutableFileInfoFailed { + error: "No active debugging session. Target process may not be attached or initialization failed." + .to_string(), + }); + } +} + /// Handle InfoShare command pub async fn handle_info_share( session: &Option, @@ -136,6 +203,7 @@ pub async fn handle_info_share( debug_info_available: lib.debug_info_available, library_path: lib.library_path, size: lib.size, + debug_file_path: lib.debug_file_path, }) .collect();