change gui with notebook
This commit is contained in:
parent
aaf358e1e1
commit
edec4eda3e
@ -141,111 +141,164 @@ class GDBGui(tk.Tk):
|
|||||||
def _create_widgets(self):
|
def _create_widgets(self):
|
||||||
main_frame = ttk.Frame(self, padding="10")
|
main_frame = ttk.Frame(self, padding="10")
|
||||||
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1) # Permette a main_frame di espandersi orizzontalmente
|
||||||
self.rowconfigure(0, weight=1)
|
self.rowconfigure(0, weight=1) # Permette a main_frame di espandersi verticalmente
|
||||||
|
|
||||||
# --- Configuration Status Frame ---
|
# Configura le righe di main_frame per l'espansione
|
||||||
# MODIFIED: Replaced GDB/Dumper path entries with status labels and a configure button
|
# row 0: config_status_frame (non espandibile verticalmente)
|
||||||
config_status_frame = ttk.LabelFrame(main_frame, text="Critical Configuration Status", padding="10")
|
# row 1: mode_notebook (moderatamente espandibile o fisso)
|
||||||
config_status_frame.grid(row=0, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
# row 2: output_log_notebook (massima espansione verticale)
|
||||||
config_status_frame.columnconfigure(1, weight=1) # Allow status label to expand
|
# row 3: status_bar (non espandibile verticalmente)
|
||||||
|
main_frame.rowconfigure(0, weight=0)
|
||||||
|
main_frame.rowconfigure(1, weight=1) # Diamo un po' di peso al notebook delle modalità
|
||||||
|
main_frame.rowconfigure(2, weight=3) # Diamo più peso al notebook dei log per espansione
|
||||||
|
main_frame.rowconfigure(3, weight=0)
|
||||||
|
main_frame.columnconfigure(0, weight=1) # Permette espansione orizzontale dei figli
|
||||||
|
|
||||||
|
# --- 1. Frame Stato Configurazione Critica (IN ALTO) ---
|
||||||
|
self._create_config_status_widgets(main_frame)
|
||||||
|
|
||||||
|
# --- 2. Notebook per Modalità di Debug (Manuale / Automatico) ---
|
||||||
|
self._create_mode_notebook_widgets(main_frame)
|
||||||
|
|
||||||
|
# --- 3. Notebook per Output e Log (IN BASSO, SOPRA LA STATUS BAR) ---
|
||||||
|
self._create_output_log_widgets(main_frame)
|
||||||
|
|
||||||
|
# --- 4. Barra di Stato (IN FONDO) ---
|
||||||
|
self._create_status_bar(main_frame)
|
||||||
|
|
||||||
|
def _create_config_status_widgets(self, parent_frame: ttk.Frame):
|
||||||
|
"""Crea i widget per lo stato della configurazione critica, tutti su una riga."""
|
||||||
|
config_status_frame = ttk.LabelFrame(parent_frame, text="Critical Configuration Status", padding=(10, 5, 10, 10)) # Aggiustato padding
|
||||||
|
config_status_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=5, padx=0)
|
||||||
|
|
||||||
|
# Configura le colonne per distribuire lo spazio
|
||||||
|
# Colonna 0: Label "GDB Executable:"
|
||||||
|
# Colonna 1: Status GDB (espandibile)
|
||||||
|
# Colonna 2: Label "GDB Dumper Script:"
|
||||||
|
# Colonna 3: Status Dumper (espandibile)
|
||||||
|
# Colonna 4: Bottone "Open Configuration..."
|
||||||
|
config_status_frame.columnconfigure(0, weight=0) # Label fissa
|
||||||
|
config_status_frame.columnconfigure(1, weight=1) # Status espandibile
|
||||||
|
config_status_frame.columnconfigure(2, weight=0) # Label fissa
|
||||||
|
config_status_frame.columnconfigure(3, weight=1) # Status espandibile
|
||||||
|
config_status_frame.columnconfigure(4, weight=0) # Bottone fisso
|
||||||
|
|
||||||
|
# Riga 0
|
||||||
|
ttk.Label(config_status_frame, text="GDB:").grid(row=0, column=0, sticky=tk.W, padx=(5,0), pady=5)
|
||||||
|
self.gdb_exe_status_label = ttk.Label(config_status_frame, textvariable=self.gdb_exe_status_var, relief="sunken", padding=(5,2), anchor=tk.W)
|
||||||
|
self.gdb_exe_status_label.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0,10), pady=5)
|
||||||
|
|
||||||
|
ttk.Label(config_status_frame, text="Dumper:").grid(row=0, column=2, sticky=tk.W, padx=(5,0), pady=5)
|
||||||
|
self.gdb_dumper_status_label = ttk.Label(config_status_frame, textvariable=self.gdb_dumper_status_var, relief="sunken", padding=(5,2), anchor=tk.W)
|
||||||
|
self.gdb_dumper_status_label.grid(row=0, column=3, sticky=(tk.W, tk.E), padx=(0,10), pady=5)
|
||||||
|
|
||||||
|
ttk.Button(config_status_frame, text="Configure...", command=self._open_config_window).grid(
|
||||||
|
row=0, column=4, padx=(5,5), pady=5, sticky=tk.E
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_mode_notebook_widgets(self, parent_frame: ttk.Frame):
|
||||||
|
"""Crea il Notebook per le modalità di debug (Manuale, Automatico)."""
|
||||||
|
mode_notebook = ttk.Notebook(parent_frame)
|
||||||
|
# MODIFIED: sticky per espandere in tutte le direzioni, padx per allineare
|
||||||
|
mode_notebook.grid(row=1, column=0, columnspan=1, sticky='nsew', pady=5, padx=0)
|
||||||
|
|
||||||
|
# Tab: Manual Debug
|
||||||
|
manual_debug_frame = ttk.Frame(mode_notebook, padding="5")
|
||||||
|
mode_notebook.add(manual_debug_frame, text="Manual Debug")
|
||||||
|
self._populate_manual_debug_tab(manual_debug_frame)
|
||||||
|
|
||||||
|
# Tab: Automatic Debug (Placeholder)
|
||||||
|
automatic_debug_frame = ttk.Frame(mode_notebook, padding="10")
|
||||||
|
mode_notebook.add(automatic_debug_frame, text="Automatic Debug")
|
||||||
|
ttk.Label(automatic_debug_frame, text="Automated profile execution controls will be here.").pack(padx=5, pady=5)
|
||||||
|
|
||||||
|
|
||||||
|
def _populate_manual_debug_tab(self, parent_tab_frame: ttk.Frame):
|
||||||
|
"""Popola la scheda "Manual Debug" con i controlli necessari."""
|
||||||
|
parent_tab_frame.columnconfigure(0, weight=1) # Permetti al contenuto di espandersi
|
||||||
|
|
||||||
|
# Frame per Target & Debug Session Settings (precedentemente runtime_config_frame)
|
||||||
|
manual_target_settings_frame = ttk.LabelFrame(parent_tab_frame, text="Target & Debug Session Settings", padding="10")
|
||||||
|
manual_target_settings_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=5)
|
||||||
|
manual_target_settings_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
row_idx = 0
|
row_idx = 0
|
||||||
ttk.Label(config_status_frame, text="GDB Executable:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
ttk.Label(manual_target_settings_frame, text="Target Executable:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
||||||
ttk.Label(config_status_frame, textvariable=self.gdb_exe_status_var, relief="sunken", padding=(5,2), width=60).grid(row=row_idx, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
|
ttk.Entry(manual_target_settings_frame, textvariable=self.exe_path_var, width=70).grid(row=row_idx, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
|
||||||
|
ttk.Button(manual_target_settings_frame, text="Browse...", command=self._browse_target_exe).grid(row=row_idx, column=2, padx=5, pady=2)
|
||||||
row_idx += 1
|
row_idx += 1
|
||||||
|
|
||||||
ttk.Label(config_status_frame, text="GDB Dumper Script:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
ttk.Label(manual_target_settings_frame, text="Program Parameters:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
||||||
ttk.Label(config_status_frame, textvariable=self.gdb_dumper_status_var, relief="sunken", padding=(5,2), width=60).grid(row=row_idx, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
|
ttk.Entry(manual_target_settings_frame, textvariable=self.params_var).grid(row=row_idx, column=1, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=2)
|
||||||
row_idx += 1
|
row_idx += 1
|
||||||
|
|
||||||
ttk.Button(config_status_frame, text="Open Configuration...", command=self._open_config_window).grid(
|
ttk.Label(manual_target_settings_frame, text="Breakpoint Location:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
||||||
row=0, column=2, rowspan=row_idx, padx=10, pady=5, sticky=(tk.N, tk.S, tk.E, tk.W) # Span rows and fill vertically
|
ttk.Entry(manual_target_settings_frame, textvariable=self.breakpoint_var).grid(row=row_idx, column=1, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=2)
|
||||||
)
|
|
||||||
|
|
||||||
# --- Target and Runtime Configuration Frame ---
|
|
||||||
# This frame now contains the rest of the previous "GDB Configuration"
|
|
||||||
runtime_config_frame = ttk.LabelFrame(main_frame, text="Target & Debug Session Settings", padding="10")
|
|
||||||
runtime_config_frame.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
|
||||||
runtime_config_frame.columnconfigure(1, weight=1) # Allow entry fields to expand
|
|
||||||
|
|
||||||
row_idx = 0 # Reset row index for the new frame
|
|
||||||
# Target Executable Path
|
|
||||||
ttk.Label(runtime_config_frame, text="Target Executable:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
|
||||||
ttk.Entry(runtime_config_frame, textvariable=self.exe_path_var, width=70).grid(row=row_idx, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
|
|
||||||
ttk.Button(runtime_config_frame, text="Browse...", command=self._browse_target_exe).grid(row=row_idx, column=2, padx=5, pady=2)
|
|
||||||
row_idx += 1
|
row_idx += 1
|
||||||
|
|
||||||
# Program Parameters
|
bp_help_text = "Examples: main, myfile.cpp:123, MyClass::myMethod"
|
||||||
ttk.Label(runtime_config_frame, text="Program Parameters:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
ttk.Label(manual_target_settings_frame, text=bp_help_text, foreground="gray", font=("TkDefaultFont", 8)).grid(
|
||||||
ttk.Entry(runtime_config_frame, textvariable=self.params_var).grid(row=row_idx, column=1, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=2)
|
row=row_idx, column=1, columnspan=2, sticky=tk.W, padx=7, pady=(0,5))
|
||||||
row_idx += 1
|
row_idx += 1
|
||||||
|
|
||||||
# Breakpoint
|
ttk.Label(manual_target_settings_frame, text="Variable/Expression:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
||||||
ttk.Label(runtime_config_frame, text="Breakpoint Location:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
ttk.Entry(manual_target_settings_frame, textvariable=self.variable_var).grid(row=row_idx, column=1, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=2)
|
||||||
ttk.Entry(runtime_config_frame, textvariable=self.breakpoint_var).grid(row=row_idx, column=1, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=2)
|
|
||||||
row_idx += 1
|
# Frame per Session Control (precedentemente control_frame)
|
||||||
|
manual_session_control_frame = ttk.LabelFrame(parent_tab_frame, text="Session Control", padding="10")
|
||||||
|
manual_session_control_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(10,5))
|
||||||
|
|
||||||
bp_help_text = "Examples: main, myfile.cpp:123, MyClass::myMethod" # Shortened
|
button_flow_frame = ttk.Frame(manual_session_control_frame) # Contenitore per i bottoni
|
||||||
ttk.Label(runtime_config_frame, text=bp_help_text, foreground="gray", font=("TkDefaultFont", 8)).grid(
|
button_flow_frame.pack(fill=tk.X, expand=True) # Permetti ai bottoni di distribuirsi
|
||||||
row=row_idx, column=1, columnspan=2, sticky=tk.W, padx=7, pady=(0,5)
|
|
||||||
)
|
|
||||||
row_idx += 1
|
|
||||||
|
|
||||||
# Variable to Dump
|
self.start_gdb_button = ttk.Button(button_flow_frame, text="1. Start GDB", command=self._start_gdb_session_action, state=tk.DISABLED)
|
||||||
ttk.Label(runtime_config_frame, text="Variable/Expression:").grid(row=row_idx, column=0, sticky=tk.W, padx=5, pady=2)
|
self.start_gdb_button.pack(side=tk.LEFT, padx=2, pady=5, fill=tk.X, expand=True)
|
||||||
ttk.Entry(runtime_config_frame, textvariable=self.variable_var).grid(row=row_idx, column=1, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=2)
|
|
||||||
|
|
||||||
# --- Control Frame (Starts at row 2 now) ---
|
|
||||||
control_frame = ttk.LabelFrame(main_frame, text="Session Control", padding="10")
|
|
||||||
control_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
|
|
||||||
|
|
||||||
button_flow_frame = ttk.Frame(control_frame)
|
self.set_bp_button = ttk.Button(button_flow_frame, text="2. Set BP", command=self._set_gdb_breakpoint_action, state=tk.DISABLED)
|
||||||
button_flow_frame.pack(fill=tk.X, expand=True)
|
self.set_bp_button.pack(side=tk.LEFT, padx=2, pady=5, fill=tk.X, expand=True)
|
||||||
|
|
||||||
self.start_gdb_button = ttk.Button(button_flow_frame, text="1. Start GDB Session", command=self._start_gdb_session_action, state=tk.DISABLED) # Start disabled
|
self.run_button = ttk.Button(button_flow_frame, text="3. Run", command=self._run_or_continue_gdb_action, state=tk.DISABLED)
|
||||||
self.start_gdb_button.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
self.run_button.pack(side=tk.LEFT, padx=2, pady=5, fill=tk.X, expand=True)
|
||||||
|
|
||||||
|
self.dump_var_button = ttk.Button(button_flow_frame, text="4. Dump Var", command=self._dump_gdb_variable_action, state=tk.DISABLED)
|
||||||
|
self.dump_var_button.pack(side=tk.LEFT, padx=2, pady=5, fill=tk.X, expand=True)
|
||||||
|
|
||||||
self.set_bp_button = ttk.Button(button_flow_frame, text="2. Set Breakpoint", command=self._set_gdb_breakpoint_action, state=tk.DISABLED)
|
self.stop_gdb_button = ttk.Button(button_flow_frame, text="Stop GDB", command=self._stop_gdb_session_action, state=tk.DISABLED)
|
||||||
self.set_bp_button.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
self.stop_gdb_button.pack(side=tk.LEFT, padx=2, pady=5, fill=tk.X, expand=True)
|
||||||
|
|
||||||
self.run_button = ttk.Button(button_flow_frame, text="3. Run Program", command=self._run_or_continue_gdb_action, state=tk.DISABLED)
|
# Frame per Save Dumped Data (precedentemente save_frame)
|
||||||
self.run_button.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
manual_save_data_frame = ttk.LabelFrame(parent_tab_frame, text="Save Dumped Data", padding="10")
|
||||||
|
manual_save_data_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=5)
|
||||||
self.dump_var_button = ttk.Button(button_flow_frame, text="4. Dump Variable", command=self._dump_gdb_variable_action, state=tk.DISABLED)
|
|
||||||
self.dump_var_button.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
|
||||||
|
|
||||||
self.stop_gdb_button = ttk.Button(button_flow_frame, text="Stop GDB Session", command=self._stop_gdb_session_action, state=tk.DISABLED)
|
self.save_json_button = ttk.Button(manual_save_data_frame, text="Save as JSON", command=lambda: self._save_dumped_data("json"), state=tk.DISABLED)
|
||||||
self.stop_gdb_button.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
|
||||||
|
|
||||||
# --- Output and Log Tabs (Starts at row 3 now) ---
|
|
||||||
output_notebook = ttk.Notebook(main_frame)
|
|
||||||
output_notebook.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
|
|
||||||
main_frame.rowconfigure(3, weight=1) # Allow notebook to expand vertically
|
|
||||||
|
|
||||||
self.gdb_raw_output_text = scrolledtext.ScrolledText(output_notebook, wrap=tk.WORD, height=10, state=tk.DISABLED, font=("Consolas", 9))
|
|
||||||
output_notebook.add(self.gdb_raw_output_text, text="GDB Raw Output")
|
|
||||||
|
|
||||||
self.parsed_json_output_text = scrolledtext.ScrolledText(output_notebook, wrap=tk.WORD, height=10, state=tk.DISABLED, font=("Consolas", 9))
|
|
||||||
output_notebook.add(self.parsed_json_output_text, text="Parsed JSON Output")
|
|
||||||
|
|
||||||
self.app_log_text = scrolledtext.ScrolledText(output_notebook, wrap=tk.WORD, height=10, state=tk.DISABLED, font=("Consolas", 9))
|
|
||||||
output_notebook.add(self.app_log_text, text="Application Log")
|
|
||||||
|
|
||||||
# --- Save Frame (Starts at row 4 now) ---
|
|
||||||
save_frame = ttk.LabelFrame(main_frame, text="Save Dumped Data", padding="10")
|
|
||||||
save_frame.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
|
||||||
|
|
||||||
self.save_json_button = ttk.Button(save_frame, text="Save as JSON", command=lambda: self._save_dumped_data("json"), state=tk.DISABLED)
|
|
||||||
self.save_json_button.pack(side=tk.LEFT, padx=5, pady=5)
|
self.save_json_button.pack(side=tk.LEFT, padx=5, pady=5)
|
||||||
|
|
||||||
self.save_csv_button = ttk.Button(save_frame, text="Save as CSV", command=lambda: self._save_dumped_data("csv"), state=tk.DISABLED)
|
self.save_csv_button = ttk.Button(manual_save_data_frame, text="Save as CSV", command=lambda: self._save_dumped_data("csv"), state=tk.DISABLED)
|
||||||
self.save_csv_button.pack(side=tk.LEFT, padx=5, pady=5)
|
self.save_csv_button.pack(side=tk.LEFT, padx=5, pady=5)
|
||||||
|
|
||||||
# Status Bar (Starts at row 5 now)
|
def _create_output_log_widgets(self, parent_frame: ttk.Frame):
|
||||||
self.status_var = tk.StringVar(value="Ready. Please configure GDB path if needed via Options > Configure.")
|
"""Crea il Notebook per i log e l'output JSON."""
|
||||||
status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
|
output_log_notebook = ttk.Notebook(parent_frame)
|
||||||
status_bar.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(5,0), ipady=2)
|
# MODIFIED: sticky per espandere, padx per allineare
|
||||||
|
output_log_notebook.grid(row=2, column=0, columnspan=1, sticky='nsew', pady=(5,0), padx=0)
|
||||||
|
|
||||||
|
# Le ScrolledText ora vengono create qui e aggiunte al notebook
|
||||||
|
self.gdb_raw_output_text = scrolledtext.ScrolledText(output_log_notebook, wrap=tk.WORD, height=10, state=tk.DISABLED, font=("Consolas", 9))
|
||||||
|
output_log_notebook.add(self.gdb_raw_output_text, text="GDB Raw Output")
|
||||||
|
|
||||||
|
self.parsed_json_output_text = scrolledtext.ScrolledText(output_log_notebook, wrap=tk.WORD, height=10, state=tk.DISABLED, font=("Consolas", 9))
|
||||||
|
output_log_notebook.add(self.parsed_json_output_text, text="Parsed JSON Output")
|
||||||
|
|
||||||
|
self.app_log_text = scrolledtext.ScrolledText(output_log_notebook, wrap=tk.WORD, height=10, state=tk.DISABLED, font=("Consolas", 9))
|
||||||
|
output_log_notebook.add(self.app_log_text, text="Application Log")
|
||||||
|
|
||||||
|
def _create_status_bar(self, parent_frame: ttk.Frame):
|
||||||
|
"""Crea la barra di stato."""
|
||||||
|
self.status_var = tk.StringVar(value="Ready. Configure GDB via Options menu if needed.")
|
||||||
|
status_bar = ttk.Label(parent_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
|
||||||
|
# MODIFIED: sticky per espandere orizzontalmente, padx per allineare
|
||||||
|
status_bar.grid(row=3, column=0, columnspan=1, sticky=(tk.W, tk.E), pady=(5,0), ipady=2, padx=0)
|
||||||
|
|
||||||
def _setup_logging_redirect_to_gui(self):
|
def _setup_logging_redirect_to_gui(self):
|
||||||
if not hasattr(self, 'app_log_text') or not self.app_log_text:
|
if not hasattr(self, 'app_log_text') or not self.app_log_text:
|
||||||
|
|||||||
191
todo.md
191
todo.md
@ -49,4 +49,193 @@ START_JSON_OUTPUT
|
|||||||
END_JSON_OUTPUT
|
END_JSON_OUTPUT
|
||||||
(gdb) dump_json myStruct
|
(gdb) dump_json myStruct
|
||||||
START_JSON_OUTPUT
|
START_JSON_OUTPUT
|
||||||
DEBUG_STRING_TRACE: _serializ
|
DEBUG_STRING_TRACE: _serializ
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Ottima idea! La funzionalità dei "profili di lancio automatici" aggiungerebbe un valore enorme al tuo strumento, trasformandolo da un helper interattivo a uno strumento di data-collection e analisi automatizzata durante il debug. Questo è particolarmente utile per:
|
||||||
|
|
||||||
|
* **Regression Testing**: Verificare che determinate strutture dati mantengano valori attesi in punti specifici dell'esecuzione dopo modifiche al codice.
|
||||||
|
* **Performance Profiling (rudimentale)**: Collezionare dati in vari punti per capire come evolvono.
|
||||||
|
* **Debugging di Bug Complessi**: Automatizzare la raccolta di informazioni in scenari difficili da riprodurre manualmente più volte.
|
||||||
|
* **Snapshotting**: Creare "istantanee" dello stato di variabili chiave in momenti cruciali.
|
||||||
|
|
||||||
|
Analizziamo come realizzarla e poi vediamo se ci sono altre idee.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Proposta di Realizzazione: Profili di Lancio Automatici
|
||||||
|
|
||||||
|
Questa è una funzionalità complessa che toccherà diversi aspetti dell'applicazione. Ecco una possibile suddivisione e come potremmo implementarla:
|
||||||
|
|
||||||
|
**1. Definizione della Struttura Dati per i Profili:**
|
||||||
|
|
||||||
|
Ogni profilo dovrà contenere:
|
||||||
|
|
||||||
|
* **Nome del Profilo**: Un identificatore univoco per l'utente (es. "Test Vettore Con Input X", "Snapshot Inizializzazione Modulo Y").
|
||||||
|
* **Percorso Eseguibile Target**: Il percorso dell'eseguibile specifico per questo profilo.
|
||||||
|
* **Parametri di Avvio**: Argomenti da passare all'eseguibile.
|
||||||
|
* **Lista di "Azioni di Debug"**: Questa è la parte centrale. Ogni azione potrebbe essere una tupla o un dizionario che specifica:
|
||||||
|
* `breakpoint_location`: La stringa del breakpoint (es. `main`, `file.cpp:123`, `MyClass::myMethod`).
|
||||||
|
* `variables_to_dump`: Una lista di stringhe, ognuna rappresentante una variabile o espressione da dumpare a quel breakpoint.
|
||||||
|
* `output_format`: Per ogni variabile, o globalmente per l'azione, se salvare in "JSON" o "CSV".
|
||||||
|
* `output_directory`: Directory dove salvare i file dumpati per questa azione (potrebbe essere globale per il profilo o specifica per azione/breakpoint).
|
||||||
|
* `output_filename_pattern`: Un pattern per generare i nomi dei file (es. `{app_name}_{timestamp}_{breakpoint}_{variable}.{format}`).
|
||||||
|
* `continue_after_dump`: Un booleano che indica se GDB deve continuare l'esecuzione (`continue`) dopo aver effettuato il dump a questo breakpoint, o fermarsi in attesa di interazione (meno utile per un profilo automatico, ma potrebbe esserci un'opzione "run to end" o "run until next profile breakpoint").
|
||||||
|
* `max_hits_before_continue` (opzionale, avanzato): Per breakpoint che potrebbero essere colpiti più volte in un ciclo, quante volte fare il dump prima di continuare automaticamente.
|
||||||
|
|
||||||
|
**2. Modifiche alla Gestione della Configurazione (`AppSettings` in `core/config_manager.py`):**
|
||||||
|
|
||||||
|
* `AppSettings` dovrà essere esteso per salvare e caricare una lista di questi profili. Potremmo avere una nuova categoria `"profiles"` nel dizionario `self._settings`.
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"general": { ... },
|
||||||
|
"timeouts": { ... },
|
||||||
|
"dumper_options": { ... },
|
||||||
|
"gui": { ... },
|
||||||
|
"profiles": [
|
||||||
|
{
|
||||||
|
"profile_name": "TestVector",
|
||||||
|
"target_executable": "/path/to/my_app.exe",
|
||||||
|
"program_parameters": "-testmode",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"breakpoint_location": "main",
|
||||||
|
"variables_to_dump": ["argc", "argv[0]"],
|
||||||
|
"output_format": "json",
|
||||||
|
"output_directory": "/tmp/debug_dumps/TestVector",
|
||||||
|
"filename_pattern": "{breakpoint}_{variable}_{timestamp}.json",
|
||||||
|
"continue_after_dump": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"breakpoint_location": "MyClass::processData:125",
|
||||||
|
"variables_to_dump": ["this->internal_vector", "local_status_flag"],
|
||||||
|
"output_format": "csv", // Magari per il vettore
|
||||||
|
"output_directory": "/tmp/debug_dumps/TestVector",
|
||||||
|
"filename_pattern": "{breakpoint}_{variable}_{timestamp}.csv",
|
||||||
|
"continue_after_dump": false // Si ferma qui
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// ... altri profili ...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Nuova Interfaccia Utente per la Gestione dei Profili (`gui/profile_manager_window.py`):**
|
||||||
|
|
||||||
|
* Servirà una nuova finestra `Toplevel` (es. `ProfileManagerWindow`) accessibile da un menu nella `GDBGui` (es. "Profiles" > "Manage Profiles...").
|
||||||
|
* Questa finestra permetterà di:
|
||||||
|
* Visualizzare la lista dei profili esistenti.
|
||||||
|
* Creare un nuovo profilo.
|
||||||
|
* Modificare un profilo esistente (nome, eseguibile, parametri, e la lista di azioni di debug).
|
||||||
|
* Duplicare un profilo.
|
||||||
|
* Eliminare un profilo.
|
||||||
|
* Per la gestione delle "Azioni di Debug" all'interno di un profilo, potrebbe essere necessario un sub-dialog o una lista editabile complessa (es. `tkinter.Listbox` con pulsanti per aggiungere/modificare/rimuovere azioni, e ogni azione apre un altro piccolo form per i suoi dettagli: breakpoint, variabili, formato output, ecc.). Qui `tkinter.Treeview` potrebbe essere utile per visualizzare la struttura profilo->azione.
|
||||||
|
|
||||||
|
**4. Modifiche alla Finestra Principale (`gui/main_window.py`):**
|
||||||
|
|
||||||
|
* Un nuovo menu "Profiles" con:
|
||||||
|
* "Manage Profiles..." (apre `ProfileManagerWindow`).
|
||||||
|
* Una lista dinamica dei profili salvati. Selezionandone uno, si potrebbe popolare la GUI principale con l'eseguibile e i parametri del profilo (per un lancio manuale modificato) OPPURE avere un'opzione "Run Profile (Automatic)".
|
||||||
|
* Un nuovo pulsante/modalità "Run Automated Profile" o un selettore di profili con un pulsante "Run Selected Profile".
|
||||||
|
|
||||||
|
**5. Logica di Esecuzione Automatica del Profilo (`core/profile_executor.py` o integrata in `gdb_controller.py`):**
|
||||||
|
|
||||||
|
Questa è la parte più complessa. Quando si lancia un profilo in modalità automatica:
|
||||||
|
|
||||||
|
* La `GDBGui` (o una nuova classe `ProfileExecutor`) prenderà il profilo selezionato.
|
||||||
|
* **Avvierà `GDBSession`** con l'eseguibile e le opzioni del profilo.
|
||||||
|
* **Imposterà TUTTI i breakpoint** definiti nelle azioni del profilo. È importante che GDB permetta di impostare breakpoint che potrebbero diventare attivi solo più tardi (es. in librerie caricate dinamicamente).
|
||||||
|
* **Avvierà il programma (`run` con i parametri del profilo).**
|
||||||
|
* **Ciclo di Eventi GDB Modificato**: Invece di aspettare l'input dell'utente, il `ProfileExecutor` dovrà reagire agli eventi di GDB (principalmente "breakpoint hit").
|
||||||
|
* Quando un breakpoint del profilo viene raggiunto:
|
||||||
|
1. Identificare quale "azione di debug" del profilo corrisponde al breakpoint corrente.
|
||||||
|
2. Per ogni variabile in `variables_to_dump` per quell'azione:
|
||||||
|
* Inviare il comando `dump_json nome_variabile` a `GDBSession`.
|
||||||
|
* Ricevere il JSON (o l'errore).
|
||||||
|
* Formattare il nome del file di output usando `output_filename_pattern` (sostituendo placeholder come `{app_name}`, `{timestamp}`, `{breakpoint}`, `{variable}`).
|
||||||
|
* Salvare il JSON/CSV nella `output_directory` specificata, usando `output_formatter.py`.
|
||||||
|
3. Se l'azione specifica `continue_after_dump: true`, inviare un comando `continue` a GDB.
|
||||||
|
4. Se `continue_after_dump: false` (o se è l'ultima azione del profilo e si vuole terminare), la sessione GDB potrebbe fermarsi o uscire.
|
||||||
|
* **Gestione Fine Esecuzione/Interruzione**:
|
||||||
|
* Il ciclo automatico continua finché il programma target termina, o GDB si chiude, o l'utente interrompe manualmente il processo automatico (serve un pulsante "Stop Automated Run" nella GUI).
|
||||||
|
* Se il programma termina normalmente o va in crash prima di raggiungere tutti i breakpoint previsti, questo dovrebbe essere loggato.
|
||||||
|
* **Feedback all'Utente**: La GUI dovrebbe mostrare lo stato dell'esecuzione automatica (es. "Running profile 'X'...", "Hit breakpoint Y, dumping Z...", "Profile completed/terminated."). La scheda "Application Log" e "GDB Raw Output" saranno cruciali.
|
||||||
|
|
||||||
|
**6. Modifiche a `GDBSession` (`core/gdb_controller.py`):**
|
||||||
|
|
||||||
|
* Potrebbe non necessitare di modifiche enormi se la logica di esecuzione del profilo è gestita esternamente da una classe `ProfileExecutor` che usa `GDBSession` come fa ora `GDBGui`.
|
||||||
|
* Tuttavia, `GDBSession` deve già supportare il passaggio dei timeout e delle `dumper_options` in modo flessibile, cosa che abbiamo già fatto.
|
||||||
|
|
||||||
|
**Come Strutturare il Lavoro:**
|
||||||
|
|
||||||
|
1. **Fase 1: Struttura Dati e Configurazione.**
|
||||||
|
* Definire la struttura JSON esatta per i profili.
|
||||||
|
* Estendere `AppSettings` per salvare/caricare la lista di profili.
|
||||||
|
2. **Fase 2: UI per la Gestione dei Profili.**
|
||||||
|
* Creare `ProfileManagerWindow` per CRUD (Create, Read, Update, Delete) dei profili e delle loro azioni. Questa è una grossa fetta di lavoro UI.
|
||||||
|
3. **Fase 3: Logica di Esecuzione del Profilo.**
|
||||||
|
* Creare la classe `ProfileExecutor` (o integrare in `GDBGui` se si preferisce, ma separare potrebbe essere più pulito).
|
||||||
|
* Implementare il ciclo di esecuzione automatica, gestione dei breakpoint, dump e salvataggio automatico dei file.
|
||||||
|
4. **Fase 4: Integrazione con la GUI Principale.**
|
||||||
|
* Aggiungere il menu "Profiles" e i controlli per lanciare un profilo in modalità automatica.
|
||||||
|
* Fornire feedback all'utente durante l'esecuzione automatica.
|
||||||
|
|
||||||
|
**Considerazioni Aggiuntive:**
|
||||||
|
|
||||||
|
* **Validazione dei Profili**: Assicurarsi che i percorsi e i breakpoint siano validi quando si salva un profilo.
|
||||||
|
* **Templating Nomi File**: Usare `string.Template` o f-string per i pattern dei nomi dei file.
|
||||||
|
* **Error Handling**: Gestione robusta degli errori durante l'esecuzione automatica (es. GDB crasha, variabile non trovata, impossibile scrivere file).
|
||||||
|
* **Interruzione Utente**: Un modo pulito per l'utente di interrompere un'esecuzione di profilo lunga.
|
||||||
|
* **Interfaccia per Azioni Complesse**: La UI per definire le "azioni di debug" (breakpoint -> variabili -> formato -> output) deve essere ben pensata per non risultare troppo confusionaria. Un approccio master-detail potrebbe funzionare.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Altre Idee Oltre ai Profili?
|
||||||
|
|
||||||
|
Sì, partendo dalla base attuale, ecco alcune altre direzioni o miglioramenti possibili, alcuni più semplici, altri più complessi:
|
||||||
|
|
||||||
|
1. **Miglioramento dell'Analisi dell'Output JSON nella GUI**:
|
||||||
|
* **Visualizzatore JSON ad Albero**: Invece di solo testo, usare un widget `tkinter.ttk.Treeview` per visualizzare il JSON in modo espandibile/collassabile. Questo renderebbe la navigazione di strutture complesse molto più semplice direttamente nell'app. Esistono librerie o esempi per questo.
|
||||||
|
* **Filtri e Ricerca nel JSON**: Possibilità di cercare chiavi o valori nell'output JSON visualizzato.
|
||||||
|
|
||||||
|
2. **Integrazione più Profonda con i Comandi GDB**:
|
||||||
|
* **Console GDB Interattiva Semplificata**: Una piccola entry box nella GUI per inviare comandi GDB arbitrari e vedere l'output (oltre ai pulsanti predefiniti). Questo darebbe più flessibilità senza dover passare al terminale GDB completo.
|
||||||
|
* **Visualizzazione Stack Trace**: Quando si è a un breakpoint, mostrare lo stack trace corrente in un'area dedicata della GUI.
|
||||||
|
* **Visualizzazione Registri**: Mostrare i valori dei registri della CPU.
|
||||||
|
|
||||||
|
3. **Supporto per GDB Remote Debugging**:
|
||||||
|
* Aggiungere opzioni di configurazione per connettersi a un `gdbserver` (host, porta).
|
||||||
|
* Modificare `GDBSession` per supportare l'avvio di GDB con target remoto.
|
||||||
|
|
||||||
|
4. **Scripting Utente Avanzato**:
|
||||||
|
* Permettere agli utenti di specificare piccoli snippet di script Python GDB da eseguire a determinati breakpoint, oltre al semplice dump di variabili (questo si sovrappone un po' alla logica dei profili ma potrebbe essere più generico).
|
||||||
|
|
||||||
|
5. **Miglioramenti alla Gestione degli Errori e Feedback Utente**:
|
||||||
|
* Diagnostica più dettagliata quando GDB fallisce l'avvio o un comando.
|
||||||
|
* Suggerimenti più specifici basati sui messaggi di errore comuni di GDB.
|
||||||
|
|
||||||
|
6. **"Watch Window" Semplice**:
|
||||||
|
* Un'area dove l'utente può inserire alcune variabili che vengono automaticamente interrogate e aggiornate ogni volta che GDB si ferma (step, breakpoint). Richiederebbe un polling più attivo di GDB.
|
||||||
|
|
||||||
|
7. **Internazionalizzazione (i18n)**:
|
||||||
|
* Supporto per più lingue nell'interfaccia utente (anche se hai detto di mantenere i commenti in inglese, la UI potrebbe essere multilingua).
|
||||||
|
|
||||||
|
**Discussione:**
|
||||||
|
|
||||||
|
La tua idea sui **profili di lancio automatici è decisamente la più impattante e utile** tra le possibili evoluzioni. Trasforma lo strumento in qualcosa di molto più potente per l'automazione.
|
||||||
|
|
||||||
|
**Prima di iniziare a scrivere codice per i profili, ti suggerirei di:**
|
||||||
|
|
||||||
|
1. **Confermare la Struttura Dati del Profilo**: Siamo d'accordo sulla struttura JSON/dizionario che ho proposto per un profilo? Ci sono campi mancanti o da modificare?
|
||||||
|
2. **Prioritizzare le Funzionalità del Profilo**: L'intera gestione dei profili è un grosso lavoro. Potremmo iniziare con una versione più semplice?
|
||||||
|
* Ad esempio, Fase 1: solo un profilo alla volta, senza UI di gestione complessa, ma definito direttamente nel file JSON di configurazione.
|
||||||
|
* Oppure, Fase 1: UI per un singolo profilo, e la logica di esecuzione per quel singolo profilo.
|
||||||
|
* Poi, Fase 2: UI per gestire più profili.
|
||||||
|
3. **Discutere la UI per le "Azioni di Debug"**: Come immagini che l'utente configuri "a questo breakpoint, dumpa queste N variabili, con questi formati, in questa directory"? Questa è una sfida UI non banale se vogliamo che sia user-friendly.
|
||||||
|
|
||||||
|
Penso che la tua idea sia eccellente. Se sei d'accordo, potremmo iniziare a dettagliare la Fase 1 dei profili (struttura dati e `AppSettings`).
|
||||||
|
|
||||||
|
Cosa ne pensi di questa analisi e della proposta di realizzazione per i profili? E le altre idee ti danno qualche spunto o preferisci concentrarti sui profili per ora?
|
||||||
Loading…
Reference in New Issue
Block a user