/* Chrysalide - Outil d'analyse de fichiers binaires * gleak.c - aide à la détection de fuites d'instances de GTypes * * Copyright (C) 2019 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide 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. * * Chrysalide 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 Chrysalide. If not, see . */ #include "gleak.h" #include #include #include #include #include #include #include #include "common/environment.h" #define DUMP_OPEN_MSG "\n---------- GType instance leak dump ----------\n\n" #define DUMP_CLOSE_MSG "\n----------------------------------------------\n\n" /* Description de type utilisé */ typedef struct _tracked_gtype_t { GType type; /* Type GLib représenté */ char *name; /* Désignation humaine */ } tracked_gtype_t; /* Conservation des types même sans les greffons associés */ static tracked_gtype_t *__tracking = NULL; static size_t __count = 0; /* Effectue une comparaison entre deux mémorisations de GTypes. */ static int compare_gtypes_for_leaks(const tracked_gtype_t *, const tracked_gtype_t *); /* Constitue une base de données de nom de tous les GTypes. */ static void _remember_gtypes_for_leaks(GType); /* Parcourt l'arborescence des types à la recherche de fuites. */ static void track_gtype_for_leak(GType, bool *); /****************************************************************************** * * * Paramètres : a = première description à comparer. * * b = seconde description à comparer. * * * * Description : Effectue une comparaison entre deux mémorisations de GTypes. * * * * Retour : Bilan de l'opération (-1, 0 ou 1). * * * * Remarques : - * * * ******************************************************************************/ static int compare_gtypes_for_leaks(const tracked_gtype_t *a, const tracked_gtype_t *b) { int result; /* Bilan à retourner */ if (a->type < b->type) result = -1; else if (a->type > b->type) result = 1; else result = 0; return result; } /****************************************************************************** * * * Paramètres : root = racine des types d'instance à parcourir. * * * * Description : Constitue une base de données de nom de tous les GTypes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void _remember_gtypes_for_leaks(GType root) { GType *children; /* Liste de tous les sous-types*/ guint count; /* Taille de cette liste */ tracked_gtype_t *new; /* Nouvelle feuille à mémoriser*/ GType *iter; /* Boucle de parcours */ children = g_type_children(root, &count); if (count == 0) { __tracking = realloc(__tracking, ++__count * sizeof(tracked_gtype_t)); new = &__tracking[__count - 1]; new->type = root; new->name = strdup(g_type_name(root)); } else for (iter = children; *iter != 0; iter++) _remember_gtypes_for_leaks(*iter); g_free(children); } /****************************************************************************** * * * Paramètres : root = racine des types d'instance à parcourir. * * * * Description : Constitue une base de données de nom de tous les GTypes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void remember_gtypes_for_leaks(void) { _remember_gtypes_for_leaks(G_TYPE_OBJECT); } /****************************************************************************** * * * Paramètres : root = racine des types d'instance à retrouver. * * first = suivi des premières impressions. [OUT] * * * * Description : Parcourt l'arborescence des types à la recherche de fuites. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void track_gtype_for_leak(GType root, bool *first) { GType *children; /* Liste de tous les sous-types*/ guint count; /* Taille de cette liste */ int remaining; /* Nombre d'instances restantes*/ const tracked_gtype_t *tracked; /* Infos de type mémorisées */ GType *iter; /* Boucle de parcours */ children = g_type_children(root, &count); if (count == 0) { remaining = g_type_get_instance_count(root); if (remaining > 0) { if (*first) { fprintf(stderr, DUMP_OPEN_MSG); *first = false; } tracked = bsearch((tracked_gtype_t []) { { .type = root } }, __tracking, __count, sizeof(tracked_gtype_t), (__compar_fn_t)compare_gtypes_for_leaks); assert(tracked != NULL); fprintf(stderr, "%s: %d\n", tracked->name, remaining); } } else for (iter = children; *iter != 0; iter++) track_gtype_for_leak(*iter, first); g_free(children); } /****************************************************************************** * * * Paramètres : - * * * * Description : Affiche la liste des instances courantes restantes par type. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void dump_remaining_gtypes(void) { char *debug; /* Conditions d'environnement */ bool first; /* Première fois ? */ size_t i; /* Boucle de parcours */ debug = get_env_var("GOBJECT_DEBUG"); if (strstr(debug, "instance-count") == NULL) { fprintf(stderr, DUMP_OPEN_MSG); fprintf(stderr, "The GOBJECT_DEBUG variable does not include instance-count.\n"); fprintf(stderr, "Exiting the dumping process...\n"); fprintf(stderr, DUMP_CLOSE_MSG); } else { first = true; qsort(__tracking, __count, sizeof(tracked_gtype_t), (__compar_fn_t)compare_gtypes_for_leaks); track_gtype_for_leak(G_TYPE_OBJECT, &first); if (!first) fprintf(stderr, DUMP_CLOSE_MSG); } free(debug); for (i = 0; i < __count; i++) free(__tracking[i].name); if (__tracking != NULL) { free(__tracking); __tracking = NULL; __count = 0; } }