Logo Search packages:      
Sourcecode: jmp version File versions  Download package

instance_owners.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <gtkutils.h>
#include <cls.h>
#include <obj.h>
#include <ui.h>
#include <heap_dump.h>
#include <object_dump.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <jmp-config.h>
#include <inttypes.h>

#ifndef        PRId32
/* Some systems still don't have the "print formatting" stuff in inttypes.h */
#define        PRId32 "d"
#endif

enum {
    INFO,
    OO_PARENT,
    NUM_COLUMNS
};

enum {
    OOS_CLASS,
    OOS_INSTANCES,
    OOSN_COLUMNS
};

static char buf[1024];

static int MAX_TOP_LEVEL_OBJECTS = 100;

typedef struct tc {
    GtkTreeStore* tree;
    void* data;
    size_t count;
} tc;

typedef struct hc {
    hashtab* hash;    /** statistics is stored in this hash 
                   *  store struct cc in its entries. 
                   */
    hashtab* visited; /** The objects we have visited already. */
    cls* clz;         /** class we are counting statistics for. */    
} hc;

typedef struct cc {
    cls* clz;       /** The class we accumulating statistics for. */
    size_t count;   /** Current instance count. */
} cc;

static size_t cc_jmphash_func (void* c, size_t len) {
    cc* cp = (cc*)c;
    return cls_jmphash_func (cp->clz, len);
}

static int cc_cmp_func (void* c1, void* c2) {
    cc* cp1 = (cc*)c1;
    cc* cp2 = (cc*)c2;
    return cls_cmp_func (cp1->clz, cp2->clz);
}

/** In case we want to store more information further on... 
 *  I think this might be nice to have around..
 */
00071 typedef struct objid_info {
    jobjectID obj_id;
} objid_info;

static size_t objid_jmphash_func (void* c, size_t len) {
    objid_info* o = (objid_info*)c;
    return ((long)o->obj_id) % len;;
}

static int objid_cmp_func (void* c1, void* c2) {
    objid_info* o1 = (objid_info*)c1;
    objid_info* o2 = (objid_info*)c2;
    return o1->obj_id != o2->obj_id;
}

static int not_in_stack (jobjectID obj, jobjectID* stack, size_t level) {
    size_t i;
    for (i = 0; i < level; i++) 
      if (obj == stack[i])
          return 0;
    return 1;
}

static int list_owners (jobjectID o, size_t level, 
                  size_t maxlevel, jobjectID* stack, 
                  GtkTreeStore* tree, GtkTreeIter* parent_node) {
    GtkTreeIter iter;
    object_link* owners;
    if (level >= maxlevel)
      return 0;
    stack[level] = o;
    owners = get_owners (o);
    if (owners == NULL)
      return 0;
    while (owners != NULL) {
      obj* parent;
      cls* pc = NULL;
      cls* vc = NULL;
      char* var = NULL;
      if (owners->parent != NULL && owners->clz != NULL) {
          parent = get_object (owners->parent);
          if (parent == NULL) {
            /* quite useful for debugging. /robo 
            fprintf (stderr, "object: %p not found at level: %d, requesting => ", 
                   owners->parent, level);
            */
            get_object_alloc (owners->parent);
            parent = get_object (owners->parent);
            /*
            fprintf (stderr, "parent: %p is now of %s\n", 
                   parent, cls_get_name (obj_get_class (parent)));
            */
          }
          if (parent) {
            pc = get_class (obj_get_class_id (parent));
          } else { 
            pc = get_class (owners->clz);
          }
          if (parent && pc == NULL) {
            get_class_load (obj_get_class_id (parent));
            pc = get_class (obj_get_class_id (parent));
          } 
          vc = get_class (owners->clz);
          if (pc && vc) {
            switch (owners->type) {     
            case STATIC_VARIABLE:
                var = vc->statics[owners->variable].field_name;
                snprintf (buf, 1024, _("%p is a static variable (%s) in class %s"),
                        owners->obj, var, vc->name);
                break;
            case VARIABLE:
                var = vc->instances[owners->variable].field_name;
                snprintf (buf, 1024, _("%p is a variable (%s) in object %p of class %s"),
                        owners->obj, var, owners->parent, pc->name);
                break;
            case ARRAY:
                var = "[]";
                snprintf (buf, 1024, _("%p is located in an array %p of %s at index %d"),
                        owners->obj, owners->parent, vc->name, owners->index);
                break;
            default:
                fprintf (stderr, "unknown type: %d\n", owners->type);
            }
          } else {
            snprintf (buf, 1024, _("not able to determine class(%p) or "
                               "parent class(%p), probably GC:ed"), vc, pc);
          }
      
          gtk_tree_store_append (tree, &iter, parent_node);
          gtk_tree_store_set (tree, &iter, INFO, buf, OO_PARENT, parent, -1);
      
          if (not_in_stack (owners->parent, stack, level) && 
            owners->parent != owners->obj &&
            !owners->type == STATIC_VARIABLE) {
            list_owners (owners->parent, level + 1, maxlevel, 
                       stack, tree, &iter);
          }
      } else {
          fprintf (stderr, 
                 "Warning: owners->parent_object: %p, owners->parent_clz: %p "
                 "for object: %p of class: %s\n", 
                 owners->parent, owners->clz, o, 
                 cls_get_name (obj_get_class (get_object (o))));
      }
      owners = owners->next;
      if (pc == NULL || vc == NULL)
          owners = NULL;
    }
    return 1;
}

