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 "native_client/src/trusted/desc/nacl_desc_wrapper.h" 8 #include "ppapi/native_client/src/trusted/plugin/plugin.h" 9 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" 10 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h" 11 #include "ppapi/native_client/src/trusted/plugin/srpc_params.h" 12 #include "ppapi/native_client/src/trusted/plugin/temporary_file.h" 13 #include "ppapi/native_client/src/trusted/plugin/utility.h" 14 15 namespace plugin { 16 17 PnaclTranslateThread::PnaclTranslateThread() : llc_subprocess_active_(false), 18 ld_subprocess_active_(false), 19 done_(false), 20 time_stats_(), 21 manifest_(NULL), 22 obj_file_(NULL), 23 nexe_file_(NULL), 24 coordinator_error_info_(NULL), 25 resources_(NULL), 26 coordinator_(NULL), 27 plugin_(NULL) { 28 NaClXMutexCtor(&subprocess_mu_); 29 NaClXMutexCtor(&cond_mu_); 30 NaClXCondVarCtor(&buffer_cond_); 31 } 32 33 void PnaclTranslateThread::RunTranslate( 34 const pp::CompletionCallback& finish_callback, 35 const Manifest* manifest, 36 TempFile* obj_file, 37 TempFile* nexe_file, 38 ErrorInfo* error_info, 39 PnaclResources* resources, 40 PnaclOptions* pnacl_options, 41 PnaclCoordinator* coordinator, 42 Plugin* plugin) { 43 PLUGIN_PRINTF(("PnaclStreamingTranslateThread::RunTranslate)\n")); 44 manifest_ = manifest; 45 obj_file_ = obj_file; 46 nexe_file_ = nexe_file; 47 coordinator_error_info_ = error_info; 48 resources_ = resources; 49 pnacl_options_ = pnacl_options; 50 coordinator_ = coordinator; 51 plugin_ = plugin; 52 53 // Invoke llc followed by ld off the main thread. This allows use of 54 // blocking RPCs that would otherwise block the JavaScript main thread. 55 report_translate_finished_ = finish_callback; 56 translate_thread_.reset(new NaClThread); 57 if (translate_thread_ == NULL) { 58 TranslateFailed(ERROR_PNACL_THREAD_CREATE, 59 "could not allocate thread struct."); 60 return; 61 } 62 const int32_t kArbitraryStackSize = 128 * 1024; 63 if (!NaClThreadCreateJoinable(translate_thread_.get(), 64 DoTranslateThread, 65 this, 66 kArbitraryStackSize)) { 67 TranslateFailed(ERROR_PNACL_THREAD_CREATE, 68 "could not create thread."); 69 translate_thread_.reset(NULL); 70 } 71 } 72 73 // Called from main thread to send bytes to the translator. 74 void PnaclTranslateThread::PutBytes(std::vector<char>* bytes, 75 int count) { 76 PLUGIN_PRINTF(("PutBytes (this=%p, bytes=%p, size=%" NACL_PRIuS 77 ", count=%d)\n", 78 this, bytes, bytes ? bytes->size() : 0, count)); 79 size_t buffer_size = 0; 80 // If we are done (error or not), Signal the translation thread to stop. 81 if (count <= PP_OK) { 82 NaClXMutexLock(&cond_mu_); 83 done_ = true; 84 NaClXCondVarSignal(&buffer_cond_); 85 NaClXMutexUnlock(&cond_mu_); 86 return; 87 } 88 89 CHECK(bytes != NULL); 90 // Ensure that the buffer we send to the translation thread is the right size 91 // (count can be < the buffer size). This can be done without the lock. 92 buffer_size = bytes->size(); 93 bytes->resize(count); 94 95 NaClXMutexLock(&cond_mu_); 96 97 data_buffers_.push_back(std::vector<char>()); 98 bytes->swap(data_buffers_.back()); // Avoid copying the buffer data. 99 100 NaClXCondVarSignal(&buffer_cond_); 101 NaClXMutexUnlock(&cond_mu_); 102 103 // Ensure the buffer we send back to the coordinator is the expected size 104 bytes->resize(buffer_size); 105 } 106 107 NaClSubprocess* PnaclTranslateThread::StartSubprocess( 108 const nacl::string& url_for_nexe, 109 const Manifest* manifest, 110 ErrorInfo* error_info) { 111 PLUGIN_PRINTF(("PnaclTranslateThread::StartSubprocess (url_for_nexe=%s)\n", 112 url_for_nexe.c_str())); 113 nacl::DescWrapper* wrapper = resources_->WrapperForUrl(url_for_nexe); 114 nacl::scoped_ptr<NaClSubprocess> subprocess( 115 plugin_->LoadHelperNaClModule(wrapper, manifest, error_info)); 116 if (subprocess.get() == NULL) { 117 PLUGIN_PRINTF(( 118 "PnaclTranslateThread::StartSubprocess: subprocess creation failed\n")); 119 return NULL; 120 } 121 return subprocess.release(); 122 } 123 124 void WINAPI PnaclTranslateThread::DoTranslateThread(void* arg) { 125 PnaclTranslateThread* translator = 126 reinterpret_cast<PnaclTranslateThread*>(arg); 127 translator->DoTranslate(); 128 } 129 130 void PnaclTranslateThread::DoTranslate() { 131 ErrorInfo error_info; 132 SrpcParams params; 133 nacl::DescWrapper* llc_out_file = obj_file_->write_wrapper(); 134 135 { 136 nacl::MutexLocker ml(&subprocess_mu_); 137 int64_t llc_start_time = NaClGetTimeOfDayMicroseconds(); 138 llc_subprocess_.reset( 139 StartSubprocess(resources_->GetLlcUrl(), manifest_, &error_info)); 140 if (llc_subprocess_ == NULL) { 141 TranslateFailed(ERROR_PNACL_LLC_SETUP, 142 "Compile process could not be created: " + 143 error_info.message()); 144 return; 145 } 146 llc_subprocess_active_ = true; 147 time_stats_.pnacl_llc_load_time = 148 (NaClGetTimeOfDayMicroseconds() - llc_start_time); 149 // Run LLC. 150 PluginReverseInterface* llc_reverse = 151 llc_subprocess_->service_runtime()->rev_interface(); 152 llc_reverse->AddTempQuotaManagedFile(obj_file_->identifier()); 153 } 154 155 int64_t compile_start_time = NaClGetTimeOfDayMicroseconds(); 156 bool init_success; 157 std::vector<char> options = pnacl_options_->GetOptCommandline(); 158 init_success = llc_subprocess_->InvokeSrpcMethod( 159 "StreamInitWithOverrides", 160 "hC", 161 ¶ms, 162 llc_out_file->desc(), 163 &options[0], 164 options.size()); 165 166 if (!init_success) { 167 if (llc_subprocess_->srpc_client()->GetLastError() == 168 NACL_SRPC_RESULT_APP_ERROR) { 169 // The error message is only present if the error was returned from llc 170 TranslateFailed(ERROR_PNACL_LLC_INTERNAL, 171 nacl::string("Stream init failed: ") + 172 nacl::string(params.outs()[0]->arrays.str)); 173 } else { 174 TranslateFailed(ERROR_PNACL_LLC_INTERNAL, 175 "Stream init internal error"); 176 } 177 return; 178 } 179 180 PLUGIN_PRINTF(("PnaclCoordinator: StreamInit successful\n")); 181 pp::Core* core = pp::Module::Get()->core(); 182 183 // llc process is started. 184 while(!done_ || data_buffers_.size() > 0) { 185 NaClXMutexLock(&cond_mu_); 186 while(!done_ && data_buffers_.size() == 0) { 187 NaClXCondVarWait(&buffer_cond_, &cond_mu_); 188 } 189 PLUGIN_PRINTF(("PnaclTranslateThread awake (done=%d, size=%" NACL_PRIuS 190 ")\n", 191 done_, data_buffers_.size())); 192 if (data_buffers_.size() > 0) { 193 std::vector<char> data; 194 data.swap(data_buffers_.front()); 195 data_buffers_.pop_front(); 196 NaClXMutexUnlock(&cond_mu_); 197 PLUGIN_PRINTF(("StreamChunk\n")); 198 if (!llc_subprocess_->InvokeSrpcMethod("StreamChunk", 199 "C", 200 ¶ms, 201 &data[0], 202 data.size())) { 203 TranslateFailed(ERROR_PNACL_LLC_INTERNAL, 204 "Compile stream chunk failed."); 205 return; 206 } 207 PLUGIN_PRINTF(("StreamChunk Successful\n")); 208 core->CallOnMainThread( 209 0, 210 coordinator_->GetCompileProgressCallback(data.size()), 211 PP_OK); 212 } else { 213 NaClXMutexUnlock(&cond_mu_); 214 } 215 } 216 PLUGIN_PRINTF(("PnaclTranslateThread done with chunks\n")); 217 // Finish llc. 218 if (!llc_subprocess_->InvokeSrpcMethod("StreamEnd", std::string(), ¶ms)) { 219 PLUGIN_PRINTF(("PnaclTranslateThread StreamEnd failed\n")); 220 if (llc_subprocess_->srpc_client()->GetLastError() == 221 NACL_SRPC_RESULT_APP_ERROR) { 222 // The error string is only present if the error was sent back from llc. 223 TranslateFailed(ERROR_PNACL_LLC_INTERNAL, 224 params.outs()[3]->arrays.str); 225 } else { 226 TranslateFailed(ERROR_PNACL_LLC_INTERNAL, 227 "Compile StreamEnd internal error"); 228 } 229 return; 230 } 231 time_stats_.pnacl_compile_time = 232 (NaClGetTimeOfDayMicroseconds() - compile_start_time); 233 234 // LLC returns values that are used to determine how linking is done. 235 int is_shared_library = (params.outs()[0]->u.ival != 0); 236 nacl::string soname = params.outs()[1]->arrays.str; 237 nacl::string lib_dependencies = params.outs()[2]->arrays.str; 238 PLUGIN_PRINTF(("PnaclCoordinator: compile (translator=%p) succeeded" 239 " is_shared_library=%d, soname='%s', lib_dependencies='%s')\n", 240 this, is_shared_library, soname.c_str(), 241 lib_dependencies.c_str())); 242 243 // Shut down the llc subprocess. 244 NaClXMutexLock(&subprocess_mu_); 245 llc_subprocess_active_ = false; 246 llc_subprocess_.reset(NULL); 247 NaClXMutexUnlock(&subprocess_mu_); 248 249 if(!RunLdSubprocess(is_shared_library, soname, lib_dependencies)) { 250 return; 251 } 252 core->CallOnMainThread(0, report_translate_finished_, PP_OK); 253 } 254 255 bool PnaclTranslateThread::RunLdSubprocess(int is_shared_library, 256 const nacl::string& soname, 257 const nacl::string& lib_dependencies 258 ) { 259 ErrorInfo error_info; 260 SrpcParams params; 261 // Reset object file for reading first. 262 if (!obj_file_->Reset()) { 263 TranslateFailed(ERROR_PNACL_LD_SETUP, 264 "Link process could not reset object file"); 265 return false; 266 } 267 nacl::DescWrapper* ld_in_file = obj_file_->read_wrapper(); 268 nacl::DescWrapper* ld_out_file = nexe_file_->write_wrapper(); 269 270 { 271 // Create LD process 272 nacl::MutexLocker ml(&subprocess_mu_); 273 int64_t ld_start_time = NaClGetTimeOfDayMicroseconds(); 274 ld_subprocess_.reset( 275 StartSubprocess(resources_->GetLdUrl(), manifest_, &error_info)); 276 if (ld_subprocess_ == NULL) { 277 TranslateFailed(ERROR_PNACL_LD_SETUP, 278 "Link process could not be created: " + 279 error_info.message()); 280 return false; 281 } 282 ld_subprocess_active_ = true; 283 time_stats_.pnacl_ld_load_time = 284 (NaClGetTimeOfDayMicroseconds() - ld_start_time); 285 PluginReverseInterface* ld_reverse = 286 ld_subprocess_->service_runtime()->rev_interface(); 287 ld_reverse->AddTempQuotaManagedFile(nexe_file_->identifier()); 288 } 289 290 int64_t link_start_time = NaClGetTimeOfDayMicroseconds(); 291 // Run LD. 292 if (!ld_subprocess_->InvokeSrpcMethod("RunWithDefaultCommandLine", 293 "hhiss", 294 ¶ms, 295 ld_in_file->desc(), 296 ld_out_file->desc(), 297 is_shared_library, 298 soname.c_str(), 299 lib_dependencies.c_str())) { 300 TranslateFailed(ERROR_PNACL_LD_INTERNAL, 301 "link failed."); 302 return false; 303 } 304 time_stats_.pnacl_link_time = 305 NaClGetTimeOfDayMicroseconds() - link_start_time; 306 PLUGIN_PRINTF(("PnaclCoordinator: link (translator=%p) succeeded\n", 307 this)); 308 // Shut down the ld subprocess. 309 NaClXMutexLock(&subprocess_mu_); 310 ld_subprocess_active_ = false; 311 ld_subprocess_.reset(NULL); 312 NaClXMutexUnlock(&subprocess_mu_); 313 return true; 314 } 315 316 void PnaclTranslateThread::TranslateFailed( 317 enum PluginErrorCode err_code, 318 const nacl::string& error_string) { 319 PLUGIN_PRINTF(("PnaclTranslateThread::TranslateFailed (error_string='%s')\n", 320 error_string.c_str())); 321 pp::Core* core = pp::Module::Get()->core(); 322 if (coordinator_error_info_->message().empty()) { 323 // Only use our message if one hasn't already been set by the coordinator 324 // (e.g. pexe load failed). 325 coordinator_error_info_->SetReport(err_code, 326 nacl::string("PnaclCoordinator: ") + 327 error_string); 328 } 329 core->CallOnMainThread(0, report_translate_finished_, PP_ERROR_FAILED); 330 } 331 332 void PnaclTranslateThread::AbortSubprocesses() { 333 PLUGIN_PRINTF(("PnaclTranslateThread::AbortSubprocesses\n")); 334 NaClXMutexLock(&subprocess_mu_); 335 if (llc_subprocess_ != NULL && llc_subprocess_active_) { 336 llc_subprocess_->service_runtime()->Shutdown(); 337 llc_subprocess_active_ = false; 338 } 339 if (ld_subprocess_ != NULL && ld_subprocess_active_) { 340 ld_subprocess_->service_runtime()->Shutdown(); 341 ld_subprocess_active_ = false; 342 } 343 NaClXMutexUnlock(&subprocess_mu_); 344 nacl::MutexLocker ml(&cond_mu_); 345 done_ = true; 346 // Free all buffered bitcode chunks 347 data_buffers_.clear(); 348 NaClXCondVarSignal(&buffer_cond_); 349 } 350 351 PnaclTranslateThread::~PnaclTranslateThread() { 352 PLUGIN_PRINTF(("~PnaclTranslateThread (translate_thread=%p)\n", this)); 353 AbortSubprocesses(); 354 if (translate_thread_ != NULL) 355 NaClThreadJoin(translate_thread_.get()); 356 PLUGIN_PRINTF(("~PnaclTranslateThread joined\n")); 357 NaClCondVarDtor(&buffer_cond_); 358 NaClMutexDtor(&cond_mu_); 359 NaClMutexDtor(&subprocess_mu_); 360 } 361 362 } // namespace plugin 363