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_translate_thread.h"
10 #include "components/nacl/renderer/plugin/plugin.h"
11 #include "components/nacl/renderer/plugin/plugin_error.h"
12 #include "components/nacl/renderer/plugin/srpc_params.h"
13 #include "components/nacl/renderer/plugin/temporary_file.h"
14 #include "components/nacl/renderer/plugin/utility.h"
15 #include "native_client/src/shared/platform/nacl_check.h"
16 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
17 #include "ppapi/cpp/var.h"
22 template <typename Val
>
23 std::string
MakeCommandLineArg(const char* key
, const Val val
) {
29 void GetLlcCommandLine(std::vector
<char>* split_args
,
30 size_t obj_files_size
,
33 const std::string
& architecture_attributes
) {
34 typedef std::vector
<std::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 (const std::string
& arg
: args
) {
48 std::copy(arg
.begin(), arg
.end(), std::back_inserter(*split_args
));
49 split_args
->push_back('\x00');
53 void GetSubzeroCommandLine(std::vector
<char>* split_args
,
56 const std::string
& architecture_attributes
) {
57 typedef std::vector
<std::string
> Args
;
60 args
.push_back(MakeCommandLineArg("-O", opt_level
));
62 // TODO(stichnot): enable this once the mattr flag formatting is
63 // compatible: https://code.google.com/p/nativeclient/issues/detail?id=4132
64 // if (!architecture_attributes.empty())
65 // args.push_back("-mattr=" + architecture_attributes);
67 for (const std::string
& arg
: args
) {
68 std::copy(arg
.begin(), arg
.end(), std::back_inserter(*split_args
));
69 split_args
->push_back('\x00');
75 PnaclTranslateThread::PnaclTranslateThread()
76 : compiler_subprocess_(NULL
),
78 compiler_subprocess_active_(false),
79 ld_subprocess_active_(false),
85 coordinator_error_info_(NULL
),
87 NaClXMutexCtor(&subprocess_mu_
);
88 NaClXMutexCtor(&cond_mu_
);
89 NaClXCondVarCtor(&buffer_cond_
);
92 void PnaclTranslateThread::SetupState(
93 const pp::CompletionCallback
& finish_callback
,
94 NaClSubprocess
* compiler_subprocess
,
95 NaClSubprocess
* ld_subprocess
,
96 const std::vector
<TempFile
*>* obj_files
,
99 nacl::DescWrapper
* invalid_desc_wrapper
,
100 ErrorInfo
* error_info
,
101 PP_PNaClOptions
* pnacl_options
,
102 const std::string
& architecture_attributes
,
103 PnaclCoordinator
* coordinator
) {
104 PLUGIN_PRINTF(("PnaclTranslateThread::SetupState)\n"));
105 compiler_subprocess_
= compiler_subprocess
;
106 ld_subprocess_
= ld_subprocess
;
107 obj_files_
= obj_files
;
108 num_threads_
= num_threads
;
109 nexe_file_
= nexe_file
;
110 invalid_desc_wrapper_
= invalid_desc_wrapper
;
111 coordinator_error_info_
= error_info
;
112 pnacl_options_
= pnacl_options
;
113 architecture_attributes_
= architecture_attributes
;
114 coordinator_
= coordinator
;
116 report_translate_finished_
= finish_callback
;
119 void PnaclTranslateThread::RunCompile(
120 const pp::CompletionCallback
& compile_finished_callback
) {
121 PLUGIN_PRINTF(("PnaclTranslateThread::RunCompile)\n"));
123 DCHECK(compiler_subprocess_
->service_runtime());
124 compiler_subprocess_active_
= true;
126 compile_finished_callback_
= compile_finished_callback
;
127 translate_thread_
.reset(new NaClThread
);
128 if (translate_thread_
== NULL
) {
129 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE
,
130 "could not allocate thread struct.");
133 const int32_t kArbitraryStackSize
= 128 * 1024;
134 if (!NaClThreadCreateJoinable(translate_thread_
.get(), DoCompileThread
, this,
135 kArbitraryStackSize
)) {
136 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE
,
137 "could not create thread.");
138 translate_thread_
.reset(NULL
);
142 void PnaclTranslateThread::RunLink() {
143 PLUGIN_PRINTF(("PnaclTranslateThread::RunLink)\n"));
145 DCHECK(ld_subprocess_
->service_runtime());
146 ld_subprocess_active_
= true;
148 // Tear down the previous thread.
149 // TODO(jvoung): Use base/threading or something where we can have a
150 // persistent thread and easily post tasks to that persistent thread.
151 NaClThreadJoin(translate_thread_
.get());
152 translate_thread_
.reset(new NaClThread
);
153 if (translate_thread_
== NULL
) {
154 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE
,
155 "could not allocate thread struct.");
158 const int32_t kArbitraryStackSize
= 128 * 1024;
159 if (!NaClThreadCreateJoinable(translate_thread_
.get(), DoLinkThread
, this,
160 kArbitraryStackSize
)) {
161 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE
,
162 "could not create thread.");
163 translate_thread_
.reset(NULL
);
167 // Called from main thread to send bytes to the translator.
168 void PnaclTranslateThread::PutBytes(const void* bytes
, int32_t count
) {
169 CHECK(bytes
!= NULL
);
170 NaClXMutexLock(&cond_mu_
);
171 data_buffers_
.push_back(std::vector
<char>());
172 data_buffers_
.back().insert(data_buffers_
.back().end(),
173 static_cast<const char*>(bytes
),
174 static_cast<const char*>(bytes
) + count
);
175 NaClXCondVarSignal(&buffer_cond_
);
176 NaClXMutexUnlock(&cond_mu_
);
179 void PnaclTranslateThread::EndStream() {
180 NaClXMutexLock(&cond_mu_
);
182 NaClXCondVarSignal(&buffer_cond_
);
183 NaClXMutexUnlock(&cond_mu_
);
186 void WINAPI
PnaclTranslateThread::DoCompileThread(void* arg
) {
187 PnaclTranslateThread
* translator
=
188 reinterpret_cast<PnaclTranslateThread
*>(arg
);
189 translator
->DoCompile();
192 void PnaclTranslateThread::DoCompile() {
194 nacl::MutexLocker
ml(&subprocess_mu_
);
195 // If the main thread asked us to exit in between starting the thread
196 // and now, just leave now.
197 if (!compiler_subprocess_active_
)
199 // Now that we are in helper thread, we can do the the blocking
200 // StartSrpcServices operation.
201 if (!compiler_subprocess_
->StartSrpcServices()) {
203 PP_NACL_ERROR_SRPC_CONNECTION_FAIL
,
204 "SRPC connection failure for " + compiler_subprocess_
->description());
210 std::vector
<nacl::DescWrapper
*> compile_out_files
;
212 for (i
= 0; i
< obj_files_
->size(); i
++)
213 compile_out_files
.push_back((*obj_files_
)[i
]->write_wrapper());
214 for (; i
< PnaclCoordinator::kMaxTranslatorObjectFiles
; i
++)
215 compile_out_files
.push_back(invalid_desc_wrapper_
);
217 PLUGIN_PRINTF(("DoCompile using subzero: %d\n", pnacl_options_
->use_subzero
));
219 pp::Core
* core
= pp::Module::Get()->core();
220 int64_t do_compile_start_time
= NaClGetTimeOfDayMicroseconds();
223 std::vector
<char> split_args
;
224 if (pnacl_options_
->use_subzero
) {
225 GetSubzeroCommandLine(&split_args
, pnacl_options_
->opt_level
,
226 pnacl_options_
->is_debug
, architecture_attributes_
);
228 GetLlcCommandLine(&split_args
, obj_files_
->size(),
229 pnacl_options_
->opt_level
, pnacl_options_
->is_debug
,
230 architecture_attributes_
);
233 init_success
= compiler_subprocess_
->InvokeSrpcMethod(
234 "StreamInitWithSplit", "ihhhhhhhhhhhhhhhhC", ¶ms
, num_threads_
,
235 compile_out_files
[0]->desc(), compile_out_files
[1]->desc(),
236 compile_out_files
[2]->desc(), compile_out_files
[3]->desc(),
237 compile_out_files
[4]->desc(), compile_out_files
[5]->desc(),
238 compile_out_files
[6]->desc(), compile_out_files
[7]->desc(),
239 compile_out_files
[8]->desc(), compile_out_files
[9]->desc(),
240 compile_out_files
[10]->desc(), compile_out_files
[11]->desc(),
241 compile_out_files
[12]->desc(), compile_out_files
[13]->desc(),
242 compile_out_files
[14]->desc(), compile_out_files
[15]->desc(),
243 &split_args
[0], split_args
.size());
245 if (compiler_subprocess_
->srpc_client()->GetLastError() ==
246 NACL_SRPC_RESULT_APP_ERROR
) {
247 // The error message is only present if the error was returned from llc
248 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
249 std::string("Stream init failed: ") +
250 std::string(params
.outs()[0]->arrays
.str
));
252 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
253 "Stream init internal error");
257 PLUGIN_PRINTF(("PnaclCoordinator: StreamInit successful\n"));
259 // llc process is started.
260 while(!done_
|| data_buffers_
.size() > 0) {
261 NaClXMutexLock(&cond_mu_
);
262 while(!done_
&& data_buffers_
.size() == 0) {
263 NaClXCondVarWait(&buffer_cond_
, &cond_mu_
);
265 PLUGIN_PRINTF(("PnaclTranslateThread awake (done=%d, size=%" NACL_PRIuS
267 done_
, data_buffers_
.size()));
268 if (data_buffers_
.size() > 0) {
269 std::vector
<char> data
;
270 data
.swap(data_buffers_
.front());
271 data_buffers_
.pop_front();
272 NaClXMutexUnlock(&cond_mu_
);
273 PLUGIN_PRINTF(("StreamChunk\n"));
274 if (!compiler_subprocess_
->InvokeSrpcMethod("StreamChunk", "C", ¶ms
,
275 &data
[0], data
.size())) {
276 if (compiler_subprocess_
->srpc_client()->GetLastError() !=
277 NACL_SRPC_RESULT_APP_ERROR
) {
278 // If the error was reported by the translator, then we fall through
279 // and call StreamEnd, which returns a string describing the error,
280 // which we can then send to the Javascript console. Otherwise just
281 // fail here, since the translator has probably crashed or asserted.
282 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
283 "Compile stream chunk failed. "
284 "The PNaCl translator has probably crashed.");
289 PLUGIN_PRINTF(("StreamChunk Successful\n"));
290 core
->CallOnMainThread(
292 coordinator_
->GetCompileProgressCallback(data
.size()),
296 NaClXMutexUnlock(&cond_mu_
);
299 PLUGIN_PRINTF(("PnaclTranslateThread done with chunks\n"));
301 if (!compiler_subprocess_
->InvokeSrpcMethod("StreamEnd", std::string(),
303 PLUGIN_PRINTF(("PnaclTranslateThread StreamEnd failed\n"));
304 if (compiler_subprocess_
->srpc_client()->GetLastError() ==
305 NACL_SRPC_RESULT_APP_ERROR
) {
306 // The error string is only present if the error was sent back from llc.
307 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
308 params
.outs()[3]->arrays
.str
);
310 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL
,
311 "Compile StreamEnd internal error");
315 compile_time_
= NaClGetTimeOfDayMicroseconds() - do_compile_start_time
;
316 GetNaClInterface()->LogTranslateTime("NaCl.Perf.PNaClLoadTime.CompileTime",
318 GetNaClInterface()->LogTranslateTime(
319 pnacl_options_
->use_subzero
320 ? "NaCl.Perf.PNaClLoadTime.CompileTime.Subzero"
321 : "NaCl.Perf.PNaClLoadTime.CompileTime.LLC",
324 // Shut down the compiler subprocess.
325 NaClXMutexLock(&subprocess_mu_
);
326 compiler_subprocess_active_
= false;
327 compiler_subprocess_
->Shutdown();
328 NaClXMutexUnlock(&subprocess_mu_
);
330 core
->CallOnMainThread(0, compile_finished_callback_
, PP_OK
);
333 void WINAPI
PnaclTranslateThread::DoLinkThread(void* arg
) {
334 PnaclTranslateThread
* translator
=
335 reinterpret_cast<PnaclTranslateThread
*>(arg
);
336 translator
->DoLink();
339 void PnaclTranslateThread::DoLink() {
341 nacl::MutexLocker
ml(&subprocess_mu_
);
342 // If the main thread asked us to exit in between starting the thread
343 // and now, just leave now.
344 if (!ld_subprocess_active_
)
346 // Now that we are in helper thread, we can do the the blocking
347 // StartSrpcServices operation.
348 if (!ld_subprocess_
->StartSrpcServices()) {
350 PP_NACL_ERROR_SRPC_CONNECTION_FAIL
,
351 "SRPC connection failure for " + ld_subprocess_
->description());
357 std::vector
<nacl::DescWrapper
*> ld_in_files
;
359 for (i
= 0; i
< obj_files_
->size(); i
++) {
360 // Reset object file for reading first.
361 if (!(*obj_files_
)[i
]->Reset()) {
362 TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP
,
363 "Link process could not reset object file");
365 ld_in_files
.push_back((*obj_files_
)[i
]->read_wrapper());
367 for (; i
< PnaclCoordinator::kMaxTranslatorObjectFiles
; i
++)
368 ld_in_files
.push_back(invalid_desc_wrapper_
);
370 nacl::DescWrapper
* ld_out_file
= nexe_file_
->write_wrapper();
371 int64_t link_start_time
= NaClGetTimeOfDayMicroseconds();
373 bool success
= ld_subprocess_
->InvokeSrpcMethod(
375 "ihhhhhhhhhhhhhhhhh",
377 static_cast<int>(obj_files_
->size()),
378 ld_in_files
[0]->desc(),
379 ld_in_files
[1]->desc(),
380 ld_in_files
[2]->desc(),
381 ld_in_files
[3]->desc(),
382 ld_in_files
[4]->desc(),
383 ld_in_files
[5]->desc(),
384 ld_in_files
[6]->desc(),
385 ld_in_files
[7]->desc(),
386 ld_in_files
[8]->desc(),
387 ld_in_files
[9]->desc(),
388 ld_in_files
[10]->desc(),
389 ld_in_files
[11]->desc(),
390 ld_in_files
[12]->desc(),
391 ld_in_files
[13]->desc(),
392 ld_in_files
[14]->desc(),
393 ld_in_files
[15]->desc(),
394 ld_out_file
->desc());
396 TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL
,
400 GetNaClInterface()->LogTranslateTime(
401 "NaCl.Perf.PNaClLoadTime.LinkTime",
402 NaClGetTimeOfDayMicroseconds() - link_start_time
);
403 PLUGIN_PRINTF(("PnaclCoordinator: link (translator=%p) succeeded\n",
406 // Shut down the ld subprocess.
407 NaClXMutexLock(&subprocess_mu_
);
408 ld_subprocess_active_
= false;
409 ld_subprocess_
->Shutdown();
410 NaClXMutexUnlock(&subprocess_mu_
);
412 pp::Core
* core
= pp::Module::Get()->core();
413 core
->CallOnMainThread(0, report_translate_finished_
, PP_OK
);
416 void PnaclTranslateThread::TranslateFailed(
417 PP_NaClError err_code
,
418 const std::string
& error_string
) {
419 PLUGIN_PRINTF(("PnaclTranslateThread::TranslateFailed (error_string='%s')\n",
420 error_string
.c_str()));
421 pp::Core
* core
= pp::Module::Get()->core();
422 if (coordinator_error_info_
->message().empty()) {
423 // Only use our message if one hasn't already been set by the coordinator
424 // (e.g. pexe load failed).
425 coordinator_error_info_
->SetReport(err_code
,
426 std::string("PnaclCoordinator: ") +
429 core
->CallOnMainThread(0, report_translate_finished_
, PP_ERROR_FAILED
);
432 void PnaclTranslateThread::AbortSubprocesses() {
433 PLUGIN_PRINTF(("PnaclTranslateThread::AbortSubprocesses\n"));
434 NaClXMutexLock(&subprocess_mu_
);
435 if (compiler_subprocess_
!= NULL
&& compiler_subprocess_active_
) {
436 // We only run the service_runtime's Shutdown and do not run the
437 // NaClSubprocess Shutdown, which would otherwise nullify some
438 // pointers that could still be in use (srpc_client, etc.).
439 compiler_subprocess_
->service_runtime()->Shutdown();
440 compiler_subprocess_active_
= false;
442 if (ld_subprocess_
!= NULL
&& ld_subprocess_active_
) {
443 ld_subprocess_
->service_runtime()->Shutdown();
444 ld_subprocess_active_
= false;
446 NaClXMutexUnlock(&subprocess_mu_
);
447 nacl::MutexLocker
ml(&cond_mu_
);
449 // Free all buffered bitcode chunks
450 data_buffers_
.clear();
451 NaClXCondVarSignal(&buffer_cond_
);
454 PnaclTranslateThread::~PnaclTranslateThread() {
455 PLUGIN_PRINTF(("~PnaclTranslateThread (translate_thread=%p)\n", this));
457 if (translate_thread_
!= NULL
)
458 NaClThreadJoin(translate_thread_
.get());
459 PLUGIN_PRINTF(("~PnaclTranslateThread joined\n"));
460 NaClCondVarDtor(&buffer_cond_
);
461 NaClMutexDtor(&cond_mu_
);
462 NaClMutexDtor(&subprocess_mu_
);
465 } // namespace plugin