1 /*
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 // for manual checking of stale entries during LogBuffer::erase()
17 //#define DEBUG_CHECK_FOR_STALE_ENTRIES
18 
19 #include <ctype.h>
20 #include <endian.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/cdefs.h>
25 #include <sys/user.h>
26 #include <time.h>
27 #include <unistd.h>
28 
29 #include <unordered_map>
30 
31 #include <cutils/properties.h>
32 #include <private/android_logger.h>
33 
34 #include "LogBuffer.h"
35 #include "LogKlog.h"
36 #include "LogReader.h"
37 #include "LogUtils.h"
38 
39 #ifndef __predict_false
40 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
41 #endif
42 
43 // Default
44 #define log_buffer_size(id) mMaxSize[id]
45 
46 const log_time LogBuffer::pruneMargin(3, 0);
47 
init()48 void LogBuffer::init() {
49     log_id_for_each(i) {
50         mLastSet[i] = false;
51         mLast[i] = mLogElements.begin();
52 
53         if (setSize(i, __android_logger_get_buffer_size(i))) {
54             setSize(i, LOG_BUFFER_MIN_SIZE);
55         }
56     }
57     bool lastMonotonic = monotonic;
58     monotonic = android_log_clockid() == CLOCK_MONOTONIC;
59     if (lastMonotonic != monotonic) {
60         //
61         // Fixup all timestamps, may not be 100% accurate, but better than
62         // throwing what we have away when we get 'surprised' by a change.
63         // In-place element fixup so no need to check reader-lock. Entries
64         // should already be in timestamp order, but we could end up with a
65         // few out-of-order entries if new monotonics come in before we
66         // are notified of the reinit change in status. A Typical example would
67         // be:
68         //  --------- beginning of system
69         //      10.494082   184   201 D Cryptfs : Just triggered post_fs_data
70         //  --------- beginning of kernel
71         //       0.000000     0     0 I         : Initializing cgroup subsys
72         // as the act of mounting /data would trigger persist.logd.timestamp to
73         // be corrected. 1/30 corner case YMMV.
74         //
75         rdlock();
76         LogBufferElementCollection::iterator it = mLogElements.begin();
77         while ((it != mLogElements.end())) {
78             LogBufferElement* e = *it;
79             if (monotonic) {
80                 if (!android::isMonotonic(e->mRealTime)) {
81                     LogKlog::convertRealToMonotonic(e->mRealTime);
82                     if ((e->mRealTime.tv_nsec % 1000) == 0) {
83                         e->mRealTime.tv_nsec++;
84                     }
85                 }
86             } else {
87                 if (android::isMonotonic(e->mRealTime)) {
88                     LogKlog::convertMonotonicToReal(e->mRealTime);
89                     if ((e->mRealTime.tv_nsec % 1000) == 0) {
90                         e->mRealTime.tv_nsec++;
91                     }
92                 }
93             }
94             ++it;
95         }
96         unlock();
97     }
98 
99     // We may have been triggered by a SIGHUP. Release any sleeping reader
100     // threads to dump their current content.
101     //
102     // NB: this is _not_ performed in the context of a SIGHUP, it is
103     // performed during startup, and in context of reinit administrative thread
104     LogTimeEntry::wrlock();
105 
106     LastLogTimes::iterator times = mTimes.begin();
107     while (times != mTimes.end()) {
108         LogTimeEntry* entry = (*times);
109         if (entry->owned_Locked()) {
110             entry->triggerReader_Locked();
111         }
112         times++;
113     }
114 
115     LogTimeEntry::unlock();
116 }
117 
LogBuffer(LastLogTimes * times)118 LogBuffer::LogBuffer(LastLogTimes* times)
119     : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
120     pthread_rwlock_init(&mLogElementsLock, nullptr);
121 
122     log_id_for_each(i) {
123         lastLoggedElements[i] = nullptr;
124         droppedElements[i] = nullptr;
125     }
126 
127     init();
128 }
129 
~LogBuffer()130 LogBuffer::~LogBuffer() {
131     log_id_for_each(i) {
132         delete lastLoggedElements[i];
133         delete droppedElements[i];
134     }
135 }
136 
137 enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
138 
identical(LogBufferElement * elem,LogBufferElement * last)139 static enum match_type identical(LogBufferElement* elem,
140                                  LogBufferElement* last) {
141     // is it mostly identical?
142     //  if (!elem) return DIFFERENT;
143     ssize_t lenl = elem->getMsgLen();
144     if (lenl <= 0) return DIFFERENT;  // value if this represents a chatty elem
145     //  if (!last) return DIFFERENT;
146     ssize_t lenr = last->getMsgLen();
147     if (lenr <= 0) return DIFFERENT;  // value if this represents a chatty elem
148     //  if (elem->getLogId() != last->getLogId()) return DIFFERENT;
149     if (elem->getUid() != last->getUid()) return DIFFERENT;
150     if (elem->getPid() != last->getPid()) return DIFFERENT;
151     if (elem->getTid() != last->getTid()) return DIFFERENT;
152 
153     // last is more than a minute old, stop squashing identical messages
154     if (elem->getRealTime().nsec() >
155         (last->getRealTime().nsec() + 60 * NS_PER_SEC))
156         return DIFFERENT;
157 
158     // Identical message
159     const char* msgl = elem->getMsg();
160     const char* msgr = last->getMsg();
161     if (lenl == lenr) {
162         if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
163         // liblog tagged messages (content gets summed)
164         if ((elem->getLogId() == LOG_ID_EVENTS) &&
165             (lenl == sizeof(android_log_event_int_t)) &&
166             !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) -
167                                              sizeof(int32_t)) &&
168             (elem->getTag() == LIBLOG_LOG_TAG)) {
169             return SAME_LIBLOG;
170         }
171     }
172 
173     // audit message (except sequence number) identical?
174     if (last->isBinary() &&
175         (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
176         (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
177         if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
178                                             sizeof(int32_t))) {
179             return DIFFERENT;
180         }
181         msgl += sizeof(android_log_event_string_t);
182         lenl -= sizeof(android_log_event_string_t);
183         msgr += sizeof(android_log_event_string_t);
184         lenr -= sizeof(android_log_event_string_t);
185     }
186     static const char avc[] = "): avc: ";
187     const char* avcl = android::strnstr(msgl, lenl, avc);
188     if (!avcl) return DIFFERENT;
189     lenl -= avcl - msgl;
190     const char* avcr = android::strnstr(msgr, lenr, avc);
191     if (!avcr) return DIFFERENT;
192     lenr -= avcr - msgr;
193     if (lenl != lenr) return DIFFERENT;
194     if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
195                         lenl - strlen(avc))) {
196         return DIFFERENT;
197     }
198     return SAME;
199 }
200 
log(log_id_t log_id,log_time realtime,uid_t uid,pid_t pid,pid_t tid,const char * msg,unsigned short len)201 int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
202                    pid_t tid, const char* msg, unsigned short len) {
203     if (log_id >= LOG_ID_MAX) {
204         return -EINVAL;
205     }
206 
207     // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
208     // This prevents any chance that an outside source can request an
209     // exact entry with time specified in ms or us precision.
210     if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
211 
212     LogBufferElement* elem =
213         new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
214     if (log_id != LOG_ID_SECURITY) {
215         int prio = ANDROID_LOG_INFO;
216         const char* tag = nullptr;
217         size_t tag_len = 0;
218         if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
219             tag = tagToName(elem->getTag());
220             if (tag) {
221                 tag_len = strlen(tag);
222             }
223         } else {
224             prio = *msg;
225             tag = msg + 1;
226             tag_len = strnlen(tag, len - 1);
227         }
228         if (!__android_log_is_loggable_len(prio, tag, tag_len,
229                                            ANDROID_LOG_VERBOSE)) {
230             // Log traffic received to total
231             wrlock();
232             stats.addTotal(elem);
233             unlock();
234             delete elem;
235             return -EACCES;
236         }
237     }
238 
239     wrlock();
240     LogBufferElement* currentLast = lastLoggedElements[log_id];
241     if (currentLast) {
242         LogBufferElement* dropped = droppedElements[log_id];
243         unsigned short count = dropped ? dropped->getDropped() : 0;
244         //
245         // State Init
246         //     incoming:
247         //         dropped = nullptr
248         //         currentLast = nullptr;
249         //         elem = incoming message
250         //     outgoing:
251         //         dropped = nullptr -> State 0
252         //         currentLast = copy of elem
253         //         log elem
254         // State 0
255         //     incoming:
256         //         count = 0
257         //         dropped = nullptr
258         //         currentLast = copy of last message
259         //         elem = incoming message
260         //     outgoing: if match != DIFFERENT
261         //         dropped = copy of first identical message -> State 1
262         //         currentLast = reference to elem
263         //     break: if match == DIFFERENT
264         //         dropped = nullptr -> State 0
265         //         delete copy of last message (incoming currentLast)
266         //         currentLast = copy of elem
267         //         log elem
268         // State 1
269         //     incoming:
270         //         count = 0
271         //         dropped = copy of first identical message
272         //         currentLast = reference to last held-back incoming
273         //                       message
274         //         elem = incoming message
275         //     outgoing: if match == SAME
276         //         delete copy of first identical message (dropped)
277         //         dropped = reference to last held-back incoming
278         //                   message set to chatty count of 1 -> State 2
279         //         currentLast = reference to elem
280         //     outgoing: if match == SAME_LIBLOG
281         //         dropped = copy of first identical message -> State 1
282         //         take sum of currentLast and elem
283         //         if sum overflows:
284         //             log currentLast
285         //             currentLast = reference to elem
286         //         else
287         //             delete currentLast
288         //             currentLast = reference to elem, sum liblog.
289         //     break: if match == DIFFERENT
290         //         delete dropped
291         //         dropped = nullptr -> State 0
292         //         log reference to last held-back (currentLast)
293         //         currentLast = copy of elem
294         //         log elem
295         // State 2
296         //     incoming:
297         //         count = chatty count
298         //         dropped = chatty message holding count
299         //         currentLast = reference to last held-back incoming
300         //                       message.
301         //         dropped = chatty message holding count
302         //         elem = incoming message
303         //     outgoing: if match != DIFFERENT
304         //         delete chatty message holding count
305         //         dropped = reference to last held-back incoming
306         //                   message, set to chatty count + 1
307         //         currentLast = reference to elem
308         //     break: if match == DIFFERENT
309         //         log dropped (chatty message)
310         //         dropped = nullptr -> State 0
311         //         log reference to last held-back (currentLast)
312         //         currentLast = copy of elem
313         //         log elem
314         //
315         enum match_type match = identical(elem, currentLast);
316         if (match != DIFFERENT) {
317             if (dropped) {
318                 // Sum up liblog tag messages?
319                 if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
320                     android_log_event_int_t* event =
321                         reinterpret_cast<android_log_event_int_t*>(
322                             const_cast<char*>(currentLast->getMsg()));
323                     //
324                     // To unit test, differentiate with something like:
325                     //    event->header.tag = htole32(CHATTY_LOG_TAG);
326                     // here, then instead of delete currentLast below,
327                     // log(currentLast) to see the incremental sums form.
328                     //
329                     uint32_t swab = event->payload.data;
330                     unsigned long long total = htole32(swab);
331                     event = reinterpret_cast<android_log_event_int_t*>(
332                         const_cast<char*>(elem->getMsg()));
333                     swab = event->payload.data;
334 
335                     lastLoggedElements[LOG_ID_EVENTS] = elem;
336                     total += htole32(swab);
337                     // check for overflow
338                     if (total >= UINT32_MAX) {
339                         log(currentLast);
340                         unlock();
341                         return len;
342                     }
343                     stats.addTotal(currentLast);
344                     delete currentLast;
345                     swab = total;
346                     event->payload.data = htole32(swab);
347                     unlock();
348                     return len;
349                 }
350                 if (count == USHRT_MAX) {
351                     log(dropped);
352                     count = 1;
353                 } else {
354                     delete dropped;
355                     ++count;
356                 }
357             }
358             if (count) {
359                 stats.addTotal(currentLast);
360                 currentLast->setDropped(count);
361             }
362             droppedElements[log_id] = currentLast;
363             lastLoggedElements[log_id] = elem;
364             unlock();
365             return len;
366         }
367         if (dropped) {         // State 1 or 2
368             if (count) {       // State 2
369                 log(dropped);  // report chatty
370             } else {           // State 1
371                 delete dropped;
372             }
373             droppedElements[log_id] = nullptr;
374             log(currentLast);  // report last message in the series
375         } else {               // State 0
376             delete currentLast;
377         }
378     }
379     lastLoggedElements[log_id] = new LogBufferElement(*elem);
380 
381     log(elem);
382     unlock();
383 
384     return len;
385 }
386 
387 // assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
log(LogBufferElement * elem)388 void LogBuffer::log(LogBufferElement* elem) {
389     // cap on how far back we will sort in-place, otherwise append
390     static uint32_t too_far_back = 5;  // five seconds
391     // Insert elements in time sorted order if possible
392     //  NB: if end is region locked, place element at end of list
393     LogBufferElementCollection::iterator it = mLogElements.end();
394     LogBufferElementCollection::iterator last = it;
395     if (__predict_true(it != mLogElements.begin())) --it;
396     if (__predict_false(it == mLogElements.begin()) ||
397         __predict_true((*it)->getRealTime() <= elem->getRealTime()) ||
398         __predict_false((((*it)->getRealTime().tv_sec - too_far_back) >
399                          elem->getRealTime().tv_sec) &&
400                         (elem->getLogId() != LOG_ID_KERNEL) &&
401                         ((*it)->getLogId() != LOG_ID_KERNEL))) {
402         mLogElements.push_back(elem);
403     } else {
404         log_time end = log_time::EPOCH;
405         bool end_set = false;
406         bool end_always = false;
407 
408         LogTimeEntry::rdlock();
409 
410         LastLogTimes::iterator times = mTimes.begin();
411         while (times != mTimes.end()) {
412             LogTimeEntry* entry = (*times);
413             if (entry->owned_Locked()) {
414                 if (!entry->mNonBlock) {
415                     end_always = true;
416                     break;
417                 }
418                 // it passing mEnd is blocked by the following checks.
419                 if (!end_set || (end <= entry->mEnd)) {
420                     end = entry->mEnd;
421                     end_set = true;
422                 }
423             }
424             times++;
425         }
426 
427         if (end_always || (end_set && (end > (*it)->getRealTime()))) {
428             mLogElements.push_back(elem);
429         } else {
430             // should be short as timestamps are localized near end()
431             do {
432                 last = it;
433                 if (__predict_false(it == mLogElements.begin())) {
434                     break;
435                 }
436                 --it;
437             } while (((*it)->getRealTime() > elem->getRealTime()) &&
438                      (!end_set || (end <= (*it)->getRealTime())));
439             mLogElements.insert(last, elem);
440         }
441         LogTimeEntry::unlock();
442     }
443 
444     stats.add(elem);
445     maybePrune(elem->getLogId());
446 }
447 
448 // Prune at most 10% of the log entries or maxPrune, whichever is less.
449 //
450 // LogBuffer::wrlock() must be held when this function is called.
maybePrune(log_id_t id)451 void LogBuffer::maybePrune(log_id_t id) {
452     size_t sizes = stats.sizes(id);
453     unsigned long maxSize = log_buffer_size(id);
454     if (sizes > maxSize) {
455         size_t sizeOver = sizes - ((maxSize * 9) / 10);
456         size_t elements = stats.realElements(id);
457         size_t minElements = elements / 100;
458         if (minElements < minPrune) {
459             minElements = minPrune;
460         }
461         unsigned long pruneRows = elements * sizeOver / sizes;
462         if (pruneRows < minElements) {
463             pruneRows = minElements;
464         }
465         if (pruneRows > maxPrune) {
466             pruneRows = maxPrune;
467         }
468         prune(id, pruneRows);
469     }
470 }
471 
erase(LogBufferElementCollection::iterator it,bool coalesce)472 LogBufferElementCollection::iterator LogBuffer::erase(
473     LogBufferElementCollection::iterator it, bool coalesce) {
474     LogBufferElement* element = *it;
475     log_id_t id = element->getLogId();
476 
477     // Remove iterator references in the various lists that will become stale
478     // after the element is erased from the main logging list.
479 
480     {  // start of scope for found iterator
481         int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
482                       ? element->getTag()
483                       : element->getUid();
484         LogBufferIteratorMap::iterator found = mLastWorst[id].find(key);
485         if ((found != mLastWorst[id].end()) && (it == found->second)) {
486             mLastWorst[id].erase(found);
487         }
488     }
489 
490     {  // start of scope for pid found iterator
491         // element->getUid() may not be AID_SYSTEM for next-best-watermark.
492         // will not assume id != LOG_ID_EVENTS or LOG_ID_SECURITY for KISS and
493         // long term code stability, find() check should be fast for those ids.
494         LogBufferPidIteratorMap::iterator found =
495             mLastWorstPidOfSystem[id].find(element->getPid());
496         if ((found != mLastWorstPidOfSystem[id].end()) &&
497             (it == found->second)) {
498             mLastWorstPidOfSystem[id].erase(found);
499         }
500     }
501 
502     bool setLast[LOG_ID_MAX];
503     bool doSetLast = false;
504     log_id_for_each(i) {
505         doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
506     }
507 #ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
508     LogBufferElementCollection::iterator bad = it;
509     int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
510                   ? element->getTag()
511                   : element->getUid();
512 #endif
513     it = mLogElements.erase(it);
514     if (doSetLast) {
515         log_id_for_each(i) {
516             if (setLast[i]) {
517                 if (__predict_false(it == mLogElements.end())) {  // impossible
518                     mLastSet[i] = false;
519                     mLast[i] = mLogElements.begin();
520                 } else {
521                     mLast[i] = it;  // push down the road as next-best-watermark
522                 }
523             }
524         }
525     }
526 #ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
527     log_id_for_each(i) {
528         for (auto b : mLastWorst[i]) {
529             if (bad == b.second) {
530                 android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n", i,
531                                  b.first, key);
532             }
533         }
534         for (auto b : mLastWorstPidOfSystem[i]) {
535             if (bad == b.second) {
536                 android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n", i,
537                                  b.first);
538             }
539         }
540         if (mLastSet[i] && (bad == mLast[i])) {
541             android::prdebug("stale mLast[%d]\n", i);
542             mLastSet[i] = false;
543             mLast[i] = mLogElements.begin();
544         }
545     }
546 #endif
547     if (coalesce) {
548         stats.erase(element);
549     } else {
550         stats.subtract(element);
551     }
552     delete element;
553 
554     return it;
555 }
556 
557 // Define a temporary mechanism to report the last LogBufferElement pointer
558 // for the specified uid, pid and tid. Used below to help merge-sort when
559 // pruning for worst UID.
560 class LogBufferElementKey {
561     const union {
562         struct {
563             uint32_t uid;
564             uint16_t pid;
565             uint16_t tid;
566         } __packed;
567         uint64_t value;
568     } __packed;
569 
570    public:
LogBufferElementKey(uid_t uid,pid_t pid,pid_t tid)571     LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid)
572         : uid(uid), pid(pid), tid(tid) {
573     }
LogBufferElementKey(uint64_t key)574     explicit LogBufferElementKey(uint64_t key) : value(key) {
575     }
576 
getKey()577     uint64_t getKey() {
578         return value;
579     }
580 };
581 
582 class LogBufferElementLast {
583     typedef std::unordered_map<uint64_t, LogBufferElement*> LogBufferElementMap;
584     LogBufferElementMap map;
585 
586    public:
coalesce(LogBufferElement * element,unsigned short dropped)587     bool coalesce(LogBufferElement* element, unsigned short dropped) {
588         LogBufferElementKey key(element->getUid(), element->getPid(),
589                                 element->getTid());
590         LogBufferElementMap::iterator it = map.find(key.getKey());
591         if (it != map.end()) {
592             LogBufferElement* found = it->second;
593             unsigned short moreDropped = found->getDropped();
594             if ((dropped + moreDropped) > USHRT_MAX) {
595                 map.erase(it);
596             } else {
597                 found->setDropped(dropped + moreDropped);
598                 return true;
599             }
600         }
601         return false;
602     }
603 
add(LogBufferElement * element)604     void add(LogBufferElement* element) {
605         LogBufferElementKey key(element->getUid(), element->getPid(),
606                                 element->getTid());
607         map[key.getKey()] = element;
608     }
609 
clear()610     inline void clear() {
611         map.clear();
612     }
613 
clear(LogBufferElement * element)614     void clear(LogBufferElement* element) {
615         log_time current =
616             element->getRealTime() - log_time(EXPIRE_RATELIMIT, 0);
617         for (LogBufferElementMap::iterator it = map.begin(); it != map.end();) {
618             LogBufferElement* mapElement = it->second;
619             if ((mapElement->getDropped() >= EXPIRE_THRESHOLD) &&
620                 (current > mapElement->getRealTime())) {
621                 it = map.erase(it);
622             } else {
623                 ++it;
624             }
625         }
626     }
627 };
628 
629 // Determine if watermark is within pruneMargin + 1s from the end of the list,
630 // the caller will use this result to set an internal busy flag indicating
631 // the prune operation could not be completed because a reader is blocking
632 // the request.
isBusy(log_time watermark)633 bool LogBuffer::isBusy(log_time watermark) {
634     LogBufferElementCollection::iterator ei = mLogElements.end();
635     --ei;
636     return watermark < ((*ei)->getRealTime() - pruneMargin - log_time(1, 0));
637 }
638 
639 // If the selected reader is blocking our pruning progress, decide on
640 // what kind of mitigation is necessary to unblock the situation.
kickMe(LogTimeEntry * me,log_id_t id,unsigned long pruneRows)641 void LogBuffer::kickMe(LogTimeEntry* me, log_id_t id, unsigned long pruneRows) {
642     if (stats.sizes(id) > (2 * log_buffer_size(id))) {  // +100%
643         // A misbehaving or slow reader has its connection
644         // dropped if we hit too much memory pressure.
645         me->release_Locked();
646     } else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
647         // Allow a blocked WRAP timeout reader to
648         // trigger and start reporting the log data.
649         me->triggerReader_Locked();
650     } else {
651         // tell slow reader to skip entries to catch up
652         me->triggerSkip_Locked(id, pruneRows);
653     }
654 }
655 
656 // prune "pruneRows" of type "id" from the buffer.
657 //
658 // This garbage collection task is used to expire log entries. It is called to
659 // remove all logs (clear), all UID logs (unprivileged clear), or every
660 // 256 or 10% of the total logs (whichever is less) to prune the logs.
661 //
662 // First there is a prep phase where we discover the reader region lock that
663 // acts as a backstop to any pruning activity to stop there and go no further.
664 //
665 // There are three major pruning loops that follow. All expire from the oldest
666 // entries. Since there are multiple log buffers, the Android logging facility
667 // will appear to drop entries 'in the middle' when looking at multiple log
668 // sources and buffers. This effect is slightly more prominent when we prune
669 // the worst offender by logging source. Thus the logs slowly loose content
670 // and value as you move back in time. This is preferred since chatty sources
671 // invariably move the logs value down faster as less chatty sources would be
672 // expired in the noise.
673 //
674 // The first loop performs blacklisting and worst offender pruning. Falling
675 // through when there are no notable worst offenders and have not hit the
676 // region lock preventing further worst offender pruning. This loop also looks
677 // after managing the chatty log entries and merging to help provide
678 // statistical basis for blame. The chatty entries are not a notification of
679 // how much logs you may have, but instead represent how much logs you would
680 // have had in a virtual log buffer that is extended to cover all the in-memory
681 // logs without loss. They last much longer than the represented pruned logs
682 // since they get multiplied by the gains in the non-chatty log sources.
683 //
684 // The second loop get complicated because an algorithm of watermarks and
685 // history is maintained to reduce the order and keep processing time
686 // down to a minimum at scale. These algorithms can be costly in the face
687 // of larger log buffers, or severly limited processing time granted to a
688 // background task at lowest priority.
689 //
690 // This second loop does straight-up expiration from the end of the logs
691 // (again, remember for the specified log buffer id) but does some whitelist
692 // preservation. Thus whitelist is a Hail Mary low priority, blacklists and
693 // spam filtration all take priority. This second loop also checks if a region
694 // lock is causing us to buffer too much in the logs to help the reader(s),
695 // and will tell the slowest reader thread to skip log entries, and if
696 // persistent and hits a further threshold, kill the reader thread.
697 //
698 // The third thread is optional, and only gets hit if there was a whitelist
699 // and more needs to be pruned against the backstop of the region lock.
700 //
701 // LogBuffer::wrlock() must be held when this function is called.
702 //
prune(log_id_t id,unsigned long pruneRows,uid_t caller_uid)703 bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
704     LogTimeEntry* oldest = nullptr;
705     bool busy = false;
706     bool clearAll = pruneRows == ULONG_MAX;
707 
708     LogTimeEntry::rdlock();
709 
710     // Region locked?
711     LastLogTimes::iterator times = mTimes.begin();
712     while (times != mTimes.end()) {
713         LogTimeEntry* entry = (*times);
714         if (entry->owned_Locked() && entry->isWatching(id) &&
715             (!oldest || (oldest->mStart > entry->mStart) ||
716              ((oldest->mStart == entry->mStart) &&
717               (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec)))) {
718             oldest = entry;
719         }
720         times++;
721     }
722     log_time watermark(log_time::tv_sec_max, log_time::tv_nsec_max);
723     if (oldest) watermark = oldest->mStart - pruneMargin;
724 
725     LogBufferElementCollection::iterator it;
726 
727     if (__predict_false(caller_uid != AID_ROOT)) {  // unlikely
728         // Only here if clear all request from non system source, so chatty
729         // filter logistics is not required.
730         it = mLastSet[id] ? mLast[id] : mLogElements.begin();
731         while (it != mLogElements.end()) {
732             LogBufferElement* element = *it;
733 
734             if ((element->getLogId() != id) ||
735                 (element->getUid() != caller_uid)) {
736                 ++it;
737                 continue;
738             }
739 
740             if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
741                 mLast[id] = it;
742                 mLastSet[id] = true;
743             }
744 
745             if (oldest && (watermark <= element->getRealTime())) {
746                 busy = isBusy(watermark);
747                 if (busy) kickMe(oldest, id, pruneRows);
748                 break;
749             }
750 
751             it = erase(it);
752             if (--pruneRows == 0) {
753                 break;
754             }
755         }
756         LogTimeEntry::unlock();
757         return busy;
758     }
759 
760     // prune by worst offenders; by blacklist, UID, and by PID of system UID
761     bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
762     while (!clearAll && (pruneRows > 0)) {
763         // recalculate the worst offender on every batched pass
764         int worst = -1;  // not valid for getUid() or getKey()
765         size_t worst_sizes = 0;
766         size_t second_worst_sizes = 0;
767         pid_t worstPid = 0;  // POSIX guarantees PID != 0
768 
769         if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
770             // Calculate threshold as 12.5% of available storage
771             size_t threshold = log_buffer_size(id) / 8;
772 
773             if ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) {
774                 stats.sortTags(AID_ROOT, (pid_t)0, 2, id)
775                     .findWorst(worst, worst_sizes, second_worst_sizes,
776                                threshold);
777                 // per-pid filter for AID_SYSTEM sources is too complex
778             } else {
779                 stats.sort(AID_ROOT, (pid_t)0, 2, id)
780                     .findWorst(worst, worst_sizes, second_worst_sizes,
781                                threshold);
782 
783                 if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
784                     stats.sortPids(worst, (pid_t)0, 2, id)
785                         .findWorst(worstPid, worst_sizes, second_worst_sizes);
786                 }
787             }
788         }
789 
790         // skip if we have neither worst nor naughty filters
791         if ((worst == -1) && !hasBlacklist) {
792             break;
793         }
794 
795         bool kick = false;
796         bool leading = true;
797         it = mLastSet[id] ? mLast[id] : mLogElements.begin();
798         // Perform at least one mandatory garbage collection cycle in following
799         // - clear leading chatty tags
800         // - coalesce chatty tags
801         // - check age-out of preserved logs
802         bool gc = pruneRows <= 1;
803         if (!gc && (worst != -1)) {
804             {  // begin scope for worst found iterator
805                 LogBufferIteratorMap::iterator found =
806                     mLastWorst[id].find(worst);
807                 if ((found != mLastWorst[id].end()) &&
808                     (found->second != mLogElements.end())) {
809                     leading = false;
810                     it = found->second;
811                 }
812             }
813             if (worstPid) {  // begin scope for pid worst found iterator
814                 // FYI: worstPid only set if !LOG_ID_EVENTS and
815                 //      !LOG_ID_SECURITY, not going to make that assumption ...
816                 LogBufferPidIteratorMap::iterator found =
817                     mLastWorstPidOfSystem[id].find(worstPid);
818                 if ((found != mLastWorstPidOfSystem[id].end()) &&
819                     (found->second != mLogElements.end())) {
820                     leading = false;
821                     it = found->second;
822                 }
823             }
824         }
825         static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
826         LogBufferElementCollection::iterator lastt;
827         lastt = mLogElements.end();
828         --lastt;
829         LogBufferElementLast last;
830         while (it != mLogElements.end()) {
831             LogBufferElement* element = *it;
832 
833             if (oldest && (watermark <= element->getRealTime())) {
834                 busy = isBusy(watermark);
835                 // Do not let chatty eliding trigger any reader mitigation
836                 break;
837             }
838 
839             if (element->getLogId() != id) {
840                 ++it;
841                 continue;
842             }
843             // below this point element->getLogId() == id
844 
845             if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
846                 mLast[id] = it;
847                 mLastSet[id] = true;
848             }
849 
850             unsigned short dropped = element->getDropped();
851 
852             // remove any leading drops
853             if (leading && dropped) {
854                 it = erase(it);
855                 continue;
856             }
857 
858             if (dropped && last.coalesce(element, dropped)) {
859                 it = erase(it, true);
860                 continue;
861             }
862 
863             int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
864                           ? element->getTag()
865                           : element->getUid();
866 
867             if (hasBlacklist && mPrune.naughty(element)) {
868                 last.clear(element);
869                 it = erase(it);
870                 if (dropped) {
871                     continue;
872                 }
873 
874                 pruneRows--;
875                 if (pruneRows == 0) {
876                     break;
877                 }
878 
879                 if (key == worst) {
880                     kick = true;
881                     if (worst_sizes < second_worst_sizes) {
882                         break;
883                     }
884                     worst_sizes -= element->getMsgLen();
885                 }
886                 continue;
887             }
888 
889             if ((element->getRealTime() < ((*lastt)->getRealTime() - too_old)) ||
890                 (element->getRealTime() > (*lastt)->getRealTime())) {
891                 break;
892             }
893 
894             if (dropped) {
895                 last.add(element);
896                 if (worstPid &&
897                     ((!gc && (element->getPid() == worstPid)) ||
898                      (mLastWorstPidOfSystem[id].find(element->getPid()) ==
899                       mLastWorstPidOfSystem[id].end()))) {
900                     // element->getUid() may not be AID_SYSTEM, next best
901                     // watermark if current one empty. id is not LOG_ID_EVENTS
902                     // or LOG_ID_SECURITY because of worstPid check.
903                     mLastWorstPidOfSystem[id][element->getPid()] = it;
904                 }
905                 if ((!gc && !worstPid && (key == worst)) ||
906                     (mLastWorst[id].find(key) == mLastWorst[id].end())) {
907                     mLastWorst[id][key] = it;
908                 }
909                 ++it;
910                 continue;
911             }
912 
913             if ((key != worst) ||
914                 (worstPid && (element->getPid() != worstPid))) {
915                 leading = false;
916                 last.clear(element);
917                 ++it;
918                 continue;
919             }
920             // key == worst below here
921             // If worstPid set, then element->getPid() == worstPid below here
922 
923             pruneRows--;
924             if (pruneRows == 0) {
925                 break;
926             }
927 
928             kick = true;
929 
930             unsigned short len = element->getMsgLen();
931 
932             // do not create any leading drops
933             if (leading) {
934                 it = erase(it);
935             } else {
936                 stats.drop(element);
937                 element->setDropped(1);
938                 if (last.coalesce(element, 1)) {
939                     it = erase(it, true);
940                 } else {
941                     last.add(element);
942                     if (worstPid &&
943                         (!gc || (mLastWorstPidOfSystem[id].find(worstPid) ==
944                                  mLastWorstPidOfSystem[id].end()))) {
945                         // element->getUid() may not be AID_SYSTEM, next best
946                         // watermark if current one empty. id is not
947                         // LOG_ID_EVENTS or LOG_ID_SECURITY because of worstPid.
948                         mLastWorstPidOfSystem[id][worstPid] = it;
949                     }
950                     if ((!gc && !worstPid) ||
951                         (mLastWorst[id].find(worst) == mLastWorst[id].end())) {
952                         mLastWorst[id][worst] = it;
953                     }
954                     ++it;
955                 }
956             }
957             if (worst_sizes < second_worst_sizes) {
958                 break;
959             }
960             worst_sizes -= len;
961         }
962         last.clear();
963 
964         if (!kick || !mPrune.worstUidEnabled()) {
965             break;  // the following loop will ask bad clients to skip/drop
966         }
967     }
968 
969     bool whitelist = false;
970     bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
971     it = mLastSet[id] ? mLast[id] : mLogElements.begin();
972     while ((pruneRows > 0) && (it != mLogElements.end())) {
973         LogBufferElement* element = *it;
974 
975         if (element->getLogId() != id) {
976             it++;
977             continue;
978         }
979 
980         if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
981             mLast[id] = it;
982             mLastSet[id] = true;
983         }
984 
985         if (oldest && (watermark <= element->getRealTime())) {
986             busy = isBusy(watermark);
987             if (!whitelist && busy) kickMe(oldest, id, pruneRows);
988             break;
989         }
990 
991         if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
992             // WhiteListed
993             whitelist = true;
994             it++;
995             continue;
996         }
997 
998         it = erase(it);
999         pruneRows--;
1000     }
1001 
1002     // Do not save the whitelist if we are reader range limited
1003     if (whitelist && (pruneRows > 0)) {
1004         it = mLastSet[id] ? mLast[id] : mLogElements.begin();
1005         while ((it != mLogElements.end()) && (pruneRows > 0)) {
1006             LogBufferElement* element = *it;
1007 
1008             if (element->getLogId() != id) {
1009                 ++it;
1010                 continue;
1011             }
1012 
1013             if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
1014                 mLast[id] = it;
1015                 mLastSet[id] = true;
1016             }
1017 
1018             if (oldest && (watermark <= element->getRealTime())) {
1019                 busy = isBusy(watermark);
1020                 if (busy) kickMe(oldest, id, pruneRows);
1021                 break;
1022             }
1023 
1024             it = erase(it);
1025             pruneRows--;
1026         }
1027     }
1028 
1029     LogTimeEntry::unlock();
1030 
1031     return (pruneRows > 0) && busy;
1032 }
1033 
1034 // clear all rows of type "id" from the buffer.
clear(log_id_t id,uid_t uid)1035 bool LogBuffer::clear(log_id_t id, uid_t uid) {
1036     bool busy = true;
1037     // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
1038     for (int retry = 4;;) {
1039         if (retry == 1) {  // last pass
1040             // Check if it is still busy after the sleep, we say prune
1041             // one entry, not another clear run, so we are looking for
1042             // the quick side effect of the return value to tell us if
1043             // we have a _blocked_ reader.
1044             wrlock();
1045             busy = prune(id, 1, uid);
1046             unlock();
1047             // It is still busy, blocked reader(s), lets kill them all!
1048             // otherwise, lets be a good citizen and preserve the slow
1049             // readers and let the clear run (below) deal with determining
1050             // if we are still blocked and return an error code to caller.
1051             if (busy) {
1052                 LogTimeEntry::wrlock();
1053                 LastLogTimes::iterator times = mTimes.begin();
1054                 while (times != mTimes.end()) {
1055                     LogTimeEntry* entry = (*times);
1056                     // Killer punch
1057                     if (entry->owned_Locked() && entry->isWatching(id)) {
1058                         entry->release_Locked();
1059                     }
1060                     times++;
1061                 }
1062                 LogTimeEntry::unlock();
1063             }
1064         }
1065         wrlock();
1066         busy = prune(id, ULONG_MAX, uid);
1067         unlock();
1068         if (!busy || !--retry) {
1069             break;
1070         }
1071         sleep(1);  // Let reader(s) catch up after notification
1072     }
1073     return busy;
1074 }
1075 
1076 // get the used space associated with "id".
getSizeUsed(log_id_t id)1077 unsigned long LogBuffer::getSizeUsed(log_id_t id) {
1078     rdlock();
1079     size_t retval = stats.sizes(id);
1080     unlock();
1081     return retval;
1082 }
1083 
1084 // set the total space allocated to "id"
setSize(log_id_t id,unsigned long size)1085 int LogBuffer::setSize(log_id_t id, unsigned long size) {
1086     // Reasonable limits ...
1087     if (!__android_logger_valid_buffer_size(size)) {
1088         return -1;
1089     }
1090     wrlock();
1091     log_buffer_size(id) = size;
1092     unlock();
1093     return 0;
1094 }
1095 
1096 // get the total space allocated to "id"
getSize(log_id_t id)1097 unsigned long LogBuffer::getSize(log_id_t id) {
1098     rdlock();
1099     size_t retval = log_buffer_size(id);
1100     unlock();
1101     return retval;
1102 }
1103 
flushTo(SocketClient * reader,const log_time & start,pid_t * lastTid,bool privileged,bool security,int (* filter)(const LogBufferElement * element,void * arg),void * arg)1104 log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
1105                             pid_t* lastTid, bool privileged, bool security,
1106                             int (*filter)(const LogBufferElement* element,
1107                                           void* arg),
1108                             void* arg) {
1109     LogBufferElementCollection::iterator it;
1110     uid_t uid = reader->getUid();
1111 
1112     rdlock();
1113 
1114     if (start == log_time::EPOCH) {
1115         // client wants to start from the beginning
1116         it = mLogElements.begin();
1117     } else {
1118         // Cap to 300 iterations we look back for out-of-order entries.
1119         size_t count = 300;
1120 
1121         // Client wants to start from some specified time. Chances are
1122         // we are better off starting from the end of the time sorted list.
1123         LogBufferElementCollection::iterator last;
1124         for (last = it = mLogElements.end(); it != mLogElements.begin();
1125              /* do nothing */) {
1126             --it;
1127             LogBufferElement* element = *it;
1128             if (element->getRealTime() > start) {
1129                 last = it;
1130             } else if (element->getRealTime() == start) {
1131                 last = ++it;
1132                 break;
1133             } else if (!--count) {
1134                 break;
1135             }
1136         }
1137         it = last;
1138     }
1139 
1140     log_time curr = start;
1141 
1142     LogBufferElement* lastElement = nullptr;  // iterator corruption paranoia
1143     static const size_t maxSkip = 4194304;    // maximum entries to skip
1144     size_t skip = maxSkip;
1145     for (; it != mLogElements.end(); ++it) {
1146         LogBufferElement* element = *it;
1147 
1148         if (!--skip) {
1149             android::prdebug("reader.per: too many elements skipped");
1150             break;
1151         }
1152         if (element == lastElement) {
1153             android::prdebug("reader.per: identical elements");
1154             break;
1155         }
1156         lastElement = element;
1157 
1158         if (!privileged && (element->getUid() != uid)) {
1159             continue;
1160         }
1161 
1162         if (!security && (element->getLogId() == LOG_ID_SECURITY)) {
1163             continue;
1164         }
1165 
1166         // NB: calling out to another object with wrlock() held (safe)
1167         if (filter) {
1168             int ret = (*filter)(element, arg);
1169             if (ret == false) {
1170                 continue;
1171             }
1172             if (ret != true) {
1173                 break;
1174             }
1175         }
1176 
1177         bool sameTid = false;
1178         if (lastTid) {
1179             sameTid = lastTid[element->getLogId()] == element->getTid();
1180             // Dropped (chatty) immediately following a valid log from the
1181             // same source in the same log buffer indicates we have a
1182             // multiple identical squash.  chatty that differs source
1183             // is due to spam filter.  chatty to chatty of different
1184             // source is also due to spam filter.
1185             lastTid[element->getLogId()] =
1186                 (element->getDropped() && !sameTid) ? 0 : element->getTid();
1187         }
1188 
1189         unlock();
1190 
1191         // range locking in LastLogTimes looks after us
1192         curr = element->flushTo(reader, this, privileged, sameTid);
1193 
1194         if (curr == element->FLUSH_ERROR) {
1195             return curr;
1196         }
1197 
1198         skip = maxSkip;
1199         rdlock();
1200     }
1201     unlock();
1202 
1203     return curr;
1204 }
1205 
formatStatistics(uid_t uid,pid_t pid,unsigned int logMask)1206 std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
1207                                         unsigned int logMask) {
1208     wrlock();
1209 
1210     std::string ret = stats.format(uid, pid, logMask);
1211 
1212     unlock();
1213 
1214     return ret;
1215 }
1216