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 
LogStatistics()29 LogStatistics::LogStatistics() : enable(false) {
30     log_id_for_each(id) {
31         mSizes[id] = 0;
32         mElements[id] = 0;
33         mSizesTotal[id] = 0;
34         mElementsTotal[id] = 0;
35     }
36 }
37 
38 namespace android {
39 
40 // caller must own and free character string
pidToName(pid_t pid)41 char *pidToName(pid_t pid) {
42     char *retval = NULL;
43     if (pid == 0) { // special case from auditd/klogd for kernel
44         retval = strdup("logd");
45     } else {
46         char buffer[512];
47         snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
48         int fd = open(buffer, O_RDONLY);
49         if (fd >= 0) {
50             ssize_t ret = read(fd, buffer, sizeof(buffer));
51             if (ret > 0) {
52                 buffer[sizeof(buffer)-1] = '\0';
53                 // frameworks intermediate state
54                 if (strcmp(buffer, "<pre-initialized>")) {
55                     retval = strdup(buffer);
56                 }
57             }
58             close(fd);
59         }
60     }
61     return retval;
62 }
63 
64 }
65 
add(LogBufferElement * e)66 void LogStatistics::add(LogBufferElement *e) {
67     log_id_t log_id = e->getLogId();
68     unsigned short size = e->getMsgLen();
69     mSizes[log_id] += size;
70     ++mElements[log_id];
71 
72     mSizesTotal[log_id] += size;
73     ++mElementsTotal[log_id];
74 
75     if (log_id == LOG_ID_KERNEL) {
76         return;
77     }
78 
79     uidTable[log_id].add(e->getUid(), e);
80 
81     if (!enable) {
82         return;
83     }
84 
85     pidTable.add(e->getPid(), e);
86     tidTable.add(e->getTid(), e);
87 
88     uint32_t tag = e->getTag();
89     if (tag) {
90         tagTable.add(tag, e);
91     }
92 }
93 
subtract(LogBufferElement * e)94 void LogStatistics::subtract(LogBufferElement *e) {
95     log_id_t log_id = e->getLogId();
96     unsigned short size = e->getMsgLen();
97     mSizes[log_id] -= size;
98     --mElements[log_id];
99 
100     if (log_id == LOG_ID_KERNEL) {
101         return;
102     }
103 
104     uidTable[log_id].subtract(e->getUid(), e);
105 
106     if (!enable) {
107         return;
108     }
109 
110     pidTable.subtract(e->getPid(), e);
111     tidTable.subtract(e->getTid(), e);
112 
113     uint32_t tag = e->getTag();
114     if (tag) {
115         tagTable.subtract(tag, e);
116     }
117 }
118 
119 // Atomically set an entry to drop
120 // entry->setDropped(1) must follow this call, caller should do this explicitly.
drop(LogBufferElement * e)121 void LogStatistics::drop(LogBufferElement *e) {
122     log_id_t log_id = e->getLogId();
123     unsigned short size = e->getMsgLen();
124     mSizes[log_id] -= size;
125 
126     uidTable[log_id].drop(e->getUid(), e);
127 
128     if (!enable) {
129         return;
130     }
131 
132     pidTable.drop(e->getPid(), e);
133     tidTable.drop(e->getTid(), e);
134 }
135 
136 // caller must own and free character string
uidToName(uid_t uid)137 char *LogStatistics::uidToName(uid_t uid) {
138     // Local hard coded favourites
139     if (uid == AID_LOGD) {
140         return strdup("auditd");
141     }
142 
143     // Android hard coded
144     const struct android_id_info *info = android_ids;
145 
146     for (size_t i = 0; i < android_id_count; ++i) {
147         if (info->aid == uid) {
148             return strdup(info->name);
149         }
150         ++info;
151     }
152 
153     // Parse /data/system/packages.list
154     uid_t userId = uid % AID_USER;
155     char *name = android::uidToName(userId);
156     if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
157         name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
158     }
159     if (name) {
160         return name;
161     }
162 
163     // report uid -> pid(s) -> pidToName if unique
164     for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
165         const PidEntry &entry = it->second;
166 
167         if (entry.getUid() == uid) {
168             const char *n = entry.getName();
169 
170             if (n) {
171                 if (!name) {
172                     name = strdup(n);
173                 } else if (strcmp(name, n)) {
174                     free(name);
175                     name = NULL;
176                     break;
177                 }
178             }
179         }
180     }
181 
182     // No one
183     return name;
184 }
185 
format_line(android::String8 & output,android::String8 & name,android::String8 & size,android::String8 & pruned)186 static void format_line(android::String8 &output,
187         android::String8 &name, android::String8 &size, android::String8 &pruned) {
188     static const size_t pruned_len = 6;
189     static const size_t total_len = 70 + pruned_len;
190 
191     ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
192     ssize_t size_len = std::max(size.length() + 1,
193                                 total_len - name.length() - drop_len - 1);
194 
195     if (pruned.length()) {
196         output.appendFormat("%s%*s%*s\n", name.string(),
197                                           (int)size_len, size.string(),
198                                           (int)drop_len, pruned.string());
199     } else {
200         output.appendFormat("%s%*s\n", name.string(),
201                                        (int)size_len, size.string());
202     }
203 }
204 
format(char ** buf,uid_t uid,unsigned int logMask)205 void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
206     static const unsigned short spaces_total = 19;
207 
208     if (*buf) {
209         free(*buf);
210         *buf = NULL;
211     }
212 
213     // Report on total logging, current and for all time
214 
215     android::String8 output("size/num");
216     size_t oldLength;
217     short spaces = 1;
218 
219     log_id_for_each(id) {
220         if (!(logMask & (1 << id))) {
221             continue;
222         }
223         oldLength = output.length();
224         if (spaces < 0) {
225             spaces = 0;
226         }
227         output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
228         spaces += spaces_total + oldLength - output.length();
229     }
230 
231     spaces = 4;
232     output.appendFormat("\nTotal");
233 
234     log_id_for_each(id) {
235         if (!(logMask & (1 << id))) {
236             continue;
237         }
238         oldLength = output.length();
239         if (spaces < 0) {
240             spaces = 0;
241         }
242         output.appendFormat("%*s%zu/%zu", spaces, "",
243                             sizesTotal(id), elementsTotal(id));
244         spaces += spaces_total + oldLength - output.length();
245     }
246 
247     spaces = 6;
248     output.appendFormat("\nNow");
249 
250     log_id_for_each(id) {
251         if (!(logMask & (1 << id))) {
252             continue;
253         }
254 
255         size_t els = elements(id);
256         if (els) {
257             oldLength = output.length();
258             if (spaces < 0) {
259                 spaces = 0;
260             }
261             output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
262             spaces -= output.length() - oldLength;
263         }
264         spaces += spaces_total;
265     }
266 
267     // Report on Chattiest
268 
269     // Chattiest by application (UID)
270     static const size_t maximum_sorted_entries = 32;
271     log_id_for_each(id) {
272         if (!(logMask & (1 << id))) {
273             continue;
274         }
275 
276         bool headerPrinted = false;
277         std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
278         ssize_t index = -1;
279         while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
280             const UidEntry *entry = sorted[index];
281             uid_t u = entry->getKey();
282             if ((uid != AID_ROOT) && (u != uid)) {
283                 continue;
284             }
285 
286             if (!headerPrinted) {
287                 output.appendFormat("\n\n");
288                 android::String8 name("");
289                 if (uid == AID_ROOT) {
290                     name.appendFormat(
291                         "Chattiest UIDs in %s log buffer:",
292                         android_log_id_to_name(id));
293                 } else {
294                     name.appendFormat(
295                         "Logging for your UID in %s log buffer:",
296                         android_log_id_to_name(id));
297                 }
298                 android::String8 size("Size");
299                 android::String8 pruned("Pruned");
300                 if (!worstUidEnabledForLogid(id)) {
301                     pruned.setTo("");
302                 }
303                 format_line(output, name, size, pruned);
304 
305                 name.setTo("UID   PACKAGE");
306                 size.setTo("BYTES");
307                 pruned.setTo("LINES");
308                 if (!worstUidEnabledForLogid(id)) {
309                     pruned.setTo("");
310                 }
311                 format_line(output, name, size, pruned);
312 
313                 headerPrinted = true;
314             }
315 
316             android::String8 name("");
317             name.appendFormat("%u", u);
318             char *n = uidToName(u);
319             if (n) {
320                 name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
321                 free(n);
322             }
323 
324             android::String8 size("");
325             size.appendFormat("%zu", entry->getSizes());
326 
327             android::String8 pruned("");
328             size_t dropped = entry->getDropped();
329             if (dropped) {
330                 pruned.appendFormat("%zu", dropped);
331             }
332 
333             format_line(output, name, size, pruned);
334         }
335     }
336 
337     if (enable) {
338         // Pid table
339         bool headerPrinted = false;
340         std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
341         ssize_t index = -1;
342         while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
343             const PidEntry *entry = sorted[index];
344             uid_t u = entry->getUid();
345             if ((uid != AID_ROOT) && (u != uid)) {
346                 continue;
347             }
348 
349             if (!headerPrinted) {
350                 output.appendFormat("\n\n");
351                 android::String8 name("");
352                 if (uid == AID_ROOT) {
353                     name.appendFormat("Chattiest PIDs:");
354                 } else {
355                     name.appendFormat("Logging for this PID:");
356                 }
357                 android::String8 size("Size");
358                 android::String8 pruned("Pruned");
359                 format_line(output, name, size, pruned);
360 
361                 name.setTo("  PID/UID   COMMAND LINE");
362                 size.setTo("BYTES");
363                 pruned.setTo("LINES");
364                 format_line(output, name, size, pruned);
365 
366                 headerPrinted = true;
367             }
368 
369             android::String8 name("");
370             name.appendFormat("%5u/%u", entry->getKey(), u);
371             const char *n = entry->getName();
372             if (n) {
373                 name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
374             } else {
375                 char *un = uidToName(u);
376                 if (un) {
377                     name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
378                     free(un);
379                 }
380             }
381 
382             android::String8 size("");
383             size.appendFormat("%zu", entry->getSizes());
384 
385             android::String8 pruned("");
386             size_t dropped = entry->getDropped();
387             if (dropped) {
388                 pruned.appendFormat("%zu", dropped);
389             }
390 
391             format_line(output, name, size, pruned);
392         }
393     }
394 
395     if (enable) {
396         // Tid table
397         bool headerPrinted = false;
398         // sort() returns list of references, unique_ptr makes sure self-delete
399         std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries);
400         ssize_t index = -1;
401         while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
402             const TidEntry *entry = sorted[index];
403             uid_t u = entry->getUid();
404             if ((uid != AID_ROOT) && (u != uid)) {
405                 continue;
406             }
407 
408             if (!headerPrinted) { // Only print header if we have table to print
409                 output.appendFormat("\n\n");
410                 android::String8 name("Chattiest TIDs:");
411                 android::String8 size("Size");
412                 android::String8 pruned("Pruned");
413                 format_line(output, name, size, pruned);
414 
415                 name.setTo("  TID/UID   COMM");
416                 size.setTo("BYTES");
417                 pruned.setTo("LINES");
418                 format_line(output, name, size, pruned);
419 
420                 headerPrinted = true;
421             }
422 
423             android::String8 name("");
424             name.appendFormat("%5u/%u", entry->getKey(), u);
425             const char *n = entry->getName();
426             if (n) {
427                 name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
428             } else {
429                 // if we do not have a PID name, lets punt to try UID name?
430                 char *un = uidToName(u);
431                 if (un) {
432                     name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
433                     free(un);
434                 }
435                 // We tried, better to not have a name at all, we still
436                 // have TID/UID by number to report in any case.
437             }
438 
439             android::String8 size("");
440             size.appendFormat("%zu", entry->getSizes());
441 
442             android::String8 pruned("");
443             size_t dropped = entry->getDropped();
444             if (dropped) {
445                 pruned.appendFormat("%zu", dropped);
446             }
447 
448             format_line(output, name, size, pruned);
449         }
450     }
451 
452     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
453         // Tag table
454         bool headerPrinted = false;
455         std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries);
456         ssize_t index = -1;
457         while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
458             const TagEntry *entry = sorted[index];
459             uid_t u = entry->getUid();
460             if ((uid != AID_ROOT) && (u != uid)) {
461                 continue;
462             }
463 
464             android::String8 pruned("");
465 
466             if (!headerPrinted) {
467                 output.appendFormat("\n\n");
468                 android::String8 name("Chattiest events log buffer TAGs:");
469                 android::String8 size("Size");
470                 format_line(output, name, size, pruned);
471 
472                 name.setTo("    TAG/UID   TAGNAME");
473                 size.setTo("BYTES");
474                 format_line(output, name, size, pruned);
475 
476                 headerPrinted = true;
477             }
478 
479             android::String8 name("");
480             if (u == (uid_t)-1) {
481                 name.appendFormat("%7u", entry->getKey());
482             } else {
483                 name.appendFormat("%7u/%u", entry->getKey(), u);
484             }
485             const char *n = entry->getName();
486             if (n) {
487                 name.appendFormat("%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", n);
488             }
489 
490             android::String8 size("");
491             size.appendFormat("%zu", entry->getSizes());
492 
493             format_line(output, name, size, pruned);
494         }
495     }
496 
497     *buf = strdup(output.string());
498 }
499 
500 namespace android {
501 
pidToUid(pid_t pid)502 uid_t pidToUid(pid_t pid) {
503     char buffer[512];
504     snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
505     FILE *fp = fopen(buffer, "r");
506     if (fp) {
507         while (fgets(buffer, sizeof(buffer), fp)) {
508             int uid;
509             if (sscanf(buffer, "Uid: %d", &uid) == 1) {
510                 fclose(fp);
511                 return uid;
512             }
513         }
514         fclose(fp);
515     }
516     return AID_LOGD; // associate this with the logger
517 }
518 
519 }
520 
pidToUid(pid_t pid)521 uid_t LogStatistics::pidToUid(pid_t pid) {
522     return pidTable.add(pid)->second.getUid();
523 }
524 
525 // caller must free character string
pidToName(pid_t pid)526 char *LogStatistics::pidToName(pid_t pid) {
527     const char *name = pidTable.add(pid)->second.getName();
528     if (!name) {
529         return NULL;
530     }
531     return strdup(name);
532 }
533