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/ppb_file_io.h" 18 #include "ppapi/c/private/ppb_uma_private.h" 19 #include "ppapi/cpp/file_io.h" 20 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" 29 30 namespace { 31 const char kPnaclTempDir[] = "/.pnacl"; 32 const uint32_t kCopyBufSize = 512 << 10; 33 } 34 35 namespace plugin { 36 37 ////////////////////////////////////////////////////////////////////// 38 // Pnacl-specific manifest support. 39 ////////////////////////////////////////////////////////////////////// 40 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() { } 50 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; 62 } 63 64 virtual bool ResolveURL(const nacl::string& relative_url, 65 nacl::string* full_url, 66 ErrorInfo* error_info) const { 67 // Does not do general URL resolution, simply appends relative_url to 68 // the end of manifest_base_url_. 69 UNREFERENCED_PARAMETER(error_info); 70 *full_url = manifest_base_url_ + relative_url; 71 return true; 72 } 73 74 virtual bool GetFileKeys(std::set<nacl::string>* keys) const { 75 // Does not support enumeration. 76 PLUGIN_PRINTF(("PnaclManifest does not support key enumeration\n")); 77 UNREFERENCED_PARAMETER(keys); 78 return false; 79 } 80 81 virtual bool ResolveKey(const nacl::string& key, 82 nacl::string* full_url, 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; 94 } 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); 100 } 101 102 private: 103 NACL_DISALLOW_COPY_AND_ASSIGN(PnaclManifest); 104 105 nacl::string manifest_base_url_; 106 }; 107 108 ////////////////////////////////////////////////////////////////////// 109 // UMA stat helpers. 110 ////////////////////////////////////////////////////////////////////// 111 112 namespace { 113 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; 119 120 const int32_t kSizeKBMin = 1; 121 const int32_t kSizeKBMax = 512*1024; // very large .pexe / .nexe. 122 const uint32_t kSizeKBBuckets = 100; 123 124 const int32_t kRatioMin = 10; 125 const int32_t kRatioMax = 10*100; // max of 10x difference. 126 const uint32_t kRatioBuckets = 100; 127 128 const int32_t kKBPSMin = 1; 129 const int32_t kKBPSMax = 30*1000; // max of 30 MB / sec. 130 const uint32_t kKBPSBuckets = 100; 131 132 const PPB_UMA_Private* uma_interface = NULL; 133 134 const PPB_UMA_Private* GetUMAInterface() { 135 if (uma_interface != NULL) { 136 return uma_interface; 137 } 138 pp::Module *module = pp::Module::Get(); 139 DCHECK(module); 140 uma_interface = static_cast<const PPB_UMA_Private*>( 141 module->GetBrowserInterface(PPB_UMA_PRIVATE_INTERFACE)); 142 return uma_interface; 143 } 144 145 void HistogramTime(const std::string& name, int64_t ms) { 146 if (ms < 0) return; 147 148 const PPB_UMA_Private* ptr = GetUMAInterface(); 149 if (ptr == NULL) return; 150 151 ptr->HistogramCustomTimes(pp::Var(name).pp_var(), 152 ms, 153 kTimeLargeMin, kTimeLargeMax, 154 kTimeLargeBuckets); 155 } 156 157 void HistogramSizeKB(const std::string& name, int32_t kb) { 158 if (kb < 0) return; 159 160 const PPB_UMA_Private* ptr = GetUMAInterface(); 161 if (ptr == NULL) return; 162 163 ptr->HistogramCustomCounts(pp::Var(name).pp_var(), 164 kb, 165 kSizeKBMin, kSizeKBMax, 166 kSizeKBBuckets); 167 } 168 169 void HistogramRatio(const std::string& name, int64_t a, int64_t b) { 170 if (a < 0 || b <= 0) return; 171 172 const PPB_UMA_Private* ptr = GetUMAInterface(); 173 if (ptr == NULL) return; 174 175 ptr->HistogramCustomCounts(pp::Var(name).pp_var(), 176 100 * a / b, 177 kRatioMin, kRatioMax, 178 kRatioBuckets); 179 } 180 181 void HistogramKBPerSec(const std::string& name, double kb, double s) { 182 if (kb < 0.0 || s <= 0.0) return; 183 184 const PPB_UMA_Private* ptr = GetUMAInterface(); 185 if (ptr == NULL) return; 186 187 ptr->HistogramCustomCounts(pp::Var(name).pp_var(), 188 static_cast<int64_t>(kb / s), 189 kKBPSMin, kKBPSMax, 190 kKBPSBuckets); 191 } 192 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); 198 } 199 200 // Opt level is expected to be 0 to 3. Treating 4 as unknown. 201 const int8_t kOptUnknown = 4; 202 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; 208 } 209 ptr->HistogramEnumeration(pp::Var("NaCl.Options.PNaCl.OptLevel").pp_var(), 210 opt_level, kOptUnknown+1); 211 } 212 213 } // namespace 214 215 216 ////////////////////////////////////////////////////////////////////// 217 // The coordinator class. 218 ////////////////////////////////////////////////////////////////////// 219 220 // Out-of-line destructor to keep it from getting put in every .o where 221 // callback_source.h is included 222 template<> 223 CallbackSource<FileStreamData>::~CallbackSource() {} 224 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_)); 243 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; 252 } 253 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; 281 } 282 } 283 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(); 295 } 296 } 297 298 void PnaclCoordinator::ReportNonPpapiError(enum PluginErrorCode err_code, 299 const nacl::string& message) { 300 error_info_.SetReport(err_code, message); 301 ExitWithError(); 302 } 303 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(); 311 } 312 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")); 332 } 333 } 334 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); 346 } 347 ExitWithError(); 348 return; 349 } 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_); 359 } 360 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)); 377 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); 389 } 390 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(); 394 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); 400 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; 411 } 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); 426 } 427 } 428 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; 438 } 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; 446 } 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; 454 } 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; 461 } 462 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_OTHER, 463 pp_error, 464 "PNaCl translation cache failed to open file for write."); 465 return; 466 } 467 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; 479 } 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; 485 } 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); 489 } 490 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; 502 } 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; 508 } 509 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; 525 } 526 } 527 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; 538 } 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; 544 } 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); 551 } 552 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; 562 } 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); 567 } 568 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. 578 } 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; 586 } 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; 592 } 593 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_OTHER, 594 orig_pp_error, 595 "Failed to copy translated nexe to cache."); 596 return; 597 } 598 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")); 622 } 623 } 624 625 cached_nexe_file_->FinishRename(); 626 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); 633 634 // Open the cache file for reading. 635 pp::CompletionCallback cb = 636 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen); 637 cached_nexe_file_->OpenRead(cb); 638 } 639 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; 649 } 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; 655 } 656 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_OTHER, 657 pp_error, 658 "Failed to open translated nexe."); 659 return; 660 } 661 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()); 667 } 668 translate_notify_callback_.Run(pp_error); 669 } 670 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 component is not installed" 676 " or has been disabled.")); 677 return; 678 } 679 680 // Loading resources (e.g. llc and ld nexes) is done with PnaclResources. 681 resources_.reset(new PnaclResources(plugin_, 682 this, 683 this->manifest_.get())); 684 CHECK(resources_ != NULL); 685 686 // The first step of loading resources: read the resource info file. 687 pp::CompletionCallback resource_info_read_cb = 688 callback_factory_.NewCallback( 689 &PnaclCoordinator::ResourceInfoWasRead); 690 resources_->ReadResourceInfo(PnaclUrls::GetResourceInfoUrl(), 691 resource_info_read_cb); 692 } 693 694 void PnaclCoordinator::ResourceInfoWasRead(int32_t pp_error) { 695 PLUGIN_PRINTF(("PluginCoordinator::ResourceInfoWasRead (pp_error=%" 696 NACL_PRId32 ")\n", pp_error)); 697 // Second step of loading resources: call StartLoad. 698 pp::CompletionCallback resources_cb = 699 callback_factory_.NewCallback(&PnaclCoordinator::ResourcesDidLoad); 700 resources_->StartLoad(resources_cb); 701 } 702 703 void PnaclCoordinator::ResourcesDidLoad(int32_t pp_error) { 704 PLUGIN_PRINTF(("PnaclCoordinator::ResourcesDidLoad (pp_error=%" 705 NACL_PRId32 ")\n", pp_error)); 706 if (pp_error != PP_OK) { 707 // Finer-grained error code should have already been reported by 708 // the PnaclResources class. 709 return; 710 } 711 712 if (!off_the_record_) { 713 if (use_new_cache_) { 714 OpenBitcodeStream(); 715 } else { 716 // Open the local temporary FS to see if we get a hit in the cache. 717 pp::CompletionCallback cb = 718 callback_factory_.NewCallback(&PnaclCoordinator::FileSystemDidOpen); 719 int32_t open_error = file_system_->Open(0, cb); 720 if (open_error != PP_OK_COMPLETIONPENDING) { 721 // At this point, no async request has kicked off to check for 722 // permissions, space, etc., so the only error that can be detected 723 // now is that an open() is already in progress (or a really terrible 724 // error). 725 if (pp_error == PP_ERROR_INPROGRESS) { 726 ReportPpapiError( 727 ERROR_PNACL_CACHE_OPEN_INPROGRESS, 728 pp_error, 729 "File system for PNaCl translation cache failed to open " 730 "(in progress)."); 731 return; 732 } 733 ReportPpapiError( 734 ERROR_PNACL_CACHE_OPEN_OTHER, 735 pp_error, 736 "File system for PNaCl translation cache failed to open."); 737 } 738 } 739 } else { 740 // We don't have a cache, so do the non-cached codepath. 741 OpenBitcodeStream(); 742 } 743 } 744 745 void PnaclCoordinator::FileSystemDidOpen(int32_t pp_error) { 746 PLUGIN_PRINTF(("PnaclCoordinator::FileSystemDidOpen (pp_error=%" 747 NACL_PRId32 ")\n", pp_error)); 748 if (pp_error != PP_OK) { 749 if (pp_error == PP_ERROR_NOACCESS) { 750 ReportPpapiError( 751 ERROR_PNACL_CACHE_OPEN_NOACCESS, 752 pp_error, 753 "File system for PNaCl translation cache failed to open " 754 "(no access)."); 755 return; 756 } 757 if (pp_error == PP_ERROR_NOQUOTA) { 758 ReportPpapiError( 759 ERROR_PNACL_CACHE_OPEN_NOQUOTA, 760 pp_error, 761 "File system for PNaCl translation cache failed to open " 762 "(no quota)."); 763 return; 764 } 765 if (pp_error == PP_ERROR_NOSPACE) { 766 ReportPpapiError( 767 ERROR_PNACL_CACHE_OPEN_NOSPACE, 768 pp_error, 769 "File system for PNaCl translation cache failed to open " 770 "(no space)."); 771 return; 772 } 773 ReportPpapiError(ERROR_PNACL_CACHE_OPEN_OTHER, 774 pp_error, 775 "File system for PNaCl translation cache failed to open."); 776 } 777 dir_ref_.reset(new pp::FileRef(*file_system_, kPnaclTempDir)); 778 // Attempt to create the directory. 779 pp::CompletionCallback cb = 780 callback_factory_.NewCallback(&PnaclCoordinator::DirectoryWasCreated); 781 dir_ref_->MakeDirectory(cb); 782 } 783 784 void PnaclCoordinator::DirectoryWasCreated(int32_t pp_error) { 785 PLUGIN_PRINTF(("PnaclCoordinator::DirectoryWasCreated (pp_error=%" 786 NACL_PRId32 ")\n", pp_error)); 787 if (pp_error != PP_ERROR_FILEEXISTS && pp_error != PP_OK) { 788 // Directory did not exist and could not be created. 789 if (pp_error == PP_ERROR_NOACCESS) { 790 ReportPpapiError( 791 ERROR_PNACL_CACHE_DIRECTORY_CREATE, 792 pp_error, 793 "PNaCl translation cache directory creation/check failed " 794 "(no access)."); 795 return; 796 } 797 ReportPpapiError( 798 ERROR_PNACL_CACHE_DIRECTORY_CREATE, 799 pp_error, 800 "PNaCl translation cache directory creation/check failed."); 801 return; 802 } 803 OpenBitcodeStream(); 804 } 805 806 void PnaclCoordinator::OpenBitcodeStream() { 807 // Now open the pexe stream. 808 streaming_downloader_.reset(new FileDownloader()); 809 streaming_downloader_->Initialize(plugin_); 810 811 // Even though we haven't started downloading, create the translation 812 // thread object immediately. This ensures that any pieces of the file 813 // that get downloaded before the compilation thread is accepting 814 // SRPCs won't get dropped. 815 translate_thread_.reset(new PnaclTranslateThread()); 816 if (translate_thread_ == NULL) { 817 ReportNonPpapiError( 818 ERROR_PNACL_THREAD_CREATE, 819 "PnaclCoordinator: could not allocate translation thread."); 820 return; 821 } 822 if (!use_new_cache_) { 823 // We also want to open the object file now so the 824 // translator can start writing to it during streaming translation. 825 obj_file_.reset(new TempFile(plugin_)); 826 pp::CompletionCallback obj_cb = 827 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen); 828 obj_file_->Open(obj_cb, true); 829 } 830 831 pp::CompletionCallback cb = 832 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidOpen); 833 if (!streaming_downloader_->OpenStream(pexe_url_, cb, this)) { 834 ReportNonPpapiError( 835 ERROR_PNACL_PEXE_FETCH_OTHER, 836 nacl::string("PnaclCoordinator: failed to open stream ") + pexe_url_); 837 return; 838 } 839 } 840 841 void PnaclCoordinator::BitcodeStreamDidOpen(int32_t pp_error) { 842 if (pp_error != PP_OK) { 843 BitcodeStreamDidFinish(pp_error); 844 // In the new cache case, we have not spun up the translation process yet, 845 // so we need to call TranslateFinished here. 846 if (use_new_cache_) 847 TranslateFinished(pp_error); 848 return; 849 } 850 851 if (!off_the_record_ || use_new_cache_) { 852 // Get the cache key and try to open an existing entry. 853 nacl::string headers = streaming_downloader_->GetResponseHeaders(); 854 NaClHttpResponseHeaders parser; 855 parser.Parse(headers); 856 nacl::string cache_validators = parser.GetCacheValidators(); 857 if (parser.CacheControlNoStore() || cache_validators.empty()) { 858 // We can't cache in this case. 859 pnacl_options_.set_cache_validators(""); 860 CachedFileDidOpen(PP_ERROR_FAILED); 861 return; 862 } else { 863 nacl::string url = streaming_downloader_->url(); 864 // For now, combine the cache_validators + the URL as the key. 865 // When we change the cache backend to be not-origin-specific 866 // we should send the URL separately, and check in the browser's 867 // RenderViewHost / SiteInstance's IsSameWebsite() to prevent 868 // people from forging the URL for a different origin. 869 pnacl_options_.set_cache_validators(cache_validators + url); 870 } 871 if (use_new_cache_) { 872 pp::CompletionCallback cb = 873 callback_factory_.NewCallback(&PnaclCoordinator::NexeFdDidOpen); 874 int32_t nexe_fd_err = 875 plugin_->nacl_interface()->GetNexeFd( 876 plugin_->pp_instance(), 877 streaming_downloader_->url().c_str(), 878 // TODO(dschuff): Get this value from the pnacl json file after it 879 // rolls in from NaCl. 880 1, 881 pnacl_options_.opt_level(), 882 parser.GetHeader("last-modified").c_str(), 883 parser.GetHeader("etag").c_str(), 884 &is_cache_hit_, 885 &nexe_handle_, 886 cb.pp_completion_callback()); 887 if (nexe_fd_err < PP_OK_COMPLETIONPENDING) { 888 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, nexe_fd_err, 889 nacl::string("Call to GetNexeFd failed")); 890 return; 891 } 892 } else { 893 cached_nexe_file_.reset(new LocalTempFile( 894 plugin_, file_system_.get(), 895 nacl::string(kPnaclTempDir), 896 pnacl_options_.GetCacheKey())); 897 pp::CompletionCallback cb = 898 callback_factory_.NewCallback(&PnaclCoordinator::CachedFileDidOpen); 899 cached_nexe_file_->OpenRead(cb); 900 } 901 } else { 902 // No cache case. 903 CachedFileDidOpen(PP_ERROR_FAILED); 904 } 905 } 906 907 void PnaclCoordinator::NexeFdDidOpen(int32_t pp_error) { 908 PLUGIN_PRINTF(("PnaclCoordinator::NexeFdDidOpen (pp_error=%" 909 NACL_PRId32 ", hit=%d, handle=%d)\n", pp_error, 910 is_cache_hit_ == PP_TRUE, 911 nexe_handle_)); 912 if (pp_error < PP_OK) { 913 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, pp_error, 914 nacl::string("GetNexeFd failed")); 915 return; 916 } 917 temp_nexe_file_.reset(new TempFile(plugin_)); 918 if (!temp_nexe_file_->SetExistingFd(nexe_handle_)) { 919 ReportNonPpapiError( 920 ERROR_PNACL_CREATE_TEMP, 921 nacl::string( 922 "PnaclCoordinator: Got bad temp file handle from GetNexeFd")); 923 return; 924 } 925 HistogramEnumerateTranslationCache(is_cache_hit_); 926 927 if (is_cache_hit_ == PP_TRUE) { 928 // Cache hit -- no need to stream the rest of the file. 929 streaming_downloader_.reset(NULL); 930 // Open it for reading as the cached nexe file. 931 pp::CompletionCallback cb = 932 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen); 933 temp_nexe_file_->Open(cb, false); 934 } else { 935 // Open an object file first so the translator can start writing to it 936 // during streaming translation. 937 obj_file_.reset(new TempFile(plugin_)); 938 pp::CompletionCallback obj_cb = 939 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen); 940 obj_file_->Open(obj_cb, true); 941 942 // Meanwhile, a miss means we know we need to stream the bitcode, so stream 943 // the rest of it now. (Calling FinishStreaming means that the downloader 944 // will begin handing data to the coordinator, which is safe any time after 945 // the translate_thread_ object has been initialized). 946 pp::CompletionCallback finish_cb = callback_factory_.NewCallback( 947 &PnaclCoordinator::BitcodeStreamDidFinish); 948 streaming_downloader_->FinishStreaming(finish_cb); 949 } 950 } 951 952 void PnaclCoordinator::CachedFileDidOpen(int32_t pp_error) { 953 PLUGIN_PRINTF(("PnaclCoordinator::CachedFileDidOpen (pp_error=%" 954 NACL_PRId32 ")\n", pp_error)); 955 if (pp_error == PP_OK) { 956 // Cache hit -- no need to stream the rest of the file. 957 streaming_downloader_.reset(NULL); 958 HistogramEnumerateTranslationCache(true); 959 NexeReadDidOpen(PP_OK); 960 return; 961 } 962 // Otherwise, the cache file is missing so we must translate. 963 HistogramEnumerateTranslationCache(false); 964 965 // Continue streaming. 966 pp::CompletionCallback cb = 967 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidFinish); 968 streaming_downloader_->FinishStreaming(cb); 969 } 970 971 void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) { 972 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%" 973 NACL_PRId32 ")\n", pp_error)); 974 if (pp_error != PP_OK) { 975 // Defer reporting the error and cleanup until after the translation 976 // thread returns, because it may be accessing the coordinator's 977 // objects or writing to the files. 978 translate_finish_error_ = pp_error; 979 if (pp_error == PP_ERROR_ABORTED) { 980 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_ABORTED, 981 "PnaclCoordinator: pexe load failed (aborted)."); 982 } 983 if (pp_error == PP_ERROR_NOACCESS) { 984 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_NOACCESS, 985 "PnaclCoordinator: pexe load failed (no access)."); 986 } else { 987 nacl::stringstream ss; 988 ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ")."; 989 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_OTHER, ss.str()); 990 } 991 if (use_new_cache_) { 992 plugin_->nacl_interface()->ReportTranslationFinished( 993 plugin_->pp_instance(), 994 PP_FALSE); 995 } 996 translate_thread_->AbortSubprocesses(); 997 } else { 998 // Compare download completion pct (100% now), to compile completion pct. 999 HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded", 1000 pexe_bytes_compiled_, pexe_size_); 1001 } 1002 } 1003 1004 void PnaclCoordinator::BitcodeStreamGotData(int32_t pp_error, 1005 FileStreamData data) { 1006 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamGotData (pp_error=%" 1007 NACL_PRId32 ", data=%p)\n", pp_error, data ? &(*data)[0] : 0)); 1008 DCHECK(translate_thread_.get()); 1009 1010 translate_thread_->PutBytes(data, pp_error); 1011 // If pp_error > 0, then it represents the number of bytes received. 1012 if (data && pp_error > 0) { 1013 pexe_size_ += pp_error; 1014 } 1015 } 1016 1017 StreamCallback PnaclCoordinator::GetCallback() { 1018 return callback_factory_.NewCallbackWithOutput( 1019 &PnaclCoordinator::BitcodeStreamGotData); 1020 } 1021 1022 void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error, 1023 int64_t bytes_compiled) { 1024 pexe_bytes_compiled_ += bytes_compiled; 1025 // If we don't know the expected total yet, ask. 1026 if (!ExpectedProgressKnown()) { 1027 int64_t amount_downloaded; // dummy variable. 1028 streaming_downloader_->GetDownloadProgress(&amount_downloaded, 1029 &expected_pexe_size_); 1030 } 1031 // Hold off reporting the last few bytes of progress, since we don't know 1032 // when they are actually completely compiled. "bytes_compiled" only means 1033 // that bytes were sent to the compiler. 1034 if (ExpectedProgressKnown()) { 1035 if (!ShouldDelayProgressEvent()) { 1036 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress, 1037 pexe_url_, 1038 plugin::Plugin::LENGTH_IS_COMPUTABLE, 1039 pexe_bytes_compiled_, 1040 expected_pexe_size_); 1041 } 1042 } else { 1043 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress, 1044 pexe_url_, 1045 plugin::Plugin::LENGTH_IS_NOT_COMPUTABLE, 1046 pexe_bytes_compiled_, 1047 expected_pexe_size_); 1048 } 1049 } 1050 1051 pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback( 1052 int64_t bytes_compiled) { 1053 return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled, 1054 bytes_compiled); 1055 } 1056 1057 void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded, 1058 int64_t* bytes_total) { 1059 *bytes_loaded = pexe_bytes_compiled_; 1060 *bytes_total = expected_pexe_size_; 1061 } 1062 1063 void PnaclCoordinator::ObjectFileDidOpen(int32_t pp_error) { 1064 PLUGIN_PRINTF(("PnaclCoordinator::ObjectFileDidOpen (pp_error=%" 1065 NACL_PRId32 ")\n", pp_error)); 1066 if (pp_error != PP_OK) { 1067 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, 1068 pp_error, 1069 "Failed to open scratch object file."); 1070 if (use_new_cache_) { 1071 plugin_->nacl_interface()->ReportTranslationFinished( 1072 plugin_->pp_instance(), 1073 PP_FALSE); 1074 } 1075 return; 1076 } 1077 // Open the nexe file for connecting ld and sel_ldr. 1078 // Start translation when done with this last step of setup! 1079 if (!use_new_cache_) 1080 // In the new cache case, the TempFile has already been created. 1081 temp_nexe_file_.reset(new TempFile(plugin_)); 1082 1083 pp::CompletionCallback cb = 1084 callback_factory_.NewCallback(&PnaclCoordinator::RunTranslate); 1085 temp_nexe_file_->Open(cb, true); 1086 } 1087 1088 void PnaclCoordinator::RunTranslate(int32_t pp_error) { 1089 PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%" 1090 NACL_PRId32 ")\n", pp_error)); 1091 // Invoke llc followed by ld off the main thread. This allows use of 1092 // blocking RPCs that would otherwise block the JavaScript main thread. 1093 pp::CompletionCallback report_translate_finished = 1094 callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished); 1095 1096 CHECK(translate_thread_ != NULL); 1097 translate_thread_->RunTranslate(report_translate_finished, 1098 manifest_.get(), 1099 obj_file_.get(), 1100 temp_nexe_file_.get(), 1101 &error_info_, 1102 resources_.get(), 1103 &pnacl_options_, 1104 this, 1105 plugin_); 1106 } 1107 1108 } // namespace plugin 1109