• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <pwd.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
24 #include <list>
25 
26 #include <android/log.h>
27 
28 #include "LogStatistics.h"
29 
30 size_t LogStatistics::SizesTotal;
31 
LogStatistics()32 LogStatistics::LogStatistics() : enable(false) {
33     log_id_for_each(id) {
34         mSizes[id] = 0;
35         mElements[id] = 0;
36         mDroppedElements[id] = 0;
37         mSizesTotal[id] = 0;
38         mElementsTotal[id] = 0;
39     }
40 }
41 
42 namespace android {
43 
sizesTotal()44 size_t sizesTotal() {
45     return LogStatistics::sizesTotal();
46 }
47 
48 // caller must own and free character string
pidToName(pid_t pid)49 char* pidToName(pid_t pid) {
50     char* retval = NULL;
51     if (pid == 0) {  // special case from auditd/klogd for kernel
52         retval = strdup("logd");
53     } else {
54         char buffer[512];
55         snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
56         int fd = open(buffer, O_RDONLY);
57         if (fd >= 0) {
58             ssize_t ret = read(fd, buffer, sizeof(buffer));
59             if (ret > 0) {
60                 buffer[sizeof(buffer) - 1] = '\0';
61                 // frameworks intermediate state
62                 if (fastcmp<strcmp>(buffer, "<pre-initialized>")) {
63                     retval = strdup(buffer);
64                 }
65             }
66             close(fd);
67         }
68     }
69     return retval;
70 }
71 }
72 
add(LogBufferElement * element)73 void LogStatistics::add(LogBufferElement* element) {
74     log_id_t log_id = element->getLogId();
75     unsigned short size = element->getMsgLen();
76     mSizes[log_id] += size;
77     ++mElements[log_id];
78 
79     if (element->getDropped()) {
80         ++mDroppedElements[log_id];
81     } else {
82         // When caller adding a chatty entry, they will have already
83         // called add() and subtract() for each entry as they are
84         // evaluated and trimmed, thus recording size and number of
85         // elements, but we must recognize the manufactured dropped
86         // entry as not contributing to the lifetime totals.
87         mSizesTotal[log_id] += size;
88         SizesTotal += size;
89         ++mElementsTotal[log_id];
90     }
91 
92     if (log_id == LOG_ID_KERNEL) {
93         return;
94     }
95 
96     uidTable[log_id].add(element->getUid(), element);
97     if (element->getUid() == AID_SYSTEM) {
98         pidSystemTable[log_id].add(element->getPid(), element);
99     }
100 
101     if (!enable) {
102         return;
103     }
104 
105     pidTable.add(element->getPid(), element);
106     tidTable.add(element->getTid(), element);
107 
108     uint32_t tag = element->getTag();
109     if (tag) {
110         if (log_id == LOG_ID_SECURITY) {
111             securityTagTable.add(tag, element);
112         } else {
113             tagTable.add(tag, element);
114         }
115     }
116 }
117 
subtract(LogBufferElement * element)118 void LogStatistics::subtract(LogBufferElement* element) {
119     log_id_t log_id = element->getLogId();
120     unsigned short size = element->getMsgLen();
121     mSizes[log_id] -= size;
122     --mElements[log_id];
123     if (element->getDropped()) {
124         --mDroppedElements[log_id];
125     }
126 
127     if (log_id == LOG_ID_KERNEL) {
128         return;
129     }
130 
131     uidTable[log_id].subtract(element->getUid(), element);
132     if (element->getUid() == AID_SYSTEM) {
133         pidSystemTable[log_id].subtract(element->getPid(), element);
134     }
135 
136     if (!enable) {
137         return;
138     }
139 
140     pidTable.subtract(element->getPid(), element);
141     tidTable.subtract(element->getTid(), element);
142 
143     uint32_t tag = element->getTag();
144     if (tag) {
145         if (log_id == LOG_ID_SECURITY) {
146             securityTagTable.subtract(tag, element);
147         } else {
148             tagTable.subtract(tag, element);
149         }
150     }
151 }
152 
153 // Atomically set an entry to drop
154 // entry->setDropped(1) must follow this call, caller should do this explicitly.
drop(LogBufferElement * element)155 void LogStatistics::drop(LogBufferElement* element) {
156     log_id_t log_id = element->getLogId();
157     unsigned short size = element->getMsgLen();
158     mSizes[log_id] -= size;
159     ++mDroppedElements[log_id];
160 
161     uidTable[log_id].drop(element->getUid(), element);
162     if (element->getUid() == AID_SYSTEM) {
163         pidSystemTable[log_id].drop(element->getPid(), element);
164     }
165 
166     if (!enable) {
167         return;
168     }
169 
170     pidTable.drop(element->getPid(), element);
171     tidTable.drop(element->getTid(), element);
172 
173     uint32_t tag = element->getTag();
174     if (tag) {
175         if (log_id == LOG_ID_SECURITY) {
176             securityTagTable.drop(tag, element);
177         } else {
178             tagTable.drop(tag, element);
179         }
180     }
181 }
182 
183 // caller must own and free character string
uidToName(uid_t uid) const184 const char* LogStatistics::uidToName(uid_t uid) const {
185     // Local hard coded favourites
186     if (uid == AID_LOGD) {
187         return strdup("auditd");
188     }
189 
190     // Android system
191     if (uid < AID_APP) {
192         // in bionic, thread safe as long as we copy the results
193         struct passwd* pwd = getpwuid(uid);
194         if (pwd) {
195             return strdup(pwd->pw_name);
196         }
197     }
198 
199     // Parse /data/system/packages.list
200     uid_t userId = uid % AID_USER_OFFSET;
201     const char* name = android::uidToName(userId);
202     if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
203         name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
204     }
205     if (name) {
206         return name;
207     }
208 
209     // Android application
210     if (uid >= AID_APP) {
211         struct passwd* pwd = getpwuid(uid);
212         if (pwd) {
213             return strdup(pwd->pw_name);
214         }
215     }
216 
217     // report uid -> pid(s) -> pidToName if unique
218     for (pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end();
219          ++it) {
220         const PidEntry& entry = it->second;
221 
222         if (entry.getUid() == uid) {
223             const char* nameTmp = entry.getName();
224 
225             if (nameTmp) {
226                 if (!name) {
227                     name = strdup(nameTmp);
228                 } else if (fastcmp<strcmp>(name, nameTmp)) {
229                     free(const_cast<char*>(name));
230                     name = NULL;
231                     break;
232                 }
233             }
234         }
235     }
236 
237     // No one
238     return name;
239 }
240 
formatHeader(const std::string & name,log_id_t id) const241 std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
242     bool isprune = worstUidEnabledForLogid(id);
243     return formatLine(android::base::StringPrintf(name.c_str(),
244                                                   android_log_id_to_name(id)),
245                       std::string("Size"),
246                       std::string(isprune ? "+/-  Pruned" : "")) +
247            formatLine(std::string("UID   PACKAGE"), std::string("BYTES"),
248                       std::string(isprune ? "NUM" : ""));
249 }
250 
format(const LogStatistics & stat,log_id_t id) const251 std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
252     uid_t uid = getUid();
253     std::string name = android::base::StringPrintf("%u", uid);
254     const char* nameTmp = stat.uidToName(uid);
255     if (nameTmp) {
256         name += android::base::StringPrintf(
257             "%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", nameTmp);
258         free(const_cast<char*>(nameTmp));
259     }
260 
261     std::string size = android::base::StringPrintf("%zu", getSizes());
262 
263     std::string pruned = "";
264     if (worstUidEnabledForLogid(id)) {
265         size_t totalDropped = 0;
266         for (LogStatistics::uidTable_t::const_iterator it =
267                  stat.uidTable[id].begin();
268              it != stat.uidTable[id].end(); ++it) {
269             totalDropped += it->second.getDropped();
270         }
271         size_t sizes = stat.sizes(id);
272         size_t totalSize = stat.sizesTotal(id);
273         size_t totalElements = stat.elementsTotal(id);
274         float totalVirtualSize =
275             (float)sizes + (float)totalDropped * totalSize / totalElements;
276         size_t entrySize = getSizes();
277         float virtualEntrySize = entrySize;
278         int realPermille = virtualEntrySize * 1000.0 / sizes;
279         size_t dropped = getDropped();
280         if (dropped) {
281             pruned = android::base::StringPrintf("%zu", dropped);
282             virtualEntrySize += (float)dropped * totalSize / totalElements;
283         }
284         int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
285         int permille =
286             (realPermille - virtualPermille) * 1000L / (virtualPermille ?: 1);
287         if ((permille < -1) || (1 < permille)) {
288             std::string change;
289             const char* units = "%";
290             const char* prefix = (permille > 0) ? "+" : "";
291 
292             if (permille > 999) {
293                 permille = (permille + 1000) / 100;  // Now tenths fold
294                 units = "X";
295                 prefix = "";
296             }
297             if ((-99 < permille) && (permille < 99)) {
298                 change = android::base::StringPrintf(
299                     "%s%d.%u%s", prefix, permille / 10,
300                     ((permille < 0) ? (-permille % 10) : (permille % 10)),
301                     units);
302             } else {
303                 change = android::base::StringPrintf(
304                     "%s%d%s", prefix, (permille + 5) / 10, units);
305             }
306             ssize_t spaces = EntryBaseConstants::pruned_len - 2 -
307                              pruned.length() - change.length();
308             if ((spaces <= 0) && pruned.length()) {
309                 spaces = 1;
310             }
311             if (spaces > 0) {
312                 change += android::base::StringPrintf("%*s", (int)spaces, "");
313             }
314             pruned = change + pruned;
315         }
316     }
317 
318     std::string output = formatLine(name, size, pruned);
319 
320     if (uid != AID_SYSTEM) {
321         return output;
322     }
323 
324     static const size_t maximum_sorted_entries = 32;
325     std::unique_ptr<const PidEntry* []> sorted =
326         stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
327 
328     if (!sorted.get()) {
329         return output;
330     }
331     std::string byPid;
332     size_t index;
333     bool hasDropped = false;
334     for (index = 0; index < maximum_sorted_entries; ++index) {
335         const PidEntry* entry = sorted[index];
336         if (!entry) {
337             break;
338         }
339         if (entry->getSizes() <= (getSizes() / 100)) {
340             break;
341         }
342         if (entry->getDropped()) {
343             hasDropped = true;
344         }
345         byPid += entry->format(stat, id);
346     }
347     if (index > 1) {  // print this only if interesting
348         std::string ditto("\" ");
349         output += formatLine(std::string("  PID/UID   COMMAND LINE"), ditto,
350                              hasDropped ? ditto : std::string(""));
351         output += byPid;
352     }
353 
354     return output;
355 }
356 
formatHeader(const std::string & name,log_id_t) const357 std::string PidEntry::formatHeader(const std::string& name,
358                                    log_id_t /* id */) const {
359     return formatLine(name, std::string("Size"), std::string("Pruned")) +
360            formatLine(std::string("  PID/UID   COMMAND LINE"),
361                       std::string("BYTES"), std::string("NUM"));
362 }
363 
format(const LogStatistics & stat,log_id_t) const364 std::string PidEntry::format(const LogStatistics& stat,
365                              log_id_t /* id */) const {
366     uid_t uid = getUid();
367     pid_t pid = getPid();
368     std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
369     const char* nameTmp = getName();
370     if (nameTmp) {
371         name += android::base::StringPrintf(
372             "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
373     } else if ((nameTmp = stat.uidToName(uid))) {
374         name += android::base::StringPrintf(
375             "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
376         free(const_cast<char*>(nameTmp));
377     }
378 
379     std::string size = android::base::StringPrintf("%zu", getSizes());
380 
381     std::string pruned = "";
382     size_t dropped = getDropped();
383     if (dropped) {
384         pruned = android::base::StringPrintf("%zu", dropped);
385     }
386 
387     return formatLine(name, size, pruned);
388 }
389 
formatHeader(const std::string & name,log_id_t) const390 std::string TidEntry::formatHeader(const std::string& name,
391                                    log_id_t /* id */) const {
392     return formatLine(name, std::string("Size"), std::string("Pruned")) +
393            formatLine(std::string("  TID/UID   COMM"), std::string("BYTES"),
394                       std::string("NUM"));
395 }
396 
format(const LogStatistics & stat,log_id_t) const397 std::string TidEntry::format(const LogStatistics& stat,
398                              log_id_t /* id */) const {
399     uid_t uid = getUid();
400     std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
401     const char* nameTmp = getName();
402     if (nameTmp) {
403         name += android::base::StringPrintf(
404             "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
405     } else if ((nameTmp = stat.uidToName(uid))) {
406         // if we do not have a PID name, lets punt to try UID name?
407         name += android::base::StringPrintf(
408             "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp);
409         free(const_cast<char*>(nameTmp));
410         // We tried, better to not have a name at all, we still
411         // have TID/UID by number to report in any case.
412     }
413 
414     std::string size = android::base::StringPrintf("%zu", getSizes());
415 
416     std::string pruned = "";
417     size_t dropped = getDropped();
418     if (dropped) {
419         pruned = android::base::StringPrintf("%zu", dropped);
420     }
421 
422     return formatLine(name, size, pruned);
423 }
424 
formatHeader(const std::string & name,log_id_t id) const425 std::string TagEntry::formatHeader(const std::string& name, log_id_t id) const {
426     bool isprune = worstUidEnabledForLogid(id);
427     return formatLine(name, std::string("Size"),
428                       std::string(isprune ? "Prune" : "")) +
429            formatLine(std::string("    TAG/UID   TAGNAME"),
430                       std::string("BYTES"), std::string(isprune ? "NUM" : ""));
431 }
432 
format(const LogStatistics &,log_id_t) const433 std::string TagEntry::format(const LogStatistics& /* stat */,
434                              log_id_t /* id */) const {
435     std::string name;
436     uid_t uid = getUid();
437     if (uid == (uid_t)-1) {
438         name = android::base::StringPrintf("%7u", getKey());
439     } else {
440         name = android::base::StringPrintf("%7u/%u", getKey(), uid);
441     }
442     const char* nameTmp = getName();
443     if (nameTmp) {
444         name += android::base::StringPrintf(
445             "%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", nameTmp);
446     }
447 
448     std::string size = android::base::StringPrintf("%zu", getSizes());
449 
450     std::string pruned = "";
451     size_t dropped = getDropped();
452     if (dropped) {
453         pruned = android::base::StringPrintf("%zu", dropped);
454     }
455 
456     return formatLine(name, size, pruned);
457 }
458 
format(uid_t uid,pid_t pid,unsigned int logMask) const459 std::string LogStatistics::format(uid_t uid, pid_t pid,
460                                   unsigned int logMask) const {
461     static const unsigned short spaces_total = 19;
462 
463     // Report on total logging, current and for all time
464 
465     std::string output = "size/num";
466     size_t oldLength;
467     short spaces = 1;
468 
469     log_id_for_each(id) {
470         if (!(logMask & (1 << id))) continue;
471         oldLength = output.length();
472         if (spaces < 0) spaces = 0;
473         output += android::base::StringPrintf("%*s%s", spaces, "",
474                                               android_log_id_to_name(id));
475         spaces += spaces_total + oldLength - output.length();
476     }
477     if (spaces < 0) spaces = 0;
478     output += android::base::StringPrintf("%*sTotal", spaces, "");
479 
480     static const char TotalStr[] = "\nTotal";
481     spaces = 10 - strlen(TotalStr);
482     output += TotalStr;
483 
484     size_t totalSize = 0;
485     size_t totalEls = 0;
486     log_id_for_each(id) {
487         if (!(logMask & (1 << id))) continue;
488         oldLength = output.length();
489         if (spaces < 0) spaces = 0;
490         size_t szs = sizesTotal(id);
491         totalSize += szs;
492         size_t els = elementsTotal(id);
493         totalEls += els;
494         output +=
495             android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
496         spaces += spaces_total + oldLength - output.length();
497     }
498     if (spaces < 0) spaces = 0;
499     output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
500                                           totalEls);
501 
502     static const char NowStr[] = "\nNow";
503     spaces = 10 - strlen(NowStr);
504     output += NowStr;
505 
506     totalSize = 0;
507     totalEls = 0;
508     log_id_for_each(id) {
509         if (!(logMask & (1 << id))) continue;
510 
511         size_t els = elements(id);
512         if (els) {
513             oldLength = output.length();
514             if (spaces < 0) spaces = 0;
515             size_t szs = sizes(id);
516             totalSize += szs;
517             totalEls += els;
518             output +=
519                 android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
520             spaces -= output.length() - oldLength;
521         }
522         spaces += spaces_total;
523     }
524     if (spaces < 0) spaces = 0;
525     output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
526                                           totalEls);
527 
528     static const char OverheadStr[] = "\nOverhead";
529     spaces = 10 - strlen(OverheadStr);
530     output += OverheadStr;
531 
532     totalSize = 0;
533     log_id_for_each(id) {
534         if (!(logMask & (1 << id))) continue;
535 
536         size_t els = elements(id);
537         if (els) {
538             oldLength = output.length();
539             if (spaces < 0) spaces = 0;
540             // estimate the std::list overhead.
541             static const size_t overhead =
542                 ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
543                  -sizeof(uint64_t)) +
544                 sizeof(std::list<LogBufferElement*>);
545             size_t szs = sizes(id) + els * overhead;
546             totalSize += szs;
547             output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
548             spaces -= output.length() - oldLength;
549         }
550         spaces += spaces_total;
551     }
552     totalSize += sizeOf();
553     if (spaces < 0) spaces = 0;
554     output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize);
555 
556     // Report on Chattiest
557 
558     std::string name;
559 
560     // Chattiest by application (UID)
561     log_id_for_each(id) {
562         if (!(logMask & (1 << id))) continue;
563 
564         name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
565                                  : "Logging for your UID in %s log buffer:";
566         output += uidTable[id].format(*this, uid, pid, name, id);
567     }
568 
569     if (enable) {
570         name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
571                                            : "Logging for this PID:";
572         output += pidTable.format(*this, uid, pid, name);
573         name = "Chattiest TIDs";
574         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
575         name += ":";
576         output += tidTable.format(*this, uid, pid, name);
577     }
578 
579     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
580         name = "Chattiest events log buffer TAGs";
581         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
582         name += ":";
583         output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
584     }
585 
586     if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
587         name = "Chattiest security log buffer TAGs";
588         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
589         name += ":";
590         output +=
591             securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
592     }
593 
594     return output;
595 }
596 
597 namespace android {
598 
pidToUid(pid_t pid)599 uid_t pidToUid(pid_t pid) {
600     char buffer[512];
601     snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
602     FILE* fp = fopen(buffer, "r");
603     if (fp) {
604         while (fgets(buffer, sizeof(buffer), fp)) {
605             int uid;
606             if (sscanf(buffer, "Uid: %d", &uid) == 1) {
607                 fclose(fp);
608                 return uid;
609             }
610         }
611         fclose(fp);
612     }
613     return AID_LOGD;  // associate this with the logger
614 }
615 }
616 
pidToUid(pid_t pid)617 uid_t LogStatistics::pidToUid(pid_t pid) {
618     return pidTable.add(pid)->second.getUid();
619 }
620 
621 // caller must free character string
pidToName(pid_t pid) const622 const char* LogStatistics::pidToName(pid_t pid) const {
623     // An inconvenient truth ... getName() can alter the object
624     pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
625     const char* name = writablePidTable.add(pid)->second.getName();
626     if (!name) {
627         return NULL;
628     }
629     return strdup(name);
630 }
631