Commit e8865fd5 authored by Brian Geffon's avatar Brian Geffon Committed by Commit Bot

[PM] Add a working set trimmer mechanism.

As previously discussed this CL refactors the WorkingSetTrimmer into a
mechanism concept and adds support for ChromeOS. After this CL is
submitted the second step will be to refactor the existing observer into
a platform specific policy pattern.

Bug: 973963
Change-Id: Iacfc70a37f5b0a8519575359a6f842c2cd1f03f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1696306Reviewed-by: default avatarSébastien Marchand <sebmarchand@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Commit-Queue: Brian Geffon <bgeffon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#678435}
parent 07f00b6e
...@@ -1060,6 +1060,12 @@ jumbo_split_static_library("browser") { ...@@ -1060,6 +1060,12 @@ jumbo_split_static_library("browser") {
"performance_manager/graph/system_node.cc", "performance_manager/graph/system_node.cc",
"performance_manager/graph/system_node_impl.cc", "performance_manager/graph/system_node_impl.cc",
"performance_manager/graph/system_node_impl.h", "performance_manager/graph/system_node_impl.h",
"performance_manager/mechanisms/working_set_trimmer.cc",
"performance_manager/mechanisms/working_set_trimmer.h",
"performance_manager/mechanisms/working_set_trimmer_chromeos.cc",
"performance_manager/mechanisms/working_set_trimmer_chromeos.h",
"performance_manager/mechanisms/working_set_trimmer_win.cc",
"performance_manager/mechanisms/working_set_trimmer_win.h",
"performance_manager/observers/background_metrics_reporter.h", "performance_manager/observers/background_metrics_reporter.h",
"performance_manager/observers/graph_observer.cc", "performance_manager/observers/graph_observer.cc",
"performance_manager/observers/graph_observer.h", "performance_manager/observers/graph_observer.h",
...@@ -1067,8 +1073,8 @@ jumbo_split_static_library("browser") { ...@@ -1067,8 +1073,8 @@ jumbo_split_static_library("browser") {
"performance_manager/observers/isolation_context_metrics.h", "performance_manager/observers/isolation_context_metrics.h",
"performance_manager/observers/metrics_collector.cc", "performance_manager/observers/metrics_collector.cc",
"performance_manager/observers/metrics_collector.h", "performance_manager/observers/metrics_collector.h",
"performance_manager/observers/working_set_trimmer_win.cc", "performance_manager/observers/working_set_trimmer_observer_win.cc",
"performance_manager/observers/working_set_trimmer_win.h", "performance_manager/observers/working_set_trimmer_observer_win.h",
"performance_manager/performance_manager.cc", "performance_manager/performance_manager.cc",
"performance_manager/performance_manager.h", "performance_manager/performance_manager.h",
"performance_manager/performance_manager_clock.cc", "performance_manager/performance_manager_clock.cc",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/performance_manager/mechanisms/working_set_trimmer.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include "chrome/browser/performance_manager/mechanisms/working_set_trimmer_win.h"
#elif defined(OS_CHROMEOS)
#include "chrome/browser/performance_manager/mechanisms/working_set_trimmer_chromeos.h"
#endif
namespace performance_manager {
namespace mechanism {
namespace {
// The NoOpWorkingSetTrimmer provides an implementation of a working set trimmer
// that does nothing on unsupported platforms.
class NoOpWorkingSetTrimmer : public WorkingSetTrimmer {
public:
~NoOpWorkingSetTrimmer() override = default;
NoOpWorkingSetTrimmer() = default;
// WorkingSetTrimmer implementation:
bool PlatformSupportsWorkingSetTrim() override { return false; }
bool TrimWorkingSet(const ProcessNode* node) override { return false; }
};
} // namespace
WorkingSetTrimmer* WorkingSetTrimmer::GetInstance() {
#if defined(OS_WIN)
static base::NoDestructor<WorkingSetTrimmerWin> trimmer;
#elif defined(OS_CHROMEOS)
static base::NoDestructor<WorkingSetTrimmerChromeOS> trimmer;
#else
static base::NoDestructor<NoOpWorkingSetTrimmer> trimmer;
#endif
return trimmer.get();
}
} // namespace mechanism
} // namespace performance_manager
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_H_
#define CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_H_
#include "base/macros.h"
#include "base/no_destructor.h"
namespace performance_manager {
class ProcessNode;
namespace mechanism {
// A WorkingSetTrimmer will reduce a ProcessNode's memory footprint by giving a
// hint to the operating system that this processes memory may be reclaimed or
// trimmed.
class WorkingSetTrimmer {
public:
virtual ~WorkingSetTrimmer() = default;
// GetInstance will return the singleton instance of a working set trimmer for
// this platform.
static WorkingSetTrimmer* GetInstance();
// Returns true if the WorkingSetTrimmer is supported on the current platform.
virtual bool PlatformSupportsWorkingSetTrim() = 0;
// Returns true if working set trim succeeded for the provided ProcessNode.
virtual bool TrimWorkingSet(const ProcessNode* process_node) = 0;
protected:
// A WorkingSetTrimmer should never be created directly it should only be
// retrieved via WorkingSetTrimmer::GetInstance().
WorkingSetTrimmer() = default;
private:
friend class base::NoDestructor<WorkingSetTrimmer>;
DISALLOW_COPY_AND_ASSIGN(WorkingSetTrimmer);
};
} // namespace mechanism
} // namespace performance_manager
#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/performance_manager/mechanisms/working_set_trimmer_chromeos.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/performance_manager/public/graph/process_node.h"
namespace performance_manager {
namespace mechanism {
namespace {
// The chromeos kernel supports per-process reclaim if there exists a /reclaim
// file in a procfs node. We will simply stat /proc/self/reclaim to detect this
// support.
bool KernelSupportsReclaim() {
return base::PathExists(base::FilePath("/proc/self/reclaim"));
}
} // namespace
WorkingSetTrimmerChromeOS::WorkingSetTrimmerChromeOS() = default;
WorkingSetTrimmerChromeOS::~WorkingSetTrimmerChromeOS() = default;
bool WorkingSetTrimmerChromeOS::PlatformSupportsWorkingSetTrim() {
static const bool kPlatformSupported = KernelSupportsReclaim();
return kPlatformSupported;
}
bool WorkingSetTrimmerChromeOS::TrimWorkingSet(
const ProcessNode* process_node) {
if (!process_node->GetProcess().IsValid())
return false;
const std::string reclaim_file =
base::StringPrintf("/proc/%d/reclaim", process_node->GetProcessId());
const std::string kReclaimMode = "all";
ssize_t written = base::WriteFile(base::FilePath(reclaim_file),
kReclaimMode.c_str(), kReclaimMode.size());
// We won't log an error if reclaim failed due to the process being dead.
PLOG_IF(ERROR, written < 0 && errno != ENOENT)
<< "Write failed on " << reclaim_file << " mode: " << kReclaimMode;
return written > 0;
}
} // namespace mechanism
} // namespace performance_manager
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_CHROMEOS_H_
#define CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_CHROMEOS_H_
#include "base/no_destructor.h"
#include "chrome/browser/performance_manager/mechanisms/working_set_trimmer.h"
namespace performance_manager {
namespace mechanism {
// WorkingSetTrimmerChromeOS is the platform specific implementation of a
// working set trimmer for ChromeOS. This class should not be used directly it
// should be used via the WorkingSetTrimmer::GetInstance() method.
class WorkingSetTrimmerChromeOS : public WorkingSetTrimmer {
public:
~WorkingSetTrimmerChromeOS() override;
// WorkingSetTrimmer implementation:
bool PlatformSupportsWorkingSetTrim() override;
bool TrimWorkingSet(const ProcessNode* process_node) override;
private:
friend class base::NoDestructor<WorkingSetTrimmerChromeOS>;
// The constructor is made private to prevent instantiation of this class
// directly, it should always be retrieved via
// WorkingSetTrimmer::GetInstance().
WorkingSetTrimmerChromeOS();
DISALLOW_COPY_AND_ASSIGN(WorkingSetTrimmerChromeOS);
};
} // namespace mechanism
} // namespace performance_manager
#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_CHROMEOS_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/performance_manager/mechanisms/working_set_trimmer_win.h"
#include <windows.h> // Must be in front of other Windows header files.
#include <psapi.h>
#include "base/logging.h"
#include "base/process/process.h"
#include "chrome/browser/performance_manager/public/graph/process_node.h"
namespace performance_manager {
namespace mechanism {
WorkingSetTrimmerWin::WorkingSetTrimmerWin() = default;
WorkingSetTrimmerWin::~WorkingSetTrimmerWin() = default;
bool WorkingSetTrimmerWin::TrimWorkingSet(const ProcessNode* process_node) {
// Open a new handle to the process with the specific access needed.
const base::Process& process = process_node->GetProcess();
if (!process.IsValid())
return false;
base::Process process_copy = base::Process::OpenWithAccess(
process.Pid(), PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_QUOTA);
if (!process_copy.IsValid()) {
DPLOG(ERROR) << "Working set not emptied because process handle could not "
"be obtained.";
return false;
}
BOOL empty_working_set_success = ::EmptyWorkingSet(process.Handle());
DPLOG_IF(ERROR, !empty_working_set_success)
<< "Working set not emptied because EmptyWorkingSet() failed";
return empty_working_set_success;
}
bool WorkingSetTrimmerWin::PlatformSupportsWorkingSetTrim() {
return true;
}
} // namespace mechanism
} // namespace performance_manager
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_WIN_H_
#define CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_WIN_H_
#include "chrome/browser/performance_manager/mechanisms/working_set_trimmer.h"
namespace performance_manager {
namespace mechanism {
// WorkingSetTrimmerWin is the platform specific implementation of a
// working set trimmer for Windows. This class should not be used directly it
// should be used via the WorkingSetTrimmer::GetIntsance() method.
class WorkingSetTrimmerWin : public WorkingSetTrimmer {
public:
~WorkingSetTrimmerWin() override;
bool PlatformSupportsWorkingSetTrim() override;
bool TrimWorkingSet(const ProcessNode* process_node) override;
private:
friend class base::NoDestructor<WorkingSetTrimmerWin>;
// The constructor is made private to prevent instantiation of this class
// directly, it should always be retrieved via
// WorkingSetTrimmer::GetIntsance().
WorkingSetTrimmerWin();
DISALLOW_COPY_AND_ASSIGN(WorkingSetTrimmerWin);
};
} // namespace mechanism
} // namespace performance_manager
#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_WORKING_SET_TRIMMER_WIN_H_
...@@ -2,47 +2,17 @@ ...@@ -2,47 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/performance_manager/observers/working_set_trimmer_win.h" #include "chrome/browser/performance_manager/observers/working_set_trimmer_observer_win.h"
#include <windows.h> // Must be in front of other Windows header files.
#include <psapi.h>
#include "base/logging.h" #include "base/logging.h"
#include "base/process/process.h" #include "base/process/process.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/performance_manager/graph/node_base.h" #include "chrome/browser/performance_manager/graph/node_base.h"
#include "chrome/browser/performance_manager/graph/process_node_impl.h" #include "chrome/browser/performance_manager/graph/process_node_impl.h"
#include "chrome/browser/performance_manager/mechanisms/working_set_trimmer.h"
namespace performance_manager { namespace performance_manager {
namespace {
// Empties the working set of a process with id |process_id| and creation time
// |process_creation_time|. The creation time is verified to ensure that we
// don't empty the working set of the wrong process if the target process exits
// and its id is reused.
void EmptyWorkingSet(const base::Process& process,
base::Time process_creation_time) {
// Open a new handle to the process with the specific access needed.
base::Process process_copy = base::Process::OpenWithAccess(
process.Pid(), PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_QUOTA);
if (!process_copy.IsValid()) {
DPLOG(ERROR) << "Working set not emptied because process handle could not "
"be obtained.";
return;
}
#if DCHECK_IS_ON()
BOOL empty_working_set_success =
#endif
::EmptyWorkingSet(process.Handle());
DPLOG_IF(ERROR, !empty_working_set_success)
<< "Working set not emptied because EmptyWorkingSet() failed";
}
} // namespace
WorkingSetTrimmer::WorkingSetTrimmer() = default; WorkingSetTrimmer::WorkingSetTrimmer() = default;
WorkingSetTrimmer::~WorkingSetTrimmer() = default; WorkingSetTrimmer::~WorkingSetTrimmer() = default;
...@@ -56,8 +26,11 @@ void WorkingSetTrimmer::OnTakenFromGraph(Graph* graph) { ...@@ -56,8 +26,11 @@ void WorkingSetTrimmer::OnTakenFromGraph(Graph* graph) {
void WorkingSetTrimmer::OnAllFramesInProcessFrozen( void WorkingSetTrimmer::OnAllFramesInProcessFrozen(
const ProcessNode* process_node) { const ProcessNode* process_node) {
if (process_node->GetProcess().IsValid()) auto* trimmer = mechanism::WorkingSetTrimmer::GetInstance();
EmptyWorkingSet(process_node->GetProcess(), process_node->GetLaunchTime());
if (process_node->GetProcess().IsValid() &&
trimmer->PlatformSupportsWorkingSetTrim())
trimmer->TrimWorkingSet(process_node);
} }
void WorkingSetTrimmer::RegisterObservers(Graph* graph) { void WorkingSetTrimmer::RegisterObservers(Graph* graph) {
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_WORKING_SET_TRIMMER_WIN_H_ #ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_WORKING_SET_TRIMMER_OBSERVER_WIN_H_
#define CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_WORKING_SET_TRIMMER_WIN_H_ #define CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_WORKING_SET_TRIMMER_OBSERVER_WIN_H_
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/performance_manager/public/graph/graph.h" #include "chrome/browser/performance_manager/public/graph/graph.h"
...@@ -53,4 +53,4 @@ class WorkingSetTrimmer : public GraphOwned, ...@@ -53,4 +53,4 @@ class WorkingSetTrimmer : public GraphOwned,
} // namespace performance_manager } // namespace performance_manager
#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_WORKING_SET_TRIMMER_WIN_H_ #endif // CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_WORKING_SET_TRIMMER_OBSERVER_WIN_H_
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/performance_manager/observers/working_set_trimmer_win.h" #include "chrome/browser/performance_manager/observers/working_set_trimmer_observer_win.h"
#include <windows.h> // Must be in front of other Windows header files. #include <windows.h> // Must be in front of other Windows header files.
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include "chrome/browser/performance_manager/graph/system_node_impl.h" #include "chrome/browser/performance_manager/graph/system_node_impl.h"
#include "chrome/browser/performance_manager/observers/isolation_context_metrics.h" #include "chrome/browser/performance_manager/observers/isolation_context_metrics.h"
#include "chrome/browser/performance_manager/observers/metrics_collector.h" #include "chrome/browser/performance_manager/observers/metrics_collector.h"
#include "chrome/browser/performance_manager/observers/working_set_trimmer_win.h" #include "chrome/browser/performance_manager/observers/working_set_trimmer_observer_win.h"
#include "content/public/browser/system_connector.h" #include "content/public/browser/system_connector.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h" #include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
......
...@@ -2981,7 +2981,7 @@ test("unit_tests") { ...@@ -2981,7 +2981,7 @@ test("unit_tests") {
"../browser/performance_manager/observers/graph_observer_unittest.cc", "../browser/performance_manager/observers/graph_observer_unittest.cc",
"../browser/performance_manager/observers/isolation_context_metrics_unittest.cc", "../browser/performance_manager/observers/isolation_context_metrics_unittest.cc",
"../browser/performance_manager/observers/metrics_collector_unittest.cc", "../browser/performance_manager/observers/metrics_collector_unittest.cc",
"../browser/performance_manager/observers/working_set_trimmer_win_unittest.cc", "../browser/performance_manager/observers/working_set_trimmer_observer_win_unittest.cc",
"../browser/performance_manager/performance_manager_tab_helper_unittest.cc", "../browser/performance_manager/performance_manager_tab_helper_unittest.cc",
"../browser/performance_manager/performance_manager_test_harness.cc", "../browser/performance_manager/performance_manager_test_harness.cc",
"../browser/performance_manager/performance_manager_test_harness.h", "../browser/performance_manager/performance_manager_test_harness.h",
......
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