Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / src / trusted / plugin / plugin.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/plugin.h"
6
7 #include <sys/stat.h>
8 #include <sys/types.h>
9
10 #include <string>
11
12 #include "native_client/src/include/nacl_base.h"
13 #include "native_client/src/include/nacl_macros.h"
14 #include "native_client/src/include/nacl_scoped_ptr.h"
15 #include "native_client/src/include/portability.h"
16 #include "native_client/src/include/portability_io.h"
17 #include "native_client/src/shared/platform/nacl_check.h"
18 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
19
20 #include "ppapi/c/pp_errors.h"
21 #include "ppapi/c/private/ppb_nacl_private.h"
22 #include "ppapi/cpp/module.h"
23
24 #include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h"
25 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
26 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
27 #include "ppapi/native_client/src/trusted/plugin/utility.h"
28
29 namespace plugin {
30
31 namespace {
32
33 // Up to 20 seconds
34 const int64_t kTimeSmallMin = 1;         // in ms
35 const int64_t kTimeSmallMax = 20000;     // in ms
36 const uint32_t kTimeSmallBuckets = 100;
37
38 }  // namespace
39
40 void Plugin::ShutDownSubprocesses() {
41   PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
42                  static_cast<void*>(this)));
43
44   // Shut down service runtime. This must be done before all other calls so
45   // they don't block forever when waiting for the upcall thread to exit.
46   main_subprocess_.Shutdown();
47
48   PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
49                  static_cast<void*>(this)));
50 }
51
52 void Plugin::HistogramTimeSmall(const std::string& name,
53                                 int64_t ms) {
54   if (ms < 0) return;
55   uma_interface_.HistogramCustomTimes(name,
56                                       ms,
57                                       kTimeSmallMin, kTimeSmallMax,
58                                       kTimeSmallBuckets);
59 }
60
61 bool Plugin::LoadHelperNaClModuleInternal(NaClSubprocess* subprocess,
62                                           const SelLdrStartParams& params) {
63   CHECK(!pp::Module::Get()->core()->IsMainThread());
64   ServiceRuntime* service_runtime =
65       new ServiceRuntime(this,
66                          pp_instance(),
67                          false,  // No main_service_runtime.
68                          false,  // No non-SFI mode (i.e. in SFI-mode).
69                          pp::BlockUntilComplete());
70   subprocess->set_service_runtime(service_runtime);
71
72   // Now start the SelLdr instance.  This must be created on the main thread.
73   bool service_runtime_started = false;
74   pp::CompletionCallback sel_ldr_callback =
75       callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone,
76                                     &service_runtime_started,
77                                     service_runtime);
78   pp::CompletionCallback callback =
79       callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
80                                     service_runtime, params,
81                                     sel_ldr_callback);
82   pp::Module::Get()->core()->CallOnMainThread(0, callback, 0);
83   if (!service_runtime->WaitForSelLdrStart()) {
84     PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
85                    "WaitForSelLdrStart timed out!\n"));
86     return false;
87   }
88   PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (service_runtime_started=%d)\n",
89                  service_runtime_started));
90   if (!service_runtime_started)
91     return false;
92
93   // Now actually start the nexe.
94   //
95   // We can't use pp::BlockUntilComplete() inside an in-process plugin, so we
96   // have to roll our own blocking logic, similar to WaitForSelLdrStart()
97   // above, except without timeout logic.
98   pp::Module::Get()->core()->CallOnMainThread(
99       0,
100       callback_factory_.NewCallback(&Plugin::StartNexe, service_runtime));
101   return service_runtime->WaitForNexeStart();
102 }
103
104 void Plugin::StartSelLdrOnMainThread(int32_t pp_error,
105                                      ServiceRuntime* service_runtime,
106                                      const SelLdrStartParams& params,
107                                      pp::CompletionCallback callback) {
108   CHECK(pp_error == PP_OK);
109   service_runtime->StartSelLdr(params, callback);
110 }
111
112 void Plugin::SignalStartSelLdrDone(int32_t pp_error,
113                                    bool* started,
114                                    ServiceRuntime* service_runtime) {
115   *started = (pp_error == PP_OK);
116   service_runtime->SignalStartSelLdrDone();
117 }
118
119 void Plugin::LoadNaClModule(PP_NaClFileInfo file_info,
120                             bool uses_nonsfi_mode,
121                             PP_NaClAppProcessType process_type,
122                             const pp::CompletionCallback& init_done_cb) {
123   CHECK(pp::Module::Get()->core()->IsMainThread());
124   // Before forking a new sel_ldr process, ensure that we do not leak
125   // the ServiceRuntime object for an existing subprocess, and that any
126   // associated listener threads do not go unjoined because if they
127   // outlive the Plugin object, they will not be memory safe.
128   ShutDownSubprocesses();
129   pp::Var manifest_base_url =
130       pp::Var(pp::PASS_REF, nacl_interface_->GetManifestBaseURL(pp_instance()));
131   std::string manifest_base_url_str = manifest_base_url.AsString();
132
133   SelLdrStartParams params(manifest_base_url_str,
134                            file_info,
135                            process_type);
136   ErrorInfo error_info;
137   ServiceRuntime* service_runtime = new ServiceRuntime(
138       this, pp_instance(), true, uses_nonsfi_mode, init_done_cb);
139   main_subprocess_.set_service_runtime(service_runtime);
140   if (NULL == service_runtime) {
141     error_info.SetReport(
142         PP_NACL_ERROR_SEL_LDR_INIT,
143         "sel_ldr init failure " + main_subprocess_.description());
144     ReportLoadError(error_info);
145     return;
146   }
147
148   // We don't take any action once nexe loading has completed, so pass an empty
149   // callback here for |callback|.
150   pp::CompletionCallback callback = callback_factory_.NewCallback(
151       &Plugin::StartNexe, service_runtime);
152   StartSelLdrOnMainThread(
153       static_cast<int32_t>(PP_OK), service_runtime, params, callback);
154 }
155
156 void Plugin::StartNexe(int32_t pp_error, ServiceRuntime* service_runtime) {
157   CHECK(pp::Module::Get()->core()->IsMainThread());
158   if (pp_error != PP_OK)
159     return;
160   service_runtime->StartNexe();
161 }
162
163 bool Plugin::LoadNaClModuleContinuationIntern() {
164   ErrorInfo error_info;
165   if (!uses_nonsfi_mode_) {
166     if (!main_subprocess_.StartSrpcServices()) {
167       // The NaCl process probably crashed. On Linux, a crash causes this
168       // error, while on other platforms, the error is detected below, when we
169       // attempt to start the proxy. Report a module initialization error here,
170       // to make it less confusing for developers.
171       NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
172               "StartSrpcServices failed\n");
173       error_info.SetReport(PP_NACL_ERROR_START_PROXY_MODULE,
174                            "could not initialize module.");
175       ReportLoadError(error_info);
176       return false;
177     }
178   }
179
180   return PP_ToBool(nacl_interface_->StartPpapiProxy(pp_instance()));
181 }
182
183 NaClSubprocess* Plugin::LoadHelperNaClModule(const std::string& helper_url,
184                                              PP_NaClFileInfo file_info,
185                                              ErrorInfo* error_info) {
186   nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
187       new NaClSubprocess("helper module", NULL, NULL));
188   if (NULL == nacl_subprocess.get()) {
189     error_info->SetReport(PP_NACL_ERROR_SEL_LDR_INIT,
190                           "unable to allocate helper subprocess.");
191     return NULL;
192   }
193
194   // Do not report UMA stats for translator-related nexes.
195   // TODO(sehr): define new UMA stats for translator related nexe events.
196   // NOTE: The PNaCl translator nexes are not built to use the IRT.  This is
197   // done to save on address space and swap space.
198   SelLdrStartParams params(helper_url,
199                            file_info,
200                            PP_PNACL_TRANSLATOR_PROCESS_TYPE);
201
202   // Helper NaCl modules always use the PNaCl manifest, as there is no
203   // corresponding NMF.
204   if (!LoadHelperNaClModuleInternal(nacl_subprocess.get(), params))
205     return NULL;
206
207   // We need not wait for the init_done callback.  We can block
208   // here in StartSrpcServices, since helper NaCl modules
209   // are spawned from a private thread.
210   //
211   // TODO(bsy): if helper module crashes, we should abort.
212   // crash_cb is not used here, so we are relying on crashes
213   // being detected in StartSrpcServices or later.
214   //
215   // NB: More refactoring might be needed, however, if helper
216   // NaCl modules have their own manifest.  Currently the
217   // manifest is a per-plugin-instance object, not a per
218   // NaClSubprocess object.
219   if (!nacl_subprocess->StartSrpcServices()) {
220     error_info->SetReport(PP_NACL_ERROR_SRPC_CONNECTION_FAIL,
221                           "SRPC connection failure for " +
222                           nacl_subprocess->description());
223     return NULL;
224   }
225
226   PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s, %s)\n",
227                  helper_url.c_str(),
228                  nacl_subprocess.get()->detailed_description().c_str()));
229
230   return nacl_subprocess.release();
231 }
232
233 // All failures of this function will show up as "Missing Plugin-in", so
234 // there is no need to log to JS console that there was an initialization
235 // failure. Note that module loading functions will log their own errors.
236 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
237   nacl_interface_->InitializePlugin(pp_instance(), argc, argn, argv);
238   wrapper_factory_ = new nacl::DescWrapperFactory();
239   pp::CompletionCallback open_cb =
240       callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
241   nacl_interface_->RequestNaClManifest(pp_instance(),
242                                        open_cb.pp_completion_callback());
243   return true;
244 }
245
246 Plugin::Plugin(PP_Instance pp_instance)
247     : pp::Instance(pp_instance),
248       main_subprocess_("main subprocess", NULL, NULL),
249       uses_nonsfi_mode_(false),
250       wrapper_factory_(NULL),
251       nacl_interface_(NULL),
252       uma_interface_(this) {
253   callback_factory_.Initialize(this);
254   nacl_interface_ = GetNaClInterface();
255   CHECK(nacl_interface_ != NULL);
256
257   // Notify PPB_NaCl_Private that the instance is created before altering any
258   // state that it tracks.
259   nacl_interface_->InstanceCreated(pp_instance);
260   nexe_file_info_ = kInvalidNaClFileInfo;
261 }
262
263 Plugin::~Plugin() {
264   int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
265
266   // Destroy the coordinator while the rest of the data is still there
267   pnacl_coordinator_.reset(NULL);
268
269   nacl_interface_->InstanceDestroyed(pp_instance());
270
271   // ShutDownSubprocesses shuts down the main subprocess, which shuts
272   // down the main ServiceRuntime object, which kills the subprocess.
273   // As a side effect of the subprocess being killed, the reverse
274   // services thread(s) will get EOF on the reverse channel(s), and
275   // the thread(s) will exit.  In ServiceRuntime::Shutdown, we invoke
276   // ReverseService::WaitForServiceThreadsToExit(), so that there will
277   // not be an extent thread(s) hanging around.  This means that the
278   // ~Plugin will block until this happens.  This is a requirement,
279   // since the renderer should be free to unload the plugin code, and
280   // we cannot have threads running code that gets unloaded before
281   // they exit.
282   //
283   // By waiting for the threads here, we also ensure that the Plugin
284   // object and the subprocess and ServiceRuntime objects is not
285   // (fully) destroyed while the threads are running, so resources
286   // that are destroyed after ShutDownSubprocesses (below) are
287   // guaranteed to be live and valid for access from the service
288   // threads.
289   //
290   // The main_subprocess object, which wraps the main service_runtime
291   // object, is dtor'd implicitly after the explicit code below runs,
292   // so the main service runtime object will not have been dtor'd,
293   // though the Shutdown method may have been called, during the
294   // lifetime of the service threads.
295   ShutDownSubprocesses();
296
297   delete wrapper_factory_;
298
299   HistogramTimeSmall(
300       "NaCl.Perf.ShutdownTime.Total",
301       (NaClGetTimeOfDayMicroseconds() - shutdown_start)
302           / NACL_MICROS_PER_MILLI);
303 }
304
305 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
306   // We don't know if the plugin will handle the document load, but return
307   // true in order to give it a chance to respond once the proxy is started.
308   return true;
309 }
310
311 void Plugin::NexeFileDidOpen(int32_t pp_error) {
312   if (pp_error != PP_OK)
313     return;
314   LoadNaClModule(
315       nexe_file_info_,
316       uses_nonsfi_mode_,
317       PP_NATIVE_NACL_PROCESS_TYPE,
318       callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation));
319 }
320
321 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
322   UNREFERENCED_PARAMETER(pp_error);
323   NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
324   if (LoadNaClModuleContinuationIntern()) {
325     NaClLog(4, "NexeFileDidOpenContinuation: success;"
326             " setting histograms\n");
327     int64_t nexe_size = nacl_interface_->GetNexeSize(pp_instance());
328     nacl_interface_->ReportLoadSuccess(
329         pp_instance(), nexe_size, nexe_size);
330   } else {
331     NaClLog(4, "NexeFileDidOpenContinuation: failed.");
332   }
333   NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
334 }
335
336 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
337   PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
338                  pp_error));
339   if (pp_error != PP_OK) {
340     // Error should have been reported by pnacl. Just return.
341     return;
342   }
343
344   // Inform JavaScript that we successfully translated the bitcode to a nexe.
345   PP_FileHandle handle = pnacl_coordinator_->TakeTranslatedFileHandle();
346
347   PP_NaClFileInfo info;
348   info.handle = handle;
349   info.token_lo = 0;
350   info.token_hi = 0;
351   LoadNaClModule(
352       info,
353       false, /* uses_nonsfi_mode */
354       PP_PNACL_PROCESS_TYPE,
355       callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation));
356 }
357
358 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
359   NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
360   UNREFERENCED_PARAMETER(pp_error);
361   if (LoadNaClModuleContinuationIntern()) {
362     int64_t loaded;
363     int64_t total;
364     // TODO(teravest): Tighten this up so we can get rid of
365     // GetCurrentProgress(). loaded should always equal total.
366     pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
367     nacl_interface_->ReportLoadSuccess(pp_instance(), loaded, total);
368   }
369 }
370
371 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
372   PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
373                  NACL_PRId32 ")\n", pp_error));
374   if (pp_error != PP_OK)
375     return;
376
377   PP_Var pp_program_url;
378   PP_PNaClOptions pnacl_options = {PP_FALSE, PP_FALSE, 2};
379   PP_Bool uses_nonsfi_mode;
380   if (nacl_interface_->GetManifestProgramURL(
381           pp_instance(), &pp_program_url, &pnacl_options, &uses_nonsfi_mode)) {
382     std::string program_url = pp::Var(pp::PASS_REF, pp_program_url).AsString();
383     // TODO(teravest): Make ProcessNaClManifest take responsibility for more of
384     // this function.
385     nacl_interface_->ProcessNaClManifest(pp_instance(), program_url.c_str());
386     uses_nonsfi_mode_ = PP_ToBool(uses_nonsfi_mode);
387     if (pnacl_options.translate) {
388       pp::CompletionCallback translate_callback =
389           callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
390       pnacl_coordinator_.reset(
391           PnaclCoordinator::BitcodeToNative(this,
392                                             program_url,
393                                             pnacl_options,
394                                             translate_callback));
395       return;
396     } else {
397       pp::CompletionCallback open_callback =
398           callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
399       // Will always call the callback on success or failure.
400       nacl_interface_->DownloadNexe(pp_instance(),
401                                     program_url.c_str(),
402                                     &nexe_file_info_,
403                                     open_callback.pp_completion_callback());
404       return;
405     }
406   }
407 }
408
409 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
410   nacl_interface_->ReportLoadError(pp_instance(),
411                                    error_info.error_code(),
412                                    error_info.message().c_str());
413 }
414
415 }  // namespace plugin