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