Updated French translation
[bug-buddy.git] / src / gdb-buddy.c
blob16cbe8d58c5f971c3d818da331ea4b2c1951d8ad
1 /* bug-buddy bug submitting program
3 * Copyright (C) Jacob Berkman
5 * Author: Jacob Berkman <jacob@bug-buddy.org>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21 #include <config.h>
23 #include <stdio.h>
25 #include <sys/wait.h>
26 #include <signal.h>
27 #include <unistd.h>
28 #include <math.h>
29 #include <string.h>
31 #include <glib/gi18n.h>
32 #include <gtk/gtk.h>
34 #include "gdb-buddy.h"
36 #define d(x)
38 typedef struct {
39 gint pid;
40 GIOChannel *ioc;
41 GString *stacktrace;
42 gpointer user_data;
43 GdbCallback callback;
44 } GdbData;
46 static void
47 gdb_stop (GIOChannel *ioc, int pid)
49 if (!ioc) {
50 d(g_message (_("gdb has already exited")));
51 return;
54 g_io_channel_shutdown (ioc, 1, NULL);
56 kill (pid, SIGTERM);
57 /* i don't think we need to SIGKILL it */
58 /*kill (druid_data.gdb_pid, SIGKILL);*/
59 waitpid (pid, NULL, 0);
61 pid = 0;
63 #if 0
64 /* sometimes gdb doesn't restart the old app... */
65 if (druid_data.app_pid) {
66 kill (druid_data.app_pid, SIGCONT);
67 druid_data.app_pid = 0;
69 #endif
72 static gboolean
73 gdb_handle_input (GIOChannel *ioc, GIOCondition condition, gpointer data)
75 gboolean retval = FALSE;
76 gchar buf[1024];
77 gsize len;
78 GIOStatus io_status;
79 GdbData *gdb_data = (GdbData *)data;
81 while (gtk_events_pending ())
82 gtk_main_iteration ();
84 gdb_try_read:
85 io_status = g_io_channel_read_chars (ioc, buf, 1024, &len, NULL);
87 switch (io_status) {
88 case G_IO_STATUS_AGAIN:
89 goto gdb_try_read;
90 case G_IO_STATUS_ERROR:
91 d(g_warning (_("Error on read; aborting.")));
92 break;
93 case G_IO_STATUS_NORMAL:
94 retval = TRUE;
95 break;
96 default:
97 break;
100 if (len > 0) {
101 char *utftext;
102 gsize localelen;
103 gsize utflen;
105 /* gdb charset is ISO-8859-1 */
106 utftext = g_convert_with_fallback (buf, len, "UTF-8", "ISO-8859-1", NULL, &localelen, &utflen, NULL);
107 gdb_data->stacktrace = g_string_append (gdb_data->stacktrace, utftext);
108 g_free (utftext);
111 if (!retval || io_status == G_IO_STATUS_EOF) {
112 gdb_stop (ioc, gdb_data->pid);
113 gdb_data->pid = 0;
114 gdb_data->ioc = NULL;
116 /* call the user specified callback function with the string containing
117 * the stacktrace, and the user specified data */
118 (*(gdb_data->callback))(gdb_data->stacktrace->str, gdb_data->user_data);
119 return FALSE;
122 return retval;
125 static void
126 gdb_destroy (gpointer user_data)
128 GdbData *gdb_data;
130 g_return_if_fail (user_data != NULL);
132 d (g_print ("entering gdb_destroy\n"));
134 gdb_data = (GdbData *)user_data;
136 if (gdb_data->pid != 0 && gdb_data->ioc != NULL) {
137 d (g_print ("stopping gdb, pid = %d\n", gdb_data->pid));
138 gdb_stop (gdb_data->ioc, gdb_data->pid);
141 if (gdb_data->pid != 0)
142 gdb_data->pid = 0;
144 if (gdb_data->ioc != NULL)
145 gdb_data->ioc = NULL;
147 if (gdb_data->stacktrace != NULL)
148 g_string_free (gdb_data->stacktrace, TRUE);
150 g_free (gdb_data);
152 return;
155 GQuark
156 gdb_buddy_error_quark (void)
158 return g_quark_from_static_string ("gdb_buddy_error");
161 static char *
162 get_process_executable (int pid)
164 char *link, *exe;
165 GError *error = NULL;
167 #if defined (__linux__)
168 link = g_strdup_printf ("/proc/%d/exe", pid);
169 #elif defined (sun) && defined (__SVR4)
170 link = g_strdup_printf ("/proc/%d/path/a.out", pid);
171 #else
172 /* if someone knows how to do this on BSD, please send a patch */
173 return NULL;
174 #endif
175 exe = g_file_read_link (link, &error);
177 if (error) {
178 g_warning ("Could not read %s: %s\n", link, error->message);
179 g_error_free (error);
182 return exe;
186 * @app: the executable name of the program that crashed
187 * @pid: the process id of the application that crashed
188 * @gdb_pid: the pid of the GDB process that is collecting the stacktrace
189 * @user_data: a pointer that will be passed to the gdb_finish function
190 * @gdb_finish: A callback function that is called after gdb finishes getting the stack trace
191 * @err: if the function returns NULL, then *err is populated with a GError
193 * This function calls the gdb_finish callback function with backtrace obtained from gdb
194 * and the pointer user_data, for a given process id and application name. If there is an error
195 * during processing, *err will be populated with a GError message.
197 * This function returns the event source id that is added to the main loop, or zero if a failure
198 * occurs in the function. If you need to terminate the application before the gdb_finish callback
199 * has been called, then use g_source_remove(). This will cleanup the GIOChannel and stop any
200 * running gdb processes.
202 guint
203 gdb_get_trace (const gchar *app, int pid, gpointer user_data, GdbCallback gdb_finish, GError **err)
205 char *s;
206 char *long_app;
207 int gdb_pid;
208 int fd;
209 guint source_id;
210 GIOChannel *ioc;
211 GError *error = NULL;
212 GdbData *gdb_data = NULL;
213 char *args[] = { "gdb",
214 "--batch",
215 "--quiet",
216 "--command=" BUDDY_DATADIR "/gdb-cmd",
217 NULL, NULL, NULL };
219 g_return_val_if_fail (app != NULL, 0);
220 g_return_val_if_fail (*app != '\0', 0);
221 g_return_val_if_fail (pid != 0, 0);
222 g_return_val_if_fail (gdb_finish != NULL, 0);
223 g_return_val_if_fail (pid != 0, 0);
224 g_return_val_if_fail (err == NULL || *err == NULL, 0);
226 d (g_print ("app=%s\n", app));
228 /* apply a SIGCONT to the process */
229 kill (pid, SIGCONT);
231 /* to get the application path do the following:
232 * - if the path is absolute, it has been provided by the user, so just use it.
233 * - otherwise look for it in /proc based on its pid.
234 * - if that fails, look for the program in path.
235 * - finally, use $libexecdir as fallback.
238 if (g_path_is_absolute (app)) {
239 long_app = g_strdup (app);
240 } else {
241 long_app = get_process_executable (pid);
244 if (!long_app) {
245 long_app = g_find_program_in_path (app);
246 if (!long_app) {
247 /* this should never fail */
248 long_app = g_strconcat (GNOMELIBEXECDIR,"/", app, NULL);
252 args[0] = g_find_program_in_path ("gdb");
253 args[4] = long_app;
255 if (args[0] == NULL) {
256 d(g_message ("Path: %s", getenv ("PATH")));
257 g_free (long_app);
258 g_set_error (err, GDB_BUDDY_ERROR, GDB_BUDDY_GDB_NOT_FOUND,
259 _("GDB could not be found on your system. "
260 "Debugging information will not be obtained."));
261 return 0;
264 d(g_message ("About to debug '%s'", long_app));
266 if (!g_file_test (BUDDY_DATADIR "/gdb-cmd", G_FILE_TEST_EXISTS)) {
267 g_set_error (err, GDB_BUDDY_ERROR, GDB_BUDDY_GDB_CMD_NOT_FOUND,
268 _("Could not find the gdb-cmd file.\n"
269 "Please try reinstalling Bug Buddy."));
270 g_free (args[0]);
271 g_free (long_app);
272 return 0;
275 args[5] = g_strdup_printf ("%d", pid);
277 if (!g_spawn_async_with_pipes (NULL, args, NULL, 0, NULL, NULL,
278 &gdb_pid,
279 NULL,
280 &fd,
281 NULL, &error)) {
282 g_set_error (err, GDB_BUDDY_ERROR, GDB_BUDDY_GDB_ERROR,
283 _("There was an error running gdb:\n\n%s"),
284 error->message);
285 g_error_free (error);
286 g_free (args[0]);
287 g_free (args[5]);
288 g_free (long_app);
289 return 0;
292 ioc = g_io_channel_unix_new (fd);
293 g_io_channel_set_encoding (ioc, NULL, NULL);
294 g_io_channel_set_flags (ioc, G_IO_FLAG_NONBLOCK, &error);
296 gdb_data = g_new0 (GdbData, 1);
298 s = g_strdup_printf ("Backtrace was generated from '%s'\n\n", long_app);
299 gdb_data->stacktrace = g_string_new (s);
300 g_free (s);
302 gdb_data->pid = gdb_pid;
303 gdb_data->ioc = ioc;
304 gdb_data->user_data = user_data;
305 gdb_data->callback = gdb_finish;
307 source_id = g_io_add_watch_full (ioc, 100, G_IO_IN | G_IO_HUP,
308 gdb_handle_input, gdb_data, gdb_destroy);
309 g_io_channel_unref (ioc);
311 g_free (args[0]);
312 g_free (args[5]);
313 g_free (long_app);
315 return source_id;