Skip to content

Commit 58ce085

Browse files
michael-chuhyonghong-song
authored andcommitted
tools: Add hash table item batch operations support
Commit c3b11e5 (add bpf_map_lookup_and_delete_batch in bcc (#3234)) supports hash table use batch operation to solve race condition issue and reduce items syscalls to improve performance. Update tools with hash operations. Improvement can be test with the belove commands: # filetop # syscount --syscall bpf -i 1 # mkdir test; for i in `seq 1 100000`;do touch test/$i;done Before patch filetop, syscall counts are more than 2k. [16:58:42] SYSCALL COUNT bpf 55 [16:58:43] SYSCALL COUNT bpf 2735 [16:58:44] SYSCALL COUNT bpf 2263 After patch batch operations, result shows except 1 syscall for syscount itself, filetop syscall count is reduced to 1. Tracing syscall 'bpf'... Ctrl+C to quit. [17:00:36] SYSCALL COUNT bpf 1 [17:00:37] SYSCALL COUNT bpf 2 [17:00:38] SYSCALL COUNT bpf 2 Change-Id: Icc7f6d39f339bfc492ddcfc213ac91cdbdbe3583 Signed-off-by: Mickey Zhu <[email protected]>
1 parent 952415e commit 58ce085

16 files changed

+178
-46
lines changed

Diff for: tools/argdist.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,12 @@ def attach(self, bpf):
470470
self._attach_k()
471471
if self.entry_probe_required:
472472
self._attach_entry_probe()
473+
# Check whether hash table batch ops is supported
474+
if self.type == "freq" and self.bpf.kernel_struct_has_field(
475+
b'bpf_map_ops', b'map_lookup_and_delete_batch') == 1:
476+
self.htab_batch_ops = True
477+
else:
478+
self.htab_batch_ops = False
473479

474480
def _v2s(self, v):
475481
# Most fields can be converted with plain str(), but strings
@@ -510,7 +516,9 @@ def display(self, top):
510516
if self.type == "freq":
511517
print(self.label or self.raw_spec)
512518
print("\t%-10s %s" % ("COUNT", "EVENT"))
513-
sdata = sorted(data.items(), key=lambda p: p[1].value)
519+
sdata = sorted(data.items_lookup_batch()
520+
if self.htab_batch_ops else data.items(),
521+
key=lambda p: p[1].value)
514522
if top is not None:
515523
sdata = sdata[-top:]
516524
for key, value in sdata:
@@ -531,6 +539,9 @@ def display(self, top):
531539
if not self.is_default_expr else "retval")
532540
data.print_log2_hist(val_type=label)
533541
if not self.cumulative:
542+
if self.htab_batch_ops:
543+
data.items_delete_batch()
544+
else:
534545
data.clear()
535546

536547
def __str__(self):

Diff for: tools/biopattern.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ def mkdev(major, minor):
105105

106106
b = BPF(text=bpf_text)
107107

108+
# check whether hash table batch ops is supported
109+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
110+
b'map_lookup_and_delete_batch') == 1 else False
111+
108112
exiting = 0 if args.interval else 1
109113
counters = b.get_table("counters")
110114

@@ -117,7 +121,8 @@ def mkdev(major, minor):
117121
except KeyboardInterrupt:
118122
exiting = 1
119123

120-
for k, v in counters.items():
124+
for k, v in (counters.items_lookup_and_delete_batch()
125+
if htab_batch_ops else counters.items()):
121126
total = v.random + v.sequential
122127
if total == 0:
123128
continue
@@ -133,7 +138,8 @@ def mkdev(major, minor):
133138
total,
134139
v.bytes / 1024))
135140

136-
counters.clear()
141+
if not htab_batch_ops:
142+
counters.clear()
137143

138144
countdown -= 1
139145
if exiting or countdown == 0:

Diff for: tools/biotop.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@
287287
else:
288288
b.attach_tracepoint(tp="block:block_io_done", fn_name="trace_req_completion_tp")
289289

