Gtk memory leak

I have provided my sample code below. I am getting a memory leak. If I do all in main, i can mange memory-leak, but I want to do the way I have written the code and resolve the leak. Any suggestions?

I am using gtk 3.22.30.

This is my code:

#include <stdio.h>
#include <gtk/gtk.h>
#include <cairo-pdf.h>
#include <fontconfig/fontconfig.h>
#define NUM_NAMES 4

const gchar* names [] = {"Andrew", "Joe", "Samantha", "Jonathan"};
static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);
gboolean draw_text (GtkWidget* w, cairo_t* cr, gchar* data);
static gboolean draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data);
static GtkWidget*  main_rowcol = (GtkWidget*) NULL;
static GtkWidget*  drawing_area = (GtkWidget*) NULL;
static GtkWidget*  window = (GtkWidget*) NULL;

GdkDisplay *display;
GdkScreen* screen;
static cairo_scaled_font_t * scaled_font;
static cairo_font_face_t * font_face;
int num_glyphs;
int num_clusters;
static cairo_text_cluster_flags_t cluster_flags;
static cairo_glyph_t * glyph = NULL;
static cairo_text_cluster_t *clusters = NULL;
static cairo_surface_t * surface ;

gboolean draw_text (GtkWidget* w, cairo_t* cr, gchar* data) {
   if (cr ==NULL) {
     fprintf (stderr, "cairo is NULL\n");
     return FALSE;
   }
   guint width, height;
   int tick1;
   int tick2;
   int i;
   char buf[32];
   int text_width =0;
   int text_half_width =0;
   int font_off =0;

  width = gtk_widget_get_allocated_width (w);
  height = gtk_widget_get_allocated_height (w);
  tick1 = width/2;
  tick2 = height/2;
  cairo_save (cr);
  font_face = cairo_toy_font_face_create("courier", CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_NORMAL);
  surface = cairo_pdf_surface_create("test.pdf", 300, 300);
  cairo_set_font_face(cr, font_face);
  cairo_set_font_size(cr, 30);
  scaled_font = cairo_get_scaled_font(cr);

  cairo_text_extents_t extents_text;
  cairo_font_extents_t extents_font;
  GdkRGBA white;
  gdk_rgba_parse (&white, "black");
  gdk_cairo_set_source_rgba (cr, &white);
  cairo_set_font_size(cr, 18);
 cairo_font_extents  (cr, &extents_font);
  cairo_text_extents (cr,data, &extents_text);
  text_width = (int)extents_text.width;
  text_half_width = (int)(text_width/2 +0.5);
  font_off = (int)extents_font.descent;
  cairo_move_to (cr,tick1-text_half_width, tick2+font_off);
  cairo_show_text (cr, data);
  return TRUE;
}
static gboolean draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data) {
   guint width, height;
   width = gtk_widget_get_allocated_width (widget);
   height = gtk_widget_get_allocated_height (widget);
   GtkStyleContext *context;
   context = gtk_widget_get_style_context (widget);
   gtk_render_background (context, cr, 0, 0, width, height);
   gchar* text = "Heading";
   draw_text (widget, cr, text);
   return TRUE;
}
int main(int argc, char*argv []){
   gtk_init (&argc, &argv);
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_container_set_border_width (GTK_CONTAINER(window),10);
   //gtk_widget_set_name (window, "gtkWin");
   gtk_widget_set_size_request (window, 600,600);
   main_rowcol = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (main_rowcol), 0);
  gtk_grid_set_column_spacing (GTK_GRID (main_rowcol),0);
  gtk_grid_set_row_homogeneous (GTK_GRID (main_rowcol), FALSE);
  gtk_grid_set_column_homogeneous(GTK_GRID(main_rowcol),FALSE);
  gtk_container_add (GTK_CONTAINER (window), main_rowcol);
  drawing_area = gtk_drawing_area_new ();
  gtk_widget_set_size_request (drawing_area, 300, 300);
  g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
  g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (delete_event), NULL);
  unsigned int refcount = cairo_font_face_get_reference_count (font_face);
  g_signal_connect (G_OBJECT (drawing_area), "draw", G_CALLBACK (draw_callback),NULL);
  refcount = cairo_font_face_get_reference_count (font_face);
  gtk_grid_attach (GTK_GRID(main_rowcol),drawing_area,0,0,10,10);
  gtk_widget_show_all (window);
  cairo_glyph_free(glyph);
  cairo_text_cluster_free (clusters);
  cairo_surface_destroy(surface);
  cairo_debug_reset_static_data();
  FcFini();
  gtk_main();
  return 0;
}

