Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Unified Diff: chrome/browser/extensions/api/processes/processes_api.cc

Issue 1584473004: Migrate ProcessesEventRouter to the new task manager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix nit. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/api/processes/processes_api.cc
diff --git a/chrome/browser/extensions/api/processes/processes_api.cc b/chrome/browser/extensions/api/processes/processes_api.cc
index ae43fd23870720bc227e4e58662776163e1a1c94..77a49a02cd2156f3f3609bb2e454b70c714d1f97 100644
--- a/chrome/browser/extensions/api/processes/processes_api.cc
+++ b/chrome/browser/extensions/api/processes/processes_api.cc
@@ -4,57 +4,57 @@
#include "chrome/browser/extensions/api/processes/processes_api.h"
-#include <stddef.h>
#include <stdint.h>
#include <algorithm>
-#include <utility>
-#include "base/callback.h"
-#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
-#include "base/location.h"
-#include "base/metrics/histogram.h"
-#include "base/single_thread_task_runner.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/thread_task_runner_handle.h"
-#include "base/values.h"
-#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
-#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/task_manager/resource_provider.h"
-#include "chrome/browser/task_manager/task_manager.h"
+#include "chrome/browser/task_management/task_manager_interface.h"
#include "chrome/common/extensions/api/processes.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/notification_types.h"
+#include "content/public/browser/browser_child_process_host.h"
+#include "content/public/browser/child_process_data.h"
#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/render_widget_host.h"
-#include "content/public/browser/render_widget_host_iterator.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/browser/event_router.h"
-#include "extensions/browser/extension_function_registry.h"
-#include "extensions/browser/extension_function_util.h"
#include "extensions/common/error_utils.h"
+#include "third_party/WebKit/public/web/WebCache.h"
namespace extensions {
namespace errors {
const char kNotAllowedToTerminate[] = "Not allowed to terminate process: *.";
const char kProcessNotFound[] = "Process not found: *.";
-const char kInavlidArgument[] = "Invalid argument: *.";
+const char kInvalidArgument[] = "Invalid argument: *.";
} // namespace errors
namespace {
-#if defined(ENABLE_TASK_MANAGER)
+base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI>>
+ 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_management::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_management::REFRESH_TYPE_CPU |
+ task_management::REFRESH_TYPE_NETWORK_USAGE |
+ task_management::REFRESH_TYPE_SQLITE_MEMORY |
+ task_management::REFRESH_TYPE_V8_MEMORY |
+ task_management::REFRESH_TYPE_WEBCACHE_STATS;
+}
scoped_ptr<api::processes::Cache> CreateCacheData(
const blink::WebCache::ResourceTypeStat& stat) {
@@ -64,279 +64,232 @@ scoped_ptr<api::processes::Cache> CreateCacheData(
return cache;
}
-api::processes::ProcessType GetProcessType(TaskManagerModel* model,
- int index) {
- // Determine process type.
- task_manager::Resource::Type resource_type = model->GetResourceType(index);
- switch (resource_type) {
- case task_manager::Resource::BROWSER:
+api::processes::ProcessType GetProcessType(
+ task_management::Task::Type task_type) {
+ switch (task_type) {
+ case task_management::Task::BROWSER:
return api::processes::PROCESS_TYPE_BROWSER;
- case task_manager::Resource::RENDERER:
+ case task_management::Task::RENDERER:
return api::processes::PROCESS_TYPE_RENDERER;
- case task_manager::Resource::EXTENSION:
- case task_manager::Resource::GUEST:
+ case task_management::Task::EXTENSION:
+ case task_management::Task::GUEST:
return api::processes::PROCESS_TYPE_EXTENSION;
- case task_manager::Resource::NOTIFICATION:
- return api::processes::PROCESS_TYPE_NOTIFICATION;
-
- case task_manager::Resource::PLUGIN:
+ case task_management::Task::PLUGIN:
return api::processes::PROCESS_TYPE_PLUGIN;
- case task_manager::Resource::WORKER:
+ case task_management::Task::WORKER:
return api::processes::PROCESS_TYPE_WORKER;
- case task_manager::Resource::NACL:
+ case task_management::Task::NACL:
return api::processes::PROCESS_TYPE_NACL;
- case task_manager::Resource::UTILITY:
+ case task_management::Task::UTILITY:
return api::processes::PROCESS_TYPE_UTILITY;
- case task_manager::Resource::GPU:
+ case task_management::Task::GPU:
return api::processes::PROCESS_TYPE_GPU;
- case task_manager::Resource::ZYGOTE:
- case task_manager::Resource::SANDBOX_HELPER:
- case task_manager::Resource::UNKNOWN:
+ case task_management::Task::UNKNOWN:
+ case task_management::Task::ARC:
+ case task_management::Task::SANDBOX_HELPER:
+ case task_management::Task::ZYGOTE:
return api::processes::PROCESS_TYPE_OTHER;
}
- NOTREACHED() << "Unknown resource type.";
+ NOTREACHED() << "Unknown task type.";
return api::processes::PROCESS_TYPE_NONE;
}
-void FillTabsForProcess(int process_id, api::processes::Process* out_process) {
+// 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_management::TaskId id,
+ task_management::TaskManagerInterface* task_manager,
+ bool include_optional,
+ api::processes::Process* out_process) {
DCHECK(out_process);
- // The tabs list only makes sense for render processes, so if we don't find
- // one, just return the empty list.
- content::RenderProcessHost* rph =
- content::RenderProcessHost::FromID(process_id);
- if (!rph)
- return;
-
- int tab_id = -1;
- // We need to loop through all the RVHs to ensure we collect the set of all
- // tabs using this renderer process.
- scoped_ptr<content::RenderWidgetHostIterator> widgets(
- content::RenderWidgetHost::GetRenderWidgetHosts());
- while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
- if (widget->GetProcess()->GetID() != process_id)
- continue;
-
- content::RenderViewHost* host = content::RenderViewHost::From(widget);
- content::WebContents* contents =
- content::WebContents::FromRenderViewHost(host);
- if (contents) {
- tab_id = ExtensionTabUtil::GetTabId(contents);
- if (tab_id != -1)
- out_process->tabs.push_back(tab_id);
- }
+ 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_management::TaskIdList tasks_on_process =
+ task_manager->GetIdsOfTasksSharingSameProcess(id);
+ for (const auto& task_id : tasks_on_process) {
+ linked_ptr<api::processes::TaskInfo> task_info(
+ new api::processes::TaskInfo());
+ task_info->title = base::UTF16ToUTF8(task_manager->GetTitle(task_id));
+ const int tab_id = task_manager->GetTabId(task_id);
+ if (tab_id != -1)
+ task_info->tab_id.reset(new int(tab_id));
+
+ out_process->tasks.push_back(task_info);
}
-}
-
-// This function fills |out_process| with the data of the process with
-// |process_id|. For memory details, which are not added by this function,
-// the callers need to use AddMemoryDetails.
-void FillProcessData(int process_id,
- TaskManagerModel* model,
- int index,
- bool include_optional,
- api::processes::Process* out_process) {
- DCHECK(out_process);
-
- out_process->id = process_id;
- out_process->os_process_id = model->GetProcessId(index);
- out_process->type = GetProcessType(model, index);
- out_process->title = base::UTF16ToUTF8(model->GetResourceTitle(index));
- out_process->profile =
- base::UTF16ToUTF8(model->GetResourceProfileName(index));
- out_process->nacl_debug_port = model->GetNaClDebugStubPort(index);
-
- FillTabsForProcess(process_id, out_process);
// If we don't need to include the optional properties, just return now.
if (!include_optional)
return;
- out_process->cpu.reset(new double(model->GetCPUUsage(index)));
+ out_process->cpu.reset(new double(task_manager->GetCpuUsage(id)));
+
+ out_process->network.reset(new double(static_cast<double>(
+ task_manager->GetProcessTotalNetworkUsage(id))));
- size_t mem = 0;
- if (model->GetV8Memory(index, &mem)) {
+ 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>(
- mem)));
+ v8_allocated)));
+ out_process->js_memory_used.reset(new double(static_cast<double>(v8_used)));
}
- if (model->GetV8MemoryUsed(index, &mem))
- out_process->js_memory_used.reset(new double(static_cast<double>(mem)));
-
- if (model->GetSqliteMemoryUsedBytes(index, &mem))
- out_process->sqlite_memory.reset(new double(static_cast<double>(mem)));
+ 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 (model->GetWebCoreCacheStats(index, &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.cssStyleSheets);
}
-
- // Network is reported by the TaskManager per resource (tab), not per
- // process, therefore we need to iterate through the group of resources
- // and aggregate the data.
- int64_t net = 0;
- int length = model->GetGroupRangeForResource(index).second;
- for (int i = 0; i < length; ++i)
- net += model->GetNetworkUsage(index + i);
- out_process->network.reset(new double(static_cast<double>(net)));
-}
-
-// Since memory details are expensive to gather, we don't do it by default.
-// This function is a helper to add memory details data to an existing
-// Process object |out_process|.
-void AddMemoryDetails(TaskManagerModel* model,
- int index,
- api::processes::Process* out_process) {
- DCHECK(out_process);
-
- size_t mem;
- int64_t pr_mem =
- model->GetPrivateMemory(index, &mem) ? static_cast<int64_t>(mem) : -1;
- out_process->private_memory.reset(new double(static_cast<double>(pr_mem)));
}
-#endif // defined(ENABLE_TASK_MANAGER)
-
} // namespace
-ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context)
- : browser_context_(context), listeners_(0), task_manager_listening_(false) {
-#if defined(ENABLE_TASK_MANAGER)
- model_ = TaskManager::GetInstance()->model();
- model_->AddObserver(this);
+////////////////////////////////////////////////////////////////////////////////
+// ProcessesEventRouter:
+////////////////////////////////////////////////////////////////////////////////
- registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
- content::NotificationService::AllSources());
- registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
- content::NotificationService::AllSources());
-#endif // defined(ENABLE_TASK_MANAGER)
+ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context)
+ : task_management::TaskManagerObserver(base::TimeDelta::FromSeconds(1),
+ task_management::REFRESH_TYPE_NONE),
+ browser_context_(context),
+ listeners_(0) {
}
ProcessesEventRouter::~ProcessesEventRouter() {
-#if defined(ENABLE_TASK_MANAGER)
- registrar_.Remove(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
- content::NotificationService::AllSources());
- registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
- content::NotificationService::AllSources());
-
- if (task_manager_listening_)
- model_->StopListening();
-
- model_->RemoveObserver(this);
-#endif // defined(ENABLE_TASK_MANAGER)
}
void ProcessesEventRouter::ListenerAdded() {
-#if defined(ENABLE_TASK_MANAGER)
- // The task manager has its own ref count to balance other callers of
- // StartUpdating/StopUpdating.
- model_->StartUpdating();
-#endif // defined(ENABLE_TASK_MANAGER)
- ++listeners_;
+ UpdateRefreshTypesFlagsBasedOnListeners();
+
+ if (listeners_++ == 0) {
+ // The first listener to be added.
+ task_management::TaskManagerInterface::GetTaskManager()->AddObserver(this);
+ }
}
void ProcessesEventRouter::ListenerRemoved() {
- DCHECK_GT(listeners_, 0);
- --listeners_;
-#if defined(ENABLE_TASK_MANAGER)
- // The task manager has its own ref count to balance other callers of
- // StartUpdating/StopUpdating.
- model_->StopUpdating();
-#endif // defined(ENABLE_TASK_MANAGER)
-}
-
-void ProcessesEventRouter::StartTaskManagerListening() {
-#if defined(ENABLE_TASK_MANAGER)
- if (!task_manager_listening_) {
- model_->StartListening();
- task_manager_listening_ = true;
- }
-#endif // defined(ENABLE_TASK_MANAGER)
-}
-
-void ProcessesEventRouter::Observe(
- int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
-
- switch (type) {
- case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
- ProcessHangEvent(
- content::Source<content::RenderWidgetHost>(source).ptr());
- break;
- case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
- ProcessClosedEvent(
- content::Source<content::RenderProcessHost>(source).ptr(),
- content::Details<content::RenderProcessHost::RendererClosedDetails>(
- details).ptr());
- break;
- default:
- NOTREACHED() << "Unexpected observe of type " << type;
+ UpdateRefreshTypesFlagsBasedOnListeners();
+
+ if (--listeners_ == 0) {
+ // Last listener to be removed.
+ task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(
+ this);
}
- return;
}
-void ProcessesEventRouter::OnItemsAdded(int start, int length) {
-#if defined(ENABLE_TASK_MANAGER)
- DCHECK_EQ(length, 1);
+void ProcessesEventRouter::OnTaskAdded(task_management::TaskId id) {
if (!HasEventListeners(api::processes::OnCreated::kEventName))
return;
- // If the item being added is not the first one in the group, find the base
- // index and use it for retrieving the process data.
- if (!model_->IsResourceFirstInGroup(start))
- start = model_->GetGroupIndexForResource(start);
+ int child_process_host_id = 0;
+ if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id))
+ return;
api::processes::Process process;
- FillProcessData(model_->GetUniqueChildProcessId(start), model_, start,
- false /* include_optional */, &process);
+ FillProcessData(id,
+ observed_task_manager(),
+ false, // include_optional
+ &process);
DispatchEvent(events::PROCESSES_ON_CREATED,
api::processes::OnCreated::kEventName,
api::processes::OnCreated::Create(process));
-#endif // defined(ENABLE_TASK_MANAGER)
}
-void ProcessesEventRouter::OnItemsChanged(int start, int length) {
-#if defined(ENABLE_TASK_MANAGER)
- // If we don't have any listeners, return immediately.
- if (listeners_ == 0)
+void ProcessesEventRouter::OnTaskToBeRemoved(task_management::TaskId id) {
+ if (!HasEventListeners(api::processes::OnExited::kEventName))
return;
- if (!model_)
+ int child_process_host_id = 0;
+ if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id))
return;
- // We need to know which type of onUpdated events to fire and whether to
- // collect memory or not.
- bool updated = HasEventListeners(api::processes::OnUpdated::kEventName);
- bool updated_memory =
+ 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_management::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 (!updated && !updated_memory)
+
+ 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 (int i = start; i < start + length; ++i) {
- if (model_->IsResourceFirstInGroup(i)) {
- int id = model_->GetUniqueChildProcessId(i);
- api::processes::Process process;
- FillProcessData(id, model_, i, true /* include_optional */, &process);
- if (updated_memory)
- AddMemoryDetails(model_, i, &process);
- processes_dictionary.Set(base::IntToString(id), process.ToValue());
+ 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 private memory usage to the process data.
+ const int64_t private_memory =
+ observed_task_manager()->GetPrivateMemoryUsage(task_id);
+ process.private_memory.reset(new double(static_cast<double>(
+ private_memory)));
}
+
+ // Store each process indexed by the string version of its ChildProcessHost
+ // ID.
+ processes_dictionary.Set(base::IntToString(child_process_host_id),
+ process.ToValue());
}
- if (updated) {
+ // 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,
@@ -347,80 +300,33 @@ void ProcessesEventRouter::OnItemsChanged(int start, int length) {
api::processes::OnUpdated::Create(processes));
}
- if (updated_memory) {
+ 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));}
-#endif // defined(ENABLE_TASK_MANAGER)
-}
-
-void ProcessesEventRouter::OnItemsToBeRemoved(int start, int length) {
-#if defined(ENABLE_TASK_MANAGER)
- DCHECK_EQ(length, 1);
-
- if (!HasEventListeners(api::processes::OnExited::kEventName))
- return;
-
- // Process exit for renderer processes has the data about exit code and
- // termination status, therefore we will rely on notifications and not on
- // the Task Manager data. We do use the rest of this method for non-renderer
- // processes.
- if (model_->GetResourceType(start) == task_manager::Resource::RENDERER)
- return;
-
- DispatchEvent(events::PROCESSES_ON_EXITED,
- api::processes::OnExited::kEventName,
- api::processes::OnExited::Create(
- model_->GetUniqueChildProcessId(start),
- 0 /* exit_type */,
- 0 /* exit_code */));
-#endif // defined(ENABLE_TASK_MANAGER)
-}
-
-void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost* widget) {
-#if defined(ENABLE_TASK_MANAGER)
- if (!HasEventListeners(api::processes::OnUnresponsive::kEventName))
- return;
-
- int count = model_->ResourceCount();
- int id = widget->GetProcess()->GetID();
-
- for (int i = 0; i < count; ++i) {
- if (model_->IsResourceFirstInGroup(i)) {
- if (id == model_->GetUniqueChildProcessId(i)) {
- api::processes::Process process;
- FillProcessData(id, model_, i, false /* include_optional */, &process);
- DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE,
- api::processes::OnUnresponsive::kEventName,
- api::processes::OnUnresponsive::Create(process));
- return;
- }
- }
+ api::processes::OnUpdatedWithMemory::Create(processes));
}
-#endif // defined(ENABLE_TASK_MANAGER)
}
-void ProcessesEventRouter::ProcessClosedEvent(
- content::RenderProcessHost* rph,
- content::RenderProcessHost::RendererClosedDetails* details) {
-#if defined(ENABLE_TASK_MANAGER)
- if (!HasEventListeners(api::processes::OnExited::kEventName))
+void ProcessesEventRouter::OnTaskUnresponsive(task_management::TaskId id) {
+ if (!HasEventListeners(api::processes::OnUnresponsive::kEventName))
return;
- DispatchEvent(events::PROCESSES_ON_EXITED,
- api::processes::OnExited::kEventName,
- api::processes::OnExited::Create(rph->GetID(),
- details->status,
- details->exit_code));
-#endif // defined(ENABLE_TASK_MANAGER)
+ 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,
- scoped_ptr<base::ListValue> event_args) {
+ scoped_ptr<base::ListValue> event_args) const {
EventRouter* event_router = EventRouter::Get(browser_context_);
if (event_router) {
scoped_ptr<Event> event(
@@ -429,11 +335,53 @@ void ProcessesEventRouter::DispatchEvent(
}
}
-bool ProcessesEventRouter::HasEventListeners(const std::string& event_name) {
+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_management::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_management::REFRESH_TYPE_NONE;
+ if (HasEventListeners(api::processes::OnCreated::kEventName) ||
+ HasEventListeners(api::processes::OnUnresponsive::kEventName)) {
+ refresh_types |= GetRefreshTypesFlagOnlyEssentialData();
+ }
+
+ if (HasEventListeners(api::processes::OnUpdated::kEventName))
+ refresh_types |= GetRefreshTypesForProcessOptionalData();
+
+ if (HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName))
+ refresh_types |= task_management::REFRESH_TYPE_MEMORY;
+
+ SetRefreshTypesFlags(refresh_types);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ProcessesAPI:
+////////////////////////////////////////////////////////////////////////////////
+
ProcessesAPI::ProcessesAPI(content::BrowserContext* context)
: browser_context_(context) {
EventRouter* event_router = EventRouter::Get(browser_context_);
@@ -449,19 +397,13 @@ ProcessesAPI::ProcessesAPI(content::BrowserContext* context)
}
ProcessesAPI::~ProcessesAPI() {
+ // This object has already been unregistered as an observer in Shutdown().
}
-void ProcessesAPI::Shutdown() {
- EventRouter::Get(browser_context_)->UnregisterObserver(this);
-}
-
-static base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI> >
- g_factory = LAZY_INSTANCE_INITIALIZER;
-
// static
BrowserContextKeyedAPIFactory<ProcessesAPI>*
ProcessesAPI::GetFactoryInstance() {
- return g_factory.Pointer();
+ return g_processes_api_factory.Pointer();
}
// static
@@ -469,292 +411,280 @@ ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) {
return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context);
}
-ProcessesEventRouter* ProcessesAPI::processes_event_router() {
- if (!processes_event_router_)
- processes_event_router_.reset(new ProcessesEventRouter(browser_context_));
- return processes_event_router_.get();
+void ProcessesAPI::Shutdown() {
+ EventRouter::Get(browser_context_)->UnregisterObserver(this);
}
void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) {
- // We lazily tell the TaskManager to start updating when listeners to the
- // processes.onUpdated or processes.onUpdatedWithMemory events arrive.
+ // 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 or processes.onUpdatedWithMemory event listener
- // is removed (or a process with one exits), then we let the extension API
- // know that it has one fewer listener.
+ // 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:
////////////////////////////////////////////////////////////////////////////////
-ProcessesGetProcessIdForTabFunction::ProcessesGetProcessIdForTabFunction()
- : tab_id_(-1) {
-}
-
ExtensionFunction::ResponseAction ProcessesGetProcessIdForTabFunction::Run() {
-#if defined(ENABLE_TASK_MANAGER)
+ // For this function, the task manager doesn't even need to be running.
scoped_ptr<api::processes::GetProcessIdForTab::Params> params(
- api::processes::GetProcessIdForTab::Params::Create(*args_));
+ api::processes::GetProcessIdForTab::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
- tab_id_ = params->tab_id;
-
- if (tab_id_ < 0) {
- return RespondNow(Error(errors::kInavlidArgument,
- base::IntToString(tab_id_)));
- }
-
- // Add a reference, which is balanced in GetProcessIdForTab to keep the object
- // around and allow for the callback to be invoked.
- AddRef();
-
- // If the task manager is already listening, just post a task to execute
- // which will invoke the callback once we have returned from this function.
- // Otherwise, wait for the notification that the task manager is done with
- // the data gathering.
- if (ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
- ->processes_event_router()
- ->is_task_manager_listening()) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(&ProcessesGetProcessIdForTabFunction::GetProcessIdForTab,
- this));
- } else {
- TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
- base::Bind(&ProcessesGetProcessIdForTabFunction::GetProcessIdForTab,
- this));
-
- ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
- ->processes_event_router()
- ->StartTaskManagerListening();
- }
- return RespondLater();
-#else
- return RespondNow(Error(errors::kExtensionNotSupported));
-#endif // defined(ENABLE_TASK_MANAGER)
-}
-
-void ProcessesGetProcessIdForTabFunction::GetProcessIdForTab() {
- content::WebContents* contents = NULL;
+ const int tab_id = params->tab_id;
+ content::WebContents* contents = nullptr;
int tab_index = -1;
if (!ExtensionTabUtil::GetTabById(
- tab_id_,
+ tab_id,
Profile::FromBrowserContext(browser_context()),
include_incognito(),
nullptr,
nullptr,
&contents,
&tab_index)) {
- Respond(Error(tabs_constants::kTabNotFoundError,
- base::IntToString(tab_id_)));
- } else {
- int process_id = contents->GetRenderProcessHost()->GetID();
- Respond(ArgumentList(
- api::processes::GetProcessIdForTab::Results::Create(process_id)));
+ return RespondNow(Error(tabs_constants::kTabNotFoundError,
+ base::IntToString(tab_id)));
}
- // Balance the AddRef in the Run.
- Release();
+ const int process_id = contents->GetRenderProcessHost()->GetID();
+ return RespondNow(ArgumentList(
+ api::processes::GetProcessIdForTab::Results::Create(process_id)));
}
////////////////////////////////////////////////////////////////////////////////
-// ProcessesTerminateFunction
+// ProcessesTerminateFunction:
////////////////////////////////////////////////////////////////////////////////
-ProcessesTerminateFunction::ProcessesTerminateFunction() : process_id_(-1) {
-}
-
ExtensionFunction::ResponseAction ProcessesTerminateFunction::Run() {
-#if defined(ENABLE_TASK_MANAGER)
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ // For this function, the task manager doesn't even need to be running.
scoped_ptr<api::processes::Terminate::Params> params(
api::processes::Terminate::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
- process_id_ = params->process_id;
-
- // Add a reference, which is balanced in TerminateProcess to keep the object
- // around and allow for the callback to be invoked.
- AddRef();
- // If the task manager is already listening, just post a task to execute
- // which will invoke the callback once we have returned from this function.
- // Otherwise, wait for the notification that the task manager is done with
- // the data gathering.
- if (ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
- ->processes_event_router()
- ->is_task_manager_listening()) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(&ProcessesTerminateFunction::TerminateProcess,
- this));
- } else {
- TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
- base::Bind(&ProcessesTerminateFunction::TerminateProcess, this));
-
- ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
- ->processes_event_router()
- ->StartTaskManagerListening();
+ child_process_host_id_ = params->process_id;
+ if (child_process_host_id_ < 0) {
+ return RespondNow(Error(errors::kInvalidArgument,
+ base::IntToString(child_process_host_id_)));
+ } else if (child_process_host_id_ == 0) {
+ // Cannot kill the browser process.
+ return RespondNow(Error(errors::kNotAllowedToTerminate,
+ base::IntToString(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->GetHandle()));
+
+ // 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.
+ content::BrowserThread::PostTaskAndReplyWithResult(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ProcessesTerminateFunction::GetProcessHandleOnIO,
+ this,
+ child_process_host_id_),
+ base::Bind(&ProcessesTerminateFunction::OnProcessHandleOnUI, this));
+
+ // Promise to respond later.
return RespondLater();
-#else
- return RespondNow(Error(errors::kExtensionNotSupported));
-#endif // defined(ENABLE_TASK_MANAGER)
-}
-
-void ProcessesTerminateFunction::TerminateProcess() {
-#if defined(ENABLE_TASK_MANAGER)
- TaskManagerModel* model = TaskManager::GetInstance()->model();
-
- bool found = false;
- for (int i = 0, count = model->ResourceCount(); i < count; ++i) {
- if (model->IsResourceFirstInGroup(i) &&
- process_id_ == model->GetUniqueChildProcessId(i)) {
- base::ProcessHandle process_handle = model->GetProcess(i);
- if (process_handle == base::GetCurrentProcessHandle()) {
- // Cannot kill the browser process.
- // TODO(kalman): Are there other sensitive processes?
- Respond(Error(errors::kNotAllowedToTerminate,
- base::IntToString(process_id_)));
- } else {
- base::Process process =
- base::Process::DeprecatedGetProcessFromHandle(process_handle);
- bool did_terminate =
- process.Terminate(content::RESULT_CODE_KILLED, true);
- if (did_terminate)
- UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
-
- Respond(ArgumentList(
- api::processes::Terminate::Results::Create(did_terminate)));
- }
- found = true;
- break;
- }
+}
+
+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().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::IntToString(child_process_host_id_));
}
- if (!found)
- Respond(Error(errors::kProcessNotFound, base::IntToString(process_id_)));
+ if (handle == base::GetCurrentProcessHandle()) {
+ // Cannot kill the browser process.
+ return Error(errors::kNotAllowedToTerminate,
+ base::IntToString(child_process_host_id_));
+ }
- // Balance the AddRef in the Run.
- Release();
-#endif // defined(ENABLE_TASK_MANAGER)
+ base::Process process = base::Process::Open(base::GetProcId(handle));
+ if (!process.IsValid()) {
+ return Error(errors::kProcessNotFound,
+ base::IntToString(child_process_host_id_));
+ }
+
+ const bool did_terminate =
+ process.Terminate(content::RESULT_CODE_KILLED, true /* wait */);
+ if (did_terminate)
+ UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
+
+ return ArgumentList(
+ api::processes::Terminate::Results::Create(did_terminate));
}
////////////////////////////////////////////////////////////////////////////////
-// ProcessesGetProcessInfoFunction
+// ProcessesGetProcessInfoFunction:
////////////////////////////////////////////////////////////////////////////////
ProcessesGetProcessInfoFunction::ProcessesGetProcessInfoFunction()
-#if defined(ENABLE_TASK_MANAGER)
- : memory_(false)
-#endif
- {
+ : task_management::TaskManagerObserver(
+ base::TimeDelta::FromSeconds(1),
+ GetRefreshTypesFlagOnlyEssentialData()) {
}
ExtensionFunction::ResponseAction ProcessesGetProcessInfoFunction::Run() {
-#if defined(ENABLE_TASK_MANAGER)
scoped_ptr<api::processes::GetProcessInfo::Params> params(
api::processes::GetProcessInfo::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
if (params->process_ids.as_integer)
- process_ids_.push_back(*params->process_ids.as_integer);
+ process_host_ids_.push_back(*params->process_ids.as_integer);
else
- process_ids_.swap(*params->process_ids.as_integers);
+ process_host_ids_.swap(*params->process_ids.as_integers);
- memory_ = params->include_memory;
+ include_memory_ = params->include_memory;
+ if (include_memory_)
+ AddRefreshType(task_management::REFRESH_TYPE_MEMORY);
- // Add a reference, which is balanced in GatherProcessInfo to keep the object
- // around and allow for the callback to be invoked.
+ // Keep this object alive until the first of either OnTasksRefreshed() or
+ // OnTasksRefreshedWithBackgroundCalculations() is received depending on
+ // |include_memory_|.
AddRef();
- // If the task manager is already listening, just post a task to execute
- // which will invoke the callback once we have returned from this function.
- // Otherwise, wait for the notification that the task manager is done with
- // the data gathering.
- if (ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
- ->processes_event_router()
- ->is_task_manager_listening()) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(&ProcessesGetProcessInfoFunction::GatherProcessInfo, this));
- } else {
- TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
- base::Bind(&ProcessesGetProcessInfoFunction::GatherProcessInfo, this));
-
- ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
- ->processes_event_router()
- ->StartTaskManagerListening();
- }
+ // The task manager needs to be enabled for this function.
+ // Start observing the task manager and wait for the next refresh event.
+ task_management::TaskManagerInterface::GetTaskManager()->AddObserver(this);
return RespondLater();
-#else
- return RespondNow(Error(errors::kExtensionNotSupported));
-#endif // defined(ENABLE_TASK_MANAGER)
}
-ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() {
+void ProcessesGetProcessInfoFunction::OnTasksRefreshed(
+ const task_management::TaskIdList& task_ids) {
+ // Memory is background calculated and will be ready when
+ // OnTasksRefreshedWithBackgroundCalculations() is invoked.
+ if (include_memory_)
+ return;
+
+ GatherDataAndRespond(task_ids);
}
-void ProcessesGetProcessInfoFunction::GatherProcessInfo() {
-#if defined(ENABLE_TASK_MANAGER)
- TaskManagerModel* model = TaskManager::GetInstance()->model();
- api::processes::GetProcessInfo::Results::Processes processes;
+void
+ProcessesGetProcessInfoFunction::OnTasksRefreshedWithBackgroundCalculations(
+ const task_management::TaskIdList& task_ids) {
+ if (!include_memory_)
+ return;
+ GatherDataAndRespond(task_ids);
+}
+
+ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() {}
+
+void ProcessesGetProcessInfoFunction::GatherDataAndRespond(
+ const task_management::TaskIdList& task_ids) {
// If there are no process IDs specified, it means we need to return all of
// the ones we know of.
- if (process_ids_.size() == 0) {
- int resources = model->ResourceCount();
- for (int i = 0; i < resources; ++i) {
- if (model->IsResourceFirstInGroup(i)) {
- int id = model->GetUniqueChildProcessId(i);
- api::processes::Process process;
- FillProcessData(id, model, i, false, &process);
- if (memory_)
- AddMemoryDetails(model, i, &process);
- processes.additional_properties.Set(base::IntToString(id),
- process.ToValue());
- }
- }
- } else {
- int resources = model->ResourceCount();
- for (int i = 0; i < resources; ++i) {
- if (model->IsResourceFirstInGroup(i)) {
- int id = model->GetUniqueChildProcessId(i);
- std::vector<int>::iterator proc_id = std::find(process_ids_.begin(),
- process_ids_.end(), id);
- if (proc_id != process_ids_.end()) {
- api::processes::Process process;
- FillProcessData(id, model, i, false, &process);
- if (memory_)
- AddMemoryDetails(model, i, &process);
- processes.additional_properties.Set(base::IntToString(id),
- process.ToValue());
-
- process_ids_.erase(proc_id);
- if (process_ids_.size() == 0)
- break;
- }
- }
+ 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);
}
- // If not all processes were found, log them to the extension's console to
- // help the developer, but don't fail the API call.
- for (int pid : process_ids_) {
- WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR,
- ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
- base::IntToString(pid)));
+
+ 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 private memory usage to the process data.
+ const int64_t private_memory =
+ observed_task_manager()->GetPrivateMemoryUsage(task_id);
+ process.private_memory.reset(new double(static_cast<double>(
+ private_memory)));
}
+
+ // Store each process indexed by the string version of its
+ // ChildProcessHost ID.
+ processes.additional_properties.Set(
+ base::IntToString(child_process_host_id),
+ process.ToValue());
+ }
+
+ // Report the invalid host ids sent in the arguments.
+ for (const auto& host_id : process_host_ids_) {
+ WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR,
+ ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
+ base::IntToString(host_id)));
}
// Send the response.
Respond(ArgumentList(
api::processes::GetProcessInfo::Results::Create(processes)));
- // Balance the AddRef in the Run.
+ // Stop observing the task manager, and balance the AddRef() in Run().
+ task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(this);
Release();
-#endif // defined(ENABLE_TASK_MANAGER)
}
} // namespace extensions
« no previous file with comments | « chrome/browser/extensions/api/processes/processes_api.h ('k') | chrome/browser/extensions/api/processes/processes_apitest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698