pnacl_coordinator.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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. 4 5#include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h" 6 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" 13#include "native_client/src/trusted/service_runtime/include/sys/stat.h" 14 15#include "ppapi/c/pp_bool.h" 16#include "ppapi/c/pp_errors.h" 17#include "ppapi/c/private/ppb_uma_private.h" 18 19#include "ppapi/native_client/src/trusted/plugin/manifest.h" 20#include "ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.h" 21#include "ppapi/native_client/src/trusted/plugin/plugin.h" 22#include "ppapi/native_client/src/trusted/plugin/plugin_error.h" 23#include "ppapi/native_client/src/trusted/plugin/pnacl_translate_thread.h" 24#include "ppapi/native_client/src/trusted/plugin/service_runtime.h" 25#include "ppapi/native_client/src/trusted/plugin/temporary_file.h" 26 27namespace plugin { 28 29////////////////////////////////////////////////////////////////////// 30// Pnacl-specific manifest support. 31////////////////////////////////////////////////////////////////////// 32 33// The PNaCl linker gets file descriptors via the service runtime's 34// reverse service lookup. The reverse service lookup requires a manifest. 35// Normally, that manifest is an NMF containing mappings for shared libraries. 36// Here, we provide a manifest that redirects to PNaCl component files 37// that are part of Chrome. 38class PnaclManifest : public Manifest { 39 public: 40 PnaclManifest() : manifest_base_url_(PnaclUrls::GetBaseUrl()) { } 41 virtual ~PnaclManifest() { } 42 43 virtual bool GetProgramURL(nacl::string* full_url, 44 PnaclOptions* pnacl_options, 45 ErrorInfo* error_info) const { 46 // Does not contain program urls. 47 UNREFERENCED_PARAMETER(full_url); 48 UNREFERENCED_PARAMETER(pnacl_options); 49 UNREFERENCED_PARAMETER(error_info); 50 PLUGIN_PRINTF(("PnaclManifest does not contain a program\n")); 51 error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL, 52 "pnacl manifest does not contain a program."); 53 return false; 54 } 55 56 virtual bool ResolveURL(const nacl::string& relative_url, 57 nacl::string* full_url, 58 ErrorInfo* error_info) const { 59 // Does not do general URL resolution, simply appends relative_url to 60 // the end of manifest_base_url_. 61 UNREFERENCED_PARAMETER(error_info); 62 *full_url = manifest_base_url_ + relative_url; 63 return true; 64 } 65 66 virtual bool GetFileKeys(std::set<nacl::string>* keys) const { 67 // Does not support enumeration. 68 PLUGIN_PRINTF(("PnaclManifest does not support key enumeration\n")); 69 UNREFERENCED_PARAMETER(keys); 70 return false; 71 } 72 73 virtual bool ResolveKey(const nacl::string& key, 74 nacl::string* full_url, 75 PnaclOptions* pnacl_options, 76 ErrorInfo* error_info) const { 77 // All of the component files are native (do not require pnacl translate). 78 pnacl_options->set_translate(false); 79 // We can only resolve keys in the files/ namespace. 80 const nacl::string kFilesPrefix = "files/"; 81 size_t files_prefix_pos = key.find(kFilesPrefix); 82 if (files_prefix_pos == nacl::string::npos) { 83 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, 84 "key did not start with files/"); 85 return false; 86 } 87 // Resolve the full URL to the file. Provide it with a platform-specific 88 // prefix. 89 nacl::string key_basename = key.substr(kFilesPrefix.length()); 90 return ResolveURL(PnaclUrls::PrependPlatformPrefix(key_basename), 91 full_url, error_info); 92 } 93 94 private: 95 NACL_DISALLOW_COPY_AND_ASSIGN(PnaclManifest); 96 97 nacl::string manifest_base_url_; 98}; 99 100////////////////////////////////////////////////////////////////////// 101// UMA stat helpers. 102////////////////////////////////////////////////////////////////////// 103 104namespace { 105 106// Assume translation time metrics *can be* large. 107// Up to 12 minutes. 108const int64_t kTimeLargeMin = 10; // in ms 109const int64_t kTimeLargeMax = 720000; // in ms 110const uint32_t kTimeLargeBuckets = 100; 111 112const int32_t kSizeKBMin = 1; 113const int32_t kSizeKBMax = 512*1024; // very large .pexe / .nexe. 114const uint32_t kSizeKBBuckets = 100; 115 116const int32_t kRatioMin = 10; 117const int32_t kRatioMax = 10*100; // max of 10x difference. 118const uint32_t kRatioBuckets = 100; 119 120const int32_t kKBPSMin = 1; 121const int32_t kKBPSMax = 30*1000; // max of 30 MB / sec. 122const uint32_t kKBPSBuckets = 100; 123 124const PPB_UMA_Private* uma_interface = NULL; 125 126const PPB_UMA_Private* GetUMAInterface() { 127 if (uma_interface != NULL) { 128 return uma_interface; 129 } 130 pp::Module *module = pp::Module::Get(); 131 DCHECK(module); 132 uma_interface = static_cast<const PPB_UMA_Private*>( 133 module->GetBrowserInterface(PPB_UMA_PRIVATE_INTERFACE)); 134 return uma_interface; 135} 136 137void HistogramTime(const std::string& name, int64_t ms) { 138 if (ms < 0) return; 139 140 const PPB_UMA_Private* ptr = GetUMAInterface(); 141 if (ptr == NULL) return; 142 143 ptr->HistogramCustomTimes(pp::Var(name).pp_var(), 144 ms, 145 kTimeLargeMin, kTimeLargeMax, 146 kTimeLargeBuckets); 147} 148 149void HistogramSizeKB(const std::string& name, int32_t kb) { 150 if (kb < 0) return; 151 152 const PPB_UMA_Private* ptr = GetUMAInterface(); 153 if (ptr == NULL) return; 154 155 ptr->HistogramCustomCounts(pp::Var(name).pp_var(), 156 kb, 157 kSizeKBMin, kSizeKBMax, 158 kSizeKBBuckets); 159} 160 161void HistogramRatio(const std::string& name, int64_t a, int64_t b) { 162 if (a < 0 || b <= 0) return; 163 164 const PPB_UMA_Private* ptr = GetUMAInterface(); 165 if (ptr == NULL) return; 166 167 ptr->HistogramCustomCounts(pp::Var(name).pp_var(), 168 100 * a / b, 169 kRatioMin, kRatioMax, 170 kRatioBuckets); 171} 172 173void HistogramKBPerSec(const std::string& name, double kb, double s) { 174 if (kb < 0.0 || s <= 0.0) return; 175 176 const PPB_UMA_Private* ptr = GetUMAInterface(); 177 if (ptr == NULL) return; 178 179 ptr->HistogramCustomCounts(pp::Var(name).pp_var(), 180 static_cast<int64_t>(kb / s), 181 kKBPSMin, kKBPSMax, 182 kKBPSBuckets); 183} 184 185void HistogramEnumerateTranslationCache(bool hit) { 186 const PPB_UMA_Private* ptr = GetUMAInterface(); 187 if (ptr == NULL) return; 188 ptr->HistogramEnumeration(pp::Var("NaCl.Perf.PNaClCache.IsHit").pp_var(), 189 hit, 2); 190} 191 192// Opt level is expected to be 0 to 3. Treating 4 as unknown. 193const int8_t kOptUnknown = 4; 194 195void HistogramOptLevel(int8_t opt_level) { 196 const PPB_UMA_Private* ptr = GetUMAInterface(); 197 if (ptr == NULL) return; 198 if (opt_level < 0 || opt_level > 3) { 199 opt_level = kOptUnknown; 200 } 201 ptr->HistogramEnumeration(pp::Var("NaCl.Options.PNaCl.OptLevel").pp_var(), 202 opt_level, kOptUnknown+1); 203} 204 205} // namespace 206 207 208////////////////////////////////////////////////////////////////////// 209// The coordinator class. 210////////////////////////////////////////////////////////////////////// 211 212// Out-of-line destructor to keep it from getting put in every .o where 213// callback_source.h is included 214template<> 215CallbackSource<FileStreamData>::~CallbackSource() {} 216 217PnaclCoordinator* PnaclCoordinator::BitcodeToNative( 218 Plugin* plugin, 219 const nacl::string& pexe_url, 220 const PnaclOptions& pnacl_options, 221 const pp::CompletionCallback& translate_notify_callback) { 222 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (plugin=%p, pexe=%s)\n", 223 static_cast<void*>(plugin), pexe_url.c_str())); 224 PnaclCoordinator* coordinator = 225 new PnaclCoordinator(plugin, pexe_url, 226 pnacl_options, 227 translate_notify_callback); 228 coordinator->pnacl_init_time_ = NaClGetTimeOfDayMicroseconds(); 229 coordinator->off_the_record_ = 230 plugin->nacl_interface()->IsOffTheRecord(); 231 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (manifest=%p, " 232 "off_the_record=%d)\n", 233 reinterpret_cast<const void*>(coordinator->manifest_.get()), 234 coordinator->off_the_record_)); 235 236 // First check that PNaCl is installed. 237 pp::CompletionCallback pnacl_installed_cb = 238 coordinator->callback_factory_.NewCallback( 239 &PnaclCoordinator::DidCheckPnaclInstalled); 240 plugin->nacl_interface()->EnsurePnaclInstalled( 241 plugin->pp_instance(), 242 pnacl_installed_cb.pp_completion_callback()); 243 return coordinator; 244} 245 246PnaclCoordinator::PnaclCoordinator( 247 Plugin* plugin, 248 const nacl::string& pexe_url, 249 const PnaclOptions& pnacl_options, 250 const pp::CompletionCallback& translate_notify_callback) 251 : translate_finish_error_(PP_OK), 252 plugin_(plugin), 253 translate_notify_callback_(translate_notify_callback), 254 translation_finished_reported_(false), 255 manifest_(new PnaclManifest()), 256 pexe_url_(pexe_url), 257 pnacl_options_(pnacl_options), 258 is_cache_hit_(PP_FALSE), 259 error_already_reported_(false), 260 off_the_record_(false), 261 pnacl_init_time_(0), 262 pexe_size_(0), 263 pexe_bytes_compiled_(0), 264 expected_pexe_size_(-1) { 265 PLUGIN_PRINTF(("PnaclCoordinator::PnaclCoordinator (this=%p, plugin=%p)\n", 266 static_cast<void*>(this), static_cast<void*>(plugin))); 267 callback_factory_.Initialize(this); 268} 269 270PnaclCoordinator::~PnaclCoordinator() { 271 PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, " 272 "translate_thread=%p\n", 273 static_cast<void*>(this), translate_thread_.get())); 274 // Stopping the translate thread will cause the translate thread to try to 275 // run translation_complete_callback_ on the main thread. This destructor is 276 // running from the main thread, and by the time it exits, callback_factory_ 277 // will have been destroyed. This will result in the cancellation of 278 // translation_complete_callback_, so no notification will be delivered. 279 if (translate_thread_.get() != NULL) { 280 translate_thread_->AbortSubprocesses(); 281 } 282 if (!translation_finished_reported_) { 283 plugin_->nacl_interface()->ReportTranslationFinished( 284 plugin_->pp_instance(), 285 PP_FALSE); 286 } 287} 288 289nacl::DescWrapper* PnaclCoordinator::ReleaseTranslatedFD() { 290 DCHECK(temp_nexe_file_ != NULL); 291 return temp_nexe_file_->release_read_wrapper(); 292} 293 294void PnaclCoordinator::ReportNonPpapiError(enum PluginErrorCode err_code, 295 const nacl::string& message) { 296 error_info_.SetReport(err_code, message); 297 ExitWithError(); 298} 299 300void PnaclCoordinator::ReportPpapiError(enum PluginErrorCode err_code, 301 int32_t pp_error, 302 const nacl::string& message) { 303 nacl::stringstream ss; 304 ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ")."; 305 error_info_.SetReport(err_code, ss.str()); 306 ExitWithError(); 307} 308 309void PnaclCoordinator::ExitWithError() { 310 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError (error_code=%d, " 311 "message='%s')\n", 312 error_info_.error_code(), 313 error_info_.message().c_str())); 314 plugin_->ReportLoadError(error_info_); 315 // Free all the intermediate callbacks we ever created. 316 // Note: this doesn't *cancel* the callbacks from the factories attached 317 // to the various helper classes (e.g., pnacl_resources). Thus, those 318 // callbacks may still run asynchronously. We let those run but ignore 319 // any other errors they may generate so that they do not end up running 320 // translate_notify_callback_, which has already been freed. 321 callback_factory_.CancelAll(); 322 if (!error_already_reported_) { 323 error_already_reported_ = true; 324 translation_finished_reported_ = true; 325 plugin_->nacl_interface()->ReportTranslationFinished( 326 plugin_->pp_instance(), 327 PP_FALSE); 328 translate_notify_callback_.Run(PP_ERROR_FAILED); 329 } else { 330 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError an earlier error was " 331 "already reported -- Skipping.\n")); 332 } 333} 334 335// Signal that Pnacl translation completed normally. 336void 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 ExitWithError(); 343 return; 344 } 345 // Send out one last progress event, to finish up the progress events 346 // that were delayed (see the delay inserted in BitcodeGotCompiled). 347 if (ExpectedProgressKnown()) { 348 pexe_bytes_compiled_ = expected_pexe_size_; 349 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress, 350 pexe_url_, 351 plugin::Plugin::LENGTH_IS_COMPUTABLE, 352 pexe_bytes_compiled_, 353 expected_pexe_size_); 354 } 355 356 // If there are no errors, report stats from this thread (the main thread). 357 HistogramOptLevel(pnacl_options_.opt_level()); 358 const plugin::PnaclTimeStats& time_stats = translate_thread_->GetTimeStats(); 359 HistogramTime("NaCl.Perf.PNaClLoadTime.LoadCompiler", 360 time_stats.pnacl_llc_load_time / NACL_MICROS_PER_MILLI); 361 HistogramTime("NaCl.Perf.PNaClLoadTime.CompileTime", 362 time_stats.pnacl_compile_time / NACL_MICROS_PER_MILLI); 363 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec", 364 pexe_size_ / 1024.0, 365 time_stats.pnacl_compile_time / 1000000.0); 366 HistogramTime("NaCl.Perf.PNaClLoadTime.LoadLinker", 367 time_stats.pnacl_ld_load_time / NACL_MICROS_PER_MILLI); 368 HistogramTime("NaCl.Perf.PNaClLoadTime.LinkTime", 369 time_stats.pnacl_link_time / NACL_MICROS_PER_MILLI); 370 HistogramSizeKB("NaCl.Perf.Size.Pexe", 371 static_cast<int64_t>(pexe_size_ / 1024)); 372 373 struct nacl_abi_stat stbuf; 374 struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc(); 375 int stat_ret; 376 if (0 != (stat_ret = (*((struct NaClDescVtbl const *) desc->base.vtbl)-> 377 Fstat)(desc, &stbuf))) { 378 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished can't stat nexe.\n")); 379 } else { 380 size_t nexe_size = stbuf.nacl_abi_st_size; 381 HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe", 382 static_cast<int64_t>(nexe_size / 1024)); 383 HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct", pexe_size_, nexe_size); 384 } 385 386 int64_t total_time = NaClGetTimeOfDayMicroseconds() - pnacl_init_time_; 387 HistogramTime("NaCl.Perf.PNaClLoadTime.TotalUncachedTime", 388 total_time / NACL_MICROS_PER_MILLI); 389 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec", 390 pexe_size_ / 1024.0, 391 total_time / 1000000.0); 392 393 // The nexe is written to the temp_nexe_file_. We must Reset() the file 394 // pointer to be able to read it again from the beginning. 395 temp_nexe_file_->Reset(); 396 397 // Report to the browser that translation finished. The browser will take 398 // care of storing the nexe in the cache. 399 translation_finished_reported_ = true; 400 plugin_->nacl_interface()->ReportTranslationFinished( 401 plugin_->pp_instance(), PP_TRUE); 402 403 NexeReadDidOpen(PP_OK); 404} 405 406void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) { 407 PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%" 408 NACL_PRId32 ")\n", pp_error)); 409 if (pp_error != PP_OK) { 410 if (pp_error == PP_ERROR_FILENOTFOUND) { 411 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOTFOUND, 412 pp_error, 413 "Failed to open translated nexe (not found)."); 414 return; 415 } 416 if (pp_error == PP_ERROR_NOACCESS) { 417 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOACCESS, 418 pp_error, 419 "Failed to open translated nexe (no access)."); 420 return; 421 } 422 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_OTHER, 423 pp_error, 424 "Failed to open translated nexe."); 425 return; 426 } 427 428 translate_notify_callback_.Run(pp_error); 429} 430 431void PnaclCoordinator::DidCheckPnaclInstalled(int32_t pp_error) { 432 if (pp_error != PP_OK) { 433 ReportNonPpapiError( 434 ERROR_PNACL_RESOURCE_FETCH, 435 nacl::string("The Portable Native Client (pnacl) component is not " 436 "installed. Please consult chrome://components for more " 437 "information.")); 438 return; 439 } 440 441 // Loading resources (e.g. llc and ld nexes) is done with PnaclResources. 442 resources_.reset(new PnaclResources(plugin_, 443 this, 444 this->manifest_.get())); 445 CHECK(resources_ != NULL); 446 447 // The first step of loading resources: read the resource info file. 448 pp::CompletionCallback resource_info_read_cb = 449 callback_factory_.NewCallback( 450 &PnaclCoordinator::ResourceInfoWasRead); 451 resources_->ReadResourceInfo(PnaclUrls::GetResourceInfoUrl(), 452 resource_info_read_cb); 453} 454 455void PnaclCoordinator::ResourceInfoWasRead(int32_t pp_error) { 456 PLUGIN_PRINTF(("PluginCoordinator::ResourceInfoWasRead (pp_error=%" 457 NACL_PRId32 ")\n", pp_error)); 458 // Second step of loading resources: call StartLoad. 459 pp::CompletionCallback resources_cb = 460 callback_factory_.NewCallback(&PnaclCoordinator::ResourcesDidLoad); 461 resources_->StartLoad(resources_cb); 462} 463 464void PnaclCoordinator::ResourcesDidLoad(int32_t pp_error) { 465 PLUGIN_PRINTF(("PnaclCoordinator::ResourcesDidLoad (pp_error=%" 466 NACL_PRId32 ")\n", pp_error)); 467 if (pp_error != PP_OK) { 468 // Finer-grained error code should have already been reported by 469 // the PnaclResources class. 470 return; 471 } 472 473 OpenBitcodeStream(); 474} 475 476void PnaclCoordinator::OpenBitcodeStream() { 477 // Now open the pexe stream. 478 streaming_downloader_.reset(new FileDownloader()); 479 streaming_downloader_->Initialize(plugin_); 480 481 // Even though we haven't started downloading, create the translation 482 // thread object immediately. This ensures that any pieces of the file 483 // that get downloaded before the compilation thread is accepting 484 // SRPCs won't get dropped. 485 translate_thread_.reset(new PnaclTranslateThread()); 486 if (translate_thread_ == NULL) { 487 ReportNonPpapiError( 488 ERROR_PNACL_THREAD_CREATE, 489 "PnaclCoordinator: could not allocate translation thread."); 490 return; 491 } 492 493 pp::CompletionCallback cb = 494 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidOpen); 495 if (!streaming_downloader_->OpenStream(pexe_url_, cb, this)) { 496 ReportNonPpapiError( 497 ERROR_PNACL_PEXE_FETCH_OTHER, 498 nacl::string("PnaclCoordinator: failed to open stream ") + pexe_url_); 499 return; 500 } 501} 502 503void PnaclCoordinator::BitcodeStreamDidOpen(int32_t pp_error) { 504 if (pp_error != PP_OK) { 505 BitcodeStreamDidFinish(pp_error); 506 // We have not spun up the translation process yet, so we need to call 507 // TranslateFinished here. 508 TranslateFinished(pp_error); 509 return; 510 } 511 512 // Get the cache key and try to open an existing entry. 513 nacl::string headers = streaming_downloader_->GetResponseHeaders(); 514 NaClHttpResponseHeaders parser; 515 parser.Parse(headers); 516 517 temp_nexe_file_.reset(new TempFile(plugin_)); 518 pp::CompletionCallback cb = 519 callback_factory_.NewCallback(&PnaclCoordinator::NexeFdDidOpen); 520 int32_t nexe_fd_err = 521 plugin_->nacl_interface()->GetNexeFd( 522 plugin_->pp_instance(), 523 streaming_downloader_->url().c_str(), 524 // TODO(dschuff): Get this value from the pnacl json file after it 525 // rolls in from NaCl. 526 1, 527 pnacl_options_.opt_level(), 528 parser.GetHeader("last-modified").c_str(), 529 parser.GetHeader("etag").c_str(), 530 PP_FromBool(parser.CacheControlNoStore()), 531 &is_cache_hit_, 532 temp_nexe_file_->existing_handle(), 533 cb.pp_completion_callback()); 534 if (nexe_fd_err < PP_OK_COMPLETIONPENDING) { 535 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, nexe_fd_err, 536 nacl::string("Call to GetNexeFd failed")); 537 } 538} 539 540void PnaclCoordinator::NexeFdDidOpen(int32_t pp_error) { 541 PLUGIN_PRINTF(("PnaclCoordinator::NexeFdDidOpen (pp_error=%" 542 NACL_PRId32 ", hit=%d, handle=%d)\n", pp_error, 543 is_cache_hit_ == PP_TRUE, 544 *temp_nexe_file_->existing_handle())); 545 if (pp_error < PP_OK) { 546 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, pp_error, 547 nacl::string("GetNexeFd failed")); 548 return; 549 } 550 551 if (*temp_nexe_file_->existing_handle() == PP_kInvalidFileHandle) { 552 ReportNonPpapiError( 553 ERROR_PNACL_CREATE_TEMP, 554 nacl::string( 555 "PnaclCoordinator: Got bad temp file handle from GetNexeFd")); 556 return; 557 } 558 HistogramEnumerateTranslationCache(is_cache_hit_); 559 560 if (is_cache_hit_ == PP_TRUE) { 561 // Cache hit -- no need to stream the rest of the file. 562 streaming_downloader_.reset(NULL); 563 // Open it for reading as the cached nexe file. 564 pp::CompletionCallback cb = 565 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen); 566 temp_nexe_file_->Open(cb, false); 567 } else { 568 // Open an object file first so the translator can start writing to it 569 // during streaming translation. 570 obj_file_.reset(new TempFile(plugin_)); 571 pp::CompletionCallback obj_cb = 572 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen); 573 obj_file_->Open(obj_cb, true); 574 575 // Meanwhile, a miss means we know we need to stream the bitcode, so stream 576 // the rest of it now. (Calling FinishStreaming means that the downloader 577 // will begin handing data to the coordinator, which is safe any time after 578 // the translate_thread_ object has been initialized). 579 pp::CompletionCallback finish_cb = callback_factory_.NewCallback( 580 &PnaclCoordinator::BitcodeStreamDidFinish); 581 streaming_downloader_->FinishStreaming(finish_cb); 582 } 583} 584 585void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) { 586 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%" 587 NACL_PRId32 ")\n", pp_error)); 588 if (pp_error != PP_OK) { 589 // Defer reporting the error and cleanup until after the translation 590 // thread returns, because it may be accessing the coordinator's 591 // objects or writing to the files. 592 translate_finish_error_ = pp_error; 593 if (pp_error == PP_ERROR_ABORTED) { 594 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_ABORTED, 595 "PnaclCoordinator: pexe load failed (aborted)."); 596 } 597 if (pp_error == PP_ERROR_NOACCESS) { 598 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_NOACCESS, 599 "PnaclCoordinator: pexe load failed (no access)."); 600 } else { 601 nacl::stringstream ss; 602 ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ")."; 603 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_OTHER, ss.str()); 604 } 605 translate_thread_->AbortSubprocesses(); 606 } else { 607 // Compare download completion pct (100% now), to compile completion pct. 608 HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded", 609 pexe_bytes_compiled_, pexe_size_); 610 } 611} 612 613void PnaclCoordinator::BitcodeStreamGotData(int32_t pp_error, 614 FileStreamData data) { 615 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamGotData (pp_error=%" 616 NACL_PRId32 ", data=%p)\n", pp_error, data ? &(*data)[0] : 0)); 617 DCHECK(translate_thread_.get()); 618 619 translate_thread_->PutBytes(data, pp_error); 620 // If pp_error > 0, then it represents the number of bytes received. 621 if (data && pp_error > 0) { 622 pexe_size_ += pp_error; 623 } 624} 625 626StreamCallback PnaclCoordinator::GetCallback() { 627 return callback_factory_.NewCallbackWithOutput( 628 &PnaclCoordinator::BitcodeStreamGotData); 629} 630 631void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error, 632 int64_t bytes_compiled) { 633 pexe_bytes_compiled_ += bytes_compiled; 634 // If we don't know the expected total yet, ask. 635 if (!ExpectedProgressKnown()) { 636 int64_t amount_downloaded; // dummy variable. 637 streaming_downloader_->GetDownloadProgress(&amount_downloaded, 638 &expected_pexe_size_); 639 } 640 // Hold off reporting the last few bytes of progress, since we don't know 641 // when they are actually completely compiled. "bytes_compiled" only means 642 // that bytes were sent to the compiler. 643 if (ExpectedProgressKnown()) { 644 if (!ShouldDelayProgressEvent()) { 645 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress, 646 pexe_url_, 647 plugin::Plugin::LENGTH_IS_COMPUTABLE, 648 pexe_bytes_compiled_, 649 expected_pexe_size_); 650 } 651 } else { 652 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress, 653 pexe_url_, 654 plugin::Plugin::LENGTH_IS_NOT_COMPUTABLE, 655 pexe_bytes_compiled_, 656 expected_pexe_size_); 657 } 658} 659 660pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback( 661 int64_t bytes_compiled) { 662 return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled, 663 bytes_compiled); 664} 665 666void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded, 667 int64_t* bytes_total) { 668 *bytes_loaded = pexe_bytes_compiled_; 669 *bytes_total = expected_pexe_size_; 670} 671 672void PnaclCoordinator::ObjectFileDidOpen(int32_t pp_error) { 673 PLUGIN_PRINTF(("PnaclCoordinator::ObjectFileDidOpen (pp_error=%" 674 NACL_PRId32 ")\n", pp_error)); 675 if (pp_error != PP_OK) { 676 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, 677 pp_error, 678 "Failed to open scratch object file."); 679 return; 680 } 681 // Open the nexe file for connecting ld and sel_ldr. 682 // Start translation when done with this last step of setup! 683 pp::CompletionCallback cb = 684 callback_factory_.NewCallback(&PnaclCoordinator::RunTranslate); 685 temp_nexe_file_->Open(cb, true); 686} 687 688void PnaclCoordinator::RunTranslate(int32_t pp_error) { 689 PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%" 690 NACL_PRId32 ")\n", pp_error)); 691 // Invoke llc followed by ld off the main thread. This allows use of 692 // blocking RPCs that would otherwise block the JavaScript main thread. 693 pp::CompletionCallback report_translate_finished = 694 callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished); 695 696 CHECK(translate_thread_ != NULL); 697 translate_thread_->RunTranslate(report_translate_finished, 698 manifest_.get(), 699 obj_file_.get(), 700 temp_nexe_file_.get(), 701 &error_info_, 702 resources_.get(), 703 &pnacl_options_, 704 this, 705 plugin_); 706} 707 708} // namespace plugin 709