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 length_before = previous_.Length(); | |
rmacnak
2015/02/24 20:52:43
vm/cc/Service_Profile is hitting an assert here be
Cutch
2015/02/24 20:54:37
Done.
| |
50 intptr_t length_after = current_.Length(); | |
51 intptr_t size_before = SizeOf(previous_); | |
52 intptr_t size_after = SizeOf(current_); | |
53 if ((size_before > 0) && FLAG_trace_profiler) { | |
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 |