dbus_memory_pressure_evaluator_linux.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "chrome/browser/dbus_memory_pressure_evaluator_linux.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/containers/contains.h"
  8. #include "base/logging.h"
  9. #include "base/memory/memory_pressure_listener.h"
  10. #include "base/memory/scoped_refptr.h"
  11. #include "chrome/common/chrome_features.h"
  12. #include "components/dbus/thread_linux/dbus_thread_linux.h"
  13. #include "dbus/message.h"
  14. #include "dbus/object_path.h"
  15. #include "dbus/object_proxy.h"
  16. namespace {
  17. scoped_refptr<dbus::Bus> CreateBusOfType(dbus::Bus::BusType type) {
  18. dbus::Bus::Options options;
  19. options.bus_type = type;
  20. options.connection_type = dbus::Bus::PRIVATE;
  21. options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
  22. return base::MakeRefCounted<dbus::Bus>(options);
  23. }
  24. } // namespace
  25. const char DbusMemoryPressureEvaluatorLinux::kMethodNameHasOwner[] =
  26. "NameHasOwner";
  27. const char DbusMemoryPressureEvaluatorLinux::kMethodListActivatableNames[] =
  28. "ListActivatableNames";
  29. const char DbusMemoryPressureEvaluatorLinux::kLmmService[] =
  30. "org.freedesktop.LowMemoryMonitor";
  31. const char DbusMemoryPressureEvaluatorLinux::kLmmObject[] =
  32. "/org/freedesktop/LowMemoryMonitor";
  33. const char DbusMemoryPressureEvaluatorLinux::kLmmInterface[] =
  34. "org.freedesktop.LowMemoryMonitor";
  35. const char DbusMemoryPressureEvaluatorLinux::kXdgPortalService[] =
  36. "org.freedesktop.portal.Desktop";
  37. const char DbusMemoryPressureEvaluatorLinux::kXdgPortalObject[] =
  38. "/org/freedesktop/portal/desktop";
  39. const char
  40. DbusMemoryPressureEvaluatorLinux::kXdgPortalMemoryMonitorInterface[] =
  41. "org.freedesktop.portal.MemoryMonitor";
  42. const char DbusMemoryPressureEvaluatorLinux::kLowMemoryWarningSignal[] =
  43. "LowMemoryWarning";
  44. // LMM emits signals every 15 seconds on pressure, so if we've been quiet for 20
  45. // seconds, the pressure is likely cleared up.
  46. const base::TimeDelta DbusMemoryPressureEvaluatorLinux::kResetVotePeriod =
  47. base::Seconds(20);
  48. DbusMemoryPressureEvaluatorLinux::DbusMemoryPressureEvaluatorLinux(
  49. std::unique_ptr<memory_pressure::MemoryPressureVoter> voter)
  50. : DbusMemoryPressureEvaluatorLinux(std::move(voter), nullptr, nullptr) {
  51. // Only start the service checks in the public constructor, so the tests can
  52. // have time to set up mocks first when using the private constructor.
  53. CheckIfLmmIsAvailable();
  54. }
  55. DbusMemoryPressureEvaluatorLinux::DbusMemoryPressureEvaluatorLinux(
  56. std::unique_ptr<memory_pressure::MemoryPressureVoter> voter,
  57. scoped_refptr<dbus::Bus> system_bus,
  58. scoped_refptr<dbus::Bus> session_bus)
  59. : memory_pressure::SystemMemoryPressureEvaluator(std::move(voter)),
  60. system_bus_(system_bus),
  61. session_bus_(session_bus) {
  62. moderate_level_ = features::kLinuxLowMemoryMonitorModerateLevel.Get();
  63. critical_level_ = features::kLinuxLowMemoryMonitorCriticalLevel.Get();
  64. CHECK(critical_level_ > moderate_level_);
  65. }
  66. DbusMemoryPressureEvaluatorLinux::~DbusMemoryPressureEvaluatorLinux() {
  67. if (system_bus_) {
  68. system_bus_->ShutdownOnDBusThreadAndBlock();
  69. system_bus_.reset();
  70. }
  71. if (session_bus_) {
  72. session_bus_->ShutdownOnDBusThreadAndBlock();
  73. session_bus_.reset();
  74. }
  75. }
  76. void DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailable() {
  77. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  78. if (!system_bus_)
  79. system_bus_ = CreateBusOfType(dbus::Bus::SYSTEM);
  80. CheckIfServiceIsAvailable(
  81. system_bus_, kLmmService,
  82. base::BindOnce(
  83. &DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailableResponse,
  84. weak_ptr_factory_.GetWeakPtr()));
  85. }
  86. void DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailableResponse(
  87. bool is_available) {
  88. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  89. if (is_available) {
  90. VLOG(1) << "LMM is available, using " << kLmmInterface;
  91. object_proxy_ =
  92. system_bus_->GetObjectProxy(kLmmService, dbus::ObjectPath(kLmmObject));
  93. object_proxy_->ConnectToSignal(
  94. kLmmInterface, kLowMemoryWarningSignal,
  95. base::BindRepeating(
  96. &DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning,
  97. weak_ptr_factory_.GetWeakPtr()),
  98. base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnSignalConnected,
  99. weak_ptr_factory_.GetWeakPtr()));
  100. } else {
  101. VLOG(1) << "LMM is not available, checking for portal";
  102. ResetBus(system_bus_);
  103. CheckIfPortalIsAvailable();
  104. }
  105. }
  106. void DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailable() {
  107. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  108. if (!session_bus_)
  109. session_bus_ = CreateBusOfType(dbus::Bus::SESSION);
  110. CheckIfServiceIsAvailable(
  111. session_bus_, kXdgPortalService,
  112. base::BindOnce(
  113. &DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailableResponse,
  114. weak_ptr_factory_.GetWeakPtr()));
  115. }
  116. void DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailableResponse(
  117. bool is_available) {
  118. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  119. if (is_available) {
  120. VLOG(1) << "Portal is available, using "
  121. << kXdgPortalMemoryMonitorInterface;
  122. object_proxy_ = session_bus_->GetObjectProxy(
  123. kXdgPortalService, dbus::ObjectPath(kXdgPortalObject));
  124. object_proxy_->ConnectToSignal(
  125. kXdgPortalMemoryMonitorInterface, kLowMemoryWarningSignal,
  126. base::BindRepeating(
  127. &DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning,
  128. weak_ptr_factory_.GetWeakPtr()),
  129. base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnSignalConnected,
  130. weak_ptr_factory_.GetWeakPtr()));
  131. } else {
  132. VLOG(1) << "No memory monitor found";
  133. ResetBus(session_bus_);
  134. }
  135. }
  136. void DbusMemoryPressureEvaluatorLinux::CheckIfServiceIsAvailable(
  137. scoped_refptr<dbus::Bus> bus,
  138. const std::string& service,
  139. base::OnceCallback<void(bool)> callback) {
  140. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  141. dbus::ObjectProxy* dbus_proxy =
  142. bus->GetObjectProxy(DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS));
  143. dbus::MethodCall method_call(DBUS_INTERFACE_DBUS, kMethodNameHasOwner);
  144. dbus::MessageWriter writer(&method_call);
  145. writer.AppendString(service);
  146. dbus_proxy->CallMethod(
  147. &method_call, DBUS_TIMEOUT_USE_DEFAULT,
  148. base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnNameHasOwnerResponse,
  149. weak_ptr_factory_.GetWeakPtr(), std::move(bus), service,
  150. std::move(callback)));
  151. }
  152. void DbusMemoryPressureEvaluatorLinux::OnNameHasOwnerResponse(
  153. scoped_refptr<dbus::Bus> bus,
  154. const std::string& service,
  155. base::OnceCallback<void(bool)> callback,
  156. dbus::Response* response) {
  157. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  158. bool is_running = false;
  159. if (response) {
  160. dbus::MessageReader reader(response);
  161. bool owned = false;
  162. if (!reader.PopBool(&owned)) {
  163. LOG(ERROR) << "Failed to read " << kMethodNameHasOwner << " response";
  164. } else if (owned) {
  165. is_running = true;
  166. }
  167. } else {
  168. LOG(ERROR) << "Failed to call " << kMethodNameHasOwner;
  169. }
  170. if (is_running) {
  171. std::move(callback).Run(true);
  172. } else {
  173. dbus::ObjectProxy* dbus_proxy = bus->GetObjectProxy(
  174. DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS));
  175. dbus::MethodCall method_call(DBUS_INTERFACE_DBUS,
  176. kMethodListActivatableNames);
  177. dbus_proxy->CallMethod(
  178. &method_call, DBUS_TIMEOUT_USE_DEFAULT,
  179. base::BindOnce(
  180. &DbusMemoryPressureEvaluatorLinux::OnListActivatableNamesResponse,
  181. weak_ptr_factory_.GetWeakPtr(), std::move(service),
  182. std::move(callback)));
  183. }
  184. }
  185. void DbusMemoryPressureEvaluatorLinux::OnListActivatableNamesResponse(
  186. const std::string& service,
  187. base::OnceCallback<void(bool)> callback,
  188. dbus::Response* response) {
  189. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  190. bool is_activatable = false;
  191. if (response) {
  192. dbus::MessageReader reader(response);
  193. std::vector<std::string> names;
  194. if (!reader.PopArrayOfStrings(&names)) {
  195. LOG(ERROR) << "Failed to read " << kMethodListActivatableNames
  196. << " response";
  197. } else if (base::Contains(names, service)) {
  198. is_activatable = true;
  199. }
  200. } else {
  201. LOG(ERROR) << "Failed to call " << kMethodListActivatableNames;
  202. }
  203. std::move(callback).Run(is_activatable);
  204. }
  205. void DbusMemoryPressureEvaluatorLinux::ResetBus(scoped_refptr<dbus::Bus>& bus) {
  206. if (!bus)
  207. return;
  208. bus->GetDBusTaskRunner()->PostTask(
  209. FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus));
  210. bus.reset();
  211. }
  212. void DbusMemoryPressureEvaluatorLinux::OnSignalConnected(
  213. const std::string& interface,
  214. const std::string& signal,
  215. bool connected) {
  216. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  217. if (!connected) {
  218. LOG(WARNING) << "Failed to connect to " << interface << '.' << signal;
  219. ResetBus(system_bus_);
  220. ResetBus(session_bus_);
  221. }
  222. }
  223. void DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning(
  224. dbus::Signal* signal) {
  225. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  226. dbus::MessageReader reader(signal);
  227. uint8_t lmm_level;
  228. if (!reader.PopByte(&lmm_level)) {
  229. LOG(WARNING) << "Failed to parse low memory level";
  230. return;
  231. }
  232. // static_cast is needed as lmm_level is a uint8_t, which is often an alias to
  233. // char, meaning that sending it to the output stream would just print the
  234. // character representation rather than the numeric representation.
  235. VLOG(1) << "Monitor sent memory pressure level: "
  236. << static_cast<int>(lmm_level);
  237. base::MemoryPressureListener::MemoryPressureLevel new_level =
  238. LmmToBasePressureLevel(lmm_level);
  239. VLOG(1) << "MemoryPressureLevel: " << new_level;
  240. UpdateLevel(new_level);
  241. }
  242. base::MemoryPressureListener::MemoryPressureLevel
  243. DbusMemoryPressureEvaluatorLinux::LmmToBasePressureLevel(uint8_t lmm_level) {
  244. if (lmm_level >= critical_level_)
  245. return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
  246. if (lmm_level >= moderate_level_)
  247. return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
  248. return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
  249. }
  250. void DbusMemoryPressureEvaluatorLinux::UpdateLevel(
  251. base::MemoryPressureListener::MemoryPressureLevel new_level) {
  252. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  253. reset_vote_timer_.Stop();
  254. SetCurrentVote(new_level);
  255. switch (new_level) {
  256. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
  257. // By convention no notifications are sent when returning to NONE level.
  258. SendCurrentVote(false);
  259. break;
  260. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
  261. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
  262. SendCurrentVote(true);
  263. reset_vote_timer_.Start(
  264. FROM_HERE, kResetVotePeriod,
  265. base::BindOnce(
  266. &DbusMemoryPressureEvaluatorLinux::UpdateLevel,
  267. weak_ptr_factory_.GetWeakPtr(),
  268. base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE));
  269. break;
  270. }
  271. }