blob: a9103a84a37bd7d025248284f65862ca659a09db [file] [log] [blame]
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +00001// Copyright 2013 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 "v8.h"
29
30#include "allocation-tracker.h"
31
32#include "heap-snapshot-generator.h"
33#include "frames-inl.h"
34
35namespace v8 {
36namespace internal {
37
38AllocationTraceNode::AllocationTraceNode(
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +000039 AllocationTraceTree* tree, unsigned function_info_index)
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000040 : tree_(tree),
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +000041 function_info_index_(function_info_index),
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000042 total_size_(0),
43 allocation_count_(0),
44 id_(tree->next_node_id()) {
45}
46
47
48AllocationTraceNode::~AllocationTraceNode() {
bmeurer@chromium.org25530ce2014-02-07 09:11:16 +000049 for (int i = 0; i < children_.length(); i++) delete children_[i];
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000050}
51
52
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +000053AllocationTraceNode* AllocationTraceNode::FindChild(
54 unsigned function_info_index) {
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000055 for (int i = 0; i < children_.length(); i++) {
56 AllocationTraceNode* node = children_[i];
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +000057 if (node->function_info_index() == function_info_index) return node;
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000058 }
59 return NULL;
60}
61
62
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +000063AllocationTraceNode* AllocationTraceNode::FindOrAddChild(
64 unsigned function_info_index) {
65 AllocationTraceNode* child = FindChild(function_info_index);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000066 if (child == NULL) {
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +000067 child = new AllocationTraceNode(tree_, function_info_index);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000068 children_.Add(child);
69 }
70 return child;
71}
72
73
74void AllocationTraceNode::AddAllocation(unsigned size) {
75 total_size_ += size;
76 ++allocation_count_;
77}
78
79
80void AllocationTraceNode::Print(int indent, AllocationTracker* tracker) {
81 OS::Print("%10u %10u %*c", total_size_, allocation_count_, indent, ' ');
82 if (tracker != NULL) {
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +000083 AllocationTracker::FunctionInfo* info =
84 tracker->function_info_list()[function_info_index_];
85 OS::Print("%s #%u", info->name, id_);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000086 } else {
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +000087 OS::Print("%u #%u", function_info_index_, id_);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +000088 }
89 OS::Print("\n");
90 indent += 2;
91 for (int i = 0; i < children_.length(); i++) {
92 children_[i]->Print(indent, tracker);
93 }
94}
95
96
97AllocationTraceTree::AllocationTraceTree()
98 : next_node_id_(1),
99 root_(this, 0) {
100}
101
102
103AllocationTraceTree::~AllocationTraceTree() {
104}
105
106
107AllocationTraceNode* AllocationTraceTree::AddPathFromEnd(
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000108 const Vector<unsigned>& path) {
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000109 AllocationTraceNode* node = root();
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000110 for (unsigned* entry = path.start() + path.length() - 1;
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000111 entry != path.start() - 1;
112 --entry) {
113 node = node->FindOrAddChild(*entry);
114 }
115 return node;
116}
117
118
119void AllocationTraceTree::Print(AllocationTracker* tracker) {
120 OS::Print("[AllocationTraceTree:]\n");
121 OS::Print("Total size | Allocation count | Function id | id\n");
122 root()->Print(0, tracker);
123}
124
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000125
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000126void AllocationTracker::DeleteUnresolvedLocation(
127 UnresolvedLocation** location) {
128 delete *location;
129}
130
131
132AllocationTracker::FunctionInfo::FunctionInfo()
133 : name(""),
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000134 function_id(0),
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000135 script_name(""),
136 script_id(0),
137 line(-1),
138 column(-1) {
139}
140
141
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000142void AddressToTraceMap::AddRange(Address start, int size,
143 unsigned trace_node_id) {
144 Address end = start + size;
145 RemoveRange(start, end);
146
147 RangeStack new_range(start, trace_node_id);
148 ranges_.insert(RangeMap::value_type(end, new_range));
149}
150
151
152unsigned AddressToTraceMap::GetTraceNodeId(Address addr) {
153 RangeMap::const_iterator it = ranges_.upper_bound(addr);
154 if (it == ranges_.end()) return 0;
155 if (it->second.start <= addr) {
156 return it->second.trace_node_id;
157 }
158 return 0;
159}
160
161
162void AddressToTraceMap::MoveObject(Address from, Address to, int size) {
163 unsigned trace_node_id = GetTraceNodeId(from);
164 if (trace_node_id == 0) return;
165 RemoveRange(from, from + size);
166 AddRange(to, size, trace_node_id);
167}
168
169
170void AddressToTraceMap::Clear() {
171 ranges_.clear();
172}
173
174
175void AddressToTraceMap::Print() {
176 PrintF("[AddressToTraceMap (%" V8PRIuPTR "): \n", ranges_.size());
177 for (RangeMap::iterator it = ranges_.begin(); it != ranges_.end(); ++it) {
178 PrintF("[%p - %p] => %u\n", it->second.start, it->first,
179 it->second.trace_node_id);
180 }
181 PrintF("]\n");
182}
183
184
185void AddressToTraceMap::RemoveRange(Address start, Address end) {
186 RangeMap::iterator it = ranges_.upper_bound(start);
187 if (it == ranges_.end()) return;
188
189 RangeStack prev_range(0, 0);
190
191 RangeMap::iterator to_remove_begin = it;
192 if (it->second.start < start) {
193 prev_range = it->second;
194 }
195 do {
196 if (it->first > end) {
197 if (it->second.start < end) {
198 it->second.start = end;
199 }
200 break;
201 }
202 ++it;
203 }
204 while (it != ranges_.end());
205
206 ranges_.erase(to_remove_begin, it);
207
208 if (prev_range.start != 0) {
209 ranges_.insert(RangeMap::value_type(start, prev_range));
210 }
211}
212
213
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000214static bool AddressesMatch(void* key1, void* key2) {
215 return key1 == key2;
216}
217
218
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000219void AllocationTracker::DeleteFunctionInfo(FunctionInfo** info) {
220 delete *info;
221}
222
223
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000224AllocationTracker::AllocationTracker(
225 HeapObjectsMap* ids, StringsStorage* names)
226 : ids_(ids),
227 names_(names),
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000228 id_to_function_info_index_(AddressesMatch),
229 info_index_for_other_state_(0) {
230 FunctionInfo* info = new FunctionInfo();
231 info->name = "(root)";
232 function_info_list_.Add(info);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000233}
234
235
236AllocationTracker::~AllocationTracker() {
237 unresolved_locations_.Iterate(DeleteUnresolvedLocation);
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000238 function_info_list_.Iterate(&DeleteFunctionInfo);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000239}
240
241
242void AllocationTracker::PrepareForSerialization() {
243 List<UnresolvedLocation*> copy(unresolved_locations_.length());
244 copy.AddAll(unresolved_locations_);
245 unresolved_locations_.Clear();
246 for (int i = 0; i < copy.length(); i++) {
247 copy[i]->Resolve();
248 delete copy[i];
249 }
250}
251
252
yangguo@chromium.orgcc536052013-11-29 11:43:20 +0000253void AllocationTracker::AllocationEvent(Address addr, int size) {
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000254 DisallowHeapAllocation no_allocation;
255 Heap* heap = ids_->heap();
256
257 // Mark the new block as FreeSpace to make sure the heap is iterable
258 // while we are capturing stack trace.
259 FreeListNode::FromAddress(addr)->set_size(heap, size);
260 ASSERT_EQ(HeapObject::FromAddress(addr)->Size(), size);
261 ASSERT(FreeListNode::IsFreeListNode(HeapObject::FromAddress(addr)));
262
263 Isolate* isolate = heap->isolate();
264 int length = 0;
265 StackTraceFrameIterator it(isolate);
266 while (!it.done() && length < kMaxAllocationTraceLength) {
267 JavaScriptFrame* frame = it.frame();
268 SharedFunctionInfo* shared = frame->function()->shared();
yangguo@chromium.orgcc536052013-11-29 11:43:20 +0000269 SnapshotObjectId id = ids_->FindOrAddEntry(
270 shared->address(), shared->Size(), false);
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000271 allocation_trace_buffer_[length++] = AddFunctionInfo(shared, id);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000272 it.Advance();
273 }
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000274 if (length == 0) {
275 unsigned index = functionInfoIndexForVMState(isolate->current_vm_state());
276 if (index != 0) {
277 allocation_trace_buffer_[length++] = index;
278 }
279 }
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000280 AllocationTraceNode* top_node = trace_tree_.AddPathFromEnd(
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000281 Vector<unsigned>(allocation_trace_buffer_, length));
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000282 top_node->AddAllocation(size);
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000283
284 address_to_trace_.AddRange(addr, size, top_node->id());
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000285}
286
287
288static uint32_t SnapshotObjectIdHash(SnapshotObjectId id) {
289 return ComputeIntegerHash(static_cast<uint32_t>(id),
290 v8::internal::kZeroHashSeed);
291}
292
293
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000294unsigned AllocationTracker::AddFunctionInfo(SharedFunctionInfo* shared,
295 SnapshotObjectId id) {
296 HashMap::Entry* entry = id_to_function_info_index_.Lookup(
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000297 reinterpret_cast<void*>(id), SnapshotObjectIdHash(id), true);
298 if (entry->value == NULL) {
299 FunctionInfo* info = new FunctionInfo();
300 info->name = names_->GetFunctionName(shared->DebugName());
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000301 info->function_id = id;
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000302 if (shared->script()->IsScript()) {
303 Script* script = Script::cast(shared->script());
304 if (script->name()->IsName()) {
305 Name* name = Name::cast(script->name());
306 info->script_name = names_->GetName(name);
307 }
308 info->script_id = script->id()->value();
309 // Converting start offset into line and column may cause heap
310 // allocations so we postpone them until snapshot serialization.
311 unresolved_locations_.Add(new UnresolvedLocation(
312 script,
313 shared->start_position(),
314 info));
315 }
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000316 entry->value = reinterpret_cast<void*>(function_info_list_.length());
317 function_info_list_.Add(info);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000318 }
machenbach@chromium.orgca2f2042014-03-10 10:03:12 +0000319 return static_cast<unsigned>(reinterpret_cast<intptr_t>((entry->value)));
320}
321
322
323unsigned AllocationTracker::functionInfoIndexForVMState(StateTag state) {
324 if (state != OTHER) return 0;
325 if (info_index_for_other_state_ == 0) {
326 FunctionInfo* info = new FunctionInfo();
327 info->name = "(V8 API)";
328 info_index_for_other_state_ = function_info_list_.length();
329 function_info_list_.Add(info);
330 }
331 return info_index_for_other_state_;
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000332}
333
334
335AllocationTracker::UnresolvedLocation::UnresolvedLocation(
336 Script* script, int start, FunctionInfo* info)
337 : start_position_(start),
338 info_(info) {
339 script_ = Handle<Script>::cast(
340 script->GetIsolate()->global_handles()->Create(script));
hpayer@chromium.org4f99be92013-12-18 16:23:55 +0000341 GlobalHandles::MakeWeak(reinterpret_cast<Object**>(script_.location()),
342 this,
343 &HandleWeakScript);
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000344}
345
346
347AllocationTracker::UnresolvedLocation::~UnresolvedLocation() {
348 if (!script_.is_null()) {
hpayer@chromium.org4f99be92013-12-18 16:23:55 +0000349 GlobalHandles::Destroy(reinterpret_cast<Object**>(script_.location()));
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000350 }
351}
352
353
354void AllocationTracker::UnresolvedLocation::Resolve() {
355 if (script_.is_null()) return;
titzer@chromium.orgf5a24542014-03-04 09:06:17 +0000356 HandleScope scope(script_->GetIsolate());
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000357 info_->line = GetScriptLineNumber(script_, start_position_);
358 info_->column = GetScriptColumnNumber(script_, start_position_);
359}
360
361
362void AllocationTracker::UnresolvedLocation::HandleWeakScript(
hpayer@chromium.org4f99be92013-12-18 16:23:55 +0000363 const v8::WeakCallbackData<v8::Value, void>& data) {
364 UnresolvedLocation* loc =
365 reinterpret_cast<UnresolvedLocation*>(data.GetParameter());
366 GlobalHandles::Destroy(reinterpret_cast<Object**>(loc->script_.location()));
367 loc->script_ = Handle<Script>::null();
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000368}
369
370
371} } // namespace v8::internal