kallsyms: Improve the performance of kallsyms_lookup_name()
[linux-2.6-block.git] / kernel / kallsyms.c
index 60c20f301a6ba2c794c7716c87e7bde61de196e7..ba351dfa109b6acea5b8238d8538140027db41a8 100644 (file)
@@ -187,26 +187,90 @@ static bool cleanup_symbol_name(char *s)
        return false;
 }
 
+static int compare_symbol_name(const char *name, char *namebuf)
+{
+       int ret;
+
+       ret = strcmp(name, namebuf);
+       if (!ret)
+               return ret;
+
+       if (cleanup_symbol_name(namebuf) && !strcmp(name, namebuf))
+               return 0;
+
+       return ret;
+}
+
+static int kallsyms_lookup_names(const char *name,
+                                unsigned int *start,
+                                unsigned int *end)
+{
+       int ret;
+       int low, mid, high;
+       unsigned int seq, off;
+       char namebuf[KSYM_NAME_LEN];
+
+       low = 0;
+       high = kallsyms_num_syms - 1;
+
+       while (low <= high) {
+               mid = low + (high - low) / 2;
+               seq = kallsyms_seqs_of_names[mid];
+               off = get_symbol_offset(seq);
+               kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
+               ret = compare_symbol_name(name, namebuf);
+               if (ret > 0)
+                       low = mid + 1;
+               else if (ret < 0)
+                       high = mid - 1;
+               else
+                       break;
+       }
+
+       if (low > high)
+               return -ESRCH;
+
+       low = mid;
+       while (low) {
+               seq = kallsyms_seqs_of_names[low - 1];
+               off = get_symbol_offset(seq);
+               kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
+               if (compare_symbol_name(name, namebuf))
+                       break;
+               low--;
+       }
+       *start = low;
+
+       if (end) {
+               high = mid;
+               while (high < kallsyms_num_syms - 1) {
+                       seq = kallsyms_seqs_of_names[high + 1];
+                       off = get_symbol_offset(seq);
+                       kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
+                       if (compare_symbol_name(name, namebuf))
+                               break;
+                       high++;
+               }
+               *end = high;
+       }
+
+       return 0;
+}
+
 /* Lookup the address for this symbol. Returns 0 if not found. */
 unsigned long kallsyms_lookup_name(const char *name)
 {
-       char namebuf[KSYM_NAME_LEN];
-       unsigned long i;
-       unsigned int off;
+       int ret;
+       unsigned int i;
 
        /* Skip the search for empty string. */
        if (!*name)
                return 0;
 
-       for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
-               off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
-
-               if (strcmp(namebuf, name) == 0)
-                       return kallsyms_sym_address(i);
+       ret = kallsyms_lookup_names(name, &i, NULL);
+       if (!ret)
+               return kallsyms_sym_address(kallsyms_seqs_of_names[i]);
 
-               if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0)
-                       return kallsyms_sym_address(i);
-       }
        return module_kallsyms_lookup_name(name);
 }