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