tools/bpf: support __int128 in bpftool map pretty dumper
authorYonghong Song <yhs@fb.com>
Wed, 16 Jan 2019 01:07:52 +0000 (17:07 -0800)
committerDaniel Borkmann <daniel@iogearbox.net>
Wed, 16 Jan 2019 21:53:44 +0000 (22:53 +0100)
For formatted output, currently when json is enabled, the decimal
number is required. Similar to kernel bpffs printout,
for int128 numbers, only hex numbers are dumped, which are
quoted as strings.

The below is an example to show plain and json pretty print
based on the map in test_btf pretty print test.

  $ bpftool m s
  75: hash  name pprint_test_has  flags 0x0
        key 4B  value 112B  max_entries 4  memlock 4096B
  $ bpftool m d id 75
  ......
    {
        "key": 3,
        "value": {
            "ui32": 3,
            "ui16": 0,
            "si32": -3,
            "unused_bits2a": 0x3,
            "bits28": 0x3,
            "unused_bits2b": 0x3,
            "": {
                "ui64": 3,
                "ui8a": [3,0,0,0,0,0,0,0
                ]
            },
            "aenum": 3,
            "ui32b": 4,
            "bits2c": 0x1,
            "si128a": 0x3,
            "si128b": 0xfffffffd,
            "bits3": 0x3,
            "bits80": 0x10000000000000003,
            "ui128": 0x20000000000000003
        }
    },
  ......

  $ bptfool -p -j m d id 75
  ......
  {
        "key": ["0x03","0x00","0x00","0x00"
        ],
        "value": ["0x03","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0xfd","0xff","0xff","0xff","0x0f","0x00","0x00","0xc0",
                  "0x03","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0x03","0x00","0x00","0x00","0x04","0x00","0x00","0x00",
                  "0x01","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0x00","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0x03","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0x00","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0xfd","0xff","0xff","0xff","0x00","0x00","0x00","0x00",
                  "0x00","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0x1b","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0x08","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0x03","0x00","0x00","0x00","0x00","0x00","0x00","0x00",
                  "0x02","0x00","0x00","0x00","0x00","0x00","0x00","0x00"
        ],
        "formatted": {
            "key": 3,
            "value": {
                "ui32": 3,
                "ui16": 0,
                "si32": -3,
                "unused_bits2a": "0x3",
                "bits28": "0x3",
                "unused_bits2b": "0x3",
                "": {
                    "ui64": 3,
                    "ui8a": [3,0,0,0,0,0,0,0
                    ]
                },
                "aenum": 3,
                "ui32b": 4,
                "bits2c": "0x1",
                "si128a": "0x3",
                "si128b": "0xfffffffd",
                "bits3": "0x3",
                "bits80": "0x10000000000000003",
                "ui128": "0x20000000000000003"
            }
        }
    }
  ......

Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
tools/bpf/bpftool/btf_dumper.c

index 6ba5f567a9d86778df796b3864c33b646d1db3de..e63bce0755ebe605870af4b6b6e9322006f64a3d 100644 (file)
@@ -73,35 +73,104 @@ static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id,
        return ret;
 }
 
+static void btf_int128_print(json_writer_t *jw, const void *data,
+                            bool is_plain_text)
+{
+       /* data points to a __int128 number.
+        * Suppose
+        *     int128_num = *(__int128 *)data;
+        * The below formulas shows what upper_num and lower_num represents:
+        *     upper_num = int128_num >> 64;
+        *     lower_num = int128_num & 0xffffffffFFFFFFFFULL;
+        */
+       __u64 upper_num, lower_num;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       upper_num = *(__u64 *)data;
+       lower_num = *(__u64 *)(data + 8);
+#else
+       upper_num = *(__u64 *)(data + 8);
+       lower_num = *(__u64 *)data;
+#endif
+
+       if (is_plain_text) {
+               if (upper_num == 0)
+                       jsonw_printf(jw, "0x%llx", lower_num);
+               else
+                       jsonw_printf(jw, "0x%llx%016llx", upper_num, lower_num);
+       } else {
+               if (upper_num == 0)
+                       jsonw_printf(jw, "\"0x%llx\"", lower_num);
+               else
+                       jsonw_printf(jw, "\"0x%llx%016llx\"", upper_num, lower_num);
+       }
+}
+
+static void btf_int128_shift(__u64 *print_num, u16 left_shift_bits,
+                            u16 right_shift_bits)
+{
+       __u64 upper_num, lower_num;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       upper_num = print_num[0];
+       lower_num = print_num[1];
+#else
+       upper_num = print_num[1];
+       lower_num = print_num[0];
+#endif
+
+       /* shake out un-needed bits by shift/or operations */
+       if (left_shift_bits >= 64) {
+               upper_num = lower_num << (left_shift_bits - 64);
+               lower_num = 0;
+       } else {
+               upper_num = (upper_num << left_shift_bits) |
+                           (lower_num >> (64 - left_shift_bits));
+               lower_num = lower_num << left_shift_bits;
+       }
+
+       if (right_shift_bits >= 64) {
+               lower_num = upper_num >> (right_shift_bits - 64);
+               upper_num = 0;
+       } else {
+               lower_num = (lower_num >> right_shift_bits) |
+                           (upper_num << (64 - right_shift_bits));
+               upper_num = upper_num >> right_shift_bits;
+       }
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       print_num[0] = upper_num;
+       print_num[1] = lower_num;
+#else
+       print_num[0] = lower_num;
+       print_num[1] = upper_num;
+#endif
+}
+
 static void btf_dumper_bitfield(__u32 nr_bits, __u8 bit_offset,
                                const void *data, json_writer_t *jw,
                                bool is_plain_text)
 {
        int left_shift_bits, right_shift_bits;
+       __u64 print_num[2] = {};
        int bytes_to_copy;
        int bits_to_copy;
-       __u64 print_num;
 
        bits_to_copy = bit_offset + nr_bits;
        bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
 
-       print_num = 0;
-       memcpy(&print_num, data, bytes_to_copy);
+       memcpy(print_num, data, bytes_to_copy);
 #if defined(__BIG_ENDIAN_BITFIELD)
        left_shift_bits = bit_offset;
 #elif defined(__LITTLE_ENDIAN_BITFIELD)
-       left_shift_bits = 64 - bits_to_copy;
+       left_shift_bits = 128 - bits_to_copy;
 #else
 #error neither big nor little endian
 #endif
-       right_shift_bits = 64 - nr_bits;
+       right_shift_bits = 128 - nr_bits;
 
-       print_num <<= left_shift_bits;
-       print_num >>= right_shift_bits;
-       if (is_plain_text)
-               jsonw_printf(jw, "0x%llx", print_num);
-       else
-               jsonw_printf(jw, "%llu", print_num);
+       btf_int128_shift(print_num, left_shift_bits, right_shift_bits);
+       btf_int128_print(jw, print_num, is_plain_text);
 }
 
 
@@ -113,7 +182,7 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
        int total_bits_offset;
 
        /* bits_offset is at most 7.
-        * BTF_INT_OFFSET() cannot exceed 64 bits.
+        * BTF_INT_OFFSET() cannot exceed 128 bits.
         */
        total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
        data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
@@ -139,6 +208,11 @@ static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
                return 0;
        }
 
+       if (nr_bits == 128) {
+               btf_int128_print(jw, data, is_plain_text);
+               return 0;
+       }
+
        switch (BTF_INT_ENCODING(*int_type)) {
        case 0:
                if (BTF_INT_BITS(*int_type) == 64)