| 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 1047 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1058 ProfileFunctionTrieNodeCode code_object(index); | 1058 ProfileFunctionTrieNodeCode code_object(index); |
| 1059 code_object.Tick(); | 1059 code_object.Tick(); |
| 1060 code_objects_.Add(code_object); | 1060 code_objects_.Add(code_object); |
| 1061 } | 1061 } |
| 1062 | 1062 |
| 1063 private: | 1063 private: |
| 1064 ZoneGrowableArray<ProfileFunctionTrieNodeCode> code_objects_; | 1064 ZoneGrowableArray<ProfileFunctionTrieNodeCode> code_objects_; |
| 1065 }; | 1065 }; |
| 1066 | 1066 |
| 1067 | 1067 |
| 1068 class ProfileCodeInlinedFunctionsCache : public ValueObject { |
| 1069 public: |
| 1070 ProfileCodeInlinedFunctionsCache() |
| 1071 : cache_cursor_(0), |
| 1072 last_hit_(0) { |
| 1073 for (intptr_t i = 0; i < kCacheSize; i++) { |
| 1074 cache_[i].Reset(); |
| 1075 } |
| 1076 cache_hit_ = 0; |
| 1077 cache_miss_ = 0; |
| 1078 } |
| 1079 |
| 1080 ~ProfileCodeInlinedFunctionsCache() { |
| 1081 if (FLAG_trace_profiler) { |
| 1082 intptr_t total = cache_hit_ + cache_miss_; |
| 1083 OS::Print("LOOKUPS: %" Pd " HITS: %" Pd " MISSES: %" Pd "\n", |
| 1084 total, |
| 1085 cache_hit_, |
| 1086 cache_miss_); |
| 1087 } |
| 1088 } |
| 1089 |
| 1090 void Get(uword pc, |
| 1091 const Code& code, |
| 1092 ProcessedSample* sample, |
| 1093 intptr_t frame_index, |
| 1094 // Outputs: |
| 1095 GrowableArray<Function*>** inlined_functions, |
| 1096 GrowableArray<TokenPosition>** inlined_token_positions, |
| 1097 TokenPosition* token_position) { |
| 1098 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index); |
| 1099 if (FindInCache(pc, |
| 1100 offset, |
| 1101 inlined_functions, |
| 1102 inlined_token_positions, |
| 1103 token_position)) { |
| 1104 // Found in cache. |
| 1105 return; |
| 1106 } |
| 1107 Add(pc, code, sample, frame_index, |
| 1108 inlined_functions, inlined_token_positions, token_position); |
| 1109 } |
| 1110 |
| 1111 private: |
| 1112 bool FindInCache(uword pc, |
| 1113 intptr_t offset, |
| 1114 GrowableArray<Function*>** inlined_functions, |
| 1115 GrowableArray<TokenPosition>** inlined_token_positions, |
| 1116 TokenPosition* token_position) { |
| 1117 // Simple linear scan. |
| 1118 for (intptr_t i = 0; i < kCacheSize; i++) { |
| 1119 intptr_t index = (last_hit_ + i) % kCacheSize; |
| 1120 if ((cache_[index].pc == pc) && (cache_[index].offset == offset)) { |
| 1121 // Hit. |
| 1122 if (cache_[index].inlined_functions.length() == 0) { |
| 1123 *inlined_functions = NULL; |
| 1124 *inlined_token_positions = NULL; |
| 1125 } else { |
| 1126 *inlined_functions = &cache_[index].inlined_functions; |
| 1127 *inlined_token_positions = &cache_[index].inlined_token_positions; |
| 1128 } |
| 1129 *token_position = cache_[index].token_position; |
| 1130 cache_hit_++; |
| 1131 last_hit_ = index; |
| 1132 return true; |
| 1133 } |
| 1134 } |
| 1135 cache_miss_++; |
| 1136 return false; |
| 1137 } |
| 1138 |
| 1139 // Add to cache and fill in outputs. |
| 1140 void Add(uword pc, |
| 1141 const Code& code, |
| 1142 ProcessedSample* sample, |
| 1143 intptr_t frame_index, |
| 1144 // Outputs: |
| 1145 GrowableArray<Function*>** inlined_functions, |
| 1146 GrowableArray<TokenPosition>** inlined_token_positions, |
| 1147 TokenPosition* token_position) { |
| 1148 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index); |
| 1149 CacheEntry* cache_entry = &cache_[NextFreeIndex()]; |
| 1150 cache_entry->pc = pc; |
| 1151 cache_entry->offset = offset; |
| 1152 code.GetInlinedFunctionsAt(offset, |
| 1153 &(cache_entry->inlined_functions), |
| 1154 &(cache_entry->inlined_token_positions)); |
| 1155 cache_entry->token_position = code.GetTokenPositionAt(offset); |
| 1156 *token_position = (cache_entry->token_position); |
| 1157 if (cache_entry->inlined_functions.length() == 0) { |
| 1158 *inlined_functions = NULL; |
| 1159 *inlined_token_positions = NULL; |
| 1160 return; |
| 1161 } |
| 1162 // The inlined token position table does not include the token position |
| 1163 // of the final call. Insert it at the beginning because the table. |
| 1164 // is reversed. |
| 1165 cache_entry->inlined_token_positions.InsertAt( |
| 1166 0, |
| 1167 cache_entry->token_position); |
| 1168 |
| 1169 // Write outputs. |
| 1170 *inlined_functions = &(cache_entry->inlined_functions); |
| 1171 *inlined_token_positions = &(cache_entry->inlined_token_positions); |
| 1172 } |
| 1173 |
| 1174 intptr_t NextFreeIndex() { |
| 1175 cache_cursor_ = (cache_cursor_ + 1) % kCacheSize; |
| 1176 return cache_cursor_; |
| 1177 } |
| 1178 |
| 1179 intptr_t OffsetForPC(uword pc, |
| 1180 const Code& code, |
| 1181 ProcessedSample* sample, |
| 1182 intptr_t frame_index) { |
| 1183 intptr_t offset = pc - code.EntryPoint(); |
| 1184 if (frame_index != 0) { |
| 1185 // The PC of frames below the top frame is a call's return address, |
| 1186 // which can belong to a different inlining interval than the call. |
| 1187 offset--; |
| 1188 } else if (sample->IsAllocationSample()) { |
| 1189 // Allocation samples skip the top frame, so the top frame's pc is |
| 1190 // also a call's return address. |
| 1191 offset--; |
| 1192 } else if (!sample->first_frame_executing()) { |
| 1193 // If the first frame wasn't executing code (i.e. we started to collect |
| 1194 // the stack trace at an exit frame), the top frame's pc is also a |
| 1195 // call's return address. |
| 1196 offset--; |
| 1197 } |
| 1198 return offset; |
| 1199 } |
| 1200 |
| 1201 struct CacheEntry { |
| 1202 void Reset() { |
| 1203 pc = 0; |
| 1204 offset = 0; |
| 1205 } |
| 1206 uword pc; |
| 1207 intptr_t offset; |
| 1208 GrowableArray<Function*> inlined_functions; |
| 1209 GrowableArray<TokenPosition> inlined_token_positions; |
| 1210 TokenPosition token_position; |
| 1211 }; |
| 1212 |
| 1213 static const intptr_t kCacheSize = 128; |
| 1214 intptr_t cache_cursor_; |
| 1215 intptr_t last_hit_; |
| 1216 CacheEntry cache_[kCacheSize]; |
| 1217 intptr_t cache_miss_; |
| 1218 intptr_t cache_hit_; |
| 1219 }; |
| 1220 |
| 1221 |
| 1068 class ProfileBuilder : public ValueObject { | 1222 class ProfileBuilder : public ValueObject { |
| 1069 public: | 1223 public: |
| 1070 enum ProfileInfoKind { | 1224 enum ProfileInfoKind { |
| 1071 kNone, | 1225 kNone, |
| 1072 kOptimized, | 1226 kOptimized, |
| 1073 kUnoptimized, | 1227 kUnoptimized, |
| 1074 kNative, | 1228 kNative, |
| 1075 kInlineStart, | 1229 kInlineStart, |
| 1076 kInlineFinish, | 1230 kInlineFinish, |
| 1077 kNumProfileInfoKind, | 1231 kNumProfileInfoKind, |
| (...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1459 } | 1613 } |
| 1460 | 1614 |
| 1461 // Truncated tag. | 1615 // Truncated tag. |
| 1462 if (sample->truncated()) { | 1616 if (sample->truncated()) { |
| 1463 current = AppendTruncatedTag(current); | 1617 current = AppendTruncatedTag(current); |
| 1464 InclusiveTickTruncatedTag(); | 1618 InclusiveTickTruncatedTag(); |
| 1465 } | 1619 } |
| 1466 } | 1620 } |
| 1467 } | 1621 } |
| 1468 | 1622 |
| 1469 intptr_t OffsetForPC(uword pc, | |
| 1470 const Code& code, | |
| 1471 ProcessedSample* sample, | |
| 1472 intptr_t frame_index) { | |
| 1473 intptr_t offset = pc - code.EntryPoint(); | |
| 1474 if (frame_index != 0) { | |
| 1475 // The PC of frames below the top frame is a call's return address, | |
| 1476 // which can belong to a different inlining interval than the call. | |
| 1477 offset--; | |
| 1478 } else if (sample->IsAllocationSample()) { | |
| 1479 // Allocation samples skip the top frame, so the top frame's pc is | |
| 1480 // also a call's return address. | |
| 1481 offset--; | |
| 1482 } else if (!sample->first_frame_executing()) { | |
| 1483 // If the first frame wasn't executing code (i.e. we started to collect | |
| 1484 // the stack trace at an exit frame), the top frame's pc is also a | |
| 1485 // call's return address. | |
| 1486 offset--; | |
| 1487 } | |
| 1488 return offset; | |
| 1489 } | |
| 1490 | |
| 1491 ProfileFunctionTrieNode* ProcessFrame( | 1623 ProfileFunctionTrieNode* ProcessFrame( |
| 1492 ProfileFunctionTrieNode* current, | 1624 ProfileFunctionTrieNode* current, |
| 1493 intptr_t sample_index, | 1625 intptr_t sample_index, |
| 1494 ProcessedSample* sample, | 1626 ProcessedSample* sample, |
| 1495 intptr_t frame_index) { | 1627 intptr_t frame_index) { |
| 1496 const uword pc = sample->At(frame_index); | 1628 const uword pc = sample->At(frame_index); |
| 1497 ProfileCode* profile_code = GetProfileCode(pc, | 1629 ProfileCode* profile_code = GetProfileCode(pc, |
| 1498 sample->timestamp()); | 1630 sample->timestamp()); |
| 1499 ProfileFunction* function = profile_code->function(); | 1631 ProfileFunction* function = profile_code->function(); |
| 1500 ASSERT(function != NULL); | 1632 ASSERT(function != NULL); |
| 1501 const intptr_t code_index = profile_code->code_table_index(); | 1633 const intptr_t code_index = profile_code->code_table_index(); |
| 1502 ASSERT(profile_code != NULL); | 1634 ASSERT(profile_code != NULL); |
| 1503 const Code& code = Code::ZoneHandle(profile_code->code()); | 1635 const Code& code = Code::ZoneHandle(profile_code->code()); |
| 1504 GrowableArray<Function*> inlined_functions; | 1636 GrowableArray<Function*>* inlined_functions = NULL; |
| 1505 GrowableArray<TokenPosition> inlined_token_positions; | 1637 GrowableArray<TokenPosition>* inlined_token_positions = NULL; |
| 1506 TokenPosition token_position = TokenPosition::kNoSource; | 1638 TokenPosition token_position = TokenPosition::kNoSource; |
| 1507 if (!code.IsNull()) { | 1639 if (!code.IsNull()) { |
| 1508 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index); | 1640 inlined_functions_cache_.Get(pc, code, sample, frame_index, |
| 1509 code.GetInlinedFunctionsAt(offset, | 1641 &inlined_functions, |
| 1510 &inlined_functions, | 1642 &inlined_token_positions, |
| 1511 &inlined_token_positions); | 1643 &token_position); |
| 1512 token_position = code.GetTokenPositionAt(offset); | |
| 1513 if (inlined_functions.length() > 0) { | |
| 1514 // The inlined token position table does not include the token position | |
| 1515 // of the final call. Insert it at the beginning because the table. | |
| 1516 // is reversed. | |
| 1517 inlined_token_positions.InsertAt(0, token_position); | |
| 1518 } | |
| 1519 ASSERT(inlined_functions.length() <= inlined_token_positions.length()); | |
| 1520 if (FLAG_trace_profiler_verbose) { | 1644 if (FLAG_trace_profiler_verbose) { |
| 1521 for (intptr_t i = 0; i < inlined_functions.length(); i++) { | 1645 for (intptr_t i = 0; i < inlined_functions->length(); i++) { |
| 1522 const String& name = | 1646 const String& name = |
| 1523 String::Handle(inlined_functions[i]->QualifiedScrubbedName()); | 1647 String::Handle((*inlined_functions)[i]->QualifiedScrubbedName()); |
| 1524 THR_Print("InlinedFunction[%" Pd "] = {%s, %s}\n", | 1648 THR_Print("InlinedFunction[%" Pd "] = {%s, %s}\n", |
| 1525 i, | 1649 i, |
| 1526 name.ToCString(), | 1650 name.ToCString(), |
| 1527 inlined_token_positions[i].ToCString()); | 1651 (*inlined_token_positions)[i].ToCString()); |
| 1528 } | 1652 } |
| 1529 } | 1653 } |
| 1530 } | 1654 } |
| 1531 if (code.IsNull() || (inlined_functions.length() == 0)) { | 1655 if (code.IsNull() || |
| 1656 (inlined_functions == NULL) || |
| 1657 (inlined_functions->length() == 0)) { |
| 1532 // No inlined functions. | 1658 // No inlined functions. |
| 1533 if (inclusive_tree_) { | 1659 if (inclusive_tree_) { |
| 1534 current = AppendKind(code, current); | 1660 current = AppendKind(code, current); |
| 1535 } | 1661 } |
| 1536 current = ProcessFunction(current, | 1662 current = ProcessFunction(current, |
| 1537 sample_index, | 1663 sample_index, |
| 1538 sample, | 1664 sample, |
| 1539 frame_index, | 1665 frame_index, |
| 1540 function, | 1666 function, |
| 1541 token_position, | 1667 token_position, |
| 1542 code_index); | 1668 code_index); |
| 1543 if (!inclusive_tree_) { | 1669 if (!inclusive_tree_) { |
| 1544 current = AppendKind(code, current); | 1670 current = AppendKind(code, current); |
| 1545 } | 1671 } |
| 1546 return current; | 1672 return current; |
| 1547 } | 1673 } |
| 1548 | 1674 |
| 1549 ASSERT(code.is_optimized()); | 1675 ASSERT(code.is_optimized()); |
| 1550 | 1676 |
| 1551 if (inclusive_tree_) { | 1677 if (inclusive_tree_) { |
| 1552 for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) { | 1678 for (intptr_t i = inlined_functions->length() - 1; i >= 0; i--) { |
| 1553 Function* inlined_function = inlined_functions[i]; | 1679 Function* inlined_function = (*inlined_functions)[i]; |
| 1554 ASSERT(inlined_function != NULL); | 1680 ASSERT(inlined_function != NULL); |
| 1555 ASSERT(!inlined_function->IsNull()); | 1681 ASSERT(!inlined_function->IsNull()); |
| 1556 TokenPosition inlined_token_position = inlined_token_positions[i]; | 1682 TokenPosition inlined_token_position = (*inlined_token_positions)[i]; |
| 1557 const bool inliner = i == (inlined_functions.length() - 1); | 1683 const bool inliner = i == (inlined_functions->length() - 1); |
| 1558 if (inliner) { | 1684 if (inliner) { |
| 1559 current = AppendKind(code, current); | 1685 current = AppendKind(code, current); |
| 1560 } | 1686 } |
| 1561 current = ProcessInlinedFunction(current, | 1687 current = ProcessInlinedFunction(current, |
| 1562 sample_index, | 1688 sample_index, |
| 1563 sample, | 1689 sample, |
| 1564 frame_index, | 1690 frame_index, |
| 1565 inlined_function, | 1691 inlined_function, |
| 1566 inlined_token_position, | 1692 inlined_token_position, |
| 1567 code_index); | 1693 code_index); |
| 1568 if (inliner) { | 1694 if (inliner) { |
| 1569 current = AppendKind(kInlineStart, current); | 1695 current = AppendKind(kInlineStart, current); |
| 1570 } | 1696 } |
| 1571 } | 1697 } |
| 1572 current = AppendKind(kInlineFinish, current); | 1698 current = AppendKind(kInlineFinish, current); |
| 1573 } else { | 1699 } else { |
| 1574 // Append the inlined children. | 1700 // Append the inlined children. |
| 1575 current = AppendKind(kInlineFinish, current); | 1701 current = AppendKind(kInlineFinish, current); |
| 1576 for (intptr_t i = 0; i < inlined_functions.length(); i++) { | 1702 for (intptr_t i = 0; i < inlined_functions->length(); i++) { |
| 1577 Function* inlined_function = inlined_functions[i]; | 1703 Function* inlined_function = (*inlined_functions)[i]; |
| 1578 ASSERT(inlined_function != NULL); | 1704 ASSERT(inlined_function != NULL); |
| 1579 ASSERT(!inlined_function->IsNull()); | 1705 ASSERT(!inlined_function->IsNull()); |
| 1580 TokenPosition inlined_token_position = inlined_token_positions[i]; | 1706 TokenPosition inlined_token_position = (*inlined_token_positions)[i]; |
| 1581 const bool inliner = i == (inlined_functions.length() - 1); | 1707 const bool inliner = i == (inlined_functions->length() - 1); |
| 1582 if (inliner) { | 1708 if (inliner) { |
| 1583 current = AppendKind(kInlineStart, current); | 1709 current = AppendKind(kInlineStart, current); |
| 1584 } | 1710 } |
| 1585 current = ProcessInlinedFunction(current, | 1711 current = ProcessInlinedFunction(current, |
| 1586 sample_index, | 1712 sample_index, |
| 1587 sample, | 1713 sample, |
| 1588 frame_index + i, | 1714 frame_index + i, |
| 1589 inlined_function, | 1715 inlined_function, |
| 1590 inlined_token_position, | 1716 inlined_token_position, |
| 1591 code_index); | 1717 code_index); |
| (...skipping 527 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2119 Isolate* vm_isolate_; | 2245 Isolate* vm_isolate_; |
| 2120 SampleFilter* filter_; | 2246 SampleFilter* filter_; |
| 2121 Profile::TagOrder tag_order_; | 2247 Profile::TagOrder tag_order_; |
| 2122 intptr_t extra_tags_; | 2248 intptr_t extra_tags_; |
| 2123 Profile* profile_; | 2249 Profile* profile_; |
| 2124 DeoptimizedCodeSet* deoptimized_code_; | 2250 DeoptimizedCodeSet* deoptimized_code_; |
| 2125 const Code& null_code_; | 2251 const Code& null_code_; |
| 2126 const Function& null_function_; | 2252 const Function& null_function_; |
| 2127 bool tick_functions_; | 2253 bool tick_functions_; |
| 2128 bool inclusive_tree_; | 2254 bool inclusive_tree_; |
| 2129 | 2255 ProfileCodeInlinedFunctionsCache inlined_functions_cache_; |
| 2130 ProcessedSampleBuffer* samples_; | 2256 ProcessedSampleBuffer* samples_; |
| 2131 ProfileInfoKind info_kind_; | 2257 ProfileInfoKind info_kind_; |
| 2132 }; // ProfileBuilder. | 2258 }; // ProfileBuilder. |
| 2133 | 2259 |
| 2134 | 2260 |
| 2135 Profile::Profile(Isolate* isolate) | 2261 Profile::Profile(Isolate* isolate) |
| 2136 : isolate_(isolate), | 2262 : isolate_(isolate), |
| 2137 zone_(Thread::Current()->zone()), | 2263 zone_(Thread::Current()->zone()), |
| 2138 samples_(NULL), | 2264 samples_(NULL), |
| 2139 live_code_(NULL), | 2265 live_code_(NULL), |
| (...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2619 // Disable thread interrupts while processing the buffer. | 2745 // Disable thread interrupts while processing the buffer. |
| 2620 DisableThreadInterruptsScope dtis(thread); | 2746 DisableThreadInterruptsScope dtis(thread); |
| 2621 | 2747 |
| 2622 ClearProfileVisitor clear_profile(isolate); | 2748 ClearProfileVisitor clear_profile(isolate); |
| 2623 sample_buffer->VisitSamples(&clear_profile); | 2749 sample_buffer->VisitSamples(&clear_profile); |
| 2624 } | 2750 } |
| 2625 | 2751 |
| 2626 #endif // !PRODUCT | 2752 #endif // !PRODUCT |
| 2627 | 2753 |
| 2628 } // namespace dart | 2754 } // namespace dart |
| OLD | NEW |