mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-25 14:23:12 +00:00
Enable heap profiling on MacOS
This commit is contained in:
@@ -88,6 +88,7 @@ my %obj_tool_map = (
|
|||||||
#"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables
|
#"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables
|
||||||
#"addr2line_pdb" => "addr2line-pdb", # ditto
|
#"addr2line_pdb" => "addr2line-pdb", # ditto
|
||||||
#"otool" => "otool", # equivalent of objdump on OS X
|
#"otool" => "otool", # equivalent of objdump on OS X
|
||||||
|
#"dyld_info" => "dyld_info", # equivalent of otool on OS X for shared cache
|
||||||
);
|
);
|
||||||
# NOTE: these are lists, so you can put in commandline flags if you want.
|
# NOTE: these are lists, so you can put in commandline flags if you want.
|
||||||
my @DOT = ("dot"); # leave non-absolute, since it may be in /usr/local
|
my @DOT = ("dot"); # leave non-absolute, since it may be in /usr/local
|
||||||
@@ -4661,7 +4662,65 @@ sub ParseTextSectionHeaderFromOtool {
|
|||||||
return $r;
|
return $r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Parse text section header of a library in OS X shared cache using dyld_info
|
||||||
|
sub ParseTextSectionHeaderFromDyldInfo {
|
||||||
|
my $lib = shift;
|
||||||
|
|
||||||
|
my $size = undef;
|
||||||
|
my $vma;
|
||||||
|
my $file_offset;
|
||||||
|
# Get dyld_info output from the library file to figure out how to
|
||||||
|
# map between mapped addresses and addresses in the library.
|
||||||
|
my $cmd = ShellEscape($obj_tool_map{"dyld_info"}, "-segments", $lib);
|
||||||
|
open(DYLD, "$cmd |") || error("$cmd: $!\n");
|
||||||
|
|
||||||
|
while (<DYLD>) {
|
||||||
|
s/\r//g; # turn windows-looking lines into unix-looking lines
|
||||||
|
# -segments:
|
||||||
|
# load-address segment section sect-size seg-size perm
|
||||||
|
# 0x1803E0000 __TEXT 112KB r.x
|
||||||
|
# 0x1803E4F34 __text 80960
|
||||||
|
# 0x1803F8B74 __auth_stubs 768
|
||||||
|
# 0x1803F8E74 __init_offsets 4
|
||||||
|
# 0x1803F8E78 __gcc_except_tab 1180
|
||||||
|
my @x = split;
|
||||||
|
if ($#x >= 2) {
|
||||||
|
if ($x[0] eq 'load-offset') {
|
||||||
|
# dyld_info should only be used for the shared lib.
|
||||||
|
return undef;
|
||||||
|
} elsif ($x[1] eq '__TEXT') {
|
||||||
|
$file_offset = $x[0];
|
||||||
|
} elsif ($x[1] eq '__text') {
|
||||||
|
$size = $x[2];
|
||||||
|
$vma = $x[0];
|
||||||
|
$file_offset = AddressSub($x[0], $file_offset);
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(DYLD);
|
||||||
|
|
||||||
|
if (!defined($vma) || !defined($size) || !defined($file_offset)) {
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $r = {};
|
||||||
|
$r->{size} = $size;
|
||||||
|
$r->{vma} = $vma;
|
||||||
|
$r->{file_offset} = $file_offset;
|
||||||
|
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
sub ParseTextSectionHeader {
|
sub ParseTextSectionHeader {
|
||||||
|
# obj_tool_map("dyld_info") is only defined if we're in a Mach-O environment
|
||||||
|
if (defined($obj_tool_map{"dyld_info"})) {
|
||||||
|
my $r = ParseTextSectionHeaderFromDyldInfo(@_);
|
||||||
|
if (defined($r)){
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# if dyld_info doesn't work, or we don't have it, fall back to otool
|
||||||
# obj_tool_map("otool") is only defined if we're in a Mach-O environment
|
# obj_tool_map("otool") is only defined if we're in a Mach-O environment
|
||||||
if (defined($obj_tool_map{"otool"})) {
|
if (defined($obj_tool_map{"otool"})) {
|
||||||
my $r = ParseTextSectionHeaderFromOtool(@_);
|
my $r = ParseTextSectionHeaderFromOtool(@_);
|
||||||
@@ -4702,7 +4761,7 @@ sub ParseLibraries {
|
|||||||
$offset = HexExtend($3);
|
$offset = HexExtend($3);
|
||||||
$lib = $4;
|
$lib = $4;
|
||||||
$lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
|
$lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
|
||||||
} elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
|
} elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.(so|dll|dylib|bundle)(\.\d+)*)/) {
|
||||||
# Cooked line from DumpAddressMap. Example:
|
# Cooked line from DumpAddressMap. Example:
|
||||||
# 40000000-40015000: /lib/ld-2.3.2.so
|
# 40000000-40015000: /lib/ld-2.3.2.so
|
||||||
$start = HexExtend($1);
|
$start = HexExtend($1);
|
||||||
@@ -4719,6 +4778,15 @@ sub ParseLibraries {
|
|||||||
$offset = HexExtend($3);
|
$offset = HexExtend($3);
|
||||||
$lib = $4;
|
$lib = $4;
|
||||||
$lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
|
$lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
|
||||||
|
} elsif (($l =~ /^\s*($h)-($h):\s*(\S+)/) && ($3 eq $prog)) {
|
||||||
|
# PIEs and address space randomization do not play well with our
|
||||||
|
# default assumption that main executable is at lowest
|
||||||
|
# addresses. So we're detecting main executable from
|
||||||
|
# DumpAddressMap as well.
|
||||||
|
$start = HexExtend($1);
|
||||||
|
$finish = HexExtend($2);
|
||||||
|
$offset = $zero_offset;
|
||||||
|
$lib = $3;
|
||||||
}
|
}
|
||||||
# FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
|
# FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
|
||||||
# function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
|
# function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
|
||||||
@@ -5249,6 +5317,7 @@ sub ConfigureObjTools {
|
|||||||
if ($file_type =~ /Mach-O/) {
|
if ($file_type =~ /Mach-O/) {
|
||||||
# OS X uses otool to examine Mach-O files, rather than objdump.
|
# OS X uses otool to examine Mach-O files, rather than objdump.
|
||||||
$obj_tool_map{"otool"} = "otool";
|
$obj_tool_map{"otool"} = "otool";
|
||||||
|
$obj_tool_map{"dyld_info"} = "dyld_info";
|
||||||
$obj_tool_map{"addr2line"} = "false"; # no addr2line
|
$obj_tool_map{"addr2line"} = "false"; # no addr2line
|
||||||
$obj_tool_map{"objdump"} = "false"; # no objdump
|
$obj_tool_map{"objdump"} = "false"; # no objdump
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -605,6 +605,72 @@ prof_dump_close(prof_dump_arg_t *arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
|
||||||
|
#ifdef __LP64__
|
||||||
|
typedef struct mach_header_64 mach_header_t;
|
||||||
|
typedef struct segment_command_64 segment_command_t;
|
||||||
|
#define MH_MAGIC_VALUE MH_MAGIC_64
|
||||||
|
#define MH_CIGAM_VALUE MH_CIGAM_64
|
||||||
|
#define LC_SEGMENT_VALUE LC_SEGMENT_64
|
||||||
|
#else
|
||||||
|
typedef struct mach_header mach_header_t;
|
||||||
|
typedef struct segment_command segment_command_t;
|
||||||
|
#define MH_MAGIC_VALUE MH_MAGIC
|
||||||
|
#define MH_CIGAM_VALUE MH_CIGAM
|
||||||
|
#define LC_SEGMENT_VALUE LC_SEGMENT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
prof_dump_dyld_image_vmaddr(buf_writer_t *buf_writer, uint32_t image_index) {
|
||||||
|
const mach_header_t *header = (const mach_header_t *)
|
||||||
|
_dyld_get_image_header(image_index);
|
||||||
|
if (header == NULL || (header->magic != MH_MAGIC_VALUE &&
|
||||||
|
header->magic != MH_CIGAM_VALUE)) {
|
||||||
|
// Invalid header
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
intptr_t slide = _dyld_get_image_vmaddr_slide(image_index);
|
||||||
|
const char *name = _dyld_get_image_name(image_index);
|
||||||
|
struct load_command *load_cmd = (struct load_command *)
|
||||||
|
((char *)header + sizeof(mach_header_t));
|
||||||
|
for (uint32_t i = 0; load_cmd && (i < header->ncmds); i++) {
|
||||||
|
if (load_cmd->cmd == LC_SEGMENT_VALUE) {
|
||||||
|
const segment_command_t *segment_cmd =
|
||||||
|
(const segment_command_t *)load_cmd;
|
||||||
|
if (!strcmp(segment_cmd->segname, "__TEXT")) {
|
||||||
|
char buffer[PATH_MAX + 1];
|
||||||
|
malloc_snprintf(buffer, sizeof(buffer),
|
||||||
|
"%016llx-%016llx: %s\n", segment_cmd->vmaddr + slide,
|
||||||
|
segment_cmd->vmaddr + slide + segment_cmd->vmsize, name);
|
||||||
|
buf_writer_cb(buf_writer, buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
load_cmd =
|
||||||
|
(struct load_command *)((char *)load_cmd + load_cmd->cmdsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
prof_dump_dyld_maps(buf_writer_t *buf_writer) {
|
||||||
|
uint32_t image_count = _dyld_image_count();
|
||||||
|
for (uint32_t i = 0; i < image_count; i++) {
|
||||||
|
prof_dump_dyld_image_vmaddr(buf_writer, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prof_dump_open_maps_t *JET_MUTABLE prof_dump_open_maps = NULL;
|
||||||
|
|
||||||
|
static void
|
||||||
|
prof_dump_maps(buf_writer_t *buf_writer) {
|
||||||
|
buf_writer_cb(buf_writer, "\nMAPPED_LIBRARIES:\n");
|
||||||
|
/* No proc map file to read on MacOS, dump dyld maps for backtrace. */
|
||||||
|
prof_dump_dyld_maps(buf_writer);
|
||||||
|
}
|
||||||
|
#else /* !__APPLE__ */
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
JEMALLOC_FORMAT_PRINTF(1, 2)
|
JEMALLOC_FORMAT_PRINTF(1, 2)
|
||||||
static int
|
static int
|
||||||
@@ -670,6 +736,7 @@ prof_dump_maps(buf_writer_t *buf_writer) {
|
|||||||
buf_writer_pipe(buf_writer, prof_dump_read_maps_cb, &mfd);
|
buf_writer_pipe(buf_writer, prof_dump_read_maps_cb, &mfd);
|
||||||
close(mfd);
|
close(mfd);
|
||||||
}
|
}
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
prof_dump(tsd_t *tsd, bool propagate_err, const char *filename,
|
prof_dump(tsd_t *tsd, bool propagate_err, const char *filename,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
if [ "x${enable_prof}" = "x1" ] ; then
|
if [ "x${enable_prof}" = "x1" ] ; then
|
||||||
export MALLOC_CONF="prof:true,prof_active:false,prof_gdump:true"
|
export MALLOC_CONF="prof:true,prof_active:false,prof_gdump:true,lg_prof_sample:0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ expect_maps_write_failure(int count) {
|
|||||||
TEST_BEGIN(test_mdump_maps_error) {
|
TEST_BEGIN(test_mdump_maps_error) {
|
||||||
test_skip_if(!config_prof);
|
test_skip_if(!config_prof);
|
||||||
test_skip_if(!config_debug);
|
test_skip_if(!config_debug);
|
||||||
|
test_skip_if(prof_dump_open_maps == NULL);
|
||||||
|
|
||||||
prof_dump_open_file_t *open_file_orig = prof_dump_open_file;
|
prof_dump_open_file_t *open_file_orig = prof_dump_open_file;
|
||||||
prof_dump_write_file_t *write_file_orig = prof_dump_write_file;
|
prof_dump_write_file_t *write_file_orig = prof_dump_write_file;
|
||||||
|
|||||||
Reference in New Issue
Block a user