blob: f22cfa437c6d33f6d5eb4351b95a01a1ba9ff59d [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 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
Ben Murdochca12bfa2013-07-23 11:17:05 +01005#include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00006
7#include <utility>
8#include <vector>
9
10#include "native_client/src/include/checked_cast.h"
11#include "native_client/src/include/portability_io.h"
12#include "native_client/src/shared/platform/nacl_check.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000013#include "native_client/src/trusted/service_runtime/include/sys/stat.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014
15#include "ppapi/c/pp_bool.h"
16#include "ppapi/c/pp_errors.h"
17#include "ppapi/c/ppb_file_io.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000018#include "ppapi/c/private/ppb_uma_private.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000019#include "ppapi/cpp/file_io.h"
20
Ben Murdochca12bfa2013-07-23 11:17:05 +010021#include "ppapi/native_client/src/trusted/plugin/local_temp_file.h"
22#include "ppapi/native_client/src/trusted/plugin/manifest.h"
23#include "ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.h"
24#include "ppapi/native_client/src/trusted/plugin/plugin.h"
25#include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
26#include "ppapi/native_client/src/trusted/plugin/pnacl_translate_thread.h"
27#include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
28#include "ppapi/native_client/src/trusted/plugin/temporary_file.h"
29
Torne (Richard Coles)58218062012-11-14 11:43:16 +000030namespace {
31const char kPnaclTempDir[] = "/.pnacl";
32const uint32_t kCopyBufSize = 512 << 10;
33}
34
35namespace plugin {
36
37//////////////////////////////////////////////////////////////////////
38// Pnacl-specific manifest support.
39//////////////////////////////////////////////////////////////////////
40
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010041// The PNaCl linker gets file descriptors via the service runtime's
42// reverse service lookup. The reverse service lookup requires a manifest.
43// Normally, that manifest is an NMF containing mappings for shared libraries.
44// Here, we provide a manifest that redirects to PNaCl component files
45// that are part of Chrome.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000046class PnaclManifest : public Manifest {
47 public:
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010048 PnaclManifest() : manifest_base_url_(PnaclUrls::GetBaseUrl()) { }
Torne (Richard Coles)58218062012-11-14 11:43:16 +000049 virtual ~PnaclManifest() { }
50
51 virtual bool GetProgramURL(nacl::string* full_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000052 PnaclOptions* pnacl_options,
53 ErrorInfo* error_info) const {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000054 // Does not contain program urls.
55 UNREFERENCED_PARAMETER(full_url);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000056 UNREFERENCED_PARAMETER(pnacl_options);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000057 UNREFERENCED_PARAMETER(error_info);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000058 PLUGIN_PRINTF(("PnaclManifest does not contain a program\n"));
59 error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL,
60 "pnacl manifest does not contain a program.");
61 return false;
62 }
63
64 virtual bool ResolveURL(const nacl::string& relative_url,
65 nacl::string* full_url,
66 ErrorInfo* error_info) const {
67 // Does not do general URL resolution, simply appends relative_url to
68 // the end of manifest_base_url_.
69 UNREFERENCED_PARAMETER(error_info);
70 *full_url = manifest_base_url_ + relative_url;
71 return true;
72 }
73
74 virtual bool GetFileKeys(std::set<nacl::string>* keys) const {
75 // Does not support enumeration.
76 PLUGIN_PRINTF(("PnaclManifest does not support key enumeration\n"));
77 UNREFERENCED_PARAMETER(keys);
78 return false;
79 }
80
81 virtual bool ResolveKey(const nacl::string& key,
82 nacl::string* full_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000083 PnaclOptions* pnacl_options,
84 ErrorInfo* error_info) const {
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010085 // All of the component files are native (do not require pnacl translate).
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000086 pnacl_options->set_translate(false);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000087 // We can only resolve keys in the files/ namespace.
88 const nacl::string kFilesPrefix = "files/";
89 size_t files_prefix_pos = key.find(kFilesPrefix);
90 if (files_prefix_pos == nacl::string::npos) {
91 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL,
92 "key did not start with files/");
93 return false;
94 }
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010095 // Resolve the full URL to the file. Provide it with a platform-specific
96 // prefix.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000097 nacl::string key_basename = key.substr(kFilesPrefix.length());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010098 return ResolveURL(PnaclUrls::PrependPlatformPrefix(key_basename),
99 full_url, error_info);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000100 }
101
102 private:
103 NACL_DISALLOW_COPY_AND_ASSIGN(PnaclManifest);
104
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000105 nacl::string manifest_base_url_;
106};
107
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000108//////////////////////////////////////////////////////////////////////
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000109// UMA stat helpers.
110//////////////////////////////////////////////////////////////////////
111
112namespace {
113
114// Assume translation time metrics *can be* large.
115// Up to 12 minutes.
116const int64_t kTimeLargeMin = 10; // in ms
117const int64_t kTimeLargeMax = 720000; // in ms
118const uint32_t kTimeLargeBuckets = 100;
119
120const int32_t kSizeKBMin = 1;
121const int32_t kSizeKBMax = 512*1024; // very large .pexe / .nexe.
122const uint32_t kSizeKBBuckets = 100;
123
124const int32_t kRatioMin = 10;
125const int32_t kRatioMax = 10*100; // max of 10x difference.
126const uint32_t kRatioBuckets = 100;
127
128const int32_t kKBPSMin = 1;
129const int32_t kKBPSMax = 30*1000; // max of 30 MB / sec.
130const uint32_t kKBPSBuckets = 100;
131
132const PPB_UMA_Private* uma_interface = NULL;
133
134const PPB_UMA_Private* GetUMAInterface() {
135 if (uma_interface != NULL) {
136 return uma_interface;
137 }
138 pp::Module *module = pp::Module::Get();
139 DCHECK(module);
140 uma_interface = static_cast<const PPB_UMA_Private*>(
141 module->GetBrowserInterface(PPB_UMA_PRIVATE_INTERFACE));
142 return uma_interface;
143}
144
145void HistogramTime(const std::string& name, int64_t ms) {
146 if (ms < 0) return;
147
148 const PPB_UMA_Private* ptr = GetUMAInterface();
149 if (ptr == NULL) return;
150
151 ptr->HistogramCustomTimes(pp::Var(name).pp_var(),
152 ms,
153 kTimeLargeMin, kTimeLargeMax,
154 kTimeLargeBuckets);
155}
156
157void HistogramSizeKB(const std::string& name, int32_t kb) {
158 if (kb < 0) return;
159
160 const PPB_UMA_Private* ptr = GetUMAInterface();
161 if (ptr == NULL) return;
162
163 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
164 kb,
165 kSizeKBMin, kSizeKBMax,
166 kSizeKBBuckets);
167}
168
169void HistogramRatio(const std::string& name, int64_t a, int64_t b) {
170 if (a < 0 || b <= 0) return;
171
172 const PPB_UMA_Private* ptr = GetUMAInterface();
173 if (ptr == NULL) return;
174
175 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
176 100 * a / b,
177 kRatioMin, kRatioMax,
178 kRatioBuckets);
179}
180
181void HistogramKBPerSec(const std::string& name, double kb, double s) {
182 if (kb < 0.0 || s <= 0.0) return;
183
184 const PPB_UMA_Private* ptr = GetUMAInterface();
185 if (ptr == NULL) return;
186
187 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
188 static_cast<int64_t>(kb / s),
189 kKBPSMin, kKBPSMax,
190 kKBPSBuckets);
191}
192
193void HistogramEnumerateTranslationCache(bool hit) {
194 const PPB_UMA_Private* ptr = GetUMAInterface();
195 if (ptr == NULL) return;
196 ptr->HistogramEnumeration(pp::Var("NaCl.Perf.PNaClCache.IsHit").pp_var(),
197 hit, 2);
198}
199
200// Opt level is expected to be 0 to 3. Treating 4 as unknown.
201const int8_t kOptUnknown = 4;
202
203void HistogramOptLevel(int8_t opt_level) {
204 const PPB_UMA_Private* ptr = GetUMAInterface();
205 if (ptr == NULL) return;
206 if (opt_level < 0 || opt_level > 3) {
207 opt_level = kOptUnknown;
208 }
209 ptr->HistogramEnumeration(pp::Var("NaCl.Options.PNaCl.OptLevel").pp_var(),
210 opt_level, kOptUnknown+1);
211}
212
213} // namespace
214
215
216//////////////////////////////////////////////////////////////////////
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000217// The coordinator class.
218//////////////////////////////////////////////////////////////////////
219
220// Out-of-line destructor to keep it from getting put in every .o where
221// callback_source.h is included
222template<>
223CallbackSource<FileStreamData>::~CallbackSource() {}
224
225PnaclCoordinator* PnaclCoordinator::BitcodeToNative(
226 Plugin* plugin,
227 const nacl::string& pexe_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000228 const PnaclOptions& pnacl_options,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000229 const pp::CompletionCallback& translate_notify_callback) {
230 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (plugin=%p, pexe=%s)\n",
231 static_cast<void*>(plugin), pexe_url.c_str()));
232 PnaclCoordinator* coordinator =
233 new PnaclCoordinator(plugin, pexe_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000234 pnacl_options,
235 translate_notify_callback);
236 coordinator->pnacl_init_time_ = NaClGetTimeOfDayMicroseconds();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000237 coordinator->off_the_record_ =
238 plugin->nacl_interface()->IsOffTheRecord();
239 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (manifest=%p, "
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000240 "off_the_record=%d)\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000241 reinterpret_cast<const void*>(coordinator->manifest_.get()),
242 coordinator->off_the_record_));
243
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100244 // Loading resources (e.g. llc and ld nexes) is done with PnaclResources.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000245 coordinator->resources_.reset(
246 new PnaclResources(plugin,
247 coordinator,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100248 coordinator->manifest_.get()));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000249 CHECK(coordinator->resources_ != NULL);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000250
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100251 // The first step of loading resources: read the resource info file.
252 pp::CompletionCallback resource_info_read_cb =
253 coordinator->callback_factory_.NewCallback(
254 &PnaclCoordinator::ResourceInfoWasRead);
255 coordinator->resources_->ReadResourceInfo(PnaclUrls::GetResourceInfoUrl(),
256 resource_info_read_cb);
257 return coordinator;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000258}
259
260PnaclCoordinator::PnaclCoordinator(
261 Plugin* plugin,
262 const nacl::string& pexe_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000263 const PnaclOptions& pnacl_options,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000264 const pp::CompletionCallback& translate_notify_callback)
265 : translate_finish_error_(PP_OK),
266 plugin_(plugin),
267 translate_notify_callback_(translate_notify_callback),
268 file_system_(new pp::FileSystem(plugin, PP_FILESYSTEMTYPE_LOCALTEMPORARY)),
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100269 manifest_(new PnaclManifest()),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000270 pexe_url_(pexe_url),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000271 pnacl_options_(pnacl_options),
Ben Murdocheb525c52013-07-10 11:40:50 +0100272 use_new_cache_(false),
273 is_cache_hit_(PP_FALSE),
274 nexe_handle_(PP_kInvalidFileHandle),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000275 error_already_reported_(false),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000276 off_the_record_(false),
277 pnacl_init_time_(0),
278 pexe_size_(0),
279 pexe_bytes_compiled_(0),
280 expected_pexe_size_(-1) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000281 PLUGIN_PRINTF(("PnaclCoordinator::PnaclCoordinator (this=%p, plugin=%p)\n",
282 static_cast<void*>(this), static_cast<void*>(plugin)));
283 callback_factory_.Initialize(this);
Ben Murdocheb525c52013-07-10 11:40:50 +0100284 if (getenv("PNACL_USE_NEW_CACHE")) {
285 PLUGIN_PRINTF(("PnaclCoordinator using new translation cache\n"));
286 use_new_cache_ = true;
287 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000288}
289
290PnaclCoordinator::~PnaclCoordinator() {
291 PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, "
292 "translate_thread=%p\n",
293 static_cast<void*>(this), translate_thread_.get()));
294 // Stopping the translate thread will cause the translate thread to try to
295 // run translation_complete_callback_ on the main thread. This destructor is
296 // running from the main thread, and by the time it exits, callback_factory_
297 // will have been destroyed. This will result in the cancellation of
298 // translation_complete_callback_, so no notification will be delivered.
299 if (translate_thread_.get() != NULL) {
300 translate_thread_->AbortSubprocesses();
301 }
302}
303
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000304void PnaclCoordinator::ReportNonPpapiError(enum PluginErrorCode err_code,
305 const nacl::string& message) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100306 error_info_.SetReport(err_code, message);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000307 ExitWithError();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000308}
309
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000310void PnaclCoordinator::ReportPpapiError(enum PluginErrorCode err_code,
311 int32_t pp_error,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000312 const nacl::string& message) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000313 nacl::stringstream ss;
314 ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ").";
315 error_info_.SetReport(err_code, ss.str());
316 ExitWithError();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000317}
318
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000319void PnaclCoordinator::ExitWithError() {
320 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError (error_code=%d, "
321 "message='%s')\n",
322 error_info_.error_code(),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000323 error_info_.message().c_str()));
324 plugin_->ReportLoadError(error_info_);
325 // Free all the intermediate callbacks we ever created.
326 // Note: this doesn't *cancel* the callbacks from the factories attached
327 // to the various helper classes (e.g., pnacl_resources). Thus, those
328 // callbacks may still run asynchronously. We let those run but ignore
329 // any other errors they may generate so that they do not end up running
330 // translate_notify_callback_, which has already been freed.
331 callback_factory_.CancelAll();
332 if (!error_already_reported_) {
333 error_already_reported_ = true;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000334 translate_notify_callback_.Run(PP_ERROR_FAILED);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000335 } else {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000336 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError an earlier error was "
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000337 "already reported -- Skipping.\n"));
338 }
339}
340
341// Signal that Pnacl translation completed normally.
342void PnaclCoordinator::TranslateFinished(int32_t pp_error) {
343 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100344 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000345 // Bail out if there was an earlier error (e.g., pexe load failure).
346 if (translate_finish_error_ != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000347 ExitWithError();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000348 return;
349 }
350 // Bail out if there is an error from the translation thread.
351 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000352 ExitWithError();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000353 return;
354 }
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100355 // Send out one last progress event, to finish up the progress events
356 // that were delayed (see the delay inserted in BitcodeGotCompiled).
357 if (ExpectedProgressKnown()) {
358 pexe_bytes_compiled_ = expected_pexe_size_;
359 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
360 pexe_url_,
361 plugin::Plugin::LENGTH_IS_COMPUTABLE,
362 pexe_bytes_compiled_,
363 expected_pexe_size_);
364 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000365
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000366 // If there are no errors, report stats from this thread (the main thread).
367 HistogramOptLevel(pnacl_options_.opt_level());
368 const plugin::PnaclTimeStats& time_stats = translate_thread_->GetTimeStats();
369 HistogramTime("NaCl.Perf.PNaClLoadTime.LoadCompiler",
370 time_stats.pnacl_llc_load_time / NACL_MICROS_PER_MILLI);
371 HistogramTime("NaCl.Perf.PNaClLoadTime.CompileTime",
372 time_stats.pnacl_compile_time / NACL_MICROS_PER_MILLI);
373 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
374 pexe_size_ / 1024.0,
375 time_stats.pnacl_compile_time / 1000000.0);
376 HistogramTime("NaCl.Perf.PNaClLoadTime.LoadLinker",
377 time_stats.pnacl_ld_load_time / NACL_MICROS_PER_MILLI);
378 HistogramTime("NaCl.Perf.PNaClLoadTime.LinkTime",
379 time_stats.pnacl_link_time / NACL_MICROS_PER_MILLI);
380 HistogramSizeKB("NaCl.Perf.Size.Pexe",
381 static_cast<int64_t>(pexe_size_ / 1024));
382
383 struct nacl_abi_stat stbuf;
384 struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc();
385 int stat_ret;
386 if (0 != (stat_ret = (*((struct NaClDescVtbl const *) desc->base.vtbl)->
387 Fstat)(desc, &stbuf))) {
388 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished can't stat nexe.\n"));
389 } else {
390 size_t nexe_size = stbuf.nacl_abi_st_size;
391 HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe",
392 static_cast<int64_t>(nexe_size / 1024));
393 HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct", pexe_size_, nexe_size);
394 }
395
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000396 // The nexe is written to the temp_nexe_file_. We must Reset() the file
397 // pointer to be able to read it again from the beginning.
398 temp_nexe_file_->Reset();
399
Ben Murdocheb525c52013-07-10 11:40:50 +0100400 if (use_new_cache_) {
401 // Report to the browser that translation finished. The browser will take
402 // care of caching.
403 plugin_->nacl_interface()->ReportTranslationFinished(
404 plugin_->pp_instance());
405 NexeReadDidOpen(PP_OK);
406 return;
407 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000408 if (pnacl_options_.HasCacheKey() && cached_nexe_file_ != NULL) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000409 // We are using a cache, but had a cache miss, which is why we did the
410 // translation. Reset cached_nexe_file_ to have a random name,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000411 // for scratch purposes, before renaming to the final cache_identity.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000412 cached_nexe_file_.reset(new LocalTempFile(plugin_, file_system_.get(),
413 nacl::string(kPnaclTempDir)));
414 pp::CompletionCallback cb = callback_factory_.NewCallback(
415 &PnaclCoordinator::CachedNexeOpenedForWrite);
416 cached_nexe_file_->OpenWrite(cb);
417 } else {
418 // For now, tolerate bitcode that is missing a cache identity, and
419 // tolerate the lack of caching in incognito mode.
420 PLUGIN_PRINTF(("PnaclCoordinator -- not caching.\n"));
421 NexeReadDidOpen(PP_OK);
422 }
423}
424
425void PnaclCoordinator::CachedNexeOpenedForWrite(int32_t pp_error) {
426 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000427 if (pp_error == PP_ERROR_NOACCESS) {
428 ReportPpapiError(
429 ERROR_PNACL_CACHE_FILEOPEN_NOACCESS,
430 pp_error,
431 "PNaCl translation cache failed to open file for write "
432 "(no access).");
433 return;
434 }
435 if (pp_error == PP_ERROR_NOQUOTA) {
436 ReportPpapiError(
437 ERROR_PNACL_CACHE_FILEOPEN_NOQUOTA,
438 pp_error,
439 "PNaCl translation cache failed to open file for write "
440 "(no quota).");
441 return;
442 }
443 if (pp_error == PP_ERROR_NOSPACE) {
444 ReportPpapiError(
445 ERROR_PNACL_CACHE_FILEOPEN_NOSPACE,
446 pp_error,
447 "PNaCl translation cache failed to open file for write "
448 "(no space).");
449 return;
450 }
451 if (pp_error == PP_ERROR_NOTAFILE) {
452 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_NOTAFILE,
453 pp_error,
454 "PNaCl translation cache failed to open file for write."
455 " File already exists as a directory.");
456 return;
457 }
458 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_OTHER,
459 pp_error,
460 "PNaCl translation cache failed to open file for write.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000461 return;
462 }
463
464 // Copy the contents from temp_nexe_file_ -> cached_nexe_file_,
465 // then rename the cached_nexe_file_ file to the cache id.
466 int64_t cur_offset = 0;
467 nacl::DescWrapper* read_wrapper = temp_nexe_file_->read_wrapper();
468 char buf[kCopyBufSize];
469 int32_t num_read =
470 nacl::assert_cast<int32_t>(read_wrapper->Read(buf, sizeof buf));
471 // Hit EOF or something.
472 if (num_read == 0) {
473 NexeWasCopiedToCache(PP_OK);
474 return;
475 }
476 if (num_read < 0) {
477 PLUGIN_PRINTF(("PnaclCoordinator::CachedNexeOpenedForWrite read failed "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100478 "(error=%" NACL_PRId32 ")\n", num_read));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000479 NexeWasCopiedToCache(PP_ERROR_FAILED);
480 return;
481 }
482 pp::CompletionCallback cb = callback_factory_.NewCallback(
483 &PnaclCoordinator::DidCopyNexeToCachePartial, num_read, cur_offset);
484 cached_nexe_file_->write_file_io()->Write(cur_offset, buf, num_read, cb);
485}
486
487void PnaclCoordinator::DidCopyNexeToCachePartial(int32_t pp_error,
488 int32_t num_read_prev,
489 int64_t cur_offset) {
490 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100491 "(pp_error=%" NACL_PRId32 ", num_read_prev=%" NACL_PRId32
492 ", cur_offset=%" NACL_PRId64 ").\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000493 pp_error, num_read_prev, cur_offset));
494 // Assume we are done.
495 if (pp_error == PP_OK) {
496 NexeWasCopiedToCache(PP_OK);
497 return;
498 }
499 if (pp_error < PP_OK) {
500 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial failed (err=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100501 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000502 NexeWasCopiedToCache(pp_error);
503 return;
504 }
505
506 // Check if we wrote as much as we read.
507 nacl::DescWrapper* read_wrapper = temp_nexe_file_->read_wrapper();
508 if (pp_error != num_read_prev) {
509 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial partial "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100510 "write (bytes_written=%" NACL_PRId32 " vs "
511 "read=%" NACL_PRId32 ")\n", pp_error, num_read_prev));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000512 CHECK(pp_error < num_read_prev);
513 // Seek back to re-read the bytes that were not written.
514 nacl_off64_t seek_result =
515 read_wrapper->Seek(pp_error - num_read_prev, SEEK_CUR);
516 if (seek_result < 0) {
517 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial seek failed "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100518 "(err=%" NACL_PRId64 ")\n", seek_result));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000519 NexeWasCopiedToCache(PP_ERROR_FAILED);
520 return;
521 }
522 }
523
524 int64_t next_offset = cur_offset + pp_error;
525 char buf[kCopyBufSize];
526 int32_t num_read =
527 nacl::assert_cast<int32_t>(read_wrapper->Read(buf, sizeof buf));
528 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial read (bytes=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100529 NACL_PRId32 ")\n", num_read));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000530 // Hit EOF or something.
531 if (num_read == 0) {
532 NexeWasCopiedToCache(PP_OK);
533 return;
534 }
535 if (num_read < 0) {
536 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial read failed "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100537 "(error=%" NACL_PRId32 ")\n", num_read));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000538 NexeWasCopiedToCache(PP_ERROR_FAILED);
539 return;
540 }
541 pp::CompletionCallback cb = callback_factory_.NewCallback(
542 &PnaclCoordinator::DidCopyNexeToCachePartial, num_read, next_offset);
543 PLUGIN_PRINTF(("PnaclCoordinator::CopyNexeToCache Writing ("
Ben Murdochbbcdd452013-07-25 10:06:34 +0100544 "bytes=%" NACL_PRId32 ", buf=%p, file_io=%p)\n", num_read, buf,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000545 cached_nexe_file_->write_file_io()));
546 cached_nexe_file_->write_file_io()->Write(next_offset, buf, num_read, cb);
547}
548
549void PnaclCoordinator::NexeWasCopiedToCache(int32_t pp_error) {
550 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000551 // Try to delete the partially written not-yet-committed cache file before
552 // returning. We pass the current pp_error along so that it can be reported
553 // before returning.
554 pp::CompletionCallback cb = callback_factory_.NewCallback(
555 &PnaclCoordinator::CorruptCacheFileWasDeleted, pp_error);
556 cached_nexe_file_->Delete(cb);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000557 return;
558 }
559 // Rename the cached_nexe_file_ file to the cache id, to finalize.
560 pp::CompletionCallback cb =
561 callback_factory_.NewCallback(&PnaclCoordinator::NexeFileWasRenamed);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000562 cached_nexe_file_->Rename(pnacl_options_.GetCacheKey(), cb);
563}
564
565void PnaclCoordinator::CorruptCacheFileWasDeleted(int32_t delete_pp_error,
566 int32_t orig_pp_error) {
567 if (delete_pp_error != PP_OK) {
568 // The cache file was certainly already opened by the time we tried
569 // to write to it, so it should certainly be deletable.
570 PLUGIN_PRINTF(("PnaclCoordinator::CorruptCacheFileWasDeleted "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100571 "delete failed with pp_error=%" NACL_PRId32 "\n",
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000572 delete_pp_error));
573 // fall through and report the original error.
574 }
575 // Report the original error that caused us to consider the
576 // cache file corrupted.
577 if (orig_pp_error == PP_ERROR_NOQUOTA) {
578 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_NOQUOTA,
579 orig_pp_error,
580 "Failed to copy translated nexe to cache (no quota).");
581 return;
582 }
583 if (orig_pp_error == PP_ERROR_NOSPACE) {
584 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_NOSPACE,
585 orig_pp_error,
586 "Failed to copy translated nexe to cache (no space).");
587 return;
588 }
589 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_OTHER,
590 orig_pp_error,
591 "Failed to copy translated nexe to cache.");
592 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000593}
594
595void PnaclCoordinator::NexeFileWasRenamed(int32_t pp_error) {
596 PLUGIN_PRINTF(("PnaclCoordinator::NexeFileWasRenamed (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100597 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000598 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000599 if (pp_error == PP_ERROR_NOACCESS) {
600 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_RENAME_NOACCESS,
601 pp_error,
602 "Failed to finalize cached translation (no access).");
603 return;
604 } else if (pp_error != PP_ERROR_FILEEXISTS) {
605 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_RENAME_OTHER,
606 pp_error,
607 "Failed to finalize cached translation.");
608 return;
609 } else { // pp_error == PP_ERROR_FILEEXISTS.
610 // NOTE: if the file already existed, it looks like the rename will
611 // happily succeed. However, we should add a test for this.
612 // Could be a hash collision, or it could also be two tabs racing to
613 // translate the same pexe. We may want UMA stats to know if this happens.
614 // For now, assume that it is a race and try to continue.
615 // If there is truly a corrupted file, then sel_ldr should prevent the
616 // file from loading due to the file size not matching the ELF header.
617 PLUGIN_PRINTF(("PnaclCoordinator::NexeFileWasRenamed file existed\n"));
618 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000619 }
620
621 cached_nexe_file_->FinishRename();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000622
623 int64_t total_time = NaClGetTimeOfDayMicroseconds() - pnacl_init_time_;
624 HistogramTime("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
625 total_time / NACL_MICROS_PER_MILLI);
626 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
627 pexe_size_ / 1024.0,
628 total_time / 1000000.0);
629
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000630 // Open the cache file for reading.
631 pp::CompletionCallback cb =
632 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen);
633 cached_nexe_file_->OpenRead(cb);
634}
635
636void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) {
637 PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100638 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000639 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000640 if (pp_error == PP_ERROR_FILENOTFOUND) {
641 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOTFOUND,
642 pp_error,
643 "Failed to open translated nexe (not found).");
644 return;
645 }
646 if (pp_error == PP_ERROR_NOACCESS) {
647 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOACCESS,
648 pp_error,
649 "Failed to open translated nexe (no access).");
650 return;
651 }
652 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_OTHER,
653 pp_error,
654 "Failed to open translated nexe.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000655 return;
656 }
657
658 // Transfer ownership of cache/temp file's wrapper to the coordinator.
659 if (cached_nexe_file_ != NULL) {
660 translated_fd_.reset(cached_nexe_file_->release_read_wrapper());
661 } else {
662 translated_fd_.reset(temp_nexe_file_->release_read_wrapper());
663 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000664 translate_notify_callback_.Run(pp_error);
665}
666
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100667void PnaclCoordinator::ResourceInfoWasRead(int32_t pp_error) {
668 PLUGIN_PRINTF(("PluginCoordinator::ResourceInfoWasRead (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100669 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100670 // Second step of loading resources: call StartLoad.
671 pp::CompletionCallback resources_cb =
672 callback_factory_.NewCallback(&PnaclCoordinator::ResourcesDidLoad);
673 resources_->StartLoad(resources_cb);
674}
675
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000676void PnaclCoordinator::ResourcesDidLoad(int32_t pp_error) {
677 PLUGIN_PRINTF(("PnaclCoordinator::ResourcesDidLoad (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100678 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000679 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000680 // Finer-grained error code should have already been reported by
681 // the PnaclResources class.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000682 return;
683 }
684
685 if (!off_the_record_) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100686 if (use_new_cache_) {
687 OpenBitcodeStream();
688 } else {
689 // Open the local temporary FS to see if we get a hit in the cache.
690 pp::CompletionCallback cb =
691 callback_factory_.NewCallback(&PnaclCoordinator::FileSystemDidOpen);
692 int32_t open_error = file_system_->Open(0, cb);
693 if (open_error != PP_OK_COMPLETIONPENDING) {
694 // At this point, no async request has kicked off to check for
695 // permissions, space, etc., so the only error that can be detected
696 // now is that an open() is already in progress (or a really terrible
697 // error).
698 if (pp_error == PP_ERROR_INPROGRESS) {
699 ReportPpapiError(
700 ERROR_PNACL_CACHE_OPEN_INPROGRESS,
701 pp_error,
702 "File system for PNaCl translation cache failed to open "
703 "(in progress).");
704 return;
705 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000706 ReportPpapiError(
Ben Murdocheb525c52013-07-10 11:40:50 +0100707 ERROR_PNACL_CACHE_OPEN_OTHER,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000708 pp_error,
Ben Murdocheb525c52013-07-10 11:40:50 +0100709 "File system for PNaCl translation cache failed to open.");
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000710 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000711 }
712 } else {
713 // We don't have a cache, so do the non-cached codepath.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100714 OpenBitcodeStream();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000715 }
716}
717
718void PnaclCoordinator::FileSystemDidOpen(int32_t pp_error) {
719 PLUGIN_PRINTF(("PnaclCoordinator::FileSystemDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100720 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000721 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000722 if (pp_error == PP_ERROR_NOACCESS) {
723 ReportPpapiError(
724 ERROR_PNACL_CACHE_OPEN_NOACCESS,
725 pp_error,
726 "File system for PNaCl translation cache failed to open "
727 "(no access).");
728 return;
729 }
730 if (pp_error == PP_ERROR_NOQUOTA) {
731 ReportPpapiError(
732 ERROR_PNACL_CACHE_OPEN_NOQUOTA,
733 pp_error,
734 "File system for PNaCl translation cache failed to open "
735 "(no quota).");
736 return;
737 }
738 if (pp_error == PP_ERROR_NOSPACE) {
739 ReportPpapiError(
740 ERROR_PNACL_CACHE_OPEN_NOSPACE,
741 pp_error,
742 "File system for PNaCl translation cache failed to open "
743 "(no space).");
744 return;
745 }
746 ReportPpapiError(ERROR_PNACL_CACHE_OPEN_OTHER,
747 pp_error,
748 "File system for PNaCl translation cache failed to open.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000749 }
750 dir_ref_.reset(new pp::FileRef(*file_system_, kPnaclTempDir));
751 // Attempt to create the directory.
752 pp::CompletionCallback cb =
753 callback_factory_.NewCallback(&PnaclCoordinator::DirectoryWasCreated);
754 dir_ref_->MakeDirectory(cb);
755}
756
757void PnaclCoordinator::DirectoryWasCreated(int32_t pp_error) {
758 PLUGIN_PRINTF(("PnaclCoordinator::DirectoryWasCreated (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100759 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000760 if (pp_error != PP_ERROR_FILEEXISTS && pp_error != PP_OK) {
761 // Directory did not exist and could not be created.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000762 if (pp_error == PP_ERROR_NOACCESS) {
763 ReportPpapiError(
764 ERROR_PNACL_CACHE_DIRECTORY_CREATE,
765 pp_error,
766 "PNaCl translation cache directory creation/check failed "
767 "(no access).");
768 return;
769 }
770 ReportPpapiError(
771 ERROR_PNACL_CACHE_DIRECTORY_CREATE,
772 pp_error,
773 "PNaCl translation cache directory creation/check failed.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000774 return;
775 }
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100776 OpenBitcodeStream();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000777}
778
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100779void PnaclCoordinator::OpenBitcodeStream() {
780 // Now open the pexe stream.
781 streaming_downloader_.reset(new FileDownloader());
782 streaming_downloader_->Initialize(plugin_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000783
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100784 // Even though we haven't started downloading, create the translation
785 // thread object immediately. This ensures that any pieces of the file
786 // that get downloaded before the compilation thread is accepting
787 // SRPCs won't get dropped.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000788 translate_thread_.reset(new PnaclTranslateThread());
789 if (translate_thread_ == NULL) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100790 ReportNonPpapiError(
791 ERROR_PNACL_THREAD_CREATE,
792 "PnaclCoordinator: could not allocate translation thread.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000793 return;
794 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100795 if (!use_new_cache_) {
796 // We also want to open the object file now so the
797 // translator can start writing to it during streaming translation.
798 obj_file_.reset(new TempFile(plugin_));
799 pp::CompletionCallback obj_cb =
800 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen);
801 obj_file_->Open(obj_cb, true);
802 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000803
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000804 pp::CompletionCallback cb =
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100805 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidOpen);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000806 if (!streaming_downloader_->OpenStream(pexe_url_, cb, this)) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100807 ReportNonPpapiError(
808 ERROR_PNACL_PEXE_FETCH_OTHER,
809 nacl::string("PnaclCoordinator: failed to open stream ") + pexe_url_);
810 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000811 }
812}
813
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100814void PnaclCoordinator::BitcodeStreamDidOpen(int32_t pp_error) {
815 if (pp_error != PP_OK) {
816 BitcodeStreamDidFinish(pp_error);
Ben Murdocheb525c52013-07-10 11:40:50 +0100817 // In the new cache case, we have not spun up the translation process yet,
818 // so we need to call TranslateFinished here.
819 if (use_new_cache_)
820 TranslateFinished(pp_error);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100821 return;
822 }
823
824 if (!off_the_record_) {
825 // Get the cache key and try to open an existing entry.
826 nacl::string headers = streaming_downloader_->GetResponseHeaders();
827 NaClHttpResponseHeaders parser;
828 parser.Parse(headers);
829 nacl::string cache_validators = parser.GetCacheValidators();
830 if (parser.CacheControlNoStore() || cache_validators.empty()) {
831 // We can't cache in this case.
832 pnacl_options_.set_cache_validators("");
833 CachedFileDidOpen(PP_ERROR_FAILED);
834 return;
835 } else {
836 nacl::string url = streaming_downloader_->url();
837 // For now, combine the cache_validators + the URL as the key.
838 // When we change the cache backend to be not-origin-specific
839 // we should send the URL separately, and check in the browser's
840 // RenderViewHost / SiteInstance's IsSameWebsite() to prevent
841 // people from forging the URL for a different origin.
842 pnacl_options_.set_cache_validators(cache_validators + url);
843 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100844 if (use_new_cache_) {
845 pp::CompletionCallback cb =
846 callback_factory_.NewCallback(&PnaclCoordinator::NexeFdDidOpen);
847 int32_t nexe_fd_err =
848 plugin_->nacl_interface()->GetNexeFd(
849 plugin_->pp_instance(),
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100850 streaming_downloader_->url().c_str(),
851 // TODO(dschuff): use the right abi version here.
852 0,
853 pnacl_options_.opt_level(),
854 parser.GetHeader("last-modified").c_str(),
855 parser.GetHeader("etag").c_str(),
Ben Murdocheb525c52013-07-10 11:40:50 +0100856 &is_cache_hit_,
857 &nexe_handle_,
858 cb.pp_completion_callback());
859 if (nexe_fd_err < PP_OK_COMPLETIONPENDING) {
860 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, nexe_fd_err,
861 nacl::string("Call to GetNexeFd failed"));
862 return;
863 }
864 } else {
865 cached_nexe_file_.reset(new LocalTempFile(
866 plugin_, file_system_.get(),
867 nacl::string(kPnaclTempDir),
868 pnacl_options_.GetCacheKey()));
869 pp::CompletionCallback cb =
870 callback_factory_.NewCallback(&PnaclCoordinator::CachedFileDidOpen);
871 cached_nexe_file_->OpenRead(cb);
872 }
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100873 } else {
874 // No cache case.
875 CachedFileDidOpen(PP_ERROR_FAILED);
876 }
877}
878
Ben Murdocheb525c52013-07-10 11:40:50 +0100879void PnaclCoordinator::NexeFdDidOpen(int32_t pp_error) {
880 PLUGIN_PRINTF(("PnaclCoordinator::NexeFdDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100881 NACL_PRId32 ", hit=%d, handle=%d)\n", pp_error,
Ben Murdocheb525c52013-07-10 11:40:50 +0100882 is_cache_hit_ == PP_TRUE,
883 nexe_handle_));
884 if (pp_error < PP_OK) {
885 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, pp_error,
886 nacl::string("GetNexeFd failed"));
887 return;
888 }
889 temp_nexe_file_.reset(new TempFile(plugin_));
890 if (!temp_nexe_file_->SetExistingFd(nexe_handle_)) {
891 ReportNonPpapiError(
892 ERROR_PNACL_CREATE_TEMP,
893 nacl::string(
894 "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
895 return;
896 }
897 if (is_cache_hit_ == PP_TRUE) {
898 // Cache hit -- no need to stream the rest of the file.
899 streaming_downloader_.reset(NULL);
900 // TODO(dschuff): update UMA stats for hit/miss once there could actually
901 // be hits/misses.
902 // Open it for reading as the cached nexe file.
903 pp::CompletionCallback cb =
904 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen);
905 temp_nexe_file_->Open(cb, false);
906 } else {
907 // Open an object file first so the translator can start writing to it
908 // during streaming translation.
909 obj_file_.reset(new TempFile(plugin_));
910 pp::CompletionCallback obj_cb =
911 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen);
912 obj_file_->Open(obj_cb, true);
913
914 // Meanwhile, a miss means we know we need to stream the bitcode, so stream
915 // the rest of it now. (Calling FinishStreaming means that the downloader
916 // will begin handing data to the coordinator, which is safe any time after
917 // the translate_thread_ object has been initialized).
918 pp::CompletionCallback finish_cb = callback_factory_.NewCallback(
919 &PnaclCoordinator::BitcodeStreamDidFinish);
920 streaming_downloader_->FinishStreaming(finish_cb);
921 }
922}
923
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100924void PnaclCoordinator::CachedFileDidOpen(int32_t pp_error) {
925 PLUGIN_PRINTF(("PnaclCoordinator::CachedFileDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100926 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100927 if (pp_error == PP_OK) {
928 // Cache hit -- no need to stream the rest of the file.
929 streaming_downloader_.reset(NULL);
930 HistogramEnumerateTranslationCache(true);
931 NexeReadDidOpen(PP_OK);
932 return;
933 }
934 // Otherwise, the cache file is missing so we must translate.
935 HistogramEnumerateTranslationCache(false);
936
937 // Continue streaming.
938 pp::CompletionCallback cb =
939 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidFinish);
940 streaming_downloader_->FinishStreaming(cb);
941}
942
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000943void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) {
944 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100945 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000946 if (pp_error != PP_OK) {
947 // Defer reporting the error and cleanup until after the translation
948 // thread returns, because it may be accessing the coordinator's
949 // objects or writing to the files.
950 translate_finish_error_ = pp_error;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000951 if (pp_error == PP_ERROR_ABORTED) {
952 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_ABORTED,
953 "PnaclCoordinator: pexe load failed (aborted).");
954 }
955 if (pp_error == PP_ERROR_NOACCESS) {
956 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_NOACCESS,
957 "PnaclCoordinator: pexe load failed (no access).");
958 } else {
959 nacl::stringstream ss;
960 ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ").";
961 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_OTHER, ss.str());
962 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000963 translate_thread_->AbortSubprocesses();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000964 } else {
965 // Compare download completion pct (100% now), to compile completion pct.
966 HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
967 pexe_bytes_compiled_, pexe_size_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000968 }
969}
970
971void PnaclCoordinator::BitcodeStreamGotData(int32_t pp_error,
972 FileStreamData data) {
973 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamGotData (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100974 NACL_PRId32 ", data=%p)\n", pp_error, data ? &(*data)[0] : 0));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000975 DCHECK(translate_thread_.get());
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100976
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000977 translate_thread_->PutBytes(data, pp_error);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000978 // If pp_error > 0, then it represents the number of bytes received.
979 if (data && pp_error > 0) {
980 pexe_size_ += pp_error;
981 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000982}
983
984StreamCallback PnaclCoordinator::GetCallback() {
985 return callback_factory_.NewCallbackWithOutput(
986 &PnaclCoordinator::BitcodeStreamGotData);
987}
988
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000989void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error,
990 int64_t bytes_compiled) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000991 pexe_bytes_compiled_ += bytes_compiled;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100992 // If we don't know the expected total yet, ask.
993 if (!ExpectedProgressKnown()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000994 int64_t amount_downloaded; // dummy variable.
995 streaming_downloader_->GetDownloadProgress(&amount_downloaded,
996 &expected_pexe_size_);
997 }
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100998 // Hold off reporting the last few bytes of progress, since we don't know
999 // when they are actually completely compiled. "bytes_compiled" only means
1000 // that bytes were sent to the compiler.
1001 if (ExpectedProgressKnown()) {
1002 if (!ShouldDelayProgressEvent()) {
1003 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
1004 pexe_url_,
1005 plugin::Plugin::LENGTH_IS_COMPUTABLE,
1006 pexe_bytes_compiled_,
1007 expected_pexe_size_);
1008 }
1009 } else {
1010 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
1011 pexe_url_,
1012 plugin::Plugin::LENGTH_IS_NOT_COMPUTABLE,
1013 pexe_bytes_compiled_,
1014 expected_pexe_size_);
1015 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001016}
1017
1018pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback(
1019 int64_t bytes_compiled) {
1020 return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled,
1021 bytes_compiled);
1022}
1023
1024void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded,
1025 int64_t* bytes_total) {
1026 *bytes_loaded = pexe_bytes_compiled_;
1027 *bytes_total = expected_pexe_size_;
1028}
1029
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001030void PnaclCoordinator::ObjectFileDidOpen(int32_t pp_error) {
1031 PLUGIN_PRINTF(("PnaclCoordinator::ObjectFileDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +01001032 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001033 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001034 ReportPpapiError(ERROR_PNACL_CREATE_TEMP,
1035 pp_error,
1036 "Failed to open scratch object file.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001037 return;
1038 }
Ben Murdocheb525c52013-07-10 11:40:50 +01001039 // Open the nexe file for connecting ld and sel_ldr.
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001040 // Start translation when done with this last step of setup!
Ben Murdocheb525c52013-07-10 11:40:50 +01001041 if (!use_new_cache_)
1042 // In the new cache case, the TempFile has already been created.
1043 temp_nexe_file_.reset(new TempFile(plugin_));
1044
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001045 pp::CompletionCallback cb =
1046 callback_factory_.NewCallback(&PnaclCoordinator::RunTranslate);
Ben Murdocheb525c52013-07-10 11:40:50 +01001047 temp_nexe_file_->Open(cb, true);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001048}
1049
1050void PnaclCoordinator::RunTranslate(int32_t pp_error) {
1051 PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +01001052 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001053 // Invoke llc followed by ld off the main thread. This allows use of
1054 // blocking RPCs that would otherwise block the JavaScript main thread.
1055 pp::CompletionCallback report_translate_finished =
1056 callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished);
1057
1058 CHECK(translate_thread_ != NULL);
1059 translate_thread_->RunTranslate(report_translate_finished,
1060 manifest_.get(),
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001061 obj_file_.get(),
1062 temp_nexe_file_.get(),
1063 &error_info_,
1064 resources_.get(),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001065 &pnacl_options_,
1066 this,
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001067 plugin_);
1068}
1069
1070} // namespace plugin