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