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