Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/profiler_service.h" | 5 #include "vm/profiler_service.h" |
| 6 | 6 |
| 7 #include "vm/growable_array.h" | 7 #include "vm/growable_array.h" |
| 8 #include "vm/hash_map.h" | 8 #include "vm/hash_map.h" |
| 9 #include "vm/log.h" | 9 #include "vm/log.h" |
| 10 #include "vm/native_symbol.h" | 10 #include "vm/native_symbol.h" |
| (...skipping 1037 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1048 ProfileFunctionTrieNodeCode code_object(index); | 1048 ProfileFunctionTrieNodeCode code_object(index); |
| 1049 code_object.Tick(); | 1049 code_object.Tick(); |
| 1050 code_objects_.Add(code_object); | 1050 code_objects_.Add(code_object); |
| 1051 } | 1051 } |
| 1052 | 1052 |
| 1053 private: | 1053 private: |
| 1054 ZoneGrowableArray<ProfileFunctionTrieNodeCode> code_objects_; | 1054 ZoneGrowableArray<ProfileFunctionTrieNodeCode> code_objects_; |
| 1055 }; | 1055 }; |
| 1056 | 1056 |
| 1057 | 1057 |
| 1058 class ProfileCodeInlinedFunctionsCache : public ValueObject { | |
| 1059 public: | |
| 1060 ProfileCodeInlinedFunctionsCache() | |
| 1061 : cache_cursor_(0) { | |
| 1062 for (intptr_t i = 0; i < kCacheSize; i++) { | |
| 1063 cache_[i].Reset(); | |
| 1064 } | |
| 1065 cache_hit_ = 0; | |
| 1066 cache_miss_ = 0; | |
| 1067 } | |
| 1068 | |
| 1069 ~ProfileCodeInlinedFunctionsCache() { | |
| 1070 if (FLAG_trace_profiler) { | |
| 1071 intptr_t total = cache_hit_ + cache_miss_; | |
| 1072 OS::Print("LOOKUPS: %" Pd " HITS: %" Pd " MISSES: %" Pd "\n", | |
| 1073 total, | |
| 1074 cache_hit_, | |
| 1075 cache_miss_); | |
| 1076 } | |
| 1077 } | |
| 1078 | |
| 1079 void Get(uword pc, | |
| 1080 const Code& code, | |
| 1081 ProcessedSample* sample, | |
| 1082 intptr_t frame_index, | |
| 1083 // Outputs: | |
| 1084 GrowableArray<Function*>** inlined_functions, | |
| 1085 GrowableArray<TokenPosition>** inlined_token_positions, | |
| 1086 TokenPosition* token_position) { | |
| 1087 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index); | |
| 1088 if (FindInCache(pc, | |
| 1089 offset, | |
| 1090 inlined_functions, | |
| 1091 inlined_token_positions, | |
| 1092 token_position)) { | |
| 1093 // Found in cache. | |
| 1094 return; | |
| 1095 } | |
| 1096 Add(pc, code, sample, frame_index, | |
| 1097 inlined_functions, inlined_token_positions, token_position); | |
| 1098 } | |
| 1099 | |
| 1100 private: | |
| 1101 bool FindInCache(uword pc, | |
| 1102 intptr_t offset, | |
| 1103 GrowableArray<Function*>** inlined_functions, | |
| 1104 GrowableArray<TokenPosition>** inlined_token_positions, | |
| 1105 TokenPosition* token_position) { | |
| 1106 // Simple linear scan. | |
| 1107 for (intptr_t i = 0; i < kCacheSize; i++) { | |
| 1108 if ((cache_[i].pc == pc) && (cache_[i].offset == offset)) { | |
| 1109 // Hit. | |
| 1110 if (cache_[i].inlined_functions.length() == 0) { | |
| 1111 *inlined_functions = NULL; | |
| 1112 *inlined_token_positions = NULL; | |
| 1113 } else { | |
| 1114 *inlined_functions = &cache_[i].inlined_functions; | |
| 1115 *inlined_token_positions = &cache_[i].inlined_token_positions; | |
| 1116 } | |
| 1117 *token_position = cache_[i].token_position; | |
| 1118 cache_hit_++; | |
| 1119 return true; | |
| 1120 } | |
| 1121 } | |
| 1122 cache_miss_++; | |
| 1123 return false; | |
| 1124 } | |
| 1125 | |
| 1126 // Add to cache and fill in outputs. | |
| 1127 void Add(uword pc, | |
| 1128 const Code& code, | |
| 1129 ProcessedSample* sample, | |
| 1130 intptr_t frame_index, | |
| 1131 // Outputs: | |
| 1132 GrowableArray<Function*>** inlined_functions, | |
| 1133 GrowableArray<TokenPosition>** inlined_token_positions, | |
| 1134 TokenPosition* token_position) { | |
| 1135 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index); | |
| 1136 CacheEntry* cache_entry = &cache_[NextFreeIndex()]; | |
| 1137 cache_entry->pc = pc; | |
| 1138 cache_entry->offset = offset; | |
| 1139 code.GetInlinedFunctionsAt(offset, | |
| 1140 &(cache_entry->inlined_functions), | |
| 1141 &(cache_entry->inlined_token_positions)); | |
| 1142 cache_entry->token_position = code.GetTokenPositionAt(offset); | |
| 1143 *token_position = (cache_entry->token_position); | |
| 1144 *inlined_functions = NULL; | |
| 1145 *inlined_token_positions = NULL; | |
|
srdjan
2016/03/23 21:55:41
Move this two lines inside the if below, or move t
Cutch
2016/03/23 22:31:17
Done.
| |
| 1146 if (cache_entry->inlined_functions.length() == 0) { | |
| 1147 return; | |
| 1148 } | |
| 1149 // The inlined token position table does not include the token position | |
| 1150 // of the final call. Insert it at the beginning because the table. | |
| 1151 // is reversed. | |
| 1152 cache_entry->inlined_token_positions.InsertAt( | |
| 1153 0, | |
| 1154 cache_entry->token_position); | |
| 1155 | |
| 1156 // Write outputs. | |
| 1157 *inlined_functions = &(cache_entry->inlined_functions); | |
| 1158 *inlined_token_positions = &(cache_entry->inlined_token_positions); | |
| 1159 } | |
| 1160 | |
| 1161 intptr_t NextFreeIndex() { | |
| 1162 cache_cursor_ = (cache_cursor_ + 1) % kCacheSize; | |
| 1163 return cache_cursor_; | |
| 1164 } | |
| 1165 | |
| 1166 intptr_t OffsetForPC(uword pc, | |
| 1167 const Code& code, | |
| 1168 ProcessedSample* sample, | |
| 1169 intptr_t frame_index) { | |
| 1170 intptr_t offset = pc - code.EntryPoint(); | |
| 1171 if (frame_index != 0) { | |
| 1172 // The PC of frames below the top frame is a call's return address, | |
| 1173 // which can belong to a different inlining interval than the call. | |
| 1174 offset--; | |
| 1175 } else if (sample->IsAllocationSample()) { | |
| 1176 // Allocation samples skip the top frame, so the top frame's pc is | |
| 1177 // also a call's return address. | |
| 1178 offset--; | |
| 1179 } else if (!sample->first_frame_executing()) { | |
| 1180 // If the first frame wasn't executing code (i.e. we started to collect | |
| 1181 // the stack trace at an exit frame), the top frame's pc is also a | |
| 1182 // call's return address. | |
| 1183 offset--; | |
| 1184 } | |
| 1185 return offset; | |
| 1186 } | |
| 1187 | |
| 1188 struct CacheEntry { | |
| 1189 void Reset() { | |
| 1190 pc = 0; | |
| 1191 offset = 0; | |
| 1192 } | |
| 1193 uword pc; | |
| 1194 intptr_t offset; | |
| 1195 GrowableArray<Function*> inlined_functions; | |
| 1196 GrowableArray<TokenPosition> inlined_token_positions; | |
| 1197 TokenPosition token_position; | |
| 1198 }; | |
| 1199 | |
| 1200 static const intptr_t kCacheSize = 128; | |
|
srdjan
2016/03/23 21:55:40
Do you have any performance data for cache size 16
Cutch
2016/03/23 22:31:17
I've tried 32 and 64 and settled on 128 because it
| |
| 1201 intptr_t cache_cursor_; | |
| 1202 CacheEntry cache_[kCacheSize]; | |
| 1203 intptr_t cache_miss_; | |
| 1204 intptr_t cache_hit_; | |
| 1205 }; | |
| 1206 | |
| 1207 | |
| 1058 class ProfileBuilder : public ValueObject { | 1208 class ProfileBuilder : public ValueObject { |
| 1059 public: | 1209 public: |
| 1060 enum ProfileInfoKind { | 1210 enum ProfileInfoKind { |
| 1061 kNone, | 1211 kNone, |
| 1062 kOptimized, | 1212 kOptimized, |
| 1063 kUnoptimized, | 1213 kUnoptimized, |
| 1064 kNative, | 1214 kNative, |
| 1065 kInlineStart, | 1215 kInlineStart, |
| 1066 kInlineFinish, | 1216 kInlineFinish, |
| 1067 kNumProfileInfoKind, | 1217 kNumProfileInfoKind, |
| (...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1449 } | 1599 } |
| 1450 | 1600 |
| 1451 // Truncated tag. | 1601 // Truncated tag. |
| 1452 if (sample->truncated()) { | 1602 if (sample->truncated()) { |
| 1453 current = AppendTruncatedTag(current); | 1603 current = AppendTruncatedTag(current); |
| 1454 InclusiveTickTruncatedTag(); | 1604 InclusiveTickTruncatedTag(); |
| 1455 } | 1605 } |
| 1456 } | 1606 } |
| 1457 } | 1607 } |
| 1458 | 1608 |
| 1459 intptr_t OffsetForPC(uword pc, | |
| 1460 const Code& code, | |
| 1461 ProcessedSample* sample, | |
| 1462 intptr_t frame_index) { | |
| 1463 intptr_t offset = pc - code.EntryPoint(); | |
| 1464 if (frame_index != 0) { | |
| 1465 // The PC of frames below the top frame is a call's return address, | |
| 1466 // which can belong to a different inlining interval than the call. | |
| 1467 offset--; | |
| 1468 } else if (sample->IsAllocationSample()) { | |
| 1469 // Allocation samples skip the top frame, so the top frame's pc is | |
| 1470 // also a call's return address. | |
| 1471 offset--; | |
| 1472 } else if (!sample->first_frame_executing()) { | |
| 1473 // If the first frame wasn't executing code (i.e. we started to collect | |
| 1474 // the stack trace at an exit frame), the top frame's pc is also a | |
| 1475 // call's return address. | |
| 1476 offset--; | |
| 1477 } | |
| 1478 return offset; | |
| 1479 } | |
| 1480 | |
| 1481 ProfileFunctionTrieNode* ProcessFrame( | 1609 ProfileFunctionTrieNode* ProcessFrame( |
| 1482 ProfileFunctionTrieNode* current, | 1610 ProfileFunctionTrieNode* current, |
| 1483 intptr_t sample_index, | 1611 intptr_t sample_index, |
| 1484 ProcessedSample* sample, | 1612 ProcessedSample* sample, |
| 1485 intptr_t frame_index) { | 1613 intptr_t frame_index) { |
| 1486 const uword pc = sample->At(frame_index); | 1614 const uword pc = sample->At(frame_index); |
| 1487 ProfileCode* profile_code = GetProfileCode(pc, | 1615 ProfileCode* profile_code = GetProfileCode(pc, |
| 1488 sample->timestamp()); | 1616 sample->timestamp()); |
| 1489 ProfileFunction* function = profile_code->function(); | 1617 ProfileFunction* function = profile_code->function(); |
| 1490 ASSERT(function != NULL); | 1618 ASSERT(function != NULL); |
| 1491 const intptr_t code_index = profile_code->code_table_index(); | 1619 const intptr_t code_index = profile_code->code_table_index(); |
| 1492 ASSERT(profile_code != NULL); | 1620 ASSERT(profile_code != NULL); |
| 1493 const Code& code = Code::ZoneHandle(profile_code->code()); | 1621 const Code& code = Code::ZoneHandle(profile_code->code()); |
| 1494 GrowableArray<Function*> inlined_functions; | 1622 GrowableArray<Function*>* inlined_functions = NULL; |
| 1495 GrowableArray<TokenPosition> inlined_token_positions; | 1623 GrowableArray<TokenPosition>* inlined_token_positions = NULL; |
| 1496 TokenPosition token_position = TokenPosition::kNoSource; | 1624 TokenPosition token_position = TokenPosition::kNoSource; |
| 1497 if (!code.IsNull()) { | 1625 if (!code.IsNull()) { |
| 1498 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index); | 1626 inlined_functions_cache_.Get(pc, code, sample, frame_index, |
| 1499 code.GetInlinedFunctionsAt(offset, | 1627 &inlined_functions, |
| 1500 &inlined_functions, | 1628 &inlined_token_positions, |
| 1501 &inlined_token_positions); | 1629 &token_position); |
| 1502 token_position = code.GetTokenPositionAt(offset); | |
| 1503 if (inlined_functions.length() > 0) { | |
| 1504 // The inlined token position table does not include the token position | |
| 1505 // of the final call. Insert it at the beginning because the table. | |
| 1506 // is reversed. | |
| 1507 inlined_token_positions.InsertAt(0, token_position); | |
| 1508 } | |
| 1509 ASSERT(inlined_functions.length() <= inlined_token_positions.length()); | |
| 1510 if (FLAG_trace_profiler_verbose) { | 1630 if (FLAG_trace_profiler_verbose) { |
| 1511 for (intptr_t i = 0; i < inlined_functions.length(); i++) { | 1631 for (intptr_t i = 0; i < inlined_functions->length(); i++) { |
| 1512 const String& name = | 1632 const String& name = |
| 1513 String::Handle(inlined_functions[i]->QualifiedScrubbedName()); | 1633 String::Handle((*inlined_functions)[i]->QualifiedScrubbedName()); |
| 1514 THR_Print("InlinedFunction[%" Pd "] = {%s, %s}\n", | 1634 THR_Print("InlinedFunction[%" Pd "] = {%s, %s}\n", |
| 1515 i, | 1635 i, |
| 1516 name.ToCString(), | 1636 name.ToCString(), |
| 1517 inlined_token_positions[i].ToCString()); | 1637 (*inlined_token_positions)[i].ToCString()); |
| 1518 } | 1638 } |
| 1519 } | 1639 } |
| 1520 } | 1640 } |
| 1521 if (code.IsNull() || (inlined_functions.length() == 0)) { | 1641 if (code.IsNull() || |
| 1642 (inlined_functions == NULL) || | |
| 1643 (inlined_functions->length() == 0)) { | |
| 1522 // No inlined functions. | 1644 // No inlined functions. |
| 1523 if (inclusive_tree_) { | 1645 if (inclusive_tree_) { |
| 1524 current = AppendKind(code, current); | 1646 current = AppendKind(code, current); |
| 1525 } | 1647 } |
| 1526 current = ProcessFunction(current, | 1648 current = ProcessFunction(current, |
| 1527 sample_index, | 1649 sample_index, |
| 1528 sample, | 1650 sample, |
| 1529 frame_index, | 1651 frame_index, |
| 1530 function, | 1652 function, |
| 1531 token_position, | 1653 token_position, |
| 1532 code_index); | 1654 code_index); |
| 1533 if (!inclusive_tree_) { | 1655 if (!inclusive_tree_) { |
| 1534 current = AppendKind(code, current); | 1656 current = AppendKind(code, current); |
| 1535 } | 1657 } |
| 1536 return current; | 1658 return current; |
| 1537 } | 1659 } |
| 1538 | 1660 |
| 1539 ASSERT(code.is_optimized()); | 1661 ASSERT(code.is_optimized()); |
| 1540 | 1662 |
| 1541 if (inclusive_tree_) { | 1663 if (inclusive_tree_) { |
| 1542 for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) { | 1664 for (intptr_t i = inlined_functions->length() - 1; i >= 0; i--) { |
| 1543 Function* inlined_function = inlined_functions[i]; | 1665 Function* inlined_function = (*inlined_functions)[i]; |
| 1544 ASSERT(inlined_function != NULL); | 1666 ASSERT(inlined_function != NULL); |
| 1545 ASSERT(!inlined_function->IsNull()); | 1667 ASSERT(!inlined_function->IsNull()); |
| 1546 TokenPosition inlined_token_position = inlined_token_positions[i]; | 1668 TokenPosition inlined_token_position = (*inlined_token_positions)[i]; |
| 1547 const bool inliner = i == (inlined_functions.length() - 1); | 1669 const bool inliner = i == (inlined_functions->length() - 1); |
| 1548 if (inliner) { | 1670 if (inliner) { |
| 1549 current = AppendKind(code, current); | 1671 current = AppendKind(code, current); |
| 1550 } | 1672 } |
| 1551 current = ProcessInlinedFunction(current, | 1673 current = ProcessInlinedFunction(current, |
| 1552 sample_index, | 1674 sample_index, |
| 1553 sample, | 1675 sample, |
| 1554 frame_index, | 1676 frame_index, |
| 1555 inlined_function, | 1677 inlined_function, |
| 1556 inlined_token_position, | 1678 inlined_token_position, |
| 1557 code_index); | 1679 code_index); |
| 1558 if (inliner) { | 1680 if (inliner) { |
| 1559 current = AppendKind(kInlineStart, current); | 1681 current = AppendKind(kInlineStart, current); |
| 1560 } | 1682 } |
| 1561 } | 1683 } |
| 1562 current = AppendKind(kInlineFinish, current); | 1684 current = AppendKind(kInlineFinish, current); |
| 1563 } else { | 1685 } else { |
| 1564 // Append the inlined children. | 1686 // Append the inlined children. |
| 1565 current = AppendKind(kInlineFinish, current); | 1687 current = AppendKind(kInlineFinish, current); |
| 1566 for (intptr_t i = 0; i < inlined_functions.length(); i++) { | 1688 for (intptr_t i = 0; i < inlined_functions->length(); i++) { |
| 1567 Function* inlined_function = inlined_functions[i]; | 1689 Function* inlined_function = (*inlined_functions)[i]; |
| 1568 ASSERT(inlined_function != NULL); | 1690 ASSERT(inlined_function != NULL); |
| 1569 ASSERT(!inlined_function->IsNull()); | 1691 ASSERT(!inlined_function->IsNull()); |
| 1570 TokenPosition inlined_token_position = inlined_token_positions[i]; | 1692 TokenPosition inlined_token_position = (*inlined_token_positions)[i]; |
| 1571 const bool inliner = i == (inlined_functions.length() - 1); | 1693 const bool inliner = i == (inlined_functions->length() - 1); |
| 1572 if (inliner) { | 1694 if (inliner) { |
| 1573 current = AppendKind(kInlineStart, current); | 1695 current = AppendKind(kInlineStart, current); |
| 1574 } | 1696 } |
| 1575 current = ProcessInlinedFunction(current, | 1697 current = ProcessInlinedFunction(current, |
| 1576 sample_index, | 1698 sample_index, |
| 1577 sample, | 1699 sample, |
| 1578 frame_index + i, | 1700 frame_index + i, |
| 1579 inlined_function, | 1701 inlined_function, |
| 1580 inlined_token_position, | 1702 inlined_token_position, |
| 1581 code_index); | 1703 code_index); |
| (...skipping 524 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2106 Isolate* vm_isolate_; | 2228 Isolate* vm_isolate_; |
| 2107 SampleFilter* filter_; | 2229 SampleFilter* filter_; |
| 2108 Profile::TagOrder tag_order_; | 2230 Profile::TagOrder tag_order_; |
| 2109 intptr_t extra_tags_; | 2231 intptr_t extra_tags_; |
| 2110 Profile* profile_; | 2232 Profile* profile_; |
| 2111 DeoptimizedCodeSet* deoptimized_code_; | 2233 DeoptimizedCodeSet* deoptimized_code_; |
| 2112 const Code& null_code_; | 2234 const Code& null_code_; |
| 2113 const Function& null_function_; | 2235 const Function& null_function_; |
| 2114 bool tick_functions_; | 2236 bool tick_functions_; |
| 2115 bool inclusive_tree_; | 2237 bool inclusive_tree_; |
| 2116 | 2238 ProfileCodeInlinedFunctionsCache inlined_functions_cache_; |
| 2117 ProcessedSampleBuffer* samples_; | 2239 ProcessedSampleBuffer* samples_; |
| 2118 ProfileInfoKind info_kind_; | 2240 ProfileInfoKind info_kind_; |
| 2119 }; // ProfileBuilder. | 2241 }; // ProfileBuilder. |
| 2120 | 2242 |
| 2121 | 2243 |
| 2122 Profile::Profile(Isolate* isolate) | 2244 Profile::Profile(Isolate* isolate) |
| 2123 : isolate_(isolate), | 2245 : isolate_(isolate), |
| 2124 zone_(Thread::Current()->zone()), | 2246 zone_(Thread::Current()->zone()), |
| 2125 samples_(NULL), | 2247 samples_(NULL), |
| 2126 live_code_(NULL), | 2248 live_code_(NULL), |
| (...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2606 // Disable thread interrupts while processing the buffer. | 2728 // Disable thread interrupts while processing the buffer. |
| 2607 DisableThreadInterruptsScope dtis(thread); | 2729 DisableThreadInterruptsScope dtis(thread); |
| 2608 | 2730 |
| 2609 ClearProfileVisitor clear_profile(isolate); | 2731 ClearProfileVisitor clear_profile(isolate); |
| 2610 sample_buffer->VisitSamples(&clear_profile); | 2732 sample_buffer->VisitSamples(&clear_profile); |
| 2611 } | 2733 } |
| 2612 | 2734 |
| 2613 #endif // !PRODUCT | 2735 #endif // !PRODUCT |
| 2614 | 2736 |
| 2615 } // namespace dart | 2737 } // namespace dart |
| OLD | NEW |