Merge "logging: Use more inclusive language."

This commit is contained in:
Tom Cherry 2020-06-16 22:36:15 +00:00 committed by Gerrit Code Review
commit e72b290c8e
15 changed files with 349 additions and 429 deletions

View File

@ -333,14 +333,14 @@ Logd control:
This can individually control each buffer's size with -b.
-S, --statistics Output statistics.
--pid can be used to provide pid specific stats.
-p, --prune Print prune white and ~black list. Service is specified as UID,
UID/PID or /PID. Weighed for quicker pruning if prefix with ~,
otherwise weighed for longevity if unadorned. All other pruning
activity is oldest first. Special case ~! represents an automatic
quicker pruning for the noisiest UID as determined by the current
statistics.
-P, --prune='<list> ...' Set prune white and ~black list, using same format as listed above.
Must be quoted.
-p, --prune Print prune rules. Each rule is specified as UID, UID/PID or /PID. A
'~' prefix indicates that elements matching the rule should be pruned
with higher priority otherwise they're pruned with lower priority. All
other pruning activity is oldest first. Special case ~! represents an
automatic pruning for the noisiest UID as determined by the current
statistics. Special case ~1000/! represents pruning of the worst PID
within AID_SYSTEM when AID_SYSTEM is the noisiest UID.
-P, --prune='<list> ...' Set prune rules, using same format as listed above. Must be quoted.
Filtering:
-s Set default filter to silent. Equivalent to filterspec '*:S'

View File

