liblog: add __android_log_pmsg_file_write
(cherry pick from commit d4b061bde2
)
- 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
"<dirbase>:<filebase>".
- 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
This commit is contained in:
parent
facf94c74a
commit
10bdf61e5f
|
@ -19,7 +19,10 @@
|
|||
#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
|
||||
#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
|
||||
|
||||
/* Android private interfaces */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <log/log.h>
|
||||
#include <log/log_read.h>
|
||||
|
@ -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_ */
|
||||
|
|
|
@ -157,3 +157,114 @@ static int pmsgWrite(log_id_t logId, struct timespec *ts,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Virtual pmsg filesystem
|
||||
*
|
||||
* Payload will comprise the string "<basedir>:<basefile>\0<content>" 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 = <basedir>:<basename>) */
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue