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 |