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