/* * Set breakpoints condition. * arguments: * file - breakpoints filename * line - breakpoints line * condition - breakpoints line */ void breaks_set_condition(const char* file, int line, const char* condition) { /* do not process async break manipulation on modules that do not support async interuppt */ enum dbs state = debug_get_state(); if (DBS_RUNNING == state && !debug_supports_async_breaks()) return; /* lookup for breakpoint */ breakpoint* bp = NULL; if (!(bp = breaks_lookup_breakpoint(file, line))) return; /* change condition */ strcpy(bp->condition, condition); /* handle setting condition instantly if debugger is idle or stopped and request debug module interruption overwise */ if (state == DBS_IDLE) { on_set_condition(bp); config_set_debug_changed(); } else if (state == DBS_STOPPED) breaks_set_condition_debug(bp); else if (state != DBS_STOP_REQUESTED) debug_request_interrupt((bs_callback)breaks_set_condition_debug, (gpointer)bp); }
/* * Switch breakpoints state. * arguments: * file - breakpoints filename * line - breakpoints line */ void breaks_switch(const char* file, int line) { /* do not process async break manipulation on modules that do not support async interuppt */ enum dbs state = debug_get_state(); if (DBS_RUNNING == state && !debug_supports_async_breaks()) return; /* lookup for breakpoint */ breakpoint* bp = NULL; if (!(bp = breaks_lookup_breakpoint(file, line))) return; /* change activeness */ bp->enabled = !bp->enabled; /* handle switching instantly if debugger is idle or stopped and request debug module interruption overwise */ if (DBS_IDLE == state) { on_switch(bp); config_set_debug_changed(); } else if (DBS_STOPPED == state) breaks_switch_debug(bp); else if (DBS_STOP_REQUESTED != state) debug_request_interrupt((bs_callback)breaks_switch_debug, (gpointer)bp); }
/* * Add new breakpoint. * arguments: * file - breakpoints filename * line - breakpoints line * condition - breakpoints line * enabled - is new breakpoint enabled * hitscount - breakpoints hitscount */ void breaks_add(const char* file, int line, char* condition, int enabled, int hitscount) { /* do not process async break manipulation on modules that do not support async interuppt */ enum dbs state = debug_get_state(); if (DBS_RUNNING == state && !debug_supports_async_breaks()) return; /* allocate memory */ breakpoint* bp = break_new_full(file, line, condition, enabled, hitscount); /* check whether GTree for this file exists and create if doesn't */ GTree *tree; if (!(tree = g_hash_table_lookup(files, bp->file))) { char *newfile = g_strdup(bp->file); tree = g_tree_new_full(compare_func, NULL, NULL, (GDestroyNotify)g_free); g_hash_table_insert(files, newfile, tree); } /* insert to internal storage */ g_tree_insert(tree, GINT_TO_POINTER(bp->line), bp); /* handle creation instantly if debugger is idle or stopped and request debug module interruption overwise */ if (DBS_IDLE == state) { on_add(bp); config_set_debug_changed(); } else if (DBS_STOPPED == state) breaks_add_debug(bp); else if (DBS_STOP_REQUESTED != state) debug_request_interrupt((bs_callback)breaks_add_debug, (gpointer)bp); }
/* Called by Geany before unloading the plugin. * Here any UI changes should be removed, memory freed and any other finalization done. * Be sure to leave Geany as it was before plugin_init(). */ void plugin_cleanup(void) { /* stop debugger if running */ if (DBS_IDLE != debug_get_state()) { debug_stop(); while (DBS_IDLE != debug_get_state()) g_main_context_iteration(NULL,FALSE); } config_destroy(); pixbufs_destroy(); debug_destroy(); breaks_destroy(); dpaned_destroy(); envtree_destroy(); /* release other allocated strings and objects */ gtk_widget_destroy(hbox); }
/* * Occures on document opening. * Used to set breaks markers */ void on_document_open(GObject *obj, GeanyDocument *doc, gpointer user_data) { /*set markers*/ markers_set_for_document(doc->editor->sci); /*set dwell interval*/ scintilla_send_message(doc->editor->sci, SCI_SETMOUSEDWELLTIME, 500, 0); /* set tab size for calltips */ scintilla_send_message(doc->editor->sci, SCI_CALLTIPUSESTYLE, 20, (long)NULL); /* set breakpoint and frame markers */ set_markers_for_file(DOC_FILENAME(doc)); /* if debug is active - tell the debug module that a file was opened */ if (DBS_IDLE != debug_get_state()) debug_on_file_open(doc); }
/* * Set breakpoint and stack markers for a file */ static void set_markers_for_file(const gchar* file) { GList *breaks; if ( (breaks = breaks_get_for_document(file)) ) { GList *iter = breaks; while (iter) { breakpoint *bp = (breakpoint*)iter->data; markers_add_breakpoint(bp); iter = iter->next; } g_list_free(breaks); } /* set frames markers if exists */ if (DBS_STOPPED == debug_get_state()) { int active_frame_index = debug_get_active_frame(); GList *iter = debug_get_stack(); int frame_index = 0; for (; iter; iter = iter->next, frame_index++) { if (iter) { frame *f = (frame*)iter->data; if (f->have_source && !strcmp(f->file, file)) { if (active_frame_index == frame_index) { markers_add_current_instruction(f->file, f->line); } else { markers_add_frame(f->file, f->line); } } } } } }
/* * Remove all breakpoints in the list. * arguments: * list - list f breakpoints */ void breaks_remove_list(GList *list) { /* do not process async break manipulation on modules that do not support async interuppt */ enum dbs state = debug_get_state(); if (DBS_RUNNING == state && !debug_supports_async_breaks()) return; /* handle removing instantly if debugger is idle or stopped and request debug module interruption overwise */ if (DBS_IDLE == state) { on_remove_list(list); g_list_free(list); config_set_debug_changed(); } else if (DBS_STOPPED == state) breaks_remove_list_debug(list); else if (DBS_STOP_REQUESTED != state) debug_request_interrupt((bs_callback)breaks_remove_list_debug, (gpointer)list); }
/* * sets all breakpoints fo the file enabled or disabled. * arguments: * file - list of breakpoints * enabled - anble or disable breakpoints */ void breaks_set_enabled_for_file(const const char *file, gboolean enabled) { /* do not process async break manipulation on modules that do not support async interuppt */ enum dbs state = debug_get_state(); if (DBS_RUNNING == state && !debug_supports_async_breaks()) return; GList *breaks = breaks_get_for_document(file); /* handle switching instantly if debugger is idle or stopped and request debug module interruption overwise */ if (DBS_IDLE == state) { on_set_enabled_list(breaks, enabled); g_list_free(breaks); config_set_debug_changed(); } else if (DBS_STOPPED == state) enabled ? breaks_set_enabled_list_debug(breaks) : breaks_set_disabled_list_debug(breaks); else if (DBS_STOP_REQUESTED != state) debug_request_interrupt((bs_callback)(enabled ? breaks_set_enabled_list_debug : breaks_set_disabled_list_debug), (gpointer)breaks); }
static gboolean on_read_from_gdb(GIOChannel * src, GIOCondition cond, gpointer data) { gchar *line; gsize length; if (G_IO_STATUS_NORMAL != g_io_channel_read_line(src, &line, NULL, &length, NULL)) return TRUE; gboolean prompt = !strcmp(line, GDB_PROMPT); *(line + length) = '\0'; if (!prompt) { if ('~' == line[0]) { colorize_message(line); } else { gchar *compressed = g_strcompress(line); colorize_message(compressed); g_free(compressed); } } if (!target_pid && g_str_has_prefix(line, "=thread-group-created")) { *(strrchr(line, '\"')) = '\0'; target_pid = atoi(line + strlen("=thread-group-created,id=\"")); } else if (g_str_has_prefix(line, "=thread-created")) { *(strrchr(line, ',') - 1) = '\0'; int thread_id = atoi(line + strlen("=thread-created,id=\"")); dbg_cbs->add_thread(thread_id); } else if (g_str_has_prefix(line, "=thread-exited")) { *(strrchr(line, ',') - 1) = '\0'; int thread_id = atoi(line + strlen("=thread-exited,id=\"")); dbg_cbs->remove_thread(thread_id); } else if (g_str_has_prefix(line, "=library-loaded") || g_str_has_prefix(line, "=library-unloaded")) { file_refresh_needed = TRUE; } else if (*line == '*') { /* asyncronous record found */ char *record = NULL; if ( (record = strchr(line, ',')) ) { *record = '\0'; record++; } else record = line + strlen(line); if (!strcmp(line, "*running")) dbg_cbs->set_run(); else if (!strcmp(line, "*stopped")) { /* removing read callback (will pulling all output left manually) */ g_source_remove(gdb_id_out); /* looking for a reason to stop */ char *next = NULL; char *reason = strstr(record, "reason=\""); if (reason) { reason += strlen("reason=\""); next = strstr(reason, "\"") + 1; *(next - 1) = '\0'; if (!strcmp(reason, "breakpoint-hit")) stop_reason = SR_BREAKPOINT_HIT; else if (!strcmp(reason, "end-stepping-range")) stop_reason = SR_END_STEPPING_RANGE; else if (!strcmp(reason, "signal-received")) stop_reason = SR_SIGNAL_RECIEVED; else if (!strcmp(reason, "exited-normally")) stop_reason = SR_EXITED_NORMALLY; else if (!strcmp(reason, "exited-signalled")) stop_reason = SR_EXITED_SIGNALLED; } else { /* somehow, sometimes there can be no stop reason */ stop_reason = SR_END_STEPPING_RANGE; } if (SR_BREAKPOINT_HIT == stop_reason || SR_END_STEPPING_RANGE == stop_reason || SR_SIGNAL_RECIEVED == stop_reason) { gchar *thread_id = strstr(reason + strlen(reason) + 1,"thread-id=\"") + strlen("thread-id=\""); *(strchr(thread_id, '\"')) = '\0'; if (SR_BREAKPOINT_HIT == stop_reason || SR_END_STEPPING_RANGE == stop_reason) { /* update autos */ update_autos(); /* update watches */ update_watches(); /* update files */ if (file_refresh_needed) { update_files(); file_refresh_needed = FALSE; } dbg_cbs->set_stopped(atoi(thread_id)); } else { if (!requested_interrupt) dbg_cbs->report_error(_("Program received a signal")); else requested_interrupt = FALSE; dbg_cbs->set_stopped(atoi(thread_id)); } } else if (stop_reason == SR_EXITED_NORMALLY || stop_reason == SR_EXITED_SIGNALLED) { stop(); } } } else if (g_str_has_prefix (line, "^error")) { /* removing read callback (will pulling all output left manually) */ g_source_remove(gdb_id_out); /* set debugger stopped if is running */ if (DBS_STOPPED != debug_get_state()) { gchar *thread_id = strstr(line + strlen(line) + 1,"thread-id=\""); *(strchr(thread_id, '\"')) = '\0'; dbg_cbs->set_stopped(atoi(thread_id)); } /* get message */ char *msg = strstr(line, "msg=\"") + strlen("msg=\""); *strrchr(msg, '\"') = '\0'; msg = g_strcompress(msg); /* reading until prompt */ GList *lines = read_until_prompt(); GList *iter = lines; while(iter) { gchar *l = (gchar*)iter->data; if (strcmp(l, GDB_PROMPT)) colorize_message(l); g_free(l); iter = iter->next; } g_list_free (lines); /* send error message */ dbg_cbs->report_error(msg); g_free(msg); } g_free(line); return TRUE; }
/* * Occures when key is pressed. * Handles debug Run/Stop/... and add/remove breakpoint activities */ gboolean keys_callback(guint key_id) { switch (key_id) { case KEY_RUN: debug_run(); break; case KEY_STOP: debug_stop(); break; case KEY_RESTART: debug_restart(); break; case KEY_STEP_OVER: debug_step_over(); break; case KEY_STEP_INTO: debug_step_into(); break; case KEY_STEP_OUT: debug_step_out(); break; case KEY_EXECUTE_UNTIL: { GeanyDocument *doc = document_get_current(); if (doc) { int line = sci_get_current_line(doc->editor->sci) + 1; debug_execute_until(DOC_FILENAME(doc), line); } break; } case KEY_BREAKPOINT: { GeanyDocument *doc = document_get_current(); if (doc) { int line = sci_get_current_line(doc->editor->sci) + 1; break_state bs = breaks_get_state(DOC_FILENAME(doc), line); if (BS_NOT_SET == bs) breaks_add(DOC_FILENAME(doc), line, NULL, TRUE, 0); else if (BS_ENABLED == bs) breaks_remove(DOC_FILENAME(doc), line); else if (BS_DISABLED == bs) breaks_switch(DOC_FILENAME(doc), line); scintilla_send_message(doc->editor->sci, SCI_SETFOCUS, TRUE, 0); } break; } case KEY_CURRENT_INSTRUCTION: { if (DBS_STOPPED == debug_get_state() && debug_current_instruction_have_sources()) { debug_jump_to_current_instruction(); gtk_widget_set_sensitive(tab_call_stack, FALSE); stree_select_first_frame(FALSE); gtk_widget_set_sensitive(tab_call_stack, TRUE); } } } return TRUE; }
/* * Occures on notify from editor. * Handles margin click to set/remove breakpoint */ gboolean on_editor_notify( GObject *object, GeanyEditor *editor, SCNotification *nt, gpointer data) { if (!editor->document->real_path) { /* no other way to handle removing a file from outside of geany */ markers_remove_all(editor->document); } switch (nt->nmhdr.code) { case SCN_MARGINCLICK: { char* file; int line; break_state bs; if (!editor->document->real_path || 1 != nt->margin) break; file = editor->document->file_name; line = sci_get_line_from_position(editor->sci, nt->position) + 1; bs = breaks_get_state(file, line); if (BS_NOT_SET == bs) breaks_add(file, line, NULL, TRUE, 0); else if (BS_ENABLED == bs) breaks_remove(file, line); else if (BS_DISABLED == bs) breaks_switch(file, line); scintilla_send_message(editor->sci, SCI_SETFOCUS, TRUE, 0); return TRUE; } case SCN_DWELLSTART: { GString *word; if (DBS_STOPPED != debug_get_state ()) break; /* get a word under the cursor */ word = get_word_at_position(editor->sci, nt->position); if (word->len) { gchar *calltip = debug_get_calltip_for_expression(word->str); if (calltip) { leave_signal = g_signal_connect(G_OBJECT(editor->sci), "leave-notify-event", G_CALLBACK(on_mouse_leave), NULL); scintilla_send_message (editor->sci, SCI_CALLTIPSHOW, nt->position, (long)calltip); } } g_string_free(word, TRUE); break; } case SCN_DWELLEND: { if (DBS_STOPPED != debug_get_state ()) break; if (scintilla_send_message (editor->sci, SCI_CALLTIPACTIVE, 0, 0)) { g_signal_handler_disconnect(G_OBJECT(editor->sci), leave_signal); scintilla_send_message (editor->sci, SCI_CALLTIPCANCEL, 0, 0); } break; } case SCN_MODIFYATTEMPTRO: { dialogs_show_msgbox(GTK_MESSAGE_INFO, _("To edit source files stop debugging session")); break; } case SCN_MODIFIED: { if(((SC_MOD_INSERTTEXT & nt->modificationType) || (SC_MOD_DELETETEXT && nt->modificationType)) && editor->document->file_name && nt->linesAdded) { int line = sci_get_line_from_position(editor->sci, nt->position) + 1; GList *breaks = breaks_get_for_document(editor->document->file_name); if (breaks) { GList *iter = breaks; while (iter) { breakpoint *bp = (breakpoint*)iter->data; if (nt->linesAdded > 0 && bp->line >= line) { breaks_move_to_line(bp->file, bp->line, bp->line + nt->linesAdded); bptree_update_breakpoint(bp); } else if (nt->linesAdded < 0 && bp->line >= line) { if (bp->line < line - nt->linesAdded) { breaks_remove(bp->file, bp->line); } else { breaks_move_to_line(bp->file, bp->line, bp->line + nt->linesAdded); bptree_update_breakpoint(bp); } } iter = iter->next; } config_set_debug_changed(); g_list_free(breaks); } } break; } } return FALSE; }