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 <fcntl.h>
18#include <pwd.h>
19#include <stdio.h>
20#include <string.h>
21#include <sys/types.h>
22#include <unistd.h>
23
24#include <list>
25
26#include <android/log.h>
27
28#include "LogStatistics.h"
29
30size_t LogStatistics::SizesTotal;
31
32LogStatistics::LogStatistics() : enable(false) {
33    log_id_for_each(id) {
34        mSizes[id] = 0;
35        mElements[id] = 0;
36        mDroppedElements[id] = 0;
37        mSizesTotal[id] = 0;
38        mElementsTotal[id] = 0;
39    }
40}
41
42namespace android {
43
44size_t sizesTotal() {
45    return LogStatistics::sizesTotal();
46}
47
48// caller must own and free character string
49char* pidToName(pid_t pid) {
50    char* retval = NULL;
51    if (pid == 0) {  // special case from auditd/klogd for kernel
52        retval = strdup("logd");
53    } else {
54        char buffer[512];
55        snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
56        int fd = open(buffer, O_RDONLY);
57        if (fd >= 0) {
58            ssize_t ret = read(fd, buffer, sizeof(buffer));
59            if (ret > 0) {
60                buffer[sizeof(buffer) - 1] = '\0';
61                // frameworks intermediate state
62                if (fastcmp<strcmp>(buffer, "<pre-initialized>")) {
63                    retval = strdup(buffer);
64                }
65            }
66            close(fd);
67        }
68    }
69    return retval;
70}
71}
72
73void LogStatistics::add(LogBufferElement* element) {
74    log_id_t log_id = element->getLogId();
75    unsigned short size = element->getMsgLen();
76    mSizes[log_id] += size;
77    ++mElements[log_id];
78
79    if (element->getDropped()) {
80        ++mDroppedElements[log_id];
81    } else {
82        // When caller adding a chatty entry, they will have already
83        // called add() and subtract() for each entry as they are
84        // evaluated and trimmed, thus recording size and number of
85        // elements, but we must recognize the manufactured dropped
86        // entry as not contributing to the lifetime totals.
87        mSizesTotal[log_id] += size;
88        SizesTotal += size;
89        ++mElementsTotal[log_id];
90    }
91
92    if (log_id == LOG_ID_KERNEL) {
93        return;
94    }
95
96    uidTable[log_id].add(element->getUid(), element);
97    if (element->getUid() == AID_SYSTEM) {
98        pidSystemTable[log_id].add(element->getPid(), element);
99    }
100
101    if (!enable) {
102        return;
103    }
104
105    pidTable.add(element->getPid(), element);
106    tidTable.add(element->getTid(), element);
107
108    uint32_t tag = element->getTag();
109    if (tag) {
110        if (log_id == LOG_ID_SECURITY) {
111            securityTagTable.add(tag, element);
112        } else {
113            tagTable.add(tag, element);
114        }
115    }
116}
117
118void LogStatistics::subtract(LogBufferElement* element) {
119    log_id_t log_id = element->getLogId();
120    unsigned short size = element->getMsgLen();
121    mSizes[log_id] -= size;
122    --mElements[log_id];
123    if (element->getDropped()) {
124        --mDroppedElements[log_id];
125    }
126
127    if (log_id == LOG_ID_KERNEL) {
128        return;
129    }
130
131    uidTable[log_id].subtract(element->getUid(), element);
132    if (element->getUid() == AID_SYSTEM) {
133        pidSystemTable[log_id].subtract(element->getPid(), element);
134    }
135
136    if (!enable) {
137        return;
138    }
139
140    pidTable.subtract(element->getPid(), element);
141    tidTable.subtract(element->getTid(), element);
142
143    uint32_t tag = element->getTag();
144    if (tag) {
145        if (log_id == LOG_ID_SECURITY) {
146            securityTagTable.subtract(tag, element);
147        } else {
148            tagTable.subtract(tag, element);
149        }
150    }
151}
152
153// Atomically set an entry to drop
154// entry->setDropped(1) must follow this call, caller should do this explicitly.
155void LogStatistics::drop(LogBufferElement* element) {
156    log_id_t log_id = element->getLogId();
157    unsigned short size = element->getMsgLen();
158    mSizes[log_id] -= size;
159    ++mDroppedElements[log_id];
160
161    uidTable[log_id].drop(element->getUid(), element);
162    if (element->getUid() == AID_SYSTEM) {
163        pidSystemTable[log_id].drop(element->getPid(), element);
164    }
165
166    if (!enable) {
167        return;
168    }
169
170    pidTable.drop(element->getPid(), element);
171    tidTable.drop(element->getTid(), element);
172
173    uint32_t tag = element->getTag();
174    if (tag) {
175        if (log_id == LOG_ID_SECURITY) {
176            securityTagTable.drop(tag, element);
177        } else {
178            tagTable.drop(tag, element);
179        }
180    }
181}
182
183// caller must own and free character string
184const char* LogStatistics::uidToName(uid_t uid) const {
185    // Local hard coded favourites
186    if (uid == AID_LOGD) {
187        return strdup("auditd");
188    }
189
190    // Android system
191    if (uid < AID_APP) {
192        // in bionic, thread safe as long as we copy the results
193        struct passwd* pwd = getpwuid(uid);
194        if (pwd) {
195            return strdup(pwd->pw_name);
196        }
197    }
198
199    // Parse /data/system/packages.list
200    uid_t userId = uid % AID_USER_OFFSET;
201    const char* name = android::uidToName(userId);
202    if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
203        name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
204    }
205    if (name) {
206        return name;
207    }
208
209    // Android application
210    if (uid >= AID_APP) {
211        struct passwd* pwd = getpwuid(uid);
212        if (pwd) {
213            return strdup(pwd->pw_name);
214        }
215    }
216
217    // report uid -> pid(s) -> pidToName if unique
218    for (pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end();
219         ++it) {
220        const PidEntry& entry = it->second;
221
222        if (entry.getUid() == uid) {
223            const char* nameTmp = entry.getName();
224
225            if (nameTmp) {
226                if (!name) {
227                    name = strdup(nameTmp);
228                } else if (fastcmp<strcmp>(name, nameTmp)) {
229                    free(const_cast<char*>(name));
230                    name = NULL;
231                    break;
232                }
233            }
234        }
235    }
236
237    // No one
238    return name;
239}
240
241std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
242    bool isprune = worstUidEnabledForLogid(id);
243    return formatLine(android::base::StringPrintf(name.c_str(),
244                                                  android_log_id_to_name(id)),
245                      std::string("Size"),
246                      std::string(isprune ? "+/-  Pruned" : "")) +
247           formatLine(std::string("UID   PACKAGE"), std::string("BYTES"),
248                      std::string(isprune ? "NUM" : ""));
249}
250
251std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
252    uid_t uid = getUid();
253    std::string name = android::base::StringPrintf("%u", uid);
254    const char* nameTmp = stat.uidToName(uid);
255    if (nameTmp) {
256        name += android::base::StringPrintf(
257            "%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", nameTmp);
258        free(const_cast<char*>(nameTmp));
259    }
260
261    std::string size = android::base::StringPrintf("%zu", getSizes());
262
263    std::string pruned = "";
264    if (worstUidEnabledForLogid(id)) {
265        size_t totalDropped = 0;
266        for (LogStatistics::uidTable_t::const_iterator it =
267                 stat.uidTable[id].begin();
268             it != stat.uidTable[id].end(); ++it) {
269            totalDropped += it->second.getDropped();
270        }
271        size_t sizes = stat.sizes(id);
272        size_t totalSize = stat.sizesTotal(id);
273        size_t totalElements = stat.elementsTotal(id);
274        float totalVirtualSize =
275            (float)sizes + (float)totalDropped * totalSize / totalElements;
276        size_t entrySize = getSizes();
277        float virtualEntrySize = entrySize;
278        int realPermille = virtualEntrySize * 1000.0 / sizes;
279        size_t dropped = getDropped();
280        if (dropped) {
281            pruned = android::base::StringPrintf("%zu", dropped);
282            virtualEntrySize += (float)dropped * totalSize / totalElements;
283        }
284        int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
285        int permille =
286            (realPermille - virtualPermille) * 1000L / (virtualPermille ?: 1);
287        if ((permille < -1) || (1 < permille)) {
288            std::string change;
289            const char* units = "%";
290            const char* prefix = (permille > 0) ? "+" : "";
291
292            if (permille > 999) {
293                permille = (permille + 1000) / 100;  // Now tenths fold
294                units = "X";
295                prefix = "";
296            }
297            if ((-99 < permille) && (permille < 99)) {
298                change = android::base::StringPrintf(
299                    "%s%d.%u%s", prefix, permille / 10,
300                    ((permille < 0) ? (-permille % 10) : (permille % 10)),
301                    units);
302            } else {
303                change = android::base::StringPrintf(
304                    "%s%d%s", prefix, (permille + 5) / 10, units);
305            }
306            ssize_t spaces = EntryBaseConstants::pruned_len - 2 -
307                             pruned.length() - change.length();
308            if ((spaces <= 0) && pruned.length()) {
309                spaces = 1;
310            }
311            if (spaces > 0) {
312                change += android::base::StringPrintf("%*s", (int)spaces, "");
313            }
314            pruned = change + pruned;
315        }
316    }
317
318    std::string output = formatLine(name, size, pruned);
319
320    if (uid != AID_SYSTEM) {
321        return output;
322    }
323
324    static const size_t maximum_sorted_entries = 32;
325    std::unique_ptr<const PidEntry* []> sorted =
326        stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
327
328    if (!sorted.get()) {
329        return output;
330    }
331    std::string byPid;
332    size_t index;
333    bool hasDropped = false;
334    for (index = 0; index < maximum_sorted_entries; ++index) {
335        const PidEntry* entry = sorted[index];
336        if (!entry) {
337            break;
338        }
339        if (entry->getSizes() <= (getSizes() / 100)) {
340            break;
341        }
342        if (entry->getDropped()) {
343            hasDropped = true;
344        }
345        byPid += entry->format(stat, id);
346    }
347    if (index > 1) {  // print this only if interesting
348        std::string ditto("\" ");
349        output += formatLine(std::string("  PID/UID   COMMAND LINE"), ditto,
350                             hasDropped ? ditto : std::string(""));
351        output += byPid;
352    }
353
354    return output;
355}
356
357std::string PidEntry::formatHeader(const std::string& name,
358                                   log_id_t /* id */) const {
359    return formatLine(name, std::string("Size"), std::string("Pruned")) +
360           formatLine(std::string("  PID/UID   COMMAND LINE"),
361                      std::string("BYTES"), std::string("NUM"));
362}
363
364std::string PidEntry::format(const LogStatistics& stat,
365                             log_id_t /* id */) const {
366    uid_t uid = getUid();
367    pid_t pid = getPid();
368    std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
369    const char* nameTmp = getName();
370    if (nameTmp) {
371        name += android::base::StringPrintf(
372            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
373    } else if ((nameTmp = stat.uidToName(uid))) {
374        name += android::base::StringPrintf(
375            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
376        free(const_cast<char*>(nameTmp));
377    }
378
379    std::string size = android::base::StringPrintf("%zu", getSizes());
380
381    std::string pruned = "";
382    size_t dropped = getDropped();
383    if (dropped) {
384        pruned = android::base::StringPrintf("%zu", dropped);
385    }
386
387    return formatLine(name, size, pruned);
388}
389
390std::string TidEntry::formatHeader(const std::string& name,
391                                   log_id_t /* id */) const {
392    return formatLine(name, std::string("Size"), std::string("Pruned")) +
393           formatLine(std::string("  TID/UID   COMM"), std::string("BYTES"),
394                      std::string("NUM"));
395}
396
397std::string TidEntry::format(const LogStatistics& stat,
398                             log_id_t /* id */) const {
399    uid_t uid = getUid();
400    std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
401    const char* nameTmp = getName();
402    if (nameTmp) {
403        name += android::base::StringPrintf(
404            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
405    } else if ((nameTmp = stat.uidToName(uid))) {
406        // if we do not have a PID name, lets punt to try UID name?
407        name += android::base::StringPrintf(
408            "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
409        free(const_cast<char*>(nameTmp));
410        // We tried, better to not have a name at all, we still
411        // have TID/UID by number to report in any case.
412    }
413
414    std::string size = android::base::StringPrintf("%zu", getSizes());
415
416    std::string pruned = "";
417    size_t dropped = getDropped();
418    if (dropped) {
419        pruned = android::base::StringPrintf("%zu", dropped);
420    }
421
422    return formatLine(name, size, pruned);
423}
424
425std::string TagEntry::formatHeader(const std::string& name, log_id_t id) const {
426    bool isprune = worstUidEnabledForLogid(id);
427    return formatLine(name, std::string("Size"),
428                      std::string(isprune ? "Prune" : "")) +
429           formatLine(std::string("    TAG/UID   TAGNAME"),
430                      std::string("BYTES"), std::string(isprune ? "NUM" : ""));
431}
432
433std::string TagEntry::format(const LogStatistics& /* stat */,
434                             log_id_t /* id */) const {
435    std::string name;
436    uid_t uid = getUid();
437    if (uid == (uid_t)-1) {
438        name = android::base::StringPrintf("%7u", getKey());
439    } else {
440        name = android::base::StringPrintf("%7u/%u", getKey(), uid);
441    }
442    const char* nameTmp = getName();
443    if (nameTmp) {
444        name += android::base::StringPrintf(
445            "%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", nameTmp);
446    }
447
448    std::string size = android::base::StringPrintf("%zu", getSizes());
449
450    std::string pruned = "";
451    size_t dropped = getDropped();
452    if (dropped) {
453        pruned = android::base::StringPrintf("%zu", dropped);
454    }
455
456    return formatLine(name, size, pruned);
457}
458
459std::string LogStatistics::format(uid_t uid, pid_t pid,
460                                  unsigned int logMask) const {
461    static const unsigned short spaces_total = 19;
462
463    // Report on total logging, current and for all time
464
465    std::string output = "size/num";
466    size_t oldLength;
467    short spaces = 1;
468
469    log_id_for_each(id) {
470        if (!(logMask & (1 << id))) continue;
471        oldLength = output.length();
472        if (spaces < 0) spaces = 0;
473        output += android::base::StringPrintf("%*s%s", spaces, "",
474                                              android_log_id_to_name(id));
475        spaces += spaces_total + oldLength - output.length();
476    }
477    if (spaces < 0) spaces = 0;
478    output += android::base::StringPrintf("%*sTotal", spaces, "");
479
480    static const char TotalStr[] = "\nTotal";
481    spaces = 10 - strlen(TotalStr);
482    output += TotalStr;
483
484    size_t totalSize = 0;
485    size_t totalEls = 0;
486    log_id_for_each(id) {
487        if (!(logMask & (1 << id))) continue;
488        oldLength = output.length();
489        if (spaces < 0) spaces = 0;
490        size_t szs = sizesTotal(id);
491        totalSize += szs;
492        size_t els = elementsTotal(id);
493        totalEls += els;
494        output +=
495            android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
496        spaces += spaces_total + oldLength - output.length();
497    }
498    if (spaces < 0) spaces = 0;
499    output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
500                                          totalEls);
501
502    static const char NowStr[] = "\nNow";
503    spaces = 10 - strlen(NowStr);
504    output += NowStr;
505
506    totalSize = 0;
507    totalEls = 0;
508    log_id_for_each(id) {
509        if (!(logMask & (1 << id))) continue;
510
511        size_t els = elements(id);
512        if (els) {
513            oldLength = output.length();
514            if (spaces < 0) spaces = 0;
515            size_t szs = sizes(id);
516            totalSize += szs;
517            totalEls += els;
518            output +=
519                android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
520            spaces -= output.length() - oldLength;
521        }
522        spaces += spaces_total;
523    }
524    if (spaces < 0) spaces = 0;
525    output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
526                                          totalEls);
527
528    static const char OverheadStr[] = "\nOverhead";
529    spaces = 10 - strlen(OverheadStr);
530    output += OverheadStr;
531
532    totalSize = 0;
533    log_id_for_each(id) {
534        if (!(logMask & (1 << id))) continue;
535
536        size_t els = elements(id);
537        if (els) {
538            oldLength = output.length();
539            if (spaces < 0) spaces = 0;
540            // estimate the std::list overhead.
541            static const size_t overhead =
542                ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
543                 -sizeof(uint64_t)) +
544                sizeof(std::list<LogBufferElement*>);
545            size_t szs = sizes(id) + els * overhead;
546            totalSize += szs;
547            output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
548            spaces -= output.length() - oldLength;
549        }
550        spaces += spaces_total;
551    }
552    totalSize += sizeOf();
553    if (spaces < 0) spaces = 0;
554    output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize);
555
556    // Report on Chattiest
557
558    std::string name;
559
560    // Chattiest by application (UID)
561    log_id_for_each(id) {
562        if (!(logMask & (1 << id))) continue;
563
564        name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
565                                 : "Logging for your UID in %s log buffer:";
566        output += uidTable[id].format(*this, uid, pid, name, id);
567    }
568
569    if (enable) {
570        name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
571                                           : "Logging for this PID:";
572        output += pidTable.format(*this, uid, pid, name);
573        name = "Chattiest TIDs";
574        if (pid) name += android::base::StringPrintf(" for PID %d", pid);
575        name += ":";
576        output += tidTable.format(*this, uid, pid, name);
577    }
578
579    if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
580        name = "Chattiest events log buffer TAGs";
581        if (pid) name += android::base::StringPrintf(" for PID %d", pid);
582        name += ":";
583        output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
584    }
585
586    if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
587        name = "Chattiest security log buffer TAGs";
588        if (pid) name += android::base::StringPrintf(" for PID %d", pid);
589        name += ":";
590        output +=
591            securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
592    }
593
594    return output;
595}
596
597namespace android {
598
599uid_t pidToUid(pid_t pid) {
600    char buffer[512];
601    snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
602    FILE* fp = fopen(buffer, "r");
603    if (fp) {
604        while (fgets(buffer, sizeof(buffer), fp)) {
605            int uid;
606            if (sscanf(buffer, "Uid: %d", &uid) == 1) {
607                fclose(fp);
608                return uid;
609            }
610        }
611        fclose(fp);
612    }
613    return AID_LOGD;  // associate this with the logger
614}
615}
616
617uid_t LogStatistics::pidToUid(pid_t pid) {
618    return pidTable.add(pid)->second.getUid();
619}
620
621// caller must free character string
622const char* LogStatistics::pidToName(pid_t pid) const {
623    // An inconvenient truth ... getName() can alter the object
624    pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
625    const char* name = writablePidTable.add(pid)->second.getName();
626    if (!name) {
627        return NULL;
628    }
629    return strdup(name);
630}
631