Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 03/05/2011, à 20:52

Pylades

Memcheck et Readline plus GIO

Bonjour à tous !

J'ai un projet pour lequel je suis amené à utiliser Readline et GIO (que j'ai choisi pour identifier les types Mime des fichiers). Le problème, c'est que l'outil Memcheck de Valgrind me rapporte que des quantités industrielles de mémoire sont allouées par ces bibliothèques mais ne sont pas libérées. Du coup, j'en viens à me demander s'il y a une erreur de ma part, si ce sont ces bibliothèques qui fuient ou si tout est normal et qu'il me faut juste un fichier de suppression. Et j'aimerais bien connaître, le cas échéant, la façon de créer un fichier de suppression.


Voici donc le code, relativement hideux pour le moment. Ne faites pas attention, je suis en phase de tests et ce code n'est qu'un embryon. Ne vous questionnez pas non plus sur l'utilité du programme, je ne suis pas sûr qu'il en ait une… ^^


clfb.h

/*
 *  pylCLFB: the command-line file browser, by Pyl
 *  Copyright (C) 2011 Paul Bazin
 *
 *  This file is part of pylCLFB.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef H_CLFB
#define H_CLFB

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <magic.h>
#include <readline/readline.h>

#define WD_SIZ_BASE 256U
#define PROMPT "clfb: %s> "
#define NOTICE "The Command-Line File Browser, version 0.1\n"\
    "Copyright (C) 2011 Paul Bazin <pyl{a}alwaysdata{*}net>\n"\
    "This program is free software: you can redistribute it and/or "\
    "modify it under\nthe terms of the GNU General Public License.\n"\
    "This program is distributed in the hope that it will be useful, "\
    "but WITHOUT ANY\nWARRANTY; without even the implied warranty of "\
    "MERCHANTABILITY or FITNESS FOR A\nPARTICULAR PURPOSE.\n"\
    "See the sources for detailed information.\n"

static void main_loop(magic_t);
static void args(int, char**);
static char* gwd(void);
static char* gen_prompt(void);
static void die(int, const char*, ...);
static char* strip_white(char*);
static int parse(const char*, magic_t);

#endif /* H_CLFB */

clfb.c

/*
 *  pylCLFB: the command-line file browser, by Pyl
 *  Copyright (C) 2011 Paul Bazin
 *
 *  This file is part of pylCLFB.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "clfb.h"
#include <gio/gio.h>
static void gio_way(void);
static int gio_parse(const char* line);

int main(int argc, char** argv)
{
    /*magic_t base;*/
    args(argc, argv);
    g_type_init();
    /*if (base = magic_open(MAGIC_SYMLINK | MAGIC_DEVICES |
            MAGIC_MIME | MAGIC_PRESERVE_ATIME))
    {
        if (!magic_load(base, NULL))
            main_loop(base);
        magic_close(base);
    }*/
    gio_way();
    return 0;
}

static void main_loop(magic_t base)
{
    for (;;)
    {
        char* line;
        char* prompt;
        const char* data;
        line = readline(prompt=gen_prompt());
        free(prompt);
        /* FIXME: error handling to be improved */
        if (!line)
            break;
        data = strip_white(line);
        if (data)
            /* TODO: history? */
            parse(data, base);
        free(line);
    }
    putchar('\n');
}

static void gio_way(void)
{
    for (;;)
    {
        char* line;
        char* prompt;
        const char* data;
        line = readline(prompt=gen_prompt());
        free(prompt);
        /* FIXME: error handling to be improved */
        if (!line)
            break;
        data = strip_white(line);
        if (data)
            /* TODO: history? */
            gio_parse(data);
        free(line);
    }
    putchar('\n');
}

static void args(int argc, char** argv)
{
    if (argc == 2)
        if (!strcmp(argv[1], "--version"))
        {
            fputs(NOTICE, stdout);
            exit(0);
        }
}

static char* gwd(void)
{
    size_t n = WD_SIZ_BASE;
    char* wd = NULL;
    char loop=1;
    while (loop)
    {
        free(wd);
        wd = malloc(n);
        if (wd)
        {
            if (getcwd(wd, n))
                loop=0;
            else
            {
                if (errno == ERANGE)
                    n += n/2;
                else if (errno == ENOENT)
                {
                    free(wd);
                    die(-2, "The working directory was removed. "
                            "This error is not handled yet.\n");
                }
                else
                {
                    free(wd);
                    die(-1, "Unknown error occured.\n");
                }
            }
        }
        else
            die(-1, "Alloc' failed.\n");
    }
    return wd;
}

