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.
31 #include <glib/gi18n.h>
34 #include "gdb-buddy.h"
47 gdb_stop (GIOChannel
*ioc
, int pid
)
50 d(g_message (_("gdb has already exited")));
54 g_io_channel_shutdown (ioc
, 1, NULL
);
57 /* i don't think we need to SIGKILL it */
58 /*kill (druid_data.gdb_pid, SIGKILL);*/
59 waitpid (pid
, NULL
, 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;
73 gdb_handle_input (GIOChannel
*ioc
, GIOCondition condition
, gpointer data
)
75 gboolean retval
= FALSE
;
79 GdbData
*gdb_data
= (GdbData
*)data
;
81 while (gtk_events_pending ())
82 gtk_main_iteration ();
85 io_status
= g_io_channel_read_chars (ioc
, buf
, 1024, &len
, NULL
);
88 case G_IO_STATUS_AGAIN
:
90 case G_IO_STATUS_ERROR
:
91 d(g_warning (_("Error on read; aborting.")));
93 case G_IO_STATUS_NORMAL
:
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
);
111 if (!retval
|| io_status
== G_IO_STATUS_EOF
) {
112 gdb_stop (ioc
, gdb_data
->pid
);
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
);
126 gdb_destroy (gpointer user_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)
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
);
156 gdb_buddy_error_quark (void)
158 return g_quark_from_static_string ("gdb_buddy_error");
162 get_process_executable (int pid
)
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
);
172 /* if someone knows how to do this on BSD, please send a patch */
175 exe
= g_file_read_link (link
, &error
);
178 g_warning ("Could not read %s: %s\n", link
, error
->message
);
179 g_error_free (error
);
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.
203 gdb_get_trace (const gchar
*app
, int pid
, gpointer user_data
, GdbCallback gdb_finish
, GError
**err
)
211 GError
*error
= NULL
;
212 GdbData
*gdb_data
= NULL
;
213 char *args
[] = { "gdb",
216 "--command=" BUDDY_DATADIR
"/gdb-cmd",
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 */
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
);
241 long_app
= get_process_executable (pid
);
245 long_app
= g_find_program_in_path (app
);
247 /* this should never fail */
248 long_app
= g_strconcat (GNOMELIBEXECDIR
,"/", app
, NULL
);
252 args
[0] = g_find_program_in_path ("gdb");
255 if (args
[0] == NULL
) {
256 d(g_message ("Path: %s", getenv ("PATH")));
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."));
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."));
275 args
[5] = g_strdup_printf ("%d", pid
);
277 if (!g_spawn_async_with_pipes (NULL
, args
, NULL
, 0, NULL
, NULL
,
282 g_set_error (err
, GDB_BUDDY_ERROR
, GDB_BUDDY_GDB_ERROR
,
283 _("There was an error running gdb:\n\n%s"),
285 g_error_free (error
);
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
);
302 gdb_data
->pid
= gdb_pid
;
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
);