sampler.cc 21.4 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/libsampler/sampler.h"
6

7 8 9
#include "include/v8-isolate.h"
#include "include/v8-unwinder.h"

10
#ifdef USE_SIGNALS
11 12 13 14 15

#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
16
#include <atomic>
17

18
#if !V8_OS_QNX && !V8_OS_AIX
19
#include <sys/syscall.h>
20 21
#endif

22
#if V8_OS_DARWIN
23 24 25
#include <mach/mach.h>
// OpenBSD doesn't have <ucontext.h>. ucontext_t lives in <signal.h>
// and is a typedef for struct sigcontext. There is no uc_mcontext.
26
#elif !V8_OS_OPENBSD
27 28 29 30 31 32 33
#include <ucontext.h>
#endif

#include <unistd.h>

#elif V8_OS_WIN || V8_OS_CYGWIN

34 35
#include <windows.h>

36 37
#include "src/base/win32-headers.h"

38 39 40 41 42 43 44
#elif V8_OS_FUCHSIA

#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/debug.h>
#include <zircon/types.h>

45 46 47 48 49 50 51 52 53
// TODO(wez): Remove this once the Fuchsia SDK has rolled.
#if defined(ZX_THREAD_STATE_REGSET0)
#define ZX_THREAD_STATE_GENERAL_REGS ZX_THREAD_STATE_REGSET0
zx_status_t zx_thread_read_state(zx_handle_t h, uint32_t k, void* b, size_t l) {
  uint32_t dummy_out_len = 0;
  return zx_thread_read_state(h, k, b, static_cast<uint32_t>(l),
                              &dummy_out_len);
}
#if defined(__x86_64__)
54
using zx_thread_state_general_regs_t = zx_x86_64_general_regs_t;
55
#else
56
using zx_thread_state_general_regs_t = zx_arm64_general_regs_t;
57 58 59
#endif
#endif  // !defined(ZX_THREAD_STATE_GENERAL_REGS)

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
#endif

#include <algorithm>
#include <vector>

#include "src/base/atomic-utils.h"
#include "src/base/platform/platform.h"

#if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T)

// Not all versions of Android's C library provide ucontext_t.
// Detect this and provide custom but compatible definitions. Note that these
// follow the GLibc naming convention to access register values from
// mcontext_t.
//
// See http://code.google.com/p/android/issues/detail?id=34784

#if defined(__arm__)

79
using mcontext_t = struct sigcontext;
80

81
struct ucontext_t {
82 83 84 85 86
  uint32_t uc_flags;
  struct ucontext* uc_link;
  stack_t uc_stack;
  mcontext_t uc_mcontext;
  // Other fields are not used by V8, don't define them here.
87
};
88 89 90

#elif defined(__aarch64__)

91
using mcontext_t = struct sigcontext;
92

93
struct ucontext_t {
94
  uint64_t uc_flags;
Brice Dobry's avatar
Brice Dobry committed
95
  struct ucontext* uc_link;
96 97 98
  stack_t uc_stack;
  mcontext_t uc_mcontext;
  // Other fields are not used by V8, don't define them here.
99
};
100 101 102

#elif defined(__mips__)
// MIPS version of sigcontext, for Android bionic.
103
struct mcontext_t {
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
  uint32_t regmask;
  uint32_t status;
  uint64_t pc;
  uint64_t gregs[32];
  uint64_t fpregs[32];
  uint32_t acx;
  uint32_t fpc_csr;
  uint32_t fpc_eir;
  uint32_t used_math;
  uint32_t dsp;
  uint64_t mdhi;
  uint64_t mdlo;
  uint32_t hi1;
  uint32_t lo1;
  uint32_t hi2;
  uint32_t lo2;
  uint32_t hi3;
  uint32_t lo3;
122
};
123

124
struct ucontext_t {
125 126 127 128 129
  uint32_t uc_flags;
  struct ucontext* uc_link;
  stack_t uc_stack;
  mcontext_t uc_mcontext;
  // Other fields are not used by V8, don't define them here.
130
};
131 132 133

