版权声明:本文为作者原创,转载必须注明出处。
转载请注明出处://www.greatytc.com/p/2759436e2040
基于Android 8.0,来分析Android系统中Bugreport的实现方式
一、概述
Bugreport包含设备日志,堆栈跟踪和其他诊断信息,可帮助您查找和修复应用中的错误。您可以使用设备上的Take bug report开发人员选项,Android Emulator菜单或adb bugreport开发计算机上的命令从设备捕获错误报告。要获取错误报告,您必须 在设备上启用“ 开发者”选项,以便可以访问“ 获取错误”报告选项。
获取Bugreport的方式有如下几种:
1. 要直接从您的设备获取错误报告,请执行以下操作:
- 确保已启用开发者选项。
- 在Developer选项中,点击Take bug report。
- 选择所需的错误报告类型,然后点击“报告”。
片刻之后,您会收到错误报告准备就绪的通知。 - 要分享错误报告,请点按通知。
2. 使用adb捕获
adb bugreport > bugreport.txt
3. 手机厂商暗码调用
小米手机:
手机拨号键盘输入:*#*#284#*#*
对于Android系统调试分析,bugreport信息量非常之大,几乎涵盖整个系统各个层面内容,对于分析BUG是一大利器,本文先从从源码角度来分析一下Bugreport的实现原理。
二、原理分析
所涉及到的类如下:
framework/native/cmds/bugreport/bugreport.cpp
framework/native/cmds/dumpstate/dumpstate.cpp
framework/native/cmds/dumpstate/utils.c
Android 系统源码中framework/native/cmds/bugreport目录通过Android.bp定义了bugreport项目,在系统编译完成后会生成bugreport的可执行文件,位于系统/system/bin/bugreport.当执行adb bugreport
时,便会调用到这个可执行文件,进入bugreport.cpp中的main函数。
Android.bp
cc_binary {
name: "bugreport",
srcs: ["bugreport.cpp"],
cflags: ["-Wall"],
shared_libs: ["libcutils"],
}
2.1 bugreport.main()函数
bugreport.cpp
// This program will trigger the dumpstate service to start a call to
// dumpstate, then connect to the dumpstate local client to read the
// output. All of the dumpstate output is written to stdout, including
// any errors encountered while reading/writing the output.
int main() {
fprintf(stderr, "=============================================================================\n");
fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport <zip_file> instead\n");
fprintf(stderr, "=============================================================================\n\n\n");
// 启动dumpstate服务.
property_set("ctl.start", "dumpstate");
// 多次尝试socket连接,直到dumpstate服务启动完成,才正式建立socket连接
int s;
for (int i = 0; i < 20; i++) {
s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if (s >= 0)
break;
// Try again in 1 second.
sleep(1);
}
if (s == -1) {
printf("Failed to connect to dumpstate service: %s\n", strerror(errno));
return 1;
}
//当3分钟没有任何数据可读,则超时停止读取并退出。
//dumpstate服务中不存在大于1分钟的timetout,因而不可预见的超时的情况下留有很大的回旋余地。
struct timeval tv;
tv.tv_sec = 3 * 60;
tv.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
}
while (1) {
char buffer[65536];
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
if (bytes_read == 0) {
break;
} else if (bytes_read == -1) {
// EAGAIN 意味着timeout,Bugreport读取异常然后终止打印如下错误信息。
if (errno == EAGAIN) {
errno = ETIMEDOUT;
}
printf("\nBugreport read terminated abnormally (%s).\n", strerror(errno));
break;
}
ssize_t bytes_to_send = bytes_read;
ssize_t bytes_written;
//不断循环得将读取数据输出到stdout
do {
bytes_written = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
buffer + bytes_read - bytes_to_send,
bytes_to_send));
if (bytes_written == -1) {
printf("Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
bytes_read, bytes_to_send, strerror(errno));
return 1;
}
bytes_to_send -= bytes_written;
} while (bytes_written != 0 && bytes_to_send > 0);
}
close(s);
return 0;
}
根据代码可知main函数首先通过property_set("ctl.start", "dumpstate")来启动dumpstate服务。dumpstate服务主要是通过init进行通过fork方式来创建进程/system/bin/dumpstate
,然后Bugreport再通过socket连接建立与dumpstate的通信,这个过程有尝试20次,每次sleep 1s,直到socket连接建立成功。如果连续3min都没有任何数据可读写,则出发超时机制停止读取并退出。由于dumpstate服务中不存在大于1分钟的timetout,因而不可预见的超时的情况下留有很大的回旋余地。
当从socket读取到数据后,写入到标准时输出或者重定向到文件。可见bugreport数据的来源都是dumpstate服务,那么接下来去看看dumpstate服务的工作。
2.2 dumpstate.main()函数
dumpstate.cpp
int main(int argc, char *argv[]) {
int do_add_date = 0;
int do_zip_file = 0;
int do_vibrate = 1;
char* use_outfile = 0;
int use_socket = 0;
int use_control_socket = 0;
int do_fb = 0;
int do_broadcast = 0;
int is_remote_mode = 0;
bool show_header_only = false;
bool do_start_service = false;
bool telephony_only = false;
//提高当前进程的优先级,防止被OOM Killer杀死
setpriority(PRIO_PROCESS, 0, -20);
FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we");
if (oom_adj) {
fputs("-1000", oom_adj);
fclose(oom_adj);
} else {
/* 兼容判断当内核 <= 2.6.35 */
oom_adj = fopen("/proc/self/oom_adj", "we");
if (oom_adj) {
fputs("-17", oom_adj);
fclose(oom_adj);
}
}
/* 参数解析 */
int c;
while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
switch (c) {
// clang-format off
case 'd': do_add_date = 1; break;
case 'z': do_zip_file = 1; break;
case 'o': use_outfile = optarg; break;
case 's': use_socket = 1; break;
case 'S': use_control_socket = 1; break;
case 'v': show_header_only = true; break;
case 'q': do_vibrate = 0; break;
case 'p': do_fb = 1; break;
case 'P': ds.update_progress_ = true; break;
case 'R': is_remote_mode = 1; break;
case 'B': do_broadcast = 1; break;
case 'V': break; // compatibility no-op
case 'h':
ShowUsageAndExit(0);
break;
default:
fprintf(stderr, "Invalid option: %c\n", c);
ShowUsageAndExit();
// clang-format on
}
}
// TODO: use helper function to convert argv into a string
for (int i = 0; i < argc; i++) {
ds.args_ += argv[i];
if (i < argc - 1) {
ds.args_ += " ";
}
}
ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
if (!ds.extra_options_.empty()) {
// Framework 使用一个系统属性来重写一些命令行参数.
// 目前,它包含所请求的bugreport的类型。
if (ds.extra_options_ == "bugreportplus") {
// 目前,dumpstate的binder数据只通过命令行来更新
do_start_service = true;
ds.update_progress_ = true;
do_fb = 0;
} else if (ds.extra_options_ == "bugreportremote") {
do_vibrate = 0;
is_remote_mode = 1;
do_fb = 0;
} else if (ds.extra_options_ == "bugreportwear") {
ds.update_progress_ = true;
} else if (ds.extra_options_ == "bugreporttelephony") {
telephony_only = true;
} else {
MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
}
// 重置属性PROPERTY_EXTRA_OPTIONS=""
//static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
}
ds.notification_title = android::base::GetProperty(PROPERTY_EXTRA_TITLE, "");
if (!ds.notification_title.empty()) {
// 重置属性PROPERTY_EXTRA_TITLE=""
//static constexpr char PROPERTY_EXTRA_TITLE[] = "dumpstate.options.title";
android::base::SetProperty(PROPERTY_EXTRA_TITLE, "");
ds.notification_description = android::base::GetProperty(PROPERTY_EXTRA_DESCRIPTION, "");
if (!ds.notification_description.empty()) {
// 重置属性PROPERTY_EXTRA_DESCRIPTION=""
//static constexpr char PROPERTY_EXTRA_DESCRIPTION[] = "dumpstate.options.description";
android::base::SetProperty(PROPERTY_EXTRA_DESCRIPTION, "");
}
MYLOGD("notification (title: %s, description: %s)\n",
ds.notification_title.c_str(), ds.notification_description.c_str());
}
if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) {
ExitOnInvalidArgs();
}
if (use_control_socket && !do_zip_file) {
ExitOnInvalidArgs();
}
if (ds.update_progress_ && !do_broadcast) {
ExitOnInvalidArgs();
}
if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
ExitOnInvalidArgs();
}
if (ds.version_ == VERSION_DEFAULT) {
ds.version_ = VERSION_CURRENT;
}
if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) {
MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
VERSION_SPLIT_ANR.c_str());
exit(1);
}
if (show_header_only) {
ds.PrintHeader();
exit(0);
}
/* 如果需要就重定向输出 */
bool is_redirecting = !use_socket && use_outfile;
// TODO: temporarily set progress until it's part of the Dumpstate constructor
std::string stats_path =
is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", dirname(use_outfile))
: "";
ds.progress_.reset(new Progress(stats_path));
/* 获取序列 id
static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id" */
uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
ds.id_ = ++last_id;
android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
MYLOGI("begin\n");
register_sig_handler();
if (do_start_service) {
MYLOGI("Starting 'dumpstate' service\n");
android::status_t ret;
if ((ret = android::os::DumpstateService::Start()) != android::OK) {
MYLOGE("Unable to start DumpstateService: %d\n", ret);
}
}
//根据系统属性dumpstate.dry_run来判断
if (PropertiesHelper::IsDryRun()) {
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
ds.extra_options_.c_str());
MYLOGI("bugreport format version: %s\n", ds.version_.c_str());
ds.do_early_screenshot_ = ds.update_progress_;
//简历socket连接
if (use_socket) {
redirect_to_socket(stdout, "dumpstate");
}
if (use_control_socket) {
MYLOGD("Opening control socket\n");
ds.control_socket_fd_ = open_socket("dumpstate");
ds.update_progress_ = 1;
}
if (is_redirecting) {
ds.bugreport_dir_ = dirname(use_outfile);
std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE");
ds.base_name_ = android::base::StringPrintf("%s-%s-%s", basename(use_outfile),
device_name.c_str(), build_id.c_str());
if (do_add_date) {
char date[80];
strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
ds.name_ = date;
} else {
ds.name_ = "undated";
}
if (telephony_only) {
ds.base_name_ += "-telephony";
}
if (do_fb) {
ds.screenshot_path_ = ds.GetPath(".png");
}
ds.tmp_path_ = ds.GetPath(".tmp");
ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
MYLOGD(
"Bugreport dir: %s\n"
"Base name: %s\n"
"Suffix: %s\n"
"Log path: %s\n"
"Temporary path: %s\n"
"Screenshot path: %s\n",
ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(),
ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
if (do_zip_file) {
ds.path_ = ds.GetPath(".zip");
MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
create_parent_dirs(ds.path_.c_str());
ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
if (ds.zip_file == nullptr) {
MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
do_zip_file = 0;
} else {
ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
}
ds.AddTextZipEntry("version.txt", ds.version_);
}
if (ds.update_progress_) {
if (do_broadcast) {
// clang-format off
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP",
"--es", "android.intent.extra.NAME", ds.name_,
"--ei", "android.intent.extra.ID", std::to_string(ds.id_),
"--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
"--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
};
// clang-format on
SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
}
if (use_control_socket) {
dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str());
}
}
}
/* 读取 /proc/cmdline 在改变root之前*/
FILE *cmdline = fopen("/proc/cmdline", "re");
if (cmdline) {
fgets(cmdline_buf, sizeof(cmdline_buf), cmdline);
fclose(cmdline);
}
//打开震动
if (do_vibrate) {
Vibrate(150);
}
if (do_fb && ds.do_early_screenshot_) {
if (ds.screenshot_path_.empty()) {
// should not have happened
MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
} else {
MYLOGI("taking early screenshot\n");
ds.TakeScreenshot();
}
}
if (do_zip_file) {
if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(),
strerror(errno));
}
}
if (is_redirecting) {
redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
ds.log_path_.c_str(), strerror(errno));
}
/* TODO: rather than generating a text file now and zipping it later,
it would be more efficient to redirect stdout to the zip entry
directly, but the libziparchive doesn't support that option yet. */
redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str()));
if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
ds.tmp_path_.c_str(), strerror(errno));
}
}
// Don't buffer stdout
setvbuf(stdout, nullptr, _IONBF, 0);
// 打印系统build相关信息
ds.PrintHeader();
//根据ds.extra_options来dump不同的信息
if (telephony_only) {
DumpIpTables();
if (!DropRootUser()) {
return -1;
}
do_dmesg();
DoLogcat();
DoKmsg();
ds.DumpstateBoard();
DumpModemLogs();
} else {
// Dumps systrace right away, otherwise it will be filled with unnecessary events.
// First try to dump anrd trace if the daemon is running. Otherwise, dump
// the raw trace.
if (!dump_anrd_trace()) {
dump_systrace();
}
// 在dump_traces()之前调用以下dumpsys调用,以尝试使系统统计信息尽可能接近其初始状态。
RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
CommandOptions::WithTimeout(90).DropRoot().Build());
RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"},
CommandOptions::WithTimeout(10).DropRoot().Build());
// TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
//看起来还有一些需要完善的点,在某些问题修复后
dump_raft();
/* 收集虚拟机和native进程的stack traces(需要root权限) */
dump_traces_path = dump_traces();
/* 获取tombstone文件描述符. */
get_tombstone_fds(tombstone_data);
ds.AddDir(RECOVERY_DIR, true);
ds.AddDir(RECOVERY_DATA_DIR, true);
ds.AddDir(LOGPERSIST_DATA_DIR, false);
if (!PropertiesHelper::IsUserBuild()) {
ds.AddDir(PROFILE_DATA_DIR_CUR, true);
ds.AddDir(PROFILE_DATA_DIR_REF, true);
}
add_mountinfo();
DumpIpTables();
// 捕获正在使用的任何IPSec策略。 这里没有key
RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
CommandOptions::WithTimeout(10).Build());
// 以root身份运行ss,以便我们可以看到socket标记.
RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"},
CommandOptions::WithTimeout(10).Build());
if (!DropRootUser()) {
return -1;
}
//这里是真正干活的地方
dumpstate();
}
/* 如果需要关闭输出 */
if (is_redirecting) {
fclose(stdout);
}
/* 重命名或者压缩.tmp 文件 到最终的地方*/
if (use_outfile) {
/* 检查用户是否使用系统属性更改了后缀 */
std::string name = android::base::GetProperty(
android::base::StringPrintf("dumpstate.%d.name", ds.pid_), "");
bool change_suffix= false;
if (!name.empty()) {
/* 字符规则匹配 */
std::regex valid_regex("^[-_a-zA-Z0-9]+$");
if (std::regex_match(name.c_str(), valid_regex)) {
change_suffix = true;
} else {
MYLOGE("invalid suffix provided by user: %s\n", name.c_str());
}
}
if (change_suffix) {
MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
ds.name_ = name;
if (!ds.screenshot_path_.empty()) {
std::string new_screenshot_path = ds.GetPath(".png");
if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
new_screenshot_path.c_str(), strerror(errno));
} else {
ds.screenshot_path_ = new_screenshot_path;
}
}
}
bool do_text_file = true;
if (do_zip_file) {
if (!ds.FinishZipFile()) {
MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
do_text_file = true;
} else {
do_text_file = false;
// 如果之前的压缩文件存在,则要重命名
std::string new_path = ds.GetPath(".zip");
if (ds.path_ != new_path) {
MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
if (rename(ds.path_.c_str(), new_path.c_str())) {
MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
strerror(errno));
} else {
ds.path_ = new_path;
}
}
}
}
if (do_text_file) {
ds.path_ = ds.GetPath(".txt");
MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(),
ds.tmp_path_.c_str());
if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(),
strerror(errno));
ds.path_.clear();
}
}
if (use_control_socket) {
if (do_text_file) {
dprintf(ds.control_socket_fd_,
"FAIL:could not create zip file, check %s "
"for more details\n",
ds.log_path_.c_str());
} else {
dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
}
}
}
/* 震动告知用户bugreport已经生成 */
for (int i = 0; i < 3; i++) {
Vibrate(75);
usleep((75 + 50) * 1000);
}
/* 通过发送广播告知ActivityManager已完成bugreport操作 */
if (do_broadcast) {
if (!ds.path_.empty()) {
MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
// clang-format off
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP",
"--ei", "android.intent.extra.ID", std::to_string(ds.id_),
"--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
"--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
"--es", "android.intent.extra.BUGREPORT", ds.path_,
"--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
};
// clang-format on
if (do_fb) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.SCREENSHOT");
am_args.push_back(ds.screenshot_path_);
}
if (!ds.notification_title.empty()) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.TITLE");
am_args.push_back(ds.notification_title);
if (!ds.notification_description.empty()) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.DESCRIPTION");
am_args.push_back(ds.notification_description);
}
}
if (is_remote_mode) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
am_args.push_back(SHA256_file_hash(ds.path_));
SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED",
am_args);
} else {
SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args);
}
} else {
MYLOGE("Skipping finished broadcast because bugreport could not be generated\n");
}
}
MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(),
ds.progress_->GetInitialMax());
ds.progress_->Save();
MYLOGI("done (id %d)\n", ds.id_);
if (is_redirecting) {
fclose(stderr);
}
if (use_control_socket && ds.control_socket_fd_ != -1) {
MYLOGD("Closing control socket\n");
close(ds.control_socket_fd_);
}
return 0;
}
从代码上来看8.0与6.0源码改动并不是很大。
整个工作流程如下:
- 提高执行dumpsate所在进程的优先级,防止被OOM Killer杀死,这里针对内核版本做了一些兼容;
- 参数解析,可通过命令adb shell dumpstate -h查看dumpstate命令所支持的参数;
- 根据PROPERTY_EXTRA_OPTIONS系统属性来做区分,主要类型有bugreportplus,bugreportremote,bugreportwear,bugreporttelephony
并获取ams出设置的通知的标题,描述, - 打开vibrator,用于在执行bugreport时,手机会先震动一下用于提醒开始抓取系统信息;
- 通过dump_traces()来完成收集虚拟机和native进程的stack traces;
- 通过get_tombstone_fds来获取tombstone文件描述符;
- 根据ds.extra_options来dump不同的信息 telephony_only为判断标识,否则执行dumpstate(),这里是真正干活的地方;
- 再次通过震动以提醒dump操作执行完成;
- 发送广播,告知ActivityManager已完成bugreport操作。
接下来就重点说说dumpstate()
功能:
2.3 dumpstate()
该方法负责整个bugreport内容输出的最为核心的功能。
dumpstate.cpp
static void dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
//记录系统运行时长和休眠时长
RunCommand("UPTIME", {"uptime"});
//输出mmcblk0设备信息
dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
DumpFile("MEMORY INFO", "/proc/meminfo");
RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
"pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20);
DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
DumpFile("SLAB INFO", "/proc/slabinfo");
DumpFile("ZONEINFO", "/proc/zoneinfo");
DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo");
DumpFile("BUDDYINFO", "/proc/buddyinfo");
DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources");
DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
DumpFile("KERNEL SYNC", "/d/sync");
RunCommand("PROCESSES AND THREADS",
{"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT);
if (ds.IsZipping()) {
RunCommand(
"HARDWARE HALS",
{"lshal", std::string("--debug=") + kLsHalDebugPath},
CommandOptions::AS_ROOT);
ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath);
unlink(kLsHalDebugPath.c_str());
} else {
RunCommand(
"HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::AS_ROOT);
}
RunCommand("PRINTENV", {"printenv"});
RunCommand("NETSTAT", {"netstat", "-nW"});
struct stat s;
if (stat("/proc/modules", &s) != 0) {
MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
} else {
RunCommand("LSMOD", {"lsmod"});
}
//输出kernel log
do_dmesg();
//所有已打开文件
RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
//遍历所有进程的show map
for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
//显示所有线程的blocked位置
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
/* Dump Bluetooth HCI logs */
ds.AddDir("/data/misc/bluetooth/logs", true);
if (!ds.do_early_screenshot_) {
MYLOGI("taking late screenshot\n");
ds.TakeScreenshot();
}
//获取main,system,event,radio ,last logcat等信息
DoLogcat();
//添加anr trace 文件
AddAnrTraceFiles();
int dumped = 0;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
if (tombstone_data[i].fd != -1) {
const char *name = tombstone_data[i].name;
int fd = tombstone_data[i].fd;
dumped = 1;
if (ds.IsZipping()) {
if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
MYLOGE("Unable to add tombstone %s to zip file\n", name);
}
} else {
dump_file_from_fd("TOMBSTONE", name, fd);
}
close(fd);
tombstone_data[i].fd = -1;
}
}
if (!dumped) {
printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
}
DumpFile("NETWORK DEV INFO", "/proc/net/dev");
DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
//kernel log
DoKmsg();
/* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
//wifi驱动/固件 以及ip相关信息
RunCommand("NETWORK INTERFACES", {"ip", "link"});
RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
RunCommand("IP RULES", {"ip", "rule", "show"});
RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
dump_route_tables();
RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"});
RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
CommandOptions::WithTimeout(20).Build());
RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
CommandOptions::WithTimeout(10).Build());
RunCommand("SYSTEM PROPERTIES", {"getprop"});
RunCommand("VOLD DUMP", {"vdc", "dump"});
RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
RunCommand("STORAGED TASKIOINFO", {"storaged", "-u"}, CommandOptions::WithTimeout(10).Build());
RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
//背光信息
printf("------ BACKLIGHTS ------\n");
printf("LCD brightness=");
DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
printf("Button brightness=");
DumpFile("", "/sys/class/leds/button-backlight/brightness");
printf("Keyboard brightness=");
DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
printf("ALS mode=");
DumpFile("", "/sys/class/leds/lcd-backlight/als");
printf("LCD driver registers:\n");
DumpFile("", "/sys/class/leds/lcd-backlight/registers");
printf("\n");
/* Binder state is expensive to look at as it uses a lot of memory.
binder相关信息 */
DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
ds.DumpstateBoard();
/* Migrate the ril_dumpstate to a device specific dumpstate? */
int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
if (rilDumpstateTimeout > 0) {
// su does not exist on user builds, so try running without it.
// This way any implementations of vril-dump that do not require
// root can run on user builds.
CommandOptions::CommandOptionsBuilder options =
CommandOptions::WithTimeout(rilDumpstateTimeout);
if (!PropertiesHelper::IsUserBuild()) {
options.AsRoot();
}
RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
}
//输出framework各种服务的dumpsys信息
printf("========================================================\n");
printf("== Android Framework Services\n");
printf("========================================================\n");
RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(),
10);
printf("========================================================\n");
printf("== Checkins\n");
printf("========================================================\n");
RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
//输出当前 运行中activity/service/provider信息
printf("========================================================\n");
printf("== Running Application Activities\n");
printf("========================================================\n");
RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"});
printf("========================================================\n");
printf("== Running Application Services\n");
printf("========================================================\n");
RunDumpsys("APP SERVICES", {"activity", "service", "all"});
printf("========================================================\n");
printf("== Running Application Providers\n");
printf("========================================================\n");
RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
printf("========================================================\n");
printf("== Dropbox crashes\n");
printf("========================================================\n");
RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"});
RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"});
//获取modem log
DumpModemLogs();
printf("========================================================\n");
printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
ds.progress_->GetMax(), ds.progress_->GetInitialMax());
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
}
按照源码中的调用顺序,依次列出相关的函数如下:
2.3.1 PrintHeader()
打印了系统build相关信息
void Dumpstate::PrintHeader() const {
std::string build, fingerprint, radio, bootloader, network;
char date[80];
build = android::base::GetProperty("ro.build.display.id", "(unknown)");
fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)");
radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));
printf("========================================================\n");
printf("== dumpstate: %s\n", date);
printf("========================================================\n");
printf("\n");
printf("Build: %s\n", build.c_str());
// NOTE: fingerprint entry format is important for other tools.
printf("Build fingerprint: '%s'\n", fingerprint.c_str());
printf("Bootloader: %s\n", bootloader.c_str());
printf("Radio: %s\n", radio.c_str());
printf("Network: %s\n", network.c_str());
printf("Kernel: ");
DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
printf("Bugreport format version: %s\n", version_.c_str());
printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
printf("\n");
}
2.3.2 do_dmesg()
utils.cpp
void do_dmesg() {
const char *title = "KERNEL LOG (dmesg)";
DurationReporter duration_reporter(title);
printf("------ %s ------\n", title);
if (PropertiesHelper::IsDryRun()) return;
/* 获取kernel buffer的大小*/
int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
if (size <= 0) {
printf("Unexpected klogctl return value: %d\n\n", size);
return;
}
char *buf = (char *) malloc(size + 1);
if (buf == NULL) {
printf("memory allocation failed\n\n");
return;
}
//获取kernel log
int retval = klogctl(KLOG_READ_ALL, buf, size);
if (retval < 0) {
printf("klogctl failure\n\n");
free(buf);
return;
}
buf[retval] = '\0';
printf("%s\n\n", buf);
free(buf);
return;
}
2.3.3 DoLogcat()
static void DoLogcat() {
unsigned long timeout;
// DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
// calculate timeout
timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
if (timeout < 20000) {
timeout = 20000;
}
RunCommand("SYSTEM LOG",
{"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid",
"-d", "*:v"},
CommandOptions::WithTimeout(timeout / 1000).Build());
timeout = logcat_timeout("events");
if (timeout < 20000) {
timeout = 20000;
}
RunCommand("EVENT LOG",
{"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid",
"-d", "*:v"},
CommandOptions::WithTimeout(timeout / 1000).Build());
timeout = logcat_timeout("radio");
if (timeout < 20000) {
timeout = 20000;
}
RunCommand("RADIO LOG",
{"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid",
"-d", "*:v"},
CommandOptions::WithTimeout(timeout / 1000).Build());
RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
/* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
RunCommand("LAST LOGCAT",
{"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid",
"-d", "*:v"});
}
2.3.4 AddAnrTraceFiles()
static void AddAnrTraceFiles() {
bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
std::string dump_traces_dir;
/* show the traces we collected in main(), if that was done */
if (dump_traces_path != nullptr) {
if (add_to_zip) {
dump_traces_dir = dirname(dump_traces_path);
MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
ds.AddDir(dump_traces_dir, true);
} else {
MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
dump_traces_path);
ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
}
}
std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
std::string anr_traces_dir = dirname(anr_traces_path.c_str());
// Make sure directory is not added twice.
// TODO: this is an overzealous check because it's relying on dump_traces_path - which is
// generated by dump_traces() - and anr_traces_path - which is retrieved from a system
// property - but in reality they're the same path (although the former could be nullptr).
// Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
// be revisited.
bool already_dumped = anr_traces_dir == dump_traces_dir;
MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
if (anr_traces_path.empty()) {
printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
} else {
int fd = TEMP_FAILURE_RETRY(
open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
if (fd < 0) {
printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
strerror(errno));
} else {
if (add_to_zip) {
if (!already_dumped) {
MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
anr_traces_dir.c_str());
ds.AddDir(anr_traces_dir, true);
already_dumped = true;
}
} else {
MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
anr_traces_path.c_str());
dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
}
}
}
if (add_to_zip && already_dumped) {
MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
return;
}
/* 输出慢操作的vm traces,例如/data/anr/slow1.txt*/
struct stat st;
if (!anr_traces_path.empty()) {
int tail = anr_traces_path.size() - 1;
while (tail > 0 && anr_traces_path.at(tail) != '/') {
tail--;
}
int i = 0;
while (1) {
anr_traces_path = anr_traces_path.substr(0, tail + 1) +
android::base::StringPrintf("slow%02d.txt", i);
if (stat(anr_traces_path.c_str(), &st)) {
// No traces file at this index, done with the files.
break;
}
ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
i++;
}
}
}
2.3.5 RunCommand()
static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT) {
return ds.RunCommand(title, full_command, options);
}
2.3.6 RunDumpsys()
static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
long dumpsysTimeout = 0) {
return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout);
}
2.3.7 dump_files()
```utils.cpp```
/* calls skip to gate calling dump_from_fd recursively
* in the specified directory. dump_from_fd defaults to
* dump_file_from_fd above when set to NULL. skip defaults
* to false when set to NULL. dump_from_fd will always be
* called with title NULL.
*/
int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
int (*dump_from_fd)(const char* title, const char* path, int fd)) {
DurationReporter duration_reporter(title);
DIR *dirp;
struct dirent *d;
char *newpath = NULL;
const char *slash = "/";
int fd, retval = 0;
if (!title.empty()) {
printf("------ %s (%s) ------\n", title.c_str(), dir);
}
if (PropertiesHelper::IsDryRun()) return 0;
if (dir[strlen(dir) - 1] == '/') {
++slash;
}
dirp = opendir(dir);
if (dirp == NULL) {
retval = -errno;
MYLOGE("%s: %s\n", dir, strerror(errno));
return retval;
}
if (!dump_from_fd) {
dump_from_fd = dump_file_from_fd;
}
for (; ((d = readdir(dirp))); free(newpath), newpath = NULL) {
if ((d->d_name[0] == '.')
&& (((d->d_name[1] == '.') && (d->d_name[2] == '\0'))
|| (d->d_name[1] == '\0'))) {
continue;
}
asprintf(&newpath, "%s%s%s%s", dir, slash, d->d_name,
(d->d_type == DT_DIR) ? "/" : "");
if (!newpath) {
retval = -errno;
continue;
}
if (skip && (*skip)(newpath)) {
continue;
}
if (d->d_type == DT_DIR) {
int ret = dump_files("", newpath, skip, dump_from_fd);
if (ret < 0) {
retval = ret;
}
continue;
}
fd = TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
if (fd < 0) {
retval = fd;
printf("*** %s: %s\n", newpath, strerror(errno));
continue;
}
(*dump_from_fd)(NULL, newpath, fd);
}
closedir(dirp);
if (!title.empty()) {
printf("\n");
}
return retval;
}
2.3.8 DumpFile()
utils.cpp
int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
DurationReporter duration_reporter(title);
int status = DumpFileToFd(STDOUT_FILENO, title, path);
UpdateProgress(WEIGHT_FILE);
return status;
}
2.3.9 DumpFileToFd()
DumpstateUtil.cpp
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
if (fd < 0) {
int err = errno;
if (title.empty()) {
dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err));
} else {
dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(),
strerror(err));
}
fsync(out_fd);
return -1;
}
return DumpFileFromFdToFd(title, path, fd, out_fd, PropertiesHelper::IsDryRun());
}
2.3.10 DumpFileFromFdToFd()
DumpstateInternal.cpp
int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
bool dry_run) {
const char* path = path_string.c_str();
if (!title.empty()) {
dprintf(out_fd, "------ %s (%s", title.c_str(), path);
struct stat st;
// Only show the modification time of non-device files.
size_t path_len = strlen(path);
if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
(path_len < 5 || memcmp(path, "/sys/", 5)) &&
(path_len < 3 || memcmp(path, "/d/", 3)) && !fstat(fd, &st)) {
char stamp[80];
time_t mtime = st.st_mtime;
strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
dprintf(out_fd, ": %s", stamp);
}
dprintf(out_fd, ") ------\n");
fsync(out_fd);
}
if (dry_run) {
if (out_fd != STDOUT_FILENO) {
// There is no title, but we should still print a dry-run message
dprintf(out_fd, "%s: skipped on dry run\n", path);
} else if (!title.empty()) {
dprintf(out_fd, "\t(skipped on dry run)\n");
}
fsync(out_fd);
return 0;
}
bool newline = false;
fd_set read_set;
timeval tm;
while (true) {
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
/* Timeout if no data is read for 30 seconds. */
tm.tv_sec = 30;
tm.tv_usec = 0;
uint64_t elapsed = Nanotime();
int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, nullptr, nullptr, &tm));
if (ret == -1) {
dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
newline = true;
break;
} else if (ret == 0) {
elapsed = Nanotime() - elapsed;
dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
newline = true;
break;
} else {
char buffer[65536];
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
if (bytes_read > 0) {
android::base::WriteFully(out_fd, buffer, bytes_read);
newline = (buffer[bytes_read - 1] == '\n');
} else {
if (bytes_read == -1) {
dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno));
newline = true;
}
break;
}
}
}
close(fd);
if (!newline) dprintf(out_fd, "\n");
if (!title.empty()) dprintf(out_fd, "\n");
return 0;
}
2.4 总结
Bugreport通过socket与dumpstate服务建立通信,在dumpstate.cpp中根据系统属性PROPERTY_EXTRA_OPTIONS来区分bugreportplus,bugreportremote,bugreportwear,bugreporttelephony等这几种类型来获取不同的信息。如果是telephony_only为ture,则会调用DumpIpTables(), do_dmesg(), DoLogcat(),DoKmsg(),DumpstateBoard(), DumpModemLogs(),其他类型则会最终调用到dumpstate()方法完成核心功能,该功能依次输出内容项, 主要分为5大类:
- current log: kernel,system, event, radio;
- last log: kernel, system, radio;
- vm traces: just now, last ANR, tombstones
- dumpsys: all, checkin, app
- system info:cpu, memory, io等
从bugreport内容的输出顺序的角度,再详细列举其内容:
- 系统build以及运行时长等相关信息;
- 内存/CPU/进程等信息;
- kernel log;
- lsof、map及Wait-Channels;
- system log;
- event log;
- radio log;
- vm traces:
-- VM TRACES JUST NOW (/data/anr/traces.txt.bugreport) (抓bugreport时主动触发)
-- VM TRACES AT LAST ANR (/data/anr/traces.txt) (存在则输出)
-- TOMBSTONE (/data/tombstones/tombstone_xx) (存在这输出) - network相关信息;
- last kernel log;
- last system log;
- ip相关信息;
- 中断向量表
- property以及fs等信息
- last radio log;
- Binder相关信息;
- dumpsys all:
- dumpsys checkin相关:
-- dumpsys batterystats电池统计;
-- dumpsys meminfo内存
-- dumpsys netstats网络统计;
-- dumpsys procstats进程统计;
-- dumpsys usagestats使用情况;
-- dumpsys package. - dumpsys app相关
-- dumpsys activity;
-- dumpsys activity service all;
-- dumpsys activity provider all.
Bugreport几乎涵盖整个系统信息,内容非常长。跟android6.0相比改动不大,只是针对不同的arm设备做了更精细的分类和处理。在Bugreport中,几乎很多信息子项都以------ xxx ------开头。 例如APP ACTIVITIES的开头便是 ------ APP ACTIVITIES (dumpsys activity all) ------,其中括号内的便是输出该信息指令,即dumpsys activity all。Bugreport对于分析系统问题有很重要的帮助。