1 | // Copyright 2012 the V8 project authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | // This module contains the platform-specific code. This make the rest of the |
6 | // code less dependent on operating system, compilers and runtime libraries. |
7 | // This module does specifically not deal with differences between different |
8 | // processor architecture. |
9 | // The platform classes have the same definition for all platforms. The |
10 | // implementation for a particular platform is put in platform_<os>.cc. |
11 | // The build system then uses the implementation for the target platform. |
12 | // |
13 | // This design has been chosen because it is simple and fast. Alternatively, |
14 | // the platform dependent classes could have been implemented using abstract |
15 | // superclasses with virtual methods and having specializations for each |
16 | // platform. This design was rejected because it was more complicated and |
17 | // slower. It would require factory methods for selecting the right |
18 | // implementation and the overhead of virtual methods for performance |
19 | // sensitive like mutex locking/unlocking. |
20 | |
21 | #ifndef V8_BASE_PLATFORM_PLATFORM_H_ |
22 | #define V8_BASE_PLATFORM_PLATFORM_H_ |
23 | |
24 | #include <cstdarg> |
25 | #include <string> |
26 | #include <vector> |
27 | |
28 | #include "src/base/base-export.h" |
29 | #include "src/base/build_config.h" |
30 | #include "src/base/compiler-specific.h" |
31 | #include "src/base/platform/mutex.h" |
32 | #include "src/base/platform/semaphore.h" |
33 | |
34 | #if V8_OS_QNX |
35 | #include "src/base/qnx-math.h" |
36 | #endif |
37 | |
38 | namespace v8 { |
39 | |
40 | namespace base { |
41 | |
42 | // ---------------------------------------------------------------------------- |
43 | // Fast TLS support |
44 | |
45 | #ifndef V8_NO_FAST_TLS |
46 | |
47 | #if V8_CC_MSVC && V8_HOST_ARCH_IA32 |
48 | |
49 | #define V8_FAST_TLS_SUPPORTED 1 |
50 | |
51 | V8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index); |
52 | |
53 | inline intptr_t InternalGetExistingThreadLocal(intptr_t index) { |
54 | const intptr_t kTibInlineTlsOffset = 0xE10; |
55 | const intptr_t kTibExtraTlsOffset = 0xF94; |
56 | const intptr_t kMaxInlineSlots = 64; |
57 | const intptr_t kMaxSlots = kMaxInlineSlots + 1024; |
58 | const intptr_t kSystemPointerSize = sizeof(void*); |
59 | DCHECK(0 <= index && index < kMaxSlots); |
60 | USE(kMaxSlots); |
61 | if (index < kMaxInlineSlots) { |
62 | return static_cast<intptr_t>( |
63 | __readfsdword(kTibInlineTlsOffset + kSystemPointerSize * index)); |
64 | } |
65 | intptr_t extra = static_cast<intptr_t>(__readfsdword(kTibExtraTlsOffset)); |
66 | DCHECK_NE(extra, 0); |
67 | return *reinterpret_cast<intptr_t*>(extra + kSystemPointerSize * |
68 | (index - kMaxInlineSlots)); |
69 | } |
70 | |
71 | #elif defined(__APPLE__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64) |
72 | |
73 | #define V8_FAST_TLS_SUPPORTED 1 |
74 | |
75 | extern V8_BASE_EXPORT intptr_t kMacTlsBaseOffset; |
76 | |
77 | V8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index); |
78 | |
79 | inline intptr_t InternalGetExistingThreadLocal(intptr_t index) { |
80 | intptr_t result; |
81 | #if V8_HOST_ARCH_IA32 |
82 | asm("movl %%gs:(%1,%2,4), %0;" |
83 | :"=r" (result) // Output must be a writable register. |
84 | :"r" (kMacTlsBaseOffset), "r" (index)); |
85 | #else |
86 | asm("movq %%gs:(%1,%2,8), %0;" |
87 | :"=r" (result) |
88 | :"r" (kMacTlsBaseOffset), "r" (index)); |
89 | #endif |
90 | return result; |
91 | } |
92 | |
93 | #endif |
94 | |
95 | #endif // V8_NO_FAST_TLS |
96 | |
97 | class PageAllocator; |
98 | class TimezoneCache; |
99 | |
100 | // ---------------------------------------------------------------------------- |
101 | // OS |
102 | // |
103 | // This class has static methods for the different platform specific |
104 | // functions. Add methods here to cope with differences between the |
105 | // supported platforms. |
106 | |
107 | class V8_BASE_EXPORT OS { |
108 | public: |
109 | // Initialize the OS class. |
110 | // - hard_abort: If true, OS::Abort() will crash instead of aborting. |
111 | // - gc_fake_mmap: Name of the file for fake gc mmap used in ll_prof. |
112 | static void Initialize(bool hard_abort, const char* const gc_fake_mmap); |
113 | |
114 | // Returns the accumulated user time for thread. This routine |
115 | // can be used for profiling. The implementation should |
116 | // strive for high-precision timer resolution, preferable |
117 | // micro-second resolution. |
118 | static int GetUserTime(uint32_t* secs, uint32_t* usecs); |
119 | |
120 | // Returns current time as the number of milliseconds since |
121 | // 00:00:00 UTC, January 1, 1970. |
122 | static double TimeCurrentMillis(); |
123 | |
124 | static TimezoneCache* CreateTimezoneCache(); |
125 | |
126 | // Returns last OS error. |
127 | static int GetLastError(); |
128 | |
129 | static FILE* FOpen(const char* path, const char* mode); |
130 | static bool Remove(const char* path); |
131 | |
132 | static char DirectorySeparator(); |
133 | static bool isDirectorySeparator(const char ch); |
134 | |
135 | // Opens a temporary file, the file is auto removed on close. |
136 | static FILE* OpenTemporaryFile(); |
137 | |
138 | // Log file open mode is platform-dependent due to line ends issues. |
139 | static const char* const LogFileOpenMode; |
140 | |
141 | // Print output to console. This is mostly used for debugging output. |
142 | // On platforms that has standard terminal output, the output |
143 | // should go to stdout. |
144 | static PRINTF_FORMAT(1, 2) void Print(const char* format, ...); |
145 | static PRINTF_FORMAT(1, 0) void VPrint(const char* format, va_list args); |
146 | |
147 | // Print output to a file. This is mostly used for debugging output. |
148 | static PRINTF_FORMAT(2, 3) void FPrint(FILE* out, const char* format, ...); |
149 | static PRINTF_FORMAT(2, 0) void VFPrint(FILE* out, const char* format, |
150 | va_list args); |
151 | |
152 | // Print error output to console. This is mostly used for error message |
153 | // output. On platforms that has standard terminal output, the output |
154 | // should go to stderr. |
155 | static PRINTF_FORMAT(1, 2) void PrintError(const char* format, ...); |
156 | static PRINTF_FORMAT(1, 0) void VPrintError(const char* format, va_list args); |
157 | |
158 | // Memory permissions. These should be kept in sync with the ones in |
159 | // v8::PageAllocator. |
160 | enum class MemoryPermission { |
161 | kNoAccess, |
162 | kRead, |
163 | kReadWrite, |
164 | // TODO(hpayer): Remove this flag. Memory should never be rwx. |
165 | kReadWriteExecute, |
166 | kReadExecute |
167 | }; |
168 | |
169 | static bool HasLazyCommits(); |
170 | |
171 | // Sleep for a specified time interval. |
172 | static void Sleep(TimeDelta interval); |
173 | |
174 | // Abort the current process. |
175 | [[noreturn]] static void Abort(); |
176 | |
177 | // Debug break. |
178 | static void DebugBreak(); |
179 | |
180 | // Walk the stack. |
181 | static const int kStackWalkError = -1; |
182 | static const int kStackWalkMaxNameLen = 256; |
183 | static const int kStackWalkMaxTextLen = 256; |
184 | struct StackFrame { |
185 | void* address; |
186 | char text[kStackWalkMaxTextLen]; |
187 | }; |
188 | |
189 | class V8_BASE_EXPORT MemoryMappedFile { |
190 | public: |
191 | enum class FileMode { kReadOnly, kReadWrite }; |
192 | |
193 | virtual ~MemoryMappedFile() = default; |
194 | virtual void* memory() const = 0; |
195 | virtual size_t size() const = 0; |
196 | |
197 | static MemoryMappedFile* open(const char* name, |
198 | FileMode mode = FileMode::kReadWrite); |
199 | static MemoryMappedFile* create(const char* name, size_t size, |
200 | void* initial); |
201 | }; |
202 | |
203 | // Safe formatting print. Ensures that str is always null-terminated. |
204 | // Returns the number of chars written, or -1 if output was truncated. |
205 | static PRINTF_FORMAT(3, 4) int SNPrintF(char* str, int length, |
206 | const char* format, ...); |
207 | static PRINTF_FORMAT(3, 0) int VSNPrintF(char* str, int length, |
208 | const char* format, va_list args); |
209 | |
210 | static char* StrChr(char* str, int c); |
211 | static void StrNCpy(char* dest, int length, const char* src, size_t n); |
212 | |
213 | // Support for the profiler. Can do nothing, in which case ticks |
214 | // occurring in shared libraries will not be properly accounted for. |
215 | struct SharedLibraryAddress { |
216 | SharedLibraryAddress(const std::string& library_path, uintptr_t start, |
217 | uintptr_t end) |
218 | : library_path(library_path), start(start), end(end), aslr_slide(0) {} |
219 | SharedLibraryAddress(const std::string& library_path, uintptr_t start, |
220 | uintptr_t end, intptr_t aslr_slide) |
221 | : library_path(library_path), |
222 | start(start), |
223 | end(end), |
224 | aslr_slide(aslr_slide) {} |
225 | |
226 | std::string library_path; |
227 | uintptr_t start; |
228 | uintptr_t end; |
229 | intptr_t aslr_slide; |
230 | }; |
231 | |
232 | static std::vector<SharedLibraryAddress> GetSharedLibraryAddresses(); |
233 | |
234 | // Support for the profiler. Notifies the external profiling |
235 | // process that a code moving garbage collection starts. Can do |
236 | // nothing, in which case the code objects must not move (e.g., by |
237 | // using --never-compact) if accurate profiling is desired. |
238 | static void SignalCodeMovingGC(); |
239 | |
240 | // Support runtime detection of whether the hard float option of the |
241 | // EABI is used. |
242 | static bool ArmUsingHardFloat(); |
243 | |
244 | // Returns the activation frame alignment constraint or zero if |
245 | // the platform doesn't care. Guaranteed to be a power of two. |
246 | static int ActivationFrameAlignment(); |
247 | |
248 | static int GetCurrentProcessId(); |
249 | |
250 | static int GetCurrentThreadId(); |
251 | |
252 | static void ExitProcess(int exit_code); |
253 | |
254 | private: |
255 | // These classes use the private memory management API below. |
256 | friend class MemoryMappedFile; |
257 | friend class PosixMemoryMappedFile; |
258 | friend class v8::base::PageAllocator; |
259 | |
260 | static size_t AllocatePageSize(); |
261 | |
262 | static size_t CommitPageSize(); |
263 | |
264 | static void SetRandomMmapSeed(int64_t seed); |
265 | |
266 | static void* GetRandomMmapAddr(); |
267 | |
268 | V8_WARN_UNUSED_RESULT static void* Allocate(void* address, size_t size, |
269 | size_t alignment, |
270 | MemoryPermission access); |
271 | |
272 | V8_WARN_UNUSED_RESULT static bool Free(void* address, const size_t size); |
273 | |
274 | V8_WARN_UNUSED_RESULT static bool Release(void* address, size_t size); |
275 | |
276 | V8_WARN_UNUSED_RESULT static bool SetPermissions(void* address, size_t size, |
277 | MemoryPermission access); |
278 | |
279 | V8_WARN_UNUSED_RESULT static bool DiscardSystemPages(void* address, |
280 | size_t size); |
281 | |
282 | static const int msPerSecond = 1000; |
283 | |
284 | #if V8_OS_POSIX |
285 | static const char* GetGCFakeMMapFile(); |
286 | #endif |
287 | |
288 | DISALLOW_IMPLICIT_CONSTRUCTORS(OS); |
289 | }; |
290 | |
291 | #if (defined(_WIN32) || defined(_WIN64)) |
292 | V8_BASE_EXPORT void EnsureConsoleOutputWin32(); |
293 | #endif // (defined(_WIN32) || defined(_WIN64)) |
294 | |
295 | inline void EnsureConsoleOutput() { |
296 | #if (defined(_WIN32) || defined(_WIN64)) |
297 | // Windows requires extra calls to send assert output to the console |
298 | // rather than a dialog box. |
299 | EnsureConsoleOutputWin32(); |
300 | #endif // (defined(_WIN32) || defined(_WIN64)) |
301 | } |
302 | |
303 | // ---------------------------------------------------------------------------- |
304 | // Thread |
305 | // |
306 | // Thread objects are used for creating and running threads. When the start() |
307 | // method is called the new thread starts running the run() method in the new |
308 | // thread. The Thread object should not be deallocated before the thread has |
309 | // terminated. |
310 | |
311 | class V8_BASE_EXPORT Thread { |
312 | public: |
313 | // Opaque data type for thread-local storage keys. |
314 | using LocalStorageKey = int32_t; |
315 | |
316 | class Options { |
317 | public: |
318 | Options() : name_("v8:<unknown>" ), stack_size_(0) {} |
319 | explicit Options(const char* name, int stack_size = 0) |
320 | : name_(name), stack_size_(stack_size) {} |
321 | |
322 | const char* name() const { return name_; } |
323 | int stack_size() const { return stack_size_; } |
324 | |
325 | private: |
326 | const char* name_; |
327 | int stack_size_; |
328 | }; |
329 | |
330 | // Create new thread. |
331 | explicit Thread(const Options& options); |
332 | virtual ~Thread(); |
333 | |
334 | // Start new thread by calling the Run() method on the new thread. |
335 | void Start(); |
336 | |
337 | // Start new thread and wait until Run() method is called on the new thread. |
338 | void StartSynchronously() { |
339 | start_semaphore_ = new Semaphore(0); |
340 | Start(); |
341 | start_semaphore_->Wait(); |
342 | delete start_semaphore_; |
343 | start_semaphore_ = nullptr; |
344 | } |
345 | |
346 | // Wait until thread terminates. |
347 | void Join(); |
348 | |
349 | inline const char* name() const { |
350 | return name_; |
351 | } |
352 | |
353 | // Abstract method for run handler. |
354 | virtual void Run() = 0; |
355 | |
356 | // Thread-local storage. |
357 | static LocalStorageKey CreateThreadLocalKey(); |
358 | static void DeleteThreadLocalKey(LocalStorageKey key); |
359 | static void* GetThreadLocal(LocalStorageKey key); |
360 | static int GetThreadLocalInt(LocalStorageKey key) { |
361 | return static_cast<int>(reinterpret_cast<intptr_t>(GetThreadLocal(key))); |
362 | } |
363 | static void SetThreadLocal(LocalStorageKey key, void* value); |
364 | static void SetThreadLocalInt(LocalStorageKey key, int value) { |
365 | SetThreadLocal(key, reinterpret_cast<void*>(static_cast<intptr_t>(value))); |
366 | } |
367 | static bool HasThreadLocal(LocalStorageKey key) { |
368 | return GetThreadLocal(key) != nullptr; |
369 | } |
370 | |
371 | #ifdef V8_FAST_TLS_SUPPORTED |
372 | static inline void* GetExistingThreadLocal(LocalStorageKey key) { |
373 | void* result = reinterpret_cast<void*>( |
374 | InternalGetExistingThreadLocal(static_cast<intptr_t>(key))); |
375 | DCHECK(result == GetThreadLocal(key)); |
376 | return result; |
377 | } |
378 | #else |
379 | static inline void* GetExistingThreadLocal(LocalStorageKey key) { |
380 | return GetThreadLocal(key); |
381 | } |
382 | #endif |
383 | |
384 | // The thread name length is limited to 16 based on Linux's implementation of |
385 | // prctl(). |
386 | static const int kMaxThreadNameLength = 16; |
387 | |
388 | class PlatformData; |
389 | PlatformData* data() { return data_; } |
390 | |
391 | void NotifyStartedAndRun() { |
392 | if (start_semaphore_) start_semaphore_->Signal(); |
393 | Run(); |
394 | } |
395 | |
396 | private: |
397 | void set_name(const char* name); |
398 | |
399 | PlatformData* data_; |
400 | |
401 | char name_[kMaxThreadNameLength]; |
402 | int stack_size_; |
403 | Semaphore* start_semaphore_; |
404 | |
405 | DISALLOW_COPY_AND_ASSIGN(Thread); |
406 | }; |
407 | |
408 | } // namespace base |
409 | } // namespace v8 |
410 | |
411 | #endif // V8_BASE_PLATFORM_PLATFORM_H_ |
412 | |