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