LogStatistics.cpp revision f31ae3d666f6e723a7dde6734c6d8395f8bcdc11
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
251// Helper to truncate name, if too long, and add name dressings
252static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
253                      std::string& name, std::string& size, size_t nameLen) {
254    const char* allocNameTmp = nullptr;
255    if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
256    if (nameTmp) {
257        size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
258        size_t len = EntryBaseConstants::total_len -
259                     EntryBaseConstants::pruned_len - size.length() -
260                     name.length() - lenSpace - 2;
261        size_t lenNameTmp = strlen(nameTmp);
262        while ((len < lenNameTmp) && (lenSpace > 1)) {
263            ++len;
264            --lenSpace;
265        }
266        name += android::base::StringPrintf("%*s", (int)lenSpace, "");
267        if (len < lenNameTmp) {
268            name += "...";
269            nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
270        }
271        name += nameTmp;
272        free(const_cast<char*>(allocNameTmp));
273    }
274}
275
276std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
277    uid_t uid = getUid();
278    std::string name = android::base::StringPrintf("%u", uid);
279    std::string size = android::base::StringPrintf("%zu", getSizes());
280
281    formatTmp(stat, nullptr, uid, name, size, 6);
282
283    std::string pruned = "";
284    if (worstUidEnabledForLogid(id)) {
285        size_t totalDropped = 0;
286        for (LogStatistics::uidTable_t::const_iterator it =
287                 stat.uidTable[id].begin();
288             it != stat.uidTable[id].end(); ++it) {
289            totalDropped += it->second.getDropped();
290        }
291        size_t sizes = stat.sizes(id);
292        size_t totalSize = stat.sizesTotal(id);
293        size_t totalElements = stat.elementsTotal(id);
294        float totalVirtualSize =
295            (float)sizes + (float)totalDropped * totalSize / totalElements;
296        size_t entrySize = getSizes();
297        float virtualEntrySize = entrySize;
298        int realPermille = virtualEntrySize * 1000.0 / sizes;
299        size_t dropped = getDropped();
300        if (dropped) {
301            pruned = android::base::StringPrintf("%zu", dropped);
302            virtualEntrySize += (float)dropped * totalSize / totalElements;
303        }
304        int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
305        int permille =
306            (realPermille - virtualPermille) * 1000L / (virtualPermille ?: 1);
307        if ((permille < -1) || (1 < permille)) {
308            std::string change;
309            const char* units = "%";
310            const char* prefix = (permille > 0) ? "+" : "";
311
312            if (permille > 999) {
313                permille = (permille + 1000) / 100;  // Now tenths fold
314                units = "X";
315                prefix = "";
316            }
317            if ((-99 < permille) && (permille < 99)) {
318                change = android::base::StringPrintf(
319                    "%s%d.%u%s", prefix, permille / 10,
320                    ((permille < 0) ? (-permille % 10) : (permille % 10)),
321                    units);
322            } else {
323                change = android::base::StringPrintf(
324                    "%s%d%s", prefix, (permille + 5) / 10, units);
325            }
326            ssize_t spaces = EntryBaseConstants::pruned_len - 2 -
327                             pruned.length() - change.length();
328            if ((spaces <= 0) && pruned.length()) {
329                spaces = 1;
330            }
331            if (spaces > 0) {
332                change += android::base::StringPrintf("%*s", (int)spaces, "");
333            }
334            pruned = change + pruned;
335        }
336    }
337
338    std::string output = formatLine(name, size, pruned);
339
340    if (uid != AID_SYSTEM) {
341        return output;
342    }
343
344    static const size_t maximum_sorted_entries = 32;
345    std::unique_ptr<const PidEntry* []> sorted =
346        stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
347
348    if (!sorted.get()) {
349        return output;
350    }
351    std::string byPid;
352    size_t index;
353    bool hasDropped = false;
354    for (index = 0; index < maximum_sorted_entries; ++index) {
355        const PidEntry* entry = sorted[index];
356        if (!entry) {
357            break;
358        }
359        if (entry->getSizes() <= (getSizes() / 100)) {
360            break;
361        }
362        if (entry->getDropped()) {
363            hasDropped = true;
364        }
365        byPid += entry->format(stat, id);
366    }
367    if (index > 1) {  // print this only if interesting
368        std::string ditto("\" ");
369        output += formatLine(std::string("  PID/UID   COMMAND LINE"), ditto,
370                             hasDropped ? ditto : std::string(""));
371        output += byPid;
372    }
373
374    return output;
375}
376
377std::string PidEntry::formatHeader(const std::string& name,
378                                   log_id_t /* id */) const {
379    return formatLine(name, std::string("Size"), std::string("Pruned")) +
380           formatLine(std::string("  PID/UID   COMMAND LINE"),
381                      std::string("BYTES"), std::string("NUM"));
382}
383
384std::string PidEntry::format(const LogStatistics& stat,
385                             log_id_t /* id */) const {
386    uid_t uid = getUid();
387    pid_t pid = getPid();
388    std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
389    std::string size = android::base::StringPrintf("%zu", getSizes());
390
391    formatTmp(stat, getName(), uid, name, size, 12);
392
393    std::string pruned = "";
394    size_t dropped = getDropped();
395    if (dropped) {
396        pruned = android::base::StringPrintf("%zu", dropped);
397    }
398
399    return formatLine(name, size, pruned);
400}
401
402std::string TidEntry::formatHeader(const std::string& name,
403                                   log_id_t /* id */) const {
404    return formatLine(name, std::string("Size"), std::string("Pruned")) +
405           formatLine(std::string("  TID/UID   COMM"), std::string("BYTES"),
406                      std::string("NUM"));
407}
408
409std::string TidEntry::format(const LogStatistics& stat,
410                             log_id_t /* id */) const {
411    uid_t uid = getUid();
412    std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
413    std::string size = android::base::StringPrintf("%zu", getSizes());
414
415    formatTmp(stat, getName(), uid, name, size, 12);
416
417    std::string pruned = "";
418    size_t dropped = getDropped();
419    if (dropped) {
420        pruned = android::base::StringPrintf("%zu", dropped);
421    }
422
423    return formatLine(name, size, pruned);
424}
425
426std::string TagEntry::formatHeader(const std::string& name, log_id_t id) const {
427    bool isprune = worstUidEnabledForLogid(id);
428    return formatLine(name, std::string("Size"),
429                      std::string(isprune ? "Prune" : "")) +
430           formatLine(std::string("    TAG/UID   TAGNAME"),
431                      std::string("BYTES"), std::string(isprune ? "NUM" : ""));
432}
433
434std::string TagEntry::format(const LogStatistics& /* stat */,
435                             log_id_t /* id */) const {
436    std::string name;
437    uid_t uid = getUid();
438    if (uid == (uid_t)-1) {
439        name = android::base::StringPrintf("%7u", getKey());
440    } else {
441        name = android::base::StringPrintf("%7u/%u", getKey(), uid);
442    }
443    const char* nameTmp = getName();
444    if (nameTmp) {
445        name += android::base::StringPrintf(
446            "%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", nameTmp);
447    }
448
449    std::string size = android::base::StringPrintf("%zu", getSizes());
450
451    std::string pruned = "";
452    size_t dropped = getDropped();
453    if (dropped) {
454        pruned = android::base::StringPrintf("%zu", dropped);
455    }
456
457    return formatLine(name, size, pruned);
458}
459
460std::string LogStatistics::format(uid_t uid, pid_t pid,
461                                  unsigned int logMask) const {
462    static const unsigned short spaces_total = 19;
463
464    // Report on total logging, current and for all time
465
466    std::string output = "size/num";
467    size_t oldLength;
468    short spaces = 1;
469
470    log_id_for_each(id) {
471        if (!(logMask & (1 << id))) continue;
472        oldLength = output.length();
473        if (spaces < 0) spaces = 0;
474        output += android::base::StringPrintf("%*s%s", spaces, "",
475                                              android_log_id_to_name(id));
476        spaces += spaces_total + oldLength - output.length();
477    }
478    if (spaces < 0) spaces = 0;
479    output += android::base::StringPrintf("%*sTotal", spaces, "");
480
481    static const char TotalStr[] = "\nTotal";
482    spaces = 10 - strlen(TotalStr);
483    output += TotalStr;
484
485    size_t totalSize = 0;
486    size_t totalEls = 0;
487    log_id_for_each(id) {
488        if (!(logMask & (1 << id))) continue;
489        oldLength = output.length();
490        if (spaces < 0) spaces = 0;
491        size_t szs = sizesTotal(id);
492        totalSize += szs;
493        size_t els = elementsTotal(id);
494        totalEls += els;
495        output +=
496            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,
501                                          totalEls);
502
503    static const char NowStr[] = "\nNow";
504    spaces = 10 - strlen(NowStr);
505    output += NowStr;
506
507    totalSize = 0;
508    totalEls = 0;
509    log_id_for_each(id) {
510        if (!(logMask & (1 << id))) continue;
511
512        size_t els = elements(id);
513        if (els) {
514            oldLength = output.length();
515            if (spaces < 0) spaces = 0;
516            size_t szs = sizes(id);
517            totalSize += szs;
518            totalEls += els;
519            output +=
520                android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
521            spaces -= output.length() - oldLength;
522        }
523        spaces += spaces_total;
524    }
525    if (spaces < 0) spaces = 0;
526    output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
527                                          totalEls);
528
529    static const char OverheadStr[] = "\nOverhead";
530    spaces = 10 - strlen(OverheadStr);
531    output += OverheadStr;
532
533    totalSize = 0;
534    log_id_for_each(id) {
535        if (!(logMask & (1 << id))) continue;
536
537        size_t els = elements(id);
538        if (els) {
539            oldLength = output.length();
540            if (spaces < 0) spaces = 0;
541            // estimate the std::list overhead.
542            static const size_t overhead =
543                ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
544                 -sizeof(uint64_t)) +
545                sizeof(std::list<LogBufferElement*>);
546            size_t szs = sizes(id) + els * overhead;
547            totalSize += szs;
548            output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
549            spaces -= output.length() - oldLength;
550        }
551        spaces += spaces_total;
552    }
553    totalSize += sizeOf();
554    if (spaces < 0) spaces = 0;
555    output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize);
556
557    // Report on Chattiest
558
559    std::string name;
560
561    // Chattiest by application (UID)
562    log_id_for_each(id) {
563        if (!(logMask & (1 << id))) continue;
564
565        name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
566                                 : "Logging for your UID in %s log buffer:";
567        output += uidTable[id].format(*this, uid, pid, name, id);
568    }
569
570    if (enable) {
571        name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
572                                           : "Logging for this PID:";
573        output += pidTable.format(*this, uid, pid, name);
574        name = "Chattiest TIDs";
575        if (pid) name += android::base::StringPrintf(" for PID %d", pid);
576        name += ":";
577        output += tidTable.format(*this, uid, pid, name);
578    }
579
580    if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
581        name = "Chattiest events log buffer TAGs";
582        if (pid) name += android::base::StringPrintf(" for PID %d", pid);
583        name += ":";
584        output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
585    }
586
587    if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
588        name = "Chattiest security log buffer TAGs";
589        if (pid) name += android::base::StringPrintf(" for PID %d", pid);
590        name += ":";
591        output +=
592            securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
593    }
594
595    return output;
596}
597
598namespace android {
599
600uid_t pidToUid(pid_t pid) {
601    char buffer[512];
602    snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
603    FILE* fp = fopen(buffer, "r");
604    if (fp) {
605        while (fgets(buffer, sizeof(buffer), fp)) {
606            int uid;
607            if (sscanf(buffer, "Uid: %d", &uid) == 1) {
608                fclose(fp);
609                return uid;
610            }
611        }
612        fclose(fp);
613    }
614    return AID_LOGD;  // associate this with the logger
615}
616}
617
618uid_t LogStatistics::pidToUid(pid_t pid) {
619    return pidTable.add(pid)->second.getUid();
620}
621
622// caller must free character string
623const char* LogStatistics::pidToName(pid_t pid) const {
624    // An inconvenient truth ... getName() can alter the object
625    pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
626    const char* name = writablePidTable.add(pid)->second.getName();
627    if (!name) {
628        return NULL;
629    }
630    return strdup(name);
631}
632