Flesh out the logging implementation.

Add PLOG and ensure we have CHECK_STREQ and CHECK_STRNE to go with
the existing non-functional DCHECK_STREQ and DCHECK_STRNE.

This also gives us two different logging implementations, one for
the host that just uses std::cerr, and one for Android that uses
the Android log.

Also bring in the StringPrintf family.

Change-Id: I8e190c2c58f26b22ee76a2a87d447df6eb0fa73b
diff --git a/build/Android.common.mk b/build/Android.common.mk
index bf442a3..ac9553e 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -50,15 +50,18 @@
 	src/raw_dex_file.cc \
 	src/runtime.cc \
 	src/stringpiece.cc \
+	src/stringprintf.cc \
 	src/thread.cc
 
 LIBART_TARGET_SRC_FILES := \
 	$(LIBART_COMMON_SRC_FILES) \
-	src/assembler_arm.cc
+	src/assembler_arm.cc \
+	src/logging_android.cc
 
 LIBART_HOST_SRC_FILES := \
 	$(LIBART_COMMON_SRC_FILES) \
-	src/assembler_x86.cc
+	src/assembler_x86.cc \
+	src/logging_linux.cc
 
 TEST_COMMON_SRC_FILES := \
 	src/class_linker_test.cc \
diff --git a/src/log_severity.h b/src/log_severity.h
new file mode 100644
index 0000000..d9ac925
--- /dev/null
+++ b/src/log_severity.h
@@ -0,0 +1,11 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: enh@google.com (Elliott Hughes)
+
+#ifndef BASE_LOG_SEVERITY_H_
+#define BASE_LOG_SEVERITY_H_
+
+typedef int LogSeverity;
+
+const int INFO = 0, WARNING = 1, ERROR = 2, FATAL = 3, NUM_SEVERITIES = 4;
+
+#endif  // BASE_LOG_SEVERITY_H_
diff --git a/src/logging.h b/src/logging.h
index e1b99d6..24ffdbc 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -14,18 +14,35 @@
 #ifndef ART_SRC_LOGGING_H_
 #define ART_SRC_LOGGING_H_
 
-#include <cstdlib>
+#include <cerrno>
+#include <cstring>
 #include <iostream>  // NOLINT
-#include "src/macros.h"
+#include <sstream>
+#include "log_severity.h"
+#include "macros.h"
 
 #define CHECK(x) \
-  if (!(x)) LogMessageFatal(__FILE__, __LINE__).stream() << "Check failed: " #x
+  if (!(x)) \
+    LogMessage(__FILE__, __LINE__, FATAL, -1).stream() << "Check failed: " #x
+
 #define CHECK_EQ(x, y) CHECK((x) == (y))
 #define CHECK_NE(x, y) CHECK((x) != (y))
 #define CHECK_LE(x, y) CHECK((x) <= (y))
 #define CHECK_LT(x, y) CHECK((x) < (y))
 #define CHECK_GE(x, y) CHECK((x) >= (y))
 #define CHECK_GT(x, y) CHECK((x) > (y))
+#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
+#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
+
+#define CHECK_STROP(s1, s2, sense) \
+  do { \
+    if ((strcmp(s1, s2) == 0) != sense) { \
+      LOG(FATAL) << "Check failed: " \
+                 << "\"" << s1 << "\"" \
+                 << (sense ? " == " : " != ") \
+                 << "\"" << s2 << "\""; \
+    } \
+  } while (false)
 
 #ifndef NDEBUG
 
@@ -36,6 +53,8 @@
 #define DCHECK_LT(x, y) CHECK_LT(x, y)
 #define DCHECK_GE(x, y) CHECK_GE(x, y)
 #define DCHECK_GT(x, y) CHECK_GT(x, y)
+#define DCHECK_STREQ(s1, s2) CHECK_STREQ(s1, s2)
+#define DCHECK_STRNE(s1, s2) CHECK_STRNE(s1, s2)
 
 #else  // NDEBUG
 
