Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 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 | |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 35 | #include <v8.h> |
| 36 | |
| 37 | namespace { |
| 38 | |
| 39 | using google_breakpad::Minidump; |
| 40 | using google_breakpad::MinidumpContext; |
| 41 | using google_breakpad::MinidumpThread; |
| 42 | using google_breakpad::MinidumpThreadList; |
| 43 | using google_breakpad::MinidumpException; |
| 44 | using google_breakpad::MinidumpMemoryRegion; |
| 45 | |
| 46 | const 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 | |
| 58 | u_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 | |
| 69 | void 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 | |
| 81 | u_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 | |
| 92 | double toM(int size) { |
| 93 | return size / (1024. * 1024.); |
| 94 | } |
| 95 | |
| 96 | |
| 97 | class 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 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 109 | |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 110 | void 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); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 164 | CHECK(READ_FIELD(24) == v8::internal::HeapStats::kEndMarker); |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 165 | |
| 166 | const int new_space_size = READ_FIELD(1); |
| 167 | const int new_space_capacity = READ_FIELD(2); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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); |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 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); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 226 | PRINT_MB_STAT(old_space_size); |
| 227 | PRINT_MB_STAT(old_space_capacity); |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 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); |
Kristian Monsen | 80d68ea | 2010-09-08 11:05:35 +0100 | [diff] [blame] | 242 | PRINT_INT_STAT(os_error); |
Iain Merrick | 7568138 | 2010-08-19 15:07:18 +0100 | [diff] [blame] | 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 | |
| 274 | int 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 | } |