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