1
2 // Copyright Oliver Kowalke 2017.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6
7 #include "boost/fiber/numa/topology.hpp"
8
9 #include <exception>
10 #include <map>
11 #include <regex>
12 #include <set>
13 #include <string>
14 #include <utility>
15
16 #include <boost/algorithm/string.hpp>
17 #include <boost/assert.hpp>
18 #include <boost/config.hpp>
19 #include <boost/filesystem.hpp>
20 #include <boost/filesystem/fstream.hpp>
21 #include <boost/format.hpp>
22
23 #include "boost/fiber/exceptions.hpp"
24
25 #ifdef BOOST_HAS_ABI_HEADERS
26 # include BOOST_ABI_PREFIX
27 #endif
28
29 #if !defined(BOOST_NO_CXX11_HDR_REGEX)
30
31 namespace al = boost::algorithm;
32 namespace fs = boost::filesystem;
33
34 namespace {
35
36 class directory_iterator {
37 private:
38 fs::directory_iterator i_;
39 fs::directory_iterator e_;
40 std::regex exp_;
41 std::pair< std::uint32_t, fs::path > idx_;
42
eval_(fs::directory_entry const & entry)43 bool eval_( fs::directory_entry const& entry) {
44 std::string filename( entry.path().filename().string() );
45 std::smatch what;
46 if ( ! std::regex_search( filename, what, exp_) ) {
47 return false;
48 }
49 idx_ = std::make_pair( std::stoul( what[1].str() ), entry.path() );
50 return true;
51 }
52
53 public:
54 typedef std::input_iterator_tag iterator_category;
55 typedef const std::pair< std::uint32_t, fs::path > value_type;
56 typedef std::ptrdiff_t difference_type;
57 typedef value_type * pointer;
58 typedef value_type & reference;
59
directory_iterator()60 directory_iterator() :
61 i_(), e_(), exp_(), idx_() {
62 }
63
directory_iterator(fs::path const & dir,std::string const & exp)64 directory_iterator( fs::path const& dir, std::string const& exp) :
65 i_( dir), e_(), exp_( exp), idx_() {
66 while ( i_ != e_ && ! eval_( * i_) ) {
67 ++i_;
68 }
69 }
70
operator ==(directory_iterator const & other)71 bool operator==( directory_iterator const& other) {
72 return i_ == other.i_;
73 }
74
operator !=(directory_iterator const & other)75 bool operator!=( directory_iterator const& other) {
76 return i_ != other.i_;
77 }
78
operator ++()79 directory_iterator & operator++() {
80 do {
81 ++i_;
82 } while ( i_ != e_ && ! eval_( * i_) );
83 return * this;
84 }
85
operator ++(int)86 directory_iterator operator++( int) {
87 directory_iterator tmp( * this);
88 ++*this;
89 return tmp;
90 }
91
operator *() const92 reference operator*() const {
93 return idx_;
94 }
95
operator ->() const96 pointer operator->() const {
97 return & idx_;
98 }
99 };
100
ids_from_line(std::string const & content)101 std::set< std::uint32_t > ids_from_line( std::string const& content) {
102 std::set< std::uint32_t > ids;
103 std::vector< std::string > v1;
104 al::split( v1, content, al::is_any_of(",") );
105 for ( std::string entry : v1) {
106 al::trim( entry);
107 std::vector< std::string > v2;
108 al::split( v2, entry, al::is_any_of("-") );
109 BOOST_ASSERT( ! v2.empty() );
110 if ( 1 == v2.size() ) {
111 // only one ID
112 ids.insert( std::stoul( v2[0]) );
113 } else {
114 // range of IDs
115 std::uint32_t first = std::stoul( * v2.begin() );
116 std::uint32_t last = std::stoul( * v2.rbegin() );
117 for ( std::uint32_t i = first; i <= last; ++i) {
118 ids.insert( i);
119 }
120 }
121 }
122 return ids;
123 }
124
distance_from_line(std::string const & content)125 std::vector< std::uint32_t > distance_from_line( std::string const& content) {
126 std::vector< std::uint32_t > distance;
127 std::vector< std::string > v1;
128 al::split( v1, content, al::is_any_of(" ") );
129 for ( std::string entry : v1) {
130 al::trim( entry);
131 BOOST_ASSERT( ! entry.empty() );
132 distance.push_back( std::stoul( entry) );
133 }
134 return distance;
135 }
136
137 }
138
139 namespace boost {
140 namespace fibers {
141 namespace numa {
142
143 BOOST_FIBERS_DECL
topology()144 std::vector< node > topology() {
145 std::vector< node > topo;
146 // 1. parse list of CPUs which are online
147 fs::ifstream fs_online{ fs::path("/sys/devices/system/cpu/online") };
148 std::string content;
149 std::getline( fs_online, content);
150 std::set< std::uint32_t > cpus = ids_from_line( content);
151 if ( cpus.empty() ) {
152 // parsing cpus failed
153 return topo;
154 }
155 std::map< std::uint32_t, node > map;
156 // iterate list of logical cpus
157 for ( std::uint32_t cpu_id : cpus) {
158 fs::path cpu_path{
159 boost::str(
160 boost::format("/sys/devices/system/cpu/cpu%d/") % cpu_id) };
161 BOOST_ASSERT( fs::exists( cpu_path) );
162 // 2. to witch NUMA node the CPU belongs to
163 directory_iterator e;
164 for ( directory_iterator i{ cpu_path, "^node([0-9]+)$" };
165 i != e; ++i) {
166 std::uint32_t node_id = i->first;
167 map[node_id].id = node_id;
168 map[node_id].logical_cpus.insert( cpu_id);
169 // assigned to only one NUMA node
170 break;
171 }
172 }
173 if ( map.empty() ) {
174 // maybe /sys/devices/system/cpu/cpu[0-9]/node[0-9] was not defined
175 // put all CPUs to NUMA node 0
176 map[0].id = 0;
177 for ( std::uint32_t cpu_id : cpus) {
178 map[0].logical_cpus.insert( cpu_id);
179 }
180 }
181 for ( auto entry : map) {
182 // NUMA-node distance
183 fs::path distance_path{
184 boost::str(
185 boost::format("/sys/devices/system/node/node%d/distance") % entry.second.id) };
186 if ( fs::exists( distance_path) ) {
187 fs::ifstream fs_distance{ distance_path };
188 std::string content;
189 std::getline( fs_distance, content);
190 entry.second.distance = distance_from_line( content);
191 topo.push_back( entry.second);
192 } else {
193 // fake NUMA distance
194 entry.second.distance.push_back( 10);
195 topo.push_back( entry.second);
196 }
197 }
198 return topo;
199 }
200
201 }}}
202
203 #else
204
205 namespace boost {
206 namespace fibers {
207 namespace numa {
208
209 #if BOOST_COMP_CLANG || \
210 BOOST_COMP_GNUC || \
211 BOOST_COMP_INTEL || \
212 BOOST_COMP_MSVC
213 # pragma message "topology() not supported without <regex>"
214 #endif
215
216 BOOST_FIBERS_DECL
topology()217 std::vector< node > topology() {
218 throw fiber_error{
219 std::make_error_code( std::errc::function_not_supported),
220 "boost fiber: topology() not supported without <regex>" };
221 return std::vector< node >{};
222 }
223
224 }}}
225
226 #endif
227
228 #ifdef BOOST_HAS_ABI_HEADERS
229 # include BOOST_ABI_SUFFIX
230 #endif
231