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 18namespace plugin { 19namespace { 20 21template <typename Val> 22std::string MakeCommandLineArg(const char* key, const Val val) { 23 std::stringstream ss; 24 ss << key << val; 25 return ss.str(); 26} 27 28void 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 std::string &architecture_attributes) { 34 typedef std::vector<std::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 55PnaclTranslateThread::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 71void 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 std::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. 114void 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_); 123} 124 125void PnaclTranslateThread::EndStream() { 126 NaClXMutexLock(&cond_mu_); 127 done_ = true; 128 NaClXCondVarSignal(&buffer_cond_); 129 NaClXMutexUnlock(&cond_mu_); 130} 131 132void WINAPI PnaclTranslateThread::DoTranslateThread(void* arg) { 133 PnaclTranslateThread* translator = 134 reinterpret_cast<PnaclTranslateThread*>(arg); 135 translator->DoTranslate(); 136} 137 138void PnaclTranslateThread::DoTranslate() { 139 ErrorInfo error_info; 140 SrpcParams params; 141 std::vector<nacl::DescWrapper*> llc_out_files; 142 size_t i; 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_); 147 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()); 160 return; 161 } 162 GetNaClInterface()->LogTranslateTime( 163 "NaCl.Perf.PNaClLoadTime.LoadCompiler", 164 NaClGetTimeOfDayMicroseconds() - llc_start_time); 165 166 { 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; 173 return; 174 } 175 llc_subprocess_.reset(llc_subprocess); 176 llc_subprocess = NULL; 177 llc_subprocess_active_ = true; 178 } 179 180 int64_t compile_start_time = NaClGetTimeOfDayMicroseconds(); 181 bool init_success; 182 183 std::vector<char> split_args; 184 GetLlcCommandLine(plugin_, 185 &split_args, 186 obj_files_->size(), 187 pnacl_options_->opt_level, 188 pnacl_options_->is_debug, 189 architecture_attributes_); 190 init_success = llc_subprocess_->InvokeSrpcMethod( 191 "StreamInitWithSplit", 192 "ihhhhhhhhhhhhhhhhC", 193 ¶ms, 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(), 211 &split_args[0], 212 split_args.size()); 213 if (!init_success) { 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 std::string("Stream init failed: ") + 219 std::string(params.outs()[0]->arrays.str)); 220 } else { 221 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL, 222 "Stream init internal error"); 223 } 224 return; 225 } 226 PLUGIN_PRINTF(("PnaclCoordinator: StreamInit successful\n")); 227 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_); 233 } 234 PLUGIN_PRINTF(("PnaclTranslateThread awake (done=%d, size=%" NACL_PRIuS 235 ")\n", 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", 244 "C", 245 ¶ms, 246 &data[0], 247 data.size())) { 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."); 257 return; 258 } 259 break; 260 } else { 261 PLUGIN_PRINTF(("StreamChunk Successful\n")); 262 core->CallOnMainThread( 263 0, 264 coordinator_->GetCompileProgressCallback(data.size()), 265 PP_OK); 266 } 267 } else { 268 NaClXMutexUnlock(&cond_mu_); 269 } 270 } 271 PLUGIN_PRINTF(("PnaclTranslateThread done with chunks\n")); 272 // Finish llc. 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); 280 } else { 281 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL, 282 "Compile StreamEnd internal error"); 283 } 284 return; 285 } 286 compile_time_ = NaClGetTimeOfDayMicroseconds() - compile_start_time; 287 GetNaClInterface()->LogTranslateTime("NaCl.Perf.PNaClLoadTime.CompileTime", 288 compile_time_); 289 290 // Shut down the llc subprocess. 291 NaClXMutexLock(&subprocess_mu_); 292 llc_subprocess_active_ = false; 293 llc_subprocess_.reset(NULL); 294 NaClXMutexUnlock(&subprocess_mu_); 295 296 if(!RunLdSubprocess()) { 297 return; 298 } 299 core->CallOnMainThread(0, report_translate_finished_, PP_OK); 300} 301 302bool PnaclTranslateThread::RunLdSubprocess() { 303 ErrorInfo error_info; 304 SrpcParams params; 305 306 std::vector<nacl::DescWrapper*> ld_in_files; 307 size_t i; 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"); 313 return false; 314 } 315 ld_in_files.push_back((*obj_files_)[i]->read_wrapper()); 316 } 317 for (; i < PnaclCoordinator::kMaxTranslatorObjectFiles; i++) 318 ld_in_files.push_back(invalid_desc_wrapper_); 319 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(), 326 ld_file_info, 327 &error_info)); 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()); 334 return false; 335 } 336 GetNaClInterface()->LogTranslateTime( 337 "NaCl.Perf.PNaClLoadTime.LoadLinker", 338 NaClGetTimeOfDayMicroseconds() - ld_start_time); 339 { 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(); 345 return false; 346 } 347 DCHECK(ld_subprocess_.get() == NULL); 348 ld_subprocess_.swap(ld_subprocess); 349 ld_subprocess_active_ = true; 350 } 351 352 int64_t link_start_time = NaClGetTimeOfDayMicroseconds(); 353 // Run LD. 354 bool success = ld_subprocess_->InvokeSrpcMethod( 355 "RunWithSplit", 356 "ihhhhhhhhhhhhhhhhh", 357 ¶ms, 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()); 376 if (!success) { 377 TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL, 378 "link failed."); 379 return false; 380 } 381 GetNaClInterface()->LogTranslateTime( 382 "NaCl.Perf.PNaClLoadTime.LinkTime", 383 NaClGetTimeOfDayMicroseconds() - link_start_time); 384 PLUGIN_PRINTF(("PnaclCoordinator: link (translator=%p) succeeded\n", 385 this)); 386 // Shut down the ld subprocess. 387 NaClXMutexLock(&subprocess_mu_); 388 ld_subprocess_active_ = false; 389 ld_subprocess_.reset(NULL); 390 NaClXMutexUnlock(&subprocess_mu_); 391 return true; 392} 393 394void PnaclTranslateThread::TranslateFailed( 395 PP_NaClError err_code, 396 const std::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 std::string("PnaclCoordinator: ") + 405 error_string); 406 } 407 core->CallOnMainThread(0, report_translate_finished_, PP_ERROR_FAILED); 408} 409 410void 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; 416 } 417 if (ld_subprocess_ != NULL && ld_subprocess_active_) { 418 ld_subprocess_->service_runtime()->Shutdown(); 419 ld_subprocess_active_ = false; 420 } 421 subprocesses_aborted_ = true; 422 NaClXMutexUnlock(&subprocess_mu_); 423 nacl::MutexLocker ml(&cond_mu_); 424 done_ = true; 425 // Free all buffered bitcode chunks 426 data_buffers_.clear(); 427 NaClXCondVarSignal(&buffer_cond_); 428} 429 430PnaclTranslateThread::~PnaclTranslateThread() { 431 PLUGIN_PRINTF(("~PnaclTranslateThread (translate_thread=%p)\n", this)); 432 AbortSubprocesses(); 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_); 439} 440 441} // namespace plugin 442