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