• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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