text: //=-- lsan_fuchsia.cpp ---------------------------------------------------===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===---------------------------------------------------------------------===//
  //
  // This file is a part of LeakSanitizer.
  // Standalone LSan RTL code specific to Fuchsia.
  //
  //===---------------------------------------------------------------------===//
  
  #include "sanitizer_common/sanitizer_platform.h"
  
  #if SANITIZER_FUCHSIA
  #include <zircon/sanitizer.h>
  
  #include "lsan.h"
  #include "lsan_allocator.h"
  
  using namespace __lsan;
  
  namespace __lsan {
  
  void LsanOnDeadlySignal(int signo, void *siginfo, void *context) {}
  
  ThreadContext::ThreadContext(int tid) : ThreadContextLsanBase(tid) {}
  
  struct OnCreatedArgs {
    uptr stack_begin, stack_end;
  };
  
  // On Fuchsia, the stack bounds of a new thread are available before
  // the thread itself has started running.
  void ThreadContext::OnCreated(void *arg) {
    // Stack bounds passed through from __sanitizer_before_thread_create_hook
    // or InitializeMainThread.
    auto args = reinterpret_cast<const OnCreatedArgs *>(arg);
    stack_begin_ = args->stack_begin;
    stack_end_ = args->stack_end;
  }
  
  struct OnStartedArgs {
    uptr cache_begin, cache_end;
  };
  
  void ThreadContext::OnStarted(void *arg) {
    auto args = reinterpret_cast<const OnStartedArgs *>(arg);
    cache_begin_ = args->cache_begin;
    cache_end_ = args->cache_end;
  }
  
  void ThreadStart(u32 tid) {
    OnStartedArgs args;
    GetAllocatorCacheRange(&args.cache_begin, &args.cache_end);
    CHECK_EQ(args.cache_end - args.cache_begin, sizeof(AllocatorCache));
    ThreadContextLsanBase::ThreadStart(tid, GetTid(), ThreadType::Regular, &args);
  }
  
  void InitializeMainThread() {
    OnCreatedArgs args;
    __sanitizer::GetThreadStackTopAndBottom(true, &args.stack_end,
                                            &args.stack_begin);
    u32 tid = ThreadCreate(kMainTid, true, &args);
    CHECK_EQ(tid, 0);
    ThreadStart(tid);
  }
  
  void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches) {
    GetThreadRegistryLocked()->RunCallbackForEachThreadLocked(
        [](ThreadContextBase *tctx, void *arg) {
          auto ctx = static_cast<ThreadContext *>(tctx);
          static_cast<decltype(caches)>(arg)->push_back(ctx->cache_begin());
        },
        caches);
  }
  
  // On Fuchsia, leak detection is done by a special hook after atexit hooks.
  // So this doesn't install any atexit hook like on other platforms.
  void InstallAtExitCheckLeaks() {}
  
  // ASan defines this to check its `halt_on_error` flag.
  bool UseExitcodeOnLeak() { return true; }
  
  }  // namespace __lsan
  
  // These are declared (in extern "C") by <zircon/sanitizer.h>.
  // The system runtime will call our definitions directly.
  
  // This is called before each thread creation is attempted.  So, in
  // its first call, the calling thread is the initial and sole thread.
  void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached,
                                              const char *name, void *stack_base,
                                              size_t stack_size) {
    ENSURE_LSAN_INITED;
    EnsureMainThreadIDIsCorrect();
    OnCreatedArgs args;
    args.stack_begin = reinterpret_cast<uptr>(stack_base);
    args.stack_end = args.stack_begin + stack_size;
    u32 parent_tid = GetCurrentThread();
    u32 tid = ThreadCreate(parent_tid, detached, &args);
    return reinterpret_cast<void *>(static_cast<uptr>(tid));
  }
  
  // This is called after creating a new thread (in the creating thread),
  // with the pointer returned by __sanitizer_before_thread_create_hook (above).
  void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) {
    u32 tid = static_cast<u32>(reinterpret_cast<uptr>(hook));
    // On success, there is nothing to do here.
    if (error != thrd_success) {
      // Clean up the thread registry for the thread creation that didn't happen.
      GetThreadRegistryLocked()->FinishThread(tid);
    }
  }
  
  // This is called in the newly-created thread before it runs anything else,
  // with the pointer returned by __sanitizer_before_thread_create_hook (above).
  void __sanitizer_thread_start_hook(void *hook, thrd_t self) {
    u32 tid = static_cast<u32>(reinterpret_cast<uptr>(hook));
    ThreadStart(tid);
  }
  
  // Each thread runs this just before it exits,
  // with the pointer returned by BeforeThreadCreateHook (above).
  // All per-thread destructors have already been called.
  void __sanitizer_thread_exit_hook(void *hook, thrd_t self) { ThreadFinish(); }
  
  #endif  // SANITIZER_FUCHSIA
  