Home | History | Annotate | Download | only in oom_dump
      1 // Copyright 2010 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 
     31 #include <algorithm>
     32 
     33 #include <google_breakpad/processor/minidump.h>
     34 
     35 #define ENABLE_DEBUGGER_SUPPORT
     36 
     37 #include <v8.h>
     38 
     39 namespace {
     40 
     41 using google_breakpad::Minidump;
     42 using google_breakpad::MinidumpContext;
     43 using google_breakpad::MinidumpThread;
     44 using google_breakpad::MinidumpThreadList;
     45 using google_breakpad::MinidumpException;
     46 using google_breakpad::MinidumpMemoryRegion;
     47 
     48 const char* InstanceTypeToString(int type) {
     49   static char const* names[v8::internal::LAST_TYPE] = {0};
     50   if (names[v8::internal::STRING_TYPE] == NULL) {
     51     using namespace v8::internal;
     52 #define SET(type) names[type] = #type;
     53     INSTANCE_TYPE_LIST(SET)
     54 #undef SET
     55   }
     56   return names[type];
     57 }
     58 
     59 
     60 u_int32_t ReadPointedValue(MinidumpMemoryRegion* region,
     61                            u_int64_t base,
     62                            int offset) {
     63   u_int32_t ptr = 0;
     64   CHECK(region->GetMemoryAtAddress(base + 4 * offset, &ptr));
     65   u_int32_t value = 0;
     66   CHECK(region->GetMemoryAtAddress(ptr, &value));
     67   return value;
     68 }
     69 
     70 
     71 void ReadArray(MinidumpMemoryRegion* region,
     72                u_int64_t array_ptr,
     73                int size,
     74                int* output) {
     75   for (int i = 0; i < size; i++) {
     76     u_int32_t value;
     77     CHECK(region->GetMemoryAtAddress(array_ptr + 4 * i, &value));
     78     output[i] = value;
     79   }
     80 }
     81 
     82 
     83 u_int32_t ReadArrayFrom(MinidumpMemoryRegion* region,
     84                         u_int64_t base,
     85                         int offset,
     86                         int size,
     87                         int* output) {
     88   u_int32_t ptr = 0;
     89   CHECK(region->GetMemoryAtAddress(base + 4 * offset, &ptr));
     90   ReadArray(region, ptr, size, output);
     91 }
     92 
     93 
     94 double toM(int size) {
     95   return size / (1024. * 1024.);
     96 }
     97 
     98 
     99 class IndirectSorter {
    100  public:
    101   explicit IndirectSorter(int* a) : a_(a) { }
    102 
    103   bool operator() (int i0, int i1) {
    104     return a_[i0] > a_[i1];
    105   }
    106 
    107  private:
    108   int* a_;
    109 };
    110 
    111 
    112 void DumpHeapStats(const char *minidump_file) {
    113   Minidump minidump(minidump_file);
    114   CHECK(minidump.Read());
    115 
    116   MinidumpException *exception = minidump.GetException();
    117   CHECK(exception);
    118 
    119   MinidumpContext* crash_context = exception->GetContext();
    120   CHECK(crash_context);
    121 
    122   u_int32_t exception_thread_id = 0;
    123   CHECK(exception->GetThreadID(&exception_thread_id));
    124 
    125   MinidumpThreadList* thread_list = minidump.GetThreadList();
    126   CHECK(thread_list);
    127 
    128   MinidumpThread* exception_thread =
    129       thread_list->GetThreadByID(exception_thread_id);
    130   CHECK(exception_thread);
    131 
    132   // Currently only 32-bit Windows minidumps are supported.
    133   CHECK_EQ(MD_CONTEXT_X86, crash_context->GetContextCPU());
    134 
    135   const MDRawContextX86* contextX86 = crash_context->GetContextX86();
    136   CHECK(contextX86);
    137 
    138   const u_int32_t esp = contextX86->esp;
    139 
    140   MinidumpMemoryRegion* memory_region = exception_thread->GetMemory();
    141   CHECK(memory_region);
    142 
    143   const u_int64_t last = memory_region->GetBase() + memory_region->GetSize();
    144 
    145   u_int64_t heap_stats_addr = 0;
    146   for (u_int64_t addr = esp; addr < last; addr += 4) {
    147     u_int32_t value = 0;
    148     CHECK(memory_region->GetMemoryAtAddress(addr, &value));
    149     if (value >= esp && value < last) {
    150       u_int32_t value2 = 0;
    151       CHECK(memory_region->GetMemoryAtAddress(value, &value2));
    152       if (value2 == v8::internal::HeapStats::kStartMarker) {
    153         heap_stats_addr = addr;
    154         break;
    155       }
    156     }
    157   }
    158   CHECK(heap_stats_addr);
    159 
    160   // Read heap stats.
    161 
    162 #define READ_FIELD(offset) \
    163   ReadPointedValue(memory_region, heap_stats_addr, offset)
    164 
    165   CHECK(READ_FIELD(0) == v8::internal::HeapStats::kStartMarker);
    166   CHECK(READ_FIELD(24) == v8::internal::HeapStats::kEndMarker);
    167 
    168   const int new_space_size = READ_FIELD(1);
    169   const int new_space_capacity = READ_FIELD(2);
    170   const int old_pointer_space_size = READ_FIELD(3);
    171   const int old_pointer_space_capacity = READ_FIELD(4);
    172   const int old_data_space_size = READ_FIELD(5);
    173   const int old_data_space_capacity = READ_FIELD(6);
    174   const int code_space_size = READ_FIELD(7);
    175   const int code_space_capacity = READ_FIELD(8);
    176   const int map_space_size = READ_FIELD(9);
    177   const int map_space_capacity = READ_FIELD(10);
    178   const int cell_space_size = READ_FIELD(11);
    179   const int cell_space_capacity = READ_FIELD(12);
    180   const int lo_space_size = READ_FIELD(13);
    181   const int global_handle_count = READ_FIELD(14);
    182   const int weak_global_handle_count = READ_FIELD(15);
    183   const int pending_global_handle_count = READ_FIELD(16);
    184   const int near_death_global_handle_count = READ_FIELD(17);
    185   const int destroyed_global_handle_count = READ_FIELD(18);
    186   const int memory_allocator_size = READ_FIELD(19);
    187   const int memory_allocator_capacity = READ_FIELD(20);
    188   const int os_error = READ_FIELD(23);
    189 #undef READ_FIELD
    190 
    191   int objects_per_type[v8::internal::LAST_TYPE + 1] = {0};
    192   ReadArrayFrom(memory_region, heap_stats_addr, 21,
    193                 v8::internal::LAST_TYPE + 1, objects_per_type);
    194 
    195   int size_per_type[v8::internal::LAST_TYPE + 1] = {0};
    196   ReadArrayFrom(memory_region, heap_stats_addr, 22, v8::internal::LAST_TYPE + 1,
    197                 size_per_type);
    198 
    199   int js_global_objects =
    200       objects_per_type[v8::internal::JS_GLOBAL_OBJECT_TYPE];
    201   int js_builtins_objects =
    202       objects_per_type[v8::internal::JS_BUILTINS_OBJECT_TYPE];
    203   int js_global_proxies =
    204       objects_per_type[v8::internal::JS_GLOBAL_PROXY_TYPE];
    205 
    206   int indices[v8::internal::LAST_TYPE + 1];
    207   for (int i = 0; i <= v8::internal::LAST_TYPE; i++) {
    208     indices[i] = i;
    209   }
    210 
    211   std::stable_sort(indices, indices + sizeof(indices)/sizeof(indices[0]),
    212                   IndirectSorter(size_per_type));
    213 
    214   int total_size = 0;
    215   for (int i = 0; i <= v8::internal::LAST_TYPE; i++) {
    216     total_size += size_per_type[i];
    217   }
    218 
    219   // Print heap stats.
    220 
    221   printf("exception thread ID: %" PRIu32 " (%#" PRIx32 ")\n",
    222          exception_thread_id, exception_thread_id);
    223   printf("heap stats address: %#" PRIx64 "\n", heap_stats_addr);
    224 #define PRINT_INT_STAT(stat) \
    225     printf("\t%-25s\t% 10d\n", #stat ":", stat);
    226 #define PRINT_MB_STAT(stat) \
    227     printf("\t%-25s\t% 10.3f MB\n", #stat ":", toM(stat));
    228   PRINT_MB_STAT(new_space_size);
    229   PRINT_MB_STAT(new_space_capacity);
    230   PRINT_MB_STAT(old_pointer_space_size);
    231   PRINT_MB_STAT(old_pointer_space_capacity);
    232   PRINT_MB_STAT(old_data_space_size);
    233   PRINT_MB_STAT(old_data_space_capacity);
    234   PRINT_MB_STAT(code_space_size);
    235   PRINT_MB_STAT(code_space_capacity);
    236   PRINT_MB_STAT(map_space_size);
    237   PRINT_MB_STAT(map_space_capacity);
    238   PRINT_MB_STAT(cell_space_size);
    239   PRINT_MB_STAT(cell_space_capacity);
    240   PRINT_MB_STAT(lo_space_size);
    241   PRINT_INT_STAT(global_handle_count);
    242   PRINT_INT_STAT(weak_global_handle_count);
    243   PRINT_INT_STAT(pending_global_handle_count);
    244   PRINT_INT_STAT(near_death_global_handle_count);
    245   PRINT_INT_STAT(destroyed_global_handle_count);
    246   PRINT_MB_STAT(memory_allocator_size);
    247   PRINT_MB_STAT(memory_allocator_capacity);
    248   PRINT_INT_STAT(os_error);
    249 #undef PRINT_STAT
    250 
    251   printf("\n");
    252 
    253   printf(
    254       "\tJS_GLOBAL_OBJECT_TYPE/JS_BUILTINS_OBJECT_TYPE/JS_GLOBAL_PROXY_TYPE: "
    255       "%d/%d/%d\n\n",
    256       js_global_objects, js_builtins_objects, js_global_proxies);
    257 
    258   int running_size = 0;
    259   for (int i = 0; i <= v8::internal::LAST_TYPE; i++) {
    260     int type = indices[i];
    261     const char* name = InstanceTypeToString(type);
    262     if (name == NULL) {
    263       // Unknown instance type.  Check that there is no objects of that type.
    264       CHECK_EQ(0, objects_per_type[type]);
    265       CHECK_EQ(0, size_per_type[type]);
    266       continue;
    267     }
    268     int size = size_per_type[type];
    269     running_size += size;
    270     printf("\t%-37s% 9d% 11.3f MB% 10.3f%%% 10.3f%%\n",
    271            name, objects_per_type[type], toM(size),
    272            100. * size / total_size, 100. * running_size / total_size);
    273   }
    274   printf("\t%-37s% 9d% 11.3f MB% 10.3f%%% 10.3f%%\n",
    275          "total", 0, toM(total_size), 100., 100.);
    276 }
    277 
    278 }  // namespace
    279 
    280 int main(int argc, char **argv) {
    281   if (argc != 2) {
    282     fprintf(stderr, "usage: %s <minidump>\n", argv[0]);
    283     return 1;
    284   }
    285 
    286   DumpHeapStats(argv[1]);
    287 
    288   return 0;
    289 }
    290