- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / src / trusted / plugin / pnacl_coordinator.cc
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
27 namespace 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.
38 class 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
104 namespace {
105
106 // Assume translation time metrics *can be* large.
107 // Up to 12 minutes.
108 const int64_t kTimeLargeMin = 10;          // in ms
109 const int64_t kTimeLargeMax = 720000;      // in ms
110 const uint32_t kTimeLargeBuckets = 100;
111
112 const int32_t kSizeKBMin = 1;
113 const int32_t kSizeKBMax = 512*1024;       // very large .pexe / .nexe.
114 const uint32_t kSizeKBBuckets = 100;
115
116 const int32_t kRatioMin = 10;
117 const int32_t kRatioMax = 10*100;          // max of 10x difference.
118 const uint32_t kRatioBuckets = 100;
119
120 const int32_t kKBPSMin = 1;
121 const int32_t kKBPSMax = 30*1000;          // max of 30 MB / sec.
122 const uint32_t kKBPSBuckets = 100;
123
124 const PPB_UMA_Private* uma_interface = NULL;
125
126 const 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
137 void 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
149 void 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
161 void 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
173 void 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
185 void 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.
193 const int8_t kOptUnknown = 4;
194
195 void 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
214 template<>
215 CallbackSource<FileStreamData>::~CallbackSource() {}
216
217 PnaclCoordinator* 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 start a network request for the pexe, to tickle the component
237   // updater's On-Demand resource throttler, and to get Last-Modified/ETag
238   // cache information. We can cancel the request later if there's
239   // a bitcode->nexe cache hit.
240   coordinator->OpenBitcodeStream();
241   return coordinator;
242 }
243
244 PnaclCoordinator::PnaclCoordinator(
245     Plugin* plugin,
246     const nacl::string& pexe_url,
247     const PnaclOptions& pnacl_options,
248     const pp::CompletionCallback& translate_notify_callback)
249   : translate_finish_error_(PP_OK),
250     plugin_(plugin),
251     translate_notify_callback_(translate_notify_callback),
252     translation_finished_reported_(false),
253     manifest_(new PnaclManifest()),
254     pexe_url_(pexe_url),
255     pnacl_options_(pnacl_options),
256     is_cache_hit_(PP_FALSE),
257     error_already_reported_(false),
258     off_the_record_(false),
259     pnacl_init_time_(0),
260     pexe_size_(0),
261     pexe_bytes_compiled_(0),
262     expected_pexe_size_(-1) {
263   PLUGIN_PRINTF(("PnaclCoordinator::PnaclCoordinator (this=%p, plugin=%p)\n",
264                  static_cast<void*>(this), static_cast<void*>(plugin)));
265   callback_factory_.Initialize(this);
266 }
267
268 PnaclCoordinator::~PnaclCoordinator() {
269   PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, "
270                  "translate_thread=%p\n",
271                  static_cast<void*>(this), translate_thread_.get()));
272   // Stopping the translate thread will cause the translate thread to try to
273   // run translation_complete_callback_ on the main thread.  This destructor is
274   // running from the main thread, and by the time it exits, callback_factory_
275   // will have been destroyed.  This will result in the cancellation of
276   // translation_complete_callback_, so no notification will be delivered.
277   if (translate_thread_.get() != NULL) {
278     translate_thread_->AbortSubprocesses();
279   }
280   if (!translation_finished_reported_) {
281     plugin_->nacl_interface()->ReportTranslationFinished(
282         plugin_->pp_instance(),
283         PP_FALSE);
284   }
285 }
286
287 nacl::DescWrapper* PnaclCoordinator::ReleaseTranslatedFD() {
288   DCHECK(temp_nexe_file_ != NULL);
289   return temp_nexe_file_->release_read_wrapper();
290 }
291
292 void PnaclCoordinator::ReportNonPpapiError(enum PluginErrorCode err_code,
293                                            const nacl::string& message) {
294   error_info_.SetReport(err_code, message);
295   ExitWithError();
296 }
297
298 void PnaclCoordinator::ReportPpapiError(enum PluginErrorCode err_code,
299                                         int32_t pp_error,
300                                         const nacl::string& message) {
301   nacl::stringstream ss;
302   ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ").";
303   error_info_.SetReport(err_code, ss.str());
304   ExitWithError();
305 }
306
307 void PnaclCoordinator::ExitWithError() {
308   PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError (error_code=%d, "
309                  "message='%s')\n",
310                  error_info_.error_code(),
311                  error_info_.message().c_str()));
312   plugin_->ReportLoadError(error_info_);
313   // Free all the intermediate callbacks we ever created.
314   // Note: this doesn't *cancel* the callbacks from the factories attached
315   // to the various helper classes (e.g., pnacl_resources). Thus, those
316   // callbacks may still run asynchronously.  We let those run but ignore
317   // any other errors they may generate so that they do not end up running
318   // translate_notify_callback_, which has already been freed.
319   callback_factory_.CancelAll();
320   if (!error_already_reported_) {
321     error_already_reported_ = true;
322     translation_finished_reported_ = true;
323     plugin_->nacl_interface()->ReportTranslationFinished(
324         plugin_->pp_instance(),
325         PP_FALSE);
326     translate_notify_callback_.Run(PP_ERROR_FAILED);
327   } else {
328     PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError an earlier error was "
329                    "already reported -- Skipping.\n"));
330   }
331 }
332
333 // Signal that Pnacl translation completed normally.
334 void PnaclCoordinator::TranslateFinished(int32_t pp_error) {
335   PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%"
336                  NACL_PRId32 ")\n", pp_error));
337   // Bail out if there was an earlier error (e.g., pexe load failure),
338   // or if there is an error from the translation thread.
339   if (translate_finish_error_ != PP_OK || pp_error != PP_OK) {
340     ExitWithError();
341     return;
342   }
343   // Send out one last progress event, to finish up the progress events
344   // that were delayed (see the delay inserted in BitcodeGotCompiled).
345   if (ExpectedProgressKnown()) {
346     pexe_bytes_compiled_ = expected_pexe_size_;
347     plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
348                                   pexe_url_,
349                                   plugin::Plugin::LENGTH_IS_COMPUTABLE,
350                                   pexe_bytes_compiled_,
351                                   expected_pexe_size_);
352   }
353
354   // If there are no errors, report stats from this thread (the main thread).
355   HistogramOptLevel(pnacl_options_.opt_level());
356   const plugin::PnaclTimeStats& time_stats = translate_thread_->GetTimeStats();
357   HistogramTime("NaCl.Perf.PNaClLoadTime.LoadCompiler",
358                 time_stats.pnacl_llc_load_time / NACL_MICROS_PER_MILLI);
359   HistogramTime("NaCl.Perf.PNaClLoadTime.CompileTime",
360                 time_stats.pnacl_compile_time / NACL_MICROS_PER_MILLI);
361   HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
362                     pexe_size_ / 1024.0,
363                     time_stats.pnacl_compile_time / 1000000.0);
364   HistogramTime("NaCl.Perf.PNaClLoadTime.LoadLinker",
365                 time_stats.pnacl_ld_load_time / NACL_MICROS_PER_MILLI);
366   HistogramTime("NaCl.Perf.PNaClLoadTime.LinkTime",
367                 time_stats.pnacl_link_time / NACL_MICROS_PER_MILLI);
368   HistogramSizeKB("NaCl.Perf.Size.Pexe",
369                   static_cast<int64_t>(pexe_size_ / 1024));
370
371   struct nacl_abi_stat stbuf;
372   struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc();
373   int stat_ret;
374   if (0 != (stat_ret = (*((struct NaClDescVtbl const *) desc->base.vtbl)->
375                         Fstat)(desc, &stbuf))) {
376     PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished can't stat nexe.\n"));
377   } else {
378     size_t nexe_size = stbuf.nacl_abi_st_size;
379     HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe",
380                     static_cast<int64_t>(nexe_size / 1024));
381     HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct", pexe_size_, nexe_size);
382   }
383
384   int64_t total_time = NaClGetTimeOfDayMicroseconds() - pnacl_init_time_;
385   HistogramTime("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
386                 total_time / NACL_MICROS_PER_MILLI);
387   HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
388                     pexe_size_ / 1024.0,
389                     total_time / 1000000.0);
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   // Report to the browser that translation finished. The browser will take
396   // care of storing the nexe in the cache.
397   translation_finished_reported_ = true;
398   plugin_->nacl_interface()->ReportTranslationFinished(
399       plugin_->pp_instance(), PP_TRUE);
400
401   NexeReadDidOpen(PP_OK);
402 }
403
404 void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) {
405   PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%"
406                  NACL_PRId32 ")\n", pp_error));
407   if (pp_error != PP_OK) {
408     if (pp_error == PP_ERROR_FILENOTFOUND) {
409       ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOTFOUND,
410                        pp_error,
411                        "Failed to open translated nexe (not found).");
412       return;
413     }
414     if (pp_error == PP_ERROR_NOACCESS) {
415       ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOACCESS,
416                        pp_error,
417                        "Failed to open translated nexe (no access).");
418       return;
419     }
420     ReportPpapiError(ERROR_PNACL_CACHE_FETCH_OTHER,
421                      pp_error,
422                      "Failed to open translated nexe.");
423     return;
424   }
425
426   translate_notify_callback_.Run(pp_error);
427 }
428
429 void PnaclCoordinator::OpenBitcodeStream() {
430   // Now open the pexe stream.
431   streaming_downloader_.reset(new FileDownloader());
432   streaming_downloader_->Initialize(plugin_);
433   // Mark the request as requesting a PNaCl bitcode file,
434   // so that component updater can detect this user action.
435   streaming_downloader_->set_request_headers(
436       "Accept: application/x-pnacl, */*");
437
438   // Even though we haven't started downloading, create the translation
439   // thread object immediately. This ensures that any pieces of the file
440   // that get downloaded before the compilation thread is accepting
441   // SRPCs won't get dropped.
442   translate_thread_.reset(new PnaclTranslateThread());
443   if (translate_thread_ == NULL) {
444     ReportNonPpapiError(
445         ERROR_PNACL_THREAD_CREATE,
446         "PnaclCoordinator: could not allocate translation thread.");
447     return;
448   }
449
450   pp::CompletionCallback cb =
451       callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidOpen);
452   if (!streaming_downloader_->OpenStream(pexe_url_, cb, this)) {
453     ReportNonPpapiError(
454         ERROR_PNACL_PEXE_FETCH_OTHER,
455         nacl::string("PnaclCoordinator: failed to open stream ") + pexe_url_);
456     return;
457   }
458 }
459
460 void PnaclCoordinator::BitcodeStreamDidOpen(int32_t pp_error) {
461   if (pp_error != PP_OK) {
462     BitcodeStreamDidFinish(pp_error);
463     // We have not spun up the translation process yet, so we need to call
464     // TranslateFinished here.
465     TranslateFinished(pp_error);
466     return;
467   }
468
469   // The component updater's resource throttles + OnDemand update/install
470   // should block the URL request until the compiler is present. Now we
471   // can load the resources (e.g. llc and ld nexes).
472   resources_.reset(new PnaclResources(plugin_, this, this->manifest_.get()));
473   CHECK(resources_ != NULL);
474
475   // The first step of loading resources: read the resource info file.
476   pp::CompletionCallback resource_info_read_cb =
477       callback_factory_.NewCallback(&PnaclCoordinator::ResourceInfoWasRead);
478   resources_->ReadResourceInfo(PnaclUrls::GetResourceInfoUrl(),
479                                resource_info_read_cb);
480 }
481
482 void PnaclCoordinator::ResourceInfoWasRead(int32_t pp_error) {
483   PLUGIN_PRINTF(("PluginCoordinator::ResourceInfoWasRead (pp_error=%"
484                 NACL_PRId32 ")\n", pp_error));
485   // Second step of loading resources: call StartLoad to load pnacl-llc
486   // and pnacl-ld, based on the filenames found in the resource info file.
487   pp::CompletionCallback resources_cb =
488       callback_factory_.NewCallback(&PnaclCoordinator::ResourcesDidLoad);
489   resources_->StartLoad(resources_cb);
490 }
491
492 void PnaclCoordinator::ResourcesDidLoad(int32_t pp_error) {
493   PLUGIN_PRINTF(("PnaclCoordinator::ResourcesDidLoad (pp_error=%"
494                  NACL_PRId32 ")\n", pp_error));
495   if (pp_error != PP_OK) {
496     // Finer-grained error code should have already been reported by
497     // the PnaclResources class.
498     return;
499   }
500
501   // Okay, now that we've started the HTTP request for the pexe
502   // and we've ensured that the PNaCl compiler + metadata is installed,
503   // get the cache key from the response headers and from the
504   // compiler's version metadata.
505   nacl::string headers = streaming_downloader_->GetResponseHeaders();
506   NaClHttpResponseHeaders parser;
507   parser.Parse(headers);
508
509   temp_nexe_file_.reset(new TempFile(plugin_));
510   pp::CompletionCallback cb =
511       callback_factory_.NewCallback(&PnaclCoordinator::NexeFdDidOpen);
512   int32_t nexe_fd_err =
513       plugin_->nacl_interface()->GetNexeFd(
514           plugin_->pp_instance(),
515           streaming_downloader_->url().c_str(),
516           // TODO(dschuff): Get this value from the pnacl json file after it
517           // rolls in from NaCl.
518           1,
519           pnacl_options_.opt_level(),
520           parser.GetHeader("last-modified").c_str(),
521           parser.GetHeader("etag").c_str(),
522           PP_FromBool(parser.CacheControlNoStore()),
523           &is_cache_hit_,
524           temp_nexe_file_->existing_handle(),
525           cb.pp_completion_callback());
526   if (nexe_fd_err < PP_OK_COMPLETIONPENDING) {
527     ReportPpapiError(ERROR_PNACL_CREATE_TEMP, nexe_fd_err,
528                      nacl::string("Call to GetNexeFd failed"));
529   }
530 }
531
532 void PnaclCoordinator::NexeFdDidOpen(int32_t pp_error) {
533   PLUGIN_PRINTF(("PnaclCoordinator::NexeFdDidOpen (pp_error=%"
534                  NACL_PRId32 ", hit=%d, handle=%d)\n", pp_error,
535                  is_cache_hit_ == PP_TRUE,
536                  *temp_nexe_file_->existing_handle()));
537   if (pp_error < PP_OK) {
538     ReportPpapiError(ERROR_PNACL_CREATE_TEMP, pp_error,
539                      nacl::string("GetNexeFd failed"));
540     return;
541   }
542
543   if (*temp_nexe_file_->existing_handle() == PP_kInvalidFileHandle) {
544     ReportNonPpapiError(
545         ERROR_PNACL_CREATE_TEMP,
546         nacl::string(
547             "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
548     return;
549   }
550   HistogramEnumerateTranslationCache(is_cache_hit_);
551
552   if (is_cache_hit_ == PP_TRUE) {
553     // Cache hit -- no need to stream the rest of the file.
554     streaming_downloader_.reset(NULL);
555     // Open it for reading as the cached nexe file.
556     pp::CompletionCallback cb =
557         callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen);
558     temp_nexe_file_->Open(cb, false);
559   } else {
560     // Open an object file first so the translator can start writing to it
561     // during streaming translation.
562     obj_file_.reset(new TempFile(plugin_));
563     pp::CompletionCallback obj_cb =
564         callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen);
565     obj_file_->Open(obj_cb, true);
566
567     // Meanwhile, a miss means we know we need to stream the bitcode, so stream
568     // the rest of it now. (Calling FinishStreaming means that the downloader
569     // will begin handing data to the coordinator, which is safe any time after
570     // the translate_thread_ object has been initialized).
571     pp::CompletionCallback finish_cb = callback_factory_.NewCallback(
572         &PnaclCoordinator::BitcodeStreamDidFinish);
573     streaming_downloader_->FinishStreaming(finish_cb);
574   }
575 }
576
577 void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) {
578   PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
579                  NACL_PRId32 ")\n", pp_error));
580   if (pp_error != PP_OK) {
581     // Defer reporting the error and cleanup until after the translation
582     // thread returns, because it may be accessing the coordinator's
583     // objects or writing to the files.
584     translate_finish_error_ = pp_error;
585     if (pp_error == PP_ERROR_ABORTED) {
586       error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_ABORTED,
587                             "PnaclCoordinator: pexe load failed (aborted).");
588     }
589     if (pp_error == PP_ERROR_NOACCESS) {
590       error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_NOACCESS,
591                             "PnaclCoordinator: pexe load failed (no access).");
592     } else {
593       nacl::stringstream ss;
594       ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ").";
595       error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_OTHER, ss.str());
596     }
597     translate_thread_->AbortSubprocesses();
598   } else {
599     // Compare download completion pct (100% now), to compile completion pct.
600     HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
601                    pexe_bytes_compiled_, pexe_size_);
602   }
603 }
604
605 void PnaclCoordinator::BitcodeStreamGotData(int32_t pp_error,
606                                             FileStreamData data) {
607   PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamGotData (pp_error=%"
608                  NACL_PRId32 ", data=%p)\n", pp_error, data ? &(*data)[0] : 0));
609   DCHECK(translate_thread_.get());
610
611   translate_thread_->PutBytes(data, pp_error);
612   // If pp_error > 0, then it represents the number of bytes received.
613   if (data && pp_error > 0) {
614     pexe_size_ += pp_error;
615   }
616 }
617
618 StreamCallback PnaclCoordinator::GetCallback() {
619   return callback_factory_.NewCallbackWithOutput(
620       &PnaclCoordinator::BitcodeStreamGotData);
621 }
622
623 void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error,
624                                           int64_t bytes_compiled) {
625   pexe_bytes_compiled_ += bytes_compiled;
626   // If we don't know the expected total yet, ask.
627   if (!ExpectedProgressKnown()) {
628     int64_t amount_downloaded;  // dummy variable.
629     streaming_downloader_->GetDownloadProgress(&amount_downloaded,
630                                                &expected_pexe_size_);
631   }
632   // Hold off reporting the last few bytes of progress, since we don't know
633   // when they are actually completely compiled.  "bytes_compiled" only means
634   // that bytes were sent to the compiler.
635   if (ExpectedProgressKnown()) {
636     if (!ShouldDelayProgressEvent()) {
637       plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
638                                     pexe_url_,
639                                     plugin::Plugin::LENGTH_IS_COMPUTABLE,
640                                     pexe_bytes_compiled_,
641                                     expected_pexe_size_);
642     }
643   } else {
644     plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
645                                   pexe_url_,
646                                   plugin::Plugin::LENGTH_IS_NOT_COMPUTABLE,
647                                   pexe_bytes_compiled_,
648                                   expected_pexe_size_);
649   }
650 }
651
652 pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback(
653     int64_t bytes_compiled) {
654   return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled,
655                                        bytes_compiled);
656 }
657
658 void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded,
659                                           int64_t* bytes_total) {
660   *bytes_loaded = pexe_bytes_compiled_;
661   *bytes_total = expected_pexe_size_;
662 }
663
664 void PnaclCoordinator::ObjectFileDidOpen(int32_t pp_error) {
665   PLUGIN_PRINTF(("PnaclCoordinator::ObjectFileDidOpen (pp_error=%"
666                  NACL_PRId32 ")\n", pp_error));
667   if (pp_error != PP_OK) {
668     ReportPpapiError(ERROR_PNACL_CREATE_TEMP,
669                      pp_error,
670                      "Failed to open scratch object file.");
671     return;
672   }
673   // Open the nexe file for connecting ld and sel_ldr.
674   // Start translation when done with this last step of setup!
675   pp::CompletionCallback cb =
676       callback_factory_.NewCallback(&PnaclCoordinator::RunTranslate);
677   temp_nexe_file_->Open(cb, true);
678 }
679
680 void PnaclCoordinator::RunTranslate(int32_t pp_error) {
681   PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%"
682                  NACL_PRId32 ")\n", pp_error));
683   // Invoke llc followed by ld off the main thread.  This allows use of
684   // blocking RPCs that would otherwise block the JavaScript main thread.
685   pp::CompletionCallback report_translate_finished =
686       callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished);
687
688   CHECK(translate_thread_ != NULL);
689   translate_thread_->RunTranslate(report_translate_finished,
690                                   manifest_.get(),
691                                   obj_file_.get(),
692                                   temp_nexe_file_.get(),
693                                   &error_info_,
694                                   resources_.get(),
695                                   &pnacl_options_,
696                                   this,
697                                   plugin_);
698 }
699
700 }  // namespace plugin