~ubuntu-branches/ubuntu/karmic/moon/karmic

« back to all changes in this revision

Viewing changes to test/harness/agviewer/agviewer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-14 12:01:08 UTC
  • Revision ID: james.westby@ubuntu.com-20090214120108-06539vb25vhbd8bn
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Permission is hereby granted, free of charge, to any person obtaining
 
3
 * a copy of this software and associated documentation files (the
 
4
 * "Software"), to deal in the Software without restriction, including
 
5
 * without limitation the rights to use, copy, modify, merge, publish,
 
6
 * distribute, sublicense, and/or sell copies of the Software, and to
 
7
 * permit persons to whom the Software is furnished to do so, subject to
 
8
 * the following conditions:
 
9
 *
 
10
 * The above copyright notice and this permission notice shall be
 
11
 * included in all copies or substantial portions of the Software.
 
12
 *
 
13
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
14
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
15
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
16
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
17
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
18
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
19
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
20
 *
 
21
 * Copyright (c) 2007-2008 Novell, Inc.
 
22
 *
 
23
 * Authors:
 
24
 *      Jackson Harper (jackson@ximian.com)
 
25
 *
 
26
 */
 
27
 
 
28
 
 
29
#include <string.h>
 
30
#include "config.h"
 
31
#include <stdlib.h>
 
32
#include <unistd.h>
 
33
#include <gtk/gtk.h>
 
34
#include <gdk/gdkkeysyms.h>
 
35
#include <dbus/dbus-glib.h>
 
36
#include <dbus/dbus-glib-bindings.h>
 
37
#include <signal.h>
 
38
#include <sys/syscall.h>
 
39
#include <sys/wait.h>
 
40
#include <dlfcn.h>
 
41
 
 
42
#if HAVE_GECKO_1_9
 
43
#include <gtkmozembed.h>
 
44
#else
 
45
#include "gtkembedmoz/gtkmozembed.h"
 
46
#endif
 
47
 
 
48
 
 
49
#include "agserver.h"
 
50
#include "AgserverObject.h"
 
51
 
 
52
#include "nsXPCOMGlue.h"
 
53
 
 
54
 
 
55
 
 
56
#define DRT_SERVICE             "mono.moonlight.tests"
 
57
 
 
58
#define DRT_LOGGER_PATH         "/mono/moonlight/tests/logger"
 
59
#define DRT_LOGGER_INTERFACE    "mono.moonlight.tests.logger.ITestLogger"
 
60
 
 
61
#define DRT_RUNNER_PATH         "/mono/moonlight/tests/runner"
 
62
#define DRT_RUNNER_INTERFACE    "mono.moonlight.tests.runner.ITestRunner"
 
63
 
 
64
 
 
65
#define DRT_AGSERVER_SERVICE    "mono.moonlight.agserver"
 
66
#define DRT_AGSERVER_PATH       "/mono/moonlight/agserver"
 
67
#define DRT_AGSERVER_INTERFACE  "mono.moonlight.agserver.IAgserver"
 
68
 
 
69
 
 
70
#define DEFAULT_TIMEOUT 20000
 
71
 
 
72
 
 
73
typedef struct _AgViewer {
 
74
        GtkWindow  *top_level_window;
 
75
        GtkWidget  *moz_embed;
 
76
} AgViewer;
 
77
 
 
78
 
 
79
 
 
80
static AgViewer* browser = NULL;
 
81
static char* test_path = NULL;
 
82
static const char* working_dir = NULL;
 
83
static guint timeout_id = 0;
 
84
static time_t hung_time = 0;
 
85
 
 
86
static void run_test (char* test_path, int timeout);
 
87
static bool move_to_next_test ();
 
88
static void request_test_runner_shutdown ();
 
89
static void signal_test_complete (const char* test_name, bool successful);
 
90
static bool wait_for_next_test (int* timeout);
 
91
static void mark_test_as_complete_and_start_next_test (gboolean successful);
 
