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