First attempt to capture parser/translation errors in browser.

Adds a notion of an (optional) error stream to the existing
log and emit streams. If not specified, the log stream is used.
Error messages in parser/translation are sent to this new error
stream.

In the browser compiler server, a separate error (string) stream is
created to capture errors. Method onEndCallBack returns the contents
of the error stream (if non-empty) instead of a generic error message.

BUG= https://code.google.com/p/nativeclient/issues/detail?id=4138
R=jvoung@chromium.org

Review URL: https://codereview.chromium.org/1052833003
diff --git a/src/IceBrowserCompileServer.cpp b/src/IceBrowserCompileServer.cpp
index 85c2efe..92d42e2 100644
--- a/src/IceBrowserCompileServer.cpp
+++ b/src/IceBrowserCompileServer.cpp
@@ -81,7 +81,8 @@
   // TODO(jvoung): Also return an error string, and UMA data.
   // Set up a report_fatal_error handler to grab that string.
   if (gCompileServer->getErrorCode().value()) {
-    return strdup("Some error occurred");
+    const std::string Error = gCompileServer->getErrorStream().getContents();
+    return strdup(Error.empty() ? "Some error occurred" : Error.c_str());
   }
   return nullptr;
 }
@@ -142,9 +143,12 @@
   LogStream->SetUnbuffered();
   EmitStream = getOutputStream(ObjFD);
   EmitStream->SetBufferSize(1 << 14);