static char* gen_prompt(void)
{
    char* prompt;
    const char* dir;
    char* pwd = gwd();
    dir = strrchr(pwd, '/') + 1;
    prompt = malloc(sizeof PROMPT + strlen(dir) -2);
    if (!prompt)
        die(-1, "Alloc' failed.\n");
    sprintf(prompt, PROMPT, dir);
    free(pwd);
    return prompt;
}

static void die(int stat, const char* errstr, ...)
{
    va_list ap;
    va_start(ap, errstr);
    vfprintf(stderr, errstr, ap);
    va_end(ap);
    exit(stat);
}

static char* strip_white(char* str)
{
    register char* s;
    register char* t;
    for (s=str; isspace(*s); s++);
    if (*s == '\0')
        return NULL;
    t = s + strlen (s) - 1;
    while (t > s && isspace(*t))
        t--;
    *++t = '\0';
    return s;
}

/* FIXME: to be rewrited. Fully. */
static int parse(const char* line, magic_t base)
{
    int ret=0;
    const char* type = magic_file(base, line);
    if (!type)
    {
        puts(magic_error(base));
        ret=-1;
    }
    else
    {
        puts(type);
        if (!strcmp(type, "application/x-directory; charset=binary"))
            chdir(line);
        else
            puts(":P");
    }
    return ret;
}

static int gio_parse(const char* line)
{
    int ret=0;
    /*gboolean unsure;
    const gchar* type = g_content_type_guess(line, NULL, 0, &unsure);
    puts(type);
    g_free(type);*/
    GError* error;
    GFile* file = g_file_new_for_path(line); /* we may use URI */
    GFileInfo* type = g_file_query_info(file,
            G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
            G_FILE_QUERY_INFO_NONE, NULL, &error);
    g_object_unref(file);
    puts(g_file_info_get_content_type(type)); /* to return */
    g_object_unref(type);
    return ret;
}

Et voici aussi la ligne utilisée pour Valgrind :

valgrind --tool=memcheck --leak-check=full --show-reachable=yes ./clfb

Si par ailleurs vous aviez un meilleure solution pour récupérer les types Mime au si vous saviez comment faire pour que Readline gère les noms de fichier avec espaces (comme avec Bash), je vous serais grandement reconnaissent de m'expliquer comment. big_smile


Merci d'avance pour le temps que vous voudrez bien m'accorder.


“Any if-statement is a goto. As are all structured loops.
“And sometimes structure is good. When it’s good, you should use it.
“And sometimes structure is _bad_, and gets into the way, and using a goto is just much clearer.”
                Linus Torvalds – 12 janvier 2003

Hors ligne

#2 Le 03/05/2011, à 21:49

grim7reaper

Re : Memcheck et Readline plus GIO

Pas le temps de regarder tes problèmes en détail ce soir, mais je peux au moins répondre à ça

Pylade a écrit :

Et j'aimerais bien connaître, le cas échéant, la façon de créer un fichier de suppression.

En prenant ta ligne de Valgrind ça donne

valgrind --tool=memcheck --leak-check=full --show-reachable=yes --gen-suppressions=all ./clfb 2>&1 | egrep "^[{}]|^   [^ \t]" > CLFB.supp

Le filtre n'est pas de moi, il faut rendre César ce qui est à César : merci Link31 (on ne le voit plus par ici et c'est vraiment dommage sad, lui c'était un bon, un très bon même…)

Hors ligne

#3 Le 03/05/2011, à 22:12

grim7reaper

Re : Memcheck et Readline plus GIO

Pylade a écrit :

comment faire pour que Readline gère les noms de fichier avec espaces (comme avec Bash), je vous serais grandement reconnaissent de m'expliquer comment. big_smile

Chai pas si c'est vraiment ce que tu entends par « comme avec Bash », mais je peux te proposer ce qui suit.

Bon, c'est pas compliqué (suffit d'arriver à mettre la main sur de la doc…).

Quelques initialisation d'abord

rl_filename_quote_characters  = " ";  /* Liste des caracteres qui provoquent la "quotation" du nom de fichier par la completion s'ils sont present dans le nom du fichier. */
rl_completer_quote_characters = "\""; /* Liste des caracteres utilises pour quoter. */
rl_attempted_completion_function = quote; /* La fonction qui quote */

Bon pour la fonction qui quote : si un des caractère de rl_filename_quote_characters apparaît dans le nom du fichier, je le quote en entier.

char** quote(const char* text, int start, int end)
{    
    rl_filename_quoting_desired = 1; /* Si different de zero ça veut dire qu'on quote. */
    return NULL;
}

Échapper chaque caractère chiant avec un backslash doit être faisable mais c'est sûrement plus difficile à mettre en place (je ne sais pas si c'est ce que tu voulais donc j'ai fait au plus simple big_smile)

Je te laisse ce lien fort utile : http://cnswww.cns.cwru.edu/php/chet/rea … html#SEC47

