Home | History | Annotate | Download | only in plugin
      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