1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/system_logs/debug_daemon_log_source.h"
6
7 #include <stddef.h>
8
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/files/file_util.h"
14 #include "base/logging.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/no_destructor.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/task/post_task.h"
21 #include "base/task/thread_pool.h"
22 #include "chrome/browser/chromeos/profiles/profile_helper.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chromeos/cryptohome/cryptohome_parameters.h"
25 #include "chromeos/dbus/dbus_thread_manager.h"
26 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
27 #include "components/feedback/feedback_util.h"
28 #include "components/user_manager/user.h"
29 #include "components/user_manager/user_manager.h"
30 #include "content/public/browser/browser_thread.h"
31
32 namespace system_logs {
33
34 namespace {
35
36 constexpr char kNotAvailable[] = "<not available>";
37 constexpr char kRoutesKeyName[] = "routes";
38 constexpr char kLogTruncated[] = "<earlier logs truncated>\n";
39
40 // List of user log files that Chrome reads directly as these logs are generated
41 // by Chrome itself.
42 constexpr struct UserLogs {
43 // A string key used as a title for this log in feedback reports.
44 const char* log_key;
45
46 // The log file's path relative to the user's profile directory.
47 const char* log_file_relative_path;
48 } kUserLogs[] = {
49 {"chrome_user_log", "log/chrome"},
50 {"chrome_user_log.PREVIOUS", "log/chrome.PREVIOUS"},
51 {"libassistant_user_log", "log/libassistant.log"},
52 {"login-times", "login-times"},
53 {"logout-times", "logout-times"},
54 };
55
56 // List of debugd entries to exclude from the results.
57 constexpr std::array<const char*, 2> kExcludeList = {
58 // Shill device and service properties are retrieved by ShillLogSource.
59 // TODO(https://crbug.com/967800): Modify debugd to omit these for
60 // feedback report gathering and remove these entries.
61 "network-devices",
62 "network-services",
63 };
64
65 // Buffer size for user logs in bytes. Given that maximum feedback report size
66 // is ~7M and that majority of log files are under 1M, we set a per-file limit
67 // of 1MiB.
68 const int64_t kMaxLogSize = 1024 * 1024;
69
70 } // namespace
71
72 // Reads the contents of the user log files listed in |kUserLogs| and adds them
73 // to the |response| parameter.
ReadUserLogFiles(const std::vector<base::FilePath> & profile_dirs,SystemLogsResponse * response)74 void ReadUserLogFiles(const std::vector<base::FilePath>& profile_dirs,
75 SystemLogsResponse* response) {
76 for (size_t i = 0; i < profile_dirs.size(); ++i) {
77 std::string profile_prefix = "Profile[" + base::NumberToString(i) + "] ";
78 for (const auto& log : kUserLogs) {
79 std::string value;
80 const bool read_success = feedback_util::ReadEndOfFile(
81 profile_dirs[i].Append(log.log_file_relative_path), kMaxLogSize,
82 &value);
83
84 if (read_success && value.length() == kMaxLogSize) {
85 value.replace(0, strlen(kLogTruncated), kLogTruncated);
86
87 LOG(WARNING) << "Large log file was likely truncated: "
88 << log.log_file_relative_path;
89 }
90
91 response->emplace(
92 profile_prefix + log.log_key,
93 (read_success && !value.empty()) ? std::move(value) : kNotAvailable);
94 }
95 }
96 }
97
DebugDaemonLogSource(bool scrub)98 DebugDaemonLogSource::DebugDaemonLogSource(bool scrub)
99 : SystemLogsSource("DebugDemon"),
100 response_(new SystemLogsResponse()),
101 num_pending_requests_(0),
102 scrub_(scrub) {}
103
~DebugDaemonLogSource()104 DebugDaemonLogSource::~DebugDaemonLogSource() {}
105
Fetch(SysLogsSourceCallback callback)106 void DebugDaemonLogSource::Fetch(SysLogsSourceCallback callback) {
107 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
108 DCHECK(!callback.is_null());
109 DCHECK(callback_.is_null());
110
111 callback_ = std::move(callback);
112 chromeos::DebugDaemonClient* client =
113 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
114
115 client->GetRoutes(true, // Numeric
116 false, // No IPv6
117 base::BindOnce(&DebugDaemonLogSource::OnGetRoutes,
118 weak_ptr_factory_.GetWeakPtr()));
119 ++num_pending_requests_;
120
121 if (scrub_) {
122 const user_manager::User* user =
123 user_manager::UserManager::Get()->GetActiveUser();
124 client->GetScrubbedBigLogs(
125 cryptohome::CreateAccountIdentifierFromAccountId(
126 user ? user->GetAccountId() : EmptyAccountId()),
127 base::BindOnce(&DebugDaemonLogSource::OnGetLogs,
128 weak_ptr_factory_.GetWeakPtr()));
129 } else {
130 client->GetAllLogs(base::BindOnce(&DebugDaemonLogSource::OnGetLogs,
131 weak_ptr_factory_.GetWeakPtr()));
132 }
133 ++num_pending_requests_;
134 }
135
OnGetRoutes(base::Optional<std::vector<std::string>> routes)136 void DebugDaemonLogSource::OnGetRoutes(
137 base::Optional<std::vector<std::string>> routes) {
138 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
139
140 (*response_)[kRoutesKeyName] = routes.has_value()
141 ? base::JoinString(routes.value(), "\n")
142 : kNotAvailable;
143 RequestCompleted();
144 }
145
OnGetOneLog(std::string key,base::Optional<std::string> status)146 void DebugDaemonLogSource::OnGetOneLog(std::string key,
147 base::Optional<std::string> status) {
148 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
149
150 (*response_)[std::move(key)] = std::move(status).value_or(kNotAvailable);
151 RequestCompleted();
152 }
153
OnGetLogs(bool,const KeyValueMap & logs)154 void DebugDaemonLogSource::OnGetLogs(bool /* succeeded */,
155 const KeyValueMap& logs) {
156 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
157
158 // We ignore 'succeeded' for this callback - we want to display as much of the
159 // debug info as we can even if we failed partway through parsing, and if we
160 // couldn't fetch any of it, none of the fields will even appear.
161 for (const auto& log : logs) {
162 if (base::Contains(kExcludeList, log.first))
163 continue;
164 response_->insert(log);
165 }
166 RequestCompleted();
167 }
168
GetLoggedInUsersLogFiles()169 void DebugDaemonLogSource::GetLoggedInUsersLogFiles() {
170 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
171
172 // List all logged-in users' profile directories.
173 std::vector<base::FilePath> profile_dirs;
174 const user_manager::UserList& users =
175 user_manager::UserManager::Get()->GetLoggedInUsers();
176 for (const auto* user : users) {
177 if (user->username_hash().empty())
178 continue;
179
180 profile_dirs.emplace_back(
181 chromeos::ProfileHelper::GetProfilePathByUserIdHash(
182 user->username_hash()));
183 }
184
185 auto response = std::make_unique<SystemLogsResponse>();
186 SystemLogsResponse* response_ptr = response.get();
187 base::ThreadPool::PostTaskAndReply(
188 FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
189 base::BindOnce(&ReadUserLogFiles, profile_dirs, response_ptr),
190 base::BindOnce(&DebugDaemonLogSource::MergeUserLogFilesResponse,
191 weak_ptr_factory_.GetWeakPtr(), std::move(response)));
192 }
193
MergeUserLogFilesResponse(std::unique_ptr<SystemLogsResponse> response)194 void DebugDaemonLogSource::MergeUserLogFilesResponse(
195 std::unique_ptr<SystemLogsResponse> response) {
196 for (auto& pair : *response)
197 response_->emplace(pair.first, std::move(pair.second));
198
199 auto response_to_return = std::make_unique<SystemLogsResponse>();
200 std::swap(response_to_return, response_);
201 DCHECK(!callback_.is_null());
202 std::move(callback_).Run(std::move(response_to_return));
203 }
204
RequestCompleted()205 void DebugDaemonLogSource::RequestCompleted() {
206 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
207 DCHECK(!callback_.is_null());
208
209 --num_pending_requests_;
210 if (num_pending_requests_ > 0)
211 return;
212
213 // When all other logs are collected, fetch the user logs, because any errors
214 // fetching the other logs is reported in the user logs.
215 GetLoggedInUsersLogFiles();
216 }
217
218 } // namespace system_logs
219