From 10bdf61e5ffbd99efec4d4168aee73a93c5e88be Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Thu, 10 Mar 2016 09:50:08 -0800 Subject: [PATCH] liblog: add __android_log_pmsg_file_write (cherry pick from commit d4b061bde280fce9b5426b5738a02d42ec263c48) - This is considered an Android Private function, not exported for general use. - goal is to record a file's content into a series of log messages into pmsg, to be retrieved after a reboot for transfer to a persistent location. - filename reference is converted to a tag-unique ":". - buffer and length representing the filename contents are recorded, along with a sequence number placed into the nsec time field to ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE. - Add a gTest for this function. Bug: 27176738 Change-Id: If93df3ae8bfc1bb75516d4a1fd8dae0301af644b --- include/private/android_logger.h | 20 ++++++ liblog/pmsg_writer.c | 111 +++++++++++++++++++++++++++++++ liblog/tests/liblog_test.cpp | 15 ++++- 3 files changed, 145 insertions(+), 1 deletion(-) diff --git a/include/private/android_logger.h b/include/private/android_logger.h index 04238a6fd..a637ca572 100644 --- a/include/private/android_logger.h +++ b/include/private/android_logger.h @@ -19,7 +19,10 @@ #ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ #define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ +/* Android private interfaces */ + #include +#include #include #include @@ -95,4 +98,21 @@ typedef struct __attribute__((__packed__)) { char data[]; } android_log_event_string_t; +#if defined(__cplusplus) +extern "C" { #endif + +#define ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE 256 /* 1MB file */ +#define ANDROID_LOG_PMSG_FILE_SEQUENCE 1000 + +ssize_t __android_log_pmsg_file_write( + log_id_t logId, + char prio, + const char *filename, + const char *buf, size_t len); + +#if defined(__cplusplus) +} +#endif + +#endif /* _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ */ diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c index 7a89e5d54..7034cebaa 100644 --- a/liblog/pmsg_writer.c +++ b/liblog/pmsg_writer.c @@ -157,3 +157,114 @@ static int pmsgWrite(log_id_t logId, struct timespec *ts, return ret; } + +/* + * Virtual pmsg filesystem + * + * Payload will comprise the string ":\0" to a + * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the + * file. + * + * Will hijack the header.realtime.tv_nsec field for a sequence number in usec. + */ + +static inline const char *strnrchr(const char *buf, size_t len, char c) { + const char *cp = buf + len; + while ((--cp > buf) && (*cp != c)); + if (cp <= buf) { + return buf + len; + } + return cp; +} + +/* Write a buffer as filename references (tag = :) */ +LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write( + log_id_t logId, + char prio, + const char *filename, + const char *buf, size_t len) { + int fd; + size_t length, packet_len; + const char *tag; + char *cp, *slash; + struct timespec ts; + struct iovec vec[3]; + + /* Make sure the logId value is not a bad idea */ + if ((logId == LOG_ID_KERNEL) || /* Verbotten */ + (logId == LOG_ID_EVENTS) || /* Do not support binary content */ + (logId == LOG_ID_SECURITY) || /* Bad idea to allow */ + ((unsigned)logId >= 32)) { /* fit within logMask on arch32 */ + return -EINVAL; + } + + clock_gettime(android_log_clockid(), &ts); + + cp = strdup(filename); + if (!cp) { + return -ENOMEM; + } + + fd = pmsgLoggerWrite.context.fd; + if (fd < 0) { + __android_log_lock(); + fd = pmsgOpen(); + __android_log_unlock(); + if (fd < 0) { + return -EBADF; + } + } + + tag = cp; + slash = strrchr(cp, '/'); + if (slash) { + *slash = ':'; + slash = strrchr(cp, '/'); + if (slash) { + tag = slash + 1; + } + } + + length = strlen(tag) + 1; + packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length; + + vec[0].iov_base = &prio; + vec[0].iov_len = sizeof(char); + vec[1].iov_base = (unsigned char *)tag; + vec[1].iov_len = length; + + for (ts.tv_nsec = 0, length = len; + length; + ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) { + ssize_t ret; + size_t transfer; + + if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= + ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) { + len -= length; + break; + } + + transfer = length; + if (transfer > packet_len) { + transfer = strnrchr(buf, packet_len - 1, '\n') - buf; + if ((transfer < length) && (buf[transfer] == '\n')) { + ++transfer; + } + } + + vec[2].iov_base = (unsigned char *)buf; + vec[2].iov_len = transfer; + + ret = pmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0])); + + if (ret <= 0) { + free(cp); + return ret; + } + length -= transfer; + buf += transfer; + } + free(cp); + return len; +} diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index af3a3b432..97391d4a6 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -896,7 +896,10 @@ Good Signior Leonato, you are come to meet your\n\ trouble: the fashion of the world is to avoid\n\ cost, and you encounter it\n\ LEONATO\n\ -Never came trouble to my house in the likeness of your grace"; +Never came trouble to my house in the likeness of your grace,\n\ +for trouble being gone, comfort should remain, but\n\ +when you depart from me, sorrow abides and happiness\n\ +takes his leave."; TEST(liblog, max_payload) { pid_t pid = getpid(); @@ -2451,3 +2454,13 @@ TEST(liblog, create_android_logger_overflow) { EXPECT_LE(0, android_log_destroy(&ctx)); ASSERT_TRUE(NULL == ctx); } + +static const char __pmsg_file[] = + "/data/william-shakespeare/MuchAdoAboutNothing.txt"; + +TEST(liblog, __android_log_pmsg_file_write) { + EXPECT_LT(0, __android_log_pmsg_file_write( + LOG_ID_CRASH, ANDROID_LOG_VERBOSE, + __pmsg_file, max_payload_buf, sizeof(max_payload_buf))); + fprintf(stderr, "Reboot, ensure file %s matches\n", __pmsg_file); +}