static void find_instance_owners (void* object, void* tz) {
    obj* o = (obj*)object;
    tc* t = (tc*)tz;
    cls* c = (cls*)t->data;
    size_t maxlevel = 5;
    jobjectID* save = malloc (sizeof (jobjectID) * maxlevel);
    if (c != NULL && obj_get_class_id (o) == c->class_id) {
      GtkTreeIter iter;
      if (((tc*)tz)->count > MAX_TOP_LEVEL_OBJECTS) {
          ((tc*)tz)->count += get_owners (obj_get_object_id (o)) != NULL;
          return;
      }
      gtk_tree_model_get_iter_first (GTK_TREE_MODEL (t->tree), &iter);
      ((tc*)tz)->count += list_owners (obj_get_object_id (o), 0, maxlevel, 
                               save, t->tree, &iter);
    }
    free (save);
}

static void find_owners (void* object, void* tz) {
    obj* o = (obj*)object;
    tc* t = (tc*)tz;
    obj* ob = (obj*)t->data;
    size_t maxlevel = 5;
    jobjectID* save = malloc (sizeof (jobjectID) * maxlevel);
    if (o != NULL && ob == o) {
      GtkTreeIter iter;
      if (((tc*)tz)->count > MAX_TOP_LEVEL_OBJECTS) {
          ((tc*)tz)->count += get_owners (obj_get_object_id (o)) != NULL;
          return;
      }
      gtk_tree_model_get_iter_first (GTK_TREE_MODEL (t->tree), &iter);
      ((tc*)tz)->count += list_owners (obj_get_object_id (o), 0, maxlevel, 
                               save, t->tree, &iter);
    }
    free (save);
}

static void
add_columns (GtkTreeView *treeview) {
    GtkCellRenderer *renderer;
    renderer = gtk_cell_renderer_text_new ();
    gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
                                     -1, _("Class"),
                                     renderer, "text",
                                     INFO,
                                     NULL);
}

static jobjectID* get_stack (GtkTreeModel* model, GtkTreeIter* node, int* n) {
    GtkTreeIter iter;
    gboolean valid;
    jobjectID* stack;
    obj* current;
    int level;
    valid = gtk_tree_model_iter_parent (model, &iter, node);
    (*n)++;
    level = *n;
    if (valid == FALSE) {
      /* root node is only name, no object so we dont want to add that. */
      (*n)--;  
      stack = malloc (sizeof (jobjectID) * (*n));
    } else {
      stack = get_stack (model, &iter, n); 
      gtk_tree_model_get (model, node, OO_PARENT, &current, -1);
      stack[(*n - level)] = current ? obj_get_object_id (current) : NULL; 
    }
    return stack;
}

static void expand_instance (GtkObject* list) {
    GtkTreeIter parent;
    GtkTreeModel *model;
    GtkTreeSelection *selection;
    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
    if (gtk_tree_selection_get_selected (selection, &model, &parent)) {
      obj* current_object;
      int stack_depth = 0;
      GtkTreeStore* treestore = (GtkTreeStore*)gtk_tree_view_get_model (GTK_TREE_VIEW (list));
      jobjectID* save = get_stack (model, &parent, &stack_depth);
      remove_children (treestore, &parent);
      gtk_tree_model_get (model, &parent, OO_PARENT, &current_object, -1);
      list_owners (obj_get_object_id (current_object), 
                 0, 1, save, treestore, &parent);
      free (save);
      expand_node (treestore, list, &parent);
    } 
}

static GtkWidget *build_menu (GtkWidget* object_list, obj* o) {
    char buf[200];
    GtkWidget* omenu;
    GtkWidget* menuitem;
    omenu = gtk_menu_new ();
    menuitem = gtk_menu_item_new_with_label (_("expand"));
    gtk_menu_append (GTK_MENU (omenu), menuitem);    
    gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
                         GTK_SIGNAL_FUNC (expand_instance),
                         object_list);

    sprintf (buf, _("show other instances owned by %s"), cls_get_name (obj_get_class (o)));
    menuitem = gtk_menu_item_new_with_label (buf);
    gtk_menu_append (GTK_MENU (omenu), menuitem);    
    gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
                         GTK_SIGNAL_FUNC (show_object),
                         o);

    gtk_widget_show_all (omenu);
    return omenu;
}

static gint instance_button_handler (GtkWidget *widget,
                             GdkEventButton *event,
                             gpointer model) {
    GtkTreeIter iter;
    GtkTreeSelection *select; 
    obj* current_object;
    GtkTreeModel* m = (GtkTreeModel*)model;
    select = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
    if (gtk_tree_selection_get_selected (select, &m, &iter)) 
      gtk_tree_model_get (model, &iter, OO_PARENT, 
                      &current_object, -1);
    if (event->button == 3 && current_object != NULL) {
      GtkWidget* imenu = build_menu (widget, current_object);
      gtk_menu_popup (GTK_MENU (imenu), NULL, NULL, NULL, NULL,
                  event->button, event->time);  
      return TRUE;
    }
    return FALSE;
}


void show_instance_owners_base (hashtab* objects, cls* c, void* data, jmphash_iter_fa func) {
    GtkWidget *win;
    GtkWidget *scrolledwindow;
    GtkWidget *tree;    
    GtkTreeIter   iter;
    GtkTreeStore* model;
    GtkWidget *label;
    GtkWidget *status;
    GtkWidget *box;
    char buf[200];
    tc tz;

    if (data == NULL) {
      fprintf (stderr, "show_instance_owners called with null\n");
      return;
    }
    win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (win), _("Instance owners"));
    
    box = gtk_vbox_new (FALSE, 0);
    label = gtk_label_new(_("Please dump heap and retry if list seems incomplete/incorrect"));
    gtk_box_pack_start(GTK_BOX (box), label, FALSE, FALSE, 0);
    
    sprintf (buf, _("Showing a max of %0" PRId32 " instances"), MAX_TOP_LEVEL_OBJECTS);
    label = gtk_label_new(_(buf));
    gtk_box_pack_start(GTK_BOX (box), label, FALSE, FALSE, 0);

    label = gtk_label_new(_("Expand nodes with right-click menu"));
    gtk_box_pack_start(GTK_BOX (box), label, FALSE, FALSE, 0);

    scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
    gtk_box_pack_start(GTK_BOX (box), scrolledwindow, TRUE, TRUE, 0);
    model = gtk_tree_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);

    snprintf (buf, 1024, "%s", cls_get_name (c));
    gtk_tree_store_append (model, &iter, NULL);
    gtk_tree_store_set (model, &iter, INFO, buf, OO_PARENT, NULL, -1); 
    tz.data = data;
    tz.tree = model;
    tz.count = 0;
    jmphash_for_each_with_arg (func, objects, &tz);

    tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
    expand_node (model, GTK_OBJECT (tree), &iter);
    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
    add_columns (GTK_TREE_VIEW (tree));
    gtk_container_add (GTK_CONTAINER (scrolledwindow), tree);
    gtk_signal_connect (GTK_OBJECT(tree), "button_press_event",
                  GTK_SIGNAL_FUNC (instance_button_handler), 
                  model);

    gtk_container_add (GTK_CONTAINER (win), box);

    sprintf (buf, _("Found %0" PRId32 
                " instances (in last heap dump) of the %1" PRId32 
                " current instances"), 
           (int)tz.count, (int)cls_get_instances (c));
    status = gtk_statusbar_new ();
    gtk_statusbar_push (GTK_STATUSBAR (status), 0, buf);
    gtk_box_pack_start(GTK_BOX (box), status, FALSE, FALSE, 0);

    gtk_widget_set_usize (win, 500, 200);
    gtk_widget_show_all (win);
}

