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