diff options
| author | Josh Poimboeuf <jpoimboe@redhat.com> | 2016-03-09 01:07:00 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2016-03-09 04:48:10 -0500 |
| commit | 042ba73fe7eb63872ee2d6ac86410052210c1f16 (patch) | |
| tree | 6f3efe312fc1f2716f0f477a5860a3b6aed3e351 /tools/objtool | |
| parent | 1698872b5c772aebc5c43ca445cc0a79f12b9fcc (diff) | |
objtool: Add several performance improvements
Use hash tables for instruction and rela lookups (and keep the linked
lists around for sequential access).
Also cache the section struct for the "__func_stack_frame_non_standard"
section.
With this change, "objtool check net/wireless/nl80211.o" goes from:
real 0m1.168s
user 0m1.163s
sys 0m0.005s
to:
real 0m0.059s
user 0m0.042s
sys 0m0.017s
for a 20x speedup.
With the same object, it should be noted that the memory heap usage grew
from 8MB to 62MB. Reducing the memory usage is on the TODO list.
Reported-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Bernd Petrovitsch <bernd@petrovitsch.priv.at>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Chris J Arges <chris.j.arges@canonical.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Michal Marek <mmarek@suse.cz>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Pedro Alves <palves@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: live-patching@vger.kernel.org
Link: http://lkml.kernel.org/r/dd0d8e1449506cfa7701b4e7ba73577077c44253.1457502970.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/objtool')
| -rw-r--r-- | tools/objtool/builtin-check.c | 18 | ||||
| -rw-r--r-- | tools/objtool/elf.c | 21 | ||||
| -rw-r--r-- | tools/objtool/elf.h | 10 |
3 files changed, 35 insertions, 14 deletions
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index cf1e48dbfa97..bfeee227aaab 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c | |||
| @@ -34,6 +34,8 @@ | |||
| 34 | #include "arch.h" | 34 | #include "arch.h" |
| 35 | #include "warn.h" | 35 | #include "warn.h" |
| 36 | 36 | ||
| 37 | #include <linux/hashtable.h> | ||
| 38 | |||
| 37 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | 39 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
| 38 | 40 | ||
| 39 | #define STATE_FP_SAVED 0x1 | 41 | #define STATE_FP_SAVED 0x1 |
| @@ -42,6 +44,7 @@ | |||
| 42 | 44 | ||
| 43 | struct instruction { | 45 | struct instruction { |
| 44 | struct list_head list; | 46 | struct list_head list; |
| 47 | struct hlist_node hash; | ||
| 45 | struct section *sec; | 48 | struct section *sec; |
| 46 | unsigned long offset; | 49 | unsigned long offset; |
| 47 | unsigned int len, state; | 50 | unsigned int len, state; |
| @@ -61,7 +64,8 @@ struct alternative { | |||
| 61 | struct objtool_file { | 64 | struct objtool_file { |
| 62 | struct elf *elf; | 65 | struct elf *elf; |
| 63 | struct list_head insn_list; | 66 | struct list_head insn_list; |
| 64 | struct section *rodata; | 67 | DECLARE_HASHTABLE(insn_hash, 16); |
| 68 | struct section *rodata, *whitelist; | ||
| 65 | }; | 69 | }; |
| 66 | 70 | ||
| 67 | const char *objname; | 71 | const char *objname; |
| @@ -72,7 +76,7 @@ static struct instruction *find_insn(struct objtool_file *file, | |||
| 72 | { | 76 | { |
| 73 | struct instruction *insn; | 77 | struct instruction *insn; |
| 74 | 78 | ||
| 75 | list_for_each_entry(insn, &file->insn_list, list) | 79 | hash_for_each_possible(file->insn_hash, insn, hash, offset) |
| 76 | if (insn->sec == sec && insn->offset == offset) | 80 | if (insn->sec == sec && insn->offset == offset) |
| 77 | return insn; | 81 | return insn; |
| 78 | 82 | ||
| @@ -111,14 +115,12 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file, | |||
| 111 | */ | 115 | */ |
| 112 | static bool ignore_func(struct objtool_file *file, struct symbol *func) | 116 | static bool ignore_func(struct objtool_file *file, struct symbol *func) |
| 113 | { | 117 | { |
| 114 | struct section *macro_sec; | ||
| 115 | struct rela *rela; | 118 | struct rela *rela; |
| 116 | struct instruction *insn; | 119 | struct instruction *insn; |
| 117 | 120 | ||
| 118 | /* check for STACK_FRAME_NON_STANDARD */ | 121 | /* check for STACK_FRAME_NON_STANDARD */ |
| 119 | macro_sec = find_section_by_name(file->elf, "__func_stack_frame_non_standard"); | 122 | if (file->whitelist && file->whitelist->rela) |
| 120 | if (macro_sec && macro_sec->rela) | 123 | list_for_each_entry(rela, &file->whitelist->rela->rela_list, list) |
| 121 | list_for_each_entry(rela, ¯o_sec->rela->rela_list, list) | ||
| 122 | if (rela->sym->sec == func->sec && | 124 | if (rela->sym->sec == func->sec && |
| 123 | rela->addend == func->offset) | 125 | rela->addend == func->offset) |
| 124 | return true; | 126 | return true; |
| @@ -276,6 +278,7 @@ static int decode_instructions(struct objtool_file *file) | |||
| 276 | return -1; | 278 | return -1; |
| 277 | } | 279 | } |
| 278 | 280 | ||
| 281 | hash_add(file->insn_hash, &insn->hash, insn->offset); | ||
| 279 | list_add_tail(&insn->list, &file->insn_list); | 282 | list_add_tail(&insn->list, &file->insn_list); |
| 280 | } | 283 | } |
| 281 | } | 284 | } |
| @@ -729,6 +732,7 @@ static int decode_sections(struct objtool_file *file) | |||
| 729 | { | 732 | { |
| 730 | int ret; | 733 | int ret; |
| 731 | 734 | ||
| 735 | file->whitelist = find_section_by_name(file->elf, "__func_stack_frame_non_standard"); | ||
| 732 | file->rodata = find_section_by_name(file->elf, ".rodata"); | 736 | file->rodata = find_section_by_name(file->elf, ".rodata"); |
| 733 | 737 | ||
| 734 | ret = decode_instructions(file); | 738 | ret = decode_instructions(file); |
| @@ -1091,6 +1095,7 @@ static void cleanup(struct objtool_file *file) | |||
| 1091 | free(alt); | 1095 | free(alt); |
| 1092 | } | 1096 | } |
| 1093 | list_del(&insn->list); | 1097 | list_del(&insn->list); |
| 1098 | hash_del(&insn->hash); | ||
| 1094 | free(insn); | 1099 | free(insn); |
| 1095 | } | 1100 | } |
| 1096 | elf_close(file->elf); | 1101 | elf_close(file->elf); |
| @@ -1125,6 +1130,7 @@ int cmd_check(int argc, const char **argv) | |||
| 1125 | } | 1130 | } |
| 1126 | 1131 | ||
| 1127 | INIT_LIST_HEAD(&file.insn_list); | 1132 | INIT_LIST_HEAD(&file.insn_list); |
| 1133 | hash_init(file.insn_hash); | ||
| 1128 | 1134 | ||
| 1129 | ret = decode_sections(&file); | 1135 | ret = decode_sections(&file); |
| 1130 | if (ret < 0) | 1136 | if (ret < 0) |
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 7de243f0a7be..e11f6b69cce6 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c | |||
| @@ -59,7 +59,7 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx) | |||
| 59 | struct symbol *sym; | 59 | struct symbol *sym; |
| 60 | 60 | ||
| 61 | list_for_each_entry(sec, &elf->sections, list) | 61 | list_for_each_entry(sec, &elf->sections, list) |
| 62 | list_for_each_entry(sym, &sec->symbol_list, list) | 62 | hash_for_each_possible(sec->symbol_hash, sym, hash, idx) |
| 63 | if (sym->idx == idx) | 63 | if (sym->idx == idx) |
| 64 | return sym; | 64 | return sym; |
| 65 | 65 | ||
| @@ -82,13 +82,15 @@ struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, | |||
| 82 | unsigned int len) | 82 | unsigned int len) |
| 83 | { | 83 | { |
| 84 | struct rela *rela; | 84 | struct rela *rela; |
| 85 | unsigned long o; | ||
| 85 | 86 | ||
| 86 | if (!sec->rela) | 87 | if (!sec->rela) |
| 87 | return NULL; | 88 | return NULL; |
| 88 | 89 | ||
| 89 | list_for_each_entry(rela, &sec->rela->rela_list, list) | 90 | for (o = offset; o < offset + len; o++) |
| 90 | if (rela->offset >= offset && rela->offset < offset + len) | 91 | hash_for_each_possible(sec->rela->rela_hash, rela, hash, o) |
| 91 | return rela; | 92 | if (rela->offset == o) |
| 93 | return rela; | ||
| 92 | 94 | ||
| 93 | return NULL; | 95 | return NULL; |
| 94 | } | 96 | } |
| @@ -137,6 +139,8 @@ static int read_sections(struct elf *elf) | |||
| 137 | 139 | ||
| 138 | INIT_LIST_HEAD(&sec->symbol_list); | 140 | INIT_LIST_HEAD(&sec->symbol_list); |
| 139 | INIT_LIST_HEAD(&sec->rela_list); | 141 | INIT_LIST_HEAD(&sec->rela_list); |
| 142 | hash_init(sec->rela_hash); | ||
| 143 | hash_init(sec->symbol_hash); | ||
| 140 | 144 | ||
| 141 | list_add_tail(&sec->list, &elf->sections); | 145 | list_add_tail(&sec->list, &elf->sections); |
| 142 | 146 | ||
| @@ -261,6 +265,7 @@ static int read_symbols(struct elf *elf) | |||
| 261 | } | 265 | } |
| 262 | } | 266 | } |
| 263 | list_add(&sym->list, entry); | 267 | list_add(&sym->list, entry); |
| 268 | hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx); | ||
| 264 | } | 269 | } |
| 265 | 270 | ||
| 266 | return 0; | 271 | return 0; |
| @@ -298,8 +303,6 @@ static int read_relas(struct elf *elf) | |||
| 298 | } | 303 | } |
| 299 | memset(rela, 0, sizeof(*rela)); | 304 | memset(rela, 0, sizeof(*rela)); |
| 300 | 305 | ||
| 301 | list_add_tail(&rela->list, &sec->rela_list); | ||
| 302 | |||
| 303 | if (!gelf_getrela(sec->elf_data, i, &rela->rela)) { | 306 | if (!gelf_getrela(sec->elf_data, i, &rela->rela)) { |
| 304 | perror("gelf_getrela"); | 307 | perror("gelf_getrela"); |
| 305 | return -1; | 308 | return -1; |
| @@ -315,6 +318,10 @@ static int read_relas(struct elf *elf) | |||
| 315 | symndx, sec->name); | 318 | symndx, sec->name); |
| 316 | return -1; | 319 | return -1; |
| 317 | } | 320 | } |
| 321 | |||
| 322 | list_add_tail(&rela->list, &sec->rela_list); | ||
| 323 | hash_add(sec->rela_hash, &rela->hash, rela->offset); | ||
| 324 | |||
| 318 | } | 325 | } |
| 319 | } | 326 | } |
| 320 | 327 | ||
| @@ -384,10 +391,12 @@ void elf_close(struct elf *elf) | |||
| 384 | list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { | 391 | list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { |
| 385 | list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { | 392 | list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { |
| 386 | list_del(&sym->list); | 393 | list_del(&sym->list); |
| 394 | hash_del(&sym->hash); | ||
| 387 | free(sym); | 395 | free(sym); |
| 388 | } | 396 | } |
| 389 | list_for_each_entry_safe(rela, tmprela, &sec->rela_list, list) { | 397 | list_for_each_entry_safe(rela, tmprela, &sec->rela_list, list) { |
| 390 | list_del(&rela->list); | 398 | list_del(&rela->list); |
| 399 | hash_del(&rela->hash); | ||
| 391 | free(rela); | 400 | free(rela); |
| 392 | } | 401 | } |
| 393 | list_del(&sec->list); | 402 | list_del(&sec->list); |
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index 57e4653f8383..7f3e00a2f907 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h | |||
| @@ -21,12 +21,15 @@ | |||
| 21 | #include <stdio.h> | 21 | #include <stdio.h> |
| 22 | #include <gelf.h> | 22 | #include <gelf.h> |
| 23 | #include <linux/list.h> | 23 | #include <linux/list.h> |
| 24 | #include <linux/hashtable.h> | ||
| 24 | 25 | ||
| 25 | struct section { | 26 | struct section { |
| 26 | struct list_head list; | 27 | struct list_head list; |
| 27 | GElf_Shdr sh; | 28 | GElf_Shdr sh; |
| 28 | struct list_head symbol_list; | 29 | struct list_head symbol_list; |
| 30 | DECLARE_HASHTABLE(symbol_hash, 8); | ||
| 29 | struct list_head rela_list; | 31 | struct list_head rela_list; |
| 32 | DECLARE_HASHTABLE(rela_hash, 16); | ||
| 30 | struct section *base, *rela; | 33 | struct section *base, *rela; |
| 31 | struct symbol *sym; | 34 | struct symbol *sym; |
| 32 | Elf_Data *elf_data; | 35 | Elf_Data *elf_data; |
| @@ -38,10 +41,11 @@ struct section { | |||
| 38 | 41 | ||
| 39 | struct symbol { | 42 | struct symbol { |
| 40 | struct list_head list; | 43 | struct list_head list; |
| 44 | struct hlist_node hash; | ||
| 41 | GElf_Sym sym; | 45 | GElf_Sym sym; |
| 42 | struct section *sec; | 46 | struct section *sec; |
| 43 | char *name; | 47 | char *name; |
| 44 | int idx; | 48 | unsigned int idx; |
| 45 | unsigned char bind, type; | 49 | unsigned char bind, type; |
| 46 | unsigned long offset; | 50 | unsigned long offset; |
| 47 | unsigned int len; | 51 | unsigned int len; |
| @@ -49,10 +53,11 @@ struct symbol { | |||
| 49 | 53 | ||
| 50 | struct rela { | 54 | struct rela { |
| 51 | struct list_head list; | 55 | struct list_head list; |
| 56 | struct hlist_node hash; | ||
| 52 | GElf_Rela rela; | 57 | GElf_Rela rela; |
| 53 | struct symbol *sym; | 58 | struct symbol *sym; |
| 54 | unsigned int type; | 59 | unsigned int type; |
| 55 | int offset; | 60 | unsigned long offset; |
| 56 | int addend; | 61 | int addend; |
| 57 | }; | 62 | }; |
| 58 | 63 | ||
| @@ -62,6 +67,7 @@ struct elf { | |||
| 62 | int fd; | 67 | int fd; |
| 63 | char *name; | 68 | char *name; |
| 64 | struct list_head sections; | 69 | struct list_head sections; |
| 70 | DECLARE_HASHTABLE(rela_hash, 16); | ||
| 65 | }; | 71 | }; |
| 66 | 72 | ||
| 67 | 73 | ||
