Skip to main content

monarch_tensor_worker/
test_util.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9use std::io::IsTerminal;
10
11use anyhow::Result;
12use monarch_gil::GilSite;
13use monarch_gil::monarch_with_gil_blocking;
14use pyo3::Python;
15use pyo3::ffi::c_str;
16use tracing_subscriber::fmt::format::FmtSpan;
17
18pub fn test_setup() -> Result<()> {
19    let _ = tracing_subscriber::fmt()
20        .with_thread_ids(true)
21        .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
22        .with_max_level(tracing::Level::DEBUG)
23        .with_ansi(std::io::stderr().is_terminal())
24        .with_writer(std::io::stderr)
25        .try_init();
26
27    // Redirect NCCL_DEBUG log output to a file so it doesn't clash on stdout.
28    // TestX requires stdout to have JSON output on individual lines, and
29    // the NCCL output is not JSON. Because it runs in a different thread, it'll
30    // race on writing to stdout.
31    // Do this regardless of whether NCCL_DEBUG is set or not, because it can
32    // be set after this point in the test. If it doesn't get set, NCCL_DEBUG_FILE
33    // will be ignored.
34    // %h becomes hostname, %p becomes pid.
35    let nccl_debug_file = std::env::temp_dir().join("nccl_debug.%h.%p");
36    tracing::debug!("Set NCCL_DEBUG_FILE to {:?}", nccl_debug_file);
37    // Safety: Can be unsound if there are multiple threads
38    // reading and writing the environment.
39    unsafe {
40        std::env::set_var("NCCL_DEBUG_FILE", nccl_debug_file);
41    }
42    // NOTE(agallagher): Calling `Python::initialize()` appears to
43    // clear `PYTHONPATH` in the env, which we need for test subprocesses
44    // to work.  So, manually preserve it.
45    let py_path = std::env::var("PYTHONPATH");
46    Python::initialize();
47    if let Ok(py_path) = py_path {
48        // SAFETY: Re-setting env var cleared by `Python::initialize()`.
49        unsafe { std::env::set_var("PYTHONPATH", py_path) }
50    }
51
52    // We need to load torch to initialize some internal structures used by
53    // the FFI funcs we use to convert ivalues to/from py objects.
54    monarch_with_gil_blocking(GilSite::Test, |py| {
55        py.run(c_str!("import torch"), None, None)
56    })?;
57
58    Ok(())
59}