[geany/geany-plugins] ca3391: scope: switched GDB I/O to Geany spawn

Dimitar Zhekov git-noreply at xxxxx
Thu Oct 1 17:34:26 UTC 2015


Branch:      refs/heads/master
Author:      Dimitar Zhekov <dimitar.zhekov at gmail.com>
Committer:   Dimitar Zhekov <dimitar.zhekov at gmail.com>
Date:        Thu, 01 Oct 2015 17:34:26 UTC
Commit:      ca339163e5429ad69dbd1509ec7cebfc94d05289
             https://github.com/geany/geany-plugins/commit/ca339163e5429ad69dbd1509ec7cebfc94d05289

Log Message:
-----------
scope: switched GDB I/O to Geany spawn

Consequently removed gdb_buffer_length, gdb_send_interval and
gdb_wait_death. Geany 1.25+ required.


Modified Paths:
--------------
    scope/ChangeLog
    scope/NEWS
    scope/NOTES
    scope/README
    scope/docs/scope.html
    scope/src/debug.c
    scope/src/prefs.c
    scope/src/prefs.h
    scope/src/scope.c

Modified: scope/ChangeLog
17 lines changed, 16 insertions(+), 1 deletions(-)
===================================================================
@@ -1,8 +1,23 @@
+2015-10-01  Dimitar Zhekov  <dimitar.zhekov at gmail.com>
+
+ * src/debug.c:
+   Switched gdb I/O to Geany spawn. No more attempts to send the
+   commands at 50 ms and permanent console under Windows. The gdb
+   buffer size is now 1 MB (no performance penalty for large buffer
+   sizes either), longer lines or ones with '\0' are not parsed.
+ * src/debug.c, src/prefs.c, src/prefs.h, docs/scope.html:
+   Removed gdb_buffer_length, gdb_send_interval and gdb_wait_death
+   preferences. Obsoleted by the new gdb I/O and the fact that
+   spawn sends SIGTERM under *nix, not SIGKILL (though _death was
+   unneeded anyway).
+ * docs/scope.html, src/scope.c:
+   Increased version to 0.94.
+
+
 2015-04-24  Dimitar Zhekov  <dimitar.zhekov at gmail.com>
 
  * data/scope.glade, src/Makefile.am, src/common.h, src/scope.c:
    Removed support for gtk+ 2.16.
-
  * src/gtk216.c, src/gtk216.h:
    Removed.
 


Modified: scope/NEWS
9 lines changed, 9 insertions(+), 0 deletions(-)
===================================================================
@@ -1,3 +1,12 @@
+Scope 0.94 (2015-10-01):
+
+    * Better GDB I/O based on Geany spawn, consequently removed
+      gdb_buffer_length, gdb_send_interval and gdb_wait_death.
+      To leave the Windows console open on program termination,
+      set breakpoints at ExitProcess / TerminateProcess.
+      Geany 1.25+ required.
+
+
 Scope 0.93.2 (2014-02-13):
 
     * Remember the total (initial) breakpoint ignore count and


Modified: scope/NOTES
5 lines changed, 1 insertions(+), 4 deletions(-)
===================================================================
@@ -48,13 +48,10 @@ insignificant; for large texts, using string is faster, but not much
 CPU load on F8 with vte console: ~88%
 with context: ~80%, null output: ~75%
 
-win~1 hang with poll_pipe(): 1 second for 3h25m
-win~1 send on 50ms: 2 seconds for 1h, 1445 KB sent
-
 GtkTextView with wrapping CHAR: the last character of a wrapped line can be
 selected with the mouse only by moving to the next line
 
-Geany under Win~1, with FF also running, eats 7-8% CPU time, other
+Geany gtk+2 under Win~1, with FF also running, eats 7-8% CPU time, other
 applications/conditions may cause such CPU load too; scope doesn't matter
 
 http://sourceware.org/bugzilla/show_bug.cgi?id=9659


Modified: scope/README
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -18,7 +18,7 @@ expect (stepping, breakpoints...), and a few notable features:
 
 Requirements
 ------------
-Geany 1.22 or later and the respective libraries, including VTE under *nix.
+Geany 1.25 or later and the respective libraries, including VTE under *nix.
 
 GLib-2.18 or later. For Windows, 2.24 is recommended.
 


Modified: scope/docs/scope.html
30 lines changed, 12 insertions(+), 18 deletions(-)
===================================================================
@@ -50,6 +50,7 @@
 		<li><a href="#local_watch">Locals/Watches</a></li>
 		<li><a href="#console">Debug Console</a></li>
 	</ul></li>
+	<li><a href="#windows_console">Windows console</a></li>
 	<li>Side pages<ul>
 		<li><a href="#inspect">Inspect</a></li>
 		<li><a href="#register">Registers</a></li>
@@ -101,7 +102,7 @@
 
 <h3><a name="requirements">Requirements</a></h3>
 
-<p>Geany 1.22 or later and the respective libraries.</p>
+<p>Geany 1.25 or later and the respective libraries.</p>
 
 <p>GDB 7.3 or later.</p>
 
@@ -308,7 +309,7 @@
 <p>Sorting by file name (including breakpoint location) uses the full locale name, so the
 order may be different from the displayed UTF-8 base names.</p>
 
