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