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 "components/nacl/renderer/plugin/pnacl_coordinator.h"
11 #include "components/nacl/renderer/plugin/plugin.h"
12 #include "components/nacl/renderer/plugin/plugin_error.h"
13 #include "components/nacl/renderer/plugin/pnacl_translate_thread.h"
14 #include "components/nacl/renderer/plugin/service_runtime.h"
15 #include "components/nacl/renderer/plugin/temporary_file.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/service_runtime/include/sys/stat.h"
19 #include "ppapi/c/pp_bool.h"
20 #include "ppapi/c/pp_errors.h"
26 std::string
GetArchitectureAttributes(Plugin
* plugin
) {
27 pp::Var
attrs_var(pp::PASS_REF
,
28 plugin
->nacl_interface()->GetCpuFeatureAttrs());
29 return attrs_var
.AsString();
32 void DidCacheHit(void* user_data
, PP_FileHandle nexe_file_handle
) {
33 PnaclCoordinator
* coordinator
= static_cast<PnaclCoordinator
*>(user_data
);
34 coordinator
->BitcodeStreamCacheHit(nexe_file_handle
);
37 void DidCacheMiss(void* user_data
, int64_t expected_pexe_size
,
38 PP_FileHandle temp_nexe_file
) {
39 PnaclCoordinator
* coordinator
= static_cast<PnaclCoordinator
*>(user_data
);
40 coordinator
->BitcodeStreamCacheMiss(expected_pexe_size
,
44 void DidStreamData(void* user_data
, const void* stream_data
, int32_t length
) {
45 PnaclCoordinator
* coordinator
= static_cast<PnaclCoordinator
*>(user_data
);
46 coordinator
->BitcodeStreamGotData(stream_data
, length
);
49 void DidFinishStream(void* user_data
, int32_t pp_error
) {
50 PnaclCoordinator
* coordinator
= static_cast<PnaclCoordinator
*>(user_data
);
51 coordinator
->BitcodeStreamDidFinish(pp_error
);
54 PPP_PexeStreamHandler kPexeStreamHandler
= {
63 PnaclCoordinator
* PnaclCoordinator::BitcodeToNative(
65 const std::string
& pexe_url
,
66 const PP_PNaClOptions
& pnacl_options
,
67 const pp::CompletionCallback
& translate_notify_callback
) {
68 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (plugin=%p, pexe=%s)\n",
69 static_cast<void*>(plugin
), pexe_url
.c_str()));
70 PnaclCoordinator
* coordinator
=
71 new PnaclCoordinator(plugin
, pexe_url
,
73 translate_notify_callback
);
75 GetNaClInterface()->SetPNaClStartTime(plugin
->pp_instance());
76 int cpus
= plugin
->nacl_interface()->GetNumberOfProcessors();
77 coordinator
->num_threads_
= std::min(4, std::max(1, cpus
));
78 if (pnacl_options
.use_subzero
) {
79 coordinator
->split_module_count_
= 1;
81 coordinator
->split_module_count_
= coordinator
->num_threads_
;
83 // First start a network request for the pexe, to tickle the component
84 // updater's On-Demand resource throttler, and to get Last-Modified/ETag
85 // cache information. We can cancel the request later if there's
86 // a bitcode->nexe cache hit.
87 coordinator
->OpenBitcodeStream();
91 PnaclCoordinator::PnaclCoordinator(
93 const std::string
& pexe_url
,
94 const PP_PNaClOptions
& pnacl_options
,
95 const pp::CompletionCallback
& translate_notify_callback
)
96 : translate_finish_error_(PP_OK
),
98 translate_notify_callback_(translate_notify_callback
),
99 translation_finished_reported_(false),
100 compiler_subprocess_("compiler.nexe", NULL
, NULL
),
101 ld_subprocess_("linker.nexe", NULL
, NULL
),
103 pnacl_options_(pnacl_options
),
104 architecture_attributes_(GetArchitectureAttributes(plugin
)),
105 split_module_count_(0),
107 error_already_reported_(false),
109 pexe_bytes_compiled_(0),
110 expected_pexe_size_(-1) {
111 callback_factory_
.Initialize(this);
114 PnaclCoordinator::~PnaclCoordinator() {
115 PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, "
116 "translate_thread=%p\n",
117 static_cast<void*>(this), translate_thread_
.get()));
118 // Stopping the translate thread will cause the translate thread to try to
119 // run translation_complete_callback_ on the main thread. This destructor is
120 // running from the main thread, and by the time it exits, callback_factory_
121 // will have been destroyed. This will result in the cancellation of
122 // translation_complete_callback_, so no notification will be delivered.
123 if (translate_thread_
.get() != NULL
)
124 translate_thread_
->AbortSubprocesses();
125 if (!translation_finished_reported_
) {
126 plugin_
->nacl_interface()->ReportTranslationFinished(
127 plugin_
->pp_instance(), PP_FALSE
, pnacl_options_
.opt_level
,
128 pnacl_options_
.use_subzero
, 0, 0, 0);
130 // Force deleting the translate_thread now. It must be deleted
131 // before any scoped_* fields hanging off of PnaclCoordinator
132 // since the thread may be accessing those fields.
133 // It will also be accessing obj_files_.
134 translate_thread_
.reset(NULL
);
135 for (size_t i
= 0; i
< obj_files_
.size(); i
++)
136 delete obj_files_
[i
];
139 PP_FileHandle
PnaclCoordinator::TakeTranslatedFileHandle() {
140 DCHECK(temp_nexe_file_
!= NULL
);
141 return temp_nexe_file_
->TakeFileHandle();
144 void PnaclCoordinator::ReportNonPpapiError(PP_NaClError err_code
,
145 const std::string
& message
) {
146 ErrorInfo error_info
;
147 error_info
.SetReport(err_code
, message
);
148 plugin_
->ReportLoadError(error_info
);
152 void PnaclCoordinator::ReportPpapiError(PP_NaClError err_code
,
154 const std::string
& message
) {
155 std::stringstream ss
;
156 ss
<< "PnaclCoordinator: " << message
<< " (pp_error=" << pp_error
<< ").";
157 ErrorInfo error_info
;
158 error_info
.SetReport(err_code
, ss
.str());
159 plugin_
->ReportLoadError(error_info
);
163 void PnaclCoordinator::ExitWithError() {
164 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError\n"));
165 // Free all the intermediate callbacks we ever created.
166 // Note: this doesn't *cancel* the callbacks from the factories attached
167 // to the various helper classes (e.g., pnacl_resources). Thus, those
168 // callbacks may still run asynchronously. We let those run but ignore
169 // any other errors they may generate so that they do not end up running
170 // translate_notify_callback_, which has already been freed.
171 callback_factory_
.CancelAll();
172 if (!error_already_reported_
) {
173 error_already_reported_
= true;
174 translation_finished_reported_
= true;
175 plugin_
->nacl_interface()->ReportTranslationFinished(
176 plugin_
->pp_instance(), PP_FALSE
, pnacl_options_
.opt_level
,
177 pnacl_options_
.use_subzero
, 0, 0, 0);
178 translate_notify_callback_
.Run(PP_ERROR_FAILED
);
182 // Signal that Pnacl translation completed normally.
183 void PnaclCoordinator::TranslateFinished(int32_t pp_error
) {
184 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%"
185 NACL_PRId32
")\n", pp_error
));
186 // Bail out if there was an earlier error (e.g., pexe load failure),
187 // or if there is an error from the translation thread.
188 if (translate_finish_error_
!= PP_OK
|| pp_error
!= PP_OK
) {
189 plugin_
->ReportLoadError(error_info_
);
194 // Send out one last progress event, to finish up the progress events
195 // that were delayed (see the delay inserted in BitcodeGotCompiled).
196 if (expected_pexe_size_
!= -1) {
197 pexe_bytes_compiled_
= expected_pexe_size_
;
198 GetNaClInterface()->DispatchEvent(plugin_
->pp_instance(),
199 PP_NACL_EVENT_PROGRESS
,
202 pexe_bytes_compiled_
,
203 expected_pexe_size_
);
205 nacl_abi_off_t nexe_size
= 0;
206 struct nacl_abi_stat stbuf
;
207 struct NaClDesc
* desc
= temp_nexe_file_
->read_wrapper()->desc();
208 if (0 == (*((struct NaClDescVtbl
const *)desc
->base
.vtbl
)->Fstat
)(desc
,
210 nexe_size
= stbuf
.nacl_abi_st_size
;
212 // The nexe is written to the temp_nexe_file_. We must Reset() the file
213 // pointer to be able to read it again from the beginning.
214 temp_nexe_file_
->Reset();
216 // Report to the browser that translation finished. The browser will take
217 // care of storing the nexe in the cache.
218 translation_finished_reported_
= true;
219 plugin_
->nacl_interface()->ReportTranslationFinished(
220 plugin_
->pp_instance(), PP_TRUE
, pnacl_options_
.opt_level
,
221 pnacl_options_
.use_subzero
, nexe_size
, pexe_size_
,
222 translate_thread_
->GetCompileTime());
224 NexeReadDidOpen(PP_OK
);
227 void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error
) {
228 PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%"
229 NACL_PRId32
")\n", pp_error
));
230 if (pp_error
!= PP_OK
) {
231 if (pp_error
== PP_ERROR_FILENOTFOUND
) {
232 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_NOTFOUND
,
234 "Failed to open translated nexe (not found).");
237 if (pp_error
== PP_ERROR_NOACCESS
) {
238 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_NOACCESS
,
240 "Failed to open translated nexe (no access).");
243 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_OTHER
,
245 "Failed to open translated nexe.");
249 translate_notify_callback_
.Run(PP_OK
);
252 void PnaclCoordinator::OpenBitcodeStream() {
253 // Even though we haven't started downloading, create the translation
254 // thread object immediately. This ensures that any pieces of the file
255 // that get downloaded before the compilation thread is accepting
256 // SRPCs won't get dropped.
257 translate_thread_
.reset(new PnaclTranslateThread());
258 if (translate_thread_
== NULL
) {
260 PP_NACL_ERROR_PNACL_THREAD_CREATE
,
261 "PnaclCoordinator: could not allocate translation thread.");
265 GetNaClInterface()->StreamPexe(
266 plugin_
->pp_instance(), pexe_url_
.c_str(), pnacl_options_
.opt_level
,
267 pnacl_options_
.use_subzero
, &kPexeStreamHandler
, this);
270 void PnaclCoordinator::BitcodeStreamCacheHit(PP_FileHandle handle
) {
271 if (handle
== PP_kInvalidFileHandle
) {
273 PP_NACL_ERROR_PNACL_CREATE_TEMP
,
275 "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
276 BitcodeStreamDidFinish(PP_ERROR_FAILED
);
279 temp_nexe_file_
.reset(new TempFile(plugin_
, handle
));
280 // Open it for reading as the cached nexe file.
281 NexeReadDidOpen(temp_nexe_file_
->Open(false));
284 void PnaclCoordinator::BitcodeStreamCacheMiss(int64_t expected_pexe_size
,
285 PP_FileHandle nexe_handle
) {
286 // IMPORTANT: Make sure that PnaclResources::StartLoad() is only
287 // called after you receive a response to a request for a .pexe file.
289 // The component updater's resource throttles + OnDemand update/install
290 // should block the URL request until the compiler is present. Now we
291 // can load the resources (e.g. llc and ld nexes).
292 resources_
.reset(new PnaclResources(plugin_
, pnacl_options_
.use_subzero
));
293 CHECK(resources_
!= NULL
);
295 // The first step of loading resources: read the resource info file.
296 if (!resources_
->ReadResourceInfo()) {
301 // Second step of loading resources: call StartLoad to load pnacl-llc
302 // and pnacl-ld, based on the filenames found in the resource info file.
303 if (!resources_
->StartLoad()) {
305 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
306 std::string("The Portable Native Client (pnacl) component is not "
307 "installed. Please consult chrome://components for more "
312 expected_pexe_size_
= expected_pexe_size
;
314 for (int i
= 0; i
< split_module_count_
; i
++) {
315 PP_FileHandle obj_handle
=
316 plugin_
->nacl_interface()->CreateTemporaryFile(plugin_
->pp_instance());
317 nacl::scoped_ptr
<TempFile
> temp_file(new TempFile(plugin_
, obj_handle
));
318 int32_t pp_error
= temp_file
->Open(true);
319 if (pp_error
!= PP_OK
) {
320 ReportPpapiError(PP_NACL_ERROR_PNACL_CREATE_TEMP
,
322 "Failed to open scratch object file.");
325 obj_files_
.push_back(temp_file
.release());
328 invalid_desc_wrapper_
.reset(plugin_
->wrapper_factory()->MakeInvalid());
330 temp_nexe_file_
.reset(new TempFile(plugin_
, nexe_handle
));
331 // Open the nexe file for connecting ld and sel_ldr.
332 // Start translation when done with this last step of setup!
333 int32_t pp_error
= temp_nexe_file_
->Open(true);
334 if (pp_error
!= PP_OK
) {
336 PP_NACL_ERROR_PNACL_CREATE_TEMP
,
338 "PnaclCoordinator: Got bad temp file handle from writing nexe"));
344 void PnaclCoordinator::BitcodeStreamGotData(const void* data
, int32_t length
) {
345 DCHECK(translate_thread_
.get());
347 translate_thread_
->PutBytes(data
, length
);
348 if (data
&& length
> 0)
349 pexe_size_
+= length
;
352 void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error
) {
353 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
354 NACL_PRId32
")\n", pp_error
));
355 if (pp_error
!= PP_OK
) {
356 // Defer reporting the error and cleanup until after the translation
357 // thread returns, because it may be accessing the coordinator's
358 // objects or writing to the files.
359 translate_finish_error_
= pp_error
;
360 if (pp_error
== PP_ERROR_ABORTED
) {
361 error_info_
.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_ABORTED
,
362 "PnaclCoordinator: pexe load failed (aborted).");
364 if (pp_error
== PP_ERROR_NOACCESS
) {
365 error_info_
.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_NOACCESS
,
366 "PnaclCoordinator: pexe load failed (no access).");
368 std::stringstream ss
;
369 ss
<< "PnaclCoordinator: pexe load failed (pp_error=" << pp_error
<< ").";
370 error_info_
.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_OTHER
, ss
.str());
373 if (translate_thread_
->started())
374 translate_thread_
->AbortSubprocesses();
376 TranslateFinished(pp_error
);
378 // Compare download completion pct (100% now), to compile completion pct.
379 GetNaClInterface()->LogBytesCompiledVsDownloaded(
380 pnacl_options_
.use_subzero
, pexe_bytes_compiled_
, pexe_size_
);
381 translate_thread_
->EndStream();
385 void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error
,
386 int64_t bytes_compiled
) {
387 DCHECK(pp_error
== PP_OK
);
388 pexe_bytes_compiled_
+= bytes_compiled
;
389 // Hold off reporting the last few bytes of progress, since we don't know
390 // when they are actually completely compiled. "bytes_compiled" only means
391 // that bytes were sent to the compiler.
392 if (expected_pexe_size_
!= -1) {
393 if (!ShouldDelayProgressEvent()) {
394 GetNaClInterface()->DispatchEvent(plugin_
->pp_instance(),
395 PP_NACL_EVENT_PROGRESS
,
398 pexe_bytes_compiled_
,
399 expected_pexe_size_
);
402 GetNaClInterface()->DispatchEvent(plugin_
->pp_instance(),
403 PP_NACL_EVENT_PROGRESS
,
406 pexe_bytes_compiled_
,
407 expected_pexe_size_
);
411 pp::CompletionCallback
PnaclCoordinator::GetCompileProgressCallback(
412 int64_t bytes_compiled
) {
413 return callback_factory_
.NewCallback(&PnaclCoordinator::BitcodeGotCompiled
,
417 void PnaclCoordinator::LoadCompiler() {
418 PLUGIN_PRINTF(("PnaclCoordinator::LoadCompiler"));
419 int64_t compiler_load_start_time
= NaClGetTimeOfDayMicroseconds();
420 pp::CompletionCallback load_finished
= callback_factory_
.NewCallback(
421 &PnaclCoordinator::RunCompile
, compiler_load_start_time
);
422 PnaclResources::ResourceType compiler_type
= pnacl_options_
.use_subzero
423 ? PnaclResources::SUBZERO
424 : PnaclResources::LLC
;
425 // Transfer file_info ownership to the sel_ldr launcher.
426 PP_NaClFileInfo file_info
= resources_
->TakeFileInfo(compiler_type
);
427 const std::string
& url
= resources_
->GetUrl(compiler_type
);
428 plugin_
->LoadHelperNaClModule(url
, file_info
, &compiler_subprocess_
,
432 void PnaclCoordinator::RunCompile(int32_t pp_error
,
433 int64_t compiler_load_start_time
) {
435 ("PnaclCoordinator::RunCompile (pp_error=%" NACL_PRId32
")\n", pp_error
));
436 if (pp_error
!= PP_OK
) {
438 PP_NACL_ERROR_PNACL_LLC_SETUP
,
439 "PnaclCoordinator: Compiler process could not be created.");
442 int64_t compiler_load_time_total
=
443 NaClGetTimeOfDayMicroseconds() - compiler_load_start_time
;
444 GetNaClInterface()->LogTranslateTime("NaCl.Perf.PNaClLoadTime.LoadCompiler",
445 compiler_load_time_total
);
446 GetNaClInterface()->LogTranslateTime(
447 pnacl_options_
.use_subzero
448 ? "NaCl.Perf.PNaClLoadTime.LoadCompiler.Subzero"
449 : "NaCl.Perf.PNaClLoadTime.LoadCompiler.LLC",
450 compiler_load_time_total
);
452 // Invoke llc followed by ld off the main thread. This allows use of
453 // blocking RPCs that would otherwise block the JavaScript main thread.
454 pp::CompletionCallback report_translate_finished
=
455 callback_factory_
.NewCallback(&PnaclCoordinator::TranslateFinished
);
456 pp::CompletionCallback compile_finished
=
457 callback_factory_
.NewCallback(&PnaclCoordinator::LoadLinker
);
458 CHECK(translate_thread_
!= NULL
);
459 translate_thread_
->SetupState(
460 report_translate_finished
, &compiler_subprocess_
, &ld_subprocess_
,
461 &obj_files_
, num_threads_
, temp_nexe_file_
.get(),
462 invalid_desc_wrapper_
.get(), &error_info_
, &pnacl_options_
,
463 architecture_attributes_
, this);
464 translate_thread_
->RunCompile(compile_finished
);
467 void PnaclCoordinator::LoadLinker(int32_t pp_error
) {
469 ("PnaclCoordinator::LoadLinker (pp_error=%" NACL_PRId32
")\n", pp_error
));
470 // Errors in the previous step would have skipped to TranslateFinished
471 // so we only expect PP_OK here.
472 DCHECK(pp_error
== PP_OK
);
473 if (pp_error
!= PP_OK
) {
476 ErrorInfo error_info
;
477 int64_t ld_load_start_time
= NaClGetTimeOfDayMicroseconds();
478 pp::CompletionCallback load_finished
= callback_factory_
.NewCallback(
479 &PnaclCoordinator::RunLink
, ld_load_start_time
);
480 // Transfer file_info ownership to the sel_ldr launcher.
481 PP_NaClFileInfo ld_file_info
= resources_
->TakeFileInfo(PnaclResources::LD
);
482 plugin_
->LoadHelperNaClModule(resources_
->GetUrl(PnaclResources::LD
),
483 ld_file_info
, &ld_subprocess_
, load_finished
);
486 void PnaclCoordinator::RunLink(int32_t pp_error
, int64_t ld_load_start_time
) {
488 ("PnaclCoordinator::RunLink (pp_error=%" NACL_PRId32
")\n", pp_error
));
489 if (pp_error
!= PP_OK
) {
491 PP_NACL_ERROR_PNACL_LD_SETUP
,
492 "PnaclCoordinator: Linker process could not be created.");
495 GetNaClInterface()->LogTranslateTime(
496 "NaCl.Perf.PNaClLoadTime.LoadLinker",
497 NaClGetTimeOfDayMicroseconds() - ld_load_start_time
);
498 translate_thread_
->RunLink();
501 } // namespace plugin