Adb日志
想查看Android端侧的日志信息输出,一般采用命令行 adb logcat
或Android Stadio中Logcat插件的方式.最近要实现一个Android App的自动测试,所以需要使用真机连接到服务器上运行,因此更多使用命令行输出Logcat的形式.
但是Adb Logcat的输出中只包含pid\tid等信息,不包含包名等属性,导致无法过滤来自指定包名所有Activity的log.在Github上翻了下,好像也没有支持Package Name过滤的Logcat客户端.因此决定自己改一个出来.
如何获取Package Name?
这个问题的解答可能还要从AS的Logcat Plugin的源码入手.这部分功能在Plugin中被称为 ProcessMonitor
.具体的代码仓库位于这里.这个功能由两部分组成,运行在端侧的Agent(C++编写),运行在Server端的AgentTracker(来采集并缓存ProcessName与Pid的映射)以及管理Agent运行.
端侧的Agent代码十分简单,只是一个loop来从procfs中获取进程信息,并从cmdline文件中截断出文件名.在loop中不断轮询/proc
下所有pid,并与缓存的pid set进行比较,来输出pid新增或减少情况.Server端的Tracker根据Agent的输出来更新pid与ProcessName(在Zygote启动的Java App子进程中,这应当是包名).
/**
* Reads processes from "/proc" and retrieves their name from
* "/proc/<pid>/cmdline" and prints: When a process is added: "+ <pid>
* <process-name>" When a process is removed: "- <pid>" New processes are
* added to the processes set and processes that not longer exists are removed
* from it.
*/
void scanProcesses() {
// Whatever is left in this set needs to be removed from ::processes
set<int> markedForRemoval(processes);
DIR* pDir = opendir("/proc");
while (true) {
dirent* pDirent = readdir(pDir);
if (pDirent == nullptr) {
break;
}
int pid = parseInt(pDirent->d_name, -1);
// Ignore entries that are not a valid pid
if (pid < 0) {
continue;
}
markedForRemoval.erase(pid);
if (processes.find(pid) != processes.end()) {
continue;
}
// New process found
string path = string("/proc/") + pDirent->d_name;
const string& name = readCommand(path);
if (name.empty() || startsWith(name, "zygote") ||
name == "<pre-initialized>") {
// Ignore processes without a name or that haven't initialized yet
continue;
}
processes.insert(pid);
printf("+ %d %s\n", pid, name.c_str());
}
closedir(pDir);
for (const int pid : markedForRemoval) {
processes.erase(pid);
printf("- %d\n", pid);
}
::fflush(stdout);
}
这样实现确实比较简单,但是感觉并不算优雅.AS作为第一方的App可以采用这种设计,但是如果要设计一个第三方实现,在端侧启动一个Forever Loop应用程序似乎并不太合适.翻了下老版本的实现,发现有注释提到可以用 ps aux
来提取ProcessName.(这样的另一个好处是避免了权限问题,而且某些命令行服务在cmdline文件中可能是以全路径的格式保存,还要进一步分割).
成品
在Github上翻找一通,只找到了一个6年前的Rust项目rogcat
大致符合我的要求,奈何依赖库太老,甚至Tokio都还没有正式版.按照他的思路(使用nom处理logcat的原输出,Stream处理流式输出)仿了一个精简版:
如果你感兴趣,也可以在这里找到这个简陋的项目.