#elif defined(__i386__)
// x86 version for Android.
134
struct mcontext_t {
135 136 137 138
  uint32_t gregs[19];
  void* fpregs;
  uint32_t oldmask;
  uint32_t cr2;
139
};
140

141 142
using kernel_sigset_t = uint32_t[2];  // x86 kernel uses 64-bit signal masks
struct ucontext_t {
143 144 145 146 147
  uint32_t uc_flags;
  struct ucontext* uc_link;
  stack_t uc_stack;
  mcontext_t uc_mcontext;
  // Other fields are not used by V8, don't define them here.
148
};
149 150 151 152
enum { REG_EBP = 6, REG_ESP = 7, REG_EIP = 14 };

#elif defined(__x86_64__)
// x64 version for Android.
153
struct mcontext_t {
154 155 156
  uint64_t gregs[23];
  void* fpregs;
  uint64_t __reserved1[8];
157
};
158

159
struct ucontext_t {
160
  uint64_t uc_flags;
Brice Dobry's avatar
Brice Dobry committed
161
  struct ucontext* uc_link;
162 163 164
  stack_t uc_stack;
  mcontext_t uc_mcontext;
  // Other fields are not used by V8, don't define them here.
165
};
166 167 168 169 170 171 172 173 174 175
enum { REG_RBP = 10, REG_RSP = 15, REG_RIP = 16 };
#endif

#endif  // V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T)

namespace v8 {
namespace sampler {

#if defined(USE_SIGNALS)

176 177 178 179 180 181 182 183 184
AtomicGuard::AtomicGuard(AtomicMutex* atomic, bool is_blocking)
    : atomic_(atomic), is_success_(false) {
  do {
    bool expected = false;
    // We have to use the strong version here for the case where is_blocking
    // is false, and we will only attempt the exchange once.
    is_success_ = atomic->compare_exchange_strong(expected, true);
  } while (is_blocking && !is_success_);
}
185

186 187 188 189
AtomicGuard::~AtomicGuard() {
  if (!is_success_) return;
  atomic_->store(false);
}
190

191
bool AtomicGuard::is_success() const { return is_success_; }
192 193 194 195 196 197 198 199 200 201

class Sampler::PlatformData {
 public:
  PlatformData() : vm_tid_(pthread_self()) {}
  pthread_t vm_tid() const { return vm_tid_; }

 private:
  pthread_t vm_tid_;
};

202 203
void SamplerManager::AddSampler(Sampler* sampler) {
  AtomicGuard atomic_guard(&samplers_access_counter_);
204
  DCHECK(sampler->IsActive());
205 206 207 208 209 210 211
  pthread_t thread_id = sampler->platform_data()->vm_tid();
  auto it = sampler_map_.find(thread_id);
  if (it == sampler_map_.end()) {
    SamplerList samplers;
    samplers.push_back(sampler);
    sampler_map_.emplace(thread_id, std::move(samplers));
  } else {
212
    SamplerList& samplers = it->second;
213 214
    auto sampler_it = std::find(samplers.begin(), samplers.end(), sampler);
    if (sampler_it == samplers.end()) samplers.push_back(sampler);
215
  }
216
}
217

218 219
void SamplerManager::RemoveSampler(Sampler* sampler) {
  AtomicGuard atomic_guard(&samplers_access_counter_);
220
  DCHECK(sampler->IsActive());
221 222 223 224 225 226 227 228
  pthread_t thread_id = sampler->platform_data()->vm_tid();
  auto it = sampler_map_.find(thread_id);
  DCHECK_NE(it, sampler_map_.end());
  SamplerList& samplers = it->second;
  samplers.erase(std::remove(samplers.begin(), samplers.end(), sampler),
                 samplers.end());
  if (samplers.empty()) {
    sampler_map_.erase(it);
229
  }
230
}
231

232 233
void SamplerManager::DoSample(const v8::RegisterState& state) {
  AtomicGuard atomic_guard(&samplers_access_counter_, false);
234
  // TODO(petermarshall): Add stat counters for the bailouts here.
235 236 237 238 239 240 241
  if (!atomic_guard.is_success()) return;
  pthread_t thread_id = pthread_self();
  auto it = sampler_map_.find(thread_id);
  if (it == sampler_map_.end()) return;
  SamplerList& samplers = it->second;

  for (Sampler* sampler : samplers) {
242
    if (!sampler->ShouldRecordSample()) continue;
243 244 245 246
    Isolate* isolate = sampler->isolate();
    // We require a fully initialized and entered isolate.
    if (isolate == nullptr || !isolate->IsInUse()) continue;
    sampler->SampleStack(state);
247
  }
248
}
249

250 251 252 253
SamplerManager* SamplerManager::instance() {
  static base::LeakyObject<SamplerManager> instance;
  return instance.get();
}
254

255 256 257 258 259 260 261 262 263 264 265 266 267 268
#elif V8_OS_WIN || V8_OS_CYGWIN

// ----------------------------------------------------------------------------
// Win32 profiler support. On Cygwin we use the same sampler implementation as
// on Win32.

class Sampler::PlatformData {
 public:
  // Get a handle to the calling thread. This is the thread that we are
  // going to profile. We need to make a copy of the handle because we are
  // going to use it in the sampler thread. Using GetThreadHandle() will
  // not work in this case. We're using OpenThread because DuplicateHandle
  // for some reason doesn't work in Chrome's sandbox.
  PlatformData()
Brice Dobry's avatar
Brice Dobry committed
269 270 271
      : profiled_thread_(OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME |
                                        THREAD_QUERY_INFORMATION,
                                    false, GetCurrentThreadId())) {}
272 273

