How to debug a GTK app

Is installing a GTK debug binary the only way to effectively debug a GTK/X11 crash?

An otherwise normal activate function

static void activate (GtkApplication* app, gpointer user_data) {
    GtkWidget *lwindow;
    GtkWidget *rwindow;

    lwindow = gtk_application_window_new(app);
    rwindow = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (lwindow), "Left Panel");
    gtk_window_set_title (GTK_WINDOW (rwindow), "Right Panel");
    GdkDisplay * display; GListModel* monitors;
    display = gdk_display_get_default();
    monitors = gdk_display_get_monitors(display);
    GdkMonitor * monitor = (GdkMonitor *) g_list_model_get_item(monitors, 0);
    GdkMonitor * monitor2 = (GdkMonitor *) g_list_model_get_item(monitors, 1); //1 2 for 3 displays, 0 1 for 2
    GtkCssProvider * cssProvider = gtk_css_provider_new();
    gtk_css_provider_load_from_path(cssProvider, "styling.css");
    gtk_style_context_add_provider_for_display(gtk_widget_get_display(lwindow), GTK_STYLE_PROVIDER(cssProvider),
    gtk_style_context_add_provider_for_display(gtk_widget_get_display(rwindow), GTK_STYLE_PROVIDER(cssProvider),
    make_toggles(lwindow, (unsigned char) 0);
    make_toggles(rwindow, (unsigned char) 1);
    if (pthread_create(&thread_id, NULL, updateThread, NULL)) {
        log_perror("GTK could not create thread");
    gtk_window_fullscreen_on_monitor(GTK_WINDOW(rwindow), monitor);
    gtk_window_fullscreen_on_monitor(GTK_WINDOW(lwindow), monitor2);

crashes on gtk_window_present(GTK_WINDOW(lwindow)), if the following is uncommented in make_toggles for the lwindow:

if (!type) {
        gtk_widget_set_vexpand(box_row, FALSE);
        GtkWidget * vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
        utility = gtk_toggle_button_new();
        label = gtk_label_new("Soufflerie"); gtk_label_set_wrap(GTK_LABEL(label), TRUE);
        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
        gtk_button_set_child(GTK_BUTTON(utility), label);
        gtk_widget_add_css_class(utility, "ctrl_btn"); 
        gtk_widget_set_margin_start(utility, 65); gtk_widget_set_margin_top(utility, 30);
        g_signal_connect(utility, "toggled", G_CALLBACK(utility_toggle), NULL);
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(utility), TRUE);
        gtk_box_append(GTK_BOX(vbox), utility);
        GtkWidget * shutdown_ = gtk_button_new();
        label = gtk_label_new(SHUTDOWN_NAME); gtk_label_set_wrap(GTK_LABEL(label), TRUE); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
        gtk_widget_add_css_class(shutdown_, "ctrl_btn"); 
        gtk_button_set_child(GTK_BUTTON(shutdown_), label);
        gtk_widget_set_margin_start(shutdown_, 65); gtk_widget_set_margin_top(shutdown_, 40); gtk_widget_set_margin_bottom(shutdown_, 40);
        g_signal_connect(shutdown_, "clicked", G_CALLBACK(utility_toggle), NULL);
        gtk_box_append(GTK_BOX(vbox), shutdown_);
        gtk_box_append(GTK_BOX(box_row), vbox);

Essentially I am adding two buttons to the final row of buttons boxrow in lwindow. The code worked fine on a different linux system with a slightly different monitor setup (3 monitors instead of 2), but crashes on this system. There is no way to get a backtrace by typing bt full on gdb. This is GTK 4.10.
The X11 error that shows up is:

test:42726): Gdk-WARNING **: 19:29:30.795: The program 'test' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadIDChoice (invalid resource ID chosen for this connection)'.
  (Details: serial 441 error_code 14 request_code 1 (core protocol) minor_code 0)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error a while after causing it.
   To debug your program, run it with the GDK_SYNCHRONIZE environment
   variable to change this behavior. You can then get a meaningful
   backtrace from your debugger if you break on the gdk_x_error() function.)

My second question is: I set GDK_SYNCRHONIZE to 1 globally and in gdb as well, and didn’t notice a difference in my debugging experience. Is there a proper way to do this?

By a stroke of luck I found it, the error was due to this line:

g_signal_connect(utility, "toggled", G_CALLBACK(utility_toggle), NULL);

which in turn was handled as follows:

if (strstr(label_text, "Soufflerie")) {
        if (!gtk_toggle_button_get_active(source)) {
        else {
            gtk_widget_add_css_class(GTK_WIDGET(source), "ctrl_btn_pressed");
            pid_t child = fork(); 
            if (child == -1)
                log_perror("failed to create MidiRelay child process \n");
            else if (!child)
                execl("/opt/dev/RelayProcess", "/opt/dev/RelayProcess", (char*) NULL);

The program RelayProcess did not exist on this computer, so either i) I created an additional process that didn’t terminate on its own, creating multiple processes on same GTK and thus crashing or ii) execl crashed when it could not find the file and that crashed the entire program.

Out of curiosity my questions on GDK_SYNCHRONIZE and debugging remain.

It is, if you want to get a readable backtrace from a debugger. Otherwise you’ll just get blank symbols.

GDK_SYNCHRONIZE will make every X11 request a synchronous one; it’s useful when debugging because X11 requests are asynchronous in nature, so you may get an error for a request when the actual failure happened three requests before. If you’re “lucky”, you may not need it, but it’s better to be sure.

Do not ever do this, under any circumstances.

If you want to execute a process from your GTK application, either use g_spawn_async() or, preferably, GSubprocess.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.