void show_instance_owners (hashtab* objects, cls* c) {
    show_instance_owners_base (objects, c, c, find_instance_owners);
}

void show_owner_for_object (hashtab* objects, obj* o) {
    show_instance_owners_base (objects, obj_get_class (o), o, find_owners);
}

static int already_visited (hc* h, object_link* o) {
    objid_info* os;
    objid_info  oi;
    oi.obj_id = o->obj;
    os = jmphash_search (&oi, h->visited);
    return os != NULL;
}

static void mark_visited (hc* h, object_link* o) {
    objid_info* os;
    os = malloc (sizeof (*os));
    os->obj_id = o->obj;
    jmphash_insert (os, h->visited);
}

static void add_statistics (hc* h, cls* pc) {
    cc* scp;
    cc sc;
    sc.clz = pc;            
    scp = jmphash_search (&sc, h->hash);
    if (scp) {
      scp->count++;
    } else {
      scp = malloc (sizeof(*scp));
      scp->clz = pc;
      scp->count = 1;
      jmphash_insert (scp, h->hash);
    }
}

static void find_owners_statistics (void* object, void* hz) {
    obj* o = (obj*)object;
    hc* h = (hc*)hz;
    cls* c = h->clz;
    if (c != NULL && obj_get_class_id (o) == c->class_id) {
      object_link* owners = get_owners (obj_get_object_id (o));
      while (owners) {
          cls* pc = NULL;
          if (!already_visited (h, owners)) {
            mark_visited (hz, owners);
            if (owners->parent) {
                obj* po = get_object (owners->parent);            
                if (po) {           
                  pc = obj_get_class (po);
                }
            }
            if (!pc) {
                /* we try this instead... */
                pc = get_class (owners->clz);
            }
            
            if (pc) {
                add_statistics (h, pc); 
            }
          }
          owners = owners->next;
      }
    }
}

static gint sort_stats_by_class_name (GtkTreeModel *model,
                              GtkTreeIter *a,
                              GtkTreeIter *b,
                              gpointer user_data) {
    char* cn1 = NULL;
    char* cn2 = NULL;
    gtk_tree_model_get (model, a, OOS_CLASS, &cn1, -1);
    gtk_tree_model_get (model, b, OOS_CLASS, &cn2, -1); 
    return (strcmp (cn1, cn2));    
}

static gint sort_stats_by_instances (GtkTreeModel *model,
                             GtkTreeIter *a,
                             GtkTreeIter *b,
                             gpointer user_data) {
    glong cn1;
    glong cn2;
    gtk_tree_model_get (model, a, OOS_INSTANCES, &cn1, -1);
    gtk_tree_model_get (model, b, OOS_INSTANCES, &cn2, -1); 
    return cn2 - cn1;
}


