add counter for analyze

This commit is contained in:
VALLONGOL 2025-05-23 15:45:13 +02:00
parent b6db1c8623
commit 0988c35b11
2 changed files with 213 additions and 68 deletions

View File

@ -120,6 +120,71 @@ class SymbolAnalysisProgressDialog(tk.Toplevel):
self.set_status("Analysis failed or was aborted. Check log. You can close this window.")
class SymbolListViewerDialog(tk.Toplevel): # NUOVA DIALOG PER VISUALIZZARE LISTE
"""A simple dialog to view a list of symbols."""
def __init__(self, parent: tk.Widget, symbols: List[str], title: str = "Symbol List"):
super().__init__(parent)
self.title(title)
parent_x = parent.winfo_x()
parent_y = parent.winfo_y()
parent_width = parent.winfo_width()
parent_height = parent.winfo_height()
width = 500
height = 450
x = parent_x + (parent_width // 2) - (width // 2)
y = parent_y + (parent_height // 2) - (height // 2)
self.geometry(f'{width}x{height}+{x}+{y}')
self.transient(parent)
self.grab_set()
main_frame = ttk.Frame(self, padding="10")
main_frame.pack(expand=True, fill=tk.BOTH)
main_frame.rowconfigure(1, weight=1)
main_frame.columnconfigure(0, weight=1)
filter_frame = ttk.Frame(main_frame)
filter_frame.grid(row=0, column=0, sticky="ew", pady=(0, 5))
ttk.Label(filter_frame, text="Filter:").pack(side=tk.LEFT, padx=(0,5))
self.filter_var = tk.StringVar()
self.filter_var.trace_add("write", self._apply_filter)
ttk.Entry(filter_frame, textvariable=self.filter_var, width=40).pack(side=tk.LEFT, expand=True, fill=tk.X)
self.listbox = tk.Listbox(main_frame, selectmode=tk.SINGLE)
self.listbox.grid(row=1, column=0, sticky="nsew")
scrollbar_y = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.listbox.yview)
scrollbar_y.grid(row=1, column=1, sticky="ns")
self.listbox.configure(yscrollcommand=scrollbar_y.set)
scrollbar_x = ttk.Scrollbar(main_frame, orient=tk.HORIZONTAL, command=self.listbox.xview)
scrollbar_x.grid(row=2, column=0, sticky="ew")
self.listbox.configure(xscrollcommand=scrollbar_x.set)
self._original_symbols = sorted(symbols)
self._populate_listbox(self._original_symbols)
button_frame = ttk.Frame(main_frame, padding=(0, 10, 0, 0))
button_frame.grid(row=3, column=0, columnspan=2, sticky="e")
ttk.Button(button_frame, text="Close", command=self.destroy).pack()
self.protocol("WM_DELETE_WINDOW", self.destroy)
def _populate_listbox(self, symbols_to_show: List[str]):
self.listbox.delete(0, tk.END)
for item in symbols_to_show:
self.listbox.insert(tk.END, item)
def _apply_filter(self, *args):
filter_text = self.filter_var.get().lower()
if not filter_text:
self._populate_listbox(self._original_symbols)
else:
filtered_list = [s for s in self._original_symbols if filter_text in s.lower()]
self._populate_listbox(filtered_list)
class ProfileManagerWindow(tk.Toplevel):
def __init__(self, parent: 'GDBGui', app_settings: 'AppSettings'):
super().__init__(parent)
@ -127,7 +192,7 @@ class ProfileManagerWindow(tk.Toplevel):
self.app_settings: 'AppSettings' = app_settings
self.title("Profile Manager")
self.geometry("1050x700")
self.geometry("1050x750") # Aumentata leggermente l'altezza per i nuovi widget
self.transient(parent)
self.grab_set()
@ -145,6 +210,12 @@ class ProfileManagerWindow(tk.Toplevel):
self._current_profile_target_exe_details_label_var = tk.StringVar(value="Target: N/A")
self._current_profile_analysis_status_label_var = tk.StringVar(value="Symbol Analysis: Not Performed")
self.progress_dialog: Optional[SymbolAnalysisProgressDialog] = None
# StringVars per i conteggi
self.functions_count_var = tk.StringVar(value="Functions: N/A")
# self.variables_count_var = tk.StringVar(value="Globals: N/A") # Futuro
# self.types_count_var = tk.StringVar(value="Types: N/A") # Futuro
# self.sources_count_var = tk.StringVar(value="Sources: N/A") # Futuro
self._load_profiles_from_settings()
self._create_widgets()
@ -183,15 +254,15 @@ class ProfileManagerWindow(tk.Toplevel):
def _create_widgets(self) -> None:
main_frame = ttk.Frame(self, padding="10")
main_frame.pack(expand=True, fill=tk.BOTH)
main_frame.columnconfigure(0, weight=1, minsize=250)
main_frame.columnconfigure(0, weight=1, minsize=250)
main_frame.columnconfigure(1, weight=3)
main_frame.rowconfigure(0, weight=1)
main_frame.rowconfigure(1, weight=0)
left_pane = ttk.Frame(main_frame)
left_pane.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
left_pane.rowconfigure(0, weight=1)
left_pane.rowconfigure(1, weight=0)
left_pane.rowconfigure(0, weight=1)
left_pane.rowconfigure(1, weight=0)
left_pane.columnconfigure(0, weight=1)
profiles_list_frame = ttk.LabelFrame(left_pane, text="Profiles", padding="5")
@ -220,7 +291,8 @@ class ProfileManagerWindow(tk.Toplevel):
right_pane.grid(row=0, column=1, sticky="nsew")
right_pane.rowconfigure(0, weight=0)
right_pane.rowconfigure(1, weight=0)
right_pane.rowconfigure(2, weight=1)
right_pane.rowconfigure(2, weight=0) # Riga per Conteggi Simboli
right_pane.rowconfigure(3, weight=1) # Riga per Azioni di Debug
right_pane.columnconfigure(0, weight=1)
details_form_frame = ttk.LabelFrame(right_pane, text="Profile Details", padding="10")
@ -241,7 +313,7 @@ class ProfileManagerWindow(tk.Toplevel):
self.program_params_entry = ttk.Entry(details_form_frame, textvariable=self.program_params_var, state=tk.DISABLED)
self.program_params_entry.grid(row=2, column=1, columnspan=2, sticky="ew", padx=5, pady=3)
analysis_control_frame = ttk.LabelFrame(right_pane, text="Symbol Analysis", padding="10")
analysis_control_frame = ttk.LabelFrame(right_pane, text="Symbol Analysis Status & Control", padding="10")
analysis_control_frame.grid(row=1, column=0, sticky="new", pady=5)
analysis_control_frame.columnconfigure(0, weight=1)
@ -254,14 +326,32 @@ class ProfileManagerWindow(tk.Toplevel):
self.analyse_symbols_button = ttk.Button(analysis_control_frame, text="Analyse Target Symbols", command=self._trigger_symbol_analysis, state=tk.DISABLED)
self.analyse_symbols_button.grid(row=1, column=1, sticky="e", padx=5, pady=2)
symbols_summary_frame = ttk.LabelFrame(right_pane, text="Analyzed Symbols Summary", padding="10")
symbols_summary_frame.grid(row=2, column=0, sticky="new", pady=5)
symbols_summary_frame.columnconfigure(0, weight=1)
symbols_summary_frame.columnconfigure(1, weight=0)
row_s = 0
ttk.Label(symbols_summary_frame, textvariable=self.functions_count_var).grid(row=row_s, column=0, sticky="w", padx=5, pady=2)
self.view_functions_button = ttk.Button(symbols_summary_frame, text="View...", command=self._view_analyzed_functions, state=tk.DISABLED, width=8)
self.view_functions_button.grid(row=row_s, column=1, padx=(10,5), pady=2, sticky="w")
row_s += 1
# Placeholder per futuri conteggi e bottoni (Iterazione 3)
# ttk.Label(symbols_summary_frame, textvariable=self.variables_count_var).grid(row=row_s, column=0, sticky="w", padx=5, pady=2)
# self.view_variables_button = ttk.Button(symbols_summary_frame, text="View...", command=self._view_analyzed_variables, state=tk.DISABLED, width=8)
# self.view_variables_button.grid(row=row_s, column=1, padx=(10,5), pady=2, sticky="w")
# row_s += 1
# ... (e così via per tipi e sorgenti)
actions_ui_frame = ttk.LabelFrame(right_pane, text="Debug Actions", padding="10")
actions_ui_frame.grid(row=2, column=0, sticky="nsew", pady=5)
actions_ui_frame.grid(row=3, column=0, sticky="nsew", pady=5) # Riga 3
actions_ui_frame.rowconfigure(0, weight=1)
actions_ui_frame.columnconfigure(0, weight=1)
actions_ui_frame.columnconfigure(1, weight=0)
actions_ui_frame.columnconfigure(2, weight=0)
self.actions_listbox = tk.Listbox(actions_ui_frame, exportselection=False, selectmode=tk.SINGLE, height=8)
self.actions_listbox = tk.Listbox(actions_ui_frame, exportselection=False, selectmode=tk.SINGLE, height=6)
self.actions_listbox.grid(row=0, column=0, sticky="nsew", pady=5, padx=(0,5))
self.actions_listbox.bind("<<ListboxSelect>>", self._on_action_select_in_listbox)
@ -273,11 +363,11 @@ class ProfileManagerWindow(tk.Toplevel):
action_buttons_frame.grid(row=0, column=2, sticky="ns", padx=(5,0), pady=5)
action_btn_width = 8
self.add_action_button = ttk.Button(action_buttons_frame, text="Add...", command=self._add_action, state=tk.DISABLED, width=action_btn_width)
self.add_action_button.pack(fill=tk.X, pady=2)
self.add_action_button.pack(fill=tk.X, pady=2, anchor="n")
self.edit_action_button = ttk.Button(action_buttons_frame, text="Edit...", command=self._edit_action, state=tk.DISABLED, width=action_btn_width)
self.edit_action_button.pack(fill=tk.X, pady=2)
self.edit_action_button.pack(fill=tk.X, pady=2, anchor="n")
self.remove_action_button = ttk.Button(action_buttons_frame, text="Remove", command=self._remove_action, state=tk.DISABLED, width=action_btn_width)
self.remove_action_button.pack(fill=tk.X, pady=2)
self.remove_action_button.pack(fill=tk.X, pady=2, anchor="n")
bottom_buttons_frame = ttk.Frame(main_frame)
bottom_buttons_frame.grid(row=1, column=0, columnspan=2, sticky="sew", pady=(10,0))
@ -347,14 +437,13 @@ class ProfileManagerWindow(tk.Toplevel):
if not (0 <= index < len(self._profiles_data)):
self._clear_profile_form()
self._selected_profile_index = None
# _update_analysis_status_display() è chiamato da _clear_profile_form
return
self._selected_profile_index = index
profile = self._profiles_data[index]
self.profile_name_var.set(profile.get("profile_name", ""))
self.target_exe_var.set(profile.get("target_executable", "")) # Triggera _on_target_exe_changed_in_form
self.target_exe_var.set(profile.get("target_executable", ""))
self.program_params_var.set(profile.get("program_parameters", ""))
self._populate_actions_listbox()
@ -368,15 +457,12 @@ class ProfileManagerWindow(tk.Toplevel):
self.profiles_listbox.see(index)
self.add_action_button.config(state=tk.NORMAL)
# _update_analysis_status_display() è chiamato da _on_target_exe_changed_in_form
# o dovrebbe essere chiamato esplicitamente qui se il set di target_exe_var non lo fa sempre.
# Per sicurezza, lo chiamiamo esplicitamente dopo aver impostato tutte le var.
self._update_analysis_status_display()
def _clear_profile_form(self) -> None:
self.profile_name_var.set("")
self.target_exe_var.set("") # Triggera _on_target_exe_changed_in_form
self.target_exe_var.set("")
self.program_params_var.set("")
self._populate_actions_listbox()
self._enable_profile_form_editing(False)
@ -392,7 +478,6 @@ class ProfileManagerWindow(tk.Toplevel):
self.target_exe_entry.config(state=state)
self.browse_exe_button.config(state=state)
self.program_params_entry.config(state=state)
# analyse_symbols_button state è gestito da _update_analysis_status_display
def _update_profile_action_buttons_state(self) -> None:
profile_selected = self._selected_profile_index is not None
@ -409,7 +494,7 @@ class ProfileManagerWindow(tk.Toplevel):
parent=self
)
if path:
self.target_exe_var.set(path) # Triggera _on_target_exe_changed_in_form
self.target_exe_var.set(path)
def _save_current_form_to_profile_data(self, profile_index: int) -> bool:
if not (0 <= profile_index < len(self._profiles_data)):
@ -432,7 +517,7 @@ class ProfileManagerWindow(tk.Toplevel):
old_name = target_profile.get("profile_name")
target_profile["profile_name"] = profile_name
target_profile["target_executable"] = self.target_exe_var.get().strip() # Questo aggiorna il path
target_profile["target_executable"] = self.target_exe_var.get().strip()
target_profile["program_parameters"] = self.program_params_var.get()
self._current_profile_modified_in_form = False
@ -443,15 +528,12 @@ class ProfileManagerWindow(tk.Toplevel):
self.profiles_listbox.insert(profile_index, profile_name)
self.profiles_listbox.selection_set(profile_index)
# Dopo aver salvato il form, lo stato dell'analisi potrebbe dover essere rivalutato
# se target_executable è cambiato. _update_analysis_status_display lo farà.
self._update_analysis_status_display()
logger.info(f"Profile '{profile_name}' (index {profile_index}) basic details updated.")
return True
def _new_profile(self) -> None:
if self._selected_profile_index is not None and self._current_profile_modified_in_form:
# ... (logica gestione modifiche non salvate come prima) ...
response = messagebox.askyesnocancel("Unsaved Changes",
f"Profile '{self._profiles_data[self._selected_profile_index].get('profile_name')}' has unsaved changes in the form.\n"
"Do you want to save them before creating a new profile?",
@ -462,7 +544,7 @@ class ProfileManagerWindow(tk.Toplevel):
elif response is None:
return
new_p = json.loads(json.dumps(DEFAULT_PROFILE)) # Deep copy del template
new_p = json.loads(json.dumps(DEFAULT_PROFILE))
base_name = "New Profile"
name_candidate = base_name
@ -489,7 +571,7 @@ class ProfileManagerWindow(tk.Toplevel):
return
original_profile = self._profiles_data[self._selected_profile_index]
duplicated_profile = json.loads(json.dumps(original_profile)) # Deep copy
duplicated_profile = json.loads(json.dumps(original_profile))
base_name = f"{original_profile.get('profile_name', 'Profile')}_copy"
name_candidate = base_name
@ -499,9 +581,6 @@ class ProfileManagerWindow(tk.Toplevel):
name_candidate = f"{base_name}_{count}"
count += 1
duplicated_profile["profile_name"] = name_candidate
# L'analisi dei simboli duplicata si riferirà all'exe originale;
# l'utente dovrà rianalizzare se cambia l'exe del profilo duplicato.
# L'impostazione del nome del profilo non dovrebbe cancellare symbol_analysis
self._profiles_data.append(duplicated_profile)
self._profiles_list_changed_overall = True
@ -530,9 +609,9 @@ class ProfileManagerWindow(tk.Toplevel):
self._clear_profile_form()
else:
new_selection_idx = min(idx_to_delete, new_list_size - 1)
if new_selection_idx >= 0: # Assicura che l'indice sia valido
if new_selection_idx >= 0:
self._select_profile_by_index(new_selection_idx)
else: # Se la lista è diventata vuota, pulisci
else:
self._clear_profile_form()
@ -544,7 +623,6 @@ class ProfileManagerWindow(tk.Toplevel):
profile_names_seen = set()
for i, profile in enumerate(self._profiles_data):
# ... (validazione nomi profili e struttura azioni come prima) ...
name = profile.get("profile_name", "").strip()
if not name:
messagebox.showerror("Validation Error", f"Profile at index {i} has an empty name.", parent=self)
@ -572,18 +650,16 @@ class ProfileManagerWindow(tk.Toplevel):
messagebox.showerror("Save Error", "Could not save profiles to the settings file. Check logs.", parent=self)
def _on_closing_button(self) -> None:
# ... (come prima) ...
needs_save_prompt = False
prompt_message = ""
if self._selected_profile_index is not None and self._current_profile_modified_in_form:
needs_save_prompt = True
# ... (costruzione messaggio)
profile_name = self.profile_name_var.get() or self._profiles_data[self._selected_profile_index].get('profile_name', 'current profile')
prompt_message = f"Profile '{profile_name}' has unsaved changes in the form.\n"
if self._profiles_list_changed_overall: # Se la lista stessa è cambiata (add/del/rename)
if self._profiles_list_changed_overall:
needs_save_prompt = True
if prompt_message: # Se già c'era un messaggio per il form
if prompt_message:
prompt_message += "Additionally, the overall list of profiles (or their content) has changed.\n"
else:
prompt_message = "The list of profiles (or their content) has changed.\n"
@ -593,21 +669,15 @@ class ProfileManagerWindow(tk.Toplevel):
response = messagebox.askyesnocancel("Unsaved Changes", prompt_message, default=messagebox.CANCEL, parent=self)
if response is True:
self._save_all_profiles_to_settings()
# Se il salvataggio fallisce, _save_all_profiles_to_settings mostra un errore,
# ma la finestra si chiuderà comunque. Potremmo voler cambiare questo comportamento.
elif response is None: # Cancel
elif response is None:
return
# Se False (No), procedi a chiudere senza salvare
if self.progress_dialog and self.progress_dialog.winfo_exists():
logger.warning("Closing ProfileManagerWindow while symbol analysis dialog might be open.")
# Potremmo voler provare a chiudere la dialog di progresso qui, o avvisare.
# Per ora, la dialog di progresso gestisce la sua chiusura.
self.parent_window.focus_set()
self.destroy()
# --- Gestione Azioni (Listbox e Bottoni Azione) ---
def _populate_actions_listbox(self) -> None:
self.actions_listbox.delete(0, tk.END)
self._selected_action_index_in_profile = None
@ -616,7 +686,7 @@ class ProfileManagerWindow(tk.Toplevel):
profile = self._profiles_data[self._selected_profile_index]
actions = profile.get("actions", [])
for i, action in enumerate(actions):
bp = action.get("breakpoint_location", "N/A")[:30] # Tronca per display
bp = action.get("breakpoint_location", "N/A")[:30]
num_vars = len(action.get("variables_to_dump", []))
fmt = action.get("output_format", "N/A")
cont = "Yes" if action.get("continue_after_dump", False) else "No"
@ -657,7 +727,7 @@ class ProfileManagerWindow(tk.Toplevel):
current_profile["actions"] = []
current_profile["actions"].append(new_action_data)
self._profiles_list_changed_overall = True
self._current_profile_modified_in_form = True # Il profilo nel form è cambiato
self._current_profile_modified_in_form = True
self._populate_actions_listbox()
self.actions_listbox.selection_set(tk.END)
self._on_action_select_in_listbox()
@ -665,6 +735,7 @@ class ProfileManagerWindow(tk.Toplevel):
def _edit_action(self) -> None:
if self._selected_profile_index is None or self._selected_action_index_in_profile is None: return
current_profile = self._profiles_data[self._selected_profile_index]
actions_list = current_profile.get("actions", [])
if not (0 <= self._selected_action_index_in_profile < len(actions_list)): return
@ -692,9 +763,10 @@ class ProfileManagerWindow(tk.Toplevel):
def _remove_action(self) -> None:
if self._selected_profile_index is None or self._selected_action_index_in_profile is None: return
# ... (come prima) ...
profile = self._profiles_data[self._selected_profile_index]
action_summary_to_delete = self.actions_listbox.get(self._selected_action_index_in_profile)
if not messagebox.askyesno("Confirm Delete Action",
f"Are you sure you want to delete this action?\n\n{action_summary_to_delete}",
parent=self): return
@ -718,28 +790,38 @@ class ProfileManagerWindow(tk.Toplevel):
self._current_profile_target_exe_details_label_var.set("Target: N/A")
self._current_profile_analysis_status_label_var.set("Symbol Analysis: Select a profile.")
self.analyse_symbols_button.config(state=tk.DISABLED)
self.functions_count_var.set("Functions: N/A")
self.view_functions_button.config(state=tk.DISABLED)
return
profile = self._profiles_data[self._selected_profile_index]
target_exe_from_form = self.target_exe_var.get() # Path attuale nel form
target_exe_in_form = self.target_exe_var.get()
exe_display_name = os.path.basename(target_exe_from_form) if target_exe_from_form else "N/A"
exe_display_name = os.path.basename(target_exe_in_form) if target_exe_in_form else "N/A"
details_text_lines = [f"Target in Form: {exe_display_name}"]
status_text = "Symbol Analysis: "
status_color = "blue" # Default
status_color = "blue"
funcs_count_text = "Functions: N/A"
view_funcs_btn_state = tk.DISABLED
analysis_button_state = tk.DISABLED
if target_exe_from_form and os.path.isfile(target_exe_from_form):
analysis_button_state = tk.NORMAL # Abilita se il file nel form esiste
if target_exe_in_form and os.path.isfile(target_exe_in_form):
analysis_button_state = tk.NORMAL
if not target_exe_from_form:
if not target_exe_in_form:
status_text += "Target executable not specified in form."
elif not os.path.isfile(target_exe_from_form):
elif not os.path.isfile(target_exe_in_form):
status_text += f"Target '{exe_display_name}' not found on disk."
status_color = "red"
else: # Il file nel form esiste, ora controlla i dati di analisi salvati
else:
analysis_data = profile.get("symbol_analysis")
if analysis_data and isinstance(analysis_data, dict):
symbols_dict = analysis_data.get("symbols", {})
num_functions = symbols_dict.get("functions_count", 0)
funcs_count_text = f"Functions: {num_functions}"
if num_functions > 0 :
view_funcs_btn_state = tk.NORMAL
saved_checksum = analysis_data.get("executable_checksum")
saved_analysis_ts_str = analysis_data.get("analysis_timestamp")
saved_exe_at_analysis = analysis_data.get("analyzed_executable_path", "Unknown")
@ -750,22 +832,28 @@ class ProfileManagerWindow(tk.Toplevel):
details_text_lines.append(f" Analysis Date: {saved_analysis_ts_str or 'N/A'}")
details_text_lines.append(f" Saved Checksum: {saved_checksum or 'N/A'}")
current_checksum_for_form_exe = self._calculate_file_checksum(target_exe_from_form)
current_checksum_for_form_exe = self._calculate_file_checksum(target_exe_in_form)
details_text_lines.append(f" Current Form Exe Checksum: {current_checksum_for_form_exe or 'N/A (calc failed)'}")
if os.path.normpath(saved_exe_at_analysis) != os.path.normpath(target_exe_from_form):
if os.path.normpath(saved_exe_at_analysis) != os.path.normpath(target_exe_in_form):
status_text += "TARGET CHANGED since last analysis. RE-ANALYSIS RECOMMENDED."
status_color = "orange red"
# Disabilita i bottoni View se il target è cambiato
view_funcs_btn_state = tk.DISABLED
elif saved_checksum and current_checksum_for_form_exe and saved_checksum == current_checksum_for_form_exe:
status_text += "Up-to-date."
status_color = "dark green"
elif saved_checksum and current_checksum_for_form_exe and saved_checksum != current_checksum_for_form_exe:
status_text += "EXECUTABLE CHANGED since last analysis. RE-ANALYSIS REQUIRED."
status_color = "red"
# Disabilita i bottoni View se l'eseguibile è cambiato
view_funcs_btn_state = tk.DISABLED
else: # Checksum mancanti o confronto non possibile
status_text += "Status unclear. Consider re-analysing."
status_color = "orange red"
else: # Nessuna analisi salvata per questo profilo
# Disabilita i bottoni View se lo stato è incerto
view_funcs_btn_state = tk.DISABLED
else: # Nessuna analisi salvata
status_text += "Not performed. Click 'Analyse' to generate."
status_color = "blue"
@ -773,6 +861,9 @@ class ProfileManagerWindow(tk.Toplevel):
self._current_profile_target_exe_details_label_var.set("\n".join(details_text_lines))
self._current_profile_analysis_status_label_var.set(status_text)
self.analysis_status_label.config(foreground=status_color)
self.functions_count_var.set(funcs_count_text)
self.view_functions_button.config(state=view_funcs_btn_state)
def _calculate_file_checksum(self, filepath: str, hash_type: str = "md5") -> Optional[str]:
if not os.path.isfile(filepath): return None
@ -821,7 +912,16 @@ class ProfileManagerWindow(tk.Toplevel):
"executable_timestamp": "N/A",
"analysis_timestamp": "N/A",
"gdb_version_info": "N/A",
"symbols": {"functions": []}
"symbols": {
"functions": [],
"functions_count": 0,
# "global_variables": [], # Per Iterazione 3
# "global_variables_count": 0,# Per Iterazione 3
# "types": [], # Per Iterazione 3
# "types_count": 0, # Per Iterazione 3
# "source_files": [], # Per Iterazione 3
# "source_files_count": 0 # Per Iterazione 3
}
}
analysis_succeeded_overall = False
@ -848,23 +948,31 @@ class ProfileManagerWindow(tk.Toplevel):
gui_log("WARNING: GDB reported no debugging symbols. Analysis may be limited.")
gui_set_status("Fetching GDB version..."); gui_log("Fetching GDB version...")
analysis_data_dict["gdb_version_info"] = temp_gdb_session.get_gdb_version(timeout=command_timeout) or "N/A"
gdb_version = temp_gdb_session.get_gdb_version(timeout=command_timeout)
analysis_data_dict["gdb_version_info"] = gdb_version if gdb_version else "N/A"
gui_log(f"GDB Version: {analysis_data_dict['gdb_version_info']}")
gui_set_status("Fetching function list..."); gui_log("Fetching function list from GDB...")
functions = temp_gdb_session.list_functions(timeout=command_timeout * 3) # Dare più tempo a info functions
functions = temp_gdb_session.list_functions(timeout=command_timeout * 4) # Più tempo per info functions
analysis_data_dict["symbols"]["functions"] = functions
analysis_data_dict["symbols"]["functions_count"] = len(functions) # Salva il conteggio
gui_log(f"Found {len(functions)} functions.")
# Altre analisi (variabili, tipi) verranno qui nelle iterazioni future
# analysis_data_dict["symbols"]["global_variables"] = temp_gdb_session.list_global_variables(...)
# --- In future iterations, call methods for other symbols and store counts ---
# gui_set_status("Fetching global variables..."); gui_log("Fetching global variables...")
# globals_list = temp_gdb_session.list_global_variables(timeout=command_timeout * 2)
# analysis_data_dict["symbols"]["global_variables"] = globals_list
# analysis_data_dict["symbols"]["global_variables_count"] = len(globals_list)
# gui_log(f"Found {len(globals_list)} global variables.")
# ... (similarly for types and source_files) ...
gui_set_status("Calculating file checksum and timestamp..."); gui_log("Calculating file checksum and timestamp...")
analysis_data_dict["executable_checksum"] = self._calculate_file_checksum(target_exe_path)
try:
mtime = os.path.getmtime(target_exe_path)
analysis_data_dict["executable_timestamp"] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(mtime))
except OSError: pass # Lascia N/A
except OSError: pass
gui_log(f"Checksum (MD5): {analysis_data_dict['executable_checksum'] or 'N/A'}")
gui_log(f"File Timestamp: {analysis_data_dict['executable_timestamp']}")
@ -899,19 +1007,50 @@ class ProfileManagerWindow(tk.Toplevel):
if success:
profile_to_update["symbol_analysis"] = analysis_data
self._profiles_list_changed_overall = True
self._current_profile_modified_in_form = True # Il contenuto del profilo è cambiato
self._current_profile_modified_in_form = True
logger.info(f"Symbol analysis data updated for profile: '{profile_to_update.get('profile_name')}'.")
if self.winfo_exists(): # Assicura che la finestra genitore esista ancora
if self.winfo_exists():
messagebox.showinfo("Analysis Complete", "Symbol analysis has finished successfully.", parent=self)
else:
# Non aggiorniamo symbol_analysis nel profilo se fallisce, ma potremmo salvare dati parziali se utile
logger.error(f"Symbol analysis failed for profile: '{profile_to_update.get('profile_name')}'.")
if self.winfo_exists():
messagebox.showerror("Analysis Failed", "Symbol analysis did not complete successfully. Check logs.", parent=self)
if progress_dialog and progress_dialog.winfo_exists():
progress_dialog.analysis_complete_or_failed(success)
# La dialog ora ha un bottone Close abilitato, l'utente la chiuderà.
# Oppure: self.after(2000, lambda: progress_dialog.destroy() if progress_dialog.winfo_exists() else None)
self._update_analysis_status_display()
self._update_analysis_status_display()
def _view_analyzed_functions(self) -> None:
if self._selected_profile_index is None or \
not (0 <= self._selected_profile_index < len(self._profiles_data)):
messagebox.showinfo("Info", "No profile selected or data available.", parent=self)
return
profile = self._profiles_data[self._selected_profile_index]
analysis_data = profile.get("symbol_analysis")
if not analysis_data or not isinstance(analysis_data.get("symbols"), dict):
messagebox.showinfo("No Analysis Data", "No symbol analysis data available for this profile.", parent=self)
return
functions_list = analysis_data["symbols"].get("functions", [])
if not functions_list:
messagebox.showinfo("No Functions", "No functions found in the last analysis for this profile.", parent=self)
return
target_exe_in_form = self.target_exe_var.get()
analyzed_exe_path = analysis_data.get("analyzed_executable_path", "")
exe_name_for_title = os.path.basename(target_exe_in_form) if target_exe_in_form else "Unknown Executable"
is_obsolete = True
if os.path.normpath(analyzed_exe_path) == os.path.normpath(target_exe_in_form):
current_checksum = self._calculate_file_checksum(target_exe_in_form)
saved_checksum = analysis_data.get("executable_checksum")
if current_checksum and saved_checksum and current_checksum == saved_checksum:
is_obsolete = False
title_suffix = " (Analysis might be obsolete)" if is_obsolete else ""
dialog_title = f"Analyzed Functions for '{exe_name_for_title}'{title_suffix}"
SymbolListViewerDialog(self, functions_list, title=dialog_title)

View File

@ -54,6 +54,12 @@ DEBUG_STRING_TRACE: _serializ
questo run funziona
(gdb) run /c /t 4:"C:/__Voli/Volo_12_25maggio2025/sar_367-435/_25-05-15-12-22-52_sata_367-n69.out" /b
Assolutamente sì, la tua idea è eccellente e va nella direzione giusta per rendere l'applicazione molto più potente e user-friendly! Sfruttare le capacità introspettive di GDB per assistere l'utente nella creazione di profili di debug è una funzionalità di grande valore.
**Concetto Chiave: Introspezione di GDB per l'Assistenza all'Utente**