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