Commit fd07ad08 by Jim Stichnoth

Subzero: Allow overriding command-line args from the browser.

In the browser build only, allows arguments to be explicitly passed to pnacl-sz, in two ways: 1. The SZARGFILE envvar contains the name of a file with arguments, one per line. For each line, initial whitespace is ignored, and lines starting with the '#' comment character are also ignored. 2. The SZARGLIST envvar contains all the arguments, separated by the '|' character. Chrome needs to be started with special options to allow the envvars to be passed through, and also to allow access to the local file system. In addition, specifying "-log=/dev/stderr" or "-o /dev/stderr" gets mapped to std::cerr, in the same way "-" gets mapped to std::cout. BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4370 R=jpp@chromium.org Review URL: https://codereview.chromium.org/1903553004 .
parent 3d3507ce
......@@ -15,6 +15,7 @@
// Can only compile this with the NaCl compiler (needs irt.h, and the
// unsandboxed LLVM build using the trusted compiler does not have irt.h).
#include "IceBrowserCompileServer.h"
#include "IceRangeSpec.h"
#if PNACL_BROWSER_TRANSLATOR
......@@ -27,6 +28,7 @@
#include "llvm/Support/QueueStreamer.h"
#include <cstring>
#include <fstream>
#include <irt.h>
#include <irt_dev.h>
#include <pthread.h>
......@@ -48,6 +50,60 @@ void getIRTInterfaces() {
llvm::report_fatal_error("Failed to get translator compile IRT interface");
}
// Allow pnacl-sz arguments to be supplied externally, instead of coming from
// the browser. This is meant to be used for debugging.
//
// If the SZARGFILE environment variable is set to a file name, arguments are
// read from that file, one argument per line. This requires setting 3
// environment variables before starting the browser:
//
// NACL_ENV_PASSTHROUGH=NACL_DANGEROUS_ENABLE_FILE_ACCESS,NACLENV_SZARGFILE
// NACL_DANGEROUS_ENABLE_FILE_ACCESS=1
// NACLENV_SZARGFILE=/path/to/myargs.txt
//
// In addition, Chrome needs to be launched with the "--no-sandbox" argument.
//
// If the SZARGLIST environment variable is set, arguments are extracted from
// that variable's value, separated by the '|' character (being careful to
// escape/quote special shell characters). This requires setting 2 environment
// variables before starting the browser:
//
// NACL_ENV_PASSTHROUGH=NACLENV_SZARGLIST
// NACLENV_SZARGLIST=arg
//
// This does not require the "--no-sandbox" argument, and is therefore much
// safer, but does require restarting the browser to change the arguments.
//
// If external arguments are supplied, the browser's NumThreads specification is
// ignored, to allow -threads to be specified as an external argument. Note
// that the browser normally supplies the "-O2" argument, so externally supplied
// arguments might want to provide an explicit -O argument.
std::vector<std::string> getExternalArgs() {
std::vector<std::string> ExternalArgs;
if (BuildDefs::minimal())
return ExternalArgs;
char ArgsFileVar[] = "SZARGFILE";
char ArgsListVar[] = "SZARGLIST";
if (const char *ArgsFilename = getenv(ArgsFileVar)) {
std::ifstream ArgsStream(ArgsFilename);
std::string Arg;
while (ArgsStream >> std::ws, std::getline(ArgsStream, Arg)) {
if (!Arg.empty() && Arg[0] == '#')
continue;
ExternalArgs.emplace_back(Arg);
}
if (ExternalArgs.empty()) {
llvm::report_fatal_error("Failed to read arguments from file '" +
std::string(ArgsFilename) + "'");
}
} else if (const char *ArgsList = getenv(ArgsListVar)) {
// Leverage the RangeSpec tokenizer.
auto Args = RangeSpec::tokenize(ArgsList, '|');
ExternalArgs.insert(ExternalArgs.end(), Args.begin(), Args.end());
}
return ExternalArgs;
}
char *onInitCallback(uint32_t NumThreads, int *ObjFileFDs,
size_t ObjFileFDCount, char **CLArgs, size_t CLArgsLen) {
if (ObjFileFDCount < 1) {
......@@ -65,16 +121,27 @@ char *onInitCallback(uint32_t NumThreads, int *ObjFileFDs,
return strdup(StrBuf.str().c_str());
}
// CLArgs is almost an "argv", but is missing the argv[0] program name.
std::vector<char *> Argv;
char ProgramName[] = "pnacl-sz.nexe";
std::vector<const char *> Argv;
constexpr static char ProgramName[] = "pnacl-sz.nexe";
Argv.reserve(CLArgsLen + 1);
Argv.push_back(ProgramName);
for (size_t i = 0; i < CLArgsLen; ++i) {
Argv.push_back(CLArgs[i]);
bool UseNumThreadsFromBrowser = true;
auto ExternalArgs = getExternalArgs();
if (ExternalArgs.empty()) {
for (size_t i = 0; i < CLArgsLen; ++i) {
Argv.push_back(CLArgs[i]);
}
} else {
for (auto &Arg : ExternalArgs) {
Argv.emplace_back(Arg.c_str());
}
UseNumThreadsFromBrowser = false;
}
// NOTE: strings pointed to by argv are owned by the caller, but we parse
// here before returning and don't store them.
gCompileServer->getParsedFlags(NumThreads, Argv.size(), Argv.data());
gCompileServer->getParsedFlags(UseNumThreadsFromBrowser, NumThreads,
Argv.size(), Argv.data());
gCompileServer->startCompileThread(ObjFileFD);
return nullptr;
}
......@@ -162,12 +229,14 @@ void BrowserCompileServer::run() {
gIRTFuncs.serve_translate_request(&SubzeroCallbacks);
}
void BrowserCompileServer::getParsedFlags(uint32_t NumThreads, int argc,
char **argv) {
void BrowserCompileServer::getParsedFlags(bool UseNumThreadsFromBrowser,
uint32_t NumThreads, int argc,
const char *const *argv) {
ClFlags::parseFlags(argc, argv);
ClFlags::getParsedClFlags(ClFlags::Flags);
// Set some defaults which aren't specified via the argv string.
ClFlags::Flags.setNumTranslationThreads(NumThreads);
if (UseNumThreadsFromBrowser)
ClFlags::Flags.setNumTranslationThreads(NumThreads);
ClFlags::Flags.setUseSandboxing(true);
ClFlags::Flags.setOutFileType(FT_Elf);
ClFlags::Flags.setTargetArch(getTargetArch());
......@@ -206,8 +275,19 @@ void BrowserCompileServer::endInputStream() { InputStream->SetDone(); }
void BrowserCompileServer::startCompileThread(int ObjFD) {
InputStream = new llvm::QueueStreamer();
LogStream = getOutputStream(STDOUT_FILENO);
bool LogStreamFailure = false;
int LogFD = STDOUT_FILENO;
if (getFlags().getLogFilename() == "/dev/stderr") {
LogFD = STDERR_FILENO;
} else {
LogStreamFailure = true;
}
LogStream = getOutputStream(LogFD);
LogStream->SetUnbuffered();
if (LogStreamFailure) {
*LogStream
<< "Warning: Log file name must be either '-' or '/dev/stderr'\n";
}
EmitStream = getOutputStream(ObjFD);
EmitStream->SetBufferSize(1 << 14);
std::unique_ptr<StringStream> ErrStrm(new StringStream());
......
......@@ -50,7 +50,8 @@ public:
ErrorCode &getErrorCode() final;
/// Parse and set up the flags for compile jobs.
void getParsedFlags(uint32_t NumThreads, int argc, char **argv);
void getParsedFlags(bool UseNumThreadsFromBrowser, uint32_t NumThreads,
int argc, const char *const *argv);
/// Creates the streams + context and starts the compile thread, handing off
/// the streams + context.
......
......@@ -106,7 +106,7 @@ namespace Ice {
ClFlags ClFlags::Flags;
void ClFlags::parseFlags(int argc, char **argv) {
void ClFlags::parseFlags(int argc, const char *const *argv) {
cl::ParseCommandLineOptions(argc, argv);
AppNameObj = argv[0];
}
......
......@@ -82,7 +82,7 @@ public:
///
/// This is done use cl::ParseCommandLineOptions() and the static variables of
/// type cl::opt defined in IceClFlags.cpp
static void parseFlags(int argc, char *argv[]);
static void parseFlags(int argc, const char *const *argv);
/// Reset all configuration options to their nominal values.
void resetClFlags();
......
......@@ -89,6 +89,8 @@ std::unique_ptr<Ostream> makeStream(const std::string &Filename,
std::error_code &EC) {
if (Filename == "-") {
return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cout));
} else if (Filename == "/dev/stderr") {
return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cerr));
} else {
return std::unique_ptr<Ostream>(
new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None));
......
......@@ -36,24 +36,6 @@ bool RangeSpec::HasNames = false;
namespace {
/// Helper function to tokenize a string into a vector of string tokens, given a
/// single delimiter character. An empty string produces an empty token vector.
/// Zero-length tokens are allowed, e.g. ",a,,,b," may tokenize to
/// {"","a","","","b",""}.
std::vector<std::string> tokenize(const std::string &Spec, char Delimiter) {
std::vector<std::string> Tokens;
if (!Spec.empty()) {
std::string::size_type StartPos = 0;
std::string::size_type DelimPos = 0;
while (DelimPos != std::string::npos) {
DelimPos = Spec.find(Delimiter, StartPos);
Tokens.emplace_back(Spec.substr(StartPos, DelimPos - StartPos));
StartPos = DelimPos + 1;
}
}
return Tokens;
}
/// Helper function to parse "X" or "X:Y" into First and Last.
/// - "X" is treated as "X:X+1".
/// - ":Y" is treated as "0:Y".
......@@ -66,7 +48,7 @@ std::vector<std::string> tokenize(const std::string &Spec, char Delimiter) {
/// report_fatal_error is called.
void getRange(const std::string &Token, uint32_t *First, uint32_t *Last) {
bool Error = false;
auto Tokens = tokenize(Token, RangeSpec::DELIM_RANGE);
auto Tokens = RangeSpec::tokenize(Token, RangeSpec::DELIM_RANGE);
if (Tokens.size() == 1) {
*First = std::stoul(Tokens[0]);
*Last = *First + 1;
......@@ -112,6 +94,21 @@ void record(const std::string &Token, RangeSpec::Desc *D) {
} // end of anonymous namespace
std::vector<std::string> RangeSpec::tokenize(const std::string &Spec,
char Delimiter) {
std::vector<std::string> Tokens;
if (!Spec.empty()) {
std::string::size_type StartPos = 0;
std::string::size_type DelimPos = 0;
while (DelimPos != std::string::npos) {
DelimPos = Spec.find(Delimiter, StartPos);
Tokens.emplace_back(Spec.substr(StartPos, DelimPos - StartPos));
StartPos = DelimPos + 1;
}
}
return Tokens;
}
/// Initialize the RangeSpec with the given string. Calling init multiple times
/// (e.g. init("A");init("B");) is equivalent to init("A,B"); .
void RangeSpec::init(const std::string &Spec) {
......
......@@ -59,6 +59,12 @@ public:
// matching on function name works correctly. Note that this is not
// thread-safe, so we count on all this being handled by the startup thread.
static bool hasNames() { return HasNames; }
// Helper function to tokenize a string into a vector of string tokens, given
// a single delimiter character. An empty string produces an empty token
// vector. Zero-length tokens are allowed, e.g. ",a,,,b," may tokenize to
// {"","a","","","b",""}.
static std::vector<std::string> tokenize(const std::string &Spec,
char Delimiter);
private:
void include(const std::string &Token);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment