diff --git a/include/log/logprint.h b/include/log/logprint.h index 1e42b4739..96249e98f 100644 --- a/include/log/logprint.h +++ b/include/log/logprint.h @@ -36,7 +36,9 @@ typedef enum { FORMAT_TIME, FORMAT_THREADTIME, FORMAT_LONG, - FORMAT_COLOR, + /* The following two are modifiers to above formats */ + FORMAT_MODIFIER_COLOR, /* converts priority to color */ + FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */ } AndroidLogPrintFormat; typedef struct AndroidLogFormat_t AndroidLogFormat; @@ -56,7 +58,8 @@ AndroidLogFormat *android_log_format_new(); void android_log_format_free(AndroidLogFormat *p_format); -void android_log_setPrintFormat(AndroidLogFormat *p_format, +/* currently returns 0 if format is a modifier, 1 if not */ +int android_log_setPrintFormat(AndroidLogFormat *p_format, AndroidLogPrintFormat format); /** @@ -64,7 +67,7 @@ void android_log_setPrintFormat(AndroidLogFormat *p_format, */ AndroidLogPrintFormat android_log_formatFromString(const char *s); -/** +/** * filterExpression: a single filter expression * eg "AT:d" * @@ -74,12 +77,12 @@ AndroidLogPrintFormat android_log_formatFromString(const char *s); * */ -int android_log_addFilterRule(AndroidLogFormat *p_format, +int android_log_addFilterRule(AndroidLogFormat *p_format, const char *filterExpression); -/** - * filterString: a whitespace-separated set of filter expressions +/** + * filterString: a whitespace-separated set of filter expressions * eg "AT:d *:i" * * returns 0 on success and -1 on invalid expression @@ -92,7 +95,7 @@ int android_log_addFilterString(AndroidLogFormat *p_format, const char *filterString); -/** +/** * returns 1 if this log line should be printed based on its priority * and tag, and 0 if it should not */ @@ -129,7 +132,7 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf, * Returns NULL on malloc error */ -char *android_log_formatLogLine ( +char *android_log_formatLogLine ( AndroidLogFormat *p_format, char *defaultBuffer, size_t defaultBufferSize, diff --git a/liblog/logprint.c b/liblog/logprint.c index 0f01542a3..a3f1d7e5f 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -43,6 +43,7 @@ struct AndroidLogFormat_t { FilterInfo *filters; AndroidLogPrintFormat format; bool colored_output; + bool usec_time_output; }; /* @@ -185,6 +186,7 @@ AndroidLogFormat *android_log_format_new() p_ret->global_pri = ANDROID_LOG_VERBOSE; p_ret->format = FORMAT_BRIEF; p_ret->colored_output = false; + p_ret->usec_time_output = false; return p_ret; } @@ -207,13 +209,19 @@ void android_log_format_free(AndroidLogFormat *p_format) -void android_log_setPrintFormat(AndroidLogFormat *p_format, +int android_log_setPrintFormat(AndroidLogFormat *p_format, AndroidLogPrintFormat format) { - if (format == FORMAT_COLOR) + if (format == FORMAT_MODIFIER_COLOR) { p_format->colored_output = true; - else - p_format->format = format; + return 0; + } + if (format == FORMAT_MODIFIER_TIME_USEC) { + p_format->usec_time_output = true; + return 0; + } + p_format->format = format; + return 1; } /** @@ -231,7 +239,8 @@ AndroidLogPrintFormat android_log_formatFromString(const char * formatString) else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; - else if (strcmp(formatString, "color") == 0) format = FORMAT_COLOR; + else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR; + else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC; else format = FORMAT_OFF; return format; @@ -745,7 +754,7 @@ char *android_log_formatLogLine ( struct tm tmBuf; #endif struct tm* ptm; - char timeBuf[32]; + char timeBuf[32]; /* good margin, 23+nul for msec, 26+nul for usec */ char prefixBuf[128], suffixBuf[128]; char priChar; int prefixSuffixIsHeaderFooter = 0; @@ -771,6 +780,14 @@ char *android_log_formatLogLine ( #endif //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + len = strlen(timeBuf); + if (p_format->usec_time_output) { + snprintf(timeBuf + len, sizeof(timeBuf) - len, + ".%06ld", entry->tv_nsec / 1000); + } else { + snprintf(timeBuf + len, sizeof(timeBuf) - len, + ".%03ld", entry->tv_nsec / 1000000); + } /* * Construct a buffer containing the log header and log message. @@ -811,23 +828,21 @@ char *android_log_formatLogLine ( break; case FORMAT_TIME: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, - "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, - priChar, entry->tag, entry->pid); + "%s %c/%-8s(%5d): ", timeBuf, priChar, entry->tag, entry->pid); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_THREADTIME: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, - "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, + "%s %5d %5d %c %-8s: ", timeBuf, entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf + suffixLen, "\n"); ++suffixLen; break; case FORMAT_LONG: len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, - "[ %s.%03ld %5d:%5d %c/%-8s ]\n", - timeBuf, entry->tv_nsec / 1000000, entry->pid, - entry->tid, priChar, entry->tag); + "[ %s %5d:%5d %c/%-8s ]\n", + timeBuf, entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf + suffixLen, "\n\n"); suffixLen += 2; prefixSuffixIsHeaderFooter = 1; diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 2b19b93b8..2c2d785ce 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -235,7 +235,7 @@ static void show_help(const char *cmd) " -r Rotate log every kbytes. Requires -f\n" " -n Sets max number of rotated logs to , default 4\n" " -v Sets the log print format, where is:\n\n" - " brief color long process raw tag thread threadtime time\n\n" + " brief color long process raw tag thread threadtime time usec\n\n" " -D print dividers between each log buffer\n" " -c clear (flush) the entire log and exit\n" " -d dump the log and then exit (don't block)\n" @@ -291,9 +291,7 @@ static int setLogFormat(const char * formatString) return -1; } - android_log_setPrintFormat(g_logformat, format); - - return 0; + return android_log_setPrintFormat(g_logformat, format); } static const char multipliers[][2] = { @@ -569,10 +567,7 @@ int main(int argc, char **argv) if (err < 0) { logcat_panic(true, "Invalid parameter %s to -v\n", optarg); } - - if (strcmp("color", optarg)) { // exception for modifiers - hasSetLogFormat = 1; - } + hasSetLogFormat |= err; break; case 'Q': diff --git a/logd/Android.mk b/logd/Android.mk index e65e9ff79..73da8dc61 100644 --- a/logd/Android.mk +++ b/logd/Android.mk @@ -18,6 +18,7 @@ LOCAL_SRC_FILES := \ LogWhiteBlackList.cpp \ libaudit.c \ LogAudit.cpp \ + LogKlog.cpp \ event.logtags LOCAL_SHARED_LIBRARIES := \ diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp index bdfed3b2b..5489cc945 100644 --- a/logd/CommandListener.cpp +++ b/logd/CommandListener.cpp @@ -33,9 +33,9 @@ #include "LogCommand.h" CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, - LogListener * /*swl*/) - : FrameworkListener(getLogSocket()) - , mBuf(*buf) { + LogListener * /*swl*/) : + FrameworkListener(getLogSocket()), + mBuf(*buf) { // registerCmd(new ShutdownCmd(buf, writer, swl)); registerCmd(new ClearCmd(buf)); registerCmd(new GetBufSizeCmd(buf)); @@ -48,12 +48,12 @@ CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, } CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader, - LogListener *swl) - : LogCommand("shutdown") - , mBuf(*buf) - , mReader(*reader) - , mSwl(*swl) -{ } + LogListener *swl) : + LogCommand("shutdown"), + mBuf(*buf), + mReader(*reader), + mSwl(*swl) { +} int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/, int /*argc*/, @@ -63,10 +63,10 @@ int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/, exit(0); } -CommandListener::ClearCmd::ClearCmd(LogBuffer *buf) - : LogCommand("clear") - , mBuf(*buf) -{ } +CommandListener::ClearCmd::ClearCmd(LogBuffer *buf) : + LogCommand("clear"), + mBuf(*buf) { +} static void setname() { static bool name_set; @@ -100,10 +100,10 @@ int CommandListener::ClearCmd::runCommand(SocketClient *cli, return 0; } -CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf) - : LogCommand("getLogSize") - , mBuf(*buf) -{ } +CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf) : + LogCommand("getLogSize"), + mBuf(*buf) { +} int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli, int argc, char **argv) { @@ -126,10 +126,10 @@ int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli, return 0; } -CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) - : LogCommand("setLogSize") - , mBuf(*buf) -{ } +CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf) : + LogCommand("setLogSize"), + mBuf(*buf) { +} int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli, int argc, char **argv) { @@ -160,10 +160,10 @@ int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli, return 0; } -CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) - : LogCommand("getLogSizeUsed") - , mBuf(*buf) -{ } +CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf) : + LogCommand("getLogSizeUsed"), + mBuf(*buf) { +} int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli, int argc, char **argv) { @@ -186,10 +186,10 @@ int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli, return 0; } -CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf) - : LogCommand("getStatistics") - , mBuf(*buf) -{ } +CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf) : + LogCommand("getStatistics"), + mBuf(*buf) { +} static void package_string(char **strp) { const char *a = *strp; @@ -243,10 +243,10 @@ int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli, return 0; } -CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) - : LogCommand("getPruneList") - , mBuf(*buf) -{ } +CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf) : + LogCommand("getPruneList"), + mBuf(*buf) { +} int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli, int /*argc*/, char ** /*argv*/) { @@ -263,10 +263,10 @@ int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli, return 0; } -CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf) - : LogCommand("setPruneList") - , mBuf(*buf) -{ } +CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf) : + LogCommand("setPruneList"), + mBuf(*buf) { +} int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli, int argc, char **argv) { @@ -301,9 +301,8 @@ int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli, return 0; } -CommandListener::ReinitCmd::ReinitCmd() - : LogCommand("reinit") -{ } +CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") { +} int CommandListener::ReinitCmd::runCommand(SocketClient *cli, int /*argc*/, char ** /*argv*/) { diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp index 26a18610d..d58492586 100644 --- a/logd/FlushCommand.cpp +++ b/logd/FlushCommand.cpp @@ -27,14 +27,14 @@ FlushCommand::FlushCommand(LogReader &reader, unsigned long tail, unsigned int logMask, pid_t pid, - uint64_t start) - : mReader(reader) - , mNonBlock(nonBlock) - , mTail(tail) - , mLogMask(logMask) - , mPid(pid) - , mStart(start) -{ } + uint64_t start) : + mReader(reader), + mNonBlock(nonBlock), + mTail(tail), + mLogMask(logMask), + mPid(pid), + mStart(start) { +} // runSocketCommand is called once for every open client on the // log reader socket. Here we manage and associated the reader diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp index caae54be9..4ec2e59f2 100644 --- a/logd/LogAudit.cpp +++ b/logd/LogAudit.cpp @@ -29,6 +29,7 @@ #include "libaudit.h" #include "LogAudit.h" +#include "LogKlog.h" #define KMSG_PRIORITY(PRI) \ '<', \ @@ -36,12 +37,12 @@ '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, \ '>' -LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) - : SocketListener(getLogSocket(), false) - , logbuf(buf) - , reader(reader) - , fdDmesg(fdDmesg) - , initialized(false) { +LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) : + SocketListener(getLogSocket(), false), + logbuf(buf), + reader(reader), + fdDmesg(fdDmesg), + initialized(false) { static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO), 'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':', ' ', 's', 't', 'a', 'r', 't', '\n' }; @@ -121,6 +122,15 @@ int LogAudit::logPrint(const char *fmt, ...) { && (*cp == ':')) { memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3); memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1); + // + // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to + // differentiate without prejudice, we use 1980 to delineate, earlier + // is monotonic, later is real. + // +# define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60) + if (now.tv_sec < EPOCH_PLUS_10_YEARS) { + LogKlog::convertMonotonicToReal(now); + } } else { now.strptime("", ""); // side effect of setting CLOCK_REALTIME } @@ -223,7 +233,7 @@ int LogAudit::logPrint(const char *fmt, ...) { int LogAudit::log(char *buf) { char *audit = strstr(buf, " audit("); if (!audit) { - return -EXDEV; + return 0; } *audit = '\0'; diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp index b6b612468..c33dca68d 100644 --- a/logd/LogBuffer.cpp +++ b/logd/LogBuffer.cpp @@ -126,8 +126,7 @@ void LogBuffer::init() { } } -LogBuffer::LogBuffer(LastLogTimes *times) - : mTimes(*times) { +LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) { pthread_mutex_init(&mLogElementsLock, NULL); init(); diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp index 164faa9f8..f98d5cdc1 100644 --- a/logd/LogBufferElement.cpp +++ b/logd/LogBufferElement.cpp @@ -34,14 +34,14 @@ atomic_int_fast64_t LogBufferElement::sequence; LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, - const char *msg, unsigned short len) - : mLogId(log_id) - , mUid(uid) - , mPid(pid) - , mTid(tid) - , mMsgLen(len) - , mSequence(sequence.fetch_add(1, memory_order_relaxed)) - , mRealTime(realtime) { + const char *msg, unsigned short len) : + mLogId(log_id), + mUid(uid), + mPid(pid), + mTid(tid), + mMsgLen(len), + mSequence(sequence.fetch_add(1, memory_order_relaxed)), + mRealTime(realtime) { mMsg = new char[len]; memcpy(mMsg, msg, len); } diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h index 75ec59e80..bca30438e 100644 --- a/logd/LogBufferElement.h +++ b/logd/LogBufferElement.h @@ -42,7 +42,7 @@ const char *tagToName(uint32_t tag); } static inline bool worstUidEnabledForLogid(log_id_t id) { - return (id != LOG_ID_CRASH) && (id != LOG_ID_EVENTS); + return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS); } class LogBuffer; diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp index b78c0e0a7..06d865cd5 100644 --- a/logd/LogCommand.cpp +++ b/logd/LogCommand.cpp @@ -23,8 +23,7 @@ #include "LogCommand.h" -LogCommand::LogCommand(const char *cmd) - : FrameworkCommand(cmd) { +LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) { } // gets a list of supplementary group IDs associated with diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp new file mode 100644 index 000000000..8df0d0a84 --- /dev/null +++ b/logd/LogKlog.cpp @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2014 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "LogKlog.h" + +#define KMSG_PRIORITY(PRI) \ + '<', \ + '0' + (LOG_SYSLOG | (PRI)) / 10, \ + '0' + (LOG_SYSLOG | (PRI)) % 10, \ + '>' + +static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' }; + +log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC); + +LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd) : + SocketListener(fdRead, false), + logbuf(buf), + reader(reader), + signature(CLOCK_MONOTONIC), + fdWrite(fdWrite), + fdRead(fdRead), + initialized(false), + enableLogging(true), + auditd(auditd) { + static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n"; + char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4]; + snprintf(buffer, sizeof(buffer), klogd_message, priority_message, + signature.nsec()); + write(fdWrite, buffer, strlen(buffer)); +} + +bool LogKlog::onDataAvailable(SocketClient *cli) { + if (!initialized) { + prctl(PR_SET_NAME, "logd.klogd"); + initialized = true; + enableLogging = false; + } + + char buffer[LOGGER_ENTRY_MAX_PAYLOAD]; + size_t len = 0; + + for(;;) { + ssize_t retval = 0; + if ((sizeof(buffer) - 1 - len) > 0) { + retval = read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len); + } + if ((retval == 0) && (len == 0)) { + break; + } + if (retval < 0) { + return false; + } + len += retval; + bool full = len == (sizeof(buffer) - 1); + char *ep = buffer + len; + *ep = '\0'; + len = 0; + for(char *ptr, *tok = buffer; + ((tok = strtok_r(tok, "\r\n", &ptr))); + tok = NULL) { + if (((tok + strlen(tok)) == ep) && (retval != 0) && full) { + len = strlen(tok); + memmove(buffer, tok, len); + break; + } + if (*tok) { + log(tok); + } + } + } + + return true; +} + + +void LogKlog::calculateCorrection(const log_time &monotonic, + const char *real_string) { + log_time real; + if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) { + return; + } + // kernel report UTC, log_time::strptime is localtime from calendar. + // Bionic and liblog strptime does not support %z or %Z to pick up + // timezone so we are calculating our own correction. + time_t now = real.tv_sec; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + localtime_r(&now, &tm); + real.tv_sec += tm.tm_gmtoff; + correction = real - monotonic; +} + +void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) { + const char *cp; + if ((cp = now.strptime(*buf, "[ %s.%q]"))) { + static const char suspend[] = "PM: suspend entry "; + static const char resume[] = "PM: suspend exit "; + static const char suspended[] = "Suspended for "; + + if (isspace(*cp)) { + ++cp; + } + if (!strncmp(cp, suspend, sizeof(suspend) - 1)) { + calculateCorrection(now, cp + sizeof(suspend) - 1); + } else if (!strncmp(cp, resume, sizeof(resume) - 1)) { + calculateCorrection(now, cp + sizeof(resume) - 1); + } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) { + log_time real; + char *endp; + real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10); + if (*endp == '.') { + real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L; + if (reverse) { + correction -= real; + } else { + correction += real; + } + } + } + + convertMonotonicToReal(now); + *buf = cp; + } else { + now = log_time(CLOCK_REALTIME); + } +} + +// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a +// compensated start time. +void LogKlog::synchronize(const char *buf) { + const char *cp = strstr(buf, "] PM: suspend e"); + if (!cp) { + return; + } + + do { + --cp; + } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.'))); + + log_time now; + sniffTime(now, &cp, true); + + char *suspended = strstr(buf, "] Suspended for "); + if (!suspended || (suspended > cp)) { + return; + } + cp = suspended; + + do { + --cp; + } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.'))); + + sniffTime(now, &cp, true); +} + +// kernel log prefix, convert to a kernel log priority number +static int parseKernelPrio(const char **buf) { + int pri = LOG_USER | LOG_INFO; + const char *cp = *buf; + if (*cp == '<') { + pri = 0; + while(isdigit(*++cp)) { + pri = (pri * 10) + *cp - '0'; + } + if (*cp == '>') { + ++cp; + } else { + cp = *buf; + pri = LOG_USER | LOG_INFO; + } + *buf = cp; + } + return pri; +} + +// Convert kernel log priority number into an Android Logger priority number +static int convertKernelPrioToAndroidPrio(int pri) { + switch(pri & LOG_PRIMASK) { + case LOG_EMERG: + // FALLTHRU + case LOG_ALERT: + // FALLTHRU + case LOG_CRIT: + return ANDROID_LOG_FATAL; + + case LOG_ERR: + return ANDROID_LOG_ERROR; + + case LOG_WARNING: + return ANDROID_LOG_WARN; + + default: + // FALLTHRU + case LOG_NOTICE: + // FALLTHRU + case LOG_INFO: + break; + + case LOG_DEBUG: + return ANDROID_LOG_DEBUG; + } + + return ANDROID_LOG_INFO; +} + +// +// log a message into the kernel log buffer +// +// Filter rules to parse