Loading cmds/dumpstate/DumpstateService.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "extra_options: %s\n", ds_.options_->extra_options.c_str()); dprintf(fd, "version: %s\n", ds_.version_.c_str()); dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.c_str()); dprintf(fd, "bugreport_internal_dir_: %s\n", ds_.bugreport_internal_dir_.c_str()); dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str()); dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str()); dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str()); Loading cmds/dumpstate/dumpstate.cpp +148 −22 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ using android::String8; using android::TIMED_OUT; using android::UNKNOWN_ERROR; using android::Vector; using android::base::StringPrintf; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; using android::os::dumpstate::DumpstateSectionReporter; Loading Loading @@ -121,6 +122,69 @@ static const std::string ANR_FILE_PREFIX = "anr_"; // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); namespace android { namespace os { namespace { static int Open(std::string path, int flags, mode_t mode = 0) { int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); if (fd == -1) { MYLOGE("open(%s, %s)\n", path.c_str(), strerror(errno)); } return fd; } static int OpenForWrite(std::string path) { return Open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); } static int OpenForRead(std::string path) { return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); } bool CopyFile(int in_fd, int out_fd) { char buf[4096]; ssize_t byte_count; while ((byte_count = TEMP_FAILURE_RETRY(read(in_fd, buf, sizeof(buf)))) > 0) { if (!android::base::WriteFully(out_fd, buf, byte_count)) { return false; } } return (byte_count != -1); } static bool CopyFileToFd(const std::string& input_file, int out_fd) { MYLOGD("Going to copy bugreport file (%s) to %d\n", ds.path_.c_str(), out_fd); // Obtain a handle to the source file. android::base::unique_fd in_fd(OpenForRead(input_file)); if (out_fd != -1 && in_fd.get() != -1) { if (CopyFile(in_fd.get(), out_fd)) { return true; } MYLOGE("Failed to copy zip file: %s\n", strerror(errno)); } return false; } static bool CopyFileToFile(const std::string& input_file, const std::string& output_file) { if (input_file == output_file) { MYLOGD("Skipping copying bugreport file since the destination is the same (%s)\n", output_file.c_str()); return false; } MYLOGD("Going to copy bugreport file (%s) to %s\n", input_file.c_str(), output_file.c_str()); android::base::unique_fd out_fd(OpenForWrite(output_file)); return CopyFileToFd(input_file, out_fd.get()); } } // namespace } // namespace os } // namespace android static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, const CommandOptions& options = CommandOptions::DEFAULT) { return ds.RunCommand(title, fullCommand, options); Loading @@ -137,8 +201,6 @@ static int DumpFile(const std::string& title, const std::string& path) { // Relative directory (inside the zip) for all files copied as-is into the bugreport. static const std::string ZIP_ROOT_DIR = "FS"; // Must be hardcoded because dumpstate HAL implementation need SELinux access to it static const std::string kDumpstateBoardPath = "/bugreports/"; static const std::string kProtoPath = "proto/"; static const std::string kProtoExt = ".proto"; static const std::string kDumpstateBoardFiles[] = { Loading Loading @@ -1145,7 +1207,7 @@ static void DumpHals() { return !isalnum(c) && std::string("@-_:.").find(c) == std::string::npos; }, '_'); const std::string path = kDumpstateBoardPath + "lshal_debug_" + cleanName; const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName; { auto fd = android::base::unique_fd( Loading Loading @@ -1529,7 +1591,8 @@ void Dumpstate::DumpstateBoard() { std::vector<std::string> paths; std::vector<android::base::ScopeGuard<std::function<void()>>> remover; for (int i = 0; i < NUM_OF_DUMPS; i++) { paths.emplace_back(kDumpstateBoardPath + kDumpstateBoardFiles[i]); paths.emplace_back(StringPrintf("%s/%s", ds.bugreport_internal_dir_.c_str(), kDumpstateBoardFiles[i].c_str())); remover.emplace_back(android::base::make_scope_guard(std::bind( [](std::string path) { if (remove(path.c_str()) != 0 && errno != ENOENT) { Loading Loading @@ -1687,7 +1750,8 @@ bool Dumpstate::FinishZipFile() { MYLOGE("Failed to add dumpstate log to .zip file\n"); return false; } // ... and re-opens it for further logging. // TODO: Should truncate the existing file. // ... and re-open it for further logging. redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str())); fprintf(stderr, "\n"); Loading Loading @@ -1770,17 +1834,30 @@ static void Vibrate(int duration_ms) { // clang-format on } static void MaybeResolveSymlink(std::string* path) { std::string resolved_path; if (android::base::Readlink(*path, &resolved_path)) { *path = resolved_path; } } /* * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter * if we are writing zip files and adds the version file. */ static void PrepareToWriteToFile() { MaybeResolveSymlink(&ds.bugreport_internal_dir_); std::string base_name_part1 = "bugreport"; if (!ds.options_->use_outfile.empty()) { ds.bugreport_dir_ = dirname(ds.options_->use_outfile.c_str()); base_name_part1 = basename(ds.options_->use_outfile.c_str()); } 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(ds.options_->use_outfile.c_str()), device_name.c_str(), build_id.c_str()); StringPrintf("%s-%s-%s", base_name_part1.c_str(), device_name.c_str(), build_id.c_str()); if (ds.options_->do_add_date) { char date[80]; strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); Loading @@ -1801,15 +1878,18 @@ static void PrepareToWriteToFile() { ds.tmp_path_ = ds.GetPath(".tmp"); ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt"); std::string destination = ds.options_->fd != -1 ? StringPrintf("[fd:%d]", ds.options_->fd) : ds.bugreport_dir_.c_str(); MYLOGD( "Bugreport dir: %s\n" "Internal 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()); destination.c_str(), ds.bugreport_internal_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 (ds.options_->do_zip_file) { ds.path_ = ds.GetPath(".zip"); Loading Loading @@ -1875,6 +1955,19 @@ static void FinalizeFile() { ds.path_ = new_path; } } // The zip file lives in an internal directory. Copy it over to output. bool copy_succeeded = false; if (ds.options_->fd != -1) { copy_succeeded = android::os::CopyFileToFd(ds.path_, ds.options_->fd); } else { ds.final_path_ = ds.GetPath(ds.bugreport_dir_, ".zip"); copy_succeeded = android::os::CopyFileToFile(ds.path_, ds.final_path_); } if (copy_succeeded) { if (remove(ds.path_.c_str())) { MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno)); } } } } if (do_text_file) { Loading @@ -1899,8 +1992,9 @@ static void FinalizeFile() { /* Broadcasts that we are done with the bugreport */ static void SendBugreportFinishedBroadcast() { if (!ds.path_.empty()) { MYLOGI("Final bugreport path: %s\n", ds.path_.c_str()); // TODO(b/111441001): use callback instead of broadcast. if (!ds.final_path_.empty()) { MYLOGI("Final bugreport path: %s\n", ds.final_path_.c_str()); // clang-format off std::vector<std::string> am_args = { Loading @@ -1908,7 +2002,7 @@ static void SendBugreportFinishedBroadcast() { "--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.BUGREPORT", ds.final_path_, "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_ }; // clang-format on Loading @@ -1930,7 +2024,7 @@ static void SendBugreportFinishedBroadcast() { if (ds.options_->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_)); am_args.push_back(SHA256_file_hash(ds.final_path_)); SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", am_args); } else { SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args); Loading Loading @@ -2060,6 +2154,7 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI("telephony_only: %d\n", options.telephony_only); MYLOGI("wifi_only: %d\n", options.wifi_only); MYLOGI("do_progress_updates: %d\n", options.do_progress_updates); MYLOGI("fd: %d\n", options.fd); MYLOGI("use_outfile: %s\n", options.use_outfile.c_str()); MYLOGI("extra_options: %s\n", options.extra_options.c_str()); MYLOGI("args: %s\n", options.args.c_str()); Loading Loading @@ -2125,8 +2220,13 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) } bool Dumpstate::DumpOptions::ValidateOptions() const { if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && use_outfile.empty()) { if (fd != -1 && !do_zip_file) { return false; } bool has_out_file_options = !use_outfile.empty() || fd != -1; if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && !has_out_file_options) { return false; } Loading @@ -2148,10 +2248,31 @@ void Dumpstate::SetOptions(std::unique_ptr<DumpOptions> options) { options_ = std::move(options); } /* * Dumps relevant information to a bugreport based on the given options. * * The bugreport can be dumped to a file or streamed to a socket. * * How dumping to file works: * stdout is redirected to a temporary file. This will later become the main bugreport entry. * stderr is redirected a log file. * * The temporary bugreport is then populated via printfs, dumping contents of files and * output of commands to stdout. * * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final * text file. * * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also * gets added to the archive. * * Bugreports are first generated in a local directory and later copied to the caller's fd or * directory. */ Dumpstate::RunStatus Dumpstate::Run() { LogDumpOptions(*options_); if (!options_->ValidateOptions()) { MYLOGE("Invalid options specified\n"); LogDumpOptions(*options_); return RunStatus::INVALID_INPUT; } /* set as high priority, and protect from OOM killer */ Loading Loading @@ -2191,8 +2312,8 @@ Dumpstate::RunStatus Dumpstate::Run() { // 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(options_->use_outfile.c_str())) is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()) : ""; progress_.reset(new Progress(stats_path)); Loading Loading @@ -2289,13 +2410,18 @@ Dumpstate::RunStatus Dumpstate::Run() { int dup_stdout_fd; int dup_stderr_fd; if (is_redirecting) { // Redirect stderr to log_path_ for debugging. TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); redirect_to_file(stderr, const_cast<char*>(log_path_.c_str())); if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(), strerror(errno)); } // 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 entry directly, but the libziparchive doesn't support that option yet. */ Loading Loading @@ -2370,7 +2496,7 @@ Dumpstate::RunStatus Dumpstate::Run() { return RunStatus::OK; } /* Main entry point for dumpstate. */ /* Main entry point for dumpstate binary. */ int run_main(int argc, char* argv[]) { std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); Dumpstate::RunStatus status = options->Initialize(argc, argv); Loading @@ -2382,7 +2508,7 @@ int run_main(int argc, char* argv[]) { switch (status) { case Dumpstate::RunStatus::OK: return 0; break; // TODO(b/111441001): Exit directly in the following cases. case Dumpstate::RunStatus::HELP: ShowUsageAndExit(0 /* exit code */); break; Loading cmds/dumpstate/dumpstate.h +33 −12 Original line number Diff line number Diff line Loading @@ -142,7 +142,7 @@ class Progress { float growth_factor_; int32_t n_runs_; int32_t average_max_; const std::string& path_; std::string path_; }; /* Loading @@ -163,6 +163,11 @@ static std::string VERSION_SPLIT_ANR = "3.0-dev-split-anr"; */ static std::string VERSION_DEFAULT = "default"; /* * Directory used by Dumpstate binary to keep its local files. */ static const std::string DUMPSTATE_DIRECTORY = "/bugreports"; /* * Structure that contains the information of an open dump file. */ Loading Loading @@ -300,7 +305,11 @@ class Dumpstate { */ bool FinishZipFile(); /* Gets the path of a bugreport file with the given suffix. */ /* Constructs a full path inside directory with file name formatted using the given suffix. */ std::string GetPath(const std::string& directory, const std::string& suffix) const; /* Constructs a full path inside bugreport_internal_dir_ with file name formatted using the * given suffix. */ std::string GetPath(const std::string& suffix) const; /* Returns true if the current version supports priority dump feature. */ Loading @@ -314,7 +323,6 @@ class Dumpstate { /* Sets runtime options. */ void SetOptions(std::unique_ptr<DumpOptions> options); // TODO: add other options from DumpState. /* * Structure to hold options that determine the behavior of dumpstate. */ Loading @@ -333,6 +341,10 @@ class Dumpstate { bool wifi_only = false; // Whether progress updates should be published. bool do_progress_updates = false; // File descriptor to output zip file. -1 indicates not set. Takes precedence over // use_outfile. int fd = -1; // Partial path to output file. std::string use_outfile; // TODO: rename to MODE. // Extra options passed as system property. Loading Loading @@ -381,12 +393,6 @@ class Dumpstate { // Bugreport format version; std::string version_ = VERSION_CURRENT; // Full path of the directory where the bugreport files will be written. std::string bugreport_dir_; // Full path of the temporary file containing the screenshot (when requested). std::string screenshot_path_; time_t now_; // Base name (without suffix or extensions) of the bugreport files, typically Loading @@ -397,15 +403,30 @@ class Dumpstate { // `-d`), but it could be changed by the user.. std::string name_; // Full path of the temporary file containing the bugreport. std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY; // Full path of the temporary file containing the bugreport, inside bugreport_internal_dir_. // At the very end this file is pulled into the zip file. std::string tmp_path_; // Full path of the file containing the dumpstate logs. // Full path of the file containing the dumpstate logs, inside bugreport_internal_dir_. // This is useful for debugging. std::string log_path_; // Pointer to the actual path, be it zip or text. // Full path of the bugreport file, be it zip or text, inside bugreport_internal_dir_. std::string path_; // TODO: If temporary this should be removed at the end. // Full path of the temporary file containing the screenshot (when requested). std::string screenshot_path_; // TODO(b/111441001): remove when obsolete. // Full path of the final zip file inside the caller-specified directory, if available. std::string final_path_; // The caller-specified directory, if available. std::string bugreport_dir_; // Pointer to the zipped file. std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose}; Loading cmds/dumpstate/utils.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -229,7 +229,11 @@ bool Dumpstate::IsZipping() const { } std::string Dumpstate::GetPath(const std::string& suffix) const { return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(), return GetPath(bugreport_internal_dir_, suffix); } std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), name_.c_str(), suffix.c_str()); } Loading Loading
cmds/dumpstate/DumpstateService.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "extra_options: %s\n", ds_.options_->extra_options.c_str()); dprintf(fd, "version: %s\n", ds_.version_.c_str()); dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.c_str()); dprintf(fd, "bugreport_internal_dir_: %s\n", ds_.bugreport_internal_dir_.c_str()); dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str()); dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str()); dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str()); Loading
cmds/dumpstate/dumpstate.cpp +148 −22 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ using android::String8; using android::TIMED_OUT; using android::UNKNOWN_ERROR; using android::Vector; using android::base::StringPrintf; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; using android::os::dumpstate::DumpstateSectionReporter; Loading Loading @@ -121,6 +122,69 @@ static const std::string ANR_FILE_PREFIX = "anr_"; // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); namespace android { namespace os { namespace { static int Open(std::string path, int flags, mode_t mode = 0) { int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); if (fd == -1) { MYLOGE("open(%s, %s)\n", path.c_str(), strerror(errno)); } return fd; } static int OpenForWrite(std::string path) { return Open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); } static int OpenForRead(std::string path) { return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); } bool CopyFile(int in_fd, int out_fd) { char buf[4096]; ssize_t byte_count; while ((byte_count = TEMP_FAILURE_RETRY(read(in_fd, buf, sizeof(buf)))) > 0) { if (!android::base::WriteFully(out_fd, buf, byte_count)) { return false; } } return (byte_count != -1); } static bool CopyFileToFd(const std::string& input_file, int out_fd) { MYLOGD("Going to copy bugreport file (%s) to %d\n", ds.path_.c_str(), out_fd); // Obtain a handle to the source file. android::base::unique_fd in_fd(OpenForRead(input_file)); if (out_fd != -1 && in_fd.get() != -1) { if (CopyFile(in_fd.get(), out_fd)) { return true; } MYLOGE("Failed to copy zip file: %s\n", strerror(errno)); } return false; } static bool CopyFileToFile(const std::string& input_file, const std::string& output_file) { if (input_file == output_file) { MYLOGD("Skipping copying bugreport file since the destination is the same (%s)\n", output_file.c_str()); return false; } MYLOGD("Going to copy bugreport file (%s) to %s\n", input_file.c_str(), output_file.c_str()); android::base::unique_fd out_fd(OpenForWrite(output_file)); return CopyFileToFd(input_file, out_fd.get()); } } // namespace } // namespace os } // namespace android static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, const CommandOptions& options = CommandOptions::DEFAULT) { return ds.RunCommand(title, fullCommand, options); Loading @@ -137,8 +201,6 @@ static int DumpFile(const std::string& title, const std::string& path) { // Relative directory (inside the zip) for all files copied as-is into the bugreport. static const std::string ZIP_ROOT_DIR = "FS"; // Must be hardcoded because dumpstate HAL implementation need SELinux access to it static const std::string kDumpstateBoardPath = "/bugreports/"; static const std::string kProtoPath = "proto/"; static const std::string kProtoExt = ".proto"; static const std::string kDumpstateBoardFiles[] = { Loading Loading @@ -1145,7 +1207,7 @@ static void DumpHals() { return !isalnum(c) && std::string("@-_:.").find(c) == std::string::npos; }, '_'); const std::string path = kDumpstateBoardPath + "lshal_debug_" + cleanName; const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName; { auto fd = android::base::unique_fd( Loading Loading @@ -1529,7 +1591,8 @@ void Dumpstate::DumpstateBoard() { std::vector<std::string> paths; std::vector<android::base::ScopeGuard<std::function<void()>>> remover; for (int i = 0; i < NUM_OF_DUMPS; i++) { paths.emplace_back(kDumpstateBoardPath + kDumpstateBoardFiles[i]); paths.emplace_back(StringPrintf("%s/%s", ds.bugreport_internal_dir_.c_str(), kDumpstateBoardFiles[i].c_str())); remover.emplace_back(android::base::make_scope_guard(std::bind( [](std::string path) { if (remove(path.c_str()) != 0 && errno != ENOENT) { Loading Loading @@ -1687,7 +1750,8 @@ bool Dumpstate::FinishZipFile() { MYLOGE("Failed to add dumpstate log to .zip file\n"); return false; } // ... and re-opens it for further logging. // TODO: Should truncate the existing file. // ... and re-open it for further logging. redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str())); fprintf(stderr, "\n"); Loading Loading @@ -1770,17 +1834,30 @@ static void Vibrate(int duration_ms) { // clang-format on } static void MaybeResolveSymlink(std::string* path) { std::string resolved_path; if (android::base::Readlink(*path, &resolved_path)) { *path = resolved_path; } } /* * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter * if we are writing zip files and adds the version file. */ static void PrepareToWriteToFile() { MaybeResolveSymlink(&ds.bugreport_internal_dir_); std::string base_name_part1 = "bugreport"; if (!ds.options_->use_outfile.empty()) { ds.bugreport_dir_ = dirname(ds.options_->use_outfile.c_str()); base_name_part1 = basename(ds.options_->use_outfile.c_str()); } 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(ds.options_->use_outfile.c_str()), device_name.c_str(), build_id.c_str()); StringPrintf("%s-%s-%s", base_name_part1.c_str(), device_name.c_str(), build_id.c_str()); if (ds.options_->do_add_date) { char date[80]; strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); Loading @@ -1801,15 +1878,18 @@ static void PrepareToWriteToFile() { ds.tmp_path_ = ds.GetPath(".tmp"); ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt"); std::string destination = ds.options_->fd != -1 ? StringPrintf("[fd:%d]", ds.options_->fd) : ds.bugreport_dir_.c_str(); MYLOGD( "Bugreport dir: %s\n" "Internal 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()); destination.c_str(), ds.bugreport_internal_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 (ds.options_->do_zip_file) { ds.path_ = ds.GetPath(".zip"); Loading Loading @@ -1875,6 +1955,19 @@ static void FinalizeFile() { ds.path_ = new_path; } } // The zip file lives in an internal directory. Copy it over to output. bool copy_succeeded = false; if (ds.options_->fd != -1) { copy_succeeded = android::os::CopyFileToFd(ds.path_, ds.options_->fd); } else { ds.final_path_ = ds.GetPath(ds.bugreport_dir_, ".zip"); copy_succeeded = android::os::CopyFileToFile(ds.path_, ds.final_path_); } if (copy_succeeded) { if (remove(ds.path_.c_str())) { MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno)); } } } } if (do_text_file) { Loading @@ -1899,8 +1992,9 @@ static void FinalizeFile() { /* Broadcasts that we are done with the bugreport */ static void SendBugreportFinishedBroadcast() { if (!ds.path_.empty()) { MYLOGI("Final bugreport path: %s\n", ds.path_.c_str()); // TODO(b/111441001): use callback instead of broadcast. if (!ds.final_path_.empty()) { MYLOGI("Final bugreport path: %s\n", ds.final_path_.c_str()); // clang-format off std::vector<std::string> am_args = { Loading @@ -1908,7 +2002,7 @@ static void SendBugreportFinishedBroadcast() { "--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.BUGREPORT", ds.final_path_, "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_ }; // clang-format on Loading @@ -1930,7 +2024,7 @@ static void SendBugreportFinishedBroadcast() { if (ds.options_->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_)); am_args.push_back(SHA256_file_hash(ds.final_path_)); SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", am_args); } else { SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args); Loading Loading @@ -2060,6 +2154,7 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI("telephony_only: %d\n", options.telephony_only); MYLOGI("wifi_only: %d\n", options.wifi_only); MYLOGI("do_progress_updates: %d\n", options.do_progress_updates); MYLOGI("fd: %d\n", options.fd); MYLOGI("use_outfile: %s\n", options.use_outfile.c_str()); MYLOGI("extra_options: %s\n", options.extra_options.c_str()); MYLOGI("args: %s\n", options.args.c_str()); Loading Loading @@ -2125,8 +2220,13 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) } bool Dumpstate::DumpOptions::ValidateOptions() const { if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && use_outfile.empty()) { if (fd != -1 && !do_zip_file) { return false; } bool has_out_file_options = !use_outfile.empty() || fd != -1; if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && !has_out_file_options) { return false; } Loading @@ -2148,10 +2248,31 @@ void Dumpstate::SetOptions(std::unique_ptr<DumpOptions> options) { options_ = std::move(options); } /* * Dumps relevant information to a bugreport based on the given options. * * The bugreport can be dumped to a file or streamed to a socket. * * How dumping to file works: * stdout is redirected to a temporary file. This will later become the main bugreport entry. * stderr is redirected a log file. * * The temporary bugreport is then populated via printfs, dumping contents of files and * output of commands to stdout. * * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final * text file. * * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also * gets added to the archive. * * Bugreports are first generated in a local directory and later copied to the caller's fd or * directory. */ Dumpstate::RunStatus Dumpstate::Run() { LogDumpOptions(*options_); if (!options_->ValidateOptions()) { MYLOGE("Invalid options specified\n"); LogDumpOptions(*options_); return RunStatus::INVALID_INPUT; } /* set as high priority, and protect from OOM killer */ Loading Loading @@ -2191,8 +2312,8 @@ Dumpstate::RunStatus Dumpstate::Run() { // 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(options_->use_outfile.c_str())) is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()) : ""; progress_.reset(new Progress(stats_path)); Loading Loading @@ -2289,13 +2410,18 @@ Dumpstate::RunStatus Dumpstate::Run() { int dup_stdout_fd; int dup_stderr_fd; if (is_redirecting) { // Redirect stderr to log_path_ for debugging. TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); redirect_to_file(stderr, const_cast<char*>(log_path_.c_str())); if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(), strerror(errno)); } // 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 entry directly, but the libziparchive doesn't support that option yet. */ Loading Loading @@ -2370,7 +2496,7 @@ Dumpstate::RunStatus Dumpstate::Run() { return RunStatus::OK; } /* Main entry point for dumpstate. */ /* Main entry point for dumpstate binary. */ int run_main(int argc, char* argv[]) { std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); Dumpstate::RunStatus status = options->Initialize(argc, argv); Loading @@ -2382,7 +2508,7 @@ int run_main(int argc, char* argv[]) { switch (status) { case Dumpstate::RunStatus::OK: return 0; break; // TODO(b/111441001): Exit directly in the following cases. case Dumpstate::RunStatus::HELP: ShowUsageAndExit(0 /* exit code */); break; Loading
cmds/dumpstate/dumpstate.h +33 −12 Original line number Diff line number Diff line Loading @@ -142,7 +142,7 @@ class Progress { float growth_factor_; int32_t n_runs_; int32_t average_max_; const std::string& path_; std::string path_; }; /* Loading @@ -163,6 +163,11 @@ static std::string VERSION_SPLIT_ANR = "3.0-dev-split-anr"; */ static std::string VERSION_DEFAULT = "default"; /* * Directory used by Dumpstate binary to keep its local files. */ static const std::string DUMPSTATE_DIRECTORY = "/bugreports"; /* * Structure that contains the information of an open dump file. */ Loading Loading @@ -300,7 +305,11 @@ class Dumpstate { */ bool FinishZipFile(); /* Gets the path of a bugreport file with the given suffix. */ /* Constructs a full path inside directory with file name formatted using the given suffix. */ std::string GetPath(const std::string& directory, const std::string& suffix) const; /* Constructs a full path inside bugreport_internal_dir_ with file name formatted using the * given suffix. */ std::string GetPath(const std::string& suffix) const; /* Returns true if the current version supports priority dump feature. */ Loading @@ -314,7 +323,6 @@ class Dumpstate { /* Sets runtime options. */ void SetOptions(std::unique_ptr<DumpOptions> options); // TODO: add other options from DumpState. /* * Structure to hold options that determine the behavior of dumpstate. */ Loading @@ -333,6 +341,10 @@ class Dumpstate { bool wifi_only = false; // Whether progress updates should be published. bool do_progress_updates = false; // File descriptor to output zip file. -1 indicates not set. Takes precedence over // use_outfile. int fd = -1; // Partial path to output file. std::string use_outfile; // TODO: rename to MODE. // Extra options passed as system property. Loading Loading @@ -381,12 +393,6 @@ class Dumpstate { // Bugreport format version; std::string version_ = VERSION_CURRENT; // Full path of the directory where the bugreport files will be written. std::string bugreport_dir_; // Full path of the temporary file containing the screenshot (when requested). std::string screenshot_path_; time_t now_; // Base name (without suffix or extensions) of the bugreport files, typically Loading @@ -397,15 +403,30 @@ class Dumpstate { // `-d`), but it could be changed by the user.. std::string name_; // Full path of the temporary file containing the bugreport. std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY; // Full path of the temporary file containing the bugreport, inside bugreport_internal_dir_. // At the very end this file is pulled into the zip file. std::string tmp_path_; // Full path of the file containing the dumpstate logs. // Full path of the file containing the dumpstate logs, inside bugreport_internal_dir_. // This is useful for debugging. std::string log_path_; // Pointer to the actual path, be it zip or text. // Full path of the bugreport file, be it zip or text, inside bugreport_internal_dir_. std::string path_; // TODO: If temporary this should be removed at the end. // Full path of the temporary file containing the screenshot (when requested). std::string screenshot_path_; // TODO(b/111441001): remove when obsolete. // Full path of the final zip file inside the caller-specified directory, if available. std::string final_path_; // The caller-specified directory, if available. std::string bugreport_dir_; // Pointer to the zipped file. std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose}; Loading
cmds/dumpstate/utils.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -229,7 +229,11 @@ bool Dumpstate::IsZipping() const { } std::string Dumpstate::GetPath(const std::string& suffix) const { return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(), return GetPath(bugreport_internal_dir_, suffix); } std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), name_.c_str(), suffix.c_str()); } Loading