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(int, profile_period); | 18 DECLARE_FLAG(int, profile_period); |
19 | 19 |
20 DEFINE_FLAG(bool, trace_profiler, false, "Trace profiler."); | 20 DEFINE_FLAG(bool, trace_profiler, true, "Trace profiler."); |
rmacnak
2015/06/26 17:37:44
Disable before landing
Cutch
2015/06/26 17:48:40
Done.
| |
21 | |
22 // Forward declarations. | |
23 class CodeRegion; | |
24 class ProfileFunction; | |
25 class ProfileFunctionTable; | |
26 | |
27 | 21 |
28 class DeoptimizedCodeSet : public ZoneAllocated { | 22 class DeoptimizedCodeSet : public ZoneAllocated { |
29 public: | 23 public: |
30 explicit DeoptimizedCodeSet(Isolate* isolate) | 24 explicit DeoptimizedCodeSet(Isolate* isolate) |
31 : previous_( | 25 : previous_( |
32 GrowableObjectArray::ZoneHandle(isolate->deoptimized_code_array())), | 26 GrowableObjectArray::ZoneHandle(isolate->deoptimized_code_array())), |
33 current_(GrowableObjectArray::ZoneHandle( | 27 current_(GrowableObjectArray::ZoneHandle( |
34 previous_.IsNull() ? GrowableObjectArray::null() : | 28 previous_.IsNull() ? GrowableObjectArray::null() : |
35 GrowableObjectArray::New())) { | 29 GrowableObjectArray::New())) { |
36 } | 30 } |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
85 } | 79 } |
86 return size; | 80 return size; |
87 } | 81 } |
88 | 82 |
89 // Array holding code that is being kept around only for the profiler. | 83 // Array holding code that is being kept around only for the profiler. |
90 const GrowableObjectArray& previous_; | 84 const GrowableObjectArray& previous_; |
91 // Array holding code that should continue to be kept around for the profiler. | 85 // Array holding code that should continue to be kept around for the profiler. |
92 const GrowableObjectArray& current_; | 86 const GrowableObjectArray& current_; |
93 }; | 87 }; |
94 | 88 |
95 class ProfileFunction : public ZoneAllocated { | 89 |
96 public: | 90 ProfileFunction::ProfileFunction(Kind kind, |
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, | 91 const char* name, |
106 const Function& function, | 92 const Function& function, |
107 const intptr_t table_index) | 93 const intptr_t table_index) |
108 : kind_(kind), | 94 : kind_(kind), |
109 name_(name), | 95 name_(name), |
110 function_(Function::ZoneHandle(function.raw())), | 96 function_(Function::ZoneHandle(function.raw())), |
111 table_index_(table_index), | 97 table_index_(table_index), |
112 code_objects_(new ZoneGrowableArray<intptr_t>()), | 98 profile_codes_(0), |
113 exclusive_ticks_(0), | 99 exclusive_ticks_(0), |
114 inclusive_ticks_(0), | 100 inclusive_ticks_(0) { |
115 inclusive_tick_serial_(0) { | 101 ASSERT((kind_ != kDartFunction) || !function_.IsNull()); |
116 ASSERT((kind_ != kDartFunction) || !function_.IsNull()); | 102 ASSERT((kind_ != kDartFunction) || (table_index_ >= 0)); |
117 ASSERT((kind_ != kDartFunction) || (table_index_ >= 0)); | 103 ASSERT(profile_codes_.length() == 0); |
118 ASSERT(code_objects_->length() == 0); | 104 } |
119 } | 105 |
120 | 106 |
121 const char* name() const { | 107 void ProfileFunction::Tick(bool exclusive, intptr_t inclusive_serial) { |
122 ASSERT(name_ != NULL); | 108 if (exclusive) { |
123 return name_; | 109 exclusive_ticks_++; |
124 } | 110 } else { |
125 | 111 if (inclusive_serial_ == inclusive_serial) { |
126 RawFunction* function() const { | 112 // Already ticket. |
127 return function_.raw(); | 113 return; |
128 } | 114 } |
129 | 115 inclusive_serial_ = inclusive_serial; |
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 void inc_inclusive_ticks() { | |
178 inclusive_ticks_++; | 116 inclusive_ticks_++; |
179 } | 117 } |
180 intptr_t exclusive_ticks() const { | 118 } |
181 return exclusive_ticks_; | 119 |
182 } | 120 |
183 | 121 const char* ProfileFunction::KindToCString(Kind kind) { |
184 void Tick(bool exclusive, intptr_t serial) { | 122 switch (kind) { |
185 // Assert that exclusive ticks are never passed a valid serial number. | 123 case kDartFunction: |
186 ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1))); | 124 return "Dart"; |
187 if (!exclusive && (inclusive_tick_serial_ == serial)) { | 125 case kNativeFunction: |
188 // We've already given this object an inclusive tick for this sample. | 126 return "Native"; |
127 case kTagFunction: | |
128 return "Tag"; | |
129 case kStubFunction: | |
130 return "Stub"; | |
131 case kUnkownFunction: | |
132 return "Collected"; | |
133 default: | |
134 UNIMPLEMENTED(); | |
135 return ""; | |
136 } | |
137 } | |
138 | |
139 | |
140 void ProfileFunction::PrintToJSONObject(JSONObject* func) { | |
141 func->AddProperty("type", "@Function"); | |
142 func->AddProperty("name", name()); | |
143 func->AddProperty("_kind", KindToCString(kind())); | |
144 } | |
145 | |
146 | |
147 void ProfileFunction::PrintToJSONArray(JSONArray* functions) { | |
148 JSONObject obj(functions); | |
149 obj.AddProperty("kind", KindToCString(kind())); | |
150 obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); | |
151 obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); | |
152 if (kind() == kDartFunction) { | |
153 ASSERT(!function_.IsNull()); | |
154 obj.AddProperty("function", function_); | |
155 } else { | |
156 JSONObject func(&obj, "function"); | |
157 PrintToJSONObject(&func); | |
158 } | |
159 { | |
160 JSONArray codes(&obj, "codes"); | |
161 for (intptr_t i = 0; i < profile_codes_.length(); i++) { | |
162 intptr_t code_index = profile_codes_[i]; | |
163 codes.AddValue(code_index); | |
164 } | |
165 } | |
166 } | |
167 | |
168 | |
169 void ProfileFunction::AddProfileCode(intptr_t code_table_index) { | |
170 for (intptr_t i = 0; i < profile_codes_.length(); i++) { | |
171 if (profile_codes_[i] == code_table_index) { | |
189 return; | 172 return; |
190 } | 173 } |
191 if (exclusive) { | 174 } |
192 exclusive_ticks_++; | 175 profile_codes_.Add(code_table_index); |
193 } else { | 176 } |
194 inclusive_ticks_++; | 177 |
195 // Mark the last serial we ticked the inclusive count. | 178 |
196 inclusive_tick_serial_ = serial; | 179 ProfileCodeAddress::ProfileCodeAddress(uword pc) |
197 } | 180 : pc_(pc), |
198 } | 181 exclusive_ticks_(0), |
199 | 182 inclusive_ticks_(0) { |
200 void PrintToJSONObject(JSONObject* func) { | 183 } |
201 func->AddProperty("type", "@Function"); | 184 |
202 func->AddProperty("name", name()); | 185 |
203 func->AddProperty("_kind", KindToCString(kind())); | 186 void ProfileCodeAddress::Tick(bool exclusive) { |
204 } | 187 if (exclusive) { |
205 | 188 exclusive_ticks_++; |
206 void PrintToJSONArray(JSONArray* functions) { | 189 } else { |
207 JSONObject obj(functions); | 190 inclusive_ticks_++; |
208 obj.AddProperty("kind", KindToCString(kind())); | 191 } |
209 obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); | 192 } |
210 obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); | 193 |
211 if (kind() == kDartFunction) { | 194 |
212 ASSERT(!function_.IsNull()); | 195 ProfileCode::ProfileCode(Kind kind, |
213 obj.AddProperty("function", function_); | 196 uword start, |
214 } else { | 197 uword end, |
215 JSONObject func(&obj, "function"); | 198 int64_t timestamp, |
216 PrintToJSONObject(&func); | 199 const Code& code) |
217 } | 200 : kind_(kind), |
218 { | 201 start_(start), |
219 JSONArray codes(&obj, "codes"); | 202 end_(end), |
220 for (intptr_t i = 0; i < code_objects_->length(); i++) { | 203 exclusive_ticks_(0), |
221 intptr_t code_index = (*code_objects_)[i]; | 204 inclusive_ticks_(0), |
222 codes.AddValue(code_index); | 205 inclusive_serial_(-1), |
223 } | 206 code_(code), |
224 } | 207 name_(NULL), |
225 } | 208 compile_timestamp_(0), |
226 | 209 function_(NULL), |
227 private: | 210 code_table_index_(-1), |
228 const Kind kind_; | 211 address_ticks_(0) { |
229 const char* name_; | 212 } |
230 const Function& function_; | 213 |
231 const intptr_t table_index_; | 214 |
232 ZoneGrowableArray<intptr_t>* code_objects_; | 215 void ProfileCode::AdjustExtent(uword start, uword end) { |
233 intptr_t exclusive_ticks_; | 216 if (start < start_) { |
234 intptr_t inclusive_ticks_; | 217 start_ = start; |
235 intptr_t inclusive_tick_serial_; | 218 } |
236 }; | 219 if (end > end_) { |
237 | 220 end_ = end; |
238 | 221 } |
239 class ProfileFunctionTable : public ValueObject { | 222 ASSERT(start_ < end_); |
223 } | |
224 | |
225 | |
226 bool ProfileCode::Overlaps(const ProfileCode* other) const { | |
227 ASSERT(other != NULL); | |
228 return other->Contains(start_) || | |
229 other->Contains(end_ - 1) || | |
230 Contains(other->start()) || | |
231 Contains(other->end() - 1); | |
232 } | |
233 | |
234 | |
235 bool ProfileCode::IsOptimizedDart() const { | |
236 return !code_.IsNull() && code_.is_optimized(); | |
237 } | |
238 | |
239 | |
240 void ProfileCode::SetName(const char* name) { | |
241 if (name == NULL) { | |
242 name_ = NULL; | |
243 } | |
244 intptr_t len = strlen(name); | |
245 name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1); | |
246 strncpy(const_cast<char*>(name_), name, len); | |
247 const_cast<char*>(name_)[len] = '\0'; | |
248 } | |
249 | |
250 | |
251 void ProfileCode::GenerateAndSetSymbolName(const char* prefix) { | |
252 const intptr_t kBuffSize = 512; | |
253 char buff[kBuffSize]; | |
254 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", | |
255 prefix, start(), end()); | |
256 SetName(buff); | |
257 } | |
258 | |
259 | |
260 void ProfileCode::Tick(uword pc, bool exclusive, intptr_t serial) { | |
261 if (exclusive) { | |
262 exclusive_ticks_++; | |
263 } else { | |
264 if (inclusive_serial_ == serial) { | |
265 // Already ticked for this sample. | |
266 return; | |
267 } | |
268 inclusive_serial_ = serial; | |
269 inclusive_ticks_++; | |
270 } | |
271 TickAddress(pc, exclusive); | |
272 } | |
273 | |
274 | |
275 void ProfileCode::TickAddress(uword pc, bool exclusive) { | |
276 const intptr_t length = address_ticks_.length(); | |
277 | |
278 intptr_t i = 0; | |
279 for (; i < length; i++) { | |
280 ProfileCodeAddress& entry = address_ticks_[i]; | |
281 if (entry.pc() == pc) { | |
282 // Tick the address entry. | |
283 entry.Tick(exclusive); | |
284 return; | |
285 } | |
286 if (entry.pc() > pc) { | |
287 break; | |
288 } | |
289 } | |
290 | |
291 // New address, add entry. | |
292 ProfileCodeAddress entry(pc); | |
293 | |
294 entry.Tick(exclusive); | |
295 | |
296 if (i < length) { | |
297 // Insert at i. | |
298 address_ticks_.InsertAt(i, entry); | |
299 } else { | |
300 // Add to end. | |
301 address_ticks_.Add(entry); | |
302 } | |
303 } | |
304 | |
305 | |
306 void ProfileCode::PrintNativeCode(JSONObject* profile_code_obj) { | |
307 ASSERT(kind() == kNativeCode); | |
308 JSONObject obj(profile_code_obj, "code"); | |
309 obj.AddProperty("type", "@Code"); | |
310 obj.AddProperty("kind", "Native"); | |
311 obj.AddProperty("name", name()); | |
312 obj.AddProperty("_optimized", false); | |
313 obj.AddPropertyF("start", "%" Px "", start()); | |
314 obj.AddPropertyF("end", "%" Px "", end()); | |
315 { | |
316 // Generate a fake function entry. | |
317 JSONObject func(&obj, "function"); | |
318 ASSERT(function_ != NULL); | |
319 function_->PrintToJSONObject(&func); | |
320 } | |
321 } | |
322 | |
323 | |
324 void ProfileCode::PrintCollectedCode(JSONObject* profile_code_obj) { | |
325 ASSERT(kind() == kCollectedCode); | |
326 JSONObject obj(profile_code_obj, "code"); | |
327 obj.AddProperty("type", "@Code"); | |
328 obj.AddProperty("kind", "Collected"); | |
329 obj.AddProperty("name", name()); | |
330 obj.AddProperty("_optimized", false); | |
331 obj.AddPropertyF("start", "%" Px "", start()); | |
332 obj.AddPropertyF("end", "%" Px "", end()); | |
333 { | |
334 // Generate a fake function entry. | |
335 JSONObject func(&obj, "function"); | |
336 ASSERT(function_ != NULL); | |
337 function_->PrintToJSONObject(&func); | |
338 } | |
339 } | |
340 | |
341 | |
342 void ProfileCode::PrintOverwrittenCode(JSONObject* profile_code_obj) { | |
343 ASSERT(kind() == kReusedCode); | |
344 JSONObject obj(profile_code_obj, "code"); | |
345 obj.AddProperty("type", "@Code"); | |
346 obj.AddProperty("kind", "Collected"); | |
347 obj.AddProperty("name", name()); | |
348 obj.AddProperty("_optimized", false); | |
349 obj.AddPropertyF("start", "%" Px "", start()); | |
350 obj.AddPropertyF("end", "%" Px "", end()); | |
351 { | |
352 // Generate a fake function entry. | |
353 JSONObject func(&obj, "function"); | |
354 ASSERT(function_ != NULL); | |
355 function_->PrintToJSONObject(&func); | |
356 } | |
357 } | |
358 | |
359 | |
360 void ProfileCode::PrintTagCode(JSONObject* profile_code_obj) { | |
361 ASSERT(kind() == kTagCode); | |
362 JSONObject obj(profile_code_obj, "code"); | |
363 obj.AddProperty("type", "@Code"); | |
364 obj.AddProperty("kind", "Tag"); | |
365 obj.AddProperty("name", name()); | |
366 obj.AddPropertyF("start", "%" Px "", start()); | |
367 obj.AddPropertyF("end", "%" Px "", end()); | |
368 obj.AddProperty("_optimized", false); | |
369 { | |
370 // Generate a fake function entry. | |
371 JSONObject func(&obj, "function"); | |
372 ASSERT(function_ != NULL); | |
373 function_->PrintToJSONObject(&func); | |
374 } | |
375 } | |
376 | |
377 | |
378 const char* ProfileCode::KindToCString(Kind kind) { | |
379 switch (kind) { | |
380 case kDartCode: | |
381 return "Dart"; | |
382 case kCollectedCode: | |
383 return "Collected"; | |
384 case kNativeCode: | |
385 return "Native"; | |
386 case kReusedCode: | |
387 return "Overwritten"; | |
388 case kTagCode: | |
389 return "Tag"; | |
390 } | |
391 UNREACHABLE(); | |
392 return NULL; | |
393 } | |
394 | |
395 | |
396 void ProfileCode::PrintToJSONArray(JSONArray* codes) { | |
397 JSONObject obj(codes); | |
398 obj.AddProperty("kind", ProfileCode::KindToCString(kind())); | |
399 obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); | |
400 obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); | |
401 if (kind() == kDartCode) { | |
402 ASSERT(!code_.IsNull()); | |
403 obj.AddProperty("code", code_); | |
404 } else if (kind() == kCollectedCode) { | |
405 PrintCollectedCode(&obj); | |
406 } else if (kind() == kReusedCode) { | |
407 PrintOverwrittenCode(&obj); | |
408 } else if (kind() == kTagCode) { | |
409 PrintTagCode(&obj); | |
410 } else { | |
411 ASSERT(kind() == kNativeCode); | |
412 PrintNativeCode(&obj); | |
413 } | |
414 { | |
415 JSONArray ticks(&obj, "ticks"); | |
416 for (intptr_t i = 0; i < address_ticks_.length(); i++) { | |
417 const ProfileCodeAddress& entry = address_ticks_[i]; | |
418 ticks.AddValueF("%" Px "", entry.pc()); | |
419 ticks.AddValueF("%" Pd "", entry.exclusive_ticks()); | |
420 ticks.AddValueF("%" Pd "", entry.inclusive_ticks()); | |
421 } | |
422 } | |
423 } | |
424 | |
425 | |
426 class ProfileFunctionTable : public ZoneAllocated { | |
240 public: | 427 public: |
241 ProfileFunctionTable() | 428 ProfileFunctionTable() |
242 : null_function_(Function::ZoneHandle()), | 429 : null_function_(Function::ZoneHandle()), |
243 table_(new ZoneGrowableArray<ProfileFunction*>()), | 430 table_(8), |
244 unknown_function_(NULL) { | 431 unknown_function_(NULL) { |
432 unknown_function_ = Add(ProfileFunction::kUnkownFunction, | |
433 "<unknown Dart function>"); | |
245 } | 434 } |
246 | 435 |
247 ProfileFunction* LookupOrAdd(const Function& function) { | 436 ProfileFunction* LookupOrAdd(const Function& function) { |
248 ASSERT(!function.IsNull()); | 437 ASSERT(!function.IsNull()); |
249 ProfileFunction* profile_function = Lookup(function); | 438 ProfileFunction* profile_function = Lookup(function); |
250 if (profile_function != NULL) { | 439 if (profile_function != NULL) { |
251 return profile_function; | 440 return profile_function; |
252 } | 441 } |
253 return Add(function); | 442 return Add(function); |
254 } | 443 } |
255 | 444 |
256 intptr_t LookupIndex(const Function& function) { | 445 intptr_t LookupIndex(const Function& function) { |
257 ASSERT(!function.IsNull()); | 446 ASSERT(!function.IsNull()); |
258 for (intptr_t i = 0; i < table_->length(); i++) { | 447 for (intptr_t i = 0; i < table_.length(); i++) { |
259 ProfileFunction* profile_function = (*table_)[i]; | 448 ProfileFunction* profile_function = table_[i]; |
260 if (profile_function->function() == function.raw()) { | 449 if (profile_function->function() == function.raw()) { |
261 return i; | 450 return i; |
262 } | 451 } |
263 } | 452 } |
264 return -1; | 453 return -1; |
265 } | 454 } |
266 | 455 |
267 ProfileFunction* GetUnknown() { | 456 ProfileFunction* GetUnknown() { |
268 if (unknown_function_ == NULL) { | |
269 // Construct. | |
270 unknown_function_ = Add(ProfileFunction::kUnkownFunction, | |
271 "<unknown Dart function>"); | |
272 } | |
273 ASSERT(unknown_function_ != NULL); | 457 ASSERT(unknown_function_ != NULL); |
274 return unknown_function_; | 458 return unknown_function_; |
275 } | 459 } |
276 | 460 |
277 // No protection against being called more than once for the same tag_id. | 461 // No protection against being called more than once for the same tag_id. |
278 ProfileFunction* AddTag(uword tag_id, const char* name) { | 462 ProfileFunction* AddTag(uword tag_id, const char* name) { |
279 // TODO(johnmccutchan): Canonicalize ProfileFunctions for tags. | 463 // TODO(johnmccutchan): Canonicalize ProfileFunctions for tags. |
280 return Add(ProfileFunction::kTagFunction, name); | 464 return Add(ProfileFunction::kTagFunction, name); |
281 } | 465 } |
282 | 466 |
283 // No protection against being called more than once for the same native | 467 // No protection against being called more than once for the same native |
284 // address. | 468 // address. |
285 ProfileFunction* AddNative(uword start_address, const char* name) { | 469 ProfileFunction* AddNative(uword start_address, const char* name) { |
286 // TODO(johnmccutchan): Canonicalize ProfileFunctions for natives. | 470 // TODO(johnmccutchan): Canonicalize ProfileFunctions for natives. |
287 return Add(ProfileFunction::kNativeFunction, name); | 471 return Add(ProfileFunction::kNativeFunction, name); |
288 } | 472 } |
289 | 473 |
290 // No protection against being called more tha once for the same stub. | 474 // No protection against being called more tha once for the same stub. |
291 ProfileFunction* AddStub(uword start_address, const char* name) { | 475 ProfileFunction* AddStub(uword start_address, const char* name) { |
292 return Add(ProfileFunction::kStubFunction, name); | 476 return Add(ProfileFunction::kStubFunction, name); |
293 } | 477 } |
294 | 478 |
295 intptr_t Length() const { | 479 intptr_t length() const { |
296 return table_->length(); | 480 return table_.length(); |
297 } | 481 } |
298 | 482 |
299 ProfileFunction* At(intptr_t i) const { | 483 ProfileFunction* At(intptr_t i) const { |
300 ASSERT(i >= 0); | 484 ASSERT(i >= 0); |
301 ASSERT(i < Length()); | 485 ASSERT(i < length()); |
302 return (*table_)[i]; | 486 return table_[i]; |
303 } | 487 } |
304 | 488 |
305 private: | 489 private: |
306 ProfileFunction* Add(ProfileFunction::Kind kind, const char* name) { | 490 ProfileFunction* Add(ProfileFunction::Kind kind, const char* name) { |
307 ASSERT(kind != ProfileFunction::kDartFunction); | 491 ASSERT(kind != ProfileFunction::kDartFunction); |
308 ASSERT(name != NULL); | 492 ASSERT(name != NULL); |
309 ProfileFunction* profile_function = | 493 ProfileFunction* profile_function = |
310 new ProfileFunction(kind, | 494 new ProfileFunction(kind, |
311 name, | 495 name, |
312 null_function_, | 496 null_function_, |
313 table_->length()); | 497 table_.length()); |
314 table_->Add(profile_function); | 498 table_.Add(profile_function); |
315 return profile_function; | 499 return profile_function; |
316 } | 500 } |
317 | 501 |
318 ProfileFunction* Add(const Function& function) { | 502 ProfileFunction* Add(const Function& function) { |
319 ASSERT(Lookup(function) == NULL); | 503 ASSERT(Lookup(function) == NULL); |
320 ProfileFunction* profile_function = | 504 ProfileFunction* profile_function = |
321 new ProfileFunction(ProfileFunction::kDartFunction, | 505 new ProfileFunction(ProfileFunction::kDartFunction, |
322 NULL, | 506 NULL, |
323 function, | 507 function, |
324 table_->length()); | 508 table_.length()); |
325 table_->Add(profile_function); | 509 table_.Add(profile_function); |
326 return profile_function; | 510 return profile_function; |
327 } | 511 } |
328 | 512 |
329 ProfileFunction* Lookup(const Function& function) { | 513 ProfileFunction* Lookup(const Function& function) { |
330 ASSERT(!function.IsNull()); | 514 ASSERT(!function.IsNull()); |
331 intptr_t index = LookupIndex(function); | 515 intptr_t index = LookupIndex(function); |
332 if (index == -1) { | 516 if (index == -1) { |
333 return NULL; | 517 return NULL; |
334 } | 518 } |
335 return (*table_)[index]; | 519 return table_[index]; |
336 } | 520 } |
337 | 521 |
338 const Function& null_function_; | 522 const Function& null_function_; |
339 ZoneGrowableArray<ProfileFunction*>* table_; | 523 ZoneGrowableArray<ProfileFunction*> table_; |
340 | |
341 ProfileFunction* unknown_function_; | 524 ProfileFunction* unknown_function_; |
342 }; | 525 }; |
343 | 526 |
344 | 527 |
345 struct AddressEntry { | 528 ProfileFunction* ProfileCode::SetFunctionAndName(ProfileFunctionTable* table) { |
346 uword pc; | 529 ASSERT(function_ == NULL); |
347 intptr_t exclusive_ticks; | |
348 intptr_t inclusive_ticks; | |
349 | 530 |
350 void tick(bool exclusive) { | 531 ProfileFunction* function = NULL; |
351 if (exclusive) { | 532 if ((kind() == kReusedCode) || (kind() == kCollectedCode)) { |
352 exclusive_ticks++; | 533 if (name() == NULL) { |
534 // Lazily set generated name. | |
535 GenerateAndSetSymbolName("[Collected]"); | |
536 } | |
537 // Map these to a canonical unknown function. | |
538 function = table->GetUnknown(); | |
539 } else if (kind() == kDartCode) { | |
540 ASSERT(!code_.IsNull()); | |
541 const Object& obj = Object::Handle(code_.owner()); | |
542 if (obj.IsFunction()) { | |
543 const String& user_name = String::Handle(code_.PrettyName()); | |
544 function = table->LookupOrAdd(Function::Cast(obj)); | |
545 SetName(user_name.ToCString()); | |
353 } else { | 546 } else { |
354 inclusive_ticks++; | 547 // A stub. |
548 const String& user_name = String::Handle(code_.PrettyName()); | |
549 function = table->AddStub(start(), user_name.ToCString()); | |
550 SetName(user_name.ToCString()); | |
355 } | 551 } |
552 } else if (kind() == kNativeCode) { | |
553 if (name() == NULL) { | |
554 // Lazily set generated name. | |
555 GenerateAndSetSymbolName("[Native]"); | |
556 } | |
557 function = table->AddNative(start(), name()); | |
558 } else if (kind() == kTagCode) { | |
559 if (name() == NULL) { | |
560 if (UserTags::IsUserTag(start())) { | |
561 const char* tag_name = UserTags::TagName(start()); | |
562 ASSERT(tag_name != NULL); | |
563 SetName(tag_name); | |
564 } else if (VMTag::IsVMTag(start()) || | |
565 VMTag::IsRuntimeEntryTag(start()) || | |
566 VMTag::IsNativeEntryTag(start())) { | |
567 const char* tag_name = VMTag::TagName(start()); | |
568 ASSERT(tag_name != NULL); | |
569 SetName(tag_name); | |
570 } else { | |
571 if (start() == VMTag::kRootTagId) { | |
572 SetName("Root"); | |
573 } else { | |
574 ASSERT(start() == VMTag::kTruncatedTagId); | |
575 SetName("[Truncated]"); | |
576 } | |
577 } | |
578 } | |
579 function = table->AddTag(start(), name()); | |
580 } else { | |
581 UNREACHABLE(); | |
356 } | 582 } |
357 }; | 583 ASSERT(function != NULL); |
358 | 584 |
359 typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); | 585 function->AddProfileCode(code_table_index()); |
360 | 586 |
361 // A contiguous address region that holds code. Each CodeRegion has a "kind" | 587 function_ = function; |
362 // which describes the type of code contained inside the region. Each | 588 return function_; |
363 // region covers the following interval: [start, end). | 589 } |
364 class CodeRegion : public ZoneAllocated { | 590 |
591 | |
592 typedef bool (*RangeCompare)(uword pc, uword region_start, uword region_end); | |
593 | |
594 class ProfileCodeTable : public ZoneAllocated { | |
365 public: | 595 public: |
366 enum Kind { | 596 ProfileCodeTable() |
367 kDartCode, // Live Dart code. | 597 : table_(8) { |
368 kCollectedCode, // Dead Dart code. | |
369 kNativeCode, // Native code. | |
370 kReusedCode, // Dead Dart code that has been reused by new kDartCode. | |
371 kTagCode, // A special kind of code representing a tag. | |
372 }; | |
373 | |
374 CodeRegion(Kind kind, | |
375 uword start, | |
376 uword end, | |
377 int64_t timestamp, | |
378 const Code& code) | |
379 : kind_(kind), | |
380 start_(start), | |
381 end_(end), | |
382 inclusive_ticks_(0), | |
383 exclusive_ticks_(0), | |
384 inclusive_tick_serial_(0), | |
385 name_(NULL), | |
386 compile_timestamp_(timestamp), | |
387 code_(Code::ZoneHandle(code.raw())), | |
388 profile_function_(NULL), | |
389 code_table_index_(-1) { | |
390 ASSERT(start_ < end_); | |
391 // Ensure all kDartCode have a valid code_ object. | |
392 ASSERT((kind != kDartCode) || (!code_.IsNull())); | |
393 } | 598 } |
394 | 599 |
395 uword start() const { return start_; } | 600 intptr_t length() const { return table_.length(); } |
396 void set_start(uword start) { | 601 |
397 start_ = start; | 602 ProfileCode* At(intptr_t index) const { |
603 ASSERT(index >= 0); | |
604 ASSERT(index < length()); | |
605 return table_[index]; | |
398 } | 606 } |
399 | 607 |
400 uword end() const { return end_; } | 608 // Find the table index to the ProfileCode containing pc. |
401 void set_end(uword end) { | |
402 end_ = end; | |
403 } | |
404 | |
405 void AdjustExtent(uword start, uword end) { | |
406 if (start < start_) { | |
407 start_ = start; | |
408 } | |
409 if (end > end_) { | |
410 end_ = end; | |
411 } | |
412 ASSERT(start_ < end_); | |
413 } | |
414 | |
415 bool contains(uword pc) const { | |
416 return (pc >= start_) && (pc < end_); | |
417 } | |
418 | |
419 bool overlaps(const CodeRegion* other) const { | |
420 ASSERT(other != NULL); | |
421 return other->contains(start_) || | |
422 other->contains(end_ - 1) || | |
423 contains(other->start()) || | |
424 contains(other->end() - 1); | |
425 } | |
426 | |
427 int64_t compile_timestamp() const { return compile_timestamp_; } | |
428 void set_compile_timestamp(int64_t timestamp) { | |
429 compile_timestamp_ = timestamp; | |
430 } | |
431 | |
432 intptr_t inclusive_ticks() const { return inclusive_ticks_; } | |
433 void set_inclusive_ticks(intptr_t inclusive_ticks) { | |
434 inclusive_ticks_ = inclusive_ticks; | |
435 } | |
436 void inc_inclusive_ticks() { | |
437 inclusive_ticks_++; | |
438 } | |
439 | |
440 intptr_t exclusive_ticks() const { return exclusive_ticks_; } | |
441 void set_exclusive_ticks(intptr_t exclusive_ticks) { | |
442 exclusive_ticks_ = exclusive_ticks; | |
443 } | |
444 | |
445 const char* name() const { return name_; } | |
446 void SetName(const char* name) { | |
447 if (name == NULL) { | |
448 name_ = NULL; | |
449 } | |
450 intptr_t len = strlen(name); | |
451 name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1); | |
452 strncpy(const_cast<char*>(name_), name, len); | |
453 const_cast<char*>(name_)[len] = '\0'; | |
454 } | |
455 | |
456 bool IsOptimizedDart() const { | |
457 return !code_.IsNull() && code_.is_optimized(); | |
458 } | |
459 | |
460 RawCode* code() const { | |
461 return code_.raw(); | |
462 } | |
463 | |
464 ProfileFunction* SetFunctionAndName(ProfileFunctionTable* table) { | |
465 ASSERT(profile_function_ == NULL); | |
466 | |
467 ProfileFunction* function = NULL; | |
468 if ((kind() == kReusedCode) || (kind() == kCollectedCode)) { | |
469 if (name() == NULL) { | |
470 // Lazily set generated name. | |
471 GenerateAndSetSymbolName("[Collected]"); | |
472 } | |
473 // Map these to a canonical unknown function. | |
474 function = table->GetUnknown(); | |
475 } else if (kind() == kDartCode) { | |
476 ASSERT(!code_.IsNull()); | |
477 const Object& obj = Object::Handle(code_.owner()); | |
478 if (obj.IsFunction()) { | |
479 const String& user_name = String::Handle(code_.PrettyName()); | |
480 function = table->LookupOrAdd(Function::Cast(obj)); | |
481 SetName(user_name.ToCString()); | |
482 } else { | |
483 // A stub. | |
484 const String& user_name = String::Handle(code_.PrettyName()); | |
485 function = table->AddStub(start(), user_name.ToCString()); | |
486 SetName(user_name.ToCString()); | |
487 } | |
488 } else if (kind() == kNativeCode) { | |
489 if (name() == NULL) { | |
490 // Lazily set generated name. | |
491 GenerateAndSetSymbolName("[Native]"); | |
492 } | |
493 function = table->AddNative(start(), name()); | |
494 } else if (kind() == kTagCode) { | |
495 if (name() == NULL) { | |
496 if (UserTags::IsUserTag(start())) { | |
497 const char* tag_name = UserTags::TagName(start()); | |
498 ASSERT(tag_name != NULL); | |
499 SetName(tag_name); | |
500 } else if (VMTag::IsVMTag(start()) || | |
501 VMTag::IsRuntimeEntryTag(start()) || | |
502 VMTag::IsNativeEntryTag(start())) { | |
503 const char* tag_name = VMTag::TagName(start()); | |
504 ASSERT(tag_name != NULL); | |
505 SetName(tag_name); | |
506 } else { | |
507 if (start() == VMTag::kRootTagId) { | |
508 SetName("Root"); | |
509 } else { | |
510 ASSERT(start() == VMTag::kTruncatedTagId); | |
511 SetName("[Truncated]"); | |
512 } | |
513 } | |
514 } | |
515 function = table->AddTag(start(), name()); | |
516 } else { | |
517 UNREACHABLE(); | |
518 } | |
519 ASSERT(function != NULL); | |
520 // Register this CodeRegion with this function. | |
521 function->AddCodeObjectIndex(code_table_index()); | |
522 profile_function_ = function; | |
523 return profile_function_; | |
524 } | |
525 | |
526 ProfileFunction* function() const { | |
527 ASSERT(profile_function_ != NULL); | |
528 return profile_function_; | |
529 } | |
530 | |
531 void set_code_table_index(intptr_t code_table_index) { | |
532 ASSERT(code_table_index_ == -1); | |
533 ASSERT(code_table_index != -1); | |
534 code_table_index_ = code_table_index; | |
535 } | |
536 intptr_t code_table_index() const { | |
537 ASSERT(code_table_index_ != -1); | |
538 return code_table_index_; | |
539 } | |
540 | |
541 Kind kind() const { return kind_; } | |
542 | |
543 static const char* KindToCString(Kind kind) { | |
544 switch (kind) { | |
545 case kDartCode: | |
546 return "Dart"; | |
547 case kCollectedCode: | |
548 return "Collected"; | |
549 case kNativeCode: | |
550 return "Native"; | |
551 case kReusedCode: | |
552 return "Overwritten"; | |
553 case kTagCode: | |
554 return "Tag"; | |
555 } | |
556 UNREACHABLE(); | |
557 return NULL; | |
558 } | |
559 | |
560 void DebugPrint() const { | |
561 OS::Print("%s [%" Px ", %" Px ") %" Pd64 "\n", | |
562 KindToCString(kind_), | |
563 start(), | |
564 end(), | |
565 compile_timestamp_); | |
566 } | |
567 | |
568 void Tick(uword pc, bool exclusive, intptr_t serial) { | |
569 // Assert that exclusive ticks are never passed a valid serial number. | |
570 ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1))); | |
571 if (!exclusive && (inclusive_tick_serial_ == serial)) { | |
572 // We've already given this code object an inclusive tick for this sample. | |
573 return; | |
574 } | |
575 // Tick the code object. | |
576 if (exclusive) { | |
577 exclusive_ticks_++; | |
578 } else { | |
579 inclusive_ticks_++; | |
580 // Mark the last serial we ticked the inclusive count. | |
581 inclusive_tick_serial_ = serial; | |
582 } | |
583 TickAddress(pc, exclusive); | |
584 } | |
585 | |
586 void PrintNativeCode(JSONObject* profile_code_obj) { | |
587 ASSERT(kind() == kNativeCode); | |
588 JSONObject obj(profile_code_obj, "code"); | |
589 obj.AddProperty("type", "@Code"); | |
590 obj.AddProperty("kind", "Native"); | |
591 obj.AddProperty("name", name()); | |
592 obj.AddProperty("_optimized", false); | |
593 obj.AddPropertyF("start", "%" Px "", start()); | |
594 obj.AddPropertyF("end", "%" Px "", end()); | |
595 { | |
596 // Generate a fake function entry. | |
597 JSONObject func(&obj, "function"); | |
598 profile_function_->PrintToJSONObject(&func); | |
599 } | |
600 } | |
601 | |
602 void PrintCollectedCode(JSONObject* profile_code_obj) { | |
603 ASSERT(kind() == kCollectedCode); | |
604 JSONObject obj(profile_code_obj, "code"); | |
605 obj.AddProperty("type", "@Code"); | |
606 obj.AddProperty("kind", "Collected"); | |
607 obj.AddProperty("name", name()); | |
608 obj.AddProperty("_optimized", false); | |
609 obj.AddPropertyF("start", "%" Px "", start()); | |
610 obj.AddPropertyF("end", "%" Px "", end()); | |
611 { | |
612 // Generate a fake function entry. | |
613 JSONObject func(&obj, "function"); | |
614 profile_function_->PrintToJSONObject(&func); | |
615 } | |
616 } | |
617 | |
618 void PrintOverwrittenCode(JSONObject* profile_code_obj) { | |
619 ASSERT(kind() == kReusedCode); | |
620 JSONObject obj(profile_code_obj, "code"); | |
621 obj.AddProperty("type", "@Code"); | |
622 obj.AddProperty("kind", "Collected"); | |
623 obj.AddProperty("name", name()); | |
624 obj.AddProperty("_optimized", false); | |
625 obj.AddPropertyF("start", "%" Px "", start()); | |
626 obj.AddPropertyF("end", "%" Px "", end()); | |
627 { | |
628 // Generate a fake function entry. | |
629 JSONObject func(&obj, "function"); | |
630 ASSERT(profile_function_ != NULL); | |
631 profile_function_->PrintToJSONObject(&func); | |
632 } | |
633 } | |
634 | |
635 void PrintTagCode(JSONObject* profile_code_obj) { | |
636 ASSERT(kind() == kTagCode); | |
637 JSONObject obj(profile_code_obj, "code"); | |
638 obj.AddProperty("type", "@Code"); | |
639 obj.AddProperty("kind", "Tag"); | |
640 obj.AddProperty("name", name()); | |
641 obj.AddPropertyF("start", "%" Px "", start()); | |
642 obj.AddPropertyF("end", "%" Px "", end()); | |
643 obj.AddProperty("_optimized", false); | |
644 { | |
645 // Generate a fake function entry. | |
646 JSONObject func(&obj, "function"); | |
647 ASSERT(profile_function_ != NULL); | |
648 profile_function_->PrintToJSONObject(&func); | |
649 } | |
650 } | |
651 | |
652 void PrintToJSONArray(JSONArray* codes) { | |
653 JSONObject obj(codes); | |
654 obj.AddProperty("kind", KindToCString(kind())); | |
655 obj.AddPropertyF("inclusiveTicks", "%" Pd "", inclusive_ticks()); | |
656 obj.AddPropertyF("exclusiveTicks", "%" Pd "", exclusive_ticks()); | |
657 if (kind() == kDartCode) { | |
658 ASSERT(!code_.IsNull()); | |
659 obj.AddProperty("code", code_); | |
660 } else if (kind() == kCollectedCode) { | |
661 PrintCollectedCode(&obj); | |
662 } else if (kind() == kReusedCode) { | |
663 PrintOverwrittenCode(&obj); | |
664 } else if (kind() == kTagCode) { | |
665 PrintTagCode(&obj); | |
666 } else { | |
667 ASSERT(kind() == kNativeCode); | |
668 PrintNativeCode(&obj); | |
669 } | |
670 { | |
671 JSONArray ticks(&obj, "ticks"); | |
672 for (intptr_t i = 0; i < address_table_.length(); i++) { | |
673 const AddressEntry& entry = address_table_[i]; | |
674 ticks.AddValueF("%" Px "", entry.pc); | |
675 ticks.AddValueF("%" Pd "", entry.exclusive_ticks); | |
676 ticks.AddValueF("%" Pd "", entry.inclusive_ticks); | |
677 } | |
678 } | |
679 } | |
680 | |
681 private: | |
682 void TickAddress(uword pc, bool exclusive) { | |
683 const intptr_t length = address_table_.length(); | |
684 intptr_t i = 0; | |
685 for (; i < length; i++) { | |
686 AddressEntry& entry = address_table_[i]; | |
687 if (entry.pc == pc) { | |
688 // Tick the address entry. | |
689 entry.tick(exclusive); | |
690 return; | |
691 } | |
692 if (entry.pc > pc) { | |
693 break; | |
694 } | |
695 } | |
696 // New address, add entry. | |
697 AddressEntry entry; | |
698 entry.pc = pc; | |
699 entry.exclusive_ticks = 0; | |
700 entry.inclusive_ticks = 0; | |
701 entry.tick(exclusive); | |
702 if (i < length) { | |
703 // Insert at i. | |
704 address_table_.InsertAt(i, entry); | |
705 } else { | |
706 // Add to end. | |
707 address_table_.Add(entry); | |
708 } | |
709 } | |
710 | |
711 void GenerateAndSetSymbolName(const char* prefix) { | |
712 const intptr_t kBuffSize = 512; | |
713 char buff[kBuffSize]; | |
714 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", | |
715 prefix, start(), end()); | |
716 SetName(buff); | |
717 } | |
718 | |
719 // CodeRegion kind. | |
720 const Kind kind_; | |
721 // CodeRegion start address. | |
722 uword start_; | |
723 // CodeRegion end address. | |
724 uword end_; | |
725 // Inclusive ticks. | |
726 intptr_t inclusive_ticks_; | |
727 // Exclusive ticks. | |
728 intptr_t exclusive_ticks_; | |
729 // Inclusive tick serial number, ensures that each CodeRegion is only given | |
730 // a single inclusive tick per sample. | |
731 intptr_t inclusive_tick_serial_; | |
732 // Name of code region. | |
733 const char* name_; | |
734 // The compilation timestamp associated with this code region. | |
735 int64_t compile_timestamp_; | |
736 // Dart code object (may be null). | |
737 const Code& code_; | |
738 // Pointer to ProfileFunction. | |
739 ProfileFunction* profile_function_; | |
740 // Final code table index. | |
741 intptr_t code_table_index_; | |
742 ZoneGrowableArray<AddressEntry> address_table_; | |
743 DISALLOW_COPY_AND_ASSIGN(CodeRegion); | |
744 }; | |
745 | |
746 | |
747 // A sorted table of CodeRegions. Does not allow for overlap. | |
748 class CodeRegionTable : public ValueObject { | |
749 public: | |
750 enum TickResult { | |
751 kTicked = 0, // CodeRegion found and ticked. | |
752 kNotFound = -1, // No CodeRegion found. | |
753 kNewerCode = -2, // CodeRegion found but it was compiled after sample. | |
754 }; | |
755 | |
756 CodeRegionTable() : | |
757 code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) { | |
758 } | |
759 | |
760 // Ticks the CodeRegion containing pc if it is alive at timestamp. | |
761 TickResult Tick(uword pc, | |
762 bool exclusive, | |
763 intptr_t serial, | |
764 int64_t timestamp) { | |
765 intptr_t index = FindIndex(pc); | |
766 if (index < 0) { | |
767 // Not found. | |
768 return kNotFound; | |
769 } | |
770 ASSERT(index < code_region_table_->length()); | |
771 CodeRegion* region = At(index); | |
772 if (region->compile_timestamp() > timestamp) { | |
773 // Compiled after tick. | |
774 return kNewerCode; | |
775 } | |
776 region->Tick(pc, exclusive, serial); | |
777 return kTicked; | |
778 } | |
779 | |
780 // Table length. | |
781 intptr_t Length() const { return code_region_table_->length(); } | |
782 | |
783 // Get the CodeRegion at index. | |
784 CodeRegion* At(intptr_t index) const { | |
785 return (*code_region_table_)[index]; | |
786 } | |
787 | |
788 // Find the table index to the CodeRegion containing pc. | |
789 // Returns < 0 if not found. | 609 // Returns < 0 if not found. |
790 intptr_t FindIndex(uword pc) const { | 610 intptr_t FindCodeIndexForPC(uword pc) const { |
791 intptr_t index = FindRegionIndex(pc, &CompareLowerBound); | 611 intptr_t index = FindCodeIndex(pc, &CompareLowerBound); |
792 const CodeRegion* code_region = NULL; | 612 if (index == length()) { |
793 if (index == code_region_table_->length()) { | |
794 // Not present. | 613 // Not present. |
795 return -1; | 614 return -1; |
796 } | 615 } |
797 code_region = At(index); | 616 const ProfileCode* code = At(index); |
798 if (code_region->contains(pc)) { | 617 if (!code->Contains(pc)) { |
799 // Found at index. | 618 // Not present. |
800 return index; | 619 return -1; |
801 } | 620 } |
802 return -2; | 621 // Found at index. |
622 return index; | |
803 } | 623 } |
804 | 624 |
805 // Insert code_region into the table. Returns the table index where the | 625 ProfileCode* FindCodeForPC(uword pc) const { |
806 // CodeRegion was inserted. Will merge with an overlapping CodeRegion if | 626 intptr_t index = FindCodeIndexForPC(pc); |
807 // one is present. | 627 if (index < 0) { |
808 intptr_t InsertCodeRegion(CodeRegion* code_region) { | 628 return NULL; |
809 const uword start = code_region->start(); | 629 } |
810 const uword end = code_region->end(); | 630 return At(index); |
811 const intptr_t length = code_region_table_->length(); | 631 } |
632 | |
633 // Insert |new_code| into the table. Returns the table index where |new_code| | |
634 // was inserted. Will merge with an overlapping ProfileCode if one is present. | |
635 intptr_t InsertCode(ProfileCode* new_code) { | |
636 const uword start = new_code->start(); | |
637 const uword end = new_code->end(); | |
638 const intptr_t length = table_.length(); | |
812 if (length == 0) { | 639 if (length == 0) { |
813 code_region_table_->Add(code_region); | 640 table_.Add(new_code); |
814 return length; | 641 return length; |
815 } | 642 } |
816 // Determine the correct place to insert or merge code_region into table. | 643 // Determine the correct place to insert or merge |new_code| into table. |
817 intptr_t lo = FindRegionIndex(start, &CompareLowerBound); | 644 intptr_t lo = FindCodeIndex(start, &CompareLowerBound); |
818 intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound); | 645 intptr_t hi = FindCodeIndex(end - 1, &CompareUpperBound); |
819 // TODO(johnmccutchan): Simplify below logic. | 646 // TODO(johnmccutchan): Simplify below logic. |
820 if ((lo == length) && (hi == length)) { | 647 if ((lo == length) && (hi == length)) { |
821 lo = length - 1; | 648 lo = length - 1; |
822 } | 649 } |
823 if (lo == length) { | 650 if (lo == length) { |
824 CodeRegion* region = At(hi); | 651 ProfileCode* code = At(hi); |
825 if (region->overlaps(code_region)) { | 652 if (code->Overlaps(new_code)) { |
826 HandleOverlap(region, code_region, start, end); | 653 HandleOverlap(code, new_code, start, end); |
827 return hi; | 654 return hi; |
828 } | 655 } |
829 code_region_table_->Add(code_region); | 656 table_.Add(new_code); |
830 return length; | 657 return length; |
831 } else if (hi == length) { | 658 } else if (hi == length) { |
832 CodeRegion* region = At(lo); | 659 ProfileCode* code = At(lo); |
833 if (region->overlaps(code_region)) { | 660 if (code->Overlaps(new_code)) { |
834 HandleOverlap(region, code_region, start, end); | 661 HandleOverlap(code, new_code, start, end); |
835 return lo; | 662 return lo; |
836 } | 663 } |
837 code_region_table_->Add(code_region); | 664 table_.Add(new_code); |
838 return length; | 665 return length; |
839 } else if (lo == hi) { | 666 } else if (lo == hi) { |
840 CodeRegion* region = At(lo); | 667 ProfileCode* code = At(lo); |
841 if (region->overlaps(code_region)) { | 668 if (code->Overlaps(new_code)) { |
842 HandleOverlap(region, code_region, start, end); | 669 HandleOverlap(code, new_code, start, end); |
843 return lo; | 670 return lo; |
844 } | 671 } |
845 code_region_table_->InsertAt(lo, code_region); | 672 table_.InsertAt(lo, new_code); |
846 return lo; | 673 return lo; |
847 } else { | 674 } else { |
848 CodeRegion* region = At(lo); | 675 ProfileCode* code = At(lo); |
849 if (region->overlaps(code_region)) { | 676 if (code->Overlaps(new_code)) { |
850 HandleOverlap(region, code_region, start, end); | 677 HandleOverlap(code, new_code, start, end); |
851 return lo; | 678 return lo; |
852 } | 679 } |
853 region = At(hi); | 680 code = At(hi); |
854 if (region->overlaps(code_region)) { | 681 if (code->Overlaps(new_code)) { |
855 HandleOverlap(region, code_region, start, end); | 682 HandleOverlap(code, new_code, start, end); |
856 return hi; | 683 return hi; |
857 } | 684 } |
858 code_region_table_->InsertAt(hi, code_region); | 685 table_.InsertAt(hi, new_code); |
859 return hi; | 686 return hi; |
860 } | 687 } |
861 UNREACHABLE(); | 688 UNREACHABLE(); |
862 } | 689 } |
863 | 690 |
864 void Verify() { | |
865 VerifyOrder(); | |
866 VerifyOverlap(); | |
867 } | |
868 | |
869 void DebugPrint() { | |
870 OS::Print("Dumping CodeRegionTable:\n"); | |
871 for (intptr_t i = 0; i < code_region_table_->length(); i++) { | |
872 CodeRegion* region = At(i); | |
873 region->DebugPrint(); | |
874 } | |
875 } | |
876 | |
877 private: | 691 private: |
878 intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const { | 692 intptr_t FindCodeIndex(uword pc, RangeCompare comparator) const { |
879 ASSERT(comparator != NULL); | 693 ASSERT(comparator != NULL); |
880 intptr_t count = code_region_table_->length(); | 694 intptr_t count = table_.length(); |
881 intptr_t first = 0; | 695 intptr_t first = 0; |
882 while (count > 0) { | 696 while (count > 0) { |
883 intptr_t it = first; | 697 intptr_t it = first; |
884 intptr_t step = count / 2; | 698 intptr_t step = count / 2; |
885 it += step; | 699 it += step; |
886 const CodeRegion* code_region = At(it); | 700 const ProfileCode* code = At(it); |
887 if (comparator(pc, code_region->start(), code_region->end())) { | 701 if (comparator(pc, code->start(), code->end())) { |
888 first = ++it; | 702 first = ++it; |
889 count -= (step + 1); | 703 count -= (step + 1); |
890 } else { | 704 } else { |
891 count = step; | 705 count = step; |
892 } | 706 } |
893 } | 707 } |
894 return first; | 708 return first; |
895 } | 709 } |
896 | 710 |
897 static bool CompareUpperBound(uword pc, uword start, uword end) { | 711 static bool CompareUpperBound(uword pc, uword start, uword end) { |
898 return pc >= end; | 712 return pc >= end; |
899 } | 713 } |
900 | 714 |
901 static bool CompareLowerBound(uword pc, uword start, uword end) { | 715 static bool CompareLowerBound(uword pc, uword start, uword end) { |
902 return end <= pc; | 716 return end <= pc; |
903 } | 717 } |
904 | 718 |
905 void HandleOverlap(CodeRegion* region, CodeRegion* code_region, | 719 void HandleOverlap(ProfileCode* existing, ProfileCode* code, |
906 uword start, uword end) { | 720 uword start, uword end) { |
907 // We should never see overlapping Dart code regions. | 721 // We should never see overlapping Dart code regions. |
908 ASSERT(region->kind() != CodeRegion::kDartCode); | 722 ASSERT(existing->kind() != ProfileCode::kDartCode); |
909 // We should never see overlapping Tag code regions. | 723 // We should never see overlapping Tag code regions. |
910 ASSERT(region->kind() != CodeRegion::kTagCode); | 724 ASSERT(existing->kind() != ProfileCode::kTagCode); |
911 // When code regions overlap, they should be of the same kind. | 725 // When code regions overlap, they should be of the same kind. |
912 ASSERT(region->kind() == code_region->kind()); | 726 ASSERT(existing->kind() == code->kind()); |
913 region->AdjustExtent(start, end); | 727 existing->AdjustExtent(start, end); |
914 } | 728 } |
915 | 729 |
916 void VerifyOrder() { | 730 void VerifyOrder() { |
917 const intptr_t length = code_region_table_->length(); | 731 const intptr_t length = table_.length(); |
918 if (length == 0) { | 732 if (length == 0) { |
919 return; | 733 return; |
920 } | 734 } |
921 uword last = (*code_region_table_)[0]->end(); | 735 uword last = table_[0]->end(); |
922 for (intptr_t i = 1; i < length; i++) { | 736 for (intptr_t i = 1; i < length; i++) { |
923 CodeRegion* a = (*code_region_table_)[i]; | 737 ProfileCode* a = table_[i]; |
924 ASSERT(last <= a->start()); | 738 ASSERT(last <= a->start()); |
925 last = a->end(); | 739 last = a->end(); |
926 } | 740 } |
927 } | 741 } |
928 | 742 |
929 void VerifyOverlap() { | 743 void VerifyOverlap() { |
930 const intptr_t length = code_region_table_->length(); | 744 const intptr_t length = table_.length(); |
931 for (intptr_t i = 0; i < length; i++) { | 745 for (intptr_t i = 0; i < length; i++) { |
932 CodeRegion* a = (*code_region_table_)[i]; | 746 ProfileCode* a = table_[i]; |
933 for (intptr_t j = i+1; j < length; j++) { | 747 for (intptr_t j = i+1; j < length; j++) { |
934 CodeRegion* b = (*code_region_table_)[j]; | 748 ProfileCode* b = table_[j]; |
935 ASSERT(!a->contains(b->start()) && | 749 ASSERT(!a->Contains(b->start()) && |
936 !a->contains(b->end() - 1) && | 750 !a->Contains(b->end() - 1) && |
937 !b->contains(a->start()) && | 751 !b->Contains(a->start()) && |
938 !b->contains(a->end() - 1)); | 752 !b->Contains(a->end() - 1)); |
939 } | 753 } |
940 } | 754 } |
941 } | 755 } |
942 | 756 |
943 ZoneGrowableArray<CodeRegion*>* code_region_table_; | 757 ZoneGrowableArray<ProfileCode*> table_; |
944 }; | 758 }; |
945 | 759 |
946 | 760 |
947 class CodeRegionTableBuilder { | 761 ProfileTrieNode::ProfileTrieNode(intptr_t table_index) |
762 : table_index_(table_index), | |
763 count_(0), | |
764 children_(0) { | |
765 ASSERT(table_index_ >= 0); | |
766 } | |
767 | |
768 | |
769 ProfileTrieNode::~ProfileTrieNode() { | |
770 } | |
771 | |
772 | |
773 void ProfileTrieNode::SortChildren() { | |
774 children_.Sort(ProfileTrieNodeCompare); | |
775 // Recurse. | |
776 for (intptr_t i = 0; i < children_.length(); i++) { | |
777 children_[i]->SortChildren(); | |
778 } | |
779 } | |
780 | |
781 | |
782 class ProfileCodeTrieNode : public ProfileTrieNode { | |
948 public: | 783 public: |
949 CodeRegionTableBuilder(Isolate* isolate, | 784 explicit ProfileCodeTrieNode(intptr_t table_index) |
950 CodeRegionTable* live_code_table, | 785 : ProfileTrieNode(table_index) { |
951 CodeRegionTable* dead_code_table, | |
952 CodeRegionTable* tag_code_table, | |
953 DeoptimizedCodeSet* deoptimized_code) | |
954 : live_code_table_(live_code_table), | |
955 dead_code_table_(dead_code_table), | |
956 tag_code_table_(tag_code_table), | |
957 isolate_(isolate), | |
958 vm_isolate_(Dart::vm_isolate()), | |
959 null_code_(Code::ZoneHandle()), | |
960 deoptimized_code_(deoptimized_code) { | |
961 ASSERT(live_code_table_ != NULL); | |
962 ASSERT(dead_code_table_ != NULL); | |
963 ASSERT(tag_code_table_ != NULL); | |
964 ASSERT(isolate_ != NULL); | |
965 ASSERT(vm_isolate_ != NULL); | |
966 ASSERT(null_code_.IsNull()); | |
967 frames_ = 0; | |
968 min_time_ = kMaxInt64; | |
969 max_time_ = 0; | |
970 } | 786 } |
971 | 787 |
972 void Build(ProcessedSampleBuffer* buffer) { | 788 void PrintToJSONArray(JSONArray* array) const { |
973 for (intptr_t i = 0; i < buffer->length(); i++) { | 789 ASSERT(array != NULL); |
974 ProcessedSample* sample = buffer->At(i); | 790 // Write CodeRegion index. |
975 VisitSample(i, sample); | 791 array->AddValue(table_index()); |
792 // Write count. | |
793 array->AddValue(count()); | |
794 // Write number of children. | |
795 intptr_t child_count = NumChildren(); | |
796 array->AddValue(child_count); | |
797 // Recurse. | |
798 for (intptr_t i = 0; i < child_count; i++) { | |
799 children_[i]->PrintToJSONArray(array); | |
976 } | 800 } |
977 } | 801 } |
978 | 802 |
979 intptr_t frames() const { return frames_; } | 803 ProfileCodeTrieNode* GetChild(intptr_t child_table_index) { |
980 | 804 const intptr_t length = NumChildren(); |
981 intptr_t TimeDeltaMicros() const { | 805 intptr_t i = 0; |
982 return static_cast<intptr_t>(max_time_ - min_time_); | 806 while (i < length) { |
807 ProfileCodeTrieNode* child = | |
808 reinterpret_cast<ProfileCodeTrieNode*>(children_[i]); | |
809 if (child->table_index() == child_table_index) { | |
810 return child; | |
811 } | |
812 if (child->table_index() > child_table_index) { | |
813 break; | |
814 } | |
815 i++; | |
816 } | |
817 ProfileCodeTrieNode* child = new ProfileCodeTrieNode(child_table_index); | |
818 if (i < length) { | |
819 // Insert at i. | |
820 children_.InsertAt(i, reinterpret_cast<ProfileTrieNode*>(child)); | |
821 } else { | |
822 // Add to end. | |
823 children_.Add(reinterpret_cast<ProfileTrieNode*>(child)); | |
824 } | |
825 return child; | |
983 } | 826 } |
984 int64_t max_time() const { return max_time_; } | |
985 | |
986 private: | |
987 void VisitSample(intptr_t serial, ProcessedSample* sample) { | |
988 int64_t timestamp = sample->timestamp(); | |
989 if (timestamp > max_time_) { | |
990 max_time_ = timestamp; | |
991 } | |
992 if (timestamp < min_time_) { | |
993 min_time_ = timestamp; | |
994 } | |
995 | |
996 // Make sure VM tag is created. | |
997 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | |
998 CreateTag(VMTag::kNativeTagId); | |
999 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | |
1000 CreateTag(VMTag::kRuntimeTagId); | |
1001 } | |
1002 CreateTag(sample->vm_tag()); | |
1003 | |
1004 // Make sure user tag is created. | |
1005 CreateUserTag(sample->user_tag()); | |
1006 | |
1007 // Exclusive tick for top frame if the first frame was executing. | |
1008 if (!sample->first_frame_executing()) { | |
1009 Tick(sample->At(0), true, serial, timestamp); | |
1010 } | |
1011 | |
1012 // Inclusive tick for all frames. | |
1013 for (intptr_t i = 0; i < sample->length(); i++) { | |
1014 ASSERT(sample->At(i) != 0); | |
1015 frames_++; | |
1016 Tick(sample->At(i), false, serial, timestamp); | |
1017 } | |
1018 } | |
1019 | |
1020 void CreateTag(uword tag) { | |
1021 intptr_t index = tag_code_table_->FindIndex(tag); | |
1022 if (index >= 0) { | |
1023 // Already created. | |
1024 return; | |
1025 } | |
1026 CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, | |
1027 tag, | |
1028 tag + 1, | |
1029 0, | |
1030 null_code_); | |
1031 index = tag_code_table_->InsertCodeRegion(region); | |
1032 ASSERT(index >= 0); | |
1033 } | |
1034 | |
1035 void CreateUserTag(uword tag) { | |
1036 if (tag == 0) { | |
1037 // None set. | |
1038 return; | |
1039 } | |
1040 return CreateTag(tag); | |
1041 } | |
1042 | |
1043 void Tick(uword pc, bool exclusive, intptr_t serial, int64_t timestamp) { | |
1044 CodeRegionTable::TickResult r; | |
1045 if (exclusive) { | |
1046 // Exclusive ticks do not have an associated serial. | |
1047 serial = -1; | |
1048 } | |
1049 | |
1050 r = live_code_table_->Tick(pc, exclusive, serial, timestamp); | |
1051 if (r == CodeRegionTable::kTicked) { | |
1052 // Live code found and ticked. | |
1053 return; | |
1054 } | |
1055 | |
1056 if (r == CodeRegionTable::kNewerCode) { | |
1057 // Code has been overwritten by newer code. | |
1058 // Update shadow table of dead code regions. | |
1059 r = dead_code_table_->Tick(pc, exclusive, serial, timestamp); | |
1060 ASSERT(r != CodeRegionTable::kNewerCode); | |
1061 if (r == CodeRegionTable::kTicked) { | |
1062 // Dead code found and ticked. | |
1063 return; | |
1064 } | |
1065 ASSERT(r == CodeRegionTable::kNotFound); | |
1066 CreateAndTickDeadCodeRegion(pc, exclusive, serial); | |
1067 return; | |
1068 } | |
1069 | |
1070 // Create new live CodeRegion. | |
1071 ASSERT(r == CodeRegionTable::kNotFound); | |
1072 CodeRegion* region = CreateCodeRegion(pc); | |
1073 intptr_t index = live_code_table_->InsertCodeRegion(region); | |
1074 ASSERT(index >= 0); | |
1075 region = live_code_table_->At(index); | |
1076 if (region->compile_timestamp() <= timestamp) { | |
1077 region->Tick(pc, exclusive, serial); | |
1078 return; | |
1079 } | |
1080 | |
1081 // We have created a new code region but it's for a CodeRegion | |
1082 // compiled after the sample. | |
1083 ASSERT(region->kind() == CodeRegion::kDartCode); | |
1084 CreateAndTickDeadCodeRegion(pc, exclusive, serial); | |
1085 } | |
1086 | |
1087 void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) { | |
1088 // Need to create dead code. | |
1089 CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode, | |
1090 pc, | |
1091 pc + 1, | |
1092 0, | |
1093 null_code_); | |
1094 intptr_t index = dead_code_table_->InsertCodeRegion(region); | |
1095 ASSERT(index >= 0); | |
1096 dead_code_table_->At(index)->Tick(pc, exclusive, serial); | |
1097 } | |
1098 | |
1099 CodeRegion* CreateCodeRegion(uword pc) { | |
1100 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); | |
1101 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); | |
1102 Code& code = Code::Handle(isolate_); | |
1103 | |
1104 // Check current isolate for pc. | |
1105 if (isolate_->heap()->CodeContains(pc)) { | |
1106 code ^= Code::LookupCode(pc); | |
1107 if (!code.IsNull()) { | |
1108 deoptimized_code_->Add(code); | |
1109 return new CodeRegion(CodeRegion::kDartCode, | |
1110 code.EntryPoint(), | |
1111 code.EntryPoint() + code.Size(), | |
1112 code.compile_timestamp(), | |
1113 code); | |
1114 } | |
1115 return new CodeRegion(CodeRegion::kCollectedCode, | |
1116 pc, | |
1117 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | |
1118 0, | |
1119 code); | |
1120 } | |
1121 | |
1122 // Check VM isolate for pc. | |
1123 if (vm_isolate_->heap()->CodeContains(pc)) { | |
1124 code ^= Code::LookupCodeInVmIsolate(pc); | |
1125 if (!code.IsNull()) { | |
1126 return new CodeRegion(CodeRegion::kDartCode, | |
1127 code.EntryPoint(), | |
1128 code.EntryPoint() + code.Size(), | |
1129 code.compile_timestamp(), | |
1130 code); | |
1131 } | |
1132 return new CodeRegion(CodeRegion::kCollectedCode, | |
1133 pc, | |
1134 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | |
1135 0, | |
1136 code); | |
1137 } | |
1138 | |
1139 // Check NativeSymbolResolver for pc. | |
1140 uintptr_t native_start = 0; | |
1141 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, | |
1142 &native_start); | |
1143 if (native_name == NULL) { | |
1144 // No native name found. | |
1145 return new CodeRegion(CodeRegion::kNativeCode, | |
1146 pc, | |
1147 pc + 1, | |
1148 0, | |
1149 code); | |
1150 } | |
1151 ASSERT(pc >= native_start); | |
1152 CodeRegion* code_region = | |
1153 new CodeRegion(CodeRegion::kNativeCode, | |
1154 native_start, | |
1155 pc + 1, | |
1156 0, | |
1157 code); | |
1158 code_region->SetName(native_name); | |
1159 free(native_name); | |
1160 return code_region; | |
1161 } | |
1162 | |
1163 intptr_t frames_; | |
1164 int64_t min_time_; | |
1165 int64_t max_time_; | |
1166 CodeRegionTable* live_code_table_; | |
1167 CodeRegionTable* dead_code_table_; | |
1168 CodeRegionTable* tag_code_table_; | |
1169 Isolate* isolate_; | |
1170 Isolate* vm_isolate_; | |
1171 const Code& null_code_; | |
1172 DeoptimizedCodeSet* deoptimized_code_; | |
1173 }; | |
1174 | |
1175 | |
1176 class CodeRegionFunctionMapper : public ValueObject { | |
1177 public: | |
1178 CodeRegionFunctionMapper(Isolate* isolate, | |
1179 CodeRegionTable* live_code_table, | |
1180 CodeRegionTable* dead_code_table, | |
1181 CodeRegionTable* tag_code_table, | |
1182 ProfileFunctionTable* function_table) | |
1183 : isolate_(isolate), | |
1184 live_code_table_(live_code_table), | |
1185 dead_code_table_(dead_code_table), | |
1186 tag_code_table_(tag_code_table), | |
1187 function_table_(function_table) { | |
1188 ASSERT(isolate_ != NULL); | |
1189 ASSERT(live_code_table_ != NULL); | |
1190 ASSERT(dead_code_table_ != NULL); | |
1191 ASSERT(tag_code_table_ != NULL); | |
1192 dead_code_table_offset_ = live_code_table_->Length(); | |
1193 tag_code_table_offset_ = dead_code_table_offset_ + | |
1194 dead_code_table_->Length(); | |
1195 | |
1196 const Code& null_code = Code::ZoneHandle(); | |
1197 | |
1198 // Create the truncated tag. | |
1199 intptr_t truncated_index = | |
1200 tag_code_table_->FindIndex(VMTag::kTruncatedTagId); | |
1201 ASSERT(truncated_index < 0); | |
1202 CodeRegion* truncated = | |
1203 new CodeRegion(CodeRegion::kTagCode, | |
1204 VMTag::kTruncatedTagId, | |
1205 VMTag::kTruncatedTagId + 1, | |
1206 0, | |
1207 null_code); | |
1208 truncated_index = tag_code_table_->InsertCodeRegion(truncated); | |
1209 ASSERT(truncated_index >= 0); | |
1210 | |
1211 // Create the root tag. | |
1212 intptr_t root_index = tag_code_table_->FindIndex(VMTag::kRootTagId); | |
1213 ASSERT(root_index < 0); | |
1214 CodeRegion* root = new CodeRegion(CodeRegion::kTagCode, | |
1215 VMTag::kRootTagId, | |
1216 VMTag::kRootTagId + 1, | |
1217 0, | |
1218 null_code); | |
1219 root_index = tag_code_table_->InsertCodeRegion(root); | |
1220 ASSERT(root_index >= 0); | |
1221 } | |
1222 | |
1223 void Map() { | |
1224 // Calculate final indexes in code table for each CodeRegion. | |
1225 for (intptr_t i = 0; i < live_code_table_->Length(); i++) { | |
1226 const intptr_t index = i; | |
1227 CodeRegion* region = live_code_table_->At(i); | |
1228 ASSERT(region != NULL); | |
1229 region->set_code_table_index(index); | |
1230 } | |
1231 | |
1232 for (intptr_t i = 0; i < dead_code_table_->Length(); i++) { | |
1233 const intptr_t index = dead_code_table_offset_ + i; | |
1234 CodeRegion* region = dead_code_table_->At(i); | |
1235 ASSERT(region != NULL); | |
1236 region->set_code_table_index(index); | |
1237 } | |
1238 | |
1239 for (intptr_t i = 0; i < tag_code_table_->Length(); i++) { | |
1240 const intptr_t index = tag_code_table_offset_ + i; | |
1241 CodeRegion* region = tag_code_table_->At(i); | |
1242 ASSERT(region != NULL); | |
1243 region->set_code_table_index(index); | |
1244 } | |
1245 | |
1246 // Associate a ProfileFunction with each CodeRegion. | |
1247 for (intptr_t i = 0; i < live_code_table_->Length(); i++) { | |
1248 CodeRegion* region = live_code_table_->At(i); | |
1249 ASSERT(region != NULL); | |
1250 region->SetFunctionAndName(function_table_); | |
1251 } | |
1252 | |
1253 for (intptr_t i = 0; i < dead_code_table_->Length(); i++) { | |
1254 CodeRegion* region = dead_code_table_->At(i); | |
1255 ASSERT(region != NULL); | |
1256 region->SetFunctionAndName(function_table_); | |
1257 } | |
1258 | |
1259 for (intptr_t i = 0; i < tag_code_table_->Length(); i++) { | |
1260 CodeRegion* region = tag_code_table_->At(i); | |
1261 ASSERT(region != NULL); | |
1262 region->SetFunctionAndName(function_table_); | |
1263 } | |
1264 } | |
1265 | |
1266 private: | |
1267 Isolate* isolate_; | |
1268 CodeRegionTable* live_code_table_; | |
1269 CodeRegionTable* dead_code_table_; | |
1270 CodeRegionTable* tag_code_table_; | |
1271 ProfileFunctionTable* function_table_; | |
1272 intptr_t dead_code_table_offset_; | |
1273 intptr_t tag_code_table_offset_; | |
1274 }; | 827 }; |
1275 | 828 |
1276 | 829 |
1277 class ProfileFunctionTrieNodeCode { | 830 class ProfileFunctionTrieNodeCode { |
1278 public: | 831 public: |
1279 explicit ProfileFunctionTrieNodeCode(intptr_t index) | 832 explicit ProfileFunctionTrieNodeCode(intptr_t index) |
1280 : code_index_(index), | 833 : code_index_(index), |
1281 ticks_(0) { | 834 ticks_(0) { |
1282 } | 835 } |
1283 | 836 |
1284 intptr_t index() const { | 837 intptr_t index() const { |
1285 return code_index_; | 838 return code_index_; |
1286 } | 839 } |
1287 | 840 |
1288 void Tick() { | 841 void Tick() { |
1289 ticks_++; | 842 ticks_++; |
1290 } | 843 } |
1291 | 844 |
1292 intptr_t ticks() const { | 845 intptr_t ticks() const { |
1293 return ticks_; | 846 return ticks_; |
1294 } | 847 } |
1295 | 848 |
1296 private: | 849 private: |
1297 intptr_t code_index_; | 850 intptr_t code_index_; |
1298 intptr_t ticks_; | 851 intptr_t ticks_; |
1299 }; | 852 }; |
1300 | 853 |
1301 | 854 |
1302 class ProfileFunctionTrieNode : public ZoneAllocated { | 855 class ProfileFunctionTrieNode : public ProfileTrieNode { |
1303 public: | 856 public: |
1304 explicit ProfileFunctionTrieNode(intptr_t profile_function_table_index) | 857 explicit ProfileFunctionTrieNode(intptr_t table_index) |
1305 : profile_function_table_index_(profile_function_table_index), | 858 : ProfileTrieNode(table_index), |
1306 count_(0), | 859 code_objects_(1) { |
1307 code_objects_(new ZoneGrowableArray<ProfileFunctionTrieNodeCode>()) { | |
1308 } | 860 } |
1309 | 861 |
1310 void Tick() { | 862 void PrintToJSONArray(JSONArray* array) const { |
1311 count_++; | 863 ASSERT(array != NULL); |
864 // Write CodeRegion index. | |
865 array->AddValue(table_index()); | |
866 // Write count. | |
867 array->AddValue(count()); | |
868 // Write number of code objects. | |
869 intptr_t code_count = code_objects_.length(); | |
870 array->AddValue(code_count); | |
871 // Write each code object index and ticks. | |
872 for (intptr_t i = 0; i < code_count; i++) { | |
873 array->AddValue(code_objects_[i].index()); | |
874 array->AddValue(code_objects_[i].ticks()); | |
875 } | |
876 // Write number of children. | |
877 intptr_t child_count = children_.length(); | |
878 array->AddValue(child_count); | |
879 // Recurse. | |
880 for (intptr_t i = 0; i < child_count; i++) { | |
881 children_[i]->PrintToJSONArray(array); | |
882 } | |
1312 } | 883 } |
1313 | 884 |
1314 intptr_t count() const { | 885 ProfileFunctionTrieNode* GetChild(intptr_t child_table_index) { |
1315 return count_; | 886 const intptr_t length = NumChildren(); |
1316 } | |
1317 | |
1318 intptr_t profile_function_table_index() const { | |
1319 return profile_function_table_index_; | |
1320 } | |
1321 | |
1322 | |
1323 ProfileFunctionTrieNode* GetChild(intptr_t child_index) { | |
1324 const intptr_t length = children_.length(); | |
1325 intptr_t i = 0; | 887 intptr_t i = 0; |
1326 while (i < length) { | 888 while (i < length) { |
1327 ProfileFunctionTrieNode* child = children_[i]; | 889 ProfileFunctionTrieNode* child = |
1328 if (child->profile_function_table_index() == child_index) { | 890 reinterpret_cast<ProfileFunctionTrieNode*>(children_[i]); |
891 if (child->table_index() == child_table_index) { | |
1329 return child; | 892 return child; |
1330 } | 893 } |
1331 if (child->profile_function_table_index() > child_index) { | 894 if (child->table_index() > child_table_index) { |
1332 break; | 895 break; |
1333 } | 896 } |
1334 i++; | 897 i++; |
1335 } | 898 } |
1336 // Add new ProfileFunctionTrieNode, sorted by index. | |
1337 ProfileFunctionTrieNode* child = | 899 ProfileFunctionTrieNode* child = |
1338 new ProfileFunctionTrieNode(child_index); | 900 new ProfileFunctionTrieNode(child_table_index); |
1339 if (i < length) { | 901 if (i < length) { |
1340 // Insert at i. | 902 // Insert at i. |
1341 children_.InsertAt(i, child); | 903 children_.InsertAt(i, reinterpret_cast<ProfileTrieNode*>(child)); |
1342 } else { | 904 } else { |
1343 // Add to end. | 905 // Add to end. |
1344 children_.Add(child); | 906 children_.Add(reinterpret_cast<ProfileTrieNode*>(child)); |
1345 } | 907 } |
1346 return child; | 908 return child; |
1347 } | 909 } |
1348 | 910 |
1349 void AddCodeObjectIndex(intptr_t index) { | 911 void AddCodeObjectIndex(intptr_t index) { |
1350 for (intptr_t i = 0; i < code_objects_->length(); i++) { | 912 for (intptr_t i = 0; i < code_objects_.length(); i++) { |
1351 ProfileFunctionTrieNodeCode& code_object = (*code_objects_)[i]; | 913 ProfileFunctionTrieNodeCode& code_object = code_objects_[i]; |
1352 if (code_object.index() == index) { | 914 if (code_object.index() == index) { |
1353 code_object.Tick(); | 915 code_object.Tick(); |
1354 return; | 916 return; |
1355 } | 917 } |
1356 } | 918 } |
1357 ProfileFunctionTrieNodeCode code_object(index); | 919 ProfileFunctionTrieNodeCode code_object(index); |
1358 code_object.Tick(); | 920 code_object.Tick(); |
1359 code_objects_->Add(code_object); | 921 code_objects_.Add(code_object); |
1360 } | |
1361 | |
1362 // This should only be called after the trie is completely built. | |
1363 void SortByCount() { | |
1364 code_objects_->Sort(ProfileFunctionTrieNodeCodeCompare); | |
1365 children_.Sort(ProfileFunctionTrieNodeCompare); | |
1366 intptr_t child_count = children_.length(); | |
1367 // Recurse. | |
1368 for (intptr_t i = 0; i < child_count; i++) { | |
1369 children_[i]->SortByCount(); | |
1370 } | |
1371 } | |
1372 | |
1373 void PrintToJSONArray(JSONArray* array) const { | |
1374 ASSERT(array != NULL); | |
1375 // Write CodeRegion index. | |
1376 array->AddValue(profile_function_table_index_); | |
1377 // Write count. | |
1378 array->AddValue(count_); | |
1379 // Write number of code objects. | |
1380 intptr_t code_count = code_objects_->length(); | |
1381 array->AddValue(code_count); | |
1382 // Write each code object index and ticks. | |
1383 for (intptr_t i = 0; i < code_count; i++) { | |
1384 array->AddValue((*code_objects_)[i].index()); | |
1385 array->AddValue((*code_objects_)[i].ticks()); | |
1386 } | |
1387 // Write number of children. | |
1388 intptr_t child_count = children_.length(); | |
1389 array->AddValue(child_count); | |
1390 // Recurse. | |
1391 for (intptr_t i = 0; i < child_count; i++) { | |
1392 children_[i]->PrintToJSONArray(array); | |
1393 } | |
1394 } | 922 } |
1395 | 923 |
1396 private: | 924 private: |
1397 static int ProfileFunctionTrieNodeCodeCompare( | 925 ZoneGrowableArray<ProfileFunctionTrieNodeCode> code_objects_; |
1398 const ProfileFunctionTrieNodeCode* a, | |
1399 const ProfileFunctionTrieNodeCode* b) { | |
1400 ASSERT(a != NULL); | |
1401 ASSERT(b != NULL); | |
1402 return b->ticks() - a->ticks(); | |
1403 } | |
1404 | |
1405 static int ProfileFunctionTrieNodeCompare(ProfileFunctionTrieNode* const* a, | |
1406 ProfileFunctionTrieNode* const* b) { | |
1407 ASSERT(a != NULL); | |
1408 ASSERT(b != NULL); | |
1409 return (*b)->count() - (*a)->count(); | |
1410 } | |
1411 | |
1412 const intptr_t profile_function_table_index_; | |
1413 intptr_t count_; | |
1414 ZoneGrowableArray<ProfileFunctionTrieNode*> children_; | |
1415 ZoneGrowableArray<ProfileFunctionTrieNodeCode>* code_objects_; | |
1416 }; | 926 }; |
1417 | 927 |
1418 | 928 |
1419 class TrieBuilder : public ValueObject { | 929 class ProfileBuilder : public ValueObject { |
1420 public: | 930 public: |
1421 TrieBuilder(CodeRegionTable* live_code_table, | 931 ProfileBuilder(Isolate* isolate, |
1422 CodeRegionTable* dead_code_table, | 932 SampleFilter* filter, |
1423 CodeRegionTable* tag_code_table) | 933 Profile::TagOrder tag_order, |
1424 : live_code_table_(live_code_table), | 934 Profile* profile) |
1425 dead_code_table_(dead_code_table), | 935 : isolate_(isolate), |
1426 tag_code_table_(tag_code_table) { | 936 vm_isolate_(Dart::vm_isolate()), |
1427 ASSERT(live_code_table_ != NULL); | 937 filter_(filter), |
1428 ASSERT(dead_code_table_ != NULL); | 938 tag_order_(tag_order), |
1429 ASSERT(tag_code_table_ != NULL); | 939 profile_(profile), |
1430 } | 940 deoptimized_code_(new DeoptimizedCodeSet(isolate)), |
1431 | 941 null_code_(Code::ZoneHandle()), |
1432 ProfilerService::TagOrder tag_order() const { | 942 null_function_(Function::ZoneHandle()), |
1433 return tag_order_; | 943 tick_functions_(false), |
1434 } | 944 samples_(NULL) { |
1435 | 945 ASSERT(profile_ != NULL); |
1436 void set_tag_order(ProfilerService::TagOrder tag_order) { | 946 } |
1437 tag_order_ = tag_order; | 947 |
1438 } | 948 void Build() { |
1439 | 949 ScopeTimer sw("ProfileBuilder::Build", FLAG_trace_profiler); |
1440 protected: | 950 Setup(); |
1441 intptr_t FindTagIndex(uword tag) const { | 951 |
1442 if (tag == 0) { | 952 FilterSamples(); |
1443 UNREACHABLE(); | 953 |
1444 return -1; | 954 if ((samples_ == NULL) || (samples_->length() == 0)) { |
1445 } | 955 // Empty. |
1446 intptr_t index = tag_code_table_->FindIndex(tag); | 956 return; |
1447 if (index < 0) { | 957 } |
1448 UNREACHABLE(); | 958 |
1449 return -1; | 959 BuildCodeTable(); |
1450 } | 960 FinalizeCodeIndexes(); |
1451 ASSERT(index >= 0); | 961 BuildFunctionTable(); |
1452 CodeRegion* region = tag_code_table_->At(index); | 962 |
1453 ASSERT(region->contains(tag)); | 963 BuildCodeTrie(Profile::kExclusiveCode); |
1454 return region->code_table_index(); | 964 BuildCodeTrie(Profile::kInclusiveCode); |
1455 } | 965 |
1456 | 966 BuildFunctionTrie(Profile::kExclusiveFunction); |
1457 intptr_t FindDeadIndex(uword pc, int64_t timestamp) const { | 967 BuildFunctionTrie(Profile::kInclusiveFunction); |
1458 intptr_t index = dead_code_table_->FindIndex(pc); | |
1459 if (index < 0) { | |
1460 OS::Print("%" Px " cannot be found\n", pc); | |
1461 return -1; | |
1462 } | |
1463 CodeRegion* region = dead_code_table_->At(index); | |
1464 ASSERT(region->contains(pc)); | |
1465 ASSERT(region->compile_timestamp() <= timestamp); | |
1466 return region->code_table_index(); | |
1467 } | |
1468 | |
1469 intptr_t FindFinalIndex(uword pc, int64_t timestamp) const { | |
1470 intptr_t index = live_code_table_->FindIndex(pc); | |
1471 if (index < 0) { | |
1472 // Try dead code table. | |
1473 return FindDeadIndex(pc, timestamp); | |
1474 } | |
1475 CodeRegion* region = live_code_table_->At(index); | |
1476 ASSERT(region->contains(pc)); | |
1477 if (region->compile_timestamp() > timestamp) { | |
1478 // Overwritten code, find in dead code table. | |
1479 return FindDeadIndex(pc, timestamp); | |
1480 } | |
1481 ASSERT(region->compile_timestamp() <= timestamp); | |
1482 return region->code_table_index(); | |
1483 } | |
1484 | |
1485 bool vm_tags_emitted() const { | |
1486 return (tag_order_ == ProfilerService::kUserVM) || | |
1487 (tag_order_ == ProfilerService::kVMUser) || | |
1488 (tag_order_ == ProfilerService::kVM); | |
1489 } | |
1490 | |
1491 CodeRegion* FindCodeObject(uword pc, int64_t timestamp) const { | |
1492 intptr_t index = live_code_table_->FindIndex(pc); | |
1493 if (index < 0) { | |
1494 return NULL; | |
1495 } | |
1496 CodeRegion* region = live_code_table_->At(index); | |
1497 ASSERT(region->contains(pc)); | |
1498 if (region->compile_timestamp() > timestamp) { | |
1499 // Overwritten code, find in dead code table. | |
1500 index = dead_code_table_->FindIndex(pc); | |
1501 if (index < 0) { | |
1502 return NULL; | |
1503 } | |
1504 region = dead_code_table_->At(index); | |
1505 ASSERT(region->contains(pc)); | |
1506 ASSERT(region->compile_timestamp() <= timestamp); | |
1507 return region; | |
1508 } | |
1509 ASSERT(region->compile_timestamp() <= timestamp); | |
1510 return region; | |
1511 } | |
1512 | |
1513 CodeRegionTable* live_code_table_; | |
1514 CodeRegionTable* dead_code_table_; | |
1515 CodeRegionTable* tag_code_table_; | |
1516 ProfilerService::TagOrder tag_order_; | |
1517 }; | |
1518 | |
1519 | |
1520 class ProfileFunctionTrieBuilder : public TrieBuilder { | |
1521 public: | |
1522 ProfileFunctionTrieBuilder(CodeRegionTable* live_code_table, | |
1523 CodeRegionTable* dead_code_table, | |
1524 CodeRegionTable* tag_code_table, | |
1525 ProfileFunctionTable* function_table) | |
1526 : TrieBuilder(live_code_table, dead_code_table, tag_code_table), | |
1527 function_table_(function_table), | |
1528 inclusive_tree_(false) { | |
1529 ASSERT(function_table_ != NULL); | |
1530 set_tag_order(ProfilerService::kUserVM); | |
1531 | |
1532 // Verify that the truncated tag exists. | |
1533 ASSERT(tag_code_table_->FindIndex(VMTag::kTruncatedTagId) >= 0); | |
1534 | |
1535 // Verify that the root tag exists. | |
1536 intptr_t root_index = tag_code_table_->FindIndex(VMTag::kRootTagId); | |
1537 ASSERT(root_index >= 0); | |
1538 | |
1539 // Setup root. | |
1540 CodeRegion* region = tag_code_table_->At(root_index); | |
1541 ASSERT(region != NULL); | |
1542 ProfileFunction* function = region->function(); | |
1543 ASSERT(function != NULL); | |
1544 | |
1545 exclusive_root_ = new ProfileFunctionTrieNode(function->index()); | |
1546 inclusive_root_ = new ProfileFunctionTrieNode(function->index()); | |
1547 } | |
1548 | |
1549 void VisitSample(intptr_t sample_idx, ProcessedSample* sample) { | |
1550 inclusive_tree_ = false; | |
1551 ProcessSampleExclusive(sample_idx, sample); | |
1552 inclusive_tree_ = true; | |
1553 ProcessSampleInclusive(sample_idx, sample); | |
1554 } | |
1555 | |
1556 void Build(ProcessedSampleBuffer* buffer) { | |
1557 for (intptr_t i = 0; i < buffer->length(); i++) { | |
1558 ProcessedSample* sample = buffer->At(i); | |
1559 VisitSample(i, sample); | |
1560 } | |
1561 } | |
1562 | |
1563 ProfileFunctionTrieNode* exclusive_root() const { | |
1564 return exclusive_root_; | |
1565 } | |
1566 | |
1567 ProfileFunctionTrieNode* inclusive_root() const { | |
1568 return inclusive_root_; | |
1569 } | |
1570 | |
1571 ProfilerService::TagOrder tag_order() const { | |
1572 return tag_order_; | |
1573 } | |
1574 | |
1575 bool vm_tags_emitted() const { | |
1576 return (tag_order_ == ProfilerService::kUserVM) || | |
1577 (tag_order_ == ProfilerService::kVMUser) || | |
1578 (tag_order_ == ProfilerService::kVM); | |
1579 } | |
1580 | |
1581 void set_tag_order(ProfilerService::TagOrder tag_order) { | |
1582 tag_order_ = tag_order; | |
1583 } | 968 } |
1584 | 969 |
1585 private: | 970 private: |
1586 void ProcessSampleInclusive(intptr_t sample_idx, ProcessedSample* sample) { | 971 static bool IsInclusiveTrie(Profile::TrieKind kind) { |
1587 // Give the root a tick. | 972 return (kind == Profile::kInclusiveFunction) || |
1588 inclusive_root_->Tick(); | 973 (kind == Profile::kInclusiveCode); |
1589 ProfileFunctionTrieNode* current = inclusive_root_; | 974 } |
1590 current = AppendTags(sample, current); | 975 |
1591 if (sample->truncated()) { | 976 void Setup() { |
1592 InclusiveTickTruncatedTag(); | 977 profile_->live_code_ = new ProfileCodeTable(); |
1593 current = AppendTruncatedTag(current); | 978 profile_->dead_code_ = new ProfileCodeTable(); |
1594 } | 979 profile_->tag_code_ = new ProfileCodeTable(); |
1595 // Walk the sampled PCs. | 980 profile_->functions_ = new ProfileFunctionTable(); |
1596 for (intptr_t i = sample->length() - 1; i >= 0; i--) { | 981 // Register some synthetic tags. |
1597 ASSERT(sample->At(i) != 0); | 982 RegisterProfileCodeTag(VMTag::kRootTagId); |
1598 current = ProcessPC(sample->At(i), | 983 RegisterProfileCodeTag(VMTag::kTruncatedTagId); |
1599 sample->timestamp(), | 984 } |
1600 current, | 985 |
1601 sample_idx, | 986 void FilterSamples() { |
1602 (i == 0), | 987 ScopeTimer sw("ProfileBuilder::FilterSamples", FLAG_trace_profiler); |
1603 !sample->first_frame_executing() && (i == 0)); | 988 MutexLocker profiler_data_lock(isolate_->profiler_data_mutex()); |
1604 } | 989 IsolateProfilerData* profiler_data = isolate_->profiler_data(); |
1605 } | 990 if (profiler_data == NULL) { |
1606 | 991 return; |
1607 void ProcessSampleExclusive(intptr_t sample_idx, ProcessedSample* sample) { | 992 } |
1608 // Give the root a tick. | 993 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
1609 exclusive_root_->Tick(); | 994 if (sample_buffer == NULL) { |
1610 ProfileFunctionTrieNode* current = exclusive_root_; | 995 return; |
1611 current = AppendTags(sample, current); | 996 } |
1612 // Walk the sampled PCs. | 997 samples_ = sample_buffer->BuildProcessedSampleBuffer(filter_); |
1613 for (intptr_t i = 0; i < sample->length(); i++) { | 998 profile_->sample_count_ = samples_->length(); |
1614 ASSERT(sample->At(i) != 0); | 999 } |
1615 current = ProcessPC(sample->At(i), | 1000 |
1616 sample->timestamp(), | 1001 void UpdateMinMaxTimes(int64_t timestamp) { |
1617 current, | 1002 profile_->min_time_ = |
1618 sample_idx, | 1003 timestamp < profile_->min_time_ ? timestamp : profile_->min_time_; |
1619 (i == 0), | 1004 profile_->max_time_ = |
1620 !sample->first_frame_executing() && (i == 0)); | 1005 timestamp > profile_->max_time_ ? timestamp : profile_->max_time_; |
1621 } | 1006 } |
1622 if (sample->truncated()) { | 1007 |
1623 current = AppendTruncatedTag(current); | 1008 void BuildCodeTable() { |
1624 } | 1009 ScopeTimer sw("ProfileBuilder::BuildCodeTable", FLAG_trace_profiler); |
1625 } | 1010 for (intptr_t i = 0; i < samples_->length(); i++) { |
1626 | 1011 ProcessedSample* sample = samples_->At(i); |
1627 ProfileFunctionTrieNode* AppendUserTag(ProcessedSample* sample, | 1012 const int64_t timestamp = sample->timestamp(); |
1628 ProfileFunctionTrieNode* current) { | 1013 |
1629 intptr_t user_tag_index = FindTagFunctionIndex(sample->user_tag()); | 1014 // This is our first pass over the sample buffer, use this as an |
1630 if (user_tag_index >= 0) { | 1015 // opportunity to determine the min and max time ranges of this profile. |
1631 current = current->GetChild(user_tag_index); | 1016 UpdateMinMaxTimes(timestamp); |
1632 // Give the tag a tick. | 1017 |
1018 // Make sure VM tag exists. | |
1019 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | |
1020 RegisterProfileCodeTag(VMTag::kNativeTagId); | |
1021 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | |
1022 RegisterProfileCodeTag(VMTag::kRuntimeTagId); | |
1023 } | |
1024 RegisterProfileCodeTag(sample->vm_tag()); | |
1025 // Make sure user tag exists. | |
1026 RegisterProfileCodeTag(sample->user_tag()); | |
1027 | |
1028 // Make sure that a ProfileCode objects exist for all pcs in the sample | |
1029 // and tick each one. | |
1030 for (intptr_t i = 0; i < sample->length(); i++) { | |
1031 const uword pc = sample->At(i); | |
1032 ASSERT(pc != 0); | |
1033 ProfileCode* code = RegisterProfileCode(pc, timestamp); | |
1034 ASSERT(code != NULL); | |
1035 code->Tick(pc, (i == 0), i); | |
1036 } | |
1037 } | |
1038 } | |
1039 | |
1040 void FinalizeCodeIndexes() { | |
1041 ScopeTimer sw("ProfileBuilder::FinalizeCodeIndexes", FLAG_trace_profiler); | |
1042 ProfileCodeTable* live_table = profile_->live_code_; | |
1043 ProfileCodeTable* dead_table = profile_->dead_code_; | |
1044 ProfileCodeTable* tag_table = profile_->tag_code_; | |
1045 const intptr_t dead_code_index_offset = live_table->length(); | |
1046 const intptr_t tag_code_index_offset = | |
1047 dead_table->length() + dead_code_index_offset; | |
1048 | |
1049 profile_->dead_code_index_offset_ = dead_code_index_offset; | |
1050 profile_->tag_code_index_offset_ = tag_code_index_offset; | |
1051 | |
1052 for (intptr_t i = 0; i < live_table->length(); i++) { | |
1053 const intptr_t index = i; | |
1054 ProfileCode* code = live_table->At(i); | |
1055 ASSERT(code != NULL); | |
1056 code->set_code_table_index(index); | |
1057 } | |
1058 | |
1059 for (intptr_t i = 0; i < dead_table->length(); i++) { | |
1060 const intptr_t index = dead_code_index_offset + i; | |
1061 ProfileCode* code = dead_table->At(i); | |
1062 ASSERT(code != NULL); | |
1063 code->set_code_table_index(index); | |
1064 } | |
1065 | |
1066 for (intptr_t i = 0; i < tag_table->length(); i++) { | |
1067 const intptr_t index = tag_code_index_offset + i; | |
1068 ProfileCode* code = tag_table->At(i); | |
1069 ASSERT(code != NULL); | |
1070 code->set_code_table_index(index); | |
1071 } | |
1072 } | |
1073 | |
1074 void BuildFunctionTable() { | |
1075 ScopeTimer sw("ProfileBuilder::BuildFunctionTable", FLAG_trace_profiler); | |
1076 ProfileCodeTable* live_table = profile_->live_code_; | |
1077 ProfileCodeTable* dead_table = profile_->dead_code_; | |
1078 ProfileCodeTable* tag_table = profile_->tag_code_; | |
1079 ProfileFunctionTable* function_table = profile_->functions_; | |
1080 for (intptr_t i = 0; i < live_table->length(); i++) { | |
1081 ProfileCode* code = live_table->At(i); | |
1082 ASSERT(code != NULL); | |
1083 code->SetFunctionAndName(function_table); | |
1084 } | |
1085 | |
1086 for (intptr_t i = 0; i < dead_table->length(); i++) { | |
1087 ProfileCode* code = dead_table->At(i); | |
1088 ASSERT(code != NULL); | |
1089 code->SetFunctionAndName(function_table); | |
1090 } | |
1091 | |
1092 for (intptr_t i = 0; i < tag_table->length(); i++) { | |
1093 ProfileCode* code = tag_table->At(i); | |
1094 ASSERT(code != NULL); | |
1095 code->SetFunctionAndName(function_table); | |
1096 } | |
1097 } | |
1098 | |
1099 void BuildCodeTrie(Profile::TrieKind kind) { | |
1100 ProfileCodeTrieNode* root = | |
1101 new ProfileCodeTrieNode(GetProfileCodeTagIndex(VMTag::kRootTagId)); | |
1102 if (IsInclusiveTrie(kind)) { | |
1103 BuildInclusiveCodeTrie(root); | |
1104 } else { | |
1105 BuildExclusiveCodeTrie(root); | |
1106 } | |
1107 root->SortChildren(); | |
1108 profile_->roots_[static_cast<intptr_t>(kind)] = root; | |
1109 } | |
1110 | |
1111 void BuildInclusiveCodeTrie(ProfileCodeTrieNode* root) { | |
1112 ScopeTimer sw("ProfileBuilder::BuildInclusiveCodeTrie", | |
1113 FLAG_trace_profiler); | |
1114 for (intptr_t i = 0; i < samples_->length(); i++) { | |
1115 ProcessedSample* sample = samples_->At(i); | |
1116 | |
1117 // Tick the root. | |
1118 ProfileCodeTrieNode* current = root; | |
1633 current->Tick(); | 1119 current->Tick(); |
1634 } | 1120 |
1635 return current; | 1121 // VM & User tags. |
1636 } | 1122 current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
1637 | 1123 |
1638 | 1124 // Truncated tag. |
1639 ProfileFunctionTrieNode* AppendTruncatedTag( | 1125 if (sample->truncated()) { |
1640 ProfileFunctionTrieNode* current) { | 1126 current = AppendTruncatedTag(current); |
1641 intptr_t truncated_tag_index = FindTagFunctionIndex(VMTag::kTruncatedTagId); | 1127 } |
1642 ASSERT(truncated_tag_index >= 0); | 1128 |
1643 current = current->GetChild(truncated_tag_index); | 1129 // Walk the sampled PCs. |
1644 current->Tick(); | 1130 for (intptr_t j = sample->length() - 1; j >= 0; j--) { |
1645 return current; | 1131 ASSERT(sample->At(j) != 0); |
1646 } | 1132 intptr_t index = |
1647 | 1133 GetProfileCodeIndex(sample->At(j), sample->timestamp()); |
1648 void InclusiveTickTruncatedTag() { | 1134 ASSERT(index >= 0); |
1649 intptr_t index = tag_code_table_->FindIndex(VMTag::kTruncatedTagId); | 1135 current = current->GetChild(index); |
1650 CodeRegion* region = tag_code_table_->At(index); | 1136 current->Tick(); |
1651 ProfileFunction* function = region->function(); | 1137 } |
1652 function->inc_inclusive_ticks(); | 1138 } |
1653 } | 1139 } |
1654 | 1140 |
1655 ProfileFunctionTrieNode* AppendVMTag(ProcessedSample* sample, | 1141 void BuildExclusiveCodeTrie(ProfileCodeTrieNode* root) { |
1656 ProfileFunctionTrieNode* current) { | 1142 ScopeTimer sw("ProfileBuilder::BuildExclusiveCodeTrie", |
1657 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | 1143 FLAG_trace_profiler); |
1658 // Insert a dummy kNativeTagId node. | 1144 for (intptr_t i = 0; i < samples_->length(); i++) { |
1659 intptr_t tag_index = FindTagFunctionIndex(VMTag::kNativeTagId); | 1145 ProcessedSample* sample = samples_->At(i); |
1660 current = current->GetChild(tag_index); | 1146 |
1661 // Give the tag a tick. | 1147 // Tick the root. |
1148 ProfileCodeTrieNode* current = root; | |
1662 current->Tick(); | 1149 current->Tick(); |
1663 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1150 |
1664 // Insert a dummy kRuntimeTagId node. | 1151 // VM & User tags. |
1665 intptr_t tag_index = FindTagFunctionIndex(VMTag::kRuntimeTagId); | 1152 current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
1666 current = current->GetChild(tag_index); | 1153 |
1667 // Give the tag a tick. | 1154 // Walk the sampled PCs. |
1155 for (intptr_t j = 0; j < sample->length(); j++) { | |
1156 ASSERT(sample->At(j) != 0); | |
1157 intptr_t index = | |
1158 GetProfileCodeIndex(sample->At(j), sample->timestamp()); | |
1159 ASSERT(index >= 0); | |
1160 current = current->GetChild(index); | |
1161 | |
1162 if (j == 0) { | |
1163 // Executing PC. | |
1164 if (!sample->first_frame_executing() || vm_tags_emitted()) { | |
1165 // Only tick if this isn't an exit frame or VM tags are emitted. | |
1166 current->Tick(); | |
1167 } | |
1168 } else { | |
1169 // Caller PCs. | |
1170 current->Tick(); | |
1171 } | |
1172 | |
1173 current->Tick(); | |
1174 } | |
1175 | |
1176 // Truncated tag. | |
1177 if (sample->truncated()) { | |
1178 current = AppendTruncatedTag(current); | |
1179 } | |
1180 } | |
1181 } | |
1182 | |
1183 void BuildFunctionTrie(Profile::TrieKind kind) { | |
1184 ProfileFunctionTrieNode* root = | |
1185 new ProfileFunctionTrieNode( | |
1186 GetProfileFunctionTagIndex(VMTag::kRootTagId)); | |
1187 // We tick the functions while building the trie, but, we don't want to do | |
1188 // it for both tries, just one. | |
1189 tick_functions_ = IsInclusiveTrie(kind); | |
1190 if (IsInclusiveTrie(kind)) { | |
1191 BuildInclusiveFunctionTrie(root); | |
1192 } else { | |
1193 BuildExclusiveFunctionTrie(root); | |
1194 } | |
1195 root->SortChildren(); | |
1196 profile_->roots_[static_cast<intptr_t>(kind)] = root; | |
1197 } | |
1198 | |
1199 void BuildInclusiveFunctionTrie(ProfileFunctionTrieNode* root) { | |
1200 ScopeTimer sw("ProfileBuilder::BuildInclusiveFunctionTrie", | |
1201 FLAG_trace_profiler); | |
1202 for (intptr_t i = 0; i < samples_->length(); i++) { | |
1203 ProcessedSample* sample = samples_->At(i); | |
1204 | |
1205 // Tick the root. | |
1206 ProfileFunctionTrieNode* current = root; | |
1668 current->Tick(); | 1207 current->Tick(); |
1669 } else { | 1208 |
1670 intptr_t tag_index = FindTagFunctionIndex(sample->vm_tag()); | 1209 // VM & User tags. |
1671 current = current->GetChild(tag_index); | 1210 current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
1672 // Give the tag a tick. | 1211 |
1212 // Truncated tag. | |
1213 if (sample->truncated()) { | |
1214 current = AppendTruncatedTag(current); | |
1215 InclusiveTickTruncatedTag(); | |
1216 } | |
1217 | |
1218 // Walk the sampled PCs. | |
1219 for (intptr_t j = sample->length() - 1; j >= 0; j--) { | |
1220 ASSERT(sample->At(j) != 0); | |
1221 current = ProcessFunctionPC( | |
1222 sample->At(j), | |
1223 sample->timestamp(), | |
1224 current, | |
1225 i, | |
1226 (j == 0), | |
1227 sample->first_frame_executing(), | |
1228 true); | |
1229 } | |
1230 } | |
1231 } | |
1232 | |
1233 void BuildExclusiveFunctionTrie(ProfileFunctionTrieNode* root) { | |
1234 ScopeTimer sw("ProfileBuilder::BuildExclusiveFunctionTrie", | |
1235 FLAG_trace_profiler); | |
1236 for (intptr_t i = 0; i < samples_->length(); i++) { | |
1237 ProcessedSample* sample = samples_->At(i); | |
1238 | |
1239 // Tick the root. | |
1240 ProfileFunctionTrieNode* current = root; | |
1673 current->Tick(); | 1241 current->Tick(); |
1674 } | 1242 |
1675 return current; | 1243 // VM & User tags. |
1676 } | 1244 current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
1677 | 1245 |
1678 ProfileFunctionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( | 1246 // Walk the sampled PCs. |
1679 ProcessedSample* sample, ProfileFunctionTrieNode* current) { | 1247 for (intptr_t j = 0; j < sample->length(); j++) { |
1680 // Only Native and Runtime entries have a second VM tag. | 1248 ASSERT(sample->At(j) != 0); |
1681 if (!VMTag::IsNativeEntryTag(sample->vm_tag()) && | 1249 current = ProcessFunctionPC( |
1682 !VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1250 sample->At(j), |
1683 return current; | 1251 sample->timestamp(), |
1684 } | 1252 current, |
1685 intptr_t tag_index = FindTagFunctionIndex(sample->vm_tag()); | 1253 i, |
1686 current = current->GetChild(tag_index); | 1254 (j == 0), |
1687 // Give the tag a tick. | 1255 sample->first_frame_executing(), |
1688 current->Tick(); | 1256 false); |
1689 return current; | 1257 } |
1690 } | 1258 |
1691 | 1259 // Truncated tag. |
1692 ProfileFunctionTrieNode* AppendVMTags(ProcessedSample* sample, | 1260 if (sample->truncated()) { |
1693 ProfileFunctionTrieNode* current) { | 1261 current = AppendTruncatedTag(current); |
1694 current = AppendVMTag(sample, current); | 1262 } |
1695 current = AppendSpecificNativeRuntimeEntryVMTag(sample, current); | 1263 } |
1696 return current; | 1264 } |
1697 } | 1265 |
1698 | 1266 ProfileFunctionTrieNode* ProcessFunctionPC( |
1699 ProfileFunctionTrieNode* AppendTags(ProcessedSample* sample, | 1267 uword pc, |
1700 ProfileFunctionTrieNode* current) { | 1268 int64_t timestamp, |
1701 // None. | 1269 ProfileFunctionTrieNode* current, |
1702 if (tag_order() == ProfilerService::kNoTags) { | 1270 intptr_t inclusive_serial, |
1703 return current; | 1271 bool top_frame, |
1704 } | 1272 bool top_frame_executing, |
1705 // User first. | 1273 bool inclusive_tree) { |
1706 if ((tag_order() == ProfilerService::kUserVM) || | 1274 ProfileCode* profile_code = GetProfileCode(pc, timestamp); |
1707 (tag_order() == ProfilerService::kUser)) { | 1275 ASSERT(profile_code != NULL); |
1708 current = AppendUserTag(sample, current); | 1276 const char* code_name = profile_code->name(); |
1709 // Only user. | 1277 if (code_name == NULL) { |
1710 if (tag_order() == ProfilerService::kUser) { | 1278 code_name = ""; |
1711 return current; | 1279 } |
1712 } | 1280 intptr_t code_index = profile_code->code_table_index(); |
1713 return AppendVMTags(sample, current); | 1281 const Code& code = Code::ZoneHandle(profile_code->code()); |
1714 } | |
1715 // VM first. | |
1716 ASSERT((tag_order() == ProfilerService::kVMUser) || | |
1717 (tag_order() == ProfilerService::kVM)); | |
1718 current = AppendVMTags(sample, current); | |
1719 // Only VM. | |
1720 if (tag_order() == ProfilerService::kVM) { | |
1721 return current; | |
1722 } | |
1723 return AppendUserTag(sample, current); | |
1724 } | |
1725 | |
1726 intptr_t FindTagFunctionIndex(uword tag) const { | |
1727 if (tag == 0) { | |
1728 UNREACHABLE(); | |
1729 return -1; | |
1730 } | |
1731 intptr_t index = tag_code_table_->FindIndex(tag); | |
1732 if (index < 0) { | |
1733 UNREACHABLE(); | |
1734 return -1; | |
1735 } | |
1736 ASSERT(index >= 0); | |
1737 CodeRegion* region = tag_code_table_->At(index); | |
1738 ASSERT(region->contains(tag)); | |
1739 ProfileFunction* function = region->function(); | |
1740 ASSERT(function != NULL); | |
1741 return function->index(); | |
1742 } | |
1743 | |
1744 void Dump(ProfileFunctionTrieNode* current) { | |
1745 int current_index = current->profile_function_table_index(); | |
1746 ProfileFunction* function = function_table_->At(current_index); | |
1747 function->Dump(); | |
1748 OS::Print("\n"); | |
1749 } | |
1750 | |
1751 ProfileFunctionTrieNode* ProcessPC(uword pc, | |
1752 int64_t timestamp, | |
1753 ProfileFunctionTrieNode* current, | |
1754 intptr_t inclusive_serial, | |
1755 bool top_frame, | |
1756 bool exit_frame) { | |
1757 CodeRegion* region = FindCodeObject(pc, timestamp); | |
1758 if (region == NULL) { | |
1759 return current; | |
1760 } | |
1761 const char* region_name = region->name(); | |
1762 if (region_name == NULL) { | |
1763 region_name = ""; | |
1764 } | |
1765 intptr_t code_index = region->code_table_index(); | |
1766 const Code& code = Code::ZoneHandle(region->code()); | |
1767 GrowableArray<Function*> inlined_functions; | 1282 GrowableArray<Function*> inlined_functions; |
1768 if (!code.IsNull()) { | 1283 if (!code.IsNull()) { |
1769 intptr_t offset = pc - code.EntryPoint(); | 1284 intptr_t offset = pc - code.EntryPoint(); |
1770 code.GetInlinedFunctionsAt(offset, &inlined_functions); | 1285 code.GetInlinedFunctionsAt(offset, &inlined_functions); |
1771 } | 1286 } |
1772 if (code.IsNull() || (inlined_functions.length() == 0)) { | 1287 if (code.IsNull() || (inlined_functions.length() == 0)) { |
1773 // No inlined functions. | 1288 // No inlined functions. |
1774 ProfileFunction* function = region->function(); | 1289 ProfileFunction* function = profile_code->function(); |
1775 ASSERT(function != NULL); | 1290 ASSERT(function != NULL); |
1776 current = ProcessFunction(function, | 1291 current = ProcessFunction(function, |
1777 current, | 1292 current, |
1778 inclusive_serial, | 1293 inclusive_serial, |
1779 top_frame, | 1294 top_frame, |
1780 exit_frame, | 1295 top_frame_executing, |
1781 code_index); | 1296 code_index); |
1782 return current; | 1297 return current; |
1783 } | 1298 } |
1784 | 1299 |
1785 if (inclusive_tree_) { | 1300 if (inclusive_tree) { |
1786 for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) { | 1301 for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) { |
1787 Function* inlined_function = inlined_functions[i]; | 1302 Function* inlined_function = inlined_functions[i]; |
1788 ASSERT(inlined_function != NULL); | 1303 ASSERT(inlined_function != NULL); |
1789 ASSERT(!inlined_function->IsNull()); | 1304 ASSERT(!inlined_function->IsNull()); |
1790 current = ProcessInlinedFunction(inlined_function, | 1305 current = ProcessInlinedFunction(inlined_function, |
1791 current, | 1306 current, |
1792 inclusive_serial, | 1307 inclusive_serial, |
1793 top_frame, | 1308 top_frame, |
1794 exit_frame, | 1309 top_frame_executing, |
1795 code_index); | 1310 code_index); |
1796 top_frame = false; | 1311 top_frame = false; |
1797 } | 1312 } |
1798 } else { | 1313 } else { |
1799 for (intptr_t i = 0; i < inlined_functions.length(); i++) { | 1314 for (intptr_t i = 0; i < inlined_functions.length(); i++) { |
1800 Function* inlined_function = inlined_functions[i]; | 1315 Function* inlined_function = inlined_functions[i]; |
1801 ASSERT(inlined_function != NULL); | 1316 ASSERT(inlined_function != NULL); |
1802 ASSERT(!inlined_function->IsNull()); | 1317 ASSERT(!inlined_function->IsNull()); |
1803 current = ProcessInlinedFunction(inlined_function, | 1318 current = ProcessInlinedFunction(inlined_function, |
1804 current, | 1319 current, |
1805 inclusive_serial, | 1320 inclusive_serial, |
1806 top_frame, | 1321 top_frame, |
1807 exit_frame, | 1322 top_frame_executing, |
1808 code_index); | 1323 code_index); |
1809 top_frame = false; | 1324 top_frame = false; |
1810 } | 1325 } |
1811 } | 1326 } |
1812 | 1327 |
1813 return current; | 1328 return current; |
1814 } | 1329 } |
1815 | 1330 |
1816 ProfileFunctionTrieNode* ProcessInlinedFunction( | 1331 ProfileFunctionTrieNode* ProcessInlinedFunction( |
1817 Function* inlined_function, | 1332 Function* inlined_function, |
1818 ProfileFunctionTrieNode* current, | 1333 ProfileFunctionTrieNode* current, |
1819 intptr_t inclusive_serial, | 1334 intptr_t inclusive_serial, |
1820 bool top_frame, | 1335 bool top_frame, |
1821 bool exit_frame, | 1336 bool top_frame_executing, |
1822 intptr_t code_index) { | 1337 intptr_t code_index) { |
1823 ProfileFunction* function = | 1338 ProfileFunctionTable* function_table = profile_->functions_; |
1824 function_table_->LookupOrAdd(*inlined_function); | 1339 ProfileFunction* function = function_table->LookupOrAdd(*inlined_function); |
1825 ASSERT(function != NULL); | 1340 ASSERT(function != NULL); |
1826 return ProcessFunction(function, | 1341 return ProcessFunction(function, |
1827 current, | 1342 current, |
1828 inclusive_serial, | 1343 inclusive_serial, |
1829 top_frame, | 1344 top_frame, |
1830 exit_frame, | 1345 top_frame_executing, |
1831 code_index); | 1346 code_index); |
1832 } | 1347 } |
1833 | 1348 |
1834 ProfileFunctionTrieNode* ProcessFunction(ProfileFunction* function, | 1349 ProfileFunctionTrieNode* ProcessFunction(ProfileFunction* function, |
1835 ProfileFunctionTrieNode* current, | 1350 ProfileFunctionTrieNode* current, |
1836 intptr_t inclusive_serial, | 1351 intptr_t inclusive_serial, |
1837 bool top_frame, | 1352 bool top_frame, |
1838 bool exit_frame, | 1353 bool top_frame_executing, |
1839 intptr_t code_index) { | 1354 intptr_t code_index) { |
1840 const bool exclusive = top_frame && !exit_frame; | 1355 const bool exclusive = top_frame && top_frame_executing; |
1841 if (!inclusive_tree_) { | 1356 if (tick_functions_) { |
1842 // We process functions for the inclusive and exclusive trees. | |
1843 // Only tick the function for the exclusive tree. | |
1844 function->Tick(exclusive, exclusive ? -1 : inclusive_serial); | 1357 function->Tick(exclusive, exclusive ? -1 : inclusive_serial); |
1845 } | 1358 } |
1846 function->AddCodeObjectIndex(code_index); | 1359 function->AddProfileCode(code_index); |
1847 current = current->GetChild(function->index()); | 1360 current = current->GetChild(function->table_index()); |
1848 current->AddCodeObjectIndex(code_index); | 1361 current->AddCodeObjectIndex(code_index); |
1849 if (top_frame) { | 1362 if (top_frame) { |
1850 if (!exit_frame || vm_tags_emitted()) { | 1363 if (top_frame_executing || vm_tags_emitted()) { |
1851 // Only tick if this isn't an exit frame or VM tags are emitted. | 1364 // Only tick if this function is using CPU time or VM tags are emitted. |
1852 current->Tick(); | 1365 current->Tick(); |
1853 } | 1366 } |
1854 } else { | 1367 } else { |
1855 current->Tick(); | 1368 current->Tick(); |
1856 } | 1369 } |
1857 return current; | 1370 return current; |
1858 } | 1371 } |
1859 | 1372 |
1860 ProfileFunctionTrieNode* exclusive_root_; | 1373 // Tick the truncated tag's inclusive tick count. |
1861 ProfileFunctionTrieNode* inclusive_root_; | 1374 void InclusiveTickTruncatedTag() { |
1862 ProfileFunctionTable* function_table_; | 1375 ProfileCodeTable* tag_table = profile_->tag_code_; |
1863 bool inclusive_tree_; | 1376 intptr_t index = tag_table->FindCodeIndexForPC(VMTag::kTruncatedTagId); |
1864 }; | 1377 ASSERT(index >= 0); |
1378 ProfileCode* code = tag_table->At(index); | |
1379 code->IncInclusiveTicks(); | |
1380 ASSERT(code != NULL); | |
1381 ProfileFunction* function = code->function(); | |
1382 function->IncInclusiveTicks(); | |
1383 } | |
1865 | 1384 |
1866 | 1385 |
1867 class CodeRegionTrieNode : public ZoneAllocated { | 1386 // Tag append functions are overloaded for |ProfileCodeTrieNode| and |
1868 public: | 1387 // |ProfileFunctionTrieNode| types. |
1869 explicit CodeRegionTrieNode(intptr_t code_region_index) | 1388 |
1870 : code_region_index_(code_region_index), | 1389 // ProfileCodeTrieNode |
1871 count_(0), | 1390 ProfileCodeTrieNode* AppendUserTag(uword user_tag, |
1872 children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) { | 1391 ProfileCodeTrieNode* current) { |
1392 intptr_t user_tag_index = GetProfileCodeTagIndex(user_tag); | |
1393 if (user_tag_index >= 0) { | |
1394 current = current->GetChild(user_tag_index); | |
1395 current->Tick(); | |
1396 } | |
1397 return current; | |
1873 } | 1398 } |
1874 | 1399 |
1875 void Tick() { | 1400 ProfileCodeTrieNode* AppendTruncatedTag(ProfileCodeTrieNode* current) { |
1876 ASSERT(code_region_index_ >= 0); | 1401 intptr_t truncated_tag_index = |
1877 count_++; | 1402 GetProfileCodeTagIndex(VMTag::kTruncatedTagId); |
1403 ASSERT(truncated_tag_index >= 0); | |
1404 current = current->GetChild(truncated_tag_index); | |
1405 current->Tick(); | |
1406 return current; | |
1878 } | 1407 } |
1879 | 1408 |
1880 intptr_t count() const { | 1409 ProfileCodeTrieNode* AppendVMTag(uword vm_tag, |
1881 ASSERT(code_region_index_ >= 0); | 1410 ProfileCodeTrieNode* current) { |
1882 return count_; | 1411 if (VMTag::IsNativeEntryTag(vm_tag)) { |
1883 } | 1412 // Insert a dummy kNativeTagId node. |
1884 | 1413 intptr_t tag_index = GetProfileCodeTagIndex(VMTag::kNativeTagId); |
1885 intptr_t code_region_index() const { | 1414 current = current->GetChild(tag_index); |
1886 return code_region_index_; | 1415 // Give the tag a tick. |
1887 } | 1416 current->Tick(); |
1888 | 1417 } else if (VMTag::IsRuntimeEntryTag(vm_tag)) { |
1889 ZoneGrowableArray<CodeRegionTrieNode*>& children() const { | 1418 // Insert a dummy kRuntimeTagId node. |
1890 return *children_; | 1419 intptr_t tag_index = GetProfileCodeTagIndex(VMTag::kRuntimeTagId); |
1891 } | 1420 current = current->GetChild(tag_index); |
1892 | 1421 // Give the tag a tick. |
1893 CodeRegionTrieNode* GetChild(intptr_t child_code_region_index) { | 1422 current->Tick(); |
1894 const intptr_t length = children_->length(); | |
1895 intptr_t i = 0; | |
1896 while (i < length) { | |
1897 CodeRegionTrieNode* child = (*children_)[i]; | |
1898 if (child->code_region_index() == child_code_region_index) { | |
1899 return child; | |
1900 } | |
1901 if (child->code_region_index() > child_code_region_index) { | |
1902 break; | |
1903 } | |
1904 i++; | |
1905 } | |
1906 // Add new CodeRegion, sorted by CodeRegionTable index. | |
1907 CodeRegionTrieNode* child = new CodeRegionTrieNode(child_code_region_index); | |
1908 if (i < length) { | |
1909 // Insert at i. | |
1910 children_->InsertAt(i, child); | |
1911 } else { | 1423 } else { |
1912 // Add to end. | 1424 intptr_t tag_index = GetProfileCodeTagIndex(vm_tag); |
1913 children_->Add(child); | 1425 current = current->GetChild(tag_index); |
1914 } | |
1915 return child; | |
1916 } | |
1917 | |
1918 // This should only be called after the trie is completely built. | |
1919 void SortByCount() { | |
1920 children_->Sort(CodeRegionTrieNodeCompare); | |
1921 ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); | |
1922 intptr_t child_count = kids.length(); | |
1923 // Recurse. | |
1924 for (intptr_t i = 0; i < child_count; i++) { | |
1925 kids[i]->SortByCount(); | |
1926 } | |
1927 } | |
1928 | |
1929 void PrintToJSONArray(JSONArray* array) const { | |
1930 ASSERT(array != NULL); | |
1931 // Write CodeRegion index. | |
1932 array->AddValue(code_region_index_); | |
1933 // Write count. | |
1934 array->AddValue(count_); | |
1935 // Write number of children. | |
1936 ZoneGrowableArray<CodeRegionTrieNode*>& kids = children(); | |
1937 intptr_t child_count = kids.length(); | |
1938 array->AddValue(child_count); | |
1939 // Recurse. | |
1940 for (intptr_t i = 0; i < child_count; i++) { | |
1941 kids[i]->PrintToJSONArray(array); | |
1942 } | |
1943 } | |
1944 | |
1945 private: | |
1946 static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a, | |
1947 CodeRegionTrieNode* const* b) { | |
1948 ASSERT(a != NULL); | |
1949 ASSERT(b != NULL); | |
1950 return (*b)->count() - (*a)->count(); | |
1951 } | |
1952 | |
1953 const intptr_t code_region_index_; | |
1954 intptr_t count_; | |
1955 ZoneGrowableArray<CodeRegionTrieNode*>* children_; | |
1956 }; | |
1957 | |
1958 | |
1959 class CodeRegionTrieBuilder : public TrieBuilder { | |
1960 public: | |
1961 CodeRegionTrieBuilder(Isolate* isolate, | |
1962 CodeRegionTable* live_code_table, | |
1963 CodeRegionTable* dead_code_table, | |
1964 CodeRegionTable* tag_code_table) | |
1965 : TrieBuilder(live_code_table, dead_code_table, tag_code_table) { | |
1966 set_tag_order(ProfilerService::kUserVM); | |
1967 | |
1968 // Verify that the truncated tag exists. | |
1969 ASSERT(tag_code_table_->FindIndex(VMTag::kTruncatedTagId) >= 0); | |
1970 | |
1971 // Verify that the root tag exists. | |
1972 intptr_t root_index = tag_code_table_->FindIndex(VMTag::kRootTagId); | |
1973 ASSERT(root_index >= 0); | |
1974 CodeRegion* region = tag_code_table_->At(root_index); | |
1975 ASSERT(region != NULL); | |
1976 | |
1977 exclusive_root_ = new CodeRegionTrieNode(region->code_table_index()); | |
1978 inclusive_root_ = new CodeRegionTrieNode(region->code_table_index()); | |
1979 } | |
1980 | |
1981 void Build(ProcessedSampleBuffer* buffer) { | |
1982 for (intptr_t i = 0; i < buffer->length(); i++) { | |
1983 ProcessedSample* sample = buffer->At(i); | |
1984 VisitSample(sample); | |
1985 } | |
1986 } | |
1987 | |
1988 CodeRegionTrieNode* inclusive_root() const { | |
1989 return inclusive_root_; | |
1990 } | |
1991 | |
1992 CodeRegionTrieNode* exclusive_root() const { | |
1993 return exclusive_root_; | |
1994 } | |
1995 | |
1996 private: | |
1997 void VisitSample(ProcessedSample* sample) { | |
1998 ProcessSampleExclusive(sample); | |
1999 ProcessSampleInclusive(sample); | |
2000 } | |
2001 | |
2002 void ProcessSampleInclusive(ProcessedSample* sample) { | |
2003 // Give the root a tick. | |
2004 inclusive_root_->Tick(); | |
2005 CodeRegionTrieNode* current = inclusive_root_; | |
2006 current = AppendTags(sample, current); | |
2007 if (sample->truncated()) { | |
2008 current = AppendTruncatedTag(current); | |
2009 } | |
2010 // Walk the sampled PCs. | |
2011 for (intptr_t i = sample->length() - 1; i >= 0; i--) { | |
2012 ASSERT(sample->At(i) != 0); | |
2013 intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); | |
2014 if (index < 0) { | |
2015 continue; | |
2016 } | |
2017 current = current->GetChild(index); | |
2018 current->Tick(); | |
2019 } | |
2020 } | |
2021 | |
2022 void ProcessSampleExclusive(ProcessedSample* sample) { | |
2023 // Give the root a tick. | |
2024 exclusive_root_->Tick(); | |
2025 CodeRegionTrieNode* current = exclusive_root_; | |
2026 current = AppendTags(sample, current); | |
2027 // Walk the sampled PCs. | |
2028 for (intptr_t i = 0; i < sample->length(); i++) { | |
2029 ASSERT(sample->At(i) != 0); | |
2030 intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp()); | |
2031 if (index < 0) { | |
2032 continue; | |
2033 } | |
2034 current = current->GetChild(index); | |
2035 if (i == 0) { | |
2036 // Executing PC. | |
2037 if (!sample->first_frame_executing() || vm_tags_emitted()) { | |
2038 // Only tick if this isn't an exit frame or VM tags are emitted. | |
2039 current->Tick(); | |
2040 } | |
2041 } else { | |
2042 // Caller PCs. | |
2043 current->Tick(); | |
2044 } | |
2045 } | |
2046 if (sample->truncated()) { | |
2047 current = AppendTruncatedTag(current); | |
2048 } | |
2049 } | |
2050 | |
2051 CodeRegionTrieNode* AppendUserTag(ProcessedSample* sample, | |
2052 CodeRegionTrieNode* current) { | |
2053 intptr_t user_tag_index = FindTagIndex(sample->user_tag()); | |
2054 if (user_tag_index >= 0) { | |
2055 current = current->GetChild(user_tag_index); | |
2056 // Give the tag a tick. | 1426 // Give the tag a tick. |
2057 current->Tick(); | 1427 current->Tick(); |
2058 } | 1428 } |
2059 return current; | 1429 return current; |
2060 } | 1430 } |
2061 | 1431 |
2062 CodeRegionTrieNode* AppendTruncatedTag(CodeRegionTrieNode* current) { | 1432 ProfileCodeTrieNode* AppendSpecificNativeRuntimeEntryVMTag( |
2063 intptr_t truncated_tag_index = FindTagIndex(VMTag::kTruncatedTagId); | 1433 uword vm_tag, ProfileCodeTrieNode* current) { |
1434 // Only Native and Runtime entries have a second VM tag. | |
1435 if (!VMTag::IsNativeEntryTag(vm_tag) && | |
1436 !VMTag::IsRuntimeEntryTag(vm_tag)) { | |
1437 return current; | |
1438 } | |
1439 intptr_t tag_index = GetProfileCodeTagIndex(vm_tag); | |
1440 current = current->GetChild(tag_index); | |
1441 // Give the tag a tick. | |
1442 current->Tick(); | |
1443 return current; | |
1444 } | |
1445 | |
1446 ProfileCodeTrieNode* AppendVMTags(uword vm_tag, | |
1447 ProfileCodeTrieNode* current) { | |
1448 current = AppendVMTag(vm_tag, current); | |
1449 current = AppendSpecificNativeRuntimeEntryVMTag(vm_tag, current); | |
1450 return current; | |
1451 } | |
1452 | |
1453 ProfileCodeTrieNode* AppendTags(uword vm_tag, | |
1454 uword user_tag, | |
1455 ProfileCodeTrieNode* current) { | |
1456 // None. | |
1457 if (tag_order() == Profile::kNoTags) { | |
1458 return current; | |
1459 } | |
1460 // User first. | |
1461 if ((tag_order() == Profile::kUserVM) || | |
1462 (tag_order() == Profile::kUser)) { | |
1463 current = AppendUserTag(user_tag, current); | |
1464 // Only user. | |
1465 if (tag_order() == Profile::kUser) { | |
1466 return current; | |
1467 } | |
1468 return AppendVMTags(vm_tag, current); | |
1469 } | |
1470 // VM first. | |
1471 ASSERT((tag_order() == Profile::kVMUser) || | |
1472 (tag_order() == Profile::kVM)); | |
1473 current = AppendVMTags(vm_tag, current); | |
1474 // Only VM. | |
1475 if (tag_order() == Profile::kVM) { | |
1476 return current; | |
1477 } | |
1478 return AppendUserTag(user_tag, current); | |
1479 } | |
1480 | |
1481 // ProfileFunctionTrieNode | |
1482 ProfileFunctionTrieNode* AppendUserTag(uword user_tag, | |
1483 ProfileFunctionTrieNode* current) { | |
1484 intptr_t user_tag_index = GetProfileFunctionTagIndex(user_tag); | |
1485 if (user_tag_index >= 0) { | |
1486 current = current->GetChild(user_tag_index); | |
1487 current->Tick(); | |
1488 } | |
1489 return current; | |
1490 } | |
1491 | |
1492 ProfileFunctionTrieNode* AppendTruncatedTag( | |
1493 ProfileFunctionTrieNode* current) { | |
1494 intptr_t truncated_tag_index = | |
1495 GetProfileFunctionTagIndex(VMTag::kTruncatedTagId); | |
2064 ASSERT(truncated_tag_index >= 0); | 1496 ASSERT(truncated_tag_index >= 0); |
2065 current = current->GetChild(truncated_tag_index); | 1497 current = current->GetChild(truncated_tag_index); |
2066 current->Tick(); | 1498 current->Tick(); |
2067 return current; | 1499 return current; |
2068 } | 1500 } |
2069 | 1501 |
2070 CodeRegionTrieNode* AppendVMTag(ProcessedSample* sample, | 1502 ProfileFunctionTrieNode* AppendVMTag(uword vm_tag, |
2071 CodeRegionTrieNode* current) { | 1503 ProfileFunctionTrieNode* current) { |
2072 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | 1504 if (VMTag::IsNativeEntryTag(vm_tag)) { |
2073 // Insert a dummy kNativeTagId node. | 1505 // Insert a dummy kNativeTagId node. |
2074 intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId); | 1506 intptr_t tag_index = GetProfileFunctionTagIndex(VMTag::kNativeTagId); |
2075 current = current->GetChild(tag_index); | 1507 current = current->GetChild(tag_index); |
2076 // Give the tag a tick. | 1508 // Give the tag a tick. |
2077 current->Tick(); | 1509 current->Tick(); |
2078 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1510 } else if (VMTag::IsRuntimeEntryTag(vm_tag)) { |
2079 // Insert a dummy kRuntimeTagId node. | 1511 // Insert a dummy kRuntimeTagId node. |
2080 intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId); | 1512 intptr_t tag_index = GetProfileFunctionTagIndex(VMTag::kRuntimeTagId); |
2081 current = current->GetChild(tag_index); | 1513 current = current->GetChild(tag_index); |
2082 // Give the tag a tick. | 1514 // Give the tag a tick. |
2083 current->Tick(); | 1515 current->Tick(); |
2084 } else { | 1516 } else { |
2085 intptr_t tag_index = FindTagIndex(sample->vm_tag()); | 1517 intptr_t tag_index = GetProfileFunctionTagIndex(vm_tag); |
2086 current = current->GetChild(tag_index); | 1518 current = current->GetChild(tag_index); |
2087 // Give the tag a tick. | 1519 // Give the tag a tick. |
2088 current->Tick(); | 1520 current->Tick(); |
2089 } | 1521 } |
2090 return current; | 1522 return current; |
2091 } | 1523 } |
2092 | 1524 |
2093 CodeRegionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( | 1525 ProfileFunctionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( |
2094 ProcessedSample* sample, CodeRegionTrieNode* current) { | 1526 uword vm_tag, ProfileFunctionTrieNode* current) { |
2095 // Only Native and Runtime entries have a second VM tag. | 1527 // Only Native and Runtime entries have a second VM tag. |
2096 if (!VMTag::IsNativeEntryTag(sample->vm_tag()) && | 1528 if (!VMTag::IsNativeEntryTag(vm_tag) && |
2097 !VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1529 !VMTag::IsRuntimeEntryTag(vm_tag)) { |
2098 return current; | 1530 return current; |
2099 } | 1531 } |
2100 intptr_t tag_index = FindTagIndex(sample->vm_tag()); | 1532 intptr_t tag_index = GetProfileFunctionTagIndex(vm_tag); |
2101 current = current->GetChild(tag_index); | 1533 current = current->GetChild(tag_index); |
2102 // Give the tag a tick. | 1534 // Give the tag a tick. |
2103 current->Tick(); | 1535 current->Tick(); |
2104 return current; | 1536 return current; |
2105 } | 1537 } |
2106 | 1538 |
2107 CodeRegionTrieNode* AppendVMTags(ProcessedSample* sample, | 1539 ProfileFunctionTrieNode* AppendVMTags(uword vm_tag, |
2108 CodeRegionTrieNode* current) { | 1540 ProfileFunctionTrieNode* current) { |
2109 current = AppendVMTag(sample, current); | 1541 current = AppendVMTag(vm_tag, current); |
2110 current = AppendSpecificNativeRuntimeEntryVMTag(sample, current); | 1542 current = AppendSpecificNativeRuntimeEntryVMTag(vm_tag, current); |
2111 return current; | 1543 return current; |
2112 } | 1544 } |
2113 | 1545 |
2114 CodeRegionTrieNode* AppendTags(ProcessedSample* sample, | 1546 ProfileFunctionTrieNode* AppendTags(uword vm_tag, |
2115 CodeRegionTrieNode* current) { | 1547 uword user_tag, |
1548 ProfileFunctionTrieNode* current) { | |
2116 // None. | 1549 // None. |
2117 if (tag_order() == ProfilerService::kNoTags) { | 1550 if (tag_order() == Profile::kNoTags) { |
2118 return current; | 1551 return current; |
2119 } | 1552 } |
2120 // User first. | 1553 // User first. |
2121 if ((tag_order() == ProfilerService::kUserVM) || | 1554 if ((tag_order() == Profile::kUserVM) || |
2122 (tag_order() == ProfilerService::kUser)) { | 1555 (tag_order() == Profile::kUser)) { |
2123 current = AppendUserTag(sample, current); | 1556 current = AppendUserTag(user_tag, current); |
2124 // Only user. | 1557 // Only user. |
2125 if (tag_order() == ProfilerService::kUser) { | 1558 if (tag_order() == Profile::kUser) { |
2126 return current; | 1559 return current; |
2127 } | 1560 } |
2128 return AppendVMTags(sample, current); | 1561 return AppendVMTags(vm_tag, current); |
2129 } | 1562 } |
2130 // VM first. | 1563 // VM first. |
2131 ASSERT((tag_order() == ProfilerService::kVMUser) || | 1564 ASSERT((tag_order() == Profile::kVMUser) || |
2132 (tag_order() == ProfilerService::kVM)); | 1565 (tag_order() == Profile::kVM)); |
2133 current = AppendVMTags(sample, current); | 1566 current = AppendVMTags(vm_tag, current); |
2134 // Only VM. | 1567 // Only VM. |
2135 if (tag_order() == ProfilerService::kVM) { | 1568 if (tag_order() == Profile::kVM) { |
2136 return current; | 1569 return current; |
2137 } | 1570 } |
2138 return AppendUserTag(sample, current); | 1571 return AppendUserTag(user_tag, current); |
2139 } | 1572 } |
2140 | 1573 |
2141 CodeRegionTrieNode* exclusive_root_; | 1574 intptr_t GetProfileCodeTagIndex(uword tag) { |
2142 CodeRegionTrieNode* inclusive_root_; | 1575 ProfileCodeTable* tag_table = profile_->tag_code_; |
1576 intptr_t index = tag_table->FindCodeIndexForPC(tag); | |
1577 ASSERT(index >= 0); | |
1578 ProfileCode* code = tag_table->At(index); | |
1579 ASSERT(code != NULL); | |
1580 return code->code_table_index(); | |
1581 } | |
1582 | |
1583 intptr_t GetProfileFunctionTagIndex(uword tag) { | |
1584 ProfileCodeTable* tag_table = profile_->tag_code_; | |
1585 intptr_t index = tag_table->FindCodeIndexForPC(tag); | |
1586 ASSERT(index >= 0); | |
1587 ProfileCode* code = tag_table->At(index); | |
1588 ASSERT(code != NULL); | |
1589 ProfileFunction* function = code->function(); | |
1590 ASSERT(function != NULL); | |
1591 return function->table_index(); | |
1592 } | |
1593 | |
1594 intptr_t GetProfileCodeIndex(uword pc, int64_t timestamp) { | |
1595 return GetProfileCode(pc, timestamp)->code_table_index(); | |
1596 } | |
1597 | |
1598 ProfileCode* GetProfileCode(uword pc, int64_t timestamp) { | |
1599 ProfileCodeTable* live_table = profile_->live_code_; | |
1600 ProfileCodeTable* dead_table = profile_->dead_code_; | |
1601 | |
1602 intptr_t index = live_table->FindCodeIndexForPC(pc); | |
1603 ProfileCode* code = NULL; | |
1604 if (index < 0) { | |
1605 index = dead_table->FindCodeIndexForPC(pc); | |
1606 ASSERT(index >= 0); | |
1607 code = dead_table->At(index); | |
1608 } else { | |
1609 code = live_table->At(index); | |
1610 ASSERT(code != NULL); | |
1611 if (code->compile_timestamp() > timestamp) { | |
1612 // Code is newer than sample. Fall back to dead code table. | |
1613 index = dead_table->FindCodeIndexForPC(pc); | |
1614 ASSERT(index >= 0); | |
1615 code = dead_table->At(index); | |
1616 } | |
1617 } | |
1618 | |
1619 ASSERT(code != NULL); | |
1620 ASSERT(code->Contains(pc)); | |
1621 ASSERT(code->compile_timestamp() <= timestamp); | |
1622 return code; | |
1623 } | |
1624 | |
1625 void RegisterProfileCodeTag(uword tag) { | |
1626 if (tag == 0) { | |
1627 // No tag. | |
1628 return; | |
1629 } | |
1630 ProfileCodeTable* tag_table = profile_->tag_code_; | |
1631 intptr_t index = tag_table->FindCodeIndexForPC(tag); | |
1632 if (index >= 0) { | |
1633 // Already created. | |
1634 return; | |
1635 } | |
1636 ProfileCode* region = new ProfileCode(ProfileCode::kTagCode, | |
1637 tag, | |
1638 tag + 1, | |
1639 0, | |
1640 null_code_); | |
1641 index = tag_table->InsertCode(region); | |
1642 ASSERT(index >= 0); | |
1643 } | |
1644 | |
1645 ProfileCode* CreateProfileCodeReused(uword pc) { | |
1646 ProfileCode* code = new ProfileCode(ProfileCode::kReusedCode, | |
1647 pc, | |
1648 pc + 1, | |
1649 0, | |
1650 null_code_); | |
1651 return code; | |
1652 } | |
1653 | |
1654 ProfileCode* CreateProfileCode(uword pc) { | |
1655 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); | |
1656 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); | |
1657 Code& code = Code::Handle(isolate_); | |
1658 | |
1659 // Check current isolate for pc. | |
1660 if (isolate_->heap()->CodeContains(pc)) { | |
1661 code ^= Code::LookupCode(pc); | |
1662 if (!code.IsNull()) { | |
1663 deoptimized_code_->Add(code); | |
1664 return new ProfileCode(ProfileCode::kDartCode, | |
1665 code.EntryPoint(), | |
1666 code.EntryPoint() + code.Size(), | |
1667 code.compile_timestamp(), | |
1668 code); | |
1669 } | |
1670 return new ProfileCode(ProfileCode::kCollectedCode, | |
1671 pc, | |
1672 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | |
1673 0, | |
1674 code); | |
1675 } | |
1676 | |
1677 // Check VM isolate for pc. | |
1678 if (vm_isolate_->heap()->CodeContains(pc)) { | |
1679 code ^= Code::LookupCodeInVmIsolate(pc); | |
1680 if (!code.IsNull()) { | |
1681 return new ProfileCode(ProfileCode::kDartCode, | |
1682 code.EntryPoint(), | |
1683 code.EntryPoint() + code.Size(), | |
1684 code.compile_timestamp(), | |
1685 code); | |
1686 } | |
1687 return new ProfileCode(ProfileCode::kCollectedCode, | |
1688 pc, | |
1689 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | |
1690 0, | |
1691 code); | |
1692 } | |
1693 | |
1694 // Check NativeSymbolResolver for pc. | |
1695 uintptr_t native_start = 0; | |
1696 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, | |
1697 &native_start); | |
1698 if (native_name == NULL) { | |
1699 // No native name found. | |
1700 return new ProfileCode(ProfileCode::kNativeCode, | |
1701 pc, | |
1702 pc + 1, | |
1703 0, | |
1704 code); | |
1705 } | |
1706 ASSERT(pc >= native_start); | |
1707 ProfileCode* profile_code = | |
1708 new ProfileCode(ProfileCode::kNativeCode, | |
1709 native_start, | |
1710 pc + 1, | |
1711 0, | |
1712 code); | |
1713 profile_code->SetName(native_name); | |
1714 free(native_name); | |
1715 return profile_code; | |
1716 } | |
1717 | |
1718 ProfileCode* RegisterProfileCode(uword pc, int64_t timestamp) { | |
1719 ProfileCodeTable* live_table = profile_->live_code_; | |
1720 ProfileCodeTable* dead_table = profile_->dead_code_; | |
1721 | |
1722 ProfileCode* code = live_table->FindCodeForPC(pc); | |
1723 if (code == NULL) { | |
1724 // Code not found. | |
1725 intptr_t index = live_table->InsertCode(CreateProfileCode(pc)); | |
1726 ASSERT(index >= 0); | |
1727 code = live_table->At(index); | |
1728 if (code->compile_timestamp() <= timestamp) { | |
1729 // Code was compiled before sample was taken. | |
1730 return code; | |
1731 } | |
1732 // Code was compiled after the sample was taken. Insert code object into | |
1733 // the dead code table. | |
1734 index = dead_table->InsertCode(CreateProfileCodeReused(pc)); | |
1735 ASSERT(index >= 0); | |
1736 return dead_table->At(index); | |
1737 } | |
1738 // Existing code found. | |
1739 if (code->compile_timestamp() <= timestamp) { | |
1740 // Code was compiled before sample was taken. | |
1741 return code; | |
1742 } | |
1743 // Code was compiled after the sample was taken. Check if we have an entry | |
1744 // in the dead code table. | |
1745 code = dead_table->FindCodeForPC(pc); | |
1746 if (code != NULL) { | |
1747 return code; | |
1748 } | |
1749 // Create a new dead code entry. | |
1750 intptr_t index = dead_table->InsertCode(CreateProfileCodeReused(pc)); | |
1751 ASSERT(index >= 0); | |
1752 return dead_table->At(index); | |
1753 } | |
1754 | |
1755 Profile::TagOrder tag_order() const { | |
1756 return tag_order_; | |
1757 } | |
1758 | |
1759 bool vm_tags_emitted() const { | |
1760 return (tag_order_ == Profile::kUserVM) || | |
1761 (tag_order_ == Profile::kVMUser) || | |
1762 (tag_order_ == Profile::kVM); | |
1763 } | |
1764 | |
1765 Isolate* isolate_; | |
1766 Isolate* vm_isolate_; | |
1767 SampleFilter* filter_; | |
1768 Profile::TagOrder tag_order_; | |
1769 Profile* profile_; | |
1770 DeoptimizedCodeSet* deoptimized_code_; | |
1771 const Code& null_code_; | |
1772 const Function& null_function_; | |
1773 bool tick_functions_; | |
1774 | |
1775 ProcessedSampleBuffer* samples_; | |
2143 }; | 1776 }; |
2144 | 1777 |
2145 | 1778 |
1779 Profile::Profile(Isolate* isolate) | |
1780 : isolate_(isolate), | |
1781 live_code_(NULL), | |
1782 dead_code_(NULL), | |
1783 tag_code_(NULL), | |
1784 functions_(NULL), | |
1785 dead_code_index_offset_(-1), | |
1786 tag_code_index_offset_(-1), | |
1787 min_time_(kMaxInt64), | |
1788 max_time_(0) { | |
1789 ASSERT(isolate_ != NULL); | |
1790 for (intptr_t i = 0; i < kNumTrieKinds; i++) { | |
1791 roots_[i] = NULL; | |
1792 } | |
1793 } | |
1794 | |
1795 | |
1796 void Profile::Build(SampleFilter* filter, TagOrder tag_order) { | |
1797 ProfileBuilder builder(isolate_, filter, tag_order, this); | |
1798 builder.Build(); | |
1799 } | |
1800 | |
1801 | |
1802 ProfileFunction* Profile::GetFunction(intptr_t index) { | |
1803 ASSERT(functions_ != NULL); | |
1804 return functions_->At(index); | |
1805 } | |
1806 | |
1807 | |
1808 ProfileCode* Profile::GetCode(intptr_t index) { | |
1809 ASSERT(live_code_ != NULL); | |
1810 ASSERT(dead_code_ != NULL); | |
1811 ASSERT(tag_code_ != NULL); | |
1812 ASSERT(dead_code_index_offset_ >= 0); | |
1813 ASSERT(tag_code_index_offset_ >= 0); | |
1814 | |
1815 // Code indexes span three arrays. | |
1816 // 0 ... |live_code| | |
1817 // |live_code| ... |dead_code| | |
1818 // |dead_code| ... |tag_code| | |
1819 | |
1820 if (index < dead_code_index_offset_) { | |
1821 return live_code_->At(index); | |
1822 } | |
1823 | |
1824 if (index < tag_code_index_offset_) { | |
1825 index -= dead_code_index_offset_; | |
1826 return dead_code_->At(index); | |
1827 } | |
1828 | |
1829 index -= tag_code_index_offset_; | |
1830 return tag_code_->At(index); | |
1831 } | |
1832 | |
1833 | |
1834 ProfileTrieNode* Profile::GetTrieRoot(TrieKind trie_kind) { | |
1835 return roots_[static_cast<intptr_t>(trie_kind)]; | |
1836 } | |
1837 | |
1838 | |
1839 void Profile::PrintJSON(JSONStream* stream) { | |
1840 ScopeTimer sw("Profile::PrintJSON", FLAG_trace_profiler); | |
1841 JSONObject obj(stream); | |
1842 obj.AddProperty("type", "_CpuProfile"); | |
1843 obj.AddProperty("samplePeriod", | |
1844 static_cast<intptr_t>(FLAG_profile_period)); | |
1845 obj.AddProperty("stackDepth", | |
1846 static_cast<intptr_t>(FLAG_profile_depth)); | |
1847 obj.AddProperty("sampleCount", sample_count()); | |
1848 obj.AddProperty("timeSpan", MicrosecondsToSeconds(GetTimeSpan())); | |
1849 { | |
1850 JSONArray codes(&obj, "codes"); | |
1851 for (intptr_t i = 0; i < live_code_->length(); i++) { | |
1852 ProfileCode* code = live_code_->At(i); | |
1853 ASSERT(code != NULL); | |
1854 code->PrintToJSONArray(&codes); | |
1855 } | |
1856 for (intptr_t i = 0; i < dead_code_->length(); i++) { | |
1857 ProfileCode* code = dead_code_->At(i); | |
1858 ASSERT(code != NULL); | |
1859 code->PrintToJSONArray(&codes); | |
1860 } | |
1861 for (intptr_t i = 0; i < tag_code_->length(); i++) { | |
1862 ProfileCode* code = tag_code_->At(i); | |
1863 ASSERT(code != NULL); | |
1864 code->PrintToJSONArray(&codes); | |
1865 } | |
1866 } | |
1867 | |
1868 { | |
1869 JSONArray functions(&obj, "functions"); | |
1870 for (intptr_t i = 0; i < functions_->length(); i++) { | |
1871 ProfileFunction* function = functions_->At(i); | |
1872 ASSERT(function != NULL); | |
1873 function->PrintToJSONArray(&functions); | |
1874 } | |
1875 } | |
1876 { | |
1877 JSONArray code_trie(&obj, "exclusiveCodeTrie"); | |
1878 ProfileTrieNode* root = roots_[static_cast<intptr_t>(kExclusiveCode)]; | |
1879 ASSERT(root != NULL); | |
1880 root->PrintToJSONArray(&code_trie); | |
1881 } | |
1882 { | |
1883 JSONArray code_trie(&obj, "inclusiveCodeTrie"); | |
1884 ProfileTrieNode* root = roots_[static_cast<intptr_t>(kInclusiveCode)]; | |
1885 ASSERT(root != NULL); | |
1886 root->PrintToJSONArray(&code_trie); | |
1887 } | |
1888 { | |
1889 JSONArray function_trie(&obj, "exclusiveFunctionTrie"); | |
1890 ProfileTrieNode* root = roots_[static_cast<intptr_t>(kExclusiveFunction)]; | |
1891 ASSERT(root != NULL); | |
1892 root->PrintToJSONArray(&function_trie); | |
1893 } | |
1894 { | |
1895 JSONArray function_trie(&obj, "inclusiveFunctionTrie"); | |
1896 ProfileTrieNode* root = roots_[static_cast<intptr_t>(kInclusiveFunction)]; | |
1897 ASSERT(root != NULL); | |
1898 root->PrintToJSONArray(&function_trie); | |
1899 } | |
1900 } | |
1901 | |
1902 | |
2146 class NoAllocationSampleFilter : public SampleFilter { | 1903 class NoAllocationSampleFilter : public SampleFilter { |
2147 public: | 1904 public: |
2148 explicit NoAllocationSampleFilter(Isolate* isolate) | 1905 explicit NoAllocationSampleFilter(Isolate* isolate) |
2149 : SampleFilter(isolate) { | 1906 : SampleFilter(isolate) { |
2150 } | 1907 } |
2151 | 1908 |
2152 bool FilterSample(Sample* sample) { | 1909 bool FilterSample(Sample* sample) { |
2153 return !sample->is_allocation_sample(); | 1910 return !sample->is_allocation_sample(); |
2154 } | 1911 } |
2155 }; | 1912 }; |
2156 | 1913 |
2157 | 1914 |
2158 void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { | 1915 void ProfilerService::PrintJSON(JSONStream* stream, |
1916 Profile::TagOrder tag_order) { | |
2159 Isolate* isolate = Isolate::Current(); | 1917 Isolate* isolate = Isolate::Current(); |
2160 // Disable profile interrupts while processing the buffer. | 1918 // Disable profile interrupts while processing the buffer. |
2161 Profiler::EndExecution(isolate); | 1919 Profiler::EndExecution(isolate); |
2162 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 1920 |
2163 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 1921 { |
2164 if (profiler_data == NULL) { | 1922 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
2165 stream->PrintError(kFeatureDisabled, NULL); | 1923 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
2166 return; | 1924 if (profiler_data == NULL) { |
1925 stream->PrintError(kFeatureDisabled, NULL); | |
1926 return; | |
1927 } | |
2167 } | 1928 } |
2168 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 1929 |
2169 ASSERT(sample_buffer != NULL); | |
2170 ScopeTimer sw("ProfilerService::PrintJSON", FLAG_trace_profiler); | |
2171 { | 1930 { |
2172 StackZone zone(isolate); | 1931 StackZone zone(isolate); |
2173 HANDLESCOPE(isolate); | 1932 HANDLESCOPE(isolate); |
1933 Profile profile(isolate); | |
1934 NoAllocationSampleFilter filter(isolate); | |
1935 profile.Build(&filter, tag_order); | |
1936 profile.PrintJSON(stream); | |
1937 } | |
2174 | 1938 |
2175 ProcessedSampleBuffer* processed_samples = NULL; | |
2176 { | |
2177 ScopeTimer sw("BuildProcessedSampleBuffer", FLAG_trace_profiler); | |
2178 NoAllocationSampleFilter filter(isolate); | |
2179 processed_samples = sample_buffer->BuildProcessedSampleBuffer(&filter); | |
2180 } | |
2181 | |
2182 { | |
2183 // Live code holds Dart, Native, and Collected CodeRegions. | |
2184 CodeRegionTable live_code_table; | |
2185 // Dead code holds Overwritten CodeRegions. | |
2186 CodeRegionTable dead_code_table; | |
2187 // Tag code holds Tag CodeRegions. | |
2188 CodeRegionTable tag_code_table; | |
2189 // Table holding all ProfileFunctions. | |
2190 ProfileFunctionTable function_table; | |
2191 // Set of deoptimized code still referenced by the profiler. | |
2192 DeoptimizedCodeSet* deoptimized_code = new DeoptimizedCodeSet(isolate); | |
2193 | |
2194 // Build CodeRegion tables. | |
2195 CodeRegionTableBuilder builder(isolate, | |
2196 &live_code_table, | |
2197 &dead_code_table, | |
2198 &tag_code_table, | |
2199 deoptimized_code); | |
2200 { | |
2201 ScopeTimer sw("CodeRegionTableBuilder::Build", FLAG_trace_profiler); | |
2202 builder.Build(processed_samples); | |
2203 } | |
2204 intptr_t samples = processed_samples->length(); | |
2205 intptr_t frames = builder.frames(); | |
2206 if (FLAG_trace_profiler) { | |
2207 intptr_t total_live_code_objects = live_code_table.Length(); | |
2208 intptr_t total_dead_code_objects = dead_code_table.Length(); | |
2209 intptr_t total_tag_code_objects = tag_code_table.Length(); | |
2210 OS::Print( | |
2211 "Processed %" Pd " samples with %" Pd " frames\n", samples, frames); | |
2212 OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n", | |
2213 total_live_code_objects, | |
2214 total_dead_code_objects, | |
2215 total_tag_code_objects); | |
2216 } | |
2217 | |
2218 if (FLAG_trace_profiler) { | |
2219 ScopeTimer sw("CodeRegionTableVerify", FLAG_trace_profiler); | |
2220 live_code_table.Verify(); | |
2221 dead_code_table.Verify(); | |
2222 tag_code_table.Verify(); | |
2223 } | |
2224 | |
2225 { | |
2226 ScopeTimer st("CodeRegionFunctionMapping", FLAG_trace_profiler); | |
2227 CodeRegionFunctionMapper mapper(isolate, &live_code_table, | |
2228 &dead_code_table, | |
2229 &tag_code_table, | |
2230 &function_table); | |
2231 mapper.Map(); | |
2232 } | |
2233 if (FLAG_trace_profiler) { | |
2234 intptr_t total_functions = function_table.Length(); | |
2235 OS::Print("FunctionTable: size=%" Pd "\n", total_functions); | |
2236 } | |
2237 CodeRegionTrieBuilder code_trie_builder(isolate, | |
2238 &live_code_table, | |
2239 &dead_code_table, | |
2240 &tag_code_table); | |
2241 code_trie_builder.set_tag_order(tag_order); | |
2242 { | |
2243 // Build CodeRegion trie. | |
2244 ScopeTimer sw("CodeRegionTrieBuilder::Build", FLAG_trace_profiler); | |
2245 code_trie_builder.Build(processed_samples); | |
2246 code_trie_builder.exclusive_root()->SortByCount(); | |
2247 code_trie_builder.inclusive_root()->SortByCount(); | |
2248 } | |
2249 if (FLAG_trace_profiler) { | |
2250 OS::Print("Code Trie Root Count: E: %" Pd " I: %" Pd "\n", | |
2251 code_trie_builder.exclusive_root()->count(), | |
2252 code_trie_builder.inclusive_root()->count()); | |
2253 } | |
2254 ProfileFunctionTrieBuilder function_trie_builder(&live_code_table, | |
2255 &dead_code_table, | |
2256 &tag_code_table, | |
2257 &function_table); | |
2258 function_trie_builder.set_tag_order(tag_order); | |
2259 { | |
2260 // Build ProfileFunction trie. | |
2261 ScopeTimer sw("ProfileFunctionTrieBuilder::Build", | |
2262 FLAG_trace_profiler); | |
2263 function_trie_builder.Build(processed_samples); | |
2264 function_trie_builder.exclusive_root()->SortByCount(); | |
2265 function_trie_builder.inclusive_root()->SortByCount(); | |
2266 } | |
2267 if (FLAG_trace_profiler) { | |
2268 OS::Print("Function Trie Root Count: E: %" Pd " I: %" Pd "\n", | |
2269 function_trie_builder.exclusive_root()->count(), | |
2270 function_trie_builder.inclusive_root()->count()); | |
2271 } | |
2272 { | |
2273 ScopeTimer sw("CpuProfileJSONStream", FLAG_trace_profiler); | |
2274 // Serialize to JSON. | |
2275 JSONObject obj(stream); | |
2276 obj.AddProperty("type", "_CpuProfile"); | |
2277 obj.AddProperty("sampleCount", samples); | |
2278 obj.AddProperty("samplePeriod", | |
2279 static_cast<intptr_t>(FLAG_profile_period)); | |
2280 obj.AddProperty("stackDepth", | |
2281 static_cast<intptr_t>(FLAG_profile_depth)); | |
2282 obj.AddProperty("timeSpan", | |
2283 MicrosecondsToSeconds(builder.TimeDeltaMicros())); | |
2284 { | |
2285 JSONArray code_trie(&obj, "exclusiveCodeTrie"); | |
2286 CodeRegionTrieNode* root = code_trie_builder.exclusive_root(); | |
2287 ASSERT(root != NULL); | |
2288 root->PrintToJSONArray(&code_trie); | |
2289 } | |
2290 { | |
2291 JSONArray code_trie(&obj, "inclusiveCodeTrie"); | |
2292 CodeRegionTrieNode* root = code_trie_builder.inclusive_root(); | |
2293 ASSERT(root != NULL); | |
2294 root->PrintToJSONArray(&code_trie); | |
2295 } | |
2296 { | |
2297 JSONArray function_trie(&obj, "exclusiveFunctionTrie"); | |
2298 ProfileFunctionTrieNode* root = | |
2299 function_trie_builder.exclusive_root(); | |
2300 ASSERT(root != NULL); | |
2301 root->PrintToJSONArray(&function_trie); | |
2302 } | |
2303 { | |
2304 JSONArray function_trie(&obj, "inclusiveFunctionTrie"); | |
2305 ProfileFunctionTrieNode* root = | |
2306 function_trie_builder.inclusive_root(); | |
2307 ASSERT(root != NULL); | |
2308 root->PrintToJSONArray(&function_trie); | |
2309 } | |
2310 { | |
2311 JSONArray codes(&obj, "codes"); | |
2312 for (intptr_t i = 0; i < live_code_table.Length(); i++) { | |
2313 CodeRegion* region = live_code_table.At(i); | |
2314 ASSERT(region != NULL); | |
2315 region->PrintToJSONArray(&codes); | |
2316 } | |
2317 for (intptr_t i = 0; i < dead_code_table.Length(); i++) { | |
2318 CodeRegion* region = dead_code_table.At(i); | |
2319 ASSERT(region != NULL); | |
2320 region->PrintToJSONArray(&codes); | |
2321 } | |
2322 for (intptr_t i = 0; i < tag_code_table.Length(); i++) { | |
2323 CodeRegion* region = tag_code_table.At(i); | |
2324 ASSERT(region != NULL); | |
2325 region->PrintToJSONArray(&codes); | |
2326 } | |
2327 } | |
2328 { | |
2329 JSONArray functions(&obj, "functions"); | |
2330 for (intptr_t i = 0; i < function_table.Length(); i++) { | |
2331 ProfileFunction* function = function_table.At(i); | |
2332 ASSERT(function != NULL); | |
2333 function->PrintToJSONArray(&functions); | |
2334 } | |
2335 } | |
2336 } | |
2337 // Update the isolates set of dead code. | |
2338 deoptimized_code->UpdateIsolate(isolate); | |
2339 } | |
2340 } | |
2341 // Enable profile interrupts. | 1939 // Enable profile interrupts. |
2342 Profiler::BeginExecution(isolate); | 1940 Profiler::BeginExecution(isolate); |
2343 } | 1941 } |
2344 | 1942 |
2345 | 1943 |
2346 void ProfilerService::ClearSamples() { | 1944 void ProfilerService::ClearSamples() { |
2347 Isolate* isolate = Isolate::Current(); | 1945 Isolate* isolate = Isolate::Current(); |
2348 | 1946 |
2349 // Disable profile interrupts while processing the buffer. | 1947 // Disable profile interrupts while processing the buffer. |
2350 Profiler::EndExecution(isolate); | 1948 Profiler::EndExecution(isolate); |
2351 | 1949 |
2352 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 1950 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
2353 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 1951 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
2354 if (profiler_data == NULL) { | 1952 if (profiler_data == NULL) { |
2355 return; | 1953 return; |
2356 } | 1954 } |
2357 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 1955 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
2358 ASSERT(sample_buffer != NULL); | 1956 ASSERT(sample_buffer != NULL); |
2359 | 1957 |
2360 ClearProfileVisitor clear_profile(isolate); | 1958 ClearProfileVisitor clear_profile(isolate); |
2361 sample_buffer->VisitSamples(&clear_profile); | 1959 sample_buffer->VisitSamples(&clear_profile); |
2362 | 1960 |
2363 // Enable profile interrupts. | 1961 // Enable profile interrupts. |
2364 Profiler::BeginExecution(isolate); | 1962 Profiler::BeginExecution(isolate); |
2365 } | 1963 } |
2366 | 1964 |
2367 } // namespace dart | 1965 } // namespace dart |
OLD | NEW |