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:
10
* The above copyright notice and this permission notice shall be
11
* included in all copies or substantial portions of the Software.
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.
21
* Copyright (c) 2007-2008 Novell, Inc.
24
* Jackson Harper (jackson@ximian.com)
34
#include <gdk/gdkkeysyms.h>
35
#include <dbus/dbus-glib.h>
36
#include <dbus/dbus-glib-bindings.h>
38
#include <sys/syscall.h>
43
#include <gtkmozembed.h>
45
#include "gtkembedmoz/gtkmozembed.h"
50
#include "AgserverObject.h"
52
#include "nsXPCOMGlue.h"
56
#define DRT_SERVICE "mono.moonlight.tests"
58
#define DRT_LOGGER_PATH "/mono/moonlight/tests/logger"
59
#define DRT_LOGGER_INTERFACE "mono.moonlight.tests.logger.ITestLogger"
61
#define DRT_RUNNER_PATH "/mono/moonlight/tests/runner"
62
#define DRT_RUNNER_INTERFACE "mono.moonlight.tests.runner.ITestRunner"
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"
70
#define DEFAULT_TIMEOUT 20000
73
typedef struct _AgViewer {
74
GtkWindow *top_level_window;
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;
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 ();
100
hang_detector (gpointer data)
106
if (hung_time > 0 && hung_time < now) {
107
fprintf (stderr, "Agviewer seems to be hung, aborting process.\n");
108
print_stack_traces ();
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);
118
agserver_class_init (AgserverClass* agserver_class)
120
dbus_g_object_type_install_info (AGSERVER_TYPE, &dbus_glib_agserver_object_info);
124
agserver_init (Agserver* agserver)
129
// Called from libshocker when a test is finished
132
signal_shutdown (Agserver* server, GError** error)
134
mark_test_as_complete_and_start_next_test (true);
140
register_agserver_object ()
142
DBusGConnection *connection;
144
GError *error = NULL;
146
guint request_name_ret;
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);
156
proxy = dbus_g_proxy_new_for_name (connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
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);
163
if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
164
printf ("Unable to become the primary owner of IAgserver interface.\n");
168
obj = G_OBJECT (g_object_new (AGSERVER_TYPE, NULL));
169
dbus_g_connection_register_g_object (connection, DRT_AGSERVER_PATH, obj);
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);
179
static gboolean test_timeout (gpointer data);
183
set_current_dir (const char* test_path)
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);
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);
195
dir = g_path_get_dirname (test_path);
200
dir = g_path_get_dirname (test_path);
203
if (chdir (dir) != 0)
204
g_warning ("Unable to set working directory to: %s\n", dir);
211
main(int argc, char **argv)
213
int frame_width = 800;
214
int frame_height = 800;
216
agviewer_add_signal_handler ();
218
gtk_init (&argc, &argv);
220
if (!g_thread_supported ())
221
g_thread_init (NULL);
222
g_thread_create (hang_detector, NULL, FALSE, NULL);
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);
231
if (!g_strcasecmp ("-frameheight", argv [i]) && i < argc) {
232
frame_height = strtol (argv [++i], NULL, 10);
236
if (!g_strcasecmp ("-working-dir", argv [i]) && i < argc) {
237
working_dir = argv [++i];
241
if (!g_strcasecmp ("-server", argv [i]))
246
test_path = g_strdup (argv [argc - 1]);
248
browser = new_gtk_browser ();
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));
256
register_agserver_object ();
258
if (test_path && test_path[0]) {
259
run_test (test_path, DEFAULT_TIMEOUT);
262
if (move_to_next_test ())
272
run_test (char* test_path, int timeout)
275
g_source_remove (timeout_id);
276
timeout_id = g_timeout_add (timeout, test_timeout, test_path);
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;
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);
292
if (wait_for_next_test (&timeout)) {
293
run_test (test_path, timeout);
296
if (gtk_main_level ())
303
mark_test_as_complete_and_start_next_test (gboolean successful)
305
DBusGProxy* dbus_proxy;
306
DBusGConnection* connection;
307
GError* error = NULL;
310
connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
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);
317
dbus_proxy = dbus_g_proxy_new_for_name (connection,
320
DRT_RUNNER_INTERFACE);
324
char* test_name = g_path_get_basename (test_path);
329
if (!dbus_g_proxy_call (dbus_proxy, "MarkTestAsCompleteAndGetNextTest", &error,
330
G_TYPE_STRING, test_name,
331
G_TYPE_BOOLEAN, successful,
333
G_TYPE_BOOLEAN, &available,
334
G_TYPE_STRING, &test_path,
335
G_TYPE_INT, &timeout,
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");
345
run_test (test_path, timeout);
354
AgViewer *browser = 0;
356
browser = g_new0 (AgViewer, 1);
357
browser->top_level_window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
359
static const GREVersionRange gre_version = {
364
char xpcom_lib_path [PATH_MAX];
365
char* xpcom_dir_path;
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);
371
gtk_moz_embed_set_path (xpcom_dir_path);
373
gtk_moz_embed_set_comp_path (xpcom_dir_path);
375
g_free (xpcom_dir_path);
377
browser->moz_embed = gtk_moz_embed_new();
378
gtk_container_add (GTK_CONTAINER (browser->top_level_window),
381
g_signal_connect (G_OBJECT (browser->top_level_window), "key_press_event", G_CALLBACK (key_press_cb), browser->top_level_window);
387
key_press_cb (GtkWidget* widget, GdkEventKey* event, GtkWindow* window)
389
if ((event->state & GDK_CONTROL_MASK) == 0)
392
switch (event->keyval) {
395
if (close_key_pressed_count == 0)
396
request_test_runner_shutdown ();
397
else if (close_key_pressed_count > 0)
399
close_key_pressed_count++;
403
mark_test_as_complete_and_start_next_test (false);
411
test_timeout (gpointer data)
413
const char* test = (const char *) data;
414
log_message (test, "test timed out.");
416
mark_test_as_complete_and_start_next_test (false);
421
request_test_runner_shutdown ()
425
DBusGProxy* dbus_proxy;
426
DBusGConnection* connection;
427
GError* error = NULL;
430
connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
432
g_warning ("Failed to open connection to bus: %s\n", error->message);
433
g_error_free (error);
436
dbus_proxy = dbus_g_proxy_new_for_name (connection,
439
DRT_RUNNER_INTERFACE);
441
dbus_g_proxy_call_no_reply (dbus_proxy, "RequestShutdown", G_TYPE_INVALID);
445
signal_test_complete (const char *test_path, bool successful)
449
DBusGProxy* dbus_proxy;
450
DBusGConnection* connection;
451
GError* error = NULL;
454
connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
456
g_warning ("Failed to open connection to bus while signalling shutdown: %s\n", error->message);
457
g_error_free (error);
460
dbus_proxy = dbus_g_proxy_new_for_name (connection,
463
DRT_RUNNER_INTERFACE);
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);
473
wait_for_next_test (int *timeout)
477
DBusGProxy* dbus_proxy;
478
DBusGConnection* connection;
479
GError* error = NULL;
482
connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
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);
489
dbus_proxy = dbus_g_proxy_new_for_name (connection,
492
DRT_RUNNER_INTERFACE);
500
if (!dbus_g_proxy_call (dbus_proxy, "GetNextTest", &error,
502
G_TYPE_BOOLEAN, &available,
503
G_TYPE_STRING, &test_path,
506
printf ("error while making GetNextTest dbus call: %s\n", error->message);
514
log_message (const char* test_name, const char* message)
516
log_output (test_name, message, "Error");
520
log_output (const char* test_name, const char* message, const char* level)
522
DBusGProxy* dbus_proxy;
523
DBusGConnection* connection;
524
GError* error = NULL;
527
connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
529
g_warning ("Failed to open connection to bus to log message %s\n", error->message);
530
g_error_free (error);
533
dbus_proxy = dbus_g_proxy_new_for_name (connection,
536
DRT_LOGGER_INTERFACE);
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);
542
agviewer_add_signal_handler ()
546
sa.sa_handler = agviewer_handle_native_sigsegv;
547
sigemptyset (&sa.sa_mask);
550
g_assert (sigaction (SIGSEGV, &sa, NULL) != -1);
551
g_assert (sigaction (SIGQUIT, &sa, NULL) != -1);
555
* agviewer_handle_native_sigsegv:
556
* (this is a slightly modified version of mono_handle_native_sigsegv from mono/mono/mini/mini-exceptions.c)
560
static bool handling_sigsegv = false;
562
typedef void void_method ();
565
agviewer_handle_native_sigsegv (int signal)
567
const char *signal_str = (signal == SIGSEGV) ? "SIGSEGV" : "SIGABRT";
569
case SIGSEGV: signal_str = "SIGSEGV"; break;
570
case SIGABRT: signal_str = "SIGABRT"; break;
571
case SIGQUIT: signal_str = "SIGQUIT"; break;
573
signal_str = "UNKNOWN"; break;
576
if (handling_sigsegv) {
577
fprintf (stderr, "\nGot a SIGSEGV while executing the SIGSEGV handler, aborting.\n");
582
/* To prevent infinite loops when the stack walk causes a crash */
583
handling_sigsegv = true;
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.
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"
600
print_stack_traces ();
602
if (signal != SIGQUIT) {
605
handling_sigsegv = false;
610
print_stack_traces ()
612
/* Try to get more meaningful information using gdb */
613
#if !defined(PLATFORM_WIN32)
614
/* From g_spawn_command_line_sync () in eglib */
616
int stdout_pipe [2] = { -1, -1 };
618
const char *argv [16];
623
res = pipe (stdout_pipe);
624
g_assert (res != -1);
627
* glibc fork acquires some locks, so if the crash happened inside malloc/free,
628
* it will deadlock. Call the syscall directly instead.
630
pid = syscall (SYS_fork);
632
close (stdout_pipe [0]);
633
dup2 (stdout_pipe [1], STDOUT_FILENO);
635
for (int i = getdtablesize () - 1; i >= 3; i--)
638
argv [0] = g_find_program_in_path ("gdb");
639
if (argv [0] == NULL) {
640
close (STDOUT_FILENO);
645
sprintf (buf1, "attach %ld", (long)getpid ());
648
argv [4] = "info threads";
650
argv [6] = "thread apply all bt";
651
argv [7] = "--batch";
654
execv (argv [0], (char**)argv);
658
close (stdout_pipe [1]);
660
fprintf (stderr, "\nDebug info from gdb:\n\n");
663
int nread = read (stdout_pipe [0], buffer, 1024);
667
write (STDERR_FILENO, buffer, nread);
670
waitpid (pid, &status, WNOHANG);
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 ();