// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_HUD_DISPLAY_ASH_TRACING_REQUEST_H_
#define ASH_HUD_DISPLAY_ASH_TRACING_REQUEST_H_
#include <sys/stat.h>
#include <memory>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/platform_file.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "ash/ash_export.h"
namespace ash {
namespace hud_display {
class AshTracingManager;
class AshTracingHandler;
// This is needed for testing to override File IO.
class ASH_EXPORT AshTraceDestinationIO {
public:
virtual ~AshTraceDestinationIO();
// Overrides base::CreateDirectory.
virtual bool CreateDirectory(const base::FilePath& path) = 0;
// Overrides base::File::File(). Returns pair {File file, bool success}.
// Test implementation may return success with invalid file.
virtual std::tuple<base::File, bool> CreateTracingFile(
const base::FilePath& path) = 0;
// Implements memfd_create(2). Returns pair {int fd, bool success}.
// Test implementation may return success with invalid fd.
virtual std::tuple<base::PlatformFile, bool> CreateMemFD(
const char* name,
unsigned int flags) = 0;
// Takes GetPlatformFile() from AshTraceDestination and returns true if
// given fd is valid for storing traces. Checks for -1 in regular case,
// and checks internal status in tests.
virtual bool CanWriteFile(base::PlatformFile fd) = 0;
virtual int fstat(base::PlatformFile fd, struct stat* statbuf) = 0;
virtual ssize_t sendfile(base::PlatformFile out_fd,
base::PlatformFile in_fd,
off_t* offset,
size_t size) = 0;
};
class ASH_EXPORT AshTraceDestination {
public:
AshTraceDestination();
AshTraceDestination(std::unique_ptr<AshTraceDestinationIO> io,
const base::FilePath& path,
base::File&& file,
base::PlatformFile memfd);
AshTraceDestination(const AshTraceDestination&) = delete;
AshTraceDestination& operator=(const AshTraceDestination&) = delete;
~AshTraceDestination();
const base::FilePath& path() const { return path_; }
// Returns PlatformFile for storing trace.
// Can be memfd or file depending on the setup.
base::PlatformFile GetPlatformFile() const;
// Reurns true if GetPlatformFile() will return valid file descriptor.
// In tests when test IO layer is used returns true if test IO layer will
// succeed saving file.
bool CanWriteFile() const;
// Close all files.
void Done();
AshTraceDestinationIO* io() const { return io_.get(); }
private:
std::unique_ptr<AshTraceDestinationIO> io_;
base::FilePath path_;
base::File file_;
base::PlatformFile memfd_ = base::kInvalidPlatformFile;
};
class AshTracingRequest {
public:
enum class Status {
kEmpty, // Object created.
kInitialized, // File data is initialized
kStarted, // Tracing is in progress.
kStopping, // Tracing is being stopped.
kPendingMount, // Tracing is complete, waiting for home directory mount.
kWritingFile, // Writing trace file from memory to file after user login.
kCompleted, // Trace file is written. Object has valid path.
};
// Will start tracing (asynchronously).
explicit AshTracingRequest(AshTracingManager* tracing_manager);
AshTracingRequest(const AshTracingRequest&) = delete;
AshTracingRequest& operator=(const AshTracingRequest&) = delete;
~AshTracingRequest();
void Stop();
// Receive notifications from AshTracingHandler.
void OnTracingStarted();
void OnTracingFinished();
// Will trigger trace file write if needed.
void OnUserLoggedIn();
// Returns file descriptor that will actually be used for tracing.
base::PlatformFile GetPlatformFile() const;
Status status() const { return status_; }
const std::string& error_message() const { return error_message_; }
// Tests generate specific fake IO.
static ASH_EXPORT void SetAshTraceDestinationIOCreatorForTesting(
std::unique_ptr<AshTraceDestinationIO> (*creator)(void));
static ASH_EXPORT void ResetAshTraceDestinationIOCreatorForTesting();
// Tests explicitly check AshTraceDestination behavior and they need to
// be able to generate ThreadPool tasks to crete AshTraceDestination.
// So this function will return a task that can be sent to IO-enabled
// sequence runner to create AshTraceDestination.
using AshTraceDestinationUniquePtr = std::unique_ptr<AshTraceDestination>;
using GenerateTraceDestinationTask =
base::OnceCallback<AshTraceDestinationUniquePtr(void)>;
ASH_EXPORT static GenerateTraceDestinationTask
CreateGenerateTraceDestinationTaskForTesting(
std::unique_ptr<AshTraceDestinationIO> io,
base::Time timestamp);
ASH_EXPORT const AshTraceDestination* GetTraceDestinationForTesting() const;
private:
// Starts tracing after `destination` was initialized on the ThreadPool.
void OnTraceDestinationInitialized(
std::unique_ptr<AshTraceDestination> destination);
// Marks file export operation completed.
void OnPendingFileStored(std::unique_ptr<AshTraceDestination> destination,
bool success,
std::string error_message);
// Stores memory trace file to permanent location.
void StorePendingFile();
// Trace status
Status status_ = Status::kEmpty;
// When trace was started.
const base::Time timestamp_;
bool user_logged_in_ = false;
raw_ptr<AshTracingManager> tracing_manager_;
// This object is deleted once tracing is stopped.
std::unique_ptr<AshTracingHandler> tracing_handler_;
// Non-empty if error has occurred.
std::string error_message_;
std::unique_ptr<AshTraceDestination> trace_destination_;
SEQUENCE_CHECKER(my_sequence_checker_);
base::WeakPtrFactory<AshTracingRequest> weak_factory_{this};
};
} // namespace hud_display
} // namespace ash
#endif // ASH_HUD_DISPLAY_ASH_TRACING_REQUEST_H_