-
Notifications
You must be signed in to change notification settings - Fork 148
Guest tracing improvements to use tracing
crate
#844
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
base: main
Are you sure you want to change the base?
Changes from all commits
99c8673
8638825
896eeca
7073c43
f60fd7b
cab09b7
6e36dd5
1449189
884fddb
77317a8
b1f71b8
0ed8377
b59bc85
4b95111
f00d63e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,12 +94,11 @@ Once the container or the exe is running, the trace output can be viewed in the | |
|
||
## Guest Tracing, Unwinding, and Memory Profiling | ||
|
||
Hyperlight provides advanced observability features for guest code running inside micro virtual machines. You can enable guest-side tracing, stack unwinding, and memory profiling using the `trace_guest`, `unwind_guest`, and `mem_profile` features. This section explains how to build, run, and inspect guest traces. | ||
Hyperlight provides advanced observability features for guest code running inside micro virtual machines. You can enable guest-side tracing, stack unwinding, and memory profiling using the `trace_guest` and `mem_profile` features. This section explains how to build, run, and inspect guest traces. | ||
|
||
The following features are available for guest tracing: | ||
- `trace_guest`: Enables tracing for guest code, capturing function calls and execution time. | ||
- `unwind_guest`: Enables stack unwinding for guest code, allowing you to capture stack traces. | ||
- `mem_profile`: Enables memory profiling for guest code, capturing memory allocations and usage. | ||
- `mem_profile`: Enables memory profiling for guest code with stack unwinding, capturing memory allocations and usage. | ||
|
||
### Building a Guest with Tracing Support | ||
|
||
|
@@ -110,19 +109,57 @@ just build-rust-guests debug trace_guest | |
just move-rust-guests debug | ||
``` | ||
|
||
This will build the guest binaries with the `trace_guest` feature enabled and move them to the appropriate location for use by the host. | ||
This builds the guest binaries with the `trace_guest` feature enabled and move them to the appropriate location for use by the host. | ||
|
||
**NOTE**: To enable the tracing in your application you need to use the `trace_guest` feature on the `hyperlight-guest-bin` and `hyperlight-guest` crates. | ||
|
||
### Running a Hyperlight Example with Guest Tracing | ||
|
||
Once the guest is built, you can run a Hyperlight example with guest tracing enabled. For example: | ||
|
||
```bash | ||
cargo run --example hello-world --features trace_guest | ||
RUST_LOG="info,hyperlight_host::sandbox=info,hyperlight_guest=trace,hyperlight_guest_bin=trace" cargo run --example tracing-otlp --features trace_guest | ||
``` | ||
|
||
This will execute the `hello-world` example, loading the guest with tracing enabled. During execution, trace data will be collected and written to a file in the `trace` directory. | ||
This will execute the `tracing-otlp` example, loading the guest with tracing enabled. | ||
During execution, trace data will be collected on the host and exported as `opentelemetry` spans/events. | ||
|
||
You can set up a collector to gather all the traces and inspect the traces from both host and guests. | ||
|
||
Due to the nature of execution inside a Sandbox, on a call basis, the guest tracing sets up a stack of spans to keep track of the correct parents for the incoming | ||
guest spans. | ||
We start with `call-to-guest` which contains all the spans coming from a guest. Additionally, for each exit into the host, we add another layer marking it with | ||
a `call-to-host` span to follow the execution in the host and correctly set it as a child of the active span in the guest. | ||
This logic simulates the propagation of `Opentelemetry` context that is usually done between two services, but cannot be done here seamlessly because the guest side | ||
runs `no_std` which `opentelemetry` doesn't know. | ||
|
||
#### How it works | ||
|
||
##### Guest | ||
|
||
When the guest starts executing the `entrypoint` function, it receives a `max_log_level` parameter that tells the guest what kind of logging level is expected from it. | ||
|
||
The `trace_guest` logic takes advantage of this parameter and when the `max_log_level` is `trace`, it allocates a custom made `GuestSubscriber` that implements the `Subscriber` | ||
trait from `tracing_core` that allows defining a subscriber for the `tracing` crate to handle new spans and events. | ||
|
||
This custom subscriber stores the spans and events in a buffer initialized only when tracing is enabled. For each new span and event, a method is called on the custom subscriber which | ||
not only stores the data, but also keeps track of the hierarchy and dependencies between the other spans/events. | ||
|
||
When the storage space is filled, the guest triggers a VM Exit that sends the guest pointers to the host. The host can access the guest memory, get the data and parse it to create the `spans` and `events` using the `opentelemetry` crate which allows specifying the starting and ending timestamps | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe an option could be to only emit logs during regular VMexits in order to minimize context switches. Or maybe another option could be to discard logs when the buffer is full in order to not incur extra context switches |
||
which are captured in the guest using the `TSC`. | ||
|
||
To improve performance, for each VMExit, the guest adds metadata for the host to be able to report the tracing data and free space. | ||
|
||
##### Host | ||
|
||
When a guest exits, the host checks for metadata from the guest reporting tracing data. | ||
If tracing data is found, the host starts parsing it and reconstructing a tree which represents the spans hierarchy. | ||
|
||
Additionally, the host also adds new children `span`s to the guest's reported active span, emphasizing the spans created on the host as a result of a temporary VM Exit. This helps visualize a call into the guest with context propagated across the VM boundary. | ||
|
||
The host creates `opentelemetry` spans and events for each guest span and event reported. | ||
|
||
### Inspecting Guest Trace Files | ||
### Inspecting Guest memory Trace Files (for mem_profile) | ||
|
||
To inspect the trace file generated by the guest, use the `trace_dump` crate. You will need the path to the guest symbols and the trace file. Run the following command: | ||
|
||
|
@@ -140,7 +177,7 @@ This command will list the stack frames and tracing information captured during | |
cargo run -p trace_dump ./src/tests/rust_guests/bin/debug/simpleguest ./trace/<UUID>.trace list_frames | ||
``` | ||
|
||
You can use additional features such as `unwind_guest` and `mem_profile` by enabling them during the build and run steps. | ||
You can use the `mem_profile` additional feature by enabling them during the build and run steps. | ||
|
||
> **Note:** Make sure to follow the build and run steps in order, and ensure that the guest binaries are up to date before running the host example. | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.