Upstream version 11.40.277.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 <algorithm>
8 #include <utility>
9
10 #include "native_client/src/include/portability_io.h"
11 #include "native_client/src/shared/platform/nacl_check.h"
12 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
13
14 #include "ppapi/c/pp_bool.h"
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/c/private/ppb_uma_private.h"
17
18 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
19 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
20 #include "ppapi/native_client/src/trusted/plugin/pnacl_translate_thread.h"
21 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
22 #include "ppapi/native_client/src/trusted/plugin/temporary_file.h"
23
24 namespace plugin {
25
26 namespace {
27
28 const int32_t kSizeKBMin = 1;
29 const int32_t kSizeKBMax = 512*1024;       // very large .pexe / .nexe.
30 const uint32_t kSizeKBBuckets = 100;
31
32 const int32_t kRatioMin = 10;
33 const int32_t kRatioMax = 10*100;          // max of 10x difference.
34 const uint32_t kRatioBuckets = 100;
35
36 void HistogramSizeKB(pp::UMAPrivate& uma,
37                      const std::string& name, int32_t kb) {
38   if (kb < 0) return;
39   uma.HistogramCustomCounts(name,
40                             kb,
41                             kSizeKBMin, kSizeKBMax,
42                             kSizeKBBuckets);
43 }
44
45 void HistogramRatio(pp::UMAPrivate& uma,
46                     const std::string& name, int64_t a, int64_t b) {
47   if (a < 0 || b <= 0) return;
48   uma.HistogramCustomCounts(name,
49                             100 * a / b,
50                             kRatioMin, kRatioMax,
51                             kRatioBuckets);
52 }
53
54 std::string GetArchitectureAttributes(Plugin* plugin) {
55   pp::Var attrs_var(pp::PASS_REF,
56                     plugin->nacl_interface()->GetCpuFeatureAttrs());
57   return attrs_var.AsString();
58 }
59
60 void DidCacheHit(void* user_data, PP_FileHandle nexe_file_handle) {
61   PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
62   coordinator->BitcodeStreamCacheHit(nexe_file_handle);
63 }
64
65 void DidCacheMiss(void* user_data, int64_t expected_pexe_size,
66                   PP_FileHandle temp_nexe_file) {
67   PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
68   coordinator->BitcodeStreamCacheMiss(expected_pexe_size,
69                                       temp_nexe_file);
70 }
71
72 void DidStreamData(void* user_data, const void* stream_data, int32_t length) {
73   PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
74   coordinator->BitcodeStreamGotData(stream_data, length);
75 }
76
77 void DidFinishStream(void* user_data, int32_t pp_error) {
78   PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
79   coordinator->BitcodeStreamDidFinish(pp_error);
80 }
81
82 PPP_PexeStreamHandler kPexeStreamHandler = {
83   &DidCacheHit,
84   &DidCacheMiss,
85   &DidStreamData,
86   &DidFinishStream
87 };
88
89 }  // namespace
90
91 PnaclCoordinator* PnaclCoordinator::BitcodeToNative(
92     Plugin* plugin,
93     const std::string& pexe_url,
94     const PP_PNaClOptions& pnacl_options,
95     const pp::CompletionCallback& translate_notify_callback) {
96   PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (plugin=%p, pexe=%s)\n",
97                  static_cast<void*>(plugin), pexe_url.c_str()));
98   PnaclCoordinator* coordinator =
99       new PnaclCoordinator(plugin, pexe_url,
100                            pnacl_options,
101                            translate_notify_callback);
102
103   GetNaClInterface()->SetPNaClStartTime(plugin->pp_instance());
104   int cpus = plugin->nacl_interface()->GetNumberOfProcessors();
105   coordinator->split_module_count_ = std::min(4, std::max(1, cpus));
106
107   // First start a network request for the pexe, to tickle the component
108   // updater's On-Demand resource throttler, and to get Last-Modified/ETag
109   // cache information. We can cancel the request later if there's
110   // a bitcode->nexe cache hit.
111   coordinator->OpenBitcodeStream();
112   return coordinator;
113 }
114
115 PnaclCoordinator::PnaclCoordinator(
116     Plugin* plugin,
117     const std::string& pexe_url,
118     const PP_PNaClOptions& pnacl_options,
119     const pp::CompletionCallback& translate_notify_callback)
120   : translate_finish_error_(PP_OK),
121     plugin_(plugin),
122     translate_notify_callback_(translate_notify_callback),
123     translation_finished_reported_(false),
124     pexe_url_(pexe_url),
125     pnacl_options_(pnacl_options),
126     architecture_attributes_(GetArchitectureAttributes(plugin)),
127     split_module_count_(1),
128     error_already_reported_(false),
129     pexe_size_(0),
130     pexe_bytes_compiled_(0),
131     expected_pexe_size_(-1) {
132   callback_factory_.Initialize(this);
133 }
134
135 PnaclCoordinator::~PnaclCoordinator() {
136   PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, "
137                  "translate_thread=%p\n",
138                  static_cast<void*>(this), translate_thread_.get()));
139   // Stopping the translate thread will cause the translate thread to try to
140   // run translation_complete_callback_ on the main thread.  This destructor is
141   // running from the main thread, and by the time it exits, callback_factory_
142   // will have been destroyed.  This will result in the cancellation of
143   // translation_complete_callback_, so no notification will be delivered.
144   if (translate_thread_.get() != NULL)
145     translate_thread_->AbortSubprocesses();
146   if (!translation_finished_reported_) {
147     plugin_->nacl_interface()->ReportTranslationFinished(
148         plugin_->pp_instance(),
149         PP_FALSE, 0, 0, 0);
150   }
151   // Force deleting the translate_thread now. It must be deleted
152   // before any scoped_* fields hanging off of PnaclCoordinator
153   // since the thread may be accessing those fields.
154   // It will also be accessing obj_files_.
155   translate_thread_.reset(NULL);
156   for (size_t i = 0; i < obj_files_.size(); i++)
157     delete obj_files_[i];
158 }
159
160 PP_FileHandle PnaclCoordinator::TakeTranslatedFileHandle() {
161   DCHECK(temp_nexe_file_ != NULL);
162   return temp_nexe_file_->TakeFileHandle();
163 }
164
165 void PnaclCoordinator::ReportNonPpapiError(PP_NaClError err_code,
166                                            const std::string& message) {
167   ErrorInfo error_info;
168   error_info.SetReport(err_code, message);
169   plugin_->ReportLoadError(error_info);
170   ExitWithError();
171 }
172
173 void PnaclCoordinator::ReportPpapiError(PP_NaClError err_code,
174                                         int32_t pp_error,
175                                         const std::string& message) {
176   std::stringstream ss;
177   ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ").";
178   ErrorInfo error_info;
179   error_info.SetReport(err_code, ss.str());
180   plugin_->ReportLoadError(error_info);
181   ExitWithError();
182 }
183
184 void PnaclCoordinator::ExitWithError() {
185   PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError\n"));
186   // Free all the intermediate callbacks we ever created.
187   // Note: this doesn't *cancel* the callbacks from the factories attached
188   // to the various helper classes (e.g., pnacl_resources). Thus, those
189   // callbacks may still run asynchronously.  We let those run but ignore
190   // any other errors they may generate so that they do not end up running
191   // translate_notify_callback_, which has already been freed.
192   callback_factory_.CancelAll();
193   if (!error_already_reported_) {
194     error_already_reported_ = true;
195     translation_finished_reported_ = true;
196     plugin_->nacl_interface()->ReportTranslationFinished(
197         plugin_->pp_instance(),
198         PP_FALSE, 0, 0, 0);
199     translate_notify_callback_.Run(PP_ERROR_FAILED);
200   }
201 }
202
203 // Signal that Pnacl translation completed normally.
204 void PnaclCoordinator::TranslateFinished(int32_t pp_error) {
205   PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%"
206                  NACL_PRId32 ")\n", pp_error));
207   // Bail out if there was an earlier error (e.g., pexe load failure),
208   // or if there is an error from the translation thread.
209   if (translate_finish_error_ != PP_OK || pp_error != PP_OK) {
210     plugin_->ReportLoadError(error_info_);
211     ExitWithError();
212     return;
213   }
214
215   // Send out one last progress event, to finish up the progress events
216   // that were delayed (see the delay inserted in BitcodeGotCompiled).
217   if (expected_pexe_size_ != -1) {
218     pexe_bytes_compiled_ = expected_pexe_size_;
219     GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
220                                       PP_NACL_EVENT_PROGRESS,
221                                       pexe_url_.c_str(),
222                                       PP_TRUE,
223                                       pexe_bytes_compiled_,
224                                       expected_pexe_size_);
225   }
226   struct nacl_abi_stat stbuf;
227   struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc();
228   if (0 == (*((struct NaClDescVtbl const *)desc->base.vtbl)->Fstat)(desc,
229                                                                     &stbuf)) {
230     size_t nexe_size = stbuf.nacl_abi_st_size;
231     HistogramSizeKB(plugin_->uma_interface(),
232                     "NaCl.Perf.Size.PNaClTranslatedNexe",
233                     static_cast<int64_t>(nexe_size / 1024));
234     HistogramRatio(plugin_->uma_interface(),
235                    "NaCl.Perf.Size.PexeNexeSizePct", pexe_size_, nexe_size);
236   }
237   // The nexe is written to the temp_nexe_file_.  We must Reset() the file
238   // pointer to be able to read it again from the beginning.
239   temp_nexe_file_->Reset();
240
241   // Report to the browser that translation finished. The browser will take
242   // care of storing the nexe in the cache.
243   translation_finished_reported_ = true;
244   plugin_->nacl_interface()->ReportTranslationFinished(
245       plugin_->pp_instance(), PP_TRUE, pnacl_options_.opt_level,
246       pexe_size_, translate_thread_->GetCompileTime());
247
248   NexeReadDidOpen(PP_OK);
249 }
250
251 void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) {
252   PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%"
253                  NACL_PRId32 ")\n", pp_error));
254   if (pp_error != PP_OK) {
255     if (pp_error == PP_ERROR_FILENOTFOUND) {
256       ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_NOTFOUND,
257                        pp_error,
258                        "Failed to open translated nexe (not found).");
259       return;
260     }
261     if (pp_error == PP_ERROR_NOACCESS) {
262       ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_NOACCESS,
263                        pp_error,
264                        "Failed to open translated nexe (no access).");
265       return;
266     }
267     ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_OTHER,
268                      pp_error,
269                      "Failed to open translated nexe.");
270     return;
271   }
272
273   translate_notify_callback_.Run(PP_OK);
274 }
275
276 void PnaclCoordinator::OpenBitcodeStream() {
277   // Even though we haven't started downloading, create the translation
278   // thread object immediately. This ensures that any pieces of the file
279   // that get downloaded before the compilation thread is accepting
280   // SRPCs won't get dropped.
281   translate_thread_.reset(new PnaclTranslateThread());
282   if (translate_thread_ == NULL) {
283     ReportNonPpapiError(
284         PP_NACL_ERROR_PNACL_THREAD_CREATE,
285         "PnaclCoordinator: could not allocate translation thread.");
286     return;
287   }
288
289   GetNaClInterface()->StreamPexe(plugin_->pp_instance(),
290                                  pexe_url_.c_str(),
291                                  pnacl_options_.opt_level,
292                                  &kPexeStreamHandler,
293                                  this);
294 }
295
296 void PnaclCoordinator::BitcodeStreamCacheHit(PP_FileHandle handle) {
297   if (handle == PP_kInvalidFileHandle) {
298     ReportNonPpapiError(
299         PP_NACL_ERROR_PNACL_CREATE_TEMP,
300         std::string(
301             "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
302     BitcodeStreamDidFinish(PP_ERROR_FAILED);
303     return;
304   }
305   temp_nexe_file_.reset(new TempFile(plugin_, handle));
306   // Open it for reading as the cached nexe file.
307   NexeReadDidOpen(temp_nexe_file_->Open(false));
308 }
309
310 void PnaclCoordinator::BitcodeStreamCacheMiss(int64_t expected_pexe_size,
311                                               PP_FileHandle nexe_handle) {
312   // IMPORTANT: Make sure that PnaclResources::StartLoad() is only
313   // called after you receive a response to a request for a .pexe file.
314   //
315   // The component updater's resource throttles + OnDemand update/install
316   // should block the URL request until the compiler is present. Now we
317   // can load the resources (e.g. llc and ld nexes).
318   resources_.reset(new PnaclResources(plugin_));
319   CHECK(resources_ != NULL);
320
321   // The first step of loading resources: read the resource info file.
322   if (!resources_->ReadResourceInfo()) {
323     ExitWithError();
324     return;
325   }
326
327   // Second step of loading resources: call StartLoad to load pnacl-llc
328   // and pnacl-ld, based on the filenames found in the resource info file.
329   if (!resources_->StartLoad()) {
330     ReportNonPpapiError(
331         PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
332         std::string("The Portable Native Client (pnacl) component is not "
333                      "installed. Please consult chrome://components for more "
334                       "information."));
335     return;
336   }
337
338   expected_pexe_size_ = expected_pexe_size;
339
340   for (int i = 0; i < split_module_count_; i++) {
341     PP_FileHandle obj_handle =
342         plugin_->nacl_interface()->CreateTemporaryFile(plugin_->pp_instance());
343     nacl::scoped_ptr<TempFile> temp_file(new TempFile(plugin_, obj_handle));
344     int32_t pp_error = temp_file->Open(true);
345     if (pp_error != PP_OK) {
346       ReportPpapiError(PP_NACL_ERROR_PNACL_CREATE_TEMP,
347                        pp_error,
348                        "Failed to open scratch object file.");
349       return;
350     } else {
351       obj_files_.push_back(temp_file.release());
352     }
353   }
354   invalid_desc_wrapper_.reset(plugin_->wrapper_factory()->MakeInvalid());
355
356   temp_nexe_file_.reset(new TempFile(plugin_, nexe_handle));
357   // Open the nexe file for connecting ld and sel_ldr.
358   // Start translation when done with this last step of setup!
359   RunTranslate(temp_nexe_file_->Open(true));
360 }
361
362 void PnaclCoordinator::BitcodeStreamGotData(const void* data, int32_t length) {
363   DCHECK(translate_thread_.get());
364
365   translate_thread_->PutBytes(data, length);
366   if (data && length > 0)
367     pexe_size_ += length;
368 }
369
370 void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) {
371   PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
372                  NACL_PRId32 ")\n", pp_error));
373   if (pp_error != PP_OK) {
374     // Defer reporting the error and cleanup until after the translation
375     // thread returns, because it may be accessing the coordinator's
376     // objects or writing to the files.
377     translate_finish_error_ = pp_error;
378     if (pp_error == PP_ERROR_ABORTED) {
379       error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_ABORTED,
380                             "PnaclCoordinator: pexe load failed (aborted).");
381     }
382     if (pp_error == PP_ERROR_NOACCESS) {
383       error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_NOACCESS,
384                             "PnaclCoordinator: pexe load failed (no access).");
385     } else {
386       std::stringstream ss;
387       ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ").";
388       error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_OTHER, ss.str());
389     }
390
391     if (translate_thread_->started())
392       translate_thread_->AbortSubprocesses();
393     else
394       TranslateFinished(pp_error);
395   } else {
396     // Compare download completion pct (100% now), to compile completion pct.
397     HistogramRatio(plugin_->uma_interface(),
398                    "NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
399                    pexe_bytes_compiled_, pexe_size_);
400     translate_thread_->EndStream();
401   }
402 }
403
404 void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error,
405                                           int64_t bytes_compiled) {
406   DCHECK(pp_error == PP_OK);
407   pexe_bytes_compiled_ += bytes_compiled;
408   // Hold off reporting the last few bytes of progress, since we don't know
409   // when they are actually completely compiled.  "bytes_compiled" only means
410   // that bytes were sent to the compiler.
411   if (expected_pexe_size_ != -1) {
412     if (!ShouldDelayProgressEvent()) {
413       GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
414                                         PP_NACL_EVENT_PROGRESS,
415                                         pexe_url_.c_str(),
416                                         PP_TRUE,
417                                         pexe_bytes_compiled_,
418                                         expected_pexe_size_);
419     }
420   } else {
421     GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
422                                       PP_NACL_EVENT_PROGRESS,
423                                       pexe_url_.c_str(),
424                                       PP_FALSE,
425                                       pexe_bytes_compiled_,
426                                       expected_pexe_size_);
427   }
428 }
429
430 pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback(
431     int64_t bytes_compiled) {
432   return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled,
433                                        bytes_compiled);
434 }
435
436 void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded,
437                                           int64_t* bytes_total) {
438   *bytes_loaded = pexe_bytes_compiled_;
439   *bytes_total = expected_pexe_size_;
440 }
441
442 void PnaclCoordinator::RunTranslate(int32_t pp_error) {
443   PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%"
444                  NACL_PRId32 ")\n", pp_error));
445   // Invoke llc followed by ld off the main thread.  This allows use of
446   // blocking RPCs that would otherwise block the JavaScript main thread.
447   pp::CompletionCallback report_translate_finished =
448       callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished);
449
450   CHECK(translate_thread_ != NULL);
451   translate_thread_->RunTranslate(report_translate_finished,
452                                   &obj_files_,
453                                   temp_nexe_file_.get(),
454                                   invalid_desc_wrapper_.get(),
455                                   &error_info_,
456                                   resources_.get(),
457                                   &pnacl_options_,
458                                   architecture_attributes_,
459                                   this,
460                                   plugin_);
461 }
462
463 }  // namespace plugin