• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "components/nacl/renderer/plugin/pnacl_translate_thread.h"
11 
12 #include <stddef.h>
13 
14 #include <iterator>
15 #include <memory>
16 #include <sstream>
17 
18 #include "base/check.h"
19 #include "base/time/time.h"
20 #include "components/nacl/renderer/plugin/plugin.h"
21 #include "components/nacl/renderer/plugin/plugin_error.h"
22 #include "ppapi/c/ppb_file_io.h"
23 #include "ppapi/cpp/var.h"
24 #include "ppapi/proxy/ppapi_messages.h"
25 
26 namespace plugin {
27 namespace {
28 
29 template <typename Val>
MakeCommandLineArg(const char * key,const Val val)30 std::string MakeCommandLineArg(const char* key, const Val val) {
31   std::stringstream ss;
32   ss << key << val;
33   return ss.str();
34 }
35 
GetLlcCommandLine(std::vector<std::string> * args,size_t obj_files_size,int32_t opt_level,bool is_debug,const std::string & architecture_attributes)36 void GetLlcCommandLine(std::vector<std::string>* args,
37                        size_t obj_files_size,
38                        int32_t opt_level,
39                        bool is_debug,
40                        const std::string& architecture_attributes) {
41   // TODO(dschuff): This CL override is ugly. Change llc to default to
42   // using the number of modules specified in the first param, and
43   // ignore multiple uses of -split-module
44   args->push_back(MakeCommandLineArg("-split-module=", obj_files_size));
45   args->push_back(MakeCommandLineArg("-O", opt_level));
46   if (is_debug)
47     args->push_back("-bitcode-format=llvm");
48   if (!architecture_attributes.empty())
49     args->push_back("-mattr=" + architecture_attributes);
50 }
51 
GetSubzeroCommandLine(std::vector<std::string> * args,int32_t opt_level,bool is_debug,const std::string & architecture_attributes)52 void GetSubzeroCommandLine(std::vector<std::string>* args,
53                            int32_t opt_level,
54                            bool is_debug,
55                            const std::string& architecture_attributes) {
56   args->push_back(MakeCommandLineArg("-O", opt_level));
57   DCHECK(!is_debug);
58   // TODO(stichnot): enable this once the mattr flag formatting is
59   // compatible: https://code.google.com/p/nativeclient/issues/detail?id=4132
60   // if (!architecture_attributes.empty())
61   //   args->push_back("-mattr=" + architecture_attributes);
62 }
63 
64 }  // namespace
65 
PnaclTranslateThread()66 PnaclTranslateThread::PnaclTranslateThread()
67     : compiler_subprocess_(nullptr),
68       ld_subprocess_(nullptr),
69       compiler_subprocess_active_(false),
70       ld_subprocess_active_(false),
71       buffer_cond_(&cond_mu_),
72       done_(false),
73       compile_time_(0),
74       obj_files_(nullptr),
75       num_threads_(0),
76       nexe_file_(nullptr),
77       coordinator_error_info_(nullptr),
78       coordinator_(nullptr) {}
79 
SetupState(const pp::CompletionCallback & finish_callback,NaClSubprocess * compiler_subprocess,NaClSubprocess * ld_subprocess,std::vector<base::File> * obj_files,int num_threads,base::File * nexe_file,ErrorInfo * error_info,PP_PNaClOptions * pnacl_options,const std::string & architecture_attributes,PnaclCoordinator * coordinator)80 void PnaclTranslateThread::SetupState(
81     const pp::CompletionCallback& finish_callback,
82     NaClSubprocess* compiler_subprocess,
83     NaClSubprocess* ld_subprocess,
84     std::vector<base::File>* obj_files,
85     int num_threads,
86     base::File* nexe_file,
87     ErrorInfo* error_info,
88     PP_PNaClOptions* pnacl_options,
89     const std::string& architecture_attributes,
90     PnaclCoordinator* coordinator) {
91   compiler_subprocess_ = compiler_subprocess;
92   ld_subprocess_ = ld_subprocess;
93   obj_files_ = obj_files;
94   num_threads_ = num_threads;
95   nexe_file_ = nexe_file;
96   coordinator_error_info_ = error_info;
97   pnacl_options_ = pnacl_options;
98   architecture_attributes_ = architecture_attributes;
99   coordinator_ = coordinator;
100 
101   report_translate_finished_ = finish_callback;
102 }
103 
RunCompile(const pp::CompletionCallback & compile_finished_callback)104 void PnaclTranslateThread::RunCompile(
105     const pp::CompletionCallback& compile_finished_callback) {
106   DCHECK(started());
107   DCHECK(compiler_subprocess_->service_runtime());
108   compiler_subprocess_active_ = true;
109 
110   // Take ownership of this IPC channel to make sure that it does not get
111   // freed on the child thread when the child thread calls Shutdown().
112   compiler_channel_ =
113       compiler_subprocess_->service_runtime()->TakeTranslatorChannel();
114   // compiler_channel_ is a IPC::SyncChannel, which is not thread-safe and
115   // cannot be used directly by the child thread, so create a
116   // SyncMessageFilter which can be used by the child thread.
117   compiler_channel_filter_ = compiler_channel_->CreateSyncMessageFilter();
118 
119   compile_finished_callback_ = compile_finished_callback;
120   translate_thread_ = std::make_unique<CompileThread>(this);
121   translate_thread_->Start();
122 }
123 
RunLink()124 void PnaclTranslateThread::RunLink() {
125   DCHECK(started());
126   DCHECK(ld_subprocess_->service_runtime());
127   ld_subprocess_active_ = true;
128 
129   // Take ownership of this IPC channel to make sure that it does not get
130   // freed on the child thread when the child thread calls Shutdown().
131   ld_channel_ = ld_subprocess_->service_runtime()->TakeTranslatorChannel();
132   // ld_channel_ is a IPC::SyncChannel, which is not thread-safe and cannot be
133   // used directly by the child thread, so create a SyncMessageFilter which
134   // can be used by the child thread.
135   ld_channel_filter_ = ld_channel_->CreateSyncMessageFilter();
136 
137   // Tear down the previous thread.
138   translate_thread_->Join();
139   translate_thread_ = std::make_unique<LinkThread>(this);
140   translate_thread_->Start();
141 }
142 
143 // Called from main thread to send bytes to the translator.
PutBytes(const void * bytes,int32_t count)144 void PnaclTranslateThread::PutBytes(const void* bytes, int32_t count) {
145   CHECK(bytes);
146   base::AutoLock lock(cond_mu_);
147   data_buffers_.push_back(std::string());
148   data_buffers_.back().insert(data_buffers_.back().end(),
149                               static_cast<const char*>(bytes),
150                               static_cast<const char*>(bytes) + count);
151   buffer_cond_.Signal();
152 }
153 
EndStream()154 void PnaclTranslateThread::EndStream() {
155   base::AutoLock lock(cond_mu_);
156   done_ = true;
157   buffer_cond_.Signal();
158 }
159 
GetHandleForSubprocess(base::File * file,int32_t open_flags)160 ppapi::proxy::SerializedHandle PnaclTranslateThread::GetHandleForSubprocess(
161     base::File* file,
162     int32_t open_flags) {
163   DCHECK(file->IsValid());
164   IPC::PlatformFileForTransit file_for_transit =
165       IPC::GetPlatformFileForTransit(file->GetPlatformFile(), false);
166 
167   // Using 0 disables any use of quota enforcement for this file handle.
168   PP_Resource file_io = 0;
169 
170   ppapi::proxy::SerializedHandle handle;
171   handle.set_file_handle(file_for_transit, open_flags, file_io);
172   return handle;
173 }
174 
Run()175 void PnaclTranslateThread::CompileThread::Run() {
176   pnacl_translate_thread_->DoCompile();
177 }
178 
DoCompile()179 void PnaclTranslateThread::DoCompile() {
180   {
181     base::AutoLock lock(subprocess_mu_);
182     // If the main thread asked us to exit in between starting the thread
183     // and now, just leave now.
184     if (!compiler_subprocess_active_)
185       return;
186   }
187 
188   std::vector<ppapi::proxy::SerializedHandle> compiler_output_files;
189   for (base::File& obj_file : *obj_files_) {
190     compiler_output_files.push_back(
191         GetHandleForSubprocess(&obj_file, PP_FILEOPENFLAG_WRITE));
192   }
193 
194   pp::Core* core = pp::Module::Get()->core();
195   base::TimeTicks do_compile_start_time = base::TimeTicks::Now();
196 
197   std::vector<std::string> args;
198   if (pnacl_options_->use_subzero) {
199     GetSubzeroCommandLine(&args, pnacl_options_->opt_level,
200                           PP_ToBool(pnacl_options_->is_debug),
201                           architecture_attributes_);
202   } else {
203     GetLlcCommandLine(&args, obj_files_->size(),
204                       pnacl_options_->opt_level,
205                       PP_ToBool(pnacl_options_->is_debug),
206                       architecture_attributes_);
207   }
208 
209   bool success = false;
210   std::string error_str;
211   if (!compiler_channel_filter_->Send(
212       new PpapiMsg_PnaclTranslatorCompileInit(
213           num_threads_, compiler_output_files, args, &success, &error_str))) {
214     TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
215                     "Compile stream init failed: "
216                     "reply not received from PNaCl translator "
217                     "(it probably crashed)");
218     return;
219   }
220   if (!success) {
221     TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
222                     std::string("Stream init failed: ") + error_str);
223     return;
224   }
225 
226   // llc process is started.
227   while(!done_ || data_buffers_.size() > 0) {
228     cond_mu_.Acquire();
229     while(!done_ && data_buffers_.size() == 0) {
230       buffer_cond_.Wait();
231     }
232     if (data_buffers_.size() > 0) {
233       std::string data;
234       data.swap(data_buffers_.front());
235       data_buffers_.pop_front();
236       cond_mu_.Release();
237 
238       if (!compiler_channel_filter_->Send(
239               new PpapiMsg_PnaclTranslatorCompileChunk(data, &success))) {
240         TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
241                         "Compile stream chunk failed: "
242                         "reply not received from PNaCl translator "
243                         "(it probably crashed)");
244         return;
245       }
246       if (!success) {
247         // If the error was reported by the translator, then we fall through
248         // and call PpapiMsg_PnaclTranslatorCompileEnd, which returns a string
249         // describing the error, which we can then send to the Javascript
250         // console.
251         break;
252       }
253       core->CallOnMainThread(
254           0,
255           coordinator_->GetCompileProgressCallback(data.size()),
256           PP_OK);
257     } else {
258       cond_mu_.Release();
259     }
260   }
261   // Finish llc.
262   if (!compiler_channel_filter_->Send(
263           new PpapiMsg_PnaclTranslatorCompileEnd(&success, &error_str))) {
264     TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
265                     "Compile stream end failed: "
266                     "reply not received from PNaCl translator "
267                     "(it probably crashed)");
268     return;
269   }
270   if (!success) {
271     TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL, error_str);
272     return;
273   }
274   compile_time_ =
275     (base::TimeTicks::Now() - do_compile_start_time).InMicroseconds();
276   nacl::PPBNaClPrivate::LogTranslateTime("NaCl.Perf.PNaClLoadTime.CompileTime",
277                                          compile_time_);
278   nacl::PPBNaClPrivate::LogTranslateTime(
279       pnacl_options_->use_subzero
280           ? "NaCl.Perf.PNaClLoadTime.CompileTime.Subzero"
281           : "NaCl.Perf.PNaClLoadTime.CompileTime.LLC",
282       compile_time_);
283 
284   // Shut down the compiler subprocess.
285   {
286     base::AutoLock lock(subprocess_mu_);
287     compiler_subprocess_active_ = false;
288   }
289 
290   core->CallOnMainThread(0, compile_finished_callback_, PP_OK);
291 }
292 
Run()293 void PnaclTranslateThread::LinkThread::Run() {
294   pnacl_translate_thread_->DoLink();
295 }
296 
DoLink()297 void PnaclTranslateThread::DoLink() {
298   {
299     base::AutoLock lock(subprocess_mu_);
300     // If the main thread asked us to exit in between starting the thread
301     // and now, just leave now.
302     if (!ld_subprocess_active_)
303       return;
304   }
305 
306   // Reset object files for reading first.  We do this before duplicating
307   // handles/FDs to prevent any handle/FD leaks in case any of the Seek()
308   // calls fail.
309   for (base::File& obj_file : *obj_files_) {
310     if (obj_file.Seek(base::File::FROM_BEGIN, 0) != 0) {
311       TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP,
312                       "Link process could not reset object file");
313       return;
314     }
315   }
316 
317   ppapi::proxy::SerializedHandle nexe_file =
318       GetHandleForSubprocess(nexe_file_, PP_FILEOPENFLAG_WRITE);
319   std::vector<ppapi::proxy::SerializedHandle> ld_input_files;
320   for (base::File& obj_file : *obj_files_) {
321     ld_input_files.push_back(
322         GetHandleForSubprocess(&obj_file, PP_FILEOPENFLAG_READ));
323   }
324 
325   base::TimeTicks link_start_time = base::TimeTicks::Now();
326   bool success = false;
327   bool sent = ld_channel_filter_->Send(
328       new PpapiMsg_PnaclTranslatorLink(ld_input_files, nexe_file, &success));
329   if (!sent) {
330     TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL,
331                     "link failed: reply not received from linker.");
332     return;
333   }
334   if (!success) {
335     TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL,
336                     "link failed: linker returned failure status.");
337     return;
338   }
339 
340   nacl::PPBNaClPrivate::LogTranslateTime(
341       "NaCl.Perf.PNaClLoadTime.LinkTime",
342       (base::TimeTicks::Now() - link_start_time).InMicroseconds());
343 
344   // Shut down the ld subprocess.
345   {
346     base::AutoLock lock(subprocess_mu_);
347     ld_subprocess_active_ = false;
348   }
349 
350   pp::Core* core = pp::Module::Get()->core();
351   core->CallOnMainThread(0, report_translate_finished_, PP_OK);
352 }
353 
TranslateFailed(PP_NaClError err_code,const std::string & error_string)354 void PnaclTranslateThread::TranslateFailed(
355     PP_NaClError err_code,
356     const std::string& error_string) {
357   pp::Core* core = pp::Module::Get()->core();
358   if (coordinator_error_info_->message().empty()) {
359     // Only use our message if one hasn't already been set by the coordinator
360     // (e.g. pexe load failed).
361     coordinator_error_info_->SetReport(err_code,
362                                        std::string("PnaclCoordinator: ") +
363                                        error_string);
364   }
365   core->CallOnMainThread(0, report_translate_finished_, PP_ERROR_FAILED);
366 }
367 
AbortSubprocesses()368 void PnaclTranslateThread::AbortSubprocesses() {
369   {
370     base::AutoLock lock(subprocess_mu_);
371     if (compiler_subprocess_ && compiler_subprocess_active_) {
372       // We only run the service_runtime's Shutdown and do not run the
373       // NaClSubprocess Shutdown, which would otherwise nullify some
374       // pointers that could still be in use (srpc_client, etc.).
375       compiler_subprocess_->service_runtime()->Shutdown();
376       compiler_subprocess_active_ = false;
377     }
378     if (ld_subprocess_ && ld_subprocess_active_) {
379       ld_subprocess_->service_runtime()->Shutdown();
380       ld_subprocess_active_ = false;
381     }
382   }
383   base::AutoLock lock(cond_mu_);
384   done_ = true;
385   // Free all buffered bitcode chunks
386   data_buffers_.clear();
387   buffer_cond_.Signal();
388 }
389 
~PnaclTranslateThread()390 PnaclTranslateThread::~PnaclTranslateThread() {
391   AbortSubprocesses();
392   if (translate_thread_)
393     translate_thread_->Join();
394 }
395 
396 } // namespace plugin
397