static void destroy (GtkWidget *window, gpointer data ) {
   gtk_main_quit ();
}

static gboolean delete_event (GtkWidget* window, GdkEvent *event, gpointer data ) {
   return FALSE;
}

memory_leak:

IT has been maintained in several sources. But I think, the leak has something to do with the coding part.

360 (256 direct, 104 indirect) bytes in 1 blocks are definitely lost in loss record 5,072 of 5,418
==14200==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==14200==    by 0x7131109: ??? (in /usr/lib64/libfontconfig.so.1.11.1)
==14200==    by 0x7131844: ??? (in /usr/lib64/libfontconfig.so.1.11.1)
==14200==    by 0x7132404: FcPatternDuplicate (in /usr/lib64/libfontconfig.so.1.11.1)
==14200==    by 0x694360E: ??? (in /usr/lib64/libcairo.so.2.11512.0)
==14200==    by 0x694395A: ??? (in /usr/lib64/libcairo.so.2.11512.0)
==14200==    by 0x68F5D6F: cairo_toy_font_face_create (in /usr/lib64/libcairo.so.2.11512.0)
==14200==    by 0x401ABA: draw_text(_GtkWidget*, _cairo*, char*) (test74.cpp:48)
==14200==    by 0x401D4D: draw_callback(_GtkWidget*, _cairo*, void*) (test74.cpp:79)
==14200==    by 0x5068240: ??? (in /usr/lib64/libgtk-3.so.0.2200.30)
==14200==    by 0x51B0C91: ??? (in /usr/lib64/libgtk-3.so.0.2200.30)
==14200==    by 0x6BBAC46: ??? (in /usr/lib64/libgobject-2.0.so.0.5600.1)

You have to ignore leaks from fontconfig until somebody fixes Code obfuscation results in memory leaks reported by Valgrind, AddressSanitizer (#77) · Issues · fontconfig / fontconfig

if i do the font-config in main, i do not see any leak. However, i want use draw_callback and i get the leak.
I do agree with you, but i still think some issues in my code may be.
Thanks

That code runs every time the ::draw signal is emitted, but never freed (with cairo_font_face_destroy() and cairo_surface_destroy() respectively)

1 Like

I tried that. That does not help unless I use cairo_debug_reset_static_data(); and it can not be used in gtk in a routine. It has to be used at the end of the main only.

One off allocations are still not leaks, and we don’t have generic API anywhere in the stack to reset those one off allocations because doing so is more inefficient than letting the OS reclaim the memory at the end of the process.

You will need to use suppression files. That’s just how it is.

FWIW I think the suggestion to use suppression files is unrealistic. We know how to ensure valgrind does not complain about one-time allocations – ensure there is a pointer to the start of the allocation stored in a static variable – and it’s easier to fix our software accordingly than to tell developers to use a different likely-unmaintained suppressions file for every library they use.

It’s what we have. Unless Valgrind suppression files start support an “include” directive, that allows chaining up rules from dependencies, there’s not much else to say.

That is a simplistic-bordering-on-naive interpretation of what “one time allocations” are or do. Not all one-off allocations are overallocations-and-return-a-pointer-in-the-middle memory areas; and even when they are, the only way to actually have Valgrind manage them is to:

  • depend on Valgrind or copy-parse valgrind.h into your project
  • have a separate code path that deals with the “running under valgrind” case, in which you can jiggle your overallocations to add an additional pointer to the beginning of the memory area

Not everything can be fixed in a backward compatible, or in an ABI-compatible way.

Suppression files should be maintained just like any other aspect of a library. If they aren’t, then you’re welcome to help out.

I have put in my sample code. If anyone can show how to fix it up, i will gladly do. I am not able to fix it in the routine. I should know how to empty the hash table. I don’t know it is proper to empty the hash table in a routine.
However, i am not successful yet.
I agree with the above, suppression file is not a good idea.
Thanks,
lalitha V.

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