processgroup.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. /*
  2. * Copyright 2014 Google, Inc
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. //#define LOG_NDEBUG 0
  17. #define LOG_TAG "libprocessgroup"
  18. #include <assert.h>
  19. #include <dirent.h>
  20. #include <errno.h>
  21. #include <fcntl.h>
  22. #include <inttypes.h>
  23. #include <signal.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <sys/stat.h>
  27. #include <sys/types.h>
  28. #include <unistd.h>
  29. #include <chrono>
  30. #include <map>
  31. #include <memory>
  32. #include <mutex>
  33. #include <set>
  34. #include <string>
  35. #include <thread>
  36. #include <android-base/file.h>
  37. #include <android-base/logging.h>
  38. #include <android-base/properties.h>
  39. #include <android-base/stringprintf.h>
  40. #include <android-base/strings.h>
  41. #include <cutils/android_filesystem_config.h>
  42. #include <processgroup/processgroup.h>
  43. #include <task_profiles.h>
  44. using android::base::GetBoolProperty;
  45. using android::base::StartsWith;
  46. using android::base::StringPrintf;
  47. using android::base::WriteStringToFile;
  48. using namespace std::chrono_literals;
  49. #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
  50. bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
  51. auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
  52. if (!controller.HasValue()) {
  53. return false;
  54. }
  55. if (path) {
  56. *path = controller.path();
  57. }
  58. return true;
  59. }
  60. bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
  61. const TaskProfiles& tp = TaskProfiles::GetInstance();
  62. const ProfileAttribute* attr = tp.GetAttribute(attr_name);
  63. if (attr == nullptr) {
  64. return false;
  65. }
  66. if (path) {
  67. *path = StringPrintf("%s/%s", attr->controller()->path(), attr->file_name().c_str());
  68. }
  69. return true;
  70. }
  71. bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
  72. const TaskProfiles& tp = TaskProfiles::GetInstance();
  73. const ProfileAttribute* attr = tp.GetAttribute(attr_name);
  74. if (attr == nullptr) {
  75. return false;
  76. }
  77. if (!attr->GetPathForTask(tid, path)) {
  78. PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
  79. return false;
  80. }
  81. return true;
  82. }
  83. bool UsePerAppMemcg() {
  84. bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
  85. return GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
  86. }
  87. static bool isMemoryCgroupSupported() {
  88. static bool memcg_supported = CgroupMap::GetInstance().FindController("memory").IsUsable();
  89. return memcg_supported;
  90. }
  91. bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
  92. bool use_fd_cache) {
  93. const TaskProfiles& tp = TaskProfiles::GetInstance();
  94. for (const auto& name : profiles) {
  95. TaskProfile* profile = tp.GetProfile(name);
  96. if (profile != nullptr) {
  97. if (use_fd_cache) {
  98. profile->EnableResourceCaching();
  99. }
  100. if (!profile->ExecuteForProcess(uid, pid)) {
  101. PLOG(WARNING) << "Failed to apply " << name << " process profile";
  102. }
  103. } else {
  104. PLOG(WARNING) << "Failed to find " << name << "process profile";
  105. }
  106. }
  107. return true;
  108. }
  109. bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
  110. const TaskProfiles& tp = TaskProfiles::GetInstance();
  111. for (const auto& name : profiles) {
  112. TaskProfile* profile = tp.GetProfile(name);
  113. if (profile != nullptr) {
  114. if (use_fd_cache) {
  115. profile->EnableResourceCaching();
  116. }
  117. if (!profile->ExecuteForTask(tid)) {
  118. PLOG(WARNING) << "Failed to apply " << name << " task profile";
  119. }
  120. } else {
  121. PLOG(WARNING) << "Failed to find " << name << "task profile";
  122. }
  123. }
  124. return true;
  125. }
  126. static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
  127. return StringPrintf("%s/uid_%d", cgroup, uid);
  128. }
  129. static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
  130. return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
  131. }
  132. static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
  133. int ret;
  134. auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
  135. ret = rmdir(uid_pid_path.c_str());
  136. auto uid_path = ConvertUidToPath(cgroup, uid);
  137. rmdir(uid_path.c_str());
  138. return ret;
  139. }
  140. static bool RemoveUidProcessGroups(const std::string& uid_path) {
  141. std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
  142. bool empty = true;
  143. if (uid != NULL) {
  144. dirent* dir;
  145. while ((dir = readdir(uid.get())) != nullptr) {
  146. if (dir->d_type != DT_DIR) {
  147. continue;
  148. }
  149. if (!StartsWith(dir->d_name, "pid_")) {
  150. continue;
  151. }
  152. auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
  153. LOG(VERBOSE) << "Removing " << path;
  154. if (rmdir(path.c_str()) == -1) {
  155. if (errno != EBUSY) {
  156. PLOG(WARNING) << "Failed to remove " << path;
  157. }
  158. empty = false;
  159. }
  160. }
  161. }
  162. return empty;
  163. }
  164. void removeAllProcessGroups() {
  165. LOG(VERBOSE) << "removeAllProcessGroups()";
  166. std::vector<std::string> cgroups;
  167. std::string path;
  168. if (CgroupGetControllerPath("cpuacct", &path)) {
  169. cgroups.push_back(path);
  170. }
  171. if (CgroupGetControllerPath("memory", &path)) {
  172. cgroups.push_back(path + "/apps");
  173. }
  174. for (std::string cgroup_root_path : cgroups) {
  175. std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
  176. if (root == NULL) {
  177. PLOG(ERROR) << "Failed to open " << cgroup_root_path;
  178. } else {
  179. dirent* dir;
  180. while ((dir = readdir(root.get())) != nullptr) {
  181. if (dir->d_type != DT_DIR) {
  182. continue;
  183. }
  184. if (!StartsWith(dir->d_name, "uid_")) {
  185. continue;
  186. }
  187. auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
  188. if (!RemoveUidProcessGroups(path)) {
  189. LOG(VERBOSE) << "Skip removing " << path;
  190. continue;
  191. }
  192. LOG(VERBOSE) << "Removing " << path;
  193. if (rmdir(path.c_str()) == -1 && errno != EBUSY) {
  194. PLOG(WARNING) << "Failed to remove " << path;
  195. }
  196. }
  197. }
  198. }
  199. }
  200. static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
  201. if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
  202. return false;
  203. }
  204. if (chown(path.c_str(), uid, gid) == -1) {
  205. int saved_errno = errno;
  206. rmdir(path.c_str());
  207. errno = saved_errno;
  208. return false;
  209. }
  210. return true;
  211. }
  212. // Returns number of processes killed on success
  213. // Returns 0 if there are no processes in the process cgroup left to kill
  214. // Returns -1 on error
  215. static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) {
  216. auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
  217. std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
  218. if (!fd) {
  219. if (errno == ENOENT) {
  220. // This happens when process is already dead
  221. return 0;
  222. }
  223. PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
  224. return -1;
  225. }
  226. // We separate all of the pids in the cgroup into those pids that are also the leaders of
  227. // process groups (stored in the pgids set) and those that are not (stored in the pids set).
  228. std::set<pid_t> pgids;
  229. pgids.emplace(initialPid);
  230. std::set<pid_t> pids;
  231. pid_t pid;
  232. int processes = 0;
  233. while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
  234. processes++;
  235. if (pid == 0) {
  236. // Should never happen... but if it does, trying to kill this
  237. // will boomerang right back and kill us! Let's not let that happen.
  238. LOG(WARNING) << "Yikes, we've been told to kill pid 0! How about we don't do that?";
  239. continue;
  240. }
  241. pid_t pgid = getpgid(pid);
  242. if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
  243. if (pgid == pid) {
  244. pgids.emplace(pid);
  245. } else {
  246. pids.emplace(pid);
  247. }
  248. }
  249. // Erase all pids that will be killed when we kill the process groups.
  250. for (auto it = pids.begin(); it != pids.end();) {
  251. pid_t pgid = getpgid(*it);
  252. if (pgids.count(pgid) == 1) {
  253. it = pids.erase(it);
  254. } else {
  255. ++it;
  256. }
  257. }
  258. // Kill all process groups.
  259. for (const auto pgid : pgids) {
  260. LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
  261. << " as part of process cgroup " << initialPid;
  262. if (kill(-pgid, signal) == -1 && errno != ESRCH) {
  263. PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
  264. }
  265. }
  266. // Kill remaining pids.
  267. for (const auto pid : pids) {
  268. LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
  269. << initialPid;
  270. if (kill(pid, signal) == -1 && errno != ESRCH) {
  271. PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
  272. }
  273. }
  274. return feof(fd.get()) ? processes : -1;
  275. }
  276. static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
  277. std::string cpuacct_path;
  278. std::string memory_path;
  279. CgroupGetControllerPath("cpuacct", &cpuacct_path);
  280. CgroupGetControllerPath("memory", &memory_path);
  281. memory_path += "/apps";
  282. const char* cgroup =
  283. (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
  284. ? cpuacct_path.c_str()
  285. : memory_path.c_str();
  286. std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
  287. int retry = retries;
  288. int processes;
  289. while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
  290. LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
  291. if (retry > 0) {
  292. std::this_thread::sleep_for(5ms);
  293. --retry;
  294. } else {
  295. break;
  296. }
  297. }
  298. if (processes < 0) {
  299. PLOG(ERROR) << "Error encountered killing process cgroup uid " << uid << " pid "
  300. << initialPid;
  301. return -1;
  302. }
  303. std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
  304. auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
  305. // We only calculate the number of 'processes' when killing the processes.
  306. // In the retries == 0 case, we only kill the processes once and therefore
  307. // will not have waited then recalculated how many processes are remaining
  308. // after the first signals have been sent.
  309. // Logging anything regarding the number of 'processes' here does not make sense.
  310. if (processes == 0) {
  311. if (retries > 0) {
  312. LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
  313. << " in " << static_cast<int>(ms) << "ms";
  314. }
  315. return RemoveProcessGroup(cgroup, uid, initialPid);
  316. } else {
  317. if (retries > 0) {
  318. LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
  319. << " in " << static_cast<int>(ms) << "ms, " << processes
  320. << " processes remain";
  321. }
  322. return -1;
  323. }
  324. }
  325. int killProcessGroup(uid_t uid, int initialPid, int signal) {
  326. return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
  327. }
  328. int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
  329. return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
  330. }
  331. int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
  332. std::string cgroup;
  333. if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
  334. CgroupGetControllerPath("memory", &cgroup);
  335. cgroup += "/apps";
  336. } else {
  337. CgroupGetControllerPath("cpuacct", &cgroup);
  338. }
  339. auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
  340. if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
  341. PLOG(ERROR) << "Failed to make and chown " << uid_path;
  342. return -errno;
  343. }
  344. auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
  345. if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
  346. PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
  347. return -errno;
  348. }
  349. auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
  350. int ret = 0;
  351. if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
  352. ret = -errno;
  353. PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file;
  354. }
  355. return ret;
  356. }
  357. static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
  358. if (!isMemoryCgroupSupported()) {
  359. PLOG(ERROR) << "Memcg is not mounted.";
  360. return false;
  361. }
  362. std::string path;
  363. if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {
  364. PLOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
  365. return false;
  366. }
  367. if (!WriteStringToFile(std::to_string(value), path)) {
  368. PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
  369. return false;
  370. }
  371. return true;
  372. }
  373. bool setProcessGroupSwappiness(uid_t, int pid, int swappiness) {
  374. return SetProcessGroupValue(pid, "MemSwappiness", swappiness);
  375. }
  376. bool setProcessGroupSoftLimit(uid_t, int pid, int64_t soft_limit_in_bytes) {
  377. return SetProcessGroupValue(pid, "MemSoftLimit", soft_limit_in_bytes);
  378. }
  379. bool setProcessGroupLimit(uid_t, int pid, int64_t limit_in_bytes) {
  380. return SetProcessGroupValue(pid, "MemLimit", limit_in_bytes);
  381. }