perf.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2. // Copyright (C) 2018 Facebook
  3. // Author: Yonghong Song <[email protected]>
  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 <dirent.h>
  14. #include <bpf/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. default:
  97. break;
  98. }
  99. jsonw_end_object(json_wtr);
  100. }
  101. static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
  102. char *buf, __u64 probe_offset, __u64 probe_addr)
  103. {
  104. printf("pid %d fd %d: prog_id %u ", pid, fd, prog_id);
  105. switch (fd_type) {
  106. case BPF_FD_TYPE_RAW_TRACEPOINT:
  107. printf("raw_tracepoint %s\n", buf);
  108. break;
  109. case BPF_FD_TYPE_TRACEPOINT:
  110. printf("tracepoint %s\n", buf);
  111. break;
  112. case BPF_FD_TYPE_KPROBE:
  113. if (buf[0] != '\0')
  114. printf("kprobe func %s offset %llu\n", buf,
  115. probe_offset);
  116. else
  117. printf("kprobe addr %llu\n", probe_addr);
  118. break;
  119. case BPF_FD_TYPE_KRETPROBE:
  120. if (buf[0] != '\0')
  121. printf("kretprobe func %s offset %llu\n", buf,
  122. probe_offset);
  123. else
  124. printf("kretprobe addr %llu\n", probe_addr);
  125. break;
  126. case BPF_FD_TYPE_UPROBE:
  127. printf("uprobe filename %s offset %llu\n", buf, probe_offset);
  128. break;
  129. case BPF_FD_TYPE_URETPROBE:
  130. printf("uretprobe filename %s offset %llu\n", buf,
  131. probe_offset);
  132. break;
  133. default:
  134. break;
  135. }
  136. }
  137. static int show_proc(void)
  138. {
  139. struct dirent *proc_de, *pid_fd_de;
  140. __u64 probe_offset, probe_addr;
  141. __u32 len, prog_id, fd_type;
  142. DIR *proc, *pid_fd;
  143. int err, pid, fd;
  144. const char *pch;
  145. char buf[4096];
  146. proc = opendir("/proc");
  147. if (!proc)
  148. return -1;
  149. while ((proc_de = readdir(proc))) {
  150. pid = 0;
  151. pch = proc_de->d_name;
  152. /* pid should be all numbers */
  153. while (isdigit(*pch)) {
  154. pid = pid * 10 + *pch - '0';
  155. pch++;
  156. }
  157. if (*pch != '\0')
  158. continue;
  159. err = snprintf(buf, sizeof(buf), "/proc/%s/fd", proc_de->d_name);
  160. if (err < 0 || err >= (int)sizeof(buf))
  161. continue;
  162. pid_fd = opendir(buf);
  163. if (!pid_fd)
  164. continue;
  165. while ((pid_fd_de = readdir(pid_fd))) {
  166. fd = 0;
  167. pch = pid_fd_de->d_name;
  168. /* fd should be all numbers */
  169. while (isdigit(*pch)) {
  170. fd = fd * 10 + *pch - '0';
  171. pch++;
  172. }
  173. if (*pch != '\0')
  174. continue;
  175. /* query (pid, fd) for potential perf events */
  176. len = sizeof(buf);
  177. err = bpf_task_fd_query(pid, fd, 0, buf, &len,
  178. &prog_id, &fd_type,
  179. &probe_offset, &probe_addr);
  180. if (err < 0)
  181. continue;
  182. if (json_output)
  183. print_perf_json(pid, fd, prog_id, fd_type, buf,
  184. probe_offset, probe_addr);
  185. else
  186. print_perf_plain(pid, fd, prog_id, fd_type, buf,
  187. probe_offset, probe_addr);
  188. }
  189. closedir(pid_fd);
  190. }
  191. closedir(proc);
  192. return 0;
  193. }
  194. static int do_show(int argc, char **argv)
  195. {
  196. int err;
  197. if (!has_perf_query_support())
  198. return -1;
  199. if (json_output)
  200. jsonw_start_array(json_wtr);
  201. err = show_proc();
  202. if (json_output)
  203. jsonw_end_array(json_wtr);
  204. return err;
  205. }
  206. static int do_help(int argc, char **argv)
  207. {
  208. fprintf(stderr,
  209. "Usage: %1$s %2$s { show | list }\n"
  210. " %1$s %2$s help }\n"
  211. "\n"
  212. " " HELP_SPEC_OPTIONS " }\n"
  213. "",
  214. bin_name, argv[-2]);
  215. return 0;
  216. }
  217. static const struct cmd cmds[] = {
  218. { "show", do_show },
  219. { "list", do_show },
  220. { "help", do_help },
  221. { 0 }
  222. };
  223. int do_perf(int argc, char **argv)
  224. {
  225. return cmd_select(cmds, argc, argv, do_help);
  226. }