1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274

content / browser / tracing / tracing_scenario.h [blame]

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_TRACING_TRACING_SCENARIO_H_
#define CONTENT_BROWSER_TRACING_TRACING_SCENARIO_H_

#include "base/cancelable_callback.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/token.h"
#include "base/trace_event/trace_config.h"
#include "content/browser/tracing/background_tracing_rule.h"
#include "content/common/content_export.h"
#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
#include "third_party/perfetto/include/perfetto/tracing/tracing.h"
#include "third_party/perfetto/protos/perfetto/config/chrome/scenario_config.gen.h"

namespace content {

class CONTENT_EXPORT TracingScenarioBase {
 public:
  virtual ~TracingScenarioBase();

  // Disables a scenario.
  virtual void Disable();
  // Enables a disabled scenario.
  virtual void Enable();

  const std::string& scenario_name() const { return scenario_name_; }

 protected:
  explicit TracingScenarioBase(std::string scenario_name);

  virtual bool OnStartTrigger(const BackgroundTracingRule* rule) = 0;
  virtual bool OnStopTrigger(const BackgroundTracingRule* rule) = 0;
  virtual bool OnUploadTrigger(const BackgroundTracingRule* rule) = 0;

  uint32_t TriggerNameHash(const BackgroundTracingRule* triggered_rule) const;

  std::vector<std::unique_ptr<BackgroundTracingRule>> start_rules_;
  std::vector<std::unique_ptr<BackgroundTracingRule>> stop_rules_;
  std::vector<std::unique_ptr<BackgroundTracingRule>> upload_rules_;

  std::string scenario_name_;

  scoped_refptr<base::SequencedTaskRunner> task_runner_;
  SEQUENCE_CHECKER(sequence_checker_);
};

// NestedTracingScenario manages triggers for a single nested tracing
// scenario. Unlike TracingScenario below, it doesn't manage a tracing
// session, but inherits from the parent's session instead.
class CONTENT_EXPORT NestedTracingScenario : public TracingScenarioBase {
 public:
  enum class State {
    // The scenario is disabled and no rule is installed.
    kDisabled,
    // The scenario is enabled, start rules are installed and
    // OnNestedScenarioStart() is called.
    kEnabled,
    // The tracing session is active and stop/upload rules are installed.
    kActive,
    // A stop rule was triggered and only upload rules are installed.
    // After stopping, the nested scenario becomes kDisabled and
    // OnNestedScenarioStop() is called.
    kStopping,
  };

  // The delegate gets notified of state transitions and receives traces.
  class Delegate {
   public:
    // Called when a start rule is triggered and |scenario| becomes kActive.
    virtual void OnNestedScenarioStart(
        NestedTracingScenario* active_scenario) = 0;
    // Called when a stop rule is triggered and |scenario| becomes kStopping.
    // Disable() is expected to be called shortly after.
    virtual void OnNestedScenarioStop(NestedTracingScenario* idle_scenario) = 0;
    // Called when an upload rule is triggered and |scenario| becomes kDisabled.
    virtual void OnNestedScenarioUpload(
        NestedTracingScenario* scenario,
        const BackgroundTracingRule* triggered_rule) = 0;

   protected:
    ~Delegate() = default;
  };

  static std::unique_ptr<NestedTracingScenario> Create(
      const perfetto::protos::gen::NestedScenarioConfig& config,
      Delegate* scenario_delegate);

  ~NestedTracingScenario() override;

  // Disables a scenario.
  void Disable() override;
  // Enables a disabled scenario. Cannot be called after the scenario is
  // enabled.
  void Enable() override;
  // Request to stop an active scenario. Upload rules are still active until
  // Disable() is called.
  void Stop();

  State current_state() const { return current_state_; }

 protected:
  NestedTracingScenario(
      const perfetto::protos::gen::NestedScenarioConfig& config,
      Delegate* scenario_delegate);

  bool Initialize(const perfetto::protos::gen::NestedScenarioConfig& config);

 private:
  bool OnStartTrigger(const BackgroundTracingRule* rule) override;
  bool OnStopTrigger(const BackgroundTracingRule* rule) override;
  bool OnUploadTrigger(const BackgroundTracingRule* rule) override;

  void SetState(State new_state);