  ~PlatformData() {
274
    if (profiled_thread_ != nullptr) {
275
      CloseHandle(profiled_thread_);
276
      profiled_thread_ = nullptr;
277 278 279 280 281 282 283 284
    }
  }

  HANDLE profiled_thread() { return profiled_thread_; }

 private:
  HANDLE profiled_thread_;
};
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306

#elif V8_OS_FUCHSIA

class Sampler::PlatformData {
 public:
  PlatformData() {
    zx_handle_duplicate(zx_thread_self(), ZX_RIGHT_SAME_RIGHTS,
                        &profiled_thread_);
  }
  ~PlatformData() {
    if (profiled_thread_ != ZX_HANDLE_INVALID) {
      zx_handle_close(profiled_thread_);
      profiled_thread_ = ZX_HANDLE_INVALID;
    }
  }

  zx_handle_t profiled_thread() { return profiled_thread_; }

 private:
  zx_handle_t profiled_thread_ = ZX_HANDLE_INVALID;
};

307 308 309 310 311 312
#endif  // USE_SIGNALS

#if defined(USE_SIGNALS)
class SignalHandler {
 public:
  static void IncreaseSamplerCount() {
313
    base::MutexGuard lock_guard(mutex_.Pointer());
314 315 316 317
    if (++client_count_ == 1) Install();
  }

  static void DecreaseSamplerCount() {
318
    base::MutexGuard lock_guard(mutex_.Pointer());
319 320 321 322
    if (--client_count_ == 0) Restore();
  }

  static bool Installed() {
323
    base::MutexGuard lock_guard(mutex_.Pointer());
324 325 326 327 328 329 330 331 332
    return signal_handler_installed_;
  }

 private:
  static void Install() {
    struct sigaction sa;
    sa.sa_sigaction = &HandleProfilerSignal;
    sigemptyset(&sa.sa_mask);
#if V8_OS_QNX
333
    sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
334
#else
335
    sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
336 337 338 339 340 341 342
#endif
    signal_handler_installed_ =
        (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
  }

  static void Restore() {
    if (signal_handler_installed_) {
343
      sigaction(SIGPROF, &old_signal_handler_, nullptr);
344 345 346 347 348 349
      signal_handler_installed_ = false;
    }
  }

  static void FillRegisterState(void* context, RegisterState* regs);
  static void HandleProfilerSignal(int signal, siginfo_t* info, void* context);
yangguo's avatar
yangguo committed
350

351
  // Protects the process wide state below.
352
  static base::LazyMutex mutex_;
353 354 355 356 357
  static int client_count_;
  static bool signal_handler_installed_;
  static struct sigaction old_signal_handler_;
};

358
base::LazyMutex SignalHandler::mutex_ = LAZY_MUTEX_INITIALIZER;
359 360 361
int SignalHandler::client_count_ = 0;
struct sigaction SignalHandler::old_signal_handler_;
bool SignalHandler::signal_handler_installed_ = false;
362

363 364 365 366 367 368
void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info,
                                         void* context) {
  USE(info);
  if (signal != SIGPROF) return;
  v8::RegisterState state;
  FillRegisterState(context, &state);
369
  SamplerManager::instance()->DoSample(state);
370 371 372 373 374
}

void SignalHandler::FillRegisterState(void* context, RegisterState* state) {
  // Extracting the sample from the context is extremely machine dependent.
  ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
375 376 377
#if !(V8_OS_OPENBSD || \
      (V8_OS_LINUX &&  \
       (V8_HOST_ARCH_PPC || V8_HOST_ARCH_S390 || V8_HOST_ARCH_PPC64)))
378 379
  mcontext_t& mcontext = ucontext->uc_mcontext;
#endif
380
#if V8_OS_LINUX
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
#if V8_HOST_ARCH_IA32
  state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_EIP]);
  state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]);
  state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]);
#elif V8_HOST_ARCH_X64
  state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_RIP]);
  state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]);
  state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]);
#elif V8_HOST_ARCH_ARM
#if V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4)
  // Old GLibc ARM versions used a gregs[] array to access the register
  // values from mcontext_t.
  state->pc = reinterpret_cast<void*>(mcontext.gregs[R15]);
  state->sp = reinterpret_cast<void*>(mcontext.gregs[R13]);
  state->fp = reinterpret_cast<void*>(mcontext.gregs[R11]);
396
  state->lr = reinterpret_cast<void*>(mcontext.gregs[R14]);
397 398 399 400
#else
  state->pc = reinterpret_cast<void*>(mcontext.arm_pc);
  state->sp = reinterpret_cast<void*>(mcontext.arm_sp);
  state->fp = reinterpret_cast<void*>(mcontext.arm_fp);
401
  state->lr = reinterpret_cast<void*>(mcontext.arm_lr);
402 403 404 405 406 407
#endif  // V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4)
#elif V8_HOST_ARCH_ARM64
  state->pc = reinterpret_cast<void*>(mcontext.pc);
  state->sp = reinterpret_cast<void*>(mcontext.sp);
  // FP is an alias for x29.
  state->fp = reinterpret_cast<void*>(mcontext.regs[29]);
408 409
  // LR is an alias for x30.
  state->lr = reinterpret_cast<void*>(mcontext.regs[30]);
410 411 412 413 414 415 416 417
#elif V8_HOST_ARCH_MIPS
  state->pc = reinterpret_cast<void*>(mcontext.pc);
  state->sp = reinterpret_cast<void*>(mcontext.gregs[29]);
  state->fp = reinterpret_cast<void*>(mcontext.gregs[30]);
#elif V8_HOST_ARCH_MIPS64
  state->pc = reinterpret_cast<void*>(mcontext.pc);
  state->sp = reinterpret_cast<void*>(mcontext.gregs[29]);
  state->fp = reinterpret_cast<void*>(mcontext.gregs[30]);
418 419 420 421
#elif V8_HOST_ARCH_LOONG64
  state->pc = reinterpret_cast<void*>(mcontext.__pc);
  state->sp = reinterpret_cast<void*>(mcontext.__gregs[3]);
  state->fp = reinterpret_cast<void*>(mcontext.__gregs[22]);
422
#elif V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64
423
#if V8_LIBC_GLIBC
424
  state->pc = reinterpret_cast<void*>(ucontext->uc_mcontext.regs->nip);
Brice Dobry's avatar
Brice Dobry committed
425 426
  state->sp = reinterpret_cast<void*>(ucontext->uc_mcontext.regs->gpr[PT_R1]);
  state->fp = reinterpret_cast<void*>(ucontext->uc_mcontext.regs->gpr[PT_R31]);
427
  state->lr = reinterpret_cast<void*>(ucontext->uc_mcontext.regs->link);
