LogStatistics.cpp revision 5720d2c168f17afb57eefb6ed762a120a24c8ecb
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
42static char *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    uid_t uid = e->getUid();
74    unsigned short dropped = e->getDropped();
75    android::hash_t hash = android::hash_type(uid);
76    uidTable_t &table = uidTable[log_id];
77    ssize_t index = table.find(-1, hash, uid);
78    if (index == -1) {
79        UidEntry initEntry(uid);
80        initEntry.add(size);
81        initEntry.add_dropped(dropped);
82        table.add(hash, initEntry);
83    } else {
84        UidEntry &entry = table.editEntryAt(index);
85        entry.add(size);
86        entry.add_dropped(dropped);
87    }
88
89    mSizesTotal[log_id] += size;
90    ++mElementsTotal[log_id];
91
92    if (!enable) {
93        return;
94    }
95
96    pid_t pid = e->getPid();
97    hash = android::hash_type(pid);
98    index = pidTable.find(-1, hash, pid);
99    if (index == -1) {
100        PidEntry initEntry(pid, uid, android::pidToName(pid));
101        initEntry.add(size);
102        initEntry.add_dropped(dropped);
103        pidTable.add(hash, initEntry);
104    } else {
105        PidEntry &entry = pidTable.editEntryAt(index);
106        if (entry.getUid() != uid) {
107            entry.setUid(uid);
108            entry.setName(android::pidToName(pid));
109        } else if (!entry.getName()) {
110            char *name = android::pidToName(pid);
111            if (name) {
112                entry.setName(name);
113            }
114        }
115        entry.add(size);
116        entry.add_dropped(dropped);
117    }
118}
119
120void LogStatistics::subtract(LogBufferElement *e) {
121    log_id_t log_id = e->getLogId();
122    unsigned short size = e->getMsgLen();
123    mSizes[log_id] -= size;
124    --mElements[log_id];
125
126    uid_t uid = e->getUid();
127    unsigned short dropped = e->getDropped();
128    android::hash_t hash = android::hash_type(uid);
129    uidTable_t &table = uidTable[log_id];
130    ssize_t index = table.find(-1, hash, uid);
131    if (index != -1) {
132        UidEntry &entry = table.editEntryAt(index);
133        if (entry.subtract(size) || entry.subtract_dropped(dropped)) {
134            table.removeAt(index);
135        }
136    }
137
138    if (!enable) {
139        return;
140    }
141
142    pid_t pid = e->getPid();
143    hash = android::hash_type(pid);
144    index = pidTable.find(-1, hash, pid);
145    if (index != -1) {
146        PidEntry &entry = pidTable.editEntryAt(index);
147        if (entry.subtract(size) || entry.subtract_dropped(dropped)) {
148            pidTable.removeAt(index);
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 *e) {
156    log_id_t log_id = e->getLogId();
157    unsigned short size = e->getMsgLen();
158    mSizes[log_id] -= size;
159
160    uid_t uid = e->getUid();
161    android::hash_t hash = android::hash_type(uid);
162    typeof uidTable[0] &table = uidTable[log_id];
163    ssize_t index = table.find(-1, hash, uid);
164    if (index != -1) {
165        UidEntry &entry = table.editEntryAt(index);
166        entry.subtract(size);
167        entry.add_dropped(1);
168    }
169
170    if (!enable) {
171        return;
172    }
173
174    pid_t pid = e->getPid();
175    hash = android::hash_type(pid);
176    index = pidTable.find(-1, hash, pid);
177    if (index != -1) {
178        PidEntry &entry = pidTable.editEntryAt(index);
179        entry.subtract(size);
180        entry.add_dropped(1);
181    }
182}
183
184// caller must own and free character string
185char *LogStatistics::uidToName(uid_t uid) {
186    // Local hard coded favourites
187    if (uid == AID_LOGD) {
188        return strdup("auditd");
189    }
190
191    // Android hard coded
192    const struct android_id_info *info = android_ids;
193
194    for (size_t i = 0; i < android_id_count; ++i) {
195        if (info->aid == uid) {
196            return strdup(info->name);
197        }
198        ++info;
199    }
200
201    // Parse /data/system/packages.list
202    char *name = android::uidToName(uid);
203    if (name) {
204        return name;
205    }
206
207    // report uid -> pid(s) -> pidToName if unique
208    ssize_t index = -1;
209    while ((index = pidTable.next(index)) != -1) {
210        const PidEntry &entry = pidTable.entryAt(index);
211
212        if (entry.getUid() == uid) {
213            const char *n = entry.getName();
214
215            if (n) {
216                if (!name) {
217                    name = strdup(n);
218                } else if (strcmp(name, n)) {
219                    free(name);
220                    return NULL;
221                }
222            }
223        }
224    }
225
226    // No one
227    return name;
228}
229
230static void format_line(android::String8 &output,
231        android::String8 &name, android::String8 &size, android::String8 &pruned) {
232    static const size_t pruned_len = 6;
233    static const size_t total_len = 70 + pruned_len;
234
235    ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
236    ssize_t size_len = std::max(size.length() + 1,
237                                total_len - name.length() - drop_len - 1);
238
239    if (pruned.length()) {
240        output.appendFormat("%s%*s%*s\n", name.string(),
241                                          (int)size_len, size.string(),
242                                          (int)drop_len, pruned.string());
243    } else {
244        output.appendFormat("%s%*s\n", name.string(),
245                                       (int)size_len, size.string());
246    }
247}
248
249void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
250    static const unsigned short spaces_total = 19;
251
252    if (*buf) {
253        free(*buf);
254        *buf = NULL;
255    }
256
257    // Report on total logging, current and for all time
258
259    android::String8 output("size/num");
260    size_t oldLength;
261    short spaces = 1;
262
263    log_id_for_each(id) {
264        if (!(logMask & (1 << id))) {
265            continue;
266        }
267        oldLength = output.length();
268        if (spaces < 0) {
269            spaces = 0;
270        }
271        output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
272        spaces += spaces_total + oldLength - output.length();
273    }
274
275    spaces = 4;
276    output.appendFormat("\nTotal");
277
278    log_id_for_each(id) {
279        if (!(logMask & (1 << id))) {
280            continue;
281        }
282        oldLength = output.length();
283        if (spaces < 0) {
284            spaces = 0;
285        }
286        output.appendFormat("%*s%zu/%zu", spaces, "",
287                            sizesTotal(id), elementsTotal(id));
288        spaces += spaces_total + oldLength - output.length();
289    }
290
291    spaces = 6;
292    output.appendFormat("\nNow");
293
294    log_id_for_each(id) {
295        if (!(logMask & (1 << id))) {
296            continue;
297        }
298
299        size_t els = elements(id);
300        if (els) {
301            oldLength = output.length();
302            if (spaces < 0) {
303                spaces = 0;
304            }
305            output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
306            spaces -= output.length() - oldLength;
307        }
308        spaces += spaces_total;
309    }
310
311    // Report on Chattiest
312
313    // Chattiest by application (UID)
314    static const size_t maximum_sorted_entries = 32;
315    log_id_for_each(id) {
316        if (!(logMask & (1 << id))) {
317            continue;
318        }
319
320        bool headerPrinted = false;
321        std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
322        ssize_t index = -1;
323        while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
324            const UidEntry *entry = sorted[index];
325            uid_t u = entry->getKey();
326            if ((uid != AID_ROOT) && (u != uid)) {
327                continue;
328            }
329
330            if (!headerPrinted) {
331                output.appendFormat("\n\n");
332                android::String8 name("");
333                if (uid == AID_ROOT) {
334                    name.appendFormat(
335                        "Chattiest UIDs in %s log buffer:",
336                        android_log_id_to_name(id));
337                } else {
338                    name.appendFormat(
339                        "Logging for your UID in %s log buffer:",
340                        android_log_id_to_name(id));
341                }
342                android::String8 size("Size");
343                android::String8 pruned("Pruned");
344                if (!worstUidEnabledForLogid(id)) {
345                    pruned.setTo("");
346                }
347                format_line(output, name, size, pruned);
348
349                name.setTo("UID   PACKAGE");
350                size.setTo("BYTES");
351                pruned.setTo("LINES");
352                if (!worstUidEnabledForLogid(id)) {
353                    pruned.setTo("");
354                }
355                format_line(output, name, size, pruned);
356
357                headerPrinted = true;
358            }
359
360            android::String8 name("");
361            name.appendFormat("%u", u);
362            char *n = uidToName(u);
363            if (n) {
364                name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
365                free(n);
366            }
367
368            android::String8 size("");
369            size.appendFormat("%zu", entry->getSizes());
370
371            android::String8 pruned("");
372            size_t dropped = entry->getDropped();
373            if (dropped) {
374                pruned.appendFormat("%zu", dropped);
375            }
376
377            format_line(output, name, size, pruned);
378        }
379    }
380
381    if (enable) {
382        bool headerPrinted = false;
383        std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
384        ssize_t index = -1;
385        while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
386            const PidEntry *entry = sorted[index];
387            uid_t u = entry->getUid();
388            if ((uid != AID_ROOT) && (u != uid)) {
389                continue;
390            }
391
392            if (!headerPrinted) {
393                output.appendFormat("\n\n");
394                android::String8 name("");
395                if (uid == AID_ROOT) {
396                    name.appendFormat("Chattiest PIDs:");
397                } else {
398                    name.appendFormat("Logging for this PID:");
399                }
400                android::String8 size("Size");
401                android::String8 pruned("Pruned");
402                format_line(output, name, size, pruned);
403
404                name.setTo("  PID/UID   COMMAND LINE");
405                size.setTo("BYTES");
406                pruned.setTo("LINES");
407                format_line(output, name, size, pruned);
408
409                headerPrinted = true;
410            }
411
412            android::String8 name("");
413            name.appendFormat("%5u/%u", entry->getKey(), u);
414            const char *n = entry->getName();
415            if (n) {
416                name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
417            } else {
418                char *un = uidToName(u);
419                if (un) {
420                    name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
421                    free(un);
422                }
423            }
424
425            android::String8 size("");
426            size.appendFormat("%zu", entry->getSizes());
427
428            android::String8 pruned("");
429            size_t dropped = entry->getDropped();
430            if (dropped) {
431                pruned.appendFormat("%zu", dropped);
432            }
433
434            format_line(output, name, size, pruned);
435        }
436    }
437
438    *buf = strdup(output.string());
439}
440
441namespace android {
442
443uid_t pidToUid(pid_t pid) {
444    char buffer[512];
445    snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
446    FILE *fp = fopen(buffer, "r");
447    if (fp) {
448        while (fgets(buffer, sizeof(buffer), fp)) {
449            int uid;
450            if (sscanf(buffer, "Uid: %d", &uid) == 1) {
451                fclose(fp);
452                return uid;
453            }
454        }
455        fclose(fp);
456    }
457    return AID_LOGD; // associate this with the logger
458}
459
460}
461
462uid_t LogStatistics::pidToUid(pid_t pid) {
463    uid_t uid;
464    android::hash_t hash = android::hash_type(pid);
465    ssize_t index = pidTable.find(-1, hash, pid);
466    if (index == -1) {
467        uid = android::pidToUid(pid);
468        PidEntry initEntry(pid, uid, android::pidToName(pid));
469        pidTable.add(hash, initEntry);
470    } else {
471        PidEntry &entry = pidTable.editEntryAt(index);
472        if (!entry.getName()) {
473            char *name = android::pidToName(pid);
474            if (name) {
475                entry.setName(name);
476            }
477        }
478        uid = entry.getUid();
479    }
480    return uid;
481}
482
483// caller must free character string
484char *LogStatistics::pidToName(pid_t pid) {
485    char *name;
486
487    android::hash_t hash = android::hash_type(pid);
488    ssize_t index = pidTable.find(-1, hash, pid);
489    if (index == -1) {
490        name = android::pidToName(pid);
491        PidEntry initEntry(pid, android::pidToUid(pid), name ? strdup(name) : NULL);
492        pidTable.add(hash, initEntry);
493    } else {
494        PidEntry &entry = pidTable.editEntryAt(index);
495        const char *n = entry.getName();
496        if (n) {
497            name = strdup(n);
498        } else {
499            name = android::pidToName(pid);
500            if (name) {
501                entry.setName(strdup(name));
502            }
503        }
504    }
505
506    return name;
507}
508