Skip to content

Commit 7076d1e

Browse files
author
ndenoyelle
committed
add tool and tests for binding threads according to a set of locations
1 parent a1b0f2c commit 7076d1e

File tree

5 files changed

+1030
-0
lines changed

5 files changed

+1030
-0
lines changed

utils/hwloc/Makefile.am

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,24 @@ endif HWLOC_HAVE_LINUX
169169

170170
distclean-local:
171171
rm -f $(nodist_man_MANS)
172+
173+
# Build hwloc-thread-bind
174+
if HWLOC_HAVE_PTRACE
175+
if HWLOC_HAVE_PTHREAD
176+
AM_LDFLAGS+=-lpthread
177+
endif #HWLOC_HAVE_PHREAD
178+
noinst_LTLIBRARIES+=libhwloc-thread-bind.la
179+
libhwloc_thread_bind_la_SOURCES=hwloc-thread-bind-utils.c hwloc-thread-bind.h
180+
LIBDADD=$(HWLOC_top_builddir)/hwloc/libhwloc.la
181+
182+
bin_PROGRAMS += hwloc-thread-bind
183+
hwloc_thread_bind_SOURCES = \
184+
hwloc-thread-bind.c \
185+
hwloc-thread-bind.h
186+
LDADD+=$(HWLOC_top_builddir)/hwloc/libhwloc.la
187+
LDADD+=libhwloc-thread-bind.la
188+
check_PROGRAMS=test-hwloc-thread-bind
189+
if !HWLOC_HAVE_MINGW32
190+
TESTS+=test-hwloc-thread-bind
191+
endif #!HWLOC_HAVE_MINGW32
192+
endif #HWLOC_HAVE_PTRACE

