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