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