Adam Shih | b5b3cb7 | 2023-03-22 11:26:34 +0800 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #define LOG_TAG "dumpstate_device" |
| 18 | |
| 19 | #include <inttypes.h> |
| 20 | |
| 21 | #include <android-base/file.h> |
| 22 | #include <android-base/stringprintf.h> |
| 23 | #include <android-base/properties.h> |
| 24 | #include <android-base/unique_fd.h> |
| 25 | #include <log/log.h> |
| 26 | #include <sys/stat.h> |
| 27 | #include <dump/pixel_dump.h> |
| 28 | #include "Dumpstate.h" |
| 29 | |
| 30 | #include "DumpstateUtil.h" |
| 31 | |
| 32 | #define HW_REVISION "ro.boot.hardware.revision" |
| 33 | |
| 34 | using android::os::dumpstate::CommandOptions; |
| 35 | using android::os::dumpstate::DumpFileToFd; |
| 36 | using android::os::dumpstate::PropertiesHelper; |
| 37 | using android::os::dumpstate::RunCommandToFd; |
| 38 | |
| 39 | namespace aidl { |
| 40 | namespace android { |
| 41 | namespace hardware { |
| 42 | namespace dumpstate { |
| 43 | |
| 44 | typedef std::chrono::time_point<std::chrono::steady_clock> timepoint_t; |
| 45 | |
| 46 | const char kVerboseLoggingProperty[] = "persist.vendor.verbose_logging_enabled"; |
| 47 | |
| 48 | timepoint_t startSection(int fd, const std::string §ionName) { |
| 49 | ::android::base::WriteStringToFd( |
| 50 | "\n" |
| 51 | "------ Section start: " + sectionName + " ------\n" |
| 52 | "\n", fd); |
| 53 | return std::chrono::steady_clock::now(); |
| 54 | } |
| 55 | |
| 56 | void endSection(int fd, const std::string §ionName, timepoint_t startTime) { |
| 57 | auto endTime = std::chrono::steady_clock::now(); |
| 58 | auto elapsedMsec = std::chrono::duration_cast<std::chrono::milliseconds> |
| 59 | (endTime - startTime).count(); |
| 60 | |
| 61 | ::android::base::WriteStringToFd( |
| 62 | "\n" |
| 63 | "------ Section end: " + sectionName + " ------\n" |
| 64 | "Elapsed msec: " + std::to_string(elapsedMsec) + "\n" |
| 65 | "\n", fd); |
| 66 | } |
| 67 | |
| 68 | // Dump data requested by an argument to the "dump" interface, or help info |
| 69 | // if the specified section is not supported. |
| 70 | void Dumpstate::dumpTextSection(int fd, const std::string §ionName) { |
| 71 | bool dumpAll = (sectionName == kAllSections); |
| 72 | std::string dumpFiles; |
| 73 | |
| 74 | // Execute all or designated programs under vendor/bin/dump/ |
| 75 | std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/vendor/bin/dump"), closedir); |
| 76 | if (!dir) { |
| 77 | ALOGE("Fail To Open Dir vendor/bin/dump/"); |
| 78 | ::android::base::WriteStringToFd("Fail To Open Dir vendor/bin/dump/\n", fd); |
| 79 | return; |
| 80 | } |
| 81 | dirent *entry; |
| 82 | while ((entry = readdir(dir.get())) != nullptr) { |
| 83 | // Skip '.', '..' |
| 84 | if (entry->d_name[0] == '.') { |
| 85 | continue; |
| 86 | } |
| 87 | std::string bin(entry->d_name); |
| 88 | dumpFiles = dumpFiles + " " + bin; |
| 89 | if (dumpAll || sectionName == bin) { |
| 90 | auto startTime = startSection(fd, bin); |
| 91 | RunCommandToFd(fd, "/vendor/bin/dump/"+bin, {"/vendor/bin/dump/"+bin}, CommandOptions::WithTimeout(15).Build()); |
| 92 | endSection(fd, bin, startTime); |
| 93 | if (!dumpAll) { |
| 94 | return; |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | if (dumpAll) { |
| 100 | RunCommandToFd(fd, "VENDOR PROPERTIES", {"/vendor/bin/getprop"}); |
| 101 | return; |
| 102 | } |
| 103 | |
| 104 | // An unsupported section was requested on the command line |
| 105 | ::android::base::WriteStringToFd("Unrecognized text section: " + sectionName + "\n", fd); |
| 106 | ::android::base::WriteStringToFd("Try \"" + kAllSections + "\" or one of the following:", fd); |
| 107 | ::android::base::WriteStringToFd(dumpFiles, fd); |
| 108 | ::android::base::WriteStringToFd("\nNote: sections with attachments (e.g. dump_soc) are" |
| 109 | "not available from the command line.\n", fd); |
| 110 | } |
| 111 | |
| 112 | void Dumpstate::dumpLogSection(int fd, int fd_bin) |
| 113 | { |
| 114 | std::string logDir = MODEM_LOG_DIRECTORY; |
| 115 | const std::string logCombined = logDir + "/combined_logs.tar"; |
| 116 | const std::string logAllDir = logDir + "/all_logs"; |
| 117 | |
| 118 | RunCommandToFd(fd, "MKDIR LOG", {"/vendor/bin/mkdir", "-p", logAllDir.c_str()}, CommandOptions::WithTimeout(2).Build()); |
| 119 | |
| 120 | dumpTextSection(fd, kAllSections); |
| 121 | |
| 122 | RunCommandToFd(fd, "TAR LOG", {"/vendor/bin/tar", "cvf", logCombined.c_str(), "-C", logAllDir.c_str(), "."}, CommandOptions::WithTimeout(20).Build()); |
| 123 | RunCommandToFd(fd, "CHG PERM", {"/vendor/bin/chmod", "a+w", logCombined.c_str()}, CommandOptions::WithTimeout(2).Build()); |
| 124 | |
| 125 | std::vector<uint8_t> buffer(65536); |
| 126 | ::android::base::unique_fd fdLog(TEMP_FAILURE_RETRY(open(logCombined.c_str(), O_RDONLY | O_CLOEXEC | O_NONBLOCK))); |
| 127 | |
| 128 | if (fdLog >= 0) { |
| 129 | while (1) { |
| 130 | ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fdLog, buffer.data(), buffer.size())); |
| 131 | |
| 132 | if (bytes_read == 0) { |
| 133 | break; |
| 134 | } else if (bytes_read < 0) { |
| 135 | ALOGD("read(%s): %s\n", logCombined.c_str(), strerror(errno)); |
| 136 | break; |
| 137 | } |
| 138 | |
| 139 | ssize_t result = TEMP_FAILURE_RETRY(write(fd_bin, buffer.data(), bytes_read)); |
| 140 | |
| 141 | if (result != bytes_read) { |
| 142 | ALOGD("Failed to write %zd bytes, actually written: %zd", bytes_read, result); |
| 143 | break; |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | RunCommandToFd(fd, "RM LOG DIR", { "/vendor/bin/rm", "-r", logAllDir.c_str()}, CommandOptions::WithTimeout(2).Build()); |
| 149 | RunCommandToFd(fd, "RM LOG", { "/vendor/bin/rm", logCombined.c_str()}, CommandOptions::WithTimeout(2).Build()); |
| 150 | } |
| 151 | |
| 152 | ndk::ScopedAStatus Dumpstate::dumpstateBoard(const std::vector<::ndk::ScopedFileDescriptor>& in_fds, |
| 153 | IDumpstateDevice::DumpstateMode in_mode, |
| 154 | int64_t in_timeoutMillis) { |
| 155 | // Unused arguments. |
| 156 | (void) in_timeoutMillis; |
| 157 | (void) in_mode; |
| 158 | |
| 159 | if (in_fds.size() < 1) { |
| 160 | ALOGE("no FDs\n"); |
| 161 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, |
| 162 | "No file descriptor"); |
| 163 | } |
| 164 | |
| 165 | int fd = in_fds[0].get(); |
| 166 | if (fd < 0) { |
| 167 | ALOGE("invalid FD: %d\n", fd); |
| 168 | return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, |
| 169 | "Invalid file descriptor"); |
| 170 | } |
| 171 | |
| 172 | if (in_fds.size() < 2) { |
| 173 | ALOGE("no FD for dumpstate_board binary\n"); |
| 174 | } else { |
| 175 | int fd_bin = in_fds[1].get(); |
| 176 | dumpLogSection(fd, fd_bin); |
| 177 | } |
| 178 | |
| 179 | return ndk::ScopedAStatus::ok(); |
| 180 | } |
| 181 | |
| 182 | ndk::ScopedAStatus Dumpstate::setVerboseLoggingEnabled(bool in_enable) { |
| 183 | ::android::base::SetProperty(kVerboseLoggingProperty, in_enable ? "true" : "false"); |
| 184 | return ndk::ScopedAStatus::ok(); |
| 185 | } |
| 186 | |
| 187 | ndk::ScopedAStatus Dumpstate::getVerboseLoggingEnabled(bool* _aidl_return) { |
| 188 | *_aidl_return = ::android::base::GetBoolProperty(kVerboseLoggingProperty, false); |
| 189 | return ndk::ScopedAStatus::ok(); |
| 190 | } |
| 191 | |
| 192 | // Since AIDLs that support the dump() interface are automatically invoked during |
| 193 | // bugreport generation and we don't want to generate a second copy of the same |
| 194 | // data that will go into dumpstate_board.txt, this function will only do |
| 195 | // something if it is called with an option, e.g. |
| 196 | // dumpsys android.hardware.dumpstate.IDumpstateDevice/default all |
| 197 | // |
| 198 | // Also, note that sections which generate attachments and/or binary data when |
| 199 | // included in a bugreport are not available through the dump() interface. |
| 200 | binder_status_t Dumpstate::dump(int fd, const char** args, uint32_t numArgs) { |
| 201 | |
| 202 | if (numArgs != 1) { |
| 203 | return STATUS_OK; |
| 204 | } |
| 205 | |
| 206 | dumpTextSection(fd, static_cast<std::string>(args[0])); |
| 207 | |
| 208 | fsync(fd); |
| 209 | return STATUS_OK; |
| 210 | } |
| 211 | |
| 212 | } // namespace dumpstate |
| 213 | } // namespace hardware |
| 214 | } // namespace android |
| 215 | } // namespace aidl |