// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h" #include <utility> #include <vector> #include "native_client/src/include/checked_cast.h" #include "native_client/src/include/portability_io.h" #include "native_client/src/shared/platform/nacl_check.h" #include "native_client/src/trusted/service_runtime/include/sys/stat.h" #include "ppapi/c/pp_bool.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/private/ppb_uma_private.h" #include "ppapi/native_client/src/trusted/plugin/manifest.h" #include "ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.h" #include "ppapi/native_client/src/trusted/plugin/plugin.h" #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" #include "ppapi/native_client/src/trusted/plugin/pnacl_translate_thread.h" #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" #include "ppapi/native_client/src/trusted/plugin/temporary_file.h" namespace plugin { ////////////////////////////////////////////////////////////////////// // Pnacl-specific manifest support. ////////////////////////////////////////////////////////////////////// // The PNaCl linker gets file descriptors via the service runtime's // reverse service lookup. The reverse service lookup requires a manifest. // Normally, that manifest is an NMF containing mappings for shared libraries. // Here, we provide a manifest that redirects to PNaCl component files // that are part of Chrome. class PnaclManifest : public Manifest { public: PnaclManifest() : manifest_base_url_(PnaclUrls::GetBaseUrl()) { } virtual ~PnaclManifest() { } virtual bool GetProgramURL(nacl::string* full_url, PnaclOptions* pnacl_options, ErrorInfo* error_info) const { // Does not contain program urls. UNREFERENCED_PARAMETER(full_url); UNREFERENCED_PARAMETER(pnacl_options); UNREFERENCED_PARAMETER(error_info); PLUGIN_PRINTF(("PnaclManifest does not contain a program\n")); error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL, "pnacl manifest does not contain a program."); return false; } virtual bool ResolveURL(const nacl::string& relative_url, nacl::string* full_url, ErrorInfo* error_info) const { // Does not do general URL resolution, simply appends relative_url to // the end of manifest_base_url_. UNREFERENCED_PARAMETER(error_info); *full_url = manifest_base_url_ + relative_url; return true; } virtual bool GetFileKeys(std::set<nacl::string>* keys) const { // Does not support enumeration. PLUGIN_PRINTF(("PnaclManifest does not support key enumeration\n")); UNREFERENCED_PARAMETER(keys); return false; } virtual bool ResolveKey(const nacl::string& key, nacl::string* full_url, PnaclOptions* pnacl_options, ErrorInfo* error_info) const { // All of the component files are native (do not require pnacl translate). pnacl_options->set_translate(false); // We can only resolve keys in the files/ namespace. const nacl::string kFilesPrefix = "files/"; size_t files_prefix_pos = key.find(kFilesPrefix); if (files_prefix_pos == nacl::string::npos) { error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL, "key did not start with files/"); return false; } // Resolve the full URL to the file. Provide it with a platform-specific // prefix. nacl::string key_basename = key.substr(kFilesPrefix.length()); return ResolveURL(PnaclUrls::PrependPlatformPrefix(key_basename), full_url, error_info); } private: NACL_DISALLOW_COPY_AND_ASSIGN(PnaclManifest); nacl::string manifest_base_url_; }; ////////////////////////////////////////////////////////////////////// // UMA stat helpers. ////////////////////////////////////////////////////////////////////// namespace { // Assume translation time metrics *can be* large. // Up to 12 minutes. const int64_t kTimeLargeMin = 10; // in ms const int64_t kTimeLargeMax = 720000; // in ms const uint32_t kTimeLargeBuckets = 100; const int32_t kSizeKBMin = 1; const int32_t kSizeKBMax = 512*1024; // very large .pexe / .nexe. const uint32_t kSizeKBBuckets = 100; const int32_t kRatioMin = 10; const int32_t kRatioMax = 10*100; // max of 10x difference. const uint32_t kRatioBuckets = 100; const int32_t kKBPSMin = 1; const int32_t kKBPSMax = 30*1000; // max of 30 MB / sec. const uint32_t kKBPSBuckets = 100; const PPB_UMA_Private* uma_interface = NULL; const PPB_UMA_Private* GetUMAInterface() { if (uma_interface != NULL) { return uma_interface; } pp::Module *module = pp::Module::Get(); DCHECK(module); uma_interface = static_cast<const PPB_UMA_Private*>( module->GetBrowserInterface(PPB_UMA_PRIVATE_INTERFACE)); return uma_interface; } void HistogramTime(const std::string& name, int64_t ms) { if (ms < 0) return; const PPB_UMA_Private* ptr = GetUMAInterface(); if (ptr == NULL) return; ptr->HistogramCustomTimes(pp::Var(name).pp_var(), ms, kTimeLargeMin, kTimeLargeMax, kTimeLargeBuckets); } void HistogramSizeKB(const std::string& name, int32_t kb) { if (kb < 0) return; const PPB_UMA_Private* ptr = GetUMAInterface(); if (ptr == NULL) return; ptr->HistogramCustomCounts(pp::Var(name).pp_var(), kb, kSizeKBMin, kSizeKBMax, kSizeKBBuckets); } void HistogramRatio(const std::string& name, int64_t a, int64_t b) { if (a < 0 || b <= 0) return; const PPB_UMA_Private* ptr = GetUMAInterface(); if (ptr == NULL) return; ptr->HistogramCustomCounts(pp::Var(name).pp_var(), 100 * a / b, kRatioMin, kRatioMax, kRatioBuckets); } void HistogramKBPerSec(const std::string& name, double kb, double s) { if (kb < 0.0 || s <= 0.0) return; const PPB_UMA_Private* ptr = GetUMAInterface(); if (ptr == NULL) return; ptr->HistogramCustomCounts(pp::Var(name).pp_var(), static_cast<int64_t>(kb / s), kKBPSMin, kKBPSMax, kKBPSBuckets); } void HistogramEnumerateTranslationCache(bool hit) { const PPB_UMA_Private* ptr = GetUMAInterface(); if (ptr == NULL) return; ptr->HistogramEnumeration(pp::Var("NaCl.Perf.PNaClCache.IsHit").pp_var(), hit, 2); } // Opt level is expected to be 0 to 3. Treating 4 as unknown. const int8_t kOptUnknown = 4; void HistogramOptLevel(int8_t opt_level) { const PPB_UMA_Private* ptr = GetUMAInterface(); if (ptr == NULL) return; if (opt_level < 0 || opt_level > 3) { opt_level = kOptUnknown; } ptr->HistogramEnumeration(pp::Var("NaCl.Options.PNaCl.OptLevel").pp_var(), opt_level, kOptUnknown+1); } } // namespace ////////////////////////////////////////////////////////////////////// // The coordinator class. ////////////////////////////////////////////////////////////////////// // Out-of-line destructor to keep it from getting put in every .o where // callback_source.h is included template<> CallbackSource<FileStreamData>::~CallbackSource() {} PnaclCoordinator* PnaclCoordinator::BitcodeToNative( Plugin* plugin, const nacl::string& pexe_url, const PnaclOptions& pnacl_options, const pp::CompletionCallback& translate_notify_callback) { PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (plugin=%p, pexe=%s)\n", static_cast<void*>(plugin), pexe_url.c_str())); PnaclCoordinator* coordinator = new PnaclCoordinator(plugin, pexe_url, pnacl_options, translate_notify_callback); coordinator->pnacl_init_time_ = NaClGetTimeOfDayMicroseconds(); PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (manifest=%p, ", reinterpret_cast<const void*>(coordinator->manifest_.get()))); // First start a network request for the pexe, to tickle the component // updater's On-Demand resource throttler, and to get Last-Modified/ETag // cache information. We can cancel the request later if there's // a bitcode->nexe cache hit. coordinator->OpenBitcodeStream(); return coordinator; } PnaclCoordinator::PnaclCoordinator( Plugin* plugin, const nacl::string& pexe_url, const PnaclOptions& pnacl_options, const pp::CompletionCallback& translate_notify_callback) : translate_finish_error_(PP_OK), plugin_(plugin), translate_notify_callback_(translate_notify_callback), translation_finished_reported_(false), manifest_(new PnaclManifest()), pexe_url_(pexe_url), pnacl_options_(pnacl_options), is_cache_hit_(PP_FALSE), error_already_reported_(false), pnacl_init_time_(0), pexe_size_(0), pexe_bytes_compiled_(0), expected_pexe_size_(-1) { PLUGIN_PRINTF(("PnaclCoordinator::PnaclCoordinator (this=%p, plugin=%p)\n", static_cast<void*>(this), static_cast<void*>(plugin))); callback_factory_.Initialize(this); } PnaclCoordinator::~PnaclCoordinator() { PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, " "translate_thread=%p\n", static_cast<void*>(this), translate_thread_.get())); // Stopping the translate thread will cause the translate thread to try to // run translation_complete_callback_ on the main thread. This destructor is // running from the main thread, and by the time it exits, callback_factory_ // will have been destroyed. This will result in the cancellation of // translation_complete_callback_, so no notification will be delivered. if (translate_thread_.get() != NULL) { translate_thread_->AbortSubprocesses(); } if (!translation_finished_reported_) { plugin_->nacl_interface()->ReportTranslationFinished( plugin_->pp_instance(), PP_FALSE); } } nacl::DescWrapper* PnaclCoordinator::ReleaseTranslatedFD() { DCHECK(temp_nexe_file_ != NULL); return temp_nexe_file_->release_read_wrapper(); } void PnaclCoordinator::ReportNonPpapiError(enum PluginErrorCode err_code, const nacl::string& message) { error_info_.SetReport(err_code, message); ExitWithError(); } void PnaclCoordinator::ReportPpapiError(enum PluginErrorCode err_code, int32_t pp_error, const nacl::string& message) { nacl::stringstream ss; ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ")."; error_info_.SetReport(err_code, ss.str()); ExitWithError(); } void PnaclCoordinator::ExitWithError() { PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError (error_code=%d, " "message='%s')\n", error_info_.error_code(), error_info_.message().c_str())); plugin_->ReportLoadError(error_info_); // Free all the intermediate callbacks we ever created. // Note: this doesn't *cancel* the callbacks from the factories attached // to the various helper classes (e.g., pnacl_resources). Thus, those // callbacks may still run asynchronously. We let those run but ignore // any other errors they may generate so that they do not end up running // translate_notify_callback_, which has already been freed. callback_factory_.CancelAll(); if (!error_already_reported_) { error_already_reported_ = true; translation_finished_reported_ = true; plugin_->nacl_interface()->ReportTranslationFinished( plugin_->pp_instance(), PP_FALSE); translate_notify_callback_.Run(PP_ERROR_FAILED); } else { PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError an earlier error was " "already reported -- Skipping.\n")); } } // Signal that Pnacl translation completed normally. void PnaclCoordinator::TranslateFinished(int32_t pp_error) { PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%" NACL_PRId32 ")\n", pp_error)); // Bail out if there was an earlier error (e.g., pexe load failure), // or if there is an error from the translation thread. if (translate_finish_error_ != PP_OK || pp_error != PP_OK) { ExitWithError(); return; } // Send out one last progress event, to finish up the progress events // that were delayed (see the delay inserted in BitcodeGotCompiled). if (ExpectedProgressKnown()) { pexe_bytes_compiled_ = expected_pexe_size_; plugin_->EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS, pexe_url_, plugin::Plugin::LENGTH_IS_COMPUTABLE, pexe_bytes_compiled_, expected_pexe_size_); } // If there are no errors, report stats from this thread (the main thread). HistogramOptLevel(pnacl_options_.opt_level()); const plugin::PnaclTimeStats& time_stats = translate_thread_->GetTimeStats(); HistogramTime("NaCl.Perf.PNaClLoadTime.LoadCompiler", time_stats.pnacl_llc_load_time / NACL_MICROS_PER_MILLI); HistogramTime("NaCl.Perf.PNaClLoadTime.CompileTime", time_stats.pnacl_compile_time / NACL_MICROS_PER_MILLI); HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec", pexe_size_ / 1024.0, time_stats.pnacl_compile_time / 1000000.0); HistogramTime("NaCl.Perf.PNaClLoadTime.LoadLinker", time_stats.pnacl_ld_load_time / NACL_MICROS_PER_MILLI); HistogramTime("NaCl.Perf.PNaClLoadTime.LinkTime", time_stats.pnacl_link_time / NACL_MICROS_PER_MILLI); HistogramSizeKB("NaCl.Perf.Size.Pexe", static_cast<int64_t>(pexe_size_ / 1024)); struct nacl_abi_stat stbuf; struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc(); int stat_ret; if (0 != (stat_ret = (*((struct NaClDescVtbl const *) desc->base.vtbl)-> Fstat)(desc, &stbuf))) { PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished can't stat nexe.\n")); } else { size_t nexe_size = stbuf.nacl_abi_st_size; HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe", static_cast<int64_t>(nexe_size / 1024)); HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct", pexe_size_, nexe_size); } int64_t total_time = NaClGetTimeOfDayMicroseconds() - pnacl_init_time_; HistogramTime("NaCl.Perf.PNaClLoadTime.TotalUncachedTime", total_time / NACL_MICROS_PER_MILLI); HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec", pexe_size_ / 1024.0, total_time / 1000000.0); // The nexe is written to the temp_nexe_file_. We must Reset() the file // pointer to be able to read it again from the beginning. temp_nexe_file_->Reset(); // Report to the browser that translation finished. The browser will take // care of storing the nexe in the cache. translation_finished_reported_ = true; plugin_->nacl_interface()->ReportTranslationFinished( plugin_->pp_instance(), PP_TRUE); NexeReadDidOpen(PP_OK); } void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) { PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%" NACL_PRId32 ")\n", pp_error)); if (pp_error != PP_OK) { if (pp_error == PP_ERROR_FILENOTFOUND) { ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOTFOUND, pp_error, "Failed to open translated nexe (not found)."); return; } if (pp_error == PP_ERROR_NOACCESS) { ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOACCESS, pp_error, "Failed to open translated nexe (no access)."); return; } ReportPpapiError(ERROR_PNACL_CACHE_FETCH_OTHER, pp_error, "Failed to open translated nexe."); return; } translate_notify_callback_.Run(pp_error); } void PnaclCoordinator::OpenBitcodeStream() { // Now open the pexe stream. streaming_downloader_.reset(new FileDownloader()); streaming_downloader_->Initialize(plugin_); // Mark the request as requesting a PNaCl bitcode file, // so that component updater can detect this user action. streaming_downloader_->set_request_headers( "Accept: application/x-pnacl, */*"); // Even though we haven't started downloading, create the translation // thread object immediately. This ensures that any pieces of the file // that get downloaded before the compilation thread is accepting // SRPCs won't get dropped. translate_thread_.reset(new PnaclTranslateThread()); if (translate_thread_ == NULL) { ReportNonPpapiError( ERROR_PNACL_THREAD_CREATE, "PnaclCoordinator: could not allocate translation thread."); return; } pp::CompletionCallback cb = callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidOpen); if (!streaming_downloader_->OpenStream(pexe_url_, cb, this)) { ReportNonPpapiError( ERROR_PNACL_PEXE_FETCH_OTHER, nacl::string("PnaclCoordinator: failed to open stream ") + pexe_url_); return; } } void PnaclCoordinator::BitcodeStreamDidOpen(int32_t pp_error) { if (pp_error != PP_OK) { BitcodeStreamDidFinish(pp_error); // We have not spun up the translation process yet, so we need to call // TranslateFinished here. TranslateFinished(pp_error); return; } // The component updater's resource throttles + OnDemand update/install // should block the URL request until the compiler is present. Now we // can load the resources (e.g. llc and ld nexes). resources_.reset(new PnaclResources(plugin_, this, this->manifest_.get())); CHECK(resources_ != NULL); // The first step of loading resources: read the resource info file. pp::CompletionCallback resource_info_read_cb = callback_factory_.NewCallback(&PnaclCoordinator::ResourceInfoWasRead); resources_->ReadResourceInfo(PnaclUrls::GetResourceInfoUrl(), resource_info_read_cb); } void PnaclCoordinator::ResourceInfoWasRead(int32_t pp_error) { PLUGIN_PRINTF(("PluginCoordinator::ResourceInfoWasRead (pp_error=%" NACL_PRId32 ")\n", pp_error)); // Second step of loading resources: call StartLoad to load pnacl-llc // and pnacl-ld, based on the filenames found in the resource info file. pp::CompletionCallback resources_cb = callback_factory_.NewCallback(&PnaclCoordinator::ResourcesDidLoad); resources_->StartLoad(resources_cb); } void PnaclCoordinator::ResourcesDidLoad(int32_t pp_error) { PLUGIN_PRINTF(("PnaclCoordinator::ResourcesDidLoad (pp_error=%" NACL_PRId32 ")\n", pp_error)); if (pp_error != PP_OK) { // Finer-grained error code should have already been reported by // the PnaclResources class. return; } // Okay, now that we've started the HTTP request for the pexe // and we've ensured that the PNaCl compiler + metadata is installed, // get the cache key from the response headers and from the // compiler's version metadata. nacl::string headers = streaming_downloader_->GetResponseHeaders(); NaClHttpResponseHeaders parser; parser.Parse(headers); temp_nexe_file_.reset(new TempFile(plugin_)); pp::CompletionCallback cb = callback_factory_.NewCallback(&PnaclCoordinator::NexeFdDidOpen); int32_t nexe_fd_err = plugin_->nacl_interface()->GetNexeFd( plugin_->pp_instance(), streaming_downloader_->url().c_str(), // TODO(dschuff): Get this value from the pnacl json file after it // rolls in from NaCl. 1, pnacl_options_.opt_level(), parser.GetHeader("last-modified").c_str(), parser.GetHeader("etag").c_str(), PP_FromBool(parser.CacheControlNoStore()), &is_cache_hit_, temp_nexe_file_->existing_handle(), cb.pp_completion_callback()); if (nexe_fd_err < PP_OK_COMPLETIONPENDING) { ReportPpapiError(ERROR_PNACL_CREATE_TEMP, nexe_fd_err, nacl::string("Call to GetNexeFd failed")); } } void PnaclCoordinator::NexeFdDidOpen(int32_t pp_error) { PLUGIN_PRINTF(("PnaclCoordinator::NexeFdDidOpen (pp_error=%" NACL_PRId32 ", hit=%d, handle=%d)\n", pp_error, is_cache_hit_ == PP_TRUE, *temp_nexe_file_->existing_handle())); if (pp_error < PP_OK) { ReportPpapiError(ERROR_PNACL_CREATE_TEMP, pp_error, nacl::string("GetNexeFd failed")); return; } if (*temp_nexe_file_->existing_handle() == PP_kInvalidFileHandle) { ReportNonPpapiError( ERROR_PNACL_CREATE_TEMP, nacl::string( "PnaclCoordinator: Got bad temp file handle from GetNexeFd")); return; } HistogramEnumerateTranslationCache(is_cache_hit_); if (is_cache_hit_ == PP_TRUE) { // Cache hit -- no need to stream the rest of the file. streaming_downloader_.reset(NULL); // Open it for reading as the cached nexe file. pp::CompletionCallback cb = callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen); temp_nexe_file_->Open(cb, false); } else { // Open an object file first so the translator can start writing to it // during streaming translation. obj_file_.reset(new TempFile(plugin_)); pp::CompletionCallback obj_cb = callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen); obj_file_->Open(obj_cb, true); // Meanwhile, a miss means we know we need to stream the bitcode, so stream // the rest of it now. (Calling FinishStreaming means that the downloader // will begin handing data to the coordinator, which is safe any time after // the translate_thread_ object has been initialized). pp::CompletionCallback finish_cb = callback_factory_.NewCallback( &PnaclCoordinator::BitcodeStreamDidFinish); streaming_downloader_->FinishStreaming(finish_cb); } } void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) { PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%" NACL_PRId32 ")\n", pp_error)); if (pp_error != PP_OK) { // Defer reporting the error and cleanup until after the translation // thread returns, because it may be accessing the coordinator's // objects or writing to the files. translate_finish_error_ = pp_error; if (pp_error == PP_ERROR_ABORTED) { error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_ABORTED, "PnaclCoordinator: pexe load failed (aborted)."); } if (pp_error == PP_ERROR_NOACCESS) { error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_NOACCESS, "PnaclCoordinator: pexe load failed (no access)."); } else { nacl::stringstream ss; ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ")."; error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_OTHER, ss.str()); } translate_thread_->AbortSubprocesses(); } else { // Compare download completion pct (100% now), to compile completion pct. HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded", pexe_bytes_compiled_, pexe_size_); } } void PnaclCoordinator::BitcodeStreamGotData(int32_t pp_error, FileStreamData data) { PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamGotData (pp_error=%" NACL_PRId32 ", data=%p)\n", pp_error, data ? &(*data)[0] : 0)); DCHECK(translate_thread_.get()); translate_thread_->PutBytes(data, pp_error); // If pp_error > 0, then it represents the number of bytes received. if (data && pp_error > 0) { pexe_size_ += pp_error; } } StreamCallback PnaclCoordinator::GetCallback() { return callback_factory_.NewCallbackWithOutput( &PnaclCoordinator::BitcodeStreamGotData); } void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error, int64_t bytes_compiled) { pexe_bytes_compiled_ += bytes_compiled; // If we don't know the expected total yet, ask. if (!ExpectedProgressKnown()) { int64_t amount_downloaded; // dummy variable. streaming_downloader_->GetDownloadProgress(&amount_downloaded, &expected_pexe_size_); } // Hold off reporting the last few bytes of progress, since we don't know // when they are actually completely compiled. "bytes_compiled" only means // that bytes were sent to the compiler. if (ExpectedProgressKnown()) { if (!ShouldDelayProgressEvent()) { plugin_->EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS, pexe_url_, plugin::Plugin::LENGTH_IS_COMPUTABLE, pexe_bytes_compiled_, expected_pexe_size_); } } else { plugin_->EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS, pexe_url_, plugin::Plugin::LENGTH_IS_NOT_COMPUTABLE, pexe_bytes_compiled_, expected_pexe_size_); } } pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback( int64_t bytes_compiled) { return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled, bytes_compiled); } void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded, int64_t* bytes_total) { *bytes_loaded = pexe_bytes_compiled_; *bytes_total = expected_pexe_size_; } void PnaclCoordinator::ObjectFileDidOpen(int32_t pp_error) { PLUGIN_PRINTF(("PnaclCoordinator::ObjectFileDidOpen (pp_error=%" NACL_PRId32 ")\n", pp_error)); if (pp_error != PP_OK) { ReportPpapiError(ERROR_PNACL_CREATE_TEMP, pp_error, "Failed to open scratch object file."); return; } // Open the nexe file for connecting ld and sel_ldr. // Start translation when done with this last step of setup! pp::CompletionCallback cb = callback_factory_.NewCallback(&PnaclCoordinator::RunTranslate); temp_nexe_file_->Open(cb, true); } void PnaclCoordinator::RunTranslate(int32_t pp_error) { PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%" NACL_PRId32 ")\n", pp_error)); // Invoke llc followed by ld off the main thread. This allows use of // blocking RPCs that would otherwise block the JavaScript main thread. pp::CompletionCallback report_translate_finished = callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished); CHECK(translate_thread_ != NULL); translate_thread_->RunTranslate(report_translate_finished, manifest_.get(), obj_file_.get(), temp_nexe_file_.get(), &error_info_, resources_.get(), &pnacl_options_, this, plugin_); } } // namespace plugin