pnacl_translate_thread.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/native_client/src/trusted/plugin/plugin.h" 11#include "ppapi/native_client/src/trusted/plugin/plugin_error.h" 12#include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h" 13#include "ppapi/native_client/src/trusted/plugin/srpc_params.h" 14#include "ppapi/native_client/src/trusted/plugin/temporary_file.h" 15#include "ppapi/native_client/src/trusted/plugin/utility.h" 16 17namespace plugin { 18 19PnaclTranslateThread::PnaclTranslateThread() : llc_subprocess_active_(false), 20 ld_subprocess_active_(false), 21 done_(false), 22 time_stats_(), 23 manifest_(NULL), 24 obj_files_(NULL), 25 nexe_file_(NULL), 26 coordinator_error_info_(NULL), 27 resources_(NULL), 28 coordinator_(NULL), 29 plugin_(NULL) { 30 NaClXMutexCtor(&subprocess_mu_); 31 NaClXMutexCtor(&cond_mu_); 32 NaClXCondVarCtor(&buffer_cond_); 33} 34 35void PnaclTranslateThread::RunTranslate( 36 const pp::CompletionCallback& finish_callback, 37 const Manifest* manifest, 38 const std::vector<TempFile*>* obj_files, 39 TempFile* nexe_file, 40 nacl::DescWrapper* invalid_desc_wrapper, 41 ErrorInfo* error_info, 42 PnaclResources* resources, 43 PnaclOptions* pnacl_options, 44 PnaclCoordinator* coordinator, 45 Plugin* plugin) { 46 PLUGIN_PRINTF(("PnaclStreamingTranslateThread::RunTranslate)\n")); 47 manifest_ = manifest; 48 obj_files_ = obj_files; 49 nexe_file_ = nexe_file; 50 invalid_desc_wrapper_ = invalid_desc_wrapper; 51 coordinator_error_info_ = error_info; 52 resources_ = resources; 53 pnacl_options_ = pnacl_options; 54 coordinator_ = coordinator; 55 plugin_ = plugin; 56 57 // Invoke llc followed by ld off the main thread. This allows use of 58 // blocking RPCs that would otherwise block the JavaScript main thread. 59 report_translate_finished_ = finish_callback; 60 translate_thread_.reset(new NaClThread); 61 if (translate_thread_ == NULL) { 62 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE, 63 "could not allocate thread struct."); 64 return; 65 } 66 const int32_t kArbitraryStackSize = 128 * 1024; 67 if (!NaClThreadCreateJoinable(translate_thread_.get(), 68 DoTranslateThread, 69 this, 70 kArbitraryStackSize)) { 71 TranslateFailed(PP_NACL_ERROR_PNACL_THREAD_CREATE, 72 "could not create thread."); 73 translate_thread_.reset(NULL); 74 } 75} 76 77// Called from main thread to send bytes to the translator. 78void PnaclTranslateThread::PutBytes(std::vector<char>* bytes, 79 int count) { 80 PLUGIN_PRINTF(("PutBytes (this=%p, bytes=%p, size=%" NACL_PRIuS 81 ", count=%d)\n", 82 this, bytes, bytes ? bytes->size() : 0, count)); 83 size_t buffer_size = 0; 84 // If we are done (error or not), Signal the translation thread to stop. 85 if (count <= PP_OK) { 86 NaClXMutexLock(&cond_mu_); 87 done_ = true; 88 NaClXCondVarSignal(&buffer_cond_); 89 NaClXMutexUnlock(&cond_mu_); 90 return; 91 } 92 93 CHECK(bytes != NULL); 94 // Ensure that the buffer we send to the translation thread is the right size 95 // (count can be < the buffer size). This can be done without the lock. 96 buffer_size = bytes->size(); 97 bytes->resize(count); 98 99 NaClXMutexLock(&cond_mu_); 100 101 data_buffers_.push_back(std::vector<char>()); 102 bytes->swap(data_buffers_.back()); // Avoid copying the buffer data. 103 104 NaClXCondVarSignal(&buffer_cond_); 105 NaClXMutexUnlock(&cond_mu_); 106 107 // Ensure the buffer we send back to the coordinator is the expected size 108 bytes->resize(buffer_size); 109} 110 111NaClSubprocess* PnaclTranslateThread::StartSubprocess( 112 const nacl::string& url_for_nexe, 113 const Manifest* manifest, 114 ErrorInfo* error_info) { 115 PLUGIN_PRINTF(("PnaclTranslateThread::StartSubprocess (url_for_nexe=%s)\n", 116 url_for_nexe.c_str())); 117 nacl::DescWrapper* wrapper = resources_->WrapperForUrl(url_for_nexe); 118 // Supply a URL for the translator components, different from the app URL, 119 // so that NaCl GDB can filter-out the translator processes (and not debug 120 // the translator itself). Must have a full URL with schema, otherwise the 121 // string gets silently dropped by GURL. 122 nacl::string full_url = resources_->GetFullUrl( 123 url_for_nexe, plugin_->nacl_interface()->GetSandboxArch()); 124 nacl::scoped_ptr<NaClSubprocess> subprocess( 125 plugin_->LoadHelperNaClModule(full_url, wrapper, manifest, error_info)); 126 if (subprocess.get() == NULL) { 127 PLUGIN_PRINTF(( 128 "PnaclTranslateThread::StartSubprocess: subprocess creation failed\n")); 129 return NULL; 130 } 131 return subprocess.release(); 132} 133 134void WINAPI PnaclTranslateThread::DoTranslateThread(void* arg) { 135 PnaclTranslateThread* translator = 136 reinterpret_cast<PnaclTranslateThread*>(arg); 137 translator->DoTranslate(); 138} 139 140void 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 } 148 for (; i < PnaclCoordinator::kMaxTranslatorObjectFiles; i++) { 149 llc_out_files.push_back(invalid_desc_wrapper_); 150 } 151 152 { 153 nacl::MutexLocker ml(&subprocess_mu_); 154 int64_t llc_start_time = NaClGetTimeOfDayMicroseconds(); 155 llc_subprocess_.reset( 156 StartSubprocess(resources_->GetLlcUrl(), manifest_, &error_info)); 157 if (llc_subprocess_ == NULL) { 158 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_SETUP, 159 "Compile process could not be created: " + 160 error_info.message()); 161 return; 162 } 163 llc_subprocess_active_ = true; 164 time_stats_.pnacl_llc_load_time = 165 (NaClGetTimeOfDayMicroseconds() - llc_start_time); 166 // Run LLC. 167 PluginReverseInterface* llc_reverse = 168 llc_subprocess_->service_runtime()->rev_interface(); 169 for (size_t i = 0; i < obj_files_->size(); i++) { 170 llc_reverse->AddTempQuotaManagedFile((*obj_files_)[i]->identifier()); 171 } 172 } 173 174 int64_t compile_start_time = NaClGetTimeOfDayMicroseconds(); 175 bool init_success; 176 177 std::vector<char> split_args; 178 nacl::stringstream ss; 179 // TODO(dschuff): This CL override is ugly. Change llc to default to using 180 // the number of modules specified in the first param, and ignore multiple 181 // uses of -split-module 182 ss << "-split-module=" << obj_files_->size(); 183 nacl::string split_arg = ss.str(); 184 std::copy(split_arg.begin(), split_arg.end(), std::back_inserter(split_args)); 185 split_args.push_back('\x00'); 186 std::vector<char> options = pnacl_options_->GetOptCommandline(); 187 std::copy(options.begin(), options.end(), std::back_inserter(split_args)); 188 init_success = llc_subprocess_->InvokeSrpcMethod( 189 "StreamInitWithSplit", 190 "ihhhhhhhhhhhhhhhhC", 191 ¶ms, 192 static_cast<int>(obj_files_->size()), 193 llc_out_files[0]->desc(), 194 llc_out_files[1]->desc(), 195 llc_out_files[2]->desc(), 196 llc_out_files[3]->desc(), 197 llc_out_files[4]->desc(), 198 llc_out_files[5]->desc(), 199 llc_out_files[6]->desc(), 200 llc_out_files[7]->desc(), 201 llc_out_files[8]->desc(), 202 llc_out_files[9]->desc(), 203 llc_out_files[10]->desc(), 204 llc_out_files[11]->desc(), 205 llc_out_files[12]->desc(), 206 llc_out_files[13]->desc(), 207 llc_out_files[14]->desc(), 208 llc_out_files[15]->desc(), 209 &split_args[0], 210 split_args.size()); 211 if (!init_success) { 212 if (llc_subprocess_->srpc_client()->GetLastError() == 213 NACL_SRPC_RESULT_APP_ERROR) { 214 // The error message is only present if the error was returned from llc 215 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL, 216 nacl::string("Stream init failed: ") + 217 nacl::string(params.outs()[0]->arrays.str)); 218 } else { 219 TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL, 220 "Stream init internal error"); 221 } 222 return; 223 } 224 225 PLUGIN_PRINTF(("PnaclCoordinator: StreamInit successful\n")); 226 pp::Core* core = pp::Module::Get()->core(); 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 time_stats_.pnacl_compile_time = 287 (NaClGetTimeOfDayMicroseconds() - compile_start_time); 288 289 // Shut down the llc subprocess. 290 NaClXMutexLock(&subprocess_mu_); 291 llc_subprocess_active_ = false; 292 llc_subprocess_.reset(NULL); 293 NaClXMutexUnlock(&subprocess_mu_); 294 295 if(!RunLdSubprocess()) { 296 return; 297 } 298 core->CallOnMainThread(0, report_translate_finished_, PP_OK); 299} 300 301bool PnaclTranslateThread::RunLdSubprocess() { 302 ErrorInfo error_info; 303 SrpcParams params; 304 305 std::vector<nacl::DescWrapper*> ld_in_files; 306 size_t i; 307 for (i = 0; i < obj_files_->size(); i++) { 308 // Reset object file for reading first. 309 if (!(*obj_files_)[i]->Reset()) { 310 TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP, 311 "Link process could not reset object file"); 312 return false; 313 } 314 ld_in_files.push_back((*obj_files_)[i]->read_wrapper()); 315 } 316 for (; i < PnaclCoordinator::kMaxTranslatorObjectFiles; i++) { 317 ld_in_files.push_back(invalid_desc_wrapper_); 318 } 319 320 nacl::DescWrapper* ld_out_file = nexe_file_->write_wrapper(); 321 322 { 323 // Create LD process 324 nacl::MutexLocker ml(&subprocess_mu_); 325 int64_t ld_start_time = NaClGetTimeOfDayMicroseconds(); 326 ld_subprocess_.reset( 327 StartSubprocess(resources_->GetLdUrl(), manifest_, &error_info)); 328 if (ld_subprocess_ == NULL) { 329 TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP, 330 "Link process could not be created: " + 331 error_info.message()); 332 return false; 333 } 334 ld_subprocess_active_ = true; 335 time_stats_.pnacl_ld_load_time = 336 (NaClGetTimeOfDayMicroseconds() - ld_start_time); 337 PluginReverseInterface* ld_reverse = 338 ld_subprocess_->service_runtime()->rev_interface(); 339 ld_reverse->AddTempQuotaManagedFile(nexe_file_->identifier()); 340 } 341 342 int64_t link_start_time = NaClGetTimeOfDayMicroseconds(); 343 // Run LD. 344 bool success = ld_subprocess_->InvokeSrpcMethod( 345 "RunWithSplit", 346 "ihhhhhhhhhhhhhhhhh", 347 ¶ms, 348 static_cast<int>(obj_files_->size()), 349 ld_in_files[0]->desc(), 350 ld_in_files[1]->desc(), 351 ld_in_files[2]->desc(), 352 ld_in_files[3]->desc(), 353 ld_in_files[4]->desc(), 354 ld_in_files[5]->desc(), 355 ld_in_files[6]->desc(), 356 ld_in_files[7]->desc(), 357 ld_in_files[8]->desc(), 358 ld_in_files[9]->desc(), 359 ld_in_files[10]->desc(), 360 ld_in_files[11]->desc(), 361 ld_in_files[12]->desc(), 362 ld_in_files[13]->desc(), 363 ld_in_files[14]->desc(), 364 ld_in_files[15]->desc(), 365 ld_out_file->desc()); 366 if (!success) { 367 TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL, 368 "link failed."); 369 return false; 370 } 371 time_stats_.pnacl_link_time = 372 NaClGetTimeOfDayMicroseconds() - link_start_time; 373 PLUGIN_PRINTF(("PnaclCoordinator: link (translator=%p) succeeded\n", 374 this)); 375 // Shut down the ld subprocess. 376 NaClXMutexLock(&subprocess_mu_); 377 ld_subprocess_active_ = false; 378 ld_subprocess_.reset(NULL); 379 NaClXMutexUnlock(&subprocess_mu_); 380 return true; 381} 382 383void PnaclTranslateThread::TranslateFailed( 384 PP_NaClError err_code, 385 const nacl::string& error_string) { 386 PLUGIN_PRINTF(("PnaclTranslateThread::TranslateFailed (error_string='%s')\n", 387 error_string.c_str())); 388 pp::Core* core = pp::Module::Get()->core(); 389 if (coordinator_error_info_->message().empty()) { 390 // Only use our message if one hasn't already been set by the coordinator 391 // (e.g. pexe load failed). 392 coordinator_error_info_->SetReport(err_code, 393 nacl::string("PnaclCoordinator: ") + 394 error_string); 395 } 396 core->CallOnMainThread(0, report_translate_finished_, PP_ERROR_FAILED); 397} 398 399void PnaclTranslateThread::AbortSubprocesses() { 400 PLUGIN_PRINTF(("PnaclTranslateThread::AbortSubprocesses\n")); 401 NaClXMutexLock(&subprocess_mu_); 402 if (llc_subprocess_ != NULL && llc_subprocess_active_) { 403 llc_subprocess_->service_runtime()->Shutdown(); 404 llc_subprocess_active_ = false; 405 } 406 if (ld_subprocess_ != NULL && ld_subprocess_active_) { 407 ld_subprocess_->service_runtime()->Shutdown(); 408 ld_subprocess_active_ = false; 409 } 410 NaClXMutexUnlock(&subprocess_mu_); 411 nacl::MutexLocker ml(&cond_mu_); 412 done_ = true; 413 // Free all buffered bitcode chunks 414 data_buffers_.clear(); 415 NaClXCondVarSignal(&buffer_cond_); 416} 417 418PnaclTranslateThread::~PnaclTranslateThread() { 419 PLUGIN_PRINTF(("~PnaclTranslateThread (translate_thread=%p)\n", this)); 420 AbortSubprocesses(); 421 if (translate_thread_ != NULL) 422 NaClThreadJoin(translate_thread_.get()); 423 PLUGIN_PRINTF(("~PnaclTranslateThread joined\n")); 424 NaClCondVarDtor(&buffer_cond_); 425 NaClMutexDtor(&cond_mu_); 426 NaClMutexDtor(&subprocess_mu_); 427} 428 429} // namespace plugin 430