#include #include #include typedef struct { gsize size; const char *file; guint line; GTrackedMemType type; gulong data; char *data_str; } TrackedMemoryInfo; gulong n_info; TrackedMemoryInfo *info; static void hash_to_list (gpointer key, gpointer value, gpointer user_data) { GList **list = user_data; *list = g_list_prepend (*list, value); } typedef struct { gsize size; int count; } SizeInfo; typedef struct { int line; gsize size; int count; } LineInfo; typedef struct { const char *file; gsize size; } FileInfo; typedef struct { const char *type; gsize size; int count; } ObjectInfo; static gint compare_object_info (gconstpointer a, gconstpointer b) { const ObjectInfo *aa = a; const ObjectInfo *bb = b; if (aa->size < bb->size) return 1; else if (aa->size == bb->size) return 0; else return -1; } static gint compare_file_info (gconstpointer a, gconstpointer b) { const FileInfo *aa = a; const FileInfo *bb = b; if (aa->size < bb->size) return 1; else if (aa->size == bb->size) return 0; else return -1; } static gint compare_size_info (gconstpointer a, gconstpointer b) { const SizeInfo *aa = a; const SizeInfo *bb = b; if (aa->size < bb->size) return 1; else if (aa->size == bb->size) return 0; else return -1; } static gint compare_total_size_info (gconstpointer a, gconstpointer b) { const SizeInfo *aa = a; const SizeInfo *bb = b; if (aa->size * aa->count < bb->size * bb->count) return 1; else if (aa->size * aa->count == bb->size * bb->count) return 0; else return -1; } static gint compare_line_info (gconstpointer a, gconstpointer b) { const LineInfo *aa = a; const LineInfo *bb = b; if (aa->size < bb->size) return -1; else if (aa->size == bb->size) return 0; else return 1; } static void print_filename_data (const char *filename) { int i; GHashTable *size_hash, *line_hash; gsize total = 0; gsize per_type[G_MEM_TYPE_LAST] = {0}; SizeInfo *size_info; LineInfo *line_info; GList *list, *l; size_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); line_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); for (i = 0; i < n_info; i++) { if (info[i].file == NULL || strcmp (info[i].file, filename) != 0) continue; total += info[i].size; if (info[i].type >= 0 && info[i].type < G_MEM_TYPE_LAST) per_type[info[i].type] += info[i].size; size_info = g_hash_table_lookup (size_hash, GINT_TO_POINTER (info[i].size)); if (size_info == NULL) { size_info = g_new (SizeInfo, 1); size_info->size = info[i].size; size_info->count = 0; g_hash_table_insert (size_hash, GINT_TO_POINTER (info[i].size), size_info); } size_info->count++; line_info = g_hash_table_lookup (line_hash, GINT_TO_POINTER (info[i].line)); if (line_info == NULL) { line_info = g_new (LineInfo, 1); line_info->line = info[i].line; line_info->size = 0; line_info->count = 0; g_hash_table_insert (line_hash, GINT_TO_POINTER (info[i].line), line_info); } line_info->size += info[i].size; line_info->count++; } g_print ("For the file '%s':\n" " total size: %zu\n" " unknown type: %zu\n" " strings: %zu\n" " objects: %zu\n" " slice: %zu\n" " list: %zu\n" " pixbuf data: %zu\n", filename, total, per_type[G_MEM_TYPE_UNKNOWN], per_type[G_MEM_TYPE_STRING], per_type[G_MEM_TYPE_OBJECT], per_type[G_MEM_TYPE_SLICE], per_type[G_MEM_TYPE_LIST], per_type[G_MEM_TYPE_PIXBUF_DATA]); list = NULL; g_hash_table_foreach (size_hash, hash_to_list, &list); list = g_list_sort (list, compare_size_info); for (l = list; l != NULL; l = l->next) { size_info = l->data; g_print ("block size %zu: %d allocations (%zu bytes)\n", size_info->size, size_info->count, size_info->size * size_info->count); } g_hash_table_destroy (size_hash); g_list_free (list); list = NULL; g_hash_table_foreach (line_hash, hash_to_list, &list); list = g_list_sort (list, compare_line_info); for (l = list; l != NULL; l = l->next) { line_info = l->data; g_print ("line %d: %zu bytes (%d allocations)\n", line_info->line, line_info->size, line_info->count); } g_hash_table_destroy (line_hash); g_list_free (list); } static void print_summary (void) { int i; gsize total = 0; gsize per_type[G_MEM_TYPE_LAST] = {0}; for (i = 0; i < n_info; i++) { total += info[i].size; if (info[i].type >= 0 && info[i].type < G_MEM_TYPE_LAST) per_type[info[i].type] += info[i].size; } g_print ("total size: %zu\n" " unknown type: %zu\n" " strings: %zu\n" " objects: %zu\n" " slice: %zu\n" " list headers: %zu\n" " pixbuf data: %zu\n", total, per_type[G_MEM_TYPE_UNKNOWN], per_type[G_MEM_TYPE_STRING], per_type[G_MEM_TYPE_OBJECT], per_type[G_MEM_TYPE_SLICE], per_type[G_MEM_TYPE_LIST], per_type[G_MEM_TYPE_PIXBUF_DATA]); } static void print_objects (void) { int i; ObjectInfo *object_info; GHashTable *hash; GList *list, *l; hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); for (i = 0; i < n_info; i++) { if (info[i].type != G_MEM_TYPE_OBJECT) continue; object_info = g_hash_table_lookup (hash, info[i].data_str); if (object_info == NULL) { object_info = g_new (ObjectInfo, 1); object_info->type = info[i].data_str; object_info->size = 0; object_info->count = 0; g_hash_table_insert (hash, info[i].data_str, object_info); } object_info->size += info[i].size; object_info->count++; } list = NULL; g_hash_table_foreach (hash, hash_to_list, &list); list = g_list_sort (list, compare_object_info); for (l = list; l != NULL; l = l->next) { object_info = l->data; g_print ("type '%s': %zu bytes, %d objects\n", object_info->type, object_info->size, object_info->count); } } static void print_strings (void) { int i; GHashTable *size_hash, *file_hash; gsize total = 0; gsize no_file = 0; SizeInfo *size_info; FileInfo *file_info; GList *list, *l; size_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); file_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); for (i = 0; i < n_info; i++) { if (info[i].type != G_MEM_TYPE_STRING) continue; total += info[i].size; if (info[i].file == NULL) no_file += info[i].size; else { file_info = g_hash_table_lookup (file_hash, info[i].file); if (file_info == NULL) { file_info = g_new (FileInfo, 1); file_info->file = info[i].file; file_info->size = 0; g_hash_table_insert (file_hash, (char *)info[i].file, file_info); } file_info->size += info[i].size; } size_info = g_hash_table_lookup (size_hash, GINT_TO_POINTER (info[i].size)); if (size_info == NULL) { size_info = g_new (SizeInfo, 1); size_info->size = info[i].size; size_info->count = 0; g_hash_table_insert (size_hash, GINT_TO_POINTER (info[i].size), size_info); } size_info->count++; } list = NULL; g_hash_table_foreach (file_hash, hash_to_list, &list); list = g_list_sort (list, compare_file_info); g_print ("String allocations:\n"); if (no_file > 0) g_print ("without file info: %zu bytes\n", no_file); for (l = list; l != NULL; l = l->next) { file_info = l->data; g_print ("file %s: %zu bytes\n", file_info->file, file_info->size); } g_hash_table_destroy (file_hash); g_list_free (list); g_print("\n"); list = NULL; g_hash_table_foreach (size_hash, hash_to_list, &list); list = g_list_sort (list, compare_total_size_info); for (l = list; l != NULL; l = l->next) { size_info = l->data; g_print ("string of size %zu: %d (%zu bytes)\n", size_info->size, size_info->count, size_info->size * size_info->count); } g_hash_table_destroy (size_hash); g_list_free (list); } static void print_per_file (void) { int i; GHashTable *hash; gsize no_file = 0; FileInfo *per_file; GList *list, *l; hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); for (i = 0; i < n_info; i++) { if (info[i].file == NULL) no_file += info[i].size; else { per_file = g_hash_table_lookup (hash, info[i].file); if (per_file == NULL) { per_file = g_new (FileInfo, 1); per_file->file = info[i].file; per_file->size = 0; g_hash_table_insert (hash, (char *)info[i].file, per_file); } per_file->size += info[i].size; } } g_print ("Per-file memory use:\n"); if (no_file > 0) g_print ("No file info: %zu\n", no_file); list = NULL; g_hash_table_foreach (hash, hash_to_list, &list); list = g_list_sort (list, compare_file_info); for (l = list; l != NULL; l = l->next) { per_file = l->data; g_print ("file '%s': %zu\n", per_file->file, per_file->size); } g_hash_table_destroy (hash); g_list_free (list); } static guchar read_byte (FILE *file) { guchar v; fread (&v, 1, 1, file); return v; } static guint read_uint (FILE *file) { guint v; fread (&v, sizeof (v), 1, file); return v; } static gulong read_ulong (FILE *file) { gulong v; fread (&v, sizeof (v), 1, file); return v; } static char * read_string (FILE *file) { char *str; guint len; len = read_uint (file); str = g_malloc0 (len + 1); fread (str, len, 1, file); return str; } int main (int argc, char *argv[]) { int n_strs, i; FILE *file; GHashTable *str_hash; GOptionContext *context; gboolean summary = 0, per_file = 0, objects = 0, strings = 0; char *filename = NULL; GOptionEntry entries[] = { {"summary", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, (gpointer)&summary }, {"per-file", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, (gpointer)&per_file }, {"objects", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, (gpointer)&objects }, {"file", 'f', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_STRING, (gpointer)&filename }, {"strings", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, (gpointer)&strings }, {NULL} }; context = g_option_context_new (""); g_option_context_set_help_enabled (context, TRUE); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, NULL)) return 1; if (argc != 2) { fprintf (stderr, "Give a tracker file on the command line\n"); return 1; } file = fopen (argv[1], "r"); if (file == NULL) { fprintf (stderr, "Can't open %s\n", argv[1]); return 1; } str_hash = g_hash_table_new (g_direct_hash, g_direct_equal); n_strs = read_uint (file); for (i = 0; i < n_strs; i++) { char *str = read_string (file); g_hash_table_insert (str_hash, GINT_TO_POINTER (i+1), str); } n_info = read_ulong (file); info = g_malloc0 (n_info * sizeof (TrackedMemoryInfo)); for (i = 0; i < n_info; i++) { info[i].size = read_ulong (file); info[i].file = g_hash_table_lookup (str_hash, GINT_TO_POINTER (read_uint (file))); info[i].line = read_uint (file); info[i].type = read_byte (file); info[i].data = read_ulong (file); if (info[i].type == G_MEM_TYPE_OBJECT) info[i].data_str = g_hash_table_lookup (str_hash, GINT_TO_POINTER (info[i].data)); } fclose (file); if (summary) print_summary (); if (per_file) print_per_file (); if (filename) print_filename_data (filename); if (objects) print_objects (); if (strings) print_strings (); return 0; }