zng/
app.rs

1//! App extensions, context, events and commands API.
2//!
3//! # Runtime
4//!
5//! A typical app instance has two processes, the initial process called the *app-process*, and a second process called the
6//! *view-process*. The app-process implements the event loop and updates, the view-process implements the platform integration and
7//! renderer, the app-process controls the view-process, most of the time app implementers don't interact directly with it, except
8//! at the start where the view-process is spawned.
9//!
10//! The reason for this dual process architecture is mostly for resilience, the unsafe interactions with the operating system and
11//! graphics driver are isolated in a different process, in case of crashes the view-process is respawned automatically and
12//! all windows are recreated. It is possible to run the app in a single process, in this case the view runs in the main thread
13//! and the app main loop in another.
14//!
15//! ## View-Process
16//!
17//! To simplify distribution the view-process is an instance of the same app executable, the view-process crate injects
18//! their own "main" in the [`zng::env::init!`] call, automatically taking over the process if the executable spawns as a view-process.
19//!
20//! On the first instance of the app executable the `init` only inits the env and returns, the app init spawns a second process
21//! marked as the view-process, on this second instance the init call never returns, for this reason the init
22//! must be called early in main, all code before the `init` call runs in both the app and view processes.
23//!
24//! ```toml
25//! [dependencies]
26//! zng = { version = "0.19.2", features = ["view_prebuilt"] }
27//! ```
28//!
29//! ```no_run
30//! use zng::prelude::*;
31//!
32//! fn main() {
33//!     app_and_view();
34//!     zng::env::init!(); // init only returns if it is not called in the view-process.
35//!     app();
36//! }
37//!
38//! fn app_and_view() {
39//!     // code here runs in the app-process and view-process.
40//! }
41//!
42//! fn app() {
43//!     // code here only runs in the app-process.
44//!
45//!     APP.defaults().run(async {
46//!         // ..
47//!     })
48//! }
49//! ```
50//!
51//! ## Same Process
52//!
53//! You can also run the view in the same process, this mode of execution is slightly more efficient, but
54//! your app will not be resilient to crashes caused by the operating system or graphics driver, the app code
55//! will also run in a different thread, not the main.
56//!
57//! ```no_run
58//! use zng::prelude::*;
59//!
60//! fn main() {
61//!     zng::env::init!();
62//!     zng::view_process::prebuilt::run_same_process(app);
63//! }
64//!
65//! fn app() {
66//!     // code here runs in a different thread, the main thread becomes the view.
67//!     APP.defaults().run(async {
68//!         // ..
69//!     })
70//! }
71//! ```
72//!
73//! Note that you must still call `init!` as it also initializes the app metadata and directories.
74//!
75//! # Headless
76//!
77//! The app can also run *headless*, where no window is actually created, optionally with real rendering.
78//! This mode is useful for running integration tests, or for rendering images.
79//!
80//! ```
81//! use zng::prelude::*;
82//!
83//! let mut app = APP.defaults().run_headless(/* with_renderer: */ false);
84//! app.run_window(async {
85//!     Window! {
86//!         child = Text!("Some text");
87//!         auto_size = true;
88//!
89//!         render_mode = window::RenderMode::Software;
90//!         frame_capture_mode = window::FrameCaptureMode::Next;
91//!
92//!         on_frame_image_ready = async_hn!(|args| {
93//!             if let Some(img) = args.frame_image {
94//!                 // if the app runs with `run_headless(/* with_renderer: */ true)` an image is captured
95//!                 // and saved here.
96//!                 img.save("screenshot.png").await.unwrap();
97//!             }
98//!
99//!             // close the window, causing the app to exit.
100//!             WINDOW.close();
101//!         });
102//!     }
103//! });
104//! ```
105//!
106//! You can also run multiple headless apps in the same process, one per thread, if the crate is build using the `"multi_app"` feature.
107//!
108//! # App Extension
109//!
110//! Apps can be extended to provide new services and events, in fact all default services and events are implemented as extensions
111//! loaded by [`APP.defaults()`]. The app extension API is [`AppExtension`]. Usually extensions are named with suffix `Manager`, but
112//! that is not a requirement.
113//!
114//! ```
115//! use zng::{
116//!     APP,
117//!     app::{AppExtended, AppExtension},
118//! };
119//!
120//! #[derive(Default)]
121//! pub struct HelloManager {}
122//! impl AppExtension for HelloManager {
123//!     fn init(&mut self) {
124//!         println!("Hello init!");
125//!     }
126//!
127//!     fn update_preview(&mut self) {
128//!         println!("Hello before UI!");
129//!     }
130//!
131//!     fn update(&mut self) {
132//!         println!("Hello after UI!");
133//!     }
134//! }
135//!
136//! pub fn app() -> AppExtended<impl AppExtension> {
137//!     APP.defaults().extend(HelloManager::default())
138//! }
139//! ```
140//!
141//! ## Services
142//!
143//! App services are defined by convention, there is no service trait or struct. Proper service implementations follow
144//! these rules:
145//!
146//! #### App services are an unit struct named like a static
147//!
148//! This is because services are a kind of *singleton*. The service API is implemented as methods on the service struct.
149//!
150//! ```
151//! # use zng::var::*;
152//! #[expect(non_camel_case_types)]
153//! pub struct SCREAMING_CASE;
154//! impl SCREAMING_CASE {
155//!     pub fn state(&self) -> Var<bool> {
156//!         # var(true)
157//!     }
158//! }
159//! ```
160//!
161//! Note that you need to suppress a lint if the service name has more then one word.
162//!
163//! Service state and config methods should prefer variables over direct values. The use of variables allows the service state
164//! to be plugged directly into the UI. Async operations should prefer using [`ResponseVar<R>`] over `async` methods for
165//! the same reason.
166//!
167//! #### App services lifetime is the current app lifetime
168//!
169//! Unlike a simple singleton app services must only live for the duration of the app and must support
170//! multiple parallel instances if built with the `"multi_app"` feature. You can use private
171//! [`app_local!`] static variables as backing storage to fulfill this requirement.
172//!
173//! A common pattern in the zng services is to name the app locals with a `_SV` suffix.
174//!
175//! Services do not expose the app local locking, all state output is cloned the state is only locked
176//! for the duration of the service method call.
177//!
178//! #### App services don't change public state mid update
179//!
180//! All widgets using the service during the same update see the same state. State change requests are scheduled
181//! for the next update, just like variable updates or event notifications. Services also request
182//! an [`UPDATES.update`] after scheduling to wake-up the app in case the service request was made from a [`task`] thread.
183//!
184//! This is even true for the [`INSTANT`] service, although this can be configured for this service using [`APP.pause_time_for_update`].
185//!
186//! [`APP.pause_time_for_update`]: zng_app::APP::pause_time_for_update
187//!
188//! ### Examples
189//!
190//! Fulfilling service requests is where the [`AppExtension`] comes in, it is possible to declare a simple standalone
191//! service using only variables, `Event::on_event` and `UPDATES.run_hn_once`, but an app extension is more efficient
192//! and more easy to implement.
193//!
194//! If the service request can fail or be delayed it is common for the request method to return a [`ResponseVar<R>`]
195//! that is updated once the request is finished. You can also make the method `async`, but a response var is superior
196//! because it can be plugged directly into any UI property, and it can still be awaited using the variable async methods.
197//!
198//! If the service request cannot fail and it is guaranteed to affect an observable change in the service state in the
199//! next update a response var is not needed.
200//!
201//! The example below demonstrates an app extension implementation that provides a service.
202//!
203//! ```
204//! use zng::{app::AppExtension, prelude_wgt::*};
205//!
206//! /// Foo service.
207//! pub struct FOO;
208//!
209//! impl FOO {
210//!     /// Foo read-write var.
211//!     pub fn config(&self) -> Var<bool> {
212//!         FOO_SV.read().config.clone()
213//!     }
214//!
215//!     /// Foo request.
216//!     pub fn request(&self, request: char) -> ResponseVar<char> {
217//!         UPDATES.update(None);
218//!
219//!         let mut foo = FOO_SV.write();
220//!         let (responder, response) = response_var();
221//!         foo.requests.push((request, responder));
222//!         response
223//!     }
224//! }
225//!
226//! struct FooService {
227//!     config: Var<bool>,
228//!     requests: Vec<(char, ResponderVar<char>)>,
229//! }
230//!
231//! app_local! {
232//!     static FOO_SV: FooService = FooService {
233//!         config: var(false),
234//!         requests: vec![],
235//!     };
236//! }
237//!
238//! /// Foo app extension.
239//! ///
240//! /// # Services
241//! ///
242//! /// Services provided by this extension.
243//! ///
244//! /// * [`FOO`]
245//! #[derive(Default)]
246//! #[non_exhaustive]
247//! pub struct FooManager {}
248//!
249//! impl AppExtension for FooManager {
250//!     fn update(&mut self) {
251//!         let mut foo = FOO_SV.write();
252//!
253//!         if let Some(cfg) = foo.config.get_new() {
254//!             println!("foo cfg={cfg}");
255//!         }
256//!
257//!         for (request, responder) in foo.requests.drain(..) {
258//!             println!("foo request {request:?}");
259//!             responder.respond(request);
260//!         }
261//!     }
262//! }
263//! ```
264//!
265//! Note that in the example requests are processed in the [`AppExtension::update`] update that is called
266//! after all widgets have had a chance to make requests. Requests can also be made from parallel [`task`] threads so
267//! the service also requests an [`UPDATES.update`] just in case there is no update running. If you expect to receive many
268//! requests from parallel tasks you can also process requests in the [`AppExtension::update`] instead, but there is probably
269//! little practical difference.
270//!
271//! # Init & Main Loop
272//!
273//! A headed app initializes in this sequence:
274//!
275//! 1. [`AppExtension::register`] is called.
276//! 2. Spawn view-process.
277//! 3. [`AppExtension::init`] is called.
278//! 4. Schedule the app run future to run in the first preview update.
279//! 5. Does [updates loop](#updates-loop).
280//! 6. Does [update events loop](#update-events-loop).
281//! 7. Does [main loop](#main-loop).
282//!
283//! #### Main Loop
284//!
285//! The main loop coordinates view-process events, timers, app events and updates. There is no scheduler, update and event requests
286//! are captured and coalesced to various buffers that are drained in known sequential order. App extensions update one at a time
287//! in the order they are registered. Windows and widgets update in parallel by default, this is controlled by [`WINDOWS.parallel`] and [`parallel`].
288//!
289//! 1. Sleep if there are not pending events or updates.
290//!    * If the view-process is busy blocks until it sends a message, this is a mechanism to stop the app-process
291//!      from overwhelming the view-process.
292//!    * Block until a message is received, from the view-process or from other app threads.
293//!    * If there are [`TIMERS`] or [`VARS`] animations the message block has a deadline to the nearest timer or animation frame.
294//!        * Animations have a fixed frame-rate defined in [`VARS.frame_duration`], it is 60 frames-per-second by default.
295//! 2. Calls elapsed timer handlers.
296//! 3. Calls elapsed animation handlers.
297//!     * These handlers mostly just request var updates that are applied in the updates loop.
298//! 4. Does a [view events loop](#view-events-loop).
299//! 4. Does an [updates loop](#updates-loop).
300//! 5. Does an [update events loop](#update-events-loop).
301//! 6. If the view-process is not busy does a [layout loop and render](#layout-loop-and-render).
302//! 7. If exit was requested and not cancelled breaks the loop.
303//!     * Exit is requested automatically when the last open window closes, this is controlled by [`WINDOWS.exit_on_last_close`].
304//!     * Exit can also be requested using [`APP.exit`].
305//!
306//! #### View Events Loop
307//!
308//! All pending events received from the view-process are coalesced and notify sequentially.
309//!
310//! 1. For each event in the received order (FIFO) that converts to a `RAW_*_EVENT`.
311//!     1. Calls [`AppExtension::event_preview`].
312//!     2. Calls [`Event::on_pre_event`] handlers.
313//!     3. Calls [`AppExtension::event_ui`].
314//!         * Raw events don't target any widget, but widgets can subscribe, subscribers receive the event in parallel by default.
315//!     4. Calls [`AppExtension::event`].
316//!     5. Calls [`Event::on_event`] handlers.
317//!     6. Does an [updates loop](#updates-loop).
318//! 2. Frame rendered raw event.
319//!     * Same notification sequence as other view-events, just delayed.
320//!
321//! #### Updates Loop
322//!
323//! The updates loop rebuilds info trees if needed , applies pending variable updates and hooks and collects event updates
324//! requested by the app.
325//!
326//! 1. Takes info rebuild request flag.
327//!     * Calls [`AppExtension::info`] if needed.
328//!     * Windows and widgets that requested info (re)build are called.
329//!     * Info rebuild happens in parallel by default.
330//! 2. Takes events and updates requests.
331//!     1. Event hooks are called for new event requests.
332//!         * Full event notification is delayed to after the updates loop.
333//!     2. [var updates loop](#var-updates-loop)
334//!     3. Calls [`AppExtension::update_preview`] if any update was requested.
335//!     4. Calls [`UPDATES.on_pre_update`] handlers if needed.
336//!     5. Calls [`AppExtension::update_ui`] if any update was requested.
337//!         * Windows and widgets that requested update receive it here.
338//!         * All the pending updates are processed in one pass, all targeted widgets are visited once, in parallel by default.
339//!     6. Calls [`AppExtension::update`] if any update was requested.
340//!     7. Calls [`UPDATES.on_update`] handlers if needed.
341//! 3. The loop repeats immediately if any info rebuild or update was requested by update callbacks.
342//!     * The loops breaks if it repeats over 1000 times.
343//!     * An error is logged with a trace of the most frequent sources of update requests.
344//!
345//! #### Var Updates Loop
346//!
347//! The variable updates loop applies pending modifications, calls hooks to update variable and bindings.
348//!
349//! 1. Pending variable modifications are applied.
350//! 2. Var hooks are called.
351//!     * The mapping and binding mechanism is implemented using hooks.
352//! 3. The loop repeats until hooks have stopped modifying variables.
353//!     * The loop breaks if it repeats over 1000 times.
354//!     * An error is logged if this happens.
355//!
356//! #### Update Events Loop
357//!
358//! The update events loop notifies each event raised by the app code during previous updates.
359//!
360//! 1. For each event in the request order (FIFO).
361//!     1. Calls [`AppExtension::event_preview`].
362//!     2. Calls [`Event::on_pre_event`] handlers.
363//!     3. Calls [`AppExtension::event_ui`].
364//!         * Windows and widgets targeted by the event update receive it here.
365//!         * If the event targets multiple widgets they receive it in parallel by default.
366//!     4. Calls [`AppExtension::event`].
367//!     5. Calls [`Event::on_event`] handlers.
368//!     6. Does an [updates loop](#updates-loop).
369//!
370//! #### Layout Loop and Render
371//!
372//! Layout and render requests are coalesced, multiple layout requests for the same widget update it once, multiple
373//! render requests become one frame, and if both `render` and `render_update` are requested for a window it will just fully `render`.
374//!
375//! 1. Take layout and render requests.
376//! 2. Layout loop.
377//!     1. Calls [`AppExtension::layout`].
378//!         * Windows and widgets that requested layout update in parallel by default.
379//!     2. Does an [updates loop](#updates-loop).
380//!     3. Does [update events loop](#update-events-loop).
381//!     3. Take layout and render requests, the loop repeats immediately if layout was requested again.
382//!         * The loop breaks if it repeats over 1000 times.
383//!         * An error is logged with a trace the most frequent sources of update requests.
384//! 3. If render was requested, calls [`AppExtension::render`].
385//!     * Windows and widgets that requested render (or render_update) are rendered in parallel by default.
386//!     * The render pass updates widget transforms and hit-test, generates a display list and sends it to the view-process.
387//!
388//! [`APP.defaults()`]: crate::APP::defaults
389//! [`UPDATES.update`]: crate::update::UPDATES::update
390//! [`task`]: crate::task
391//! [`ResponseVar<R>`]: crate::var::ResponseVar
392//! [`TIMERS`]: crate::timer::TIMERS
393//! [`VARS`]: crate::var::VARS
394//! [`VARS.frame_duration`]: crate::var::VARS::frame_duration
395//! [`WINDOWS.parallel`]: crate::window::WINDOWS::parallel
396//! [`parallel`]: fn@crate::widget::parallel
397//! [`UPDATES.on_pre_update`]: crate::update::UPDATES::on_pre_update
398//! [`UPDATES.on_update`]: crate::update::UPDATES::on_update
399//! [`Event::on_pre_event`]: crate::event::Event::on_pre_event
400//! [`Event::on_event`]: crate::event::Event::on_event
401//! [`WINDOWS.exit_on_last_close`]: crate::window::WINDOWS::exit_on_last_close
402//! [`APP.exit`]: crate::APP#method.exit
403//!
404//! # Full API
405//!
406//! This module provides most of the app API needed to make and extend apps, some more advanced or experimental API
407//! may be available at the [`zng_app`], [`zng_app_context`] and [`zng_ext_single_instance`] base crates.
408
409pub use zng_app::{
410    AppControlFlow, AppEventObserver, AppExtended, AppExtension, AppExtensionBoxed, AppExtensionInfo, AppStartArgs, DInstant, Deadline,
411    EXIT_CMD, EXIT_REQUESTED_EVENT, ExitRequestedArgs, HeadlessApp, INSTANT, InstantMode, on_app_start, print_tracing,
412    print_tracing_filter, spawn_deadlock_detection,
413};
414
415#[cfg(feature = "test_util")]
416pub use zng_app::test_log;
417
418pub use zng_app_context::{
419    AppId, AppLocal, AppScope, CaptureFilter, ContextLocal, ContextValueSet, LocalContext, MappedRwLockReadGuardOwned,
420    MappedRwLockWriteGuardOwned, ReadOnlyRwLock, RunOnDrop, RwLockReadGuardOwned, RwLockWriteGuardOwned, app_local, context_local,
421};
422pub use zng_wgt_input::cmd::{
423    NEW_CMD, OPEN_CMD, SAVE_AS_CMD, SAVE_CMD, can_new, can_open, can_save, can_save_as, on_new, on_open, on_pre_new, on_pre_open,
424    on_pre_save, on_pre_save_as, on_save, on_save_as,
425};
426
427pub use zng_app::view_process::raw_events::{LOW_MEMORY_EVENT, LowMemoryArgs};
428
429/// Input device hardware ID and events.
430///
431/// # Full API
432///
433/// See [`zng_app::view_process::raw_device_events`] for the full API.
434pub mod raw_device_events {
435    pub use zng_app::view_process::raw_device_events::{
436        AXIS_MOTION_EVENT, AxisId, AxisMotionArgs, INPUT_DEVICES, INPUT_DEVICES_CHANGED_EVENT, InputDeviceCapability, InputDeviceId,
437        InputDeviceInfo, InputDevicesChangedArgs,
438    };
439}
440
441#[cfg(single_instance)]
442pub use zng_ext_single_instance::{APP_INSTANCE_EVENT, AppInstanceArgs};
443
444/// App-process crash handler.
445///
446/// In builds with `"crash_handler"` feature the crash handler takes over the first "app-process" turning it into
447/// the monitor-process, it spawns another process that is the monitored app-process. If the app-process crashes
448/// the monitor-process spawns a dialog-process that calls the dialog handler to show an error message, upload crash reports, etc.
449///
450/// The dialog handler can be set using [`crash_handler_config!`].
451///
452/// [`crash_handler_config!`]: crate::app::crash_handler::crash_handler_config
453///
454/// # Examples
455///
456/// The example below demonstrates an app setup to show a custom crash dialog.
457///
458/// ```no_run
459/// use zng::prelude::*;
460///
461/// fn main() {
462///     // tracing applied to all processes.
463///     zng::app::print_tracing(tracing::Level::INFO);
464///
465///     // monitor-process spawns app-process and if needed dialog-process here.
466///     zng::env::init!();
467///
468///     // app-process:
469///     app_main();
470/// }
471///
472/// fn app_main() {
473///     APP.defaults().run_window(async {
474///         Window! {
475///             child_align = Align::CENTER;
476///             child = Stack! {
477///                 direction = StackDirection::top_to_bottom();
478///                 spacing = 5;
479///                 children = ui_vec![
480///                     Button! {
481///                         child = Text!("Crash (panic)");
482///                         on_click = hn_once!(|_| {
483///                             panic!("Test panic!");
484///                         });
485///                     },
486///                     Button! {
487///                         child = Text!("Crash (access violation)");
488///                         on_click = hn_once!(|_| {
489///                             // SAFETY: deliberate access violation
490///                             #[expect(deref_nullptr)]
491///                             unsafe {
492///                                 *std::ptr::null_mut() = true;
493///                             }
494///                         });
495///                     }
496///                 ];
497///             };
498///         }
499///     });
500/// }
501///
502/// zng::app::crash_handler::crash_handler_config!(|cfg| {
503///     // monitor-process and dialog-process
504///
505///     cfg.dialog(|args| {
506///         // dialog-process
507///         APP.defaults().run_window(async move {
508///             Window! {
509///                 title = "App Crashed!";
510///                 auto_size = true;
511///                 min_size = (300, 100);
512///                 start_position = window::StartPosition::CenterMonitor;
513///                 on_load = hn_once!(|_| WINDOW.bring_to_top());
514///                 padding = 10;
515///                 child_spacing = 10;
516///                 child = Text!(args.latest().message());
517///                 child_bottom = Stack! {
518///                     direction = StackDirection::start_to_end();
519///                     layout::align = Align::BOTTOM_END;
520///                     spacing = 5;
521///                     children = ui_vec![
522///                         Button! {
523///                             child = Text!("Restart App");
524///                             on_click = hn_once!(args, |_| {
525///                                 args.restart();
526///                             });
527///                         },
528///                         Button! {
529///                             child = Text!("Exit App");
530///                             on_click = hn_once!(|_| {
531///                                 args.exit(0);
532///                             });
533///                         },
534///                     ];
535///                 };
536///             }
537///         });
538///     });
539/// });
540/// ```
541///
542/// # Debugger
543///
544/// Note that because the crash handler spawns a different process for the app debuggers will not
545/// stop at break points in the app code. You can configure your debugger to set the `NO_ZNG_CRASH_HANDLER` environment
546/// variable to not use a crash handler in debug runs.
547///
548/// On VS Code with the CodeLLDB extension you can add this workspace configuration:
549///
550/// ```json
551/// "lldb.launch.env": {
552///    "ZNG_NO_CRASH_HANDLER": ""
553/// }
554/// ```
555///
556/// # Full API
557///
558/// See [`zng_app::crash_handler`] and [`zng_wgt_inspector::crash_handler`] for the full API.
559#[cfg(crash_handler)]
560pub mod crash_handler {
561    pub use zng_app::crash_handler::{BacktraceFrame, CrashArgs, CrashConfig, CrashError, CrashPanic, crash_handler_config};
562
563    #[cfg(feature = "crash_handler_debug")]
564    pub use zng_wgt_inspector::crash_handler::debug_dialog;
565
566    crash_handler_config!(|cfg| {
567        cfg.default_dialog(|args| {
568            if let Some(c) = &args.dialog_crash {
569                eprintln!("DEBUG CRASH DIALOG ALSO CRASHED");
570                eprintln!("   {}", c.message());
571                eprintln!("ORIGINAL APP CRASH");
572                eprintln!("   {}", args.latest().message());
573                args.exit(0xBADC0DE)
574            } else {
575                #[cfg(feature = "crash_handler_debug")]
576                {
577                    use crate::prelude::*;
578                    APP.defaults()
579                        .run_window(async_clmv!(args, { zng_wgt_inspector::crash_handler::debug_dialog(args) }));
580                }
581
582                #[cfg(not(feature = "crash_handler_debug"))]
583                {
584                    eprintln!(
585                        "app crashed {}\n\nbuild with feature = \"crash_handler_debug\" to se the debug crash dialog",
586                        args.latest().message()
587                    );
588                }
589            }
590            args.exit(0)
591        });
592    });
593}
594
595/// Trace recording and data model.
596///
597/// All tracing instrumentation in Zng projects is done using the `tracing` crate, trace recording is done using the `tracing-chrome` crate.
598/// The recorded traces can be viewed in `chrome://tracing` or `ui.perfetto.dev` and can be parsed by the [`Trace`] data model.
599///
600/// Build the app with `"trace_recorder"` and run the with the `"ZNG_RECORD_TRACE"` env var set to record all other processes spawned by the app.
601///
602/// ```no_run
603/// use zng::prelude::*;
604///
605/// fn main() {
606///     unsafe {
607///         std::env::set_var("ZNG_RECORD_TRACE", "");
608///     }
609///     unsafe {
610///         std::env::set_var("ZNG_RECORD_TRACE_FILTER", "debug");
611///     }
612///
613///     // recording start here for all app processes when ZNG_RECORD_TRACE is set.
614///     zng::env::init!();
615///
616///     // .. app
617/// }
618/// ```
619///
620/// The example above hardcodes trace recording for all app processes by setting the `"ZNG_RECORD_TRACE"` environment
621/// variable before the `init!()` call. It also sets `"ZNG_RECORD_TRACE_FILTER"` to a slightly less verbose level.
622///
623/// # Config
624///
625/// The `"ZNG_RECORD_TRACE_DIR"` variable can be set to define a custom output directory path, relative to the current dir.
626/// The default dir is `"./zng-trace/"`.
627///
628/// The `"ZNG_RECORD_TRACE_FILTER"` or `"RUST_LOG"` variables can be used to set custom tracing filters, see the [filter syntax] for details.
629/// The default filter is `"trace"` that records all spans and events.
630///
631/// # Output
632///
633/// Raw trace files are saved to `"{ZNG_RECORD_TRACE_DIR}/{timestamp}/{pid}.json"`.
634///
635/// The timestamp is in microseconds from Unix epoch and is defined by the first process that runs. All processes are
636/// recorded to the same *timestamp* folder.
637///
638/// The process name is defined by an event INFO message that reads `"pid: {pid}, name: {name}"`. See [`zng::env::process_name`] for more details.
639///
640/// The process record start timestamp is defined by an event INFO message that reads `"zng-record-start: {timestamp}"`. This timestamp is also
641/// in microseconds from Unix epoch.
642///
643/// # Cargo Zng
644///
645/// You can also use the `cargo zng trace` subcommand to record traces, it handles setting the env variables, merges the multi
646/// process traces into a single file and properly names the processes for better compatibility with trace viewers.
647///
648/// ```console
649/// cargo zng trace --filter debug "path/my-exe"
650/// ```
651///
652/// You can also run using custom commands after `--`:
653///
654/// ```console
655/// cargo zng trace -- cargo run my-exe
656/// ```
657///
658/// Call `cargo zng trace --help` for more details.
659///
660/// # Full API
661///
662/// See [`zng_app::trace_recorder`] for the full API.
663///
664/// [`Trace`]: zng::app::trace_recorder::Trace
665/// [filter syntax]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/index.html#filtering-events-with-environment-variables
666#[cfg(trace_recorder)]
667pub mod trace_recorder {
668    pub use zng_app::trace_recorder::{EventTrace, ProcessTrace, ThreadTrace, Trace, stop_recording};
669}
670
671/// Heap memory usage profiling.
672///
673/// Build with debug symbols and `"memory_profiler"` feature to record heap allocations.
674///
675/// Instrumentation and recording is done with the `dhat` crate. Recorded profiles can be visualized using the
676/// [online DHAT Viewer](https://nnethercote.github.io/dh_view/dh_view.html). Stack traces are captured for each significant allocation.
677///
678/// # Config
679///
680/// The `"ZNG_MEMORY_PROFILER_DIR"` variable can be set to define a custom output directory path, relative to the current dir.
681/// The default dir is `"./zng-dhat/"`.
682///
683/// # Output
684///
685/// The recorded data is saved to `"{ZNG_MEMORY_PROFILER_DIR}/{timestamp}/{pname}-{pid}.json"`.
686///
687/// The timestamp is in microseconds from Unix epoch and is defined by the first process that runs. All processes are recorded
688/// to the same *timestamp* folder, even worker processes started later.
689///
690/// The primary process is named `"app-process"`. See [`zng::env::process_name`] for more details about the default processes.
691///
692/// # Limitations
693///
694/// Only heap allocations using the `#[global_allocator]` are captured, some dependencies can skip the allocator, for example, the view-process
695/// only traces a fraction of allocations because most of its heap usage comes from the graphics driver.
696///
697/// Compiling with `"memory_profiler"` feature replaces the global allocator, so if you use a custom allocator you need to setup
698/// a feature that disables it, otherwise it will not compile. The instrumented allocator also has an impact in performance so
699/// it is only recommended for test builds.
700///
701/// As an alternative on Unix you can use the external [Valgrind DHAT tool](https://valgrind.org/docs/manual/dh-manual.html).
702/// On Windows you can [Record a Heap Snapshot](https://learn.microsoft.com/en-us/windows-hardware/test/wpt/record-heap-snapshot).
703///
704/// # Full API
705///
706/// See [`zng_app::memory_profiler`] for the full API.
707#[cfg(memory_profiler)]
708pub mod memory_profiler {
709    pub use zng_app::memory_profiler::stop_recording;
710}