// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "vm/globals.h"
#if defined(DART_HOST_OS_WINDOWS)

#include "vm/lockers.h"
#include "vm/native_symbol.h"
#include "vm/os.h"
#include "vm/os_thread.h"

#include <dbghelp.h>  // NOLINT

namespace dart {

static bool running_ = false;
static Mutex* lock_ = nullptr;

void NativeSymbolResolver::Init() {
  ASSERT(running_ == false);
  if (lock_ == nullptr) {
    lock_ = new Mutex();
  }
  running_ = true;

// Symbol resolution API's used in this file are not supported
// when compiled in UWP.
#ifndef DART_TARGET_OS_WINDOWS_UWP
  SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
  HANDLE hProcess = GetCurrentProcess();
  if (!SymInitialize(hProcess, nullptr, TRUE)) {
    DWORD error = GetLastError();
    OS::PrintErr("Failed to init NativeSymbolResolver (SymInitialize %" Pu32
                 ")\n",
                 error);
    return;
  }
#endif
}

void NativeSymbolResolver::Cleanup() {
  MutexLocker lock(lock_);
  if (!running_) {
    return;
  }
  running_ = false;
#ifndef DART_TARGET_OS_WINDOWS_UWP
  HANDLE hProcess = GetCurrentProcess();
  if (!SymCleanup(hProcess)) {
    DWORD error = GetLastError();
    OS::PrintErr("Failed to shutdown NativeSymbolResolver (SymCleanup  %" Pu32
                 ")\n",
                 error);
  }
#endif
}

const char* NativeSymbolResolver::LookupSymbolName(uword pc, uword* start) {
#ifdef DART_TARGET_OS_WINDOWS_UWP
  return nullptr;
#else
  const intptr_t kMaxNameLength = 2048;
  const intptr_t kSymbolInfoSize = sizeof(SYMBOL_INFO);  // NOLINT.
  static char buffer[kSymbolInfoSize + kMaxNameLength];
  MutexLocker lock(lock_);
  if (!running_) {
    return nullptr;
  }
  if (start != nullptr) {
    *start = 0;
  }
  memset(&buffer[0], 0, sizeof(buffer));
  HANDLE hProcess = GetCurrentProcess();
  DWORD64 address = static_cast<DWORD64>(pc);
  PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
  pSymbol->SizeOfStruct = kSymbolInfoSize;
  pSymbol->MaxNameLen = kMaxNameLength;
  DWORD64 displacement;
  BOOL r = SymFromAddr(hProcess, address, &displacement, pSymbol);
  if (r == FALSE) {
    return nullptr;
  }
  if (start != nullptr) {
    *start = pc - displacement;
  }
  return Utils::StrDup(pSymbol->Name);
#endif  // ifdef DART_TARGET_OS_WINDOWS_UWP
}

void NativeSymbolResolver::FreeSymbolName(const char* name) {
  free(const_cast<char*>(name));
}

struct Context {
  uword pc;
  uword dso_base;
  const char* dso_name;
  bool success;
};

BOOL CALLBACK EnumerateModulesCallback(PCSTR ModuleName,
                                       DWORD64 ModuleBase,
                                       ULONG ModuleSize,
                                       PVOID UserContext) {
  Context* context = reinterpret_cast<Context*>(UserContext);
  if ((ModuleBase <= context->pc) && (context->pc < ModuleBase + ModuleSize)) {
    context->dso_base = ModuleBase;
    context->dso_name = Utils::StrDup(ModuleName);
    context->success = true;
    return FALSE;  // stop enumeration
  }
  return TRUE;  // continue enumeration
}

bool NativeSymbolResolver::LookupSharedObject(uword pc,
                                              uword* dso_base,
                                              const char** dso_name) {
#ifdef DART_TARGET_OS_WINDOWS_UWP
  return false;
#else
  Context context;
  context.pc = pc;
  context.success = false;
  if (!EnumerateLoadedModulesEx(GetCurrentProcess(), EnumerateModulesCallback,
                                &context)) {
    return false;
  }
  if (!context.success) {
    return false;
  }
  if (dso_base != nullptr) {
    *dso_base = context.dso_base;
  }
  if (dso_name != nullptr) {
    *dso_name = context.dso_name;
  }
  return true;
#endif  // ifdef DART_TARGET_OS_WINDOWS_UWP
}

void NativeSymbolResolver::AddSymbols(const char* dso_name,
                                      void* buffer,
                                      size_t size) {
  OS::PrintErr("warning: Dart_AddSymbols has no effect on Windows\n");
}

}  // namespace dart

#endif  // defined(DART_HOST_OS_WINDOWS)
