Skip to content

Commit 29fce8d

Browse files
anakryikoacmel
authored andcommitted
strings: use BTF's string APIs for strings management
Switch strings container to using struct btf and its btf__add_str()/btf__find_str() APIs, which do equivalent internal string deduplication. This turns out to be a very significantly faster than using tsearch functions. To satisfy CTF encoding use case, some hacky string size fetching approach is utilized, as libbpf doesn't provide direct API to get total string section size and to copy over just strings data section. BEFORE: 22,624.28 msec task-clock # 1.000 CPUs utilized 85 context-switches # 0.004 K/sec 3 cpu-migrations # 0.000 K/sec 622,545 page-faults # 0.028 M/sec 68,177,206,387 cycles # 3.013 GHz (24.99%) 114,370,031,619 instructions # 1.68 insn per cycle (25.01%) 26,125,001,179 branches # 1154.733 M/sec (25.01%) 458,861,243 branch-misses # 1.76% of all branches (25.00%) 24,533,455,967 L1-dcache-loads # 1084.386 M/sec (25.02%) 973,500,214 L1-dcache-load-misses # 3.97% of all L1-dcache hits (25.05%) 338,773,561 LLC-loads # 14.974 M/sec (25.02%) 12,651,196 LLC-load-misses # 3.73% of all LL-cache hits (25.00%) 22.628910615 seconds time elapsed 21.341063000 seconds user 1.283763000 seconds sys AFTER: 18,362.97 msec task-clock # 1.000 CPUs utilized 37 context-switches # 0.002 K/sec 0 cpu-migrations # 0.000 K/sec 626,281 page-faults # 0.034 M/sec 52,480,619,000 cycles # 2.858 GHz (25.00%) 104,736,434,384 instructions # 2.00 insn per cycle (25.01%) 23,878,428,465 branches # 1300.358 M/sec (25.01%) 252,669,685 branch-misses # 1.06% of all branches (25.03%) 21,829,390,952 L1-dcache-loads # 1188.772 M/sec (25.04%) 638,086,339 L1-dcache-load-misses # 2.92% of all L1-dcache hits (25.02%) 212,327,435 LLC-loads # 11.563 M/sec (25.00%) 14,578,117 LLC-load-misses # 6.87% of all LL-cache hits (25.00%) 18.364427347 seconds time elapsed 16.985494000 seconds user 1.377959000 seconds sys Committer testing: Before: $ perf stat -r5 pahole -J vmlinux Performance counter stats for 'pahole -J vmlinux' (5 runs): 8,735.92 msec task-clock:u # 0.998 CPUs utilized ( +- 0.34% ) 0 context-switches:u # 0.000 K/sec 0 cpu-migrations:u # 0.000 K/sec 353,978 page-faults:u # 0.041 M/sec ( +- 0.00% ) 34,722,167,335 cycles:u # 3.975 GHz ( +- 0.12% ) (83.33%) 555,981,118 stalled-cycles-frontend:u # 1.60% frontend cycles idle ( +- 1.53% ) (83.33%) 5,215,370,531 stalled-cycles-backend:u # 15.02% backend cycles idle ( +- 1.31% ) (83.33%) 72,615,773,119 instructions:u # 2.09 insn per cycle # 0.07 stalled cycles per insn ( +- 0.02% ) (83.34%) 16,624,959,121 branches:u # 1903.057 M/sec ( +- 0.01% ) (83.33%) 229,962,327 branch-misses:u # 1.38% of all branches ( +- 0.07% ) (83.33%) 8.7503 +- 0.0301 seconds time elapsed ( +- 0.34% ) $ After: $ perf stat -r5 pahole -J vmlinux Performance counter stats for 'pahole -J vmlinux' (5 runs): 7,302.31 msec task-clock:u # 0.998 CPUs utilized ( +- 1.16% ) 0 context-switches:u # 0.000 K/sec 0 cpu-migrations:u # 0.000 K/sec 355,884 page-faults:u # 0.049 M/sec ( +- 0.00% ) 29,150,861,078 cycles:u # 3.992 GHz ( +- 0.35% ) (83.33%) 478,705,326 stalled-cycles-frontend:u # 1.64% frontend cycles idle ( +- 2.70% ) (83.33%) 5,351,001,796 stalled-cycles-backend:u # 18.36% backend cycles idle ( +- 1.20% ) (83.33%) 65,835,888,022 instructions:u # 2.26 insn per cycle # 0.08 stalled cycles per insn ( +- 0.03% ) (83.33%) 15,025,195,460 branches:u # 2057.594 M/sec ( +- 0.05% ) (83.34%) 141,209,214 branch-misses:u # 0.94% of all branches ( +- 0.15% ) (83.33%) 7.3140 +- 0.0851 seconds time elapsed ( +- 1.16% ) $ 16.04% less cycles, keep the patches coming! :-) Had to add this patch tho: +++ b/dwarf_loader.c @@ -2159,7 +2159,7 @@ static unsigned long long dwarf_tag__orig_id(const struct tag *tag, static const char *dwarf__strings_ptr(const struct cu *cu __unused, strings_t s) { - return strings__ptr(strings, s); + return s ? strings__ptr(strings, s) : NULL; } To keep preexisting behaviour and to do what the BTF specific strings_ptr method does: static const char *btf_elf__strings_ptr(const struct cu *cu, strings_t s) { return btf_elf__string(cu->priv, s); } const char *btf_elf__string(struct btf_elf *btfe, uint32_t ref) { const char *s = btf__str_by_offset(btfe->btf, ref); return s && s[0] == '\0' ? NULL : s; } With these adjustments, btfdiff on a vmlinux with BTF and DWARF is again clean, i.e. pretty printing from BTF matches what we get when using DWARF. Signed-off-by: Andrii Nakryiko <[email protected]> Tested-by: Arnaldo Carvalho de Melo <[email protected]> Cc: Alexei Starovoitov <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent 75f3520 commit 29fce8d

