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