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