add analyze function for exe
This commit is contained in:
parent
4aed5cee70
commit
b6db1c8623
@ -76,6 +76,60 @@ class GDBSession:
|
|||||||
if self.dumper_options:
|
if self.dumper_options:
|
||||||
logger.info(f"Dumper options provided: {self.dumper_options}")
|
logger.info(f"Dumper options provided: {self.dumper_options}")
|
||||||
|
|
||||||
|
def get_gdb_version(self, timeout: int = DEFAULT_GDB_OPERATION_TIMEOUT) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Retrieves the GDB version string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
timeout: Timeout for the GDB command.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The GDB version string (typically the first line of 'gdb --version'),
|
||||||
|
or None if an error occurs or version cannot be parsed.
|
||||||
|
"""
|
||||||
|
if not self.child or not self.child.isalive():
|
||||||
|
# Questo metodo potrebbe essere chiamato anche prima che una sessione completa sia "startata"
|
||||||
|
# per l'analisi dei simboli, quindi potremmo dover avviare GDB solo per questo.
|
||||||
|
# Per ora, assumiamo che sia chiamato su una sessione già avviata,
|
||||||
|
# o che il chiamante gestisca l'avvio/chiusura di una sessione temporanea.
|
||||||
|
# In alternativa, potrebbe essere un metodo statico o una funzione helper
|
||||||
|
# che lancia 'gdb --version' come processo separato.
|
||||||
|
# Per coerenza con gli altri metodi, lo lasciamo come metodo d'istanza.
|
||||||
|
# Se la sessione non è 'start()'ata (cioè non c'è un eseguibile caricato),
|
||||||
|
# GDB potrebbe comunque rispondere a 'show version'.
|
||||||
|
logger.warning("GDB session not fully active, attempting 'show version'.")
|
||||||
|
# Se child non esiste, non possiamo fare nulla qui.
|
||||||
|
# Il chiamante (es. ProfileManagerWindow per l'analisi) dovrà gestire
|
||||||
|
# l'avvio di una sessione GDB se necessario.
|
||||||
|
# Questa implementazione assume che self.child esista.
|
||||||
|
if not self.child:
|
||||||
|
logger.error("No GDB child process available to get version.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Usiamo 'show version' che funziona all'interno di una sessione GDB attiva
|
||||||
|
# 'gdb --version' è per l'uso da riga di comando esterna.
|
||||||
|
command = "show version"
|
||||||
|
logger.info(f"Requesting GDB version with command: '{command}'")
|
||||||
|
try:
|
||||||
|
output = self.send_cmd(command, expect_prompt=True, timeout=timeout)
|
||||||
|
# L'output di 'show version' è multiriga. La prima riga è di solito quella che vogliamo.
|
||||||
|
# Esempio:
|
||||||
|
# GNU gdb (GDB) 16.2
|
||||||
|
# Copyright (C) 2024 Free Software Foundation, Inc.
|
||||||
|
# ...
|
||||||
|
if output:
|
||||||
|
first_line = output.splitlines()[0].strip()
|
||||||
|
logger.info(f"GDB version string: {first_line}")
|
||||||
|
return first_line
|
||||||
|
logger.warning("No output received for 'show version' command.")
|
||||||
|
return None
|
||||||
|
except (ConnectionError, TimeoutError) as e:
|
||||||
|
logger.error(f"Error getting GDB version: {e}", exc_info=True)
|
||||||
|
return None
|
||||||
|
except Exception as e_parse:
|
||||||
|
logger.error(f"Error parsing 'show version' output: {e_parse}", exc_info=True)
|
||||||
|
return None
|
||||||
|
|
||||||
def start(self, timeout: int = DEFAULT_GDB_OPERATION_TIMEOUT) -> None:
|
def start(self, timeout: int = DEFAULT_GDB_OPERATION_TIMEOUT) -> None:
|
||||||
command = f'"{self.gdb_path}" --nx --quiet "{self.executable_path}"'
|
command = f'"{self.gdb_path}" --nx --quiet "{self.executable_path}"'
|
||||||
logger.info(f"Spawning GDB process: {command} with startup timeout: {timeout}s")
|
logger.info(f"Spawning GDB process: {command} with startup timeout: {timeout}s")
|
||||||
@ -140,6 +194,8 @@ class GDBSession:
|
|||||||
def list_functions(self, regex_filter: Optional[str] = None, timeout: int = DEFAULT_GDB_OPERATION_TIMEOUT) -> List[str]:
|
def list_functions(self, regex_filter: Optional[str] = None, timeout: int = DEFAULT_GDB_OPERATION_TIMEOUT) -> List[str]:
|
||||||
"""
|
"""
|
||||||
Lists functions known to GDB, optionally filtered by a regex.
|
Lists functions known to GDB, optionally filtered by a regex.
|
||||||
|
(Implementazione precedente di list_functions è già abbastanza buona, la riporto qui per completezza
|
||||||
|
assicurandoci che sia allineata con le necessità)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
regex_filter: Optional regex to filter function names.
|
regex_filter: Optional regex to filter function names.
|
||||||
@ -159,109 +215,83 @@ class GDBSession:
|
|||||||
logger.info(f"Requesting GDB function list with command: '{command}'")
|
logger.info(f"Requesting GDB function list with command: '{command}'")
|
||||||
functions: List[str] = []
|
functions: List[str] = []
|
||||||
try:
|
try:
|
||||||
|
# Assicurarsi che la paginazione sia disattivata è gestito in start()
|
||||||
output = self.send_cmd(command, expect_prompt=True, timeout=timeout)
|
output = self.send_cmd(command, expect_prompt=True, timeout=timeout)
|
||||||
# L'output di 'info functions' può essere complesso.
|
|
||||||
# Esempio:
|
|
||||||
# File my_source.cpp:
|
|
||||||
# 123: void MyClass::myMethod(int);
|
|
||||||
# 456: int anotherFunction();
|
|
||||||
# Non-debugging symbols:
|
|
||||||
# 0x00401000 _start
|
|
||||||
# 0x004010a0 __do_global_dtors_aux
|
|
||||||
#
|
|
||||||
# Cerchiamo di estrarre nomi di funzioni che sembrano validi identificatori C/C++
|
|
||||||
# Questo regex cerca identificatori che possono includere :: e <template parametri>,
|
|
||||||
# spesso terminanti con ( o a volte solo il nome se è un simbolo non-debugging.
|
|
||||||
# È un'euristica e potrebbe aver bisogno di affinamenti.
|
|
||||||
# Prioritizziamo le linee che iniziano con numeri di riga o nomi di file.
|
|
||||||
|
|
||||||
potential_function_lines = []
|
potential_function_lines = []
|
||||||
current_file_context = None
|
current_file_context = None
|
||||||
|
# Flag per indicare se siamo nella sezione "Non-debugging symbols"
|
||||||
|
in_non_debugging_symbols_section = False
|
||||||
|
|
||||||
for line in output.splitlines():
|
for line in output.splitlines():
|
||||||
line_strip = line.strip()
|
line_strip = line.strip()
|
||||||
if not line_strip:
|
if not line_strip:
|
||||||
|
in_non_debugging_symbols_section = False # Una riga vuota potrebbe resettare la sezione
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Cattura il contesto del file, se presente
|
if line_strip.startswith("All defined functions"): # Ignora questa intestazione comune
|
||||||
|
continue
|
||||||
|
if line_strip.startswith("File "): # Resetta contesto non-debug se incontriamo un nuovo file
|
||||||
|
in_non_debugging_symbols_section = False
|
||||||
file_match = re.match(r"File\s+(.+):", line_strip)
|
file_match = re.match(r"File\s+(.+):", line_strip)
|
||||||
if file_match:
|
if file_match:
|
||||||
current_file_context = file_match.group(1).strip()
|
current_file_context = file_match.group(1).strip()
|
||||||
logger.debug(f"Function parsing context: File '{current_file_context}'")
|
logger.debug(f"Function parsing context: File '{current_file_context}'")
|
||||||
continue # Passa alla riga successiva
|
|
||||||
|
|
||||||
# Ignora le sezioni "Non-debugging symbols" per ora, a meno che non si vogliano includere
|
|
||||||
if line_strip.startswith("Non-debugging symbols:") or \
|
|
||||||
(line_strip.startswith("0x") and " " in line_strip and not line_strip.endswith(";") and not line_strip.endswith(")")): # Heuristica per simboli non-debugging
|
|
||||||
logger.debug(f"Skipping non-debugging symbol line: {line_strip}")
|
|
||||||
current_file_context = None # Resetta contesto file se entriamo in questa sezione
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Regex per estrarre il nome della funzione da righe tipo "123: void MyNamespace::MyClass<Template>::Method(int);"
|
if line_strip.startswith("Non-debugging symbols:"):
|
||||||
# o "ReturnType funcName(params)"
|
in_non_debugging_symbols_section = True
|
||||||
# Questo cerca qualcosa che assomigli a un nome di funzione prima di una parentesi aperta
|
logger.debug("Entering Non-debugging symbols section.")
|
||||||
# o prima di un punto e virgola se non ci sono parametri visibili.
|
continue
|
||||||
# Il nome può contenere '::', '<', '>', '_', alfanumerici.
|
|
||||||
# ^\s*(?:\w+\s+)?([a-zA-Z_][\w:<>()]*?(?:::[a-zA-Z_][\w:<>()]*?)*)\s*\(.*?\);?$
|
|
||||||
# ^\s*(?:[^\s]+\s+)*([a-zA-Z_][\w:<>()~*\s&-]*?(?:::[a-zA-Z_][\w:<>()~*\s&-]*?)*)\s*\(
|
|
||||||
# Il seguente è un tentativo più semplice e robusto
|
|
||||||
# Cerca un identificatore valido seguito da una parentesi aperta '(',
|
|
||||||
# opzionalmente preceduto da tipo di ritorno e numero di riga.
|
|
||||||
# Pattern per identificatore C++: ([a-zA-Z_]\w*(::[a-zA-Z_]\w*)*(<[^>]*>)?(?:\s*const)?)
|
|
||||||
# Regex per matchare: opzionale(numero_riga: tipo_ritorno) NOME_FUNZIONE (parametri) ;
|
|
||||||
# Difficile fare un regex perfetto. Proviamo un approccio più semplice per ora:
|
|
||||||
# cerca un nome valido seguito da '('.
|
|
||||||
|
|
||||||
# Tentativo 1: Estrarre da linee con numero di riga e tipo
|
# Se siamo nella sezione non-debugging, i simboli sono spesso solo indirizzo e nome
|
||||||
# es: "123: void MyClass::myMethod(int);"
|
if in_non_debugging_symbols_section:
|
||||||
m = re.match(r"^\s*\d+:\s+(?:[\w\s:*&<>~]+\s+)?([a-zA-Z_][\w:<>\s~*&-]*?(?:::[a-zA-Z_][\w:<>\s~*&-]*?)*)\s*\(", line_strip)
|
# Esempio: 0x00401000 _start
|
||||||
if m:
|
m_non_debug = re.match(r"^\s*0x[0-9a-fA-F]+\s+([a-zA-Z_][\w:<>\.~]*)", line_strip)
|
||||||
func_name = m.group(1).strip()
|
if m_non_debug:
|
||||||
# Rimuovi eventuali spazi extra o qualificatori 'const' alla fine se catturati per errore
|
func_name = m_non_debug.group(1)
|
||||||
func_name = re.sub(r'\s+const\s*$', '', func_name)
|
if func_name not in functions:
|
||||||
if func_name not in functions: # Evita duplicati se GDB è verboso
|
|
||||||
functions.append(func_name)
|
functions.append(func_name)
|
||||||
logger.debug(f"Found function (type 1): {func_name}")
|
logger.debug(f"Found non-debugging symbol/function: {func_name}")
|
||||||
continue
|
continue # Processa la prossima riga
|
||||||
|
|
||||||
# Tentativo 2: Linee che iniziano direttamente con il nome della funzione o tipo di ritorno
|
|
||||||
# es: "int main()" o "MyClass::MyClass()"
|
# Pattern per simboli di debug (più strutturati)
|
||||||
# ([a-zA-Z_][\w:<>\s~*&-]*?(?:::[a-zA-Z_][\w:<>\s~*&-]*?)*)\s*\(
|
# Tentativo 1: "numero_riga: [tipo_ritorno] nome_funzione(parametri);"
|
||||||
m2 = re.match(r"^\s*(?:[\w\s:*&<>~]+\s+)?([a-zA-Z_][\w:<>\s~*&-]*?(?:::[a-zA-Z_][\w:<>\s~*&-]*?)*)\s*\(", line_strip)
|
m_debug_line = re.match(r"^\s*\d+:\s+(?:[\w\s:*&<>~\[\]]+\s+)?([a-zA-Z_][\w:<>\s~*&\-\[\]]*?(?:::[a-zA-Z_][\w:<>\s~*&\-\[\]]*?)*)\s*\(", line_strip)
|
||||||
if m2:
|
if m_debug_line:
|
||||||
func_name = m2.group(1).strip()
|
func_name = m_debug_line.group(1).strip()
|
||||||
func_name = re.sub(r'\s+const\s*$', '', func_name)
|
func_name = re.sub(r'\s+const\s*$', '', func_name).strip() # Rimuovi 'const' alla fine e spazi
|
||||||
if func_name and func_name not in functions: # Assicurati che non sia vuoto
|
if func_name and func_name not in functions:
|
||||||
functions.append(func_name)
|
functions.append(func_name)
|
||||||
logger.debug(f"Found function (type 2): {func_name}")
|
logger.debug(f"Found function (debug, type 1): {func_name}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# A volte 'info functions' lista solo il nome, specialmente per simboli senza info complete
|
# Tentativo 2: "[tipo_ritorno] nome_funzione(parametri)" (senza numero riga)
|
||||||
# Es. "_ZN12MyNamespace8MyClassILi1EE11anotherFuncEv" (mangled)
|
m_debug_no_line = re.match(r"^\s*(?:[\w\s:*&<>~\[\]]+\s+)?([a-zA-Z_][\w:<>\s~*&\-\[\]]*?(?:::[a-zA-Z_][\w:<>\s~*&\-\[\]]*?)*)\s*\(", line_strip)
|
||||||
# O simboli semplici come "main" su una riga a sé se non c'è file context
|
if m_debug_no_line:
|
||||||
# Questo è più rischioso perché potrebbe catturare non-funzioni.
|
func_name = m_debug_no_line.group(1).strip()
|
||||||
# Lo attiviamo solo se non c'è un contesto di file attivo (per evitare di prendere nomi di file come funzioni)
|
func_name = re.sub(r'\s+const\s*$', '', func_name).strip()
|
||||||
# e se la linea sembra un identificatore C++ valido.
|
if func_name and func_name not in functions:
|
||||||
if not current_file_context and re.match(r"^[a-zA-Z_][\w:<>\.~]*$", line_strip) and '(' not in line_strip and ';' not in line_strip:
|
# Evita di aggiungere tipi o parole chiave come funzioni
|
||||||
# Evita di aggiungere cose che sono chiaramente tipi o altro
|
if not (func_name in ["void", "int", "char", "short", "long", "float", "double", "bool",
|
||||||
if "::" in line_strip or line_strip.startswith("_Z") or (not any(c.islower() for c in line_strip) and len(line_strip) > 4): # Heuristica per nomi mangled o solo maiuscole (costanti?)
|
"class", "struct", "enum", "union", "typename", "template"] or func_name.endswith("operator")):
|
||||||
if line_strip not in functions:
|
functions.append(func_name)
|
||||||
functions.append(line_strip)
|
logger.debug(f"Found function (debug, type 2): {func_name}")
|
||||||
logger.debug(f"Found potential symbol (type 3): {line_strip}")
|
continue
|
||||||
|
|
||||||
|
if functions:
|
||||||
if not functions and output: # Se non abbiamo trovato nulla con regex ma c'era output
|
|
||||||
logger.warning(f"Could not parse function names reliably from 'info functions' output. Output was:\n{output}")
|
|
||||||
elif functions:
|
|
||||||
logger.info(f"Successfully parsed {len(functions)} function names.")
|
logger.info(f"Successfully parsed {len(functions)} function names.")
|
||||||
functions.sort() # Ordina per una visualizzazione migliore
|
functions.sort()
|
||||||
|
elif output: # C'era output ma non abbiamo parsato nulla
|
||||||
|
logger.warning(f"Could not parse any function names from 'info functions' output, though output was received. First 200 chars of output:\n{output[:200]}")
|
||||||
|
|
||||||
except (ConnectionError, TimeoutError) as e:
|
except (ConnectionError, TimeoutError) as e:
|
||||||
logger.error(f"Error listing functions from GDB: {e}", exc_info=True)
|
logger.error(f"Error listing functions from GDB: {e}", exc_info=True)
|
||||||
return [] # Ritorna lista vuota in caso di errore di comunicazione
|
return []
|
||||||
except Exception as e_parse:
|
except Exception as e_parse:
|
||||||
logger.error(f"Error parsing 'info functions' output: {e_parse}", exc_info=True)
|
logger.error(f"Error parsing 'info functions' output: {e_parse}", exc_info=True)
|
||||||
return [] # Ritorna lista vuota in caso di errore di parsing
|
return []
|
||||||
|
|
||||||
return functions
|
return functions
|
||||||
|
|
||||||
|
|||||||
@ -65,21 +65,22 @@ class ActionEditorWindow(tk.Toplevel):
|
|||||||
def __init__(self, parent: tk.Widget,
|
def __init__(self, parent: tk.Widget,
|
||||||
action_data: Optional[Dict[str, Any]] = None,
|
action_data: Optional[Dict[str, Any]] = None,
|
||||||
is_new: bool = True,
|
is_new: bool = True,
|
||||||
target_executable_path: Optional[str] = None, # NUOVO PARAMETRO
|
target_executable_path: Optional[str] = None,
|
||||||
app_settings: Optional[AppSettings] = None): # NUOVO PARAMETRO
|
app_settings: Optional[AppSettings] = None,
|
||||||
|
symbol_analysis_data: Optional[Dict[str, Any]] = None): # NUOVO PARAMETRO
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.parent_window = parent
|
self.parent_window = parent
|
||||||
self.is_new_action = is_new
|
self.is_new_action = is_new
|
||||||
self.result: Optional[Dict[str, Any]] = None
|
self.result: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
# --- NUOVI ATTRIBUTI ---
|
|
||||||
self.target_executable_path = target_executable_path
|
self.target_executable_path = target_executable_path
|
||||||
self.app_settings = app_settings # Necessario per gdb_path, timeouts
|
self.app_settings = app_settings
|
||||||
# --- FINE NUOVI ATTRIBUTI ---
|
self.symbol_analysis_data = symbol_analysis_data # NUOVO ATTRIBUTO
|
||||||
|
|
||||||
|
# ... (resto dell'init come prima) ...
|
||||||
title = "Add New Action" if self.is_new_action else "Edit Action"
|
title = "Add New Action" if self.is_new_action else "Edit Action"
|
||||||
self.title(title)
|
self.title(title)
|
||||||
self.geometry("650x580") # Aumentato leggermente per il nuovo bottone
|
self.geometry("650x580") # Potrebbe servire aggiustare
|
||||||
self.resizable(False, False)
|
self.resizable(False, False)
|
||||||
|
|
||||||
self.transient(parent)
|
self.transient(parent)
|
||||||
@ -256,6 +257,28 @@ class ActionEditorWindow(tk.Toplevel):
|
|||||||
|
|
||||||
# --- NUOVA FUNZIONE ---
|
# --- NUOVA FUNZIONE ---
|
||||||
def _browse_functions(self) -> None:
|
def _browse_functions(self) -> None:
|
||||||
|
# --- LOGICA AGGIORNATA ---
|
||||||
|
functions_to_show: List[str] = []
|
||||||
|
source_of_functions = "live GDB query" # Default
|
||||||
|
|
||||||
|
# 1. Prova a usare i dati di analisi dei simboli pre-calcolati, se validi
|
||||||
|
if self.symbol_analysis_data and isinstance(self.symbol_analysis_data, dict):
|
||||||
|
# Verifica che i dati di analisi siano per l'eseguibile corrente
|
||||||
|
# (confronto semplice del path, idealmente si usa checksum/timestamp in futuro)
|
||||||
|
analyzed_exe = self.symbol_analysis_data.get("analyzed_executable_path")
|
||||||
|
if analyzed_exe and os.path.normpath(analyzed_exe) == os.path.normpath(str(self.target_executable_path)): # Confronta path normalizzati
|
||||||
|
cached_functions = self.symbol_analysis_data.get("symbols", {}).get("functions", [])
|
||||||
|
if isinstance(cached_functions, list) and cached_functions: # Se abbiamo funzioni cachate
|
||||||
|
functions_to_show = cached_functions
|
||||||
|
source_of_functions = "cached analysis"
|
||||||
|
logger.info(f"Using {len(functions_to_show)} cached functions for browsing.")
|
||||||
|
else:
|
||||||
|
logger.info("Cached symbol analysis is for a different executable or path mismatch. Will perform live query if target is valid.")
|
||||||
|
if analyzed_exe: logger.debug(f"Analyzed exe: '{analyzed_exe}', Current target: '{self.target_executable_path}'")
|
||||||
|
|
||||||
|
|
||||||
|
# 2. Se non ci sono funzioni cachate valide, esegui una query live a GDB
|
||||||
|
if not functions_to_show:
|
||||||
if not self.target_executable_path or not os.path.isfile(self.target_executable_path):
|
if not self.target_executable_path or not os.path.isfile(self.target_executable_path):
|
||||||
messagebox.showerror("Error", "Target executable for the profile is not set or invalid. Cannot browse functions.", parent=self)
|
messagebox.showerror("Error", "Target executable for the profile is not set or invalid. Cannot browse functions.", parent=self)
|
||||||
return
|
return
|
||||||
@ -268,68 +291,40 @@ class ActionEditorWindow(tk.Toplevel):
|
|||||||
messagebox.showerror("GDB Error", f"GDB executable not found or not configured: {gdb_exe_path}", parent=self)
|
messagebox.showerror("GDB Error", f"GDB executable not found or not configured: {gdb_exe_path}", parent=self)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Mostra un messaggio di attesa
|
|
||||||
self.config(cursor="watch")
|
self.config(cursor="watch")
|
||||||
self.update_idletasks()
|
self.update_idletasks()
|
||||||
|
|
||||||
temp_gdb_session: Optional[GDBSession] = None
|
temp_gdb_session: Optional[GDBSession] = None
|
||||||
functions_list: List[str] = []
|
live_query_error = False
|
||||||
error_occurred = False
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"Creating temporary GDBSession for function listing. Target: {self.target_executable_path}")
|
logger.info(f"Performing live GDB query for functions. Target: {self.target_executable_path}")
|
||||||
# Creiamo una sessione GDBSession senza script dumper e senza opzioni dumper,
|
temp_gdb_session = GDBSession(gdb_exe_path, self.target_executable_path, None, {})
|
||||||
# serve solo per 'info functions'.
|
startup_timeout = self.app_settings.get_setting("timeouts", "gdb_start", 30) // 2
|
||||||
temp_gdb_session = GDBSession(
|
command_timeout = self.app_settings.get_setting("timeouts", "gdb_command", 30) // 2
|
||||||
gdb_path=gdb_exe_path,
|
|
||||||
executable_path=self.target_executable_path,
|
|
||||||
gdb_script_full_path=None, # Non serve lo script dumper per info functions
|
|
||||||
dumper_options={}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Usiamo timeout brevi per l'avvio e il comando
|
|
||||||
startup_timeout = self.app_settings.get_setting("timeouts", "gdb_start", 30) // 2 # Più breve
|
|
||||||
command_timeout = self.app_settings.get_setting("timeouts", "gdb_command", 30) // 2 # Più breve
|
|
||||||
|
|
||||||
logger.debug("Starting temporary GDB session...")
|
|
||||||
temp_gdb_session.start(timeout=startup_timeout) # Avvia GDB
|
|
||||||
|
|
||||||
|
temp_gdb_session.start(timeout=startup_timeout)
|
||||||
if not temp_gdb_session.symbols_found:
|
if not temp_gdb_session.symbols_found:
|
||||||
messagebox.showwarning("No Debug Symbols",
|
messagebox.showwarning("No Debug Symbols",
|
||||||
"GDB reported no debugging symbols found in the executable. "
|
"GDB reported no debugging symbols in the executable. "
|
||||||
"The function list might be empty or incomplete.", parent=self)
|
"The function list (live query) might be empty or incomplete.", parent=self)
|
||||||
# Continuiamo comunque, GDB potrebbe listare alcuni simboli non-debugging.
|
functions_to_show = temp_gdb_session.list_functions(timeout=command_timeout)
|
||||||
|
|
||||||
logger.debug("Listing functions...")
|
|
||||||
functions_list = temp_gdb_session.list_functions(timeout=command_timeout)
|
|
||||||
|
|
||||||
except FileNotFoundError as fnf_e:
|
|
||||||
logger.error(f"Error during function browsing (FileNotFound): {fnf_e}", exc_info=True)
|
|
||||||
messagebox.showerror("Error", f"File not found during GDB interaction: {fnf_e}", parent=self)
|
|
||||||
error_occurred = True
|
|
||||||
except (ConnectionError, TimeoutError) as session_e:
|
|
||||||
logger.error(f"Session error during function browsing: {session_e}", exc_info=True)
|
|
||||||
messagebox.showerror("GDB Error", f"Could not communicate with GDB to list functions: {session_e}", parent=self)
|
|
||||||
error_occurred = True
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error during function browsing: {e}", exc_info=True)
|
logger.error(f"Error during live function query: {e}", exc_info=True)
|
||||||
messagebox.showerror("Error", f"An unexpected error occurred: {e}", parent=self)
|
messagebox.showerror("GDB Query Error", f"Could not retrieve functions from GDB: {e}", parent=self)
|
||||||
error_occurred = True
|
live_query_error = True
|
||||||
finally:
|
finally:
|
||||||
if temp_gdb_session and temp_gdb_session.is_alive():
|
if temp_gdb_session and temp_gdb_session.is_alive():
|
||||||
logger.debug("Quitting temporary GDB session...")
|
|
||||||
quit_timeout = self.app_settings.get_setting("timeouts", "gdb_quit", 10) // 2
|
quit_timeout = self.app_settings.get_setting("timeouts", "gdb_quit", 10) // 2
|
||||||
try:
|
try: temp_gdb_session.quit(timeout=quit_timeout)
|
||||||
temp_gdb_session.quit(timeout=quit_timeout)
|
except Exception: pass # Ignora errori nel quit della sessione temporanea
|
||||||
except Exception as e_quit:
|
self.config(cursor="")
|
||||||
logger.error(f"Error quitting temporary GDB session: {e_quit}")
|
if live_query_error: return # Non mostrare la dialog se c'è stato un errore
|
||||||
self.config(cursor="") # Ripristina cursore
|
|
||||||
|
|
||||||
if not error_occurred:
|
# 3. Mostra la dialog con le funzioni (cachate o da query live)
|
||||||
if functions_list:
|
if functions_to_show:
|
||||||
dialog = FunctionSelectorDialog(self, functions_list)
|
logger.info(f"Displaying FunctionSelectorDialog with {len(functions_to_show)} functions from '{source_of_functions}'.")
|
||||||
|
dialog = FunctionSelectorDialog(self, functions_to_show, title=f"Select Function (from {source_of_functions})")
|
||||||
selected_function = dialog.result
|
selected_function = dialog.result
|
||||||
if selected_function:
|
if selected_function:
|
||||||
self.breakpoint_var.set(selected_function)
|
self.breakpoint_var.set(selected_function)
|
||||||
else:
|
else:
|
||||||
messagebox.showinfo("No Functions", "No functions found or GDB did not return any.", parent=self)
|
messagebox.showinfo("No Functions", f"No functions found (source: {source_of_functions}). Ensure target is compiled with debug symbols.", parent=self)
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user