Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* Copyright (c) 2018 Facebook */ |
| 3 | |
| 4 | #include <ctype.h> |
| 5 | #include <stdio.h> /* for (FILE *) used by json_writer */ |
| 6 | #include <string.h> |
| 7 | #include <asm/byteorder.h> |
| 8 | #include <linux/bitops.h> |
| 9 | #include <linux/btf.h> |
| 10 | #include <linux/err.h> |
| 11 | |
| 12 | #include "btf.h" |
| 13 | #include "json_writer.h" |
| 14 | #include "main.h" |
| 15 | |
| 16 | #define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1) |
| 17 | #define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK) |
| 18 | #define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3) |
| 19 | #define BITS_ROUNDUP_BYTES(bits) \ |
| 20 | (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits)) |
| 21 | |
| 22 | static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id, |
| 23 | __u8 bit_offset, const void *data); |
| 24 | |
| 25 | static void btf_dumper_ptr(const void *data, json_writer_t *jw, |
| 26 | bool is_plain_text) |
| 27 | { |
| 28 | if (is_plain_text) |
| 29 | jsonw_printf(jw, "%p", *(unsigned long *)data); |
| 30 | else |
| 31 | jsonw_printf(jw, "%u", *(unsigned long *)data); |
| 32 | } |
| 33 | |
| 34 | static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id, |
| 35 | const void *data) |
| 36 | { |
| 37 | int actual_type_id; |
| 38 | |
| 39 | actual_type_id = btf__resolve_type(d->btf, type_id); |
| 40 | if (actual_type_id < 0) |
| 41 | return actual_type_id; |
| 42 | |
| 43 | return btf_dumper_do_type(d, actual_type_id, 0, data); |
| 44 | } |
| 45 | |
| 46 | static void btf_dumper_enum(const void *data, json_writer_t *jw) |
| 47 | { |
| 48 | jsonw_printf(jw, "%d", *(int *)data); |
| 49 | } |
| 50 | |
| 51 | static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id, |
| 52 | const void *data) |
| 53 | { |
| 54 | const struct btf_type *t = btf__type_by_id(d->btf, type_id); |
| 55 | struct btf_array *arr = (struct btf_array *)(t + 1); |
| 56 | long long elem_size; |
| 57 | int ret = 0; |
| 58 | __u32 i; |
| 59 | |
| 60 | elem_size = btf__resolve_size(d->btf, arr->type); |
| 61 | if (elem_size < 0) |
| 62 | return elem_size; |
| 63 | |
| 64 | jsonw_start_array(d->jw); |
| 65 | for (i = 0; i < arr->nelems; i++) { |
| 66 | ret = btf_dumper_do_type(d, arr->type, 0, |
| 67 | data + i * elem_size); |
| 68 | if (ret) |
| 69 | break; |
| 70 | } |
| 71 | |
| 72 | jsonw_end_array(d->jw); |
| 73 | return ret; |
| 74 | } |
| 75 | |
| 76 | static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset, |
| 77 | const void *data, json_writer_t *jw, |
| 78 | bool is_plain_text) |
| 79 | { |
| 80 | int left_shift_bits, right_shift_bits; |
| 81 | int nr_bits = BTF_INT_BITS(int_type); |
| 82 | int total_bits_offset; |
| 83 | int bytes_to_copy; |
| 84 | int bits_to_copy; |
| 85 | __u64 print_num; |
| 86 | |
| 87 | total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type); |
| 88 | data += BITS_ROUNDDOWN_BYTES(total_bits_offset); |
| 89 | bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset); |
| 90 | bits_to_copy = bit_offset + nr_bits; |
| 91 | bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy); |
| 92 | |
| 93 | print_num = 0; |
| 94 | memcpy(&print_num, data, bytes_to_copy); |
| 95 | #if defined(__BIG_ENDIAN_BITFIELD) |
| 96 | left_shift_bits = bit_offset; |
| 97 | #elif defined(__LITTLE_ENDIAN_BITFIELD) |
| 98 | left_shift_bits = 64 - bits_to_copy; |
| 99 | #else |
| 100 | #error neither big nor little endian |
| 101 | #endif |
| 102 | right_shift_bits = 64 - nr_bits; |
| 103 | |
| 104 | print_num <<= left_shift_bits; |
| 105 | print_num >>= right_shift_bits; |
| 106 | if (is_plain_text) |
| 107 | jsonw_printf(jw, "0x%llx", print_num); |
| 108 | else |
| 109 | jsonw_printf(jw, "%llu", print_num); |
| 110 | } |
| 111 | |
| 112 | static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset, |
| 113 | const void *data, json_writer_t *jw, |
| 114 | bool is_plain_text) |
| 115 | { |
| 116 | __u32 *int_type; |
| 117 | __u32 nr_bits; |
| 118 | |
| 119 | int_type = (__u32 *)(t + 1); |
| 120 | nr_bits = BTF_INT_BITS(*int_type); |
| 121 | /* if this is bit field */ |
| 122 | if (bit_offset || BTF_INT_OFFSET(*int_type) || |
| 123 | BITS_PER_BYTE_MASKED(nr_bits)) { |
| 124 | btf_dumper_int_bits(*int_type, bit_offset, data, jw, |
| 125 | is_plain_text); |
| 126 | return 0; |
| 127 | } |
| 128 | |
| 129 | switch (BTF_INT_ENCODING(*int_type)) { |
| 130 | case 0: |
| 131 | if (BTF_INT_BITS(*int_type) == 64) |
| 132 | jsonw_printf(jw, "%lu", *(__u64 *)data); |
| 133 | else if (BTF_INT_BITS(*int_type) == 32) |
| 134 | jsonw_printf(jw, "%u", *(__u32 *)data); |
| 135 | else if (BTF_INT_BITS(*int_type) == 16) |
| 136 | jsonw_printf(jw, "%hu", *(__u16 *)data); |
| 137 | else if (BTF_INT_BITS(*int_type) == 8) |
| 138 | jsonw_printf(jw, "%hhu", *(__u8 *)data); |
| 139 | else |
| 140 | btf_dumper_int_bits(*int_type, bit_offset, data, jw, |
| 141 | is_plain_text); |
| 142 | break; |
| 143 | case BTF_INT_SIGNED: |
| 144 | if (BTF_INT_BITS(*int_type) == 64) |
| 145 | jsonw_printf(jw, "%ld", *(long long *)data); |
| 146 | else if (BTF_INT_BITS(*int_type) == 32) |
| 147 | jsonw_printf(jw, "%d", *(int *)data); |
| 148 | else if (BTF_INT_BITS(*int_type) == 16) |
| 149 | jsonw_printf(jw, "%hd", *(short *)data); |
| 150 | else if (BTF_INT_BITS(*int_type) == 8) |
| 151 | jsonw_printf(jw, "%hhd", *(char *)data); |
| 152 | else |
| 153 | btf_dumper_int_bits(*int_type, bit_offset, data, jw, |
| 154 | is_plain_text); |
| 155 | break; |
| 156 | case BTF_INT_CHAR: |
| 157 | if (isprint(*(char *)data)) |
| 158 | jsonw_printf(jw, "\"%c\"", *(char *)data); |
| 159 | else |
| 160 | if (is_plain_text) |
| 161 | jsonw_printf(jw, "0x%hhx", *(char *)data); |
| 162 | else |
| 163 | jsonw_printf(jw, "\"\\u00%02hhx\"", |
| 164 | *(char *)data); |
| 165 | break; |
| 166 | case BTF_INT_BOOL: |
| 167 | jsonw_bool(jw, *(int *)data); |
| 168 | break; |
| 169 | default: |
| 170 | /* shouldn't happen */ |
| 171 | return -EINVAL; |
| 172 | } |
| 173 | |
| 174 | return 0; |
| 175 | } |
| 176 | |
| 177 | static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id, |
| 178 | const void *data) |
| 179 | { |
| 180 | const struct btf_type *t; |
| 181 | struct btf_member *m; |
| 182 | const void *data_off; |
| 183 | int ret = 0; |
| 184 | int i, vlen; |
| 185 | |
| 186 | t = btf__type_by_id(d->btf, type_id); |
| 187 | if (!t) |
| 188 | return -EINVAL; |
| 189 | |
| 190 | vlen = BTF_INFO_VLEN(t->info); |
| 191 | jsonw_start_object(d->jw); |
| 192 | m = (struct btf_member *)(t + 1); |
| 193 | |
| 194 | for (i = 0; i < vlen; i++) { |
| 195 | data_off = data + BITS_ROUNDDOWN_BYTES(m[i].offset); |
| 196 | jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off)); |
| 197 | ret = btf_dumper_do_type(d, m[i].type, |
| 198 | BITS_PER_BYTE_MASKED(m[i].offset), |
| 199 | data_off); |
| 200 | if (ret) |
| 201 | break; |
| 202 | } |
| 203 | |
| 204 | jsonw_end_object(d->jw); |
| 205 | |
| 206 | return ret; |
| 207 | } |
| 208 | |
| 209 | static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id, |
| 210 | __u8 bit_offset, const void *data) |
| 211 | { |
| 212 | const struct btf_type *t = btf__type_by_id(d->btf, type_id); |
| 213 | |
| 214 | switch (BTF_INFO_KIND(t->info)) { |
| 215 | case BTF_KIND_INT: |
| 216 | return btf_dumper_int(t, bit_offset, data, d->jw, |
| 217 | d->is_plain_text); |
| 218 | case BTF_KIND_STRUCT: |
| 219 | case BTF_KIND_UNION: |
| 220 | return btf_dumper_struct(d, type_id, data); |
| 221 | case BTF_KIND_ARRAY: |
| 222 | return btf_dumper_array(d, type_id, data); |
| 223 | case BTF_KIND_ENUM: |
| 224 | btf_dumper_enum(data, d->jw); |
| 225 | return 0; |
| 226 | case BTF_KIND_PTR: |
| 227 | btf_dumper_ptr(data, d->jw, d->is_plain_text); |
| 228 | return 0; |
| 229 | case BTF_KIND_UNKN: |
| 230 | jsonw_printf(d->jw, "(unknown)"); |
| 231 | return 0; |
| 232 | case BTF_KIND_FWD: |
| 233 | /* map key or value can't be forward */ |
| 234 | jsonw_printf(d->jw, "(fwd-kind-invalid)"); |
| 235 | return -EINVAL; |
| 236 | case BTF_KIND_TYPEDEF: |
| 237 | case BTF_KIND_VOLATILE: |
| 238 | case BTF_KIND_CONST: |
| 239 | case BTF_KIND_RESTRICT: |
| 240 | return btf_dumper_modifier(d, type_id, data); |
| 241 | default: |
| 242 | jsonw_printf(d->jw, "(unsupported-kind"); |
| 243 | return -EINVAL; |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | int btf_dumper_type(const struct btf_dumper *d, __u32 type_id, |
| 248 | const void *data) |
| 249 | { |
| 250 | return btf_dumper_do_type(d, type_id, 0, data); |
| 251 | } |