Skip to content

Commit

Permalink
Improve $pointery handling
Browse files Browse the repository at this point in the history
  • Loading branch information
parttimenerd committed Oct 22, 2024
1 parent 3c6ed63 commit e588f9c
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* <li>{@code $strlen$this}: length of the {@code $this} interpreted as a string literal</li>
* <li>{@code $strlen$argN}: length of the {@code $argN} interpreted as a string literal</li>
* <li>{@code $str$argN}: Asserts that {@code $argN} is a string literal</li>
* <li>{@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</li>
* <li>{@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</li>
* </ul>
* <p>
* Example: {@snippet :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,6 +24,32 @@
*/
public record MethodTemplate(String methodName, String raw, List<TemplatePart> parts) {

public class NewVariableContext {

private record NewVariable(String name, String value) {
}

private List<NewVariable> 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<CAST.Declarator> typeArguments,
Expand All @@ -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));
}
Expand All @@ -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(", "));
Expand All @@ -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");
}
Expand All @@ -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());
}
Expand All @@ -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));
}
Expand All @@ -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");
}
Expand All @@ -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");
}
Expand All @@ -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);
}
}
}
Expand Down Expand Up @@ -304,7 +339,9 @@ static MethodTemplate parse(String methodName, String template, @Nullable Method
}

public Expression call(CallArgs args) {
List<String> renderedParts = parts.stream().map(part -> part.render(new CallProps(methodName, args)))
NewVariableContext context = new NewVariableContext();
List<String> 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++) {
Expand All @@ -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()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,8 @@ private List<Path> cachedFiles() {
throw new RuntimeException(e);
}
}

public Path getCacheFolder() {
return cacheFolder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ private byte[] compile(CombinedCode code, Path ebpfFile) {
try {
var tempFile = Files.createTempFile("ebpf", ".o");
tempFile.toFile().deleteOnExit();
List<String> command = List.of(newestClang, "-O2", "-g", "-target", "bpf", "-c", "-o",
List<String> 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",
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ public void enterOpenat2(int dfd, String filename, Ptr<open_how> 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);
}
Expand Down
64 changes: 64 additions & 0 deletions bpf/src/main/java/me/bechberger/ebpf/bpf/Scheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions bpf/src/main/java/me/bechberger/ebpf/bpf/map/BPFBaseMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ public enum PutMode implements Enum<PutMode> {
* Put a value into the map, updates it if its already there
* <p>Usage in ebpf:</p>
* 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)
*/
Expand All @@ -105,8 +105,8 @@ public boolean put(K key, V value, PutMode mode) {
*
* <p>Usage in ebpf:</p>
* 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)
*/
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit e588f9c

Please sign in to comment.