chromium/chrome/browser/memory_details_mac.cc

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

#include "chrome/browser/memory_details.h"

#include <stddef.h>

#include <memory>
#include <set>
#include <string>

#include "base/apple/foundation_util.h"
#include "base/file_version_info.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/process/process_iterator.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/branded_strings.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/process_type.h"
#include "ui/base/l10n/l10n_util.h"

namespace {

// A helper for |CollectProcessData()|, collecting data on the Chrome/Chromium
// process with PID |pid|. The collected data is added to |processes|.
void CollectProcessDataForChromeProcess(
    const std::vector<ProcessMemoryInformation>& child_info,
    base::ProcessId pid,
    ProcessMemoryInformationList* processes) {
  ProcessMemoryInformation info;
  info.pid = pid;
  if (info.pid == base::GetCurrentProcId())
    info.process_type = content::PROCESS_TYPE_BROWSER;
  else
    info.process_type = content::PROCESS_TYPE_UNKNOWN;

  info.product_name = base::ASCIIToUTF16(version_info::GetProductName());
  info.version = base::ASCIIToUTF16(version_info::GetVersionNumber());

  // A PortProvider is not necessary to acquire information about the number
  // of open file descriptors.
  std::unique_ptr<base::ProcessMetrics> metrics(
      base::ProcessMetrics::CreateProcessMetrics(pid, nullptr));
  info.num_open_fds = metrics->GetOpenFdCount();
  info.open_fds_soft_limit = metrics->GetOpenFdSoftLimit();

  // Check if this is one of the child processes whose data was already
  // collected and exists in |child_data|.
  for (const ProcessMemoryInformation& child : child_info) {
    if (child.pid == info.pid) {
      info.titles = child.titles;
      info.process_type = child.process_type;
      break;
    }
  }

  processes->push_back(info);
}

}  // namespace

MemoryDetails::MemoryDetails() {
  const base::FilePath browser_process_path =
      base::GetProcessExecutablePath(base::GetCurrentProcessHandle());

  ProcessData process;
  process.name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
  process.process_name =
      base::UTF8ToUTF16(browser_process_path.BaseName().value());
  process_data_.push_back(process);
}

ProcessData* MemoryDetails::ChromeBrowser() {
  return &process_data_[0];
}

void MemoryDetails::CollectProcessData(
    const std::vector<ProcessMemoryInformation>& child_info) {
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);

  // Clear old data.
  process_data_[0].processes.clear();

  // First, we use |NamedProcessIterator| to get the PIDs of the processes we're
  // interested in; we save our results to avoid extra calls to
  // |NamedProcessIterator| (for performance reasons) and to avoid additional
  // inconsistencies caused by racing. Then we run |/bin/ps| *once* to get
  // information on those PIDs. Then we used our saved information to iterate
  // over browsers, then over PIDs.

  // Get PIDs of main browser processes.
  std::vector<base::ProcessId> all_pids;
  {
    base::NamedProcessIterator process_it(
        base::UTF16ToUTF8(process_data_[0].process_name), NULL);

    while (const base::ProcessEntry* entry = process_it.NextProcessEntry()) {
      all_pids.push_back(entry->pid());
    }
  }

  // Get PIDs of the helper.
  {
    base::NamedProcessIterator helper_it(chrome::kHelperProcessExecutableName,
                                         NULL, /*use_prefix_match=*/true);
    while (const base::ProcessEntry* entry = helper_it.NextProcessEntry()) {
      all_pids.push_back(entry->pid());
    }
  }

  ProcessMemoryInformationList* chrome_processes = &process_data_[0].processes;

  // Collect data about Chrome/Chromium.
  for (const base::ProcessId& pid : all_pids)
    CollectProcessDataForChromeProcess(child_info, pid, chrome_processes);

  // Finally return to the browser thread.
  content::GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(&MemoryDetails::CollectChildInfoOnUIThread, this));
}