428 429 430 431 432
#else
  // Some C libraries, notably Musl, define the regs member as a void pointer
  state->pc = reinterpret_cast<void*>(ucontext->uc_mcontext.gp_regs[32]);
  state->sp = reinterpret_cast<void*>(ucontext->uc_mcontext.gp_regs[1]);
  state->fp = reinterpret_cast<void*>(ucontext->uc_mcontext.gp_regs[31]);
433
  state->lr = reinterpret_cast<void*>(ucontext->uc_mcontext.gp_regs[36]);
434
#endif
435 436 437 438 439 440 441 442 443 444 445
#elif V8_HOST_ARCH_S390
#if V8_TARGET_ARCH_32_BIT
  // 31-bit target will have bit 0 (MSB) of the PSW set to denote addressing
  // mode.  This bit needs to be masked out to resolve actual address.
  state->pc =
      reinterpret_cast<void*>(ucontext->uc_mcontext.psw.addr & 0x7FFFFFFF);
#else
  state->pc = reinterpret_cast<void*>(ucontext->uc_mcontext.psw.addr);
#endif  // V8_TARGET_ARCH_32_BIT
  state->sp = reinterpret_cast<void*>(ucontext->uc_mcontext.gregs[15]);
  state->fp = reinterpret_cast<void*>(ucontext->uc_mcontext.gregs[11]);
446
  state->lr = reinterpret_cast<void*>(ucontext->uc_mcontext.gregs[14]);
Brice Dobry's avatar
Brice Dobry committed
447 448 449 450 451 452
#elif V8_HOST_ARCH_RISCV64
  // Spec CH.25 RISC-V Assembly Programmer’s Handbook
  state->pc = reinterpret_cast<void*>(mcontext.__gregs[REG_PC]);
  state->sp = reinterpret_cast<void*>(mcontext.__gregs[REG_SP]);
  state->fp = reinterpret_cast<void*>(mcontext.__gregs[REG_S0]);
  state->lr = reinterpret_cast<void*>(mcontext.__gregs[REG_RA]);
453
#endif  // V8_HOST_ARCH_*
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
#elif V8_OS_IOS

#if V8_TARGET_ARCH_ARM64
  // Building for the iOS device.
  state->pc = reinterpret_cast<void*>(mcontext->__ss.__pc);
  state->sp = reinterpret_cast<void*>(mcontext->__ss.__sp);
  state->fp = reinterpret_cast<void*>(mcontext->__ss.__fp);
#elif V8_TARGET_ARCH_X64
  // Building for the iOS simulator.
  state->pc = reinterpret_cast<void*>(mcontext->__ss.__rip);
  state->sp = reinterpret_cast<void*>(mcontext->__ss.__rsp);
  state->fp = reinterpret_cast<void*>(mcontext->__ss.__rbp);
#else
#error Unexpected iOS target architecture.
#endif  // V8_TARGET_ARCH_ARM64

470
#elif V8_OS_DARWIN
471 472 473 474 475 476 477 478
#if V8_HOST_ARCH_X64
  state->pc = reinterpret_cast<void*>(mcontext->__ss.__rip);
  state->sp = reinterpret_cast<void*>(mcontext->__ss.__rsp);
  state->fp = reinterpret_cast<void*>(mcontext->__ss.__rbp);
#elif V8_HOST_ARCH_IA32
  state->pc = reinterpret_cast<void*>(mcontext->__ss.__eip);
  state->sp = reinterpret_cast<void*>(mcontext->__ss.__esp);
  state->fp = reinterpret_cast<void*>(mcontext->__ss.__ebp);
479 480 481 482 483 484 485 486
#elif V8_HOST_ARCH_ARM64
  state->pc =
      reinterpret_cast<void*>(arm_thread_state64_get_pc(mcontext->__ss));
  state->sp =
      reinterpret_cast<void*>(arm_thread_state64_get_sp(mcontext->__ss));
  state->fp =
      reinterpret_cast<void*>(arm_thread_state64_get_fp(mcontext->__ss));