  State current_state_ = State::kDisabled;
  raw_ptr<Delegate> scenario_delegate_;
};

// TracingScenario manages triggers and tracing session for a single field
// tracing scenario. TracingScenario allows for multiple scenarios to be enabled
// and watch for rules at once, and is meant to replace
// BackgroundTracingActiveScenario.
// TODO(crbug.com/40257548): Update the comment above once
// BackgroundTracingActiveScenario is deleted.
class CONTENT_EXPORT TracingScenario : public TracingScenarioBase,
                                       public NestedTracingScenario::Delegate {
 public:
  enum class State {
    // The scenario is disabled and no rule is installed.
    kDisabled,
    // The scenario is enabled and setup/start rules are installed.
    kEnabled,
    // The tracing session was setup and the scenario is ready to start.
    kSetup,
    // The tracing session is recording.
    kRecording,
    // A stop rule was triggered and the tracing session is stopping.
    kStopping,
    // An upload rule was triggered and the tracing session is finalizing.
    kFinalizing
  };

  // The delegate gets notified of state transitions and receives traces.
  class Delegate {
   public:
    // Called when |scenario| becomes active, i.e. kSetup or kRecoding. Returns
    // true if tracing is allowed to begin.
    virtual bool OnScenarioActive(TracingScenario* scenario) = 0;
    // Called when |scenario| becomes idle again. Returns true if tracing is
    // allowed to finalize.
    virtual bool OnScenarioIdle(TracingScenario* scenario) = 0;
    // Called when |scenario| starts recording a trace.
    virtual void OnScenarioRecording(TracingScenario* scenario) = 0;
    // Called when a trace was collected.
    virtual void SaveTrace(TracingScenario* scenario,
                           base::Token trace_uuid,
                           const BackgroundTracingRule* triggered_rule,
                           std::string&& serialized_trace) = 0;

   protected:
    ~Delegate() = default;
  };

  static std::unique_ptr<TracingScenario> Create(
      const perfetto::protos::gen::ScenarioConfig& config,
      bool enable_privacy_filter,
      bool enable_package_name_filter,
      bool request_startup_tracing,
      Delegate* scenario_delegate);

  ~TracingScenario() override;

  // Disables an enabled but non-active scenario. Cannot be called after the
  // scenario activates.
  void Disable() override;
  // Enables a disabled scenario. Cannot be called after the scenario is
  // enabled.
  void Enable() override;
  // Aborts an active scenario.
  void Abort();

  void GenerateMetadataProto(
      perfetto::protos::pbzero::ChromeMetadataPacket* metadata);

  State current_state() const { return current_state_; }
  bool privacy_filter_enabled() const { return privacy_filtering_enabled_; }
  std::string config_hash() const { return config_hash_; }

  base::Token GetSessionID() const { return session_id_; }

 protected:
  TracingScenario(const perfetto::protos::gen::ScenarioConfig& config,
                  Delegate* scenario_delegate,
                  bool enable_privacy_filter,
                  bool request_startup_tracing);

  bool Initialize(const perfetto::protos::gen::ScenarioConfig& config,
                  bool enable_package_name_filter);

  virtual std::unique_ptr<perfetto::TracingSession> CreateTracingSession();

 private:
  // Helper deleter to automatically clear on-error callback from
  // perfetto::TracingSession. Without clearing the callback, it is
  // invoked whenever a session is deleted.
  struct TracingSessionDeleter {
    TracingSessionDeleter() = default;
    // NOLINTNEXTLINE(google-explicit-constructor)
    TracingSessionDeleter(std::default_delete<perfetto::TracingSession>) {}
    void operator()(perfetto::TracingSession* ptr) const;
  };
  using TracingSession =
      std::unique_ptr<perfetto::TracingSession, TracingSessionDeleter>;
  class TraceReader;

  void SetupTracingSession();
  void OnTracingError(perfetto::TracingError error);
  void OnTracingStop();
  void OnTracingStart();
  void OnFinalizingDone(base::Token trace_uuid,
                        std::string&& serialized_trace,
                        TracingSession tracing_session,
                        const BackgroundTracingRule* triggered_rule);
  void DisableNestedScenarios();

  // NestedTracingScenario::Delegate:
  // When called, the base scenario stop rules are uninstalled and other
  // nested scenarios are disabled.
  void OnNestedScenarioStart(NestedTracingScenario* scenario) override;
  // When called, the base scenario remains active and becomes the leaf; stop
  // rules are installed again and all nested scenarios are enabled.
  void OnNestedScenarioStop(NestedTracingScenario* scenario) override;
  // When called, all rules are uinstalled and the tracing session is
  // stopped and finalized.
  void OnNestedScenarioUpload(
      NestedTracingScenario* scenario,
      const BackgroundTracingRule* triggered_rule) override;

  bool OnSetupTrigger(const BackgroundTracingRule* rule);
  bool OnStartTrigger(const BackgroundTracingRule* rule) override;
  bool OnStopTrigger(const BackgroundTracingRule* rule) override;
  bool OnUploadTrigger(const BackgroundTracingRule* rule) override;

  base::WeakPtr<TracingScenario> GetWeakPtr();
  void SetState(State new_state);

  const std::string config_hash_;
  const bool privacy_filtering_enabled_;
  const bool request_startup_tracing_;
  State current_state_ = State::kDisabled;
  std::vector<std::unique_ptr<BackgroundTracingRule>> setup_rules_;

  std::vector<std::unique_ptr<NestedTracingScenario>> nested_scenarios_;
  raw_ptr<NestedTracingScenario> active_scenario_{nullptr};
  base::CancelableOnceClosure on_nested_stopped_;

  perfetto::TraceConfig trace_config_;
  raw_ptr<Delegate> scenario_delegate_;
  TracingSession tracing_session_;
  base::Token session_id_;
  raw_ptr<const BackgroundTracingRule> triggered_rule_;

  // NOTE: Weak pointers must be invalidated before all other member variables.
  base::WeakPtrFactory<TracingScenario> weak_ptr_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_TRACING_TRACING_SCENARIO_H_