am 1fad20cf: am 0c6c4150: Merge changes I2cda9dd7,Ifb560850,I8d4c7c5a

* commit '1fad20cf4943cbc231fe8541fb507b67f71fa9b1':
  logd: Cleanup
  logcat: liblog: Add "usec" format argument
  logd: Add klogd
This commit is contained in:
Mark Salyzyn 2015-05-13 16:48:59 +00:00 committed by Android Git Automerger
commit 5689c8b86f
20 changed files with 729 additions and 161 deletions

View file

@ -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,

View file

@ -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;

View file

@ -235,7 +235,7 @@ static void show_help(const char *cmd)
" -r <kbytes> Rotate log every kbytes. Requires -f\n"
" -n <count> Sets max number of rotated logs to <count>, default 4\n"
" -v <format> Sets the log print format, where <format> 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':

View file

@ -18,6 +18,7 @@ LOCAL_SRC_FILES := \
LogWhiteBlackList.cpp \
libaudit.c \
LogAudit.cpp \
LogKlog.cpp \
event.logtags
LOCAL_SHARED_LIBRARIES := \

View file

@ -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*/) {

View file

@ -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

View file

@ -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';

View file

@ -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();

View file

@ -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);
}

View file

@ -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;

View file

@ -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

454
logd/LogKlog.cpp Normal file
View file

