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