diff --git a/.github/script/bpftrace/hello_world.bt b/.github/script/bpftrace/hello_world.bt new file mode 100644 index 00000000..0d5aeb01 --- /dev/null +++ b/.github/script/bpftrace/hello_world.bt @@ -0,0 +1 @@ +BEGIN { printf("hello world\n"); } \ No newline at end of file diff --git a/.github/script/bpftrace/trace_open_syscalls.bt b/.github/script/bpftrace/trace_open_syscalls.bt new file mode 100644 index 00000000..cfe3d8a1 --- /dev/null +++ b/.github/script/bpftrace/trace_open_syscalls.bt @@ -0,0 +1,29 @@ +BEGIN +{ + printf("Tracing open syscalls... Hit Ctrl-C to end.\n"); + printf("%-6s %-16s %4s %3s %s\n", "PID", "COMM", "FD", "ERR", "PATH"); +} + +tracepoint:syscalls:sys_enter_open, +tracepoint:syscalls:sys_enter_openat +{ + @filename[tid] = args.filename; +} + +tracepoint:syscalls:sys_exit_open, +tracepoint:syscalls:sys_exit_openat +/@filename[tid]/ +{ + $ret = args.ret; + $fd = $ret >= 0 ? $ret : -1; + $errno = $ret >= 0 ? 0 : - $ret; + + printf("%-6d %-16s %4d %3d %s\n", pid, comm, $fd, $errno, + str(@filename[tid])); + delete(@filename[tid]); +} + +END +{ + clear(@filename); +} diff --git a/.github/script/run_bpftrace.py b/.github/script/run_bpftrace.py new file mode 100644 index 00000000..61e4efac --- /dev/null +++ b/.github/script/run_bpftrace.py @@ -0,0 +1,89 @@ +import sys +import asyncio +import typing +import signal +import re +import logging + +AGENT_TIMEOUT = 5 + +class BPFTraceTestFail(Exception): + pass + +async def handle_stdout(stdout: asyncio.StreamReader, notify: asyncio.Event, title: str, check_error: bool = False, pattern: typing.Optional[str] = None) -> bool: + try: + while True: + task_notify = asyncio.create_task(notify.wait()) + task_readline = asyncio.create_task(stdout.readline()) + done, pending = await asyncio.wait([task_notify, task_readline], return_when=asyncio.FIRST_COMPLETED) + + for task in pending: + task.cancel() + + if task_readline in done: + output = task_readline.result().decode() + print(f"{title}:", output, end="") + + if pattern is not None: + if re.match(pattern, output.strip()): + notify.set() + else: + if pattern == output.strip(): + notify.set() + + if task_notify in done or stdout.at_eof(): + notify.set() + except asyncio.CancelledError: + pass # Masquer l'erreur CancelledError + +async def start_agent(cmd_args: typing.List[str], title: str, notify: asyncio.Event, pattern: typing.Optional[str] = None) -> asyncio.Task: + print(" ".join(cmd_args)) + process = await asyncio.create_subprocess_exec(*cmd_args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT) + if process: + return asyncio.create_task(handle_stdout(process.stdout, notify, title, check_error=True, pattern=pattern)) + else: + return None + +async def main(): + if len(sys.argv) < 4: + print("Usage: python script.py ") + return + + bpftime_path = sys.argv[1] + install_directory = sys.argv[2] + bpftrace_cmd = sys.argv[3] + + pattern = None + if len(sys.argv) > 4: + pattern = sys.argv[4] + + try: + should_exit = asyncio.Event() + agent_out_task = await start_agent( + [bpftime_path, "-i", install_directory, "start", "bpftrace", bpftrace_cmd], + "AGENT", + should_exit, + pattern=pattern + ) + test_fail = False + + if agent_out_task: + try: + await asyncio.wait_for(should_exit.wait(), AGENT_TIMEOUT) + except asyncio.TimeoutError: + test_fail = True + finally: + if not agent_out_task.cancelled(): + agent_out_task.cancel() + if test_fail: + raise BPFTraceTestFail(f"The test with command \"{bpftrace_cmd}\" has failed") + logging.info(f"Test \"{bpftrace_cmd}\" succeed") + + + finally: + should_exit.set() + if agent_out_task: + agent_out_task.cancel() + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/.github/workflows/test-bpftrace.yml b/.github/workflows/test-bpftrace.yml new file mode 100644 index 00000000..1ad2dec3 --- /dev/null +++ b/.github/workflows/test-bpftrace.yml @@ -0,0 +1,111 @@ +name: Install and test bpftrace + +on: + workflow_dispatch: + push: + branches: ["*"] + pull_request: + branches: ['master'] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +jobs: + build-runtime: + strategy: + matrix: + enable_jit: + - true + - false + container: + - image: ubuntu-2204 + name: ubuntu + - image: fedora-39 + name: fedora + runs-on: ubuntu-22.04 + container: + image: "manjusakalza/bpftime-base-image:${{matrix.container.image}}" + options: --privileged + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Build and install runtime (with llvm-jit) + if: ${{matrix.enable_jit}} + run: | + make release-with-llvm-jit -j + - name: Build and install runtime (without llvm-jit) + if: ${{!matrix.enable_jit}} + run: | + make release -j + - name: Upload build results (without jit) + uses: actions/upload-artifact@v3 + if: ${{!matrix.enable_jit}} + with: + name: runtime-package-no-jit-${{matrix.container.name}} + path: ~/.bpftime + - name: Upload build results (with jit) + uses: actions/upload-artifact@v3 + if: ${{matrix.enable_jit}} + with: + name: runtime-package-jit-${{matrix.container.name}} + path: ~/.bpftime + install-bpftrace-and-test: + runs-on: "ubuntu-latest" + needs: [build-runtime] + strategy: + matrix: + container: + - image: ubuntu-2204 + name: ubuntu + - image: fedora-39 + name: fedora + enable_jit: + - true + - false + bpftrace_tests: + - command: /__w/bpftime/bpftime/.github/script/bpftrace/trace_open_syscalls.bt + expected_output: '^\d+\s+\w+(\s+#\d+)?\s+-?\d+\s+-?\d+\s+\S+$' + - command: /__w/bpftime/bpftime/.github/script/bpftrace/hello_world.bt + expected_output: hello world + container: + image: "manjusakalza/bpftime-base-image:${{matrix.container.image}}" + options: --privileged -v /sys/kernel/debug/:/sys/kernel/debug:rw -v /sys/kernel/tracing:/sys/kernel/tracing:rw + steps: + - name: Download prebuilt runtime (with jit) + if: ${{matrix.enable_jit}} + uses: actions/download-artifact@v3 + with: + name: runtime-package-jit-${{matrix.container.name}} + path: /app/.bpftime + - name: Download prebuilt runtime (without jit) + if: ${{!matrix.enable_jit}} + uses: actions/download-artifact@v3 + with: + name: runtime-package-no-jit-${{matrix.container.name}} + path: /app/.bpftime + - name: Set permissions + run: | + chmod +x /app/.bpftime/* + - name: Show downloaded artifacts + run: | + ls /app/.bpftime + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Install bpftrace + run: | + if [ "${{ matrix.container.name }}" = "ubuntu" ]; then + apt-get update && apt-get install -y bpftrace + elif [ "${{ matrix.container.name }}" = "fedora" ]; then + dnf install -y bpftrace + else + echo "Unsupported container type" + exit 1 + fi + - name: Find tree + run: ls /**/* + - name: Run bpftrace commands + shell: bash + run: python3 /__w/bpftime/bpftime/.github/script/run_bpftrace.py "/app/.bpftime/bpftime" "/app/.bpftime" "${{matrix.bpftrace_tests.command}}" "${{matrix.bpftrace_tests.expected_output}}"