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#include <v8.h> 36 37namespace { 38 39using google_breakpad::Minidump; 40using google_breakpad::MinidumpContext; 41using google_breakpad::MinidumpThread; 42using google_breakpad::MinidumpThreadList; 43using google_breakpad::MinidumpException; 44using google_breakpad::MinidumpMemoryRegion; 45 46const char* InstanceTypeToString(int type) { 47 static char const* names[v8::internal::LAST_TYPE] = {0}; 48 if (names[v8::internal::STRING_TYPE] == NULL) { 49 using namespace v8::internal; 50#define SET(type) names[type] = #type; 51 INSTANCE_TYPE_LIST(SET) 52#undef SET 53 } 54 return names[type]; 55} 56 57 58u_int32_t ReadPointedValue(MinidumpMemoryRegion* region, 59 u_int64_t base, 60 int offset) { 61 u_int32_t ptr = 0; 62 CHECK(region->GetMemoryAtAddress(base + 4 * offset, &ptr)); 63 u_int32_t value = 0; 64 CHECK(region->GetMemoryAtAddress(ptr, &value)); 65 return value; 66} 67 68 69void ReadArray(MinidumpMemoryRegion* region, 70 u_int64_t array_ptr, 71 int size, 72 int* output) { 73 for (int i = 0; i < size; i++) { 74 u_int32_t value; 75 CHECK(region->GetMemoryAtAddress(array_ptr + 4 * i, &value)); 76 output[i] = value; 77 } 78} 79 80 81u_int32_t ReadArrayFrom(MinidumpMemoryRegion* region, 82 u_int64_t base, 83 int offset, 84 int size, 85 int* output) { 86 u_int32_t ptr = 0; 87 CHECK(region->GetMemoryAtAddress(base + 4 * offset, &ptr)); 88 ReadArray(region, ptr, size, output); 89} 90 91 92double toM(int size) { 93 return size / (1024. * 1024.); 94} 95 96 97class IndirectSorter { 98 public: 99 explicit IndirectSorter(int* a) : a_(a) { } 100 101 bool operator() (int i0, int i1) { 102 return a_[i0] > a_[i1]; 103 } 104 105 private: 106 int* a_; 107}; 108 109 110void DumpHeapStats(const char *minidump_file) { 111 Minidump minidump(minidump_file); 112 CHECK(minidump.Read()); 113 114 MinidumpException *exception = minidump.GetException(); 115 CHECK(exception); 116 117 MinidumpContext* crash_context = exception->GetContext(); 118 CHECK(crash_context); 119 120 u_int32_t exception_thread_id = 0; 121 CHECK(exception->GetThreadID(&exception_thread_id)); 122 123 MinidumpThreadList* thread_list = minidump.GetThreadList(); 124 CHECK(thread_list); 125 126 MinidumpThread* exception_thread = 127 thread_list->GetThreadByID(exception_thread_id); 128 CHECK(exception_thread); 129 130 // Currently only 32-bit Windows minidumps are supported. 131 CHECK_EQ(MD_CONTEXT_X86, crash_context->GetContextCPU()); 132 133 const MDRawContextX86* contextX86 = crash_context->GetContextX86(); 134 CHECK(contextX86); 135 136 const u_int32_t esp = contextX86->esp; 137 138 MinidumpMemoryRegion* memory_region = exception_thread->GetMemory(); 139 CHECK(memory_region); 140 141 const u_int64_t last = memory_region->GetBase() + memory_region->GetSize(); 142 143 u_int64_t heap_stats_addr = 0; 144 for (u_int64_t addr = esp; addr < last; addr += 4) { 145 u_int32_t value = 0; 146 CHECK(memory_region->GetMemoryAtAddress(addr, &value)); 147 if (value >= esp && value < last) { 148 u_int32_t value2 = 0; 149 CHECK(memory_region->GetMemoryAtAddress(value, &value2)); 150 if (value2 == v8::internal::HeapStats::kStartMarker) { 151 heap_stats_addr = addr; 152 break; 153 } 154 } 155 } 156 CHECK(heap_stats_addr); 157 158 // Read heap stats. 159 160#define READ_FIELD(offset) \ 161 ReadPointedValue(memory_region, heap_stats_addr, offset) 162 163 CHECK(READ_FIELD(0) == v8::internal::HeapStats::kStartMarker); 164 CHECK(READ_FIELD(24) == v8::internal::HeapStats::kEndMarker); 165 166 const int new_space_size = READ_FIELD(1); 167 const int new_space_capacity = READ_FIELD(2); 168 const int old_pointer_space_size = READ_FIELD(3); 169 const int old_pointer_space_capacity = READ_FIELD(4); 170 const int old_data_space_size = READ_FIELD(5); 171 const int old_data_space_capacity = READ_FIELD(6); 172 const int code_space_size = READ_FIELD(7); 173 const int code_space_capacity = READ_FIELD(8); 174 const int map_space_size = READ_FIELD(9); 175 const int map_space_capacity = READ_FIELD(10); 176 const int cell_space_size = READ_FIELD(11); 177 const int cell_space_capacity = READ_FIELD(12); 178 const int lo_space_size = READ_FIELD(13); 179 const int global_handle_count = READ_FIELD(14); 180 const int weak_global_handle_count = READ_FIELD(15); 181 const int pending_global_handle_count = READ_FIELD(16); 182 const int near_death_global_handle_count = READ_FIELD(17); 183 const int destroyed_global_handle_count = READ_FIELD(18); 184 const int memory_allocator_size = READ_FIELD(19); 185 const int memory_allocator_capacity = READ_FIELD(20); 186 const int os_error = READ_FIELD(23); 187#undef READ_FIELD 188 189 int objects_per_type[v8::internal::LAST_TYPE + 1] = {0}; 190 ReadArrayFrom(memory_region, heap_stats_addr, 21, 191 v8::internal::LAST_TYPE + 1, objects_per_type); 192 193 int size_per_type[v8::internal::LAST_TYPE + 1] = {0}; 194 ReadArrayFrom(memory_region, heap_stats_addr, 22, v8::internal::LAST_TYPE + 1, 195 size_per_type); 196 197 int js_global_objects = 198 objects_per_type[v8::internal::JS_GLOBAL_OBJECT_TYPE]; 199 int js_builtins_objects = 200 objects_per_type[v8::internal::JS_BUILTINS_OBJECT_TYPE]; 201 int js_global_proxies = 202 objects_per_type[v8::internal::JS_GLOBAL_PROXY_TYPE]; 203 204 int indices[v8::internal::LAST_TYPE + 1]; 205 for (int i = 0; i <= v8::internal::LAST_TYPE; i++) { 206 indices[i] = i; 207 } 208 209 std::stable_sort(indices, indices + sizeof(indices)/sizeof(indices[0]), 210 IndirectSorter(size_per_type)); 211 212 int total_size = 0; 213 for (int i = 0; i <= v8::internal::LAST_TYPE; i++) { 214 total_size += size_per_type[i]; 215 } 216 217 // Print heap stats. 218 219 printf("exception thread ID: %" PRIu32 " (%#" PRIx32 ")\n", 220 exception_thread_id, exception_thread_id); 221 printf("heap stats address: %#" PRIx64 "\n", heap_stats_addr); 222#define PRINT_INT_STAT(stat) \ 223 printf("\t%-25s\t% 10d\n", #stat ":", stat); 224#define PRINT_MB_STAT(stat) \ 225 printf("\t%-25s\t% 10.3f MB\n", #stat ":", toM(stat)); 226 PRINT_MB_STAT(new_space_size); 227 PRINT_MB_STAT(new_space_capacity); 228 PRINT_MB_STAT(old_pointer_space_size); 229 PRINT_MB_STAT(old_pointer_space_capacity); 230 PRINT_MB_STAT(old_data_space_size); 231 PRINT_MB_STAT(old_data_space_capacity); 232 PRINT_MB_STAT(code_space_size); 233 PRINT_MB_STAT(code_space_capacity); 234 PRINT_MB_STAT(map_space_size); 235 PRINT_MB_STAT(map_space_capacity); 236 PRINT_MB_STAT(cell_space_size); 237 PRINT_MB_STAT(cell_space_capacity); 238 PRINT_MB_STAT(lo_space_size); 239 PRINT_INT_STAT(global_handle_count); 240 PRINT_INT_STAT(weak_global_handle_count); 241 PRINT_INT_STAT(pending_global_handle_count); 242 PRINT_INT_STAT(near_death_global_handle_count); 243 PRINT_INT_STAT(destroyed_global_handle_count); 244 PRINT_MB_STAT(memory_allocator_size); 245 PRINT_MB_STAT(memory_allocator_capacity); 246 PRINT_INT_STAT(os_error); 247#undef PRINT_STAT 248 249 printf("\n"); 250 251 printf( 252 "\tJS_GLOBAL_OBJECT_TYPE/JS_BUILTINS_OBJECT_TYPE/JS_GLOBAL_PROXY_TYPE: " 253 "%d/%d/%d\n\n", 254 js_global_objects, js_builtins_objects, js_global_proxies); 255 256 int running_size = 0; 257 for (int i = 0; i <= v8::internal::LAST_TYPE; i++) { 258 int type = indices[i]; 259 const char* name = InstanceTypeToString(type); 260 if (name == NULL) { 261 // Unknown instance type. Check that there is no objects of that type. 262 CHECK_EQ(0, objects_per_type[type]); 263 CHECK_EQ(0, size_per_type[type]); 264 continue; 265 } 266 int size = size_per_type[type]; 267 running_size += size; 268 printf("\t%-37s% 9d% 11.3f MB% 10.3f%%% 10.3f%%\n", 269 name, objects_per_type[type], toM(size), 270 100. * size / total_size, 100. * running_size / total_size); 271 } 272 printf("\t%-37s% 9d% 11.3f MB% 10.3f%%% 10.3f%%\n", 273 "total", 0, toM(total_size), 100., 100.); 274} 275 276} // namespace 277 278int main(int argc, char **argv) { 279 if (argc != 2) { 280 fprintf(stderr, "usage: %s <minidump>\n", argv[0]); 281 return 1; 282 } 283 284 DumpHeapStats(argv[1]); 285 286 return 0; 287} 288