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
7 #include "src/frames-inl.h"
\r
8 #include "src/xdk-utils.h"
\r
11 namespace internal {
\r
13 static bool AddressesMatch(void* key1, void* key2) {
\r
14 return key1 == key2;
\r
18 static uint32_t CharAddressHash(char* addr) {
\r
19 return ComputeIntegerHash(static_cast<uint32_t>(
\r
20 reinterpret_cast<uintptr_t>(addr)),
\r
21 v8::internal::kZeroHashSeed);
\r
25 static uint32_t AddressHash(Address addr) {
\r
26 return ComputeIntegerHash(static_cast<uint32_t>(
\r
27 reinterpret_cast<uintptr_t>(addr)),
\r
28 v8::internal::kZeroHashSeed);
\r
32 ClassNames::ClassNames(StringsStorage* names)
\r
34 char_to_idx_(AddressesMatch),
\r
39 unsigned ClassNames::registerName(const char* name) {
\r
40 // since const char is retained outside and cannot be moved, we rely on this
\r
41 // and just compare the pointers. It should be enough for the strings from the
\r
42 // only one StringStorage
\r
44 HashMap::Entry* entry = char_to_idx_.Lookup(const_cast<char*>(name),
\r
45 CharAddressHash(const_cast<char*>(name)),
\r
47 if (entry->value == NULL) {
\r
48 counter = ++counter_;
\r
49 entry->value = reinterpret_cast<void*>(counter);
\r
51 counter = static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));
\r
57 std::string ClassNames::SerializeChunk() {
\r
58 std::stringstream serialized;
\r
59 for (HashMap::Entry* p = char_to_idx_.Start(); p != NULL;
\r
60 p = char_to_idx_.Next(p)) {
\r
61 serialized << static_cast<unsigned>(
\r
62 reinterpret_cast<uintptr_t>(p->value)) << "," <<
\r
63 reinterpret_cast<char*>(p->key) << std::endl;
\r
66 return serialized.str();
\r
70 String* ClassNames::GetConstructorName(Address address) {
\r
71 HeapObject *heap_object = HeapObject::FromAddress(address);
\r
72 bool is_js_object = heap_object->IsJSObject();
\r
73 if (!is_js_object) {
\r
74 // TODO(amalyshe): look for another function for taking the class name
\r
75 // String* constructor_name = object2->constructor_name();
\r
78 JSObject* object = JSObject::cast(heap_object);
\r
79 Heap* heap = object->GetHeap();
\r
80 if (object->IsJSFunction()) return heap->closure_string();
\r
81 return object->constructor_name();
\r
85 // -----------------------------------------------------------------------------
\r
86 ShadowStack::ShadowStack() {
\r
88 serializedCounter_ = last_index_;
\r
90 root_.parent_ = NULL;
\r
91 root_.callsite_ = 0;
\r
95 ShadowStack::~ShadowStack() {
\r
96 // erasing all objects from the current container
\r
97 std::map<unsigned, CallTree*>::iterator eit = allNodes_.begin();
\r
98 while (eit != allNodes_.end()) {
\r
105 unsigned ShadowStack::registerStack(const List<unsigned>& shadow_stack_) {
\r
106 // look for the latest node
\r
107 CallTree* pNode = &root_;
\r
108 // go over all entries and add them to the tree if they are not in the map
\r
110 for (i = shadow_stack_.length()-1; i != -1; i--) {
\r
111 std::map<unsigned, CallTree*>::iterator it =
\r
112 pNode->children_.find(shadow_stack_[i]);
\r
113 if (it == pNode->children_.end())
\r
115 pNode = it->second;
\r
117 // verification if we need to add something or not
\r
118 for (j = i; j != -1; j--) {
\r
119 CallTree* pNodeTmp = new CallTree;
\r
120 pNodeTmp->index_ = last_index_++;
\r
121 pNodeTmp->parent_ = pNode;
\r
122 pNodeTmp->callsite_ = shadow_stack_[j];
\r
123 pNode->children_[shadow_stack_[j]] = pNodeTmp;
\r
124 allNodes_[pNodeTmp->index_] = pNodeTmp;
\r
127 return pNode->index_;
\r
131 std::string ShadowStack::SerializeChunk() {
\r
132 std::stringstream str;
\r
133 std::map<unsigned, CallTree*>::iterator it =
\r
134 allNodes_.find(serializedCounter_);
\r
135 while (it!= allNodes_.end()) {
\r
136 str << it->first << "," << it->second->callsite_ << "," <<
\r
137 it->second->parent_->index_ << std::endl;
\r
141 serializedCounter_ = last_index_;
\r
146 // -----------------------------------------------------------------------------
\r
147 static bool SymInfoMatch(void* key1, void* key2) {
\r
148 SymInfoKey* key_c1 = reinterpret_cast<SymInfoKey*>(key1);
\r
149 SymInfoKey* key_c2 = reinterpret_cast<SymInfoKey*>(key2);
\r
150 return *key_c1 == *key_c2;
\r
154 static uint32_t SymInfoHash(const SymInfoKey& key) {
\r
156 // take the low 16 bits of function_id_
\r
157 hash |= (key.function_id_ & 0xffff);
\r
158 // take the low 8 bits of line_ and column_ and init highest bits
\r
159 hash |= ((key.line_ & 0xff) << 16);
\r
160 hash |= ((key.column_ & 0xff) << 14);
\r
166 struct SymbolCached {
\r
167 unsigned int symbol_id_;
\r
168 uintptr_t function_;
\r
172 SymbolsStorage::SymbolsStorage(Heap* heap, StringsStorage* names) :
\r
173 symbols_(SymInfoMatch),
\r
175 sym_info_hash_(AddressesMatch),
\r
178 reserved_key_ = new SymInfoKey();
\r
182 SymbolsStorage::~SymbolsStorage() {
\r
183 // go over map and delete all keys and values
\r
184 for (HashMap::Entry* p = symbols_.Start(); p != NULL; p = symbols_.Next(p)) {
\r
185 delete reinterpret_cast<SymInfoValue*>(p->value);
\r
186 delete reinterpret_cast<SymInfoKey*>(p->key);
\r
188 delete reserved_key_;
\r
192 unsigned SymbolsStorage::registerSymInfo(size_t functionId,
\r
193 std::string functionName,
\r
194 std::string sourceName,
\r
197 if (sourceName.empty()) {
\r
198 sourceName = "unknown";
\r
201 reserved_key_->function_id_ = functionId;
\r
202 reserved_key_->line_ = line;
\r
203 reserved_key_->column_ = column;
\r
205 HashMap::Entry* entry = symbols_.Lookup(reserved_key_,
\r
206 SymInfoHash(*reserved_key_), true);
\r
207 if (entry->value) {
\r
208 return reinterpret_cast<SymInfoValue*>(entry->value)->symId_;
\r
211 // else initialize by new one
\r
212 SymInfoValue* value = new SymInfoValue;
\r
213 value->symId_ = curSym_++;
\r
214 value->funcName_ = functionName;
\r
215 value->sourceFile_ = sourceName;
\r
216 entry->value = value;
\r
218 // compensation for registered one
\r
219 reserved_key_ = new SymInfoKey();
\r
221 return value->symId_;
\r
225 std::string SymbolsStorage::SerializeChunk() {
\r
226 std::stringstream serialized;
\r
227 for (HashMap::Entry* p = symbols_.Start(); p != NULL; p = symbols_.Next(p)) {
\r
228 SymInfoValue* v = reinterpret_cast<SymInfoValue*>(p->value);
\r
229 SymInfoKey* k = reinterpret_cast<SymInfoKey*>(p->key);
\r
230 serialized << v->symId_ << "," << k->function_id_ << "," <<
\r
231 v->funcName_ << "," << v->sourceFile_ << "," <<
\r
232 k->line_ << "," << k->column_ << std::endl;
\r
235 return serialized.str();
\r
239 unsigned SymbolsStorage::FindOrRegisterFrame(JavaScriptFrame* frame) {
\r
240 SharedFunctionInfo *shared = frame->function()->shared();
\r
242 Isolate *isolate = heap_->isolate();
\r
244 Address pc = frame->pc();
\r
245 unsigned int symbolId = 0;
\r
247 // We don't rely on the address only. Since this is JIT based language,
\r
248 // the address might be occupied by other function
\r
249 // thus we are verifying if the same function takes this place
\r
250 // before we take symbol info from the cache
\r
251 HashMap::Entry* sym_entry = sym_info_hash_.Lookup(
\r
252 reinterpret_cast<void*>(pc), AddressHash(pc), true);
\r
253 if (sym_entry->value == NULL ||
\r
254 (reinterpret_cast<SymbolCached*>(sym_entry->value)->function_ !=
\r
255 reinterpret_cast<uintptr_t>(frame->function()))) {
\r
256 if (sym_entry->value) {
\r
257 delete reinterpret_cast<SymbolCached*>(sym_entry->value);
\r
260 const char *s = names_->GetFunctionName(shared->DebugName());
\r
261 // trying to get the source name and line#
\r
262 Code *code = Code::cast(isolate->FindCodeObject(pc));
\r
264 int source_pos = code->SourcePosition(pc);
\r
265 Object *maybe_script = shared->script();
\r
266 if (maybe_script && maybe_script->IsScript()) {
\r
267 Handle<Script> script(Script::cast(maybe_script));
\r
268 if (!script.is_null()) {
\r
269 int line = script->GetLineNumber(source_pos) + 1;
\r
270 // TODO(amalyshe): check if this can be used:
\r
271 // int line = GetScriptLineNumberSafe(script, source_pos) + 1;
\r
272 // TODO(amalyshe): add column number getting
\r
273 int column = 0; // GetScriptColumnNumber(script, source_pos);
\r
274 Object *script_name_raw = script->name();
\r
275 if (script_name_raw->IsString()) {
\r
276 String *script_name = String::cast(script->name());
\r
277 SmartArrayPointer<char> c_script_name =
\r
278 script_name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
\r
279 symbolId = registerSymInfo((size_t)frame->function(), s,
\r
280 c_script_name.get(), line, column);
\r
285 if (symbolId == 0) {
\r
286 symbolId = registerSymInfo((size_t)frame->function(), s, "", 0, 0);
\r
289 SymbolCached* symCached = new SymbolCached;
\r
290 symCached->function_ = reinterpret_cast<uintptr_t>(frame->function());
\r
291 symCached->symbol_id_ = symbolId;
\r
292 sym_entry->value = symCached;
\r
294 symbolId = reinterpret_cast<SymbolCached*>(sym_entry->value)->symbol_id_;
\r
300 // -----------------------------------------------------------------------------
\r
301 RuntimeInfo::RuntimeInfo(AggregatedChunks* aggregated_chunks):
\r
302 working_set_hash_(AddressesMatch),
\r
303 aggregated_chunks_(aggregated_chunks),
\r
304 AllocatedBeforeCollectionFrame_(0) {
\r
308 PostCollectedInfo* RuntimeInfo::FindPostCollectedInfo(Address addr) {
\r
309 HashMap::Entry* entry = working_set_hash_.Lookup(
\r
310 reinterpret_cast<void*>(addr), AddressHash(addr), false);
\r
311 if (entry && entry->value) {
\r
312 PostCollectedInfo* info =
\r
313 reinterpret_cast<PostCollectedInfo*>(entry->value);
\r
320 PostCollectedInfo* RuntimeInfo::AddPostCollectedInfo(Address addr,
\r
321 unsigned time_delta,
\r
322 PostCollectedInfo* info) {
\r
323 PostCollectedInfo* info_new = NULL;
\r
325 info_new = new PostCollectedInfo;
\r
330 HashMap::Entry* entry = working_set_hash_.Lookup(
\r
331 reinterpret_cast<void*>(addr), AddressHash(addr), true);
\r
333 if (entry->value != NULL) {
\r
334 // compensation of the wrong deallocation place
\r
335 // we were not able to work the GC epilogue callback because GC is not
\r
336 // iteratable in the prologue
\r
337 // thus we need to mark the object as freed
\r
338 PostCollectedInfo* info_old =
\r
339 static_cast<PostCollectedInfo*>(entry->value);
\r
340 aggregated_chunks_->addObjectToAggregated(info_old, time_delta);
\r
344 entry->value = info_new;
\r
349 PostCollectedInfo* RuntimeInfo::AddPreCollectionInfo(Address addr,
\r
351 PostCollectedInfo* info = AddPostCollectedInfo(addr);
\r
352 info->size_ = size;
\r
353 info->timeStamp_ = 0;
\r
354 info->stackId_ = AllocatedBeforeCollectionFrame_;
\r
355 info->className_ = (unsigned)-1;
\r
360 void RuntimeInfo::RemoveInfo(Address addr) {
\r
361 working_set_hash_.Remove(reinterpret_cast<void*>(addr), AddressHash(addr));
\r
365 void RuntimeInfo::InitABCFrame(unsigned abc_frame) {
\r
366 AllocatedBeforeCollectionFrame_ = abc_frame;
\r
370 void RuntimeInfo::CollectGarbaged(unsigned ts) {
\r
371 // iteration over the working_set_hash_
\r
372 for (HashMap::Entry* p = working_set_hash_.Start(); p != NULL;
\r
373 p = working_set_hash_.Next(p)) {
\r
375 PostCollectedInfo* info = static_cast<PostCollectedInfo*>(p->value);
\r
376 if (info->dirty_ == false) {
\r
377 // need to care of allocated during collection.
\r
378 // if timeStamp_ == 0 this object was allocated before collection
\r
379 // and we don't care of it
\r
380 aggregated_chunks_->addObjectToAggregated(info, ts);
\r
384 info->dirty_ = false;
\r
391 //------------------------------------------------------------------------------
\r
392 static bool AggregatedMatch(void* key1, void* key2) {
\r
393 // cast to the AggregatedKey
\r
394 AggregatedKey* key_c1 = reinterpret_cast<AggregatedKey*>(key1);
\r
395 AggregatedKey* key_c2 = reinterpret_cast<AggregatedKey*>(key2);
\r
396 return *key_c1 == *key_c2;
\r
400 static uint32_t AggregatedHash(const AggregatedKey& key) {
\r
402 // take the low 8 bits of stackId_
\r
403 hash |= (key.stackId_ & 0xff);
\r
404 // take the low 8 bits of classId_ and init hash from 8th to 15th bits
\r
405 hash |= ((key.classId_ & 0xff) << 8);
\r
406 // since times are well graduated it's no sense take the lowest 8 bit
\r
407 // instead this we will move to 3 bits and only then take 8 bits
\r
408 hash |= (((key.tsBegin_ >> 3) & 0xff) << 16);
\r
409 hash |= (((key.tsBegin_ >> 3) & 0xff) << 24);
\r
414 AggregatedChunks::AggregatedChunks() :
\r
415 aggregated_map_(AggregatedMatch),
\r
417 reserved_key_ = new AggregatedKey();
\r
421 AggregatedChunks::~AggregatedChunks() {
\r
422 delete reserved_key_;
\r
426 void AggregatedChunks::addObjectToAggregated(PostCollectedInfo* info,
\r
428 reserved_key_->stackId_ = info->stackId_;
\r
429 reserved_key_->classId_ = info->className_;
\r
430 // get the bucket for the first time
\r
431 reserved_key_->tsBegin_ = info->timeStamp_ - (info->timeStamp_ % bucketSize_);
\r
432 reserved_key_->tsEnd_ = td - (td % bucketSize_);
\r
434 HashMap::Entry* aggregated_entry = aggregated_map_.Lookup(reserved_key_,
\r
435 AggregatedHash(*reserved_key_),
\r
437 if (aggregated_entry->value) {
\r
438 // no need to store the latest record in the aggregated_keys_list_
\r
439 AggregatedValue* value =
\r
440 reinterpret_cast<AggregatedValue*>(aggregated_entry->value);
\r
442 value->size_ += info->size_;
\r
444 reserved_key_ = new AggregatedKey;
\r
445 AggregatedValue* value = new AggregatedValue;
\r
446 value->objects_ = 1;
\r
447 value->size_ = info->size_;
\r
448 aggregated_entry->value = value;
\r
453 std::string AggregatedChunks::SerializeChunk() {
\r
454 std::stringstream schunks;
\r
455 for (HashMap::Entry* p = aggregated_map_.Start(); p != NULL;
\r
456 p = aggregated_map_.Next(p)) {
\r
457 if (p->key && p->value) {
\r
458 AggregatedKey* key = reinterpret_cast<AggregatedKey*>(p->key);
\r
459 AggregatedValue* value = reinterpret_cast<AggregatedValue*>(p->value);
\r
461 key->tsBegin_ << "," << key->tsEnd_ << "," <<
\r
462 key->stackId_ << "," << key->classId_ << "," <<
\r
463 value->size_ << "," << value->objects_ << std::endl;
\r
469 aggregated_map_.Clear();
\r
471 return schunks.str();
\r
475 // -----------------------------------------------------------------------------
\r
476 void References::addReference(const RefId& parent, const RefSet& refSet,
\r
478 // looking for the parent in the refMap_
\r
479 PARENTREFMAP::iterator cit = refMap_.find(parent);
\r
480 if (cit != refMap_.end()) {
\r
481 REFERENCESETS& sets = cit->second;
\r
482 REFERENCESETS::iterator it = sets.find(refSet);
\r
483 if (it != sets.end()) {
\r
484 // look for the time
\r
485 TIMETOCOUNT::iterator cittc = it->second.find(parentTime);
\r
486 if (cittc != it->second.end()) {
\r
489 it->second[parentTime] = 1;
\r
493 tc[parentTime] = 1;
\r
497 // adding new parent, new sets
\r
498 REFERENCESETS sets;
\r
500 tc[parentTime] = 1;
\r
502 refMap_[parent] = sets;
\r
507 void References::clear() {
\r
512 std::string References::serialize() const {
\r
513 std::stringstream str;
\r
514 PARENTREFMAP::const_iterator citrefs = refMap_.begin();
\r
515 while (citrefs != refMap_.end()) {
\r
516 REFERENCESETS::const_iterator citsets = citrefs->second.begin();
\r
517 while (citsets != citrefs->second.end()) {
\r
518 str << citrefs->first.stackId_ << "," << citrefs->first.classId_;
\r
519 // output of length, and content of TIMETOCOUNT
\r
520 str << "," << citsets->second.size();
\r
521 TIMETOCOUNT::const_iterator cittc = citsets->second.begin();
\r
522 while (cittc != citsets->second.end()) {
\r
523 str << "," << cittc->first << "," << cittc->second;
\r
526 REFERENCESET::const_iterator citset = citsets->first.references_.begin();
\r
527 while (citset != citsets->first.references_.end()) {
\r
528 str << "," << citset->stackId_ << "," << citset->classId_<< "," <<
\r
541 } } // namespace v8::internal
\r