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