1 /*
2 * Copyright (C) 2021 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 #include <stdio.h>
18 #include <fstream>
19 #include <string>
20
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <android-base/strings.h>
24 #include <gflags/gflags.h>
25
26 #include "common/libs/fs/shared_fd.h"
27 #include "common/libs/fs/shared_select.h"
28 #include "common/libs/utils/environment.h"
29 #include "common/libs/utils/files.h"
30 #include "common/libs/utils/known_paths.h"
31 #include "common/libs/utils/subprocess.h"
32 #include "common/libs/utils/tee_logging.h"
33 #include "host/libs/config/cuttlefish_config.h"
34 #include "ziparchive/zip_writer.h"
35
36 DEFINE_string(output, "host_bugreport.zip", "Where to write the output");
37 DEFINE_bool(include_adb_bugreport, false, "Includes device's `adb bugreport`.");
38
39 namespace cuttlefish {
40 namespace {
41
SaveFile(ZipWriter & writer,const std::string & zip_path,const std::string & file_path)42 void SaveFile(ZipWriter& writer, const std::string& zip_path,
43 const std::string& file_path) {
44 writer.StartEntry(zip_path, ZipWriter::kCompress | ZipWriter::kAlign32);
45 std::fstream file(file_path, std::fstream::in | std::fstream::binary);
46 do {
47 char data[1024 * 10];
48 file.read(data, sizeof(data));
49 writer.WriteBytes(data, file.gcount());
50 } while (file);
51 writer.FinishEntry();
52 if (file.bad()) {
53 LOG(ERROR) << "Error in logging " << file_path << " to " << zip_path;
54 }
55 }
56
AddNetsimdLogs(ZipWriter & writer)57 void AddNetsimdLogs(ZipWriter& writer) {
58 // The temp directory name depends on whether the `USER` environment variable
59 // is defined.
60 // https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:tools/netsim/rust/common/src/system/mod.rs;l=37-57;drc=360ddb57df49472a40275b125bb56af2a65395c7
61 std::string user = StringFromEnv("USER", "");
62 std::string dir = user.empty()
63 ? TempDir() + "/android/netsimd"
64 : fmt::format("{}/android-{}/netsimd", TempDir(), user);
65 if (!DirectoryExists(dir)) {
66 LOG(INFO) << "netsimd logs directory: `" << dir << "` does not exist.";
67 return;
68 }
69 auto names = DirectoryContents(dir);
70 if (!names.ok()) {
71 LOG(ERROR) << "Cannot read from netsimd directory `" << dir
72 << "`: " << names.error().FormatForEnv(/* color = */ false);
73 return;
74 }
75 for (const auto& name : names.value()) {
76 SaveFile(writer, "netsimd/" + name, dir + "/" + name);
77 }
78 }
79
CreateDeviceBugreport(const CuttlefishConfig::InstanceSpecific & ins,const std::string & out_dir)80 Result<void> CreateDeviceBugreport(
81 const CuttlefishConfig::InstanceSpecific& ins, const std::string& out_dir) {
82 std::string adb_bin_path = HostBinaryPath("adb");
83 CF_EXPECT(FileExists(adb_bin_path),
84 "adb binary not found at: " << adb_bin_path);
85 Command connect_cmd("timeout");
86 connect_cmd.SetWorkingDirectory(
87 "/"); // Use a deterministic working directory
88 connect_cmd.AddParameter("30s")
89 .AddParameter(adb_bin_path)
90 .AddParameter("connect")
91 .AddParameter(ins.adb_ip_and_port());
92 CF_EXPECT_EQ(connect_cmd.Start().Wait(), 0, "adb connect failed");
93 Command wait_for_device_cmd("timeout");
94 wait_for_device_cmd.SetWorkingDirectory(
95 "/"); // Use a deterministic working directory
96 wait_for_device_cmd.AddParameter("30s")
97 .AddParameter(adb_bin_path)
98 .AddParameter("-s")
99 .AddParameter(ins.adb_ip_and_port())
100 .AddParameter("wait-for-device");
101 CF_EXPECT_EQ(wait_for_device_cmd.Start().Wait(), 0,
102 "adb wait-for-device failed");
103 Command bugreport_cmd("timeout");
104 bugreport_cmd.SetWorkingDirectory(
105 "/"); // Use a deterministic working directory
106 bugreport_cmd.AddParameter("300s")
107 .AddParameter(adb_bin_path)
108 .AddParameter("-s")
109 .AddParameter(ins.adb_ip_and_port())
110 .AddParameter("bugreport")
111 .AddParameter(out_dir);
112 CF_EXPECT_EQ(bugreport_cmd.Start().Wait(), 0, "adb bugreport failed");
113 return {};
114 }
115
CvdHostBugreportMain(int argc,char ** argv)116 Result<void> CvdHostBugreportMain(int argc, char** argv) {
117 ::android::base::InitLogging(argv, android::base::StderrLogger);
118 google::ParseCommandLineFlags(&argc, &argv, true);
119
120 std::string log_filename = TempDir() + "/cvd_hbr.log.XXXXXX";
121 {
122 auto fd = SharedFD::Mkstemp(&log_filename);
123 CF_EXPECT(fd->IsOpen(), "Unable to create log file: " << fd->StrError());
124 android::base::SetLogger(TeeLogger({
125 {ConsoleSeverity(), SharedFD::Dup(2), MetadataLevel::ONLY_MESSAGE},
126 {LogFileSeverity(), fd, MetadataLevel::FULL},
127 }));
128 }
129
130 auto config = CuttlefishConfig::Get();
131 CHECK(config) << "Unable to find the config";
132
133 auto out_path = FLAGS_output.c_str();
134 std::unique_ptr<FILE, decltype(&fclose)> out(fopen(out_path, "wbe"), &fclose);
135 ZipWriter writer(out.get());
136
137 auto save = [&writer, config](const std::string& path) {
138 SaveFile(writer, "cuttlefish_assembly/" + path, config->AssemblyPath(path));
139 };
140 save("assemble_cvd.log");
141 save("cuttlefish_config.json");
142
143 for (const auto& instance : config->Instances()) {
144 auto save = [&writer, instance](const std::string& path) {
145 const auto& zip_name = instance.instance_name() + "/" + path;
146 const auto& file_name = instance.PerInstancePath(path.c_str());
147 SaveFile(writer, zip_name, file_name);
148 };
149 save("cuttlefish_config.json");
150 save("disk_config.txt");
151 if (DirectoryExists(instance.PerInstancePath("logs"))) {
152 auto result = DirectoryContents(instance.PerInstancePath("logs"));
153 if (result.ok()) {
154 for (const auto& log : result.value()) {
155 save("logs/" + log);
156 }
157 } else {
158 LOG(ERROR) << "Cannot read from logs directory: "
159 << result.error().FormatForEnv(/* color = */ false);
160 }
161 } else {
162 save("kernel.log");
163 save("launcher.log");
164 save("logcat");
165 save("metrics.log");
166 }
167
168 {
169 auto result = DirectoryContents(instance.PerInstancePath("tombstones"));
170 if (result.ok()) {
171 for (const auto& tombstone : result.value()) {
172 save("tombstones/" + tombstone);
173 }
174 } else {
175 LOG(ERROR) << "Cannot read from tombstones directory: "
176 << result.error().FormatForEnv(/* color = */ false);
177 }
178 }
179
180 {
181 auto result = DirectoryContents(instance.PerInstancePath("recording"));
182 if (result.ok()) {
183 for (const auto& recording : result.value()) {
184 save("recording/" + recording);
185 }
186 } else {
187 LOG(ERROR) << "Cannot read from recording directory: "
188 << result.error().FormatForEnv(/* color = */ false);
189 }
190 }
191
192 if (FLAGS_include_adb_bugreport) {
193 // TODO(b/359657254) Create the `adb bugreport` asynchronously.
194 std::string device_br_dir = TempDir() + "/cvd_dbrXXXXXX";
195 CF_EXPECTF(mkdtemp(device_br_dir.data()) != nullptr,
196 "mkdtemp failed: '{}'", strerror(errno));
197 auto result = CreateDeviceBugreport(instance, device_br_dir);
198 if (result.ok()) {
199 auto names = DirectoryContents(device_br_dir);
200 if (names.ok()) {
201 for (const auto& name : names.value()) {
202 std::string filename = device_br_dir + "/" + name;
203 SaveFile(writer, android::base::Basename(filename), filename);
204 }
205 } else {
206 LOG(ERROR) << "Cannot read from device bugreport directory: "
207 << names.error().FormatForEnv(/* color = */ false);
208 }
209 } else {
210 LOG(ERROR) << "Failed to create device bugreport: "
211 << result.error().FormatForEnv(/* color = */ false);
212 }
213 static_cast<void>(RecursivelyRemoveDirectory(device_br_dir));
214 }
215 }
216
217 AddNetsimdLogs(writer);
218
219 LOG(INFO) << "Building cvd bugreport completed";
220
221 SaveFile(writer, "cvd_bugreport_builder.log", log_filename);
222
223 writer.Finish();
224
225 LOG(INFO) << "Saved to \"" << FLAGS_output << "\"";
226
227 if (!RemoveFile(log_filename)) {
228 LOG(INFO) << "Failed to remove host bug report log file: " << log_filename;
229 }
230
231 return {};
232 }
233
234 } // namespace
235 } // namespace cuttlefish
236
main(int argc,char ** argv)237 int main(int argc, char** argv) {
238 auto result = cuttlefish::CvdHostBugreportMain(argc, argv);
239 CHECK(result.ok()) << result.error().FormatForEnv();
240 return 0;
241 }
242