[libc] add proc number parser and sysconf wrapper (#194159)
Add the functionality to detect number of processors with best effort. Needed by STL to detect parallelism. Assisted-by: Codex with gpt-5.4 high fast
This commit is contained in:
committed by
GitHub
parent
6f1e6e47bd
commit
bcc9a55bdb
@@ -17,6 +17,8 @@
|
||||
|
||||
#define _SC_PAGESIZE 1
|
||||
#define _SC_PAGE_SIZE _SC_PAGESIZE
|
||||
#define _SC_NPROCESSORS_CONF 83
|
||||
#define _SC_NPROCESSORS_ONLN 84
|
||||
|
||||
#define _PC_FILESIZEBITS 0
|
||||
#define _PC_LINK_MAX 1
|
||||
|
||||
@@ -14,6 +14,14 @@ macros:
|
||||
macro_header: unistd-macros.h
|
||||
- macro_name: STDERR_FILENO
|
||||
macro_header: unistd-macros.h
|
||||
- macro_name: _SC_PAGESIZE
|
||||
macro_header: unistd-macros.h
|
||||
- macro_name: _SC_PAGE_SIZE
|
||||
macro_header: unistd-macros.h
|
||||
- macro_name: _SC_NPROCESSORS_CONF
|
||||
macro_header: unistd-macros.h
|
||||
- macro_name: _SC_NPROCESSORS_ONLN
|
||||
macro_header: unistd-macros.h
|
||||
types:
|
||||
- type_name: uid_t
|
||||
- type_name: gid_t
|
||||
@@ -364,7 +372,7 @@ functions:
|
||||
- name: sysconf
|
||||
standards:
|
||||
- POSIX
|
||||
return_type: int
|
||||
return_type: long
|
||||
arguments:
|
||||
- type: int
|
||||
- name: truncate
|
||||
|
||||
@@ -38,6 +38,23 @@ add_header_library(
|
||||
libc.src.__support.threads.callonce
|
||||
)
|
||||
|
||||
add_header_library(
|
||||
sysinfo
|
||||
HDRS
|
||||
sysinfo.h
|
||||
DEPENDS
|
||||
libc.hdr.errno_macros
|
||||
libc.src.__support.CPP.array
|
||||
libc.src.__support.CPP.bit
|
||||
libc.src.__support.CPP.optional
|
||||
libc.src.__support.ctype_utils
|
||||
libc.src.__support.macros.config
|
||||
libc.src.__support.OSUtil.linux.syscall_wrappers.close
|
||||
libc.src.__support.OSUtil.linux.syscall_wrappers.open
|
||||
libc.src.__support.OSUtil.linux.syscall_wrappers.read
|
||||
libc.src.__support.OSUtil.linux.syscall_wrappers.sched_getaffinity
|
||||
)
|
||||
|
||||
add_header_library(
|
||||
vdso_sym
|
||||
HDRS
|
||||
|
||||
@@ -11,6 +11,20 @@ add_header_library(
|
||||
libc.include.sys_syscall
|
||||
)
|
||||
|
||||
add_header_library(
|
||||
sched_getaffinity
|
||||
HDRS
|
||||
sched_getaffinity.h
|
||||
DEPENDS
|
||||
libc.hdr.types.pid_t
|
||||
libc.src.__support.CPP.span
|
||||
libc.src.__support.OSUtil.osutil
|
||||
libc.src.__support.common
|
||||
libc.src.__support.error_or
|
||||
libc.src.__support.macros.config
|
||||
libc.include.sys_syscall
|
||||
)
|
||||
|
||||
add_header_library(
|
||||
close
|
||||
HDRS
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//===-- Implementation header for sched_getaffinity -----------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SCHED_GETAFFINITY_H
|
||||
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SCHED_GETAFFINITY_H
|
||||
|
||||
#include "hdr/types/pid_t.h"
|
||||
#include "src/__support/CPP/span.h"
|
||||
#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/error_or.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
|
||||
#include <sys/syscall.h> // For syscall numbers
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace linux_syscalls {
|
||||
|
||||
LIBC_INLINE ErrorOr<int> sched_getaffinity(pid_t tid,
|
||||
cpp::span<unsigned char> mask) {
|
||||
int ret =
|
||||
syscall_impl<int>(SYS_sched_getaffinity, tid, mask.size(), mask.data());
|
||||
if (ret < 0)
|
||||
return Error(-ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace linux_syscalls
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SCHED_GETAFFINITY_H
|
||||
191
libc/src/__support/OSUtil/linux/sysinfo.h
Normal file
191
libc/src/__support/OSUtil/linux/sysinfo.h
Normal file
@@ -0,0 +1,191 @@
|
||||
//===------------- Linux sysinfo support -------------------------------------//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSINFO_H
|
||||
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSINFO_H
|
||||
|
||||
#include "hdr/errno_macros.h"
|
||||
#include "src/__support/CPP/array.h"
|
||||
#include "src/__support/CPP/bit.h"
|
||||
#include "src/__support/CPP/optional.h"
|
||||
#include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
|
||||
#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
|
||||
#include "src/__support/OSUtil/linux/syscall_wrappers/read.h"
|
||||
#include "src/__support/OSUtil/linux/syscall_wrappers/sched_getaffinity.h"
|
||||
#include "src/__support/ctype_utils.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace sysinfo {
|
||||
|
||||
LIBC_INLINE_VAR constexpr char POSSIBLE_NPROC_PATH[] =
|
||||
"/sys/devices/system/cpu/possible";
|
||||
LIBC_INLINE_VAR constexpr char ONLINE_NPROC_PATH[] =
|
||||
"/sys/devices/system/cpu/online";
|
||||
|
||||
// Parses Linux CPU-list syntax:
|
||||
// list := item (',' item)*
|
||||
// item := number | number '-' number
|
||||
// number := [0-9]+
|
||||
class ProcParser {
|
||||
enum class ProcParserState {
|
||||
ParseUnstarted,
|
||||
ParseNumber,
|
||||
ParseRangeSeparator,
|
||||
ParseRangeEnd
|
||||
};
|
||||
|
||||
ProcParserState state;
|
||||
cpp::array<char, 128> buffer;
|
||||
int fd;
|
||||
size_t cursor;
|
||||
size_t buffer_end;
|
||||
size_t cpu_count;
|
||||
size_t current_number;
|
||||
size_t range_start;
|
||||
bool has_error;
|
||||
|
||||
LIBC_INLINE static int open_path(const char *path) {
|
||||
ErrorOr<int> open_result =
|
||||
linux_syscalls::open(path, O_RDONLY | O_CLOEXEC, 0);
|
||||
return open_result ? *open_result : -1;
|
||||
}
|
||||
|
||||
LIBC_INLINE cpp::optional<char> next_char() {
|
||||
if (fd < 0)
|
||||
return cpp::nullopt;
|
||||
|
||||
while (cursor == buffer_end) {
|
||||
ErrorOr<ssize_t> bytes_read =
|
||||
linux_syscalls::read(fd, buffer.data(), buffer.size());
|
||||
if (!bytes_read) {
|
||||
if (bytes_read.error() == EINTR)
|
||||
continue;
|
||||
has_error = true;
|
||||
return cpp::nullopt;
|
||||
}
|
||||
|
||||
if (*bytes_read == 0)
|
||||
return cpp::nullopt;
|
||||
|
||||
cursor = 0;
|
||||
buffer_end = static_cast<size_t>(*bytes_read);
|
||||
}
|
||||
|
||||
return buffer[cursor++];
|
||||
}
|
||||
|
||||
LIBC_INLINE bool finish_group() {
|
||||
if (state == ProcParserState::ParseUnstarted)
|
||||
return true;
|
||||
if (state == ProcParserState::ParseRangeSeparator)
|
||||
return false;
|
||||
|
||||
if (state == ProcParserState::ParseRangeEnd) {
|
||||
if (current_number < range_start)
|
||||
return false;
|
||||
cpu_count += current_number - range_start + 1;
|
||||
} else {
|
||||
++cpu_count;
|
||||
}
|
||||
|
||||
current_number = 0;
|
||||
range_start = 0;
|
||||
state = ProcParserState::ParseUnstarted;
|
||||
return true;
|
||||
}
|
||||
|
||||
LIBC_INLINE bool consume(char ch) {
|
||||
if (internal::isdigit(ch)) {
|
||||
// Not using internal::strtointeger here because a number can be across
|
||||
// two reads in rare cases.
|
||||
current_number = current_number * 10 + static_cast<size_t>(ch - '0');
|
||||
if (state == ProcParserState::ParseUnstarted)
|
||||
state = ProcParserState::ParseNumber;
|
||||
else if (state == ProcParserState::ParseRangeSeparator)
|
||||
state = ProcParserState::ParseRangeEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ch == '-') {
|
||||
if (state != ProcParserState::ParseNumber)
|
||||
return false;
|
||||
range_start = current_number;
|
||||
current_number = 0;
|
||||
state = ProcParserState::ParseRangeSeparator;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ch == ',' || ch == '\n')
|
||||
return finish_group();
|
||||
|
||||
if (ch == ' ' || ch == '\t' || ch == '\r')
|
||||
return state == ProcParserState::ParseUnstarted ? true : finish_group();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
// Using string view isn't exactly correct because we demands null-terminated
|
||||
// strings.
|
||||
LIBC_INLINE explicit ProcParser(const char *path)
|
||||
: state(ProcParserState::ParseUnstarted), buffer{}, fd(open_path(path)),
|
||||
cursor(buffer.size()), buffer_end(buffer.size()), cpu_count(0),
|
||||
current_number(0), range_start(0), has_error(fd < 0) {}
|
||||
|
||||
LIBC_INLINE ~ProcParser() {
|
||||
if (fd >= 0)
|
||||
linux_syscalls::close(fd);
|
||||
}
|
||||
|
||||
LIBC_INLINE cpp::optional<size_t> parse() {
|
||||
if (fd < 0)
|
||||
return cpp::nullopt;
|
||||
|
||||
while (cpp::optional<char> ch = next_char())
|
||||
if (!consume(*ch))
|
||||
return cpp::nullopt;
|
||||
|
||||
if (has_error)
|
||||
return cpp::nullopt;
|
||||
|
||||
if (!finish_group())
|
||||
return cpp::nullopt;
|
||||
|
||||
if (cpu_count == 0)
|
||||
return cpp::nullopt;
|
||||
return cpu_count;
|
||||
}
|
||||
};
|
||||
|
||||
LIBC_INLINE cpp::optional<size_t> parse_nproc_from(const char *path) {
|
||||
return ProcParser(path).parse();
|
||||
}
|
||||
|
||||
LIBC_INLINE size_t parse_nproc_with_fallback_from(const char *path) {
|
||||
if (cpp::optional<size_t> cpu_count = parse_nproc_from(path))
|
||||
return *cpu_count;
|
||||
|
||||
cpp::array<unsigned char, 128> mask_buffer = {};
|
||||
|
||||
ErrorOr<int> affinity_result =
|
||||
linux_syscalls::sched_getaffinity(0, mask_buffer);
|
||||
if (!affinity_result)
|
||||
return 1;
|
||||
|
||||
size_t cpu_count = 0;
|
||||
for (unsigned char byte : mask_buffer)
|
||||
cpu_count += static_cast<size_t>(cpp::popcount(byte));
|
||||
|
||||
return cpu_count > 0 ? cpu_count : 1;
|
||||
}
|
||||
|
||||
} // namespace sysinfo
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_LINUX_SYSINFO_H
|
||||
@@ -612,9 +612,11 @@ add_entrypoint_object(
|
||||
HDRS
|
||||
../sysconf.h
|
||||
DEPENDS
|
||||
libc.include.unistd
|
||||
libc.include.sys_auxv
|
||||
libc.hdr.unistd_macros
|
||||
libc.hdr.sys_auxv_macros
|
||||
libc.src.__support.libc_errno
|
||||
libc.src.__support.macros.config
|
||||
libc.src.__support.OSUtil.linux.sysinfo
|
||||
libc.src.__support.OSUtil.linux.auxv
|
||||
)
|
||||
|
||||
|
||||
@@ -10,28 +10,35 @@
|
||||
|
||||
#include "src/__support/common.h"
|
||||
|
||||
#include "hdr/sys_auxv_macros.h"
|
||||
#include "hdr/unistd_macros.h"
|
||||
#include "src/__support/OSUtil/linux/auxv.h"
|
||||
#include "src/__support/OSUtil/linux/sysinfo.h"
|
||||
#include "src/__support/libc_errno.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
LLVM_LIBC_FUNCTION(long, sysconf, (int name)) {
|
||||
long ret = 0;
|
||||
if (name == _SC_PAGESIZE) {
|
||||
cpp::optional<unsigned long> page_size = auxv::get(AT_PAGESZ);
|
||||
if (page_size)
|
||||
return static_cast<long>(*page_size);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
// TODO: Complete the rest of the sysconf options.
|
||||
if (ret < 0) {
|
||||
libc_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
|
||||
if (name == _SC_NPROCESSORS_CONF)
|
||||
return static_cast<long>(
|
||||
sysinfo::parse_nproc_with_fallback_from(sysinfo::POSSIBLE_NPROC_PATH));
|
||||
|
||||
if (name == _SC_NPROCESSORS_ONLN)
|
||||
return static_cast<long>(
|
||||
sysinfo::parse_nproc_with_fallback_from(sysinfo::ONLINE_NPROC_PATH));
|
||||
|
||||
// TODO: Complete the rest of the sysconf options.
|
||||
libc_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
@@ -2,6 +2,19 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
|
||||
add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
|
||||
endif()
|
||||
|
||||
add_libc_test(
|
||||
sysinfo_test
|
||||
SUITE libc-osutil-tests
|
||||
SRCS sysinfo_test.cpp
|
||||
DEPENDS
|
||||
libc.src.__support.CPP.string_view
|
||||
libc.src.__support.OSUtil.linux.sysinfo
|
||||
libc.src.fcntl.open
|
||||
libc.src.unistd.close
|
||||
libc.src.unistd.unlink
|
||||
libc.src.unistd.write
|
||||
)
|
||||
|
||||
add_libc_test(
|
||||
vdso_test
|
||||
SUITE libc-osutil-tests
|
||||
|
||||
93
libc/test/src/__support/OSUtil/linux/sysinfo_test.cpp
Normal file
93
libc/test/src/__support/OSUtil/linux/sysinfo_test.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//===-- Unittests for Linux sysinfo support -------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/__support/CPP/string_view.h"
|
||||
#include "src/__support/OSUtil/linux/sysinfo.h"
|
||||
#include "src/fcntl/open.h"
|
||||
#include "src/unistd/close.h"
|
||||
#include "src/unistd/unlink.h"
|
||||
#include "src/unistd/write.h"
|
||||
#include "test/UnitTest/Test.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
static int write_test_file(const char *path, cpp::string_view contents) {
|
||||
int fd = LIBC_NAMESPACE::open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (LIBC_NAMESPACE::write(fd, contents.data(), contents.size()) !=
|
||||
static_cast<ssize_t>(contents.size())) {
|
||||
LIBC_NAMESPACE::close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return LIBC_NAMESPACE::close(fd);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcOSUtilSysinfoTest, PossibleCpuCountSmokeTest) {
|
||||
cpp::optional<size_t> parsed =
|
||||
sysinfo::parse_nproc_from(sysinfo::POSSIBLE_NPROC_PATH);
|
||||
ASSERT_TRUE(static_cast<bool>(parsed));
|
||||
EXPECT_GT(*parsed, size_t(0));
|
||||
EXPECT_GT(
|
||||
sysinfo::parse_nproc_with_fallback_from(sysinfo::POSSIBLE_NPROC_PATH),
|
||||
size_t(0));
|
||||
}
|
||||
|
||||
TEST(LlvmLibcOSUtilSysinfoTest, OnlineCpuCountSmokeTest) {
|
||||
cpp::optional<size_t> parsed =
|
||||
sysinfo::parse_nproc_from(sysinfo::ONLINE_NPROC_PATH);
|
||||
ASSERT_TRUE(static_cast<bool>(parsed));
|
||||
EXPECT_GT(*parsed, size_t(0));
|
||||
EXPECT_GT(sysinfo::parse_nproc_with_fallback_from(sysinfo::ONLINE_NPROC_PATH),
|
||||
size_t(0));
|
||||
}
|
||||
|
||||
TEST(LlvmLibcOSUtilSysinfoTest, SyntheticCpuLists) {
|
||||
constexpr char FILENAME[] =
|
||||
APPEND_LIBC_TEST("sysinfo.synthetic_cpu_list.test");
|
||||
CString test_file = libc_make_test_file_path(FILENAME);
|
||||
|
||||
struct TestCase {
|
||||
cpp::string_view contents;
|
||||
cpp::optional<size_t> expected;
|
||||
};
|
||||
|
||||
constexpr TestCase TEST_CASES[] = {
|
||||
{"0\n", 1},
|
||||
{"0-7\n", 8},
|
||||
{"0-0,2,4-6\n", 5},
|
||||
{"0-3,8-11\n", 8},
|
||||
{"0-3,8-11,16\n", 9},
|
||||
{"1,2,3,4-9,99\n", 10},
|
||||
{"3-1\n", cpp::nullopt},
|
||||
{"0-\n", cpp::nullopt},
|
||||
};
|
||||
|
||||
for (const TestCase &test_case : TEST_CASES) {
|
||||
ASSERT_EQ(write_test_file(test_file, test_case.contents), 0);
|
||||
cpp::optional<size_t> parsed = sysinfo::parse_nproc_from(test_file);
|
||||
EXPECT_EQ(static_cast<bool>(parsed), static_cast<bool>(test_case.expected));
|
||||
if (parsed)
|
||||
EXPECT_EQ(*parsed, *test_case.expected);
|
||||
EXPECT_GT(sysinfo::parse_nproc_with_fallback_from(test_file), size_t(0));
|
||||
}
|
||||
|
||||
ASSERT_EQ(LIBC_NAMESPACE::unlink(test_file), 0);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcOSUtilSysinfoTest, NonexistentPath) {
|
||||
constexpr const char *TEST_PATH =
|
||||
"/not-exist-at-all-path-for-libc-nproc-test";
|
||||
|
||||
EXPECT_FALSE(static_cast<bool>(sysinfo::parse_nproc_from(TEST_PATH)));
|
||||
EXPECT_GT(sysinfo::parse_nproc_with_fallback_from(TEST_PATH), size_t(0));
|
||||
}
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
@@ -15,3 +15,13 @@ TEST(LlvmLibcSysconfTest, PagesizeTest) {
|
||||
long pagesize = LIBC_NAMESPACE::sysconf(_SC_PAGESIZE);
|
||||
ASSERT_GT(pagesize, 0l);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcSysconfTest, NprocessorsConfTest) {
|
||||
long sysconf_count = LIBC_NAMESPACE::sysconf(_SC_NPROCESSORS_CONF);
|
||||
ASSERT_GT(sysconf_count, 0l);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcSysconfTest, NprocessorsOnlnTest) {
|
||||
long sysconf_count = LIBC_NAMESPACE::sysconf(_SC_NPROCESSORS_ONLN);
|
||||
ASSERT_GT(sysconf_count, 0l);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user