2014-02-06 23:48:50 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
#include <algorithm> // std::max
|
2014-04-07 16:05:40 +02:00
|
|
|
#include <fcntl.h>
|
2015-03-10 21:51:35 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2014-02-06 23:48:50 +01:00
|
|
|
|
|
|
|
#include <log/logger.h>
|
|
|
|
#include <private/android_filesystem_config.h>
|
|
|
|
#include <utils/String8.h>
|
|
|
|
|
|
|
|
#include "LogStatistics.h"
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
LogStatistics::LogStatistics() {
|
|
|
|
log_id_for_each(id) {
|
|
|
|
mSizes[id] = 0;
|
|
|
|
mElements[id] = 0;
|
|
|
|
mSizesTotal[id] = 0;
|
|
|
|
mElementsTotal[id] = 0;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
// caller must own and free character string
|
|
|
|
char *LogStatistics::pidToName(pid_t pid) {
|
2014-04-07 16:05:40 +02:00
|
|
|
char *retval = NULL;
|
2014-09-21 23:22:18 +02:00
|
|
|
if (pid == 0) { // special case from auditd for kernel
|
|
|
|
retval = strdup("logd.auditd");
|
2015-03-10 21:51:35 +01:00
|
|
|
} else {
|
2014-04-07 16:05:40 +02:00
|
|
|
char buffer[512];
|
|
|
|
snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
|
|
|
|
int fd = open(buffer, O_RDONLY);
|
|
|
|
if (fd >= 0) {
|
|
|
|
ssize_t ret = read(fd, buffer, sizeof(buffer));
|
|
|
|
if (ret > 0) {
|
|
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
// frameworks intermediate state
|
|
|
|
if (strcmp(buffer, "<pre-initialized>")) {
|
|
|
|
retval = strdup(buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
void LogStatistics::add(LogBufferElement *e) {
|
|
|
|
log_id_t log_id = e->getLogId();
|
|
|
|
unsigned short size = e->getMsgLen();
|
|
|
|
mSizes[log_id] += size;
|
|
|
|
++mElements[log_id];
|
2014-02-06 23:48:50 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
uid_t uid = e->getUid();
|
|
|
|
android::hash_t hash = android::hash_type(uid);
|
|
|
|
uidTable_t &table = uidTable[log_id];
|
|
|
|
ssize_t index = table.find(-1, hash, uid);
|
|
|
|
if (index == -1) {
|
|
|
|
UidEntry initEntry(uid);
|
|
|
|
initEntry.add(size);
|
|
|
|
table.add(hash, initEntry);
|
2014-02-06 23:48:50 +01:00
|
|
|
} else {
|
2015-03-10 21:51:35 +01:00
|
|
|
UidEntry &entry = table.editEntryAt(index);
|
|
|
|
entry.add(size);
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
2014-04-05 01:35:59 +02:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
mSizesTotal[log_id] += size;
|
|
|
|
++mElementsTotal[log_id];
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
void LogStatistics::subtract(LogBufferElement *e) {
|
|
|
|
log_id_t log_id = e->getLogId();
|
|
|
|
unsigned short size = e->getMsgLen();
|
|
|
|
mSizes[log_id] -= size;
|
|
|
|
--mElements[log_id];
|
2014-02-06 23:48:50 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
uid_t uid = e->getUid();
|
|
|
|
android::hash_t hash = android::hash_type(uid);
|
|
|
|
uidTable_t &table = uidTable[log_id];
|
|
|
|
ssize_t index = table.find(-1, hash, uid);
|
|
|
|
if (index != -1) {
|
|
|
|
UidEntry &entry = table.editEntryAt(index);
|
|
|
|
if (entry.subtract(size)) {
|
|
|
|
table.removeAt(index);
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
// caller must own and delete UidEntry array
|
|
|
|
const UidEntry **LogStatistics::sort(size_t n, log_id id) {
|
|
|
|
if (!n) {
|
|
|
|
return NULL;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
const UidEntry **retval = new const UidEntry* [n];
|
|
|
|
memset(retval, 0, sizeof(*retval) * n);
|
2014-02-06 23:48:50 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
uidTable_t &table = uidTable[id];
|
|
|
|
ssize_t index = -1;
|
|
|
|
while ((index = table.next(index)) >= 0) {
|
|
|
|
const UidEntry &entry = table.entryAt(index);
|
|
|
|
size_t s = entry.getSizes();
|
|
|
|
ssize_t i = n - 1;
|
|
|
|
while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0));
|
|
|
|
if (++i < (ssize_t)n) {
|
|
|
|
size_t b = n - i - 1;
|
|
|
|
if (b) {
|
|
|
|
memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
|
2014-04-05 01:35:59 +02:00
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
retval[i] = &entry;
|
2014-04-05 01:35:59 +02:00
|
|
|
}
|
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
return retval;
|
2014-04-05 01:35:59 +02:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
// caller must own and free character string
|
|
|
|
char *LogStatistics::uidToName(uid_t uid) {
|
|
|
|
// Local hard coded favourites
|
|
|
|
if (uid == AID_LOGD) {
|
|
|
|
return strdup("auditd");
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
// Android hard coded
|
|
|
|
const struct android_id_info *info = android_ids;
|
2014-02-06 23:48:50 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
for (size_t i = 0; i < android_id_count; ++i) {
|
|
|
|
if (info->aid == uid) {
|
|
|
|
return strdup(info->name);
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
++info;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
// No one
|
|
|
|
return NULL;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
static void format_line(android::String8 &output,
|
|
|
|
android::String8 &name, android::String8 &size) {
|
|
|
|
static const size_t total_len = 70;
|
2014-02-06 23:48:50 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
output.appendFormat("%s%*s\n", name.string(),
|
2015-03-20 21:44:53 +01:00
|
|
|
(int)std::max(total_len - name.length() - 1, size.length() + 1),
|
2015-03-10 21:51:35 +01:00
|
|
|
size.string());
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
|
2014-04-07 16:05:40 +02:00
|
|
|
static const unsigned short spaces_total = 19;
|
2014-02-06 23:48:50 +01:00
|
|
|
|
|
|
|
if (*buf) {
|
2014-04-07 06:25:58 +02:00
|
|
|
free(*buf);
|
2014-02-06 23:48:50 +01:00
|
|
|
*buf = NULL;
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
// Report on total logging, current and for all time
|
|
|
|
|
|
|
|
android::String8 output("size/num");
|
2014-02-06 23:48:50 +01:00
|
|
|
size_t oldLength;
|
2015-03-10 21:51:35 +01:00
|
|
|
short spaces = 1;
|
2014-02-06 23:48:50 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
log_id_for_each(id) {
|
|
|
|
if (!(logMask & (1 << id))) {
|
2014-04-05 01:35:59 +02:00
|
|
|
continue;
|
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
oldLength = output.length();
|
2014-04-05 01:35:59 +02:00
|
|
|
if (spaces < 0) {
|
|
|
|
spaces = 0;
|
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
output.appendFormat("%*s%s", spaces, "", android_log_id_to_name(id));
|
|
|
|
spaces += spaces_total + oldLength - output.length();
|
|
|
|
}
|
2014-04-05 01:35:59 +02:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
spaces = 4;
|
|
|
|
output.appendFormat("\nTotal");
|
2014-04-05 01:35:59 +02:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
log_id_for_each(id) {
|
|
|
|
if (!(logMask & (1 << id))) {
|
|
|
|
continue;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
oldLength = output.length();
|
|
|
|
if (spaces < 0) {
|
|
|
|
spaces = 0;
|
2014-02-20 02:18:31 +01:00
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
output.appendFormat("%*s%zu/%zu", spaces, "",
|
|
|
|
sizesTotal(id), elementsTotal(id));
|
|
|
|
spaces += spaces_total + oldLength - output.length();
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
spaces = 6;
|
|
|
|
output.appendFormat("\nNow");
|
2014-02-06 23:48:50 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
log_id_for_each(id) {
|
|
|
|
if (!(logMask & (1 << id))) {
|
2014-02-06 23:48:50 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
size_t els = elements(id);
|
2014-02-06 23:48:50 +01:00
|
|
|
if (els) {
|
2015-03-10 21:51:35 +01:00
|
|
|
oldLength = output.length();
|
2014-02-20 02:18:31 +01:00
|
|
|
if (spaces < 0) {
|
|
|
|
spaces = 0;
|
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
output.appendFormat("%*s%zu/%zu", spaces, "", sizes(id), els);
|
|
|
|
spaces -= output.length() - oldLength;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
spaces += spaces_total;
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
// Report on Chattiest
|
2014-03-26 18:46:39 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
// Chattiest by application (UID)
|
|
|
|
log_id_for_each(id) {
|
|
|
|
if (!(logMask & (1 << id))) {
|
2014-03-26 18:46:39 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
static const size_t maximum_sorted_entries = 32;
|
|
|
|
const UidEntry **sorted = sort(maximum_sorted_entries, id);
|
2014-03-26 18:46:39 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
if (!sorted) {
|
|
|
|
continue;
|
2014-03-26 18:46:39 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
bool print = false;
|
|
|
|
for(size_t index = 0; index < maximum_sorted_entries; ++index) {
|
|
|
|
const UidEntry *entry = sorted[index];
|
2014-03-26 18:46:39 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
if (!entry) {
|
2014-03-26 18:46:39 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
size_t sizes = entry->getSizes();
|
|
|
|
if (sizes < (65536/100)) {
|
|
|
|
break;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
uid_t u = entry->getKey();
|
|
|
|
if ((uid != AID_ROOT) && (u != uid)) {
|
2014-02-06 23:48:50 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
if (!print) {
|
|
|
|
if (uid == AID_ROOT) {
|
|
|
|
output.appendFormat(
|
|
|
|
"\n\nChattiest UIDs in %s:\n",
|
|
|
|
android_log_id_to_name(id));
|
|
|
|
android::String8 name("UID");
|
|
|
|
android::String8 size("Size");
|
|
|
|
format_line(output, name, size);
|
|
|
|
} else {
|
|
|
|
output.appendFormat(
|
|
|
|
"\n\nLogging for your UID in %s:\n",
|
|
|
|
android_log_id_to_name(id));
|
2014-02-20 02:18:31 +01:00
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
print = true;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
android::String8 name("");
|
|
|
|
name.appendFormat("%u", u);
|
|
|
|
char *n = uidToName(u);
|
|
|
|
if (n) {
|
|
|
|
name.appendFormat("%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", n);
|
|
|
|
free(n);
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
android::String8 size("");
|
|
|
|
size.appendFormat("%zu", sizes);
|
2014-02-06 23:48:50 +01:00
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
format_line(output, name, size);
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
|
|
|
|
delete [] sorted;
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 21:51:35 +01:00
|
|
|
*buf = strdup(output.string());
|
2014-02-06 23:48:50 +01:00
|
|
|
}
|
2014-04-07 16:15:33 +02:00
|
|
|
|
|
|
|
uid_t LogStatistics::pidToUid(pid_t pid) {
|
2015-03-10 21:51:35 +01:00
|
|
|
char buffer[512];
|
|
|
|
snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
|
|
|
|
FILE *fp = fopen(buffer, "r");
|
|
|
|
if (fp) {
|
|
|
|
while (fgets(buffer, sizeof(buffer), fp)) {
|
|
|
|
int uid;
|
|
|
|
if (sscanf(buffer, "Groups: %d", &uid) == 1) {
|
|
|
|
fclose(fp);
|
|
|
|
return uid;
|
2014-04-07 16:15:33 +02:00
|
|
|
}
|
|
|
|
}
|
2015-03-10 21:51:35 +01:00
|
|
|
fclose(fp);
|
2014-04-07 16:15:33 +02:00
|
|
|
}
|
|
|
|
return getuid(); // associate this with the logger
|
|
|
|
}
|