avoid large map memory allocation in userspace
authorYonghong Song <yhs@fb.com>
Wed, 16 Aug 2017 23:18:22 +0000 (16:18 -0700)
committerYonghong Song <yhs@fb.com>
Thu, 17 Aug 2017 00:26:11 +0000 (17:26 -0700)
In bcc, internal BPF_F_TABLE defines a structure to
contain all the table information for later easy
extraction. A global structure will be defined
with this type. Note that this structure will be
allocated by LLVM during compilation.

In the table structure, one of field is:
   _leaf_type data[_max_entries]

If the _leaf_type and _max_entries are big,
significant memory will be consumed. A big
_leaf_type size example is for BPF_STACK_TRACE map
with 127*8=1016 bytes. If max_entries is bigger
as well, significant amount of memory will be
consumed by LLVM.

This patch replaces
  _leaf_type data[_max_entries]
to
  unsigned ing max_entries

The detail of a test example can be found in issue #1291.
For the example in #1291, without this patch, for a
BPF_STACK_TRACE map with 1M entries, the RSS is roughly
3GB (roughly 3KB per entry). With this patch, it is 5.8MB.

Signed-off-by: Yonghong Song <yhs@fb.com>
src/cc/export/helpers.h
src/cc/frontends/clang/b_frontend_action.cc

index c9ad1439d708a389cf4d1e9832f60cbed079674c..4b982ce65d38ca5fe3f7ab6bc8709b43ecd61e71 100644 (file)
@@ -50,11 +50,11 @@ struct _name##_table_t { \
   void (*call) (void *, int index); \
   void (*increment) (_key_type); \
   int (*get_stackid) (void *, u64); \
-  _leaf_type data[_max_entries]; \
+  u32 max_entries; \
   int flags; \
 }; \
 __attribute__((section("maps/" _table_type))) \
-struct _name##_table_t _name = { .flags = (_flags) }
+struct _name##_table_t _name = { .flags = (_flags), .max_entries = (_max_entries) }
 
 #define BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries) \
 BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, 0)
@@ -84,10 +84,10 @@ struct _name##_table_t { \
   /* map.perf_submit(ctx, data, data_size) */ \
   int (*perf_submit) (void *, void *, u32); \
   int (*perf_submit_skb) (void *, u32, void *, u32); \
-  u32 data[0]; \
+  u32 max_entries; \
 }; \
 __attribute__((section("maps/perf_output"))) \
-struct _name##_table_t _name
+struct _name##_table_t _name = { .max_entries = 0 }
 
 // Table for reading hw perf cpu counters
 #define BPF_PERF_ARRAY(_name, _max_entries) \
@@ -96,10 +96,10 @@ struct _name##_table_t { \
   u32 leaf; \
   /* counter = map.perf_read(index) */ \
   u64 (*perf_read) (int); \
-  u32 data[_max_entries]; \
+  u32 max_entries; \
 }; \
 __attribute__((section("maps/perf_array"))) \
-struct _name##_table_t _name
+struct _name##_table_t _name = { .max_entries = (_max_entries) }
 
 #define BPF_HASH1(_name) \
   BPF_TABLE("hash", u64, u64, _name, 10240)
index 39047e1f63a62d4d540d0ef3ebe1627837c6b3e5..b2f9206d34ebcf5dc407f979899b55ae432916c0 100644 (file)
@@ -633,8 +633,14 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
         }
         table.leaf_size = sz;
         leaf_type = F->getType();
-      } else if (F->getName() == "data") {
-        table.max_entries = sz / table.leaf_size;
+      } else if (F->getName() == "max_entries") {
+        unsigned idx = F->getFieldIndex();
+        if (auto I = dyn_cast_or_null<InitListExpr>(Decl->getInit())) {
+          llvm::APSInt res;
+          if (I->getInit(idx)->EvaluateAsInt(res, C)) {
+            table.max_entries = res.getExtValue();
+          }
+        }
       } else if (F->getName() == "flags") {
         unsigned idx = F->getFieldIndex();
         if (auto I = dyn_cast_or_null<InitListExpr>(Decl->getInit())) {