OSDN Git Service

Fix the way to count online cpus.
authorYabin Cui <yabinc@google.com>
Tue, 29 Sep 2015 23:11:45 +0000 (16:11 -0700)
committerYabin Cui <yabinc@google.com>
Wed, 30 Sep 2015 00:49:37 +0000 (17:49 -0700)
Read /proc/stat to count online cpus is not correct for all android
kernels. Change to reading /sys/devices/system/cpu/online instead.

Bug: 24376925
Change-Id: I3785a6c7aa15a467022a9a261b457194d688fb38

libc/bionic/sysinfo.cpp
libc/private/get_cpu_count_from_string.h [new file with mode: 0644]
tests/unistd_test.cpp

index a48bfea..1cb5c79 100644 (file)
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "private/get_cpu_count_from_string.h"
 #include "private/ScopedReaddir.h"
 
 static bool __matches_cpuN(const char* s) {
@@ -61,26 +62,18 @@ int get_nprocs_conf() {
 }
 
 int get_nprocs() {
-  FILE* fp = fopen("/proc/stat", "re");
-  if (fp == NULL) {
-    return 1;
-  }
-
-  int result = 0;
-  char buf[256];
-  while (fgets(buf, sizeof(buf), fp) != NULL) {
-    // Extract just the first word from the line.
-    // 'cpu0 7976751 1364388 3116842 469770388 8629405 0 49047 0 0 0'
-    char* p = strchr(buf, ' ');
-    if (p != NULL) {
-      *p = 0;
-    }
-    if (__matches_cpuN(buf)) {
-      ++result;
+  int cpu_count = 1;
+  FILE* fp = fopen("/sys/devices/system/cpu/online", "re");
+  if (fp != nullptr) {
+    char* line = nullptr;
+    size_t len = 0;
+    if (getline(&line, &len, fp) != -1) {
+      cpu_count = GetCpuCountFromString(line);
+      free(line);
     }
+    fclose(fp);
   }
-  fclose(fp);
-  return result;
+  return cpu_count;
 }
 
 static int __get_meminfo_page_count(const char* pattern) {
diff --git a/libc/private/get_cpu_count_from_string.h b/libc/private/get_cpu_count_from_string.h
new file mode 100644 (file)
index 0000000..a0cb95d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+
+// Parse a string like: 0, 2-4, 6.
+static int GetCpuCountFromString(const char* s) {
+  int cpu_count = 0;
+  int last_cpu = -1;
+  while (*s != '\0') {
+    if (isdigit(*s)) {
+      int cpu = static_cast<int>(strtol(s, const_cast<char**>(&s), 10));
+      if (last_cpu != -1) {
+        cpu_count += cpu - last_cpu;
+      } else {
+        cpu_count++;
+      }
+      last_cpu = cpu;
+    } else {
+      if (*s == ',') {
+        last_cpu = -1;
+      }
+      s++;
+    }
+  }
+  return cpu_count;
+}
index 8e1f412..500377c 100644 (file)
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <base/file.h>
+#include <base/strings.h>
+
+#include "private/get_cpu_count_from_string.h"
+
 static void* get_brk() {
   return sbrk(0);
 }
@@ -819,6 +824,29 @@ TEST(unistd, sysconf) {
 #endif // defined(__BIONIC__)
 }
 
+TEST(unistd, get_cpu_count_from_string) {
+  ASSERT_EQ(0, GetCpuCountFromString(" "));
+  ASSERT_EQ(1, GetCpuCountFromString("0"));
+  ASSERT_EQ(40, GetCpuCountFromString("0-39"));
+  ASSERT_EQ(4, GetCpuCountFromString("0, 1-2, 4\n"));
+}
+
+TEST(unistd, sysconf_SC_NPROCESSORS_ONLN) {
+  std::string s;
+  ASSERT_TRUE(android::base::ReadFileToString("/sys/devices/system/cpu/online", &s));
+  std::vector<std::string> strs = android::base::Split(s, ",");
+  long online_cpus = 0;
+  for (auto& s : strs) {
+    std::vector<std::string> numbers = android::base::Split(s, "-");
+    if (numbers.size() == 1u) {
+      online_cpus++;
+    } else {
+      online_cpus += atoi(numbers[1].c_str()) - atoi(numbers[0].c_str()) + 1;
+    }
+  }
+  ASSERT_EQ(online_cpus, sysconf(_SC_NPROCESSORS_ONLN));
+}
+
 TEST(unistd, dup2_same) {
   // POSIX says of dup2:
   // If fildes2 is already a valid open file descriptor ...