Getting 'g_param_value_validate: assertion 'G_IS_PARAM_SPEC (pspec)' failed' when appending text to GtkTextView

I’m trying to append text to a GtkTextView whenever I press a button (just for testing atm), but in one of the callbacks, I keep getting the following message:

(caliprint:19537): GLib-GObject-CRITICAL **: 22:04:19.253: g_param_value_validate: assertion 'G_IS_PARAM_SPEC (pspec)' failed

And here’s the function that generates the message:

//Connect Button
gboolean on_control_connect_pressed_callback( GtkWidget *object, gpointer user_data )
{
    gui_context * context = (gui_context *)user_data;

    //Disconnect
    if( context->serial != NULL && serial_is_connected( context->serial ) )
    {
        log_printf( 
            context->control_log, 
            "Disconnecting from %s...\n", 
            serial_get_port( context->serial )
        );

        serial_free_driver( context->serial );
        context->serial = NULL;

        gtk_button_set_label(
            context->control_connect,
            "connect"
        );

        return true;
    }

    //Connect
    if( context->serial != NULL )
    {
        serial_free_driver( context->serial );   
        context->serial = NULL;
    }
    
    //context->serial = serial_create_driver( "/dev/ttyUSB0", SP115200, false );
    if( serial_get_status( context->serial ) != SERIAL_OK )
    {
        log_printf( 
            context->control_log, 
            "Cannot connect to serial port %s\n", 
            serial_get_port( context->serial )
        );
        gtk_button_set_label(
            context->control_connect,
            "Connect"
        );

        return true;
    }
    else
    {
        log_printf( 
            context->control_log, 
            "Connected to %s\n",
            serial_get_port( context->serial )
        );

        gtk_button_set_label(
            context->control_connect,
            "Disconnect"
        );
    }


    return true;
}

And of course, here’s the funtion(s) in charge of appending the text:

static void log_append_to_textview( GtkTextView * tv, char * msg );

void log_printf( GtkTextView * tv, const char * format, ... )
{
    va_list arg;
    va_start( arg, format );

    //1000 for buffer size (+1 for the \0) and +12 for the time
    static char buffer[1013] = {0};
    buffer[0] = 0;
    char * tgt = get_time( buffer );
    vsnprintf( tgt, 1000, format, arg );

    log_append_to_textview( tv, buffer );
}

void log_append_to_textview( GtkTextView * tv, char * msg )
{
    GtkTextBuffer * buff =
        gtk_text_view_get_buffer(tv);

    GtkTextMark *mark = 
        gtk_text_buffer_get_insert( buff );

    GtkTextIter end;
    gtk_text_buffer_get_end_iter(
        buff,
        &end
    );

    gtk_text_buffer_move_mark( buff, mark, &end );
    gtk_text_buffer_insert_at_cursor( 
        buff, 
        msg, 
        -1 
    );
    gtk_text_view_scroll_to_mark( 
        tv,
        mark, 
        0.0,
        TRUE, 
        0.5, 
        1 
    );
}

The funny thing is, all of the other callbacks seem to work just fine. I do believe the warning is caused by this segment:

