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