-<p><b><a name="terminal">Program Terminal</a></b></p>
+<p><b><a name="terminal">Program Terminal (*nix only)</a></b></p>
 
 <p>Will be disabled if a pseudo-tty can not be created.</p>
 
@@ -454,8 +455,7 @@
 <p>Groups are not wrapped, so with <em>Group by</em> > 1, less than
 <em>memory_line_bytes</em> may be displayed.</p>
 
-<p>A maximum of 16K may be displayed (128 lines * 128 bytes); <em>gdb_buffer_length</em> also
-limits the size.</p>
+<p>A maximum of 16K may be displayed (128 lines * 128 bytes).</p>
 
 <p><b><a name="console">Debug Console</a></b></p>
 
@@ -473,6 +473,12 @@
 immediately resuming the program execution; or the prompt may be followed by asynchronous
 messages. The Scope state (Busy, Debug etc.) is more accurate.</p>
 
+<h3><a name="windows_console">Windows console</a></h3>
+
+<p>Displayed automatically by Windows when starting a Windows console program, and closed
+automatically when the program terminates. To leave the console open on program termination,
+set breakpoints at ExitProcess / TerminateProcess.</p>
+
 <h3>Side pages</h3>
 
 <p><b><a name="inspect">Inspect</a></b></p>
@@ -514,18 +520,6 @@
 
 <p>[scope]</p>
 
-<p><em>gdb_buffer_length</em> - the maximum length of a single gdb output message. Longer
-messages will be cut, and an "overflow" will be displayed in the debug console, possibly
-followed by a few other parsing errors. Default = 16383. Actual value is (the nearest larger
-power of 2) - 1, for example 32768 becomes 65535.
-
-<p><em>gdb_wait_death</em> - hundreds of seconds to wait(3) gdb death on scope unload.
-Default = 20. When closing Geany, gdb will be destroyed by the operating system.</p>
-
-<p><em>gdb_send_interval</em> (win~1 only) - interval on which to retry sending commands to
-gdb if it's input buffer is full, in hundreds of seconds. Default = 5. The actual value may be
-larger, for example Scope may be activated only 10 times per second if Geany is idle.</p>
-
 <p><em>async_break_bugs</em> (win~1 only) - whether gdb escapes slashes twice in the
 asynchronous break messages. if true (the default), only <tt>=breakpoint-deleted</tt> will be
 handled.</p>
@@ -589,7 +583,7 @@
 
 <p>[terminal] (*nix only)</p>
 
-<p><em>width</em>, <em>height</em> - VTE widget (not terminal window) size</p>
+<p><em>width</em>, <em>height</em> - VTE widget (not the entire terminal window) size</p>
 
 <p><em>save_pos</em> - auto save window position and widget size</p>
 
@@ -766,7 +760,7 @@
 
 <b><a name="copyright">Copyright</a></b>
 
-<p>Scope 0.93.2, Copyright (C) 2013 Dimitar Toshkov Zhekov</p>
+<p>Scope 0.94, Copyright (C) 2015 Dimitar Toshkov Zhekov</p>
 
 <p>The menu and toolbar icons are from <a href="http://netbeans.org">Netbeans</a>, except for
 BreakPoint.</p>


Modified: scope/src/debug.c
694 lines changed, 272 insertions(+), 422 deletions(-)
===================================================================
@@ -24,49 +24,8 @@
 
 #include <glib.h>
 
-#ifdef G_OS_UNIX
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#else  /* G_OS_UNIX */
-#include <windows.h>
-
-#define WNOHANG 0
-
-static int waitpid(HANDLE pid, int *stat_loc, int options)
-{
-	if (options == WNOHANG)
-	{
-		DWORD status;
-
-		if (GetExitCodeProcess(pid, &status))
-		{
-			if (status == STILL_ACTIVE)
-				return 0;
-
-			if (stat_loc)
-				*stat_loc = status;
-			return 1;
-		}
-	}
-
-	errno = EINVAL;
-	return -1;
-}
-
-#define SIGKILL 9
-
-static int kill(HANDLE pid, int sig)
-{
-	if (TerminateProcess(pid, sig))
-		return 0;
-
-	errno = EINVAL;
-	return -1;
-}
-#endif  /* G_OS_UNIX */
-
 #include "common.h"
+#include "spawn.h"
 
 extern guint thread_count;
 extern guint thread_prompt;
@@ -79,57 +38,123 @@ typedef enum _GdbState
 } GdbState;
 
 static GdbState gdb_state = INACTIVE;
-static GSource *gdb_source;
 static GPid gdb_pid;
 
-static GPollFD gdb_in = { -1, G_IO_OUT | G_IO_ERR, 0 };
-static GPollFD gdb_out = { -1, G_IO_IN | G_IO_HUP | G_IO_ERR, 0 };
-static GPollFD gdb_err = { -1, G_IO_IN | G_IO_HUP | G_IO_ERR, 0 };
+static gboolean wait_prompt;
+static GString *commands;
 
