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)```
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:
[search /examples](https://github.com/iovisor/bcc/search?q=BPF_TABLE+path%3Aexamples&type=Code),
......@@ -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.
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:
[search /examples](https://github.com/iovisor/bcc/search?q=BPF_HASH+path%3Aexamples&type=Code),
......@@ -530,7 +530,16 @@ Examples in situ:
[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)
### 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)```
......@@ -540,7 +549,7 @@ Examples in situ:
[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)
### 12. map.get_stackid()
### 13. map.get_stackid()
Syntax: ```int map.get_stackid(void *ctx, u64 flags)```
......@@ -550,7 +559,7 @@ Examples in situ:
[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)
### 13. map.perf_read()
### 14. map.perf_read()
Syntax: ```u64 map.perf_read(u32 cpu)```
......
......@@ -57,7 +57,7 @@ int classify_neighbor(struct __sk_buff *skb) {
u32 sip = ip->src;
struct ipkey key = {.client_ip=sip};
int val = 1;
learned_ips.update(&key, &val);
learned_ips.insert(&key, &val);
goto EOP;
}
EOP:
......
......@@ -45,6 +45,7 @@ struct _name##_table_t { \
_leaf_type * (*lookup) (_key_type *); \
_leaf_type * (*lookup_or_init) (_key_type *, _leaf_type *); \
int (*update) (_key_type *, _leaf_type *); \
int (*insert) (_key_type *, _leaf_type *); \
int (*delete) (_key_type *); \
void (*call) (void *, int index); \
void (*increment) (_key_type); \
......
......@@ -369,12 +369,10 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
}
string fd = to_string(table_it->fd);
string prefix, suffix;
string map_update_policy = "BPF_ANY";
string txt;
auto rewrite_start = Call->getLocStart();
auto rewrite_end = Call->getLocEnd();
if (memb_name == "lookup_or_init") {
map_update_policy = "BPF_NOEXIST";
string name = Ref->getDecl()->getName();
string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange()));
string arg1 = rewriter_.getRewrittenText(expansionRange(Call->getArg(1)->getSourceRange()));
......@@ -382,7 +380,7 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")";
txt = "({typeof(" + name + ".leaf) *leaf = " + lookup + ", " + arg0 + "); ";
txt += "if (!leaf) {";
txt += " " + update + ", " + arg0 + ", " + arg1 + ", " + map_update_policy + ");";
txt += " " + update + ", " + arg0 + ", " + arg1 + ", BPF_NOEXIST);";
txt += " leaf = " + lookup + ", " + arg0 + ");";
txt += " if (!leaf) return 0;";
txt += "}";
......@@ -433,7 +431,13 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
suffix = ")";
} else if (memb_name == "update") {
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") {
prefix = "bpf_map_delete_elem";
suffix = ")";
......
......@@ -3,7 +3,7 @@
# Licensed under the Apache License, Version 2.0 (the "License")
from bcc import BPF
import ctypes
import ctypes as ct
from unittest import main, TestCase
import os
import sys
......@@ -113,7 +113,7 @@ BPF_HASH(stats, int, struct { u32 a[3]; u32 b; }, 10);
t = b.get_table("stats")
s1 = t.key_sprintf(t.Key(2))
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 }")
l = t.leaf_scanf(s2)
self.assertEqual(l.a[0], 1)
......@@ -330,8 +330,7 @@ BPF_ARRAY(t2, struct list *, 1);
BPF_ARRAY(t3, union emptyu, 1);
"""
b = BPF(text=text)
import ctypes
self.assertEqual(ctypes.sizeof(b["t3"].Leaf), 8)
self.assertEqual(ct.sizeof(b["t3"].Leaf), 8)
def test_cflags(self):
text = """
......@@ -485,5 +484,25 @@ int trace_entry(struct pt_regs *ctx) {
self.assertIn(expectedWarn, output)
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__":
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