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