kallsyms: Improve the performance of kallsyms_lookup_name()
Currently, to search for a symbol, we need to expand the symbols in 'kallsyms_names' one by one, and then use the expanded string for comparison. It's O(n). If we sort names in ascending order like addresses, we can also use binary search. It's O(log(n)). In order not to change the implementation of "/proc/kallsyms", the table kallsyms_names[] is still stored in a one-to-one correspondence with the address in ascending order. Add array kallsyms_seqs_of_names[], it's indexed by the sequence number of the sorted names, and the corresponding content is the sequence number of the sorted addresses. For example: Assume that the index of NameX in array kallsyms_seqs_of_names[] is 'i', the content of kallsyms_seqs_of_names[i] is 'k', then the corresponding address of NameX is kallsyms_addresses[k]. The offset in kallsyms_names[] is get_symbol_offset(k). Note that the memory usage will increase by (4 * kallsyms_num_syms) bytes, the next two patches will reduce (1 * kallsyms_num_syms) bytes and properly handle the case CONFIG_LTO_CLANG=y. Performance test results: (x86) Before: min=234, max=10364402, avg=5206926 min=267, max=11168517, avg=5207587 After: min=1016, max=90894, avg=7272 min=1014, max=93470, avg=7293 The average lookup performance of kallsyms_lookup_name() improved 715x. Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com> Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
This commit is contained in:
parent
fa04aad614
commit
9fdbd3eed2
2 changed files with 113 additions and 11 deletions
|
@ -49,6 +49,7 @@ extern const char kallsyms_token_table[] __weak;
|
||||||
extern const u16 kallsyms_token_index[] __weak;
|
extern const u16 kallsyms_token_index[] __weak;
|
||||||
|
|
||||||
extern const unsigned int kallsyms_markers[] __weak;
|
extern const unsigned int kallsyms_markers[] __weak;
|
||||||
|
extern const unsigned int kallsyms_seqs_of_names[] __weak;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expand a compressed symbol data into the resulting uncompressed string,
|
* Expand a compressed symbol data into the resulting uncompressed string,
|
||||||
|
@ -181,22 +182,86 @@ static inline char *cleanup_symbol_name(char *s)
|
||||||
static inline char *cleanup_symbol_name(char *s) { return NULL; }
|
static inline char *cleanup_symbol_name(char *s) { return NULL; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
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. */
|
/* Lookup the address for this symbol. Returns 0 if not found. */
|
||||||
unsigned long kallsyms_lookup_name(const char *name)
|
unsigned long kallsyms_lookup_name(const char *name)
|
||||||
{
|
{
|
||||||
char namebuf[KSYM_NAME_LEN];
|
int ret;
|
||||||
unsigned long i;
|
unsigned int i;
|
||||||
unsigned int off;
|
|
||||||
|
|
||||||
for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
|
ret = kallsyms_lookup_names(name, &i, NULL);
|
||||||
off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
|
if (!ret)
|
||||||
|
return kallsyms_sym_address(kallsyms_seqs_of_names[i]);
|
||||||
|
|
||||||
if (strcmp(namebuf, name) == 0)
|
|
||||||
return kallsyms_sym_address(i);
|
|
||||||
|
|
||||||
if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0)
|
|
||||||
return kallsyms_sym_address(i);
|
|
||||||
}
|
|
||||||
return module_kallsyms_lookup_name(name);
|
return module_kallsyms_lookup_name(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
struct sym_entry {
|
struct sym_entry {
|
||||||
unsigned long long addr;
|
unsigned long long addr;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
|
unsigned int seq;
|
||||||
unsigned int start_pos;
|
unsigned int start_pos;
|
||||||
unsigned int percpu_absolute;
|
unsigned int percpu_absolute;
|
||||||
unsigned char sym[];
|
unsigned char sym[];
|
||||||
|
@ -384,6 +385,35 @@ static int symbol_absolute(const struct sym_entry *s)
|
||||||
return s->percpu_absolute;
|
return s->percpu_absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int compare_names(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char sa_namebuf[KSYM_NAME_LEN];
|
||||||
|
char sb_namebuf[KSYM_NAME_LEN];
|
||||||
|
const struct sym_entry *sa = *(const struct sym_entry **)a;
|
||||||
|
const struct sym_entry *sb = *(const struct sym_entry **)b;
|
||||||
|
|
||||||
|
expand_symbol(sa->sym, sa->len, sa_namebuf);
|
||||||
|
expand_symbol(sb->sym, sb->len, sb_namebuf);
|
||||||
|
ret = strcmp(&sa_namebuf[1], &sb_namebuf[1]);
|
||||||
|
if (!ret) {
|
||||||
|
if (sa->addr > sb->addr)
|
||||||
|
return 1;
|
||||||
|
else if (sa->addr < sb->addr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* keep old order */
|
||||||
|
return (int)(sa->seq - sb->seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sort_symbols_by_name(void)
|
||||||
|
{
|
||||||
|
qsort(table, table_cnt, sizeof(table[0]), compare_names);
|
||||||
|
}
|
||||||
|
|
||||||
static void write_src(void)
|
static void write_src(void)
|
||||||
{
|
{
|
||||||
unsigned int i, k, off;
|
unsigned int i, k, off;
|
||||||
|
@ -469,6 +499,7 @@ static void write_src(void)
|
||||||
for (i = 0; i < table_cnt; i++) {
|
for (i = 0; i < table_cnt; i++) {
|
||||||
if ((i & 0xFF) == 0)
|
if ((i & 0xFF) == 0)
|
||||||
markers[i >> 8] = off;
|
markers[i >> 8] = off;
|
||||||
|
table[i]->seq = i;
|
||||||
|
|
||||||
printf("\t.byte 0x%02x", table[i]->len);
|
printf("\t.byte 0x%02x", table[i]->len);
|
||||||
for (k = 0; k < table[i]->len; k++)
|
for (k = 0; k < table[i]->len; k++)
|
||||||
|
@ -486,6 +517,12 @@ static void write_src(void)
|
||||||
|
|
||||||
free(markers);
|
free(markers);
|
||||||
|
|
||||||
|
sort_symbols_by_name();
|
||||||
|
output_label("kallsyms_seqs_of_names");
|
||||||
|
for (i = 0; i < table_cnt; i++)
|
||||||
|
printf("\t.long\t%u\n", table[i]->seq);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
output_label("kallsyms_token_table");
|
output_label("kallsyms_token_table");
|
||||||
off = 0;
|
off = 0;
|
||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
|
|
Loading…
Reference in a new issue