ghostscope 0.1.1

Command-line entrypoint that drives GhostScope compiler, loader, and UI end-to-end.
mod cli;
mod config;
mod core;
mod logging;
mod runtime;
mod script;
mod tracing;
mod util;

use anyhow::Result;
// Use external tracing crate (not the local tracing module)
use ::tracing::{info, warn};
use libc as c;

#[tokio::main]
async fn main() -> Result<()> {
    // Setup panic hook before doing anything else
    crate::util::setup_panic_hook();

    // Pre-clean any stale per-process pinned offsets map from a previous crashed session.
    // This prevents PID reuse collisions leaving an old map affecting the new instance.
    let _ = ghostscope_process::maps::cleanup_pinned_proc_offsets();

    // Parse command line arguments
    let parsed_args = config::Args::parse_args();

    // Load and merge configuration
    let config_path = parsed_args.config.clone();
    let merged_config =
        match config::MergedConfig::new_with_explicit_config(parsed_args, config_path) {
            Ok(config) => config,
            Err(e) => {
                eprintln!("❌ Configuration Error:\n{e}");
                eprintln!("\n💡 Tips:");
                eprintln!("  • Check the example config.toml in the project root");
                eprintln!("  • Verify TOML syntax is correct");
                eprintln!("  • Ensure all values use the correct format");
                std::process::exit(1);
            }
        };

    // Initialize logging with full configuration
    let log_file_string = merged_config.log_file.to_string_lossy().to_string();
    let log_file_path = Some(log_file_string.as_str());
    if let Err(e) = logging::initialize_logging_with_config(
        log_file_path,
        merged_config.enable_logging,
        merged_config.enable_console_logging,
        merged_config.log_level,
        merged_config.tui_mode,
    ) {
        eprintln!("Failed to initialize logging: {e}");
        return Err(anyhow::anyhow!("Failed to initialize logging: {}", e));
    }

    // Register atexit cleanup for pinned maps (per-process path)
    unsafe {
        c::atexit(crate::util::cleanup_pinned_maps_on_exit);
    }

    // Log which configuration file was loaded (after logging is initialized)
    if let Some(config_path) = &merged_config.config_file_path {
        info!("Configuration loaded from: {}", config_path.display());
    } else {
        let home_hint = std::env::var("HOME").unwrap_or_else(|_| "(unset)".into());
        info!(
            "Using built-in defaults (no config found at {}/.ghostscope/config.toml or ./ghostscope.toml)",
            home_hint
        );
    }

    // Ensure we have the privileges needed for eBPF interaction
    crate::util::ensure_privileges();

    // Detect kernel eBPF capabilities once at startup
    if merged_config.ebpf_config.force_perf_event_array {
        warn!("⚠️  TESTING MODE: force_perf_event_array=true - will use PerfEventArray");
        info!("Skipping RingBuf detection, validating PerfEventArray support...");
        match ghostscope_loader::KernelCapabilities::get_perf_only() {
            Ok(kernel_caps) => {
                info!("Kernel eBPF capabilities:");
                info!(
                    "  PerfEventArray support: {}",
                    kernel_caps.supports_perf_event_array
                );
            }
            Err(err) => {
                eprintln!("Error: {err}");
                eprintln!("GhostScope requires Linux kernel >= 4.3 with PerfEventArray enabled.");
                std::process::exit(1);
            }
        }
    } else {
        match ghostscope_loader::KernelCapabilities::get() {
            Ok(kernel_caps) => {
                info!("Kernel eBPF capabilities:");
                info!("  RingBuf support: {}", kernel_caps.supports_ringbuf);

                if !kernel_caps.supports_ringbuf {
                    warn!("⚠️  Kernel does not support RingBuf (requires >= 5.8)");
                    warn!("⚠️  GhostScope will use PerfEventArray as fallback");
                }
            }
            Err(err) => {
                eprintln!("Error: {err}");
                eprintln!(
                    "GhostScope requires Linux kernel >= 4.3 with either RingBuf (>= 5.8) or PerfEventArray support."
                );
                eprintln!(
                    "Hint: ensure CONFIG_BPF, CONFIG_BPF_SYSCALL and CONFIG_UPROBE_EVENTS are enabled in your kernel."
                );
                std::process::exit(1);
            }
        }
    }

    // Validate core arguments (TODO: move validation to MergedConfig)
    // For now, create a temporary ParsedArgs for validation
    let temp_args = config::ParsedArgs {
        binary_path: merged_config.binary_path.clone(),
        target_path: merged_config.target_path.clone(),
        binary_args: merged_config.binary_args.clone(),
        log_file: Some(merged_config.log_file.clone()),
        enable_logging: merged_config.enable_logging,
        enable_console_logging: merged_config.enable_console_logging,
        log_level: merged_config.log_level,
        config: None, // Not needed for validation
        debug_file: merged_config.debug_file.clone(),
        script: merged_config.script.clone(),
        script_file: merged_config.script_file.clone(),
        pid: merged_config.pid,
        tui_mode: merged_config.tui_mode,
        should_save_llvm_ir: merged_config.should_save_llvm_ir,
        should_save_ebpf: merged_config.should_save_ebpf,
        should_save_ast: merged_config.should_save_ast,
        layout_mode: merged_config.layout_mode,
        has_explicit_log_flag: false, // Not needed for validation
        has_explicit_console_log_flag: false, // Not needed for validation
        force_perf_event_array: merged_config.ebpf_config.force_perf_event_array,
        enable_sysmon_for_shared_lib: merged_config.ebpf_config.enable_sysmon_for_shared_lib,
        allow_loose_debug_match: merged_config.dwarf_allow_loose_debug_match,
        source_panel: false,
        no_source_panel: false,
    };
    temp_args.validate()?;

    // Route to appropriate runtime mode
    if merged_config.tui_mode {
        runtime::run_tui_coordinator_with_config(merged_config).await
    } else {
        cli::run_command_line_runtime_with_config(merged_config).await
    }
}