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

ui.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <jvmpi.h>
#include <gtk/gtk.h>
#include <gtkutils.h>
#include <hash.h>
#include <jmp.h>
#include <ui.h>
#include <jthread.h>
#include <filterconfig.h>
#include <eventconfig.h>
#include <threads_window.h>
#include <comparators.h>
#include <dumper.h>
#include <heapgraph.h>
#include <about_dialog.h>
#include <heap_dump.h>
#include <monitor_dialog.h>
#include <visible_row_config.h>
#include <method_window.h>
#include <class_window.h>

/** state tokens. */
static int        init_done = 0;
static int        quit = 0;
static int        is_updating = 1;

/** id of last dump file... */
static int dump_id = -1;

/** Are we dumping on a timer basis? */
static gint  current_timer = 0; /* the timer identifier. */
static long  dumptimer = 0;     /* The countdown value (10)... */
static long  dumptime = 0;      /* Current value (1, 2, 3, 4,....) */

static GtkWidget* statusbar;
static GtkTooltips* tooltips;
static GtkWidget* JMPmain;

/** arrays that hold the methods/objects we show. */
static jthread** threadslist = NULL;
static int threads_count = 0;

/** The window of the current threads..  This is used in the
    timerstack locking functions to decide iff we need to lock. */
static threads_window *threadswin = NULL;

/** This is the timeout function called from gtk-main.
 *  Check if it is time to quit.
 */
gint run_updates (gpointer data) {
    if (quit == 1) {
      return TRUE;
    }
    if (quit == 2) {
      if (usingUI ()) {
          gtk_main_quit ();   /* time to shut down. */
      }
      return FALSE;           /* stop timer. */
    }
    
    if (usingUI ()) {
      gdk_threads_enter ();   
      updateUI (get_classes (), get_methods ());
      gdk_threads_leave ();
    }
    
    if (dumptimer > 0) {
      dumptime++;
      if (dumptime == dumptimer) {
          run_data_dump ();
          dumptime = 0;
      }
    }
    
    return TRUE;                /* keep timer */
}

void run_dump (GtkMenuItem *menuitem, gpointer user_data) {
    run_data_dump ();
}

void run_garbage_collector (GtkMenuItem *menuitem, gpointer user_data) {
    set_status (_("Requesting garbage collection ..."));
    run_GC ();
}

void run_heap_dumper (GtkMenuItem *menuitem, gpointer user_data) {
    set_status (_("Getting heap dump"));
    run_heap_dump ();
    set_status (_("Heap dumped"));
}

void run_monitor_dumper (GtkMenuItem *menuitem, gpointer user_data) {
    hashtab* monitors;
    set_status (_("Getting monitor dump"));
    monitors = run_monitor_dump ();
    show_monitor_dialog (monitors);
    set_status (_("Monitors dumped"));
    cleanup_monitor_information ();
}

void run_reset_counter (GtkMenuItem *menuitem, gpointer user_data) {
    set_status (_("Resetting counters..."));
    reset_counters ();
    set_status (_("Counters reseted"));
}

void run_restore_counter (GtkMenuItem *menuitem, gpointer user_data) {
    set_status (_("Resetoring counters..."));
    restore_counters ();
    set_status (_("Counters restored"));
}

static void freeze_ui (GtkWidget* button) {
    is_updating = !is_updating;
    set_status (is_updating ? 
            _("UI updating continued") : 
            _("UI updating frozen"));
    gtk_label_parse_uline (GTK_LABEL (GTK_BIN (GTK_BUTTON (button))->child), 
                     is_updating ? _("_Freeze ui") : _("Un_freeze"));
}

static void show_threads () {
    set_status ("showing threads...");
    if (threadswin == NULL)
      threadswin = create_threads_window (get_threads ());
    else 
      update_threads_window (threadswin, get_threads ());
    timerstacks_set_need_locks (1);
    gtk_widget_show_all (GTK_WIDGET (threadswin->window));
}

static void about_jmp (GtkMenuItem *menuitem, gpointer user_data) {
    GtkWidget *about = create_about_dialog ();  
    gtk_widget_show_all (about);
}

