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