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