-static void free_gdb(void)
+DebugState debug_state(void)
 {
-	g_spawn_close_pid(gdb_pid);
-	close(gdb_in.fd);
-	close(gdb_out.fd);
-	close(gdb_err.fd);
+	DebugState state;
+
+	if (gdb_state == INACTIVE)
+		state = DS_INACTIVE;
+	else if (gdb_state == KILLING || wait_prompt || commands->len)
+		state = DS_BUSY;
+	else if (thread_count)
+	{
+		if (thread_state <= THREAD_RUNNING)
+			state = pref_gdb_async_mode || thread_prompt ? DS_READY : DS_BUSY;
+		else
+			state = DS_DEBUG;
+	}
+	else /* at prompt, no threads */
+		state = DS_HANGING;
+
+	return state;
 }
 
-static gboolean io_error_show(gpointer gdata)
+
+void on_debug_list_source(GArray *nodes)
 {
-	show_error("%s", (gchar *) gdata);
-	g_free(gdata);
-	return FALSE;
+	ParseLocation loc;
+
+	parse_location(nodes, &loc);
+
+	iff (loc.line, "no line or abs file")
+		debug_send_format(N, "02-break-insert -t %s:%d\n05", loc.file, loc.line);
+
+	parse_location_free(&loc);
 }
 
-#ifdef G_OS_UNIX
-static void gdb_io_check(ssize_t count, const char *operation, G_GNUC_UNUSED int eagain)
+static gboolean debug_auto_run;
+static gboolean debug_auto_exit;
+static gboolean debug_load_error;
+
+void on_debug_error(GArray *nodes)
 {
-	if (count == -1 && errno != EAGAIN && gdb_state != KILLING)
-#else  /* G_OS_UNIX */
-static void gdb_io_check(ssize_t count, const char *operation, int eagain)
+	debug_auto_run = FALSE;  /* may be an initialization command failure */
+	on_error(nodes);
+}
+
+void on_debug_loaded(GArray *nodes)
 {
-	if (count == -1 && errno != EAGAIN && errno != eagain && gdb_state != KILLING)
-#endif  /* G_OS_UNIX */
+	const char *token = parse_grab_token(nodes);
+
+	if (!debug_load_error && (*token + !*program_load_script >= '1'))
 	{
-		plugin_idle_add(geany_plugin, io_error_show,
-			g_strdup_printf(_("%s: %s."), operation, g_strerror(errno)));
+		breaks_apply();
+		inspects_apply();
+		view_dirty(VIEW_WATCHES);
 
-		if (kill(gdb_pid, SIGKILL) == -1)
+		if (program_temp_breakpoint)
 		{
-			plugin_idle_add(geany_plugin, io_error_show,
-				g_strdup_printf(_("%s: %s."), "kill(gdb)", g_strerror(errno)));
+			if (*program_temp_break_location)
+			{
+				debug_send_format(N, "02-break-insert -t %s\n05",
+					program_temp_break_location);
+			}
+			else
+			{
+				/* 1st loc, dette koi ---*/
+				debug_send_command(N, "-gdb-set listsize 1\n"
+					"02-file-list-exec-source-file\n"
+					"-gdb-set listsize 10");
+			}
 		}
+		else
+			debug_send_command(N, "05");
+	}
+}
+
+void on_debug_load_error(GArray *nodes)
+{
+	debug_load_error = TRUE;
+	on_error(nodes);
+}
+
+void on_debug_auto_run(G_GNUC_UNUSED GArray *nodes)
+{
+	if (debug_auto_run && !thread_count)
+	{
+		if (breaks_active())
+			debug_send_command(N, "-exec-run");
+		else
+			dialogs_show_msgbox(GTK_MESSAGE_INFO, _("No breakpoints. Hanging."));
+	}
+}
+
+void on_debug_auto_exit(void)
+{
+	if (debug_auto_exit)
+	{
+		debug_send_command(N, "-gdb-exit");
 		gdb_state = KILLING;
 	}
 }
 
+#define G_IO_FAILURE (G_IO_ERR | G_IO_HUP | G_IO_NVAL)  /* always used together */
+
+static GIOChannel *send_channel = NULL;
+static guint send_source_id = 0;
 static guint wait_result;
-static gboolean wait_prompt;
-static GString *commands;
 
-static void send_commands(void)
+static gboolean send_commands_cb(GIOChannel *channel, GIOCondition condition,
+	G_GNUC_UNUSED gpointer gdata)
 {
-	ssize_t count = write(gdb_in.fd, commands->str, commands->len);
+	SpawnWriteData data = { commands->str, commands->len };
+	gboolean result = spawn_write_data(channel, condition, &data);
+	gssize count = commands->len - data.size;
 
 	if (count > 0)
 	{
@@ -150,28 +175,66 @@ static void send_commands(void)
 		g_string_erase(commands, 0, count);
 		update_state(DS_BUSY);
 	}
-	else
-		gdb_io_check(count, "write(gdb_in)", ENOSPC);
+
+	return result;
 }
 
-#ifndef G_OS_UNIX
-static DWORD last_send_ticks;
-#endif
+static void send_source_destroy_cb(G_GNUC_UNUSED gpointer gdata)
+{
+	send_source_id = 0;
+}
+
+/*
+ * We need to release the initial stdin cb to avoid it being called constantly, and attach a
+ * source when we have data to send. Unfortunately, glib does not allow re-attaching removed
+ * sources, so we create one each time.
+ */
+
+static void create_send_source(void)
+{
+	GSource *send_source = g_io_create_watch(send_channel, G_IO_OUT | G_IO_FAILURE);
+
+	g_io_channel_unref(send_channel);
+	g_source_set_callback(send_source, (GSourceFunc) send_commands_cb, NULL,
+		send_source_destroy_cb);
+	send_source_id = g_source_attach(send_source, NULL);
+}
 
