From 29c5ecb5388a5e52b048c06c4bf1f3177ff0a913 Mon Sep 17 00:00:00 2001 From: Al Poole Date: Wed, 8 Nov 2017 21:26:42 +0000 Subject: [PATCH] edi_debug: improve debugging support. This improves support and simplifies some of the debugging tasks. --- src/bin/edi_config.c | 7 + src/bin/edi_config.h | 2 + src/bin/edi_debug.c | 226 ++++++++++++++++++++++ src/bin/edi_debug.h | 103 ++++++++++ src/bin/edi_debugpanel.c | 335 +++++++++------------------------ src/bin/edi_debugpanel.h | 4 +- src/bin/edi_main.c | 35 +++- src/bin/edi_private.h | 2 + src/bin/meson.build | 2 + src/bin/screens/edi_settings.c | 122 +++++++++--- 10 files changed, 563 insertions(+), 275 deletions(-) create mode 100644 src/bin/edi_debug.c create mode 100644 src/bin/edi_debug.h diff --git a/src/bin/edi_config.c b/src/bin/edi_config.c index c5119fb..6ef604c 100644 --- a/src/bin/edi_config.c +++ b/src/bin/edi_config.c @@ -85,6 +85,12 @@ _edi_project_config_dir_get(void) return dir; } +const char * +_edi_project_config_debug_command_get(void) +{ + return _edi_project_config->debug_command; +} + /* local functions */ static Edi_Config_DD * _edi_config_descriptor_new(const char *name, int size) @@ -278,6 +284,7 @@ _edi_config_init(void) EDI_CONFIG_VAL(D, T, launch.path, EET_T_STRING); EDI_CONFIG_VAL(D, T, launch.args, EET_T_STRING); + EDI_CONFIG_VAL(D, T, debug_command, EET_T_STRING); EDI_CONFIG_VAL(D, T, user_fullname, EET_T_STRING); EDI_CONFIG_VAL(D, T, user_email, EET_T_STRING); diff --git a/src/bin/edi_config.h b/src/bin/edi_config.h index d6a19fd..9a265bb 100644 --- a/src/bin/edi_config.h +++ b/src/bin/edi_config.h @@ -88,6 +88,7 @@ struct _Edi_Project_Config } gui; Edi_Project_Config_Launch launch; + Eina_Stringshare *debug_command; Eina_Stringshare *user_fullname; Eina_Stringshare *user_email; @@ -103,6 +104,7 @@ extern Edi_Project_Config *_edi_project_config; Eina_Bool _edi_config_init(void); Eina_Bool _edi_config_shutdown(void); const char *_edi_config_dir_get(void); +const char *_edi_project_config_debug_command_get(void); // Global configuration handling diff --git a/src/bin/edi_debug.c b/src/bin/edi_debug.c new file mode 100644 index 0000000..3a9b855 --- /dev/null +++ b/src/bin/edi_debug.c @@ -0,0 +1,226 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined (__APPLE__) || defined(__OpenBSD__) + #include + #include + #include + #include +#endif +#if defined(__OpenBSD__) + #include +#endif + +#include +#include +#include + +#include "edi_debug.h" +#include "edi_config.h" +#include "edi_private.h" + +Edi_Debug_Tool _debugger_tools[] = { + { "gdb", "gdb", NULL, "run\n", "c\n", "set args %s" }, + { "lldb", "lldb", NULL, "run\n", "c\n", "settings set target.run-args %s" }, + { "pdb", "pdb", NULL, NULL, "c\n", "run %s" }, + { "memcheck", "valgrind", "--tool=memcheck", NULL, NULL, NULL }, + { "massif", "valgrind", "--tool=massif", NULL, NULL, NULL }, + { "callgrind", "valgrind", "--tool=callgrind", NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL, NULL, NULL }, +}; + +Edi_Debug *_debugger = NULL; + +Edi_Debug *edi_debug_new(void) +{ + _debugger = calloc(1, sizeof(Edi_Debug)); + return _debugger; +} + +Edi_Debug *edi_debug_get(void) +{ + return _debugger; +} + +Edi_Debug_Tool *edi_debug_tools_get(void) +{ + return _debugger_tools; +} + +Edi_Debug_Tool *edi_debug_tool_get(const char *name) +{ + int i; + + for (i = 0; _debugger_tools[i].name && name; i++) + { + if (!strcmp(_debugger_tools[i].name, name) && + ecore_file_app_installed(_debugger_tools[i].exec)) + return &_debugger_tools[i]; + } + + // Fallback, but not installed. + if (name || !ecore_file_app_installed(_debugger_tools[0].exec)) + return NULL; + + // Fallback to first. + return &_debugger_tools[0]; +} + +#if defined(__FreeBSD__) || defined(__DragonFly__) +static long int +_sysctlfromname(const char *name, void *mib, int depth, size_t *len) +{ + long int result; + + if (sysctlnametomib(name, mib, len) < 0) return -1; + *len = sizeof(result); + if (sysctl(mib, depth, &result, len, NULL, 0) < 0) return -1; + + return result; +} +#endif + +/* Get the process ID of the child process being debugged in *our* session */ +int edi_debug_process_id(Edi_Debug *debugger) +{ + int my_pid, child_pid = -1; +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined (__APPLE__) || defined(__OpenBSD__) + struct kinfo_proc kp; + int mib[6]; + size_t len; + int max_pid, i; + + if (!debugger->program_name) return -1; + if (!debugger->exe) return -1; + +#if defined(__FreeBSD__) || defined(__DragonFly__) + len = sizeof(max_pid); + max_pid = _sysctlfromname("kern.lastpid", mib, 2, &len); +#elif defined(PID_MAX) + max_pid = PID_MAX; +#else + max_pid = 99999; +#endif + my_pid = ecore_exe_pid_get(debugger->exe); + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) + if (sysctlnametomib("kern.proc.pid", mib, &len) < 0) return -1; +#elif defined(__OpenBSD__) + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[4] = sizeof(struct kinfo_proc); + mib[5] = 1; +#endif + + for (i = my_pid; i <= max_pid; i++) + { + mib[3] = i; + len = sizeof(kp); +#if defined(__OpenBSD__) + if (sysctl(mib, 6, &kp, &len, NULL, 0) == -1) continue; +#else + if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) continue; +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) + if (kp.ki_ppid != my_pid) continue; + if (strcmp(debugger->program_name, kp.ki_comm)) continue; + child_pid = kp.ki_pid; + if (kp.ki_stat == SRUN || kp.ki_stat == SSLEEP) + debugger->state = EDI_DEBUG_PROCESS_ACTIVE; + else + debugger->state = EDI_DEBUG_PROCESS_SLEEPING; +#elif defined(__OpenBSD__) + if (kp.p_ppid != my_pid) continue; + if (strcmp(debugger->program_name, kp.p_comm)) continue; + child_pid = kp.p_pid; + + if (kp.p_stat == SRUN || kp.p_stat == SSLEEP) + debugger->state = EDI_DEBUG_PROCESS_ACTIVE; + else + debugger->state = EDI_DEBUG_PROCESS_SLEEPING; +#else /* APPLE */ + if (kp.kp_proc.p_oppid != my_pid) continue; + if (strcmp(debugger->program_name, kp.kp_proc.p_comm)) continue; + child_pid = kp.kp_proc.p_pid; + if (kp.kp_proc.p_stat == SRUN || kp.kp_proc.p_stat == SSLEEP) + debugger->state = EDI_DEBUG_PROCESS_ACTIVE; + else + debugger->state = EDI_DEBUG_PROCESS_SLEEPING; +#endif + break; + } +#else + Eina_List *files, *l; + const char *temp_name; + char path[PATH_MAX]; + char buf[4096]; + char *p, *name, *end; + FILE *f; + int count, parent_pid, pid; + + if (!debugger->program_name) + return -1; + + if (!debugger->exe) return -1; + + my_pid = ecore_exe_pid_get(debugger->exe); + + files = ecore_file_ls("/proc"); + + EINA_LIST_FOREACH(files, l, name) + { + pid = atoi(name); + if (!pid) continue; + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + f = fopen(path, "r"); + if (!f) continue; + p = fgets(buf, sizeof(buf), f); + fclose(f); + if (!p) continue; + temp_name = ecore_file_file_get(buf); + if (!strcmp(temp_name, debugger->program_name)) + { + parent_pid = 0; + // Match success - program name with pid. + child_pid = pid; + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + f = fopen(path, "r"); + if (f) + { + count = 0; + p = fgets(buf, sizeof(buf), f); + while (*p++ != '\0') + { + if (p[0] == ' ') { count++; p++; } + if (count == 2) + { + if (p[0] == 'S' || p[0] == 'R') + debugger->state = EDI_DEBUG_PROCESS_ACTIVE; + else + debugger->state = EDI_DEBUG_PROCESS_SLEEPING; + } + if (count == 3) break; + } + end = strchr(p, ' '); + if (end) + { + *end = '\0'; + // parent pid matches - right process. + parent_pid = atoi(p); + } + fclose(f); + } + if (parent_pid == my_pid) + break; + } + } + + if (files) + eina_list_free(files); +#endif + return child_pid; +} + diff --git a/src/bin/edi_debug.h b/src/bin/edi_debug.h new file mode 100644 index 0000000..5a7829b --- /dev/null +++ b/src/bin/edi_debug.h @@ -0,0 +1,103 @@ +#ifndef EDI_DEBUG_H_ +# define EDI_DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * @brief These routines are used for managing debugging features. + */ + +typedef enum { + EDI_DEBUG_PROCESS_SLEEPING = 0, + EDI_DEBUG_PROCESS_ACTIVE +} Edi_Debug_Process_State; + +typedef struct _Edi_Debug_Tool { + const char *name; + const char *exec; + const char *arguments; + const char *command_start; + const char *command_continue; + const char *command_arguments; +} Edi_Debug_Tool; + +typedef struct _Edi_Debug { + Edi_Debug_Tool *tool; + const char *program_name; + Ecore_Exe *exe; + char cmd[1024]; + Edi_Debug_Process_State state; +} Edi_Debug; + +/** + * @brief Debug management functions. + * @defgroup Debug + * + * @{ + * + * Initialisation and management of debugging features. + * + */ + +/** + * Initialise debugger internals. + * + * @return pointer to initialized debug instance. + * + * @ingroup Debug + */ +Edi_Debug *edi_debug_new(void); + +/** + * Obtain pointer to initialized debug instance. + * + * @ingroup Debug + */ +Edi_Debug *edi_debug_get(void); + +/** + * Obtain process information of debugged process. + * + * @param debugger Edi_Debug instance. + * + * @return process id of debugged process that is child of running debugger. + * + * @ingroup Debug + */ +int edi_debug_process_id(Edi_Debug *debugger); + +/** + * Obtain debugging info for given program name. + * + * @param name The name of the tool used to obtain helper data for given program. + * + * @return Pointer to debugging information instance associated with its name. + * + * @ingroup Debug + */ +Edi_Debug_Tool *edi_debug_tool_get(const char *name); + +/** + * Return a pointer to the list of available debugging tools. + * + * @return Pointer to debugging information for all available tools. + * + * @ingroup Debug + */ +Edi_Debug_Tool *edi_debug_tools_get(void); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* EDI_DEBUG_H_ */ diff --git a/src/bin/edi_debugpanel.c b/src/bin/edi_debugpanel.c index c59f77f..35f290a 100644 --- a/src/bin/edi_debugpanel.c +++ b/src/bin/edi_debugpanel.c @@ -2,20 +2,11 @@ # include "config.h" #endif -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined (__APPLE__) || defined(__OpenBSD__) - #include - #include - #include - #include -#endif -#if defined(__OpenBSD__) - #include -#endif - #include #include #include +#include "edi_debug.h" #include "edi_debugpanel.h" #include "edi_config.h" @@ -27,21 +18,16 @@ #define LIBTOOL_COMMAND "libtool" #endif -static Ecore_Exe *_debug_exe = NULL; static Evas_Object *_info_widget, *_entry_widget, *_button_start, *_button_quit; static Evas_Object *_button_int, *_button_term; static Elm_Code *_debug_output; -#define DEBUG_PROCESS_SLEEPING 0 -#define DEBUG_PROCESS_ACTIVE 1 - static void _edi_debugpanel_line_cb(void *data EINA_UNUSED, const Efl_Event *event) { Elm_Code_Line *line; line = (Elm_Code_Line *)event->info; - if (line->data) line->status = ELM_CODE_STATUS_TYPE_ERROR; } @@ -58,10 +44,17 @@ static Eina_Bool _debugpanel_stdout_handler(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_Exe_Event_Data *ev; + Edi_Debug *debug; int idx; char *start, *end = NULL; + ev = event; + debug = edi_debug_get(); + + if (ev->exe != debug->exe) + return ECORE_CALLBACK_RENEW; + if (ev && ev->size) { if (!ev->data) return ECORE_CALLBACK_DONE; @@ -102,6 +95,7 @@ _edi_debugpanel_keypress_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Ob const char *text_markup; char *command, *text; Eina_Bool res; + Edi_Debug *debug = edi_debug_get(); event = event_info; @@ -111,7 +105,7 @@ _edi_debugpanel_keypress_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Ob if (!strcmp(event->key, "Return")) { - if (!_debug_exe) return; + if (!debug->exe) return; text_markup = elm_object_part_text_get(_entry_widget, NULL); text = elm_entry_markup_to_utf8(text_markup); @@ -119,7 +113,7 @@ _edi_debugpanel_keypress_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Ob { command = malloc(strlen(text) + 2); snprintf(command, strlen(text) + 2, "%s\n", text); - res = ecore_exe_send(_debug_exe, command, strlen(command)); + res = ecore_exe_send(debug->exe, command, strlen(command)); if (res) elm_code_file_line_append(_debug_output->file, command, strlen(command) - 1, NULL); @@ -130,189 +124,17 @@ _edi_debugpanel_keypress_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Ob } } -#if defined(__FreeBSD__) || defined(__DragonFly__) - -static long int -_sysctlfromname(const char *name, void *mib, int depth, size_t *len) -{ - long int result; - - if (sysctlnametomib(name, mib, len) < 0) return -1; - *len = sizeof(result); - if (sysctl(mib, depth, &result, len, NULL, 0) < 0) return -1; - - return result; -} -#endif - -/* Get the process ID of the child process being debugged in *our* session */ -static int -_edi_debug_process_id(int *state) -{ - const char *program_name; - int my_pid, child_pid = -1; -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined (__APPLE__) || defined(__OpenBSD__) - struct kinfo_proc kp; - int mib[6]; - size_t len; - int max_pid, i; - - if (!_edi_project_config->launch.path) return -1; - - if (!_debug_exe) return -1; - -#if defined(__FreeBSD__) || defined(__DragonFly__) - len = sizeof(max_pid); - max_pid = _sysctlfromname("kern.lastpid", mib, 2, &len); -#elif defined(PID_MAX) - max_pid = PID_MAX; -#else - max_pid = 99999; -#endif - my_pid = ecore_exe_pid_get(_debug_exe); - -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) - if (sysctlnametomib("kern.proc.pid", mib, &len) < 0) return -1; -#elif defined(__OpenBSD__) - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[4] = sizeof(struct kinfo_proc); - mib[5] = 1; -#endif - program_name = ecore_file_file_get(_edi_project_config->launch.path); - - for (i = my_pid; i <= max_pid; i++) - { - mib[3] = i; - len = sizeof(kp); -#if defined(__OpenBSD__) - if (sysctl(mib, 6, &kp, &len, NULL, 0) == -1) continue; -#else - if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) continue; -#endif - -#if defined(__FreeBSD__) || defined(__DragonFly__) - if (kp.ki_ppid != my_pid) continue; - if (strcmp(program_name, kp.ki_comm)) continue; - child_pid = kp.ki_pid; - if (state) - { - if (kp.ki_stat == SRUN || kp.ki_stat == SSLEEP) - *state = DEBUG_PROCESS_ACTIVE; - else - *state = DEBUG_PROCESS_SLEEPING; - } -#elif defined(__OpenBSD__) - if (kp.p_ppid != my_pid) continue; - if (strcmp(program_name, kp.p_comm)) continue; - child_pid = kp.p_pid; - - if (state) - { - if (kp.p_stat == SRUN || kp.p_stat == SSLEEP) - *state = DEBUG_PROCESS_ACTIVE; - else - *state = DEBUG_PROCESS_SLEEPING; - } -#else /* APPLE */ - if (kp.kp_proc.p_oppid != my_pid) continue; - if (strcmp(program_name, kp.kp_proc.p_comm)) continue; - child_pid = kp.kp_proc.p_pid; - if (state) - { - if (kp.kp_proc.p_stat == SRUN || kp.kp_proc.p_stat == SSLEEP) - *state = DEBUG_PROCESS_ACTIVE; - else - *state = DEBUG_PROCESS_SLEEPING; - } -#endif - break; - } -#else - Eina_List *files, *l; - const char *temp_name; - char path[PATH_MAX]; - char buf[4096]; - char *p, *name, *end; - FILE *f; - int count, parent_pid, pid; - - if (!_edi_project_config->launch.path) - return -1; - - if (!_debug_exe) return -1; - - my_pid = ecore_exe_pid_get(_debug_exe); - - program_name = ecore_file_file_get(_edi_project_config->launch.path); - - files = ecore_file_ls("/proc"); - - EINA_LIST_FOREACH(files, l, name) - { - pid = atoi(name); - if (!pid) continue; - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - f = fopen(path, "r"); - if (!f) continue; - p = fgets(buf, sizeof(buf), f); - fclose(f); - if (!p) continue; - temp_name = ecore_file_file_get(buf); - if (!strcmp(temp_name, program_name)) - { - parent_pid = 0; - // Match success - program name with pid. - child_pid = pid; - snprintf(path, sizeof(path), "/proc/%d/stat", pid); - f = fopen(path, "r"); - if (f) - { - count = 0; - p = fgets(buf, sizeof(buf), f); - while (*p++ != '\0') - { - if (p[0] == ' ') { count++; p++; } - if (count == 2) - { - if (state) - { - if (p[0] == 'S' || p[0] == 'R') - *state = DEBUG_PROCESS_ACTIVE; - else - *state = DEBUG_PROCESS_SLEEPING; - } - } - if (count == 3) break; - } - end = strchr(p, ' '); - if (end) - { - *end = '\0'; - // parent pid matches - right process. - parent_pid = atoi(p); - } - fclose(f); - } - if (parent_pid == my_pid) - break; - } - } - - if (files) - eina_list_free(files); -#endif - return child_pid; -} - static void _edi_debugpanel_bt_sigterm_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { pid_t pid; Evas_Object *ico_int; + Edi_Debug *debug; - pid = _edi_debug_process_id(NULL); + debug = edi_debug_get(); + if (!debug) return; + + pid = edi_debug_process_id(debug); if (pid <= 0) return; ico_int = elm_icon_add(_button_int); @@ -323,13 +145,13 @@ _edi_debugpanel_bt_sigterm_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUS } static void -_edi_debugpanel_icons_update(int state) +_edi_debugpanel_icons_update(Edi_Debug_Process_State state) { Evas_Object *ico_int; ico_int = elm_icon_add(_button_int); - if (state == DEBUG_PROCESS_ACTIVE) + if (state == EDI_DEBUG_PROCESS_ACTIVE) elm_icon_standard_set(ico_int, "media-playback-pause"); else elm_icon_standard_set(ico_int, "media-playback-start"); @@ -341,17 +163,20 @@ static void _edi_debugpanel_bt_sigint_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { pid_t pid; - int state; + Edi_Debug *debug; - pid = _edi_debug_process_id(&state); + debug = edi_debug_get(); + if (!debug) return; + + pid = edi_debug_process_id(debug); if (pid <= 0) return; - if (state == DEBUG_PROCESS_ACTIVE) + if (debug->state == EDI_DEBUG_PROCESS_ACTIVE) kill(pid, SIGINT); - else - ecore_exe_send(_debug_exe, "c\n", 2); + else if (debug->tool->command_continue) + ecore_exe_send(debug->exe, debug->tool->command_continue, strlen(debug->tool->command_continue)); - _edi_debugpanel_icons_update(state); + _edi_debugpanel_icons_update(debug->state); } static void @@ -363,18 +188,20 @@ _edi_debugpanel_button_quit_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNU static void _edi_debugpanel_button_start_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) { - edi_debugpanel_start(); + edi_debugpanel_start(_edi_project_config_debug_command_get()); } static Eina_Bool _edi_debug_active_check_cb(void *data EINA_UNUSED) { - int state, pid = ecore_exe_pid_get(_debug_exe); + int pid; + Edi_Debug *debug = edi_debug_get(); + pid = ecore_exe_pid_get(debug->exe); if (pid == -1) { - if (_debug_exe) ecore_exe_quit(_debug_exe); - _debug_exe = NULL; + if (debug->exe) ecore_exe_quit(debug->exe); + debug->exe = NULL; elm_object_disabled_set(_button_quit, EINA_TRUE); elm_object_disabled_set(_button_start, EINA_FALSE); elm_object_disabled_set(_button_int, EINA_TRUE); @@ -382,8 +209,8 @@ _edi_debug_active_check_cb(void *data EINA_UNUSED) } else { - if (_edi_debug_process_id(&state) > 0) - _edi_debugpanel_icons_update(state); + if (edi_debug_process_id(debug) > 0) + _edi_debugpanel_icons_update(debug->state); } return ECORE_CALLBACK_RENEW; @@ -391,28 +218,61 @@ _edi_debug_active_check_cb(void *data EINA_UNUSED) void edi_debugpanel_stop(void) { + Edi_Debug *debug; int pid; - if (_debug_exe) - ecore_exe_terminate(_debug_exe); + debug = edi_debug_get(); + if (!debug) + return; - pid = ecore_exe_pid_get(_debug_exe); + if (debug->exe) + ecore_exe_terminate(debug->exe); + + pid = ecore_exe_pid_get(debug->exe); if (pid != -1) - ecore_exe_quit(_debug_exe); + ecore_exe_quit(debug->exe); - _debug_exe = NULL; + debug->exe = NULL; elm_object_disabled_set(_button_quit, EINA_TRUE); elm_object_disabled_set(_button_int, EINA_TRUE); elm_object_disabled_set(_button_term, EINA_TRUE); } -void edi_debugpanel_start(void) +static void +_edi_debugger_run(Edi_Debug *debug) { - char cmd[1024]; + const char *fmt; char *args; int len; - const char *warning, *mime, *fmt; + + debug->exe = ecore_exe_pipe_run(debug->cmd, ECORE_EXE_PIPE_WRITE | + ECORE_EXE_PIPE_ERROR | + ECORE_EXE_PIPE_READ, NULL); + + if (debug->tool->command_arguments && _edi_project_config->launch.args) + { + fmt = debug->tool->command_arguments; + len = strlen(fmt) + strlen(_edi_project_config->launch.args) + 1; + args = malloc(len); + snprintf(args, len, fmt, _edi_project_config->launch.args); + ecore_exe_send(debug->exe, args, strlen(args)); + free(args); + } + + if (debug->tool->command_start) + ecore_exe_send(debug->exe, debug->tool->command_start, strlen(debug->tool->command_start)); +} + +void edi_debugpanel_start(const char *name) +{ + const char *mime; + Edi_Debug *debug; + + debug = edi_debug_get(); + if (!debug) return; + + if (debug->exe) return; if (!_edi_project_config->launch.path) { @@ -420,44 +280,31 @@ void edi_debugpanel_start(void) return; } - if (_debug_exe) return; + debug->program_name = ecore_file_file_get(_edi_project_config->launch.path); - if (!ecore_file_exists(_edi_project_config->launch.path)) + debug->tool = edi_debug_tool_get(name); + if (!debug->tool) { - warning = _("Warning: executable does not exists (run make?)"); - elm_code_file_line_append(_debug_output->file, warning, strlen(warning), NULL); + edi_debug_exe_missing(); return; } mime = efreet_mime_type_get(_edi_project_config->launch.path); - if (!strcmp(mime, "application/x-shellscript")) - snprintf(cmd, sizeof(cmd), LIBTOOL_COMMAND " --mode execute gdb %s", _edi_project_config->launch.path); + if (mime && !strcmp(mime, "application/x-shellscript")) + snprintf(debug->cmd, sizeof(debug->cmd), LIBTOOL_COMMAND " --mode execute %s %s", debug->tool->exec, _edi_project_config->launch.path); + else if (debug->tool->arguments) + snprintf(debug->cmd, sizeof(debug->cmd), "%s %s %s", debug->tool->exec, debug->tool->arguments, _edi_project_config->launch.path); else - snprintf(cmd, sizeof(cmd), "gdb %s", _edi_project_config->launch.path); - - _debug_exe = ecore_exe_pipe_run(cmd, ECORE_EXE_PIPE_WRITE | - ECORE_EXE_PIPE_ERROR | - ECORE_EXE_PIPE_READ, NULL); - - ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _debugpanel_stdout_handler, NULL); - ecore_event_handler_add(ECORE_EXE_EVENT_ERROR, _debugpanel_stdout_handler, NULL); + snprintf(debug->cmd, sizeof(debug->cmd), "%s %s", debug->tool->exec, _edi_project_config->launch.path); elm_object_disabled_set(_button_int, EINA_FALSE); elm_object_disabled_set(_button_term, EINA_FALSE); elm_object_disabled_set(_button_quit, EINA_FALSE); - - if (_edi_project_config->launch.args) - { - fmt = "set args %s\n"; - len = strlen(fmt) + strlen(_edi_project_config->launch.args) + 1; - args = malloc(len); - snprintf(args, len, fmt, _edi_project_config->launch.args); - ecore_exe_send(_debug_exe, args, strlen(args)); - free(args); - } - - ecore_exe_send(_debug_exe, "run\n", 4); elm_object_disabled_set(_button_start, EINA_TRUE); + + elm_code_file_clear(_debug_output->file); + + _edi_debugger_run(debug); } void edi_debugpanel_add(Evas_Object *parent) @@ -542,11 +389,15 @@ void edi_debugpanel_add(Evas_Object *parent) _info_widget = widget; _entry_widget = entry; + edi_debug_new(); + timer = ecore_timer_add(1.0, _edi_debug_active_check_cb, NULL); (void) timer; elm_box_pack_end(parent, widget); elm_box_pack_end(parent, table); + ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _debugpanel_stdout_handler, NULL); + ecore_event_handler_add(ECORE_EXE_EVENT_ERROR, _debugpanel_stdout_handler, NULL); ecore_event_handler_add(EDI_EVENT_CONFIG_CHANGED, _edi_debugpanel_config_changed, NULL); } diff --git a/src/bin/edi_debugpanel.h b/src/bin/edi_debugpanel.h index 6f189d0..47b7547 100644 --- a/src/bin/edi_debugpanel.h +++ b/src/bin/edi_debugpanel.h @@ -34,9 +34,11 @@ void edi_debugpanel_add(Evas_Object *parent); /** * Start a new debugging session * + * @param The toolname to do debugging with. + * * @ingroup UI */ -void edi_debugpanel_start(); +void edi_debugpanel_start(const char *toolname); /** * Stop existing debugging session diff --git a/src/bin/edi_main.c b/src/bin/edi_main.c index d00fed7..83cc1b7 100644 --- a/src/bin/edi_main.c +++ b/src/bin/edi_main.c @@ -563,6 +563,17 @@ edi_launcher_config_missing() edi_screens_message(_edi_main_win, title, message); } +void +edi_debug_exe_missing(void) +{ + const char *title, *message; + + title = _("Unable to launch debugger"); + message = _("No debug binary found, please check system configuration and Settings."); + + edi_screens_message(_edi_main_win, title, message); +} + static void _edi_project_credentials_missing() @@ -729,6 +740,12 @@ _edi_build_display_status_cb(int status, void *data) eina_strbuf_free(message); } +static void +_edi_debug_project(void) +{ + edi_debugpanel_start(_edi_project_config_debug_command_get()); +} + static void _edi_build_project(void) { @@ -796,7 +813,7 @@ static void _tb_debug_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { edi_debugpanel_show(); - edi_debugpanel_start(); + _edi_debug_project(); } static void @@ -1012,12 +1029,21 @@ _edi_menu_clean_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, _edi_build_clean_project(); } +static void +_edi_menu_memcheck_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + edi_debugpanel_show(); + edi_debugpanel_stop(); + edi_debugpanel_start("memcheck"); +} + static void _edi_menu_debug_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { edi_debugpanel_show(); - edi_debugpanel_start(); + _edi_debug_project(); } static void @@ -1189,9 +1215,12 @@ _edi_menu_setup(Evas_Object *win) _edi_menu_build = elm_menu_item_add(menu, menu_it, "system-run", _("Build"), _edi_menu_build_cb, NULL); _edi_menu_test = elm_menu_item_add(menu, menu_it, "media-record", _("Test"), _edi_menu_test_cb, NULL); elm_menu_item_add(menu, menu_it, "media-playback-start", _("Run"), _edi_menu_run_cb, NULL); - elm_menu_item_add(menu, menu_it, "utilities-terminal", _("Debug"), _edi_menu_debug_cb, NULL); _edi_menu_clean = elm_menu_item_add(menu, menu_it, "edit-clear", _("Clean"), _edi_menu_clean_cb, NULL); + menu_it = elm_menu_item_add(menu, NULL, NULL, _("Debug"), NULL, NULL); + elm_menu_item_add(menu, menu_it, "utilities-terminal", _("Debugger"), _edi_menu_debug_cb, NULL); + elm_menu_item_add(menu, menu_it, "applications-electronics", _("Memcheck"), _edi_menu_memcheck_cb, NULL); + menu_it = elm_menu_item_add(menu, NULL, NULL, _("Project"), NULL, NULL); _edi_menu_init = elm_menu_item_add(menu, menu_it, "media-playback-start", _("Init"), _edi_menu_scm_init_cb, NULL); _edi_menu_commit = elm_menu_item_add(menu, menu_it, "mail-send", _("Commit"), _edi_menu_scm_commit_cb, NULL); diff --git a/src/bin/edi_private.h b/src/bin/edi_private.h index 6e5dc58..5301a72 100644 --- a/src/bin/edi_private.h +++ b/src/bin/edi_private.h @@ -55,6 +55,8 @@ void edi_open_url(); Eina_Bool edi_noproject(); void edi_launcher_config_missing(); +void edi_debug_exe_missing(void); + Eina_Bool _edi_project_config_save_no_notify(void); diff --git a/src/bin/meson.build b/src/bin/meson.build index c8d8308..96ff1a3 100644 --- a/src/bin/meson.build +++ b/src/bin/meson.build @@ -7,6 +7,8 @@ src = files([ 'edi_consolepanel.h', 'edi_content_provider.c', 'edi_content_provider.h', + 'edi_debug.c', + 'edi_debug.h', 'edi_debugpanel.c', 'edi_debugpanel.h', 'edi_file.c', diff --git a/src/bin/screens/edi_settings.c b/src/bin/screens/edi_settings.c index c9b4b08..dd206b0 100644 --- a/src/bin/screens/edi_settings.c +++ b/src/bin/screens/edi_settings.c @@ -8,6 +8,7 @@ #include "Edi.h" #include "edi_screens.h" #include "edi_config.h" +#include "edi_debug.h" #include "edi_private.h" @@ -293,76 +294,139 @@ _edi_settings_builds_args_cb(void *data EINA_UNUSED, Evas_Object *obj, _edi_project_config_save(); } +static char * +_edi_settings_builds_debug_tool_text_get_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED) +{ + Edi_Debug_Tool *tool; + int i; + + i = (int)(uintptr_t) data; + + tool = &edi_debug_tools_get()[i]; + + return strdup(tool->name); +} + +static void _edi_settings_builds_debug_pressed_cb(void *data EINA_UNUSED, Evas_Object *obj, void *event_info) +{ + const char *text = elm_object_item_text_get(event_info); + + if (_edi_project_config->debug_command) + eina_stringshare_del(_edi_project_config->debug_command); + + _edi_project_config->debug_command = eina_stringshare_add(text); + _edi_project_config_save(); + + elm_object_text_set(obj, text); + elm_combobox_hover_end(obj); +} + static Evas_Object * _edi_settings_builds_create(Evas_Object *parent) { - Evas_Object *box, *frame, *hbox, *label, *ic, *selector, *file, *entry; + Evas_Object *box, *frame, *table, *label, *ic, *selector, *file, *entry; + Evas_Object *combobox; + Elm_Genlist_Item_Class *itc; + Edi_Debug_Tool *tools; + int i; frame = _edi_settings_panel_create(parent, _("Builds")); box = elm_object_part_content_get(frame, "default"); - hbox = elm_box_add(parent); - elm_box_horizontal_set(hbox, EINA_TRUE); - evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, 0.0); - evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL); - elm_box_pack_end(box, hbox); - evas_object_show(hbox); + table = elm_table_add(parent); + elm_table_padding_set(table, 5, 5); + evas_object_size_hint_weight_set(table, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(table, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(table); + elm_box_pack_end(box, table); - label = elm_label_add(hbox); - elm_object_text_set(label, _("Runtime binary")); + label = elm_label_add(box); + elm_object_text_set(label, _("Runtime binary:")); evas_object_size_hint_weight_set(label, 0.0, 0.0); evas_object_size_hint_align_set(label, 0.0, EVAS_HINT_FILL); - elm_box_pack_end(hbox, label); + elm_table_pack(table, label, 0, 0, 1, 1); evas_object_show(label); - ic = elm_icon_add(hbox); + ic = elm_icon_add(box); elm_icon_standard_set(ic, "file"); evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1); + evas_object_show(ic); selector = elm_fileselector_button_add(box); elm_object_text_set(selector, _("Select")); elm_object_part_content_set(selector, "icon", ic); elm_fileselector_path_set(selector, edi_project_get()); - evas_object_size_hint_weight_set(selector, 0.25, 0.0); evas_object_size_hint_align_set(selector, EVAS_HINT_FILL, EVAS_HINT_FILL); - elm_box_pack_end(hbox, selector); + elm_table_pack(table, selector, 1, 0, 1, 1); evas_object_show(selector); - elm_object_focus_set(selector, EINA_TRUE); - - file = elm_label_add(hbox); + file = elm_entry_add(box); + elm_entry_editable_set(file, EINA_FALSE); + elm_entry_single_line_set(file, EINA_TRUE); + elm_entry_scrollable_set(file, EINA_TRUE); elm_object_text_set(file, _edi_project_config->launch.path); evas_object_size_hint_weight_set(file, 0.75, 0.0); evas_object_size_hint_align_set(file, EVAS_HINT_FILL, EVAS_HINT_FILL); - elm_box_pack_end(hbox, file); + elm_table_pack(table, file, 2, 0, 1, 1); evas_object_show(file); evas_object_smart_callback_add(selector, "file,chosen", _edi_settings_builds_binary_chosen_cb, file); - hbox = elm_box_add(parent); - elm_box_horizontal_set(hbox, EINA_TRUE); - evas_object_size_hint_weight_set(hbox, EVAS_HINT_EXPAND, 0.0); - evas_object_size_hint_align_set(hbox, EVAS_HINT_FILL, EVAS_HINT_FILL); - elm_box_pack_end(box, hbox); - evas_object_show(hbox); - - label = elm_label_add(hbox); - elm_object_text_set(label, _("Runtime arguments")); + label = elm_label_add(box); + elm_object_text_set(label, _("Runtime arguments:")); evas_object_size_hint_weight_set(label, 0.0, 0.0); evas_object_size_hint_align_set(label, 0.0, EVAS_HINT_FILL); - elm_box_pack_end(hbox, label); + elm_table_pack(table, label, 0, 1, 1, 1); evas_object_show(label); - entry = elm_entry_add(hbox); + entry = elm_entry_add(box); elm_object_text_set(entry, _edi_project_config->launch.args); + elm_entry_editable_set(entry, EINA_TRUE); + elm_entry_single_line_set(entry, EINA_TRUE); + elm_entry_scrollable_set(entry, EINA_TRUE); evas_object_size_hint_weight_set(entry, 0.75, 0.0); evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); - elm_box_pack_end(hbox, entry); + elm_table_pack(table, entry, 1, 1, 2, 1); evas_object_show(entry); evas_object_smart_callback_add(entry, "changed", _edi_settings_builds_args_cb, NULL); + label = elm_label_add(box); + elm_object_text_set(label, _("Default debugger:")); + evas_object_size_hint_weight_set(label, 0.0, 0.0); + evas_object_size_hint_align_set(label, 0.0, EVAS_HINT_FILL); + elm_table_pack(table, label, 0, 2, 1, 1); + evas_object_show(label); + + combobox = elm_combobox_add(box); + if (_edi_project_config->debug_command) + elm_object_part_text_set(combobox, "guide", _edi_project_config->debug_command); + else + elm_object_part_text_set(combobox, "guide", _("Please choose ...")); + + evas_object_size_hint_weight_set(combobox, 0.75, 0.0); + evas_object_size_hint_align_set(combobox, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(combobox); + evas_object_smart_callback_add(combobox, "item,pressed", + _edi_settings_builds_debug_pressed_cb, NULL); + + elm_table_pack(table, combobox, 1, 2, 2, 1); + + itc = elm_genlist_item_class_new(); + itc->item_style = "default"; + itc->func.text_get = _edi_settings_builds_debug_tool_text_get_cb; + + tools = edi_debug_tools_get(); + for (i = 0; tools[i].name; i++) + { + if (ecore_file_app_installed(tools[i].exec)) + elm_genlist_item_append(combobox, itc, (void *)(uintptr_t) i, NULL, ELM_GENLIST_ITEM_NONE, NULL, (void *)(uintptr_t) i); + } + + elm_genlist_realized_items_update(combobox); + elm_genlist_item_class_free(itc); + return frame; }