LogStatistics.cpp revision 0adcc3e3e8bd6d7c57994f828ee22c104b4eb043
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
157// caller must own and free character string
158const char *LogStatistics::uidToName(uid_t uid) const {
159    // Local hard coded favourites
160    if (uid == AID_LOGD) {
161        return strdup("auditd");
162    }
163
164    // Android system
165    if (uid < AID_APP) {
166        // in bionic, thread safe as long as we copy the results
167        struct passwd *pwd = getpwuid(uid);
168        if (pwd) {
169            return strdup(pwd->pw_name);
170        }
171    }
172
173    // Parse /data/system/packages.list
174    uid_t userId = uid % AID_USER;
175    const char *name = android::uidToName(userId);
176    if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
177        name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
178    }
179    if (name) {
180        return name;
181    }
182
183    // Android application
184    if (uid >= AID_APP) {
185        struct passwd *pwd = getpwuid(uid);
186        if (pwd) {
187            return strdup(pwd->pw_name);
188        }
189    }
190
191    // report uid -> pid(s) -> pidToName if unique
192    for(pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
193        const PidEntry &entry = it->second;
194
195        if (entry.getUid() == uid) {
196            const char *nameTmp = entry.getName();
197
198            if (nameTmp) {
199                if (!name) {
200                    name = strdup(nameTmp);
201                } else if (fast<strcmp>(name, nameTmp)) {
202                    free(const_cast<char *>(name));
203                    name = NULL;
204                    break;
205                }
206            }
207        }
208    }
209
210    // No one
211    return name;
212}
213
214std::string UidEntry::formatHeader(const std::string &name, log_id_t id) const {
215    bool isprune = worstUidEnabledForLogid(id);
216    return formatLine(android::base::StringPrintf(
217                          name.c_str(), android_log_id_to_name(id)),
218                      std::string("Size"),
219                      std::string(isprune ? "+/-  Pruned" : ""))
220         + formatLine(std::string("UID   PACKAGE"),
221                      std::string("BYTES"),
222                      std::string(isprune ? "NUM" : ""));
223}
224
225std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
226    uid_t uid = getUid();
227    std::string name = android::base::StringPrintf("%u", uid);
228    const char *nameTmp = stat.uidToName(uid);
229    if (nameTmp) {
230        name += android::base::StringPrintf(
231            "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
232            "", nameTmp);
233        free(const_cast<char *>(nameTmp));
234    }
235
236    std::string size = android::base::StringPrintf("%zu", getSizes());
237
238    std::string pruned = "";
239    if (worstUidEnabledForLogid(id)) {
240        size_t totalDropped = 0;
241        for (LogStatistics::uidTable_t::const_iterator it = stat.uidTable[id].begin();
242                it != stat.uidTable[id].end(); ++it) {
243            totalDropped += it->second.getDropped();
244        }
245        size_t sizes = stat.sizes(id);
246        size_t totalSize = stat.sizesTotal(id);
247        size_t totalElements = stat.elementsTotal(id);
248        float totalVirtualSize = (float)sizes + (float)totalDropped * totalSize
249                                / totalElements;
250        size_t entrySize = getSizes();
251        float virtualEntrySize = entrySize;
252        int realPermille = virtualEntrySize * 1000.0 / sizes;
253        size_t dropped = getDropped();
254        if (dropped) {
255            pruned = android::base::StringPrintf("%zu", dropped);
256            virtualEntrySize += (float)dropped * totalSize / totalElements;
257        }
258        int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
259        int permille = (realPermille - virtualPermille) * 1000L
260                     / (virtualPermille ?: 1);
261        if ((permille < -1) || (1 < permille)) {
262            std::string change;
263            const char *units = "%";
264            const char *prefix = (permille > 0) ? "+" : "";
265
266            if (permille > 999) {
267                permille = (permille + 1000) / 100; // Now tenths fold
268                units = "X";
269                prefix = "";
270            }
271            if ((-99 < permille) && (permille < 99)) {
272                change = android::base::StringPrintf("%s%d.%u%s",
273                    prefix,
274                    permille / 10,
275                    ((permille < 0) ? (-permille % 10) : (permille % 10)),
276                    units);
277            } else {
278                change = android::base::StringPrintf("%s%d%s",
279                    prefix,
280                    (permille + 5) / 10, units);
281            }
282            ssize_t spaces = EntryBaseConstants::pruned_len
283                           - 2 - pruned.length() - change.length();
284            if ((spaces <= 0) && pruned.length()) {
285                spaces = 1;
286            }
287            if ((spaces > 0) && (pruned.length() != 0)) {
288                change += android::base::StringPrintf("%*s", (int)spaces, "");
289            }
290            pruned = change + pruned;
291        }
292    }
293
294    std::string output = formatLine(name, size, pruned);
295
296    if (uid != AID_SYSTEM) {
297        return output;
298    }
299
300    static const size_t maximum_sorted_entries = 32;
301    std::unique_ptr<const PidEntry *[]> sorted
302        = stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
303
304    if (!sorted.get()) {
305        return output;
306    }
307    std::string byPid;
308    size_t index;
309    bool hasDropped = false;
310    for (index = 0; index < maximum_sorted_entries; ++index) {
311        const PidEntry *entry = sorted[index];
312        if (!entry) {
313            break;
314        }
315        if (entry->getSizes() <= (getSizes() / 100)) {
316            break;
317        }
318        if (entry->getDropped()) {
319            hasDropped = true;
320        }
321        byPid += entry->format(stat, id);
322    }
323    if (index > 1) { // print this only if interesting
324        std::string ditto("\" ");
325        output += formatLine(std::string("  PID/UID   COMMAND LINE"),
326                             ditto, hasDropped ? ditto : std::string(""));
327        output += byPid;
328    }
329
330    return output;
331}
332
333std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
334    return formatLine(name,
335                      std::string("Size"),
336                      std::string("Pruned"))
337         + formatLine(std::string("  PID/UID   COMMAND LINE"),
338                      std::string("BYTES"),
339                      std::string("NUM"));
340}
341
342std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
343    uid_t uid = getUid();
344    pid_t pid = getPid();
345    std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
346    const char *nameTmp = getName();
347    if (nameTmp) {
348        name += android::base::StringPrintf(
349            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
350            "", nameTmp);
351    } else if ((nameTmp = stat.uidToName(uid))) {
352        name += android::base::StringPrintf(
353            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
354            "", nameTmp);
355        free(const_cast<char *>(nameTmp));
356    }
357
358    std::string size = android::base::StringPrintf("%zu",
359                                                   getSizes());
360
361    std::string pruned = "";
362    size_t dropped = getDropped();
363    if (dropped) {
364        pruned = android::base::StringPrintf("%zu", dropped);
365    }
366
367    return formatLine(name, size, pruned);
368}
369
370std::string TidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
371    return formatLine(name,
372                      std::string("Size"),
373                      std::string("Pruned"))
374         + formatLine(std::string("  TID/UID   COMM"),
375                      std::string("BYTES"),
376                      std::string("NUM"));
377}
378
379std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
380    uid_t uid = getUid();
381    std::string name = android::base::StringPrintf("%5u/%u",
382                                                   getTid(), uid);
383    const char *nameTmp = getName();
384    if (nameTmp) {
385        name += android::base::StringPrintf(
386            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
387            "", nameTmp);
388    } else if ((nameTmp = stat.uidToName(uid))) {
389        // if we do not have a PID name, lets punt to try UID name?
390        name += android::base::StringPrintf(
391            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
392            "", nameTmp);
393        free(const_cast<char *>(nameTmp));
394        // We tried, better to not have a name at all, we still
395        // have TID/UID by number to report in any case.
396    }
397
398    std::string size = android::base::StringPrintf("%zu",
399                                                   getSizes());
400
401    std::string pruned = "";
402    size_t dropped = getDropped();
403    if (dropped) {
404        pruned = android::base::StringPrintf("%zu", dropped);
405    }
406
407    return formatLine(name, size, pruned);
408}
409
410std::string TagEntry::formatHeader(const std::string &name, log_id_t id) const {
411    bool isprune = worstUidEnabledForLogid(id);
412    return formatLine(name,
413                      std::string("Size"),
414                      std::string(isprune ? "Prune" : ""))
415         + formatLine(std::string("    TAG/UID   TAGNAME"),
416                      std::string("BYTES"),
417                      std::string(isprune ? "NUM" : ""));
418}
419
420std::string TagEntry::format(const LogStatistics & /* stat */, log_id_t /* id */) const {
421    std::string name;
422    uid_t uid = getUid();
423    if (uid == (uid_t)-1) {
424        name = android::base::StringPrintf("%7u",
425                                           getKey());
426    } else {
427        name = android::base::StringPrintf("%7u/%u",
428                                           getKey(), uid);
429    }
430    const char *nameTmp = getName();
431    if (nameTmp) {
432        name += android::base::StringPrintf(
433            "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
434            "", nameTmp);
435    }
436
437    std::string size = android::base::StringPrintf("%zu",
438                                                   getSizes());
439
440    std::string pruned = "";
441
442    return formatLine(name, size, pruned);
443}
444
445std::string LogStatistics::format(uid_t uid, pid_t pid,
446                                  unsigned int logMask) const {
447    static const unsigned short spaces_total = 19;
448
449    // Report on total logging, current and for all time
450
451    std::string output = "size/num";
452    size_t oldLength;
453    short spaces = 1;
454
455    log_id_for_each(id) {
456        if (!(logMask & (1 << id))) {
457            continue;
458        }
459        oldLength = output.length();
460        if (spaces < 0) {
461            spaces = 0;
462        }
463        output += android::base::StringPrintf("%*s%s", spaces, "",
464                                              android_log_id_to_name(id));
465        spaces += spaces_total + oldLength - output.length();
466    }
467
468    spaces = 4;
469    output += "\nTotal";
470
471    log_id_for_each(id) {
472        if (!(logMask & (1 << id))) {
473            continue;
474        }
475        oldLength = output.length();
476        if (spaces < 0) {
477            spaces = 0;
478        }
479        output += android::base::StringPrintf("%*s%zu/%zu", spaces, "",
480                                              sizesTotal(id),
481                                              elementsTotal(id));
482        spaces += spaces_total + oldLength - output.length();
483    }
484
485    spaces = 6;
486    output += "\nNow";
487
488    log_id_for_each(id) {
489        if (!(logMask & (1 << id))) {
490            continue;
491        }
492
493        size_t els = elements(id);
494        if (els) {
495            oldLength = output.length();
496            if (spaces < 0) {
497                spaces = 0;
498            }
499            output += android::base::StringPrintf("%*s%zu/%zu", spaces, "",
500                                                  sizes(id), els);
501            spaces -= output.length() - oldLength;
502        }
503        spaces += spaces_total;
504    }
505
506    // Report on Chattiest
507
508    std::string name;
509
510    // Chattiest by application (UID)
511    log_id_for_each(id) {
512        if (!(logMask & (1 << id))) {
513            continue;
514        }
515
516        name = (uid == AID_ROOT)
517            ? "Chattiest UIDs in %s log buffer:"
518            : "Logging for your UID in %s log buffer:";
519        output += uidTable[id].format(*this, uid, pid, name, id);
520    }
521
522    if (enable) {
523        name = ((uid == AID_ROOT) && !pid)
524            ? "Chattiest PIDs:"
525            : "Logging for this PID:";
526        output += pidTable.format(*this, uid, pid, name);
527        name = "Chattiest TIDs";
528        if (pid) {
529            name += android::base::StringPrintf(" for PID %d", pid);
530        }
531        name += ":";
532        output += tidTable.format(*this, uid, pid, name);
533    }
534
535    if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
536        name = "Chattiest events log buffer TAGs";
537        if (pid) {
538            name += android::base::StringPrintf(" for PID %d", pid);
539        }
540        name += ":";
541        output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
542    }
543
544    if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
545        name = "Chattiest security log buffer TAGs";
546        if (pid) {
547            name += android::base::StringPrintf(" for PID %d", pid);
548        }
549        name += ":";
550        output += securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
551    }
552
553    return output;
554}
555
556namespace android {
557
558uid_t pidToUid(pid_t pid) {
559    char buffer[512];
560    snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
561    FILE *fp = fopen(buffer, "r");
562    if (fp) {
563        while (fgets(buffer, sizeof(buffer), fp)) {
564            int uid;
565            if (sscanf(buffer, "Uid: %d", &uid) == 1) {
566                fclose(fp);
567                return uid;
568            }
569        }
570        fclose(fp);
571    }
572    return AID_LOGD; // associate this with the logger
573}
574
575}
576
577uid_t LogStatistics::pidToUid(pid_t pid) {
578    return pidTable.add(pid)->second.getUid();
579}
580
581// caller must free character string
582const char *LogStatistics::pidToName(pid_t pid) const {
583    // An inconvenient truth ... getName() can alter the object
584    pidTable_t &writablePidTable = const_cast<pidTable_t &>(pidTable);
585    const char *name = writablePidTable.add(pid)->second.getName();
586    if (!name) {
587        return NULL;
588    }
589    return strdup(name);
590}
591