ash_tracing_request.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. // Copyright 2021 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 "ash/hud_display/ash_tracing_request.h"
  5. #include <sys/mman.h>
  6. #include <sys/sendfile.h>
  7. #include "ash/hud_display/ash_tracing_handler.h"
  8. #include "ash/hud_display/ash_tracing_manager.h"
  9. #include "ash/shell.h"
  10. #include "ash/shell_delegate.h"
  11. #include "base/bind.h"
  12. #include "base/callback.h"
  13. #include "base/files/file.h"
  14. #include "base/files/file_path.h"
  15. #include "base/files/file_util.h"
  16. #include "base/files/platform_file.h"
  17. #include "base/logging.h"
  18. #include "base/posix/safe_strerror.h"
  19. #include "base/strings/stringprintf.h"
  20. #include "base/task/task_traits.h"
  21. #include "base/task/thread_pool.h"
  22. namespace ash {
  23. namespace hud_display {
  24. namespace {
  25. // Tests supply their own IO layer generator.
  26. static std::unique_ptr<AshTraceDestinationIO> (
  27. *test_ash_trace_destination_io_creator)(void) = nullptr;
  28. class DefaultAshTraceDestinationIO : public AshTraceDestinationIO {
  29. public:
  30. ~DefaultAshTraceDestinationIO() override = default;
  31. // Overrides base::CreateDirectory.
  32. bool CreateDirectory(const base::FilePath& path) override {
  33. base::File::Error error;
  34. if (!base::CreateDirectoryAndGetError(path, &error)) {
  35. LOG(ERROR) << "Failed to create Ash trace file directory '"
  36. << path.value() << "' : error " << error;
  37. return false;
  38. }
  39. return true;
  40. }
  41. // Overrides base::File::File(). Returns pair {File file, bool success}.
  42. std::tuple<base::File, bool> CreateTracingFile(
  43. const base::FilePath& path) override {
  44. base::File file(path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
  45. const bool success = file.IsValid();
  46. return std::make_tuple(std::move(file), success);
  47. }
  48. // Implements memfd_create(2).
  49. std::tuple<base::PlatformFile, bool> CreateMemFD(
  50. const char* name,
  51. unsigned int flags) override {
  52. base::PlatformFile memfd = memfd_create(name, flags);
  53. return std::make_tuple(memfd, memfd != base::kInvalidPlatformFile);
  54. }
  55. bool CanWriteFile(base::PlatformFile fd) override {
  56. return fd != base::kInvalidPlatformFile;
  57. }
  58. int fstat(base::PlatformFile fd, struct stat* statbuf) override {
  59. return ::fstat(fd, statbuf);
  60. }
  61. ssize_t sendfile(base::PlatformFile out_fd,
  62. base::PlatformFile in_fd,
  63. off_t* offset,
  64. size_t size) override {
  65. return ::sendfile(out_fd, in_fd, offset, size);
  66. }
  67. };
  68. std::string GenerateTraceFileName(base::Time timestamp) {
  69. base::Time::Exploded time_deets;
  70. timestamp.LocalExplode(&time_deets);
  71. return base::StringPrintf(
  72. "ash-trace_%02d%02d%02d-%02d%02d%02d.%03d.dat", time_deets.year,
  73. time_deets.month, time_deets.day_of_month, time_deets.hour,
  74. time_deets.minute, time_deets.second, time_deets.millisecond);
  75. }
  76. std::unique_ptr<AshTraceDestination> GenerateTraceDestinationFile(
  77. std::unique_ptr<AshTraceDestinationIO> io,
  78. const base::FilePath& tracng_directory_path,
  79. base::Time timestamp) {
  80. if (!io->CreateDirectory(tracng_directory_path))
  81. return nullptr;
  82. base::FilePath path =
  83. tracng_directory_path.AppendASCII(GenerateTraceFileName(timestamp));
  84. auto [file, success] = io->CreateTracingFile(path);
  85. if (!success) {
  86. LOG(ERROR) << "Failed to create Ash trace '" << path.value() << "' : error "
  87. << file.error_details();
  88. return nullptr;
  89. }
  90. return std::make_unique<AshTraceDestination>(std::move(io), std::move(path),
  91. std::move(file),
  92. base::kInvalidPlatformFile);
  93. }
  94. std::unique_ptr<AshTraceDestination> GenerateTraceDestinationMemFD(
  95. std::unique_ptr<AshTraceDestinationIO> io) {
  96. constexpr char kMemFDDebugName[] = "ash-trace-buffer.dat";
  97. auto [memfd, success] = io->CreateMemFD(kMemFDDebugName, MFD_CLOEXEC);
  98. if (!success) {
  99. LOG(ERROR) << "Failed to create memfd for '" << kMemFDDebugName
  100. << "', error:" << base::safe_strerror(errno);
  101. return nullptr;
  102. }
  103. return std::make_unique<AshTraceDestination>(std::move(io), base::FilePath(),
  104. base::File(), memfd);
  105. }
  106. // Must be called with blocking allowed (i.e. from the thread pool).
  107. // Returns null pointer in case of error.
  108. std::unique_ptr<AshTraceDestination> GenerateTraceDestination(
  109. std::unique_ptr<AshTraceDestinationIO> io,
  110. base::Time timestamp,
  111. bool is_logging_redirect_disabled,
  112. const base::FilePath& user_downloads_folder) {
  113. constexpr char kTracingDir[] = "tracing";
  114. constexpr char kGlobalTracingPath[] = "/run/chrome/";
  115. if (is_logging_redirect_disabled) {
  116. return GenerateTraceDestinationFile(
  117. std::move(io),
  118. base::FilePath(kGlobalTracingPath).AppendASCII(kTracingDir), timestamp);
  119. }
  120. if (!user_downloads_folder.empty()) {
  121. // User already logged in.
  122. return GenerateTraceDestinationFile(
  123. std::move(io),
  124. base::FilePath(user_downloads_folder.AppendASCII(kTracingDir)),
  125. timestamp);
  126. }
  127. // Need to write trace to the user Downloads folder, but it is not available
  128. // yet. Create memfd.
  129. return GenerateTraceDestinationMemFD(std::move(io));
  130. }
  131. base::FilePath GetUserDownloadsFolder() {
  132. return Shell::Get()->shell_delegate()->GetPrimaryUserDownloadsFolder();
  133. }
  134. bool IsLoggingRedirectDisabled() {
  135. return Shell::Get()->shell_delegate()->IsLoggingRedirectDisabled();
  136. }
  137. struct ExportStatus {
  138. std::unique_ptr<AshTraceDestination> destination;
  139. bool success = false;
  140. std::string error_message;
  141. };
  142. ExportStatus ExportDataOnThreadPool(base::PlatformFile memfd,
  143. const base::FilePath& user_downloads_folder,
  144. base::Time timestamp) {
  145. ExportStatus result;
  146. result.destination = GenerateTraceDestination(
  147. test_ash_trace_destination_io_creator
  148. ? test_ash_trace_destination_io_creator()
  149. : std::make_unique<DefaultAshTraceDestinationIO>(),
  150. timestamp,
  151. /*is_logging_redirect_disabled=*/false, user_downloads_folder);
  152. DCHECK(!result.destination->path().empty());
  153. struct stat statbuf;
  154. if (result.destination->io()->fstat(memfd, &statbuf)) {
  155. result.error_message = std::string("Failed to stat memfd, error: ") +
  156. base::safe_strerror(errno);
  157. LOG(ERROR) << result.error_message;
  158. return result;
  159. }
  160. off_t offset = 0;
  161. const ssize_t written = result.destination->io()->sendfile(
  162. result.destination->GetPlatformFile(), memfd, &offset, statbuf.st_size);
  163. if (written != statbuf.st_size) {
  164. const std::string system_error = base::safe_strerror(errno);
  165. result.error_message =
  166. base::StringPrintf("Stored only %zd trace bytes of %" PRId64 " to '",
  167. written, static_cast<int64_t>(statbuf.st_size)) +
  168. result.destination->path().value() + "', error: " + system_error;
  169. LOG(ERROR) << result.error_message;
  170. return result;
  171. }
  172. result.success = true;
  173. return result;
  174. }
  175. AshTracingRequest::GenerateTraceDestinationTask
  176. CreateGenerateTraceDestinationTask(std::unique_ptr<AshTraceDestinationIO> io,
  177. base::Time timestamp) {
  178. return base::BindOnce(&GenerateTraceDestination, std::move(io), timestamp,
  179. IsLoggingRedirectDisabled(), GetUserDownloadsFolder());
  180. }
  181. } // anonymous namespace
  182. AshTraceDestinationIO::~AshTraceDestinationIO() = default;
  183. AshTraceDestination::AshTraceDestination() = default;
  184. AshTraceDestination::AshTraceDestination(
  185. std::unique_ptr<AshTraceDestinationIO> io,
  186. const base::FilePath& path,
  187. base::File&& file,
  188. base::PlatformFile fd)
  189. : io_(std::move(io)), path_(path), file_(std::move(file)), memfd_(fd) {}
  190. AshTraceDestination::~AshTraceDestination() {
  191. Done();
  192. }
  193. base::PlatformFile AshTraceDestination::GetPlatformFile() const {
  194. if (memfd_ != base::kInvalidPlatformFile)
  195. return memfd_;
  196. return file_.GetPlatformFile();
  197. }
  198. bool AshTraceDestination::CanWriteFile() const {
  199. return io_->CanWriteFile(GetPlatformFile());
  200. }
  201. void AshTraceDestination::Done() {
  202. if (memfd_ != base::kInvalidPlatformFile) {
  203. base::ThreadPool::PostTask(
  204. FROM_HERE,
  205. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  206. base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
  207. base::BindOnce([](base::PlatformFile fd) { close(fd); }, memfd_));
  208. memfd_ = base::kInvalidPlatformFile;
  209. }
  210. if (file_.IsValid()) {
  211. base::ThreadPool::PostTask(
  212. FROM_HERE,
  213. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  214. base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
  215. base::BindOnce([](base::File fd) { fd.Close(); }, std::move(file_)));
  216. }
  217. }
  218. AshTracingRequest::AshTracingRequest(AshTracingManager* manager)
  219. : timestamp_(base::Time::Now()), tracing_manager_(manager) {
  220. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  221. std::unique_ptr<AshTraceDestinationIO> io =
  222. test_ash_trace_destination_io_creator
  223. ? test_ash_trace_destination_io_creator()
  224. : std::make_unique<DefaultAshTraceDestinationIO>();
  225. base::ThreadPool::PostTaskAndReplyWithResult(
  226. FROM_HERE,
  227. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  228. base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
  229. CreateGenerateTraceDestinationTask(std::move(io), timestamp_),
  230. base::BindOnce(&AshTracingRequest::OnTraceDestinationInitialized,
  231. weak_factory_.GetWeakPtr()));
  232. }
  233. AshTracingRequest::~AshTracingRequest() = default;
  234. void AshTracingRequest::Stop() {
  235. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  236. DCHECK(tracing_handler_);
  237. status_ = Status::kStopping;
  238. tracing_manager_->OnRequestStatusChanged(this);
  239. tracing_handler_->Stop();
  240. }
  241. void AshTracingRequest::OnTracingStarted() {
  242. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  243. status_ = Status::kStarted;
  244. tracing_manager_->OnRequestStatusChanged(this);
  245. }
  246. void AshTracingRequest::OnTracingFinished() {
  247. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  248. tracing_handler_.reset();
  249. if (!trace_destination_->path().empty()) {
  250. // Trace was already stored to the real file.
  251. status_ = Status::kCompleted;
  252. trace_destination_->Done();
  253. tracing_manager_->OnRequestStatusChanged(this);
  254. return;
  255. }
  256. status_ = Status::kPendingMount;
  257. tracing_manager_->OnRequestStatusChanged(this);
  258. // User logged in while tracing. Need to start saving file immediately.
  259. if (user_logged_in_)
  260. StorePendingFile();
  261. }
  262. // Will trigger trace file write if needed.
  263. // If trace is already finalized, `on_completed` will be called immediately.
  264. void AshTracingRequest::OnUserLoggedIn() {
  265. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  266. user_logged_in_ = true;
  267. StorePendingFile();
  268. }
  269. base::PlatformFile AshTracingRequest::GetPlatformFile() const {
  270. return trace_destination_->GetPlatformFile();
  271. }
  272. // static
  273. void AshTracingRequest::SetAshTraceDestinationIOCreatorForTesting(
  274. std::unique_ptr<AshTraceDestinationIO> (*creator)(void)) {
  275. CHECK(!test_ash_trace_destination_io_creator);
  276. test_ash_trace_destination_io_creator = creator;
  277. }
  278. // static
  279. void AshTracingRequest::ResetAshTraceDestinationIOCreatorForTesting() {
  280. test_ash_trace_destination_io_creator = nullptr;
  281. }
  282. // static
  283. AshTracingRequest::GenerateTraceDestinationTask
  284. AshTracingRequest::CreateGenerateTraceDestinationTaskForTesting(
  285. std::unique_ptr<AshTraceDestinationIO> io,
  286. base::Time timestamp) {
  287. return CreateGenerateTraceDestinationTask(std::move(io), timestamp);
  288. }
  289. const AshTraceDestination* AshTracingRequest::GetTraceDestinationForTesting()
  290. const {
  291. return trace_destination_.get();
  292. }
  293. void AshTracingRequest::OnTraceDestinationInitialized(
  294. std::unique_ptr<AshTraceDestination> destination) {
  295. DCHECK(!trace_destination_.get());
  296. trace_destination_ = std::move(destination);
  297. status_ = Status::kInitialized;
  298. tracing_manager_->OnRequestStatusChanged(this);
  299. tracing_handler_ = std::make_unique<AshTracingHandler>();
  300. tracing_handler_->Start(this);
  301. }
  302. void AshTracingRequest::OnPendingFileStored(
  303. std::unique_ptr<AshTraceDestination> destination,
  304. bool success,
  305. std::string error_message) {
  306. trace_destination_ = std::move(destination);
  307. // Cleanup possible errors.
  308. trace_destination_->Done();
  309. status_ = Status::kCompleted;
  310. error_message_ = error_message;
  311. tracing_manager_->OnRequestStatusChanged(this);
  312. }
  313. void AshTracingRequest::StorePendingFile() {
  314. DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
  315. if (status_ != Status::kPendingMount)
  316. return;
  317. if (!user_logged_in_)
  318. return;
  319. base::FilePath user_downloads_folder = GetUserDownloadsFolder();
  320. if (user_downloads_folder.empty()) {
  321. error_message_ = "No user Downloads folder.";
  322. status_ = Status::kCompleted;
  323. trace_destination_->Done();
  324. tracing_manager_->OnRequestStatusChanged(this);
  325. return;
  326. }
  327. status_ = Status::kWritingFile;
  328. tracing_manager_->OnRequestStatusChanged(this);
  329. DCHECK(trace_destination_->CanWriteFile());
  330. base::ThreadPool::PostTaskAndReplyWithResult(
  331. FROM_HERE,
  332. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  333. base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
  334. base::BindOnce(&ExportDataOnThreadPool, GetPlatformFile(),
  335. GetUserDownloadsFolder(), timestamp_),
  336. base::BindOnce(
  337. [](base::WeakPtr<AshTracingRequest> self,
  338. ExportStatus export_status) {
  339. if (!self)
  340. return;
  341. self->OnPendingFileStored(std::move(export_status.destination),
  342. export_status.success,
  343. export_status.error_message);
  344. },
  345. weak_factory_.GetWeakPtr()));
  346. }
  347. } // namespace hud_display
  348. } // namespace ash