perf.c 5.8 KB

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