Martin KaFai Lau | f89cb40 | 2017-10-19 23:52:54 -0700 | [diff] [blame] | 1 | #include <time.h> |
| 2 | #include <stdio.h> |
| 3 | #include <errno.h> |
| 4 | #include <string.h> |
| 5 | #include <stdlib.h> |
| 6 | #include <stdio.h> |
| 7 | #include <stdint.h> |
| 8 | #include <unistd.h> |
| 9 | #include <ctype.h> |
| 10 | #include <sysexits.h> |
| 11 | |
| 12 | #include "libbpf.h" |
| 13 | |
nikolay.samofatov | c5308e9 | 2017-12-28 19:01:31 +0300 | [diff] [blame] | 14 | // TODO: Remove this when CentOS 6 support is not needed anymore |
| 15 | #ifndef CLOCK_BOOTTIME |
| 16 | #define CLOCK_BOOTTIME 7 |
| 17 | #endif |
| 18 | |
Martin KaFai Lau | f89cb40 | 2017-10-19 23:52:54 -0700 | [diff] [blame] | 19 | static const char * const prog_type_strings[] = { |
| 20 | [BPF_PROG_TYPE_UNSPEC] = "unspec", |
| 21 | [BPF_PROG_TYPE_SOCKET_FILTER] = "socket filter", |
| 22 | [BPF_PROG_TYPE_KPROBE] = "kprobe", |
| 23 | [BPF_PROG_TYPE_SCHED_CLS] = "sched cls", |
| 24 | [BPF_PROG_TYPE_SCHED_ACT] = "sched act", |
| 25 | [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", |
| 26 | [BPF_PROG_TYPE_XDP] = "xdp", |
| 27 | [BPF_PROG_TYPE_PERF_EVENT] = "perf event", |
| 28 | [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup skb", |
| 29 | [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup sock", |
| 30 | [BPF_PROG_TYPE_LWT_IN] = "lwt in", |
| 31 | [BPF_PROG_TYPE_LWT_OUT] = "lwt out", |
| 32 | [BPF_PROG_TYPE_LWT_XMIT] = "lwt xmit", |
| 33 | [BPF_PROG_TYPE_SOCK_OPS] = "sock ops", |
| 34 | [BPF_PROG_TYPE_SK_SKB] = "sk skb", |
| 35 | }; |
| 36 | |
| 37 | static const char * const map_type_strings[] = { |
| 38 | [BPF_MAP_TYPE_UNSPEC] = "unspec", |
| 39 | [BPF_MAP_TYPE_HASH] = "hash", |
| 40 | [BPF_MAP_TYPE_ARRAY] = "array", |
| 41 | [BPF_MAP_TYPE_PROG_ARRAY] = "prog array", |
| 42 | [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf-ev array", |
| 43 | [BPF_MAP_TYPE_PERCPU_HASH] = "percpu hash", |
| 44 | [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu array", |
| 45 | [BPF_MAP_TYPE_STACK_TRACE] = "stack trace", |
| 46 | [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup array", |
| 47 | [BPF_MAP_TYPE_LRU_HASH] = "lru hash", |
| 48 | [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru percpu hash", |
| 49 | [BPF_MAP_TYPE_LPM_TRIE] = "lpm trie", |
| 50 | [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array of maps", |
| 51 | [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash of maps", |
| 52 | [BPF_MAP_TYPE_DEVMAP] = "devmap", |
| 53 | [BPF_MAP_TYPE_SOCKMAP] = "sockmap", |
| 54 | }; |
| 55 | |
| 56 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) |
| 57 | #define LAST_KNOWN_PROG_TYPE (ARRAY_SIZE(prog_type_strings) - 1) |
| 58 | #define LAST_KNOWN_MAP_TYPE (ARRAY_SIZE(map_type_strings) - 1) |
| 59 | #define min(x, y) ((x) < (y) ? (x) : (y)) |
| 60 | |
| 61 | static inline uint64_t ptr_to_u64(const void *ptr) |
| 62 | { |
| 63 | return (uint64_t) (unsigned long) ptr; |
| 64 | } |
| 65 | |
| 66 | static inline void * u64_to_ptr(uint64_t ptr) |
| 67 | { |
| 68 | return (void *) (unsigned long ) ptr; |
| 69 | } |
| 70 | |
| 71 | static int handle_get_next_errno(int eno) |
| 72 | { |
| 73 | switch (eno) { |
| 74 | case ENOENT: |
| 75 | return 0; |
| 76 | case EINVAL: |
| 77 | fprintf(stderr, "Kernel does not support BPF introspection\n"); |
| 78 | return EX_UNAVAILABLE; |
| 79 | case EPERM: |
| 80 | fprintf(stderr, |
| 81 | "Require CAP_SYS_ADMIN capability. Please retry as root\n"); |
| 82 | return EX_NOPERM; |
| 83 | default: |
| 84 | fprintf(stderr, "%s\n", strerror(errno)); |
| 85 | return 1; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | static void print_prog_hdr(void) |
| 90 | { |
| 91 | printf("%9s %-15s %8s %6s %-12s %-15s\n", |
| 92 | "BID", "TYPE", "UID", "#MAPS", "LoadTime", "NAME"); |
| 93 | } |
| 94 | |
| 95 | static void print_prog_info(const struct bpf_prog_info *prog_info) |
| 96 | { |
| 97 | struct timespec real_time_ts, boot_time_ts; |
| 98 | time_t wallclock_load_time = 0; |
| 99 | char unknown_prog_type[16]; |
| 100 | const char *prog_type; |
| 101 | char load_time[16]; |
| 102 | struct tm load_tm; |
| 103 | |
| 104 | if (prog_info->type > LAST_KNOWN_PROG_TYPE) { |
| 105 | snprintf(unknown_prog_type, sizeof(unknown_prog_type), "<%u>", |
| 106 | prog_info->type); |
| 107 | unknown_prog_type[sizeof(unknown_prog_type) - 1] = '\0'; |
| 108 | prog_type = unknown_prog_type; |
| 109 | } else { |
| 110 | prog_type = prog_type_strings[prog_info->type]; |
| 111 | } |
| 112 | |
| 113 | if (!clock_gettime(CLOCK_REALTIME, &real_time_ts) && |
| 114 | !clock_gettime(CLOCK_BOOTTIME, &boot_time_ts) && |
| 115 | real_time_ts.tv_sec >= boot_time_ts.tv_sec) |
| 116 | wallclock_load_time = |
| 117 | (real_time_ts.tv_sec - boot_time_ts.tv_sec) + |
| 118 | prog_info->load_time / 1000000000; |
| 119 | |
| 120 | if (wallclock_load_time && localtime_r(&wallclock_load_time, &load_tm)) |
| 121 | strftime(load_time, sizeof(load_time), "%b%d/%H:%M", &load_tm); |
| 122 | else |
| 123 | snprintf(load_time, sizeof(load_time), "<%llu>", |
| 124 | prog_info->load_time / 1000000000); |
| 125 | load_time[sizeof(load_time) - 1] = '\0'; |
| 126 | |
| 127 | if (prog_info->jited_prog_len) |
| 128 | printf("%9u %-15s %8u %6u %-12s %-15s\n", |
| 129 | prog_info->id, prog_type, prog_info->created_by_uid, |
| 130 | prog_info->nr_map_ids, load_time, prog_info->name); |
| 131 | else |
| 132 | printf("%8u- %-15s %8u %6u %-12s %-15s\n", |
| 133 | prog_info->id, prog_type, prog_info->created_by_uid, |
| 134 | prog_info->nr_map_ids, load_time, prog_info->name); |
| 135 | } |
| 136 | |
| 137 | static void print_map_hdr(void) |
| 138 | { |
| 139 | printf("%8s %-15s %-10s %8s %8s %8s %-15s\n", |
| 140 | "MID", "TYPE", "FLAGS", "KeySz", "ValueSz", "MaxEnts", |
| 141 | "NAME"); |
| 142 | } |
| 143 | |
| 144 | static void print_map_info(const struct bpf_map_info *map_info) |
| 145 | { |
| 146 | char unknown_map_type[16]; |
| 147 | const char *map_type; |
| 148 | |
| 149 | if (map_info->type > LAST_KNOWN_MAP_TYPE) { |
| 150 | snprintf(unknown_map_type, sizeof(unknown_map_type), |
| 151 | "<%u>", map_info->type); |
| 152 | unknown_map_type[sizeof(unknown_map_type) - 1] = '\0'; |
| 153 | map_type = unknown_map_type; |
| 154 | } else { |
| 155 | map_type = map_type_strings[map_info->type]; |
| 156 | } |
| 157 | |
| 158 | printf("%8u %-15s 0x%-8x %8u %8u %8u %-15s\n", |
| 159 | map_info->id, map_type, map_info->map_flags, map_info->key_size, |
| 160 | map_info->value_size, map_info->max_entries, |
| 161 | map_info->name); |
| 162 | } |
| 163 | |
| 164 | static int print_one_prog(uint32_t prog_id) |
| 165 | { |
| 166 | const uint32_t usual_nr_map_ids = 64; |
| 167 | uint32_t nr_map_ids = usual_nr_map_ids; |
| 168 | struct bpf_prog_info prog_info; |
| 169 | uint32_t *map_ids = NULL; |
| 170 | uint32_t info_len; |
| 171 | int ret = 0; |
| 172 | int prog_fd; |
| 173 | uint32_t i; |
| 174 | |
| 175 | prog_fd = bpf_prog_get_fd_by_id(prog_id); |
| 176 | if (prog_fd == -1) { |
| 177 | if (errno == ENOENT) { |
| 178 | fprintf(stderr, "BID:%u not found\n", prog_id); |
| 179 | return EX_DATAERR; |
| 180 | } else { |
| 181 | return handle_get_next_errno(errno); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | /* Retry at most one time for larger map_ids array */ |
| 186 | for (i = 0; i < 2; i++) { |
| 187 | bzero(&prog_info, sizeof(prog_info)); |
| 188 | prog_info.map_ids = ptr_to_u64(realloc(map_ids, |
| 189 | nr_map_ids * sizeof(*map_ids))); |
| 190 | if (!prog_info.map_ids) { |
| 191 | fprintf(stderr, |
| 192 | "Cannot allocate memory for %u map_ids for BID:%u\n", |
| 193 | nr_map_ids, prog_id); |
| 194 | close(prog_fd); |
| 195 | free(map_ids); |
| 196 | return 1; |
| 197 | } |
| 198 | |
| 199 | map_ids = u64_to_ptr(prog_info.map_ids); |
| 200 | prog_info.nr_map_ids = nr_map_ids; |
| 201 | info_len = sizeof(prog_info); |
| 202 | ret = bpf_obj_get_info(prog_fd, &prog_info, &info_len); |
| 203 | if (ret) { |
| 204 | fprintf(stderr, "Cannot get info for BID:%u. %s(%d)\n", |
| 205 | prog_id, strerror(errno), errno); |
| 206 | close(prog_fd); |
| 207 | free(map_ids); |
| 208 | return ret; |
| 209 | } |
| 210 | |
| 211 | if (prog_info.nr_map_ids <= nr_map_ids) |
| 212 | break; |
| 213 | |
| 214 | nr_map_ids = prog_info.nr_map_ids; |
| 215 | } |
| 216 | close(prog_fd); |
| 217 | |
| 218 | print_prog_hdr(); |
| 219 | print_prog_info(&prog_info); |
| 220 | printf("\n"); |
| 221 | |
| 222 | /* Print all map_info used by the prog */ |
| 223 | print_map_hdr(); |
| 224 | nr_map_ids = min(prog_info.nr_map_ids, nr_map_ids); |
| 225 | for (i = 0; i < nr_map_ids; i++) { |
| 226 | struct bpf_map_info map_info = {}; |
Teng Qin | d77a41b | 2017-10-31 12:05:17 -0700 | [diff] [blame] | 227 | info_len = sizeof(map_info); |
Martin KaFai Lau | f89cb40 | 2017-10-19 23:52:54 -0700 | [diff] [blame] | 228 | int map_fd; |
| 229 | |
| 230 | map_fd = bpf_map_get_fd_by_id(map_ids[i]); |
| 231 | if (map_fd == -1) { |
| 232 | if (errno == -ENOENT) |
| 233 | continue; |
| 234 | |
| 235 | fprintf(stderr, |
| 236 | "Cannot get fd for map:%u. %s(%d)\n", |
| 237 | map_ids[i], strerror(errno), errno); |
| 238 | ret = map_fd; |
| 239 | break; |
| 240 | } |
| 241 | |
| 242 | ret = bpf_obj_get_info(map_fd, &map_info, &info_len); |
| 243 | close(map_fd); |
| 244 | if (ret) { |
| 245 | fprintf(stderr, "Cannot get info for map:%u. %s(%d)\n", |
| 246 | map_ids[i], strerror(errno), errno); |
| 247 | break; |
| 248 | } |
| 249 | |
| 250 | print_map_info(&map_info); |
| 251 | } |
| 252 | |
| 253 | free(map_ids); |
| 254 | return ret; |
| 255 | } |
| 256 | |
| 257 | int print_all_progs(void) |
| 258 | { |
| 259 | uint32_t next_id = 0; |
| 260 | |
| 261 | print_prog_hdr(); |
| 262 | |
| 263 | while (!bpf_prog_get_next_id(next_id, &next_id)) { |
| 264 | struct bpf_prog_info prog_info = {}; |
| 265 | uint32_t prog_info_len = sizeof(prog_info); |
| 266 | int prog_fd; |
| 267 | int ret; |
| 268 | |
| 269 | prog_fd = bpf_prog_get_fd_by_id(next_id); |
| 270 | if (prog_fd < 0) { |
| 271 | if (errno == ENOENT) |
| 272 | continue; |
| 273 | fprintf(stderr, |
| 274 | "Cannot get fd for BID:%u. %s(%d)\n", |
| 275 | next_id, strerror(errno), errno); |
| 276 | return 1; |
| 277 | } |
| 278 | |
| 279 | ret = bpf_obj_get_info(prog_fd, &prog_info, &prog_info_len); |
| 280 | close(prog_fd); |
| 281 | if (ret) { |
| 282 | fprintf(stderr, |
| 283 | "Cannot get bpf_prog_info for BID:%u. %s(%d)\n", |
| 284 | next_id, strerror(errno), errno); |
| 285 | return ret; |
| 286 | } |
| 287 | |
| 288 | print_prog_info(&prog_info); |
| 289 | } |
| 290 | |
| 291 | return handle_get_next_errno(errno); |
| 292 | } |
| 293 | |
| 294 | void usage(void) |
| 295 | { |
| 296 | printf("BPF Program Snapshot (bps):\n" |
| 297 | "List of all BPF programs loaded into the system.\n\n"); |
| 298 | printf("Usage: bps [bpf-prog-id]\n"); |
| 299 | printf(" [bpf-prog-id] If specified, it shows the details info of the bpf-prog\n"); |
| 300 | printf("\n"); |
| 301 | } |
| 302 | |
| 303 | int main(int argc, char **argv) |
| 304 | { |
| 305 | if (argc > 1) { |
| 306 | if (!isdigit(*argv[1])) { |
| 307 | usage(); |
| 308 | return EX_USAGE; |
| 309 | } |
| 310 | return print_one_prog((uint32_t)atoi(argv[1])); |
| 311 | } |
| 312 | |
| 313 | return print_all_progs(); |
| 314 | } |