Commit fe6fbee6 authored by Erik Chen's avatar Erik Chen Committed by Commit Bot

OOP HP: The profiling service exposes a list of profiled processes.

This CL adds a new method of the profiling_service mojom interface which
asynchronously returns the list of profiled processes. This is used in two places:
  * By the chrome://memory-internals to determine whether a process is being
  profiled. This also allows chrome://memory-internals to expose a "start
  profiling" button for non-profiled processes.
  * By the background profiling triggers, since traces should only be triggered
  if a profiled process exceeds the thresholds.

Bug: 
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I44da48eb1203b82f9cef8907fc7c2ab0fdcfdfce
Reviewed-on: https://chromium-review.googlesource.com/809505
Commit-Queue: Erik Chen <erikchen@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarAlbert J. Wong <ajwong@chromium.org>
Reviewed-by: default avatarBrett Wilson <brettw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#522037}
parent 2a5efd62
...@@ -74,10 +74,11 @@ void BackgroundProfilingTriggers::StartTimer() { ...@@ -74,10 +74,11 @@ void BackgroundProfilingTriggers::StartTimer() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// Register a repeating timer to check memory usage periodically. // Register a repeating timer to check memory usage periodically.
timer_.Start( timer_.Start(FROM_HERE,
FROM_HERE, base::TimeDelta::FromHours(kRepeatingCheckMemoryDelayInHours), base::TimeDelta::FromHours(kRepeatingCheckMemoryDelayInHours),
base::Bind(&BackgroundProfilingTriggers::PerformMemoryUsageChecks, base::BindRepeating(
weak_ptr_factory_.GetWeakPtr())); &BackgroundProfilingTriggers::PerformMemoryUsageChecks,
weak_ptr_factory_.GetWeakPtr()));
} }
bool BackgroundProfilingTriggers::IsAllowedToUpload() const { bool BackgroundProfilingTriggers::IsAllowedToUpload() const {
...@@ -126,13 +127,20 @@ void BackgroundProfilingTriggers::PerformMemoryUsageChecks() { ...@@ -126,13 +127,20 @@ void BackgroundProfilingTriggers::PerformMemoryUsageChecks() {
return; return;
} }
memory_instrumentation::MemoryInstrumentation::GetInstance() auto callback = base::BindOnce(
->RequestGlobalDump( [](base::WeakPtr<BackgroundProfilingTriggers> weak_ptr,
base::Bind(&BackgroundProfilingTriggers::OnReceivedMemoryDump, std::vector<base::ProcessId> result) {
weak_ptr_factory_.GetWeakPtr())); memory_instrumentation::MemoryInstrumentation::GetInstance()
->RequestGlobalDump(
base::Bind(&BackgroundProfilingTriggers::OnReceivedMemoryDump,
std::move(weak_ptr), std::move(result)));
},
weak_ptr_factory_.GetWeakPtr());
host_->GetProfiledPids(std::move(callback));
} }
void BackgroundProfilingTriggers::OnReceivedMemoryDump( void BackgroundProfilingTriggers::OnReceivedMemoryDump(
std::vector<base::ProcessId> profiled_pids,
bool success, bool success,
memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) { memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
...@@ -143,6 +151,9 @@ void BackgroundProfilingTriggers::OnReceivedMemoryDump( ...@@ -143,6 +151,9 @@ void BackgroundProfilingTriggers::OnReceivedMemoryDump(
bool should_send_report = false; bool should_send_report = false;
for (const auto& proc : dump->process_dumps) { for (const auto& proc : dump->process_dumps) {
if (std::find(profiled_pids.begin(), profiled_pids.end(), proc->pid) ==
profiled_pids.end())
continue;
if (IsOverTriggerThreshold(GetContentProcessType(proc->process_type), if (IsOverTriggerThreshold(GetContentProcessType(proc->process_type),
proc->os_dump->private_footprint_kb)) { proc->os_dump->private_footprint_kb)) {
should_send_report = true; should_send_report = true;
...@@ -156,12 +167,12 @@ void BackgroundProfilingTriggers::OnReceivedMemoryDump( ...@@ -156,12 +167,12 @@ void BackgroundProfilingTriggers::OnReceivedMemoryDump(
// If a report was sent, throttle the memory data collection rate to // If a report was sent, throttle the memory data collection rate to
// kThrottledReportRepeatingCheckMemoryDelayInHours to avoid sending too // kThrottledReportRepeatingCheckMemoryDelayInHours to avoid sending too
// many reports from a known problematic client. // many reports from a known problematic client.
timer_.Start( timer_.Start(FROM_HERE,
FROM_HERE, base::TimeDelta::FromHours(
base::TimeDelta::FromHours( kThrottledReportRepeatingCheckMemoryDelayInHours),
kThrottledReportRepeatingCheckMemoryDelayInHours), base::BindRepeating(
base::Bind(&BackgroundProfilingTriggers::PerformMemoryUsageChecks, &BackgroundProfilingTriggers::PerformMemoryUsageChecks,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
} }
......
...@@ -53,6 +53,7 @@ class BackgroundProfilingTriggers { ...@@ -53,6 +53,7 @@ class BackgroundProfilingTriggers {
// checks on memory usage and trigger a memory report with // checks on memory usage and trigger a memory report with
// |TriggerMemoryReportForProcess| if needed. // |TriggerMemoryReportForProcess| if needed.
void OnReceivedMemoryDump( void OnReceivedMemoryDump(
std::vector<base::ProcessId> profiled_pids,
bool success, bool success,
memory_instrumentation::mojom::GlobalMemoryDumpPtr ptr); memory_instrumentation::mojom::GlobalMemoryDumpPtr ptr);
......
...@@ -89,6 +89,8 @@ class BackgroundProfilingTriggersTest : public testing::Test { ...@@ -89,6 +89,8 @@ class BackgroundProfilingTriggersTest : public testing::Test {
ASSERT_TRUE(testing_profile_manager_.SetUp()); ASSERT_TRUE(testing_profile_manager_.SetUp());
ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting( ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(
&is_metrics_enabled_); &is_metrics_enabled_);
for (base::ProcessId i = 1; i <= 10; ++i)
profiled_pids_.push_back(i);
} }
void TearDown() override { void TearDown() override {
...@@ -105,6 +107,7 @@ class BackgroundProfilingTriggersTest : public testing::Test { ...@@ -105,6 +107,7 @@ class BackgroundProfilingTriggersTest : public testing::Test {
ProfilingProcessHost host_; ProfilingProcessHost host_;
FakeBackgroundProfilingTriggers triggers_; FakeBackgroundProfilingTriggers triggers_;
std::vector<base::ProcessId> profiled_pids_;
bool is_metrics_enabled_; bool is_metrics_enabled_;
}; };
...@@ -117,35 +120,36 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_EmptyCases) { ...@@ -117,35 +120,36 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_EmptyCases) {
GlobalMemoryDumpPtr dump_empty( GlobalMemoryDumpPtr dump_empty(
memory_instrumentation::mojom::GlobalMemoryDump::New()); memory_instrumentation::mojom::GlobalMemoryDump::New());
triggers_.OnReceivedMemoryDump(true, std::move(dump_empty)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump_empty));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
triggers_.Reset(); triggers_.Reset();
GlobalMemoryDumpPtr dump_browser( GlobalMemoryDumpPtr dump_browser(
memory_instrumentation::mojom::GlobalMemoryDump::New()); memory_instrumentation::mojom::GlobalMemoryDump::New());
PopulateMetrics(&dump_browser, 1, ProcessType::BROWSER, 1, 1, 1); PopulateMetrics(&dump_browser, 1, ProcessType::BROWSER, 1, 1, 1);
triggers_.OnReceivedMemoryDump(true, std::move(dump_browser)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump_browser));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
triggers_.Reset(); triggers_.Reset();
GlobalMemoryDumpPtr dump_gpu( GlobalMemoryDumpPtr dump_gpu(
memory_instrumentation::mojom::GlobalMemoryDump::New()); memory_instrumentation::mojom::GlobalMemoryDump::New());
PopulateMetrics(&dump_gpu, 1, ProcessType::GPU, 1, 1, 1); PopulateMetrics(&dump_gpu, 1, ProcessType::GPU, 1, 1, 1);
triggers_.OnReceivedMemoryDump(true, std::move(dump_gpu)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump_gpu));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
triggers_.Reset(); triggers_.Reset();
GlobalMemoryDumpPtr dump_renderer( GlobalMemoryDumpPtr dump_renderer(
memory_instrumentation::mojom::GlobalMemoryDump::New()); memory_instrumentation::mojom::GlobalMemoryDump::New());
PopulateMetrics(&dump_renderer, 1, ProcessType::RENDERER, 1, 1, 1); PopulateMetrics(&dump_renderer, 1, ProcessType::RENDERER, 1, 1, 1);
triggers_.OnReceivedMemoryDump(true, std::move(dump_renderer)); triggers_.OnReceivedMemoryDump(profiled_pids_, true,
std::move(dump_renderer));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
triggers_.Reset(); triggers_.Reset();
GlobalMemoryDumpPtr dump_other( GlobalMemoryDumpPtr dump_other(
memory_instrumentation::mojom::GlobalMemoryDump::New()); memory_instrumentation::mojom::GlobalMemoryDump::New());
PopulateMetrics(&dump_other, 1, ProcessType::OTHER, 1, 1, 1); PopulateMetrics(&dump_other, 1, ProcessType::OTHER, 1, 1, 1);
triggers_.OnReceivedMemoryDump(true, std::move(dump_other)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump_other));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
triggers_.Reset(); triggers_.Reset();
} }
...@@ -168,7 +172,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeNone) { ...@@ -168,7 +172,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeNone) {
PopulateMetrics(&dump, 6, ProcessType::RENDERER, kProcessMallocTriggerKb, PopulateMetrics(&dump, 6, ProcessType::RENDERER, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
} }
...@@ -183,7 +187,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeAll) { ...@@ -183,7 +187,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeAll) {
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
// Small processes and OTHER type processes don't trigger. // Small processes and OTHER type processes don't trigger.
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
// Ensure each process type triggers. // Ensure each process type triggers.
...@@ -191,21 +195,39 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeAll) { ...@@ -191,21 +195,39 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeAll) {
dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); dump = memory_instrumentation::mojom::GlobalMemoryDump::New();
PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb, PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered()); EXPECT_TRUE(triggers_.WasReportTriggered());
triggers_.Reset(); triggers_.Reset();
dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); dump = memory_instrumentation::mojom::GlobalMemoryDump::New();
PopulateMetrics(&dump, 1, ProcessType::RENDERER, kProcessMallocTriggerKb, PopulateMetrics(&dump, 1, ProcessType::RENDERER, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered()); EXPECT_TRUE(triggers_.WasReportTriggered());
triggers_.Reset(); triggers_.Reset();
dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); dump = memory_instrumentation::mojom::GlobalMemoryDump::New();
PopulateMetrics(&dump, 1, ProcessType::GPU, kProcessMallocTriggerKb, PopulateMetrics(&dump, 1, ProcessType::GPU, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered());
}
// Non-profiled processes don't trigger.
TEST_F(BackgroundProfilingTriggersTest, OnlyProfiledProcessesTrigger) {
SetMode(ProfilingProcessHost::Mode::kAll);
GlobalMemoryDumpPtr dump(
memory_instrumentation::mojom::GlobalMemoryDump::New());
PopulateMetrics(&dump, 101, ProcessType::BROWSER, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_FALSE(triggers_.WasReportTriggered());
dump = memory_instrumentation::mojom::GlobalMemoryDump::New();
PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered()); EXPECT_TRUE(triggers_.WasReportTriggered());
} }
...@@ -224,7 +246,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeMinimal) { ...@@ -224,7 +246,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeMinimal) {
// Ensure Browser and GPU processes under threshold do not trigger. // Ensure Browser and GPU processes under threshold do not trigger.
// Ensure other process types ignored. // Ensure other process types ignored.
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
// Ensure BROWSER and GPU types trigger. // Ensure BROWSER and GPU types trigger.
...@@ -233,7 +255,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeMinimal) { ...@@ -233,7 +255,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeMinimal) {
PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb, PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
PopulateMetrics(&dump, 2, ProcessType::GPU, 1, 1, 1); PopulateMetrics(&dump, 2, ProcessType::GPU, 1, 1, 1);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered()); EXPECT_TRUE(triggers_.WasReportTriggered());
triggers_.Reset(); triggers_.Reset();
...@@ -241,7 +263,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeMinimal) { ...@@ -241,7 +263,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeMinimal) {
PopulateMetrics(&dump, 1, ProcessType::GPU, kProcessMallocTriggerKb, PopulateMetrics(&dump, 1, ProcessType::GPU, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
PopulateMetrics(&dump, 2, ProcessType::BROWSER, 1, 1, 1); PopulateMetrics(&dump, 2, ProcessType::BROWSER, 1, 1, 1);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered()); EXPECT_TRUE(triggers_.WasReportTriggered());
} }
...@@ -261,7 +283,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeBrowser) { ...@@ -261,7 +283,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeBrowser) {
// Ensure Browser processes under threshold do not trigger. // Ensure Browser processes under threshold do not trigger.
// Ensure other process types ignored. // Ensure other process types ignored.
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
// Ensure BROWSER type triggers. // Ensure BROWSER type triggers.
...@@ -269,7 +291,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeBrowser) { ...@@ -269,7 +291,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeBrowser) {
dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); dump = memory_instrumentation::mojom::GlobalMemoryDump::New();
PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb, PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered()); EXPECT_TRUE(triggers_.WasReportTriggered());
} }
...@@ -289,7 +311,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeGpu) { ...@@ -289,7 +311,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeGpu) {
// Ensure GPU processes under threshold do not trigger. // Ensure GPU processes under threshold do not trigger.
// Ensure other process types ignored. // Ensure other process types ignored.
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
// Ensure GPU type triggers. // Ensure GPU type triggers.
...@@ -297,7 +319,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeGpu) { ...@@ -297,7 +319,7 @@ TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ModeGpu) {
dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); dump = memory_instrumentation::mojom::GlobalMemoryDump::New();
PopulateMetrics(&dump, 1, ProcessType::GPU, kProcessMallocTriggerKb, PopulateMetrics(&dump, 1, ProcessType::GPU, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered()); EXPECT_TRUE(triggers_.WasReportTriggered());
} }
...@@ -318,7 +340,7 @@ TEST_F(BackgroundProfilingTriggersTest, ...@@ -318,7 +340,7 @@ TEST_F(BackgroundProfilingTriggersTest,
// Ensure RENDERER processes under threshold do not trigger. // Ensure RENDERER processes under threshold do not trigger.
// Ensure other process types ignored. // Ensure other process types ignored.
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_FALSE(triggers_.WasReportTriggered()); EXPECT_FALSE(triggers_.WasReportTriggered());
// Ensure RENDERER type triggers. // Ensure RENDERER type triggers.
...@@ -326,7 +348,7 @@ TEST_F(BackgroundProfilingTriggersTest, ...@@ -326,7 +348,7 @@ TEST_F(BackgroundProfilingTriggersTest,
dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); dump = memory_instrumentation::mojom::GlobalMemoryDump::New();
PopulateMetrics(&dump, 1, ProcessType::RENDERER, kProcessMallocTriggerKb, PopulateMetrics(&dump, 1, ProcessType::RENDERER, kProcessMallocTriggerKb,
kProcessMallocTriggerKb, kProcessMallocTriggerKb); kProcessMallocTriggerKb, kProcessMallocTriggerKb);
triggers_.OnReceivedMemoryDump(true, std::move(dump)); triggers_.OnReceivedMemoryDump(profiled_pids_, true, std::move(dump));
EXPECT_TRUE(triggers_.WasReportTriggered()); EXPECT_TRUE(triggers_.WasReportTriggered());
} }
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "chrome/common/profiling/profiling_constants.h" #include "chrome/common/profiling/profiling_constants.h"
#include "components/version_info/version_info.h" #include "components/version_info/version_info.h"
#include "content/public/browser/browser_child_process_host.h" #include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h" #include "content/public/browser/notification_types.h"
...@@ -192,7 +193,6 @@ ProfilingProcessHost::ProfilingProcessHost() ...@@ -192,7 +193,6 @@ ProfilingProcessHost::ProfilingProcessHost()
: is_registered_(false), : is_registered_(false),
background_triggers_(this), background_triggers_(this),
mode_(Mode::kNone), mode_(Mode::kNone),
profiled_renderer_(nullptr),
always_sample_for_tests_(false) {} always_sample_for_tests_(false) {}
ProfilingProcessHost::~ProfilingProcessHost() { ProfilingProcessHost::~ProfilingProcessHost() {
...@@ -229,17 +229,7 @@ void ProfilingProcessHost::BrowserChildProcessLaunchedAndConnected( ...@@ -229,17 +229,7 @@ void ProfilingProcessHost::BrowserChildProcessLaunchedAndConnected(
return; return;
} }
profiling::mojom::ProcessType process_type = StartProfilingNonRendererChild(data);
(data.process_type == content::ProcessType::PROCESS_TYPE_GPU)
? profiling::mojom::ProcessType::GPU
: profiling::mojom::ProcessType::OTHER;
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
->PostTask(
FROM_HERE,
base::BindOnce(&ProfilingProcessHost::StartProfilingNonRendererChild,
base::Unretained(this), data.id,
base::GetProcId(data.handle), process_type));
} }
void ProfilingProcessHost::Observe( void ProfilingProcessHost::Observe(
...@@ -255,32 +245,14 @@ void ProfilingProcessHost::Observe( ...@@ -255,32 +245,14 @@ void ProfilingProcessHost::Observe(
// the RenderProcessHost's lifetime is ending. Ideally, we'd only listen to // the RenderProcessHost's lifetime is ending. Ideally, we'd only listen to
// the former, but if the RenderProcessHost is destroyed before the // the former, but if the RenderProcessHost is destroyed before the
// RenderProcess, then the former is never sent. // RenderProcess, then the former is never sent.
if (host == profiled_renderer_ && if ((type == content::NOTIFICATION_RENDERER_PROCESS_TERMINATED ||
(type == content::NOTIFICATION_RENDERER_PROCESS_TERMINATED ||
type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED)) { type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED)) {
// |profiled_renderer_| is only ever set in kRendererSampling mode. This profiled_renderers_.erase(host);
// code is a deadstore otherwise so it's safe but having a DCHECK makes the
// intent clear.
DCHECK_EQ(mode(), Mode::kRendererSampling);
profiled_renderer_ = nullptr;
} }
if (type == content::NOTIFICATION_RENDERER_PROCESS_CREATED && if (type == content::NOTIFICATION_RENDERER_PROCESS_CREATED &&
ShouldProfileNewRenderer(host)) { ShouldProfileNewRenderer(host)) {
// In kRendererSampling mode, store the RPH to ensure only one renderer is StartProfilingRenderer(host);
// ever sampled inside a chrome instance.
if (mode() == Mode::kRendererSampling) {
profiled_renderer_ = host;
}
// Tell the child process to start profiling.
ProfilingClientBinder client(host);
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
->PostTask(
FROM_HERE,
base::BindOnce(&ProfilingProcessHost::AddClientToProfilingService,
base::Unretained(this), client.take(),
base::GetProcId(host->GetHandle()),
profiling::mojom::ProcessType::RENDERER));
} }
} }
...@@ -567,6 +539,85 @@ void ProfilingProcessHost::RequestTraceWithHeapDump( ...@@ -567,6 +539,85 @@ void ProfilingProcessHost::RequestTraceWithHeapDump(
} }
} }
void ProfilingProcessHost::GetProfiledPids(GetProfiledPidsCallback callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
->PostTask(
FROM_HERE,
base::BindOnce(&ProfilingProcessHost::GetProfiledPidsOnIOThread,
base::Unretained(this), std::move(callback)));
}
void ProfilingProcessHost::GetProfiledPidsOnIOThread(
GetProfiledPidsCallback callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
if (!profiling_service_.is_bound()) {
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI)
->PostTask(FROM_HERE, base::BindOnce(std::move(callback),
std::vector<base::ProcessId>()));
return;
}
auto post_result_to_ui_thread = base::BindOnce(
[](GetProfiledPidsCallback callback,
const std::vector<base::ProcessId>& result) {
content::BrowserThread::GetTaskRunnerForThread(
content::BrowserThread::UI)
->PostTask(FROM_HERE, base::BindOnce(std::move(callback), result));
},
std::move(callback));
profiling_service_->GetProfiledPids(std::move(post_result_to_ui_thread));
}
void ProfilingProcessHost::StartProfiling(base::ProcessId pid) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// The RenderProcessHost iterator must be used on the UI thread.
// The BrowserChildProcessHostIterator iterator must be used on the IO thread.
for (auto iter = content::RenderProcessHost::AllHostsIterator();
!iter.IsAtEnd(); iter.Advance()) {
if (pid == base::GetProcId(iter.GetCurrentValue()->GetHandle())) {
StartProfilingRenderer(iter.GetCurrentValue());
return;
}
}
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
->PostTask(
FROM_HERE,
base::BindOnce(&ProfilingProcessHost::StartProfilingPidOnIOThread,
base::Unretained(this), pid));
}
void ProfilingProcessHost::StartProfilingPidOnIOThread(base::ProcessId pid) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
// Check if the request is for the current process.
if (pid == base::GetCurrentProcId()) {
ProfilingClientBinder client(connector_.get());
AddClientToProfilingService(client.take(), base::Process::Current().Pid(),
profiling::mojom::ProcessType::BROWSER);
return;
}
for (content::BrowserChildProcessHostIterator browser_child_iter;
!browser_child_iter.Done(); ++browser_child_iter) {
const content::ChildProcessData& data = browser_child_iter.GetData();
if (base::GetProcId(data.handle) == pid) {
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::UI)
->PostTask(FROM_HERE,
base::BindOnce(
&ProfilingProcessHost::StartProfilingNonRendererChild,
base::Unretained(this), data));
return;
}
}
DLOG(WARNING)
<< "Attempt to start profiling failed as no process was found with pid: "
<< pid;
}
void ProfilingProcessHost::MakeConnector( void ProfilingProcessHost::MakeConnector(
content::ServiceManagerConnection* connection) { content::ServiceManagerConnection* connection) {
connector_ = connection->GetConnector()->Clone(); connector_ = connection->GetConnector()->Clone();
...@@ -685,7 +736,7 @@ bool ProfilingProcessHost::ShouldProfileNewRenderer( ...@@ -685,7 +736,7 @@ bool ProfilingProcessHost::ShouldProfileNewRenderer(
if (mode() == Mode::kAll) { if (mode() == Mode::kAll) {
return true; return true;
} else if (mode() == Mode::kRendererSampling && !profiled_renderer_) { } else if (mode() == Mode::kRendererSampling && profiled_renderers_.empty()) {
if (always_sample_for_tests_) { if (always_sample_for_tests_) {
return true; return true;
} }
...@@ -698,6 +749,23 @@ bool ProfilingProcessHost::ShouldProfileNewRenderer( ...@@ -698,6 +749,23 @@ bool ProfilingProcessHost::ShouldProfileNewRenderer(
} }
void ProfilingProcessHost::StartProfilingNonRendererChild( void ProfilingProcessHost::StartProfilingNonRendererChild(
const content::ChildProcessData& data) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
profiling::mojom::ProcessType process_type =
(data.process_type == content::ProcessType::PROCESS_TYPE_GPU)
? profiling::mojom::ProcessType::GPU
: profiling::mojom::ProcessType::OTHER;
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
->PostTask(
FROM_HERE,
base::BindOnce(
&ProfilingProcessHost::StartProfilingNonRendererChildOnIOThread,
base::Unretained(this), data.id, base::GetProcId(data.handle),
process_type));
}
void ProfilingProcessHost::StartProfilingNonRendererChildOnIOThread(
int child_process_id, int child_process_id,
base::ProcessId proc_id, base::ProcessId proc_id,
profiling::mojom::ProcessType process_type) { profiling::mojom::ProcessType process_type) {
...@@ -713,6 +781,22 @@ void ProfilingProcessHost::StartProfilingNonRendererChild( ...@@ -713,6 +781,22 @@ void ProfilingProcessHost::StartProfilingNonRendererChild(
AddClientToProfilingService(client.take(), proc_id, process_type); AddClientToProfilingService(client.take(), proc_id, process_type);
} }
void ProfilingProcessHost::StartProfilingRenderer(
content::RenderProcessHost* host) {
profiled_renderers_.insert(host);
// Tell the child process to start profiling.
ProfilingClientBinder client(host);
content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
->PostTask(
FROM_HERE,
base::BindOnce(&ProfilingProcessHost::AddClientToProfilingService,
base::Unretained(this), client.take(),
base::GetProcId(host->GetHandle()),
profiling::mojom::ProcessType::RENDERER));
}
void ProfilingProcessHost::SetRendererSamplingAlwaysProfileForTest() { void ProfilingProcessHost::SetRendererSamplingAlwaysProfileForTest() {
always_sample_for_tests_ = true; always_sample_for_tests_ = true;
} }
......
...@@ -137,6 +137,15 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver, ...@@ -137,6 +137,15 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver,
TraceFinishedCallback callback, TraceFinishedCallback callback,
bool stop_immediately_after_heap_dump_for_tests); bool stop_immediately_after_heap_dump_for_tests);
// Returns the pids of all profiled processes. The callback is posted on the
// UI thread.
using GetProfiledPidsCallback =
base::OnceCallback<void(std::vector<base::ProcessId> pids)>;
void GetProfiledPids(GetProfiledPidsCallback callback);
// Starts profiling the process with the given id.
void StartProfiling(base::ProcessId pid);
private: private:
friend struct base::DefaultSingletonTraits<ProfilingProcessHost>; friend struct base::DefaultSingletonTraits<ProfilingProcessHost>;
friend class BackgroundProfilingTriggersTest; friend class BackgroundProfilingTriggersTest;
...@@ -197,14 +206,20 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver, ...@@ -197,14 +206,20 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver,
bool ShouldProfileNewRenderer(content::RenderProcessHost* renderer) const; bool ShouldProfileNewRenderer(content::RenderProcessHost* renderer) const;
// Sets up the profiling connection for the given child process. // Sets up the profiling connection for the given child process.
void StartProfilingNonRendererChild( void StartProfilingNonRendererChild(const content::ChildProcessData&);
void StartProfilingNonRendererChildOnIOThread(
int child_process_id, int child_process_id,
base::ProcessId proc_id, base::ProcessId proc_id,
profiling::mojom::ProcessType process_type); profiling::mojom::ProcessType process_type);
void StartProfilingRenderer(content::RenderProcessHost* host);
// SetRenderer. // SetRenderer.
void SetRendererSamplingAlwaysProfileForTest(); void SetRendererSamplingAlwaysProfileForTest();
void GetProfiledPidsOnIOThread(GetProfiledPidsCallback callback);
void StartProfilingPidOnIOThread(base::ProcessId pid);
content::NotificationRegistrar registrar_; content::NotificationRegistrar registrar_;
std::unique_ptr<service_manager::Connector> connector_; std::unique_ptr<service_manager::Connector> connector_;
mojom::ProfilingServicePtr profiling_service_; mojom::ProfilingServicePtr profiling_service_;
...@@ -225,17 +240,21 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver, ...@@ -225,17 +240,21 @@ class ProfilingProcessHost : public content::BrowserChildProcessObserver,
// Whether or not the profiling host is started. // Whether or not the profiling host is started.
static bool has_started_; static bool has_started_;
// If in kRendererSampling mode, this is used to identify the currently // This is used to identify the currently profiled renderers. Elements should
// profiled renderer. If no renderer is being profiled, this is set to // only be accessed on the UI thread and their values should be considered
// nullptr. This variable shouild only be accessed on the UI thread and // opaque.
// the value should be considered opaque.
// //
// Semantically, the value must be something that identifies which // Semantically, the elements must be something that identifies which
// specific RenderProcess is being profiled. When the underlying RenderProcess // specific RenderProcess is being profiled. When the underlying RenderProcess
// goes away, this value needs to be reset to nullptr. The RenderProcessHost // goes away, the element must be removed. The RenderProcessHost
// pointer and the NOTIFICATION_RENDERER_PROCESS_CREATED notification can be // pointer and the NOTIFICATION_RENDERER_PROCESS_CREATED notification can be
// used to provide these semantics. // used to provide these semantics.
void* profiled_renderer_; //
// This variable represents renderers that have been instructed to start
// profiling - it does not reflect whether a renderer is currently still being
// profiled. That information is only known by the profiling service, and for
// simplicity, it's easier to just track this variable in this process.
std::unordered_set<void*> profiled_renderers_;
// For Tests. In kRendererSampling mode, overrides sampling to always profile // For Tests. In kRendererSampling mode, overrides sampling to always profile
// a renderer process if one is already not going. // a renderer process if one is already not going.
......
...@@ -16,6 +16,10 @@ function reportProcess(pid) { ...@@ -16,6 +16,10 @@ function reportProcess(pid) {
chrome.send('reportProcess', [pid]); chrome.send('reportProcess', [pid]);
} }
function startProfiling(pid) {
chrome.send('startProfiling', [pid]);
}
// celltype should either be "td" or "th". The contents of the |cols| will be // celltype should either be "td" or "th". The contents of the |cols| will be
// added as children of each table cell if they are non-null. // added as children of each table cell if they are non-null.
function addListRow(table, celltype, cols) { function addListRow(table, celltype, cols) {
...@@ -68,17 +72,25 @@ function returnProcessList(data) { ...@@ -68,17 +72,25 @@ function returnProcessList(data) {
null, document.createTextNode('Process ID'), document.createTextNode('Name') null, document.createTextNode('Process ID'), document.createTextNode('Name')
]); ]);
for (let proc of processes) { for (const proc of processes) {
let procId = proc[0]; const procId = proc[0];
let reportButton = document.createElement('button'); const procIdText = document.createTextNode(procId.toString());
reportButton.innerText = '\uD83D\uDC1E Report'; const description = document.createTextNode(proc[1]);
reportButton.onclick = () => reportProcess(procId); const profiled = proc[2];
let procIdText = document.createTextNode(procId.toString()); let button = null;
let description = document.createTextNode(proc[1]); if (profiled) {
button = document.createElement('button');
addListRow(table, 'td', [reportButton, procIdText, description]); button.innerText = '\uD83D\uDC1E Report';
button.onclick = () => reportProcess(procId);
} else {
button = document.createElement('button');
button.innerText = '\u2600 Start profiling';
button.onclick = () => startProfiling(procId);
}
addListRow(table, 'td', [button, procIdText, description]);
} }
proclist.appendChild(table); proclist.appendChild(table);
......
...@@ -131,12 +131,17 @@ class MemoryInternalsDOMHandler : public content::WebUIMessageHandler, ...@@ -131,12 +131,17 @@ class MemoryInternalsDOMHandler : public content::WebUIMessageHandler,
// Callback for the "reportProcess" message. // Callback for the "reportProcess" message.
void HandleReportProcess(const base::ListValue* args); void HandleReportProcess(const base::ListValue* args);
// Callback for the "startProfiling" message.
void HandleStartProfiling(const base::ListValue* args);
private: private:
// Hops to the IO thread to enumerate child processes, and back to the UI // Hops to the IO thread to enumerate child processes, and back to the UI
// thread to fill in the renderer processes. // thread to fill in the renderer processes.
static void GetChildProcessesOnIOThread( static void GetChildProcessesOnIOThread(
base::WeakPtr<MemoryInternalsDOMHandler> dom_handler); base::WeakPtr<MemoryInternalsDOMHandler> dom_handler);
void ReturnProcessListOnUIThread(std::vector<base::Value> children); void GetProfiledPids(std::vector<base::Value> children);
void ReturnProcessListOnUIThread(std::vector<base::Value> children,
std::vector<base::ProcessId> profiled_pids);
// SelectFileDialog::Listener implementation: // SelectFileDialog::Listener implementation:
void FileSelected(const base::FilePath& path, void FileSelected(const base::FilePath& path,
...@@ -177,6 +182,10 @@ void MemoryInternalsDOMHandler::RegisterMessages() { ...@@ -177,6 +182,10 @@ void MemoryInternalsDOMHandler::RegisterMessages() {
"reportProcess", "reportProcess",
base::BindRepeating(&MemoryInternalsDOMHandler::HandleReportProcess, base::BindRepeating(&MemoryInternalsDOMHandler::HandleReportProcess,
base::Unretained(this))); base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"startProfiling",
base::BindRepeating(&MemoryInternalsDOMHandler::HandleStartProfiling,
base::Unretained(this)));
} }
void MemoryInternalsDOMHandler::HandleRequestProcessList( void MemoryInternalsDOMHandler::HandleRequestProcessList(
...@@ -234,33 +243,48 @@ void MemoryInternalsDOMHandler::HandleReportProcess( ...@@ -234,33 +243,48 @@ void MemoryInternalsDOMHandler::HandleReportProcess(
"MEMLOG_MANUAL_TRIGGER"); "MEMLOG_MANUAL_TRIGGER");
} }
void MemoryInternalsDOMHandler::HandleStartProfiling(
const base::ListValue* args) {
if (!args->is_list() || args->GetList().size() != 1)
return;
ProfilingProcessHost::GetInstance()->StartProfiling(
args->GetList()[0].GetInt());
}
void MemoryInternalsDOMHandler::GetChildProcessesOnIOThread( void MemoryInternalsDOMHandler::GetChildProcessesOnIOThread(
base::WeakPtr<MemoryInternalsDOMHandler> dom_handler) { base::WeakPtr<MemoryInternalsDOMHandler> dom_handler) {
std::vector<base::Value> result; std::vector<base::Value> result;
if (ProfilingProcessHost::GetCurrentMode() != // The only non-renderer child process that currently supports out-of-process
ProfilingProcessHost::Mode::kNone) { // heap profiling is GPU.
// Add child processes (this does not include renderers). for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { // Note that ChildProcessData.id is a child ID and not an OS PID.
// Note that ChildProcessData.id is a child ID and not an OS PID. const content::ChildProcessData& data = iter.GetData();
const content::ChildProcessData& data = iter.GetData();
if (data.process_type == content::PROCESS_TYPE_GPU) {
if (ProfilingProcessHost::GetInstance()->ShouldProfileProcessType( result.push_back(MakeProcessInfo(base::GetProcId(data.handle),
data.process_type)) { GetChildDescription(data)));
result.push_back(MakeProcessInfo(base::GetProcId(data.handle),
GetChildDescription(data)));
}
} }
} }
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, content::BrowserThread::UI, FROM_HERE,
base::BindOnce(&MemoryInternalsDOMHandler::GetProfiledPids, dom_handler,
std::move(result)));
}
void MemoryInternalsDOMHandler::GetProfiledPids(
std::vector<base::Value> children) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
ProfilingProcessHost::GetInstance()->GetProfiledPids(
base::BindOnce(&MemoryInternalsDOMHandler::ReturnProcessListOnUIThread, base::BindOnce(&MemoryInternalsDOMHandler::ReturnProcessListOnUIThread,
dom_handler, std::move(result))); weak_factory_.GetWeakPtr(), std::move(children)));
} }
void MemoryInternalsDOMHandler::ReturnProcessListOnUIThread( void MemoryInternalsDOMHandler::ReturnProcessListOnUIThread(
std::vector<base::Value> children) { std::vector<base::Value> children,
std::vector<base::ProcessId> profiled_pids) {
// This function will be called with the child processes that are not // This function will be called with the child processes that are not
// renderers. It will fill in the browser and renderer processes on the UI // renderers. It will fill in the browser and renderer processes on the UI
// thread (RenderProcessHost is UI-thread only) and return the full list. // thread (RenderProcessHost is UI-thread only) and return the full list.
...@@ -268,26 +292,19 @@ void MemoryInternalsDOMHandler::ReturnProcessListOnUIThread( ...@@ -268,26 +292,19 @@ void MemoryInternalsDOMHandler::ReturnProcessListOnUIThread(
std::vector<base::Value>& process_list = process_list_value.GetList(); std::vector<base::Value>& process_list = process_list_value.GetList();
// Add browser process. // Add browser process.
if (ProfilingProcessHost::GetInstance()->ShouldProfileProcessType( process_list.push_back(MakeProcessInfo(base::GetCurrentProcId(), "Browser"));
content::ProcessType::PROCESS_TYPE_BROWSER)) {
process_list.push_back(
MakeProcessInfo(base::GetCurrentProcId(), "Browser"));
}
// Append renderer processes. // Append renderer processes.
if (ProfilingProcessHost::GetInstance()->ShouldProfileProcessType( auto iter = content::RenderProcessHost::AllHostsIterator();
content::ProcessType::PROCESS_TYPE_RENDERER)) { while (!iter.IsAtEnd()) {
auto iter = content::RenderProcessHost::AllHostsIterator(); base::ProcessHandle renderer_handle = iter.GetCurrentValue()->GetHandle();
while (!iter.IsAtEnd()) { base::ProcessId renderer_pid = base::GetProcId(renderer_handle);
base::ProcessHandle renderer_handle = iter.GetCurrentValue()->GetHandle(); if (renderer_pid != 0) {
base::ProcessId renderer_pid = base::GetProcId(renderer_handle); // TODO(brettw) make a better description of the process, maybe see
if (renderer_pid != 0) { // what TaskManager does to get the page title.
// TODO(brettw) make a better description of the process, maybe see process_list.push_back(MakeProcessInfo(renderer_pid, "Renderer"));
// what TaskManager does to get the page title.
process_list.push_back(MakeProcessInfo(renderer_pid, "Renderer"));
}
iter.Advance();
} }
iter.Advance();
} }
// Append all child processes collected on the IO thread. // Append all child processes collected on the IO thread.
...@@ -295,6 +312,21 @@ void MemoryInternalsDOMHandler::ReturnProcessListOnUIThread( ...@@ -295,6 +312,21 @@ void MemoryInternalsDOMHandler::ReturnProcessListOnUIThread(
std::make_move_iterator(std::begin(children)), std::make_move_iterator(std::begin(children)),
std::make_move_iterator(std::end(children))); std::make_move_iterator(std::end(children)));
// Sort profiled_pids to allow binary_search in the loop.
std::sort(profiled_pids.begin(), profiled_pids.end());
// Append whether each process is being profiled.
for (base::Value& value : process_list) {
std::vector<base::Value>& value_as_list = value.GetList();
DCHECK_EQ(value_as_list.size(), 2u);
base::ProcessId pid =
static_cast<base::ProcessId>(value_as_list[0].GetInt());
bool is_profiled =
std::binary_search(profiled_pids.begin(), profiled_pids.end(), pid);
value_as_list.push_back(base::Value(is_profiled));
}
// Pass the results in a dictionary. // Pass the results in a dictionary.
base::Value result(base::Value::Type::DICTIONARY); base::Value result(base::Value::Type::DICTIONARY);
result.SetKey("message", base::Value(GetMessageString())); result.SetKey("message", base::Value(GetMessageString()));
......
...@@ -20,7 +20,8 @@ namespace { ...@@ -20,7 +20,8 @@ namespace {
const int kTimeoutDurationMs = 10000; const int kTimeoutDurationMs = 10000;
} // namespace } // namespace
ProfilingClient::ProfilingClient() : binding_(this) {} ProfilingClient::ProfilingClient()
: binding_(this), started_profiling_(false) {}
ProfilingClient::~ProfilingClient() { ProfilingClient::~ProfilingClient() {
StopAllocatorShimDangerous(); StopAllocatorShimDangerous();
...@@ -49,6 +50,10 @@ void ProfilingClient::BindToInterface(mojom::ProfilingClientRequest request) { ...@@ -49,6 +50,10 @@ void ProfilingClient::BindToInterface(mojom::ProfilingClientRequest request) {
} }
void ProfilingClient::StartProfiling(mojo::ScopedHandle memlog_sender_pipe) { void ProfilingClient::StartProfiling(mojo::ScopedHandle memlog_sender_pipe) {
if (started_profiling_)
return;
started_profiling_ = true;
base::PlatformFile platform_file; base::PlatformFile platform_file;
CHECK_EQ(MOJO_RESULT_OK, mojo::UnwrapPlatformFile( CHECK_EQ(MOJO_RESULT_OK, mojo::UnwrapPlatformFile(
std::move(memlog_sender_pipe), &platform_file)); std::move(memlog_sender_pipe), &platform_file));
......
...@@ -38,6 +38,8 @@ class ProfilingClient : public mojom::ProfilingClient { ...@@ -38,6 +38,8 @@ class ProfilingClient : public mojom::ProfilingClient {
// The most recent client request is bound and kept alive. // The most recent client request is bound and kept alive.
mojo::Binding<mojom::ProfilingClient> binding_; mojo::Binding<mojom::ProfilingClient> binding_;
bool started_profiling_;
std::unique_ptr<MemlogSenderPipe> memlog_sender_pipe_; std::unique_ptr<MemlogSenderPipe> memlog_sender_pipe_;
}; };
......
...@@ -47,4 +47,7 @@ interface ProfilingService { ...@@ -47,4 +47,7 @@ interface ProfilingService {
// |buffers|. // |buffers|.
DumpProcessesForTracing() => DumpProcessesForTracing() =>
(array<SharedBufferWithSize> buffers); (array<SharedBufferWithSize> buffers);
// Returns the pids of all profiled processes.
GetProfiledPids() => (array<mojo.common.mojom.ProcessId> pids);
}; };
...@@ -113,8 +113,19 @@ void MemlogConnectionManager::OnNewConnection( ...@@ -113,8 +113,19 @@ void MemlogConnectionManager::OnNewConnection(
mojom::ProcessType process_type) { mojom::ProcessType process_type) {
base::AutoLock lock(connections_lock_); base::AutoLock lock(connections_lock_);
// Shouldn't be asked to profile a process more than once. // Attempting to start profiling on an already profiled processs should have
DCHECK(connections_.find(pid) == connections_.end()); // no effect.
if (connections_.find(pid) != connections_.end())
return;
// It's theoretically possible that we started profiling a process, the
// profiling was stopped [e.g. by hitting the 10-s timeout], and then we tried
// to start profiling again. The ProfilingClient will refuse to start again.
// But the MemlogConnectionManager will not be able to distinguish this
// never-started ProfilingClient from a brand new ProfilingClient that happens
// to share the same pid. This is a rare condition which should only happen
// when the user is attempting to manually start profiling for processes, so
// we ignore this edge case.
base::PlatformFile receiver_handle; base::PlatformFile receiver_handle;
CHECK_EQ(MOJO_RESULT_OK, mojo::UnwrapPlatformFile( CHECK_EQ(MOJO_RESULT_OK, mojo::UnwrapPlatformFile(
...@@ -151,6 +162,16 @@ void MemlogConnectionManager::OnNewConnection( ...@@ -151,6 +162,16 @@ void MemlogConnectionManager::OnNewConnection(
connections_[pid] = std::move(connection); // Transfers ownership. connections_[pid] = std::move(connection); // Transfers ownership.
} }
std::vector<base::ProcessId> MemlogConnectionManager::GetConnectionPids() {
base::AutoLock lock(connections_lock_);
std::vector<base::ProcessId> results;
results.reserve(connections_.size());
for (const auto& pair : connections_) {
results.push_back(pair.first);
}
return results;
}
void MemlogConnectionManager::OnConnectionComplete(base::ProcessId pid) { void MemlogConnectionManager::OnConnectionComplete(base::ProcessId pid) {
base::AutoLock lock(connections_lock_); base::AutoLock lock(connections_lock_);
auto found = connections_.find(pid); auto found = connections_.find(pid);
......
...@@ -73,6 +73,8 @@ class MemlogConnectionManager { ...@@ -73,6 +73,8 @@ class MemlogConnectionManager {
mojo::ScopedHandle receiver_pipe_end, mojo::ScopedHandle receiver_pipe_end,
mojom::ProcessType process_type); mojom::ProcessType process_type);
std::vector<base::ProcessId> GetConnectionPids();
private: private:
struct Connection; struct Connection;
struct DumpProcessesForTracingTracking; struct DumpProcessesForTracingTracking;
......
...@@ -80,6 +80,10 @@ void ProfilingService::DumpProcessesForTracing( ...@@ -80,6 +80,10 @@ void ProfilingService::DumpProcessesForTracing(
weak_factory_.GetWeakPtr(), base::Passed(&callback))); weak_factory_.GetWeakPtr(), base::Passed(&callback)));
} }
void ProfilingService::GetProfiledPids(GetProfiledPidsCallback callback) {
std::move(callback).Run(connection_manager_.GetConnectionPids());
}
void ProfilingService::OnGetVmRegionsCompleteForDumpProcessesForTracing( void ProfilingService::OnGetVmRegionsCompleteForDumpProcessesForTracing(
mojom::ProfilingService::DumpProcessesForTracingCallback callback, mojom::ProfilingService::DumpProcessesForTracingCallback callback,
bool success, bool success,
......
...@@ -48,6 +48,7 @@ class ProfilingService : public service_manager::Service, ...@@ -48,6 +48,7 @@ class ProfilingService : public service_manager::Service,
mojom::ProcessType process_type) override; mojom::ProcessType process_type) override;
void DumpProcessesForTracing( void DumpProcessesForTracing(
DumpProcessesForTracingCallback callback) override; DumpProcessesForTracingCallback callback) override;
void GetProfiledPids(GetProfiledPidsCallback callback) override;
private: private:
void MaybeRequestQuitDelayed(); void MaybeRequestQuitDelayed();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment