| 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 |