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.
5 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
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"
20 #include "ppapi/c/pp_errors.h"
21 #include "ppapi/c/private/ppb_nacl_private.h"
22 #include "ppapi/cpp/module.h"
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"
34 const int64_t kTimeSmallMin = 1; // in ms
35 const int64_t kTimeSmallMax = 20000; // in ms
36 const uint32_t kTimeSmallBuckets = 100;
40 void Plugin::ShutDownSubprocesses() {
41 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
42 static_cast<void*>(this)));
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();
48 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
49 static_cast<void*>(this)));
52 void Plugin::HistogramTimeSmall(const std::string& name,
55 uma_interface_.HistogramCustomTimes(name,
57 kTimeSmallMin, kTimeSmallMax,
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,
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);
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,
78 pp::CompletionCallback callback =
79 callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
80 service_runtime, params,
82 pp::Module::Get()->core()->CallOnMainThread(0, callback, 0);
83 if (!service_runtime->WaitForSelLdrStart()) {
84 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
85 "WaitForSelLdrStart timed out!\n"));
88 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (service_runtime_started=%d)\n",
89 service_runtime_started));
90 if (!service_runtime_started)
93 // Now actually start the nexe.
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(
100 callback_factory_.NewCallback(&Plugin::StartNexe, service_runtime));
101 return service_runtime->WaitForNexeStart();
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);
112 void Plugin::SignalStartSelLdrDone(int32_t pp_error,
114 ServiceRuntime* service_runtime) {
115 *started = (pp_error == PP_OK);
116 service_runtime->SignalStartSelLdrDone();
119 void Plugin::LoadNaClModule(PP_NaClFileInfo file_info,
120 bool uses_nonsfi_mode,
121 bool enable_dyncode_syscalls,
122 bool enable_exception_handling,
123 bool enable_crash_throttling,
124 const pp::CompletionCallback& init_done_cb) {
125 CHECK(pp::Module::Get()->core()->IsMainThread());
126 // Before forking a new sel_ldr process, ensure that we do not leak
127 // the ServiceRuntime object for an existing subprocess, and that any
128 // associated listener threads do not go unjoined because if they
129 // outlive the Plugin object, they will not be memory safe.
130 ShutDownSubprocesses();
131 pp::Var manifest_base_url =
132 pp::Var(pp::PASS_REF, nacl_interface_->GetManifestBaseURL(pp_instance()));
133 std::string manifest_base_url_str = manifest_base_url.AsString();
135 SelLdrStartParams params(manifest_base_url_str,
138 true /* uses_ppapi */,
139 enable_dyncode_syscalls,
140 enable_exception_handling,
141 enable_crash_throttling);
142 ErrorInfo error_info;
143 ServiceRuntime* service_runtime = new ServiceRuntime(
144 this, pp_instance(), true, uses_nonsfi_mode, init_done_cb);
145 main_subprocess_.set_service_runtime(service_runtime);
146 if (NULL == service_runtime) {
147 error_info.SetReport(
148 PP_NACL_ERROR_SEL_LDR_INIT,
149 "sel_ldr init failure " + main_subprocess_.description());
150 ReportLoadError(error_info);
154 // We don't take any action once nexe loading has completed, so pass an empty
155 // callback here for |callback|.
156 pp::CompletionCallback callback = callback_factory_.NewCallback(
157 &Plugin::StartNexe, service_runtime);
158 StartSelLdrOnMainThread(
159 static_cast<int32_t>(PP_OK), service_runtime, params, callback);
162 void Plugin::StartNexe(int32_t pp_error, ServiceRuntime* service_runtime) {
163 CHECK(pp::Module::Get()->core()->IsMainThread());
164 if (pp_error != PP_OK)
166 service_runtime->StartNexe();
169 bool Plugin::LoadNaClModuleContinuationIntern() {
170 ErrorInfo error_info;
171 if (!uses_nonsfi_mode_) {
172 if (!main_subprocess_.StartSrpcServices()) {
173 // The NaCl process probably crashed. On Linux, a crash causes this
174 // error, while on other platforms, the error is detected below, when we
175 // attempt to start the proxy. Report a module initialization error here,
176 // to make it less confusing for developers.
177 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
178 "StartSrpcServices failed\n");
179 error_info.SetReport(PP_NACL_ERROR_START_PROXY_MODULE,
180 "could not initialize module.");
181 ReportLoadError(error_info);
186 return PP_ToBool(nacl_interface_->StartPpapiProxy(pp_instance()));
189 NaClSubprocess* Plugin::LoadHelperNaClModule(const std::string& helper_url,
190 PP_NaClFileInfo file_info,
191 ErrorInfo* error_info) {
192 nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
193 new NaClSubprocess("helper module", NULL, NULL));
194 if (NULL == nacl_subprocess.get()) {
195 error_info->SetReport(PP_NACL_ERROR_SEL_LDR_INIT,
196 "unable to allocate helper subprocess.");
200 // Do not report UMA stats for translator-related nexes.
201 // TODO(sehr): define new UMA stats for translator related nexe events.
202 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is
203 // done to save on address space and swap space.
204 SelLdrStartParams params(helper_url,
206 false /* uses_irt */,
207 false /* uses_ppapi */,
208 false /* enable_dyncode_syscalls */,
209 false /* enable_exception_handling */,
210 true /* enable_crash_throttling */);
212 // Helper NaCl modules always use the PNaCl manifest, as there is no
213 // corresponding NMF.
214 if (!LoadHelperNaClModuleInternal(nacl_subprocess.get(), params))
217 // We need not wait for the init_done callback. We can block
218 // here in StartSrpcServices, since helper NaCl modules
219 // are spawned from a private thread.
221 // TODO(bsy): if helper module crashes, we should abort.
222 // crash_cb is not used here, so we are relying on crashes
223 // being detected in StartSrpcServices or later.
225 // NB: More refactoring might be needed, however, if helper
226 // NaCl modules have their own manifest. Currently the
227 // manifest is a per-plugin-instance object, not a per
228 // NaClSubprocess object.
229 if (!nacl_subprocess->StartSrpcServices()) {
230 error_info->SetReport(PP_NACL_ERROR_SRPC_CONNECTION_FAIL,
231 "SRPC connection failure for " +
232 nacl_subprocess->description());
236 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s, %s)\n",
238 nacl_subprocess.get()->detailed_description().c_str()));
240 return nacl_subprocess.release();
243 // All failures of this function will show up as "Missing Plugin-in", so
244 // there is no need to log to JS console that there was an initialization
245 // failure. Note that module loading functions will log their own errors.
246 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
247 nacl_interface_->InitializePlugin(pp_instance(), argc, argn, argv);
248 wrapper_factory_ = new nacl::DescWrapperFactory();
249 pp::CompletionCallback open_cb =
250 callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
251 nacl_interface_->RequestNaClManifest(pp_instance(),
252 open_cb.pp_completion_callback());
256 Plugin::Plugin(PP_Instance pp_instance)
257 : pp::Instance(pp_instance),
258 main_subprocess_("main subprocess", NULL, NULL),
259 uses_nonsfi_mode_(false),
260 wrapper_factory_(NULL),
261 nacl_interface_(NULL),
262 uma_interface_(this) {
263 callback_factory_.Initialize(this);
264 nacl_interface_ = GetNaClInterface();
265 CHECK(nacl_interface_ != NULL);
267 // Notify PPB_NaCl_Private that the instance is created before altering any
268 // state that it tracks.
269 nacl_interface_->InstanceCreated(pp_instance);
270 nexe_file_info_ = kInvalidNaClFileInfo;
274 int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
276 // Destroy the coordinator while the rest of the data is still there
277 pnacl_coordinator_.reset(NULL);
279 nacl_interface_->InstanceDestroyed(pp_instance());
281 // ShutDownSubprocesses shuts down the main subprocess, which shuts
282 // down the main ServiceRuntime object, which kills the subprocess.
283 // As a side effect of the subprocess being killed, the reverse
284 // services thread(s) will get EOF on the reverse channel(s), and
285 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke
286 // ReverseService::WaitForServiceThreadsToExit(), so that there will
287 // not be an extent thread(s) hanging around. This means that the
288 // ~Plugin will block until this happens. This is a requirement,
289 // since the renderer should be free to unload the plugin code, and
290 // we cannot have threads running code that gets unloaded before
293 // By waiting for the threads here, we also ensure that the Plugin
294 // object and the subprocess and ServiceRuntime objects is not
295 // (fully) destroyed while the threads are running, so resources
296 // that are destroyed after ShutDownSubprocesses (below) are
297 // guaranteed to be live and valid for access from the service
300 // The main_subprocess object, which wraps the main service_runtime
301 // object, is dtor'd implicitly after the explicit code below runs,
302 // so the main service runtime object will not have been dtor'd,
303 // though the Shutdown method may have been called, during the
304 // lifetime of the service threads.
305 ShutDownSubprocesses();
307 delete wrapper_factory_;
310 "NaCl.Perf.ShutdownTime.Total",
311 (NaClGetTimeOfDayMicroseconds() - shutdown_start)
312 / NACL_MICROS_PER_MILLI);
315 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
316 // We don't know if the plugin will handle the document load, but return
317 // true in order to give it a chance to respond once the proxy is started.
321 void Plugin::NexeFileDidOpen(int32_t pp_error) {
322 if (pp_error != PP_OK)
327 true, /* enable_dyncode_syscalls */
328 true, /* enable_exception_handling */
329 false, /* enable_crash_throttling */
330 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation));
333 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
334 UNREFERENCED_PARAMETER(pp_error);
335 NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
336 if (LoadNaClModuleContinuationIntern()) {
337 NaClLog(4, "NexeFileDidOpenContinuation: success;"
338 " setting histograms\n");
339 int64_t nexe_size = nacl_interface_->GetNexeSize(pp_instance());
340 nacl_interface_->ReportLoadSuccess(
341 pp_instance(), nexe_size, nexe_size);
343 NaClLog(4, "NexeFileDidOpenContinuation: failed.");
345 NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
348 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
349 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
351 if (pp_error != PP_OK) {
352 // Error should have been reported by pnacl. Just return.
356 // Inform JavaScript that we successfully translated the bitcode to a nexe.
357 PP_FileHandle handle = pnacl_coordinator_->TakeTranslatedFileHandle();
359 PP_NaClFileInfo info;
360 info.handle = handle;
365 false, /* uses_nonsfi_mode */
366 false, /* enable_dyncode_syscalls */
367 false, /* enable_exception_handling */
368 true, /* enable_crash_throttling */
369 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation));
372 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
373 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
374 UNREFERENCED_PARAMETER(pp_error);
375 if (LoadNaClModuleContinuationIntern()) {
378 // TODO(teravest): Tighten this up so we can get rid of
379 // GetCurrentProgress(). loaded should always equal total.
380 pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
381 nacl_interface_->ReportLoadSuccess(pp_instance(), loaded, total);
385 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
386 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
387 NACL_PRId32 ")\n", pp_error));
388 if (pp_error != PP_OK)
391 PP_Var pp_program_url;
392 PP_PNaClOptions pnacl_options = {PP_FALSE, PP_FALSE, 2};
393 PP_Bool uses_nonsfi_mode;
394 if (nacl_interface_->GetManifestProgramURL(
395 pp_instance(), &pp_program_url, &pnacl_options, &uses_nonsfi_mode)) {
396 std::string program_url = pp::Var(pp::PASS_REF, pp_program_url).AsString();
397 // TODO(teravest): Make ProcessNaClManifest take responsibility for more of
399 nacl_interface_->ProcessNaClManifest(pp_instance(), program_url.c_str());
400 uses_nonsfi_mode_ = PP_ToBool(uses_nonsfi_mode);
401 if (pnacl_options.translate) {
402 pp::CompletionCallback translate_callback =
403 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
404 pnacl_coordinator_.reset(
405 PnaclCoordinator::BitcodeToNative(this,
408 translate_callback));
411 pp::CompletionCallback open_callback =
412 callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
413 // Will always call the callback on success or failure.
414 nacl_interface_->DownloadNexe(pp_instance(),
417 open_callback.pp_completion_callback());
423 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
424 nacl_interface_->ReportLoadError(pp_instance(),
425 error_info.error_code(),
426 error_info.message().c_str());
429 } // namespace plugin