@ -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 <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/uio.h>
#include <syslog.h>
#include <log/logger.h>
#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 <PRI> <TIME> <tag> and <message> in order for
// them to appear correct in the logcat output:
//
// LOG_KERN (0):
// <PRI>[<TIME>] <tag> ":" <message>
// <PRI>[<TIME>] <tag> <tag> ":" <message>
// <PRI>[<TIME>] <tag> <tag>_work ":" <message>
// <PRI>[<TIME>] <tag> '<tag>.<num>' ":" <message>
// <PRI>[<TIME>] <tag> '<tag><num>' ":" <message>
// <PRI>[<TIME>] <tag>_host '<tag>.<num>' ":" <message>
// (unimplemented) <PRI>[<TIME>] <tag> '<num>.<tag>' ":" <message>
// <PRI>[<TIME>] "[INFO]"<tag> : <message>
// <PRI>[<TIME>] "------------[ cut here ]------------" (?)
// <PRI>[<TIME>] "---[ end trace 3225a3070ca3e4ac ]---" (?)
// LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_SYSLOG, LOG_LPR, LOG_NEWS
// LOG_UUCP, LOG_CRON, LOG_AUTHPRIV, LOG_FTP:
// <PRI+TAG>[<TIME>] (see sys/syslog.h)
// Observe:
// Minimum tag length = 3 NB: drops things like r5:c00bbadf, but allow PM:
// Maximum tag words = 2
// Maximum tag length = 16 NB: we are thinking of how ugly logcat can get.
// Not a Tag if there is no message content.
// leading additional spaces means no tag, inherit last tag.
// Not a Tag if <tag>: is "ERROR:", "WARNING:", "INFO:" or "CPU:"
// Drop:
// empty messages
// messages with ' audit(' in them if auditd is running
// logd.klogd:
// return -1 if message logd.klogd: <signature>
//
int LogKlog::log(const char *buf) {
if (auditd && strstr(buf, " audit(")) {
return 0;
}
int pri = parseKernelPrio(&buf);
log_time now;
sniffTime(now, &buf, false);
// sniff for start marker
const char klogd_message[] = "logd.klogd: ";
if (!strncmp(buf, klogd_message, sizeof(klogd_message) - 1)) {
char *endp;
uint64_t sig = strtoll(buf + sizeof(klogd_message) - 1, &endp, 10);
if (sig == signature.nsec()) {
if (initialized) {
enableLogging = true;
} else {
enableLogging = false;
}
return -1;
}
return 0;
}
if (!enableLogging) {
return 0;
}
// Parse pid, tid and uid (not possible)
const pid_t pid = 0;
const pid_t tid = 0;
const uid_t uid = 0;
// Parse (rules at top) to pull out a tag from the incoming kernel message.
// Some may view the following as an ugly heuristic, the desire is to
// beautify the kernel logs into an Android Logging format; the goal is
// admirable but costly.
while (isspace(*buf)) {
++buf;
}
if (!*buf) {
return 0;
}
const char *start = buf;
const char *tag = "";
const char *etag = tag;
if (!isspace(*buf)) {
const char *bt, *et, *cp;
bt = buf;
if (!strncmp(buf, "[INFO]", 6)) {
// <PRI>[<TIME>] "[INFO]"<tag> ":" message
bt = buf + 6;
}
for(et = bt; *et && (*et != ':') && !isspace(*et); ++et);
for(cp = et; isspace(*cp); ++cp);
size_t size;
if (*cp == ':') {
// One Word
tag = bt;
etag = et;
buf = cp + 1;
} else {
size = et - bt;
if (strncmp(bt, cp, size)) {
// <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
if (!strncmp(bt + size - 5, "_host", 5)
&& !strncmp(bt, cp, size - 5)) {
const char *b = cp;
cp += size - 5;
if (*cp == '.') {
while (!isspace(*++cp) && (*cp != ':'));
const char *e;
for(e = cp; isspace(*cp); ++cp);
if (*cp == ':') {
tag = b;
etag = e;
buf = cp + 1;
}
}
} else {
while (!isspace(*++cp) && (*cp != ':'));
const char *e;
for(e = cp; isspace(*cp); ++cp);
// Two words
if (*cp == ':') {
tag = bt;
etag = e;
buf = cp + 1;
}
}
} else if (isspace(cp[size])) {
const char *b = cp;
cp += size;
while (isspace(*++cp));
// <PRI>[<TIME>] <tag> <tag> : message
if (*cp == ':') {
tag = bt;
etag = et;
buf = cp + 1;
}
} else if (cp[size] == ':') {
// <PRI>[<TIME>] <tag> <tag> : message
tag = bt;
etag = et;
buf = cp + size + 1;
} else if ((cp[size] == '.') || isdigit(cp[size])) {
// <PRI>[<TIME>] <tag> '<tag>.<num>' : message
// <PRI>[<TIME>] <tag> '<tag><num>' : message
const char *b = cp;
cp += size;
while (!isspace(*++cp) && (*cp != ':'));
const char *e = cp;
while (isspace(*cp)) {
++cp;
}
if (*cp == ':') {
tag = b;
etag = e;
buf = cp + 1;
}
} else {
while (!isspace(*++cp) && (*cp != ':'));
const char *e = cp;
while (isspace(*cp)) {
++cp;
}
// Two words
if (*cp == ':') {
tag = bt;
etag = e;
buf = cp + 1;
}
}
}
size = etag - tag;
if ((size <= 1)
|| ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
|| ((size == 3) && !strncmp(tag, "CPU", 3))
|| ((size == 7) && !strncmp(tag, "WARNING", 7))
|| ((size == 5) && !strncmp(tag, "ERROR", 5))
|| ((size == 4) && !strncmp(tag, "INFO", 4))) {
buf = start;
etag = tag = "";
}
}
size_t l = etag - tag;
while (isspace(*buf)) {
++buf;
}
size_t n = 1 + l + 1 + strlen(buf) + 1;
// Allocate a buffer to hold the interpreted log message
int rc = n;
char *newstr = reinterpret_cast<char *>(malloc(n));
if (!newstr) {
rc = -ENOMEM;
return rc;
}
char *np = newstr;
// Convert priority into single-byte Android logger priority
*np = convertKernelPrioToAndroidPrio(pri);
++np;
// Copy parsed tag following priority
strncpy(np, tag, l);
np += l;
*np = '\0';
++np;
// Copy main message to the remainder
strcpy(np, buf);
// Log message
rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
(n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
free(newstr);
// notify readers
if (!rc) {
reader->notifyNewLog();
}
return rc;
}

55
logd/LogKlog.h Normal file
View file

@ -0,0 +1,55 @@
/*
* 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.
*/
#ifndef _LOGD_LOG_KLOG_H__
#define _LOGD_LOG_KLOG_H__
#include <sysutils/SocketListener.h>
#include <log/log_read.h>
#include "LogReader.h"
class LogKlog : public SocketListener {
LogBuffer *logbuf;
LogReader *reader;
const log_time signature;
const int fdWrite; // /dev/kmsg
const int fdRead; // /proc/kmsg
// Set once thread is started, separates KLOG_ACTION_READ_ALL
// and KLOG_ACTION_READ phases.
bool initialized;
// Used during each of the above phases to control logging.
bool enableLogging;
// set if we are also running auditd, to filter out audit reports from
// our copy of the kernel log
bool auditd;
static log_time correction;
public:
LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd);
int log(const char *buf);
void synchronize(const char *buf);
static void convertMonotonicToReal(log_time &real) { real += correction; }
protected:
void sniffTime(log_time &now, const char **buf, bool reverse);
void calculateCorrection(const log_time &monotonic, const char *real_string);
virtual bool onDataAvailable(SocketClient *cli);
};
#endif

