Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 "platform/utils.h" | 5 #include "platform/utils.h" |
| 6 | 6 |
| 7 #include "vm/allocation.h" | 7 #include "vm/allocation.h" |
| 8 #include "vm/atomic.h" | 8 #include "vm/atomic.h" |
| 9 #include "vm/code_patcher.h" | 9 #include "vm/code_patcher.h" |
| 10 #include "vm/isolate.h" | 10 #include "vm/isolate.h" |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 155 return; | 155 return; |
| 156 } | 156 } |
| 157 if (!FLAG_profile) { | 157 if (!FLAG_profile) { |
| 158 return; | 158 return; |
| 159 } | 159 } |
| 160 ASSERT(initialized_); | 160 ASSERT(initialized_); |
| 161 ThreadInterrupter::Unregister(); | 161 ThreadInterrupter::Unregister(); |
| 162 } | 162 } |
| 163 | 163 |
| 164 | 164 |
| 165 class ScopeStopwatch : public ValueObject { | |
| 166 public: | |
| 167 explicit ScopeStopwatch(const char* name) : name_(name) { | |
| 168 start_ = OS::GetCurrentTimeMillis(); | |
|
siva
2014/03/13 17:12:38
This also should be done conditionally under the f
Cutch
2014/03/13 17:33:57
Done.
| |
| 169 } | |
| 170 | |
| 171 intptr_t GetElapsed() const { | |
| 172 intptr_t end = OS::GetCurrentTimeMillis(); | |
| 173 ASSERT(end >= start_); | |
| 174 return end - start_; | |
| 175 } | |
| 176 | |
| 177 ~ScopeStopwatch() { | |
| 178 if (FLAG_trace_profiled_isolates) { | |
| 179 intptr_t elapsed = GetElapsed(); | |
| 180 OS::Print("%s took %" Pd " millis.\n", name_, elapsed); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 private: | |
| 185 const char* name_; | |
| 186 intptr_t start_; | |
| 187 }; | |
| 188 | |
| 189 | |
| 165 struct AddressEntry { | 190 struct AddressEntry { |
| 166 uword pc; | 191 uword pc; |
| 167 intptr_t exclusive_ticks; | 192 intptr_t exclusive_ticks; |
| 168 intptr_t inclusive_ticks; | 193 intptr_t inclusive_ticks; |
| 169 | 194 |
| 170 void tick(bool exclusive) { | 195 void tick(bool exclusive) { |
| 171 if (exclusive) { | 196 if (exclusive) { |
| 172 exclusive_ticks++; | 197 exclusive_ticks++; |
| 173 } else { | 198 } else { |
| 174 inclusive_ticks++; | 199 inclusive_ticks++; |
| 175 } | 200 } |
| 176 } | 201 } |
| 177 }; | 202 }; |
| 178 | 203 |
| 179 struct CallEntry { | 204 struct CallEntry { |
| 180 intptr_t code_table_index; | 205 intptr_t code_table_index; |
| 181 intptr_t count; | 206 intptr_t count; |
| 182 }; | 207 }; |
| 183 | 208 |
| 184 typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); | 209 typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end); |
| 185 | 210 |
| 186 // A region of code. Each region is a kind of code (Dart, Collected, or Native). | 211 // A contiguous address region that holds code. Each CodeRegion has a "kind" |
| 212 // which describes the type of code contained inside the region. Each | |
| 213 // region covers the following interval: [start, end). | |
| 187 class CodeRegion : public ZoneAllocated { | 214 class CodeRegion : public ZoneAllocated { |
| 188 public: | 215 public: |
| 189 enum Kind { | 216 enum Kind { |
| 190 kDartCode, | 217 kDartCode, // Live Dart code. |
| 191 kCollectedCode, | 218 kCollectedCode, // Dead Dart code. |
| 192 kNativeCode | 219 kNativeCode, // Native code. |
| 220 kOverwrittenCode, // Dead Dart code that has been overwritten by kDartCode. | |
|
siva
2014/03/13 17:12:38
I would call this kReusedCode as it is a region th
Cutch
2014/03/13 17:33:57
Done.
| |
| 221 kTagCode, // A special kind of code representing a tag. | |
| 193 }; | 222 }; |
| 194 | 223 |
| 195 CodeRegion(Kind kind, uword start, uword end) : | 224 CodeRegion(Kind kind, uword start, uword end, int64_t timestamp) : |
| 196 kind_(kind), | 225 kind_(kind), |
| 197 start_(start), | 226 start_(start), |
| 198 end_(end), | 227 end_(end), |
| 199 inclusive_ticks_(0), | 228 inclusive_ticks_(0), |
| 200 exclusive_ticks_(0), | 229 exclusive_ticks_(0), |
| 201 inclusive_tick_serial_(0), | 230 inclusive_tick_serial_(0), |
| 202 name_(NULL), | 231 name_(NULL), |
| 232 compile_timestamp_(timestamp), | |
| 233 creation_serial_(0), | |
| 203 address_table_(new ZoneGrowableArray<AddressEntry>()), | 234 address_table_(new ZoneGrowableArray<AddressEntry>()), |
| 204 callers_table_(new ZoneGrowableArray<CallEntry>()), | 235 callers_table_(new ZoneGrowableArray<CallEntry>()), |
| 205 callees_table_(new ZoneGrowableArray<CallEntry>()) { | 236 callees_table_(new ZoneGrowableArray<CallEntry>()) { |
| 206 ASSERT(start_ < end_); | 237 ASSERT(start_ < end_); |
| 207 } | 238 } |
| 208 | 239 |
| 209 ~CodeRegion() { | |
| 210 } | |
| 211 | 240 |
| 212 uword start() const { return start_; } | 241 uword start() const { return start_; } |
| 213 void set_start(uword start) { | 242 void set_start(uword start) { |
| 214 start_ = start; | 243 start_ = start; |
| 215 } | 244 } |
| 216 | 245 |
| 217 uword end() const { return end_; } | 246 uword end() const { return end_; } |
| 218 void set_end(uword end) { | 247 void set_end(uword end) { |
| 219 end_ = end; | 248 end_ = end; |
| 220 } | 249 } |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 234 } | 263 } |
| 235 | 264 |
| 236 bool overlaps(const CodeRegion* other) const { | 265 bool overlaps(const CodeRegion* other) const { |
| 237 ASSERT(other != NULL); | 266 ASSERT(other != NULL); |
| 238 return other->contains(start_) || | 267 return other->contains(start_) || |
| 239 other->contains(end_ - 1) || | 268 other->contains(end_ - 1) || |
| 240 contains(other->start()) || | 269 contains(other->start()) || |
| 241 contains(other->end() - 1); | 270 contains(other->end() - 1); |
| 242 } | 271 } |
| 243 | 272 |
| 273 intptr_t creation_serial() const { return creation_serial_; } | |
| 274 void set_creation_serial(intptr_t serial) { | |
| 275 creation_serial_ = serial; | |
| 276 } | |
| 277 int64_t compile_timestamp() const { return compile_timestamp_; } | |
| 278 void set_compile_timestamp(int64_t timestamp) { | |
| 279 compile_timestamp_ = timestamp; | |
| 280 } | |
| 281 | |
| 244 intptr_t inclusive_ticks() const { return inclusive_ticks_; } | 282 intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
| 245 void set_inclusive_ticks(intptr_t inclusive_ticks) { | 283 void set_inclusive_ticks(intptr_t inclusive_ticks) { |
| 246 inclusive_ticks_ = inclusive_ticks; | 284 inclusive_ticks_ = inclusive_ticks; |
| 247 } | 285 } |
| 248 | 286 |
| 249 intptr_t exclusive_ticks() const { return exclusive_ticks_; } | 287 intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
| 250 void set_exclusive_ticks(intptr_t exclusive_ticks) { | 288 void set_exclusive_ticks(intptr_t exclusive_ticks) { |
| 251 exclusive_ticks_ = exclusive_ticks; | 289 exclusive_ticks_ = exclusive_ticks; |
| 252 } | 290 } |
| 253 | 291 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 265 Kind kind() const { return kind_; } | 303 Kind kind() const { return kind_; } |
| 266 | 304 |
| 267 static const char* KindToCString(Kind kind) { | 305 static const char* KindToCString(Kind kind) { |
| 268 switch (kind) { | 306 switch (kind) { |
| 269 case kDartCode: | 307 case kDartCode: |
| 270 return "Dart"; | 308 return "Dart"; |
| 271 case kCollectedCode: | 309 case kCollectedCode: |
| 272 return "Collected"; | 310 return "Collected"; |
| 273 case kNativeCode: | 311 case kNativeCode: |
| 274 return "Native"; | 312 return "Native"; |
| 313 case kOverwrittenCode: | |
| 314 return "Overwritten"; | |
| 315 case kTagCode: | |
| 316 return "Tag"; | |
| 275 } | 317 } |
| 276 UNREACHABLE(); | 318 UNREACHABLE(); |
| 277 return NULL; | 319 return NULL; |
| 278 } | 320 } |
| 279 | 321 |
| 280 void DebugPrint() const { | 322 void DebugPrint() const { |
| 281 printf("%s [%" Px ", %" Px ") %s\n", KindToCString(kind_), start(), end(), | 323 OS::Print("%s [%" Px ", %" Px ") %"Pd" %"Pd64"\n", |
| 282 name_); | 324 KindToCString(kind_), |
| 325 start(), | |
| 326 end(), | |
| 327 creation_serial_, | |
| 328 compile_timestamp_); | |
| 283 } | 329 } |
| 284 | 330 |
| 285 void AddTickAtAddress(uword pc, bool exclusive, intptr_t serial) { | 331 void Tick(uword pc, bool exclusive, intptr_t serial) { |
| 286 // Assert that exclusive ticks are never passed a valid serial number. | 332 // Assert that exclusive ticks are never passed a valid serial number. |
| 287 ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1))); | 333 ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1))); |
| 288 if (!exclusive && (inclusive_tick_serial_ == serial)) { | 334 if (!exclusive && (inclusive_tick_serial_ == serial)) { |
| 289 // We've already given this code object an inclusive tick for this sample. | 335 // We've already given this code object an inclusive tick for this sample. |
| 290 return; | 336 return; |
| 291 } | 337 } |
| 292 // Tick the code object. | 338 // Tick the code object. |
| 293 if (exclusive) { | 339 if (exclusive) { |
| 294 exclusive_ticks_++; | 340 exclusive_ticks_++; |
| 295 } else { | 341 } else { |
| 342 inclusive_ticks_++; | |
| 296 // Mark the last serial we ticked the inclusive count. | 343 // Mark the last serial we ticked the inclusive count. |
| 297 inclusive_tick_serial_ = serial; | 344 inclusive_tick_serial_ = serial; |
| 298 inclusive_ticks_++; | |
| 299 } | 345 } |
| 300 // Tick the address entry. | 346 TickAddress(pc, exclusive); |
| 301 const intptr_t length = address_table_->length(); | |
| 302 intptr_t i = 0; | |
| 303 for (; i < length; i++) { | |
| 304 AddressEntry& entry = (*address_table_)[i]; | |
| 305 if (entry.pc == pc) { | |
| 306 entry.tick(exclusive); | |
| 307 return; | |
| 308 } | |
| 309 if (entry.pc > pc) { | |
| 310 break; | |
| 311 } | |
| 312 } | |
| 313 AddressEntry entry; | |
| 314 entry.pc = pc; | |
| 315 entry.exclusive_ticks = 0; | |
| 316 entry.inclusive_ticks = 0; | |
| 317 entry.tick(exclusive); | |
| 318 if (i < length) { | |
| 319 // Insert at i. | |
| 320 address_table_->InsertAt(i, entry); | |
| 321 } else { | |
| 322 // Add to end. | |
| 323 address_table_->Add(entry); | |
| 324 } | |
| 325 } | 347 } |
| 326 | 348 |
| 327 void AddCaller(intptr_t index) { | 349 void AddCaller(intptr_t index) { |
| 328 AddCallEntry(callers_table_, index); | 350 AddCallEntry(callers_table_, index); |
| 329 } | 351 } |
| 330 | 352 |
| 331 void AddCallee(intptr_t index) { | 353 void AddCallee(intptr_t index) { |
| 332 AddCallEntry(callees_table_, index); | 354 AddCallEntry(callees_table_, index); |
| 333 } | 355 } |
| 334 | 356 |
| 335 void PrintNativeCode(JSONObject* profile_code_obj) { | 357 void PrintNativeCode(JSONObject* profile_code_obj) { |
| 336 ASSERT(kind() == kNativeCode); | 358 ASSERT(kind() == kNativeCode); |
| 337 JSONObject obj(profile_code_obj, "code"); | 359 JSONObject obj(profile_code_obj, "code"); |
| 338 obj.AddProperty("type", "@Code"); | 360 obj.AddProperty("type", "@Code"); |
| 339 obj.AddProperty("kind", "Native"); | 361 obj.AddProperty("kind", "Native"); |
| 340 obj.AddProperty("name", name()); | 362 obj.AddProperty("name", name()); |
| 341 obj.AddProperty("user_name", name()); | 363 obj.AddProperty("user_name", name()); |
| 342 obj.AddPropertyF("start", "%" Px "", start()); | 364 obj.AddPropertyF("start", "%" Px "", start()); |
| 343 obj.AddPropertyF("end", "%" Px "", end()); | 365 obj.AddPropertyF("end", "%" Px "", end()); |
| 344 obj.AddPropertyF("id", "code/native/%" Px "", start()); | 366 obj.AddPropertyF("id", "/code/native-%" Px "", start()); |
| 345 { | 367 { |
| 346 // Generate a fake function entry. | 368 // Generate a fake function entry. |
| 347 JSONObject func(&obj, "function"); | 369 JSONObject func(&obj, "function"); |
| 348 func.AddProperty("type", "@Function"); | 370 func.AddProperty("type", "@Function"); |
| 349 func.AddPropertyF("id", "native/functions/%" Pd "", start()); | 371 func.AddPropertyF("id", "/functions/native-%" Px "", start()); |
| 350 func.AddProperty("name", name()); | 372 func.AddProperty("name", name()); |
| 351 func.AddProperty("user_name", name()); | 373 func.AddProperty("user_name", name()); |
| 352 func.AddProperty("kind", "Native"); | 374 func.AddProperty("kind", "Native"); |
| 353 } | 375 } |
| 354 } | 376 } |
| 355 | 377 |
| 356 void PrintCollectedCode(JSONObject* profile_code_obj) { | 378 void PrintCollectedCode(JSONObject* profile_code_obj) { |
| 357 ASSERT(kind() == kCollectedCode); | 379 ASSERT(kind() == kCollectedCode); |
| 358 JSONObject obj(profile_code_obj, "code"); | 380 JSONObject obj(profile_code_obj, "code"); |
| 359 obj.AddProperty("type", "@Code"); | 381 obj.AddProperty("type", "@Code"); |
| 360 obj.AddProperty("kind", "Collected"); | 382 obj.AddProperty("kind", "Collected"); |
| 361 obj.AddProperty("name", name()); | 383 obj.AddProperty("name", name()); |
| 362 obj.AddProperty("user_name", name()); | 384 obj.AddProperty("user_name", name()); |
| 363 obj.AddPropertyF("start", "%" Px "", start()); | 385 obj.AddPropertyF("start", "%" Px "", start()); |
| 364 obj.AddPropertyF("end", "%" Px "", end()); | 386 obj.AddPropertyF("end", "%" Px "", end()); |
| 365 obj.AddPropertyF("id", "code/collected/%" Px "", start()); | 387 obj.AddPropertyF("id", "/code/collected-%" Px "", start()); |
| 366 { | 388 { |
| 367 // Generate a fake function entry. | 389 // Generate a fake function entry. |
| 368 JSONObject func(&obj, "function"); | 390 JSONObject func(&obj, "function"); |
| 369 func.AddProperty("type", "@Function"); | 391 func.AddProperty("type", "@Function"); |
| 370 func.AddPropertyF("id", "collected/functions/%" Pd "", start()); | 392 obj.AddPropertyF("id", "/functions/collected-%" Px "", start()); |
| 371 func.AddProperty("name", name()); | 393 func.AddProperty("name", name()); |
| 372 func.AddProperty("user_name", name()); | 394 func.AddProperty("user_name", name()); |
| 373 func.AddProperty("kind", "Collected"); | 395 func.AddProperty("kind", "Collected"); |
| 374 } | 396 } |
| 375 } | 397 } |
| 376 | 398 |
| 399 void PrintOverwrittenCode(JSONObject* profile_code_obj) { | |
| 400 ASSERT(kind() == kOverwrittenCode); | |
| 401 JSONObject obj(profile_code_obj, "code"); | |
| 402 obj.AddProperty("type", "@Code"); | |
| 403 obj.AddProperty("kind", "Overwritten"); | |
| 404 obj.AddProperty("name", name()); | |
| 405 obj.AddProperty("user_name", name()); | |
| 406 obj.AddPropertyF("start", "%" Px "", start()); | |
| 407 obj.AddPropertyF("end", "%" Px "", end()); | |
| 408 obj.AddPropertyF("id", "/code/overwritten-%" Px "", start()); | |
| 409 { | |
| 410 // Generate a fake function entry. | |
| 411 JSONObject func(&obj, "function"); | |
| 412 func.AddProperty("type", "@Function"); | |
| 413 obj.AddPropertyF("id", "/functions/overwritten-%" Px "", start()); | |
| 414 func.AddProperty("name", name()); | |
| 415 func.AddProperty("user_name", name()); | |
| 416 func.AddProperty("kind", "Overwritten"); | |
| 417 } | |
| 418 } | |
| 419 | |
| 377 void PrintToJSONArray(Isolate* isolate, JSONArray* events, bool full) { | 420 void PrintToJSONArray(Isolate* isolate, JSONArray* events, bool full) { |
| 378 JSONObject obj(events); | 421 JSONObject obj(events); |
| 379 obj.AddProperty("type", "ProfileCode"); | 422 obj.AddProperty("type", "CodeRegion"); |
| 380 obj.AddProperty("kind", KindToCString(kind())); | 423 obj.AddProperty("kind", KindToCString(kind())); |
| 381 obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks()); | 424 obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks()); |
| 382 obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks()); | 425 obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks()); |
| 383 if (kind() == kDartCode) { | 426 if (kind() == kDartCode) { |
| 384 // Look up code in Dart heap. | 427 // Look up code in Dart heap. |
| 385 Code& code = Code::Handle(); | 428 Code& code = Code::Handle(); |
| 386 code ^= Code::LookupCode(start()); | 429 code ^= Code::LookupCode(start()); |
| 387 if (code.IsNull()) { | 430 if (code.IsNull()) { |
| 388 // Code is a stub in the Vm isolate. | 431 // Code is a stub in the Vm isolate. |
| 389 code ^= Code::LookupCodeInVmIsolate(start()); | 432 code ^= Code::LookupCodeInVmIsolate(start()); |
| 390 } | 433 } |
| 391 ASSERT(!code.IsNull()); | 434 ASSERT(!code.IsNull()); |
| 392 obj.AddProperty("code", code, !full); | 435 obj.AddProperty("code", code, !full); |
| 393 } else if (kind() == kCollectedCode) { | 436 } else if (kind() == kCollectedCode) { |
| 394 if (name() == NULL) { | 437 if (name() == NULL) { |
| 395 // Lazily set generated name. | 438 // Lazily set generated name. |
| 396 GenerateAndSetSymbolName("Collected"); | 439 GenerateAndSetSymbolName("Collected"); |
| 397 } | 440 } |
| 398 PrintCollectedCode(&obj); | 441 PrintCollectedCode(&obj); |
| 442 } else if (kind() == kOverwrittenCode) { | |
| 443 if (name() == NULL) { | |
| 444 // Lazily set generated name. | |
| 445 GenerateAndSetSymbolName("Overwritten"); | |
| 446 } | |
| 447 PrintOverwrittenCode(&obj); | |
| 399 } else { | 448 } else { |
| 400 ASSERT(kind() == kNativeCode); | 449 ASSERT(kind() == kNativeCode); |
| 401 if (name() == NULL) { | 450 if (name() == NULL) { |
| 402 // Lazily set generated name. | 451 // Lazily set generated name. |
| 403 GenerateAndSetSymbolName("Native"); | 452 GenerateAndSetSymbolName("Native"); |
| 404 } | 453 } |
| 405 PrintNativeCode(&obj); | 454 PrintNativeCode(&obj); |
| 406 } | 455 } |
| 407 { | 456 { |
| 408 JSONArray ticks(&obj, "ticks"); | 457 JSONArray ticks(&obj, "ticks"); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 425 JSONArray callees(&obj, "callees"); | 474 JSONArray callees(&obj, "callees"); |
| 426 for (intptr_t i = 0; i < callees_table_->length(); i++) { | 475 for (intptr_t i = 0; i < callees_table_->length(); i++) { |
| 427 const CallEntry& entry = (*callees_table_)[i]; | 476 const CallEntry& entry = (*callees_table_)[i]; |
| 428 callees.AddValueF("%" Pd "", entry.code_table_index); | 477 callees.AddValueF("%" Pd "", entry.code_table_index); |
| 429 callees.AddValueF("%" Pd "", entry.count); | 478 callees.AddValueF("%" Pd "", entry.count); |
| 430 } | 479 } |
| 431 } | 480 } |
| 432 } | 481 } |
| 433 | 482 |
| 434 private: | 483 private: |
| 484 void TickAddress(uword pc, bool exclusive) { | |
| 485 const intptr_t length = address_table_->length(); | |
| 486 intptr_t i = 0; | |
| 487 for (; i < length; i++) { | |
| 488 AddressEntry& entry = (*address_table_)[i]; | |
| 489 if (entry.pc == pc) { | |
| 490 // Tick the address entry. | |
| 491 entry.tick(exclusive); | |
| 492 return; | |
| 493 } | |
| 494 if (entry.pc > pc) { | |
| 495 break; | |
| 496 } | |
| 497 } | |
| 498 // New address, add entry. | |
| 499 AddressEntry entry; | |
| 500 entry.pc = pc; | |
| 501 entry.exclusive_ticks = 0; | |
| 502 entry.inclusive_ticks = 0; | |
| 503 entry.tick(exclusive); | |
| 504 if (i < length) { | |
| 505 // Insert at i. | |
| 506 address_table_->InsertAt(i, entry); | |
| 507 } else { | |
| 508 // Add to end. | |
| 509 address_table_->Add(entry); | |
| 510 } | |
| 511 } | |
| 512 | |
| 513 | |
| 435 void AddCallEntry(ZoneGrowableArray<CallEntry>* table, intptr_t index) { | 514 void AddCallEntry(ZoneGrowableArray<CallEntry>* table, intptr_t index) { |
| 436 const intptr_t length = table->length(); | 515 const intptr_t length = table->length(); |
| 437 intptr_t i = 0; | 516 intptr_t i = 0; |
| 438 for (; i < length; i++) { | 517 for (; i < length; i++) { |
| 439 CallEntry& entry = (*table)[i]; | 518 CallEntry& entry = (*table)[i]; |
| 440 if (entry.code_table_index == index) { | 519 if (entry.code_table_index == index) { |
| 441 entry.count++; | 520 entry.count++; |
| 442 return; | 521 return; |
| 443 } | 522 } |
| 444 if (entry.code_table_index > index) { | 523 if (entry.code_table_index > index) { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 456 } | 535 } |
| 457 | 536 |
| 458 void GenerateAndSetSymbolName(const char* prefix) { | 537 void GenerateAndSetSymbolName(const char* prefix) { |
| 459 const intptr_t kBuffSize = 512; | 538 const intptr_t kBuffSize = 512; |
| 460 char buff[kBuffSize]; | 539 char buff[kBuffSize]; |
| 461 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", | 540 OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", |
| 462 prefix, start(), end()); | 541 prefix, start(), end()); |
| 463 SetName(buff); | 542 SetName(buff); |
| 464 } | 543 } |
| 465 | 544 |
| 466 Kind kind_; | 545 // CodeRegion kind. |
| 546 const Kind kind_; | |
| 547 // CodeRegion start address. | |
| 467 uword start_; | 548 uword start_; |
| 549 // CodeRegion end address. | |
| 468 uword end_; | 550 uword end_; |
| 551 // Inclusive ticks. | |
| 469 intptr_t inclusive_ticks_; | 552 intptr_t inclusive_ticks_; |
| 553 // Exclusive ticks. | |
| 470 intptr_t exclusive_ticks_; | 554 intptr_t exclusive_ticks_; |
| 555 // Inclusive tick serial number, ensures that each CodeRegion is only given | |
| 556 // a single inclusive tick per sample. | |
| 471 intptr_t inclusive_tick_serial_; | 557 intptr_t inclusive_tick_serial_; |
| 558 // Name of code region. | |
| 472 const char* name_; | 559 const char* name_; |
| 560 // The compilation timestamp associated with this code region. | |
| 561 int64_t compile_timestamp_; | |
| 562 // Serial number at which this CodeRegion was created. | |
| 563 intptr_t creation_serial_; | |
| 473 ZoneGrowableArray<AddressEntry>* address_table_; | 564 ZoneGrowableArray<AddressEntry>* address_table_; |
| 474 ZoneGrowableArray<CallEntry>* callers_table_; | 565 ZoneGrowableArray<CallEntry>* callers_table_; |
| 475 ZoneGrowableArray<CallEntry>* callees_table_; | 566 ZoneGrowableArray<CallEntry>* callees_table_; |
| 476 DISALLOW_COPY_AND_ASSIGN(CodeRegion); | 567 DISALLOW_COPY_AND_ASSIGN(CodeRegion); |
| 477 }; | 568 }; |
| 478 | 569 |
| 570 // A sorted table of CodeRegions. Does not allow for overlap. | |
| 571 class CodeRegionTable : public ValueObject { | |
| 572 public: | |
| 573 enum TickResult { | |
| 574 kTicked = 0, // CodeRegion found and ticked. | |
| 575 kNotFound = -1, // No CodeRegion found. | |
| 576 kNewerCode = -2, // CodeRegion found but it was compiled after sample. | |
| 577 }; | |
| 479 | 578 |
| 480 class ScopeStopwatch : public ValueObject { | 579 explicit CodeRegionTable(Isolate* isolate) : |
| 481 public: | |
| 482 explicit ScopeStopwatch(const char* name) : name_(name) { | |
| 483 start_ = OS::GetCurrentTimeMillis(); | |
| 484 } | |
| 485 | |
| 486 intptr_t GetElapsed() const { | |
| 487 intptr_t end = OS::GetCurrentTimeMillis(); | |
| 488 ASSERT(end >= start_); | |
| 489 return end - start_; | |
| 490 } | |
| 491 | |
| 492 ~ScopeStopwatch() { | |
| 493 if (FLAG_trace_profiled_isolates) { | |
| 494 intptr_t elapsed = GetElapsed(); | |
| 495 OS::Print("%s took %" Pd " millis.\n", name_, elapsed); | |
| 496 } | |
| 497 } | |
| 498 | |
| 499 private: | |
| 500 const char* name_; | |
| 501 intptr_t start_; | |
| 502 }; | |
| 503 | |
| 504 | |
| 505 // All code regions. Code region tables are built on demand when a profile | |
| 506 // is requested (through the service or on isolate shutdown). | |
| 507 class ProfilerCodeRegionTable : public ValueObject { | |
| 508 public: | |
| 509 explicit ProfilerCodeRegionTable(Isolate* isolate) : | |
| 510 heap_(isolate->heap()), | 580 heap_(isolate->heap()), |
| 511 code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) { | 581 code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) { |
| 512 } | 582 } |
| 513 | 583 |
| 514 ~ProfilerCodeRegionTable() { | 584 // Ticks the CodeRegion containing pc if it is alive at timestamp. |
| 585 TickResult Tick(uword pc, bool exclusive, intptr_t serial, | |
| 586 int64_t timestamp) { | |
| 587 intptr_t index = FindIndex(pc); | |
| 588 if (index < 0) { | |
| 589 // Not found. | |
| 590 return kNotFound; | |
| 591 } | |
| 592 ASSERT(index < code_region_table_->length()); | |
| 593 CodeRegion* region = At(index); | |
| 594 if (region->compile_timestamp() > timestamp) { | |
| 595 // Compiled after tick. | |
| 596 return kNewerCode; | |
| 597 } | |
| 598 region->Tick(pc, exclusive, serial); | |
| 599 return kTicked; | |
| 515 } | 600 } |
| 516 | 601 |
| 517 void AddTick(uword pc, bool exclusive, intptr_t serial) { | 602 // Table length. |
| 518 intptr_t index = FindIndex(pc); | 603 intptr_t Length() const { return code_region_table_->length(); } |
| 519 if (index < 0) { | |
| 520 CodeRegion* code_region = CreateCodeRegion(pc); | |
| 521 ASSERT(code_region != NULL); | |
| 522 index = InsertCodeRegion(code_region); | |
| 523 } | |
| 524 ASSERT(index >= 0); | |
| 525 ASSERT(index < code_region_table_->length()); | |
| 526 | 604 |
| 527 // Update code object counters. | 605 // Get the CodeRegion at index. |
| 528 (*code_region_table_)[index]->AddTickAtAddress(pc, exclusive, serial); | 606 CodeRegion* At(intptr_t index) const { |
| 607 return (*code_region_table_)[index]; | |
| 529 } | 608 } |
| 530 | 609 |
| 531 intptr_t Length() const { return code_region_table_->length(); } | 610 // Find the table index to the CodeRegion containing pc. |
| 532 | 611 // Returns < 0 if not found. |
| 533 CodeRegion* At(intptr_t idx) { | |
| 534 return (*code_region_table_)[idx]; | |
| 535 } | |
| 536 | |
| 537 intptr_t FindIndex(uword pc) const { | 612 intptr_t FindIndex(uword pc) const { |
| 538 intptr_t index = FindRegionIndex(pc, &CompareLowerBound); | 613 intptr_t index = FindRegionIndex(pc, &CompareLowerBound); |
| 539 const CodeRegion* code_region = NULL; | 614 const CodeRegion* code_region = NULL; |
| 540 if (index == code_region_table_->length()) { | 615 if (index == code_region_table_->length()) { |
| 541 // Not present. | 616 // Not present. |
| 542 return -1; | 617 return -1; |
| 543 } | 618 } |
| 544 code_region = (*code_region_table_)[index]; | 619 code_region = At(index); |
| 545 if (code_region->contains(pc)) { | 620 if (code_region->contains(pc)) { |
| 546 // Found at index. | 621 // Found at index. |
| 547 return index; | 622 return index; |
| 548 } | 623 } |
| 549 return -1; | 624 return -2; |
| 550 } | 625 } |
| 551 | 626 |
| 552 #if defined(DEBUG) | 627 // Insert code_region into the table. Returns the table index where the |
| 553 void Verify() { | 628 // CodeRegion was inserted. Will merge with an overlapping CodeRegion if |
| 554 VerifyOrder(); | 629 // one is present. |
| 555 VerifyOverlap(); | |
| 556 } | |
| 557 #endif | |
| 558 | |
| 559 private: | |
| 560 intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const { | |
| 561 ASSERT(comparator != NULL); | |
| 562 intptr_t count = code_region_table_->length(); | |
| 563 intptr_t first = 0; | |
| 564 while (count > 0) { | |
| 565 intptr_t it = first; | |
| 566 intptr_t step = count / 2; | |
| 567 it += step; | |
| 568 const CodeRegion* code_region = (*code_region_table_)[it]; | |
| 569 if (comparator(pc, code_region->start(), code_region->end())) { | |
| 570 first = ++it; | |
| 571 count -= (step + 1); | |
| 572 } else { | |
| 573 count = step; | |
| 574 } | |
| 575 } | |
| 576 return first; | |
| 577 } | |
| 578 | |
| 579 static bool CompareUpperBound(uword pc, uword start, uword end) { | |
| 580 return pc >= end; | |
| 581 } | |
| 582 | |
| 583 static bool CompareLowerBound(uword pc, uword start, uword end) { | |
| 584 return end <= pc; | |
| 585 } | |
| 586 | |
| 587 CodeRegion* CreateCodeRegion(uword pc) { | |
| 588 Code& code = Code::Handle(); | |
| 589 code ^= Code::LookupCode(pc); | |
| 590 if (!code.IsNull()) { | |
| 591 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | |
| 592 code.EntryPoint() + code.Size()); | |
| 593 } | |
| 594 code ^= Code::LookupCodeInVmIsolate(pc); | |
| 595 if (!code.IsNull()) { | |
| 596 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | |
| 597 code.EntryPoint() + code.Size()); | |
| 598 } | |
| 599 if (heap_->CodeContains(pc)) { | |
| 600 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); | |
| 601 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); | |
| 602 return new CodeRegion(CodeRegion::kCollectedCode, pc, | |
| 603 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment); | |
| 604 } | |
| 605 uintptr_t native_start = 0; | |
| 606 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, | |
| 607 &native_start); | |
| 608 if (native_name == NULL) { | |
| 609 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1); | |
| 610 } | |
| 611 ASSERT(pc >= native_start); | |
| 612 CodeRegion* code_region = | |
| 613 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1); | |
| 614 code_region->SetName(native_name); | |
| 615 free(native_name); | |
| 616 return code_region; | |
| 617 } | |
| 618 | |
| 619 void HandleOverlap(CodeRegion* region, CodeRegion* code_region, | |
| 620 uword start, uword end) { | |
| 621 // We should never see overlapping Dart code regions. | |
| 622 ASSERT(region->kind() != CodeRegion::kDartCode); | |
| 623 // When code regions overlap, they should be of the same kind. | |
| 624 ASSERT(region->kind() == code_region->kind()); | |
| 625 region->AdjustExtent(start, end); | |
| 626 } | |
| 627 | |
| 628 intptr_t InsertCodeRegion(CodeRegion* code_region) { | 630 intptr_t InsertCodeRegion(CodeRegion* code_region) { |
| 629 const uword start = code_region->start(); | 631 const uword start = code_region->start(); |
| 630 const uword end = code_region->end(); | 632 const uword end = code_region->end(); |
| 631 const intptr_t length = code_region_table_->length(); | 633 const intptr_t length = code_region_table_->length(); |
| 632 if (length == 0) { | 634 if (length == 0) { |
| 633 code_region_table_->Add(code_region); | 635 code_region_table_->Add(code_region); |
| 634 return length; | 636 return length; |
| 635 } | 637 } |
| 636 // Determine the correct place to insert or merge code_region into table. | 638 // Determine the correct place to insert or merge code_region into table. |
| 637 intptr_t lo = FindRegionIndex(start, &CompareLowerBound); | 639 intptr_t lo = FindRegionIndex(start, &CompareLowerBound); |
| 638 intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound); | 640 intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound); |
| 641 // TODO(johnmccutchan): Simplify below logic. | |
| 639 if ((lo == length) && (hi == length)) { | 642 if ((lo == length) && (hi == length)) { |
| 640 lo = length - 1; | 643 lo = length - 1; |
| 641 } | 644 } |
| 642 if (lo == length) { | 645 if (lo == length) { |
| 643 CodeRegion* region = (*code_region_table_)[hi]; | 646 CodeRegion* region = At(hi); |
| 644 if (region->overlaps(code_region)) { | 647 if (region->overlaps(code_region)) { |
| 645 HandleOverlap(region, code_region, start, end); | 648 HandleOverlap(region, code_region, start, end); |
| 646 return hi; | 649 return hi; |
| 647 } | 650 } |
| 648 code_region_table_->Add(code_region); | 651 code_region_table_->Add(code_region); |
| 649 return length; | 652 return length; |
| 650 } else if (hi == length) { | 653 } else if (hi == length) { |
| 651 CodeRegion* region = (*code_region_table_)[lo]; | 654 CodeRegion* region = At(lo); |
| 652 if (region->overlaps(code_region)) { | 655 if (region->overlaps(code_region)) { |
| 653 HandleOverlap(region, code_region, start, end); | 656 HandleOverlap(region, code_region, start, end); |
| 654 return lo; | 657 return lo; |
| 655 } | 658 } |
| 656 code_region_table_->Add(code_region); | 659 code_region_table_->Add(code_region); |
| 657 return length; | 660 return length; |
| 658 } else if (lo == hi) { | 661 } else if (lo == hi) { |
| 659 CodeRegion* region = (*code_region_table_)[lo]; | 662 CodeRegion* region = At(lo); |
| 660 if (region->overlaps(code_region)) { | 663 if (region->overlaps(code_region)) { |
| 661 HandleOverlap(region, code_region, start, end); | 664 HandleOverlap(region, code_region, start, end); |
| 662 return lo; | 665 return lo; |
| 663 } | 666 } |
| 664 code_region_table_->InsertAt(lo, code_region); | 667 code_region_table_->InsertAt(lo, code_region); |
| 665 return lo; | 668 return lo; |
| 666 } else { | 669 } else { |
| 667 CodeRegion* region = (*code_region_table_)[lo]; | 670 CodeRegion* region = At(lo); |
| 668 if (region->overlaps(code_region)) { | 671 if (region->overlaps(code_region)) { |
| 669 HandleOverlap(region, code_region, start, end); | 672 HandleOverlap(region, code_region, start, end); |
| 670 return lo; | 673 return lo; |
| 671 } | 674 } |
| 672 region = (*code_region_table_)[hi]; | 675 region = At(hi); |
| 673 if (region->overlaps(code_region)) { | 676 if (region->overlaps(code_region)) { |
| 674 HandleOverlap(region, code_region, start, end); | 677 HandleOverlap(region, code_region, start, end); |
| 675 return hi; | 678 return hi; |
| 676 } | 679 } |
| 677 code_region_table_->InsertAt(hi, code_region); | 680 code_region_table_->InsertAt(hi, code_region); |
| 678 return hi; | 681 return hi; |
| 679 } | 682 } |
| 680 UNREACHABLE(); | 683 UNREACHABLE(); |
| 681 } | 684 } |
| 682 | 685 |
| 683 #if defined(DEBUG) | 686 #if defined(DEBUG) |
| 687 void Verify() { | |
| 688 VerifyOrder(); | |
| 689 VerifyOverlap(); | |
| 690 } | |
| 691 #endif | |
| 692 | |
| 693 void DebugPrint() { | |
| 694 OS::Print("Dumping CodeRegionTable:\n"); | |
| 695 for (intptr_t i = 0; i < code_region_table_->length(); i++) { | |
| 696 CodeRegion* region = At(i); | |
| 697 region->DebugPrint(); | |
| 698 } | |
| 699 } | |
| 700 | |
| 701 private: | |
| 702 intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const { | |
| 703 ASSERT(comparator != NULL); | |
| 704 intptr_t count = code_region_table_->length(); | |
| 705 intptr_t first = 0; | |
| 706 while (count > 0) { | |
| 707 intptr_t it = first; | |
| 708 intptr_t step = count / 2; | |
| 709 it += step; | |
| 710 const CodeRegion* code_region = At(it); | |
| 711 if (comparator(pc, code_region->start(), code_region->end())) { | |
| 712 first = ++it; | |
| 713 count -= (step + 1); | |
| 714 } else { | |
| 715 count = step; | |
| 716 } | |
| 717 } | |
| 718 return first; | |
| 719 } | |
| 720 | |
| 721 static bool CompareUpperBound(uword pc, uword start, uword end) { | |
| 722 return pc >= end; | |
| 723 } | |
| 724 | |
| 725 static bool CompareLowerBound(uword pc, uword start, uword end) { | |
| 726 return end <= pc; | |
| 727 } | |
| 728 | |
| 729 void HandleOverlap(CodeRegion* region, CodeRegion* code_region, | |
| 730 uword start, uword end) { | |
| 731 // We should never see overlapping Dart code regions. | |
| 732 ASSERT(region->kind() != CodeRegion::kDartCode); | |
| 733 // When code regions overlap, they should be of the same kind. | |
| 734 ASSERT(region->kind() == code_region->kind()); | |
| 735 region->AdjustExtent(start, end); | |
| 736 } | |
| 737 | |
| 738 #if defined(DEBUG) | |
| 684 void VerifyOrder() { | 739 void VerifyOrder() { |
| 685 const intptr_t length = code_region_table_->length(); | 740 const intptr_t length = code_region_table_->length(); |
| 686 if (length == 0) { | 741 if (length == 0) { |
| 687 return; | 742 return; |
| 688 } | 743 } |
| 689 uword last = (*code_region_table_)[0]->end(); | 744 uword last = (*code_region_table_)[0]->end(); |
| 690 for (intptr_t i = 1; i < length; i++) { | 745 for (intptr_t i = 1; i < length; i++) { |
| 691 CodeRegion* a = (*code_region_table_)[i]; | 746 CodeRegion* a = (*code_region_table_)[i]; |
| 692 ASSERT(last <= a->start()); | 747 ASSERT(last <= a->start()); |
| 693 last = a->end(); | 748 last = a->end(); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 710 #endif | 765 #endif |
| 711 | 766 |
| 712 Heap* heap_; | 767 Heap* heap_; |
| 713 ZoneGrowableArray<CodeRegion*>* code_region_table_; | 768 ZoneGrowableArray<CodeRegion*>* code_region_table_; |
| 714 }; | 769 }; |
| 715 | 770 |
| 716 | 771 |
| 717 class CodeRegionTableBuilder : public SampleVisitor { | 772 class CodeRegionTableBuilder : public SampleVisitor { |
| 718 public: | 773 public: |
| 719 CodeRegionTableBuilder(Isolate* isolate, | 774 CodeRegionTableBuilder(Isolate* isolate, |
| 720 ProfilerCodeRegionTable* code_region_table) | 775 CodeRegionTable* live_code_table, |
| 721 : SampleVisitor(isolate), code_region_table_(code_region_table) { | 776 CodeRegionTable* dead_code_table) |
| 777 : SampleVisitor(isolate), | |
| 778 live_code_table_(live_code_table), | |
| 779 dead_code_table_(dead_code_table), | |
| 780 heap_(isolate->heap()) { | |
|
siva
2014/03/13 17:12:38
Why not store the isolate as a field in this class
Cutch
2014/03/13 17:33:57
Done.
| |
| 781 ASSERT(live_code_table_ != NULL); | |
| 782 ASSERT(dead_code_table_ != NULL); | |
| 722 frames_ = 0; | 783 frames_ = 0; |
| 723 min_time_ = kMaxInt64; | 784 min_time_ = kMaxInt64; |
| 724 max_time_ = 0; | 785 max_time_ = 0; |
| 786 vm_isolate_heap_ = Dart::vm_isolate()->heap(); | |
| 787 ASSERT(vm_isolate_heap_ != NULL); | |
| 725 } | 788 } |
| 726 | 789 |
| 790 /* | |
| 791 code_region_table_->AddTick(sample->At(i), false, visited()); | |
| 792 ASSERT(index >= 0); | |
| 793 ASSERT(index < code_region_table_->length()); | |
| 794 if (index < 0) { | |
| 795 CodeRegion* code_region = CreateCodeRegion(pc); | |
| 796 ASSERT(code_region != NULL); | |
| 797 index = InsertCodeRegion(code_region); | |
| 798 } | |
| 799 ASSERT(index >= 0); | |
| 800 */ | |
|
siva
2014/03/13 17:12:38
Why do you want to keep this commented out code ar
Cutch
2014/03/13 17:33:57
Removed.
| |
| 801 | |
| 727 void VisitSample(Sample* sample) { | 802 void VisitSample(Sample* sample) { |
| 728 int64_t timestamp = sample->timestamp(); | 803 int64_t timestamp = sample->timestamp(); |
| 729 if (timestamp > max_time_) { | 804 if (timestamp > max_time_) { |
| 730 max_time_ = timestamp; | 805 max_time_ = timestamp; |
| 731 } | 806 } |
| 732 if (timestamp < min_time_) { | 807 if (timestamp < min_time_) { |
| 733 min_time_ = timestamp; | 808 min_time_ = timestamp; |
| 734 } | 809 } |
| 735 // Give the bottom frame an exclusive tick. | 810 // Exclusive tick for bottom frame. |
| 736 code_region_table_->AddTick(sample->At(0), true, -1); | 811 Tick(sample->At(0), true, timestamp); |
| 737 // Give all frames (including the bottom) an inclusive tick. | 812 // Inclusive tick for all frames. |
| 738 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { | 813 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { |
| 739 if (sample->At(i) == 0) { | 814 if (sample->At(i) == 0) { |
| 740 break; | 815 break; |
| 741 } | 816 } |
| 742 frames_++; | 817 frames_++; |
| 743 code_region_table_->AddTick(sample->At(i), false, visited()); | 818 Tick(sample->At(i), false, timestamp); |
| 744 } | 819 } |
| 745 } | 820 } |
| 746 | 821 |
| 747 intptr_t frames() const { return frames_; } | 822 intptr_t frames() const { return frames_; } |
| 823 | |
| 748 intptr_t TimeDeltaMicros() const { | 824 intptr_t TimeDeltaMicros() const { |
| 749 return static_cast<intptr_t>(max_time_ - min_time_); | 825 return static_cast<intptr_t>(max_time_ - min_time_); |
| 750 } | 826 } |
| 751 int64_t max_time() const { return max_time_; } | 827 int64_t max_time() const { return max_time_; } |
| 752 | 828 |
| 753 private: | 829 private: |
| 830 void Tick(uword pc, bool exclusive, int64_t timestamp) { | |
| 831 CodeRegionTable::TickResult r; | |
| 832 intptr_t serial = exclusive ? -1 : visited(); | |
| 833 r = live_code_table_->Tick(pc, exclusive, serial, timestamp); | |
| 834 if (r == CodeRegionTable::kTicked) { | |
| 835 // Live code found and ticked. | |
| 836 return; | |
| 837 } | |
| 838 if (r == CodeRegionTable::kNewerCode) { | |
| 839 // Code has been overwritten by newer code. | |
| 840 // Update shadow table of dead code regions. | |
| 841 r = dead_code_table_->Tick(pc, exclusive, serial, timestamp); | |
| 842 ASSERT(r != CodeRegionTable::kNewerCode); | |
| 843 if (r == CodeRegionTable::kTicked) { | |
| 844 // Dead code found and ticked. | |
| 845 return; | |
| 846 } | |
| 847 ASSERT(r == CodeRegionTable::kNotFound); | |
| 848 CreateAndTickDeadCodeRegion(pc, exclusive, serial); | |
| 849 return; | |
| 850 } | |
| 851 // Create new live CodeRegion. | |
| 852 ASSERT(r == CodeRegionTable::kNotFound); | |
| 853 CodeRegion* region = CreateCodeRegion(pc); | |
| 854 region->set_creation_serial(visited()); | |
| 855 intptr_t index = live_code_table_->InsertCodeRegion(region); | |
| 856 ASSERT(index >= 0); | |
| 857 region = live_code_table_->At(index); | |
| 858 if (region->compile_timestamp() <= timestamp) { | |
| 859 region->Tick(pc, exclusive, serial); | |
| 860 return; | |
| 861 } | |
| 862 // We have created a new code region but it's for a CodeRegion | |
| 863 // compiled after the sample. | |
| 864 ASSERT(region->kind() == CodeRegion::kDartCode); | |
| 865 CreateAndTickDeadCodeRegion(pc, exclusive, serial); | |
| 866 } | |
| 867 | |
| 868 void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) { | |
| 869 // Need to create dead code. | |
| 870 CodeRegion* region = new CodeRegion(CodeRegion::kOverwrittenCode, | |
| 871 pc, | |
| 872 pc + 1, | |
| 873 0); | |
| 874 intptr_t index = dead_code_table_->InsertCodeRegion(region); | |
| 875 region->set_creation_serial(visited()); | |
| 876 ASSERT(index >= 0); | |
| 877 dead_code_table_->At(index)->Tick(pc, exclusive, serial); | |
| 878 } | |
| 879 | |
| 880 CodeRegion* CreateCodeRegion(uword pc) { | |
| 881 Code& code = Code::Handle(); | |
| 882 code ^= Code::LookupCode(pc); | |
| 883 if (!code.IsNull()) { | |
| 884 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | |
| 885 code.EntryPoint() + code.Size(), | |
| 886 code.compile_timestamp()); | |
| 887 } | |
| 888 code ^= Code::LookupCodeInVmIsolate(pc); | |
| 889 if (!code.IsNull()) { | |
| 890 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), | |
| 891 code.EntryPoint() + code.Size(), | |
| 892 code.compile_timestamp()); | |
| 893 } | |
| 894 if (heap_->CodeContains(pc) || vm_isolate_heap_->CodeContains(pc)) { | |
|
siva
2014/03/13 17:12:38
Wouldn't it be more efficient to do this check fir
Cutch
2014/03/13 17:33:57
Done.
| |
| 895 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); | |
| 896 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); | |
| 897 return new CodeRegion(CodeRegion::kCollectedCode, pc, | |
| 898 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, | |
| 899 0); | |
| 900 } | |
| 901 uintptr_t native_start = 0; | |
| 902 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, | |
| 903 &native_start); | |
| 904 if (native_name == NULL) { | |
| 905 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1, 0); | |
| 906 } | |
| 907 ASSERT(pc >= native_start); | |
| 908 CodeRegion* code_region = | |
| 909 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1, 0); | |
| 910 code_region->SetName(native_name); | |
| 911 free(native_name); | |
| 912 return code_region; | |
| 913 } | |
| 914 | |
| 754 intptr_t frames_; | 915 intptr_t frames_; |
| 755 int64_t min_time_; | 916 int64_t min_time_; |
| 756 int64_t max_time_; | 917 int64_t max_time_; |
| 757 ProfilerCodeRegionTable* code_region_table_; | 918 CodeRegionTable* live_code_table_; |
| 919 CodeRegionTable* dead_code_table_; | |
| 920 const Heap* heap_; | |
| 921 const Heap* vm_isolate_heap_; | |
| 758 }; | 922 }; |
| 759 | 923 |
| 760 | 924 |
| 761 class CodeRegionTableCallersBuilder : public SampleVisitor { | 925 class CodeRegionTableCallersBuilder : public SampleVisitor { |
| 762 public: | 926 public: |
| 763 CodeRegionTableCallersBuilder(Isolate* isolate, | 927 CodeRegionTableCallersBuilder(Isolate* isolate, |
| 764 ProfilerCodeRegionTable* code_region_table) | 928 CodeRegionTable* live_code_table, |
| 765 : SampleVisitor(isolate), code_region_table_(code_region_table) { | 929 CodeRegionTable* dead_code_table) |
| 766 ASSERT(code_region_table_ != NULL); | 930 : SampleVisitor(isolate), |
| 931 live_code_table_(live_code_table), | |
| 932 dead_code_table_(dead_code_table) { | |
| 933 ASSERT(live_code_table_ != NULL); | |
| 934 ASSERT(dead_code_table_ != NULL); | |
| 935 dead_code_table_offset_ = live_code_table_->Length(); | |
| 767 } | 936 } |
| 768 | 937 |
| 769 void VisitSample(Sample* sample) { | 938 void VisitSample(Sample* sample) { |
| 770 intptr_t current_index = code_region_table_->FindIndex(sample->At(0)); | 939 int64_t timestamp = sample->timestamp(); |
| 771 ASSERT(current_index != -1); | 940 intptr_t current_index = FindFinalIndex(sample->At(0), timestamp); |
| 772 CodeRegion* current = code_region_table_->At(current_index); | 941 ASSERT(current_index >= 0); |
| 942 CodeRegion* current = At(current_index); | |
| 773 intptr_t caller_index = -1; | 943 intptr_t caller_index = -1; |
| 774 CodeRegion* caller = NULL; | 944 CodeRegion* caller = NULL; |
| 775 intptr_t callee_index = -1; | 945 intptr_t callee_index = -1; |
| 776 CodeRegion* callee = NULL; | 946 CodeRegion* callee = NULL; |
| 777 for (intptr_t i = 1; i < FLAG_profile_depth; i++) { | 947 for (intptr_t i = 1; i < FLAG_profile_depth; i++) { |
| 778 if (sample->At(i) == 0) { | 948 if (sample->At(i) == 0) { |
| 779 break; | 949 break; |
| 780 } | 950 } |
| 781 caller_index = code_region_table_->FindIndex(sample->At(i)); | 951 caller_index = FindFinalIndex(sample->At(i), timestamp); |
| 782 ASSERT(caller_index != -1); | 952 ASSERT(caller_index >= 0); |
| 783 caller = code_region_table_->At(caller_index); | 953 caller = At(caller_index); |
| 784 current->AddCaller(caller_index); | 954 current->AddCaller(caller_index); |
| 785 if (callee != NULL) { | 955 if (callee != NULL) { |
| 786 current->AddCallee(callee_index); | 956 current->AddCallee(callee_index); |
| 787 } | 957 } |
| 788 // Move cursors. | 958 // Move cursors. |
| 789 callee_index = current_index; | 959 callee_index = current_index; |
| 790 callee = current; | 960 callee = current; |
| 791 current_index = caller_index; | 961 current_index = caller_index; |
| 792 current = caller; | 962 current = caller; |
| 793 } | 963 } |
| 794 } | 964 } |
| 795 | 965 |
| 796 private: | 966 private: |
| 797 ProfilerCodeRegionTable* code_region_table_; | 967 intptr_t FindFinalIndex(uword pc, int64_t timestamp) { |
|
siva
2014/03/13 17:12:38
) const {
Cutch
2014/03/13 17:33:57
Done.
| |
| 968 intptr_t index = live_code_table_->FindIndex(pc); | |
| 969 ASSERT(index >= 0); | |
| 970 CodeRegion* region = live_code_table_->At(index); | |
| 971 ASSERT(region->contains(pc)); | |
| 972 if (region->compile_timestamp() > timestamp) { | |
| 973 // Overwritten code, find in dead code table. | |
| 974 index = dead_code_table_->FindIndex(pc); | |
| 975 ASSERT(index >= 0); | |
| 976 region = dead_code_table_->At(index); | |
| 977 ASSERT(region->contains(pc)); | |
| 978 ASSERT(region->compile_timestamp() <= timestamp); | |
| 979 return index + dead_code_table_offset_; | |
| 980 } | |
| 981 ASSERT(region->compile_timestamp() <= timestamp); | |
| 982 return index; | |
| 983 } | |
| 984 | |
| 985 CodeRegion* At(intptr_t final_index) { | |
| 986 ASSERT(final_index >= 0); | |
| 987 if (final_index < dead_code_table_offset_) { | |
| 988 return live_code_table_->At(final_index); | |
| 989 } else { | |
| 990 return dead_code_table_->At(final_index - dead_code_table_offset_); | |
| 991 } | |
| 992 } | |
| 993 | |
| 994 CodeRegionTable* live_code_table_; | |
| 995 CodeRegionTable* dead_code_table_; | |
| 996 intptr_t dead_code_table_offset_; | |
| 798 }; | 997 }; |
| 799 | 998 |
| 800 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream, | 999 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream, |
| 801 bool full) { | 1000 bool full) { |
| 802 ASSERT(isolate == Isolate::Current()); | 1001 ASSERT(isolate == Isolate::Current()); |
| 803 // Disable profile interrupts while processing the buffer. | 1002 // Disable profile interrupts while processing the buffer. |
| 804 EndExecution(isolate); | 1003 EndExecution(isolate); |
| 805 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 1004 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| 806 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 1005 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 807 if (profiler_data == NULL) { | 1006 if (profiler_data == NULL) { |
| 808 JSONObject error(stream); | 1007 JSONObject error(stream); |
| 809 error.AddProperty("type", "Error"); | 1008 error.AddProperty("type", "Error"); |
| 810 error.AddProperty("text", "Isolate does not have profiling enabled."); | 1009 error.AddProperty("text", "Isolate does not have profiling enabled."); |
| 811 return; | 1010 return; |
| 812 } | 1011 } |
| 813 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 1012 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 814 ASSERT(sample_buffer != NULL); | 1013 ASSERT(sample_buffer != NULL); |
| 815 { | 1014 { |
| 816 StackZone zone(isolate); | 1015 StackZone zone(isolate); |
| 817 { | 1016 { |
| 818 // Build code region table. | 1017 // Live code holds Dart, Native, and Collected CodeRegions. |
| 819 ProfilerCodeRegionTable code_region_table(isolate); | 1018 CodeRegionTable live_code_table(isolate); |
| 820 CodeRegionTableBuilder builder(isolate, &code_region_table); | 1019 // Dead code holds Overwritten CodeRegions. |
| 821 CodeRegionTableCallersBuilder build_callers(isolate, &code_region_table); | 1020 CodeRegionTable dead_code_table(isolate); |
| 1021 CodeRegionTableBuilder builder(isolate, | |
| 1022 &live_code_table, | |
| 1023 &dead_code_table); | |
| 822 { | 1024 { |
| 1025 // Build CodeRegion tables. | |
| 823 ScopeStopwatch sw("CodeTableBuild"); | 1026 ScopeStopwatch sw("CodeTableBuild"); |
| 824 sample_buffer->VisitSamples(&builder); | 1027 sample_buffer->VisitSamples(&builder); |
| 825 } | 1028 } |
| 826 #if defined(DEBUG) | |
| 827 code_region_table.Verify(); | |
| 828 #endif | |
| 829 { | |
| 830 ScopeStopwatch sw("CodeTableCallersBuild"); | |
| 831 sample_buffer->VisitSamples(&build_callers); | |
| 832 } | |
| 833 // Number of samples we processed. | |
| 834 intptr_t samples = builder.visited(); | 1029 intptr_t samples = builder.visited(); |
| 835 intptr_t frames = builder.frames(); | 1030 intptr_t frames = builder.frames(); |
| 836 if (FLAG_trace_profiled_isolates) { | 1031 if (FLAG_trace_profiled_isolates) { |
| 837 OS::Print("%" Pd " frames produced %" Pd " code objects.\n", | 1032 intptr_t total_live_code_objects = live_code_table.Length(); |
| 838 frames, code_region_table.Length()); | 1033 intptr_t total_dead_code_objects = dead_code_table.Length(); |
| 1034 OS::Print("Processed %" Pd " frames\n", frames); | |
| 1035 OS::Print("CodeTables: live=%" Pd " dead=%" Pd "\n", | |
| 1036 total_live_code_objects, | |
| 1037 total_dead_code_objects); | |
| 1038 } | |
| 1039 #if defined(DEBUG) | |
| 1040 live_code_table.Verify(); | |
| 1041 dead_code_table.Verify(); | |
| 1042 if (FLAG_trace_profiled_isolates) { | |
| 1043 OS::Print("CodeRegionTables verified to be ordered and not overlap.\n"); | |
| 1044 } | |
| 1045 #endif | |
| 1046 CodeRegionTableCallersBuilder build_callers(isolate, | |
| 1047 &live_code_table, | |
| 1048 &dead_code_table); | |
| 1049 { | |
| 1050 // Build CodeRegion callers. | |
| 1051 ScopeStopwatch sw("CodeTableCallersBuild"); | |
| 1052 sample_buffer->VisitSamples(&build_callers); | |
| 839 } | 1053 } |
| 840 { | 1054 { |
| 841 ScopeStopwatch sw("CodeTableStream"); | 1055 ScopeStopwatch sw("CodeTableStream"); |
| 842 // Serialize to JSON. | 1056 // Serialize to JSON. |
| 843 JSONObject obj(stream); | 1057 JSONObject obj(stream); |
| 844 obj.AddProperty("type", "Profile"); | 1058 obj.AddProperty("type", "Profile"); |
| 845 obj.AddProperty("id", "profile"); | 1059 obj.AddProperty("id", "profile"); |
| 846 obj.AddProperty("samples", samples); | 1060 obj.AddProperty("samples", samples); |
| 847 obj.AddProperty("time_delta_micros", builder.TimeDeltaMicros()); | 1061 obj.AddProperty("time_delta_micros", builder.TimeDeltaMicros()); |
| 848 JSONArray codes(&obj, "codes"); | 1062 JSONArray codes(&obj, "codes"); |
| 849 for (intptr_t i = 0; i < code_region_table.Length(); i++) { | 1063 for (intptr_t i = 0; i < live_code_table.Length(); i++) { |
| 850 CodeRegion* region = code_region_table.At(i); | 1064 CodeRegion* region = live_code_table.At(i); |
| 851 ASSERT(region != NULL); | 1065 ASSERT(region != NULL); |
| 852 region->PrintToJSONArray(isolate, &codes, full); | 1066 region->PrintToJSONArray(isolate, &codes, full); |
| 853 } | 1067 } |
| 1068 for (intptr_t i = 0; i < dead_code_table.Length(); i++) { | |
| 1069 CodeRegion* region = dead_code_table.At(i); | |
| 1070 ASSERT(region != NULL); | |
| 1071 region->PrintToJSONArray(isolate, &codes, full); | |
| 1072 } | |
| 854 } | 1073 } |
| 855 } | 1074 } |
| 856 } | 1075 } |
| 857 // Enable profile interrupts. | 1076 // Enable profile interrupts. |
| 858 BeginExecution(isolate); | 1077 BeginExecution(isolate); |
| 859 } | 1078 } |
| 860 | 1079 |
| 861 | 1080 |
| 862 void Profiler::WriteProfile(Isolate* isolate) { | 1081 void Profiler::WriteProfile(Isolate* isolate) { |
| 863 if (isolate == NULL) { | 1082 if (isolate == NULL) { |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1070 stack_lower = 0; | 1289 stack_lower = 0; |
| 1071 stack_upper = 0; | 1290 stack_upper = 0; |
| 1072 } | 1291 } |
| 1073 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, | 1292 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, |
| 1074 state.pc, state.fp, state.sp); | 1293 state.pc, state.fp, state.sp); |
| 1075 stackWalker.walk(isolate->heap()); | 1294 stackWalker.walk(isolate->heap()); |
| 1076 } | 1295 } |
| 1077 | 1296 |
| 1078 | 1297 |
| 1079 } // namespace dart | 1298 } // namespace dart |
| OLD | NEW |