-
-
Notifications
You must be signed in to change notification settings - Fork 194
feat(tracepoint): add sched_process_exec tracepoint #2154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sparkzky
wants to merge
6
commits into
DragonOS-Community:master
Choose a base branch
from
sparkzky:feature/sched-process-exec-tracepoint
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
e673c05
feat(tracepoint): add sched_process_exec tracepoint
sparkzky 5b1c509
test(dunitest): add sched_process_exec tracepoint test
sparkzky 0cff71c
fix(test): sched_tracepoint id assertion - id starts from 0
sparkzky 72222ff
fix(execve): release basic read lock before triggering sched_process_…
sparkzky a9e1305
fix(execve): avoid splitting multi-byte UTF-8 char in comm truncation
sparkzky 5721983
style(execve): use name.len() per clippy needless_as_bytes
sparkzky File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| //! 进程/调度类 tracepoint 声明。 | ||
| //! | ||
| //! 字段参考 Linux `include/trace/events/sched.h`。 | ||
| //! 注意:DragonOS 的 `define_event_trace!` 宏不支持 Linux 的 `__string` 动态字符串, | ||
| //! 且当前未实现 `bpf_get_current_comm()` helper,故 `comm` 直接放入 payload。 | ||
|
|
||
| use crate::define_event_trace; | ||
|
|
||
| define_event_trace!( | ||
| sched_process_exec, | ||
| TP_system(sched), | ||
| TP_PROTO(comm: &str, pid: i32, old_pid: i32), | ||
| TP_STRUCT__entry { | ||
| comm: [u8; 16], | ||
| pid: i32, | ||
| old_pid: i32, | ||
| }, | ||
| TP_fast_assign { | ||
| comm: { | ||
| // 对齐 Linux TASK_COMM_LEN=16(15 字符 + NUL)。 | ||
| let mut buf = [0u8; 16]; | ||
| let bytes = comm.as_bytes(); | ||
| let len = bytes.len().min(15); | ||
| buf[..len].copy_from_slice(&bytes[..len]); | ||
| buf | ||
| }, | ||
| pid: pid, | ||
| old_pid: old_pid, | ||
| }, | ||
| TP_ident(__entry), | ||
| TP_printk({ | ||
| let nul = __entry.comm.iter().position(|&b| b == 0).unwrap_or(16); | ||
| let comm = core::str::from_utf8(&__entry.comm[..nul]).unwrap_or("invalid utf8"); | ||
| format!("comm={} pid={} old_pid={}", comm, __entry.pid, __entry.old_pid) | ||
| }) | ||
| ); | ||
191 changes: 191 additions & 0 deletions
191
user/apps/tests/dunitest/suites/normal/sched_tracepoint.cc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| // sched_process_exec tracepoint 语义测试。 | ||
| // | ||
| // 验证: | ||
| // 1. sched_process_exec 事件在 debugfs 下正确导出(enable/format/id 文件 + format 字段)。 | ||
| // 2. enable 后执行 execve 会触发该 tracepoint,并在 trace 文件中留下记录。 | ||
| // | ||
| // 对应 issue #2149。 | ||
|
|
||
| #include <gtest/gtest.h> | ||
|
|
||
| #include <errno.h> | ||
| #include <fcntl.h> | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include <sys/mount.h> | ||
| #include <sys/stat.h> | ||
| #include <sys/wait.h> | ||
| #include <unistd.h> | ||
|
|
||
| #include <string> | ||
|
|
||
| namespace { | ||
|
|
||
| constexpr char kHelperExec[] = "--sched-tp-helper-exit0"; | ||
|
|
||
| // 读 path 全部内容(非阻塞,读到 EOF 为止)。 | ||
| std::string read_all(const char* path) { | ||
| int fd = open(path, O_RDONLY); | ||
| EXPECT_GE(fd, 0) << "open(" << path << ") failed: errno=" << errno << " (" | ||
| << strerror(errno) << ")"; | ||
| if (fd < 0) { | ||
| return {}; | ||
| } | ||
| std::string out; | ||
| char buf[256]; | ||
| while (true) { | ||
| ssize_t n = read(fd, buf, sizeof(buf)); | ||
| if (n <= 0) { | ||
| break; | ||
| } | ||
| out.append(buf, static_cast<size_t>(n)); | ||
| } | ||
| close(fd); | ||
| return out; | ||
| } | ||
|
|
||
| // 向 path 写 data,返回是否写成功。 | ||
| bool write_file(const char* path, const char* data) { | ||
| int fd = open(path, O_WRONLY); | ||
| if (fd < 0) { | ||
| return false; | ||
| } | ||
| size_t len = strlen(data); | ||
| ssize_t n = write(fd, data, len); | ||
| close(fd); | ||
| return n == static_cast<ssize_t>(len); | ||
| } | ||
|
|
||
| // mount debugfs 到 root。 | ||
| void mount_debugfs(const char* root) { | ||
| ASSERT_EQ(0, mount("none", root, "debugfs", 0, nullptr)) | ||
| << "mount debugfs failed: errno=" << errno << " (" << strerror(errno) << ")"; | ||
| } | ||
|
|
||
| // 子模式:被 execve 进来后立即退出 0。 | ||
| [[noreturn]] void helper_exec_exit0() { | ||
| char arg0[] = "/proc/self/exe"; | ||
| char arg1[] = "--sched-tp-helper-exit0"; | ||
| char* const argv[] = {arg0, arg1, nullptr}; | ||
| char* const envp[] = {nullptr}; | ||
| execve("/proc/self/exe", argv, envp); | ||
| _exit(127); | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| // 事件文件存在且 format 含全部字段。 | ||
| TEST(SchedProcessExecTp, EventFilesExist) { | ||
| char root[128] = {}; | ||
| snprintf(root, sizeof(root), "/tmp/sched_tp_events_%d", getpid()); | ||
| ASSERT_EQ(0, mkdir(root, 0755)) << strerror(errno); | ||
|
|
||
| mount_debugfs(root); | ||
|
|
||
| const char* base_rel = "/tracing/events/sched/sched_process_exec"; | ||
| char base[256] = {}; | ||
| snprintf(base, sizeof(base), "%s%s", root, base_rel); | ||
|
|
||
| struct stat st = {}; | ||
| ASSERT_EQ(0, stat(base, &st)) << "missing event dir " << base << ": " << strerror(errno); | ||
| EXPECT_TRUE(S_ISDIR(st.st_mode)); | ||
|
|
||
| char file[320] = {}; | ||
| for (const char* leaf : {"enable", "format", "id"}) { | ||
| snprintf(file, sizeof(file), "%s/%s", base, leaf); | ||
| ASSERT_EQ(0, stat(file, &st)) | ||
| << "missing " << file << ": " << strerror(errno); | ||
| } | ||
|
|
||
| // format 文件应含事件名与全部字段。 | ||
| snprintf(file, sizeof(file), "%s/format", base); | ||
| std::string fmt = read_all(file); | ||
| ASSERT_FALSE(fmt.empty()); | ||
| for (const char* needle : | ||
| {"sched_process_exec", "common_pid", "comm", "pid", "old_pid"}) { | ||
| EXPECT_NE(std::string::npos, fmt.find(needle)) | ||
| << "format missing \"" << needle << "\"\n" | ||
| << fmt; | ||
| } | ||
|
|
||
| // enable 默认为 "0"(未启用)。 | ||
| snprintf(file, sizeof(file), "%s/enable", base); | ||
| std::string enable = read_all(file); | ||
| EXPECT_NE(std::string::npos, enable.find("0")) << "enable not '0' by default: " << enable; | ||
|
|
||
| // id 应为非负整数(DragonOS tracepoint id 从 0 开始递增分配)。 | ||
| snprintf(file, sizeof(file), "%s/id", base); | ||
| std::string id = read_all(file); | ||
| EXPECT_FALSE(id.empty()); | ||
| char* end = nullptr; | ||
| long idval = strtol(id.c_str(), &end, 10); | ||
| // 确认整个字符串都是数字(允许尾部换行),而非仅前缀可解析。 | ||
| ASSERT_NE(end, id.c_str()) << "id not numeric: " << id; | ||
| while (end != nullptr && (*end == '\n' || *end == '\r' || *end == ' ')) ++end; | ||
| EXPECT_EQ(end != nullptr && *end == '\0', true) << "id has trailing garbage: " << id; | ||
| EXPECT_GE(idval, 0) << "invalid id: " << id; | ||
|
|
||
| EXPECT_EQ(0, umount(root)) << strerror(errno); | ||
| EXPECT_EQ(0, rmdir(root)) << strerror(errno); | ||
| } | ||
|
|
||
| // enable 后 execve 应触发事件,trace 文件留下记录。 | ||
| TEST(SchedProcessExecTp, FiresOnExecve) { | ||
| char root[128] = {}; | ||
| snprintf(root, sizeof(root), "/tmp/sched_tp_fire_%d", getpid()); | ||
| ASSERT_EQ(0, mkdir(root, 0755)) << strerror(errno); | ||
|
|
||
| mount_debugfs(root); | ||
|
|
||
| const char* base_rel = "/tracing/events/sched/sched_process_exec"; | ||
| char base[256] = {}; | ||
| snprintf(base, sizeof(base), "%s%s", root, base_rel); | ||
|
|
||
| // 启用事件。 | ||
| char enable_path[320] = {}; | ||
| snprintf(enable_path, sizeof(enable_path), "%s/enable", base); | ||
| ASSERT_TRUE(write_file(enable_path, "1")) << "enable write failed"; | ||
|
|
||
| // 清空 ring buffer:向 trace 写任意字节触发 clear。 | ||
| char trace_path[256] = {}; | ||
| snprintf(trace_path, sizeof(trace_path), "%s/tracing/trace", root); | ||
| ASSERT_TRUE(write_file(trace_path, "1")) << "trace clear write failed"; | ||
|
|
||
| // fork + execve 自身触发 sched_process_exec。 | ||
| pid_t child = fork(); | ||
| ASSERT_GE(child, 0) << "fork failed: " << strerror(errno); | ||
| if (child == 0) { | ||
| helper_exec_exit0(); | ||
| } | ||
|
|
||
| int status = 0; | ||
| ASSERT_EQ(child, waitpid(child, &status, 0)) << "waitpid failed: " << strerror(errno); | ||
| ASSERT_TRUE(WIFEXITED(status)) << "child did not exit normally, status=" << status; | ||
| EXPECT_EQ(0, WEXITSTATUS(status)) << "helper exit code != 0"; | ||
|
|
||
| // 读 trace 快照,断言含 sched_process_exec 记录。 | ||
| std::string trace = read_all(trace_path); | ||
| ASSERT_FALSE(trace.empty()) << "trace empty after execve"; | ||
| EXPECT_NE(std::string::npos, trace.find("sched_process_exec(")) | ||
| << "no sched_process_exec record in trace:\n" | ||
| << trace; | ||
| // TP_printk 输出的字段。 | ||
| EXPECT_NE(std::string::npos, trace.find("comm=")) | ||
| << "trace missing comm= field:\n" | ||
| << trace; | ||
|
|
||
| // 关闭事件并清理。 | ||
| write_file(enable_path, "0"); | ||
| EXPECT_EQ(0, umount(root)) << strerror(errno); | ||
| EXPECT_EQ(0, rmdir(root)) << strerror(errno); | ||
| } | ||
|
|
||
| int main(int argc, char** argv) { | ||
| // 子模式:被 execve 进来后立即退出 0。 | ||
| if (argc >= 2 && strcmp(argv[1], kHelperExec) == 0) { | ||
| _exit(0); | ||
| } | ||
| ::testing::InitGoogleTest(&argc, argv); | ||
| return RUN_ALL_TESTS(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里把事件 payload 定义成
comm会让/tracing/events/sched/sched_process_exec/format暴露为comm,pid,old_pid,而 Linux 6.6 的同名 tracepoint ABI 是filename,pid,old_pid(filename还是第一项,见 https://codebrowser.dev/linux/linux/include/trace/events/sched.h.html#431)。因此按 Linux format 编译或按filename过滤/解析的 eBPF/trace 工具会在 DragonOS 上找不到字段,raw offset 也会读错,恰好破坏本次为 agentsight 兼容该 tracepoint 的目的;即使暂时不能实现动态__string,也应保留filename字段语义/名称并从 exec 参数填充。Useful? React with 👍 / 👎.