/** Add a button to the button box.
 * @param label the text on the button
 * @param tooltip the tool tip for the button giving a more detailed description
 *  of the actions of the button.
 * @param hbuttonbox the button box to add the button to.
 * @param handler the callback for the button.
 */
static void add_button (char* label, char* tooltip, 
                  GtkWidget* hbuttonbox, GCallback* handler) {
    GtkWidget* button;
    guint button_key;
    button = gtk_button_new_with_label ("");
    button_key = 
      gtk_label_parse_uline (GTK_LABEL (GTK_BIN (button)->child), label);
    gtk_tooltips_set_tip (tooltips, button, tooltip, NULL);
    gtk_container_add (GTK_CONTAINER (hbuttonbox), button);
    g_signal_connect (G_OBJECT (button), "clicked",
                  G_CALLBACK (handler), button);
}

/** Build the main window.
 *  Names of widgets is not totally nice though...
 */
GtkWidget* create_JMP (void) {
    GtkWidget *JMP;
    GtkWidget *vbox;
    GtkWidget *menubar;
    GtkWidget *hbuttonbox;
    GtkWidget *heapgraph;
    GtkItemFactoryEntry menu_items[] = {
      { _("/_File"),                  NULL, NULL,                  0, "<Branch>" },
      { _("/File/_Dump"),             NULL, run_dump,              0, NULL },
      { _("/File/_Reset counters"),   NULL, run_reset_counter,     0, NULL },
      { _("/File/Re_store counters"), NULL, run_restore_counter,   0, NULL },
      { _("/File/System GC"),         NULL, run_garbage_collector, 0, NULL },
      { _("/File/_Heapdump"),         NULL, run_heap_dumper,       0, NULL },
      { _("/File/_Monitors"),         NULL, run_monitor_dumper,    0, NULL },
      { _("/File/_Freeze UI"),        NULL, freeze_ui,             0, NULL },
      { _("/File/Threads"),           NULL, show_threads,          0, NULL },
      { _("/_Options"),               NULL, NULL,                  0, "<Branch>" },
      { _("/Options/Filter"),         NULL, filter_edit_options,   0, NULL },
      { _("/Options/Events"),         NULL, event_window,          0, NULL },
      { _("/Options/Visible Rows"),   NULL, set_visible_rows,      0, NULL }, 
      { _("/_Help"),                  NULL, NULL,                  0, "<LastBranch>" },
      { _("/_Help/About"),            NULL, about_jmp,             0, NULL },
    };

    GtkItemFactory *item_factory;
    GtkAccelGroup *accel_group;
    gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);


    JMP = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_signal_connect (GTK_OBJECT (JMP), "delete_event",
                  GTK_SIGNAL_FUNC (ignore_delete_event), NULL);

    gtk_window_set_title (GTK_WINDOW (JMP), _("Java Memory Profiler - Main"));
    vbox = gtk_vbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (JMP), vbox);
    
    /* build menues. */
    accel_group = gtk_accel_group_new ();
    item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", 
                               accel_group);
    gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
    gtk_window_add_accel_group (GTK_WINDOW (JMP), accel_group);
    menubar = gtk_item_factory_get_widget (item_factory, "<main>");
    gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
    
    /* build the button bar. */
    hbuttonbox = gtk_hbutton_box_new ();
    gtk_hbutton_box_set_spacing_default (0);
    gtk_box_pack_start (GTK_BOX (vbox), hbuttonbox, TRUE, TRUE, 0);

    add_button (_("_Dump"), _("Dump data to a text file"), 
            hbuttonbox, (GCallback*)run_dump);
    add_button (_("_Reset"), _("Reset counters to zero"), 
            hbuttonbox, (GCallback*)run_reset_counter);
    add_button (_("Re_store"), _("Restore counters to the standard (full) values"), 
            hbuttonbox, (GCallback*)run_restore_counter);
    add_button (_("System._GC"), _("Run the java virtual machines garbage collector"),
            hbuttonbox, (GCallback*)run_garbage_collector);
    add_button (_("_Heapdump"), _("Show the current heap"), 
            hbuttonbox, (GCallback*)run_heap_dumper);
    add_button (_("_Monitors"), _("Show the current monitors"), 
            hbuttonbox, (GCallback*)run_monitor_dumper);
    add_button (_("_Freeze ui"), _("Stop updating values in the JMP windows"),
            hbuttonbox, (GCallback*)freeze_ui);
    add_button (_("Threads"), _("Show the current threads"),
            hbuttonbox, (GCallback*)show_threads);
 
    /* add a heap size graph */
    heapgraph = get_heap_graph ();
    gtk_box_pack_start (GTK_BOX (vbox), heapgraph, TRUE, TRUE, 0);   

    /* add status bar. */
    statusbar = gtk_statusbar_new ();
    gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 0);

    return JMP;
}

void init_ui (void) {
    if (usingUI ()) {
      int argc = 1;
      char** argv;
      argv = malloc (sizeof (*argv));
      argv[0] = "jmp";  

      g_thread_init (NULL);
      gdk_threads_init ();
      gtk_init (&argc, &argv);

      free (argv);
    }
}


/** The thread used to run gtk-main. 
 */
void gtkthread (void* data) {
    /* avoid gtk if we are not using any graphical ui */
    if (usingUI ()) {
      if (tracing_objects ()) {
          setup_class_tracing ();
      }
      if (tracing_methods ()) {
          setup_method_tracing ();
      }
      
      tooltips = gtk_tooltips_new ();
      JMPmain = create_JMP ();
      gtk_widget_show_all (JMPmain);
      current_timer = g_timeout_add (1000, run_updates, NULL);
      init_done = 1;
      set_status (_("Ok"));
      gdk_threads_enter ();   
      gtk_main ();
      gdk_threads_leave ();
      ui_shutdown_complete ();
    } else {
      while (!quit) {
          sleep (1);
          run_updates (NULL);
      }
    }
}

void set_ui_update_interval (int seconds) {
    if (usingUI () && current_timer) {
      gtk_timeout_remove (current_timer);
      current_timer = gtk_timeout_add (seconds * 1000, run_updates, NULL);
    }
}

/** Tell the ui system to shut itself down. */
void quit_ui (void) {
    quit = 1;
    quit_class_window ();
    if (threadslist)
      free (threadslist);
    quit_method_window ();
    if (threadswin)
      destroy_threads_window (threadswin);
    filter_destroy ();
    set_visible_rows_destroy ();
    event_window_destroy ();
    gtk_widget_destroy (JMPmain);
    threadslist = NULL;
    quit = 2;
}

/** Count the number of threads that are currently active */
static void count_threads (void* data) {
    threads_count++;
}

/* Add a thread to the array of threads. */
static void add_threads_row (void* data) {
    jthread* t = (jthread*)data;
    /* do tests ? */
    threadslist[threads_count++] = t;
}

static int (*jthread_compr) (const void* v1, const void* v2) = jthread_compr_name;

/** Set the status text... */
void set_status (char* text) {
    set_status_internal (statusbar, text, 0);
}

static char status_buffer[400];

static gboolean set_status_later (gpointer text) {
    set_status_internal (statusbar, (char*)text, 0);
    return FALSE;
}

/** Set the status text... */
void set_status_lock (char* text) {
    /*set_status_internal (statusbar, text, 1);*/
    strncpy (status_buffer, text, sizeof(status_buffer) - 1);
    status_buffer[sizeof(status_buffer) - 1] = 0;
    g_idle_add (set_status_later, status_buffer);
}

/** Update the statistics. */
void updateUI (hashtab* classes, hashtab* methods) {
    if (!init_done)
      return;

    if (!is_updating)
      return;

    if (!quit && tracing_objects ()) {
      update_class_tree (classes);
    }

    if (!quit && tracing_methods ()) {
      update_method_tree (methods);
    }
      
    if (threadswin != NULL && GTK_WIDGET_VISIBLE (threadswin->window)) {
      update_threads_window (threadswin, get_threads ());
    }
}

