1 // Copyright 2013 The Chromium 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.
5 #include "gin/v8_initializer.h"
7 #include "base/basictypes.h"
8 #include "base/files/file.h"
9 #include "base/files/file_path.h"
10 #include "base/files/memory_mapped_file.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/rand_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time/time.h"
17 #include "crypto/sha2.h"
19 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
20 #if defined(OS_MACOSX)
21 #include "base/mac/foundation_util.h"
23 #include "base/path_service.h"
24 #endif // V8_USE_EXTERNAL_STARTUP_DATA
30 base::MemoryMappedFile
* g_mapped_natives
= nullptr;
31 base::MemoryMappedFile
* g_mapped_snapshot
= nullptr;
33 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
34 #if !defined(OS_MACOSX)
35 const int kV8SnapshotBasePathKey
=
36 #if defined(OS_ANDROID)
37 base::DIR_ANDROID_APP_DATA
;
38 #elif defined(OS_POSIX)
45 const char kNativesFileName
[] = "natives_blob.bin";
46 const char kSnapshotFileName
[] = "snapshot_blob.bin";
48 // Constants for snapshot loading retries taken from:
49 // https://support.microsoft.com/en-us/kb/316609.
50 const int kMaxOpenAttempts
= 5;
51 const int kOpenRetryDelayMillis
= 250;
53 void GetV8FilePaths(base::FilePath
* natives_path_out
,
54 base::FilePath
* snapshot_path_out
) {
55 #if !defined(OS_MACOSX)
56 base::FilePath data_path
;
57 PathService::Get(kV8SnapshotBasePathKey
, &data_path
);
58 DCHECK(!data_path
.empty());
60 *natives_path_out
= data_path
.AppendASCII(kNativesFileName
);
61 *snapshot_path_out
= data_path
.AppendASCII(kSnapshotFileName
);
62 #else // !defined(OS_MACOSX)
63 base::ScopedCFTypeRef
<CFStringRef
> natives_file_name(
64 base::SysUTF8ToCFStringRef(kNativesFileName
));
66 base::mac::PathForFrameworkBundleResource(natives_file_name
);
67 base::ScopedCFTypeRef
<CFStringRef
> snapshot_file_name(
68 base::SysUTF8ToCFStringRef(kSnapshotFileName
));
70 base::mac::PathForFrameworkBundleResource(snapshot_file_name
);
71 DCHECK(!natives_path_out
->empty());
72 DCHECK(!snapshot_path_out
->empty());
73 #endif // !defined(OS_MACOSX)
76 static bool MapV8Files(base::File natives_file
,
77 base::File snapshot_file
,
78 base::MemoryMappedFile::Region natives_region
=
79 base::MemoryMappedFile::Region::kWholeFile
,
80 base::MemoryMappedFile::Region snapshot_region
=
81 base::MemoryMappedFile::Region::kWholeFile
) {
82 g_mapped_natives
= new base::MemoryMappedFile
;
83 if (!g_mapped_natives
->IsValid()) {
84 if (!g_mapped_natives
->Initialize(natives_file
.Pass(), natives_region
)) {
85 delete g_mapped_natives
;
86 g_mapped_natives
= NULL
;
87 LOG(FATAL
) << "Couldn't mmap v8 natives data file";
92 g_mapped_snapshot
= new base::MemoryMappedFile
;
93 if (!g_mapped_snapshot
->IsValid()) {
94 if (!g_mapped_snapshot
->Initialize(snapshot_file
.Pass(), snapshot_region
)) {
95 delete g_mapped_snapshot
;
96 g_mapped_snapshot
= NULL
;
97 LOG(ERROR
) << "Couldn't mmap v8 snapshot data file";
105 static bool OpenV8File(const base::FilePath
& path
,
108 // Re-try logic here is motivated by http://crbug.com/479537
109 // for A/V on Windows (https://support.microsoft.com/en-us/kb/316609).
111 // These match tools/metrics/histograms.xml
112 enum OpenV8FileResult
{
120 OpenV8FileResult result
= OpenV8FileResult::FAILED_IN_USE
;
121 for (int attempt
= 0; attempt
< kMaxOpenAttempts
; attempt
++) {
122 file
.Initialize(path
, flags
);
123 if (file
.IsValid()) {
125 result
= OpenV8FileResult::OPENED
;
128 result
= OpenV8FileResult::OPENED_RETRY
;
131 } else if (file
.error_details() != base::File::FILE_ERROR_IN_USE
) {
132 result
= OpenV8FileResult::FAILED_OTHER
;
134 } else if (kMaxOpenAttempts
- 1 != attempt
) {
135 base::PlatformThread::Sleep(
136 base::TimeDelta::FromMilliseconds(kOpenRetryDelayMillis
));
140 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.OpenV8File.Result",
142 OpenV8FileResult::MAX_VALUE
);
144 return result
== OpenV8FileResult::OPENED
145 || result
== OpenV8FileResult::OPENED_RETRY
;
148 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
149 bool VerifyV8SnapshotFile(base::MemoryMappedFile
* snapshot_file
,
150 const unsigned char* fingerprint
) {
151 unsigned char output
[crypto::kSHA256Length
];
152 crypto::SHA256HashString(
153 base::StringPiece(reinterpret_cast<const char*>(snapshot_file
->data()),
154 snapshot_file
->length()),
155 output
, sizeof(output
));
156 return !memcmp(fingerprint
, output
, sizeof(output
));
158 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
159 #endif // V8_USE_EXTERNAL_STARTUP_DATA
161 bool GenerateEntropy(unsigned char* buffer
, size_t amount
) {
162 base::RandBytes(buffer
, amount
);
168 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
169 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
170 // Defined in gen/gin/v8_snapshot_fingerprint.cc
171 extern const unsigned char g_natives_fingerprint
[];
172 extern const unsigned char g_snapshot_fingerprint
[];
173 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
176 bool V8Initializer::LoadV8Snapshot() {
178 enum LoadV8SnapshotResult
{
186 if (g_mapped_natives
&& g_mapped_snapshot
)
189 base::FilePath natives_data_path
;
190 base::FilePath snapshot_data_path
;
191 GetV8FilePaths(&natives_data_path
, &snapshot_data_path
);
193 base::File natives_file
;
194 base::File snapshot_file
;
195 int flags
= base::File::FLAG_OPEN
| base::File::FLAG_READ
;
197 LoadV8SnapshotResult result
;
198 if (!OpenV8File(natives_data_path
, flags
, natives_file
) ||
199 !OpenV8File(snapshot_data_path
, flags
, snapshot_file
)) {
200 result
= LoadV8SnapshotResult::FAILED_OPEN
;
201 } else if (!MapV8Files(natives_file
.Pass(), snapshot_file
.Pass())) {
202 result
= LoadV8SnapshotResult::FAILED_MAP
;
203 #if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
204 } else if (!VerifyV8SnapshotFile(g_mapped_natives
, g_natives_fingerprint
) ||
205 !VerifyV8SnapshotFile(g_mapped_snapshot
, g_snapshot_fingerprint
)) {
206 result
= LoadV8SnapshotResult::FAILED_VERIFY
;
207 #endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
209 result
= LoadV8SnapshotResult::SUCCESS
;
212 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.LoadV8Snapshot.Result",
214 LoadV8SnapshotResult::MAX_VALUE
);
215 return result
== LoadV8SnapshotResult::SUCCESS
;
219 bool V8Initializer::LoadV8SnapshotFromFD(base::PlatformFile natives_pf
,
220 int64 natives_offset
,
222 base::PlatformFile snapshot_pf
,
223 int64 snapshot_offset
,
224 int64 snapshot_size
) {
225 if (g_mapped_natives
&& g_mapped_snapshot
)
228 base::MemoryMappedFile::Region natives_region
=
229 base::MemoryMappedFile::Region::kWholeFile
;
230 if (natives_size
!= 0 || natives_offset
!= 0) {
232 base::MemoryMappedFile::Region(natives_offset
, natives_size
);
235 base::MemoryMappedFile::Region snapshot_region
=
236 base::MemoryMappedFile::Region::kWholeFile
;
237 if (natives_size
!= 0 || natives_offset
!= 0) {
239 base::MemoryMappedFile::Region(snapshot_offset
, snapshot_size
);
242 return MapV8Files(base::File(natives_pf
), base::File(snapshot_pf
),
243 natives_region
, snapshot_region
);
247 bool V8Initializer::OpenV8FilesForChildProcesses(
248 base::PlatformFile
* natives_fd_out
,
249 base::PlatformFile
* snapshot_fd_out
) {
250 base::FilePath natives_data_path
;
251 base::FilePath snapshot_data_path
;
252 GetV8FilePaths(&natives_data_path
, &snapshot_data_path
);
254 base::File natives_data_file
;
255 base::File snapshot_data_file
;
256 int file_flags
= base::File::FLAG_OPEN
| base::File::FLAG_READ
;
258 bool success
= OpenV8File(natives_data_path
, file_flags
, natives_data_file
) &&
259 OpenV8File(snapshot_data_path
, file_flags
, snapshot_data_file
);
261 *natives_fd_out
= natives_data_file
.TakePlatformFile();
262 *snapshot_fd_out
= snapshot_data_file
.TakePlatformFile();
267 #endif // V8_USE_EXTERNAL_STARTUP_DATA
270 void V8Initializer::Initialize(gin::IsolateHolder::ScriptMode mode
) {
271 static bool v8_is_initialized
= false;
272 if (v8_is_initialized
)
275 v8::V8::InitializePlatform(V8Platform::Get());
277 if (gin::IsolateHolder::kStrictMode
== mode
) {
278 static const char use_strict
[] = "--use_strict";
279 v8::V8::SetFlagsFromString(use_strict
, sizeof(use_strict
) - 1);
282 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
283 v8::StartupData natives
;
284 natives
.data
= reinterpret_cast<const char*>(g_mapped_natives
->data());
285 natives
.raw_size
= static_cast<int>(g_mapped_natives
->length());
286 v8::V8::SetNativesDataBlob(&natives
);
288 v8::StartupData snapshot
;
289 snapshot
.data
= reinterpret_cast<const char*>(g_mapped_snapshot
->data());
290 snapshot
.raw_size
= static_cast<int>(g_mapped_snapshot
->length());
291 v8::V8::SetSnapshotDataBlob(&snapshot
);
292 #endif // V8_USE_EXTERNAL_STARTUP_DATA
294 v8::V8::SetEntropySource(&GenerateEntropy
);
295 v8::V8::Initialize();
297 v8_is_initialized
= true;
301 void V8Initializer::GetV8ExternalSnapshotData(const char** natives_data_out
,
302 int* natives_size_out
,
303 const char** snapshot_data_out
,
304 int* snapshot_size_out
) {
305 if (!g_mapped_natives
|| !g_mapped_snapshot
) {
306 *natives_data_out
= *snapshot_data_out
= NULL
;
307 *natives_size_out
= *snapshot_size_out
= 0;
310 *natives_data_out
= reinterpret_cast<const char*>(g_mapped_natives
->data());
311 *snapshot_data_out
= reinterpret_cast<const char*>(g_mapped_snapshot
->data());
312 *natives_size_out
= static_cast<int>(g_mapped_natives
->length());
313 *snapshot_size_out
= static_cast<int>(g_mapped_snapshot
->length());