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

#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_CONNECTION_MANAGER_H_
#define COMPONENTS_SERVICES_HEAP_PROFILING_CONNECTION_MANAGER_H_

#include <string>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process_handle.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/services/heap_profiling/allocation_event.h"
#include "components/services/heap_profiling/allocation_tracker.h"
#include "components/services/heap_profiling/backtrace_storage.h"
#include "components/services/heap_profiling/public/mojom/heap_profiling_service.mojom.h"
#include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"

namespace base {

class SequencedTaskRunner;

}  // namespace base

namespace heap_profiling {

using VmRegions =
    base::flat_map<base::ProcessId,
                   std::vector<memory_instrumentation::mojom::VmRegionPtr>>;

// Manages all connections and logging for each process. Pipes are supplied by
// the pipe server and this class will connect them to a parser and logger.
//
// Note |backtrace_storage| must outlive ConnectionManager.
//
// This object is constructed on the UI thread, but the rest of the usage
// (including deletion) is on the IO thread.
class ConnectionManager {
  using DumpProcessesForTracingCallback = memory_instrumentation::mojom::
      HeapProfiler::DumpProcessesForTracingCallback;

 public:
  ConnectionManager();
  ~ConnectionManager();

  // Shared types for the dump-type-specific args structures.
  struct DumpArgs {
    DumpArgs();
    DumpArgs(DumpArgs&&) noexcept;
    ~DumpArgs();

   private:
    friend ConnectionManager;

    // This lock keeps the backtrace atoms alive throughout the dumping
    // process. It will be initialized by DumpProcess.
    BacktraceStorage::Lock backtrace_storage_lock;

    DISALLOW_COPY_AND_ASSIGN(DumpArgs);
  };

  // Dumping is asynchronous so will not be complete when this function
  // returns. The dump is complete when the callback provided in the args is
  // fired.
  void DumpProcessesForTracing(bool keep_small_allocations,
                               bool strip_path_from_mapped_files,
                               DumpProcessesForTracingCallback callback,
                               VmRegions vm_regions);

  void OnNewConnection(base::ProcessId pid,
                       mojom::ProfilingClientPtr client,
                       mojo::ScopedHandle receiver_pipe_end,
                       mojom::ProcessType process_type,
                       mojom::ProfilingParamsPtr params);

  std::vector<base::ProcessId> GetConnectionPids();
  std::vector<base::ProcessId> GetConnectionPidsThatNeedVmRegions();

 private:
  struct Connection;
  struct DumpProcessesForTracingTracking;

  void DoDumpOneProcessForTracing(
      scoped_refptr<DumpProcessesForTracingTracking> tracking,
      base::ProcessId pid,
      mojom::ProcessType process_type,
      bool keep_small_allocations,
      bool strip_path_from_mapped_files,
      uint32_t sampling_rate,
      bool success,
      AllocationCountMap counts,
      AllocationTracker::ContextMap context,
      AllocationTracker::AddressToStringMap mapped_strings);

  // Notification that a connection is complete. Unlike OnNewConnection which
  // is signaled by the pipe server, this is signaled by the allocation tracker
  // to ensure that the pipeline for this process has been flushed of all
  // messages.
  void OnConnectionComplete(base::ProcessId pid);

  // Reports the ProcessTypes of the processes being profiled.
  void ReportMetrics();

  // These thunks post the request back to the given thread.
  static void OnConnectionCompleteThunk(
      scoped_refptr<base::SequencedTaskRunner> main_loop,
      base::WeakPtr<ConnectionManager> connection_manager,
      base::ProcessId process_id);

  BacktraceStorage backtrace_storage_;

  // Next ID to use for a barrier request. This is incremented for each use
  // to ensure barrier IDs are unique.
  uint32_t next_barrier_id_ = 1;

  // The next ID to use when exporting a heap dump.
  size_t next_id_ = 1;

  // Maps process ID to the connection information for it.
  base::flat_map<base::ProcessId, std::unique_ptr<Connection>> connections_;
  base::Lock connections_lock_;

  // Every 24-hours, reports the types of profiled processes.
  base::RepeatingTimer metrics_timer_;

  // To avoid deadlock, synchronous calls to the browser are made on a dedicated
  // thread that does nothing else. Both the IO thread and connection-specific
  // threads could potentially be processing messages from the browser process,
  // which in turn could be blocked on sending more messages over the pipe.
  base::Thread blocking_thread_;

  // Must be last.
  base::WeakPtrFactory<ConnectionManager> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
};

}  // namespace heap_profiling

#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_CONNECTION_MANAGER_H_
