chromium/chrome/browser/ash/chromebox_for_meetings/browser/cfm_memory_details.cc

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

#include "chrome/browser/ash/chromebox_for_meetings/browser/cfm_memory_details.h"

#include "base/containers/map_util.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/process_map.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension_set.h"

namespace ash::cfm {

// TODO(https://crbug.com/1403174): Remove when namespace of mojoms for CfM are
// migarted to ash.
namespace mojom = ::chromeos::cfm::mojom;

// static
void CfmMemoryDetails::Collect(
    mojom::CfmBrowser::GetMemoryDetailsCallback callback) {
  // Deletes itself upon completion.
  CfmMemoryDetails* details = new CfmMemoryDetails(std::move(callback));
  details->StartFetch();
}

CfmMemoryDetails::CfmMemoryDetails(
    mojom::CfmBrowser::GetMemoryDetailsCallback callback)
    : callback_(std::move(callback)) {
  AddRef();  // Released at the end of FinishFetch().
}

CfmMemoryDetails::~CfmMemoryDetails() = default;

void CfmMemoryDetails::OnDetailsAvailable() {
  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
    content::GetUIThreadTaskRunner({})->PostTask(
        FROM_HERE, base::BindOnce(&CfmMemoryDetails::OnDetailsAvailable, this));
    return;
  }

  CollectProcessInformation();

  // Now Post to UI thread for expensive extensions information lookups.
  content::GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(&CfmMemoryDetails::CollectExtensionsInformation, this));
}

void CfmMemoryDetails::CollectProcessInformation() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  proc_data_list_.reserve(processes().size());
  for (const ProcessData& proc_data : processes()) {
    std::vector<mojom::ProcessMemoryInformationPtr> proc_mem_info_list;
    proc_mem_info_list.reserve(proc_data.processes.size());
    for (const ProcessMemoryInformation& proc_mem_info : proc_data.processes) {
      auto proc_mem_info_mojom = mojom::ProcessMemoryInformation::New();
      proc_mem_info_mojom->pid = proc_mem_info.pid;
      proc_mem_info_mojom->version = base::UTF16ToUTF8(proc_mem_info.version);
      proc_mem_info_mojom->product_name =
          base::UTF16ToUTF8(proc_mem_info.product_name);
      proc_mem_info_mojom->num_processes = proc_mem_info.num_processes;
      proc_mem_info_mojom->num_open_fds = proc_mem_info.num_open_fds;
      proc_mem_info_mojom->open_fds_soft_limit =
          proc_mem_info.open_fds_soft_limit;
      proc_mem_info_mojom->private_memory_footprint_kb =
          proc_mem_info.private_memory_footprint_kb;

      // Avoid DCHECK in test builds by defining unknown directly
      bool process_type_unknown = proc_mem_info.process_type ==
                                  content::ProcessType::PROCESS_TYPE_UNKNOWN;
      bool process_type_renderer = proc_mem_info.process_type ==
                                   content::ProcessType::PROCESS_TYPE_RENDERER;
      bool renderer_type_unknown =
          proc_mem_info.renderer_type ==
          ProcessMemoryInformation::RendererProcessType::RENDERER_UNKNOWN;

      proc_mem_info_mojom->process_type =
          (process_type_renderer && renderer_type_unknown) ||
                  process_type_unknown
              ? "unknown"
              : ProcessMemoryInformation::GetFullTypeNameInEnglish(
                    proc_mem_info.process_type, proc_mem_info.renderer_type);

      proc_mem_info_mojom->renderer_type =
          renderer_type_unknown
              ? "unknown"
              : ProcessMemoryInformation::GetRendererTypeNameInEnglish(
                    proc_mem_info.renderer_type);

      auto& titles = proc_mem_info_mojom->titles;
      titles.reserve(proc_mem_info.titles.size());
      for (const std::u16string& title : proc_mem_info.titles) {
        titles.push_back(base::UTF16ToUTF8(title));
      }

      proc_mem_info_list.push_back(std::move(proc_mem_info_mojom));
      // We push back to a map that we can use to complete filling out
      // various information such as extensions
      proc_mem_info_map_[proc_mem_info.pid] = proc_mem_info_list.back().get();
    }
    proc_data_list_.push_back(
        mojom::ProcessData::New(base::UTF16ToUTF8(proc_data.name),
                                base::UTF16ToUTF8(proc_data.process_name),
                                std::move(proc_mem_info_list)));
  }
}

void CfmMemoryDetails::CollectExtensionsInformation() {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

#if BUILDFLAG(ENABLE_EXTENSIONS)
  for (content::RenderProcessHost::iterator it(
           content::RenderProcessHost::AllHostsIterator());
       !it.IsAtEnd(); it.Advance()) {
    content::RenderProcessHost* host = it.GetCurrentValue();
    // Only add valid processes
    if (!host->GetProcess().IsValid()) {
      continue;
    }

    // Check if process was added in current list
    auto* proc_mem_info =
        base::FindPtrOrNull(proc_mem_info_map_, host->GetProcess().Pid());
    if (!proc_mem_info) {
      continue;
    }

    // If no extension can be found in this process then no more work is
    // needed
    if (const extensions::Extension* extension =
            extensions::ProcessMap::Get(host->GetBrowserContext())
                ->GetEnabledExtensionByProcessID(host->GetID())) {
      proc_mem_info->extension_info.push_back(mojom::ExtensionData::New(
          extension->name(), extension->GetVersionForDisplay(), extension->id(),
          extension->hashed_id().value(), extension->description()));
    }
  }
#endif

  FinishFetch();
}

void CfmMemoryDetails::UpdateGpuInfo() {
  // Chrome OS exposes system-wide graphics driver memory which has
  // historically been a source of leak/bloat.
  base::GetGraphicsMemoryInfo(&gpu_meminfo_);
}

void CfmMemoryDetails::FinishFetch() {
  UpdateGpuInfo();
  std::move(callback_).Run(std::move(proc_data_list_),
                           gpu_meminfo_.gpu_memory_size);

  // Cleanup to ensure we are correctly released from memory.
  Release();
}

}  // namespace ash::cfm