/** Return the id of the dump file. */
int dump_data (char* dumpdir, hashtab* classes, hashtab* methods, hashtab* threads) {
    FILE* f;
    int i;
    int oldcount = 0;
    char filename[256];
    char status[128];
    snprintf (filename, 256, "%s/jmp_dump-%d.txt", 
            (dumpdir == NULL ? "." : dumpdir), ++dump_id);    
    snprintf (status, 128, _("dumped to: %s"), filename);
    set_status (status);
    f = fopen (filename, "w");
    if (f == NULL)
      return -1;

    fprintf (f, "Threads\n");
    fprintf (f, "thread\tgroup\tparent\tclass\tmethod\n");
    fprintf (f, "--------------------------------------------------------------\n");
    if (threads) {
      oldcount = threads_count;
      threads_count = 0;
      jmphash_for_each ((jmphash_iter_f)count_threads, threads);
      if (oldcount != threads_count) 
          threadslist = realloc (threadslist, threads_count * sizeof(jthread*));
      threads_count = 0;
      jmphash_for_each ((jmphash_iter_f)add_threads_row, threads);
      qsort(threadslist, threads_count, sizeof(jthread*), jthread_compr);
      for (i = 0; i < threads_count; i++){
          dump_jthread_row (threadslist[i], f);
      }
    } else {
      fprintf (stderr, "threads hash is NULL, wont dump it\n");
    }

    dump_classes (classes, f);
    dump_methods (methods, f);

    fflush (f);
    fclose (f);
    return dump_id;
}

/** Set the dump timer... */
void set_dump_timer (int dt) {
    dumptimer = dt;
}

/** Get the current dump timer. */
int get_dump_timer () {
    return dumptimer;
}

/** Check if we have any ui-handling to do. */
int events_pending (void) {
    return gtk_events_pending ();
}

/** Update the ui-toolkit (gtk_main_iteration ()) */
int ui_iteration (void) {
    return gtk_main_iteration ();
}

/** check if a threads window is showing. This info is needed to 
 *  decide if we should lock the timer stacks.
 */
int is_threads_window_showing () {
    return threadswin != NULL && 
      GTK_WIDGET_VISIBLE (threadswin->window);
}

/** Show deadlock */
void show_deadlock (visited_threads* vt) {
   GtkWidget *dialog, *label;
   char buf[300];
   dialog = gtk_dialog_new_with_buttons (_("Deadlock detected"),
                                         GTK_WINDOW (JMPmain),
                                         GTK_DIALOG_DESTROY_WITH_PARENT,
                                         _("OK"),
                                         GTK_RESPONSE_NONE,
                                         NULL);

   label = gtk_label_new (_("Warning deadlock detected!\n"));  
   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label);
   while (vt != NULL) {
       if (vt->next) 
         snprintf (buf, 300, _("%s holding %s (%p), is trying to enter %s (%p)"), 
                 jthread_get_thread_name (vt->mi->owner), 
                 vt->mi->name, vt->mi->id, 
                 vt->next->mi->name, vt->next->mi->id);
       else {
         snprintf (buf, 300, _("%s holding %s (%p)"), 
                 jthread_get_thread_name (vt->mi->owner), 
                 vt->mi->name, vt->mi->id); 
       }
       vt = vt->next;
       label = gtk_label_new (buf);  
       gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label);
   }    
   
   g_signal_connect_swapped (GTK_OBJECT (dialog), 
                             "response", 
                             G_CALLBACK (gtk_widget_destroy),
                             GTK_OBJECT (dialog));
   gtk_widget_show_all (dialog);
}

char* jmp_utf16_to_utf8 (unsigned char* text, size_t count, long* items_written, int* err) {
    GError* gerr = NULL;
    glong iw;
    glong ir;
    char* ret;
    
    ret = g_utf16_to_utf8 ((gunichar2*)text, count, &ir, &iw, &gerr);
    *items_written = iw;
    return ret;
}

/* 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