@@ -71,33 +90,29 @@
   while (false) \
     CHECK_STREQ(str1, str2)
 
+#define DCHECK_STRNE(str1, str2) \
+  while (false) \
+    CHECK_STRNE(str1, str2)
+
 #endif
 
-#define LOG_INFO LogMessage(__FILE__, __LINE__)
-#define LOG_WARN LOG_INFO  // TODO
-#define LOG_FATAL LogMessageFatal(__FILE__, __LINE__)
-#define LOG(severity) LOG_ ## severity.stream()
+#define LOG(severity) LogMessage(__FILE__, __LINE__, severity, -1).stream()
+#define PLOG(severity) LogMessage(__FILE__, __LINE__, severity, errno).stream()
+
 #define LG LOG(INFO)
 
 class LogMessage {
  public:
-  LogMessage(const char* file, int line) {}
-  ~LogMessage() { std::cerr << "\n"; }
-  std::ostream& stream() { return std::cerr; }
- private:
-  DISALLOW_COPY_AND_ASSIGN(LogMessage);
-};
+  LogMessage(const char* file, int line, LogSeverity severity, int error);
+  ~LogMessage();
+  std::ostream& stream();
 
-class LogMessageFatal : public LogMessage {
- public:
-  LogMessageFatal(const char* file, int line)
-    : LogMessage(file, line) {}
-  ~LogMessageFatal() {
-    std::cerr << "\n";
-    std::abort();
-  }
  private:
-  DISALLOW_COPY_AND_ASSIGN(LogMessageFatal);
+  std::stringstream buffer_;
+  LogSeverity severity_;
+  int errno_;
+
+  DISALLOW_COPY_AND_ASSIGN(LogMessage);
 };
 
 #endif  // ART_SRC_LOGGING_H_
diff --git a/src/logging_android.cc b/src/logging_android.cc
new file mode 100644
index 0000000..9d97dbb
--- /dev/null
+++ b/src/logging_android.cc
@@ -0,0 +1,33 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: enh@google.com (Elliott Hughes)
+
+#include "logging.h"
+
+#include <iostream>
+#include <unistd.h>
+
+#include "cutils/log.h"
+
+static const int kLogSeverityToAndroidLogPriority[] = {
+  ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL
+};
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity, int error)
+: severity_(severity), errno_(error)
+{
+}
+
+LogMessage::~LogMessage() {
+  if (errno_ != -1) {
+    stream() << ": " << strerror(errno);
+  }
+  int priority = kLogSeverityToAndroidLogPriority[severity_];
+  LOG_PRI(priority, LOG_TAG, "%s", buffer_.str().c_str());
+  if (severity_ == FATAL) {
+    abort();  // TODO: dvmAbort
+  }
+}
+
+std::ostream& LogMessage::stream() {
+  return buffer_;
+}
diff --git a/src/logging_linux.cc b/src/logging_linux.cc
new file mode 100644
index 0000000..9feceea
--- /dev/null
+++ b/src/logging_linux.cc
@@ -0,0 +1,69 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: enh@google.com (Elliott Hughes)
+
+#include "logging.h"
+
+#include "scoped_ptr.h"
+#include "stringprintf.h"
+
+#include <cstdio>
+#include <cstring>
+#include <execinfo.h>
+#include <iostream>
+#include <sys/types.h>
+#include <unistd.h>
+
+// glibc doesn't expose gettid(2).
+#define __KERNEL__
+# include <linux/unistd.h>
+#ifdef _syscall0
+_syscall0(pid_t,gettid)
+#else
+pid_t gettid() { return syscall(__NR_gettid);}
+#endif
+#undef __KERNEL__
+
+static void dumpStackTrace(std::ostream& os) {
+  // Get the raw stack frames.
+  size_t MAX_STACK_FRAMES = 64;
+  void* stack_frames[MAX_STACK_FRAMES];
+  size_t frame_count = backtrace(stack_frames, MAX_STACK_FRAMES);
+
+  // Turn them into something human-readable with symbols.
+  // TODO: in practice, we may find that we should use backtrace_symbols_fd
+  // to avoid allocation, rather than use our own custom formatting.
+  art::scoped_ptr_malloc<char*> strings(backtrace_symbols(stack_frames, frame_count));
+  if (strings.get() == NULL) {
+    os << "backtrace_symbols failed: " << strerror(errno) << std::endl;
+    return;
+  }
+
+  for (size_t i = 0; i < frame_count; ++i) {
+    os << StringPrintf("\t#%02d %s", i, strings.get()[i]) << std::endl;
+  }
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity, int error)
+: severity_(severity), errno_(error)
+{
+  const char* last_slash = strrchr(file, '/');
+  const char* leaf = (last_slash == NULL) ? file : last_slash + 1;
+  stream() << StringPrintf("%c %5d %5d %s:%d] ",
+    "IWEF"[severity], getpid(), gettid(), leaf, line);
+}
+
+LogMessage::~LogMessage() {
+  if (errno_ != -1) {
+    stream() << ": " << strerror(errno);
+  }
+  stream() << std::endl;
+  if (severity_ == FATAL) {
+    stream() << "Aborting:" << std::endl;
+    dumpStackTrace(stream());
+    abort();
+  }
+}
+
+std::ostream& LogMessage::stream() {
+  return std::cerr;
+}
diff --git a/src/raw_dex_file.cc b/src/raw_dex_file.cc
index 2d97a9a..2aaa5d9 100644
--- a/src/raw_dex_file.cc
+++ b/src/raw_dex_file.cc
@@ -2,7 +2,6 @@
 
 #include "src/raw_dex_file.h"
 
