5

How can I debug (and reach some breakpoint) with gdb my buggy program (using GTK3) showing:

(monimelt:161): Gtk-WARNING **: Invalid text buffer iterator: either the iterator
is uninitialized, or the characters/pixbufs/widgets in the buffer have been
modified since the iterator was created.
You must use marks, character numbers, or line numbers to preserve a position 
across buffer modifications.
You can apply tags and insert marks without invalidating your iterators,
but any mutation that affects 'indexable' buffer contents (contents that can 
be referred to by character offset)
will invalidate all outstanding iterators

FWIW, the faulty program is some GPLv3 free software that I am developing (the same I asked this question for, where I give more context and explanation). It is on github: commit fc8e0b247d8dac4 (to reproduce my bug, build it, run it as ./monimelt, type exactly p a y TAB in the window labelled monimelt command).

I have tried (all, in the same gdb session):

  • calling quite early in my main the g_log_set_default_handler function (as suggested here) with my own mom_g_log_handler log handler and put a gdb breakpoint there

  • putting a gdb breakpoint on g_warning (does not work, since that is probably a macro)

  • putting a gdb breakpoint on g_log_default_handler

  • also calling (in commit fb374425c69f2ddbd...)

    g_log_set_handler ("Gtk", G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL
                       | G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING
                       | G_LOG_FLAG_RECURSION, mom_g_log_handler, NULL);
    

    don't help...

But those breakpoints are not reached and the Gtk-WARNING **: Invalid text buffer iterator message is still appearing.

Perhaps GTK is creating some pthreads, or perhaps it is registering its own log handler...

how I found my bug....

I finally found my bug (corrected in commit 82bb111402fdd97...). It was two calls to gtk_text_iter_get_offset happening (incorrectly) after gtk_text_buffer_insert.

But I am not satisfied with the way I found my bug. I had to fetch gtk+-3.21.6.tar.xz and I patched its gtk/gtktextiter.c to code near line 180 the following:

extern void basile_gtk_text_iter_warning (void);
void basile_gtk_text_iter_warning (void)
{
  usleep (1000);
}

/* This function ensures that the segment-dependent information is
   truly computed lazily; often we don't need to do the full make_real
   work. This ensures the btree and line are valid, but doesn't
   update the segments. */
static GtkTextRealIter*
gtk_text_iter_make_surreal (const GtkTextIter *_iter)
{
  GtkTextRealIter *iter = (GtkTextRealIter*)_iter;

  if (iter->chars_changed_stamp !=
      _gtk_text_btree_get_chars_changed_stamp (iter->tree))
    {
      g_warning ("Invalid text buffer iterator: either the iterator "
                 "is uninitialized, or the characters/pixbufs/widgets "
                 "in the buffer have been modified since the iterator "
                 "was created.\nYou must use marks, character numbers, "
                 "or line numbers to preserve a position across buffer "
                 "modifications.\nYou can apply tags and insert marks "
                 "without invalidating your iterators,\n"
                 "but any mutation that affects 'indexable' buffer contents "
                 "(contents that can be referred to by character offset)\n"
                 "will invalidate all outstanding iterators");
      basile_gtk_text_iter_warning ();
      return NULL;
    }

The only change I made (w.r.t. pristine GTK3.21.26) is the definition and call to basile_gtk_text_iter_warning and I put a breakpoint in it with gdb.

Being able to do such dirty tricks is one of the reasons I am only using free software as much as possible, but having to make such tricks is really a shame, and there must be a better way.

So my question still stands, how could I have put such a breakpoint without recompiling GTK3? Or more generally how could I use GDB to catch such bugs (of course without recompiling GTK3).

BTW, using the simple --g-fatal-warnings option is not really a solution, since I am also getting

(monimelt:21949): Gtk-WARNING **: Failed to get the GNOME session proxy: 
The name org.gnome.SessionManager is not owned

which I consider being a spurious warning, because my running desktop is Mate, not Gnome. And my code is calling gtk_application_new & g_application_run and having a activate signal on my mom_gtkapp.


PS. On Linux/Debian/Sid/x86-64, GTK3 is 3.21.5, compiled with GCC6 and -g3 -Og flags...

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • Macros suck for just this reason. I've had to do the same thing you ended up doing, add some debug lines before the macro call. In theory, the macro must call some function to print out the error so the breakpoint could have been there. Oftentimes, that call is a generic fprintf however so the bp is not useful as it constantly gets hit. – Matthew Fisher Sep 15 '16 at 14:28
  • No, I disagree. Macros are very useful (and are not less hard to debug than inlined functions). What is missing is a documentation saying "to catch GTK warnings in a debugger, do this and that" (e.g. "put a breakpoint on such function"). – Basile Starynkevitch Sep 15 '16 at 14:38
  • Restating slightly, Macros suck to debug for just this reason – Matthew Fisher Sep 15 '16 at 14:40
  • But then what about inlined functions? They are as difficult to debug as macros (and you cannot "uninline" a function inside a shared library) – Basile Starynkevitch Sep 15 '16 at 16:07
  • Yes, but I have not seen that situation as much. I've found that complex functions are usually the ones with bugs. Functions that get inlined are generally short and bug free. OTOH, I have seen macros consistently misused to encapsulate complex un-debuggable functionality. – Matthew Fisher Sep 15 '16 at 16:19
  • I'd start with `b write if $_regex((char *)$rsi, ".*Invalid text buffer iterator.*")` and then do a backtrace when it hits that breakpoint. Reversible debugging may also be useful. – Mark Plotnick Sep 15 '16 at 16:55

2 Answers2

5

You can break on a g_warning (and any other log level message) by putting a breakpoint on g_logv.

I found this info here, fyi: http://wiki.inkscape.org/wiki/index.php/Debugging_Inkscape

Stepan
  • 163
  • 1
  • 6
  • Add / set environment variable `G_DEBUG` to `fatal-warnings`. Then run your application in debug mode as usual. When time comes the debugger will stop at the code that generates the warning with complete back-trace. – Mohith Nov 25 '20 at 05:45
2

Use G_DEBUG=fatal-warnings gdb ./your_app to make gdb stop on warnings, too.

Reference: https://developer.gnome.org/glib/unstable/glib-Message-Logging.html#g-warning

DarkTrick
  • 2,447
  • 1
  • 21
  • 39