mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-04-15 07:01:42 +03:00
169 lines
4.6 KiB
C
169 lines
4.6 KiB
C
#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 <errno.h>
|
|
# include <fcntl.h>
|
|
# include <stdio.h>
|
|
# include <stdlib.h> // strtoul
|
|
# include <string.h>
|
|
# include <unistd.h>
|
|
|
|
/*
|
|
* 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/<pid>/maps is:
|
|
* <start>-<end> <perms> <offset> <dev> <inode> <pathname>
|
|
*
|
|
* 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 <start>-<end> */
|
|
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/<pid>/maps as [STACK:<tid>]. 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/<pid>/task/<tid>/maps (though not
|
|
* labeled as [STACK:tid]).
|
|
* https://lists.ubuntu.com/archives/kernel-team/2016-March/074681.html
|
|
*/
|
|
char maps_path[64]; // "/proc/<pid>/task/<tid>/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__
|