Skip to content

Commit b497633

Browse files
committed
Try not to break if batch bcc functions are unavailable
1 parent 54f3a6d commit b497633

File tree

3 files changed

+88
-6
lines changed

3 files changed

+88
-6
lines changed

bcc/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@
9999
<version>RELEASE</version>
100100
<scope>test</scope>
101101
</dependency>
102+
<dependency>
103+
<groupId>org.jetbrains</groupId>
104+
<artifactId>annotations</artifactId>
105+
<version>24.0.1</version>
106+
<scope>compile</scope>
107+
</dependency>
102108
</dependencies>
103109

104110
<parent>

bcc/src/main/java/me/bechberger/ebpf/bcc/BPFTable.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.concurrent.atomic.AtomicInteger;
3535
import java.util.function.Function;
3636
import java.util.function.UnaryOperator;
37+
import java.util.stream.Stream;
3738

3839
import static me.bechberger.ebpf.bcc.PanamaUtil.*;
3940

@@ -191,18 +192,39 @@ public void rawRemove(Object key) {
191192
}
192193
}
193194

195+
/**
196+
* Only use this method if batch functions are not available.
197+
*/
198+
private Stream<K> slowKeyStream() {
199+
assert !PanamaUtil.hasBCCBatchFunctions();
200+
return keySet().stream();
201+
}
202+
194203
@NotNull
195204
public Set<K> keySet() {
205+
if (!PanamaUtil.hasBCCBatchFunctions()) {
206+
// very inefficient, but some bcc versions do not support batch functions
207+
return slowKeyStream().collect(java.util.stream.Collectors.toSet());
208+
}
196209
return items_lookup_batch().stream().map(Map.Entry::getKey).collect(java.util.stream.Collectors.toSet());
197210
}
198211

199212
@NotNull
200213
public Collection<V> values() {
214+
if (!PanamaUtil.hasBCCBatchFunctions()) {
215+
// very inefficient, but some bcc versions do not support batch functions
216+
return slowKeyStream().map(this::get).collect(java.util.stream.Collectors.toList());
217+
}
201218
return items_lookup_batch().stream().map(Map.Entry::getValue).collect(java.util.stream.Collectors.toList());
202219
}
203220

204221
@NotNull
205222
public Set<Map.Entry<K, V>> entrySet() {
223+
if (!PanamaUtil.hasBCCBatchFunctions()) {
224+
// very inefficient, but some bcc versions do not support batch functions
225+
return slowKeyStream().map(k -> new AbstractMap.SimpleEntry<>(k, get(k)))
226+
.collect(java.util.stream.Collectors.toSet());
227+
}
206228
return new HashSet<>(items_lookup_batch());
207229
}
208230

