| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/profiler_service.h" | 5 #include "vm/profiler_service.h" |
| 6 | 6 |
| 7 #include "vm/growable_array.h" | 7 #include "vm/growable_array.h" |
| 8 #include "vm/native_symbol.h" | 8 #include "vm/native_symbol.h" |
| 9 #include "vm/object.h" | 9 #include "vm/object.h" |
| 10 #include "vm/os.h" | 10 #include "vm/os.h" |
| 11 #include "vm/profiler.h" | 11 #include "vm/profiler.h" |
| 12 #include "vm/reusable_handles.h" | 12 #include "vm/reusable_handles.h" |
| 13 #include "vm/scope_timer.h" | 13 #include "vm/scope_timer.h" |
| 14 | 14 |
| 15 namespace dart { | 15 namespace dart { |
| 16 | 16 |
| 17 DECLARE_FLAG(int, profile_depth); | 17 DECLARE_FLAG(int, profile_depth); |
| 18 DECLARE_FLAG(bool, trace_profiler); | |
| 19 DECLARE_FLAG(int, profile_period); | 18 DECLARE_FLAG(int, profile_period); |
| 20 | 19 |
| 20 DEFINE_FLAG(bool, trace_profiler, false, "Trace profiler."); |
| 21 |
| 22 // Forward declarations. |
| 23 class CodeRegion; |
| 24 class ProfileFunction; |
| 25 class ProfileFunctionTable; |
| 26 |
| 27 |
| 28 class DeoptimizedCodeSet : public ZoneAllocated { |
| 29 public: |
| 30 explicit DeoptimizedCodeSet(Isolate* isolate) |
| 31 : previous_( |
| 32 GrowableObjectArray::ZoneHandle(isolate->deoptimized_code_array())), |
| 33 current_(GrowableObjectArray::ZoneHandle( |
| 34 previous_.IsNull() ? GrowableObjectArray::null() : |
| 35 GrowableObjectArray::New())) { |
| 36 } |
| 37 |
| 38 void Add(const Code& code) { |
| 39 if (current_.IsNull()) { |
| 40 return; |
| 41 } |
| 42 if (!Contained(code, previous_) || Contained(code, current_)) { |
| 43 return; |
| 44 } |
| 45 current_.Add(code); |
| 46 } |
| 47 |
| 48 void UpdateIsolate(Isolate* isolate) { |
| 49 intptr_t size_before = SizeOf(previous_); |
| 50 intptr_t size_after = SizeOf(current_); |
| 51 if ((size_before > 0) && FLAG_trace_profiler) { |
| 52 intptr_t length_before = previous_.Length(); |
| 53 intptr_t length_after = current_.Length(); |
| 54 OS::Print("Updating isolate deoptimized code array: " |
| 55 "%" Pd " -> %" Pd " [%" Pd " -> %" Pd "]\n", |
| 56 size_before, size_after, length_before, length_after); |
| 57 } |
| 58 isolate->set_deoptimized_code_array(current_); |
| 59 } |
| 60 |
| 61 private: |
| 62 bool Contained(const Code& code, const GrowableObjectArray& array) { |
| 63 if (array.IsNull() || code.IsNull()) { |
| 64 return false; |
| 65 } |
| 66 NoGCScope no_gc_scope; |
| 67 for (intptr_t i = 0; array.Length(); i++) { |
| 68 if (code.raw() == array.At(i)) { |
| 69 return true; |
| 70 } |
| 71 } |
| 72 return false; |
| 73 } |
| 74 |
| 75 intptr_t SizeOf(const GrowableObjectArray& array) { |
| 76 if (array.IsNull()) { |
| 77 return 0; |
| 78 } |
| 79 Code& code = Code::ZoneHandle(); |
| 80 intptr_t size = 0; |
| 81 for (intptr_t i = 0; i < array.Length(); i++) { |
| 82 code ^= array.At(i); |
| 83 ASSERT(!code.IsNull()); |
| 84 size += code.Size(); |
| 85 } |
| 86 return size; |
| 87 } |
| 88 |
| 89 // Array holding code that is being kept around only for the profiler. |
| 90 const GrowableObjectArray& previous_; |
| 91 // Array holding code that should continue to be kept around for the profiler. |
| 92 const GrowableObjectArray& current_; |
| 93 }; |
| 94 |
| 95 class ProfileFunction : public ZoneAllocated { |
| 96 public: |
| 97 enum Kind { |
| 98 kDartFunction, // Dart function. |
| 99 kNativeFunction, // Synthetic function for Native (C/C++). |
| 100 kTagFunction, // Synthetic function for a VM or User tag. |
| 101 kStubFunction, // Synthetic function for stub code. |
| 102 kUnkownFunction, // A singleton function for unknown objects. |
| 103 }; |
| 104 ProfileFunction(Kind kind, |
| 105 const char* name, |
| 106 const Function& function, |
| 107 const intptr_t table_index) |
| 108 : kind_(kind), |
| 109 name_(name), |
| 110 function_(Function::ZoneHandle(function.raw())), |
| 111 table_index_(table_index), |
| 112 code_objects_(new ZoneGrowableArray<intptr_t>()), |
| 113 exclusive_ticks_(0), |
| 114 inclusive_ticks_(0), |
| 115 inclusive_tick_serial_(0) { |
| 116 ASSERT((kind_ != kDartFunction) || !function_.IsNull()); |
| 117 ASSERT((kind_ != kDartFunction) || (table_index_ >= 0)); |
| 118 ASSERT(code_objects_->length() == 0); |
| 119 } |
| 120 |
| 121 const char* name() const { |
| 122 ASSERT(name_ != NULL); |
| 123 return name_; |
| 124 } |
| 125 |
| 126 RawFunction* function() const { |
| 127 return function_.raw(); |
| 128 } |
| 129 |
| 130 intptr_t index() const { |
| 131 return table_index_; |
| 132 } |
| 133 |
| 134 Kind kind() const { |
| 135 return kind_; |
| 136 } |
| 137 |
| 138 const char* KindToCString(Kind kind) { |
| 139 switch (kind) { |
| 140 case kDartFunction: |
| 141 return "Dart"; |
| 142 case kNativeFunction: |
| 143 return "Native"; |
| 144 case kTagFunction: |
| 145 return "Tag"; |
| 146 case kStubFunction: |
| 147 return "Stub"; |
| 148 case kUnkownFunction: |
| 149 return "Collected"; |
| 150 default: |
| 151 UNIMPLEMENTED(); |
| 152 return ""; |
| 153 } |
| 154 } |
| 155 |
| 156 void Dump() { |
| 157 const char* n = (name_ == NULL) ? "<NULL>" : name_; |
| 158 const char* fn = ""; |
| 159 if (!function_.IsNull()) { |
| 160 fn = function_.ToQualifiedCString(); |
| 161 } |
| 162 OS::Print("%s %s [%s]", KindToCString(kind()), n, fn); |
| 163 } |
| 164 |
| 165 void AddCodeObjectIndex(intptr_t index) { |
| 166 for (intptr_t i = 0; i < code_objects_->length(); i++) { |
| 167 if ((*code_objects_)[i] == index) { |
| 168 return; |
| 169 } |
| 170 } |
| 171 code_objects_->Add(index); |
| 172 } |
| 173 |
| 174 intptr_t inclusive_ticks() const { |
| 175 return inclusive_ticks_; |
| 176 } |
| 177 |
| 178 intptr_t exclusive_ticks() const { |
| 179 return exclusive_ticks_; |
| 180 } |
| 181 |
| 182 void Tick(bool exclusive, intptr_t serial) { |
| 183 // Assert that exclusive ticks are never passed a valid serial number. |
| 184 ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1))); |
| 185 if (!exclusive && (inclusive_tick_serial_ == serial)) { |
| 186 // We've already given this object an inclusive tick for this sample. |
| 187 return; |
| 188 } |
| 189 if (exclusive) { |
| 190 exclusive_ticks_++; |
| 191 } else { |
| 192 inclusive_ticks_++; |
| 193 // Mark the last serial we ticked the inclusive count. |
| 194 inclusive_tick_serial_ = serial; |
| 195 } |
| 196 } |
| 197 |
| 198 void PrintToJSONObject(JSONObject* func) { |
| 199 if (kind() == kNativeFunction) { |
| 200 func->AddProperty("type", "@Function"); |
| 201 func->AddProperty("name", name()); |
| 202 func->AddProperty("kind", "Native"); |
| 203 } else if (kind() == kTagFunction) { |
| 204 func->AddProperty("type", "@Function"); |
| 205 func->AddProperty("kind", "Tag"); |
| 206 func->AddProperty("name", name()); |
| 207 } else if (kind() == kUnkownFunction) { |
| 208 func->AddProperty("type", "@Function"); |
| 209 func->AddProperty("name", name()); |
| 210 func->AddProperty("kind", "Collected"); |
| 211 } else if (kind() == kStubFunction) { |
| 212 func->AddProperty("type", "@Function"); |
| 213 func->AddProperty("name", name()); |
| 214 func->AddProperty("kind", "Stub"); |
| 215 } else { |
| 216 UNREACHABLE(); |
| 217 } |
| 218 } |
| 219 |
| 220 void PrintToJSONArray(JSONArray* functions) { |
| 221 JSONObject obj(functions); |
| 222 obj.AddProperty("kind", KindToCString(kind())); |
| 223 obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); |
| 224 obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); |
| 225 if (kind() == kDartFunction) { |
| 226 ASSERT(!function_.IsNull()); |
| 227 obj.AddProperty("function", function_); |
| 228 } else { |
| 229 JSONObject func(&obj, "function"); |
| 230 PrintToJSONObject(&func); |
| 231 } |
| 232 { |
| 233 JSONArray codes(&obj, "codes"); |
| 234 for (intptr_t i = 0; i < code_objects_->length(); i++) { |
| 235 intptr_t code_index = (*code_objects_)[i]; |
| 236 codes.AddValue(code_index); |
| 237 } |
| 238 } |
| 239 } |
| 240 |
| 241 private: |
| 242 const Kind kind_; |
| 243 const char* name_; |
| 244 const Function& function_; |
| 245 const intptr_t table_index_; |
| 246 ZoneGrowableArray<intptr_t>* code_objects_; |
| 247 intptr_t exclusive_ticks_; |
| 248 intptr_t inclusive_ticks_; |
| 249 intptr_t inclusive_tick_serial_; |
| 250 }; |
| 251 |
| 252 |
| 253 class ProfileFunctionTable : public ValueObject { |
| 254 public: |
| 255 ProfileFunctionTable() |
| 256 : null_function_(Function::ZoneHandle()), |
| 257 table_(new ZoneGrowableArray<ProfileFunction*>()), |
| 258 unknown_function_(NULL) { |
| 259 } |
| 260 |
| 261 ProfileFunction* LookupOrAdd(const Function& function) { |
| 262 ASSERT(!function.IsNull()); |
| 263 ProfileFunction* profile_function = Lookup(function); |
| 264 if (profile_function != NULL) { |
| 265 return profile_function; |
| 266 } |
| 267 return Add(function); |
| 268 } |
| 269 |
| 270 intptr_t LookupIndex(const Function& function) { |
| 271 ASSERT(!function.IsNull()); |
| 272 for (intptr_t i = 0; i < table_->length(); i++) { |
| 273 ProfileFunction* profile_function = (*table_)[i]; |
| 274 if (profile_function->function() == function.raw()) { |
| 275 return i; |
| 276 } |
| 277 } |
| 278 return -1; |
| 279 } |
| 280 |
| 281 ProfileFunction* GetUnknown() { |
| 282 if (unknown_function_ == NULL) { |
| 283 // Construct. |
| 284 unknown_function_ = Add(ProfileFunction::kUnkownFunction, |
| 285 "<unknown Dart function>"); |
| 286 } |
| 287 ASSERT(unknown_function_ != NULL); |
| 288 return unknown_function_; |
| 289 } |
| 290 |
| 291 // No protection against being called more than once for the same tag_id. |
| 292 ProfileFunction* AddTag(uword tag_id, const char* name) { |
| 293 // TODO(johnmccutchan): Canonicalize ProfileFunctions for tags. |
| 294 return Add(ProfileFunction::kTagFunction, name); |
| 295 } |
| 296 |
| 297 // No protection against being called more than once for the same native |
| 298 // address. |
| 299 ProfileFunction* AddNative(uword start_address, const char* name) { |
| 300 // TODO(johnmccutchan): Canonicalize ProfileFunctions for natives. |
| 301 return Add(ProfileFunction::kNativeFunction, name); |
| 302 } |
| 303 |
| 304 // No protection against being called more tha once for the same stub. |
| 305 ProfileFunction* AddStub(uword start_address, const char* name) { |
| 306 return Add(ProfileFunction::kStubFunction, name); |
| 307 } |
| 308 |
| 309 intptr_t Length() const { |
| 310 return table_->length(); |
| 311 } |
| 312 |
| 313 ProfileFunction* At(intptr_t i) const { |
| 314 ASSERT(i >= 0); |
| 315 ASSERT(i < Length()); |
| 316 return (*table_)[i]; |
| 317 } |
| 318 |
| 319 private: |
| 320 ProfileFunction* Add(ProfileFunction::Kind kind, const char* name) { |
| 321 ASSERT(kind != ProfileFunction::kDartFunction); |
| 322 ASSERT(name != NULL); |
| 323 ProfileFunction* profile_function = |
| 324 new ProfileFunction(kind, |
| 325 name, |
| 326 null_function_, |
| 327 table_->length()); |
| 328 table_->Add(profile_function); |
| 329 return profile_function; |
| 330 } |
| 331 |
| 332 ProfileFunction* Add(const Function& function) { |
| 333 ASSERT(Lookup(function) == NULL); |
| 334 ProfileFunction* profile_function = |
| 335 new ProfileFunction(ProfileFunction::kDartFunction, |
| 336 NULL, |
| 337 function, |
| 338 table_->length()); |
| 339 table_->Add(profile_function); |
| 340 return profile_function; |
| 341 } |
| 342 |
| 343 ProfileFunction* Lookup(const Function& function) { |
| 344 ASSERT(!function.IsNull()); |
| 345 intptr_t index = LookupIndex(function); |
| 346 if (index == -1) { |
| 347 return NULL; |
| 348 } |
| 349 return (*table_)[index]; |
| 350 } |
| 351 |
| 352 const Function& null_function_; |
| 353 ZoneGrowableArray<ProfileFunction*>* table_; |
| 354 |
| 355 ProfileFunction* unknown_function_; |
| 356 }; |
| 357 |
| 358 |
| 21 struct AddressEntry { | 359 struct AddressEntry { |
| 22 uword pc; | 360 uword pc; |
| 23 intptr_t exclusive_ticks; | 361 intptr_t exclusive_ticks; |
| 24 intptr_t inclusive_ticks; | 362 intptr_t inclusive_ticks; |
| 25 | 363 |
| 26 void tick(bool exclusive) { | 364 void tick(bool exclusive) { |
| 27 if (exclusive) { | 365 if (exclusive) { |
| 28 exclusive_ticks++; | 366 exclusive_ticks++; |
| 29 } else { | 367 } else { |
| 30 inclusive_ticks++; | 368 inclusive_ticks++; |
| 31 } | 369 } |
| 32 } | 370 } |
| 33 }; | 371 }; |
| 34 | 372 |
| 35 | |
| 36 struct CallEntry { | |
| 37 intptr_t code_table_index; | |
| 38 intptr_t count; | |
| 39 }; | |
| 40 | |
| 41 | |
| 42 typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); | 373 typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); |
| 43 | 374 |
| 44 | |
| 45 class CodeRegionTrieNode : public ZoneAllocated { | |
| 46 public: | |
| 47 explicit CodeRegionTrieNode(intptr_t code_region_index) | |
| 48 : code_region_index_(code_region_index), | |
| 49 count_(0), | |
| 50 children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) { | |
| 51 } | |
| 52 | |
| 53 void Tick() { | |
| 54 ASSERT(code_region_index_ >= 0); | |
| 55 count_++; | |
| 56 } | |
| 57 | |
| 58 intptr_t count() const { | |
| 59 ASSERT(code_region_index_ >= 0); | |
| 60 return count_; | |
| 61 } | |
| 62 | |
| 63 intptr_t code_region_index() const { | |
| 64 return code_region_index_; | |
| 65 } | |
| 66 | |
| 67 ZoneGrowableArray<CodeRegionTrieNode*>& children() const { | |
| 68 return *children_; | |
| 69 } | |
| 70 | |
| 71 CodeRegionTrieNode* GetChild(intptr_t child_code_region_index) { | |
| 72 const intptr_t length = children_->length(); | |
| 73 intptr_t i = 0; | |
| 74 while (i < length) { | |
| 75 CodeRegionTrieNode* child = (*children_)[i]; | |
| 76 if (child->code_region_index() == child_code_region_index) { | |
| 77 return child; | |
| 78 } | |
| 79 if (child->code_region_index() > child_code_region_index) { | |
| 80 break; | |
| 81 } | |
| 82 i++; | |
| 83 } | |
| 84 // Add new CodeRegion, sorted by CodeRegionTable index. | |
| 85 CodeRegionTrieNode* child = new CodeRegionTrieNode(child_code_region_index); | |
| 86 if (i < length) { | |
| 87 // Insert at i. | |
| 88 children_->InsertAt(i, child); | |
| 89 } else { | |
| 90 // Add to end. | |
| 91 children_->Add(child); | |
| 92 } | |
| 93 return child; | |
| 94 } | |
| 95 | |
| 96 // Sort this's children and (recursively) all descendants by count. | |
| 97 // This should only be called after the trie is completely built. | |
| 98 void SortByCount() { | |
| 99 children_->Sort(CodeRegionTrieNodeCompare); | |
| 100 ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); | |
| 101 intptr_t child_count = kids.length(); | |
| 102 // Recurse. | |
| 103 for (intptr_t i = 0; i < child_count; i++) { | |
| 104 kids[i]->SortByCount(); | |
| 105 } | |
| 106 } | |
| 107 | |
| 108 void PrintToJSONArray(JSONArray* array) const { | |
| 109 ASSERT(array != NULL); | |
| 110 // Write CodeRegion index. | |
| 111 array->AddValue(code_region_index_); | |
| 112 // Write count. | |
| 113 array->AddValue(count_); | |
| 114 // Write number of children. | |
| 115 ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); | |
| 116 intptr_t child_count = kids.length(); | |
| 117 array->AddValue(child_count); | |
| 118 // Recurse. | |
| 119 for (intptr_t i = 0; i < child_count; i++) { | |
| 120 kids[i]->PrintToJSONArray(array); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 private: | |
| 125 static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a, | |
| 126 CodeRegionTrieNode* const* b) { | |
| 127 ASSERT(a != NULL); | |
| 128 ASSERT(b != NULL); | |
| 129 return (*b)->count() - (*a)->count(); | |
| 130 } | |
| 131 | |
| 132 const intptr_t code_region_index_; | |
| 133 intptr_t count_; | |
| 134 ZoneGrowableArray<CodeRegionTrieNode*>* children_; | |
| 135 }; | |
| 136 | |
| 137 | |
| 138 // A contiguous address region that holds code. Each CodeRegion has a "kind" | 375 // A contiguous address region that holds code. Each CodeRegion has a "kind" |
| 139 // which describes the type of code contained inside the region. Each | 376 // which describes the type of code contained inside the region. Each |
| 140 // region covers the following interval: [start, end). | 377 // region covers the following interval: [start, end). |
| 141 class CodeRegion : public ZoneAllocated { | 378 class CodeRegion : public ZoneAllocated { |
| 142 public: | 379 public: |
| 143 enum Kind { | 380 enum Kind { |
| 144 kDartCode, // Live Dart code. | 381 kDartCode, // Live Dart code. |
| 145 kCollectedCode, // Dead Dart code. | 382 kCollectedCode, // Dead Dart code. |
| 146 kNativeCode, // Native code. | 383 kNativeCode, // Native code. |
| 147 kReusedCode, // Dead Dart code that has been reused by new kDartCode. | 384 kReusedCode, // Dead Dart code that has been reused by new kDartCode. |
| 148 kTagCode, // A special kind of code representing a tag. | 385 kTagCode, // A special kind of code representing a tag. |
| 149 }; | 386 }; |
| 150 | 387 |
| 151 CodeRegion(Kind kind, uword start, uword end, int64_t timestamp) | 388 CodeRegion(Kind kind, |
| 389 uword start, |
| 390 uword end, |
| 391 int64_t timestamp, |
| 392 const Code& code) |
| 152 : kind_(kind), | 393 : kind_(kind), |
| 153 start_(start), | 394 start_(start), |
| 154 end_(end), | 395 end_(end), |
| 155 inclusive_ticks_(0), | 396 inclusive_ticks_(0), |
| 156 exclusive_ticks_(0), | 397 exclusive_ticks_(0), |
| 157 inclusive_tick_serial_(0), | 398 inclusive_tick_serial_(0), |
| 158 name_(NULL), | 399 name_(NULL), |
| 159 compile_timestamp_(timestamp), | 400 compile_timestamp_(timestamp), |
| 160 creation_serial_(0), | 401 creation_serial_(0), |
| 161 address_table_(new ZoneGrowableArray<AddressEntry>()), | 402 code_(Code::ZoneHandle(code.raw())), |
| 162 callers_table_(new ZoneGrowableArray<CallEntry>()), | 403 profile_function_(NULL), |
| 163 callees_table_(new ZoneGrowableArray<CallEntry>()) { | 404 code_table_index_(-1) { |
| 164 ASSERT(start_ < end_); | 405 ASSERT(start_ < end_); |
| 406 // Ensure all kDartCode have a valid code_ object. |
| 407 ASSERT((kind != kDartCode) || (!code_.IsNull())); |
| 165 } | 408 } |
| 166 | 409 |
| 167 | |
| 168 uword start() const { return start_; } | 410 uword start() const { return start_; } |
| 169 void set_start(uword start) { | 411 void set_start(uword start) { |
| 170 start_ = start; | 412 start_ = start; |
| 171 } | 413 } |
| 172 | 414 |
| 173 uword end() const { return end_; } | 415 uword end() const { return end_; } |
| 174 void set_end(uword end) { | 416 void set_end(uword end) { |
| 175 end_ = end; | 417 end_ = end; |
| 176 } | 418 } |
| 177 | 419 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 void SetName(const char* name) { | 462 void SetName(const char* name) { |
| 221 if (name == NULL) { | 463 if (name == NULL) { |
| 222 name_ = NULL; | 464 name_ = NULL; |
| 223 } | 465 } |
| 224 intptr_t len = strlen(name); | 466 intptr_t len = strlen(name); |
| 225 name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1); | 467 name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1); |
| 226 strncpy(const_cast<char*>(name_), name, len); | 468 strncpy(const_cast<char*>(name_), name, len); |
| 227 const_cast<char*>(name_)[len] = '\0'; | 469 const_cast<char*>(name_)[len] = '\0'; |
| 228 } | 470 } |
| 229 | 471 |
| 472 bool IsOptimizedDart() const { |
| 473 return !code_.IsNull() && code_.is_optimized(); |
| 474 } |
| 475 |
| 476 RawCode* code() const { |
| 477 return code_.raw(); |
| 478 } |
| 479 |
| 480 ProfileFunction* SetFunctionAndName(ProfileFunctionTable* table) { |
| 481 ASSERT(profile_function_ == NULL); |
| 482 |
| 483 ProfileFunction* function = NULL; |
| 484 if ((kind() == kReusedCode) || (kind() == kCollectedCode)) { |
| 485 if (name() == NULL) { |
| 486 // Lazily set generated name. |
| 487 GenerateAndSetSymbolName("[Collected]"); |
| 488 } |
| 489 // Map these to a canonical unknown function. |
| 490 function = table->GetUnknown(); |
| 491 } else if (kind() == kDartCode) { |
| 492 ASSERT(!code_.IsNull()); |
| 493 const Object& obj = Object::Handle(code_.owner()); |
| 494 if (obj.IsFunction()) { |
| 495 const String& user_name = String::Handle(code_.PrettyName()); |
| 496 function = table->LookupOrAdd(Function::Cast(obj)); |
| 497 SetName(user_name.ToCString()); |
| 498 } else { |
| 499 // A stub. |
| 500 const String& user_name = String::Handle(code_.PrettyName()); |
| 501 function = table->AddStub(start(), user_name.ToCString()); |
| 502 SetName(user_name.ToCString()); |
| 503 } |
| 504 } else if (kind() == kNativeCode) { |
| 505 if (name() == NULL) { |
| 506 // Lazily set generated name. |
| 507 GenerateAndSetSymbolName("[Native]"); |
| 508 } |
| 509 function = table->AddNative(start(), name()); |
| 510 } else if (kind() == kTagCode) { |
| 511 if (name() == NULL) { |
| 512 if (UserTags::IsUserTag(start())) { |
| 513 const char* tag_name = UserTags::TagName(start()); |
| 514 ASSERT(tag_name != NULL); |
| 515 SetName(tag_name); |
| 516 } else if (VMTag::IsVMTag(start()) || |
| 517 VMTag::IsRuntimeEntryTag(start()) || |
| 518 VMTag::IsNativeEntryTag(start())) { |
| 519 const char* tag_name = VMTag::TagName(start()); |
| 520 ASSERT(tag_name != NULL); |
| 521 SetName(tag_name); |
| 522 } else { |
| 523 ASSERT(start() == 0); |
| 524 SetName("root"); |
| 525 } |
| 526 } |
| 527 function = table->AddTag(start(), name()); |
| 528 } else { |
| 529 UNREACHABLE(); |
| 530 } |
| 531 ASSERT(function != NULL); |
| 532 // Register this CodeRegion with this function. |
| 533 function->AddCodeObjectIndex(code_table_index()); |
| 534 profile_function_ = function; |
| 535 return profile_function_; |
| 536 } |
| 537 |
| 538 ProfileFunction* function() const { |
| 539 ASSERT(profile_function_ != NULL); |
| 540 return profile_function_; |
| 541 } |
| 542 |
| 543 void set_code_table_index(intptr_t code_table_index) { |
| 544 ASSERT(code_table_index_ == -1); |
| 545 ASSERT(code_table_index != -1); |
| 546 code_table_index_ = code_table_index; |
| 547 } |
| 548 intptr_t code_table_index() const { |
| 549 ASSERT(code_table_index_ != -1); |
| 550 return code_table_index_; |
| 551 } |
| 552 |
| 230 Kind kind() const { return kind_; } | 553 Kind kind() const { return kind_; } |
| 231 | 554 |
| 232 static const char* KindToCString(Kind kind) { | 555 static const char* KindToCString(Kind kind) { |
| 233 switch (kind) { | 556 switch (kind) { |
| 234 case kDartCode: | 557 case kDartCode: |
| 235 return "Dart"; | 558 return "Dart"; |
| 236 case kCollectedCode: | 559 case kCollectedCode: |
| 237 return "Collected"; | 560 return "Collected"; |
| 238 case kNativeCode: | 561 case kNativeCode: |
| 239 return "Native"; | 562 return "Native"; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 266 if (exclusive) { | 589 if (exclusive) { |
| 267 exclusive_ticks_++; | 590 exclusive_ticks_++; |
| 268 } else { | 591 } else { |
| 269 inclusive_ticks_++; | 592 inclusive_ticks_++; |
| 270 // Mark the last serial we ticked the inclusive count. | 593 // Mark the last serial we ticked the inclusive count. |
| 271 inclusive_tick_serial_ = serial; | 594 inclusive_tick_serial_ = serial; |
| 272 } | 595 } |
| 273 TickAddress(pc, exclusive); | 596 TickAddress(pc, exclusive); |
| 274 } | 597 } |
| 275 | 598 |
| 276 void AddCaller(intptr_t index, intptr_t count) { | |
| 277 AddCallEntry(callers_table_, index, count); | |
| 278 } | |
| 279 | |
| 280 void AddCallee(intptr_t index, intptr_t count) { | |
| 281 AddCallEntry(callees_table_, index, count); | |
| 282 } | |
| 283 | |
| 284 void PrintNativeCode(JSONObject* profile_code_obj) { | 599 void PrintNativeCode(JSONObject* profile_code_obj) { |
| 285 ASSERT(kind() == kNativeCode); | 600 ASSERT(kind() == kNativeCode); |
| 286 JSONObject obj(profile_code_obj, "code"); | 601 JSONObject obj(profile_code_obj, "code"); |
| 287 obj.AddProperty("type", "@Code"); | 602 obj.AddProperty("type", "@Code"); |
| 288 obj.AddProperty("kind", "Native"); | 603 obj.AddProperty("kind", "Native"); |
| 289 obj.AddProperty("name", name()); | 604 obj.AddProperty("name", name()); |
| 290 obj.AddPropertyF("start", "%" Px "", start()); | 605 obj.AddPropertyF("start", "%" Px "", start()); |
| 291 obj.AddPropertyF("end", "%" Px "", end()); | 606 obj.AddPropertyF("end", "%" Px "", end()); |
| 292 obj.AddPropertyF("id", "code/native-%" Px "", start()); | |
| 293 { | 607 { |
| 294 // Generate a fake function entry. | 608 // Generate a fake function entry. |
| 295 JSONObject func(&obj, "function"); | 609 JSONObject func(&obj, "function"); |
| 296 func.AddProperty("type", "@Function"); | 610 profile_function_->PrintToJSONObject(&func); |
| 297 func.AddPropertyF("id", "functions/native-%" Px "", start()); | |
| 298 func.AddProperty("name", name()); | |
| 299 func.AddProperty("kind", "Native"); | |
| 300 } | 611 } |
| 301 } | 612 } |
| 302 | 613 |
| 303 void PrintCollectedCode(JSONObject* profile_code_obj) { | 614 void PrintCollectedCode(JSONObject* profile_code_obj) { |
| 304 ASSERT(kind() == kCollectedCode); | 615 ASSERT(kind() == kCollectedCode); |
| 305 JSONObject obj(profile_code_obj, "code"); | 616 JSONObject obj(profile_code_obj, "code"); |
| 306 obj.AddProperty("type", "@Code"); | 617 obj.AddProperty("type", "@Code"); |
| 307 obj.AddProperty("kind", "Collected"); | 618 obj.AddProperty("kind", "Collected"); |
| 308 obj.AddProperty("name", name()); | 619 obj.AddProperty("name", name()); |
| 309 obj.AddPropertyF("start", "%" Px "", start()); | 620 obj.AddPropertyF("start", "%" Px "", start()); |
| 310 obj.AddPropertyF("end", "%" Px "", end()); | 621 obj.AddPropertyF("end", "%" Px "", end()); |
| 311 obj.AddPropertyF("id", "code/collected-%" Px "", start()); | |
| 312 { | 622 { |
| 313 // Generate a fake function entry. | 623 // Generate a fake function entry. |
| 314 JSONObject func(&obj, "function"); | 624 JSONObject func(&obj, "function"); |
| 315 func.AddProperty("type", "@Function"); | 625 profile_function_->PrintToJSONObject(&func); |
| 316 obj.AddPropertyF("id", "functions/collected-%" Px "", start()); | |
| 317 func.AddProperty("name", name()); | |
| 318 func.AddProperty("kind", "Collected"); | |
| 319 } | 626 } |
| 320 } | 627 } |
| 321 | 628 |
| 322 void PrintOverwrittenCode(JSONObject* profile_code_obj) { | 629 void PrintOverwrittenCode(JSONObject* profile_code_obj) { |
| 323 ASSERT(kind() == kReusedCode); | 630 ASSERT(kind() == kReusedCode); |
| 324 JSONObject obj(profile_code_obj, "code"); | 631 JSONObject obj(profile_code_obj, "code"); |
| 325 obj.AddProperty("type", "@Code"); | 632 obj.AddProperty("type", "@Code"); |
| 326 obj.AddProperty("kind", "Reused"); | 633 obj.AddProperty("kind", "Reused"); |
| 327 obj.AddProperty("name", name()); | 634 obj.AddProperty("name", name()); |
| 328 obj.AddPropertyF("start", "%" Px "", start()); | 635 obj.AddPropertyF("start", "%" Px "", start()); |
| 329 obj.AddPropertyF("end", "%" Px "", end()); | 636 obj.AddPropertyF("end", "%" Px "", end()); |
| 330 obj.AddPropertyF("id", "code/reused-%" Px "", start()); | |
| 331 { | |
| 332 // Generate a fake function entry. | |
| 333 JSONObject func(&obj, "function"); | |
| 334 func.AddProperty("type", "@Function"); | |
| 335 obj.AddPropertyF("id", "functions/reused-%" Px "", start()); | |
| 336 func.AddProperty("name", name()); | |
| 337 func.AddProperty("kind", "Reused"); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 void PrintTagCode(JSONObject* profile_code_obj) { | |
| 342 ASSERT(kind() == kTagCode); | |
| 343 JSONObject obj(profile_code_obj, "code"); | |
| 344 obj.AddProperty("type", "@Code"); | |
| 345 obj.AddProperty("kind", "Tag"); | |
| 346 obj.AddPropertyF("id", "code/tag-%" Px "", start()); | |
| 347 obj.AddProperty("name", name()); | |
| 348 obj.AddPropertyF("start", "%" Px "", start()); | |
| 349 obj.AddPropertyF("end", "%" Px "", end()); | |
| 350 { | 637 { |
| 351 // Generate a fake function entry. | 638 // Generate a fake function entry. |
| 352 JSONObject func(&obj, "function"); | 639 JSONObject func(&obj, "function"); |
| 353 func.AddProperty("type", "@Function"); | 640 ASSERT(profile_function_ != NULL); |
| 354 func.AddProperty("kind", "Tag"); | 641 profile_function_->PrintToJSONObject(&func); |
| 355 obj.AddPropertyF("id", "functions/tag-%" Px "", start()); | |
| 356 func.AddProperty("name", name()); | |
| 357 } | 642 } |
| 358 } | 643 } |
| 359 | 644 |
| 360 void PrintToJSONArray(Isolate* isolate, JSONArray* events) { | 645 void PrintTagCode(JSONObject* profile_code_obj) { |
| 361 JSONObject obj(events); | 646 ASSERT(kind() == kTagCode); |
| 647 JSONObject obj(profile_code_obj, "code"); |
| 648 obj.AddProperty("type", "@Code"); |
| 649 obj.AddProperty("kind", "Tag"); |
| 650 obj.AddProperty("name", name()); |
| 651 obj.AddPropertyF("start", "%" Px "", start()); |
| 652 obj.AddPropertyF("end", "%" Px "", end()); |
| 653 { |
| 654 // Generate a fake function entry. |
| 655 JSONObject func(&obj, "function"); |
| 656 ASSERT(profile_function_ != NULL); |
| 657 profile_function_->PrintToJSONObject(&func); |
| 658 } |
| 659 } |
| 660 |
| 661 void PrintToJSONArray(JSONArray* codes) { |
| 662 JSONObject obj(codes); |
| 362 obj.AddProperty("kind", KindToCString(kind())); | 663 obj.AddProperty("kind", KindToCString(kind())); |
| 363 obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks()); | 664 obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); |
| 364 obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks()); | 665 obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); |
| 365 if (kind() == kDartCode) { | 666 if (kind() == kDartCode) { |
| 366 // Look up code in Dart heap. | 667 ASSERT(!code_.IsNull()); |
| 367 Code& code = Code::Handle(isolate); | 668 obj.AddProperty("code", code_); |
| 368 code ^= Code::LookupCode(start()); | |
| 369 if (code.IsNull()) { | |
| 370 // Code is a stub in the Vm isolate. | |
| 371 code ^= Code::LookupCodeInVmIsolate(start()); | |
| 372 } | |
| 373 ASSERT(!code.IsNull()); | |
| 374 obj.AddProperty("code", code); | |
| 375 } else if (kind() == kCollectedCode) { | 669 } else if (kind() == kCollectedCode) { |
| 376 if (name() == NULL) { | |
| 377 // Lazily set generated name. | |
| 378 GenerateAndSetSymbolName("[Collected]"); | |
| 379 } | |
| 380 PrintCollectedCode(&obj); | 670 PrintCollectedCode(&obj); |
| 381 } else if (kind() == kReusedCode) { | 671 } else if (kind() == kReusedCode) { |
| 382 if (name() == NULL) { | |
| 383 // Lazily set generated name. | |
| 384 GenerateAndSetSymbolName("[Reused]"); | |
| 385 } | |
| 386 PrintOverwrittenCode(&obj); | 672 PrintOverwrittenCode(&obj); |
| 387 } else if (kind() == kTagCode) { | 673 } else if (kind() == kTagCode) { |
| 388 if (name() == NULL) { | |
| 389 if (UserTags::IsUserTag(start())) { | |
| 390 const char* tag_name = UserTags::TagName(start()); | |
| 391 ASSERT(tag_name != NULL); | |
| 392 SetName(tag_name); | |
| 393 } else if (VMTag::IsVMTag(start()) || | |
| 394 VMTag::IsRuntimeEntryTag(start()) || | |
| 395 VMTag::IsNativeEntryTag(start())) { | |
| 396 const char* tag_name = VMTag::TagName(start()); | |
| 397 ASSERT(tag_name != NULL); | |
| 398 SetName(tag_name); | |
| 399 } else { | |
| 400 ASSERT(start() == 0); | |
| 401 SetName("root"); | |
| 402 } | |
| 403 } | |
| 404 PrintTagCode(&obj); | 674 PrintTagCode(&obj); |
| 405 } else { | 675 } else { |
| 406 ASSERT(kind() == kNativeCode); | 676 ASSERT(kind() == kNativeCode); |
| 407 if (name() == NULL) { | |
| 408 // Lazily set generated name. | |
| 409 GenerateAndSetSymbolName("[Native]"); | |
| 410 } | |
| 411 PrintNativeCode(&obj); | 677 PrintNativeCode(&obj); |
| 412 } | 678 } |
| 413 { | 679 { |
| 414 JSONArray ticks(&obj, "ticks"); | 680 JSONArray ticks(&obj, "ticks"); |
| 415 for (intptr_t i = 0; i < address_table_->length(); i++) { | 681 for (intptr_t i = 0; i < address_table_.length(); i++) { |
| 416 const AddressEntry& entry = (*address_table_)[i]; | 682 const AddressEntry& entry = address_table_[i]; |
| 417 ticks.AddValueF("%" Px "", entry.pc); | 683 ticks.AddValueF("%" Px "", entry.pc); |
| 418 ticks.AddValueF("%" Pd "", entry.exclusive_ticks); | 684 ticks.AddValueF("%" Pd "", entry.exclusive_ticks); |
| 419 ticks.AddValueF("%" Pd "", entry.inclusive_ticks); | 685 ticks.AddValueF("%" Pd "", entry.inclusive_ticks); |
| 420 } | 686 } |
| 421 } | 687 } |
| 422 { | |
| 423 JSONArray callers(&obj, "callers"); | |
| 424 for (intptr_t i = 0; i < callers_table_->length(); i++) { | |
| 425 const CallEntry& entry = (*callers_table_)[i]; | |
| 426 callers.AddValueF("%" Pd "", entry.code_table_index); | |
| 427 callers.AddValueF("%" Pd "", entry.count); | |
| 428 } | |
| 429 } | |
| 430 { | |
| 431 JSONArray callees(&obj, "callees"); | |
| 432 for (intptr_t i = 0; i < callees_table_->length(); i++) { | |
| 433 const CallEntry& entry = (*callees_table_)[i]; | |
| 434 callees.AddValueF("%" Pd "", entry.code_table_index); | |
| 435 callees.AddValueF("%" Pd "", entry.count); | |
| 436 } | |
| 437 } | |
| 438 } | 688 } |
| 439 | 689 |
| 440 private: | 690 private: |
| 441 void TickAddress(uword pc, bool exclusive) { | 691 void TickAddress(uword pc, bool exclusive) { |
| 442 const intptr_t length = address_table_->length(); | 692 const intptr_t length = address_table_.length(); |
| 443 intptr_t i = 0; | 693 intptr_t i = 0; |
| 444 for (; i < length; i++) { | 694 for (; i < length; i++) { |
| 445 AddressEntry& entry = (*address_table_)[i]; | 695 AddressEntry& entry = address_table_[i]; |
| 446 if (entry.pc == pc) { | 696 if (entry.pc == pc) { |
| 447 // Tick the address entry. | 697 // Tick the address entry. |
| 448 entry.tick(exclusive); | 698 entry.tick(exclusive); |
| 449 return; | 699 return; |
| 450 } | 700 } |
| 451 if (entry.pc > pc) { | 701 if (entry.pc > pc) { |
| 452 break; | 702 break; |
| 453 } | 703 } |
| 454 } | 704 } |
| 455 // New address, add entry. | 705 // New address, add entry. |
| 456 AddressEntry entry; | 706 AddressEntry entry; |
| 457 entry.pc = pc; | 707 entry.pc = pc; |
| 458 entry.exclusive_ticks = 0; | 708 entry.exclusive_ticks = 0; |
| 459 entry.inclusive_ticks = 0; | 709 entry.inclusive_ticks = 0; |
| 460 entry.tick(exclusive); | 710 entry.tick(exclusive); |
| 461 if (i < length) { | 711 if (i < length) { |
| 462 // Insert at i. | 712 // Insert at i. |
| 463 address_table_->InsertAt(i, entry); | 713 address_table_.InsertAt(i, entry); |
| 464 } else { | 714 } else { |
| 465 // Add to end. | 715 // Add to end. |
| 466 address_table_->Add(entry); | 716 address_table_.Add(entry); |
| 467 } | 717 } |
| 468 } | 718 } |
| 469 | 719 |
| 470 | |
| 471 void AddCallEntry(ZoneGrowableArray<CallEntry>* table, intptr_t index, | |
| 472 intptr_t count) { | |
| 473 const intptr_t length = table->length(); | |
| 474 intptr_t i = 0; | |
| 475 for (; i < length; i++) { | |
| 476 CallEntry& entry = (*table)[i]; | |
| 477 if (entry.code_table_index == index) { | |
| 478 entry.count += count; | |
| 479 return; | |
| 480 } | |
| 481 if (entry.code_table_index > index) { | |
| 482 break; | |
| 483 } | |
| 484 } | |
| 485 CallEntry entry; | |
| 486 entry.code_table_index = index; | |
| 487 entry.count = count; | |
| 488 if (i < length) { | |
| 489 table->InsertAt(i, entry); | |
| 490 } else { | |
| 491 table->Add(entry); | |
| 492 } | |
| 493 } | |
| 494 | |
| 495 void GenerateAndSetSymbolName(const char* prefix) { | 720 void GenerateAndSetSymbolName(const char* prefix) { |
| 496 const intptr_t kBuffSize = 512; | 721 const intptr_t kBuffSize = 512; |
| 497 char buff[kBuffSize]; | 722 char buff[kBuffSize]; |
| 498 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", | 723 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", |
| 499 prefix, start(), end()); | 724 prefix, start(), end()); |
| 500 SetName(buff); | 725 SetName(buff); |
| 501 } | 726 } |
| 502 | 727 |
| 503 // CodeRegion kind. | 728 // CodeRegion kind. |
| 504 const Kind kind_; | 729 const Kind kind_; |
| 505 // CodeRegion start address. | 730 // CodeRegion start address. |
| 506 uword start_; | 731 uword start_; |
| 507 // CodeRegion end address. | 732 // CodeRegion end address. |
| 508 uword end_; | 733 uword end_; |
| 509 // Inclusive ticks. | 734 // Inclusive ticks. |
| 510 intptr_t inclusive_ticks_; | 735 intptr_t inclusive_ticks_; |
| 511 // Exclusive ticks. | 736 // Exclusive ticks. |
| 512 intptr_t exclusive_ticks_; | 737 intptr_t exclusive_ticks_; |
| 513 // Inclusive tick serial number, ensures that each CodeRegion is only given | 738 // Inclusive tick serial number, ensures that each CodeRegion is only given |
| 514 // a single inclusive tick per sample. | 739 // a single inclusive tick per sample. |
| 515 intptr_t inclusive_tick_serial_; | 740 intptr_t inclusive_tick_serial_; |
| 516 // Name of code region. | 741 // Name of code region. |
| 517 const char* name_; | 742 const char* name_; |
| 518 // The compilation timestamp associated with this code region. | 743 // The compilation timestamp associated with this code region. |
| 519 int64_t compile_timestamp_; | 744 int64_t compile_timestamp_; |
| 520 // Serial number at which this CodeRegion was created. | 745 // Serial number at which this CodeRegion was created. |
| 521 intptr_t creation_serial_; | 746 intptr_t creation_serial_; |
| 522 ZoneGrowableArray<AddressEntry>* address_table_; | 747 // Dart code object (may be null). |
| 523 ZoneGrowableArray<CallEntry>* callers_table_; | 748 const Code& code_; |
| 524 ZoneGrowableArray<CallEntry>* callees_table_; | 749 // Pointer to ProfileFunction. |
| 750 ProfileFunction* profile_function_; |
| 751 // Final code table index. |
| 752 intptr_t code_table_index_; |
| 753 ZoneGrowableArray<AddressEntry> address_table_; |
| 525 DISALLOW_COPY_AND_ASSIGN(CodeRegion); | 754 DISALLOW_COPY_AND_ASSIGN(CodeRegion); |
| 526 }; | 755 }; |
| 527 | 756 |
| 528 | 757 |
| 529 // A sorted table of CodeRegions. Does not allow for overlap. | 758 // A sorted table of CodeRegions. Does not allow for overlap. |
| 530 class CodeRegionTable : public ValueObject { | 759 class CodeRegionTable : public ValueObject { |
| 531 public: | 760 public: |
| 532 enum TickResult { | 761 enum TickResult { |
| 533 kTicked = 0, // CodeRegion found and ticked. | 762 kTicked = 0, // CodeRegion found and ticked. |
| 534 kNotFound = -1, // No CodeRegion found. | 763 kNotFound = -1, // No CodeRegion found. |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 634 if (region->overlaps(code_region)) { | 863 if (region->overlaps(code_region)) { |
| 635 HandleOverlap(region, code_region, start, end); | 864 HandleOverlap(region, code_region, start, end); |
| 636 return hi; | 865 return hi; |
| 637 } | 866 } |
| 638 code_region_table_->InsertAt(hi, code_region); | 867 code_region_table_->InsertAt(hi, code_region); |
| 639 return hi; | 868 return hi; |
| 640 } | 869 } |
| 641 UNREACHABLE(); | 870 UNREACHABLE(); |
| 642 } | 871 } |
| 643 | 872 |
| 644 #if defined(DEBUG) | |
| 645 void Verify() { | 873 void Verify() { |
| 646 VerifyOrder(); | 874 VerifyOrder(); |
| 647 VerifyOverlap(); | 875 VerifyOverlap(); |
| 648 } | 876 } |
| 649 #endif | |
| 650 | 877 |
| 651 void DebugPrint() { | 878 void DebugPrint() { |
| 652 OS::Print("Dumping CodeRegionTable:\n"); | 879 OS::Print("Dumping CodeRegionTable:\n"); |
| 653 for (intptr_t i = 0; i < code_region_table_->length(); i++) { | 880 for (intptr_t i = 0; i < code_region_table_->length(); i++) { |
| 654 CodeRegion* region = At(i); | 881 CodeRegion* region = At(i); |
| 655 region->DebugPrint(); | 882 region->DebugPrint(); |
| 656 } | 883 } |
| 657 } | 884 } |
| 658 | 885 |
| 659 private: | 886 private: |
| (...skipping 28 matching lines...) Expand all Loading... |
| 688 uword start, uword end) { | 915 uword start, uword end) { |
| 689 // We should never see overlapping Dart code regions. | 916 // We should never see overlapping Dart code regions. |
| 690 ASSERT(region->kind() != CodeRegion::kDartCode); | 917 ASSERT(region->kind() != CodeRegion::kDartCode); |
| 691 // We should never see overlapping Tag code regions. | 918 // We should never see overlapping Tag code regions. |
| 692 ASSERT(region->kind() != CodeRegion::kTagCode); | 919 ASSERT(region->kind() != CodeRegion::kTagCode); |
| 693 // When code regions overlap, they should be of the same kind. | 920 // When code regions overlap, they should be of the same kind. |
| 694 ASSERT(region->kind() == code_region->kind()); | 921 ASSERT(region->kind() == code_region->kind()); |
| 695 region->AdjustExtent(start, end); | 922 region->AdjustExtent(start, end); |
| 696 } | 923 } |
| 697 | 924 |
| 698 #if defined(DEBUG) | |
| 699 void VerifyOrder() { | 925 void VerifyOrder() { |
| 700 const intptr_t length = code_region_table_->length(); | 926 const intptr_t length = code_region_table_->length(); |
| 701 if (length == 0) { | 927 if (length == 0) { |
| 702 return; | 928 return; |
| 703 } | 929 } |
| 704 uword last = (*code_region_table_)[0]->end(); | 930 uword last = (*code_region_table_)[0]->end(); |
| 705 for (intptr_t i = 1; i < length; i++) { | 931 for (intptr_t i = 1; i < length; i++) { |
| 706 CodeRegion* a = (*code_region_table_)[i]; | 932 CodeRegion* a = (*code_region_table_)[i]; |
| 707 ASSERT(last <= a->start()); | 933 ASSERT(last <= a->start()); |
| 708 last = a->end(); | 934 last = a->end(); |
| 709 } | 935 } |
| 710 } | 936 } |
| 711 | 937 |
| 712 void VerifyOverlap() { | 938 void VerifyOverlap() { |
| 713 const intptr_t length = code_region_table_->length(); | 939 const intptr_t length = code_region_table_->length(); |
| 714 for (intptr_t i = 0; i < length; i++) { | 940 for (intptr_t i = 0; i < length; i++) { |
| 715 CodeRegion* a = (*code_region_table_)[i]; | 941 CodeRegion* a = (*code_region_table_)[i]; |
| 716 for (intptr_t j = i+1; j < length; j++) { | 942 for (intptr_t j = i+1; j < length; j++) { |
| 717 CodeRegion* b = (*code_region_table_)[j]; | 943 CodeRegion* b = (*code_region_table_)[j]; |
| 718 ASSERT(!a->contains(b->start()) && | 944 ASSERT(!a->contains(b->start()) && |
| 719 !a->contains(b->end() - 1) && | 945 !a->contains(b->end() - 1) && |
| 720 !b->contains(a->start()) && | 946 !b->contains(a->start()) && |
| 721 !b->contains(a->end() - 1)); | 947 !b->contains(a->end() - 1)); |
| 722 } | 948 } |
| 723 } | 949 } |
| 724 } | 950 } |
| 725 #endif | |
| 726 | 951 |
| 727 ZoneGrowableArray<CodeRegion*>* code_region_table_; | 952 ZoneGrowableArray<CodeRegion*>* code_region_table_; |
| 728 }; | 953 }; |
| 729 | 954 |
| 730 | 955 |
| 731 class FixTopFrameVisitor : public SampleVisitor { | |
| 732 public: | |
| 733 explicit FixTopFrameVisitor(Isolate* isolate) | |
| 734 : SampleVisitor(isolate), | |
| 735 vm_isolate_(Dart::vm_isolate()) { | |
| 736 } | |
| 737 | |
| 738 void VisitSample(Sample* sample) { | |
| 739 if (sample->processed()) { | |
| 740 // Already processed. | |
| 741 return; | |
| 742 } | |
| 743 REUSABLE_CODE_HANDLESCOPE(isolate()); | |
| 744 // Mark that we've processed this sample. | |
| 745 sample->set_processed(true); | |
| 746 // Lookup code object for leaf frame. | |
| 747 Code& code = reused_code_handle.Handle(); | |
| 748 code = FindCodeForPC(sample->At(0)); | |
| 749 sample->set_leaf_frame_is_dart(!code.IsNull()); | |
| 750 if (sample->pc_marker() == 0) { | |
| 751 // No pc marker. Nothing to do. | |
| 752 return; | |
| 753 } | |
| 754 if (!code.IsNull() && (code.compile_timestamp() > sample->timestamp())) { | |
| 755 // Code compiled after sample. Ignore. | |
| 756 return; | |
| 757 } | |
| 758 if (sample->leaf_frame_is_dart()) { | |
| 759 CheckForMissingDartFrame(code, sample); | |
| 760 } | |
| 761 } | |
| 762 | |
| 763 private: | |
| 764 void CheckForMissingDartFrame(const Code& code, Sample* sample) const { | |
| 765 // Some stubs (and intrinsics) do not push a frame onto the stack leaving | |
| 766 // the frame pointer in the caller. | |
| 767 // | |
| 768 // PC -> STUB | |
| 769 // FP -> DART3 <-+ | |
| 770 // DART2 <-| <- TOP FRAME RETURN ADDRESS. | |
| 771 // DART1 <-| | |
| 772 // ..... | |
| 773 // | |
| 774 // In this case, traversing the linked stack frames will not collect a PC | |
| 775 // inside DART3. The stack will incorrectly be: STUB, DART2, DART1. | |
| 776 // In Dart code, after pushing the FP onto the stack, an IP in the current | |
| 777 // function is pushed onto the stack as well. This stack slot is called | |
| 778 // the PC marker. We can use the PC marker to insert DART3 into the stack | |
| 779 // so that it will correctly be: STUB, DART3, DART2, DART1. Note the | |
| 780 // inserted PC may not accurately reflect the true return address from STUB. | |
| 781 ASSERT(!code.IsNull()); | |
| 782 if (sample->sp() == sample->fp()) { | |
| 783 // Haven't pushed pc marker yet. | |
| 784 return; | |
| 785 } | |
| 786 uword pc_marker = sample->pc_marker(); | |
| 787 if (code.ContainsInstructionAt(pc_marker)) { | |
| 788 // PC marker is in the same code as pc, no missing frame. | |
| 789 return; | |
| 790 } | |
| 791 if (!ContainedInDartCodeHeaps(pc_marker)) { | |
| 792 // Not a valid PC marker. | |
| 793 return; | |
| 794 } | |
| 795 sample->InsertCallerForTopFrame(pc_marker); | |
| 796 } | |
| 797 | |
| 798 bool ContainedInDartCodeHeaps(uword pc) const { | |
| 799 return isolate()->heap()->CodeContains(pc) || | |
| 800 vm_isolate()->heap()->CodeContains(pc); | |
| 801 } | |
| 802 | |
| 803 Isolate* vm_isolate() const { | |
| 804 return vm_isolate_; | |
| 805 } | |
| 806 | |
| 807 RawCode* FindCodeForPC(uword pc) const { | |
| 808 // Check current isolate for pc. | |
| 809 if (isolate()->heap()->CodeContains(pc)) { | |
| 810 return Code::LookupCode(pc); | |
| 811 } | |
| 812 // Check VM isolate for pc. | |
| 813 if (vm_isolate()->heap()->CodeContains(pc)) { | |
| 814 return Code::LookupCodeInVmIsolate(pc); | |
| 815 } | |
| 816 return Code::null(); | |
| 817 } | |
| 818 | |
| 819 Isolate* vm_isolate_; | |
| 820 }; | |
| 821 | |
| 822 | |
| 823 class CodeRegionTableBuilder : public SampleVisitor { | 956 class CodeRegionTableBuilder : public SampleVisitor { |
| 824 public: | 957 public: |
| 825 CodeRegionTableBuilder(Isolate* isolate, | 958 CodeRegionTableBuilder(Isolate* isolate, |
| 826 CodeRegionTable* live_code_table, | 959 CodeRegionTable* live_code_table, |
| 827 CodeRegionTable* dead_code_table, | 960 CodeRegionTable* dead_code_table, |
| 828 CodeRegionTable* tag_code_table) | 961 CodeRegionTable* tag_code_table, |
| 962 DeoptimizedCodeSet* deoptimized_code) |
| 829 : SampleVisitor(isolate), | 963 : SampleVisitor(isolate), |
| 830 live_code_table_(live_code_table), | 964 live_code_table_(live_code_table), |
| 831 dead_code_table_(dead_code_table), | 965 dead_code_table_(dead_code_table), |
| 832 tag_code_table_(tag_code_table), | 966 tag_code_table_(tag_code_table), |
| 833 isolate_(isolate), | 967 isolate_(isolate), |
| 834 vm_isolate_(Dart::vm_isolate()) { | 968 vm_isolate_(Dart::vm_isolate()), |
| 969 null_code_(Code::ZoneHandle()), |
| 970 deoptimized_code_(deoptimized_code) { |
| 835 ASSERT(live_code_table_ != NULL); | 971 ASSERT(live_code_table_ != NULL); |
| 836 ASSERT(dead_code_table_ != NULL); | 972 ASSERT(dead_code_table_ != NULL); |
| 837 ASSERT(tag_code_table_ != NULL); | 973 ASSERT(tag_code_table_ != NULL); |
| 974 ASSERT(isolate_ != NULL); |
| 975 ASSERT(vm_isolate_ != NULL); |
| 976 ASSERT(null_code_.IsNull()); |
| 838 frames_ = 0; | 977 frames_ = 0; |
| 839 min_time_ = kMaxInt64; | 978 min_time_ = kMaxInt64; |
| 840 max_time_ = 0; | 979 max_time_ = 0; |
| 841 ASSERT(isolate_ != NULL); | |
| 842 ASSERT(vm_isolate_ != NULL); | |
| 843 } | 980 } |
| 844 | 981 |
| 845 void VisitSample(Sample* sample) { | 982 void VisitSample(Sample* sample) { |
| 846 int64_t timestamp = sample->timestamp(); | 983 int64_t timestamp = sample->timestamp(); |
| 847 if (timestamp > max_time_) { | 984 if (timestamp > max_time_) { |
| 848 max_time_ = timestamp; | 985 max_time_ = timestamp; |
| 849 } | 986 } |
| 850 if (timestamp < min_time_) { | 987 if (timestamp < min_time_) { |
| 851 min_time_ = timestamp; | 988 min_time_ = timestamp; |
| 852 } | 989 } |
| 853 // Make sure VM tag is created. | 990 // Make sure VM tag is created. |
| 854 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | 991 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
| 855 CreateTag(VMTag::kNativeTagId); | 992 CreateTag(VMTag::kNativeTagId); |
| 856 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 993 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
| 857 CreateTag(VMTag::kRuntimeTagId); | 994 CreateTag(VMTag::kRuntimeTagId); |
| 858 } | 995 } |
| 859 CreateTag(sample->vm_tag()); | 996 CreateTag(sample->vm_tag()); |
| 860 // Make sure user tag is created. | 997 // Make sure user tag is created. |
| 861 CreateUserTag(sample->user_tag()); | 998 CreateUserTag(sample->user_tag()); |
| 862 // Exclusive tick for bottom frame if we aren't sampled from an exit frame. | 999 // Exclusive tick for top frame if we aren't sampled from an exit frame. |
| 863 if (!sample->exit_frame_sample()) { | 1000 if (!sample->exit_frame_sample()) { |
| 864 Tick(sample->At(0), true, timestamp); | 1001 Tick(sample->At(0), true, timestamp); |
| 865 } | 1002 } |
| 866 // Inclusive tick for all frames. | 1003 // Inclusive tick for all frames. |
| 867 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { | 1004 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { |
| 868 if (sample->At(i) == 0) { | 1005 if (sample->At(i) == 0) { |
| 869 break; | 1006 break; |
| 870 } | 1007 } |
| 871 frames_++; | 1008 frames_++; |
| 872 Tick(sample->At(i), false, timestamp); | 1009 Tick(sample->At(i), false, timestamp); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 883 private: | 1020 private: |
| 884 void CreateTag(uword tag) { | 1021 void CreateTag(uword tag) { |
| 885 intptr_t index = tag_code_table_->FindIndex(tag); | 1022 intptr_t index = tag_code_table_->FindIndex(tag); |
| 886 if (index >= 0) { | 1023 if (index >= 0) { |
| 887 // Already created. | 1024 // Already created. |
| 888 return; | 1025 return; |
| 889 } | 1026 } |
| 890 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, | 1027 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, |
| 891 tag, | 1028 tag, |
| 892 tag + 1, | 1029 tag + 1, |
| 893 0); | 1030 0, |
| 1031 null_code_); |
| 894 index = tag_code_table_->InsertCodeRegion(region); | 1032 index = tag_code_table_->InsertCodeRegion(region); |
| 895 ASSERT(index >= 0); | 1033 ASSERT(index >= 0); |
| 896 region->set_creation_serial(visited()); | 1034 region->set_creation_serial(visited()); |
| 897 } | 1035 } |
| 898 | 1036 |
| 899 void CreateUserTag(uword tag) { | 1037 void CreateUserTag(uword tag) { |
| 900 if (tag == 0) { | 1038 if (tag == 0) { |
| 901 // None set. | 1039 // None set. |
| 902 return; | 1040 return; |
| 903 } | 1041 } |
| 904 intptr_t index = tag_code_table_->FindIndex(tag); | 1042 return CreateTag(tag); |
| 905 if (index >= 0) { | |
| 906 // Already created. | |
| 907 return; | |
| 908 } | |
| 909 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, | |
| 910 tag, | |
| 911 tag + 1, | |
| 912 0); | |
| 913 index = tag_code_table_->InsertCodeRegion(region); | |
| 914 ASSERT(index >= 0); | |
| 915 region->set_creation_serial(visited()); | |
| 916 } | 1043 } |
| 917 | 1044 |
| 918 void Tick(uword pc, bool exclusive, int64_t timestamp) { | 1045 void Tick(uword pc, bool exclusive, int64_t timestamp) { |
| 919 CodeRegionTable::TickResult r; | 1046 CodeRegionTable::TickResult r; |
| 920 intptr_t serial = exclusive ? -1 : visited(); | 1047 intptr_t serial = exclusive ? -1 : visited(); |
| 921 r = live_code_table_->Tick(pc, exclusive, serial, timestamp); | 1048 r = live_code_table_->Tick(pc, exclusive, serial, timestamp); |
| 922 if (r == CodeRegionTable::kTicked) { | 1049 if (r == CodeRegionTable::kTicked) { |
| 923 // Live code found and ticked. | 1050 // Live code found and ticked. |
| 924 return; | 1051 return; |
| 925 } | 1052 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 951 // compiled after the sample. | 1078 // compiled after the sample. |
| 952 ASSERT(region->kind() == CodeRegion::kDartCode); | 1079 ASSERT(region->kind() == CodeRegion::kDartCode); |
| 953 CreateAndTickDeadCodeRegion(pc, exclusive, serial); | 1080 CreateAndTickDeadCodeRegion(pc, exclusive, serial); |
| 954 } | 1081 } |
| 955 | 1082 |
| 956 void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) { | 1083 void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) { |
| 957 // Need to create dead code. | 1084 // Need to create dead code. |
| 958 CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode, | 1085 CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode, |
| 959 pc, | 1086 pc, |
| 960 pc + 1, | 1087 pc + 1, |
| 961 0); | 1088 0, |
| 1089 null_code_); |
| 962 intptr_t index = dead_code_table_->InsertCodeRegion(region); | 1090 intptr_t index = dead_code_table_->InsertCodeRegion(region); |
| 963 region->set_creation_serial(visited()); | 1091 region->set_creation_serial(visited()); |
| 964 ASSERT(index >= 0); | 1092 ASSERT(index >= 0); |
| 965 dead_code_table_->At(index)->Tick(pc, exclusive, serial); | 1093 dead_code_table_->At(index)->Tick(pc, exclusive, serial); |
| 966 } | 1094 } |
| 967 | 1095 |
| 968 CodeRegion* CreateCodeRegion(uword pc) { | 1096 CodeRegion* CreateCodeRegion(uword pc) { |
| 969 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); | 1097 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); |
| 970 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); | 1098 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); |
| 971 Code& code = Code::Handle(isolate_); | 1099 Code& code = Code::Handle(isolate_); |
| 972 // Check current isolate for pc. | 1100 // Check current isolate for pc. |
| 973 if (isolate_->heap()->CodeContains(pc)) { | 1101 if (isolate_->heap()->CodeContains(pc)) { |
| 974 code ^= Code::LookupCode(pc); | 1102 code ^= Code::LookupCode(pc); |
| 975 if (!code.IsNull()) { | 1103 if (!code.IsNull()) { |
| 976 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | 1104 deoptimized_code_->Add(code); |
| 1105 return new CodeRegion(CodeRegion::kDartCode, |
| 1106 code.EntryPoint(), |
| 977 code.EntryPoint() + code.Size(), | 1107 code.EntryPoint() + code.Size(), |
| 978 code.compile_timestamp()); | 1108 code.compile_timestamp(), |
| 1109 code); |
| 979 } | 1110 } |
| 980 return new CodeRegion(CodeRegion::kCollectedCode, pc, | 1111 return new CodeRegion(CodeRegion::kCollectedCode, |
| 1112 pc, |
| 981 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | 1113 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| 982 0); | 1114 0, |
| 1115 code); |
| 983 } | 1116 } |
| 984 // Check VM isolate for pc. | 1117 // Check VM isolate for pc. |
| 985 if (vm_isolate_->heap()->CodeContains(pc)) { | 1118 if (vm_isolate_->heap()->CodeContains(pc)) { |
| 986 code ^= Code::LookupCodeInVmIsolate(pc); | 1119 code ^= Code::LookupCodeInVmIsolate(pc); |
| 987 if (!code.IsNull()) { | 1120 if (!code.IsNull()) { |
| 988 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | 1121 return new CodeRegion(CodeRegion::kDartCode, |
| 1122 code.EntryPoint(), |
| 989 code.EntryPoint() + code.Size(), | 1123 code.EntryPoint() + code.Size(), |
| 990 code.compile_timestamp()); | 1124 code.compile_timestamp(), |
| 1125 code); |
| 991 } | 1126 } |
| 992 return new CodeRegion(CodeRegion::kCollectedCode, pc, | 1127 return new CodeRegion(CodeRegion::kCollectedCode, |
| 1128 pc, |
| 993 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | 1129 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| 994 0); | 1130 0, |
| 1131 code); |
| 995 } | 1132 } |
| 996 // Check NativeSymbolResolver for pc. | 1133 // Check NativeSymbolResolver for pc. |
| 997 uintptr_t native_start = 0; | 1134 uintptr_t native_start = 0; |
| 998 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, | 1135 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, |
| 999 &native_start); | 1136 &native_start); |
| 1000 if (native_name == NULL) { | 1137 if (native_name == NULL) { |
| 1001 // No native name found. | 1138 // No native name found. |
| 1002 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1, 0); | 1139 return new CodeRegion(CodeRegion::kNativeCode, |
| 1140 pc, |
| 1141 pc + 1, |
| 1142 0, |
| 1143 code); |
| 1003 } | 1144 } |
| 1004 ASSERT(pc >= native_start); | 1145 ASSERT(pc >= native_start); |
| 1005 CodeRegion* code_region = | 1146 CodeRegion* code_region = |
| 1006 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1, 0); | 1147 new CodeRegion(CodeRegion::kNativeCode, |
| 1148 native_start, |
| 1149 pc + 1, |
| 1150 0, |
| 1151 code); |
| 1007 code_region->SetName(native_name); | 1152 code_region->SetName(native_name); |
| 1008 free(native_name); | 1153 free(native_name); |
| 1009 return code_region; | 1154 return code_region; |
| 1010 } | 1155 } |
| 1011 | 1156 |
| 1012 intptr_t frames_; | 1157 intptr_t frames_; |
| 1013 int64_t min_time_; | 1158 int64_t min_time_; |
| 1014 int64_t max_time_; | 1159 int64_t max_time_; |
| 1015 CodeRegionTable* live_code_table_; | 1160 CodeRegionTable* live_code_table_; |
| 1016 CodeRegionTable* dead_code_table_; | 1161 CodeRegionTable* dead_code_table_; |
| 1017 CodeRegionTable* tag_code_table_; | 1162 CodeRegionTable* tag_code_table_; |
| 1018 Isolate* isolate_; | 1163 Isolate* isolate_; |
| 1019 Isolate* vm_isolate_; | 1164 Isolate* vm_isolate_; |
| 1165 const Code& null_code_; |
| 1166 DeoptimizedCodeSet* deoptimized_code_; |
| 1020 }; | 1167 }; |
| 1021 | 1168 |
| 1022 | 1169 |
| 1023 class CodeRegionExclusiveTrieBuilder : public SampleVisitor { | 1170 class CodeRegionFunctionMapper : public ValueObject { |
| 1024 public: | 1171 public: |
| 1025 CodeRegionExclusiveTrieBuilder(Isolate* isolate, | 1172 CodeRegionFunctionMapper(Isolate* isolate, |
| 1026 CodeRegionTable* live_code_table, | 1173 CodeRegionTable* live_code_table, |
| 1027 CodeRegionTable* dead_code_table, | 1174 CodeRegionTable* dead_code_table, |
| 1028 CodeRegionTable* tag_code_table) | 1175 CodeRegionTable* tag_code_table, |
| 1029 : SampleVisitor(isolate), | 1176 ProfileFunctionTable* function_table) |
| 1177 : isolate_(isolate), |
| 1030 live_code_table_(live_code_table), | 1178 live_code_table_(live_code_table), |
| 1031 dead_code_table_(dead_code_table), | 1179 dead_code_table_(dead_code_table), |
| 1032 tag_code_table_(tag_code_table) { | 1180 tag_code_table_(tag_code_table), |
| 1181 function_table_(function_table) { |
| 1182 ASSERT(isolate_ != NULL); |
| 1033 ASSERT(live_code_table_ != NULL); | 1183 ASSERT(live_code_table_ != NULL); |
| 1034 ASSERT(dead_code_table_ != NULL); | 1184 ASSERT(dead_code_table_ != NULL); |
| 1035 ASSERT(tag_code_table_ != NULL); | 1185 ASSERT(tag_code_table_ != NULL); |
| 1036 dead_code_table_offset_ = live_code_table_->Length(); | 1186 dead_code_table_offset_ = live_code_table_->Length(); |
| 1037 tag_code_table_offset_ = dead_code_table_offset_ + | 1187 tag_code_table_offset_ = dead_code_table_offset_ + |
| 1038 dead_code_table_->Length(); | 1188 dead_code_table_->Length(); |
| 1039 intptr_t root_index = tag_code_table_->FindIndex(0); | 1189 intptr_t root_index = tag_code_table_->FindIndex(0); |
| 1040 // Verify that the "0" tag does not exist. | 1190 // Verify that the "0" tag does not exist. |
| 1041 ASSERT(root_index < 0); | 1191 ASSERT(root_index < 0); |
| 1042 // Insert the dummy tag CodeRegion that is used for the Trie root. | 1192 // Insert the dummy tag CodeRegion as the root. |
| 1043 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, 0, 1, 0); | 1193 const Code& null_code = Code::ZoneHandle(); |
| 1194 CodeRegion* region = |
| 1195 new CodeRegion(CodeRegion::kTagCode, 0, 1, 0, null_code); |
| 1044 root_index = tag_code_table_->InsertCodeRegion(region); | 1196 root_index = tag_code_table_->InsertCodeRegion(region); |
| 1045 ASSERT(root_index >= 0); | 1197 ASSERT(root_index >= 0); |
| 1046 region->set_creation_serial(0); | 1198 region->set_creation_serial(0); |
| 1047 root_ = new CodeRegionTrieNode(tag_code_table_offset_ + root_index); | 1199 } |
| 1200 |
| 1201 void Map() { |
| 1202 // Calculate final indexes in code table for each CodeRegion. |
| 1203 for (intptr_t i = 0; i < live_code_table_->Length(); i++) { |
| 1204 const intptr_t index = i; |
| 1205 CodeRegion* region = live_code_table_->At(i); |
| 1206 ASSERT(region != NULL); |
| 1207 region->set_code_table_index(index); |
| 1208 } |
| 1209 |
| 1210 for (intptr_t i = 0; i < dead_code_table_->Length(); i++) { |
| 1211 const intptr_t index = dead_code_table_offset_ + i; |
| 1212 CodeRegion* region = dead_code_table_->At(i); |
| 1213 ASSERT(region != NULL); |
| 1214 region->set_code_table_index(index); |
| 1215 } |
| 1216 |
| 1217 for (intptr_t i = 0; i < tag_code_table_->Length(); i++) { |
| 1218 const intptr_t index = tag_code_table_offset_ + i; |
| 1219 CodeRegion* region = tag_code_table_->At(i); |
| 1220 ASSERT(region != NULL); |
| 1221 region->set_code_table_index(index); |
| 1222 } |
| 1223 |
| 1224 // Associate a ProfileFunction with each CodeRegion. |
| 1225 for (intptr_t i = 0; i < live_code_table_->Length(); i++) { |
| 1226 CodeRegion* region = live_code_table_->At(i); |
| 1227 ASSERT(region != NULL); |
| 1228 region->SetFunctionAndName(function_table_); |
| 1229 } |
| 1230 |
| 1231 for (intptr_t i = 0; i < dead_code_table_->Length(); i++) { |
| 1232 CodeRegion* region = dead_code_table_->At(i); |
| 1233 ASSERT(region != NULL); |
| 1234 region->SetFunctionAndName(function_table_); |
| 1235 } |
| 1236 |
| 1237 for (intptr_t i = 0; i < tag_code_table_->Length(); i++) { |
| 1238 CodeRegion* region = tag_code_table_->At(i); |
| 1239 ASSERT(region != NULL); |
| 1240 region->SetFunctionAndName(function_table_); |
| 1241 } |
| 1242 } |
| 1243 |
| 1244 private: |
| 1245 Isolate* isolate_; |
| 1246 CodeRegionTable* live_code_table_; |
| 1247 CodeRegionTable* dead_code_table_; |
| 1248 CodeRegionTable* tag_code_table_; |
| 1249 ProfileFunctionTable* function_table_; |
| 1250 intptr_t dead_code_table_offset_; |
| 1251 intptr_t tag_code_table_offset_; |
| 1252 }; |
| 1253 |
| 1254 |
| 1255 class ProfileFunctionTrieNodeCode { |
| 1256 public: |
| 1257 explicit ProfileFunctionTrieNodeCode(intptr_t index) |
| 1258 : code_index_(index), |
| 1259 ticks_(0) { |
| 1260 } |
| 1261 |
| 1262 intptr_t index() const { |
| 1263 return code_index_; |
| 1264 } |
| 1265 |
| 1266 void Tick() { |
| 1267 ticks_++; |
| 1268 } |
| 1269 |
| 1270 intptr_t ticks() const { |
| 1271 return ticks_; |
| 1272 } |
| 1273 |
| 1274 private: |
| 1275 intptr_t code_index_; |
| 1276 intptr_t ticks_; |
| 1277 }; |
| 1278 |
| 1279 |
| 1280 class ProfileFunctionTrieNode : public ZoneAllocated { |
| 1281 public: |
| 1282 explicit ProfileFunctionTrieNode(intptr_t profile_function_table_index) |
| 1283 : profile_function_table_index_(profile_function_table_index), |
| 1284 count_(0), |
| 1285 code_objects_(new ZoneGrowableArray<ProfileFunctionTrieNodeCode>()) { |
| 1286 } |
| 1287 |
| 1288 void Tick() { |
| 1289 count_++; |
| 1290 } |
| 1291 |
| 1292 intptr_t count() const { |
| 1293 return count_; |
| 1294 } |
| 1295 |
| 1296 intptr_t profile_function_table_index() const { |
| 1297 return profile_function_table_index_; |
| 1298 } |
| 1299 |
| 1300 |
| 1301 ProfileFunctionTrieNode* GetChild(intptr_t child_index) { |
| 1302 const intptr_t length = children_.length(); |
| 1303 intptr_t i = 0; |
| 1304 while (i < length) { |
| 1305 ProfileFunctionTrieNode* child = children_[i]; |
| 1306 if (child->profile_function_table_index() == child_index) { |
| 1307 return child; |
| 1308 } |
| 1309 if (child->profile_function_table_index() > child_index) { |
| 1310 break; |
| 1311 } |
| 1312 i++; |
| 1313 } |
| 1314 // Add new ProfileFunctionTrieNode, sorted by index. |
| 1315 ProfileFunctionTrieNode* child = |
| 1316 new ProfileFunctionTrieNode(child_index); |
| 1317 if (i < length) { |
| 1318 // Insert at i. |
| 1319 children_.InsertAt(i, child); |
| 1320 } else { |
| 1321 // Add to end. |
| 1322 children_.Add(child); |
| 1323 } |
| 1324 return child; |
| 1325 } |
| 1326 |
| 1327 void AddCodeObjectIndex(intptr_t index) { |
| 1328 for (intptr_t i = 0; i < code_objects_->length(); i++) { |
| 1329 ProfileFunctionTrieNodeCode& code_object = (*code_objects_)[i]; |
| 1330 if (code_object.index() == index) { |
| 1331 code_object.Tick(); |
| 1332 return; |
| 1333 } |
| 1334 } |
| 1335 ProfileFunctionTrieNodeCode code_object(index); |
| 1336 code_object.Tick(); |
| 1337 code_objects_->Add(code_object); |
| 1338 } |
| 1339 |
| 1340 // This should only be called after the trie is completely built. |
| 1341 void SortByCount() { |
| 1342 code_objects_->Sort(ProfileFunctionTrieNodeCodeCompare); |
| 1343 children_.Sort(ProfileFunctionTrieNodeCompare); |
| 1344 intptr_t child_count = children_.length(); |
| 1345 // Recurse. |
| 1346 for (intptr_t i = 0; i < child_count; i++) { |
| 1347 children_[i]->SortByCount(); |
| 1348 } |
| 1349 } |
| 1350 |
| 1351 void PrintToJSONArray(JSONArray* array) const { |
| 1352 ASSERT(array != NULL); |
| 1353 // Write CodeRegion index. |
| 1354 array->AddValue(profile_function_table_index_); |
| 1355 // Write count. |
| 1356 array->AddValue(count_); |
| 1357 // Write number of code objects. |
| 1358 intptr_t code_count = code_objects_->length(); |
| 1359 array->AddValue(code_count); |
| 1360 // Write each code object index and ticks. |
| 1361 for (intptr_t i = 0; i < code_count; i++) { |
| 1362 array->AddValue((*code_objects_)[i].index()); |
| 1363 array->AddValue((*code_objects_)[i].ticks()); |
| 1364 } |
| 1365 // Write number of children. |
| 1366 intptr_t child_count = children_.length(); |
| 1367 array->AddValue(child_count); |
| 1368 // Recurse. |
| 1369 for (intptr_t i = 0; i < child_count; i++) { |
| 1370 children_[i]->PrintToJSONArray(array); |
| 1371 } |
| 1372 } |
| 1373 |
| 1374 private: |
| 1375 static int ProfileFunctionTrieNodeCodeCompare( |
| 1376 const ProfileFunctionTrieNodeCode* a, |
| 1377 const ProfileFunctionTrieNodeCode* b) { |
| 1378 ASSERT(a != NULL); |
| 1379 ASSERT(b != NULL); |
| 1380 return b->ticks() - a->ticks(); |
| 1381 } |
| 1382 |
| 1383 static int ProfileFunctionTrieNodeCompare(ProfileFunctionTrieNode* const* a, |
| 1384 ProfileFunctionTrieNode* const* b) { |
| 1385 ASSERT(a != NULL); |
| 1386 ASSERT(b != NULL); |
| 1387 return (*b)->count() - (*a)->count(); |
| 1388 } |
| 1389 |
| 1390 const intptr_t profile_function_table_index_; |
| 1391 intptr_t count_; |
| 1392 ZoneGrowableArray<ProfileFunctionTrieNode*> children_; |
| 1393 ZoneGrowableArray<ProfileFunctionTrieNodeCode>* code_objects_; |
| 1394 }; |
| 1395 |
| 1396 |
| 1397 class ProfileFunctionExclusiveTrieBuilder : public SampleVisitor { |
| 1398 public: |
| 1399 ProfileFunctionExclusiveTrieBuilder(Isolate* isolate, |
| 1400 CodeRegionTable* live_code_table, |
| 1401 CodeRegionTable* dead_code_table, |
| 1402 CodeRegionTable* tag_code_table, |
| 1403 ProfileFunctionTable* function_table) |
| 1404 : SampleVisitor(isolate), |
| 1405 live_code_table_(live_code_table), |
| 1406 dead_code_table_(dead_code_table), |
| 1407 tag_code_table_(tag_code_table), |
| 1408 function_table_(function_table), |
| 1409 trace_(false), |
| 1410 trace_code_filter_(NULL) { |
| 1411 ASSERT(live_code_table_ != NULL); |
| 1412 ASSERT(dead_code_table_ != NULL); |
| 1413 ASSERT(tag_code_table_ != NULL); |
| 1414 ASSERT(function_table_ != NULL); |
| 1048 set_tag_order(ProfilerService::kUserVM); | 1415 set_tag_order(ProfilerService::kUserVM); |
| 1416 |
| 1417 intptr_t root_index = tag_code_table_->FindIndex(0); |
| 1418 // Verify that the "0" tag does exist. |
| 1419 ASSERT(root_index >= 0); |
| 1420 CodeRegion* region = tag_code_table_->At(root_index); |
| 1421 ASSERT(region != NULL); |
| 1422 |
| 1423 ProfileFunction* function = region->function(); |
| 1424 root_ = new ProfileFunctionTrieNode(function->index()); |
| 1049 } | 1425 } |
| 1050 | 1426 |
| 1051 void VisitSample(Sample* sample) { | 1427 void VisitSample(Sample* sample) { |
| 1052 // Give the root a tick. | 1428 // Give the root a tick. |
| 1053 root_->Tick(); | 1429 root_->Tick(); |
| 1054 CodeRegionTrieNode* current = root_; | 1430 ProfileFunctionTrieNode* current = root_; |
| 1055 current = ProcessTags(sample, current); | 1431 current = ProcessTags(sample, current); |
| 1056 // Walk the sampled PCs. | 1432 // Walk the sampled PCs. |
| 1057 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { | 1433 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { |
| 1058 if (sample->At(i) == 0) { | 1434 if (sample->At(i) == 0) { |
| 1059 break; | 1435 break; |
| 1060 } | 1436 } |
| 1061 intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); | 1437 // If we aren't sampled out of an exit frame and this is the top |
| 1062 current = current->GetChild(index); | 1438 // frame. |
| 1063 current->Tick(); | 1439 bool exclusive_tick = (i == 0) && !sample->exit_frame_sample(); |
| 1064 } | 1440 current = ProcessPC(sample->At(i), sample->timestamp(), current, |
| 1065 } | 1441 visited(), exclusive_tick, |
| 1066 | 1442 sample->missing_frame_inserted()); |
| 1067 CodeRegionTrieNode* root() const { | 1443 } |
| 1444 } |
| 1445 |
| 1446 ProfileFunctionTrieNode* root() const { |
| 1068 return root_; | 1447 return root_; |
| 1069 } | 1448 } |
| 1070 | 1449 |
| 1071 ProfilerService::TagOrder tag_order() const { | 1450 ProfilerService::TagOrder tag_order() const { |
| 1072 return tag_order_; | 1451 return tag_order_; |
| 1073 } | 1452 } |
| 1074 | 1453 |
| 1075 void set_tag_order(ProfilerService::TagOrder tag_order) { | 1454 void set_tag_order(ProfilerService::TagOrder tag_order) { |
| 1076 tag_order_ = tag_order; | 1455 tag_order_ = tag_order; |
| 1077 } | 1456 } |
| 1078 | 1457 |
| 1079 private: | 1458 private: |
| 1080 CodeRegionTrieNode* ProcessUserTags(Sample* sample, | 1459 ProfileFunctionTrieNode* ProcessUserTags(Sample* sample, |
| 1081 CodeRegionTrieNode* current) { | 1460 ProfileFunctionTrieNode* current) { |
| 1082 intptr_t user_tag_index = FindTagIndex(sample->user_tag()); | 1461 intptr_t user_tag_index = FindTagIndex(sample->user_tag()); |
| 1083 if (user_tag_index >= 0) { | 1462 if (user_tag_index >= 0) { |
| 1084 current = current->GetChild(user_tag_index); | 1463 current = current->GetChild(user_tag_index); |
| 1085 // Give the tag a tick. | 1464 // Give the tag a tick. |
| 1086 current->Tick(); | 1465 current->Tick(); |
| 1087 } | 1466 } |
| 1088 return current; | 1467 return current; |
| 1089 } | 1468 } |
| 1090 | 1469 |
| 1091 CodeRegionTrieNode* ProcessVMTags(Sample* sample, | 1470 ProfileFunctionTrieNode* ProcessVMTags(Sample* sample, |
| 1092 CodeRegionTrieNode* current) { | 1471 ProfileFunctionTrieNode* current) { |
| 1093 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | 1472 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
| 1094 // Insert a dummy kNativeTagId node. | 1473 // Insert a dummy kNativeTagId node. |
| 1095 intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId); | 1474 intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId); |
| 1096 current = current->GetChild(tag_index); | 1475 current = current->GetChild(tag_index); |
| 1097 // Give the tag a tick. | 1476 // Give the tag a tick. |
| 1098 current->Tick(); | 1477 current->Tick(); |
| 1099 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1478 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
| 1100 // Insert a dummy kRuntimeTagId node. | 1479 // Insert a dummy kRuntimeTagId node. |
| 1101 intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId); | 1480 intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId); |
| 1102 current = current->GetChild(tag_index); | 1481 current = current->GetChild(tag_index); |
| 1103 // Give the tag a tick. | 1482 // Give the tag a tick. |
| 1104 current->Tick(); | 1483 current->Tick(); |
| 1105 } | 1484 } |
| 1106 intptr_t tag_index = FindTagIndex(sample->vm_tag()); | 1485 intptr_t tag_index = FindTagIndex(sample->vm_tag()); |
| 1107 current = current->GetChild(tag_index); | 1486 current = current->GetChild(tag_index); |
| 1108 // Give the tag a tick. | 1487 // Give the tag a tick. |
| 1109 current->Tick(); | 1488 current->Tick(); |
| 1110 return current; | 1489 return current; |
| 1111 } | 1490 } |
| 1112 | 1491 |
| 1113 CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) { | 1492 ProfileFunctionTrieNode* ProcessTags(Sample* sample, |
| 1493 ProfileFunctionTrieNode* current) { |
| 1114 // None. | 1494 // None. |
| 1115 if (tag_order() == ProfilerService::kNoTags) { | 1495 if (tag_order() == ProfilerService::kNoTags) { |
| 1116 return current; | 1496 return current; |
| 1117 } | 1497 } |
| 1118 // User first. | 1498 // User first. |
| 1119 if ((tag_order() == ProfilerService::kUserVM) || | 1499 if ((tag_order() == ProfilerService::kUserVM) || |
| 1120 (tag_order() == ProfilerService::kUser)) { | 1500 (tag_order() == ProfilerService::kUser)) { |
| 1121 current = ProcessUserTags(sample, current); | 1501 current = ProcessUserTags(sample, current); |
| 1122 // Only user. | 1502 // Only user. |
| 1123 if (tag_order() == ProfilerService::kUser) { | 1503 if (tag_order() == ProfilerService::kUser) { |
| 1124 return current; | 1504 return current; |
| 1125 } | 1505 } |
| 1126 return ProcessVMTags(sample, current); | 1506 return ProcessVMTags(sample, current); |
| 1127 } | 1507 } |
| 1128 // VM first. | 1508 // VM first. |
| 1129 ASSERT((tag_order() == ProfilerService::kVMUser) || | 1509 ASSERT((tag_order() == ProfilerService::kVMUser) || |
| 1130 (tag_order() == ProfilerService::kVM)); | 1510 (tag_order() == ProfilerService::kVM)); |
| 1131 current = ProcessVMTags(sample, current); | 1511 current = ProcessVMTags(sample, current); |
| 1132 // Only VM. | 1512 // Only VM. |
| 1133 if (tag_order() == ProfilerService::kVM) { | 1513 if (tag_order() == ProfilerService::kVM) { |
| 1134 return current; | 1514 return current; |
| 1135 } | 1515 } |
| 1136 return ProcessUserTags(sample, current); | 1516 return ProcessUserTags(sample, current); |
| 1137 } | 1517 } |
| 1138 | 1518 |
| 1139 intptr_t FindTagIndex(uword tag) const { | 1519 intptr_t FindTagIndex(uword tag) const { |
| 1140 if (tag == 0) { | 1520 if (tag == 0) { |
| 1521 UNREACHABLE(); |
| 1141 return -1; | 1522 return -1; |
| 1142 } | 1523 } |
| 1143 intptr_t index = tag_code_table_->FindIndex(tag); | 1524 intptr_t index = tag_code_table_->FindIndex(tag); |
| 1144 if (index <= 0) { | 1525 if (index < 0) { |
| 1526 UNREACHABLE(); |
| 1145 return -1; | 1527 return -1; |
| 1146 } | 1528 } |
| 1147 ASSERT(index >= 0); | 1529 ASSERT(index >= 0); |
| 1148 ASSERT((tag_code_table_->At(index))->contains(tag)); | 1530 CodeRegion* region = tag_code_table_->At(index); |
| 1149 return tag_code_table_offset_ + index; | 1531 ASSERT(region->contains(tag)); |
| 1150 } | 1532 ProfileFunction* function = region->function(); |
| 1151 | 1533 ASSERT(function != NULL); |
| 1152 intptr_t FindFinalIndex(uword pc, int64_t timestamp) const { | 1534 return function->index(); |
| 1535 } |
| 1536 |
| 1537 void Dump(ProfileFunctionTrieNode* current) { |
| 1538 int current_index = current->profile_function_table_index(); |
| 1539 ProfileFunction* function = function_table_->At(current_index); |
| 1540 function->Dump(); |
| 1541 OS::Print("\n"); |
| 1542 } |
| 1543 |
| 1544 ProfileFunctionTrieNode* ProcessPC(uword pc, int64_t timestamp, |
| 1545 ProfileFunctionTrieNode* current, |
| 1546 intptr_t inclusive_serial, |
| 1547 bool exclusive, |
| 1548 bool missing_frame_inserted) { |
| 1549 CodeRegion* region = FindCodeObject(pc, timestamp); |
| 1550 if (region == NULL) { |
| 1551 return current; |
| 1552 } |
| 1553 const char* region_name = region->name(); |
| 1554 if (region_name == NULL) { |
| 1555 region_name = ""; |
| 1556 } |
| 1557 intptr_t code_index = region->code_table_index(); |
| 1558 const Code& code = Code::ZoneHandle(region->code()); |
| 1559 GrowableArray<Function*> inlined_functions; |
| 1560 if (!code.IsNull()) { |
| 1561 intptr_t offset = pc - code.EntryPoint(); |
| 1562 code.GetInlinedFunctionsAt(offset, &inlined_functions); |
| 1563 } |
| 1564 if (code.IsNull() || (inlined_functions.length() == 0)) { |
| 1565 // No inlined functions. |
| 1566 ProfileFunction* function = region->function(); |
| 1567 ASSERT(function != NULL); |
| 1568 if (trace_) { |
| 1569 OS::Print("[%" Px "] X - %s (%s)\n", |
| 1570 pc, function->name(), region_name); |
| 1571 } |
| 1572 function->Tick(exclusive, exclusive ? -1 : inclusive_serial); |
| 1573 current = current->GetChild(function->index()); |
| 1574 current->AddCodeObjectIndex(code_index); |
| 1575 current->Tick(); |
| 1576 if ((trace_code_filter_ != NULL) && |
| 1577 (strstr(region_name, trace_code_filter_) != NULL)) { |
| 1578 trace_ = true; |
| 1579 OS::Print("Tracing from: %" Px " [%s] ", pc, |
| 1580 missing_frame_inserted ? "INSERTED" : ""); |
| 1581 Dump(current); |
| 1582 } |
| 1583 return current; |
| 1584 } |
| 1585 |
| 1586 |
| 1587 for (intptr_t i = 0; i < inlined_functions.length(); i++) { |
| 1588 Function* inlined_function = inlined_functions[i]; |
| 1589 ASSERT(inlined_function != NULL); |
| 1590 ASSERT(!inlined_function->IsNull()); |
| 1591 const char* inline_name = inlined_function->ToQualifiedCString(); |
| 1592 if (trace_) { |
| 1593 OS::Print("[%" Px "] %" Pd " - %s (%s)\n", |
| 1594 pc, i, inline_name, region_name); |
| 1595 } |
| 1596 ProfileFunction* function = |
| 1597 function_table_->LookupOrAdd(*inlined_function); |
| 1598 ASSERT(function != NULL); |
| 1599 function->AddCodeObjectIndex(code_index); |
| 1600 function->Tick(exclusive, exclusive ? -1 : inclusive_serial); |
| 1601 exclusive = false; |
| 1602 current = current->GetChild(function->index()); |
| 1603 current->AddCodeObjectIndex(code_index); |
| 1604 current->Tick(); |
| 1605 if ((trace_code_filter_ != NULL) && |
| 1606 (strstr(region_name, trace_code_filter_) != NULL)) { |
| 1607 trace_ = true; |
| 1608 OS::Print("Tracing from: %" Px " [%s] ", |
| 1609 pc, missing_frame_inserted ? "INSERTED" : ""); |
| 1610 Dump(current); |
| 1611 } |
| 1612 } |
| 1613 return current; |
| 1614 } |
| 1615 |
| 1616 CodeRegion* FindCodeObject(uword pc, int64_t timestamp) const { |
| 1153 intptr_t index = live_code_table_->FindIndex(pc); | 1617 intptr_t index = live_code_table_->FindIndex(pc); |
| 1154 ASSERT(index >= 0); | 1618 if (index < 0) { |
| 1619 return NULL; |
| 1620 } |
| 1155 CodeRegion* region = live_code_table_->At(index); | 1621 CodeRegion* region = live_code_table_->At(index); |
| 1156 ASSERT(region->contains(pc)); | 1622 ASSERT(region->contains(pc)); |
| 1157 if (region->compile_timestamp() > timestamp) { | 1623 if (region->compile_timestamp() > timestamp) { |
| 1158 // Overwritten code, find in dead code table. | 1624 // Overwritten code, find in dead code table. |
| 1159 index = dead_code_table_->FindIndex(pc); | 1625 index = dead_code_table_->FindIndex(pc); |
| 1160 ASSERT(index >= 0); | 1626 if (index < 0) { |
| 1627 return NULL; |
| 1628 } |
| 1161 region = dead_code_table_->At(index); | 1629 region = dead_code_table_->At(index); |
| 1162 ASSERT(region->contains(pc)); | 1630 ASSERT(region->contains(pc)); |
| 1163 ASSERT(region->compile_timestamp() <= timestamp); | 1631 ASSERT(region->compile_timestamp() <= timestamp); |
| 1164 return index + dead_code_table_offset_; | 1632 return region; |
| 1165 } | 1633 } |
| 1166 ASSERT(region->compile_timestamp() <= timestamp); | 1634 ASSERT(region->compile_timestamp() <= timestamp); |
| 1167 return index; | 1635 return region; |
| 1636 } |
| 1637 |
| 1638 ProfilerService::TagOrder tag_order_; |
| 1639 ProfileFunctionTrieNode* root_; |
| 1640 CodeRegionTable* live_code_table_; |
| 1641 CodeRegionTable* dead_code_table_; |
| 1642 CodeRegionTable* tag_code_table_; |
| 1643 ProfileFunctionTable* function_table_; |
| 1644 bool trace_; |
| 1645 const char* trace_code_filter_; |
| 1646 }; |
| 1647 |
| 1648 |
| 1649 class CodeRegionTrieNode : public ZoneAllocated { |
| 1650 public: |
| 1651 explicit CodeRegionTrieNode(intptr_t code_region_index) |
| 1652 : code_region_index_(code_region_index), |
| 1653 count_(0), |
| 1654 children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) { |
| 1655 } |
| 1656 |
| 1657 void Tick() { |
| 1658 ASSERT(code_region_index_ >= 0); |
| 1659 count_++; |
| 1660 } |
| 1661 |
| 1662 intptr_t count() const { |
| 1663 ASSERT(code_region_index_ >= 0); |
| 1664 return count_; |
| 1665 } |
| 1666 |
| 1667 intptr_t code_region_index() const { |
| 1668 return code_region_index_; |
| 1669 } |
| 1670 |
| 1671 ZoneGrowableArray<CodeRegionTrieNode*>& children() const { |
| 1672 return *children_; |
| 1673 } |
| 1674 |
| 1675 CodeRegionTrieNode* GetChild(intptr_t child_code_region_index) { |
| 1676 const intptr_t length = children_->length(); |
| 1677 intptr_t i = 0; |
| 1678 while (i < length) { |
| 1679 CodeRegionTrieNode* child = (*children_)[i]; |
| 1680 if (child->code_region_index() == child_code_region_index) { |
| 1681 return child; |
| 1682 } |
| 1683 if (child->code_region_index() > child_code_region_index) { |
| 1684 break; |
| 1685 } |
| 1686 i++; |
| 1687 } |
| 1688 // Add new CodeRegion, sorted by CodeRegionTable index. |
| 1689 CodeRegionTrieNode* child = new CodeRegionTrieNode(child_code_region_index); |
| 1690 if (i < length) { |
| 1691 // Insert at i. |
| 1692 children_->InsertAt(i, child); |
| 1693 } else { |
| 1694 // Add to end. |
| 1695 children_->Add(child); |
| 1696 } |
| 1697 return child; |
| 1698 } |
| 1699 |
| 1700 // This should only be called after the trie is completely built. |
| 1701 void SortByCount() { |
| 1702 children_->Sort(CodeRegionTrieNodeCompare); |
| 1703 ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); |
| 1704 intptr_t child_count = kids.length(); |
| 1705 // Recurse. |
| 1706 for (intptr_t i = 0; i < child_count; i++) { |
| 1707 kids[i]->SortByCount(); |
| 1708 } |
| 1709 } |
| 1710 |
| 1711 void PrintToJSONArray(JSONArray* array) const { |
| 1712 ASSERT(array != NULL); |
| 1713 // Write CodeRegion index. |
| 1714 array->AddValue(code_region_index_); |
| 1715 // Write count. |
| 1716 array->AddValue(count_); |
| 1717 // Write number of children. |
| 1718 ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); |
| 1719 intptr_t child_count = kids.length(); |
| 1720 array->AddValue(child_count); |
| 1721 // Recurse. |
| 1722 for (intptr_t i = 0; i < child_count; i++) { |
| 1723 kids[i]->PrintToJSONArray(array); |
| 1724 } |
| 1725 } |
| 1726 |
| 1727 private: |
| 1728 static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a, |
| 1729 CodeRegionTrieNode* const* b) { |
| 1730 ASSERT(a != NULL); |
| 1731 ASSERT(b != NULL); |
| 1732 return (*b)->count() - (*a)->count(); |
| 1733 } |
| 1734 |
| 1735 const intptr_t code_region_index_; |
| 1736 intptr_t count_; |
| 1737 ZoneGrowableArray<CodeRegionTrieNode*>* children_; |
| 1738 }; |
| 1739 |
| 1740 |
| 1741 class CodeRegionExclusiveTrieBuilder : public SampleVisitor { |
| 1742 public: |
| 1743 CodeRegionExclusiveTrieBuilder(Isolate* isolate, |
| 1744 CodeRegionTable* live_code_table, |
| 1745 CodeRegionTable* dead_code_table, |
| 1746 CodeRegionTable* tag_code_table) |
| 1747 : SampleVisitor(isolate), |
| 1748 live_code_table_(live_code_table), |
| 1749 dead_code_table_(dead_code_table), |
| 1750 tag_code_table_(tag_code_table) { |
| 1751 ASSERT(live_code_table_ != NULL); |
| 1752 ASSERT(dead_code_table_ != NULL); |
| 1753 ASSERT(tag_code_table_ != NULL); |
| 1754 set_tag_order(ProfilerService::kUserVM); |
| 1755 |
| 1756 intptr_t root_index = tag_code_table_->FindIndex(0); |
| 1757 // Verify that the "0" (root) tag does exist. |
| 1758 ASSERT(root_index >= 0); |
| 1759 CodeRegion* region = tag_code_table_->At(root_index); |
| 1760 ASSERT(region != NULL); |
| 1761 root_ = new CodeRegionTrieNode(region->code_table_index()); |
| 1762 } |
| 1763 |
| 1764 void VisitSample(Sample* sample) { |
| 1765 // Give the root a tick. |
| 1766 root_->Tick(); |
| 1767 CodeRegionTrieNode* current = root_; |
| 1768 current = ProcessTags(sample, current); |
| 1769 // Walk the sampled PCs. |
| 1770 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { |
| 1771 if (sample->At(i) == 0) { |
| 1772 break; |
| 1773 } |
| 1774 intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); |
| 1775 if (index < 0) { |
| 1776 continue; |
| 1777 } |
| 1778 current = current->GetChild(index); |
| 1779 current->Tick(); |
| 1780 } |
| 1781 } |
| 1782 |
| 1783 CodeRegionTrieNode* root() const { |
| 1784 return root_; |
| 1785 } |
| 1786 |
| 1787 ProfilerService::TagOrder tag_order() const { |
| 1788 return tag_order_; |
| 1789 } |
| 1790 |
| 1791 void set_tag_order(ProfilerService::TagOrder tag_order) { |
| 1792 tag_order_ = tag_order; |
| 1793 } |
| 1794 |
| 1795 private: |
| 1796 CodeRegionTrieNode* ProcessUserTags(Sample* sample, |
| 1797 CodeRegionTrieNode* current) { |
| 1798 intptr_t user_tag_index = FindTagIndex(sample->user_tag()); |
| 1799 if (user_tag_index >= 0) { |
| 1800 current = current->GetChild(user_tag_index); |
| 1801 // Give the tag a tick. |
| 1802 current->Tick(); |
| 1803 } |
| 1804 return current; |
| 1805 } |
| 1806 |
| 1807 CodeRegionTrieNode* ProcessVMTags(Sample* sample, |
| 1808 CodeRegionTrieNode* current) { |
| 1809 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
| 1810 // Insert a dummy kNativeTagId node. |
| 1811 intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId); |
| 1812 current = current->GetChild(tag_index); |
| 1813 // Give the tag a tick. |
| 1814 current->Tick(); |
| 1815 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
| 1816 // Insert a dummy kRuntimeTagId node. |
| 1817 intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId); |
| 1818 current = current->GetChild(tag_index); |
| 1819 // Give the tag a tick. |
| 1820 current->Tick(); |
| 1821 } |
| 1822 intptr_t tag_index = FindTagIndex(sample->vm_tag()); |
| 1823 current = current->GetChild(tag_index); |
| 1824 // Give the tag a tick. |
| 1825 current->Tick(); |
| 1826 return current; |
| 1827 } |
| 1828 |
| 1829 CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) { |
| 1830 // None. |
| 1831 if (tag_order() == ProfilerService::kNoTags) { |
| 1832 return current; |
| 1833 } |
| 1834 // User first. |
| 1835 if ((tag_order() == ProfilerService::kUserVM) || |
| 1836 (tag_order() == ProfilerService::kUser)) { |
| 1837 current = ProcessUserTags(sample, current); |
| 1838 // Only user. |
| 1839 if (tag_order() == ProfilerService::kUser) { |
| 1840 return current; |
| 1841 } |
| 1842 return ProcessVMTags(sample, current); |
| 1843 } |
| 1844 // VM first. |
| 1845 ASSERT((tag_order() == ProfilerService::kVMUser) || |
| 1846 (tag_order() == ProfilerService::kVM)); |
| 1847 current = ProcessVMTags(sample, current); |
| 1848 // Only VM. |
| 1849 if (tag_order() == ProfilerService::kVM) { |
| 1850 return current; |
| 1851 } |
| 1852 return ProcessUserTags(sample, current); |
| 1853 } |
| 1854 |
| 1855 intptr_t FindTagIndex(uword tag) const { |
| 1856 if (tag == 0) { |
| 1857 UNREACHABLE(); |
| 1858 return -1; |
| 1859 } |
| 1860 intptr_t index = tag_code_table_->FindIndex(tag); |
| 1861 if (index < 0) { |
| 1862 UNREACHABLE(); |
| 1863 return -1; |
| 1864 } |
| 1865 ASSERT(index >= 0); |
| 1866 CodeRegion* region = tag_code_table_->At(index); |
| 1867 ASSERT(region->contains(tag)); |
| 1868 return region->code_table_index(); |
| 1869 } |
| 1870 |
| 1871 intptr_t FindDeadIndex(uword pc, int64_t timestamp) const { |
| 1872 intptr_t index = dead_code_table_->FindIndex(pc); |
| 1873 if (index < 0) { |
| 1874 OS::Print("%" Px " cannot be found\n", pc); |
| 1875 return -1; |
| 1876 } |
| 1877 CodeRegion* region = dead_code_table_->At(index); |
| 1878 ASSERT(region->contains(pc)); |
| 1879 ASSERT(region->compile_timestamp() <= timestamp); |
| 1880 return region->code_table_index(); |
| 1881 } |
| 1882 |
| 1883 intptr_t FindFinalIndex(uword pc, int64_t timestamp) const { |
| 1884 intptr_t index = live_code_table_->FindIndex(pc); |
| 1885 if (index < 0) { |
| 1886 // Try dead code table. |
| 1887 return FindDeadIndex(pc, timestamp); |
| 1888 } |
| 1889 CodeRegion* region = live_code_table_->At(index); |
| 1890 ASSERT(region->contains(pc)); |
| 1891 if (region->compile_timestamp() > timestamp) { |
| 1892 // Overwritten code, find in dead code table. |
| 1893 return FindDeadIndex(pc, timestamp); |
| 1894 } |
| 1895 ASSERT(region->compile_timestamp() <= timestamp); |
| 1896 return region->code_table_index(); |
| 1168 } | 1897 } |
| 1169 | 1898 |
| 1170 ProfilerService::TagOrder tag_order_; | 1899 ProfilerService::TagOrder tag_order_; |
| 1171 CodeRegionTrieNode* root_; | 1900 CodeRegionTrieNode* root_; |
| 1172 CodeRegionTable* live_code_table_; | 1901 CodeRegionTable* live_code_table_; |
| 1173 CodeRegionTable* dead_code_table_; | 1902 CodeRegionTable* dead_code_table_; |
| 1174 CodeRegionTable* tag_code_table_; | 1903 CodeRegionTable* tag_code_table_; |
| 1175 intptr_t dead_code_table_offset_; | |
| 1176 intptr_t tag_code_table_offset_; | |
| 1177 }; | 1904 }; |
| 1178 | 1905 |
| 1179 | 1906 |
| 1180 class CodeRegionTableCallersBuilder { | |
| 1181 public: | |
| 1182 CodeRegionTableCallersBuilder(CodeRegionTrieNode* exclusive_root, | |
| 1183 CodeRegionTable* live_code_table, | |
| 1184 CodeRegionTable* dead_code_table, | |
| 1185 CodeRegionTable* tag_code_table) | |
| 1186 : exclusive_root_(exclusive_root), | |
| 1187 live_code_table_(live_code_table), | |
| 1188 dead_code_table_(dead_code_table), | |
| 1189 tag_code_table_(tag_code_table) { | |
| 1190 ASSERT(exclusive_root_ != NULL); | |
| 1191 ASSERT(live_code_table_ != NULL); | |
| 1192 ASSERT(dead_code_table_ != NULL); | |
| 1193 ASSERT(tag_code_table_ != NULL); | |
| 1194 dead_code_table_offset_ = live_code_table_->Length(); | |
| 1195 tag_code_table_offset_ = dead_code_table_offset_ + | |
| 1196 dead_code_table_->Length(); | |
| 1197 } | |
| 1198 | |
| 1199 void Build() { | |
| 1200 ProcessNode(exclusive_root_); | |
| 1201 } | |
| 1202 | |
| 1203 private: | |
| 1204 void ProcessNode(CodeRegionTrieNode* parent) { | |
| 1205 const ZoneGrowableArray<CodeRegionTrieNode*>& children = parent->children(); | |
| 1206 intptr_t parent_index = parent->code_region_index(); | |
| 1207 ASSERT(parent_index >= 0); | |
| 1208 CodeRegion* parent_region = At(parent_index); | |
| 1209 ASSERT(parent_region != NULL); | |
| 1210 for (intptr_t i = 0; i < children.length(); i++) { | |
| 1211 CodeRegionTrieNode* node = children[i]; | |
| 1212 ProcessNode(node); | |
| 1213 intptr_t index = node->code_region_index(); | |
| 1214 ASSERT(index >= 0); | |
| 1215 CodeRegion* region = At(index); | |
| 1216 ASSERT(region != NULL); | |
| 1217 region->AddCallee(parent_index, node->count()); | |
| 1218 parent_region->AddCaller(index, node->count()); | |
| 1219 } | |
| 1220 } | |
| 1221 | |
| 1222 CodeRegion* At(intptr_t final_index) { | |
| 1223 ASSERT(final_index >= 0); | |
| 1224 if (final_index < dead_code_table_offset_) { | |
| 1225 return live_code_table_->At(final_index); | |
| 1226 } else if (final_index < tag_code_table_offset_) { | |
| 1227 return dead_code_table_->At(final_index - dead_code_table_offset_); | |
| 1228 } else { | |
| 1229 return tag_code_table_->At(final_index - tag_code_table_offset_); | |
| 1230 } | |
| 1231 } | |
| 1232 | |
| 1233 CodeRegionTrieNode* exclusive_root_; | |
| 1234 CodeRegionTable* live_code_table_; | |
| 1235 CodeRegionTable* dead_code_table_; | |
| 1236 CodeRegionTable* tag_code_table_; | |
| 1237 intptr_t dead_code_table_offset_; | |
| 1238 intptr_t tag_code_table_offset_; | |
| 1239 }; | |
| 1240 | |
| 1241 | |
| 1242 void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { | 1907 void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { |
| 1243 Isolate* isolate = Isolate::Current(); | 1908 Isolate* isolate = Isolate::Current(); |
| 1244 // Disable profile interrupts while processing the buffer. | 1909 // Disable profile interrupts while processing the buffer. |
| 1245 Profiler::EndExecution(isolate); | 1910 Profiler::EndExecution(isolate); |
| 1246 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 1911 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| 1247 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 1912 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 1248 if (profiler_data == NULL) { | 1913 if (profiler_data == NULL) { |
| 1249 JSONObject error(stream); | 1914 JSONObject error(stream); |
| 1250 error.AddProperty("type", "Error"); | 1915 error.AddProperty("type", "Error"); |
| 1251 error.AddProperty("text", "Isolate does not have profiling enabled."); | 1916 error.AddProperty("text", "Isolate does not have profiling enabled."); |
| 1252 return; | 1917 return; |
| 1253 } | 1918 } |
| 1254 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 1919 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 1255 ASSERT(sample_buffer != NULL); | 1920 ASSERT(sample_buffer != NULL); |
| 1921 ScopeTimer sw("ProfilerService::PrintJSON", FLAG_trace_profiler); |
| 1256 { | 1922 { |
| 1257 StackZone zone(isolate); | 1923 StackZone zone(isolate); |
| 1924 HANDLESCOPE(isolate); |
| 1258 { | 1925 { |
| 1259 // Live code holds Dart, Native, and Collected CodeRegions. | 1926 // Live code holds Dart, Native, and Collected CodeRegions. |
| 1260 CodeRegionTable live_code_table; | 1927 CodeRegionTable live_code_table; |
| 1261 // Dead code holds Overwritten CodeRegions. | 1928 // Dead code holds Overwritten CodeRegions. |
| 1262 CodeRegionTable dead_code_table; | 1929 CodeRegionTable dead_code_table; |
| 1263 // Tag code holds Tag CodeRegions. | 1930 // Tag code holds Tag CodeRegions. |
| 1264 CodeRegionTable tag_code_table; | 1931 CodeRegionTable tag_code_table; |
| 1932 // Table holding all ProfileFunctions. |
| 1933 ProfileFunctionTable function_table; |
| 1934 // Set of deoptimized code still referenced by the profiler. |
| 1935 DeoptimizedCodeSet* deoptimized_code = new DeoptimizedCodeSet(isolate); |
| 1936 |
| 1937 { |
| 1938 ScopeTimer sw("PreprocessSamples", FLAG_trace_profiler); |
| 1939 // Preprocess samples. |
| 1940 PreprocessVisitor preprocessor(isolate); |
| 1941 sample_buffer->VisitSamples(&preprocessor); |
| 1942 } |
| 1943 |
| 1944 // Build CodeRegion tables. |
| 1265 CodeRegionTableBuilder builder(isolate, | 1945 CodeRegionTableBuilder builder(isolate, |
| 1266 &live_code_table, | 1946 &live_code_table, |
| 1267 &dead_code_table, | 1947 &dead_code_table, |
| 1268 &tag_code_table); | 1948 &tag_code_table, |
| 1949 deoptimized_code); |
| 1269 { | 1950 { |
| 1270 ScopeTimer sw("FixTopFrame", FLAG_trace_profiler); | |
| 1271 // Preprocess samples and fix the caller when the top PC is in a | |
| 1272 // stub or intrinsic without a frame. | |
| 1273 FixTopFrameVisitor fixTopFrame(isolate); | |
| 1274 sample_buffer->VisitSamples(&fixTopFrame); | |
| 1275 } | |
| 1276 { | |
| 1277 // Build CodeRegion tables. | |
| 1278 ScopeTimer sw("CodeRegionTableBuilder", FLAG_trace_profiler); | 1951 ScopeTimer sw("CodeRegionTableBuilder", FLAG_trace_profiler); |
| 1279 sample_buffer->VisitSamples(&builder); | 1952 sample_buffer->VisitSamples(&builder); |
| 1280 } | 1953 } |
| 1281 intptr_t samples = builder.visited(); | 1954 intptr_t samples = builder.visited(); |
| 1282 intptr_t frames = builder.frames(); | 1955 intptr_t frames = builder.frames(); |
| 1283 if (FLAG_trace_profiler) { | 1956 if (FLAG_trace_profiler) { |
| 1284 intptr_t total_live_code_objects = live_code_table.Length(); | 1957 intptr_t total_live_code_objects = live_code_table.Length(); |
| 1285 intptr_t total_dead_code_objects = dead_code_table.Length(); | 1958 intptr_t total_dead_code_objects = dead_code_table.Length(); |
| 1286 intptr_t total_tag_code_objects = tag_code_table.Length(); | 1959 intptr_t total_tag_code_objects = tag_code_table.Length(); |
| 1287 OS::Print("Processed %" Pd " frames\n", frames); | 1960 OS::Print("Processed %" Pd " frames\n", frames); |
| 1288 OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n", | 1961 OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n", |
| 1289 total_live_code_objects, | 1962 total_live_code_objects, |
| 1290 total_dead_code_objects, | 1963 total_dead_code_objects, |
| 1291 total_tag_code_objects); | 1964 total_tag_code_objects); |
| 1292 } | 1965 } |
| 1293 #if defined(DEBUG) | 1966 |
| 1294 live_code_table.Verify(); | |
| 1295 dead_code_table.Verify(); | |
| 1296 tag_code_table.Verify(); | |
| 1297 if (FLAG_trace_profiler) { | 1967 if (FLAG_trace_profiler) { |
| 1298 OS::Print("CodeRegionTables verified to be ordered and not overlap.\n"); | 1968 ScopeTimer sw("CodeRegionTableVerify", FLAG_trace_profiler); |
| 1969 live_code_table.Verify(); |
| 1970 dead_code_table.Verify(); |
| 1971 tag_code_table.Verify(); |
| 1299 } | 1972 } |
| 1300 #endif | 1973 |
| 1301 CodeRegionExclusiveTrieBuilder build_trie(isolate, | 1974 { |
| 1302 &live_code_table, | 1975 ScopeTimer st("CodeRegionFunctionMapping", FLAG_trace_profiler); |
| 1303 &dead_code_table, | 1976 CodeRegionFunctionMapper mapper(isolate, &live_code_table, |
| 1304 &tag_code_table); | 1977 &dead_code_table, |
| 1305 build_trie.set_tag_order(tag_order); | 1978 &tag_code_table, |
| 1979 &function_table); |
| 1980 mapper.Map(); |
| 1981 } |
| 1982 if (FLAG_trace_profiler) { |
| 1983 intptr_t total_functions = function_table.Length(); |
| 1984 OS::Print("FunctionTable: size=%" Pd "\n", total_functions); |
| 1985 } |
| 1986 CodeRegionExclusiveTrieBuilder code_trie_builder(isolate, |
| 1987 &live_code_table, |
| 1988 &dead_code_table, |
| 1989 &tag_code_table); |
| 1990 code_trie_builder.set_tag_order(tag_order); |
| 1306 { | 1991 { |
| 1307 // Build CodeRegion trie. | 1992 // Build CodeRegion trie. |
| 1308 ScopeTimer sw("CodeRegionExclusiveTrieBuilder", FLAG_trace_profiler); | 1993 ScopeTimer sw("CodeRegionExclusiveTrieBuilder", FLAG_trace_profiler); |
| 1309 sample_buffer->VisitSamples(&build_trie); | 1994 sample_buffer->VisitSamples(&code_trie_builder); |
| 1310 build_trie.root()->SortByCount(); | 1995 code_trie_builder.root()->SortByCount(); |
| 1311 } | 1996 } |
| 1312 CodeRegionTableCallersBuilder build_callers(build_trie.root(), | 1997 ProfileFunctionExclusiveTrieBuilder |
| 1313 &live_code_table, | 1998 function_trie_builder(isolate, |
| 1314 &dead_code_table, | 1999 &live_code_table, |
| 1315 &tag_code_table); | 2000 &dead_code_table, |
| 2001 &tag_code_table, |
| 2002 &function_table); |
| 2003 function_trie_builder.set_tag_order(tag_order); |
| 1316 { | 2004 { |
| 1317 // Build CodeRegion callers. | 2005 // Build ProfileFunction trie. |
| 1318 ScopeTimer sw("CodeRegionTableCallersBuilder", FLAG_trace_profiler); | 2006 ScopeTimer sw("ProfileFunctionExclusiveTrieBuilder", |
| 1319 build_callers.Build(); | 2007 FLAG_trace_profiler); |
| 2008 sample_buffer->VisitSamples(&function_trie_builder); |
| 2009 function_trie_builder.root()->SortByCount(); |
| 1320 } | 2010 } |
| 1321 { | 2011 { |
| 1322 ScopeTimer sw("CodeTableStream", FLAG_trace_profiler); | 2012 ScopeTimer sw("CodeTableStream", FLAG_trace_profiler); |
| 1323 // Serialize to JSON. | 2013 // Serialize to JSON. |
| 1324 JSONObject obj(stream); | 2014 JSONObject obj(stream); |
| 1325 obj.AddProperty("type", "CpuProfile"); | 2015 obj.AddProperty("type", "_CpuProfile"); |
| 1326 obj.AddProperty("id", "profile"); | 2016 obj.AddProperty("sampleCount", samples); |
| 1327 obj.AddProperty("samples", samples); | 2017 obj.AddProperty("samplePeriod", |
| 1328 obj.AddProperty("depth", static_cast<intptr_t>(FLAG_profile_depth)); | 2018 static_cast<intptr_t>(FLAG_profile_period)); |
| 1329 obj.AddProperty("period", static_cast<intptr_t>(FLAG_profile_period)); | 2019 obj.AddProperty("stackDepth", |
| 2020 static_cast<intptr_t>(FLAG_profile_depth)); |
| 1330 obj.AddProperty("timeSpan", | 2021 obj.AddProperty("timeSpan", |
| 1331 MicrosecondsToSeconds(builder.TimeDeltaMicros())); | 2022 MicrosecondsToSeconds(builder.TimeDeltaMicros())); |
| 1332 { | 2023 { |
| 1333 JSONArray exclusive_trie(&obj, "exclusive_trie"); | 2024 JSONArray exclusive_trie(&obj, "exclusiveCodeTrie"); |
| 1334 CodeRegionTrieNode* root = build_trie.root(); | 2025 CodeRegionTrieNode* root = code_trie_builder.root(); |
| 1335 ASSERT(root != NULL); | 2026 ASSERT(root != NULL); |
| 1336 root->PrintToJSONArray(&exclusive_trie); | 2027 root->PrintToJSONArray(&exclusive_trie); |
| 1337 } | 2028 } |
| 1338 JSONArray codes(&obj, "codes"); | 2029 { |
| 1339 for (intptr_t i = 0; i < live_code_table.Length(); i++) { | 2030 JSONArray function_trie(&obj, "exclusiveFunctionTrie"); |
| 1340 CodeRegion* region = live_code_table.At(i); | 2031 ProfileFunctionTrieNode* root = function_trie_builder.root(); |
| 1341 ASSERT(region != NULL); | 2032 ASSERT(root != NULL); |
| 1342 region->PrintToJSONArray(isolate, &codes); | 2033 root->PrintToJSONArray(&function_trie); |
| 1343 } | 2034 } |
| 1344 for (intptr_t i = 0; i < dead_code_table.Length(); i++) { | 2035 { |
| 1345 CodeRegion* region = dead_code_table.At(i); | 2036 JSONArray codes(&obj, "codes"); |
| 1346 ASSERT(region != NULL); | 2037 for (intptr_t i = 0; i < live_code_table.Length(); i++) { |
| 1347 region->PrintToJSONArray(isolate, &codes); | 2038 CodeRegion* region = live_code_table.At(i); |
| 2039 ASSERT(region != NULL); |
| 2040 region->PrintToJSONArray(&codes); |
| 2041 } |
| 2042 for (intptr_t i = 0; i < dead_code_table.Length(); i++) { |
| 2043 CodeRegion* region = dead_code_table.At(i); |
| 2044 ASSERT(region != NULL); |
| 2045 region->PrintToJSONArray(&codes); |
| 2046 } |
| 2047 for (intptr_t i = 0; i < tag_code_table.Length(); i++) { |
| 2048 CodeRegion* region = tag_code_table.At(i); |
| 2049 ASSERT(region != NULL); |
| 2050 region->PrintToJSONArray(&codes); |
| 2051 } |
| 1348 } | 2052 } |
| 1349 for (intptr_t i = 0; i < tag_code_table.Length(); i++) { | 2053 { |
| 1350 CodeRegion* region = tag_code_table.At(i); | 2054 JSONArray functions(&obj, "functions"); |
| 1351 ASSERT(region != NULL); | 2055 for (intptr_t i = 0; i < function_table.Length(); i++) { |
| 1352 region->PrintToJSONArray(isolate, &codes); | 2056 ProfileFunction* function = function_table.At(i); |
| 2057 ASSERT(function != NULL); |
| 2058 function->PrintToJSONArray(&functions); |
| 2059 } |
| 1353 } | 2060 } |
| 1354 } | 2061 } |
| 2062 // Update the isolates set of dead code. |
| 2063 deoptimized_code->UpdateIsolate(isolate); |
| 1355 } | 2064 } |
| 1356 } | 2065 } |
| 1357 // Enable profile interrupts. | 2066 // Enable profile interrupts. |
| 1358 Profiler::BeginExecution(isolate); | 2067 Profiler::BeginExecution(isolate); |
| 1359 } | 2068 } |
| 1360 | 2069 |
| 2070 |
| 2071 void ProfilerService::ClearSamples() { |
| 2072 Isolate* isolate = Isolate::Current(); |
| 2073 |
| 2074 // Disable profile interrupts while processing the buffer. |
| 2075 Profiler::EndExecution(isolate); |
| 2076 |
| 2077 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| 2078 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 2079 if (profiler_data == NULL) { |
| 2080 return; |
| 2081 } |
| 2082 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 2083 ASSERT(sample_buffer != NULL); |
| 2084 |
| 2085 ClearProfileVisitor clear_profile(isolate); |
| 2086 sample_buffer->VisitSamples(&clear_profile); |
| 2087 |
| 2088 // Enable profile interrupts. |
| 2089 Profiler::BeginExecution(isolate); |
| 2090 } |
| 2091 |
| 1361 } // namespace dart | 2092 } // namespace dart |
| OLD | NEW |