• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace android {
310 uid_t pidToUid(pid_t pid);
311 }
312 
313 struct PidEntry : public EntryBaseDropped {
314     const pid_t pid;
315     uid_t uid;
316     char* name;
317 
PidEntryPidEntry318     explicit PidEntry(pid_t pid)
319         : EntryBaseDropped(),
320           pid(pid),
321           uid(android::pidToUid(pid)),
322           name(android::pidToName(pid)) {
323     }
PidEntryPidEntry324     explicit PidEntry(const LogBufferElement* element)
325         : EntryBaseDropped(element),
326           pid(element->getPid()),
327           uid(element->getUid()),
328           name(android::pidToName(pid)) {
329     }
PidEntryPidEntry330     PidEntry(const PidEntry& element)
331         : EntryBaseDropped(element),
332           pid(element.pid),
333           uid(element.uid),
334           name(element.name ? strdup(element.name) : nullptr) {
335     }
~PidEntryPidEntry336     ~PidEntry() {
337         free(name);
338     }
339 
getKeyPidEntry340     const pid_t& getKey() const {
341         return pid;
342     }
getPidPidEntry343     const pid_t& getPid() const {
344         return getKey();
345     }
getUidPidEntry346     const uid_t& getUid() const {
347         return uid;
348     }
getNamePidEntry349     const char* getName() const {
350         return name;
351     }
352 
addPidEntry353     inline void add(pid_t newPid) {
354         if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
355             free(name);
356             name = nullptr;
357         }
358         if (!name) {
359             name = android::pidToName(newPid);
360         }
361     }
362 
addPidEntry363     inline void add(const LogBufferElement* element) {
364         uid_t incomingUid = element->getUid();
365         if (getUid() != incomingUid) {
366             uid = incomingUid;
367             free(name);
368             name = android::pidToName(element->getPid());
369         } else {
370             add(element->getPid());
371         }
372         EntryBaseDropped::add(element);
373     }
374 
375     std::string formatHeader(const std::string& name, log_id_t id) const;
376     std::string format(const LogStatistics& stat, log_id_t id) const;
377 };
378 
379 struct TidEntry : public EntryBaseDropped {
380     const pid_t tid;
381     pid_t pid;
382     uid_t uid;
383     char* name;
384 
TidEntryTidEntry385     TidEntry(pid_t tid, pid_t pid)
386         : EntryBaseDropped(),
387           tid(tid),
388           pid(pid),
389           uid(android::pidToUid(tid)),
390           name(android::tidToName(tid)) {
391     }
TidEntryTidEntry392     explicit TidEntry(const LogBufferElement* element)
393         : EntryBaseDropped(element),
394           tid(element->getTid()),
395           pid(element->getPid()),
396           uid(element->getUid()),
397           name(android::tidToName(tid)) {
398     }
TidEntryTidEntry399     TidEntry(const TidEntry& element)
400         : EntryBaseDropped(element),
401           tid(element.tid),
402           pid(element.pid),
403           uid(element.uid),
404           name(element.name ? strdup(element.name) : nullptr) {
405     }
~TidEntryTidEntry406     ~TidEntry() {
407         free(name);
408     }
409 
getKeyTidEntry410     const pid_t& getKey() const {
411         return tid;
412     }
getTidTidEntry413     const pid_t& getTid() const {
414         return getKey();
415     }
getPidTidEntry416     const pid_t& getPid() const {
417         return pid;
418     }
getUidTidEntry419     const uid_t& getUid() const {
420         return uid;
421     }
getNameTidEntry422     const char* getName() const {
423         return name;
424     }
425 
addTidEntry426     inline void add(pid_t incomingTid) {
427         if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
428             free(name);
429             name = nullptr;
430         }
431         if (!name) {
432             name = android::tidToName(incomingTid);
433         }
434     }
435 
addTidEntry436     inline void add(const LogBufferElement* element) {
437         uid_t incomingUid = element->getUid();
438         pid_t incomingPid = element->getPid();
439         if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
440             uid = incomingUid;
441             pid = incomingPid;
442             free(name);
443             name = android::tidToName(element->getTid());
444         } else {
445             add(element->getTid());
446         }
447         EntryBaseDropped::add(element);
448     }
449 
450     std::string formatHeader(const std::string& name, log_id_t id) const;
451     std::string format(const LogStatistics& stat, log_id_t id) const;
452 };
453 
454 struct TagEntry : public EntryBaseDropped {
455     const uint32_t tag;
456     pid_t pid;
457     uid_t uid;
458 
TagEntryTagEntry459     explicit TagEntry(const LogBufferElement* element)
460         : EntryBaseDropped(element),
461           tag(element->getTag()),
462           pid(element->getPid()),
463           uid(element->getUid()) {
464     }
465 
getKeyTagEntry466     const uint32_t& getKey() const {
467         return tag;
468     }
getPidTagEntry469     const pid_t& getPid() const {
470         return pid;
471     }
getUidTagEntry472     const uid_t& getUid() const {
473         return uid;
474     }
getNameTagEntry475     const char* getName() const {
476         return android::tagToName(tag);
477     }
478 
addTagEntry479     inline void add(const LogBufferElement* element) {
480         if (uid != element->getUid()) {
481             uid = -1;
482         }
483         if (pid != element->getPid()) {
484             pid = -1;
485         }
486         EntryBaseDropped::add(element);
487     }
488 
489     std::string formatHeader(const std::string& name, log_id_t id) const;
490     std::string format(const LogStatistics& stat, log_id_t id) const;
491 };
492 
493 struct TagNameKey {
494     std::string* alloc;
495     std::string_view name;  // Saves space if const char*
496 
TagNameKeyTagNameKey497     explicit TagNameKey(const LogBufferElement* element)
498         : alloc(nullptr), name("", strlen("")) {
499         if (element->isBinary()) {
500             uint32_t tag = element->getTag();
501             if (tag) {
502                 const char* cp = android::tagToName(tag);
503                 if (cp) {
504                     name = std::string_view(cp, strlen(cp));
505                     return;
506                 }
507             }
508             alloc = new std::string(
509                 android::base::StringPrintf("[%" PRIu32 "]", tag));
510             if (!alloc) return;
511             name = std::string_view(alloc->c_str(), alloc->size());
512             return;
513         }
514         const char* msg = element->getMsg();
515         if (!msg) {
516             name = std::string_view("chatty", strlen("chatty"));
517             return;
518         }
519         ++msg;
520         uint16_t len = element->getMsgLen();
521         len = (len <= 1) ? 0 : strnlen(msg, len - 1);
522         if (!len) {
523             name = std::string_view("<NULL>", strlen("<NULL>"));
524             return;
525         }
526         alloc = new std::string(msg, len);
527         if (!alloc) return;
528         name = std::string_view(alloc->c_str(), alloc->size());
529     }
530 
TagNameKeyTagNameKey531     explicit TagNameKey(TagNameKey&& rval) noexcept
532         : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
533         rval.alloc = nullptr;
534     }
535 
TagNameKeyTagNameKey536     explicit TagNameKey(const TagNameKey& rval)
537         : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
538           name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
539     }
540 
~TagNameKeyTagNameKey541     ~TagNameKey() {
542         if (alloc) delete alloc;
543     }
544 
string_viewTagNameKey545     operator const std::string_view() const {
546         return name;
547     }
548 
dataTagNameKey549     const char* data() const {
550         return name.data();
551     }
lengthTagNameKey552     size_t length() const {
553         return name.length();
554     }
555 
556     bool operator==(const TagNameKey& rval) const {
557         if (length() != rval.length()) return false;
558         if (length() == 0) return true;
559         return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
560     }
561     bool operator!=(const TagNameKey& rval) const {
562         return !(*this == rval);
563     }
564 
getAllocLengthTagNameKey565     size_t getAllocLength() const {
566         return alloc ? alloc->length() + 1 + sizeof(std::string) : 0;
567     }
568 };
569 
570 // Hash for TagNameKey
571 template <>
572 struct std::hash<TagNameKey>
573     : public std::unary_function<const TagNameKey&, size_t> {
574     size_t operator()(const TagNameKey& __t) const noexcept {
575         if (!__t.length()) return 0;
576         return std::hash<std::string_view>()(std::string_view(__t));
577     }
578 };
579 
580 struct TagNameEntry : public EntryBase {
581     pid_t tid;
582     pid_t pid;
583     uid_t uid;
584     TagNameKey name;
585 
586     explicit TagNameEntry(const LogBufferElement* element)
587         : EntryBase(element),
588           tid(element->getTid()),
589           pid(element->getPid()),
590           uid(element->getUid()),
591           name(element) {
592     }
593 
594     const TagNameKey& getKey() const {
595         return name;
596     }
597     const pid_t& getTid() const {
598         return tid;
599     }
600     const pid_t& getPid() const {
601         return pid;
602     }
603     const uid_t& getUid() const {
604         return uid;
605     }
606     const char* getName() const {
607         return name.data();
608     }
609     size_t getNameAllocLength() const {
610         return name.getAllocLength();
611     }
612 
613     inline void add(const LogBufferElement* element) {
614         if (uid != element->getUid()) {
615             uid = -1;
616         }
617         if (pid != element->getPid()) {
618             pid = -1;
619         }
620         if (tid != element->getTid()) {
621             tid = -1;
622         }
623         EntryBase::add(element);
624     }
625 
626     std::string formatHeader(const std::string& name, log_id_t id) const;
627     std::string format(const LogStatistics& stat, log_id_t id) const;
628 };
629 
630 template <typename TEntry>
631 class LogFindWorst {
632     std::unique_ptr<const TEntry* []> sorted;
633 
634    public:
635     explicit LogFindWorst(std::unique_ptr<const TEntry* []>&& sorted)
636         : sorted(std::move(sorted)) {
637     }
638 
639     void findWorst(int& worst, size_t& worst_sizes, size_t& second_worst_sizes,
640                    size_t threshold) {
641         if (sorted.get() && sorted[0] && sorted[1]) {
642             worst_sizes = sorted[0]->getSizes();
643             if ((worst_sizes > threshold)
644                 // Allow time horizon to extend roughly tenfold, assume
645                 // average entry length is 100 characters.
646                 && (worst_sizes > (10 * sorted[0]->getDropped()))) {
647                 worst = sorted[0]->getKey();
648                 second_worst_sizes = sorted[1]->getSizes();
649                 if (second_worst_sizes < threshold) {
650                     second_worst_sizes = threshold;
651                 }
652             }
653         }
654     }
655 
656     void findWorst(int& worst, size_t worst_sizes, size_t& second_worst_sizes) {
657         if (sorted.get() && sorted[0] && sorted[1]) {
658             worst = sorted[0]->getKey();
659             second_worst_sizes =
660                 worst_sizes - sorted[0]->getSizes() + sorted[1]->getSizes();
661         }
662     }
663 };
664 
665 // Log Statistics
666 class LogStatistics {
667     friend UidEntry;
668 
669     size_t mSizes[LOG_ID_MAX];
670     size_t mElements[LOG_ID_MAX];
671     size_t mDroppedElements[LOG_ID_MAX];
672     size_t mSizesTotal[LOG_ID_MAX];
673     size_t mElementsTotal[LOG_ID_MAX];
674     log_time mOldest[LOG_ID_MAX];
675     log_time mNewest[LOG_ID_MAX];
676     log_time mNewestDropped[LOG_ID_MAX];
677     static size_t SizesTotal;
678     bool enable;
679 
680     // uid to size list
681     typedef LogHashtable<uid_t, UidEntry> uidTable_t;
682     uidTable_t uidTable[LOG_ID_MAX];
683 
684     // pid of system to size list
685     typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
686     pidSystemTable_t pidSystemTable[LOG_ID_MAX];
687 
688     // pid to uid list
689     typedef LogHashtable<pid_t, PidEntry> pidTable_t;
690     pidTable_t pidTable;
691 
692     // tid to uid list
693     typedef LogHashtable<pid_t, TidEntry> tidTable_t;
694     tidTable_t tidTable;
695 
696     // tag list
697     typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
698     tagTable_t tagTable;
699 
700     // security tag list
701     tagTable_t securityTagTable;
702 
703     // global tag list
704     typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
705     tagNameTable_t tagNameTable;
706 
707     size_t sizeOf() const {
708         size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
709                       tagTable.sizeOf() + securityTagTable.sizeOf() +
710                       tagNameTable.sizeOf() +
711                       (pidTable.size() * sizeof(pidTable_t::iterator)) +
712                       (tagTable.size() * sizeof(tagTable_t::iterator));
713         for (auto it : pidTable) {
714             const char* name = it.second.getName();
715             if (name) size += strlen(name) + 1;
716         }
717         for (auto it : tidTable) {
718             const char* name = it.second.getName();
719             if (name) size += strlen(name) + 1;
720         }
721         for (auto it : tagNameTable) size += it.second.getNameAllocLength();
722         log_id_for_each(id) {
723             size += uidTable[id].sizeOf();
724             size += uidTable[id].size() * sizeof(uidTable_t::iterator);
725             size += pidSystemTable[id].sizeOf();
726             size +=
727                 pidSystemTable[id].size() * sizeof(pidSystemTable_t::iterator);
728         }
729         return size;
730     }
731 
732    public:
733     LogStatistics();
734 
735     void enableStatistics() {
736         enable = true;
737     }
738 
739     void addTotal(LogBufferElement* entry);
740     void add(LogBufferElement* entry);
741     void subtract(LogBufferElement* entry);
742     // entry->setDropped(1) must follow this call
743     void drop(LogBufferElement* entry);
744     // Correct for coalescing two entries referencing dropped content
745     void erase(LogBufferElement* element) {
746         log_id_t log_id = element->getLogId();
747         --mElements[log_id];
748         --mDroppedElements[log_id];
749     }
750 
751     LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
752         return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
753     }
754     LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len,
755                                     log_id id) {
756         return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
757     }
758     LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
759         return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
760     }
761 
762     // fast track current value by id only
763     size_t sizes(log_id_t id) const {
764         return mSizes[id];
765     }
766     size_t elements(log_id_t id) const {
767         return mElements[id];
768     }
769     size_t realElements(log_id_t id) const {
770         return mElements[id] - mDroppedElements[id];
771     }
772     size_t sizesTotal(log_id_t id) const {
773         return mSizesTotal[id];
774     }
775     size_t elementsTotal(log_id_t id) const {
776         return mElementsTotal[id];
777     }
778     static size_t sizesTotal() {
779         return SizesTotal;
780     }
781 
782     std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
783 
784     // helper (must be locked directly or implicitly by mLogElementsLock)
785     const char* pidToName(pid_t pid) const;
786     uid_t pidToUid(pid_t pid);
787     const char* uidToName(uid_t uid) const;
788 };
789 
790 #endif  // _LOGD_LOG_STATISTICS_H__
791