@@ -297,6 +319,11 @@ private int sanity_check_keys_values(@Nullable MemorySegment keys, @Nullable Mem
297319
* @return key-value pairs
298320
*/
299321
public List<Map.Entry<K, V>> items_lookup_batch() {
322+
if (!PanamaUtil.hasBCCBatchFunctions()) {
323+
// very inefficient, but some bcc versions do not support batch functions
324+
return slowKeyStream().map(k -> new AbstractMap.SimpleEntry<>(k, get(k)))
325+
.collect(java.util.stream.Collectors.toList());
326+
}
300327
return items_lookup_and_optionally_delete_batch(false);
301328
}
302329

@@ -317,7 +344,8 @@ private ResultAndErr<Integer> bpf_delete_batch(Arena arena, int map_fd, @Nullabl
317344
* deletes all the related keys-values.
318345
* If keys is None (default) then it deletes all entries.
319346
*/
320-
public void items_delete_batch(Arena arena, @Nullable MemorySegment keys) {
347+
private void items_delete_batch(Arena arena, @Nullable MemorySegment keys) {
348+
assert PanamaUtil.hasBCCBatchFunctions();
321349
if (keys != null) {
322350
var count = sanity_check_keys_values(keys, null);
323351
var countRef = arena.allocate(ValueLayout.JAVA_LONG);
@@ -338,6 +366,15 @@ public void items_delete_batch(Arena arena, @Nullable MemorySegment keys) {
338366
* @param keys keys array to delete. If an array of keys is given then it deletes all
339367
*/
340368
public void delete_keys(@Nullable List<K> keys) {
369+
if (!PanamaUtil.hasBCCBatchFunctions()) {
370+
// very inefficient, but some bcc versions do not support batch functions
371+
if (keys == null) {
372+
zero();
373+
return;
374+
}
375+
keys.forEach(this::rawRemove);
376+
return;
377+
}
341378
try (var arena = Arena.ofConfined()) {
342379
if (keys != null) {
343380
var allocated = alloc_key_values(arena, true, false, keys.size());
@@ -367,7 +404,7 @@ private static ResultAndErr<Integer> bpf_update_batch(Arena arena, int map_fd, @
367404
* @param keys keys array to update
368405
* @param values values array to update
369406
*/
370-
public void items_update_batch(Arena arena, @Nullable MemorySegment keys, @Nullable MemorySegment values) {
407+
private void items_update_batch(Arena arena, @Nullable MemorySegment keys, @Nullable MemorySegment values) {
371408
var count = sanity_check_keys_values(keys, values);
372409
var countRef = arena.allocate(ValueLayout.JAVA_LONG);
373410
countRef.set(ValueLayout.JAVA_LONG, 0, count);
@@ -380,7 +417,7 @@ public void items_update_batch(Arena arena, @Nullable MemorySegment keys, @Nulla
380417
/**
381418
* Look up and delete all the key-value pairs in the map.
382419
*/
383-
public List<Map.Entry<K, V>> items_lookup_and_delete_batch() {
420+
private List<Map.Entry<K, V>> items_lookup_and_delete_batch() {
384421
return items_lookup_and_optionally_delete_batch(true);
385422
}
386423

bcc/src/main/java/me/bechberger/ebpf/bcc/PanamaUtil.java

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import java.lang.invoke.MethodHandle;
88
import java.lang.invoke.VarHandle;
99
import java.util.NoSuchElementException;
10+
import java.util.function.Function;
11+
import java.util.function.Supplier;
1012

1113
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
1214

@@ -19,6 +21,18 @@ public class PanamaUtil {
1921
public static final char O_WRONLY = 1;
2022
public static final char O_RDWR = 2;
2123

24+
private static final boolean HAS_BCC_BATCH_FUNCTIONS;
25+
static {
26+
boolean b = false;
27+
try {
28+
PanamaUtil.lookup("bpf_lookup_and_delete_batch");
29+
b = true;
30+
} catch (NoSuchElementException e) {
31+
// ignore
32+
}
33+
HAS_BCC_BATCH_FUNCTIONS = b;
34+
}
35+
2236
/**
2337
* Convert a memory segment to a string, returns null if segment is NULL
2438
*/
@@ -31,13 +45,25 @@ public static String toString(MemorySegment segment) {
3145

3246
/**
3347
* Lookup a symbol in the current process
48+
*
49+
* @throws NoSuchElementException if the symbol is not found
3450
*/
3551
public static MemorySegment lookup(String symbol) {
3652
return Linker.nativeLinker().defaultLookup().find(symbol)
3753
.or(() -> SymbolLookup.loaderLookup().find(symbol))
3854
.orElseThrow(() -> new NoSuchElementException("Symbol not found: " + symbol));
3955
}
4056

57+
/**
58+
* Check if the batch functions of bcc (like bpf_lookup_and_delete_batch) are available.
59+
* <p>
60+
* Some versions of bcc (on some platforms) apparently do not have these functions.
61+
* @return true if the batch functions are available
62+
*/
63+
public static boolean hasBCCBatchFunctions() {
64+
return HAS_BCC_BATCH_FUNCTIONS;
65+
}
66+
4167
/**
4268
* Pointer type
4369
*/
@@ -78,10 +104,22 @@ public record ResultAndErr<R>(R result, int err) {
78104
/**
79105
* Wraps a method handle and captures the errno value
80106
*/
81-
public record HandlerWithErrno<R>(MethodHandle handle) {
107+
public static class HandlerWithErrno<R> {
108+
109+
private MethodHandle handle = null;
110+
private final Supplier<MethodHandle> handleSupplier;
82111

83112
public HandlerWithErrno(String symbol, FunctionDescriptor descriptor) {
84-
this(Linker.nativeLinker().downcallHandle(PanamaUtil.lookup(symbol), descriptor, Linker.Option.captureCallState("errno")));
113+
handleSupplier = () -> Linker.nativeLinker()
114+
.downcallHandle(PanamaUtil.lookup(symbol), descriptor,
115+
Linker.Option.captureCallState("errno"));
116+
}
117+
118+
private MethodHandle getHandle() {
119+
if (handle == null) {
120+
handle = handleSupplier.get();
121+
}
122+
return handle;
85123
}
86124

87125
@SuppressWarnings("unchecked")
@@ -95,7 +133,8 @@ public ResultAndErr<R> call(Arena arena, Object... args) {
95133
Object[] argsWithState = new Object[args.length + 1];
96134
argsWithState[0] = capturedState;
97135
System.arraycopy(args, 0, argsWithState, 1, args.length);
98-
return new ResultAndErr<>((R) handle.invokeWithArguments(argsWithState), (int) errnoHandle.get(capturedState));
136+
return new ResultAndErr<>((R) getHandle().invokeWithArguments(argsWithState),
137+
(int) errnoHandle.get(capturedState));
99138
} catch (Throwable throwable) {
100139
throw new RuntimeException(throwable);
101140
}

0 commit comments

Comments
 (0)