libxsmm_trace.c 20.8 KB
Newer Older
lisj's avatar
lisj committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
/******************************************************************************
* Copyright (c) Intel Corporation - All rights reserved.                      *
* This file is part of the LIBXSMM library.                                   *
*                                                                             *
* For information on the license, see the LICENSE file.                       *
* Further information: https://github.com/hfp/libxsmm/                        *
* SPDX-License-Identifier: BSD-3-Clause                                       *
******************************************************************************/
/* Hans Pabst (Intel Corp.)
******************************************************************************/
#include "libxsmm_trace.h"
#include "libxsmm_main.h"

#if !defined(LIBXSMM_TRACE_MINDEPTH) || 0 > (LIBXSMM_TRACE_MINDEPTH)
# undef LIBXSMM_TRACE_MINDEPTH
# define LIBXSMM_TRACE_MINDEPTH 1
#endif
#if !defined(LIBXSMM_TRACE_MAXDEPTH) || 0 >= (LIBXSMM_TRACE_MAXDEPTH)
# undef LIBXSMM_TRACE_MAXDEPTH
# define LIBXSMM_TRACE_MAXDEPTH 1024
#endif
#if !defined(LIBXSMM_TRACE_SYMBOLSIZE) || 0 >= (LIBXSMM_TRACE_SYMBOLSIZE)
# undef LIBXSMM_TRACE_SYMBOLSIZE
# define LIBXSMM_TRACE_SYMBOLSIZE 256
#endif
#if !defined(LIBXSMM_TRACE_DLINFO) && defined(__USE_GNU)
# define LIBXSMM_TRACE_DLINFO
#endif

#if defined(LIBXSMM_OFFLOAD_TARGET)
# pragma offload_attribute(push,target(LIBXSMM_OFFLOAD_TARGET))
#endif
#if !defined(NDEBUG)
# include <errno.h>
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
# include <Windows.h>
# if defined(_MSC_VER)
#   pragma warning(push)
#   pragma warning(disable: 4091)
# endif
# include <DbgHelp.h>
# if defined(_MSC_VER)
#   pragma comment(lib, "dbghelp")
# endif
# if defined(_MSC_VER)
#   pragma warning(pop)
# endif
LIBXSMM_APIVAR_DEFINE(volatile LONG internal_trace_initialized);
#else
LIBXSMM_APIVAR_DEFINE(volatile int internal_trace_initialized);
# include <execinfo.h>
# if defined(LIBXSMM_TRACE_DLINFO)
#   include <dlfcn.h>
# else
#   include <sys/stat.h>
#   include <sys/mman.h>
#   include <unistd.h>
#   include <pthread.h>
#   include <fcntl.h>
#   if (0 != LIBXSMM_SYNC)
LIBXSMM_APIVAR_DEFINE(LIBXSMM_TLS_TYPE internal_trace_key);
LIBXSMM_APIVAR_DEFINE(void* internal_trace_symbols[LIBXSMM_NTHREADS_MAX]);
#   endif
LIBXSMM_API_INLINE void internal_delete(void* value)
{
  int fd;
# if !(defined(__APPLE__) && defined(__MACH__))
  LIBXSMM_ASSERT(NULL != value);
# endif
  fd = *((int*)value);
# if defined(NDEBUG)
  munmap(value, LIBXSMM_TRACE_SYMBOLSIZE);
# else /* library code is expected to be mute */
  if (0 != munmap(value, LIBXSMM_TRACE_SYMBOLSIZE)) {
    const int error = errno;
    fprintf(stderr, "LIBXSMM ERROR: %s (munmap error #%i at %p)\n",
      strerror(error), error, value);
  }
# endif
  if (0 <= fd) {
    close(fd);
  }
# if !defined(NDEBUG) /* library code is expected to be mute */
  else {
    fprintf(stderr, "LIBXSMM ERROR: invalid file descriptor (%i)\n", fd);
  }
# endif
}
# if defined(__APPLE__) && defined(__MACH__)
/* taken from "libtransmission" fdlimit.c */
LIBXSMM_API_INLINE int posix_fallocate(int fd, off_t offset, off_t length)
{
  fstore_t fst;
  fst.fst_flags = F_ALLOCATECONTIG;
  fst.fst_posmode = F_PEOFPOSMODE;
  fst.fst_offset = offset;
  fst.fst_length = length;
  fst.fst_bytesalloc = 0;
  return fcntl(fd, F_PREALLOCATE, &fst);
}
# elif (!defined(_XOPEN_SOURCE) || 600 > _XOPEN_SOURCE) && \
       (!defined(_POSIX_C_SOURCE) || 200112L > _POSIX_C_SOURCE)