if( serial_get_status( context->serial ) != SERIAL_OK )
    {
        log_printf( 
            context->control_log, 
            "Cannot connect to serial port %s\n", 
            serial_get_port( context->serial )
        );
        gtk_button_set_label(
            context->control_connect,
            "Connect"
        );

As it doesn’t occur when the other conditions are met. I check and it doesn’t seem to be a problem with the pointers for the widgets. I imagine the warning is not critical as it does not seem to make the application crash, but of course I don’t want to just ignore it for now and worry about it later. I appreciate any feedback you guys might provide, and sorry if I’m not providing enough info, I’m still a little new to Gtk.

I also didn’t wanted to post the whole code behind serial since I feel like this was getting a little bit too long already, but you can also check the code on Github

Please, run your application under GDB, and set the G_DEBUG environment variable to fatal-criticals before doing so, e.g.:

export G_DEBUG=fatal-warnings
gdb --args your-application

When the application will emit a critical warning, the flow will pause and you can gather a backtrace at that point to see what is causing the precondition type check to fail.

A quick tutorial on how to use GDB is available here.

No, the warning is critical, and it typically means a precondition being checked has failed—e.g. you passed an argument of a certain type to a function that expects another type; or you’re passing a pointer to some uninitialized or invalid data to a function that expects valid data. Anything that follows a critical warning is considered undefined behaviour and anything can happen—not necessarily crashing, but that too.

1 Like

Thank you for the tip!, and after I used gdb looks like the problem is caused by the

    gtk_text_buffer_insert_at_cursor( 
        buff, 
        msg, 
        -1 
    );

function. I checked the values of all of the pointers in two separate instances, one where the function didn’t execute with errors, and one where it did, but both results turned out the same.

# This was good
(gdb) print buff
$1 = 0x70cc90
(gdb) print msg
$2 = 0x406180 <buffer> "[10:20:42] Cannot connect to serial port (null)\n"

# This was bad
(gdb) print buff
$3 = 0x70cc90
(gdb) print msg
$4 = 0x406180 <buffer> "[10:21:03] Cannot connect to serial port (null)\n"

And here is the “good” callback by the way:

gboolean on_control_preferences_pressed_callback( GtkWidget *object, gpointer user_data )
{
    gui_context * context = (gui_context *)user_data;
    log_printf(context->control_log, "Cannot connect to serial port (null)\n");
    return true;
}

I don’t see where I’m modifying the GtkTextView pointer used by log_printf, so I don’t really see why the function call wouldn’t work the second time though. Any thoughts?

I’ll keep that in mind then. I’m still rather new when it comes to Gtk, but I have encountered similar warnings in the past, but given that the application didn’t crashed, I just ignored them until the feature I was working on at the time was implemented. Thanks for letting me now

That callback is connected to what signal? It looks like an event-based one, given the boolean return value, in which case the second argument is not the user data, but a GdkEvent pointer.

If you want to know if a GtkButton has been pressed, you should use the clicked signal, not the GtkWidget::button-press-event signal.

Oh, that’s the signal that I’m using for it yeah, sorry for not clarifying:


    g_signal_connect(
        context->control_preferences,
        "clicked",
        G_CALLBACK(on_control_preferences_pressed_callback),
        context
    );

same goes for the other callback as well

Then I’m afraid you’ll have to dig deeper, and print out what kind of objects you’re getting.

You should also post a full backtrace from the warning you’re getting.

Will do, I’ll upload an update once I get more info on the issue. In the meantime, by backtrace do you mean this?:

(gdb) backtrace
#0  0x00007ffff719a765 in _g_log_abort () at /lib64/libglib-2.0.so.0
#1  0x00007ffff719ba36 in g_logv () at /lib64/libglib-2.0.so.0
#2  0x00007ffff719bc03 in g_log () at /lib64/libglib-2.0.so.0
#3  0x00007ffff72857ca in g_object_notify_by_pspec () at /lib64/libgobject-2.0.so.0
#4  0x00007ffff727e742 in g_closure_invoke () at /lib64/libgobject-2.0.so.0
#5  0x00007ffff7291d84 in signal_emit_unlocked_R () at /lib64/libgobject-2.0.so.0
#6  0x00007ffff729b3ae in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#7  0x00007ffff729b9d3 in g_signal_emit () at /lib64/libgobject-2.0.so.0
#8  0x00007ffff7b900b7 in gtk_text_buffer_real_insert_text () at /lib64/libgtk-3.so.0
#9  0x00007ffff727e742 in g_closure_invoke () at /lib64/libgobject-2.0.so.0
#10 0x00007ffff7291d84 in signal_emit_unlocked_R () at /lib64/libgobject-2.0.so.0
#11 0x00007ffff729b3ae in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#12 0x00007ffff729b9d3 in g_signal_emit () at /lib64/libgobject-2.0.so.0
#13 0x00007ffff7b914fe in gtk_text_buffer_insert_at_cursor () at /lib64/libgtk-3.so.0
--Type <RET> for more, q to quit, c to continue without paging--
#14 0x00000000004028e8 in log_append_to_textview (msg=0x406180 <buffer> "[10:44:42] Cannot connect to serial port (null)\n", tv=0x572580)
    at /home/lastc/Documents/caliprint/src/log.c:35
#15 log_printf (tv=0x572580, format=format@entry=0x404060 "Cannot connect to serial port %s\n") at /home/lastc/Documents/caliprint/src/log.c:17
#16 0x00000000004024a8 in on_control_connect_pressed_callback (object=<optimized out>, user_data=0x410610)
    at /home/lastc/Documents/caliprint/src/callbacks.c:53
#17 0x00007ffff727e996 in _g_closure_invoke_va () at /lib64/libgobject-2.0.so.0
#18 0x00007ffff729b228 in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#19 0x00007ffff729b9d3 in g_signal_emit () at /lib64/libgobject-2.0.so.0
#20 0x00007ffff79c353e in gtk_button_do_release () at /lib64/libgtk-3.so.0
#21 0x00007ffff79c35a8 in gtk_real_button_released () at /lib64/libgtk-3.so.0
#22 0x00007ffff727e742 in g_closure_invoke () at /lib64/libgobject-2.0.so.0
#23 0x00007ffff7292749 in signal_emit_unlocked_R () at /lib64/libgobject-2.0.so.0
#24 0x00007ffff729b3ae in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#25 0x00007ffff729b9d3 in g_signal_emit () at /lib64/libgobject-2.0.so.0
--Type <RET> for more, q to quit, c to continue without paging--
#26 0x00007ffff79c19d4 in multipress_released_cb () at /lib64/libgtk-3.so.0
#27 0x00007ffff7c72291 in _gtk_marshal_VOID__INT_DOUBLE_DOUBLEv () at /lib64/libgtk-3.so.0
#28 0x00007ffff727e996 in _g_closure_invoke_va () at /lib64/libgobject-2.0.so.0
#29 0x00007ffff729b228 in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#30 0x00007ffff729b9d3 in g_signal_emit () at /lib64/libgobject-2.0.so.0
#31 0x00007ffff7a8a24c in gtk_gesture_multi_press_end () at /lib64/libgtk-3.so.0
#32 0x00007ffff7281b96 in g_cclosure_marshal_VOID__BOXEDv () at /lib64/libgobject-2.0.so.0
#33 0x00007ffff727e996 in _g_closure_invoke_va () at /lib64/libgobject-2.0.so.0
#34 0x00007ffff729b228 in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#35 0x00007ffff729b9d3 in g_signal_emit () at /lib64/libgobject-2.0.so.0
#36 0x00007ffff7a872b2 in _gtk_gesture_check_recognized () at /lib64/libgtk-3.so.0
#37 0x00007ffff7a888e3 in gtk_gesture_handle_event () at /lib64/libgtk-3.so.0
#38 0x00007ffff7a8b8b6 in gtk_gesture_single_handle_event () at /lib64/libgtk-3.so.0
#39 0x00007ffff7a52ec0 in gtk_event_controller_handle_event () at /lib64/libgtk-3.so.0
--Type <RET> for more, q to quit, c to continue without paging--
#40 0x00007ffff7c14d8d in _gtk_widget_run_controllers () at /lib64/libgtk-3.so.0
#41 0x00007ffff7c6bd9f in _gtk_marshal_BOOLEAN__BOXEDv () at /lib64/libgtk-3.so.0
#42 0x00007ffff727e996 in _g_closure_invoke_va () at /lib64/libgobject-2.0.so.0
#43 0x00007ffff729ae6a in g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#44 0x00007ffff729b9d3 in g_signal_emit () at /lib64/libgobject-2.0.so.0
#45 0x00007ffff7c16823 in gtk_widget_event_internal () at /lib64/libgtk-3.so.0
#46 0x00007ffff7ad2168 in propagate_event () at /lib64/libgtk-3.so.0
#47 0x00007ffff7ad437b in gtk_main_do_event () at /lib64/libgtk-3.so.0
#48 0x00007ffff77bcf79 in _gdk_event_emit () at /lib64/libgdk-3.so.0
#49 0x00007ffff77f0086 in gdk_event_source_dispatch () at /lib64/libgdk-3.so.0
#50 0x00007ffff7194520 in g_main_context_dispatch () at /lib64/libglib-2.0.so.0
#51 0x00007ffff71948b0 in g_main_context_iterate.isra () at /lib64/libglib-2.0.so.0
#52 0x00007ffff7194ba3 in g_main_loop_run () at /lib64/libglib-2.0.so.0
#53 0x00007ffff7ad33bd in gtk_main () at /lib64/libgtk-3.so.0
--Type <RET> for more, q to quit, c to continue without paging--
#54 0x0000000000402618 in gui_init (context=context@entry=0x410610, argc=argc@entry=0x7fffffffe5bc, argv=argv@entry=0x7fffffffe5b0)
    at /home/lastc/Documents/caliprint/src/gui.c:34
#55 0x00000000004022c6 in main (argc=<optimized out>, argv=<optimized out>) at /home/lastc/Documents/caliprint/src/main.c:16

Edit: Added the rest of the output

Your code does:

    //1000 for buffer size (+1 for the \0) and +12 for the time
    static char buffer[1013] = {0};
    buffer[0] = 0;
    char * tgt = get_time( buffer );
    vsnprintf( tgt, 1000, format, arg );

which means it reuses the same buffer array across calls. You never check what the function returns, and how many bytes it wrote. In the get_time() function you add constants, instead of checking the return of snprintf() as well.

Since you’re passing -1 as the length of the buffer later on when calling gtk_text_buffer_insert_at_cursor(), GTK will try to measure the length of the buffer using strlen(), and you could end up overflowing that buffer.

My suggestion would be to use g_strdup_printf() and g_date_time_format() instead of your own homegrown variants.

You may also want to look at the contents of the buffer from within GDB, and use Valgrind to see if you’re overflowing memory boundaries.

I added some of the improvements that you mentioned (as well as a size of gtk_text_buffer_insert_at_cursor() instead of just giving it a -1), but the issue persisted. However, after a bit more testing, I noticed that the warning was caused by the fact that context (the object with all of the application pointers) was created using malloc not calloc as I thought it was, so all of its members remained uninitialized. That wasn’t a problem until

if( context->serial != NULL ) 
{
    serial_free_driver( context->serial );
}

where context->serial was not NULL, but it was not initialized, which caused it to call free(context->serial) and yeah, weird things happened after that. I now fixed the issue. Thanks a lot for your help regardless, I really learned a lot about gdb and specially the g_date_time_ functions. Also, yeah I did had some leaks, which I’ll be fixing later today, so thanks for reminding me to check. And lastly, sorry this issue was not caused by Gtk nor Glib, I’ll make sure to spend more time investigating problems before asking any questions.

Edit: Fixed typos

Glad your debugging paid off!

If you want to make sure that a pointer is NULL-ified after free, use g_clear_pointer(), e.g.

g_clear_pointer (&context->serial, serial_free_driver));

You can use it with every function that behaves like free().

1 Like

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