diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/.clang-format | 11 | ||||
-rw-r--r-- | base/Android.mk | 109 | ||||
-rw-r--r-- | base/CPPLINT.cfg | 2 | ||||
-rw-r--r-- | base/file.cpp | 148 | ||||
-rw-r--r-- | base/file_test.cpp | 106 | ||||
-rw-r--r-- | base/include/base/file.h | 43 | ||||
-rw-r--r-- | base/include/base/logging.h | 297 | ||||
-rw-r--r-- | base/include/base/macros.h | 188 | ||||
-rw-r--r-- | base/include/base/memory.h | 47 | ||||
-rw-r--r-- | base/include/base/stringprintf.h | 40 | ||||
-rw-r--r-- | base/include/base/strings.h | 52 | ||||
-rw-r--r-- | base/logging.cpp | 304 | ||||
-rw-r--r-- | base/logging_test.cpp | 171 | ||||
-rw-r--r-- | base/stringprintf.cpp | 85 | ||||
-rw-r--r-- | base/stringprintf_test.cpp | 60 | ||||
-rw-r--r-- | base/strings.cpp | 124 | ||||
-rw-r--r-- | base/strings_test.cpp | 161 | ||||
-rw-r--r-- | base/test_main.cpp | 25 | ||||
-rw-r--r-- | base/test_utils.cpp | 38 | ||||
-rw-r--r-- | base/test_utils.h | 32 |
20 files changed, 2043 insertions, 0 deletions
diff --git a/base/.clang-format b/base/.clang-format new file mode 100644 index 0000000..2b83a1f --- /dev/null +++ b/base/.clang-format @@ -0,0 +1,11 @@ +BasedOnStyle: Google +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: false + +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +IndentWidth: 2 +PointerAlignment: Left +TabWidth: 2 +UseTab: Never +PenaltyExcessCharacter: 32 diff --git a/base/Android.mk b/base/Android.mk new file mode 100644 index 0000000..ad85c6b --- /dev/null +++ b/base/Android.mk @@ -0,0 +1,109 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +libbase_src_files := \ + file.cpp \ + stringprintf.cpp \ + strings.cpp \ + +libbase_test_src_files := \ + file_test.cpp \ + stringprintf_test.cpp \ + strings_test.cpp \ + test_main.cpp \ + test_utils.cpp \ + +libbase_cppflags := \ + -Wall \ + -Wextra \ + -Werror \ + +# Device +# ------------------------------------------------------------------------------ +include $(CLEAR_VARS) +LOCAL_MODULE := libbase +LOCAL_CLANG := true +LOCAL_SRC_FILES := $(libbase_src_files) logging.cpp +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CPPFLAGS := $(libbase_cppflags) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_STATIC_LIBRARIES := libcutils +LOCAL_MULTILIB := both +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := libbase +LOCAL_CLANG := true +LOCAL_WHOLE_STATIC_LIBRARIES := libbase +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_MULTILIB := both +include $(BUILD_SHARED_LIBRARY) + +# Host +# ------------------------------------------------------------------------------ +include $(CLEAR_VARS) +LOCAL_MODULE := libbase +LOCAL_SRC_FILES := $(libbase_src_files) +ifneq ($(HOST_OS),windows) + LOCAL_SRC_FILES += logging.cpp +endif +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CPPFLAGS := $(libbase_cppflags) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_STATIC_LIBRARIES := libcutils +LOCAL_MULTILIB := both +include $(BUILD_HOST_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := libbase +LOCAL_WHOLE_STATIC_LIBRARIES := libbase +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_STATIC_LIBRARIES := libcutils +LOCAL_MULTILIB := both +include $(BUILD_HOST_SHARED_LIBRARY) + +# Tests +# ------------------------------------------------------------------------------ +include $(CLEAR_VARS) +LOCAL_MODULE := libbase_test +LOCAL_CLANG := true +LOCAL_SRC_FILES := $(libbase_test_src_files) logging_test.cpp +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_CPPFLAGS := $(libbase_cppflags) +LOCAL_SHARED_LIBRARIES := libbase +LOCAL_MULTILIB := both +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +include $(BUILD_NATIVE_TEST) + +include $(CLEAR_VARS) +LOCAL_MODULE := libbase_test +LOCAL_SRC_FILES := $(libbase_test_src_files) +ifneq ($(HOST_OS),windows) + LOCAL_SRC_FILES += logging_test.cpp +endif +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_CPPFLAGS := $(libbase_cppflags) +LOCAL_SHARED_LIBRARIES := libbase +LOCAL_MULTILIB := both +LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32 +LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64 +include $(BUILD_HOST_NATIVE_TEST) diff --git a/base/CPPLINT.cfg b/base/CPPLINT.cfg new file mode 100644 index 0000000..d94a89c --- /dev/null +++ b/base/CPPLINT.cfg @@ -0,0 +1,2 @@ +set noparent +filter=-build/header_guard,-build/include,-build/c++11,-whitespace/operators diff --git a/base/file.cpp b/base/file.cpp new file mode 100644 index 0000000..6b19818 --- /dev/null +++ b/base/file.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/file.h" + +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <string> + +#include "base/macros.h" // For TEMP_FAILURE_RETRY on Darwin. +#define LOG_TAG "base.file" +#include "cutils/log.h" +#include "utils/Compat.h" + +namespace android { +namespace base { + +bool ReadFdToString(int fd, std::string* content) { + content->clear(); + + char buf[BUFSIZ]; + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) { + content->append(buf, n); + } + return (n == 0) ? true : false; +} + +bool ReadFileToString(const std::string& path, std::string* content) { + content->clear(); + + int fd = + TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (fd == -1) { + return false; + } + bool result = ReadFdToString(fd, content); + TEMP_FAILURE_RETRY(close(fd)); + return result; +} + +bool WriteStringToFd(const std::string& content, int fd) { + const char* p = content.data(); + size_t left = content.size(); + while (left > 0) { + ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left)); + if (n == -1) { + return false; + } + p += n; + left -= n; + } + return true; +} + +static bool CleanUpAfterFailedWrite(const std::string& path) { + // Something went wrong. Let's not leave a corrupt file lying around. + int saved_errno = errno; + unlink(path.c_str()); + errno = saved_errno; + return false; +} + +#if !defined(_WIN32) +bool WriteStringToFile(const std::string& content, const std::string& path, + mode_t mode, uid_t owner, gid_t group) { + int fd = TEMP_FAILURE_RETRY( + open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + mode)); + if (fd == -1) { + ALOGE("android::WriteStringToFile open failed: %s", strerror(errno)); + return false; + } + + // We do an explicit fchmod here because we assume that the caller really + // meant what they said and doesn't want the umask-influenced mode. + if (fchmod(fd, mode) == -1) { + ALOGE("android::WriteStringToFile fchmod failed: %s", strerror(errno)); + return CleanUpAfterFailedWrite(path); + } + if (fchown(fd, owner, group) == -1) { + ALOGE("android::WriteStringToFile fchown failed: %s", strerror(errno)); + return CleanUpAfterFailedWrite(path); + } + if (!WriteStringToFd(content, fd)) { + ALOGE("android::WriteStringToFile write failed: %s", strerror(errno)); + return CleanUpAfterFailedWrite(path); + } + TEMP_FAILURE_RETRY(close(fd)); + return true; +} +#endif + +bool WriteStringToFile(const std::string& content, const std::string& path) { + int fd = TEMP_FAILURE_RETRY( + open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + DEFFILEMODE)); + if (fd == -1) { + return false; + } + + bool result = WriteStringToFd(content, fd); + TEMP_FAILURE_RETRY(close(fd)); + return result || CleanUpAfterFailedWrite(path); +} + +bool ReadFully(int fd, void* data, size_t byte_count) { + uint8_t* p = reinterpret_cast<uint8_t*>(data); + size_t remaining = byte_count; + while (remaining > 0) { + ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining)); + if (n <= 0) return false; + p += n; + remaining -= n; + } + return true; +} + +bool WriteFully(int fd, const void* data, size_t byte_count) { + const uint8_t* p = reinterpret_cast<const uint8_t*>(data); + size_t remaining = byte_count; + while (remaining > 0) { + ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining)); + if (n == -1) return false; + p += n; + remaining -= n; + } + return true; +} + +} // namespace base +} // namespace android diff --git a/base/file_test.cpp b/base/file_test.cpp new file mode 100644 index 0000000..e5cf696 --- /dev/null +++ b/base/file_test.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/file.h" + +#include <gtest/gtest.h> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <string> + +#include "test_utils.h" + +TEST(file, ReadFileToString_ENOENT) { + std::string s("hello"); + errno = 0; + ASSERT_FALSE(android::base::ReadFileToString("/proc/does-not-exist", &s)); + EXPECT_EQ(ENOENT, errno); + EXPECT_EQ("", s); // s was cleared. +} + +TEST(file, ReadFileToString_success) { + std::string s("hello"); + ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s)) << errno; + EXPECT_GT(s.length(), 6U); + EXPECT_EQ('\n', s[s.length() - 1]); + s[5] = 0; + EXPECT_STREQ("Linux", s.c_str()); +} + +TEST(file, WriteStringToFile) { + TemporaryFile tf; + ASSERT_TRUE(tf.fd != -1); + ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename)) << errno; + std::string s; + ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno; + EXPECT_EQ("abc", s); +} + +TEST(file, WriteStringToFile2) { + TemporaryFile tf; + ASSERT_TRUE(tf.fd != -1); + ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename, 0660, + getuid(), getgid())) + << errno; + struct stat sb; + ASSERT_EQ(0, stat(tf.filename, &sb)); + ASSERT_EQ(0660U, (sb.st_mode & ~S_IFMT)); + ASSERT_EQ(getuid(), sb.st_uid); + ASSERT_EQ(getgid(), sb.st_gid); + std::string s; + ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno; + EXPECT_EQ("abc", s); +} + +TEST(file, WriteStringToFd) { + TemporaryFile tf; + ASSERT_TRUE(tf.fd != -1); + ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd)); + + ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << errno; + + std::string s; + ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << errno; + EXPECT_EQ("abc", s); +} + +TEST(file, ReadFully) { + int fd = open("/proc/version", O_RDONLY); + ASSERT_NE(-1, fd) << strerror(errno); + + char buf[1024]; + memset(buf, 0, sizeof(buf)); + ASSERT_TRUE(android::base::ReadFully(fd, buf, 5)); + ASSERT_STREQ("Linux", buf); + + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); + + ASSERT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf))); + + close(fd); +} + +TEST(file, WriteFully) { + TemporaryFile tf; + ASSERT_TRUE(tf.fd != -1); + ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3)); + std::string s; + ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno; + EXPECT_EQ("abc", s); +} diff --git a/base/include/base/file.h b/base/include/base/file.h new file mode 100644 index 0000000..acd29b3 --- /dev/null +++ b/base/include/base/file.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_FILE_H +#define BASE_FILE_H + +#include <sys/stat.h> +#include <string> + +namespace android { +namespace base { + +bool ReadFdToString(int fd, std::string* content); +bool ReadFileToString(const std::string& path, std::string* content); + +bool WriteStringToFile(const std::string& content, const std::string& path); +bool WriteStringToFd(const std::string& content, int fd); + +#if !defined(_WIN32) +bool WriteStringToFile(const std::string& content, const std::string& path, + mode_t mode, uid_t owner, gid_t group); +#endif + +bool ReadFully(int fd, void* data, size_t byte_count); +bool WriteFully(int fd, const void* data, size_t byte_count); + +} // namespace base +} // namespace android + +#endif // BASE_FILE_H diff --git a/base/include/base/logging.h b/base/include/base/logging.h new file mode 100644 index 0000000..230adb8 --- /dev/null +++ b/base/include/base/logging.h @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_LOGGING_H +#define BASE_LOGGING_H + +#include <functional> +#include <memory> +#include <ostream> + +#include "base/macros.h" + +namespace android { +namespace base { + +enum LogSeverity { + VERBOSE, + DEBUG, + INFO, + WARNING, + ERROR, + FATAL, +}; + +enum LogId { + DEFAULT, + MAIN, + SYSTEM, +}; + +typedef std::function<void(LogId, LogSeverity, const char*, const char*, + unsigned int, const char*)> LogFunction; + +extern void StderrLogger(LogId, LogSeverity, const char*, const char*, + unsigned int, const char*); + +#ifdef __ANDROID__ +// We expose this even though it is the default because a user that wants to +// override the default log buffer will have to construct this themselves. +class LogdLogger { + public: + explicit LogdLogger(LogId default_log_id = android::base::MAIN); + + void operator()(LogId, LogSeverity, const char* tag, const char* file, + unsigned int line, const char* message); + + private: + LogId default_log_id_; +}; +#endif + +// Configure logging based on ANDROID_LOG_TAGS environment variable. +// We need to parse a string that looks like +// +// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i +// +// The tag (or '*' for the global level) comes first, followed by a colon and a +// letter indicating the minimum priority level we're expected to log. This can +// be used to reveal or conceal logs with specific tags. +extern void InitLogging(char* argv[], LogFunction&& logger); + +// Configures logging using the default logger (logd for the device, stderr for +// the host). +extern void InitLogging(char* argv[]); + +// Replace the current logger. +extern void SetLogger(LogFunction&& logger); + +// Logs a message to logcat on Android otherwise to stderr. If the severity is +// FATAL it also causes an abort. For example: +// +// LOG(FATAL) << "We didn't expect to reach here"; +#define LOG(severity) \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \ + ::android::base::severity, -1).stream() + +// Logs a message to logcat with the specified log ID on Android otherwise to +// stderr. If the severity is FATAL it also causes an abort. +#define LOG_TO(dest, severity) \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \ + ::android::base::severity, -1).stream() + +// A variant of LOG that also logs the current errno value. To be used when +// library calls fail. +#define PLOG(severity) \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \ + ::android::base::severity, errno).stream() + +// Behaves like PLOG, but logs to the specified log ID. +#define PLOG_TO(dest, severity) \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \ + ::android::base::severity, errno).stream() + +// Marker that code is yet to be implemented. +#define UNIMPLEMENTED(level) \ + LOG(level) << __PRETTY_FUNCTION__ << " unimplemented " + +// Check whether condition x holds and LOG(FATAL) if not. The value of the +// expression x is only evaluated once. Extra logging can be appended using << +// after. For example: +// +// CHECK(false == true) results in a log message of +// "Check failed: false == true". +#define CHECK(x) \ + if (UNLIKELY(!(x))) \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \ + ::android::base::FATAL, -1).stream() \ + << "Check failed: " #x << " " + +// Helper for CHECK_xx(x,y) macros. +#define CHECK_OP(LHS, RHS, OP) \ + for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \ + UNLIKELY(!(_values.lhs OP _values.rhs)); \ + /* empty */) \ + ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \ + ::android::base::FATAL, -1).stream() \ + << "Check failed: " << #LHS << " " << #OP << " " << #RHS \ + << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") " + +// Check whether a condition holds between x and y, LOG(FATAL) if not. The value +// of the expressions x and y is evaluated once. Extra logging can be appended +// using << after. For example: +// +// CHECK_NE(0 == 1, false) results in +// "Check failed: false != false (0==1=false, false=false) ". +#define CHECK_EQ(x, y) CHECK_OP(x, y, == ) +#define CHECK_NE(x, y) CHECK_OP(x, y, != ) +#define CHECK_LE(x, y) CHECK_OP(x, y, <= ) +#define CHECK_LT(x, y) CHECK_OP(x, y, < ) +#define CHECK_GE(x, y) CHECK_OP(x, y, >= ) +#define CHECK_GT(x, y) CHECK_OP(x, y, > ) + +// Helper for CHECK_STRxx(s1,s2) macros. +#define CHECK_STROP(s1, s2, sense) \ + if (UNLIKELY((strcmp(s1, s2) == 0) != sense)) \ + LOG(FATAL) << "Check failed: " \ + << "\"" << s1 << "\"" \ + << (sense ? " == " : " != ") << "\"" << s2 << "\"" + +// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not. +#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true) +#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false) + +// Perform the pthread function call(args), LOG(FATAL) on error. +#define CHECK_PTHREAD_CALL(call, args, what) \ + do { \ + int rc = call args; \ + if (rc != 0) { \ + errno = rc; \ + PLOG(FATAL) << #call << " failed for " << what; \ + } \ + } while (false) + +// CHECK that can be used in a constexpr function. For example: +// +// constexpr int half(int n) { +// return +// DCHECK_CONSTEXPR(n >= 0, , 0) +// CHECK_CONSTEXPR((n & 1) == 0), +// << "Extra debugging output: n = " << n, 0) +// n / 2; +// } +#define CHECK_CONSTEXPR(x, out, dummy) \ + (UNLIKELY(!(x))) \ + ? (LOG(FATAL) << "Check failed: " << #x out, dummy) \ + : + +// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally +// CHECK should be used unless profiling identifies a CHECK as being in +// performance critical code. +#if defined(NDEBUG) +static constexpr bool kEnableDChecks = false; +#else +static constexpr bool kEnableDChecks = true; +#endif + +#define DCHECK(x) \ + if (::android::base::kEnableDChecks) CHECK(x) +#define DCHECK_EQ(x, y) \ + if (::android::base::kEnableDChecks) CHECK_EQ(x, y) +#define DCHECK_NE(x, y) \ + if (::android::base::kEnableDChecks) CHECK_NE(x, y) +#define DCHECK_LE(x, y) \ + if (::android::base::kEnableDChecks) CHECK_LE(x, y) +#define DCHECK_LT(x, y) \ + if (::android::base::kEnableDChecks) CHECK_LT(x, y) +#define DCHECK_GE(x, y) \ + if (::android::base::kEnableDChecks) CHECK_GE(x, y) +#define DCHECK_GT(x, y) \ + if (::android::base::kEnableDChecks) CHECK_GT(x, y) +#define DCHECK_STREQ(s1, s2) \ + if (::android::base::kEnableDChecks) CHECK_STREQ(s1, s2) +#define DCHECK_STRNE(s1, s2) \ + if (::android::base::kEnableDChecks) CHECK_STRNE(s1, s2) +#if defined(NDEBUG) +#define DCHECK_CONSTEXPR(x, out, dummy) +#else +#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy) +#endif + +// Temporary class created to evaluate the LHS and RHS, used with +// MakeEagerEvaluator to infer the types of LHS and RHS. +template <typename LHS, typename RHS> +struct EagerEvaluator { + EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) { + } + LHS lhs; + RHS rhs; +}; + +// Helper function for CHECK_xx. +template <typename LHS, typename RHS> +static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) { + return EagerEvaluator<LHS, RHS>(lhs, rhs); +} + +// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated +// as strings. To compare strings use CHECK_STREQ and CHECK_STRNE. We rely on +// signed/unsigned warnings to protect you against combinations not explicitly +// listed below. +#define EAGER_PTR_EVALUATOR(T1, T2) \ + template <> \ + struct EagerEvaluator<T1, T2> { \ + EagerEvaluator(T1 l, T2 r) \ + : lhs(reinterpret_cast<const void*>(l)), \ + rhs(reinterpret_cast<const void*>(r)) { \ + } \ + const void* lhs; \ + const void* rhs; \ + } +EAGER_PTR_EVALUATOR(const char*, const char*); +EAGER_PTR_EVALUATOR(const char*, char*); +EAGER_PTR_EVALUATOR(char*, const char*); +EAGER_PTR_EVALUATOR(char*, char*); +EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*); +EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*); +EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*); +EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*); +EAGER_PTR_EVALUATOR(const signed char*, const signed char*); +EAGER_PTR_EVALUATOR(const signed char*, signed char*); +EAGER_PTR_EVALUATOR(signed char*, const signed char*); +EAGER_PTR_EVALUATOR(signed char*, signed char*); + +// Data for the log message, not stored in LogMessage to avoid increasing the +// stack size. +class LogMessageData; + +// A LogMessage is a temporarily scoped object used by LOG and the unlikely part +// of a CHECK. The destructor will abort if the severity is FATAL. +class LogMessage { + public: + LogMessage(const char* file, unsigned int line, LogId id, + LogSeverity severity, int error); + + ~LogMessage(); + + // Returns the stream associated with the message, the LogMessage performs + // output when it goes out of scope. + std::ostream& stream(); + + // The routine that performs the actual logging. + static void LogLine(const char* file, unsigned int line, LogId id, + LogSeverity severity, const char* msg); + + private: + const std::unique_ptr<LogMessageData> data_; + + DISALLOW_COPY_AND_ASSIGN(LogMessage); +}; + +// Allows to temporarily change the minimum severity level for logging. +class ScopedLogSeverity { + public: + explicit ScopedLogSeverity(LogSeverity level); + ~ScopedLogSeverity(); + + private: + LogSeverity old_; +}; + +} // namespace base +} // namespace android + +#endif // BASE_LOGGING_H diff --git a/base/include/base/macros.h b/base/include/base/macros.h new file mode 100644 index 0000000..b1ce7c6 --- /dev/null +++ b/base/include/base/macros.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_MACROS_H +#define UTILS_MACROS_H + +#include <stddef.h> // for size_t +#include <unistd.h> // for TEMP_FAILURE_RETRY + +// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't. +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(exp) \ + ({ \ + decltype(exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; \ + }) +#endif + +// A macro to disallow the copy constructor and operator= functions +// This must be placed in the private: declarations for a class. +// +// For disallowing only assign or copy, delete the relevant operator or +// constructor, for example: +// void operator=(const TypeName&) = delete; +// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken +// semantically, one should either use disallow both or neither. Try to +// avoid these in new code. +// +// When building with C++11 toolchains, just use the language support +// for explicitly deleted methods. +#if __cplusplus >= 201103L +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +#else +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +// +// One caveat is that arraysize() doesn't accept any array of an +// anonymous type or a type defined inside a function. In these rare +// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is +// due to a limitation in C++'s template system. The limitation might +// eventually be removed, but it hasn't happened yet. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +template <typename T, size_t N> +char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting) + +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + +// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize, +// but can be used on anonymous types or types defined inside +// functions. It's less safe than arraysize as it accepts some +// (although not all) pointers. Therefore, you should use arraysize +// whenever possible. +// +// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type +// size_t. +// +// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error +// +// "warning: division by zero in ..." +// +// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer. +// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays. +// +// The following comments are on the implementation details, and can +// be ignored by the users. +// +// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in +// the array) and sizeof(*(arr)) (the # of bytes in one array +// element). If the former is divisible by the latter, perhaps arr is +// indeed an array, in which case the division result is the # of +// elements in the array. Otherwise, arr cannot possibly be an array, +// and we generate a compiler error to prevent the code from +// compiling. +// +// Since the size of bool is implementation-defined, we need to cast +// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final +// result has type size_t. +// +// This macro is not perfect as it wrongfully accepts certain +// pointers, namely where the pointer size is divisible by the pointee +// size. Since all our code has to go through a 32-bit compiler, +// where a pointer is 4 bytes, this means all pointers to a type whose +// size is 3 or greater than 4 will be (righteously) rejected. +#define ARRAYSIZE_UNSAFE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) + +#define LIKELY(x) __builtin_expect((x), true) +#define UNLIKELY(x) __builtin_expect((x), false) + +#define WARN_UNUSED __attribute__((warn_unused_result)) + +// A deprecated function to call to create a false use of the parameter, for +// example: +// int foo(int x) { UNUSED(x); return 10; } +// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED. +template <typename... T> +void UNUSED(const T&...) { +} + +// An attribute to place on a parameter to a function, for example: +// int foo(int x ATTRIBUTE_UNUSED) { return 10; } +// to avoid compiler warnings. +#define ATTRIBUTE_UNUSED __attribute__((__unused__)) + +// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through +// between switch labels: +// switch (x) { +// case 40: +// case 41: +// if (truth_is_out_there) { +// ++x; +// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in +// // comments. +// } else { +// return x; +// } +// case 42: +// ... +// +// As shown in the example above, the FALLTHROUGH_INTENDED macro should be +// followed by a semicolon. It is designed to mimic control-flow statements +// like 'break;', so it can be placed in most places where 'break;' can, but +// only if there are no statements on the execution path between it and the +// next switch label. +// +// When compiled with clang in C++11 mode, the FALLTHROUGH_INTENDED macro is +// expanded to [[clang::fallthrough]] attribute, which is analysed when +// performing switch labels fall-through diagnostic ('-Wimplicit-fallthrough'). +// See clang documentation on language extensions for details: +// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough +// +// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no +// effect on diagnostics. +// +// In either case this macro has no effect on runtime behavior and performance +// of code. +#if defined(__clang__) && __cplusplus >= 201103L && defined(__has_warning) +#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT +#endif +#endif + +#ifndef FALLTHROUGH_INTENDED +#define FALLTHROUGH_INTENDED \ + do { \ + } while (0) +#endif + +#endif // UTILS_MACROS_H diff --git a/base/include/base/memory.h b/base/include/base/memory.h new file mode 100644 index 0000000..882582f --- /dev/null +++ b/base/include/base/memory.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_MEMORY_H +#define BASE_MEMORY_H + +namespace android { +namespace base { + +// Use packed structures for access to unaligned data on targets with alignment +// restrictions. The compiler will generate appropriate code to access these +// structures without generating alignment exceptions. +template <typename T> +static inline T get_unaligned(const T* address) { + struct unaligned { + T v; + } __attribute__((packed)); + const unaligned* p = reinterpret_cast<const unaligned*>(address); + return p->v; +} + +template <typename T> +static inline void put_unaligned(T* address, T v) { + struct unaligned { + T v; + } __attribute__((packed)); + unaligned* p = reinterpret_cast<unaligned*>(address); + p->v = v; +} + +} // namespace base +} // namespace android + +#endif // BASE_MEMORY_H diff --git a/base/include/base/stringprintf.h b/base/include/base/stringprintf.h new file mode 100644 index 0000000..195c1de --- /dev/null +++ b/base/include/base/stringprintf.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_STRINGPRINTF_H +#define BASE_STRINGPRINTF_H + +#include <stdarg.h> +#include <string> + +namespace android { +namespace base { + +// 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); + +} // namespace base +} // namespace android + +#endif // BASE_STRINGPRINTF_H diff --git a/base/include/base/strings.h b/base/include/base/strings.h new file mode 100644 index 0000000..3559342 --- /dev/null +++ b/base/include/base/strings.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_STRINGS_H +#define BASE_STRINGS_H + +#include <string> +#include <vector> + +namespace android { +namespace base { + +// Splits a string into a vector of strings. +// +// The string is split at each occurrence of a character in delimiters. +// +// Empty splits will be omitted. I.e. Split("a,,b", ",") -> {"a", "b"} +// +// The empty string is not a valid delimiter list. +std::vector<std::string> Split(const std::string& s, + const std::string& delimiters); + +// Trims whitespace off both ends of the given string. +std::string Trim(const std::string& s); + +// Joins a vector of strings into a single string, using the given separator. +template <typename StringT> +std::string Join(const std::vector<StringT>& strings, char separator); + +// Tests whether 's' starts with 'prefix'. +bool StartsWith(const std::string& s, const char* prefix); + +// Tests whether 's' ends with 'suffix'. +bool EndsWith(const std::string& s, const char* suffix); + +} // namespace base +} // namespace android + +#endif // BASE_STRINGS_H diff --git a/base/logging.cpp b/base/logging.cpp new file mode 100644 index 0000000..0142b70 --- /dev/null +++ b/base/logging.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/logging.h" + +#include <libgen.h> + +// For getprogname(3) or program_invocation_short_name. +#if defined(__ANDROID__) || defined(__APPLE__) +#include <stdlib.h> +#elif defined(__GLIBC__) +#include <errno.h> +#endif + +#include <iostream> +#include <limits> +#include <mutex> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#include "base/strings.h" +#include "cutils/threads.h" + +// Headers for LogMessage::LogLine. +#ifdef __ANDROID__ +#include <android/set_abort_message.h> +#include "cutils/log.h" +#else +#include <sys/types.h> +#include <unistd.h> +#endif + +namespace android { +namespace base { + +static std::mutex logging_lock; + +#ifdef __ANDROID__ +static LogFunction gLogger = LogdLogger(); +#else +static LogFunction gLogger = StderrLogger; +#endif + +static bool gInitialized = false; +static LogSeverity gMinimumLogSeverity = INFO; +static std::unique_ptr<std::string> gProgramInvocationName; + +#if defined(__GLIBC__) +static const char* getprogname() { + return program_invocation_short_name; +} +#endif + +static const char* ProgramInvocationName() { + if (gProgramInvocationName == nullptr) { + gProgramInvocationName.reset(new std::string(getprogname())); + } + + return gProgramInvocationName->c_str(); +} + +void StderrLogger(LogId, LogSeverity severity, const char*, const char* file, + unsigned int line, const char* message) { + static const char* log_characters = "VDIWEF"; + CHECK_EQ(strlen(log_characters), FATAL + 1U); + char severity_char = log_characters[severity]; + fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(), + severity_char, getpid(), gettid(), file, line, message); +} + + +#ifdef __ANDROID__ +LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) { +} + +static const android_LogPriority kLogSeverityToAndroidLogPriority[] = { + ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, + ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, +}; +static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1, + "Mismatch in size of kLogSeverityToAndroidLogPriority and values " + "in LogSeverity"); + +static const log_id kLogIdToAndroidLogId[] = { + LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM, +}; +static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1, + "Mismatch in size of kLogIdToAndroidLogId and values in LogId"); + +void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, + const char* file, unsigned int line, + const char* message) { + int priority = kLogSeverityToAndroidLogPriority[severity]; + if (id == DEFAULT) { + id = default_log_id_; + } + + log_id lg_id = kLogIdToAndroidLogId[id]; + + if (priority == ANDROID_LOG_FATAL) { + __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line, + message); + } else { + __android_log_buf_print(lg_id, priority, tag, "%s", message); + } +} +#endif + +void InitLogging(char* argv[], LogFunction&& logger) { + SetLogger(std::forward<LogFunction>(logger)); + InitLogging(argv); +} + +void InitLogging(char* argv[]) { + if (gInitialized) { + return; + } + + gInitialized = true; + + // Stash the command line for later use. We can use /proc/self/cmdline on + // Linux to recover this, but we don't have that luxury on the Mac, and there + // are a couple of argv[0] variants that are commonly used. + if (argv != nullptr) { + gProgramInvocationName.reset(new std::string(basename(argv[0]))); + } + + const char* tags = getenv("ANDROID_LOG_TAGS"); + if (tags == nullptr) { + return; + } + + std::vector<std::string> specs = Split(tags, " "); + for (size_t i = 0; i < specs.size(); ++i) { + // "tag-pattern:[vdiwefs]" + std::string spec(specs[i]); + if (spec.size() == 3 && StartsWith(spec, "*:")) { + switch (spec[2]) { + case 'v': + gMinimumLogSeverity = VERBOSE; + continue; + case 'd': + gMinimumLogSeverity = DEBUG; + continue; + case 'i': + gMinimumLogSeverity = INFO; + continue; + case 'w': + gMinimumLogSeverity = WARNING; + continue; + case 'e': + gMinimumLogSeverity = ERROR; + continue; + case 'f': + gMinimumLogSeverity = FATAL; + continue; + // liblog will even suppress FATAL if you say 's' for silent, but that's + // crazy! + case 's': + gMinimumLogSeverity = FATAL; + continue; + } + } + LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags + << ")"; + } +} + +void SetLogger(LogFunction&& logger) { + std::lock_guard<std::mutex> lock(logging_lock); + gLogger = std::move(logger); +} + +// This indirection greatly reduces the stack impact of having lots of +// checks/logging in a function. +class LogMessageData { + public: + LogMessageData(const char* file, unsigned int line, LogId id, + LogSeverity severity, int error) + : file_(file), + line_number_(line), + id_(id), + severity_(severity), + error_(error) { + const char* last_slash = strrchr(file, '/'); + file = (last_slash == nullptr) ? file : last_slash + 1; + } + + const char* GetFile() const { + return file_; + } + + unsigned int GetLineNumber() const { + return line_number_; + } + + LogSeverity GetSeverity() const { + return severity_; + } + + LogId GetId() const { + return id_; + } + + int GetError() const { + return error_; + } + + std::ostream& GetBuffer() { + return buffer_; + } + + std::string ToString() const { + return buffer_.str(); + } + + private: + std::ostringstream buffer_; + const char* const file_; + const unsigned int line_number_; + const LogId id_; + const LogSeverity severity_; + const int error_; + + DISALLOW_COPY_AND_ASSIGN(LogMessageData); +}; + +LogMessage::LogMessage(const char* file, unsigned int line, LogId id, + LogSeverity severity, int error) + : data_(new LogMessageData(file, line, id, severity, error)) { +} + +LogMessage::~LogMessage() { + if (data_->GetSeverity() < gMinimumLogSeverity) { + return; // No need to format something we're not going to output. + } + + // Finish constructing the message. + if (data_->GetError() != -1) { + data_->GetBuffer() << ": " << strerror(data_->GetError()); + } + std::string msg(data_->ToString()); + + if (msg.find('\n') == std::string::npos) { + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), + data_->GetSeverity(), msg.c_str()); + } else { + msg += '\n'; + size_t i = 0; + while (i < msg.size()) { + size_t nl = msg.find('\n', i); + msg[nl] = '\0'; + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), + data_->GetSeverity(), &msg[i]); + i = nl + 1; + } + } + + // Abort if necessary. + if (data_->GetSeverity() == FATAL) { +#ifdef __ANDROID__ + android_set_abort_message(msg.c_str()); +#endif + abort(); + } +} + +std::ostream& LogMessage::stream() { + return data_->GetBuffer(); +} + +void LogMessage::LogLine(const char* file, unsigned int line, LogId id, + LogSeverity severity, const char* message) { + const char* tag = ProgramInvocationName(); + std::lock_guard<std::mutex> lock(logging_lock); + gLogger(id, severity, tag, file, line, message); +} + +ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) { + old_ = gMinimumLogSeverity; + gMinimumLogSeverity = level; +} + +ScopedLogSeverity::~ScopedLogSeverity() { + gMinimumLogSeverity = old_; +} + +} // namespace base +} // namespace android diff --git a/base/logging_test.cpp b/base/logging_test.cpp new file mode 100644 index 0000000..d947c1d --- /dev/null +++ b/base/logging_test.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/logging.h" + +#include <regex> +#include <string> + +#include "base/file.h" +#include "base/stringprintf.h" +#include "test_utils.h" + +#include <gtest/gtest.h> + +#ifdef __ANDROID__ +#define HOST_TEST(suite, name) TEST(suite, DISABLED_ ## name) +#else +#define HOST_TEST(suite, name) TEST(suite, name) +#endif + +class CapturedStderr { + public: + CapturedStderr() : old_stderr_(-1) { + init(); + } + + ~CapturedStderr() { + reset(); + } + + int fd() const { + return temp_file_.fd; + } + + private: + void init() { + old_stderr_ = dup(STDERR_FILENO); + ASSERT_NE(-1, old_stderr_); + ASSERT_NE(-1, dup2(fd(), STDERR_FILENO)); + } + + void reset() { + ASSERT_NE(-1, dup2(old_stderr_, STDERR_FILENO)); + ASSERT_EQ(0, close(old_stderr_)); + } + + TemporaryFile temp_file_; + int old_stderr_; +}; + +TEST(logging, CHECK) { + ASSERT_DEATH(CHECK(false), "Check failed: false "); + CHECK(true); + + ASSERT_DEATH(CHECK_EQ(0, 1), "Check failed: 0 == 1 "); + CHECK_EQ(0, 0); + + ASSERT_DEATH(CHECK_STREQ("foo", "bar"), R"(Check failed: "foo" == "bar")"); + CHECK_STREQ("foo", "foo"); +} + +std::string make_log_pattern(android::base::LogSeverity severity, + const char* message) { + static const char* log_characters = "VDIWEF"; + char log_char = log_characters[severity]; + return android::base::StringPrintf( + "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ " __FILE__ + ":[[:digit:]]+] %s", + log_char, message); +} + +TEST(logging, LOG) { + ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar"); + + { + CapturedStderr cap; + LOG(WARNING) << "foobar"; + ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0)); + + std::string output; + android::base::ReadFdToString(cap.fd(), &output); + + std::regex message_regex( + make_log_pattern(android::base::WARNING, "foobar")); + ASSERT_TRUE(std::regex_search(output, message_regex)); + } + + { + CapturedStderr cap; + LOG(INFO) << "foobar"; + ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0)); + + std::string output; + android::base::ReadFdToString(cap.fd(), &output); + + std::regex message_regex( + make_log_pattern(android::base::INFO, "foobar")); + ASSERT_TRUE(std::regex_search(output, message_regex)); + } + + { + CapturedStderr cap; + LOG(DEBUG) << "foobar"; + ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0)); + + std::string output; + android::base::ReadFdToString(cap.fd(), &output); + ASSERT_TRUE(output.empty()); + } + + { + android::base::ScopedLogSeverity severity(android::base::DEBUG); + CapturedStderr cap; + LOG(DEBUG) << "foobar"; + ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0)); + + std::string output; + android::base::ReadFdToString(cap.fd(), &output); + + std::regex message_regex( + make_log_pattern(android::base::DEBUG, "foobar")); + ASSERT_TRUE(std::regex_search(output, message_regex)); + } +} + +TEST(logging, PLOG) { + { + CapturedStderr cap; + errno = ENOENT; + PLOG(INFO) << "foobar"; + ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0)); + + std::string output; + android::base::ReadFdToString(cap.fd(), &output); + + std::regex message_regex(make_log_pattern( + android::base::INFO, "foobar: No such file or directory")); + ASSERT_TRUE(std::regex_search(output, message_regex)); + } +} + +TEST(logging, UNIMPLEMENTED) { + { + CapturedStderr cap; + errno = ENOENT; + UNIMPLEMENTED(ERROR); + ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0)); + + std::string output; + android::base::ReadFdToString(cap.fd(), &output); + + std::string expected_message = + android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__); + std::regex message_regex( + make_log_pattern(android::base::ERROR, expected_message.c_str())); + ASSERT_TRUE(std::regex_search(output, message_regex)); + } +} diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp new file mode 100644 index 0000000..d55ff52 --- /dev/null +++ b/base/stringprintf.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/stringprintf.h" + +#include <stdio.h> + +#include <string> + +namespace android { +namespace base { + +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 < static_cast<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); +} + +} // namespace base +} // namespace android diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp new file mode 100644 index 0000000..5cc2086 --- /dev/null +++ b/base/stringprintf_test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/stringprintf.h" + +#include <gtest/gtest.h> + +#include <string> + +TEST(StringPrintfTest, HexSizeT) { + size_t size = 0x00107e59; + EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size)); + EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size)); +} + +TEST(StringPrintfTest, StringAppendF) { + std::string s("a"); + android::base::StringAppendF(&s, "b"); + EXPECT_EQ("ab", s); +} + +TEST(StringPrintfTest, Errno) { + errno = 123; + android::base::StringPrintf("hello %s", "world"); + EXPECT_EQ(123, errno); +} + +void TestN(size_t n) { + char* buf = new char[n + 1]; + memset(buf, 'x', n); + buf[n] = '\0'; + std::string s(android::base::StringPrintf("%s", buf)); + EXPECT_EQ(buf, s); + delete[] buf; +} + +TEST(StringPrintfTest, At1023) { + TestN(1023); +} + +TEST(StringPrintfTest, At1024) { + TestN(1024); +} + +TEST(StringPrintfTest, At1025) { + TestN(1025); +} diff --git a/base/strings.cpp b/base/strings.cpp new file mode 100644 index 0000000..6f698d9 --- /dev/null +++ b/base/strings.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/strings.h" + +#include <stdlib.h> +#include <string.h> + +#include <string> +#include <vector> + +namespace android { +namespace base { + +#define CHECK_NE(a, b) \ + if ((a) == (b)) abort(); + +std::vector<std::string> Split(const std::string& s, + const std::string& delimiters) { + CHECK_NE(delimiters.size(), 0U); + + std::vector<std::string> split; + if (s.size() == 0) { + // Split("", d) returns {} rather than {""}. + return split; + } + + size_t base = 0; + size_t found; + do { + found = s.find_first_of(delimiters, base); + if (found != base) { + split.push_back(s.substr(base, found - base)); + } + + base = found + 1; + } while (found != s.npos); + + return split; +} + +std::string Trim(const std::string& s) { + std::string result; + + if (s.size() == 0) { + return result; + } + + size_t start_index = 0; + size_t end_index = s.size() - 1; + + // Skip initial whitespace. + while (start_index < s.size()) { + if (!isspace(s[start_index])) { + break; + } + start_index++; + } + + // Skip terminating whitespace. + while (end_index >= start_index) { + if (!isspace(s[end_index])) { + break; + } + end_index--; + } + + // All spaces, no beef. + if (end_index < start_index) { + return ""; + } + // Start_index is the first non-space, end_index is the last one. + return s.substr(start_index, end_index - start_index + 1); +} + +template <typename StringT> +std::string Join(const std::vector<StringT>& strings, char separator) { + if (strings.empty()) { + return ""; + } + + std::string result(strings[0]); + for (size_t i = 1; i < strings.size(); ++i) { + result += separator; + result += strings[i]; + } + return result; +} + +// Explicit instantiations. +template std::string Join<std::string>(const std::vector<std::string>& strings, + char separator); +template std::string Join<const char*>(const std::vector<const char*>& strings, + char separator); + +bool StartsWith(const std::string& s, const char* prefix) { + return s.compare(0, strlen(prefix), prefix) == 0; +} + +bool EndsWith(const std::string& s, const char* suffix) { + size_t suffix_length = strlen(suffix); + size_t string_length = s.size(); + if (suffix_length > string_length) { + return false; + } + size_t offset = string_length - suffix_length; + return s.compare(offset, suffix_length, suffix) == 0; +} + +} // namespace base +} // namespace android diff --git a/base/strings_test.cpp b/base/strings_test.cpp new file mode 100644 index 0000000..1bf07a1 --- /dev/null +++ b/base/strings_test.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/strings.h" + +#include <gtest/gtest.h> + +#include <string> +#include <vector> + +TEST(strings, split_empty) { + std::vector<std::string> parts = android::base::Split("", ","); + ASSERT_EQ(0U, parts.size()); +} + +TEST(strings, split_single) { + std::vector<std::string> parts = android::base::Split("foo", ","); + ASSERT_EQ(1U, parts.size()); + ASSERT_EQ("foo", parts[0]); +} + +TEST(strings, split_simple) { + std::vector<std::string> parts = android::base::Split("foo,bar,baz", ","); + ASSERT_EQ(3U, parts.size()); + ASSERT_EQ("foo", parts[0]); + ASSERT_EQ("bar", parts[1]); + ASSERT_EQ("baz", parts[2]); +} + +TEST(strings, split_with_empty_part) { + std::vector<std::string> parts = android::base::Split("foo,,bar", ","); + ASSERT_EQ(2U, parts.size()); + ASSERT_EQ("foo", parts[0]); + ASSERT_EQ("bar", parts[1]); +} + +TEST(strings, split_null_char) { + std::vector<std::string> parts = + android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1)); + ASSERT_EQ(2U, parts.size()); + ASSERT_EQ("foo", parts[0]); + ASSERT_EQ("bar", parts[1]); +} + +TEST(strings, split_any) { + std::vector<std::string> parts = android::base::Split("foo:bar,baz", ",:"); + ASSERT_EQ(3U, parts.size()); + ASSERT_EQ("foo", parts[0]); + ASSERT_EQ("bar", parts[1]); + ASSERT_EQ("baz", parts[2]); +} + +TEST(strings, split_any_with_empty_part) { + std::vector<std::string> parts = android::base::Split("foo:,bar", ",:"); + ASSERT_EQ(2U, parts.size()); + ASSERT_EQ("foo", parts[0]); + ASSERT_EQ("bar", parts[1]); +} + +TEST(strings, trim_empty) { + ASSERT_EQ("", android::base::Trim("")); +} + +TEST(strings, trim_already_trimmed) { + ASSERT_EQ("foo", android::base::Trim("foo")); +} + +TEST(strings, trim_left) { + ASSERT_EQ("foo", android::base::Trim(" foo")); +} + +TEST(strings, trim_right) { + ASSERT_EQ("foo", android::base::Trim("foo ")); +} + +TEST(strings, trim_both) { + ASSERT_EQ("foo", android::base::Trim(" foo ")); +} + +TEST(strings, trim_no_trim_middle) { + ASSERT_EQ("foo bar", android::base::Trim("foo bar")); +} + +TEST(strings, trim_other_whitespace) { + ASSERT_EQ("foo", android::base::Trim("\v\tfoo\n\f")); +} + +TEST(strings, join_nothing) { + std::vector<std::string> list = {}; + ASSERT_EQ("", android::base::Join(list, ',')); +} + +TEST(strings, join_single) { + std::vector<std::string> list = {"foo"}; + ASSERT_EQ("foo", android::base::Join(list, ',')); +} + +TEST(strings, join_simple) { + std::vector<std::string> list = {"foo", "bar", "baz"}; + ASSERT_EQ("foo,bar,baz", android::base::Join(list, ',')); +} + +TEST(strings, join_separator_in_vector) { + std::vector<std::string> list = {",", ","}; + ASSERT_EQ(",,,", android::base::Join(list, ',')); +} + +TEST(strings, startswith_empty) { + ASSERT_FALSE(android::base::StartsWith("", "foo")); + ASSERT_TRUE(android::base::StartsWith("", "")); +} + +TEST(strings, startswith_simple) { + ASSERT_TRUE(android::base::StartsWith("foo", "")); + ASSERT_TRUE(android::base::StartsWith("foo", "f")); + ASSERT_TRUE(android::base::StartsWith("foo", "fo")); + ASSERT_TRUE(android::base::StartsWith("foo", "foo")); +} + +TEST(strings, startswith_prefix_too_long) { + ASSERT_FALSE(android::base::StartsWith("foo", "foobar")); +} + +TEST(strings, startswith_contains_prefix) { + ASSERT_FALSE(android::base::StartsWith("foobar", "oba")); + ASSERT_FALSE(android::base::StartsWith("foobar", "bar")); +} + +TEST(strings, endswith_empty) { + ASSERT_FALSE(android::base::EndsWith("", "foo")); + ASSERT_TRUE(android::base::EndsWith("", "")); +} + +TEST(strings, endswith_simple) { + ASSERT_TRUE(android::base::EndsWith("foo", "")); + ASSERT_TRUE(android::base::EndsWith("foo", "o")); + ASSERT_TRUE(android::base::EndsWith("foo", "oo")); + ASSERT_TRUE(android::base::EndsWith("foo", "foo")); +} + +TEST(strings, endswith_prefix_too_long) { + ASSERT_FALSE(android::base::EndsWith("foo", "foobar")); +} + +TEST(strings, endswith_contains_prefix) { + ASSERT_FALSE(android::base::EndsWith("foobar", "oba")); + ASSERT_FALSE(android::base::EndsWith("foobar", "foo")); +} diff --git a/base/test_main.cpp b/base/test_main.cpp new file mode 100644 index 0000000..546923d --- /dev/null +++ b/base/test_main.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "base/logging.h" + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + android::base::InitLogging(argv, android::base::StderrLogger); + return RUN_ALL_TESTS(); +} diff --git a/base/test_utils.cpp b/base/test_utils.cpp new file mode 100644 index 0000000..1f6d3cf --- /dev/null +++ b/base/test_utils.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "test_utils.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +TemporaryFile::TemporaryFile() { + init("/data/local/tmp"); + if (fd == -1) { + init("/tmp"); + } +} + +TemporaryFile::~TemporaryFile() { + close(fd); + unlink(filename); +} + +void TemporaryFile::init(const char* tmp_dir) { + snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir); + fd = mkstemp(filename); +} diff --git a/base/test_utils.h b/base/test_utils.h new file mode 100644 index 0000000..132d3a7 --- /dev/null +++ b/base/test_utils.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEST_UTILS_H +#define TEST_UTILS_H + +class TemporaryFile { + public: + TemporaryFile(); + ~TemporaryFile(); + + int fd; + char filename[1024]; + + private: + void init(const char* tmp_dir); +}; + +#endif // TEST_UTILS_H |