Совсем случайно наткнулся в сети на
wmbday. Это апплет для Window Maker'а, который напоминает вам о днях рождения ваших друзей. Т.е. вы создаёте файлик в который записываете их даты рождения и имена. А этот апплет показывает вам четыре следующих дня рождения. Прежде всего мне в программе понравился её стиль. У меня все апплеты в этом стиле :) Поэтому даже думал (да и думаю) её собрать в Сизиф.
Распаковал тарболл и сразу понял: программу писал простой такой программер :) Всего 10 файлов где-то, все в одном каталоге. Вручную написанный даже не автором configure в 40 строк, также вручную написанный Makefile, который не знает ни про DESTDIR, ни про CFLAGS :-/ Вообщем, проект в зачаточном состоянии. Я такие как раз люблю - кода пока не много, можно порыться, разобраться легче ну и конечно же мой любимый сишный код :) В нём мне нравится копаться :)
Сразу же разбежались глаза: с чего начать... патчить ;))) Как минимум, нужно патчить Makefile, потом добавить в заголовочные файлы #ifndef'ы, чтобы они не включались по несколько раз и ещё много чего. Но это дело слегка рутинное, не интересное. Да и вечер уже был и разбираться с этим не хотелось. Запустил я это чудо под valgrind'ом:
==15293== 112 bytes in 1 blocks are definitely lost in loss record 49 of 78
==15293== at 0x401A541: malloc (vg_replace_malloc.c:149)
==15293== by 0x4045013: XCreateGC (in /usr/lib/libX11.so.6.2.0)
==15293== by 0x804A2E0: CreateWindowGC (main.c:288)
==15293== by 0x804B012: main (main.c:615)
==15293==
==15293== LEAK SUMMARY:
==15293== definitely lost: 112 bytes in 1 blocks.
==15293== possibly lost: 0 bytes in 0 blocks.
Утечке я, признаюсь, даже порадовался :) Нуачо? Не радость ли это, в свободное время покопаться в коде, поисследовать? Для меня радость - мне нравится помогать свободным проектам и к тому же учиться и совершенствоваться самому. Да, я не написал ни одной программы, но иногда я думаю: а зачем? Зачем мне изобретать велосипеды? Я лучше уж кому-нить помогу доизобрести его ;)
Так вот. Эта утечка оказалось не такой уж и простой. Я как думал: память с помощью XCreateGC() выделили, а освободить с помощью XFreeGC() забыли. Добавил вызов XFreeGC() - всё равно течёт. Гм... Ну что ж, полезем в отладчик что ли? После make debug я уже жмякал ентером и следил за происходящим. Смотрите сами, память выделяется здесь:
int CreateWindowGC() {
XGCValues values;
if((theGC = XCreateGC(mySettings.display, RootWindow(mySettings.display, DefaultScreen(mySettings.display)), 0, &values)) == 0)
return 0; /* error */
else
return 1;
}
Дальше глобальная переменная theGC активно используется в других ф-циях. Дошел я до вызова этой самой ф-ци в отладчике:
288 if((theGC = XCreateGC(mySettings.display, RootWindow(mySettings.display, DefaultScreen(mySettings.display)), 0, &values)) == 0)
(gdb) s
291 return 1;
(gdb) print theGC
$3 = 0x80537f8
Убедился, что память таки выделилась... иду дальше, дальше, дальше... и тут на тебе! Снова эта ф-ция вызывается! Я сразу думаю: зачем? Ведь ф-ция всего лишь присваивает значение для глобальной переменной? Эта ф-ция всего лишь инициализирует её каким-либо значением.
Наверное, тут уже многие поняли секрет утечки.. НЕТ? :) Окей, давайте расскажу:
(gdb) p theGC
$6 = 0x80578c8
Сравните адреса! Они разные! :) А переменная при этом одна! Понимаете, о чем я? :)
Поскольку программа маленькая, я не поленился добавить printf'ов с выводом значения переменной theGC и увидел, что эта переменная попросту переписывается при повторном вызове CreateWindowGC() !
В итоге я сделал такой патчик:
[c0der@rock /tmp]$ cat wmbday-0.3-alt-src-memory_leak_fix.patch
diff -ru wmbday-0.3.orig/main.c wmbday-0.3/main.c
--- wmbday-0.3.orig/main.c 2005-07-01 18:46:15 +0700
+++ wmbday-0.3/main.c 2006-04-23 00:17:25 +0700
@@ -24,7 +24,7 @@
char* enum_months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
Window window;
Window iconwin;
-GC theGC;
+GC theGC = NULL;
Pixmap pix_on;
Pixmap pix_off;
Pixmap pix_text;
@@ -285,6 +285,10 @@
int CreateWindowGC() {
XGCValues values;
+ /* php-coder: plug memory leak */
+ if (theGC != NULL)
+ return 1;
+
if((theGC = XCreateGC(mySettings.display, RootWindow(mySettings.display, DefaultScreen(mySettings.display)), 0, &values)) == 0)
return 0; /* error */
else
Вот такое весёлое приключение! :) Сейчас патч отправлю автору, запостю этот пост в ЖЖ ну и буду радоваться что у меня прибавилось опыта ;))
Всем удачных выходных!
Viva Open Source!
Updated: Этот патч рабочий но не совсем кошерный :) Задачей этого патча было заткнуть дыру в максимально малое количество изменений. Я подумал, что более правильным вариантом в данном случае будет вообще удалить эту ф-цию. Она используется всего два раза, причем второй раз абсолютно лишний. Поэтому я вынес её в сам исходник.
Результируюший патч я только что отправил разработчику. Сделал ещё три маленьких патча, всего они доступны здесь:
ftp://ftp.altlinux.org/pub/people/php-coder/patches/wmbday/ Updated(20080515): fixed typo