Home | History | Annotate | Download | only in src
      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 
     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 
     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 
     87 uint64_t OS::CpuFeaturesImpliedByPlatform() {
     88   return 0;  // FreeBSD runs on anything.
     89 }
     90 
     91 
     92 int OS::ActivationFrameAlignment() {
     93   // 16 byte alignment on FreeBSD
     94   return 16;
     95 }
     96 
     97 
     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 
    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 
    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 
    133 bool OS::IsOutsideAllocatedSpace(void* address) {
    134   return address < lowest_ever_allocated || address >= highest_ever_allocated;
    135 }
    136 
    137 
    138 size_t OS::AllocateAlignment() {
    139   return getpagesize();
    140 }
    141 
    142 
    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 
    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 
    170 void OS::Protect(void* address, size_t size) {
    171   UNIMPLEMENTED();
    172 }
    173 
    174 
    175 void OS::Unprotect(void* address, size_t size, bool is_executable) {
    176   UNIMPLEMENTED();
    177 }
    178 
    179 #endif
    180 
    181 
    182 void OS::Sleep(int milliseconds) {
    183   unsigned int ms = static_cast<unsigned int>(milliseconds);
    184   usleep(1000 * ms);
    185 }
    186 
    187 
    188 void OS::Abort() {
    189   // Redirect to std abort to signal abnormal program termination.
    190   abort();
    191 }
    192 
    193 
    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:
    206   PosixMemoryMappedFile(FILE* file, void* memory, int size)
    207     : file_(file), memory_(memory), size_(size) { }
    208   virtual ~PosixMemoryMappedFile();
    209   virtual void* memory() { return memory_; }
    210  private:
    211   FILE* file_;
    212   void* memory_;
    213   int size_;
    214 };
    215 
    216 
    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 
    232 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
    233   if (memory_) munmap(memory_, size_);
    234   fclose(file_);
    235 }
    236 
    237 
    238 #ifdef ENABLE_LOGGING_AND_PROFILING
    239 static unsigned StringToLong(char* buffer) {
    240   return static_cast<unsigned>(strtol(buffer, NULL, 16));  // NOLINT
    241 }
    242 #endif
    243 
    244 
    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 
    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 
    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 
    331 VirtualMemory::~VirtualMemory() {
    332   if (IsReserved()) {
    333     if (0 == munmap(address(), size())) address_ = MAP_FAILED;
    334   }
    335 }
    336 
    337 
    338 bool VirtualMemory::IsReserved() {
    339   return address_ != MAP_FAILED;
    340 }
    341 
    342 
    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 
    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:
    365   explicit PlatformData(ThreadHandle::Kind kind) {
    366     Initialize(kind);
    367   }
    368 
    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 
    379 ThreadHandle::ThreadHandle(Kind kind) {
    380   data_ = new PlatformData(kind);
    381 }
    382 
    383 
    384 void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
    385   data_->Initialize(kind);
    386 }
    387 
    388 
    389 ThreadHandle::~ThreadHandle() {
    390   delete data_;
    391 }
    392 
    393 
    394 bool ThreadHandle::IsSelf() const {
    395   return pthread_equal(data_->thread_, pthread_self());
    396 }
    397 
    398 
    399 bool ThreadHandle::IsValid() const {
    400   return data_->thread_ != kNoThread;
    401 }
    402 
    403 
    404 Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
    405 }
    406 
    407 
    408 Thread::~Thread() {
    409 }
    410 
    411 
    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 
    424 void Thread::Start() {
    425   pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
    426   ASSERT(IsValid());
    427 }
    428 
    429 
    430 void Thread::Join() {
    431   pthread_join(thread_handle_data()->thread_, NULL);
    432 }
    433 
    434 
    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 
    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 
    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 
    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 
    464 void Thread::YieldCPU() {
    465   sched_yield();
    466 }
    467 
    468 
    469 class FreeBSDMutex : public Mutex {
    470  public:
    471 
    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 
    482   virtual ~FreeBSDMutex() { pthread_mutex_destroy(&mutex_); }
    483 
    484   virtual int Lock() {
    485     int result = pthread_mutex_lock(&mutex_);
    486     return result;
    487   }
    488 
    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 
    499 Mutex* OS::CreateMutex() {
    500   return new FreeBSDMutex();
    501 }
    502 
    503 
    504 class FreeBSDSemaphore : public Semaphore {
    505  public:
    506   explicit FreeBSDSemaphore(int count) {  sem_init(&sem_, 0, count); }
    507   virtual ~FreeBSDSemaphore() { sem_destroy(&sem_); }
    508 
    509   virtual void Wait();
    510   virtual bool Wait(int timeout);
    511   virtual void Signal() { sem_post(&sem_); }
    512  private:
    513   sem_t sem_;
    514 };
    515 
    516 
    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 
    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 
    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 
    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:
    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 
    611 Sampler::Sampler(int interval, bool profiling)
    612     : interval_(interval), profiling_(profiling), active_(false) {
    613   data_ = new PlatformData();
    614 }
    615 
    616 
    617 Sampler::~Sampler() {
    618   delete data_;
    619 }
    620 
    621 
    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 
    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