diff --git a/gdbui_server/main.py b/gdbui_server/main.py index 6539127..15f9e56 100644 --- a/gdbui_server/main.py +++ b/gdbui_server/main.py @@ -1,6 +1,7 @@ from flask import Flask, request, jsonify from pygdbmi.gdbcontroller import GdbController from flask_cors import CORS +from werkzeug.utils import secure_filename import subprocess import os @@ -8,11 +9,20 @@ cors = CORS(app) app.config['CORS_HEADERS'] = 'Content-Type' -gdb_controller = None -program_name = None +OUTPUT_DIR = 'output' +os.makedirs(OUTPUT_DIR, exist_ok=True) -def execute_gdb_command(command): - response2 = gdb_controller.write(command) +# Per-session state: maps X-Session-ID header to session dict +_sessions = {} + +def get_session(session_id): + """Get or create a session dict for the given session ID.""" + if session_id not in _sessions: + _sessions[session_id] = {'controller': None, 'program': None} + return _sessions[session_id] + +def execute_gdb_command(session, command): + response2 = session['controller'].write(command) if response2 is None: raise RuntimeError("No response from GDB controller") strm = "" @@ -23,23 +33,24 @@ def execute_gdb_command(command): def ensure_exe_extension(name): return name if name.endswith('.exe') else name + '.exe' -def start_gdb_session(program): - global gdb_controller, program_name - program_name = program +def start_gdb_session(session, program): + session['program'] = program try: - gdb_controller = GdbController() + session['controller'] = GdbController() except Exception as e: raise RuntimeError(f"Failed to initialize GDB controller: {e}") try: - response = gdb_controller.write(f"-file-exec-and-symbols {os.path.join('output/', ensure_exe_extension(program_name))}") + response = session['controller'].write( + f"-file-exec-and-symbols {os.path.join(OUTPUT_DIR, ensure_exe_extension(program))}" + ) if response is None: raise RuntimeError("No response from GDB controller") except Exception as e: raise RuntimeError(f"Failed to set program file: {e}") - + try: - response = gdb_controller.write("run") + response = session['controller'].write("run") if response is None: raise RuntimeError("No response from GDB controller") except Exception as e: @@ -47,15 +58,16 @@ def start_gdb_session(program): @app.route('/gdb_command', methods=['POST']) def gdb_command(): - global program_name data = request.get_json() command = data.get('command') file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command(command) + result = execute_gdb_command(session, command) response = { 'success': True, 'result': result, @@ -67,39 +79,52 @@ def gdb_command(): 'error': str(e), 'code': f"execute_gdb_command('{command}')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/compile', methods=['POST']) def compile_code(): - global program_name data = request.get_json() code = data.get('code') - name = data.get('name') + raw_name = data.get('name', '') + name = secure_filename(raw_name) + + if not name or name != raw_name: + return jsonify({'success': False, 'output': 'Invalid or missing file name.'}), 400 + + cpp_path = os.path.join(OUTPUT_DIR, f'{name}.cpp') + exe_path = os.path.join(OUTPUT_DIR, f'{name}.exe') - with open(f'{name}.cpp', 'w') as file: - file.write(code) + with open(cpp_path, 'w') as f: + f.write(code) - result = subprocess.run(['g++', f'{name}.cpp', '-o', f'output/{name}.exe'], capture_output=True, text=True) + result = subprocess.run( + ['g++', cpp_path, '-o', exe_path], + capture_output=True, text=True + ) if result.returncode == 0: - program_name = None + # Invalidate all sessions that were using this program + for session in _sessions.values(): + if session['program'] == name: + session['program'] = None return jsonify({'success': True, 'output': 'Compilation successful.'}) else: - return jsonify({'success': False, 'output': result.stderr}) + return jsonify({'success': False, 'output': result.stderr}), 400 -@app.route('/upload_file', methods=['POST']) +@app.route('/upload_file', methods=['POST']) def upload_file(): if 'file' not in request.files or 'name' not in request.form: return jsonify({'success': False, 'error': 'No file or name provided'}), 400 file = request.files['file'] - name = request.form['name'] + name = secure_filename(request.form['name']) - if file.filename == '': + if not name or file.filename == '': return jsonify({'success': False, 'error': 'No selected file'}), 400 - file_path = os.path.join('output/', ensure_exe_extension(name)) + file_path = os.path.join(OUTPUT_DIR, ensure_exe_extension(name)) file.save(file_path) return jsonify({'success': True, 'message': 'File uploaded successfully', 'file_path': file_path}) @@ -107,15 +132,16 @@ def upload_file(): @app.route('/set_breakpoint', methods=['POST']) def set_breakpoint(): - global program_name data = request.get_json() location = data.get('location') file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command(f"break {location}") + result = execute_gdb_command(session, f"break {location}") response = { 'success': True, 'result': result, @@ -127,19 +153,21 @@ def set_breakpoint(): 'error': str(e), 'code': f"execute_gdb_command('break {location}')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/info_breakpoints', methods=['POST']) def info_breakpoints(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("info breakpoints") + result = execute_gdb_command(session, "info breakpoints") response = { 'success': True, 'result': result, @@ -151,19 +179,21 @@ def info_breakpoints(): 'error': str(e), 'code': "execute_gdb_command('info breakpoints')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/stack_trace', methods=['POST']) def stack_trace(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("bt") + result = execute_gdb_command(session, "bt") response = { 'success': True, 'result': result, @@ -175,19 +205,21 @@ def stack_trace(): 'error': str(e), 'code': "execute_gdb_command('bt')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/threads', methods=['POST']) def threads(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("info threads") + result = execute_gdb_command(session, "info threads") response = { 'success': True, 'result': result, @@ -199,19 +231,21 @@ def threads(): 'error': str(e), 'code': "execute_gdb_command('info threads')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/get_registers', methods=['POST']) def get_registers(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("info registers") + result = execute_gdb_command(session, "info registers") response = { 'success': True, 'result': result, @@ -223,19 +257,21 @@ def get_registers(): 'error': str(e), 'code': "execute_gdb_command('info registers')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/get_locals', methods=['POST']) def get_locals(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("info locals") + result = execute_gdb_command(session, "info locals") response = { 'success': True, 'result': result, @@ -247,19 +283,21 @@ def get_locals(): 'error': str(e), 'code': "execute_gdb_command('info locals')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/run', methods=['POST']) def run_program(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("run") + result = execute_gdb_command(session, "run") response = { 'success': True, 'result': result, @@ -271,19 +309,21 @@ def run_program(): 'error': str(e), 'code': "execute_gdb_command('run')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/memory_map', methods=['POST']) def memory_map(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("info proc mappings") + result = execute_gdb_command(session, "info proc mappings") response = { 'success': True, 'result': result, @@ -295,19 +335,21 @@ def memory_map(): 'error': str(e), 'code': "execute_gdb_command('info proc mappings')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/continue', methods=['POST']) def continue_execution(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("continue") + result = execute_gdb_command(session, "continue") response = { 'success': True, 'result': result, @@ -319,19 +361,21 @@ def continue_execution(): 'error': str(e), 'code': "execute_gdb_command('continue')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/step_over', methods=['POST']) def step_over(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("next") + result = execute_gdb_command(session, "next") response = { 'success': True, 'result': result, @@ -343,19 +387,21 @@ def step_over(): 'error': str(e), 'code': "execute_gdb_command('next')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/step_into', methods=['POST']) def step_into(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("step") + result = execute_gdb_command(session, "step") response = { 'success': True, 'result': result, @@ -367,19 +413,21 @@ def step_into(): 'error': str(e), 'code': "execute_gdb_command('step')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/step_out', methods=['POST']) def step_out(): - global program_name data = request.get_json() file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command("finish") + result = execute_gdb_command(session, "finish") response = { 'success': True, 'result': result, @@ -391,20 +439,22 @@ def step_out(): 'error': str(e), 'code': "execute_gdb_command('finish')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/add_watchpoint', methods=['POST']) def add_watchpoint(): - global program_name data = request.get_json() variable = data.get('variable') file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command(f"watch {variable}") + result = execute_gdb_command(session, f"watch {variable}") response = { 'success': True, 'result': result, @@ -416,20 +466,22 @@ def add_watchpoint(): 'error': str(e), 'code': f"execute_gdb_command('watch {variable}')" } - + return jsonify(response), 500 + return jsonify(response) @app.route('/delete_breakpoint', methods=['POST']) def delete_breakpoint(): - global program_name data = request.get_json() breakpoint_number = data.get('breakpoint_number') file = data.get('name') - if program_name != file: - start_gdb_session(f'{file}') + session_id = request.headers.get('X-Session-ID', 'default') + session = get_session(session_id) + if session['program'] != file: + start_gdb_session(session, file) try: - result = execute_gdb_command(f"delete {breakpoint_number}") + result = execute_gdb_command(session, f"delete {breakpoint_number}") response = { 'success': True, 'result': result, @@ -441,9 +493,14 @@ def delete_breakpoint(): 'error': str(e), 'code': f"execute_gdb_command('delete {breakpoint_number}')" } - + return jsonify(response), 500 + return jsonify(response) +@app.route('/health', methods=['GET']) +def health(): + return jsonify({'status': 'ok', 'active_sessions': len(_sessions)}) + if __name__ == '__main__': app.run(host='0.0.0.0', port=10000) diff --git a/gdbui_server/output/bad_program.cpp b/gdbui_server/output/bad_program.cpp new file mode 100644 index 0000000..760abfa --- /dev/null +++ b/gdbui_server/output/bad_program.cpp @@ -0,0 +1 @@ +this is not valid c++ \ No newline at end of file diff --git a/gdbui_server/output/test_program.cpp b/gdbui_server/output/test_program.cpp new file mode 100644 index 0000000..a9546ba --- /dev/null +++ b/gdbui_server/output/test_program.cpp @@ -0,0 +1,2 @@ +#include +int main() { std::cout << 'Hello, World!'; return 0; } \ No newline at end of file diff --git a/gdbui_server/output/test_program.exe b/gdbui_server/output/test_program.exe new file mode 100755 index 0000000..441a624 Binary files /dev/null and b/gdbui_server/output/test_program.exe differ diff --git a/gdbui_server/output/test_program2.cpp b/gdbui_server/output/test_program2.cpp new file mode 100644 index 0000000..c0b8e4b --- /dev/null +++ b/gdbui_server/output/test_program2.cpp @@ -0,0 +1,2 @@ +#include +int main() { std::cout << "Hello, Universe!"; return 0; } \ No newline at end of file diff --git a/gdbui_server/output/test_program2.exe b/gdbui_server/output/test_program2.exe new file mode 100755 index 0000000..1b8fe19 Binary files /dev/null and b/gdbui_server/output/test_program2.exe differ diff --git a/webapp/.env.development b/webapp/.env.development new file mode 100644 index 0000000..d407c8f --- /dev/null +++ b/webapp/.env.development @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://127.0.0.1:10000 diff --git a/webapp/.env.production b/webapp/.env.production new file mode 100644 index 0000000..64a1208 --- /dev/null +++ b/webapp/.env.production @@ -0,0 +1 @@ +VITE_API_BASE_URL=https://your-gdbui-server.com diff --git a/webapp/src/App.jsx b/webapp/src/App.jsx index 4145449..b48d10a 100644 --- a/webapp/src/App.jsx +++ b/webapp/src/App.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { Route, Routes } from "react-router-dom"; +import { Route, Routes, Navigate } from "react-router-dom"; import Debug from "./pages/Debug/Debug"; import Threads from "./components/GdbComponents/Threads/Threads"; import LocalVariable from "./components/GdbComponents/LocalVariable/LocalVariable"; @@ -30,14 +30,16 @@ const App = () => { /> - }> + }> } /> } /> } /> } /> } /> - {/* You can add more routes here */} + } /> + } /> + } />