Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / src / trusted / plugin / pnacl_translate_thread.cc
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
17 namespace plugin {
18
19 PnaclTranslateThread::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
35 void 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(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(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.
78 void 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
111 NaClSubprocess* 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   nacl::scoped_ptr<NaClSubprocess> subprocess(
119       plugin_->LoadHelperNaClModule(wrapper, manifest, error_info));
120   if (subprocess.get() == NULL) {
121     PLUGIN_PRINTF((
122         "PnaclTranslateThread::StartSubprocess: subprocess creation failed\n"));
123     return NULL;
124   }
125   return subprocess.release();
126 }
127
128 void WINAPI PnaclTranslateThread::DoTranslateThread(void* arg) {
129   PnaclTranslateThread* translator =
130       reinterpret_cast<PnaclTranslateThread*>(arg);
131   translator->DoTranslate();
132 }
133
134 void PnaclTranslateThread::DoTranslate() {
135   ErrorInfo error_info;
136   SrpcParams params;
137   std::vector<nacl::DescWrapper*> llc_out_files;
138   size_t i;
139   for (i = 0; i < obj_files_->size(); i++) {
140     llc_out_files.push_back((*obj_files_)[i]->write_wrapper());
141   }
142   for (; i < PnaclCoordinator::kMaxTranslatorObjectFiles; i++) {
143     llc_out_files.push_back(invalid_desc_wrapper_);
144   }
145
146   {
147     nacl::MutexLocker ml(&subprocess_mu_);
148     int64_t llc_start_time = NaClGetTimeOfDayMicroseconds();
149     llc_subprocess_.reset(
150       StartSubprocess(resources_->GetLlcUrl(), manifest_, &error_info));
151     if (llc_subprocess_ == NULL) {
152       TranslateFailed(ERROR_PNACL_LLC_SETUP,
153                       "Compile process could not be created: " +
154                       error_info.message());
155       return;
156     }
157     llc_subprocess_active_ = true;
158     time_stats_.pnacl_llc_load_time =
159         (NaClGetTimeOfDayMicroseconds() - llc_start_time);
160     // Run LLC.
161     PluginReverseInterface* llc_reverse =
162         llc_subprocess_->service_runtime()->rev_interface();
163     for (size_t i = 0; i < obj_files_->size(); i++) {
164       llc_reverse->AddTempQuotaManagedFile((*obj_files_)[i]->identifier());
165     }
166   }
167
168   int64_t compile_start_time = NaClGetTimeOfDayMicroseconds();
169   bool init_success;
170   std::vector<char> options = pnacl_options_->GetOptCommandline();
171
172   // Try to init with splitting
173   // TODO(dschuff): This CL override is ugly. Change llc to default to using
174   // the number of modules specified in the first param, and ignore multiple
175   // uses of -split-module
176   std::vector<char> split_args;
177   nacl::stringstream ss;
178   ss << "-split-module=" << obj_files_->size();
179   nacl::string split_arg = ss.str();
180   std::copy(split_arg.begin(), split_arg.end(), std::back_inserter(split_args));
181   split_args.push_back('\x00');
182   std::copy(options.begin(), options.end(), std::back_inserter(split_args));
183   int modules_used = static_cast<int>(obj_files_->size());
184   init_success = llc_subprocess_->InvokeSrpcMethod(
185       "StreamInitWithSplit",
186       "ihhhhhhhhhhhhhhhhC",
187       &params,
188       modules_used,
189       llc_out_files[0]->desc(),
190       llc_out_files[1]->desc(),
191       llc_out_files[2]->desc(),
192       llc_out_files[3]->desc(),
193       llc_out_files[4]->desc(),
194       llc_out_files[5]->desc(),
195       llc_out_files[6]->desc(),
196       llc_out_files[7]->desc(),
197       llc_out_files[8]->desc(),
198       llc_out_files[9]->desc(),
199       llc_out_files[10]->desc(),
200       llc_out_files[11]->desc(),
201       llc_out_files[12]->desc(),
202       llc_out_files[13]->desc(),
203       llc_out_files[14]->desc(),
204       llc_out_files[15]->desc(),
205       &split_args[0],
206       split_args.size());
207   if (!init_success) {
208     init_success = llc_subprocess_->InvokeSrpcMethod(
209         "StreamInitWithOverrides",
210         "hC",
211         &params,
212         llc_out_files[0]->desc(),
213         &options[0],
214         options.size());
215     modules_used = 1;
216   }
217
218   if (!init_success) {
219     if (llc_subprocess_->srpc_client()->GetLastError() ==
220         NACL_SRPC_RESULT_APP_ERROR) {
221       // The error message is only present if the error was returned from llc
222       TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
223                       nacl::string("Stream init failed: ") +
224                       nacl::string(params.outs()[0]->arrays.str));
225     } else {
226       TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
227                       "Stream init internal error");
228     }
229     return;
230   }
231
232   PLUGIN_PRINTF(("PnaclCoordinator: StreamInit successful\n"));
233   pp::Core* core = pp::Module::Get()->core();
234
235   // llc process is started.
236   while(!done_ || data_buffers_.size() > 0) {
237     NaClXMutexLock(&cond_mu_);
238     while(!done_ && data_buffers_.size() == 0) {
239       NaClXCondVarWait(&buffer_cond_, &cond_mu_);
240     }
241     PLUGIN_PRINTF(("PnaclTranslateThread awake (done=%d, size=%" NACL_PRIuS
242                    ")\n",
243                    done_, data_buffers_.size()));
244     if (data_buffers_.size() > 0) {
245       std::vector<char> data;
246       data.swap(data_buffers_.front());
247       data_buffers_.pop_front();
248       NaClXMutexUnlock(&cond_mu_);
249       PLUGIN_PRINTF(("StreamChunk\n"));
250       if (!llc_subprocess_->InvokeSrpcMethod("StreamChunk",
251                                              "C",
252                                              &params,
253                                              &data[0],
254                                              data.size())) {
255         if (llc_subprocess_->srpc_client()->GetLastError() !=
256             NACL_SRPC_RESULT_APP_ERROR) {
257           // If the error was reported by the translator, then we fall through
258           // and call StreamEnd, which returns a string describing the error,
259           // which we can then send to the Javascript console. Otherwise just
260           // fail here, since the translator has probably crashed or asserted.
261           TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
262                           "Compile stream chunk failed. "
263                           "The PNaCl translator has probably crashed.");
264           return;
265         }
266         break;
267       } else {
268         PLUGIN_PRINTF(("StreamChunk Successful\n"));
269         core->CallOnMainThread(
270             0,
271             coordinator_->GetCompileProgressCallback(data.size()),
272             PP_OK);
273       }
274     } else {
275       NaClXMutexUnlock(&cond_mu_);
276     }
277   }
278   PLUGIN_PRINTF(("PnaclTranslateThread done with chunks\n"));
279   // Finish llc.
280   if (!llc_subprocess_->InvokeSrpcMethod("StreamEnd", std::string(), &params)) {
281     PLUGIN_PRINTF(("PnaclTranslateThread StreamEnd failed\n"));
282     if (llc_subprocess_->srpc_client()->GetLastError() ==
283         NACL_SRPC_RESULT_APP_ERROR) {
284       // The error string is only present if the error was sent back from llc.
285       TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
286                       params.outs()[3]->arrays.str);
287     } else {
288       TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
289                       "Compile StreamEnd internal error");
290     }
291     return;
292   }
293   time_stats_.pnacl_compile_time =
294       (NaClGetTimeOfDayMicroseconds() - compile_start_time);
295
296   // LLC returns values that are used to determine how linking is done.
297   int is_shared_library = (params.outs()[0]->u.ival != 0);
298   nacl::string soname = params.outs()[1]->arrays.str;
299   nacl::string lib_dependencies = params.outs()[2]->arrays.str;
300   PLUGIN_PRINTF(("PnaclCoordinator: compile (translator=%p) succeeded"
301                  " is_shared_library=%d, soname='%s', lib_dependencies='%s')\n",
302                  this, is_shared_library, soname.c_str(),
303                  lib_dependencies.c_str()));
304
305   // Shut down the llc subprocess.
306   NaClXMutexLock(&subprocess_mu_);
307   llc_subprocess_active_ = false;
308   llc_subprocess_.reset(NULL);
309   NaClXMutexUnlock(&subprocess_mu_);
310
311   if(!RunLdSubprocess(
312          modules_used, is_shared_library, soname, lib_dependencies)) {
313     return;
314   }
315   core->CallOnMainThread(0, report_translate_finished_, PP_OK);
316 }
317
318 bool PnaclTranslateThread::RunLdSubprocess(int modules_used,
319                                            int is_shared_library,
320                                            const nacl::string& soname,
321                                            const nacl::string& lib_dependencies
322                                            ) {
323   ErrorInfo error_info;
324   SrpcParams params;
325
326   std::vector<nacl::DescWrapper*> ld_in_files;
327   size_t i;
328   for (i = 0; i < obj_files_->size(); i++) {
329     // Reset object file for reading first.
330     if (!(*obj_files_)[i]->Reset()) {
331       TranslateFailed(ERROR_PNACL_LD_SETUP,
332                       "Link process could not reset object file");
333       return false;
334     }
335     ld_in_files.push_back((*obj_files_)[i]->read_wrapper());
336   }
337   for (; i < PnaclCoordinator::kMaxTranslatorObjectFiles; i++) {
338     ld_in_files.push_back(invalid_desc_wrapper_);
339   }
340
341   nacl::DescWrapper* ld_out_file = nexe_file_->write_wrapper();
342
343   {
344     // Create LD process
345     nacl::MutexLocker ml(&subprocess_mu_);
346     int64_t ld_start_time = NaClGetTimeOfDayMicroseconds();
347     ld_subprocess_.reset(
348       StartSubprocess(resources_->GetLdUrl(), manifest_, &error_info));
349     if (ld_subprocess_ == NULL) {
350       TranslateFailed(ERROR_PNACL_LD_SETUP,
351                       "Link process could not be created: " +
352                       error_info.message());
353       return false;
354     }
355     ld_subprocess_active_ = true;
356     time_stats_.pnacl_ld_load_time =
357         (NaClGetTimeOfDayMicroseconds() - ld_start_time);
358     PluginReverseInterface* ld_reverse =
359         ld_subprocess_->service_runtime()->rev_interface();
360     ld_reverse->AddTempQuotaManagedFile(nexe_file_->identifier());
361   }
362
363   int64_t link_start_time = NaClGetTimeOfDayMicroseconds();
364   // Run LD.
365   bool success;
366   // If we ran LLC with module splitting, we can't fall back here.
367   if (modules_used > 1) {
368     success = ld_subprocess_->InvokeSrpcMethod("RunWithSplit",
369                                                "ihhhhhhhhhhhhhhhhh",
370                                                &params,
371                                                modules_used,
372                                                ld_in_files[0]->desc(),
373                                                ld_in_files[1]->desc(),
374                                                ld_in_files[2]->desc(),
375                                                ld_in_files[3]->desc(),
376                                                ld_in_files[4]->desc(),
377                                                ld_in_files[5]->desc(),
378                                                ld_in_files[6]->desc(),
379                                                ld_in_files[7]->desc(),
380                                                ld_in_files[8]->desc(),
381                                                ld_in_files[9]->desc(),
382                                                ld_in_files[10]->desc(),
383                                                ld_in_files[11]->desc(),
384                                                ld_in_files[12]->desc(),
385                                                ld_in_files[13]->desc(),
386                                                ld_in_files[14]->desc(),
387                                                ld_in_files[15]->desc(),
388                                                ld_out_file->desc());
389   } else {
390     success = ld_subprocess_->InvokeSrpcMethod("RunWithDefaultCommandLine",
391                                                "hhiss",
392                                                &params,
393                                                ld_in_files[0]->desc(),
394                                                ld_out_file->desc(),
395                                                is_shared_library,
396                                                soname.c_str(),
397                                                lib_dependencies.c_str());
398   }
399   if (!success) {
400     TranslateFailed(ERROR_PNACL_LD_INTERNAL,
401                     "link failed.");
402     return false;
403   }
404   time_stats_.pnacl_link_time =
405       NaClGetTimeOfDayMicroseconds() - link_start_time;
406   PLUGIN_PRINTF(("PnaclCoordinator: link (translator=%p) succeeded\n",
407                  this));
408   // Shut down the ld subprocess.
409   NaClXMutexLock(&subprocess_mu_);
410   ld_subprocess_active_ = false;
411   ld_subprocess_.reset(NULL);
412   NaClXMutexUnlock(&subprocess_mu_);
413   return true;
414 }
415
416 void PnaclTranslateThread::TranslateFailed(
417     enum PluginErrorCode err_code,
418     const nacl::string& error_string) {
419   PLUGIN_PRINTF(("PnaclTranslateThread::TranslateFailed (error_string='%s')\n",
420                  error_string.c_str()));
421   pp::Core* core = pp::Module::Get()->core();
422   if (coordinator_error_info_->message().empty()) {
423     // Only use our message if one hasn't already been set by the coordinator
424     // (e.g. pexe load failed).
425     coordinator_error_info_->SetReport(err_code,
426                                        nacl::string("PnaclCoordinator: ") +
427                                        error_string);
428   }
429   core->CallOnMainThread(0, report_translate_finished_, PP_ERROR_FAILED);
430 }
431
432 void PnaclTranslateThread::AbortSubprocesses() {
433   PLUGIN_PRINTF(("PnaclTranslateThread::AbortSubprocesses\n"));
434   NaClXMutexLock(&subprocess_mu_);
435   if (llc_subprocess_ != NULL && llc_subprocess_active_) {
436     llc_subprocess_->service_runtime()->Shutdown();
437     llc_subprocess_active_ = false;
438   }
439   if (ld_subprocess_ != NULL && ld_subprocess_active_) {
440     ld_subprocess_->service_runtime()->Shutdown();
441     ld_subprocess_active_ = false;
442   }
443   NaClXMutexUnlock(&subprocess_mu_);
444   nacl::MutexLocker ml(&cond_mu_);
445   done_ = true;
446   // Free all buffered bitcode chunks
447   data_buffers_.clear();
448   NaClXCondVarSignal(&buffer_cond_);
449 }
450
451 PnaclTranslateThread::~PnaclTranslateThread() {
452   PLUGIN_PRINTF(("~PnaclTranslateThread (translate_thread=%p)\n", this));
453   AbortSubprocesses();
454   if (translate_thread_ != NULL)
455     NaClThreadJoin(translate_thread_.get());
456   PLUGIN_PRINTF(("~PnaclTranslateThread joined\n"));
457   NaClCondVarDtor(&buffer_cond_);
458   NaClMutexDtor(&cond_mu_);
459   NaClMutexDtor(&subprocess_mu_);
460 }
461
462 } // namespace plugin