• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
InstanceTypeToString(int type)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 
ReadPointedValue(MinidumpMemoryRegion * region,u_int64_t base,int offset)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 
ReadArray(MinidumpMemoryRegion * region,u_int64_t array_ptr,int size,int * output)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 
ReadArrayFrom(MinidumpMemoryRegion * region,u_int64_t base,int offset,int size,int * output)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 
toM(int size)94 double toM(int size) {
95   return size / (1024. * 1024.);
96 }
97 
98 
99 class IndirectSorter {
100  public:
IndirectSorter(int * a)101   explicit IndirectSorter(int* a) : a_(a) { }
102 
operator ()(int i0,int i1)103   bool operator() (int i0, int i1) {
104     return a_[i0] > a_[i1];
105   }
106 
107  private:
108   int* a_;
109 };
110 
111 
DumpHeapStats(const char * minidump_file)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 
main(int argc,char ** argv)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