static gint ((*stats_comprs[])(GtkTreeModel *model,
                         GtkTreeIter *a,
                         GtkTreeIter *b,
                         gpointer user_data)) = { sort_stats_by_class_name,
                                          sort_stats_by_instances
                         };

static void os_column_clicked (GtkWidget *treeviewcolumn, gpointer user_data) {
    GtkTreeView* tree;
    GtkTreeModel* clist;
    int column;
    tree = GTK_TREE_VIEW (user_data);
    clist = gtk_tree_view_get_model (tree);
    for (column = 0; ; column++) {
      if (treeviewcolumn == (GtkWidget*)gtk_tree_view_get_column (tree, column))
          break;
    }
    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (clist), column, GTK_SORT_ASCENDING);
    gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (clist), column, stats_comprs[column], NULL, NULL);
}

static void add_statistics_object (cc* sc, GtkListStore* clist) {
    GtkTreeIter iter;
    gtk_list_store_append (clist, &iter);
    gtk_list_store_set (clist, &iter, 
                  OOS_CLASS, cls_get_name (sc->clz), 
                  OOS_INSTANCES, sc->count,
                  -1);
}

static void show_owner_statistics_window (hc* hz) {
    GtkWidget* scrolledwindow;
    GtkListStore* clist;
    GtkWidget* tree;
    GtkWidget* box;
    GtkWidget* w;
    GtkWidget* statistics_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (statistics_window), _("Owner statistics"));
    box = gtk_vbox_new (FALSE, 2);
    gtk_container_add (GTK_CONTAINER (statistics_window), box);
    
    w = gtk_label_new (cls_get_name (hz->clz));
    gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);    

    scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
    gtk_box_pack_start (GTK_BOX (box), scrolledwindow, TRUE, TRUE, 0);
    
    clist = gtk_list_store_new (OOSN_COLUMNS, G_TYPE_STRING, G_TYPE_LONG);
    tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (clist));
    add_column (tree, _("Class"), OOS_CLASS, (gpointer)tree, os_column_clicked, 300, 0);
    add_column (tree, _("Instances"), OOS_INSTANCES, (gpointer)tree, os_column_clicked, 80, 1);
    gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (tree), TRUE);
    gtk_container_add (GTK_CONTAINER (scrolledwindow), tree);

    jmphash_for_each_with_arg ((jmphash_iter_fa)add_statistics_object, hz->hash, clist);
    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (clist), 0, GTK_SORT_ASCENDING);
    gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (clist), 0, 
                             sort_stats_by_instances, NULL, NULL);
    gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (clist));
    gtk_widget_set_usize (statistics_window, 400, 200);
    gtk_widget_show_all (statistics_window);    
}

void show_owners_statistics (hashtab* objects, cls* c) {
    hc hz;
    hz.hash = jmphash_new (42, cc_jmphash_func, cc_cmp_func, "cc_classes");
    hz.visited = jmphash_new (42, objid_jmphash_func, objid_cmp_func, "cc_visited");
    hz.clz = c;
    jmphash_for_each_with_arg (find_owners_statistics, objects, &hz);
    show_owner_statistics_window (&hz);
    jmphash_for_each (free, hz.hash);
    jmphash_free (hz.hash);
    jmphash_for_each (free, hz.visited);
    jmphash_free (hz.visited);
}

void show_owner (jobjectID jid) {
    obj* o;
    if (!is_get_owners_possible ()) {
      run_heap_dump ();
    }
    o = get_object (jid);
    if (o)
      run_find_instance_owners (o);
    else 
      set_status (_("unable to find object"));
}

/* Emacs Local Variables: */
/* Emacs mode:C */
/* Emacs c-indentation-style:"gnu" */
/* Emacs c-hanging-braces-alist:((brace-list-open)(brace-entry-open)(defun-open after)(substatement-open after)(block-close . c-snug-do-while)(extern-lang-open after)) */
/* Emacs c-cleanup-list:(brace-else-brace brace-elseif-brace space-before-funcall) */
/* Emacs c-basic-offset:4 */
/* Emacs End: */

Generated by  Doxygen 1.6.0   Back to index