#include "jemalloc/internal/jemalloc_preamble.h" #include "jemalloc/internal/jemalloc_internal_includes.h" #include "jemalloc/internal/malloc_io.h" #include "jemalloc/internal/prof_sys.h" #if defined(__linux__) && defined(JEMALLOC_HAVE_GETTID) # include # include # include # include // strtoul # include # include /* * Converts a string representing a hexadecimal number to an unsigned long long * integer. Functionally equivalent to strtoull() (for base 16) but faster for * that case. * * @param nptr Pointer to the string to be converted. * @param endptr Pointer to a pointer to character, which will be set to the * character in `nptr` where parsing stopped. Can be NULL. * @return The converted unsigned long long integer value. */ static inline unsigned long long int strtoull_hex(const char *nptr, char **endptr) { unsigned long long int val = 0; int ii = 0; for (; ii < 16; ++ii) { char c = nptr[ii]; if (c >= '0' && c <= '9') { val = (val << 4) + (c - '0'); } else if (c >= 'a' && c <= 'f') { val = (val << 4) + (c - 'a' + 10); } else { break; } } if (endptr) { *endptr = (char *)(nptr + ii); } return val; } static int prof_mapping_containing_addr(uintptr_t addr, const char *maps_path, uintptr_t *mm_start, uintptr_t *mm_end) { int ret = ENOENT; /* not found */ *mm_start = *mm_end = 0; /* * Each line of /proc//maps is: * - * * The fields we care about are always within the first 34 characters so * as long as `buf` contains the start of a mapping line it can always be * parsed. */ static const int kMappingFieldsWidth = 34; int fd = -1; char buf[4096]; ssize_t remaining = 0; /* actual number of bytes read to buf */ char *line = NULL; while (1) { if (fd < 0) { /* case 0: initial open of maps file */ fd = malloc_open(maps_path, O_RDONLY); if (fd < 0) { return errno; } remaining = malloc_read_fd(fd, buf, sizeof(buf)); if (remaining <= 0) { ret = errno; break; } line = buf; } else if (line == NULL) { /* case 1: no newline found in buf */ remaining = malloc_read_fd(fd, buf, sizeof(buf)); if (remaining <= 0) { ret = errno; break; } line = memchr(buf, '\n', remaining); if (line != NULL) { line++; /* advance to character after newline */ remaining -= (line - buf); } } else if (line != NULL && remaining < kMappingFieldsWidth) { /* * case 2: found newline but insufficient characters remaining in * buf */ memcpy(buf, line, remaining); /* copy remaining characters to start of buf */ line = buf; size_t count = malloc_read_fd( fd, buf + remaining, sizeof(buf) - remaining); if (count <= 0) { ret = errno; break; } remaining += count; /* actual number of bytes read to buf */ } else { /* case 3: found newline and sufficient characters to parse */ /* parse - */ char *tmp = line; uintptr_t start_addr = (uintptr_t)strtoull_hex( tmp, &tmp); if (addr >= start_addr) { tmp++; /* advance to character after '-' */ uintptr_t end_addr = (uintptr_t)strtoull_hex( tmp, NULL); if (addr < end_addr) { *mm_start = start_addr; *mm_end = end_addr; ret = 0; break; } } /* Advance to character after next newline in the current buf. */ char *prev_line = line; line = memchr(line, '\n', remaining); if (line != NULL) { line++; /* advance to character after newline */ remaining -= (line - prev_line); } } } malloc_close(fd); return ret; } int prof_thread_stack_range(uintptr_t fp, uintptr_t *low, uintptr_t *high) { /* * NOTE: Prior to kernel 4.5 an entry for every thread stack was included in * /proc//maps as [STACK:]. Starting with kernel 4.5 only the main * thread stack remains as the [stack] mapping. For other thread stacks the * mapping is still visible in /proc//task//maps (though not * labeled as [STACK:tid]). * https://lists.ubuntu.com/archives/kernel-team/2016-March/074681.html */ char maps_path[64]; // "/proc//task//maps" malloc_snprintf(maps_path, sizeof(maps_path), "/proc/%d/task/%d/maps", getpid(), gettid()); return prof_mapping_containing_addr(fp, maps_path, low, high); } #else int prof_thread_stack_range( UNUSED uintptr_t addr, uintptr_t *stack_start, uintptr_t *stack_end) { *stack_start = *stack_end = 0; return ENOENT; } #endif // __linux__