handle errors better
[oweals/gnunet.git] / src / util / scheduler.c
index dfc7510481e5368235da1acaf1ca9af61925a670..1f77d3a6845fc52ade187c5540fbb601425fe2d7 100644 (file)
  */
 #include "platform.h"
 #include "gnunet_common.h"
+#include "gnunet_os_lib.h"
 #include "gnunet_scheduler_lib.h"
 #include "gnunet_signal_lib.h"
 #include "gnunet_time_lib.h"
+#ifdef LINUX
+#include "execinfo.h"
+/**
+ * Obtain trace information for all scheduler calls that schedule tasks.
+ */
+#define EXECINFO GNUNET_NO
+
+/**
+ * Depth of the traces collected via EXECINFO.
+ */
+#define MAX_TRACE_DEPTH 50
+#endif
 
 #define DEBUG_TASKS GNUNET_NO
 
+/**
+ * Should we figure out which tasks are delayed for a while
+ * before they are run? (Consider using in combination with EXECINFO).
+ */
+#define PROFILE_DELAYS GNUNET_NO
+
+/**
+ * Task that were in the queue for longer than this are reported if
+ * PROFILE_DELAYS is active.
+ */
+#define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
+
 /**
  * Linked list of pending tasks.
  */
@@ -82,6 +107,13 @@ struct Task
    */
   struct GNUNET_TIME_Absolute timeout;
 
+#if PROFILE_DELAYS
+  /**
+   * When was the task scheduled?
+   */
+  struct GNUNET_TIME_Absolute start_time;
+#endif
+
   /**
    * Why is the task ready?  Set after task is added to ready queue.
    * Initially set to zero.  All reasons that have already been
@@ -94,6 +126,19 @@ struct Task
    */
   enum GNUNET_SCHEDULER_Priority priority;
 
+#if EXECINFO
+  /**
+   * Array of strings which make up a backtrace from the point when this
+   * task was scheduled (essentially, who scheduled the task?)
+   */
+  char **backtrace_strings;
+
+  /**
+   * Size of the backtrace_strings array
+   */
+  int num_backtrace_strings;
+#endif
+
 };
 
 
@@ -149,6 +194,11 @@ struct GNUNET_SCHEDULER_Handle
    */
   enum GNUNET_SCHEDULER_Priority current_priority;
 
+  /**
+   * How 'nice' are we right now?
+   */
+  int nice_level;
+
 };
 
 
@@ -438,6 +488,9 @@ destroy_task (struct Task *t)
     GNUNET_NETWORK_fdset_destroy (t->read_set);
   if (NULL != t->write_set)
     GNUNET_NETWORK_fdset_destroy (t->write_set);
+#if EXECINFO
+  GNUNET_free (t->backtrace_strings);
+#endif
   GNUNET_free (t);
 }
 
@@ -474,9 +527,31 @@ run_ready (struct GNUNET_SCHEDULER_Handle *sched)
       GNUNET_assert (pos != NULL);      /* ready_count wrong? */
       sched->ready[p] = pos->next;
       sched->ready_count--;
-      sched->current_priority = pos->priority;
-      GNUNET_assert (pos->priority == p);
+      if (sched->current_priority != pos->priority)
+       {
+         sched->current_priority = pos->priority;
+         (void) GNUNET_OS_set_process_priority (0, pos->priority);
+       }
       sched->active_task = pos;
+#if PROFILE_DELAYS
+      if (GNUNET_TIME_absolute_get_duration (pos->start_time).value >
+         DELAY_THRESHOLD.value)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     "Task %u took %llums to be scheduled\n",
+                     pos->id,
+                     (unsigned long long) GNUNET_TIME_absolute_get_duration (pos->start_time).value);
+#if EXECINFO
+         int i;
+         for (i=0;i<pos->num_backtrace_strings;i++)
+           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                       "Task %u trace %d: %s\n",
+                       pos->id,
+                       i,
+                       pos->backtrace_strings[i]);
+#endif
+       }
+#endif
       tc.sched = sched;
       tc.reason = pos->reason;
       tc.read_ready = pos->read_set;
@@ -583,7 +658,8 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
           if (errno == EINTR)
             continue;
           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
-          break;
+          abort ();
+         break;
         }
       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
         {
@@ -750,11 +826,20 @@ GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
                                    enum GNUNET_SCHEDULER_Reason reason)
 {
   struct Task *t;
-
+#if EXECINFO
+  void *backtrace_array[50];
+#endif
   t = GNUNET_malloc (sizeof (struct Task));
+#if EXECINFO
+  t->num_backtrace_strings = backtrace(backtrace_array, 50);
+  t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
+#endif
   t->callback = task;
   t->callback_cls = task_cls;
   t->id = ++sched->last_id;
+#if PROFILE_DELAYS
+  t->start_time = GNUNET_TIME_absolute_get ();
+#endif
   t->reason = reason;
   t->priority = sched->current_priority;
 #if DEBUG_TASKS
@@ -1069,10 +1154,18 @@ GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
                              GNUNET_SCHEDULER_Task task, void *task_cls)
 {
   struct Task *t;
+#if EXECINFO
+  void *backtrace_array[MAX_TRACE_DEPTH];
+#endif
 
+  GNUNET_assert (NULL != task);
   t = GNUNET_malloc (sizeof (struct Task));
   t->callback = task;
   t->callback_cls = task_cls;
+#if EXECINFO
+  t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
+  t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
+#endif
   if (rs != NULL)
     {
       t->read_set = GNUNET_NETWORK_fdset_create ();
@@ -1084,6 +1177,9 @@ GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
     }
   t->id = ++sched->last_id;
+#if PROFILE_DELAYS
+  t->start_time = GNUNET_TIME_absolute_get ();
+#endif
   t->prereq_id = prerequisite_task;
   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
   t->priority =