Replace binderdebug regex
It's incredibly slow!
Walking through the text using android-base/strings.h and std::string
operations is much faster.
We are making the same assumptions about the format of the files as we
were with regex, but we have fewer hard requirements for the format now.
See bug for detailed timing results with simpleperf.
Test: atest binderdebug_tests
Bug: 270596767
Change-Id: I674414504050371083aad7a93714114190e8a2b4
diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp
index d086b49..a8f2cbf 100644
--- a/libs/binderdebug/BinderDebug.cpp
+++ b/libs/binderdebug/BinderDebug.cpp
@@ -48,14 +48,12 @@
return -errno;
}
}
- static const std::regex kContextLine("^context (\\w+)$");
bool isDesiredContext = false;
std::string line;
- std::smatch match;
while (getline(ifs, line)) {
- if (std::regex_search(line, match, kContextLine)) {
- isDesiredContext = match.str(1) == contextName;
+ if (base::StartsWith(line, "context")) {
+ isDesiredContext = base::Split(line, " ").back() == contextName;
continue;
}
if (!isDesiredContext) {
@@ -66,57 +64,72 @@
return OK;
}
+// Examples of what we are looking at:
+// node 66730: u00007590061890e0 c0000759036130950 pri 0:120 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 tr 1 proc 2300 1790
+// thread 2999: l 00 need_return 1 tr 0
status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo) {
std::smatch match;
static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
std::string contextStr = contextToString(context);
status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
- if (std::regex_search(line, match, kReferencePrefix)) {
- const std::string& ptrString = "0x" + match.str(2); // use number after c
- uint64_t ptr;
- if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
- // Should not reach here, but just be tolerant.
- return;
- }
- const std::string proc = " proc ";
- auto pos = line.rfind(proc);
- if (pos != std::string::npos) {
- for (const std::string& pidStr : base::Split(line.substr(pos + proc.size()), " ")) {
- int32_t pid;
- if (!::android::base::ParseInt(pidStr, &pid)) {
+ if (base::StartsWith(line, " node")) {
+ std::vector<std::string> splitString = base::Tokenize(line, " ");
+ bool pids = false;
+ uint64_t ptr = 0;
+ for (const auto& token : splitString) {
+ if (base::StartsWith(token, "u")) {
+ const std::string ptrString = "0x" + token.substr(1);
+ if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+ LOG(ERROR) << "Failed to parse pointer: " << ptrString;
return;
}
- pidInfo->refPids[ptr].push_back(pid);
+ } else {
+ // The last numbers in the line after "proc" are all client PIDs
+ if (token == "proc") {
+ pids = true;
+ } else if (pids) {
+ int32_t pid;
+ if (!::android::base::ParseInt(token, &pid)) {
+ LOG(ERROR) << "Failed to parse pid int: " << token;
+ return;
+ }
+ if (ptr == 0) {
+ LOG(ERROR) << "We failed to parse the pointer, so we can't add the refPids";
+ return;
+ }
+ pidInfo->refPids[ptr].push_back(pid);
+ }
}
}
+ } else if (base::StartsWith(line, " thread")) {
+ auto pos = line.find("l ");
+ if (pos != std::string::npos) {
+ // "1" is waiting in binder driver
+ // "2" is poll. It's impossible to tell if these are in use.
+ // and HIDL default code doesn't use it.
+ bool isInUse = line.at(pos + 2) != '1';
+ // "0" is a thread that has called into binder
+ // "1" is looper thread
+ // "2" is main looper thread
+ bool isBinderThread = line.at(pos + 3) != '0';
+ if (!isBinderThread) {
+ return;
+ }
+ if (isInUse) {
+ pidInfo->threadUsage++;
+ }
- return;
- }
- if (std::regex_search(line, match, kThreadPrefix)) {
- // "1" is waiting in binder driver
- // "2" is poll. It's impossible to tell if these are in use.
- // and HIDL default code doesn't use it.
- bool isInUse = match.str(1) != "1";
- // "0" is a thread that has called into binder
- // "1" is looper thread
- // "2" is main looper thread
- bool isBinderThread = match.str(2) != "0";
- if (!isBinderThread) {
- return;
+ pidInfo->threadCount++;
}
- if (isInUse) {
- pidInfo->threadUsage++;
- }
-
- pidInfo->threadCount++;
- return;
}
- return;
});
return ret;
}
+// Examples of what we are looking at:
+// ref 52493: desc 910 node 52492 s 1 w 1 d 0000000000000000
+// node 29413: u00007803fc982e80 c000078042c982210 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 tr 1 proc 488 683
status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid,
int32_t handle, std::vector<pid_t>* pids) {
std::smatch match;
@@ -124,51 +137,64 @@
std::string contextStr = contextToString(context);
int32_t node;
status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
- if (std::regex_search(line, match, kNodeNumber)) {
- const std::string& descString = match.str(1);
- int32_t desc;
- if (!::android::base::ParseInt(descString.c_str(), &desc)) {
- LOG(ERROR) << "Failed to parse desc int: " << descString;
- return;
- }
- if (handle != desc) {
- return;
- }
- const std::string& nodeString = match.str(2);
- if (!::android::base::ParseInt(nodeString.c_str(), &node)) {
- LOG(ERROR) << "Failed to parse node int: " << nodeString;
- return;
- }
+ if (!base::StartsWith(line, " ref")) return;
+
+ std::vector<std::string> splitString = base::Tokenize(line, " ");
+ if (splitString.size() < 12) {
+ LOG(ERROR) << "Failed to parse binder_logs ref entry. Expecting size greater than 11, but got: " << splitString.size();
return;
}
- return;
+ int32_t desc;
+ if (!::android::base::ParseInt(splitString[3].c_str(), &desc)) {
+ LOG(ERROR) << "Failed to parse desc int: " << splitString[3];
+ return;
+ }
+ if (handle != desc) {
+ return;
+ }
+ if (!::android::base::ParseInt(splitString[5].c_str(), &node)) {
+ LOG(ERROR) << "Failed to parse node int: " << splitString[5];
+ return;
+ }
+ LOG(INFO) << "Parsed the node: " << node;
});
if (ret != OK) {
return ret;
}
- static const std::regex kClients("^\\s+node\\s+(\\d+).*proc\\s+([\\d+\\s*]*)");
+
ret = scanBinderContext(servicePid, contextStr, [&](const std::string& line) {
- if (std::regex_search(line, match, kClients)) {
- const std::string nodeString = match.str(1);
- int32_t matchedNode;
- if (!::android::base::ParseInt(nodeString.c_str(), &matchedNode)) {
- LOG(ERROR) << "Failed to parse node int: " << nodeString;
- return;
- }
- if (node != matchedNode) {
- return;
- }
- const std::string clients = match.str(2);
- for (const std::string& pidStr : base::Split(clients, " ")) {
+ if (!base::StartsWith(line, " node")) return;
+
+ std::vector<std::string> splitString = base::Tokenize(line, " ");
+ if (splitString.size() < 21) {
+ LOG(ERROR) << "Failed to parse binder_logs node entry. Expecting size greater than 20, but got: " << splitString.size();
+ return;
+ }
+
+ // remove the colon
+ const std::string nodeString = splitString[1].substr(0, splitString[1].size() - 1);
+ int32_t matchedNode;
+ if (!::android::base::ParseInt(nodeString.c_str(), &matchedNode)) {
+ LOG(ERROR) << "Failed to parse node int: " << nodeString;
+ return;
+ }
+
+ if (node != matchedNode) {
+ return;
+ }
+ bool pidsSection = false;
+ for (const auto& token : splitString) {
+ if (token == "proc") {
+ pidsSection = true;
+ } else if (pidsSection == true) {
int32_t pid;
- if (!::android::base::ParseInt(pidStr, &pid)) {
+ if (!::android::base::ParseInt(token.c_str(), &pid)) {
+ LOG(ERROR) << "Failed to parse PID int: " << token;
return;
}
pids->push_back(pid);
}
- return;
}
- return;
});
return ret;
}