| /* Copyright (C) 2018-2021 Free Software Foundation, Inc. |
| Contributed by Jakub Jelinek <jakub@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_INTTYPES_H |
| # include <inttypes.h> /* For PRIx64. */ |
| #endif |
| #ifdef HAVE_UNAME |
| #include <sys/utsname.h> |
| #endif |
| |
| bool |
| gomp_print_string (const char *str, size_t len) |
| { |
| return fwrite (str, 1, len, stderr) != len; |
| } |
| |
| 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 (team_num, 't'), |
| AFFINITY_TYPE (num_teams, 'T'), |
| AFFINITY_TYPE (nesting_level, 'L'), |
| AFFINITY_TYPE (thread_num, 'n'), |
| AFFINITY_TYPE (num_threads, 'N'), |
| AFFINITY_TYPE (ancestor_tnum, 'a'), |
| AFFINITY_TYPE (host, 'H'), |
| AFFINITY_TYPE (process_id, 'P'), |
| AFFINITY_TYPE (native_thread_id, 'i'), |
| 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 't': |
| val = omp_get_team_num (); |
| goto do_int; |
| case 'T': |
| val = omp_get_num_teams (); |
| goto do_int; |
| case 'L': |
| val = ts->level; |
| goto do_int; |
| case 'n': |
| val = ts->team_id; |
| 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 '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 'i': |
| #if defined(LIBGOMP_USE_PTHREADS) && defined(__GNUC__) |
| { |
| char buf[3 * (sizeof (handle) + sizeof (uintptr_t) + sizeof (int)) |
| + 4]; |
| /* This macro returns expr unmodified for integral or pointer |
| types and 0 for anything else (e.g. aggregates). */ |
| #define gomp_nonaggregate(expr) \ |
| __builtin_choose_expr (__builtin_classify_type (expr) == 1 \ |
| || __builtin_classify_type (expr) == 5, expr, 0) |
| /* This macro returns expr unmodified for integral types, |
| (uintptr_t) (expr) for pointer types and 0 for anything else |
| (e.g. aggregates). */ |
| #define gomp_integral(expr) \ |
| __builtin_choose_expr (__builtin_classify_type (expr) == 5, \ |
| (uintptr_t) gomp_nonaggregate (expr), \ |
| gomp_nonaggregate (expr)) |
| |
| if (sizeof (gomp_integral (handle)) == sizeof (unsigned long)) |
| sprintf (buf, "0x%lx", (unsigned long) gomp_integral (handle)); |
| #if defined (HAVE_INTTYPES_H) && defined (PRIx64) |
| else if (sizeof (gomp_integral (handle)) == sizeof (uint64_t)) |
| sprintf (buf, "0x%" PRIx64, (uint64_t) gomp_integral (handle)); |
| #else |
| else if (sizeof (gomp_integral (handle)) |
| == sizeof (unsigned long long)) |
| sprintf (buf, "0x%llx", |
| (unsigned long long) gomp_integral (handle)); |
| #endif |
| else |
| sprintf (buf, "0x%x", (unsigned int) gomp_integral (handle)); |
| gomp_display_num (buffer, size, &ret, zero, right, sz, buf); |
| break; |
| } |
| #else |
| val = 0; |
| goto do_int; |
| #endif |
| 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'; |
| gomp_print_string (buf, ret + 1); |
| return; |
| } |
| b = gomp_malloc (ret + 1); |
| ialias_call (omp_capture_affinity) (b, ret + 1, format); |
| b[ret] = '\n'; |
| gomp_print_string (b, ret + 1); |
| 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'; |
| gomp_print_string (buf, ret + 1); |
| return; |
| } |
| b = gomp_malloc (ret + 1); |
| gomp_display_affinity (b, ret + 1, gomp_affinity_format_var, |
| handle, ts, place); |
| b[ret] = '\n'; |
| gomp_print_string (b, ret + 1); |
| free (b); |
| } |