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"
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"
31 const char kPnaclTempDir
[] = "/.pnacl";
32 const uint32_t kCopyBufSize
= 512 << 10;
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
{
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.");
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
;
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
);
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/");
95 // Resolve the full URL to the file. Provide it with a platform-specific
97 nacl::string key_basename
= key
.substr(kFilesPrefix
.length());
98 return ResolveURL(PnaclUrls::PrependPlatformPrefix(key_basename
),
99 full_url
, error_info
);
103 NACL_DISALLOW_COPY_AND_ASSIGN(PnaclManifest
);
105 nacl::string manifest_base_url_
;
108 //////////////////////////////////////////////////////////////////////
110 //////////////////////////////////////////////////////////////////////
114 // Assume translation time metrics *can be* large.
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();
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
) {
148 const PPB_UMA_Private
* ptr
= GetUMAInterface();
149 if (ptr
== NULL
) return;
151 ptr
->HistogramCustomTimes(pp::Var(name
).pp_var(),
153 kTimeLargeMin
, kTimeLargeMax
,
157 void HistogramSizeKB(const std::string
& name
, int32_t kb
) {
160 const PPB_UMA_Private
* ptr
= GetUMAInterface();
161 if (ptr
== NULL
) return;
163 ptr
->HistogramCustomCounts(pp::Var(name
).pp_var(),
165 kSizeKBMin
, kSizeKBMax
,
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(),
177 kRatioMin
, kRatioMax
,
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
),
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(),
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);
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
223 CallbackSource
<FileStreamData
>::~CallbackSource() {}
225 PnaclCoordinator
* PnaclCoordinator::BitcodeToNative(
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
,
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());
254 PnaclCoordinator::PnaclCoordinator(
256 const nacl::string
& pexe_url
,
257 const PnaclOptions
& pnacl_options
,
258 const pp::CompletionCallback
& translate_notify_callback
)
259 : translate_finish_error_(PP_OK
),
261 translate_notify_callback_(translate_notify_callback
),
262 file_system_(new pp::FileSystem(plugin
, PP_FILESYSTEMTYPE_LOCALTEMPORARY
)),
263 manifest_(new PnaclManifest()),
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),
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
);
304 void PnaclCoordinator::ReportPpapiError(enum PluginErrorCode err_code
,
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());
313 void PnaclCoordinator::ExitWithError() {
314 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError (error_code=%d, "
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
);
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(),
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
,
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",
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();
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"));
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
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",
408 total_time
/ 1000000.0);
409 NexeReadDidOpen(PP_OK
);
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
);
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
) {
433 ERROR_PNACL_CACHE_FILEOPEN_NOACCESS
,
435 "PNaCl translation cache failed to open file for write "
439 if (pp_error
== PP_ERROR_NOQUOTA
) {
441 ERROR_PNACL_CACHE_FILEOPEN_NOQUOTA
,
443 "PNaCl translation cache failed to open file for write "
447 if (pp_error
== PP_ERROR_NOSPACE
) {
449 ERROR_PNACL_CACHE_FILEOPEN_NOSPACE
,
451 "PNaCl translation cache failed to open file for write "
455 if (pp_error
== PP_ERROR_NOTAFILE
) {
456 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_NOTAFILE
,
458 "PNaCl translation cache failed to open file for write."
459 " File already exists as a directory.");
462 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_OTHER
,
464 "PNaCl translation cache failed to open file for write.");
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
];
474 nacl::assert_cast
<int32_t>(read_wrapper
->Read(buf
, sizeof buf
));
475 // Hit EOF or something.
477 NexeWasCopiedToCache(PP_OK
);
481 PLUGIN_PRINTF(("PnaclCoordinator::CachedNexeOpenedForWrite read failed "
482 "(error=%" NACL_PRId32
")\n", num_read
));
483 NexeWasCopiedToCache(PP_ERROR_FAILED
);
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
);
503 if (pp_error
< PP_OK
) {
504 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial failed (err=%"
505 NACL_PRId32
")\n", pp_error
));
506 NexeWasCopiedToCache(pp_error
);
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
);
528 int64_t next_offset
= cur_offset
+ pp_error
;
529 char buf
[kCopyBufSize
];
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.
536 NexeWasCopiedToCache(PP_OK
);
540 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial read failed "
541 "(error=%" NACL_PRId32
")\n", num_read
));
542 NexeWasCopiedToCache(PP_ERROR_FAILED
);
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
558 pp::CompletionCallback cb
= callback_factory_
.NewCallback(
559 &PnaclCoordinator::CorruptCacheFileWasDeleted
, pp_error
);
560 cached_nexe_file_
->Delete(cb
);
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",
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
,
584 "Failed to copy translated nexe to cache (no quota).");
587 if (orig_pp_error
== PP_ERROR_NOSPACE
) {
588 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_NOSPACE
,
590 "Failed to copy translated nexe to cache (no space).");
593 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_OTHER
,
595 "Failed to copy translated nexe to cache.");
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
,
606 "Failed to finalize cached translation (no access).");
608 } else if (pp_error
!= PP_ERROR_FILEEXISTS
) {
609 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_RENAME_OTHER
,
611 "Failed to finalize cached translation.");
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",
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
,
647 "Failed to open translated nexe (not found).");
650 if (pp_error
== PP_ERROR_NOACCESS
) {
651 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOACCESS
,
653 "Failed to open translated nexe (no access).");
656 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_OTHER
,
658 "Failed to open translated nexe.");
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());
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
) {
674 ERROR_PNACL_RESOURCE_FETCH
,
675 nacl::string("The Portable Native Client (pnacl) component is not "
676 "installed. Please consult chrome://components for more "
681 // Loading resources (e.g. llc and ld nexes) is done with PnaclResources.
682 resources_
.reset(new PnaclResources(plugin_
,
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.
713 if (!off_the_record_
) {
714 if (use_new_cache_
) {
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
726 if (pp_error
== PP_ERROR_INPROGRESS
) {
728 ERROR_PNACL_CACHE_OPEN_INPROGRESS
,
730 "File system for PNaCl translation cache failed to open "
735 ERROR_PNACL_CACHE_OPEN_OTHER
,
737 "File system for PNaCl translation cache failed to open.");
741 // We don't have a cache, so do the non-cached codepath.
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
) {
752 ERROR_PNACL_CACHE_OPEN_NOACCESS
,
754 "File system for PNaCl translation cache failed to open "
758 if (pp_error
== PP_ERROR_NOQUOTA
) {
760 ERROR_PNACL_CACHE_OPEN_NOQUOTA
,
762 "File system for PNaCl translation cache failed to open "
766 if (pp_error
== PP_ERROR_NOSPACE
) {
768 ERROR_PNACL_CACHE_OPEN_NOSPACE
,
770 "File system for PNaCl translation cache failed to open "
774 ReportPpapiError(ERROR_PNACL_CACHE_OPEN_OTHER
,
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
) {
792 ERROR_PNACL_CACHE_DIRECTORY_CREATE
,
794 "PNaCl translation cache directory creation/check failed "
799 ERROR_PNACL_CACHE_DIRECTORY_CREATE
,
801 "PNaCl translation cache directory creation/check failed.");
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
) {
819 ERROR_PNACL_THREAD_CREATE
,
820 "PnaclCoordinator: could not allocate translation thread.");
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)) {
836 ERROR_PNACL_PEXE_FETCH_OTHER
,
837 nacl::string("PnaclCoordinator: failed to open stream ") + pexe_url_
);
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.
848 TranslateFinished(pp_error
);
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
);
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(),
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"));
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
);
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
,
913 if (pp_error
< PP_OK
) {
914 ReportPpapiError(ERROR_PNACL_CREATE_TEMP
, pp_error
,
915 nacl::string("GetNexeFd failed"));
918 temp_nexe_file_
.reset(new TempFile(plugin_
));
919 if (!temp_nexe_file_
->SetExistingFd(nexe_handle_
)) {
921 ERROR_PNACL_CREATE_TEMP
,
923 "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
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);
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
);
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).");
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(),
997 translate_thread_
->AbortSubprocesses();
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
,
1039 plugin::Plugin::LENGTH_IS_COMPUTABLE
,
1040 pexe_bytes_compiled_
,
1041 expected_pexe_size_
);
1044 plugin_
->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress
,
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
,
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
,
1070 "Failed to open scratch object file.");
1071 if (use_new_cache_
) {
1072 plugin_
->nacl_interface()->ReportTranslationFinished(
1073 plugin_
->pp_instance(),
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
,
1101 temp_nexe_file_
.get(),
1109 } // namespace plugin