am c3d40b86: Merge "logd: Add Pid statistics"

* commit 'c3d40b86911867439fc01829d4f1011dae2a31be':
  logd: Add Pid statistics
This commit is contained in:
Mark Salyzyn 2015-04-08 23:27:05 +00:00 committed by Android Git Automerger
commit d8ff56c9f1
3 changed files with 248 additions and 58 deletions

View file

@ -16,7 +16,6 @@
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/user.h>
#include <time.h>
@ -281,15 +280,14 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
size_t second_worst_sizes = 0;
if ((id != LOG_ID_CRASH) && mPrune.worstUidEnabled()) {
const UidEntry **sorted = stats.sort(2, id);
std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);
if (sorted) {
if (sorted.get()) {
if (sorted[0] && sorted[1]) {
worst = sorted[0]->getKey();
worst_sizes = sorted[0]->getSizes();
second_worst_sizes = sorted[1]->getSizes();
}
delete [] sorted;
}
}

View file

@ -17,7 +17,6 @@
#include <algorithm> // std::max
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -27,7 +26,8 @@
#include "LogStatistics.h"
LogStatistics::LogStatistics() {
LogStatistics::LogStatistics()
: enable(false) {
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
@ -36,8 +36,10 @@ LogStatistics::LogStatistics() {
}
}
namespace android {
// caller must own and free character string
char *LogStatistics::pidToName(pid_t pid) {
static char *pidToName(pid_t pid) {
char *retval = NULL;
if (pid == 0) { // special case from auditd for kernel
retval = strdup("logd.auditd");
@ -60,6 +62,8 @@ char *LogStatistics::pidToName(pid_t pid) {
return retval;
}
}
void LogStatistics::add(LogBufferElement *e) {
log_id_t log_id = e->getLogId();
unsigned short size = e->getMsgLen();
@ -81,6 +85,31 @@ void LogStatistics::add(LogBufferElement *e) {
mSizesTotal[log_id] += size;
++mElementsTotal[log_id];
if (!enable) {
return;
}
pid_t pid = e->getPid();
hash = android::hash_type(pid);
index = pidTable.find(-1, hash, pid);
if (index == -1) {
PidEntry initEntry(pid, uid, android::pidToName(pid));
initEntry.add(size);
pidTable.add(hash, initEntry);
} else {
PidEntry &entry = pidTable.editEntryAt(index);
if (entry.getUid() != uid) {
entry.setUid(uid);
entry.setName(android::pidToName(pid));
} else if (!entry.getName()) {
char *name = android::pidToName(pid);
if (name) {
entry.setName(name);
}
}
entry.add(size);
}
}
void LogStatistics::subtract(LogBufferElement *e) {
@ -99,33 +128,20 @@ void LogStatistics::subtract(LogBufferElement *e) {
table.removeAt(index);
}
}
}
// caller must own and delete UidEntry array
const UidEntry **LogStatistics::sort(size_t n, log_id id) {
if (!n) {
return NULL;
if (!enable) {
return;
}
const UidEntry **retval = new const UidEntry* [n];
memset(retval, 0, sizeof(*retval) * n);
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]));
}
retval[i] = &entry;
pid_t pid = e->getPid();
hash = android::hash_type(pid);
index = pidTable.find(-1, hash, pid);
if (index != -1) {
PidEntry &entry = pidTable.editEntryAt(index);
if (entry.subtract(size)) {
pidTable.removeAt(index);
}
}
return retval;
}
// caller must own and free character string
@ -145,8 +161,29 @@ char *LogStatistics::uidToName(uid_t uid) {
++info;
}
char *name = NULL;
// report uid -> pid(s) -> pidToName if unique
ssize_t index = -1;
while ((index = pidTable.next(index)) != -1) {
const PidEntry &entry = pidTable.entryAt(index);
if (entry.getUid() == uid) {
const char *n = entry.getName();
if (n) {
if (!name) {
name = strdup(n);
} else if (strcmp(name, n)) {
free(name);
return NULL;
}
}
}
}
// No one
return NULL;
return name;
}
static void format_line(android::String8 &output,
@ -223,37 +260,23 @@ void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
// Report on Chattiest
// Chattiest by application (UID)
static const size_t maximum_sorted_entries = 32;
log_id_for_each(id) {
if (!(logMask & (1 << id))) {
continue;
}
static const size_t maximum_sorted_entries = 32;
const UidEntry **sorted = sort(maximum_sorted_entries, id);
if (!sorted) {
continue;
}
bool print = false;
for(size_t index = 0; index < maximum_sorted_entries; ++index) {
bool headerPrinted = false;
std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
ssize_t index = -1;
while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
const UidEntry *entry = sorted[index];
if (!entry) {
break;
}
size_t sizes = entry->getSizes();
if (sizes < (65536/100)) {
break;
}
uid_t u = entry->getKey();
if ((uid != AID_ROOT) && (u != uid)) {
continue;
}
if (!print) {
if (!headerPrinted) {
if (uid == AID_ROOT) {
output.appendFormat(
"\n\nChattiest UIDs in %s:\n",
@ -266,7 +289,7 @@ void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
"\n\nLogging for your UID in %s:\n",
android_log_id_to_name(id));
}
print = true;
headerPrinted = true;
}
android::String8 name("");
@ -278,18 +301,62 @@ void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
}
android::String8 size("");
size.appendFormat("%zu", sizes);
size.appendFormat("%zu", entry->getSizes());
format_line(output, name, size);
}
}
delete [] sorted;
if (enable) {
bool headerPrinted = false;
std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
ssize_t index = -1;
while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
const PidEntry *entry = sorted[index];
uid_t u = entry->getUid();
if ((uid != AID_ROOT) && (u != uid)) {
continue;
}
if (!headerPrinted) {
if (uid == AID_ROOT) {
output.appendFormat("\n\nChattiest PIDs:\n");
} else {
output.appendFormat("\n\nLogging for this PID:\n");
}
android::String8 name(" PID/UID");
android::String8 size("Size");
android::String8 pruned("Pruned");
format_line(output, name, size, pruned);
headerPrinted = true;
}
android::String8 name("");
name.appendFormat("%5u/%u", entry->getKey(), u);
const char *n = entry->getName();
if (n) {
name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
} else {
char *un = uidToName(u);
if (un) {
name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
free(un);
}
}
android::String8 size("");
size.appendFormat("%zu", entry->getSizes());
format_line(output, name, size);
}
}
*buf = strdup(output.string());
}
uid_t LogStatistics::pidToUid(pid_t pid) {
namespace android {
uid_t pidToUid(pid_t pid) {
char buffer[512];
snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
FILE *fp = fopen(buffer, "r");
@ -305,3 +372,52 @@ uid_t LogStatistics::pidToUid(pid_t pid) {
}
return getuid(); // associate this with the logger
}
}
uid_t LogStatistics::pidToUid(pid_t pid) {
uid_t uid;
android::hash_t hash = android::hash_type(pid);
ssize_t index = pidTable.find(-1, hash, pid);
if (index == -1) {
uid = android::pidToUid(pid);
PidEntry initEntry(pid, uid, android::pidToName(pid));
pidTable.add(hash, initEntry);
} else {
PidEntry &entry = pidTable.editEntryAt(index);
if (!entry.getName()) {
char *name = android::pidToName(pid);
if (name) {
entry.setName(name);
}
}
uid = entry.getUid();
}
return uid;
}
// caller must free character string
char *LogStatistics::pidToName(pid_t pid) {
char *name;
android::hash_t hash = android::hash_type(pid);
ssize_t index = pidTable.find(-1, hash, pid);
if (index == -1) {
name = android::pidToName(pid);
PidEntry initEntry(pid, android::pidToUid(pid), name ? strdup(name) : NULL);
pidTable.add(hash, initEntry);
} else {
PidEntry &entry = pidTable.editEntryAt(index);
const char *n = entry.getName();
if (n) {
name = strdup(n);
} else {
name = android::pidToName(pid);
if (name) {
entry.setName(strdup(name));
}
}
}
return name;
}

View file

@ -17,6 +17,8 @@
#ifndef _LOGD_LOG_STATISTICS_H__
#define _LOGD_LOG_STATISTICS_H__
#include <memory>
#include <stdlib.h>
#include <sys/types.h>
#include <log/log.h>
@ -27,6 +29,52 @@
#define log_id_for_each(i) \
for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
template <typename TKey, typename TEntry>
class LogHashtable : public android::BasicHashtable<TKey, TEntry> {
public:
std::unique_ptr<const TEntry *[]> sort(size_t n) {
if (!n) {
std::unique_ptr<const TEntry *[]> sorted(NULL);
return sorted;
}
const TEntry **retval = new const TEntry* [n];
memset(retval, 0, sizeof(*retval) * n);
ssize_t index = -1;
while ((index = android::BasicHashtable<TKey, TEntry>::next(index)) >= 0) {
const TEntry &entry = android::BasicHashtable<TKey, TEntry>::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]));
}
retval[i] = &entry;
}
}
std::unique_ptr<const TEntry *[]> sorted(retval);
return sorted;
}
// Iteration handler for the sort method output
static ssize_t next(ssize_t index, std::unique_ptr<const TEntry *[]> &sorted, size_t n) {
++index;
if (!sorted.get() || (index < 0) || (n <= (size_t)index) || !sorted[index]
|| (sorted[index]->getSizes() <= (sorted[0]->getSizes() / 100))) {
return -1;
}
return index;
}
ssize_t next(ssize_t index) {
return android::BasicHashtable<TKey, TEntry>::next(index);
}
};
struct UidEntry {
const uid_t uid;
size_t size;
@ -39,27 +87,55 @@ struct UidEntry {
inline bool subtract(size_t s) { size -= s; return !size; }
};
struct PidEntry {
const pid_t pid;
uid_t uid;
char *name;
size_t size;
PidEntry(pid_t p, uid_t u, char *n):pid(p),uid(u),name(n),size(0) { }
PidEntry(const PidEntry &c):
pid(c.pid),
uid(c.uid),
name(c.name ? strdup(c.name) : NULL),
size(c.size) { }
~PidEntry() { free(name); }
const pid_t&getKey() const { return pid; }
const uid_t&getUid() const { return uid; }
uid_t&setUid(uid_t u) { return uid = u; }
const char*getName() const { return name; }
char *setName(char *n) { free(name); return name = n; }
size_t getSizes() const { return size; }
inline void add(size_t s) { size += s; }
inline bool subtract(size_t s) { size -= s; return !size; }
};
// Log Statistics
class LogStatistics {
size_t mSizes[LOG_ID_MAX];
size_t mElements[LOG_ID_MAX];
size_t mSizesTotal[LOG_ID_MAX];
size_t mElementsTotal[LOG_ID_MAX];
bool enable;
// uid to size list
typedef android::BasicHashtable<uid_t, UidEntry> uidTable_t;
typedef LogHashtable<uid_t, UidEntry> uidTable_t;
uidTable_t uidTable[LOG_ID_MAX];
// pid to uid list
typedef LogHashtable<pid_t, PidEntry> pidTable_t;
pidTable_t pidTable;
public:
LogStatistics();
void enableStatistics() { }
void enableStatistics() { enable = true; }
void add(LogBufferElement *entry);
void subtract(LogBufferElement *entry);
// Caller must delete array
const UidEntry **sort(size_t n, log_id i);
std::unique_ptr<const UidEntry *[]> sort(size_t n, log_id i) { return uidTable[i].sort(n); }
// fast track current value by id only
size_t sizes(log_id_t id) const { return mSizes[id]; }