1

I am using Pango library alongside Cairo, without GTK, in a test-drive application which I'm currently compiling on MacOSX. I have a memory leakage problem, that I have traced to this function:

void draw_with_cairo (void)
{
    PangoLayout *layout;
    PangoFontDescription *desc;
    int i;

    cairo_save (cr);
    cairo_scale (cr, 1, -1);
    cairo_translate (cr, 0, -HEIGHT);

    cairo_translate (cr, 400, 300);

    layout = pango_cairo_create_layout (cr);

    pango_layout_set_text (layout, "Test", -1);
    desc = pango_font_description_from_string ("‌BMitra 32");
    pango_layout_set_font_description (layout, desc);
    pango_font_description_free (desc);

    for (i = 0; i < 12; i++)
    {
        int width, height;
        double angle = iter + (360.0 * i) / 12;
        double red;

        cairo_save (cr);

        red   = (1 + cos ((angle - 60) * G_PI / 180.)) / 2;
        cairo_set_source_rgb (cr, red, 0, 1.0 - red);

        cairo_rotate (cr, angle * G_PI / 180.);

        pango_cairo_update_layout (cr, layout);

        pango_layout_get_size (layout, &width, &height);
        cairo_move_to (cr, - ((double)width / PANGO_SCALE) / 2, - 250);
        pango_cairo_show_layout (cr, layout);

        cairo_restore (cr);
    }

    cairo_restore (cr);
    g_object_unref (layout);

}

This routine is being called a lot, maybe a hundred times in a second. And the memory leak is huge, around 30MB in 3secs, and has a constant rate. When I compare this code, it seems quite fine to me. I have searched for this, have found many references to memory leaks while using pango in Gtk applications, and they all look for a patch in pango or gtk. I am really puzzled and can't believe there would be such a bug in a heavily used library like pango and think this is a problem with my own code. Any suggestions is appreciated.

This is the vmmap result for Uli's code:

Executing vmmap -resident 25897 | grep TOTAL at beginning of main()
TOTAL                            321.3M   126.2M      485 
TOTAL                              18.0M       200K       1323       173K      0%       2
Executing vmmap -resident 25897 | grep TOTAL after cairo init
TOTAL                            331.3M   126.4M      489 
TOTAL                              27.0M       224K       1327      1155K      4%       6
Executing vmmap -resident 25897 | grep TOTAL after one iteration
TOTAL                            383.2M   143.9M      517 
TOTAL                              37.2M      3368K      18634      3423K      8%       5
Executing vmmap -resident 25897 | grep TOTAL after loop
TOTAL                            481.6M   244.1M      514 
TOTAL                             137.2M     103.7M     151961      66.4M     48%       6
Executing vmmap -resident 25897 | grep TOTAL at end
TOTAL                            481.6M   244.1M      520 
TOTAL                             136.3M     103.1M     151956      65.4M     48%      11

And this is the unfiltered output of the last stage:

Executing vmmap -resident 25751 at end
Process:         main [25751]
Path:            /PATH/OMITTED/main
Load Address:    0x109b9c000
Identifier:      main
Version:         ???
Code Type:       X86-64
Parent Process:  bash [837]

Date/Time:       2016-01-30 23:28:35.866 +0330
Launch Time:     2016-01-30 23:27:35.148 +0330
OS Version:      Mac OS X 10.11.2 (15C50)
Report Version:  7
Analysis Tool:   /Applications/Xcode.app/Contents/Developer/usr/bin/vmmap
Analysis Tool Version:  Xcode 7.0.1 (7A1001)
----

Virtual Memory Map of process 25751 (main)
Output report format:  2.4  -- 64-bit process
VM page size:  4096 bytes

==== Non-writable regions for process 25751

==== Legend
SM=sharing mode:  
    COW=copy_on_write PRV=private NUL=empty ALI=aliased 
    SHM=shared ZER=zero_filled S/A=shared_alias

==== Summary for process 25751
ReadOnly portion of Libraries: Total=219.6M resident=112.2M(51%) swapped_out_or_unallocated=107.5M(49%)
Writable regions: Total=155.7M written=5448K(3%) resident=104.1M(67%) swapped_out=0K(0%) unallocated=51.6M(33%)

                                VIRTUAL RESIDENT   REGION 
REGION TYPE                        SIZE     SIZE    COUNT (non-coalesced) 
===========                     ======= ========  ======= 
Activity Tracing                  2048K      12K        2 
Dispatch continuations            8192K      32K        2 
Kernel Alloc Once                    8K       8K        3 
MALLOC guard page                   32K       0K        7 
MALLOC metadata                    364K      84K       11 
MALLOC_LARGE                       260K     260K        2         see MALLOC ZONE table below
MALLOC_LARGE (empty)               980K     668K        2         see MALLOC ZONE table below
MALLOC_LARGE metadata                4K       4K        2         see MALLOC ZONE table below
MALLOC_SMALL                      32.0M     880K        3         see MALLOC ZONE table below
MALLOC_TINY                      104.0M   102.1M        7         see MALLOC ZONE table below
STACK GUARD                       56.0M       0K        3 
Stack                             8264K      60K        3 
VM_ALLOCATE                         16K       8K        2 
__DATA                            16.7M    13.6M      217 
__IMAGE                            528K     104K        2 
__LINKEDIT                        92.4M    22.5M       34 
__TEXT                           127.2M    89.6M      220 
__UNICODE                          552K     476K        2 
mapped file                       32.2M    13.7M        4 
shared memory                      328K     172K       10 
===========                     ======= ========  ======= 
TOTAL                            481.6M   244.3M      518 

                                 VIRTUAL   RESIDENT ALLOCATION      BYTES          REGION
