Coverage for /Syzygy/agent/asan/error_info.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
94.7%2162280.C++source

Line-by-line coverage:

   1    :  // Copyright 2014 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/agent/asan/error_info.h"
  16    :  
  17    :  #include "base/strings/string_util.h"
  18    :  #include "syzygy/agent/asan/asan_runtime.h"
  19    :  #include "syzygy/agent/asan/block_utils.h"
  20    :  #include "syzygy/agent/asan/shadow.h"
  21    :  #include "syzygy/agent/asan/stack_capture_cache.h"
  22    :  #include "syzygy/crashdata/crashdata.h"
  23    :  
  24    :  namespace agent {
  25    :  namespace asan {
  26    :  
  27    :  namespace {
  28    :  
  29    :  // Copy a stack capture object into an array.
  30    :  // @param stack_capture The stack capture that we want to copy.
  31    :  // @param dst Will receive the stack frames.
  32    :  // @param dst_size Will receive the number of frames that has been copied.
  33    :  void CopyStackCaptureToArray(const common::StackCapture* stack_capture,
  34  E :                               void* dst, uint8* dst_size) {
  35  E :    DCHECK_NE(reinterpret_cast<common::StackCapture*>(NULL), stack_capture);
  36  E :    DCHECK_NE(reinterpret_cast<void*>(NULL), dst);
  37  E :    DCHECK_NE(reinterpret_cast<uint8*>(NULL), dst_size);
  38    :    ::memcpy(dst,
  39    :             stack_capture->frames(),
  40  E :             stack_capture->num_frames() * sizeof(void*));
  41  E :    *dst_size = stack_capture->num_frames();
  42  E :  }
  43    :  
  44    :  // Get the information about an address relative to a block.
  45    :  // @param header The header of the block containing this address.
  46    :  // @param bad_access_info Will receive the information about this address.
  47    :  void GetAddressInformation(BlockHeader* header,
  48  E :                             AsanErrorInfo* bad_access_info) {
  49  E :    DCHECK(header != NULL);
  50  E :    DCHECK(bad_access_info != NULL);
  51    :  
  52  E :    DCHECK(header != NULL);
  53  E :    DCHECK(bad_access_info != NULL);
  54  E :    DCHECK(bad_access_info->location != NULL);
  55    :  
  56  E :    BlockInfo block_info = {};
  57  E :    Shadow::BlockInfoFromShadow(header, &block_info);
  58  E :    int offset = 0;
  59  E :    char* offset_relativity = "";
  60  E :    switch (bad_access_info->error_type) {
  61    :      case HEAP_BUFFER_OVERFLOW: {
  62    :        offset = static_cast<const uint8*>(bad_access_info->location)
  63  E :            - block_info.body - block_info.body_size;
  64  E :        offset_relativity = "beyond";
  65  E :        break;
  66    :      }
  67    :      case HEAP_BUFFER_UNDERFLOW: {
  68    :        offset = block_info.body -
  69  E :            static_cast<const uint8*>(bad_access_info->location);
  70  E :        offset_relativity = "before";
  71  E :        break;
  72    :      }
  73    :      case USE_AFTER_FREE: {
  74    :        offset = static_cast<const uint8*>(bad_access_info->location)
  75  E :            - block_info.body;
  76  E :        offset_relativity = "inside";
  77  E :        break;
  78    :      }
  79    :      case WILD_ACCESS:
  80    :      case DOUBLE_FREE:
  81    :      case UNKNOWN_BAD_ACCESS:
  82    :      case CORRUPT_BLOCK:
  83  E :        return;
  84    :      default:
  85  i :        NOTREACHED() << "Error trying to dump address information.";
  86    :    }
  87    :  
  88    :    size_t shadow_info_bytes = base::snprintf(
  89    :        bad_access_info->shadow_info,
  90    :        arraysize(bad_access_info->shadow_info) - 1,
  91    :        "%08X is %d bytes %s %d-byte block [%08X,%08X)\n",
  92    :        bad_access_info->location,
  93    :        offset,
  94    :        offset_relativity,
  95    :        block_info.body_size,
  96    :        block_info.body,
  97  E :        block_info.trailer_padding);
  98    :  
  99  E :    std::string shadow_memory;
 100  E :    Shadow::AppendShadowArrayText(bad_access_info->location, &shadow_memory);
 101    :    size_t shadow_mem_bytes = base::snprintf(
 102    :        bad_access_info->shadow_memory,
 103    :        arraysize(bad_access_info->shadow_memory) - 1,
 104    :        "%s",
 105  E :        shadow_memory.c_str());
 106    :  
 107    :    // Ensure that we had enough space to store the full shadow information.
 108  E :    DCHECK_LE(shadow_info_bytes, arraysize(bad_access_info->shadow_info) - 1);
 109  E :    DCHECK_LE(shadow_mem_bytes, arraysize(bad_access_info->shadow_memory) - 1);
 110  E :  }
 111    :  
 112    :  }  // namespace
 113    :  
 114    :  const char kHeapUseAfterFree[] = "heap-use-after-free";
 115    :  const char kHeapBufferUnderFlow[] = "heap-buffer-underflow";
 116    :  const char kHeapBufferOverFlow[] = "heap-buffer-overflow";
 117    :  const char kAttemptingDoubleFree[] = "attempting double-free";
 118    :  const char kInvalidAddress[] = "invalid-address";
 119    :  const char kWildAccess[] = "wild-access";
 120    :  const char kHeapUnknownError[] = "heap-unknown-error";
 121    :  const char kHeapCorruptBlock[] = "corrupt-block";
 122    :  const char kCorruptHeap[] = "corrupt-heap";
 123    :  
 124  E :  const char* ErrorInfoAccessTypeToStr(BadAccessKind bad_access_kind) {
 125  E :    switch (bad_access_kind) {
 126    :      case USE_AFTER_FREE:
 127  E :        return kHeapUseAfterFree;
 128    :      case HEAP_BUFFER_UNDERFLOW:
 129  E :        return kHeapBufferUnderFlow;
 130    :      case HEAP_BUFFER_OVERFLOW:
 131  E :        return kHeapBufferOverFlow;
 132    :      case WILD_ACCESS:
 133  E :        return kWildAccess;
 134    :      case INVALID_ADDRESS:
 135  E :        return kInvalidAddress;
 136    :      case DOUBLE_FREE:
 137  E :        return kAttemptingDoubleFree;
 138    :      case UNKNOWN_BAD_ACCESS:
 139  E :        return kHeapUnknownError;
 140    :      case CORRUPT_BLOCK:
 141  E :        return kHeapCorruptBlock;
 142    :      case CORRUPT_HEAP:
 143  E :        return kCorruptHeap;
 144    :      default:
 145  i :        NOTREACHED() << "Unexpected bad access kind.";
 146  i :        return NULL;
 147    :    }
 148  E :  }
 149    :  
 150    :  bool ErrorInfoGetBadAccessInformation(StackCaptureCache* stack_cache,
 151  E :                                        AsanErrorInfo* bad_access_info) {
 152  E :    DCHECK_NE(reinterpret_cast<StackCaptureCache*>(NULL), stack_cache);
 153  E :    DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), bad_access_info);
 154  E :    BlockInfo block_info = {};
 155  E :    if (!Shadow::BlockInfoFromShadow(bad_access_info->location, &block_info))
 156  E :      return false;
 157    :  
 158    :    // Fill out the information about the primary block.
 159    :    ErrorInfoGetAsanBlockInfo(
 160  E :        block_info, stack_cache, &bad_access_info->block_info);
 161    :  
 162    :    if (bad_access_info->error_type != DOUBLE_FREE &&
 163  E :        bad_access_info->error_type != CORRUPT_BLOCK) {
 164    :      bad_access_info->error_type =
 165  E :          ErrorInfoGetBadAccessKind(bad_access_info->location, block_info.header);
 166    :    }
 167    :  
 168    :    // Get the bad access description if we've been able to determine its kind.
 169  E :    if (bad_access_info->error_type != UNKNOWN_BAD_ACCESS) {
 170  E :      GetAddressInformation(block_info.header, bad_access_info);
 171  E :      return true;
 172    :    }
 173    :  
 174  i :    return false;
 175  E :  }
 176    :  
 177    :  BadAccessKind ErrorInfoGetBadAccessKind(const void* addr,
 178  E :                                          const BlockHeader* header) {
 179  E :    DCHECK_NE(reinterpret_cast<const void*>(NULL), addr);
 180  E :    DCHECK_NE(reinterpret_cast<const BlockHeader*>(NULL), header);
 181    :  
 182  E :    BadAccessKind bad_access_kind = UNKNOWN_BAD_ACCESS;
 183    :  
 184  E :    if (header->state == QUARANTINED_BLOCK) {
 185  E :      bad_access_kind = USE_AFTER_FREE;
 186  E :    } else {
 187  E :      BlockInfo block_info = {};
 188  E :      Shadow::BlockInfoFromShadow(header, &block_info);
 189  E :      if (addr < block_info.body) {
 190  E :        bad_access_kind = HEAP_BUFFER_UNDERFLOW;
 191  E :      } else if (addr >= (block_info.body + block_info.body_size)) {
 192  E :        bad_access_kind = HEAP_BUFFER_OVERFLOW;
 193  i :      } else if (Shadow::GetShadowMarkerForAddress(addr) == kHeapFreedMarker) {
 194    :        // This is a use after free on a block managed by a nested heap.
 195  i :        bad_access_kind = USE_AFTER_FREE;
 196    :      }
 197    :    }
 198  E :    return bad_access_kind;
 199  E :  }
 200    :  
 201    :  void ErrorInfoGetAsanBlockInfo(const BlockInfo& block_info,
 202    :                                 StackCaptureCache* stack_cache,
 203  E :                                 AsanBlockInfo* asan_block_info) {
 204  E :    DCHECK_NE(reinterpret_cast<StackCaptureCache*>(NULL), stack_cache);
 205  E :    DCHECK_NE(reinterpret_cast<AsanBlockInfo*>(NULL), asan_block_info);
 206    :  
 207  E :    ::memset(asan_block_info, 0, sizeof(*asan_block_info));
 208  E :    BlockAnalyze(block_info, &asan_block_info->analysis);
 209    :  
 210  E :    asan_block_info->header = block_info.header;
 211  E :    asan_block_info->user_size = block_info.header->body_size;
 212  E :    asan_block_info->state = block_info.header->state;
 213  E :    asan_block_info->alloc_tid = block_info.trailer->alloc_tid;
 214  E :    asan_block_info->free_tid = block_info.trailer->free_tid;
 215    :  
 216    :    if (block_info.header->state != ALLOCATED_BLOCK &&
 217  E :        block_info.trailer->free_ticks != 0) {
 218    :      asan_block_info->milliseconds_since_free =
 219  E :          ::GetTickCount() - block_info.trailer->free_ticks;
 220    :    }
 221    :  
 222    :    // TODO(chrisha): Use detailed analysis results to do this more efficiently.
 223  E :    asan_block_info->heap_type = kUnknownHeapType;
 224  E :    HeapManagerInterface::HeapId heap_id = block_info.trailer->heap_id;
 225  E :    if (heap_id != 0) {
 226  E :      AsanRuntime* runtime = AsanRuntime::runtime();
 227  E :      DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
 228  E :      asan_block_info->heap_type = runtime->GetHeapType(heap_id);
 229    :    }
 230    :  
 231    :    // Copy the alloc and free stack traces if they're valid.
 232    :    // TODO(chrisha): Use detailed analysis results that have been gathered
 233    :    //                once, rather than recalculating this.
 234    :    if (stack_cache->StackCapturePointerIsValid(
 235  E :            block_info.header->alloc_stack)) {
 236    :      CopyStackCaptureToArray(block_info.header->alloc_stack,
 237    :                              asan_block_info->alloc_stack,
 238  E :                              &asan_block_info->alloc_stack_size);
 239    :    }
 240    :    if (block_info.header->state != ALLOCATED_BLOCK &&
 241    :        stack_cache->StackCapturePointerIsValid(
 242  E :            block_info.header->free_stack)) {
 243    :      CopyStackCaptureToArray(block_info.header->free_stack,
 244    :                              asan_block_info->free_stack,
 245  E :                              &asan_block_info->free_stack_size);
 246    :    }
 247  E :  }
 248    :  
 249    :  namespace {
 250    :  
 251    :  // Converts an access mode to a string.
 252  E :  void AccessModeToString(AccessMode access_mode, std::string* str) {
 253  E :    DCHECK_NE(static_cast<std::string*>(nullptr), str);
 254  E :    switch (access_mode) {
 255  E :      case ASAN_READ_ACCESS: *str = "read"; break;
 256  i :      case ASAN_WRITE_ACCESS: *str = "write"; break;
 257  E :      default: *str = "(unknown)"; break;
 258    :    }
 259  E :  }
 260    :  
 261    :  // Converts a block state to a string.
 262  E :  void BlockStateToString(BlockState block_state, std::string* str) {
 263  E :    DCHECK_NE(static_cast<std::string*>(nullptr), str);
 264  E :    switch (block_state) {
 265  E :      case ALLOCATED_BLOCK: *str = "allocated"; break;
 266  E :      case QUARANTINED_BLOCK: *str = "quarantined"; break;
 267  i :      case FREED_BLOCK: *str = "freed"; break;
 268  i :      default: *str = "(unknown)"; break;
 269    :    }
 270  E :  }
 271    :  
 272  E :  uint64 CastAddress(const void* address) {
 273  E :    return static_cast<uint64>(reinterpret_cast<uint32>(address));
 274  E :  }
 275    :  
 276    :  void PopulateStackTrace(const void* const* frames,
 277    :                          size_t frame_count,
 278  E :                          crashdata::StackTrace* stack_trace) {
 279  E :    DCHECK_NE(static_cast<void*>(nullptr), frames);
 280  E :    DCHECK_LT(0u, frame_count);
 281  E :    DCHECK_NE(static_cast<crashdata::StackTrace*>(nullptr), stack_trace);
 282  E :    for (size_t i = 0; i < frame_count; ++i)
 283  E :      stack_trace->add_frames(CastAddress(frames[i]));
 284  E :  }
 285    :  
 286  E :  void DataStateToString(DataState data_state, std::string* str) {
 287  E :    DCHECK_NE(static_cast<std::string*>(nullptr), str);
 288  E :    switch (data_state) {
 289    :      default:
 290  E :      case kDataStateUnknown: *str = "(unknown)"; break;
 291  E :      case kDataIsClean: *str = "clean"; break;
 292  E :      case kDataIsCorrupt: *str = "corrupt"; break;
 293    :    }
 294  E :  }
 295    :  
 296    :  void PopulateBlockAnalysisResult(const BlockAnalysisResult& analysis,
 297  E :                                   crashdata::Dictionary* dict) {
 298  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 299    :    DataStateToString(
 300    :        analysis.block_state,
 301  E :        crashdata::LeafGetString(crashdata::DictAddLeaf("block", dict)));
 302    :    DataStateToString(
 303    :        analysis.header_state,
 304  E :        crashdata::LeafGetString(crashdata::DictAddLeaf("header", dict)));
 305    :    DataStateToString(
 306    :        analysis.body_state,
 307  E :        crashdata::LeafGetString(crashdata::DictAddLeaf("body", dict)));
 308    :    DataStateToString(
 309    :        analysis.trailer_state,
 310  E :        crashdata::LeafGetString(crashdata::DictAddLeaf("trailer", dict)));
 311  E :  }
 312    :  
 313    :  }  // namespace
 314    :  
 315    :  void PopulateBlockInfo(const AsanBlockInfo& block_info,
 316  E :                         crashdata::Value* value) {
 317  E :    DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
 318    :  
 319  E :    crashdata::Dictionary* dict = ValueGetDict(value);
 320  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 321    :  
 322    :    // Set block properties.
 323    :    crashdata::LeafGetAddress(crashdata::DictAddLeaf("header", dict))
 324  E :        ->set_address(CastAddress(block_info.header));
 325    :    crashdata::LeafSetUInt(block_info.user_size,
 326  E :                           crashdata::DictAddLeaf("user-size", dict));
 327    :    BlockStateToString(
 328    :        static_cast<BlockState>(block_info.state),
 329  E :        crashdata::LeafGetString(crashdata::DictAddLeaf("state", dict)));
 330    :    crashdata::LeafGetString(crashdata::DictAddLeaf("heap-type", dict))
 331  E :        ->assign(kHeapTypes[block_info.heap_type]);
 332    :  
 333    :    // Set the block analysis.
 334    :    PopulateBlockAnalysisResult(
 335    :        block_info.analysis,
 336  E :        crashdata::ValueGetDict(crashdata::DictAddValue("analysis", dict)));
 337    :  
 338    :    // Set the allocation information.
 339  E :    if (block_info.alloc_stack_size != 0) {
 340    :      crashdata::LeafSetUInt(block_info.alloc_tid,
 341  E :                             crashdata::DictAddLeaf("alloc-thread-id", dict));
 342    :      PopulateStackTrace(block_info.alloc_stack,
 343    :                         block_info.alloc_stack_size,
 344    :                         crashdata::LeafGetStackTrace(
 345  E :                             crashdata::DictAddLeaf("alloc-stack", dict)));
 346    :    }
 347    :  
 348    :    // Set the free information if available.
 349  E :    if (block_info.free_stack_size != 0) {
 350    :      crashdata::LeafSetUInt(block_info.free_tid,
 351  E :                             crashdata::DictAddLeaf("free-thread-id", dict));
 352    :      PopulateStackTrace(block_info.free_stack,
 353    :                         block_info.free_stack_size,
 354    :                         crashdata::LeafGetStackTrace(
 355  E :                             crashdata::DictAddLeaf("free-stack", dict)));
 356    :      crashdata::LeafSetUInt(
 357    :          block_info.milliseconds_since_free,
 358  E :          crashdata::DictAddLeaf("milliseconds-since-free", dict));
 359    :    }
 360  E :  }
 361    :  
 362    :  void PopulateCorruptBlockRange(const AsanCorruptBlockRange& range,
 363  E :                                 crashdata::Value* value) {
 364  E :    DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
 365    :  
 366  E :    crashdata::Dictionary* dict = ValueGetDict(value);
 367  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 368    :  
 369    :    crashdata::LeafGetAddress(crashdata::DictAddLeaf("address", dict))
 370  E :        ->set_address(CastAddress(range.address));
 371  E :    crashdata::LeafSetUInt(range.length, crashdata::DictAddLeaf("length", dict));
 372    :    crashdata::LeafSetUInt(range.block_count,
 373  E :                           crashdata::DictAddLeaf("block-count", dict));
 374    :  
 375    :    // Add the blocks.
 376  E :    if (range.block_info_count > 0) {
 377    :      crashdata::List* list = crashdata::ValueGetList(
 378  E :          crashdata::DictAddValue("blocks", dict));
 379  E :      for (size_t i = 0; i < range.block_info_count; ++i) {
 380  E :        if (range.block_info[i].header != nullptr)
 381  E :          PopulateBlockInfo(range.block_info[i], list->add_values());
 382  E :      }
 383    :    }
 384  E :  }
 385    :  
 386    :  namespace {
 387    :  
 388    :  void PopulateShadowMemoryBlob(const AsanErrorInfo& error_info,
 389  E :                                crashdata::Dictionary* dict) {
 390  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 391    :  
 392    :    // The shadow-info string can be reconstructed from information already in
 393    :    // the crash (location, block-info, access-mode, access-size), so there's no
 394    :    // need to send it. This is emitted as a blob.
 395  E :    uintptr_t index = reinterpret_cast<uintptr_t>(error_info.location);
 396  E :    index >>= kShadowRatioLog;
 397  E :    index = (index / Shadow::kShadowBytesPerLine) * Shadow::kShadowBytesPerLine;
 398    :    uintptr_t index_min = index -
 399  E :        Shadow::kShadowContextLines * Shadow::kShadowBytesPerLine;
 400  E :    if (index_min > index)
 401  i :      index_min = 0;
 402    :    uintptr_t index_max = index +
 403  E :        Shadow::kShadowContextLines * Shadow::kShadowBytesPerLine;
 404  E :    if (index_max < index)
 405  i :      index_max = 0;
 406  E :    uintptr_t length = index_max - index_min;
 407    :    crashdata::LeafSetUInt(
 408  E :        index, crashdata::DictAddLeaf("shadow-memory-index", dict));
 409    :    crashdata::Blob* blob = crashdata::LeafGetBlob(
 410  E :        crashdata::DictAddLeaf("shadow-memory", dict));
 411    :    blob->mutable_data()->assign(
 412    :        reinterpret_cast<const char*>(Shadow::shadow() + index_min),
 413  E :        length);
 414  E :  }
 415    :  
 416    :  void PopulatePageBitsBlob(const AsanErrorInfo& error_info,
 417  E :                            crashdata::Dictionary* dict) {
 418  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 419    :  
 420    :    // Emit information about page protections surround the address in question.
 421    :    static const size_t kPageBitsContext = 2;
 422  E :    uintptr_t index = reinterpret_cast<uintptr_t>(error_info.location);
 423  E :    index /= GetPageSize();  // 1 bit per page.
 424  E :    index /= 8;  // 8 bits per byte.
 425  E :    uintptr_t index_min = index - kPageBitsContext;
 426  E :    if (index_min > index)
 427  E :      index_min = 0;
 428  E :    uintptr_t index_max = index + 1 + kPageBitsContext;
 429  E :    if (index_max < index)
 430  i :      index_max = 0;
 431  E :    uintptr_t length = index_max - index_min;
 432    :    crashdata::LeafSetUInt(
 433  E :        index, crashdata::DictAddLeaf("page-bits-index", dict));
 434    :    crashdata::Blob* blob = crashdata::LeafGetBlob(
 435  E :        crashdata::DictAddLeaf("page-bits", dict));
 436    :    blob->mutable_data()->assign(
 437    :        reinterpret_cast<const char*>(Shadow::page_bits() + index_min),
 438  E :        length);
 439  E :  }
 440    :  
 441    :  }  // namespace
 442    :  
 443    :  // TODO(chrisha): Only emit information that makes sense for the given error
 444    :  //                type. For example, wild-access errors have no associated
 445    :  //                block information.
 446    :  void PopulateErrorInfo(const AsanErrorInfo& error_info,
 447  E :                         crashdata::Value* value) {
 448  E :    DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
 449    :  
 450    :    // Create a single outermost dictionary.
 451  E :    crashdata::Dictionary* dict = ValueGetDict(value);
 452  E :    DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
 453    :  
 454    :    crashdata::LeafGetAddress(crashdata::DictAddLeaf("location", dict))
 455  E :        ->set_address(CastAddress(error_info.location));
 456    :    crashdata::LeafSetUInt(error_info.crash_stack_id,
 457  E :                           crashdata::DictAddLeaf("crash-stack-id", dict));
 458  E :    if (error_info.block_info.header != nullptr) {
 459    :      PopulateBlockInfo(error_info.block_info,
 460  E :                        crashdata::DictAddValue("block-info", dict));
 461    :    }
 462    :    crashdata::LeafGetString(crashdata::DictAddLeaf("error-type", dict))
 463  E :        ->assign(ErrorInfoAccessTypeToStr(error_info.error_type));
 464    :    AccessModeToString(
 465    :        error_info.access_mode,
 466  E :        crashdata::LeafGetString(crashdata::DictAddLeaf("access-mode", dict)));
 467    :    crashdata::LeafSetUInt(error_info.access_size,
 468  E :                           crashdata::DictAddLeaf("access-size", dict));
 469    :  
 470  E :    PopulateShadowMemoryBlob(error_info, dict);
 471  E :    PopulatePageBitsBlob(error_info, dict);
 472    :  
 473    :    // Send information about corruption.
 474    :    crashdata::LeafSetUInt(error_info.heap_is_corrupt,
 475  E :                           crashdata::DictAddLeaf("heap-is-corrupt", dict));
 476    :    crashdata::LeafSetUInt(error_info.corrupt_range_count,
 477  E :                           crashdata::DictAddLeaf("corrupt-range-count", dict));
 478    :    crashdata::LeafSetUInt(error_info.corrupt_block_count,
 479  E :                           crashdata::DictAddLeaf("corrupt-block-count", dict));
 480  E :    if (error_info.corrupt_ranges_reported > 0) {
 481    :      crashdata::List* list = crashdata::ValueGetList(
 482  E :          crashdata::DictAddValue("corrupt-ranges", dict));
 483  E :      for (size_t i = 0; i < error_info.corrupt_ranges_reported; ++i) {
 484    :        PopulateCorruptBlockRange(error_info.corrupt_ranges[i],
 485  E :                                  list->add_values());
 486  E :      }
 487    :    }
 488  E :  }
 489    :  
 490    :  }  // namespace asan
 491    :  }  // namespace agent

Coverage information generated Thu Mar 26 16:15:41 2015.