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 
LogStatistics()26 LogStatistics::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 
36 namespace android {
37 
38 // caller must own and free character string
pidToName(pid_t pid)39 char *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 
add(LogBufferElement * element)64 void 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 
subtract(LogBufferElement * element)99 void 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.
drop(LogBufferElement * element)136 void 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
uidToName(uid_t uid) const156 const 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 
formatHeader(const std::string & name,log_id_t id) const205 std::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 
format(const LogStatistics & stat,log_id_t id) const216 std::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 
formatHeader(const std::string & name,log_id_t) const324 std::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 
format(const LogStatistics & stat,log_id_t) const333 std::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 
formatHeader(const std::string & name,log_id_t) const361 std::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 
format(const LogStatistics & stat,log_id_t) const370 std::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 
formatHeader(const std::string & name,log_id_t id) const401 std::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 
format(const LogStatistics &,log_id_t) const411 std::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 
format(uid_t uid,pid_t pid,unsigned int logMask) const436 std::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 
547 namespace android {
548 
pidToUid(pid_t pid)549 uid_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 
pidToUid(pid_t pid)568 uid_t LogStatistics::pidToUid(pid_t pid) {
569     return pidTable.add(pid)->second.getUid();
570 }
571 
572 // caller must free character string
pidToName(pid_t pid) const573 const 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