View file

@ -28,11 +28,11 @@
#include "LogListener.h"
LogListener::LogListener(LogBuffer *buf, LogReader *reader)
: SocketListener(getLogSocket(), false)
, logbuf(buf)
, reader(reader)
{ }
LogListener::LogListener(LogBuffer *buf, LogReader *reader) :
SocketListener(getLogSocket(), false),
logbuf(buf),
reader(reader) {
}
bool LogListener::onDataAvailable(SocketClient *cli) {
static bool name_set;
@ -88,7 +88,7 @@ bool LogListener::onDataAvailable(SocketClient *cli) {
}
android_log_header_t *header = reinterpret_cast<android_log_header_t *>(buffer);
if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX) {
if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX || header->id == LOG_ID_KERNEL) {
return false;
}

View file

@ -24,10 +24,10 @@
#include "LogReader.h"
#include "FlushCommand.h"
LogReader::LogReader(LogBuffer *logbuf)
: SocketListener(getLogSocket(), true)
, mLogbuf(*logbuf)
{ }
LogReader::LogReader(LogBuffer *logbuf) :
SocketListener(getLogSocket(), true),
mLogbuf(*logbuf) {
}
// When we are notified a new log entry is available, inform
// all of our listening sockets.
@ -116,14 +116,14 @@ bool LogReader::onDataAvailable(SocketClient *cli) {
uint64_t last;
public:
LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence)
: mPid(pid)
, mLogMask(logMask)
, startTimeSet(false)
, start(start)
, sequence(sequence)
, last(sequence)
{ }
LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
mPid(pid),
mLogMask(logMask),
startTimeSet(false),
start(start),
sequence(sequence),
last(sequence) {
}
static int callback(const LogBufferElement *element, void *obj) {
LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);

View file

