Augment SyzyASAN crash reports with stack traces on alloc/free.

BUG=

Review URL: https://codereview.appspot.com/6817056

git-svn-id: http://sawbuck.googlecode.com/svn/trunk/syzygy@1211 15e8cca8-e42c-11de-a347-f34a4f72eb7d
diff --git a/agent/asan/asan.gyp b/agent/asan/asan.gyp
index ba27a8b..0bd3659 100644
--- a/agent/asan/asan.gyp
+++ b/agent/asan/asan.gyp
@@ -1,10 +1,10 @@
-# Copyright 2012 Google Inc.
+# Copyright 2012 Google Inc. All Rights Reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
-#      http://www.apache.org/licenses/LICENSE-2.0
+#     http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -54,9 +54,9 @@
       'type': 'executable',
       'sources': [
         'asan_heap_unittest.cc',
-        'asan_shadow_unittest.cc',
         'asan_rtl_unittest.cc',
         'asan_rtl_unittests_main.cc',
+        'asan_shadow_unittest.cc',
       ],
       'dependencies': [
         'asan_rtl',
diff --git a/agent/asan/asan_heap.cc b/agent/asan/asan_heap.cc
index b396b99..4a3fe45 100644
--- a/agent/asan/asan_heap.cc
+++ b/agent/asan/asan_heap.cc
@@ -23,7 +23,7 @@
 namespace {
 
 // Redzone size allocated at the start of every heap block.
-const size_t kRedZoneSize = 32;
+const size_t kRedZoneSize = 32U;
 
 // Utility class which implements an auto lock for a HeapProxy.
 class HeapLocker {
@@ -48,6 +48,28 @@
   DISALLOW_COPY_AND_ASSIGN(HeapLocker);
 };
 
+// Capture a stack trace and store it in an array of instruction pointer values.
+// @param stack_trace A pointer to a non-allocated array of instruction pointer,
+//     *stack_trace is assumed to be equal NULL when we call this function, the
+//     array will be dynamically allocated.
+// @param trace_size A pointer to the variable where the stack size should be
+//     saved.
+void CaptureStackTrace(void** stack_trace, uint8* trace_size) {
+  DCHECK(stack_trace != NULL);
+  DCHECK(*stack_trace == NULL);
+
+  size_t trace_depth = 0;
+  base::debug::StackTrace trace;
+  const void* temp_trace = trace.Addresses(&trace_depth);
+  *stack_trace = new void*[trace_depth];
+  memcpy(*stack_trace, temp_trace, trace_depth * sizeof(void*));
+
+  // From http://msdn.microsoft.com/en-us/library/bb204633.aspx,
+  // the sum of FramesToSkip and FramesToCapture must be less than 63.
+  DCHECK_LE(trace_depth, 62U);
+  *trace_size = static_cast<uint8>(trace_depth);
+}
+
 }  // namespace
 
 HeapProxy::HeapProxy()
@@ -115,6 +137,9 @@
   block->magic_number = kBlockHeaderSignature;
   block->size = bytes;
   block->state = ALLOCATED;
+  block->alloc_stack_trace = NULL;
+  block->free_stack_trace = NULL;
+  CaptureStackTrace(&block->alloc_stack_trace, &block->alloc_stack_trace_size);
 
   uint8* block_alloc = ToAlloc(block);
   Shadow::Unpoison(block_alloc, bytes);
@@ -158,12 +183,12 @@
     return false;
   }
 
+  CaptureStackTrace(&block->free_stack_trace, &block->free_stack_trace_size);
   DCHECK(ToAlloc(block) == mem);
   if (!Shadow::IsAccessible(ToAlloc(block)))
     return false;
 
   QuarantineBlock(block);
-
   return true;
 }
 
@@ -241,7 +266,6 @@
   quarantine_size_ += alloc_size;
   // Mark the block as quarantined.
   free_block->state = QUARANTINED;
-
   // Arbitrarily keep ten megabytes of quarantine per heap.
   const size_t kMaxQuarantineSizeBytes = 10 * 1024 * 1024;
 
@@ -258,6 +282,16 @@
     Shadow::Unpoison(free_block, alloc_size);
     free_block->state = FREED;
     free_block->magic_number = ~kBlockHeaderSignature;
+    if (free_block->alloc_stack_trace != NULL) {
+      delete free_block->alloc_stack_trace;
+      free_block->alloc_stack_trace = NULL;
+      free_block->alloc_stack_trace_size = 0;
+    }
+    if (free_block->free_stack_trace != NULL) {
+      delete free_block->free_stack_trace;
+      free_block->free_stack_trace = NULL;
+      free_block->free_stack_trace_size = 0;
+    }
     DCHECK_NE(kBlockHeaderSignature, free_block->magic_number);
     ::HeapFree(heap_, 0, free_block);
 
@@ -303,7 +337,6 @@
                                         BadAccessKind bad_access_kind) {
   DCHECK(addr != NULL);
   DCHECK(header != NULL);
-
   uint8* block_alloc = ToAlloc(header);
   int offset = 0;
   char* offset_relativity = "";
@@ -332,6 +365,20 @@
           header->size,
           block_alloc,
           block_alloc + header->size);
+  if (header->free_stack_trace != NULL) {
+    fprintf(stderr, "freed here:\n");
+    base::debug::StackTrace alloc_trace(
+        static_cast<const void* const*>(header->free_stack_trace),
+        header->free_stack_trace_size);
+    alloc_trace.PrintBacktrace();
+  }
+  if (header->alloc_stack_trace != NULL) {
+    fprintf(stderr, "previously allocated here:\n");
+    base::debug::StackTrace alloc_trace(
+        static_cast<const void* const*>(header->alloc_stack_trace),
+        header->alloc_stack_trace_size);
+    alloc_trace.PrintBacktrace();
+  }
 
   Shadow::PrintShadowMemoryForAddress(addr);
 }
diff --git a/agent/asan/asan_heap.h b/agent/asan/asan_heap.h
index 692676d..5d7cfc1 100644
--- a/agent/asan/asan_heap.h
+++ b/agent/asan/asan_heap.h
@@ -20,6 +20,7 @@
 
 #include <windows.h>  // NOLINT
 
+#include "base/debug/stack_trace.h"
 #include "base/synchronization/lock.h"
 #include "syzygy/agent/common/dlist.h"
 
@@ -96,11 +97,14 @@
   };
 
   // Every allocated block starts with a BlockHeader.
-  // TODO(sebmarchand): Add field for stack trace etc.
   struct BlockHeader {
     size_t magic_number;
     size_t size;
     BlockState state;
+    void* alloc_stack_trace;
+    void* free_stack_trace;
+    uint8 alloc_stack_trace_size;
+    uint8 free_stack_trace_size;
   };
 
   // Returns the block header for an alloc.
@@ -179,7 +183,7 @@
   // Total size of blocks in quarantine.
   size_t quarantine_size_;  // Under lock_.
 
-   // The entry linking to us.
+  // The entry linking to us.
   LIST_ENTRY list_entry_;
 };