Commit 6ceb3291 authored by Paul Chaignon's avatar Paul Chaignon Committed by Sasha Goldshtein

map.insert bcc helper to expose the BPF_NOEXIST flag (#1085)

Inserts element in map only if it does not already exist. Throws a
warning during rewriter step if used on a BPF array.
parent 9e718f5a
...@@ -375,7 +375,7 @@ Syntax: ```BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries)``` ...@@ -375,7 +375,7 @@ Syntax: ```BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries)```
Creates a map named ```_name```. Most of the time this will be used via higher-level macros, like BPF_HASH, BPF_HIST, etc. Creates a map named ```_name```. Most of the time this will be used via higher-level macros, like BPF_HASH, BPF_HIST, etc.
Methods (covered later): map.lookup(), map.lookup_or_init(), map.delete(), map.update(), map.increment(). Methods (covered later): map.lookup(), map.lookup_or_init(), map.delete(), map.update(), map.insert(), map.increment().
Examples in situ: Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=BPF_TABLE+path%3Aexamples&type=Code), [search /examples](https://github.com/iovisor/bcc/search?q=BPF_TABLE+path%3Aexamples&type=Code),
...@@ -397,7 +397,7 @@ BPF_HASH(start, struct request *); ...@@ -397,7 +397,7 @@ BPF_HASH(start, struct request *);
This creates a hash named ```start``` where the key is a ```struct request *```, and the value defaults to u64. This hash is used by the disksnoop.py example for saving timestamps for each I/O request, where the key is the pointer to struct request, and the value is the timestamp. This creates a hash named ```start``` where the key is a ```struct request *```, and the value defaults to u64. This hash is used by the disksnoop.py example for saving timestamps for each I/O request, where the key is the pointer to struct request, and the value is the timestamp.
Methods (covered later): map.lookup(), map.lookup_or_init(), map.delete(), map.update(), map.increment(). Methods (covered later): map.lookup(), map.lookup_or_init(), map.delete(), map.update(), map.insert(), map.increment().
Examples in situ: Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=BPF_HASH+path%3Aexamples&type=Code), [search /examples](https://github.com/iovisor/bcc/search?q=BPF_HASH+path%3Aexamples&type=Code),
...@@ -530,7 +530,16 @@ Examples in situ: ...@@ -530,7 +530,16 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=update+path%3Aexamples&type=Code), [search /examples](https://github.com/iovisor/bcc/search?q=update+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=update+path%3Atools&type=Code) [search /tools](https://github.com/iovisor/bcc/search?q=update+path%3Atools&type=Code)
### 11. map.increment() ### 11. map.insert()
Syntax: ```map.insert(&key, &val)```
Associate the value in the second argument to the key, only if there was no previous value.
Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=insert+path%3Aexamples&type=Code)
### 12. map.increment()
Syntax: ```map.increment(key)``` Syntax: ```map.increment(key)```
...@@ -540,7 +549,7 @@ Examples in situ: ...@@ -540,7 +549,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=increment+path%3Aexamples&type=Code), [search /examples](https://github.com/iovisor/bcc/search?q=increment+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=increment+path%3Atools&type=Code) [search /tools](https://github.com/iovisor/bcc/search?q=increment+path%3Atools&type=Code)
### 12. map.get_stackid() ### 13. map.get_stackid()
Syntax: ```int map.get_stackid(void *ctx, u64 flags)``` Syntax: ```int map.get_stackid(void *ctx, u64 flags)```
...@@ -550,7 +559,7 @@ Examples in situ: ...@@ -550,7 +559,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=get_stackid+path%3Aexamples&type=Code), [search /examples](https://github.com/iovisor/bcc/search?q=get_stackid+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=get_stackid+path%3Atools&type=Code) [search /tools](https://github.com/iovisor/bcc/search?q=get_stackid+path%3Atools&type=Code)
### 13. map.perf_read() ### 14. map.perf_read()
Syntax: ```u64 map.perf_read(u32 cpu)``` Syntax: ```u64 map.perf_read(u32 cpu)```
......
...@@ -57,7 +57,7 @@ int classify_neighbor(struct __sk_buff *skb) { ...@@ -57,7 +57,7 @@ int classify_neighbor(struct __sk_buff *skb) {
u32 sip = ip->src; u32 sip = ip->src;
struct ipkey key = {.client_ip=sip}; struct ipkey key = {.client_ip=sip};
int val = 1; int val = 1;
learned_ips.update(&key, &val); learned_ips.insert(&key, &val);
goto EOP; goto EOP;
} }
EOP: EOP:
......
...@@ -45,6 +45,7 @@ struct _name##_table_t { \ ...@@ -45,6 +45,7 @@ struct _name##_table_t { \
_leaf_type * (*lookup) (_key_type *); \ _leaf_type * (*lookup) (_key_type *); \
_leaf_type * (*lookup_or_init) (_key_type *, _leaf_type *); \ _leaf_type * (*lookup_or_init) (_key_type *, _leaf_type *); \
int (*update) (_key_type *, _leaf_type *); \ int (*update) (_key_type *, _leaf_type *); \
int (*insert) (_key_type *, _leaf_type *); \
int (*delete) (_key_type *); \ int (*delete) (_key_type *); \
void (*call) (void *, int index); \ void (*call) (void *, int index); \
void (*increment) (_key_type); \ void (*increment) (_key_type); \
......
...@@ -369,12 +369,10 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { ...@@ -369,12 +369,10 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
} }
string fd = to_string(table_it->fd); string fd = to_string(table_it->fd);
string prefix, suffix; string prefix, suffix;
string map_update_policy = "BPF_ANY";
string txt; string txt;
auto rewrite_start = Call->getLocStart(); auto rewrite_start = Call->getLocStart();
auto rewrite_end = Call->getLocEnd(); auto rewrite_end = Call->getLocEnd();
if (memb_name == "lookup_or_init") { if (memb_name == "lookup_or_init") {
map_update_policy = "BPF_NOEXIST";
string name = Ref->getDecl()->getName(); string name = Ref->getDecl()->getName();
string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange())); string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange()));
string arg1 = rewriter_.getRewrittenText(expansionRange(Call->getArg(1)->getSourceRange())); string arg1 = rewriter_.getRewrittenText(expansionRange(Call->getArg(1)->getSourceRange()));
...@@ -382,7 +380,7 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { ...@@ -382,7 +380,7 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")"; string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")";
txt = "({typeof(" + name + ".leaf) *leaf = " + lookup + ", " + arg0 + "); "; txt = "({typeof(" + name + ".leaf) *leaf = " + lookup + ", " + arg0 + "); ";
txt += "if (!leaf) {"; txt += "if (!leaf) {";
txt += " " + update + ", " + arg0 + ", " + arg1 + ", " + map_update_policy + ");"; txt += " " + update + ", " + arg0 + ", " + arg1 + ", BPF_NOEXIST);";
txt += " leaf = " + lookup + ", " + arg0 + ");"; txt += " leaf = " + lookup + ", " + arg0 + ");";
txt += " if (!leaf) return 0;"; txt += " if (!leaf) return 0;";
txt += "}"; txt += "}";
...@@ -433,7 +431,13 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { ...@@ -433,7 +431,13 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
suffix = ")"; suffix = ")";
} else if (memb_name == "update") { } else if (memb_name == "update") {
prefix = "bpf_map_update_elem"; prefix = "bpf_map_update_elem";
suffix = ", " + map_update_policy + ")"; suffix = ", BPF_ANY)";
} else if (memb_name == "insert") {
if (table_it->type == BPF_MAP_TYPE_ARRAY) {
warning(Call->getLocStart(), "all element of an array already exist; insert() will have no effect");
}
prefix = "bpf_map_update_elem";
suffix = ", BPF_NOEXIST)";
} else if (memb_name == "delete") { } else if (memb_name == "delete") {
prefix = "bpf_map_delete_elem"; prefix = "bpf_map_delete_elem";
suffix = ")"; suffix = ")";
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# Licensed under the Apache License, Version 2.0 (the "License") # Licensed under the Apache License, Version 2.0 (the "License")
from bcc import BPF from bcc import BPF
import ctypes import ctypes as ct
from unittest import main, TestCase from unittest import main, TestCase
import os import os
import sys import sys
...@@ -113,7 +113,7 @@ BPF_HASH(stats, int, struct { u32 a[3]; u32 b; }, 10); ...@@ -113,7 +113,7 @@ BPF_HASH(stats, int, struct { u32 a[3]; u32 b; }, 10);
t = b.get_table("stats") t = b.get_table("stats")
s1 = t.key_sprintf(t.Key(2)) s1 = t.key_sprintf(t.Key(2))
self.assertEqual(s1, b"0x2") self.assertEqual(s1, b"0x2")
s2 = t.leaf_sprintf(t.Leaf((ctypes.c_uint * 3)(1,2,3), 4)) s2 = t.leaf_sprintf(t.Leaf((ct.c_uint * 3)(1,2,3), 4))
self.assertEqual(s2, b"{ [ 0x1 0x2 0x3 ] 0x4 }") self.assertEqual(s2, b"{ [ 0x1 0x2 0x3 ] 0x4 }")
l = t.leaf_scanf(s2) l = t.leaf_scanf(s2)
self.assertEqual(l.a[0], 1) self.assertEqual(l.a[0], 1)
...@@ -330,8 +330,7 @@ BPF_ARRAY(t2, struct list *, 1); ...@@ -330,8 +330,7 @@ BPF_ARRAY(t2, struct list *, 1);
BPF_ARRAY(t3, union emptyu, 1); BPF_ARRAY(t3, union emptyu, 1);
""" """
b = BPF(text=text) b = BPF(text=text)
import ctypes self.assertEqual(ct.sizeof(b["t3"].Leaf), 8)
self.assertEqual(ctypes.sizeof(b["t3"].Leaf), 8)
def test_cflags(self): def test_cflags(self):
text = """ text = """
...@@ -485,5 +484,25 @@ int trace_entry(struct pt_regs *ctx) { ...@@ -485,5 +484,25 @@ int trace_entry(struct pt_regs *ctx) {
self.assertIn(expectedWarn, output) self.assertIn(expectedWarn, output)
r.close() r.close()
def test_map_insert(self):
text = """
BPF_HASH(dummy);
void do_trace(struct pt_regs *ctx) {
u64 key = 0, val = 2;
dummy.insert(&key, &val);
key = 1;
dummy.update(&key, &val);
}
"""
b = BPF(text=text)
c_val = ct.c_ulong(1)
b["dummy"][ct.c_ulong(0)] = c_val
b["dummy"][ct.c_ulong(1)] = c_val
b.attach_kprobe(event="sys_sync", fn_name="do_trace")
libc = ct.CDLL("libc.so.6")
libc.sync()
self.assertEqual(1, b["dummy"][ct.c_ulong(0)].value)
self.assertEqual(2, b["dummy"][ct.c_ulong(1)].value)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment