1 // Copyright 2013 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/v8.h" 6 7 #include "src/allocation-tracker.h" 8 9 #include "src/heap-snapshot-generator.h" 10 #include "src/frames-inl.h" 11 12 namespace v8 { 13 namespace internal { 14 15 AllocationTraceNode::AllocationTraceNode( 16 AllocationTraceTree* tree, unsigned function_info_index) 17 : tree_(tree), 18 function_info_index_(function_info_index), 19 total_size_(0), 20 allocation_count_(0), 21 id_(tree->next_node_id()) { 22 } 23 24 25 AllocationTraceNode::~AllocationTraceNode() { 26 for (int i = 0; i < children_.length(); i++) delete children_[i]; 27 } 28 29 30 AllocationTraceNode* AllocationTraceNode::FindChild( 31 unsigned function_info_index) { 32 for (int i = 0; i < children_.length(); i++) { 33 AllocationTraceNode* node = children_[i]; 34 if (node->function_info_index() == function_info_index) return node; 35 } 36 return NULL; 37 } 38 39 40 AllocationTraceNode* AllocationTraceNode::FindOrAddChild( 41 unsigned function_info_index) { 42 AllocationTraceNode* child = FindChild(function_info_index); 43 if (child == NULL) { 44 child = new AllocationTraceNode(tree_, function_info_index); 45 children_.Add(child); 46 } 47 return child; 48 } 49 50 51 void AllocationTraceNode::AddAllocation(unsigned size) { 52 total_size_ += size; 53 ++allocation_count_; 54 } 55 56 57 void AllocationTraceNode::Print(int indent, AllocationTracker* tracker) { 58 OS::Print("%10u %10u %*c", total_size_, allocation_count_, indent, ' '); 59 if (tracker != NULL) { 60 AllocationTracker::FunctionInfo* info = 61 tracker->function_info_list()[function_info_index_]; 62 OS::Print("%s #%u", info->name, id_); 63 } else { 64 OS::Print("%u #%u", function_info_index_, id_); 65 } 66 OS::Print("\n"); 67 indent += 2; 68 for (int i = 0; i < children_.length(); i++) { 69 children_[i]->Print(indent, tracker); 70 } 71 } 72 73 74 AllocationTraceTree::AllocationTraceTree() 75 : next_node_id_(1), 76 root_(this, 0) { 77 } 78 79 80 AllocationTraceTree::~AllocationTraceTree() { 81 } 82 83 84 AllocationTraceNode* AllocationTraceTree::AddPathFromEnd( 85 const Vector<unsigned>& path) { 86 AllocationTraceNode* node = root(); 87 for (unsigned* entry = path.start() + path.length() - 1; 88 entry != path.start() - 1; 89 --entry) { 90 node = node->FindOrAddChild(*entry); 91 } 92 return node; 93 } 94 95 96 void AllocationTraceTree::Print(AllocationTracker* tracker) { 97 OS::Print("[AllocationTraceTree:]\n"); 98 OS::Print("Total size | Allocation count | Function id | id\n"); 99 root()->Print(0, tracker); 100 } 101 102 103 void AllocationTracker::DeleteUnresolvedLocation( 104 UnresolvedLocation** location) { 105 delete *location; 106 } 107 108 109 AllocationTracker::FunctionInfo::FunctionInfo() 110 : name(""), 111 function_id(0), 112 script_name(""), 113 script_id(0), 114 line(-1), 115 column(-1) { 116 } 117 118 119 void AddressToTraceMap::AddRange(Address start, int size, 120 unsigned trace_node_id) { 121 Address end = start + size; 122 RemoveRange(start, end); 123 124 RangeStack new_range(start, trace_node_id); 125 ranges_.insert(RangeMap::value_type(end, new_range)); 126 } 127 128 129 unsigned AddressToTraceMap::GetTraceNodeId(Address addr) { 130 RangeMap::const_iterator it = ranges_.upper_bound(addr); 131 if (it == ranges_.end()) return 0; 132 if (it->second.start <= addr) { 133 return it->second.trace_node_id; 134 } 135 return 0; 136 } 137 138 139 void AddressToTraceMap::MoveObject(Address from, Address to, int size) { 140 unsigned trace_node_id = GetTraceNodeId(from); 141 if (trace_node_id == 0) return; 142 RemoveRange(from, from + size); 143 AddRange(to, size, trace_node_id); 144 } 145 146 147 void AddressToTraceMap::Clear() { 148 ranges_.clear(); 149 } 150 151 152 void AddressToTraceMap::Print() { 153 PrintF("[AddressToTraceMap (%" V8PRIuPTR "): \n", ranges_.size()); 154 for (RangeMap::iterator it = ranges_.begin(); it != ranges_.end(); ++it) { 155 PrintF("[%p - %p] => %u\n", it->second.start, it->first, 156 it->second.trace_node_id); 157 } 158 PrintF("]\n"); 159 } 160 161 162 void AddressToTraceMap::RemoveRange(Address start, Address end) { 163 RangeMap::iterator it = ranges_.upper_bound(start); 164 if (it == ranges_.end()) return; 165 166 RangeStack prev_range(0, 0); 167 168 RangeMap::iterator to_remove_begin = it; 169 if (it->second.start < start) { 170 prev_range = it->second; 171 } 172 do { 173 if (it->first > end) { 174 if (it->second.start < end) { 175 it->second.start = end; 176 } 177 break; 178 } 179 ++it; 180 } 181 while (it != ranges_.end()); 182 183 ranges_.erase(to_remove_begin, it); 184 185 if (prev_range.start != 0) { 186 ranges_.insert(RangeMap::value_type(start, prev_range)); 187 } 188 } 189 190 191 void AllocationTracker::DeleteFunctionInfo(FunctionInfo** info) { 192 delete *info; 193 } 194 195 196 AllocationTracker::AllocationTracker( 197 HeapObjectsMap* ids, StringsStorage* names) 198 : ids_(ids), 199 names_(names), 200 id_to_function_info_index_(HashMap::PointersMatch), 201 info_index_for_other_state_(0) { 202 FunctionInfo* info = new FunctionInfo(); 203 info->name = "(root)"; 204 function_info_list_.Add(info); 205 } 206 207 208 AllocationTracker::~AllocationTracker() { 209 unresolved_locations_.Iterate(DeleteUnresolvedLocation); 210 function_info_list_.Iterate(&DeleteFunctionInfo); 211 } 212 213 214 void AllocationTracker::PrepareForSerialization() { 215 List<UnresolvedLocation*> copy(unresolved_locations_.length()); 216 copy.AddAll(unresolved_locations_); 217 unresolved_locations_.Clear(); 218 for (int i = 0; i < copy.length(); i++) { 219 copy[i]->Resolve(); 220 delete copy[i]; 221 } 222 } 223 224 225 void AllocationTracker::AllocationEvent(Address addr, int size) { 226 DisallowHeapAllocation no_allocation; 227 Heap* heap = ids_->heap(); 228 229 // Mark the new block as FreeSpace to make sure the heap is iterable 230 // while we are capturing stack trace. 231 FreeListNode::FromAddress(addr)->set_size(heap, size); 232 ASSERT_EQ(HeapObject::FromAddress(addr)->Size(), size); 233 ASSERT(FreeListNode::IsFreeListNode(HeapObject::FromAddress(addr))); 234 235 Isolate* isolate = heap->isolate(); 236 int length = 0; 237 StackTraceFrameIterator it(isolate); 238 while (!it.done() && length < kMaxAllocationTraceLength) { 239 JavaScriptFrame* frame = it.frame(); 240 SharedFunctionInfo* shared = frame->function()->shared(); 241 SnapshotObjectId id = ids_->FindOrAddEntry( 242 shared->address(), shared->Size(), false); 243 allocation_trace_buffer_[length++] = AddFunctionInfo(shared, id); 244 it.Advance(); 245 } 246 if (length == 0) { 247 unsigned index = functionInfoIndexForVMState(isolate->current_vm_state()); 248 if (index != 0) { 249 allocation_trace_buffer_[length++] = index; 250 } 251 } 252 AllocationTraceNode* top_node = trace_tree_.AddPathFromEnd( 253 Vector<unsigned>(allocation_trace_buffer_, length)); 254 top_node->AddAllocation(size); 255 256 address_to_trace_.AddRange(addr, size, top_node->id()); 257 } 258 259 260 static uint32_t SnapshotObjectIdHash(SnapshotObjectId id) { 261 return ComputeIntegerHash(static_cast<uint32_t>(id), 262 v8::internal::kZeroHashSeed); 263 } 264 265 266 unsigned AllocationTracker::AddFunctionInfo(SharedFunctionInfo* shared, 267 SnapshotObjectId id) { 268 HashMap::Entry* entry = id_to_function_info_index_.Lookup( 269 reinterpret_cast<void*>(id), SnapshotObjectIdHash(id), true); 270 if (entry->value == NULL) { 271 FunctionInfo* info = new FunctionInfo(); 272 info->name = names_->GetFunctionName(shared->DebugName()); 273 info->function_id = id; 274 if (shared->script()->IsScript()) { 275 Script* script = Script::cast(shared->script()); 276 if (script->name()->IsName()) { 277 Name* name = Name::cast(script->name()); 278 info->script_name = names_->GetName(name); 279 } 280 info->script_id = script->id()->value(); 281 // Converting start offset into line and column may cause heap 282 // allocations so we postpone them until snapshot serialization. 283 unresolved_locations_.Add(new UnresolvedLocation( 284 script, 285 shared->start_position(), 286 info)); 287 } 288 entry->value = reinterpret_cast<void*>(function_info_list_.length()); 289 function_info_list_.Add(info); 290 } 291 return static_cast<unsigned>(reinterpret_cast<intptr_t>((entry->value))); 292 } 293 294 295 unsigned AllocationTracker::functionInfoIndexForVMState(StateTag state) { 296 if (state != OTHER) return 0; 297 if (info_index_for_other_state_ == 0) { 298 FunctionInfo* info = new FunctionInfo(); 299 info->name = "(V8 API)"; 300 info_index_for_other_state_ = function_info_list_.length(); 301 function_info_list_.Add(info); 302 } 303 return info_index_for_other_state_; 304 } 305 306 307 AllocationTracker::UnresolvedLocation::UnresolvedLocation( 308 Script* script, int start, FunctionInfo* info) 309 : start_position_(start), 310 info_(info) { 311 script_ = Handle<Script>::cast( 312 script->GetIsolate()->global_handles()->Create(script)); 313 GlobalHandles::MakeWeak(reinterpret_cast<Object**>(script_.location()), 314 this, 315 &HandleWeakScript); 316 } 317 318 319 AllocationTracker::UnresolvedLocation::~UnresolvedLocation() { 320 if (!script_.is_null()) { 321 GlobalHandles::Destroy(reinterpret_cast<Object**>(script_.location())); 322 } 323 } 324 325 326 void AllocationTracker::UnresolvedLocation::Resolve() { 327 if (script_.is_null()) return; 328 HandleScope scope(script_->GetIsolate()); 329 info_->line = Script::GetLineNumber(script_, start_position_); 330 info_->column = Script::GetColumnNumber(script_, start_position_); 331 } 332 333 334 void AllocationTracker::UnresolvedLocation::HandleWeakScript( 335 const v8::WeakCallbackData<v8::Value, void>& data) { 336 UnresolvedLocation* loc = 337 reinterpret_cast<UnresolvedLocation*>(data.GetParameter()); 338 GlobalHandles::Destroy(reinterpret_cast<Object**>(loc->script_.location())); 339 loc->script_ = Handle<Script>::null(); 340 } 341 342 343 } } // namespace v8::internal 344