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