#endif  // V8_HOST_ARCH_*
487 488 489 490 491 492 493 494 495 496
#elif V8_OS_FREEBSD
#if V8_HOST_ARCH_IA32
  state->pc = reinterpret_cast<void*>(mcontext.mc_eip);
  state->sp = reinterpret_cast<void*>(mcontext.mc_esp);
  state->fp = reinterpret_cast<void*>(mcontext.mc_ebp);
#elif V8_HOST_ARCH_X64
  state->pc = reinterpret_cast<void*>(mcontext.mc_rip);
  state->sp = reinterpret_cast<void*>(mcontext.mc_rsp);
  state->fp = reinterpret_cast<void*>(mcontext.mc_rbp);
#elif V8_HOST_ARCH_ARM
497 498 499
  state->pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_PC]);
  state->sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_SP]);
  state->fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_FP]);
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
#endif  // V8_HOST_ARCH_*
#elif V8_OS_NETBSD
#if V8_HOST_ARCH_IA32
  state->pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_EIP]);
  state->sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_ESP]);
  state->fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_EBP]);
#elif V8_HOST_ARCH_X64
  state->pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_RIP]);
  state->sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RSP]);
  state->fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RBP]);
#endif  // V8_HOST_ARCH_*
#elif V8_OS_OPENBSD
#if V8_HOST_ARCH_IA32
  state->pc = reinterpret_cast<void*>(ucontext->sc_eip);
  state->sp = reinterpret_cast<void*>(ucontext->sc_esp);
  state->fp = reinterpret_cast<void*>(ucontext->sc_ebp);
#elif V8_HOST_ARCH_X64
  state->pc = reinterpret_cast<void*>(ucontext->sc_rip);
  state->sp = reinterpret_cast<void*>(ucontext->sc_rsp);
  state->fp = reinterpret_cast<void*>(ucontext->sc_rbp);
#endif  // V8_HOST_ARCH_*
#elif V8_OS_SOLARIS
  state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_PC]);
  state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_SP]);
  state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_FP]);
#elif V8_OS_QNX
#if V8_HOST_ARCH_IA32
  state->pc = reinterpret_cast<void*>(mcontext.cpu.eip);
  state->sp = reinterpret_cast<void*>(mcontext.cpu.esp);
  state->fp = reinterpret_cast<void*>(mcontext.cpu.ebp);
#elif V8_HOST_ARCH_ARM
  state->pc = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_PC]);
  state->sp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_SP]);
  state->fp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_FP]);
#endif  // V8_HOST_ARCH_*
#elif V8_OS_AIX
  state->pc = reinterpret_cast<void*>(mcontext.jmp_context.iar);
  state->sp = reinterpret_cast<void*>(mcontext.jmp_context.gpr[1]);
  state->fp = reinterpret_cast<void*>(mcontext.jmp_context.gpr[31]);
539
  state->lr = reinterpret_cast<void*>(mcontext.jmp_context.lr);
540 541 542 543 544 545
#endif  // V8_OS_AIX
}

#endif  // USE_SIGNALS

Sampler::Sampler(Isolate* isolate)
546
    : isolate_(isolate), data_(std::make_unique<PlatformData>()) {}
547

Brice Dobry's avatar
Brice Dobry committed
548
Sampler::~Sampler() { DCHECK(!IsActive()); }
549 550 551 552 553

void Sampler::Start() {
  DCHECK(!IsActive());
  SetActive(true);
#if defined(USE_SIGNALS)
554
  SignalHandler::IncreaseSamplerCount();
555
  SamplerManager::instance()->AddSampler(this);
556 557 558 559 560
#endif
}

void Sampler::Stop() {
#if defined(USE_SIGNALS)
561
  SamplerManager::instance()->RemoveSampler(this);
562
  SignalHandler::DecreaseSamplerCount();
563 564 565 566 567 568 569 570 571
#endif
  DCHECK(IsActive());
  SetActive(false);
}

#if defined(USE_SIGNALS)

void Sampler::DoSample() {
  if (!SignalHandler::Installed()) return;
572
  DCHECK(IsActive());
573
  SetShouldRecordSample();
574 575 576 577 578 579 580
  pthread_kill(platform_data()->vm_tid(), SIGPROF);
}

#elif V8_OS_WIN || V8_OS_CYGWIN