6 files changed

+50
-95
lines changed

ctf_encoder.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ int cu__encode_ctf(struct cu *cu, int verbose)
248248
if (cu__cache_symtab(cu) < 0)
249249
goto out_delete;
250250

251-
ctf__set_strings(ctf, &strings->gb);
251+
ctf__set_strings(ctf, strings);
252252

253253
uint32_t id;
254254
struct tag *pos;

dwarf_loader.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2159,7 +2159,7 @@ static unsigned long long dwarf_tag__orig_id(const struct tag *tag,
21592159
static const char *dwarf__strings_ptr(const struct cu *cu __unused,
21602160
strings_t s)
21612161
{
2162-
return strings__ptr(strings, s);
2162+
return s ? strings__ptr(strings, s) : NULL;
21632163
}
21642164

21652165
struct debug_fmt_ops dwarf__ops;

libctf.c

+7-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "ctf.h"
2020
#include "dutil.h"
2121
#include "gobuffer.h"
22+
#include "pahole_strings.h"
2223

2324
bool ctf__ignore_symtab_function(const GElf_Sym *sym, const char *sym_name)
2425
{
@@ -287,7 +288,7 @@ int ctf__load_symtab(struct ctf *ctf)
287288
return ctf->symtab == NULL ? -1 : 0;
288289
}
289290

290-
void ctf__set_strings(struct ctf *ctf, struct gobuffer *strings)
291+
void ctf__set_strings(struct ctf *ctf, struct strings *strings)
291292
{
292293
ctf->strings = strings;
293294
}
@@ -570,7 +571,7 @@ int ctf__encode(struct ctf *ctf, uint8_t flags)
570571
size = (gobuffer__size(&ctf->types) +
571572
gobuffer__size(&ctf->objects) +
572573
gobuffer__size(&ctf->funcs) +
573-
gobuffer__size(ctf->strings));
574+
strings__size(ctf->strings));
574575

575576
ctf->size = sizeof(*hdr) + size;
576577
ctf->buf = malloc(ctf->size);
@@ -594,13 +595,13 @@ int ctf__encode(struct ctf *ctf, uint8_t flags)
594595
hdr->ctf_type_off = offset;
595596
offset += gobuffer__size(&ctf->types);
596597
hdr->ctf_str_off = offset;
597-
hdr->ctf_str_len = gobuffer__size(ctf->strings);
598+
hdr->ctf_str_len = strings__size(ctf->strings);
598599

