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