diff --git a/libc/bionic/sysinfo.cpp b/libc/bionic/sysinfo.cpp index a48bfea29..1cb5c79ea 100644 --- a/libc/bionic/sysinfo.cpp +++ b/libc/bionic/sysinfo.cpp @@ -33,6 +33,7 @@ #include #include +#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 index 000000000..a0cb95de0 --- /dev/null +++ b/libc/private/get_cpu_count_from_string.h @@ -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 +#include + +// 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(strtol(s, const_cast(&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; +} diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp index 8e1f41245..500377c52 100644 --- a/tests/unistd_test.cpp +++ b/tests/unistd_test.cpp @@ -30,6 +30,11 @@ #include #include +#include +#include + +#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 strs = android::base::Split(s, ","); + long online_cpus = 0; + for (auto& s : strs) { + std::vector 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 ...