add check if not present debug symbol

This commit is contained in:
VALLONGOL 2025-05-23 13:32:57 +02:00
parent 6310fb0ce6
commit 503b81ee22
3 changed files with 212 additions and 639 deletions

View File

@ -80,19 +80,31 @@ class GDBSession:
command = f'"{self.gdb_path}" --nx --quiet "{self.executable_path}"'
logger.info(f"Spawning GDB process: {command} with startup timeout: {timeout}s")
try:
self.child = wexpect.spawn(
command, timeout=timeout, encoding="utf-8", errors="replace"
)
self.child.expect_exact(self.gdb_prompt, timeout=max(timeout, 15))
# Aumentiamo leggermente il timeout per wexpect.spawn per dare a GDB il tempo di caricare i simboli (o fallire)
# e stampare l'output prima del primo prompt.
spawn_timeout = max(timeout, 5) # Assicurati almeno 5 secondi per lo spawn e il primo output
self.child = wexpect.spawn(command, timeout=spawn_timeout, encoding='utf-8', errors='replace')
# Aspetta il primo prompt. L'output prima di questo prompt conterrà i messaggi di caricamento.
self.child.expect_exact(self.gdb_prompt, timeout=max(timeout, 15)) # Timeout per il prompt
output_at_startup = self.child.before if hasattr(self.child, 'before') else ""
logger.debug(f"GDB output at startup (before first prompt):\n{output_at_startup}") # Logga l'output iniziale
# --- NUOVO: Controllo per assenza di simboli ---
no_symbols_message = "No debugging symbols found"
if no_symbols_message in output_at_startup:
self.symbols_found = False
logger.warning(f"'{no_symbols_message}' detected in GDB startup output. Debugging capabilities will be limited.")
else:
self.symbols_found = True
logger.info("Debugging symbols appear to be loaded (no 'No debugging symbols found' message detected).")
# --- FINE NUOVO CONTROLLO ---
logger.info("GDB started successfully and prompt received.")
pagination_timeout = max(5, timeout // 2)
logger.info(
f"Disabling GDB pagination ('set pagination off') with timeout: {pagination_timeout}s."
)
self.send_cmd(
"set pagination off", expect_prompt=True, timeout=pagination_timeout
)
logger.info(f"Disabling GDB pagination ('set pagination off') with timeout: {pagination_timeout}s.")
self.send_cmd("set pagination off", expect_prompt=True, timeout=pagination_timeout)
logger.info("GDB pagination disabled.")
if self.gdb_script_path:
@ -102,36 +114,27 @@ class GDBSession:
logger.info("No GDB dumper script path provided; skipping sourcing.")
self.gdb_script_sourced_successfully = False
except wexpect.TIMEOUT as e_timeout: # Specific wexpect Timeout
error_msg = (
f"Timeout ({timeout}s) waiting for GDB prompt or during GDB startup."
)
except wexpect.TIMEOUT as e_timeout:
error_msg = f"Timeout ({timeout}s) waiting for GDB prompt or during GDB startup."
logger.error(error_msg)
debug_output = "<no output captured>"
try:
if self.child:
debug_output = self.child.read_nonblocking(size=2048, timeout=1)
except Exception:
pass
logger.error(f"GDB output before timeout (if any): {debug_output}")
if self.child and self.child.isalive():
self.child.close()
if self.child: debug_output = self.child.read_nonblocking(size=2048, timeout=1)
# Aggiungiamo anche l'output catturato da `before` se disponibile
if hasattr(e_timeout, 'value') and isinstance(e_timeout.value, str): # wexpect.TIMEOUT può avere 'value'
debug_output += "\nOutput before timeout (from exception value):\n" + e_timeout.value
elif output_at_startup: # Se abbiamo catturato output_at_startup prima del timeout del prompt
debug_output += "\nOutput at startup before timeout:\n" + output_at_startup
except Exception: pass
logger.error(f"GDB output details before timeout: {debug_output}")
if self.child and self.child.isalive(): self.child.close()
self.child = None
raise TimeoutError(
error_msg
) from e_timeout # Riconverti in TimeoutError standard
except Exception as e: # QUALSIASI ALTRA ECCEZIONE
# MODIFIED: Log dell'eccezione originale in modo più dettagliato
logger.error(
f"!!! Unexpected exception in GDBSession.start(): {type(e).__name__}: {e}",
exc_info=True,
)
# logger.error(f"!!! Type of e: {type(e)}") # Aggiungi questo per vedere il tipo esatto
# logger.error(f"!!! Args of e: {e.args}") # E i suoi argomenti
if self.child and self.child.isalive():
self.child.close()
raise TimeoutError(error_msg) from e_timeout
except Exception as e:
logger.error(f"!!! Unexpected exception in GDBSession.start(): {type(e).__name__}: {e}", exc_info=True)
if self.child and self.child.isalive(): self.child.close()
self.child = None
# Rilancia l'eccezione originale per vedere se il messaggio cambia in main_window
raise # RILANCIA L'ECCEZIONE ORIGINALE
def _set_gdb_dumper_variables(

View File

@ -266,18 +266,16 @@ class ProfileExecutor:
"end_time": None,
"status": "Initialized",
"actions_summary": [
{
"action_index": i,
"breakpoint_spec": action.get("breakpoint_location", "N/A"),
"gdb_bp_num_assigned": None, # Will be filled
"address_resolved": None, # Will be filled
"variables_dumped_count": 0,
"status": "Pending",
}
{"action_index": i,
"breakpoint_spec": action.get("breakpoint_location", "N/A"),
"gdb_bp_num_assigned": None,
"address_resolved": None,
"variables_dumped_count": 0,
"status": "Pending"}
for i, action in enumerate(self.profile.get("actions", []))
],
"execution_log": [],
"files_produced_detailed": [],
"files_produced_detailed": []
}
gdb_exe = self._get_setting("general", "gdb_executable_path")
@ -289,508 +287,113 @@ class ProfileExecutor:
self._log_event(msg, True)
self.profile_execution_summary["status"] = "Error: Target not found"
self.is_running = False
self._finalize_summary_report(None)
self._finalize_summary_report(None) # Passa None perché potremmo non avere un output_path
return
actions = self.profile.get("actions", [])
if not actions:
self._log_event(
f"Profile '{profile_name}' has no actions defined. Stopping.", True
)
self._log_event(f"Profile '{profile_name}' has no actions defined. Stopping.", True)
self.profile_execution_summary["status"] = "Error: No actions"
self.is_running = False # Assicura che is_running sia False
self._finalize_summary_report(None)
return
first_action_output_dir_base = actions[0].get("output_directory", ".")
self.current_run_output_path = self._prepare_output_directory(
first_action_output_dir_base, profile_name
)
if not self.current_run_output_path:
self.profile_execution_summary["status"] = (
"Error: Cannot create output directory"
)
self.is_running = False
self._finalize_summary_report(None)
return
# Prepara la directory di output solo se procediamo
# Lo spostiamo dopo il controllo dei simboli per evitare di creare cartelle inutilmente.
# self.current_run_output_path = self._prepare_output_directory(...)
try:
self.gdb_session = GDBSession(
gdb_path=gdb_exe,
executable_path=target_exe,
gdb_script_full_path=gdb_script_path,
dumper_options=self._get_dumper_options(),
gdb_path=gdb_exe, executable_path=target_exe,
gdb_script_full_path=gdb_script_path, dumper_options=self._get_dumper_options()
)
startup_timeout = self._get_setting("timeouts", "gdb_start", 30)
self._log_event(
f"Spawning GDB for '{os.path.basename(target_exe)}'...", True
)
self._log_event(f"Spawning GDB for '{os.path.basename(target_exe)}'...", True)
self.gdb_session.start(timeout=startup_timeout)
self.gdb_output_writer(
f"GDB session started for profile '{profile_name}'.\n"
)
self._log_event("GDB session started.", False)
# Non loggare "GDB session started" qui, lo faremo dopo il check dei simboli.
# --- NUOVO: Controllo simboli e interruzione se necessario ---
if not self.gdb_session.symbols_found:
msg = (f"Error for profile '{profile_name}': No debugging symbols found in "
f"'{os.path.basename(target_exe)}'. Profile execution aborted.")
self._log_event(msg, True) # Invia alla GUI
self.profile_execution_summary["status"] = "Error: No Debug Symbols"
self.profile_execution_summary["end_time"] = datetime.now().isoformat() # Imposta end_time
self.profile_execution_summary["execution_log"] = self.execution_event_log # Salva log eventi finora
# Prepara la directory di output (anche se l'esecuzione è abortita, per salvare il sommario)
# Se `actions` non è vuoto, prendi la dir dalla prima azione, altrimenti usa un default.
base_output_dir = "."
if actions and "output_directory" in actions[0]:
base_output_dir = actions[0].get("output_directory", ".")
self.current_run_output_path = self._prepare_output_directory(base_output_dir, profile_name)
# Se current_run_output_path è None (fallimento creazione dir), _finalize_summary_report lo gestirà.
self._cleanup_session() # Chiudi GDB
self._finalize_summary_report(self.current_run_output_path) # Salva sommario
self.is_running = False
return # Interrompi l'esecuzione del profilo
# --- FINE NUOVO CONTROLLO SIMBOLI ---
self._log_event("GDB session started.", False) # Ora logghiamo dopo il check dei simboli
if gdb_script_path and self.gdb_session.gdb_script_sourced_successfully:
self._log_event("GDB dumper script sourced successfully.", False)
elif gdb_script_path:
self._log_event("Warning: GDB dumper script failed to load.", False)
self._log_event("GDB dumper script sourced successfully.", False)
elif gdb_script_path: # Implica fallimento sourcing
self._log_event(f"Warning: GDB dumper script '{os.path.basename(gdb_script_path)}' failed to load.", False)
# Potremmo anche considerare di interrompere qui se lo script è critico, ma per ora solo un warning.
# --- PHASE 1: Set all breakpoints and build maps ---
cmd_timeout = self._get_setting("timeouts", "gdb_command", 30)
num_successfully_mapped_breakpoints = 0
for action_idx, action_config in enumerate(actions):
if self._stop_requested:
break
bp_spec = action_config.get("breakpoint_location")
action_summary = self.profile_execution_summary["actions_summary"][
action_idx
]
# Prepara la directory di output ora che sappiamo che i simboli ci sono (o si procede comunque)
first_action_output_dir_base = actions[0].get("output_directory", ".")
self.current_run_output_path = self._prepare_output_directory(first_action_output_dir_base, profile_name)
if not bp_spec:
self._log_event(
f"Action {action_idx + 1}: No breakpoint location. Skipping.",
False,
)
action_summary["status"] = "Skipped (No BP Spec)"
continue
self._log_event(
f"Setting BP for Action {action_idx + 1} ('{bp_spec}')...", False
)
bp_set_output = self.gdb_session.set_breakpoint(
bp_spec, timeout=cmd_timeout
)
self.gdb_output_writer(bp_set_output)
parsed_bp_info = self._parse_gdb_set_breakpoint_output(bp_set_output)
if parsed_bp_info:
gdb_bp_num, address_str = parsed_bp_info
action_summary["gdb_bp_num_assigned"] = gdb_bp_num
action_summary["address_resolved"] = address_str
self.gdb_bp_num_to_details_map[gdb_bp_num] = {
"address": address_str,
"action_index": action_idx,
"bp_spec": bp_spec,
}
if (
address_str != "pending"
): # Only map non-pending to address map for execution
if address_str not in self.address_to_action_indices_map:
self.address_to_action_indices_map[address_str] = []
if (
action_idx
not in self.address_to_action_indices_map[address_str]
): # Avoid duplicates if GDB maps same BP spec multiple times (unlikely)
self.address_to_action_indices_map[address_str].append(
action_idx
)
self._log_event(
f"Action {action_idx+1} ('{bp_spec}'): GDB BP {gdb_bp_num} at {address_str}.",
False,
)
num_successfully_mapped_breakpoints += 1
else:
self._log_event(
f"Action {action_idx+1} ('{bp_spec}'): GDB BP {gdb_bp_num} is PENDING. Will not trigger until resolved.",
False,
)
action_summary["status"] = "Pending in GDB"
else:
self._log_event(
f"Error: Action {action_idx + 1}: Failed to parse GDB BP info for '{bp_spec}'. Output: {bp_set_output[:100]}",
True,
)
action_summary["status"] = "Error (BP Set/Parse)"
if self._stop_requested:
raise InterruptedError("User requested stop during BP setup.")
if num_successfully_mapped_breakpoints == 0:
self._log_event(
"No non-pending breakpoints successfully mapped. Aborting profile.",
True,
)
self.profile_execution_summary["status"] = "Error: No BPs Mapped"
self._cleanup_session() # Ensure GDB is closed
self._finalize_summary_report(self.current_run_output_path)
if not self.current_run_output_path: # Se la creazione della directory fallisce
self.profile_execution_summary["status"] = "Error: Cannot create output directory"
self.profile_execution_summary["end_time"] = datetime.now().isoformat()
self._cleanup_session()
self._finalize_summary_report(self.current_run_output_path) # Prova a salvare il sommario se possibile
self.is_running = False
return
# --- PHASE 2: Run program and handle breakpoint hits ---
program_params = self.profile.get("program_parameters", "")
self._log_event(
f"Running program '{os.path.basename(target_exe)} {program_params}'...",
True,
)
run_timeout = self._get_setting("timeouts", "program_run_continue", 120)
gdb_output = self.gdb_session.run_program(
program_params, timeout=run_timeout
)
self.gdb_output_writer(gdb_output)
# --- FASE 1: Imposta tutti i breakpoint e costruisci le mappe ---
# ... (resto della logica per impostare BP, eseguire, ecc.)
cmd_timeout = self._get_setting("timeouts", "gdb_command", 30)
# ... (il resto della funzione run() rimane invariato da qui in poi) ...
program_has_exited = (
"Program exited normally" in gdb_output
or "exited with code" in gdb_output
)
if program_has_exited:
self._log_event(
f"Program exited on initial run. Output: {gdb_output[:100]}", True
)
# Main event loop
while (
self.gdb_session.is_alive()
and not program_has_exited
and not self._stop_requested
):
hit_gdb_bp_num = self._parse_breakpoint_hit_output(gdb_output)
# NEW: Try to get current PC if no direct BP number is parsed, or to confirm address
current_pc_address: Optional[str] = None
if (
self.gdb_session
and self.gdb_session.is_alive()
and not program_has_exited
):
try:
# This is an extra command to GDB, use with caution if performance is critical
# For now, it helps resolve the actual stopping address.
pc_out = self.gdb_session.send_cmd(
"p/x $pc", expect_prompt=True, timeout=cmd_timeout
)
self.gdb_output_writer(f"$pc query: {pc_out}\n")
pc_match = re.search(r"=\s*(0x[0-9a-fA-F]+)", pc_out)
if pc_match:
current_pc_address = pc_match.group(1).lower()
self._log_event(f"Current PC: {current_pc_address}", False)
except Exception as e_pc:
self._log_event(f"Could not get current PC: {e_pc}", False)
actions_to_process_at_this_stop: List[int] = []
hit_bp_details_for_log = "N/A"
if (
hit_gdb_bp_num is not None
and hit_gdb_bp_num in self.gdb_bp_num_to_details_map
):
# GDB reported a direct BP number hit
bp_details = self.gdb_bp_num_to_details_map[hit_gdb_bp_num]
address_of_hit = bp_details["address"]
hit_bp_details_for_log = f"GDB BP {hit_gdb_bp_num} ('{bp_details['bp_spec']}') at {address_of_hit}"
if (
address_of_hit != "pending"
and address_of_hit in self.address_to_action_indices_map
):
actions_to_process_at_this_stop.extend(
self.address_to_action_indices_map[address_of_hit]
)
elif (
current_pc_address
and current_pc_address in self.address_to_action_indices_map
):
# Stopped at a known address, even if GDB didn't report a specific BP number we parsed
actions_to_process_at_this_stop.extend(
self.address_to_action_indices_map[current_pc_address]
)
hit_bp_details_for_log = (
f"PC {current_pc_address} (mapped to actions)"
)
if actions_to_process_at_this_stop:
self._log_event(
f"Processing stop at {hit_bp_details_for_log}.", True
)
# Process all actions mapped to this address/hit
# Deduplicate action indices in case of multiple GDB BPs mapping to same address and action
unique_action_indices_to_process = sorted(
list(set(actions_to_process_at_this_stop))
)
should_continue_after_all_these_actions = True # Default
for action_idx in unique_action_indices_to_process:
if self._stop_requested:
break
current_action_config = actions[action_idx]
action_summary = self.profile_execution_summary[
"actions_summary"
][action_idx]
# Check if this action was already completed (e.g. if multiple GDB BPs mapped to it)
if action_summary["status"].startswith("Completed"):
self._log_event(
f"Action {action_idx + 1} ('{current_action_config.get('breakpoint_location')}') already completed. Skipping.",
False,
)
if not current_action_config.get(
"continue_after_dump", True
):
should_continue_after_all_these_actions = (
False # If one says stop, we stop
)
continue
self._log_event(
f"Executing Action {action_idx + 1} ('{current_action_config.get('breakpoint_location')}')...",
False,
)
action_summary["status"] = "Processing Dumps"
# ... (dumping logic for variables in current_action_config - same as before)
vars_to_dump_for_action = current_action_config.get(
"variables_to_dump", []
)
filename_pattern = current_action_config.get(
"filename_pattern",
"{breakpoint}_{variable}_{timestamp}.{format}",
)
output_format_for_action = current_action_config.get(
"output_format", "json"
).lower()
bp_spec_for_file = current_action_config.get(
"breakpoint_location", "unknown_bp"
)
dump_success_count = 0
for var_name in vars_to_dump_for_action:
# ... (dumping and saving logic for each var)
# Make sure to use bp_spec_for_file in _add_produced_file_entry and _generate_output_filename
# ...
dump_timeout = self._get_setting(
"timeouts", "dump_variable", 60
)
dumped_data = None
file_save_path = ""
dump_status_msg = "Failed"
dump_details_msg = ""
if (
not self.gdb_session.gdb_script_sourced_successfully
and output_format_for_action == "json"
):
msg = f"Dumper script unavailable for '{var_name}' (JSON)."
self._log_event(msg, False)
dump_details_msg = msg
self.json_data_handler(
{
"_profile_executor_error": msg,
"variable": var_name,
}
)
else:
dumped_data = self.gdb_session.dump_variable_to_json(
var_name, timeout=dump_timeout
)
self.json_data_handler(dumped_data)
if (
isinstance(dumped_data, dict)
and "_gdb_tool_error" in dumped_data
):
err_detail = dumped_data.get(
"details", dumped_data["_gdb_tool_error"]
)
self._log_event(
f"Error dumping '{var_name}': {err_detail}",
False,
)
dump_details_msg = f"GDB Tool Error: {err_detail}"
elif dumped_data is not None:
output_filename = self._generate_output_filename(
filename_pattern,
profile_name,
bp_spec_for_file,
var_name,
output_format_for_action,
)
file_save_path = os.path.join(
self.current_run_output_path, output_filename
)
try:
if output_format_for_action == "json":
save_data_to_json_file(
dumped_data, file_save_path
)
elif output_format_for_action == "csv":
data_for_csv = (
dumped_data # Adapt as before
)
if isinstance(
data_for_csv, dict
) and not isinstance(data_for_csv, list):
data_for_csv = [data_for_csv] # etc.
save_data_to_csv_file(
data_for_csv, file_save_path
)
else:
raise ValueError(
f"Unsupported format: {output_format_for_action}"
)
self._log_event(
f"Saved '{var_name}' to '{output_filename}'.",
False,
)
dump_status_msg = "Success"
dump_success_count += 1
except Exception as save_e:
self._log_event(
f"Error saving dump of '{var_name}': {save_e}",
False,
)
dump_details_msg = f"Save Error: {save_e}"
else:
self._log_event(
f"Dump of '{var_name}' returned no data.", False
)
dump_details_msg = "Dump returned no data"
self._add_produced_file_entry(
bp_spec_for_file,
var_name,
file_save_path,
dump_status_msg,
gdb_bp_num=hit_gdb_bp_num,
address=current_pc_address,
details=dump_details_msg,
)
action_summary["variables_dumped_count"] = dump_success_count
if (
dump_success_count == len(vars_to_dump_for_action)
and vars_to_dump_for_action
):
action_summary["status"] = "Completed"
elif not vars_to_dump_for_action:
action_summary["status"] = "Completed (No Vars)"
else:
action_summary["status"] = "Completed with Errors"
if not current_action_config.get("continue_after_dump", True):
should_continue_after_all_these_actions = False
if self._stop_requested:
break
if should_continue_after_all_these_actions:
self._log_event(
f"Continuing after processing actions at {hit_bp_details_for_log}...",
True,
)
gdb_output = self.gdb_session.continue_execution(
timeout=run_timeout
)
self.gdb_output_writer(gdb_output)
if (
"Program exited normally" in gdb_output
or "exited with code" in gdb_output
):
program_has_exited = True
self._log_event(
f"Program exited after continue. Output: {gdb_output[:100]}",
True,
)
else:
self._log_event(
f"Execution halted after processing actions at {hit_bp_details_for_log} as per profile.",
True,
)
program_has_exited = (
True # Treat as if program ended for the profile
)
elif (
"Program exited normally" in gdb_output
or "exited with code" in gdb_output
):
program_has_exited = True
self._log_event(f"Program exited. Output: {gdb_output[:100]}", True)
elif "received signal" in gdb_output.lower():
program_has_exited = True
self._log_event(
f"Program received signal. Output: {gdb_output[:100]}", True
)
self.profile_execution_summary["status"] = (
"Completed (Program Signalled/Crashed)"
)
else:
self._log_event(
f"GDB unresponsive or no recognized output after previous step. Output: {gdb_output[:200]}",
True,
)
program_has_exited = True # Assume cannot proceed
if program_has_exited:
break # Exit while loop
# After loop summary status update
final_status = "Completed"
if program_has_exited and not self._stop_requested:
# Check if any actions are still pending (implies program exited before all BPs were hit)
if any(
s["status"] == "Pending" or s["status"] == "Pending in GDB"
for s in self.profile_execution_summary["actions_summary"]
):
final_status = "Completed (Program Exited Prematurely)"
# Preserve crash status if set
if self.profile_execution_summary["status"] not in [
"Initialized",
"Error: No BPs Mapped",
]: # if not already an error
if (
"Crashed" in self.profile_execution_summary["status"]
or "Signalled" in self.profile_execution_summary["status"]
):
pass # Keep the more specific crash status
else:
self.profile_execution_summary["status"] = final_status
elif self._stop_requested:
self.profile_execution_summary["status"] = "Completed (User Stopped)"
elif (
not (self.gdb_session and self.gdb_session.is_alive())
and not program_has_exited
):
self.profile_execution_summary["status"] = (
"Error: GDB Died Unexpectedly"
)
self._log_event(
"Error: GDB session died unexpectedly during execution.", True
)
else: # Loop finished, all actions processed or halted by continue=false
self.profile_execution_summary["status"] = (
"Completed (All Actions Processed or Halted by Profile)"
)
except InterruptedError as ie: # Custom for user stop
# ... (gestione eccezioni e blocco finally rimangono invariati) ...
except InterruptedError as ie: # Custom for user stop
self.profile_execution_summary["status"] = "Interrupted (User Stop)"
self._log_event(str(ie), True)
except FileNotFoundError as fnf_e: # ... (standard error handling)
except FileNotFoundError as fnf_e:
msg = f"Error running profile '{profile_name}': File not found - {fnf_e}"
self._log_event(msg, True)
self.profile_execution_summary["status"] = f"Error: {fnf_e}"
self._log_event(msg, True); self.profile_execution_summary["status"] = f"Error: {fnf_e}"
except (ConnectionError, TimeoutError) as session_e:
msg = f"Session error running profile '{profile_name}': {type(session_e).__name__} - {session_e}"
self._log_event(msg, True)
self.profile_execution_summary["status"] = f"Error: {session_e}"
self._log_event(msg, True); self.profile_execution_summary["status"] = f"Error: {session_e}"
except Exception as e:
msg = f"Unexpected error running profile '{profile_name}': {type(e).__name__} - {e}"
self._log_event(msg, True)
logger.critical(msg, exc_info=True)
self._log_event(msg, True); logger.critical(msg, exc_info=True)
self.profile_execution_summary["status"] = f"Critical Error: {e}"
finally:
self._cleanup_session()
self.profile_execution_summary["end_time"] = datetime.now().isoformat()
self.profile_execution_summary["execution_log"] = self.execution_event_log
self.profile_execution_summary["files_produced_detailed"] = (
self.produced_files_log
)
# Assicurati che end_time e altri campi del sommario siano impostati anche se c'è un return anticipato
if self.profile_execution_summary.get("end_time") is None:
self.profile_execution_summary["end_time"] = datetime.now().isoformat()
if not self.profile_execution_summary.get("execution_log"): # Se non già popolato da un errore precedente
self.profile_execution_summary["execution_log"] = self.execution_event_log
if not self.profile_execution_summary.get("files_produced_detailed"):
self.profile_execution_summary["files_produced_detailed"] = self.produced_files_log
summary_file_path = self._finalize_summary_report(
self.current_run_output_path
)
# Add summary file to produced_files_log AFTER it's written, if successful.
# This needs to be handled carefully to avoid adding it if _finalize_summary_report fails.
# The _finalize_summary_report could internally call _add_produced_file_entry,
# or we add it here based on its return value.
# For now, _finalize_summary_report does not call it to prevent recursion on error.
self._cleanup_session() # Questo ora viene chiamato prima di _finalize_summary_report nel flusso normale
# e anche nei casi di errore sopra.
self._log_event(
f"Profile '{profile_name}' execution cycle finished. Summary report generation attempt at: {summary_file_path if summary_file_path else 'N/A'}.",
True,
)
# Il path del sommario viene determinato e il sommario scritto da _finalize_summary_report
# Se current_run_output_path non è stato creato (es. errore simboli prima della creazione dir),
# _finalize_summary_report non salverà su file specifico ma loggherà il sommario.
summary_file_path = self._finalize_summary_report(self.current_run_output_path)
final_gui_message = (f"Profile '{profile_name}' execution cycle finished. "
f"Status: {self.profile_execution_summary.get('status', 'Unknown')}. "
f"Summary report attempt at: {summary_file_path if summary_file_path else 'N/A (see logs)'}.")
self._log_event(final_gui_message, True)
self.is_running = False
def _finalize_summary_report(self, run_output_path: Optional[str]) -> Optional[str]:

View File

@ -660,155 +660,125 @@ class GDBGui(tk.Tk):
messagebox.showerror("GDB Operation Error", error_message, parent=self)
def _start_gdb_session_action(self):
# ... (implementation as before, including disabling profile controls) ...
if self.profile_executor_instance and self.profile_executor_instance.is_running:
messagebox.showwarning(
"Profile Running",
"An automated profile is running. Please stop it first.",
parent=self,
)
messagebox.showwarning("Profile Running", "An automated profile is running. Please stop it first.", parent=self)
return
gdb_exe = self.app_settings.get_setting("general", "gdb_executable_path")
target_exe = self.exe_path_var.get()
gdb_script = self.app_settings.get_setting("general", "gdb_dumper_script_path")
# ... (validazioni iniziali per gdb_exe, target_exe, etc. rimangono invariate) ...
if not gdb_exe or not os.path.isfile(gdb_exe):
messagebox.showerror(
"Configuration Error",
"GDB executable path is not configured correctly. Please check Options > Configure.",
parent=self,
)
messagebox.showerror("Configuration Error", "GDB executable path is not configured correctly. Please check Options > Configure.", parent=self)
self._check_critical_configs_and_update_gui()
return
if not target_exe:
messagebox.showerror(
"Input Error", "Target executable path is required.", parent=self
)
messagebox.showerror("Input Error", "Target executable path is required.", parent=self)
return
if not os.path.exists(target_exe):
messagebox.showerror(
"File Not Found",
f"Target executable not found: {target_exe}",
parent=self,
)
messagebox.showerror("File Not Found", f"Target executable not found: {target_exe}", parent=self)
return
dumper_script_invalid = False
if gdb_script and not os.path.isfile(gdb_script):
messagebox.showwarning(
"Configuration Warning",
f"GDB dumper script path is set to:\n'{gdb_script}'\nbut the file was not found or is invalid.\n\nJSON dumping via script will be unavailable. You can correct this in Options > Configure.",
parent=self,
)
gdb_script = None
self.gdb_dumper_status_var.set(
f"Dumper: '{self.app_settings.get_setting('general', 'gdb_dumper_script_path')}' (Not Found!)"
)
dumper_script_invalid = True
self.gdb_dumper_status_var.set(f"Dumper: '{self.app_settings.get_setting('general', 'gdb_dumper_script_path')}' (Not Found!)")
if self.gdb_session and self.gdb_session.is_alive():
messagebox.showwarning(
"Session Active",
"A GDB session is already active. Please stop it first.",
parent=self,
)
messagebox.showwarning("Session Active", "A GDB session is already active. Please stop it first.", parent=self)
return
self._update_status_bar("Starting GDB session...")
self._update_gdb_raw_output(
"Attempting to start GDB session...\n", append=False
)
self._update_gdb_raw_output("Attempting to start GDB session...\n", append=False)
self._update_parsed_json_output(None)
try:
startup_timeout = self.app_settings.get_setting("timeouts", "gdb_start", 30)
current_dumper_options = self.app_settings.get_category_settings(
"dumper_options", {}
) # Ensure this is fetched
quit_timeout_on_no_symbols = self.app_settings.get_setting("timeouts", "gdb_quit", 10) # Timeout per quit
current_dumper_options = self.app_settings.get_category_settings("dumper_options", {})
self.gdb_session = GDBSession(
gdb_path=gdb_exe,
executable_path=target_exe,
gdb_script_full_path=gdb_script,
dumper_options=current_dumper_options,
gdb_path=gdb_exe, executable_path=target_exe,
gdb_script_full_path=gdb_script, dumper_options=current_dumper_options
)
self.gdb_session.start(
timeout=startup_timeout
) # This call internally sets gdb_script_sourced_successfully
self.gdb_session.start(timeout=startup_timeout)
self._update_gdb_raw_output(
f"GDB session started for '{os.path.basename(target_exe)}'.\n"
)
# --- NUOVO LOGGING DIAGNOSTICO ---
self._update_gdb_raw_output(f"GDB session started for '{os.path.basename(target_exe)}'.\n")
# Logging diagnostico (mantenuto)
logger.info(f"MAIN_WINDOW_CHECK: gdb_script path = '{gdb_script}'")
if self.gdb_session: # Assicurati che gdb_session esista
logger.info(
f"MAIN_WINDOW_CHECK: self.gdb_session.gdb_script_path (internal) = '{self.gdb_session.gdb_script_path}'"
)
logger.info(
f"MAIN_WINDOW_CHECK: self.gdb_session.gdb_script_sourced_successfully = {self.gdb_session.gdb_script_sourced_successfully}"
)
if self.gdb_session:
logger.info(f"MAIN_WINDOW_CHECK: self.gdb_session.gdb_script_path (internal GDBSession path) = '{self.gdb_session.gdb_script_path}'")
logger.info(f"MAIN_WINDOW_CHECK: self.gdb_session.gdb_script_sourced_successfully = {self.gdb_session.gdb_script_sourced_successfully}")
logger.info(f"MAIN_WINDOW_CHECK: self.gdb_session.symbols_found = {self.gdb_session.symbols_found}")
else:
logger.error(
"MAIN_WINDOW_CHECK: self.gdb_session is None at the point of checking dumper status!"
)
# --- FINE NUOVO LOGGING DIAGNOSTICO ---
logger.error("MAIN_WINDOW_CHECK: self.gdb_session is None at the point of checking dumper/symbols status!")
if (
gdb_script
and self.gdb_session
and self.gdb_session.gdb_script_sourced_successfully
): # Aggiunto check self.gdb_session
self._update_gdb_raw_output(
f"GDB dumper script '{os.path.basename(gdb_script)}' sourced successfully.\n",
append=True,
)
self._update_status_bar(
f"GDB active. Dumper '{os.path.basename(gdb_script)}' loaded."
)
self.gdb_dumper_status_var.set(
f"Dumper: {os.path.basename(gdb_script)} (Loaded)"
)
elif (
gdb_script and self.gdb_session
): # Aggiunto check self.gdb_session, implica che gdb_script_sourced_successfully è False
self._update_gdb_raw_output(
f"Warning: GDB dumper script '{os.path.basename(gdb_script)}' specified but failed to load.\n",
append=True,
)
self._update_status_bar(
f"GDB active. Dumper script issues (check logs).", is_error=True
)
self.gdb_dumper_status_var.set(
f"Dumper: {os.path.basename(gdb_script)} (Load Failed!)"
)
# Mostra il warning solo se la finestra esiste ancora
symbols_ok = self.gdb_session and self.gdb_session.symbols_found
dumper_loaded_successfully = self.gdb_session and self.gdb_session.gdb_script_sourced_successfully
if not symbols_ok:
self._update_gdb_raw_output("ERROR: No debugging symbols found in the executable. GDB session will be terminated.\n", append=True)
# Mostra il warning all'utente
if self.winfo_exists():
messagebox.showwarning(
"Dumper Script Issue",
f"The GDB dumper script '{os.path.basename(gdb_script)}' may have failed to load.\nJSON dumping might be affected. Check logs.",
parent=self,
)
elif not gdb_script and self.gdb_session: # Aggiunto check self.gdb_session
self._update_gdb_raw_output(
"No GDB dumper script. JSON dump via script unavailable.\n",
append=True,
)
self._update_status_bar("GDB session active. No dumper script.")
messagebox.showwarning("No Debug Symbols - Session Aborted",
f"GDB reported no debugging symbols found in:\n{os.path.basename(target_exe)}\n\n"
"The GDB session will be terminated as debugging capabilities are severely limited.",
parent=self)
self._update_status_bar("GDB session aborted: No debug symbols.", is_error=True)
# Termina la sessione GDB e resetta la GUI
if self.gdb_session and self.gdb_session.is_alive():
try:
self.gdb_session.quit(timeout=quit_timeout_on_no_symbols)
except Exception as e_quit:
logger.error(f"Exception during GDB quit (no symbols scenario): {e_quit}")
self.gdb_session = None
self._reset_gui_to_stopped_state()
self._check_critical_configs_and_update_gui() # Per aggiornare lo stato GDB/Dumper
return # Esci dalla funzione _start_gdb_session_action
# Fallback per lo stato del dumper se non è configurato
if not self.app_settings.get_setting("general", "gdb_dumper_script_path"):
self.gdb_dumper_status_var.set("Dumper: Not Configured (Optional).")
# Se siamo qui, i simboli sono stati trovati (symbols_ok è True)
# Procedi con la logica del dumper script
if gdb_script:
if dumper_script_invalid:
self._update_gdb_raw_output(f"Warning: GDB dumper script path '{gdb_script}' is invalid and was not found.\n", append=True)
self._update_status_bar(f"GDB active. Dumper script path invalid.", is_error=True)
self.gdb_dumper_status_var.set(f"Dumper: {os.path.basename(gdb_script)} (Path Invalid!)")
elif dumper_loaded_successfully:
self._update_gdb_raw_output(f"GDB dumper script '{os.path.basename(gdb_script)}' sourced successfully.\n", append=True)
self._update_status_bar(f"GDB active. Dumper '{os.path.basename(gdb_script)}' loaded.")
self.gdb_dumper_status_var.set(f"Dumper: {os.path.basename(gdb_script)} (Loaded)")
else: # path valido, ma caricamento fallito
self._update_gdb_raw_output(f"Warning: GDB dumper script '{os.path.basename(gdb_script)}' specified but FAILED to load correctly.\n", append=True)
self._update_status_bar(f"GDB active. Dumper script load issue (check logs).", is_error=True)
if self.winfo_exists():
messagebox.showwarning("Dumper Script Issue",
f"The GDB dumper script '{os.path.basename(gdb_script)}' may have failed to load correctly.\n"
"JSON dumping might be affected. Check logs.",
parent=self)
self.gdb_dumper_status_var.set(f"Dumper: {os.path.basename(gdb_script)} (Load Failed!)")
elif self.gdb_session: # Nessuno script dumper specificato
self._update_gdb_raw_output("No GDB dumper script specified. JSON dump via script unavailable.\n", append=True)
self._update_status_bar("GDB session active. No dumper script.")
self.gdb_dumper_status_var.set("Dumper: Not Configured (Optional).")
# Abilita i bottoni della GUI dato che i simboli sono OK
self.start_gdb_button.config(state=tk.DISABLED)
self.set_bp_button.config(state=tk.NORMAL)
self.run_button.config(
state=tk.DISABLED, text="3. Run Program"
) # Inizialmente disabilitato fino a BP
self.dump_var_button.config(
state=tk.DISABLED
) # Disabilitato finché non si è a un breakpoint
self.run_button.config(state=tk.DISABLED, text="3. Run Program")
self.dump_var_button.config(state=tk.DISABLED)
self.stop_gdb_button.config(state=tk.NORMAL)
# Disabilita controlli profilo
if hasattr(self, "run_profile_button"):
self.run_profile_button.config(state=tk.DISABLED)
if hasattr(self, "profile_selection_combo"):
self.profile_selection_combo.config(state=tk.DISABLED)
self.program_started_once = False # Resetta per la nuova sessione
if hasattr(self, 'run_profile_button'): self.run_profile_button.config(state=tk.DISABLED)
if hasattr(self, 'profile_selection_combo'): self.profile_selection_combo.config(state=tk.DISABLED)
self.program_started_once = False
self.last_dumped_data = None
self._disable_save_buttons()
@ -816,16 +786,13 @@ class GDBGui(tk.Tk):
self._handle_gdb_operation_error("start session", e_specific)
self.gdb_session = None
self._reset_gui_to_stopped_state()
self._check_critical_configs_and_update_gui()
except Exception as e:
logger.critical(
f"!!! MAIN_WINDOW CATCH-ALL: Unhandled exception type: {type(e).__name__}, message: '{e}'",
exc_info=True,
)
self._handle_gdb_operation_error(
"start session (unexpected from main_window catch-all)", e
)
logger.critical(f"!!! MAIN_WINDOW CATCH-ALL: Unhandled exception type: {type(e).__name__}, message: '{e}'", exc_info=True)
self._handle_gdb_operation_error("start session (unexpected from main_window catch-all)", e)
self.gdb_session = None
self._reset_gui_to_stopped_state()
self._check_critical_configs_and_update_gui() # Resetta la GUI
def _set_gdb_breakpoint_action(self):
# ... (implementation as before)