| 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 |