blob: 67cc65ecfe905e2de86c12c69c8a718868185c28 [file] [log] [blame]
/*
* 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 "FlushCommand.h"
#include "LogBuffer.h"
#include "LogTimes.h"
#include "LogReader.h"
pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
const struct timespec LogTimeEntry::EPOCH = { 0, 1 };
LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
bool nonBlock, unsigned long tail,
unsigned int logMask, pid_t pid)
: mRefCount(1)
, mRelease(false)
, mError(false)
, threadRunning(false)
, threadTriggered(true)
, mReader(reader)
, mLogMask(logMask)
, mPid(pid)
, skipAhead(0)
, mCount(0)
, mTail(tail)
, mIndex(0)
, mClient(client)
, mStart(EPOCH)
, mNonBlock(nonBlock)
, mEnd(CLOCK_MONOTONIC)
{ }
void LogTimeEntry::startReader_Locked(void) {
threadRunning = true;
if (pthread_create(&mThread, NULL, LogTimeEntry::threadStart, this)) {
threadRunning = false;
if (mClient) {
mClient->decRef();
}
decRef_Locked();
}
}
void LogTimeEntry::threadStop(void *obj) {
LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
lock();
me->threadRunning = false;
if (me->mNonBlock) {
me->error_Locked();
}
SocketClient *client = me->mClient;
if (me->isError_Locked()) {
LogReader &reader = me->mReader;
LastLogTimes &times = reader.logbuf().mTimes;
LastLogTimes::iterator it = times.begin();
while(it != times.end()) {
if (*it == me) {
times.erase(it);
me->release_Locked();
break;
}
it++;
}
me->mClient = NULL;
reader.release(client);
}
if (client) {
client->decRef();
}
me->decRef_Locked();
unlock();
}
void *LogTimeEntry::threadStart(void *obj) {
LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
pthread_cleanup_push(threadStop, obj);
SocketClient *client = me->mClient;
if (!client) {
me->error();
pthread_exit(NULL);
}
LogBuffer &logbuf = me->mReader.logbuf();
bool privileged = FlushCommand::hasReadLogs(client);
lock();
me->threadTriggered = true;
while(me->threadTriggered && !me->isError_Locked()) {
me->threadTriggered = false;
log_time start = me->mStart;
unlock();
if (me->mTail) {
logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
}
start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);
if (start == LogBufferElement::FLUSH_ERROR) {
me->error();
}
if (me->mNonBlock) {
lock();
break;
}
sched_yield();
lock();
}
unlock();
pthread_exit(NULL);
pthread_cleanup_pop(true);
return NULL;
}
// A first pass to count the number of elements
bool LogTimeEntry::FilterFirstPass(const LogBufferElement *element, void *obj) {
LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
LogTimeEntry::lock();
if (me->mCount == 0) {
me->mStart = element->getMonotonicTime();
}
if ((!me->mPid || (me->mPid == element->getPid()))
&& (me->mLogMask & (1 << element->getLogId()))) {
++me->mCount;
}
LogTimeEntry::unlock();
return false;
}
// A second pass to send the selected elements
bool LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) {
LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
LogTimeEntry::lock();
if (me->skipAhead) {
me->skipAhead--;
}
me->mStart = element->getMonotonicTime();
// Truncate to close race between first and second pass
if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
goto skip;
}
if ((me->mLogMask & (1 << element->getLogId())) == 0) {
goto skip;
}
if (me->mPid && (me->mPid != element->getPid())) {
goto skip;
}
if (me->isError_Locked()) {
goto skip;
}
if (!me->mTail) {
goto ok;
}
++me->mIndex;
if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
goto skip;
}
if (!me->mNonBlock) {
me->mTail = 0;
}
ok:
if (!me->skipAhead) {
LogTimeEntry::unlock();
return true;
}
// FALLTHRU
skip:
LogTimeEntry::unlock();
return false;
}