perf.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // SPDX-License-Identifier: GPL-2.0+
  2. // Copyright (C) 2018 Facebook
  3. // Author: Yonghong Song <yhs@fb.com>
  4. #define _GNU_SOURCE
  5. #include <ctype.h>
  6. #include <errno.h>
  7. #include <fcntl.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <sys/stat.h>
  11. #include <sys/types.h>
  12. #include <unistd.h>
  13. #include <ftw.h>
  14. #include <bpf.h>
  15. #include "main.h"
  16. /* 0: undecided, 1: supported, 2: not supported */
  17. static int perf_query_supported;
  18. static bool has_perf_query_support(void)
  19. {
  20. __u64 probe_offset, probe_addr;
  21. __u32 len, prog_id, fd_type;
  22. char buf[256];
  23. int fd;
  24. if (perf_query_supported)
  25. goto out;
  26. fd = open("/", O_RDONLY);
  27. if (fd < 0) {
  28. p_err("perf_query_support: cannot open directory \"/\" (%s)",
  29. strerror(errno));
  30. goto out;
  31. }
  32. /* the following query will fail as no bpf attachment,
  33. * the expected errno is ENOTSUPP
  34. */
  35. errno = 0;
  36. len = sizeof(buf);
  37. bpf_task_fd_query(getpid(), fd, 0, buf, &len, &prog_id,
  38. &fd_type, &probe_offset, &probe_addr);
  39. if (errno == 524 /* ENOTSUPP */) {
  40. perf_query_supported = 1;
  41. goto close_fd;
  42. }
  43. perf_query_supported = 2;
  44. p_err("perf_query_support: %s", strerror(errno));
  45. fprintf(stderr,
  46. "HINT: non root or kernel doesn't support TASK_FD_QUERY\n");
  47. close_fd:
  48. close(fd);
  49. out:
  50. return perf_query_supported == 1;
  51. }
  52. static void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type,
  53. char *buf, __u64 probe_offset, __u64 probe_addr)
  54. {
  55. jsonw_start_object(json_wtr);
  56. jsonw_int_field(json_wtr, "pid", pid);
  57. jsonw_int_field(json_wtr, "fd", fd);
  58. jsonw_uint_field(json_wtr, "prog_id", prog_id);
  59. switch (fd_type) {
  60. case BPF_FD_TYPE_RAW_TRACEPOINT:
  61. jsonw_string_field(json_wtr, "fd_type", "raw_tracepoint");
  62. jsonw_string_field(json_wtr, "tracepoint", buf);
  63. break;
  64. case BPF_FD_TYPE_TRACEPOINT:
  65. jsonw_string_field(json_wtr, "fd_type", "tracepoint");
  66. jsonw_string_field(json_wtr, "tracepoint", buf);
  67. break;
  68. case BPF_FD_TYPE_KPROBE:
  69. jsonw_string_field(json_wtr, "fd_type", "kprobe");
  70. if (buf[0] != '\0') {
  71. jsonw_string_field(json_wtr, "func", buf);
  72. jsonw_lluint_field(json_wtr, "offset", probe_offset);
  73. } else {
  74. jsonw_lluint_field(json_wtr, "addr", probe_addr);
  75. }
  76. break;
  77. case BPF_FD_TYPE_KRETPROBE:
  78. jsonw_string_field(json_wtr, "fd_type", "kretprobe");
  79. if (buf[0] != '\0') {
  80. jsonw_string_field(json_wtr, "func", buf);
  81. jsonw_lluint_field(json_wtr, "offset", probe_offset);
  82. } else {
  83. jsonw_lluint_field(json_wtr, "addr", probe_addr);
  84. }
  85. break;
  86. case BPF_FD_TYPE_UPROBE:
  87. jsonw_string_field(json_wtr, "fd_type", "uprobe");
  88. jsonw_string_field(json_wtr, "filename", buf);
  89. jsonw_lluint_field(json_wtr, "offset", probe_offset);
  90. break;
  91. case BPF_FD_TYPE_URETPROBE:
  92. jsonw_string_field(json_wtr, "fd_type", "uretprobe");
  93. jsonw_string_field(json_wtr, "filename", buf);
  94. jsonw_lluint_field(json_wtr, "offset", probe_offset);
  95. break;
  96. }
  97. jsonw_end_object(json_wtr);
  98. }
  99. static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
  100. char *buf, __u64 probe_offset, __u64 probe_addr)
  101. {
  102. printf("pid %d fd %d: prog_id %u ", pid, fd, prog_id);
  103. switch (fd_type) {
  104. case BPF_FD_TYPE_RAW_TRACEPOINT:
  105. printf("raw_tracepoint %s\n", buf);
  106. break;
  107. case BPF_FD_TYPE_TRACEPOINT:
  108. printf("tracepoint %s\n", buf);
  109. break;
  110. case BPF_FD_TYPE_KPROBE:
  111. if (buf[0] != '\0')
  112. printf("kprobe func %s offset %llu\n", buf,
  113. probe_offset);
  114. else
  115. printf("kprobe addr %llu\n", probe_addr);
  116. break;
  117. case BPF_FD_TYPE_KRETPROBE:
  118. if (buf[0] != '\0')
  119. printf("kretprobe func %s offset %llu\n", buf,
  120. probe_offset);
  121. else
  122. printf("kretprobe addr %llu\n", probe_addr);
  123. break;
  124. case BPF_FD_TYPE_UPROBE:
  125. printf("uprobe filename %s offset %llu\n", buf, probe_offset);
  126. break;
  127. case BPF_FD_TYPE_URETPROBE:
  128. printf("uretprobe filename %s offset %llu\n", buf,
  129. probe_offset);
  130. break;
  131. }
  132. }
  133. static int show_proc(const char *fpath, const struct stat *sb,
  134. int tflag, struct FTW *ftwbuf)
  135. {
  136. __u64 probe_offset, probe_addr;
  137. __u32 len, prog_id, fd_type;
  138. int err, pid = 0, fd = 0;
  139. const char *pch;
  140. char buf[4096];
  141. /* prefix always /proc */
  142. pch = fpath + 5;
  143. if (*pch == '\0')
  144. return 0;
  145. /* pid should be all numbers */
  146. pch++;
  147. while (isdigit(*pch)) {
  148. pid = pid * 10 + *pch - '0';
  149. pch++;
  150. }
  151. if (*pch == '\0')
  152. return 0;
  153. if (*pch != '/')
  154. return FTW_SKIP_SUBTREE;
  155. /* check /proc/<pid>/fd directory */
  156. pch++;
  157. if (strncmp(pch, "fd", 2))
  158. return FTW_SKIP_SUBTREE;
  159. pch += 2;
  160. if (*pch == '\0')
  161. return 0;
  162. if (*pch != '/')
  163. return FTW_SKIP_SUBTREE;
  164. /* check /proc/<pid>/fd/<fd_num> */
  165. pch++;
  166. while (isdigit(*pch)) {
  167. fd = fd * 10 + *pch - '0';
  168. pch++;
  169. }
  170. if (*pch != '\0')
  171. return FTW_SKIP_SUBTREE;
  172. /* query (pid, fd) for potential perf events */
  173. len = sizeof(buf);
  174. err = bpf_task_fd_query(pid, fd, 0, buf, &len, &prog_id, &fd_type,
  175. &probe_offset, &probe_addr);
  176. if (err < 0)
  177. return 0;
  178. if (json_output)
  179. print_perf_json(pid, fd, prog_id, fd_type, buf, probe_offset,
  180. probe_addr);
  181. else
  182. print_perf_plain(pid, fd, prog_id, fd_type, buf, probe_offset,
  183. probe_addr);
  184. return 0;
  185. }
  186. static int do_show(int argc, char **argv)
  187. {
  188. int flags = FTW_ACTIONRETVAL | FTW_PHYS;
  189. int err = 0, nopenfd = 16;
  190. if (!has_perf_query_support())
  191. return -1;
  192. if (json_output)
  193. jsonw_start_array(json_wtr);
  194. if (nftw("/proc", show_proc, nopenfd, flags) == -1) {
  195. p_err("%s", strerror(errno));
  196. err = -1;
  197. }
  198. if (json_output)
  199. jsonw_end_array(json_wtr);
  200. return err;
  201. }
  202. static int do_help(int argc, char **argv)
  203. {
  204. fprintf(stderr,
  205. "Usage: %s %s { show | list | help }\n"
  206. "",
  207. bin_name, argv[-2]);
  208. return 0;
  209. }
  210. static const struct cmd cmds[] = {
  211. { "show", do_show },
  212. { "list", do_show },
  213. { "help", do_help },
  214. { 0 }
  215. };
  216. int do_perf(int argc, char **argv)
  217. {
  218. return cmd_select(cmds, argc, argv, do_help);
  219. }