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/pnacl_translate_thread.h"
9 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
10 #include "ppapi/cpp/var.h"
11 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
12 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
13 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h"
14 #include "ppapi/native_client/src/trusted/plugin/srpc_params.h"
15 #include "ppapi/native_client/src/trusted/plugin/temporary_file.h"
16 #include "ppapi/native_client/src/trusted/plugin/utility.h"
21 template <typename Val
>
22 nacl::string
MakeCommandLineArg(const char* key
, const Val val
) {
23 nacl::stringstream ss
;
28 void GetLlcCommandLine(Plugin
* plugin
,
29 std::vector
<char>* split_args
,
30 size_t obj_files_size
,
33 const nacl::string
&architecture_attributes
) {
34 typedef std::vector
<nacl::string
> Args
;
37 // TODO(dschuff): This CL override is ugly. Change llc to default to
38 // using the number of modules specified in the first param, and
39 // ignore multiple uses of -split-module
40 args
.push_back(MakeCommandLineArg("-split-module=", obj_files_size
));
41 args
.push_back(MakeCommandLineArg("-O=", opt_level
));
43 args
.push_back("-bitcode-format=llvm");
44 if (!architecture_attributes
.empty())
45 args
.push_back("-mattr=" + architecture_attributes
);
47 for (Args::const_iterator
arg(args
.begin()); arg
!= args
.end(); ++arg
) {
48 std::copy(arg
->begin(), arg
->end(), std::back_inserter(*split_args
));
49 split_args
->push_back('\x00');
55 PnaclTranslateThread::PnaclTranslateThread() : llc_subprocess_active_(false),
56 ld_subprocess_active_(false),
57 subprocesses_aborted_(false),
62 coordinator_error_info_(NULL
),
66 NaClXMutexCtor(&subprocess_mu_
);
67 NaClXMutexCtor(&cond_mu_
);
68 NaClXCondVarCtor(&buffer_cond_
);
71 void PnaclTranslateThread::RunTranslate(
72 const pp::CompletionCallback
& finish_callback
,
73 const std::vector
<TempFile
*>* obj_files
,
75 nacl::DescWrapper
* invalid_desc_wrapper
,
76 ErrorInfo
* error_info
,
77 PnaclResources
* resources
,
78 PP_PNaClOptions
* pnacl_options
,
79 const nacl::string
&architecture_attributes
,
80 PnaclCoordinator
* coordinator
,
82 PLUGIN_PRINTF(("PnaclStreamingTranslateThread::RunTranslate)\n"));
83 obj_files_
= obj_files
;
84 nexe_file_
= nexe_file
;
85 invalid_desc_wrapper_
= invalid_desc_wrapper
;
86 coordinator_error_info_
= error_info
;
87 resources_
= resources
;
88 pnacl_options_
= pnacl_options
;
89 architecture_attributes_
= architecture_attributes
;
90 coordinator_
= coordinator
;
93 // Invoke llc followed by ld off the main thread. This allows use of
94 // blocking RPCs that would otherwise block the JavaScript main thread.
95 report_translate_finished_
= finish_callback
;
96 translate_thread_
.reset(new NaClThread
);
97 if (translate_thread_
== NULL
) {
98 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE
,
99 "could not allocate thread struct.");
102 const int32_t kArbitraryStackSize
= 128 * 1024;
103 if (!NaClThreadCreateJoinable(translate_thread_
.get(),
106 kArbitraryStackSize
)) {
107 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE
,
108 "could not create thread.");
109 translate_thread_
.reset(NULL
);
113 // Called from main thread to send bytes to the translator.
114 void PnaclTranslateThread::PutBytes(const void* bytes
, int32_t count
) {
115 CHECK(bytes
!= NULL
);
116 NaClXMutexLock(&cond_mu_
);
117 data_buffers_
.push_back(std::vector
<char>());
118 data_buffers_
.back().insert(data_buffers_
.back().end(),
119 static_cast<const char*>(bytes
),
120 static_cast<const char*>(bytes
) + count
);
121 NaClXCondVarSignal(&buffer_cond_
);
122 NaClXMutexUnlock(&cond_mu_
);
125 void PnaclTranslateThread::EndStream() {
126 NaClXMutexLock(&cond_mu_
);
128 NaClXCondVarSignal(&buffer_cond_
);
129 NaClXMutexUnlock(&cond_mu_
);
132 void WINAPI
PnaclTranslateThread::DoTranslateThread(void* arg
) {
133 PnaclTranslateThread
* translator
=
134 reinterpret_cast<PnaclTranslateThread
*>(arg
);
135 translator
->DoTranslate();
138 void PnaclTranslateThread::DoTranslate() {
139 ErrorInfo error_info
;
141 std::vector
<nacl::DescWrapper
*> llc_out_files
;
143 for (i
= 0; i
< obj_files_
->size(); i
++)
144 llc_out_files
.push_back((*obj_files_
)[i
]->write_wrapper());
145 for (; i
< PnaclCoordinator::kMaxTranslatorObjectFiles
; i
++)
146 llc_out_files
.push_back(invalid_desc_wrapper_
);
148 pp::Core
* core
= pp::Module::Get()->core();
149 int64_t llc_start_time
= NaClGetTimeOfDayMicroseconds();
150 PP_NaClFileInfo llc_file_info
= resources_
->TakeLlcFileInfo();
151 // On success, ownership of llc_file_info is transferred.
152 NaClSubprocess
* llc_subprocess
= plugin_
->LoadHelperNaClModule(
153 resources_
->GetLlcUrl(), llc_file_info
, &error_info
);
154 if (llc_subprocess
== NULL
) {
155 if (llc_file_info
.handle
!= PP_kInvalidFileHandle
)
156 CloseFileHandle(llc_file_info
.handle
);
157 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_SETUP
,
158 "Compile process could not be created: " +
159 error_info
.message());
162 GetNaClInterface()->LogTranslateTime(
163 "NaCl.Perf.PNaClLoadTime.LoadCompiler",
164 NaClGetTimeOfDayMicroseconds() - llc_start_time
);
167 nacl::MutexLocker
ml(&subprocess_mu_
);
168 // If we received a call to AbortSubprocesses() before we had a chance to
169 // set llc_subprocess_, shut down and clean up the subprocess started here.
170 if (subprocesses_aborted_
) {
171 llc_subprocess
->service_runtime()->Shutdown();
172 delete llc_subprocess
;
175 llc_subprocess_
.reset(llc_subprocess
);
176 llc_subprocess
= NULL
;
177 llc_subprocess_active_
= true;
180 int64_t compile_start_time
= NaClGetTimeOfDayMicroseconds();
183 std::vector
<char> split_args
;
184 GetLlcCommandLine(plugin_
,
187 pnacl_options_
->opt_level
,
188 pnacl_options_
->is_debug
,
189 architecture_attributes_
);
190 init_success
= llc_subprocess_
->InvokeSrpcMethod(
191 "StreamInitWithSplit",
192 "ihhhhhhhhhhhhhhhhC",
194 static_cast<int>(obj_files_
->size()),
195 llc_out_files
[0]->desc(),
196 llc_out_files
[1]->desc(),
197 llc_out_files
[2]->desc(),
198 llc_out_files
[3]->desc(),
199 llc_out_files
[4]->desc(),
200 llc_out_files
[5]->desc(),
201 llc_out_files
[6]->desc(),
202 llc_out_files
[7]->desc(),
203 llc_out_files
[8]->desc(),
204 llc_out_files
[9]->desc(),
205 llc_out_files
[10]->desc(),
206 llc_out_files
[11]->desc(),
207 llc_out_files
[12]->desc(),
208 llc_out_files
[13]->desc(),
209 llc_out_files
[14]->desc(),
210 llc_out_files
[15]->desc(),
214 if (llc_subprocess_
->srpc_client()->GetLastError() ==
215 NACL_SRPC_RESULT_APP_ERROR
) {
216 // The error message is only present if the error was returned from llc
217 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
218 nacl::string("Stream init failed: ") +
219 nacl::string(params
.outs()[0]->arrays
.str
));
221 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
222 "Stream init internal error");
226 PLUGIN_PRINTF(("PnaclCoordinator: StreamInit successful\n"));
228 // llc process is started.
229 while(!done_
|| data_buffers_
.size() > 0) {
230 NaClXMutexLock(&cond_mu_
);
231 while(!done_
&& data_buffers_
.size() == 0) {
232 NaClXCondVarWait(&buffer_cond_
, &cond_mu_
);
234 PLUGIN_PRINTF(("PnaclTranslateThread awake (done=%d, size=%" NACL_PRIuS
236 done_
, data_buffers_
.size()));
237 if (data_buffers_
.size() > 0) {
238 std::vector
<char> data
;
239 data
.swap(data_buffers_
.front());
240 data_buffers_
.pop_front();
241 NaClXMutexUnlock(&cond_mu_
);
242 PLUGIN_PRINTF(("StreamChunk\n"));
243 if (!llc_subprocess_
->InvokeSrpcMethod("StreamChunk",
248 if (llc_subprocess_
->srpc_client()->GetLastError() !=
249 NACL_SRPC_RESULT_APP_ERROR
) {
250 // If the error was reported by the translator, then we fall through
251 // and call StreamEnd, which returns a string describing the error,
252 // which we can then send to the Javascript console. Otherwise just
253 // fail here, since the translator has probably crashed or asserted.
254 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
255 "Compile stream chunk failed. "
256 "The PNaCl translator has probably crashed.");
261 PLUGIN_PRINTF(("StreamChunk Successful\n"));
262 core
->CallOnMainThread(
264 coordinator_
->GetCompileProgressCallback(data
.size()),
268 NaClXMutexUnlock(&cond_mu_
);
271 PLUGIN_PRINTF(("PnaclTranslateThread done with chunks\n"));
273 if (!llc_subprocess_
->InvokeSrpcMethod("StreamEnd", std::string(), ¶ms
)) {
274 PLUGIN_PRINTF(("PnaclTranslateThread StreamEnd failed\n"));
275 if (llc_subprocess_
->srpc_client()->GetLastError() ==
276 NACL_SRPC_RESULT_APP_ERROR
) {
277 // The error string is only present if the error was sent back from llc.
278 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
279 params
.outs()[3]->arrays
.str
);
281 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
282 "Compile StreamEnd internal error");
286 compile_time_
= NaClGetTimeOfDayMicroseconds() - compile_start_time
;
287 GetNaClInterface()->LogTranslateTime("NaCl.Perf.PNaClLoadTime.CompileTime",
290 // Shut down the llc subprocess.
291 NaClXMutexLock(&subprocess_mu_
);
292 llc_subprocess_active_
= false;
293 llc_subprocess_
.reset(NULL
);
294 NaClXMutexUnlock(&subprocess_mu_
);
296 if(!RunLdSubprocess()) {
299 core
->CallOnMainThread(0, report_translate_finished_
, PP_OK
);
302 bool PnaclTranslateThread::RunLdSubprocess() {
303 ErrorInfo error_info
;
306 std::vector
<nacl::DescWrapper
*> ld_in_files
;
308 for (i
= 0; i
< obj_files_
->size(); i
++) {
309 // Reset object file for reading first.
310 if (!(*obj_files_
)[i
]->Reset()) {
311 TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP
,
312 "Link process could not reset object file");
315 ld_in_files
.push_back((*obj_files_
)[i
]->read_wrapper());
317 for (; i
< PnaclCoordinator::kMaxTranslatorObjectFiles
; i
++)
318 ld_in_files
.push_back(invalid_desc_wrapper_
);
320 nacl::DescWrapper
* ld_out_file
= nexe_file_
->write_wrapper();
321 int64_t ld_start_time
= NaClGetTimeOfDayMicroseconds();
322 PP_NaClFileInfo ld_file_info
= resources_
->TakeLdFileInfo();
323 // On success, ownership of ld_file_info is transferred.
324 nacl::scoped_ptr
<NaClSubprocess
> ld_subprocess(
325 plugin_
->LoadHelperNaClModule(resources_
->GetLdUrl(),
328 if (ld_subprocess
.get() == NULL
) {
329 if (ld_file_info
.handle
!= PP_kInvalidFileHandle
)
330 CloseFileHandle(ld_file_info
.handle
);
331 TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP
,
332 "Link process could not be created: " +
333 error_info
.message());
336 GetNaClInterface()->LogTranslateTime(
337 "NaCl.Perf.PNaClLoadTime.LoadLinker",
338 NaClGetTimeOfDayMicroseconds() - ld_start_time
);
340 nacl::MutexLocker
ml(&subprocess_mu_
);
341 // If we received a call to AbortSubprocesses() before we had a chance to
342 // set llc_subprocess_, shut down and clean up the subprocess started here.
343 if (subprocesses_aborted_
) {
344 ld_subprocess
->service_runtime()->Shutdown();
347 DCHECK(ld_subprocess_
.get() == NULL
);
348 ld_subprocess_
.swap(ld_subprocess
);
349 ld_subprocess_active_
= true;
352 int64_t link_start_time
= NaClGetTimeOfDayMicroseconds();
354 bool success
= ld_subprocess_
->InvokeSrpcMethod(
356 "ihhhhhhhhhhhhhhhhh",
358 static_cast<int>(obj_files_
->size()),
359 ld_in_files
[0]->desc(),
360 ld_in_files
[1]->desc(),
361 ld_in_files
[2]->desc(),
362 ld_in_files
[3]->desc(),
363 ld_in_files
[4]->desc(),
364 ld_in_files
[5]->desc(),
365 ld_in_files
[6]->desc(),
366 ld_in_files
[7]->desc(),
367 ld_in_files
[8]->desc(),
368 ld_in_files
[9]->desc(),
369 ld_in_files
[10]->desc(),
370 ld_in_files
[11]->desc(),
371 ld_in_files
[12]->desc(),
372 ld_in_files
[13]->desc(),
373 ld_in_files
[14]->desc(),
374 ld_in_files
[15]->desc(),
375 ld_out_file
->desc());
377 TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL
,
381 GetNaClInterface()->LogTranslateTime(
382 "NaCl.Perf.PNaClLoadTime.LinkTime",
383 NaClGetTimeOfDayMicroseconds() - link_start_time
);
384 PLUGIN_PRINTF(("PnaclCoordinator: link (translator=%p) succeeded\n",
386 // Shut down the ld subprocess.
387 NaClXMutexLock(&subprocess_mu_
);
388 ld_subprocess_active_
= false;
389 ld_subprocess_
.reset(NULL
);
390 NaClXMutexUnlock(&subprocess_mu_
);
394 void PnaclTranslateThread::TranslateFailed(
395 PP_NaClError err_code
,
396 const nacl::string
& error_string
) {
397 PLUGIN_PRINTF(("PnaclTranslateThread::TranslateFailed (error_string='%s')\n",
398 error_string
.c_str()));
399 pp::Core
* core
= pp::Module::Get()->core();
400 if (coordinator_error_info_
->message().empty()) {
401 // Only use our message if one hasn't already been set by the coordinator
402 // (e.g. pexe load failed).
403 coordinator_error_info_
->SetReport(err_code
,
404 nacl::string("PnaclCoordinator: ") +
407 core
->CallOnMainThread(0, report_translate_finished_
, PP_ERROR_FAILED
);
410 void PnaclTranslateThread::AbortSubprocesses() {
411 PLUGIN_PRINTF(("PnaclTranslateThread::AbortSubprocesses\n"));
412 NaClXMutexLock(&subprocess_mu_
);
413 if (llc_subprocess_
!= NULL
&& llc_subprocess_active_
) {
414 llc_subprocess_
->service_runtime()->Shutdown();
415 llc_subprocess_active_
= false;
417 if (ld_subprocess_
!= NULL
&& ld_subprocess_active_
) {
418 ld_subprocess_
->service_runtime()->Shutdown();
419 ld_subprocess_active_
= false;
421 subprocesses_aborted_
= true;
422 NaClXMutexUnlock(&subprocess_mu_
);
423 nacl::MutexLocker
ml(&cond_mu_
);
425 // Free all buffered bitcode chunks
426 data_buffers_
.clear();
427 NaClXCondVarSignal(&buffer_cond_
);
430 PnaclTranslateThread::~PnaclTranslateThread() {
431 PLUGIN_PRINTF(("~PnaclTranslateThread (translate_thread=%p)\n", this));
433 if (translate_thread_
!= NULL
)
434 NaClThreadJoin(translate_thread_
.get());
435 PLUGIN_PRINTF(("~PnaclTranslateThread joined\n"));
436 NaClCondVarDtor(&buffer_cond_
);
437 NaClMutexDtor(&cond_mu_
);
438 NaClMutexDtor(&subprocess_mu_
);
441 } // namespace plugin