-static void debug_send_commands(void)
+#define HAS_SPAWN_LEAVE_STDIN_OPEN 0
+
+static gboolean obtain_send_channel_cb(GIOChannel *channel, GIOCondition condition,
+	G_GNUC_UNUSED gpointer gdata)
 {
-	send_commands();
-	if (commands->len)
+#if HAS_SPAWN_LEAVE_STDIN_OPEN
+	if (condition & G_IO_FAILURE)
+		g_io_channel_shutdown(channel, FALSE, NULL);
+	else
 	{
+		g_io_channel_ref(channel);
+		send_channel = channel;
+		create_send_source();  /* for the initialization commands */
+	}
+#else
+	if (!(condition & G_IO_FAILURE))
+	{
+		gint stdin_fd = dup(g_io_channel_unix_get_fd(channel));
+
 	#ifdef G_OS_UNIX
-		g_source_add_poll(gdb_source, &gdb_in);
+		send_channel = g_io_channel_unix_new(stdin_fd);
+		g_io_channel_set_flags(send_channel, G_IO_FLAG_NONBLOCK, NULL);
 	#else
-		last_send_ticks = GetTickCount();
+		send_channel = g_io_channel_win32_new_fd(stdin_fd);
 	#endif
+		g_io_channel_set_encoding(send_channel, NULL, NULL);
+		g_io_channel_set_buffered(send_channel, FALSE);
+		create_send_source();  /* for the initialization commands */
 	}
+#endif
+
+	return FALSE;
 }
 
-static void pre_parse(char *string, gboolean overflow)
+static void debug_parse(char *string, const char *error)
 {
 	if (*string && strchr("~@&", *string))
 	{
@@ -189,8 +252,8 @@ static void pre_parse(char *string, gboolean overflow)
 			end = NULL;
 		}
 
-		if (overflow)
-			dc_error("overflow");
+		if (error)
+			dc_error(error);
 		else if (!end)
 			dc_error("\" expected");
 		else if (g_str_has_prefix(string, "~^(Scope)#07"))
@@ -207,14 +270,9 @@ static void pre_parse(char *string, gboolean overflow)
 
 		for (message = string; isdigit(*message); message++);
 
-		if (option_library_messages || !g_str_has_prefix(message, "=library-"))
-		{
+		if (error || option_library_messages || !g_str_has_prefix(message, "=library-"))
 			dc_output_nl(1, string, -1);
 
-			if (overflow)
-				dc_error("overflow");
-		}
-
 		if (*message == '^')
 		{
 			iff (wait_result, "extra result")
@@ -229,269 +287,82 @@ static void pre_parse(char *string, gboolean overflow)
 		else
 			string = NULL;  /* no token */
 
-		parse_message(message, string);
+		if (error)
+			dc_error("%s, ignoring to EOLN", error);
+		else
+			parse_message(message, string);
 	}
 }
 
-static guint MAXLEN;
-static GString *received;
-static gboolean leading_receive;
-static char *reading_pos;
+static gboolean leading_receive;  /* FALSE for continuation of a too long / incomplete line */
 
