main.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. #include <iostream>
  2. #include <phpcpp.h>
  3. #include <fstream>
  4. #include "bcc_common.h"
  5. #include "bpf_module.h"
  6. #include "libbpf.h"
  7. #include "BPF.h"
  8. #include <csignal>
  9. #include <atomic>
  10. #include <sstream>
  11. #include <utility>
  12. #include <vector>
  13. constexpr const char *TRACE_PIPE_PATH = "/sys/kernel/debug/tracing/trace_pipe";
  14. // Php::out << "Value of fn: " << fnn << std::endl;
  15. //static std::string cb_fn;
  16. const std::vector<std::string> syscall_prefixes = {
  17. "sys_",
  18. "__x64_sys_",
  19. "__x32_compat_sys_",
  20. "__ia32_compat_sys_",
  21. "__arm64_sys_",
  22. "__s390x_sys_",
  23. "__s390_sys_",
  24. "__riscv_sys_"
  25. };
  26. class PerfEvent : public Php::Base {
  27. private:
  28. std::string cb;
  29. public:
  30. ebpf::BPF *bpf;
  31. std::string event_name;
  32. PerfEvent(ebpf::BPF *bpf_instance, const std::string &event_name)
  33. : bpf(bpf_instance), event_name(event_name) {}
  34. virtual ~PerfEvent() = default;
  35. static void callbackfn(void *cookie, void *data, int data_size) {
  36. auto *instance = static_cast<PerfEvent *>(cookie);
  37. if (!instance) return;
  38. Php::Value phpData((const char *) data, data_size);
  39. Php::call(instance->cb.c_str(), nullptr, phpData, data_size);
  40. }
  41. void php_open_perf_buffer(Php::Parameters &params) {
  42. this->cb = params[0].stringValue();
  43. auto res = this->bpf->open_perf_buffer(this->event_name, callbackfn, nullptr, this);
  44. if (!res.ok()) {
  45. throw Php::Exception("open_perf_buffer error:" + res.msg());
  46. }
  47. }
  48. int perf_buffer_poll(int timeout_ms) {
  49. return bpf->poll_perf_buffer(this->event_name, timeout_ms); // perf_reader_event_read
  50. }
  51. };
  52. class EbpfExtension : public Php::Base {
  53. private:
  54. ebpf::BPF bpf;
  55. Php::Object _perf_event;
  56. PerfEvent *_class_perf_event;
  57. static std::string add_prefix(const std::string &prefix, const std::string &name) {
  58. if (name.rfind(prefix, 0) != 0) {
  59. return prefix + name;
  60. }
  61. return name;
  62. }
  63. std::string fix_syscall_fnname(const std::string &name) {
  64. for (const auto &prefix: syscall_prefixes) {
  65. if (name.rfind(prefix, 0) == 0) {
  66. return bpf.get_syscall_fnname(name.substr(prefix.length()));
  67. }
  68. }
  69. return name;
  70. }
  71. void _trace_autoload() {
  72. size_t num_funcs = bpf.get_num_functions();
  73. for (size_t i = 0; i < num_funcs; i++) {
  74. const char *func_name = bpf.get_function_name(i);
  75. std::string fn_name(func_name);
  76. if (fn_name.rfind("kprobe__", 0) == 0) {
  77. std::string kernel_func = fix_syscall_fnname(fn_name.substr(8));
  78. bpf.attach_kprobe(kernel_func, fn_name);
  79. } else if (fn_name.rfind("kretprobe__", 0) == 0) {
  80. std::string kernel_func = fix_syscall_fnname(fn_name.substr(11));
  81. bpf.attach_kprobe(kernel_func, fn_name, 0, BPF_PROBE_RETURN);
  82. } else if (fn_name.rfind("tracepoint__", 0) == 0) {
  83. std::string tp_name = fn_name.substr(12);
  84. std::replace(tp_name.begin(), tp_name.end(), '_', ':');
  85. bpf.attach_tracepoint(tp_name, fn_name);
  86. } else if (fn_name.rfind("raw_tracepoint__", 0) == 0) {
  87. std::string tp_name = fn_name.substr(16);
  88. bpf.attach_raw_tracepoint(tp_name, fn_name);
  89. } else if (fn_name.rfind("kfunc__", 0) == 0) {
  90. fn_name = add_prefix("kfunc__", fn_name);
  91. attach_kfunc(fn_name);
  92. } else if (fn_name.rfind("kretfunc__", 0) == 0) {
  93. fn_name = add_prefix("kretfunc__", fn_name);
  94. attach_kfunc(fn_name);
  95. } else if (fn_name.rfind("lsm__", 0) == 0) {
  96. fn_name = add_prefix("lsm__", fn_name);
  97. attach_lsm(fn_name);
  98. }
  99. }
  100. }
  101. public:
  102. EbpfExtension() = default;
  103. virtual ~EbpfExtension() = default;
  104. // Php::Value __get(const Php::Value &name)
  105. // {
  106. // if (name == "events") return _perf_event;
  107. //
  108. // auto a = new PerfEvent;
  109. // a->getMessage();
  110. //
  111. // return Php::Base::__get(name);
  112. // }
  113. void __construct(Php::Parameters &params) {
  114. std::string bpf_code = params[0].stringValue();
  115. auto init_res = bpf.init(bpf_code);
  116. if (!init_res.ok()) {
  117. throw Php::Exception("init error: " + init_res.msg());
  118. }
  119. _trace_autoload();
  120. }
  121. ebpf::StatusTuple attach_kfunc(const std::string &kfn) {
  122. int probe_fd;
  123. auto fn = bpf.load_func(kfn, BPF_PROG_TYPE_TRACING, probe_fd);
  124. int res_fd = bpf_attach_kfunc(probe_fd);
  125. if (res_fd < 0) {
  126. TRY2(bpf.unload_func(kfn));
  127. return ebpf::StatusTuple(-1, "Unable to attach kfunc using %s",
  128. kfn.c_str());
  129. }
  130. return ebpf::StatusTuple::OK();
  131. }
  132. ebpf::StatusTuple attach_lsm(const std::string &lsm) {
  133. int probe_fd;
  134. auto fn = bpf.load_func(lsm, BPF_PROG_TYPE_LSM, probe_fd);
  135. int res_fd = bpf_attach_lsm(probe_fd);
  136. if (res_fd < 0) {
  137. TRY2(bpf.unload_func(lsm));
  138. return ebpf::StatusTuple(-1, "Unable to attach lsm using %s",
  139. lsm.c_str());
  140. }
  141. return ebpf::StatusTuple::OK();
  142. }
  143. void php_attach_kprobe(Php::Parameters &params) {
  144. std::string kernel_func = params[0].stringValue();
  145. std::string probe_func = params[1].stringValue();
  146. auto attach_res = bpf.attach_kprobe(kernel_func, probe_func);
  147. if (!attach_res.ok()) {
  148. throw Php::Exception("attach error: " + attach_res.msg());
  149. }
  150. }
  151. void php_attach_tracepoint(Php::Parameters &params) {
  152. std::string tp_func = params[0].stringValue();
  153. std::string probe_func = params[1].stringValue();
  154. auto attach_res = bpf.attach_tracepoint(tp_func, probe_func);
  155. if (!attach_res.ok()) {
  156. throw Php::Exception("attach error: " + attach_res.msg());
  157. }
  158. }
  159. void php_attach_raw_tracepoint(Php::Parameters &params) {
  160. std::string tp_func = params[0].stringValue();
  161. std::string probe_func = params[1].stringValue();
  162. auto attach_res = bpf.attach_raw_tracepoint(tp_func, probe_func);
  163. if (!attach_res.ok()) {
  164. throw Php::Exception("attach error: " + attach_res.msg());
  165. }
  166. }
  167. void php_attach_kfunc(Php::Parameters &params) {
  168. std::string kfunc = params[0].stringValue();
  169. auto attach_res = attach_kfunc(kfunc);
  170. if (!attach_res.ok()) {
  171. throw Php::Exception("attach error: " + attach_res.msg());
  172. }
  173. }
  174. void php_attach_lsm(Php::Parameters &params) {
  175. std::string kfunc = params[0].stringValue();
  176. auto attach_res = attach_lsm(kfunc);
  177. if (!attach_res.ok()) {
  178. throw Php::Exception("attach error: " + attach_res.msg());
  179. }
  180. }
  181. void php_attach_uprobe(Php::Parameters &params) {
  182. std::string binary_path = params[0].stringValue();
  183. std::string symbol = params[1].stringValue();
  184. std::string probe_func = params[2].stringValue();
  185. int64_t symbol_addr = 0, symbol_offset = 0, pid_param;
  186. uint32_t ref_ctr_offset = 0;
  187. pid_t pid = -1;
  188. if (params.size() > 3) {
  189. Php::Value options = params[3];
  190. symbol_addr = options.get("symbol_addr").numericValue();
  191. symbol_offset = options.get("symbol_offset").numericValue();
  192. ref_ctr_offset = options.get("ref_ctr_offset").numericValue();
  193. pid_param = options.get("pid").numericValue();
  194. if (pid_param > 0) {
  195. pid = static_cast<pid_t>(pid_param);
  196. }
  197. }
  198. auto attach_res = bpf.attach_uprobe(binary_path, symbol, probe_func, symbol_addr, BPF_PROBE_ENTRY, pid,
  199. symbol_offset, ref_ctr_offset);
  200. if (!attach_res.ok()) {
  201. throw Php::Exception("attach error: " + attach_res.msg());
  202. }
  203. }
  204. void php_detach_kprobe(Php::Parameters &params) {
  205. std::string fn = params[0].stringValue();
  206. auto detach_res = bpf.detach_kprobe(fn);
  207. if (!detach_res.ok()) {
  208. throw Php::Exception("detach_kprobe error: " + detach_res.msg());
  209. }
  210. }
  211. void php_detach_uprobe(Php::Parameters &params) {
  212. std::string binary_path = params[0].stringValue();
  213. std::string symbol = params[1].stringValue();
  214. int64_t symbol_addr = 0, symbol_offset = 0, pid_param;
  215. pid_t pid = -1;
  216. if (params.size() > 2) {
  217. Php::Value options = params[2];
  218. symbol_addr = options.get("symbol_addr").numericValue();
  219. symbol_offset = options.get("symbol_offset").numericValue();
  220. pid_param = options.get("pid").numericValue();
  221. if (pid_param > 0) {
  222. pid = static_cast<pid_t>(pid_param);
  223. }
  224. }
  225. auto detach_res = bpf.detach_uprobe(binary_path, symbol, symbol_addr, BPF_PROBE_ENTRY, pid, symbol_offset);
  226. if (!detach_res.ok()) {
  227. throw Php::Exception("detach_kprobe error: " + detach_res.msg());
  228. }
  229. }
  230. [[noreturn]] static void php_trace_print() {
  231. std::ifstream pipe(TRACE_PIPE_PATH);
  232. std::string line;
  233. if (!pipe.is_open()) {
  234. throw Php::Exception("Failed to open trace_pipe");
  235. }
  236. std::cout << "Press Ctrl+C to stop..." << std::endl;
  237. while (true) {
  238. if (std::getline(pipe, line)) {
  239. std::cout << line << std::endl;
  240. }
  241. }
  242. }
  243. static Php::Value php_trace_fields(Php::Parameters &params) {
  244. std::ifstream traceFile(TRACE_PIPE_PATH);
  245. if (!traceFile.is_open()) {
  246. throw Php::Exception("Failed to open trace_pipe");
  247. }
  248. std::string line;
  249. while (std::getline(traceFile, line)) {
  250. if (line.empty()) {
  251. continue;
  252. }
  253. if (line.rfind("CPU:", 0) == 0) {
  254. continue;
  255. }
  256. std::string task = line.substr(0, 16);
  257. task.erase(0, task.find_first_not_of(" "));
  258. std::istringstream iss(line.substr(17));
  259. std::string pid, cpu, flags, ts, msg;
  260. char delim;
  261. if (!(iss >> pid >> delim >> cpu >> delim >> flags >> ts)) {
  262. continue;
  263. }
  264. size_t sym_end = iss.str().find(": ", iss.tellg());
  265. if (sym_end != std::string::npos) {
  266. msg = iss.str().substr(sym_end + 2);
  267. }
  268. Php::Array result;
  269. result[0] = task;
  270. result[1] = std::stoi(pid);
  271. result[2] = std::stoi(cpu.substr(1, cpu.size() - 2)); // 去掉方括号
  272. result[3] = flags;
  273. result[4] = std::stod(ts);
  274. result[5] = msg;
  275. return result;
  276. }
  277. return Php::Value();
  278. }
  279. Php::Value php_perf_event(Php::Parameters &params) {
  280. std::string event_name = params[0].stringValue(); // Get event_name from the parameters
  281. this->_class_perf_event = new PerfEvent(&this->bpf, event_name);
  282. return Php::Object("PerfEvent", this->_class_perf_event);
  283. }
  284. void php_perf_buffer_poll(Php::Parameters &params) {
  285. if (!this->_class_perf_event) {
  286. throw Php::Exception("perf event is null.");
  287. }
  288. int timeout_ms = -1;
  289. int res = this->_class_perf_event->perf_buffer_poll(timeout_ms);
  290. if (res < 0) {
  291. throw Php::Exception("perf buffer poll error.");
  292. }
  293. }
  294. Php::Value php_get_syscall_fnname(Php::Parameters &params) {
  295. std::string name = params[0].stringValue();
  296. return bpf.get_syscall_fnname(name);
  297. }
  298. };
  299. std::string sanitize_str(std::string str, bool (*validator)(char),
  300. char replacement = '_') {
  301. for (size_t i = 0; i < str.length(); i++)
  302. if (!validator(str[i]))
  303. str[i] = replacement;
  304. return str;
  305. }
  306. std::string attach_type_prefix(bpf_probe_attach_type type) {
  307. switch (type) {
  308. case BPF_PROBE_ENTRY:
  309. return "p";
  310. case BPF_PROBE_RETURN:
  311. return "r";
  312. }
  313. return "ERROR";
  314. }
  315. static bool kprobe_event_validator(char c) {
  316. return (c != '+') && (c != '.');
  317. }
  318. std::string get_kprobe_event(const std::string &kernel_func,
  319. bpf_probe_attach_type type) {
  320. std::string res = attach_type_prefix(type) + "_";
  321. res += sanitize_str(kernel_func, &kprobe_event_validator);
  322. return res;
  323. }
  324. void bpf_new2(Php::Parameters &params) {
  325. std::string param = params[0].stringValue();
  326. ebpf::BPF bpf;
  327. // void *mod = bpf_module_create_c_from_string(param.c_str(), 4, NULL, 0, true, NULL);
  328. std::string kernel_func = bpf.get_syscall_fnname("clone");
  329. bpf.init(param);
  330. const char *prob_fn = "kprobe__sys_clone";
  331. auto attach_res = bpf.attach_kprobe(kernel_func, prob_fn);
  332. Php::out << "Value of fn:kernel_func]" << kernel_func.c_str() << std::endl;
  333. Php::out << "Value of fn:prob_fn]" << prob_fn << std::endl;
  334. std::ifstream pipe("/sys/kernel/debug/tracing/trace_pipe");
  335. std::string line;
  336. if (!attach_res.ok()) {
  337. std::cerr << attach_res.msg() << std::endl;
  338. } else {
  339. std::cout << "Press Ctrl+C to stop..." << std::endl;
  340. while (true) {
  341. if (std::getline(pipe, line)) {
  342. std::cout << line << std::endl;
  343. }
  344. }
  345. }
  346. }
  347. void bpf_new(Php::Parameters &params) {
  348. std::string param = params[0].stringValue();
  349. void *mod = bpf_module_create_c_from_string(param.c_str(), 4, NULL, 0, true, NULL);
  350. // void *mod = bpf_module_create_c_from_string("BPF_TABLE(\"array\", int, int, stats, 10);\n", 4, NULL, 0, true, NULL);
  351. // bpf_prog_get_fd_by_id() ;
  352. Php::out << "Value of mod: " << mod << std::endl;
  353. // size_t num = bpf_num_functions(mod);
  354. // for (size_t i = 0; i < num; ++i) {
  355. // const char *fnn = bpf_function_name(mod, i);
  356. // Php::out << "Value of fn: " << fnn << std::endl;
  357. // }
  358. // Php::out << "Value of bpf_num_functions: " << num << std::endl;
  359. ebpf::BPF bpf;
  360. std::string kernel_func = bpf.get_syscall_fnname("clone");
  361. const char *prob_fn = "hello";
  362. void *func_start = bpf_function_start(mod, prob_fn);
  363. if (!func_start) {
  364. Php::out << "can not find of func_start: " << func_start << std::endl;
  365. }
  366. int log_level = 1;
  367. std::cout << "func_load ]" << prob_fn << std::endl;
  368. std::cout << "func_load func_start]" << func_start << std::endl;
  369. std::cout << "func_load func_size]" << bpf_function_size(mod, prob_fn) << std::endl;
  370. std::cout << "func_load license]" << bpf_module_license(mod) << std::endl;
  371. std::cout << "func_load kern_version]" << bpf_module_kern_version(mod) << std::endl;
  372. int fn_fd = bcc_func_load(mod, BPF_PROG_TYPE_KPROBE, prob_fn,
  373. static_cast<const bpf_insn *>(func_start),
  374. static_cast<int>(bpf_function_size(mod, prob_fn)),
  375. bpf_module_license(mod),
  376. bpf_module_kern_version(mod),
  377. log_level, nullptr, 0, 0, -1);
  378. if (fn_fd < 0) {
  379. Php::out << "Failed to load BPF program " << fn_fd << std::endl;
  380. } else {
  381. Php::out << "BPF program fd ]" << fn_fd << std::endl;
  382. }
  383. std::string probe_event = get_kprobe_event(kernel_func, BPF_PROBE_ENTRY);
  384. std::cout << "kernel_func]" << kernel_func.c_str() << "! probe_event]" << probe_event.c_str() << std::endl;
  385. int kernel_func_offset = 0;
  386. // bpf_attach_kprobe(fn_fd, BPF_PROBE_ENTRY, probe_event.c_str(), fn, fn_offset, 0);
  387. int attach_res2 = bpf_attach_kprobe(fn_fd, BPF_PROBE_ENTRY, probe_event.c_str(), kernel_func.c_str(),
  388. kernel_func_offset, 0);
  389. std::cout << "main.cpp attach_res2---------->]" << attach_res2 << std::endl;
  390. // Php::out << "Value of clone_fnname: " << kernel_func << std::endl;
  391. std::ifstream pipe("/sys/kernel/debug/tracing/trace_pipe");
  392. std::string line;
  393. //
  394. // bpf.init(param);
  395. auto attach_res = bpf.attach_kprobe(kernel_func, prob_fn);
  396. std::cout << "main.cpp attach_res2-------->]" << attach_res.ok() << std::endl;
  397. if (!attach_res.ok() && attach_res2 <= 0) {
  398. std::cerr << attach_res.msg() << std::endl;
  399. } else {
  400. std::cout << "Starting HelloWorld with BCC " << 111 << std::endl;
  401. while (true) {
  402. if (std::getline(pipe, line)) {
  403. std::cout << "aaa Waiting for a sys_clone event" << std::endl;
  404. std::cout << line << std::endl;
  405. // Detach the probe if we got at least one line.
  406. auto detach_res = bpf.detach_kprobe(kernel_func);
  407. if (!detach_res.ok()) {
  408. std::cerr << detach_res.msg() << std::endl;
  409. }
  410. break;
  411. } else {
  412. std::cout << "Waiting for a sys_clone event" << std::endl;
  413. sleep(1);
  414. }
  415. }
  416. }
  417. }
  418. extern "C" {
  419. PHPCPP_EXPORT void *get_module() {
  420. static Php::Extension extension("ebpf", "1.0.0");
  421. extension.add<bpf_new>("bpf_new", {Php::ByVal("a", Php::Type::String)});
  422. extension.add<bpf_new2>("bpf_new2", {Php::ByVal("a", Php::Type::String)});
  423. Php::Class<EbpfExtension> ebpf_class("Ebpf");
  424. ebpf_class.method<&EbpfExtension::php_trace_print>("trace_print");
  425. ebpf_class.method<&EbpfExtension::php_trace_fields>("trace_fields");
  426. ebpf_class.method<&EbpfExtension::__construct>("__construct", {
  427. Php::ByVal("bpf_code", Php::Type::String)
  428. });
  429. ebpf_class.method<&EbpfExtension::php_attach_kprobe>("attach_kprobe", {
  430. Php::ByVal("kernel_func", Php::Type::String),
  431. Php::ByVal("probe_func", Php::Type::String)
  432. });
  433. ebpf_class.method<&EbpfExtension::php_attach_tracepoint>("attach_tracepoint", {
  434. Php::ByVal("tp", Php::Type::String),
  435. Php::ByVal("probe_func", Php::Type::String)
  436. });
  437. ebpf_class.method<&EbpfExtension::php_attach_raw_tracepoint>("attach_raw_tracepoint", {
  438. Php::ByVal("tp", Php::Type::String),
  439. Php::ByVal("probe_func", Php::Type::String)
  440. });
  441. ebpf_class.method<&EbpfExtension::php_attach_kfunc>("attach_kfunc", {
  442. Php::ByVal("kfn", Php::Type::String),
  443. });
  444. ebpf_class.method<&EbpfExtension::php_attach_lsm>("attach_lsm", {
  445. Php::ByVal("lsm", Php::Type::String),
  446. });
  447. ebpf_class.method<&EbpfExtension::php_attach_uprobe>("attach_uprobe", {
  448. Php::ByVal("binary_path", Php::Type::String),
  449. Php::ByVal("symbol", Php::Type::String),
  450. Php::ByVal("probe_func", Php::Type::String),
  451. Php::ByVal("options", Php::Type::Array, false),
  452. });
  453. // ebpf_class.method<&EbpfExtension::php_open_perf_buffer>("open_perf_buffer", {
  454. // Php::ByVal("callback", Php::Type::Callable),
  455. // });
  456. ebpf_class.method<&EbpfExtension::php_perf_event>("perf_event", {
  457. Php::ByVal("ev_name", Php::Type::String),
  458. });
  459. ebpf_class.method<&EbpfExtension::php_perf_buffer_poll>("perf_buffer_poll", {});
  460. ebpf_class.method<&EbpfExtension::php_get_syscall_fnname>("get_syscall_fnname", {
  461. Php::ByVal("name", Php::Type::String),
  462. });
  463. /*detach*/
  464. ebpf_class.method<&EbpfExtension::php_detach_kprobe>("detach_kprobe", {
  465. Php::ByVal("kernel_func", Php::Type::String),
  466. });
  467. ebpf_class.method<&EbpfExtension::php_detach_uprobe>("detach_uprobe", {
  468. Php::ByVal("binary_path", Php::Type::String),
  469. Php::ByVal("symbol", Php::Type::String),
  470. Php::ByVal("options", Php::Type::Array, false),
  471. });
  472. Php::Class<PerfEvent> ebpf_perf_event_class("PerfEvent");
  473. ebpf_perf_event_class.method<&PerfEvent::php_open_perf_buffer>("open_perf_buffer", {
  474. Php::ByVal("callback", Php::Type::Callable),
  475. });
  476. extension.add(std::move(ebpf_class));
  477. extension.add(std::move(ebpf_perf_event_class));
  478. return extension;
  479. }
  480. }