llkd: enhance list properties
Because of the limited length of properties, and to ease the
complexity of product and vendor adjustments, the comma separated
list properties will use a leading comma to preserve the defaults
and add or subtract entries with + and - prefixes respectively.
Without the leading comma, the list is explicitly specified as before.
Cleanup:
- use empty() instead of space() == 0 (or converse if != 0)
- if (unlikely) pprocp can not be allocated, to a to_string(ppid) check
For testing, observe before and after llkd_unit_test below to
confirm leading comma effects for example:
livelock: ro.llk.stack=wait_on_page_bit_killable,bit_wait_io,\
__get_user_pages,cma_alloc
livelock: ro.llk.stack=...,SyS_openat,...
Test: llkd_unit_test
Bug: 120983740
Change-Id: Ia3d164c2fdac5295a474c6c1294a34e4ae9d0b61
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 7f47fc9..3a593ec 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -24,6 +24,7 @@
#include <pwd.h> // getpwuid()
#include <signal.h>
#include <stdint.h>
+#include <string.h>
#include <sys/cdefs.h> // ___STRING, __predict_true() and _predict_false()
#include <sys/mman.h> // mlockall()
#include <sys/prctl.h>
@@ -617,17 +618,24 @@
std::string llkFormat(const std::unordered_set<std::string>& blacklist) {
std::string ret;
for (const auto& entry : blacklist) {
- if (ret.size()) {
- ret += ",";
- }
+ if (!ret.empty()) ret += ",";
ret += entry;
}
return ret;
}
+// This function parses the properties as a list, incorporating the supplied
+// default. A leading comma separator means preserve the defaults and add
+// entries (with an optional leading + sign), or removes entries with a leading
+// - sign.
+//
// We only officially support comma separators, but wetware being what they
// are will take some liberty and I do not believe they should be punished.
-std::unordered_set<std::string> llkSplit(const std::string& s) {
+std::unordered_set<std::string> llkSplit(const std::string& prop, const std::string& def) {
+ auto s = android::base::GetProperty(prop, def);
+ constexpr char separators[] = ", \t:;";
+ if (!s.empty() && (s != def) && strchr(separators, s[0])) s = def + s;
+
std::unordered_set<std::string> result;
// Special case, allow boolean false to empty the list, otherwise expected
@@ -637,9 +645,29 @@
size_t base = 0;
while (s.size() > base) {
- auto found = s.find_first_of(", \t:", base);
- // Only emplace content, empty entries are not an option
- if (found != base) result.emplace(s.substr(base, found - base));
+ auto found = s.find_first_of(separators, base);
+ // Only emplace unique content, empty entries are not an option
+ if (found != base) {
+ switch (s[base]) {
+ case '-':
+ ++base;
+ if (base >= s.size()) break;
+ if (base != found) {
+ auto have = result.find(s.substr(base, found - base));
+ if (have != result.end()) result.erase(have);
+ }
+ break;
+ case '+':
+ ++base;
+ if (base >= s.size()) break;
+ if (base == found) break;
+ // FALLTHRU (for gcc, lint, pcc, etc; following for clang)
+ FALLTHROUGH_INTENDED;
+ default:
+ result.emplace(s.substr(base, found - base));
+ break;
+ }
+ }
if (found == s.npos) break;
base = found + 1;
}
@@ -648,13 +676,21 @@
bool llkSkipName(const std::string& name,
const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
- if ((name.size() == 0) || (blacklist.size() == 0)) {
- return false;
- }
+ if (name.empty() || blacklist.empty()) return false;
return blacklist.find(name) != blacklist.end();
}
+bool llkSkipProc(proc* procp,
+ const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
+ if (!procp) return false;
+ if (llkSkipName(std::to_string(procp->pid), blacklist)) return true;
+ if (llkSkipName(procp->getComm(), blacklist)) return true;
+ if (llkSkipName(procp->getCmdline(), blacklist)) return true;
+ if (llkSkipName(android::base::Basename(procp->getCmdline()), blacklist)) return true;
+ return false;
+}
+
bool llkSkipPid(pid_t pid) {
return llkSkipName(std::to_string(pid), llkBlacklistProcess);
}
@@ -730,11 +766,7 @@
}
// Don't check process that are known to block ptrace, save sepolicy noise.
- if (llkSkipName(std::to_string(procp->pid), llkBlacklistStack)) return false;
- if (llkSkipName(procp->getComm(), llkBlacklistStack)) return false;
- if (llkSkipName(procp->getCmdline(), llkBlacklistStack)) return false;
- if (llkSkipName(android::base::Basename(procp->getCmdline()), llkBlacklistStack)) return false;
-
+ if (llkSkipProc(procp, llkBlacklistStack)) return false;
auto kernel_stack = ReadFile(piddir + "/stack");
if (kernel_stack.empty()) {
LOG(VERBOSE) << piddir << "/stack empty comm=" << procp->getComm()
@@ -780,12 +812,12 @@
// but if there are problems we assume at least a few
// samples of reads occur before we take any real action.
std::string schedString = ReadFile(piddir + "/sched");
- if (schedString.size() == 0) {
+ if (schedString.empty()) {
// /schedstat is not as standardized, but in 3.1+
// Android devices, the third field is nr_switches
// from /sched:
schedString = ReadFile(piddir + "/schedstat");
- if (schedString.size() == 0) {
+ if (schedString.empty()) {
return;
}
auto val = static_cast<unsigned long long>(-1);
@@ -943,7 +975,7 @@
// Get the process stat
std::string stat = ReadFile(piddir + "/stat");
- if (stat.size() == 0) {
+ if (stat.empty()) {
continue;
}
unsigned tid = -1;
@@ -1032,11 +1064,10 @@
if (pprocp == nullptr) {
pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
}
- if ((pprocp != nullptr) &&
- (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
- llkSkipName(pprocp->getCmdline(), llkBlacklistParent) ||
- llkSkipName(android::base::Basename(pprocp->getCmdline()), llkBlacklistParent))) {
- break;
+ if (pprocp) {
+ if (llkSkipProc(pprocp, llkBlacklistParent)) break;
+ } else {
+ if (llkSkipName(std::to_string(ppid), llkBlacklistParent)) break;
}
if ((llkBlacklistUid.size() != 0) && llkSkipUid(procp->getUid())) {
@@ -1135,21 +1166,15 @@
if (!p->second.updated) {
IF_ALOG(LOG_VERBOSE, LOG_TAG) {
std::string ppidCmdline = llkProcGetName(p->second.ppid, nullptr, nullptr);
- if (ppidCmdline.size()) {
- ppidCmdline = "(" + ppidCmdline + ")";
- }
+ if (!ppidCmdline.empty()) ppidCmdline = "(" + ppidCmdline + ")";
std::string pidCmdline;
if (p->second.pid != p->second.tid) {
pidCmdline = llkProcGetName(p->second.pid, nullptr, p->second.getCmdline());
- if (pidCmdline.size()) {
- pidCmdline = "(" + pidCmdline + ")";
- }
+ if (!pidCmdline.empty()) pidCmdline = "(" + pidCmdline + ")";
}
std::string tidCmdline =
llkProcGetName(p->second.tid, p->second.getComm(), p->second.getCmdline());
- if (tidCmdline.size()) {
- tidCmdline = "(" + tidCmdline + ")";
- }
+ if (!tidCmdline.empty()) tidCmdline = "(" + tidCmdline + ")";
LOG(VERBOSE) << "thread " << p->second.ppid << ppidCmdline << "->" << p->second.pid
<< pidCmdline << "->" << p->second.tid << tidCmdline << " removed";
}
@@ -1226,13 +1251,11 @@
llkValidate(); // validate all (effectively minus llkTimeoutMs)
#ifdef __PTRACE_ENABLED__
if (debuggable) {
- llkCheckStackSymbols = llkSplit(
- android::base::GetProperty(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT));
+ llkCheckStackSymbols = llkSplit(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT);
}
std::string defaultBlacklistStack(LLK_BLACKLIST_STACK_DEFAULT);
if (!debuggable) defaultBlacklistStack += ",logd,/system/bin/logd";
- llkBlacklistStack = llkSplit(
- android::base::GetProperty(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack));
+ llkBlacklistStack = llkSplit(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack);
#endif
std::string defaultBlacklistProcess(
std::to_string(kernelPid) + "," + std::to_string(initPid) + "," +
@@ -1244,17 +1267,14 @@
for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {
defaultBlacklistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
}
- defaultBlacklistProcess =
- android::base::GetProperty(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
- llkBlacklistProcess = llkSplit(defaultBlacklistProcess);
+ llkBlacklistProcess = llkSplit(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
if (!llkSkipName("[khungtaskd]")) { // ALWAYS ignore as special
llkBlacklistProcess.emplace("[khungtaskd]");
}
- llkBlacklistParent = llkSplit(android::base::GetProperty(
- LLK_BLACKLIST_PARENT_PROPERTY, std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
- "," LLK_BLACKLIST_PARENT_DEFAULT));
- llkBlacklistUid =
- llkSplit(android::base::GetProperty(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT));
+ llkBlacklistParent = llkSplit(LLK_BLACKLIST_PARENT_PROPERTY,
+ std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
+ "," LLK_BLACKLIST_PARENT_DEFAULT);
+ llkBlacklistUid = llkSplit(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT);
// internal watchdog
::signal(SIGALRM, llkAlarmHandler);