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