-#include <errno.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/mman.h>
@@ -30,7 +29,7 @@
   };
   virtual ~MmapCloser() {
     if (munmap(addr_, length_) == -1) {
-      LG << "munmap: " << strerror(errno);  // TODO: PLOG
+      PLOG(INFO) << "munmap failed";
     }
   }
  private:
@@ -53,20 +52,20 @@
   CHECK(filename != NULL);
   int fd = open(filename, O_RDONLY);  // TODO: scoped_fd
   if (fd == -1) {
-    LG << "open: " << strerror(errno);  // TODO: PLOG
+    PLOG(ERROR) << "open(\"" << filename << "\", O_RDONLY) failed";
     return NULL;
   }
   struct stat sbuf;
   memset(&sbuf, 0, sizeof(sbuf));
   if (fstat(fd, &sbuf) == -1) {
-    LG << "fstat: " << strerror(errno);  // TODO: PLOG
+    PLOG(ERROR) << "fstat \"" << filename << "\" failed";
     close(fd);
     return NULL;
   }
   size_t length = sbuf.st_size;
   void* addr = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0);
   if (addr == MAP_FAILED) {
-    LG << "mmap: " << strerror(errno);  // TODO: PLOG
+    PLOG(ERROR) << "mmap \"" << filename << "\" failed";
     close(fd);
     return NULL;
   }
