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