/* C89: avoid warning about posix_fallocate declared implicitly */
LIBXSMM_EXTERN int posix_fallocate(int, off_t, off_t);
# endif
# endif
LIBXSMM_EXTERN int mkstemp(char*) LIBXSMM_NOTHROW;
#endif
#if defined(LIBXSMM_OFFLOAD_TARGET)
# pragma offload_attribute(pop)
#endif

LIBXSMM_APIVAR_DEFINE(int internal_trace_mindepth);
LIBXSMM_APIVAR_DEFINE(int internal_trace_threadid);
LIBXSMM_APIVAR_DEFINE(int internal_trace_maxnsyms);


LIBXSMM_API LIBXSMM_ATTRIBUTE_NO_TRACE int libxsmm_trace_init(int /*filter_threadid*/, int /*filter_mindepth*/, int /*filter_maxnsyms*/);
LIBXSMM_API int libxsmm_trace_init(int filter_threadid, int filter_mindepth, int filter_maxnsyms)
{
  int result = EXIT_SUCCESS;
  if (0 == internal_trace_initialized) {
    if (0 <= filter_threadid) ++filter_threadid;
#if defined(__TRACE)
    { const char *const env = getenv("LIBXSMM_TRACE");
      if (NULL != env && 0 != *env) {
        char buffer[32] = { 0 };
        if (1 == sscanf(env, "%32[^,],", buffer)) {
          result = (0 <= sscanf(buffer, "%i", &filter_threadid) ? EXIT_SUCCESS : EXIT_FAILURE);
        }
        if (1 == sscanf(env, "%*[^,],%32[^,],", buffer)) {
          result = (0 <= sscanf(buffer, "%i", &filter_mindepth) ? EXIT_SUCCESS : EXIT_FAILURE);
        }
        if (1 == sscanf(env, "%*[^,],%*[^,],%32s", buffer)) {
          result = (0 <= sscanf(buffer, "%i", &filter_maxnsyms) ? EXIT_SUCCESS : EXIT_FAILURE);
        }
        else {
          filter_maxnsyms = -1; /* all */
        }
        if (EXIT_SUCCESS == result) {
          internal_trace_initialized = -1; /* auto */
        }
      }
    }
    if (EXIT_SUCCESS == result)
#endif
    {
#if defined(LIBXSMM_TRACE)
# if defined(_WIN32) || defined(__CYGWIN__)
      SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
      result = (FALSE != SymInitialize(GetCurrentProcess(), NULL, TRUE) ? EXIT_SUCCESS : GetLastError());
# elif (0 != LIBXSMM_SYNC) && !defined(LIBXSMM_TRACE_DLINFO)
      result = LIBXSMM_TLS_CREATE(&internal_trace_key);
# endif
      if (EXIT_SUCCESS == result) {
        internal_trace_threadid = filter_threadid;
        internal_trace_maxnsyms = filter_maxnsyms;
        internal_trace_mindepth = filter_mindepth;
        if (0 == internal_trace_initialized) {
          internal_trace_initialized = 1;
        }
      }
#else
      LIBXSMM_UNUSED(filter_threadid);
      LIBXSMM_UNUSED(filter_mindepth);
      LIBXSMM_UNUSED(filter_maxnsyms);
#endif
    }
  }
  return result;
}


LIBXSMM_API LIBXSMM_ATTRIBUTE_NO_TRACE int libxsmm_trace_finalize(void);
LIBXSMM_API int libxsmm_trace_finalize(void)
{
  int result;
#if defined(LIBXSMM_TRACE)
  result = EXIT_SUCCESS;
  if (0 != internal_trace_initialized) {
    internal_trace_initialized = 0; /* disable */
# if defined(_WIN32) || defined(__CYGWIN__)
    result = (FALSE != SymCleanup(GetCurrentProcess()) ? EXIT_SUCCESS : GetLastError());
# elif (0 != LIBXSMM_SYNC) && !defined(LIBXSMM_TRACE_DLINFO)
    result = LIBXSMM_TLS_DESTROY(internal_trace_key);
    { int i = 0;
      for (; i < LIBXSMM_NTHREADS_MAX; ++i) {
        void *const buffer = internal_trace_symbols[i];
        if (NULL != buffer) internal_delete(buffer);
      }
    }
# endif
  }
#else
  result = EXIT_FAILURE;
#endif
  return result;
}


LIBXSMM_API LIBXSMM_ATTRIBUTE_NO_TRACE unsigned int libxsmm_backtrace(const void* /*buffer*/[], unsigned int /*size*/, unsigned int /*skip*/);
LIBXSMM_API
#if defined(_WIN32)
/*TODO: no inline*/
#elif defined(__GNUC__)
/*LIBXSMM_ATTRIBUTE(noinline)*/
#endif
unsigned int libxsmm_backtrace(const void* buffer[], unsigned int size, unsigned int skip)
{
  unsigned int result;
  if (NULL != buffer && 0 != size && skip < size) {
    skip += LIBXSMM_TRACE_MINDEPTH;
#if defined(_WIN32) || defined(__CYGWIN__)
    result = CaptureStackBackTrace(skip, LIBXSMM_MIN(size, LIBXSMM_TRACE_MAXDEPTH), (PVOID*)buffer, NULL/*hash*/);
#else
    { const int n = backtrace((void**)buffer, LIBXSMM_MIN((int)(size + skip), LIBXSMM_TRACE_MAXDEPTH));
      if ((int)skip < n) {
        result = n - skip;
        if (0 != skip) {
          memmove(buffer, buffer + skip, result * sizeof(void*));
        }
      }
      else {
        result = 0;
      }
    }
#endif
  }
  else {
    result = 0;
  }
  return result;
}


#if !defined(_WIN32) && !defined(__CYGWIN__)
LIBXSMM_API_INLINE const char* internal_trace_get_symbolname(const void* address, char* map, int fd, off_t fdoff)
{
  const char* result = NULL;
#if defined(LIBXSMM_TRACE_DLINFO)
  Dl_info info;
  LIBXSMM_UNUSED(fd); LIBXSMM_UNUSED(fdoff);
  LIBXSMM_ASSERT(NULL != address && NULL != map);
  if (0 != dladdr(address, &info) && NULL != info.dli_sname) {
    strncpy(map, info.dli_sname, LIBXSMM_TRACE_SYMBOLSIZE - 1);
    result = map;
  }
#else
  LIBXSMM_ASSERT(NULL != address && NULL != map);
  backtrace_symbols_fd((void**)&address, 1, fd);
  if (fdoff == lseek(fd, fdoff, SEEK_SET) /* reset map */
    && 1 == sscanf(map, "%*[^(](%s0x", map))
  {
    char* c = map;
    for (; '+' != *c && 0 != *c; ++c);
    if ('+' == *c && c != map) {
      result = map;
      map = c;
    }
  }
  *map = 0; /* terminate */
#endif
  return result;
}
#endif


LIBXSMM_API LIBXSMM_ATTRIBUTE_NO_TRACE
const char* libxsmm_trace_info(unsigned int* /*depth*/, unsigned int* /*threadid*/, const int* /*filter_threadid*/,
  const void* /*filter_symbol*/, const int* /*filter_mindepth*/, const int* /*filter_maxnsyms*/);

LIBXSMM_API
#if defined(_WIN32)
/*TODO: no inline*/
#elif defined(__GNUC__)
/*LIBXSMM_ATTRIBUTE(noinline)*/
#endif
const char* libxsmm_trace_info(unsigned int* depth, unsigned int* threadid, const int* filter_threadid,
  const void* filter_symbol, const int* filter_mindepth, const int* filter_maxnsyms)
{
  const char *fname = NULL;
#if defined(LIBXSMM_TRACE)
  static LIBXSMM_TLS int cerberus = 0;
  /* check against entering a recursion (recursion should not happen due to
   * attribute "no_instrument_function" but better prevent this in any case)
   */
  if (0 == cerberus) {
    int init;
    ++cerberus;
# if defined(__GNUC__) && !defined(_CRAYC)
    __asm__("");
# endif
    init = LIBXSMM_ATOMIC_LOAD(&internal_trace_initialized, LIBXSMM_ATOMIC_RELAXED);
    if (0 != init) { /* do nothing if not yet initialized */
      const int mindepth = (NULL != filter_mindepth ? *filter_mindepth : internal_trace_mindepth);
      const int maxnsyms = (NULL != filter_maxnsyms ? *filter_maxnsyms : internal_trace_maxnsyms);
      const void *stacktrace[LIBXSMM_TRACE_MAXDEPTH];
      const int n = libxsmm_backtrace(stacktrace, LIBXSMM_TRACE_MAXDEPTH, 0);
      int symbol = 0;
      if (0 < n) {
        const int filter = (NULL != filter_threadid ? *filter_threadid : internal_trace_threadid);
        int abs_tid = 0;
# if defined(_WIN32) || defined(__CYGWIN__) || defined(LIBXSMM_TRACE_DLINFO)
        static LIBXSMM_TLS struct {
#   if defined(_WIN32) || defined(__CYGWIN__)
          char buffer[sizeof(SYMBOL_INFO)+LIBXSMM_TRACE_SYMBOLSIZE];
#   else
          char buffer[LIBXSMM_TRACE_SYMBOLSIZE];
#   endif
          int tid;
        } info;
        if (0 != info.tid) {
          abs_tid = LIBXSMM_ABS(info.tid);
        }
        else {
          const int tid = LIBXSMM_ATOMIC_ADD_FETCH(&internal_trace_initialized, 0 < init ? 1 : -1, LIBXSMM_ATOMIC_RELAXED);
          abs_tid = LIBXSMM_ABS(tid) - 1;
          /* use sign bit to flag enabled fallback for symbol resolution */
          info.tid = -abs_tid;
        }
        LIBXSMM_ASSERT(0 < abs_tid);
        if (0 > filter || filter == abs_tid) {
          int next = symbol + 1;
#   if defined(_WIN32) || defined(__CYGWIN__)
          const HANDLE process = GetCurrentProcess();
          PSYMBOL_INFO value = (PSYMBOL_INFO)info.buffer;
          value->SizeOfStruct = sizeof(SYMBOL_INFO);
          value->MaxNameLen = LIBXSMM_TRACE_SYMBOLSIZE - 1;
          value->NameLen = 0;
#   endif
          if (NULL != filter_symbol) {
            struct { size_t d; int s; } approx = { (size_t)LIBXSMM_UNLIMITED, 0 };
            while (next < n && (filter_symbol == stacktrace[symbol] ||
#   if defined(_WIN32) || defined(__CYGWIN__)
              (FALSE != SymFromAddr(process, (DWORD64)stacktrace[symbol], NULL, value) && 0 < value->NameLen)))
            {
              if (filter_symbol == stacktrace[symbol] || NULL != strstr(value->Name, (const char*)filter_symbol)) {
#   else
              (NULL != internal_trace_get_symbolname(stacktrace[symbol], info.buffer, 0, 0))))
            {
              if (filter_symbol == stacktrace[symbol] || NULL != strstr(info.buffer, (const char*)filter_symbol)) {
#   endif
                symbol = next++; /* determine the symbol after the match which is checked below */
                break;
              }
              { const size_t d = LIBXSMM_DELTA((const char*)filter_symbol, (const char*)stacktrace[symbol]);
                if (d < approx.d) {
                  approx.s = symbol + 1;
                  approx.d = d;
                }
              }
              symbol = next++;
            }
            symbol = LIBXSMM_MAX((next != n ? symbol : approx.s/*not found*/) + mindepth/*shift*/, 0);
          }
          /* apply filters based on absolute symbol position */
          if ((NULL != filter_symbol || LIBXSMM_MAX(mindepth, 0) <= symbol) && (0 >= maxnsyms || symbol < maxnsyms)) {
            if (symbol != next && symbol < n && filter_symbol != stacktrace[symbol] &&
#   if defined(_WIN32) || defined(__CYGWIN__)
              FALSE != SymFromAddr(process, (DWORD64)stacktrace[symbol], NULL, value) && 0 < value->NameLen)
#   else
              NULL != internal_trace_get_symbolname(stacktrace[symbol], info.buffer, 0, 0))
#   endif
            {
              /* disable fallback allowing unresolved symbol names */
              info.tid = abs_tid; /* make unsigned */
#   if defined(_WIN32) || defined(__CYGWIN__)
              fname = value->Name;
#   else
              fname = info.buffer;
#   endif
            }
            if (NULL == fname && 0 > info.tid) { /* fallback allowing unresolved symbol names */
#   if defined(__MINGW32__)
              sprintf(info.buffer, "%p", stacktrace[symbol]);
#   else
              sprintf(info.buffer, "0x%" PRIxPTR, (uintptr_t)stacktrace[symbol]);
#   endif
              fname = info.buffer;
            }
          }
        }
# else
#   if (0 == LIBXSMM_SYNC)
        static char raw_c;
        char */*const*/ raw_value = &raw_c; /* const: avoid warning (below / constant control-flow) */
#   else
        char *const raw_value = (char*)LIBXSMM_TLS_GETVALUE(internal_trace_key);
#   endif
        const off_t fdoff = sizeof(int) * 2;
        int* ivalue = NULL, fd = -1;
        char* value = NULL;
        if (NULL != raw_value) {
          ivalue = (int*)raw_value;
          abs_tid = (0 <= ivalue[1] ? ivalue[1] : -ivalue[1]);
          if (0 > filter || filter == abs_tid) {
            fd = ivalue[0];
            if (0 <= fd && fdoff == lseek(fd, fdoff, SEEK_SET)) {
              value = raw_value + fdoff;
            }
#   if !defined(NDEBUG) /* library code is expected to be mute */
            else {
              fprintf(stderr, "LIBXSMM ERROR: failed to get buffer\n");
            }
#   endif
          }
        }
        else {
          char filename[] = "/tmp/.libxsmm_map." LIBXSMM_MKTEMP_PATTERN;
          /* coverity[secure_temp] */
          fd = mkstemp(filename);
          if (0 <= fd) {
            if (0 == unlink(filename) && 0 == posix_fallocate(fd, 0, LIBXSMM_TRACE_SYMBOLSIZE)) {
              char *const buffer = (char*)mmap(NULL, LIBXSMM_TRACE_SYMBOLSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
              if (MAP_FAILED != buffer) {
                int check = -1;
                ivalue = (int*)buffer;
                ivalue[0] = fd; /* valid file descriptor for internal_delete */
                if (
#   if (0 != LIBXSMM_SYNC)
                  0 == LIBXSMM_TLS_SETVALUE(internal_trace_key, buffer) &&
#   endif
                  (sizeof(int) * 1) == read(fd, &check, sizeof(int)) &&
                  fdoff == lseek(fd, sizeof(int), SEEK_CUR) &&
                  check == fd)
                {
                  const int tid = LIBXSMM_ATOMIC_ADD_FETCH(&internal_trace_initialized, 0 < init ? 1 : -1, LIBXSMM_ATOMIC_RELAXED);
                  abs_tid = LIBXSMM_ABS(tid) - 1;
                  LIBXSMM_ASSERT(0 < abs_tid);
#   if (0 != LIBXSMM_SYNC)
                  LIBXSMM_ASSERT(abs_tid < LIBXSMM_NTHREADS_MAX);
                  internal_trace_symbols[abs_tid] = buffer;
#   endif
                  /* use sign bit to flag enabled fallback for symbol resolution */
                  ivalue[1] = -abs_tid;
                  if (0 > filter || (abs_tid - 1) == filter) {
                    value = buffer + fdoff;
                  }
                }
                else {
#   if !defined(NDEBUG) /* library code is expected to be mute */
                  fprintf(stderr, "LIBXSMM ERROR: failed to setup buffer\n");
#   endif
                  internal_delete(buffer);
                }
              }
#   if !defined(NDEBUG)
              else {
                const int error = errno;
                fprintf(stderr, "LIBXSMM ERROR: %s (mmap allocation error #%i)\n",
                  strerror(error), error);
              }
#   endif
            }
#   if !defined(NDEBUG) /* library code is expected to be mute */
            else {
              fprintf(stderr, "LIBXSMM ERROR: failed to setup file descriptor (%i)\n", fd);
            }
#   endif
          }
        }
        if (NULL != value) {
          int next = symbol + 1;
          if (NULL != filter_symbol) {
            struct { size_t d; int s; } approx = { (size_t)LIBXSMM_UNLIMITED, 0 };
            while (next < n && (filter_symbol == stacktrace[symbol] ||
              NULL != internal_trace_get_symbolname(stacktrace[symbol], value, fd, fdoff)))
            {
              if (filter_symbol == stacktrace[symbol] || NULL != strstr(value, (const char*)filter_symbol)) {
                symbol = next++; /* determine the symbol after the match which is checked below */
                break;
              }
              { const size_t d = LIBXSMM_DELTA((const char*)filter_symbol, (const char*)stacktrace[symbol]);
                if (d < approx.d) {
                  approx.s = symbol + 1;
                  approx.d = d;
                }
              }
              symbol = next++;
            }
            symbol = LIBXSMM_MAX((next != n ? symbol : approx.s/*not found*/) + mindepth/*shift*/, 0);
          }
          /* apply filters based on absolute symbol position */
          if ((NULL != filter_symbol || LIBXSMM_MAX(mindepth, 0) <= symbol) && (0 >= maxnsyms || symbol < maxnsyms)) {
            if (symbol != next && symbol < n && filter_symbol != stacktrace[symbol] &&
              NULL != internal_trace_get_symbolname(stacktrace[symbol], value, fd, fdoff))
            {
              /* disable fallback allowing unresolved symbol names */
              ivalue[1] = abs_tid; /* make unsigned */
              fname = value;
            }
            if (NULL == fname && 0 > ivalue[1]) { /* fallback to symbol address */
              sprintf(value, "0x%llx", (unsigned long long)stacktrace[symbol]);
              fname = value;
            }
          }
        }
# endif
        if (threadid) *threadid = abs_tid - 1;
        if (depth) *depth = symbol;
      }
    }
    --cerberus;
  }
#else
  LIBXSMM_UNUSED(depth);
  LIBXSMM_UNUSED(threadid);
  LIBXSMM_UNUSED(filter_threadid);
  LIBXSMM_UNUSED(filter_symbol);
  LIBXSMM_UNUSED(filter_mindepth);
  LIBXSMM_UNUSED(filter_maxnsyms);
#endif
  return fname;
}


LIBXSMM_API LIBXSMM_ATTRIBUTE_NO_TRACE
void libxsmm_trace(FILE* stream, const int* /*filter_threadid*/, const void* /*filter_symbol*/, const int* /*filter_mindepth*/, const int* /*filter_maxnsyms*/);

LIBXSMM_API void libxsmm_trace(FILE* stream, const int* filter_threadid, const void* filter_symbol, const int* filter_mindepth, const int* filter_maxnsyms)
{
#if defined(LIBXSMM_TRACE)
  unsigned int depth, threadid;
  const char *const name = libxsmm_trace_info(&depth, &threadid, filter_threadid, filter_symbol, filter_mindepth, filter_maxnsyms);
  if (NULL != name && 0 != *name) { /* implies actual other results to be valid */
    LIBXSMM_ASSERT(NULL != stream/*otherwise fprintf handles the error*/);
    if ((NULL == filter_threadid && 0 > internal_trace_threadid) || (NULL != filter_threadid && 0 > *filter_threadid)) {
      fprintf(stream, "%*s%s@%u\n", (int)depth, "", name, threadid);
    }
    else {
      fprintf(stream, "%*s%s\n", (int)depth, "", name);
    }
  }
#else /* suppress warning */
  LIBXSMM_UNUSED(stream);
  LIBXSMM_UNUSED(filter_threadid);
  LIBXSMM_UNUSED(filter_symbol);
  LIBXSMM_UNUSED(filter_mindepth);
  LIBXSMM_UNUSED(filter_maxnsyms);
#endif
}


#if defined(__TRACE) && defined(__GNUC__) && defined(LIBXSMM_BUILD)

LIBXSMM_API LIBXSMM_ATTRIBUTE_NO_TRACE void __cyg_profile_func_enter(void* /*this_fn*/, void* /*call_site*/);
LIBXSMM_API void __cyg_profile_func_enter(void* this_fn, void* call_site)
{
#if defined(LIBXSMM_TRACE)
  if (0 > internal_trace_initialized) {
    /* NULL: inherit global settings from libxsmm_trace_init */
    libxsmm_trace(stderr, NULL/*filter_threadid*/, "__cyg_profile_func_enter"/*LIBXSMM_FUNCNAME*/, NULL, NULL);
  }
#endif
  LIBXSMM_UNUSED(this_fn); LIBXSMM_UNUSED(call_site);
}


LIBXSMM_API LIBXSMM_ATTRIBUTE_NO_TRACE void __cyg_profile_func_exit(void* /*this_fn*/, void* /*call_site*/);
LIBXSMM_API void __cyg_profile_func_exit(void* this_fn, void* call_site)
{
  LIBXSMM_UNUSED(this_fn); LIBXSMM_UNUSED(call_site); /* suppress warning */
}

#endif /*defined(__TRACE) && defined(__GNUC__) && defined(LIBXSMM_BUILD)*/