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_space_size = READ_FIELD(3); 169 const int old_space_capacity = READ_FIELD(4); 170 const int code_space_size = READ_FIELD(5); 171 const int code_space_capacity = READ_FIELD(6); 172 const int map_space_size = READ_FIELD(7); 173 const int map_space_capacity = READ_FIELD(8); 174 const int cell_space_size = READ_FIELD(9); 175 const int cell_space_capacity = READ_FIELD(10); 176 const int lo_space_size = READ_FIELD(11); 177 const int global_handle_count = READ_FIELD(12); 178 const int weak_global_handle_count = READ_FIELD(13); 179 const int pending_global_handle_count = READ_FIELD(14); 180 const int near_death_global_handle_count = READ_FIELD(15); 181 const int destroyed_global_handle_count = READ_FIELD(16); 182 const int memory_allocator_size = READ_FIELD(17); 183 const int memory_allocator_capacity = READ_FIELD(18); 184 const int os_error = READ_FIELD(19); 185#undef READ_FIELD 186 187 int objects_per_type[v8::internal::LAST_TYPE + 1] = {0}; 188 ReadArrayFrom(memory_region, heap_stats_addr, 21, 189 v8::internal::LAST_TYPE + 1, objects_per_type); 190 191 int size_per_type[v8::internal::LAST_TYPE + 1] = {0}; 192 ReadArrayFrom(memory_region, heap_stats_addr, 22, v8::internal::LAST_TYPE + 1, 193 size_per_type); 194 195 int js_global_objects = 196 objects_per_type[v8::internal::JS_GLOBAL_OBJECT_TYPE]; 197 int js_builtins_objects = 198 objects_per_type[v8::internal::JS_BUILTINS_OBJECT_TYPE]; 199 int js_global_proxies = 200 objects_per_type[v8::internal::JS_GLOBAL_PROXY_TYPE]; 201 202 int indices[v8::internal::LAST_TYPE + 1]; 203 for (int i = 0; i <= v8::internal::LAST_TYPE; i++) { 204 indices[i] = i; 205 } 206 207 std::stable_sort(indices, indices + sizeof(indices)/sizeof(indices[0]), 208 IndirectSorter(size_per_type)); 209 210 int total_size = 0; 211 for (int i = 0; i <= v8::internal::LAST_TYPE; i++) { 212 total_size += size_per_type[i]; 213 } 214 215 // Print heap stats. 216 217 printf("exception thread ID: %" PRIu32 " (%#" PRIx32 ")\n", 218 exception_thread_id, exception_thread_id); 219 printf("heap stats address: %#" PRIx64 "\n", heap_stats_addr); 220#define PRINT_INT_STAT(stat) \ 221 printf("\t%-25s\t% 10d\n", #stat ":", stat); 222#define PRINT_MB_STAT(stat) \ 223 printf("\t%-25s\t% 10.3f MB\n", #stat ":", toM(stat)); 224 PRINT_MB_STAT(new_space_size); 225 PRINT_MB_STAT(new_space_capacity); 226 PRINT_MB_STAT(old_space_size); 227 PRINT_MB_STAT(old_space_capacity); 228 PRINT_MB_STAT(code_space_size); 229 PRINT_MB_STAT(code_space_capacity); 230 PRINT_MB_STAT(map_space_size); 231 PRINT_MB_STAT(map_space_capacity); 232 PRINT_MB_STAT(cell_space_size); 233 PRINT_MB_STAT(cell_space_capacity); 234 PRINT_MB_STAT(lo_space_size); 235 PRINT_INT_STAT(global_handle_count); 236 PRINT_INT_STAT(weak_global_handle_count); 237 PRINT_INT_STAT(pending_global_handle_count); 238 PRINT_INT_STAT(near_death_global_handle_count); 239 PRINT_INT_STAT(destroyed_global_handle_count); 240 PRINT_MB_STAT(memory_allocator_size); 241 PRINT_MB_STAT(memory_allocator_capacity); 242 PRINT_INT_STAT(os_error); 243#undef PRINT_STAT 244 245 printf("\n"); 246 247 printf( 248 "\tJS_GLOBAL_OBJECT_TYPE/JS_BUILTINS_OBJECT_TYPE/JS_GLOBAL_PROXY_TYPE: " 249 "%d/%d/%d\n\n", 250 js_global_objects, js_builtins_objects, js_global_proxies); 251 252 int running_size = 0; 253 for (int i = 0; i <= v8::internal::LAST_TYPE; i++) { 254 int type = indices[i]; 255 const char* name = InstanceTypeToString(type); 256 if (name == NULL) { 257 // Unknown instance type. Check that there is no objects of that type. 258 CHECK_EQ(0, objects_per_type[type]); 259 CHECK_EQ(0, size_per_type[type]); 260 continue; 261 } 262 int size = size_per_type[type]; 263 running_size += size; 264 printf("\t%-37s% 9d% 11.3f MB% 10.3f%%% 10.3f%%\n", 265 name, objects_per_type[type], toM(size), 266 100. * size / total_size, 100. * running_size / total_size); 267 } 268 printf("\t%-37s% 9d% 11.3f MB% 10.3f%%% 10.3f%%\n", 269 "total", 0, toM(total_size), 100., 100.); 270} 271 272} // namespace 273 274int main(int argc, char **argv) { 275 if (argc != 2) { 276 fprintf(stderr, "usage: %s <minidump>\n", argv[0]); 277 return 1; 278 } 279 280 DumpHeapStats(argv[1]); 281 282 return 0; 283} 284