92
static void log_message (const char* test_name, const char* message);
 
93
static void log_output (const char* test_name, const char* message, const char* level);
 
94
static void set_current_dir (const char* test_path);
 
95
static void agviewer_handle_native_sigsegv (int signal);
 
96
static void agviewer_add_signal_handler ();
 
97
static void print_stack_traces ();
 
98
 
 
99
static gpointer
 
100
hang_detector (gpointer data)
 
101
{
 
102
        time_t now;
 
103
        
 
104
        while (true) {
 
105
                now = time (NULL);
 
106
                if (hung_time > 0 && hung_time < now) {
 
107
                        fprintf (stderr, "Agviewer seems to be hung, aborting process.\n");
 
108
                        print_stack_traces ();
 
109
                        abort ();
 
110
                }
 
111
                //printf ("hang_detector: We're %li seconds from hanging\n", hung_time - now);
 
112
                // Check once a second.
 
113
                g_usleep (G_USEC_PER_SEC);
 
114
        }
 
115
}
 
116
 
 
117
static void
 
118
agserver_class_init (AgserverClass* agserver_class)
 
119
{
 
120
        dbus_g_object_type_install_info (AGSERVER_TYPE, &dbus_glib_agserver_object_info);
 
121
}
 
122
 
 
123
static void
 
124
agserver_init (Agserver* agserver)
 
125
{
 
126
}
 
127
 
 
128
//
 
129
// Called from libshocker when a test is finished
 
130
//
 
131
gboolean
 
132
signal_shutdown (Agserver* server, GError** error)
 
133
{
 
134
        mark_test_as_complete_and_start_next_test (true);
 
135
        return true;
 
136
}
 
137
 
 
138
 
 
139
static void
 
140
register_agserver_object ()
 
141
{
 
142
        DBusGConnection *connection;
 
143
        DBusGProxy *proxy;
 
144
        GError *error = NULL;
 
145
        GObject *obj;
 
146
        guint request_name_ret;
 
147
 
 
148
        g_type_init ();
 
149
 
 
150
        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
 
151
        if (connection == NULL) {
 
152
                printf ("Failed to open connection to bus  %s\n", error->message);
 
153
                return;
 
154
        }
 
155
 
 
156
        proxy = dbus_g_proxy_new_for_name (connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
 
157
 
 
158
        if (!org_freedesktop_DBus_request_name (proxy, DRT_AGSERVER_SERVICE, 0, &request_name_ret, &error)) {
 
159
                printf ("Unable to request name:  %s\n", error->message);
 
160
                return;
 
161
        }
 
162
 
 
163
        if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
 
164
                printf ("Unable to become the primary owner of IAgserver interface.\n");
 
165
                return;
 
166
        }
 
167
 
 
168
        obj = G_OBJECT (g_object_new (AGSERVER_TYPE, NULL));
 
169
        dbus_g_connection_register_g_object (connection, DRT_AGSERVER_PATH, obj);
 
170
}
 
171
 
 
172
 
 
173
 
 
174
 
 
175
static int close_key_pressed_count = 0;
 
176
static AgViewer *new_gtk_browser ();
 
177
static gboolean key_press_cb (GtkWidget* widget, GdkEventKey* event, GtkWindow* window);
 
178
 
 
179
static gboolean test_timeout (gpointer data);
 
180
 
 
181
 
 
182
static void
 
183
set_current_dir (const char* test_path)
 
