2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <algorithm> // std::max
23 #include <log/logger.h>
24 #include <private/android_filesystem_config.h>
25 #include <utils/String8.h>
27 #include "LogStatistics.h"
29 LogStatistics::LogStatistics()
35 mElementsTotal[id] = 0;
41 // caller must own and free character string
42 static char *pidToName(pid_t pid) {
44 if (pid == 0) { // special case from auditd for kernel
45 retval = strdup("logd.auditd");
48 snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
49 int fd = open(buffer, O_RDONLY);
51 ssize_t ret = read(fd, buffer, sizeof(buffer));
53 buffer[sizeof(buffer)-1] = '\0';
54 // frameworks intermediate state
55 if (strcmp(buffer, "<pre-initialized>")) {
56 retval = strdup(buffer);
67 void LogStatistics::add(LogBufferElement *e) {
68 log_id_t log_id = e->getLogId();
69 unsigned short size = e->getMsgLen();
70 mSizes[log_id] += size;
73 uid_t uid = e->getUid();
74 unsigned short dropped = e->getDropped();
75 android::hash_t hash = android::hash_type(uid);
76 uidTable_t &table = uidTable[log_id];
77 ssize_t index = table.find(-1, hash, uid);
79 UidEntry initEntry(uid);
81 initEntry.add_dropped(dropped);
82 table.add(hash, initEntry);
84 UidEntry &entry = table.editEntryAt(index);
86 entry.add_dropped(dropped);
89 mSizesTotal[log_id] += size;
90 ++mElementsTotal[log_id];
96 pid_t pid = e->getPid();
97 hash = android::hash_type(pid);
98 index = pidTable.find(-1, hash, pid);
100 PidEntry initEntry(pid, uid, android::pidToName(pid));
102 initEntry.add_dropped(dropped);
103 pidTable.add(hash, initEntry);
105 PidEntry &entry = pidTable.editEntryAt(index);
106 if (entry.getUid() != uid) {
108 entry.setName(android::pidToName(pid));
109 } else if (!entry.getName()) {
110 char *name = android::pidToName(pid);
116 entry.add_dropped(dropped);
120 void LogStatistics::subtract(LogBufferElement *e) {
121 log_id_t log_id = e->getLogId();
122 unsigned short size = e->getMsgLen();
123 mSizes[log_id] -= size;
126 uid_t uid = e->getUid();
127 unsigned short dropped = e->getDropped();
128 android::hash_t hash = android::hash_type(uid);
129 uidTable_t &table = uidTable[log_id];
130 ssize_t index = table.find(-1, hash, uid);
132 UidEntry &entry = table.editEntryAt(index);
133 if (entry.subtract(size) || entry.subtract_dropped(dropped)) {
134 table.removeAt(index);
142 pid_t pid = e->getPid();
143 hash = android::hash_type(pid);
144 index = pidTable.find(-1, hash, pid);
146 PidEntry &entry = pidTable.editEntryAt(index);
147 if (entry.subtract(size) || entry.subtract_dropped(dropped)) {
148 pidTable.removeAt(index);
153 // Atomically set an entry to drop
154 // entry->setDropped(1) must follow this call, caller should do this explicitly.
155 void LogStatistics::drop(LogBufferElement *e) {
156 log_id_t log_id = e->getLogId();
157 unsigned short size = e->getMsgLen();
158 mSizes[log_id] -= size;
160 uid_t uid = e->getUid();
161 android::hash_t hash = android::hash_type(uid);
162 typeof uidTable[0] &table = uidTable[log_id];
163 ssize_t index = table.find(-1, hash, uid);
165 UidEntry &entry = table.editEntryAt(index);
166 entry.subtract(size);
167 entry.add_dropped(1);
174 pid_t pid = e->getPid();
175 hash = android::hash_type(pid);
176 index = pidTable.find(-1, hash, pid);
178 PidEntry &entry = pidTable.editEntryAt(index);
179 entry.subtract(size);
180 entry.add_dropped(1);
184 // caller must own and free character string
185 char *LogStatistics::uidToName(uid_t uid) {
186 // Local hard coded favourites
187 if (uid == AID_LOGD) {
188 return strdup("auditd");
191 // Android hard coded
192 const struct android_id_info *info = android_ids;
194 for (size_t i = 0; i < android_id_count; ++i) {
195 if (info->aid == uid) {
196 return strdup(info->name);
201 // Parse /data/system/packages.list
202 char *name = android::uidToName(uid);
207 // report uid -> pid(s) -> pidToName if unique
209 while ((index = pidTable.next(index)) != -1) {
210 const PidEntry &entry = pidTable.entryAt(index);
212 if (entry.getUid() == uid) {
213 const char *n = entry.getName();
218 } else if (strcmp(name, n)) {
230 static void format_line(android::String8 &output,
231 android::String8 &name, android::String8 &size, android::String8 &pruned) {
232 static const size_t pruned_len = 6;
233 static const size_t total_len = 70 + pruned_len;
235 ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
236 ssize_t size_len = std::max(size.length() + 1,
237 total_len - name.length() - drop_len - 1);
239 if (pruned.length()) {
240 output.appendFormat("%s%*s%*s\n", name.string(),
241 (int)size_len, size.string(),
242 (int)drop_len, pruned.string());
244 output.appendFormat("%s%*s\n", name.string(),
245 (int)size_len, size.string());
249 void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
250 static const unsigned short spaces_total = 19;
257 // Report on total logging, current and for all time
259 android::String8 output("size/num");
263 log_id_for_each(id) {
264 if (!(logMask & (1 << id))) {
267 oldLength = output.length();
271 output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
272 spaces += spaces_total + oldLength - output.length();
276 output.appendFormat("\nTotal");
278 log_id_for_each(id) {
279 if (!(logMask & (1 << id))) {
282 oldLength = output.length();
286 output.appendFormat("%*s%zu/%zu", spaces, "",
287 sizesTotal(id), elementsTotal(id));
288 spaces += spaces_total + oldLength - output.length();
292 output.appendFormat("\nNow");
294 log_id_for_each(id) {
295 if (!(logMask & (1 << id))) {
299 size_t els = elements(id);
301 oldLength = output.length();
305 output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
306 spaces -= output.length() - oldLength;
308 spaces += spaces_total;
311 // Report on Chattiest
313 // Chattiest by application (UID)
314 static const size_t maximum_sorted_entries = 32;
315 log_id_for_each(id) {
316 if (!(logMask & (1 << id))) {
320 bool headerPrinted = false;
321 std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
323 while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
324 const UidEntry *entry = sorted[index];
325 uid_t u = entry->getKey();
326 if ((uid != AID_ROOT) && (u != uid)) {
330 if (!headerPrinted) {
331 output.appendFormat("\n\n");
332 android::String8 name("");
333 if (uid == AID_ROOT) {
335 "Chattiest UIDs in %s log buffer:",
336 android_log_id_to_name(id));
339 "Logging for your UID in %s log buffer:",
340 android_log_id_to_name(id));
342 android::String8 size("Size");
343 android::String8 pruned("Pruned");
344 if (!worstUidEnabledForLogid(id)) {
347 format_line(output, name, size, pruned);
349 name.setTo("UID PACKAGE");
351 pruned.setTo("LINES");
352 if (!worstUidEnabledForLogid(id)) {
355 format_line(output, name, size, pruned);
357 headerPrinted = true;
360 android::String8 name("");
361 name.appendFormat("%u", u);
362 char *n = uidToName(u);
364 name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
368 android::String8 size("");
369 size.appendFormat("%zu", entry->getSizes());
371 android::String8 pruned("");
372 size_t dropped = entry->getDropped();
374 pruned.appendFormat("%zu", dropped);
377 format_line(output, name, size, pruned);
382 bool headerPrinted = false;
383 std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
385 while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
386 const PidEntry *entry = sorted[index];
387 uid_t u = entry->getUid();
388 if ((uid != AID_ROOT) && (u != uid)) {
392 if (!headerPrinted) {
393 output.appendFormat("\n\n");
394 android::String8 name("");
395 if (uid == AID_ROOT) {
396 name.appendFormat("Chattiest PIDs:");
398 name.appendFormat("Logging for this PID:");
400 android::String8 size("Size");
401 android::String8 pruned("Pruned");
402 format_line(output, name, size, pruned);
404 name.setTo(" PID/UID COMMAND LINE");
406 pruned.setTo("LINES");
407 format_line(output, name, size, pruned);
409 headerPrinted = true;
412 android::String8 name("");
413 name.appendFormat("%5u/%u", entry->getKey(), u);
414 const char *n = entry->getName();
416 name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
418 char *un = uidToName(u);
420 name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
425 android::String8 size("");
426 size.appendFormat("%zu", entry->getSizes());
428 android::String8 pruned("");
429 size_t dropped = entry->getDropped();
431 pruned.appendFormat("%zu", dropped);
434 format_line(output, name, size, pruned);
438 *buf = strdup(output.string());
443 uid_t pidToUid(pid_t pid) {
445 snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
446 FILE *fp = fopen(buffer, "r");
448 while (fgets(buffer, sizeof(buffer), fp)) {
450 if (sscanf(buffer, "Uid: %d", &uid) == 1) {
457 return AID_LOGD; // associate this with the logger
462 uid_t LogStatistics::pidToUid(pid_t pid) {
464 android::hash_t hash = android::hash_type(pid);
465 ssize_t index = pidTable.find(-1, hash, pid);
467 uid = android::pidToUid(pid);
468 PidEntry initEntry(pid, uid, android::pidToName(pid));
469 pidTable.add(hash, initEntry);
471 PidEntry &entry = pidTable.editEntryAt(index);
472 if (!entry.getName()) {
473 char *name = android::pidToName(pid);
478 uid = entry.getUid();
483 // caller must free character string
484 char *LogStatistics::pidToName(pid_t pid) {
487 android::hash_t hash = android::hash_type(pid);
488 ssize_t index = pidTable.find(-1, hash, pid);
490 name = android::pidToName(pid);
491 PidEntry initEntry(pid, android::pidToUid(pid), name ? strdup(name) : NULL);
492 pidTable.add(hash, initEntry);
494 PidEntry &entry = pidTable.editEntryAt(index);
495 const char *n = entry.getName();
499 name = android::pidToName(pid);
501 entry.setName(strdup(name));