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, false, "Trace profiler."); |
21 | 21 |
22 // Forward declarations. | |
23 class CodeRegion; | |
24 class ProfileFunction; | |
25 class ProfileFunctionTable; | |
26 | |
27 | |
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 } |
37 | 31 |
(...skipping 47 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 kUnknownFunction: |
| 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::kUnknownFunction, |
| 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 FilterSamples(); |
1441 intptr_t FindTagIndex(uword tag) const { | 951 |
1442 if (tag == 0) { | 952 Setup(); |
1443 UNREACHABLE(); | 953 BuildCodeTable(); |
1444 return -1; | 954 FinalizeCodeIndexes(); |
1445 } | 955 BuildFunctionTable(); |
1446 intptr_t index = tag_code_table_->FindIndex(tag); | 956 |
1447 if (index < 0) { | 957 BuildCodeTrie(Profile::kExclusiveCode); |
1448 UNREACHABLE(); | 958 BuildCodeTrie(Profile::kInclusiveCode); |
1449 return -1; | 959 |
1450 } | 960 BuildFunctionTrie(Profile::kExclusiveFunction); |
1451 ASSERT(index >= 0); | 961 BuildFunctionTrie(Profile::kInclusiveFunction); |
1452 CodeRegion* region = tag_code_table_->At(index); | |
1453 ASSERT(region->contains(tag)); | |
1454 return region->code_table_index(); | |
1455 } | |
1456 | |
1457 intptr_t FindDeadIndex(uword pc, int64_t timestamp) const { | |
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 } | 962 } |
1584 | 963 |
1585 private: | 964 private: |
1586 void ProcessSampleInclusive(intptr_t sample_idx, ProcessedSample* sample) { | 965 static bool IsInclusiveTrie(Profile::TrieKind kind) { |
1587 // Give the root a tick. | 966 return (kind == Profile::kInclusiveFunction) || |
1588 inclusive_root_->Tick(); | 967 (kind == Profile::kInclusiveCode); |
1589 ProfileFunctionTrieNode* current = inclusive_root_; | 968 } |
1590 current = AppendTags(sample, current); | 969 |
1591 if (sample->truncated()) { | 970 void Setup() { |
1592 InclusiveTickTruncatedTag(); | 971 profile_->live_code_ = new ProfileCodeTable(); |
1593 current = AppendTruncatedTag(current); | 972 profile_->dead_code_ = new ProfileCodeTable(); |
1594 } | 973 profile_->tag_code_ = new ProfileCodeTable(); |
1595 // Walk the sampled PCs. | 974 profile_->functions_ = new ProfileFunctionTable(); |
1596 for (intptr_t i = sample->length() - 1; i >= 0; i--) { | 975 // Register some synthetic tags. |
1597 ASSERT(sample->At(i) != 0); | 976 RegisterProfileCodeTag(VMTag::kRootTagId); |
1598 current = ProcessPC(sample->At(i), | 977 RegisterProfileCodeTag(VMTag::kTruncatedTagId); |
1599 sample->timestamp(), | 978 } |
1600 current, | 979 |
1601 sample_idx, | 980 void FilterSamples() { |
1602 (i == 0), | 981 ScopeTimer sw("ProfileBuilder::FilterSamples", FLAG_trace_profiler); |
1603 !sample->first_frame_executing() && (i == 0)); | 982 MutexLocker profiler_data_lock(isolate_->profiler_data_mutex()); |
1604 } | 983 IsolateProfilerData* profiler_data = isolate_->profiler_data(); |
1605 } | 984 if (profiler_data == NULL) { |
1606 | 985 return; |
1607 void ProcessSampleExclusive(intptr_t sample_idx, ProcessedSample* sample) { | 986 } |
1608 // Give the root a tick. | 987 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
1609 exclusive_root_->Tick(); | 988 if (sample_buffer == NULL) { |
1610 ProfileFunctionTrieNode* current = exclusive_root_; | 989 return; |
1611 current = AppendTags(sample, current); | 990 } |
1612 // Walk the sampled PCs. | 991 samples_ = sample_buffer->BuildProcessedSampleBuffer(filter_); |
1613 for (intptr_t i = 0; i < sample->length(); i++) { | 992 profile_->sample_count_ = samples_->length(); |
1614 ASSERT(sample->At(i) != 0); | 993 } |
1615 current = ProcessPC(sample->At(i), | 994 |
1616 sample->timestamp(), | 995 void UpdateMinMaxTimes(int64_t timestamp) { |
1617 current, | 996 profile_->min_time_ = |
1618 sample_idx, | 997 timestamp < profile_->min_time_ ? timestamp : profile_->min_time_; |
1619 (i == 0), | 998 profile_->max_time_ = |
1620 !sample->first_frame_executing() && (i == 0)); | 999 timestamp > profile_->max_time_ ? timestamp : profile_->max_time_; |
1621 } | 1000 } |
1622 if (sample->truncated()) { | 1001 |
1623 current = AppendTruncatedTag(current); | 1002 void BuildCodeTable() { |
1624 } | 1003 ScopeTimer sw("ProfileBuilder::BuildCodeTable", FLAG_trace_profiler); |
1625 } | 1004 for (intptr_t i = 0; i < samples_->length(); i++) { |
1626 | 1005 ProcessedSample* sample = samples_->At(i); |
1627 ProfileFunctionTrieNode* AppendUserTag(ProcessedSample* sample, | 1006 const int64_t timestamp = sample->timestamp(); |
1628 ProfileFunctionTrieNode* current) { | 1007 |
1629 intptr_t user_tag_index = FindTagFunctionIndex(sample->user_tag()); | 1008 // This is our first pass over the sample buffer, use this as an |
1630 if (user_tag_index >= 0) { | 1009 // opportunity to determine the min and max time ranges of this profile. |
1631 current = current->GetChild(user_tag_index); | 1010 UpdateMinMaxTimes(timestamp); |
1632 // Give the tag a tick. | 1011 |
| 1012 // Make sure VM tag exists. |
| 1013 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { |
| 1014 RegisterProfileCodeTag(VMTag::kNativeTagId); |
| 1015 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { |
| 1016 RegisterProfileCodeTag(VMTag::kRuntimeTagId); |
| 1017 } |
| 1018 RegisterProfileCodeTag(sample->vm_tag()); |
| 1019 // Make sure user tag exists. |
| 1020 RegisterProfileCodeTag(sample->user_tag()); |
| 1021 |
| 1022 // Make sure that a ProfileCode objects exist for all pcs in the sample |
| 1023 // and tick each one. |
| 1024 for (intptr_t i = 0; i < sample->length(); i++) { |
| 1025 const uword pc = sample->At(i); |
| 1026 ASSERT(pc != 0); |
| 1027 ProfileCode* code = RegisterProfileCode(pc, timestamp); |
| 1028 ASSERT(code != NULL); |
| 1029 code->Tick(pc, (i == 0), i); |
| 1030 } |
| 1031 } |
| 1032 } |
| 1033 |
| 1034 void FinalizeCodeIndexes() { |
| 1035 ScopeTimer sw("ProfileBuilder::FinalizeCodeIndexes", FLAG_trace_profiler); |
| 1036 ProfileCodeTable* live_table = profile_->live_code_; |
| 1037 ProfileCodeTable* dead_table = profile_->dead_code_; |
| 1038 ProfileCodeTable* tag_table = profile_->tag_code_; |
| 1039 const intptr_t dead_code_index_offset = live_table->length(); |
| 1040 const intptr_t tag_code_index_offset = |
| 1041 dead_table->length() + dead_code_index_offset; |
| 1042 |
| 1043 profile_->dead_code_index_offset_ = dead_code_index_offset; |
| 1044 profile_->tag_code_index_offset_ = tag_code_index_offset; |
| 1045 |
| 1046 for (intptr_t i = 0; i < live_table->length(); i++) { |
| 1047 const intptr_t index = i; |
| 1048 ProfileCode* code = live_table->At(i); |
| 1049 ASSERT(code != NULL); |
| 1050 code->set_code_table_index(index); |
| 1051 } |
| 1052 |
| 1053 for (intptr_t i = 0; i < dead_table->length(); i++) { |
| 1054 const intptr_t index = dead_code_index_offset + i; |
| 1055 ProfileCode* code = dead_table->At(i); |
| 1056 ASSERT(code != NULL); |
| 1057 code->set_code_table_index(index); |
| 1058 } |
| 1059 |
| 1060 for (intptr_t i = 0; i < tag_table->length(); i++) { |
| 1061 const intptr_t index = tag_code_index_offset + i; |
| 1062 ProfileCode* code = tag_table->At(i); |
| 1063 ASSERT(code != NULL); |
| 1064 code->set_code_table_index(index); |
| 1065 } |
| 1066 } |
| 1067 |
| 1068 void BuildFunctionTable() { |
| 1069 ScopeTimer sw("ProfileBuilder::BuildFunctionTable", FLAG_trace_profiler); |
| 1070 ProfileCodeTable* live_table = profile_->live_code_; |
| 1071 ProfileCodeTable* dead_table = profile_->dead_code_; |
| 1072 ProfileCodeTable* tag_table = profile_->tag_code_; |
| 1073 ProfileFunctionTable* function_table = profile_->functions_; |
| 1074 for (intptr_t i = 0; i < live_table->length(); i++) { |
| 1075 ProfileCode* code = live_table->At(i); |
| 1076 ASSERT(code != NULL); |
| 1077 code->SetFunctionAndName(function_table); |
| 1078 } |
| 1079 |
| 1080 for (intptr_t i = 0; i < dead_table->length(); i++) { |
| 1081 ProfileCode* code = dead_table->At(i); |
| 1082 ASSERT(code != NULL); |
| 1083 code->SetFunctionAndName(function_table); |
| 1084 } |
| 1085 |
| 1086 for (intptr_t i = 0; i < tag_table->length(); i++) { |
| 1087 ProfileCode* code = tag_table->At(i); |
| 1088 ASSERT(code != NULL); |
| 1089 code->SetFunctionAndName(function_table); |
| 1090 } |
| 1091 } |
| 1092 |
| 1093 void BuildCodeTrie(Profile::TrieKind kind) { |
| 1094 ProfileCodeTrieNode* root = |
| 1095 new ProfileCodeTrieNode(GetProfileCodeTagIndex(VMTag::kRootTagId)); |
| 1096 if (IsInclusiveTrie(kind)) { |
| 1097 BuildInclusiveCodeTrie(root); |
| 1098 } else { |
| 1099 BuildExclusiveCodeTrie(root); |
| 1100 } |
| 1101 root->SortChildren(); |
| 1102 profile_->roots_[static_cast<intptr_t>(kind)] = root; |
| 1103 } |
| 1104 |
| 1105 void BuildInclusiveCodeTrie(ProfileCodeTrieNode* root) { |
| 1106 ScopeTimer sw("ProfileBuilder::BuildInclusiveCodeTrie", |
| 1107 FLAG_trace_profiler); |
| 1108 for (intptr_t i = 0; i < samples_->length(); i++) { |
| 1109 ProcessedSample* sample = samples_->At(i); |
| 1110 |
| 1111 // Tick the root. |
| 1112 ProfileCodeTrieNode* current = root; |
1633 current->Tick(); | 1113 current->Tick(); |
1634 } | 1114 |
1635 return current; | 1115 // VM & User tags. |
1636 } | 1116 current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
1637 | 1117 |
1638 | 1118 // Truncated tag. |
1639 ProfileFunctionTrieNode* AppendTruncatedTag( | 1119 if (sample->truncated()) { |
1640 ProfileFunctionTrieNode* current) { | 1120 current = AppendTruncatedTag(current); |
1641 intptr_t truncated_tag_index = FindTagFunctionIndex(VMTag::kTruncatedTagId); | 1121 } |
1642 ASSERT(truncated_tag_index >= 0); | 1122 |
1643 current = current->GetChild(truncated_tag_index); | 1123 // Walk the sampled PCs. |
1644 current->Tick(); | 1124 for (intptr_t j = sample->length() - 1; j >= 0; j--) { |
1645 return current; | 1125 ASSERT(sample->At(j) != 0); |
1646 } | 1126 intptr_t index = |
1647 | 1127 GetProfileCodeIndex(sample->At(j), sample->timestamp()); |
1648 void InclusiveTickTruncatedTag() { | 1128 ASSERT(index >= 0); |
1649 intptr_t index = tag_code_table_->FindIndex(VMTag::kTruncatedTagId); | 1129 current = current->GetChild(index); |
1650 CodeRegion* region = tag_code_table_->At(index); | 1130 current->Tick(); |
1651 ProfileFunction* function = region->function(); | 1131 } |
1652 function->inc_inclusive_ticks(); | 1132 } |
1653 } | 1133 } |
1654 | 1134 |
1655 ProfileFunctionTrieNode* AppendVMTag(ProcessedSample* sample, | 1135 void BuildExclusiveCodeTrie(ProfileCodeTrieNode* root) { |
1656 ProfileFunctionTrieNode* current) { | 1136 ScopeTimer sw("ProfileBuilder::BuildExclusiveCodeTrie", |
1657 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | 1137 FLAG_trace_profiler); |
1658 // Insert a dummy kNativeTagId node. | 1138 for (intptr_t i = 0; i < samples_->length(); i++) { |
1659 intptr_t tag_index = FindTagFunctionIndex(VMTag::kNativeTagId); | 1139 ProcessedSample* sample = samples_->At(i); |
1660 current = current->GetChild(tag_index); | 1140 |
1661 // Give the tag a tick. | 1141 // Tick the root. |
| 1142 ProfileCodeTrieNode* current = root; |
1662 current->Tick(); | 1143 current->Tick(); |
1663 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1144 |
1664 // Insert a dummy kRuntimeTagId node. | 1145 // VM & User tags. |
1665 intptr_t tag_index = FindTagFunctionIndex(VMTag::kRuntimeTagId); | 1146 current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
1666 current = current->GetChild(tag_index); | 1147 |
1667 // Give the tag a tick. | 1148 // Walk the sampled PCs. |
| 1149 for (intptr_t j = 0; j < sample->length(); j++) { |
| 1150 ASSERT(sample->At(j) != 0); |
| 1151 intptr_t index = |
| 1152 GetProfileCodeIndex(sample->At(j), sample->timestamp()); |
| 1153 ASSERT(index >= 0); |
| 1154 current = current->GetChild(index); |
| 1155 |
| 1156 if (j == 0) { |
| 1157 // Executing PC. |
| 1158 if (!sample->first_frame_executing() || vm_tags_emitted()) { |
| 1159 // Only tick if this isn't an exit frame or VM tags are emitted. |
| 1160 current->Tick(); |
| 1161 } |
| 1162 } else { |
| 1163 // Caller PCs. |
| 1164 current->Tick(); |
| 1165 } |
| 1166 |
| 1167 current->Tick(); |
| 1168 } |
| 1169 |
| 1170 // Truncated tag. |
| 1171 if (sample->truncated()) { |
| 1172 current = AppendTruncatedTag(current); |
| 1173 } |
| 1174 } |
| 1175 } |
| 1176 |
| 1177 void BuildFunctionTrie(Profile::TrieKind kind) { |
| 1178 ProfileFunctionTrieNode* root = |
| 1179 new ProfileFunctionTrieNode( |
| 1180 GetProfileFunctionTagIndex(VMTag::kRootTagId)); |
| 1181 // We tick the functions while building the trie, but, we don't want to do |
| 1182 // it for both tries, just one. |
| 1183 tick_functions_ = IsInclusiveTrie(kind); |
| 1184 if (IsInclusiveTrie(kind)) { |
| 1185 BuildInclusiveFunctionTrie(root); |
| 1186 } else { |
| 1187 BuildExclusiveFunctionTrie(root); |
| 1188 } |
| 1189 root->SortChildren(); |
| 1190 profile_->roots_[static_cast<intptr_t>(kind)] = root; |
| 1191 } |
| 1192 |
| 1193 void BuildInclusiveFunctionTrie(ProfileFunctionTrieNode* root) { |
| 1194 ScopeTimer sw("ProfileBuilder::BuildInclusiveFunctionTrie", |
| 1195 FLAG_trace_profiler); |
| 1196 for (intptr_t i = 0; i < samples_->length(); i++) { |
| 1197 ProcessedSample* sample = samples_->At(i); |
| 1198 |
| 1199 // Tick the root. |
| 1200 ProfileFunctionTrieNode* current = root; |
1668 current->Tick(); | 1201 current->Tick(); |
1669 } else { | 1202 |
1670 intptr_t tag_index = FindTagFunctionIndex(sample->vm_tag()); | 1203 // VM & User tags. |
1671 current = current->GetChild(tag_index); | 1204 current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
1672 // Give the tag a tick. | 1205 |
| 1206 // Truncated tag. |
| 1207 if (sample->truncated()) { |
| 1208 current = AppendTruncatedTag(current); |
| 1209 InclusiveTickTruncatedTag(); |
| 1210 } |
| 1211 |
| 1212 // Walk the sampled PCs. |
| 1213 for (intptr_t j = sample->length() - 1; j >= 0; j--) { |
| 1214 ASSERT(sample->At(j) != 0); |
| 1215 current = ProcessFunctionPC( |
| 1216 sample->At(j), |
| 1217 sample->timestamp(), |
| 1218 current, |
| 1219 i, |
| 1220 (j == 0), |
| 1221 sample->first_frame_executing(), |
| 1222 true); |
| 1223 } |
| 1224 } |
| 1225 } |
| 1226 |
| 1227 void BuildExclusiveFunctionTrie(ProfileFunctionTrieNode* root) { |
| 1228 ScopeTimer sw("ProfileBuilder::BuildExclusiveFunctionTrie", |
| 1229 FLAG_trace_profiler); |
| 1230 for (intptr_t i = 0; i < samples_->length(); i++) { |
| 1231 ProcessedSample* sample = samples_->At(i); |
| 1232 |
| 1233 // Tick the root. |
| 1234 ProfileFunctionTrieNode* current = root; |
1673 current->Tick(); | 1235 current->Tick(); |
1674 } | 1236 |
1675 return current; | 1237 // VM & User tags. |
1676 } | 1238 current = AppendTags(sample->vm_tag(), sample->user_tag(), current); |
1677 | 1239 |
1678 ProfileFunctionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( | 1240 // Walk the sampled PCs. |
1679 ProcessedSample* sample, ProfileFunctionTrieNode* current) { | 1241 for (intptr_t j = 0; j < sample->length(); j++) { |
1680 // Only Native and Runtime entries have a second VM tag. | 1242 ASSERT(sample->At(j) != 0); |
1681 if (!VMTag::IsNativeEntryTag(sample->vm_tag()) && | 1243 current = ProcessFunctionPC( |
1682 !VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1244 sample->At(j), |
1683 return current; | 1245 sample->timestamp(), |
1684 } | 1246 current, |
1685 intptr_t tag_index = FindTagFunctionIndex(sample->vm_tag()); | 1247 i, |
1686 current = current->GetChild(tag_index); | 1248 (j == 0), |
1687 // Give the tag a tick. | 1249 sample->first_frame_executing(), |
1688 current->Tick(); | 1250 false); |
1689 return current; | 1251 } |
1690 } | 1252 |
1691 | 1253 // Truncated tag. |
1692 ProfileFunctionTrieNode* AppendVMTags(ProcessedSample* sample, | 1254 if (sample->truncated()) { |
1693 ProfileFunctionTrieNode* current) { | 1255 current = AppendTruncatedTag(current); |
1694 current = AppendVMTag(sample, current); | 1256 } |
1695 current = AppendSpecificNativeRuntimeEntryVMTag(sample, current); | 1257 } |
1696 return current; | 1258 } |
1697 } | 1259 |
1698 | 1260 ProfileFunctionTrieNode* ProcessFunctionPC( |
1699 ProfileFunctionTrieNode* AppendTags(ProcessedSample* sample, | 1261 uword pc, |
1700 ProfileFunctionTrieNode* current) { | 1262 int64_t timestamp, |
1701 // None. | 1263 ProfileFunctionTrieNode* current, |
1702 if (tag_order() == ProfilerService::kNoTags) { | 1264 intptr_t inclusive_serial, |
1703 return current; | 1265 bool top_frame, |
1704 } | 1266 bool top_frame_executing, |
1705 // User first. | 1267 bool inclusive_tree) { |
1706 if ((tag_order() == ProfilerService::kUserVM) || | 1268 ProfileCode* profile_code = GetProfileCode(pc, timestamp); |
1707 (tag_order() == ProfilerService::kUser)) { | 1269 ASSERT(profile_code != NULL); |
1708 current = AppendUserTag(sample, current); | 1270 const char* code_name = profile_code->name(); |
1709 // Only user. | 1271 if (code_name == NULL) { |
1710 if (tag_order() == ProfilerService::kUser) { | 1272 code_name = ""; |
1711 return current; | 1273 } |
1712 } | 1274 intptr_t code_index = profile_code->code_table_index(); |
1713 return AppendVMTags(sample, current); | 1275 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; | 1276 GrowableArray<Function*> inlined_functions; |
1768 if (!code.IsNull()) { | 1277 if (!code.IsNull()) { |
1769 intptr_t offset = pc - code.EntryPoint(); | 1278 intptr_t offset = pc - code.EntryPoint(); |
1770 code.GetInlinedFunctionsAt(offset, &inlined_functions); | 1279 code.GetInlinedFunctionsAt(offset, &inlined_functions); |
1771 } | 1280 } |
1772 if (code.IsNull() || (inlined_functions.length() == 0)) { | 1281 if (code.IsNull() || (inlined_functions.length() == 0)) { |
1773 // No inlined functions. | 1282 // No inlined functions. |
1774 ProfileFunction* function = region->function(); | 1283 ProfileFunction* function = profile_code->function(); |
1775 ASSERT(function != NULL); | 1284 ASSERT(function != NULL); |
1776 current = ProcessFunction(function, | 1285 current = ProcessFunction(function, |
1777 current, | 1286 current, |
1778 inclusive_serial, | 1287 inclusive_serial, |
1779 top_frame, | 1288 top_frame, |
1780 exit_frame, | 1289 top_frame_executing, |
1781 code_index); | 1290 code_index); |
1782 return current; | 1291 return current; |
1783 } | 1292 } |
1784 | 1293 |
1785 if (inclusive_tree_) { | 1294 if (inclusive_tree) { |
1786 for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) { | 1295 for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) { |
1787 Function* inlined_function = inlined_functions[i]; | 1296 Function* inlined_function = inlined_functions[i]; |
1788 ASSERT(inlined_function != NULL); | 1297 ASSERT(inlined_function != NULL); |
1789 ASSERT(!inlined_function->IsNull()); | 1298 ASSERT(!inlined_function->IsNull()); |
1790 current = ProcessInlinedFunction(inlined_function, | 1299 current = ProcessInlinedFunction(inlined_function, |
1791 current, | 1300 current, |
1792 inclusive_serial, | 1301 inclusive_serial, |
1793 top_frame, | 1302 top_frame, |
1794 exit_frame, | 1303 top_frame_executing, |
1795 code_index); | 1304 code_index); |
1796 top_frame = false; | 1305 top_frame = false; |
1797 } | 1306 } |
1798 } else { | 1307 } else { |
1799 for (intptr_t i = 0; i < inlined_functions.length(); i++) { | 1308 for (intptr_t i = 0; i < inlined_functions.length(); i++) { |
1800 Function* inlined_function = inlined_functions[i]; | 1309 Function* inlined_function = inlined_functions[i]; |
1801 ASSERT(inlined_function != NULL); | 1310 ASSERT(inlined_function != NULL); |
1802 ASSERT(!inlined_function->IsNull()); | 1311 ASSERT(!inlined_function->IsNull()); |
1803 current = ProcessInlinedFunction(inlined_function, | 1312 current = ProcessInlinedFunction(inlined_function, |
1804 current, | 1313 current, |
1805 inclusive_serial, | 1314 inclusive_serial, |
1806 top_frame, | 1315 top_frame, |
1807 exit_frame, | 1316 top_frame_executing, |
1808 code_index); | 1317 code_index); |
1809 top_frame = false; | 1318 top_frame = false; |
1810 } | 1319 } |
1811 } | 1320 } |
1812 | 1321 |
1813 return current; | 1322 return current; |
1814 } | 1323 } |
1815 | 1324 |
1816 ProfileFunctionTrieNode* ProcessInlinedFunction( | 1325 ProfileFunctionTrieNode* ProcessInlinedFunction( |
1817 Function* inlined_function, | 1326 Function* inlined_function, |
1818 ProfileFunctionTrieNode* current, | 1327 ProfileFunctionTrieNode* current, |
1819 intptr_t inclusive_serial, | 1328 intptr_t inclusive_serial, |
1820 bool top_frame, | 1329 bool top_frame, |
1821 bool exit_frame, | 1330 bool top_frame_executing, |
1822 intptr_t code_index) { | 1331 intptr_t code_index) { |
1823 ProfileFunction* function = | 1332 ProfileFunctionTable* function_table = profile_->functions_; |
1824 function_table_->LookupOrAdd(*inlined_function); | 1333 ProfileFunction* function = function_table->LookupOrAdd(*inlined_function); |
1825 ASSERT(function != NULL); | 1334 ASSERT(function != NULL); |
1826 return ProcessFunction(function, | 1335 return ProcessFunction(function, |
1827 current, | 1336 current, |
1828 inclusive_serial, | 1337 inclusive_serial, |
1829 top_frame, | 1338 top_frame, |
1830 exit_frame, | 1339 top_frame_executing, |
1831 code_index); | 1340 code_index); |
1832 } | 1341 } |
1833 | 1342 |
1834 ProfileFunctionTrieNode* ProcessFunction(ProfileFunction* function, | 1343 ProfileFunctionTrieNode* ProcessFunction(ProfileFunction* function, |
1835 ProfileFunctionTrieNode* current, | 1344 ProfileFunctionTrieNode* current, |
1836 intptr_t inclusive_serial, | 1345 intptr_t inclusive_serial, |
1837 bool top_frame, | 1346 bool top_frame, |
1838 bool exit_frame, | 1347 bool top_frame_executing, |
1839 intptr_t code_index) { | 1348 intptr_t code_index) { |
1840 const bool exclusive = top_frame && !exit_frame; | 1349 const bool exclusive = top_frame && top_frame_executing; |
1841 if (!inclusive_tree_) { | 1350 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); | 1351 function->Tick(exclusive, exclusive ? -1 : inclusive_serial); |
1845 } | 1352 } |
1846 function->AddCodeObjectIndex(code_index); | 1353 function->AddProfileCode(code_index); |
1847 current = current->GetChild(function->index()); | 1354 current = current->GetChild(function->table_index()); |
1848 current->AddCodeObjectIndex(code_index); | 1355 current->AddCodeObjectIndex(code_index); |
1849 if (top_frame) { | 1356 if (top_frame) { |
1850 if (!exit_frame || vm_tags_emitted()) { | 1357 if (top_frame_executing || vm_tags_emitted()) { |
1851 // Only tick if this isn't an exit frame or VM tags are emitted. | 1358 // Only tick if this function is using CPU time or VM tags are emitted. |
1852 current->Tick(); | 1359 current->Tick(); |
1853 } | 1360 } |
1854 } else { | 1361 } else { |
1855 current->Tick(); | 1362 current->Tick(); |
1856 } | 1363 } |
1857 return current; | 1364 return current; |
1858 } | 1365 } |
1859 | 1366 |
1860 ProfileFunctionTrieNode* exclusive_root_; | 1367 // Tick the truncated tag's inclusive tick count. |
1861 ProfileFunctionTrieNode* inclusive_root_; | 1368 void InclusiveTickTruncatedTag() { |
1862 ProfileFunctionTable* function_table_; | 1369 ProfileCodeTable* tag_table = profile_->tag_code_; |
1863 bool inclusive_tree_; | 1370 intptr_t index = tag_table->FindCodeIndexForPC(VMTag::kTruncatedTagId); |
1864 }; | 1371 ASSERT(index >= 0); |
| 1372 ProfileCode* code = tag_table->At(index); |
| 1373 code->IncInclusiveTicks(); |
| 1374 ASSERT(code != NULL); |
| 1375 ProfileFunction* function = code->function(); |
| 1376 function->IncInclusiveTicks(); |
| 1377 } |
1865 | 1378 |
1866 | 1379 |
1867 class CodeRegionTrieNode : public ZoneAllocated { | 1380 // Tag append functions are overloaded for |ProfileCodeTrieNode| and |
1868 public: | 1381 // |ProfileFunctionTrieNode| types. |
1869 explicit CodeRegionTrieNode(intptr_t code_region_index) | 1382 |
1870 : code_region_index_(code_region_index), | 1383 // ProfileCodeTrieNode |
1871 count_(0), | 1384 ProfileCodeTrieNode* AppendUserTag(uword user_tag, |
1872 children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) { | 1385 ProfileCodeTrieNode* current) { |
| 1386 intptr_t user_tag_index = GetProfileCodeTagIndex(user_tag); |
| 1387 if (user_tag_index >= 0) { |
| 1388 current = current->GetChild(user_tag_index); |
| 1389 current->Tick(); |
| 1390 } |
| 1391 return current; |
1873 } | 1392 } |
1874 | 1393 |
1875 void Tick() { | 1394 ProfileCodeTrieNode* AppendTruncatedTag(ProfileCodeTrieNode* current) { |
1876 ASSERT(code_region_index_ >= 0); | 1395 intptr_t truncated_tag_index = |
1877 count_++; | 1396 GetProfileCodeTagIndex(VMTag::kTruncatedTagId); |
| 1397 ASSERT(truncated_tag_index >= 0); |
| 1398 current = current->GetChild(truncated_tag_index); |
| 1399 current->Tick(); |
| 1400 return current; |
1878 } | 1401 } |
1879 | 1402 |
1880 intptr_t count() const { | 1403 ProfileCodeTrieNode* AppendVMTag(uword vm_tag, |
1881 ASSERT(code_region_index_ >= 0); | 1404 ProfileCodeTrieNode* current) { |
1882 return count_; | 1405 if (VMTag::IsNativeEntryTag(vm_tag)) { |
1883 } | 1406 // Insert a dummy kNativeTagId node. |
1884 | 1407 intptr_t tag_index = GetProfileCodeTagIndex(VMTag::kNativeTagId); |
1885 intptr_t code_region_index() const { | 1408 current = current->GetChild(tag_index); |
1886 return code_region_index_; | 1409 // Give the tag a tick. |
1887 } | 1410 current->Tick(); |
1888 | 1411 } else if (VMTag::IsRuntimeEntryTag(vm_tag)) { |
1889 ZoneGrowableArray<CodeRegionTrieNode*>& children() const { | 1412 // Insert a dummy kRuntimeTagId node. |
1890 return *children_; | 1413 intptr_t tag_index = GetProfileCodeTagIndex(VMTag::kRuntimeTagId); |
1891 } | 1414 current = current->GetChild(tag_index); |
1892 | 1415 // Give the tag a tick. |
1893 CodeRegionTrieNode* GetChild(intptr_t child_code_region_index) { | 1416 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 { | 1417 } else { |
1912 // Add to end. | 1418 intptr_t tag_index = GetProfileCodeTagIndex(vm_tag); |
1913 children_->Add(child); | 1419 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. | 1420 // Give the tag a tick. |
2057 current->Tick(); | 1421 current->Tick(); |
2058 } | 1422 } |
2059 return current; | 1423 return current; |
2060 } | 1424 } |
2061 | 1425 |
2062 CodeRegionTrieNode* AppendTruncatedTag(CodeRegionTrieNode* current) { | 1426 ProfileCodeTrieNode* AppendSpecificNativeRuntimeEntryVMTag( |
2063 intptr_t truncated_tag_index = FindTagIndex(VMTag::kTruncatedTagId); | 1427 uword vm_tag, ProfileCodeTrieNode* current) { |
| 1428 // Only Native and Runtime entries have a second VM tag. |
| 1429 if (!VMTag::IsNativeEntryTag(vm_tag) && |
| 1430 !VMTag::IsRuntimeEntryTag(vm_tag)) { |
| 1431 return current; |
| 1432 } |
| 1433 intptr_t tag_index = GetProfileCodeTagIndex(vm_tag); |
| 1434 current = current->GetChild(tag_index); |
| 1435 // Give the tag a tick. |
| 1436 current->Tick(); |
| 1437 return current; |
| 1438 } |
| 1439 |
| 1440 ProfileCodeTrieNode* AppendVMTags(uword vm_tag, |
| 1441 ProfileCodeTrieNode* current) { |
| 1442 current = AppendVMTag(vm_tag, current); |
| 1443 current = AppendSpecificNativeRuntimeEntryVMTag(vm_tag, current); |
| 1444 return current; |
| 1445 } |
| 1446 |
| 1447 ProfileCodeTrieNode* AppendTags(uword vm_tag, |
| 1448 uword user_tag, |
| 1449 ProfileCodeTrieNode* current) { |
| 1450 // None. |
| 1451 if (tag_order() == Profile::kNoTags) { |
| 1452 return current; |
| 1453 } |
| 1454 // User first. |
| 1455 if ((tag_order() == Profile::kUserVM) || |
| 1456 (tag_order() == Profile::kUser)) { |
| 1457 current = AppendUserTag(user_tag, current); |
| 1458 // Only user. |
| 1459 if (tag_order() == Profile::kUser) { |
| 1460 return current; |
| 1461 } |
| 1462 return AppendVMTags(vm_tag, current); |
| 1463 } |
| 1464 // VM first. |
| 1465 ASSERT((tag_order() == Profile::kVMUser) || |
| 1466 (tag_order() == Profile::kVM)); |
| 1467 current = AppendVMTags(vm_tag, current); |
| 1468 // Only VM. |
| 1469 if (tag_order() == Profile::kVM) { |
| 1470 return current; |
| 1471 } |
| 1472 return AppendUserTag(user_tag, current); |
| 1473 } |
| 1474 |
| 1475 // ProfileFunctionTrieNode |
| 1476 ProfileFunctionTrieNode* AppendUserTag(uword user_tag, |
| 1477 ProfileFunctionTrieNode* current) { |
| 1478 intptr_t user_tag_index = GetProfileFunctionTagIndex(user_tag); |
| 1479 if (user_tag_index >= 0) { |
| 1480 current = current->GetChild(user_tag_index); |
| 1481 current->Tick(); |
| 1482 } |
| 1483 return current; |
| 1484 } |
| 1485 |
| 1486 ProfileFunctionTrieNode* AppendTruncatedTag( |
| 1487 ProfileFunctionTrieNode* current) { |
| 1488 intptr_t truncated_tag_index = |
| 1489 GetProfileFunctionTagIndex(VMTag::kTruncatedTagId); |
2064 ASSERT(truncated_tag_index >= 0); | 1490 ASSERT(truncated_tag_index >= 0); |
2065 current = current->GetChild(truncated_tag_index); | 1491 current = current->GetChild(truncated_tag_index); |
2066 current->Tick(); | 1492 current->Tick(); |
2067 return current; | 1493 return current; |
2068 } | 1494 } |
2069 | 1495 |
2070 CodeRegionTrieNode* AppendVMTag(ProcessedSample* sample, | 1496 ProfileFunctionTrieNode* AppendVMTag(uword vm_tag, |
2071 CodeRegionTrieNode* current) { | 1497 ProfileFunctionTrieNode* current) { |
2072 if (VMTag::IsNativeEntryTag(sample->vm_tag())) { | 1498 if (VMTag::IsNativeEntryTag(vm_tag)) { |
2073 // Insert a dummy kNativeTagId node. | 1499 // Insert a dummy kNativeTagId node. |
2074 intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId); | 1500 intptr_t tag_index = GetProfileFunctionTagIndex(VMTag::kNativeTagId); |
2075 current = current->GetChild(tag_index); | 1501 current = current->GetChild(tag_index); |
2076 // Give the tag a tick. | 1502 // Give the tag a tick. |
2077 current->Tick(); | 1503 current->Tick(); |
2078 } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1504 } else if (VMTag::IsRuntimeEntryTag(vm_tag)) { |
2079 // Insert a dummy kRuntimeTagId node. | 1505 // Insert a dummy kRuntimeTagId node. |
2080 intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId); | 1506 intptr_t tag_index = GetProfileFunctionTagIndex(VMTag::kRuntimeTagId); |
2081 current = current->GetChild(tag_index); | 1507 current = current->GetChild(tag_index); |
2082 // Give the tag a tick. | 1508 // Give the tag a tick. |
2083 current->Tick(); | 1509 current->Tick(); |
2084 } else { | 1510 } else { |
2085 intptr_t tag_index = FindTagIndex(sample->vm_tag()); | 1511 intptr_t tag_index = GetProfileFunctionTagIndex(vm_tag); |
2086 current = current->GetChild(tag_index); | 1512 current = current->GetChild(tag_index); |
2087 // Give the tag a tick. | 1513 // Give the tag a tick. |
2088 current->Tick(); | 1514 current->Tick(); |
2089 } | 1515 } |
2090 return current; | 1516 return current; |
2091 } | 1517 } |
2092 | 1518 |
2093 CodeRegionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( | 1519 ProfileFunctionTrieNode* AppendSpecificNativeRuntimeEntryVMTag( |
2094 ProcessedSample* sample, CodeRegionTrieNode* current) { | 1520 uword vm_tag, ProfileFunctionTrieNode* current) { |
2095 // Only Native and Runtime entries have a second VM tag. | 1521 // Only Native and Runtime entries have a second VM tag. |
2096 if (!VMTag::IsNativeEntryTag(sample->vm_tag()) && | 1522 if (!VMTag::IsNativeEntryTag(vm_tag) && |
2097 !VMTag::IsRuntimeEntryTag(sample->vm_tag())) { | 1523 !VMTag::IsRuntimeEntryTag(vm_tag)) { |
2098 return current; | 1524 return current; |
2099 } | 1525 } |
2100 intptr_t tag_index = FindTagIndex(sample->vm_tag()); | 1526 intptr_t tag_index = GetProfileFunctionTagIndex(vm_tag); |
2101 current = current->GetChild(tag_index); | 1527 current = current->GetChild(tag_index); |
2102 // Give the tag a tick. | 1528 // Give the tag a tick. |
2103 current->Tick(); | 1529 current->Tick(); |
2104 return current; | 1530 return current; |
2105 } | 1531 } |
2106 | 1532 |
2107 CodeRegionTrieNode* AppendVMTags(ProcessedSample* sample, | 1533 ProfileFunctionTrieNode* AppendVMTags(uword vm_tag, |
2108 CodeRegionTrieNode* current) { | 1534 ProfileFunctionTrieNode* current) { |
2109 current = AppendVMTag(sample, current); | 1535 current = AppendVMTag(vm_tag, current); |
2110 current = AppendSpecificNativeRuntimeEntryVMTag(sample, current); | 1536 current = AppendSpecificNativeRuntimeEntryVMTag(vm_tag, current); |
2111 return current; | 1537 return current; |
2112 } | 1538 } |
2113 | 1539 |
2114 CodeRegionTrieNode* AppendTags(ProcessedSample* sample, | 1540 ProfileFunctionTrieNode* AppendTags(uword vm_tag, |
2115 CodeRegionTrieNode* current) { | 1541 uword user_tag, |
| 1542 ProfileFunctionTrieNode* current) { |
2116 // None. | 1543 // None. |
2117 if (tag_order() == ProfilerService::kNoTags) { | 1544 if (tag_order() == Profile::kNoTags) { |
2118 return current; | 1545 return current; |
2119 } | 1546 } |
2120 // User first. | 1547 // User first. |
2121 if ((tag_order() == ProfilerService::kUserVM) || | 1548 if ((tag_order() == Profile::kUserVM) || |
2122 (tag_order() == ProfilerService::kUser)) { | 1549 (tag_order() == Profile::kUser)) { |
2123 current = AppendUserTag(sample, current); | 1550 current = AppendUserTag(user_tag, current); |
2124 // Only user. | 1551 // Only user. |
2125 if (tag_order() == ProfilerService::kUser) { | 1552 if (tag_order() == Profile::kUser) { |
2126 return current; | 1553 return current; |
2127 } | 1554 } |
2128 return AppendVMTags(sample, current); | 1555 return AppendVMTags(vm_tag, current); |
2129 } | 1556 } |
2130 // VM first. | 1557 // VM first. |
2131 ASSERT((tag_order() == ProfilerService::kVMUser) || | 1558 ASSERT((tag_order() == Profile::kVMUser) || |
2132 (tag_order() == ProfilerService::kVM)); | 1559 (tag_order() == Profile::kVM)); |
2133 current = AppendVMTags(sample, current); | 1560 current = AppendVMTags(vm_tag, current); |
2134 // Only VM. | 1561 // Only VM. |
2135 if (tag_order() == ProfilerService::kVM) { | 1562 if (tag_order() == Profile::kVM) { |
2136 return current; | 1563 return current; |
2137 } | 1564 } |
2138 return AppendUserTag(sample, current); | 1565 return AppendUserTag(user_tag, current); |
2139 } | 1566 } |
2140 | 1567 |
2141 CodeRegionTrieNode* exclusive_root_; | 1568 intptr_t GetProfileCodeTagIndex(uword tag) { |
2142 CodeRegionTrieNode* inclusive_root_; | 1569 ProfileCodeTable* tag_table = profile_->tag_code_; |
| 1570 intptr_t index = tag_table->FindCodeIndexForPC(tag); |
| 1571 ASSERT(index >= 0); |
| 1572 ProfileCode* code = tag_table->At(index); |
| 1573 ASSERT(code != NULL); |
| 1574 return code->code_table_index(); |
| 1575 } |
| 1576 |
| 1577 intptr_t GetProfileFunctionTagIndex(uword tag) { |
| 1578 ProfileCodeTable* tag_table = profile_->tag_code_; |
| 1579 intptr_t index = tag_table->FindCodeIndexForPC(tag); |
| 1580 ASSERT(index >= 0); |
| 1581 ProfileCode* code = tag_table->At(index); |
| 1582 ASSERT(code != NULL); |
| 1583 ProfileFunction* function = code->function(); |
| 1584 ASSERT(function != NULL); |
| 1585 return function->table_index(); |
| 1586 } |
| 1587 |
| 1588 intptr_t GetProfileCodeIndex(uword pc, int64_t timestamp) { |
| 1589 return GetProfileCode(pc, timestamp)->code_table_index(); |
| 1590 } |
| 1591 |
| 1592 ProfileCode* GetProfileCode(uword pc, int64_t timestamp) { |
| 1593 ProfileCodeTable* live_table = profile_->live_code_; |
| 1594 ProfileCodeTable* dead_table = profile_->dead_code_; |
| 1595 |
| 1596 intptr_t index = live_table->FindCodeIndexForPC(pc); |
| 1597 ProfileCode* code = NULL; |
| 1598 if (index < 0) { |
| 1599 index = dead_table->FindCodeIndexForPC(pc); |
| 1600 ASSERT(index >= 0); |
| 1601 code = dead_table->At(index); |
| 1602 } else { |
| 1603 code = live_table->At(index); |
| 1604 ASSERT(code != NULL); |
| 1605 if (code->compile_timestamp() > timestamp) { |
| 1606 // Code is newer than sample. Fall back to dead code table. |
| 1607 index = dead_table->FindCodeIndexForPC(pc); |
| 1608 ASSERT(index >= 0); |
| 1609 code = dead_table->At(index); |
| 1610 } |
| 1611 } |
| 1612 |
| 1613 ASSERT(code != NULL); |
| 1614 ASSERT(code->Contains(pc)); |
| 1615 ASSERT(code->compile_timestamp() <= timestamp); |
| 1616 return code; |
| 1617 } |
| 1618 |
| 1619 void RegisterProfileCodeTag(uword tag) { |
| 1620 if (tag == 0) { |
| 1621 // No tag. |
| 1622 return; |
| 1623 } |
| 1624 ProfileCodeTable* tag_table = profile_->tag_code_; |
| 1625 intptr_t index = tag_table->FindCodeIndexForPC(tag); |
| 1626 if (index >= 0) { |
| 1627 // Already created. |
| 1628 return; |
| 1629 } |
| 1630 ProfileCode* code = new ProfileCode(ProfileCode::kTagCode, |
| 1631 tag, |
| 1632 tag + 1, |
| 1633 0, |
| 1634 null_code_); |
| 1635 index = tag_table->InsertCode(code); |
| 1636 ASSERT(index >= 0); |
| 1637 } |
| 1638 |
| 1639 ProfileCode* CreateProfileCodeReused(uword pc) { |
| 1640 ProfileCode* code = new ProfileCode(ProfileCode::kReusedCode, |
| 1641 pc, |
| 1642 pc + 1, |
| 1643 0, |
| 1644 null_code_); |
| 1645 return code; |
| 1646 } |
| 1647 |
| 1648 ProfileCode* CreateProfileCode(uword pc) { |
| 1649 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); |
| 1650 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); |
| 1651 Code& code = Code::Handle(isolate_); |
| 1652 |
| 1653 // Check current isolate for pc. |
| 1654 if (isolate_->heap()->CodeContains(pc)) { |
| 1655 code ^= Code::LookupCode(pc); |
| 1656 if (!code.IsNull()) { |
| 1657 deoptimized_code_->Add(code); |
| 1658 return new ProfileCode(ProfileCode::kDartCode, |
| 1659 code.EntryPoint(), |
| 1660 code.EntryPoint() + code.Size(), |
| 1661 code.compile_timestamp(), |
| 1662 code); |
| 1663 } |
| 1664 return new ProfileCode(ProfileCode::kCollectedCode, |
| 1665 pc, |
| 1666 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| 1667 0, |
| 1668 code); |
| 1669 } |
| 1670 |
| 1671 // Check VM isolate for pc. |
| 1672 if (vm_isolate_->heap()->CodeContains(pc)) { |
| 1673 code ^= Code::LookupCodeInVmIsolate(pc); |
| 1674 if (!code.IsNull()) { |
| 1675 return new ProfileCode(ProfileCode::kDartCode, |
| 1676 code.EntryPoint(), |
| 1677 code.EntryPoint() + code.Size(), |
| 1678 code.compile_timestamp(), |
| 1679 code); |
| 1680 } |
| 1681 return new ProfileCode(ProfileCode::kCollectedCode, |
| 1682 pc, |
| 1683 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| 1684 0, |
| 1685 code); |
| 1686 } |
| 1687 |
| 1688 // Check NativeSymbolResolver for pc. |
| 1689 uintptr_t native_start = 0; |
| 1690 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, |
| 1691 &native_start); |
| 1692 if (native_name == NULL) { |
| 1693 // No native name found. |
| 1694 return new ProfileCode(ProfileCode::kNativeCode, |
| 1695 pc, |
| 1696 pc + 1, |
| 1697 0, |
| 1698 code); |
| 1699 } |
| 1700 ASSERT(pc >= native_start); |
| 1701 ProfileCode* profile_code = |
| 1702 new ProfileCode(ProfileCode::kNativeCode, |
| 1703 native_start, |
| 1704 pc + 1, |
| 1705 0, |
| 1706 code); |
| 1707 profile_code->SetName(native_name); |
| 1708 free(native_name); |
| 1709 return profile_code; |
| 1710 } |
| 1711 |
| 1712 ProfileCode* RegisterProfileCode(uword pc, int64_t timestamp) { |
| 1713 ProfileCodeTable* live_table = profile_->live_code_; |
| 1714 ProfileCodeTable* dead_table = profile_->dead_code_; |
| 1715 |
| 1716 ProfileCode* code = live_table->FindCodeForPC(pc); |
| 1717 if (code == NULL) { |
| 1718 // Code not found. |
| 1719 intptr_t index = live_table->InsertCode(CreateProfileCode(pc)); |
| 1720 ASSERT(index >= 0); |
| 1721 code = live_table->At(index); |
| 1722 if (code->compile_timestamp() <= timestamp) { |
| 1723 // Code was compiled before sample was taken. |
| 1724 return code; |
| 1725 } |
| 1726 // Code was compiled after the sample was taken. Insert code object into |
| 1727 // the dead code table. |
| 1728 index = dead_table->InsertCode(CreateProfileCodeReused(pc)); |
| 1729 ASSERT(index >= 0); |
| 1730 return dead_table->At(index); |
| 1731 } |
| 1732 // Existing code found. |
| 1733 if (code->compile_timestamp() <= timestamp) { |
| 1734 // Code was compiled before sample was taken. |
| 1735 return code; |
| 1736 } |
| 1737 // Code was compiled after the sample was taken. Check if we have an entry |
| 1738 // in the dead code table. |
| 1739 code = dead_table->FindCodeForPC(pc); |
| 1740 if (code != NULL) { |
| 1741 return code; |
| 1742 } |
| 1743 // Create a new dead code entry. |
| 1744 intptr_t index = dead_table->InsertCode(CreateProfileCodeReused(pc)); |
| 1745 ASSERT(index >= 0); |
| 1746 return dead_table->At(index); |
| 1747 } |
| 1748 |
| 1749 Profile::TagOrder tag_order() const { |
| 1750 return tag_order_; |
| 1751 } |
| 1752 |
| 1753 bool vm_tags_emitted() const { |
| 1754 return (tag_order_ == Profile::kUserVM) || |
| 1755 (tag_order_ == Profile::kVMUser) || |
| 1756 (tag_order_ == Profile::kVM); |
| 1757 } |
| 1758 |
| 1759 Isolate* isolate_; |
| 1760 Isolate* vm_isolate_; |
| 1761 SampleFilter* filter_; |
| 1762 Profile::TagOrder tag_order_; |
| 1763 Profile* profile_; |
| 1764 DeoptimizedCodeSet* deoptimized_code_; |
| 1765 const Code& null_code_; |
| 1766 const Function& null_function_; |
| 1767 bool tick_functions_; |
| 1768 |
| 1769 ProcessedSampleBuffer* samples_; |
2143 }; | 1770 }; |
2144 | 1771 |
2145 | 1772 |
| 1773 Profile::Profile(Isolate* isolate) |
| 1774 : isolate_(isolate), |
| 1775 live_code_(NULL), |
| 1776 dead_code_(NULL), |
| 1777 tag_code_(NULL), |
| 1778 functions_(NULL), |
| 1779 dead_code_index_offset_(-1), |
| 1780 tag_code_index_offset_(-1), |
| 1781 min_time_(kMaxInt64), |
| 1782 max_time_(0) { |
| 1783 ASSERT(isolate_ != NULL); |
| 1784 for (intptr_t i = 0; i < kNumTrieKinds; i++) { |
| 1785 roots_[i] = NULL; |
| 1786 } |
| 1787 } |
| 1788 |
| 1789 |
| 1790 void Profile::Build(SampleFilter* filter, TagOrder tag_order) { |
| 1791 ProfileBuilder builder(isolate_, filter, tag_order, this); |
| 1792 builder.Build(); |
| 1793 } |
| 1794 |
| 1795 |
| 1796 ProfileFunction* Profile::GetFunction(intptr_t index) { |
| 1797 ASSERT(functions_ != NULL); |
| 1798 return functions_->At(index); |
| 1799 } |
| 1800 |
| 1801 |
| 1802 ProfileCode* Profile::GetCode(intptr_t index) { |
| 1803 ASSERT(live_code_ != NULL); |
| 1804 ASSERT(dead_code_ != NULL); |
| 1805 ASSERT(tag_code_ != NULL); |
| 1806 ASSERT(dead_code_index_offset_ >= 0); |
| 1807 ASSERT(tag_code_index_offset_ >= 0); |
| 1808 |
| 1809 // Code indexes span three arrays. |
| 1810 // 0 ... |live_code| |
| 1811 // |live_code| ... |dead_code| |
| 1812 // |dead_code| ... |tag_code| |
| 1813 |
| 1814 if (index < dead_code_index_offset_) { |
| 1815 return live_code_->At(index); |
| 1816 } |
| 1817 |
| 1818 if (index < tag_code_index_offset_) { |
| 1819 index -= dead_code_index_offset_; |
| 1820 return dead_code_->At(index); |
| 1821 } |
| 1822 |
| 1823 index -= tag_code_index_offset_; |
| 1824 return tag_code_->At(index); |
| 1825 } |
| 1826 |
| 1827 |
| 1828 ProfileTrieNode* Profile::GetTrieRoot(TrieKind trie_kind) { |
| 1829 return roots_[static_cast<intptr_t>(trie_kind)]; |
| 1830 } |
| 1831 |
| 1832 |
| 1833 void Profile::PrintJSON(JSONStream* stream) { |
| 1834 ScopeTimer sw("Profile::PrintJSON", FLAG_trace_profiler); |
| 1835 JSONObject obj(stream); |
| 1836 obj.AddProperty("type", "_CpuProfile"); |
| 1837 obj.AddProperty("samplePeriod", |
| 1838 static_cast<intptr_t>(FLAG_profile_period)); |
| 1839 obj.AddProperty("stackDepth", |
| 1840 static_cast<intptr_t>(FLAG_profile_depth)); |
| 1841 obj.AddProperty("sampleCount", sample_count()); |
| 1842 obj.AddProperty("timeSpan", MicrosecondsToSeconds(GetTimeSpan())); |
| 1843 { |
| 1844 JSONArray codes(&obj, "codes"); |
| 1845 for (intptr_t i = 0; i < live_code_->length(); i++) { |
| 1846 ProfileCode* code = live_code_->At(i); |
| 1847 ASSERT(code != NULL); |
| 1848 code->PrintToJSONArray(&codes); |
| 1849 } |
| 1850 for (intptr_t i = 0; i < dead_code_->length(); i++) { |
| 1851 ProfileCode* code = dead_code_->At(i); |
| 1852 ASSERT(code != NULL); |
| 1853 code->PrintToJSONArray(&codes); |
| 1854 } |
| 1855 for (intptr_t i = 0; i < tag_code_->length(); i++) { |
| 1856 ProfileCode* code = tag_code_->At(i); |
| 1857 ASSERT(code != NULL); |
| 1858 code->PrintToJSONArray(&codes); |
| 1859 } |
| 1860 } |
| 1861 |
| 1862 { |
| 1863 JSONArray functions(&obj, "functions"); |
| 1864 for (intptr_t i = 0; i < functions_->length(); i++) { |
| 1865 ProfileFunction* function = functions_->At(i); |
| 1866 ASSERT(function != NULL); |
| 1867 function->PrintToJSONArray(&functions); |
| 1868 } |
| 1869 } |
| 1870 { |
| 1871 JSONArray code_trie(&obj, "exclusiveCodeTrie"); |
| 1872 ProfileTrieNode* root = roots_[static_cast<intptr_t>(kExclusiveCode)]; |
| 1873 ASSERT(root != NULL); |
| 1874 root->PrintToJSONArray(&code_trie); |
| 1875 } |
| 1876 { |
| 1877 JSONArray code_trie(&obj, "inclusiveCodeTrie"); |
| 1878 ProfileTrieNode* root = roots_[static_cast<intptr_t>(kInclusiveCode)]; |
| 1879 ASSERT(root != NULL); |
| 1880 root->PrintToJSONArray(&code_trie); |
| 1881 } |
| 1882 { |
| 1883 JSONArray function_trie(&obj, "exclusiveFunctionTrie"); |
| 1884 ProfileTrieNode* root = roots_[static_cast<intptr_t>(kExclusiveFunction)]; |
| 1885 ASSERT(root != NULL); |
| 1886 root->PrintToJSONArray(&function_trie); |
| 1887 } |
| 1888 { |
| 1889 JSONArray function_trie(&obj, "inclusiveFunctionTrie"); |
| 1890 ProfileTrieNode* root = roots_[static_cast<intptr_t>(kInclusiveFunction)]; |
| 1891 ASSERT(root != NULL); |
| 1892 root->PrintToJSONArray(&function_trie); |
| 1893 } |
| 1894 } |
| 1895 |
| 1896 |
2146 class NoAllocationSampleFilter : public SampleFilter { | 1897 class NoAllocationSampleFilter : public SampleFilter { |
2147 public: | 1898 public: |
2148 explicit NoAllocationSampleFilter(Isolate* isolate) | 1899 explicit NoAllocationSampleFilter(Isolate* isolate) |
2149 : SampleFilter(isolate) { | 1900 : SampleFilter(isolate) { |
2150 } | 1901 } |
2151 | 1902 |
2152 bool FilterSample(Sample* sample) { | 1903 bool FilterSample(Sample* sample) { |
2153 return !sample->is_allocation_sample(); | 1904 return !sample->is_allocation_sample(); |
2154 } | 1905 } |
2155 }; | 1906 }; |
2156 | 1907 |
2157 | 1908 |
2158 void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) { | 1909 void ProfilerService::PrintJSON(JSONStream* stream, |
| 1910 Profile::TagOrder tag_order) { |
2159 Isolate* isolate = Isolate::Current(); | 1911 Isolate* isolate = Isolate::Current(); |
2160 // Disable profile interrupts while processing the buffer. | 1912 // Disable profile interrupts while processing the buffer. |
2161 Profiler::EndExecution(isolate); | 1913 Profiler::EndExecution(isolate); |
2162 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 1914 |
2163 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 1915 { |
2164 if (profiler_data == NULL) { | 1916 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
2165 stream->PrintError(kFeatureDisabled, NULL); | 1917 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
2166 return; | 1918 if (profiler_data == NULL) { |
| 1919 stream->PrintError(kFeatureDisabled, NULL); |
| 1920 return; |
| 1921 } |
2167 } | 1922 } |
2168 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 1923 |
2169 ASSERT(sample_buffer != NULL); | |
2170 ScopeTimer sw("ProfilerService::PrintJSON", FLAG_trace_profiler); | |
2171 { | 1924 { |
2172 StackZone zone(isolate); | 1925 StackZone zone(isolate); |
2173 HANDLESCOPE(isolate); | 1926 HANDLESCOPE(isolate); |
| 1927 Profile profile(isolate); |
| 1928 NoAllocationSampleFilter filter(isolate); |
| 1929 profile.Build(&filter, tag_order); |
| 1930 profile.PrintJSON(stream); |
| 1931 } |
2174 | 1932 |
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. | 1933 // Enable profile interrupts. |
2342 Profiler::BeginExecution(isolate); | 1934 Profiler::BeginExecution(isolate); |
2343 } | 1935 } |
2344 | 1936 |
2345 | 1937 |
2346 void ProfilerService::ClearSamples() { | 1938 void ProfilerService::ClearSamples() { |
2347 Isolate* isolate = Isolate::Current(); | 1939 Isolate* isolate = Isolate::Current(); |
2348 | 1940 |
2349 // Disable profile interrupts while processing the buffer. | 1941 // Disable profile interrupts while processing the buffer. |
2350 Profiler::EndExecution(isolate); | 1942 Profiler::EndExecution(isolate); |
2351 | 1943 |
2352 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 1944 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
2353 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 1945 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
2354 if (profiler_data == NULL) { | 1946 if (profiler_data == NULL) { |
2355 return; | 1947 return; |
2356 } | 1948 } |
2357 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 1949 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
2358 ASSERT(sample_buffer != NULL); | 1950 ASSERT(sample_buffer != NULL); |
2359 | 1951 |
2360 ClearProfileVisitor clear_profile(isolate); | 1952 ClearProfileVisitor clear_profile(isolate); |
2361 sample_buffer->VisitSamples(&clear_profile); | 1953 sample_buffer->VisitSamples(&clear_profile); |
2362 | 1954 |
2363 // Enable profile interrupts. | 1955 // Enable profile interrupts. |
2364 Profiler::BeginExecution(isolate); | 1956 Profiler::BeginExecution(isolate); |
2365 } | 1957 } |
2366 | 1958 |
2367 } // namespace dart | 1959 } // namespace dart |
OLD | NEW |