-static gboolean source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout)
+static void receive_output_cb(GString *string, GIOCondition condition,
+	G_GNUC_UNUSED gpointer gdata)
 {
-	*timeout = -1;
-	return gdb_state != INACTIVE && reading_pos > received->str;
-}
-
-#ifdef G_OS_UNIX
-static gboolean source_check(G_GNUC_UNUSED GSource *source)
-{
-	return gdb_state != INACTIVE && (gdb_err.revents || reading_pos > received->str ||
-		gdb_out.revents || (commands->len && gdb_in.revents));
-}
-#else  /* G_OS_UNIX */
-static gboolean peek_pipe(GPollFD *fd)
-{
-	DWORD available;
-	return !PeekNamedPipe((HANDLE) _get_osfhandle(fd->fd), NULL, 0, NULL, &available,
-		NULL) || available;
-}
-
-static gboolean source_check(G_GNUC_UNUSED GSource *source)
-{
-	return gdb_state != INACTIVE && (reading_pos > received->str || peek_pipe(&gdb_err) ||
-		peek_pipe(&gdb_out) || (commands->len &&
-		GetTickCount() - last_send_ticks >= (guint) pref_gdb_send_interval * 10));
-}
-#endif  /* G_OS_UNIX */
-
-static guint source_id = 0;
-
-static gboolean source_dispatch(G_GNUC_UNUSED GSource *source,
-	G_GNUC_UNUSED GSourceFunc callback, G_GNUC_UNUSED gpointer gdata)
-{
-	int status;
-	pid_t result;
-	ssize_t count;
-	char buffer[0x200];
-	char *pos;
-
-	/* show errors */
-	while ((count = read(gdb_err.fd, buffer, sizeof buffer - 1)) > 0)
-		dc_output(2, buffer, count);
-
-	gdb_io_check(count, "read(gdb_err)", EINVAL);
-
-	/* receive */
-	count = read(gdb_out.fd, received->str + received->len, MAXLEN - received->len);
-
-	if (count > 0)
-		g_string_set_size(received, received->len + count);
-	else
-		gdb_io_check(count, "read(gdb_out)", EINVAL);
-
-	while (pos = reading_pos, (reading_pos = strchr(pos, '\n')) != NULL)
-	{
-		if (leading_receive)
-		{
-		#ifdef G_OS_UNIX
-			*reading_pos++ = '\0';
-		#else
-			gboolean cr = reading_pos > received->str && reading_pos[-1] == '\r';
-			reading_pos[-cr]= '\0';
-			reading_pos++;
-		#endif
-			pre_parse(pos, FALSE);
-		}
-		else
-		{
-			reading_pos++;
-			leading_receive = TRUE;
-		}
-	}
-
-	g_string_erase(received, 0, pos - received->str);
-
-	if (G_UNLIKELY(received->len == MAXLEN))
+	if (condition & (G_IO_IN | G_IO_PRI))
 	{
-		if (leading_receive)
-		{
-			reading_pos = received->str + MAXLEN;
-			pre_parse(received->str, TRUE);
-		}
-		g_string_truncate(received, 0);
-		leading_receive = FALSE;
-	}
+		char *term = string->str + string->len - 1;
+		const char *error = NULL;
 
-	reading_pos = received->str;
-	result = waitpid(gdb_pid, &status, WNOHANG);
-
-	if (result == 0)
-	{
-		if (commands->len)
+		switch (*term)
 		{
-			/* send */
-		#ifdef G_OS_UNIX
-			send_commands();
-			if (!commands->len)
-				g_source_remove_poll(gdb_source, &gdb_in);
-		#else
-			debug_send_commands();
-		#endif
-		}
-		else
-		{
-			/* idle update */
-			DebugState state = debug_state();
-
-			if (state & DS_SENDABLE)
-				views_update(state);
+			case '\n' : if (string->len >= 2 && term[-1] == '\r') term--;  /* falldown */
+			case '\r' : *term = '\0'; break;
+			case '\0' : error = "binary zero encountered"; break;
+			default : error = "line too long or incomplete";
 		}
-	}
-	else if (gdb_state != INACTIVE)
-	{
-		GdbState state = gdb_state;
-		/* shutdown */
-		gdb_state = INACTIVE;
-		signal(SIGINT, SIG_DFL);
-		g_source_remove(source_id);
-
-		if (result == -1)
-			show_errno("waitpid(gdb)");
-		else if (state == ACTIVE)
-			show_error(_("GDB died unexpectedly with status %d."), status);
-		else if (thread_count)
-			ui_set_statusbar(FALSE, _("Program terminated."));
-
-		free_gdb();
-		views_clear();
-		utils_lock_all(FALSE);
-	}
-
-	update_state(debug_state());
 
-	return TRUE;
-}
-
-static void source_finalize(G_GNUC_UNUSED GSource *source)
-{
-	source_id = 0;
-}
-
-DebugState debug_state(void)
-{
-	DebugState state;
+		if (leading_receive)
+			debug_parse(string->str, error);
 
-	if (gdb_state == INACTIVE)
-		state = DS_INACTIVE;
-	else if (gdb_state == KILLING || wait_prompt || commands->len)
-		state = DS_BUSY;
-	else if (thread_count)
-	{
-		if (thread_state <= THREAD_RUNNING)
-			state = pref_gdb_async_mode || thread_prompt ? DS_READY : DS_BUSY;
-		else
-			state = DS_DEBUG;
+		leading_receive = !error;
 	}
-	else /* at prompt, no threads */
-		state = DS_HANGING;
 
-	return state;
-}
-
-static gboolean debug_auto_run;
-static gboolean debug_auto_exit;
-
-void on_debug_list_source(GArray *nodes)
-{
-	ParseLocation loc;
-
-	parse_location(nodes, &loc);
+	if (!commands->len)
+		views_update(debug_state());
 
-	iff (loc.line, "no line or abs file")
-		debug_send_format(N, "02-break-insert -t %s:%d\n05", loc.file, loc.line);
-
-	parse_location_free(&loc);
+	update_state(debug_state());
 }
 
-void on_debug_error(GArray *nodes)
+static void receive_errors_cb(GString *string, GIOCondition condition,
+	G_GNUC_UNUSED gpointer gdata)
 {
-	debug_auto_run = FALSE;
-	on_error(nodes);
+	if (condition & (G_IO_IN | G_IO_PRI))
+		dc_output(2, string->str, string->len);
 }
 
-static gboolean debug_load_error;
-
-void on_debug_loaded(GArray *nodes)
+static void gdb_finalize(void)
 {
-	const char *token = parse_grab_token(nodes);
+	signal(SIGINT, SIG_DFL);
 
-	if (!debug_load_error && (*token + !*program_load_script >= '1'))
+	if (send_channel)
 	{
-		breaks_apply();
-		inspects_apply();
-		view_dirty(VIEW_WATCHES);
+		g_io_channel_shutdown(send_channel, FALSE, NULL);
+		g_io_channel_unref(send_channel);
+		send_channel = NULL;
 
-		if (program_temp_breakpoint)
-		{
-			if (*program_temp_break_location)
-			{
-				debug_send_format(N, "02-break-insert -t %s\n05",
-					program_temp_break_location);
-			}
-			else
-			{
-				/* 1st loc, dette koi ---*/
-				debug_send_command(N, "-gdb-set listsize 1\n"
-					"02-file-list-exec-source-file\n"
-					"-gdb-set listsize 10");
-			}
-		}
-		else
-			debug_send_command(N, "05");
+		if (send_source_id)
+			g_source_remove(send_source_id);
 	}
 }
 
