| OLD | NEW | 
|---|
|  | (Empty) | 
| 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 |  | 
| 70   // Now start the SelLdr instance.  This must be created on the main thread. |  | 
| 71   bool service_runtime_started = false; |  | 
| 72   pp::CompletionCallback sel_ldr_callback = |  | 
| 73       callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone, |  | 
| 74                                     &service_runtime_started, |  | 
| 75                                     service_runtime); |  | 
| 76   pp::CompletionCallback callback = |  | 
| 77       callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread, |  | 
| 78                                     service_runtime, params, |  | 
| 79                                     sel_ldr_callback); |  | 
| 80   pp::Module::Get()->core()->CallOnMainThread(0, callback, 0); |  | 
| 81   if (!service_runtime->WaitForSelLdrStart()) { |  | 
| 82     PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule " |  | 
| 83                    "WaitForSelLdrStart timed out!\n")); |  | 
| 84     service_runtime->Shutdown(); |  | 
| 85     // Don't delete service_runtime here; it could still be used by the pending |  | 
| 86     // SignalStartSelLdrDone callback. |  | 
| 87     return false; |  | 
| 88   } |  | 
| 89   PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (service_runtime_started=%d)\n", |  | 
| 90                  service_runtime_started)); |  | 
| 91   if (!service_runtime_started) { |  | 
| 92     service_runtime->Shutdown(); |  | 
| 93     delete service_runtime; |  | 
| 94     return false; |  | 
| 95   } |  | 
| 96 |  | 
| 97   // Now actually start the nexe. |  | 
| 98   // |  | 
| 99   // We can't use pp::BlockUntilComplete() inside an in-process plugin, so we |  | 
| 100   // have to roll our own blocking logic, similar to WaitForSelLdrStart() |  | 
| 101   // above, except without timeout logic. |  | 
| 102   pp::Module::Get()->core()->CallOnMainThread( |  | 
| 103       0, |  | 
| 104       callback_factory_.NewCallback(&Plugin::StartNexe, service_runtime)); |  | 
| 105   if (!service_runtime->WaitForNexeStart()) { |  | 
| 106     service_runtime->Shutdown(); |  | 
| 107     delete service_runtime; |  | 
| 108     return false; |  | 
| 109   } |  | 
| 110   subprocess->set_service_runtime(service_runtime); |  | 
| 111   return true; |  | 
| 112 } |  | 
| 113 |  | 
| 114 void Plugin::StartSelLdrOnMainThread(int32_t pp_error, |  | 
| 115                                      ServiceRuntime* service_runtime, |  | 
| 116                                      const SelLdrStartParams& params, |  | 
| 117                                      pp::CompletionCallback callback) { |  | 
| 118   CHECK(pp_error == PP_OK); |  | 
| 119   service_runtime->StartSelLdr(params, callback); |  | 
| 120 } |  | 
| 121 |  | 
| 122 void Plugin::SignalStartSelLdrDone(int32_t pp_error, |  | 
| 123                                    bool* started, |  | 
| 124                                    ServiceRuntime* service_runtime) { |  | 
| 125   if (service_runtime->SelLdrWaitTimedOut()) { |  | 
| 126     delete service_runtime; |  | 
| 127   } else { |  | 
| 128     *started = (pp_error == PP_OK); |  | 
| 129     service_runtime->SignalStartSelLdrDone(); |  | 
| 130   } |  | 
| 131 } |  | 
| 132 |  | 
| 133 void Plugin::LoadNaClModule(PP_NaClFileInfo file_info, |  | 
| 134                             bool uses_nonsfi_mode, |  | 
| 135                             PP_NaClAppProcessType process_type) { |  | 
| 136   CHECK(pp::Module::Get()->core()->IsMainThread()); |  | 
| 137   // Before forking a new sel_ldr process, ensure that we do not leak |  | 
| 138   // the ServiceRuntime object for an existing subprocess, and that any |  | 
| 139   // associated listener threads do not go unjoined because if they |  | 
| 140   // outlive the Plugin object, they will not be memory safe. |  | 
| 141   ShutDownSubprocesses(); |  | 
| 142   pp::Var manifest_base_url = |  | 
| 143       pp::Var(pp::PASS_REF, nacl_interface_->GetManifestBaseURL(pp_instance())); |  | 
| 144   std::string manifest_base_url_str = manifest_base_url.AsString(); |  | 
| 145 |  | 
| 146   SelLdrStartParams params(manifest_base_url_str, |  | 
| 147                            file_info, |  | 
| 148                            process_type); |  | 
| 149   ErrorInfo error_info; |  | 
| 150   ServiceRuntime* service_runtime = new ServiceRuntime( |  | 
| 151       this, pp_instance(), true, uses_nonsfi_mode); |  | 
| 152   main_subprocess_.set_service_runtime(service_runtime); |  | 
| 153   if (NULL == service_runtime) { |  | 
| 154     error_info.SetReport( |  | 
| 155         PP_NACL_ERROR_SEL_LDR_INIT, |  | 
| 156         "sel_ldr init failure " + main_subprocess_.description()); |  | 
| 157     ReportLoadError(error_info); |  | 
| 158     return; |  | 
| 159   } |  | 
| 160 |  | 
| 161   // We don't take any action once nexe loading has completed, so pass an empty |  | 
| 162   // callback here for |callback|. |  | 
| 163   pp::CompletionCallback callback = callback_factory_.NewCallback( |  | 
| 164       &Plugin::StartNexe, service_runtime); |  | 
| 165   StartSelLdrOnMainThread( |  | 
| 166       static_cast<int32_t>(PP_OK), service_runtime, params, callback); |  | 
| 167 } |  | 
| 168 |  | 
| 169 void Plugin::StartNexe(int32_t pp_error, ServiceRuntime* service_runtime) { |  | 
| 170   CHECK(pp::Module::Get()->core()->IsMainThread()); |  | 
| 171   if (pp_error != PP_OK) |  | 
| 172     return; |  | 
| 173   service_runtime->StartNexe(); |  | 
| 174 } |  | 
| 175 |  | 
| 176 NaClSubprocess* Plugin::LoadHelperNaClModule(const std::string& helper_url, |  | 
| 177                                              PP_NaClFileInfo file_info, |  | 
| 178                                              ErrorInfo* error_info) { |  | 
| 179   nacl::scoped_ptr<NaClSubprocess> nacl_subprocess( |  | 
| 180       new NaClSubprocess("helper module", NULL, NULL)); |  | 
| 181   if (NULL == nacl_subprocess.get()) { |  | 
| 182     error_info->SetReport(PP_NACL_ERROR_SEL_LDR_INIT, |  | 
| 183                           "unable to allocate helper subprocess."); |  | 
| 184     return NULL; |  | 
| 185   } |  | 
| 186 |  | 
| 187   // Do not report UMA stats for translator-related nexes. |  | 
| 188   // TODO(sehr): define new UMA stats for translator related nexe events. |  | 
| 189   // NOTE: The PNaCl translator nexes are not built to use the IRT.  This is |  | 
| 190   // done to save on address space and swap space. |  | 
| 191   SelLdrStartParams params(helper_url, |  | 
| 192                            file_info, |  | 
| 193                            PP_PNACL_TRANSLATOR_PROCESS_TYPE); |  | 
| 194 |  | 
| 195   // Helper NaCl modules always use the PNaCl manifest, as there is no |  | 
| 196   // corresponding NMF. |  | 
| 197   if (!LoadHelperNaClModuleInternal(nacl_subprocess.get(), params)) |  | 
| 198     return NULL; |  | 
| 199 |  | 
| 200   // We can block here in StartSrpcServices, since helper NaCl |  | 
| 201   // modules are spawned from a private thread. |  | 
| 202   // |  | 
| 203   // TODO(bsy): if helper module crashes, we should abort. |  | 
| 204   // crash_cb is not used here, so we are relying on crashes |  | 
| 205   // being detected in StartSrpcServices or later. |  | 
| 206   // |  | 
| 207   // NB: More refactoring might be needed, however, if helper |  | 
| 208   // NaCl modules have their own manifest.  Currently the |  | 
| 209   // manifest is a per-plugin-instance object, not a per |  | 
| 210   // NaClSubprocess object. |  | 
| 211   if (!nacl_subprocess->StartSrpcServices()) { |  | 
| 212     error_info->SetReport(PP_NACL_ERROR_SRPC_CONNECTION_FAIL, |  | 
| 213                           "SRPC connection failure for " + |  | 
| 214                           nacl_subprocess->description()); |  | 
| 215     return NULL; |  | 
| 216   } |  | 
| 217 |  | 
| 218   PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s, %s)\n", |  | 
| 219                  helper_url.c_str(), |  | 
| 220                  nacl_subprocess.get()->detailed_description().c_str())); |  | 
| 221 |  | 
| 222   return nacl_subprocess.release(); |  | 
| 223 } |  | 
| 224 |  | 
| 225 // All failures of this function will show up as "Missing Plugin-in", so |  | 
| 226 // there is no need to log to JS console that there was an initialization |  | 
| 227 // failure. Note that module loading functions will log their own errors. |  | 
| 228 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) { |  | 
| 229   nacl_interface_->InitializePlugin(pp_instance(), argc, argn, argv); |  | 
| 230   wrapper_factory_ = new nacl::DescWrapperFactory(); |  | 
| 231   pp::CompletionCallback open_cb = |  | 
| 232       callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen); |  | 
| 233   nacl_interface_->RequestNaClManifest(pp_instance(), |  | 
| 234                                        open_cb.pp_completion_callback()); |  | 
| 235   return true; |  | 
| 236 } |  | 
| 237 |  | 
| 238 Plugin::Plugin(PP_Instance pp_instance) |  | 
| 239     : pp::Instance(pp_instance), |  | 
| 240       main_subprocess_("main subprocess", NULL, NULL), |  | 
| 241       uses_nonsfi_mode_(false), |  | 
| 242       wrapper_factory_(NULL), |  | 
| 243       nacl_interface_(NULL), |  | 
| 244       uma_interface_(this) { |  | 
| 245   callback_factory_.Initialize(this); |  | 
| 246   nacl_interface_ = GetNaClInterface(); |  | 
| 247   CHECK(nacl_interface_ != NULL); |  | 
| 248 |  | 
| 249   // Notify PPB_NaCl_Private that the instance is created before altering any |  | 
| 250   // state that it tracks. |  | 
| 251   nacl_interface_->InstanceCreated(pp_instance); |  | 
| 252   nexe_file_info_ = kInvalidNaClFileInfo; |  | 
| 253 } |  | 
| 254 |  | 
| 255 Plugin::~Plugin() { |  | 
| 256   int64_t shutdown_start = NaClGetTimeOfDayMicroseconds(); |  | 
| 257 |  | 
| 258   // Destroy the coordinator while the rest of the data is still there |  | 
| 259   pnacl_coordinator_.reset(NULL); |  | 
| 260 |  | 
| 261   nacl_interface_->InstanceDestroyed(pp_instance()); |  | 
| 262 |  | 
| 263   // ShutDownSubprocesses shuts down the main subprocess, which shuts |  | 
| 264   // down the main ServiceRuntime object, which kills the subprocess. |  | 
| 265   // As a side effect of the subprocess being killed, the reverse |  | 
| 266   // services thread(s) will get EOF on the reverse channel(s), and |  | 
| 267   // the thread(s) will exit.  In ServiceRuntime::Shutdown, we invoke |  | 
| 268   // ReverseService::WaitForServiceThreadsToExit(), so that there will |  | 
| 269   // not be an extent thread(s) hanging around.  This means that the |  | 
| 270   // ~Plugin will block until this happens.  This is a requirement, |  | 
| 271   // since the renderer should be free to unload the plugin code, and |  | 
| 272   // we cannot have threads running code that gets unloaded before |  | 
| 273   // they exit. |  | 
| 274   // |  | 
| 275   // By waiting for the threads here, we also ensure that the Plugin |  | 
| 276   // object and the subprocess and ServiceRuntime objects is not |  | 
| 277   // (fully) destroyed while the threads are running, so resources |  | 
| 278   // that are destroyed after ShutDownSubprocesses (below) are |  | 
| 279   // guaranteed to be live and valid for access from the service |  | 
| 280   // threads. |  | 
| 281   // |  | 
| 282   // The main_subprocess object, which wraps the main service_runtime |  | 
| 283   // object, is dtor'd implicitly after the explicit code below runs, |  | 
| 284   // so the main service runtime object will not have been dtor'd, |  | 
| 285   // though the Shutdown method may have been called, during the |  | 
| 286   // lifetime of the service threads. |  | 
| 287   ShutDownSubprocesses(); |  | 
| 288 |  | 
| 289   delete wrapper_factory_; |  | 
| 290 |  | 
| 291   HistogramTimeSmall( |  | 
| 292       "NaCl.Perf.ShutdownTime.Total", |  | 
| 293       (NaClGetTimeOfDayMicroseconds() - shutdown_start) |  | 
| 294           / NACL_MICROS_PER_MILLI); |  | 
| 295 } |  | 
| 296 |  | 
| 297 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) { |  | 
| 298   // We don't know if the plugin will handle the document load, but return |  | 
| 299   // true in order to give it a chance to respond once the proxy is started. |  | 
| 300   return true; |  | 
| 301 } |  | 
| 302 |  | 
| 303 void Plugin::NexeFileDidOpen(int32_t pp_error) { |  | 
| 304   if (pp_error != PP_OK) |  | 
| 305     return; |  | 
| 306   LoadNaClModule( |  | 
| 307       nexe_file_info_, |  | 
| 308       uses_nonsfi_mode_, |  | 
| 309       PP_NATIVE_NACL_PROCESS_TYPE); |  | 
| 310 } |  | 
| 311 |  | 
| 312 void Plugin::BitcodeDidTranslate(int32_t pp_error) { |  | 
| 313   PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n", |  | 
| 314                  pp_error)); |  | 
| 315   if (pp_error != PP_OK) { |  | 
| 316     // Error should have been reported by pnacl. Just return. |  | 
| 317     return; |  | 
| 318   } |  | 
| 319 |  | 
| 320   // Inform JavaScript that we successfully translated the bitcode to a nexe. |  | 
| 321   PP_FileHandle handle = pnacl_coordinator_->TakeTranslatedFileHandle(); |  | 
| 322 |  | 
| 323   PP_NaClFileInfo info; |  | 
| 324   info.handle = handle; |  | 
| 325   info.token_lo = 0; |  | 
| 326   info.token_hi = 0; |  | 
| 327   LoadNaClModule( |  | 
| 328       info, |  | 
| 329       false, /* uses_nonsfi_mode */ |  | 
| 330       PP_PNACL_PROCESS_TYPE); |  | 
| 331 } |  | 
| 332 |  | 
| 333 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) { |  | 
| 334   PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%" |  | 
| 335                  NACL_PRId32 ")\n", pp_error)); |  | 
| 336   if (pp_error != PP_OK) |  | 
| 337     return; |  | 
| 338 |  | 
| 339   PP_Var pp_program_url; |  | 
| 340   PP_PNaClOptions pnacl_options = {PP_FALSE, PP_FALSE, 2}; |  | 
| 341   PP_Bool uses_nonsfi_mode; |  | 
| 342   if (nacl_interface_->GetManifestProgramURL( |  | 
| 343           pp_instance(), &pp_program_url, &pnacl_options, &uses_nonsfi_mode)) { |  | 
| 344     std::string program_url = pp::Var(pp::PASS_REF, pp_program_url).AsString(); |  | 
| 345     // TODO(teravest): Make ProcessNaClManifest take responsibility for more of |  | 
| 346     // this function. |  | 
| 347     nacl_interface_->ProcessNaClManifest(pp_instance(), program_url.c_str()); |  | 
| 348     uses_nonsfi_mode_ = PP_ToBool(uses_nonsfi_mode); |  | 
| 349     if (pnacl_options.translate) { |  | 
| 350       pp::CompletionCallback translate_callback = |  | 
| 351           callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate); |  | 
| 352       pnacl_coordinator_.reset( |  | 
| 353           PnaclCoordinator::BitcodeToNative(this, |  | 
| 354                                             program_url, |  | 
| 355                                             pnacl_options, |  | 
| 356                                             translate_callback)); |  | 
| 357       return; |  | 
| 358     } else { |  | 
| 359       pp::CompletionCallback open_callback = |  | 
| 360           callback_factory_.NewCallback(&Plugin::NexeFileDidOpen); |  | 
| 361       // Will always call the callback on success or failure. |  | 
| 362       nacl_interface_->DownloadNexe(pp_instance(), |  | 
| 363                                     program_url.c_str(), |  | 
| 364                                     &nexe_file_info_, |  | 
| 365                                     open_callback.pp_completion_callback()); |  | 
| 366       return; |  | 
| 367     } |  | 
| 368   } |  | 
| 369 } |  | 
| 370 |  | 
| 371 void Plugin::ReportLoadError(const ErrorInfo& error_info) { |  | 
| 372   nacl_interface_->ReportLoadError(pp_instance(), |  | 
| 373                                    error_info.error_code(), |  | 
| 374                                    error_info.message().c_str()); |  | 
| 375 } |  | 
| 376 |  | 
| 377 }  // namespace plugin |  | 
| OLD | NEW | 
|---|