From 0175b0747a1f55329109e84c9a1322dcb95e2848 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Wed, 26 Feb 2014 09:50:16 -0800 Subject: logd: initial checkin. * Create a new userspace log daemon for handling logging messages. Original-Change-Id: I75267df16359684490121e6c31cca48614d79856 Signed-off-by: Nick Kralevich * Merge conflicts * rename new syslog daemon to logd to prevent confusion with bionic syslog * replace racy getGroups call with KISS call to client->getGid() * Timestamps are filed at logging source * insert entries into list in timestamp order * Added LogTimeEntry tail filtration handling * Added region locking around LogWriter list * separate threads for each writer * /dev/socket/logd* permissions Signed-off-by: Mark Salyzyn (cherry picked from commit 3e76e0a49760c4970b7cda6153e51026af98e4f3) Author: Nick Kralevich Change-Id: Ice88b1412d8f9daa7f9119b2b5aaf684a5e28098 --- logd/LogBuffer.cpp | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 logd/LogBuffer.cpp (limited to 'logd/LogBuffer.cpp') diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp new file mode 100644 index 0000000..8b273e2 --- /dev/null +++ b/logd/LogBuffer.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2012-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 "LogBuffer.h" +#include "LogReader.h" + +#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here? + +LogBuffer::LogBuffer(LastLogTimes *times) + : mTimes(*times) { + int i; + for (i = 0; i < LOG_ID_MAX; i++) { + mSizes[i] = 0; + mElements[i] = 0; + } + pthread_mutex_init(&mLogElementsLock, NULL); +} + +void LogBuffer::log(log_id_t log_id, struct timespec realtime, + uid_t uid, pid_t pid, const char *msg, + unsigned short len) { + if ((log_id >= LOG_ID_MAX) || (log_id < 0)) { + return; + } + LogBufferElement *elem = new LogBufferElement(log_id, realtime, + uid, pid, msg, len); + + pthread_mutex_lock(&mLogElementsLock); + + // Insert elements in time sorted order if possible + // NB: if end is region locked, place element at end of list + LogBufferElementCollection::iterator it = mLogElements.end(); + LogBufferElementCollection::iterator last = it; + while (--it != mLogElements.begin()) { + if ((*it)->getRealTime() <= elem->getRealTime()) { + break; + } + last = it; + } + if (last == mLogElements.end()) { + mLogElements.push_back(elem); + } else { + log_time end; + bool end_set = false; + bool end_always = false; + + LogTimeEntry::lock(); + + LastLogTimes::iterator t = mTimes.begin(); + while(t != mTimes.end()) { + LogTimeEntry *entry = (*t); + if (entry->owned_Locked()) { + if (!entry->mNonBlock) { + end_always = true; + break; + } + if (!end_set || (end <= entry->mEnd)) { + end = entry->mEnd; + end_set = true; + } + } + t++; + } + + if (end_always + || (end_set && (end >= (*last)->getMonotonicTime()))) { + mLogElements.push_back(elem); + } else { + mLogElements.insert(last,elem); + } + + LogTimeEntry::unlock(); + } + + mSizes[log_id] += len; + mElements[log_id]++; + maybePrune(log_id); + pthread_mutex_unlock(&mLogElementsLock); +} + +// If we're using more than 256K of memory for log entries, prune +// 10% of the log entries. +// +// mLogElementsLock must be held when this function is called. +void LogBuffer::maybePrune(log_id_t id) { + if (mSizes[id] > LOG_BUFFER_SIZE) { + prune(id, mElements[id] / 10); + } +} + +// prune "pruneRows" of type "id" from the buffer. +// +// mLogElementsLock must be held when this function is called. +void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { + LogTimeEntry *oldest = NULL; + + LogTimeEntry::lock(); + + // Region locked? + LastLogTimes::iterator t = mTimes.begin(); + while(t != mTimes.end()) { + LogTimeEntry *entry = (*t); + if (entry->owned_Locked() + && (!oldest || (oldest->mStart > entry->mStart))) { + oldest = entry; + } + t++; + } + + LogBufferElementCollection::iterator it = mLogElements.begin(); + while((pruneRows > 0) && (it != mLogElements.end())) { + LogBufferElement *e = *it; + if (e->getLogId() == id) { + if (oldest && (oldest->mStart <= e->getMonotonicTime())) { + if (mSizes[id] > (2 * LOG_BUFFER_SIZE)) { + // kick a misbehaving log reader client off the island + oldest->release_Locked(); + } else { + oldest->triggerSkip_Locked(pruneRows); + } + break; + } + it = mLogElements.erase(it); + mSizes[id] -= e->getMsgLen(); + mElements[id]--; + delete e; + pruneRows--; + } else { + it++; + } + } + + LogTimeEntry::unlock(); +} + +// clear all rows of type "id" from the buffer. +void LogBuffer::clear(log_id_t id) { + pthread_mutex_lock(&mLogElementsLock); + prune(id, ULONG_MAX); + pthread_mutex_unlock(&mLogElementsLock); +} + +// get the used space associated with "id". +unsigned long LogBuffer::getSizeUsed(log_id_t id) { + pthread_mutex_lock(&mLogElementsLock); + unsigned long retval = mSizes[id]; + pthread_mutex_unlock(&mLogElementsLock); + return retval; +} + +// get the total space allocated to "id" +unsigned long LogBuffer::getSize(log_id_t /*id*/) { + return LOG_BUFFER_SIZE; +} + +struct timespec LogBuffer::flushTo( + SocketClient *reader, const struct timespec start, bool privileged, + bool (*filter)(const LogBufferElement *element, void *arg), void *arg) { + LogBufferElementCollection::iterator it; + log_time max = start; + uid_t uid = reader->getUid(); + + pthread_mutex_lock(&mLogElementsLock); + for (it = mLogElements.begin(); it != mLogElements.end(); ++it) { + LogBufferElement *element = *it; + + if (!privileged && (element->getUid() != uid)) { + continue; + } + + if (element->getMonotonicTime() <= start) { + continue; + } + + // NB: calling out to another object with mLogElementsLock held (safe) + if (filter && !(*filter)(element, arg)) { + continue; + } + + pthread_mutex_unlock(&mLogElementsLock); + + // range locking in LastLogTimes looks after us + max = element->flushTo(reader); + + if (max == element->FLUSH_ERROR) { + return max; + } + + pthread_mutex_lock(&mLogElementsLock); + } + pthread_mutex_unlock(&mLogElementsLock); + + return max; +} -- cgit v1.1