chromium/chrome/browser/metrics/perf/perf_events_collector.h

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

#ifndef CHROME_BROWSER_METRICS_PERF_PERF_EVENTS_COLLECTOR_H_
#define CHROME_BROWSER_METRICS_PERF_PERF_EVENTS_COLLECTOR_H_

#include <map>
#include <memory>
#include <string>
#include <string_view>
#include <vector>

#include "base/feature_list.h"
#include "base/time/time.h"
#include "chrome/browser/metrics/perf/metric_collector.h"
#include "chrome/browser/metrics/perf/perf_output.h"
#include "chrome/browser/metrics/perf/random_selector.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
#include "third_party/metrics_proto/system_profile.pb.h"

namespace ash {
class DebugDaemonClientProvider;
}

namespace base {
class SequencedTaskRunner;
}

namespace metrics {

struct CPUIdentity;
class WindowedIncognitoObserver;

BASE_DECLARE_FEATURE(kCWPCollectsETM);

// Enables collection of perf events profile data. perf aka "perf events" is a
// performance profiling infrastructure built into the linux kernel. For more
// information, see: https://perf.wiki.kernel.org/index.php/Main_Page.
class PerfCollector : public internal::MetricCollector {
 public:
  PerfCollector();

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

  // MetricCollector:
  ~PerfCollector() override;
  const char* ToolName() const override;

 protected:
  // For testing to mock PerfOutputCall.
  virtual std::unique_ptr<PerfOutputCall> CreatePerfOutputCall(
      const std::vector<std::string>& quipper_args,
      bool disable_cpu_idle,
      PerfOutputCall::DoneCallback callback);

  void OnPerfOutputComplete(
      std::unique_ptr<WindowedIncognitoObserver> incognito_observer,
      std::unique_ptr<SampledProfile> sampled_profile,
      bool has_cycles,
      std::string perf_stdout);

  // Parses a PerfDataProto from serialized data |perf_stdout|, if non-empty.
  // If |perf_stdout| is empty, it is counted as an error. |incognito_observer|
  // indicates whether an incognito window had been opened during the profile
  // collection period. If there was an incognito window, discard the incoming
  // data.
  void ParseOutputProtoIfValid(
      std::unique_ptr<WindowedIncognitoObserver> incognito_observer,
      std::unique_ptr<SampledProfile> sampled_profile,
      bool has_cycles,
      std::string perf_stdout);

  // MetricCollector:
  void SetUp() override;
  base::WeakPtr<internal::MetricCollector> GetWeakPtr() override;
  bool ShouldCollect() const override;
  void CollectProfile(std::unique_ptr<SampledProfile> sampled_profile) override;
  void StopCollection() override;

  const RandomSelector& command_selector() const { return command_selector_; }

  // Collects both Ash and Lacros Chrome process and thread types.
  static void CollectProcessTypes(SampledProfile* sampled_profile);

  // Executes asynchronously on another thread pool and retries with a delay if
  // all frequencies couldn't be read. Posts a task on the given task_runner
  // after each attempt.
  static void ParseCPUFrequencies(
      scoped_refptr<base::SequencedTaskRunner> task_runner,
      base::WeakPtr<PerfCollector> perf_collector,
      int attempt,
      int max_retries);
  // Saves the given frequencies to |max_frequencies_mhz_|.
  void SaveCPUFrequencies(const std::vector<uint32_t>& frequencies);

  const std::vector<uint32_t>& max_frequencies_mhz() const {
    return max_frequencies_mhz_;
  }

  // Enumeration representing success and various failure modes for parsing CPU
  // frequencies. These values are persisted to logs. Entries should not be
  // renumbered and numeric values should never be reused.
  enum class ParseFrequencyStatus {
    kSuccess,
    kNumCPUsIsZero,
    kSomeZeroCPUFrequencies,
    kAllZeroCPUFrequencies,
    kSuccessOnRetry,
    kNumCPUsMoreThanPossible,
    // Magic constant used by the histogram macros.
    kMaxValue = kNumCPUsMoreThanPossible,
  };

  // Extracts the |lacros_channel| and |lacros_version| from |lacros_path|.
  static bool LacrosChannelAndVersion(
      std::string_view lacros_path,
      metrics::SystemProfileProto_Channel& lacros_channel,
      std::string& lacros_version);

  // Enumeration of various locations gotten from parsing a Lacros binary path.
  // This is used to monitor any change to the Lacros path.
  enum class ParseLacrosPath {
    kRootfs,
    kStateful,
    kUnrecognized,
    // Magic constant used by the histogram macros.
    kMaxValue = kUnrecognized,
  };

  // Annotations on the collected sampled_profile, including adding process
  // types and PSI CPU data.
  static void PostCollectionProfileAnnotation(SampledProfile* sampled_profile,
                                              bool has_cycles);

  // Collect PSI CPU data and add to sampled_profile.
  static void CollectPSICPU(SampledProfile* sampled_profile,
                            const std::string& psi_cpu_path);

  // Enumeration representing success and various failure modes for parsing PSI
  // CPU data. These values are persisted to logs. Entries should not be
  // renumbered and numeric values should never be reused.
  enum class ParsePSICPUStatus {
    kSuccess,
    kReadFileFailed,
    kUnexpectedDataFormat,
    kParsePSIValueFailed,
    // Magic constant used by the histogram macros.
    kMaxValue = kParsePSIValueFailed,
  };

  SampledProfile::TriggerEvent current_trigger_ =
      SampledProfile::UNKNOWN_TRIGGER_EVENT;

  // Enumeration representing event types that need additional treatment
  // during or after the collection.
  enum class EventType {
    kOther,
    kCycles,
    kETM,
  };
  static EventType CommandEventType(const std::vector<std::string>& args);

 private:
  // Change the values in |collection_params_| and the commands in
  // |command_selector| for any keys that are present in |params|.
  void SetCollectionParamsFromVariationParams(
      const std::map<std::string, std::string>& params);

  // Set of commands to choose from.
  RandomSelector command_selector_;

  // |debugd_client_provider_| hosts the private DBus connection to debugd.
  std::unique_ptr<ash::DebugDaemonClientProvider> debugd_client_provider_;

  // An active call to perf/quipper, if set.
  std::unique_ptr<PerfOutputCall> perf_output_call_;

  // Vector of max frequencies associated with each logical CPU. Computed
  // asynchronously at start.
  std::vector<uint32_t> max_frequencies_mhz_;

  base::WeakPtrFactory<PerfCollector> weak_factory_{this};
};

// Exposed for unit testing.
namespace internal {

// Return the default set of perf commands and their odds of selection given
// the identity of the CPU in |cpuid|.
std::vector<RandomSelector::WeightAndValue> GetDefaultCommandsForCpuModel(
    const CPUIdentity& cpuid,
    const std::string& model);

// For the "PerfCommand::"-prefixed keys in |params|, return the cpu specifier
// that is the narrowest match for the CPU identified by |cpuid|.
// Valid CPU specifiers, in increasing order of specificity, are:
// "default", a system architecture (e.g. "x86_64"), a CPU microarchitecture
// (currently only some Intel and AMD uarchs supported), or a CPU model name
// substring.
std::string FindBestCpuSpecifierFromParams(
    const std::map<std::string, std::string>& params,
    const CPUIdentity& cpuid);

// Returns if the given perf command samples CPU cycles.
bool CommandSamplesCPUCycles(const std::vector<std::string>& args);

}  // namespace internal

}  // namespace metrics

#endif  // CHROME_BROWSER_METRICS_PERF_PERF_EVENTS_COLLECTOR_H_