311a5929361635f5ded042005be6021662a919aa
[platform/framework/web/crosswalk.git] / src / v8 / src / xdk-allocation.cc
1 // Copyright 2014 the V8 project authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4 \r
5 #include <sstream>\r
6 #include <string>\r
7 \r
8 #include "src/v8.h"\r
9 \r
10 #include "src/xdk-allocation.h"\r
11 \r
12 #include "frames-inl.h"\r
13 #include "src/xdk-utils.h"\r
14 \r
15 namespace v8 {\r
16 namespace internal {\r
17 \r
18 XDKAllocationTracker::XDKAllocationTracker(HeapProfiler* heap_profiler,\r
19                                            HeapObjectsMap *ids,\r
20                                            StringsStorage *names,\r
21                                            int stackDepth,\r
22                                            bool collectRetention,\r
23                                            bool strict_collection)\r
24     : heap_profiler_(heap_profiler),\r
25     ids_(ids),\r
26     names_(names),\r
27     stackDepth_(stackDepth),\r
28     collectRetention_(collectRetention),\r
29     strict_collection_(strict_collection) {\r
30   references_ = new References();\r
31   aggregated_chunks_ = new AggregatedChunks();\r
32   runtime_info_ = new RuntimeInfo(aggregated_chunks_);\r
33   symbols_ = new SymbolsStorage(ids_->heap(), names_);\r
34   collectedStacks_ = new ShadowStack();\r
35   classNames_ = new ClassNames(names_);\r
36 \r
37   List<unsigned> stack_ooc;\r
38   stack_ooc.Add(symbols_->registerSymInfo(1, "OutOfContext", "NoSource",\r
39                                           0, 0));\r
40   outOfContextFrame_ = collectedStacks_->registerStack(stack_ooc);\r
41 \r
42   List<unsigned> stack_abc;\r
43   stack_abc.Add(symbols_->registerSymInfo(2, "AllocatedBeforeCollection",\r
44                                           "NoSource", 0, 0));\r
45   AllocatedBeforeCollectionFrame_ = collectedStacks_->registerStack(stack_abc);\r
46 \r
47   runtime_info_->InitABCFrame(AllocatedBeforeCollectionFrame_);\r
48 \r
49   baseTime_ = v8::base::Time::Now();\r
50   latest_delta_ = 0;\r
51 }\r
52 \r
53 \r
54 XDKAllocationTracker::~XDKAllocationTracker() {\r
55   delete collectedStacks_;\r
56   delete classNames_;\r
57   delete aggregated_chunks_;\r
58   delete runtime_info_;\r
59   delete symbols_;\r
60   delete references_;\r
61 }\r
62 \r
63 \r
64 // Heap profiler regularly takes time for storing when object was allocated,\r
65 // deallocated, when object's retention snapshot was taken, etc\r
66 unsigned int XDKAllocationTracker::GetTimeDelta() {\r
67   v8::base::TimeDelta td = v8::base::Time::Now() - baseTime_;\r
68   return static_cast<unsigned int>(td.InMilliseconds());\r
69 }\r
70 \r
71 \r
72 void XDKAllocationTracker::OnAlloc(Address addr, int size) {\r
73   DisallowHeapAllocation no_alloc;\r
74   Heap *heap = ids_->heap();\r
75 \r
76   // below call saves from the crash during StackTraceFrameIterator creation\r
77   // Mark the new block as FreeSpace to make sure the heap is iterable\r
78   // while we are capturing stack trace.\r
79   FreeListNode::FromAddress(addr)->set_size(heap, size);\r
80 \r
81   Isolate *isolate = heap->isolate();\r
82   StackTraceFrameIterator it(isolate);\r
83   List<unsigned> stack;\r
84 \r
85   // TODO(amalyshe): checking of isolate->handle_scope_data()->level is quite\r
86   // artificial. need to understand when we can have such behaviour\r
87   // if level == 0 we will crash in getting of source info\r
88   while (isolate->handle_scope_data()->level && !it.done() &&\r
89       stack.length() < stackDepth_) {\r
90     JavaScriptFrame *frame = it.frame();\r
91     if (!frame->function())\r
92       break;\r
93     SharedFunctionInfo *shared = frame->function()->shared();\r
94     if (!shared)\r
95       break;\r
96 \r
97     stack.Add(symbols_->FindOrRegisterFrame(frame));\r
98     it.Advance();\r
99   }\r
100 \r
101   unsigned sid;\r
102   if (!stack.is_empty()) {\r
103     sid = collectedStacks_->registerStack(stack);\r
104   } else {\r
105     sid = outOfContextFrame_;\r
106   }\r
107 \r
108   latest_delta_ = GetTimeDelta();\r
109 \r
110   PostCollectedInfo* info = runtime_info_->AddPostCollectedInfo(addr,\r
111                                                                 latest_delta_);\r
112   info->size_ = size;\r
113   info->timeStamp_ = latest_delta_;\r
114   info->stackId_ = sid;\r
115   info->className_ = (unsigned int)-1;\r
116   info->dirty_ = false;\r
117 }\r
118 \r
119 \r
120 void XDKAllocationTracker::OnMove(Address from, Address to, int size) {\r
121   DisallowHeapAllocation no_alloc;\r
122   // look for the prev address\r
123   PostCollectedInfo* info_from = runtime_info_->FindPostCollectedInfo(from);\r
124   if (info_from == NULL) {\r
125     return;\r
126   }\r
127 \r
128   runtime_info_->AddPostCollectedInfo(to, latest_delta_, info_from);\r
129   runtime_info_->RemoveInfo(from);\r
130 }\r
131 \r
132 \r
133 HeapEventXDK* XDKAllocationTracker::stopTracking() {\r
134   std::string symbols, types, frames, chunks, retentions;\r
135   SerializeChunk(&symbols, &types, &frames, &chunks, &retentions);\r
136   CollectFreedObjects(true);\r
137   std::string symbolsA, typesA, framesA, chunksA, retentionsA;\r
138   SerializeChunk(&symbolsA, &typesA, &framesA, &chunksA, &retentionsA, true);\r
139 \r
140   // TODO(amalyshe): check who releases this object - new HeapEventXDK\r
141   return (new HeapEventXDK(GetTimeDelta(), symbols+symbolsA, types+typesA,\r
142       frames+framesA, chunks+chunksA, ""));\r
143 }\r
144 \r
145 \r
146 void XDKAllocationTracker::CollectFreedObjects(bool bAll, bool initPreCollect) {\r
147   clearIndividualReteiners();\r
148   if (collectRetention_) {\r
149     XDKSnapshotFiller filler(ids_, names_, this);\r
150     HeapSnapshotGenerator generator(heap_profiler_, NULL, NULL, NULL,\r
151                                     ids_->heap(), &filler);\r
152     generator.GenerateSnapshot();\r
153   }\r
154 \r
155   Heap *heap = ids_->heap();\r
156   if (!heap) {\r
157     return;\r
158   }\r
159 \r
160   unsigned int ts = GetTimeDelta();\r
161   if (bAll) {\r
162     ts += RETAINED_DELTA;\r
163   }\r
164 \r
165   // CDT heap profiler calls CollectAllGarbage twice because after the first\r
166   // pass there are weak retained object not collected, but due to perf issues\r
167   // and because we do garbage collection regularly, we leave here only one call\r
168   // only for strict collection like in test where we need to make sure that\r
169   // object is definitely collected, we collect twice\r
170   heap->CollectAllGarbage(\r
171       Heap::kMakeHeapIterableMask,\r
172       "XDKAllocationTracker::CollectFreedObjects");\r
173   if (strict_collection_) {\r
174     heap->CollectAllGarbage(\r
175         Heap::kMakeHeapIterableMask,\r
176         "XDKAllocationTracker::CollectFreedObjects");\r
177   }\r
178   std::map<Address, RefSet> individualReteiners;\r
179 \r
180   // TODO(amalyshe): check what DisallowHeapAllocation no_alloc means because in\r
181   // standalone v8 this part is crashed if DisallowHeapAllocation is defined\r
182   // DisallowHeapAllocation no_alloc;\r
183   if (!bAll) {\r
184     HeapIterator iterator(heap);\r
185     HeapObject* obj = iterator.next();\r
186     for (;\r
187          obj != NULL;\r
188          obj = iterator.next()) {\r
189       Address addr = obj->address();\r
190 \r
191       PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(addr);\r
192       if (!info) {\r
193         // if we don't find info, we consider it as pre collection allocated\r
194         // object. need to add to the full picture for retentions\r
195         if (initPreCollect) {\r
196           info = runtime_info_->AddPreCollectionInfo(addr, obj->Size());\r
197         }\r
198       }\r
199 \r
200       if (info) {\r
201         info->dirty_ = true;\r
202       }\r
203       // check of the class name and its initialization\r
204       if ((info && info->className_ == (unsigned)-1) || !info) {\r
205         InitClassName(addr, ts, obj->Size());\r
206       }\r
207     }\r
208   }\r
209 \r
210   if (collectRetention_) {\r
211     std::map<Address, RefSet>::const_iterator citir =\r
212         individualReteiners_.begin();\r
213     while (citir != individualReteiners_.end()) {\r
214       PostCollectedInfo* infoChild =\r
215           runtime_info_->FindPostCollectedInfo(citir->first);\r
216       if (infoChild) {\r
217         RefId rfId;\r
218         rfId.stackId_ = infoChild->stackId_;\r
219         rfId.classId_ = infoChild->className_;\r
220 \r
221         references_->addReference(rfId, citir->second, infoChild->timeStamp_);\r
222       }\r
223       citir++;\r
224     }\r
225   }\r
226 \r
227   runtime_info_->CollectGarbaged(ts);\r
228 }\r
229 \r
230 \r
231 void XDKAllocationTracker::SerializeChunk(std::string* symbols,\r
232                                           std::string* types,\r
233                                           std::string* frames,\r
234                                           std::string* chunks,\r
235                                           std::string* retentions,\r
236                                           bool final) {\r
237   if (final) {\r
238     *symbols = symbols_->SerializeChunk();\r
239     *types = classNames_->SerializeChunk();\r
240   }\r
241   *frames = collectedStacks_->SerializeChunk();\r
242   *chunks = aggregated_chunks_->SerializeChunk();\r
243 \r
244   *retentions = references_->serialize();\r
245   std::stringstream retentionsT;\r
246   retentionsT << GetTimeDelta() << std::endl << retentions->c_str();\r
247   *retentions = retentionsT.str();\r
248   references_->clear();\r
249 }\r
250 \r
251 \r
252 OutputStream::WriteResult XDKAllocationTracker::SendChunk(\r
253     OutputStream* stream) {\r
254   // go over all aggregated_ and send data to the stream\r
255   std::string symbols, types, frames, chunks, retentions;\r
256   SerializeChunk(&symbols, &types, &frames, &chunks, &retentions);\r
257 \r
258   OutputStream::WriteResult ret = stream->WriteHeapXDKChunk(\r
259       symbols.c_str(), symbols.length(),\r
260       frames.c_str(), frames.length(),\r
261       types.c_str(), types.length(),\r
262       chunks.c_str(), chunks.length(),\r
263       retentions.c_str(), retentions.length());\r
264   return ret;\r
265 }\r
266 \r
267 \r
268 unsigned XDKAllocationTracker::GetTraceNodeId(Address address) {\r
269     PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);\r
270     if (info) {\r
271       return info->stackId_;\r
272     } else {\r
273       return AllocatedBeforeCollectionFrame_;\r
274     }\r
275 }\r
276 \r
277 \r
278 void XDKAllocationTracker::clearIndividualReteiners() {\r
279   individualReteiners_.clear();\r
280 }\r
281 \r
282 \r
283 std::map<Address, RefSet>* XDKAllocationTracker::GetIndividualReteiners() {\r
284   return &individualReteiners_;\r
285 }\r
286 \r
287 \r
288 unsigned XDKAllocationTracker::FindClassName(Address address) {\r
289   PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);\r
290   if (info) {\r
291     return info->className_;\r
292   } else {\r
293     return (unsigned)-1;\r
294   }\r
295 }\r
296 \r
297 \r
298 unsigned XDKAllocationTracker::InitClassName(Address address, unsigned ts,\r
299                                              unsigned size) {\r
300   unsigned id = -2;\r
301   PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);\r
302   if (!info) {\r
303     info = runtime_info_->AddPostCollectedInfo(address, ts);\r
304     info->className_ = -1;\r
305     info->stackId_ = outOfContextFrame_;\r
306     info->timeStamp_ = ts;\r
307     info->size_ = size;\r
308   }\r
309   if (info->className_ == (unsigned)-1) {\r
310     String* str = classNames_->GetConstructorName(address);\r
311     if (str) {\r
312       // get const char*, it's safe because pointer will be retained in the\r
313       // name_ until it is destroyed\r
314       id = classNames_->registerName(names_->GetName(str));\r
315     }\r
316   }\r
317   info->className_ = id;\r
318   return id;\r
319 }\r
320 \r
321 \r
322 unsigned XDKAllocationTracker::FindOrInitClassName(Address address,\r
323                                                    unsigned ts) {\r
324   unsigned id = FindClassName(address);\r
325   if (id == (unsigned)-1) {\r
326     // TODO(amalyshe) check if 0 size here is appropriate\r
327     id = InitClassName(address, ts, 0);\r
328   }\r
329   return id;\r
330 }\r
331 \r
332 \r
333 // -----------------------------------------------------------------------------\r
334 // this is almost a copy and duplication of\r
335 // V8HeapExplorer::AddEntry. refactoring is impossible because\r
336 // heap-snapshot-generator rely on it's structure which is not fully suitable\r
337 // for us.\r
338 HeapEntry* XDKSnapshotFiller::AddEntry(HeapThing ptr,\r
339                                        HeapEntriesAllocator* allocator) {\r
340   HeapObject* object = reinterpret_cast<HeapObject*>(ptr);\r
341   if (object->IsJSFunction()) {\r
342     JSFunction* func = JSFunction::cast(object);\r
343     SharedFunctionInfo* shared = func->shared();\r
344     const char* name = shared->bound() ? "native_bind" :\r
345         names_->GetName(String::cast(shared->name()));\r
346     return AddEntry(ptr, object, HeapEntry::kClosure, name);\r
347   } else if (object->IsJSRegExp()) {\r
348     JSRegExp* re = JSRegExp::cast(object);\r
349     return AddEntry(ptr, object,\r
350                     HeapEntry::kRegExp,\r
351                     names_->GetName(re->Pattern()));\r
352   } else if (object->IsJSObject()) {\r
353     return AddEntry(ptr, object, HeapEntry::kObject, "");\r
354   } else if (object->IsString()) {\r
355     String* string = String::cast(object);\r
356     if (string->IsConsString())\r
357       return AddEntry(ptr, object,\r
358                       HeapEntry::kConsString,\r
359                       "(concatenated string)");\r
360     if (string->IsSlicedString())\r
361       return AddEntry(ptr, object,\r
362                       HeapEntry::kSlicedString,\r
363                       "(sliced string)");\r
364     return AddEntry(ptr, object,\r
365                     HeapEntry::kString,\r
366                     names_->GetName(String::cast(object)));\r
367   } else if (object->IsSymbol()) {\r
368     return AddEntry(ptr, object, HeapEntry::kSymbol, "symbol");\r
369   } else if (object->IsCode()) {\r
370     return AddEntry(ptr, object, HeapEntry::kCode, "");\r
371   } else if (object->IsSharedFunctionInfo()) {\r
372     String* name = String::cast(SharedFunctionInfo::cast(object)->name());\r
373     return AddEntry(ptr, object,\r
374                     HeapEntry::kCode,\r
375                     names_->GetName(name));\r
376   } else if (object->IsScript()) {\r
377     Object* name = Script::cast(object)->name();\r
378     return AddEntry(ptr, object,\r
379                     HeapEntry::kCode,\r
380                     name->IsString()\r
381                         ? names_->GetName(String::cast(name))\r
382                         : "");\r
383   } else if (object->IsNativeContext()) {\r
384     return AddEntry(ptr, object, HeapEntry::kHidden, "system / NativeContext");\r
385   } else if (object->IsContext()) {\r
386     return AddEntry(ptr, object, HeapEntry::kObject, "system / Context");\r
387   } else if (object->IsFixedArray() ||\r
388              object->IsFixedDoubleArray() ||\r
389              object->IsByteArray() ||\r
390              object->IsExternalArray()) {\r
391     return AddEntry(ptr, object, HeapEntry::kArray, "");\r
392   } else if (object->IsHeapNumber()) {\r
393     return AddEntry(ptr, object, HeapEntry::kHeapNumber, "number");\r
394   }\r
395 \r
396   return AddEntry(ptr, object, HeapEntry::kHidden, "system / NOT SUPORTED YET");\r
397 }\r
398 \r
399 \r
400 HeapEntry* XDKSnapshotFiller::AddEntry(HeapThing thing,\r
401                     HeapObject* object,\r
402                     HeapEntry::Type type,\r
403                     const char* name) {\r
404   Address address = object->address();\r
405   unsigned trace_node_id = 0;\r
406   trace_node_id = allocation_tracker_->GetTraceNodeId(address);\r
407 \r
408   // cannot store pointer in the map because List reallcoates content regularly\r
409   // and the only  one way to find the entry - by index. so, index is cached in\r
410   // the map\r
411   // TODO(amalyshe): need to reuse type. it seems it is important\r
412   HeapEntry entry(NULL, &heap_entries_list_, type, name, 0, 0,\r
413                   trace_node_id);\r
414   heap_entries_list_.Add(entry);\r
415   HeapEntry* pEntry = &heap_entries_list_.last();\r
416 \r
417   HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing), true);\r
418   DCHECK(cache_entry->value == NULL);\r
419   int index = pEntry->index();\r
420   cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(index));\r
421 \r
422   // TODO(amalyshe): it seems this storage might be optimized\r
423   HashMap::Entry* address_entry = index_to_address_.Lookup(\r
424       reinterpret_cast<void*>(index+1), HashInt(index+1), true);\r
425   address_entry->value = reinterpret_cast<void*>(address);\r
426 \r
427   return pEntry;\r
428 }\r
429 \r
430 \r
431 HeapEntry* XDKSnapshotFiller::FindEntry(HeapThing thing) {\r
432   HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing), false);\r
433   if (cache_entry == NULL) return NULL;\r
434   return &heap_entries_list_[static_cast<int>(\r
435       reinterpret_cast<intptr_t>(cache_entry->value))];\r
436 }\r
437 \r
438 \r
439 HeapEntry* XDKSnapshotFiller::FindOrAddEntry(HeapThing ptr,\r
440                                              HeapEntriesAllocator* allocator) {\r
441   HeapEntry* entry = FindEntry(ptr);\r
442   return entry != NULL ? entry : AddEntry(ptr, allocator);\r
443 }\r
444 \r
445 \r
446 void XDKSnapshotFiller::SetIndexedReference(HeapGraphEdge::Type type,\r
447     int parent,\r
448     int index,\r
449     HeapEntry* child_entry) {\r
450   if (child_entry->trace_node_id() < 3) {\r
451     return;\r
452   }\r
453   HashMap::Entry* address_entry_child = index_to_address_.Lookup(\r
454       reinterpret_cast<void*>(child_entry->index()+1),\r
455       HashInt(child_entry->index()+1), false);\r
456   DCHECK(address_entry_child != NULL);\r
457   if (!address_entry_child) {\r
458     return;\r
459   }\r
460 \r
461   Address child_addr = reinterpret_cast<Address>(address_entry_child->value);\r
462 \r
463   std::map<Address, RefSet>* individualReteiners =\r
464       allocation_tracker_->GetIndividualReteiners();\r
465   // get the parent's address, constructor name and form the RefId\r
466   HashMap::Entry* address_entry = index_to_address_.Lookup(\r
467       reinterpret_cast<void*>(parent+1), HashInt(parent+1), false);\r
468   DCHECK(address_entry != NULL);\r
469   if (!address_entry) {\r
470     return;\r
471   }\r
472   HeapEntry* parent_entry = &(heap_entries_list_[parent]);\r
473   Address parent_addr = reinterpret_cast<Address>(address_entry->value);\r
474   RefId parent_ref_id;\r
475   parent_ref_id.stackId_ = parent_entry->trace_node_id();\r
476   parent_ref_id.classId_ =\r
477       allocation_tracker_->FindOrInitClassName(parent_addr, 0);\r
478 \r
479   std::stringstream str;\r
480   str << index << " element in Array";\r
481   parent_ref_id.field_ = str.str();\r
482 \r
483   (*individualReteiners)[child_addr].references_.insert(parent_ref_id);\r
484 }\r
485 \r
486 \r
487 void XDKSnapshotFiller::SetIndexedAutoIndexReference(HeapGraphEdge::Type type,\r
488     int parent,\r
489     HeapEntry* child_entry) {\r
490 }\r
491 \r
492 \r
493 void XDKSnapshotFiller::SetNamedReference(HeapGraphEdge::Type type,\r
494     int parent,\r
495     const char* reference_name,\r
496     HeapEntry* child_entry) {\r
497   if (child_entry->trace_node_id() < 3) {\r
498     return;\r
499   }\r
500 \r
501   std::map<Address, RefSet>* individualReteiners =\r
502       allocation_tracker_->GetIndividualReteiners();\r
503   // get the parent's address, constructor name and form the RefId\r
504   HashMap::Entry* address_entry = index_to_address_.Lookup(\r
505       reinterpret_cast<void*>(parent+1), HashInt(parent+1), false);\r
506   DCHECK(address_entry != NULL);\r
507   if (!address_entry) {\r
508     return;\r
509   }\r
510   HeapEntry* parent_entry = &(heap_entries_list_[parent]);\r
511   Address parent_addr = reinterpret_cast<Address>(address_entry->value);\r
512   RefId parent_ref_id;\r
513   parent_ref_id.stackId_ = parent_entry->trace_node_id();\r
514   // TODO(amalyshe): need to get access to classNames_\r
515   parent_ref_id.classId_ =\r
516       allocation_tracker_->FindOrInitClassName(parent_addr, 0);\r
517   parent_ref_id.field_ = reference_name;\r
518 \r
519   HashMap::Entry* address_entry_child = index_to_address_.Lookup(\r
520       reinterpret_cast<void*>(child_entry->index()+1),\r
521       HashInt(child_entry->index()+1), false);\r
522   DCHECK(address_entry_child != NULL);\r
523   if (!address_entry_child) {\r
524     return;\r
525   }\r
526   Address child_addr = reinterpret_cast<Address>(address_entry_child->value);\r
527 \r
528   (*individualReteiners)[child_addr].references_.insert(parent_ref_id);\r
529 }\r
530 \r
531 \r
532 void XDKSnapshotFiller::SetNamedAutoIndexReference(HeapGraphEdge::Type type,\r
533                                 int parent,\r
534                                 HeapEntry* child_entry) {\r
535 }\r
536 \r
537 \r
538 }\r
539 }  // namespace v8::internal\r