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