MALLOC ZONE                         SIZE       SIZE      COUNT  ALLOCATED  % FULL   COUNT
===========                      =======  =========  =========  =========  ======  ======
DefaultMallocZone_0x109bd0000     136.3M     103.2M     151952      65.4M     48%      10
GFXMallocZone_0x109bd3000             0K         0K          0         0K               0
===========                      =======  =========  =========  =========  ======  ======
TOTAL                             136.3M     103.2M     151952      65.4M     48%      10

I have omitted the non-writable regions part because it was overflowing stackoverflow limits!

arashka
  • 1,226
  • 3
  • 17
  • 30
  • Do you have any self-contained example showing the problem that I could try out? – Uli Schlachter Jan 29 '16 at 18:09
  • Which Pango version are you using? Google found https://bugzilla.gnome.org/show_bug.cgi?id=474708 which is a memory leak which was apparently fixed in Pango 1.18.2. – Uli Schlachter Feb 01 '16 at 09:35
  • I just checked, pango-1.38.1. In fact it was the latest in macports. I guess it is an unreported bug. But after all these searches, I've lost my trust in pango! There are so many reports of memory leaks here and there, this shouldn't be the case in a well structured code. – arashka Feb 01 '16 at 10:50
  • BTW I have found out that the memory leak happens in pango_cairo_update_layout function. – arashka Feb 04 '16 at 07:43

1 Answers1

1

I don't see any memory leaks. The following program prints its memory usage before and after running your above function 100.000 times. Both numbers are the same for me.

#include <cairo.h>
#include <math.h>
#include <pango/pangocairo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define HEIGHT 500
#define WIDTH 500

void draw_with_cairo (cairo_t *cr)
{
    PangoLayout *layout;
    PangoFontDescription *desc;
    int i;

    cairo_save (cr);
    cairo_scale (cr, 1, -1);
    cairo_translate (cr, 0, -HEIGHT);

    cairo_translate (cr, 400, 300);

    layout = pango_cairo_create_layout (cr);

    pango_layout_set_text (layout, "Test", -1);
    desc = pango_font_description_from_string ("‌BMitra 32");
    pango_layout_set_font_description (layout, desc);
    pango_font_description_free (desc);

    for (i = 0; i < 12; i++)
    {
        int width, height;
        double angle = i + (360.0 * i) / 12;
        double red;

        cairo_save (cr);

        red   = (1 + cos ((angle - 60) * G_PI / 180.)) / 2;
        cairo_set_source_rgb (cr, red, 0, 1.0 - red);

        cairo_rotate (cr, angle * G_PI / 180.);

        pango_cairo_update_layout (cr, layout);

        pango_layout_get_size (layout, &width, &height);
        cairo_move_to (cr, - ((double)width / PANGO_SCALE) / 2, - 250);
        pango_cairo_show_layout (cr, layout);

        cairo_restore (cr);
    }

    cairo_restore (cr);
    g_object_unref (layout);
}

static void print_memory_usage(const char *comment)
{
    char buffer[1024];
    sprintf(buffer, "grep -E VmPeak\\|VmSize /proc/%d/status", getpid());
    printf("Executing %s %s\n", buffer, comment);
    system(buffer);
}

int main()
{
    cairo_surface_t *s;
    cairo_t *cr;
    int i;

    print_memory_usage("at beginning of main()");

    s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT);
    cr = cairo_create(s);

    print_memory_usage("after cairo init");

    draw_with_cairo(cr);
    print_memory_usage("after one iteration");

    for (i = 0; i < 100 * 1000; i++)
        draw_with_cairo(cr);

    print_memory_usage("after loop");

    cairo_surface_destroy(s);
    cairo_destroy(cr);

    print_memory_usage("at end");
    return 0;
}

Output for me (with no traces of any memory leaks):

Executing grep -E VmPeak\|VmSize /proc/31881/status at beginning of main()
VmPeak:    76660 kB
VmSize:    76660 kB
Executing grep -E VmPeak\|VmSize /proc/31881/status after cairo init
VmPeak:    77640 kB
VmSize:    77640 kB
Executing grep -E VmPeak\|VmSize /proc/31881/status after one iteration
VmPeak:    79520 kB
VmSize:    79520 kB
Executing grep -E VmPeak\|VmSize /proc/31881/status after loop
VmPeak:    79520 kB
VmSize:    79520 kB
Executing grep -E VmPeak\|VmSize /proc/31881/status at end
VmPeak:    79520 kB
VmSize:    78540 kB

P.S.: I tested this on an up-to-date debian testing amd64.

Uli Schlachter
  • 9,337
  • 1
  • 23
  • 39
  • So I guess the problem is in the mac version of pango. I see the leak and it is definitely in that routine. In fact I have pin pointed it to be pango layout. – arashka Jan 29 '16 at 20:15
  • Well this is a tough situation, I don't tend to modify pango at all! – arashka Jan 29 '16 at 20:23
  • Does something like the above reproduce your problem? (After removing the /proc-magic, I guess replacing that with getrusage() makes sense) – Uli Schlachter Jan 30 '16 at 09:31
  • First of all, thank you for teaching me! I ran your code, with a mirror modification. Instead of /proc-magic (as you said!), I had to use vmmap as follows: "vmmap -resident %d | grep TOTAL". I just grep-ed it to give me the total memory usage. I appended the results to the question text. Thanks again. – arashka Jan 30 '16 at 20:12