perf.c 5.7 KB

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