小学生的做试卷儿的网站 你这,iis 5.1 新建网站,吴忠网站建设,免费学服装设计的网站Bugreport实现原理
Bugreport
Bugreport介绍
Android Bugreport是一个用于记录和收集 Android设备上系统信息、日志和调试信息的工具。 系统发生某些问题时#xff0c;可以通过bugreport把系统当前时刻点#xff08;运行BugRepot的时刻#xff09;的系统相关的状态和信息…Bugreport实现原理
Bugreport
Bugreport介绍
Android Bugreport是一个用于记录和收集 Android设备上系统信息、日志和调试信息的工具。 系统发生某些问题时可以通过bugreport把系统当前时刻点运行BugRepot的时刻的系统相关的状态和信息都抓到一个zip中。 通过bugreport可以帮忙开发人员分析和解决问题。
Bugreport其实就是一系列的信息的汇总包括日志、内存状态、进程信息、崩溃信息、服务状态等等。用一个大而近乎全的现场来帮忙更好的分析问题。 并非所有问题都需要用Bugreport抓取一份大而全的现场。可以根据经验考虑选用bugreport或者其他工具。 Bugreport收集的信息一般包括
设备软硬件信息系统日志系统运行状态如cpu、内存、网络状态等。程序崩溃信息如anr、墓碑等。
Bugreport使用方式
adb方式
adb bugreportconsole方式
bugreportz -p执行成功后会在/data/user_de/0/com.android.shell/files/bugreports/下成一个生成一个 bugreport-*.zip的文件。
Bugrepot成果物的命名方式
文件命名形式为 bugreport-[device_name]-[build_id]-[localtime].zip device_name:属性ro.product.name默认为UNKONW_DEVICE build_id属性ro.build.id的值默认为UNKOWN_BUILD localtime: 抓取bugreport时的本地时间 例如 bugreport-arm-123.123-2024-02-28-19-18-14.zip device_name:arm build_id:123.123 localtime:2024-02-28-19-18-14
bugreport的实现
adb bugreport会调用adbd让adbd执行bugreportz -p的shell命令。bugreportz 调用dumpstate -S该命令会生成一个*.zip的bugreport文件。 生成后bugreportz会将生成的通知通过STDOUT_FILENO告知adb。adb收到这个通知后将对应的文件pull到Host上。
adb bugreport到adbd执行bugrepotz -p
adb bugreport执行(Host端解析参数bugreport。
// packages/modules/adb/client/main.cppint main(int argc, char* argv[], char* envp[]) {__adb_argv const_castconst char**(argv);__adb_envp const_castconst char**(envp);adb_trace_init(argv);return adb_commandline(argc - 1, const_castconst char**(argv 1));
}// packages/modules/adb/client/commandline.cppint adb_commandline(int argc, const char** argv) {// 省略/* adb_connect() commands */if (!strcmp(argv[0], devices)) {// 省略} else if (!strcmp(argv[0], bugreport)) {Bugreport bugreport;return bugreport.DoIt(argc, argv);} else if (!strcmp(argv[0], forward) || !strcmp(argv[0], reverse)) {// 省略
}Bugreport类Adb模块DoIt函数向Adbd发送“bugreportz -v和”bugreportz -p“命令。执行bugreportz -v获取bugreportz的版本一方面是执行版本差异的流程另一方面也是作为Test测试bugreportz是否可以执行。
int Bugreport::DoIt(int argc, const char** argv) {if (argc 2) error_exit(usage: adb bugreport [[PATH] | [--stream]]);// Gets bugreportz version.std::string bugz_stdout, bugz_stderr;DefaultStandardStreamsCallback version_callback(bugz_stdout, bugz_stderr);int status SendShellCommand(bugreportz -v, false, version_callback);std::string bugz_version android::base::Trim(bugz_stderr);std::string bugz_output android::base::Trim(bugz_stdout);int bugz_ver_major 0, bugz_ver_minor 0;if (status ! 0 || bugz_version.empty()) {D(bugreportz -v results: status%d, stdout%s, stderr%s, status,bugz_output.c_str(), bugz_version.c_str());if (argc 1) {// Device does not support bugreportz: if called as adb bugreport, just falls out to// the flat-file version.fprintf(stderr,Failed to get bugreportz version, which is only available on devices running Android 7.0 or later.\nTrying a plain-text bug report instead.\n);return SendShellCommand(bugreport, false);}// But if user explicitly asked for a zipped bug report, fails instead (otherwise calling// bugreport would generate a lot of output the user might not be prepared to handle).fprintf(stderr,Failed to get bugreportz version: bugreportz -v returned %s (code %d).\nIf the device does not run Android 7.0 or above, try this instead:\n\tadb bugreport bugreport.txt\n,bugz_output.c_str(), status);return status ! 0 ? status : -1;}std::sscanf(bugz_version.c_str(), %d.%d, bugz_ver_major, bugz_ver_minor);std::string dest_file, dest_dir;if (argc 1) {// No args - use current directoryif (!getcwd(dest_dir)) {perror(adb: getcwd failed);return 1;}} else if (!strcmp(argv[1], --stream)) {if (bugz_ver_major 1 bugz_ver_minor 2) {fprintf(stderr,Failed to stream bugreport: bugreportz does not support stream.\n);} else {return SendShellCommand(bugreportz -s, false);}} else {// Check whether argument is a directory or fileif (directory_exists(argv[1])) {dest_dir argv[1];} else {dest_file argv[1];}}if (dest_file.empty()) {// Uses a default value until device provides the proper namedest_file bugreport.zip;} else {if (!android::base::EndsWithIgnoreCase(dest_file, .zip)) {dest_file .zip;}}bool show_progress true;std::string bugz_command bugreportz -p;if (bugz_version 1.0) {// 1.0 does not support progress notifications, so print a disclaimer// message instead.fprintf(stderr,Bugreport is in progress and it could take minutes to complete.\nPlease be patient and do not cancel or disconnect your device until it completes.\n);show_progress false;bugz_command bugreportz;}BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);return SendShellCommand(bugz_command, false, bugz_callback);
}adbd执行bugrepotz -p
此部分省略。就是adbd执行下面两个命令shell bugreportz -v 和 bugreportz -p 不是主要关注点想了了解的自行阅读源码即可。
bugreportz -p执行并调用dumpstate -S
通过bugreportz -p收集系统当前时刻点的各种信息信息参考上面的内容。bugreportz -v比较简单仅为输出一下bugreportz的版本。
// frameworks/native/cmds/bugreportz/main.cpp
static constexpr char VERSION[] 1.2;static void show_usage() {fprintf(stderr,usage: bugreportz [-hpsv]\n -h: to display this help message\n -p: display progress\n -s: stream content to standard output\n -v: to display the version\n or no arguments to generate a zipped bugreport\n);
}static void show_version() {fprintf(stderr, %s\n, VERSION);
}int main(int argc, char* argv[]) {bool show_progress false;bool stream_data false;if (argc 1) {/* parse arguments */int c;while ((c getopt(argc, argv, hpsv)) ! -1) {switch (c) {case h:show_usage();return EXIT_SUCCESS;case p:show_progress true;break;case s:stream_data true;break;case v:show_version();return EXIT_SUCCESS;default:show_usage();return EXIT_FAILURE;}}}// We dont support any non-option arguments.if (optind ! argc) {show_usage();return EXIT_FAILURE;}// TODO: code below was copy-and-pasted from bugreport.cpp (except by the// timeout value);// should be reused instead.// Start the dumpstatez service.if (stream_data) {property_set(ctl.start, dumpstate);} else {// 调用dumpstatezproperty_set(ctl.start, dumpstatez);}// Socket will not be available until service starts.int s -1;for (int i 0; i 20; i) {// 接连dumpstatez的socket接收状态信息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(FAIL:Failed to connect to dumpstatez service: %s\n, strerror(errno));return EXIT_FAILURE;}// Set a timeout so that if nothing is read in 10 minutes, well stop// reading and quit. No timeout in dumpstate is longer than 60 seconds,// so this gives lots of leeway in case of unforeseen time outs.struct timeval tv;tv.tv_sec 10 * 60;tv.tv_usec 0;if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, tv, sizeof(tv)) -1) {fprintf(stderr,WARNING: Cannot set socket timeout, bugreportz might hang indefinitely: %s\n,strerror(errno));}int ret;if (stream_data) {ret bugreportz_stream(s);} else {// 走这里show_progress为Trueret bugreportz(s, show_progress);}if (close(s) -1) {fprintf(stderr, WARNING: error closing socket: %s\n, strerror(errno));ret EXIT_FAILURE;}return ret;
}bugreportz函数中接收dumpstatez通过socket返回的状态信息并将其写到标准输出中。adb会通过标准输出了解到命令执行的状态。为啥dumpstatez不将状态信息直接写到标准输出中因为dumpstatez将标准输出重定向到文件了。
static constexpr char BEGIN_PREFIX[] BEGIN:;
static constexpr char PROGRESS_PREFIX[] PROGRESS:;static void write_line(const std::string line, bool show_progress) { if (line.empty()) return;// When not invoked with the -p option, it must skip BEGIN and PROGRESS lines otherwise it// will break adb (which is expecting either OK or FAIL).if (!show_progress (android::base::StartsWith(line, PROGRESS_PREFIX) ||android::base::StartsWith(line, BEGIN_PREFIX)))return;android::base::WriteStringToFd(line, STDOUT_FILENO);
}
int bugreportz(int s, bool show_progress) { std::string line;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 really means time out, so change the errno.if (errno EAGAIN) {errno ETIMEDOUT;}printf(FAIL:Bugreport read terminated abnormally (%s)\n, strerror(errno));return EXIT_FAILURE;}// Writes line by line.for (int i 0; i bytes_read; i) {char c buffer[i];line.append(1, c);if (c \n) {write_line(line, show_progress);line.clear();}}}// Process final line, in case it didnt finish with newlinewrite_line(line, show_progress);return EXIT_SUCCESS;
}上面代码中通过 “ctl.start”, dumpstatez执行了dumpstatez。查看dumpstatez对应的rc文件。其对应/system/bin/dumpstate -S注意为大写S)
service dumpstatez /system/bin/dumpstate -Ssocket dumpstate stream 0660 shell logclass maindisabledoneshotdumpstate -S生成Bugreport对应的zip文件
dumpstate -s命令执行
// frameworks/native/cmds/dumpstate/main.cpp
int main(int argc, char* argv[]) {if (ShouldStartServiceAndWait(argc, argv)) {int ret;if ((ret android::os::DumpstateService::Start()) ! android::OK) {MYLOGE(Unable to start dumpstate service: %d, ret);exit(1);}MYLOGI(dumpstate service started and will wait for a call to startBugreport());// Waits forever for an incoming connection.// TODO(b/111441001): should this time out?android::IPCThreadState::self()-joinThreadPool();return 0;} else {return run_main(argc, argv);}
}// frameworks/native/cmds/dumpstate/dumpstate.cpp
/* Main entry point for dumpstate binary. */
int run_main(int argc, char* argv[]) {Dumpstate::RunStatus status ds.ParseCommandlineAndRun(argc, argv);switch (status) {case Dumpstate::RunStatus::OK:exit(0);case Dumpstate::RunStatus::HELP:ShowUsage();exit(0);case Dumpstate::RunStatus::INVALID_INPUT:fprintf(stderr, Invalid combination of args\n);ShowUsage();exit(1);case Dumpstate::RunStatus::ERROR:FALLTHROUGH_INTENDED;case Dumpstate::RunStatus::USER_CONSENT_DENIED:FALLTHROUGH_INTENDED;case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:exit(2);}
}Dumpstate::RunStatus Dumpstate::ParseCommandlineAndRun(int argc, char* argv[]) {std::unique_ptrDumpstate::DumpOptions options std::make_uniqueDumpstate::DumpOptions();Dumpstate::RunStatus status options-Initialize(argc, argv);if (status Dumpstate::RunStatus::OK) {SetOptions(std::move(options));// When directly running dumpstate binary, the output is not expected to be written// to any external file descriptor.assert(options_-bugreport_fd.get() -1);// calling_uid and calling_package are for user consent to share the bugreport with// an app; they are irrelevant here because bugreport is triggered via command line.// Update Last ID before calling Run().Initialize();status Run(-1 /* calling_uid */, /* calling_package */);}return status;
}创建Dumpstate::DumpOptions对象调用Initialize函数解析输入参数“-S”。S(大写会将参数的progress_updates_to_socket设置为ture这个flag标志着dumpstate将状态告知给调用者通过socket
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,const android::base::unique_fd bugreport_fd_in,const android::base::unique_fd screenshot_fd_in,bool is_screenshot_requested) {// Duplicate the fds because the passed in fds dont outlive the binder transaction.bugreport_fd.reset(dup(bugreport_fd_in.get()));screenshot_fd.reset(dup(screenshot_fd_in.get()));SetOptionsFromMode(bugreport_mode, this, is_screenshot_requested);
}Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {RunStatus status RunStatus::OK;int c;while ((c getopt(argc, argv, dho:svqzpLPBRSV:w)) ! -1) {switch (c) {// clang-format offcase o: out_dir optarg; break;case s: stream_to_socket true; break;case S: progress_updates_to_socket true; break;case v: show_header_only true; break;case q: do_vibrate false; break;case p: do_screenshot true; break;case P: do_progress_updates true; break;case R: is_remote_mode true; break;case L: limited_only true; break;case V:case d:case z:// compatibility no-opbreak;case w:// This was already processedbreak;case h:status RunStatus::HELP;break;default:fprintf(stderr, Invalid option: %c\n, c);status RunStatus::INVALID_INPUT;break;// clang-format on}}for (int i 0; i argc; i) {args argv[i];if (i argc - 1) {args ;}}// Reset next index used by getopt so this can be called multiple times, for eg, in tests.optind 1;return status;
}然后调用Dumpstate::Initialize 和Dumpstate::Run开始收集bugreport的内容。
void Dumpstate::Initialize() {/* gets the sequential id */uint32_t last_id android::base::GetIntProperty(PROPERTY_LAST_ID, 0);id_ last_id;android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
}Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string calling_package) {Dumpstate::RunStatus status RunInternal(calling_uid, calling_package);if (listener_ ! nullptr) {switch (status) {case Dumpstate::RunStatus::OK:listener_-onFinished();break;case Dumpstate::RunStatus::HELP:break;case Dumpstate::RunStatus::INVALID_INPUT:listener_-onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);break;case Dumpstate::RunStatus::ERROR:listener_-onError(IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);break;case Dumpstate::RunStatus::USER_CONSENT_DENIED:listener_-onError(IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT);break;case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:listener_-onError(IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);break;}}return status;
}Dumpstate::Run函数中调用RunInternal实现Bugreport的收集。该函数内容比较多只关注三个主要流程主要文件bugreport-*.txt的生成log、anr等文件copy到zip文件中、zip文件的生成。
Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,const std::string calling_package) {DurationReporter duration_reporter(RUN INTERNAL, /* logcat_only */true);LogDumpOptions(*options_);if (!options_-ValidateOptions()) {MYLOGE(Invalid options specified\n);return RunStatus::INVALID_INPUT;}/* set as high priority, and protect from 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 {/* fallback to kernels 2.6.35 */oom_adj fopen(/proc/self/oom_adj, we);if (oom_adj) {fputs(-17, oom_adj);fclose(oom_adj);}}MYLOGI(dumpstate info: id%d, args%s, bugreport_mode %s bugreport format version: %s\n,id_, options_-args.c_str(), options_-bugreport_mode.c_str(), version_.c_str());// If we are going to use a socket, do it as early as possible// to avoid timeouts from bugreport.if (options_-stream_to_socket || options_-progress_updates_to_socket) {MYLOGD(Opening control socket\n);control_socket_fd_ open_socket_fn_(dumpstate);if (control_socket_fd_ -1) {return ERROR;}if (options_-progress_updates_to_socket) {options_-do_progress_updates 1;}}// 准备文件if (!PrepareToWriteToFile()) {return ERROR;}// 将标准输出重定向到临时文件Bugreport-*.tmp文件中// 通过Bugreport-*.tmp文件产生最终的Bugreport-*.zip// Redirect stdout to tmp_path_. This is the main bugreport entry and will be// moved into zip file later, if zipping.TEMP_FAILURE_RETRY(dup_stdout_fd dup(fileno(stdout)));// TODO: why not write to a file instead of stdout to overcome this problem?/* TODO: rather than generating a text file now and zipping it later,it would be more efficient to redirect stdout to the zip entrydirectly, but the libziparchive doesnt support that option yet. */if (!redirect_to_file(stdout, const_castchar*(tmp_path_.c_str()))) {return ERROR;}if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {MYLOGE(Unable to change ownership of temporary bugreport file %s: %s\n,tmp_path_.c_str(), strerror(errno));}// 输出头部分信息就是Bugreport-*.txt最开头的一些版本信息)// NOTE: there should be no stdout output until now, otherwise it would break the header.// In particular, DurationReport objects should be created passing title, NULL, so their// duration is logged into MYLOG instead.PrintHeader();bool is_dumpstate_restricted options_-telephony_only|| options_-wifi_only|| options_-limited_only;if (!is_dumpstate_restricted) {// Dump系统关键服务的状态// ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------的信息// Invoke critical dumpsys first to preserve system state, before doing anything else.RunDumpsysCritical();}if (options_-telephony_only) {DumpstateTelephonyOnly(calling_package);} else if (options_-wifi_only) {DumpstateWifiOnly();} else if (options_-limited_only) {DumpstateLimitedOnly();} else {// Dump state for the default case. This also drops root.// dump额外信息RunStatus s DumpstateDefaultAfterCritical();if (s ! RunStatus::OK) {if (s RunStatus::USER_CONSENT_DENIED) {HandleUserConsentDenied();}return s;}}// 解除重定向/* close output if needed */TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));// 完成zip的打包删除临时文件// Zip the (now complete) .tmp file within the internal directory.FinalizeFile();// 省略
}PrepareToWriteToFile函数中确定bugreport信息写入的文件名。文件名命名方式为bugreport-[device_name]-[build_id]-[localtime]。文件信息会输出到Log中。
dumpstate:
Bugreport dir: [/data/user_de/0/com.android.shell/files/bugreports]
Base name: [*] Suffix: [2024-04-22-19-18-14]
Log path: [/data/user_de/0/com.android.shell/files/bugreports/bugreport-*-2024-04-22-19-18-14-dumpstate_log-10419.txt]
Temporary path: [/data/user_de/0/com.android.shell/files/bugreports/bugreport-*-2024-04-22-19-18-14-.tmp] Screenshot path: []/** Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter* and adds the version file. Return false if zip_file could not be open to write.*/
static bool PrepareToWriteToFile() {MaybeResolveSymlink(ds.bugreport_internal_dir_);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_ StringPrintf(bugreport-%s-%s, device_name.c_str(), build_id.c_str());char date[80];strftime(date, sizeof(date), %Y-%m-%d-%H-%M-%S, localtime(ds.now_));ds.name_ date;if (ds.options_-telephony_only) {ds.base_name_ -telephony;} else if (ds.options_-wifi_only) {ds.base_name_ -wifi;}if (ds.options_-do_screenshot) {ds.screenshot_path_ ds.GetPath(ds.CalledByApi() ? -png.tmp : .png);}ds.tmp_path_ ds.GetPath(.tmp);ds.log_path_ ds.GetPath(-dumpstate_log- std::to_string(ds.pid_) .txt);std::string destination ds.CalledByApi()? StringPrintf([fd:%d], ds.options_-bugreport_fd.get()): ds.bugreport_internal_dir_.c_str();MYLOGD(Bugreport dir: [%s] Base name: [%s] Suffix: [%s] Log path: [%s] Temporary path: [%s] Screenshot path: [%s]\n,destination.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());ds.path_ ds.GetPath(ds.CalledByApi() ? -zip.tmp : .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));return false;}ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));ds.AddTextZipEntry(version.txt, ds.version_);return true;
}在PrintHeader函数中将bugreport-.txt中的头部信息输入到标准输出中而标准输出已经重定向到了bugreport-.tmp文件中。
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());int64_t module_metadata_version android::os::GetModuleMetadataVersion();if (module_metadata_version ! 0) {printf(Module Metadata version: % PRId64 \n, module_metadata_version);}printf(SDK extension versions [r%s s%s]\n,android::base::GetProperty(build.version.extensions.r, -).c_str(),android::base::GetProperty(build.version.extensions.s, -).c_str());printf(Kernel: );DumpFileToFd(STDOUT_FILENO, , /proc/version);printf(Command line: %s\n, strtok(cmdline_buf, \n));printf(Uptime: );RunCommandToFd(STDOUT_FILENO, , {uptime, -p},CommandOptions::WithTimeout(1).Always().Build());printf(Bugreport format version: %s\n, version_.c_str());printf(Dumpstate info: id%d pid%d dry_run%d parallel_run%d args%s bugreport_mode%s\n,id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),options_-args.c_str(), options_-bugreport_mode.c_str());printf(\n);
}然后在RunDumpsysCritical函数中通过ServiceManager获取当前系统的Service并调用Service的dump将Service的dump信息输出到bugreport-*.tmp文件中。另外会通过proto的形式再输出一份service的dump信息。
// frameworks/native/cmds/dumpstate/dumpstate.cpp
static void RunDumpsysText(const std::string title, int priority, std::chrono::milliseconds timeout,std::chrono::milliseconds service_timeout) {DurationReporter duration_reporter(title);dprintf(STDOUT_FILENO, ------ %s (/system/bin/dumpsys) ------\n, title.c_str());fsync(STDOUT_FILENO);RunDumpsysTextByPriority(title, priority, timeout, service_timeout);
}static void RunDumpsysText(const std::string title, int priority, std::chrono::milliseconds timeout,std::chrono::milliseconds service_timeout) {DurationReporter duration_reporter(title);dprintf(STDOUT_FILENO, ------ %s (/system/bin/dumpsys) ------\n, title.c_str());fsync(STDOUT_FILENO);RunDumpsysTextByPriority(title, priority, timeout, service_timeout);
}static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string title, int priority,std::chrono::milliseconds timeout,std::chrono::milliseconds service_timeout) {auto start std::chrono::steady_clock::now();spandroid::IServiceManager sm defaultServiceManager();Dumpsys dumpsys(sm.get());VectorString16 args;Dumpsys::setServiceArgs(args, /* asProto */ false, priority);VectorString16 services dumpsys.listServices(priority, /* supports_proto */ false);for (const String16 service : services) {RETURN_IF_USER_DENIED_CONSENT();std::string path(title);path.append( - ).append(String8(service).c_str());size_t bytes_written 0;// 在dumpthread中调用service的dumpstatus_t status dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args);if (status OK) {dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority);std::chrono::durationdouble elapsed_seconds;if (priority IServiceManager::DUMP_FLAG_PRIORITY_HIGH service String16(meminfo)) {// Use a longer timeout for meminfo, since 30s is not always enough.status dumpsys.writeDump(STDOUT_FILENO, service, 60s,/* as_proto */ false, elapsed_seconds, bytes_written);} else {status dumpsys.writeDump(STDOUT_FILENO, service, service_timeout,/* as_proto */ false, elapsed_seconds, bytes_written);}dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds);bool dump_complete (status OK);dumpsys.stopDumpThread(dump_complete);}auto elapsed_duration std::chrono::duration_caststd::chrono::milliseconds(std::chrono::steady_clock::now() - start);if (elapsed_duration timeout) {MYLOGE(*** command %s timed out after %llums\n, title.c_str(),elapsed_duration.count());break;}}return Dumpstate::RunStatus::OK;
}在DumpstateDefaultAfterCritical函数中dump额外信息以及将log、anr等等文件拷贝到zip文件中。
// frameworks/native/cmds/dumpstate/dumpstate.cpp
Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() {// Capture first logcat early on; useful to take a snapshot before dumpstate logs take over the// buffer.DoLogcat();// Capture timestamp after first logcat to use in next logcattime_t logcat_ts time(nullptr);/* collect stack traces from Dalvik and native processes (needs root) */if (dump_pool_) {RETURN_IF_USER_DENIED_CONSENT();// One thread is enough since we only need to enqueue DumpTraces here.dump_pool_-start(/* thread_counts */1);// DumpTraces takes long time, post it to the another thread in the// pool, if pool is availabledump_pool_-enqueueTask(DUMP_TRACES_TASK, Dumpstate::DumpTraces, ds, dump_traces_path);} else {RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_TRACES_TASK, ds.DumpTraces,dump_traces_path);}/* Run some operations that require root. */ds.tombstone_data_ GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());ds.anr_data_ GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping());ds.AddDir(RECOVERY_DIR, true);ds.AddDir(RECOVERY_DATA_DIR, true);ds.AddDir(UPDATE_ENGINE_LOG_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);ds.AddZipEntry(ZIP_ROOT_DIR PACKAGE_DEX_USE_LIST, PACKAGE_DEX_USE_LIST);}ds.AddDir(PREREBOOT_DATA_DIR, false);add_mountinfo();DumpIpTablesAsRoot();DumpDynamicPartitionInfo();ds.AddDir(OTA_METADATA_DIR, true);// Capture any IPSec policies in play. No keys are exposed here.RunCommand(IP XFRM POLICY, {ip, xfrm, policy}, CommandOptions::WithTimeout(10).Build());// Dump IPsec stats. No keys are exposed here.DumpFile(XFRM STATS, XFRM_STAT_PROC_FILE);// Run ss as root so we can see socket marks.RunCommand(DETAILED SOCKET STATE, {ss, -eionptu}, CommandOptions::WithTimeout(10).Build());// Run iotop as root to show top 100 IO threadsRunCommand(IOTOP, {iotop, -n, 1, -m, 100});// Gather shared memory buffer info if the product implements itRunCommand(Dmabuf dump, {dmabuf_dump});RunCommand(Dmabuf per-buffer/per-exporter/per-device stats, {dmabuf_dump, -b});DumpFile(PSI cpu, /proc/pressure/cpu);DumpFile(PSI memory, /proc/pressure/memory);DumpFile(PSI io, /proc/pressure/io);if (dump_pool_) {RETURN_IF_USER_DENIED_CONSENT();dump_pool_-waitForTask(DUMP_TRACES_TASK);// Current running thread in the pool is the root user also. Shutdown// the pool and restart later to ensure all threads in the pool could// drop the root user.dump_pool_-shutdown();}if (!DropRootUser()) {return Dumpstate::RunStatus::ERROR;}RETURN_IF_USER_DENIED_CONSENT();Dumpstate::RunStatus status dumpstate();// Capture logcat since the last time we did it.DoSystemLogcat(logcat_ts);return status;
}最后在FinalizeFile函数中将临时文件Bugreport-.tmpcopy到zip中并命名为Bugreport-.zip。然后删除临时文件完成zip文件的落盘。
/** Finalizes writing to the file by zipping the tmp file to the final location,* printing zipped file status, etc.*/static void FinalizeFile() { bool do_text_file !ds.FinishZipFile();if (do_text_file) {MYLOGE(Failed to finish zip file; sending text bugreport instead\n);}std::string final_path ds.path_;if (ds.options_-OutputToCustomFile()) {final_path ds.GetPath(ds.options_-out_dir, .zip);android::os::CopyFileToFile(ds.path_, final_path);}if (ds.options_-stream_to_socket) {android::os::CopyFileToFd(ds.path_, ds.control_socket_fd_);} else if (ds.options_-progress_updates_to_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, final_path.c_str());}}
}adb将Bugrepo-*.zip pull到本地
当bugreport文件收集好后会触发adb将相关文件pull到本地(Host)路径为执行adb命令的路径。
// packages/modules/adb/client/bugreport.cpp// Custom callback used to handle the output of zipped bugreports.
class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {public:BugreportStandardStreamsCallback(const std::string dest_dir, const std::string dest_file,bool show_progress, Bugreport* br): br_(br),src_file_(),dest_dir_(dest_dir),dest_file_(dest_file),line_message_(),invalid_lines_(),show_progress_(show_progress),status_(0),line_(),last_progress_percentage_(0) {SetLineMessage(generating);}int Done(int unused_) {// Pull the generated bug report.if (status_ 0) {// 将Bugreport-*.zip文件 pull到本地(host)status_ br_-DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1;if (status_ 0) {printf(Bug report copied to %s\n, destination.c_str());} else {fprintf(stderr,Bug report finished but could not be copied to %s.\nTry to run adb pull %s directory\nto copy it to a directory that can be written.\n,destination.c_str(), src_file_.c_str());}}return status_;}