LogReader.cpp revision 8daa9af02dc0e63ce220e3fa95bf5fe4d6b7a99a
1/*
2 * Copyright (C) 2012-2013 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 <ctype.h>
18#include <poll.h>
19#include <sys/prctl.h>
20#include <sys/socket.h>
21
22#include <cutils/sockets.h>
23
24#include "LogReader.h"
25#include "FlushCommand.h"
26
27LogReader::LogReader(LogBuffer *logbuf)
28        : SocketListener(getLogSocket(), true)
29        , mLogbuf(*logbuf)
30{ }
31
32// When we are notified a new log entry is available, inform
33// all of our listening sockets.
34void LogReader::notifyNewLog() {
35    FlushCommand command(*this);
36    runOnEachSocket(&command);
37}
38
39bool LogReader::onDataAvailable(SocketClient *cli) {
40    prctl(PR_SET_NAME, "logd.reader");
41
42    char buffer[255];
43
44    int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
45    if (len <= 0) {
46        doSocketDelete(cli);
47        return false;
48    }
49    buffer[len] = '\0';
50
51    unsigned long tail = 0;
52    static const char _tail[] = " tail=";
53    char *cp = strstr(buffer, _tail);
54    if (cp) {
55        tail = atol(cp + sizeof(_tail) - 1);
56    }
57
58    log_time start(log_time::EPOCH);
59    static const char _start[] = " start=";
60    cp = strstr(buffer, _start);
61    if (cp) {
62        // Parse errors will result in current time
63        start.strptime(cp + sizeof(_start) - 1, "%s.%q");
64    }
65
66    unsigned int logMask = -1;
67    static const char _logIds[] = " lids=";
68    cp = strstr(buffer, _logIds);
69    if (cp) {
70        logMask = 0;
71        cp += sizeof(_logIds) - 1;
72        while (*cp && *cp != '\0') {
73            int val = 0;
74            while (isdigit(*cp)) {
75                val = val * 10 + *cp - '0';
76                ++cp;
77            }
78            logMask |= 1 << val;
79            if (*cp != ',') {
80                break;
81            }
82            ++cp;
83        }
84    }
85
86    pid_t pid = 0;
87    static const char _pid[] = " pid=";
88    cp = strstr(buffer, _pid);
89    if (cp) {
90        pid = atol(cp + sizeof(_pid) - 1);
91    }
92
93    bool nonBlock = false;
94    if (strncmp(buffer, "dumpAndClose", 12) == 0) {
95        nonBlock = true;
96    }
97
98    // Convert realtime to monotonic time
99    if (start == log_time::EPOCH) {
100        start = LogTimeEntry::EPOCH;
101    } else {
102        class LogFindStart {
103            const pid_t mPid;
104            const unsigned mLogMask;
105            bool startTimeSet;
106            log_time &start;
107            log_time last;
108
109        public:
110            LogFindStart(unsigned logMask, pid_t pid, log_time &start)
111                    : mPid(pid)
112                    , mLogMask(logMask)
113                    , startTimeSet(false)
114                    , start(start)
115                    , last(LogTimeEntry::EPOCH)
116            { }
117
118            static bool callback(const LogBufferElement *element, void *obj) {
119                LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
120                if (!me->startTimeSet
121                        && (!me->mPid || (me->mPid == element->getPid()))
122                        && (me->mLogMask & (1 << element->getLogId()))) {
123                    if (me->start == element->getRealTime()) {
124                        me->start = element->getMonotonicTime();
125                        me->startTimeSet = true;
126                    } else {
127                        if (me->start < element->getRealTime()) {
128                            me->start = me->last;
129                            me->startTimeSet = true;
130                        }
131                        me->last = element->getMonotonicTime();
132                    }
133                }
134                return false;
135            }
136
137            bool found() { return startTimeSet; }
138        } logFindStart(logMask, pid, start);
139
140        logbuf().flushTo(cli, LogTimeEntry::EPOCH,
141                         FlushCommand::hasReadLogs(cli),
142                         logFindStart.callback, &logFindStart);
143
144        if (!logFindStart.found()) {
145            if (nonBlock) {
146                doSocketDelete(cli);
147                return false;
148            }
149            log_time now(CLOCK_MONOTONIC);
150            start = now;
151        }
152    }
153
154    FlushCommand command(*this, nonBlock, tail, logMask, pid, start);
155    command.runSocketCommand(cli);
156    return true;
157}
158
159void LogReader::doSocketDelete(SocketClient *cli) {
160    LastLogTimes &times = mLogbuf.mTimes;
161    LogTimeEntry::lock();
162    LastLogTimes::iterator it = times.begin();
163    while(it != times.end()) {
164        LogTimeEntry *entry = (*it);
165        if (entry->mClient == cli) {
166            times.erase(it);
167            entry->release_Locked();
168            break;
169        }
170        it++;
171    }
172    LogTimeEntry::unlock();
173}
174
175int LogReader::getLogSocket() {
176    static const char socketName[] = "logdr";
177    int sock = android_get_control_socket(socketName);
178
179    if (sock < 0) {
180        sock = socket_local_server(socketName,
181                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
182                                   SOCK_SEQPACKET);
183    }
184
185    return sock;
186}
187