1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "src/sampler.h"
7 #if V8_OS_POSIX && !V8_OS_CYGWIN
16 #if !V8_OS_QNX && !V8_OS_NACL
17 #include <sys/syscall.h> // NOLINT
21 #include <mach/mach.h>
22 // OpenBSD doesn't have <ucontext.h>. ucontext_t lives in <signal.h>
23 // and is a typedef for struct sigcontext. There is no uc_mcontext.
24 #elif(!V8_OS_ANDROID || defined(__BIONIC_HAVE_UCONTEXT_T)) && \
25 !V8_OS_OPENBSD && !V8_OS_NACL
31 // GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'.
32 // Old versions of the C library <signal.h> didn't define the type.
33 #if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T) && \
34 (defined(__arm__) || defined(__aarch64__)) && \
35 !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT)
36 #include <asm/sigcontext.h> // NOLINT
39 #elif V8_OS_WIN || V8_OS_CYGWIN
41 #include "src/base/win32-headers.h"
47 #include "src/base/platform/platform.h"
48 #include "src/cpu-profiler-inl.h"
49 #include "src/flags.h"
50 #include "src/frames-inl.h"
52 #include "src/simulator.h"
53 #include "src/v8threads.h"
54 #include "src/vm-state-inl.h"
57 #if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T)
59 // Not all versions of Android's C library provide ucontext_t.
60 // Detect this and provide custom but compatible definitions. Note that these
61 // follow the GLibc naming convention to access register values from
64 // See http://code.google.com/p/android/issues/detail?id=34784
68 typedef struct sigcontext mcontext_t;
70 typedef struct ucontext {
72 struct ucontext* uc_link;
74 mcontext_t uc_mcontext;
75 // Other fields are not used by V8, don't define them here.
78 #elif defined(__aarch64__)
80 typedef struct sigcontext mcontext_t;
82 typedef struct ucontext {
84 struct ucontext *uc_link;
86 mcontext_t uc_mcontext;
87 // Other fields are not used by V8, don't define them here.
90 #elif defined(__mips__)
91 // MIPS version of sigcontext, for Android bionic.
113 typedef struct ucontext {
115 struct ucontext* uc_link;
117 mcontext_t uc_mcontext;
118 // Other fields are not used by V8, don't define them here.
121 #elif defined(__i386__)
122 // x86 version for Android.
130 typedef uint32_t kernel_sigset_t[2]; // x86 kernel uses 64-bit signal masks
131 typedef struct ucontext {
133 struct ucontext* uc_link;
135 mcontext_t uc_mcontext;
136 // Other fields are not used by V8, don't define them here.
138 enum { REG_EBP = 6, REG_ESP = 7, REG_EIP = 14 };
140 #elif defined(__x86_64__)
141 // x64 version for Android.
145 uint64_t __reserved1[8];
148 typedef struct ucontext {
150 struct ucontext *uc_link;
152 mcontext_t uc_mcontext;
153 // Other fields are not used by V8, don't define them here.
155 enum { REG_RBP = 10, REG_RSP = 15, REG_RIP = 16 };
158 #endif // V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T)
166 class PlatformDataCommon : public Malloced {
168 PlatformDataCommon() : profiled_thread_id_(ThreadId::Current()) {}
169 ThreadId profiled_thread_id() { return profiled_thread_id_; }
172 ~PlatformDataCommon() {}
175 ThreadId profiled_thread_id_;
180 #if defined(USE_SIGNALS)
182 class Sampler::PlatformData : public PlatformDataCommon {
184 PlatformData() : vm_tid_(pthread_self()) {}
185 pthread_t vm_tid() const { return vm_tid_; }
191 #elif V8_OS_WIN || V8_OS_CYGWIN
193 // ----------------------------------------------------------------------------
194 // Win32 profiler support. On Cygwin we use the same sampler implementation as
197 class Sampler::PlatformData : public PlatformDataCommon {
199 // Get a handle to the calling thread. This is the thread that we are
200 // going to profile. We need to make a copy of the handle because we are
201 // going to use it in the sampler thread. Using GetThreadHandle() will
202 // not work in this case. We're using OpenThread because DuplicateHandle
203 // for some reason doesn't work in Chrome's sandbox.
205 : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
206 THREAD_SUSPEND_RESUME |
207 THREAD_QUERY_INFORMATION,
209 GetCurrentThreadId())) {}
212 if (profiled_thread_ != NULL) {
213 CloseHandle(profiled_thread_);
214 profiled_thread_ = NULL;
218 HANDLE profiled_thread() { return profiled_thread_; }
221 HANDLE profiled_thread_;
226 #if defined(USE_SIMULATOR)
227 class SimulatorHelper {
229 inline bool Init(Isolate* isolate) {
230 simulator_ = isolate->thread_local_top()->simulator_;
231 // Check if there is active simulator.
232 return simulator_ != NULL;
235 inline void FillRegisters(v8::RegisterState* state) {
236 #if V8_TARGET_ARCH_ARM
237 state->pc = reinterpret_cast<Address>(simulator_->get_pc());
238 state->sp = reinterpret_cast<Address>(simulator_->get_register(
240 state->fp = reinterpret_cast<Address>(simulator_->get_register(
242 #elif V8_TARGET_ARCH_ARM64
243 if (simulator_->sp() == 0 || simulator_->fp() == 0) {
244 // It's possible that the simulator is interrupted while it is updating
245 // the sp or fp register. ARM64 simulator does this in two steps:
246 // first setting it to zero and then setting it to a new value.
247 // Bailout if sp/fp doesn't contain the new value.
250 state->pc = reinterpret_cast<Address>(simulator_->pc());
251 state->sp = reinterpret_cast<Address>(simulator_->sp());
252 state->fp = reinterpret_cast<Address>(simulator_->fp());
253 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
254 state->pc = reinterpret_cast<Address>(simulator_->get_pc());
255 state->sp = reinterpret_cast<Address>(simulator_->get_register(
257 state->fp = reinterpret_cast<Address>(simulator_->get_register(
263 Simulator* simulator_;
265 #endif // USE_SIMULATOR
268 #if defined(USE_SIGNALS)
270 class SignalHandler : public AllStatic {
272 static void SetUp() { if (!mutex_) mutex_ = new base::Mutex(); }
273 static void TearDown() { delete mutex_; mutex_ = NULL; }
275 static void IncreaseSamplerCount() {
276 base::LockGuard<base::Mutex> lock_guard(mutex_);
277 if (++client_count_ == 1) Install();
280 static void DecreaseSamplerCount() {
281 base::LockGuard<base::Mutex> lock_guard(mutex_);
282 if (--client_count_ == 0) Restore();
285 static bool Installed() {
286 return signal_handler_installed_;
290 static void Install() {
293 sa.sa_sigaction = &HandleProfilerSignal;
294 sigemptyset(&sa.sa_mask);
296 sa.sa_flags = SA_SIGINFO;
298 sa.sa_flags = SA_RESTART | SA_SIGINFO;
300 signal_handler_installed_ =
301 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
305 static void Restore() {
307 if (signal_handler_installed_) {
308 sigaction(SIGPROF, &old_signal_handler_, 0);
309 signal_handler_installed_ = false;
315 static void HandleProfilerSignal(int signal, siginfo_t* info, void* context);
317 // Protects the process wide state below.
318 static base::Mutex* mutex_;
319 static int client_count_;
320 static bool signal_handler_installed_;
321 static struct sigaction old_signal_handler_;
325 base::Mutex* SignalHandler::mutex_ = NULL;
326 int SignalHandler::client_count_ = 0;
327 struct sigaction SignalHandler::old_signal_handler_;
328 bool SignalHandler::signal_handler_installed_ = false;
331 // As Native Client does not support signal handling, profiling is disabled.
333 void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info,
336 if (signal != SIGPROF) return;
337 Isolate* isolate = Isolate::UnsafeCurrent();
338 if (isolate == NULL || !isolate->IsInUse()) {
339 // We require a fully initialized and entered isolate.
342 if (v8::Locker::IsActive() &&
343 !isolate->thread_manager()->IsLockedByCurrentThread()) {
347 Sampler* sampler = isolate->logger()->sampler();
348 if (sampler == NULL) return;
350 v8::RegisterState state;
352 #if defined(USE_SIMULATOR)
353 SimulatorHelper helper;
354 if (!helper.Init(isolate)) return;
355 helper.FillRegisters(&state);
356 // It possible that the simulator is interrupted while it is updating
357 // the sp or fp register. ARM64 simulator does this in two steps:
358 // first setting it to zero and then setting it to the new value.
359 // Bailout if sp/fp doesn't contain the new value.
360 if (state.sp == 0 || state.fp == 0) return;
362 // Extracting the sample from the context is extremely machine dependent.
363 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
365 mcontext_t& mcontext = ucontext->uc_mcontext;
368 #if V8_HOST_ARCH_IA32
369 state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
370 state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
371 state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
372 #elif V8_HOST_ARCH_X64
373 state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
374 state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
375 state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
376 #elif V8_HOST_ARCH_ARM
377 #if V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4)
378 // Old GLibc ARM versions used a gregs[] array to access the register
379 // values from mcontext_t.
380 state.pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
381 state.sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
382 state.fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
384 state.pc = reinterpret_cast<Address>(mcontext.arm_pc);
385 state.sp = reinterpret_cast<Address>(mcontext.arm_sp);
386 state.fp = reinterpret_cast<Address>(mcontext.arm_fp);
387 #endif // V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4)
388 #elif V8_HOST_ARCH_ARM64
389 state.pc = reinterpret_cast<Address>(mcontext.pc);
390 state.sp = reinterpret_cast<Address>(mcontext.sp);
391 // FP is an alias for x29.
392 state.fp = reinterpret_cast<Address>(mcontext.regs[29]);
393 #elif V8_HOST_ARCH_MIPS
394 state.pc = reinterpret_cast<Address>(mcontext.pc);
395 state.sp = reinterpret_cast<Address>(mcontext.gregs[29]);
396 state.fp = reinterpret_cast<Address>(mcontext.gregs[30]);
397 #elif V8_HOST_ARCH_MIPS64
398 state.pc = reinterpret_cast<Address>(mcontext.pc);
399 state.sp = reinterpret_cast<Address>(mcontext.gregs[29]);
400 state.fp = reinterpret_cast<Address>(mcontext.gregs[30]);
401 #endif // V8_HOST_ARCH_*
405 state.pc = reinterpret_cast<Address>(mcontext->__ss.__rip);
406 state.sp = reinterpret_cast<Address>(mcontext->__ss.__rsp);
407 state.fp = reinterpret_cast<Address>(mcontext->__ss.__rbp);
408 #else // !__DARWIN_UNIX03
409 state.pc = reinterpret_cast<Address>(mcontext->ss.rip);
410 state.sp = reinterpret_cast<Address>(mcontext->ss.rsp);
411 state.fp = reinterpret_cast<Address>(mcontext->ss.rbp);
412 #endif // __DARWIN_UNIX03
413 #elif V8_HOST_ARCH_IA32
415 state.pc = reinterpret_cast<Address>(mcontext->__ss.__eip);
416 state.sp = reinterpret_cast<Address>(mcontext->__ss.__esp);
417 state.fp = reinterpret_cast<Address>(mcontext->__ss.__ebp);
418 #else // !__DARWIN_UNIX03
419 state.pc = reinterpret_cast<Address>(mcontext->ss.eip);
420 state.sp = reinterpret_cast<Address>(mcontext->ss.esp);
421 state.fp = reinterpret_cast<Address>(mcontext->ss.ebp);
422 #endif // __DARWIN_UNIX03
423 #endif // V8_HOST_ARCH_IA32
425 #if V8_HOST_ARCH_IA32
426 state.pc = reinterpret_cast<Address>(mcontext.mc_eip);
427 state.sp = reinterpret_cast<Address>(mcontext.mc_esp);
428 state.fp = reinterpret_cast<Address>(mcontext.mc_ebp);
429 #elif V8_HOST_ARCH_X64
430 state.pc = reinterpret_cast<Address>(mcontext.mc_rip);
431 state.sp = reinterpret_cast<Address>(mcontext.mc_rsp);
432 state.fp = reinterpret_cast<Address>(mcontext.mc_rbp);
433 #elif V8_HOST_ARCH_ARM
434 state.pc = reinterpret_cast<Address>(mcontext.mc_r15);
435 state.sp = reinterpret_cast<Address>(mcontext.mc_r13);
436 state.fp = reinterpret_cast<Address>(mcontext.mc_r11);
437 #endif // V8_HOST_ARCH_*
439 #if V8_HOST_ARCH_IA32
440 state.pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_EIP]);
441 state.sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_ESP]);
442 state.fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_EBP]);
443 #elif V8_HOST_ARCH_X64
444 state.pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_RIP]);
445 state.sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RSP]);
446 state.fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RBP]);
447 #endif // V8_HOST_ARCH_*
449 #if V8_HOST_ARCH_IA32
450 state.pc = reinterpret_cast<Address>(ucontext->sc_eip);
451 state.sp = reinterpret_cast<Address>(ucontext->sc_esp);
452 state.fp = reinterpret_cast<Address>(ucontext->sc_ebp);
453 #elif V8_HOST_ARCH_X64
454 state.pc = reinterpret_cast<Address>(ucontext->sc_rip);
455 state.sp = reinterpret_cast<Address>(ucontext->sc_rsp);
456 state.fp = reinterpret_cast<Address>(ucontext->sc_rbp);
457 #endif // V8_HOST_ARCH_*
459 state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]);
460 state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]);
461 state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]);
463 #if V8_HOST_ARCH_IA32
464 state.pc = reinterpret_cast<Address>(mcontext.cpu.eip);
465 state.sp = reinterpret_cast<Address>(mcontext.cpu.esp);
466 state.fp = reinterpret_cast<Address>(mcontext.cpu.ebp);
467 #elif V8_HOST_ARCH_ARM
468 state.pc = reinterpret_cast<Address>(mcontext.cpu.gpr[ARM_REG_PC]);
469 state.sp = reinterpret_cast<Address>(mcontext.cpu.gpr[ARM_REG_SP]);
470 state.fp = reinterpret_cast<Address>(mcontext.cpu.gpr[ARM_REG_FP]);
471 #endif // V8_HOST_ARCH_*
473 #endif // USE_SIMULATOR
474 sampler->SampleStack(state);
481 class SamplerThread : public base::Thread {
483 static const int kSamplerThreadStackSize = 64 * KB;
485 explicit SamplerThread(int interval)
486 : Thread(base::Thread::Options("SamplerThread", kSamplerThreadStackSize)),
487 interval_(interval) {}
489 static void SetUp() { if (!mutex_) mutex_ = new base::Mutex(); }
490 static void TearDown() { delete mutex_; mutex_ = NULL; }
492 static void AddActiveSampler(Sampler* sampler) {
493 bool need_to_start = false;
494 base::LockGuard<base::Mutex> lock_guard(mutex_);
495 if (instance_ == NULL) {
496 // Start a thread that will send SIGPROF signal to VM threads,
497 // when CPU profiling will be enabled.
498 instance_ = new SamplerThread(sampler->interval());
499 need_to_start = true;
502 DCHECK(sampler->IsActive());
503 DCHECK(!instance_->active_samplers_.Contains(sampler));
504 DCHECK(instance_->interval_ == sampler->interval());
505 instance_->active_samplers_.Add(sampler);
507 if (need_to_start) instance_->StartSynchronously();
510 static void RemoveActiveSampler(Sampler* sampler) {
511 SamplerThread* instance_to_remove = NULL;
513 base::LockGuard<base::Mutex> lock_guard(mutex_);
515 DCHECK(sampler->IsActive());
516 bool removed = instance_->active_samplers_.RemoveElement(sampler);
520 // We cannot delete the instance immediately as we need to Join() the
521 // thread but we are holding mutex_ and the thread may try to acquire it.
522 if (instance_->active_samplers_.is_empty()) {
523 instance_to_remove = instance_;
528 if (!instance_to_remove) return;
529 instance_to_remove->Join();
530 delete instance_to_remove;
533 // Implement Thread::Run().
537 base::LockGuard<base::Mutex> lock_guard(mutex_);
538 if (active_samplers_.is_empty()) break;
539 // When CPU profiling is enabled both JavaScript and C++ code is
540 // profiled. We must not suspend.
541 for (int i = 0; i < active_samplers_.length(); ++i) {
542 Sampler* sampler = active_samplers_.at(i);
543 if (!sampler->IsProfiling()) continue;
547 base::OS::Sleep(interval_);
552 // Protects the process wide state below.
553 static base::Mutex* mutex_;
554 static SamplerThread* instance_;
557 List<Sampler*> active_samplers_;
559 DISALLOW_COPY_AND_ASSIGN(SamplerThread);
563 base::Mutex* SamplerThread::mutex_ = NULL;
564 SamplerThread* SamplerThread::instance_ = NULL;
568 // StackTracer implementation
570 DISABLE_ASAN void TickSample::Init(Isolate* isolate,
571 const v8::RegisterState& regs,
572 RecordCEntryFrame record_c_entry_frame) {
573 timestamp = base::TimeTicks::HighResolutionNow();
574 pc = reinterpret_cast<Address>(regs.pc);
575 state = isolate->current_vm_state();
577 // Avoid collecting traces while doing GC.
578 if (state == GC) return;
580 Address js_entry_sp = isolate->js_entry_sp();
581 if (js_entry_sp == 0) return; // Not executing JS now.
583 ExternalCallbackScope* scope = isolate->external_callback_scope();
584 Address handler = Isolate::handler(isolate->thread_local_top());
585 // If there is a handler on top of the external callback scope then
586 // we have already entrered JavaScript again and the external callback
587 // is not the top function.
588 if (scope && scope->scope_address() < handler) {
589 external_callback = scope->callback();
590 has_external_callback = true;
592 // Sample potential return address value for frameless invocation of
593 // stubs (we'll figure out later, if this value makes sense).
594 tos = Memory::Address_at(reinterpret_cast<Address>(regs.sp));
595 has_external_callback = false;
598 SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp),
599 reinterpret_cast<Address>(regs.sp), js_entry_sp);
600 top_frame_type = it.top_frame_type();
603 GetStackSample(isolate, regs, record_c_entry_frame,
604 reinterpret_cast<void**>(&stack[0]), kMaxFramesCount, &info);
605 frames_count = static_cast<unsigned>(info.frames_count);
609 void TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs,
610 RecordCEntryFrame record_c_entry_frame,
611 void** frames, size_t frames_limit,
612 v8::SampleInfo* sample_info) {
613 sample_info->frames_count = 0;
614 sample_info->vm_state = isolate->current_vm_state();
615 if (sample_info->vm_state == GC) return;
617 Address js_entry_sp = isolate->js_entry_sp();
618 if (js_entry_sp == 0) return; // Not executing JS now.
620 SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp),
621 reinterpret_cast<Address>(regs.sp), js_entry_sp);
623 if (record_c_entry_frame == kIncludeCEntryFrame && !it.done() &&
624 it.top_frame_type() == StackFrame::EXIT) {
625 frames[i++] = isolate->c_function();
627 while (!it.done() && i < frames_limit) {
628 frames[i++] = it.frame()->pc();
631 sample_info->frames_count = i;
635 void Sampler::SetUp() {
636 #if defined(USE_SIGNALS)
637 SignalHandler::SetUp();
639 SamplerThread::SetUp();
643 void Sampler::TearDown() {
644 SamplerThread::TearDown();
645 #if defined(USE_SIGNALS)
646 SignalHandler::TearDown();
651 Sampler::Sampler(Isolate* isolate, int interval)
655 has_processing_thread_(false),
657 is_counting_samples_(false),
658 js_and_external_sample_count_(0) {
659 data_ = new PlatformData;
663 Sampler::~Sampler() {
669 void Sampler::Start() {
672 SamplerThread::AddActiveSampler(this);
676 void Sampler::Stop() {
678 SamplerThread::RemoveActiveSampler(this);
683 void Sampler::IncreaseProfilingDepth() {
684 base::NoBarrier_AtomicIncrement(&profiling_, 1);
685 #if defined(USE_SIGNALS)
686 SignalHandler::IncreaseSamplerCount();
691 void Sampler::DecreaseProfilingDepth() {
692 #if defined(USE_SIGNALS)
693 SignalHandler::DecreaseSamplerCount();
695 base::NoBarrier_AtomicIncrement(&profiling_, -1);
699 void Sampler::SampleStack(const v8::RegisterState& state) {
700 TickSample* sample = isolate_->cpu_profiler()->StartTickSample();
701 TickSample sample_obj;
702 if (sample == NULL) sample = &sample_obj;
703 sample->Init(isolate_, state, TickSample::kIncludeCEntryFrame);
704 if (is_counting_samples_) {
705 if (sample->state == JS || sample->state == EXTERNAL) {
706 ++js_and_external_sample_count_;
710 if (sample != &sample_obj) {
711 isolate_->cpu_profiler()->FinishTickSample();
716 #if defined(USE_SIGNALS)
718 void Sampler::DoSample() {
719 if (!SignalHandler::Installed()) return;
720 pthread_kill(platform_data()->vm_tid(), SIGPROF);
723 #elif V8_OS_WIN || V8_OS_CYGWIN
725 void Sampler::DoSample() {
726 HANDLE profiled_thread = platform_data()->profiled_thread();
727 if (profiled_thread == NULL) return;
729 #if defined(USE_SIMULATOR)
730 SimulatorHelper helper;
731 if (!helper.Init(isolate())) return;
734 const DWORD kSuspendFailed = static_cast<DWORD>(-1);
735 if (SuspendThread(profiled_thread) == kSuspendFailed) return;
737 // Context used for sampling the register state of the profiled thread.
739 memset(&context, 0, sizeof(context));
740 context.ContextFlags = CONTEXT_FULL;
741 if (GetThreadContext(profiled_thread, &context) != 0) {
742 v8::RegisterState state;
743 #if defined(USE_SIMULATOR)
744 helper.FillRegisters(&state);
747 state.pc = reinterpret_cast<Address>(context.Rip);
748 state.sp = reinterpret_cast<Address>(context.Rsp);
749 state.fp = reinterpret_cast<Address>(context.Rbp);
751 state.pc = reinterpret_cast<Address>(context.Eip);
752 state.sp = reinterpret_cast<Address>(context.Esp);
753 state.fp = reinterpret_cast<Address>(context.Ebp);
755 #endif // USE_SIMULATOR
758 ResumeThread(profiled_thread);
761 #endif // USE_SIGNALS
764 } } // namespace v8::internal