utils/hwloc/hwloc-thread-bind-utils.c

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <sys/wait.h>
4+
#include <sys/types.h>
5+
#include <unistd.h>
6+
#include "private/autogen/config.h"
7+
#if HWLOC_HAVE_PTRACE
8+
#include <sys/ptrace.h>
9+
#endif
10+
#include "hwloc-thread-bind.h"
11+
#include "hwloc/helper.h"
12+
#include "misc.h"
13+
#include "hwloc-calc.h"
14+
15+
extern int logical; // Whether indexing is logical
16+
extern int verbose; // Whether to verbose print.
17+
18+
/** Maximum len of string containing a hwloc_obj logical index **/
19+
#define STR_OBJ_MAX 32
20+
21+
/**********************************************************************/
22+
/* enum structure */
23+
/**********************************************************************/
24+
25+
struct cpuaffinity_enum{
26+
/** The topology used to build enum **/
27+
hwloc_topology_t topology;
28+
/** Index of current hwloc_obj in enumeration **/
29+
unsigned current;
30+
/** Number of hwloc_obj in enumeration **/
31+
unsigned n;
32+
/**
33+
* Maximum number of hwloc_obj storable.
34+
* This the number of HWLOC_OBJ_PU in topology.
35+
**/
36+
unsigned nmax;
37+
/**
38+
* Array of processing units.
39+
* These are the pointers from
40+
* an existing topology. Topology must not
41+
* be destroyed until the enum is.
42+
**/
43+
hwloc_obj_t *obj;
44+
};
45+
46+
struct cpuaffinity_enum *
47+
cpuaffinity_enum_alloc(hwloc_topology_t topology)
48+
{
49+
unsigned i, nmax = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_PU);
50+
51+
struct cpuaffinity_enum *obj = malloc(sizeof *obj);
52+
53+
if (obj == NULL)
54+
return NULL;
55+
56+
obj->topology = topology;
57+
obj->obj = malloc(nmax * sizeof(*obj->obj));
58+
if (obj->obj == NULL) {
59+
free(obj);
60+
return NULL;
61+
}
62+
63+
for (i = 0; i < nmax; i++)
64+
obj->obj[i] = NULL;
65+
obj->n = 0;
66+
obj->nmax = nmax;
67+
obj->current = 0;
68+
69+
return obj;
70+
}
71+
72+
void
73+
cpuaffinity_enum_free(struct cpuaffinity_enum *obj)
74+
{
75+
free(obj->obj);
76+
free(obj);
77+
}
78+
79+
size_t
80+
cpuaffinity_enum_size(struct cpuaffinity_enum *obj)
81+
{
82+
return obj->n;
83+
}
84+
85+
int
86+
cpuaffinity_enum_append(struct cpuaffinity_enum *e, hwloc_obj_t obj)
87+
{
88+
unsigned i;
89+
if (e == NULL)
90+
goto out_einval;
91+
92+
if (e->n == e->nmax)
93+
goto out_edom;
94+
95+
if (obj == NULL || (e->obj[0] && obj->type != e->obj[0]->type))
96+
goto out_einval;
97+
98+
for (i = 0; i < e->n; i++)
99+
if (e->obj[i]->logical_index == obj->logical_index)
100+
goto out_einval;
101+
102+
e->obj[e->n++] = obj;
103+
return 0;
104+
out_einval:
105+
errno = EINVAL;
106+
return -1;
107+
out_edom:
108+
errno = EDOM;
109+
return -1;
110+
}
111+
112+
hwloc_obj_t
113+
cpuaffinity_enum_next(struct cpuaffinity_enum *e)
114+
{
115+
hwloc_obj_t obj = e->obj[e->current];
116+
e->current = (e->current + 1) % e->n;
117+
return obj;
118+
}
119+
120+
hwloc_obj_t
121+
cpuaffinity_enum_get(struct cpuaffinity_enum * e,
122+
const size_t i)
123+
{
124+
if(e == NULL)
125+
return NULL;
126+
return e->obj[i % e->n];
127+
}
128+
129+
static int cpuaffinity_obj_snprintf(char* str,
130+
const size_t len,
131+
const char *sep,
132+
const hwloc_obj_t obj,
133+
const int cpuset,
134+
const int taskset)
135+
{
136+
char* c = str;
137+
int index = logical ? obj->logical_index : obj->os_index;
138+
if(taskset)
139+
c+=hwloc_bitmap_taskset_snprintf(str, len, obj->cpuset);
140+
else if(cpuset)
141+
c+=hwloc_bitmap_snprintf(str, len, obj->cpuset);
142+
else
143+
c += snprintf(c, len, "%d", index);
144+
c += snprintf(c, c-str+len, "%s", sep);
145+
return c-str;
146+
}
147+
148+
void cpuaffinity_enum_print(const struct cpuaffinity_enum *e,
149+
const char *sep,
150+
const int cpuset,
151+
const int taskset,
152+
const int reverse,
153+
unsigned num)
154+
{
155+
size_t len = e->n * (strlen(sep) + STR_OBJ_MAX);
156+
char *c, *enum_str = malloc(len);
157+
int i, start, end;
158+
num = num == 0 ? e->n : num;
159+
num = num > e->n ? e->n : num;
160+
161+
if (enum_str == NULL) {
162+
errno = ENOMEM;
163+
return;
164+
}
165+
166+
memset(enum_str, 0, len);
167+
c = enum_str;
168+
169+
start = reverse ? num-1 : 0;
170+
end = reverse ? 0 : num-1;
171+
for (i = start; i != end; reverse ? i-- : i++){
172+
c+=cpuaffinity_obj_snprintf(c,
173+
len + enum_str - c,
174+
sep,
175+
e->obj[i],
176+
cpuset,
177+
taskset);
178+
}
179+
c+=cpuaffinity_obj_snprintf(c,
180+
len + enum_str - c,
181+
"",
182+
e->obj[i],
183+
cpuset,
184+
taskset);
185+
printf("%s\n", enum_str);
186+
free(enum_str);
187+
}
188+
189+
hwloc_obj_t
190+
cpuaffinity_bind_thread(struct cpuaffinity_enum * objs,
191+
const pid_t tid)
192+
{
193+
hwloc_obj_t obj = cpuaffinity_enum_next(objs);
194+
195+
if(hwloc_set_proc_cpubind(objs->topology,
196+
tid,
197+
obj->cpuset,
198+
HWLOC_CPUBIND_THREAD) == -1){
199+
perror("hwloc_set_cpubind");
200+
return NULL;
201+
}
202+
return obj;
203+
}
204+
205+
#if HWLOC_HAVE_PTRACE
206+
int
207+
cpuaffinity_attach(const pid_t pid,
208+
struct cpuaffinity_enum *e,
209+
const int repeat,
210+
const int stopped)
211+
{
212+
const struct hwloc_topology_support * support =
213+
hwloc_topology_get_support(e->topology);
214+
if(!support->cpubind->set_thread_cpubind){
215+
fprintf(stderr,
216+
"cpuaffinity_attach: set_thread_cpubind not supported.\n");
217+
return -1;
218+
}
219+
220+
/* Wait for child to stop */
221+
if(!stopped)
222+
kill(pid, SIGSTOP);
223+
waitpid(pid, NULL, WUNTRACED);
224+
225+
/* attach and set options to trace threads creation and process exit */
226+
if(ptrace(PTRACE_SEIZE,
227+
pid,
228+
NULL,
229+
(void*)(PTRACE_O_TRACECLONE|PTRACE_O_TRACEFORK)) == -1){
230+
perror("PTRACE_SEIZE");
231+
return -1;
232+
}
233+
234+
/* Resume stopped child */
235+
kill(pid, SIGCONT);
236+
237+
/* wait childrens until process exits */
238+
do{
239+
int status;
240+
pid_t child = waitpid(-1, &status, __WALL);
241+
if(WIFEXITED(status) && child == pid){ return WEXITSTATUS(status);}
242+
if(WIFSIGNALED(status) && child == pid){break;}
243+
244+
/* Child Stopped */
245+
if(WIFSTOPPED(status)){
246+
int sig = WSTOPSIG(status);
247+
if(sig == SIGTRAP){
248+
/*Get ptrace event that triggered the stop*/
249+
int event = status >> 8;
250+
unsigned long eventmsg;
251+
252+
if(ptrace(PTRACE_GETEVENTMSG,
253+
child,
254+
NULL,
255+
(void*)(&eventmsg)) == -1){
256+
perror("PTRACE_GETEVENTMSG");
257+
continue;
258+
}
259+
if(event == (SIGTRAP|(PTRACE_EVENT_FORK<<8)) ||
260+
event == (SIGTRAP|(PTRACE_EVENT_VFORK<<8)) ||
261+
event == (SIGTRAP|(PTRACE_EVENT_CLONE<<8))){
262+
if(e->current < e->n || repeat){
263+
if(verbose){
264+
hwloc_obj_t loc = e->obj[e->current];
265+
printf("Binding thread %lu to %s:%d\n",
266+
eventmsg,
267+
hwloc_obj_type_string(loc->type),
268+
logical ? loc->logical_index
269+
: loc->os_index);
270+
}
271+
cpuaffinity_bind_thread(e, eventmsg);
272+
}
273+
}
274+
}
275+
/* Resume stopped child */
276+
if(ptrace(PTRACE_CONT, child, NULL, NULL) == -1) {
277+
perror("PTRACE_CONT(interrupt)");
278+
}
279+
}
280+
} while(1);
281+
return 0;
282+
}
283+
#endif // HWLOC_HAVE_PTRACE
284+
285+
hwloc_cpuset_t
286+
hwloc_process_location(hwloc_topology_t topology,
287+
const char* str)
288+
{
289+
int err;
290+
hwloc_bitmap_t cpuset;
291+
292+
cpuset = hwloc_bitmap_alloc();
293+
if(cpuset == NULL){
294+
perror("hwloc_bitmap_alloc");
295+
exit(1);
296+
}
297+
298+
struct hwloc_calc_location_context_s lcontext = {
299+
.topology = topology,
300+
.topodepth = hwloc_topology_get_depth(topology),
301+
.only_hbm = -1,
302+
.logical = logical,
303+
.verbose = 0
304+
};
305+
struct hwloc_calc_set_context_s scontext = {
306+
.nodeset_input = 0,
307+
.nodeset_output = 0,
308+
.output_set = cpuset,
309+
};
310+
311+
err = hwloc_calc_process_location_as_set(&lcontext,
312+
&scontext,
313+
str);
314+
315+
if(err < 0){
316+
fprintf(stderr,
317+
"Obj %s is not recognized or does not contain a cpuset.\n",
318+
str);
319+
return NULL;
320+
}
321+
322+
return cpuset;
323+
}
324+
325+
int
326+
restrict_topology(hwloc_topology_t topology,
327+
const char *restrict_str)
328+
{
329+
int err = 0;
330+
hwloc_bitmap_t restrict_cpuset;
331+
332+
restrict_cpuset = hwloc_process_location(topology, restrict_str);
333+
if(restrict_cpuset == NULL)
334+
return -1;
335+
336+
hwloc_topology_restrict(topology,
337+
restrict_cpuset,
338+
HWLOC_RESTRICT_FLAG_REMOVE_CPULESS);
339+
if(err != 0)
340+
perror("hwloc_topology_restrict");
341+
342+
hwloc_bitmap_free(restrict_cpuset);
343+
return err;
344+
}

0 commit comments

Comments
 (0)