blob: 81eb091a784461890be60b20fd61c4791a932ae3 [file] [log] [blame]
Mark Salyzyn0175b072014-02-26 09:50:16 -08001/*
2 * Copyright (C) 2012-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 <stdio.h>
18#include <string.h>
19#include <time.h>
20#include <unistd.h>
21
22#include <log/logger.h>
23
24#include "LogBuffer.h"
Mark Salyzyn34facab2014-02-06 14:48:50 -080025#include "LogStatistics.h"
Mark Salyzyndfa7a072014-02-11 12:29:31 -080026#include "LogWhiteBlackList.h"
Mark Salyzyn0175b072014-02-26 09:50:16 -080027#include "LogReader.h"
28
Mark Salyzyndfa7a072014-02-11 12:29:31 -080029// Default
Mark Salyzyn0175b072014-02-26 09:50:16 -080030#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
Mark Salyzyndfa7a072014-02-11 12:29:31 -080031#ifdef USERDEBUG_BUILD
32#define log_buffer_size(id) mMaxSize[id]
33#else
34#define log_buffer_size(id) LOG_BUFFER_SIZE
35#endif
Mark Salyzyn0175b072014-02-26 09:50:16 -080036
37LogBuffer::LogBuffer(LastLogTimes *times)
38 : mTimes(*times) {
Mark Salyzyn0175b072014-02-26 09:50:16 -080039 pthread_mutex_init(&mLogElementsLock, NULL);
Mark Salyzyne457b742014-02-19 17:18:31 -080040 dgram_qlen_statistics = false;
41
Mark Salyzynca4c63d2014-04-01 16:57:29 -070042#ifdef USERDEBUG_BUILD
Mark Salyzyndfa7a072014-02-11 12:29:31 -080043 log_id_for_each(i) {
44 mMaxSize[i] = LOG_BUFFER_SIZE;
45 }
Mark Salyzynca4c63d2014-04-01 16:57:29 -070046#endif
Mark Salyzyn0175b072014-02-26 09:50:16 -080047}
48
Mark Salyzyn7e2f83c2014-03-05 07:41:49 -080049void LogBuffer::log(log_id_t log_id, log_time realtime,
Mark Salyzynb992d0d2014-03-20 16:09:38 -070050 uid_t uid, pid_t pid, pid_t tid,
51 const char *msg, unsigned short len) {
Mark Salyzyn0175b072014-02-26 09:50:16 -080052 if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
53 return;
54 }
55 LogBufferElement *elem = new LogBufferElement(log_id, realtime,
Mark Salyzynb992d0d2014-03-20 16:09:38 -070056 uid, pid, tid, msg, len);
Mark Salyzyn0175b072014-02-26 09:50:16 -080057
58 pthread_mutex_lock(&mLogElementsLock);
59
60 // Insert elements in time sorted order if possible
61 // NB: if end is region locked, place element at end of list
62 LogBufferElementCollection::iterator it = mLogElements.end();
63 LogBufferElementCollection::iterator last = it;
64 while (--it != mLogElements.begin()) {
Mark Salyzync03e72c2014-02-18 11:23:53 -080065 if ((*it)->getRealTime() <= realtime) {
Mark Salyzyne457b742014-02-19 17:18:31 -080066 // halves the peak performance, use with caution
67 if (dgram_qlen_statistics) {
68 LogBufferElementCollection::iterator ib = it;
69 unsigned short buckets, num = 1;
70 for (unsigned short i = 0; (buckets = stats.dgram_qlen(i)); ++i) {
71 buckets -= num;
72 num += buckets;
73 while (buckets && (--ib != mLogElements.begin())) {
74 --buckets;
75 }
76 if (buckets) {
77 break;
78 }
79 stats.recordDiff(
80 elem->getRealTime() - (*ib)->getRealTime(), i);
81 }
82 }
Mark Salyzyn0175b072014-02-26 09:50:16 -080083 break;
84 }
85 last = it;
86 }
Mark Salyzync03e72c2014-02-18 11:23:53 -080087
Mark Salyzyn0175b072014-02-26 09:50:16 -080088 if (last == mLogElements.end()) {
89 mLogElements.push_back(elem);
90 } else {
91 log_time end;
92 bool end_set = false;
93 bool end_always = false;
94
95 LogTimeEntry::lock();
96
97 LastLogTimes::iterator t = mTimes.begin();
98 while(t != mTimes.end()) {
99 LogTimeEntry *entry = (*t);
100 if (entry->owned_Locked()) {
101 if (!entry->mNonBlock) {
102 end_always = true;
103 break;
104 }
105 if (!end_set || (end <= entry->mEnd)) {
106 end = entry->mEnd;
107 end_set = true;
108 }
109 }
110 t++;
111 }
112
113 if (end_always
Mark Salyzync03e72c2014-02-18 11:23:53 -0800114 || (end_set && (end >= (*last)->getMonotonicTime()))) {
Mark Salyzyn0175b072014-02-26 09:50:16 -0800115 mLogElements.push_back(elem);
116 } else {
117 mLogElements.insert(last,elem);
118 }
119
120 LogTimeEntry::unlock();
121 }
122
Mark Salyzyn34facab2014-02-06 14:48:50 -0800123 stats.add(len, log_id, uid, pid);
Mark Salyzyn0175b072014-02-26 09:50:16 -0800124 maybePrune(log_id);
125 pthread_mutex_unlock(&mLogElementsLock);
126}
127
128// If we're using more than 256K of memory for log entries, prune
Mark Salyzyn740f9b42014-01-13 16:37:51 -0800129// at least 10% of the log entries.
Mark Salyzyn0175b072014-02-26 09:50:16 -0800130//
131// mLogElementsLock must be held when this function is called.
132void LogBuffer::maybePrune(log_id_t id) {
Mark Salyzyn34facab2014-02-06 14:48:50 -0800133 size_t sizes = stats.sizes(id);
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800134 if (sizes > log_buffer_size(id)) {
135 size_t sizeOver90Percent = sizes - ((log_buffer_size(id) * 9) / 10);
Mark Salyzyn34facab2014-02-06 14:48:50 -0800136 size_t elements = stats.elements(id);
Mark Salyzyn740f9b42014-01-13 16:37:51 -0800137 unsigned long pruneRows = elements * sizeOver90Percent / sizes;
138 elements /= 10;
139 if (pruneRows <= elements) {
140 pruneRows = elements;
141 }
142 prune(id, pruneRows);
Mark Salyzyn0175b072014-02-26 09:50:16 -0800143 }
144}
145
146// prune "pruneRows" of type "id" from the buffer.
147//
148// mLogElementsLock must be held when this function is called.
149void LogBuffer::prune(log_id_t id, unsigned long pruneRows) {
150 LogTimeEntry *oldest = NULL;
151
152 LogTimeEntry::lock();
153
154 // Region locked?
155 LastLogTimes::iterator t = mTimes.begin();
156 while(t != mTimes.end()) {
157 LogTimeEntry *entry = (*t);
158 if (entry->owned_Locked()
159 && (!oldest || (oldest->mStart > entry->mStart))) {
160 oldest = entry;
161 }
162 t++;
163 }
164
Mark Salyzyn64d6fe92014-02-06 18:11:13 -0800165 LogBufferElementCollection::iterator it;
166
167 // prune by worst offender by uid
168 while (pruneRows > 0) {
169 // recalculate the worst offender on every batched pass
170 uid_t worst = (uid_t) -1;
171 size_t worst_sizes = 0;
172 size_t second_worst_sizes = 0;
173
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800174#ifdef USERDEBUG_BUILD
175 if (mPrune.worstUidEnabled())
176#endif
177 {
178 LidStatistics &l = stats.id(id);
179 UidStatisticsCollection::iterator iu;
180 for (iu = l.begin(); iu != l.end(); ++iu) {
181 UidStatistics *u = (*iu);
182 size_t sizes = u->sizes();
183 if (worst_sizes < sizes) {
184 second_worst_sizes = worst_sizes;
185 worst_sizes = sizes;
186 worst = u->getUid();
187 }
188 if ((second_worst_sizes < sizes) && (sizes < worst_sizes)) {
189 second_worst_sizes = sizes;
190 }
Mark Salyzyn64d6fe92014-02-06 18:11:13 -0800191 }
192 }
193
194 bool kick = false;
195 for(it = mLogElements.begin(); it != mLogElements.end();) {
196 LogBufferElement *e = *it;
197
198 if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
199 break;
200 }
201
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800202 if (e->getLogId() != id) {
203 ++it;
204 continue;
205 }
206
207 uid_t uid = e->getUid();
208
209 if (uid == worst) {
Mark Salyzyn64d6fe92014-02-06 18:11:13 -0800210 it = mLogElements.erase(it);
211 unsigned short len = e->getMsgLen();
212 stats.subtract(len, id, worst, e->getPid());
213 delete e;
214 kick = true;
215 pruneRows--;
216 if ((pruneRows == 0) || (worst_sizes < second_worst_sizes)) {
217 break;
218 }
219 worst_sizes -= len;
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800220 }
221#ifdef USERDEBUG_BUILD
222 else if (mPrune.naughty(e)) { // BlackListed
223 it = mLogElements.erase(it);
224 stats.subtract(e->getMsgLen(), id, uid, e->getPid());
225 delete e;
226 pruneRows--;
227 if (pruneRows == 0) {
228 break;
229 }
230 }
231#endif
232 else {
Mark Salyzyn64d6fe92014-02-06 18:11:13 -0800233 ++it;
234 }
235 }
236
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800237 if (!kick
238#ifdef USERDEBUG_BUILD
239 || !mPrune.worstUidEnabled()
240#endif
241 ) {
Mark Salyzyn64d6fe92014-02-06 18:11:13 -0800242 break; // the following loop will ask bad clients to skip/drop
243 }
244 }
245
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800246#ifdef USERDEBUG_BUILD
247 bool whitelist = false;
248#endif
Mark Salyzyn64d6fe92014-02-06 18:11:13 -0800249 it = mLogElements.begin();
Mark Salyzyn0175b072014-02-26 09:50:16 -0800250 while((pruneRows > 0) && (it != mLogElements.end())) {
251 LogBufferElement *e = *it;
252 if (e->getLogId() == id) {
253 if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800254#ifdef USERDEBUG_BUILD
255 if (!whitelist)
256#endif
257 {
258 if (stats.sizes(id) > (2 * log_buffer_size(id))) {
259 // kick a misbehaving log reader client off the island
260 oldest->release_Locked();
261 } else {
262 oldest->triggerSkip_Locked(pruneRows);
263 }
Mark Salyzyn0175b072014-02-26 09:50:16 -0800264 }
265 break;
266 }
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800267#ifdef USERDEBUG_BUILD
268 if (mPrune.nice(e)) { // WhiteListed
269 whitelist = true;
270 it++;
271 continue;
272 }
273#endif
Mark Salyzyn0175b072014-02-26 09:50:16 -0800274 it = mLogElements.erase(it);
Mark Salyzyn34facab2014-02-06 14:48:50 -0800275 stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
Mark Salyzyn0175b072014-02-26 09:50:16 -0800276 delete e;
277 pruneRows--;
278 } else {
279 it++;
280 }
281 }
282
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800283#ifdef USERDEBUG_BUILD
284 if (whitelist && (pruneRows > 0)) {
285 it = mLogElements.begin();
286 while((it != mLogElements.end()) && (pruneRows > 0)) {
287 LogBufferElement *e = *it;
288 if (e->getLogId() == id) {
289 if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
290 if (stats.sizes(id) > (2 * log_buffer_size(id))) {
291 // kick a misbehaving log reader client off the island
292 oldest->release_Locked();
293 } else {
294 oldest->triggerSkip_Locked(pruneRows);
295 }
296 break;
297 }
298 it = mLogElements.erase(it);
299 stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
300 delete e;
301 pruneRows--;
302 } else {
303 it++;
304 }
305 }
306 }
307#endif
308
Mark Salyzyn0175b072014-02-26 09:50:16 -0800309 LogTimeEntry::unlock();
310}
311
312// clear all rows of type "id" from the buffer.
313void LogBuffer::clear(log_id_t id) {
314 pthread_mutex_lock(&mLogElementsLock);
315 prune(id, ULONG_MAX);
316 pthread_mutex_unlock(&mLogElementsLock);
317}
318
319// get the used space associated with "id".
320unsigned long LogBuffer::getSizeUsed(log_id_t id) {
321 pthread_mutex_lock(&mLogElementsLock);
Mark Salyzyn34facab2014-02-06 14:48:50 -0800322 size_t retval = stats.sizes(id);
Mark Salyzyn0175b072014-02-26 09:50:16 -0800323 pthread_mutex_unlock(&mLogElementsLock);
324 return retval;
325}
326
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800327#ifdef USERDEBUG_BUILD
328
329// set the total space allocated to "id"
330int LogBuffer::setSize(log_id_t id, unsigned long size) {
331 // Reasonable limits ...
332 if ((size < (64 * 1024)) || ((256 * 1024 * 1024) < size)) {
333 return -1;
334 }
335 pthread_mutex_lock(&mLogElementsLock);
336 log_buffer_size(id) = size;
337 pthread_mutex_unlock(&mLogElementsLock);
338 return 0;
339}
340
341// get the total space allocated to "id"
342unsigned long LogBuffer::getSize(log_id_t id) {
343 pthread_mutex_lock(&mLogElementsLock);
344 size_t retval = log_buffer_size(id);
345 pthread_mutex_unlock(&mLogElementsLock);
346 return retval;
347}
348
349#else // ! USERDEBUG_BUILD
350
Mark Salyzyn0175b072014-02-26 09:50:16 -0800351// get the total space allocated to "id"
352unsigned long LogBuffer::getSize(log_id_t /*id*/) {
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800353 return log_buffer_size(id);
Mark Salyzyn0175b072014-02-26 09:50:16 -0800354}
355
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800356#endif
357
Mark Salyzyn7e2f83c2014-03-05 07:41:49 -0800358log_time LogBuffer::flushTo(
359 SocketClient *reader, const log_time start, bool privileged,
Mark Salyzyn0175b072014-02-26 09:50:16 -0800360 bool (*filter)(const LogBufferElement *element, void *arg), void *arg) {
361 LogBufferElementCollection::iterator it;
362 log_time max = start;
363 uid_t uid = reader->getUid();
364
365 pthread_mutex_lock(&mLogElementsLock);
366 for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
367 LogBufferElement *element = *it;
368
369 if (!privileged && (element->getUid() != uid)) {
370 continue;
371 }
372
373 if (element->getMonotonicTime() <= start) {
374 continue;
375 }
376
377 // NB: calling out to another object with mLogElementsLock held (safe)
378 if (filter && !(*filter)(element, arg)) {
379 continue;
380 }
381
382 pthread_mutex_unlock(&mLogElementsLock);
383
384 // range locking in LastLogTimes looks after us
385 max = element->flushTo(reader);
386
387 if (max == element->FLUSH_ERROR) {
388 return max;
389 }
390
391 pthread_mutex_lock(&mLogElementsLock);
392 }
393 pthread_mutex_unlock(&mLogElementsLock);
394
395 return max;
396}
Mark Salyzyn34facab2014-02-06 14:48:50 -0800397
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800398void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
Mark Salyzyn34facab2014-02-06 14:48:50 -0800399 log_time oldest(CLOCK_MONOTONIC);
400
401 pthread_mutex_lock(&mLogElementsLock);
402
403 // Find oldest element in the log(s)
404 LogBufferElementCollection::iterator it;
405 for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
406 LogBufferElement *element = *it;
407
408 if ((logMask & (1 << element->getLogId()))) {
409 oldest = element->getMonotonicTime();
410 break;
411 }
412 }
413
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800414 stats.format(strp, uid, logMask, oldest);
Mark Salyzyn34facab2014-02-06 14:48:50 -0800415
416 pthread_mutex_unlock(&mLogElementsLock);
Mark Salyzyn34facab2014-02-06 14:48:50 -0800417}