1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <sys/prctl.h>
18
19#include "FlushCommand.h"
20#include "LogBuffer.h"
21#include "LogTimes.h"
22#include "LogReader.h"
23
24pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
25
26const struct timespec LogTimeEntry::EPOCH = { 0, 1 };
27
28LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
29                           bool nonBlock, unsigned long tail,
30                           unsigned int logMask, pid_t pid,
31                           log_time start)
32        : mRefCount(1)
33        , mRelease(false)
34        , mError(false)
35        , threadRunning(false)
36        , mReader(reader)
37        , mLogMask(logMask)
38        , mPid(pid)
39        , skipAhead(0)
40        , mCount(0)
41        , mTail(tail)
42        , mIndex(0)
43        , mClient(client)
44        , mStart(start)
45        , mNonBlock(nonBlock)
46        , mEnd(CLOCK_MONOTONIC)
47{
48        pthread_cond_init(&threadTriggeredCondition, NULL);
49}
50
51void LogTimeEntry::startReader_Locked(void) {
52    pthread_attr_t attr;
53
54    threadRunning = true;
55
56    if (!pthread_attr_init(&attr)) {
57        if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
58            if (!pthread_create(&mThread, &attr,
59                                LogTimeEntry::threadStart, this)) {
60                pthread_attr_destroy(&attr);
61                return;
62            }
63        }
64        pthread_attr_destroy(&attr);
65    }
66    threadRunning = false;
67    if (mClient) {
68        mClient->decRef();
69    }
70    decRef_Locked();
71}
72
73void LogTimeEntry::threadStop(void *obj) {
74    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
75
76    lock();
77
78    if (me->mNonBlock) {
79        me->error_Locked();
80    }
81
82    SocketClient *client = me->mClient;
83
84    if (me->isError_Locked()) {
85        LogReader &reader = me->mReader;
86        LastLogTimes &times = reader.logbuf().mTimes;
87
88        LastLogTimes::iterator it = times.begin();
89        while(it != times.end()) {
90            if (*it == me) {
91                times.erase(it);
92                me->release_Locked();
93                break;
94            }
95            it++;
96        }
97
98        me->mClient = NULL;
99        reader.release(client);
100    }
101
102    if (client) {
103        client->decRef();
104    }
105
106    me->threadRunning = false;
107    me->decRef_Locked();
108
109    unlock();
110}
111
112void *LogTimeEntry::threadStart(void *obj) {
113    prctl(PR_SET_NAME, "logd.reader.per");
114
115    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
116
117    pthread_cleanup_push(threadStop, obj);
118
119    SocketClient *client = me->mClient;
120    if (!client) {
121        me->error();
122        return NULL;
123    }
124
125    LogBuffer &logbuf = me->mReader.logbuf();
126
127    bool privileged = FlushCommand::hasReadLogs(client);
128
129    lock();
130
131    while (me->threadRunning && !me->isError_Locked()) {
132        log_time start = me->mStart;
133
134        unlock();
135
136        if (me->mTail) {
137            logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
138        }
139        start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);
140
141        lock();
142
143        if (start == LogBufferElement::FLUSH_ERROR) {
144            me->error_Locked();
145        }
146
147        if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
148            break;
149        }
150
151        pthread_cond_wait(&me->threadTriggeredCondition, &timesLock);
152    }
153
154    unlock();
155
156    pthread_cleanup_pop(true);
157
158    return NULL;
159}
160
161// A first pass to count the number of elements
162bool LogTimeEntry::FilterFirstPass(const LogBufferElement *element, void *obj) {
163    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
164
165    LogTimeEntry::lock();
166
167    if (me->mCount == 0) {
168        me->mStart = element->getMonotonicTime();
169    }
170
171    if ((!me->mPid || (me->mPid == element->getPid()))
172            && (me->mLogMask & (1 << element->getLogId()))) {
173        ++me->mCount;
174    }
175
176    LogTimeEntry::unlock();
177
178    return false;
179}
180
181// A second pass to send the selected elements
182bool LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) {
183    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
184
185    LogTimeEntry::lock();
186
187    if (me->skipAhead) {
188        me->skipAhead--;
189        goto skip;
190    }
191
192    me->mStart = element->getMonotonicTime();
193
194    // Truncate to close race between first and second pass
195    if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
196        goto skip;
197    }
198
199    if ((me->mLogMask & (1 << element->getLogId())) == 0) {
200        goto skip;
201    }
202
203    if (me->mPid && (me->mPid != element->getPid())) {
204        goto skip;
205    }
206
207    if (me->isError_Locked()) {
208        goto skip;
209    }
210
211    if (!me->mTail) {
212        goto ok;
213    }
214
215    ++me->mIndex;
216
217    if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
218        goto skip;
219    }
220
221    if (!me->mNonBlock) {
222        me->mTail = 0;
223    }
224
225ok:
226    if (!me->skipAhead) {
227        LogTimeEntry::unlock();
228        return true;
229    }
230    // FALLTHRU
231
232skip:
233    LogTimeEntry::unlock();
234    return false;
235}
236