1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef _LOGD_LOG_STATISTICS_H__
18 #define _LOGD_LOG_STATISTICS_H__
19 
20 #include <ctype.h>
21 #include <inttypes.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 
27 #include <algorithm>  // std::max
28 #include <memory>
29 #include <string>
30 #include <string_view>
31 #include <unordered_map>
32 
33 #include <android-base/stringprintf.h>
34 #include <android/log.h>
35 #include <log/log_time.h>
36 #include <private/android_filesystem_config.h>
37 #include <utils/FastStrcmp.h>
38 
39 #include "LogBufferElement.h"
40 #include "LogUtils.h"
41 
42 #define log_id_for_each(i) \
43     for (log_id_t i = LOG_ID_MIN; (i) < LOG_ID_MAX; (i) = (log_id_t)((i) + 1))
44 
45 class LogStatistics;
46 
47 template <typename TKey, typename TEntry>
48 class LogHashtable {
49     std::unordered_map<TKey, TEntry> map;
50 
bucket_size()51     size_t bucket_size() const {
52         size_t count = 0;
53         for (size_t idx = 0; idx < map.bucket_count(); ++idx) {
54             size_t bucket_size = map.bucket_size(idx);
55             if (bucket_size == 0) bucket_size = 1;
56             count += bucket_size;
57         }
58         float load_factor = map.max_load_factor();
59         if (load_factor < 1.0) return count;
60         return count * load_factor;
61     }
62 
63     static const size_t unordered_map_per_entry_overhead = sizeof(void*);
64     static const size_t unordered_map_bucket_overhead = sizeof(void*);
65 
66    public:
size()67     size_t size() const {
68         return map.size();
69     }
70 
71     // Estimate unordered_map memory usage.
sizeOf()72     size_t sizeOf() const {
73         return sizeof(*this) +
74                (size() * (sizeof(TEntry) + unordered_map_per_entry_overhead)) +
75                (bucket_size() * sizeof(size_t) + unordered_map_bucket_overhead);
76     }
77 
78     typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
79     typedef
80         typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
81 
sort(uid_t uid,pid_t pid,size_t len)82     std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
83                                            size_t len) const {
84         if (!len) {
85             std::unique_ptr<const TEntry* []> sorted(nullptr);
86             return sorted;
87         }
88 
89         const TEntry** retval = new const TEntry*[len];
90         memset(retval, 0, sizeof(*retval) * len);
91 
92         for (const_iterator it = map.begin(); it != map.end(); ++it) {
93             const TEntry& entry = it->second;
94 
95             if ((uid != AID_ROOT) && (uid != entry.getUid())) {
96                 continue;
97             }
98             if (pid && entry.getPid() && (pid != entry.getPid())) {
99                 continue;
100             }
101 
102             size_t sizes = entry.getSizes();
103             ssize_t index = len - 1;
104             while ((!retval[index] || (sizes > retval[index]->getSizes())) &&
105                    (--index >= 0))
106                 ;
107             if (++index < (ssize_t)len) {
108                 size_t num = len - index - 1;
109                 if (num) {
110                     memmove(&retval[index + 1], &retval[index],
111                             num * sizeof(retval[0]));
112                 }
113                 retval[index] = &entry;
114             }
115         }
116         std::unique_ptr<const TEntry* []> sorted(retval);
117         return sorted;
118     }
119 
add(const TKey & key,const LogBufferElement * element)120     inline iterator add(const TKey& key, const LogBufferElement* element) {
121         iterator it = map.find(key);
122         if (it == map.end()) {
123             it = map.insert(std::make_pair(key, TEntry(element))).first;
124         } else {
125             it->second.add(element);
126         }
127         return it;
128     }
129 
add(TKey key)130     inline iterator add(TKey key) {
131         iterator it = map.find(key);
132         if (it == map.end()) {
133             it = map.insert(std::make_pair(key, TEntry(key))).first;
134         } else {
135             it->second.add(key);
136         }
137         return it;
138     }
139 
subtract(TKey && key,const LogBufferElement * element)140     void subtract(TKey&& key, const LogBufferElement* element) {
141         iterator it = map.find(std::move(key));
142         if ((it != map.end()) && it->second.subtract(element)) {
143             map.erase(it);
144         }
145     }
146 
subtract(const TKey & key,const LogBufferElement * element)147     void subtract(const TKey& key, const LogBufferElement* element) {
148         iterator it = map.find(key);
149         if ((it != map.end()) && it->second.subtract(element)) {
150             map.erase(it);
151         }
152     }
153 
drop(TKey key,const LogBufferElement * element)154     inline void drop(TKey key, const LogBufferElement* element) {
155         iterator it = map.find(key);
156         if (it != map.end()) {
157             it->second.drop(element);
158         }
159     }
160 
begin()161     inline iterator begin() {
162         return map.begin();
163     }
begin()164     inline const_iterator begin() const {
165         return map.begin();
166     }
end()167     inline iterator end() {
168         return map.end();
169     }
end()170     inline const_iterator end() const {
171         return map.end();
172     }
173 
174     std::string format(const LogStatistics& stat, uid_t uid, pid_t pid,
175                        const std::string& name = std::string(""),
176                        log_id_t id = LOG_ID_MAX) const {
177         static const size_t maximum_sorted_entries = 32;
178         std::string output;
179         std::unique_ptr<const TEntry* []> sorted =
180             sort(uid, pid, maximum_sorted_entries);
181         if (!sorted.get()) {
182             return output;
183         }
184         bool headerPrinted = false;
185         for (size_t index = 0; index < maximum_sorted_entries; ++index) {
186             const TEntry* entry = sorted[index];
187             if (!entry) {
188                 break;
189             }
190             if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
191                 break;
192             }
193             if (!headerPrinted) {
194                 output += "\n\n";
195                 output += entry->formatHeader(name, id);
196                 headerPrinted = true;
197             }
198             output += entry->format(stat, id);
199         }
200         return output;
201     }
202 };
203 
204 namespace EntryBaseConstants {
205 static constexpr size_t pruned_len = 14;
206 static constexpr size_t total_len = 80;
207 }
208 
209 struct EntryBase {
210     size_t size;
211 
EntryBaseEntryBase212     EntryBase() : size(0) {
213     }
EntryBaseEntryBase214     explicit EntryBase(const LogBufferElement* element)
215         : size(element->getMsgLen()) {
216     }
217 
getSizesEntryBase218     size_t getSizes() const {
219         return size;
220     }
221 
addEntryBase222     inline void add(const LogBufferElement* element) {
223         size += element->getMsgLen();
224     }
subtractEntryBase225     inline bool subtract(const LogBufferElement* element) {
226         size -= element->getMsgLen();
227         return !size;
228     }
229 
formatLineEntryBase230     static std::string formatLine(const std::string& name,
231                                   const std::string& size,
232                                   const std::string& pruned) {
233         ssize_t drop_len =
234             std::max(pruned.length() + 1, EntryBaseConstants::pruned_len);
235         ssize_t size_len =
236             std::max(size.length() + 1, EntryBaseConstants::total_len -
237                                             name.length() - drop_len - 1);
238 
239         std::string ret = android::base::StringPrintf(
240             "%s%*s%*s", name.c_str(), (int)size_len, size.c_str(),
241             (int)drop_len, pruned.c_str());
242         // remove any trailing spaces
243         size_t pos = ret.size();
244         size_t len = 0;
245         while (pos && isspace(ret[--pos])) ++len;
246         if (len) ret.erase(pos + 1, len);
247         return ret + "\n";
248     }
249 };
250 
251 struct EntryBaseDropped : public EntryBase {
252     size_t dropped;
253 
EntryBaseDroppedEntryBaseDropped254     EntryBaseDropped() : dropped(0) {
255     }
EntryBaseDroppedEntryBaseDropped256     explicit EntryBaseDropped(const LogBufferElement* element)
257         : EntryBase(element), dropped(element->getDropped()) {
258     }
259 
getDroppedEntryBaseDropped260     size_t getDropped() const {
261         return dropped;
262     }
263 
addEntryBaseDropped264     inline void add(const LogBufferElement* element) {
265         dropped += element->getDropped();
266         EntryBase::add(element);
267     }
subtractEntryBaseDropped268     inline bool subtract(const LogBufferElement* element) {
269         dropped -= element->getDropped();
270         return EntryBase::subtract(element) && !dropped;
271     }
dropEntryBaseDropped272     inline void drop(const LogBufferElement* element) {
273         dropped += 1;
274         EntryBase::subtract(element);
275     }
276 };
277 
278 struct UidEntry : public EntryBaseDropped {
279     const uid_t uid;
280     pid_t pid;
281 
UidEntryUidEntry282     explicit UidEntry(const LogBufferElement* element)
283         : EntryBaseDropped(element),
284           uid(element->getUid()),
285           pid(element->getPid()) {
286     }
287 
getKeyUidEntry288     inline const uid_t& getKey() const {
289         return uid;
290     }
getUidUidEntry291     inline const uid_t& getUid() const {
292         return getKey();
293     }
getPidUidEntry294     inline const pid_t& getPid() const {
295         return pid;
296     }
297 
addUidEntry298     inline void add(const LogBufferElement* element) {
299         if (pid != element->getPid()) {
300             pid = -1;
301         }
302         EntryBaseDropped::add(element);
303     }
304 
305     std::string formatHeader(const std::string& name, log_id_t id) const;
306     std::string format(const LogStatistics& stat, log_id_t id) const;
307 };
308 
309 struct PidEntry : public EntryBaseDropped {
310     const pid_t pid;
311     uid_t uid;
312     char* name;
313 
PidEntryPidEntry314     explicit PidEntry(pid_t pid)
315         : EntryBaseDropped(),
316           pid(pid),
317           uid(android::pidToUid(pid)),
318           name(android::pidToName(pid)) {
319     }
PidEntryPidEntry320     explicit PidEntry(const LogBufferElement* element)
321         : EntryBaseDropped(element),
322           pid(element->getPid()),
323           uid(element->getUid()),
324           name(android::pidToName(pid)) {
325     }
PidEntryPidEntry326     PidEntry(const PidEntry& element)
327         : EntryBaseDropped(element),
328           pid(element.pid),
329           uid(element.uid),
330           name(element.name ? strdup(element.name) : nullptr) {
331     }
~PidEntryPidEntry332     ~PidEntry() {
333         free(name);
334     }
335 
getKeyPidEntry336     const pid_t& getKey() const {
337         return pid;
338     }
getPidPidEntry339     const pid_t& getPid() const {
340         return getKey();
341     }
getUidPidEntry342     const uid_t& getUid() const {
343         return uid;
344     }
getNamePidEntry345     const char* getName() const {
346         return name;
347     }
348 
addPidEntry349     inline void add(pid_t newPid) {
350         if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
351             free(name);
352             name = nullptr;
353         }
354         if (!name) {
355             name = android::pidToName(newPid);
356         }
357     }
358 
addPidEntry359     inline void add(const LogBufferElement* element) {
360         uid_t incomingUid = element->getUid();
361         if (getUid() != incomingUid) {
362             uid = incomingUid;
363             free(name);
364             name = android::pidToName(element->getPid());
365         } else {
366             add(element->getPid());
367         }
368         EntryBaseDropped::add(element);
369     }
370 
371     std::string formatHeader(const std::string& name, log_id_t id) const;
372     std::string format(const LogStatistics& stat, log_id_t id) const;
373 };
374 
375 struct TidEntry : public EntryBaseDropped {
376     const pid_t tid;
377     pid_t pid;
378     uid_t uid;
379     char* name;
380 
TidEntryTidEntry381     TidEntry(pid_t tid, pid_t pid)
382         : EntryBaseDropped(),
383           tid(tid),
384           pid(pid),
385           uid(android::pidToUid(tid)),
386           name(android::tidToName(tid)) {
387     }
TidEntryTidEntry388     TidEntry(pid_t tid)
389         : EntryBaseDropped(),
390           tid(tid),
391           pid(android::tidToPid(tid)),
392           uid(android::pidToUid(tid)),
393           name(android::tidToName(tid)) {
394     }
TidEntryTidEntry395     explicit TidEntry(const LogBufferElement* element)
396         : EntryBaseDropped(element),
397           tid(element->getTid()),
398           pid(element->getPid()),
399           uid(element->getUid()),
400           name(android::tidToName(tid)) {
401     }
TidEntryTidEntry402     TidEntry(const TidEntry& element)
403         : EntryBaseDropped(element),
404           tid(element.tid),
405           pid(element.pid),
406           uid(element.uid),
407           name(element.name ? strdup(element.name) : nullptr) {
408     }
~TidEntryTidEntry409     ~TidEntry() {
410         free(name);
411     }
412 
getKeyTidEntry413     const pid_t& getKey() const {
414         return tid;
415     }
getTidTidEntry416     const pid_t& getTid() const {
417         return getKey();
418     }
getPidTidEntry419     const pid_t& getPid() const {
420         return pid;
421     }
getUidTidEntry422     const uid_t& getUid() const {
423         return uid;
424     }
getNameTidEntry425     const char* getName() const {
426         return name;
427     }
428 
addTidEntry429     inline void add(pid_t incomingTid) {
430         if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
431             free(name);
432             name = nullptr;
433         }
434         if (!name) {
435             name = android::tidToName(incomingTid);
436         }
437     }
438 
addTidEntry439     inline void add(const LogBufferElement* element) {
440         uid_t incomingUid = element->getUid();
441         pid_t incomingPid = element->getPid();
442         if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
443             uid = incomingUid;
444             pid = incomingPid;
445             free(name);
446             name = android::tidToName(element->getTid());
447         } else {
448             add(element->getTid());
449         }
450         EntryBaseDropped::add(element);
451     }
452 
453     std::string formatHeader(const std::string& name, log_id_t id) const;
454     std::string format(const LogStatistics& stat, log_id_t id) const;
455 };
456 
457 struct TagEntry : public EntryBaseDropped {
458     const uint32_t tag;
459     pid_t pid;
460     uid_t uid;
461 
TagEntryTagEntry462     explicit TagEntry(const LogBufferElement* element)
463         : EntryBaseDropped(element),
464           tag(element->getTag()),
465           pid(element->getPid()),
466           uid(element->getUid()) {
467     }
468 
getKeyTagEntry469     const uint32_t& getKey() const {
470         return tag;
471     }
getPidTagEntry472     const pid_t& getPid() const {
473         return pid;
474     }
getUidTagEntry475     const uid_t& getUid() const {
476         return uid;
477     }
getNameTagEntry478     const char* getName() const {
479         return android::tagToName(tag);
480     }
481 
addTagEntry482     inline void add(const LogBufferElement* element) {
483         if (uid != element->getUid()) {
484             uid = -1;
485         }
486         if (pid != element->getPid()) {
487             pid = -1;
488         }
489         EntryBaseDropped::add(element);
490     }
491 
492     std::string formatHeader(const std::string& name, log_id_t id) const;
493     std::string format(const LogStatistics& stat, log_id_t id) const;
494 };
495 
496 struct TagNameKey {
497     std::string* alloc;
498     std::string_view name;  // Saves space if const char*
499 
TagNameKeyTagNameKey500     explicit TagNameKey(const LogBufferElement* element)
501         : alloc(nullptr), name("", strlen("")) {
502         if (element->isBinary()) {
503             uint32_t tag = element->getTag();
504             if (tag) {
505                 const char* cp = android::tagToName(tag);
506                 if (cp) {
507                     name = std::string_view(cp, strlen(cp));
508                     return;
509                 }
510             }
511             alloc = new std::string(
512                 android::base::StringPrintf("[%" PRIu32 "]", tag));
513             if (!alloc) return;
514             name = std::string_view(alloc->c_str(), alloc->size());
515             return;
516         }
517         const char* msg = element->getMsg();
518         if (!msg) {
519             name = std::string_view("chatty", strlen("chatty"));
520             return;
521         }
522         ++msg;
523         unsigned short len = element->getMsgLen();
524         len = (len <= 1) ? 0 : strnlen(msg, len - 1);
525         if (!len) {
526             name = std::string_view("<NULL>", strlen("<NULL>"));
527             return;
528         }
529         alloc = new std::string(msg, len);
530         if (!alloc) return;
531         name = std::string_view(alloc->c_str(), alloc->size());
532     }
533 
TagNameKeyTagNameKey534     explicit TagNameKey(TagNameKey&& rval)
535         : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
536         rval.alloc = nullptr;
537     }
538 
TagNameKeyTagNameKey539     explicit TagNameKey(const TagNameKey& rval)
540         : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
541           name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
542     }
543 
~TagNameKeyTagNameKey544     ~TagNameKey() {
545         if (alloc) delete alloc;
546     }
547 
string_viewTagNameKey548     operator const std::string_view() const {
549         return name;
550     }
551 
dataTagNameKey552     const char* data() const {
553         return name.data();
554     }
lengthTagNameKey555     size_t length() const {
556         return name.length();
557     }
558 
559     bool operator==(const TagNameKey& rval) const {
560         if (length() != rval.length()) return false;
561         if (length() == 0) return true;
562         return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
563     }
564     bool operator!=(const TagNameKey& rval) const {
565         return !(*this == rval);
566     }
567 
getAllocLengthTagNameKey568     size_t getAllocLength() const {
569         return alloc ? alloc->length() + 1 + sizeof(std::string) : 0;
570     }
571 };
572 
573 // Hash for TagNameKey
574 template <>
575 struct std::hash<TagNameKey>
576     : public std::unary_function<const TagNameKey&, size_t> {
577     size_t operator()(const TagNameKey& __t) const noexcept {
578         if (!__t.length()) return 0;
579         return std::hash<std::string_view>()(std::string_view(__t));
580     }
581 };
582 
583 struct TagNameEntry : public EntryBase {
584     pid_t tid;
585     pid_t pid;
586     uid_t uid;
587     TagNameKey name;
588 
589     explicit TagNameEntry(const LogBufferElement* element)
590         : EntryBase(element),
591           tid(element->getTid()),
592           pid(element->getPid()),
593           uid(element->getUid()),
594           name(element) {
595     }
596 
597     const TagNameKey& getKey() const {
598         return name;
599     }
600     const pid_t& getTid() const {
601         return tid;
602     }
603     const pid_t& getPid() const {
604         return pid;
605     }
606     const uid_t& getUid() const {
607         return uid;
608     }
609     const char* getName() const {
610         return name.data();
611     }
612     size_t getNameAllocLength() const {
613         return name.getAllocLength();
614     }
615 
616     inline void add(const LogBufferElement* element) {
617         if (uid != element->getUid()) {
618             uid = -1;
619         }
620         if (pid != element->getPid()) {
621             pid = -1;
622         }
623         if (tid != element->getTid()) {
624             tid = -1;
625         }
626         EntryBase::add(element);
627     }
628 
629     std::string formatHeader(const std::string& name, log_id_t id) const;
630     std::string format(const LogStatistics& stat, log_id_t id) const;
631 };
632 
633 template <typename TEntry>
634 class LogFindWorst {
635     std::unique_ptr<const TEntry* []> sorted;
636 
637    public:
638     explicit LogFindWorst(std::unique_ptr<const TEntry* []>&& sorted)
639         : sorted(std::move(sorted)) {
640     }
641 
642     void findWorst(int& worst, size_t& worst_sizes, size_t& second_worst_sizes,
643                    size_t threshold) {
644         if (sorted.get() && sorted[0] && sorted[1]) {
645             worst_sizes = sorted[0]->getSizes();
646             if ((worst_sizes > threshold)
647                 // Allow time horizon to extend roughly tenfold, assume
648                 // average entry length is 100 characters.
649                 && (worst_sizes > (10 * sorted[0]->getDropped()))) {
650                 worst = sorted[0]->getKey();
651                 second_worst_sizes = sorted[1]->getSizes();
652                 if (second_worst_sizes < threshold) {
653                     second_worst_sizes = threshold;
654                 }
655             }
656         }
657     }
658 
659     void findWorst(int& worst, size_t worst_sizes, size_t& second_worst_sizes) {
660         if (sorted.get() && sorted[0] && sorted[1]) {
661             worst = sorted[0]->getKey();
662             second_worst_sizes =
663                 worst_sizes - sorted[0]->getSizes() + sorted[1]->getSizes();
664         }
665     }
666 };
667 
668 // Log Statistics
669 class LogStatistics {
670     friend UidEntry;
671 
672     size_t mSizes[LOG_ID_MAX];
673     size_t mElements[LOG_ID_MAX];
674     size_t mDroppedElements[LOG_ID_MAX];
675     size_t mSizesTotal[LOG_ID_MAX];
676     size_t mElementsTotal[LOG_ID_MAX];
677     log_time mOldest[LOG_ID_MAX];
678     log_time mNewest[LOG_ID_MAX];
679     log_time mNewestDropped[LOG_ID_MAX];
680     static size_t SizesTotal;
681     bool enable;
682 
683     // uid to size list
684     typedef LogHashtable<uid_t, UidEntry> uidTable_t;
685     uidTable_t uidTable[LOG_ID_MAX];
686 
687     // pid of system to size list
688     typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
689     pidSystemTable_t pidSystemTable[LOG_ID_MAX];
690 
691     // pid to uid list
692     typedef LogHashtable<pid_t, PidEntry> pidTable_t;
693     pidTable_t pidTable;
694 
695     // tid to uid list
696     typedef LogHashtable<pid_t, TidEntry> tidTable_t;
697     tidTable_t tidTable;
698 
699     // tag list
700     typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
701     tagTable_t tagTable;
702 
703     // security tag list
704     tagTable_t securityTagTable;
705 
706     // global tag list
707     typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
708     tagNameTable_t tagNameTable;
709 
710     size_t sizeOf() const {
711         size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
712                       tagTable.sizeOf() + securityTagTable.sizeOf() +
713                       tagNameTable.sizeOf() +
714                       (pidTable.size() * sizeof(pidTable_t::iterator)) +
715                       (tagTable.size() * sizeof(tagTable_t::iterator));
716         for (auto it : pidTable) {
717             const char* name = it.second.getName();
718             if (name) size += strlen(name) + 1;
719         }
720         for (auto it : tidTable) {
721             const char* name = it.second.getName();
722             if (name) size += strlen(name) + 1;
723         }
724         for (auto it : tagNameTable) size += it.second.getNameAllocLength();
725         log_id_for_each(id) {
726             size += uidTable[id].sizeOf();
727             size += uidTable[id].size() * sizeof(uidTable_t::iterator);
728             size += pidSystemTable[id].sizeOf();
729             size +=
730                 pidSystemTable[id].size() * sizeof(pidSystemTable_t::iterator);
731         }
732         return size;
733     }
734 
735    public:
736     LogStatistics();
737 
738     void enableStatistics() {
739         enable = true;
740     }
741 
742     void addTotal(LogBufferElement* entry);
743     void add(LogBufferElement* entry);
744     void subtract(LogBufferElement* entry);
745     // entry->setDropped(1) must follow this call
746     void drop(LogBufferElement* entry);
747     // Correct for coalescing two entries referencing dropped content
748     void erase(LogBufferElement* element) {
749         log_id_t log_id = element->getLogId();
750         --mElements[log_id];
751         --mDroppedElements[log_id];
752     }
753 
754     LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
755         return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
756     }
757     LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len,
758                                     log_id id) {
759         return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
760     }
761     LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
762         return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
763     }
764 
765     // fast track current value by id only
766     size_t sizes(log_id_t id) const {
767         return mSizes[id];
768     }
769     size_t elements(log_id_t id) const {
770         return mElements[id];
771     }
772     size_t realElements(log_id_t id) const {
773         return mElements[id] - mDroppedElements[id];
774     }
775     size_t sizesTotal(log_id_t id) const {
776         return mSizesTotal[id];
777     }
778     size_t elementsTotal(log_id_t id) const {
779         return mElementsTotal[id];
780     }
781     static size_t sizesTotal() {
782         return SizesTotal;
783     }
784 
785     std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
786 
787     // helper (must be locked directly or implicitly by mLogElementsLock)
788     const char* pidToName(pid_t pid) const;
789     uid_t pidToUid(pid_t pid);
790     pid_t tidToPid(pid_t tid);
791     const char* uidToName(uid_t uid) const;
792 };
793 
794 #endif  // _LOGD_LOG_STATISTICS_H__
795