@ -1301,7 +1301,7 @@ TEST(logcat, blocking_clear) {
}
#endif
static bool get_white_black(char** list) {
static bool get_prune_rules(char** list) {
FILE* fp = popen(logcat_executable " -p 2>/dev/null", "r");
if (fp == NULL) {
fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
@ -1334,7 +1334,7 @@ static bool get_white_black(char** list) {
return *list != NULL;
}
static bool set_white_black(const char* list) {
static bool set_prune_rules(const char* list) {
char buffer[BIG_BUFFER];
snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
list ? list : "");
@ -1363,28 +1363,28 @@ static bool set_white_black(const char* list) {
return pclose(fp) == 0;
}
TEST(logcat, white_black_adjust) {
TEST(logcat, prune_rules_adjust) {
char* list = NULL;
char* adjust = NULL;
get_white_black(&list);
get_prune_rules(&list);
static const char adjustment[] = "~! 300/20 300/25 2000 ~1000/5 ~1000/30";
ASSERT_EQ(true, set_white_black(adjustment));
ASSERT_EQ(true, get_white_black(&adjust));
ASSERT_EQ(true, set_prune_rules(adjustment));
ASSERT_EQ(true, get_prune_rules(&adjust));
EXPECT_STREQ(adjustment, adjust);
free(adjust);
adjust = NULL;
static const char adjustment2[] = "300/20 300/21 2000 ~1000";
ASSERT_EQ(true, set_white_black(adjustment2));
ASSERT_EQ(true, get_white_black(&adjust));
ASSERT_EQ(true, set_prune_rules(adjustment2));
ASSERT_EQ(true, get_prune_rules(&adjust));
EXPECT_STREQ(adjustment2, adjust);
free(adjust);
adjust = NULL;
ASSERT_EQ(true, set_white_black(list));
get_white_black(&adjust);
ASSERT_EQ(true, set_prune_rules(list));
get_prune_rules(&adjust);
EXPECT_STREQ(list ? list : "", adjust ? adjust : "");
free(adjust);
adjust = NULL;

View File

@ -58,8 +58,8 @@ cc_library_static {
"LogReaderThread.cpp",
"LogBufferElement.cpp",
"LogStatistics.cpp",
"LogWhiteBlackList.cpp",
"LogTags.cpp",
"PruneList.cpp",
"SerializedFlushToState.cpp",
"SerializedLogBuffer.cpp",
"SerializedLogChunk.cpp",

View File

@ -298,33 +298,37 @@ class LogBufferElementLast {
// invariably move the logs value down faster as less chatty sources would be
// expired in the noise.
//
// The first loop performs blacklisting and worst offender pruning. Falling
// through when there are no notable worst offenders and have not hit the
// region lock preventing further worst offender pruning. This loop also looks
// after managing the chatty log entries and merging to help provide
// statistical basis for blame. The chatty entries are not a notification of
// how much logs you may have, but instead represent how much logs you would
// have had in a virtual log buffer that is extended to cover all the in-memory
// logs without loss. They last much longer than the represented pruned logs
// since they get multiplied by the gains in the non-chatty log sources.
// The first pass prunes elements that match 3 possible rules:
// 1) A high priority prune rule, for example ~100/20, which indicates elements from UID 100 and PID
// 20 should be pruned in this first pass.
// 2) The default chatty pruning rule, ~!. This rule sums the total size spent on log messages for
// each UID this log buffer. If the highest sum consumes more than 12.5% of the log buffer, then
// these elements from that UID are pruned.
// 3) The default AID_SYSTEM pruning rule, ~1000/!. This rule is a special case to 2), if
// AID_SYSTEM is the top consumer of the log buffer, then this rule sums the total size spent on
// log messages for each PID in AID_SYSTEM in this log buffer and prunes elements from the PID
// with the highest sum.
// This pass reevaluates the sums for rules 2) and 3) for every log message pruned. It creates
// 'chatty' entries for the elements that it prunes and merges related chatty entries together. It
// completes when one of three conditions have been met:
// 1) The requested element count has been pruned.
// 2) There are no elements that match any of these rules.
// 3) A reader is referencing the oldest element that would match these rules.
//
// The second loop get complicated because an algorithm of watermarks and
// history is maintained to reduce the order and keep processing time
// down to a minimum at scale. These algorithms can be costly in the face
// of larger log buffers, or severly limited processing time granted to a
// background task at lowest priority.
// The second pass prunes elements starting from the beginning of the log. It skips elements that
// match any low priority prune rules. It completes when one of three conditions have been met:
// 1) The requested element count has been pruned.
// 2) All elements except those mwatching low priority prune rules have been pruned.
// 3) A reader is referencing the oldest element that would match these rules.
//
// This second loop does straight-up expiration from the end of the logs
// (again, remember for the specified log buffer id) but does some whitelist
// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
// spam filtration all take priority. This second loop also checks if a region
// lock is causing us to buffer too much in the logs to help the reader(s),
// and will tell the slowest reader thread to skip log entries, and if
// persistent and hits a further threshold, kill the reader thread.
//
// The third thread is optional, and only gets hit if there was a whitelist
// and more needs to be pruned against the backstop of the region lock.
// The final pass only happens if there are any low priority prune rules and if the first two passes
// were unable to prune the requested number of elements. It prunes elements all starting from the
// beginning of the log, regardless of if they match any low priority prune rules.
//
// If the requested number of logs was unable to be pruned, KickReader() is called to mitigate the
// situation before the next call to Prune() and the function returns false. Otherwise, if the
// requested number of logs or all logs present in the buffer are pruned, in the case of Clear(),
// it returns true.
bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogReaderThread* oldest = nullptr;
bool clearAll = pruneRows == ULONG_MAX;
@ -370,8 +374,8 @@ bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_u
return true;
}
// prune by worst offenders; by blacklist, UID, and by PID of system UID
bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
// First prune pass.
bool check_high_priority = id != LOG_ID_SECURITY && prune_->HasHighPriorityPruneRules();
while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
int worst = -1; // not valid for uid() or getKey()
@ -379,7 +383,7 @@ bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_u
size_t second_worst_sizes = 0;
pid_t worstPid = 0; // POSIX guarantees PID != 0
if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
if (worstUidEnabledForLogid(id) && prune_->worst_uid_enabled()) {
// Calculate threshold as 12.5% of available storage
size_t threshold = max_size(id) / 8;
@ -389,14 +393,14 @@ bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_u
} else {
stats()->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
if (worst == AID_SYSTEM && prune_->worstPidOfSystemEnabled()) {
if (worst == AID_SYSTEM && prune_->worst_pid_of_system_enabled()) {
stats()->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
}
}
}
// skip if we have neither worst nor naughty filters
if ((worst == -1) && !hasBlacklist) {
// skip if we have neither a worst UID or high priority prune rules
if (worst == -1 && !check_high_priority) {
break;
}
@ -464,7 +468,7 @@ bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_u
int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.GetTag()
: element.uid();
if (hasBlacklist && prune_->naughty(&element)) {
if (check_high_priority && prune_->IsHighPriority(&element)) {
last.clear(&element);
it = Erase(it);
if (dropped) {
@ -557,15 +561,17 @@ bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_u
}
last.clear();
if (!kick || !prune_->worstUidEnabled()) {
if (!kick || !prune_->worst_uid_enabled()) {
break; // the following loop will ask bad clients to skip/drop
}
}
bool whitelist = false;
bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
// Second prune pass.
bool skipped_low_priority_prune = false;
bool check_low_priority =
id != LOG_ID_SECURITY && prune_->HasLowPriorityPruneRules() && !clearAll;
it = GetOldest(id);
while ((pruneRows > 0) && (it != logs().end())) {
while (pruneRows > 0 && it != logs().end()) {
LogBufferElement& element = *it;
if (element.log_id() != id) {
@ -574,13 +580,12 @@ bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_u
}
if (oldest && oldest->start() <= element.sequence()) {
if (!whitelist) KickReader(oldest, id, pruneRows);
if (!skipped_low_priority_prune) KickReader(oldest, id, pruneRows);
break;
}
if (hasWhitelist && !element.dropped_count() && prune_->nice(&element)) {
// WhiteListed
whitelist = true;
if (check_low_priority && !element.dropped_count() && prune_->IsLowPriority(&element)) {
skipped_low_priority_prune = true;
it++;
continue;
}
@ -589,10 +594,10 @@ bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_u
pruneRows--;
}
// Do not save the whitelist if we are reader range limited
if (whitelist && (pruneRows > 0)) {
// Third prune pass.
if (skipped_low_priority_prune && pruneRows > 0) {
it = GetOldest(id);
while ((it != logs().end()) && (pruneRows > 0)) {
while (it != logs().end() && pruneRows > 0) {
LogBufferElement& element = *it;
if (element.log_id() != id) {

View File

@ -33,8 +33,8 @@
#include "LogReaderThread.h"
#include "LogStatistics.h"
#include "LogTags.h"
#include "LogWhiteBlackList.h"
#include "LogWriter.h"
#include "PruneList.h"
#include "SimpleLogBuffer.h"
#include "rwlock.h"

View File

@ -215,15 +215,13 @@ int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc,
return 0;
}
int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
int /*argc*/, char** /*argv*/) {
int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli, int, char**) {
setname();
cli->sendMsg(PackageString(prune()->format()).c_str());
cli->sendMsg(PackageString(prune()->Format()).c_str());
return 0;
}
int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc, char** argv) {
setname();
if (!clientHasLogCredentials(cli)) {
cli->sendMsg("Permission Denied");
@ -238,15 +236,12 @@ int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
str += argv[i];
}
int ret = prune()->init(str.c_str());
if (ret) {
if (!prune()->Init(str.c_str())) {
cli->sendMsg("Invalid");
return 0;
}
cli->sendMsg("success");
return 0;
}
@ -301,7 +296,7 @@ int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
LOG(INFO) << "logd reinit";
buf()->Init();
prune()->init(nullptr);
prune()->Init(nullptr);
// This only works on userdebug and eng devices to re-read the
// /data/misc/logd/event-log-tags file right after /data is mounted.

View File

@ -23,7 +23,7 @@
#include "LogListener.h"
#include "LogStatistics.h"
#include "LogTags.h"
#include "LogWhiteBlackList.h"
#include "PruneList.h"
class CommandListener : public FrameworkListener {
public:

View File

@ -25,7 +25,7 @@
#include "LogReaderList.h"
#include "LogStatistics.h"
#include "LogTags.h"
#include "LogWhiteBlackList.h"
#include "PruneList.h"
#include "SerializedLogBuffer.h"
#include "SimpleLogBuffer.h"

View File

@ -665,10 +665,9 @@ int LogKlog::log(const char* buf, ssize_t len) {
((size == 2) && (isdigit(tag[0]) || isdigit(tag[1]))) ||
// register names like x18 but not driver names like en0
((size == 3) && (isdigit(tag[1]) && isdigit(tag[2]))) ||
// blacklist
// ignore
((size == cpuLen) && !fastcmp<strncmp>(tag, cpu, cpuLen)) ||
((size == warningLen) &&
!fastcmp<strncasecmp>(tag, warning, warningLen)) ||
((size == warningLen) && !fastcmp<strncasecmp>(tag, warning, warningLen)) ||
((size == errorLen) && !fastcmp<strncasecmp>(tag, error, errorLen)) ||
((size == infoLen) && !fastcmp<strncasecmp>(tag, info, infoLen))) {
p = start;

View File

@ -1,265 +0,0 @@
/*
* 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.
*/
#include <ctype.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include "LogWhiteBlackList.h"
Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
}
int Prune::cmp(uid_t uid, pid_t pid) const {
if ((mUid == uid_all) || (mUid == uid)) {
if (mPid == pid_all) {
return 0;
}
return pid - mPid;
}
return uid - mUid;
}
std::string Prune::format() {
if (mUid != uid_all) {
if (mPid != pid_all) {
return android::base::StringPrintf("%u/%u", mUid, mPid);
}
return android::base::StringPrintf("%u", mUid);
}
if (mPid != pid_all) {
return android::base::StringPrintf("/%u", mPid);
}
// NB: mPid == pid_all can not happen if mUid == uid_all
return std::string("/");
}
PruneList::PruneList() {
init(nullptr);
}
PruneList::~PruneList() {
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
it = mNice.erase(it);
}
for (it = mNaughty.begin(); it != mNaughty.end();) {
it = mNaughty.erase(it);
}
}
int PruneList::init(const char* str) {
mWorstUidEnabled = true;
mWorstPidOfSystemEnabled = true;
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
it = mNice.erase(it);
}
for (it = mNaughty.begin(); it != mNaughty.end();) {
it = mNaughty.erase(it);
}
// default here means take ro.logd.filter, persist.logd.filter then
// internal default in that order.
if (str && !strcmp(str, "default")) {
str = nullptr;
}
if (str && !strcmp(str, "disable")) {
str = "";
}
std::string filter;
if (str) {
filter = str;
} else {
filter = android::base::GetProperty("ro.logd.filter", "default");
auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
// default here means take ro.logd.filter
if (persist_filter != "default") {
filter = persist_filter;
}
}
// default here means take internal default.
if (filter == "default") {
// See README.property for description of filter format
filter = "~! ~1000/!";
}
if (filter == "disable") {
filter = "";
}
mWorstUidEnabled = false;
mWorstPidOfSystemEnabled = false;
for (str = filter.c_str(); *str; ++str) {
if (isspace(*str)) {
continue;
}
PruneCollection* list;
if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented
++str;
// special case, translates to worst UID at priority in blacklist
if (*str == '!') {
mWorstUidEnabled = true;
++str;
if (!*str) {
break;
}
if (!isspace(*str)) {
return 1;
}
continue;
}
// special case, translated to worst PID of System at priority
static const char worstPid[] = "1000/!";
if (!strncmp(str, worstPid, sizeof(worstPid) - 1)) {
mWorstPidOfSystemEnabled = true;
str += sizeof(worstPid) - 1;
if (!*str) {
break;
}
if (!isspace(*str)) {
return 1;
}
continue;
}
if (!*str) {
return 1;
}
list = &mNaughty;
} else {
list = &mNice;
}
uid_t uid = Prune::uid_all;
if (isdigit(*str)) {
uid = 0;
do {
uid = uid * 10 + *str++ - '0';
} while (isdigit(*str));
}
pid_t pid = Prune::pid_all;
if (*str == '/') {
++str;
if (isdigit(*str)) {
pid = 0;
do {
pid = pid * 10 + *str++ - '0';
} while (isdigit(*str));
}
}
if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) {
return 1;
}
if (*str && !isspace(*str)) {
return 1;
}
// insert sequentially into list
PruneCollection::iterator it = list->begin();
while (it != list->end()) {
Prune& p = *it;
int m = uid - p.mUid;
if (m == 0) {
if (p.mPid == p.pid_all) {
break;
}
if ((pid == p.pid_all) && (p.mPid != p.pid_all)) {
it = list->erase(it);
continue;
}
m = pid - p.mPid;
}
if (m <= 0) {
if (m < 0) {
list->insert(it, Prune(uid, pid));
}
break;
}
++it;
}
if (it == list->end()) {
list->push_back(Prune(uid, pid));
}
if (!*str) {
break;
}
}
return 0;
}
std::string PruneList::format() {
static const char nice_format[] = " %s";
const char* fmt = nice_format + 1;
std::string string;
if (mWorstUidEnabled) {
string = "~!";
fmt = nice_format;
if (mWorstPidOfSystemEnabled) {
string += " ~1000/!";
}
}
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end(); ++it) {
string += android::base::StringPrintf(fmt, (*it).format().c_str());
fmt = nice_format;
}
static const char naughty_format[] = " ~%s";
fmt = naughty_format + (*fmt != ' ');
for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
string += android::base::StringPrintf(fmt, (*it).format().c_str());
fmt = naughty_format;
}
return string;
}
// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
// If there is scaling issues, resort to a better algorithm than linear
// based on these assumptions.
bool PruneList::naughty(LogBufferElement* element) {
PruneCollection::iterator it;
for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
if (!(*it).cmp(element)) {
return true;
}
}
return false;
}
bool PruneList::nice(LogBufferElement* element) {
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end(); ++it) {
if (!(*it).cmp(element)) {
return true;
}
}
return false;
}

View File

@ -1,81 +0,0 @@
/*
* 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.
*/
#pragma once
#include <sys/types.h>
#include <string.h>
#include <list>
#include "LogBufferElement.h"
class Prune {
friend class PruneList;
const uid_t mUid;
const pid_t mPid;
int cmp(uid_t uid, pid_t pid) const;
public:
static const uid_t uid_all = (uid_t)-1;
static const pid_t pid_all = (pid_t)-1;
Prune(uid_t uid, pid_t pid);
uid_t getUid() const {
return mUid;
}
pid_t getPid() const {
return mPid;
}
int cmp(LogBufferElement* e) const { return cmp(e->uid(), e->pid()); }
std::string format();
};
typedef std::list<Prune> PruneCollection;
class PruneList {
PruneCollection mNaughty;
PruneCollection mNice;
bool mWorstUidEnabled;
bool mWorstPidOfSystemEnabled;
public:
PruneList();
~PruneList();
int init(const char* str);
bool naughty(LogBufferElement* element);
bool naughty(void) {
return !mNaughty.empty();
}
bool nice(LogBufferElement* element);
bool nice(void) {
return !mNice.empty();
}
bool worstUidEnabled() const {
return mWorstUidEnabled;
}
bool worstPidOfSystemEnabled() const {
return mWorstPidOfSystemEnabled;
}
std::string format();
};

201
logd/PruneList.cpp Normal file
View File

@ -0,0 +1,201 @@
/*
* 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.
*/
#include "PruneList.h"
#include <ctype.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
bool Prune::Matches(LogBufferElement* element) const {
return (uid_ == UID_ALL || uid_ == element->uid()) &&
(pid_ == PID_ALL || pid_ == element->pid());
}
std::string Prune::Format() const {
if (uid_ != UID_ALL) {
if (pid_ != PID_ALL) {
return android::base::StringPrintf("%u/%u", uid_, pid_);
}
return android::base::StringPrintf("%u", uid_);
}
if (pid_ != PID_ALL) {
return android::base::StringPrintf("/%u", pid_);
}
// NB: pid_ == PID_ALL can not happen if uid_ == UID_ALL
return std::string("/");
}
PruneList::PruneList() {
Init(nullptr);
}
bool PruneList::Init(const char* str) {
high_priority_prune_.clear();
low_priority_prune_.clear();
// default here means take ro.logd.filter, persist.logd.filter then internal default in order.
if (str && !strcmp(str, "default")) {
str = nullptr;
}
if (str && !strcmp(str, "disable")) {
str = "";
}
std::string filter;
if (str) {
filter = str;
} else {
filter = android::base::GetProperty("ro.logd.filter", "default");
auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
// default here means take ro.logd.filter
if (persist_filter != "default") {
filter = persist_filter;
}
}
// default here means take internal default.
if (filter == "default") {
filter = "~! ~1000/!";
}
if (filter == "disable") {
filter = "";
}
worst_uid_enabled_ = false;
worst_pid_of_system_enabled_ = false;
for (str = filter.c_str(); *str; ++str) {
if (isspace(*str)) {
continue;
}
std::list<Prune>* list;
if (*str == '~' || *str == '!') { // ~ supported, ! undocumented
++str;
// special case, prune the worst UID of those using at least 1/8th of the buffer.
if (*str == '!') {
worst_uid_enabled_ = true;
++str;
if (!*str) {
break;
}
if (!isspace(*str)) {
LOG(ERROR) << "Nothing expected after '~!', but found '" << str << "'";
return false;
}
continue;
}
// special case, translated to worst PID of System at priority
static const char WORST_SYSTEM_PID[] = "1000/!";
if (!strncmp(str, WORST_SYSTEM_PID, sizeof(WORST_SYSTEM_PID) - 1)) {
worst_pid_of_system_enabled_ = true;
str += sizeof(WORST_SYSTEM_PID) - 1;
if (!*str) {
break;
}
if (!isspace(*str)) {
LOG(ERROR) << "Nothing expected after '~1000/!', but found '" << str << "'";
return false;
}
continue;
}
if (!*str) {
LOG(ERROR) << "Expected UID or PID after '~', but found nothing";
return false;
}
list = &high_priority_prune_;
} else {
list = &low_priority_prune_;
}
uid_t uid = Prune::UID_ALL;
if (isdigit(*str)) {
uid = 0;
do {
uid = uid * 10 + *str++ - '0';
} while (isdigit(*str));
}
pid_t pid = Prune::PID_ALL;
if (*str == '/') {
++str;
if (isdigit(*str)) {
pid = 0;
do {
pid = pid * 10 + *str++ - '0';
} while (isdigit(*str));
}
}
if (uid == Prune::UID_ALL && pid == Prune::PID_ALL) {
LOG(ERROR) << "Expected UID/PID combination, but found none";
return false;
}
if (*str && !isspace(*str)) {
LOG(ERROR) << "Nothing expected after UID/PID combination, but found '" << str << "'";
return false;
}
list->emplace_back(uid, pid);
if (!*str) {
break;
}
}
return true;
}
std::string PruneList::Format() const {
std::vector<std::string> prune_rules;
if (worst_uid_enabled_) {
prune_rules.emplace_back("~!");
}
if (worst_pid_of_system_enabled_) {
prune_rules.emplace_back("~1000/!");
}
for (const auto& rule : low_priority_prune_) {
prune_rules.emplace_back(rule.Format());
}
for (const auto& rule : high_priority_prune_) {
prune_rules.emplace_back("~" + rule.Format());
}
return android::base::Join(prune_rules, " ");
}
bool PruneList::IsHighPriority(LogBufferElement* element) const {
for (const auto& rule : high_priority_prune_) {
if (rule.Matches(element)) {
return true;
}
}
return false;
}
bool PruneList::IsLowPriority(LogBufferElement* element) const {
for (const auto& rule : low_priority_prune_) {
if (rule.Matches(element)) {
return true;
}
}
return false;
}

66
logd/PruneList.h Normal file
View File

@ -0,0 +1,66 @@
/*
* 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.
*/
#pragma once
#include <sys/types.h>
#include <string.h>
#include <list>
#include "LogBufferElement.h"
class Prune {
public:
static const uid_t UID_ALL = (uid_t)-1;
static const pid_t PID_ALL = (pid_t)-1;
Prune(uid_t uid, pid_t pid) : uid_(uid), pid_(pid) {}
bool Matches(LogBufferElement* element) const;
std::string Format() const;
uid_t uid() const { return uid_; }
pid_t pid() const { return pid_; }
private:
const uid_t uid_;
const pid_t pid_;
};
class PruneList {
public:
PruneList();
bool Init(const char* str);
std::string Format() const;
bool IsHighPriority(LogBufferElement* element) const;
bool IsLowPriority(LogBufferElement* element) const;
bool HasHighPriorityPruneRules() const { return !high_priority_prune_.empty(); }
bool HasLowPriorityPruneRules() const { return !low_priority_prune_.empty(); }
bool worst_uid_enabled() const { return worst_uid_enabled_; }
bool worst_pid_of_system_enabled() const { return worst_pid_of_system_enabled_; }
private:
std::list<Prune> high_priority_prune_;
std::list<Prune> low_priority_prune_;
bool worst_uid_enabled_;
bool worst_pid_of_system_enabled_;
};

View File

@ -65,8 +65,8 @@ NB:
- number - support multipliers (K or M) for convenience. Range is limited
to between 64K and 256M for log buffer sizes. Individual log buffer ids
such as main, system, ... override global default.
- Pruning filter is of form of a space-separated list of [~][UID][/PID]
references, where '~' prefix means to blacklist otherwise whitelist. For
blacklisting, UID or PID may be a '!' to instead reference the chattiest
client, with the restriction that the PID must be in the UID group 1000
(system or AID_SYSTEM).
- Pruning filter rules are specified as UID, UID/PID or /PID. A '~' prefix indicates that elements
matching the rule should be pruned with higher priority otherwise they're pruned with lower
priority. All other pruning activity is oldest first. Special case ~! represents an automatic
pruning for the noisiest UID as determined by the current statistics. Special case ~1000/!
represents pruning of the worst PID within AID_SYSTEM when AID_SYSTEM is the noisiest UID.

View File

@ -97,7 +97,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
size_t data_left = size;
const uint8_t** pdata = &data;
prune_list.init(nullptr);
prune_list.Init(nullptr);
// We want to get pruning code to get called.
log_id_for_each(i) { log_buffer->SetSize(i, 10000); }