1/* Copyright (C) 2018-2022 Free Software Foundation, Inc.
2   Contributed by Jakub Jelinek <jakub@redhat.com>.
3
4   This file is part of the GNU Offloading and Multi Processing Library
5   (libgomp).
6
7   Libgomp is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3, or (at your option)
10   any later version.
11
12   Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15   more details.
16
17   Under Section 7 of GPL version 3, you are granted additional
18   permissions described in the GCC Runtime Library Exception, version
19   3.1, as published by the Free Software Foundation.
20
21   You should have received a copy of the GNU General Public License and
22   a copy of the GCC Runtime Library Exception along with this program;
23   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24   <http://www.gnu.org/licenses/>.  */
25
26#include "libgomp.h"
27#include <string.h>
28#include <stdio.h>
29#include <stdlib.h>
30#ifdef HAVE_UNISTD_H
31#include <unistd.h>
32#endif
33#ifdef HAVE_INTTYPES_H
34# include <inttypes.h>  /* For PRIx64.  */
35#endif
36#ifdef HAVE_UNAME
37#include <sys/utsname.h>
38#endif
39
40ialias_redirect (omp_get_team_num)
41ialias_redirect (omp_get_num_teams)
42
43bool
44gomp_print_string (const char *str, size_t len)
45{
46  return fwrite (str, 1, len, stderr) != len;
47}
48
49void
50gomp_set_affinity_format (const char *format, size_t len)
51{
52  if (len < gomp_affinity_format_len)
53    memcpy (gomp_affinity_format_var, format, len);
54  else
55    {
56      char *p;
57      if (gomp_affinity_format_len)
58	p = gomp_realloc (gomp_affinity_format_var, len + 1);
59      else
60	p = gomp_malloc (len + 1);
61      memcpy (p, format, len);
62      gomp_affinity_format_var = p;
63      gomp_affinity_format_len = len + 1;
64    }
65  gomp_affinity_format_var[len] = '\0';
66}
67
68void
69omp_set_affinity_format (const char *format)
70{
71  gomp_set_affinity_format (format, strlen (format));
72}
73
74size_t
75omp_get_affinity_format (char *buffer, size_t size)
76{
77  size_t len = strlen (gomp_affinity_format_var);
78  if (size)
79    {
80      if (len < size)
81	memcpy (buffer, gomp_affinity_format_var, len + 1);
82      else
83	{
84	  memcpy (buffer, gomp_affinity_format_var, size - 1);
85	  buffer[size - 1] = '\0';
86	}
87    }
88  return len;
89}
90
91void
92gomp_display_string (char *buffer, size_t size, size_t *ret,
93		     const char *str, size_t len)
94{
95  size_t r = *ret;
96  if (size && r < size)
97    {
98      size_t l = len;
99      if (size - r < len)
100	l = size - r;
101      memcpy (buffer + r, str, l);
102    }
103  *ret += len;
104  if (__builtin_expect (r > *ret, 0))
105    gomp_fatal ("overflow in omp_capture_affinity");
106}
107
108static void
109gomp_display_repeat (char *buffer, size_t size, size_t *ret,
110		     char c, size_t len)
111{
112  size_t r = *ret;
113  if (size && r < size)
114    {
115      size_t l = len;
116      if (size - r < len)
117	l = size - r;
118      memset (buffer + r, c, l);
119    }
120  *ret += len;
121  if (__builtin_expect (r > *ret, 0))
122    gomp_fatal ("overflow in omp_capture_affinity");
123}
124
125static void
126gomp_display_num (char *buffer, size_t size, size_t *ret,
127		  bool zero, bool right, size_t sz, char *buf)
128{
129  size_t l = strlen (buf);
130  if (sz == (size_t) -1 || l >= sz)
131    {
132      gomp_display_string (buffer, size, ret, buf, l);
133      return;
134    }
135  if (zero)
136    {
137      if (buf[0] == '-')
138	gomp_display_string (buffer, size, ret, buf, 1);
139      else if (buf[0] == '0' && buf[1] == 'x')
140	gomp_display_string (buffer, size, ret, buf, 2);
141      gomp_display_repeat (buffer, size, ret, '0', sz - l);
142      if (buf[0] == '-')
143	gomp_display_string (buffer, size, ret, buf + 1, l - 1);
144      else if (buf[0] == '0' && buf[1] == 'x')
145	gomp_display_string (buffer, size, ret, buf + 2, l - 2);
146      else
147	gomp_display_string (buffer, size, ret, buf, l);
148    }
149  else if (right)
150    {
151      gomp_display_repeat (buffer, size, ret, ' ', sz - l);
152      gomp_display_string (buffer, size, ret, buf, l);
153    }
154  else
155    {
156      gomp_display_string (buffer, size, ret, buf, l);
157      gomp_display_repeat (buffer, size, ret, ' ', sz - l);
158    }
159}
160
161static void
162gomp_display_int (char *buffer, size_t size, size_t *ret,
163		  bool zero, bool right, size_t sz, int num)
164{
165  char buf[3 * sizeof (int) + 2];
166  sprintf (buf, "%d", num);
167  gomp_display_num (buffer, size, ret, zero, right, sz, buf);
168}
169
170static void
171gomp_display_string_len (char *buffer, size_t size, size_t *ret,
172			 bool right, size_t sz, char *str, size_t len)
173{
174  if (sz == (size_t) -1 || len >= sz)
175    {
176      gomp_display_string (buffer, size, ret, str, len);
177      return;
178    }
179
180  if (right)
181    {
182      gomp_display_repeat (buffer, size, ret, ' ', sz - len);
183      gomp_display_string (buffer, size, ret, str, len);
184    }
185  else
186    {
187      gomp_display_string (buffer, size, ret, str, len);
188      gomp_display_repeat (buffer, size, ret, ' ', sz - len);
189    }
190}
191
192static void
193gomp_display_hostname (char *buffer, size_t size, size_t *ret,
194		       bool right, size_t sz)
195{
196#ifdef HAVE_GETHOSTNAME
197  {
198    char buf[256];
199    char *b = buf;
200    size_t len = 256;
201    do
202      {
203	b[len - 1] = '\0';
204	if (gethostname (b, len - 1) == 0)
205	  {
206	    size_t l = strlen (b);
207	    if (l < len - 1)
208	      {
209		gomp_display_string_len (buffer, size, ret,
210					 right, sz, b, l);
211		if (b != buf)
212		  free (b);
213		return;
214	      }
215	  }
216	if (len == 1048576)
217	  break;
218	len = len * 2;
219	if (len == 512)
220	  b = gomp_malloc (len);
221	else
222	  b = gomp_realloc (b, len);
223      }
224    while (1);
225    if (b != buf)
226      free (b);
227  }
228#endif
229#ifdef HAVE_UNAME
230  {
231    struct utsname buf;
232    if (uname (&buf) == 0)
233      {
234	gomp_display_string_len (buffer, size, ret, right, sz,
235				 buf.nodename, strlen (buf.nodename));
236	return;
237      }
238  }
239#endif
240  gomp_display_string_len (buffer, size, ret, right, sz, "node", 4);
241}
242
243struct affinity_types_struct {
244  char long_str[18];
245  char long_len;
246  char short_c; };
247
248static struct affinity_types_struct affinity_types[] =
249{
250#define AFFINITY_TYPE(l, s) \
251  { #l, sizeof (#l) - 1, s }
252  AFFINITY_TYPE (team_num, 't'),
253  AFFINITY_TYPE (num_teams, 'T'),
254  AFFINITY_TYPE (nesting_level, 'L'),
255  AFFINITY_TYPE (thread_num, 'n'),
256  AFFINITY_TYPE (num_threads, 'N'),
257  AFFINITY_TYPE (ancestor_tnum, 'a'),
258  AFFINITY_TYPE (host, 'H'),
259  AFFINITY_TYPE (process_id, 'P'),
260  AFFINITY_TYPE (native_thread_id, 'i'),
261  AFFINITY_TYPE (thread_affinity, 'A')
262#undef AFFINITY_TYPE
263};
264
265size_t
266gomp_display_affinity (char *buffer, size_t size,
267		       const char *format, gomp_thread_handle handle,
268		       struct gomp_team_state *ts, unsigned int place)
269{
270  size_t ret = 0;
271  do
272    {
273      const char *p = strchr (format, '%');
274      bool zero = false;
275      bool right = false;
276      size_t sz = -1;
277      char c;
278      int val;
279      if (p == NULL)
280	p = strchr (format, '\0');
281      if (p != format)
282	gomp_display_string (buffer, size, &ret,
283			     format, p - format);
284      if (*p == '\0')
285	break;
286      p++;
287      if (*p == '%')
288	{
289	  gomp_display_string (buffer, size, &ret, "%", 1);
290	  format = p + 1;
291	  continue;
292	}
293      if (*p == '0')
294	{
295	  zero = true;
296	  p++;
297	  if (*p != '.')
298	    gomp_fatal ("leading zero not followed by dot in affinity format");
299	}
300      if (*p == '.')
301	{
302	  right = true;
303	  p++;
304	}
305      if (*p >= '1' && *p <= '9')
306	{
307	  char *end;
308	  sz = strtoul (p, &end, 10);
309	  p = end;
310	}
311      else if (zero || right)
312	gomp_fatal ("leading zero or right justification in affinity format "
313		    "requires size");
314      c = *p;
315      if (c == '{')
316	{
317	  int i;
318	  for (i = 0;
319	       i < sizeof (affinity_types) / sizeof (affinity_types[0]); ++i)
320	    if (strncmp (p + 1, affinity_types[i].long_str,
321			 affinity_types[i].long_len) == 0
322		&& p[affinity_types[i].long_len + 1] == '}')
323	      {
324		c = affinity_types[i].short_c;
325		p += affinity_types[i].long_len + 1;
326		break;
327	      }
328	  if (c == '{')
329	    {
330	      char *q = strchr (p + 1, '}');
331	      if (q)
332		gomp_fatal ("unsupported long type name '%.*s' in affinity "
333			    "format", (int) (q - (p + 1)), p + 1);
334	      else
335		gomp_fatal ("unterminated long type name '%s' in affinity "
336			    "format", p + 1);
337	    }
338	}
339      switch (c)
340	{
341	case 't':
342	  val = omp_get_team_num ();
343	  goto do_int;
344	case 'T':
345	  val = omp_get_num_teams ();
346	  goto do_int;
347	case 'L':
348	  val = ts->level;
349	  goto do_int;
350	case 'n':
351	  val = ts->team_id;
352	  goto do_int;
353	case 'N':
354	  val = ts->team ? ts->team->nthreads : 1;
355	  goto do_int;
356	case 'a':
357	  val = ts->team ? ts->team->prev_ts.team_id : -1;
358	  goto do_int;
359	case 'H':
360	  gomp_display_hostname (buffer, size, &ret, right, sz);
361	  break;
362	case 'P':
363#ifdef HAVE_GETPID
364	  val = getpid ();
365#else
366	  val = 0;
367#endif
368	  goto do_int;
369	case 'i':
370#if defined(LIBGOMP_USE_PTHREADS) && defined(__GNUC__)
371	  {
372	    char buf[3 * (sizeof (handle) + sizeof (uintptr_t) + sizeof (int))
373		     + 4];
374	    /* This macro returns expr unmodified for integral or pointer
375	       types and 0 for anything else (e.g. aggregates).  */
376#define gomp_nonaggregate(expr) \
377  __builtin_choose_expr (__builtin_classify_type (expr) == 1		    \
378			 || __builtin_classify_type (expr) == 5, expr, 0)
379	    /* This macro returns expr unmodified for integral types,
380	       (uintptr_t) (expr) for pointer types and 0 for anything else
381	       (e.g. aggregates).  */
382#define gomp_integral(expr) \
383  __builtin_choose_expr (__builtin_classify_type (expr) == 5,		    \
384			 (uintptr_t) gomp_nonaggregate (expr),		    \
385			 gomp_nonaggregate (expr))
386
387	    if (sizeof (gomp_integral (handle)) == sizeof (unsigned long))
388	      sprintf (buf, "0x%lx", (unsigned long) gomp_integral (handle));
389#if defined (HAVE_INTTYPES_H) && defined (PRIx64)
390	    else if (sizeof (gomp_integral (handle)) == sizeof (uint64_t))
391	      sprintf (buf, "0x%" PRIx64, (uint64_t) gomp_integral (handle));
392#else
393	    else if (sizeof (gomp_integral (handle))
394		     == sizeof (unsigned long long))
395	      sprintf (buf, "0x%llx",
396		       (unsigned long long) gomp_integral (handle));
397#endif
398	    else
399	      sprintf (buf, "0x%x", (unsigned int) gomp_integral (handle));
400	    gomp_display_num (buffer, size, &ret, zero, right, sz, buf);
401	    break;
402	  }
403#else
404	  val = 0;
405	  goto do_int;
406#endif
407	case 'A':
408	  if (sz == (size_t) -1)
409	    gomp_display_affinity_place (buffer, size, &ret,
410					 place - 1);
411	  else if (right)
412	    {
413	      size_t len = 0;
414	      gomp_display_affinity_place (NULL, 0, &len, place - 1);
415	      if (len < sz)
416		gomp_display_repeat (buffer, size, &ret, ' ', sz - len);
417	      gomp_display_affinity_place (buffer, size, &ret, place - 1);
418	    }
419	  else
420	    {
421	      size_t start = ret;
422	      gomp_display_affinity_place (buffer, size, &ret, place - 1);
423	      if (ret - start < sz)
424		gomp_display_repeat (buffer, size, &ret, ' ', sz - (ret - start));
425	    }
426	  break;
427	do_int:
428	  gomp_display_int (buffer, size, &ret, zero, right, sz, val);
429	  break;
430	default:
431	  gomp_fatal ("unsupported type %c in affinity format", c);
432	}
433      format = p + 1;
434    }
435  while (1);
436  return ret;
437}
438
439size_t
440omp_capture_affinity (char *buffer, size_t size, const char *format)
441{
442  struct gomp_thread *thr = gomp_thread ();
443  size_t ret
444    = gomp_display_affinity (buffer, size,
445			     format && *format
446			     ? format : gomp_affinity_format_var,
447			     gomp_thread_self (), &thr->ts, thr->place);
448  if (size)
449    {
450      if (ret >= size)
451	buffer[size - 1] = '\0';
452      else
453	buffer[ret] = '\0';
454    }
455  return ret;
456}
457ialias (omp_capture_affinity)
458
459void
460omp_display_affinity (const char *format)
461{
462  char buf[512];
463  char *b;
464  size_t ret = ialias_call (omp_capture_affinity) (buf, sizeof buf, format);
465  if (ret < sizeof buf)
466    {
467      buf[ret] = '\n';
468      gomp_print_string (buf, ret + 1);
469      return;
470    }
471  b = gomp_malloc (ret + 1);
472  ialias_call (omp_capture_affinity) (b, ret + 1, format);
473  b[ret] = '\n';
474  gomp_print_string (b, ret + 1);
475  free (b);
476}
477
478void
479gomp_display_affinity_thread (gomp_thread_handle handle,
480			      struct gomp_team_state *ts, unsigned int place)
481{
482  char buf[512];
483  char *b;
484  size_t ret = gomp_display_affinity (buf, sizeof buf, gomp_affinity_format_var,
485				      handle, ts, place);
486  if (ret < sizeof buf)
487    {
488      buf[ret] = '\n';
489      gomp_print_string (buf, ret + 1);
490      return;
491    }
492  b = gomp_malloc (ret + 1);
493  gomp_display_affinity (b, ret + 1, gomp_affinity_format_var,
494  			 handle, ts, place);
495  b[ret] = '\n';
496  gomp_print_string (b, ret + 1);
497  free (b);
498}
499