cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / ppapi / native_client / src / trusted / plugin / pnacl_coordinator.cc
blob10311a9f436ffb4b78ac7c3ef83d535f0595dbcb
1 // 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.
5 #include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
7 #include <utility>
8 #include <vector>
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"
13 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
15 #include "ppapi/c/pp_bool.h"
16 #include "ppapi/c/pp_errors.h"
17 #include "ppapi/c/ppb_file_io.h"
18 #include "ppapi/c/private/ppb_uma_private.h"
19 #include "ppapi/cpp/file_io.h"
21 #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"
30 namespace {
31 const char kPnaclTempDir[] = "/.pnacl";
32 const uint32_t kCopyBufSize = 512 << 10;
35 namespace plugin {
37 //////////////////////////////////////////////////////////////////////
38 // Pnacl-specific manifest support.
39 //////////////////////////////////////////////////////////////////////
41 // 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.
46 class PnaclManifest : public Manifest {
47 public:
48 PnaclManifest() : manifest_base_url_(PnaclUrls::GetBaseUrl()) { }
49 virtual ~PnaclManifest() { }
51 virtual bool GetProgramURL(nacl::string* full_url,
52 PnaclOptions* pnacl_options,
53 ErrorInfo* error_info) const {
54 // Does not contain program urls.
55 UNREFERENCED_PARAMETER(full_url);
56 UNREFERENCED_PARAMETER(pnacl_options);
57 UNREFERENCED_PARAMETER(error_info);
58 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;
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;
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;
81 virtual bool ResolveKey(const nacl::string& key,
82 nacl::string* full_url,
83 PnaclOptions* pnacl_options,
84 ErrorInfo* error_info) const {
85 // All of the component files are native (do not require pnacl translate).
86 pnacl_options->set_translate(false);
87 // 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;
95 // Resolve the full URL to the file. Provide it with a platform-specific
96 // prefix.
97 nacl::string key_basename = key.substr(kFilesPrefix.length());
98 return ResolveURL(PnaclUrls::PrependPlatformPrefix(key_basename),
99 full_url, error_info);
102 private:
103 NACL_DISALLOW_COPY_AND_ASSIGN(PnaclManifest);
105 nacl::string manifest_base_url_;
108 //////////////////////////////////////////////////////////////////////
109 // UMA stat helpers.
110 //////////////////////////////////////////////////////////////////////
112 namespace {
114 // Assume translation time metrics *can be* large.
115 // Up to 12 minutes.
116 const int64_t kTimeLargeMin = 10; // in ms
117 const int64_t kTimeLargeMax = 720000; // in ms
118 const uint32_t kTimeLargeBuckets = 100;
120 const int32_t kSizeKBMin = 1;
121 const int32_t kSizeKBMax = 512*1024; // very large .pexe / .nexe.
122 const uint32_t kSizeKBBuckets = 100;
124 const int32_t kRatioMin = 10;
125 const int32_t kRatioMax = 10*100; // max of 10x difference.
126 const uint32_t kRatioBuckets = 100;
128 const int32_t kKBPSMin = 1;
129 const int32_t kKBPSMax = 30*1000; // max of 30 MB / sec.
130 const uint32_t kKBPSBuckets = 100;
132 const PPB_UMA_Private* uma_interface = NULL;
134 const PPB_UMA_Private* GetUMAInterface() {
135 if (uma_interface != NULL) {
136 return uma_interface;
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;
145 void HistogramTime(const std::string& name, int64_t ms) {
146 if (ms < 0) return;
148 const PPB_UMA_Private* ptr = GetUMAInterface();
149 if (ptr == NULL) return;
151 ptr->HistogramCustomTimes(pp::Var(name).pp_var(),
153 kTimeLargeMin, kTimeLargeMax,
154 kTimeLargeBuckets);
157 void HistogramSizeKB(const std::string& name, int32_t kb) {
158 if (kb < 0) return;
160 const PPB_UMA_Private* ptr = GetUMAInterface();
161 if (ptr == NULL) return;
163 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
165 kSizeKBMin, kSizeKBMax,
166 kSizeKBBuckets);
169 void HistogramRatio(const std::string& name, int64_t a, int64_t b) {
170 if (a < 0 || b <= 0) return;
172 const PPB_UMA_Private* ptr = GetUMAInterface();
173 if (ptr == NULL) return;
175 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
176 100 * a / b,
177 kRatioMin, kRatioMax,
178 kRatioBuckets);
181 void HistogramKBPerSec(const std::string& name, double kb, double s) {
182 if (kb < 0.0 || s <= 0.0) return;
184 const PPB_UMA_Private* ptr = GetUMAInterface();
185 if (ptr == NULL) return;
187 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
188 static_cast<int64_t>(kb / s),
189 kKBPSMin, kKBPSMax,
190 kKBPSBuckets);
193 void 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);
200 // Opt level is expected to be 0 to 3. Treating 4 as unknown.
201 const int8_t kOptUnknown = 4;
203 void 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;
209 ptr->HistogramEnumeration(pp::Var("NaCl.Options.PNaCl.OptLevel").pp_var(),
210 opt_level, kOptUnknown+1);
213 } // namespace
216 //////////////////////////////////////////////////////////////////////
217 // The coordinator class.
218 //////////////////////////////////////////////////////////////////////
220 // Out-of-line destructor to keep it from getting put in every .o where
221 // callback_source.h is included
222 template<>
223 CallbackSource<FileStreamData>::~CallbackSource() {}
225 PnaclCoordinator* PnaclCoordinator::BitcodeToNative(
226 Plugin* plugin,
227 const nacl::string& pexe_url,
228 const PnaclOptions& pnacl_options,
229 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,
234 pnacl_options,
235 translate_notify_callback);
236 coordinator->pnacl_init_time_ = NaClGetTimeOfDayMicroseconds();
237 coordinator->off_the_record_ =
238 plugin->nacl_interface()->IsOffTheRecord();
239 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (manifest=%p, "
240 "off_the_record=%d)\n",
241 reinterpret_cast<const void*>(coordinator->manifest_.get()),
242 coordinator->off_the_record_));
244 // First check that PNaCl is installed.
245 pp::CompletionCallback pnacl_installed_cb =
246 coordinator->callback_factory_.NewCallback(
247 &PnaclCoordinator::DidCheckPnaclInstalled);
248 plugin->nacl_interface()->EnsurePnaclInstalled(
249 plugin->pp_instance(),
250 pnacl_installed_cb.pp_completion_callback());
251 return coordinator;
254 PnaclCoordinator::PnaclCoordinator(
255 Plugin* plugin,
256 const nacl::string& pexe_url,
257 const PnaclOptions& pnacl_options,
258 const pp::CompletionCallback& translate_notify_callback)
259 : translate_finish_error_(PP_OK),
260 plugin_(plugin),
261 translate_notify_callback_(translate_notify_callback),
262 file_system_(new pp::FileSystem(plugin, PP_FILESYSTEMTYPE_LOCALTEMPORARY)),
263 manifest_(new PnaclManifest()),
264 pexe_url_(pexe_url),
265 pnacl_options_(pnacl_options),
266 use_new_cache_(true),
267 is_cache_hit_(PP_FALSE),
268 nexe_handle_(PP_kInvalidFileHandle),
269 error_already_reported_(false),
270 off_the_record_(false),
271 pnacl_init_time_(0),
272 pexe_size_(0),
273 pexe_bytes_compiled_(0),
274 expected_pexe_size_(-1) {
275 PLUGIN_PRINTF(("PnaclCoordinator::PnaclCoordinator (this=%p, plugin=%p)\n",
276 static_cast<void*>(this), static_cast<void*>(plugin)));
277 callback_factory_.Initialize(this);
278 if (getenv("PNACL_USE_OLD_CACHE")) {
279 PLUGIN_PRINTF(("PnaclCoordinator using old translation cache\n"));
280 use_new_cache_ = false;
284 PnaclCoordinator::~PnaclCoordinator() {
285 PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, "
286 "translate_thread=%p\n",
287 static_cast<void*>(this), translate_thread_.get()));
288 // Stopping the translate thread will cause the translate thread to try to
289 // run translation_complete_callback_ on the main thread. This destructor is
290 // running from the main thread, and by the time it exits, callback_factory_
291 // will have been destroyed. This will result in the cancellation of
292 // translation_complete_callback_, so no notification will be delivered.
293 if (translate_thread_.get() != NULL) {
294 translate_thread_->AbortSubprocesses();
298 void PnaclCoordinator::ReportNonPpapiError(enum PluginErrorCode err_code,
299 const nacl::string& message) {
300 error_info_.SetReport(err_code, message);
301 ExitWithError();
304 void PnaclCoordinator::ReportPpapiError(enum PluginErrorCode err_code,
305 int32_t pp_error,
306 const nacl::string& message) {
307 nacl::stringstream ss;
308 ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ").";
309 error_info_.SetReport(err_code, ss.str());
310 ExitWithError();
313 void PnaclCoordinator::ExitWithError() {
314 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError (error_code=%d, "
315 "message='%s')\n",
316 error_info_.error_code(),
317 error_info_.message().c_str()));
318 plugin_->ReportLoadError(error_info_);
319 // Free all the intermediate callbacks we ever created.
320 // Note: this doesn't *cancel* the callbacks from the factories attached
321 // to the various helper classes (e.g., pnacl_resources). Thus, those
322 // callbacks may still run asynchronously. We let those run but ignore
323 // any other errors they may generate so that they do not end up running
324 // translate_notify_callback_, which has already been freed.
325 callback_factory_.CancelAll();
326 if (!error_already_reported_) {
327 error_already_reported_ = true;
328 translate_notify_callback_.Run(PP_ERROR_FAILED);
329 } else {
330 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError an earlier error was "
331 "already reported -- Skipping.\n"));
335 // Signal that Pnacl translation completed normally.
336 void PnaclCoordinator::TranslateFinished(int32_t pp_error) {
337 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%"
338 NACL_PRId32 ")\n", pp_error));
339 // Bail out if there was an earlier error (e.g., pexe load failure),
340 // or if there is an error from the translation thread.
341 if (translate_finish_error_ != PP_OK || pp_error != PP_OK) {
342 if (use_new_cache_) {
343 plugin_->nacl_interface()->ReportTranslationFinished(
344 plugin_->pp_instance(),
345 PP_FALSE);
347 ExitWithError();
348 return;
350 // Send out one last progress event, to finish up the progress events
351 // that were delayed (see the delay inserted in BitcodeGotCompiled).
352 if (ExpectedProgressKnown()) {
353 pexe_bytes_compiled_ = expected_pexe_size_;
354 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
355 pexe_url_,
356 plugin::Plugin::LENGTH_IS_COMPUTABLE,
357 pexe_bytes_compiled_,
358 expected_pexe_size_);
361 // If there are no errors, report stats from this thread (the main thread).
362 HistogramOptLevel(pnacl_options_.opt_level());
363 const plugin::PnaclTimeStats& time_stats = translate_thread_->GetTimeStats();
364 HistogramTime("NaCl.Perf.PNaClLoadTime.LoadCompiler",
365 time_stats.pnacl_llc_load_time / NACL_MICROS_PER_MILLI);
366 HistogramTime("NaCl.Perf.PNaClLoadTime.CompileTime",
367 time_stats.pnacl_compile_time / NACL_MICROS_PER_MILLI);
368 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
369 pexe_size_ / 1024.0,
370 time_stats.pnacl_compile_time / 1000000.0);
371 HistogramTime("NaCl.Perf.PNaClLoadTime.LoadLinker",
372 time_stats.pnacl_ld_load_time / NACL_MICROS_PER_MILLI);
373 HistogramTime("NaCl.Perf.PNaClLoadTime.LinkTime",
374 time_stats.pnacl_link_time / NACL_MICROS_PER_MILLI);
375 HistogramSizeKB("NaCl.Perf.Size.Pexe",
376 static_cast<int64_t>(pexe_size_ / 1024));
378 struct nacl_abi_stat stbuf;
379 struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc();
380 int stat_ret;
381 if (0 != (stat_ret = (*((struct NaClDescVtbl const *) desc->base.vtbl)->
382 Fstat)(desc, &stbuf))) {
383 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished can't stat nexe.\n"));
384 } else {
385 size_t nexe_size = stbuf.nacl_abi_st_size;
386 HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe",
387 static_cast<int64_t>(nexe_size / 1024));
388 HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct", pexe_size_, nexe_size);
391 // The nexe is written to the temp_nexe_file_. We must Reset() the file
392 // pointer to be able to read it again from the beginning.
393 temp_nexe_file_->Reset();
395 if (use_new_cache_) {
396 // Report to the browser that translation finished. The browser will take
397 // care of caching.
398 plugin_->nacl_interface()->ReportTranslationFinished(
399 plugin_->pp_instance(), PP_TRUE);
401 // These can maybe move up with the rest of the UMA stats when we remove
402 // the old cache code
403 int64_t total_time = NaClGetTimeOfDayMicroseconds() - pnacl_init_time_;
404 HistogramTime("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
405 total_time / NACL_MICROS_PER_MILLI);
406 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
407 pexe_size_ / 1024.0,
408 total_time / 1000000.0);
409 NexeReadDidOpen(PP_OK);
410 return;
412 if (pnacl_options_.HasCacheKey() && cached_nexe_file_ != NULL) {
413 // We are using a cache, but had a cache miss, which is why we did the
414 // translation. Reset cached_nexe_file_ to have a random name,
415 // for scratch purposes, before renaming to the final cache_identity.
416 cached_nexe_file_.reset(new LocalTempFile(plugin_, file_system_.get(),
417 nacl::string(kPnaclTempDir)));
418 pp::CompletionCallback cb = callback_factory_.NewCallback(
419 &PnaclCoordinator::CachedNexeOpenedForWrite);
420 cached_nexe_file_->OpenWrite(cb);
421 } else {
422 // For now, tolerate bitcode that is missing a cache identity, and
423 // tolerate the lack of caching in incognito mode.
424 PLUGIN_PRINTF(("PnaclCoordinator -- not caching.\n"));
425 NexeReadDidOpen(PP_OK);
429 void PnaclCoordinator::CachedNexeOpenedForWrite(int32_t pp_error) {
430 if (pp_error != PP_OK) {
431 if (pp_error == PP_ERROR_NOACCESS) {
432 ReportPpapiError(
433 ERROR_PNACL_CACHE_FILEOPEN_NOACCESS,
434 pp_error,
435 "PNaCl translation cache failed to open file for write "
436 "(no access).");
437 return;
439 if (pp_error == PP_ERROR_NOQUOTA) {
440 ReportPpapiError(
441 ERROR_PNACL_CACHE_FILEOPEN_NOQUOTA,
442 pp_error,
443 "PNaCl translation cache failed to open file for write "
444 "(no quota).");
445 return;
447 if (pp_error == PP_ERROR_NOSPACE) {
448 ReportPpapiError(
449 ERROR_PNACL_CACHE_FILEOPEN_NOSPACE,
450 pp_error,
451 "PNaCl translation cache failed to open file for write "
452 "(no space).");
453 return;
455 if (pp_error == PP_ERROR_NOTAFILE) {
456 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_NOTAFILE,
457 pp_error,
458 "PNaCl translation cache failed to open file for write."
459 " File already exists as a directory.");
460 return;
462 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_OTHER,
463 pp_error,
464 "PNaCl translation cache failed to open file for write.");
465 return;
468 // Copy the contents from temp_nexe_file_ -> cached_nexe_file_,
469 // then rename the cached_nexe_file_ file to the cache id.
470 int64_t cur_offset = 0;
471 nacl::DescWrapper* read_wrapper = temp_nexe_file_->read_wrapper();
472 char buf[kCopyBufSize];
473 int32_t num_read =
474 nacl::assert_cast<int32_t>(read_wrapper->Read(buf, sizeof buf));
475 // Hit EOF or something.
476 if (num_read == 0) {
477 NexeWasCopiedToCache(PP_OK);
478 return;
480 if (num_read < 0) {
481 PLUGIN_PRINTF(("PnaclCoordinator::CachedNexeOpenedForWrite read failed "
482 "(error=%" NACL_PRId32 ")\n", num_read));
483 NexeWasCopiedToCache(PP_ERROR_FAILED);
484 return;
486 pp::CompletionCallback cb = callback_factory_.NewCallback(
487 &PnaclCoordinator::DidCopyNexeToCachePartial, num_read, cur_offset);
488 cached_nexe_file_->write_file_io()->Write(cur_offset, buf, num_read, cb);
491 void PnaclCoordinator::DidCopyNexeToCachePartial(int32_t pp_error,
492 int32_t num_read_prev,
493 int64_t cur_offset) {
494 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial "
495 "(pp_error=%" NACL_PRId32 ", num_read_prev=%" NACL_PRId32
496 ", cur_offset=%" NACL_PRId64 ").\n",
497 pp_error, num_read_prev, cur_offset));
498 // Assume we are done.
499 if (pp_error == PP_OK) {
500 NexeWasCopiedToCache(PP_OK);
501 return;
503 if (pp_error < PP_OK) {
504 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial failed (err=%"
505 NACL_PRId32 ")\n", pp_error));
506 NexeWasCopiedToCache(pp_error);
507 return;
510 // Check if we wrote as much as we read.
511 nacl::DescWrapper* read_wrapper = temp_nexe_file_->read_wrapper();
512 if (pp_error != num_read_prev) {
513 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial partial "
514 "write (bytes_written=%" NACL_PRId32 " vs "
515 "read=%" NACL_PRId32 ")\n", pp_error, num_read_prev));
516 CHECK(pp_error < num_read_prev);
517 // Seek back to re-read the bytes that were not written.
518 nacl_off64_t seek_result =
519 read_wrapper->Seek(pp_error - num_read_prev, SEEK_CUR);
520 if (seek_result < 0) {
521 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial seek failed "
522 "(err=%" NACL_PRId64 ")\n", seek_result));
523 NexeWasCopiedToCache(PP_ERROR_FAILED);
524 return;
528 int64_t next_offset = cur_offset + pp_error;
529 char buf[kCopyBufSize];
530 int32_t num_read =
531 nacl::assert_cast<int32_t>(read_wrapper->Read(buf, sizeof buf));
532 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial read (bytes=%"
533 NACL_PRId32 ")\n", num_read));
534 // Hit EOF or something.
535 if (num_read == 0) {
536 NexeWasCopiedToCache(PP_OK);
537 return;
539 if (num_read < 0) {
540 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial read failed "
541 "(error=%" NACL_PRId32 ")\n", num_read));
542 NexeWasCopiedToCache(PP_ERROR_FAILED);
543 return;
545 pp::CompletionCallback cb = callback_factory_.NewCallback(
546 &PnaclCoordinator::DidCopyNexeToCachePartial, num_read, next_offset);
547 PLUGIN_PRINTF(("PnaclCoordinator::CopyNexeToCache Writing ("
548 "bytes=%" NACL_PRId32 ", buf=%p, file_io=%p)\n", num_read, buf,
549 cached_nexe_file_->write_file_io()));
550 cached_nexe_file_->write_file_io()->Write(next_offset, buf, num_read, cb);
553 void PnaclCoordinator::NexeWasCopiedToCache(int32_t pp_error) {
554 if (pp_error != PP_OK) {
555 // Try to delete the partially written not-yet-committed cache file before
556 // returning. We pass the current pp_error along so that it can be reported
557 // before returning.
558 pp::CompletionCallback cb = callback_factory_.NewCallback(
559 &PnaclCoordinator::CorruptCacheFileWasDeleted, pp_error);
560 cached_nexe_file_->Delete(cb);
561 return;
563 // Rename the cached_nexe_file_ file to the cache id, to finalize.
564 pp::CompletionCallback cb =
565 callback_factory_.NewCallback(&PnaclCoordinator::NexeFileWasRenamed);
566 cached_nexe_file_->Rename(pnacl_options_.GetCacheKey(), cb);
569 void PnaclCoordinator::CorruptCacheFileWasDeleted(int32_t delete_pp_error,
570 int32_t orig_pp_error) {
571 if (delete_pp_error != PP_OK) {
572 // The cache file was certainly already opened by the time we tried
573 // to write to it, so it should certainly be deletable.
574 PLUGIN_PRINTF(("PnaclCoordinator::CorruptCacheFileWasDeleted "
575 "delete failed with pp_error=%" NACL_PRId32 "\n",
576 delete_pp_error));
577 // fall through and report the original error.
579 // Report the original error that caused us to consider the
580 // cache file corrupted.
581 if (orig_pp_error == PP_ERROR_NOQUOTA) {
582 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_NOQUOTA,
583 orig_pp_error,
584 "Failed to copy translated nexe to cache (no quota).");
585 return;
587 if (orig_pp_error == PP_ERROR_NOSPACE) {
588 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_NOSPACE,
589 orig_pp_error,
590 "Failed to copy translated nexe to cache (no space).");
591 return;
593 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_OTHER,
594 orig_pp_error,
595 "Failed to copy translated nexe to cache.");
596 return;
599 void PnaclCoordinator::NexeFileWasRenamed(int32_t pp_error) {
600 PLUGIN_PRINTF(("PnaclCoordinator::NexeFileWasRenamed (pp_error=%"
601 NACL_PRId32 ")\n", pp_error));
602 if (pp_error != PP_OK) {
603 if (pp_error == PP_ERROR_NOACCESS) {
604 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_RENAME_NOACCESS,
605 pp_error,
606 "Failed to finalize cached translation (no access).");
607 return;
608 } else if (pp_error != PP_ERROR_FILEEXISTS) {
609 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_RENAME_OTHER,
610 pp_error,
611 "Failed to finalize cached translation.");
612 return;
613 } else { // pp_error == PP_ERROR_FILEEXISTS.
614 // NOTE: if the file already existed, it looks like the rename will
615 // happily succeed. However, we should add a test for this.
616 // Could be a hash collision, or it could also be two tabs racing to
617 // translate the same pexe. We may want UMA stats to know if this happens.
618 // For now, assume that it is a race and try to continue.
619 // If there is truly a corrupted file, then sel_ldr should prevent the
620 // file from loading due to the file size not matching the ELF header.
621 PLUGIN_PRINTF(("PnaclCoordinator::NexeFileWasRenamed file existed\n"));
625 cached_nexe_file_->FinishRename();
627 int64_t total_time = NaClGetTimeOfDayMicroseconds() - pnacl_init_time_;
628 HistogramTime("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
629 total_time / NACL_MICROS_PER_MILLI);
630 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
631 pexe_size_ / 1024.0,
632 total_time / 1000000.0);
634 // Open the cache file for reading.
635 pp::CompletionCallback cb =
636 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen);
637 cached_nexe_file_->OpenRead(cb);
640 void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) {
641 PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%"
642 NACL_PRId32 ")\n", pp_error));
643 if (pp_error != PP_OK) {
644 if (pp_error == PP_ERROR_FILENOTFOUND) {
645 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOTFOUND,
646 pp_error,
647 "Failed to open translated nexe (not found).");
648 return;
650 if (pp_error == PP_ERROR_NOACCESS) {
651 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOACCESS,
652 pp_error,
653 "Failed to open translated nexe (no access).");
654 return;
656 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_OTHER,
657 pp_error,
658 "Failed to open translated nexe.");
659 return;
662 // Transfer ownership of cache/temp file's wrapper to the coordinator.
663 if (cached_nexe_file_ != NULL) {
664 translated_fd_.reset(cached_nexe_file_->release_read_wrapper());
665 } else {
666 translated_fd_.reset(temp_nexe_file_->release_read_wrapper());
668 translate_notify_callback_.Run(pp_error);
671 void PnaclCoordinator::DidCheckPnaclInstalled(int32_t pp_error) {
672 if (pp_error != PP_OK) {
673 ReportNonPpapiError(
674 ERROR_PNACL_RESOURCE_FETCH,
675 nacl::string("The Portable Native Client (pnacl) component is not "
676 "installed. Please consult chrome://components for more "
677 "information."));
678 return;
681 // Loading resources (e.g. llc and ld nexes) is done with PnaclResources.
682 resources_.reset(new PnaclResources(plugin_,
683 this,
684 this->manifest_.get()));
685 CHECK(resources_ != NULL);
687 // The first step of loading resources: read the resource info file.
688 pp::CompletionCallback resource_info_read_cb =
689 callback_factory_.NewCallback(
690 &PnaclCoordinator::ResourceInfoWasRead);
691 resources_->ReadResourceInfo(PnaclUrls::GetResourceInfoUrl(),
692 resource_info_read_cb);
695 void PnaclCoordinator::ResourceInfoWasRead(int32_t pp_error) {
696 PLUGIN_PRINTF(("PluginCoordinator::ResourceInfoWasRead (pp_error=%"
697 NACL_PRId32 ")\n", pp_error));
698 // Second step of loading resources: call StartLoad.
699 pp::CompletionCallback resources_cb =
700 callback_factory_.NewCallback(&PnaclCoordinator::ResourcesDidLoad);
701 resources_->StartLoad(resources_cb);
704 void PnaclCoordinator::ResourcesDidLoad(int32_t pp_error) {
705 PLUGIN_PRINTF(("PnaclCoordinator::ResourcesDidLoad (pp_error=%"
706 NACL_PRId32 ")\n", pp_error));
707 if (pp_error != PP_OK) {
708 // Finer-grained error code should have already been reported by
709 // the PnaclResources class.
710 return;
713 if (!off_the_record_) {
714 if (use_new_cache_) {
715 OpenBitcodeStream();
716 } else {
717 // Open the local temporary FS to see if we get a hit in the cache.
718 pp::CompletionCallback cb =
719 callback_factory_.NewCallback(&PnaclCoordinator::FileSystemDidOpen);
720 int32_t open_error = file_system_->Open(0, cb);
721 if (open_error != PP_OK_COMPLETIONPENDING) {
722 // At this point, no async request has kicked off to check for
723 // permissions, space, etc., so the only error that can be detected
724 // now is that an open() is already in progress (or a really terrible
725 // error).
726 if (pp_error == PP_ERROR_INPROGRESS) {
727 ReportPpapiError(
728 ERROR_PNACL_CACHE_OPEN_INPROGRESS,
729 pp_error,
730 "File system for PNaCl translation cache failed to open "
731 "(in progress).");
732 return;
734 ReportPpapiError(
735 ERROR_PNACL_CACHE_OPEN_OTHER,
736 pp_error,
737 "File system for PNaCl translation cache failed to open.");
740 } else {
741 // We don't have a cache, so do the non-cached codepath.
742 OpenBitcodeStream();
746 void PnaclCoordinator::FileSystemDidOpen(int32_t pp_error) {
747 PLUGIN_PRINTF(("PnaclCoordinator::FileSystemDidOpen (pp_error=%"
748 NACL_PRId32 ")\n", pp_error));
749 if (pp_error != PP_OK) {
750 if (pp_error == PP_ERROR_NOACCESS) {
751 ReportPpapiError(
752 ERROR_PNACL_CACHE_OPEN_NOACCESS,
753 pp_error,
754 "File system for PNaCl translation cache failed to open "
755 "(no access).");
756 return;
758 if (pp_error == PP_ERROR_NOQUOTA) {
759 ReportPpapiError(
760 ERROR_PNACL_CACHE_OPEN_NOQUOTA,
761 pp_error,
762 "File system for PNaCl translation cache failed to open "
763 "(no quota).");
764 return;
766 if (pp_error == PP_ERROR_NOSPACE) {
767 ReportPpapiError(
768 ERROR_PNACL_CACHE_OPEN_NOSPACE,
769 pp_error,
770 "File system for PNaCl translation cache failed to open "
771 "(no space).");
772 return;
774 ReportPpapiError(ERROR_PNACL_CACHE_OPEN_OTHER,
775 pp_error,
776 "File system for PNaCl translation cache failed to open.");
778 dir_ref_.reset(new pp::FileRef(*file_system_, kPnaclTempDir));
779 // Attempt to create the directory.
780 pp::CompletionCallback cb =
781 callback_factory_.NewCallback(&PnaclCoordinator::DirectoryWasCreated);
782 dir_ref_->MakeDirectory(cb);
785 void PnaclCoordinator::DirectoryWasCreated(int32_t pp_error) {
786 PLUGIN_PRINTF(("PnaclCoordinator::DirectoryWasCreated (pp_error=%"
787 NACL_PRId32 ")\n", pp_error));
788 if (pp_error != PP_ERROR_FILEEXISTS && pp_error != PP_OK) {
789 // Directory did not exist and could not be created.
790 if (pp_error == PP_ERROR_NOACCESS) {
791 ReportPpapiError(
792 ERROR_PNACL_CACHE_DIRECTORY_CREATE,
793 pp_error,
794 "PNaCl translation cache directory creation/check failed "
795 "(no access).");
796 return;
798 ReportPpapiError(
799 ERROR_PNACL_CACHE_DIRECTORY_CREATE,
800 pp_error,
801 "PNaCl translation cache directory creation/check failed.");
802 return;
804 OpenBitcodeStream();
807 void PnaclCoordinator::OpenBitcodeStream() {
808 // Now open the pexe stream.
809 streaming_downloader_.reset(new FileDownloader());
810 streaming_downloader_->Initialize(plugin_);
812 // Even though we haven't started downloading, create the translation
813 // thread object immediately. This ensures that any pieces of the file
814 // that get downloaded before the compilation thread is accepting
815 // SRPCs won't get dropped.
816 translate_thread_.reset(new PnaclTranslateThread());
817 if (translate_thread_ == NULL) {
818 ReportNonPpapiError(
819 ERROR_PNACL_THREAD_CREATE,
820 "PnaclCoordinator: could not allocate translation thread.");
821 return;
823 if (!use_new_cache_) {
824 // We also want to open the object file now so the
825 // translator can start writing to it during streaming translation.
826 obj_file_.reset(new TempFile(plugin_));
827 pp::CompletionCallback obj_cb =
828 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen);
829 obj_file_->Open(obj_cb, true);
832 pp::CompletionCallback cb =
833 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidOpen);
834 if (!streaming_downloader_->OpenStream(pexe_url_, cb, this)) {
835 ReportNonPpapiError(
836 ERROR_PNACL_PEXE_FETCH_OTHER,
837 nacl::string("PnaclCoordinator: failed to open stream ") + pexe_url_);
838 return;
842 void PnaclCoordinator::BitcodeStreamDidOpen(int32_t pp_error) {
843 if (pp_error != PP_OK) {
844 BitcodeStreamDidFinish(pp_error);
845 // In the new cache case, we have not spun up the translation process yet,
846 // so we need to call TranslateFinished here.
847 if (use_new_cache_)
848 TranslateFinished(pp_error);
849 return;
852 if (!off_the_record_ || use_new_cache_) {
853 // Get the cache key and try to open an existing entry.
854 nacl::string headers = streaming_downloader_->GetResponseHeaders();
855 NaClHttpResponseHeaders parser;
856 parser.Parse(headers);
857 nacl::string cache_validators = parser.GetCacheValidators();
858 if (parser.CacheControlNoStore() || cache_validators.empty()) {
859 // We can't cache in this case.
860 pnacl_options_.set_cache_validators("");
861 CachedFileDidOpen(PP_ERROR_FAILED);
862 return;
863 } else {
864 nacl::string url = streaming_downloader_->url();
865 // For now, combine the cache_validators + the URL as the key.
866 // When we change the cache backend to be not-origin-specific
867 // we should send the URL separately, and check in the browser's
868 // RenderViewHost / SiteInstance's IsSameWebsite() to prevent
869 // people from forging the URL for a different origin.
870 pnacl_options_.set_cache_validators(cache_validators + url);
872 if (use_new_cache_) {
873 pp::CompletionCallback cb =
874 callback_factory_.NewCallback(&PnaclCoordinator::NexeFdDidOpen);
875 int32_t nexe_fd_err =
876 plugin_->nacl_interface()->GetNexeFd(
877 plugin_->pp_instance(),
878 streaming_downloader_->url().c_str(),
879 // TODO(dschuff): Get this value from the pnacl json file after it
880 // rolls in from NaCl.
882 pnacl_options_.opt_level(),
883 parser.GetHeader("last-modified").c_str(),
884 parser.GetHeader("etag").c_str(),
885 &is_cache_hit_,
886 &nexe_handle_,
887 cb.pp_completion_callback());
888 if (nexe_fd_err < PP_OK_COMPLETIONPENDING) {
889 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, nexe_fd_err,
890 nacl::string("Call to GetNexeFd failed"));
891 return;
893 } else {
894 cached_nexe_file_.reset(new LocalTempFile(
895 plugin_, file_system_.get(),
896 nacl::string(kPnaclTempDir),
897 pnacl_options_.GetCacheKey()));
898 pp::CompletionCallback cb =
899 callback_factory_.NewCallback(&PnaclCoordinator::CachedFileDidOpen);
900 cached_nexe_file_->OpenRead(cb);
902 } else {
903 // No cache case.
904 CachedFileDidOpen(PP_ERROR_FAILED);
908 void PnaclCoordinator::NexeFdDidOpen(int32_t pp_error) {
909 PLUGIN_PRINTF(("PnaclCoordinator::NexeFdDidOpen (pp_error=%"
910 NACL_PRId32 ", hit=%d, handle=%d)\n", pp_error,
911 is_cache_hit_ == PP_TRUE,
912 nexe_handle_));
913 if (pp_error < PP_OK) {
914 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, pp_error,
915 nacl::string("GetNexeFd failed"));
916 return;
918 temp_nexe_file_.reset(new TempFile(plugin_));
919 if (!temp_nexe_file_->SetExistingFd(nexe_handle_)) {
920 ReportNonPpapiError(
921 ERROR_PNACL_CREATE_TEMP,
922 nacl::string(
923 "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
924 return;
926 HistogramEnumerateTranslationCache(is_cache_hit_);
928 if (is_cache_hit_ == PP_TRUE) {
929 // Cache hit -- no need to stream the rest of the file.
930 streaming_downloader_.reset(NULL);
931 // Open it for reading as the cached nexe file.
932 pp::CompletionCallback cb =
933 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen);
934 temp_nexe_file_->Open(cb, false);
935 } else {
936 // Open an object file first so the translator can start writing to it
937 // during streaming translation.
938 obj_file_.reset(new TempFile(plugin_));
939 pp::CompletionCallback obj_cb =
940 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen);
941 obj_file_->Open(obj_cb, true);
943 // Meanwhile, a miss means we know we need to stream the bitcode, so stream
944 // the rest of it now. (Calling FinishStreaming means that the downloader
945 // will begin handing data to the coordinator, which is safe any time after
946 // the translate_thread_ object has been initialized).
947 pp::CompletionCallback finish_cb = callback_factory_.NewCallback(
948 &PnaclCoordinator::BitcodeStreamDidFinish);
949 streaming_downloader_->FinishStreaming(finish_cb);
953 void PnaclCoordinator::CachedFileDidOpen(int32_t pp_error) {
954 PLUGIN_PRINTF(("PnaclCoordinator::CachedFileDidOpen (pp_error=%"
955 NACL_PRId32 ")\n", pp_error));
956 if (pp_error == PP_OK) {
957 // Cache hit -- no need to stream the rest of the file.
958 streaming_downloader_.reset(NULL);
959 HistogramEnumerateTranslationCache(true);
960 NexeReadDidOpen(PP_OK);
961 return;
963 // Otherwise, the cache file is missing so we must translate.
964 HistogramEnumerateTranslationCache(false);
966 // Continue streaming.
967 pp::CompletionCallback cb =
968 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidFinish);
969 streaming_downloader_->FinishStreaming(cb);
972 void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) {
973 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
974 NACL_PRId32 ")\n", pp_error));
975 if (pp_error != PP_OK) {
976 // Defer reporting the error and cleanup until after the translation
977 // thread returns, because it may be accessing the coordinator's
978 // objects or writing to the files.
979 translate_finish_error_ = pp_error;
980 if (pp_error == PP_ERROR_ABORTED) {
981 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_ABORTED,
982 "PnaclCoordinator: pexe load failed (aborted).");
984 if (pp_error == PP_ERROR_NOACCESS) {
985 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_NOACCESS,
986 "PnaclCoordinator: pexe load failed (no access).");
987 } else {
988 nacl::stringstream ss;
989 ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ").";
990 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_OTHER, ss.str());
992 if (use_new_cache_) {
993 plugin_->nacl_interface()->ReportTranslationFinished(
994 plugin_->pp_instance(),
995 PP_FALSE);
997 translate_thread_->AbortSubprocesses();
998 } else {
999 // Compare download completion pct (100% now), to compile completion pct.
1000 HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
1001 pexe_bytes_compiled_, pexe_size_);
1005 void PnaclCoordinator::BitcodeStreamGotData(int32_t pp_error,
1006 FileStreamData data) {
1007 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamGotData (pp_error=%"
1008 NACL_PRId32 ", data=%p)\n", pp_error, data ? &(*data)[0] : 0));
1009 DCHECK(translate_thread_.get());
1011 translate_thread_->PutBytes(data, pp_error);
1012 // If pp_error > 0, then it represents the number of bytes received.
1013 if (data && pp_error > 0) {
1014 pexe_size_ += pp_error;
1018 StreamCallback PnaclCoordinator::GetCallback() {
1019 return callback_factory_.NewCallbackWithOutput(
1020 &PnaclCoordinator::BitcodeStreamGotData);
1023 void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error,
1024 int64_t bytes_compiled) {
1025 pexe_bytes_compiled_ += bytes_compiled;
1026 // If we don't know the expected total yet, ask.
1027 if (!ExpectedProgressKnown()) {
1028 int64_t amount_downloaded; // dummy variable.
1029 streaming_downloader_->GetDownloadProgress(&amount_downloaded,
1030 &expected_pexe_size_);
1032 // Hold off reporting the last few bytes of progress, since we don't know
1033 // when they are actually completely compiled. "bytes_compiled" only means
1034 // that bytes were sent to the compiler.
1035 if (ExpectedProgressKnown()) {
1036 if (!ShouldDelayProgressEvent()) {
1037 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
1038 pexe_url_,
1039 plugin::Plugin::LENGTH_IS_COMPUTABLE,
1040 pexe_bytes_compiled_,
1041 expected_pexe_size_);
1043 } else {
1044 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
1045 pexe_url_,
1046 plugin::Plugin::LENGTH_IS_NOT_COMPUTABLE,
1047 pexe_bytes_compiled_,
1048 expected_pexe_size_);
1052 pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback(
1053 int64_t bytes_compiled) {
1054 return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled,
1055 bytes_compiled);
1058 void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded,
1059 int64_t* bytes_total) {
1060 *bytes_loaded = pexe_bytes_compiled_;
1061 *bytes_total = expected_pexe_size_;
1064 void PnaclCoordinator::ObjectFileDidOpen(int32_t pp_error) {
1065 PLUGIN_PRINTF(("PnaclCoordinator::ObjectFileDidOpen (pp_error=%"
1066 NACL_PRId32 ")\n", pp_error));
1067 if (pp_error != PP_OK) {
1068 ReportPpapiError(ERROR_PNACL_CREATE_TEMP,
1069 pp_error,
1070 "Failed to open scratch object file.");
1071 if (use_new_cache_) {
1072 plugin_->nacl_interface()->ReportTranslationFinished(
1073 plugin_->pp_instance(),
1074 PP_FALSE);
1076 return;
1078 // Open the nexe file for connecting ld and sel_ldr.
1079 // Start translation when done with this last step of setup!
1080 if (!use_new_cache_)
1081 // In the new cache case, the TempFile has already been created.
1082 temp_nexe_file_.reset(new TempFile(plugin_));
1084 pp::CompletionCallback cb =
1085 callback_factory_.NewCallback(&PnaclCoordinator::RunTranslate);
1086 temp_nexe_file_->Open(cb, true);
1089 void PnaclCoordinator::RunTranslate(int32_t pp_error) {
1090 PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%"
1091 NACL_PRId32 ")\n", pp_error));
1092 // Invoke llc followed by ld off the main thread. This allows use of
1093 // blocking RPCs that would otherwise block the JavaScript main thread.
1094 pp::CompletionCallback report_translate_finished =
1095 callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished);
1097 CHECK(translate_thread_ != NULL);
1098 translate_thread_->RunTranslate(report_translate_finished,
1099 manifest_.get(),
1100 obj_file_.get(),
1101 temp_nexe_file_.get(),
1102 &error_info_,
1103 resources_.get(),
1104 &pnacl_options_,
1105 this,
1106 plugin_);
1109 } // namespace plugin