cc: Support for __data_loc tracepoint fields
authorSasha Goldshtein <goldshtn@gmail.com>
Wed, 1 Feb 2017 06:58:59 +0000 (06:58 +0000)
committerSasha Goldshtein <goldshtn@gmail.com>
Wed, 1 Feb 2017 09:40:10 +0000 (09:40 +0000)
`__data_loc` fields are dynamically sized by the kernel at
runtime. The field data follows the tracepoint structure entry,
and needs to be extracted in a special way. The `__data_loc` field
itself is a 32-bit value that consists of two 16-bit parts: the
high 16 bits are the length of the data, and the low 16 bits are
the offset of the data from the beginning of the tracepoint
structure. From a cursory look, there are >200 tracepoints in
recent kernels that have this kind of field.

This patch fixes `tp_frontend_action.cc` to recognize and emit
`__data_loc` fields correctly, as 32-bit opaque fields. Then, it
introduces two helper macros:

`TP_DATA_LOC_READ(dst, field)` reads from `args->field` by finding
the right offset and length and emitting the `bpf_probe_read`
required to fetch the data. This will only work with new kernels.

`TP_DATA_LOC_READ_CONST(dst, field, length)` takes a user-specified
length rather than finding it from `args->field`. This will work
on older kernels, where the BPF verifier doesn't allow non-constant
sizes to be passed to `bpf_probe_read`.

src/cc/export/helpers.h
src/cc/frontends/clang/tp_frontend_action.cc

index 575ba8e..ae8f523 100644 (file)
@@ -499,5 +499,18 @@ int bpf_usdt_readarg_p(int argc, struct pt_regs *ctx, void *buf, u64 len) asm("l
 #define TRACEPOINT_PROBE(category, event) \
 int tracepoint__##category##__##event(struct tracepoint__##category##__##event *args)
 
+#define TP_DATA_LOC_READ_CONST(dst, field, length)                        \
+        do {                                                              \
+            short __offset = args->data_loc_##field & 0xFFFF;             \
+            bpf_probe_read((void *)dst, length, (char *)args + __offset); \
+        } while (0);
+
+#define TP_DATA_LOC_READ(dst, field)                                        \
+        do {                                                                \
+            short __offset = args->data_loc_##field & 0xFFFF;               \
+            short __length = args->data_loc_##field >> 16;                  \
+            bpf_probe_read((void *)dst, __length, (char *)args + __offset); \
+        } while (0);
+
 #endif
 )********"
index fb21ca6..4654b58 100644 (file)
@@ -48,35 +48,42 @@ TracepointTypeVisitor::TracepointTypeVisitor(ASTContext &C, Rewriter &rewriter)
     : C(C), diag_(C.getDiagnostics()), rewriter_(rewriter), out_(llvm::errs()) {
 }
 
-static inline bool _is_valid_field(string const& line,
-                                   string& field_type,
-                                   string& field_name) {
+enum class field_kind_t {
+    common,
+    data_loc,
+    regular,
+    invalid
+};
+
+static inline field_kind_t _get_field_kind(string const& line,
+                                           string& field_type,
+                                           string& field_name) {
   auto field_pos = line.find("field:");
   if (field_pos == string::npos)
-    return false;
+    return field_kind_t::invalid;
 
   auto semi_pos = line.find(';', field_pos);
   if (semi_pos == string::npos)
-    return false;
+    return field_kind_t::invalid;
 
   auto size_pos = line.find("size:", semi_pos);
   if (size_pos == string::npos)
-    return false;
+    return field_kind_t::invalid;
 
   auto field = line.substr(field_pos + 6/*"field:"*/,
                            semi_pos - field_pos - 6);
   auto pos = field.find_last_of("\t ");
   if (pos == string::npos)
-    return false;
+    return field_kind_t::invalid;
 
   field_type = field.substr(0, pos);
   field_name = field.substr(pos + 1);
   if (field_type.find("__data_loc") != string::npos)
-    return false;
+    return field_kind_t::data_loc;
   if (field_name.find("common_") == 0)
-    return false;
+    return field_kind_t::common;
 
-  return true;
+  return field_kind_t::regular;
 }
 
 string TracepointTypeVisitor::GenerateTracepointStruct(
@@ -91,9 +98,17 @@ string TracepointTypeVisitor::GenerateTracepointStruct(
   tp_struct += "\tu64 __do_not_use__;\n";
   for (string line; getline(input, line); ) {
     string field_type, field_name;
-    if (!_is_valid_field(line, field_type, field_name))
-      continue;
-    tp_struct += "\t" + field_type + " " + field_name + ";\n";
+    switch (_get_field_kind(line, field_type, field_name)) {
+    case field_kind_t::invalid:
+    case field_kind_t::common:
+        continue;
+    case field_kind_t::data_loc:
+        tp_struct += "\tint data_loc_" + field_name + ";\n";
+        break;
+    case field_kind_t::regular:
+        tp_struct += "\t" + field_type + " " + field_name + ";\n";
+        break;
+    }
   }
 
   tp_struct += "};\n";