1#![deny(missing_docs)]
16
17pub use fuchsia_macro::{main, test};
18use libc as _;
19#[doc(hidden)]
20pub use log::error;
21use std::future::Future;
22
23#[cfg(fuchsia_api_level_less_than = "27")]
24pub use fidl_fuchsia_diagnostics::{Interest, Severity};
25#[cfg(fuchsia_api_level_at_least = "27")]
26pub use fidl_fuchsia_diagnostics_types::{Interest, Severity};
27
28#[cfg(target_os = "fuchsia")]
29pub use fuchsia_async_inspect;
30
31#[derive(Default, Clone)]
37pub struct LoggingOptions<'a> {
38 pub tags: &'a [&'static str],
41
42 pub interest: Interest,
45
46 pub panic_prefix: &'static str,
48
49 pub always_log_file_line: bool,
52}
53
54#[cfg(not(target_os = "fuchsia"))]
55impl<'a> From<LoggingOptions<'a>> for diagnostics_log::PublishOptions<'a> {
56 fn from(logging: LoggingOptions<'a>) -> Self {
57 let mut options = diagnostics_log::PublishOptions::default().tags(logging.tags);
58 if let Some(severity) = logging.interest.min_severity {
59 options = options.minimum_severity(severity);
60 }
61 options = options.panic_prefix(logging.panic_prefix);
62 options
63 }
64}
65
66#[cfg(target_os = "fuchsia")]
67impl<'a> From<LoggingOptions<'a>> for diagnostics_log::PublishOptions<'a> {
68 fn from(logging: LoggingOptions<'a>) -> Self {
69 let mut options = diagnostics_log::PublishOptions::default().tags(logging.tags);
70 if let Some(severity) = logging.interest.min_severity {
71 options = options.minimum_severity(severity);
72 }
73 if logging.always_log_file_line {
74 options = options.always_log_file_line();
75 }
76 options = options.panic_prefix(logging.panic_prefix);
77 options
78 }
79}
80
81#[doc(hidden)]
83pub fn init_logging_for_component_with_executor<'a, R>(
84 func: impl FnOnce() -> R + 'a,
85 logging: LoggingOptions<'a>,
86) -> impl FnOnce() -> R + 'a {
87 move || {
88 diagnostics_log::initialize(logging.into()).expect("initialize_logging");
89 func()
90 }
91}
92
93#[doc(hidden)]
94pub fn init_default_logging_for_component_with_executor<'a, R>(
95 func: impl FnOnce() -> R + 'a,
96 logging: LoggingOptions<'a>,
97) -> impl FnOnce() -> R + 'a {
98 init_logging_for_component_with_executor(func, logging)
99}
100
101#[doc(hidden)]
102pub fn init_noop_logging_for_component_with_executor<'a, R>(
103 func: impl FnOnce() -> R + 'a,
104 _logging: LoggingOptions<'a>,
105) -> impl FnOnce() -> R + 'a {
106 func
107}
108
109#[doc(hidden)]
111pub fn init_logging_for_component_with_threads<'a, R>(
112 func: impl FnOnce() -> R + 'a,
113 logging: LoggingOptions<'a>,
114) -> impl FnOnce() -> R + 'a {
115 move || {
116 let _guard = init_logging_with_threads(logging);
117 func()
118 }
119}
120
121#[doc(hidden)]
122pub fn init_default_logging_for_component_with_threads<'a, R>(
123 func: impl FnOnce() -> R + 'a,
124 logging: LoggingOptions<'a>,
125) -> impl FnOnce() -> R + 'a {
126 init_logging_for_component_with_threads(func, logging)
127}
128
129#[doc(hidden)]
130pub fn init_noop_logging_for_component_with_threads<'a, R>(
131 func: impl FnOnce() -> R + 'a,
132 _logging: LoggingOptions<'a>,
133) -> impl FnOnce() -> R + 'a {
134 func
135}
136
137#[doc(hidden)]
139pub fn init_logging_for_test_with_executor<'a, R>(
140 func: impl Fn(usize) -> R + 'a,
141 logging: LoggingOptions<'a>,
142) -> impl Fn(usize) -> R + 'a {
143 move |n| {
144 diagnostics_log::initialize(logging.clone().into()).expect("initalize logging");
145 func(n)
146 }
147}
148
149#[doc(hidden)]
150pub fn init_default_logging_for_test_with_executor<'a, R>(
151 func: impl Fn(usize) -> R + 'a,
152 logging: LoggingOptions<'a>,
153) -> impl Fn(usize) -> R + 'a {
154 #[cfg(target_os = "fuchsia")]
155 {
156 init_logging_for_test_with_executor(func, logging)
157 }
158 #[cfg(not(target_os = "fuchsia"))]
159 {
160 init_noop_logging_for_test_with_executor(func, logging)
161 }
162}
163
164#[doc(hidden)]
165pub fn init_noop_logging_for_test_with_executor<'a, R>(
166 func: impl Fn(usize) -> R + 'a,
167 _logging: LoggingOptions<'a>,
168) -> impl Fn(usize) -> R + 'a {
169 func
170}
171
172#[doc(hidden)]
174pub fn init_logging_for_test_with_threads<'a, R>(
175 func: impl Fn(usize) -> R + 'a,
176 logging: LoggingOptions<'a>,
177) -> impl Fn(usize) -> R + 'a {
178 move |n| {
179 init_logging_with_threads(logging.clone());
180 func(n)
181 }
182}
183
184#[doc(hidden)]
185pub fn init_default_logging_for_test_with_threads<'a, R>(
186 func: impl Fn(usize) -> R + 'a,
187 logging: LoggingOptions<'a>,
188) -> impl Fn(usize) -> R + 'a {
189 #[cfg(target_os = "fuchsia")]
190 {
191 init_logging_for_test_with_threads(func, logging)
192 }
193 #[cfg(not(target_os = "fuchsia"))]
194 {
195 init_noop_logging_for_test_with_threads(func, logging)
201 }
202}
203
204#[doc(hidden)]
205pub fn init_noop_logging_for_test_with_threads<'a, R>(
206 func: impl Fn(usize) -> R + 'a,
207 _logging: LoggingOptions<'a>,
208) -> impl Fn(usize) -> R + 'a {
209 func
210}
211
212#[cfg(target_os = "fuchsia")]
215fn init_logging_with_threads(logging: LoggingOptions<'_>) {
216 diagnostics_log::initialize_sync(logging.into());
217}
218
219#[cfg(not(target_os = "fuchsia"))]
220fn init_logging_with_threads(logging: LoggingOptions<'_>) {
221 diagnostics_log::initialize(logging.into()).expect("initialize_logging");
222}
223
224#[cfg(target_os = "fuchsia")]
225fn set_thread_role(role_name: &str) {
226 if let Err(e) = fuchsia_scheduler::set_role_for_this_thread(role_name) {
227 log::warn!(e:%, role_name:%; "Couldn't apply thread role.");
228 }
229}
230
231#[doc(hidden)]
237pub fn main_not_async<F, R>(f: F) -> R
238where
239 F: FnOnce() -> R,
240{
241 f()
242}
243
244#[doc(hidden)]
246pub fn main_not_async_with_role<F, R>(f: F, _role_name: &'static str) -> R
247where
248 F: FnOnce() -> R,
249{
250 #[cfg(target_os = "fuchsia")]
251 set_thread_role(_role_name);
252 f()
253}
254
255#[doc(hidden)]
257#[cfg(target_os = "fuchsia")]
258pub fn main_singlethreaded<F, Fut, R>(
259 f: F,
260 instrument: Option<std::sync::Arc<dyn fuchsia_async::instrument::TaskInstrument>>,
261) -> R
262where
263 F: FnOnce() -> Fut,
264 Fut: Future<Output = R> + 'static,
265{
266 fuchsia_async::LocalExecutorBuilder::new()
267 .instrument(instrument)
268 .build()
269 .run_singlethreaded(f())
270}
271
272#[doc(hidden)]
274#[cfg(not(target_os = "fuchsia"))]
275pub fn main_singlethreaded<F, Fut, R>(f: F, _instrument: Option<()>) -> R
276where
277 F: FnOnce() -> Fut,
278 Fut: Future<Output = R> + 'static,
279{
280 fuchsia_async::LocalExecutorBuilder::new().build().run_singlethreaded(f())
281}
282
283#[doc(hidden)]
285#[cfg(target_os = "fuchsia")]
286pub fn main_singlethreaded_with_role<F, Fut, R>(
287 f: F,
288 _role_name: &'static str,
289 instrument: Option<std::sync::Arc<dyn fuchsia_async::instrument::TaskInstrument>>,
290) -> R
291where
292 F: FnOnce() -> Fut,
293 Fut: Future<Output = R> + 'static,
294{
295 #[cfg(target_os = "fuchsia")]
296 set_thread_role(_role_name);
297 fuchsia_async::LocalExecutorBuilder::new()
298 .instrument(instrument)
299 .build()
300 .run_singlethreaded(f())
301}
302
303#[doc(hidden)]
305#[cfg(not(target_os = "fuchsia"))]
306pub fn main_singlethreaded_with_role<F, Fut, R>(
307 f: F,
308 _role_name: &'static str,
309 _instrument: Option<()>,
310) -> R
311where
312 F: FnOnce() -> Fut,
313 Fut: Future<Output = R> + 'static,
314{
315 fuchsia_async::LocalExecutorBuilder::new().build().run_singlethreaded(f())
316}
317
318#[doc(hidden)]
320#[cfg(target_os = "fuchsia")]
321pub fn main_multithreaded<F, Fut, R>(
322 f: F,
323 num_threads: u8,
324 instrument: Option<std::sync::Arc<dyn fuchsia_async::instrument::TaskInstrument>>,
325) -> R
326where
327 F: FnOnce() -> Fut,
328 Fut: Future<Output = R> + Send + 'static,
329 R: Send + 'static,
330{
331 fuchsia_async::SendExecutorBuilder::new()
332 .num_threads(num_threads)
333 .instrument(instrument)
334 .build()
335 .run(f())
336}
337
338#[doc(hidden)]
340#[cfg(not(target_os = "fuchsia"))]
341pub fn main_multithreaded<F, Fut, R>(f: F, num_threads: u8, _instrument: Option<()>) -> R
342where
343 F: FnOnce() -> Fut,
344 Fut: Future<Output = R> + Send + 'static,
345 R: Send + 'static,
346{
347 fuchsia_async::SendExecutorBuilder::new().num_threads(num_threads).build().run(f())
348}
349
350#[doc(hidden)]
353#[cfg(target_os = "fuchsia")]
354pub fn main_multithreaded_with_role<F, Fut, R>(
355 f: F,
356 num_threads: u8,
357 _role_name: &'static str,
358 instrument: Option<std::sync::Arc<dyn fuchsia_async::instrument::TaskInstrument>>,
359) -> R
360where
361 F: FnOnce() -> Fut,
362 Fut: Future<Output = R> + Send + 'static,
363 R: Send + 'static,
364{
365 #[cfg(target_os = "fuchsia")]
366 set_thread_role(_role_name);
367
368 let builder =
369 fuchsia_async::SendExecutorBuilder::new().num_threads(num_threads).instrument(instrument);
370
371 #[cfg(target_os = "fuchsia")]
372 let builder = builder.worker_init(move || set_thread_role(_role_name));
373
374 builder.build().run(f())
375}
376
377#[doc(hidden)]
380#[cfg(not(target_os = "fuchsia"))]
381pub fn main_multithreaded_with_role<F, Fut, R>(
382 f: F,
383 num_threads: u8,
384 _role_name: &'static str,
385 _instrument: Option<()>,
386) -> R
387where
388 F: FnOnce() -> Fut,
389 Fut: Future<Output = R> + Send + 'static,
390 R: Send + 'static,
391{
392 let builder = fuchsia_async::SendExecutorBuilder::new().num_threads(num_threads);
393
394 #[cfg(target_os = "fuchsia")]
395 let builder = builder.worker_init(move || set_thread_role(_role_name));
396
397 builder.build().run(f())
398}
399
400#[doc(hidden)]
406pub fn test_not_async<F, R>(f: F) -> R
407where
408 F: FnOnce(usize) -> R,
409 R: fuchsia_async::test_support::TestResult,
410{
411 let result = f(0);
412 if result.is_ok() {
413 install_lsan_hook();
414 }
415 result
416}
417
418#[doc(hidden)]
420#[derive(Clone, Copy, Debug, PartialEq, Eq)]
421pub struct TestOptions {
422 pub allow_interrupts: bool,
424}
425
426impl Default for TestOptions {
427 fn default() -> Self {
428 Self { allow_interrupts: false }
429 }
430}
431
432impl From<TestOptions> for fuchsia_async::test_support::TestOptions {
433 fn from(options: TestOptions) -> Self {
434 Self { allow_interrupts: options.allow_interrupts }
435 }
436}
437
438#[doc(hidden)]
440pub fn test_singlethreaded<F, Fut, R>(f: F, options: TestOptions) -> R
441where
442 F: Fn(usize) -> Fut + Sync + 'static,
443 Fut: Future<Output = R> + 'static,
444 R: fuchsia_async::test_support::TestResult,
445{
446 let result = fuchsia_async::test_support::run_singlethreaded_test(f, options.into());
447 if result.is_ok() {
448 install_lsan_hook();
449 }
450 result
451}
452
453#[doc(hidden)]
455pub fn test_multithreaded<F, Fut, R>(f: F, num_threads: u8, options: TestOptions) -> R
456where
457 F: Fn(usize) -> Fut + Sync + 'static,
458 Fut: Future<Output = R> + Send + 'static,
459 R: fuchsia_async::test_support::MultithreadedTestResult,
460{
461 let result = fuchsia_async::test_support::run_test(f, num_threads, options.into());
462 if result.is_ok() {
463 install_lsan_hook();
464 }
465 result
466}
467
468#[doc(hidden)]
470#[cfg(target_os = "fuchsia")]
471pub fn test_until_stalled<F, Fut, R>(f: F, options: TestOptions) -> R
472where
473 F: 'static + Sync + Fn(usize) -> Fut,
474 Fut: 'static + Future<Output = R>,
475 R: fuchsia_async::test_support::TestResult,
476{
477 let result = fuchsia_async::test_support::run_until_stalled_test(true, f, options.into());
478 if result.is_ok() {
479 install_lsan_hook();
480 }
481 result
482}
483
484#[doc(hidden)]
491pub fn adapt_to_parse_arguments<A, R>(f: impl FnOnce(A) -> R) -> impl FnOnce() -> R
492where
493 A: argh::TopLevelCommand,
494{
495 move || f(argh::from_env())
496}
497
498#[doc(hidden)]
501pub fn adapt_to_take_test_run_number<R>(f: impl Fn() -> R) -> impl Fn(usize) -> R {
502 move |_| f()
503}
504
505#[doc(hidden)]
513#[cfg(not(any(feature = "variant_asan", feature = "variant_hwasan")))]
514pub fn disable_lsan_for_should_panic() {}
515
516#[doc(hidden)]
517#[cfg(any(feature = "variant_asan", feature = "variant_hwasan"))]
518pub fn disable_lsan_for_should_panic() {
519 unsafe extern "C" {
520 fn __lsan_disable();
521 }
522 unsafe {
523 __lsan_disable();
524 }
525}
526
527#[cfg(not(any(feature = "variant_asan", feature = "variant_hwasan")))]
528fn install_lsan_hook() {}
529
530#[cfg(any(feature = "variant_asan", feature = "variant_hwasan"))]
531fn install_lsan_hook() {
532 unsafe extern "C" {
533 fn __lsan_do_leak_check();
534 }
535 extern "C" fn lsan_do_leak_check() {
537 unsafe { __lsan_do_leak_check() }
538 }
539 let err = unsafe { libc::atexit(lsan_do_leak_check) };
541 if err != 0 {
542 panic!("Failed to install atexit hook for LeakSanitizer: atexit returned {err}");
543 }
544}