diff --git a/annotations/src/main/java/me/bechberger/ebpf/annotations/bpf/BuiltinBPFFunction.java b/annotations/src/main/java/me/bechberger/ebpf/annotations/bpf/BuiltinBPFFunction.java
index 70e1c19..9a0ff6b 100644
--- a/annotations/src/main/java/me/bechberger/ebpf/annotations/bpf/BuiltinBPFFunction.java
+++ b/annotations/src/main/java/me/bechberger/ebpf/annotations/bpf/BuiltinBPFFunction.java
@@ -40,7 +40,7 @@
*
{@code $strlen$this}: length of the {@code $this} interpreted as a string literal
* {@code $strlen$argN}: length of the {@code $argN} interpreted as a string literal
* {@code $str$argN}: Asserts that {@code $argN} is a string literal
- * {@code $pointery$argN}: if {@code $argN} is not a pointer (or an array or a string), then prefix it with {@code &} and assume that it is an lvalue
+ * {@code $pointery$argN}: if {@code $argN} is not a pointer (or an array or a string), then prefix it with {@code &} and assign it inline to a variable if needed
*
*
* Example: {@snippet :
diff --git a/bpf-compiler-plugin/src/main/java/me/bechberger/ebpf/bpf/compiler/MethodTemplate.java b/bpf-compiler-plugin/src/main/java/me/bechberger/ebpf/bpf/compiler/MethodTemplate.java
index b9d30d6..c45b54e 100644
--- a/bpf-compiler-plugin/src/main/java/me/bechberger/ebpf/bpf/compiler/MethodTemplate.java
+++ b/bpf-compiler-plugin/src/main/java/me/bechberger/ebpf/bpf/compiler/MethodTemplate.java
@@ -10,6 +10,7 @@
import me.bechberger.ebpf.bpf.compiler.MethodTemplateCache.TemplateRenderException;
import me.bechberger.ebpf.type.Ptr;
import me.bechberger.ebpf.type.TypeUtils;
+import org.intellij.lang.annotations.RegExp;
import org.jetbrains.annotations.Nullable;
import javax.lang.model.type.TypeKind;
@@ -23,6 +24,32 @@
*/
public record MethodTemplate(String methodName, String raw, List parts) {
+ public class NewVariableContext {
+
+ private record NewVariable(String name, String value) {
+ }
+
+ private List newVariables = new ArrayList<>();
+
+ public String request(String value) {
+ var name = "___pointery__" + newVariables.size();
+ newVariables.add(new NewVariable(name, value));
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return newVariables.stream().map(nv -> String.format("auto %s = %s;", nv.name, nv.value)).collect(Collectors.joining(" "));
+ }
+
+ public VerbatimExpression wrap(VerbatimExpression expression) {
+ if (newVariables.isEmpty()) {
+ return expression;
+ }
+ return new VerbatimExpression(String.format("({%s %s;})", this, expression.toPrettyString()));
+ }
+ }
+
public record CallArgs(@Nullable CAST.Expression thisExpression,
List extends Expression> arguments,
List typeArguments,
@@ -39,25 +66,29 @@ public record CallProps(String methodName, CallArgs args) {
* A part of a template, modelled after the different placeholders in the template in {@link BuiltinBPFFunction}
*/
sealed interface TemplatePart {
- String render(CallProps props);
+ default String render(CallProps props) {
+ return render(props, null);
+ }
+
+ String render(CallProps props, @Nullable NewVariableContext context);
record Verbatim(String verb) implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
return verb;
}
}
record Name() implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
return props.methodName;
}
}
record Arg(int n) implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
if (n >= props.args.arguments.size()) {
throw new TemplateRenderException("Argument " + (n + 1) + " not given for $arg" + (n + 1));
}
@@ -67,7 +98,7 @@ public String render(CallProps props) {
record SubArgs(int n) implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
return IntStream.range(n, props.args.arguments.size())
.mapToObj(i -> props.args.arguments.get(i).toPrettyString())
.collect(Collectors.joining(", "));
@@ -76,14 +107,14 @@ public String render(CallProps props) {
record Args() implements TemplatePart {
@Override
- public String render(CallProps props) {
- return new SubArgs(0).render(props);
+ public String render(CallProps props, @Nullable NewVariableContext context) {
+ return new SubArgs(0).render(props, context);
}
}
record This() implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
if (props.args.thisExpression == null) {
throw new TemplateRenderException("No this expression given for $this");
}
@@ -92,7 +123,7 @@ public String render(CallProps props) {
}
sealed interface StrLen extends TemplatePart {
- static String render(Expression arg) {
+ static String render(Expression arg, @Nullable NewVariableContext context) {
if (arg instanceof Constant.StringConstant constant) {
return Integer.toString(constant.value().length());
}
@@ -102,24 +133,24 @@ static String render(Expression arg) {
record StrLenArg(int n) implements StrLen {
@Override
- public String render(CallProps props) {
- return StrLen.render(props.args.arguments.get(n));
+ public String render(CallProps props, @Nullable NewVariableContext context) {
+ return StrLen.render(props.args.arguments.get(n), context);
}
}
record StrLenThis() implements StrLen {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
if (props.args.thisExpression == null) {
throw new TemplateRenderException("No this expression given for $strlen$this");
}
- return StrLen.render(props.args.thisExpression);
+ return StrLen.render(props.args.thisExpression, context);
}
}
record StrArg(int n) implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
if (n >= props.args.arguments.size()) {
throw new TemplateRenderException("Argument " + (n + 1) + " not given for $str" + (n + 1));
}
@@ -133,7 +164,7 @@ public String render(CallProps props) {
record TypeArgument(int n) implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
if (n >= props.args.typeArguments.size() || props.args.typeArguments.get(n) == null){
throw new TemplateRenderException("Template type argument " + (n + 1) + " not given");
}
@@ -143,7 +174,7 @@ public String render(CallProps props) {
record ClassTypeArgument(int n) implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
if (n >= props.args.classTypeArguments.size() || props.args.classTypeArguments.get(n) == null){
throw new TemplateRenderException("Template class type argument " + (n + 1) + " not given");
}
@@ -153,11 +184,15 @@ public String render(CallProps props) {
record PointeryArg(int n) implements TemplatePart {
@Override
- public String render(CallProps props) {
+ public String render(CallProps props, @Nullable NewVariableContext context) {
if (n >= props.args.arguments.size()) {
throw new TemplateRenderException("Argument " + (n + 1) + " not given for $pointery" + (n + 1));
}
- return "&" + props.args.arguments.get(n).toPrettyString();
+ var inner = props.args.arguments.get(n).toPrettyString();
+ if (context == null || inner.matches("[(]*[a-zA-z_]+[)]*")) {
+ return "&" + inner;
+ }
+ return "&" + context.request(inner);
}
}
}
@@ -304,7 +339,9 @@ static MethodTemplate parse(String methodName, String template, @Nullable Method
}
public Expression call(CallArgs args) {
- List renderedParts = parts.stream().map(part -> part.render(new CallProps(methodName, args)))
+ NewVariableContext context = new NewVariableContext();
+ List renderedParts = parts.stream()
+ .map(part -> part.render(new CallProps(methodName, args), context))
.toList();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.size(); i++) {
@@ -321,6 +358,6 @@ public Expression call(CallArgs args) {
sb.append(renderedParts.get(i));
}
}
- return new VerbatimExpression(sb.toString());
+ return context.wrap(new VerbatimExpression(sb.toString()));
}
}
diff --git a/bpf-processor/src/main/java/me/bechberger/ebpf/bpf/processor/CompilationCache.java b/bpf-processor/src/main/java/me/bechberger/ebpf/bpf/processor/CompilationCache.java
index 3e71f6b..7d7497d 100644
--- a/bpf-processor/src/main/java/me/bechberger/ebpf/bpf/processor/CompilationCache.java
+++ b/bpf-processor/src/main/java/me/bechberger/ebpf/bpf/processor/CompilationCache.java
@@ -132,4 +132,8 @@ private List cachedFiles() {
throw new RuntimeException(e);
}
}
+
+ public Path getCacheFolder() {
+ return cacheFolder;
+ }
}
diff --git a/bpf-processor/src/main/java/me/bechberger/ebpf/bpf/processor/Processor.java b/bpf-processor/src/main/java/me/bechberger/ebpf/bpf/processor/Processor.java
index 5b68e84..49cef94 100644
--- a/bpf-processor/src/main/java/me/bechberger/ebpf/bpf/processor/Processor.java
+++ b/bpf-processor/src/main/java/me/bechberger/ebpf/bpf/processor/Processor.java
@@ -547,7 +547,7 @@ private byte[] compile(CombinedCode code, Path ebpfFile) {
try {
var tempFile = Files.createTempFile("ebpf", ".o");
tempFile.toFile().deleteOnExit();
- List command = List.of(newestClang, "-O2", "-g", "-target", "bpf", "-c", "-o",
+ List command = List.of(newestClang, "-O2", "-g", "-std=gnu2y", "-target", "bpf", "-c", "-o",
tempFile.toString(), "-I", vmlinuxHeader.getParent().toString(),
"-D__TARGET_ARCH_" + getArch(), "-Wno-parentheses-equality", "-Wno-unused-value", "-Wreturn-type",
"-Wno-incompatible-pointer-types-discards-qualifiers",
@@ -625,17 +625,41 @@ private Path obtainPathToVMLinuxHeader() {
}
// else run bpftool btf dump file /sys/kernel/btf/vmlinux format c
// save output to a temp file and return the path to the temp file
- var tempDirectory = Files.createTempDirectory("vmlinux");
- tempDirectory.toFile().deleteOnExit();
- var tempFile = tempDirectory.resolve("vmlinux.h");
- var errorFile = tempDirectory.resolve("error.txt");
+ var cacheFolder = cache.getCacheFolder();
+ var vmLinuxFile = cacheFolder.resolve("vmlinux.h");
+ if (Files.exists(vmLinuxFile)) {
+ return vmLinuxFile;
+ }
+ var errorFile = cacheFolder.resolve("vmlinux_error.txt");
var process = new ProcessBuilder("bpftool", "btf", "dump", "file", "/sys/kernel/btf/vmlinux", "format",
- "c").redirectOutput(tempFile.toFile()).redirectError(errorFile.toFile()).start();
+ "c").redirectOutput(vmLinuxFile.toFile()).redirectError(errorFile.toFile()).start();
if (process.waitFor() != 0) {
throw new UnsupportedOperationException("Could not obtain vmlinux.h header file via 'bpftool btf "
+ "dump file /sys/kernel/btf/vmlinux format c'" + Files.readString(errorFile));
+ } else {
+ Files.delete(errorFile);
}
- return tempFile;
+ // comment lines
+ // typedef _Bool bool;
+ // enum {
+ // false = 0,
+ // true = 1,
+ // };
+ String content = Files.readString(vmLinuxFile);
+ content = content.replace("typedef _Bool bool;", "// typedef _Bool bool")
+ .replaceAll("""
+ enum \\{
+ \\s+false = 0,
+ \\s+true = 1,
+ };
+ """, """
+ // enum {
+ // false = 0,
+ // true = 1,
+ // };
+ """);
+ Files.writeString(vmLinuxFile, content);
+ return vmLinuxFile;
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
diff --git a/bpf-samples/src/main/java/me/bechberger/ebpf/samples/HashMapSample.java b/bpf-samples/src/main/java/me/bechberger/ebpf/samples/HashMapSample.java
index 68f288c..a735ed2 100644
--- a/bpf-samples/src/main/java/me/bechberger/ebpf/samples/HashMapSample.java
+++ b/bpf-samples/src/main/java/me/bechberger/ebpf/samples/HashMapSample.java
@@ -38,8 +38,7 @@ public void enterOpenat2(int dfd, String filename, Ptr how) {
// increment the counter at map[comm]
Ptr<@Unsigned Integer> counter = map.bpf_get(comm);
if (counter == null) {
- @Unsigned int one = 1;
- map.put(comm, one);
+ map.put(comm, 1);
} else {
counter.set(counter.val() + 1);
}
diff --git a/bpf/src/main/java/me/bechberger/ebpf/bpf/Scheduler.java b/bpf/src/main/java/me/bechberger/ebpf/bpf/Scheduler.java
index dbdcf78..c13bc0f 100644
--- a/bpf/src/main/java/me/bechberger/ebpf/bpf/Scheduler.java
+++ b/bpf/src/main/java/me/bechberger/ebpf/bpf/Scheduler.java
@@ -139,6 +139,70 @@ public interface Scheduler {
/** No such process error code */
final int ESRCH = 3;
+ final class PerProcessFlags {
+ /** I'm a virtual CPU */
+ public static final int PF_VCPU = 0x00000001;
+ /** I am an IDLE thread */
+ public static final int PF_IDLE = 0x00000002;
+ /** Getting shut down */
+ public static final int PF_EXITING = 0x00000004;
+ /** Coredumps should ignore this task */
+ public static final int PF_POSTCOREDUMP = 0x00000008;
+ /** Task is an IO worker */
+ public static final int PF_IO_WORKER = 0x00000010;
+ /** I'm a workqueue worker */
+ public static final int PF_WQ_WORKER = 0x00000020;
+ /** Forked but didn't exec */
+ public static final int PF_FORKNOEXEC = 0x00000040;
+ /** Process policy on mce errors */
+ public static final int PF_MCE_PROCESS = 0x00000080;
+ /** Used super-user privileges */
+ public static final int PF_SUPERPRIV = 0x00000100;
+ /** Dumped core */
+ public static final int PF_DUMPCORE = 0x00000200;
+ /** Killed by a signal */
+ public static final int PF_SIGNALED = 0x00000400;
+ /** Allocating memory to free memory. See memalloc\_noreclaim\_save() */
+ public static final int PF_MEMALLOC = 0x00000800;
+ /** set\_user() noticed that RLIMIT\_NPROC was exceeded */
+ public static final int PF_NPROC_EXCEEDED = 0x00001000;
+ /** If unset the fpu must be initialized before use */
+ public static final int PF_USED_MATH = 0x00002000;
+ /** Kernel thread cloned from userspace thread */
+ public static final int PF_USER_WORKER = 0x00004000;
+ /** This thread should not be frozen */
+ public static final int PF_NOFREEZE = 0x00008000;
+ public static final int PF__HOLE__00010000 = 0x00010000;
+ /** I am kswapd */
+ public static final int PF_KSWAPD = 0x00020000;
+ /** All allocations inherit GFP\_NOFS. See memalloc\_nfs\_save() */
+ public static final int PF_MEMALLOC_NOFS = 0x00040000;
+ /** All allocations inherit GFP\_NOIO. See memalloc\_noio\_save() */
+ public static final int PF_MEMALLOC_NOIO = 0x00080000;
+ /** Throttle writes only against the bdi I write to, I am cleaning dirty pages from some other bdi. */
+ public static final int PF_LOCAL_THROTTLE = 0x00100000;
+ /** I am a kernel thread */
+ public static final int PF_KTHREAD = 0x00200000;
+ /** Randomize virtual address space */
+ public static final int PF_RANDOMIZE = 0x00400000;
+ /** All allocation requests will clear \_\_GFP\_DIRECT\_RECLAIM */
+ public static final int PF_MEMALLOC_NORECLAIM = 0x00800000;
+ /** All allocation requests will inherit \_\_GFP\_NOWARN */
+ public static final int PF_MEMALLOC_NOWARN = 0x01000000;
+ public static final int PF__HOLE__02000000 = 0x02000000;
+ /** Userland is not allowed to meddle with cpus\_mask */
+ public static final int PF_NO_SETAFFINITY = 0x04000000;
+ /** Early kill for mce process policy */
+ public static final int PF_MCE_EARLY = 0x08000000;
+ /** Allocations constrained to zones which allow long term pinning. See memalloc\_pin\_save() */
+ public static final int PF_MEMALLOC_PIN = 0x10000000;
+ /** plug has ts that needs updating */
+ public static final int PF_BLOCK_TS = 0x20000000;
+ public static final int PF__HOLE__40000000 = 0x40000000;
+ /** This thread called freeze\_processes() and should not be frozen */
+ public static final int PF_SUSPEND_TASK = 0x80000000;
+ }
+
/*
* scx_bpf_error() wraps the scx_bpf_error_bstr() kfunc with variadic arguments
* instead of an array of u64. Invoking this macro will cause the scheduler to
diff --git a/bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFBaseMap.java b/bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFBaseMap.java
index 052ed7d..459e511 100644
--- a/bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFBaseMap.java
+++ b/bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFBaseMap.java
@@ -86,8 +86,8 @@ public enum PutMode implements Enum {
* Put a value into the map, updates it if its already there
* Usage in ebpf:
* Update the value in the map with the given key
- * @param key key key if pointery, otherwise an lvalue (like a variable)
- * @param value value if pointery, otherwise an lvalue (like a variable)
+ * @param key key
+ * @param value value
* @return success?
* @see me.bechberger.ebpf.runtime.helpers.BPFHelpers#bpf_map_update_elem(Ptr, Ptr, Ptr, long)
*/
@@ -105,8 +105,8 @@ public boolean put(K key, V value, PutMode mode) {
*
* Usage in ebpf:
* Update the value in the map with the given key
- * @param key key key if pointery, otherwise an lvalue (like a variable)
- * @param value value if pointery, otherwise an lvalue (like a variable)
+ * @param key key
+ * @param value value
* @return success?
* @see me.bechberger.ebpf.runtime.helpers.BPFHelpers#bpf_map_update_elem(Ptr, Ptr, Ptr, long)
*/
@@ -285,7 +285,7 @@ public void forEach(BiConsumer super K, ? super V> action) {
* Obtain a pointer to the element in the map with the given key,
* or {@link Ptr#ofNull()} if the key is not present
*
- * @param key key if pointery, otherwise an lvalue (like a variable)
+ * @param key key
* @return pointer to the value or {@link Ptr#ofNull()}
*
* @see me.bechberger.ebpf.runtime.helpers.BPFHelpers#bpf_map_lookup_elem(Ptr, Ptr)
diff --git a/bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFQueueAndStack.java b/bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFQueueAndStack.java
index 7b5ad97..eadb11d 100644
--- a/bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFQueueAndStack.java
+++ b/bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFQueueAndStack.java
@@ -41,7 +41,7 @@ public abstract class BPFQueueAndStack extends BPFMap {
* Push a value onto the stack or the back of the queue
* Usage in ebpf:
* Update the value in the map with the given key
- * @param value value if pointery, otherwise an lvalue (like a variable)
+ * @param value value
* @return success?
* @see me.bechberger.ebpf.runtime.helpers.BPFHelpers#bpf_map_update_elem(Ptr, Ptr, Ptr, long)
*/
@@ -73,7 +73,7 @@ public boolean push(V value) {
/**
* Get the value from the top of the stack or the front of the queue, in eBPF
*
- * @param value value if pointery, otherwise an lvalue (like a variable)
+ * @param value value
* @return true if the value was peeked, false on error
* @see me.bechberger.ebpf.runtime.helpers.BPFHelpers#bpf_map_peek_elem(Ptr, Ptr)
*/
@@ -102,7 +102,7 @@ public boolean bpf_peek(V value) {
/**
* Pop the value from the top of the stack or the front of the queue, in eBPF
*
- * @param value value if pointery, otherwise an lvalue (like a variable)
+ * @param value value
* @return true if the value was popped, false on error
* @see me.bechberger.ebpf.runtime.helpers.BPFHelpers#bpf_map_pop_elem(Ptr, Ptr)
*/
diff --git a/shared/src/main/java/me/bechberger/ebpf/shared/Util.java b/shared/src/main/java/me/bechberger/ebpf/shared/Util.java
index 2334fca..422c840 100644
--- a/shared/src/main/java/me/bechberger/ebpf/shared/Util.java
+++ b/shared/src/main/java/me/bechberger/ebpf/shared/Util.java
@@ -55,4 +55,5 @@ public static String computeEditDistance(String a, String b) {
public static String getClosestString(String target, Collection options) {
return options.stream().min(Comparator.comparing(a -> computeEditDistance(target, a))).orElse(target);
}
+
}