290+
# check whether hash table batch ops is supported
291+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
292+
b'map_lookup_and_delete_batch') == 1 else False
293+
290294
print('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval)
291295

292296
# cache disk major,minor -> diskname
@@ -317,7 +321,8 @@
317321
# by-PID output
318322
counts = b.get_table("counts")
319323
line = 0
320-
for k, v in reversed(sorted(counts.items(),
324+
for k, v in reversed(sorted(counts.items_lookup_and_delete_batch()
325+
if htab_batch_ops else counts.items(),
321326
key=lambda counts: counts[1].bytes)):
322327

323328
# lookup disk
@@ -336,7 +341,9 @@
336341
line += 1
337342
if line >= maxrows:
338343
break
339-
counts.clear()
344+
345+
if not htab_batch_ops:
346+
counts.clear()
340347

341348
countdown -= 1
342349
if exiting or countdown == 0:

Diff for: tools/cachestat.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ def get_meminfo():
140140
("folio_account_dirtied", "account_page_dirtied"))
141141
b.attach_kprobe(event="mark_buffer_dirty", fn_name="do_count_mbd")
142142

143+
# check whether hash table batch ops is supported
144+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
145+
b'map_lookup_and_delete_batch') == 1 else False
146+
143147
# header
144148
if tstamp:
145149
print("%-8s " % "TIME", end="")
@@ -162,7 +166,9 @@ def get_meminfo():
162166
signal.signal(signal.SIGINT, signal_ignore)
163167

164168
counts = b["counts"]
165-
for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
169+
for k, v in sorted(counts.items_lookup_and_delete_batch()
170+
if htab_batch_ops else counts.items(),
171+
key=lambda counts: counts[1].value):
166172
# partial string matches in case of .isra (necessary?)
167173
if k.nf == 0: # NF_APCL
168174
apcl = max(0, v.value)
@@ -197,7 +203,8 @@ def get_meminfo():
197203
print("%d %d %d %d %d %d %d\n" %
198204
(mpa, mbd, apcl, apd, total, misses, hits))
199205

200-
counts.clear()
206+
if not htab_batch_ops:
207+
counts.clear()
201208

202209
# Get memory info
203210
mem = get_meminfo()

Diff for: tools/cachetop.py

+15-4
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ def get_meminfo():
6464
def get_processes_stats(
6565
bpf,
6666
sort_field=DEFAULT_SORT_FIELD,
67-
sort_reverse=False):
67+
sort_reverse=False,
68+
htab_batch_ops=False):
6869
'''
6970
Return a tuple containing:
7071
buffer
@@ -73,7 +74,8 @@ def get_processes_stats(
7374
'''
7475
counts = bpf.get_table("counts")
7576
stats = defaultdict(lambda: defaultdict(int))
76-
for k, v in counts.items():
77+
for k, v in (counts.items_lookup_batch()
78+
if htab_batch_ops else counts.items()):
7779
stats["%d-%d-%s" % (k.pid, k.uid, k.comm.decode('utf-8', 'replace'))][k.nf] = v.value
7880
stats_list = []
7981

@@ -129,7 +131,11 @@ def get_processes_stats(
129131
stats_list = sorted(
130132
stats_list, key=lambda stat: stat[sort_field], reverse=sort_reverse
131133
)
132-
counts.clear()
134+
if htab_batch_ops:
135+
counts.items_delete_batch()
136+
else:
137+
counts.clear()
138+
133139
return stats_list
134140

135141

@@ -220,6 +226,10 @@ def handle_loop(stdscr, args):
220226

221227
exiting = 0
222228

229+
# check whether hash table batch ops is supported
230+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
231+
b'map_lookup_and_delete_batch') == 1 else False
232+
223233
while 1:
224234
s = stdscr.getch()
225235
if s == ord('q'):
@@ -245,7 +255,8 @@ def handle_loop(stdscr, args):
245255
process_stats = get_processes_stats(
246256
b,
247257
sort_field=sort_field,
248-
sort_reverse=sort_reverse)
258+
sort_reverse=sort_reverse,
259+
htab_batch_ops=htab_batch_ops)
249260
stdscr.clear()
250261
stdscr.addstr(
251262
0, 0,

Diff for: tools/dirtop.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ def get_searched_ids(root_directories):
197197
b.attach_kprobe(event="vfs_read", fn_name="trace_read_entry")
198198
b.attach_kprobe(event="vfs_write", fn_name="trace_write_entry")
199199

200+
# check whether hash table batch ops is supported
201+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
202+
b'map_lookup_and_delete_batch') == 1 else False
203+
200204
DNAME_INLINE_LEN = 32 # linux/dcache.h
201205

