summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/examples/darwin/heap_find/heap/Makefile48
-rw-r--r--lldb/examples/darwin/heap_find/heap/heap_find.cpp332
2 files changed, 380 insertions, 0 deletions
diff --git a/lldb/examples/darwin/heap_find/heap/Makefile b/lldb/examples/darwin/heap_find/heap/Makefile
new file mode 100644
index 00000000000..719e367765d
--- /dev/null
+++ b/lldb/examples/darwin/heap_find/heap/Makefile
@@ -0,0 +1,48 @@
+#----------------------------------------------------------------------
+# Fill in the source files to build
+#----------------------------------------------------------------------
+# Uncomment line below for debugging shell commands
+# SHELL = /bin/sh -x
+
+#----------------------------------------------------------------------
+# Change any build/tool options needed
+#----------------------------------------------------------------------
+DS := /usr/bin/dsymutil
+CFLAGS ?=-arch x86_64 -arch i386 -gdwarf-2 -O0
+CPLUSPLUSFLAGS +=$(CFLAGS)
+CPPFLAGS +=$(CFLAGS)
+LDFLAGS = $(CFLAGS) -install_name "@executable_path/libheap.dylib" -dynamiclib
+CXX := $(shell xcrun -find clang++)
+LD := $(CXX)
+TEMPS =
+EXE=libheap.dylib
+DSYM=$(EXE).dSYM
+
+#----------------------------------------------------------------------
+# Make the dSYM file from the executable
+#----------------------------------------------------------------------
+$(DSYM) : $(EXE)
+ $(DS) -o "$(DSYM)" "$(EXE)"
+
+#----------------------------------------------------------------------
+# Compile the executable from all the objects (default rule) with no
+# dsym file.
+#----------------------------------------------------------------------
+$(EXE) : heap_find.o
+ $(LD) $(LDFLAGS) heap_find.o -o "$(EXE)"
+
+heap_find.o : heap_find.cpp
+ $(CXX) $(CFLAGS) -c heap_find.cpp
+
+#----------------------------------------------------------------------
+# Include all of the makefiles for each source file so we don't have
+# to manually track all of the prerequisites for each source file.
+#----------------------------------------------------------------------
+.PHONY: clean
+dsym: $(DSYM)
+all: $(EXE) $(DSYM)
+clean:
+ rm -rf "$(EXE)" "$(DSYM)" heap_find.o $(TEMPS)
+
+
+
diff --git a/lldb/examples/darwin/heap_find/heap/heap_find.cpp b/lldb/examples/darwin/heap_find/heap/heap_find.cpp
new file mode 100644
index 00000000000..85c3b7628bc
--- /dev/null
+++ b/lldb/examples/darwin/heap_find/heap/heap_find.cpp
@@ -0,0 +1,332 @@
+//===-- head_find.c ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file compiles into a dylib and can be used on darwin to find data that
+// is contained in active malloc blocks. To use this make the project, then
+// load the shared library in a debug session while you are stopped:
+//
+// (lldb) process load /path/to/libheap.dylib
+//
+// Now you can use the "find_pointer_in_heap" and "find_cstring_in_heap"
+// functions in the expression parser.
+//
+// This will grep everything in all active allocation blocks and print and
+// malloc blocks that contain the pointer 0x112233000000:
+//
+// (lldb) expression find_pointer_in_heap (0x112233000000)
+//
+// This will grep everything in all active allocation blocks and print and
+// malloc blocks that contain the C string "hello" (as a substring, no
+// NULL termination included):
+//
+// (lldb) expression find_cstring_in_heap ("hello")
+//
+// The results will be printed to the STDOUT of the inferior program. The
+// return value of the "find_pointer_in_heap" function is the number of
+// pointer references that were found. A quick example shows
+//
+// (lldb) expr find_pointer_in_heap(0x0000000104000410)
+// (uint32_t) $5 = 0x00000002
+// 0x104000740: 0x0000000104000410 found in malloc block 0x104000730 + 16 (malloc_size = 48)
+// 0x100820060: 0x0000000104000410 found in malloc block 0x100820000 + 96 (malloc_size = 4096)
+//
+// From the above output we see that 0x104000410 was found in the malloc block
+// at 0x104000730 and 0x100820000. If we want to see what these blocks are, we
+// can display the memory for this block using the "address" ("A" for short)
+// format. The address format shows pointers, and if those pointers point to
+// objects that have symbols or know data contents, it will display information
+// about the pointers:
+//
+// (lldb) memory read --format address --count 1 0x104000730
+// 0x104000730: 0x0000000100002460 (void *)0x0000000100002488: MyString
+//
+// We can see that the first block is a "MyString" object that contains our
+// pointer value at offset 16.
+//
+// Looking at the next pointers, are a bit more tricky:
+// (lldb) memory read -fA 0x100820000 -c1
+// 0x100820000: 0x4f545541a1a1a1a1
+// (lldb) memory read 0x100820000
+// 0x100820000: a1 a1 a1 a1 41 55 54 4f 52 45 4c 45 41 53 45 21 ....AUTORELEASE!
+// 0x100820010: 78 00 82 00 01 00 00 00 60 f9 e8 75 ff 7f 00 00 x.......`..u....
+//
+// This is an objective C auto release pool object that contains our pointer.
+// C++ classes will show up if they are virtual as something like:
+// (lldb) memory read --format address --count 1 0x104008000
+// 0x104008000: 0x109008000 vtable for lldb_private::Process
+//
+// This is a clue that the 0x104008000 is a "lldb_private::Process *".
+//===----------------------------------------------------------------------===//
+
+#include <assert.h>
+#include <ctype.h>
+#include <mach/mach.h>
+#include <malloc/malloc.h>
+#include <stack_logging.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+
+#define MAX_FRAMES 1024
+typedef void range_callback_t (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size);
+typedef void zone_callback_t (void *info, const malloc_zone_t *zone);
+
+struct range_callback_info_t
+{
+ zone_callback_t *zone_callback;
+ range_callback_t *range_callback;
+ void *baton;
+};
+
+enum data_type_t
+{
+ eDataTypeAddress,
+ eDataTypeContainsData
+};
+
+struct aligned_data_t
+{
+ const uint8_t *buffer;
+ uint32_t size;
+ uint32_t align;
+};
+
+struct range_contains_data_callback_info_t
+{
+ data_type_t type;
+ const void *lookup_addr;
+ union
+ {
+ uintptr_t addr;
+ aligned_data_t data;
+ };
+ uint32_t match_count;
+ bool done;
+};
+
+struct malloc_match
+{
+ void *addr;
+ intptr_t size;
+ intptr_t offset;
+};
+
+std::vector<malloc_match> g_matches;
+const void *g_lookup_addr = 0;
+mach_vm_address_t g_stack_frames[MAX_FRAMES];
+uint32_t g_stack_frames_count = 0;
+
+//----------------------------------------------------------------------
+// task_peek
+//
+// Reads memory from this tasks address space. This callback is needed
+// by the code that iterates through all of the malloc blocks to read
+// the memory in this process.
+//----------------------------------------------------------------------
+static kern_return_t
+task_peek (task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory)
+{
+ *local_memory = (void*) remote_address;
+ return KERN_SUCCESS;
+}
+
+
+static const void
+foreach_zone_in_this_process (range_callback_info_t *info)
+{
+ if (info == NULL || info->zone_callback == NULL)
+ return;
+
+ vm_address_t *zones = NULL;
+ unsigned int num_zones = 0;
+
+ kern_return_t err = malloc_get_all_zones (0, task_peek, &zones, &num_zones);
+ if (KERN_SUCCESS == err)
+ {
+ for (unsigned int i=0; i<num_zones; ++i)
+ {
+ info->zone_callback (info, (const malloc_zone_t *)zones[i]);
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+// dump_malloc_block_callback
+//
+// A simple callback that will dump each malloc block and all available
+// info from the enumeration callback perpective.
+//----------------------------------------------------------------------
+static void
+dump_malloc_block_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size)
+{
+ printf ("task = 0x%4.4x: baton = %p, type = %u, ptr_addr = 0x%llx + 0x%llu\n", task, baton, type, ptr_addr, ptr_size);
+}
+
+static void
+ranges_callback (task_t task, void *baton, unsigned type, vm_range_t *ptrs, unsigned count)
+{
+ range_callback_info_t *info = (range_callback_info_t *)baton;
+ while(count--) {
+ info->range_callback (task, info->baton, type, ptrs->address, ptrs->size);
+ ptrs++;
+ }
+}
+
+static void
+enumerate_range_in_zone (void *baton, const malloc_zone_t *zone)
+{
+ range_callback_info_t *info = (range_callback_info_t *)baton;
+
+ if (zone && zone->introspect)
+ zone->introspect->enumerator (mach_task_self(),
+ info,
+ MALLOC_PTR_IN_USE_RANGE_TYPE,
+ (vm_address_t)zone,
+ task_peek,
+ ranges_callback);
+}
+
+static void
+range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size)
+{
+ const uint64_t end_addr = ptr_addr + ptr_size;
+
+ range_contains_data_callback_info_t *info = (range_contains_data_callback_info_t *)baton;
+ switch (info->type)
+ {
+ case eDataTypeAddress:
+ if (ptr_addr <= info->addr && info->addr < end_addr)
+ {
+ ++info->match_count;
+ malloc_match match = { (void *)ptr_addr, ptr_size, info->addr - ptr_addr };
+ g_matches.push_back(match);
+ }
+ break;
+
+ case eDataTypeContainsData:
+ {
+ const uint32_t size = info->data.size;
+ if (size < ptr_size) // Make sure this block can contain this data
+ {
+ uint8_t *ptr_data = NULL;
+ if (task_peek (task, ptr_addr, ptr_size, (void **)&ptr_data) == KERN_SUCCESS)
+ {
+ const void *buffer = info->data.buffer;
+ assert (ptr_data);
+ const uint32_t align = info->data.align;
+ for (uint64_t addr = ptr_addr;
+ addr < end_addr && ((end_addr - addr) >= size);
+ addr += align, ptr_data += align)
+ {
+ if (memcmp (buffer, ptr_data, size) == 0)
+ {
+ ++info->match_count;
+ malloc_match match = { (void *)ptr_addr, ptr_size, addr - ptr_addr };
+ g_matches.push_back(match);
+ }
+ }
+ }
+ else
+ {
+ printf ("0x%llx: error: couldn't read %llu bytes\n", ptr_addr, ptr_size);
+ }
+ }
+ }
+ break;
+ }
+}
+
+//----------------------------------------------------------------------
+// find_pointer_in_heap
+//
+// Finds a pointer value inside one or more currently valid malloc
+// blocks.
+//----------------------------------------------------------------------
+malloc_match *
+find_pointer_in_heap (const void * addr)
+{
+ g_matches.clear();
+ // Setup "info" to look for a malloc block that contains data
+ // that is the a pointer
+ range_contains_data_callback_info_t data_info;
+ data_info.type = eDataTypeContainsData; // Check each block for data
+ g_lookup_addr = addr;
+ data_info.data.buffer = (uint8_t *)&addr; // What data? The pointer value passed in
+ data_info.data.size = sizeof(addr); // How many bytes? The byte size of a pointer
+ data_info.data.align = sizeof(addr); // Align to a pointer byte size
+ data_info.match_count = 0; // Initialize the match count to zero
+ data_info.done = false; // Set done to false so searching doesn't stop
+ range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info };
+ foreach_zone_in_this_process (&info);
+ if (g_matches.empty())
+ return NULL;
+ malloc_match match = { NULL, 0, 0 };
+ g_matches.push_back(match);
+ return g_matches.data();
+}
+
+
+//----------------------------------------------------------------------
+// find_cstring_in_heap
+//
+// Finds a C string inside one or more currently valid malloc blocks.
+//----------------------------------------------------------------------
+malloc_match *
+find_cstring_in_heap (const char *s)
+{
+ g_matches.clear();
+ if (s == NULL || s[0] == '\0')
+ {
+ printf ("error: invalid argument (empty cstring)\n");
+ return NULL;
+ }
+ // Setup "info" to look for a malloc block that contains data
+ // that is the C string passed in aligned on a 1 byte boundary
+ range_contains_data_callback_info_t data_info;
+ data_info.type = eDataTypeContainsData; // Check each block for data
+ g_lookup_addr = s; // If an expression was used, then fill in the resolved address we are looking up
+ data_info.data.buffer = (uint8_t *)s; // What data? The C string passed in
+ data_info.data.size = strlen(s); // How many bytes? The length of the C string
+ data_info.data.align = 1; // Data doesn't need to be aligned, so set the alignment to 1
+ data_info.match_count = 0; // Initialize the match count to zero
+ data_info.done = false; // Set done to false so searching doesn't stop
+ range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info };
+ foreach_zone_in_this_process (&info);
+ if (g_matches.empty())
+ return NULL;
+ malloc_match match = { NULL, 0, 0 };
+ g_matches.push_back(match);
+ return g_matches.data();
+}
+
+//----------------------------------------------------------------------
+// find_block_for_address
+//
+// Find the malloc block that whose address range contains "addr".
+//----------------------------------------------------------------------
+malloc_match *
+find_block_for_address (const void *addr)
+{
+ g_matches.clear();
+ // Setup "info" to look for a malloc block that contains data
+ // that is the C string passed in aligned on a 1 byte boundary
+ range_contains_data_callback_info_t data_info;
+ g_lookup_addr = addr; // If an expression was used, then fill in the resolved address we are looking up
+ data_info.type = eDataTypeAddress; // Check each block to see if the block contains the address passed in
+ data_info.addr = (uintptr_t)addr; // What data? The C string passed in
+ data_info.match_count = 0; // Initialize the match count to zero
+ data_info.done = false; // Set done to false so searching doesn't stop
+ range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info };
+ foreach_zone_in_this_process (&info);
+ if (g_matches.empty())
+ return NULL;
+ malloc_match match = { NULL, 0, 0 };
+ g_matches.push_back(match);
+ return g_matches.data();
+}
OpenPOWER on IntegriCloud