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