Linux 5.3 merged BPF bounded loops ( https://lwn.net/Articles/794934/ ). This may allow bpftrace to have implementations of for and while loops (not urgent, since 5.3 in production is some way away!).
( I've got a basic while POC here which supports: i:s:5 { $a = 1; while($a <= 100) { @=count(); $a++ } exit() }. It looks like LLVM9 (fedora 31) does loop unrolling up until $a <= 15 so this might work on older kernels too )
How do we want this to work?
We can go for the C style loops, e.g:
while($a < 10) {@=count(); })for($a=0; $a < 10; $a++) { @=count() }Or we can do something like go does and only implement a for which acts like a while:
for ($a < 10) { @=count(); }for($a=0; $a < 10; $a++) { @=count() }We could also implement a loop like rust does:
loop { ...; if ($a == 10) break; }Or...
I like the C ones but I don't have any strong preferences. In all cases I think it will be useful to implement the continue and break keywords
C style loops seem good to me. Everyone's going to be familiar with them too
break statement has its own issue: https://github.com/iovisor/bpftrace/issues/461, since it can also be used on unroll. continue would also make sense for unroll I think.
I'm wondering if we can also have a for ($a in @map) { ... } to iterate over map keys, arrays, etc. Not sure how viable that would be. This could also be implemented in the future if we want, as it wouldn't be a breaking change for the language.
Moving the discussion from #1066 here
ajor:
I'm not sure why anyone would want a regular loop inside an unrolled one but don't seem the harm in allowing it through (I'm assuming it would just work unless we explicitly block it).
My "issue" with allowing it is that we must also keep it working, write useful semantic and codegen tests etc.
ajor:
Might as well allow jumps since they'll be forward only in an unrolled loop.
mmarchini
breakstatement has its own issue: #461, since it can also be used onunroll.continuewould also make sense forunrollI think.
In my PR (#1066) jumps are currently not allowed (as in_loop is not incremented in unroll). Allowing those shouldn't be too much work so I'll give that a go in a a follow up PR to avoid "bloating" the current.
I'm wondering if we can also have a
for ($a in @map) { ... }to iterate over map keys, arrays, etc. Not sure how viable that would be.
AFAIK there is no way to iterate maps with bpf helpers now, you can only do that in userspace. And from bpf we also have to deal with percpu maps, cpus cannot access each others values. It will probably just be a source of confusion/bugs.
wrt loops inside unroll, we can give a loops inside unrolls are currently not supported. It might be in the future message. We could even create a tracking issue and link it on the message, so people can chime in with examples of why they tried to use it, and if there is enough interest we implement it.
I don't have strong opinions about this though. There's a good chance that, with loops, unroll will be a very niche feature.
ajor:
I'm not sure why anyone would want a regular loop inside an unrolled one but don't seem the harm in allowing it through (I'm assuming it would just work unless we explicitly block it).My "issue" with allowing it is that we must also keep it working, write useful semantic and codegen tests etc.
Sure it should be tested, but since mixing unroll and while is something that would just work otherwise, blocking it would actually add more complexity to bpftrace than allowing it, and would require testing too. Blocking it would also something we'd have to keep working - either that or we eventually just allow mixing the loop types, so we might as well just get it over with now.
I guess my thinking is that even though I don't have a use for it, someone might be doing something crazy one day that means it'd be useful to them and we shouldn't add restrictions to the language if it's reasonable (and easier!) not to.
(Maybe it could help in a program which is just over the BPF verifier's complexity limit while still being within the instruction limit?)
Allowing the loop types to be mixed means we should probably make break and continue work with unroll loops otherwise there could be some confusion about which loop is being broken out of. They shouldn't be too hard to implement though.
Implemented in #1066 .
Most helpful comment
( I've got a basic while POC here which supports:
i:s:5 { $a = 1; while($a <= 100) { @=count(); $a++ } exit() }. It looks like LLVM9 (fedora 31) does loop unrolling up until$a <= 15so this might work on older kernels too )How do we want this to work?
We can go for the C style loops, e.g:
while($a < 10) {@=count(); })for($a=0; $a < 10; $a++) { @=count() }Or we can do something like go does and only implement a
forwhich acts like a while:for ($a < 10) { @=count(); }for($a=0; $a < 10; $a++) { @=count() }We could also implement a
looplike rust does:loop { ...; if ($a == 10) break; }Or...
I like the C ones but I don't have any strong preferences. In all cases I think it will be useful to implement the
continueandbreakkeywords