-void on_debug_load_error(GArray *nodes)
+static void gdb_exit_cb(G_GNUC_UNUSED GPid pid, gint status, G_GNUC_UNUSED gpointer gdata)
 {
-	debug_load_error = TRUE;
-	on_error(nodes);
-}
+	GdbState saved_state = gdb_state;
 
-void on_debug_auto_run(G_GNUC_UNUSED GArray *nodes)
-{
-	if (debug_auto_run && !thread_count)
-	{
-		if (breaks_active())
-			debug_send_command(N, "-exec-run");
-		else
-			dialogs_show_msgbox(GTK_MESSAGE_INFO, _("No breakpoints. Hanging."));
-	}
-}
+	gdb_finalize();
+	gdb_state = INACTIVE;
 
-static void gdb_exit(void)
-{
-	debug_send_command(N, "-gdb-exit");
-	gdb_state = KILLING;
-}
+	if (saved_state == ACTIVE)
+		show_error(_("GDB died unexpectedly with status %d."), status);
+	else if (thread_count)
+		ui_set_statusbar(FALSE, _("Program terminated."));
 
-void on_debug_auto_exit(void)
-{
-	if (debug_auto_exit)
-		gdb_exit();
+	views_clear();
+	utils_lock_all(FALSE);
+	update_state(DS_INACTIVE);
 }
 
-static GSourceFuncs gdb_source_funcs =
-{
-	source_prepare,
-	source_check,
-	source_dispatch,
-	source_finalize,
-	NULL,
-	NULL
-};
-
 static void append_startup(const char *command, const gchar *value)
 {
 	if (value && *value)
@@ -502,7 +373,15 @@ static void append_startup(const char *command, const gchar *value)
 	}
 }
 
