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;
}