Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] Do not waste time calling libunwind get_proc_name in the hot spots
authorGabriel Corona <gabriel.corona@loria.fr>
Tue, 18 Feb 2014 10:38:14 +0000 (11:38 +0100)
committerGabriel Corona <gabriel.corona@loria.fr>
Tue, 18 Feb 2014 15:00:10 +0000 (16:00 +0100)
In typical executions, nearly 50% of the time was spent in libunwind
get_proc_name.

The algorithm to find the function for a given IP (instruction
pointer) was:

  (proc_name, offset) = get_proc_name(ip) // Slow!
  dwarf_ip = ip - offset
  function = functions_by_name[proc_name]

We added a structure mapping IP ranges to functions and the algorithm
is now:

  function = functions_by_ip[ip]

Instead of relying on libunwind, we use the DWARF information to find
the corresponding DWARF TAG_subprogram DIEs directly.

The secution time on some MPICH tests is nearly halved.

Notes:

 * It was necessary to disable the support for inlined_subprograms
   which was broken anyway: the inlined_subprogram entries should be
   stored as children of their parent subprogram (as a block). We need
   to add support for scope blocks inside a suprogram to handle this
   correctly.

 * Currently the translation between process virtual addresses and
   DWARF virtual addresses is handled in many different places. We
   should change this to process it only when parsing the DWARF DIEs
   and be done with it.

src/mc/mc_checkpoint.c
src/mc/mc_dwarf.c
src/mc/mc_global.c
src/mc/mc_private.h

index 260728c..6f6825a 100644 (file)
@@ -27,8 +27,6 @@ XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_checkpoint, mc,
 
 char *libsimgrid_path;
 
-static void MC_find_object_address(memory_map_t maps, mc_object_info_t result);
-
 /************************************  Free functions **************************************/
 /*****************************************************************************************/
 
@@ -174,7 +172,7 @@ static void MC_resolve_subtype(mc_object_info_t info, dw_type_t type) {
   // TODO, support "switch type" (looking up the type in another lib) when possible
 }
 
