392 lines
13 KiB
C++
392 lines
13 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "components/nacl/renderer/plugin/pnacl_translate_thread.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <sstream>
|
|
|
|
#include "base/check.h"
|
|
#include "base/time/time.h"
|
|
#include "components/nacl/renderer/plugin/plugin.h"
|
|
#include "components/nacl/renderer/plugin/plugin_error.h"
|
|
#include "ppapi/c/ppb_file_io.h"
|
|
#include "ppapi/cpp/var.h"
|
|
#include "ppapi/proxy/ppapi_messages.h"
|
|
|
|
namespace plugin {
|
|
namespace {
|
|
|
|
template <typename Val>
|
|
std::string MakeCommandLineArg(const char* key, const Val val) {
|
|
std::stringstream ss;
|
|
ss << key << val;
|
|
return ss.str();
|
|
}
|
|
|
|
void GetLlcCommandLine(std::vector<std::string>* args,
|
|
size_t obj_files_size,
|
|
int32_t opt_level,
|
|
bool is_debug,
|
|
const std::string& architecture_attributes) {
|
|
// TODO(dschuff): This CL override is ugly. Change llc to default to
|
|
// using the number of modules specified in the first param, and
|
|
// ignore multiple uses of -split-module
|
|
args->push_back(MakeCommandLineArg("-split-module=", obj_files_size));
|
|
args->push_back(MakeCommandLineArg("-O", opt_level));
|
|
if (is_debug)
|
|
args->push_back("-bitcode-format=llvm");
|
|
if (!architecture_attributes.empty())
|
|
args->push_back("-mattr=" + architecture_attributes);
|
|
}
|
|
|
|
void GetSubzeroCommandLine(std::vector<std::string>* args,
|
|
int32_t opt_level,
|
|
bool is_debug,
|
|
const std::string& architecture_attributes) {
|
|
args->push_back(MakeCommandLineArg("-O", opt_level));
|
|
DCHECK(!is_debug);
|
|
// TODO(stichnot): enable this once the mattr flag formatting is
|
|
// compatible: https://code.google.com/p/nativeclient/issues/detail?id=4132
|
|
// if (!architecture_attributes.empty())
|
|
// args->push_back("-mattr=" + architecture_attributes);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
PnaclTranslateThread::PnaclTranslateThread()
|
|
: compiler_subprocess_(nullptr),
|
|
ld_subprocess_(nullptr),
|
|
compiler_subprocess_active_(false),
|
|
ld_subprocess_active_(false),
|
|
buffer_cond_(&cond_mu_),
|
|
done_(false),
|
|
compile_time_(0),
|
|
obj_files_(nullptr),
|
|
num_threads_(0),
|
|
nexe_file_(nullptr),
|
|
coordinator_error_info_(nullptr),
|
|
coordinator_(nullptr) {}
|
|
|
|
void PnaclTranslateThread::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) {
|
|
compiler_subprocess_ = compiler_subprocess;
|
|
ld_subprocess_ = ld_subprocess;
|
|
obj_files_ = obj_files;
|
|
num_threads_ = num_threads;
|
|
nexe_file_ = nexe_file;
|
|
coordinator_error_info_ = error_info;
|
|
pnacl_options_ = pnacl_options;
|
|
architecture_attributes_ = architecture_attributes;
|
|
coordinator_ = coordinator;
|
|
|
|
report_translate_finished_ = finish_callback;
|
|
}
|
|
|
|
void PnaclTranslateThread::RunCompile(
|
|
const pp::CompletionCallback& compile_finished_callback) {
|
|
DCHECK(started());
|
|
DCHECK(compiler_subprocess_->service_runtime());
|
|
compiler_subprocess_active_ = true;
|
|
|
|
// Take ownership of this IPC channel to make sure that it does not get
|
|
// freed on the child thread when the child thread calls Shutdown().
|
|
compiler_channel_ =
|
|
compiler_subprocess_->service_runtime()->TakeTranslatorChannel();
|
|
// compiler_channel_ is a IPC::SyncChannel, which is not thread-safe and
|
|
// cannot be used directly by the child thread, so create a
|
|
// SyncMessageFilter which can be used by the child thread.
|
|
compiler_channel_filter_ = compiler_channel_->CreateSyncMessageFilter();
|
|
|
|
compile_finished_callback_ = compile_finished_callback;
|
|
translate_thread_ = std::make_unique<CompileThread>(this);
|
|
translate_thread_->Start();
|
|
}
|
|
|
|
void PnaclTranslateThread::RunLink() {
|
|
DCHECK(started());
|
|
DCHECK(ld_subprocess_->service_runtime());
|
|
ld_subprocess_active_ = true;
|
|
|
|
// Take ownership of this IPC channel to make sure that it does not get
|
|
// freed on the child thread when the child thread calls Shutdown().
|
|
ld_channel_ = ld_subprocess_->service_runtime()->TakeTranslatorChannel();
|
|
// ld_channel_ is a IPC::SyncChannel, which is not thread-safe and cannot be
|
|
// used directly by the child thread, so create a SyncMessageFilter which
|
|
// can be used by the child thread.
|
|
ld_channel_filter_ = ld_channel_->CreateSyncMessageFilter();
|
|
|
|
// Tear down the previous thread.
|
|
translate_thread_->Join();
|
|
translate_thread_ = std::make_unique<LinkThread>(this);
|
|
translate_thread_->Start();
|
|
}
|
|
|
|
// Called from main thread to send bytes to the translator.
|
|
void PnaclTranslateThread::PutBytes(const void* bytes, int32_t count) {
|
|
CHECK(bytes);
|
|
base::AutoLock lock(cond_mu_);
|
|
data_buffers_.push_back(std::string());
|
|
data_buffers_.back().insert(data_buffers_.back().end(),
|
|
static_cast<const char*>(bytes),
|
|
static_cast<const char*>(bytes) + count);
|
|
buffer_cond_.Signal();
|
|
}
|
|
|
|
void PnaclTranslateThread::EndStream() {
|
|
base::AutoLock lock(cond_mu_);
|
|
done_ = true;
|
|
buffer_cond_.Signal();
|
|
}
|
|
|
|
ppapi::proxy::SerializedHandle PnaclTranslateThread::GetHandleForSubprocess(
|
|
base::File* file,
|
|
int32_t open_flags) {
|
|
DCHECK(file->IsValid());
|
|
IPC::PlatformFileForTransit file_for_transit =
|
|
IPC::GetPlatformFileForTransit(file->GetPlatformFile(), false);
|
|
|
|
// Using 0 disables any use of quota enforcement for this file handle.
|
|
PP_Resource file_io = 0;
|
|
|
|
ppapi::proxy::SerializedHandle handle;
|
|
handle.set_file_handle(file_for_transit, open_flags, file_io);
|
|
return handle;
|
|
}
|
|
|
|
void PnaclTranslateThread::CompileThread::Run() {
|
|
pnacl_translate_thread_->DoCompile();
|
|
}
|
|
|
|
void PnaclTranslateThread::DoCompile() {
|
|
{
|
|
base::AutoLock lock(subprocess_mu_);
|
|
// If the main thread asked us to exit in between starting the thread
|
|
// and now, just leave now.
|
|
if (!compiler_subprocess_active_)
|
|
return;
|
|
}
|
|
|
|
std::vector<ppapi::proxy::SerializedHandle> compiler_output_files;
|
|
for (base::File& obj_file : *obj_files_) {
|
|
compiler_output_files.push_back(
|
|
GetHandleForSubprocess(&obj_file, PP_FILEOPENFLAG_WRITE));
|
|
}
|
|
|
|
pp::Core* core = pp::Module::Get()->core();
|
|
base::TimeTicks do_compile_start_time = base::TimeTicks::Now();
|
|
|
|
std::vector<std::string> args;
|
|
if (pnacl_options_->use_subzero) {
|
|
GetSubzeroCommandLine(&args, pnacl_options_->opt_level,
|
|
PP_ToBool(pnacl_options_->is_debug),
|
|
architecture_attributes_);
|
|
} else {
|
|
GetLlcCommandLine(&args, obj_files_->size(),
|
|
pnacl_options_->opt_level,
|
|
PP_ToBool(pnacl_options_->is_debug),
|
|
architecture_attributes_);
|
|
}
|
|
|
|
bool success = false;
|
|
std::string error_str;
|
|
if (!compiler_channel_filter_->Send(
|
|
new PpapiMsg_PnaclTranslatorCompileInit(
|
|
num_threads_, compiler_output_files, args, &success, &error_str))) {
|
|
TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
|
|
"Compile stream init failed: "
|
|
"reply not received from PNaCl translator "
|
|
"(it probably crashed)");
|
|
return;
|
|
}
|
|
if (!success) {
|
|
TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
|
|
std::string("Stream init failed: ") + error_str);
|
|
return;
|
|
}
|
|
|
|
// llc process is started.
|
|
while(!done_ || data_buffers_.size() > 0) {
|
|
cond_mu_.Acquire();
|
|
while(!done_ && data_buffers_.size() == 0) {
|
|
buffer_cond_.Wait();
|
|
}
|
|
if (data_buffers_.size() > 0) {
|
|
std::string data;
|
|
data.swap(data_buffers_.front());
|
|
data_buffers_.pop_front();
|
|
cond_mu_.Release();
|
|
|
|
if (!compiler_channel_filter_->Send(
|
|
new PpapiMsg_PnaclTranslatorCompileChunk(data, &success))) {
|
|
TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
|
|
"Compile stream chunk failed: "
|
|
"reply not received from PNaCl translator "
|
|
"(it probably crashed)");
|
|
return;
|
|
}
|
|
if (!success) {
|
|
// If the error was reported by the translator, then we fall through
|
|
// and call PpapiMsg_PnaclTranslatorCompileEnd, which returns a string
|
|
// describing the error, which we can then send to the Javascript
|
|
// console.
|
|
break;
|
|
}
|
|
core->CallOnMainThread(
|
|
0,
|
|
coordinator_->GetCompileProgressCallback(data.size()),
|
|
PP_OK);
|
|
} else {
|
|
cond_mu_.Release();
|
|
}
|
|
}
|
|
// Finish llc.
|
|
if (!compiler_channel_filter_->Send(
|
|
new PpapiMsg_PnaclTranslatorCompileEnd(&success, &error_str))) {
|
|
TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL,
|
|
"Compile stream end failed: "
|
|
"reply not received from PNaCl translator "
|
|
"(it probably crashed)");
|
|
return;
|
|
}
|
|
if (!success) {
|
|
TranslateFailed(PP_NACL_ERROR_PNACL_LLC_INTERNAL, error_str);
|
|
return;
|
|
}
|
|
compile_time_ =
|
|
(base::TimeTicks::Now() - do_compile_start_time).InMicroseconds();
|
|
nacl::PPBNaClPrivate::LogTranslateTime("NaCl.Perf.PNaClLoadTime.CompileTime",
|
|
compile_time_);
|
|
nacl::PPBNaClPrivate::LogTranslateTime(
|
|
pnacl_options_->use_subzero
|
|
? "NaCl.Perf.PNaClLoadTime.CompileTime.Subzero"
|
|
: "NaCl.Perf.PNaClLoadTime.CompileTime.LLC",
|
|
compile_time_);
|
|
|
|
// Shut down the compiler subprocess.
|
|
{
|
|
base::AutoLock lock(subprocess_mu_);
|
|
compiler_subprocess_active_ = false;
|
|
}
|
|
|
|
core->CallOnMainThread(0, compile_finished_callback_, PP_OK);
|
|
}
|
|
|
|
void PnaclTranslateThread::LinkThread::Run() {
|
|
pnacl_translate_thread_->DoLink();
|
|
}
|
|
|
|
void PnaclTranslateThread::DoLink() {
|
|
{
|
|
base::AutoLock lock(subprocess_mu_);
|
|
// If the main thread asked us to exit in between starting the thread
|
|
// and now, just leave now.
|
|
if (!ld_subprocess_active_)
|
|
return;
|
|
}
|
|
|
|
// Reset object files for reading first. We do this before duplicating
|
|
// handles/FDs to prevent any handle/FD leaks in case any of the Seek()
|
|
// calls fail.
|
|
for (base::File& obj_file : *obj_files_) {
|
|
if (obj_file.Seek(base::File::FROM_BEGIN, 0) != 0) {
|
|
TranslateFailed(PP_NACL_ERROR_PNACL_LD_SETUP,
|
|
"Link process could not reset object file");
|
|
return;
|
|
}
|
|
}
|
|
|
|
ppapi::proxy::SerializedHandle nexe_file =
|
|
GetHandleForSubprocess(nexe_file_, PP_FILEOPENFLAG_WRITE);
|
|
std::vector<ppapi::proxy::SerializedHandle> ld_input_files;
|
|
for (base::File& obj_file : *obj_files_) {
|
|
ld_input_files.push_back(
|
|
GetHandleForSubprocess(&obj_file, PP_FILEOPENFLAG_READ));
|
|
}
|
|
|
|
base::TimeTicks link_start_time = base::TimeTicks::Now();
|
|
bool success = false;
|
|
bool sent = ld_channel_filter_->Send(
|
|
new PpapiMsg_PnaclTranslatorLink(ld_input_files, nexe_file, &success));
|
|
if (!sent) {
|
|
TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL,
|
|
"link failed: reply not received from linker.");
|
|
return;
|
|
}
|
|
if (!success) {
|
|
TranslateFailed(PP_NACL_ERROR_PNACL_LD_INTERNAL,
|
|
"link failed: linker returned failure status.");
|
|
return;
|
|
}
|
|
|
|
nacl::PPBNaClPrivate::LogTranslateTime(
|
|
"NaCl.Perf.PNaClLoadTime.LinkTime",
|
|
(base::TimeTicks::Now() - link_start_time).InMicroseconds());
|
|
|
|
// Shut down the ld subprocess.
|
|
{
|
|
base::AutoLock lock(subprocess_mu_);
|
|
ld_subprocess_active_ = false;
|
|
}
|
|
|
|
pp::Core* core = pp::Module::Get()->core();
|
|
core->CallOnMainThread(0, report_translate_finished_, PP_OK);
|
|
}
|
|
|
|
void PnaclTranslateThread::TranslateFailed(
|
|
PP_NaClError err_code,
|
|
const std::string& error_string) {
|
|
pp::Core* core = pp::Module::Get()->core();
|
|
if (coordinator_error_info_->message().empty()) {
|
|
// Only use our message if one hasn't already been set by the coordinator
|
|
// (e.g. pexe load failed).
|
|
coordinator_error_info_->SetReport(err_code,
|
|
std::string("PnaclCoordinator: ") +
|
|
error_string);
|
|
}
|
|
core->CallOnMainThread(0, report_translate_finished_, PP_ERROR_FAILED);
|
|
}
|
|
|
|
void PnaclTranslateThread::AbortSubprocesses() {
|
|
{
|
|
base::AutoLock lock(subprocess_mu_);
|
|
if (compiler_subprocess_ && compiler_subprocess_active_) {
|
|
// We only run the service_runtime's Shutdown and do not run the
|
|
// NaClSubprocess Shutdown, which would otherwise nullify some
|
|
// pointers that could still be in use (srpc_client, etc.).
|
|
compiler_subprocess_->service_runtime()->Shutdown();
|
|
compiler_subprocess_active_ = false;
|
|
}
|
|
if (ld_subprocess_ && ld_subprocess_active_) {
|
|
ld_subprocess_->service_runtime()->Shutdown();
|
|
ld_subprocess_active_ = false;
|
|
}
|
|
}
|
|
base::AutoLock lock(cond_mu_);
|
|
done_ = true;
|
|
// Free all buffered bitcode chunks
|
|
data_buffers_.clear();
|
|
buffer_cond_.Signal();
|
|
}
|
|
|
|
PnaclTranslateThread::~PnaclTranslateThread() {
|
|
AbortSubprocesses();
|
|
if (translate_thread_)
|
|
translate_thread_->Join();
|
|
}
|
|
|
|
} // namespace plugin
|