diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 65b7b4157ba7..82e6ce709847 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -194,6 +194,7 @@ #include #include #include +#include #include #include #include diff --git a/crates/c-api/include/wasmtime/profiling.h b/crates/c-api/include/wasmtime/profiling.h new file mode 100644 index 000000000000..df3a689663f2 --- /dev/null +++ b/crates/c-api/include/wasmtime/profiling.h @@ -0,0 +1,113 @@ +/** + * \file wasmtime/profiling.h + * + * \brief API for Wasmtime guest profiler + */ + +#ifndef WASMTIME_PROFILING_H +#define WASMTIME_PROFILING_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Collects basic profiling data for a single WebAssembly guest. + * + * To use this, you’ll need to arrange to call #wasmtime_guestprofiler_sample at + * regular intervals while the guest is on the stack. The most straightforward + * way to do that is to call it from a callback registered with + * #wasmtime_store_epoch_deadline_callback. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.GuestProfiler.html + */ +typedef struct wasmtime_guestprofiler wasmtime_guestprofiler_t; + +/** + * \brief Deletes profiler without finishing it. + * + * \param guestprofiler profiler that is being deleted + */ +WASM_API_EXTERN void wasmtime_guestprofiler_delete( + /* own */ wasmtime_guestprofiler_t *guestprofiler); + +/** + * \typedef wasmtime_guestprofiler_modules_t + * \brief Alias to #wasmtime_guestprofiler_modules + * + * \struct #wasmtime_guestprofiler_modules + * \brief Tuple of name and module for passing into #wasmtime_guestprofiler_new. + */ +typedef struct wasmtime_guestprofiler_modules { + const wasm_name_t *name; //!< Name recorded in the profile. + const wasmtime_module_t + *mod; //!< Module that is being allowed to appear in captured stack trace. +} wasmtime_guestprofiler_modules_t; + +/** + * \brief Begin profiling a new guest. + * + * \param module_name name recorded in the profile + * \param interval_nanos intended sampling interval in nanoseconds recorded in + * the profile + * \param modules modules and associated names that will appear in + * captured stack traces, pointer to the first element + * \param modules_len count of elements in `modules` + * + * \return Created profiler that is owned by the caller. + * + * This function does not take ownership of the arguments. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.GuestProfiler.html#method.new + */ +WASM_API_EXTERN /* own */ wasmtime_guestprofiler_t *wasmtime_guestprofiler_new( + const wasm_name_t *module_name, uint64_t interval_nanos, + const wasmtime_guestprofiler_modules_t *modules, size_t modules_len); + +/** + * \brief Add a sample to the profile. + * + * \param guestprofiler the profiler the sample is being added to + * \param store store that is being used to collect the backtraces + * + * This function does not take ownership of the arguments. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.GuestProfiler.html#method.sample + */ +WASM_API_EXTERN void +wasmtime_guestprofiler_sample(wasmtime_guestprofiler_t *guestprofiler, + const wasmtime_store_t *store); + +/** + * \brief Writes out the captured profile. + * + * \param guestprofiler the profiler which is being finished and deleted + * \param out pointer to where #wasm_byte_vec_t containing generated + * file will be written + * + * \return Returns #wasmtime_error_t owned by the caller in case of error, + * `NULL` otherwise. + * + * This function takes ownership of `guestprofiler`, even when error is + * returned. + * Only when returning without error `out` is filled with #wasm_byte_vec_t owned + * by the caller. + * + * For more information see the Rust documentation at: + * https://docs.wasmtime.dev/api/wasmtime/struct.GuestProfiler.html#method.finish + */ +WASM_API_EXTERN /* own */ wasmtime_error_t * +wasmtime_guestprofiler_finish(/* own */ wasmtime_guestprofiler_t *guestprofiler, + /* own */ wasm_byte_vec_t *out); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_PROFILING_H diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index fb686427e6d6..42f61719623e 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -23,6 +23,8 @@ mod instance; mod linker; mod memory; mod module; +#[cfg(feature = "profiling")] +mod profiling; mod r#ref; mod store; mod table; diff --git a/crates/c-api/src/profiling.rs b/crates/c-api/src/profiling.rs new file mode 100644 index 000000000000..25fa32ed7ec7 --- /dev/null +++ b/crates/c-api/src/profiling.rs @@ -0,0 +1,64 @@ +use crate::{wasm_byte_vec_t, wasm_name_t, wasmtime_error_t, wasmtime_module_t, wasmtime_store_t}; +use std::slice; +use std::str::from_utf8; +use std::time::Duration; +use wasmtime::GuestProfiler; + +pub struct wasmtime_guestprofiler_t { + guest_profiler: GuestProfiler, +} + +wasmtime_c_api_macros::declare_own!(wasmtime_guestprofiler_t); + +#[repr(C)] +pub struct wasmtime_guestprofiler_modules_t<'a> { + name: &'a wasm_name_t, + module: &'a wasmtime_module_t, +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_guestprofiler_new( + module_name: &wasm_name_t, + interval_nanos: u64, + modules: *const wasmtime_guestprofiler_modules_t, + modules_len: usize, +) -> Box { + let module_name = from_utf8(&module_name.as_slice()).expect("not valid utf-8"); + let list = slice::from_raw_parts(modules, modules_len) + .iter() + .map(|entry| { + ( + from_utf8(entry.name.as_slice()) + .expect("not valid utf-8") + .to_owned(), + entry.module.module.clone(), + ) + }) + .collect(); + Box::new(wasmtime_guestprofiler_t { + guest_profiler: GuestProfiler::new(module_name, Duration::from_nanos(interval_nanos), list), + }) +} + +#[no_mangle] +pub extern "C" fn wasmtime_guestprofiler_sample( + guestprofiler: &mut wasmtime_guestprofiler_t, + store: &wasmtime_store_t, +) { + guestprofiler.guest_profiler.sample(&store.store); +} + +#[no_mangle] +pub extern "C" fn wasmtime_guestprofiler_finish( + guestprofiler: Box, + out: &mut wasm_byte_vec_t, +) -> Option> { + let mut buf = vec![]; + match guestprofiler.guest_profiler.finish(&mut buf) { + Ok(()) => { + out.set_buffer(buf); + None + } + Err(e) => Some(Box::new(e.into())), + } +}