184
{
 
185
        char *dir = NULL;
 
186
        
 
187
        if (g_str_has_prefix (test_path, "http")) {
 
188
                if (chdir (working_dir) != 0)
 
189
                        g_warning ("Unable to set working directory to: %s\n", working_dir);
 
190
        } else {
 
191
                if (!g_path_is_absolute (test_path)) {
 
192
                        char* wd = get_current_dir_name ();
 
193
                        char* abs_path = g_build_filename (wd, test_path, NULL);
 
194
                        
 
195
                        dir = g_path_get_dirname (test_path);
 
196
                        
 
197
                        free (wd);
 
198
                        g_free (abs_path);
 
199
                } else {
 
200
                        dir = g_path_get_dirname (test_path);
 
201
                }
 
202
                
 
203
                if (chdir (dir) != 0)
 
204
                        g_warning ("Unable to set working directory to: %s\n", dir);
 
205
                        
 
206
                g_free (dir);
 
207
        }
 
208
}
 
209
 
 
210
int
 
211
main(int argc, char **argv)
 
212
{
 
213
        int frame_width = 800;
 
214
        int frame_height = 800;
 
215
 
 
216
        agviewer_add_signal_handler ();
 
217
        
 
218
        gtk_init (&argc, &argv);
 
219
 
 
220
        if (!g_thread_supported ())
 
221
                g_thread_init (NULL);
 
222
        g_thread_create (hang_detector, NULL, FALSE, NULL);
 
223
        
 
224
        int i;
 
225
        for (i = 1; i < argc && argv[i][0] == '-'; i++) {
 
226
                if (!g_strcasecmp ("-framewidth", argv [i]) && i < argc) {
 
227
                        frame_width = strtol (argv [++i], NULL, 10);
 
228
                        continue;
 
229
                }
 
230
                
 
231
                if (!g_strcasecmp ("-frameheight", argv [i]) && i < argc) {
 
232
                        frame_height = strtol (argv [++i], NULL, 10);
 
233
                        continue;
 
234
                }
 
235
                
 
236
                if (!g_strcasecmp ("-working-dir", argv [i]) && i < argc) {
 
237
                        working_dir = argv [++i];
 
238
                        continue;
 
239
                }
 
240
                
 
241
                if (!g_strcasecmp ("-server", argv [i]))
 
242
                        continue;
 
243
        }
 
244
        
 
245
        if (i < argc)
 
246
                test_path = g_strdup (argv [argc - 1]);
 
247
 
 
248
        browser = new_gtk_browser ();
 
249
 
 
250
        gtk_widget_set_usize (browser->moz_embed, frame_width, frame_height);
 
251
        gtk_window_fullscreen (browser->top_level_window);
 
252
        //gtk_window_set_keep_above (browser->top_level_window, TRUE);
 
253
        gtk_widget_show_all (browser->moz_embed);
 
254
        gtk_widget_show_all (GTK_WIDGET (browser->top_level_window));
 
255
 
 
256
        register_agserver_object ();
 
257
        
 
258
        if (test_path && test_path[0]) {
 
259
                run_test (test_path, DEFAULT_TIMEOUT);
 
260
                gtk_main ();
 
261
        } else {
 
262
                if (move_to_next_test ())
 
263
                        gtk_main ();
 
264
        }
 
265
        
 
266
        g_free (test_path);
 
267
 
 
268
        return 0;
 
269
}
 
270
 
 
271
static void
 
272
run_test (char* test_path, int timeout)
 
273
{
 
274
        if (timeout_id)
 
275
                g_source_remove (timeout_id);
 
276
        timeout_id = g_timeout_add (timeout, test_timeout, test_path);
 
277
        
 
278
        // Give 60 seconds after timeout, if nothing happened,
 
279
        // we're hung and abort the process.
 
280
        hung_time = time (NULL) + timeout / 1000 + 60;
 
281
        
 
282
        set_current_dir (test_path);
 
283
        log_output (test_path, "[Test start]", "Debug");
 
284
        gtk_moz_embed_load_url (GTK_MOZ_EMBED (browser->moz_embed), test_path);
 
285
}
 
286
 
 
287
 
 
288
static bool
 
289
move_to_next_test ()
 
