| 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 |