-#define GDB_SPAWN_FLAGS (G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD)
+#if HAS_SPAWN_LEAVE_STDIN_OPEN
+#define GDB_SPAWN_FLAGS (SPAWN_STDERR_UNBUFFERED | SPAWN_STDOUT_RECURSIVE | \
+	SPAWN_STDERR_RECURSIVE | SPAWN_LEAVE_STDIN_OPEN)
+#else
+#define GDB_SPAWN_FLAGS (SPAWN_STDERR_UNBUFFERED | SPAWN_STDOUT_RECURSIVE | \
+	SPAWN_STDERR_RECURSIVE)
+#endif
+
+#define GDB_BUFFER_SIZE ((1 << 20) - 1)  /* spawn adds 1 for '\0' */
 
 static void load_program(void)
 {
@@ -515,83 +394,62 @@ static void load_program(void)
 	while (gtk_events_pending())
 		gtk_main_iteration();
 
-	if (g_spawn_async_with_pipes(NULL, args, NULL, GDB_SPAWN_FLAGS, NULL, NULL, &gdb_pid,
-		&gdb_in.fd, &gdb_out.fd, &gdb_err.fd, &gerror))
+	if (spawn_with_callbacks(NULL, NULL, args, NULL, GDB_SPAWN_FLAGS, obtain_send_channel_cb,
+		NULL, receive_output_cb, NULL, GDB_BUFFER_SIZE, receive_errors_cb, NULL, 0,
+		gdb_exit_cb, NULL, &gdb_pid, &gerror))
 	{
+		gchar **environment = g_strsplit(program_environment, "\n", -1);
+		gchar *const *envar;
+	#ifdef G_OS_UNIX
+		extern char *slave_pty_name;
+	#else
+		GString *escaped = g_string_new(program_executable);
+	#endif
+
+		/* startup */
 		gdb_state = ACTIVE;
+		dc_clear();
+		utils_lock_all(TRUE);
+		signal(SIGINT, SIG_IGN);
+		wait_result = 0;
+		wait_prompt = TRUE;
+		g_string_truncate(commands, 0);
+		leading_receive = TRUE;
 
-		if (utils_set_nonblock(&gdb_in) && utils_set_nonblock(&gdb_out) &&
-			utils_set_nonblock(&gdb_err))
+		if (pref_gdb_async_mode)
+			g_string_append(commands, "-gdb-set target-async on\n");
+		if (program_non_stop_mode)
+			g_string_append(commands, "-gdb-set non-stop on\n");
+	#ifdef G_OS_UNIX
+		append_startup("010-file-exec-and-symbols", program_executable);
+		append_startup("-gdb-set inferior-tty", slave_pty_name);
+	#else  /* G_OS_UNIX */
+		utils_string_replace_all(escaped, "\\", "\\\\");
+		append_startup("010-file-exec-and-symbols", escaped->str);
+		g_string_free(escaped, TRUE);
+		g_string_append(commands, "-gdb-set new-console on\n");
+	#endif  /* G_OS_UNIX */
+		append_startup("-environment-cd", program_working_dir);  /* no escape needed */
+		append_startup("-exec-arguments", program_arguments);
+		for (envar = environment; *envar; envar++)
+			append_startup("-gdb-set environment", *envar);
+		g_strfreev(environment);
+		append_startup("011source -v", program_load_script);
+		g_string_append(commands, "07-list-target-features\n");
+		breaks_query_async(commands);
+
+		if (*program_executable || *program_load_script)
 		{
-			gchar **environment = g_strsplit(program_environment, "\n", -1);
-			gchar *const *envar;
-		#ifdef G_OS_UNIX
-			extern char *slave_pty_name;
-		#else
-			GString *escaped = g_string_new(program_executable);
-		#endif
-
-			/* startup */
-			dc_clear();
-			utils_lock_all(TRUE);
-			signal(SIGINT, SIG_IGN);
-			wait_result = 0;
-			wait_prompt = TRUE;
-			g_string_truncate(commands, 0);
-			g_string_truncate(received, 0);
-			reading_pos = received->str;
-			leading_receive = TRUE;
-
-			gdb_source = g_source_new(&gdb_source_funcs, sizeof(GSource));
-			g_source_set_can_recurse(gdb_source, TRUE);
-			source_id = g_source_attach(gdb_source, NULL);
-			g_source_unref(gdb_source);
-		#ifdef G_OS_UNIX
-			g_source_add_poll(gdb_source, &gdb_out);
-			g_source_add_poll(gdb_source, &gdb_err);
-		#endif
-			if (pref_gdb_async_mode)
-				g_string_append(commands, "-gdb-set target-async on\n");
-			if (program_non_stop_mode)
-				g_string_append(commands, "-gdb-set non-stop on\n");
-		#ifdef G_OS_UNIX
-			append_startup("010-file-exec-and-symbols", program_executable);
-			append_startup("-gdb-set inferior-tty", slave_pty_name);
-		#else  /* G_OS_UNIX */
-			utils_string_replace_all(escaped, "\\", "\\\\");
-			append_startup("010-file-exec-and-symbols", escaped->str);
-			g_string_free(escaped, TRUE);
-		#endif  /* G_OS_UNIX */
-			append_startup("-environment-cd", program_working_dir);
-			append_startup("-exec-arguments", program_arguments);
-			for (envar = environment; *envar; envar++)
-				append_startup("-gdb-set environment", *envar);
-			g_strfreev(environment);
-			append_startup("011source -v", program_load_script);
-			g_string_append(commands, "07-list-target-features\n");
-			breaks_query_async(commands);
-
-			if (*program_executable || *program_load_script)
-			{
-				debug_load_error = FALSE;
-				debug_auto_run = debug_auto_exit = program_auto_run_exit;
-			}
-			else
-				debug_auto_run = debug_auto_exit = FALSE;
-
-			if (option_open_panel_on_load)
-				open_debug_panel();
-
-			registers_query_names();
-			debug_send_commands();
+			debug_load_error = FALSE;
+			debug_auto_run = debug_auto_exit = program_auto_run_exit;
 		}
 		else
-		{
-			show_errno("fcntl(O_NONBLOCK)");
+			debug_auto_run = debug_auto_exit = FALSE;
 
-			if (kill(gdb_pid, SIGKILL) == -1)
-				show_errno("kill(gdb)");
-		}
+		if (option_open_panel_on_load)
+			open_debug_panel();
+
+		registers_query_names();
 	}
 	else
 	{
@@ -671,25 +529,35 @@ void on_debug_terminate(const MenuItem *menu_item)
 {
 	switch (debug_state())
 	{
-		case DS_DEBUG :
+		case DS_BUSY :
+		{
+			GError *gerror = NULL;
+
+			gdb_state = KILLING;
+
+			if (!spawn_kill_process(gdb_pid, &gerror))
+			{
+				show_error(_("%s."), gerror->message);
+				g_error_free(gerror);
+			}
+
+			break;
+		}
 		case DS_READY :
+		case DS_DEBUG :
 		{
 			if (menu_item && !debug_auto_exit)
 			{
 				debug_send_command(N, "kill");
 				break;
 			}
-		}
-		case DS_HANGING :
-		{
-			gdb_exit();
-			break;
+			/* falldown */
 		}
 		default :
 		{
+			debug_send_command(N, "-gdb-exit");
 			gdb_state = KILLING;
-			if (kill(gdb_pid, SIGKILL) == -1)
-				show_errno("kill(gdb)");
+			break;
 		}
 	}
 }
@@ -698,7 +566,6 @@ void debug_send_command(gint tf, const char *command)
 {
 	if (gdb_state == ACTIVE)
 	{
-		gsize previous_len = commands->len;
 		const char *s;
 
 		for (s = command; *s && !isspace(*s); s++);
@@ -715,8 +582,8 @@ void debug_send_command(gint tf, const char *command)
 		g_string_append(commands, s);
 		g_string_append_c(commands, '\n');
 
-		if (!previous_len)
-			debug_send_commands();
+		if (send_channel && !send_source_id)
+			create_send_source();
 	}
 }
 
@@ -753,33 +620,16 @@ char *debug_send_evaluate(char token, gint scid, const gchar *expr)
 void debug_init(void)
 {
 	commands = g_string_sized_new(0x3FFF);
-	received = g_string_sized_new(pref_gdb_buffer_length);
-	MAXLEN = received->allocated_len - 1;
 }
 
 void debug_finalize(void)
 {
-	if (source_id)
-	{
-		signal(SIGINT, SIG_DFL);
-		g_source_remove(source_id);
-	}
-
 	if (gdb_state != INACTIVE)
 	{
-		gint count = 0;
-
-		if (kill(gdb_pid, SIGKILL) == 0)
-		{
-			g_usleep(G_USEC_PER_SEC / 1000);
-			while (waitpid(gdb_pid, NULL, WNOHANG) == 0 && count++ < pref_gdb_wait_death)
-				g_usleep(G_USEC_PER_SEC / 100);
-		}
-
-		free_gdb();  /* may be still alive, but we can't do anything more */
+		spawn_kill_process(gdb_pid, NULL);
+		gdb_finalize();
 		statusbar_update_state(DS_INACTIVE);
 	}
 
-	g_string_free(received, TRUE);
 	g_string_free(commands, TRUE);
 }


Modified: scope/src/prefs.c
30 lines changed, 27 insertions(+), 3 deletions(-)
===================================================================
@@ -28,7 +28,6 @@
 
 gchar *pref_gdb_executable;
 gboolean pref_gdb_async_mode;
-gint pref_gdb_buffer_length;
 gint pref_gdb_wait_death;
 #ifndef G_OS_UNIX
 gint pref_gdb_send_interval;
@@ -143,6 +142,9 @@ static void load_scope_prefs(GKeyFile *config)
 	}
 }
 