290
{
 
291
        int timeout = 0;
 
292
        if (wait_for_next_test (&timeout)) {
 
293
                run_test (test_path, timeout);
 
294
                return true;
 
295
        } else {
 
296
                if (gtk_main_level ())
 
297
                        gtk_main_quit ();
 
298
        }
 
299
        return false;
 
300
}
 
301
 
 
302
static void
 
303
mark_test_as_complete_and_start_next_test (gboolean successful)
 
304
{
 
305
        DBusGProxy* dbus_proxy;
 
306
        DBusGConnection* connection;
 
307
        GError* error = NULL;  
 
308
 
 
309
        error = NULL;
 
310
        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
 
311
        if (!connection) {
 
312
                g_warning ("Failed to open connection to bus to get next test, agivewer process will not be reused: %s\n", error->message);
 
313
                g_error_free (error);
 
314
                return;
 
315
        }
 
316
 
 
317
        dbus_proxy = dbus_g_proxy_new_for_name (connection,
 
318
                        DRT_SERVICE,
 
319
                        DRT_RUNNER_PATH,
 
320
                        DRT_RUNNER_INTERFACE);
 
321
 
 
322
        int timeout;
 
323
        gboolean available;
 
324
        char* test_name = g_path_get_basename (test_path);
 
325
 
 
326
        g_free (test_path);
 
327
        test_path = NULL;
 
328
 
 
329
        if (!dbus_g_proxy_call (dbus_proxy, "MarkTestAsCompleteAndGetNextTest", &error,
 
330
                        G_TYPE_STRING, test_name,
 
331
                        G_TYPE_BOOLEAN, successful,
 
332
                        G_TYPE_INVALID,
 
333
                        G_TYPE_BOOLEAN, &available,
 
334
                        G_TYPE_STRING, &test_path,
 
335
                        G_TYPE_INT, &timeout,
 
336
                        G_TYPE_INVALID)) {
 
337
                printf ("error while making MarkTestAsCompleteAndGetNextTest dbus call:   %s\n", error->message);
 
338
                printf ("if you are running standalone tests without a dbus test runner you can ignore this error message\n");
 
339
 
 
340
                available = false;
 
341
        }
 
342
        g_free (test_name);
 
343
 
 
344
        if (available)
 
345
                run_test (test_path, timeout);
 
346
        else
 
347
                gtk_main_quit ();
 
348
}
 
349
 
 
350
 
 
351
static AgViewer *
 
352
new_gtk_browser ()
 
353
{
 
354
        AgViewer *browser = 0;
 
355
        
 
356
        browser = g_new0 (AgViewer, 1);
 
357
        browser->top_level_window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
 
358
 
 
359
        static const GREVersionRange gre_version = {
 
360
                "1.8", PR_TRUE,
 
361
                "9.9", PR_TRUE
 
362
        };
 
363
 
 
364
        char xpcom_lib_path [PATH_MAX];
 
365
        char* xpcom_dir_path;
 
366
 
 
367
        GRE_GetGREPathWithProperties (&gre_version, 1, nsnull, 0, xpcom_lib_path, sizeof (xpcom_lib_path));
 
368
        xpcom_dir_path = g_path_get_dirname (xpcom_lib_path);
 
369
 
 
370
#if HAVE_GECKO_1_9
 
371
        gtk_moz_embed_set_path (xpcom_dir_path);
 
372
#else
 
373
        gtk_moz_embed_set_comp_path (xpcom_dir_path);
 
374
#endif
 
375
        g_free (xpcom_dir_path);
 
376
 
 
377
        browser->moz_embed = gtk_moz_embed_new();
 
378
        gtk_container_add (GTK_CONTAINER (browser->top_level_window),
 
379
                        browser->moz_embed);
 
380
 
 
381
        g_signal_connect (G_OBJECT (browser->top_level_window), "key_press_event", G_CALLBACK (key_press_cb), browser->top_level_window);
 
382
 
 
383
        return browser;
 
384
}
 
385
 
 
386
static gboolean
 
387
key_press_cb (GtkWidget* widget, GdkEventKey* event, GtkWindow* window)
 