+  std::unique_ptr<StringStream> ErrStrm(new StringStream());
+  ErrorStream = std::move(ErrStrm);
   ELFStream.reset(new ELFStreamer(*EmitStream.get()));
   Ctx.reset(new GlobalContext(LogStream.get(), EmitStream.get(),
-                              ELFStream.get(), Flags));
+                              &ErrorStream->getStream(), ELFStream.get(),
+                              Flags));
   CompileThread = std::thread([this]() {
     Ctx->initParserThread();
     this->getCompiler().run(ExtraFlags, *Ctx.get(),
diff --git a/src/IceBrowserCompileServer.h b/src/IceBrowserCompileServer.h
index 1ef8121..a34a03d 100644
--- a/src/IceBrowserCompileServer.h
+++ b/src/IceBrowserCompileServer.h
@@ -39,6 +39,7 @@
   BrowserCompileServer() = delete;
   BrowserCompileServer(const BrowserCompileServer &) = delete;
   BrowserCompileServer &operator=(const BrowserCompileServer &) = delete;
+  class StringStream;
 
 public:
   explicit BrowserCompileServer(Compiler &Comp)
@@ -74,7 +75,20 @@
     ELFStream.reset(nullptr);
   }
 
+  StringStream &getErrorStream() {
+    return *ErrorStream;
+  }
+
 private:
+  class StringStream {
+  public:
+    StringStream() : StrBuf(Buffer) {}
+    const IceString &getContents() { return StrBuf.str(); }
+    Ostream &getStream() { return StrBuf; }
+  private:
+    std::string Buffer;
+    llvm::raw_string_ostream StrBuf;
+  };
   // This currently only handles a single compile request, hence one copy
   // of the state.
   std::unique_ptr<GlobalContext> Ctx;
@@ -84,6 +98,7 @@
   llvm::QueueStreamer *InputStream;
   std::unique_ptr<Ostream> LogStream;
   std::unique_ptr<llvm::raw_fd_ostream> EmitStream;
+  std::unique_ptr<StringStream> ErrorStream;
   std::unique_ptr<ELFStreamer> ELFStream;
   ClFlags Flags;
   ClFlagsExtra ExtraFlags;
diff --git a/src/IceCompileServer.cpp b/src/IceCompileServer.cpp
index 86ebb0b..cf398d4 100644
--- a/src/IceCompileServer.cpp
+++ b/src/IceCompileServer.cpp
@@ -96,7 +96,8 @@
     return transferErrorCode(getReturnValue(ExtraFlags, Ice::EC_Bitcode));
   }
 
-  Ctx.reset(new GlobalContext(Ls.get(), Os.get(), ELFStr.get(), Flags));
+  Ctx.reset(new GlobalContext(Ls.get(), Os.get(), Ls.get(), ELFStr.get(),
+                              Flags));
   if (Ctx->getFlags().getNumTranslationThreads() != 0) {
     std::thread CompileThread([this, &ExtraFlags, &InputStream]() {
       Ctx->initParserThread();
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index 1c9e3c4..8de57f3 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -213,14 +213,18 @@
   Str << "\n";
 }
 
-GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
+GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
                              ELFStreamer *ELFStr, const ClFlags &Flags)
     : ConstPool(new ConstantPool()), ErrorStatus(), StrDump(OsDump),
-      StrEmit(OsEmit), Flags(Flags), RNG(Flags.getRandomSeed()), ObjectWriter(),
+      StrEmit(OsEmit), StrError(OsError), Flags(Flags),
+      RNG(Flags.getRandomSeed()), ObjectWriter(),
       OptQ(/*Sequential=*/Flags.isSequential(),
            /*MaxSize=*/Flags.getNumTranslationThreads()),
       // EmitQ is allowed unlimited size.
       EmitQ(/*Sequential=*/Flags.isSequential()) {
+  assert(OsDump && "OsDump is not defined for GlobalContext");
+  assert(OsEmit && "OsEmit is not defined for GlobalContext");
+  assert(OsError && "OsError is not defined for GlobalContext");
   // Make sure thread_local fields are properly initialized before any
   // accesses are made.  Do this here instead of at the start of
   // main() so that all clients (e.g. unit tests) can benefit for
@@ -278,8 +282,8 @@
     if (Func->hasError()) {
       getErrorStatus()->assign(EC_Translation);
       OstreamLocker L(this);
-      getStrDump() << "ICE translation error: " << Func->getFunctionName()
-                   << ": " << Func->getError() << "\n";
+      getStrError() << "ICE translation error: " << Func->getFunctionName()
+                    << ": " << Func->getError() << "\n";
       Item = new EmitterWorkItem(Func->getSequenceNumber());
     } else {
       Func->getAssembler<>()->setInternal(Func->getInternal());
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index 9264d43..a1fd133 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -145,14 +145,17 @@
   };
 
 public:
-  GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer,
-                const ClFlags &Flags);
+  // The dump stream is a log stream while emit is the stream code
+  // is emitted to. The error stream is strictly for logging errors.
+  GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
+                ELFStreamer *ELFStreamer, const ClFlags &Flags);
   ~GlobalContext();
 
-  // The dump and emit streams need to be used by only one thread at a
-  // time.  This is done by exclusively reserving the streams via
-  // lockStr() and unlockStr().  The OstreamLocker class can be used
-  // to conveniently manage this.
+  //
+  // The dump, error, and emit streams need to be used by only one
+  // thread at a time.  This is done by exclusively reserving the
+  // streams via lockStr() and unlockStr().  The OstreamLocker class
+  // can be used to conveniently manage this.
   //
   // The model is that a thread grabs the stream lock, then does an
   // arbitrary amount of work during which far-away callees may grab
@@ -163,6 +166,7 @@
   void lockStr() { StrLock.lock(); }
   void unlockStr() { StrLock.unlock(); }
   Ostream &getStrDump() { return *StrDump; }
+  Ostream &getStrError() { return *StrError; }
   Ostream &getStrEmit() { return *StrEmit; }
 
   LockedPtr<ErrorCode> getErrorStatus() {
@@ -418,6 +422,7 @@
   StrLockType StrLock;
   Ostream *StrDump; // Stream for dumping / diagnostics
   Ostream *StrEmit; // Stream for code emission
+  Ostream *StrError; // Stream for logging errors.
 
   ICE_CACHELINE_BOUNDARY;
 
diff --git a/src/PNaClTranslator.cpp b/src/PNaClTranslator.cpp
index ce46239..42a207b 100644
--- a/src/PNaClTranslator.cpp
+++ b/src/PNaClTranslator.cpp
@@ -488,7 +488,7 @@
   Ice::GlobalContext *Context = Translator.getContext();
   { // Lock while printing out error message.
     Ice::OstreamLocker L(Context);
-    raw_ostream &OldErrStream = setErrStream(Context->getStrDump());
+    raw_ostream &OldErrStream = setErrStream(Context->getStrError());
     NaClBitcodeParser::ErrorAt(Level, Bit, Message);
     setErrStream(OldErrStream);
   }