system_logs_fetcher.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright 2013 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. #include "components/feedback/system_logs/system_logs_fetcher.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/callback_helpers.h"
  9. #include "base/logging.h"
  10. #include "base/task/task_traits.h"
  11. #include "base/task/thread_pool.h"
  12. #include "base/threading/sequenced_task_runner_handle.h"
  13. #if BUILDFLAG(IS_CHROMEOS_ASH)
  14. #include "components/feedback/feedback_report.h"
  15. #endif
  16. namespace system_logs {
  17. namespace {
  18. // List of keys in the SystemLogsResponse map whose corresponding values will
  19. // not be redacted.
  20. constexpr const char* const kExemptKeysOfUUIDs[] = {
  21. "CHROMEOS_BOARD_APPID",
  22. "CHROMEOS_CANARY_APPID",
  23. "CHROMEOS_RELEASE_APPID",
  24. };
  25. #if BUILDFLAG(IS_CHROMEOS_ASH)
  26. constexpr char kLacrosLogEntryPrefix[] = "Lacros ";
  27. #endif
  28. // Returns true if the given |key| and its corresponding value are exempt from
  29. // redaction.
  30. bool IsKeyExempt(const std::string& key) {
  31. for (auto* const exempt_key : kExemptKeysOfUUIDs) {
  32. if (key == exempt_key)
  33. return true;
  34. }
  35. return false;
  36. }
  37. // Runs the Redaction tool over the entris of |response|.
  38. void Redact(feedback::RedactionTool* redactor, SystemLogsResponse* response) {
  39. for (auto& element : *response) {
  40. if (!IsKeyExempt(element.first))
  41. element.second = redactor->Redact(element.second);
  42. }
  43. }
  44. #if BUILDFLAG(IS_CHROMEOS_ASH)
  45. std::string MergeStingsByComma(const std::string& str1,
  46. const std::string str2) {
  47. if (str1.empty())
  48. return str2;
  49. if (str2.empty())
  50. return str1;
  51. return str1 + ", " + str2;
  52. }
  53. #endif
  54. } // namespace
  55. SystemLogsFetcher::SystemLogsFetcher(
  56. bool scrub_data,
  57. const char* const first_party_extension_ids[])
  58. : response_(std::make_unique<SystemLogsResponse>()),
  59. num_pending_requests_(0),
  60. task_runner_for_redactor_(
  61. scrub_data
  62. ? base::ThreadPool::CreateSequencedTaskRunner(
  63. // User visible because this is called when the user is
  64. // looking at the send feedback dialog, watching a spinner.
  65. {base::TaskPriority::USER_VISIBLE,
  66. base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})
  67. : nullptr),
  68. redactor_(scrub_data ? std::make_unique<feedback::RedactionTool>(
  69. first_party_extension_ids)
  70. : nullptr) {}
  71. SystemLogsFetcher::~SystemLogsFetcher() {
  72. // Ensure that destruction happens on same sequence where the object is being
  73. // accessed.
  74. if (redactor_) {
  75. DCHECK(task_runner_for_redactor_);
  76. task_runner_for_redactor_->DeleteSoon(FROM_HERE, std::move(redactor_));
  77. }
  78. }
  79. void SystemLogsFetcher::AddSource(std::unique_ptr<SystemLogsSource> source) {
  80. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  81. data_sources_.emplace_back(std::move(source));
  82. num_pending_requests_++;
  83. }
  84. void SystemLogsFetcher::Fetch(SysLogsFetcherCallback callback) {
  85. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  86. DCHECK(callback_.is_null());
  87. DCHECK(!callback.is_null());
  88. callback_ = std::move(callback);
  89. if (data_sources_.empty()) {
  90. RunCallbackAndDeleteSoon();
  91. return;
  92. }
  93. for (size_t i = 0; i < data_sources_.size(); ++i) {
  94. VLOG(1) << "Fetching SystemLogSource: " << data_sources_[i]->source_name();
  95. data_sources_[i]->Fetch(base::BindOnce(&SystemLogsFetcher::OnFetched,
  96. weak_ptr_factory_.GetWeakPtr(),
  97. data_sources_[i]->source_name()));
  98. }
  99. }
  100. void SystemLogsFetcher::OnFetched(
  101. const std::string& source_name,
  102. std::unique_ptr<SystemLogsResponse> response) {
  103. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  104. DCHECK(response);
  105. VLOG(1) << "Received SystemLogSource: " << source_name;
  106. if (redactor_) {
  107. // It is safe to pass the unretained redactor_ instance here because
  108. // the redactor_ is owned by |this| and |this| only deletes itself
  109. // once all responses have been collected and added (see AddResponse()).
  110. SystemLogsResponse* response_ptr = response.get();
  111. task_runner_for_redactor_->PostTaskAndReply(
  112. FROM_HERE,
  113. base::BindOnce(Redact, base::Unretained(redactor_.get()),
  114. base::Unretained(response_ptr)),
  115. base::BindOnce(&SystemLogsFetcher::AddResponse,
  116. weak_ptr_factory_.GetWeakPtr(), source_name,
  117. std::move(response)));
  118. } else {
  119. AddResponse(source_name, std::move(response));
  120. }
  121. }
  122. void SystemLogsFetcher::AddResponse(
  123. const std::string& source_name,
  124. std::unique_ptr<SystemLogsResponse> response) {
  125. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  126. for (const auto& it : *response) {
  127. // An element with a duplicate key would not be successfully inserted.
  128. bool ok = response_->emplace(it).second;
  129. DCHECK(ok) << "Duplicate key found: " << it.first;
  130. }
  131. --num_pending_requests_;
  132. if (num_pending_requests_ > 0)
  133. return;
  134. #if BUILDFLAG(IS_CHROMEOS_ASH)
  135. MergeAshAndLacrosCrashReportIdsInReponse();
  136. #endif
  137. RunCallbackAndDeleteSoon();
  138. }
  139. #if BUILDFLAG(IS_CHROMEOS_ASH)
  140. // TODO(https://crbug.com/1156750): Add test cases to exercise this code path.
  141. void SystemLogsFetcher::MergeAshAndLacrosCrashReportIdsInReponse() {
  142. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  143. // Merge the lacros and ash recent crash report ids into a single log entry
  144. // with the key defined by kCrashReportIdsKey, i.e. crash_report_ids.
  145. auto ash_crash_iter =
  146. response_->find(feedback::FeedbackReport::kCrashReportIdsKey);
  147. // If ash crash_report_ids log entry is not found, it means CrashIdsSource
  148. // is not included in log sources, for example, the code is called by
  149. // BuildShellSystemLogsFetcher. Stop further processing.
  150. if (ash_crash_iter == response_->end())
  151. return;
  152. std::string ash_crash_report_ids = ash_crash_iter->second;
  153. std::string lacros_crash_report_ids;
  154. std::string lacros_crash_report_key =
  155. std::string(kLacrosLogEntryPrefix) +
  156. feedback::FeedbackReport::kCrashReportIdsKey;
  157. auto lacros_crash_iter = response_->find(lacros_crash_report_key);
  158. if (lacros_crash_iter != response_->end())
  159. lacros_crash_report_ids = lacros_crash_iter->second;
  160. // Update the crash_report_ids with the merged value.
  161. ash_crash_iter->second =
  162. MergeStingsByComma(ash_crash_report_ids, lacros_crash_report_ids);
  163. // Remove the lacros log entry of recent crash report ids.
  164. response_->erase(lacros_crash_report_key);
  165. // Merge the lacros and ash all crash report ids into a single log entry
  166. // with key defined by kAllCrashReportIdsKey, i.e. all_crash_report_ids.
  167. auto ash_all_crash_iter =
  168. response_->find(feedback::FeedbackReport::kAllCrashReportIdsKey);
  169. DCHECK(ash_all_crash_iter != response_->end());
  170. std::string ash_all_crash_report_ids = ash_all_crash_iter->second;
  171. std::string lacros_all_crash_report_ids;
  172. std::string lacros_all_crash_report_key =
  173. std::string(kLacrosLogEntryPrefix) +
  174. feedback::FeedbackReport::kAllCrashReportIdsKey;
  175. auto lacros_all_crash_iter = response_->find(lacros_all_crash_report_key);
  176. if (lacros_all_crash_iter != response_->end())
  177. lacros_all_crash_report_ids = lacros_all_crash_iter->second;
  178. std::string all_crash_report_ids;
  179. // If there are only recent crashes from Lacros, let lacros crash ids
  180. // go first; otherwise, ash crash ids will go first.
  181. if (ash_crash_report_ids.empty() && !lacros_crash_report_ids.empty()) {
  182. all_crash_report_ids = MergeStingsByComma(lacros_all_crash_report_ids,
  183. ash_all_crash_report_ids);
  184. } else {
  185. all_crash_report_ids = MergeStingsByComma(ash_all_crash_report_ids,
  186. lacros_all_crash_report_ids);
  187. }
  188. // Update all_crash_report_ids with merged value.
  189. ash_all_crash_iter->second = all_crash_report_ids;
  190. // Remove the Lacros log entry of all crash report ids.
  191. response_->erase(lacros_all_crash_report_key);
  192. }
  193. #endif
  194. void SystemLogsFetcher::RunCallbackAndDeleteSoon() {
  195. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  196. DCHECK(!callback_.is_null());
  197. std::move(callback_).Run(std::move(response_));
  198. base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
  199. }
  200. } // namespace system_logs