1 /* Copyright (C) 2018-2021 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 
40 bool
gomp_print_string(const char * str,size_t len)41 gomp_print_string (const char *str, size_t len)
42 {
43   return fwrite (str, 1, len, stderr) != len;
44 }
45 
46 void
gomp_set_affinity_format(const char * format,size_t len)47 gomp_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 
65 void
omp_set_affinity_format(const char * format)66 omp_set_affinity_format (const char *format)
67 {
68   gomp_set_affinity_format (format, strlen (format));
69 }
70 
71 size_t
omp_get_affinity_format(char * buffer,size_t size)72 omp_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 
88 void
gomp_display_string(char * buffer,size_t size,size_t * ret,const char * str,size_t len)89 gomp_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 
105 static void
gomp_display_repeat(char * buffer,size_t size,size_t * ret,char c,size_t len)106 gomp_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 
122 static void
gomp_display_num(char * buffer,size_t size,size_t * ret,bool zero,bool right,size_t sz,char * buf)123 gomp_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 
158 static void
gomp_display_int(char * buffer,size_t size,size_t * ret,bool zero,bool right,size_t sz,int num)159 gomp_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 
167 static void
gomp_display_string_len(char * buffer,size_t size,size_t * ret,bool right,size_t sz,char * str,size_t len)168 gomp_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 
189 static void
gomp_display_hostname(char * buffer,size_t size,size_t * ret,bool right,size_t sz)190 gomp_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 
240 struct affinity_types_struct {
241   char long_str[18];
242   char long_len;
243   char short_c; };
244 
245 static 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 
262 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)263 gomp_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 
436 size_t
omp_capture_affinity(char * buffer,size_t size,const char * format)437 omp_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 }
ialias(omp_capture_affinity)454 ialias (omp_capture_affinity)
455 
456 void
457 omp_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 
475 void
gomp_display_affinity_thread(gomp_thread_handle handle,struct gomp_team_state * ts,unsigned int place)476 gomp_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