388
{
 
389
        if ((event->state & GDK_CONTROL_MASK) == 0)
 
390
                return FALSE;
 
391
 
 
392
        switch (event->keyval) {
 
393
        case GDK_c:
 
394
        case GDK_C:
 
395
                if (close_key_pressed_count == 0)
 
396
                        request_test_runner_shutdown ();
 
397
                else if (close_key_pressed_count > 0)
 
398
                        exit (1);
 
399
                close_key_pressed_count++;
 
400
                return TRUE;
 
401
        case GDK_n:
 
402
        case GDK_N:
 
403
                mark_test_as_complete_and_start_next_test (false);
 
404
                return TRUE;
 
405
        }
 
406
 
 
407
        return FALSE;
 
408
}
 
409
 
 
410
static gboolean
 
411
test_timeout (gpointer data)
 
412
{
 
413
        const char* test = (const char *) data;
 
414
        log_message (test, "test timed out.");
 
415
 
 
416
        mark_test_as_complete_and_start_next_test (false);
 
417
        return FALSE;
 
418
}
 
419
 
 
420
static void
 
421
request_test_runner_shutdown ()
 
422
{
 
423
        g_type_init ();
 
424
 
 
425
        DBusGProxy* dbus_proxy;
 
426
        DBusGConnection* connection;
 
427
        GError* error = NULL;  
 
428
 
 
429
        error = NULL;
 
430
        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
 
431
        if (!connection) {
 
432
                g_warning ("Failed to open connection to bus: %s\n", error->message);
 
433
                g_error_free (error);
 
434
        }
 
435
 
 
436
        dbus_proxy = dbus_g_proxy_new_for_name (connection,
 
437
                        DRT_SERVICE,
 
438
                        DRT_RUNNER_PATH,
 
439
                        DRT_RUNNER_INTERFACE);
 
440
 
 
441
        dbus_g_proxy_call_no_reply (dbus_proxy, "RequestShutdown", G_TYPE_INVALID);
 
442
}
 
443
 
 
444
static void
 
445
signal_test_complete (const char *test_path, bool successful)
 
446
{
 
447
        g_type_init ();
 
448
 
 
449
        DBusGProxy* dbus_proxy;
 
450
        DBusGConnection* connection;
 
451
        GError* error = NULL;  
 
452
 
 
453
        error = NULL;
 
454
        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
 
455
        if (!connection) {
 
456
                g_warning ("Failed to open connection to bus while signalling shutdown: %s\n", error->message);
 
457
                g_error_free (error);
 
458
        }
 
459
 
 
460
        dbus_proxy = dbus_g_proxy_new_for_name (connection,
 
461
                        DRT_SERVICE,
 
462
                        DRT_RUNNER_PATH,
 
463
                        DRT_RUNNER_INTERFACE);
 
464
 
 
465
        char* test_name = g_path_get_basename (test_path);
 
466
        dbus_g_proxy_call_no_reply (dbus_proxy, "TestComplete", G_TYPE_STRING, test_name, G_TYPE_BOOLEAN, successful, G_TYPE_INVALID);
 
467
 
 
468
        g_free (test_name);
 
469
}
 
470
 
 
471
 
 
472
static bool
 
473
wait_for_next_test (int *timeout)
 
