skia/ext: Early out from analysis when we have more than 1 draw op.
[chromium-blink-merge.git] / ppapi / native_client / src / trusted / plugin / plugin.cc
blobd96047122b3354e8e92e24ceecff3a21d7569cf2
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)));
49 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (%s)\n",
50 main_subprocess_.detailed_description().c_str()));
52 // Shut down service runtime. This must be done before all other calls so
53 // they don't block forever when waiting for the upcall thread to exit.
54 main_subprocess_.Shutdown();
56 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
57 static_cast<void*>(this)));
60 void Plugin::HistogramTimeSmall(const std::string& name,
61 int64_t ms) {
62 if (ms < 0) return;
63 uma_interface_.HistogramCustomTimes(name,
64 ms,
65 kTimeSmallMin, kTimeSmallMax,
66 kTimeSmallBuckets);
69 bool Plugin::LoadHelperNaClModuleInternal(NaClSubprocess* subprocess,
70 const SelLdrStartParams& params) {
71 CHECK(!pp::Module::Get()->core()->IsMainThread());
72 ServiceRuntime* service_runtime =
73 new ServiceRuntime(this,
74 pp_instance(),
75 false, // No main_service_runtime.
76 false, // No non-SFI mode (i.e. in SFI-mode).
77 pp::BlockUntilComplete(),
78 pp::BlockUntilComplete());
79 subprocess->set_service_runtime(service_runtime);
80 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
81 "(service_runtime=%p)\n",
82 static_cast<void*>(service_runtime)));
84 // Now start the SelLdr instance. This must be created on the main thread.
85 bool service_runtime_started = false;
86 pp::CompletionCallback sel_ldr_callback =
87 callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone,
88 &service_runtime_started,
89 service_runtime);
90 pp::CompletionCallback callback =
91 callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
92 service_runtime, params,
93 sel_ldr_callback);
94 pp::Module::Get()->core()->CallOnMainThread(0, callback, 0);
95 if (!service_runtime->WaitForSelLdrStart()) {
96 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule "
97 "WaitForSelLdrStart timed out!\n"));
98 return false;
100 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (service_runtime_started=%d)\n",
101 service_runtime_started));
102 if (!service_runtime_started)
103 return false;
105 // Now actually start the nexe.
107 // We can't use pp::BlockUntilComplete() inside an in-process plugin, so we
108 // have to roll our own blocking logic, similar to WaitForSelLdrStart()
109 // above, except without timeout logic.
110 pp::Module::Get()->core()->CallOnMainThread(
112 callback_factory_.NewCallback(&Plugin::StartNexe, service_runtime));
113 return service_runtime->WaitForNexeStart();
116 void Plugin::StartSelLdrOnMainThread(int32_t pp_error,
117 ServiceRuntime* service_runtime,
118 const SelLdrStartParams& params,
119 pp::CompletionCallback callback) {
120 if (pp_error != PP_OK) {
121 PLUGIN_PRINTF(("Plugin::StartSelLdrOnMainThread: non-PP_OK arg "
122 "-- SHOULD NOT HAPPEN\n"));
123 pp::Module::Get()->core()->CallOnMainThread(0, callback, pp_error);
124 return;
126 service_runtime->StartSelLdr(params, callback);
129 void Plugin::SignalStartSelLdrDone(int32_t pp_error,
130 bool* started,
131 ServiceRuntime* service_runtime) {
132 *started = (pp_error == PP_OK);
133 service_runtime->SignalStartSelLdrDone();
136 void Plugin::LoadNaClModule(PP_NaClFileInfo file_info,
137 bool uses_nonsfi_mode,
138 bool enable_dyncode_syscalls,
139 bool enable_exception_handling,
140 bool enable_crash_throttling,
141 const pp::CompletionCallback& init_done_cb,
142 const pp::CompletionCallback& crash_cb) {
143 CHECK(pp::Module::Get()->core()->IsMainThread());
144 // Before forking a new sel_ldr process, ensure that we do not leak
145 // the ServiceRuntime object for an existing subprocess, and that any
146 // associated listener threads do not go unjoined because if they
147 // outlive the Plugin object, they will not be memory safe.
148 ShutDownSubprocesses();
149 pp::Var manifest_base_url =
150 pp::Var(pp::PASS_REF, nacl_interface_->GetManifestBaseURL(pp_instance()));
151 std::string manifest_base_url_str = manifest_base_url.AsString();
153 SelLdrStartParams params(manifest_base_url_str,
154 file_info,
155 true /* uses_irt */,
156 true /* uses_ppapi */,
157 enable_dyncode_syscalls,
158 enable_exception_handling,
159 enable_crash_throttling);
160 ErrorInfo error_info;
161 ServiceRuntime* service_runtime = new ServiceRuntime(
162 this, pp_instance(), true, uses_nonsfi_mode, init_done_cb, crash_cb);
163 main_subprocess_.set_service_runtime(service_runtime);
164 PLUGIN_PRINTF(("Plugin::LoadNaClModule (service_runtime=%p)\n",
165 static_cast<void*>(service_runtime)));
166 if (NULL == service_runtime) {
167 error_info.SetReport(
168 PP_NACL_ERROR_SEL_LDR_INIT,
169 "sel_ldr init failure " + main_subprocess_.description());
170 ReportLoadError(error_info);
171 return;
174 // We don't take any action once nexe loading has completed, so pass an empty
175 // callback here for |callback|.
176 pp::CompletionCallback callback = callback_factory_.NewCallback(
177 &Plugin::StartNexe, service_runtime);
178 StartSelLdrOnMainThread(
179 static_cast<int32_t>(PP_OK), service_runtime, params, callback);
182 void Plugin::StartNexe(int32_t pp_error, ServiceRuntime* service_runtime) {
183 CHECK(pp::Module::Get()->core()->IsMainThread());
184 if (pp_error != PP_OK)
185 return;
186 service_runtime->StartNexe();
189 bool Plugin::LoadNaClModuleContinuationIntern() {
190 ErrorInfo error_info;
191 if (!uses_nonsfi_mode_) {
192 if (!main_subprocess_.StartSrpcServices()) {
193 // The NaCl process probably crashed. On Linux, a crash causes this
194 // error, while on other platforms, the error is detected below, when we
195 // attempt to start the proxy. Report a module initialization error here,
196 // to make it less confusing for developers.
197 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
198 "StartSrpcServices failed\n");
199 error_info.SetReport(PP_NACL_ERROR_START_PROXY_MODULE,
200 "could not initialize module.");
201 ReportLoadError(error_info);
202 return false;
206 bool result = PP_ToBool(nacl_interface_->StartPpapiProxy(pp_instance()));
207 if (result) {
208 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
209 main_subprocess_.detailed_description().c_str()));
211 return result;
214 NaClSubprocess* Plugin::LoadHelperNaClModule(const nacl::string& helper_url,
215 PP_NaClFileInfo file_info,
216 ErrorInfo* error_info) {
217 nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
218 new NaClSubprocess("helper module", NULL, NULL));
219 if (NULL == nacl_subprocess.get()) {
220 error_info->SetReport(PP_NACL_ERROR_SEL_LDR_INIT,
221 "unable to allocate helper subprocess.");
222 return NULL;
225 // Do not report UMA stats for translator-related nexes.
226 // TODO(sehr): define new UMA stats for translator related nexe events.
227 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is
228 // done to save on address space and swap space.
229 SelLdrStartParams params(helper_url,
230 file_info,
231 false /* uses_irt */,
232 false /* uses_ppapi */,
233 false /* enable_dyncode_syscalls */,
234 false /* enable_exception_handling */,
235 true /* enable_crash_throttling */);
237 // Helper NaCl modules always use the PNaCl manifest, as there is no
238 // corresponding NMF.
239 if (!LoadHelperNaClModuleInternal(nacl_subprocess.get(), params))
240 return NULL;
242 // We need not wait for the init_done callback. We can block
243 // here in StartSrpcServices, since helper NaCl modules
244 // are spawned from a private thread.
246 // TODO(bsy): if helper module crashes, we should abort.
247 // crash_cb is not used here, so we are relying on crashes
248 // being detected in StartSrpcServices or later.
250 // NB: More refactoring might be needed, however, if helper
251 // NaCl modules have their own manifest. Currently the
252 // manifest is a per-plugin-instance object, not a per
253 // NaClSubprocess object.
254 if (!nacl_subprocess->StartSrpcServices()) {
255 error_info->SetReport(PP_NACL_ERROR_SRPC_CONNECTION_FAIL,
256 "SRPC connection failure for " +
257 nacl_subprocess->description());
258 return NULL;
261 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s, %s)\n",
262 helper_url.c_str(),
263 nacl_subprocess.get()->detailed_description().c_str()));
265 return nacl_subprocess.release();
268 // All failures of this function will show up as "Missing Plugin-in", so
269 // there is no need to log to JS console that there was an initialization
270 // failure. Note that module loading functions will log their own errors.
271 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
272 PLUGIN_PRINTF(("Plugin::Init (argc=%" NACL_PRIu32 ")\n", argc));
273 nacl_interface_->InitializePlugin(pp_instance(), argc, argn, argv);
274 wrapper_factory_ = new nacl::DescWrapperFactory();
275 pp::CompletionCallback open_cb =
276 callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
277 nacl_interface_->RequestNaClManifest(pp_instance(),
278 open_cb.pp_completion_callback());
279 return true;
282 Plugin::Plugin(PP_Instance pp_instance)
283 : pp::Instance(pp_instance),
284 main_subprocess_("main subprocess", NULL, NULL),
285 uses_nonsfi_mode_(false),
286 wrapper_factory_(NULL),
287 nacl_interface_(NULL),
288 uma_interface_(this) {
289 PLUGIN_PRINTF(("Plugin::Plugin (this=%p, pp_instance=%"
290 NACL_PRId32 ")\n", static_cast<void*>(this), pp_instance));
291 callback_factory_.Initialize(this);
292 nacl_interface_ = GetNaClInterface();
293 CHECK(nacl_interface_ != NULL);
295 // Notify PPB_NaCl_Private that the instance is created before altering any
296 // state that it tracks.
297 nacl_interface_->InstanceCreated(pp_instance);
298 // We call set_exit_status() here to ensure that the 'exitStatus' property is
299 // set. This can only be called when nacl_interface_ is not NULL.
300 set_exit_status(-1);
301 nexe_file_info_ = kInvalidNaClFileInfo;
305 Plugin::~Plugin() {
306 int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
308 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p)\n",
309 static_cast<void*>(this)));
310 // Destroy the coordinator while the rest of the data is still there
311 pnacl_coordinator_.reset(NULL);
313 nacl_interface_->InstanceDestroyed(pp_instance());
315 // ShutDownSubprocesses shuts down the main subprocess, which shuts
316 // down the main ServiceRuntime object, which kills the subprocess.
317 // As a side effect of the subprocess being killed, the reverse
318 // services thread(s) will get EOF on the reverse channel(s), and
319 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke
320 // ReverseService::WaitForServiceThreadsToExit(), so that there will
321 // not be an extent thread(s) hanging around. This means that the
322 // ~Plugin will block until this happens. This is a requirement,
323 // since the renderer should be free to unload the plugin code, and
324 // we cannot have threads running code that gets unloaded before
325 // they exit.
327 // By waiting for the threads here, we also ensure that the Plugin
328 // object and the subprocess and ServiceRuntime objects is not
329 // (fully) destroyed while the threads are running, so resources
330 // that are destroyed after ShutDownSubprocesses (below) are
331 // guaranteed to be live and valid for access from the service
332 // threads.
334 // The main_subprocess object, which wraps the main service_runtime
335 // object, is dtor'd implicitly after the explicit code below runs,
336 // so the main service runtime object will not have been dtor'd,
337 // though the Shutdown method may have been called, during the
338 // lifetime of the service threads.
339 ShutDownSubprocesses();
341 delete wrapper_factory_;
343 HistogramTimeSmall(
344 "NaCl.Perf.ShutdownTime.Total",
345 (NaClGetTimeOfDayMicroseconds() - shutdown_start)
346 / NACL_MICROS_PER_MILLI);
348 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, return)\n",
349 static_cast<void*>(this)));
352 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
353 PLUGIN_PRINTF(("Plugin::HandleDocumentLoad (this=%p)\n",
354 static_cast<void*>(this)));
355 // We don't know if the plugin will handle the document load, but return
356 // true in order to give it a chance to respond once the proxy is started.
357 return true;
360 void Plugin::NexeFileDidOpen(int32_t pp_error) {
361 if (pp_error != PP_OK)
362 return;
364 NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n");
365 LoadNaClModule(
366 nexe_file_info_,
367 uses_nonsfi_mode_,
368 true, /* enable_dyncode_syscalls */
369 true, /* enable_exception_handling */
370 false, /* enable_crash_throttling */
371 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation),
372 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
375 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
376 UNREFERENCED_PARAMETER(pp_error);
377 NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
378 if (LoadNaClModuleContinuationIntern()) {
379 NaClLog(4, "NexeFileDidOpenContinuation: success;"
380 " setting histograms\n");
381 int64_t nexe_size = nacl_interface_->GetNexeSize(pp_instance());
382 nacl_interface_->ReportLoadSuccess(
383 pp_instance(), nexe_size, nexe_size);
384 } else {
385 NaClLog(4, "NexeFileDidOpenContinuation: failed.");
387 NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
390 void Plugin::NexeDidCrash(int32_t pp_error) {
391 PLUGIN_PRINTF(("Plugin::NexeDidCrash (pp_error=%" NACL_PRId32 ")\n",
392 pp_error));
393 if (pp_error != PP_OK) {
394 PLUGIN_PRINTF(("Plugin::NexeDidCrash: CallOnMainThread callback with"
395 " non-PP_OK arg -- SHOULD NOT HAPPEN\n"));
398 std::string crash_log =
399 main_subprocess_.service_runtime()->GetCrashLogOutput();
400 nacl_interface_->NexeDidCrash(pp_instance(), crash_log.c_str());
403 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
404 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
405 pp_error));
406 if (pp_error != PP_OK) {
407 // Error should have been reported by pnacl. Just return.
408 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate error in Pnacl\n"));
409 return;
412 // Inform JavaScript that we successfully translated the bitcode to a nexe.
413 PP_FileHandle handle = pnacl_coordinator_->TakeTranslatedFileHandle();
415 PP_NaClFileInfo info;
416 info.handle = handle;
417 info.token_lo = 0;
418 info.token_hi = 0;
419 LoadNaClModule(
420 info,
421 false, /* uses_nonsfi_mode */
422 false, /* enable_dyncode_syscalls */
423 false, /* enable_exception_handling */
424 true, /* enable_crash_throttling */
425 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation),
426 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
429 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
430 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
431 UNREFERENCED_PARAMETER(pp_error);
432 if (LoadNaClModuleContinuationIntern()) {
433 int64_t loaded;
434 int64_t total;
435 // TODO(teravest): Tighten this up so we can get rid of
436 // GetCurrentProgress(). loaded should always equal total.
437 pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
438 nacl_interface_->ReportLoadSuccess(pp_instance(), loaded, total);
442 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
443 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
444 NACL_PRId32 ")\n", pp_error));
445 if (pp_error != PP_OK)
446 return;
448 PP_Var pp_program_url;
449 PP_PNaClOptions pnacl_options = {PP_FALSE, PP_FALSE, 2};
450 PP_Bool uses_nonsfi_mode;
451 if (nacl_interface_->GetManifestProgramURL(
452 pp_instance(), &pp_program_url, &pnacl_options, &uses_nonsfi_mode)) {
453 std::string program_url = pp::Var(pp::PASS_REF, pp_program_url).AsString();
454 // TODO(teravest): Make ProcessNaClManifest take responsibility for more of
455 // this function.
456 nacl_interface_->ProcessNaClManifest(pp_instance(), program_url.c_str());
457 uses_nonsfi_mode_ = PP_ToBool(uses_nonsfi_mode);
458 if (pnacl_options.translate) {
459 pp::CompletionCallback translate_callback =
460 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
461 pnacl_coordinator_.reset(
462 PnaclCoordinator::BitcodeToNative(this,
463 program_url,
464 pnacl_options,
465 translate_callback));
466 return;
467 } else {
468 pp::CompletionCallback open_callback =
469 callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
470 // Will always call the callback on success or failure.
471 nacl_interface_->DownloadNexe(pp_instance(),
472 program_url.c_str(),
473 &nexe_file_info_,
474 open_callback.pp_completion_callback());
475 return;
480 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
481 nacl_interface_->ReportLoadError(pp_instance(),
482 error_info.error_code(),
483 error_info.message().c_str());
486 bool Plugin::DocumentCanRequest(const std::string& url) {
487 CHECK(pp::Module::Get()->core()->IsMainThread());
488 CHECK(pp::URLUtil_Dev::Get() != NULL);
489 return pp::URLUtil_Dev::Get()->DocumentCanRequest(this, pp::Var(url));
492 void Plugin::set_exit_status(int exit_status) {
493 pp::Core* core = pp::Module::Get()->core();
494 if (core->IsMainThread()) {
495 SetExitStatusOnMainThread(PP_OK, exit_status);
496 } else {
497 pp::CompletionCallback callback =
498 callback_factory_.NewCallback(&Plugin::SetExitStatusOnMainThread,
499 exit_status);
500 core->CallOnMainThread(0, callback, 0);
504 void Plugin::SetExitStatusOnMainThread(int32_t pp_error,
505 int exit_status) {
506 DCHECK(pp::Module::Get()->core()->IsMainThread());
507 DCHECK(nacl_interface_);
508 nacl_interface_->SetExitStatus(pp_instance(), exit_status);
512 } // namespace plugin