Merge changes I9d6dde2c,I38bfcba5 am: a269c7c3d1 am: 0379e27671 am: a0f8962885
Change-Id: Ic29b7b0718a7b9415a8e7569706a29f5d127ef3b
This commit is contained in:
commit
c226504862
17 changed files with 343 additions and 367 deletions
|
|
@ -54,6 +54,7 @@ cc_library_static {
|
|||
|
||||
cflags: [
|
||||
"-Wextra",
|
||||
"-Wthread-safety",
|
||||
] + event_flag,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "CommandListener.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
|
|
@ -35,12 +37,11 @@
|
|||
#include <private/android_filesystem_config.h>
|
||||
#include <sysutils/SocketClient.h>
|
||||
|
||||
#include "CommandListener.h"
|
||||
#include "LogCommand.h"
|
||||
#include "LogUtils.h"
|
||||
|
||||
CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune)
|
||||
: FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune) {
|
||||
CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune,
|
||||
LogStatistics* stats)
|
||||
: FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune), stats_(stats) {
|
||||
registerCmd(new ClearCmd(this));
|
||||
registerCmd(new GetBufSizeCmd(this));
|
||||
registerCmd(new SetBufSizeCmd(this));
|
||||
|
|
@ -148,7 +149,7 @@ int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
unsigned long size = buf()->getSizeUsed((log_id_t)id);
|
||||
unsigned long size = stats()->Sizes((log_id_t)id);
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), "%lu", size);
|
||||
cli->sendMsg(buf);
|
||||
|
|
@ -209,7 +210,7 @@ int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc,
|
|||
}
|
||||
}
|
||||
|
||||
cli->sendMsg(PackageString(buf()->formatStatistics(uid, pid, logMask)).c_str());
|
||||
cli->sendMsg(PackageString(stats()->Format(uid, pid, logMask)).c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,13 @@
|
|||
#include "LogCommand.h"
|
||||
#include "LogListener.h"
|
||||
#include "LogReader.h"
|
||||
#include "LogStatistics.h"
|
||||
#include "LogTags.h"
|
||||
#include "LogWhiteBlackList.h"
|
||||
|
||||
class CommandListener : public FrameworkListener {
|
||||
public:
|
||||
CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune);
|
||||
CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune, LogStatistics* log_statistics);
|
||||
virtual ~CommandListener() {}
|
||||
|
||||
private:
|
||||
|
|
@ -36,20 +37,22 @@ class CommandListener : public FrameworkListener {
|
|||
LogBuffer* buf_;
|
||||
LogTags* tags_;
|
||||
PruneList* prune_;
|
||||
LogStatistics* stats_;
|
||||
|
||||
#define LogCmd(name, command_string) \
|
||||
class name##Cmd : public LogCommand { \
|
||||
public: \
|
||||
explicit name##Cmd(CommandListener* parent) \
|
||||
: LogCommand(#command_string), parent_(parent) {} \
|
||||
virtual ~name##Cmd() {} \
|
||||
int runCommand(SocketClient* c, int argc, char** argv); \
|
||||
\
|
||||
private: \
|
||||
LogBuffer* buf() const { return parent_->buf_; } \
|
||||
LogTags* tags() const { return parent_->tags_; } \
|
||||
PruneList* prune() const { return parent_->prune_; } \
|
||||
CommandListener* parent_; \
|
||||
#define LogCmd(name, command_string) \
|
||||
class name##Cmd : public LogCommand { \
|
||||
public: \
|
||||
explicit name##Cmd(CommandListener* parent) \
|
||||
: LogCommand(#command_string), parent_(parent) {} \
|
||||
virtual ~name##Cmd() {} \
|
||||
int runCommand(SocketClient* c, int argc, char** argv); \
|
||||
\
|
||||
private: \
|
||||
LogBuffer* buf() const { return parent_->buf_; } \
|
||||
LogTags* tags() const { return parent_->tags_; } \
|
||||
PruneList* prune() const { return parent_->prune_; } \
|
||||
LogStatistics* stats() const { return parent_->stats_; } \
|
||||
CommandListener* parent_; \
|
||||
}
|
||||
|
||||
LogCmd(Clear, clear);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogAudit.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -34,8 +36,6 @@
|
|||
#include <private/android_filesystem_config.h>
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "LogAudit.h"
|
||||
#include "LogBuffer.h"
|
||||
#include "LogKlog.h"
|
||||
#include "LogReader.h"
|
||||
#include "LogUtils.h"
|
||||
|
|
@ -45,16 +45,15 @@
|
|||
'<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
|
||||
'0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
|
||||
|
||||
LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg)
|
||||
LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg, LogStatistics* stats)
|
||||
: SocketListener(getLogSocket(), false),
|
||||
logbuf(buf),
|
||||
reader(reader),
|
||||
fdDmesg(fdDmesg),
|
||||
main(__android_logger_property_get_bool("ro.logd.auditd.main",
|
||||
BOOL_DEFAULT_TRUE)),
|
||||
events(__android_logger_property_get_bool("ro.logd.auditd.events",
|
||||
BOOL_DEFAULT_TRUE)),
|
||||
initialized(false) {
|
||||
main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
|
||||
events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
|
||||
initialized(false),
|
||||
stats_(stats) {
|
||||
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
|
||||
'l',
|
||||
'o',
|
||||
|
|
@ -211,9 +210,7 @@ int LogAudit::logPrint(const char* fmt, ...) {
|
|||
++cp;
|
||||
}
|
||||
tid = pid;
|
||||
logbuf->wrlock();
|
||||
uid = logbuf->pidToUid(pid);
|
||||
logbuf->unlock();
|
||||
uid = stats_->PidToUid(pid);
|
||||
memmove(pidptr, cp, strlen(cp) + 1);
|
||||
}
|
||||
|
||||
|
|
@ -301,9 +298,7 @@ int LogAudit::logPrint(const char* fmt, ...) {
|
|||
pid = tid;
|
||||
comm = "auditd";
|
||||
} else {
|
||||
logbuf->wrlock();
|
||||
comm = commfree = logbuf->pidToName(pid);
|
||||
logbuf->unlock();
|
||||
comm = commfree = stats_->PidToName(pid);
|
||||
if (!comm) {
|
||||
comm = "unknown";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOGD_LOG_AUDIT_H__
|
||||
#define _LOGD_LOG_AUDIT_H__
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <sysutils/SocketListener.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
#include "LogStatistics.h"
|
||||
|
||||
class LogReader;
|
||||
|
||||
|
|
@ -33,14 +33,14 @@ class LogAudit : public SocketListener {
|
|||
bool events;
|
||||
bool initialized;
|
||||
|
||||
public:
|
||||
LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
|
||||
public:
|
||||
LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg, LogStatistics* stats);
|
||||
int log(char* buf, size_t len);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual bool onDataAvailable(SocketClient* cli);
|
||||
|
||||
private:
|
||||
private:
|
||||
static int getLogSocket();
|
||||
std::map<std::string, std::string> populateDenialMap();
|
||||
std::string denialParse(const std::string& denial, char terminator,
|
||||
|
|
@ -48,6 +48,6 @@ class LogAudit : public SocketListener {
|
|||
void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
|
||||
int logPrint(const char* fmt, ...)
|
||||
__attribute__((__format__(__printf__, 2, 3)));
|
||||
};
|
||||
|
||||
#endif
|
||||
LogStatistics* stats_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,8 +63,8 @@ void LogBuffer::init() {
|
|||
LogReaderThread::unlock();
|
||||
}
|
||||
|
||||
LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
|
||||
: mTimes(*times), tags_(tags), prune_(prune) {
|
||||
LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune, LogStatistics* stats)
|
||||
: mTimes(*times), tags_(tags), prune_(prune), stats_(stats) {
|
||||
pthread_rwlock_init(&mLogElementsLock, nullptr);
|
||||
|
||||
log_id_for_each(i) {
|
||||
|
|
@ -197,9 +197,7 @@ int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
|
|||
}
|
||||
if (!__android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE)) {
|
||||
// Log traffic received to total
|
||||
wrlock();
|
||||
stats.addTotal(elem);
|
||||
unlock();
|
||||
stats_->AddTotal(elem);
|
||||
delete elem;
|
||||
return -EACCES;
|
||||
}
|
||||
|
|
@ -308,7 +306,7 @@ int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
|
|||
unlock();
|
||||
return len;
|
||||
}
|
||||
stats.addTotal(currentLast);
|
||||
stats_->AddTotal(currentLast);
|
||||
delete currentLast;
|
||||
swab = total;
|
||||
event->payload.data = htole32(swab);
|
||||
|
|
@ -324,7 +322,7 @@ int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
|
|||
}
|
||||
}
|
||||
if (count) {
|
||||
stats.addTotal(currentLast);
|
||||
stats_->AddTotal(currentLast);
|
||||
currentLast->setDropped(count);
|
||||
}
|
||||
droppedElements[log_id] = currentLast;
|
||||
|
|
@ -355,31 +353,15 @@ int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
|
|||
// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
|
||||
void LogBuffer::log(LogBufferElement* elem) {
|
||||
mLogElements.push_back(elem);
|
||||
stats.add(elem);
|
||||
stats_->Add(elem);
|
||||
maybePrune(elem->getLogId());
|
||||
}
|
||||
|
||||
// Prune at most 10% of the log entries or maxPrune, whichever is less.
|
||||
//
|
||||
// LogBuffer::wrlock() must be held when this function is called.
|
||||
void LogBuffer::maybePrune(log_id_t id) {
|
||||
size_t sizes = stats.sizes(id);
|
||||
unsigned long maxSize = log_buffer_size(id);
|
||||
if (sizes > maxSize) {
|
||||
size_t sizeOver = sizes - ((maxSize * 9) / 10);
|
||||
size_t elements = stats.realElements(id);
|
||||
size_t minElements = elements / 100;
|
||||
if (minElements < minPrune) {
|
||||
minElements = minPrune;
|
||||
}
|
||||
unsigned long pruneRows = elements * sizeOver / sizes;
|
||||
if (pruneRows < minElements) {
|
||||
pruneRows = minElements;
|
||||
}
|
||||
if (pruneRows > maxPrune) {
|
||||
pruneRows = maxPrune;
|
||||
}
|
||||
prune(id, pruneRows);
|
||||
unsigned long prune_rows;
|
||||
if (stats_->ShouldPrune(id, log_buffer_size(id), &prune_rows)) {
|
||||
prune(id, prune_rows);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -452,9 +434,9 @@ LogBufferElementCollection::iterator LogBuffer::erase(
|
|||
}
|
||||
#endif
|
||||
if (coalesce) {
|
||||
stats.erase(element);
|
||||
stats_->Erase(element);
|
||||
} else {
|
||||
stats.subtract(element);
|
||||
stats_->Subtract(element);
|
||||
}
|
||||
delete element;
|
||||
|
||||
|
|
@ -535,7 +517,7 @@ class LogBufferElementLast {
|
|||
// If the selected reader is blocking our pruning progress, decide on
|
||||
// what kind of mitigation is necessary to unblock the situation.
|
||||
void LogBuffer::kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows) {
|
||||
if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
|
||||
if (stats_->Sizes(id) > (2 * log_buffer_size(id))) { // +100%
|
||||
// A misbehaving or slow reader has its connection
|
||||
// dropped if we hit too much memory pressure.
|
||||
android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
|
||||
|
|
@ -663,19 +645,14 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
|
|||
// Calculate threshold as 12.5% of available storage
|
||||
size_t threshold = log_buffer_size(id) / 8;
|
||||
|
||||
if ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) {
|
||||
stats.sortTags(AID_ROOT, (pid_t)0, 2, id)
|
||||
.findWorst(worst, worst_sizes, second_worst_sizes,
|
||||
threshold);
|
||||
if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
|
||||
stats_->WorstTwoTags(threshold, &worst, &worst_sizes, &second_worst_sizes);
|
||||
// per-pid filter for AID_SYSTEM sources is too complex
|
||||
} else {
|
||||
stats.sort(AID_ROOT, (pid_t)0, 2, id)
|
||||
.findWorst(worst, worst_sizes, second_worst_sizes,
|
||||
threshold);
|
||||
stats_->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
|
||||
|
||||
if ((worst == AID_SYSTEM) && prune_->worstPidOfSystemEnabled()) {
|
||||
stats.sortPids(worst, (pid_t)0, 2, id)
|
||||
.findWorst(worstPid, worst_sizes, second_worst_sizes);
|
||||
if (worst == AID_SYSTEM && prune_->worstPidOfSystemEnabled()) {
|
||||
stats_->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -824,7 +801,7 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
|
|||
if (leading) {
|
||||
it = erase(it);
|
||||
} else {
|
||||
stats.drop(element);
|
||||
stats_->Drop(element);
|
||||
element->setDropped(1);
|
||||
if (last.coalesce(element, 1)) {
|
||||
it = erase(it, true);
|
||||
|
|
@ -957,14 +934,6 @@ bool LogBuffer::clear(log_id_t id, uid_t uid) {
|
|||
return busy;
|
||||
}
|
||||
|
||||
// get the used space associated with "id".
|
||||
unsigned long LogBuffer::getSizeUsed(log_id_t id) {
|
||||
rdlock();
|
||||
size_t retval = stats.sizes(id);
|
||||
unlock();
|
||||
return retval;
|
||||
}
|
||||
|
||||
// set the total space allocated to "id"
|
||||
int LogBuffer::setSize(log_id_t id, unsigned long size) {
|
||||
// Reasonable limits ...
|
||||
|
|
@ -1049,7 +1018,7 @@ uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid
|
|||
unlock();
|
||||
|
||||
// range locking in LastLogTimes looks after us
|
||||
curr = element->flushTo(reader, this, sameTid);
|
||||
curr = element->flushTo(reader, stats_, sameTid);
|
||||
|
||||
if (curr == element->FLUSH_ERROR) {
|
||||
return curr;
|
||||
|
|
@ -1061,14 +1030,3 @@ uint64_t LogBuffer::flushTo(SocketClient* reader, uint64_t start, pid_t* lastTid
|
|||
|
||||
return curr;
|
||||
}
|
||||
|
||||
std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
|
||||
unsigned int logMask) {
|
||||
wrlock();
|
||||
|
||||
std::string ret = stats.format(uid, pid, logMask);
|
||||
|
||||
unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ class LogBuffer {
|
|||
LogBufferElementCollection mLogElements;
|
||||
pthread_rwlock_t mLogElementsLock;
|
||||
|
||||
LogStatistics stats;
|
||||
|
||||
// watermark of any worst/chatty uid processing
|
||||
typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
|
||||
LogBufferIteratorMap;
|
||||
|
|
@ -58,7 +56,7 @@ class LogBuffer {
|
|||
public:
|
||||
LastLogTimes& mTimes;
|
||||
|
||||
LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune);
|
||||
LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune, LogStatistics* stats);
|
||||
~LogBuffer();
|
||||
void init();
|
||||
|
||||
|
|
@ -75,22 +73,8 @@ class LogBuffer {
|
|||
bool clear(log_id_t id, uid_t uid = AID_ROOT);
|
||||
unsigned long getSize(log_id_t id);
|
||||
int setSize(log_id_t id, unsigned long size);
|
||||
unsigned long getSizeUsed(log_id_t id);
|
||||
|
||||
std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
|
||||
|
||||
void enableStatistics() {
|
||||
stats.enableStatistics();
|
||||
}
|
||||
|
||||
// helper must be protected directly or implicitly by wrlock()/unlock()
|
||||
const char* pidToName(pid_t pid) {
|
||||
return stats.pidToName(pid);
|
||||
}
|
||||
uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
|
||||
const char* uidToName(uid_t uid) {
|
||||
return stats.uidToName(uid);
|
||||
}
|
||||
private:
|
||||
void wrlock() {
|
||||
pthread_rwlock_wrlock(&mLogElementsLock);
|
||||
}
|
||||
|
|
@ -101,10 +85,6 @@ class LogBuffer {
|
|||
pthread_rwlock_unlock(&mLogElementsLock);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t minPrune = 4;
|
||||
static constexpr size_t maxPrune = 256;
|
||||
|
||||
void maybePrune(log_id_t id);
|
||||
void kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows);
|
||||
|
||||
|
|
@ -118,6 +98,7 @@ class LogBuffer {
|
|||
|
||||
LogTags* tags_;
|
||||
PruneList* prune_;
|
||||
LogStatistics* stats_;
|
||||
|
||||
// Keeps track of the iterator to the oldest log message of a given log type, as an
|
||||
// optimization when pruning logs. Use GetOldest() to retrieve.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogBufferElement.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
|
|
@ -26,7 +28,6 @@
|
|||
#include <private/android_logger.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
#include "LogBufferElement.h"
|
||||
#include "LogCommand.h"
|
||||
#include "LogReader.h"
|
||||
#include "LogUtils.h"
|
||||
|
|
@ -153,7 +154,7 @@ char* android::tidToName(pid_t tid) {
|
|||
}
|
||||
|
||||
// assumption: mMsg == NULL
|
||||
size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent,
|
||||
size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogStatistics* stats,
|
||||
bool lastSame) {
|
||||
static const char tag[] = "chatty";
|
||||
|
||||
|
|
@ -163,17 +164,13 @@ size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent
|
|||
}
|
||||
|
||||
static const char format_uid[] = "uid=%u%s%s %s %u line%s";
|
||||
parent->wrlock();
|
||||
const char* name = parent->uidToName(mUid);
|
||||
parent->unlock();
|
||||
const char* name = stats->UidToName(mUid);
|
||||
const char* commName = android::tidToName(mTid);
|
||||
if (!commName && (mTid != mPid)) {
|
||||
commName = android::tidToName(mPid);
|
||||
}
|
||||
if (!commName) {
|
||||
parent->wrlock();
|
||||
commName = parent->pidToName(mPid);
|
||||
parent->unlock();
|
||||
commName = stats->PidToName(mPid);
|
||||
}
|
||||
if (name && name[0] && commName && (name[0] == commName[0])) {
|
||||
size_t len = strlen(name + 1);
|
||||
|
|
@ -246,7 +243,7 @@ size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent
|
|||
return retval;
|
||||
}
|
||||
|
||||
uint64_t LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
|
||||
uint64_t LogBufferElement::flushTo(SocketClient* reader, LogStatistics* stats, bool lastSame) {
|
||||
struct logger_entry entry = {};
|
||||
|
||||
entry.hdr_size = sizeof(struct logger_entry);
|
||||
|
|
@ -264,7 +261,7 @@ uint64_t LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool
|
|||
char* buffer = nullptr;
|
||||
|
||||
if (mDropped) {
|
||||
entry.len = populateDroppedMessage(buffer, parent, lastSame);
|
||||
entry.len = populateDroppedMessage(buffer, stats, lastSame);
|
||||
if (!entry.len) return mSequence;
|
||||
iovec[1].iov_base = buffer;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <sysutils/SocketClient.h>
|
||||
|
||||
class LogBuffer;
|
||||
class LogStatistics;
|
||||
|
||||
#define EXPIRE_HOUR_THRESHOLD 24 // Only expire chatty UID logs to preserve
|
||||
// non-chatty UIDs less than this age in hours
|
||||
|
|
@ -55,10 +56,9 @@ class __attribute__((packed)) LogBufferElement {
|
|||
static atomic_int_fast64_t sequence;
|
||||
|
||||
// assumption: mDropped == true
|
||||
size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
|
||||
bool lastSame);
|
||||
size_t populateDroppedMessage(char*& buffer, LogStatistics* parent, bool lastSame);
|
||||
|
||||
public:
|
||||
public:
|
||||
LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
|
||||
pid_t tid, const char* msg, uint16_t len);
|
||||
LogBufferElement(const LogBufferElement& elem);
|
||||
|
|
@ -98,5 +98,5 @@ class __attribute__((packed)) LogBufferElement {
|
|||
}
|
||||
|
||||
static const uint64_t FLUSH_ERROR;
|
||||
uint64_t flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
|
||||
uint64_t flushTo(SocketClient* writer, LogStatistics* parent, bool lastSame);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogKlog.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
|
@ -29,7 +31,6 @@
|
|||
#include <private/android_logger.h>
|
||||
|
||||
#include "LogBuffer.h"
|
||||
#include "LogKlog.h"
|
||||
#include "LogReader.h"
|
||||
|
||||
#define KMSG_PRIORITY(PRI) \
|
||||
|
|
@ -201,15 +202,16 @@ log_time LogKlog::correction = (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTO
|
|||
? log_time(log_time::EPOCH)
|
||||
: (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
|
||||
|
||||
LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
|
||||
bool auditd)
|
||||
LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead, bool auditd,
|
||||
LogStatistics* stats)
|
||||
: SocketListener(fdRead, false),
|
||||
logbuf(buf),
|
||||
reader(reader),
|
||||
signature(CLOCK_MONOTONIC),
|
||||
initialized(false),
|
||||
enableLogging(true),
|
||||
auditd(auditd) {
|
||||
auditd(auditd),
|
||||
stats_(stats) {
|
||||
static const char klogd_message[] = "%s%s%" PRIu64 "\n";
|
||||
char buffer[strlen(priority_message) + strlen(klogdStr) +
|
||||
strlen(klogd_message) + 20];
|
||||
|
|
@ -528,9 +530,7 @@ int LogKlog::log(const char* buf, ssize_t len) {
|
|||
const pid_t tid = pid;
|
||||
uid_t uid = AID_ROOT;
|
||||
if (pid) {
|
||||
logbuf->wrlock();
|
||||
uid = logbuf->pidToUid(pid);
|
||||
logbuf->unlock();
|
||||
uid = stats_->PidToUid(pid);
|
||||
}
|
||||
|
||||
// Parse (rules at top) to pull out a tag from the incoming kernel message.
|
||||
|
|
|
|||
|
|
@ -14,12 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOGD_LOG_KLOG_H__
|
||||
#define _LOGD_LOG_KLOG_H__
|
||||
#pragma once
|
||||
|
||||
#include <private/android_logger.h>
|
||||
#include <sysutils/SocketListener.h>
|
||||
|
||||
#include "LogStatistics.h"
|
||||
|
||||
class LogBuffer;
|
||||
class LogReader;
|
||||
|
||||
|
|
@ -38,20 +39,19 @@ class LogKlog : public SocketListener {
|
|||
|
||||
static log_time correction;
|
||||
|
||||
public:
|
||||
LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
|
||||
bool auditd);
|
||||
public:
|
||||
LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead, bool auditd,
|
||||
LogStatistics* stats);
|
||||
int log(const char* buf, ssize_t len);
|
||||
|
||||
static void convertMonotonicToReal(log_time& real) {
|
||||
real += correction;
|
||||
}
|
||||
static void convertMonotonicToReal(log_time& real) { real += correction; }
|
||||
|
||||
protected:
|
||||
log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
|
||||
pid_t sniffPid(const char*& buf, ssize_t len);
|
||||
void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
|
||||
virtual bool onDataAvailable(SocketClient* cli);
|
||||
protected:
|
||||
log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
|
||||
pid_t sniffPid(const char*& buf, ssize_t len);
|
||||
void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
|
||||
virtual bool onDataAvailable(SocketClient* cli);
|
||||
|
||||
private:
|
||||
LogStatistics* stats_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "LogStatistics.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
|
|
@ -27,14 +29,12 @@
|
|||
|
||||
#include <private/android_logger.h>
|
||||
|
||||
#include "LogStatistics.h"
|
||||
|
||||
static const uint64_t hourSec = 60 * 60;
|
||||
static const uint64_t monthSec = 31 * 24 * hourSec;
|
||||
|
||||
size_t LogStatistics::SizesTotal;
|
||||
std::atomic<size_t> LogStatistics::SizesTotal;
|
||||
|
||||
LogStatistics::LogStatistics() : enable(false) {
|
||||
LogStatistics::LogStatistics(bool enable_statistics) : enable(enable_statistics) {
|
||||
log_time now(CLOCK_REALTIME);
|
||||
log_id_for_each(id) {
|
||||
mSizes[id] = 0;
|
||||
|
|
@ -79,7 +79,8 @@ char* pidToName(pid_t pid) {
|
|||
}
|
||||
}
|
||||
|
||||
void LogStatistics::addTotal(LogBufferElement* element) {
|
||||
void LogStatistics::AddTotal(LogBufferElement* element) {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
if (element->getDropped()) return;
|
||||
|
||||
log_id_t log_id = element->getLogId();
|
||||
|
|
@ -89,7 +90,8 @@ void LogStatistics::addTotal(LogBufferElement* element) {
|
|||
++mElementsTotal[log_id];
|
||||
}
|
||||
|
||||
void LogStatistics::add(LogBufferElement* element) {
|
||||
void LogStatistics::Add(LogBufferElement* element) {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
log_id_t log_id = element->getLogId();
|
||||
uint16_t size = element->getMsgLen();
|
||||
mSizes[log_id] += size;
|
||||
|
|
@ -159,7 +161,8 @@ void LogStatistics::add(LogBufferElement* element) {
|
|||
}
|
||||
}
|
||||
|
||||
void LogStatistics::subtract(LogBufferElement* element) {
|
||||
void LogStatistics::Subtract(LogBufferElement* element) {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
log_id_t log_id = element->getLogId();
|
||||
uint16_t size = element->getMsgLen();
|
||||
mSizes[log_id] -= size;
|
||||
|
|
@ -204,7 +207,8 @@ void LogStatistics::subtract(LogBufferElement* element) {
|
|||
|
||||
// Atomically set an entry to drop
|
||||
// entry->setDropped(1) must follow this call, caller should do this explicitly.
|
||||
void LogStatistics::drop(LogBufferElement* element) {
|
||||
void LogStatistics::Drop(LogBufferElement* element) {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
log_id_t log_id = element->getLogId();
|
||||
uint16_t size = element->getMsgLen();
|
||||
mSizes[log_id] -= size;
|
||||
|
|
@ -238,9 +242,13 @@ void LogStatistics::drop(LogBufferElement* element) {
|
|||
tagNameTable.subtract(TagNameKey(element), element);
|
||||
}
|
||||
|
||||
const char* LogStatistics::UidToName(uid_t uid) const {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
return UidToNameLocked(uid);
|
||||
}
|
||||
|
||||
// caller must own and free character string
|
||||
// Requires parent LogBuffer::wrlock() to be held
|
||||
const char* LogStatistics::uidToName(uid_t uid) const {
|
||||
const char* LogStatistics::UidToNameLocked(uid_t uid) const {
|
||||
// Local hard coded favourites
|
||||
if (uid == AID_LOGD) {
|
||||
return strdup("auditd");
|
||||
|
|
@ -297,6 +305,80 @@ const char* LogStatistics::uidToName(uid_t uid) const {
|
|||
return name;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TEntry>
|
||||
void LogStatistics::WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
|
||||
int* worst, size_t* worst_sizes,
|
||||
size_t* second_worst_sizes) const {
|
||||
std::array<const TEntry*, 2> max_entries;
|
||||
table.MaxEntries(AID_ROOT, 0, &max_entries);
|
||||
if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
|
||||
return;
|
||||
}
|
||||
*worst_sizes = max_entries[0]->getSizes();
|
||||
// b/24782000: Allow time horizon to extend roughly tenfold, assume average entry length is
|
||||
// 100 characters.
|
||||
if (*worst_sizes > threshold && *worst_sizes > (10 * max_entries[0]->getDropped())) {
|
||||
*worst = max_entries[0]->getKey();
|
||||
*second_worst_sizes = max_entries[1]->getSizes();
|
||||
if (*second_worst_sizes < threshold) {
|
||||
*second_worst_sizes = threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LogStatistics::WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
|
||||
size_t* second_worst_sizes) const {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
WorstTwoWithThreshold(uidTable[id], threshold, worst, worst_sizes, second_worst_sizes);
|
||||
}
|
||||
|
||||
void LogStatistics::WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
|
||||
size_t* second_worst_sizes) const {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
WorstTwoWithThreshold(tagTable, threshold, worst, worst_sizes, second_worst_sizes);
|
||||
}
|
||||
|
||||
void LogStatistics::WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
|
||||
size_t* second_worst_sizes) const {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
std::array<const PidEntry*, 2> max_entries;
|
||||
pidSystemTable[id].MaxEntries(AID_SYSTEM, 0, &max_entries);
|
||||
if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
*worst = max_entries[0]->getKey();
|
||||
*second_worst_sizes = worst_uid_sizes - max_entries[0]->getSizes() + max_entries[1]->getSizes();
|
||||
}
|
||||
|
||||
// Prune at most 10% of the log entries or maxPrune, whichever is less.
|
||||
bool LogStatistics::ShouldPrune(log_id id, unsigned long max_size,
|
||||
unsigned long* prune_rows) const {
|
||||
static constexpr size_t kMinPrune = 4;
|
||||
static constexpr size_t kMaxPrune = 256;
|
||||
|
||||
auto lock = std::lock_guard{lock_};
|
||||
size_t sizes = mSizes[id];
|
||||
if (sizes <= max_size) {
|
||||
return false;
|
||||
}
|
||||
size_t size_over = sizes - ((max_size * 9) / 10);
|
||||
size_t elements = mElements[id] - mDroppedElements[id];
|
||||
size_t min_elements = elements / 100;
|
||||
if (min_elements < kMinPrune) {
|
||||
min_elements = kMinPrune;
|
||||
}
|
||||
*prune_rows = elements * size_over / sizes;
|
||||
if (*prune_rows < min_elements) {
|
||||
*prune_rows = min_elements;
|
||||
}
|
||||
if (*prune_rows > kMaxPrune) {
|
||||
*prune_rows = kMaxPrune;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
|
||||
bool isprune = worstUidEnabledForLogid(id);
|
||||
return formatLine(android::base::StringPrintf(name.c_str(),
|
||||
|
|
@ -308,10 +390,10 @@ std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
|
|||
}
|
||||
|
||||
// Helper to truncate name, if too long, and add name dressings
|
||||
static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
|
||||
std::string& name, std::string& size, size_t nameLen) {
|
||||
void LogStatistics::FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
|
||||
size_t nameLen) const {
|
||||
const char* allocNameTmp = nullptr;
|
||||
if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
|
||||
if (!nameTmp) nameTmp = allocNameTmp = UidToNameLocked(uid);
|
||||
if (nameTmp) {
|
||||
size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
|
||||
size_t len = EntryBaseConstants::total_len -
|
||||
|
|
@ -332,12 +414,12 @@ static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
|
|||
}
|
||||
}
|
||||
|
||||
std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
|
||||
std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const REQUIRES(stat.lock_) {
|
||||
uid_t uid = getUid();
|
||||
std::string name = android::base::StringPrintf("%u", uid);
|
||||
std::string size = android::base::StringPrintf("%zu", getSizes());
|
||||
|
||||
formatTmp(stat, nullptr, uid, name, size, 6);
|
||||
stat.FormatTmp(nullptr, uid, name, size, 6);
|
||||
|
||||
std::string pruned = "";
|
||||
if (worstUidEnabledForLogid(id)) {
|
||||
|
|
@ -347,9 +429,9 @@ std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
|
|||
it != stat.uidTable[id].end(); ++it) {
|
||||
totalDropped += it->second.getDropped();
|
||||
}
|
||||
size_t sizes = stat.sizes(id);
|
||||
size_t totalSize = stat.sizesTotal(id);
|
||||
size_t totalElements = stat.elementsTotal(id);
|
||||
size_t sizes = stat.mSizes[id];
|
||||
size_t totalSize = stat.mSizesTotal[id];
|
||||
size_t totalElements = stat.mElementsTotal[id];
|
||||
float totalVirtualSize =
|
||||
(float)sizes + (float)totalDropped * totalSize / totalElements;
|
||||
size_t entrySize = getSizes();
|
||||
|
|
@ -401,12 +483,9 @@ std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
|
|||
}
|
||||
|
||||
static const size_t maximum_sorted_entries = 32;
|
||||
std::unique_ptr<const PidEntry* []> sorted =
|
||||
stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries);
|
||||
std::array<const PidEntry*, maximum_sorted_entries> sorted;
|
||||
stat.pidSystemTable[id].MaxEntries(uid, 0, &sorted);
|
||||
|
||||
if (!sorted.get()) {
|
||||
return output;
|
||||
}
|
||||
std::string byPid;
|
||||
size_t index;
|
||||
bool hasDropped = false;
|
||||
|
|
@ -440,14 +519,14 @@ std::string PidEntry::formatHeader(const std::string& name,
|
|||
std::string("BYTES"), std::string("NUM"));
|
||||
}
|
||||
|
||||
std::string PidEntry::format(const LogStatistics& stat,
|
||||
log_id_t /* id */) const {
|
||||
std::string PidEntry::format(const LogStatistics& stat, log_id_t /* id */) const
|
||||
REQUIRES(stat.lock_) {
|
||||
uid_t uid = getUid();
|
||||
pid_t pid = getPid();
|
||||
std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
|
||||
std::string size = android::base::StringPrintf("%zu", getSizes());
|
||||
|
||||
formatTmp(stat, getName(), uid, name, size, 12);
|
||||
stat.FormatTmp(getName(), uid, name, size, 12);
|
||||
|
||||
std::string pruned = "";
|
||||
size_t dropped = getDropped();
|
||||
|
|
@ -465,13 +544,13 @@ std::string TidEntry::formatHeader(const std::string& name,
|
|||
std::string("NUM"));
|
||||
}
|
||||
|
||||
std::string TidEntry::format(const LogStatistics& stat,
|
||||
log_id_t /* id */) const {
|
||||
std::string TidEntry::format(const LogStatistics& stat, log_id_t /* id */) const
|
||||
REQUIRES(stat.lock_) {
|
||||
uid_t uid = getUid();
|
||||
std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
|
||||
std::string size = android::base::StringPrintf("%zu", getSizes());
|
||||
|
||||
formatTmp(stat, getName(), uid, name, size, 12);
|
||||
stat.FormatTmp(getName(), uid, name, size, 12);
|
||||
|
||||
std::string pruned = "";
|
||||
size_t dropped = getDropped();
|
||||
|
|
@ -611,8 +690,36 @@ static std::string formatMsec(uint64_t val) {
|
|||
return output;
|
||||
}
|
||||
|
||||
std::string LogStatistics::format(uid_t uid, pid_t pid,
|
||||
unsigned int logMask) const {
|
||||
template <typename TKey, typename TEntry>
|
||||
std::string LogStatistics::FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid,
|
||||
pid_t pid, const std::string& name, log_id_t id) const
|
||||
REQUIRES(lock_) {
|
||||
static const size_t maximum_sorted_entries = 32;
|
||||
std::string output;
|
||||
std::array<const TEntry*, maximum_sorted_entries> sorted;
|
||||
table.MaxEntries(uid, pid, &sorted);
|
||||
bool header_printed = false;
|
||||
for (size_t index = 0; index < maximum_sorted_entries; ++index) {
|
||||
const TEntry* entry = sorted[index];
|
||||
if (!entry) {
|
||||
break;
|
||||
}
|
||||
if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
|
||||
break;
|
||||
}
|
||||
if (!header_printed) {
|
||||
output += "\n\n";
|
||||
output += entry->formatHeader(name, id);
|
||||
header_printed = true;
|
||||
}
|
||||
output += entry->format(*this, id);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string LogStatistics::Format(uid_t uid, pid_t pid, unsigned int logMask) const {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
|
||||
static const uint16_t spaces_total = 19;
|
||||
|
||||
// Report on total logging, current and for all time
|
||||
|
|
@ -642,9 +749,9 @@ std::string LogStatistics::format(uid_t uid, pid_t pid,
|
|||
if (!(logMask & (1 << id))) continue;
|
||||
oldLength = output.length();
|
||||
if (spaces < 0) spaces = 0;
|
||||
size_t szs = sizesTotal(id);
|
||||
size_t szs = mSizesTotal[id];
|
||||
totalSize += szs;
|
||||
size_t els = elementsTotal(id);
|
||||
size_t els = mElementsTotal[id];
|
||||
totalEls += els;
|
||||
output +=
|
||||
android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
|
||||
|
|
@ -663,11 +770,11 @@ std::string LogStatistics::format(uid_t uid, pid_t pid,
|
|||
log_id_for_each(id) {
|
||||
if (!(logMask & (1 << id))) continue;
|
||||
|
||||
size_t els = elements(id);
|
||||
size_t els = mElements[id];
|
||||
if (els) {
|
||||
oldLength = output.length();
|
||||
if (spaces < 0) spaces = 0;
|
||||
size_t szs = sizes(id);
|
||||
size_t szs = mSizes[id];
|
||||
totalSize += szs;
|
||||
totalEls += els;
|
||||
output +=
|
||||
|
|
@ -749,7 +856,7 @@ std::string LogStatistics::format(uid_t uid, pid_t pid,
|
|||
log_id_for_each(id) {
|
||||
if (!(logMask & (1 << id))) continue;
|
||||
|
||||
size_t els = elements(id);
|
||||
size_t els = mElements[id];
|
||||
if (els) {
|
||||
oldLength = output.length();
|
||||
if (spaces < 0) spaces = 0;
|
||||
|
|
@ -758,7 +865,7 @@ std::string LogStatistics::format(uid_t uid, pid_t pid,
|
|||
((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
|
||||
-sizeof(uint64_t)) +
|
||||
sizeof(std::list<LogBufferElement*>);
|
||||
size_t szs = sizes(id) + els * overhead;
|
||||
size_t szs = mSizes[id] + els * overhead;
|
||||
totalSize += szs;
|
||||
output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
|
||||
spaces -= output.length() - oldLength;
|
||||
|
|
@ -779,39 +886,38 @@ std::string LogStatistics::format(uid_t uid, pid_t pid,
|
|||
|
||||
name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
|
||||
: "Logging for your UID in %s log buffer:";
|
||||
output += uidTable[id].format(*this, uid, pid, name, id);
|
||||
output += FormatTable(uidTable[id], uid, pid, name, id);
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
|
||||
: "Logging for this PID:";
|
||||
output += pidTable.format(*this, uid, pid, name);
|
||||
output += FormatTable(pidTable, uid, pid, name);
|
||||
name = "Chattiest TIDs";
|
||||
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
|
||||
name += ":";
|
||||
output += tidTable.format(*this, uid, pid, name);
|
||||
output += FormatTable(tidTable, uid, pid, name);
|
||||
}
|
||||
|
||||
if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
|
||||
name = "Chattiest events log buffer TAGs";
|
||||
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
|
||||
name += ":";
|
||||
output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
|
||||
output += FormatTable(tagTable, uid, pid, name, LOG_ID_EVENTS);
|
||||
}
|
||||
|
||||
if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
|
||||
name = "Chattiest security log buffer TAGs";
|
||||
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
|
||||
name += ":";
|
||||
output +=
|
||||
securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
|
||||
output += FormatTable(securityTagTable, uid, pid, name, LOG_ID_SECURITY);
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
name = "Chattiest TAGs";
|
||||
if (pid) name += android::base::StringPrintf(" for PID %d", pid);
|
||||
name += ":";
|
||||
output += tagNameTable.format(*this, uid, pid, name);
|
||||
output += FormatTable(tagNameTable, uid, pid, name);
|
||||
}
|
||||
|
||||
return output;
|
||||
|
|
@ -839,12 +945,14 @@ uid_t pidToUid(pid_t pid) {
|
|||
}
|
||||
}
|
||||
|
||||
uid_t LogStatistics::pidToUid(pid_t pid) {
|
||||
uid_t LogStatistics::PidToUid(pid_t pid) {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
return pidTable.add(pid)->second.getUid();
|
||||
}
|
||||
|
||||
// caller must free character string
|
||||
const char* LogStatistics::pidToName(pid_t pid) const {
|
||||
const char* LogStatistics::PidToName(pid_t pid) const {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
// An inconvenient truth ... getName() can alter the object
|
||||
pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
|
||||
const char* name = writablePidTable.add(pid)->second.getName();
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOGD_LOG_STATISTICS_H__
|
||||
#define _LOGD_LOG_STATISTICS_H__
|
||||
#pragma once
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
|
@ -25,12 +24,15 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android/log.h>
|
||||
#include <log/log_time.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
|
@ -79,16 +81,12 @@ class LogHashtable {
|
|||
typedef
|
||||
typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
|
||||
|
||||
std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
|
||||
size_t len) const {
|
||||
if (!len) {
|
||||
std::unique_ptr<const TEntry* []> sorted(nullptr);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
const TEntry** retval = new const TEntry*[len];
|
||||
memset(retval, 0, sizeof(*retval) * len);
|
||||
|
||||
// Returns a sorted array of up to len highest entries sorted by size. If fewer than len
|
||||
// entries are found, their positions are set to nullptr.
|
||||
template <size_t len>
|
||||
void MaxEntries(uid_t uid, pid_t pid, std::array<const TEntry*, len>* out) const {
|
||||
auto& retval = *out;
|
||||
retval.fill(nullptr);
|
||||
for (const_iterator it = map.begin(); it != map.end(); ++it) {
|
||||
const TEntry& entry = it->second;
|
||||
|
||||
|
|
@ -113,8 +111,6 @@ class LogHashtable {
|
|||
retval[index] = &entry;
|
||||
}
|
||||
}
|
||||
std::unique_ptr<const TEntry* []> sorted(retval);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
inline iterator add(const TKey& key, const LogBufferElement* element) {
|
||||
|
|
@ -170,35 +166,6 @@ class LogHashtable {
|
|||
inline const_iterator end() const {
|
||||
return map.end();
|
||||
}
|
||||
|
||||
std::string format(const LogStatistics& stat, uid_t uid, pid_t pid,
|
||||
const std::string& name = std::string(""),
|
||||
log_id_t id = LOG_ID_MAX) const {
|
||||
static const size_t maximum_sorted_entries = 32;
|
||||
std::string output;
|
||||
std::unique_ptr<const TEntry* []> sorted =
|
||||
sort(uid, pid, maximum_sorted_entries);
|
||||
if (!sorted.get()) {
|
||||
return output;
|
||||
}
|
||||
bool headerPrinted = false;
|
||||
for (size_t index = 0; index < maximum_sorted_entries; ++index) {
|
||||
const TEntry* entry = sorted[index];
|
||||
if (!entry) {
|
||||
break;
|
||||
}
|
||||
if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
|
||||
break;
|
||||
}
|
||||
if (!headerPrinted) {
|
||||
output += "\n\n";
|
||||
output += entry->formatHeader(name, id);
|
||||
headerPrinted = true;
|
||||
}
|
||||
output += entry->format(stat, id);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
namespace EntryBaseConstants {
|
||||
|
|
@ -627,84 +594,51 @@ struct TagNameEntry : public EntryBase {
|
|||
std::string format(const LogStatistics& stat, log_id_t id) const;
|
||||
};
|
||||
|
||||
template <typename TEntry>
|
||||
class LogFindWorst {
|
||||
std::unique_ptr<const TEntry* []> sorted;
|
||||
|
||||
public:
|
||||
explicit LogFindWorst(std::unique_ptr<const TEntry* []>&& sorted)
|
||||
: sorted(std::move(sorted)) {
|
||||
}
|
||||
|
||||
void findWorst(int& worst, size_t& worst_sizes, size_t& second_worst_sizes,
|
||||
size_t threshold) {
|
||||
if (sorted.get() && sorted[0] && sorted[1]) {
|
||||
worst_sizes = sorted[0]->getSizes();
|
||||
if ((worst_sizes > threshold)
|
||||
// Allow time horizon to extend roughly tenfold, assume
|
||||
// average entry length is 100 characters.
|
||||
&& (worst_sizes > (10 * sorted[0]->getDropped()))) {
|
||||
worst = sorted[0]->getKey();
|
||||
second_worst_sizes = sorted[1]->getSizes();
|
||||
if (second_worst_sizes < threshold) {
|
||||
second_worst_sizes = threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void findWorst(int& worst, size_t worst_sizes, size_t& second_worst_sizes) {
|
||||
if (sorted.get() && sorted[0] && sorted[1]) {
|
||||
worst = sorted[0]->getKey();
|
||||
second_worst_sizes =
|
||||
worst_sizes - sorted[0]->getSizes() + sorted[1]->getSizes();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Log Statistics
|
||||
class LogStatistics {
|
||||
friend UidEntry;
|
||||
friend PidEntry;
|
||||
friend TidEntry;
|
||||
|
||||
size_t mSizes[LOG_ID_MAX];
|
||||
size_t mElements[LOG_ID_MAX];
|
||||
size_t mDroppedElements[LOG_ID_MAX];
|
||||
size_t mSizesTotal[LOG_ID_MAX];
|
||||
size_t mElementsTotal[LOG_ID_MAX];
|
||||
log_time mOldest[LOG_ID_MAX];
|
||||
log_time mNewest[LOG_ID_MAX];
|
||||
log_time mNewestDropped[LOG_ID_MAX];
|
||||
static size_t SizesTotal;
|
||||
size_t mSizes[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
size_t mElements[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
size_t mDroppedElements[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
size_t mSizesTotal[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
size_t mElementsTotal[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
log_time mOldest[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
log_time mNewest[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
log_time mNewestDropped[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
static std::atomic<size_t> SizesTotal;
|
||||
bool enable;
|
||||
|
||||
// uid to size list
|
||||
typedef LogHashtable<uid_t, UidEntry> uidTable_t;
|
||||
uidTable_t uidTable[LOG_ID_MAX];
|
||||
uidTable_t uidTable[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
|
||||
// pid of system to size list
|
||||
typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
|
||||
pidSystemTable_t pidSystemTable[LOG_ID_MAX];
|
||||
pidSystemTable_t pidSystemTable[LOG_ID_MAX] GUARDED_BY(lock_);
|
||||
|
||||
// pid to uid list
|
||||
typedef LogHashtable<pid_t, PidEntry> pidTable_t;
|
||||
pidTable_t pidTable;
|
||||
pidTable_t pidTable GUARDED_BY(lock_);
|
||||
|
||||
// tid to uid list
|
||||
typedef LogHashtable<pid_t, TidEntry> tidTable_t;
|
||||
tidTable_t tidTable;
|
||||
tidTable_t tidTable GUARDED_BY(lock_);
|
||||
|
||||
// tag list
|
||||
typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
|
||||
tagTable_t tagTable;
|
||||
tagTable_t tagTable GUARDED_BY(lock_);
|
||||
|
||||
// security tag list
|
||||
tagTable_t securityTagTable;
|
||||
tagTable_t securityTagTable GUARDED_BY(lock_);
|
||||
|
||||
// global tag list
|
||||
typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
|
||||
tagNameTable_t tagNameTable;
|
||||
|
||||
size_t sizeOf() const {
|
||||
size_t sizeOf() const REQUIRES(lock_) {
|
||||
size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
|
||||
tagTable.sizeOf() + securityTagTable.sizeOf() +
|
||||
tagNameTable.sizeOf() +
|
||||
|
|
@ -729,62 +663,59 @@ class LogStatistics {
|
|||
return size;
|
||||
}
|
||||
|
||||
public:
|
||||
LogStatistics();
|
||||
public:
|
||||
LogStatistics(bool enable_statistics);
|
||||
|
||||
void enableStatistics() {
|
||||
enable = true;
|
||||
}
|
||||
|
||||
void addTotal(LogBufferElement* entry);
|
||||
void add(LogBufferElement* entry);
|
||||
void subtract(LogBufferElement* entry);
|
||||
void AddTotal(LogBufferElement* entry) EXCLUDES(lock_);
|
||||
void Add(LogBufferElement* entry) EXCLUDES(lock_);
|
||||
void Subtract(LogBufferElement* entry) EXCLUDES(lock_);
|
||||
// entry->setDropped(1) must follow this call
|
||||
void drop(LogBufferElement* entry);
|
||||
void Drop(LogBufferElement* entry) EXCLUDES(lock_);
|
||||
// Correct for coalescing two entries referencing dropped content
|
||||
void erase(LogBufferElement* element) {
|
||||
void Erase(LogBufferElement* element) EXCLUDES(lock_) {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
log_id_t log_id = element->getLogId();
|
||||
--mElements[log_id];
|
||||
--mDroppedElements[log_id];
|
||||
}
|
||||
|
||||
LogFindWorst<UidEntry> sort(uid_t uid, pid_t pid, size_t len, log_id id) {
|
||||
return LogFindWorst<UidEntry>(uidTable[id].sort(uid, pid, len));
|
||||
}
|
||||
LogFindWorst<PidEntry> sortPids(uid_t uid, pid_t pid, size_t len,
|
||||
log_id id) {
|
||||
return LogFindWorst<PidEntry>(pidSystemTable[id].sort(uid, pid, len));
|
||||
}
|
||||
LogFindWorst<TagEntry> sortTags(uid_t uid, pid_t pid, size_t len, log_id) {
|
||||
return LogFindWorst<TagEntry>(tagTable.sort(uid, pid, len));
|
||||
}
|
||||
void WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
|
||||
size_t* second_worst_sizes) const EXCLUDES(lock_);
|
||||
void WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
|
||||
size_t* second_worst_sizes) const EXCLUDES(lock_);
|
||||
void WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
|
||||
size_t* second_worst_sizes) const EXCLUDES(lock_);
|
||||
|
||||
// fast track current value by id only
|
||||
size_t sizes(log_id_t id) const {
|
||||
bool ShouldPrune(log_id id, unsigned long max_size, unsigned long* prune_rows) const
|
||||
EXCLUDES(lock_);
|
||||
|
||||
// Snapshot of the sizes for a given log buffer.
|
||||
size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
|
||||
auto lock = std::lock_guard{lock_};
|
||||
return mSizes[id];
|
||||
}
|
||||
size_t elements(log_id_t id) const {
|
||||
return mElements[id];
|
||||
}
|
||||
size_t realElements(log_id_t id) const {
|
||||
return mElements[id] - mDroppedElements[id];
|
||||
}
|
||||
size_t sizesTotal(log_id_t id) const {
|
||||
return mSizesTotal[id];
|
||||
}
|
||||
size_t elementsTotal(log_id_t id) const {
|
||||
return mElementsTotal[id];
|
||||
}
|
||||
// TODO: Get rid of this entirely.
|
||||
static size_t sizesTotal() {
|
||||
return SizesTotal;
|
||||
}
|
||||
|
||||
std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
|
||||
std::string Format(uid_t uid, pid_t pid, unsigned int logMask) const EXCLUDES(lock_);
|
||||
|
||||
// helper (must be locked directly or implicitly by mLogElementsLock)
|
||||
const char* pidToName(pid_t pid) const;
|
||||
uid_t pidToUid(pid_t pid);
|
||||
const char* uidToName(uid_t uid) const;
|
||||
const char* PidToName(pid_t pid) const EXCLUDES(lock_);
|
||||
uid_t PidToUid(pid_t pid) EXCLUDES(lock_);
|
||||
const char* UidToName(uid_t uid) const EXCLUDES(lock_);
|
||||
|
||||
private:
|
||||
template <typename TKey, typename TEntry>
|
||||
void WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
|
||||
int* worst, size_t* worst_sizes, size_t* second_worst_sizes) const;
|
||||
template <typename TKey, typename TEntry>
|
||||
std::string FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid, pid_t pid,
|
||||
const std::string& name = std::string(""),
|
||||
log_id_t id = LOG_ID_MAX) const REQUIRES(lock_);
|
||||
void FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
|
||||
size_t nameLen) const REQUIRES(lock_);
|
||||
const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_);
|
||||
|
||||
mutable std::mutex lock_;
|
||||
};
|
||||
|
||||
#endif // _LOGD_LOG_STATISTICS_H__
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <log/log_read.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "LogStatistics.h"
|
||||
#include "LogTags.h"
|
||||
#include "LogUtils.h"
|
||||
|
||||
|
|
@ -493,7 +494,7 @@ void LogTags::WritePmsgEventLogTags(uint32_t tag, uid_t uid) {
|
|||
|
||||
// Every 16K (half the smallest configurable pmsg buffer size) record
|
||||
static const size_t rate_to_pmsg = 16 * 1024;
|
||||
if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
|
||||
if (lastTotal && (LogStatistics::sizesTotal() - lastTotal) < rate_to_pmsg) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -663,7 +664,7 @@ void LogTags::WritePersistEventLogTags(uint32_t tag, uid_t uid,
|
|||
}
|
||||
}
|
||||
|
||||
lastTotal = android::sizesTotal();
|
||||
lastTotal = LogStatistics::sizesTotal();
|
||||
if (!lastTotal) ++lastTotal;
|
||||
|
||||
// record totals for next watermark.
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ namespace android {
|
|||
char* uidToName(uid_t uid);
|
||||
void prdebug(const char* fmt, ...) __printflike(1, 2);
|
||||
|
||||
// Furnished in LogStatistics.cpp.
|
||||
size_t sizesTotal();
|
||||
// Caller must own and free returned value
|
||||
char* pidToName(pid_t pid);
|
||||
char* tidToName(pid_t tid);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "../LogBuffer.h"
|
||||
#include "../LogReaderThread.h"
|
||||
#include "../LogStatistics.h"
|
||||
|
||||
// We don't want to waste a lot of entropy on messages
|
||||
#define MAX_MSG_LENGTH 5
|
||||
|
|
@ -36,7 +37,8 @@ struct LogInput {
|
|||
unsigned int log_mask;
|
||||
};
|
||||
|
||||
int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
|
||||
int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer,
|
||||
LogStatistics* stats) {
|
||||
const uint8_t* data = *pdata;
|
||||
const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
|
||||
data += sizeof(LogInput);
|
||||
|
|
@ -71,7 +73,7 @@ int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_
|
|||
log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
|
||||
log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
|
||||
sizeof(uint32_t) + msg_length + 1);
|
||||
log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
|
||||
stats->Format(logInput->uid, logInput->pid, logInput->log_mask);
|
||||
*pdata = data;
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -96,17 +98,17 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
|||
LastLogTimes times;
|
||||
LogTags tags;
|
||||
PruneList prune_list;
|
||||
LogBuffer log_buffer(×, &tags, &prune_list);
|
||||
LogStatistics stats(true);
|
||||
LogBuffer log_buffer(×, &tags, &prune_list, &stats);
|
||||
size_t data_left = size;
|
||||
const uint8_t** pdata = &data;
|
||||
|
||||
log_buffer.enableStatistics();
|
||||
prune_list.init(nullptr);
|
||||
// We want to get pruning code to get called.
|
||||
log_id_for_each(i) { log_buffer.setSize(i, 10000); }
|
||||
|
||||
while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
|
||||
if (!write_log_messages(pdata, &data_left, &log_buffer)) {
|
||||
if (!write_log_messages(pdata, &data_left, &log_buffer, &stats)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#include "LogBuffer.h"
|
||||
#include "LogKlog.h"
|
||||
#include "LogListener.h"
|
||||
#include "LogStatistics.h"
|
||||
#include "LogTags.h"
|
||||
#include "LogUtils.h"
|
||||
|
||||
|
|
@ -272,6 +273,11 @@ int main(int argc, char* argv[]) {
|
|||
LogTags log_tags;
|
||||
// Pruning configuration.
|
||||
PruneList prune_list;
|
||||
// Partial (required for chatty) or full logging statistics.
|
||||
bool enable_full_log_statistics = __android_logger_property_get_bool(
|
||||
"logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
|
||||
BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
|
||||
LogStatistics log_statistics(enable_full_log_statistics);
|
||||
|
||||
// Serves the purpose of managing the last logs times read on a
|
||||
// socket connection, and as a reader lock on a range of log
|
||||
|
|
@ -282,14 +288,7 @@ int main(int argc, char* argv[]) {
|
|||
// LogBuffer is the object which is responsible for holding all
|
||||
// log entries.
|
||||
|
||||
LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list);
|
||||
|
||||
if (__android_logger_property_get_bool(
|
||||
"logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
|
||||
BOOL_DEFAULT_FLAG_ENG |
|
||||
BOOL_DEFAULT_FLAG_SVELTE)) {
|
||||
logBuf->enableStatistics();
|
||||
}
|
||||
LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list, &log_statistics);
|
||||
|
||||
// LogReader listens on /dev/socket/logdr. When a client
|
||||
// connects, log entries in the LogBuffer are written to the client.
|
||||
|
|
@ -311,7 +310,7 @@ int main(int argc, char* argv[]) {
|
|||
// Command listener listens on /dev/socket/logd for incoming logd
|
||||
// administrative commands.
|
||||
|
||||
CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list);
|
||||
CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
|
||||
if (cl->startListener()) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
|
@ -322,16 +321,17 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
LogAudit* al = nullptr;
|
||||
if (auditd) {
|
||||
al = new LogAudit(logBuf, reader,
|
||||
__android_logger_property_get_bool(
|
||||
"ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
|
||||
? fdDmesg
|
||||
: -1);
|
||||
al = new LogAudit(
|
||||
logBuf, reader,
|
||||
__android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
|
||||
? fdDmesg
|
||||
: -1,
|
||||
&log_statistics);
|
||||
}
|
||||
|
||||
LogKlog* kl = nullptr;
|
||||
if (klogd) {
|
||||
kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
|
||||
kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
|
||||
}
|
||||
|
||||
readDmesg(al, kl);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue