code: def yappi_profile(
      path: Optional[Union[Callable[[], str], str]] = None,
      wall_clock: Optional[bool] = True,
      separate_threads: Optional[bool] = False,
  ):
      try:
          import yappi
      except ImportError:
          print(
              "Failed to run profiler, yappi is not installed"
          )  # noqa: T201
          yield
          return
  
      yappi.set_clock_type("wall" if wall_clock else "cpu")
  
      yappi.start()
      try:
          yield
      finally:
          yappi.stop()
  
          threads = yappi.get_thread_stats()
          stats = {}
          if separate_threads:
              for thread in threads:
                  ctx_id = thread.id
                  stats[ctx_id] = yappi.get_func_stats(ctx_id=ctx_id)
          else:
              stats[None] = yappi.get_func_stats()
  
          fpath = path() if callable(path) else path
          for ctx_id, st in stats.items():
              if fpath:
                  out = (
                      f"{fpath}-{ctx_id}"
                      if ctx_id is not None
                      else fpath
                  )
                  st.save(out, type="callgrind")
              else:
                  if ctx_id is not None:
                      print(f"\nThread {ctx_id}")  # noqa: T201
                  st.print_all()
                  if ctx_id is None:
                      threads.print_all()
  
          yappi.clear_stats()
  
file_path: dvc/dvc/_debug.py
imports: from contextlib import ExitStack, contextmanager
  from datetime import datetime
  from typing import TYPE_CHECKING, Callable, Optional, Union
label_desc: Use image node_id 2 for calling a global function with example usage: yappi_profile(path, wall_clock, separate_threads) without return types