blob: dabc4cbf9f7b8e7980997341cf66aa3b09a98173 [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstdint>
#include <list>
#include <memory>
#include <string>
#include <base/command_line.h>
#include <base/process/process.h>
#include <base/time/default_tick_clock.h>
#include "diagnostics/cros_healthd/routines/diag_process_adapter.h"
#include "diagnostics/cros_healthd/routines/diag_routine.h"
namespace diagnostics {
// Output messages for the routine when in various states.
extern const char kSubprocRoutineCancelledMessage[];
extern const char kSubprocRoutineErrorMessage[];
extern const char kSubprocRoutineFailedMessage[];
extern const char kSubprocRoutineFailedToLaunchProcessMessage[];
extern const char kSubprocRoutineFailedToStopMessage[];
extern const char kSubprocRoutineProcessCancellingMessage[];
extern const char kSubprocRoutineProcessRunningMessage[];
extern const char kSubprocRoutineReadyMessage[];
extern const char kSubprocRoutineSucceededMessage[];
// We don't always know when a SubprocRoutine should finish. Sometimes we have
// to fake our prediction of percent complete.
extern const uint32_t kSubprocRoutineFakeProgressPercentUnknown;
// The SubprocRoutine takes a command line to run. It is non-interactive, and
// this does not fully support Pause and Resume. Pause will simply kill the
// process. The exit code of the process is used to determine success or failure
// of the test. So, the "check" portion of the Routine must live inside the
// sub-process.
class SubprocRoutine final : public DiagnosticRoutine {
// The state of the SubprocRoutine is modeled in the SubprocStatus enum.
enum SubprocStatus {
// Constructor to run a single executable.
SubprocRoutine(const base::CommandLine& command_line,
uint32_t predicted_duration_in_seconds);
// Constructor to run multiple executables.
SubprocRoutine(const std::list<base::CommandLine>& command_lines,
uint32_t total_predicted_duration_in_seconds);
// Constructor only for facilitating the unit test.
SubprocRoutine(std::unique_ptr<DiagProcessAdapter> process_adapter,
std::unique_ptr<base::TickClock> tick_clock,
const std::list<base::CommandLine>& command_lines,
uint32_t predicted_duration_in_seconds);
SubprocRoutine(const SubprocRoutine&) = delete;
SubprocRoutine& operator=(const SubprocRoutine&) = delete;
~SubprocRoutine() override;
// DiagnosticRoutine overrides:
void Start() override;
void Resume() override;
void Cancel() override;
void PopulateStatusUpdate(
chromeos::cros_healthd::mojom::RoutineUpdate* response,
bool include_output) override;
chromeos::cros_healthd::mojom::DiagnosticRoutineStatusEnum GetStatus()
// Registers a callback that will execute before processes start. The routine
// will stop and set status to failure if this callback returns false.
// This function should be called only once.
void RegisterPreStartCallback(base::OnceCallback<bool()> callback);
// Registers a callback that will execute after process is finished.
// This function should be called only once.
void RegisterPostStopCallback(base::OnceClosure callback);
// Functions to manipulate the child process.
void StartProcess();
void KillProcess(bool from_dtor);
// Handle state transitions due to process state within this object.
void CheckProcessStatus();
void CheckActiveProcessStatus();
uint32_t CalculateProgressPercent();
// |subproc_status_| is the state of the subproc as understood by the
// SubprocRoutine object's state machine. Essentially, this variable stores
// which state we are in.
SubprocStatus subproc_status_;
// |pre_start_callback_| can be registered via RegisterPreStartCallback()
base::OnceCallback<bool()> pre_start_callback_;
// |post_stop_callback_| can be registered via RegisterPostStopCallback()
base::OnceClosure post_stop_callback_;
// |process_adapter_| is a dependency that is injected at object creation time
// which enables swapping out process control functionality for the main
// purpose of facilitating Unit tests.
std::unique_ptr<DiagProcessAdapter> process_adapter_;
// |tick_clock_| is a dependency that is injected at object creation time
// which enables swapping out time-tracking functionality for the main
// purpose of facilitating Unit tests.
std::unique_ptr<base::TickClock> tick_clock_;
// |command_lines_| is a list of processes which run to test the diagnostic
// in question.
std::list<base::CommandLine> command_lines_;
// |predicted_duration_in_seconds_| is used to calculate progress percentage
// when it is non-zero.
uint32_t predicted_duration_in_seconds_ = 0;
// |last_reported_progress_percent_| is used to save the last reported
// progress percentage for handling progress reported across status changes.
uint32_t last_reported_progress_percent_ = 0;
// |handle_| keeps track of the running process.
base::ProcessHandle handle_ = base::kNullProcessHandle;
// |start_ticks_| records the time when the routine began. This is used with
// |predicted_duration_in_seconds_| to report on progress percentate.
base::TimeTicks start_ticks_;
} // namespace diagnostics