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.
4
5 #include "ppapi/native_client/src/trusted/plugin/pnacl_translate_thread.h"
6
7 #include <iterator>
8
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"
17
18 namespace plugin {
19 namespace {
20
21 template <typename Val>
MakeCommandLineArg(const char * key,const Val val)22 nacl::string MakeCommandLineArg(const char* key, const Val val) {
23 nacl::stringstream ss;
24 ss << key << val;
25 return ss.str();
26 }
27
GetLlcCommandLine(Plugin * plugin,std::vector<char> * split_args,size_t obj_files_size,int32_t opt_level,bool is_debug,const nacl::string & architecture_attributes)28 void GetLlcCommandLine(Plugin* plugin,
29 std::vector<char>* split_args,
30 size_t obj_files_size,
31 int32_t opt_level,
32 bool is_debug,
33 const nacl::string &architecture_attributes) {
34 typedef std::vector<nacl::string> Args;
35 Args args;
36
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));
42 if (is_debug)
43 args.push_back("-bitcode-format=llvm");
44 if (!architecture_attributes.empty())
45 args.push_back("-mattr=" + architecture_attributes);
46
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');
50 }
51 }
52
53 } // namespace
54
PnaclTranslateThread()55 PnaclTranslateThread::PnaclTranslateThread() : llc_subprocess_active_(false),
56 ld_subprocess_active_(false),
57 subprocesses_aborted_(false),
58 done_(false),
59 compile_time_(0),
60 obj_files_(NULL),
61 nexe_file_(NULL),
62 coordinator_error_info_(NULL),
63 resources_(NULL),
64 coordinator_(NULL),
65 plugin_(NULL) {
66 NaClXMutexCtor(&subprocess_mu_);
67 NaClXMutexCtor(&cond_mu_);
68 NaClXCondVarCtor(&buffer_cond_);
69 }
70
RunTranslate(const pp::CompletionCallback & finish_callback,const std::vector<TempFile * > * obj_files,TempFile * nexe_file,nacl::DescWrapper * invalid_desc_wrapper,ErrorInfo * error_info,PnaclResources * resources,PP_PNaClOptions * pnacl_options,const nacl::string & architecture_attributes,PnaclCoordinator * coordinator,Plugin * plugin)71 void PnaclTranslateThread::RunTranslate(
72 const pp::CompletionCallback& finish_callback,
73 const std::vector<TempFile*>* obj_files,
74 TempFile* nexe_file,
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,
81 Plugin* plugin) {
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;
91 plugin_ = plugin;
92
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.");
100 return;
101 }
102 const int32_t kArbitraryStackSize = 128 * 1024;
103 if (!NaClThreadCreateJoinable(translate_thread_.get(),
104 DoTranslateThread,
105 this,
106 kArbitraryStackSize)) {
107 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE,
108 "could not create thread.");
109 translate_thread_.reset(NULL);
110 }
111 }
112
113 // Called from main thread to send bytes to the translator.
PutBytes(std::vector<char> * bytes,int count)114 void PnaclTranslateThread::PutBytes(std::vector<char>* bytes,
115 int count) {
116 PLUGIN_PRINTF(("PutBytes (this=%p, bytes=%p, size=%" NACL_PRIuS
117 ", count=%d)\n",
118 this, bytes, bytes ? bytes->size() : 0, count));
119 size_t buffer_size = 0;
120 // If we are done (error or not), Signal the translation thread to stop.
121 if (count <= PP_OK) {
122 NaClXMutexLock(&cond_mu_);
123 done_ = true;
124 NaClXCondVarSignal(&buffer_cond_);
125 NaClXMutexUnlock(&cond_mu_);
126 return;
127 }
128
129 CHECK(bytes != NULL);
130 // Ensure that the buffer we send to the translation thread is the right size
131 // (count can be < the buffer size). This can be done without the lock.
132 buffer_size = bytes->size();
133 bytes->resize(count);
134
135 NaClXMutexLock(&cond_mu_);
136
137 data_buffers_.push_back(std::vector<char>());
138 bytes->swap(data_buffers_.back()); // Avoid copying the buffer data.
139
140 NaClXCondVarSignal(&buffer_cond_);
141 NaClXMutexUnlock(&cond_mu_);
142
143 // Ensure the buffer we send back to the coordinator is the expected size
144 bytes->resize(buffer_size);
145 }
146
DoTranslateThread(void * arg)147 void WINAPI PnaclTranslateThread::DoTranslateThread(void* arg) {
148 PnaclTranslateThread* translator =
149 reinterpret_cast<PnaclTranslateThread*>(arg);
150 translator->DoTranslate();
151 }
152
DoTranslate()153 void PnaclTranslateThread::DoTranslate() {
154 ErrorInfo error_info;
155 SrpcParams params;
156 std::vector<nacl::DescWrapper*> llc_out_files;
157 size_t i;
158 for (i = 0; i < obj_files_->size(); i++)
159 llc_out_files.push_back((*obj_files_)[i]->write_wrapper());
160 for (; i < PnaclCoordinator::kMaxTranslatorObjectFiles; i++)
161 llc_out_files.push_back(invalid_desc_wrapper_);
162
163 pp::Core* core = pp::Module::Get()->core();
164 int64_t llc_start_time = NaClGetTimeOfDayMicroseconds();
165 PP_FileHandle llc_file_handle = resources_->TakeLlcFileHandle();
166 // On success, ownership of llc_file_handle is transferred.
167 NaClSubprocess* llc_subprocess = plugin_->LoadHelperNaClModule(
168 resources_->GetLlcUrl(), llc_file_handle, &error_info);
169 if (llc_subprocess == NULL) {
170 if (llc_file_handle != PP_kInvalidFileHandle)
171 CloseFileHandle(llc_file_handle);
172 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_SETUP,
173 "Compile process could not be created: " +
174 error_info.message());
175 return;
176 }
177 GetNaClInterface()->LogTranslateTime(
178 "NaCl.Perf.PNaClLoadTime.LoadCompiler",
179 NaClGetTimeOfDayMicroseconds() - llc_start_time);
180
181 {
182 nacl::MutexLocker ml(&subprocess_mu_);
183 // If we received a call to AbortSubprocesses() before we had a chance to
184 // set llc_subprocess_, shut down and clean up the subprocess started here.
185 if (subprocesses_aborted_) {
186 llc_subprocess->service_runtime()->Shutdown();
187 delete llc_subprocess;
188 return;
189 }
190 llc_subprocess_.reset(llc_subprocess);
191 llc_subprocess = NULL;
192 llc_subprocess_active_ = true;
193 }
194
195 int64_t compile_start_time = NaClGetTimeOfDayMicroseconds();
196 bool init_success;
197
198 std::vector<char> split_args;
199 GetLlcCommandLine(plugin_,
200 &split_args,
201 obj_files_->size(),
202 pnacl_options_->opt_level,
203 pnacl_options_->is_debug,
204 architecture_attributes_);
205 init_success = llc_subprocess_->InvokeSrpcMethod(
206 "StreamInitWithSplit",
207 "ihhhhhhhhhhhhhhhhC",
208 ¶ms,
209 static_cast<int>(obj_files_->size()),
210 llc_out_files[0]->desc(),
211 llc_out_files[1]->desc(),
212 llc_out_files[2]->desc(),
213 llc_out_files[3]->desc(),
214 llc_out_files[4]->desc(),
215 llc_out_files[5]->desc(),
216 llc_out_files[6]->desc(),
217 llc_out_files[7]->desc(),
218 llc_out_files[8]->desc(),
219 llc_out_files[9]->desc(),
220 llc_out_files[10]->desc(),
221 llc_out_files[11]->desc(),
222 llc_out_files[12]->desc(),
223 llc_out_files[13]->desc(),
224 llc_out_files[14]->desc(),
225 llc_out_files[15]->desc(),
226 &split_args[0],
227 split_args.size());
228 if (!init_success) {
229 if (llc_subprocess_->srpc_client()->GetLastError() ==
230 NACL_SRPC_RESULT_APP_ERROR) {
231 // The error message is only present if the error was returned from llc
232 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
233 nacl::string("Stream init failed: ") +
234 nacl::string(params.outs()[0]->arrays.str));
235 } else {
236 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
237 "Stream init internal error");
238 }
239 return;
240 }
241 PLUGIN_PRINTF(("PnaclCoordinator: StreamInit successful\n"));
242
243 // llc process is started.
244 while(!done_ || data_buffers_.size() > 0) {
245 NaClXMutexLock(&cond_mu_);
246 while(!done_ && data_buffers_.size() == 0) {
247 NaClXCondVarWait(&buffer_cond_, &cond_mu_);
248 }
249 PLUGIN_PRINTF(("PnaclTranslateThread awake (done=%d, size=%" NACL_PRIuS
250 ")\n",
251 done_, data_buffers_.size()));
252 if (data_buffers_.size() > 0) {
253 std::vector<char> data;
254 data.swap(data_buffers_.front());
255 data_buffers_.pop_front();
256 NaClXMutexUnlock(&cond_mu_);
257 PLUGIN_PRINTF(("StreamChunk\n"));
258 if (!llc_subprocess_->InvokeSrpcMethod("StreamChunk",
259 "C",
260 ¶ms,
261 &data[0],
262 data.size())) {
263 if (llc_subprocess_->srpc_client()->GetLastError() !=
264 NACL_SRPC_RESULT_APP_ERROR) {
265 // If the error was reported by the translator, then we fall through
266 // and call StreamEnd, which returns a string describing the error,
267 // which we can then send to the Javascript console. Otherwise just
268 // fail here, since the translator has probably crashed or asserted.
269 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
270 "Compile stream chunk failed. "
271 "The PNaCl translator has probably crashed.");
272 return;
273 }
274 break;
275 } else {
276 PLUGIN_PRINTF(("StreamChunk Successful\n"));
277 core->CallOnMainThread(
278 0,
279 coordinator_->GetCompileProgressCallback(data.size()),
280 PP_OK);
281 }
282 } else {
283 NaClXMutexUnlock(&cond_mu_);
284 }
285 }
286 PLUGIN_PRINTF(("PnaclTranslateThread done with chunks\n"));
287 // Finish llc.
288 if (!llc_subprocess_->InvokeSrpcMethod("StreamEnd", std::string(), ¶ms)) {
289 PLUGIN_PRINTF(("PnaclTranslateThread StreamEnd failed\n"));
290 if (llc_subprocess_->srpc_client()->GetLastError() ==
291 NACL_SRPC_RESULT_APP_ERROR) {
292 // The error string is only present if the error was sent back from llc.
293 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
294 params.outs()[3]->arrays.str);
295 } else {
296 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
297 "Compile StreamEnd internal error");
298 }
299 return;
300 }
301 compile_time_ = NaClGetTimeOfDayMicroseconds() - compile_start_time;
302 GetNaClInterface()->LogTranslateTime("NaCl.Perf.PNaClLoadTime.CompileTime",
303 compile_time_);
304
305 // Shut down the llc subprocess.
306 NaClXMutexLock(&subprocess_mu_);
307 llc_subprocess_active_ = false;
308 llc_subprocess_.reset(NULL);
309 NaClXMutexUnlock(&subprocess_mu_);
310
311 if(!RunLdSubprocess()) {
312 return;
313 }
314 core->CallOnMainThread(0, report_translate_finished_, PP_OK);
315 }
316
RunLdSubprocess()317 bool PnaclTranslateThread::RunLdSubprocess() {
318 ErrorInfo error_info;
319 SrpcParams params;
320
321 std::vector<nacl::DescWrapper*> ld_in_files;
322 size_t i;
323 for (i = 0; i < obj_files_->size(); i++) {
324 // Reset object file for reading first.
325 if (!(*obj_files_)[i]->Reset()) {
326 TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP,
327 "Link process could not reset object file");
328 return false;
329 }
330 ld_in_files.push_back((*obj_files_)[i]->read_wrapper());
331 }
332 for (; i < PnaclCoordinator::kMaxTranslatorObjectFiles; i++)
333 ld_in_files.push_back(invalid_desc_wrapper_);
334
335 nacl::DescWrapper* ld_out_file = nexe_file_->write_wrapper();
336 int64_t ld_start_time = NaClGetTimeOfDayMicroseconds();
337 PP_FileHandle ld_file_handle = resources_->TakeLdFileHandle();
338 // On success, ownership of ld_file_handle is transferred.
339 nacl::scoped_ptr<NaClSubprocess> ld_subprocess(
340 plugin_->LoadHelperNaClModule(resources_->GetLlcUrl(),
341 ld_file_handle,
342 &error_info));
343 if (ld_subprocess.get() == NULL) {
344 if (ld_file_handle != PP_kInvalidFileHandle)
345 CloseFileHandle(ld_file_handle);
346 TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP,
347 "Link process could not be created: " +
348 error_info.message());
349 return false;
350 }
351 GetNaClInterface()->LogTranslateTime(
352 "NaCl.Perf.PNaClLoadTime.LoadLinker",
353 NaClGetTimeOfDayMicroseconds() - ld_start_time);
354 {
355 nacl::MutexLocker ml(&subprocess_mu_);
356 // If we received a call to AbortSubprocesses() before we had a chance to
357 // set llc_subprocess_, shut down and clean up the subprocess started here.
358 if (subprocesses_aborted_) {
359 ld_subprocess->service_runtime()->Shutdown();
360 return false;
361 }
362 DCHECK(ld_subprocess_.get() == NULL);
363 ld_subprocess_.swap(ld_subprocess);
364 ld_subprocess_active_ = true;
365 }
366
367 int64_t link_start_time = NaClGetTimeOfDayMicroseconds();
368 // Run LD.
369 bool success = ld_subprocess_->InvokeSrpcMethod(
370 "RunWithSplit",
371 "ihhhhhhhhhhhhhhhhh",
372 ¶ms,
373 static_cast<int>(obj_files_->size()),
374 ld_in_files[0]->desc(),
375 ld_in_files[1]->desc(),
376 ld_in_files[2]->desc(),
377 ld_in_files[3]->desc(),
378 ld_in_files[4]->desc(),
379 ld_in_files[5]->desc(),
380 ld_in_files[6]->desc(),
381 ld_in_files[7]->desc(),
382 ld_in_files[8]->desc(),
383 ld_in_files[9]->desc(),
384 ld_in_files[10]->desc(),
385 ld_in_files[11]->desc(),
386 ld_in_files[12]->desc(),
387 ld_in_files[13]->desc(),
388 ld_in_files[14]->desc(),
389 ld_in_files[15]->desc(),
390 ld_out_file->desc());
391 if (!success) {
392 TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL,
393 "link failed.");
394 return false;
395 }
396 GetNaClInterface()->LogTranslateTime(
397 "NaCl.Perf.PNaClLoadTime.LinkTime",
398 NaClGetTimeOfDayMicroseconds() - link_start_time);
399 PLUGIN_PRINTF(("PnaclCoordinator: link (translator=%p) succeeded\n",
400 this));
401 // Shut down the ld subprocess.
402 NaClXMutexLock(&subprocess_mu_);
403 ld_subprocess_active_ = false;
404 ld_subprocess_.reset(NULL);
405 NaClXMutexUnlock(&subprocess_mu_);
406 return true;
407 }
408
TranslateFailed(PP_NaClError err_code,const nacl::string & error_string)409 void PnaclTranslateThread::TranslateFailed(
410 PP_NaClError err_code,
411 const nacl::string& error_string) {
412 PLUGIN_PRINTF(("PnaclTranslateThread::TranslateFailed (error_string='%s')\n",
413 error_string.c_str()));
414 pp::Core* core = pp::Module::Get()->core();
415 if (coordinator_error_info_->message().empty()) {
416 // Only use our message if one hasn't already been set by the coordinator
417 // (e.g. pexe load failed).
418 coordinator_error_info_->SetReport(err_code,
419 nacl::string("PnaclCoordinator: ") +
420 error_string);
421 }
422 core->CallOnMainThread(0, report_translate_finished_, PP_ERROR_FAILED);
423 }
424
AbortSubprocesses()425 void PnaclTranslateThread::AbortSubprocesses() {
426 PLUGIN_PRINTF(("PnaclTranslateThread::AbortSubprocesses\n"));
427 NaClXMutexLock(&subprocess_mu_);
428 if (llc_subprocess_ != NULL && llc_subprocess_active_) {
429 llc_subprocess_->service_runtime()->Shutdown();
430 llc_subprocess_active_ = false;
431 }
432 if (ld_subprocess_ != NULL && ld_subprocess_active_) {
433 ld_subprocess_->service_runtime()->Shutdown();
434 ld_subprocess_active_ = false;
435 }
436 subprocesses_aborted_ = true;
437 NaClXMutexUnlock(&subprocess_mu_);
438 nacl::MutexLocker ml(&cond_mu_);
439 done_ = true;
440 // Free all buffered bitcode chunks
441 data_buffers_.clear();
442 NaClXCondVarSignal(&buffer_cond_);
443 }
444
~PnaclTranslateThread()445 PnaclTranslateThread::~PnaclTranslateThread() {
446 PLUGIN_PRINTF(("~PnaclTranslateThread (translate_thread=%p)\n", this));
447 AbortSubprocesses();
448 if (translate_thread_ != NULL)
449 NaClThreadJoin(translate_thread_.get());
450 PLUGIN_PRINTF(("~PnaclTranslateThread joined\n"));
451 NaClCondVarDtor(&buffer_cond_);
452 NaClMutexDtor(&cond_mu_);
453 NaClMutexDtor(&subprocess_mu_);
454 }
455
456 } // namespace plugin
457