Merge "Fix const issues in preparation for libcxx rebase."
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
index ca621fa..04c299c 100644
--- a/base/include/android-base/errors.h
+++ b/base/include/android-base/errors.h
@@ -27,8 +27,8 @@
// special handling to get the error string. Refer to Microsoft documentation
// to determine which error code to check for each function.
-#ifndef BASE_ERRORS_H
-#define BASE_ERRORS_H
+#ifndef ANDROID_BASE_ERRORS_H
+#define ANDROID_BASE_ERRORS_H
#include <string>
@@ -43,4 +43,4 @@
} // namespace base
} // namespace android
-#endif // BASE_ERRORS_H
+#endif // ANDROID_BASE_ERRORS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 5342d98..aa18ea7 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BASE_FILE_H
-#define BASE_FILE_H
+#ifndef ANDROID_BASE_FILE_H
+#define ANDROID_BASE_FILE_H
#include <sys/stat.h>
#include <string>
@@ -46,4 +46,4 @@
} // namespace base
} // namespace android
-#endif // BASE_FILE_H
+#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 41fe4b3..b86c232 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef BASE_LOGGING_H
-#define BASE_LOGGING_H
+
+#ifndef ANDROID_BASE_LOGGING_H
+#define ANDROID_BASE_LOGGING_H
// NOTE: For Windows, you must include logging.h after windows.h to allow the
// following code to suppress the evil ERROR macro:
@@ -346,4 +347,4 @@
} // namespace base
} // namespace android
-#endif // BASE_LOGGING_H
+#endif // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index b1ce7c6..913a9a0 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef UTILS_MACROS_H
-#define UTILS_MACROS_H
+#ifndef ANDROID_BASE_MACROS_H
+#define ANDROID_BASE_MACROS_H
#include <stddef.h> // for size_t
#include <unistd.h> // for TEMP_FAILURE_RETRY
@@ -185,4 +185,4 @@
} while (0)
#endif
-#endif // UTILS_MACROS_H
+#endif // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 882582f..3a2f8fa 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BASE_MEMORY_H
-#define BASE_MEMORY_H
+#ifndef ANDROID_BASE_MEMORY_H
+#define ANDROID_BASE_MEMORY_H
namespace android {
namespace base {
@@ -44,4 +44,4 @@
} // namespace base
} // namespace android
-#endif // BASE_MEMORY_H
+#endif // ANDROID_BASE_MEMORY_H
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 0543795..ed75e2d 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BASE_PARSEINT_H
-#define BASE_PARSEINT_H
+#ifndef ANDROID_BASE_PARSEINT_H
+#define ANDROID_BASE_PARSEINT_H
#include <errno.h>
#include <stdlib.h>
@@ -70,4 +70,4 @@
} // namespace base
} // namespace android
-#endif // BASE_PARSEINT_H
+#endif // ANDROID_BASE_PARSEINT_H
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
index 2de5ac9..b4ac025 100644
--- a/base/include/android-base/parsenetaddress.h
+++ b/base/include/android-base/parsenetaddress.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BASE_PARSENETADDRESS_H
-#define BASE_PARSENETADDRESS_H
+#ifndef ANDROID_BASE_PARSENETADDRESS_H
+#define ANDROID_BASE_PARSENETADDRESS_H
#include <string>
@@ -35,4 +35,4 @@
} // namespace base
} // namespace android
-#endif // BASE_PARSENETADDRESS_H
+#endif // ANDROID_BASE_PARSENETADDRESS_H
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
index d68af87..cf666ab 100644
--- a/base/include/android-base/stringprintf.h
+++ b/base/include/android-base/stringprintf.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BASE_STRINGPRINTF_H
-#define BASE_STRINGPRINTF_H
+#ifndef ANDROID_BASE_STRINGPRINTF_H
+#define ANDROID_BASE_STRINGPRINTF_H
#include <stdarg.h>
#include <string>
@@ -53,4 +53,4 @@
} // namespace base
} // namespace android
-#endif // BASE_STRINGPRINTF_H
+#endif // ANDROID_BASE_STRINGPRINTF_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 20da144..69781cd 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BASE_STRINGS_H
-#define BASE_STRINGS_H
+#ifndef ANDROID_BASE_STRINGS_H
+#define ANDROID_BASE_STRINGS_H
#include <sstream>
#include <string>
@@ -65,4 +65,4 @@
} // namespace base
} // namespace android
-#endif // BASE_STRINGS_H
+#endif // ANDROID_BASE_STRINGS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 3f6872c..4ea3c8e 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+#ifndef ANDROID_BASE_TEST_UTILS_H
+#define ANDROID_BASE_TEST_UTILS_H
#include <string>
@@ -48,4 +48,4 @@
DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
};
-#endif // TEST_UTILS_H
+#endif // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 90979df..2422102 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef UTILS_THREAD_ANNOTATIONS_H
-#define UTILS_THREAD_ANNOTATIONS_H
+#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
+#define ANDROID_BASE_THREAD_ANNOTATIONS_H
#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
@@ -80,4 +80,4 @@
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-#endif // UTILS_THREAD_ANNOTATIONS_H
+#endif // ANDROID_BASE_THREAD_ANNOTATIONS_H
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index d3b27ca..dfaac9d 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -21,18 +21,18 @@
#include <android-base/macros.h>
-/* Container for a file descriptor that automatically closes the descriptor as
- * it goes out of scope.
- *
- * unique_fd ufd(open("/some/path", "r"));
- *
- * if (ufd.get() < 0) // invalid descriptor
- * return error;
- *
- * // Do something useful
- *
- * return 0; // descriptor is closed here
- */
+// Container for a file descriptor that automatically closes the descriptor as
+// it goes out of scope.
+//
+// unique_fd ufd(open("/some/path", "r"));
+// if (ufd.get() == -1) return error;
+//
+// // Do something useful, possibly including 'return'.
+//
+// return 0; // Descriptor is closed for you.
+//
+// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
+// you find this class if you're searching for one of those names.
namespace android {
namespace base {
@@ -44,14 +44,18 @@
~unique_fd() { clear(); }
unique_fd(unique_fd&& other) : value_(other.release()) {}
- unique_fd& operator = (unique_fd&& s) {
+ unique_fd& operator=(unique_fd&& s) {
reset(s.release());
return *this;
}
void reset(int new_value) {
- if (value_ >= 0)
+ if (value_ != -1) {
+ // Even if close(2) fails with EINTR, the fd will have been closed.
+ // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone else's fd.
+ // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
close(value_);
+ }
value_ = new_value;
}
@@ -61,7 +65,7 @@
int get() const { return value_; }
- int release() {
+ int release() __attribute__((warn_unused_result)) {
int ret = value_;
value_ = -1;
return ret;
@@ -76,4 +80,4 @@
} // namespace base
} // namespace android
-#endif // ANDROID_BASE_UNIQUE_FD_H
+#endif // ANDROID_BASE_UNIQUE_FD_H
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 3b0ed0a..2d5a6f6 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BASE_UTF8_H
-#define BASE_UTF8_H
+#ifndef ANDROID_BASE_UTF8_H
+#define ANDROID_BASE_UTF8_H
#ifdef _WIN32
#include <string>
@@ -84,4 +84,4 @@
} // namespace base
} // namespace android
-#endif // BASE_UTF8_H
+#endif // ANDROID_BASE_UTF8_H
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 5d1fae9..ef4f68e 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -55,8 +55,11 @@
return false;
}
- int32_t value = std::stoi(content);
- bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+ // Ignore existing bootstat records (which do not contain file content).
+ if (!content.empty()) {
+ int32_t value = std::stoi(content);
+ bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+ }
return true;
}
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index 4d5deee..a2b8318 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -55,6 +55,7 @@
FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);
+ FRIEND_TEST(BootEventRecordStoreTest, GetBootEventNoFileContent);
// Sets the filesystem path of the record store.
void SetStorePath(const std::string& path);
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 343f9d0..b7dd9ba 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -17,12 +17,16 @@
#include "boot_event_record_store.h"
#include <dirent.h>
+#include <fcntl.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <unistd.h>
#include <cstdint>
#include <cstdlib>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "uptime_parser.h"
@@ -31,6 +35,36 @@
namespace {
+// Creates a fake boot event record file at |record_path| containing the boot
+// record |value|. This method is necessary as truncating a
+// BootEventRecordStore-created file would modify the mtime, which would alter
+// the value of the record.
+bool CreateEmptyBootEventRecord(const std::string& record_path, int32_t value) {
+ android::base::unique_fd record_fd(creat(record_path.c_str(), S_IRUSR | S_IWUSR));
+ if (record_fd.get() == -1) {
+ return false;
+ }
+
+ // Writing the value as content in the record file is a debug measure to
+ // ensure the validity of the file mtime value, i.e., to check that the record
+ // file mtime values are not changed once set.
+ // TODO(jhawkins): Remove this block.
+ if (!android::base::WriteStringToFd(std::to_string(value), record_fd.get())) {
+ return false;
+ }
+
+ // Set the |mtime| of the file to store the value of the boot event while
+ // preserving the |atime|.
+ struct timeval atime = {/* tv_sec */ 0, /* tv_usec */ 0};
+ struct timeval mtime = {/* tv_sec */ value, /* tv_usec */ 0};
+ const struct timeval times[] = {atime, mtime};
+ if (utimes(record_path.c_str(), times) != 0) {
+ return false;
+ }
+
+ return true;
+}
+
// Returns true if the time difference between |a| and |b| is no larger
// than 10 seconds. This allow for a relatively large fuzz when comparing
// two timestamps taken back-to-back.
@@ -178,4 +212,19 @@
// Null |record|.
EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string());
-}
\ No newline at end of file
+}
+
+// Tests that the BootEventRecordStore is capable of handling an older record
+// protocol which does not contain file contents.
+TEST_F(BootEventRecordStoreTest, GetBootEventNoFileContent) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ EXPECT_TRUE(CreateEmptyBootEventRecord(store.GetBootEventPath("devonian"), 2718));
+
+ BootEventRecordStore::BootEventRecord record;
+ bool result = store.GetBootEvent("devonian", &record);
+ EXPECT_EQ(true, result);
+ EXPECT_EQ("devonian", record.first);
+ EXPECT_EQ(2718, record.second);
+}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7e60a72..fa8f19a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -355,8 +355,8 @@
" formatting.\n"
" -s <specific device> Specify a device. For USB, provide either\n"
" a serial number or path to device port.\n"
- " For ethernet, provide an address in the"
- " form <protocol>:<hostname>[:port] where"
+ " For ethernet, provide an address in the\n"
+ " form <protocol>:<hostname>[:port] where\n"
" <protocol> is either tcp or udp.\n"
" -p <product> Specify product name.\n"
" -c <cmdline> Override kernel commandline.\n"
diff --git a/init/init.cpp b/init/init.cpp
index 69f0595..9b7d108 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,7 +18,6 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <fstream>
#include <libgen.h>
#include <paths.h>
#include <signal.h>
@@ -290,100 +289,6 @@
return result;
}
-static void security_failure() {
- ERROR("Security failure; rebooting into recovery mode...\n");
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- while (true) { pause(); } // never reached
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
- std::string path;
- if (compat) {
- path = MMAP_RND_COMPAT_PATH;
- } else {
- path = MMAP_RND_PATH;
- }
- std::ifstream inf(path, std::fstream::in);
- if (!inf) {
- return false;
- }
- while (start >= min) {
- // try to write out new value
- std::string str_val = std::to_string(start);
- std::ofstream of(path, std::fstream::out);
- if (!of) {
- return false;
- }
- of << str_val << std::endl;
- of.close();
-
- // check to make sure it was recorded
- inf.seekg(0);
- std::string str_rec;
- inf >> str_rec;
- if (str_val.compare(str_rec) == 0) {
- break;
- }
- start--;
- }
- inf.close();
- return (start >= min);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value. Apply
- * upstream patch-sets https://lkml.org/lkml/2015/12/21/337 and
- * https://lkml.org/lkml/2016/2/4/831 to enable this.
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
-{
- int ret = -1;
-
- /* values are arch-dependent */
-#if defined(__aarch64__)
- /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
- if (set_mmap_rnd_bits_min(33, 24, false)
- && set_mmap_rnd_bits_min(16, 16, true)) {
- ret = 0;
- }
-#elif defined(__x86_64__)
- /* x86_64 supports 28 - 32 bits */
- if (set_mmap_rnd_bits_min(32, 32, false)
- && set_mmap_rnd_bits_min(16, 16, true)) {
- ret = 0;
- }
-#elif defined(__arm__) || defined(__i386__)
- /* check to see if we're running on 64-bit kernel */
- bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
- /* supported 32-bit architecture must have 16 bits set */
- if (set_mmap_rnd_bits_min(16, 16, h64)) {
- ret = 0;
- }
-#elif defined(__mips__) || defined(__mips64__)
- // TODO: add mips support b/27788820
- ret = 0;
-#else
- ERROR("Unknown architecture\n");
-#endif
-
-#ifdef __BRILLO__
- // TODO: b/27794137
- ret = 0;
-#endif
- if (ret == -1) {
- ERROR("Unable to set adequate mmap entropy value!\n");
- security_failure();
- }
- return ret;
-}
-
static int keychord_init_action(const std::vector<std::string>& args)
{
keychord_init();
@@ -540,6 +445,12 @@
return 0;
}
+static void security_failure() {
+ ERROR("Security failure; rebooting into recovery mode...\n");
+ android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+ while (true) { pause(); } // never reached
+}
+
static void selinux_initialize(bool in_kernel_domain) {
Timer t;
@@ -689,7 +600,6 @@
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
- am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
diff --git a/liblog/logprint.c b/liblog/logprint.c
index d7de864..9b60d4a 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -512,8 +512,18 @@
}
if (msgStart == -1) {
- fprintf(stderr, "+++ LOG: malformed log message\n");
- return -1;
+ /* +++ LOG: malformed log message, DYB */
+ for (i = 1; i < buf->len; i++) {
+ /* odd characters in tag? */
+ if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
+ msg[i] = '\0';
+ msgStart = i + 1;
+ break;
+ }
+ }
+ if (msgStart == -1) {
+ msgStart = buf->len - 1; /* All tag, no message, print truncates */
+ }
}
if (msgEnd == -1) {
/* incoming message not null-terminated; force it */
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 941b9b9..456f8b3 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1065,8 +1065,10 @@
fflush(stderr);
int printLogLine =
android_log_printLogLine(logformat, fileno(stderr), &entry);
+ // Legacy tag truncation
EXPECT_LE(128, printLogLine);
- EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD, printLogLine);
+ // Measured maximum if we try to print part of the tag as message
+ EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD * 13 / 8, printLogLine);
}
android_log_format_free(logformat);
}
diff --git a/logcat/Android.mk b/logcat/Android.mk
index b828a9f..1dacbe1 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -5,7 +5,7 @@
LOCAL_SRC_FILES:= logcat.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils libpcrecpp
LOCAL_MODULE := logcat
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index c148d89..cc3333c 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -37,6 +37,8 @@
#include <log/logprint.h>
#include <system/thread_defs.h>
+#include <pcrecpp.h>
+
#define DEFAULT_MAX_ROTATED_LOGS 4
static AndroidLogFormat * g_logformat;
@@ -76,6 +78,10 @@
static size_t g_outByteCount = 0;
static int g_printBinary = 0;
static int g_devCount = 0; // >1 means multiple
+static pcrecpp::RE* g_regex;
+// 0 means "infinite"
+static size_t g_maxCount = 0;
+static size_t g_printCount = 0;
__noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
@@ -143,6 +149,21 @@
TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
}
+static bool regexOk(const AndroidLogEntry& entry, log_id_t id)
+{
+ if (! g_regex) {
+ return true;
+ }
+
+ if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
+ return false;
+ }
+
+ std::string messageString(entry.message, entry.messageLen);
+
+ return g_regex->PartialMatch(messageString);
+}
+
static void processBuffer(log_device_t* dev, struct log_msg *buf)
{
int bytesWritten = 0;
@@ -171,9 +192,12 @@
goto error;
}
- if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
+ if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority) &&
+ regexOk(entry, buf->id())) {
bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+ g_printCount++;
+
if (bytesWritten < 0) {
logcat_panic(false, "output error");
}
@@ -271,6 +295,10 @@
" -c clear (flush) the entire log and exit\n"
" --clear\n"
" -d dump the log and then exit (don't block)\n"
+ " -e <expr> only print lines where the log message matches <expr>\n"
+ " --regex <expr> where <expr> is a regular expression\n"
+ " -m <count> quit after printing <count> lines. This is meant to be\n"
+ " --max-count=<count> paired with --regex, but will work on its own.\n"
" -t <count> print only the most recent <count> lines (implies -d)\n"
" -t '<time>' print most recent lines since specified time (implies -d)\n"
" -T <count> print only the most recent <count> lines (does not imply -d)\n"
@@ -521,6 +549,7 @@
size_t tail_lines = 0;
log_time tail_time(log_time::EPOCH);
size_t pid = 0;
+ bool got_t = false;
signal(SIGPIPE, exit);
@@ -547,7 +576,9 @@
{ "format", required_argument, NULL, 'v' },
{ "last", no_argument, NULL, 'L' },
{ pid_str, required_argument, NULL, 0 },
+ { "max-count", required_argument, NULL, 'm' },
{ "prune", optional_argument, NULL, 'p' },
+ { "regex", required_argument, NULL, 'e' },
{ "rotate_count", required_argument, NULL, 'n' },
{ "rotate_kbytes", required_argument, NULL, 'r' },
{ "statistics", no_argument, NULL, 'S' },
@@ -556,7 +587,7 @@
{ NULL, 0, NULL, 0 }
};
- ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:",
+ ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
long_options, &option_index);
if (ret < 0) {
@@ -613,6 +644,7 @@
break;
case 't':
+ got_t = true;
mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
/* FALLTHRU */
case 'T':
@@ -644,6 +676,19 @@
printDividers = true;
break;
+ case 'e':
+ g_regex = new pcrecpp::RE(optarg);
+ break;
+
+ case 'm': {
+ char *end = NULL;
+ if (!getSizeTArg(optarg, &g_maxCount)) {
+ logcat_panic(false, "-%c \"%s\" isn't an "
+ "integer greater than zero\n", ret, optarg);
+ }
+ }
+ break;
+
case 'g':
if (!optarg) {
getLogSize = 1;
@@ -920,6 +965,10 @@
}
}
+ if (g_maxCount && got_t) {
+ logcat_panic(true, "Cannot use -m (--max-count) and -t together");
+ }
+
if (!devices) {
dev = devices = new log_device_t("main", false);
g_devCount = 1;
@@ -1135,7 +1184,8 @@
dev = NULL;
log_device_t unexpected("unexpected", false);
- while (1) {
+
+ while (!g_maxCount || g_printCount < g_maxCount) {
struct log_msg log_msg;
log_device_t* d;
int ret = android_logger_list_read(logger_list, &log_msg);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 8459bd3..c6eb7f8 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -953,3 +953,67 @@
free(list);
list = NULL;
}
+
+TEST(logcat, regex) {
+ FILE *fp;
+ int count = 0;
+
+ char buffer[5120];
+
+ snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid());
+
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaab"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaa"));
+
+ // Let the logs settle
+ sleep(1);
+
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ continue;
+ }
+
+ EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL);
+
+ count++;
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(2, count);
+}
+
+TEST(logcat, maxcount) {
+ FILE *fp;
+ int count = 0;
+
+ char buffer[5120];
+
+ snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid());
+
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+
+ // Let the logs settle
+ sleep(1);
+
+ ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+ continue;
+ }
+
+ count++;
+ }
+
+ pclose(fp);
+
+ ASSERT_EQ(3, count);
+}