@ -26,8 +26,7 @@
#include "LogStatistics.h"
LogStatistics::LogStatistics()
: enable(false) {
LogStatistics::LogStatistics() : enable(false) {
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
@ -41,8 +40,8 @@ namespace android {
// caller must own and free character string
char *pidToName(pid_t pid) {
char *retval = NULL;
if (pid == 0) { // special case from auditd for kernel
retval = strdup("logd.auditd");
if (pid == 0) { // special case from auditd/klogd for kernel
retval = strdup("logd");
} else {
char buffer[512];
snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
@ -70,11 +69,15 @@ void LogStatistics::add(LogBufferElement *e) {
mSizes[log_id] += size;
++mElements[log_id];
uidTable[log_id].add(e->getUid(), e);
mSizesTotal[log_id] += size;
++mElementsTotal[log_id];
if (log_id == LOG_ID_KERNEL) {
return;
}
uidTable[log_id].add(e->getUid(), e);
if (!enable) {
return;
}
@ -93,6 +96,10 @@ void LogStatistics::subtract(LogBufferElement *e) {
mSizes[log_id] -= size;
--mElements[log_id];
if (log_id == LOG_ID_KERNEL) {
return;
}
uidTable[log_id].subtract(e->getUid(), e);
if (!enable) {

View file

@ -26,24 +26,23 @@ pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
bool nonBlock, unsigned long tail,
unsigned int logMask, pid_t pid,
uint64_t start)
: mRefCount(1)
, mRelease(false)
, mError(false)
, threadRunning(false)
, mReader(reader)
, mLogMask(logMask)
, mPid(pid)
, mCount(0)
, mTail(tail)
, mIndex(0)
, mClient(client)
, mStart(start)
, mNonBlock(nonBlock)
, mEnd(LogBufferElement::getCurrentSequence())
{
pthread_cond_init(&threadTriggeredCondition, NULL);
cleanSkip_Locked();
uint64_t start) :
mRefCount(1),
mRelease(false),
mError(false),
threadRunning(false),
mReader(reader),
mLogMask(logMask),
mPid(pid),
mCount(0),
mTail(tail),
mIndex(0),
mClient(client),
mStart(start),
mNonBlock(nonBlock),
mEnd(LogBufferElement::getCurrentSequence()) {
pthread_cond_init(&threadTriggeredCondition, NULL);
cleanSkip_Locked();
}
void LogTimeEntry::startReader_Locked(void) {

View file

@ -22,10 +22,8 @@
// White and Black list
Prune::Prune(uid_t uid, pid_t pid)
: mUid(uid)
, mPid(pid)
{ }
Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
}
int Prune::cmp(uid_t uid, pid_t pid) const {
if ((mUid == uid_all) || (mUid == uid)) {
@ -51,8 +49,7 @@ void Prune::format(char **strp) {
}
}
PruneList::PruneList()
: mWorstUidEnabled(true) {
PruneList::PruneList() : mWorstUidEnabled(true) {
mNaughty.clear();
mNice.clear();
}

View file

@ -4,9 +4,12 @@ name type default description
logd.auditd bool true Enable selinux audit daemon
logd.auditd.dmesg bool true selinux audit messages duplicated and
sent on to dmesg log
logd.klogd bool depends Enable klogd daemon
logd.statistics bool depends Enable logcat -S statistics.
ro.config.low_ram bool false if true, logd.statistics default false
ro.build.type string if user, logd.statistics default false
ro.config.low_ram bool false if true, logd.statistics & logd.klogd
default false
ro.build.type string if user, logd.statistics & logd.klogd
default false
persist.logd.size number 256K default size of the buffer for all
log ids at initial startup, at runtime
use: logcat -b all -G <value>

View file

@ -42,6 +42,7 @@
#include "LogBuffer.h"
#include "LogListener.h"
#include "LogAudit.h"
#include "LogKlog.h"
#define KMSG_PRIORITY(PRI) \
'<', \
@ -256,6 +257,17 @@ const char *android::tagToName(uint32_t tag) {
return android_lookupEventTag(map, tag);
}
static bool property_get_bool_svelte(const char *key) {
bool not_user;
{
char property[PROPERTY_VALUE_MAX];
property_get("ro.build.type", property, "");
not_user = !!strcmp(property, "user");
}
return property_get_bool(key, not_user
&& !property_get_bool("ro.config.low_ram", false));
}
// Foreground waits for exit of the main persistent threads
// that are started here. The threads are created to manage
// UNIX domain client sockets for writing, reading and
@ -263,6 +275,11 @@ const char *android::tagToName(uint32_t tag) {
// logging plugins like auditd and restart control. Additional
// transitory per-client threads are created for each reader.
int main(int argc, char *argv[]) {
int fdPmesg = -1;
bool klogd = property_get_bool_svelte("logd.klogd");
if (klogd) {
fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
}
fdDmesg = open("/dev/kmsg", O_WRONLY);
// issue reinit command. KISS argument parsing.
@ -339,14 +356,8 @@ int main(int argc, char *argv[]) {
signal(SIGHUP, reinit_signal_handler);
{
char property[PROPERTY_VALUE_MAX];
property_get("ro.build.type", property, "");
if (property_get_bool("logd.statistics",
!!strcmp(property, "user")
&& !property_get_bool("ro.config.low_ram", false))) {
logBuf->enableStatistics();
}
if (property_get_bool_svelte("logd.statistics")) {
logBuf->enableStatistics();
}
// LogReader listens on /dev/socket/logdr. When a client
@ -381,12 +392,18 @@ int main(int argc, char *argv[]) {
bool auditd = property_get_bool("logd.auditd", true);
LogAudit *al = NULL;
if (auditd) {
bool dmesg = property_get_bool("logd.auditd.dmesg", true);
al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
}
// failure is an option ... messages are in dmesg (required by standard)
LogAudit *al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
LogKlog *kl = NULL;
if (klogd) {
kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
}
if (al || kl) {
int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
if (len > 0) {
len++;
@ -394,16 +411,31 @@ int main(int argc, char *argv[]) {
int rc = klogctl(KLOG_READ_ALL, buf, len);
if (rc >= 0) {
buf[len - 1] = '\0';
buf[len - 1] = '\0';
for (char *ptr, *tok = buf; (tok = strtok_r(tok, "\r\n", &ptr)); tok = NULL) {
al->log(tok);
if ((rc >= 0) && kl) {
kl->synchronize(buf);
}
for (char *ptr, *tok = buf;
(rc >= 0) && ((tok = strtok_r(tok, "\r\n", &ptr)));
tok = NULL) {
if (al) {
rc = al->log(tok);
}
if (kl) {
rc = kl->log(tok);
}
}
}
if (al->startListener()) {
// failure is an option ... messages are in dmesg (required by standard)
if (kl && kl->startListener()) {
delete kl;
}
if (al && al->startListener()) {
delete al;
}
}