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