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 blocking: bool,
52
53 pub panic_prefix: &'static str,
55
56 pub always_log_file_line: bool,
59}
60
61#[cfg(not(target_os = "fuchsia"))]
62impl<'a> From<LoggingOptions<'a>> for diagnostics_log::PublishOptions<'a> {
63 fn from(logging: LoggingOptions<'a>) -> Self {
64 let mut options = diagnostics_log::PublishOptions::default().tags(logging.tags);
65 if let Some(severity) = logging.interest.min_severity {
66 options = options.minimum_severity(severity);
67 }
68 options = options.panic_prefix(logging.panic_prefix);
69 options
70 }
71}
72
73#[cfg(target_os = "fuchsia")]
74impl<'a> From<LoggingOptions<'a>> for diagnostics_log::PublishOptions<'a> {
75 fn from(logging: LoggingOptions<'a>) -> Self {
76 let mut options = diagnostics_log::PublishOptions::default().tags(logging.tags);
77 options = options.blocking(logging.blocking);
78 if let Some(severity) = logging.interest.min_severity {
79 options = options.minimum_severity(severity);
80 }
81 if logging.always_log_file_line {
82 options = options.always_log_file_line();
83 }
84 options = options.panic_prefix(logging.panic_prefix);
85 options
86 }
87}
88
89#[doc(hidden)]
91pub fn init_logging_for_component_with_executor<'a, R>(
92 func: impl FnOnce() -> R + 'a,
93 logging: LoggingOptions<'a>,
94) -> impl FnOnce() -> R + 'a {
95 move || {
96 diagnostics_log::initialize(logging.into()).expect("initialize_logging");
97 func()
98 }
99}
100
101#[doc(hidden)]
103pub fn init_logging_for_component_with_threads<'a, R>(
104 func: impl FnOnce() -> R + 'a,
105 logging: LoggingOptions<'a>,
106) -> impl FnOnce() -> R + 'a {
107 move || {
108 let _guard = init_logging_with_threads(logging);
109 func()
110 }
111}
112
113#[doc(hidden)]
115#[cfg(target_os = "fuchsia")]
116pub fn init_logging_for_test_with_executor<'a, R>(
117 func: impl Fn(usize) -> R + 'a,
118 logging: LoggingOptions<'a>,
119) -> impl Fn(usize) -> R + 'a {
120 move |n| {
121 diagnostics_log::initialize(logging.clone().into()).expect("initalize logging");
122 func(n)
123 }
124}
125
126#[doc(hidden)]
128#[cfg(target_os = "fuchsia")]
129pub fn init_logging_for_test_with_threads<'a, R>(
130 func: impl Fn(usize) -> R + 'a,
131 logging: LoggingOptions<'a>,
132) -> impl Fn(usize) -> R + 'a {
133 move |n| {
134 init_logging_with_threads(logging.clone());
135 func(n)
136 }
137}
138
139#[cfg(target_os = "fuchsia")]
141fn init_logging_with_threads(logging: LoggingOptions<'_>) {
142 diagnostics_log::initialize_sync(logging.into());
143}
144
145#[cfg(not(target_os = "fuchsia"))]
146fn init_logging_with_threads(logging: LoggingOptions<'_>) {
147 diagnostics_log::initialize(logging.into()).expect("initialize_logging");
148}
149
150#[doc(hidden)]
152#[cfg(not(target_os = "fuchsia"))]
153pub fn init_logging_for_test_with_executor<'a, R>(
154 func: impl Fn(usize) -> R + 'a,
155 _logging: LoggingOptions<'a>,
156) -> impl Fn(usize) -> R + 'a {
157 move |n| func(n)
158}
159
160#[doc(hidden)]
162#[cfg(not(target_os = "fuchsia"))]
163pub fn init_logging_for_test_with_threads<'a, R>(
164 func: impl Fn(usize) -> R + 'a,
165 _logging: LoggingOptions<'a>,
166) -> impl Fn(usize) -> R + 'a {
167 move |n| func(n)
168}
169
170#[cfg(target_os = "fuchsia")]
171fn set_thread_role(role_name: &str) {
172 if let Err(e) = fuchsia_scheduler::set_role_for_this_thread(role_name) {
173 log::warn!(e:%, role_name:%; "Couldn't apply thread role.");
174 }
175}
176
177#[doc(hidden)]
183pub fn main_not_async<F, R>(f: F) -> R
184where
185 F: FnOnce() -> R,
186{
187 f()
188}
189
190#[doc(hidden)]
192pub fn main_not_async_with_role<F, R>(f: F, _role_name: &'static str) -> R
193where
194 F: FnOnce() -> R,
195{
196 #[cfg(target_os = "fuchsia")]
197 set_thread_role(_role_name);
198 f()
199}
200
201#[doc(hidden)]
203#[cfg(target_os = "fuchsia")]
204pub fn main_singlethreaded<F, Fut, R>(
205 f: F,
206 instrument: Option<std::sync::Arc<dyn fuchsia_async::instrument::TaskInstrument>>,
207) -> R
208where
209 F: FnOnce() -> Fut,
210 Fut: Future<Output = R> + 'static,
211{
212 fuchsia_async::LocalExecutorBuilder::new()
213 .instrument(instrument)
214 .build()
215 .run_singlethreaded(f())
216}
217
218#[doc(hidden)]
220#[cfg(not(target_os = "fuchsia"))]
221pub fn main_singlethreaded<F, Fut, R>(f: F, _instrument: Option<()>) -> R
222where
223 F: FnOnce() -> Fut,
224 Fut: Future<Output = R> + 'static,
225{
226 fuchsia_async::LocalExecutorBuilder::new().build().run_singlethreaded(f())
227}
228
229#[doc(hidden)]
231#[cfg(target_os = "fuchsia")]
232pub fn main_singlethreaded_with_role<F, Fut, R>(
233 f: F,
234 _role_name: &'static str,
235 instrument: Option<std::sync::Arc<dyn fuchsia_async::instrument::TaskInstrument>>,
236) -> R
237where
238 F: FnOnce() -> Fut,
239 Fut: Future<Output = R> + 'static,
240{
241 #[cfg(target_os = "fuchsia")]
242 set_thread_role(_role_name);
243 fuchsia_async::LocalExecutorBuilder::new()
244 .instrument(instrument)
245 .build()
246 .run_singlethreaded(f())
247}
248
249#[doc(hidden)]
251#[cfg(not(target_os = "fuchsia"))]
252pub fn main_singlethreaded_with_role<F, Fut, R>(
253 f: F,
254 _role_name: &'static str,
255 _instrument: Option<()>,
256) -> R
257where
258 F: FnOnce() -> Fut,
259 Fut: Future<Output = R> + 'static,
260{
261 fuchsia_async::LocalExecutorBuilder::new().build().run_singlethreaded(f())
262}
263
264#[doc(hidden)]
266#[cfg(target_os = "fuchsia")]
267pub fn main_multithreaded<F, Fut, R>(
268 f: F,
269 num_threads: u8,
270 instrument: Option<std::sync::Arc<dyn fuchsia_async::instrument::TaskInstrument>>,
271) -> R
272where
273 F: FnOnce() -> Fut,
274 Fut: Future<Output = R> + Send + 'static,
275 R: Send + 'static,
276{
277 fuchsia_async::SendExecutorBuilder::new()
278 .num_threads(num_threads)
279 .instrument(instrument)
280 .build()
281 .run(f())
282}
283
284#[doc(hidden)]
286#[cfg(not(target_os = "fuchsia"))]
287pub fn main_multithreaded<F, Fut, R>(f: F, num_threads: u8, _instrument: Option<()>) -> R
288where
289 F: FnOnce() -> Fut,
290 Fut: Future<Output = R> + Send + 'static,
291 R: Send + 'static,
292{
293 fuchsia_async::SendExecutorBuilder::new().num_threads(num_threads).build().run(f())
294}
295
296#[doc(hidden)]
299#[cfg(target_os = "fuchsia")]
300pub fn main_multithreaded_with_role<F, Fut, R>(
301 f: F,
302 num_threads: u8,
303 _role_name: &'static str,
304 instrument: Option<std::sync::Arc<dyn fuchsia_async::instrument::TaskInstrument>>,
305) -> R
306where
307 F: FnOnce() -> Fut,
308 Fut: Future<Output = R> + Send + 'static,
309 R: Send + 'static,
310{
311 #[cfg(target_os = "fuchsia")]
312 set_thread_role(_role_name);
313
314 let builder =
315 fuchsia_async::SendExecutorBuilder::new().num_threads(num_threads).instrument(instrument);
316
317 #[cfg(target_os = "fuchsia")]
318 let builder = builder.worker_init(move || set_thread_role(_role_name));
319
320 builder.build().run(f())
321}
322
323#[doc(hidden)]
326#[cfg(not(target_os = "fuchsia"))]
327pub fn main_multithreaded_with_role<F, Fut, R>(
328 f: F,
329 num_threads: u8,
330 _role_name: &'static str,
331 _instrument: Option<()>,
332) -> R
333where
334 F: FnOnce() -> Fut,
335 Fut: Future<Output = R> + Send + 'static,
336 R: Send + 'static,
337{
338 let builder = fuchsia_async::SendExecutorBuilder::new().num_threads(num_threads);
339
340 #[cfg(target_os = "fuchsia")]
341 let builder = builder.worker_init(move || set_thread_role(_role_name));
342
343 builder.build().run(f())
344}
345
346#[doc(hidden)]
352pub fn test_not_async<F, R>(f: F) -> R
353where
354 F: FnOnce(usize) -> R,
355 R: fuchsia_async::test_support::TestResult,
356{
357 let result = f(0);
358 if result.is_ok() {
359 install_lsan_hook();
360 }
361 result
362}
363
364#[doc(hidden)]
366pub fn test_singlethreaded<F, Fut, R>(f: F) -> R
367where
368 F: Fn(usize) -> Fut + Sync + 'static,
369 Fut: Future<Output = R> + 'static,
370 R: fuchsia_async::test_support::TestResult,
371{
372 let result = fuchsia_async::test_support::run_singlethreaded_test(f);
373 if result.is_ok() {
374 install_lsan_hook();
375 }
376 result
377}
378
379#[doc(hidden)]
381pub fn test_multithreaded<F, Fut, R>(f: F, num_threads: u8) -> R
382where
383 F: Fn(usize) -> Fut + Sync + 'static,
384 Fut: Future<Output = R> + Send + 'static,
385 R: fuchsia_async::test_support::MultithreadedTestResult,
386{
387 let result = fuchsia_async::test_support::run_test(f, num_threads);
388 if result.is_ok() {
389 install_lsan_hook();
390 }
391 result
392}
393
394#[doc(hidden)]
396#[cfg(target_os = "fuchsia")]
397pub fn test_until_stalled<F, Fut, R>(f: F) -> R
398where
399 F: 'static + Sync + Fn(usize) -> Fut,
400 Fut: 'static + Future<Output = R>,
401 R: fuchsia_async::test_support::TestResult,
402{
403 let result = fuchsia_async::test_support::run_until_stalled_test(true, f);
404 if result.is_ok() {
405 install_lsan_hook();
406 }
407 result
408}
409
410#[doc(hidden)]
417pub fn adapt_to_parse_arguments<A, R>(f: impl FnOnce(A) -> R) -> impl FnOnce() -> R
418where
419 A: argh::TopLevelCommand,
420{
421 move || f(argh::from_env())
422}
423
424#[doc(hidden)]
427pub fn adapt_to_take_test_run_number<R>(f: impl Fn() -> R) -> impl Fn(usize) -> R {
428 move |_| f()
429}
430
431#[doc(hidden)]
439#[cfg(not(any(feature = "variant_asan", feature = "variant_hwasan")))]
440pub fn disable_lsan_for_should_panic() {}
441
442#[doc(hidden)]
443#[cfg(any(feature = "variant_asan", feature = "variant_hwasan"))]
444pub fn disable_lsan_for_should_panic() {
445 extern "C" {
446 fn __lsan_disable();
447 }
448 unsafe {
449 __lsan_disable();
450 }
451}
452
453#[cfg(not(any(feature = "variant_asan", feature = "variant_hwasan")))]
454fn install_lsan_hook() {}
455
456#[cfg(any(feature = "variant_asan", feature = "variant_hwasan"))]
457fn install_lsan_hook() {
458 extern "C" {
459 fn __lsan_do_leak_check();
460 }
461 extern "C" fn lsan_do_leak_check() {
463 unsafe { __lsan_do_leak_check() }
464 }
465 let err = unsafe { libc::atexit(lsan_do_leak_check) };
467 if err != 0 {
468 panic!("Failed to install atexit hook for LeakSanitizer: atexit returned {err}");
469 }
470}