void Sampler::DoSample() {
  HANDLE profiled_thread = platform_data()->profiled_thread();
581
  if (profiled_thread == nullptr) return;
582 583 584 585 586 587 588 589 590 591 592 593 594 595

  const DWORD kSuspendFailed = static_cast<DWORD>(-1);
  if (SuspendThread(profiled_thread) == kSuspendFailed) return;

  // Context used for sampling the register state of the profiled thread.
  CONTEXT context;
  memset(&context, 0, sizeof(context));
  context.ContextFlags = CONTEXT_FULL;
  if (GetThreadContext(profiled_thread, &context) != 0) {
    v8::RegisterState state;
#if V8_HOST_ARCH_X64
    state.pc = reinterpret_cast<void*>(context.Rip);
    state.sp = reinterpret_cast<void*>(context.Rsp);
    state.fp = reinterpret_cast<void*>(context.Rbp);
596 597 598 599
#elif V8_HOST_ARCH_ARM64
    state.pc = reinterpret_cast<void*>(context.Pc);
    state.sp = reinterpret_cast<void*>(context.Sp);
    state.fp = reinterpret_cast<void*>(context.Fp);
600 601 602 603 604 605 606 607 608 609
#else
    state.pc = reinterpret_cast<void*>(context.Eip);
    state.sp = reinterpret_cast<void*>(context.Esp);
    state.fp = reinterpret_cast<void*>(context.Ebp);
#endif
    SampleStack(state);
  }
  ResumeThread(profiled_thread);
}

610 611 612 613 614 615
#elif V8_OS_FUCHSIA

void Sampler::DoSample() {
  zx_handle_t profiled_thread = platform_data()->profiled_thread();
  if (profiled_thread == ZX_HANDLE_INVALID) return;

616 617
  zx_handle_t suspend_token = ZX_HANDLE_INVALID;
  if (zx_task_suspend_token(profiled_thread, &suspend_token) != ZX_OK) return;
618 619 620 621 622 623 624 625 626 627 628

  // Wait for the target thread to become suspended, or to exit.
  // TODO(wez): There is currently no suspension count for threads, so there
  // is a risk that some other caller resumes the thread in-between our suspend
  // and wait calls, causing us to miss the SUSPENDED signal. We apply a 100ms
  // deadline to protect against hanging the sampler thread in this case.
  zx_signals_t signals = 0;
  zx_status_t suspended = zx_object_wait_one(
      profiled_thread, ZX_THREAD_SUSPENDED | ZX_THREAD_TERMINATED,
      zx_deadline_after(ZX_MSEC(100)), &signals);
  if (suspended != ZX_OK || (signals & ZX_THREAD_SUSPENDED) == 0) {
629
    zx_handle_close(suspend_token);
630 631 632
    return;
  }

633 634 635 636
  // Fetch a copy of its "general register" states.
  zx_thread_state_general_regs_t thread_state = {};
  if (zx_thread_read_state(profiled_thread, ZX_THREAD_STATE_GENERAL_REGS,
                           &thread_state, sizeof(thread_state)) == ZX_OK) {
637 638 639 640 641 642 643 644 645 646 647 648 649
    v8::RegisterState state;
#if V8_HOST_ARCH_X64
    state.pc = reinterpret_cast<void*>(thread_state.rip);
    state.sp = reinterpret_cast<void*>(thread_state.rsp);
    state.fp = reinterpret_cast<void*>(thread_state.rbp);
#elif V8_HOST_ARCH_ARM64
    state.pc = reinterpret_cast<void*>(thread_state.pc);
    state.sp = reinterpret_cast<void*>(thread_state.sp);
    state.fp = reinterpret_cast<void*>(thread_state.r[29]);
#endif
    SampleStack(state);
  }

650
  zx_handle_close(suspend_token);
651 652
}

653 654 655 656 657
// TODO(wez): Remove this once the Fuchsia SDK has rolled.
#if defined(ZX_THREAD_STATE_REGSET0)
#undef ZX_THREAD_STATE_GENERAL_REGS
#endif

658 659 660 661
#endif  // USE_SIGNALS

}  // namespace sampler
}  // namespace v8