202206
print('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval)
@@ -235,7 +239,8 @@ def sort_fn(counts):
235239
writes = {}
236240
reads_Kb = {}
237241
writes_Kb = {}
238-
for k, v in reversed(sorted(counts.items(),
242+
for k, v in reversed(sorted(counts.items_lookup_and_delete_batch()
243+
if htab_batch_ops else counts.items(),
239244
key=sort_fn)):
240245
# If it's the first time we see this inode
241246
if k.inode_id not in reads:
@@ -259,7 +264,8 @@ def sort_fn(counts):
259264
if line >= maxrows:
260265
break
261266

262-
counts.clear()
267+
if not htab_batch_ops:
268+
counts.clear()
263269

264270
countdown -= 1
265271
if exiting or countdown == 0:

Diff for: tools/filetop.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@
167167
b.attach_kprobe(event="vfs_read", fn_name="trace_read_entry")
168168
b.attach_kprobe(event="vfs_write", fn_name="trace_write_entry")
169169

170+
# check whether hash table batch ops is supported
171+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
172+
b'map_lookup_and_delete_batch') == 1 else False
173+
170174
DNAME_INLINE_LEN = 32 # linux/dcache.h
171175

172176
print('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval)
@@ -198,7 +202,8 @@ def sort_fn(counts):
198202
# by-TID output
199203
counts = b.get_table("counts")
200204
line = 0
201-
for k, v in reversed(sorted(counts.items(),
205+
for k, v in reversed(sorted(counts.items_lookup_and_delete_batch()
206+
if htab_batch_ops else counts.items(),
202207
key=sort_fn)):
203208
name = k.name.decode('utf-8', 'replace')
204209
if k.name_len > DNAME_INLINE_LEN:
@@ -213,7 +218,9 @@ def sort_fn(counts):
213218
line += 1
214219
if line >= maxrows:
215220
break
216-
counts.clear()
221+
222+
if not htab_batch_ops:
223+
counts.clear()
217224

218225
countdown -= 1
219226
if exiting or countdown == 0:

Diff for: tools/klockstat.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,9 @@ def display(sort, maxs, totals, counts):
413413
global missing_stacks
414414
global has_enomem
415415

416-
for k, v in sorted(sort.items(), key=lambda sort: sort[1].value, reverse=True)[:args.locks]:
416+
for k, v in sorted(sort.items_lookup_batch()
417+
if htab_batch_ops else sort.items(),
418+
key=lambda sort: sort[1].value, reverse=True)[:args.locks]:
417419
missing_stacks += int(stack_id_err(k.value))
418420
has_enomem = has_enomem or (k.value == -errno.ENOMEM)
419421

@@ -458,6 +460,10 @@ def display(sort, maxs, totals, counts):
458460
b.attach_kretprobe(event="mutex_lock", fn_name="mutex_lock_return")
459461
b.attach_kprobe(event="mutex_lock", fn_name="mutex_lock_enter")
460462

463+
# check whether hash table batch ops is supported
464+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
465+
b'map_lookup_and_delete_batch') == 1 else False
466+
461467
enabled = b.get_table("enabled");
462468

463469
stack_traces = b.get_table("stack_traces")
@@ -508,12 +514,20 @@ def display(sort, maxs, totals, counts):
508514
break;
509515

510516
stack_traces.clear()
511-
aq_counts.clear()
512-
aq_maxs.clear()
513-
aq_totals.clear()
514-
hl_counts.clear()
515-
hl_maxs.clear()
516-
hl_totals.clear()
517+
if htab_batch_ops:
518+
aq_counts.items_delete_batch()
519+
aq_maxs.items_delete_batch()
520+
aq_totals.items_delete_batch()
521+
hl_counts.items_delete_batch()
522+
hl_maxs.items_delete_batch()
523+
hl_totals.items_delete_batch()
524+
else:
525+
aq_counts.clear()
526+
aq_maxs.clear()
527+
aq_totals.clear()
528+
hl_counts.clear()
529+
hl_maxs.clear()
530+
hl_totals.clear()
517531

518532
if missing_stacks > 0:
519533
enomem_str = " Consider increasing --stack-storage-size."

Diff for: tools/netqtop.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def print_table(table, qnum):
6363
tGroup = [0,0,0,0,0]
6464
tpkt = 0
6565
tlen = 0
66-
for k, v in table.items():
66+
for k, v in (table.items_lookup_batch()
67+
if htab_batch_ops else table.items()):
6768
qids += [k.value]
6869
tlen += v.total_pkt_len
6970
tpkt += v.num_pkt
@@ -142,14 +143,20 @@ def print_result(b):
142143
print("TX")
143144
table = b['tx_q']
144145
print_table(table, tx_num)
145-
b['tx_q'].clear()
146+
if htab_batch_ops:
147+
b['tx_q'].items_delete_batch()
148+
else:
149+
b['tx_q'].clear()
146150

147151
# --------- print rx queues ---------------
148152
print("")
149153
print("RX")
150154
table = b['rx_q']
151155
print_table(table, rx_num)
152-
b['rx_q'].clear()
156+
if htab_batch_ops:
157+
b['rx_q'].items_delete_batch()
158+
else:
159+
b['rx_q'].clear()
153160
if args.throughput:
154161
print("-"*95)
155162
else:
@@ -205,6 +212,10 @@ def print_result(b):
205212

206213
################## start tracing ##################
207214
b = BPF(src_file = EBPF_FILE)
215+
# --- check whether hash table batch ops is supported ---
216+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
217+
b'map_lookup_and_delete_batch') == 1 else False
218+
208219
# --------- set hash array --------
209220
devname_map = b['name_map']
210221
_name = Devname()

Diff for: tools/slabratetop.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@
199199

200200
# initialize BPF
201201
b = BPF(text=bpf_text)
202+
# check whether hash table batch ops is supported
203+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
204+
b'map_lookup_and_delete_batch') == 1 else False
202205

203206
print('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval)
204207

@@ -222,14 +225,16 @@
222225
# by-TID output
223226
counts = b.get_table("counts")
224227
line = 0
225-
for k, v in reversed(sorted(counts.items(),
228+
for k, v in reversed(sorted(counts.items_lookup_and_delete_batch()
229+
if htab_batch_ops else counts.items(),
226230
key=lambda counts: counts[1].size)):
227231
printb(b"%-32s %6d %10d" % (k.name, v.count, v.size))
228232

229233
line += 1
230234
if line >= maxrows:
231235
break
232-
counts.clear()
236+
if not htab_batch_ops:
237+
counts.clear()
233238

234239
countdown -= 1
235240
if exiting or countdown == 0:

Diff for: tools/stackcount.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,9 @@ def run(self):
319319
print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." %
320320
(self.probe.matched, self.args.pattern))
321321
b = self.probe.bpf
322+
# check whether hash table batch ops is supported
323+
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
324+
b'map_lookup_and_delete_batch') == 1 else False
322325
exiting = 0 if self.args.interval else 1
323326
seconds = 0
324327
while True:
@@ -340,7 +343,8 @@ def run(self):
340343
counts = self.probe.bpf["counts"]
341344
stack_traces = self.probe.bpf["stack_traces"]
342345
self.comm_cache = {}
343-
for k, v in sorted(counts.items(),
346+
for k, v in sorted(counts.items_lookup_and_delete_batch()
347+
if htab_batch_ops else counts.items(),
344348
key=lambda counts: counts[1].value):
345349
user_stack = [] if k.user_stack_id < 0 else \
346350
stack_traces.walk(k.user_stack_id)
@@ -368,7 +372,8 @@ def run(self):
368372
if not self.args.pid and k.tgid != 0xffffffff:
369373
self._print_comm(k.name, k.tgid)
370374
print(" %d\n" % v.value)
371-
counts.clear()
375+
if not htab_batch_ops:
376+
counts.clear()
372377

373378
if exiting:
374379
if not self.args.folded:

0 commit comments

Comments
 (0)