474
{
 
475
        g_type_init ();
 
476
 
 
477
        DBusGProxy* dbus_proxy;
 
478
        DBusGConnection* connection;
 
479
        GError* error = NULL;  
 
480
 
 
481
        error = NULL;
 
482
        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
 
483
        if (!connection) {
 
484
                g_warning ("Failed to open connection to bus to get next test, agivewer process will not be reused: %s\n", error->message);
 
485
                g_error_free (error);
 
486
                return false;
 
487
        }
 
488
 
 
489
        dbus_proxy = dbus_g_proxy_new_for_name (connection,
 
490
                        DRT_SERVICE,
 
491
                        DRT_RUNNER_PATH,
 
492
                        DRT_RUNNER_INTERFACE);
 
493
 
 
494
        if (test_path) {
 
495
                g_free (test_path);
 
496
                test_path = NULL;
 
497
        }
 
498
 
 
499
        gboolean available;
 
500
        if (!dbus_g_proxy_call (dbus_proxy, "GetNextTest", &error,
 
501
                        G_TYPE_INVALID,
 
502
                        G_TYPE_BOOLEAN, &available,
 
503
                        G_TYPE_STRING, &test_path,
 
504
                        G_TYPE_INT, timeout,
 
505
                        G_TYPE_INVALID)) {
 
506
                printf ("error while making GetNextTest dbus call:   %s\n", error->message);
 
507
        }
 
508
 
 
509
        return available;
 
510
}
 
511
 
 
512
 
 
513
void
 
514
log_message (const char* test_name, const char* message)
 
515
{
 
516
        log_output (test_name, message, "Error");
 
517
}
 
518
 
 
519
void
 
520
log_output (const char* test_name, const char* message, const char* level)
 
521
{
 
522
        DBusGProxy* dbus_proxy;
 
523
        DBusGConnection* connection;
 
524
        GError* error = NULL;  
 
525
 
 
526
        error = NULL;
 
527
        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
 
528
        if (!connection) {
 
529
                g_warning ("Failed to open connection to bus to log message %s\n", error->message);
 
530
                g_error_free (error);
 
531
        }
 
532
 
 
533
        dbus_proxy = dbus_g_proxy_new_for_name (connection,
 
534
                        DRT_SERVICE,
 
535
                        DRT_LOGGER_PATH,
 
536
                        DRT_LOGGER_INTERFACE);
 
537
 
 
538
        dbus_g_proxy_call_no_reply (dbus_proxy, "Log", G_TYPE_STRING, test_name, G_TYPE_STRING, level, G_TYPE_STRING, message, G_TYPE_INVALID);
 
539
}
 
540
 
 
541
static void
 
542
agviewer_add_signal_handler ()
 
543
{
 
544
        struct sigaction sa;
 
545
 
 
546
        sa.sa_handler = agviewer_handle_native_sigsegv;
 
547
        sigemptyset (&sa.sa_mask);
 
548
        sa.sa_flags = 0;
 
549
 
 
550
        g_assert (sigaction (SIGSEGV, &sa, NULL) != -1);
 
551
        g_assert (sigaction (SIGQUIT, &sa, NULL) != -1);
 
552
}
 
553
 
 
554
/*
 
555
 * agviewer_handle_native_sigsegv: 
 
556
 *   (this is a slightly modified version of mono_handle_native_sigsegv from mono/mono/mini/mini-exceptions.c)
 
557
 *
 
558
 */
 
559
 
 
560
static bool handling_sigsegv = false;
 
561
 
 
562
typedef void void_method ();
 
563
 
 
564
void
 
565
agviewer_handle_native_sigsegv (int signal)
 
