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