From d58da5d92f600ca59b4debce3407dcdfc8500061 Mon Sep 17 00:00:00 2001 From: wunder957 Date: Tue, 26 Sep 2023 16:27:33 +0800 Subject: [PATCH] Draft of CORE --- docs/draft/src/CO-RE.drawio => CORE/README.md | 0 docs/draft/CO-RE.md | 116 +++++++++++++--- docs/draft/src/spm-arch.drawio | 57 ++++++++ docs/draft/src/spm-arch.png | Bin 0 -> 18262 bytes docs/draft/src/spm-state-flow.drawio | 125 ++++++++++++++++++ docs/draft/src/spm-state-flow.png | Bin 0 -> 27274 bytes 6 files changed, 278 insertions(+), 20 deletions(-) rename docs/draft/src/CO-RE.drawio => CORE/README.md (100%) create mode 100644 docs/draft/src/spm-arch.drawio create mode 100644 docs/draft/src/spm-arch.png create mode 100644 docs/draft/src/spm-state-flow.drawio create mode 100644 docs/draft/src/spm-state-flow.png diff --git a/docs/draft/src/CO-RE.drawio b/CORE/README.md similarity index 100% rename from docs/draft/src/CO-RE.drawio rename to CORE/README.md diff --git a/docs/draft/CO-RE.md b/docs/draft/CO-RE.md index dd5d600..5d258cb 100644 --- a/docs/draft/CO-RE.md +++ b/docs/draft/CO-RE.md @@ -12,17 +12,13 @@ A BPF program is a piece of user-provided code which is injected straight into a All this means that you can no longer compile your BPF program locally using kernel headers of your dev server and distribute it in compiled form to other systems, while expecting it to work and produce correct results. **This is because kernel headers for different kernel versions will specify a different memory layout of data your program relies on.** - ### 1.2 Status quo One possible solution is to compile the BPF program in real-time on the actual running machine. This is what [BCC](https://github.com/iovisor/bcc/) did and our `BccTracer` is based on it. However, this approach has a few drawbacks: - Clang/LLVM combo is a big library, resulting in big fat binaries that need to be distributed with your application.(We do suffer from this problem.) - - Clang/LLVM combo is resource-heavy, so when you are compiling BPF code at start up, you'll use a significant amount of resources, potentially tipping over a carefully balanced production workfload. And vice versa, on a busy host, compiling a small BPF program might take minutes in some cases.(Yes, it's slowing down our startup and causes a large performance impact when popping containers at scale.) - - You are making a big bet that the target system will have kernel headers present, which most of the time is not a problem, but sometimes can cause a lot of headaches. (Yes, see [how-to/run-with-docker](../how-to/run-with-docker.md)) - - BPF program testing and development iteration is quite painful as well. (Yes, we haven't found a good way to test BPF programs:[#47](https://github.com/hitsz-ids/duetector/issues/47).) ### 1.3 BPF CO-RE helps @@ -32,9 +28,7 @@ BPF CO-RE, by making the kernel self-contained information, together with a BPF BPF CO-RE requires the following parts: - BTF type information, which allows to capture crucial pieces of information about kernel and BPF program types and code, enabling all the other parts of BPF CO-RE puzzle; - - BPF loader ([libbpf](https://github.com/libbpf/libbpf)) ties BTFs from kernel and BPF program together to adjust compiled BPF code to specific kernel on target hosts; - - kernel, while staying completely BPF CO-RE-agnostic, provides advanced BPF features to enable some of the more advanced scenarios. There are three language have its own CO-RE implementation: @@ -53,38 +47,120 @@ There are three language have its own CO-RE implementation: > > - If one `SubprocessTracer` not based on `CO-RE`, it will not require BTF. -## 1.5 Introduction of SubprocessMonitor +## 2 Introduction of SubprocessMonitor `SubprocessMonitor` will provide support for subprocess-based tracer.Its primary goal is to support the CO-RE program. This is because CO-RE programs are often written through the underlying language(C/Rust). It's hard to write a CO-RE program in Python(CPython may support, but it's not a good idea). And It's hard for C/Rust to write a `duetector`-like program. So `SubprocessMonitor` will provide a way to write a CO-RE program in C/Rust and use it in Python. Instead of writing cumbersome programs in userspace, C/Rust users simply communicate with `SubprocessMonitor` through a protocol. Furthermore, `SubprocessMonitor` also support non-BPF program, as long as it can be run in a subprocess and obey the protocol. -## 2. Protocol +## 3. Protocol + +**This is a very early experimental Protocol with a high probability of extreme variability, and at the same time, many of the fields are undefined** -### 2.1 Protocol Definition +### 3.1 Protocol Definition -#### 2.1.1 Message Format +#### 3.1.1 Message Format -```json +```python { + "proto": "duetector", // or opentelemetry if type is event + "type": "" // init, event, stop, stopped + "version": "0.1", // protocol version + "payload":{} } ``` -#### 2.1.2 Init Message +#### 3.1.2 Init Message + +##### From host(reqeust) + +type: init + +payload: +- poll_time: unit: seconds +- kill_timeout: unit, seconds +- config: object, config of tracer + +##### From subprocess(response) + +type: init + +payload: +- name: string, name of tracer +- version: string, version of tracer + +#### 3.1.3 Event Message + +##### From host + +type: event + +payload: + +##### From subprocess + +type: event + +payload: object of tracking data or OpenTelemetry OTLP things + +#### 3.1.3 Stop Message + +##### From host + +type: stop + +payload: + +##### From subprocess + +type: stopped + +payload: + +### 3.2 Intergation with OpenTelemetry + +After we intro OpenTelemetry in [#25](https://github.com/hitsz-ids/duetector/issues/25), we can support OpenTelemetry protocol to passthrough the `Event` message. + +## 4. Design + +### 4.1 Architecture + +![SubprocessMonitorArch](./src/spm-arch.png) + +### 4.2 State Transfer & Failure Handling + +![SubrpocessMonitorFailureHandling](./src/spm-state-flow.png) + +- When a `SubprocessTracer` `attatch` to the `SubprocessHost`, host will `Popen` a subprocess and send `init` message to subprocess. +- After the subprocess is started, subprocess will send a `init` message to host. +- Subprocess will send `Event` message to host when it has something to report. +- Host will poll the subprocess's stdout and stderr periodically and send `Event` message to subprocess. +- When `detach` is called, host will send SIGTERM and `stop` message to subprocess and wait for subprocess to exit, if subprocess doesn't exit in `kill_timeout` seconds, host will send SIGKILL to subprocess. +- When subprocess stops, it will send a `stopped` message to host. + +#### When Host Process Crashes + +In this case, subprocess becomes an orphan process. It will be adopted by `init` process and continue to run. Subprocess will no longer receive `Event` message from host. No message is received within twice or more `poll time``, subprocess can be assumed that the host process has crashed. Subprocess will send a `stopped` message to host and exit. + +#### When Subprocess Crashes -#### 2.1.3 Event Message +In this case, host can check subprocess is alive or not. If subprocess is not alive, host should log an `warming` for every poll. -#### 2.1.3 Stop Message +Host can be configured to restart subprocess when subprocess crashes for a certain number of times. -### 2.2 Intergation with OpenTelemetry +## 5. Migration -## 3. Design +### 5.1 Migration from BccTracer -### 3.1 Architecture +1. Reimplement `Tracer` in C/Rust/Go +2. Commit code to [/CORE](../../CORE/) +3. Implement `SubprocessTracer` in `duetector` -### 3.2 Failure Handling +Note: +- Need to add the corresponding Github Action to compile the code and upload the binary to Github Release. +- Need to change our docker image to include the binary. -## 4. Migration +## 6. Others -### 4.1 Migration from BccTracer +- Support `SubprocessTracer` template in `TracerManager` diff --git a/docs/draft/src/spm-arch.drawio b/docs/draft/src/spm-arch.drawio new file mode 100644 index 0000000..055017b --- /dev/null +++ b/docs/draft/src/spm-arch.drawio @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/draft/src/spm-arch.png b/docs/draft/src/spm-arch.png new file mode 100644 index 0000000000000000000000000000000000000000..bba70802bd6d7d3cd69c30d5ffb4c7118d1258b0 GIT binary patch literal 18262 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/draft/src/spm-state-flow.png b/docs/draft/src/spm-state-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..5dc51d9ad56e835ccf6f9db0375a572d1cf9c66c GIT binary patch literal 27274 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 EA4`hI8UO$Q literal 0 HcmV?d00001