Dernière modification par grim7reaper (Le 03/05/2011, à 22:20)

Hors ligne

#4 Le 03/05/2011, à 22:25

Pylades

Re : Memcheck et Readline plus GIO

Merci.


Mais je problème, c'est que dois être sûr de mon code dans l'état actuel… ce qui n'est pas forcément le cas…

Du coup, j'ai appliqué la méthode à un code minimal (même si je ne suis pas sûr qu'il ne faille pas clore la GLib d'une quelconque façon) :

#include <stdlib.h>
#include <gio/gio.h>
#include <readline/readline.h>

int main(void)
{
    g_type_init();
    free(readline(NULL));
    return 0;
}

En appliquant le fichier de suppression ainsi généré, ça supprime toute les erreurs, mais seulement le quart de la mémoire fuyante.


À votre avis, il faudrait que j'applique la méthode à la version actuelle du code, au risque de passer à côté d'un vraie fuite de mémoire ?

Et sauriez-vous comment je pourrais m'assurer que mon programme minimal ne fuit pas et n'est pas incorrect ?


“Any if-statement is a goto. As are all structured loops.
“And sometimes structure is good. When it’s good, you should use it.
“And sometimes structure is _bad_, and gets into the way, and using a goto is just much clearer.”
                Linus Torvalds – 12 janvier 2003

Hors ligne

#5 Le 03/05/2011, à 22:35

Pylades

Re : Memcheck et Readline plus GIO

Hey, je n'avais pas vu ta seconde réponse. Merci beaucoup ! \o/


“Any if-statement is a goto. As are all structured loops.
“And sometimes structure is good. When it’s good, you should use it.
“And sometimes structure is _bad_, and gets into the way, and using a goto is just much clearer.”
                Linus Torvalds – 12 janvier 2003

Hors ligne

#6 Le 04/05/2011, à 05:56

grim7reaper

Re : Memcheck et Readline plus GIO

Pylade a écrit :

J'ai un projet pour lequel je suis amené à utiliser Readline et GIO (que j'ai choisi pour identifier les types Mime des fichiers). Le problème, c'est que l'outil Memcheck de Valgrind me rapporte que des quantités industrielles de mémoire sont allouées par ces bibliothèques mais ne sont pas libérées. Du coup, j'en viens à me demander s'il y a une erreur de ma part, si ce sont ces bibliothèques qui fuient ou si tout est normal et qu'il me faut juste un fichier de suppression.

Pour Readline je ne sais pas car  je ne l'ai jamais utilisé (donc il faut que je me renseigne là-dessus).

Par contre pour la GLib (donc GIO), c'est normal (j'avais déjà été confronté à ce problème en essayant d'utiliser Valgrind avec un programme qui utilisais GTK+). Le fait est que la GLib n'est pas du tout valgrind-friendly car elle utilise la mémoire à sa façon (pour des raisons d'optimisations) du genre (pour ce que j'en sais, il y a sûrement d'autres trucs qui entre en jeu) : utilisation de pool mémoire (les fameux g_slice) et l'allocation de variables globales pour, par exemple, l'enregistrement des GTypes, qui ne sont jamais libérées (d'après le post d'un gars sur la mailing-list valgrind-user c'est laissé à la charge de l'OS, si c'est vrai c'est un truc que je trouve particulièrement crade mais bon…).

Tout n'est pas noir, à l'époque j'avais trouvé des variables d'environnements qui, si elles sont bien positionnées, permette de pas mal limiter les dégâts (surtout les histoires de pool en fait). Mais bon, c'est pas non plus miraculeux (il reste toujours une tétrachiée d'avertissements chez moi…).

Tout ça pour dire que ce n'est pas ta faute (du moins pour GIO, pour Readline faut voir…)

Dernière modification par grim7reaper (Le 04/05/2011, à 06:00)

Hors ligne

#7 Le 04/05/2011, à 19:06

Pylades

Re : Memcheck et Readline plus GIO

OK, merci beaucoup. Bon, en combinant les variables d'environnement et le fichier de suppression de mon programme minimal, je n'ai plus aucune erreur ni fuite venant de GIO ! \o/
Bon, c'est juste dommage que je me prenne des erreurs 500 en essayant d'aller récupérer le fichier de suppression « officiel ».


Tout le reste est imputable à Readline. Rassure-moi, quand on utilise Readline, aucune initialisation ni clôture n'est nécessaire ? En tous cas, je n'en ai vu mention nulle part. Je vais essayer de me renseigner pour les fuites et erreurs.


“Any if-statement is a goto. As are all structured loops.
“And sometimes structure is good. When it’s good, you should use it.
“And sometimes structure is _bad_, and gets into the way, and using a goto is just much clearer.”
                Linus Torvalds – 12 janvier 2003

Hors ligne