-static void MC_post_process_types(mc_object_info_t info) {
+void MC_post_process_types(mc_object_info_t info) {
   xbt_dict_cursor_t cursor = NULL;
   char *origin;
   dw_type_t type;
@@ -191,18 +189,8 @@ static void MC_post_process_types(mc_object_info_t info) {
   }
 }
 
-/** \brief Finds informations about a given shared object/executable */
-mc_object_info_t MC_find_object_info(memory_map_t maps, char* name) {
-  mc_object_info_t result = MC_new_object_info();
-  result->file_name = xbt_strdup(name);
-  MC_find_object_address(maps, result);
-  MC_dwarf_get_variables(result);
-  MC_post_process_types(result);
-  return result;
-}
-
 /** \brief Fills the position of the .bss and .data sections. */
-static void MC_find_object_address(memory_map_t maps, mc_object_info_t result) {
+void MC_find_object_address(memory_map_t maps, mc_object_info_t result) {
 
   unsigned int i = 0;
   s_map_region_t reg;
@@ -304,7 +292,8 @@ static xbt_dynar_t MC_unwind_stack_frames(void *stack_context) {
 
   unw_cursor_t c;
 
-  char frame_name[256];
+  dw_frame_t test = MC_find_function_by_ip(&MC_unwind_stack_frames);
+  xbt_assert(test);
 
   int ret;
   for(ret = unw_init_local(&c, (unw_context_t *)stack_context); ret >= 0; ret = unw_step(&c)){
@@ -313,29 +302,42 @@ static xbt_dynar_t MC_unwind_stack_frames(void *stack_context) {
 
     stack_frame->unw_cursor = c;
 
-    unw_get_reg(&c, UNW_REG_IP, &stack_frame->ip);
-    unw_get_reg(&c, UNW_REG_SP, &stack_frame->sp);
+    unw_word_t ip, sp;
+
+    unw_get_reg(&c, UNW_REG_IP, &ip);
+    unw_get_reg(&c, UNW_REG_SP, &sp);
 
-    unw_word_t off;
-    unw_get_proc_name(&c, frame_name, sizeof (frame_name), &off);
-    stack_frame->frame_name = xbt_strdup(frame_name);
+    stack_frame->ip = ip;
+    stack_frame->sp = sp;
 
-    dw_frame_t frame;
-    if((long)stack_frame->ip > (long) mc_libsimgrid_info->start_exec)
-      frame = xbt_dict_get_or_null(mc_libsimgrid_info->local_variables, frame_name);
-    else
-      frame = xbt_dict_get_or_null(mc_binary_info->local_variables, frame_name);
+    // TODO, use real addresses in frame_t instead of fixing it here
+
+    dw_frame_t frame = MC_find_function_by_ip((void*) ip);
     stack_frame->frame = frame;
 
-    if(frame != NULL){
-      unw_word_t normalized_ip = (unw_word_t)frame->low_pc + (unw_word_t)off;
+    if(frame) {
+      stack_frame->frame_name = xbt_strdup(frame->name);
+
+      mc_object_info_t info = MC_ip_find_object_info((void*)ip);
+
+      // This is the instruction pointer as present in the DWARF of the object:
+      // Relocated addresses are offset for shared objets and constant for executables objects.
+      // See DWARF4 spec 7.5
+      unw_word_t normalized_ip;
+      if(info->flags & MC_OBJECT_INFO_EXECUTABLE) {
+        normalized_ip = ip;
+      } else {
+        void* base = MC_object_base_address(info);
+        normalized_ip = (unw_word_t) ip - (unw_word_t) base;
+      }
+
       stack_frame->frame_base = (unw_word_t)mc_find_frame_base(normalized_ip, frame, &c);
     } else {
       stack_frame->frame_base = 0;
     }
 
     /* Stop before context switch with maestro */
-    if(!strcmp(frame_name, "smx_ctx_sysv_wrapper"))
+    if(frame!=NULL && frame->name!=NULL && !strcmp(frame->name, "smx_ctx_sysv_wrapper"))
       break;
   }
 
index 57b348c..5a371c9 100644 (file)
@@ -928,7 +928,6 @@ static void MC_dwarf_handle_die(mc_object_info_t info, Dwarf_Die* die, Dwarf_Die
     case DW_TAG_shared_type:
       MC_dwarf_handle_type_die(info, die, unit);
       break;
-    case DW_TAG_inlined_subroutine:
     case DW_TAG_subprogram:
       MC_dwarf_handle_subprogram_die(info, die, unit, frame);
       return;
index 78049ad..2b56b16 100644 (file)
@@ -179,7 +179,7 @@ void dw_variable_free_voidp(void *t){
   dw_variable_free((dw_variable_t) * (void **) t);
 }
 
-// object_info
+// ***** object_info
 
 mc_object_info_t MC_new_object_info(void) {
   mc_object_info_t res = xbt_new0(s_mc_object_info_t, 1);
@@ -190,6 +190,7 @@ mc_object_info_t MC_new_object_info(void) {
   return res;
 }
 
+
 void MC_free_object_info(mc_object_info_t* info) {
   xbt_free(&(*info)->file_name);
   xbt_dict_free(&(*info)->local_variables);
@@ -197,9 +198,115 @@ void MC_free_object_info(mc_object_info_t* info) {
   xbt_dict_free(&(*info)->types);
   xbt_dict_free(&(*info)->types_by_name);
   xbt_free(info);
+  xbt_dynar_free(&(*info)->functions_index);
   *info = NULL;
 }
 
+// ***** Helpers
+
+void* MC_object_base_address(mc_object_info_t info) {
+  void* result = info->start_exec;
+  if(info->start_rw!=NULL && result > (void*) info->start_rw) result = info->start_rw;
+  if(info->start_ro!=NULL && result > (void*) info->start_ro) result = info->start_ro;
+  return result;
+}
+
+// ***** Functions index
+
+static int MC_compare_frame_index_items(mc_function_index_item_t a, mc_function_index_item_t b) {
+  if(a->low_pc < b->low_pc)
+    return -1;
+  else if(a->low_pc == b->low_pc)
+    return 0;
+  else
+    return 1;
+}
+
+static void MC_make_functions_index(mc_object_info_t info) {
+  xbt_dynar_t index = xbt_dynar_new(sizeof(s_mc_function_index_item_t), NULL);
+
+  // The base address of the function must be used to offset the addresses.
+  // This should be fixed this in the frame_t structure instead.
+  // Relocated addresses are offset for shared objets and constant for executables objects.
+  // See DWARF4 spec 7.5
+  void* offset = info->flags & MC_OBJECT_INFO_EXECUTABLE ? 0 : MC_object_base_address(info);
+
+  // Populate the array:
+  dw_frame_t frame = NULL;
+  xbt_dict_cursor_t cursor = NULL;
+  const char* name = NULL;
+  xbt_dict_foreach(info->local_variables, cursor, name, frame) {
+    if(frame->low_pc==NULL)
+      continue;
+    s_mc_function_index_item_t entry;
+    entry.low_pc = (char*) frame->low_pc + (unsigned long) offset;
+    entry.high_pc = (char*) frame->high_pc + (unsigned long) offset;
+    entry.function = frame;
+    xbt_dynar_push(index, &entry);
+  }
+
+  mc_function_index_item_t base = (mc_function_index_item_t) xbt_dynar_get_ptr(index, 0);
+
+  // Sort the array by low_pc:
+  qsort(base,
+    xbt_dynar_length(index),
+    sizeof(s_mc_function_index_item_t),
+    (int (*)(const void *, const void *))MC_compare_frame_index_items);
+
+  info->functions_index = index;
+}
+
+mc_object_info_t MC_ip_find_object_info(void* ip) {
+  mc_object_info_t infos[2] = { mc_binary_info, mc_libsimgrid_info };
+  size_t n = 2;
+  size_t i;
+  for(i=0; i!=n; ++i) {
+    if(ip >= (void*)infos[i]->start_exec && ip <= (void*)infos[i]->end_exec) {
+      return infos[i];
+    }
+  }
+  return NULL;
+}
+
+static dw_frame_t MC_find_function_by_ip_and_object(void* ip, mc_object_info_t info) {
+  xbt_dynar_t dynar = info->functions_index;
+  mc_function_index_item_t base = (mc_function_index_item_t) xbt_dynar_get_ptr(dynar, 0);
+  int i = 0;
+  int j = xbt_dynar_length(dynar) - 1;
+  while(j>=i) {
+    int k = i + ((j-i)/2);
+    if(ip < base[k].low_pc) {
+      j = k-1;
+    } else if(ip > base[k].high_pc) {
+      i = k+1;
+    } else {
+      return base[k].function;
+    }
+  }
+  return NULL;
+}
+
+dw_frame_t MC_find_function_by_ip(void* ip) {
+  mc_object_info_t info = MC_ip_find_object_info(ip);
+  if(info==NULL)
+    return NULL;
+  else
+    return MC_find_function_by_ip_and_object(ip, info);
+}
+
+/** \brief Finds informations about a given shared object/executable */
+mc_object_info_t MC_find_object_info(memory_map_t maps, char* name, int executable) {
+  mc_object_info_t result = MC_new_object_info();
+  if(executable)
+    result->flags |= MC_OBJECT_INFO_EXECUTABLE;
+  result->file_name = xbt_strdup(name);
+  MC_find_object_address(maps, result);
+  MC_dwarf_get_variables(result);
+  MC_post_process_types(result);
+  MC_make_functions_index(result);
+  return result;
+}
+
 /*************************************************************************/
 
 static dw_location_t MC_dwarf_get_location(xbt_dict_t location_list, char *expr){
@@ -775,8 +882,8 @@ static void MC_init_debug_info() {
   memory_map_t maps = MC_get_memory_map();
 
   /* Get local variables for state equality detection */
-  mc_binary_info = MC_find_object_info(maps, xbt_binary_name);
-  mc_libsimgrid_info = MC_find_object_info(maps, libsimgrid_path);
+  mc_binary_info = MC_find_object_info(maps, xbt_binary_name, 1);
+  mc_libsimgrid_info = MC_find_object_info(maps, libsimgrid_path, 0);
 
   MC_free_memory_map(maps);
   XBT_INFO("Get debug information done !");
index e7a2ce2..12783b6 100644 (file)
@@ -29,6 +29,7 @@
 #include "xbt/parmap.h"
 
 typedef struct s_dw_frame s_dw_frame_t, *dw_frame_t;
+typedef struct s_mc_function_index_item s_mc_function_index_item_t, *mc_function_index_item_t;
 
 /****************************** Snapshots ***********************************/
 
@@ -332,7 +333,10 @@ void MC_dump_stack_liveness(xbt_fifo_t stack);
 
 /********************************** Variables with DWARF **********************************/
 
+#define MC_OBJECT_INFO_EXECUTABLE 1
+
 struct s_mc_object_info {
+  size_t flags;
   char* file_name;
   char *start_exec, *end_exec; // Executable segment
   char *start_rw, *end_rw; // Read-write segment
@@ -341,10 +345,15 @@ struct s_mc_object_info {
   xbt_dynar_t global_variables; // xbt_dynar_t<dw_variable_t>
   xbt_dict_t types; // xbt_dict_t<origin as hexadecimal string, dw_type_t>
   xbt_dict_t types_by_name; // xbt_dict_t<name, dw_type_t> (full defined type only)
+
+  // Here we sort the minimal information for an efficient (and cache-efficient)
+  // lookup of a function given an instruction pointer.
+  // The entries are sorted by low_pc and a binary search can be used to look them up.
+  xbt_dynar_t functions_index;
 };
 
 mc_object_info_t MC_new_object_info(void);
-mc_object_info_t MC_find_object_info(memory_map_t maps, char* name);
+mc_object_info_t MC_find_object_info(memory_map_t maps, char* name, int executable);
 void MC_free_object_info(mc_object_info_t* p);
 
 void MC_dwarf_get_variables(mc_object_info_t info);
@@ -352,9 +361,15 @@ void MC_dwarf_get_variables_libdw(mc_object_info_t info);
 const char* MC_dwarf_attrname(int attr);
 const char* MC_dwarf_tagname(int tag);
 
+dw_frame_t MC_find_function_by_ip(void* ip);
+mc_object_info_t MC_ip_find_object_info(void* ip);
+
 extern mc_object_info_t mc_libsimgrid_info;
 extern mc_object_info_t mc_binary_info;
 
+void MC_find_object_address(memory_map_t maps, mc_object_info_t result);
+void MC_post_process_types(mc_object_info_t info);
+
 typedef enum {
   e_dw_loclist,
   e_dw_register,
@@ -439,6 +454,11 @@ struct s_dw_frame{
   unsigned long int end;   /* Dwarf offset of the next sibling */
 };
 
+struct s_mc_function_index_item {
+  void* low_pc, *high_pc;
+  dw_frame_t function;
+};
+
 void dw_type_free(dw_type_t t);
 void dw_variable_free(dw_variable_t v);
 void dw_variable_free_voidp(void *t);
@@ -447,6 +467,7 @@ void MC_dwarf_register_global_variable(mc_object_info_t info, dw_variable_t vari
 void MC_register_variable(mc_object_info_t info, dw_frame_t frame, dw_variable_t variable);
 void MC_dwarf_register_non_global_variable(mc_object_info_t info, dw_frame_t frame, dw_variable_t variable);
 void MC_dwarf_register_variable(mc_object_info_t info, dw_frame_t frame, dw_variable_t variable);
+void* MC_object_base_address(mc_object_info_t info);
 
 /********************************** DWARF **********************************/