+static const char *obsolete_prefs[] = { "gdb_buffer_length", "gdb_wait_death",
+	"gdb_send_interval", NULL };
+
 static void save_scope_prefs(GKeyFile *config)
 {
 	guint i;
@@ -165,6 +167,9 @@ static void save_scope_prefs(GKeyFile *config)
 		g_key_file_set_string(config, style->name, "back", tmp_string);
 		g_free(tmp_string);
 	}
+
+	for (i = 0; obsolete_prefs[i]; i++)
+		g_key_file_remove_key(config, "scope", obsolete_prefs[i], NULL);
 }
 
 static void prefs_configure(void)
@@ -223,11 +228,11 @@ void prefs_init(void)
 	char *configdir = g_build_filename(geany->app->configdir, "plugins", "scope", NULL);
 	char *configfile = prefs_file_name();
 	GKeyFile *config = g_key_file_new();
+	gboolean obsolete = FALSE;
 
 	group = stash_group_new("scope");
 	stash_group_add_string(group, &pref_gdb_executable, "gdb_executable", "gdb");
 	stash_group_add_boolean(group, &pref_gdb_async_mode, "gdb_async_mode", FALSE);
-	stash_group_add_integer(group, &pref_gdb_buffer_length, "gdb_buffer_length", 32767);
 	stash_group_add_integer(group, &pref_gdb_wait_death, "gdb_wait_death", 20);
 #ifndef G_OS_UNIX
 	stash_group_add_integer(group, &pref_gdb_send_interval, "gdb_send_interval", 5);
@@ -282,11 +287,30 @@ void prefs_init(void)
 
 	g_key_file_load_from_file(config, configfile, G_KEY_FILE_NONE, NULL);
 	load_scope_prefs(config);
+
+	for (i = 0; obsolete_prefs[i]; i++)
+	{
+		GError *gerror = NULL;
+
+		g_key_file_get_integer(config, "scope", obsolete_prefs[i], &gerror);
+
+		if (gerror)
+		{
+			g_error_free(gerror);
+			gerror = NULL;
+		}
+		else
+		{
+			obsolete = TRUE;
+			break;
+		}
+	}
+
 	pref_sci_marker_first = pref_sci_marker_1st;
 	prefs_configure();
 	program_load_config(config);
 
-	if (!g_file_test(configfile, G_FILE_TEST_IS_REGULAR))
+	if (obsolete || !g_file_test(configfile, G_FILE_TEST_IS_REGULAR))
 	{
 		gint error = utils_mkdir(configdir, TRUE);
 


Modified: scope/src/prefs.h
1 lines changed, 0 insertions(+), 1 deletions(-)
===================================================================
@@ -21,7 +21,6 @@
 
 extern gchar *pref_gdb_executable;
 extern gboolean pref_gdb_async_mode;
-extern gint pref_gdb_buffer_length;
 extern gint pref_gdb_wait_death;
 #ifndef G_OS_UNIX
 extern gint pref_gdb_send_interval;


Modified: scope/src/scope.c
4 lines changed, 2 insertions(+), 2 deletions(-)
===================================================================
@@ -29,10 +29,10 @@ GeanyPlugin *geany_plugin;
 GeanyData *geany_data;
 GeanyFunctions *geany_functions;
 
-PLUGIN_VERSION_CHECK(215)
+PLUGIN_VERSION_CHECK(224)
 
 PLUGIN_SET_TRANSLATABLE_INFO(LOCALEDIR, GETTEXT_PACKAGE, _("Scope Debugger"),
-	_("Relatively simple GDB front-end."), "0.93.2",
+	_("Relatively simple GDB front-end."), "0.94",
 	"Dimitar Toshkov Zhekov <dimitar.zhekov at gmail.com>")
 
 /* Keybinding(s) */



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Plugins-Commits mailing list