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 #include "LogStatistics.h"
18 
19 #include <ctype.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <pwd.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 
28 #include <list>
29 #include <vector>
30 
31 #include <android-base/logging.h>
32 #include <android-base/strings.h>
33 #include <private/android_logger.h>
34 
35 #include "LogBufferElement.h"
36 
37 static const uint64_t hourSec = 60 * 60;
38 static const uint64_t monthSec = 31 * 24 * hourSec;
39 
40 std::atomic<size_t> LogStatistics::SizesTotal;
41 
TagNameKey(const LogStatisticsElement & element)42 static std::string TagNameKey(const LogStatisticsElement& element) {
43     if (IsBinary(element.log_id)) {
44         uint32_t tag = element.tag;
45         if (tag) {
46             const char* cp = android::tagToName(tag);
47             if (cp) {
48                 return std::string(cp);
49             }
50         }
51         return android::base::StringPrintf("[%" PRIu32 "]", tag);
52     }
53     const char* msg = element.msg;
54     ++msg;
55     uint16_t len = element.msg_len;
56     len = (len <= 1) ? 0 : strnlen(msg, len - 1);
57     if (!len) {
58         return "<NULL>";
59     }
60     return std::string(msg, len);
61 }
62 
LogStatistics(bool enable_statistics,bool track_total_size,std::optional<log_time> start_time)63 LogStatistics::LogStatistics(bool enable_statistics, bool track_total_size,
64                              std::optional<log_time> start_time)
65     : enable(enable_statistics), track_total_size_(track_total_size) {
66     log_time now(CLOCK_REALTIME);
67     log_id_for_each(id) {
68         mSizes[id] = 0;
69         mElements[id] = 0;
70         mSizesTotal[id] = 0;
71         mElementsTotal[id] = 0;
72         if (start_time) {
73             mOldest[id] = *start_time;
74             mNewest[id] = *start_time;
75         } else {
76             mOldest[id] = now;
77             mNewest[id] = now;
78         }
79     }
80 }
81 
82 namespace android {
83 
sizesTotal()84 size_t sizesTotal() {
85     return LogStatistics::sizesTotal();
86 }
87 
88 // caller must own and free character string
pidToName(pid_t pid)89 char* pidToName(pid_t pid) {
90     char* retval = nullptr;
91     if (pid == 0) {  // special case from auditd/klogd for kernel
92         retval = strdup("logd");
93     } else {
94         char buffer[512];
95         snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
96         int fd = open(buffer, O_RDONLY | O_CLOEXEC);
97         if (fd >= 0) {
98             ssize_t ret = read(fd, buffer, sizeof(buffer));
99             if (ret > 0) {
100                 buffer[sizeof(buffer) - 1] = '\0';
101                 // frameworks intermediate state
102                 if (fastcmp<strcmp>(buffer, "<pre-initialized>")) {
103                     retval = strdup(buffer);
104                 }
105             }
106             close(fd);
107         }
108     }
109     return retval;
110 }
111 }
112 
AddTotal(log_id_t log_id,uint16_t size)113 void LogStatistics::AddTotal(log_id_t log_id, uint16_t size) {
114     auto lock = std::lock_guard{lock_};
115 
116     mSizesTotal[log_id] += size;
117     SizesTotal += size;
118     ++mElementsTotal[log_id];
119 }
120 
Add(LogStatisticsElement element)121 void LogStatistics::Add(LogStatisticsElement element) {
122     auto lock = std::lock_guard{lock_};
123 
124     if (!track_total_size_) {
125         element.total_len = element.msg_len;
126     }
127 
128     log_id_t log_id = element.log_id;
129     uint16_t size = element.total_len;
130     mSizes[log_id] += size;
131     ++mElements[log_id];
132 
133     mSizesTotal[log_id] += size;
134     SizesTotal += size;
135     ++mElementsTotal[log_id];
136 
137     log_time stamp(element.realtime);
138     if (mNewest[log_id] < stamp) {
139         // A major time update invalidates the statistics :-(
140         log_time diff = stamp - mNewest[log_id];
141         mNewest[log_id] = stamp;
142 
143         if (diff.tv_sec > hourSec) {
144             // approximate Do-Your-Best fixup
145             diff += mOldest[log_id];
146             if ((diff > stamp) && ((diff - stamp).tv_sec < hourSec)) {
147                 diff = stamp;
148             }
149             if (diff <= stamp) {
150                 mOldest[log_id] = diff;
151             }
152         }
153     }
154 
155     if (log_id == LOG_ID_KERNEL) {
156         return;
157     }
158 
159     uidTable[log_id].Add(element.uid, element);
160     if (element.uid == AID_SYSTEM) {
161         pidSystemTable[log_id].Add(element.pid, element);
162     }
163 
164     if (!enable) {
165         return;
166     }
167 
168     pidTable.Add(element.pid, element);
169     tidTable.Add(element.tid, element);
170 
171     uint32_t tag = element.tag;
172     if (tag) {
173         if (log_id == LOG_ID_SECURITY) {
174             securityTagTable.Add(tag, element);
175         } else {
176             tagTable.Add(tag, element);
177         }
178     }
179 
180     tagNameTable.Add(TagNameKey(element), element);
181 }
182 
Subtract(LogStatisticsElement element)183 void LogStatistics::Subtract(LogStatisticsElement element) {
184     auto lock = std::lock_guard{lock_};
185 
186     if (!track_total_size_) {
187         element.total_len = element.msg_len;
188     }
189 
190     log_id_t log_id = element.log_id;
191     uint16_t size = element.total_len;
192     mSizes[log_id] -= size;
193     --mElements[log_id];
194 
195     if (mOldest[log_id] < element.realtime) {
196         mOldest[log_id] = element.realtime;
197     }
198 
199     if (log_id == LOG_ID_KERNEL) {
200         return;
201     }
202 
203     uidTable[log_id].Subtract(element.uid, element);
204     if (element.uid == AID_SYSTEM) {
205         pidSystemTable[log_id].Subtract(element.pid, element);
206     }
207 
208     if (!enable) {
209         return;
210     }
211 
212     pidTable.Subtract(element.pid, element);
213     tidTable.Subtract(element.tid, element);
214 
215     uint32_t tag = element.tag;
216     if (tag) {
217         if (log_id == LOG_ID_SECURITY) {
218             securityTagTable.Subtract(tag, element);
219         } else {
220             tagTable.Subtract(tag, element);
221         }
222     }
223 
224     tagNameTable.Subtract(TagNameKey(element), element);
225 }
226 
UidToName(uid_t uid) const227 const char* LogStatistics::UidToName(uid_t uid) const {
228     auto lock = std::lock_guard{lock_};
229     return UidToNameLocked(uid);
230 }
231 
232 // caller must own and free character string
UidToNameLocked(uid_t uid) const233 const char* LogStatistics::UidToNameLocked(uid_t uid) const {
234     // Local hard coded favourites
235     if (uid == AID_LOGD) {
236         return strdup("auditd");
237     }
238 
239     // Android system
240     if (uid < AID_APP) {
241         // in bionic, thread safe as long as we copy the results
242         struct passwd* pwd = getpwuid(uid);
243         if (pwd) {
244             return strdup(pwd->pw_name);
245         }
246     }
247 
248     // Parse /data/system/packages.list
249     uid_t userId = uid % AID_USER_OFFSET;
250     const char* name = android::uidToName(userId);
251     if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
252         name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
253     }
254     if (name) {
255         return name;
256     }
257 
258     // Android application
259     if (uid >= AID_APP) {
260         struct passwd* pwd = getpwuid(uid);
261         if (pwd) {
262             return strdup(pwd->pw_name);
263         }
264     }
265 
266     // report uid -> pid(s) -> pidToName if unique
267     for (pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end();
268          ++it) {
269         const PidEntry& entry = it->second;
270 
271         if (entry.uid() == uid) {
272             const char* nameTmp = entry.name();
273 
274             if (nameTmp) {
275                 if (!name) {
276                     name = strdup(nameTmp);
277                 } else if (fastcmp<strcmp>(name, nameTmp)) {
278                     free(const_cast<char*>(name));
279                     name = nullptr;
280                     break;
281                 }
282             }
283         }
284     }
285 
286     // No one
287     return name;
288 }
289 
290 // Prune at most 10% of the log entries or maxPrune, whichever is less.
ShouldPrune(log_id id,unsigned long max_size,unsigned long * prune_rows) const291 bool LogStatistics::ShouldPrune(log_id id, unsigned long max_size,
292                                 unsigned long* prune_rows) const {
293     static constexpr size_t kMinPrune = 4;
294     static constexpr size_t kMaxPrune = 256;
295 
296     auto lock = std::lock_guard{lock_};
297     size_t sizes = mSizes[id];
298     if (sizes <= max_size) {
299         return false;
300     }
301     size_t size_over = sizes - ((max_size * 9) / 10);
302     size_t elements = mElements[id];
303     size_t min_elements = elements / 100;
304     if (min_elements < kMinPrune) {
305         min_elements = kMinPrune;
306     }
307     *prune_rows = elements * size_over / sizes;
308     if (*prune_rows < min_elements) {
309         *prune_rows = min_elements;
310     }
311     if (*prune_rows > kMaxPrune) {
312         *prune_rows = kMaxPrune;
313     }
314 
315     return true;
316 }
317 
formatHeader(const std::string & name,log_id_t id) const318 std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
319     bool isprune = worstUidEnabledForLogid(id);
320     return formatLine(android::base::StringPrintf(name.c_str(),
321                                                   android_log_id_to_name(id)),
322                       std::string("Size"),
323                       std::string(isprune ? "+/-  Pruned" : "")) +
324            formatLine(std::string("UID   PACKAGE"), std::string("BYTES"),
325                       std::string(isprune ? "NUM" : ""));
326 }
327 
328 // Helper to truncate name, if too long, and add name dressings
FormatTmp(const char * nameTmp,uid_t uid,std::string & name,std::string & size,size_t nameLen) const329 void LogStatistics::FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
330                               size_t nameLen) const {
331     const char* allocNameTmp = nullptr;
332     if (!nameTmp) nameTmp = allocNameTmp = UidToNameLocked(uid);
333     if (nameTmp) {
334         size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
335         size_t len = EntryBase::TOTAL_LEN - EntryBase::PRUNED_LEN - size.length() - name.length() -
336                      lenSpace - 2;
337         size_t lenNameTmp = strlen(nameTmp);
338         while ((len < lenNameTmp) && (lenSpace > 1)) {
339             ++len;
340             --lenSpace;
341         }
342         name += android::base::StringPrintf("%*s", (int)lenSpace, "");
343         if (len < lenNameTmp) {
344             name += "...";
345             nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
346         }
347         name += nameTmp;
348         free(const_cast<char*>(allocNameTmp));
349     }
350 }
351 
format(const LogStatistics & stat,log_id_t id,uid_t uid) const352 std::string UidEntry::format(const LogStatistics& stat, log_id_t id, uid_t uid) const
353         REQUIRES(stat.lock_) {
354     std::string name = android::base::StringPrintf("%u", uid);
355     std::string size = android::base::StringPrintf("%zu", getSizes());
356 
357     stat.FormatTmp(nullptr, uid, name, size, 6);
358 
359     std::string pruned = "";
360     std::string output = formatLine(name, size, pruned);
361 
362     if (uid != AID_SYSTEM) {
363         return output;
364     }
365 
366     static const size_t maximum_sorted_entries = 32;
367     std::array<const pid_t*, maximum_sorted_entries> sorted_pids;
368     std::array<const PidEntry*, maximum_sorted_entries> sorted_entries;
369     stat.pidSystemTable[id].MaxEntries(uid, 0, sorted_pids, sorted_entries);
370 
371     std::string byPid;
372     size_t index;
373     for (index = 0; index < maximum_sorted_entries; ++index) {
374         const PidEntry* entry = sorted_entries[index];
375         if (!entry) {
376             break;
377         }
378         if (entry->getSizes() <= (getSizes() / 100)) {
379             break;
380         }
381         byPid += entry->format(stat, id, *sorted_pids[index]);
382     }
383     if (index > 1) {  // print this only if interesting
384         std::string ditto("\" ");
385         output += formatLine(std::string("  PID/UID   COMMAND LINE"), ditto, std::string(""));
386         output += byPid;
387     }
388 
389     return output;
390 }
391 
formatHeader(const std::string & name,log_id_t) const392 std::string PidEntry::formatHeader(const std::string& name,
393                                    log_id_t /* id */) const {
394     return formatLine(name, std::string("Size"), std::string("Pruned")) +
395            formatLine(std::string("  PID/UID   COMMAND LINE"),
396                       std::string("BYTES"), std::string("NUM"));
397 }
398 
format(const LogStatistics & stat,log_id_t,pid_t pid) const399 std::string PidEntry::format(const LogStatistics& stat, log_id_t, pid_t pid) const
400         REQUIRES(stat.lock_) {
401     std::string name = android::base::StringPrintf("%5u/%u", pid, uid_);
402     std::string size = android::base::StringPrintf("%zu", getSizes());
403 
404     stat.FormatTmp(name_, uid_, name, size, 12);
405 
406     std::string pruned = "";
407     return formatLine(name, size, pruned);
408 }
409 
formatHeader(const std::string & name,log_id_t) const410 std::string TidEntry::formatHeader(const std::string& name,
411                                    log_id_t /* id */) const {
412     return formatLine(name, std::string("Size"), std::string("Pruned")) +
413            formatLine(std::string("  TID/UID   COMM"), std::string("BYTES"),
414                       std::string("NUM"));
415 }
416 
format(const LogStatistics & stat,log_id_t,pid_t tid) const417 std::string TidEntry::format(const LogStatistics& stat, log_id_t, pid_t tid) const
418         REQUIRES(stat.lock_) {
419     std::string name = android::base::StringPrintf("%5u/%u", tid, uid_);
420     std::string size = android::base::StringPrintf("%zu", getSizes());
421 
422     stat.FormatTmp(name_, uid_, name, size, 12);
423 
424     std::string pruned = "";
425     return formatLine(name, size, pruned);
426 }
427 
formatHeader(const std::string & name,log_id_t id) const428 std::string TagEntry::formatHeader(const std::string& name, log_id_t id) const {
429     bool isprune = worstUidEnabledForLogid(id);
430     return formatLine(name, std::string("Size"),
431                       std::string(isprune ? "Prune" : "")) +
432            formatLine(std::string("    TAG/UID   TAGNAME"),
433                       std::string("BYTES"), std::string(isprune ? "NUM" : ""));
434 }
435 
format(const LogStatistics &,log_id_t,uint32_t) const436 std::string TagEntry::format(const LogStatistics&, log_id_t, uint32_t) const {
437     std::string name;
438     if (uid_ == (uid_t)-1) {
439         name = android::base::StringPrintf("%7u", key());
440     } else {
441         name = android::base::StringPrintf("%7u/%u", key(), uid_);
442     }
443     const char* nameTmp = this->name();
444     if (nameTmp) {
445         name += android::base::StringPrintf(
446             "%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", nameTmp);
447     }
448 
449     std::string size = android::base::StringPrintf("%zu", getSizes());
450 
451     std::string pruned = "";
452     return formatLine(name, size, pruned);
453 }
454 
formatHeader(const std::string & name,log_id_t) const455 std::string TagNameEntry::formatHeader(const std::string& name,
456                                        log_id_t /* id */) const {
457     return formatLine(name, std::string("Size"), std::string("")) +
458            formatLine(std::string("  TID/PID/UID   LOG_TAG NAME"),
459                       std::string("BYTES"), std::string(""));
460 }
461 
format(const LogStatistics &,log_id_t,const std::string & key_name) const462 std::string TagNameEntry::format(const LogStatistics&, log_id_t,
463                                  const std::string& key_name) const {
464     std::string name;
465     std::string pidstr;
466     if (pid_ != (pid_t)-1) {
467         pidstr = android::base::StringPrintf("%u", pid_);
468         if (tid_ != (pid_t)-1 && tid_ != pid_) pidstr = "/" + pidstr;
469     }
470     int len = 9 - pidstr.length();
471     if (len < 0) len = 0;
472     if (tid_ == (pid_t)-1 || tid_ == pid_) {
473         name = android::base::StringPrintf("%*s", len, "");
474     } else {
475         name = android::base::StringPrintf("%*u", len, tid_);
476     }
477     name += pidstr;
478     if (uid_ != (uid_t)-1) {
479         name += android::base::StringPrintf("/%u", uid_);
480     }
481 
482     std::string size = android::base::StringPrintf("%zu", getSizes());
483 
484     const char* nameTmp = key_name.data();
485     if (nameTmp) {
486         size_t lenSpace = std::max(16 - name.length(), (size_t)1);
487         size_t len = EntryBase::TOTAL_LEN - EntryBase::PRUNED_LEN - size.length() - name.length() -
488                      lenSpace - 2;
489         size_t lenNameTmp = strlen(nameTmp);
490         while ((len < lenNameTmp) && (lenSpace > 1)) {
491             ++len;
492             --lenSpace;
493         }
494         name += android::base::StringPrintf("%*s", (int)lenSpace, "");
495         if (len < lenNameTmp) {
496             name += "...";
497             nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
498         }
499         name += nameTmp;
500     }
501 
502     std::string pruned = "";
503 
504     return formatLine(name, size, pruned);
505 }
506 
formatMsec(uint64_t val)507 static std::string formatMsec(uint64_t val) {
508     static const unsigned subsecDigits = 3;
509     static const uint64_t sec = MS_PER_SEC;
510 
511     static const uint64_t minute = 60 * sec;
512     static const uint64_t hour = 60 * minute;
513     static const uint64_t day = 24 * hour;
514 
515     std::string output;
516     if (val < sec) return output;
517 
518     if (val >= day) {
519         output = android::base::StringPrintf("%" PRIu64 "d ", val / day);
520         val = (val % day) + day;
521     }
522     if (val >= minute) {
523         if (val >= hour) {
524             output += android::base::StringPrintf("%" PRIu64 ":",
525                                                   (val / hour) % (day / hour));
526         }
527         output += android::base::StringPrintf(
528             (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
529             (val / minute) % (hour / minute));
530     }
531     output +=
532         android::base::StringPrintf((val >= minute) ? "%02" PRIu64 : "%" PRIu64,
533                                     (val / sec) % (minute / sec));
534     val %= sec;
535     unsigned digits = subsecDigits;
536     while (digits && ((val % 10) == 0)) {
537         val /= 10;
538         --digits;
539     }
540     if (digits) {
541         output += android::base::StringPrintf(".%0*" PRIu64, digits, val);
542     }
543     return output;
544 }
545 
546 template <typename TKey, typename TEntry>
FormatTable(const LogHashtable<TKey,TEntry> & table,uid_t uid,pid_t pid,const std::string & name,log_id_t id) const547 std::string LogStatistics::FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid,
548                                        pid_t pid, const std::string& name, log_id_t id) const
549         REQUIRES(lock_) {
550     static const size_t maximum_sorted_entries = 32;
551     std::string output;
552     std::array<const TKey*, maximum_sorted_entries> sorted_keys;
553     std::array<const TEntry*, maximum_sorted_entries> sorted_entries;
554     table.MaxEntries(uid, pid, sorted_keys, sorted_entries);
555     bool header_printed = false;
556     for (size_t index = 0; index < maximum_sorted_entries; ++index) {
557         const TEntry* entry = sorted_entries[index];
558         if (!entry) {
559             break;
560         }
561         if (entry->getSizes() <= (sorted_entries[0]->getSizes() / 100)) {
562             break;
563         }
564         if (!header_printed) {
565             output += "\n\n";
566             output += entry->formatHeader(name, id);
567             header_printed = true;
568         }
569         output += entry->format(*this, id, *sorted_keys[index]);
570     }
571     return output;
572 }
573 
ReportInteresting() const574 std::string LogStatistics::ReportInteresting() const {
575     auto lock = std::lock_guard{lock_};
576 
577     std::vector<std::string> items;
578 
579     log_id_for_each(i) { items.emplace_back(std::to_string(mElements[i])); }
580 
581     log_id_for_each(i) { items.emplace_back(std::to_string(mSizes[i])); }
582 
583     log_id_for_each(i) {
584         items.emplace_back(std::to_string(overhead_[i] ? *overhead_[i] : mSizes[i]));
585     }
586 
587     log_id_for_each(i) {
588         uint64_t oldest = mOldest[i].msec() / 1000;
589         uint64_t newest = mNewest[i].msec() / 1000;
590 
591         int span = newest - oldest;
592 
593         items.emplace_back(std::to_string(span));
594     }
595 
596     return android::base::Join(items, ",");
597 }
598 
Format(uid_t uid,pid_t pid,unsigned int logMask) const599 std::string LogStatistics::Format(uid_t uid, pid_t pid, unsigned int logMask) const {
600     auto lock = std::lock_guard{lock_};
601 
602     static const uint16_t spaces_total = 19;
603 
604     // Report on total logging, current and for all time
605 
606     std::string output = "size/num";
607     size_t oldLength;
608     int16_t spaces = 1;
609 
610     log_id_for_each(id) {
611         if (!(logMask & (1 << id))) continue;
612         oldLength = output.length();
613         if (spaces < 0) spaces = 0;
614         output += android::base::StringPrintf("%*s%s", spaces, "",
615                                               android_log_id_to_name(id));
616         spaces += spaces_total + oldLength - output.length();
617     }
618     if (spaces < 0) spaces = 0;
619     output += android::base::StringPrintf("%*sTotal", spaces, "");
620 
621     static const char TotalStr[] = "\nTotal";
622     spaces = 10 - strlen(TotalStr);
623     output += TotalStr;
624 
625     size_t totalSize = 0;
626     size_t totalEls = 0;
627     log_id_for_each(id) {
628         if (!(logMask & (1 << id))) continue;
629         oldLength = output.length();
630         if (spaces < 0) spaces = 0;
631         size_t szs = mSizesTotal[id];
632         totalSize += szs;
633         size_t els = mElementsTotal[id];
634         totalEls += els;
635         output +=
636             android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
637         spaces += spaces_total + oldLength - output.length();
638     }
639     if (spaces < 0) spaces = 0;
640     output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
641                                           totalEls);
642 
643     static const char NowStr[] = "\nNow";
644     spaces = 10 - strlen(NowStr);
645     output += NowStr;
646 
647     totalSize = 0;
648     totalEls = 0;
649     log_id_for_each(id) {
650         if (!(logMask & (1 << id))) continue;
651 
652         size_t els = mElements[id];
653         oldLength = output.length();
654         if (spaces < 0) spaces = 0;
655         size_t szs = mSizes[id];
656         totalSize += szs;
657         totalEls += els;
658         output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
659         spaces -= output.length() - oldLength;
660         spaces += spaces_total;
661     }
662     if (spaces < 0) spaces = 0;
663     output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
664                                           totalEls);
665 
666     static const char SpanStr[] = "\nLogspan";
667     spaces = 10 - strlen(SpanStr);
668     output += SpanStr;
669 
670     // Total reports the greater of the individual maximum time span, or the
671     // validated minimum start and maximum end time span if it makes sense.
672     uint64_t minTime = UINT64_MAX;
673     uint64_t maxTime = 0;
674     uint64_t maxSpan = 0;
675     totalSize = 0;
676 
677     log_id_for_each(id) {
678         if (!(logMask & (1 << id))) continue;
679 
680         // validity checking
681         uint64_t oldest = mOldest[id].msec();
682         uint64_t newest = mNewest[id].msec();
683         if (newest <= oldest) {
684             spaces += spaces_total;
685             continue;
686         }
687 
688         uint64_t span = newest - oldest;
689         if (span > (monthSec * MS_PER_SEC)) {
690             spaces += spaces_total;
691             continue;
692         }
693 
694         // total span
695         if (minTime > oldest) minTime = oldest;
696         if (maxTime < newest) maxTime = newest;
697         if (span > maxSpan) maxSpan = span;
698         totalSize += span;
699 
700         oldLength = output.length();
701         output += android::base::StringPrintf("%*s%s", spaces, "", formatMsec(span).c_str());
702         spaces -= output.length() - oldLength;
703         spaces += spaces_total;
704     }
705     if ((maxTime > minTime) && ((maxTime -= minTime) < totalSize) &&
706         (maxTime > maxSpan)) {
707         maxSpan = maxTime;
708     }
709     if (spaces < 0) spaces = 0;
710     output += android::base::StringPrintf("%*s%s", spaces, "",
711                                           formatMsec(maxSpan).c_str());
712 
713     static const char OverheadStr[] = "\nOverhead";
714     spaces = 10 - strlen(OverheadStr);
715     output += OverheadStr;
716 
717     totalSize = 0;
718     log_id_for_each(id) {
719         if (!(logMask & (1 << id))) continue;
720 
721         size_t els = mElements[id];
722         if (els) {
723             oldLength = output.length();
724             if (spaces < 0) spaces = 0;
725             size_t szs = 0;
726             if (overhead_[id]) {
727                 szs = *overhead_[id];
728             } else {
729                 CHECK(track_total_size_);
730                 szs = mSizes[id];
731             }
732             totalSize += szs;
733             output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
734             spaces -= output.length() - oldLength;
735         }
736         spaces += spaces_total;
737     }
738     totalSize += sizeOf();
739     if (spaces < 0) spaces = 0;
740     output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize);
741 
742     // Report on Chattiest
743 
744     std::string name;
745 
746     // Chattiest by application (UID)
747     log_id_for_each(id) {
748         if (!(logMask & (1 << id))) continue;
749 
750         name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
751                                  : "Logging for your UID in %s log buffer:";
752         output += FormatTable(uidTable[id], uid, pid, name, id);
753     }
754 
755     if (enable) {
756         name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
757                                            : "Logging for this PID:";
758         output += FormatTable(pidTable, uid, pid, name);
759         name = "Chattiest TIDs";
760         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
761         name += ":";
762         output += FormatTable(tidTable, uid, pid, name);
763     }
764 
765     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
766         name = "Chattiest events log buffer TAGs";
767         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
768         name += ":";
769         output += FormatTable(tagTable, uid, pid, name, LOG_ID_EVENTS);
770     }
771 
772     if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
773         name = "Chattiest security log buffer TAGs";
774         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
775         name += ":";
776         output += FormatTable(securityTagTable, uid, pid, name, LOG_ID_SECURITY);
777     }
778 
779     if (enable) {
780         name = "Chattiest TAGs";
781         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
782         name += ":";
783         output += FormatTable(tagNameTable, uid, pid, name);
784     }
785 
786     return output;
787 }
788 
789 namespace android {
790 
pidToUid(pid_t pid)791 uid_t pidToUid(pid_t pid) {
792     char buffer[512];
793     snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
794     FILE* fp = fopen(buffer, "re");
795     if (fp) {
796         while (fgets(buffer, sizeof(buffer), fp)) {
797             int uid = AID_LOGD;
798             char space = 0;
799             if ((sscanf(buffer, "Uid: %d%c", &uid, &space) == 2) &&
800                 isspace(space)) {
801                 fclose(fp);
802                 return uid;
803             }
804         }
805         fclose(fp);
806     }
807     return AID_LOGD;  // associate this with the logger
808 }
809 }
810 
PidToUid(pid_t pid)811 uid_t LogStatistics::PidToUid(pid_t pid) {
812     auto lock = std::lock_guard{lock_};
813     return pidTable.Add(pid)->second.uid();
814 }
815 
816 // caller must free character string
PidToName(pid_t pid) const817 const char* LogStatistics::PidToName(pid_t pid) const {
818     auto lock = std::lock_guard{lock_};
819     // An inconvenient truth ... getName() can alter the object
820     pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
821     const char* name = writablePidTable.Add(pid)->second.name();
822     if (!name) {
823         return nullptr;
824     }
825     return strdup(name);
826 }
827