Hi! The following patch implements functions and env vars to query and display affinity related information.
Tested on x86_64-linux, committed to gomp-5.0 branch. 2018-05-23 Jakub Jelinek <ja...@redhat.com> * configure.ac (HAVE_UNAME, HAVE_GETHOSTNAME, HAVE_GETPID): Add new tests. * configure.tgt: Add -DUSING_INITIAL_EXEC_TLS to XCFLAGS for Linux. * Makefile.am (libgomp_la_SOURCES): Add affinity-fmt.c. * libgomp.map (OMP_5.0): Export omp_{capture,display}_affinity{,_}, and omp_[gs]et_affinity_format{,_}. * libgomp.h (gomp_display_affinity_var, gomp_affinity_format_var, gomp_affinity_format_len): Declare. (GOMP_NEEDS_THREAD_HANDLE): Define if needed. (struct gomp_thread): Add handle field if GOMP_NEEDS_THREAD_HANDLE is defined. (gomp_display_affinity_place): Declare. (gomp_set_affinity_format, gomp_display_string): Likewise. (gomp_thread_handle): New typedef. (gomp_display_affinity, gomp_display_affinity_thread): Declare. (gomp_thread_self, gomp_thread_to_pthread_t): New inline functions. * affinity-fmt.c: New file. * affinity.c (gomp_display_affinity_place): New function. * config/linux/affinity.c (gomp_display_affinity_place): New function. * env.c (gomp_display_affinity_var, gomp_affinity_format_var, gomp_affinity_format_len): New variables. (handle_omp_display_env): Print OMP_DISPLAY_AFFINITY and OMP_AFFINITY_FORMAT. (initialize_env): Handle OMP_DISPLAY_AFFINITY and OMP_AFFINITY_FORMAT env vars. * fortran.c: Include stdio.h and string.h. (omp_set_affinity_format_, omp_get_affinity_format_, omp_display_affinity_, omp_capture_affinity_): New functions. * omp.h.in (omp_set_affinity_format, omp_get_affinity_format, omp_display_affinity, omp_capture_affinity): Declare. * omp_lib.f90.in (omp_set_affinity_format, omp_get_affinity_format, omp_display_affinity, omp_capture_affinity): Add new interfaces. * omp_lib.h.in (omp_set_affinity_format, omp_get_affinity_format, omp_display_affinity, omp_capture_affinity): New externs. * team.c (struct gomp_thread_start_data): Add handle field. (gomp_team_start): Handle OMP_DISPLAY_AFFINITY env var. * configure: Regenerated. * config.h.in: Regenerated. * Makefile.in: Regenerated. * testsuite/libgomp.c-c++-common/display-affinity-1.c: New test. * testsuite/libgomp.fortran/display-affinity-1.f90: New test. --- libgomp/configure.ac.jj 2018-04-30 13:19:48.198834863 +0200 +++ libgomp/configure.ac 2018-05-22 14:15:24.425935883 +0200 @@ -266,6 +266,41 @@ if test $ac_cv_func_clock_gettime = no; [Define to 1 if you have the `clock_gettime' function.])]) fi +# Check for uname. +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include <string.h> + #include <stdlib.h> + #include <sys/utsname.h>], + [struct utsname buf; + volatile size_t len = 0; + if (!uname (buf)) + len = strlen (buf.nodename);])], + AC_DEFINE(HAVE_UNAME, 1, +[ Define if uname is supported and struct utsname has nodename field.])) + +# Check for gethostname. +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include <unistd.h>], + [ +changequote(,)dnl + char buf[256]; + if (gethostname (buf, sizeof (buf) - 1) == 0) + buf[255] = '\0'; +changequote([,])dnl + ])], + AC_DEFINE(HAVE_GETHOSTNAME, 1, +[ Define if gethostname is supported.])) + +# Check for getpid. +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include <unistd.h>], + [int pid = getpid ();])], + AC_DEFINE(HAVE_GETPID, 1, +[ Define if getpid is supported.])) + # See if we support thread-local storage. GCC_CHECK_TLS --- libgomp/configure.tgt.jj 2017-05-04 15:04:53.677371383 +0200 +++ libgomp/configure.tgt 2018-05-23 14:34:01.875414884 +0200 @@ -18,7 +18,7 @@ if test $gcc_cv_have_tls = yes ; then ;; *-*-linux* | *-*-gnu*) - XCFLAGS="${XCFLAGS} -ftls-model=initial-exec" + XCFLAGS="${XCFLAGS} -ftls-model=initial-exec -DUSING_INITIAL_EXEC_TLS" ;; *-*-rtems*) --- libgomp/Makefile.am.jj 2017-05-04 15:04:53.679371358 +0200 +++ libgomp/Makefile.am 2018-05-21 17:21:48.717963247 +0200 @@ -63,7 +63,8 @@ libgomp_la_SOURCES = alloc.c atomic.c ba parallel.c sections.c single.c task.c team.c work.c lock.c mutex.c \ proc.c sem.c bar.c ptrlock.c time.c fortran.c affinity.c target.c \ splay-tree.c libgomp-plugin.c oacc-parallel.c oacc-host.c oacc-init.c \ - oacc-mem.c oacc-async.c oacc-plugin.c oacc-cuda.c priority_queue.c + oacc-mem.c oacc-async.c oacc-plugin.c oacc-cuda.c priority_queue.c \ + affinity-fmt.c include $(top_srcdir)/plugin/Makefrag.am --- libgomp/libgomp.map.jj 2018-04-30 13:19:48.356834924 +0200 +++ libgomp/libgomp.map 2018-05-23 14:08:47.362850982 +0200 @@ -164,6 +164,18 @@ OMP_4.5 { omp_target_disassociate_ptr; } OMP_4.0; +OMP_5.0 { + global: + omp_capture_affinity; + omp_capture_affinity_; + omp_display_affinity; + omp_display_affinity_; + omp_get_affinity_format; + omp_get_affinity_format_; + omp_set_affinity_format; + omp_set_affinity_format_; +} OMP_4.5; + GOMP_1.0 { global: GOMP_atomic_end; --- libgomp/libgomp.h.jj 2018-04-30 13:21:05.440865896 +0200 +++ libgomp/libgomp.h 2018-05-23 16:20:40.149705792 +0200 @@ -365,6 +365,9 @@ extern void **gomp_places_list; extern unsigned long gomp_places_list_len; extern unsigned int gomp_num_teams_var; extern int gomp_debug_var; +extern bool gomp_display_affinity_var; +extern char *gomp_affinity_format_var; +extern size_t gomp_affinity_format_len; extern int goacc_device_num; extern char *goacc_device_type; @@ -613,6 +616,19 @@ struct gomp_thread /* User pthread thread pool */ struct gomp_thread_pool *thread_pool; + +#if defined(LIBGOMP_USE_PTHREADS) \ + && (!defined(HAVE_TLS) \ + || !defined(__GLIBC__) \ + || !defined(USING_INITIAL_EXEC_TLS)) + /* pthread_t of the thread containing this gomp_thread. + On Linux when using initial-exec TLS, + (typeof (pthread_t)) gomp_thread () - pthread_self () + is constant in all threads, so we can optimize and not + store it. */ +#define GOMP_NEEDS_THREAD_HANDLE 1 + pthread_t handle; +#endif }; @@ -709,6 +725,24 @@ extern bool gomp_affinity_finalize_place extern bool gomp_affinity_init_level (int, unsigned long, bool); extern void gomp_affinity_print_place (void *); extern void gomp_get_place_proc_ids_8 (int, int64_t *); +extern void gomp_display_affinity_place (char *, size_t, size_t *, int); + +/* affinity-fmt.c */ + +extern void gomp_set_affinity_format (const char *, size_t); +extern void gomp_display_string (char *, size_t, size_t *, const char *, + size_t); +#ifdef LIBGOMP_USE_PTHREADS +typedef pthread_t gomp_thread_handle; +#else +typedef struct {} gomp_thread_handle; +#endif +extern size_t gomp_display_affinity (char *, size_t, const char *, + gomp_thread_handle, + struct gomp_team_state *, unsigned int); +extern void gomp_display_affinity_thread (gomp_thread_handle, + struct gomp_team_state *, + unsigned int) __attribute__((cold)); /* iter.c */ @@ -1131,4 +1165,42 @@ task_to_priority_node (enum priority_que return (struct priority_node *) ((char *) task + priority_queue_offset (type)); } + +#ifdef LIBGOMP_USE_PTHREADS +static inline gomp_thread_handle +gomp_thread_self (void) +{ + return pthread_self (); +} + +static inline gomp_thread_handle +gomp_thread_to_pthread_t (struct gomp_thread *thr) +{ + struct gomp_thread *this_thr = gomp_thread (); + if (thr == this_thr) + return pthread_self (); +#ifdef GOMP_NEEDS_THREAD_HANDLE + return thr->handle; +#else + /* On Linux with initial-exec TLS, the pthread_t of the thread containing + thr can be computed from thr, this_thr and pthread_self (), + as the distance between this_thr and pthread_self () is constant. */ + return pthread_self () + ((uintptr_t) thr - (uintptr_t) this_thr); +#endif +} +#else +static inline gomp_thread_handle +gomp_thread_self (void) +{ + return (gomp_thread_handle) {}; +} + +static inline gomp_thread_handle +gomp_thread_to_pthread_t (struct gomp_thread *thr) +{ + (void) thr; + return gomp_thread_self (); +} +#endif + #endif /* LIBGOMP_H */ --- libgomp/affinity-fmt.c.jj 2018-05-21 17:29:19.988327335 +0200 +++ libgomp/affinity-fmt.c 2018-05-23 16:22:32.610813049 +0200 @@ -0,0 +1,473 @@ +/* Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by Jakub Jelinek <ja...@redhat.com>. + + This file is part of the GNU Offloading and Multi Processing Library + (libgomp). + + Libgomp is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#include "libgomp.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_UNAME +#include <sys/utsname.h> +#endif + +void +gomp_set_affinity_format (const char *format, size_t len) +{ + if (len < gomp_affinity_format_len) + memcpy (gomp_affinity_format_var, format, len); + else + { + char *p; + if (gomp_affinity_format_len) + p = gomp_realloc (gomp_affinity_format_var, len + 1); + else + p = gomp_malloc (len + 1); + memcpy (p, format, len); + gomp_affinity_format_var = p; + gomp_affinity_format_len = len + 1; + } + gomp_affinity_format_var[len] = '\0'; +} + +void +omp_set_affinity_format (const char *format) +{ + gomp_set_affinity_format (format, strlen (format)); +} + +size_t +omp_get_affinity_format (char *buffer, size_t size) +{ + size_t len = strlen (gomp_affinity_format_var); + if (size) + { + if (len < size) + memcpy (buffer, gomp_affinity_format_var, len + 1); + else + { + memcpy (buffer, gomp_affinity_format_var, size - 1); + buffer[size - 1] = '\0'; + } + } + return len; +} + +void +gomp_display_string (char *buffer, size_t size, size_t *ret, + const char *str, size_t len) +{ + size_t r = *ret; + if (size && r < size) + { + size_t l = len; + if (size - r < len) + l = size - r; + memcpy (buffer + r, str, l); + } + *ret += len; + if (__builtin_expect (r > *ret, 0)) + gomp_fatal ("overflow in omp_capture_affinity"); +} + +static void +gomp_display_repeat (char *buffer, size_t size, size_t *ret, + char c, size_t len) +{ + size_t r = *ret; + if (size && r < size) + { + size_t l = len; + if (size - r < len) + l = size - r; + memset (buffer + r, c, l); + } + *ret += len; + if (__builtin_expect (r > *ret, 0)) + gomp_fatal ("overflow in omp_capture_affinity"); +} + +static void +gomp_display_num (char *buffer, size_t size, size_t *ret, + bool zero, bool right, size_t sz, char *buf) +{ + size_t l = strlen (buf); + if (sz == (size_t) -1 || l >= sz) + { + gomp_display_string (buffer, size, ret, buf, l); + return; + } + if (zero) + { + if (buf[0] == '-') + gomp_display_string (buffer, size, ret, buf, 1); + else if (buf[0] == '0' && buf[1] == 'x') + gomp_display_string (buffer, size, ret, buf, 2); + gomp_display_repeat (buffer, size, ret, '0', sz - l); + if (buf[0] == '-') + gomp_display_string (buffer, size, ret, buf + 1, l - 1); + else if (buf[0] == '0' && buf[1] == 'x') + gomp_display_string (buffer, size, ret, buf + 2, l - 2); + else + gomp_display_string (buffer, size, ret, buf, l); + } + else if (right) + { + gomp_display_repeat (buffer, size, ret, ' ', sz - l); + gomp_display_string (buffer, size, ret, buf, l); + } + else + { + gomp_display_string (buffer, size, ret, buf, l); + gomp_display_repeat (buffer, size, ret, ' ', sz - l); + } +} + +static void +gomp_display_int (char *buffer, size_t size, size_t *ret, + bool zero, bool right, size_t sz, int num) +{ + char buf[3 * sizeof (int) + 2]; + sprintf (buf, "%d", num); + gomp_display_num (buffer, size, ret, zero, right, sz, buf); +} + +static void +gomp_display_string_len (char *buffer, size_t size, size_t *ret, + bool right, size_t sz, char *str, size_t len) +{ + if (sz == (size_t) -1 || len >= sz) + { + gomp_display_string (buffer, size, ret, str, len); + return; + } + + if (right) + { + gomp_display_repeat (buffer, size, ret, ' ', sz - len); + gomp_display_string (buffer, size, ret, str, len); + } + else + { + gomp_display_string (buffer, size, ret, str, len); + gomp_display_repeat (buffer, size, ret, ' ', sz - len); + } +} + +static void +gomp_display_hostname (char *buffer, size_t size, size_t *ret, + bool right, size_t sz) +{ +#ifdef HAVE_GETHOSTNAME + { + char buf[256]; + char *b = buf; + size_t len = 256; + do + { + b[len - 1] = '\0'; + if (gethostname (b, len - 1) == 0) + { + size_t l = strlen (b); + if (l < len - 1) + { + gomp_display_string_len (buffer, size, ret, + right, sz, b, l); + if (b != buf) + free (b); + return; + } + } + if (len == 1048576) + break; + len = len * 2; + if (len == 512) + b = gomp_malloc (len); + else + b = gomp_realloc (b, len); + } + while (1); + if (b != buf) + free (b); + } +#endif +#ifdef HAVE_UNAME + { + struct utsname buf; + if (uname (&buf) == 0) + { + gomp_display_string_len (buffer, size, ret, right, sz, + buf.nodename, strlen (buf.nodename)); + return; + } + } +#endif + gomp_display_string_len (buffer, size, ret, right, sz, "node", 4); +} + +struct affinity_types_struct { + char long_str[18]; + char long_len; + char short_c; }; + +static struct affinity_types_struct affinity_types[] = +{ +#define AFFINITY_TYPE(l, s) \ + { #l, sizeof (#l) - 1, s } + AFFINITY_TYPE (thread_level, 'L'), + AFFINITY_TYPE (thread_num, 'n'), + AFFINITY_TYPE (host, 'h'), + AFFINITY_TYPE (process_id, 'P'), + AFFINITY_TYPE (thread_identifier, 'T'), + AFFINITY_TYPE (num_threads, 'N'), + AFFINITY_TYPE (ancestor_tnum, 'A'), + AFFINITY_TYPE (thread_affinity, 'a') +#undef AFFINITY_TYPE +}; + +size_t +gomp_display_affinity (char *buffer, size_t size, + const char *format, gomp_thread_handle handle, + struct gomp_team_state *ts, unsigned int place) +{ + size_t ret = 0; + do + { + const char *p = strchr (format, '%'); + bool zero = false; + bool right = false; + size_t sz = -1; + char c; + int val; + if (p == NULL) + p = strchr (format, '\0'); + if (p != format) + gomp_display_string (buffer, size, &ret, + format, p - format); + if (*p == '\0') + break; + p++; + if (*p == '%') + { + gomp_display_string (buffer, size, &ret, "%", 1); + format = p + 1; + continue; + } + if (*p == '0') + { + zero = true; + p++; + if (*p != '.') + gomp_fatal ("leading zero not followed by dot in affinity format"); + } + if (*p == '.') + { + right = true; + p++; + } + if (*p >= '1' && *p <= '9') + { + char *end; + sz = strtoul (p, &end, 10); + p = end; + } + else if (zero || right) + gomp_fatal ("leading zero or right justification in affinity format " + "requires size"); + c = *p; + if (c == '{') + { + int i; + for (i = 0; + i < sizeof (affinity_types) / sizeof (affinity_types[0]); ++i) + if (strncmp (p + 1, affinity_types[i].long_str, + affinity_types[i].long_len) == 0 + && p[affinity_types[i].long_len + 1] == '}') + { + c = affinity_types[i].short_c; + p += affinity_types[i].long_len + 1; + break; + } + if (c == '{') + { + char *q = strchr (p + 1, '}'); + if (q) + gomp_fatal ("unsupported long type name '%.*s' in affinity " + "format", (int) (q - (p + 1)), p + 1); + else + gomp_fatal ("unterminated long type name '%s' in affinity " + "format", p + 1); + } + } + switch (c) + { + case 'L': + val = ts->level; + goto do_int; + case 'n': + val = ts->team_id; + goto do_int; + case 'h': + gomp_display_hostname (buffer, size, &ret, right, sz); + break; + case 'P': +#ifdef HAVE_GETPID + val = getpid (); +#else + val = 0; +#endif + goto do_int; + case 'T': +#if defined(LIBGOMP_USE_PTHREADS) && defined(__GNUC__) + /* Handle integral pthread_t. */ + if (__builtin_classify_type (handle) == 1) + { + char buf[3 * (sizeof (handle) + sizeof (int)) + 4]; + + if (sizeof (handle) == sizeof (long)) + sprintf (buf, "0x%lx", (long) handle); + else if (sizeof (handle) == sizeof (long long)) + sprintf (buf, "0x%llx", (long long) handle); + else + sprintf (buf, "0x%x", (int) handle); + gomp_display_num (buffer, size, &ret, zero, right, sz, buf); + break; + } + /* And pointer pthread_t. */ + else if (__builtin_classify_type (handle) == 5) + { + char buf[3 * (sizeof (uintptr_t) + sizeof (int)) + 4]; + + if (sizeof (uintptr_t) == sizeof (long)) + sprintf (buf, "0x%lx", (long) (uintptr_t) handle); + else if (sizeof (uintptr_t) == sizeof (long long)) + sprintf (buf, "0x%llx", (long long) (uintptr_t) handle); + else + sprintf (buf, "0x%x", (int) (uintptr_t) handle); + gomp_display_num (buffer, size, &ret, zero, right, sz, buf); + break; + } +#endif + val = 0; + goto do_int; + case 'N': + val = ts->team ? ts->team->nthreads : 1; + goto do_int; + case 'A': + val = ts->team ? ts->team->prev_ts.team_id : -1; + goto do_int; + case 'a': + if (sz == (size_t) -1) + gomp_display_affinity_place (buffer, size, &ret, + place - 1); + else if (right) + { + size_t len = 0; + gomp_display_affinity_place (NULL, 0, &len, place - 1); + if (len < sz) + gomp_display_repeat (buffer, size, &ret, ' ', sz - len); + gomp_display_affinity_place (buffer, size, &ret, place - 1); + } + else + { + size_t start = ret; + gomp_display_affinity_place (buffer, size, &ret, place - 1); + if (ret - start < sz) + gomp_display_repeat (buffer, size, &ret, ' ', sz - (ret - start)); + } + break; + do_int: + gomp_display_int (buffer, size, &ret, zero, right, sz, val); + break; + default: + gomp_fatal ("unsupported type %c in affinity format", c); + } + format = p + 1; + } + while (1); + return ret; +} + +size_t +omp_capture_affinity (char *buffer, size_t size, const char *format) +{ + struct gomp_thread *thr = gomp_thread (); + size_t ret + = gomp_display_affinity (buffer, size, + format && *format + ? format : gomp_affinity_format_var, + gomp_thread_self (), &thr->ts, thr->place); + if (size) + { + if (ret >= size) + buffer[size - 1] = '\0'; + else + buffer[ret] = '\0'; + } + return ret; +} +ialias (omp_capture_affinity) + +void +omp_display_affinity (const char *format) +{ + char buf[512]; + char *b; + size_t ret = ialias_call (omp_capture_affinity) (buf, sizeof buf, format); + if (ret < sizeof buf) + { + buf[ret] = '\n'; + fwrite (buf, 1, ret + 1, stderr); + return; + } + b = gomp_malloc (ret + 1); + ialias_call (omp_capture_affinity) (b, ret + 1, format); + b[ret] = '\n'; + fwrite (b, 1, ret + 1, stderr); + free (b); +} + +void +gomp_display_affinity_thread (gomp_thread_handle handle, + struct gomp_team_state *ts, unsigned int place) +{ + char buf[512]; + char *b; + size_t ret = gomp_display_affinity (buf, sizeof buf, gomp_affinity_format_var, + handle, ts, place); + if (ret < sizeof buf) + { + buf[ret] = '\n'; + fwrite (buf, 1, ret + 1, stderr); + return; + } + b = gomp_malloc (ret + 1); + gomp_display_affinity (b, ret + 1, gomp_affinity_format_var, + handle, ts, place); + b[ret] = '\n'; + fwrite (b, 1, ret + 1, stderr); + free (b); +} --- libgomp/affinity.c.jj 2018-04-30 13:21:02.853864869 +0200 +++ libgomp/affinity.c 2018-05-23 11:37:46.299744109 +0200 @@ -138,5 +138,18 @@ gomp_get_place_proc_ids_8 (int place_num (void) ids; } +void +gomp_display_affinity_place (char *buffer, size_t size, size_t *ret, + int place) +{ + cpu_set_t *cpusetp; + char buf[sizeof (long) * 3 + 4]; + if (gomp_available_cpus > 1) + sprintf (buf, "0-%lu", gomp_available_cpus - 1); + else + strcpy (buf, "0"); + gomp_display_string (buffer, size, ret, buf, strlen (buf)); +} + ialias(omp_get_place_num_procs) ialias(omp_get_place_proc_ids) --- libgomp/config/linux/affinity.c.jj 2018-04-30 13:20:51.454860277 +0200 +++ libgomp/config/linux/affinity.c 2018-05-23 11:24:14.809081402 +0200 @@ -396,6 +396,56 @@ gomp_get_place_proc_ids_8 (int place_num *ids++ = i; } +void +gomp_display_affinity_place (char *buffer, size_t size, size_t *ret, + int place) +{ + cpu_set_t *cpusetp; + char buf[sizeof (long) * 3 + 4]; + if (place >= 0 && place < gomp_places_list_len) + cpusetp = (cpu_set_t *) gomp_places_list[place]; + else if (gomp_cpusetp) + cpusetp = gomp_cpusetp; + else + { + if (gomp_available_cpus > 1) + sprintf (buf, "0-%lu", gomp_available_cpus - 1); + else + strcpy (buf, "0"); + gomp_display_string (buffer, size, ret, buf, strlen (buf)); + return; + } + + unsigned long i, max = 8 * gomp_cpuset_size, start; + bool prev_set = false; + start = max; + for (i = 0; i <= max; i++) + { + bool this_set; + if (i == max) + this_set = false; + else + this_set = CPU_ISSET_S (i, gomp_cpuset_size, cpusetp); + if (this_set != prev_set) + { + prev_set = this_set; + if (this_set) + { + char *p = buf; + if (start != max) + *p++ = ','; + sprintf (p, "%lu", i); + start = i; + } + else if (i == start + 1) + continue; + else + sprintf (buf, "-%lu", i - 1); + gomp_display_string (buffer, size, ret, buf, strlen (buf)); + } + } +} + ialias(omp_get_place_num_procs) ialias(omp_get_place_proc_ids) --- libgomp/env.c.jj 2018-04-30 13:21:04.806865641 +0200 +++ libgomp/env.c 2018-05-23 12:38:58.023091979 +0200 @@ -88,6 +88,9 @@ void **gomp_places_list; unsigned long gomp_places_list_len; int gomp_debug_var; unsigned int gomp_num_teams_var; +bool gomp_display_affinity_var; +char *gomp_affinity_format_var = "level %L thread %T affinity %a"; +size_t gomp_affinity_format_len; char *goacc_device_type; int goacc_device_num; @@ -1197,6 +1200,10 @@ handle_omp_display_env (unsigned long st gomp_global_icv.default_device_var); fprintf (stderr, " OMP_MAX_TASK_PRIORITY = '%d'\n", gomp_max_task_priority_var); + fprintf (stderr, " OMP_DISPLAY_AFFINITY = '%s'\n", + gomp_display_affinity_var ? "TRUE" : "FALSE"); + fprintf (stderr, " OMP_AFFINITY_FORMAT = '%s'\n", + gomp_affinity_format_var); if (verbose) { @@ -1228,6 +1235,7 @@ initialize_env (void) parse_boolean ("OMP_DYNAMIC", &gomp_global_icv.dyn_var); parse_boolean ("OMP_NESTED", &gomp_global_icv.nest_var); parse_boolean ("OMP_CANCELLATION", &gomp_cancel_var); + parse_boolean ("OMP_DISPLAY_AFFINITY", &gomp_display_affinity_var); parse_int ("OMP_DEFAULT_DEVICE", &gomp_global_icv.default_device_var, true); parse_int ("OMP_MAX_TASK_PRIORITY", &gomp_max_task_priority_var, true); parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", &gomp_max_active_levels_var, @@ -1277,6 +1285,13 @@ initialize_env (void) } if (gomp_global_icv.bind_var != omp_proc_bind_false) gomp_init_affinity (); + + { + const char *env = getenv ("OMP_AFFINITY_FORMAT"); + if (env != NULL) + gomp_set_affinity_format (env, strlen (env)); + } + wait_policy = parse_wait_policy (); if (!parse_spincount ("GOMP_SPINCOUNT", &gomp_spin_count_var)) { --- libgomp/fortran.c.jj 2018-04-30 13:21:03.011864928 +0200 +++ libgomp/fortran.c 2018-05-23 16:15:31.826411733 +0200 @@ -28,6 +28,8 @@ #include "libgomp.h" #include "libgomp_f.h" #include <stdlib.h> +#include <stdio.h> +#include <string.h> #include <limits.h> #ifdef HAVE_ATTRIBUTE_ALIAS @@ -576,3 +578,84 @@ omp_get_max_task_priority_ (void) { return omp_get_max_task_priority (); } + +void +omp_set_affinity_format_ (const char *format, size_t format_len) +{ + gomp_set_affinity_format (format, format_len); +} + +int32_t +omp_get_affinity_format_ (char *buffer, size_t buffer_len) +{ + size_t len = strlen (gomp_affinity_format_var); + if (buffer_len) + { + if (len < buffer_len) + { + memcpy (buffer, gomp_affinity_format_var, len); + memset (buffer + len, ' ', buffer_len - len); + } + else + memcpy (buffer, gomp_affinity_format_var, buffer_len); + } + return len; +} + +void +omp_display_affinity_ (const char *format, size_t format_len) +{ + char *fmt = NULL, fmt_buf[256]; + char buf[512]; + if (format_len) + { + fmt = format_len < 256 ? fmt_buf : gomp_malloc (format_len + 1); + memcpy (fmt, format, format_len); + fmt[format_len] = '\0'; + } + struct gomp_thread *thr = gomp_thread (); + size_t ret + = gomp_display_affinity (buf, sizeof buf, + format_len ? fmt : gomp_affinity_format_var, + gomp_thread_self (), &thr->ts, thr->place); + if (ret < sizeof buf) + { + buf[ret] = '\n'; + fwrite (buf, 1, ret + 1, stderr); + } + else + { + char *b = gomp_malloc (ret + 1); + gomp_display_affinity (buf, sizeof buf, + format_len ? fmt : gomp_affinity_format_var, + gomp_thread_self (), &thr->ts, thr->place); + b[ret] = '\n'; + fwrite (b, 1, ret + 1, stderr); + free (b); + } + if (fmt && fmt != fmt_buf) + free (fmt); +} + +int32_t +omp_capture_affinity_ (char *buffer, const char *format, + size_t buffer_len, size_t format_len) +{ + char *fmt = NULL, fmt_buf[256]; + if (format_len) + { + fmt = format_len < 256 ? fmt_buf : gomp_malloc (format_len + 1); + memcpy (fmt, format, format_len); + fmt[format_len] = '\0'; + } + struct gomp_thread *thr = gomp_thread (); + size_t ret + = gomp_display_affinity (buffer, buffer_len, + format_len ? fmt : gomp_affinity_format_var, + gomp_thread_self (), &thr->ts, thr->place); + if (fmt && fmt != fmt_buf) + free (fmt); + if (ret < buffer_len) + memset (buffer + ret, ' ', buffer_len - ret); + return ret; +} --- libgomp/omp.h.in.jj 2018-05-04 12:31:28.863633774 +0200 +++ libgomp/omp.h.in 2018-05-21 15:54:08.496902995 +0200 @@ -166,6 +166,13 @@ extern int omp_target_associate_ptr (con __SIZE_TYPE__, int) __GOMP_NOTHROW; extern int omp_target_disassociate_ptr (const void *, int) __GOMP_NOTHROW; +extern void omp_set_affinity_format (const char *) __GOMP_NOTHROW; +extern __SIZE_TYPE__ omp_get_affinity_format (char *, __SIZE_TYPE__) + __GOMP_NOTHROW; +extern void omp_display_affinity (const char *) __GOMP_NOTHROW; +extern __SIZE_TYPE__ omp_capture_affinity (char *, __SIZE_TYPE__, const char *) + __GOMP_NOTHROW; + #ifdef __cplusplus } #endif --- libgomp/omp_lib.f90.in.jj 2018-04-30 13:19:49.305835311 +0200 +++ libgomp/omp_lib.f90.in 2018-05-23 13:14:15.873893636 +0200 @@ -433,4 +433,31 @@ end function omp_get_max_task_priority end interface + interface + subroutine omp_set_affinity_format (format) + character(len=*), intent(in) :: format + end subroutine omp_set_affinity_format + end interface + + interface + function omp_get_affinity_format (buffer) + integer (4) :: omp_get_affinity_format + character(len=*), intent(out) :: buffer + end function omp_get_affinity_format + end interface + + interface + subroutine omp_display_affinity (format) + character(len=*), intent(in) :: format + end subroutine omp_display_affinity + end interface + + interface + function omp_capture_affinity (buffer, format) + integer (4) :: omp_capture_affinity + character(len=*), intent(out) :: buffer + character(len=*), intent(in) :: format + end function omp_capture_affinity + end interface + end module omp_lib --- libgomp/omp_lib.h.in.jj 2018-04-30 13:21:07.207866607 +0200 +++ libgomp/omp_lib.h.in 2018-05-23 13:21:07.605241351 +0200 @@ -126,3 +126,8 @@ external omp_get_max_task_priority integer(4) omp_get_max_task_priority + + external omp_set_affinity_format, omp_get_affinity_format + external omp_display_affinity, omp_capture_affinity + integer(4) omp_get_affinity_format + integer(4) omp_capture_affinity --- libgomp/team.c.jj 2018-04-30 13:19:48.674835063 +0200 +++ libgomp/team.c 2018-05-23 16:45:09.478108168 +0200 @@ -58,6 +58,7 @@ struct gomp_thread_start_data struct gomp_thread_pool *thread_pool; unsigned int place; bool nested; + pthread_t handle; }; @@ -89,6 +90,9 @@ gomp_thread_start (void *xdata) thr->ts = data->ts; thr->task = data->task; thr->place = data->place; +#ifdef GOMP_NEEDS_THREAD_HANDLE + thr->handle = data->handle; +#endif thr->ts.team->ordered_release[thr->ts.team_id] = &thr->release; @@ -312,6 +316,7 @@ gomp_team_start (void (*fn) (void *), vo unsigned int s = 0, rest = 0, p = 0, k = 0; unsigned int affinity_count = 0; struct gomp_thread **affinity_thr = NULL; + bool force_display = false; thr = gomp_thread (); nested = thr->ts.level; @@ -319,7 +324,12 @@ gomp_team_start (void (*fn) (void *), vo task = thr->task; icv = task ? &task->icv : &gomp_global_icv; if (__builtin_expect (gomp_places_list != NULL, 0) && thr->place == 0) - gomp_init_affinity (); + { + gomp_init_affinity (); + if (__builtin_expect (gomp_display_affinity_var, 0) && nthreads == 1) + gomp_display_affinity_thread (gomp_thread_self (), &thr->ts, + thr->place); + } /* Always save the previous state, even if this isn't a nested team. In particular, we should save any work share state from an outer @@ -338,6 +348,9 @@ gomp_team_start (void (*fn) (void *), vo #endif thr->ts.static_trip = 0; thr->task = &team->implicit_task[0]; +#ifdef GOMP_NEEDS_THREAD_HANDLE + thr->handle = pthread_self (); +#endif nthreads_var = icv->nthreads_var; if (__builtin_expect (gomp_nthreads_var_list != NULL, 0) && thr->ts.level < gomp_nthreads_var_list_len) @@ -465,7 +478,7 @@ gomp_team_start (void (*fn) (void *), vo pool->threads = gomp_realloc (pool->threads, pool->threads_size - * sizeof (struct gomp_thread_data *)); + * sizeof (struct gomp_thread *)); } /* Release existing idle threads. */ @@ -540,6 +553,7 @@ gomp_team_start (void (*fn) (void *), vo + place_partition_len)) { unsigned int l; + force_display = true; if (affinity_thr == NULL) { unsigned int j; @@ -719,12 +733,11 @@ gomp_team_start (void (*fn) (void *), vo } start_data = gomp_alloca (sizeof (struct gomp_thread_start_data) - * (nthreads-i)); + * (nthreads - i)); /* Launch new threads. */ for (; i < nthreads; ++i) { - pthread_t pt; int err; start_data->ts.place_partition_off = thr->ts.place_partition_off; @@ -814,7 +827,9 @@ gomp_team_start (void (*fn) (void *), vo start_data->nested = nested; attr = gomp_adjust_thread_attr (attr, &thread_attr); - err = pthread_create (&pt, attr, gomp_thread_start, start_data++); + err = pthread_create (&start_data->handle, attr, gomp_thread_start, + start_data); + start_data++; if (err != 0) gomp_fatal ("Thread creation failed: %s", strerror (err)); } @@ -854,6 +869,42 @@ gomp_team_start (void (*fn) (void *), vo gomp_mutex_unlock (&gomp_managed_threads_lock); #endif } + if (__builtin_expect (gomp_display_affinity_var, 0)) + { + if (nested + || nthreads != old_threads_used + || force_display) + { + gomp_display_affinity_thread (gomp_thread_self (), &thr->ts, + thr->place); + if (nested) + { + start_data -= nthreads - 1; + for (i = 1; i < nthreads; ++i) + { + gomp_display_affinity_thread ( +#ifdef LIBGOMP_USE_PTHREADS + start_data->handle, +#else + gomp_thread_self (), +#endif + &start_data->ts, + start_data->place); + start_data++; + } + } + else + { + for (i = 1; i < nthreads; ++i) + { + gomp_thread_handle handle + = gomp_thread_to_pthread_t (pool->threads[i]); + gomp_display_affinity_thread (handle, &pool->threads[i]->ts, + pool->threads[i]->place); + } + } + } + } if (__builtin_expect (affinity_thr != NULL, 0) && team->prev_ts.place_partition_len > 64) free (affinity_thr); --- libgomp/configure.jj 2018-04-30 13:21:02.531864728 +0200 +++ libgomp/configure 2018-05-22 14:16:34.830984930 +0200 @@ -15784,6 +15784,72 @@ fi fi +# Check for uname. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <string.h> + #include <stdlib.h> + #include <sys/utsname.h> +int +main () +{ +struct utsname buf; + volatile size_t len = 0; + if (!uname (buf)) + len = strlen (buf.nodename); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_UNAME 1" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# Check for gethostname. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <unistd.h> +int +main () +{ + + char buf[256]; + if (gethostname (buf, sizeof (buf) - 1) == 0) + buf[255] = '\0'; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_GETHOSTNAME 1" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# Check for getpid. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <unistd.h> +int +main () +{ +int pid = getpid (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_GETPID 1" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + # See if we support thread-local storage. --- libgomp/config.h.in.jj 2017-05-04 15:04:53.606372292 +0200 +++ libgomp/config.h.in 2018-05-22 14:16:32.000000000 +0200 @@ -33,9 +33,15 @@ /* Define to 1 if you have the `getgid' function. */ #undef HAVE_GETGID +/* Define if gethostname is supported. */ +#undef HAVE_GETHOSTNAME + /* Define to 1 if you have the `getloadavg' function. */ #undef HAVE_GETLOADAVG +/* Define if getpid is supported. */ +#undef HAVE_GETPID + /* Define to 1 if you have the `getuid' function. */ #undef HAVE_GETUID @@ -103,6 +109,9 @@ /* Define to 1 if the target supports thread-local storage. */ #undef HAVE_TLS +/* Define if uname is supported and struct utsname has nodename field. */ +#undef HAVE_UNAME + /* Define to 1 if you have the <unistd.h> header file. */ #undef HAVE_UNISTD_H --- libgomp/Makefile.in.jj 2018-04-30 13:19:46.934834362 +0200 +++ libgomp/Makefile.in 2018-05-21 17:21:39.227956241 +0200 @@ -180,7 +180,8 @@ am_libgomp_la_OBJECTS = alloc.lo atomic. sem.lo bar.lo ptrlock.lo time.lo fortran.lo affinity.lo \ target.lo splay-tree.lo libgomp-plugin.lo oacc-parallel.lo \ oacc-host.lo oacc-init.lo oacc-mem.lo oacc-async.lo \ - oacc-plugin.lo oacc-cuda.lo priority_queue.lo $(am__objects_1) + oacc-plugin.lo oacc-cuda.lo priority_queue.lo affinity-fmt.lo \ + $(am__objects_1) libgomp_la_OBJECTS = $(am_libgomp_la_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/../depcomp @@ -436,7 +437,7 @@ libgomp_la_SOURCES = alloc.c atomic.c ba affinity.c target.c splay-tree.c libgomp-plugin.c \ oacc-parallel.c oacc-host.c oacc-init.c oacc-mem.c \ oacc-async.c oacc-plugin.c oacc-cuda.c priority_queue.c \ - $(am__append_3) + affinity-fmt.c $(am__append_3) # Nvidia PTX OpenACC plugin. @PLUGIN_NVPTX_TRUE@libgomp_plugin_nvptx_version_info = -version-info $(libtool_VERSION) @@ -600,6 +601,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/affinity.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/affinity-fmt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atomic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bar.Plo@am__quote@ --- libgomp/testsuite/libgomp.c-c++-common/display-affinity-1.c.jj 2018-05-23 10:06:58.261621753 +0200 +++ libgomp/testsuite/libgomp.c-c++-common/display-affinity-1.c 2018-05-23 11:21:57.641972727 +0200 @@ -0,0 +1,91 @@ +/* { dg-set-target-env-var OMP_PROC_BIND "spread,close" } */ +/* { dg-set-target-env-var OMP_PLACES "cores" } */ +/* { dg-set-target-env-var OMP_NUM_THREADS "4" } */ +/* { dg-set-target-env-var OMP_AFFINITY_FORMAT "hello" } */ + +#include <omp.h> +#include <string.h> +#include <stdlib.h> + +int +main () +{ +#define FMT "L:%0.5L%%%n>%32h<!%.33{host}!%.6P_%T_%0.18T_%0.7{ancestor_tnum} %18a" + char buf[] = FMT, hostname[256], buf2[512 + 32], *q; + size_t l, l2, l3; + char *r = getenv ("OMP_AFFINITY_FORMAT"); + if (r && strcmp (r, "hello") == 0) + { + if (omp_get_affinity_format (NULL, 0) != 5) + abort (); + if (omp_get_affinity_format (buf2, 3) != 5 + || strcmp (buf2, "he") != 0) + abort (); + if (omp_get_affinity_format (buf2, 6) != 5 + || strcmp (buf2, "hello") != 0) + abort (); + } + omp_set_affinity_format (buf); + memset (buf, '^', sizeof (buf)); + if (omp_get_affinity_format (NULL, 0) != sizeof (buf) - 1) + abort (); + if (omp_get_affinity_format (buf, 3) != sizeof (buf) - 1 + || buf[0] != FMT[0] || buf[1] != FMT[1] || buf[2] != '\0') + abort (); + memset (buf, ' ', sizeof (buf)); + if (omp_get_affinity_format (buf, sizeof (buf) - 1) != sizeof (buf) - 1 + || strncmp (buf, FMT, sizeof (buf) - 2) != 0 + || buf[sizeof (buf) - 2] != '\0') + abort (); + memset (buf, '-', sizeof (buf)); + if (omp_get_affinity_format (buf, sizeof (buf)) != sizeof (buf) - 1 + || strcmp (buf, FMT) != 0) + abort (); + memset (buf, '0', sizeof (buf)); + omp_display_affinity (NULL); + omp_display_affinity (""); + omp_display_affinity ("%%%0.9N"); + omp_set_affinity_format ("%{host}"); + l = omp_capture_affinity (hostname, sizeof hostname, NULL); + if (l < sizeof (hostname)) + { + if (strlen (hostname) != l) + abort (); + l2 = omp_capture_affinity (NULL, 0, + "%0.5{thread_level}%%%32{host}|||%.33h" + "%0.7A%3N!%N!"); + if (l2 != (5 + 1 + (l > 32 ? l : 32) + 3 + (l > 33 ? l : 33) + + 7 + 3 + 1 + 1 + 1)) + abort (); + omp_set_affinity_format ("%.5L%%%32h|||%.33{host}%0.7{ancestor_tnum}" + "%3{num_threads}!%{num_threads}!"); + l3 = omp_capture_affinity (buf2, sizeof buf2, ""); + if (l3 != l2) + abort (); + if (memcmp (buf2, " 0%", 5 + 1) != 0) + abort (); + q = buf2 + 6; + if (memcmp (q, hostname, l) != 0) + abort (); + q += l; + if (l < 32) + for (l3 = 32 - l; l3; l3--) + if (*q++ != ' ') + abort (); + if (memcmp (q, "|||", 3) != 0) + abort (); + q += 3; + if (l < 33) + for (l3 = 33 - l; l3; l3--) + if (*q++ != ' ') + abort (); + if (memcmp (q, hostname, l) != 0) + abort (); + q += l; + if (strcmp (q, "-0000011 !1!") != 0) + abort (); + } + #pragma omp parallel num_threads (4) proc_bind(spread) + omp_display_affinity ("%0.2A!%n!%.4L!%N;%a"); + return 0; +} --- libgomp/testsuite/libgomp.fortran/display-affinity-1.f90.jj 2018-05-23 13:45:12.788535876 +0200 +++ libgomp/testsuite/libgomp.fortran/display-affinity-1.f90 2018-05-23 14:24:01.349865094 +0200 @@ -0,0 +1,33 @@ +! { dg-set-target-env-var OMP_PROC_BIND "spread,close" } +! { dg-set-target-env-var OMP_PLACES "cores" } +! { dg-set-target-env-var OMP_NUM_THREADS "4" } +! { dg-set-target-env-var OMP_AFFINITY_FORMAT "hello" } + + use omp_lib + character(len=68) :: buf, buf2 + character(len=8) :: buf3 + character(len=1) :: buf4 + integer :: l1, l2 + + buf = 'L:%0.5L%%%n>%32h<!%.33{host}!%.6P_%T_%0.18T_%0.7{ancestor_tnum} %18a' + call omp_set_affinity_format (format = buf) + if (omp_get_affinity_format (buf4) /= 68) stop 1 + if (buf4 /= 'L') stop 2 + if (omp_get_affinity_format (buf2) /= 68) stop 3 + if (buf2 /= buf) stop 4 + if (omp_get_affinity_format (buf3) /= 68) stop 5 + if (buf3 /= 'L:%0.5L%') stop 6 + call omp_display_affinity ('') + call omp_display_affinity ('%%%0.9N') + l1 = omp_capture_affinity (buf4, '%0.5{thread_level}%%|||%0.7A%3N!%N!') + buf = '%.5L%%|||%0.7{ancestor_tnum}%3{num_threads}!%{num_threads}!' + call omp_set_affinity_format (trim (buf)) + l2 = omp_capture_affinity (buf2, '') + if (l1 /= l2) stop 7 + if (l1 /= 22) stop 8 + if (buf2 /= ' 0%|||-0000011 !1!') stop 9 + if (buf4 /= '0') stop 10 +!$omp parallel num_threads (4) proc_bind(spread) + call omp_display_affinity ('%0.2A!%n!%.4L!%N;%a') +!$omp end parallel +end Jakub