blob: 8f79d0d8693b23b01820e9906dad5da48a64bb77 [file] [log] [blame]
oth05c26fde2015-04-05 14:30:571// 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.
4
5#include "gin/v8_initializer.h"
6
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"
oth575f7fb52015-05-08 17:35:0012#include "base/metrics/histogram.h"
oth05c26fde2015-04-05 14:30:5713#include "base/rand_util.h"
14#include "base/strings/sys_string_conversions.h"
oth575f7fb52015-05-08 17:35:0015#include "base/threading/platform_thread.h"
16#include "base/time/time.h"
oth05c26fde2015-04-05 14:30:5717#include "crypto/sha2.h"
18
19#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
20#if defined(OS_MACOSX)
21#include "base/mac/foundation_util.h"
22#endif // OS_MACOSX
23#include "base/path_service.h"
24#endif // V8_USE_EXTERNAL_STARTUP_DATA
25
26namespace gin {
27
28namespace {
29
oth05c26fde2015-04-05 14:30:5730base::MemoryMappedFile* g_mapped_natives = nullptr;
31base::MemoryMappedFile* g_mapped_snapshot = nullptr;
32
33#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
rmcilroy54fab5e2015-04-06 21:14:5834#if !defined(OS_MACOSX)
35const int kV8SnapshotBasePathKey =
36#if defined(OS_ANDROID)
37 base::DIR_ANDROID_APP_DATA;
38#elif defined(OS_POSIX)
39 base::DIR_EXE;
40#elif defined(OS_WIN)
41 base::DIR_MODULE;
42#endif // OS_ANDROID
43#endif // !OS_MACOSX
44
45const char kNativesFileName[] = "natives_blob.bin";
46const char kSnapshotFileName[] = "snapshot_blob.bin";
47
oth575f7fb52015-05-08 17:35:0048// Constants for snapshot loading retries taken from:
49// https://support.microsoft.com/en-us/kb/316609.
50const int kMaxOpenAttempts = 5;
51const int kOpenRetryDelayMillis = 250;
52
rmcilroy54fab5e2015-04-06 21:14:5853void 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());
59
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));
65 *natives_path_out =
66 base::mac::PathForFrameworkBundleResource(natives_file_name);
67 base::ScopedCFTypeRef<CFStringRef> snapshot_file_name(
68 base::SysUTF8ToCFStringRef(kSnapshotFileName));
69 *snapshot_path_out =
70 base::mac::PathForFrameworkBundleResource(snapshot_file_name);
71 DCHECK(!natives_path_out->empty());
72 DCHECK(!snapshot_path_out->empty());
73#endif // !defined(OS_MACOSX)
74}
75
oth575f7fb52015-05-08 17:35:0076static 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) {
oth05c26fde2015-04-05 14:30:5782 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";
88 return false;
89 }
90 }
91
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";
98 return false;
99 }
100 }
101
102 return true;
103}
104
oth575f7fb52015-05-08 17:35:00105static bool OpenV8File(const base::FilePath& path,
106 int flags,
107 base::File& file) {
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).
110
111 // These match tools/metrics/histograms.xml
112 enum OpenV8FileResult {
113 OPENED = 0,
114 OPENED_RETRY,
115 FAILED_IN_USE,
116 FAILED_OTHER,
117 MAX_VALUE
118 };
119
120 OpenV8FileResult result = OpenV8FileResult::FAILED_IN_USE;
121 for (int attempt = 0; attempt < kMaxOpenAttempts; attempt++) {
122 file.Initialize(path, flags);
123 if (file.IsValid()) {
124 if (attempt == 0) {
125 result = OpenV8FileResult::OPENED;
126 break;
127 } else {
128 result = OpenV8FileResult::OPENED_RETRY;
129 break;
130 }
131 } else if (file.error_details() != base::File::FILE_ERROR_IN_USE) {
132 result = OpenV8FileResult::FAILED_OTHER;
133 break;
134 } else if (kMaxOpenAttempts - 1 != attempt) {
135 base::PlatformThread::Sleep(
136 base::TimeDelta::FromMilliseconds(kOpenRetryDelayMillis));
137 }
138 }
139
140 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.OpenV8File.Result",
141 result,
142 OpenV8FileResult::MAX_VALUE);
143
144 return result == OpenV8FileResult::OPENED
145 || result == OpenV8FileResult::OPENED_RETRY;
146}
147
oth05c26fde2015-04-05 14:30:57148#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
149bool 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));
157}
158#endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
159#endif // V8_USE_EXTERNAL_STARTUP_DATA
160
rmcilroy54fab5e2015-04-06 21:14:58161bool GenerateEntropy(unsigned char* buffer, size_t amount) {
162 base::RandBytes(buffer, amount);
163 return true;
164}
165
oth05c26fde2015-04-05 14:30:57166} // namespace
167
168#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
oth05c26fde2015-04-05 14:30:57169#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
170// Defined in gen/gin/v8_snapshot_fingerprint.cc
171extern const unsigned char g_natives_fingerprint[];
172extern const unsigned char g_snapshot_fingerprint[];
173#endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
174
oth05c26fde2015-04-05 14:30:57175// static
176bool V8Initializer::LoadV8Snapshot() {
oth575f7fb52015-05-08 17:35:00177
178 enum LoadV8SnapshotResult {
179 SUCCESS = 0,
180 FAILED_OPEN,
181 FAILED_MAP,
182 FAILED_VERIFY,
183 MAX_VALUE
184 };
185
oth05c26fde2015-04-05 14:30:57186 if (g_mapped_natives && g_mapped_snapshot)
187 return true;
188
rmcilroy54fab5e2015-04-06 21:14:58189 base::FilePath natives_data_path;
190 base::FilePath snapshot_data_path;
191 GetV8FilePaths(&natives_data_path, &snapshot_data_path);
oth05c26fde2015-04-05 14:30:57192
oth575f7fb52015-05-08 17:35:00193 base::File natives_file;
194 base::File snapshot_file;
oth05c26fde2015-04-05 14:30:57195 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
oth05c26fde2015-04-05 14:30:57196
oth575f7fb52015-05-08 17:35:00197 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;
oth05c26fde2015-04-05 14:30:57203#if defined(V8_VERIFY_EXTERNAL_STARTUP_DATA)
oth575f7fb52015-05-08 17:35:00204 } else if (!VerifyV8SnapshotFile(g_mapped_natives, g_natives_fingerprint) ||
205 !VerifyV8SnapshotFile(g_mapped_snapshot, g_snapshot_fingerprint)) {
206 result = LoadV8SnapshotResult::FAILED_VERIFY;
oth05c26fde2015-04-05 14:30:57207#endif // V8_VERIFY_EXTERNAL_STARTUP_DATA
oth575f7fb52015-05-08 17:35:00208 } else {
209 result = LoadV8SnapshotResult::SUCCESS;
210 }
211
212 UMA_HISTOGRAM_ENUMERATION("V8.Initializer.LoadV8Snapshot.Result",
213 result,
214 LoadV8SnapshotResult::MAX_VALUE);
215 return result == LoadV8SnapshotResult::SUCCESS;
oth05c26fde2015-04-05 14:30:57216}
217
218// static
219bool V8Initializer::LoadV8SnapshotFromFD(base::PlatformFile natives_pf,
220 int64 natives_offset,
221 int64 natives_size,
222 base::PlatformFile snapshot_pf,
223 int64 snapshot_offset,
224 int64 snapshot_size) {
225 if (g_mapped_natives && g_mapped_snapshot)
226 return true;
227
228 base::MemoryMappedFile::Region natives_region =
229 base::MemoryMappedFile::Region::kWholeFile;
230 if (natives_size != 0 || natives_offset != 0) {
231 natives_region =
232 base::MemoryMappedFile::Region(natives_offset, natives_size);
233 }
234
235 base::MemoryMappedFile::Region snapshot_region =
236 base::MemoryMappedFile::Region::kWholeFile;
237 if (natives_size != 0 || natives_offset != 0) {
238 snapshot_region =
239 base::MemoryMappedFile::Region(snapshot_offset, snapshot_size);
240 }
241
242 return MapV8Files(base::File(natives_pf), base::File(snapshot_pf),
243 natives_region, snapshot_region);
244}
245
rmcilroy54fab5e2015-04-06 21:14:58246// static
247bool 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);
253
oth575f7fb52015-05-08 17:35:00254 base::File natives_data_file;
255 base::File snapshot_data_file;
rmcilroy54fab5e2015-04-06 21:14:58256 int file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
rmcilroy54fab5e2015-04-06 21:14:58257
oth575f7fb52015-05-08 17:35:00258 bool success = OpenV8File(natives_data_path, file_flags, natives_data_file) &&
259 OpenV8File(snapshot_data_path, file_flags, snapshot_data_file);
260 if (success) {
261 *natives_fd_out = natives_data_file.TakePlatformFile();
262 *snapshot_fd_out = snapshot_data_file.TakePlatformFile();
263 }
264 return success;
rmcilroy54fab5e2015-04-06 21:14:58265}
266
oth05c26fde2015-04-05 14:30:57267#endif // V8_USE_EXTERNAL_STARTUP_DATA
268
269// static
jochena0b121b2015-04-30 12:56:27270void V8Initializer::Initialize(gin::IsolateHolder::ScriptMode mode) {
oth05c26fde2015-04-05 14:30:57271 static bool v8_is_initialized = false;
272 if (v8_is_initialized)
273 return;
274
275 v8::V8::InitializePlatform(V8Platform::Get());
oth05c26fde2015-04-05 14:30:57276
277 if (gin::IsolateHolder::kStrictMode == mode) {
278 static const char use_strict[] = "--use_strict";
279 v8::V8::SetFlagsFromString(use_strict, sizeof(use_strict) - 1);
280 }
281
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);
287
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
293
294 v8::V8::SetEntropySource(&GenerateEntropy);
295 v8::V8::Initialize();
296
297 v8_is_initialized = true;
298}
299
300// static
301void 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;
308 return;
309 }
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());
314}
315
316} // namespace gin