Home | History | Annotate | Download | only in plugin
      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 
     18 namespace plugin {
     19 namespace {
     20 
     21 template <typename Val>
     22 std::string MakeCommandLineArg(const char* key, const Val val) {
     23   std::stringstream ss;
     24   ss << key << val;
     25   return ss.str();
     26 }
     27 
     28 void 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 
     55 PnaclTranslateThread::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 
     71 void 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.
    114 void 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 
    125 void PnaclTranslateThread::EndStream() {
    126   NaClXMutexLock(&cond_mu_);
    127   done_ = true;
    128   NaClXCondVarSignal(&buffer_cond_);
    129   NaClXMutexUnlock(&cond_mu_);
    130 }
    131 
    132 void WINAPI PnaclTranslateThread::DoTranslateThread(void* arg) {
    133   PnaclTranslateThread* translator =
    134       reinterpret_cast<PnaclTranslateThread*>(arg);
    135   translator->DoTranslate();
    136 }
    137 
    138 void 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       &params,
    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                                              &params,
    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(), &params)) {
    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 
    302 bool 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       &params,
    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 
    394 void 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 
    410 void 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 
    430 PnaclTranslateThread::~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