@@ -127,20 +126,20 @@
 bool RawDexFile::CheckMagic(const byte* magic) {
   CHECK(magic != NULL);
   if (memcmp(magic, kDexMagic, sizeof(kDexMagic)) != 0) {
-    LOG(WARN) << "Unrecognized magic number:"
-              << " " << magic[0]
-              << " " << magic[1]
-              << " " << magic[2]
-              << " " << magic[3];
+    LOG(WARNING) << "Unrecognized magic number:"
+            << " " << magic[0]
+            << " " << magic[1]
+            << " " << magic[2]
+            << " " << magic[3];
     return false;
   }
   const byte* version = &magic[sizeof(kDexMagic)];
   if (memcmp(version, kDexMagicVersion, sizeof(kDexMagicVersion)) != 0) {
-    LOG(WARN) << "Unrecognized version number:"
-              << " " << version[0]
-              << " " << version[1]
-              << " " << version[2]
-              << " " << version[3];
+    LOG(WARNING) << "Unrecognized version number:"
+            << " " << version[0]
+            << " " << version[1]
+            << " " << version[2]
+            << " " << version[3];
     return false;
   }
   return true;
diff --git a/src/stringprintf.cc b/src/stringprintf.cc
new file mode 100644
index 0000000..4f59fc1
--- /dev/null
+++ b/src/stringprintf.cc
@@ -0,0 +1,64 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: enh@google.com (Elliott Hughes)
+
+#include "stringprintf.h"
+
+#include <stdio.h>
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+  // First try with a small fixed size buffer
+  char space[1024];
+
+  // It's possible for methods that use a va_list to invalidate
+  // the data in it upon use.  The fix is to make a copy
+  // of the structure before using it and use that copy instead.
+  va_list backup_ap;
+  va_copy(backup_ap, ap);
+  int result = vsnprintf(space, sizeof(space), format, backup_ap);
+  va_end(backup_ap);
+
+  if (result < int(sizeof(space))) {
+    if (result >= 0) {
+      // Normal case -- everything fit.
+      dst->append(space, result);
+      return;
+    }
+
+    if (result < 0) {
+      // Just an error.
+      return;
+    }
+  }
+
+  // Increase the buffer size to the size requested by vsnprintf,
+  // plus one for the closing \0.
+  int length = result+1;
+  char* buf = new char[length];
+
+  // Restore the va_list before we use it again
+  va_copy(backup_ap, ap);
+  result = vsnprintf(buf, length, format, backup_ap);
+  va_end(backup_ap);
+
+  if (result >= 0 && result < length) {
+    // It fit
+    dst->append(buf, result);
+  }
+  delete[] buf;
+}
+
+std::string StringPrintf(const char* fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  std::string result;
+  StringAppendV(&result, fmt, ap);
+  va_end(ap);
+  return result;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  StringAppendV(dst, format, ap);
+  va_end(ap);
+}
diff --git a/src/stringprintf.h b/src/stringprintf.h
new file mode 100644
index 0000000..b1b1d8d
--- /dev/null
+++ b/src/stringprintf.h
@@ -0,0 +1,21 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: enh@google.com (Elliott Hughes)
+
+#ifndef STRINGPRINTF_H_
+#define STRINGPRINTF_H_
+
+#include <stdarg.h>
+#include <string>
+
+// Returns a string corresponding to printf-like formatting of the arguments.
+std::string StringPrintf(const char* fmt, ...)
+        __attribute__((__format__ (__printf__, 1, 2)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendF(std::string* dst, const char* fmt, ...)
+        __attribute__((__format__ (__printf__, 2, 3)));
+
+// Appends a printf-like formatting of the arguments to 'dst'.
+void StringAppendV(std::string* dst, const char* format, va_list ap);
+
+#endif  // STRINGPRINTF_H_
diff --git a/src/thread.cc b/src/thread.cc
index 9320fcf..40a3f0e 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -3,8 +3,8 @@
 #include "src/thread.h"
 
 #include <algorithm>
+#include <cerrno>
 #include <list>
-#include <errno.h>
 #include <pthread.h>
 #include <sys/mman.h>
 
@@ -107,13 +107,13 @@
 bool Thread::Init() {
   // Allocate a TLS slot.
   if (pthread_key_create(&Thread::pthread_key_self_, ThreadExitCheck) != 0) {
-    LOG(WARN) << "pthread_key_create failed";
+    PLOG(WARNING) << "pthread_key_create failed";
     return false;
   }
 
   // Double-check the TLS slot allocation.
   if (pthread_getspecific(pthread_key_self_) != NULL) {
-    LOG(WARN) << "newly-created pthread TLS slot is not NULL";
+    LOG(WARNING) << "newly-created pthread TLS slot is not NULL";
     return false;
   }