599600
void *payload = ctf->buf + sizeof(*hdr);
600601
gobuffer__copy(&ctf->objects, payload + hdr->ctf_object_off);
601602
gobuffer__copy(&ctf->funcs, payload + hdr->ctf_func_off);
602603
gobuffer__copy(&ctf->types, payload + hdr->ctf_type_off);
603-
gobuffer__copy(ctf->strings, payload + hdr->ctf_str_off);
604+
strings__copy(ctf->strings, payload + hdr->ctf_str_off);
604605

605606
*(char *)(ctf->buf + sizeof(*hdr) + hdr->ctf_str_off) = '\0';
606607
if (flags & CTF_FLAGS_COMPR) {
@@ -623,11 +624,10 @@ int ctf__encode(struct ctf *ctf, uint8_t flags)
623624
}
624625
#if 0
625626
printf("\n\ntypes:\n entries: %d\n size: %u"
626-
"\nstrings:\n entries: %u\n size: %u\ncompressed size: %d\n",
627+
"\nstrings:\n size: %u\ncompressed size: %d\n",
627628
ctf->type_index,
628629
gobuffer__size(&ctf->types),
629-
gobuffer__nr_entries(ctf->strings),
630-
gobuffer__size(ctf->strings), size);
630+
strings__size(ctf->strings), size);
631631
#endif
632632
int fd = open(ctf->filename, O_RDWR);
633633
if (fd < 0) {

libctf.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct ctf {
2424
struct gobuffer objects; /* data/variables */
2525
struct gobuffer types;
2626
struct gobuffer funcs;
27-
struct gobuffer *strings;
27+
struct strings *strings;
2828
char *filename;
2929
size_t size;
3030
int swapped;
@@ -76,7 +76,7 @@ int ctf__add_function(struct ctf *ctf, uint16_t type, uint16_t nr_parms,
7676

7777
int ctf__add_object(struct ctf *ctf, uint16_t type);
7878

79-
void ctf__set_strings(struct ctf *ctf, struct gobuffer *strings);
79+
void ctf__set_strings(struct ctf *ctf, struct strings *strings);
8080
int ctf__encode(struct ctf *ctf, uint8_t flags);
8181

8282
char *ctf__string(struct ctf *ctf, uint32_t ref);

pahole_strings.h

+5-27
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
Copyright (C) 2008 Arnaldo Carvalho de Melo <[email protected]>
77
*/
88

9-
#include "gobuffer.h"
9+
#include "lib/bpf/src/btf.h"
1010

1111
typedef unsigned int strings_t;
1212

1313
struct strings {
14-
void *tree;
15-
struct gobuffer gb;
14+
struct btf *btf;
1615
};
1716

1817
struct strings *strings__new(void);
@@ -21,33 +20,12 @@ void strings__delete(struct strings *strings);
2120

2221
strings_t strings__add(struct strings *strings, const char *str);
2322
strings_t strings__find(struct strings *strings, const char *str);
24-
25-
int strings__cmp(const struct strings *strings, strings_t a, strings_t b);
23+
strings_t strings__size(const struct strings *strings);
24+
int strings__copy(const struct strings *strings, void *dst);
2625

2726
static inline const char *strings__ptr(const struct strings *strings, strings_t s)
2827
{
29-
return gobuffer__ptr(&strings->gb, s);
30-
}
31-
32-
static inline const char *strings__entries(const struct strings *strings)
33-
{
34-
return gobuffer__entries(&strings->gb);
35-
}
36-
37-
static inline unsigned int strings__nr_entries(const struct strings *strings)
38-
{
39-
return gobuffer__nr_entries(&strings->gb);
40-
}
41-
42-
static inline strings_t strings__size(const struct strings *strings)
43-
{
44-
return gobuffer__size(&strings->gb);
45-
}
46-
47-
static inline const char *strings__compress(struct strings *strings,
48-
unsigned int *size)
49-
{
50-
return gobuffer__compress(&strings->gb, size);
28+
return btf__str_by_offset(strings->btf, s);
5129
}
5230

5331
#endif /* _STRINGS_H_ */

strings.c

+34-57
Original file line numberDiff line numberDiff line change
@@ -15,97 +15,74 @@
1515
#include <zlib.h>
1616

1717
#include "dutil.h"
18+
#include "lib/bpf/src/libbpf.h"
1819

1920
struct strings *strings__new(void)
2021
{
2122
struct strings *strs = malloc(sizeof(*strs));
2223

23-
if (strs != NULL) {
24-
strs->tree = NULL;
25-
gobuffer__init(&strs->gb);
24+
if (!strs)
25+
return NULL;
26+
27+
strs->btf = btf__new_empty();
28+
if (libbpf_get_error(strs->btf)) {
29+
free(strs);
30+
return NULL;
2631
}
2732

2833
return strs;
29-
30-
}
31-
32-
static void do_nothing(void *ptr __unused)
33-
{
3434
}
3535

3636
void strings__delete(struct strings *strs)
3737
{
3838
if (strs == NULL)
3939
return;
40-
tdestroy(strs->tree, do_nothing);
41-
__gobuffer__delete(&strs->gb);
40+
btf__free(strs->btf);
4241
free(strs);
4342
}
4443

45-
static strings_t strings__insert(struct strings *strs, const char *s)
46-
{
47-
return gobuffer__add(&strs->gb, s, strlen(s) + 1);
48-
}
49-
50-
struct search_key {
51-
struct strings *strs;
52-
const char *str;
53-
};
54-
55-
static int strings__compare(const void *a, const void *b)
56-
{
57-
const struct search_key *key = a;
58-
59-
return strcmp(key->str, key->strs->gb.entries + (unsigned long)b);
60-
}
61-
6244
strings_t strings__add(struct strings *strs, const char *str)
6345
{
64-
unsigned long *s;
6546
strings_t index;
66-
struct search_key key = {
67-
.strs = strs,
68-
.str = str,
69-
};
7047

7148
if (str == NULL)
7249
return 0;
7350

74-
s = tsearch(&key, &strs->tree, strings__compare);
75-
if (s != NULL) {
76-
if (*(struct search_key **)s == (void *)&key) { /* Not found, replace with the right key */
77-
index = strings__insert(strs, str);
78-
if (index != 0)
79-
*s = (unsigned long)index;
80-
else {
81-
tdelete(&key, &strs->tree, strings__compare);
82-
return 0;
83-
}
84-
} else /* Found! */
85-
index = *s;
86-
} else
51+
index = btf__add_str(strs->btf, str);
52+
if (index < 0)
8753
return 0;
8854

8955
return index;
9056
}
9157

9258
strings_t strings__find(struct strings *strs, const char *str)
9359
{
94-
strings_t *s;
95-
struct search_key key = {
96-
.strs = strs,
97-
.str = str,
98-
};
60+
return btf__find_str(strs->btf, str);
61+
}
9962

100-
if (str == NULL)
101-
return 0;
63+
/* a horrible and inefficient hack to get string section size out of BTF */
64+
strings_t strings__size(const struct strings *strs)
65+
{
66+
const struct btf_header *p;
67+
uint32_t sz;
68+
69+
p = btf__get_raw_data(strs->btf, &sz);
70+
if (!p)
71+
return -1;
10272

103-
s = tfind(&key, &strs->tree, strings__compare);
104-
return s ? *s : 0;
73+
return p->str_len;
10574
}
10675

107-
int strings__cmp(const struct strings *strs, strings_t a, strings_t b)
76+
/* similarly horrible hack to copy out string section out of BTF */
77+
int strings__copy(const struct strings *strs, void *dst)
10878
{
109-
return a == b ? 0 : strcmp(strings__ptr(strs, a),
110-
strings__ptr(strs, b));
79+
const struct btf_header *p;
80+
uint32_t sz;
81+
82+
p = btf__get_raw_data(strs->btf, &sz);
83+
if (!p)
84+
return -1;
85+
86+
memcpy(dst, (void *)p + p->str_off, p->str_len);
87+
return 0;
11188
}

0 commit comments

Comments
 (0)