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_ = FLAG_trace_profiled_isolates ? OS::GetCurrentTimeMillis() : 0; |
| 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 kReusedCode, // Dead Dart code that has been reused by new kDartCode. |
| 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 kReusedCode: |
| 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() == kReusedCode); |
| 401 JSONObject obj(profile_code_obj, "code"); |
| 402 obj.AddProperty("type", "@Code"); |
| 403 obj.AddProperty("kind", "Reused"); |
| 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/reused-%" Px "", start()); |
| 409 { |
| 410 // Generate a fake function entry. |
| 411 JSONObject func(&obj, "function"); |
| 412 func.AddProperty("type", "@Function"); |
| 413 obj.AddPropertyF("id", "functions/reused-%" Px "", start()); |
| 414 func.AddProperty("name", name()); |
| 415 func.AddProperty("user_name", name()); |
| 416 func.AddProperty("kind", "Reused"); |
| 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(isolate); |
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() == kReusedCode) { |
| 443 if (name() == NULL) { |
| 444 // Lazily set generated name. |
| 445 GenerateAndSetSymbolName("[Reused]"); |
| 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"); |
409 for (intptr_t i = 0; i < address_table_->length(); i++) { | 458 for (intptr_t i = 0; i < address_table_->length(); i++) { |
410 const AddressEntry& entry = (*address_table_)[i]; | 459 const AddressEntry& entry = (*address_table_)[i]; |
411 ticks.AddValueF("%" Px "", entry.pc); | 460 ticks.AddValueF("%" Px "", entry.pc); |
412 ticks.AddValueF("%" Pd "", entry.exclusive_ticks); | 461 ticks.AddValueF("%" Pd "", entry.exclusive_ticks); |
413 ticks.AddValueF("%" Pd "", entry.inclusive_ticks); | 462 ticks.AddValueF("%" Pd "", entry.inclusive_ticks); |
(...skipping 11 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 CodeRegionTable() : |
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()), | |
511 code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) { | 580 code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) { |
512 } | 581 } |
513 | 582 |
514 ~ProfilerCodeRegionTable() { | 583 // Ticks the CodeRegion containing pc if it is alive at timestamp. |
| 584 TickResult Tick(uword pc, bool exclusive, intptr_t serial, |
| 585 int64_t timestamp) { |
| 586 intptr_t index = FindIndex(pc); |
| 587 if (index < 0) { |
| 588 // Not found. |
| 589 return kNotFound; |
| 590 } |
| 591 ASSERT(index < code_region_table_->length()); |
| 592 CodeRegion* region = At(index); |
| 593 if (region->compile_timestamp() > timestamp) { |
| 594 // Compiled after tick. |
| 595 return kNewerCode; |
| 596 } |
| 597 region->Tick(pc, exclusive, serial); |
| 598 return kTicked; |
515 } | 599 } |
516 | 600 |
517 void AddTick(uword pc, bool exclusive, intptr_t serial) { | 601 // Table length. |
518 intptr_t index = FindIndex(pc); | 602 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 | 603 |
527 // Update code object counters. | 604 // Get the CodeRegion at index. |
528 (*code_region_table_)[index]->AddTickAtAddress(pc, exclusive, serial); | 605 CodeRegion* At(intptr_t index) const { |
| 606 return (*code_region_table_)[index]; |
529 } | 607 } |
530 | 608 |
531 intptr_t Length() const { return code_region_table_->length(); } | 609 // Find the table index to the CodeRegion containing pc. |
532 | 610 // 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 { | 611 intptr_t FindIndex(uword pc) const { |
538 intptr_t index = FindRegionIndex(pc, &CompareLowerBound); | 612 intptr_t index = FindRegionIndex(pc, &CompareLowerBound); |
539 const CodeRegion* code_region = NULL; | 613 const CodeRegion* code_region = NULL; |
540 if (index == code_region_table_->length()) { | 614 if (index == code_region_table_->length()) { |
541 // Not present. | 615 // Not present. |
542 return -1; | 616 return -1; |
543 } | 617 } |
544 code_region = (*code_region_table_)[index]; | 618 code_region = At(index); |
545 if (code_region->contains(pc)) { | 619 if (code_region->contains(pc)) { |
546 // Found at index. | 620 // Found at index. |
547 return index; | 621 return index; |
548 } | 622 } |
549 return -1; | 623 return -2; |
550 } | 624 } |
551 | 625 |
552 #if defined(DEBUG) | 626 // Insert code_region into the table. Returns the table index where the |
553 void Verify() { | 627 // CodeRegion was inserted. Will merge with an overlapping CodeRegion if |
554 VerifyOrder(); | 628 // 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) { | 629 intptr_t InsertCodeRegion(CodeRegion* code_region) { |
629 const uword start = code_region->start(); | 630 const uword start = code_region->start(); |
630 const uword end = code_region->end(); | 631 const uword end = code_region->end(); |
631 const intptr_t length = code_region_table_->length(); | 632 const intptr_t length = code_region_table_->length(); |
632 if (length == 0) { | 633 if (length == 0) { |
633 code_region_table_->Add(code_region); | 634 code_region_table_->Add(code_region); |
634 return length; | 635 return length; |
635 } | 636 } |
636 // Determine the correct place to insert or merge code_region into table. | 637 // Determine the correct place to insert or merge code_region into table. |
637 intptr_t lo = FindRegionIndex(start, &CompareLowerBound); | 638 intptr_t lo = FindRegionIndex(start, &CompareLowerBound); |
638 intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound); | 639 intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound); |
| 640 // TODO(johnmccutchan): Simplify below logic. |
639 if ((lo == length) && (hi == length)) { | 641 if ((lo == length) && (hi == length)) { |
640 lo = length - 1; | 642 lo = length - 1; |
641 } | 643 } |
642 if (lo == length) { | 644 if (lo == length) { |
643 CodeRegion* region = (*code_region_table_)[hi]; | 645 CodeRegion* region = At(hi); |
644 if (region->overlaps(code_region)) { | 646 if (region->overlaps(code_region)) { |
645 HandleOverlap(region, code_region, start, end); | 647 HandleOverlap(region, code_region, start, end); |
646 return hi; | 648 return hi; |
647 } | 649 } |
648 code_region_table_->Add(code_region); | 650 code_region_table_->Add(code_region); |
649 return length; | 651 return length; |
650 } else if (hi == length) { | 652 } else if (hi == length) { |
651 CodeRegion* region = (*code_region_table_)[lo]; | 653 CodeRegion* region = At(lo); |
652 if (region->overlaps(code_region)) { | 654 if (region->overlaps(code_region)) { |
653 HandleOverlap(region, code_region, start, end); | 655 HandleOverlap(region, code_region, start, end); |
654 return lo; | 656 return lo; |
655 } | 657 } |
656 code_region_table_->Add(code_region); | 658 code_region_table_->Add(code_region); |
657 return length; | 659 return length; |
658 } else if (lo == hi) { | 660 } else if (lo == hi) { |
659 CodeRegion* region = (*code_region_table_)[lo]; | 661 CodeRegion* region = At(lo); |
660 if (region->overlaps(code_region)) { | 662 if (region->overlaps(code_region)) { |
661 HandleOverlap(region, code_region, start, end); | 663 HandleOverlap(region, code_region, start, end); |
662 return lo; | 664 return lo; |
663 } | 665 } |
664 code_region_table_->InsertAt(lo, code_region); | 666 code_region_table_->InsertAt(lo, code_region); |
665 return lo; | 667 return lo; |
666 } else { | 668 } else { |
667 CodeRegion* region = (*code_region_table_)[lo]; | 669 CodeRegion* region = At(lo); |
668 if (region->overlaps(code_region)) { | 670 if (region->overlaps(code_region)) { |
669 HandleOverlap(region, code_region, start, end); | 671 HandleOverlap(region, code_region, start, end); |
670 return lo; | 672 return lo; |
671 } | 673 } |
672 region = (*code_region_table_)[hi]; | 674 region = At(hi); |
673 if (region->overlaps(code_region)) { | 675 if (region->overlaps(code_region)) { |
674 HandleOverlap(region, code_region, start, end); | 676 HandleOverlap(region, code_region, start, end); |
675 return hi; | 677 return hi; |
676 } | 678 } |
677 code_region_table_->InsertAt(hi, code_region); | 679 code_region_table_->InsertAt(hi, code_region); |
678 return hi; | 680 return hi; |
679 } | 681 } |
680 UNREACHABLE(); | 682 UNREACHABLE(); |
681 } | 683 } |
682 | 684 |
683 #if defined(DEBUG) | 685 #if defined(DEBUG) |
| 686 void Verify() { |
| 687 VerifyOrder(); |
| 688 VerifyOverlap(); |
| 689 } |
| 690 #endif |
| 691 |
| 692 void DebugPrint() { |
| 693 OS::Print("Dumping CodeRegionTable:\n"); |
| 694 for (intptr_t i = 0; i < code_region_table_->length(); i++) { |
| 695 CodeRegion* region = At(i); |
| 696 region->DebugPrint(); |
| 697 } |
| 698 } |
| 699 |
| 700 private: |
| 701 intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const { |
| 702 ASSERT(comparator != NULL); |
| 703 intptr_t count = code_region_table_->length(); |
| 704 intptr_t first = 0; |
| 705 while (count > 0) { |
| 706 intptr_t it = first; |
| 707 intptr_t step = count / 2; |
| 708 it += step; |
| 709 const CodeRegion* code_region = At(it); |
| 710 if (comparator(pc, code_region->start(), code_region->end())) { |
| 711 first = ++it; |
| 712 count -= (step + 1); |
| 713 } else { |
| 714 count = step; |
| 715 } |
| 716 } |
| 717 return first; |
| 718 } |
| 719 |
| 720 static bool CompareUpperBound(uword pc, uword start, uword end) { |
| 721 return pc >= end; |
| 722 } |
| 723 |
| 724 static bool CompareLowerBound(uword pc, uword start, uword end) { |
| 725 return end <= pc; |
| 726 } |
| 727 |
| 728 void HandleOverlap(CodeRegion* region, CodeRegion* code_region, |
| 729 uword start, uword end) { |
| 730 // We should never see overlapping Dart code regions. |
| 731 ASSERT(region->kind() != CodeRegion::kDartCode); |
| 732 // When code regions overlap, they should be of the same kind. |
| 733 ASSERT(region->kind() == code_region->kind()); |
| 734 region->AdjustExtent(start, end); |
| 735 } |
| 736 |
| 737 #if defined(DEBUG) |
684 void VerifyOrder() { | 738 void VerifyOrder() { |
685 const intptr_t length = code_region_table_->length(); | 739 const intptr_t length = code_region_table_->length(); |
686 if (length == 0) { | 740 if (length == 0) { |
687 return; | 741 return; |
688 } | 742 } |
689 uword last = (*code_region_table_)[0]->end(); | 743 uword last = (*code_region_table_)[0]->end(); |
690 for (intptr_t i = 1; i < length; i++) { | 744 for (intptr_t i = 1; i < length; i++) { |
691 CodeRegion* a = (*code_region_table_)[i]; | 745 CodeRegion* a = (*code_region_table_)[i]; |
692 ASSERT(last <= a->start()); | 746 ASSERT(last <= a->start()); |
693 last = a->end(); | 747 last = a->end(); |
694 } | 748 } |
695 } | 749 } |
696 | 750 |
697 void VerifyOverlap() { | 751 void VerifyOverlap() { |
698 const intptr_t length = code_region_table_->length(); | 752 const intptr_t length = code_region_table_->length(); |
699 for (intptr_t i = 0; i < length; i++) { | 753 for (intptr_t i = 0; i < length; i++) { |
700 CodeRegion* a = (*code_region_table_)[i]; | 754 CodeRegion* a = (*code_region_table_)[i]; |
701 for (intptr_t j = i+1; j < length; j++) { | 755 for (intptr_t j = i+1; j < length; j++) { |
702 CodeRegion* b = (*code_region_table_)[j]; | 756 CodeRegion* b = (*code_region_table_)[j]; |
703 ASSERT(!a->contains(b->start()) && | 757 ASSERT(!a->contains(b->start()) && |
704 !a->contains(b->end() - 1) && | 758 !a->contains(b->end() - 1) && |
705 !b->contains(a->start()) && | 759 !b->contains(a->start()) && |
706 !b->contains(a->end() - 1)); | 760 !b->contains(a->end() - 1)); |
707 } | 761 } |
708 } | 762 } |
709 } | 763 } |
710 #endif | 764 #endif |
711 | 765 |
712 Heap* heap_; | |
713 ZoneGrowableArray<CodeRegion*>* code_region_table_; | 766 ZoneGrowableArray<CodeRegion*>* code_region_table_; |
714 }; | 767 }; |
715 | 768 |
716 | 769 |
717 class CodeRegionTableBuilder : public SampleVisitor { | 770 class CodeRegionTableBuilder : public SampleVisitor { |
718 public: | 771 public: |
719 CodeRegionTableBuilder(Isolate* isolate, | 772 CodeRegionTableBuilder(Isolate* isolate, |
720 ProfilerCodeRegionTable* code_region_table) | 773 CodeRegionTable* live_code_table, |
721 : SampleVisitor(isolate), code_region_table_(code_region_table) { | 774 CodeRegionTable* dead_code_table) |
| 775 : SampleVisitor(isolate), |
| 776 live_code_table_(live_code_table), |
| 777 dead_code_table_(dead_code_table), |
| 778 isolate_(isolate), |
| 779 vm_isolate_(Dart::vm_isolate()) { |
| 780 ASSERT(live_code_table_ != NULL); |
| 781 ASSERT(dead_code_table_ != NULL); |
722 frames_ = 0; | 782 frames_ = 0; |
723 min_time_ = kMaxInt64; | 783 min_time_ = kMaxInt64; |
724 max_time_ = 0; | 784 max_time_ = 0; |
| 785 ASSERT(isolate_ != NULL); |
| 786 ASSERT(vm_isolate_ != NULL); |
725 } | 787 } |
726 | 788 |
727 void VisitSample(Sample* sample) { | 789 void VisitSample(Sample* sample) { |
728 int64_t timestamp = sample->timestamp(); | 790 int64_t timestamp = sample->timestamp(); |
729 if (timestamp > max_time_) { | 791 if (timestamp > max_time_) { |
730 max_time_ = timestamp; | 792 max_time_ = timestamp; |
731 } | 793 } |
732 if (timestamp < min_time_) { | 794 if (timestamp < min_time_) { |
733 min_time_ = timestamp; | 795 min_time_ = timestamp; |
734 } | 796 } |
735 // Give the bottom frame an exclusive tick. | 797 // Exclusive tick for bottom frame. |
736 code_region_table_->AddTick(sample->At(0), true, -1); | 798 Tick(sample->At(0), true, timestamp); |
737 // Give all frames (including the bottom) an inclusive tick. | 799 // Inclusive tick for all frames. |
738 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { | 800 for (intptr_t i = 0; i < FLAG_profile_depth; i++) { |
739 if (sample->At(i) == 0) { | 801 if (sample->At(i) == 0) { |
740 break; | 802 break; |
741 } | 803 } |
742 frames_++; | 804 frames_++; |
743 code_region_table_->AddTick(sample->At(i), false, visited()); | 805 Tick(sample->At(i), false, timestamp); |
744 } | 806 } |
745 } | 807 } |
746 | 808 |
747 intptr_t frames() const { return frames_; } | 809 intptr_t frames() const { return frames_; } |
| 810 |
748 intptr_t TimeDeltaMicros() const { | 811 intptr_t TimeDeltaMicros() const { |
749 return static_cast<intptr_t>(max_time_ - min_time_); | 812 return static_cast<intptr_t>(max_time_ - min_time_); |
750 } | 813 } |
751 int64_t max_time() const { return max_time_; } | 814 int64_t max_time() const { return max_time_; } |
752 | 815 |
753 private: | 816 private: |
| 817 void Tick(uword pc, bool exclusive, int64_t timestamp) { |
| 818 CodeRegionTable::TickResult r; |
| 819 intptr_t serial = exclusive ? -1 : visited(); |
| 820 r = live_code_table_->Tick(pc, exclusive, serial, timestamp); |
| 821 if (r == CodeRegionTable::kTicked) { |
| 822 // Live code found and ticked. |
| 823 return; |
| 824 } |
| 825 if (r == CodeRegionTable::kNewerCode) { |
| 826 // Code has been overwritten by newer code. |
| 827 // Update shadow table of dead code regions. |
| 828 r = dead_code_table_->Tick(pc, exclusive, serial, timestamp); |
| 829 ASSERT(r != CodeRegionTable::kNewerCode); |
| 830 if (r == CodeRegionTable::kTicked) { |
| 831 // Dead code found and ticked. |
| 832 return; |
| 833 } |
| 834 ASSERT(r == CodeRegionTable::kNotFound); |
| 835 CreateAndTickDeadCodeRegion(pc, exclusive, serial); |
| 836 return; |
| 837 } |
| 838 // Create new live CodeRegion. |
| 839 ASSERT(r == CodeRegionTable::kNotFound); |
| 840 CodeRegion* region = CreateCodeRegion(pc); |
| 841 region->set_creation_serial(visited()); |
| 842 intptr_t index = live_code_table_->InsertCodeRegion(region); |
| 843 ASSERT(index >= 0); |
| 844 region = live_code_table_->At(index); |
| 845 if (region->compile_timestamp() <= timestamp) { |
| 846 region->Tick(pc, exclusive, serial); |
| 847 return; |
| 848 } |
| 849 // We have created a new code region but it's for a CodeRegion |
| 850 // compiled after the sample. |
| 851 ASSERT(region->kind() == CodeRegion::kDartCode); |
| 852 CreateAndTickDeadCodeRegion(pc, exclusive, serial); |
| 853 } |
| 854 |
| 855 void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) { |
| 856 // Need to create dead code. |
| 857 CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode, |
| 858 pc, |
| 859 pc + 1, |
| 860 0); |
| 861 intptr_t index = dead_code_table_->InsertCodeRegion(region); |
| 862 region->set_creation_serial(visited()); |
| 863 ASSERT(index >= 0); |
| 864 dead_code_table_->At(index)->Tick(pc, exclusive, serial); |
| 865 } |
| 866 |
| 867 CodeRegion* CreateCodeRegion(uword pc) { |
| 868 const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment(); |
| 869 const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1); |
| 870 Code& code = Code::Handle(isolate_); |
| 871 // Check current isolate for pc. |
| 872 if (isolate_->heap()->CodeContains(pc)) { |
| 873 code ^= Code::LookupCode(pc); |
| 874 if (!code.IsNull()) { |
| 875 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), |
| 876 code.EntryPoint() + code.Size(), |
| 877 code.compile_timestamp()); |
| 878 } |
| 879 return new CodeRegion(CodeRegion::kCollectedCode, pc, |
| 880 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| 881 0); |
| 882 } |
| 883 // Check VM isolate for pc. |
| 884 if (vm_isolate_->heap()->CodeContains(pc)) { |
| 885 code ^= Code::LookupCodeInVmIsolate(pc); |
| 886 if (!code.IsNull()) { |
| 887 return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(), |
| 888 code.EntryPoint() + code.Size(), |
| 889 code.compile_timestamp()); |
| 890 } |
| 891 return new CodeRegion(CodeRegion::kCollectedCode, pc, |
| 892 (pc & kDartCodeAlignmentMask) + kDartCodeAlignment, |
| 893 0); |
| 894 } |
| 895 // Check NativeSymbolResolver for pc. |
| 896 uintptr_t native_start = 0; |
| 897 char* native_name = NativeSymbolResolver::LookupSymbolName(pc, |
| 898 &native_start); |
| 899 if (native_name == NULL) { |
| 900 // No native name found. |
| 901 return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1, 0); |
| 902 } |
| 903 ASSERT(pc >= native_start); |
| 904 CodeRegion* code_region = |
| 905 new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1, 0); |
| 906 code_region->SetName(native_name); |
| 907 free(native_name); |
| 908 return code_region; |
| 909 } |
| 910 |
754 intptr_t frames_; | 911 intptr_t frames_; |
755 int64_t min_time_; | 912 int64_t min_time_; |
756 int64_t max_time_; | 913 int64_t max_time_; |
757 ProfilerCodeRegionTable* code_region_table_; | 914 CodeRegionTable* live_code_table_; |
| 915 CodeRegionTable* dead_code_table_; |
| 916 Isolate* isolate_; |
| 917 Isolate* vm_isolate_; |
758 }; | 918 }; |
759 | 919 |
760 | 920 |
761 class CodeRegionTableCallersBuilder : public SampleVisitor { | 921 class CodeRegionTableCallersBuilder : public SampleVisitor { |
762 public: | 922 public: |
763 CodeRegionTableCallersBuilder(Isolate* isolate, | 923 CodeRegionTableCallersBuilder(Isolate* isolate, |
764 ProfilerCodeRegionTable* code_region_table) | 924 CodeRegionTable* live_code_table, |
765 : SampleVisitor(isolate), code_region_table_(code_region_table) { | 925 CodeRegionTable* dead_code_table) |
766 ASSERT(code_region_table_ != NULL); | 926 : SampleVisitor(isolate), |
| 927 live_code_table_(live_code_table), |
| 928 dead_code_table_(dead_code_table) { |
| 929 ASSERT(live_code_table_ != NULL); |
| 930 ASSERT(dead_code_table_ != NULL); |
| 931 dead_code_table_offset_ = live_code_table_->Length(); |
767 } | 932 } |
768 | 933 |
769 void VisitSample(Sample* sample) { | 934 void VisitSample(Sample* sample) { |
770 intptr_t current_index = code_region_table_->FindIndex(sample->At(0)); | 935 int64_t timestamp = sample->timestamp(); |
771 ASSERT(current_index != -1); | 936 intptr_t current_index = FindFinalIndex(sample->At(0), timestamp); |
772 CodeRegion* current = code_region_table_->At(current_index); | 937 ASSERT(current_index >= 0); |
| 938 CodeRegion* current = At(current_index); |
773 intptr_t caller_index = -1; | 939 intptr_t caller_index = -1; |
774 CodeRegion* caller = NULL; | 940 CodeRegion* caller = NULL; |
775 intptr_t callee_index = -1; | 941 intptr_t callee_index = -1; |
776 CodeRegion* callee = NULL; | 942 CodeRegion* callee = NULL; |
777 for (intptr_t i = 1; i < FLAG_profile_depth; i++) { | 943 for (intptr_t i = 1; i < FLAG_profile_depth; i++) { |
778 if (sample->At(i) == 0) { | 944 if (sample->At(i) == 0) { |
779 break; | 945 break; |
780 } | 946 } |
781 caller_index = code_region_table_->FindIndex(sample->At(i)); | 947 caller_index = FindFinalIndex(sample->At(i), timestamp); |
782 ASSERT(caller_index != -1); | 948 ASSERT(caller_index >= 0); |
783 caller = code_region_table_->At(caller_index); | 949 caller = At(caller_index); |
784 current->AddCaller(caller_index); | 950 current->AddCaller(caller_index); |
785 if (callee != NULL) { | 951 if (callee != NULL) { |
786 current->AddCallee(callee_index); | 952 current->AddCallee(callee_index); |
787 } | 953 } |
788 // Move cursors. | 954 // Move cursors. |
789 callee_index = current_index; | 955 callee_index = current_index; |
790 callee = current; | 956 callee = current; |
791 current_index = caller_index; | 957 current_index = caller_index; |
792 current = caller; | 958 current = caller; |
793 } | 959 } |
794 } | 960 } |
795 | 961 |
796 private: | 962 private: |
797 ProfilerCodeRegionTable* code_region_table_; | 963 intptr_t FindFinalIndex(uword pc, int64_t timestamp) const { |
| 964 intptr_t index = live_code_table_->FindIndex(pc); |
| 965 ASSERT(index >= 0); |
| 966 CodeRegion* region = live_code_table_->At(index); |
| 967 ASSERT(region->contains(pc)); |
| 968 if (region->compile_timestamp() > timestamp) { |
| 969 // Overwritten code, find in dead code table. |
| 970 index = dead_code_table_->FindIndex(pc); |
| 971 ASSERT(index >= 0); |
| 972 region = dead_code_table_->At(index); |
| 973 ASSERT(region->contains(pc)); |
| 974 ASSERT(region->compile_timestamp() <= timestamp); |
| 975 return index + dead_code_table_offset_; |
| 976 } |
| 977 ASSERT(region->compile_timestamp() <= timestamp); |
| 978 return index; |
| 979 } |
| 980 |
| 981 CodeRegion* At(intptr_t final_index) { |
| 982 ASSERT(final_index >= 0); |
| 983 if (final_index < dead_code_table_offset_) { |
| 984 return live_code_table_->At(final_index); |
| 985 } else { |
| 986 return dead_code_table_->At(final_index - dead_code_table_offset_); |
| 987 } |
| 988 } |
| 989 |
| 990 CodeRegionTable* live_code_table_; |
| 991 CodeRegionTable* dead_code_table_; |
| 992 intptr_t dead_code_table_offset_; |
798 }; | 993 }; |
799 | 994 |
800 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream, | 995 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream, |
801 bool full) { | 996 bool full) { |
802 ASSERT(isolate == Isolate::Current()); | 997 ASSERT(isolate == Isolate::Current()); |
803 // Disable profile interrupts while processing the buffer. | 998 // Disable profile interrupts while processing the buffer. |
804 EndExecution(isolate); | 999 EndExecution(isolate); |
805 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); | 1000 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
806 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 1001 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
807 if (profiler_data == NULL) { | 1002 if (profiler_data == NULL) { |
808 JSONObject error(stream); | 1003 JSONObject error(stream); |
809 error.AddProperty("type", "Error"); | 1004 error.AddProperty("type", "Error"); |
810 error.AddProperty("text", "Isolate does not have profiling enabled."); | 1005 error.AddProperty("text", "Isolate does not have profiling enabled."); |
811 return; | 1006 return; |
812 } | 1007 } |
813 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 1008 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
814 ASSERT(sample_buffer != NULL); | 1009 ASSERT(sample_buffer != NULL); |
815 { | 1010 { |
816 StackZone zone(isolate); | 1011 StackZone zone(isolate); |
817 { | 1012 { |
818 // Build code region table. | 1013 // Live code holds Dart, Native, and Collected CodeRegions. |
819 ProfilerCodeRegionTable code_region_table(isolate); | 1014 CodeRegionTable live_code_table; |
820 CodeRegionTableBuilder builder(isolate, &code_region_table); | 1015 // Dead code holds Overwritten CodeRegions. |
821 CodeRegionTableCallersBuilder build_callers(isolate, &code_region_table); | 1016 CodeRegionTable dead_code_table; |
| 1017 CodeRegionTableBuilder builder(isolate, |
| 1018 &live_code_table, |
| 1019 &dead_code_table); |
822 { | 1020 { |
| 1021 // Build CodeRegion tables. |
823 ScopeStopwatch sw("CodeTableBuild"); | 1022 ScopeStopwatch sw("CodeTableBuild"); |
824 sample_buffer->VisitSamples(&builder); | 1023 sample_buffer->VisitSamples(&builder); |
825 } | 1024 } |
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(); | 1025 intptr_t samples = builder.visited(); |
835 intptr_t frames = builder.frames(); | 1026 intptr_t frames = builder.frames(); |
836 if (FLAG_trace_profiled_isolates) { | 1027 if (FLAG_trace_profiled_isolates) { |
837 OS::Print("%" Pd " frames produced %" Pd " code objects.\n", | 1028 intptr_t total_live_code_objects = live_code_table.Length(); |
838 frames, code_region_table.Length()); | 1029 intptr_t total_dead_code_objects = dead_code_table.Length(); |
| 1030 OS::Print("Processed %" Pd " frames\n", frames); |
| 1031 OS::Print("CodeTables: live=%" Pd " dead=%" Pd "\n", |
| 1032 total_live_code_objects, |
| 1033 total_dead_code_objects); |
| 1034 } |
| 1035 #if defined(DEBUG) |
| 1036 live_code_table.Verify(); |
| 1037 dead_code_table.Verify(); |
| 1038 if (FLAG_trace_profiled_isolates) { |
| 1039 OS::Print("CodeRegionTables verified to be ordered and not overlap.\n"); |
| 1040 } |
| 1041 #endif |
| 1042 CodeRegionTableCallersBuilder build_callers(isolate, |
| 1043 &live_code_table, |
| 1044 &dead_code_table); |
| 1045 { |
| 1046 // Build CodeRegion callers. |
| 1047 ScopeStopwatch sw("CodeTableCallersBuild"); |
| 1048 sample_buffer->VisitSamples(&build_callers); |
839 } | 1049 } |
840 { | 1050 { |
841 ScopeStopwatch sw("CodeTableStream"); | 1051 ScopeStopwatch sw("CodeTableStream"); |
842 // Serialize to JSON. | 1052 // Serialize to JSON. |
843 JSONObject obj(stream); | 1053 JSONObject obj(stream); |
844 obj.AddProperty("type", "Profile"); | 1054 obj.AddProperty("type", "Profile"); |
845 obj.AddProperty("id", "profile"); | 1055 obj.AddProperty("id", "profile"); |
846 obj.AddProperty("samples", samples); | 1056 obj.AddProperty("samples", samples); |
847 obj.AddProperty("time_delta_micros", builder.TimeDeltaMicros()); | 1057 obj.AddProperty("time_delta_micros", builder.TimeDeltaMicros()); |
848 JSONArray codes(&obj, "codes"); | 1058 JSONArray codes(&obj, "codes"); |
849 for (intptr_t i = 0; i < code_region_table.Length(); i++) { | 1059 for (intptr_t i = 0; i < live_code_table.Length(); i++) { |
850 CodeRegion* region = code_region_table.At(i); | 1060 CodeRegion* region = live_code_table.At(i); |
851 ASSERT(region != NULL); | 1061 ASSERT(region != NULL); |
852 region->PrintToJSONArray(isolate, &codes, full); | 1062 region->PrintToJSONArray(isolate, &codes, full); |
853 } | 1063 } |
| 1064 for (intptr_t i = 0; i < dead_code_table.Length(); i++) { |
| 1065 CodeRegion* region = dead_code_table.At(i); |
| 1066 ASSERT(region != NULL); |
| 1067 region->PrintToJSONArray(isolate, &codes, full); |
| 1068 } |
854 } | 1069 } |
855 } | 1070 } |
856 } | 1071 } |
857 // Enable profile interrupts. | 1072 // Enable profile interrupts. |
858 BeginExecution(isolate); | 1073 BeginExecution(isolate); |
859 } | 1074 } |
860 | 1075 |
861 | 1076 |
862 void Profiler::WriteProfile(Isolate* isolate) { | 1077 void Profiler::WriteProfile(Isolate* isolate) { |
863 if (isolate == NULL) { | 1078 if (isolate == NULL) { |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1070 stack_lower = 0; | 1285 stack_lower = 0; |
1071 stack_upper = 0; | 1286 stack_upper = 0; |
1072 } | 1287 } |
1073 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, | 1288 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, |
1074 state.pc, state.fp, state.sp); | 1289 state.pc, state.fp, state.sp); |
1075 stackWalker.walk(isolate->heap()); | 1290 stackWalker.walk(isolate->heap()); |
1076 } | 1291 } |
1077 | 1292 |
1078 | 1293 |
1079 } // namespace dart | 1294 } // namespace dart |
OLD | NEW |