OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | |
Vyacheslav Egorov (Chromium)
2011/02/17 14:00:48
2011
| |
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 Cygwin goes here. For the POSIX comaptible parts | |
29 // the implementation is in platform-posix.cc. | |
30 | |
31 #include <errno.h> | |
32 #include <pthread.h> | |
33 #include <semaphore.h> | |
34 #include <stdarg.h> | |
35 #include <strings.h> // index | |
36 #include <sys/time.h> | |
37 #include <sys/mman.h> // mmap & munmap | |
38 #include <unistd.h> // sysconf | |
39 | |
40 #undef MAP_TYPE | |
41 | |
42 #include "v8.h" | |
43 | |
44 #include "platform.h" | |
45 #include "top.h" | |
46 #include "v8threads.h" | |
47 #include "vm-state-inl.h" | |
48 #include "win32-headers.h" | |
49 | |
50 namespace v8 { | |
51 namespace internal { | |
52 | |
53 // 0 is never a valid thread id | |
54 static const pthread_t kNoThread = (pthread_t) 0; | |
55 | |
56 | |
57 double ceiling(double x) { | |
58 return ceil(x); | |
59 } | |
60 | |
61 | |
62 void OS::Setup() { | |
63 // Seed the random number generator. | |
64 // Convert the current time to a 64-bit integer first, before converting it | |
65 // to an unsigned. Going directly can cause an overflow and the seed to be | |
66 // set to all ones. The seed will be identical for different instances that | |
67 // call this setup code within the same millisecond. | |
68 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); | |
69 srandom(static_cast<unsigned int>(seed)); | |
70 } | |
71 | |
72 | |
73 uint64_t OS::CpuFeaturesImpliedByPlatform() { | |
74 return 0; // Nothing special about cygwin | |
Vyacheslav Egorov (Chromium)
2011/02/17 14:00:48
dot at the end of the comment.
cygwin -> Cygwin
| |
75 } | |
76 | |
77 | |
78 int OS::ActivationFrameAlignment() { | |
79 // With gcc 4.4 the tree vectorization optimizer can generate code | |
80 // that requires 16 byte alignment such as movdqa on x86. | |
81 return 16; | |
82 } | |
83 | |
84 | |
85 void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { | |
86 __asm__ __volatile__("" : : : "memory"); | |
87 // An x86 store acts as a release barrier. | |
88 *ptr = value; | |
89 } | |
90 | |
91 const char* OS::LocalTimezone(double time) { | |
92 if (isnan(time)) return ""; | |
93 time_t tv = static_cast<time_t>(floor(time/msPerSecond)); | |
94 struct tm* t = localtime(&tv); | |
95 if (NULL == t) return ""; | |
96 return tzname[0]; // The location of the timezone string on Cygwin. | |
97 } | |
98 | |
99 | |
100 double OS::LocalTimeOffset() { | |
101 // On Cygwin, struct tm does not contain a tm_gmtoff field. | |
102 time_t utc = time(NULL); | |
103 ASSERT(utc != -1); | |
104 struct tm* loc = localtime(&utc); | |
105 ASSERT(loc != NULL); | |
106 // time - localtime includes any daylight savings offset, so subtract it. | |
107 return static_cast<double>((mktime(loc) - utc) * msPerSecond - | |
108 (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0)); | |
109 } | |
110 | |
111 | |
112 // We keep the lowest and highest addresses mapped as a quick way of | |
113 // determining that pointers are outside the heap (used mostly in assertions | |
114 // and verification). The estimate is conservative, ie, not all addresses in | |
115 // 'allocated' space are actually allocated to our heap. The range is | |
116 // [lowest, highest), inclusive on the low and and exclusive on the high end. | |
117 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); | |
118 static void* highest_ever_allocated = reinterpret_cast<void*>(0); | |
119 | |
120 | |
121 static void UpdateAllocatedSpaceLimits(void* address, int size) { | |
122 lowest_ever_allocated = Min(lowest_ever_allocated, address); | |
123 highest_ever_allocated = | |
124 Max(highest_ever_allocated, | |
125 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); | |
126 } | |
127 | |
128 | |
129 bool OS::IsOutsideAllocatedSpace(void* address) { | |
130 return address < lowest_ever_allocated || address >= highest_ever_allocated; | |
131 } | |
132 | |
133 | |
134 size_t OS::AllocateAlignment() { | |
135 return sysconf(_SC_PAGESIZE); | |
136 } | |
137 | |
138 | |
139 void* OS::Allocate(const size_t requested, | |
140 size_t* allocated, | |
141 bool is_executable) { | |
142 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); | |
143 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); | |
144 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
145 if (mbase == MAP_FAILED) { | |
146 LOG(StringEvent("OS::Allocate", "mmap failed")); | |
147 return NULL; | |
148 } | |
149 *allocated = msize; | |
150 UpdateAllocatedSpaceLimits(mbase, msize); | |
151 return mbase; | |
152 } | |
153 | |
154 | |
155 void OS::Free(void* address, const size_t size) { | |
156 // TODO(1240712): munmap has a return value which is ignored here. | |
157 int result = munmap(address, size); | |
158 USE(result); | |
159 ASSERT(result == 0); | |
160 } | |
161 | |
162 | |
163 #ifdef ENABLE_HEAP_PROTECTION | |
164 | |
165 void OS::Protect(void* address, size_t size) { | |
166 // TODO(1240712): mprotect has a return value which is ignored here. | |
167 mprotect(address, size, PROT_READ); | |
168 } | |
169 | |
170 | |
171 void OS::Unprotect(void* address, size_t size, bool is_executable) { | |
172 // TODO(1240712): mprotect has a return value which is ignored here. | |
173 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); | |
174 mprotect(address, size, prot); | |
175 } | |
176 | |
177 #endif | |
178 | |
179 | |
180 void OS::Sleep(int milliseconds) { | |
181 unsigned int ms = static_cast<unsigned int>(milliseconds); | |
182 usleep(1000 * ms); | |
183 } | |
184 | |
185 | |
186 void OS::Abort() { | |
187 // Redirect to std abort to signal abnormal program termination. | |
188 abort(); | |
189 } | |
190 | |
191 | |
192 void OS::DebugBreak() { | |
193 asm("int $3"); | |
194 } | |
195 | |
196 | |
197 class PosixMemoryMappedFile : public OS::MemoryMappedFile { | |
198 public: | |
199 PosixMemoryMappedFile(FILE* file, void* memory, int size) | |
200 : file_(file), memory_(memory), size_(size) { } | |
201 virtual ~PosixMemoryMappedFile(); | |
202 virtual void* memory() { return memory_; } | |
203 virtual int size() { return size_; } | |
204 private: | |
205 FILE* file_; | |
206 void* memory_; | |
207 int size_; | |
208 }; | |
209 | |
210 | |
211 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { | |
212 FILE* file = fopen(name, "w+"); | |
213 if (file == NULL) return NULL; | |
214 | |
215 fseek(file, 0, SEEK_END); | |
216 int size = ftell(file); | |
217 | |
218 void* memory = | |
219 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); | |
220 return new PosixMemoryMappedFile(file, memory, size); | |
221 } | |
222 | |
223 | |
224 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, | |
225 void* initial) { | |
226 FILE* file = fopen(name, "w+"); | |
227 if (file == NULL) return NULL; | |
228 int result = fwrite(initial, size, 1, file); | |
229 if (result < 1) { | |
230 fclose(file); | |
231 return NULL; | |
232 } | |
233 void* memory = | |
234 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); | |
235 return new PosixMemoryMappedFile(file, memory, size); | |
236 } | |
237 | |
238 | |
239 PosixMemoryMappedFile::~PosixMemoryMappedFile() { | |
240 if (memory_) munmap(memory_, size_); | |
241 fclose(file_); | |
242 } | |
243 | |
244 | |
245 void OS::LogSharedLibraryAddresses() { | |
246 #ifdef ENABLE_LOGGING_AND_PROFILING | |
247 // This function assumes that the layout of the file is as follows: | |
248 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] | |
249 // If we encounter an unexpected situation we abort scanning further entries. | |
250 FILE* fp = fopen("/proc/self/maps", "r"); | |
251 if (fp == NULL) return; | |
252 | |
253 // Allocate enough room to be able to store a full file name. | |
254 const int kLibNameLen = FILENAME_MAX + 1; | |
255 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); | |
256 | |
257 // This loop will terminate once the scanning hits an EOF. | |
258 while (true) { | |
259 uintptr_t start, end; | |
260 char attr_r, attr_w, attr_x, attr_p; | |
261 // Parse the addresses and permission bits at the beginning of the line. | |
262 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; | |
263 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; | |
264 | |
265 int c; | |
266 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { | |
267 // Found a read-only executable entry. Skip characters until we reach | |
268 // the beginning of the filename or the end of the line. | |
269 do { | |
270 c = getc(fp); | |
271 } while ((c != EOF) && (c != '\n') && (c != '/')); | |
272 if (c == EOF) break; // EOF: Was unexpected, just exit. | |
273 | |
274 // Process the filename if found. | |
275 if (c == '/') { | |
276 ungetc(c, fp); // Push the '/' back into the stream to be read below. | |
277 | |
278 // Read to the end of the line. Exit if the read fails. | |
279 if (fgets(lib_name, kLibNameLen, fp) == NULL) break; | |
280 | |
281 // Drop the newline character read by fgets. We do not need to check | |
282 // for a zero-length string because we know that we at least read the | |
283 // '/' character. | |
284 lib_name[strlen(lib_name) - 1] = '\0'; | |
285 } else { | |
286 // No library name found, just record the raw address range. | |
287 snprintf(lib_name, kLibNameLen, | |
288 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); | |
289 } | |
290 LOG(SharedLibraryEvent(lib_name, start, end)); | |
291 } else { | |
292 // Entry not describing executable data. Skip to end of line to setup | |
293 // reading the next entry. | |
294 do { | |
295 c = getc(fp); | |
296 } while ((c != EOF) && (c != '\n')); | |
297 if (c == EOF) break; | |
298 } | |
299 } | |
300 free(lib_name); | |
301 fclose(fp); | |
302 #endif | |
303 } | |
304 | |
305 | |
306 void OS::SignalCodeMovingGC() { | |
307 // Nothing to do on Cygwin | |
Vyacheslav Egorov (Chromium)
2011/02/17 14:00:48
dot at the end of the comment.
| |
308 } | |
309 | |
310 | |
311 int OS::StackWalk(Vector<OS::StackFrame> frames) { | |
312 // Not supported on Cygwin | |
Vyacheslav Egorov (Chromium)
2011/02/17 14:00:48
ditto
| |
313 return 0; | |
314 } | |
315 | |
316 | |
317 // Constants used for mmap. | |
318 static const int kMmapFd = -1; | |
319 static const int kMmapFdOffset = 0; | |
320 | |
321 | |
322 VirtualMemory::VirtualMemory(size_t size) { | |
323 address_ = mmap(NULL, size, PROT_NONE, | |
324 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, | |
325 kMmapFd, kMmapFdOffset); | |
326 size_ = size; | |
327 } | |
328 | |
329 | |
330 VirtualMemory::~VirtualMemory() { | |
331 if (IsReserved()) { | |
332 if (0 == munmap(address(), size())) address_ = MAP_FAILED; | |
333 } | |
334 } | |
335 | |
336 | |
337 bool VirtualMemory::IsReserved() { | |
338 return address_ != MAP_FAILED; | |
339 } | |
340 | |
341 | |
342 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { | |
343 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); | |
344 | |
345 if (mprotect(address, size, prot) != 0) { | |
346 return false; | |
347 } | |
348 | |
349 UpdateAllocatedSpaceLimits(address, size); | |
350 return true; | |
351 } | |
352 | |
353 | |
354 bool VirtualMemory::Uncommit(void* address, size_t size) { | |
355 return mmap(address, size, PROT_NONE, | |
356 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, | |
357 kMmapFd, kMmapFdOffset) != MAP_FAILED; | |
358 } | |
359 | |
360 | |
361 class ThreadHandle::PlatformData : public Malloced { | |
362 public: | |
363 explicit PlatformData(ThreadHandle::Kind kind) { | |
364 Initialize(kind); | |
365 } | |
366 | |
367 void Initialize(ThreadHandle::Kind kind) { | |
368 switch (kind) { | |
369 case ThreadHandle::SELF: thread_ = pthread_self(); break; | |
370 case ThreadHandle::INVALID: thread_ = kNoThread; break; | |
371 } | |
372 } | |
373 | |
374 pthread_t thread_; // Thread handle for pthread. | |
375 }; | |
376 | |
377 | |
378 ThreadHandle::ThreadHandle(Kind kind) { | |
379 data_ = new PlatformData(kind); | |
380 } | |
381 | |
382 | |
383 void ThreadHandle::Initialize(ThreadHandle::Kind kind) { | |
384 data_->Initialize(kind); | |
385 } | |
386 | |
387 | |
388 ThreadHandle::~ThreadHandle() { | |
389 delete data_; | |
390 } | |
391 | |
392 | |
393 bool ThreadHandle::IsSelf() const { | |
394 return pthread_equal(data_->thread_, pthread_self()); | |
395 } | |
396 | |
397 | |
398 bool ThreadHandle::IsValid() const { | |
399 return data_->thread_ != kNoThread; | |
400 } | |
401 | |
402 | |
403 Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) { | |
404 set_name("v8:<unknown>"); | |
405 } | |
406 | |
407 | |
408 Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) { | |
409 set_name(name); | |
410 } | |
411 | |
412 | |
413 Thread::~Thread() { | |
414 } | |
415 | |
416 | |
417 static void* ThreadEntry(void* arg) { | |
418 Thread* thread = reinterpret_cast<Thread*>(arg); | |
419 // This is also initialized by the first argument to pthread_create() but we | |
420 // don't know which thread will run first (the original thread or the new | |
421 // one) so we initialize it here too. | |
422 thread->thread_handle_data()->thread_ = pthread_self(); | |
423 ASSERT(thread->IsValid()); | |
424 thread->Run(); | |
425 return NULL; | |
426 } | |
427 | |
428 | |
429 void Thread::set_name(const char* name) { | |
430 strncpy(name_, name, sizeof(name_)); | |
431 name_[sizeof(name_) - 1] = '\0'; | |
432 } | |
433 | |
434 | |
435 void Thread::Start() { | |
436 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this); | |
437 ASSERT(IsValid()); | |
438 } | |
439 | |
440 | |
441 void Thread::Join() { | |
442 pthread_join(thread_handle_data()->thread_, NULL); | |
443 } | |
444 | |
445 | |
446 Thread::LocalStorageKey Thread::CreateThreadLocalKey() { | |
447 pthread_key_t key; | |
448 int result = pthread_key_create(&key, NULL); | |
449 USE(result); | |
450 ASSERT(result == 0); | |
451 return static_cast<LocalStorageKey>(key); | |
452 } | |
453 | |
454 | |
455 void Thread::DeleteThreadLocalKey(LocalStorageKey key) { | |
456 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); | |
457 int result = pthread_key_delete(pthread_key); | |
458 USE(result); | |
459 ASSERT(result == 0); | |
460 } | |
461 | |
462 | |
463 void* Thread::GetThreadLocal(LocalStorageKey key) { | |
464 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); | |
465 return pthread_getspecific(pthread_key); | |
466 } | |
467 | |
468 | |
469 void Thread::SetThreadLocal(LocalStorageKey key, void* value) { | |
470 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); | |
471 pthread_setspecific(pthread_key, value); | |
472 } | |
473 | |
474 | |
475 void Thread::YieldCPU() { | |
476 sched_yield(); | |
477 } | |
478 | |
479 | |
480 class CygwinMutex : public Mutex { | |
481 public: | |
482 | |
483 CygwinMutex() { | |
484 pthread_mutexattr_t attrs; | |
485 memset(&attrs, 0, sizeof(attrs)); | |
486 | |
487 int result = pthread_mutexattr_init(&attrs); | |
488 ASSERT(result == 0); | |
489 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE); | |
490 ASSERT(result == 0); | |
491 result = pthread_mutex_init(&mutex_, &attrs); | |
492 ASSERT(result == 0); | |
493 } | |
494 | |
495 virtual ~CygwinMutex() { pthread_mutex_destroy(&mutex_); } | |
496 | |
497 virtual int Lock() { | |
498 int result = pthread_mutex_lock(&mutex_); | |
499 return result; | |
500 } | |
501 | |
502 virtual int Unlock() { | |
503 int result = pthread_mutex_unlock(&mutex_); | |
504 return result; | |
505 } | |
506 | |
507 virtual bool TryLock() { | |
508 int result = pthread_mutex_trylock(&mutex_); | |
509 // Return false if the lock is busy and locking failed. | |
510 if (result == EBUSY) { | |
511 return false; | |
512 } | |
513 ASSERT(result == 0); // Verify no other errors. | |
514 return true; | |
515 } | |
516 | |
517 private: | |
518 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms. | |
519 }; | |
520 | |
521 | |
522 Mutex* OS::CreateMutex() { | |
523 return new CygwinMutex(); | |
524 } | |
525 | |
526 | |
527 class CygwinSemaphore : public Semaphore { | |
528 public: | |
529 explicit CygwinSemaphore(int count) { sem_init(&sem_, 0, count); } | |
530 virtual ~CygwinSemaphore() { sem_destroy(&sem_); } | |
531 | |
532 virtual void Wait(); | |
533 virtual bool Wait(int timeout); | |
534 virtual void Signal() { sem_post(&sem_); } | |
535 private: | |
536 sem_t sem_; | |
537 }; | |
538 | |
539 | |
540 void CygwinSemaphore::Wait() { | |
541 while (true) { | |
542 int result = sem_wait(&sem_); | |
543 if (result == 0) return; // Successfully got semaphore. | |
544 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. | |
545 } | |
546 } | |
547 | |
548 | |
549 #ifndef TIMEVAL_TO_TIMESPEC | |
550 #define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ | |
551 (ts)->tv_sec = (tv)->tv_sec; \ | |
552 (ts)->tv_nsec = (tv)->tv_usec * 1000; \ | |
553 } while (false) | |
554 #endif | |
555 | |
556 | |
557 bool CygwinSemaphore::Wait(int timeout) { | |
558 const long kOneSecondMicros = 1000000; // NOLINT | |
559 | |
560 // Split timeout into second and nanosecond parts. | |
561 struct timeval delta; | |
562 delta.tv_usec = timeout % kOneSecondMicros; | |
563 delta.tv_sec = timeout / kOneSecondMicros; | |
564 | |
565 struct timeval current_time; | |
566 // Get the current time. | |
567 if (gettimeofday(¤t_time, NULL) == -1) { | |
568 return false; | |
569 } | |
570 | |
571 // Calculate time for end of timeout. | |
572 struct timeval end_time; | |
573 timeradd(¤t_time, &delta, &end_time); | |
574 | |
575 struct timespec ts; | |
576 TIMEVAL_TO_TIMESPEC(&end_time, &ts); | |
577 // Wait for semaphore signalled or timeout. | |
578 while (true) { | |
579 int result = sem_timedwait(&sem_, &ts); | |
580 if (result == 0) return true; // Successfully got semaphore. | |
581 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout. | |
582 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. | |
583 } | |
584 } | |
585 | |
586 | |
587 Semaphore* OS::CreateSemaphore(int count) { | |
588 return new CygwinSemaphore(count); | |
589 } | |
590 | |
591 | |
592 #ifdef ENABLE_LOGGING_AND_PROFILING | |
593 | |
594 // ---------------------------------------------------------------------------- | |
595 // Cygwin profiler support. | |
596 // | |
597 // On cygwin we use the same sampler implementation as on win32 | |
Vyacheslav Egorov (Chromium)
2011/02/17 14:00:48
cygwin -> Cygwin
dot at the end.
| |
598 | |
599 class Sampler::PlatformData : public Malloced { | |
600 public: | |
601 explicit PlatformData(Sampler* sampler) { | |
602 sampler_ = sampler; | |
603 sampler_thread_ = INVALID_HANDLE_VALUE; | |
604 profiled_thread_ = INVALID_HANDLE_VALUE; | |
605 } | |
606 | |
607 Sampler* sampler_; | |
608 HANDLE sampler_thread_; | |
609 HANDLE profiled_thread_; | |
610 RuntimeProfilerRateLimiter rate_limiter_; | |
611 | |
612 // Sampler thread handler. | |
613 void Runner() { | |
614 while (sampler_->IsActive()) { | |
615 if (rate_limiter_.SuspendIfNecessary()) continue; | |
616 Sample(); | |
617 Sleep(sampler_->interval_); | |
618 } | |
619 } | |
620 | |
621 void Sample() { | |
622 if (sampler_->IsProfiling()) { | |
623 // Context used for sampling the register state of the profiled thread. | |
624 CONTEXT context; | |
625 memset(&context, 0, sizeof(context)); | |
626 | |
627 TickSample sample_obj; | |
628 TickSample* sample = CpuProfiler::TickSampleEvent(); | |
629 if (sample == NULL) sample = &sample_obj; | |
630 | |
631 static const DWORD kSuspendFailed = static_cast<DWORD>(-1); | |
632 if (SuspendThread(profiled_thread_) == kSuspendFailed) return; | |
633 sample->state = Top::current_vm_state(); | |
634 | |
635 context.ContextFlags = CONTEXT_FULL; | |
636 if (GetThreadContext(profiled_thread_, &context) != 0) { | |
637 #if V8_HOST_ARCH_X64 | |
638 sample->pc = reinterpret_cast<Address>(context.Rip); | |
639 sample->sp = reinterpret_cast<Address>(context.Rsp); | |
640 sample->fp = reinterpret_cast<Address>(context.Rbp); | |
641 #else | |
642 sample->pc = reinterpret_cast<Address>(context.Eip); | |
643 sample->sp = reinterpret_cast<Address>(context.Esp); | |
644 sample->fp = reinterpret_cast<Address>(context.Ebp); | |
645 #endif | |
646 sampler_->SampleStack(sample); | |
647 sampler_->Tick(sample); | |
648 } | |
649 ResumeThread(profiled_thread_); | |
650 } | |
651 if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick(); | |
652 } | |
653 }; | |
654 | |
655 | |
656 // Entry point for sampler thread. | |
657 static DWORD __stdcall SamplerEntry(void* arg) { | |
658 Sampler::PlatformData* data = | |
659 reinterpret_cast<Sampler::PlatformData*>(arg); | |
660 data->Runner(); | |
661 return 0; | |
662 } | |
663 | |
664 | |
665 // Initialize a profile sampler. | |
666 Sampler::Sampler(int interval) | |
667 : interval_(interval), | |
668 profiling_(false), | |
669 active_(false), | |
670 samples_taken_(0) { | |
671 data_ = new PlatformData(this); | |
672 } | |
673 | |
674 | |
675 Sampler::~Sampler() { | |
676 delete data_; | |
677 } | |
678 | |
679 | |
680 // Start profiling. | |
681 void Sampler::Start() { | |
682 // Do not start multiple threads for the same sampler. | |
683 ASSERT(!IsActive()); | |
684 | |
685 // Get a handle to the calling thread. This is the thread that we are | |
686 // going to profile. We need to make a copy of the handle because we are | |
687 // going to use it in the sampler thread. Using GetThreadHandle() will | |
688 // not work in this case. We're using OpenThread because DuplicateHandle | |
689 // for some reason doesn't work in Chrome's sandbox. | |
690 data_->profiled_thread_ = OpenThread(THREAD_GET_CONTEXT | | |
691 THREAD_SUSPEND_RESUME | | |
692 THREAD_QUERY_INFORMATION, | |
693 false, | |
694 GetCurrentThreadId()); | |
695 BOOL ok = data_->profiled_thread_ != NULL; | |
696 if (!ok) return; | |
697 | |
698 // Start sampler thread. | |
699 DWORD tid; | |
700 SetActive(true); | |
701 data_->sampler_thread_ = CreateThread(NULL, 0, SamplerEntry, data_, 0, | |
Vyacheslav Egorov (Chromium)
2011/02/17 14:00:48
one argument per line or the whole call on the nex
| |
702 &tid); | |
703 // Set thread to high priority to increase sampling accuracy. | |
704 SetThreadPriority(data_->sampler_thread_, THREAD_PRIORITY_TIME_CRITICAL); | |
705 } | |
706 | |
707 | |
708 // Stop profiling. | |
709 void Sampler::Stop() { | |
710 // Seting active to false triggers termination of the sampler | |
711 // thread. | |
712 SetActive(false); | |
713 | |
714 // Wait for sampler thread to terminate. | |
715 Top::WakeUpRuntimeProfilerThreadBeforeShutdown(); | |
716 WaitForSingleObject(data_->sampler_thread_, INFINITE); | |
717 | |
718 // Release the thread handles | |
719 CloseHandle(data_->sampler_thread_); | |
720 CloseHandle(data_->profiled_thread_); | |
721 } | |
722 | |
723 | |
724 #endif // ENABLE_LOGGING_AND_PROFILING | |
725 | |
726 } } // namespace v8::internal | |
727 | |
OLD | NEW |