platform-solaris.cc revision 8b112d2025046f85ef7f6be087c6129c872ebad2
1// Copyright 2006-2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Platform specific code for Solaris 10 goes here. For the POSIX comaptible
29// parts the implementation is in platform-posix.cc.
30
31#ifdef __sparc
32# error "V8 does not support the SPARC CPU architecture."
33#endif
34
35#include <sys/stack.h>  // for stack alignment
36#include <unistd.h>  // getpagesize(), usleep()
37#include <sys/mman.h>  // mmap()
38#include <ucontext.h>  // walkstack(), getcontext()
39#include <dlfcn.h>     // dladdr
40#include <pthread.h>
41#include <sched.h>  // for sched_yield
42#include <semaphore.h>
43#include <time.h>
44#include <sys/time.h>  // gettimeofday(), timeradd()
45#include <errno.h>
46#include <ieeefp.h>  // finite()
47#include <signal.h>  // sigemptyset(), etc
48#include <sys/regset.h>
49
50
51#undef MAP_TYPE
52
53#include "v8.h"
54
55#include "platform.h"
56#include "vm-state-inl.h"
57
58
59// It seems there is a bug in some Solaris distributions (experienced in
60// SunOS 5.10 Generic_141445-09) which make it difficult or impossible to
61// access signbit() despite the availability of other C99 math functions.
62#ifndef signbit
63// Test sign - usually defined in math.h
64int signbit(double x) {
65  // We need to take care of the special case of both positive and negative
66  // versions of zero.
67  if (x == 0) {
68    return fpclass(x) & FP_NZERO;
69  } else {
70    // This won't detect negative NaN but that should be okay since we don't
71    // assume that behavior.
72    return x < 0;
73  }
74}
75#endif  // signbit
76
77namespace v8 {
78namespace internal {
79
80
81// 0 is never a valid thread id on Solaris since the main thread is 1 and
82// subsequent have their ids incremented from there
83static const pthread_t kNoThread = (pthread_t) 0;
84
85
86double ceiling(double x) {
87  return ceil(x);
88}
89
90
91void OS::Setup() {
92  // Seed the random number generator.
93  // Convert the current time to a 64-bit integer first, before converting it
94  // to an unsigned. Going directly will cause an overflow and the seed to be
95  // set to all ones. The seed will be identical for different instances that
96  // call this setup code within the same millisecond.
97  uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
98  srandom(static_cast<unsigned int>(seed));
99}
100
101
102uint64_t OS::CpuFeaturesImpliedByPlatform() {
103  return 0;  // Solaris runs on a lot of things.
104}
105
106
107int OS::ActivationFrameAlignment() {
108  return STACK_ALIGN;
109}
110
111
112void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
113  __asm__ __volatile__("" : : : "memory");
114  *ptr = value;
115}
116
117
118const char* OS::LocalTimezone(double time) {
119  if (isnan(time)) return "";
120  time_t tv = static_cast<time_t>(floor(time/msPerSecond));
121  struct tm* t = localtime(&tv);
122  if (NULL == t) return "";
123  return tzname[0];  // The location of the timezone string on Solaris.
124}
125
126
127double OS::LocalTimeOffset() {
128  // On Solaris, struct tm does not contain a tm_gmtoff field.
129  time_t utc = time(NULL);
130  ASSERT(utc != -1);
131  struct tm* loc = localtime(&utc);
132  ASSERT(loc != NULL);
133  return static_cast<double>((mktime(loc) - utc) * msPerSecond);
134}
135
136
137// We keep the lowest and highest addresses mapped as a quick way of
138// determining that pointers are outside the heap (used mostly in assertions
139// and verification).  The estimate is conservative, ie, not all addresses in
140// 'allocated' space are actually allocated to our heap.  The range is
141// [lowest, highest), inclusive on the low and and exclusive on the high end.
142static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
143static void* highest_ever_allocated = reinterpret_cast<void*>(0);
144
145
146static void UpdateAllocatedSpaceLimits(void* address, int size) {
147  lowest_ever_allocated = Min(lowest_ever_allocated, address);
148  highest_ever_allocated =
149      Max(highest_ever_allocated,
150          reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
151}
152
153
154bool OS::IsOutsideAllocatedSpace(void* address) {
155  return address < lowest_ever_allocated || address >= highest_ever_allocated;
156}
157
158
159size_t OS::AllocateAlignment() {
160  return static_cast<size_t>(getpagesize());
161}
162
163
164void* OS::Allocate(const size_t requested,
165                   size_t* allocated,
166                   bool is_executable) {
167  const size_t msize = RoundUp(requested, getpagesize());
168  int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
169  void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
170
171  if (mbase == MAP_FAILED) {
172    LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
173    return NULL;
174  }
175  *allocated = msize;
176  UpdateAllocatedSpaceLimits(mbase, msize);
177  return mbase;
178}
179
180
181void OS::Free(void* address, const size_t size) {
182  // TODO(1240712): munmap has a return value which is ignored here.
183  int result = munmap(address, size);
184  USE(result);
185  ASSERT(result == 0);
186}
187
188
189#ifdef ENABLE_HEAP_PROTECTION
190
191void OS::Protect(void* address, size_t size) {
192  // TODO(1240712): mprotect has a return value which is ignored here.
193  mprotect(address, size, PROT_READ);
194}
195
196
197void OS::Unprotect(void* address, size_t size, bool is_executable) {
198  // TODO(1240712): mprotect has a return value which is ignored here.
199  int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
200  mprotect(address, size, prot);
201}
202
203#endif
204
205
206void OS::Sleep(int milliseconds) {
207  useconds_t ms = static_cast<useconds_t>(milliseconds);
208  usleep(1000 * ms);
209}
210
211
212void OS::Abort() {
213  // Redirect to std abort to signal abnormal program termination.
214  abort();
215}
216
217
218void OS::DebugBreak() {
219  asm("int $3");
220}
221
222
223class PosixMemoryMappedFile : public OS::MemoryMappedFile {
224 public:
225  PosixMemoryMappedFile(FILE* file, void* memory, int size)
226    : file_(file), memory_(memory), size_(size) { }
227  virtual ~PosixMemoryMappedFile();
228  virtual void* memory() { return memory_; }
229  virtual int size() { return size_; }
230 private:
231  FILE* file_;
232  void* memory_;
233  int size_;
234};
235
236
237OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
238  FILE* file = fopen(name, "r+");
239  if (file == NULL) return NULL;
240
241  fseek(file, 0, SEEK_END);
242  int size = ftell(file);
243
244  void* memory =
245      mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
246  return new PosixMemoryMappedFile(file, memory, size);
247}
248
249
250OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
251    void* initial) {
252  FILE* file = fopen(name, "w+");
253  if (file == NULL) return NULL;
254  int result = fwrite(initial, size, 1, file);
255  if (result < 1) {
256    fclose(file);
257    return NULL;
258  }
259  void* memory =
260      mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
261  return new PosixMemoryMappedFile(file, memory, size);
262}
263
264
265PosixMemoryMappedFile::~PosixMemoryMappedFile() {
266  if (memory_) munmap(memory_, size_);
267  fclose(file_);
268}
269
270
271void OS::LogSharedLibraryAddresses() {
272}
273
274
275void OS::SignalCodeMovingGC() {
276}
277
278
279struct StackWalker {
280  Vector<OS::StackFrame>& frames;
281  int index;
282};
283
284
285static int StackWalkCallback(uintptr_t pc, int signo, void* data) {
286  struct StackWalker* walker = static_cast<struct StackWalker*>(data);
287  Dl_info info;
288
289  int i = walker->index;
290
291  walker->frames[i].address = reinterpret_cast<void*>(pc);
292
293  // Make sure line termination is in place.
294  walker->frames[i].text[OS::kStackWalkMaxTextLen - 1] = '\0';
295
296  Vector<char> text = MutableCStrVector(walker->frames[i].text,
297                                        OS::kStackWalkMaxTextLen);
298
299  if (dladdr(reinterpret_cast<void*>(pc), &info) == 0) {
300    OS::SNPrintF(text, "[0x%p]", pc);
301  } else if ((info.dli_fname != NULL && info.dli_sname != NULL)) {
302    // We have symbol info.
303    OS::SNPrintF(text, "%s'%s+0x%x", info.dli_fname, info.dli_sname, pc);
304  } else {
305    // No local symbol info.
306    OS::SNPrintF(text,
307                 "%s'0x%p [0x%p]",
308                 info.dli_fname,
309                 pc - reinterpret_cast<uintptr_t>(info.dli_fbase),
310                 pc);
311  }
312  walker->index++;
313  return 0;
314}
315
316
317int OS::StackWalk(Vector<OS::StackFrame> frames) {
318  ucontext_t ctx;
319  struct StackWalker walker = { frames, 0 };
320
321  if (getcontext(&ctx) < 0) return kStackWalkError;
322
323  if (!walkcontext(&ctx, StackWalkCallback, &walker)) {
324    return kStackWalkError;
325  }
326
327  return walker.index;
328}
329
330
331// Constants used for mmap.
332static const int kMmapFd = -1;
333static const int kMmapFdOffset = 0;
334
335
336VirtualMemory::VirtualMemory(size_t size) {
337  address_ = mmap(NULL, size, PROT_NONE,
338                  MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
339                  kMmapFd, kMmapFdOffset);
340  size_ = size;
341}
342
343
344VirtualMemory::~VirtualMemory() {
345  if (IsReserved()) {
346    if (0 == munmap(address(), size())) address_ = MAP_FAILED;
347  }
348}
349
350
351bool VirtualMemory::IsReserved() {
352  return address_ != MAP_FAILED;
353}
354
355
356bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
357  int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
358  if (MAP_FAILED == mmap(address, size, prot,
359                         MAP_PRIVATE | MAP_ANON | MAP_FIXED,
360                         kMmapFd, kMmapFdOffset)) {
361    return false;
362  }
363
364  UpdateAllocatedSpaceLimits(address, size);
365  return true;
366}
367
368
369bool VirtualMemory::Uncommit(void* address, size_t size) {
370  return mmap(address, size, PROT_NONE,
371              MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
372              kMmapFd, kMmapFdOffset) != MAP_FAILED;
373}
374
375
376class Thread::PlatformData : public Malloced {
377 public:
378  PlatformData() : thread_(kNoThread) {  }
379
380  pthread_t thread_;  // Thread handle for pthread.
381};
382
383Thread::Thread(Isolate* isolate, const Options& options)
384    : data_(new PlatformData()),
385      isolate_(isolate),
386      stack_size_(options.stack_size) {
387  set_name(options.name);
388}
389
390
391Thread::Thread(Isolate* isolate, const char* name)
392    : data_(new PlatformData()),
393      isolate_(isolate),
394      stack_size_(0) {
395  set_name(name);
396}
397
398
399Thread::~Thread() {
400  delete data_;
401}
402
403
404static void* ThreadEntry(void* arg) {
405  Thread* thread = reinterpret_cast<Thread*>(arg);
406  // This is also initialized by the first argument to pthread_create() but we
407  // don't know which thread will run first (the original thread or the new
408  // one) so we initialize it here too.
409  thread->data()->thread_ = pthread_self();
410  ASSERT(thread->data()->thread_ != kNoThread);
411  Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
412  thread->Run();
413  return NULL;
414}
415
416
417void Thread::set_name(const char* name) {
418  strncpy(name_, name, sizeof(name_));
419  name_[sizeof(name_) - 1] = '\0';
420}
421
422
423void Thread::Start() {
424  pthread_attr_t* attr_ptr = NULL;
425  pthread_attr_t attr;
426  if (stack_size_ > 0) {
427    pthread_attr_init(&attr);
428    pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
429    attr_ptr = &attr;
430  }
431  pthread_create(&data_->thread_, NULL, ThreadEntry, this);
432  ASSERT(data_->thread_ != kNoThread);
433}
434
435
436void Thread::Join() {
437  pthread_join(data_->thread_, NULL);
438}
439
440
441Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
442  pthread_key_t key;
443  int result = pthread_key_create(&key, NULL);
444  USE(result);
445  ASSERT(result == 0);
446  return static_cast<LocalStorageKey>(key);
447}
448
449
450void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
451  pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
452  int result = pthread_key_delete(pthread_key);
453  USE(result);
454  ASSERT(result == 0);
455}
456
457
458void* Thread::GetThreadLocal(LocalStorageKey key) {
459  pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
460  return pthread_getspecific(pthread_key);
461}
462
463
464void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
465  pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
466  pthread_setspecific(pthread_key, value);
467}
468
469
470void Thread::YieldCPU() {
471  sched_yield();
472}
473
474
475class SolarisMutex : public Mutex {
476 public:
477
478  SolarisMutex() {
479    pthread_mutexattr_t attr;
480    pthread_mutexattr_init(&attr);
481    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
482    pthread_mutex_init(&mutex_, &attr);
483  }
484
485  ~SolarisMutex() { pthread_mutex_destroy(&mutex_); }
486
487  int Lock() { return pthread_mutex_lock(&mutex_); }
488
489  int Unlock() { return pthread_mutex_unlock(&mutex_); }
490
491  virtual bool TryLock() {
492    int result = pthread_mutex_trylock(&mutex_);
493    // Return false if the lock is busy and locking failed.
494    if (result == EBUSY) {
495      return false;
496    }
497    ASSERT(result == 0);  // Verify no other errors.
498    return true;
499  }
500
501 private:
502  pthread_mutex_t mutex_;
503};
504
505
506Mutex* OS::CreateMutex() {
507  return new SolarisMutex();
508}
509
510
511class SolarisSemaphore : public Semaphore {
512 public:
513  explicit SolarisSemaphore(int count) {  sem_init(&sem_, 0, count); }
514  virtual ~SolarisSemaphore() { sem_destroy(&sem_); }
515
516  virtual void Wait();
517  virtual bool Wait(int timeout);
518  virtual void Signal() { sem_post(&sem_); }
519 private:
520  sem_t sem_;
521};
522
523
524void SolarisSemaphore::Wait() {
525  while (true) {
526    int result = sem_wait(&sem_);
527    if (result == 0) return;  // Successfully got semaphore.
528    CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
529  }
530}
531
532
533#ifndef TIMEVAL_TO_TIMESPEC
534#define TIMEVAL_TO_TIMESPEC(tv, ts) do {                            \
535    (ts)->tv_sec = (tv)->tv_sec;                                    \
536    (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
537} while (false)
538#endif
539
540
541#ifndef timeradd
542#define timeradd(a, b, result) \
543  do { \
544    (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
545    (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
546    if ((result)->tv_usec >= 1000000) { \
547      ++(result)->tv_sec; \
548      (result)->tv_usec -= 1000000; \
549    } \
550  } while (0)
551#endif
552
553
554bool SolarisSemaphore::Wait(int timeout) {
555  const long kOneSecondMicros = 1000000;  // NOLINT
556
557  // Split timeout into second and nanosecond parts.
558  struct timeval delta;
559  delta.tv_usec = timeout % kOneSecondMicros;
560  delta.tv_sec = timeout / kOneSecondMicros;
561
562  struct timeval current_time;
563  // Get the current time.
564  if (gettimeofday(&current_time, NULL) == -1) {
565    return false;
566  }
567
568  // Calculate time for end of timeout.
569  struct timeval end_time;
570  timeradd(&current_time, &delta, &end_time);
571
572  struct timespec ts;
573  TIMEVAL_TO_TIMESPEC(&end_time, &ts);
574  // Wait for semaphore signalled or timeout.
575  while (true) {
576    int result = sem_timedwait(&sem_, &ts);
577    if (result == 0) return true;  // Successfully got semaphore.
578    if (result == -1 && errno == ETIMEDOUT) return false;  // Timeout.
579    CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
580  }
581}
582
583
584Semaphore* OS::CreateSemaphore(int count) {
585  return new SolarisSemaphore(count);
586}
587
588
589#ifdef ENABLE_LOGGING_AND_PROFILING
590
591static Sampler* active_sampler_ = NULL;
592static pthread_t vm_tid_ = 0;
593
594
595static pthread_t GetThreadID() {
596  return pthread_self();
597}
598
599
600static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
601  USE(info);
602  if (signal != SIGPROF) return;
603  if (active_sampler_ == NULL || !active_sampler_->IsActive()) return;
604  if (vm_tid_ != GetThreadID()) return;
605
606  TickSample sample_obj;
607  TickSample* sample = CpuProfiler::TickSampleEvent();
608  if (sample == NULL) sample = &sample_obj;
609
610  // Extracting the sample from the context is extremely machine dependent.
611  ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
612  mcontext_t& mcontext = ucontext->uc_mcontext;
613  sample->state = Top::current_vm_state();
614
615  sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]);
616  sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]);
617  sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]);
618
619  active_sampler_->SampleStack(sample);
620  active_sampler_->Tick(sample);
621}
622
623
624class Sampler::PlatformData : public Malloced {
625 public:
626  enum SleepInterval {
627    FULL_INTERVAL,
628    HALF_INTERVAL
629  };
630
631  explicit PlatformData(Sampler* sampler)
632      : sampler_(sampler),
633        signal_handler_installed_(false),
634        vm_tgid_(getpid()),
635        signal_sender_launched_(false) {
636  }
637
638  void SignalSender() {
639    while (sampler_->IsActive()) {
640      if (rate_limiter_.SuspendIfNecessary()) continue;
641      if (sampler_->IsProfiling() && RuntimeProfiler::IsEnabled()) {
642        SendProfilingSignal();
643        Sleep(HALF_INTERVAL);
644        RuntimeProfiler::NotifyTick();
645        Sleep(HALF_INTERVAL);
646      } else {
647        if (sampler_->IsProfiling()) SendProfilingSignal();
648        if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick();
649        Sleep(FULL_INTERVAL);
650      }
651    }
652  }
653
654  void SendProfilingSignal() {
655    if (!signal_handler_installed_) return;
656    pthread_kill(vm_tid_, SIGPROF);
657  }
658
659  void Sleep(SleepInterval full_or_half) {
660    // Convert ms to us and subtract 100 us to compensate delays
661    // occuring during signal delivery.
662    useconds_t interval = sampler_->interval_ * 1000 - 100;
663    if (full_or_half == HALF_INTERVAL) interval /= 2;
664    int result = usleep(interval);
665#ifdef DEBUG
666    if (result != 0 && errno != EINTR) {
667      fprintf(stderr,
668              "SignalSender usleep error; interval = %u, errno = %d\n",
669              interval,
670              errno);
671      ASSERT(result == 0 || errno == EINTR);
672    }
673#endif
674    USE(result);
675  }
676
677  Sampler* sampler_;
678  bool signal_handler_installed_;
679  struct sigaction old_signal_handler_;
680  int vm_tgid_;
681  bool signal_sender_launched_;
682  pthread_t signal_sender_thread_;
683  RuntimeProfilerRateLimiter rate_limiter_;
684};
685
686
687static void* SenderEntry(void* arg) {
688  Sampler::PlatformData* data =
689      reinterpret_cast<Sampler::PlatformData*>(arg);
690  data->SignalSender();
691  return 0;
692}
693
694
695Sampler::Sampler(Isolate* isolate, int interval)
696    : isolate_(isolate),
697      interval_(interval),
698      profiling_(false),
699      active_(false),
700      samples_taken_(0) {
701  data_ = new PlatformData(this);
702}
703
704
705Sampler::~Sampler() {
706  ASSERT(!data_->signal_sender_launched_);
707  delete data_;
708}
709
710
711void Sampler::Start() {
712  // There can only be one active sampler at the time on POSIX
713  // platforms.
714  ASSERT(!IsActive());
715  vm_tid_ = GetThreadID();
716
717  // Request profiling signals.
718  struct sigaction sa;
719  sa.sa_sigaction = ProfilerSignalHandler;
720  sigemptyset(&sa.sa_mask);
721  sa.sa_flags = SA_RESTART | SA_SIGINFO;
722  data_->signal_handler_installed_ =
723      sigaction(SIGPROF, &sa, &data_->old_signal_handler_) == 0;
724
725  // Start a thread that sends SIGPROF signal to VM thread.
726  // Sending the signal ourselves instead of relying on itimer provides
727  // much better accuracy.
728  SetActive(true);
729  if (pthread_create(
730          &data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) {
731    data_->signal_sender_launched_ = true;
732  }
733
734  // Set this sampler as the active sampler.
735  active_sampler_ = this;
736}
737
738
739void Sampler::Stop() {
740  SetActive(false);
741
742  // Wait for signal sender termination (it will exit after setting
743  // active_ to false).
744  if (data_->signal_sender_launched_) {
745    Top::WakeUpRuntimeProfilerThreadBeforeShutdown();
746    pthread_join(data_->signal_sender_thread_, NULL);
747    data_->signal_sender_launched_ = false;
748  }
749
750  // Restore old signal handler
751  if (data_->signal_handler_installed_) {
752    sigaction(SIGPROF, &data_->old_signal_handler_, 0);
753    data_->signal_handler_installed_ = false;
754  }
755
756  // This sampler is no longer the active sampler.
757  active_sampler_ = NULL;
758}
759
760#endif  // ENABLE_LOGGING_AND_PROFILING
761
762} }  // namespace v8::internal
763