566
{
 
567
        const char *signal_str = (signal == SIGSEGV) ? "SIGSEGV" : "SIGABRT";
 
568
        switch (signal) {
 
569
        case SIGSEGV: signal_str = "SIGSEGV"; break;
 
570
        case SIGABRT: signal_str = "SIGABRT"; break;
 
571
        case SIGQUIT: signal_str = "SIGQUIT"; break;
 
572
        default:
 
573
                signal_str = "UNKNOWN"; break;
 
574
        }
 
575
        
 
576
        if (handling_sigsegv) {
 
577
                fprintf (stderr, "\nGot a SIGSEGV while executing the SIGSEGV handler, aborting.\n");
 
578
                abort ();
 
579
                return;
 
580
        }
 
581
 
 
582
        /* To prevent infinite loops when the stack walk causes a crash */
 
583
        handling_sigsegv = true;
 
584
 
 
585
        /*
 
586
         * A SIGSEGV indicates something went very wrong so we can no longer depend
 
587
         * on anything working. So try to print out lots of diagnostics, starting 
 
588
         * with ones which have a greater chance of working.
 
589
         */
 
590
        fprintf (stderr,
 
591
                         "\n"
 
592
                         "=============================================================\n"
 
593
                         "Got a %s while executing native code.                        \n"
 
594
                         " We'll first ask gdb for a stack trace, then try our own     \n"
 
595
                         " stack walking method (usually not as good as gdb, but it    \n"
 
596
                         " can do managed and native stack traces together)            \n"
 
597
                         "=============================================================\n"
 
598
                         "\n", signal_str);
 
599
 
 
600
        print_stack_traces ();          
 
601
 
 
602
        if (signal != SIGQUIT) {
 
603
                abort ();
 
604
        } else {
 
605
                handling_sigsegv = false;
 
606
        }
 
607
}
 
608
 
 
609
static void
 
610
print_stack_traces ()
 
611
{
 
612
        /* Try to get more meaningful information using gdb */
 
613
#if !defined(PLATFORM_WIN32)
 
614
        /* From g_spawn_command_line_sync () in eglib */
 
615
        int res;
 
616
        int stdout_pipe [2] = { -1, -1 };
 
617
        pid_t pid;
 
618
        const char *argv [16];
 
619
        char buf1 [128];
 
620
        int status;
 
621
        char buffer [1024];
 
622
 
 
623
        res = pipe (stdout_pipe);
 
624
        g_assert (res != -1);
 
625
                
 
626
        /*
 
627
         * glibc fork acquires some locks, so if the crash happened inside malloc/free,
 
628
         * it will deadlock. Call the syscall directly instead.
 
629
         */
 
630
        pid = syscall (SYS_fork);
 
631
        if (pid == 0) {
 
632
                close (stdout_pipe [0]);
 
633
                dup2 (stdout_pipe [1], STDOUT_FILENO);
 
634
 
 
635
                for (int i = getdtablesize () - 1; i >= 3; i--)
 
636
                        close (i);
 
637
 
 
638
                argv [0] = g_find_program_in_path ("gdb");
 
639
                if (argv [0] == NULL) {
 
640
                        close (STDOUT_FILENO);
 
641
                        exit (1);
 
642
                }
 
643
 
 
644
                argv [1] = "-ex";
 
645
                sprintf (buf1, "attach %ld", (long)getpid ());
 
646
                argv [2] = buf1;
 
647
                argv [3] = "--ex";
 
648
                argv [4] = "info threads";
 
649
                argv [5] = "--ex";
 
650
                argv [6] = "thread apply all bt";
 
651
                argv [7] = "--batch";
 
652
                argv [8] = 0;
 
653
 
 
654
                execv (argv [0], (char**)argv);
 
655
                exit (1);
 
656
        }
 
657
 
 
658
        close (stdout_pipe [1]);
 
659
 
 
660
        fprintf (stderr, "\nDebug info from gdb:\n\n");
 
661
 
 
662
        while (1) {
 
663
                int nread = read (stdout_pipe [0], buffer, 1024);
 
664
 
 
665
                if (nread <= 0)
 
666
                        break;
 
667
                write (STDERR_FILENO, buffer, nread);
 
668
        }               
 
669
 
 
670
        waitpid (pid, &status, WNOHANG);
 
671
        
 
672
#endif
 
673
 
 
674
        fprintf (stderr, "\nDebug info from libmoon:\n\n");
 
675
        void *libmoon = dlopen ("libmoon.so", RTLD_LOCAL | RTLD_LAZY);
 
676
        void_method *print_stack_trace = (libmoon == NULL) ? NULL : (void_method *) dlsym (libmoon, "print_stack_trace");
 
677
        if (print_stack_trace)
 
678
                print_stack_trace ();
 
679
        
 
680
}