blob: 410103d80924992d851e00a41b7f7b58d07de64f [file] [log] [blame]
#ifndef LIBOMP_TEST_TOPOLOGY_H
#define LIBOMP_TEST_TOPOLOGY_H
#include "libomp_test_affinity.h"
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <ctype.h>
#include <omp.h>
#include <stdarg.h>
typedef enum topology_obj_type_t {
TOPOLOGY_OBJ_THREAD,
TOPOLOGY_OBJ_CORE,
TOPOLOGY_OBJ_SOCKET,
TOPOLOGY_OBJ_MAX
} topology_obj_type_t;
typedef struct place_list_t {
int num_places;
int current_place;
int *place_nums;
affinity_mask_t **masks;
} place_list_t;
// Return the first character in file 'f' that is not a whitespace character
// including newlines and carriage returns
static int get_first_nonspace_from_file(FILE *f) {
int c;
do {
c = fgetc(f);
} while (c != EOF && (isspace(c) || c == '\n' || c == '\r'));
return c;
}
// Read an integer from file 'f' into 'number'
// Return 1 on successful read of integer,
// 0 on unsuccessful read of integer,
// EOF on end of file.
static int get_integer_from_file(FILE *f, int *number) {
int n;
n = fscanf(f, "%d", number);
if (feof(f))
return EOF;
if (n != 1)
return 0;
return 1;
}
// Read a siblings list file from Linux /sys/devices/system/cpu/cpu?/topology/*
static affinity_mask_t *topology_get_mask_from_file(const char *filename) {
int status = EXIT_SUCCESS;
FILE *f = fopen(filename, "r");
if (!f) {
perror(filename);
exit(EXIT_FAILURE);
}
affinity_mask_t *mask = affinity_mask_alloc();
while (1) {
int c, i, n, lower, upper;
// Read the first integer
n = get_integer_from_file(f, &lower);
if (n == EOF) {
break;
} else if (n == 0) {
fprintf(stderr, "syntax error: expected integer\n");
status = EXIT_FAILURE;
break;
}
// Now either a , or -
c = get_first_nonspace_from_file(f);
if (c == EOF || c == ',') {
affinity_mask_set(mask, lower);
if (c == EOF)
break;
} else if (c == '-') {
n = get_integer_from_file(f, &upper);
if (n == EOF || n == 0) {
fprintf(stderr, "syntax error: expected integer\n");
status = EXIT_FAILURE;
break;
}
for (i = lower; i <= upper; ++i)
affinity_mask_set(mask, i);
c = get_first_nonspace_from_file(f);
if (c == EOF) {
break;
} else if (c == ',') {
continue;
} else {
fprintf(stderr, "syntax error: unexpected character: '%c (%d)'\n", c,
c);
status = EXIT_FAILURE;
break;
}
} else {
fprintf(stderr, "syntax error: unexpected character: '%c (%d)'\n", c, c);
status = EXIT_FAILURE;
break;
}
}
fclose(f);
if (status == EXIT_FAILURE) {
affinity_mask_free(mask);
mask = NULL;
}
return mask;
}
static int topology_get_num_cpus() {
char buf[1024];
// Count the number of cpus
int cpu = 0;
while (1) {
snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%d", cpu);
DIR *dir = opendir(buf);
if (dir) {
closedir(dir);
cpu++;
} else {
break;
}
}
if (cpu == 0)
cpu = 1;
return cpu;
}
// Return whether the current thread has access to all logical processors
static int topology_using_full_mask() {
int cpu;
int has_all = 1;
int num_cpus = topology_get_num_cpus();
affinity_mask_t *mask = affinity_mask_alloc();
get_thread_affinity(mask);
for (cpu = 0; cpu < num_cpus; ++cpu) {
if (!affinity_mask_isset(mask, cpu)) {
has_all = 0;
break;
}
}
affinity_mask_free(mask);
return has_all;
}
// Return array of masks representing OMP_PLACES keyword (e.g., sockets, cores,
// threads)
static place_list_t *topology_alloc_type_places(topology_obj_type_t type) {
char buf[1024];
int i, cpu, num_places, num_unique;
int *place_nums;
int num_cpus = topology_get_num_cpus();
place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t));
affinity_mask_t **masks =
(affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_cpus);
num_unique = 0;
for (cpu = 0; cpu < num_cpus; ++cpu) {
affinity_mask_t *mask;
if (type == TOPOLOGY_OBJ_CORE) {
snprintf(buf, sizeof(buf),
"/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
cpu);
mask = topology_get_mask_from_file(buf);
} else if (type == TOPOLOGY_OBJ_SOCKET) {
snprintf(buf, sizeof(buf),
"/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
cpu);
mask = topology_get_mask_from_file(buf);
} else if (type == TOPOLOGY_OBJ_THREAD) {
mask = affinity_mask_alloc();
affinity_mask_set(mask, cpu);
} else {
fprintf(stderr, "Unknown topology type (%d)\n", (int)type);
exit(EXIT_FAILURE);
}
// Check for unique topology objects above the thread level
if (type != TOPOLOGY_OBJ_THREAD) {
for (i = 0; i < num_unique; ++i) {
if (affinity_mask_equal(masks[i], mask)) {
affinity_mask_free(mask);
mask = NULL;
break;
}
}
}
if (mask)
masks[num_unique++] = mask;
}
place_nums = (int *)malloc(sizeof(int) * num_unique);
for (i = 0; i < num_unique; ++i)
place_nums[i] = i;
places->num_places = num_unique;
places->masks = masks;
places->place_nums = place_nums;
places->current_place = -1;
return places;
}
static place_list_t *topology_alloc_openmp_places() {
int place, i;
int num_places = omp_get_num_places();
place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t));
affinity_mask_t **masks =
(affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_places);
int *place_nums = (int *)malloc(sizeof(int) * num_places);
for (place = 0; place < num_places; ++place) {
int num_procs = omp_get_place_num_procs(place);
int *ids = (int *)malloc(sizeof(int) * num_procs);
omp_get_place_proc_ids(place, ids);
affinity_mask_t *mask = affinity_mask_alloc();
for (i = 0; i < num_procs; ++i)
affinity_mask_set(mask, ids[i]);
masks[place] = mask;
place_nums[place] = place;
}
places->num_places = num_places;
places->place_nums = place_nums;
places->masks = masks;
places->current_place = omp_get_place_num();
return places;
}
static place_list_t *topology_alloc_openmp_partition() {
int p, i;
int num_places = omp_get_partition_num_places();
place_list_t *places = (place_list_t *)malloc(sizeof(place_list_t));
int *place_nums = (int *)malloc(sizeof(int) * num_places);
affinity_mask_t **masks =
(affinity_mask_t **)malloc(sizeof(affinity_mask_t *) * num_places);
omp_get_partition_place_nums(place_nums);
for (p = 0; p < num_places; ++p) {
int place = place_nums[p];
int num_procs = omp_get_place_num_procs(place);
int *ids = (int *)malloc(sizeof(int) * num_procs);
if (num_procs == 0) {
fprintf(stderr, "place %d has 0 procs?\n", place);
exit(EXIT_FAILURE);
}
omp_get_place_proc_ids(place, ids);
affinity_mask_t *mask = affinity_mask_alloc();
for (i = 0; i < num_procs; ++i)
affinity_mask_set(mask, ids[i]);
if (affinity_mask_count(mask) == 0) {
fprintf(stderr, "place %d has 0 procs set?\n", place);
exit(EXIT_FAILURE);
}
masks[p] = mask;
}
places->num_places = num_places;
places->place_nums = place_nums;
places->masks = masks;
places->current_place = omp_get_place_num();
return places;
}
// Free the array of masks from one of: topology_alloc_type_masks()
// or topology_alloc_openmp_masks()
static void topology_free_places(place_list_t *places) {
int i;
for (i = 0; i < places->num_places; ++i)
affinity_mask_free(places->masks[i]);
free(places->masks);
free(places->place_nums);
free(places);
}
static void topology_print_places(const place_list_t *p) {
int i;
char buf[1024];
for (i = 0; i < p->num_places; ++i) {
affinity_mask_snprintf(buf, sizeof(buf), p->masks[i]);
printf("Place %d: %s\n", p->place_nums[i], buf);
}
}
// Print out an error message, possibly with two problem place lists,
// and then exit with failure
static void proc_bind_die(omp_proc_bind_t proc_bind, int T, int P,
const char *format, ...) {
va_list args;
va_start(args, format);
const char *pb;
switch (proc_bind) {
case omp_proc_bind_false:
pb = "False";
break;
case omp_proc_bind_true:
pb = "True";
break;
case omp_proc_bind_master:
pb = "Master (Primary)";
break;
case omp_proc_bind_close:
pb = "Close";
break;
case omp_proc_bind_spread:
pb = "Spread";
break;
default:
pb = "(Unknown Proc Bind Type)";
break;
}
if (proc_bind == omp_proc_bind_spread || proc_bind == omp_proc_bind_close) {
if (T <= P) {
fprintf(stderr, "%s : (T(%d) <= P(%d)) : ", pb, T, P);
} else {
fprintf(stderr, "%s : (T(%d) > P(%d)) : ", pb, T, P);
}
} else {
fprintf(stderr, "%s : T = %d, P = %d : ", pb, T, P);
}
vfprintf(stderr, format, args);
va_end(args);
exit(EXIT_FAILURE);
}
// Return 1 on failure, 0 on success.
static void proc_bind_check(omp_proc_bind_t proc_bind,
const place_list_t *parent, place_list_t **children,
int nchildren) {
place_list_t *partition;
int T, i, j, place, low, high, first, last, count, current_place, num_places;
const int *place_nums;
int P = parent->num_places;
// Find the correct T (there could be null entries in children)
place_list_t **partitions =
(place_list_t **)malloc(sizeof(place_list_t *) * nchildren);
T = 0;
for (i = 0; i < nchildren; ++i)
if (children[i])
partitions[T++] = children[i];
// Only able to check spread, close, master (primary)
if (proc_bind != omp_proc_bind_spread && proc_bind != omp_proc_bind_close &&
proc_bind != omp_proc_bind_master)
proc_bind_die(proc_bind, T, P, NULL, NULL,
"Cannot check this proc bind type\n");
if (proc_bind == omp_proc_bind_spread) {
if (T <= P) {
// Run through each subpartition
for (i = 0; i < T; ++i) {
partition = partitions[i];
place_nums = partition->place_nums;
num_places = partition->num_places;
current_place = partition->current_place;
// Correct count?
low = P / T;
high = P / T + (P % T ? 1 : 0);
if (num_places != low && num_places != high) {
proc_bind_die(proc_bind, T, P,
"Incorrect number of places for thread %d: %d. "
"Expecting between %d and %d\n",
i, num_places, low, high);
}
// Consecutive places?
for (j = 1; j < num_places; ++j) {
if (place_nums[j] != (place_nums[j - 1] + 1) % P) {
proc_bind_die(proc_bind, T, P,
"Not consecutive places: %d, %d in partition\n",
place_nums[j - 1], place_nums[j]);
}
}
first = place_nums[0];
last = place_nums[num_places - 1];
// Primary thread executes on place of the parent thread?
if (i == 0) {
if (current_place != parent->current_place) {
proc_bind_die(
proc_bind, T, P,
"Primary thread not on same place (%d) as parent thread (%d)\n",
current_place, parent->current_place);
}
} else {
// Thread's current place is first place within it's partition?
if (current_place != first) {
proc_bind_die(proc_bind, T, P,
"Thread's current place (%d) is not the first place "
"in its partition [%d, %d]\n",
current_place, first, last);
}
}
// Partitions don't have intersections?
int f1 = first;
int l1 = last;
for (j = 0; j < i; ++j) {
int f2 = partitions[j]->place_nums[0];
int l2 = partitions[j]->place_nums[partitions[j]->num_places - 1];
if (f1 > l1 && f2 > l2) {
proc_bind_die(proc_bind, T, P,
"partitions intersect. [%d, %d] and [%d, %d]\n", f1,
l1, f2, l2);
}
if (f1 > l1 && f2 <= l2)
if (f1 < l2 || l1 > f2) {
proc_bind_die(proc_bind, T, P,
"partitions intersect. [%d, %d] and [%d, %d]\n", f1,
l1, f2, l2);
}
if (f1 <= l1 && f2 > l2)
if (f2 < l1 || l2 > f1) {
proc_bind_die(proc_bind, T, P,
"partitions intersect. [%d, %d] and [%d, %d]\n", f1,
l1, f2, l2);
}
if (f1 <= l1 && f2 <= l2)
if (!(f2 > l1 || l2 < f1)) {
proc_bind_die(proc_bind, T, P,
"partitions intersect. [%d, %d] and [%d, %d]\n", f1,
l1, f2, l2);
}
}
}
} else {
// T > P
// Each partition has only one place?
for (i = 0; i < T; ++i) {
if (partitions[i]->num_places != 1) {
proc_bind_die(
proc_bind, T, P,
"Incorrect number of places for thread %d: %d. Expecting 1\n", i,
partitions[i]->num_places);
}
}
// Correct number of consecutive threads per partition?
low = T / P;
high = T / P + (T % P ? 1 : 0);
for (i = 1, count = 1; i < T; ++i) {
if (partitions[i]->place_nums[0] == partitions[i - 1]->place_nums[0]) {
count++;
if (count > high) {
proc_bind_die(
proc_bind, T, P,
"Too many threads have place %d for their partition\n",
partitions[i]->place_nums[0]);
}
} else {
if (count < low) {
proc_bind_die(
proc_bind, T, P,
"Not enough threads have place %d for their partition\n",
partitions[i]->place_nums[0]);
}
count = 1;
}
}
// Primary thread executes on place of the parent thread?
current_place = partitions[0]->place_nums[0];
if (parent->current_place != -1 &&
current_place != parent->current_place) {
proc_bind_die(
proc_bind, T, P,
"Primary thread not on same place (%d) as parent thread (%d)\n",
current_place, parent->current_place);
}
}
} else if (proc_bind == omp_proc_bind_close ||
proc_bind == omp_proc_bind_master) {
// Check that each subpartition is the same as the parent
for (i = 0; i < T; ++i) {
partition = partitions[i];
place_nums = partition->place_nums;
num_places = partition->num_places;
current_place = partition->current_place;
if (parent->num_places != num_places) {
proc_bind_die(proc_bind, T, P,
"Number of places in subpartition (%d) does not match "
"parent (%d)\n",
num_places, parent->num_places);
}
for (j = 0; j < num_places; ++j) {
if (parent->place_nums[j] != place_nums[j]) {
proc_bind_die(proc_bind, T, P,
"Subpartition place (%d) does not match "
"parent partition place (%d)\n",
place_nums[j], parent->place_nums[j]);
}
}
}
// Find index into place_nums of current place for parent
for (j = 0; j < parent->num_places; ++j)
if (parent->place_nums[j] == parent->current_place)
break;
if (proc_bind == omp_proc_bind_close) {
if (T <= P) {
// close T <= P
// check place assignment for each thread
for (i = 0; i < T; ++i) {
partition = partitions[i];
current_place = partition->current_place;
if (current_place != parent->place_nums[j]) {
proc_bind_die(
proc_bind, T, P,
"Thread %d's current place (%d) is incorrect. expected %d\n", i,
current_place, parent->place_nums[j]);
}
j = (j + 1) % parent->num_places;
}
} else {
// close T > P
// check place assignment for each thread
low = T / P;
high = T / P + (T % P ? 1 : 0);
count = 1;
if (partitions[0]->current_place != parent->current_place) {
proc_bind_die(
proc_bind, T, P,
"Primary thread's place (%d) is not parent thread's place (%d)\n",
partitions[0]->current_place, parent->current_place);
}
for (i = 1; i < T; ++i) {
current_place = partitions[i]->current_place;
if (current_place == parent->place_nums[j]) {
count++;
if (count > high) {
proc_bind_die(
proc_bind, T, P,
"Too many threads have place %d for their current place\n",
current_place);
}
} else {
if (count < low) {
proc_bind_die(
proc_bind, T, P,
"Not enough threads have place %d for their current place\n",
parent->place_nums[j]);
}
j = (j + 1) % parent->num_places;
if (current_place != parent->place_nums[j]) {
proc_bind_die(
proc_bind, T, P,
"Thread %d's place (%d) is not corret. Expected %d\n", i,
partitions[i]->current_place, parent->place_nums[j]);
}
count = 1;
}
}
}
} else {
// proc_bind_primary
// Every thread should be assigned to the primary thread's place
for (i = 0; i < T; ++i) {
if (partitions[i]->current_place != parent->current_place) {
proc_bind_die(
proc_bind, T, P,
"Thread %d's place (%d) is not the primary thread's place (%d)\n",
i, partitions[i]->current_place, parent->current_place);
}
}
}
}
// Check that each partition's current place is within the partition
for (i = 0; i < T; ++i) {
current_place = partitions[i]->current_place;
num_places = partitions[i]->num_places;
first = partitions[i]->place_nums[0];
last = partitions[i]->place_nums[num_places - 1];
for (j = 0; j < num_places; ++j)
if (partitions[i]->place_nums[j] == current_place)
break;
if (j == num_places) {
proc_bind_die(proc_bind, T, P,
"Thread %d's current place (%d) is not within its "
"partition [%d, %d]\n",
i, current_place, first, last);
}
}
free(partitions);
}
#endif