Add Valgrind rosalloc support.

Bug: 9986565
Change-Id: Ibd2ba5e8b4fb7f2ed6c133a4b556a6dbb15a2f5e
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 189f01c..0f882d3 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -19,6 +19,9 @@
 
 #include "space.h"
 
+#include <valgrind.h>
+#include <memcheck/memcheck.h>
+
 namespace art {
 namespace gc {
 
@@ -185,6 +188,80 @@
   DISALLOW_COPY_AND_ASSIGN(MallocSpace);
 };
 
+// Number of bytes to use as a red zone (rdz). A red zone of this size will be placed before and
+// after each allocation. 8 bytes provides long/double alignment.
+static constexpr size_t kValgrindRedZoneBytes = 8;
+
+// A specialization of DlMallocSpace/RosAllocSpace that provides information to valgrind wrt allocations.
+template <typename BaseMallocSpaceType, typename AllocatorType>
+class ValgrindMallocSpace : public BaseMallocSpaceType {
+ public:
+  virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated) {
+    void* obj_with_rdz = BaseMallocSpaceType::AllocWithGrowth(self, num_bytes + 2 * kValgrindRedZoneBytes,
+                                                              bytes_allocated);
+    if (obj_with_rdz == NULL) {
+      return NULL;
+    }
+    mirror::Object* result = reinterpret_cast<mirror::Object*>(
+        reinterpret_cast<byte*>(obj_with_rdz) + kValgrindRedZoneBytes);
+    // Make redzones as no access.
+    VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes);
+    VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast<byte*>(result) + num_bytes, kValgrindRedZoneBytes);
+    return result;
+  }
+
+  virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) {
+    void* obj_with_rdz = BaseMallocSpaceType::Alloc(self, num_bytes + 2 * kValgrindRedZoneBytes,
+                                                    bytes_allocated);
+    if (obj_with_rdz == NULL) {
+     return NULL;
+    }
+    mirror::Object* result = reinterpret_cast<mirror::Object*>(
+        reinterpret_cast<byte*>(obj_with_rdz) + kValgrindRedZoneBytes);
+    // Make redzones as no access.
+    VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes);
+    VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast<byte*>(result) + num_bytes, kValgrindRedZoneBytes);
+    return result;
+  }
+
+  virtual size_t AllocationSize(const mirror::Object* obj) {
+    size_t result = BaseMallocSpaceType::AllocationSize(reinterpret_cast<const mirror::Object*>(
+        reinterpret_cast<const byte*>(obj) - kValgrindRedZoneBytes));
+    return result - 2 * kValgrindRedZoneBytes;
+  }
+
+  virtual size_t Free(Thread* self, mirror::Object* ptr) {
+    void* obj_after_rdz = reinterpret_cast<void*>(ptr);
+    void* obj_with_rdz = reinterpret_cast<byte*>(obj_after_rdz) - kValgrindRedZoneBytes;
+    // Make redzones undefined.
+    size_t allocation_size = BaseMallocSpaceType::AllocationSize(
+        reinterpret_cast<mirror::Object*>(obj_with_rdz));
+    VALGRIND_MAKE_MEM_UNDEFINED(obj_with_rdz, allocation_size);
+    size_t freed = BaseMallocSpaceType::Free(self, reinterpret_cast<mirror::Object*>(obj_with_rdz));
+    return freed - 2 * kValgrindRedZoneBytes;
+  }
+
+  virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
+    size_t freed = 0;
+    for (size_t i = 0; i < num_ptrs; i++) {
+      freed += Free(self, ptrs[i]);
+    }
+    return freed;
+  }
+
+  ValgrindMallocSpace(const std::string& name, MemMap* mem_map, AllocatorType allocator, byte* begin,
+                      byte* end, byte* limit, size_t growth_limit, size_t initial_size) :
+      BaseMallocSpaceType(name, mem_map, allocator, begin, end, limit, growth_limit) {
+    VALGRIND_MAKE_MEM_UNDEFINED(mem_map->Begin() + initial_size, mem_map->Size() - initial_size);
+  }
+
+  virtual ~ValgrindMallocSpace() {
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ValgrindMallocSpace);
+};
+
 }  // namespace space
 }  // namespace gc
 }  // namespace art