| // Copyright (c) 2012 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. |
| |
| #include "chrome/browser/extensions/api/processes/processes_api.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <set> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/lazy_instance.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/process/process.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "chrome/browser/extensions/api/tabs/tabs_constants.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/task_manager/task_manager_interface.h" |
| #include "chrome/common/extensions/api/processes.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/browser/child_process_data.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/child_process_host.h" |
| #include "content/public/common/result_codes.h" |
| #include "extensions/common/error_utils.h" |
| #include "third_party/blink/public/platform/web_cache.h" |
| |
| namespace extensions { |
| |
| namespace errors { |
| const char kNotAllowedToTerminate[] = "Not allowed to terminate process: *."; |
| const char kProcessNotFound[] = "Process not found: *."; |
| const char kInvalidArgument[] = "Invalid argument: *."; |
| } // namespace errors |
| |
| namespace { |
| |
| base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI>>:: |
| DestructorAtExit g_processes_api_factory = LAZY_INSTANCE_INITIALIZER; |
| |
| int64_t GetRefreshTypesFlagOnlyEssentialData() { |
| // This is the only non-optional data in the Process as defined by the API in |
| // processes.idl. |
| return task_manager::REFRESH_TYPE_NACL; |
| } |
| |
| // This does not include memory. The memory refresh flag will only be added once |
| // a listener to OnUpdatedWithMemory event is added. |
| int64_t GetRefreshTypesForProcessOptionalData() { |
| return task_manager::REFRESH_TYPE_CPU | |
| task_manager::REFRESH_TYPE_NETWORK_USAGE | |
| task_manager::REFRESH_TYPE_SQLITE_MEMORY | |
| task_manager::REFRESH_TYPE_V8_MEMORY | |
| task_manager::REFRESH_TYPE_WEBCACHE_STATS; |
| } |
| |
| std::unique_ptr<api::processes::Cache> CreateCacheData( |
| const blink::WebCache::ResourceTypeStat& stat) { |
| std::unique_ptr<api::processes::Cache> cache(new api::processes::Cache()); |
| cache->size = static_cast<double>(stat.size); |
| cache->live_size = static_cast<double>(stat.size); |
| return cache; |
| } |
| |
| api::processes::ProcessType GetProcessType( |
| task_manager::Task::Type task_type) { |
| switch (task_type) { |
| case task_manager::Task::BROWSER: |
| return api::processes::PROCESS_TYPE_BROWSER; |
| |
| case task_manager::Task::RENDERER: |
| return api::processes::PROCESS_TYPE_RENDERER; |
| |
| case task_manager::Task::EXTENSION: |
| case task_manager::Task::GUEST: |
| return api::processes::PROCESS_TYPE_EXTENSION; |
| |
| case task_manager::Task::PLUGIN: |
| return api::processes::PROCESS_TYPE_PLUGIN; |
| |
| case task_manager::Task::WORKER: |
| return api::processes::PROCESS_TYPE_WORKER; |
| |
| case task_manager::Task::NACL: |
| return api::processes::PROCESS_TYPE_NACL; |
| |
| case task_manager::Task::SERVICE_WORKER: |
| return api::processes::PROCESS_TYPE_SERVICE_WORKER; |
| |
| case task_manager::Task::UTILITY: |
| return api::processes::PROCESS_TYPE_UTILITY; |
| |
| case task_manager::Task::GPU: |
| return api::processes::PROCESS_TYPE_GPU; |
| |
| case task_manager::Task::UNKNOWN: |
| case task_manager::Task::ARC: |
| case task_manager::Task::CROSTINI: |
| case task_manager::Task::SANDBOX_HELPER: |
| case task_manager::Task::ZYGOTE: |
| return api::processes::PROCESS_TYPE_OTHER; |
| } |
| |
| NOTREACHED() << "Unknown task type."; |
| return api::processes::PROCESS_TYPE_NONE; |
| } |
| |
| // Fills |out_process| with the data of the process in which the task with |id| |
| // is running. If |include_optional| is true, this function will fill the |
| // optional fields in |api::processes::Process| except for |private_memory|, |
| // which should be filled later if needed. |
| void FillProcessData( |
| task_manager::TaskId id, |
| task_manager::TaskManagerInterface* task_manager, |
| bool include_optional, |
| api::processes::Process* out_process) { |
| DCHECK(out_process); |
| |
| out_process->id = task_manager->GetChildProcessUniqueId(id); |
| out_process->os_process_id = task_manager->GetProcessId(id); |
| out_process->type = GetProcessType(task_manager->GetType(id)); |
| out_process->profile = base::UTF16ToUTF8(task_manager->GetProfileName(id)); |
| out_process->nacl_debug_port = task_manager->GetNaClDebugStubPort(id); |
| |
| // Collect the tab IDs of all the tasks sharing this renderer if any. |
| const task_manager::TaskIdList tasks_on_process = |
| task_manager->GetIdsOfTasksSharingSameProcess(id); |
| for (const auto& task_id : tasks_on_process) { |
| api::processes::TaskInfo task_info; |
| task_info.title = base::UTF16ToUTF8(task_manager->GetTitle(task_id)); |
| const SessionID tab_id = task_manager->GetTabId(task_id); |
| if (tab_id.is_valid()) |
| task_info.tab_id.reset(new int(tab_id.id())); |
| |
| out_process->tasks.push_back(std::move(task_info)); |
| } |
| |
| // If we don't need to include the optional properties, just return now. |
| if (!include_optional) |
| return; |
| |
| out_process->cpu.reset( |
| new double(task_manager->GetPlatformIndependentCPUUsage(id))); |
| out_process->network.reset(new double(static_cast<double>( |
| task_manager->GetProcessTotalNetworkUsage(id)))); |
| |
| int64_t v8_allocated = 0; |
| int64_t v8_used = 0; |
| if (task_manager->GetV8Memory(id, &v8_allocated, &v8_used)) { |
| out_process->js_memory_allocated.reset(new double(static_cast<double>( |
| v8_allocated))); |
| out_process->js_memory_used.reset(new double(static_cast<double>(v8_used))); |
| } |
| |
| const int64_t sqlite_bytes = task_manager->GetSqliteMemoryUsed(id); |
| if (sqlite_bytes != -1) { |
| out_process->sqlite_memory.reset(new double(static_cast<double>( |
| sqlite_bytes))); |
| } |
| |
| blink::WebCache::ResourceTypeStats cache_stats; |
| if (task_manager->GetWebCacheStats(id, &cache_stats)) { |
| out_process->image_cache = CreateCacheData(cache_stats.images); |
| out_process->script_cache = CreateCacheData(cache_stats.scripts); |
| out_process->css_cache = CreateCacheData(cache_stats.css_style_sheets); |
| } |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ProcessesEventRouter: |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context) |
| : task_manager::TaskManagerObserver(base::TimeDelta::FromSeconds(1), |
| task_manager::REFRESH_TYPE_NONE), |
| browser_context_(context), |
| listeners_(0) { |
| } |
| |
| ProcessesEventRouter::~ProcessesEventRouter() { |
| } |
| |
| void ProcessesEventRouter::ListenerAdded() { |
| UpdateRefreshTypesFlagsBasedOnListeners(); |
| |
| if (listeners_++ == 0) { |
| // The first listener to be added. |
| task_manager::TaskManagerInterface::GetTaskManager()->AddObserver(this); |
| } |
| } |
| |
| void ProcessesEventRouter::ListenerRemoved() { |
| UpdateRefreshTypesFlagsBasedOnListeners(); |
| |
| if (--listeners_ == 0) { |
| // Last listener to be removed. |
| task_manager::TaskManagerInterface::GetTaskManager()->RemoveObserver( |
| this); |
| } |
| } |
| |
| void ProcessesEventRouter::OnTaskAdded(task_manager::TaskId id) { |
| if (!HasEventListeners(api::processes::OnCreated::kEventName)) |
| return; |
| |
| int child_process_host_id = 0; |
| if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id)) |
| return; |
| |
| api::processes::Process process; |
| FillProcessData(id, |
| observed_task_manager(), |
| false, // include_optional |
| &process); |
| DispatchEvent(events::PROCESSES_ON_CREATED, |
| api::processes::OnCreated::kEventName, |
| api::processes::OnCreated::Create(process)); |
| } |
| |
| void ProcessesEventRouter::OnTaskToBeRemoved(task_manager::TaskId id) { |
| if (!HasEventListeners(api::processes::OnExited::kEventName)) |
| return; |
| |
| int child_process_host_id = 0; |
| if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id)) |
| return; |
| |
| int exit_code = 0; |
| base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING; |
| observed_task_manager()->GetTerminationStatus(id, &status, &exit_code); |
| |
| DispatchEvent(events::PROCESSES_ON_EXITED, |
| api::processes::OnExited::kEventName, |
| api::processes::OnExited::Create(child_process_host_id, |
| status, |
| exit_code)); |
| } |
| |
| void ProcessesEventRouter::OnTasksRefreshedWithBackgroundCalculations( |
| const task_manager::TaskIdList& task_ids) { |
| const bool has_on_updated_listeners = |
| HasEventListeners(api::processes::OnUpdated::kEventName); |
| const bool has_on_updated_with_memory_listeners = |
| HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName); |
| |
| if (!has_on_updated_listeners && !has_on_updated_with_memory_listeners) |
| return; |
| |
| // Get the data of tasks sharing the same process only once. |
| std::set<base::ProcessId> seen_processes; |
| base::DictionaryValue processes_dictionary; |
| for (const auto& task_id : task_ids) { |
| // We are not interested in tasks, but rather the processes on which they |
| // run. |
| const base::ProcessId proc_id = |
| observed_task_manager()->GetProcessId(task_id); |
| if (seen_processes.count(proc_id)) |
| continue; |
| |
| const int child_process_host_id = |
| observed_task_manager()->GetChildProcessUniqueId(task_id); |
| // Ignore tasks that don't have a valid child process host ID like ARC |
| // processes. We report the browser process info here though. |
| if (child_process_host_id == content::ChildProcessHost::kInvalidUniqueID) |
| continue; |
| |
| seen_processes.insert(proc_id); |
| api::processes::Process process; |
| FillProcessData(task_id, |
| observed_task_manager(), |
| true, // include_optional |
| &process); |
| |
| if (has_on_updated_with_memory_listeners) { |
| // Append the memory footprint to the process data. |
| const int64_t memory_footprint = |
| observed_task_manager()->GetMemoryFootprintUsage(task_id); |
| process.private_memory = |
| std::make_unique<double>(static_cast<double>(memory_footprint)); |
| } |
| |
| // Store each process indexed by the string version of its ChildProcessHost |
| // ID. |
| processes_dictionary.Set(base::NumberToString(child_process_host_id), |
| process.ToValue()); |
| } |
| |
| // Done with data collection. Now dispatch the appropriate events according to |
| // the present listeners. |
| DCHECK(has_on_updated_listeners || has_on_updated_with_memory_listeners); |
| if (has_on_updated_listeners) { |
| api::processes::OnUpdated::Processes processes; |
| processes.additional_properties.MergeDictionary(&processes_dictionary); |
| // NOTE: If there are listeners to the updates with memory as well, |
| // listeners to onUpdated (without memory) will also get the memory info |
| // of processes as an added bonus. |
| DispatchEvent(events::PROCESSES_ON_UPDATED, |
| api::processes::OnUpdated::kEventName, |
| api::processes::OnUpdated::Create(processes)); |
| } |
| |
| if (has_on_updated_with_memory_listeners) { |
| api::processes::OnUpdatedWithMemory::Processes processes; |
| processes.additional_properties.MergeDictionary(&processes_dictionary); |
| DispatchEvent(events::PROCESSES_ON_UPDATED_WITH_MEMORY, |
| api::processes::OnUpdatedWithMemory::kEventName, |
| api::processes::OnUpdatedWithMemory::Create(processes)); |
| } |
| } |
| |
| void ProcessesEventRouter::OnTaskUnresponsive(task_manager::TaskId id) { |
| if (!HasEventListeners(api::processes::OnUnresponsive::kEventName)) |
| return; |
| |
| api::processes::Process process; |
| FillProcessData(id, |
| observed_task_manager(), |
| false, // include_optional |
| &process); |
| DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE, |
| api::processes::OnUnresponsive::kEventName, |
| api::processes::OnUnresponsive::Create(process)); |
| } |
| |
| void ProcessesEventRouter::DispatchEvent( |
| events::HistogramValue histogram_value, |
| const std::string& event_name, |
| std::unique_ptr<base::ListValue> event_args) const { |
| EventRouter* event_router = EventRouter::Get(browser_context_); |
| if (event_router) { |
| std::unique_ptr<Event> event( |
| new Event(histogram_value, event_name, std::move(event_args))); |
| event_router->BroadcastEvent(std::move(event)); |
| } |
| } |
| |
| bool ProcessesEventRouter::HasEventListeners( |
| const std::string& event_name) const { |
| EventRouter* event_router = EventRouter::Get(browser_context_); |
| return event_router && event_router->HasEventListener(event_name); |
| } |
| |
| bool ProcessesEventRouter::ShouldReportOnCreatedOrOnExited( |
| task_manager::TaskId id, |
| int* out_child_process_host_id) const { |
| // Is it the first task to be created or the last one to be removed? |
| if (observed_task_manager()->GetNumberOfTasksOnSameProcess(id) != 1) |
| return false; |
| |
| // Ignore tasks that don't have a valid child process host ID like ARC |
| // processes, as well as the browser process (neither onCreated() nor |
| // onExited() shouldn't report the browser process). |
| *out_child_process_host_id = |
| observed_task_manager()->GetChildProcessUniqueId(id); |
| if (*out_child_process_host_id == |
| content::ChildProcessHost::kInvalidUniqueID || |
| *out_child_process_host_id == 0) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void ProcessesEventRouter::UpdateRefreshTypesFlagsBasedOnListeners() { |
| int64_t refresh_types = task_manager::REFRESH_TYPE_NONE; |
| if (HasEventListeners(api::processes::OnCreated::kEventName) || |
| HasEventListeners(api::processes::OnUnresponsive::kEventName)) { |
| refresh_types |= GetRefreshTypesFlagOnlyEssentialData(); |
| } |
| |
| const int64_t on_updated_types = GetRefreshTypesForProcessOptionalData(); |
| if (HasEventListeners(api::processes::OnUpdated::kEventName)) |
| refresh_types |= on_updated_types; |
| |
| if (HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName)) { |
| refresh_types |= |
| (on_updated_types | task_manager::REFRESH_TYPE_MEMORY_FOOTPRINT); |
| } |
| |
| SetRefreshTypesFlags(refresh_types); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ProcessesAPI: |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ProcessesAPI::ProcessesAPI(content::BrowserContext* context) |
| : browser_context_(context) { |
| EventRouter* event_router = EventRouter::Get(browser_context_); |
| // Monitor when the following events are being listened to in order to know |
| // when to start the task manager. |
| event_router->RegisterObserver(this, api::processes::OnUpdated::kEventName); |
| event_router->RegisterObserver( |
| this, api::processes::OnUpdatedWithMemory::kEventName); |
| event_router->RegisterObserver(this, api::processes::OnCreated::kEventName); |
| event_router->RegisterObserver(this, |
| api::processes::OnUnresponsive::kEventName); |
| event_router->RegisterObserver(this, api::processes::OnExited::kEventName); |
| } |
| |
| ProcessesAPI::~ProcessesAPI() { |
| // This object has already been unregistered as an observer in Shutdown(). |
| } |
| |
| // static |
| BrowserContextKeyedAPIFactory<ProcessesAPI>* |
| ProcessesAPI::GetFactoryInstance() { |
| return g_processes_api_factory.Pointer(); |
| } |
| |
| // static |
| ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) { |
| return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context); |
| } |
| |
| void ProcessesAPI::Shutdown() { |
| EventRouter::Get(browser_context_)->UnregisterObserver(this); |
| } |
| |
| void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) { |
| // The ProcessesEventRouter will observe the TaskManager as long as there are |
| // listeners for the processes.onUpdated/.onUpdatedWithMemory/.onCreated ... |
| // etc. events. |
| processes_event_router()->ListenerAdded(); |
| } |
| |
| void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) { |
| // If a processes.onUpdated/.onUpdatedWithMemory/.onCreated ... etc. event |
| // listener is removed (or a process with one exits), then we let the |
| // extension API know that it has one fewer listener. |
| processes_event_router()->ListenerRemoved(); |
| } |
| |
| ProcessesEventRouter* ProcessesAPI::processes_event_router() { |
| if (!processes_event_router_.get()) |
| processes_event_router_.reset(new ProcessesEventRouter(browser_context_)); |
| return processes_event_router_.get(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ProcessesGetProcessIdForTabFunction: |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ExtensionFunction::ResponseAction ProcessesGetProcessIdForTabFunction::Run() { |
| // For this function, the task manager doesn't even need to be running. |
| std::unique_ptr<api::processes::GetProcessIdForTab::Params> params( |
| api::processes::GetProcessIdForTab::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| const int tab_id = params->tab_id; |
| content::WebContents* contents = nullptr; |
| int tab_index = -1; |
| if (!ExtensionTabUtil::GetTabById( |
| tab_id, Profile::FromBrowserContext(browser_context()), |
| include_incognito_information(), nullptr, nullptr, &contents, |
| &tab_index)) { |
| return RespondNow( |
| Error(tabs_constants::kTabNotFoundError, base::NumberToString(tab_id))); |
| } |
| |
| // TODO(https://crbug.com/767563): chrome.processes.getProcessIdForTab API |
| // incorrectly assumes a *single* renderer process per tab. |
| const int process_id = contents->GetMainFrame()->GetProcess()->GetID(); |
| return RespondNow(ArgumentList( |
| api::processes::GetProcessIdForTab::Results::Create(process_id))); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ProcessesTerminateFunction: |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ExtensionFunction::ResponseAction ProcessesTerminateFunction::Run() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // For this function, the task manager doesn't even need to be running. |
| std::unique_ptr<api::processes::Terminate::Params> params( |
| api::processes::Terminate::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| child_process_host_id_ = params->process_id; |
| if (child_process_host_id_ < 0) { |
| return RespondNow(Error(errors::kInvalidArgument, |
| base::NumberToString(child_process_host_id_))); |
| } else if (child_process_host_id_ == 0) { |
| // Cannot kill the browser process. |
| return RespondNow(Error(errors::kNotAllowedToTerminate, |
| base::NumberToString(child_process_host_id_))); |
| } |
| |
| // Check if it's a renderer. |
| auto* render_process_host = |
| content::RenderProcessHost::FromID(child_process_host_id_); |
| if (render_process_host) |
| return RespondNow( |
| TerminateIfAllowed(render_process_host->GetProcess().Handle())); |
| |
| // This could be a non-renderer child process like a plugin or a nacl |
| // process. Try to get its handle from the BrowserChildProcessHost on the |
| // IO thread. |
| base::PostTaskWithTraitsAndReplyWithResult( |
| FROM_HERE, {content::BrowserThread::IO}, |
| base::Bind(&ProcessesTerminateFunction::GetProcessHandleOnIO, this, |
| child_process_host_id_), |
| base::Bind(&ProcessesTerminateFunction::OnProcessHandleOnUI, this)); |
| |
| // Promise to respond later. |
| return RespondLater(); |
| } |
| |
| base::ProcessHandle ProcessesTerminateFunction::GetProcessHandleOnIO( |
| int child_process_host_id) const { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| auto* host = content::BrowserChildProcessHost::FromID(child_process_host_id); |
| if (host) |
| return host->GetData().GetProcess().Handle(); |
| |
| return base::kNullProcessHandle; |
| } |
| |
| void ProcessesTerminateFunction::OnProcessHandleOnUI( |
| base::ProcessHandle handle) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| Respond(TerminateIfAllowed(handle)); |
| } |
| |
| ExtensionFunction::ResponseValue |
| ProcessesTerminateFunction::TerminateIfAllowed(base::ProcessHandle handle) { |
| if (handle == base::kNullProcessHandle) { |
| return Error(errors::kProcessNotFound, |
| base::NumberToString(child_process_host_id_)); |
| } |
| |
| if (handle == base::GetCurrentProcessHandle()) { |
| // Cannot kill the browser process. |
| return Error(errors::kNotAllowedToTerminate, |
| base::NumberToString(child_process_host_id_)); |
| } |
| |
| base::Process process = base::Process::Open(base::GetProcId(handle)); |
| if (!process.IsValid()) { |
| return Error(errors::kProcessNotFound, |
| base::NumberToString(child_process_host_id_)); |
| } |
| |
| const bool did_terminate = |
| process.Terminate(content::RESULT_CODE_KILLED, true /* wait */); |
| if (did_terminate) |
| UMA_HISTOGRAM_COUNTS_1M("ChildProcess.KilledByExtensionAPI", 1); |
| |
| return ArgumentList( |
| api::processes::Terminate::Results::Create(did_terminate)); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ProcessesGetProcessInfoFunction: |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ProcessesGetProcessInfoFunction::ProcessesGetProcessInfoFunction() |
| : task_manager::TaskManagerObserver( |
| base::TimeDelta::FromSeconds(1), |
| GetRefreshTypesFlagOnlyEssentialData()) { |
| } |
| |
| ExtensionFunction::ResponseAction ProcessesGetProcessInfoFunction::Run() { |
| std::unique_ptr<api::processes::GetProcessInfo::Params> params( |
| api::processes::GetProcessInfo::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| if (params->process_ids.as_integer) |
| process_host_ids_.push_back(*params->process_ids.as_integer); |
| else |
| process_host_ids_.swap(*params->process_ids.as_integers); |
| |
| include_memory_ = params->include_memory; |
| if (include_memory_) |
| AddRefreshType(task_manager::REFRESH_TYPE_MEMORY_FOOTPRINT); |
| |
| // Keep this object alive until the first of either OnTasksRefreshed() or |
| // OnTasksRefreshedWithBackgroundCalculations() is received depending on |
| // |include_memory_|. |
| AddRef(); |
| |
| // The task manager needs to be enabled for this function. |
| // Start observing the task manager and wait for the next refresh event. |
| task_manager::TaskManagerInterface::GetTaskManager()->AddObserver(this); |
| |
| return RespondLater(); |
| } |
| |
| void ProcessesGetProcessInfoFunction::OnTasksRefreshed( |
| const task_manager::TaskIdList& task_ids) { |
| // Memory is background calculated and will be ready when |
| // OnTasksRefreshedWithBackgroundCalculations() is invoked. |
| if (include_memory_) |
| return; |
| |
| GatherDataAndRespond(task_ids); |
| } |
| |
| void |
| ProcessesGetProcessInfoFunction::OnTasksRefreshedWithBackgroundCalculations( |
| const task_manager::TaskIdList& task_ids) { |
| if (!include_memory_) |
| return; |
| |
| GatherDataAndRespond(task_ids); |
| } |
| |
| ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() {} |
| |
| void ProcessesGetProcessInfoFunction::GatherDataAndRespond( |
| const task_manager::TaskIdList& task_ids) { |
| // If there are no process IDs specified, it means we need to return all of |
| // the ones we know of. |
| const bool specific_processes_requested = !process_host_ids_.empty(); |
| std::set<base::ProcessId> seen_processes; |
| // Create the results object as defined in the generated API from process.idl |
| // and fill it with the processes info. |
| api::processes::GetProcessInfo::Results::Processes processes; |
| for (const auto& task_id : task_ids) { |
| const base::ProcessId proc_id = |
| observed_task_manager()->GetProcessId(task_id); |
| if (seen_processes.count(proc_id)) |
| continue; |
| |
| const int child_process_host_id = |
| observed_task_manager()->GetChildProcessUniqueId(task_id); |
| // Ignore tasks that don't have a valid child process host ID like ARC |
| // processes. We report the browser process info here though. |
| if (child_process_host_id == content::ChildProcessHost::kInvalidUniqueID) |
| continue; |
| |
| if (specific_processes_requested) { |
| // Note: we can't use |!process_host_ids_.empty()| directly in the above |
| // condition as we will erase from |process_host_ids_| below. |
| auto itr = std::find(process_host_ids_.begin(), |
| process_host_ids_.end(), |
| child_process_host_id); |
| if (itr == process_host_ids_.end()) |
| continue; |
| |
| // If found, we remove it from |process_host_ids|, so that at the end if |
| // anything remains in |process_host_ids|, those were invalid arguments |
| // that will be reported on the console. |
| process_host_ids_.erase(itr); |
| } |
| |
| seen_processes.insert(proc_id); |
| |
| // We do not include the optional data in this function results. |
| api::processes::Process process; |
| FillProcessData(task_id, |
| observed_task_manager(), |
| false, // include_optional |
| &process); |
| |
| if (include_memory_) { |
| // Append the memory footprint to the process data. |
| const int64_t memory_footprint = |
| observed_task_manager()->GetMemoryFootprintUsage(task_id); |
| process.private_memory = |
| std::make_unique<double>(static_cast<double>(memory_footprint)); |
| } |
| |
| // Store each process indexed by the string version of its |
| // ChildProcessHost ID. |
| processes.additional_properties.Set( |
| base::NumberToString(child_process_host_id), process.ToValue()); |
| } |
| |
| // Report the invalid host ids sent in the arguments. |
| for (const auto& host_id : process_host_ids_) { |
| WriteToConsole( |
| blink::mojom::ConsoleMessageLevel::kError, |
| ErrorUtils::FormatErrorMessage(errors::kProcessNotFound, |
| base::NumberToString(host_id))); |
| } |
| |
| // Send the response. |
| Respond(ArgumentList( |
| api::processes::GetProcessInfo::Results::Create(processes))); |
| |
| // Stop observing the task manager, and balance the AddRef() in Run(). |
| task_manager::TaskManagerInterface::GetTaskManager()->RemoveObserver(this); |
| Release(); |
| } |
| |
| } // namespace extensions |