• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2006-2008 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 FreeBSD goes here. For the POSIX comaptible parts
29 // the implementation is in platform-posix.cc.
30 
31 #include <pthread.h>
32 #include <semaphore.h>
33 #include <signal.h>
34 #include <sys/time.h>
35 #include <sys/resource.h>
36 #include <sys/types.h>
37 #include <sys/ucontext.h>
38 #include <stdlib.h>
39 
40 #include <sys/types.h>  // mmap & munmap
41 #include <sys/mman.h>   // mmap & munmap
42 #include <sys/stat.h>   // open
43 #include <sys/fcntl.h>  // open
44 #include <unistd.h>     // getpagesize
45 #include <execinfo.h>   // backtrace, backtrace_symbols
46 #include <strings.h>    // index
47 #include <errno.h>
48 #include <stdarg.h>
49 #include <limits.h>
50 
51 #undef MAP_TYPE
52 
53 #include "v8.h"
54 
55 #include "platform.h"
56 
57 
58 namespace v8 {
59 namespace internal {
60 
61 // 0 is never a valid thread id on FreeBSD since tids and pids share a
62 // name space and pid 0 is used to kill the group (see man 2 kill).
63 static const pthread_t kNoThread = (pthread_t) 0;
64 
65 
ceiling(double x)66 double ceiling(double x) {
67     // Correct as on OS X
68     if (-1.0 < x && x < 0.0) {
69         return -0.0;
70     } else {
71         return ceil(x);
72     }
73 }
74 
75 
Setup()76 void OS::Setup() {
77   // Seed the random number generator.
78   // Convert the current time to a 64-bit integer first, before converting it
79   // to an unsigned. Going directly can cause an overflow and the seed to be
80   // set to all ones. The seed will be identical for different instances that
81   // call this setup code within the same millisecond.
82   uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
83   srandom(static_cast<unsigned int>(seed));
84 }
85 
86 
CpuFeaturesImpliedByPlatform()87 uint64_t OS::CpuFeaturesImpliedByPlatform() {
88   return 0;  // FreeBSD runs on anything.
89 }
90 
91 
ActivationFrameAlignment()92 int OS::ActivationFrameAlignment() {
93   // 16 byte alignment on FreeBSD
94   return 16;
95 }
96 
97 
LocalTimezone(double time)98 const char* OS::LocalTimezone(double time) {
99   if (isnan(time)) return "";
100   time_t tv = static_cast<time_t>(floor(time/msPerSecond));
101   struct tm* t = localtime(&tv);
102   if (NULL == t) return "";
103   return t->tm_zone;
104 }
105 
106 
LocalTimeOffset()107 double OS::LocalTimeOffset() {
108   time_t tv = time(NULL);
109   struct tm* t = localtime(&tv);
110   // tm_gmtoff includes any daylight savings offset, so subtract it.
111   return static_cast<double>(t->tm_gmtoff * msPerSecond -
112                              (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
113 }
114 
115 
116 // We keep the lowest and highest addresses mapped as a quick way of
117 // determining that pointers are outside the heap (used mostly in assertions
118 // and verification).  The estimate is conservative, ie, not all addresses in
119 // 'allocated' space are actually allocated to our heap.  The range is
120 // [lowest, highest), inclusive on the low and and exclusive on the high end.
121 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
122 static void* highest_ever_allocated = reinterpret_cast<void*>(0);
123 
124 
UpdateAllocatedSpaceLimits(void * address,int size)125 static void UpdateAllocatedSpaceLimits(void* address, int size) {
126   lowest_ever_allocated = Min(lowest_ever_allocated, address);
127   highest_ever_allocated =
128       Max(highest_ever_allocated,
129           reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
130 }
131 
132 
IsOutsideAllocatedSpace(void * address)133 bool OS::IsOutsideAllocatedSpace(void* address) {
134   return address < lowest_ever_allocated || address >= highest_ever_allocated;
135 }
136 
137 
AllocateAlignment()138 size_t OS::AllocateAlignment() {
139   return getpagesize();
140 }
141 
142 
Allocate(const size_t requested,size_t * allocated,bool executable)143 void* OS::Allocate(const size_t requested,
144                    size_t* allocated,
145                    bool executable) {
146   const size_t msize = RoundUp(requested, getpagesize());
147   int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
148   void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
149 
150   if (mbase == MAP_FAILED) {
151     LOG(StringEvent("OS::Allocate", "mmap failed"));
152     return NULL;
153   }
154   *allocated = msize;
155   UpdateAllocatedSpaceLimits(mbase, msize);
156   return mbase;
157 }
158 
159 
Free(void * buf,const size_t length)160 void OS::Free(void* buf, const size_t length) {
161   // TODO(1240712): munmap has a return value which is ignored here.
162   int result = munmap(buf, length);
163   USE(result);
164   ASSERT(result == 0);
165 }
166 
167 
168 #ifdef ENABLE_HEAP_PROTECTION
169 
Protect(void * address,size_t size)170 void OS::Protect(void* address, size_t size) {
171   UNIMPLEMENTED();
172 }
173 
174 
Unprotect(void * address,size_t size,bool is_executable)175 void OS::Unprotect(void* address, size_t size, bool is_executable) {
176   UNIMPLEMENTED();
177 }
178 
179 #endif
180 
181 
Sleep(int milliseconds)182 void OS::Sleep(int milliseconds) {
183   unsigned int ms = static_cast<unsigned int>(milliseconds);
184   usleep(1000 * ms);
185 }
186 
187 
Abort()188 void OS::Abort() {
189   // Redirect to std abort to signal abnormal program termination.
190   abort();
191 }
192 
193 
DebugBreak()194 void OS::DebugBreak() {
195 #if (defined(__arm__) || defined(__thumb__)) && \
196     defined(CAN_USE_ARMV5_INSTRUCTIONS)
197   asm("bkpt 0");
198 #else
199   asm("int $3");
200 #endif
201 }
202 
203 
204 class PosixMemoryMappedFile : public OS::MemoryMappedFile {
205  public:
PosixMemoryMappedFile(FILE * file,void * memory,int size)206   PosixMemoryMappedFile(FILE* file, void* memory, int size)
207     : file_(file), memory_(memory), size_(size) { }
208   virtual ~PosixMemoryMappedFile();
memory()209   virtual void* memory() { return memory_; }
210  private:
211   FILE* file_;
212   void* memory_;
213   int size_;
214 };
215 
216 
create(const char * name,int size,void * initial)217 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
218     void* initial) {
219   FILE* file = fopen(name, "w+");
220   if (file == NULL) return NULL;
221   int result = fwrite(initial, size, 1, file);
222   if (result < 1) {
223     fclose(file);
224     return NULL;
225   }
226   void* memory =
227       mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
228   return new PosixMemoryMappedFile(file, memory, size);
229 }
230 
231 
~PosixMemoryMappedFile()232 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
233   if (memory_) munmap(memory_, size_);
234   fclose(file_);
235 }
236 
237 
238 #ifdef ENABLE_LOGGING_AND_PROFILING
StringToLong(char * buffer)239 static unsigned StringToLong(char* buffer) {
240   return static_cast<unsigned>(strtol(buffer, NULL, 16));  // NOLINT
241 }
242 #endif
243 
244 
LogSharedLibraryAddresses()245 void OS::LogSharedLibraryAddresses() {
246 #ifdef ENABLE_LOGGING_AND_PROFILING
247   static const int MAP_LENGTH = 1024;
248   int fd = open("/proc/self/maps", O_RDONLY);
249   if (fd < 0) return;
250   while (true) {
251     char addr_buffer[11];
252     addr_buffer[0] = '0';
253     addr_buffer[1] = 'x';
254     addr_buffer[10] = 0;
255     int result = read(fd, addr_buffer + 2, 8);
256     if (result < 8) break;
257     unsigned start = StringToLong(addr_buffer);
258     result = read(fd, addr_buffer + 2, 1);
259     if (result < 1) break;
260     if (addr_buffer[2] != '-') break;
261     result = read(fd, addr_buffer + 2, 8);
262     if (result < 8) break;
263     unsigned end = StringToLong(addr_buffer);
264     char buffer[MAP_LENGTH];
265     int bytes_read = -1;
266     do {
267       bytes_read++;
268       if (bytes_read >= MAP_LENGTH - 1)
269         break;
270       result = read(fd, buffer + bytes_read, 1);
271       if (result < 1) break;
272     } while (buffer[bytes_read] != '\n');
273     buffer[bytes_read] = 0;
274     // Ignore mappings that are not executable.
275     if (buffer[3] != 'x') continue;
276     char* start_of_path = index(buffer, '/');
277     // There may be no filename in this line.  Skip to next.
278     if (start_of_path == NULL) continue;
279     buffer[bytes_read] = 0;
280     LOG(SharedLibraryEvent(start_of_path, start, end));
281   }
282   close(fd);
283 #endif
284 }
285 
286 
StackWalk(Vector<OS::StackFrame> frames)287 int OS::StackWalk(Vector<OS::StackFrame> frames) {
288   int frames_size = frames.length();
289   void** addresses = NewArray<void*>(frames_size);
290 
291   int frames_count = backtrace(addresses, frames_size);
292 
293   char** symbols;
294   symbols = backtrace_symbols(addresses, frames_count);
295   if (symbols == NULL) {
296     DeleteArray(addresses);
297     return kStackWalkError;
298   }
299 
300   for (int i = 0; i < frames_count; i++) {
301     frames[i].address = addresses[i];
302     // Format a text representation of the frame based on the information
303     // available.
304     SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
305              "%s",
306              symbols[i]);
307     // Make sure line termination is in place.
308     frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
309   }
310 
311   DeleteArray(addresses);
312   free(symbols);
313 
314   return frames_count;
315 }
316 
317 
318 // Constants used for mmap.
319 static const int kMmapFd = -1;
320 static const int kMmapFdOffset = 0;
321 
322 
VirtualMemory(size_t size)323 VirtualMemory::VirtualMemory(size_t size) {
324   address_ = mmap(NULL, size, PROT_NONE,
325                   MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
326                   kMmapFd, kMmapFdOffset);
327   size_ = size;
328 }
329 
330 
~VirtualMemory()331 VirtualMemory::~VirtualMemory() {
332   if (IsReserved()) {
333     if (0 == munmap(address(), size())) address_ = MAP_FAILED;
334   }
335 }
336 
337 
IsReserved()338 bool VirtualMemory::IsReserved() {
339   return address_ != MAP_FAILED;
340 }
341 
342 
Commit(void * address,size_t size,bool executable)343 bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
344   int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
345   if (MAP_FAILED == mmap(address, size, prot,
346                          MAP_PRIVATE | MAP_ANON | MAP_FIXED,
347                          kMmapFd, kMmapFdOffset)) {
348     return false;
349   }
350 
351   UpdateAllocatedSpaceLimits(address, size);
352   return true;
353 }
354 
355 
Uncommit(void * address,size_t size)356 bool VirtualMemory::Uncommit(void* address, size_t size) {
357   return mmap(address, size, PROT_NONE,
358               MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
359               kMmapFd, kMmapFdOffset) != MAP_FAILED;
360 }
361 
362 
363 class ThreadHandle::PlatformData : public Malloced {
364  public:
PlatformData(ThreadHandle::Kind kind)365   explicit PlatformData(ThreadHandle::Kind kind) {
366     Initialize(kind);
367   }
368 
Initialize(ThreadHandle::Kind kind)369   void Initialize(ThreadHandle::Kind kind) {
370     switch (kind) {
371       case ThreadHandle::SELF: thread_ = pthread_self(); break;
372       case ThreadHandle::INVALID: thread_ = kNoThread; break;
373     }
374   }
375   pthread_t thread_;  // Thread handle for pthread.
376 };
377 
378 
ThreadHandle(Kind kind)379 ThreadHandle::ThreadHandle(Kind kind) {
380   data_ = new PlatformData(kind);
381 }
382 
383 
Initialize(ThreadHandle::Kind kind)384 void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
385   data_->Initialize(kind);
386 }
387 
388 
~ThreadHandle()389 ThreadHandle::~ThreadHandle() {
390   delete data_;
391 }
392 
393 
IsSelf() const394 bool ThreadHandle::IsSelf() const {
395   return pthread_equal(data_->thread_, pthread_self());
396 }
397 
398 
IsValid() const399 bool ThreadHandle::IsValid() const {
400   return data_->thread_ != kNoThread;
401 }
402 
403 
Thread()404 Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
405 }
406 
407 
~Thread()408 Thread::~Thread() {
409 }
410 
411 
ThreadEntry(void * arg)412 static void* ThreadEntry(void* arg) {
413   Thread* thread = reinterpret_cast<Thread*>(arg);
414   // This is also initialized by the first argument to pthread_create() but we
415   // don't know which thread will run first (the original thread or the new
416   // one) so we initialize it here too.
417   thread->thread_handle_data()->thread_ = pthread_self();
418   ASSERT(thread->IsValid());
419   thread->Run();
420   return NULL;
421 }
422 
423 
Start()424 void Thread::Start() {
425   pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
426   ASSERT(IsValid());
427 }
428 
429 
Join()430 void Thread::Join() {
431   pthread_join(thread_handle_data()->thread_, NULL);
432 }
433 
434 
CreateThreadLocalKey()435 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
436   pthread_key_t key;
437   int result = pthread_key_create(&key, NULL);
438   USE(result);
439   ASSERT(result == 0);
440   return static_cast<LocalStorageKey>(key);
441 }
442 
443 
DeleteThreadLocalKey(LocalStorageKey key)444 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
445   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
446   int result = pthread_key_delete(pthread_key);
447   USE(result);
448   ASSERT(result == 0);
449 }
450 
451 
GetThreadLocal(LocalStorageKey key)452 void* Thread::GetThreadLocal(LocalStorageKey key) {
453   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
454   return pthread_getspecific(pthread_key);
455 }
456 
457 
SetThreadLocal(LocalStorageKey key,void * value)458 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
459   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
460   pthread_setspecific(pthread_key, value);
461 }
462 
463 
YieldCPU()464 void Thread::YieldCPU() {
465   sched_yield();
466 }
467 
468 
469 class FreeBSDMutex : public Mutex {
470  public:
471 
FreeBSDMutex()472   FreeBSDMutex() {
473     pthread_mutexattr_t attrs;
474     int result = pthread_mutexattr_init(&attrs);
475     ASSERT(result == 0);
476     result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
477     ASSERT(result == 0);
478     result = pthread_mutex_init(&mutex_, &attrs);
479     ASSERT(result == 0);
480   }
481 
~FreeBSDMutex()482   virtual ~FreeBSDMutex() { pthread_mutex_destroy(&mutex_); }
483 
Lock()484   virtual int Lock() {
485     int result = pthread_mutex_lock(&mutex_);
486     return result;
487   }
488 
Unlock()489   virtual int Unlock() {
490     int result = pthread_mutex_unlock(&mutex_);
491     return result;
492   }
493 
494  private:
495   pthread_mutex_t mutex_;   // Pthread mutex for POSIX platforms.
496 };
497 
498 
CreateMutex()499 Mutex* OS::CreateMutex() {
500   return new FreeBSDMutex();
501 }
502 
503 
504 class FreeBSDSemaphore : public Semaphore {
505  public:
FreeBSDSemaphore(int count)506   explicit FreeBSDSemaphore(int count) {  sem_init(&sem_, 0, count); }
~FreeBSDSemaphore()507   virtual ~FreeBSDSemaphore() { sem_destroy(&sem_); }
508 
509   virtual void Wait();
510   virtual bool Wait(int timeout);
Signal()511   virtual void Signal() { sem_post(&sem_); }
512  private:
513   sem_t sem_;
514 };
515 
516 
Wait()517 void FreeBSDSemaphore::Wait() {
518   while (true) {
519     int result = sem_wait(&sem_);
520     if (result == 0) return;  // Successfully got semaphore.
521     CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
522   }
523 }
524 
525 
Wait(int timeout)526 bool FreeBSDSemaphore::Wait(int timeout) {
527   const long kOneSecondMicros = 1000000;  // NOLINT
528 
529   // Split timeout into second and nanosecond parts.
530   struct timeval delta;
531   delta.tv_usec = timeout % kOneSecondMicros;
532   delta.tv_sec = timeout / kOneSecondMicros;
533 
534   struct timeval current_time;
535   // Get the current time.
536   if (gettimeofday(&current_time, NULL) == -1) {
537     return false;
538   }
539 
540   // Calculate time for end of timeout.
541   struct timeval end_time;
542   timeradd(&current_time, &delta, &end_time);
543 
544   struct timespec ts;
545   TIMEVAL_TO_TIMESPEC(&end_time, &ts);
546   while (true) {
547     int result = sem_timedwait(&sem_, &ts);
548     if (result == 0) return true;  // Successfully got semaphore.
549     if (result == -1 && errno == ETIMEDOUT) return false;  // Timeout.
550     CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
551   }
552 }
553 
554 
CreateSemaphore(int count)555 Semaphore* OS::CreateSemaphore(int count) {
556   return new FreeBSDSemaphore(count);
557 }
558 
559 
560 #ifdef ENABLE_LOGGING_AND_PROFILING
561 
562 static Sampler* active_sampler_ = NULL;
563 
ProfilerSignalHandler(int signal,siginfo_t * info,void * context)564 static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
565   USE(info);
566   if (signal != SIGPROF) return;
567   if (active_sampler_ == NULL) return;
568 
569   TickSample sample;
570 
571   // If profiling, we extract the current pc and sp.
572   if (active_sampler_->IsProfiling()) {
573     // Extracting the sample from the context is extremely machine dependent.
574     ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
575     mcontext_t& mcontext = ucontext->uc_mcontext;
576 #if V8_HOST_ARCH_IA32
577     sample.pc = reinterpret_cast<Address>(mcontext.mc_eip);
578     sample.sp = reinterpret_cast<Address>(mcontext.mc_esp);
579     sample.fp = reinterpret_cast<Address>(mcontext.mc_ebp);
580 #elif V8_HOST_ARCH_X64
581     sample.pc = reinterpret_cast<Address>(mcontext.mc_rip);
582     sample.sp = reinterpret_cast<Address>(mcontext.mc_rsp);
583     sample.fp = reinterpret_cast<Address>(mcontext.mc_rbp);
584 #elif V8_HOST_ARCH_ARM
585     sample.pc = reinterpret_cast<Address>(mcontext.mc_r15);
586     sample.sp = reinterpret_cast<Address>(mcontext.mc_r13);
587     sample.fp = reinterpret_cast<Address>(mcontext.mc_r11);
588 #endif
589     active_sampler_->SampleStack(&sample);
590   }
591 
592   // We always sample the VM state.
593   sample.state = Logger::state();
594 
595   active_sampler_->Tick(&sample);
596 }
597 
598 
599 class Sampler::PlatformData : public Malloced {
600  public:
PlatformData()601   PlatformData() {
602     signal_handler_installed_ = false;
603   }
604 
605   bool signal_handler_installed_;
606   struct sigaction old_signal_handler_;
607   struct itimerval old_timer_value_;
608 };
609 
610 
Sampler(int interval,bool profiling)611 Sampler::Sampler(int interval, bool profiling)
612     : interval_(interval), profiling_(profiling), active_(false) {
613   data_ = new PlatformData();
614 }
615 
616 
~Sampler()617 Sampler::~Sampler() {
618   delete data_;
619 }
620 
621 
Start()622 void Sampler::Start() {
623   // There can only be one active sampler at the time on POSIX
624   // platforms.
625   if (active_sampler_ != NULL) return;
626 
627   // Request profiling signals.
628   struct sigaction sa;
629   sa.sa_sigaction = ProfilerSignalHandler;
630   sigemptyset(&sa.sa_mask);
631   sa.sa_flags = SA_SIGINFO;
632   if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
633   data_->signal_handler_installed_ = true;
634 
635   // Set the itimer to generate a tick for each interval.
636   itimerval itimer;
637   itimer.it_interval.tv_sec = interval_ / 1000;
638   itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
639   itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
640   itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
641   setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
642 
643   // Set this sampler as the active sampler.
644   active_sampler_ = this;
645   active_ = true;
646 }
647 
648 
Stop()649 void Sampler::Stop() {
650   // Restore old signal handler
651   if (data_->signal_handler_installed_) {
652     setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
653     sigaction(SIGPROF, &data_->old_signal_handler_, 0);
654     data_->signal_handler_installed_ = false;
655   }
656 
657   // This sampler is no longer the active sampler.
658   active_sampler_ = NULL;
659   active_ = false;
660 }
661 
662 #endif  // ENABLE_LOGGING_AND_PROFILING
663 
664 } }  // namespace v8::internal
665