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