chromium/ios/chrome/browser/web/model/ios_thread_profiler.h

// 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.

// Adapted from chrome/common/profiler/thread_profiler.h

// TODO(crbug.com/40778431): remove this once //chrome/common/profiler is moved
// to components/profiler.

#ifndef IOS_CHROME_BROWSER_WEB_MODEL_IOS_THREAD_PROFILER_H_
#define IOS_CHROME_BROWSER_WEB_MODEL_IOS_THREAD_PROFILER_H_

#include <memory>

#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/profiler/process_type.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "components/metrics/public/mojom/call_stack_profile_collector.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"

// PeriodicSamplingScheduler repeatedly schedules periodic sampling of the
// thread through calls to GetTimeToNextCollection(). This class is exposed
// to allow testing.
class PeriodicSamplingScheduler {
 public:
  PeriodicSamplingScheduler(base::TimeDelta sampling_duration,
                            double fraction_of_execution_time_to_sample,
                            base::TimeTicks start_time);
  virtual ~PeriodicSamplingScheduler();

  PeriodicSamplingScheduler(const PeriodicSamplingScheduler&) = delete;
  PeriodicSamplingScheduler& operator=(const PeriodicSamplingScheduler&) =
      delete;

  // Returns the amount of time between now and the next collection.
  base::TimeDelta GetTimeToNextCollection();

 protected:
  // Virtual to provide seams for test use.
  virtual double RandDouble() const;
  virtual base::TimeTicks Now() const;

 private:
  const base::TimeDelta period_duration_;
  const base::TimeDelta sampling_duration_;
  base::TimeTicks period_start_time_;
};

// IOSThreadProfiler performs startup and periodic profiling of Chrome
// threads.
class IOSThreadProfiler {
 public:
  ~IOSThreadProfiler();
  IOSThreadProfiler(const IOSThreadProfiler&) = delete;
  IOSThreadProfiler& operator=(const IOSThreadProfiler&) = delete;

  // Creates a profiler for a main thread and immediately starts it. This
  // function should only be used when profiling the main thread of a
  // process. The returned profiler must be destroyed prior to thread exit to
  // stop the profiling.
  //
  // SetMainThreadTaskRunner() should be called after the message loop has been
  // started on the thread. It is the caller's responsibility to ensure that
  // the instance returned by this function is still alive when the static API
  // SetMainThreadTaskRunner() is used. The latter is static to support Chrome's
  // set up where the IOSThreadProfiler is created in chrome/app which cannot be
  // easily accessed from chrome_browser_main.cc which sets the task runner.
  static std::unique_ptr<IOSThreadProfiler> CreateAndStartOnMainThread();

  // Sets the task runner when profiling on the main thread. This occurs in a
  // separate call from CreateAndStartOnMainThread so that startup profiling can
  // occur prior to message loop start. The task runner is associated with the
  // instance returned by CreateAndStartOnMainThread(), which must be alive when
  // this is called.
  static void SetMainThreadTaskRunner(
      scoped_refptr<base::SingleThreadTaskRunner> task_runner);

  // Get the stack sampling params to use.
  static base::StackSamplingProfiler::SamplingParams GetSamplingParams();

  // Creates a profiler for a child thread and immediately starts it. This
  // should be called from a task posted on the child thread immediately after
  // thread start. The thread will be profiled until exit.
  static void StartOnChildThread(base::ProfilerThreadType thread);

  // Sets the callback to use for reporting browser process profiles. This
  // indirection is required to avoid a dependency on unnecessary metrics code
  // in child processes.
  static void SetBrowserProcessReceiverCallback(
      const base::RepeatingCallback<void(base::TimeTicks,
                                         metrics::SampledProfile)>& callback);

  // This function must be called within child processes to supply the Service
  // Manager's connector, to bind the interface through which a profile is sent
  // back to the browser process.
  //
  // Note that the metrics::CallStackProfileCollector interface also must be
  // exposed to the child process, and metrics::mojom::CallStackProfileCollector
  // declared in chrome_content_browser_manifest_overlay.json, for the binding
  // to succeed.
  static void SetCollectorForChildProcess(
      mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector);

 private:
  class WorkIdRecorder;

  // Creates the profiler. The task runner will be supplied for child threads
  // but not for main threads.
  IOSThreadProfiler(
      base::ProfilerThreadType thread,
      scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner =
          scoped_refptr<base::SingleThreadTaskRunner>());

  // Posts a task on `owning_thread_task_runner` to start the next periodic
  // sampling collection on the completion of the previous collection.
  static void OnPeriodicCollectionCompleted(
      scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner,
      base::WeakPtr<IOSThreadProfiler> thread_profiler);

  // Sets the task runner when profiling on the main thread. This occurs in a
  // separate call from CreateAndStartOnMainThread so that startup profiling can
  // occur prior to message loop start.
  void SetMainThreadTaskRunnerImpl(
      scoped_refptr<base::SingleThreadTaskRunner> task_runner);

  // Posts a delayed task to start the next periodic sampling collection.
  void ScheduleNextPeriodicCollection();

  // Creates a new periodic profiler and initiates a collection with it.
  void StartPeriodicSamplingCollection();

  const base::ProfilerProcessType process_;
  const base::ProfilerThreadType thread_;

  scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_;

  std::unique_ptr<WorkIdRecorder> work_id_recorder_;

  std::unique_ptr<base::StackSamplingProfiler> startup_profiler_;

  std::unique_ptr<base::StackSamplingProfiler> periodic_profiler_;
  std::unique_ptr<PeriodicSamplingScheduler> periodic_sampling_scheduler_;

  THREAD_CHECKER(thread_checker_);
  base::WeakPtrFactory<IOSThreadProfiler> weak_factory_{this};
};

#endif  // IOS_CHROME_BROWSER_WEB_MODEL_IOS_THREAD_PROFILER_H_