Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions docs/input-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**
Expand All @@ -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

Expand Down Expand Up @@ -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 <name>` | View function info |
| `info line` | `i l` | View line info |
| `info address` | `i a` | View address info |
| `save traces` | `s t` | Save trace points |
Expand Down
52 changes: 50 additions & 2 deletions docs/zh/input-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 - 列出共享库

**语法:**
Expand All @@ -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 - 函数调试信息

Expand Down Expand Up @@ -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 <name>` | 查看函数信息 |
| `info line` | `i l` | 查看行信息 |
| `info address` | `i a` | 查看地址信息 |
| `save traces` | `s t` | 保存追踪点 |
Expand Down
133 changes: 127 additions & 6 deletions ghostscope-dwarf/src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,18 +643,116 @@ 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),
symbols_read: !module_data.get_function_names().is_empty(),
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<ExecutableFileInfo> {
// 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<Vec<(PathBuf, u64, SectionOffsets)>> {
Expand Down Expand Up @@ -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<String>, // 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<u64>,
pub has_symbols: bool,
pub has_debug_info: bool,
pub debug_file_path: Option<String>,
pub text_section: Option<SectionInfo>,
pub data_section: Option<SectionInfo>,
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
Expand Down
18 changes: 18 additions & 0 deletions ghostscope-dwarf/src/module/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf> {
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()
Expand Down
43 changes: 43 additions & 0 deletions ghostscope-ui/src/components/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -2682,6 +2724,7 @@ impl App {
| RuntimeStatus::TraceInfoFailed { .. }
| RuntimeStatus::FileInfoFailed { .. }
| RuntimeStatus::ShareInfoFailed { .. }
| RuntimeStatus::ExecutableFileInfoFailed { .. }
| RuntimeStatus::SrcPathFailed { .. }
);

Expand Down
Loading